From e247f83c7687c1b99e9a71b18a10d85e64434463 Mon Sep 17 00:00:00 2001 From: purplerain Date: Thu, 15 Aug 2024 02:21:18 +0000 Subject: [PATCH] sync with OpenBSD -current --- distrib/notes/arm64/prep | 11 +- lib/libradius/radius.c | 6 +- lib/libradius/radius_msgauth.c | 4 +- sys/arch/amd64/amd64/bus_dma.c | 142 +- sys/arch/amd64/conf/GENERIC | 3 +- sys/arch/amd64/conf/RAMDISK_CD | 3 +- sys/arch/amd64/include/bus.h | 10 +- sys/arch/arm64/conf/GENERIC | 3 +- sys/arch/arm64/conf/RAMDISK | 3 +- sys/conf/files | 6 +- sys/dev/fdt/qcpmic.c | 4 +- sys/dev/fdt/qcspmi.c | 23 +- sys/dev/ic/qwz.c | 25568 ++++++++++++++++++ sys/dev/ic/qwzreg.h | 13253 +++++++++ sys/dev/ic/qwzvar.h | 2031 ++ sys/dev/pci/files.pci | 6 +- sys/dev/pci/if_qwz_pci.c | 4142 +++ sys/kern/kern_sysctl.c | 63 +- sys/kern/uipc_domain.c | 17 +- sys/netmpls/mpls_raw.c | 10 +- sys/sys/kernel.h | 4 +- usr.bin/dig/lib/lwres/include/lwres/lwres.h | 16 +- usr.bin/dig/lib/lwres/lwconfig.c | 109 +- usr.bin/ssh/cipher.c | 4 +- usr.bin/ssh/sshbuf.c | 18 +- usr.sbin/bgpctl/bgpctl.8 | 9 +- usr.sbin/bgpctl/bgpctl.c | 6 +- usr.sbin/bgpctl/output.c | 4 +- usr.sbin/bgpctl/output_json.c | 4 +- usr.sbin/bgpctl/parser.c | 3 +- usr.sbin/bgpd/bgpd.conf.5 | 11 +- usr.sbin/bgpd/bgpd.h | 5 +- usr.sbin/bgpd/config.c | 3 +- usr.sbin/bgpd/parse.y | 13 +- usr.sbin/bgpd/printconf.c | 4 +- usr.sbin/bgpd/rde.c | 64 +- usr.sbin/bgpd/rde.h | 34 +- usr.sbin/bgpd/rde_decide.c | 6 +- usr.sbin/bgpd/rde_rib.c | 28 +- usr.sbin/radiusd/radiusd.c | 20 +- usr.sbin/radiusd/radiusd_ipcp.c | 4 +- usr.sbin/radiusd/radiusd_local.h | 3 +- 42 files changed, 45418 insertions(+), 262 deletions(-) create mode 100644 sys/dev/ic/qwz.c create mode 100644 sys/dev/ic/qwzreg.h create mode 100644 sys/dev/ic/qwzvar.h create mode 100644 sys/dev/pci/if_qwz_pci.c diff --git a/distrib/notes/arm64/prep b/distrib/notes/arm64/prep index 46d5450dc..4fe153f83 100644 --- a/distrib/notes/arm64/prep +++ b/distrib/notes/arm64/prep @@ -1,4 +1,4 @@ -dnl $OpenBSD: prep,v 1.19 2023/10/18 06:45:45 kevlo Exp $ +dnl $OpenBSD: prep,v 1.21 2024/08/14 15:34:39 jsg Exp $ To perform an installation you must be able to interact with the console of the machine. In some cases this can be done by an attached monitor and keyboard. In others a serial console is required. @@ -54,6 +54,13 @@ Install on Apple Silicon: These machines do not come with UEFI firmware by default. In order to install SecBSD on these machine you need to run the Asahi Linux installer first in macOS or the macOS recovery environment. + + If "Erase All Content and Settings" has been run, the machine will + need to connect to Apple's servers to activate. A user-linked + Activation Lock can be removed by turning off Find My for the machine + through iCloud. An Apple account is otherwise not required for + activation or installation. + Instructions on how to download and run the Asahi Linux installer can be found at https://asahilinux.org/. Run it in macOS or the macOS recovery environment. @@ -77,7 +84,7 @@ Install on Apple Silicon: Now you can copy the miniroot or install image ("miniroot{:--:}OSrev.img" or "install{:--:}OSrev.img") to a USB - drive, plug it into one of the type-C ports on the machine and reset + drive, plug it into one of the ports on the machine and reset the machine to boot into the SecBSD installer. Install on Raspberry Pi: diff --git a/lib/libradius/radius.c b/lib/libradius/radius.c index db351ea97..3fec6d9a3 100644 --- a/lib/libradius/radius.c +++ b/lib/libradius/radius.c @@ -1,4 +1,4 @@ -/* $OpenBSD: radius.c,v 1.5 2024/08/08 09:16:37 yasuoka Exp $ */ +/* $OpenBSD: radius.c,v 1.6 2024/08/14 04:50:31 yasuoka Exp $ */ /*- * Copyright (c) 2009 Internet Initiative Japan Inc. @@ -261,7 +261,7 @@ radius_check_response_authenticator(const RADIUS_PACKET * packet, uint8_t authenticator[16]; radius_calc_response_authenticator(authenticator, packet, secret); - return (timingsafe_memcmp(authenticator, packet->pdata->authenticator, + return (timingsafe_bcmp(authenticator, packet->pdata->authenticator, 16)); } @@ -300,7 +300,7 @@ radius_check_accounting_request_authenticator(const RADIUS_PACKET * packet, radius_calc_accounting_request_authenticator(authenticator, packet, secret); - return (timingsafe_memcmp(authenticator, packet->pdata->authenticator, + return (timingsafe_bcmp(authenticator, packet->pdata->authenticator, 16)); } diff --git a/lib/libradius/radius_msgauth.c b/lib/libradius/radius_msgauth.c index 97170b65d..317fbe880 100644 --- a/lib/libradius/radius_msgauth.c +++ b/lib/libradius/radius_msgauth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: radius_msgauth.c,v 1.4 2024/08/08 09:16:37 yasuoka Exp $ */ +/* $OpenBSD: radius_msgauth.c,v 1.5 2024/08/14 04:50:31 yasuoka Exp $ */ /*- * Copyright (c) 2009 Internet Initiative Japan Inc. @@ -149,5 +149,5 @@ radius_check_message_authenticator(RADIUS_PACKET * packet, const char *secret) if (len != sizeof(ma1)) return (-1); - return (timingsafe_memcmp(ma0, ma1, sizeof(ma1))); + return (timingsafe_bcmp(ma0, ma1, sizeof(ma1))); } diff --git a/sys/arch/amd64/amd64/bus_dma.c b/sys/arch/amd64/amd64/bus_dma.c index 4e6e5fc95..1d58dfcc5 100644 --- a/sys/arch/amd64/amd64/bus_dma.c +++ b/sys/arch/amd64/amd64/bus_dma.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bus_dma.c,v 1.51 2019/06/09 12:52:04 kettenis Exp $ */ +/* $OpenBSD: bus_dma.c,v 1.52 2024/08/14 18:31:33 bluhm Exp $ */ /* $NetBSD: bus_dma.c,v 1.3 2003/05/07 21:33:58 fvdl Exp $ */ /*- @@ -108,8 +108,13 @@ _bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments, bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp) { struct bus_dmamap *map; + struct pglist mlist; + struct vm_page **pg, *pgnext; + size_t mapsize, sz, ssize; + vaddr_t va, sva; void *mapstore; - size_t mapsize; + int npages, error; + const struct kmem_dyn_mode *kd; /* * Allocate and initialize the DMA map. The end of the map @@ -125,6 +130,16 @@ _bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments, */ mapsize = sizeof(struct bus_dmamap) + (sizeof(bus_dma_segment_t) * (nsegments - 1)); + + /* allocate and use bounce buffers when running as SEV guest */ + if (cpu_sev_guestmode) { + /* this many pages plus one in case we get split */ + npages = round_page(size) / PAGE_SIZE + 1; + if (npages < nsegments) + npages = nsegments; + mapsize += sizeof(struct vm_page *) * npages; + } + if ((mapstore = malloc(mapsize, M_DEVBUF, (flags & BUS_DMA_NOWAIT) ? (M_NOWAIT|M_ZERO) : (M_WAITOK|M_ZERO))) == NULL) @@ -135,8 +150,59 @@ _bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments, map->_dm_segcnt = nsegments; map->_dm_maxsegsz = maxsegsz; map->_dm_boundary = boundary; + if (cpu_sev_guestmode) { + map->_dm_pages = (void *)&map->dm_segs[nsegments]; + map->_dm_npages = npages; + } map->_dm_flags = flags & ~(BUS_DMA_WAITOK|BUS_DMA_NOWAIT); + if (!cpu_sev_guestmode) { + *dmamp = map; + return (0); + } + + sz = npages << PGSHIFT; + kd = flags & BUS_DMA_NOWAIT ? &kd_trylock : &kd_waitok; + va = (vaddr_t)km_alloc(sz, &kv_any, &kp_none, kd); + if (va == 0) { + map->_dm_npages = 0; + free(map, M_DEVBUF, mapsize); + return (ENOMEM); + } + + TAILQ_INIT(&mlist); + error = uvm_pglistalloc(sz, 0, -1, PAGE_SIZE, 0, &mlist, nsegments, + (flags & BUS_DMA_NOWAIT) ? UVM_PLA_NOWAIT : UVM_PLA_WAITOK); + if (error) { + map->_dm_npages = 0; + km_free((void *)va, sz, &kv_any, &kp_none); + free(map, M_DEVBUF, mapsize); + return (ENOMEM); + } + + sva = va; + ssize = sz; + pgnext = TAILQ_FIRST(&mlist); + for (pg = map->_dm_pages; npages--; va += PAGE_SIZE, pg++) { + *pg = pgnext; + error = pmap_enter(pmap_kernel(), va, VM_PAGE_TO_PHYS(*pg), + PROT_READ | PROT_WRITE, + PROT_READ | PROT_WRITE | PMAP_WIRED | + PMAP_CANFAIL | PMAP_NOCRYPT); + if (error) { + pmap_update(pmap_kernel()); + map->_dm_npages = 0; + km_free((void *)sva, ssize, &kv_any, &kp_none); + free(map, M_DEVBUF, mapsize); + uvm_pglistfree(&mlist); + return (ENOMEM); + } + pgnext = TAILQ_NEXT(*pg, pageq); + bzero((void *)va, PAGE_SIZE); + } + pmap_update(pmap_kernel()); + map->_dm_pgva = sva; + *dmamp = map; return (0); } @@ -149,6 +215,21 @@ void _bus_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map) { size_t mapsize; + struct vm_page **pg; + struct pglist mlist; + + if (map->_dm_pgva) { + km_free((void *)map->_dm_pgva, map->_dm_npages << PGSHIFT, + &kv_any, &kp_none); + } + + if (map->_dm_pages) { + TAILQ_INIT(&mlist); + for (pg = map->_dm_pages; map->_dm_npages--; pg++) { + TAILQ_INSERT_TAIL(&mlist, *pg, pageq); + } + uvm_pglistfree(&mlist); + } mapsize = sizeof(struct bus_dmamap) + (sizeof(bus_dma_segment_t) * (map->_dm_segcnt - 1)); @@ -383,6 +464,7 @@ _bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map) */ map->dm_mapsize = 0; map->dm_nsegs = 0; + map->_dm_nused = 0; } /* @@ -393,7 +475,40 @@ void _bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t addr, bus_size_t size, int op) { - /* Nothing to do here. */ + bus_dma_segment_t *sg; + int i, off = addr; + bus_size_t l; + + if (!cpu_sev_guestmode) + return; + + for (i = map->_dm_segcnt, sg = map->dm_segs; size && i--; sg++) { + if (off >= sg->ds_len) { + off -= sg->ds_len; + continue; + } + + l = sg->ds_len - off; + if (l > size) + l = size; + size -= l; + + /* PREREAD and POSTWRITE are no-ops. */ + + /* READ: device -> memory */ + if (op & BUS_DMASYNC_POSTREAD) { + bcopy((void *)(sg->_ds_bounce_va + off), + (void *)(sg->_ds_va + off), l); + } + + /* WRITE: memory -> device */ + if (op & BUS_DMASYNC_PREWRITE) { + bcopy((void *)(sg->_ds_va + off), + (void *)(sg->_ds_bounce_va + off), l); + } + + off = 0; + } } /* @@ -566,9 +681,10 @@ _bus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf, { bus_size_t sgsize; bus_addr_t curaddr, lastaddr, baddr, bmask; - vaddr_t vaddr = (vaddr_t)buf; - int seg; + vaddr_t pgva = -1, vaddr = (vaddr_t)buf; + int seg, page, off; pmap_t pmap; + struct vm_page *pg; if (p != NULL) pmap = p->p_vmspace->vm_map.pmap; @@ -589,6 +705,18 @@ _bus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf, panic("Non dma-reachable buffer at curaddr %#lx(raw)", curaddr); + if (cpu_sev_guestmode) { + /* use bounce buffer */ + if (map->_dm_nused + 1 >= map->_dm_npages) + return (ENOMEM); + + off = vaddr & PAGE_MASK; + pg = map->_dm_pages[page = map->_dm_nused++]; + curaddr = VM_PAGE_TO_PHYS(pg) + off; + + pgva = map->_dm_pgva + (page << PGSHIFT) + off; + } + /* * Compute the segment size, and adjust counts. */ @@ -612,6 +740,8 @@ _bus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf, if (first) { map->dm_segs[seg].ds_addr = curaddr; map->dm_segs[seg].ds_len = sgsize; + map->dm_segs[seg]._ds_va = vaddr; + map->dm_segs[seg]._ds_bounce_va = pgva; first = 0; } else { if (curaddr == lastaddr && @@ -626,6 +756,8 @@ _bus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf, break; map->dm_segs[seg].ds_addr = curaddr; map->dm_segs[seg].ds_len = sgsize; + map->dm_segs[seg]._ds_va = vaddr; + map->dm_segs[seg]._ds_bounce_va = pgva; } } diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC index b74f7cc4e..8711eb82b 100644 --- a/sys/arch/amd64/conf/GENERIC +++ b/sys/arch/amd64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.524 2024/08/04 11:05:18 kettenis Exp $ +# $OpenBSD: GENERIC,v 1.525 2024/08/14 14:40:45 patrick Exp $ # # For further information on compiling SecBSD kernels, see the config(8) # man page. @@ -589,6 +589,7 @@ iwn* at pci? # Intel WiFi Link 4965/5000/1000/6000 iwm* at pci? # Intel WiFi Link 7xxx iwx* at pci? # Intel WiFi Link 22xxx qwx* at pci? # Qualcomm 802.11ax +#qwz* at pci? # Qualcomm 802.11be ral* at pci? # Ralink RT2500/RT2501/RT2600 ral* at cardbus? # Ralink RT2500/RT2501/RT2600 rtw* at pci? # Realtek 8180 diff --git a/sys/arch/amd64/conf/RAMDISK_CD b/sys/arch/amd64/conf/RAMDISK_CD index 61362361b..9ec9d3fed 100644 --- a/sys/arch/amd64/conf/RAMDISK_CD +++ b/sys/arch/amd64/conf/RAMDISK_CD @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK_CD,v 1.206 2024/05/09 17:05:22 mglocker Exp $ +# $OpenBSD: RAMDISK_CD,v 1.207 2024/08/14 14:40:45 patrick Exp $ machine amd64 maxusers 4 @@ -289,6 +289,7 @@ iwn* at pci? # Intel Wireless WiFi Link 4965AGN iwm* at pci? # Intel WiFi Link 7xxx iwx* at pci? # Intel WiFi Link 22xxx qwx* at pci? # Qualcomm 802.11ax +#qwz* at pci? # Qualcomm 802.11be ral* at pci? # Ralink RT2500/RT2501/RT2600 ral* at cardbus? # Ralink RT2500/RT2501/RT2600 rtw* at pci? # Realtek 8180 diff --git a/sys/arch/amd64/include/bus.h b/sys/arch/amd64/include/bus.h index 0538d3b1c..e5cb36fdb 100644 --- a/sys/arch/amd64/include/bus.h +++ b/sys/arch/amd64/include/bus.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bus.h,v 1.35 2020/10/28 09:58:57 jsg Exp $ */ +/* $OpenBSD: bus.h,v 1.36 2024/08/14 18:31:33 bluhm Exp $ */ /* $NetBSD: bus.h,v 1.6 1996/11/10 03:19:25 thorpej Exp $ */ /*- @@ -552,6 +552,9 @@ typedef struct bus_dmamap *bus_dmamap_t; struct bus_dma_segment { bus_addr_t ds_addr; /* DMA address */ bus_size_t ds_len; /* length of transfer */ + vaddr_t _ds_va; /* mapped loaded data */ + vaddr_t _ds_bounce_va; /* mapped bounced data */ + /* * Ugh. need this so can pass alignment down from bus_dmamem_alloc * to scatter gather maps. only the first one is used so the rest is @@ -655,6 +658,11 @@ struct bus_dmamap { void *_dm_cookie; /* cookie for bus-specific functions */ + struct vm_page **_dm_pages; /* replacement pages */ + vaddr_t _dm_pgva; /* those above -- mapped */ + int _dm_npages; /* number of pages allocated */ + int _dm_nused; /* number of pages replaced */ + /* * PUBLIC MEMBERS: these are used by machine-independent code. */ diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC index 40d8c9aaa..5bde678be 100644 --- a/sys/arch/arm64/conf/GENERIC +++ b/sys/arch/arm64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.288 2024/07/31 10:07:33 mglocker Exp $ +# $OpenBSD: GENERIC,v 1.289 2024/08/14 14:40:46 patrick Exp $ # # GENERIC machine description file # @@ -407,6 +407,7 @@ iwn* at pci? # Intel WiFi Link 4965/5000/1000/6000 iwm* at pci? # Intel WiFi Link 7xxx iwx* at pci? # Intel WiFi Link 22xxx qwx* at pci? # Qualcomm 802.11ax +#qwz* at pci? # Qualcomm 802.11be # PCI SCSI ahci* at pci? flags 0x0000 # AHCI SATA controllers diff --git a/sys/arch/arm64/conf/RAMDISK b/sys/arch/arm64/conf/RAMDISK index b6308431a..7cb88af6f 100644 --- a/sys/arch/arm64/conf/RAMDISK +++ b/sys/arch/arm64/conf/RAMDISK @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK,v 1.218 2024/07/31 10:07:33 mglocker Exp $ +# $OpenBSD: RAMDISK,v 1.219 2024/08/14 14:40:46 patrick Exp $ machine arm64 maxusers 4 @@ -322,6 +322,7 @@ athn* at pci? # Atheros AR9k (802.11a/g/n) bwfm* at pci? # Broadcom FullMAC iwx* at pci? # Intel WiFi Link 22xxx qwx* at pci? # Qualcomm 802.11ax +#qwz* at pci? # Qualcomm 802.11be # PCI SCSI ahci* at pci? flags 0x0000 # AHCI SATA controllers diff --git a/sys/conf/files b/sys/conf/files index a97e3ef22..e26cfb429 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.734 2024/07/13 13:20:44 bluhm Exp $ +# $OpenBSD: files,v 1.735 2024/08/14 14:40:46 patrick Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -429,6 +429,10 @@ file dev/ic/bwi.c bwi device qwx: ether, ifnet, ifmedia, firmload, wlan file dev/ic/qwx.c qwx +# Qualcomm 802.11be +device qwz: ether, ifnet, ifmedia, firmload, wlan +file dev/ic/qwz.c qwz + # Intel OnChip System Fabric device iosf file dev/ic/iosf.c iosf needs-flag diff --git a/sys/dev/fdt/qcpmic.c b/sys/dev/fdt/qcpmic.c index 3a93fd5b8..7b3a71176 100644 --- a/sys/dev/fdt/qcpmic.c +++ b/sys/dev/fdt/qcpmic.c @@ -1,4 +1,4 @@ -/* $OpenBSD: qcpmic.c,v 1.1 2022/11/08 19:40:08 patrick Exp $ */ +/* $OpenBSD: qcpmic.c,v 1.2 2024/08/14 10:54:58 mglocker Exp $ */ /* * Copyright (c) 2022 Patrick Wildt * @@ -117,7 +117,7 @@ qcpmic_read(struct qcpmic_softc *sc, uint16_t addr) err = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL, addr, ®, sizeof(reg)); if (err) - printf("%s: error (%u) reading 0x%x\n", sc->sc_dev.dv_xname, + printf("%s: error (%u) reading 0x%x", sc->sc_dev.dv_xname, err, addr); return reg; diff --git a/sys/dev/fdt/qcspmi.c b/sys/dev/fdt/qcspmi.c index 5cc807f71..656213afb 100644 --- a/sys/dev/fdt/qcspmi.c +++ b/sys/dev/fdt/qcspmi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: qcspmi.c,v 1.5 2024/07/04 21:54:38 kettenis Exp $ */ +/* $OpenBSD: qcspmi.c,v 1.6 2024/08/14 10:54:58 mglocker Exp $ */ /* * Copyright (c) 2022 Patrick Wildt * @@ -408,14 +408,23 @@ qcspmi_cmd_read(void *cookie, uint8_t sid, uint8_t cmd, uint16_t addr, SPMI_OBSV_OFF(sc, sc->sc_ee, apid) + SPMI_STATUS); if (reg & SPMI_STATUS_DONE) break; + if (reg & SPMI_STATUS_FAILURE) { + printf(": transaction failed\n"); + return EIO; + } + if (reg & SPMI_STATUS_DENIED) { + printf(": transaction denied\n"); + return EIO; + } + if (reg & SPMI_STATUS_DROPPED) { + printf(": transaction dropped\n"); + return EIO; + } } - if (i == 0) + if (i == 0) { + printf("\n"); return ETIMEDOUT; - - if (reg & SPMI_STATUS_FAILURE || - reg & SPMI_STATUS_DENIED || - reg & SPMI_STATUS_DROPPED) - return EIO; + } if (len > 0) { reg = HREAD4(sc, QCSPMI_REG_OBSRVR, diff --git a/sys/dev/ic/qwz.c b/sys/dev/ic/qwz.c new file mode 100644 index 000000000..9d4f5fcb6 --- /dev/null +++ b/sys/dev/ic/qwz.c @@ -0,0 +1,25568 @@ +/* $OpenBSD: qwz.c,v 1.1 2024/08/14 14:40:46 patrick Exp $ */ + +/* + * Copyright 2023 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (c) 2018-2019 The Linux Foundation. + * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of [Owner Organization] nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Driver for Qualcomm Technologies 802.11be chipset. + */ + +#include "bpfilter.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef __HAVE_FDT +#include +#endif + +#if NBPFILTER > 0 +#include +#endif +#include +#include + +#include +#include + +#include +#include + +/* XXX linux porting goo */ +#ifdef __LP64__ +#define BITS_PER_LONG 64 +#else +#define BITS_PER_LONG 32 +#endif +#define GENMASK(h, l) (((~0UL) >> (BITS_PER_LONG - (h) - 1)) & ((~0UL) << (l))) +#define __bf_shf(x) (__builtin_ffsll(x) - 1) +#define ffz(x) ffs(~(x)) +#define FIELD_GET(_m, _v) ((typeof(_m))(((_v) & (_m)) >> __bf_shf(_m))) +#define FIELD_PREP(_m, _v) (((typeof(_m))(_v) << __bf_shf(_m)) & (_m)) +#define BIT(x) (1UL << (x)) +#define test_bit(i, a) ((a) & (1 << (i))) +#define clear_bit(i, a) ((a)) &= ~(1 << (i)) +#define set_bit(i, a) ((a)) |= (1 << (i)) +#define container_of(ptr, type, member) ({ \ + const __typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* #define QWZ_DEBUG */ + +#include +#include + +#ifdef QWZ_DEBUG +uint32_t qwz_debug = 0 + | QWZ_D_MISC +/* | QWZ_D_MHI */ +/* | QWZ_D_QMI */ +/* | QWZ_D_WMI */ +/* | QWZ_D_HTC */ +/* | QWZ_D_HTT */ +/* | QWZ_D_MAC */ +/* | QWZ_D_MGMT */ + ; +#endif + +int qwz_ce_init_pipes(struct qwz_softc *); +int qwz_hal_srng_src_num_free(struct qwz_softc *, struct hal_srng *, int); +int qwz_ce_per_engine_service(struct qwz_softc *, uint16_t); +int qwz_hal_srng_setup(struct qwz_softc *, enum hal_ring_type, int, int, + struct hal_srng_params *); +int qwz_ce_send(struct qwz_softc *, struct mbuf *, uint8_t, uint16_t); +int qwz_htc_connect_service(struct qwz_htc *, struct qwz_htc_svc_conn_req *, + struct qwz_htc_svc_conn_resp *); +void qwz_hal_srng_shadow_update_hp_tp(struct qwz_softc *, struct hal_srng *); +void qwz_wmi_free_dbring_caps(struct qwz_softc *); +int qwz_wmi_set_peer_param(struct qwz_softc *, uint8_t *, uint32_t, + uint32_t, uint32_t, uint32_t); +int qwz_wmi_peer_rx_reorder_queue_setup(struct qwz_softc *, int, int, + uint8_t *, uint64_t, uint8_t, uint8_t, uint32_t); +const void **qwz_wmi_tlv_parse_alloc(struct qwz_softc *, const void *, size_t); +int qwz_core_init(struct qwz_softc *); +int qwz_qmi_event_server_arrive(struct qwz_softc *); +int qwz_mac_register(struct qwz_softc *); +int qwz_mac_start(struct qwz_softc *); +void qwz_mac_scan_finish(struct qwz_softc *); +int qwz_mac_mgmt_tx_wmi(struct qwz_softc *, struct qwz_vif *, uint8_t, + struct ieee80211_node *, struct mbuf *); +int qwz_dp_tx(struct qwz_softc *, struct qwz_vif *, uint8_t, + struct ieee80211_node *, struct mbuf *); +int qwz_dp_tx_send_reo_cmd(struct qwz_softc *, struct dp_rx_tid *, + enum hal_reo_cmd_type , struct ath12k_hal_reo_cmd *, + void (*func)(struct qwz_dp *, void *, enum hal_reo_cmd_status)); +void qwz_dp_rx_deliver_msdu(struct qwz_softc *, struct qwz_rx_msdu *); +void qwz_dp_service_mon_ring(void *); +void qwz_peer_frags_flush(struct qwz_softc *, struct ath12k_peer *); +int qwz_wmi_vdev_install_key(struct qwz_softc *, + struct wmi_vdev_install_key_arg *, uint8_t); +int qwz_dp_peer_rx_pn_replay_config(struct qwz_softc *, struct qwz_vif *, + struct ieee80211_node *, struct ieee80211_key *, int); +void qwz_setkey_clear(struct qwz_softc *); + +int qwz_scan(struct qwz_softc *); +void qwz_scan_abort(struct qwz_softc *); +int qwz_auth(struct qwz_softc *); +int qwz_deauth(struct qwz_softc *); +int qwz_run(struct qwz_softc *); +int qwz_run_stop(struct qwz_softc *); + +struct ieee80211_node * +qwz_node_alloc(struct ieee80211com *ic) +{ + struct qwz_node *nq; + + nq = malloc(sizeof(struct qwz_node), M_DEVBUF, M_NOWAIT | M_ZERO); + nq->peer.peer_id = HAL_INVALID_PEERID; + return (struct ieee80211_node *)nq; +} + +int +qwz_init(struct ifnet *ifp) +{ + int error; + struct qwz_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + + sc->fw_mode = ATH12K_FIRMWARE_MODE_NORMAL; + /* + * There are several known hardware/software crypto issues + * on wcn6855 devices, firmware 0x1106196e. It is unclear + * if these are driver or firmware bugs. + * + * 1) Broadcast/Multicast frames will only be received on + * encrypted networks if hardware crypto is used and a + * CCMP group key is used. Otherwise such frames never + * even trigger an interrupt. This breaks ARP and IPv6. + * This issue is known to affect the Linux ath12k vendor + * driver when software crypto mode is selected. + * Workaround: Use hardware crypto on WPA2 networks. + * However, even with hardware crypto broadcast frames + * are never received if TKIP is used as the WPA2 group + * cipher and we have no workaround for this. + * + * 2) Adding WEP keys for hardware crypto crashes the firmware. + * Presumably, lack of WEP support is deliberate because the + * Linux ath12k vendor driver rejects attempts to install + * WEP keys to hardware. + * Workaround: Use software crypto if WEP is enabled. + * This suffers from the broadcast issues mentioned above. + * + * 3) A WPA1 group key handshake message from the AP is never + * received if hardware crypto is used. + * Workaround: Use software crypto if WPA1 is enabled. + * This suffers from the broadcast issues mentioned above, + * even on WPA2 networks when WPA1 and WPA2 are both enabled. + * On OpenBSD, WPA1 is disabled by default. + * + * The only known fully working configurations are unencrypted + * networks, and WPA2/CCMP-only networks provided WPA1 remains + * disabled. + */ + if ((ic->ic_flags & IEEE80211_F_WEPON) || + (ic->ic_rsnprotos & IEEE80211_PROTO_WPA)) + sc->crypto_mode = ATH12K_CRYPT_MODE_SW; + else + sc->crypto_mode = ATH12K_CRYPT_MODE_HW; + sc->frame_mode = ATH12K_HW_TXRX_NATIVE_WIFI; + ic->ic_state = IEEE80211_S_INIT; + sc->ns_nstate = IEEE80211_S_INIT; + sc->scan.state = ATH12K_SCAN_IDLE; + sc->vdev_id_11d_scan = QWZ_11D_INVALID_VDEV_ID; + + error = qwz_core_init(sc); + if (error) + return error; + + memset(&sc->qrtr_server, 0, sizeof(sc->qrtr_server)); + sc->qrtr_server.node = QRTR_NODE_BCAST; + + /* wait for QRTR init to be done */ + while (sc->qrtr_server.node == QRTR_NODE_BCAST) { + error = tsleep_nsec(&sc->qrtr_server, 0, "qwzqrtr", + SEC_TO_NSEC(5)); + if (error) { + printf("%s: qrtr init timeout\n", sc->sc_dev.dv_xname); + return error; + } + } + + error = qwz_qmi_event_server_arrive(sc); + if (error) + return error; + + if (sc->attached) { + /* Update MAC in case the upper layers changed it. */ + IEEE80211_ADDR_COPY(ic->ic_myaddr, + ((struct arpcom *)ifp)->ac_enaddr); + } else { + sc->attached = 1; + + /* Configure channel information obtained from firmware. */ + ieee80211_channel_init(ifp); + + /* Configure initial MAC address. */ + error = if_setlladdr(ifp, ic->ic_myaddr); + if (error) + printf("%s: could not set MAC address %s: %d\n", + sc->sc_dev.dv_xname, ether_sprintf(ic->ic_myaddr), + error); + + ieee80211_media_init(ifp, qwz_media_change, + ieee80211_media_status); + } + + if (ifp->if_flags & IFF_UP) { + refcnt_init(&sc->task_refs); + + ifq_clr_oactive(&ifp->if_snd); + ifp->if_flags |= IFF_RUNNING; + + error = qwz_mac_start(sc); + if (error) + return error; + + ieee80211_begin_scan(ifp); + } + + return 0; +} + +void +qwz_add_task(struct qwz_softc *sc, struct taskq *taskq, struct task *task) +{ + int s = splnet(); + + if (test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags)) { + splx(s); + return; + } + + refcnt_take(&sc->task_refs); + if (!task_add(taskq, task)) + refcnt_rele_wake(&sc->task_refs); + splx(s); +} + +void +qwz_del_task(struct qwz_softc *sc, struct taskq *taskq, struct task *task) +{ + if (task_del(taskq, task)) + refcnt_rele(&sc->task_refs); +} + +void +qwz_stop(struct ifnet *ifp) +{ + struct qwz_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + int s = splnet(); + + rw_assert_wrlock(&sc->ioctl_rwl); + + timeout_del(&sc->mon_reap_timer); + + /* Disallow new tasks. */ + set_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags); + + /* Cancel scheduled tasks and let any stale tasks finish up. */ + task_del(systq, &sc->init_task); + qwz_del_task(sc, sc->sc_nswq, &sc->newstate_task); + qwz_del_task(sc, systq, &sc->setkey_task); + refcnt_finalize(&sc->task_refs, "qwzstop"); + + qwz_setkey_clear(sc); + + clear_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags); + + ifp->if_timer = sc->sc_tx_timer = 0; + + ifp->if_flags &= ~IFF_RUNNING; + ifq_clr_oactive(&ifp->if_snd); + + sc->sc_newstate(ic, IEEE80211_S_INIT, -1); + sc->ns_nstate = IEEE80211_S_INIT; + sc->scan.state = ATH12K_SCAN_IDLE; + sc->vdev_id_11d_scan = QWZ_11D_INVALID_VDEV_ID; + sc->pdevs_active = 0; + + /* power off hardware */ + qwz_core_deinit(sc); + + splx(s); +} + +void +qwz_free_firmware(struct qwz_softc *sc) +{ + int i; + + for (i = 0; i < nitems(sc->fw_img); i++) { + free(sc->fw_img[i].data, M_DEVBUF, sc->fw_img[i].size); + sc->fw_img[i].data = NULL; + sc->fw_img[i].size = 0; + } +} + +int +qwz_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct qwz_softc *sc = ifp->if_softc; + int s, err = 0; + + /* + * Prevent processes from entering this function while another + * process is tsleep'ing in it. + */ + err = rw_enter(&sc->ioctl_rwl, RW_WRITE | RW_INTR); + if (err) + return err; + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + /* FALLTHROUGH */ + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) { + /* Force reload of firmware image from disk. */ + qwz_free_firmware(sc); + err = qwz_init(ifp); + } + } else { + if (ifp->if_flags & IFF_RUNNING) + qwz_stop(ifp); + } + break; + + default: + err = ieee80211_ioctl(ifp, cmd, data); + } + + if (err == ENETRESET) { + err = 0; + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) { + qwz_stop(ifp); + err = qwz_init(ifp); + } + } + + splx(s); + rw_exit(&sc->ioctl_rwl); + + return err; +} + +int +qwz_tx(struct qwz_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211_frame *wh; + struct qwz_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */ + uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */ + uint8_t frame_type; + + wh = mtod(m, struct ieee80211_frame *); + frame_type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + +#if NBPFILTER > 0 + if (sc->sc_drvbpf != NULL) { + struct qwz_tx_radiotap_header *tap = &sc->sc_txtap; + + bpf_mtap_hdr(sc->sc_drvbpf, tap, sc->sc_txtap_len, + m, BPF_DIRECTION_OUT); + } +#endif + + if (frame_type == IEEE80211_FC0_TYPE_MGT) + return qwz_mac_mgmt_tx_wmi(sc, arvif, pdev_id, ni, m); + + return qwz_dp_tx(sc, arvif, pdev_id, ni, m); +} + +void +qwz_start(struct ifnet *ifp) +{ + struct qwz_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct ether_header *eh; + struct mbuf *m; + + if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd)) + return; + + for (;;) { + /* why isn't this done per-queue? */ + if (sc->qfullmsk != 0) { + ifq_set_oactive(&ifp->if_snd); + break; + } + + /* need to send management frames even if we're not RUNning */ + m = mq_dequeue(&ic->ic_mgtq); + if (m) { + ni = m->m_pkthdr.ph_cookie; + goto sendit; + } + + if (ic->ic_state != IEEE80211_S_RUN || + (ic->ic_xflags & IEEE80211_F_TX_MGMT_ONLY)) + break; + + m = ifq_dequeue(&ifp->if_snd); + if (!m) + break; + if (m->m_len < sizeof (*eh) && + (m = m_pullup(m, sizeof (*eh))) == NULL) { + ifp->if_oerrors++; + continue; + } +#if NBPFILTER > 0 + if (ifp->if_bpf != NULL) + bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); +#endif + if ((m = ieee80211_encap(ifp, m, &ni)) == NULL) { + ifp->if_oerrors++; + continue; + } + + sendit: +#if NBPFILTER > 0 + if (ic->ic_rawbpf != NULL) + bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT); +#endif + if (qwz_tx(sc, m, ni) != 0) { + ieee80211_release_node(ic, ni); + ifp->if_oerrors++; + continue; + } + + if (ifp->if_flags & IFF_UP) + ifp->if_timer = 1; + } +} + +void +qwz_watchdog(struct ifnet *ifp) +{ + struct qwz_softc *sc = ifp->if_softc; + + ifp->if_timer = 0; + + if (sc->sc_tx_timer > 0) { + if (--sc->sc_tx_timer == 0) { + printf("%s: device timeout\n", sc->sc_dev.dv_xname); + if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags)) + task_add(systq, &sc->init_task); + ifp->if_oerrors++; + return; + } + ifp->if_timer = 1; + } + + ieee80211_watchdog(ifp); +} + +int +qwz_media_change(struct ifnet *ifp) +{ + int err; + + err = ieee80211_media_change(ifp); + if (err != ENETRESET) + return err; + + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) { + qwz_stop(ifp); + err = qwz_init(ifp); + } + + return err; +} + +int +qwz_queue_setkey_cmd(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k, int cmd) +{ + struct qwz_softc *sc = ic->ic_softc; + struct qwz_setkey_task_arg *a; + + if (sc->setkey_nkeys >= nitems(sc->setkey_arg) || + k->k_id > WMI_MAX_KEY_INDEX) + return ENOSPC; + + a = &sc->setkey_arg[sc->setkey_cur]; + a->ni = ieee80211_ref_node(ni); + a->k = k; + a->cmd = cmd; + sc->setkey_cur = (sc->setkey_cur + 1) % nitems(sc->setkey_arg); + sc->setkey_nkeys++; + qwz_add_task(sc, systq, &sc->setkey_task); + return EBUSY; +} + +int +qwz_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct qwz_softc *sc = ic->ic_softc; + + if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags) || + k->k_cipher == IEEE80211_CIPHER_WEP40 || + k->k_cipher == IEEE80211_CIPHER_WEP104) + return ieee80211_set_key(ic, ni, k); + + return qwz_queue_setkey_cmd(ic, ni, k, QWZ_ADD_KEY); +} + +void +qwz_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct qwz_softc *sc = ic->ic_softc; + + if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags) || + k->k_cipher == IEEE80211_CIPHER_WEP40 || + k->k_cipher == IEEE80211_CIPHER_WEP104) { + ieee80211_delete_key(ic, ni, k); + return; + } + + if (ic->ic_state != IEEE80211_S_RUN) { + /* Keys removed implicitly when firmware station is removed. */ + return; + } + + /* + * net80211 calls us with a NULL node when deleting group keys, + * but firmware expects a MAC address in the command. + */ + if (ni == NULL) + ni = ic->ic_bss; + + qwz_queue_setkey_cmd(ic, ni, k, QWZ_DEL_KEY); +} + +int +qwz_wmi_install_key_cmd(struct qwz_softc *sc, struct qwz_vif *arvif, + uint8_t *macaddr, struct ieee80211_key *k, uint32_t flags, + int delete_key) +{ + int ret; + struct wmi_vdev_install_key_arg arg = { + .vdev_id = arvif->vdev_id, + .key_idx = k->k_id, + .key_len = k->k_len, + .key_data = k->k_key, + .key_flags = flags, + .macaddr = macaddr, + }; + uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */ +#ifdef notyet + lockdep_assert_held(&arvif->ar->conf_mutex); + + reinit_completion(&ar->install_key_done); +#endif + if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags)) + return 0; + + if (delete_key) { + arg.key_cipher = WMI_CIPHER_NONE; + arg.key_data = NULL; + } else { + switch (k->k_cipher) { + case IEEE80211_CIPHER_CCMP: + arg.key_cipher = WMI_CIPHER_AES_CCM; +#if 0 + /* TODO: Re-check if flag is valid */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; +#endif + break; + case IEEE80211_CIPHER_TKIP: + arg.key_cipher = WMI_CIPHER_TKIP; + arg.key_txmic_len = 8; + arg.key_rxmic_len = 8; + break; +#if 0 + case WLAN_CIPHER_SUITE_CCMP_256: + arg.key_cipher = WMI_CIPHER_AES_CCM; + break; + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + arg.key_cipher = WMI_CIPHER_AES_GCM; + break; +#endif + default: + printf("%s: cipher %u is not supported\n", + sc->sc_dev.dv_xname, k->k_cipher); + return EOPNOTSUPP; + } +#if 0 + if (test_bit(ATH12K_FLAG_RAW_MODE, &ar->ab->dev_flags)) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV | + IEEE80211_KEY_FLAG_RESERVE_TAILROOM; +#endif + } + + sc->install_key_done = 0; + ret = qwz_wmi_vdev_install_key(sc, &arg, pdev_id); + if (ret) + return ret; + + while (!sc->install_key_done) { + ret = tsleep_nsec(&sc->install_key_done, 0, "qwzinstkey", + SEC_TO_NSEC(1)); + if (ret) { + printf("%s: install key timeout\n", + sc->sc_dev.dv_xname); + return -1; + } + } + + return sc->install_key_status; +} + +int +qwz_add_sta_key(struct qwz_softc *sc, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct qwz_node *nq = (struct qwz_node *)ni; + struct ath12k_peer *peer = &nq->peer; + struct qwz_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */ + int ret = 0; + uint32_t flags = 0; + const int want_keymask = (QWZ_NODE_FLAG_HAVE_PAIRWISE_KEY | + QWZ_NODE_FLAG_HAVE_GROUP_KEY); + + /* + * Flush the fragments cache during key (re)install to + * ensure all frags in the new frag list belong to the same key. + */ + qwz_peer_frags_flush(sc, peer); + + if (k->k_flags & IEEE80211_KEY_GROUP) + flags |= WMI_KEY_GROUP; + else + flags |= WMI_KEY_PAIRWISE; + + ret = qwz_wmi_install_key_cmd(sc, arvif, ni->ni_macaddr, k, flags, 0); + if (ret) { + printf("%s: installing crypto key failed (%d)\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = qwz_dp_peer_rx_pn_replay_config(sc, arvif, ni, k, 0); + if (ret) { + printf("%s: failed to offload PN replay detection %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + if (k->k_flags & IEEE80211_KEY_GROUP) + nq->flags |= QWZ_NODE_FLAG_HAVE_GROUP_KEY; + else + nq->flags |= QWZ_NODE_FLAG_HAVE_PAIRWISE_KEY; + + if ((nq->flags & want_keymask) == want_keymask) { + DPRINTF("marking port %s valid\n", + ether_sprintf(ni->ni_macaddr)); + ni->ni_port_valid = 1; + ieee80211_set_link_state(ic, LINK_STATE_UP); + } + + return 0; +} + +int +qwz_del_sta_key(struct qwz_softc *sc, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct qwz_node *nq = (struct qwz_node *)ni; + struct qwz_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */ + int ret = 0; + + ret = qwz_wmi_install_key_cmd(sc, arvif, ni->ni_macaddr, k, 0, 1); + if (ret) { + printf("%s: deleting crypto key failed (%d)\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = qwz_dp_peer_rx_pn_replay_config(sc, arvif, ni, k, 1); + if (ret) { + printf("%s: failed to disable PN replay detection %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + if (k->k_flags & IEEE80211_KEY_GROUP) + nq->flags &= ~QWZ_NODE_FLAG_HAVE_GROUP_KEY; + else + nq->flags &= ~QWZ_NODE_FLAG_HAVE_PAIRWISE_KEY; + + return 0; +} + +void +qwz_setkey_task(void *arg) +{ + struct qwz_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct qwz_setkey_task_arg *a; + int err = 0, s = splnet(); + + while (sc->setkey_nkeys > 0) { + if (err || test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags)) + break; + a = &sc->setkey_arg[sc->setkey_tail]; + KASSERT(a->cmd == QWZ_ADD_KEY || a->cmd == QWZ_DEL_KEY); + if (ic->ic_state == IEEE80211_S_RUN) { + if (a->cmd == QWZ_ADD_KEY) + err = qwz_add_sta_key(sc, a->ni, a->k); + else + err = qwz_del_sta_key(sc, a->ni, a->k); + } + ieee80211_release_node(ic, a->ni); + a->ni = NULL; + a->k = NULL; + sc->setkey_tail = (sc->setkey_tail + 1) % + nitems(sc->setkey_arg); + sc->setkey_nkeys--; + } + + refcnt_rele_wake(&sc->task_refs); + splx(s); +} + +void +qwz_setkey_clear(struct qwz_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct qwz_setkey_task_arg *a; + + while (sc->setkey_nkeys > 0) { + a = &sc->setkey_arg[sc->setkey_tail]; + ieee80211_release_node(ic, a->ni); + a->ni = NULL; + sc->setkey_tail = (sc->setkey_tail + 1) % + nitems(sc->setkey_arg); + sc->setkey_nkeys--; + } + memset(sc->setkey_arg, 0, sizeof(sc->setkey_arg)); + sc->setkey_cur = sc->setkey_tail = sc->setkey_nkeys = 0; +} + +int +qwz_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +{ + struct ifnet *ifp = &ic->ic_if; + struct qwz_softc *sc = ifp->if_softc; + + /* + * Prevent attempts to transition towards the same state, unless + * we are scanning in which case a SCAN -> SCAN transition + * triggers another scan iteration. And AUTH -> AUTH is needed + * to support band-steering. + */ + if (sc->ns_nstate == nstate && nstate != IEEE80211_S_SCAN && + nstate != IEEE80211_S_AUTH) + return 0; + if (ic->ic_state == IEEE80211_S_RUN) { +#if 0 + qwz_del_task(sc, systq, &sc->ba_task); +#endif + qwz_del_task(sc, systq, &sc->setkey_task); + qwz_setkey_clear(sc); +#if 0 + qwz_del_task(sc, systq, &sc->bgscan_done_task); +#endif + } + + sc->ns_nstate = nstate; + sc->ns_arg = arg; + + qwz_add_task(sc, sc->sc_nswq, &sc->newstate_task); + + return 0; +} + +void +qwz_newstate_task(void *arg) +{ + struct qwz_softc *sc = (struct qwz_softc *)arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + enum ieee80211_state nstate = sc->ns_nstate; + enum ieee80211_state ostate = ic->ic_state; + int err = 0, s = splnet(); + + if (test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags)) { + /* qwz_stop() is waiting for us. */ + refcnt_rele_wake(&sc->task_refs); + splx(s); + return; + } + + if (ostate == IEEE80211_S_SCAN) { + if (nstate == ostate) { + if (sc->scan.state != ATH12K_SCAN_IDLE) { + refcnt_rele_wake(&sc->task_refs); + splx(s); + return; + } + /* Firmware is no longer scanning. Do another scan. */ + goto next_scan; + } + } + + if (nstate <= ostate) { + switch (ostate) { + case IEEE80211_S_RUN: + err = qwz_run_stop(sc); + if (err) + goto out; + /* FALLTHROUGH */ + case IEEE80211_S_ASSOC: + case IEEE80211_S_AUTH: + if (nstate <= IEEE80211_S_AUTH) { + err = qwz_deauth(sc); + if (err) + goto out; + } + /* FALLTHROUGH */ + case IEEE80211_S_SCAN: + case IEEE80211_S_INIT: + break; + } + + /* Die now if qwz_stop() was called while we were sleeping. */ + if (test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags)) { + refcnt_rele_wake(&sc->task_refs); + splx(s); + return; + } + } + + switch (nstate) { + case IEEE80211_S_INIT: + break; + + case IEEE80211_S_SCAN: +next_scan: + err = qwz_scan(sc); + if (err) + break; + if (ifp->if_flags & IFF_DEBUG) + printf("%s: %s -> %s\n", ifp->if_xname, + ieee80211_state_name[ic->ic_state], + ieee80211_state_name[IEEE80211_S_SCAN]); +#if 0 + if ((sc->sc_flags & QWZ_FLAG_BGSCAN) == 0) { +#endif + ieee80211_set_link_state(ic, LINK_STATE_DOWN); + ieee80211_node_cleanup(ic, ic->ic_bss); +#if 0 + } +#endif + ic->ic_state = IEEE80211_S_SCAN; + refcnt_rele_wake(&sc->task_refs); + splx(s); + return; + + case IEEE80211_S_AUTH: + err = qwz_auth(sc); + break; + + case IEEE80211_S_ASSOC: + break; + + case IEEE80211_S_RUN: + err = qwz_run(sc); + break; + } +out: + if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags)) { + if (err) + task_add(systq, &sc->init_task); + else + sc->sc_newstate(ic, nstate, sc->ns_arg); + } + refcnt_rele_wake(&sc->task_refs); + splx(s); +} + +struct cfdriver qwz_cd = { + NULL, "qwz", DV_IFNET +}; + +void +qwz_init_wmi_config_qca6390(struct qwz_softc *sc, + struct target_resource_config *config) +{ + config->num_vdevs = 4; + config->num_peers = 16; + config->num_tids = 32; + + config->num_offload_peers = 3; + config->num_offload_reorder_buffs = 3; + config->num_peer_keys = TARGET_NUM_PEER_KEYS; + config->ast_skid_limit = TARGET_AST_SKID_LIMIT; + config->tx_chain_mask = (1 << sc->target_caps.num_rf_chains) - 1; + config->rx_chain_mask = (1 << sc->target_caps.num_rf_chains) - 1; + config->rx_timeout_pri[0] = TARGET_RX_TIMEOUT_LO_PRI; + config->rx_timeout_pri[1] = TARGET_RX_TIMEOUT_LO_PRI; + config->rx_timeout_pri[2] = TARGET_RX_TIMEOUT_LO_PRI; + config->rx_timeout_pri[3] = TARGET_RX_TIMEOUT_HI_PRI; + config->rx_decap_mode = TARGET_DECAP_MODE_NATIVE_WIFI; + config->scan_max_pending_req = TARGET_SCAN_MAX_PENDING_REQS; + config->bmiss_offload_max_vdev = TARGET_BMISS_OFFLOAD_MAX_VDEV; + config->roam_offload_max_vdev = TARGET_ROAM_OFFLOAD_MAX_VDEV; + config->roam_offload_max_ap_profiles = TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES; + config->num_mcast_groups = 0; + config->num_mcast_table_elems = 0; + config->mcast2ucast_mode = 0; + config->tx_dbg_log_size = TARGET_TX_DBG_LOG_SIZE; + config->num_wds_entries = 0; + config->dma_burst_size = 0; + config->rx_skip_defrag_timeout_dup_detection_check = 0; + config->vow_config = TARGET_VOW_CONFIG; + config->gtk_offload_max_vdev = 2; + config->num_msdu_desc = 0x400; + config->beacon_tx_offload_max_vdev = 2; + config->rx_batchmode = TARGET_RX_BATCHMODE; + + config->peer_map_unmap_v2_support = 0; + config->use_pdev_id = 1; + config->max_frag_entries = 0xa; + config->num_tdls_vdevs = 0x1; + config->num_tdls_conn_table_entries = 8; + config->beacon_tx_offload_max_vdev = 0x2; + config->num_multicast_filter_entries = 0x20; + config->num_wow_filters = 0x16; + config->num_keep_alive_pattern = 0; + config->flag1 |= WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64; +} + +void +qwz_hw_ipq8074_reo_setup(struct qwz_softc *sc) +{ + uint32_t reo_base = HAL_SEQ_WCSS_UMAC_REO_REG; + uint32_t val; + /* Each hash entry uses three bits to map to a particular ring. */ + uint32_t ring_hash_map = HAL_HASH_ROUTING_RING_SW1 << 0 | + HAL_HASH_ROUTING_RING_SW2 << 3 | + HAL_HASH_ROUTING_RING_SW3 << 6 | + HAL_HASH_ROUTING_RING_SW4 << 9 | + HAL_HASH_ROUTING_RING_SW1 << 12 | + HAL_HASH_ROUTING_RING_SW2 << 15 | + HAL_HASH_ROUTING_RING_SW3 << 18 | + HAL_HASH_ROUTING_RING_SW4 << 21; + + val = sc->ops.read32(sc, reo_base + HAL_REO1_GEN_ENABLE); + + val &= ~HAL_REO1_GEN_ENABLE_FRAG_DST_RING; + val |= FIELD_PREP(HAL_REO1_GEN_ENABLE_FRAG_DST_RING, + HAL_SRNG_RING_ID_REO2SW1) | + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE, 1) | + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE, 1); + sc->ops.write32(sc, reo_base + HAL_REO1_GEN_ENABLE, val); + + sc->ops.write32(sc, reo_base + HAL_REO1_AGING_THRESH_IX_0(sc), + HAL_DEFAULT_REO_TIMEOUT_USEC); + sc->ops.write32(sc, reo_base + HAL_REO1_AGING_THRESH_IX_1(sc), + HAL_DEFAULT_REO_TIMEOUT_USEC); + sc->ops.write32(sc, reo_base + HAL_REO1_AGING_THRESH_IX_2(sc), + HAL_DEFAULT_REO_TIMEOUT_USEC); + sc->ops.write32(sc, reo_base + HAL_REO1_AGING_THRESH_IX_3(sc), + HAL_DEFAULT_REO_TIMEOUT_USEC); + + sc->ops.write32(sc, reo_base + HAL_REO1_DEST_RING_CTRL_IX_0, + FIELD_PREP(HAL_REO_DEST_RING_CTRL_HASH_RING_MAP, ring_hash_map)); + sc->ops.write32(sc, reo_base + HAL_REO1_DEST_RING_CTRL_IX_1, + FIELD_PREP(HAL_REO_DEST_RING_CTRL_HASH_RING_MAP, ring_hash_map)); + sc->ops.write32(sc, reo_base + HAL_REO1_DEST_RING_CTRL_IX_2, + FIELD_PREP(HAL_REO_DEST_RING_CTRL_HASH_RING_MAP, ring_hash_map)); + sc->ops.write32(sc, reo_base + HAL_REO1_DEST_RING_CTRL_IX_3, + FIELD_PREP(HAL_REO_DEST_RING_CTRL_HASH_RING_MAP, ring_hash_map)); +} + +void +qwz_init_wmi_config_ipq8074(struct qwz_softc *sc, + struct target_resource_config *config) +{ + config->num_vdevs = sc->num_radios * TARGET_NUM_VDEVS(sc); + + if (sc->num_radios == 2) { + config->num_peers = TARGET_NUM_PEERS(sc, DBS); + config->num_tids = TARGET_NUM_TIDS(sc, DBS); + } else if (sc->num_radios == 3) { + config->num_peers = TARGET_NUM_PEERS(sc, DBS_SBS); + config->num_tids = TARGET_NUM_TIDS(sc, DBS_SBS); + } else { + /* Control should not reach here */ + config->num_peers = TARGET_NUM_PEERS(sc, SINGLE); + config->num_tids = TARGET_NUM_TIDS(sc, SINGLE); + } + config->num_offload_peers = TARGET_NUM_OFFLD_PEERS; + config->num_offload_reorder_buffs = TARGET_NUM_OFFLD_REORDER_BUFFS; + config->num_peer_keys = TARGET_NUM_PEER_KEYS; + config->ast_skid_limit = TARGET_AST_SKID_LIMIT; + config->tx_chain_mask = (1 << sc->target_caps.num_rf_chains) - 1; + config->rx_chain_mask = (1 << sc->target_caps.num_rf_chains) - 1; + config->rx_timeout_pri[0] = TARGET_RX_TIMEOUT_LO_PRI; + config->rx_timeout_pri[1] = TARGET_RX_TIMEOUT_LO_PRI; + config->rx_timeout_pri[2] = TARGET_RX_TIMEOUT_LO_PRI; + config->rx_timeout_pri[3] = TARGET_RX_TIMEOUT_HI_PRI; + + if (test_bit(ATH12K_FLAG_RAW_MODE, sc->sc_flags)) + config->rx_decap_mode = TARGET_DECAP_MODE_RAW; + else + config->rx_decap_mode = TARGET_DECAP_MODE_NATIVE_WIFI; + + config->scan_max_pending_req = TARGET_SCAN_MAX_PENDING_REQS; + config->bmiss_offload_max_vdev = TARGET_BMISS_OFFLOAD_MAX_VDEV; + config->roam_offload_max_vdev = TARGET_ROAM_OFFLOAD_MAX_VDEV; + config->roam_offload_max_ap_profiles = TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES; + config->num_mcast_groups = TARGET_NUM_MCAST_GROUPS; + config->num_mcast_table_elems = TARGET_NUM_MCAST_TABLE_ELEMS; + config->mcast2ucast_mode = TARGET_MCAST2UCAST_MODE; + config->tx_dbg_log_size = TARGET_TX_DBG_LOG_SIZE; + config->num_wds_entries = TARGET_NUM_WDS_ENTRIES; + config->dma_burst_size = TARGET_DMA_BURST_SIZE; + config->rx_skip_defrag_timeout_dup_detection_check = + TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; + config->vow_config = TARGET_VOW_CONFIG; + config->gtk_offload_max_vdev = TARGET_GTK_OFFLOAD_MAX_VDEV; + config->num_msdu_desc = TARGET_NUM_MSDU_DESC; + config->beacon_tx_offload_max_vdev = sc->num_radios * TARGET_MAX_BCN_OFFLD; + config->rx_batchmode = TARGET_RX_BATCHMODE; + config->peer_map_unmap_v2_support = 1; + config->twt_ap_pdev_count = sc->num_radios; + config->twt_ap_sta_count = 1000; + config->flag1 |= WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64; + config->flag1 |= WMI_RSRC_CFG_FLAG1_ACK_RSSI; + config->ema_max_vap_cnt = sc->num_radios; + config->ema_max_profile_period = TARGET_EMA_MAX_PROFILE_PERIOD; + config->beacon_tx_offload_max_vdev += config->ema_max_vap_cnt; +} + +void +qwz_hw_wcn6855_reo_setup(struct qwz_softc *sc) +{ + uint32_t reo_base = HAL_SEQ_WCSS_UMAC_REO_REG; + uint32_t val; + /* Each hash entry uses four bits to map to a particular ring. */ + uint32_t ring_hash_map = HAL_HASH_ROUTING_RING_SW1 << 0 | + HAL_HASH_ROUTING_RING_SW2 << 4 | + HAL_HASH_ROUTING_RING_SW3 << 8 | + HAL_HASH_ROUTING_RING_SW4 << 12 | + HAL_HASH_ROUTING_RING_SW1 << 16 | + HAL_HASH_ROUTING_RING_SW2 << 20 | + HAL_HASH_ROUTING_RING_SW3 << 24 | + HAL_HASH_ROUTING_RING_SW4 << 28; + + val = sc->ops.read32(sc, reo_base + HAL_REO1_GEN_ENABLE); + val |= FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE, 1) | + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE, 1); + sc->ops.write32(sc, reo_base + HAL_REO1_GEN_ENABLE, val); + + val = sc->ops.read32(sc, reo_base + HAL_REO1_MISC_CTL(sc)); + val &= ~HAL_REO1_MISC_CTL_FRAGMENT_DST_RING; + val |= FIELD_PREP(HAL_REO1_MISC_CTL_FRAGMENT_DST_RING, + HAL_SRNG_RING_ID_REO2SW1); + sc->ops.write32(sc, reo_base + HAL_REO1_MISC_CTL(sc), val); + + sc->ops.write32(sc, reo_base + HAL_REO1_AGING_THRESH_IX_0(sc), + HAL_DEFAULT_REO_TIMEOUT_USEC); + sc->ops.write32(sc, reo_base + HAL_REO1_AGING_THRESH_IX_1(sc), + HAL_DEFAULT_REO_TIMEOUT_USEC); + sc->ops.write32(sc, reo_base + HAL_REO1_AGING_THRESH_IX_2(sc), + HAL_DEFAULT_REO_TIMEOUT_USEC); + sc->ops.write32(sc, reo_base + HAL_REO1_AGING_THRESH_IX_3(sc), + HAL_DEFAULT_REO_TIMEOUT_USEC); + + sc->ops.write32(sc, reo_base + HAL_REO1_DEST_RING_CTRL_IX_2, + ring_hash_map); + sc->ops.write32(sc, reo_base + HAL_REO1_DEST_RING_CTRL_IX_3, + ring_hash_map); +} + +void +qwz_hw_ipq5018_reo_setup(struct qwz_softc *sc) +{ + uint32_t reo_base = HAL_SEQ_WCSS_UMAC_REO_REG; + uint32_t val; + + /* Each hash entry uses three bits to map to a particular ring. */ + uint32_t ring_hash_map = HAL_HASH_ROUTING_RING_SW1 << 0 | + HAL_HASH_ROUTING_RING_SW2 << 4 | + HAL_HASH_ROUTING_RING_SW3 << 8 | + HAL_HASH_ROUTING_RING_SW4 << 12 | + HAL_HASH_ROUTING_RING_SW1 << 16 | + HAL_HASH_ROUTING_RING_SW2 << 20 | + HAL_HASH_ROUTING_RING_SW3 << 24 | + HAL_HASH_ROUTING_RING_SW4 << 28; + + val = sc->ops.read32(sc, reo_base + HAL_REO1_GEN_ENABLE); + + val &= ~HAL_REO1_GEN_ENABLE_FRAG_DST_RING; + val |= FIELD_PREP(HAL_REO1_GEN_ENABLE_FRAG_DST_RING, + HAL_SRNG_RING_ID_REO2SW1) | + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE, 1) | + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE, 1); + sc->ops.write32(sc, reo_base + HAL_REO1_GEN_ENABLE, val); + + sc->ops.write32(sc, reo_base + HAL_REO1_AGING_THRESH_IX_0(sc), + HAL_DEFAULT_REO_TIMEOUT_USEC); + sc->ops.write32(sc, reo_base + HAL_REO1_AGING_THRESH_IX_1(sc), + HAL_DEFAULT_REO_TIMEOUT_USEC); + sc->ops.write32(sc, reo_base + HAL_REO1_AGING_THRESH_IX_2(sc), + HAL_DEFAULT_REO_TIMEOUT_USEC); + sc->ops.write32(sc, reo_base + HAL_REO1_AGING_THRESH_IX_3(sc), + HAL_DEFAULT_REO_TIMEOUT_USEC); + + sc->ops.write32(sc, reo_base + HAL_REO1_DEST_RING_CTRL_IX_0, + ring_hash_map); + sc->ops.write32(sc, reo_base + HAL_REO1_DEST_RING_CTRL_IX_1, + ring_hash_map); + sc->ops.write32(sc, reo_base + HAL_REO1_DEST_RING_CTRL_IX_2, + ring_hash_map); + sc->ops.write32(sc, reo_base + HAL_REO1_DEST_RING_CTRL_IX_3, + ring_hash_map); +} + +int +qwz_hw_mac_id_to_pdev_id_ipq8074(struct ath12k_hw_params *hw, int mac_id) +{ + return mac_id; +} + +int +qwz_hw_mac_id_to_srng_id_ipq8074(struct ath12k_hw_params *hw, int mac_id) +{ + return 0; +} + +int +qwz_hw_mac_id_to_pdev_id_qca6390(struct ath12k_hw_params *hw, int mac_id) +{ + return 0; +} + +int +qwz_hw_mac_id_to_srng_id_qca6390(struct ath12k_hw_params *hw, int mac_id) +{ + return mac_id; +} + +int +qwz_hw_ipq8074_rx_desc_get_first_msdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MSDU_END_INFO2_FIRST_MSDU, + le32toh(desc->u.ipq8074.msdu_end.info2)); +} + +uint8_t +qwz_hw_ipq8074_rx_desc_get_l3_pad_bytes(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_END_INFO2_L3_HDR_PADDING, + le32toh(desc->u.ipq8074.msdu_end.info2)); +} + +uint8_t * +qwz_hw_ipq8074_rx_desc_get_hdr_status(struct hal_rx_desc *desc) +{ + return desc->u.ipq8074.hdr_status; +} + +int +qwz_hw_ipq8074_rx_desc_encrypt_valid(struct hal_rx_desc *desc) +{ + return le32toh(desc->u.ipq8074.mpdu_start.info1) & + RX_MPDU_START_INFO1_ENCRYPT_INFO_VALID; +} + +uint32_t +qwz_hw_ipq8074_rx_desc_get_encrypt_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO2_ENC_TYPE, + le32toh(desc->u.ipq8074.mpdu_start.info2)); +} + +uint8_t +qwz_hw_ipq8074_rx_desc_get_decap_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_DECAP_FORMAT, + le32toh(desc->u.ipq8074.msdu_start.info2)); +} + +uint8_t +qwz_hw_ipq8074_rx_desc_get_mesh_ctl(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_MESH_CTRL_PRESENT, + le32toh(desc->u.ipq8074.msdu_start.info2)); +} + +int +qwz_hw_ipq8074_rx_desc_get_ldpc_support(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_LDPC, + le32toh(desc->u.ipq8074.msdu_start.info2)); +} + +int +qwz_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MPDU_START_INFO1_MPDU_SEQ_CTRL_VALID, + le32toh(desc->u.ipq8074.mpdu_start.info1)); +} + +int +qwz_hw_ipq8074_rx_desc_get_mpdu_fc_valid(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MPDU_START_INFO1_MPDU_FCTRL_VALID, + le32toh(desc->u.ipq8074.mpdu_start.info1)); +} + +uint16_t +qwz_hw_ipq8074_rx_desc_get_mpdu_start_seq_no(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO1_MPDU_SEQ_NUM, + le32toh(desc->u.ipq8074.mpdu_start.info1)); +} + +uint16_t +qwz_hw_ipq8074_rx_desc_get_msdu_len(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO1_MSDU_LENGTH, + le32toh(desc->u.ipq8074.msdu_start.info1)); +} + +uint8_t +qwz_hw_ipq8074_rx_desc_get_msdu_sgi(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_SGI, + le32toh(desc->u.ipq8074.msdu_start.info3)); +} + +uint8_t +qwz_hw_ipq8074_rx_desc_get_msdu_rate_mcs(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_RATE_MCS, + le32toh(desc->u.ipq8074.msdu_start.info3)); +} + +uint8_t +qwz_hw_ipq8074_rx_desc_get_msdu_rx_bw(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_RECV_BW, + le32toh(desc->u.ipq8074.msdu_start.info3)); +} + +uint32_t +qwz_hw_ipq8074_rx_desc_get_msdu_freq(struct hal_rx_desc *desc) +{ + return le32toh(desc->u.ipq8074.msdu_start.phy_meta_data); +} + +uint8_t +qwz_hw_ipq8074_rx_desc_get_msdu_pkt_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_PKT_TYPE, + le32toh(desc->u.ipq8074.msdu_start.info3)); +} + +uint8_t +qwz_hw_ipq8074_rx_desc_get_msdu_nss(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_MIMO_SS_BITMAP, + le32toh(desc->u.ipq8074.msdu_start.info3)); +} + +uint8_t +qwz_hw_ipq8074_rx_desc_get_mpdu_tid(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO2_TID, + le32toh(desc->u.ipq8074.mpdu_start.info2)); +} + +uint16_t +qwz_hw_ipq8074_rx_desc_get_mpdu_peer_id(struct hal_rx_desc *desc) +{ + return le16toh(desc->u.ipq8074.mpdu_start.sw_peer_id); +} + +void +qwz_hw_ipq8074_rx_desc_copy_attn_end(struct hal_rx_desc *fdesc, + struct hal_rx_desc *ldesc) +{ + memcpy((uint8_t *)&fdesc->u.ipq8074.msdu_end, (uint8_t *)&ldesc->u.ipq8074.msdu_end, + sizeof(struct rx_msdu_end_ipq8074)); + memcpy((uint8_t *)&fdesc->u.ipq8074.attention, (uint8_t *)&ldesc->u.ipq8074.attention, + sizeof(struct rx_attention)); + memcpy((uint8_t *)&fdesc->u.ipq8074.mpdu_end, (uint8_t *)&ldesc->u.ipq8074.mpdu_end, + sizeof(struct rx_mpdu_end)); +} + +uint32_t +qwz_hw_ipq8074_rx_desc_get_mpdu_start_tag(struct hal_rx_desc *desc) +{ + return FIELD_GET(HAL_TLV_HDR_TAG, + le32toh(desc->u.ipq8074.mpdu_start_tag)); +} + +uint32_t +qwz_hw_ipq8074_rx_desc_get_mpdu_ppdu_id(struct hal_rx_desc *desc) +{ + return le16toh(desc->u.ipq8074.mpdu_start.phy_ppdu_id); +} + +void +qwz_hw_ipq8074_rx_desc_set_msdu_len(struct hal_rx_desc *desc, uint16_t len) +{ + uint32_t info = le32toh(desc->u.ipq8074.msdu_start.info1); + + info &= ~RX_MSDU_START_INFO1_MSDU_LENGTH; + info |= FIELD_PREP(RX_MSDU_START_INFO1_MSDU_LENGTH, len); + + desc->u.ipq8074.msdu_start.info1 = htole32(info); +} + +int +qwz_dp_rx_h_msdu_end_first_msdu(struct qwz_softc *sc, struct hal_rx_desc *desc) +{ + return sc->hw_params.hw_ops->rx_desc_get_first_msdu(desc); +} + +int +qwz_hw_ipq8074_rx_desc_mac_addr2_valid(struct hal_rx_desc *desc) +{ + return le32toh(desc->u.ipq8074.mpdu_start.info1) & + RX_MPDU_START_INFO1_MAC_ADDR2_VALID; +} + +uint8_t * +qwz_hw_ipq8074_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) +{ + return desc->u.ipq8074.mpdu_start.addr2; +} + +struct rx_attention * +qwz_hw_ipq8074_rx_desc_get_attention(struct hal_rx_desc *desc) +{ + return &desc->u.ipq8074.attention; +} + +uint8_t * +qwz_hw_ipq8074_rx_desc_get_msdu_payload(struct hal_rx_desc *desc) +{ + return &desc->u.ipq8074.msdu_payload[0]; +} + +int +qwz_hw_qcn9074_rx_desc_get_first_msdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MSDU_END_INFO4_FIRST_MSDU, + le16toh(desc->u.qcn9074.msdu_end.info4)); +} + +int +qwz_hw_qcn9074_rx_desc_get_last_msdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MSDU_END_INFO4_LAST_MSDU, + le16toh(desc->u.qcn9074.msdu_end.info4)); +} + +uint8_t +qwz_hw_qcn9074_rx_desc_get_l3_pad_bytes(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_END_INFO4_L3_HDR_PADDING, + le16toh(desc->u.qcn9074.msdu_end.info4)); +} + +uint8_t * +qwz_hw_qcn9074_rx_desc_get_hdr_status(struct hal_rx_desc *desc) +{ + return desc->u.qcn9074.hdr_status; +} + +int +qwz_hw_qcn9074_rx_desc_encrypt_valid(struct hal_rx_desc *desc) +{ + return le32toh(desc->u.qcn9074.mpdu_start.info11) & + RX_MPDU_START_INFO11_ENCRYPT_INFO_VALID; +} + +uint32_t +qwz_hw_qcn9074_rx_desc_get_encrypt_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO9_ENC_TYPE, + le32toh(desc->u.qcn9074.mpdu_start.info9)); +} + +uint8_t +qwz_hw_qcn9074_rx_desc_get_decap_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_DECAP_FORMAT, + le32toh(desc->u.qcn9074.msdu_start.info2)); +} + +uint8_t +qwz_hw_qcn9074_rx_desc_get_mesh_ctl(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_MESH_CTRL_PRESENT, + le32toh(desc->u.qcn9074.msdu_start.info2)); +} + +int +qwz_hw_qcn9074_rx_desc_get_ldpc_support(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_LDPC, + le32toh(desc->u.qcn9074.msdu_start.info2)); +} + +int +qwz_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MPDU_START_INFO11_MPDU_SEQ_CTRL_VALID, + le32toh(desc->u.qcn9074.mpdu_start.info11)); +} + +int +qwz_hw_qcn9074_rx_desc_get_mpdu_fc_valid(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MPDU_START_INFO11_MPDU_FCTRL_VALID, + le32toh(desc->u.qcn9074.mpdu_start.info11)); +} + +uint16_t +qwz_hw_qcn9074_rx_desc_get_mpdu_start_seq_no(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO11_MPDU_SEQ_NUM, + le32toh(desc->u.qcn9074.mpdu_start.info11)); +} + +uint16_t +qwz_hw_qcn9074_rx_desc_get_msdu_len(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO1_MSDU_LENGTH, + le32toh(desc->u.qcn9074.msdu_start.info1)); +} + +uint8_t +qwz_hw_qcn9074_rx_desc_get_msdu_sgi(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_SGI, + le32toh(desc->u.qcn9074.msdu_start.info3)); +} + +uint8_t +qwz_hw_qcn9074_rx_desc_get_msdu_rate_mcs(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_RATE_MCS, + le32toh(desc->u.qcn9074.msdu_start.info3)); +} + +uint8_t +qwz_hw_qcn9074_rx_desc_get_msdu_rx_bw(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_RECV_BW, + le32toh(desc->u.qcn9074.msdu_start.info3)); +} + +uint32_t +qwz_hw_qcn9074_rx_desc_get_msdu_freq(struct hal_rx_desc *desc) +{ + return le32toh(desc->u.qcn9074.msdu_start.phy_meta_data); +} + +uint8_t +qwz_hw_qcn9074_rx_desc_get_msdu_pkt_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_PKT_TYPE, + le32toh(desc->u.qcn9074.msdu_start.info3)); +} + +uint8_t +qwz_hw_qcn9074_rx_desc_get_msdu_nss(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_MIMO_SS_BITMAP, + le32toh(desc->u.qcn9074.msdu_start.info3)); +} + +uint8_t +qwz_hw_qcn9074_rx_desc_get_mpdu_tid(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO9_TID, + le32toh(desc->u.qcn9074.mpdu_start.info9)); +} + +uint16_t +qwz_hw_qcn9074_rx_desc_get_mpdu_peer_id(struct hal_rx_desc *desc) +{ + return le16toh(desc->u.qcn9074.mpdu_start.sw_peer_id); +} + +void +qwz_hw_qcn9074_rx_desc_copy_attn_end(struct hal_rx_desc *fdesc, + struct hal_rx_desc *ldesc) +{ + memcpy((uint8_t *)&fdesc->u.qcn9074.msdu_end, (uint8_t *)&ldesc->u.qcn9074.msdu_end, + sizeof(struct rx_msdu_end_qcn9074)); + memcpy((uint8_t *)&fdesc->u.qcn9074.attention, (uint8_t *)&ldesc->u.qcn9074.attention, + sizeof(struct rx_attention)); + memcpy((uint8_t *)&fdesc->u.qcn9074.mpdu_end, (uint8_t *)&ldesc->u.qcn9074.mpdu_end, + sizeof(struct rx_mpdu_end)); +} + +uint32_t +qwz_hw_qcn9074_rx_desc_get_mpdu_start_tag(struct hal_rx_desc *desc) +{ + return FIELD_GET(HAL_TLV_HDR_TAG, + le32toh(desc->u.qcn9074.mpdu_start_tag)); +} + +uint32_t +qwz_hw_qcn9074_rx_desc_get_mpdu_ppdu_id(struct hal_rx_desc *desc) +{ + return le16toh(desc->u.qcn9074.mpdu_start.phy_ppdu_id); +} + +void +qwz_hw_qcn9074_rx_desc_set_msdu_len(struct hal_rx_desc *desc, uint16_t len) +{ + uint32_t info = le32toh(desc->u.qcn9074.msdu_start.info1); + + info &= ~RX_MSDU_START_INFO1_MSDU_LENGTH; + info |= FIELD_PREP(RX_MSDU_START_INFO1_MSDU_LENGTH, len); + + desc->u.qcn9074.msdu_start.info1 = htole32(info); +} + +struct rx_attention * +qwz_hw_qcn9074_rx_desc_get_attention(struct hal_rx_desc *desc) +{ + return &desc->u.qcn9074.attention; +} + +uint8_t * +qwz_hw_qcn9074_rx_desc_get_msdu_payload(struct hal_rx_desc *desc) +{ + return &desc->u.qcn9074.msdu_payload[0]; +} + +int +qwz_hw_ipq9074_rx_desc_mac_addr2_valid(struct hal_rx_desc *desc) +{ + return le32toh(desc->u.qcn9074.mpdu_start.info11) & + RX_MPDU_START_INFO11_MAC_ADDR2_VALID; +} + +uint8_t * +qwz_hw_ipq9074_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) +{ + return desc->u.qcn9074.mpdu_start.addr2; +} + +int +qwz_hw_wcn6855_rx_desc_get_first_msdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MSDU_END_INFO2_FIRST_MSDU_WCN6855, + le32toh(desc->u.wcn6855.msdu_end.info2)); +} + +int +qwz_hw_wcn6855_rx_desc_get_last_msdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MSDU_END_INFO2_LAST_MSDU_WCN6855, + le32toh(desc->u.wcn6855.msdu_end.info2)); +} + +uint8_t +qwz_hw_wcn6855_rx_desc_get_l3_pad_bytes(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_END_INFO2_L3_HDR_PADDING, + le32toh(desc->u.wcn6855.msdu_end.info2)); +} + +uint8_t * +qwz_hw_wcn6855_rx_desc_get_hdr_status(struct hal_rx_desc *desc) +{ + return desc->u.wcn6855.hdr_status; +} + +int +qwz_hw_wcn6855_rx_desc_encrypt_valid(struct hal_rx_desc *desc) +{ + return le32toh(desc->u.wcn6855.mpdu_start.info1) & + RX_MPDU_START_INFO1_ENCRYPT_INFO_VALID; +} + +uint32_t +qwz_hw_wcn6855_rx_desc_get_encrypt_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO2_ENC_TYPE, + le32toh(desc->u.wcn6855.mpdu_start.info2)); +} + +uint8_t +qwz_hw_wcn6855_rx_desc_get_decap_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_DECAP_FORMAT, + le32toh(desc->u.wcn6855.msdu_start.info2)); +} + +uint8_t +qwz_hw_wcn6855_rx_desc_get_mesh_ctl(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO2_MESH_CTRL_PRESENT, + le32toh(desc->u.wcn6855.msdu_start.info2)); +} + +int +qwz_hw_wcn6855_rx_desc_get_mpdu_seq_ctl_vld(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MPDU_START_INFO1_MPDU_SEQ_CTRL_VALID, + le32toh(desc->u.wcn6855.mpdu_start.info1)); +} + +int +qwz_hw_wcn6855_rx_desc_get_mpdu_fc_valid(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MPDU_START_INFO1_MPDU_FCTRL_VALID, + le32toh(desc->u.wcn6855.mpdu_start.info1)); +} + +uint16_t +qwz_hw_wcn6855_rx_desc_get_mpdu_start_seq_no(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO1_MPDU_SEQ_NUM, + le32toh(desc->u.wcn6855.mpdu_start.info1)); +} + +uint16_t +qwz_hw_wcn6855_rx_desc_get_msdu_len(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO1_MSDU_LENGTH, + le32toh(desc->u.wcn6855.msdu_start.info1)); +} + +uint8_t +qwz_hw_wcn6855_rx_desc_get_msdu_sgi(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_SGI, + le32toh(desc->u.wcn6855.msdu_start.info3)); +} + +uint8_t +qwz_hw_wcn6855_rx_desc_get_msdu_rate_mcs(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_RATE_MCS, + le32toh(desc->u.wcn6855.msdu_start.info3)); +} + +uint8_t +qwz_hw_wcn6855_rx_desc_get_msdu_rx_bw(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_RECV_BW, + le32toh(desc->u.wcn6855.msdu_start.info3)); +} + +uint32_t +qwz_hw_wcn6855_rx_desc_get_msdu_freq(struct hal_rx_desc *desc) +{ + return le32toh(desc->u.wcn6855.msdu_start.phy_meta_data); +} + +uint8_t +qwz_hw_wcn6855_rx_desc_get_msdu_pkt_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_PKT_TYPE, + le32toh(desc->u.wcn6855.msdu_start.info3)); +} + +uint8_t +qwz_hw_wcn6855_rx_desc_get_msdu_nss(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_MIMO_SS_BITMAP, + le32toh(desc->u.wcn6855.msdu_start.info3)); +} + +uint8_t +qwz_hw_wcn6855_rx_desc_get_mpdu_tid(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO2_TID_WCN6855, + le32toh(desc->u.wcn6855.mpdu_start.info2)); +} + +uint16_t +qwz_hw_wcn6855_rx_desc_get_mpdu_peer_id(struct hal_rx_desc *desc) +{ + return le16toh(desc->u.wcn6855.mpdu_start.sw_peer_id); +} + +void +qwz_hw_wcn6855_rx_desc_copy_attn_end(struct hal_rx_desc *fdesc, + struct hal_rx_desc *ldesc) +{ + memcpy((uint8_t *)&fdesc->u.wcn6855.msdu_end, (uint8_t *)&ldesc->u.wcn6855.msdu_end, + sizeof(struct rx_msdu_end_wcn6855)); + memcpy((uint8_t *)&fdesc->u.wcn6855.attention, (uint8_t *)&ldesc->u.wcn6855.attention, + sizeof(struct rx_attention)); + memcpy((uint8_t *)&fdesc->u.wcn6855.mpdu_end, (uint8_t *)&ldesc->u.wcn6855.mpdu_end, + sizeof(struct rx_mpdu_end)); +} + +uint32_t +qwz_hw_wcn6855_rx_desc_get_mpdu_start_tag(struct hal_rx_desc *desc) +{ + return FIELD_GET(HAL_TLV_HDR_TAG, + le32toh(desc->u.wcn6855.mpdu_start_tag)); +} + +uint32_t +qwz_hw_wcn6855_rx_desc_get_mpdu_ppdu_id(struct hal_rx_desc *desc) +{ + return le16toh(desc->u.wcn6855.mpdu_start.phy_ppdu_id); +} + +void +qwz_hw_wcn6855_rx_desc_set_msdu_len(struct hal_rx_desc *desc, uint16_t len) +{ + uint32_t info = le32toh(desc->u.wcn6855.msdu_start.info1); + + info &= ~RX_MSDU_START_INFO1_MSDU_LENGTH; + info |= FIELD_PREP(RX_MSDU_START_INFO1_MSDU_LENGTH, len); + + desc->u.wcn6855.msdu_start.info1 = htole32(info); +} + +struct rx_attention * +qwz_hw_wcn6855_rx_desc_get_attention(struct hal_rx_desc *desc) +{ + return &desc->u.wcn6855.attention; +} + +uint8_t * +qwz_hw_wcn6855_rx_desc_get_msdu_payload(struct hal_rx_desc *desc) +{ + return &desc->u.wcn6855.msdu_payload[0]; +} + +int +qwz_hw_wcn6855_rx_desc_mac_addr2_valid(struct hal_rx_desc *desc) +{ + return le32toh(desc->u.wcn6855.mpdu_start.info1) & + RX_MPDU_START_INFO1_MAC_ADDR2_VALID; +} + +uint8_t * +qwz_hw_wcn6855_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) +{ + return desc->u.wcn6855.mpdu_start.addr2; +} + +/* Map from pdev index to hw mac index */ +uint8_t +qwz_hw_ipq8074_mac_from_pdev_id(int pdev_idx) +{ + switch (pdev_idx) { + case 0: + return 0; + case 1: + return 2; + case 2: + return 1; + default: + return ATH12K_INVALID_HW_MAC_ID; + } +} + +uint8_t +qwz_hw_ipq6018_mac_from_pdev_id(int pdev_idx) +{ + return pdev_idx; +} + +static inline int +qwz_hw_get_mac_from_pdev_id(struct qwz_softc *sc, int pdev_idx) +{ + if (sc->hw_params.hw_ops->get_hw_mac_from_pdev_id) + return sc->hw_params.hw_ops->get_hw_mac_from_pdev_id(pdev_idx); + + return 0; +} + +const struct ath12k_hw_ops ipq8074_ops = { + .get_hw_mac_from_pdev_id = qwz_hw_ipq8074_mac_from_pdev_id, + .wmi_init_config = qwz_init_wmi_config_ipq8074, + .mac_id_to_pdev_id = qwz_hw_mac_id_to_pdev_id_ipq8074, + .mac_id_to_srng_id = qwz_hw_mac_id_to_srng_id_ipq8074, +#if notyet + .tx_mesh_enable = ath12k_hw_ipq8074_tx_mesh_enable, +#endif + .rx_desc_get_first_msdu = qwz_hw_ipq8074_rx_desc_get_first_msdu, +#if notyet + .rx_desc_get_last_msdu = ath12k_hw_ipq8074_rx_desc_get_last_msdu, +#endif + .rx_desc_get_l3_pad_bytes = qwz_hw_ipq8074_rx_desc_get_l3_pad_bytes, + .rx_desc_get_hdr_status = qwz_hw_ipq8074_rx_desc_get_hdr_status, + .rx_desc_encrypt_valid = qwz_hw_ipq8074_rx_desc_encrypt_valid, + .rx_desc_get_encrypt_type = qwz_hw_ipq8074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = qwz_hw_ipq8074_rx_desc_get_decap_type, +#ifdef notyet + .rx_desc_get_mesh_ctl = ath12k_hw_ipq8074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath12k_hw_ipq8074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath12k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath12k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, + .rx_desc_get_mpdu_start_seq_no = ath12k_hw_ipq8074_rx_desc_get_mpdu_start_seq_no, +#endif + .rx_desc_get_msdu_len = qwz_hw_ipq8074_rx_desc_get_msdu_len, +#ifdef notyet + .rx_desc_get_msdu_sgi = ath12k_hw_ipq8074_rx_desc_get_msdu_sgi, + .rx_desc_get_msdu_rate_mcs = ath12k_hw_ipq8074_rx_desc_get_msdu_rate_mcs, + .rx_desc_get_msdu_rx_bw = ath12k_hw_ipq8074_rx_desc_get_msdu_rx_bw, +#endif + .rx_desc_get_msdu_freq = qwz_hw_ipq8074_rx_desc_get_msdu_freq, +#ifdef notyet + .rx_desc_get_msdu_pkt_type = ath12k_hw_ipq8074_rx_desc_get_msdu_pkt_type, + .rx_desc_get_msdu_nss = ath12k_hw_ipq8074_rx_desc_get_msdu_nss, + .rx_desc_get_mpdu_tid = ath12k_hw_ipq8074_rx_desc_get_mpdu_tid, + .rx_desc_get_mpdu_peer_id = ath12k_hw_ipq8074_rx_desc_get_mpdu_peer_id, + .rx_desc_copy_attn_end_tlv = ath12k_hw_ipq8074_rx_desc_copy_attn_end, + .rx_desc_get_mpdu_start_tag = ath12k_hw_ipq8074_rx_desc_get_mpdu_start_tag, + .rx_desc_get_mpdu_ppdu_id = ath12k_hw_ipq8074_rx_desc_get_mpdu_ppdu_id, + .rx_desc_set_msdu_len = ath12k_hw_ipq8074_rx_desc_set_msdu_len, +#endif + .rx_desc_get_attention = qwz_hw_ipq8074_rx_desc_get_attention, +#ifdef notyet + .rx_desc_get_msdu_payload = ath12k_hw_ipq8074_rx_desc_get_msdu_payload, +#endif + .reo_setup = qwz_hw_ipq8074_reo_setup, +#ifdef notyet + .mpdu_info_get_peerid = ath12k_hw_ipq8074_mpdu_info_get_peerid, + .rx_desc_mac_addr2_valid = ath12k_hw_ipq8074_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath12k_hw_ipq8074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath12k_hw_ipq8074_get_tcl_ring_selector, +#endif +}; + +const struct ath12k_hw_ops ipq6018_ops = { + .get_hw_mac_from_pdev_id = qwz_hw_ipq6018_mac_from_pdev_id, + .wmi_init_config = qwz_init_wmi_config_ipq8074, + .mac_id_to_pdev_id = qwz_hw_mac_id_to_pdev_id_ipq8074, + .mac_id_to_srng_id = qwz_hw_mac_id_to_srng_id_ipq8074, +#if notyet + .tx_mesh_enable = ath12k_hw_ipq8074_tx_mesh_enable, +#endif + .rx_desc_get_first_msdu = qwz_hw_ipq8074_rx_desc_get_first_msdu, +#if notyet + .rx_desc_get_last_msdu = ath12k_hw_ipq8074_rx_desc_get_last_msdu, +#endif + .rx_desc_get_l3_pad_bytes = qwz_hw_ipq8074_rx_desc_get_l3_pad_bytes, + .rx_desc_get_hdr_status = qwz_hw_ipq8074_rx_desc_get_hdr_status, + .rx_desc_encrypt_valid = qwz_hw_ipq8074_rx_desc_encrypt_valid, + .rx_desc_get_encrypt_type = qwz_hw_ipq8074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = qwz_hw_ipq8074_rx_desc_get_decap_type, +#ifdef notyet + .rx_desc_get_mesh_ctl = ath12k_hw_ipq8074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath12k_hw_ipq8074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath12k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath12k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, + .rx_desc_get_mpdu_start_seq_no = ath12k_hw_ipq8074_rx_desc_get_mpdu_start_seq_no, +#endif + .rx_desc_get_msdu_len = qwz_hw_ipq8074_rx_desc_get_msdu_len, +#ifdef notyet + .rx_desc_get_msdu_sgi = ath12k_hw_ipq8074_rx_desc_get_msdu_sgi, + .rx_desc_get_msdu_rate_mcs = ath12k_hw_ipq8074_rx_desc_get_msdu_rate_mcs, + .rx_desc_get_msdu_rx_bw = ath12k_hw_ipq8074_rx_desc_get_msdu_rx_bw, +#endif + .rx_desc_get_msdu_freq = qwz_hw_ipq8074_rx_desc_get_msdu_freq, +#ifdef notyet + .rx_desc_get_msdu_pkt_type = ath12k_hw_ipq8074_rx_desc_get_msdu_pkt_type, + .rx_desc_get_msdu_nss = ath12k_hw_ipq8074_rx_desc_get_msdu_nss, + .rx_desc_get_mpdu_tid = ath12k_hw_ipq8074_rx_desc_get_mpdu_tid, + .rx_desc_get_mpdu_peer_id = ath12k_hw_ipq8074_rx_desc_get_mpdu_peer_id, + .rx_desc_copy_attn_end_tlv = ath12k_hw_ipq8074_rx_desc_copy_attn_end, + .rx_desc_get_mpdu_start_tag = ath12k_hw_ipq8074_rx_desc_get_mpdu_start_tag, + .rx_desc_get_mpdu_ppdu_id = ath12k_hw_ipq8074_rx_desc_get_mpdu_ppdu_id, + .rx_desc_set_msdu_len = ath12k_hw_ipq8074_rx_desc_set_msdu_len, +#endif + .rx_desc_get_attention = qwz_hw_ipq8074_rx_desc_get_attention, +#ifdef notyet + .rx_desc_get_msdu_payload = ath12k_hw_ipq8074_rx_desc_get_msdu_payload, +#endif + .reo_setup = qwz_hw_ipq8074_reo_setup, +#ifdef notyet + .mpdu_info_get_peerid = ath12k_hw_ipq8074_mpdu_info_get_peerid, + .rx_desc_mac_addr2_valid = ath12k_hw_ipq8074_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath12k_hw_ipq8074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath12k_hw_ipq8074_get_tcl_ring_selector, +#endif +}; + +const struct ath12k_hw_ops qca6390_ops = { + .get_hw_mac_from_pdev_id = qwz_hw_ipq8074_mac_from_pdev_id, + .wmi_init_config = qwz_init_wmi_config_qca6390, + .mac_id_to_pdev_id = qwz_hw_mac_id_to_pdev_id_qca6390, + .mac_id_to_srng_id = qwz_hw_mac_id_to_srng_id_qca6390, +#if notyet + .tx_mesh_enable = ath12k_hw_ipq8074_tx_mesh_enable, +#endif + .rx_desc_get_first_msdu = qwz_hw_ipq8074_rx_desc_get_first_msdu, +#if notyet + .rx_desc_get_last_msdu = ath12k_hw_ipq8074_rx_desc_get_last_msdu, +#endif + .rx_desc_get_l3_pad_bytes = qwz_hw_ipq8074_rx_desc_get_l3_pad_bytes, + .rx_desc_get_hdr_status = qwz_hw_ipq8074_rx_desc_get_hdr_status, + .rx_desc_encrypt_valid = qwz_hw_ipq8074_rx_desc_encrypt_valid, + .rx_desc_get_encrypt_type = qwz_hw_ipq8074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = qwz_hw_ipq8074_rx_desc_get_decap_type, +#ifdef notyet + .rx_desc_get_mesh_ctl = ath12k_hw_ipq8074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath12k_hw_ipq8074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath12k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath12k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, + .rx_desc_get_mpdu_start_seq_no = ath12k_hw_ipq8074_rx_desc_get_mpdu_start_seq_no, +#endif + .rx_desc_get_msdu_len = qwz_hw_ipq8074_rx_desc_get_msdu_len, +#ifdef notyet + .rx_desc_get_msdu_sgi = ath12k_hw_ipq8074_rx_desc_get_msdu_sgi, + .rx_desc_get_msdu_rate_mcs = ath12k_hw_ipq8074_rx_desc_get_msdu_rate_mcs, + .rx_desc_get_msdu_rx_bw = ath12k_hw_ipq8074_rx_desc_get_msdu_rx_bw, +#endif + .rx_desc_get_msdu_freq = qwz_hw_ipq8074_rx_desc_get_msdu_freq, +#ifdef notyet + .rx_desc_get_msdu_pkt_type = ath12k_hw_ipq8074_rx_desc_get_msdu_pkt_type, + .rx_desc_get_msdu_nss = ath12k_hw_ipq8074_rx_desc_get_msdu_nss, + .rx_desc_get_mpdu_tid = ath12k_hw_ipq8074_rx_desc_get_mpdu_tid, + .rx_desc_get_mpdu_peer_id = ath12k_hw_ipq8074_rx_desc_get_mpdu_peer_id, + .rx_desc_copy_attn_end_tlv = ath12k_hw_ipq8074_rx_desc_copy_attn_end, + .rx_desc_get_mpdu_start_tag = ath12k_hw_ipq8074_rx_desc_get_mpdu_start_tag, + .rx_desc_get_mpdu_ppdu_id = ath12k_hw_ipq8074_rx_desc_get_mpdu_ppdu_id, + .rx_desc_set_msdu_len = ath12k_hw_ipq8074_rx_desc_set_msdu_len, +#endif + .rx_desc_get_attention = qwz_hw_ipq8074_rx_desc_get_attention, +#ifdef notyet + .rx_desc_get_msdu_payload = ath12k_hw_ipq8074_rx_desc_get_msdu_payload, +#endif + .reo_setup = qwz_hw_ipq8074_reo_setup, +#ifdef notyet + .mpdu_info_get_peerid = ath12k_hw_ipq8074_mpdu_info_get_peerid, + .rx_desc_mac_addr2_valid = ath12k_hw_ipq8074_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath12k_hw_ipq8074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath12k_hw_ipq8074_get_tcl_ring_selector, +#endif +}; + +const struct ath12k_hw_ops qcn9074_ops = { + .get_hw_mac_from_pdev_id = qwz_hw_ipq6018_mac_from_pdev_id, + .wmi_init_config = qwz_init_wmi_config_ipq8074, + .mac_id_to_pdev_id = qwz_hw_mac_id_to_pdev_id_ipq8074, + .mac_id_to_srng_id = qwz_hw_mac_id_to_srng_id_ipq8074, +#if notyet + .tx_mesh_enable = ath12k_hw_qcn9074_tx_mesh_enable, +#endif + .rx_desc_get_first_msdu = qwz_hw_qcn9074_rx_desc_get_first_msdu, +#if notyet + .rx_desc_get_last_msdu = ath12k_hw_qcn9074_rx_desc_get_last_msdu, +#endif + .rx_desc_get_l3_pad_bytes = qwz_hw_qcn9074_rx_desc_get_l3_pad_bytes, + .rx_desc_get_hdr_status = qwz_hw_qcn9074_rx_desc_get_hdr_status, + .rx_desc_encrypt_valid = qwz_hw_qcn9074_rx_desc_encrypt_valid, + .rx_desc_get_encrypt_type = qwz_hw_qcn9074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = qwz_hw_qcn9074_rx_desc_get_decap_type, +#ifdef notyet + .rx_desc_get_mesh_ctl = ath12k_hw_qcn9074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath12k_hw_qcn9074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath12k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath12k_hw_qcn9074_rx_desc_get_mpdu_fc_valid, + .rx_desc_get_mpdu_start_seq_no = ath12k_hw_qcn9074_rx_desc_get_mpdu_start_seq_no, +#endif + .rx_desc_get_msdu_len = qwz_hw_qcn9074_rx_desc_get_msdu_len, +#ifdef notyet + .rx_desc_get_msdu_sgi = ath12k_hw_qcn9074_rx_desc_get_msdu_sgi, + .rx_desc_get_msdu_rate_mcs = ath12k_hw_qcn9074_rx_desc_get_msdu_rate_mcs, + .rx_desc_get_msdu_rx_bw = ath12k_hw_qcn9074_rx_desc_get_msdu_rx_bw, +#endif + .rx_desc_get_msdu_freq = qwz_hw_qcn9074_rx_desc_get_msdu_freq, +#ifdef notyet + .rx_desc_get_msdu_pkt_type = ath12k_hw_qcn9074_rx_desc_get_msdu_pkt_type, + .rx_desc_get_msdu_nss = ath12k_hw_qcn9074_rx_desc_get_msdu_nss, + .rx_desc_get_mpdu_tid = ath12k_hw_qcn9074_rx_desc_get_mpdu_tid, + .rx_desc_get_mpdu_peer_id = ath12k_hw_qcn9074_rx_desc_get_mpdu_peer_id, + .rx_desc_copy_attn_end_tlv = ath12k_hw_qcn9074_rx_desc_copy_attn_end, + .rx_desc_get_mpdu_start_tag = ath12k_hw_qcn9074_rx_desc_get_mpdu_start_tag, + .rx_desc_get_mpdu_ppdu_id = ath12k_hw_qcn9074_rx_desc_get_mpdu_ppdu_id, + .rx_desc_set_msdu_len = ath12k_hw_qcn9074_rx_desc_set_msdu_len, +#endif + .rx_desc_get_attention = qwz_hw_qcn9074_rx_desc_get_attention, +#ifdef notyet + .rx_desc_get_msdu_payload = ath12k_hw_qcn9074_rx_desc_get_msdu_payload, +#endif + .reo_setup = qwz_hw_ipq8074_reo_setup, +#ifdef notyet + .mpdu_info_get_peerid = ath12k_hw_ipq8074_mpdu_info_get_peerid, + .rx_desc_mac_addr2_valid = ath12k_hw_ipq9074_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath12k_hw_ipq9074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath12k_hw_ipq8074_get_tcl_ring_selector, +#endif +}; + +const struct ath12k_hw_ops wcn6855_ops = { + .get_hw_mac_from_pdev_id = qwz_hw_ipq8074_mac_from_pdev_id, + .wmi_init_config = qwz_init_wmi_config_qca6390, + .mac_id_to_pdev_id = qwz_hw_mac_id_to_pdev_id_qca6390, + .mac_id_to_srng_id = qwz_hw_mac_id_to_srng_id_qca6390, +#if notyet + .tx_mesh_enable = ath12k_hw_wcn6855_tx_mesh_enable, +#endif + .rx_desc_get_first_msdu = qwz_hw_wcn6855_rx_desc_get_first_msdu, +#if notyet + .rx_desc_get_last_msdu = ath12k_hw_wcn6855_rx_desc_get_last_msdu, +#endif + .rx_desc_get_l3_pad_bytes = qwz_hw_wcn6855_rx_desc_get_l3_pad_bytes, + .rx_desc_get_hdr_status = qwz_hw_wcn6855_rx_desc_get_hdr_status, + .rx_desc_encrypt_valid = qwz_hw_wcn6855_rx_desc_encrypt_valid, + .rx_desc_get_encrypt_type = qwz_hw_wcn6855_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = qwz_hw_wcn6855_rx_desc_get_decap_type, +#ifdef notyet + .rx_desc_get_mesh_ctl = ath12k_hw_wcn6855_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath12k_hw_wcn6855_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath12k_hw_wcn6855_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath12k_hw_wcn6855_rx_desc_get_mpdu_fc_valid, + .rx_desc_get_mpdu_start_seq_no = ath12k_hw_wcn6855_rx_desc_get_mpdu_start_seq_no, +#endif + .rx_desc_get_msdu_len = qwz_hw_wcn6855_rx_desc_get_msdu_len, +#ifdef notyet + .rx_desc_get_msdu_sgi = ath12k_hw_wcn6855_rx_desc_get_msdu_sgi, + .rx_desc_get_msdu_rate_mcs = ath12k_hw_wcn6855_rx_desc_get_msdu_rate_mcs, + .rx_desc_get_msdu_rx_bw = ath12k_hw_wcn6855_rx_desc_get_msdu_rx_bw, +#endif + .rx_desc_get_msdu_freq = qwz_hw_wcn6855_rx_desc_get_msdu_freq, +#ifdef notyet + .rx_desc_get_msdu_pkt_type = ath12k_hw_wcn6855_rx_desc_get_msdu_pkt_type, + .rx_desc_get_msdu_nss = ath12k_hw_wcn6855_rx_desc_get_msdu_nss, + .rx_desc_get_mpdu_tid = ath12k_hw_wcn6855_rx_desc_get_mpdu_tid, + .rx_desc_get_mpdu_peer_id = ath12k_hw_wcn6855_rx_desc_get_mpdu_peer_id, + .rx_desc_copy_attn_end_tlv = ath12k_hw_wcn6855_rx_desc_copy_attn_end, + .rx_desc_get_mpdu_start_tag = ath12k_hw_wcn6855_rx_desc_get_mpdu_start_tag, + .rx_desc_get_mpdu_ppdu_id = ath12k_hw_wcn6855_rx_desc_get_mpdu_ppdu_id, + .rx_desc_set_msdu_len = ath12k_hw_wcn6855_rx_desc_set_msdu_len, +#endif + .rx_desc_get_attention = qwz_hw_wcn6855_rx_desc_get_attention, +#ifdef notyet + .rx_desc_get_msdu_payload = ath12k_hw_wcn6855_rx_desc_get_msdu_payload, +#endif + .reo_setup = qwz_hw_wcn6855_reo_setup, +#ifdef notyet + .mpdu_info_get_peerid = ath12k_hw_wcn6855_mpdu_info_get_peerid, + .rx_desc_mac_addr2_valid = ath12k_hw_wcn6855_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath12k_hw_wcn6855_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath12k_hw_ipq8074_get_tcl_ring_selector, +#endif +}; + +const struct ath12k_hw_ops wcn6750_ops = { + .get_hw_mac_from_pdev_id = qwz_hw_ipq8074_mac_from_pdev_id, + .wmi_init_config = qwz_init_wmi_config_qca6390, + .mac_id_to_pdev_id = qwz_hw_mac_id_to_pdev_id_qca6390, + .mac_id_to_srng_id = qwz_hw_mac_id_to_srng_id_qca6390, +#if notyet + .tx_mesh_enable = ath12k_hw_qcn9074_tx_mesh_enable, +#endif + .rx_desc_get_first_msdu = qwz_hw_qcn9074_rx_desc_get_first_msdu, +#if notyet + .rx_desc_get_last_msdu = ath12k_hw_qcn9074_rx_desc_get_last_msdu, +#endif + .rx_desc_get_l3_pad_bytes = qwz_hw_qcn9074_rx_desc_get_l3_pad_bytes, + .rx_desc_get_hdr_status = qwz_hw_qcn9074_rx_desc_get_hdr_status, + .rx_desc_encrypt_valid = qwz_hw_qcn9074_rx_desc_encrypt_valid, + .rx_desc_get_encrypt_type = qwz_hw_qcn9074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = qwz_hw_qcn9074_rx_desc_get_decap_type, +#ifdef notyet + .rx_desc_get_mesh_ctl = ath12k_hw_qcn9074_rx_desc_get_mesh_ctl, + .rx_desc_get_ldpc_support = ath12k_hw_qcn9074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath12k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath12k_hw_qcn9074_rx_desc_get_mpdu_fc_valid, + .rx_desc_get_mpdu_start_seq_no = ath12k_hw_qcn9074_rx_desc_get_mpdu_start_seq_no, +#endif + .rx_desc_get_msdu_len = qwz_hw_qcn9074_rx_desc_get_msdu_len, +#ifdef notyet + .rx_desc_get_msdu_sgi = ath12k_hw_qcn9074_rx_desc_get_msdu_sgi, + .rx_desc_get_msdu_rate_mcs = ath12k_hw_qcn9074_rx_desc_get_msdu_rate_mcs, + .rx_desc_get_msdu_rx_bw = ath12k_hw_qcn9074_rx_desc_get_msdu_rx_bw, +#endif + .rx_desc_get_msdu_freq = qwz_hw_qcn9074_rx_desc_get_msdu_freq, +#ifdef notyet + .rx_desc_get_msdu_pkt_type = ath12k_hw_qcn9074_rx_desc_get_msdu_pkt_type, + .rx_desc_get_msdu_nss = ath12k_hw_qcn9074_rx_desc_get_msdu_nss, + .rx_desc_get_mpdu_tid = ath12k_hw_qcn9074_rx_desc_get_mpdu_tid, + .rx_desc_get_mpdu_peer_id = ath12k_hw_qcn9074_rx_desc_get_mpdu_peer_id, + .rx_desc_copy_attn_end_tlv = ath12k_hw_qcn9074_rx_desc_copy_attn_end, + .rx_desc_get_mpdu_start_tag = ath12k_hw_qcn9074_rx_desc_get_mpdu_start_tag, + .rx_desc_get_mpdu_ppdu_id = ath12k_hw_qcn9074_rx_desc_get_mpdu_ppdu_id, + .rx_desc_set_msdu_len = ath12k_hw_qcn9074_rx_desc_set_msdu_len, +#endif + .rx_desc_get_attention = qwz_hw_qcn9074_rx_desc_get_attention, +#ifdef notyet + .rx_desc_get_msdu_payload = ath12k_hw_qcn9074_rx_desc_get_msdu_payload, +#endif + .reo_setup = qwz_hw_wcn6855_reo_setup, +#ifdef notyet + .mpdu_info_get_peerid = ath12k_hw_ipq8074_mpdu_info_get_peerid, + .rx_desc_mac_addr2_valid = ath12k_hw_ipq9074_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath12k_hw_ipq9074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath12k_hw_wcn6750_get_tcl_ring_selector, +#endif +}; + +#define ATH12K_TX_RING_MASK_0 BIT(0) +#define ATH12K_TX_RING_MASK_1 BIT(1) +#define ATH12K_TX_RING_MASK_2 BIT(2) +#define ATH12K_TX_RING_MASK_3 BIT(3) +#define ATH12K_TX_RING_MASK_4 BIT(4) + +#define ATH12K_RX_RING_MASK_0 0x1 +#define ATH12K_RX_RING_MASK_1 0x2 +#define ATH12K_RX_RING_MASK_2 0x4 +#define ATH12K_RX_RING_MASK_3 0x8 + +#define ATH12K_RX_ERR_RING_MASK_0 0x1 + +#define ATH12K_RX_WBM_REL_RING_MASK_0 0x1 + +#define ATH12K_REO_STATUS_RING_MASK_0 0x1 + +#define ATH12K_RXDMA2HOST_RING_MASK_0 0x1 +#define ATH12K_RXDMA2HOST_RING_MASK_1 0x2 +#define ATH12K_RXDMA2HOST_RING_MASK_2 0x4 + +#define ATH12K_HOST2RXDMA_RING_MASK_0 0x1 +#define ATH12K_HOST2RXDMA_RING_MASK_1 0x2 +#define ATH12K_HOST2RXDMA_RING_MASK_2 0x4 + +#define ATH12K_RX_MON_STATUS_RING_MASK_0 0x1 +#define ATH12K_RX_MON_STATUS_RING_MASK_1 0x2 +#define ATH12K_RX_MON_STATUS_RING_MASK_2 0x4 + +const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_ipq8074 = { + .tx = { + ATH12K_TX_RING_MASK_0, + ATH12K_TX_RING_MASK_1, + ATH12K_TX_RING_MASK_2, + }, + .rx_mon_status = { + 0, 0, 0, 0, + ATH12K_RX_MON_STATUS_RING_MASK_0, + ATH12K_RX_MON_STATUS_RING_MASK_1, + ATH12K_RX_MON_STATUS_RING_MASK_2, + }, + .rx = { + 0, 0, 0, 0, 0, 0, 0, + ATH12K_RX_RING_MASK_0, + ATH12K_RX_RING_MASK_1, + ATH12K_RX_RING_MASK_2, + ATH12K_RX_RING_MASK_3, + }, + .rx_err = { + ATH12K_RX_ERR_RING_MASK_0, + }, + .rx_wbm_rel = { + ATH12K_RX_WBM_REL_RING_MASK_0, + }, + .reo_status = { + ATH12K_REO_STATUS_RING_MASK_0, + }, + .rxdma2host = { + ATH12K_RXDMA2HOST_RING_MASK_0, + ATH12K_RXDMA2HOST_RING_MASK_1, + ATH12K_RXDMA2HOST_RING_MASK_2, + }, + .host2rxdma = { + ATH12K_HOST2RXDMA_RING_MASK_0, + ATH12K_HOST2RXDMA_RING_MASK_1, + ATH12K_HOST2RXDMA_RING_MASK_2, + }, +}; + +const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qca6390 = { + .tx = { + ATH12K_TX_RING_MASK_0, + }, + .rx_mon_status = { + 0, 0, 0, 0, + ATH12K_RX_MON_STATUS_RING_MASK_0, + ATH12K_RX_MON_STATUS_RING_MASK_1, + ATH12K_RX_MON_STATUS_RING_MASK_2, + }, + .rx = { + 0, 0, 0, 0, 0, 0, 0, + ATH12K_RX_RING_MASK_0, + ATH12K_RX_RING_MASK_1, + ATH12K_RX_RING_MASK_2, + ATH12K_RX_RING_MASK_3, + }, + .rx_err = { + ATH12K_RX_ERR_RING_MASK_0, + }, + .rx_wbm_rel = { + ATH12K_RX_WBM_REL_RING_MASK_0, + }, + .reo_status = { + ATH12K_REO_STATUS_RING_MASK_0, + }, + .rxdma2host = { + ATH12K_RXDMA2HOST_RING_MASK_0, + ATH12K_RXDMA2HOST_RING_MASK_1, + ATH12K_RXDMA2HOST_RING_MASK_2, + }, + .host2rxdma = { + }, +}; + +const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9074 = { + .tx = { + ATH12K_TX_RING_MASK_0, + ATH12K_TX_RING_MASK_1, + ATH12K_TX_RING_MASK_2, + }, + .rx_mon_status = { + 0, 0, 0, + ATH12K_RX_MON_STATUS_RING_MASK_0, + ATH12K_RX_MON_STATUS_RING_MASK_1, + ATH12K_RX_MON_STATUS_RING_MASK_2, + }, + .rx = { + 0, 0, 0, 0, + ATH12K_RX_RING_MASK_0, + ATH12K_RX_RING_MASK_1, + ATH12K_RX_RING_MASK_2, + ATH12K_RX_RING_MASK_3, + }, + .rx_err = { + 0, 0, 0, + ATH12K_RX_ERR_RING_MASK_0, + }, + .rx_wbm_rel = { + 0, 0, 0, + ATH12K_RX_WBM_REL_RING_MASK_0, + }, + .reo_status = { + 0, 0, 0, + ATH12K_REO_STATUS_RING_MASK_0, + }, + .rxdma2host = { + 0, 0, 0, + ATH12K_RXDMA2HOST_RING_MASK_0, + }, + .host2rxdma = { + 0, 0, 0, + ATH12K_HOST2RXDMA_RING_MASK_0, + }, +}; + +const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_wcn6750 = { + .tx = { + ATH12K_TX_RING_MASK_0, + 0, + ATH12K_TX_RING_MASK_2, + 0, + ATH12K_TX_RING_MASK_4, + }, + .rx_mon_status = { + 0, 0, 0, 0, 0, 0, + ATH12K_RX_MON_STATUS_RING_MASK_0, + }, + .rx = { + 0, 0, 0, 0, 0, 0, 0, + ATH12K_RX_RING_MASK_0, + ATH12K_RX_RING_MASK_1, + ATH12K_RX_RING_MASK_2, + ATH12K_RX_RING_MASK_3, + }, + .rx_err = { + 0, ATH12K_RX_ERR_RING_MASK_0, + }, + .rx_wbm_rel = { + 0, ATH12K_RX_WBM_REL_RING_MASK_0, + }, + .reo_status = { + 0, ATH12K_REO_STATUS_RING_MASK_0, + }, + .rxdma2host = { + ATH12K_RXDMA2HOST_RING_MASK_0, + ATH12K_RXDMA2HOST_RING_MASK_1, + ATH12K_RXDMA2HOST_RING_MASK_2, + }, + .host2rxdma = { + }, +}; + +/* Target firmware's Copy Engine configuration. */ +const struct ce_pipe_config ath12k_target_ce_config_wlan_ipq8074[] = { + /* CE0: host->target HTC control and raw streams */ + { + .pipenum = htole32(0), + .pipedir = htole32(PIPEDIR_OUT), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE1: target->host HTT + HTC control */ + { + .pipenum = htole32(1), + .pipedir = htole32(PIPEDIR_IN), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE2: target->host WMI */ + { + .pipenum = htole32(2), + .pipedir = htole32(PIPEDIR_IN), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE3: host->target WMI */ + { + .pipenum = htole32(3), + .pipedir = htole32(PIPEDIR_OUT), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE4: host->target HTT */ + { + .pipenum = htole32(4), + .pipedir = htole32(PIPEDIR_OUT), + .nentries = htole32(256), + .nbytes_max = htole32(256), + .flags = htole32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), + .reserved = htole32(0), + }, + + /* CE5: target->host Pktlog */ + { + .pipenum = htole32(5), + .pipedir = htole32(PIPEDIR_IN), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(0), + .reserved = htole32(0), + }, + + /* CE6: Reserved for target autonomous hif_memcpy */ + { + .pipenum = htole32(6), + .pipedir = htole32(PIPEDIR_INOUT), + .nentries = htole32(32), + .nbytes_max = htole32(65535), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE7 used only by Host */ + { + .pipenum = htole32(7), + .pipedir = htole32(PIPEDIR_OUT), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE8 target->host used only by IPA */ + { + .pipenum = htole32(8), + .pipedir = htole32(PIPEDIR_INOUT), + .nentries = htole32(32), + .nbytes_max = htole32(65535), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE9 host->target HTT */ + { + .pipenum = htole32(9), + .pipedir = htole32(PIPEDIR_OUT), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE10 target->host HTT */ + { + .pipenum = htole32(10), + .pipedir = htole32(PIPEDIR_INOUT_H2H), + .nentries = htole32(0), + .nbytes_max = htole32(0), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE11 Not used */ +}; + +/* Map from service/endpoint to Copy Engine. + * This table is derived from the CE_PCI TABLE, above. + * It is passed to the Target at startup for use by firmware. + */ +const struct service_to_pipe ath12k_target_service_to_ce_map_wlan_ipq8074[] = { + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VO), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(3), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VO), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BK), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(3), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BK), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BE), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(3), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BE), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VI), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(3), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VI), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(3), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(7), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(9), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_RSVD_CTRL), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(0), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_RSVD_CTRL), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(1), + }, + { /* not used */ + .service_id = htole32(ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(0), + }, + { /* not used */ + .service_id = htole32(ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(1), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(4), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(1), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_PKT_LOG), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(5), + }, + + /* (Additions here) */ + + { /* terminator entry */ } +}; + +const struct service_to_pipe ath12k_target_service_to_ce_map_wlan_ipq6018[] = { + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VO), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(3), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VO), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BK), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(3), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BK), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BE), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(3), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BE), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VI), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(3), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VI), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(3), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(7), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(2), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_RSVD_CTRL), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(0), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_RSVD_CTRL), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(1), + }, + { /* not used */ + .service_id = htole32(ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(0), + }, + { /* not used */ + .service_id = htole32(ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(1), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG), + .pipedir = htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = htole32(4), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(1), + }, + { + .service_id = htole32(ATH12K_HTC_SVC_ID_PKT_LOG), + .pipedir = htole32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = htole32(5), + }, + + /* (Additions here) */ + + { /* terminator entry */ } +}; + +/* Target firmware's Copy Engine configuration. */ +const struct ce_pipe_config ath12k_target_ce_config_wlan_qca6390[] = { + /* CE0: host->target HTC control and raw streams */ + { + .pipenum = htole32(0), + .pipedir = htole32(PIPEDIR_OUT), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE1: target->host HTT + HTC control */ + { + .pipenum = htole32(1), + .pipedir = htole32(PIPEDIR_IN), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE2: target->host WMI */ + { + .pipenum = htole32(2), + .pipedir = htole32(PIPEDIR_IN), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE3: host->target WMI */ + { + .pipenum = htole32(3), + .pipedir = htole32(PIPEDIR_OUT), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE4: host->target HTT */ + { + .pipenum = htole32(4), + .pipedir = htole32(PIPEDIR_OUT), + .nentries = htole32(256), + .nbytes_max = htole32(256), + .flags = htole32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), + .reserved = htole32(0), + }, + + /* CE5: target->host Pktlog */ + { + .pipenum = htole32(5), + .pipedir = htole32(PIPEDIR_IN), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE6: Reserved for target autonomous hif_memcpy */ + { + .pipenum = htole32(6), + .pipedir = htole32(PIPEDIR_INOUT), + .nentries = htole32(32), + .nbytes_max = htole32(16384), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE7 used only by Host */ + { + .pipenum = htole32(7), + .pipedir = htole32(PIPEDIR_INOUT_H2H), + .nentries = htole32(0), + .nbytes_max = htole32(0), + .flags = htole32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), + .reserved = htole32(0), + }, + + /* CE8 target->host used only by IPA */ + { + .pipenum = htole32(8), + .pipedir = htole32(PIPEDIR_INOUT), + .nentries = htole32(32), + .nbytes_max = htole32(16384), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + /* CE 9, 10, 11 are used by MHI driver */ +}; + +/* Map from service/endpoint to Copy Engine. + * This table is derived from the CE_PCI TABLE, above. + * It is passed to the Target at startup for use by firmware. + */ +const struct service_to_pipe ath12k_target_service_to_ce_map_wlan_qca6390[] = { + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VO), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(3), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VO), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(2), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BK), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(3), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BK), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(2), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BE), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(3), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BE), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(2), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VI), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(3), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VI), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(2), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(3), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(2), + }, + { + htole32(ATH12K_HTC_SVC_ID_RSVD_CTRL), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(0), + }, + { + htole32(ATH12K_HTC_SVC_ID_RSVD_CTRL), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(2), + }, + { + htole32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(4), + }, + { + htole32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(1), + }, + + /* (Additions here) */ + + { /* must be last */ + htole32(0), + htole32(0), + htole32(0), + }, +}; + +/* Target firmware's Copy Engine configuration. */ +const struct ce_pipe_config ath12k_target_ce_config_wlan_qcn9074[] = { + /* CE0: host->target HTC control and raw streams */ + { + .pipenum = htole32(0), + .pipedir = htole32(PIPEDIR_OUT), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE1: target->host HTT + HTC control */ + { + .pipenum = htole32(1), + .pipedir = htole32(PIPEDIR_IN), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE2: target->host WMI */ + { + .pipenum = htole32(2), + .pipedir = htole32(PIPEDIR_IN), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE3: host->target WMI */ + { + .pipenum = htole32(3), + .pipedir = htole32(PIPEDIR_OUT), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE4: host->target HTT */ + { + .pipenum = htole32(4), + .pipedir = htole32(PIPEDIR_OUT), + .nentries = htole32(256), + .nbytes_max = htole32(256), + .flags = htole32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), + .reserved = htole32(0), + }, + + /* CE5: target->host Pktlog */ + { + .pipenum = htole32(5), + .pipedir = htole32(PIPEDIR_IN), + .nentries = htole32(32), + .nbytes_max = htole32(2048), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE6: Reserved for target autonomous hif_memcpy */ + { + .pipenum = htole32(6), + .pipedir = htole32(PIPEDIR_INOUT), + .nentries = htole32(32), + .nbytes_max = htole32(16384), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + + /* CE7 used only by Host */ + { + .pipenum = htole32(7), + .pipedir = htole32(PIPEDIR_INOUT_H2H), + .nentries = htole32(0), + .nbytes_max = htole32(0), + .flags = htole32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), + .reserved = htole32(0), + }, + + /* CE8 target->host used only by IPA */ + { + .pipenum = htole32(8), + .pipedir = htole32(PIPEDIR_INOUT), + .nentries = htole32(32), + .nbytes_max = htole32(16384), + .flags = htole32(CE_ATTR_FLAGS), + .reserved = htole32(0), + }, + /* CE 9, 10, 11 are used by MHI driver */ +}; + +/* Map from service/endpoint to Copy Engine. + * This table is derived from the CE_PCI TABLE, above. + * It is passed to the Target at startup for use by firmware. + */ +const struct service_to_pipe ath12k_target_service_to_ce_map_wlan_qcn9074[] = { + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VO), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(3), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VO), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(2), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BK), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(3), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BK), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(2), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BE), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(3), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_BE), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(2), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VI), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(3), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_DATA_VI), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(2), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(3), + }, + { + htole32(ATH12K_HTC_SVC_ID_WMI_CONTROL), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(2), + }, + { + htole32(ATH12K_HTC_SVC_ID_RSVD_CTRL), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(0), + }, + { + htole32(ATH12K_HTC_SVC_ID_RSVD_CTRL), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(1), + }, + { + htole32(ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(0), + }, + { + htole32(ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(1), + }, + { + htole32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG), + htole32(PIPEDIR_OUT), /* out = UL = host -> target */ + htole32(4), + }, + { + htole32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(1), + }, + { + htole32(ATH12K_HTC_SVC_ID_PKT_LOG), + htole32(PIPEDIR_IN), /* in = DL = target -> host */ + htole32(5), + }, + + /* (Additions here) */ + + { /* must be last */ + htole32(0), + htole32(0), + htole32(0), + }, +}; + +#define QWZ_CE_COUNT_IPQ8074 21 + +const struct ce_attr qwz_host_ce_config_ipq8074[QWZ_CE_COUNT_IPQ8074] = { + /* CE0: host->target HTC control and raw streams */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 16, + .src_sz_max = 2048, + .dest_nentries = 0, + .send_cb = qwz_htc_tx_completion_handler, + }, + + /* CE1: target->host HTT + HTC control */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = qwz_htc_rx_completion_handler, + }, + + /* CE2: target->host WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = qwz_htc_rx_completion_handler, + }, + + /* CE3: host->target WMI (mac0) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + .send_cb = qwz_htc_tx_completion_handler, + }, + + /* CE4: host->target HTT */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 2048, + .src_sz_max = 256, + .dest_nentries = 0, + }, + + /* CE5: target->host pktlog */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = qwz_dp_htt_htc_t2h_msg_handler, + }, + + /* CE6: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE7: host->target WMI (mac1) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + .send_cb = qwz_htc_tx_completion_handler, + }, + + /* CE8: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE9: host->target WMI (mac2) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + .send_cb = qwz_htc_tx_completion_handler, + }, + + /* CE10: target->host HTT */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = qwz_htc_rx_completion_handler, + }, + + /* CE11: Not used */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, +}; + +#define QWZ_CE_COUNT_QCA6390 9 + +const struct ce_attr qwz_host_ce_config_qca6390[QWZ_CE_COUNT_QCA6390] = { + /* CE0: host->target HTC control and raw streams */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 16, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE1: target->host HTT + HTC control */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = qwz_htc_rx_completion_handler, + }, + + /* CE2: target->host WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = qwz_htc_rx_completion_handler, + }, + + /* CE3: host->target WMI (mac0) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + .send_cb = qwz_htc_tx_completion_handler, + }, + + /* CE4: host->target HTT */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 2048, + .src_sz_max = 256, + .dest_nentries = 0, + }, + + /* CE5: target->host pktlog */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = qwz_dp_htt_htc_t2h_msg_handler, + }, + + /* CE6: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE7: host->target WMI (mac1) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + .send_cb = qwz_htc_tx_completion_handler, + }, + + /* CE8: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + +}; + +#define QWZ_CE_COUNT_QCN9074 6 + +const struct ce_attr qwz_host_ce_config_qcn9074[QWZ_CE_COUNT_QCN9074] = { + /* CE0: host->target HTC control and raw streams */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 16, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE1: target->host HTT + HTC control */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = qwz_htc_rx_completion_handler, + }, + + /* CE2: target->host WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 32, + .recv_cb = qwz_htc_rx_completion_handler, + }, + + /* CE3: host->target WMI (mac0) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + .send_cb = qwz_htc_tx_completion_handler, + }, + + /* CE4: host->target HTT */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 2048, + .src_sz_max = 256, + .dest_nentries = 0, + }, + + /* CE5: target->host pktlog */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = qwz_dp_htt_htc_t2h_msg_handler, + }, +}; + +static const struct ath12k_hw_tcl2wbm_rbm_map ath12k_hw_tcl2wbm_rbm_map_ipq8074[] = { + { + .tcl_ring_num = 0, + .wbm_ring_num = 0, + .rbm_id = HAL_RX_BUF_RBM_SW0_BM, + }, + { + .tcl_ring_num = 1, + .wbm_ring_num = 1, + .rbm_id = HAL_RX_BUF_RBM_SW1_BM, + }, + { + .tcl_ring_num = 2, + .wbm_ring_num = 2, + .rbm_id = HAL_RX_BUF_RBM_SW2_BM, + }, +}; + +static const struct ath12k_hw_tcl2wbm_rbm_map ath12k_hw_tcl2wbm_rbm_map_wcn6750[] = { + { + .tcl_ring_num = 0, + .wbm_ring_num = 0, + .rbm_id = HAL_RX_BUF_RBM_SW0_BM, + }, + { + .tcl_ring_num = 1, + .wbm_ring_num = 4, + .rbm_id = HAL_RX_BUF_RBM_SW4_BM, + }, + { + .tcl_ring_num = 2, + .wbm_ring_num = 2, + .rbm_id = HAL_RX_BUF_RBM_SW2_BM, + }, +}; + + +static const struct ath12k_hw_hal_params ath12k_hw_hal_params_ipq8074 = { + .rx_buf_rbm = HAL_RX_BUF_RBM_SW3_BM, + .tcl2wbm_rbm_map = ath12k_hw_tcl2wbm_rbm_map_ipq8074, +}; + +static const struct ath12k_hw_hal_params ath12k_hw_hal_params_qca6390 = { + .rx_buf_rbm = HAL_RX_BUF_RBM_SW1_BM, + .tcl2wbm_rbm_map = ath12k_hw_tcl2wbm_rbm_map_ipq8074, +}; + +static const struct ath12k_hw_hal_params ath12k_hw_hal_params_wcn6750 = { + .rx_buf_rbm = HAL_RX_BUF_RBM_SW1_BM, + .tcl2wbm_rbm_map = ath12k_hw_tcl2wbm_rbm_map_wcn6750, +}; + +static const struct ath12k_hw_params ath12k_hw_params[] = { + { + .hw_rev = ATH12K_HW_IPQ8074, + .name = "ipq8074 hw2.0", + .fw = { + .dir = "ipq8074-hw2.0", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, + .hw_ops = &ipq8074_ops, + .ring_mask = &ath12k_hw_ring_mask_ipq8074, + .internal_sleep_clock = false, + .regs = &ipq8074_regs, + .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074, + .host_ce_config = qwz_host_ce_config_ipq8074, + .ce_count = QWZ_CE_COUNT_IPQ8074, + .target_ce_config = ath12k_target_ce_config_wlan_ipq8074, + .target_ce_count = 11, + .svc_to_ce_map = ath12k_target_service_to_ce_map_wlan_ipq8074, + .svc_to_ce_map_len = 21, + .single_pdev_only = false, + .rxdma1_enable = true, + .num_rxmda_per_pdev = 1, + .rx_mac_buf_ring = false, + .vdev_start_delay = false, + .htt_peer_map_v2 = true, +#if notyet + .spectral = { + .fft_sz = 2, + /* HW bug, expected BIN size is 2 bytes but HW report as 4 bytes. + * so added pad size as 2 bytes to compensate the BIN size + */ + .fft_pad_sz = 2, + .summary_pad_sz = 0, + .fft_hdr_len = 16, + .max_fft_bins = 512, + .fragment_160mhz = true, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT), + .supports_monitor = true, + .full_monitor_mode = false, +#endif + .supports_shadow_regs = false, + .idle_ps = false, + .supports_sta_ps = false, + .cold_boot_calib = true, + .cbcal_restart_fw = true, + .fw_mem_mode = 0, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .supports_regdb = false, + .fix_l1ss = true, + .credit_flow = false, + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath12k_hw_hal_params_ipq8074, +#if notyet + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = true, + .supports_rssi_stats = false, +#endif + .fw_wmi_diag_event = false, + .current_cc_support = false, + .dbr_debug_support = true, + .global_reset = false, +#ifdef notyet + .bios_sar_capa = NULL, +#endif + .m3_fw_support = false, + .fixed_bdf_addr = true, + .fixed_mem_region = true, + .static_window_map = false, +#if notyet + .hybrid_bus_type = false, + .fixed_fw_mem = false, + .support_off_channel_tx = false, + .supports_multi_bssid = false, + + .sram_dump = {}, + + .tcl_ring_retry = true, +#endif + .tx_ring_size = DP_TCL_DATA_RING_SIZE, +#ifdef notyet + .smp2p_wow_exit = false, +#endif + }, + { + .hw_rev = ATH12K_HW_IPQ6018_HW10, + .name = "ipq6018 hw1.0", + .fw = { + .dir = "ipq6018-hw1.0", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + }, + .max_radios = 2, + .bdf_addr = 0x4ABC0000, + .hw_ops = &ipq6018_ops, + .ring_mask = &ath12k_hw_ring_mask_ipq8074, + .internal_sleep_clock = false, + .regs = &ipq8074_regs, + .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074, + .host_ce_config = qwz_host_ce_config_ipq8074, + .ce_count = QWZ_CE_COUNT_IPQ8074, + .target_ce_config = ath12k_target_ce_config_wlan_ipq8074, + .target_ce_count = 11, + .svc_to_ce_map = ath12k_target_service_to_ce_map_wlan_ipq6018, + .svc_to_ce_map_len = 19, + .single_pdev_only = false, + .rxdma1_enable = true, + .num_rxmda_per_pdev = 1, + .rx_mac_buf_ring = false, + .vdev_start_delay = false, + .htt_peer_map_v2 = true, +#if notyet + .spectral = { + .fft_sz = 4, + .fft_pad_sz = 0, + .summary_pad_sz = 0, + .fft_hdr_len = 16, + .max_fft_bins = 512, + .fragment_160mhz = true, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT), + .supports_monitor = true, + .full_monitor_mode = false, +#endif + .supports_shadow_regs = false, + .idle_ps = false, + .supports_sta_ps = false, + .cold_boot_calib = true, + .cbcal_restart_fw = true, + .fw_mem_mode = 0, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .supports_regdb = false, + .fix_l1ss = true, + .credit_flow = false, + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath12k_hw_hal_params_ipq8074, +#if notyet + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = true, + .supports_rssi_stats = false, +#endif + .fw_wmi_diag_event = false, + .current_cc_support = false, + .dbr_debug_support = true, + .global_reset = false, +#ifdef notyet + .bios_sar_capa = NULL, +#endif + .m3_fw_support = false, + .fixed_bdf_addr = true, + .fixed_mem_region = true, + .static_window_map = false, + .hybrid_bus_type = false, + .fixed_fw_mem = false, +#if notyet + .support_off_channel_tx = false, + .supports_multi_bssid = false, + + .sram_dump = {}, + + .tcl_ring_retry = true, +#endif + .tx_ring_size = DP_TCL_DATA_RING_SIZE, +#ifdef notyet + .smp2p_wow_exit = false, +#endif + }, + { + .name = "qca6390 hw2.0", + .hw_rev = ATH12K_HW_QCA6390_HW20, + .fw = { + .dir = "qca6390-hw2.0", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, + .hw_ops = &qca6390_ops, + .ring_mask = &ath12k_hw_ring_mask_qca6390, + .internal_sleep_clock = true, + .regs = &qca6390_regs, + .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390, + .host_ce_config = qwz_host_ce_config_qca6390, + .ce_count = QWZ_CE_COUNT_QCA6390, + .target_ce_config = ath12k_target_ce_config_wlan_qca6390, + .target_ce_count = 9, + .svc_to_ce_map = ath12k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + .htt_peer_map_v2 = false, +#if notyet + .spectral = { + .fft_sz = 0, + .fft_pad_sz = 0, + .summary_pad_sz = 0, + .fft_hdr_len = 0, + .max_fft_bins = 0, + .fragment_160mhz = false, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP), + .supports_monitor = false, + .full_monitor_mode = false, +#endif + .supports_shadow_regs = true, + .idle_ps = true, + .supports_sta_ps = true, + .cold_boot_calib = false, + .cbcal_restart_fw = false, + .fw_mem_mode = 0, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), + .supports_regdb = false, + .fix_l1ss = true, + .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath12k_hw_hal_params_qca6390, +#if notyet + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, + .supports_rssi_stats = true, +#endif + .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, + .global_reset = true, +#ifdef notyet + .bios_sar_capa = NULL, +#endif + .m3_fw_support = true, + .fixed_bdf_addr = false, + .fixed_mem_region = false, + .static_window_map = false, + .hybrid_bus_type = false, + .fixed_fw_mem = false, +#if notyet + .support_off_channel_tx = true, + .supports_multi_bssid = true, + + .sram_dump = { + .start = 0x01400000, + .end = 0x0171ffff, + }, + + .tcl_ring_retry = true, +#endif + .tx_ring_size = DP_TCL_DATA_RING_SIZE, +#ifdef notyet + .smp2p_wow_exit = false, +#endif + }, + { + .name = "qcn9074 hw1.0", + .hw_rev = ATH12K_HW_QCN9074_HW10, + .fw = { + .dir = "qcn9074-hw1.0", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + }, + .max_radios = 1, +#if notyet + .single_pdev_only = false, + .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9074, +#endif + .hw_ops = &qcn9074_ops, + .ring_mask = &ath12k_hw_ring_mask_qcn9074, + .internal_sleep_clock = false, + .regs = &qcn9074_regs, + .host_ce_config = qwz_host_ce_config_qcn9074, + .ce_count = QWZ_CE_COUNT_QCN9074, + .target_ce_config = ath12k_target_ce_config_wlan_qcn9074, + .target_ce_count = 9, + .svc_to_ce_map = ath12k_target_service_to_ce_map_wlan_qcn9074, + .svc_to_ce_map_len = 18, + .rxdma1_enable = true, + .num_rxmda_per_pdev = 1, + .rx_mac_buf_ring = false, + .vdev_start_delay = false, + .htt_peer_map_v2 = true, +#if notyet + .spectral = { + .fft_sz = 2, + .fft_pad_sz = 0, + .summary_pad_sz = 16, + .fft_hdr_len = 24, + .max_fft_bins = 1024, + .fragment_160mhz = false, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT), + .supports_monitor = true, + .full_monitor_mode = true, +#endif + .supports_shadow_regs = false, + .idle_ps = false, + .supports_sta_ps = false, + .cold_boot_calib = false, + .cbcal_restart_fw = false, + .fw_mem_mode = 2, + .num_vdevs = 8, + .num_peers = 128, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), + .supports_regdb = false, + .fix_l1ss = true, + .credit_flow = false, + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath12k_hw_hal_params_ipq8074, +#if notyet + .supports_dynamic_smps_6ghz = true, + .alloc_cacheable_memory = true, + .supports_rssi_stats = false, +#endif + .fw_wmi_diag_event = false, + .current_cc_support = false, + .dbr_debug_support = true, + .global_reset = false, +#ifdef notyet + .bios_sar_capa = NULL, +#endif + .m3_fw_support = true, + .fixed_bdf_addr = false, + .fixed_mem_region = false, + .static_window_map = true, + .hybrid_bus_type = false, + .fixed_fw_mem = false, +#if notyet + .support_off_channel_tx = false, + .supports_multi_bssid = false, + + .sram_dump = {}, + + .tcl_ring_retry = true, +#endif + .tx_ring_size = DP_TCL_DATA_RING_SIZE, +#ifdef notyet + .smp2p_wow_exit = false, +#endif + }, + { + .name = "wcn6855 hw2.0", + .hw_rev = ATH12K_HW_WCN6855_HW20, + .fw = { + .dir = "wcn6855-hw2.0", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, + .hw_ops = &wcn6855_ops, + .ring_mask = &ath12k_hw_ring_mask_qca6390, + .internal_sleep_clock = true, + .regs = &wcn6855_regs, + .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390, + .host_ce_config = qwz_host_ce_config_qca6390, + .ce_count = QWZ_CE_COUNT_QCA6390, + .target_ce_config = ath12k_target_ce_config_wlan_qca6390, + .target_ce_count = 9, + .svc_to_ce_map = ath12k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + .htt_peer_map_v2 = false, +#if notyet + .spectral = { + .fft_sz = 0, + .fft_pad_sz = 0, + .summary_pad_sz = 0, + .fft_hdr_len = 0, + .max_fft_bins = 0, + .fragment_160mhz = false, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP), + .supports_monitor = false, + .full_monitor_mode = false, +#endif + .supports_shadow_regs = true, + .idle_ps = true, + .supports_sta_ps = true, + .cold_boot_calib = false, + .cbcal_restart_fw = false, + .fw_mem_mode = 0, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), + .supports_regdb = true, + .fix_l1ss = false, + .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath12k_hw_hal_params_qca6390, +#if notyet + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, + .supports_rssi_stats = true, +#endif + .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, + .global_reset = true, +#ifdef notyet + .bios_sar_capa = &ath12k_hw_sar_capa_wcn6855, +#endif + .m3_fw_support = true, + .fixed_bdf_addr = false, + .fixed_mem_region = false, + .static_window_map = false, + .hybrid_bus_type = false, + .fixed_fw_mem = false, +#if notyet + .support_off_channel_tx = true, + .supports_multi_bssid = true, + + .sram_dump = { + .start = 0x01400000, + .end = 0x0177ffff, + }, + + .tcl_ring_retry = true, +#endif + .tx_ring_size = DP_TCL_DATA_RING_SIZE, +#ifdef notyet + .smp2p_wow_exit = false, +#endif + }, + { + .name = "wcn6855 hw2.1", + .hw_rev = ATH12K_HW_WCN6855_HW21, + .fw = { + .dir = "wcn6855-hw2.1", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, + .hw_ops = &wcn6855_ops, + .ring_mask = &ath12k_hw_ring_mask_qca6390, + .internal_sleep_clock = true, + .regs = &wcn6855_regs, + .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390, + .host_ce_config = qwz_host_ce_config_qca6390, + .ce_count = QWZ_CE_COUNT_QCA6390, + .target_ce_config = ath12k_target_ce_config_wlan_qca6390, + .target_ce_count = 9, + .svc_to_ce_map = ath12k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + .htt_peer_map_v2 = false, +#if notyet + .spectral = { + .fft_sz = 0, + .fft_pad_sz = 0, + .summary_pad_sz = 0, + .fft_hdr_len = 0, + .max_fft_bins = 0, + .fragment_160mhz = false, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP), + .supports_monitor = false, +#endif + .supports_shadow_regs = true, + .idle_ps = true, + .supports_sta_ps = true, + .cold_boot_calib = false, + .cbcal_restart_fw = false, + .fw_mem_mode = 0, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), + .supports_regdb = true, + .fix_l1ss = false, + .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath12k_hw_hal_params_qca6390, +#if notyet + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, + .supports_rssi_stats = true, +#endif + .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, + .global_reset = true, +#ifdef notyet + .bios_sar_capa = &ath12k_hw_sar_capa_wcn6855, +#endif + .m3_fw_support = true, + .fixed_bdf_addr = false, + .fixed_mem_region = false, + .static_window_map = false, + .hybrid_bus_type = false, + .fixed_fw_mem = false, +#if notyet + .support_off_channel_tx = true, + .supports_multi_bssid = true, + + .sram_dump = { + .start = 0x01400000, + .end = 0x0177ffff, + }, + + .tcl_ring_retry = true, +#endif + .tx_ring_size = DP_TCL_DATA_RING_SIZE, +#ifdef notyet + .smp2p_wow_exit = false, +#endif + }, + { + .name = "wcn6750 hw1.0", + .hw_rev = ATH12K_HW_WCN6750_HW10, + .fw = { + .dir = "wcn6750-hw1.0", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + }, + .max_radios = 1, + .bdf_addr = 0x4B0C0000, + .hw_ops = &wcn6750_ops, + .ring_mask = &ath12k_hw_ring_mask_wcn6750, + .internal_sleep_clock = false, + .regs = &wcn6750_regs, + .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN6750, + .host_ce_config = qwz_host_ce_config_qca6390, + .ce_count = QWZ_CE_COUNT_QCA6390, + .target_ce_config = ath12k_target_ce_config_wlan_qca6390, + .target_ce_count = 9, + .svc_to_ce_map = ath12k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 1, + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + .htt_peer_map_v2 = false, +#if notyet + .spectral = { + .fft_sz = 0, + .fft_pad_sz = 0, + .summary_pad_sz = 0, + .fft_hdr_len = 0, + .max_fft_bins = 0, + .fragment_160mhz = false, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP), + .supports_monitor = false, +#endif + .supports_shadow_regs = true, + .idle_ps = true, + .supports_sta_ps = true, + .cold_boot_calib = true, + .cbcal_restart_fw = false, + .fw_mem_mode = 0, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = false, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), + .supports_regdb = true, + .fix_l1ss = false, + .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath12k_hw_hal_params_wcn6750, +#if notyet + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, + .supports_rssi_stats = true, +#endif + .fw_wmi_diag_event = false, + .current_cc_support = true, + .dbr_debug_support = false, + .global_reset = false, +#ifdef notyet + .bios_sar_capa = NULL, +#endif + .m3_fw_support = false, + .fixed_bdf_addr = false, + .fixed_mem_region = false, + .static_window_map = true, + .hybrid_bus_type = true, + .fixed_fw_mem = true, +#if notyet + .support_off_channel_tx = true, + .supports_multi_bssid = true, + + .sram_dump = {}, + + .tcl_ring_retry = false, +#endif + .tx_ring_size = DP_TCL_DATA_RING_SIZE_WCN6750, +#ifdef notyet + .smp2p_wow_exit = true, +#endif + }, +}; + +const struct ath12k_hw_regs ipq8074_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .hal_tcl1_ring_base_lsb = 0x00000510, + .hal_tcl1_ring_base_msb = 0x00000514, + .hal_tcl1_ring_id = 0x00000518, + .hal_tcl1_ring_misc = 0x00000520, + .hal_tcl1_ring_tp_addr_lsb = 0x0000052c, + .hal_tcl1_ring_tp_addr_msb = 0x00000530, + .hal_tcl1_ring_consumer_int_setup_ix0 = 0x00000540, + .hal_tcl1_ring_consumer_int_setup_ix1 = 0x00000544, + .hal_tcl1_ring_msi1_base_lsb = 0x00000558, + .hal_tcl1_ring_msi1_base_msb = 0x0000055c, + .hal_tcl1_ring_msi1_data = 0x00000560, + .hal_tcl2_ring_base_lsb = 0x00000568, + .hal_tcl_ring_base_lsb = 0x00000618, + + /* TCL STATUS ring address */ + .hal_tcl_status_ring_base_lsb = 0x00000720, + + /* REO2SW(x) R0 ring configuration address */ + .hal_reo1_ring_base_lsb = 0x0000029c, + .hal_reo1_ring_base_msb = 0x000002a0, + .hal_reo1_ring_id = 0x000002a4, + .hal_reo1_ring_misc = 0x000002ac, + .hal_reo1_ring_hp_addr_lsb = 0x000002b0, + .hal_reo1_ring_hp_addr_msb = 0x000002b4, + .hal_reo1_ring_producer_int_setup = 0x000002c0, + .hal_reo1_ring_msi1_base_lsb = 0x000002e4, + .hal_reo1_ring_msi1_base_msb = 0x000002e8, + .hal_reo1_ring_msi1_data = 0x000002ec, + .hal_reo2_ring_base_lsb = 0x000002f4, + .hal_reo1_aging_thresh_ix_0 = 0x00000564, + .hal_reo1_aging_thresh_ix_1 = 0x00000568, + .hal_reo1_aging_thresh_ix_2 = 0x0000056c, + .hal_reo1_aging_thresh_ix_3 = 0x00000570, + + /* REO2SW(x) R2 ring pointers (head/tail) address */ + .hal_reo1_ring_hp = 0x00003038, + .hal_reo1_ring_tp = 0x0000303c, + .hal_reo2_ring_hp = 0x00003040, + + /* REO2TCL R0 ring configuration address */ + .hal_reo_tcl_ring_base_lsb = 0x000003fc, + .hal_reo_tcl_ring_hp = 0x00003058, + + /* REO CMD ring address */ + .hal_reo_cmd_ring_base_lsb = 0x00000194, + .hal_reo_cmd_ring_hp = 0x00003020, + + /* REO status address */ + .hal_reo_status_ring_base_lsb = 0x00000504, + .hal_reo_status_hp = 0x00003070, + + /* SW2REO ring address */ + .hal_sw2reo_ring_base_lsb = 0x000001ec, + .hal_sw2reo_ring_hp = 0x00003028, + + /* WCSS relative address */ + .hal_seq_wcss_umac_ce0_src_reg = 0x00a00000, + .hal_seq_wcss_umac_ce0_dst_reg = 0x00a01000, + .hal_seq_wcss_umac_ce1_src_reg = 0x00a02000, + .hal_seq_wcss_umac_ce1_dst_reg = 0x00a03000, + + /* WBM Idle address */ + .hal_wbm_idle_link_ring_base_lsb = 0x00000860, + .hal_wbm_idle_link_ring_misc = 0x00000870, + + /* SW2WBM release address */ + .hal_wbm_release_ring_base_lsb = 0x000001d8, + + /* WBM2SW release address */ + .hal_wbm0_release_ring_base_lsb = 0x00000910, + .hal_wbm1_release_ring_base_lsb = 0x00000968, + + /* PCIe base address */ + .pcie_qserdes_sysclk_en_sel = 0x0, + .pcie_pcs_osc_dtct_config_base = 0x0, + + /* Shadow register area */ + .hal_shadow_base_addr = 0x0, + + /* REO misc control register, not used in IPQ8074 */ + .hal_reo1_misc_ctl = 0x0, +}; + +const struct ath12k_hw_regs qca6390_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .hal_tcl1_ring_base_lsb = 0x00000684, + .hal_tcl1_ring_base_msb = 0x00000688, + .hal_tcl1_ring_id = 0x0000068c, + .hal_tcl1_ring_misc = 0x00000694, + .hal_tcl1_ring_tp_addr_lsb = 0x000006a0, + .hal_tcl1_ring_tp_addr_msb = 0x000006a4, + .hal_tcl1_ring_consumer_int_setup_ix0 = 0x000006b4, + .hal_tcl1_ring_consumer_int_setup_ix1 = 0x000006b8, + .hal_tcl1_ring_msi1_base_lsb = 0x000006cc, + .hal_tcl1_ring_msi1_base_msb = 0x000006d0, + .hal_tcl1_ring_msi1_data = 0x000006d4, + .hal_tcl2_ring_base_lsb = 0x000006dc, + .hal_tcl_ring_base_lsb = 0x0000078c, + + /* TCL STATUS ring address */ + .hal_tcl_status_ring_base_lsb = 0x00000894, + + /* REO2SW(x) R0 ring configuration address */ + .hal_reo1_ring_base_lsb = 0x00000244, + .hal_reo1_ring_base_msb = 0x00000248, + .hal_reo1_ring_id = 0x0000024c, + .hal_reo1_ring_misc = 0x00000254, + .hal_reo1_ring_hp_addr_lsb = 0x00000258, + .hal_reo1_ring_hp_addr_msb = 0x0000025c, + .hal_reo1_ring_producer_int_setup = 0x00000268, + .hal_reo1_ring_msi1_base_lsb = 0x0000028c, + .hal_reo1_ring_msi1_base_msb = 0x00000290, + .hal_reo1_ring_msi1_data = 0x00000294, + .hal_reo2_ring_base_lsb = 0x0000029c, + .hal_reo1_aging_thresh_ix_0 = 0x0000050c, + .hal_reo1_aging_thresh_ix_1 = 0x00000510, + .hal_reo1_aging_thresh_ix_2 = 0x00000514, + .hal_reo1_aging_thresh_ix_3 = 0x00000518, + + /* REO2SW(x) R2 ring pointers (head/tail) address */ + .hal_reo1_ring_hp = 0x00003030, + .hal_reo1_ring_tp = 0x00003034, + .hal_reo2_ring_hp = 0x00003038, + + /* REO2TCL R0 ring configuration address */ + .hal_reo_tcl_ring_base_lsb = 0x000003a4, + .hal_reo_tcl_ring_hp = 0x00003050, + + /* REO CMD ring address */ + .hal_reo_cmd_ring_base_lsb = 0x00000194, + .hal_reo_cmd_ring_hp = 0x00003020, + + /* REO status address */ + .hal_reo_status_ring_base_lsb = 0x000004ac, + .hal_reo_status_hp = 0x00003068, + + /* SW2REO ring address */ + .hal_sw2reo_ring_base_lsb = 0x000001ec, + .hal_sw2reo_ring_hp = 0x00003028, + + /* WCSS relative address */ + .hal_seq_wcss_umac_ce0_src_reg = 0x00a00000, + .hal_seq_wcss_umac_ce0_dst_reg = 0x00a01000, + .hal_seq_wcss_umac_ce1_src_reg = 0x00a02000, + .hal_seq_wcss_umac_ce1_dst_reg = 0x00a03000, + + /* WBM Idle address */ + .hal_wbm_idle_link_ring_base_lsb = 0x00000860, + .hal_wbm_idle_link_ring_misc = 0x00000870, + + /* SW2WBM release address */ + .hal_wbm_release_ring_base_lsb = 0x000001d8, + + /* WBM2SW release address */ + .hal_wbm0_release_ring_base_lsb = 0x00000910, + .hal_wbm1_release_ring_base_lsb = 0x00000968, + + /* PCIe base address */ + .pcie_qserdes_sysclk_en_sel = 0x01e0c0ac, + .pcie_pcs_osc_dtct_config_base = 0x01e0c628, + + /* Shadow register area */ + .hal_shadow_base_addr = 0x000008fc, + + /* REO misc control register, not used in QCA6390 */ + .hal_reo1_misc_ctl = 0x0, +}; + +const struct ath12k_hw_regs qcn9074_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .hal_tcl1_ring_base_lsb = 0x000004f0, + .hal_tcl1_ring_base_msb = 0x000004f4, + .hal_tcl1_ring_id = 0x000004f8, + .hal_tcl1_ring_misc = 0x00000500, + .hal_tcl1_ring_tp_addr_lsb = 0x0000050c, + .hal_tcl1_ring_tp_addr_msb = 0x00000510, + .hal_tcl1_ring_consumer_int_setup_ix0 = 0x00000520, + .hal_tcl1_ring_consumer_int_setup_ix1 = 0x00000524, + .hal_tcl1_ring_msi1_base_lsb = 0x00000538, + .hal_tcl1_ring_msi1_base_msb = 0x0000053c, + .hal_tcl1_ring_msi1_data = 0x00000540, + .hal_tcl2_ring_base_lsb = 0x00000548, + .hal_tcl_ring_base_lsb = 0x000005f8, + + /* TCL STATUS ring address */ + .hal_tcl_status_ring_base_lsb = 0x00000700, + + /* REO2SW(x) R0 ring configuration address */ + .hal_reo1_ring_base_lsb = 0x0000029c, + .hal_reo1_ring_base_msb = 0x000002a0, + .hal_reo1_ring_id = 0x000002a4, + .hal_reo1_ring_misc = 0x000002ac, + .hal_reo1_ring_hp_addr_lsb = 0x000002b0, + .hal_reo1_ring_hp_addr_msb = 0x000002b4, + .hal_reo1_ring_producer_int_setup = 0x000002c0, + .hal_reo1_ring_msi1_base_lsb = 0x000002e4, + .hal_reo1_ring_msi1_base_msb = 0x000002e8, + .hal_reo1_ring_msi1_data = 0x000002ec, + .hal_reo2_ring_base_lsb = 0x000002f4, + .hal_reo1_aging_thresh_ix_0 = 0x00000564, + .hal_reo1_aging_thresh_ix_1 = 0x00000568, + .hal_reo1_aging_thresh_ix_2 = 0x0000056c, + .hal_reo1_aging_thresh_ix_3 = 0x00000570, + + /* REO2SW(x) R2 ring pointers (head/tail) address */ + .hal_reo1_ring_hp = 0x00003038, + .hal_reo1_ring_tp = 0x0000303c, + .hal_reo2_ring_hp = 0x00003040, + + /* REO2TCL R0 ring configuration address */ + .hal_reo_tcl_ring_base_lsb = 0x000003fc, + .hal_reo_tcl_ring_hp = 0x00003058, + + /* REO CMD ring address */ + .hal_reo_cmd_ring_base_lsb = 0x00000194, + .hal_reo_cmd_ring_hp = 0x00003020, + + /* REO status address */ + .hal_reo_status_ring_base_lsb = 0x00000504, + .hal_reo_status_hp = 0x00003070, + + /* SW2REO ring address */ + .hal_sw2reo_ring_base_lsb = 0x000001ec, + .hal_sw2reo_ring_hp = 0x00003028, + + /* WCSS relative address */ + .hal_seq_wcss_umac_ce0_src_reg = 0x01b80000, + .hal_seq_wcss_umac_ce0_dst_reg = 0x01b81000, + .hal_seq_wcss_umac_ce1_src_reg = 0x01b82000, + .hal_seq_wcss_umac_ce1_dst_reg = 0x01b83000, + + /* WBM Idle address */ + .hal_wbm_idle_link_ring_base_lsb = 0x00000874, + .hal_wbm_idle_link_ring_misc = 0x00000884, + + /* SW2WBM release address */ + .hal_wbm_release_ring_base_lsb = 0x000001ec, + + /* WBM2SW release address */ + .hal_wbm0_release_ring_base_lsb = 0x00000924, + .hal_wbm1_release_ring_base_lsb = 0x0000097c, + + /* PCIe base address */ + .pcie_qserdes_sysclk_en_sel = 0x01e0e0a8, + .pcie_pcs_osc_dtct_config_base = 0x01e0f45c, + + /* Shadow register area */ + .hal_shadow_base_addr = 0x0, + + /* REO misc control register, not used in QCN9074 */ + .hal_reo1_misc_ctl = 0x0, +}; + +const struct ath12k_hw_regs wcn6855_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .hal_tcl1_ring_base_lsb = 0x00000690, + .hal_tcl1_ring_base_msb = 0x00000694, + .hal_tcl1_ring_id = 0x00000698, + .hal_tcl1_ring_misc = 0x000006a0, + .hal_tcl1_ring_tp_addr_lsb = 0x000006ac, + .hal_tcl1_ring_tp_addr_msb = 0x000006b0, + .hal_tcl1_ring_consumer_int_setup_ix0 = 0x000006c0, + .hal_tcl1_ring_consumer_int_setup_ix1 = 0x000006c4, + .hal_tcl1_ring_msi1_base_lsb = 0x000006d8, + .hal_tcl1_ring_msi1_base_msb = 0x000006dc, + .hal_tcl1_ring_msi1_data = 0x000006e0, + .hal_tcl2_ring_base_lsb = 0x000006e8, + .hal_tcl_ring_base_lsb = 0x00000798, + + /* TCL STATUS ring address */ + .hal_tcl_status_ring_base_lsb = 0x000008a0, + + /* REO2SW(x) R0 ring configuration address */ + .hal_reo1_ring_base_lsb = 0x00000244, + .hal_reo1_ring_base_msb = 0x00000248, + .hal_reo1_ring_id = 0x0000024c, + .hal_reo1_ring_misc = 0x00000254, + .hal_reo1_ring_hp_addr_lsb = 0x00000258, + .hal_reo1_ring_hp_addr_msb = 0x0000025c, + .hal_reo1_ring_producer_int_setup = 0x00000268, + .hal_reo1_ring_msi1_base_lsb = 0x0000028c, + .hal_reo1_ring_msi1_base_msb = 0x00000290, + .hal_reo1_ring_msi1_data = 0x00000294, + .hal_reo2_ring_base_lsb = 0x0000029c, + .hal_reo1_aging_thresh_ix_0 = 0x000005bc, + .hal_reo1_aging_thresh_ix_1 = 0x000005c0, + .hal_reo1_aging_thresh_ix_2 = 0x000005c4, + .hal_reo1_aging_thresh_ix_3 = 0x000005c8, + + /* REO2SW(x) R2 ring pointers (head/tail) address */ + .hal_reo1_ring_hp = 0x00003030, + .hal_reo1_ring_tp = 0x00003034, + .hal_reo2_ring_hp = 0x00003038, + + /* REO2TCL R0 ring configuration address */ + .hal_reo_tcl_ring_base_lsb = 0x00000454, + .hal_reo_tcl_ring_hp = 0x00003060, + + /* REO CMD ring address */ + .hal_reo_cmd_ring_base_lsb = 0x00000194, + .hal_reo_cmd_ring_hp = 0x00003020, + + /* REO status address */ + .hal_reo_status_ring_base_lsb = 0x0000055c, + .hal_reo_status_hp = 0x00003078, + + /* SW2REO ring address */ + .hal_sw2reo_ring_base_lsb = 0x000001ec, + .hal_sw2reo_ring_hp = 0x00003028, + + /* WCSS relative address */ + .hal_seq_wcss_umac_ce0_src_reg = 0x1b80000, + .hal_seq_wcss_umac_ce0_dst_reg = 0x1b81000, + .hal_seq_wcss_umac_ce1_src_reg = 0x1b82000, + .hal_seq_wcss_umac_ce1_dst_reg = 0x1b83000, + + /* WBM Idle address */ + .hal_wbm_idle_link_ring_base_lsb = 0x00000870, + .hal_wbm_idle_link_ring_misc = 0x00000880, + + /* SW2WBM release address */ + .hal_wbm_release_ring_base_lsb = 0x000001e8, + + /* WBM2SW release address */ + .hal_wbm0_release_ring_base_lsb = 0x00000920, + .hal_wbm1_release_ring_base_lsb = 0x00000978, + + /* PCIe base address */ + .pcie_qserdes_sysclk_en_sel = 0x01e0c0ac, + .pcie_pcs_osc_dtct_config_base = 0x01e0c628, + + /* Shadow register area */ + .hal_shadow_base_addr = 0x000008fc, + + /* REO misc control register, used for fragment + * destination ring config in WCN6855. + */ + .hal_reo1_misc_ctl = 0x00000630, +}; + +const struct ath12k_hw_regs wcn6750_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .hal_tcl1_ring_base_lsb = 0x00000694, + .hal_tcl1_ring_base_msb = 0x00000698, + .hal_tcl1_ring_id = 0x0000069c, + .hal_tcl1_ring_misc = 0x000006a4, + .hal_tcl1_ring_tp_addr_lsb = 0x000006b0, + .hal_tcl1_ring_tp_addr_msb = 0x000006b4, + .hal_tcl1_ring_consumer_int_setup_ix0 = 0x000006c4, + .hal_tcl1_ring_consumer_int_setup_ix1 = 0x000006c8, + .hal_tcl1_ring_msi1_base_lsb = 0x000006dc, + .hal_tcl1_ring_msi1_base_msb = 0x000006e0, + .hal_tcl1_ring_msi1_data = 0x000006e4, + .hal_tcl2_ring_base_lsb = 0x000006ec, + .hal_tcl_ring_base_lsb = 0x0000079c, + + /* TCL STATUS ring address */ + .hal_tcl_status_ring_base_lsb = 0x000008a4, + + /* REO2SW(x) R0 ring configuration address */ + .hal_reo1_ring_base_lsb = 0x000001ec, + .hal_reo1_ring_base_msb = 0x000001f0, + .hal_reo1_ring_id = 0x000001f4, + .hal_reo1_ring_misc = 0x000001fc, + .hal_reo1_ring_hp_addr_lsb = 0x00000200, + .hal_reo1_ring_hp_addr_msb = 0x00000204, + .hal_reo1_ring_producer_int_setup = 0x00000210, + .hal_reo1_ring_msi1_base_lsb = 0x00000234, + .hal_reo1_ring_msi1_base_msb = 0x00000238, + .hal_reo1_ring_msi1_data = 0x0000023c, + .hal_reo2_ring_base_lsb = 0x00000244, + .hal_reo1_aging_thresh_ix_0 = 0x00000564, + .hal_reo1_aging_thresh_ix_1 = 0x00000568, + .hal_reo1_aging_thresh_ix_2 = 0x0000056c, + .hal_reo1_aging_thresh_ix_3 = 0x00000570, + + /* REO2SW(x) R2 ring pointers (head/tail) address */ + .hal_reo1_ring_hp = 0x00003028, + .hal_reo1_ring_tp = 0x0000302c, + .hal_reo2_ring_hp = 0x00003030, + + /* REO2TCL R0 ring configuration address */ + .hal_reo_tcl_ring_base_lsb = 0x000003fc, + .hal_reo_tcl_ring_hp = 0x00003058, + + /* REO CMD ring address */ + .hal_reo_cmd_ring_base_lsb = 0x000000e4, + .hal_reo_cmd_ring_hp = 0x00003010, + + /* REO status address */ + .hal_reo_status_ring_base_lsb = 0x00000504, + .hal_reo_status_hp = 0x00003070, + + /* SW2REO ring address */ + .hal_sw2reo_ring_base_lsb = 0x0000013c, + .hal_sw2reo_ring_hp = 0x00003018, + + /* WCSS relative address */ + .hal_seq_wcss_umac_ce0_src_reg = 0x01b80000, + .hal_seq_wcss_umac_ce0_dst_reg = 0x01b81000, + .hal_seq_wcss_umac_ce1_src_reg = 0x01b82000, + .hal_seq_wcss_umac_ce1_dst_reg = 0x01b83000, + + /* WBM Idle address */ + .hal_wbm_idle_link_ring_base_lsb = 0x00000874, + .hal_wbm_idle_link_ring_misc = 0x00000884, + + /* SW2WBM release address */ + .hal_wbm_release_ring_base_lsb = 0x000001ec, + + /* WBM2SW release address */ + .hal_wbm0_release_ring_base_lsb = 0x00000924, + .hal_wbm1_release_ring_base_lsb = 0x0000097c, + + /* PCIe base address */ + .pcie_qserdes_sysclk_en_sel = 0x0, + .pcie_pcs_osc_dtct_config_base = 0x0, + + /* Shadow register area */ + .hal_shadow_base_addr = 0x00000504, + + /* REO misc control register, used for fragment + * destination ring config in WCN6750. + */ + .hal_reo1_misc_ctl = 0x000005d8, +}; + +#define QWZ_SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 +#define QWZ_HOST_CSTATE_BIT 0x04 +#define QWZ_PLATFORM_CAP_PCIE_GLOBAL_RESET 0x08 +#define QWZ_PLATFORM_CAP_PCIE_PME_D3COLD 0x10 + +static const struct qmi_elem_info qmi_response_type_v01_ei[] = { + { + .data_type = QMI_SIGNED_2_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof(struct qmi_response_type_v01, result), + .ei_array = NULL, + }, + { + .data_type = QMI_SIGNED_2_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof(struct qmi_response_type_v01, error), + .ei_array = NULL, + }, + { + .data_type = QMI_EOTI, + .elem_len = 0, + .elem_size = 0, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = 0, + .ei_array = NULL, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + initiate_cal_download_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + initiate_cal_download_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + initiate_cal_update_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + initiate_cal_update_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + msa_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + msa_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + pin_connect_result_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + pin_connect_result_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + client_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + client_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + request_mem_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + request_mem_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_mem_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_mem_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_init_done_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_init_done_enable), + }, + + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + rejuvenate_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + rejuvenate_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + xo_cal_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + xo_cal_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + cal_done_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + cal_done_enable), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_ind_register_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_ind_register_resp_msg_v01, + fw_status_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_ind_register_resp_msg_v01, + fw_status), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + num_clients_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + num_clients), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + wake_msi_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + wake_msi), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + gpios_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + gpios_len), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = QMI_WLFW_MAX_NUM_GPIO_V01, + .elem_size = sizeof(uint32_t), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + gpios), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + nm_modem_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + nm_modem), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + bdf_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + bdf_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + bdf_cache_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + bdf_cache_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + m3_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + m3_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + m3_cache_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + m3_cache_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_filesys_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_filesys_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_cache_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_cache_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_done_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_done), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + mem_bucket_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + mem_bucket), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x1C, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + mem_cfg_mode_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x1C, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + mem_cfg_mode), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_host_cap_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_cfg_s_v01, offset), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_cfg_s_v01, size), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_cfg_s_v01, secure_flag), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_s_v01, + size), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_mem_type_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_s_v01, type), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_s_v01, mem_cfg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_MEM_CFG_V01, + .elem_size = sizeof(struct qmi_wlanfw_mem_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_s_v01, mem_cfg), + .ei_array = qmi_wlanfw_mem_cfg_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_request_mem_ind_msg_v01, + mem_seg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = ATH12K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01, + .elem_size = sizeof(struct qmi_wlanfw_mem_seg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_request_mem_ind_msg_v01, + mem_seg), + .ei_array = qmi_wlanfw_mem_seg_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_resp_s_v01, addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_resp_s_v01, size), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_mem_type_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_resp_s_v01, type), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_resp_s_v01, restore), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_respond_mem_req_msg_v01, + mem_seg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = ATH12K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01, + .elem_size = sizeof(struct qmi_wlanfw_mem_seg_resp_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_respond_mem_req_msg_v01, + mem_seg), + .ei_array = qmi_wlanfw_mem_seg_resp_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_respond_mem_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_rf_chip_info_s_v01, + chip_id), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_rf_chip_info_s_v01, + chip_family), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_rf_board_info_s_v01, + board_id), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_soc_info_s_v01, soc_id), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_fw_version_info_s_v01, + fw_version), + }, + { + .data_type = QMI_STRING, + .elem_len = ATH12K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_fw_version_info_s_v01, + fw_build_timestamp), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + chip_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_wlanfw_rf_chip_info_s_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + chip_info), + .ei_array = qmi_wlanfw_rf_chip_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + board_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_wlanfw_rf_board_info_s_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + board_info), + .ei_array = qmi_wlanfw_rf_board_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + soc_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_wlanfw_soc_info_s_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + soc_info), + .ei_array = qmi_wlanfw_soc_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + fw_version_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_wlanfw_fw_version_info_s_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + fw_version_info), + .ei_array = qmi_wlanfw_fw_version_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + fw_build_id_valid), + }, + { + .data_type = QMI_STRING, + .elem_len = ATH12K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + fw_build_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + num_macs_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + num_macs), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + voltage_mv_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + voltage_mv), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + time_freq_hz_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + time_freq_hz), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + otp_version_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + otp_version), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + eeprom_read_timeout_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + eeprom_read_timeout), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + valid), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + file_id_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_cal_temp_id_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + file_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + total_size_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + total_size), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + seg_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + seg_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + data_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLANFW_MAX_DATA_SIZE_V01, + .elem_size = sizeof(uint8_t), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + data), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + end_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + end), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + bdf_type_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + bdf_type), + }, + + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_bdf_download_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_m3_info_req_msg_v01, addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_m3_info_req_msg_v01, size), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_m3_info_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_ini_req_msg_v01, + enablefwlog_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_ini_req_msg_v01, + enablefwlog), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_wlan_ini_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + pipe_num), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_pipedir_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + pipe_dir), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + nentries), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + nbytes_max), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + flags), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01, + service_id), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_pipedir_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01, + pipe_dir), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01, + pipe_num), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_shadow_reg_cfg_s_v01, id), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_shadow_reg_cfg_s_v01, + offset), + }, + { + .data_type = QMI_EOTI, + .array_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01, + addr), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_wlan_mode_req_msg_v01, + mode), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_mode_req_msg_v01, + hw_debug_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_mode_req_msg_v01, + hw_debug), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_wlan_mode_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + host_version_valid), + }, + { + .data_type = QMI_STRING, + .elem_len = QMI_WLANFW_MAX_STR_LEN_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + host_version), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + tgt_cfg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + tgt_cfg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_CE_V01, + .elem_size = sizeof( + struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + tgt_cfg), + .ei_array = qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + svc_cfg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + svc_cfg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_SVC_V01, + .elem_size = sizeof(struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + svc_cfg), + .ei_array = qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_SHADOW_REG_V01, + .elem_size = sizeof(struct qmi_wlanfw_shadow_reg_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg), + .ei_array = qmi_wlanfw_shadow_reg_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_v2_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_v2_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_SHADOW_REG_V2_V01, + .elem_size = sizeof(struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_v2), + .ei_array = qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +int +qwz_ce_intr(void *arg) +{ + struct qwz_ce_pipe *pipe = arg; + struct qwz_softc *sc = pipe->sc; + + if (!test_bit(ATH12K_FLAG_CE_IRQ_ENABLED, sc->sc_flags) || + ((sc->msi_ce_irqmask & (1 << pipe->pipe_num)) == 0)) { + DPRINTF("%s: unexpected interrupt on pipe %d\n", + __func__, pipe->pipe_num); + return 1; + } + + return qwz_ce_per_engine_service(sc, pipe->pipe_num); +} + +int +qwz_ext_intr(void *arg) +{ + struct qwz_ext_irq_grp *irq_grp = arg; + struct qwz_softc *sc = irq_grp->sc; + + if (!test_bit(ATH12K_FLAG_EXT_IRQ_ENABLED, sc->sc_flags)) { + DPRINTF("%s: unexpected interrupt for ext group %d\n", + __func__, irq_grp->grp_id); + return 1; + } + + return qwz_dp_service_srng(sc, irq_grp->grp_id); +} + +static const char *qmi_data_type_name[QMI_NUM_DATA_TYPES] = { + "EOTI", + "OPT_FLAG", + "DATA_LEN", + "UNSIGNED_1_BYTE", + "UNSIGNED_2_BYTE", + "UNSIGNED_4_BYTE", + "UNSIGNED_8_BYTE", + "SIGNED_2_BYTE_ENUM", + "SIGNED_4_BYTE_ENUM", + "STRUCT", + "STRING" +}; + +const struct qmi_elem_info * +qwz_qmi_decode_get_elem(const struct qmi_elem_info *ei, uint8_t elem_type) +{ + while (ei->data_type != QMI_EOTI && ei->tlv_type != elem_type) + ei++; + + DNPRINTF(QWZ_D_QMI, "%s: found elem 0x%x data type 0x%x\n", __func__, + ei->tlv_type, ei->data_type); + return ei; +} + +size_t +qwz_qmi_decode_min_elem_size(const struct qmi_elem_info *ei, int nested) +{ + size_t min_size = 0; + + switch (ei->data_type) { + case QMI_EOTI: + case QMI_OPT_FLAG: + break; + case QMI_DATA_LEN: + if (ei->elem_len == 1) + min_size += sizeof(uint8_t); + else + min_size += sizeof(uint16_t); + break; + case QMI_UNSIGNED_1_BYTE: + case QMI_UNSIGNED_2_BYTE: + case QMI_UNSIGNED_4_BYTE: + case QMI_UNSIGNED_8_BYTE: + case QMI_SIGNED_2_BYTE_ENUM: + case QMI_SIGNED_4_BYTE_ENUM: + min_size += ei->elem_len * ei->elem_size; + break; + case QMI_STRUCT: + if (nested > 2) { + printf("%s: QMI struct element 0x%x with " + "data type %s (0x%x) is nested too " + "deeply\n", __func__, + ei->tlv_type, + qmi_data_type_name[ei->data_type], + ei->data_type); + } + ei = ei->ei_array; + while (ei->data_type != QMI_EOTI) { + min_size += qwz_qmi_decode_min_elem_size(ei, + nested + 1); + ei++; + } + break; + case QMI_STRING: + min_size += 1; + /* Strings nested in structs use an in-band length field. */ + if (nested) { + if (ei->elem_len <= 0xff) + min_size += sizeof(uint8_t); + else + min_size += sizeof(uint16_t); + } + break; + default: + printf("%s: unhandled data type 0x%x\n", __func__, + ei->data_type); + break; + } + + return min_size; +} + +int +qwz_qmi_decode_tlv_hdr(struct qwz_softc *sc, + const struct qmi_elem_info **next_ei, uint16_t *actual_size, + size_t output_len, const struct qmi_elem_info *ei0, + uint8_t *input, size_t input_len) +{ + uint8_t *p = input; + size_t remain = input_len; + uint8_t elem_type; + uint16_t elem_size = 0; + const struct qmi_elem_info *ei; + + *next_ei = NULL; + *actual_size = 0; + + if (remain < 3) { + printf("%s: QMI message TLV header too short\n", + sc->sc_dev.dv_xname); + return -1; + } + elem_type = *p; + p++; + remain--; + + /* + * By relying on TLV type information we can skip over EIs which + * describe optional elements that have not been encoded. + * Such elements will be left at their default value (zero) in + * the decoded output struct. + * XXX We currently allow elements to appear in any order and + * we do not detect duplicates. + */ + ei = qwz_qmi_decode_get_elem(ei0, elem_type); + + DNPRINTF(QWZ_D_QMI, + "%s: decoding element 0x%x with data type %s (0x%x)\n", + __func__, elem_type, qmi_data_type_name[ei->data_type], + ei->data_type); + + if (remain < 2) { + printf("%s: QMI message too short\n", sc->sc_dev.dv_xname); + return -1; + } + + if (ei->data_type == QMI_DATA_LEN && ei->elem_len == 1) { + elem_size = p[0]; + p++; + remain--; + } else { + elem_size = (p[0] | (p[1] << 8)); + p += 2; + remain -= 2; + } + + *next_ei = ei; + *actual_size = elem_size; + + if (ei->data_type == QMI_EOTI) { + DNPRINTF(QWZ_D_QMI, + "%s: unrecognized QMI element type 0x%x size %u\n", + sc->sc_dev.dv_xname, elem_type, elem_size); + return 0; + } + + /* + * Is this an optional element which has been encoded? + * If so, use info about this optional element for verification. + */ + if (ei->data_type == QMI_OPT_FLAG) + ei++; + + DNPRINTF(QWZ_D_QMI, "%s: ei->size %u, actual size %u\n", __func__, + ei->elem_size, *actual_size); + + switch (ei->data_type) { + case QMI_UNSIGNED_1_BYTE: + case QMI_UNSIGNED_2_BYTE: + case QMI_UNSIGNED_4_BYTE: + case QMI_UNSIGNED_8_BYTE: + case QMI_SIGNED_2_BYTE_ENUM: + case QMI_SIGNED_4_BYTE_ENUM: + if (elem_size != ei->elem_size) { + printf("%s: QMI message element 0x%x " + "data type %s (0x%x) with bad size: %u\n", + sc->sc_dev.dv_xname, elem_type, + qmi_data_type_name[ei->data_type], + ei->data_type, elem_size); + return -1; + } + break; + case QMI_DATA_LEN: + break; + case QMI_STRING: + case QMI_STRUCT: + if (elem_size < qwz_qmi_decode_min_elem_size(ei, 0)) { + printf("%s: QMI message element 0x%x " + "data type %s (0x%x) with bad size: %u\n", + sc->sc_dev.dv_xname, elem_type, + qmi_data_type_name[ei->data_type], + ei->data_type, elem_size); + return -1; + } + break; + default: + printf("%s: unexpected QMI message element " + "data type 0x%x\n", sc->sc_dev.dv_xname, + ei->data_type); + return -1; + } + + if (remain < elem_size) { + printf("%s: QMI message too short\n", sc->sc_dev.dv_xname); + return -1; + } + + if (ei->offset + ei->elem_size > output_len) { + printf("%s: QMI message element type 0x%x too large: %u\n", + sc->sc_dev.dv_xname, elem_type, ei->elem_size); + return -1; + } + + return 0; +} + +int +qwz_qmi_decode_byte(void *output, const struct qmi_elem_info *ei, void *input) +{ + if (ei->elem_size != sizeof(uint8_t)) { + printf("%s: bad element size\n", __func__); + return -1; + } + + DNPRINTF(QWZ_D_QMI, "%s: element 0x%x data type 0x%x size %u\n", + __func__, ei->tlv_type, ei->data_type, ei->elem_size); + memcpy(output, input, ei->elem_size); + return 0; +} + +int +qwz_qmi_decode_word(void *output, const struct qmi_elem_info *ei, void *input) +{ + if (ei->elem_size != sizeof(uint16_t)) { + printf("%s: bad element size\n", __func__); + return -1; + } + + DNPRINTF(QWZ_D_QMI, "%s: element 0x%x data type 0x%x size %u\n", + __func__, ei->tlv_type, ei->data_type, ei->elem_size); + memcpy(output, input, ei->elem_size); + return 0; +} + +int +qwz_qmi_decode_dword(void *output, const struct qmi_elem_info *ei, void *input) +{ + if (ei->elem_size != sizeof(uint32_t)) { + printf("%s: bad element size\n", __func__); + return -1; + } + + DNPRINTF(QWZ_D_QMI, "%s: element 0x%x data type 0x%x size %u\n", + __func__, ei->tlv_type, ei->data_type, ei->elem_size); + memcpy(output, input, ei->elem_size); + return 0; +} + +int +qwz_qmi_decode_qword(void *output, const struct qmi_elem_info *ei, void *input) +{ + if (ei->elem_size != sizeof(uint64_t)) { + printf("%s: bad element size\n", __func__); + return -1; + } + + DNPRINTF(QWZ_D_QMI, "%s: element 0x%x data type 0x%x size %u\n", + __func__, ei->tlv_type, ei->data_type, ei->elem_size); + memcpy(output, input, ei->elem_size); + return 0; +} + +int +qwz_qmi_decode_datalen(struct qwz_softc *sc, size_t *used, uint32_t *datalen, + void *output, size_t output_len, const struct qmi_elem_info *ei, + uint8_t *input, uint16_t input_len) +{ + uint8_t *p = input; + size_t remain = input_len; + + *datalen = 0; + + DNPRINTF(QWZ_D_QMI, "%s: input: ", __func__); + for (int i = 0; i < input_len; i++) { + DNPRINTF(QWZ_D_QMI, " %02x", input[i]); + } + DNPRINTF(QWZ_D_QMI, "\n"); + + if (remain < ei->elem_size) { + printf("%s: QMI message too short: remain=%zu elem_size=%u\n", __func__, remain, ei->elem_size); + return -1; + } + + switch (ei->elem_size) { + case sizeof(uint8_t): + *datalen = p[0]; + break; + case sizeof(uint16_t): + *datalen = p[0] | (p[1] << 8); + break; + default: + printf("%s: bad datalen element size %u\n", + sc->sc_dev.dv_xname, ei->elem_size); + return -1; + + } + *used = ei->elem_size; + + if (ei->offset + sizeof(*datalen) > output_len) { + printf("%s: QMI message element type 0x%x too large\n", + sc->sc_dev.dv_xname, ei->tlv_type); + return -1; + } + memcpy(output + ei->offset, datalen, sizeof(*datalen)); + return 0; +} + +int +qwz_qmi_decode_string(struct qwz_softc *sc, size_t *used_total, + void *output, size_t output_len, const struct qmi_elem_info *ei, + uint8_t *input, uint16_t input_len, uint16_t elem_size, int nested) +{ + uint8_t *p = input; + uint16_t len; + size_t remain = input_len; + + *used_total = 0; + + DNPRINTF(QWZ_D_QMI, "%s: input: ", __func__); + for (int i = 0; i < input_len; i++) { + DNPRINTF(QWZ_D_QMI, " %02x", input[i]); + } + DNPRINTF(QWZ_D_QMI, "\n"); + + if (nested) { + /* Strings nested in structs use an in-band length field. */ + if (ei->elem_len <= 0xff) { + if (remain == 0) { + printf("%s: QMI string length header exceeds " + "input buffer size\n", __func__); + return -1; + } + len = p[0]; + p++; + (*used_total)++; + remain--; + } else { + if (remain < 2) { + printf("%s: QMI string length header exceeds " + "input buffer size\n", __func__); + return -1; + } + len = p[0] | (p[1] << 8); + p += 2; + *used_total += 2; + remain -= 2; + } + } else + len = elem_size; + + if (len > ei->elem_len) { + printf("%s: QMI string element of length %u exceeds " + "maximum length %u\n", __func__, len, ei->elem_len); + return -1; + } + if (len > remain) { + printf("%s: QMI string element of length %u exceeds " + "input buffer size %zu\n", __func__, len, remain); + return -1; + } + if (len > output_len) { + printf("%s: QMI string element of length %u exceeds " + "output buffer size %zu\n", __func__, len, output_len); + return -1; + } + + memcpy(output, p, len); + + p = output; + p[len] = '\0'; + DNPRINTF(QWZ_D_QMI, "%s: string (len %u): %s\n", __func__, len, p); + + *used_total += len; + return 0; +} + +int +qwz_qmi_decode_struct(struct qwz_softc *sc, size_t *used_total, + void *output, size_t output_len, + const struct qmi_elem_info *struct_ei, + uint8_t *input, uint16_t input_len, + int nested) +{ + const struct qmi_elem_info *ei = struct_ei->ei_array; + uint32_t min_size; + uint8_t *p = input; + size_t remain = input_len; + size_t used = 0; + + *used_total = 0; + + DNPRINTF(QWZ_D_QMI, "%s: input: ", __func__); + for (int i = 0; i < input_len; i++) { + DNPRINTF(QWZ_D_QMI, " %02x", input[i]); + } + DNPRINTF(QWZ_D_QMI, "\n"); + + min_size = qwz_qmi_decode_min_elem_size(struct_ei, 0); + DNPRINTF(QWZ_D_QMI, "%s: minimum struct size: %u\n", __func__, min_size); + while (*used_total < min_size && ei->data_type != QMI_EOTI) { + if (remain == 0) { + printf("%s: QMI message too short\n", __func__); + return -1; + } + + if (ei->data_type == QMI_DATA_LEN) { + uint32_t datalen; + + used = 0; + if (qwz_qmi_decode_datalen(sc, &used, &datalen, + output, output_len, ei, p, remain)) + return -1; + DNPRINTF(QWZ_D_QMI, "%s: datalen %u used %zu bytes\n", + __func__, datalen, used); + p += used; + remain -= used; + *used_total += used; + if (remain < datalen) { + printf("%s: QMI message too short\n", __func__); + return -1; + } + ei++; + DNPRINTF(QWZ_D_QMI, "%s: datalen is for data_type=0x%x " + "tlv_type=0x%x elem_size=%u(0x%x) remain=%zu\n", + __func__, ei->data_type, ei->tlv_type, + ei->elem_size, ei->elem_size, remain); + if (datalen == 0) { + ei++; + DNPRINTF(QWZ_D_QMI, + "%s: skipped to data_type=0x%x " + "tlv_type=0x%x elem_size=%u(0x%x) " + "remain=%zu\n", __func__, + ei->data_type, ei->tlv_type, + ei->elem_size, ei->elem_size, remain); + continue; + } + } else { + if (remain < ei->elem_size) { + printf("%s: QMI message too short\n", + __func__); + return -1; + } + } + + if (ei->offset + ei->elem_size > output_len) { + printf("%s: QMI message struct member element " + "type 0x%x too large: %u\n", sc->sc_dev.dv_xname, + ei->tlv_type, ei->elem_size); + return -1; + } + + DNPRINTF(QWZ_D_QMI, + "%s: decoding struct member element 0x%x with " + "data type %s (0x%x) size=%u(0x%x) remain=%zu\n", __func__, + ei->tlv_type, qmi_data_type_name[ei->data_type], + ei->data_type, ei->elem_size, ei->elem_size, remain); + switch (ei->data_type) { + case QMI_UNSIGNED_1_BYTE: + if (qwz_qmi_decode_byte(output + ei->offset, ei, p)) + return -1; + remain -= ei->elem_size; + p += ei->elem_size; + *used_total += ei->elem_size; + break; + case QMI_UNSIGNED_2_BYTE: + case QMI_SIGNED_2_BYTE_ENUM: + if (qwz_qmi_decode_word(output + ei->offset, ei, p)) + return -1; + remain -= ei->elem_size; + p += ei->elem_size; + *used_total += ei->elem_size; + break; + case QMI_UNSIGNED_4_BYTE: + case QMI_SIGNED_4_BYTE_ENUM: + if (qwz_qmi_decode_dword(output + ei->offset, ei, p)) + return -1; + remain -= ei->elem_size; + p += ei->elem_size; + *used_total += ei->elem_size; + break; + case QMI_UNSIGNED_8_BYTE: + if (qwz_qmi_decode_qword(output + ei->offset, ei, p)) + return -1; + remain -= ei->elem_size; + p += ei->elem_size; + *used_total += ei->elem_size; + break; + case QMI_STRUCT: + if (nested > 2) { + printf("%s: QMI struct element data type 0x%x " + "is nested too deeply\n", + sc->sc_dev.dv_xname, ei->data_type); + return -1; + } + used = 0; + if (qwz_qmi_decode_struct(sc, &used, + output + ei->offset, output_len - ei->offset, + ei, p, remain, nested + 1)) + return -1; + remain -= used; + p += used; + *used_total += used; + break; + case QMI_STRING: + used = 0; + if (qwz_qmi_decode_string(sc, &used, + output + ei->offset, output_len - ei->offset, + ei, p, remain, 0, 1)) + return -1; + remain -= used; + p += used; + *used_total += used; + break; + default: + printf("%s: unhandled QMI struct element " + "data type 0x%x\n", sc->sc_dev.dv_xname, + ei->data_type); + return -1; + } + + ei++; + DNPRINTF(QWZ_D_QMI, "%s: next ei 0x%x ei->data_type=0x%x\n", + __func__, ei->tlv_type, ei->data_type); + } + + DNPRINTF(QWZ_D_QMI, "%s: used_total=%zu ei->data_type=0x%x\n", + __func__, *used_total, ei->data_type); + + return 0; +} + +int +qwz_qmi_decode_msg(struct qwz_softc *sc, void *output, size_t output_len, + const struct qmi_elem_info *ei0, uint8_t *input, uint16_t input_len) +{ + uint8_t *p = input; + size_t remain = input_len, used; + const struct qmi_elem_info *ei = ei0; + + memset(output, 0, output_len); + + DNPRINTF(QWZ_D_QMI, "%s: input: ", __func__); + for (int i = 0; i < input_len; i++) { + DNPRINTF(QWZ_D_QMI, " %02x", input[i]); + } + DNPRINTF(QWZ_D_QMI, "\n"); + + while (remain > 0 && ei->data_type != QMI_EOTI) { + uint32_t nelem = 1, i; + uint16_t datalen; + + if (qwz_qmi_decode_tlv_hdr(sc, &ei, &datalen, output_len, + ei0, p, remain)) + return -1; + + /* Skip unrecognized elements. */ + if (ei->data_type == QMI_EOTI) { + p += 3 + datalen; + remain -= 3 + datalen; + ei = ei0; + continue; + } + + /* Set 'valid' flag for optional fields in output struct. */ + if (ei->data_type == QMI_OPT_FLAG) { + uint8_t *pvalid; + + if (ei->offset + ei->elem_size > output_len) { + printf("%s: QMI message element type 0x%x " + "too large: %u\n", sc->sc_dev.dv_xname, + ei->tlv_type, ei->elem_size); + } + + pvalid = (uint8_t *)output + ei->offset; + *pvalid = 1; + + ei++; + } + + p += 3; + remain -= 3; + + if (ei->data_type == QMI_DATA_LEN) { + const struct qmi_elem_info *datalen_ei = ei; + uint8_t elem_type = ei->tlv_type; + + /* + * Size info in TLV header indicates the + * total length of element data that follows. + */ + if (remain < datalen) { + printf("%s:%d QMI message too short\n", + __func__, __LINE__); + return -1; + } + + ei++; + DNPRINTF(QWZ_D_QMI, + "%s: next ei data_type=0x%x tlv_type=0x%x " + "dst elem_size=%u(0x%x) src total size=%u " + "remain=%zu\n", __func__, ei->data_type, + ei->tlv_type, ei->elem_size, ei->elem_size, + datalen, remain); + + /* Related EIs must have the same type. */ + if (ei->tlv_type != elem_type) { + printf("%s: unexepected element type 0x%x; " + "expected 0x%x\n", __func__, + ei->tlv_type, elem_type); + return -1; + } + + if (datalen == 0) { + if (ei->data_type != QMI_EOTI) + ei++; + continue; + } + + /* + * For variable length arrays a one- or two-byte + * value follows the header, indicating the number + * of elements in the array. + */ + if (ei->array_type == VAR_LEN_ARRAY) { + DNPRINTF(QWZ_D_QMI, + "%s: variable length array\n", __func__); + used = 0; + if (qwz_qmi_decode_datalen(sc, &used, &nelem, + output, output_len, datalen_ei, p, remain)) + return -1; + p += used; + remain -= used; + /* + * Previous datalen value included the total + * amount of bytes following the DATALEN TLV + * header. + */ + datalen -= used; + + if (nelem == 0) { + if (ei->data_type != QMI_EOTI) + ei++; + continue; + } + + DNPRINTF(QWZ_D_QMI, + "%s: datalen %u used %zu bytes\n", + __func__, nelem, used); + + DNPRINTF(QWZ_D_QMI, + "%s: decoding %u array elements with " + "src size %u dest size %u\n", __func__, + nelem, datalen / nelem, ei->elem_size); + } + } + + if (remain < datalen) { + printf("%s:%d QMI message too short: remain=%zu, " + "datalen=%u\n", __func__, __LINE__, remain, + datalen); + return -1; + } + if (output_len < nelem * ei->elem_size) { + printf("%s: QMI output buffer too short: remain=%zu " + "nelem=%u ei->elem_size=%u\n", __func__, remain, + nelem, ei->elem_size); + return -1; + } + + for (i = 0; i < nelem && remain > 0; i++) { + size_t outoff; + + outoff = ei->offset + (ei->elem_size * i); + switch (ei->data_type) { + case QMI_STRUCT: + used = 0; + if (qwz_qmi_decode_struct(sc, &used, + output + outoff, output_len - outoff, + ei, p, remain, 0)) + return -1; + remain -= used; + p += used; + if (used != datalen) { + DNPRINTF(QWZ_D_QMI, + "%s struct used only %zu bytes " + "of %u input bytes\n", __func__, + used, datalen); + } else { + DNPRINTF(QWZ_D_QMI, + "%s: struct used %zu bytes " + "of input\n", __func__, used); + } + break; + case QMI_STRING: + used = 0; + if (qwz_qmi_decode_string(sc, &used, + output + outoff, output_len - outoff, + ei, p, remain, datalen, 0)) + return -1; + remain -= used; + p += used; + if (used != datalen) { + DNPRINTF(QWZ_D_QMI, + "%s: string used only %zu bytes " + "of %u input bytes\n", __func__, + used, datalen); + } else { + DNPRINTF(QWZ_D_QMI, + "%s: string used %zu bytes " + "of input\n", __func__, used); + } + break; + case QMI_UNSIGNED_1_BYTE: + if (remain < ei->elem_size) { + printf("%s: QMI message too " + "short\n", __func__); + return -1; + } + if (qwz_qmi_decode_byte(output + outoff, + ei, p)) + return -1; + remain -= ei->elem_size; + p += ei->elem_size; + break; + case QMI_UNSIGNED_2_BYTE: + case QMI_SIGNED_2_BYTE_ENUM: + if (remain < ei->elem_size) { + printf("%s: QMI message too " + "short\n", __func__); + return -1; + } + if (qwz_qmi_decode_word(output + outoff, + ei, p)) + return -1; + remain -= ei->elem_size; + p += ei->elem_size; + break; + case QMI_UNSIGNED_4_BYTE: + case QMI_SIGNED_4_BYTE_ENUM: + if (remain < ei->elem_size) { + printf("%s: QMI message too " + "short\n", __func__); + return -1; + } + if (qwz_qmi_decode_dword(output + outoff, + ei, p)) + return -1; + remain -= ei->elem_size; + p += ei->elem_size; + break; + case QMI_UNSIGNED_8_BYTE: + if (remain < ei->elem_size) { + printf("%s: QMI message too " + "short 4\n", __func__); + return -1; + } + if (qwz_qmi_decode_qword(output + outoff, + ei, p)) + return -1; + remain -= ei->elem_size; + p += ei->elem_size; + break; + default: + printf("%s: unhandled QMI message element " + "data type 0x%x\n", + sc->sc_dev.dv_xname, ei->data_type); + return -1; + } + } + + ei++; + DNPRINTF(QWZ_D_QMI, + "%s: next ei 0x%x ei->data_type=0x%x remain=%zu\n", + __func__, ei->tlv_type, ei->data_type, remain); + + DNPRINTF(QWZ_D_QMI, "%s: remaining input: ", __func__); + for (int i = 0; i < remain; i++) + DNPRINTF(QWZ_D_QMI, " %02x", p[i]); + DNPRINTF(QWZ_D_QMI, "\n"); + } + + return 0; +} + +void +qwz_qmi_recv_wlanfw_ind_register_req_v1(struct qwz_softc *sc, struct mbuf *m, + uint16_t txn_id, uint16_t msg_len) +{ + struct qmi_wlanfw_ind_register_resp_msg_v01 resp; + const struct qmi_elem_info *ei; + uint8_t *msg = mtod(m, uint8_t *); + + DNPRINTF(QWZ_D_QMI, "%s\n", __func__); + + ei = qmi_wlanfw_ind_register_resp_msg_v01_ei; + if (qwz_qmi_decode_msg(sc, &resp, sizeof(resp), ei, msg, msg_len)) + return; + + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.result=0x%x\n", + __func__, le16toh(resp.resp.result)); + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.error=0x%x\n", + __func__, le16toh(resp.resp.error)); + DNPRINTF(QWZ_D_QMI, "%s: resp.fw_status=0x%llx\n", + __func__, le64toh(resp.fw_status)); + + sc->qmi_resp.result = le16toh(resp.resp.result); + sc->qmi_resp.error = le16toh(resp.resp.error); + wakeup(&sc->qmi_resp); +} + +void +qwz_qmi_recv_wlanfw_host_cap_resp_v1(struct qwz_softc *sc, struct mbuf *m, + uint16_t txn_id, uint16_t msg_len) +{ + struct qmi_wlanfw_host_cap_resp_msg_v01 resp; + const struct qmi_elem_info *ei; + uint8_t *msg = mtod(m, uint8_t *); + + DNPRINTF(QWZ_D_QMI, "%s\n", __func__); + + ei = qmi_wlanfw_host_cap_resp_msg_v01_ei; + if (qwz_qmi_decode_msg(sc, &resp, sizeof(resp), ei, msg, msg_len)) + return; + + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.result=0x%x\n", + __func__, le16toh(resp.resp.result)); + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.error=0x%x\n", + __func__, le16toh(resp.resp.error)); + + sc->qmi_resp.result = le16toh(resp.resp.result); + sc->qmi_resp.error = le16toh(resp.resp.error); + wakeup(&sc->qmi_resp); +} + +void +qwz_qmi_recv_wlanfw_respond_mem_resp_v1(struct qwz_softc *sc, struct mbuf *m, + uint16_t txn_id, uint16_t msg_len) +{ + struct qmi_wlanfw_respond_mem_resp_msg_v01 resp; + const struct qmi_elem_info *ei; + uint8_t *msg = mtod(m, uint8_t *); + + DNPRINTF(QWZ_D_QMI, "%s\n", __func__); + + ei = qmi_wlanfw_respond_mem_resp_msg_v01_ei; + if (qwz_qmi_decode_msg(sc, &resp, sizeof(resp), ei, msg, msg_len)) + return; + + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.result=0x%x\n", + __func__, le16toh(resp.resp.result)); + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.error=0x%x\n", + __func__, le16toh(resp.resp.error)); + + sc->qmi_resp.result = le16toh(resp.resp.result); + sc->qmi_resp.error = le16toh(resp.resp.error); + wakeup(&sc->qmi_resp); +} + +void +qwz_qmi_recv_wlanfw_cap_resp_v1(struct qwz_softc *sc, struct mbuf *m, + uint16_t txn_id, uint16_t msg_len) +{ + struct qmi_wlanfw_cap_resp_msg_v01 resp; + const struct qmi_elem_info *ei; + uint8_t *msg = mtod(m, uint8_t *); + + DNPRINTF(QWZ_D_QMI, "%s\n", __func__); + + memset(&resp, 0, sizeof(resp)); + + ei = qmi_wlanfw_cap_resp_msg_v01_ei; + if (qwz_qmi_decode_msg(sc, &resp, sizeof(resp), ei, msg, msg_len)) + return; + + if (resp.chip_info_valid) { + sc->qmi_target.chip_id = resp.chip_info.chip_id; + sc->qmi_target.chip_family = resp.chip_info.chip_family; + } + + if (resp.board_info_valid) + sc->qmi_target.board_id = resp.board_info.board_id; + else + sc->qmi_target.board_id = 0xFF; + + if (resp.soc_info_valid) + sc->qmi_target.soc_id = resp.soc_info.soc_id; + + if (resp.fw_version_info_valid) { + sc->qmi_target.fw_version = resp.fw_version_info.fw_version; + strlcpy(sc->qmi_target.fw_build_timestamp, + resp.fw_version_info.fw_build_timestamp, + sizeof(sc->qmi_target.fw_build_timestamp)); + } + + if (resp.fw_build_id_valid) + strlcpy(sc->qmi_target.fw_build_id, resp.fw_build_id, + sizeof(sc->qmi_target.fw_build_id)); + + if (resp.eeprom_read_timeout_valid) { + sc->qmi_target.eeprom_caldata = resp.eeprom_read_timeout; + DNPRINTF(QWZ_D_QMI, + "%s: qmi cal data supported from eeprom\n", __func__); + } + + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.result=0x%x\n", + __func__, le16toh(resp.resp.result)); + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.error=0x%x\n", + __func__, le16toh(resp.resp.error)); + + sc->qmi_resp.result = le16toh(resp.resp.result); + sc->qmi_resp.error = le16toh(resp.resp.error); + wakeup(&sc->qmi_resp); +} + +void +qwz_qmi_recv_wlanfw_bdf_download_resp_v1(struct qwz_softc *sc, struct mbuf *m, + uint16_t txn_id, uint16_t msg_len) +{ + struct qmi_wlanfw_bdf_download_resp_msg_v01 resp; + const struct qmi_elem_info *ei; + uint8_t *msg = mtod(m, uint8_t *); + + memset(&resp, 0, sizeof(resp)); + + DNPRINTF(QWZ_D_QMI, "%s\n", __func__); + + ei = qmi_wlanfw_bdf_download_resp_msg_v01_ei; + if (qwz_qmi_decode_msg(sc, &resp, sizeof(resp), ei, msg, msg_len)) + return; + + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.result=0x%x\n", + __func__, le16toh(resp.resp.result)); + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.error=0x%x\n", + __func__, le16toh(resp.resp.error)); + + sc->qmi_resp.result = le16toh(resp.resp.result); + sc->qmi_resp.error = le16toh(resp.resp.error); + wakeup(&sc->qmi_resp); +} + +void +qwz_qmi_recv_wlanfw_m3_info_resp_v1(struct qwz_softc *sc, struct mbuf *m, + uint16_t txn_id, uint16_t msg_len) +{ + struct qmi_wlanfw_m3_info_resp_msg_v01 resp; + const struct qmi_elem_info *ei; + uint8_t *msg = mtod(m, uint8_t *); + + memset(&resp, 0, sizeof(resp)); + + DNPRINTF(QWZ_D_QMI, "%s\n", __func__); + + ei = qmi_wlanfw_m3_info_resp_msg_v01_ei; + if (qwz_qmi_decode_msg(sc, &resp, sizeof(resp), ei, msg, msg_len)) + return; + + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.result=0x%x\n", + __func__, le16toh(resp.resp.result)); + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.error=0x%x\n", + __func__, le16toh(resp.resp.error)); + + sc->qmi_resp.result = le16toh(resp.resp.result); + sc->qmi_resp.error = le16toh(resp.resp.error); + wakeup(&sc->qmi_resp); +} + +void +qwz_qmi_recv_wlanfw_wlan_ini_resp_v1(struct qwz_softc *sc, struct mbuf *m, + uint16_t txn_id, uint16_t msg_len) +{ + struct qmi_wlanfw_wlan_ini_resp_msg_v01 resp; + const struct qmi_elem_info *ei; + uint8_t *msg = mtod(m, uint8_t *); + + memset(&resp, 0, sizeof(resp)); + + DNPRINTF(QWZ_D_QMI, "%s\n", __func__); + + ei = qmi_wlanfw_wlan_ini_resp_msg_v01_ei; + if (qwz_qmi_decode_msg(sc, &resp, sizeof(resp), ei, msg, msg_len)) + return; + + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.result=0x%x\n", + __func__, le16toh(resp.resp.result)); + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.error=0x%x\n", + __func__, le16toh(resp.resp.error)); + + sc->qmi_resp.result = le16toh(resp.resp.result); + sc->qmi_resp.error = le16toh(resp.resp.error); + wakeup(&sc->qmi_resp); +} + +void +qwz_qmi_recv_wlanfw_wlan_cfg_resp_v1(struct qwz_softc *sc, struct mbuf *m, + uint16_t txn_id, uint16_t msg_len) +{ + struct qmi_wlanfw_wlan_cfg_resp_msg_v01 resp; + const struct qmi_elem_info *ei; + uint8_t *msg = mtod(m, uint8_t *); + + memset(&resp, 0, sizeof(resp)); + + DNPRINTF(QWZ_D_QMI, "%s\n", __func__); + + ei = qmi_wlanfw_wlan_cfg_resp_msg_v01_ei; + if (qwz_qmi_decode_msg(sc, &resp, sizeof(resp), ei, msg, msg_len)) + return; + + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.result=0x%x\n", + __func__, le16toh(resp.resp.result)); + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.error=0x%x\n", + __func__, le16toh(resp.resp.error)); + + sc->qmi_resp.result = le16toh(resp.resp.result); + sc->qmi_resp.error = le16toh(resp.resp.error); + wakeup(&sc->qmi_resp); +} + +void +qwz_qmi_recv_wlanfw_wlan_mode_resp_v1(struct qwz_softc *sc, struct mbuf *m, + uint16_t txn_id, uint16_t msg_len) +{ + struct qmi_wlanfw_wlan_mode_resp_msg_v01 resp; + const struct qmi_elem_info *ei; + uint8_t *msg = mtod(m, uint8_t *); + + memset(&resp, 0, sizeof(resp)); + + DNPRINTF(QWZ_D_QMI, "%s\n", __func__); + + ei = qmi_wlanfw_wlan_mode_resp_msg_v01_ei; + if (qwz_qmi_decode_msg(sc, &resp, sizeof(resp), ei, msg, msg_len)) + return; + + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.result=0x%x\n", + __func__, le16toh(resp.resp.result)); + DNPRINTF(QWZ_D_QMI, "%s: resp.resp.error=0x%x\n", + __func__, le16toh(resp.resp.error)); + + sc->qmi_resp.result = le16toh(resp.resp.result); + sc->qmi_resp.error = le16toh(resp.resp.error); + wakeup(&sc->qmi_resp); +} + +void +qwz_qmi_recv_response(struct qwz_softc *sc, struct mbuf *m, + uint16_t txn_id, uint16_t msg_id, uint16_t msg_len) +{ + switch (msg_id) { + case QMI_WLANFW_IND_REGISTER_REQ_V01: + qwz_qmi_recv_wlanfw_ind_register_req_v1(sc, m, txn_id, msg_len); + break; + case QMI_WLFW_HOST_CAP_RESP_V01: + qwz_qmi_recv_wlanfw_host_cap_resp_v1(sc, m, txn_id, msg_len); + break; + case QMI_WLFW_RESPOND_MEM_RESP_V01: + qwz_qmi_recv_wlanfw_respond_mem_resp_v1(sc, m, txn_id, msg_len); + break; + case QMI_WLANFW_CAP_RESP_V01: + qwz_qmi_recv_wlanfw_cap_resp_v1(sc, m, txn_id, msg_len); + break; + case QMI_WLANFW_BDF_DOWNLOAD_RESP_V01: + qwz_qmi_recv_wlanfw_bdf_download_resp_v1(sc, m, txn_id, + msg_len); + break; + case QMI_WLANFW_M3_INFO_RESP_V01: + qwz_qmi_recv_wlanfw_m3_info_resp_v1(sc, m, txn_id, msg_len); + break; + case QMI_WLANFW_WLAN_INI_RESP_V01: + qwz_qmi_recv_wlanfw_wlan_ini_resp_v1(sc, m, txn_id, msg_len); + break; + case QMI_WLANFW_WLAN_CFG_RESP_V01: + qwz_qmi_recv_wlanfw_wlan_cfg_resp_v1(sc, m, txn_id, msg_len); + break; + case QMI_WLANFW_WLAN_MODE_RESP_V01: + qwz_qmi_recv_wlanfw_wlan_mode_resp_v1(sc, m, txn_id, msg_len); + break; + default: + printf("%s: unhandled QMI response 0x%x\n", + sc->sc_dev.dv_xname, msg_id); + break; + } +} + +void +qwz_qmi_recv_wlanfw_request_mem_indication(struct qwz_softc *sc, struct mbuf *m, + uint16_t txn_id, uint16_t msg_len) +{ + struct qmi_wlanfw_request_mem_ind_msg_v01 *ind = NULL; + const struct qmi_elem_info *ei; + uint8_t *msg = mtod(m, uint8_t *); + + DNPRINTF(QWZ_D_QMI, "%s\n", __func__); + + if (!sc->expect_fwmem_req || sc->sc_req_mem_ind != NULL) + return; + + /* This structure is too large for the stack. */ + ind = malloc(sizeof(*ind), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ind == NULL) + return; + + ei = qmi_wlanfw_request_mem_ind_msg_v01_ei; + if (qwz_qmi_decode_msg(sc, ind, sizeof(*ind), ei, msg, msg_len)) { + free(ind, M_DEVBUF, sizeof(*ind)); + return; + } + + /* Handled by qwz_qmi_mem_seg_send() in process context */ + sc->sc_req_mem_ind = ind; + wakeup(&sc->sc_req_mem_ind); +} + +void +qwz_qmi_recv_indication(struct qwz_softc *sc, struct mbuf *m, + uint16_t txn_id, uint16_t msg_id, uint16_t msg_len) +{ + switch (msg_id) { + case QMI_WLFW_REQUEST_MEM_IND_V01: + qwz_qmi_recv_wlanfw_request_mem_indication(sc, m, + txn_id, msg_len); + break; + case QMI_WLFW_FW_MEM_READY_IND_V01: + sc->fwmem_ready = 1; + wakeup(&sc->fwmem_ready); + break; + case QMI_WLFW_FW_INIT_DONE_IND_V01: + sc->fw_init_done = 1; + wakeup(&sc->fw_init_done); + break; + default: + printf("%s: unhandled QMI indication 0x%x\n", + sc->sc_dev.dv_xname, msg_id); + break; + } +} + +void +qwz_qrtr_recv_data(struct qwz_softc *sc, struct mbuf *m, size_t size) +{ + struct qmi_header hdr; + uint16_t txn_id, msg_id, msg_len; + + if (size < sizeof(hdr)) { + printf("%s: QMI message too short: %zu bytes\n", + sc->sc_dev.dv_xname, size); + return; + } + + memcpy(&hdr, mtod(m, void *), sizeof(hdr)); + + DNPRINTF(QWZ_D_QMI, + "%s: QMI message type=0x%x txn=0x%x id=0x%x len=%u\n", + __func__, hdr.type, le16toh(hdr.txn_id), + le16toh(hdr.msg_id), le16toh(hdr.msg_len)); + + txn_id = le16toh(hdr.txn_id); + msg_id = le16toh(hdr.msg_id); + msg_len = le16toh(hdr.msg_len); + if (sizeof(hdr) + msg_len != size) { + printf("%s: bad length in QMI message header: %u\n", + sc->sc_dev.dv_xname, msg_len); + return; + } + + switch (hdr.type) { + case QMI_RESPONSE: + m_adj(m, sizeof(hdr)); + qwz_qmi_recv_response(sc, m, txn_id, msg_id, msg_len); + break; + case QMI_INDICATION: + m_adj(m, sizeof(hdr)); + qwz_qmi_recv_indication(sc, m, txn_id, msg_id, msg_len); + break; + default: + printf("%s: unhandled QMI message type %u\n", + sc->sc_dev.dv_xname, hdr.type); + break; + } +} + +int +qwz_qrtr_say_hello(struct qwz_softc *sc) +{ + struct qrtr_hdr_v1 hdr; + struct qrtr_ctrl_pkt pkt; + struct mbuf *m; + size_t totlen, padlen; + int err; + + totlen = sizeof(hdr) + sizeof(pkt); + padlen = roundup(totlen, 4); + + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) { + err = ENOBUFS; + goto done; + } + + if (padlen <= MCLBYTES) + MCLGET(m, M_DONTWAIT); + else + MCLGETL(m, M_DONTWAIT, padlen); + if ((m->m_flags & M_EXT) == 0) { + err = ENOBUFS; + goto done; + } + + m->m_len = m->m_pkthdr.len = padlen; + + memset(&hdr, 0, sizeof(hdr)); + hdr.version = htole32(QRTR_PROTO_VER_1); + hdr.type = htole32(QRTR_TYPE_HELLO); + hdr.src_node_id = htole32(0x01); /* TODO make human-readable */ + hdr.src_port_id = htole32(0xfffffffeU); /* TODO make human-readable */ + hdr.dst_node_id = htole32(0x07); /* TODO make human-readable */ + hdr.dst_port_id = htole32(0xfffffffeU); /* TODO make human-readable */ + hdr.size = htole32(sizeof(pkt)); + + err = m_copyback(m, 0, sizeof(hdr), &hdr, M_NOWAIT); + if (err) + goto done; + + memset(&pkt, 0, sizeof(pkt)); + pkt.cmd = htole32(QRTR_TYPE_HELLO); + + err = m_copyback(m, sizeof(hdr), sizeof(pkt), &pkt, M_NOWAIT); + if (err) + goto done; + + /* Zero-pad the mbuf */ + if (padlen != totlen) { + uint32_t pad = 0; + err = m_copyback(m, totlen, padlen - totlen, &pad, M_NOWAIT); + if (err) + goto done; + } + + err = sc->ops.submit_xfer(sc, m); +done: + if (err) + m_freem(m); + return err; +} + +int +qwz_qrtr_resume_tx(struct qwz_softc *sc) +{ + struct qrtr_hdr_v1 hdr; + struct qrtr_ctrl_pkt pkt; + struct mbuf *m; + size_t totlen, padlen; + int err; + + totlen = sizeof(hdr) + sizeof(pkt); + padlen = roundup(totlen, 4); + + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) { + err = ENOBUFS; + goto done; + } + + if (padlen <= MCLBYTES) + MCLGET(m, M_DONTWAIT); + else + MCLGETL(m, M_DONTWAIT, padlen); + if ((m->m_flags & M_EXT) == 0) { + err = ENOBUFS; + goto done; + } + + m->m_len = m->m_pkthdr.len = padlen; + + memset(&hdr, 0, sizeof(hdr)); + hdr.version = htole32(QRTR_PROTO_VER_1); + hdr.type = htole32(QRTR_TYPE_RESUME_TX); + hdr.src_node_id = htole32(0x01); /* TODO make human-readable */ + hdr.src_port_id = htole32(0x4000); /* TODO make human-readable */ + hdr.dst_node_id = htole32(0x07); /* TODO make human-readable */ + hdr.dst_port_id = htole32(0x01); /* TODO make human-readable */ + hdr.size = htole32(sizeof(pkt)); + + err = m_copyback(m, 0, sizeof(hdr), &hdr, M_NOWAIT); + if (err) + goto done; + + memset(&pkt, 0, sizeof(pkt)); + pkt.cmd = htole32(QRTR_TYPE_RESUME_TX); + pkt.client.node = htole32(0x01); + pkt.client.port = htole32(0x4000); + + err = m_copyback(m, sizeof(hdr), sizeof(pkt), &pkt, M_NOWAIT); + if (err) + goto done; + + /* Zero-pad the mbuf */ + if (padlen != totlen) { + uint32_t pad = 0; + err = m_copyback(m, totlen, padlen - totlen, &pad, M_NOWAIT); + if (err) + goto done; + } + + err = sc->ops.submit_xfer(sc, m); +done: + if (err) + m_freem(m); + return err; +} + +void +qwz_qrtr_recv_msg(struct qwz_softc *sc, struct mbuf *m) +{ + struct qrtr_hdr_v1 *v1 = mtod(m, struct qrtr_hdr_v1 *); + struct qrtr_hdr_v2 *v2 = mtod(m, struct qrtr_hdr_v2 *); + struct qrtr_ctrl_pkt *pkt; + uint32_t type, size, hdrsize; + uint8_t ver, confirm_rx; + + ver = *mtod(m, uint8_t *); + switch (ver) { + case QRTR_PROTO_VER_1: + DNPRINTF(QWZ_D_QMI, + "%s: type %u size %u confirm_rx %u\n", __func__, + letoh32(v1->type), letoh32(v1->size), + letoh32(v1->confirm_rx)); + type = letoh32(v1->type); + size = letoh32(v1->size); + confirm_rx = !!letoh32(v1->confirm_rx); + hdrsize = sizeof(*v1); + break; + case QRTR_PROTO_VER_2: + DNPRINTF(QWZ_D_QMI, + "%s: type %u size %u confirm_rx %u\n", __func__, + v2->type, letoh32(v2->size), + !!(v2->flags & QRTR_FLAGS_CONFIRM_RX)); + type = v2->type; + size = letoh32(v2->size); + confirm_rx = !!(v2->flags & QRTR_FLAGS_CONFIRM_RX); + hdrsize = sizeof(*v2); + break; + default: + printf("%s: unsupported qrtr version %u\n", + sc->sc_dev.dv_xname, ver); + return; + } + + if (size > m->m_pkthdr.len) { + printf("%s: bad size in qrtr message header: %u\n", + sc->sc_dev.dv_xname, size); + return; + } + + switch (type) { + case QRTR_TYPE_DATA: + m_adj(m, hdrsize); + qwz_qrtr_recv_data(sc, m, size); + break; + case QRTR_TYPE_HELLO: + qwz_qrtr_say_hello(sc); + break; + case QRTR_TYPE_NEW_SERVER: + m_adj(m, hdrsize); + pkt = mtod(m, struct qrtr_ctrl_pkt *); + sc->qrtr_server.service = le32toh(pkt->server.service); + sc->qrtr_server.instance = le32toh(pkt->server.instance); + sc->qrtr_server.node = le32toh(pkt->server.node); + sc->qrtr_server.port = le32toh(pkt->server.port); + DNPRINTF(QWZ_D_QMI, + "%s: new server: service=0x%x instance=0x%x node=0x%x " + "port=0x%x\n", __func__, sc->qrtr_server.service, + sc->qrtr_server.instance, + sc->qrtr_server.node, sc->qrtr_server.port); + wakeup(&sc->qrtr_server); + break; + default: + DPRINTF("%s: unhandled qrtr type %u\n", + sc->sc_dev.dv_xname, type); + return; + } + + if (confirm_rx) + qwz_qrtr_resume_tx(sc); +} + +// Not needed because we don't implenent QMI as a network service. +#define qwz_qmi_init_service(sc) (0) +#define qwz_qmi_deinit_service(sc) (0) + +int +qwz_qmi_encode_datalen(uint8_t *p, uint32_t *datalen, + const struct qmi_elem_info *ei, void *input) +{ + memcpy(datalen, input + ei->offset, sizeof(uint32_t)); + + if (ei->elem_size == sizeof(uint8_t)) { + p[0] = (*datalen & 0xff); + } else if (ei->elem_size == sizeof(uint16_t)) { + p[0] = (*datalen & 0xff); + p[1] = (*datalen >> 8) & 0xff; + } else { + printf("%s: bad element size\n", __func__); + return -1; + } + + return 0; +} + +int +qwz_qmi_encode_byte(uint8_t *p, const struct qmi_elem_info *ei, void *input, + int i) +{ + if (ei->elem_size != sizeof(uint8_t)) { + printf("%s: bad element size\n", __func__); + return -1; + } + + if (p == NULL) + return 0; + + memcpy(p, input + ei->offset + (i * ei->elem_size), ei->elem_size); + return 0; +} + +int +qwz_qmi_encode_word(uint8_t *p, const struct qmi_elem_info *ei, void *input, + int i) +{ + uint16_t val; + + if (ei->elem_size != sizeof(val)) { + printf("%s: bad element size\n", __func__); + return -1; + } + + if (p == NULL) + return 0; + + memcpy(&val, input + ei->offset + (i * ei->elem_size), ei->elem_size); + val = htole16(val); + memcpy(p, &val, sizeof(val)); + return 0; +} + +int +qwz_qmi_encode_dword(uint8_t *p, const struct qmi_elem_info *ei, void *input, + int i) +{ + uint32_t val; + + if (ei->elem_size != sizeof(val)) { + printf("%s: bad element size\n", __func__); + return -1; + } + + if (p == NULL) + return 0; + + memcpy(&val, input + ei->offset + (i * ei->elem_size), ei->elem_size); + val = htole32(val); + memcpy(p, &val, sizeof(val)); + return 0; +} + +int +qwz_qmi_encode_qword(uint8_t *p, const struct qmi_elem_info *ei, void *input, + int i) +{ + uint64_t val; + + if (ei->elem_size != sizeof(val)) { + printf("%s: bad element size\n", __func__); + return -1; + } + + if (p == NULL) + return 0; + + memcpy(&val, input + ei->offset + (i * ei->elem_size), ei->elem_size); + val = htole64(val); + memcpy(p, &val, sizeof(val)); + return 0; +} + +int +qwz_qmi_encode_struct(uint8_t *p, size_t *encoded_len, + const struct qmi_elem_info *struct_ei, void *input, size_t input_len) +{ + const struct qmi_elem_info *ei = struct_ei->ei_array; + size_t remain = input_len; + + *encoded_len = 0; + + while (ei->data_type != QMI_EOTI) { + if (ei->data_type == QMI_OPT_FLAG) { + uint8_t do_encode, tlv_type; + + memcpy(&do_encode, input + ei->offset, sizeof(uint8_t)); + ei++; /* Advance to element we might have to encode. */ + if (ei->data_type == QMI_OPT_FLAG || + ei->data_type == QMI_EOTI) { + printf("%s: bad optional flag element\n", + __func__); + return -1; + } + if (!do_encode) { + /* The element will not be encoded. Skip it. */ + tlv_type = ei->tlv_type; + while (ei->data_type != QMI_EOTI && + ei->tlv_type == tlv_type) + ei++; + continue; + } + } + + if (ei->elem_size > remain) { + printf("%s: QMI message buffer too short\n", __func__); + return -1; + } + + switch (ei->data_type) { + case QMI_UNSIGNED_1_BYTE: + if (qwz_qmi_encode_byte(p, ei, input, 0)) + return -1; + break; + case QMI_UNSIGNED_2_BYTE: + if (qwz_qmi_encode_word(p, ei, input, 0)) + return -1; + break; + case QMI_UNSIGNED_4_BYTE: + case QMI_SIGNED_4_BYTE_ENUM: + if (qwz_qmi_encode_dword(p, ei, input, 0)) + return -1; + break; + case QMI_UNSIGNED_8_BYTE: + if (qwz_qmi_encode_qword(p, ei, input, 0)) + return -1; + break; + default: + printf("%s: unhandled QMI struct element type %d\n", + __func__, ei->data_type); + return -1; + } + + remain -= ei->elem_size; + if (p != NULL) + p += ei->elem_size; + *encoded_len += ei->elem_size; + ei++; + } + + return 0; +} + +int +qwz_qmi_encode_string(uint8_t *p, size_t *encoded_len, + const struct qmi_elem_info *string_ei, void *input, size_t input_len) +{ + *encoded_len = strnlen(input, input_len); + if (*encoded_len > string_ei->elem_len) { + printf("%s: QMI message buffer too short\n", __func__); + return -1; + } + + if (p) + memcpy(p, input, *encoded_len); + + return 0; +} + +int +qwz_qmi_encode_msg(uint8_t **encoded_msg, size_t *encoded_len, int type, + uint16_t *txn_id, uint16_t msg_id, size_t msg_len, + const struct qmi_elem_info *ei, void *input, size_t input_len) +{ + const struct qmi_elem_info *ei0 = ei; + struct qmi_header hdr; + size_t remain; + uint8_t *p, *op; + + *encoded_msg = NULL; + *encoded_len = 0; + + /* First pass: Determine length of encoded message. */ + while (ei->data_type != QMI_EOTI) { + int nelem = 1, i; + + if (ei->offset + ei->elem_size > input_len) { + printf("%s: bad input buffer offset at element 0x%x " + "data type 0x%x\n", + __func__, ei->tlv_type, ei->data_type); + goto err; + } + + /* + * OPT_FLAG determines whether the next element + * should be considered for encoding. + */ + if (ei->data_type == QMI_OPT_FLAG) { + uint8_t do_encode, tlv_type; + + memcpy(&do_encode, input + ei->offset, sizeof(uint8_t)); + ei++; /* Advance to element we might have to encode. */ + if (ei->data_type == QMI_OPT_FLAG || + ei->data_type == QMI_EOTI) { + printf("%s: bad optional element\n", __func__); + goto err; + } + if (!do_encode) { + /* The element will not be encoded. Skip it. */ + tlv_type = ei->tlv_type; + while (ei->data_type != QMI_EOTI && + ei->tlv_type == tlv_type) + ei++; + continue; + } + } + + *encoded_len += 3; /* type, length */ + if (ei->data_type == QMI_DATA_LEN) { + uint32_t datalen = 0; + uint8_t dummy[2]; + + if (qwz_qmi_encode_datalen(dummy, &datalen, ei, input)) + goto err; + *encoded_len += ei->elem_size; + ei++; + if (ei->array_type != VAR_LEN_ARRAY) { + printf("%s: data len not for a var array\n", + __func__); + goto err; + } + nelem = datalen; + if (ei->data_type == QMI_STRUCT) { + for (i = 0; i < nelem; i++) { + size_t encoded_struct_len = 0; + size_t inoff = ei->offset + (i * ei->elem_size); + + if (qwz_qmi_encode_struct(NULL, + &encoded_struct_len, ei, + input + inoff, input_len - inoff)) + goto err; + + *encoded_len += encoded_struct_len; + } + } else + *encoded_len += nelem * ei->elem_size; + ei++; + } else if (ei->data_type == QMI_STRING) { + size_t encoded_string_len = 0; + size_t inoff = ei->offset; + + if (qwz_qmi_encode_string(NULL, + &encoded_string_len, ei, + input + inoff, input_len - inoff)) + goto err; + *encoded_len += encoded_string_len; + ei++; + } else { + *encoded_len += ei->elem_size; + ei++; + } + } + + *encoded_len += sizeof(hdr); + *encoded_msg = malloc(*encoded_len, M_DEVBUF, M_NOWAIT | M_ZERO); + if (*encoded_msg == NULL) + return ENOMEM; + + hdr.type = type; + hdr.txn_id = htole16(*txn_id); + hdr.msg_id = htole16(msg_id); + hdr.msg_len = htole16(*encoded_len - sizeof(hdr)); + memcpy(*encoded_msg, &hdr, sizeof(hdr)); + + /* Second pass: Encode the message. */ + ei = ei0; + p = *encoded_msg + sizeof(hdr); + remain = *encoded_len - sizeof(hdr); + while (ei->data_type != QMI_EOTI) { + uint32_t datalen = 0; + int nelem = 1, i; + + if (ei->data_type == QMI_OPT_FLAG) { + uint8_t do_encode, tlv_type; + + memcpy(&do_encode, input + ei->offset, sizeof(uint8_t)); + ei++; /* Advance to element we might have to encode. */ + if (ei->data_type == QMI_OPT_FLAG || + ei->data_type == QMI_EOTI) { + printf("%s: bad optional flag element\n", + __func__); + goto err; + } + if (!do_encode) { + /* The element will not be encoded. Skip it. */ + tlv_type = ei->tlv_type; + while (ei->data_type != QMI_EOTI && + ei->tlv_type == tlv_type) + ei++; + continue; + } + } + + if (ei->elem_size + 3 > remain) { + printf("%s: QMI message buffer too short\n", __func__); + goto err; + } + + /* 3 bytes of type-length-value header, remember for later */ + op = p; + p += 3; + + if (ei->data_type == QMI_DATA_LEN) { + if (qwz_qmi_encode_datalen(p, &datalen, ei, input)) + goto err; + p += ei->elem_size; + ei++; + if (ei->array_type == VAR_LEN_ARRAY) + nelem = datalen; + } + + for (i = 0; i < nelem; i++) { + size_t encoded_struct_len = 0; + size_t encoded_string_len = 0; + size_t inoff = ei->offset + (i * ei->elem_size); + + switch (ei->data_type) { + case QMI_UNSIGNED_1_BYTE: + if (qwz_qmi_encode_byte(p, ei, input, i)) + goto err; + remain -= ei->elem_size; + p += ei->elem_size; + break; + case QMI_UNSIGNED_2_BYTE: + case QMI_SIGNED_2_BYTE_ENUM: + if (qwz_qmi_encode_word(p, ei, input, i)) + goto err; + remain -= ei->elem_size; + p += ei->elem_size; + break; + case QMI_UNSIGNED_4_BYTE: + case QMI_SIGNED_4_BYTE_ENUM: + if (qwz_qmi_encode_dword(p, ei, input, i)) + goto err; + remain -= ei->elem_size; + p += ei->elem_size; + break; + case QMI_UNSIGNED_8_BYTE: + if (qwz_qmi_encode_qword(p, ei, input, i)) + goto err; + remain -= ei->elem_size; + p += ei->elem_size; + break; + case QMI_STRUCT: + if (qwz_qmi_encode_struct(p, + &encoded_struct_len, ei, + input + inoff, input_len - inoff)) + goto err; + remain -= encoded_struct_len; + p += encoded_struct_len; + break; + case QMI_STRING: + if (qwz_qmi_encode_string(p, + &encoded_string_len, ei, + input + inoff, input_len - inoff)) + goto err; + remain -= encoded_string_len; + p += encoded_string_len; + break; + default: + printf("%s: unhandled QMI message element type %d\n", + __func__, ei->data_type); + goto err; + } + } + + op[0] = ei->tlv_type; + op[1] = (p - (op + 3)) & 0xff; + op[2] = ((p - (op + 3)) >> 8) & 0xff; + + ei++; + } + + if (0) { + int i; + DNPRINTF(QWZ_D_QMI, + "%s: message type 0x%x txnid 0x%x msgid 0x%x " + "msglen %zu encoded:", __func__, + type, *txn_id, msg_id, *encoded_len - sizeof(hdr)); + for (i = 0; i < *encoded_len; i++) { + DNPRINTF(QWZ_D_QMI, "%s %.2x", i % 16 == 0 ? "\n" : "", + (*encoded_msg)[i]); + } + if (i % 16) + DNPRINTF(QWZ_D_QMI, "\n"); + } + + (*txn_id)++; /* wrap-around is fine */ + return 0; +err: + free(*encoded_msg, M_DEVBUF, *encoded_len); + *encoded_msg = NULL; + *encoded_len = 0; + return -1; +} + +int +qwz_qmi_send_request(struct qwz_softc *sc, uint16_t msg_id, size_t msg_len, + const struct qmi_elem_info *ei, void *req, size_t req_len) +{ + struct qrtr_hdr_v1 hdr; + struct mbuf *m; + uint8_t *encoded_msg; + size_t encoded_len; + size_t totlen, padlen; + int err; + + if (qwz_qmi_encode_msg(&encoded_msg, &encoded_len, QMI_REQUEST, + &sc->qmi_txn_id, msg_id, msg_len, ei, req, req_len)) + return -1; + + totlen = sizeof(hdr) + encoded_len; + padlen = roundup(totlen, 4); + + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) { + err = ENOBUFS; + goto done; + } + + if (padlen <= MCLBYTES) + MCLGET(m, M_DONTWAIT); + else + MCLGETL(m, M_DONTWAIT, padlen); + if ((m->m_flags & M_EXT) == 0) { + err = ENOBUFS; + goto done; + } + + m->m_len = m->m_pkthdr.len = padlen; + + memset(&hdr, 0, sizeof(hdr)); + hdr.version = htole32(QRTR_PROTO_VER_1); + hdr.type = htole32(QRTR_TYPE_DATA); + hdr.src_node_id = htole32(0x01); /* TODO make human-readable */ + hdr.src_port_id = htole32(0x4000); /* TODO make human-readable */ + hdr.dst_node_id = htole32(0x07); /* TODO make human-readable */ + hdr.dst_port_id = htole32(0x01); /* TODO make human-readable */ + hdr.size = htole32(encoded_len); + + err = m_copyback(m, 0, sizeof(hdr), &hdr, M_NOWAIT); + if (err) + goto done; + + err = m_copyback(m, sizeof(hdr), encoded_len, encoded_msg, M_NOWAIT); + if (err) + goto done; + + /* Zero-pad the mbuf */ + if (padlen != totlen) { + uint32_t pad = 0; + err = m_copyback(m, totlen, padlen - totlen, &pad, M_NOWAIT); + if (err) + goto done; + } + + err = sc->ops.submit_xfer(sc, m); +done: + if (err) + m_freem(m); + free(encoded_msg, M_DEVBUF, encoded_len); + return err; +} + +int +qwz_qmi_fw_ind_register_send(struct qwz_softc *sc) +{ + struct qmi_wlanfw_ind_register_req_msg_v01 req; + int ret; + + memset(&req, 0, sizeof(req)); + + req.client_id_valid = 1; + req.client_id = QMI_WLANFW_CLIENT_ID; + req.fw_ready_enable_valid = 1; + req.fw_ready_enable = 1; + req.cal_done_enable_valid = 1; + req.cal_done_enable = 1; + req.fw_init_done_enable_valid = 1; + req.fw_init_done_enable = 1; + + req.pin_connect_result_enable_valid = 0; + req.pin_connect_result_enable = 0; + + /* + * WCN6750 doesn't request for DDR memory via QMI, + * instead it uses a fixed 12MB reserved memory region in DDR. + */ + if (!sc->hw_params.fixed_fw_mem) { + req.request_mem_enable_valid = 1; + req.request_mem_enable = 1; + req.fw_mem_ready_enable_valid = 1; + req.fw_mem_ready_enable = 1; + } + + DNPRINTF(QWZ_D_QMI, "%s: qmi indication register request\n", __func__); + + ret = qwz_qmi_send_request(sc, QMI_WLANFW_IND_REGISTER_REQ_V01, + QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_ind_register_req_msg_v01_ei, + &req, sizeof(req)); + if (ret) { + printf("%s: failed to send indication register request: %d\n", + sc->sc_dev.dv_xname, ret); + return -1; + } + + sc->qmi_resp.result = QMI_RESULT_FAILURE_V01; + while (sc->qmi_resp.result != QMI_RESULT_SUCCESS_V01) { + ret = tsleep_nsec(&sc->qmi_resp, 0, "qwzfwind", + SEC_TO_NSEC(1)); + if (ret) { + printf("%s: fw indication register request timeout\n", + sc->sc_dev.dv_xname); + return ret; + } + } + + return 0; +} + +int +qwz_qmi_host_cap_send(struct qwz_softc *sc) +{ + struct qmi_wlanfw_host_cap_req_msg_v01 req; + int ret; + + memset(&req, 0, sizeof(req)); + req.num_clients_valid = 1; + req.num_clients = 1; + req.mem_cfg_mode = sc->hw_params.fw_mem_mode; + req.mem_cfg_mode_valid = 1; + req.bdf_support_valid = 1; + req.bdf_support = 1; + + if (sc->hw_params.m3_fw_support) { + req.m3_support_valid = 1; + req.m3_support = 1; + req.m3_cache_support_valid = 1; + req.m3_cache_support = 1; + } else { + req.m3_support_valid = 0; + req.m3_support = 0; + req.m3_cache_support_valid = 0; + req.m3_cache_support = 0; + } + + req.cal_done_valid = 1; + req.cal_done = sc->qmi_cal_done; + + if (sc->hw_params.internal_sleep_clock) { + req.nm_modem_valid = 1; + + /* Notify firmware that this is non-qualcomm platform. */ + req.nm_modem |= QWZ_HOST_CSTATE_BIT; + + /* Notify firmware about the sleep clock selection, + * nm_modem_bit[1] is used for this purpose. Host driver on + * non-qualcomm platforms should select internal sleep + * clock. + */ + req.nm_modem |= QWZ_SLEEP_CLOCK_SELECT_INTERNAL_BIT; + } + + if (sc->hw_params.global_reset) + req.nm_modem |= QWZ_PLATFORM_CAP_PCIE_GLOBAL_RESET; + + req.nm_modem |= QWZ_PLATFORM_CAP_PCIE_PME_D3COLD; + + DNPRINTF(QWZ_D_QMI, "%s: qmi host cap request\n", __func__); + + ret = qwz_qmi_send_request(sc, QMI_WLANFW_HOST_CAP_REQ_V01, + QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_host_cap_req_msg_v01_ei, + &req, sizeof(req)); + if (ret) { + printf("%s: failed to send host cap request: %d\n", + sc->sc_dev.dv_xname, ret); + return -1; + } + + sc->qmi_resp.result = QMI_RESULT_FAILURE_V01; + while (sc->qmi_resp.result != QMI_RESULT_SUCCESS_V01) { + ret = tsleep_nsec(&sc->qmi_resp, 0, "qwzfwhcap", + SEC_TO_NSEC(1)); + if (ret) { + printf("%s: fw host cap request timeout\n", + sc->sc_dev.dv_xname); + return ret; + } + } + + return 0; +} + +int +qwz_qmi_mem_seg_send(struct qwz_softc *sc) +{ + struct qmi_wlanfw_respond_mem_req_msg_v01 *req; + struct qmi_wlanfw_request_mem_ind_msg_v01 *ind; + uint32_t mem_seg_len; + const uint32_t mem_seg_len_max = 64; /* bump if needed by future fw */ + uint16_t expected_result; + size_t total_size; + int i, ret; + + sc->fwmem_ready = 0; + + while (sc->sc_req_mem_ind == NULL) { + ret = tsleep_nsec(&sc->sc_req_mem_ind, 0, "qwzfwmem", + SEC_TO_NSEC(10)); + if (ret) { + printf("%s: fw memory request timeout\n", + sc->sc_dev.dv_xname); + return -1; + } + } + + sc->expect_fwmem_req = 0; + + ind = sc->sc_req_mem_ind; + mem_seg_len = le32toh(ind->mem_seg_len); + if (mem_seg_len > mem_seg_len_max) { + printf("%s: firmware requested too many memory segments: %u\n", + sc->sc_dev.dv_xname, mem_seg_len); + free(sc->sc_req_mem_ind, M_DEVBUF, sizeof(*sc->sc_req_mem_ind)); + sc->sc_req_mem_ind = NULL; + return -1; + } + + total_size = 0; + for (i = 0; i < mem_seg_len; i++) { + if (ind->mem_seg[i].size == 0) { + printf("%s: firmware requested zero-sized " + "memory segment %u\n", sc->sc_dev.dv_xname, i); + free(sc->sc_req_mem_ind, M_DEVBUF, + sizeof(*sc->sc_req_mem_ind)); + sc->sc_req_mem_ind = NULL; + return -1; + } + total_size += le32toh(ind->mem_seg[i].size); + } + + req = malloc(sizeof(*req), M_DEVBUF, M_NOWAIT | M_ZERO); + if (req == NULL) { + printf("%s: failed to allocate respond memory request\n", + sc->sc_dev.dv_xname); + free(sc->sc_req_mem_ind, M_DEVBUF, sizeof(*sc->sc_req_mem_ind)); + sc->sc_req_mem_ind = NULL; + return -1; + } + + if (total_size == 0) { + /* Should not happen. Send back an empty allocation. */ + printf("%s: firmware has requested no memory\n", + sc->sc_dev.dv_xname); + mem_seg_len = 0; + } else if (sc->fwmem == NULL || QWZ_DMA_LEN(sc->fwmem) < total_size) { + if (sc->fwmem != NULL) + qwz_dmamem_free(sc->sc_dmat, sc->fwmem); + sc->fwmem = qwz_dmamem_alloc(sc->sc_dmat, total_size, 65536); + if (sc->fwmem == NULL) { + printf("%s: failed to allocate %zu bytes of DMA " + "memory for firmware\n", sc->sc_dev.dv_xname, + total_size); + /* Send back an empty allocation. */ + mem_seg_len = 0; + } else + DPRINTF("%s: allocated %zu bytes of DMA memory for " + "firmware\n", sc->sc_dev.dv_xname, total_size); + } + + /* Chunk DMA memory block into segments as requested by firmware. */ + req->mem_seg_len = htole32(mem_seg_len); + if (sc->fwmem) { + uint64_t paddr = QWZ_DMA_DVA(sc->fwmem); + + for (i = 0; i < mem_seg_len; i++) { + DPRINTF("%s: mem seg[%d] addr=%llx size=%u type=%u\n", + __func__, i, paddr, le32toh(ind->mem_seg[i].size), + le32toh(ind->mem_seg[i].type)); + req->mem_seg[i].addr = htole64(paddr); + paddr += le32toh(ind->mem_seg[i].size); + + /* Values in 'ind' are in little-endian format. */ + req->mem_seg[i].size = ind->mem_seg[i].size; + req->mem_seg[i].type = ind->mem_seg[i].type; + } + } + + free(ind, M_DEVBUF, sizeof(*ind)); + sc->sc_req_mem_ind = NULL; + + ret = qwz_qmi_send_request(sc, QMI_WLANFW_RESPOND_MEM_REQ_V01, + QMI_WLANFW_RESPOND_MEM_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_respond_mem_req_msg_v01_ei, + req, sizeof(*req)); + free(req, M_DEVBUF, sizeof(*req)); + if (ret) { + printf("%s: failed to send respond memory request: %d\n", + sc->sc_dev.dv_xname, ret); + return -1; + } + + if (mem_seg_len == 0) { + expected_result = QMI_RESULT_FAILURE_V01; + sc->qmi_resp.result = QMI_RESULT_SUCCESS_V01; + } else { + expected_result = QMI_RESULT_SUCCESS_V01; + sc->qmi_resp.result = QMI_RESULT_FAILURE_V01; + } + while (sc->qmi_resp.result != expected_result) { + ret = tsleep_nsec(&sc->qmi_resp, 0, "qwzfwrespmem", + SEC_TO_NSEC(1)); + if (ret) { + printf("%s: fw respond memory request timeout\n", + sc->sc_dev.dv_xname); + return -1; + } + } + + if (mem_seg_len == 0) { + sc->expect_fwmem_req = 1; + return EBUSY; /* retry */ + } + + if (!sc->hw_params.fixed_fw_mem) { + while (!sc->fwmem_ready) { + ret = tsleep_nsec(&sc->fwmem_ready, 0, "qwzfwrdy", + SEC_TO_NSEC(10)); + if (ret) { + printf("%s: fw memory ready timeout\n", + sc->sc_dev.dv_xname); + return -1; + } + } + } + + return 0; +} + +int +qwz_core_check_smbios(struct qwz_softc *sc) +{ + return 0; /* TODO */ +} + +int +qwz_core_check_dt(struct qwz_softc *sc) +{ +#ifdef __HAVE_FDT + if (sc->sc_node == 0) + return 0; + + OF_getprop(sc->sc_node, "qcom,ath12k-calibration-variant", + sc->qmi_target.bdf_ext, sizeof(sc->qmi_target.bdf_ext) - 1); +#endif + + return 0; +} + +int +qwz_qmi_request_target_cap(struct qwz_softc *sc) +{ + struct qmi_wlanfw_cap_req_msg_v01 req; + int ret = 0; + int r; + char *fw_build_id; + int fw_build_id_mask_len; + + memset(&req, 0, sizeof(req)); + + ret = qwz_qmi_send_request(sc, QMI_WLANFW_CAP_REQ_V01, + QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_cap_req_msg_v01_ei, &req, sizeof(req)); + if (ret) { + printf("%s: failed to send qmi cap request: %d\n", + sc->sc_dev.dv_xname, ret); + goto out; + } + + sc->qmi_resp.result = QMI_RESULT_FAILURE_V01; + while (sc->qmi_resp.result != QMI_RESULT_SUCCESS_V01) { + ret = tsleep_nsec(&sc->qmi_resp, 0, "qwzfwcap", + SEC_TO_NSEC(1)); + if (ret) { + printf("%s: qmi cap request failed\n", + sc->sc_dev.dv_xname); + return ret; + } + } + + fw_build_id = sc->qmi_target.fw_build_id; + fw_build_id_mask_len = strlen(QWZ_FW_BUILD_ID_MASK); + if (!strncmp(fw_build_id, QWZ_FW_BUILD_ID_MASK, fw_build_id_mask_len)) + fw_build_id = fw_build_id + fw_build_id_mask_len; + + DPRINTF("%s: chip_id 0x%x chip_family 0x%x board_id 0x%x soc_id 0x%x\n", + sc->sc_dev.dv_xname, + sc->qmi_target.chip_id, sc->qmi_target.chip_family, + sc->qmi_target.board_id, sc->qmi_target.soc_id); + + DPRINTF("%s: fw_version 0x%x fw_build_timestamp %s fw_build_id %s\n", + sc->sc_dev.dv_xname, sc->qmi_target.fw_version, + sc->qmi_target.fw_build_timestamp, fw_build_id); + + r = qwz_core_check_smbios(sc); + if (r) + DPRINTF("%s: SMBIOS bdf variant name not set\n", __func__); + + r = qwz_core_check_dt(sc); + if (r) + DPRINTF("%s: DT bdf variant name not set\n", __func__); + +out: + return ret; +} + +int +qwz_qmi_request_device_info(struct qwz_softc *sc) +{ + /* device info message req is only sent for hybrid bus devices */ + if (!sc->hw_params.hybrid_bus_type) + return 0; + + /* TODO */ + return -1; +} + +int +_qwz_core_create_board_name(struct qwz_softc *sc, char *name, + size_t name_len, int with_variant, int bus_type_mode) +{ + /* strlen(',variant=') + strlen(ab->qmi.target.bdf_ext) */ + char variant[9 + ATH12K_QMI_BDF_EXT_STR_LENGTH] = { 0 }; + + if (with_variant && sc->qmi_target.bdf_ext[0] != '\0') + snprintf(variant, sizeof(variant), ",variant=%s", + sc->qmi_target.bdf_ext); + + switch (sc->id.bdf_search) { + case ATH12K_BDF_SEARCH_BUS_AND_BOARD: + if (bus_type_mode) + snprintf(name, name_len, "bus=%s", sc->sc_bus_str); + else + snprintf(name, name_len, + "bus=%s,vendor=%04x,device=%04x," + "subsystem-vendor=%04x,subsystem-device=%04x," + "qmi-chip-id=%d,qmi-board-id=%d%s", + sc->sc_bus_str, sc->id.vendor, sc->id.device, + sc->id.subsystem_vendor, sc->id.subsystem_device, + sc->qmi_target.chip_id, sc->qmi_target.board_id, + variant); + break; + default: + snprintf(name, name_len, + "bus=%s,qmi-chip-id=%d,qmi-board-id=%d%s", + sc->sc_bus_str, sc->qmi_target.chip_id, + sc->qmi_target.board_id, variant); + break; + } + + DPRINTF("%s: using board name '%s'\n", __func__, name); + + return 0; +} + +int +qwz_core_create_board_name(struct qwz_softc *sc, char *name, size_t name_len) +{ + return _qwz_core_create_board_name(sc, name, name_len, 1, 0); +} + +int +qwz_core_create_fallback_board_name(struct qwz_softc *sc, char *name, + size_t name_len) +{ + return _qwz_core_create_board_name(sc, name, name_len, 0, 0); +} + +int +qwz_core_create_bus_type_board_name(struct qwz_softc *sc, char *name, + size_t name_len) +{ + return _qwz_core_create_board_name(sc, name, name_len, 0, 1); +} + +struct ath12k_fw_ie { + uint32_t id; + uint32_t len; + uint8_t data[]; +}; + +enum ath12k_bd_ie_board_type { + ATH12K_BD_IE_BOARD_NAME = 0, + ATH12K_BD_IE_BOARD_DATA = 1, +}; + +enum ath12k_bd_ie_regdb_type { + ATH12K_BD_IE_REGDB_NAME = 0, + ATH12K_BD_IE_REGDB_DATA = 1, +}; + +enum ath12k_bd_ie_type { + /* contains sub IEs of enum ath12k_bd_ie_board_type */ + ATH12K_BD_IE_BOARD = 0, + /* contains sub IEs of enum ath12k_bd_ie_regdb_type */ + ATH12K_BD_IE_REGDB = 1, +}; + +static inline const char * +qwz_bd_ie_type_str(enum ath12k_bd_ie_type type) +{ + switch (type) { + case ATH12K_BD_IE_BOARD: + return "board data"; + case ATH12K_BD_IE_REGDB: + return "regdb data"; + } + + return "unknown"; +} + +int +qwz_core_parse_bd_ie_board(struct qwz_softc *sc, + const u_char **boardfw, size_t *boardfw_len, + const void *buf, size_t buf_len, + const char *boardname, int ie_id, int name_id, int data_id) +{ + const struct ath12k_fw_ie *hdr; + int name_match_found = 0; + int ret, board_ie_id; + size_t board_ie_len; + const void *board_ie_data; + + *boardfw = NULL; + *boardfw_len = 0; + + /* go through ATH12K_BD_IE_BOARD_/ATH12K_BD_IE_REGDB_ elements */ + while (buf_len > sizeof(struct ath12k_fw_ie)) { + hdr = buf; + board_ie_id = le32toh(hdr->id); + board_ie_len = le32toh(hdr->len); + board_ie_data = hdr->data; + + buf_len -= sizeof(*hdr); + buf += sizeof(*hdr); + + if (buf_len < roundup(board_ie_len, 4)) { + printf("%s: invalid %s length: %zu < %zu\n", + sc->sc_dev.dv_xname, qwz_bd_ie_type_str(ie_id), + buf_len, roundup(board_ie_len, 4)); + return EINVAL; + } + + if (board_ie_id == name_id) { + if (board_ie_len != strlen(boardname)) + goto next; + + ret = memcmp(board_ie_data, boardname, board_ie_len); + if (ret) + goto next; + + name_match_found = 1; + DPRINTF("%s: found match %s for name '%s'", __func__, + qwz_bd_ie_type_str(ie_id), boardname); + } else if (board_ie_id == data_id) { + if (!name_match_found) + /* no match found */ + goto next; + + DPRINTF("%s: found %s for '%s'", __func__, + qwz_bd_ie_type_str(ie_id), boardname); + + *boardfw = board_ie_data; + *boardfw_len = board_ie_len; + return 0; + } else { + printf("%s: unknown %s id found: %d\n", __func__, + qwz_bd_ie_type_str(ie_id), board_ie_id); + } +next: + /* jump over the padding */ + board_ie_len = roundup(board_ie_len, 4); + + buf_len -= board_ie_len; + buf += board_ie_len; + } + + /* no match found */ + return ENOENT; +} + +int +qwz_core_fetch_board_data_api_n(struct qwz_softc *sc, + const u_char **boardfw, size_t *boardfw_len, + u_char *fwdata, size_t fwdata_len, + const char *boardname, int ie_id_match, int name_id, int data_id) +{ + size_t len, magic_len; + const uint8_t *data; + char *filename; + size_t ie_len; + struct ath12k_fw_ie *hdr; + int ret, ie_id; + + filename = ATH12K_BOARD_API2_FILE; + + *boardfw = NULL; + *boardfw_len = 0; + + data = fwdata; + len = fwdata_len; + + /* magic has extra null byte padded */ + magic_len = strlen(ATH12K_BOARD_MAGIC) + 1; + if (len < magic_len) { + printf("%s: failed to find magic value in %s, " + "file too short: %zu\n", + sc->sc_dev.dv_xname, filename, len); + return EINVAL; + } + + if (memcmp(data, ATH12K_BOARD_MAGIC, magic_len)) { + DPRINTF("%s: found invalid board magic\n", sc->sc_dev.dv_xname); + return EINVAL; + } + + /* magic is padded to 4 bytes */ + magic_len = roundup(magic_len, 4); + if (len < magic_len) { + printf("%s: %s too small to contain board data, len: %zu\n", + sc->sc_dev.dv_xname, filename, len); + return EINVAL; + } + + data += magic_len; + len -= magic_len; + + while (len > sizeof(struct ath12k_fw_ie)) { + hdr = (struct ath12k_fw_ie *)data; + ie_id = le32toh(hdr->id); + ie_len = le32toh(hdr->len); + + len -= sizeof(*hdr); + data = hdr->data; + + if (len < roundup(ie_len, 4)) { + printf("%s: invalid length for board ie_id %d " + "ie_len %zu len %zu\n", + sc->sc_dev.dv_xname, ie_id, ie_len, len); + return EINVAL; + } + + if (ie_id == ie_id_match) { + ret = qwz_core_parse_bd_ie_board(sc, + boardfw, boardfw_len, data, ie_len, + boardname, ie_id_match, name_id, data_id); + if (ret == ENOENT) + /* no match found, continue */ + goto next; + else if (ret) + /* there was an error, bail out */ + return ret; + /* either found or error, so stop searching */ + goto out; + } +next: + /* jump over the padding */ + ie_len = roundup(ie_len, 4); + + len -= ie_len; + data += ie_len; + } + +out: + if (!*boardfw || !*boardfw_len) { + printf("%s: failed to fetch %s for %s from %s\n", + __func__, qwz_bd_ie_type_str(ie_id_match), + boardname, filename); + return ENOENT; + } + + return 0; +} + +int +qwz_core_fetch_bdf(struct qwz_softc *sc, u_char **data, size_t *len, + const u_char **boardfw, size_t *boardfw_len, const char *filename) +{ + char path[PATH_MAX]; + char boardname[200]; + int ret; + + ret = snprintf(path, sizeof(path), "%s-%s-%s", + ATH12K_FW_DIR, sc->hw_params.fw.dir, filename); + if (ret < 0 || ret >= sizeof(path)) + return ENOSPC; + + ret = qwz_core_create_board_name(sc, boardname, sizeof(boardname)); + if (ret) { + DPRINTF("%s: failed to create board name: %d", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = loadfirmware(path, data, len); + if (ret) { + printf("%s: could not read %s (error %d)\n", + sc->sc_dev.dv_xname, path, ret); + return ret; + } + + ret = qwz_core_fetch_board_data_api_n(sc, boardfw, boardfw_len, + *data, *len, boardname, ATH12K_BD_IE_BOARD, + ATH12K_BD_IE_BOARD_NAME, ATH12K_BD_IE_BOARD_DATA); + if (ret) { + DPRINTF("%s: failed to fetch board data for %s from %s\n", + sc->sc_dev.dv_xname, boardname, path); + return ret; + } + + return 0; +} + +int +qwz_qmi_load_file_target_mem(struct qwz_softc *sc, const u_char *data, + size_t len, int type) +{ + struct qmi_wlanfw_bdf_download_req_msg_v01 *req; + const uint8_t *p = data; +#ifdef notyet + void *bdf_addr = NULL; +#endif + int ret = EINVAL; /* empty fw image */ + uint32_t remaining = len; + + req = malloc(sizeof(*req), M_DEVBUF, M_NOWAIT | M_ZERO); + if (!req) { + printf("%s: failed to allocate bfd download request\n", + sc->sc_dev.dv_xname); + return ENOMEM; + } + + if (sc->hw_params.fixed_bdf_addr) { +#ifdef notyet + bdf_addr = ioremap(ab->hw_params.bdf_addr, ab->hw_params.fw.board_size); + if (!bdf_addr) { + ath12k_warn(ab, "qmi ioremap error for bdf_addr\n"); + ret = -EIO; + goto err_free_req; + } +#else + printf("%s: fixed bdf address not yet supported\n", + sc->sc_dev.dv_xname); + ret = EIO; + goto err_free_req; +#endif + } + + while (remaining) { + req->valid = 1; + req->file_id_valid = 1; + req->file_id = sc->qmi_target.board_id; + req->total_size_valid = 1; + req->total_size = remaining; + req->seg_id_valid = 1; + req->data_valid = 1; + req->bdf_type = type; + req->bdf_type_valid = 1; + req->end_valid = 1; + req->end = 0; + + if (remaining > QMI_WLANFW_MAX_DATA_SIZE_V01) { + req->data_len = QMI_WLANFW_MAX_DATA_SIZE_V01; + } else { + req->data_len = remaining; + req->end = 1; + } + + if (sc->hw_params.fixed_bdf_addr || + type == ATH12K_QMI_FILE_TYPE_EEPROM) { + req->data_valid = 0; + req->end = 1; + req->data_len = ATH12K_QMI_MAX_BDF_FILE_NAME_SIZE; + } else { + memcpy(req->data, p, req->data_len); + } +#ifdef notyet + if (ab->hw_params.fixed_bdf_addr) { + if (type == ATH12K_QMI_FILE_TYPE_CALDATA) + bdf_addr += ab->hw_params.fw.cal_offset; + + memcpy_toio(bdf_addr, p, len); + } +#endif + DPRINTF("%s: bdf download req fixed addr type %d\n", + __func__, type); + + ret = qwz_qmi_send_request(sc, + QMI_WLANFW_BDF_DOWNLOAD_REQ_V01, + QMI_WLANFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_bdf_download_req_msg_v01_ei, + req, sizeof(*req)); + if (ret) { + printf("%s: failed to send bdf download request\n", + sc->sc_dev.dv_xname); + goto err_iounmap; + } + + sc->qmi_resp.result = QMI_RESULT_FAILURE_V01; + while (sc->qmi_resp.result != QMI_RESULT_SUCCESS_V01) { + ret = tsleep_nsec(&sc->qmi_resp, 0, "qwzbdf", + SEC_TO_NSEC(1)); + if (ret) { + printf("%s: bdf download request timeout\n", + sc->sc_dev.dv_xname); + goto err_iounmap; + } + } + + if (sc->hw_params.fixed_bdf_addr || + type == ATH12K_QMI_FILE_TYPE_EEPROM) { + remaining = 0; + } else { + remaining -= req->data_len; + p += req->data_len; + req->seg_id++; + DPRINTF("%s: bdf download request remaining %i\n", + __func__, remaining); + } + } + +err_iounmap: +#ifdef notyet + if (ab->hw_params.fixed_bdf_addr) + iounmap(bdf_addr); +#endif +err_free_req: + free(req, M_DEVBUF, sizeof(*req)); + + return ret; +} + +#define QWZ_ELFMAG "\177ELF" +#define QWZ_SELFMAG 4 + +int +qwz_qmi_load_bdf_qmi(struct qwz_softc *sc, int regdb) +{ + u_char *data = NULL; + const u_char *boardfw; + size_t len = 0, boardfw_len; + uint32_t fw_size; + int ret = 0, bdf_type; +#ifdef notyet + const uint8_t *tmp; + uint32_t file_type; +#endif + int fw_idx = regdb ? QWZ_FW_REGDB : QWZ_FW_BOARD; + + if (sc->fw_img[fw_idx].data) { + boardfw = sc->fw_img[fw_idx].data; + boardfw_len = sc->fw_img[fw_idx].size; + } else { + ret = qwz_core_fetch_bdf(sc, &data, &len, + &boardfw, &boardfw_len, + regdb ? ATH12K_REGDB_FILE : ATH12K_BOARD_API2_FILE); + if (ret) + return ret; + + sc->fw_img[fw_idx].data = malloc(boardfw_len, M_DEVBUF, + M_NOWAIT); + if (sc->fw_img[fw_idx].data) { + memcpy(sc->fw_img[fw_idx].data, boardfw, boardfw_len); + sc->fw_img[fw_idx].size = boardfw_len; + } + } + + if (regdb) + bdf_type = ATH12K_QMI_BDF_TYPE_REGDB; + else if (boardfw_len >= QWZ_SELFMAG && + memcmp(boardfw, QWZ_ELFMAG, QWZ_SELFMAG) == 0) + bdf_type = ATH12K_QMI_BDF_TYPE_ELF; + else + bdf_type = ATH12K_QMI_BDF_TYPE_BIN; + + DPRINTF("%s: bdf_type %d\n", __func__, bdf_type); + + fw_size = MIN(sc->hw_params.fw.board_size, boardfw_len); + + ret = qwz_qmi_load_file_target_mem(sc, boardfw, fw_size, bdf_type); + if (ret) { + printf("%s: failed to load bdf file\n", __func__); + goto out; + } + + /* QCA6390/WCN6855 does not support cal data, skip it */ + if (bdf_type == ATH12K_QMI_BDF_TYPE_ELF || bdf_type == ATH12K_QMI_BDF_TYPE_REGDB) + goto out; +#ifdef notyet + if (ab->qmi.target.eeprom_caldata) { + file_type = ATH12K_QMI_FILE_TYPE_EEPROM; + tmp = filename; + fw_size = ATH12K_QMI_MAX_BDF_FILE_NAME_SIZE; + } else { + file_type = ATH12K_QMI_FILE_TYPE_CALDATA; + + /* cal--.bin */ + snprintf(filename, sizeof(filename), "cal-%s-%s.bin", + ath12k_bus_str(ab->hif.bus), dev_name(dev)); + fw_entry = ath12k_core_firmware_request(ab, filename); + if (!IS_ERR(fw_entry)) + goto success; + + fw_entry = ath12k_core_firmware_request(ab, ATH12K_DEFAULT_CAL_FILE); + if (IS_ERR(fw_entry)) { + /* Caldata may not be present during first time calibration in + * factory hence allow to boot without loading caldata in ftm mode + */ + if (ath12k_ftm_mode) { + ath12k_info(ab, + "Booting without cal data file in factory test mode\n"); + return 0; + } + ret = PTR_ERR(fw_entry); + ath12k_warn(ab, + "qmi failed to load CAL data file:%s\n", + filename); + goto out; + } +success: + fw_size = MIN(ab->hw_params.fw.board_size, fw_entry->size); + tmp = fw_entry->data; + } + + ret = ath12k_qmi_load_file_target_mem(ab, tmp, fw_size, file_type); + if (ret < 0) { + ath12k_warn(ab, "qmi failed to load caldata\n"); + goto out_qmi_cal; + } + + ath12k_dbg(ab, ATH12K_DBG_QMI, "caldata type: %u\n", file_type); + +out_qmi_cal: + if (!ab->qmi.target.eeprom_caldata) + release_firmware(fw_entry); +#endif +out: + free(data, M_DEVBUF, len); + if (ret == 0) + DPRINTF("%s: BDF download sequence completed\n", __func__); + + return ret; +} + +int +qwz_qmi_event_load_bdf(struct qwz_softc *sc) +{ + int ret; + + ret = qwz_qmi_request_target_cap(sc); + if (ret < 0) { + printf("%s: failed to request qmi target capabilities: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = qwz_qmi_request_device_info(sc); + if (ret < 0) { + printf("%s: failed to request qmi device info: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + if (sc->hw_params.supports_regdb) + qwz_qmi_load_bdf_qmi(sc, 1); + + ret = qwz_qmi_load_bdf_qmi(sc, 0); + if (ret < 0) { + printf("%s: failed to load board data file: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + return 0; +} + +int +qwz_qmi_m3_load(struct qwz_softc *sc) +{ + u_char *data; + size_t len; + char path[PATH_MAX]; + int ret; + + if (sc->fw_img[QWZ_FW_M3].data) { + data = sc->fw_img[QWZ_FW_M3].data; + len = sc->fw_img[QWZ_FW_M3].size; + } else { + ret = snprintf(path, sizeof(path), "%s-%s-%s", + ATH12K_FW_DIR, sc->hw_params.fw.dir, ATH12K_M3_FILE); + if (ret < 0 || ret >= sizeof(path)) + return ENOSPC; + + ret = loadfirmware(path, &data, &len); + if (ret) { + printf("%s: could not read %s (error %d)\n", + sc->sc_dev.dv_xname, path, ret); + return ret; + } + + sc->fw_img[QWZ_FW_M3].data = data; + sc->fw_img[QWZ_FW_M3].size = len; + } + + if (sc->m3_mem == NULL || QWZ_DMA_LEN(sc->m3_mem) < len) { + if (sc->m3_mem) + qwz_dmamem_free(sc->sc_dmat, sc->m3_mem); + sc->m3_mem = qwz_dmamem_alloc(sc->sc_dmat, len, 65536); + if (sc->m3_mem == NULL) { + printf("%s: failed to allocate %zu bytes of DMA " + "memory for M3 firmware\n", sc->sc_dev.dv_xname, + len); + return ENOMEM; + } + } + + memcpy(QWZ_DMA_KVA(sc->m3_mem), data, len); + return 0; +} + +int +qwz_qmi_wlanfw_m3_info_send(struct qwz_softc *sc) +{ + struct qmi_wlanfw_m3_info_req_msg_v01 req; + int ret = 0; + uint64_t paddr; + uint32_t size; + + memset(&req, 0, sizeof(req)); + + if (sc->hw_params.m3_fw_support) { + ret = qwz_qmi_m3_load(sc); + if (ret) { + printf("%s: failed to load m3 firmware: %d", + sc->sc_dev.dv_xname, ret); + return ret; + } + + paddr = QWZ_DMA_DVA(sc->m3_mem); + size = QWZ_DMA_LEN(sc->m3_mem); + req.addr = htole64(paddr); + req.size = htole32(size); + } else { + req.addr = 0; + req.size = 0; + } + + ret = qwz_qmi_send_request(sc, QMI_WLANFW_M3_INFO_REQ_V01, + QMI_WLANFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN, + qmi_wlanfw_m3_info_req_msg_v01_ei, &req, sizeof(req)); + if (ret) { + printf("%s: failed to send m3 information request: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + sc->qmi_resp.result = QMI_RESULT_FAILURE_V01; + while (sc->qmi_resp.result != QMI_RESULT_SUCCESS_V01) { + ret = tsleep_nsec(&sc->qmi_resp, 0, "qwzfwm3", + SEC_TO_NSEC(1)); + if (ret) { + printf("%s: m3 information request timeout\n", + sc->sc_dev.dv_xname); + return ret; + } + } + + return 0; +} + +void +qwz_hal_dump_srng_stats(struct qwz_softc *sc) +{ + DPRINTF("%s not implemented\n", __func__); +} + +uint16_t +qwz_hal_srng_get_entrysize(struct qwz_softc *sc, uint32_t ring_type) +{ + struct hal_srng_config *srng_config; + + KASSERT(ring_type < HAL_MAX_RING_TYPES); + + srng_config = &sc->hal.srng_config[ring_type]; + return (srng_config->entry_size << 2); +} + +uint32_t +qwz_hal_srng_get_max_entries(struct qwz_softc *sc, uint32_t ring_type) +{ + struct hal_srng_config *srng_config; + + KASSERT(ring_type < HAL_MAX_RING_TYPES); + + srng_config = &sc->hal.srng_config[ring_type]; + return (srng_config->max_size / srng_config->entry_size); +} + +uint32_t * +qwz_hal_srng_dst_get_next_entry(struct qwz_softc *sc, struct hal_srng *srng) +{ + uint32_t *desc; +#ifdef notyet + lockdep_assert_held(&srng->lock); +#endif + if (srng->u.dst_ring.tp == srng->u.dst_ring.cached_hp) + return NULL; + + desc = srng->ring_base_vaddr + srng->u.dst_ring.tp; + + srng->u.dst_ring.tp += srng->entry_size; + + /* wrap around to start of ring*/ + if (srng->u.dst_ring.tp == srng->ring_size) + srng->u.dst_ring.tp = 0; +#ifdef notyet + /* Try to prefetch the next descriptor in the ring */ + if (srng->flags & HAL_SRNG_FLAGS_CACHED) + ath12k_hal_srng_prefetch_desc(ab, srng); +#endif + return desc; +} + +int +qwz_hal_srng_dst_num_free(struct qwz_softc *sc, struct hal_srng *srng, + int sync_hw_ptr) +{ + uint32_t tp, hp; +#ifdef notyet + lockdep_assert_held(&srng->lock); +#endif + tp = srng->u.dst_ring.tp; + + if (sync_hw_ptr) { + hp = *srng->u.dst_ring.hp_addr; + srng->u.dst_ring.cached_hp = hp; + } else { + hp = srng->u.dst_ring.cached_hp; + } + + if (hp >= tp) + return (hp - tp) / srng->entry_size; + else + return (srng->ring_size - tp + hp) / srng->entry_size; +} + +uint32_t * +qwz_hal_srng_src_get_next_reaped(struct qwz_softc *sc, struct hal_srng *srng) +{ + uint32_t *desc; +#ifdef notyet + lockdep_assert_held(&srng->lock); +#endif + if (srng->u.src_ring.hp == srng->u.src_ring.reap_hp) + return NULL; + + desc = srng->ring_base_vaddr + srng->u.src_ring.hp; + srng->u.src_ring.hp = (srng->u.src_ring.hp + srng->entry_size) % + srng->ring_size; + + return desc; +} + +uint32_t * +qwz_hal_srng_src_peek(struct qwz_softc *sc, struct hal_srng *srng) +{ +#ifdef notyet + lockdep_assert_held(&srng->lock); +#endif + if (((srng->u.src_ring.hp + srng->entry_size) % srng->ring_size) == + srng->u.src_ring.cached_tp) + return NULL; + + return srng->ring_base_vaddr + srng->u.src_ring.hp; +} + +void +qwz_get_msi_address(struct qwz_softc *sc, uint32_t *addr_lo, + uint32_t *addr_hi) +{ + *addr_lo = sc->msi_addr_lo; + *addr_hi = sc->msi_addr_hi; +} + +int +qwz_dp_srng_find_ring_in_mask(int ring_num, const uint8_t *grp_mask) +{ + int ext_group_num; + uint8_t mask = 1 << ring_num; + + for (ext_group_num = 0; ext_group_num < ATH12K_EXT_IRQ_GRP_NUM_MAX; + ext_group_num++) { + if (mask & grp_mask[ext_group_num]) + return ext_group_num; + } + + return -1; +} + +int +qwz_dp_srng_calculate_msi_group(struct qwz_softc *sc, enum hal_ring_type type, + int ring_num) +{ + const uint8_t *grp_mask; + + switch (type) { + case HAL_WBM2SW_RELEASE: + if (ring_num == DP_RX_RELEASE_RING_NUM) { + grp_mask = &sc->hw_params.ring_mask->rx_wbm_rel[0]; + ring_num = 0; + } else { + grp_mask = &sc->hw_params.ring_mask->tx[0]; + } + break; + case HAL_REO_EXCEPTION: + grp_mask = &sc->hw_params.ring_mask->rx_err[0]; + break; + case HAL_REO_DST: + grp_mask = &sc->hw_params.ring_mask->rx[0]; + break; + case HAL_REO_STATUS: + grp_mask = &sc->hw_params.ring_mask->reo_status[0]; + break; + case HAL_RXDMA_MONITOR_STATUS: + case HAL_RXDMA_MONITOR_DST: + grp_mask = &sc->hw_params.ring_mask->rx_mon_status[0]; + break; + case HAL_RXDMA_DST: + grp_mask = &sc->hw_params.ring_mask->rxdma2host[0]; + break; + case HAL_RXDMA_BUF: + grp_mask = &sc->hw_params.ring_mask->host2rxdma[0]; + break; + case HAL_RXDMA_MONITOR_BUF: + case HAL_TCL_DATA: + case HAL_TCL_CMD: + case HAL_REO_CMD: + case HAL_SW2WBM_RELEASE: + case HAL_WBM_IDLE_LINK: + case HAL_TCL_STATUS: + case HAL_REO_REINJECT: + case HAL_CE_SRC: + case HAL_CE_DST: + case HAL_CE_DST_STATUS: + default: + return -1; + } + + return qwz_dp_srng_find_ring_in_mask(ring_num, grp_mask); +} + +void +qwz_dp_srng_msi_setup(struct qwz_softc *sc, struct hal_srng_params *ring_params, + enum hal_ring_type type, int ring_num) +{ + int msi_group_number; + uint32_t msi_data_start = 0; + uint32_t msi_data_count = 1; + uint32_t msi_irq_start = 0; + uint32_t addr_lo; + uint32_t addr_hi; + int ret; + + ret = sc->ops.get_user_msi_vector(sc, "DP", + &msi_data_count, &msi_data_start, &msi_irq_start); + if (ret) + return; + + msi_group_number = qwz_dp_srng_calculate_msi_group(sc, type, + ring_num); + if (msi_group_number < 0) { + DPRINTF("%s ring not part of an ext_group; ring_type %d," + "ring_num %d\n", __func__, type, ring_num); + ring_params->msi_addr = 0; + ring_params->msi_data = 0; + return; + } + + qwz_get_msi_address(sc, &addr_lo, &addr_hi); + + ring_params->msi_addr = addr_lo; + ring_params->msi_addr |= (((uint64_t)addr_hi) << 32); + ring_params->msi_data = (msi_group_number % msi_data_count) + + msi_data_start; + ring_params->flags |= HAL_SRNG_FLAGS_MSI_INTR; +} + +int +qwz_dp_srng_setup(struct qwz_softc *sc, struct dp_srng *ring, + enum hal_ring_type type, int ring_num, int mac_id, int num_entries) +{ + struct hal_srng_params params = { 0 }; + uint16_t entry_sz = qwz_hal_srng_get_entrysize(sc, type); + uint32_t max_entries = qwz_hal_srng_get_max_entries(sc, type); + int ret; + int cached = 0; + + if (num_entries > max_entries) + num_entries = max_entries; + + ring->size = (num_entries * entry_sz) + HAL_RING_BASE_ALIGN - 1; + +#ifdef notyet + if (sc->hw_params.alloc_cacheable_memory) { + /* Allocate the reo dst and tx completion rings from cacheable memory */ + switch (type) { + case HAL_REO_DST: + case HAL_WBM2SW_RELEASE: + cached = true; + break; + default: + cached = false; + } + + if (cached) { + ring->vaddr_unaligned = kzalloc(ring->size, GFP_KERNEL); + ring->paddr_unaligned = virt_to_phys(ring->vaddr_unaligned); + } + if (!ring->vaddr_unaligned) + return -ENOMEM; + } +#endif + if (!cached) { + ring->mem = qwz_dmamem_alloc(sc->sc_dmat, ring->size, + PAGE_SIZE); + if (ring->mem == NULL) { + printf("%s: could not allocate DP SRNG DMA memory\n", + sc->sc_dev.dv_xname); + return ENOMEM; + + } + } + + ring->vaddr = QWZ_DMA_KVA(ring->mem); + ring->paddr = QWZ_DMA_DVA(ring->mem); + + params.ring_base_vaddr = ring->vaddr; + params.ring_base_paddr = ring->paddr; + params.num_entries = num_entries; + qwz_dp_srng_msi_setup(sc, ¶ms, type, ring_num + mac_id); + + switch (type) { + case HAL_REO_DST: + params.intr_batch_cntr_thres_entries = + HAL_SRNG_INT_BATCH_THRESHOLD_RX; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX; + break; + case HAL_RXDMA_BUF: + case HAL_RXDMA_MONITOR_BUF: + case HAL_RXDMA_MONITOR_STATUS: + params.low_threshold = num_entries >> 3; + params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; + params.intr_batch_cntr_thres_entries = 0; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX; + break; + case HAL_WBM2SW_RELEASE: + if (ring_num < 3) { + params.intr_batch_cntr_thres_entries = + HAL_SRNG_INT_BATCH_THRESHOLD_TX; + params.intr_timer_thres_us = + HAL_SRNG_INT_TIMER_THRESHOLD_TX; + break; + } + /* follow through when ring_num >= 3 */ + /* FALLTHROUGH */ + case HAL_REO_EXCEPTION: + case HAL_REO_REINJECT: + case HAL_REO_CMD: + case HAL_REO_STATUS: + case HAL_TCL_DATA: + case HAL_TCL_CMD: + case HAL_TCL_STATUS: + case HAL_WBM_IDLE_LINK: + case HAL_SW2WBM_RELEASE: + case HAL_RXDMA_DST: + case HAL_RXDMA_MONITOR_DST: + case HAL_RXDMA_MONITOR_DESC: + params.intr_batch_cntr_thres_entries = + HAL_SRNG_INT_BATCH_THRESHOLD_OTHER; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_OTHER; + break; + case HAL_RXDMA_DIR_BUF: + break; + default: + printf("%s: Not a valid ring type in dp :%d\n", + sc->sc_dev.dv_xname, type); + return EINVAL; + } + + if (cached) { + params.flags |= HAL_SRNG_FLAGS_CACHED; + ring->cached = 1; + } + + ret = qwz_hal_srng_setup(sc, type, ring_num, mac_id, ¶ms); + if (ret < 0) { + printf("%s: failed to setup srng: %d ring_id %d\n", + sc->sc_dev.dv_xname, ret, ring_num); + return ret; + } + + ring->ring_id = ret; + return 0; +} + +void +qwz_hal_srng_access_begin(struct qwz_softc *sc, struct hal_srng *srng) +{ +#ifdef notyet + lockdep_assert_held(&srng->lock); +#endif + if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + srng->u.src_ring.cached_tp = + *(volatile uint32_t *)srng->u.src_ring.tp_addr; + } else { + srng->u.dst_ring.cached_hp = *srng->u.dst_ring.hp_addr; + } +} + +void +qwz_hal_srng_access_end(struct qwz_softc *sc, struct hal_srng *srng) +{ +#ifdef notyet + lockdep_assert_held(&srng->lock); +#endif + /* TODO: See if we need a write memory barrier here */ + if (srng->flags & HAL_SRNG_FLAGS_LMAC_RING) { + /* For LMAC rings, ring pointer updates are done through FW and + * hence written to a shared memory location that is read by FW + */ + if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + srng->u.src_ring.last_tp = + *(volatile uint32_t *)srng->u.src_ring.tp_addr; + *srng->u.src_ring.hp_addr = srng->u.src_ring.hp; + } else { + srng->u.dst_ring.last_hp = *srng->u.dst_ring.hp_addr; + *srng->u.dst_ring.tp_addr = srng->u.dst_ring.tp; + } + } else { + if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + srng->u.src_ring.last_tp = + *(volatile uint32_t *)srng->u.src_ring.tp_addr; + sc->ops.write32(sc, + (unsigned long)srng->u.src_ring.hp_addr - + (unsigned long)sc->mem, srng->u.src_ring.hp); + } else { + srng->u.dst_ring.last_hp = *srng->u.dst_ring.hp_addr; + sc->ops.write32(sc, + (unsigned long)srng->u.dst_ring.tp_addr - + (unsigned long)sc->mem, srng->u.dst_ring.tp); + } + } +#ifdef notyet + srng->timestamp = jiffies; +#endif +} + +int +qwz_wbm_idle_ring_setup(struct qwz_softc *sc, uint32_t *n_link_desc) +{ + struct qwz_dp *dp = &sc->dp; + uint32_t n_mpdu_link_desc, n_mpdu_queue_desc; + uint32_t n_tx_msdu_link_desc, n_rx_msdu_link_desc; + int ret = 0; + + n_mpdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_MPDUS_PER_TID_MAX) / + HAL_NUM_MPDUS_PER_LINK_DESC; + + n_mpdu_queue_desc = n_mpdu_link_desc / + HAL_NUM_MPDU_LINKS_PER_QUEUE_DESC; + + n_tx_msdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_FLOWS_PER_TID * + DP_AVG_MSDUS_PER_FLOW) / + HAL_NUM_TX_MSDUS_PER_LINK_DESC; + + n_rx_msdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_MPDUS_PER_TID_MAX * + DP_AVG_MSDUS_PER_MPDU) / + HAL_NUM_RX_MSDUS_PER_LINK_DESC; + + *n_link_desc = n_mpdu_link_desc + n_mpdu_queue_desc + + n_tx_msdu_link_desc + n_rx_msdu_link_desc; + + if (*n_link_desc & (*n_link_desc - 1)) + *n_link_desc = 1 << fls(*n_link_desc); + + ret = qwz_dp_srng_setup(sc, &dp->wbm_idle_ring, + HAL_WBM_IDLE_LINK, 0, 0, *n_link_desc); + if (ret) { + printf("%s: failed to setup wbm_idle_ring: %d\n", + sc->sc_dev.dv_xname, ret); + } + + return ret; +} + +void +qwz_dp_link_desc_bank_free(struct qwz_softc *sc, + struct dp_link_desc_bank *link_desc_banks) +{ + int i; + + for (i = 0; i < DP_LINK_DESC_BANKS_MAX; i++) { + if (link_desc_banks[i].mem) { + qwz_dmamem_free(sc->sc_dmat, link_desc_banks[i].mem); + link_desc_banks[i].mem = NULL; + } + } +} + +int +qwz_dp_link_desc_bank_alloc(struct qwz_softc *sc, + struct dp_link_desc_bank *desc_bank, int n_link_desc_bank, + int last_bank_sz) +{ + struct qwz_dp *dp = &sc->dp; + int i; + int ret = 0; + int desc_sz = DP_LINK_DESC_ALLOC_SIZE_THRESH; + + for (i = 0; i < n_link_desc_bank; i++) { + if (i == (n_link_desc_bank - 1) && last_bank_sz) + desc_sz = last_bank_sz; + + desc_bank[i].mem = qwz_dmamem_alloc(sc->sc_dmat, desc_sz, + PAGE_SIZE); + if (!desc_bank[i].mem) { + ret = ENOMEM; + goto err; + } + + desc_bank[i].vaddr = QWZ_DMA_KVA(desc_bank[i].mem); + desc_bank[i].paddr = QWZ_DMA_DVA(desc_bank[i].mem); + desc_bank[i].size = desc_sz; + } + + return 0; + +err: + qwz_dp_link_desc_bank_free(sc, dp->link_desc_banks); + + return ret; +} + +void +qwz_hal_setup_link_idle_list(struct qwz_softc *sc, + struct hal_wbm_idle_scatter_list *sbuf, + uint32_t nsbufs, uint32_t tot_link_desc, uint32_t end_offset) +{ + struct ath12k_buffer_addr *link_addr; + int i; + uint32_t reg_scatter_buf_sz = HAL_WBM_IDLE_SCATTER_BUF_SIZE / 64; + + link_addr = (void *)sbuf[0].vaddr + HAL_WBM_IDLE_SCATTER_BUF_SIZE; + + for (i = 1; i < nsbufs; i++) { + link_addr->info0 = sbuf[i].paddr & HAL_ADDR_LSB_REG_MASK; + link_addr->info1 = FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32, + (uint64_t)sbuf[i].paddr >> HAL_ADDR_MSB_REG_SHIFT) | + FIELD_PREP(HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_MATCH_TAG, + BASE_ADDR_MATCH_TAG_VAL); + + link_addr = (void *)sbuf[i].vaddr + + HAL_WBM_IDLE_SCATTER_BUF_SIZE; + } + + sc->ops.write32(sc, + HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_R0_IDLE_LIST_CONTROL_ADDR, + FIELD_PREP(HAL_WBM_SCATTER_BUFFER_SIZE, reg_scatter_buf_sz) | + FIELD_PREP(HAL_WBM_LINK_DESC_IDLE_LIST_MODE, 0x1)); + sc->ops.write32(sc, + HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_R0_IDLE_LIST_SIZE_ADDR, + FIELD_PREP(HAL_WBM_SCATTER_RING_SIZE_OF_IDLE_LINK_DESC_LIST, + reg_scatter_buf_sz * nsbufs)); + sc->ops.write32(sc, + HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_SCATTERED_RING_BASE_LSB, + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + sbuf[0].paddr & HAL_ADDR_LSB_REG_MASK)); + sc->ops.write32(sc, HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_RING_BASE_MSB, + FIELD_PREP(HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32, + (uint64_t)sbuf[0].paddr >> HAL_ADDR_MSB_REG_SHIFT) | + FIELD_PREP(HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_MATCH_TAG, + BASE_ADDR_MATCH_TAG_VAL)); + + /* Setup head and tail pointers for the idle list */ + sc->ops.write32(sc, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX0, + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, sbuf[nsbufs - 1].paddr)); + sc->ops.write32(sc, + HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX1, + FIELD_PREP(HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32, + ((uint64_t)sbuf[nsbufs - 1].paddr >> HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_WBM_SCATTERED_DESC_HEAD_P_OFFSET_IX1, + (end_offset >> 2))); + sc->ops.write32(sc, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX0, + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, sbuf[0].paddr)); + + sc->ops.write32(sc, + HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX0, + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, sbuf[0].paddr)); + sc->ops.write32(sc, + HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX1, + FIELD_PREP(HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32, + ((uint64_t)sbuf[0].paddr >> HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_WBM_SCATTERED_DESC_TAIL_P_OFFSET_IX1, 0)); + sc->ops.write32(sc, + HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_SCATTERED_DESC_PTR_HP_ADDR, + 2 * tot_link_desc); + + /* Enable the SRNG */ + sc->ops.write32(sc, + HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_IDLE_LINK_RING_MISC_ADDR(sc), + 0x40); +} + +void +qwz_hal_set_link_desc_addr(struct hal_wbm_link_desc *desc, uint32_t cookie, + bus_addr_t paddr) +{ + desc->buf_addr_info.info0 = FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + (paddr & HAL_ADDR_LSB_REG_MASK)); + desc->buf_addr_info.info1 = FIELD_PREP(BUFFER_ADDR_INFO1_ADDR, + ((uint64_t)paddr >> HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(BUFFER_ADDR_INFO1_RET_BUF_MGR, 1) | + FIELD_PREP(BUFFER_ADDR_INFO1_SW_COOKIE, cookie); +} + +void +qwz_dp_scatter_idle_link_desc_cleanup(struct qwz_softc *sc) +{ + struct qwz_dp *dp = &sc->dp; + struct hal_wbm_idle_scatter_list *slist = dp->scatter_list; + int i; + + for (i = 0; i < DP_IDLE_SCATTER_BUFS_MAX; i++) { + if (slist[i].mem == NULL) + continue; + + qwz_dmamem_free(sc->sc_dmat, slist[i].mem); + slist[i].mem = NULL; + slist[i].vaddr = NULL; + slist[i].paddr = 0L; + } +} + +int +qwz_dp_scatter_idle_link_desc_setup(struct qwz_softc *sc, int size, + uint32_t n_link_desc_bank, uint32_t n_link_desc, uint32_t last_bank_sz) +{ + struct qwz_dp *dp = &sc->dp; + struct dp_link_desc_bank *link_desc_banks = dp->link_desc_banks; + struct hal_wbm_idle_scatter_list *slist = dp->scatter_list; + uint32_t n_entries_per_buf; + int num_scatter_buf, scatter_idx; + struct hal_wbm_link_desc *scatter_buf; + int n_entries; + bus_addr_t paddr; + int rem_entries; + int i; + int ret = 0; + uint32_t end_offset; + + n_entries_per_buf = HAL_WBM_IDLE_SCATTER_BUF_SIZE / + qwz_hal_srng_get_entrysize(sc, HAL_WBM_IDLE_LINK); + num_scatter_buf = howmany(size, HAL_WBM_IDLE_SCATTER_BUF_SIZE); + + if (num_scatter_buf > DP_IDLE_SCATTER_BUFS_MAX) + return EINVAL; + + for (i = 0; i < num_scatter_buf; i++) { + slist[i].mem = qwz_dmamem_alloc(sc->sc_dmat, + HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX, PAGE_SIZE); + if (slist[i].mem == NULL) { + ret = ENOMEM; + goto err; + } + + slist[i].vaddr = QWZ_DMA_KVA(slist[i].mem); + slist[i].paddr = QWZ_DMA_DVA(slist[i].mem); + } + + scatter_idx = 0; + scatter_buf = slist[scatter_idx].vaddr; + rem_entries = n_entries_per_buf; + + for (i = 0; i < n_link_desc_bank; i++) { + n_entries = DP_LINK_DESC_ALLOC_SIZE_THRESH / HAL_LINK_DESC_SIZE; + paddr = link_desc_banks[i].paddr; + while (n_entries) { + qwz_hal_set_link_desc_addr(scatter_buf, i, paddr); + n_entries--; + paddr += HAL_LINK_DESC_SIZE; + if (rem_entries) { + rem_entries--; + scatter_buf++; + continue; + } + + rem_entries = n_entries_per_buf; + scatter_idx++; + scatter_buf = slist[scatter_idx].vaddr; + } + } + + end_offset = (scatter_buf - slist[scatter_idx].vaddr) * + sizeof(struct hal_wbm_link_desc); + qwz_hal_setup_link_idle_list(sc, slist, num_scatter_buf, + n_link_desc, end_offset); + + return 0; + +err: + qwz_dp_scatter_idle_link_desc_cleanup(sc); + + return ret; +} + +uint32_t * +qwz_hal_srng_src_get_next_entry(struct qwz_softc *sc, struct hal_srng *srng) +{ + uint32_t *desc; + uint32_t next_hp; +#ifdef notyet + lockdep_assert_held(&srng->lock); +#endif + + /* TODO: Using % is expensive, but we have to do this since size of some + * SRNG rings is not power of 2 (due to descriptor sizes). Need to see + * if separate function is defined for rings having power of 2 ring size + * (TCL2SW, REO2SW, SW2RXDMA and CE rings) so that we can avoid the + * overhead of % by using mask (with &). + */ + next_hp = (srng->u.src_ring.hp + srng->entry_size) % srng->ring_size; + + if (next_hp == srng->u.src_ring.cached_tp) + return NULL; + + desc = srng->ring_base_vaddr + srng->u.src_ring.hp; + srng->u.src_ring.hp = next_hp; + + /* TODO: Reap functionality is not used by all rings. If particular + * ring does not use reap functionality, we need not update reap_hp + * with next_hp pointer. Need to make sure a separate function is used + * before doing any optimization by removing below code updating + * reap_hp. + */ + srng->u.src_ring.reap_hp = next_hp; + + return desc; +} + +uint32_t * +qwz_hal_srng_src_reap_next(struct qwz_softc *sc, struct hal_srng *srng) +{ + uint32_t *desc; + uint32_t next_reap_hp; +#ifdef notyet + lockdep_assert_held(&srng->lock); +#endif + next_reap_hp = (srng->u.src_ring.reap_hp + srng->entry_size) % + srng->ring_size; + + if (next_reap_hp == srng->u.src_ring.cached_tp) + return NULL; + + desc = srng->ring_base_vaddr + next_reap_hp; + srng->u.src_ring.reap_hp = next_reap_hp; + + return desc; +} + +int +qwz_dp_link_desc_setup(struct qwz_softc *sc, + struct dp_link_desc_bank *link_desc_banks, uint32_t ring_type, + struct hal_srng *srng, uint32_t n_link_desc) +{ + uint32_t tot_mem_sz; + uint32_t n_link_desc_bank, last_bank_sz; + uint32_t entry_sz, n_entries; + uint64_t paddr; + uint32_t *desc; + int i, ret; + + tot_mem_sz = n_link_desc * HAL_LINK_DESC_SIZE; + tot_mem_sz += HAL_LINK_DESC_ALIGN; + + if (tot_mem_sz <= DP_LINK_DESC_ALLOC_SIZE_THRESH) { + n_link_desc_bank = 1; + last_bank_sz = tot_mem_sz; + } else { + n_link_desc_bank = tot_mem_sz / + (DP_LINK_DESC_ALLOC_SIZE_THRESH - HAL_LINK_DESC_ALIGN); + last_bank_sz = tot_mem_sz % (DP_LINK_DESC_ALLOC_SIZE_THRESH - + HAL_LINK_DESC_ALIGN); + + if (last_bank_sz) + n_link_desc_bank += 1; + } + + if (n_link_desc_bank > DP_LINK_DESC_BANKS_MAX) + return EINVAL; + + ret = qwz_dp_link_desc_bank_alloc(sc, link_desc_banks, + n_link_desc_bank, last_bank_sz); + if (ret) + return ret; + + /* Setup link desc idle list for HW internal usage */ + entry_sz = qwz_hal_srng_get_entrysize(sc, ring_type); + tot_mem_sz = entry_sz * n_link_desc; + + /* Setup scatter desc list when the total memory requirement is more */ + if (tot_mem_sz > DP_LINK_DESC_ALLOC_SIZE_THRESH && + ring_type != HAL_RXDMA_MONITOR_DESC) { + ret = qwz_dp_scatter_idle_link_desc_setup(sc, tot_mem_sz, + n_link_desc_bank, n_link_desc, last_bank_sz); + if (ret) { + printf("%s: failed to setup scatting idle list " + "descriptor :%d\n", + sc->sc_dev.dv_xname, ret); + goto fail_desc_bank_free; + } + + return 0; + } +#if 0 + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + + for (i = 0; i < n_link_desc_bank; i++) { + n_entries = (link_desc_banks[i].size) / HAL_LINK_DESC_SIZE; + paddr = link_desc_banks[i].paddr; + while (n_entries && + (desc = qwz_hal_srng_src_get_next_entry(sc, srng))) { + qwz_hal_set_link_desc_addr( + (struct hal_wbm_link_desc *) desc, i, paddr); + n_entries--; + paddr += HAL_LINK_DESC_SIZE; + } + } + + qwz_hal_srng_access_end(sc, srng); +#if 0 + spin_unlock_bh(&srng->lock); +#endif + + return 0; + +fail_desc_bank_free: + qwz_dp_link_desc_bank_free(sc, link_desc_banks); + + return ret; +} + +void +qwz_dp_srng_cleanup(struct qwz_softc *sc, struct dp_srng *ring) +{ + if (ring->mem == NULL) + return; + +#if 0 + if (ring->cached) + kfree(ring->vaddr_unaligned); + else +#endif + qwz_dmamem_free(sc->sc_dmat, ring->mem); + + ring->mem = NULL; + ring->vaddr = NULL; + ring->paddr = 0; +} + +void +qwz_dp_shadow_stop_timer(struct qwz_softc *sc, + struct qwz_hp_update_timer *update_timer) +{ + if (!sc->hw_params.supports_shadow_regs) + return; + + timeout_del(&update_timer->timer); +} + +void +qwz_dp_shadow_start_timer(struct qwz_softc *sc, struct hal_srng *srng, + struct qwz_hp_update_timer *update_timer) +{ +#ifdef notyet + lockdep_assert_held(&srng->lock); +#endif + if (!sc->hw_params.supports_shadow_regs) + return; + + update_timer->tx_num++; + if (update_timer->started) + return; + + update_timer->started = 1; + update_timer->timer_tx_num = update_timer->tx_num; + + timeout_add_msec(&update_timer->timer, update_timer->interval); +} + +void +qwz_dp_shadow_timer_handler(void *arg) +{ + struct qwz_hp_update_timer *update_timer = arg; + struct qwz_softc *sc = update_timer->sc; + struct hal_srng *srng = &sc->hal.srng_list[update_timer->ring_id]; + int s; + +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + s = splnet(); + + /* + * Update HP if there were no TX operations during the timeout interval, + * and stop the timer. Timer will be restarted if more TX happens. + */ + if (update_timer->timer_tx_num != update_timer->tx_num) { + update_timer->timer_tx_num = update_timer->tx_num; + timeout_add_msec(&update_timer->timer, update_timer->interval); + } else { + update_timer->started = 0; + qwz_hal_srng_shadow_update_hp_tp(sc, srng); + } +#ifdef notyet + spin_unlock_bh(&srng->lock); +#endif + splx(s); +} + +void +qwz_dp_stop_shadow_timers(struct qwz_softc *sc) +{ + int i; + + for (i = 0; i < sc->hw_params.max_tx_ring; i++) + qwz_dp_shadow_stop_timer(sc, &sc->dp.tx_ring_timer[i]); + + qwz_dp_shadow_stop_timer(sc, &sc->dp.reo_cmd_timer); +} + +void +qwz_dp_srng_common_cleanup(struct qwz_softc *sc) +{ + struct qwz_dp *dp = &sc->dp; + int i; + + qwz_dp_stop_shadow_timers(sc); + qwz_dp_srng_cleanup(sc, &dp->wbm_desc_rel_ring); + qwz_dp_srng_cleanup(sc, &dp->tcl_cmd_ring); + qwz_dp_srng_cleanup(sc, &dp->tcl_status_ring); + for (i = 0; i < sc->hw_params.max_tx_ring; i++) { + qwz_dp_srng_cleanup(sc, &dp->tx_ring[i].tcl_data_ring); + qwz_dp_srng_cleanup(sc, &dp->tx_ring[i].tcl_comp_ring); + } + qwz_dp_srng_cleanup(sc, &dp->reo_reinject_ring); + qwz_dp_srng_cleanup(sc, &dp->rx_rel_ring); + qwz_dp_srng_cleanup(sc, &dp->reo_except_ring); + qwz_dp_srng_cleanup(sc, &dp->reo_cmd_ring); + qwz_dp_srng_cleanup(sc, &dp->reo_status_ring); +} + +void +qwz_hal_srng_get_params(struct qwz_softc *sc, struct hal_srng *srng, + struct hal_srng_params *params) +{ + params->ring_base_paddr = srng->ring_base_paddr; + params->ring_base_vaddr = srng->ring_base_vaddr; + params->num_entries = srng->num_entries; + params->intr_timer_thres_us = srng->intr_timer_thres_us; + params->intr_batch_cntr_thres_entries = + srng->intr_batch_cntr_thres_entries; + params->low_threshold = srng->u.src_ring.low_threshold; + params->msi_addr = srng->msi_addr; + params->msi_data = srng->msi_data; + params->flags = srng->flags; +} + +void +qwz_hal_tx_init_data_ring(struct qwz_softc *sc, struct hal_srng *srng) +{ + struct hal_srng_params params; + struct hal_tlv_hdr *tlv; + int i, entry_size; + uint8_t *desc; + + memset(¶ms, 0, sizeof(params)); + + entry_size = qwz_hal_srng_get_entrysize(sc, HAL_TCL_DATA); + qwz_hal_srng_get_params(sc, srng, ¶ms); + desc = (uint8_t *)params.ring_base_vaddr; + + for (i = 0; i < params.num_entries; i++) { + tlv = (struct hal_tlv_hdr *)desc; + tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_TCL_DATA_CMD) | + FIELD_PREP(HAL_TLV_HDR_LEN, + sizeof(struct hal_tcl_data_cmd)); + desc += entry_size; + } +} + +#define DSCP_TID_MAP_TBL_ENTRY_SIZE 64 + +/* dscp_tid_map - Default DSCP-TID mapping + * + * DSCP TID + * 000000 0 + * 001000 1 + * 010000 2 + * 011000 3 + * 100000 4 + * 101000 5 + * 110000 6 + * 111000 7 + */ +static const uint8_t dscp_tid_map[DSCP_TID_MAP_TBL_ENTRY_SIZE] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, +}; + +void +qwz_hal_tx_set_dscp_tid_map(struct qwz_softc *sc, int id) +{ + uint32_t ctrl_reg_val; + uint32_t addr; + uint8_t hw_map_val[HAL_DSCP_TID_TBL_SIZE]; + int i; + uint32_t value; + int cnt = 0; + + ctrl_reg_val = sc->ops.read32(sc, HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL1_RING_CMN_CTRL_REG); + + /* Enable read/write access */ + ctrl_reg_val |= HAL_TCL1_RING_CMN_CTRL_DSCP_TID_MAP_PROG_EN; + sc->ops.write32(sc, HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL1_RING_CMN_CTRL_REG, ctrl_reg_val); + + addr = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_DSCP_TID_MAP + + (4 * id * (HAL_DSCP_TID_TBL_SIZE / 4)); + + /* Configure each DSCP-TID mapping in three bits there by configure + * three bytes in an iteration. + */ + for (i = 0; i < DSCP_TID_MAP_TBL_ENTRY_SIZE; i += 8) { + value = FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP0, + dscp_tid_map[i]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP1, + dscp_tid_map[i + 1]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP2, + dscp_tid_map[i + 2]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP3, + dscp_tid_map[i + 3]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP4, + dscp_tid_map[i + 4]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP5, + dscp_tid_map[i + 5]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP6, + dscp_tid_map[i + 6]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP7, + dscp_tid_map[i + 7]); + memcpy(&hw_map_val[cnt], (uint8_t *)&value, 3); + cnt += 3; + } + + for (i = 0; i < HAL_DSCP_TID_TBL_SIZE; i += 4) { + sc->ops.write32(sc, addr, *(uint32_t *)&hw_map_val[i]); + addr += 4; + } + + /* Disable read/write access */ + ctrl_reg_val = sc->ops.read32(sc, HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL1_RING_CMN_CTRL_REG); + ctrl_reg_val &= ~HAL_TCL1_RING_CMN_CTRL_DSCP_TID_MAP_PROG_EN; + sc->ops.write32(sc, HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL1_RING_CMN_CTRL_REG, ctrl_reg_val); +} + +void +qwz_dp_shadow_init_timer(struct qwz_softc *sc, + struct qwz_hp_update_timer *update_timer, + uint32_t interval, uint32_t ring_id) +{ + if (!sc->hw_params.supports_shadow_regs) + return; + + update_timer->tx_num = 0; + update_timer->timer_tx_num = 0; + update_timer->sc = sc; + update_timer->ring_id = ring_id; + update_timer->interval = interval; + update_timer->init = 1; + timeout_set(&update_timer->timer, qwz_dp_shadow_timer_handler, + update_timer); +} + +void +qwz_hal_reo_init_cmd_ring(struct qwz_softc *sc, struct hal_srng *srng) +{ + struct hal_srng_params params; + struct hal_tlv_hdr *tlv; + struct hal_reo_get_queue_stats *desc; + int i, cmd_num = 1; + int entry_size; + uint8_t *entry; + + memset(¶ms, 0, sizeof(params)); + + entry_size = qwz_hal_srng_get_entrysize(sc, HAL_REO_CMD); + qwz_hal_srng_get_params(sc, srng, ¶ms); + entry = (uint8_t *)params.ring_base_vaddr; + + for (i = 0; i < params.num_entries; i++) { + tlv = (struct hal_tlv_hdr *)entry; + desc = (struct hal_reo_get_queue_stats *)tlv->value; + desc->cmd.info0 = FIELD_PREP(HAL_REO_CMD_HDR_INFO0_CMD_NUMBER, + cmd_num++); + entry += entry_size; + } +} + +int +qwz_hal_reo_cmd_queue_stats(struct hal_tlv_hdr *tlv, struct ath12k_hal_reo_cmd *cmd) +{ + struct hal_reo_get_queue_stats *desc; + + tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_REO_GET_QUEUE_STATS) | + FIELD_PREP(HAL_TLV_HDR_LEN, sizeof(*desc)); + + desc = (struct hal_reo_get_queue_stats *)tlv->value; + + desc->cmd.info0 &= ~HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS) + desc->cmd.info0 |= HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + + desc->queue_addr_lo = cmd->addr_lo; + desc->info0 = FIELD_PREP(HAL_REO_GET_QUEUE_STATS_INFO0_QUEUE_ADDR_HI, + cmd->addr_hi); + if (cmd->flag & HAL_REO_CMD_FLG_STATS_CLEAR) + desc->info0 |= HAL_REO_GET_QUEUE_STATS_INFO0_CLEAR_STATS; + + return FIELD_GET(HAL_REO_CMD_HDR_INFO0_CMD_NUMBER, desc->cmd.info0); +} + +int +qwz_hal_reo_cmd_flush_cache(struct ath12k_hal *hal, struct hal_tlv_hdr *tlv, + struct ath12k_hal_reo_cmd *cmd) +{ + struct hal_reo_flush_cache *desc; + uint8_t avail_slot = ffz(hal->avail_blk_resource); + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER) { + if (avail_slot >= HAL_MAX_AVAIL_BLK_RES) + return ENOSPC; + + hal->current_blk_index = avail_slot; + } + + tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_REO_FLUSH_CACHE) | + FIELD_PREP(HAL_TLV_HDR_LEN, sizeof(*desc)); + + desc = (struct hal_reo_flush_cache *)tlv->value; + + desc->cmd.info0 &= ~HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS) + desc->cmd.info0 |= HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + + desc->cache_addr_lo = cmd->addr_lo; + desc->info0 = FIELD_PREP(HAL_REO_FLUSH_CACHE_INFO0_CACHE_ADDR_HI, + cmd->addr_hi); + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_FWD_ALL_MPDUS) + desc->info0 |= HAL_REO_FLUSH_CACHE_INFO0_FWD_ALL_MPDUS; + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER) { + desc->info0 |= HAL_REO_FLUSH_CACHE_INFO0_BLOCK_CACHE_USAGE; + desc->info0 |= + FIELD_PREP(HAL_REO_FLUSH_CACHE_INFO0_BLOCK_RESRC_IDX, + avail_slot); + } + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_NO_INVAL) + desc->info0 |= HAL_REO_FLUSH_CACHE_INFO0_FLUSH_WO_INVALIDATE; + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_ALL) + desc->info0 |= HAL_REO_FLUSH_CACHE_INFO0_FLUSH_ALL; + + return FIELD_GET(HAL_REO_CMD_HDR_INFO0_CMD_NUMBER, desc->cmd.info0); +} + +int +qwz_hal_reo_cmd_update_rx_queue(struct hal_tlv_hdr *tlv, + struct ath12k_hal_reo_cmd *cmd) +{ + struct hal_reo_update_rx_queue *desc; + + tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_REO_UPDATE_RX_REO_QUEUE) | + FIELD_PREP(HAL_TLV_HDR_LEN, sizeof(*desc)); + + desc = (struct hal_reo_update_rx_queue *)tlv->value; + + desc->cmd.info0 &= ~HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS) + desc->cmd.info0 |= HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + + desc->queue_addr_lo = cmd->addr_lo; + desc->info0 = + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_QUEUE_ADDR_HI, + cmd->addr_hi) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RX_QUEUE_NUM, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_RX_QUEUE_NUM)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_VLD, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_VLD)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_ASSOC_LNK_DESC_CNT, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_ALDC)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_DIS_DUP_DETECTION, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_DIS_DUP_DETECTION)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SOFT_REORDER_EN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_SOFT_REORDER_EN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_AC, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_AC)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BAR, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_BAR)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RETRY, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_RETRY)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_CHECK_2K_MODE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_CHECK_2K_MODE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_OOR_MODE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_OOR_MODE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BA_WINDOW_SIZE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_BA_WINDOW_SIZE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_CHECK, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_CHECK)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_EVEN_PN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_EVEN_PN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_UNEVEN_PN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_UNEVEN_PN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_HANDLE_ENABLE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_HANDLE_ENABLE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_SIZE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_SIZE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_IGNORE_AMPDU_FLG, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_IGNORE_AMPDU_FLG)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SVLD, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_SVLD)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SSN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_SSN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SEQ_2K_ERR, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_SEQ_2K_ERR)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_VALID, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_VALID)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN)); + + desc->info1 = + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_RX_QUEUE_NUMBER, + cmd->rx_queue_num) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_VLD, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_VLD)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_ASSOC_LNK_DESC_COUNTER, + FIELD_GET(HAL_REO_CMD_UPD1_ALDC, cmd->upd1)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_DIS_DUP_DETECTION, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_DIS_DUP_DETECTION)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_SOFT_REORDER_EN, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_SOFT_REORDER_EN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_AC, + FIELD_GET(HAL_REO_CMD_UPD1_AC, cmd->upd1)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_BAR, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_BAR)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_CHECK_2K_MODE, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_CHECK_2K_MODE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_RETRY, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_RETRY)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_OOR_MODE, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_OOR_MODE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_PN_CHECK, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_PN_CHECK)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_EVEN_PN, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_EVEN_PN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_UNEVEN_PN, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_UNEVEN_PN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_PN_HANDLE_ENABLE, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_PN_HANDLE_ENABLE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_IGNORE_AMPDU_FLG, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_IGNORE_AMPDU_FLG)); + + if (cmd->pn_size == 24) + cmd->pn_size = HAL_RX_REO_QUEUE_PN_SIZE_24; + else if (cmd->pn_size == 48) + cmd->pn_size = HAL_RX_REO_QUEUE_PN_SIZE_48; + else if (cmd->pn_size == 128) + cmd->pn_size = HAL_RX_REO_QUEUE_PN_SIZE_128; + + if (cmd->ba_window_size < 1) + cmd->ba_window_size = 1; + + if (cmd->ba_window_size == 1) + cmd->ba_window_size++; + + desc->info2 = FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_BA_WINDOW_SIZE, + cmd->ba_window_size - 1) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_PN_SIZE, cmd->pn_size) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_SVLD, + !!(cmd->upd2 & HAL_REO_CMD_UPD2_SVLD)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_SSN, + FIELD_GET(HAL_REO_CMD_UPD2_SSN, cmd->upd2)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_SEQ_2K_ERR, + !!(cmd->upd2 & HAL_REO_CMD_UPD2_SEQ_2K_ERR)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_PN_ERR, + !!(cmd->upd2 & HAL_REO_CMD_UPD2_PN_ERR)); + + return FIELD_GET(HAL_REO_CMD_HDR_INFO0_CMD_NUMBER, desc->cmd.info0); +} + +int +qwz_hal_reo_cmd_send(struct qwz_softc *sc, struct hal_srng *srng, + enum hal_reo_cmd_type type, struct ath12k_hal_reo_cmd *cmd) +{ + struct hal_tlv_hdr *reo_desc; + int ret; +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + reo_desc = (struct hal_tlv_hdr *)qwz_hal_srng_src_get_next_entry(sc, srng); + if (!reo_desc) { + ret = ENOBUFS; + goto out; + } + + switch (type) { + case HAL_REO_CMD_GET_QUEUE_STATS: + ret = qwz_hal_reo_cmd_queue_stats(reo_desc, cmd); + break; + case HAL_REO_CMD_FLUSH_CACHE: + ret = qwz_hal_reo_cmd_flush_cache(&sc->hal, reo_desc, cmd); + break; + case HAL_REO_CMD_UPDATE_RX_QUEUE: + ret = qwz_hal_reo_cmd_update_rx_queue(reo_desc, cmd); + break; + case HAL_REO_CMD_FLUSH_QUEUE: + case HAL_REO_CMD_UNBLOCK_CACHE: + case HAL_REO_CMD_FLUSH_TIMEOUT_LIST: + printf("%s: unsupported reo command %d\n", + sc->sc_dev.dv_xname, type); + ret = ENOTSUP; + break; + default: + printf("%s: unknown reo command %d\n", + sc->sc_dev.dv_xname, type); + ret = EINVAL; + break; + } + + qwz_dp_shadow_start_timer(sc, srng, &sc->dp.reo_cmd_timer); +out: + qwz_hal_srng_access_end(sc, srng); +#ifdef notyet + spin_unlock_bh(&srng->lock); +#endif + return ret; +} +int +qwz_dp_srng_common_setup(struct qwz_softc *sc) +{ + struct qwz_dp *dp = &sc->dp; + struct hal_srng *srng; + int i, ret; + uint8_t tcl_num, wbm_num; + + ret = qwz_dp_srng_setup(sc, &dp->wbm_desc_rel_ring, HAL_SW2WBM_RELEASE, + 0, 0, DP_WBM_RELEASE_RING_SIZE); + if (ret) { + printf("%s: failed to set up wbm2sw_release ring :%d\n", + sc->sc_dev.dv_xname, ret); + goto err; + } + + ret = qwz_dp_srng_setup(sc, &dp->tcl_cmd_ring, HAL_TCL_CMD, + 0, 0, DP_TCL_CMD_RING_SIZE); + if (ret) { + printf("%s: failed to set up tcl_cmd ring :%d\n", + sc->sc_dev.dv_xname, ret); + goto err; + } + + ret = qwz_dp_srng_setup(sc, &dp->tcl_status_ring, HAL_TCL_STATUS, + 0, 0, DP_TCL_STATUS_RING_SIZE); + if (ret) { + printf("%s: failed to set up tcl_status ring :%d\n", + sc->sc_dev.dv_xname, ret); + goto err; + } + + for (i = 0; i < sc->hw_params.max_tx_ring; i++) { + const struct ath12k_hw_hal_params *hal_params; + + hal_params = sc->hw_params.hal_params; + tcl_num = hal_params->tcl2wbm_rbm_map[i].tcl_ring_num; + wbm_num = hal_params->tcl2wbm_rbm_map[i].wbm_ring_num; + + ret = qwz_dp_srng_setup(sc, &dp->tx_ring[i].tcl_data_ring, + HAL_TCL_DATA, tcl_num, 0, sc->hw_params.tx_ring_size); + if (ret) { + printf("%s: failed to set up tcl_data ring (%d) :%d\n", + sc->sc_dev.dv_xname, i, ret); + goto err; + } + + ret = qwz_dp_srng_setup(sc, &dp->tx_ring[i].tcl_comp_ring, + HAL_WBM2SW_RELEASE, wbm_num, 0, DP_TX_COMP_RING_SIZE); + if (ret) { + printf("%s: failed to set up tcl_comp ring (%d) :%d\n", + sc->sc_dev.dv_xname, i, ret); + goto err; + } + + srng = &sc->hal.srng_list[dp->tx_ring[i].tcl_data_ring.ring_id]; + qwz_hal_tx_init_data_ring(sc, srng); + + qwz_dp_shadow_init_timer(sc, &dp->tx_ring_timer[i], + ATH12K_SHADOW_DP_TIMER_INTERVAL, + dp->tx_ring[i].tcl_data_ring.ring_id); + } + + ret = qwz_dp_srng_setup(sc, &dp->reo_reinject_ring, HAL_REO_REINJECT, + 0, 0, DP_REO_REINJECT_RING_SIZE); + if (ret) { + printf("%s: failed to set up reo_reinject ring :%d\n", + sc->sc_dev.dv_xname, ret); + goto err; + } + + ret = qwz_dp_srng_setup(sc, &dp->rx_rel_ring, HAL_WBM2SW_RELEASE, + DP_RX_RELEASE_RING_NUM, 0, DP_RX_RELEASE_RING_SIZE); + if (ret) { + printf("%s: failed to set up rx_rel ring :%d\n", + sc->sc_dev.dv_xname, ret); + goto err; + } + + ret = qwz_dp_srng_setup(sc, &dp->reo_except_ring, HAL_REO_EXCEPTION, + 0, 0, DP_REO_EXCEPTION_RING_SIZE); + if (ret) { + printf("%s: failed to set up reo_exception ring :%d\n", + sc->sc_dev.dv_xname, ret); + goto err; + } + + ret = qwz_dp_srng_setup(sc, &dp->reo_cmd_ring, HAL_REO_CMD, 0, 0, + DP_REO_CMD_RING_SIZE); + if (ret) { + printf("%s: failed to set up reo_cmd ring :%d\n", + sc->sc_dev.dv_xname, ret); + goto err; + } + + srng = &sc->hal.srng_list[dp->reo_cmd_ring.ring_id]; + qwz_hal_reo_init_cmd_ring(sc, srng); + + qwz_dp_shadow_init_timer(sc, &dp->reo_cmd_timer, + ATH12K_SHADOW_CTRL_TIMER_INTERVAL, dp->reo_cmd_ring.ring_id); + + ret = qwz_dp_srng_setup(sc, &dp->reo_status_ring, HAL_REO_STATUS, + 0, 0, DP_REO_STATUS_RING_SIZE); + if (ret) { + printf("%s: failed to set up reo_status ring :%d\n", + sc->sc_dev.dv_xname, ret); + goto err; + } + + /* When hash based routing of rx packet is enabled, 32 entries to map + * the hash values to the ring will be configured. + */ + sc->hw_params.hw_ops->reo_setup(sc); + return 0; + +err: + qwz_dp_srng_common_cleanup(sc); + + return ret; +} + +void +qwz_dp_link_desc_cleanup(struct qwz_softc *sc, + struct dp_link_desc_bank *desc_bank, uint32_t ring_type, + struct dp_srng *ring) +{ + qwz_dp_link_desc_bank_free(sc, desc_bank); + + if (ring_type != HAL_RXDMA_MONITOR_DESC) { + qwz_dp_srng_cleanup(sc, ring); + qwz_dp_scatter_idle_link_desc_cleanup(sc); + } +} + +void +qwz_dp_tx_ring_free_tx_data(struct qwz_softc *sc, struct dp_tx_ring *tx_ring) +{ + int i; + + if (tx_ring->data == NULL) + return; + + for (i = 0; i < sc->hw_params.tx_ring_size; i++) { + struct qwz_tx_data *tx_data = &tx_ring->data[i]; + + if (tx_data->map) { + bus_dmamap_unload(sc->sc_dmat, tx_data->map); + bus_dmamap_destroy(sc->sc_dmat, tx_data->map); + } + + m_freem(tx_data->m); + } + + free(tx_ring->data, M_DEVBUF, + sc->hw_params.tx_ring_size * sizeof(struct qwz_tx_data)); + tx_ring->data = NULL; +} + +int +qwz_dp_tx_ring_alloc_tx_data(struct qwz_softc *sc, struct dp_tx_ring *tx_ring) +{ + int i, ret; + + tx_ring->data = mallocarray(sc->hw_params.tx_ring_size, + sizeof(struct qwz_tx_data), M_DEVBUF, M_NOWAIT | M_ZERO); + if (tx_ring->data == NULL) + return ENOMEM; + + for (i = 0; i < sc->hw_params.tx_ring_size; i++) { + struct qwz_tx_data *tx_data = &tx_ring->data[i]; + + ret = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, 0, + BUS_DMA_NOWAIT, &tx_data->map); + if (ret) + return ret; + } + + return 0; +} + +int +qwz_dp_alloc(struct qwz_softc *sc) +{ + struct qwz_dp *dp = &sc->dp; + struct hal_srng *srng = NULL; + size_t size = 0; + uint32_t n_link_desc = 0; + int ret; + int i; + + dp->sc = sc; + + TAILQ_INIT(&dp->reo_cmd_list); + TAILQ_INIT(&dp->reo_cmd_cache_flush_list); +#if 0 + INIT_LIST_HEAD(&dp->dp_full_mon_mpdu_list); + spin_lock_init(&dp->reo_cmd_lock); +#endif + + dp->reo_cmd_cache_flush_count = 0; + + ret = qwz_wbm_idle_ring_setup(sc, &n_link_desc); + if (ret) { + printf("%s: failed to setup wbm_idle_ring: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + srng = &sc->hal.srng_list[dp->wbm_idle_ring.ring_id]; + + ret = qwz_dp_link_desc_setup(sc, dp->link_desc_banks, + HAL_WBM_IDLE_LINK, srng, n_link_desc); + if (ret) { + printf("%s: failed to setup link desc: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = qwz_dp_srng_common_setup(sc); + if (ret) + goto fail_link_desc_cleanup; + + size = sizeof(struct hal_wbm_release_ring) * DP_TX_COMP_RING_SIZE; + + for (i = 0; i < sc->hw_params.max_tx_ring; i++) { +#if 0 + idr_init(&dp->tx_ring[i].txbuf_idr); + spin_lock_init(&dp->tx_ring[i].tx_idr_lock); +#endif + ret = qwz_dp_tx_ring_alloc_tx_data(sc, &dp->tx_ring[i]); + if (ret) + goto fail_cmn_srng_cleanup; + + dp->tx_ring[i].cur = 0; + dp->tx_ring[i].queued = 0; + dp->tx_ring[i].tcl_data_ring_id = i; + dp->tx_ring[i].tx_status_head = 0; + dp->tx_ring[i].tx_status_tail = DP_TX_COMP_RING_SIZE - 1; + dp->tx_ring[i].tx_status = malloc(size, M_DEVBUF, + M_NOWAIT | M_ZERO); + if (!dp->tx_ring[i].tx_status) { + ret = ENOMEM; + goto fail_cmn_srng_cleanup; + } + } + + for (i = 0; i < HAL_DSCP_TID_MAP_TBL_NUM_ENTRIES_MAX; i++) + qwz_hal_tx_set_dscp_tid_map(sc, i); + + /* Init any SOC level resource for DP */ + + return 0; +fail_cmn_srng_cleanup: + qwz_dp_srng_common_cleanup(sc); +fail_link_desc_cleanup: + qwz_dp_link_desc_cleanup(sc, dp->link_desc_banks, HAL_WBM_IDLE_LINK, + &dp->wbm_idle_ring); + + return ret; +} + +void +qwz_dp_reo_cmd_list_cleanup(struct qwz_softc *sc) +{ + struct qwz_dp *dp = &sc->dp; + struct dp_reo_cmd *cmd, *tmp; + struct dp_reo_cache_flush_elem *cmd_cache, *tmp_cache; + struct dp_rx_tid *rx_tid; +#ifdef notyet + spin_lock_bh(&dp->reo_cmd_lock); +#endif + TAILQ_FOREACH_SAFE(cmd, &dp->reo_cmd_list, entry, tmp) { + TAILQ_REMOVE(&dp->reo_cmd_list, cmd, entry); + rx_tid = &cmd->data; + if (rx_tid->mem) { + qwz_dmamem_free(sc->sc_dmat, rx_tid->mem); + rx_tid->mem = NULL; + rx_tid->vaddr = NULL; + rx_tid->paddr = 0ULL; + rx_tid->size = 0; + } + free(cmd, M_DEVBUF, sizeof(*cmd)); + } + + TAILQ_FOREACH_SAFE(cmd_cache, &dp->reo_cmd_cache_flush_list, + entry, tmp_cache) { + TAILQ_REMOVE(&dp->reo_cmd_cache_flush_list, cmd_cache, entry); + dp->reo_cmd_cache_flush_count--; + rx_tid = &cmd_cache->data; + if (rx_tid->mem) { + qwz_dmamem_free(sc->sc_dmat, rx_tid->mem); + rx_tid->mem = NULL; + rx_tid->vaddr = NULL; + rx_tid->paddr = 0ULL; + rx_tid->size = 0; + } + free(cmd_cache, M_DEVBUF, sizeof(*cmd_cache)); + } +#ifdef notyet + spin_unlock_bh(&dp->reo_cmd_lock); +#endif +} + +void +qwz_dp_free(struct qwz_softc *sc) +{ + struct qwz_dp *dp = &sc->dp; + int i; + + qwz_dp_link_desc_cleanup(sc, dp->link_desc_banks, + HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring); + + qwz_dp_srng_common_cleanup(sc); + qwz_dp_reo_cmd_list_cleanup(sc); + for (i = 0; i < sc->hw_params.max_tx_ring; i++) { +#if 0 + spin_lock_bh(&dp->tx_ring[i].tx_idr_lock); + idr_for_each(&dp->tx_ring[i].txbuf_idr, + ath12k_dp_tx_pending_cleanup, ab); + idr_destroy(&dp->tx_ring[i].txbuf_idr); + spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock); +#endif + qwz_dp_tx_ring_free_tx_data(sc, &dp->tx_ring[i]); + free(dp->tx_ring[i].tx_status, M_DEVBUF, + sizeof(struct hal_wbm_release_ring) * DP_TX_COMP_RING_SIZE); + dp->tx_ring[i].tx_status = NULL; + } + + /* Deinit any SOC level resource */ +} + +void +qwz_qmi_process_coldboot_calibration(struct qwz_softc *sc) +{ + printf("%s not implemented\n", __func__); +} + +int +qwz_qmi_wlanfw_wlan_ini_send(struct qwz_softc *sc, int enable) +{ + int ret; + struct qmi_wlanfw_wlan_ini_req_msg_v01 req = {}; + + req.enablefwlog_valid = 1; + req.enablefwlog = enable ? 1 : 0; + + ret = qwz_qmi_send_request(sc, QMI_WLANFW_WLAN_INI_REQ_V01, + QMI_WLANFW_WLAN_INI_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_wlan_ini_req_msg_v01_ei, &req, sizeof(req)); + if (ret) { + printf("%s: failed to send wlan ini request, err = %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + sc->qmi_resp.result = QMI_RESULT_FAILURE_V01; + while (sc->qmi_resp.result != QMI_RESULT_SUCCESS_V01) { + ret = tsleep_nsec(&sc->qmi_resp, 0, "qwzini", + SEC_TO_NSEC(1)); + if (ret) { + printf("%s: wlan ini request timeout\n", + sc->sc_dev.dv_xname); + return ret; + } + } + + return 0; +} + +int +qwz_qmi_wlanfw_wlan_cfg_send(struct qwz_softc *sc) +{ + struct qmi_wlanfw_wlan_cfg_req_msg_v01 *req; + const struct ce_pipe_config *ce_cfg; + const struct service_to_pipe *svc_cfg; + int ret = 0, pipe_num; + + ce_cfg = sc->hw_params.target_ce_config; + svc_cfg = sc->hw_params.svc_to_ce_map; + + req = malloc(sizeof(*req), M_DEVBUF, M_NOWAIT | M_ZERO); + if (!req) + return ENOMEM; + + req->host_version_valid = 1; + strlcpy(req->host_version, ATH12K_HOST_VERSION_STRING, + sizeof(req->host_version)); + + req->tgt_cfg_valid = 1; + /* This is number of CE configs */ + req->tgt_cfg_len = sc->hw_params.target_ce_count; + for (pipe_num = 0; pipe_num < req->tgt_cfg_len ; pipe_num++) { + req->tgt_cfg[pipe_num].pipe_num = ce_cfg[pipe_num].pipenum; + req->tgt_cfg[pipe_num].pipe_dir = ce_cfg[pipe_num].pipedir; + req->tgt_cfg[pipe_num].nentries = ce_cfg[pipe_num].nentries; + req->tgt_cfg[pipe_num].nbytes_max = ce_cfg[pipe_num].nbytes_max; + req->tgt_cfg[pipe_num].flags = ce_cfg[pipe_num].flags; + } + + req->svc_cfg_valid = 1; + /* This is number of Service/CE configs */ + req->svc_cfg_len = sc->hw_params.svc_to_ce_map_len; + for (pipe_num = 0; pipe_num < req->svc_cfg_len; pipe_num++) { + req->svc_cfg[pipe_num].service_id = svc_cfg[pipe_num].service_id; + req->svc_cfg[pipe_num].pipe_dir = svc_cfg[pipe_num].pipedir; + req->svc_cfg[pipe_num].pipe_num = svc_cfg[pipe_num].pipenum; + } + req->shadow_reg_valid = 0; + + /* set shadow v2 configuration */ + if (sc->hw_params.supports_shadow_regs) { + req->shadow_reg_v2_valid = 1; + req->shadow_reg_v2_len = MIN(sc->qmi_ce_cfg.shadow_reg_v2_len, + QMI_WLANFW_MAX_NUM_SHADOW_REG_V2_V01); + memcpy(&req->shadow_reg_v2, sc->qmi_ce_cfg.shadow_reg_v2, + sizeof(uint32_t) * req->shadow_reg_v2_len); + } else { + req->shadow_reg_v2_valid = 0; + } + + DNPRINTF(QWZ_D_QMI, "%s: wlan cfg req\n", __func__); + + ret = qwz_qmi_send_request(sc, QMI_WLANFW_WLAN_CFG_REQ_V01, + QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_wlan_cfg_req_msg_v01_ei, req, sizeof(*req)); + if (ret) { + printf("%s: failed to send wlan config request: %d\n", + sc->sc_dev.dv_xname, ret); + goto out; + } + + sc->qmi_resp.result = QMI_RESULT_FAILURE_V01; + while (sc->qmi_resp.result != QMI_RESULT_SUCCESS_V01) { + ret = tsleep_nsec(&sc->qmi_resp, 0, "qwzwlancfg", + SEC_TO_NSEC(1)); + if (ret) { + printf("%s: wlan config request failed\n", + sc->sc_dev.dv_xname); + goto out; + } + } +out: + free(req, M_DEVBUF, sizeof(*req)); + return ret; +} + +int +qwz_qmi_wlanfw_mode_send(struct qwz_softc *sc, enum ath12k_firmware_mode mode) +{ + int ret; + struct qmi_wlanfw_wlan_mode_req_msg_v01 req = {}; + + req.mode = mode; + req.hw_debug_valid = 1; + req.hw_debug = 0; + + ret = qwz_qmi_send_request(sc, QMI_WLANFW_WLAN_MODE_REQ_V01, + QMI_WLANFW_WLAN_MODE_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_wlan_mode_req_msg_v01_ei, &req, sizeof(req)); + if (ret) { + printf("%s: failed to send wlan mode request, err = %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + sc->qmi_resp.result = QMI_RESULT_FAILURE_V01; + while (sc->qmi_resp.result != QMI_RESULT_SUCCESS_V01) { + ret = tsleep_nsec(&sc->qmi_resp, 0, "qwzfwmode", + SEC_TO_NSEC(1)); + if (ret) { + if (mode == ATH12K_FIRMWARE_MODE_OFF) + return 0; + printf("%s: wlan mode request timeout\n", + sc->sc_dev.dv_xname); + return ret; + } + } + + return 0; +} + +int +qwz_qmi_firmware_start(struct qwz_softc *sc, enum ath12k_firmware_mode mode) +{ + int ret; + + DPRINTF("%s: firmware start\n", sc->sc_dev.dv_xname); + + if (sc->hw_params.fw_wmi_diag_event) { + ret = qwz_qmi_wlanfw_wlan_ini_send(sc, 1); + if (ret < 0) { + printf("%s: qmi failed to send wlan fw ini: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + } + + ret = qwz_qmi_wlanfw_wlan_cfg_send(sc); + if (ret) { + printf("%s: qmi failed to send wlan cfg: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = qwz_qmi_wlanfw_mode_send(sc, mode); + if (ret) { + printf("%s: qmi failed to send wlan fw mode: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + return 0; +} + +void +qwz_qmi_firmware_stop(struct qwz_softc *sc) +{ + int ret; + + ret = qwz_qmi_wlanfw_mode_send(sc, ATH12K_FIRMWARE_MODE_OFF); + if (ret) { + printf("%s: qmi failed to send wlan mode off: %d\n", + sc->sc_dev.dv_xname, ret); + } +} + +int +qwz_core_start_firmware(struct qwz_softc *sc, enum ath12k_firmware_mode mode) +{ + int ret; + + qwz_ce_get_shadow_config(sc, &sc->qmi_ce_cfg.shadow_reg_v2, + &sc->qmi_ce_cfg.shadow_reg_v2_len); + + ret = qwz_qmi_firmware_start(sc, mode); + if (ret) { + printf("%s: failed to send firmware start: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + return ret; +} + +int +qwz_wmi_pdev_attach(struct qwz_softc *sc, uint8_t pdev_id) +{ + struct qwz_pdev_wmi *wmi_handle; + + if (pdev_id >= sc->hw_params.max_radios) + return EINVAL; + + wmi_handle = &sc->wmi.wmi[pdev_id]; + wmi_handle->wmi = &sc->wmi; + + wmi_handle->tx_ce_desc = 1; + + return 0; +} + +void +qwz_wmi_detach(struct qwz_softc *sc) +{ + qwz_wmi_free_dbring_caps(sc); +} + +int +qwz_wmi_attach(struct qwz_softc *sc) +{ + int ret; + + ret = qwz_wmi_pdev_attach(sc, 0); + if (ret) + return ret; + + sc->wmi.sc = sc; + sc->wmi.preferred_hw_mode = WMI_HOST_HW_MODE_MAX; + sc->wmi.tx_credits = 1; + + /* It's overwritten when service_ext_ready is handled */ + if (sc->hw_params.single_pdev_only && + sc->hw_params.num_rxmda_per_pdev > 1) + sc->wmi.preferred_hw_mode = WMI_HOST_HW_MODE_SINGLE; + + return 0; +} + +void +qwz_wmi_htc_tx_complete(struct qwz_softc *sc, struct mbuf *m) +{ + struct qwz_pdev_wmi *wmi = NULL; + uint32_t i; + uint8_t wmi_ep_count; + uint8_t eid; + + eid = (uintptr_t)m->m_pkthdr.ph_cookie; + m_freem(m); + + if (eid >= ATH12K_HTC_EP_COUNT) + return; + + wmi_ep_count = sc->htc.wmi_ep_count; + if (wmi_ep_count > sc->hw_params.max_radios) + return; + + for (i = 0; i < sc->htc.wmi_ep_count; i++) { + if (sc->wmi.wmi[i].eid == eid) { + wmi = &sc->wmi.wmi[i]; + break; + } + } + + if (wmi) + wakeup(&wmi->tx_ce_desc); +} + +int +qwz_wmi_tlv_services_parser(struct qwz_softc *sc, uint16_t tag, uint16_t len, + const void *ptr, void *data) +{ + const struct wmi_service_available_event *ev; + uint32_t *wmi_ext2_service_bitmap; + int i, j; + + switch (tag) { + case WMI_TAG_SERVICE_AVAILABLE_EVENT: + ev = (struct wmi_service_available_event *)ptr; + for (i = 0, j = WMI_MAX_SERVICE; + i < WMI_SERVICE_SEGMENT_BM_SIZE32 && + j < WMI_MAX_EXT_SERVICE; + i++) { + do { + if (ev->wmi_service_segment_bitmap[i] & + BIT(j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32)) + setbit(sc->wmi.svc_map, j); + } while (++j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32); + } + + DNPRINTF(QWZ_D_WMI, + "%s: wmi_ext_service_bitmap 0:0x%04x, 1:0x%04x, " + "2:0x%04x, 3:0x%04x\n", __func__, + ev->wmi_service_segment_bitmap[0], + ev->wmi_service_segment_bitmap[1], + ev->wmi_service_segment_bitmap[2], + ev->wmi_service_segment_bitmap[3]); + break; + case WMI_TAG_ARRAY_UINT32: + wmi_ext2_service_bitmap = (uint32_t *)ptr; + for (i = 0, j = WMI_MAX_EXT_SERVICE; + i < WMI_SERVICE_SEGMENT_BM_SIZE32 && + j < WMI_MAX_EXT2_SERVICE; + i++) { + do { + if (wmi_ext2_service_bitmap[i] & + BIT(j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32)) + setbit(sc->wmi.svc_map, j); + } while (++j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32); + } + + DNPRINTF(QWZ_D_WMI, + "%s: wmi_ext2_service__bitmap 0:0x%04x, 1:0x%04x, " + "2:0x%04x, 3:0x%04x\n", __func__, + wmi_ext2_service_bitmap[0], wmi_ext2_service_bitmap[1], + wmi_ext2_service_bitmap[2], wmi_ext2_service_bitmap[3]); + break; + } + + return 0; +} + +static const struct wmi_tlv_policy wmi_tlv_policies[] = { + [WMI_TAG_ARRAY_BYTE] + = { .min_len = 0 }, + [WMI_TAG_ARRAY_UINT32] + = { .min_len = 0 }, + [WMI_TAG_SERVICE_READY_EVENT] + = { .min_len = sizeof(struct wmi_service_ready_event) }, + [WMI_TAG_SERVICE_READY_EXT_EVENT] + = { .min_len = sizeof(struct wmi_service_ready_ext_event) }, + [WMI_TAG_SOC_MAC_PHY_HW_MODE_CAPS] + = { .min_len = sizeof(struct wmi_soc_mac_phy_hw_mode_caps) }, + [WMI_TAG_SOC_HAL_REG_CAPABILITIES] + = { .min_len = sizeof(struct wmi_soc_hal_reg_capabilities) }, + [WMI_TAG_VDEV_START_RESPONSE_EVENT] + = { .min_len = sizeof(struct wmi_vdev_start_resp_event) }, + [WMI_TAG_PEER_DELETE_RESP_EVENT] + = { .min_len = sizeof(struct wmi_peer_delete_resp_event) }, + [WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT] + = { .min_len = sizeof(struct wmi_bcn_tx_status_event) }, + [WMI_TAG_VDEV_STOPPED_EVENT] + = { .min_len = sizeof(struct wmi_vdev_stopped_event) }, + [WMI_TAG_REG_CHAN_LIST_CC_EVENT] + = { .min_len = sizeof(struct wmi_reg_chan_list_cc_event) }, + [WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT] + = { .min_len = sizeof(struct wmi_reg_chan_list_cc_ext_event) }, + [WMI_TAG_MGMT_RX_HDR] + = { .min_len = sizeof(struct wmi_mgmt_rx_hdr) }, + [WMI_TAG_MGMT_TX_COMPL_EVENT] + = { .min_len = sizeof(struct wmi_mgmt_tx_compl_event) }, + [WMI_TAG_SCAN_EVENT] + = { .min_len = sizeof(struct wmi_scan_event) }, + [WMI_TAG_PEER_STA_KICKOUT_EVENT] + = { .min_len = sizeof(struct wmi_peer_sta_kickout_event) }, + [WMI_TAG_ROAM_EVENT] + = { .min_len = sizeof(struct wmi_roam_event) }, + [WMI_TAG_CHAN_INFO_EVENT] + = { .min_len = sizeof(struct wmi_chan_info_event) }, + [WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT] + = { .min_len = sizeof(struct wmi_pdev_bss_chan_info_event) }, + [WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT] + = { .min_len = sizeof(struct wmi_vdev_install_key_compl_event) }, + [WMI_TAG_READY_EVENT] = { + .min_len = sizeof(struct wmi_ready_event_min) }, + [WMI_TAG_SERVICE_AVAILABLE_EVENT] + = {.min_len = sizeof(struct wmi_service_available_event) }, + [WMI_TAG_PEER_ASSOC_CONF_EVENT] + = { .min_len = sizeof(struct wmi_peer_assoc_conf_event) }, + [WMI_TAG_STATS_EVENT] + = { .min_len = sizeof(struct wmi_stats_event) }, + [WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT] + = { .min_len = sizeof(struct wmi_pdev_ctl_failsafe_chk_event) }, + [WMI_TAG_HOST_SWFDA_EVENT] = { + .min_len = sizeof(struct wmi_fils_discovery_event) }, + [WMI_TAG_OFFLOAD_PRB_RSP_TX_STATUS_EVENT] = { + .min_len = sizeof(struct wmi_probe_resp_tx_status_event) }, + [WMI_TAG_VDEV_DELETE_RESP_EVENT] = { + .min_len = sizeof(struct wmi_vdev_delete_resp_event) }, + [WMI_TAG_OBSS_COLOR_COLLISION_EVT] = { + .min_len = sizeof(struct wmi_obss_color_collision_event) }, + [WMI_TAG_11D_NEW_COUNTRY_EVENT] = { + .min_len = sizeof(struct wmi_11d_new_cc_ev) }, + [WMI_TAG_PER_CHAIN_RSSI_STATS] = { + .min_len = sizeof(struct wmi_per_chain_rssi_stats) }, + [WMI_TAG_TWT_ADD_DIALOG_COMPLETE_EVENT] = { + .min_len = sizeof(struct wmi_twt_add_dialog_event) }, +}; + +int +qwz_wmi_tlv_iter(struct qwz_softc *sc, const void *ptr, size_t len, + int (*iter)(struct qwz_softc *sc, uint16_t tag, uint16_t len, + const void *ptr, void *data), void *data) +{ + const void *begin = ptr; + const struct wmi_tlv *tlv; + uint16_t tlv_tag, tlv_len; + int ret; + + while (len > 0) { + if (len < sizeof(*tlv)) { + printf("%s: wmi tlv parse failure at byte %zd " + "(%zu bytes left, %zu expected)\n", __func__, + ptr - begin, len, sizeof(*tlv)); + return EINVAL; + } + + tlv = ptr; + tlv_tag = FIELD_GET(WMI_TLV_TAG, tlv->header); + tlv_len = FIELD_GET(WMI_TLV_LEN, tlv->header); + ptr += sizeof(*tlv); + len -= sizeof(*tlv); + + if (tlv_len > len) { + printf("%s: wmi tlv parse failure of tag %u " + "at byte %zd (%zu bytes left, %u expected)\n", + __func__, tlv_tag, ptr - begin, len, tlv_len); + return EINVAL; + } + + if (tlv_tag < nitems(wmi_tlv_policies) && + wmi_tlv_policies[tlv_tag].min_len && + wmi_tlv_policies[tlv_tag].min_len > tlv_len) { + printf("%s: wmi tlv parse failure of tag %u " + "at byte %zd (%u bytes is less than " + "min length %zu)\n", __func__, + tlv_tag, ptr - begin, tlv_len, + wmi_tlv_policies[tlv_tag].min_len); + return EINVAL; + } + + ret = iter(sc, tlv_tag, tlv_len, ptr, data); + if (ret) + return ret; + + ptr += tlv_len; + len -= tlv_len; + } + + return 0; +} + +int +qwz_pull_service_ready_tlv(struct qwz_softc *sc, const void *evt_buf, + struct ath12k_targ_cap *cap) +{ + const struct wmi_service_ready_event *ev = evt_buf; + + if (!ev) + return EINVAL; + + cap->phy_capability = ev->phy_capability; + cap->max_frag_entry = ev->max_frag_entry; + cap->num_rf_chains = ev->num_rf_chains; + cap->ht_cap_info = ev->ht_cap_info; + cap->vht_cap_info = ev->vht_cap_info; + cap->vht_supp_mcs = ev->vht_supp_mcs; + cap->hw_min_tx_power = ev->hw_min_tx_power; + cap->hw_max_tx_power = ev->hw_max_tx_power; + cap->sys_cap_info = ev->sys_cap_info; + cap->min_pkt_size_enable = ev->min_pkt_size_enable; + cap->max_bcn_ie_size = ev->max_bcn_ie_size; + cap->max_num_scan_channels = ev->max_num_scan_channels; + cap->max_supported_macs = ev->max_supported_macs; + cap->wmi_fw_sub_feat_caps = ev->wmi_fw_sub_feat_caps; + cap->txrx_chainmask = ev->txrx_chainmask; + cap->default_dbs_hw_mode_index = ev->default_dbs_hw_mode_index; + cap->num_msdu_desc = ev->num_msdu_desc; + + return 0; +} + +/* Save the wmi_service_bitmap into a linear bitmap. The wmi_services in + * wmi_service ready event are advertised in b0-b3 (LSB 4-bits) of each + * 4-byte word. + */ +void +qwz_wmi_service_bitmap_copy(struct qwz_pdev_wmi *wmi, + const uint32_t *wmi_svc_bm) +{ + int i, j = 0; + + for (i = 0; i < WMI_SERVICE_BM_SIZE && j < WMI_MAX_SERVICE; i++) { + do { + if (wmi_svc_bm[i] & BIT(j % WMI_SERVICE_BITS_IN_SIZE32)) + setbit(wmi->wmi->svc_map, j); + } while (++j % WMI_SERVICE_BITS_IN_SIZE32); + } +} + +int +qwz_wmi_tlv_svc_rdy_parse(struct qwz_softc *sc, uint16_t tag, uint16_t len, + const void *ptr, void *data) +{ + struct wmi_tlv_svc_ready_parse *svc_ready = data; + struct qwz_pdev_wmi *wmi_handle = &sc->wmi.wmi[0]; + uint16_t expect_len; + + switch (tag) { + case WMI_TAG_SERVICE_READY_EVENT: + if (qwz_pull_service_ready_tlv(sc, ptr, &sc->target_caps)) + return EINVAL; + break; + + case WMI_TAG_ARRAY_UINT32: + if (!svc_ready->wmi_svc_bitmap_done) { + expect_len = WMI_SERVICE_BM_SIZE * sizeof(uint32_t); + if (len < expect_len) { + printf("%s: invalid len %d for the tag 0x%x\n", + __func__, len, tag); + return EINVAL; + } + + qwz_wmi_service_bitmap_copy(wmi_handle, ptr); + + svc_ready->wmi_svc_bitmap_done = 1; + } + break; + default: + break; + } + + return 0; +} + +void +qwz_service_ready_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct wmi_tlv_svc_ready_parse svc_ready = { }; + int ret; + + ret = qwz_wmi_tlv_iter(sc, mtod(m, void *), m->m_pkthdr.len, + qwz_wmi_tlv_svc_rdy_parse, &svc_ready); + if (ret) { + printf("%s: failed to parse tlv %d\n", __func__, ret); + return; + } + + DNPRINTF(QWZ_D_WMI, "%s: event service ready\n", __func__); +} + +int +qwz_pull_svc_ready_ext(struct qwz_pdev_wmi *wmi_handle, const void *ptr, + struct ath12k_service_ext_param *param) +{ + const struct wmi_service_ready_ext_event *ev = ptr; + + if (!ev) + return EINVAL; + + /* Move this to host based bitmap */ + param->default_conc_scan_config_bits = ev->default_conc_scan_config_bits; + param->default_fw_config_bits = ev->default_fw_config_bits; + param->he_cap_info = ev->he_cap_info; + param->mpdu_density = ev->mpdu_density; + param->max_bssid_rx_filters = ev->max_bssid_rx_filters; + memcpy(¶m->ppet, &ev->ppet, sizeof(param->ppet)); + + return 0; +} + +int +qwz_pull_mac_phy_cap_svc_ready_ext(struct qwz_pdev_wmi *wmi_handle, + struct wmi_soc_mac_phy_hw_mode_caps *hw_caps, + struct wmi_hw_mode_capabilities *wmi_hw_mode_caps, + struct wmi_soc_hal_reg_capabilities *hal_reg_caps, + struct wmi_mac_phy_capabilities *wmi_mac_phy_caps, + uint8_t hw_mode_id, uint8_t phy_id, struct qwz_pdev *pdev) +{ + struct wmi_mac_phy_capabilities *mac_phy_caps; + struct qwz_softc *sc = wmi_handle->wmi->sc; + struct ath12k_band_cap *cap_band; + struct ath12k_pdev_cap *pdev_cap = &pdev->cap; + uint32_t phy_map; + uint32_t hw_idx, phy_idx = 0; + + if (!hw_caps || !wmi_hw_mode_caps || !hal_reg_caps) + return EINVAL; + + for (hw_idx = 0; hw_idx < hw_caps->num_hw_modes; hw_idx++) { + if (hw_mode_id == wmi_hw_mode_caps[hw_idx].hw_mode_id) + break; + + phy_map = wmi_hw_mode_caps[hw_idx].phy_id_map; + while (phy_map) { + phy_map >>= 1; + phy_idx++; + } + } + + if (hw_idx == hw_caps->num_hw_modes) + return EINVAL; + + phy_idx += phy_id; + if (phy_id >= hal_reg_caps->num_phy) + return EINVAL; + + mac_phy_caps = wmi_mac_phy_caps + phy_idx; + + pdev->pdev_id = mac_phy_caps->pdev_id; + pdev_cap->supported_bands |= mac_phy_caps->supported_bands; + pdev_cap->ampdu_density = mac_phy_caps->ampdu_density; + sc->target_pdev_ids[sc->target_pdev_count].supported_bands = + mac_phy_caps->supported_bands; + sc->target_pdev_ids[sc->target_pdev_count].pdev_id = mac_phy_caps->pdev_id; + sc->target_pdev_count++; + + if (!(mac_phy_caps->supported_bands & WMI_HOST_WLAN_2G_CAP) && + !(mac_phy_caps->supported_bands & WMI_HOST_WLAN_5G_CAP)) + return EINVAL; + + /* Take non-zero tx/rx chainmask. If tx/rx chainmask differs from + * band to band for a single radio, need to see how this should be + * handled. + */ + if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_2G_CAP) { + pdev_cap->tx_chain_mask = mac_phy_caps->tx_chain_mask_2g; + pdev_cap->rx_chain_mask = mac_phy_caps->rx_chain_mask_2g; + } + + if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_5G_CAP) { + pdev_cap->vht_cap = mac_phy_caps->vht_cap_info_5g; + pdev_cap->vht_mcs = mac_phy_caps->vht_supp_mcs_5g; + pdev_cap->he_mcs = mac_phy_caps->he_supp_mcs_5g; + pdev_cap->tx_chain_mask = mac_phy_caps->tx_chain_mask_5g; + pdev_cap->rx_chain_mask = mac_phy_caps->rx_chain_mask_5g; + pdev_cap->nss_ratio_enabled = + WMI_NSS_RATIO_ENABLE_DISABLE_GET(mac_phy_caps->nss_ratio); + pdev_cap->nss_ratio_info = + WMI_NSS_RATIO_INFO_GET(mac_phy_caps->nss_ratio); + } + + /* tx/rx chainmask reported from fw depends on the actual hw chains used, + * For example, for 4x4 capable macphys, first 4 chains can be used for first + * mac and the remaining 4 chains can be used for the second mac or vice-versa. + * In this case, tx/rx chainmask 0xf will be advertised for first mac and 0xf0 + * will be advertised for second mac or vice-versa. Compute the shift value + * for tx/rx chainmask which will be used to advertise supported ht/vht rates to + * mac80211. + */ + pdev_cap->tx_chain_mask_shift = ffs(pdev_cap->tx_chain_mask); + pdev_cap->rx_chain_mask_shift = ffs(pdev_cap->rx_chain_mask); + + if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_2G_CAP) { + cap_band = &pdev_cap->band[0]; + cap_band->phy_id = mac_phy_caps->phy_id; + cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_2g; + cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_2g; + cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_2g; + cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_2g_ext; + cap_band->he_mcs = mac_phy_caps->he_supp_mcs_2g; + memcpy(cap_band->he_cap_phy_info, + &mac_phy_caps->he_cap_phy_info_2g, + sizeof(uint32_t) * PSOC_HOST_MAX_PHY_SIZE); + memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet2g, + sizeof(struct ath12k_ppe_threshold)); + } + + if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_5G_CAP) { + cap_band = &pdev_cap->band[1]; + cap_band->phy_id = mac_phy_caps->phy_id; + cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_5g; + cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_5g; + cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_5g; + cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_5g_ext; + cap_band->he_mcs = mac_phy_caps->he_supp_mcs_5g; + memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_5g, + sizeof(uint32_t) * PSOC_HOST_MAX_PHY_SIZE); + memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g, + sizeof(struct ath12k_ppe_threshold)); +#if 0 + cap_band = &pdev_cap->band[NL80211_BAND_6GHZ]; + cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_5g; + cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_5g; + cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_5g; + cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_5g_ext; + cap_band->he_mcs = mac_phy_caps->he_supp_mcs_5g; + memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_5g, + sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE); + memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g, + sizeof(struct ath12k_ppe_threshold)); +#endif + } + + return 0; +} + +int +qwz_wmi_tlv_ext_soc_hal_reg_caps_parse(struct qwz_softc *sc, uint16_t len, + const void *ptr, void *data) +{ + struct qwz_pdev_wmi *wmi_handle = &sc->wmi.wmi[0]; + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + uint8_t hw_mode_id = svc_rdy_ext->pref_hw_mode_caps.hw_mode_id; + uint32_t phy_id_map; + int pdev_index = 0; + int ret; + + svc_rdy_ext->soc_hal_reg_caps = (struct wmi_soc_hal_reg_capabilities *)ptr; + svc_rdy_ext->param.num_phy = svc_rdy_ext->soc_hal_reg_caps->num_phy; + + sc->num_radios = 0; + sc->target_pdev_count = 0; + phy_id_map = svc_rdy_ext->pref_hw_mode_caps.phy_id_map; + + while (phy_id_map && sc->num_radios < MAX_RADIOS) { + ret = qwz_pull_mac_phy_cap_svc_ready_ext(wmi_handle, + svc_rdy_ext->hw_caps, + svc_rdy_ext->hw_mode_caps, + svc_rdy_ext->soc_hal_reg_caps, + svc_rdy_ext->mac_phy_caps, + hw_mode_id, sc->num_radios, &sc->pdevs[pdev_index]); + if (ret) { + printf("%s: failed to extract mac caps, idx: %d\n", + __func__, sc->num_radios); + return ret; + } + + sc->num_radios++; + + /* For QCA6390, save mac_phy capability in the same pdev */ + if (sc->hw_params.single_pdev_only) + pdev_index = 0; + else + pdev_index = sc->num_radios; + + /* TODO: mac_phy_cap prints */ + phy_id_map >>= 1; + } + + /* For QCA6390, set num_radios to 1 because host manages + * both 2G and 5G radio in one pdev. + * Set pdev_id = 0 and 0 means soc level. + */ + if (sc->hw_params.single_pdev_only) { + sc->num_radios = 1; + sc->pdevs[0].pdev_id = 0; + } + + return 0; +} + +int +qwz_wmi_tlv_hw_mode_caps_parse(struct qwz_softc *sc, uint16_t tag, uint16_t len, + const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + struct wmi_hw_mode_capabilities *hw_mode_cap; + uint32_t phy_map = 0; + + if (tag != WMI_TAG_HW_MODE_CAPABILITIES) + return EPROTO; + + if (svc_rdy_ext->n_hw_mode_caps >= svc_rdy_ext->param.num_hw_modes) + return ENOBUFS; + + hw_mode_cap = container_of(ptr, struct wmi_hw_mode_capabilities, + hw_mode_id); + svc_rdy_ext->n_hw_mode_caps++; + + phy_map = hw_mode_cap->phy_id_map; + while (phy_map) { + svc_rdy_ext->tot_phy_id++; + phy_map = phy_map >> 1; + } + + return 0; +} + +#define PRIMAP(_hw_mode_) \ + [_hw_mode_] = _hw_mode_##_PRI + +static const int qwz_hw_mode_pri_map[] = { + PRIMAP(WMI_HOST_HW_MODE_SINGLE), + PRIMAP(WMI_HOST_HW_MODE_DBS), + PRIMAP(WMI_HOST_HW_MODE_SBS_PASSIVE), + PRIMAP(WMI_HOST_HW_MODE_SBS), + PRIMAP(WMI_HOST_HW_MODE_DBS_SBS), + PRIMAP(WMI_HOST_HW_MODE_DBS_OR_SBS), + /* keep last */ + PRIMAP(WMI_HOST_HW_MODE_MAX), +}; + +int +qwz_wmi_tlv_hw_mode_caps(struct qwz_softc *sc, uint16_t len, + const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + struct wmi_hw_mode_capabilities *hw_mode_caps; + enum wmi_host_hw_mode_config_type mode, pref; + uint32_t i; + int ret; + + svc_rdy_ext->n_hw_mode_caps = 0; + svc_rdy_ext->hw_mode_caps = (struct wmi_hw_mode_capabilities *)ptr; + + ret = qwz_wmi_tlv_iter(sc, ptr, len, + qwz_wmi_tlv_hw_mode_caps_parse, svc_rdy_ext); + if (ret) { + printf("%s: failed to parse tlv %d\n", __func__, ret); + return ret; + } + + i = 0; + while (i < svc_rdy_ext->n_hw_mode_caps) { + hw_mode_caps = &svc_rdy_ext->hw_mode_caps[i]; + mode = hw_mode_caps->hw_mode_id; + pref = sc->wmi.preferred_hw_mode; + + if (qwz_hw_mode_pri_map[mode] < qwz_hw_mode_pri_map[pref]) { + svc_rdy_ext->pref_hw_mode_caps = *hw_mode_caps; + sc->wmi.preferred_hw_mode = mode; + } + i++; + } + + DNPRINTF(QWZ_D_WMI, "%s: preferred_hw_mode: %d\n", __func__, + sc->wmi.preferred_hw_mode); + if (sc->wmi.preferred_hw_mode >= WMI_HOST_HW_MODE_MAX) + return EINVAL; + + return 0; +} + +int +qwz_wmi_tlv_mac_phy_caps_parse(struct qwz_softc *sc, uint16_t tag, uint16_t len, + const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + + if (tag != WMI_TAG_MAC_PHY_CAPABILITIES) + return EPROTO; + + if (svc_rdy_ext->n_mac_phy_caps >= svc_rdy_ext->tot_phy_id) + return ENOBUFS; + + len = MIN(len, sizeof(struct wmi_mac_phy_capabilities)); + if (!svc_rdy_ext->n_mac_phy_caps) { + svc_rdy_ext->mac_phy_caps = mallocarray( + svc_rdy_ext->tot_phy_id, + sizeof(struct wmi_mac_phy_capabilities), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (!svc_rdy_ext->mac_phy_caps) + return ENOMEM; + svc_rdy_ext->mac_phy_caps_size = len * svc_rdy_ext->tot_phy_id; + } + + memcpy(svc_rdy_ext->mac_phy_caps + svc_rdy_ext->n_mac_phy_caps, + ptr, len); + svc_rdy_ext->n_mac_phy_caps++; + return 0; +} + +int +qwz_wmi_tlv_ext_hal_reg_caps_parse(struct qwz_softc *sc, + uint16_t tag, uint16_t len, const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + + if (tag != WMI_TAG_HAL_REG_CAPABILITIES_EXT) + return EPROTO; + + if (svc_rdy_ext->n_ext_hal_reg_caps >= svc_rdy_ext->param.num_phy) + return ENOBUFS; + + svc_rdy_ext->n_ext_hal_reg_caps++; + return 0; +} + +int +qwz_pull_reg_cap_svc_rdy_ext(struct qwz_pdev_wmi *wmi_handle, + struct wmi_soc_hal_reg_capabilities *reg_caps, + struct wmi_hal_reg_capabilities_ext *wmi_ext_reg_cap, + uint8_t phy_idx, struct ath12k_hal_reg_capabilities_ext *param) +{ + struct wmi_hal_reg_capabilities_ext *ext_reg_cap; + + if (!reg_caps || !wmi_ext_reg_cap) + return EINVAL; + + if (phy_idx >= reg_caps->num_phy) + return EINVAL; + + ext_reg_cap = &wmi_ext_reg_cap[phy_idx]; + + param->phy_id = ext_reg_cap->phy_id; + param->eeprom_reg_domain = ext_reg_cap->eeprom_reg_domain; + param->eeprom_reg_domain_ext = ext_reg_cap->eeprom_reg_domain_ext; + param->regcap1 = ext_reg_cap->regcap1; + param->regcap2 = ext_reg_cap->regcap2; + /* check if param->wireless_mode is needed */ + param->low_2ghz_chan = ext_reg_cap->low_2ghz_chan; + param->high_2ghz_chan = ext_reg_cap->high_2ghz_chan; + param->low_5ghz_chan = ext_reg_cap->low_5ghz_chan; + param->high_5ghz_chan = ext_reg_cap->high_5ghz_chan; + + return 0; +} + +int +qwz_wmi_tlv_ext_hal_reg_caps(struct qwz_softc *sc, uint16_t len, + const void *ptr, void *data) +{ + struct qwz_pdev_wmi *wmi_handle = &sc->wmi.wmi[0]; + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + struct ath12k_hal_reg_capabilities_ext reg_cap; + int ret; + uint32_t i; + + svc_rdy_ext->n_ext_hal_reg_caps = 0; + svc_rdy_ext->ext_hal_reg_caps = + (struct wmi_hal_reg_capabilities_ext *)ptr; + ret = qwz_wmi_tlv_iter(sc, ptr, len, + qwz_wmi_tlv_ext_hal_reg_caps_parse, svc_rdy_ext); + if (ret) { + printf("%s: failed to parse tlv %d\n", __func__, ret); + return ret; + } + + for (i = 0; i < svc_rdy_ext->param.num_phy; i++) { + ret = qwz_pull_reg_cap_svc_rdy_ext(wmi_handle, + svc_rdy_ext->soc_hal_reg_caps, + svc_rdy_ext->ext_hal_reg_caps, i, ®_cap); + if (ret) { + printf("%s: failed to extract reg cap %d\n", + __func__, i); + return ret; + } + + memcpy(&sc->hal_reg_cap[reg_cap.phy_id], ®_cap, + sizeof(sc->hal_reg_cap[0])); + } + + return 0; +} + +int +qwz_wmi_tlv_dma_ring_caps_parse(struct qwz_softc *sc, uint16_t tag, + uint16_t len, const void *ptr, void *data) +{ + struct wmi_tlv_dma_ring_caps_parse *parse = data; + + if (tag != WMI_TAG_DMA_RING_CAPABILITIES) + return EPROTO; + + parse->n_dma_ring_caps++; + return 0; +} + +int +qwz_wmi_alloc_dbring_caps(struct qwz_softc *sc, uint32_t num_cap) +{ + void *ptr; + + ptr = mallocarray(num_cap, sizeof(struct qwz_dbring_cap), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (!ptr) + return ENOMEM; + + sc->db_caps = ptr; + sc->num_db_cap = num_cap; + + return 0; +} + +void +qwz_wmi_free_dbring_caps(struct qwz_softc *sc) +{ + free(sc->db_caps, M_DEVBUF, + sc->num_db_cap * sizeof(struct qwz_dbring_cap)); + sc->db_caps = NULL; + sc->num_db_cap = 0; +} + +int +qwz_wmi_tlv_dma_ring_caps(struct qwz_softc *sc, uint16_t len, + const void *ptr, void *data) +{ + struct wmi_tlv_dma_ring_caps_parse *dma_caps_parse = data; + struct wmi_dma_ring_capabilities *dma_caps; + struct qwz_dbring_cap *dir_buff_caps; + int ret; + uint32_t i; + + dma_caps_parse->n_dma_ring_caps = 0; + dma_caps = (struct wmi_dma_ring_capabilities *)ptr; + ret = qwz_wmi_tlv_iter(sc, ptr, len, + qwz_wmi_tlv_dma_ring_caps_parse, dma_caps_parse); + if (ret) { + printf("%s: failed to parse dma ring caps tlv %d\n", + __func__, ret); + return ret; + } + + if (!dma_caps_parse->n_dma_ring_caps) + return 0; + + if (sc->num_db_cap) { + DNPRINTF(QWZ_D_WMI, + "%s: Already processed, so ignoring dma ring caps\n", + __func__); + return 0; + } + + ret = qwz_wmi_alloc_dbring_caps(sc, dma_caps_parse->n_dma_ring_caps); + if (ret) + return ret; + + dir_buff_caps = sc->db_caps; + for (i = 0; i < dma_caps_parse->n_dma_ring_caps; i++) { + if (dma_caps[i].module_id >= WMI_DIRECT_BUF_MAX) { + printf("%s: Invalid module id %d\n", __func__, + dma_caps[i].module_id); + ret = EINVAL; + goto free_dir_buff; + } + + dir_buff_caps[i].id = dma_caps[i].module_id; + dir_buff_caps[i].pdev_id = DP_HW2SW_MACID(dma_caps[i].pdev_id); + dir_buff_caps[i].min_elem = dma_caps[i].min_elem; + dir_buff_caps[i].min_buf_sz = dma_caps[i].min_buf_sz; + dir_buff_caps[i].min_buf_align = dma_caps[i].min_buf_align; + } + + return 0; + +free_dir_buff: + qwz_wmi_free_dbring_caps(sc); + return ret; +} + +int +qwz_wmi_tlv_svc_rdy_ext_parse(struct qwz_softc *sc, uint16_t tag, uint16_t len, + const void *ptr, void *data) +{ + struct qwz_pdev_wmi *wmi_handle = &sc->wmi.wmi[0]; + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + int ret; + + switch (tag) { + case WMI_TAG_SERVICE_READY_EXT_EVENT: + ret = qwz_pull_svc_ready_ext(wmi_handle, ptr, + &svc_rdy_ext->param); + if (ret) { + printf("%s: unable to extract ext params\n", __func__); + return ret; + } + break; + + case WMI_TAG_SOC_MAC_PHY_HW_MODE_CAPS: + svc_rdy_ext->hw_caps = (struct wmi_soc_mac_phy_hw_mode_caps *)ptr; + svc_rdy_ext->param.num_hw_modes = svc_rdy_ext->hw_caps->num_hw_modes; + break; + + case WMI_TAG_SOC_HAL_REG_CAPABILITIES: + ret = qwz_wmi_tlv_ext_soc_hal_reg_caps_parse(sc, len, ptr, + svc_rdy_ext); + if (ret) + return ret; + break; + + case WMI_TAG_ARRAY_STRUCT: + if (!svc_rdy_ext->hw_mode_done) { + ret = qwz_wmi_tlv_hw_mode_caps(sc, len, ptr, + svc_rdy_ext); + if (ret) + return ret; + + svc_rdy_ext->hw_mode_done = 1; + } else if (!svc_rdy_ext->mac_phy_done) { + svc_rdy_ext->n_mac_phy_caps = 0; + ret = qwz_wmi_tlv_iter(sc, ptr, len, + qwz_wmi_tlv_mac_phy_caps_parse, svc_rdy_ext); + if (ret) { + printf("%s: failed to parse tlv %d\n", + __func__, ret); + return ret; + } + + svc_rdy_ext->mac_phy_done = 1; + } else if (!svc_rdy_ext->ext_hal_reg_done) { + ret = qwz_wmi_tlv_ext_hal_reg_caps(sc, len, ptr, + svc_rdy_ext); + if (ret) + return ret; + + svc_rdy_ext->ext_hal_reg_done = 1; + } else if (!svc_rdy_ext->mac_phy_chainmask_combo_done) { + svc_rdy_ext->mac_phy_chainmask_combo_done = 1; + } else if (!svc_rdy_ext->mac_phy_chainmask_cap_done) { + svc_rdy_ext->mac_phy_chainmask_cap_done = 1; + } else if (!svc_rdy_ext->oem_dma_ring_cap_done) { + svc_rdy_ext->oem_dma_ring_cap_done = 1; + } else if (!svc_rdy_ext->dma_ring_cap_done) { + ret = qwz_wmi_tlv_dma_ring_caps(sc, len, ptr, + &svc_rdy_ext->dma_caps_parse); + if (ret) + return ret; + + svc_rdy_ext->dma_ring_cap_done = 1; + } + break; + + default: + break; + } + + return 0; +} + +void +qwz_service_ready_ext_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct wmi_tlv_svc_rdy_ext_parse svc_rdy_ext = { }; + int ret; + + ret = qwz_wmi_tlv_iter(sc, mtod(m, void *), m->m_pkthdr.len, + qwz_wmi_tlv_svc_rdy_ext_parse, &svc_rdy_ext); + if (ret) { + printf("%s: failed to parse tlv %d\n", __func__, ret); + qwz_wmi_free_dbring_caps(sc); + return; + } + + DNPRINTF(QWZ_D_WMI, "%s: event service ready ext\n", __func__); + + if (!isset(sc->wmi.svc_map, WMI_TLV_SERVICE_EXT2_MSG)) + wakeup(&sc->wmi.service_ready); + + free(svc_rdy_ext.mac_phy_caps, M_DEVBUF, + svc_rdy_ext.mac_phy_caps_size); +} + +int +qwz_wmi_tlv_svc_rdy_ext2_parse(struct qwz_softc *sc, + uint16_t tag, uint16_t len, const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext2_parse *parse = data; + int ret; + + switch (tag) { + case WMI_TAG_ARRAY_STRUCT: + if (!parse->dma_ring_cap_done) { + ret = qwz_wmi_tlv_dma_ring_caps(sc, len, ptr, + &parse->dma_caps_parse); + if (ret) + return ret; + + parse->dma_ring_cap_done = 1; + } + break; + default: + break; + } + + return 0; +} + +void +qwz_service_ready_ext2_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct wmi_tlv_svc_rdy_ext2_parse svc_rdy_ext2 = { }; + int ret; + + ret = qwz_wmi_tlv_iter(sc, mtod(m, void *), m->m_pkthdr.len, + qwz_wmi_tlv_svc_rdy_ext2_parse, &svc_rdy_ext2); + if (ret) { + printf("%s: failed to parse ext2 event tlv %d\n", + __func__, ret); + qwz_wmi_free_dbring_caps(sc); + return; + } + + DNPRINTF(QWZ_D_WMI, "%s: event service ready ext2\n", __func__); + + sc->wmi.service_ready = 1; + wakeup(&sc->wmi.service_ready); +} + +void +qwz_service_available_event(struct qwz_softc *sc, struct mbuf *m) +{ + int ret; + + ret = qwz_wmi_tlv_iter(sc, mtod(m, void *), m->m_pkthdr.len, + qwz_wmi_tlv_services_parser, NULL); + if (ret) + printf("%s: failed to parse services available tlv %d\n", + sc->sc_dev.dv_xname, ret); + + DNPRINTF(QWZ_D_WMI, "%s: event service available\n", __func__); +} + +int +qwz_pull_peer_assoc_conf_ev(struct qwz_softc *sc, struct mbuf *m, + struct wmi_peer_assoc_conf_arg *peer_assoc_conf) +{ + const void **tb; + const struct wmi_peer_assoc_conf_event *ev; + int ret; + + tb = qwz_wmi_tlv_parse_alloc(sc, mtod(m, void *), m->m_pkthdr.len); + if (tb == NULL) { + ret = ENOMEM; + printf("%s: failed to parse tlv: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ev = tb[WMI_TAG_PEER_ASSOC_CONF_EVENT]; + if (!ev) { + printf("%s: failed to fetch peer assoc conf ev\n", + sc->sc_dev.dv_xname); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EPROTO; + } + + peer_assoc_conf->vdev_id = ev->vdev_id; + peer_assoc_conf->macaddr = ev->peer_macaddr.addr; + + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return 0; +} + +void +qwz_peer_assoc_conf_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct wmi_peer_assoc_conf_arg peer_assoc_conf = {0}; + + if (qwz_pull_peer_assoc_conf_ev(sc, m, &peer_assoc_conf) != 0) { + printf("%s: failed to extract peer assoc conf event\n", + sc->sc_dev.dv_xname); + return; + } + + DNPRINTF(QWZ_D_WMI, "%s: event peer assoc conf ev vdev id %d " + "macaddr %s\n", __func__, peer_assoc_conf.vdev_id, + ether_sprintf((u_char *)peer_assoc_conf.macaddr)); + + sc->peer_assoc_done = 1; + wakeup(&sc->peer_assoc_done); +} + +int +qwz_wmi_tlv_rdy_parse(struct qwz_softc *sc, uint16_t tag, uint16_t len, + const void *ptr, void *data) +{ + struct wmi_tlv_rdy_parse *rdy_parse = data; + struct wmi_ready_event fixed_param; + struct wmi_mac_addr *addr_list; + struct qwz_pdev *pdev; + uint32_t num_mac_addr; + int i; + + switch (tag) { + case WMI_TAG_READY_EVENT: + memset(&fixed_param, 0, sizeof(fixed_param)); + memcpy(&fixed_param, (struct wmi_ready_event *)ptr, + MIN(sizeof(fixed_param), len)); + sc->wlan_init_status = fixed_param.ready_event_min.status; + rdy_parse->num_extra_mac_addr = + fixed_param.ready_event_min.num_extra_mac_addr; + + IEEE80211_ADDR_COPY(sc->mac_addr, + fixed_param.ready_event_min.mac_addr.addr); + sc->pktlog_defs_checksum = fixed_param.pktlog_defs_checksum; + sc->wmi_ready = 1; + break; + case WMI_TAG_ARRAY_FIXED_STRUCT: + addr_list = (struct wmi_mac_addr *)ptr; + num_mac_addr = rdy_parse->num_extra_mac_addr; + + if (!(sc->num_radios > 1 && num_mac_addr >= sc->num_radios)) + break; + + for (i = 0; i < sc->num_radios; i++) { + pdev = &sc->pdevs[i]; + IEEE80211_ADDR_COPY(pdev->mac_addr, addr_list[i].addr); + } + sc->pdevs_macaddr_valid = 1; + break; + default: + break; + } + + return 0; +} + +void +qwz_ready_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct wmi_tlv_rdy_parse rdy_parse = { }; + int ret; + + ret = qwz_wmi_tlv_iter(sc, mtod(m, void *), m->m_pkthdr.len, + qwz_wmi_tlv_rdy_parse, &rdy_parse); + if (ret) { + printf("%s: failed to parse tlv %d\n", __func__, ret); + return; + } + + DNPRINTF(QWZ_D_WMI, "%s: event ready", __func__); + + sc->wmi.unified_ready = 1; + wakeup(&sc->wmi.unified_ready); +} + +int +qwz_pull_peer_del_resp_ev(struct qwz_softc *sc, struct mbuf *m, + struct wmi_peer_delete_resp_event *peer_del_resp) +{ + const void **tb; + const struct wmi_peer_delete_resp_event *ev; + int ret; + + tb = qwz_wmi_tlv_parse_alloc(sc, mtod(m, void *), m->m_pkthdr.len); + if (tb == NULL) { + ret = ENOMEM; + printf("%s: failed to parse tlv: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ev = tb[WMI_TAG_PEER_DELETE_RESP_EVENT]; + if (!ev) { + printf("%s: failed to fetch peer delete resp ev\n", + sc->sc_dev.dv_xname); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EPROTO; + } + + memset(peer_del_resp, 0, sizeof(*peer_del_resp)); + + peer_del_resp->vdev_id = ev->vdev_id; + IEEE80211_ADDR_COPY(peer_del_resp->peer_macaddr.addr, + ev->peer_macaddr.addr); + + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return 0; +} + +void +qwz_peer_delete_resp_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct wmi_peer_delete_resp_event peer_del_resp; + + if (qwz_pull_peer_del_resp_ev(sc, m, &peer_del_resp) != 0) { + printf("%s: failed to extract peer delete resp", + sc->sc_dev.dv_xname); + return; + } + + sc->peer_delete_done = 1; + wakeup(&sc->peer_delete_done); + + DNPRINTF(QWZ_D_WMI, "%s: peer delete resp for vdev id %d addr %s\n", + __func__, peer_del_resp.vdev_id, + ether_sprintf(peer_del_resp.peer_macaddr.addr)); +} + +const char * +qwz_wmi_vdev_resp_print(uint32_t vdev_resp_status) +{ + switch (vdev_resp_status) { + case WMI_VDEV_START_RESPONSE_INVALID_VDEVID: + return "invalid vdev id"; + case WMI_VDEV_START_RESPONSE_NOT_SUPPORTED: + return "not supported"; + case WMI_VDEV_START_RESPONSE_DFS_VIOLATION: + return "dfs violation"; + case WMI_VDEV_START_RESPONSE_INVALID_REGDOMAIN: + return "invalid regdomain"; + default: + return "unknown"; + } +} + +int +qwz_pull_vdev_start_resp_tlv(struct qwz_softc *sc, struct mbuf *m, + struct wmi_vdev_start_resp_event *vdev_rsp) +{ + const void **tb; + const struct wmi_vdev_start_resp_event *ev; + int ret; + + tb = qwz_wmi_tlv_parse_alloc(sc, mtod(m, void *), m->m_pkthdr.len); + if (tb == NULL) { + ret = ENOMEM; + printf("%s: failed to parse tlv: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ev = tb[WMI_TAG_VDEV_START_RESPONSE_EVENT]; + if (!ev) { + printf("%s: failed to fetch vdev start resp ev\n", + sc->sc_dev.dv_xname); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EPROTO; + } + + memset(vdev_rsp, 0, sizeof(*vdev_rsp)); + + vdev_rsp->vdev_id = ev->vdev_id; + vdev_rsp->requestor_id = ev->requestor_id; + vdev_rsp->resp_type = ev->resp_type; + vdev_rsp->status = ev->status; + vdev_rsp->chain_mask = ev->chain_mask; + vdev_rsp->smps_mode = ev->smps_mode; + vdev_rsp->mac_id = ev->mac_id; + vdev_rsp->cfgd_tx_streams = ev->cfgd_tx_streams; + vdev_rsp->cfgd_rx_streams = ev->cfgd_rx_streams; + + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return 0; +} + +void +qwz_vdev_start_resp_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct wmi_vdev_start_resp_event vdev_start_resp; + uint32_t status; + + if (qwz_pull_vdev_start_resp_tlv(sc, m, &vdev_start_resp) != 0) { + printf("%s: failed to extract vdev start resp", + sc->sc_dev.dv_xname); + return; + } + + status = vdev_start_resp.status; + if (status) { + printf("%s: vdev start resp error status %d (%s)\n", + sc->sc_dev.dv_xname, status, + qwz_wmi_vdev_resp_print(status)); + } + + sc->vdev_setup_done = 1; + wakeup(&sc->vdev_setup_done); + + DNPRINTF(QWZ_D_WMI, "%s: vdev start resp for vdev id %d", __func__, + vdev_start_resp.vdev_id); +} + +int +qwz_pull_vdev_stopped_param_tlv(struct qwz_softc *sc, struct mbuf *m, + uint32_t *vdev_id) +{ + const void **tb; + const struct wmi_vdev_stopped_event *ev; + int ret; + + tb = qwz_wmi_tlv_parse_alloc(sc, mtod(m, void *), m->m_pkthdr.len); + if (tb == NULL) { + ret = ENOMEM; + printf("%s: failed to parse tlv: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ev = tb[WMI_TAG_VDEV_STOPPED_EVENT]; + if (!ev) { + printf("%s: failed to fetch vdev stop ev\n", + sc->sc_dev.dv_xname); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EPROTO; + } + + *vdev_id = ev->vdev_id; + + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return 0; +} + +void +qwz_vdev_stopped_event(struct qwz_softc *sc, struct mbuf *m) +{ + uint32_t vdev_id = 0; + + if (qwz_pull_vdev_stopped_param_tlv(sc, m, &vdev_id) != 0) { + printf("%s: failed to extract vdev stopped event\n", + sc->sc_dev.dv_xname); + return; + } + + sc->vdev_setup_done = 1; + wakeup(&sc->vdev_setup_done); + + DNPRINTF(QWZ_D_WMI, "%s: vdev stopped for vdev id %d", __func__, + vdev_id); +} + +int +qwz_wmi_tlv_iter_parse(struct qwz_softc *sc, uint16_t tag, uint16_t len, + const void *ptr, void *data) +{ + const void **tb = data; + + if (tag < WMI_TAG_MAX) + tb[tag] = ptr; + + return 0; +} + +int +qwz_wmi_tlv_parse(struct qwz_softc *sc, const void **tb, + const void *ptr, size_t len) +{ + return qwz_wmi_tlv_iter(sc, ptr, len, qwz_wmi_tlv_iter_parse, + (void *)tb); +} + +const void ** +qwz_wmi_tlv_parse_alloc(struct qwz_softc *sc, const void *ptr, size_t len) +{ + const void **tb; + int ret; + + tb = mallocarray(WMI_TAG_MAX, sizeof(*tb), M_DEVBUF, M_NOWAIT | M_ZERO); + if (!tb) + return NULL; + + ret = qwz_wmi_tlv_parse(sc, tb, ptr, len); + if (ret) { + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return NULL; + } + + return tb; +} + +static void +qwz_print_reg_rule(struct qwz_softc *sc, const char *band, + uint32_t num_reg_rules, struct cur_reg_rule *reg_rule_ptr) +{ + struct cur_reg_rule *reg_rule = reg_rule_ptr; + uint32_t count; + + DNPRINTF(QWZ_D_WMI, "%s: number of reg rules in %s band: %d\n", + __func__, band, num_reg_rules); + + for (count = 0; count < num_reg_rules; count++) { + DNPRINTF(QWZ_D_WMI, + "%s: reg rule %d: (%d - %d @ %d) (%d, %d) (FLAGS %d)\n", + __func__, count + 1, reg_rule->start_freq, + reg_rule->end_freq, reg_rule->max_bw, reg_rule->ant_gain, + reg_rule->reg_power, reg_rule->flags); + reg_rule++; + } +} + +struct cur_reg_rule * +qwz_create_reg_rules_from_wmi(uint32_t num_reg_rules, + struct wmi_regulatory_rule_struct *wmi_reg_rule) +{ + struct cur_reg_rule *reg_rule_ptr; + uint32_t count; + + reg_rule_ptr = mallocarray(num_reg_rules, sizeof(*reg_rule_ptr), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (!reg_rule_ptr) + return NULL; + + for (count = 0; count < num_reg_rules; count++) { + reg_rule_ptr[count].start_freq = FIELD_GET(REG_RULE_START_FREQ, + wmi_reg_rule[count].freq_info); + reg_rule_ptr[count].end_freq = FIELD_GET(REG_RULE_END_FREQ, + wmi_reg_rule[count].freq_info); + reg_rule_ptr[count].max_bw = FIELD_GET(REG_RULE_MAX_BW, + wmi_reg_rule[count].bw_pwr_info); + reg_rule_ptr[count].reg_power = FIELD_GET(REG_RULE_REG_PWR, + wmi_reg_rule[count].bw_pwr_info); + reg_rule_ptr[count].ant_gain = FIELD_GET(REG_RULE_ANT_GAIN, + wmi_reg_rule[count].bw_pwr_info); + reg_rule_ptr[count].flags = FIELD_GET(REG_RULE_FLAGS, + wmi_reg_rule[count].flag_info); + } + + return reg_rule_ptr; +} + +int +qwz_pull_reg_chan_list_update_ev(struct qwz_softc *sc, struct mbuf *m, + struct cur_regulatory_info *reg_info) +{ + const void **tb; + const struct wmi_reg_chan_list_cc_event *chan_list_event_hdr; + struct wmi_regulatory_rule_struct *wmi_reg_rule; + uint32_t num_2ghz_reg_rules, num_5ghz_reg_rules; + int ret; + + DNPRINTF(QWZ_D_WMI, "%s: processing regulatory channel list\n", + __func__); + + tb = qwz_wmi_tlv_parse_alloc(sc, mtod(m, void *), m->m_pkthdr.len); + if (tb == NULL) { + ret = ENOMEM; /* XXX allocation failure or parsing failure? */ + printf("%s: failed to parse tlv: %d\n", __func__, ret); + return ENOMEM; + } + + chan_list_event_hdr = tb[WMI_TAG_REG_CHAN_LIST_CC_EVENT]; + if (!chan_list_event_hdr) { + printf("%s: failed to fetch reg chan list update ev\n", + __func__); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EPROTO; + } + + reg_info->num_2ghz_reg_rules = chan_list_event_hdr->num_2ghz_reg_rules; + reg_info->num_5ghz_reg_rules = chan_list_event_hdr->num_5ghz_reg_rules; + + if (!(reg_info->num_2ghz_reg_rules + reg_info->num_5ghz_reg_rules)) { + printf("%s: No regulatory rules available in the event info\n", + __func__); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EINVAL; + } + + memcpy(reg_info->alpha2, &chan_list_event_hdr->alpha2, REG_ALPHA2_LEN); + reg_info->dfs_region = chan_list_event_hdr->dfs_region; + reg_info->phybitmap = chan_list_event_hdr->phybitmap; + reg_info->num_phy = chan_list_event_hdr->num_phy; + reg_info->phy_id = chan_list_event_hdr->phy_id; + reg_info->ctry_code = chan_list_event_hdr->country_id; + reg_info->reg_dmn_pair = chan_list_event_hdr->domain_code; + + DNPRINTF(QWZ_D_WMI, "%s: CC status_code %s\n", __func__, + qwz_cc_status_to_str(reg_info->status_code)); + + reg_info->status_code = + qwz_wmi_cc_setting_code_to_reg(chan_list_event_hdr->status_code); + + reg_info->is_ext_reg_event = false; + + reg_info->min_bw_2ghz = chan_list_event_hdr->min_bw_2ghz; + reg_info->max_bw_2ghz = chan_list_event_hdr->max_bw_2ghz; + reg_info->min_bw_5ghz = chan_list_event_hdr->min_bw_5ghz; + reg_info->max_bw_5ghz = chan_list_event_hdr->max_bw_5ghz; + + num_2ghz_reg_rules = reg_info->num_2ghz_reg_rules; + num_5ghz_reg_rules = reg_info->num_5ghz_reg_rules; + + DNPRINTF(QWZ_D_WMI, + "%s: cc %s dsf %d BW: min_2ghz %d max_2ghz %d min_5ghz %d " + "max_5ghz %d\n", __func__, reg_info->alpha2, reg_info->dfs_region, + reg_info->min_bw_2ghz, reg_info->max_bw_2ghz, + reg_info->min_bw_5ghz, reg_info->max_bw_5ghz); + + DNPRINTF(QWZ_D_WMI, + "%s: num_2ghz_reg_rules %d num_5ghz_reg_rules %d\n", __func__, + num_2ghz_reg_rules, num_5ghz_reg_rules); + + wmi_reg_rule = (struct wmi_regulatory_rule_struct *) + ((uint8_t *)chan_list_event_hdr + sizeof(*chan_list_event_hdr) + + sizeof(struct wmi_tlv)); + + if (num_2ghz_reg_rules) { + reg_info->reg_rules_2ghz_ptr = qwz_create_reg_rules_from_wmi( + num_2ghz_reg_rules, wmi_reg_rule); + if (!reg_info->reg_rules_2ghz_ptr) { + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + printf("%s: Unable to allocate memory for " + "2 GHz rules\n", __func__); + return ENOMEM; + } + + qwz_print_reg_rule(sc, "2 GHz", num_2ghz_reg_rules, + reg_info->reg_rules_2ghz_ptr); + } + + if (num_5ghz_reg_rules) { + wmi_reg_rule += num_2ghz_reg_rules; + reg_info->reg_rules_5ghz_ptr = qwz_create_reg_rules_from_wmi( + num_5ghz_reg_rules, wmi_reg_rule); + if (!reg_info->reg_rules_5ghz_ptr) { + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + printf("%s: Unable to allocate memory for " + "5 GHz rules\n", __func__); + return ENOMEM; + } + + qwz_print_reg_rule(sc, "5 GHz", num_5ghz_reg_rules, + reg_info->reg_rules_5ghz_ptr); + } + + DNPRINTF(QWZ_D_WMI, "%s: processed regulatory channel list\n", + __func__); + + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return 0; +} + +int +qwz_pull_reg_chan_list_ext_update_ev(struct qwz_softc *sc, struct mbuf *m, + struct cur_regulatory_info *reg_info) +{ + printf("%s: not implemented\n", __func__); + return ENOTSUP; +} + +void +qwz_init_channels(struct qwz_softc *sc, struct cur_regulatory_info *reg_info) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_channel *chan; + struct cur_reg_rule *rule; + int i, chnum; + uint16_t freq; + + for (i = 0; i < reg_info->num_2ghz_reg_rules; i++) { + rule = ®_info->reg_rules_2ghz_ptr[i]; + if (rule->start_freq < 2402 || + rule->start_freq > 2500 || + rule->start_freq > rule->end_freq) { + DPRINTF("%s: bad regulatory rule: start freq %u, " + "end freq %u\n", __func__, rule->start_freq, + rule->end_freq); + continue; + } + + freq = rule->start_freq + 10; + chnum = ieee80211_mhz2ieee(freq, IEEE80211_CHAN_2GHZ); + if (chnum < 1 || chnum > 14) { + DPRINTF("%s: bad regulatory rule: freq %u, " + "channel %u\n", __func__, freq, chnum); + continue; + } + while (freq <= rule->end_freq && chnum <= 14) { + chan = &ic->ic_channels[chnum]; + if (rule->flags & REGULATORY_CHAN_DISABLED) { + chan->ic_freq = 0; + chan->ic_flags = 0; + } else { + chan->ic_freq = freq; + chan->ic_flags = IEEE80211_CHAN_CCK | + IEEE80211_CHAN_OFDM | + IEEE80211_CHAN_DYN | + IEEE80211_CHAN_2GHZ; + } + chnum++; + freq = ieee80211_ieee2mhz(chnum, IEEE80211_CHAN_2GHZ); + } + } + + for (i = 0; i < reg_info->num_5ghz_reg_rules; i++) { + rule = ®_info->reg_rules_5ghz_ptr[i]; + if (rule->start_freq < 5170 || + rule->start_freq > 6000 || + rule->start_freq > rule->end_freq) { + DPRINTF("%s: bad regulatory rule: start freq %u, " + "end freq %u\n", __func__, rule->start_freq, + rule->end_freq); + continue; + } + + freq = rule->start_freq + 10; + chnum = ieee80211_mhz2ieee(freq, IEEE80211_CHAN_5GHZ); + if (chnum < 36 || chnum > IEEE80211_CHAN_MAX) { + DPRINTF("%s: bad regulatory rule: freq %u, " + "channel %u\n", __func__, freq, chnum); + continue; + } + while (freq <= rule->end_freq && freq <= 5885 && + chnum <= IEEE80211_CHAN_MAX) { + chan = &ic->ic_channels[chnum]; + if (rule->flags & (REGULATORY_CHAN_DISABLED | + REGULATORY_CHAN_NO_OFDM)) { + chan->ic_freq = 0; + chan->ic_flags = 0; + } else { + chan->ic_freq = freq; + chan->ic_flags = IEEE80211_CHAN_A; + if (rule->flags & (REGULATORY_CHAN_RADAR | + REGULATORY_CHAN_NO_IR | + REGULATORY_CHAN_INDOOR_ONLY)) { + chan->ic_flags |= + IEEE80211_CHAN_PASSIVE; + } + } + chnum += 4; + freq = ieee80211_ieee2mhz(chnum, IEEE80211_CHAN_5GHZ); + } + } +} + +int +qwz_reg_chan_list_event(struct qwz_softc *sc, struct mbuf *m, + enum wmi_reg_chan_list_cmd_type id) +{ + struct cur_regulatory_info *reg_info = NULL; + int ret = 0; +#if 0 + struct ieee80211_regdomain *regd = NULL; + bool intersect = false; + int pdev_idx, i, j; + struct ath12k *ar; +#endif + + reg_info = malloc(sizeof(*reg_info), M_DEVBUF, M_NOWAIT | M_ZERO); + if (!reg_info) { + ret = ENOMEM; + goto fallback; + } + + if (id == WMI_REG_CHAN_LIST_CC_ID) + ret = qwz_pull_reg_chan_list_update_ev(sc, m, reg_info); + else + ret = qwz_pull_reg_chan_list_ext_update_ev(sc, m, reg_info); + + if (ret) { + printf("%s: failed to extract regulatory info from " + "received event\n", sc->sc_dev.dv_xname); + goto fallback; + } + + DNPRINTF(QWZ_D_WMI, "%s: event reg chan list id %d\n", __func__, id); + + if (reg_info->status_code != REG_SET_CC_STATUS_PASS) { + /* In case of failure to set the requested ctry, + * fw retains the current regd. We print a failure info + * and return from here. + */ + printf("%s: Failed to set the requested Country " + "regulatory setting\n", __func__); + goto mem_free; + } + + qwz_init_channels(sc, reg_info); +#if 0 + pdev_idx = reg_info->phy_id; + + /* Avoid default reg rule updates sent during FW recovery if + * it is already available + */ + spin_lock(&ab->base_lock); + if (test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags) && + ab->default_regd[pdev_idx]) { + spin_unlock(&ab->base_lock); + goto mem_free; + } + spin_unlock(&ab->base_lock); + + if (pdev_idx >= ab->num_radios) { + /* Process the event for phy0 only if single_pdev_only + * is true. If pdev_idx is valid but not 0, discard the + * event. Otherwise, it goes to fallback. + */ + if (ab->hw_params.single_pdev_only && + pdev_idx < ab->hw_params.num_rxmda_per_pdev) + goto mem_free; + else + goto fallback; + } + + /* Avoid multiple overwrites to default regd, during core + * stop-start after mac registration. + */ + if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] && + !memcmp((char *)ab->default_regd[pdev_idx]->alpha2, + (char *)reg_info->alpha2, 2)) + goto mem_free; + + /* Intersect new rules with default regd if a new country setting was + * requested, i.e a default regd was already set during initialization + * and the regd coming from this event has a valid country info. + */ + if (ab->default_regd[pdev_idx] && + !ath12k_reg_is_world_alpha((char *) + ab->default_regd[pdev_idx]->alpha2) && + !ath12k_reg_is_world_alpha((char *)reg_info->alpha2)) + intersect = true; + + regd = ath12k_reg_build_regd(ab, reg_info, intersect); + if (!regd) { + ath12k_warn(ab, "failed to build regd from reg_info\n"); + goto fallback; + } + + spin_lock(&ab->base_lock); + if (ab->default_regd[pdev_idx]) { + /* The initial rules from FW after WMI Init is to build + * the default regd. From then on, any rules updated for + * the pdev could be due to user reg changes. + * Free previously built regd before assigning the newly + * generated regd to ar. NULL pointer handling will be + * taken care by kfree itself. + */ + ar = ab->pdevs[pdev_idx].ar; + kfree(ab->new_regd[pdev_idx]); + ab->new_regd[pdev_idx] = regd; + queue_work(ab->workqueue, &ar->regd_update_work); + } else { + /* This regd would be applied during mac registration and is + * held constant throughout for regd intersection purpose + */ + ab->default_regd[pdev_idx] = regd; + } + ab->dfs_region = reg_info->dfs_region; + spin_unlock(&ab->base_lock); +#endif + goto mem_free; + +fallback: + /* Fallback to older reg (by sending previous country setting + * again if fw has succeeded and we failed to process here. + * The Regdomain should be uniform across driver and fw. Since the + * FW has processed the command and sent a success status, we expect + * this function to succeed as well. If it doesn't, CTRY needs to be + * reverted at the fw and the old SCAN_CHAN_LIST cmd needs to be sent. + */ + /* TODO: This is rare, but still should also be handled */ +mem_free: + if (reg_info) { + free(reg_info->reg_rules_2ghz_ptr, M_DEVBUF, + reg_info->num_2ghz_reg_rules * + sizeof(*reg_info->reg_rules_2ghz_ptr)); + free(reg_info->reg_rules_5ghz_ptr, M_DEVBUF, + reg_info->num_5ghz_reg_rules * + sizeof(*reg_info->reg_rules_5ghz_ptr)); +#if 0 + if (reg_info->is_ext_reg_event) { + for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) + kfree(reg_info->reg_rules_6ghz_ap_ptr[i]); + + for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++) + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) + kfree(reg_info->reg_rules_6ghz_client_ptr[j][i]); + } +#endif + free(reg_info, M_DEVBUF, sizeof(*reg_info)); + } + return ret; +} + +const char * +qwz_wmi_event_scan_type_str(enum wmi_scan_event_type type, + enum wmi_scan_completion_reason reason) +{ + switch (type) { + case WMI_SCAN_EVENT_STARTED: + return "started"; + case WMI_SCAN_EVENT_COMPLETED: + switch (reason) { + case WMI_SCAN_REASON_COMPLETED: + return "completed"; + case WMI_SCAN_REASON_CANCELLED: + return "completed [cancelled]"; + case WMI_SCAN_REASON_PREEMPTED: + return "completed [preempted]"; + case WMI_SCAN_REASON_TIMEDOUT: + return "completed [timedout]"; + case WMI_SCAN_REASON_INTERNAL_FAILURE: + return "completed [internal err]"; + case WMI_SCAN_REASON_MAX: + break; + } + return "completed [unknown]"; + case WMI_SCAN_EVENT_BSS_CHANNEL: + return "bss channel"; + case WMI_SCAN_EVENT_FOREIGN_CHAN: + return "foreign channel"; + case WMI_SCAN_EVENT_DEQUEUED: + return "dequeued"; + case WMI_SCAN_EVENT_PREEMPTED: + return "preempted"; + case WMI_SCAN_EVENT_START_FAILED: + return "start failed"; + case WMI_SCAN_EVENT_RESTARTED: + return "restarted"; + case WMI_SCAN_EVENT_FOREIGN_CHAN_EXIT: + return "foreign channel exit"; + default: + return "unknown"; + } +} + +const char * +qwz_scan_state_str(enum ath12k_scan_state state) +{ + switch (state) { + case ATH12K_SCAN_IDLE: + return "idle"; + case ATH12K_SCAN_STARTING: + return "starting"; + case ATH12K_SCAN_RUNNING: + return "running"; + case ATH12K_SCAN_ABORTING: + return "aborting"; + } + + return "unknown"; +} + +int +qwz_pull_scan_ev(struct qwz_softc *sc, struct mbuf *m, + struct wmi_scan_event *scan_evt_param) +{ + const void **tb; + const struct wmi_scan_event *ev; + + tb = qwz_wmi_tlv_parse_alloc(sc, mtod(m, void *), m->m_pkthdr.len); + if (tb == NULL) { + DPRINTF("%s: failed to parse tlv\n", __func__); + return EINVAL; + } + + ev = tb[WMI_TAG_SCAN_EVENT]; + if (!ev) { + DPRINTF("%s: failed to fetch scan ev\n", __func__); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EPROTO; + } + + scan_evt_param->event_type = ev->event_type; + scan_evt_param->reason = ev->reason; + scan_evt_param->channel_freq = ev->channel_freq; + scan_evt_param->scan_req_id = ev->scan_req_id; + scan_evt_param->scan_id = ev->scan_id; + scan_evt_param->vdev_id = ev->vdev_id; + scan_evt_param->tsf_timestamp = ev->tsf_timestamp; + + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return 0; +} + +void +qwz_wmi_event_scan_started(struct qwz_softc *sc) +{ +#ifdef notyet + lockdep_assert_held(&ar->data_lock); +#endif + switch (sc->scan.state) { + case ATH12K_SCAN_IDLE: + case ATH12K_SCAN_RUNNING: + case ATH12K_SCAN_ABORTING: + printf("%s: received scan started event in an invalid " + "scan state: %s (%d)\n", sc->sc_dev.dv_xname, + qwz_scan_state_str(sc->scan.state), sc->scan.state); + break; + case ATH12K_SCAN_STARTING: + sc->scan.state = ATH12K_SCAN_RUNNING; +#if 0 + if (ar->scan.is_roc) + ieee80211_ready_on_channel(ar->hw); +#endif + wakeup(&sc->scan.state); + break; + } +} + +void +qwz_wmi_event_scan_completed(struct qwz_softc *sc) +{ +#ifdef notyet + lockdep_assert_held(&ar->data_lock); +#endif + switch (sc->scan.state) { + case ATH12K_SCAN_IDLE: + case ATH12K_SCAN_STARTING: + /* One suspected reason scan can be completed while starting is + * if firmware fails to deliver all scan events to the host, + * e.g. when transport pipe is full. This has been observed + * with spectral scan phyerr events starving wmi transport + * pipe. In such case the "scan completed" event should be (and + * is) ignored by the host as it may be just firmware's scan + * state machine recovering. + */ + printf("%s: received scan completed event in an invalid " + "scan state: %s (%d)\n", sc->sc_dev.dv_xname, + qwz_scan_state_str(sc->scan.state), sc->scan.state); + break; + case ATH12K_SCAN_RUNNING: + case ATH12K_SCAN_ABORTING: + qwz_mac_scan_finish(sc); + break; + } +} + +void +qwz_wmi_event_scan_bss_chan(struct qwz_softc *sc) +{ +#ifdef notyet + lockdep_assert_held(&ar->data_lock); +#endif + switch (sc->scan.state) { + case ATH12K_SCAN_IDLE: + case ATH12K_SCAN_STARTING: + printf("%s: received scan bss chan event in an invalid " + "scan state: %s (%d)\n", sc->sc_dev.dv_xname, + qwz_scan_state_str(sc->scan.state), sc->scan.state); + break; + case ATH12K_SCAN_RUNNING: + case ATH12K_SCAN_ABORTING: + sc->scan_channel = 0; + break; + } +} + +void +qwz_wmi_event_scan_foreign_chan(struct qwz_softc *sc, uint32_t freq) +{ +#ifdef notyet + lockdep_assert_held(&ar->data_lock); +#endif + switch (sc->scan.state) { + case ATH12K_SCAN_IDLE: + case ATH12K_SCAN_STARTING: + printf("%s: received scan foreign chan event in an invalid " + "scan state: %s (%d)\n", sc->sc_dev.dv_xname, + qwz_scan_state_str(sc->scan.state), sc->scan.state); + break; + case ATH12K_SCAN_RUNNING: + case ATH12K_SCAN_ABORTING: + sc->scan_channel = ieee80211_mhz2ieee(freq, 0); +#if 0 + if (ar->scan.is_roc && ar->scan.roc_freq == freq) + complete(&ar->scan.on_channel); +#endif + break; + } +} + +void +qwz_wmi_event_scan_start_failed(struct qwz_softc *sc) +{ +#ifdef notyet + lockdep_assert_held(&ar->data_lock); +#endif + switch (sc->scan.state) { + case ATH12K_SCAN_IDLE: + case ATH12K_SCAN_RUNNING: + case ATH12K_SCAN_ABORTING: + printf("%s: received scan start failed event in an invalid " + "scan state: %s (%d)\n", sc->sc_dev.dv_xname, + qwz_scan_state_str(sc->scan.state), sc->scan.state); + break; + case ATH12K_SCAN_STARTING: + wakeup(&sc->scan.state); + qwz_mac_scan_finish(sc); + break; + } +} + + +void +qwz_scan_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct wmi_scan_event scan_ev = { 0 }; + struct qwz_vif *arvif; + + if (qwz_pull_scan_ev(sc, m, &scan_ev) != 0) { + printf("%s: failed to extract scan event", + sc->sc_dev.dv_xname); + return; + } +#ifdef notyet + rcu_read_lock(); +#endif + TAILQ_FOREACH(arvif, &sc->vif_list, entry) { + if (arvif->vdev_id == scan_ev.vdev_id) + break; + } + + if (!arvif) { + printf("%s: received scan event for unknown vdev\n", + sc->sc_dev.dv_xname); +#if 0 + rcu_read_unlock(); +#endif + return; + } +#if 0 + spin_lock_bh(&ar->data_lock); +#endif + DNPRINTF(QWZ_D_WMI, + "%s: event scan %s type %d reason %d freq %d req_id %d scan_id %d " + "vdev_id %d state %s (%d)\n", __func__, + qwz_wmi_event_scan_type_str(scan_ev.event_type, scan_ev.reason), + scan_ev.event_type, scan_ev.reason, scan_ev.channel_freq, + scan_ev.scan_req_id, scan_ev.scan_id, scan_ev.vdev_id, + qwz_scan_state_str(sc->scan.state), sc->scan.state); + + switch (scan_ev.event_type) { + case WMI_SCAN_EVENT_STARTED: + qwz_wmi_event_scan_started(sc); + break; + case WMI_SCAN_EVENT_COMPLETED: + qwz_wmi_event_scan_completed(sc); + break; + case WMI_SCAN_EVENT_BSS_CHANNEL: + qwz_wmi_event_scan_bss_chan(sc); + break; + case WMI_SCAN_EVENT_FOREIGN_CHAN: + qwz_wmi_event_scan_foreign_chan(sc, scan_ev.channel_freq); + break; + case WMI_SCAN_EVENT_START_FAILED: + printf("%s: received scan start failure event\n", + sc->sc_dev.dv_xname); + qwz_wmi_event_scan_start_failed(sc); + break; + case WMI_SCAN_EVENT_DEQUEUED: + qwz_mac_scan_finish(sc); + break; + case WMI_SCAN_EVENT_PREEMPTED: + case WMI_SCAN_EVENT_RESTARTED: + case WMI_SCAN_EVENT_FOREIGN_CHAN_EXIT: + default: + break; + } +#if 0 + spin_unlock_bh(&ar->data_lock); + + rcu_read_unlock(); +#endif +} + +int +qwz_pull_chan_info_ev(struct qwz_softc *sc, uint8_t *evt_buf, uint32_t len, + struct wmi_chan_info_event *ch_info_ev) +{ + const void **tb; + const struct wmi_chan_info_event *ev; + + tb = qwz_wmi_tlv_parse_alloc(sc, evt_buf, len); + if (tb == NULL) { + printf("%s: failed to parse tlv\n", sc->sc_dev.dv_xname); + return EINVAL; + } + + ev = tb[WMI_TAG_CHAN_INFO_EVENT]; + if (!ev) { + printf("%s: failed to fetch chan info ev\n", + sc->sc_dev.dv_xname); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EPROTO; + } + + ch_info_ev->err_code = ev->err_code; + ch_info_ev->freq = ev->freq; + ch_info_ev->cmd_flags = ev->cmd_flags; + ch_info_ev->noise_floor = ev->noise_floor; + ch_info_ev->rx_clear_count = ev->rx_clear_count; + ch_info_ev->cycle_count = ev->cycle_count; + ch_info_ev->chan_tx_pwr_range = ev->chan_tx_pwr_range; + ch_info_ev->chan_tx_pwr_tp = ev->chan_tx_pwr_tp; + ch_info_ev->rx_frame_count = ev->rx_frame_count; + ch_info_ev->tx_frame_cnt = ev->tx_frame_cnt; + ch_info_ev->mac_clk_mhz = ev->mac_clk_mhz; + ch_info_ev->vdev_id = ev->vdev_id; + + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return 0; +} + +void +qwz_chan_info_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct qwz_vif *arvif; + struct wmi_chan_info_event ch_info_ev = {0}; + struct qwz_survey_info *survey; + int idx; + /* HW channel counters frequency value in hertz */ + uint32_t cc_freq_hz = sc->cc_freq_hz; + + if (qwz_pull_chan_info_ev(sc, mtod(m, void *), m->m_pkthdr.len, + &ch_info_ev) != 0) { + printf("%s: failed to extract chan info event\n", + sc->sc_dev.dv_xname); + return; + } + + DNPRINTF(QWZ_D_WMI, "%s: event chan info vdev_id %d err_code %d " + "freq %d cmd_flags %d noise_floor %d rx_clear_count %d " + "cycle_count %d mac_clk_mhz %d\n", __func__, + ch_info_ev.vdev_id, ch_info_ev.err_code, ch_info_ev.freq, + ch_info_ev.cmd_flags, ch_info_ev.noise_floor, + ch_info_ev.rx_clear_count, ch_info_ev.cycle_count, + ch_info_ev.mac_clk_mhz); + + if (ch_info_ev.cmd_flags == WMI_CHAN_INFO_END_RESP) { + DNPRINTF(QWZ_D_WMI, "chan info report completed\n"); + return; + } +#ifdef notyet + rcu_read_lock(); +#endif + TAILQ_FOREACH(arvif, &sc->vif_list, entry) { + if (arvif->vdev_id == ch_info_ev.vdev_id) + break; + } + if (!arvif) { + printf("%s: invalid vdev id in chan info ev %d\n", + sc->sc_dev.dv_xname, ch_info_ev.vdev_id); +#ifdef notyet + rcu_read_unlock(); +#endif + return; + } +#ifdef notyet + spin_lock_bh(&ar->data_lock); +#endif + switch (sc->scan.state) { + case ATH12K_SCAN_IDLE: + case ATH12K_SCAN_STARTING: + printf("%s: received chan info event without a scan request, " + "ignoring\n", sc->sc_dev.dv_xname); + goto exit; + case ATH12K_SCAN_RUNNING: + case ATH12K_SCAN_ABORTING: + break; + } + + idx = ieee80211_mhz2ieee(ch_info_ev.freq, 0); + if (idx >= nitems(sc->survey)) { + printf("%s: invalid frequency %d (idx %d out of bounds)\n", + sc->sc_dev.dv_xname, ch_info_ev.freq, idx); + goto exit; + } + + /* If FW provides MAC clock frequency in Mhz, overriding the initialized + * HW channel counters frequency value + */ + if (ch_info_ev.mac_clk_mhz) + cc_freq_hz = (ch_info_ev.mac_clk_mhz * 1000); + + if (ch_info_ev.cmd_flags == WMI_CHAN_INFO_START_RESP) { + survey = &sc->survey[idx]; + memset(survey, 0, sizeof(*survey)); + survey->noise = ch_info_ev.noise_floor; + survey->time = ch_info_ev.cycle_count / cc_freq_hz; + survey->time_busy = ch_info_ev.rx_clear_count / cc_freq_hz; + } +exit: +#ifdef notyet + spin_unlock_bh(&ar->data_lock); + rcu_read_unlock(); +#else + return; +#endif +} + +int +qwz_wmi_tlv_mgmt_rx_parse(struct qwz_softc *sc, uint16_t tag, uint16_t len, + const void *ptr, void *data) +{ + struct wmi_tlv_mgmt_rx_parse *parse = data; + + switch (tag) { + case WMI_TAG_MGMT_RX_HDR: + parse->fixed = ptr; + break; + case WMI_TAG_ARRAY_BYTE: + if (!parse->frame_buf_done) { + parse->frame_buf = ptr; + parse->frame_buf_done = 1; + } + break; + } + return 0; +} + +int +qwz_pull_mgmt_rx_params_tlv(struct qwz_softc *sc, struct mbuf *m, + struct mgmt_rx_event_params *hdr) +{ + struct wmi_tlv_mgmt_rx_parse parse = { 0 }; + const struct wmi_mgmt_rx_hdr *ev; + const uint8_t *frame; + int ret; + size_t totlen, hdrlen; + + ret = qwz_wmi_tlv_iter(sc, mtod(m, void *), m->m_pkthdr.len, + qwz_wmi_tlv_mgmt_rx_parse, &parse); + if (ret) { + printf("%s: failed to parse mgmt rx tlv %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ev = parse.fixed; + frame = parse.frame_buf; + + if (!ev || !frame) { + printf("%s: failed to fetch mgmt rx hdr\n", + sc->sc_dev.dv_xname); + return EPROTO; + } + + hdr->pdev_id = ev->pdev_id; + hdr->chan_freq = le32toh(ev->chan_freq); + hdr->channel = le32toh(ev->channel); + hdr->snr = le32toh(ev->snr); + hdr->rate = le32toh(ev->rate); + hdr->phy_mode = le32toh(ev->phy_mode); + hdr->buf_len = le32toh(ev->buf_len); + hdr->status = le32toh(ev->status); + hdr->flags = le32toh(ev->flags); + hdr->rssi = le32toh(ev->rssi); + hdr->tsf_delta = le32toh(ev->tsf_delta); + memcpy(hdr->rssi_ctl, ev->rssi_ctl, sizeof(hdr->rssi_ctl)); + + if (frame < mtod(m, uint8_t *) || + frame >= mtod(m, uint8_t *) + m->m_pkthdr.len) { + printf("%s: invalid mgmt rx frame pointer\n", + sc->sc_dev.dv_xname); + return EPROTO; + } + hdrlen = frame - mtod(m, uint8_t *); + + if (hdrlen + hdr->buf_len < hdr->buf_len) { + printf("%s: length overflow in mgmt rx hdr ev\n", + sc->sc_dev.dv_xname); + return EPROTO; + } + totlen = hdrlen + hdr->buf_len; + if (m->m_pkthdr.len < totlen) { + printf("%s: invalid length in mgmt rx hdr ev\n", + sc->sc_dev.dv_xname); + return EPROTO; + } + + /* shift the mbuf to point at `frame` */ + m->m_len = m->m_pkthdr.len = totlen; + m_adj(m, hdrlen); + +#if 0 /* Not needed on OpenBSD? */ + ath12k_ce_byte_swap(skb->data, hdr->buf_len); +#endif + return 0; +} + +void +qwz_mgmt_rx_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct mgmt_rx_event_params rx_ev = {0}; + struct ieee80211_rxinfo rxi; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + + if (qwz_pull_mgmt_rx_params_tlv(sc, m, &rx_ev) != 0) { + printf("%s: failed to extract mgmt rx event\n", + sc->sc_dev.dv_xname); + m_freem(m); + return; + } + + memset(&rxi, 0, sizeof(rxi)); + + DNPRINTF(QWZ_D_MGMT, "%s: event mgmt rx status %08x\n", __func__, + rx_ev.status); +#ifdef notyet + rcu_read_lock(); +#endif + if (rx_ev.pdev_id >= nitems(sc->pdevs)) { + printf("%s: invalid pdev_id %d in mgmt_rx_event\n", + sc->sc_dev.dv_xname, rx_ev.pdev_id); + m_freem(m); + goto exit; + } + + if ((test_bit(ATH12K_CAC_RUNNING, sc->sc_flags)) || + (rx_ev.status & (WMI_RX_STATUS_ERR_DECRYPT | + WMI_RX_STATUS_ERR_KEY_CACHE_MISS | WMI_RX_STATUS_ERR_CRC))) { + m_freem(m); + goto exit; + } + + if (rx_ev.status & WMI_RX_STATUS_ERR_MIC) { + ic->ic_stats.is_ccmp_dec_errs++; + m_freem(m); + goto exit; + } + + rxi.rxi_chan = rx_ev.channel; + rxi.rxi_rssi = rx_ev.snr + ATH12K_DEFAULT_NOISE_FLOOR; +#if 0 + status->rate_idx = ath12k_mac_bitrate_to_idx(sband, rx_ev.rate / 100); +#endif + + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, wh); +#if 0 + /* In case of PMF, FW delivers decrypted frames with Protected Bit set. + * Don't clear that. Also, FW delivers broadcast management frames + * (ex: group privacy action frames in mesh) as encrypted payload. + */ + if (ieee80211_has_protected(hdr->frame_control) && + !is_multicast_ether_addr(ieee80211_get_DA(hdr))) { + status->flag |= RX_FLAG_DECRYPTED; + + if (!ieee80211_is_robust_mgmt_frame(skb)) { + status->flag |= RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; + hdr->frame_control = __cpu_to_le16(fc & + ~IEEE80211_FCTL_PROTECTED); + } + } + + if (ieee80211_is_beacon(hdr->frame_control)) + ath12k_mac_handle_beacon(ar, skb); +#endif + + DNPRINTF(QWZ_D_MGMT, + "%s: event mgmt rx skb %p len %d ftype %02x stype %02x\n", + __func__, m, m->m_pkthdr.len, + wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + + DNPRINTF(QWZ_D_MGMT, "%s: event mgmt rx freq %d chan %d snr %d\n", + __func__, rx_ev.chan_freq, rx_ev.channel, rx_ev.snr); + +#if NBPFILTER > 0 + if (sc->sc_drvbpf != NULL) { + struct qwz_rx_radiotap_header *tap = &sc->sc_rxtap; + + bpf_mtap_hdr(sc->sc_drvbpf, tap, sc->sc_rxtap_len, + m, BPF_DIRECTION_IN); + } +#endif + ieee80211_input(ifp, m, ni, &rxi); + ieee80211_release_node(ic, ni); +exit: +#ifdef notyet + rcu_read_unlock(); +#else + return; +#endif +} + +int +qwz_pull_mgmt_tx_compl_param_tlv(struct qwz_softc *sc, struct mbuf *m, + struct wmi_mgmt_tx_compl_event *param) +{ + const void **tb; + const struct wmi_mgmt_tx_compl_event *ev; + int ret = 0; + + tb = qwz_wmi_tlv_parse_alloc(sc, mtod(m, void *), m->m_pkthdr.len); + if (tb == NULL) { + ret = ENOMEM; + printf("%s: failed to parse tlv: %d\n", + sc->sc_dev.dv_xname, ret); + return ENOMEM; + } + + ev = tb[WMI_TAG_MGMT_TX_COMPL_EVENT]; + if (!ev) { + printf("%s: failed to fetch mgmt tx compl ev\n", + sc->sc_dev.dv_xname); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EPROTO; + } + + param->pdev_id = ev->pdev_id; + param->desc_id = ev->desc_id; + param->status = ev->status; + param->ack_rssi = ev->ack_rssi; + + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return 0; +} + +void +qwz_wmi_process_mgmt_tx_comp(struct qwz_softc *sc, + struct wmi_mgmt_tx_compl_event *tx_compl_param) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct qwz_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */ + struct ifnet *ifp = &ic->ic_if; + struct qwz_tx_data *tx_data; + + if (tx_compl_param->desc_id >= nitems(arvif->txmgmt.data)) { + printf("%s: received mgmt tx compl for invalid buf_id: %d\n", + sc->sc_dev.dv_xname, tx_compl_param->desc_id); + return; + } + + tx_data = &arvif->txmgmt.data[tx_compl_param->desc_id]; + if (tx_data->m == NULL) { + printf("%s: received mgmt tx compl for invalid buf_id: %d\n", + sc->sc_dev.dv_xname, tx_compl_param->desc_id); + return; + } + + bus_dmamap_unload(sc->sc_dmat, tx_data->map); + m_freem(tx_data->m); + tx_data->m = NULL; + + ieee80211_release_node(ic, tx_data->ni); + tx_data->ni = NULL; + + if (arvif->txmgmt.queued > 0) + arvif->txmgmt.queued--; + + if (tx_compl_param->status != 0) + ifp->if_oerrors++; + + if (arvif->txmgmt.queued < nitems(arvif->txmgmt.data) - 1) { + sc->qfullmsk &= ~(1U << QWZ_MGMT_QUEUE_ID); + if (sc->qfullmsk == 0 && ifq_is_oactive(&ifp->if_snd)) { + ifq_clr_oactive(&ifp->if_snd); + (*ifp->if_start)(ifp); + } + } +} + +void +qwz_mgmt_tx_compl_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct wmi_mgmt_tx_compl_event tx_compl_param = { 0 }; + + if (qwz_pull_mgmt_tx_compl_param_tlv(sc, m, &tx_compl_param) != 0) { + printf("%s: failed to extract mgmt tx compl event\n", + sc->sc_dev.dv_xname); + return; + } + + qwz_wmi_process_mgmt_tx_comp(sc, &tx_compl_param); + + DNPRINTF(QWZ_D_MGMT, "%s: event mgmt tx compl ev pdev_id %d, " + "desc_id %d, status %d ack_rssi %d", __func__, + tx_compl_param.pdev_id, tx_compl_param.desc_id, + tx_compl_param.status, tx_compl_param.ack_rssi); +} + +int +qwz_pull_roam_ev(struct qwz_softc *sc, struct mbuf *m, + struct wmi_roam_event *roam_ev) +{ + const void **tb; + const struct wmi_roam_event *ev; + int ret; + + tb = qwz_wmi_tlv_parse_alloc(sc, mtod(m, void *), m->m_pkthdr.len); + if (tb == NULL) { + ret = ENOMEM; + printf("%s: failed to parse tlv: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ev = tb[WMI_TAG_ROAM_EVENT]; + if (!ev) { + printf("%s: failed to fetch roam ev\n", + sc->sc_dev.dv_xname); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EPROTO; + } + + roam_ev->vdev_id = ev->vdev_id; + roam_ev->reason = ev->reason; + roam_ev->rssi = ev->rssi; + + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return 0; +} + +void +qwz_mac_handle_beacon_miss(struct qwz_softc *sc, uint32_t vdev_id) +{ + struct ieee80211com *ic = &sc->sc_ic; + + if ((ic->ic_opmode != IEEE80211_M_STA) || + (ic->ic_state != IEEE80211_S_RUN)) + return; + + if (ic->ic_mgt_timer == 0) { + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: receiving no beacons from %s; checking if " + "this AP is still responding to probe requests\n", + sc->sc_dev.dv_xname, + ether_sprintf(ic->ic_bss->ni_macaddr)); + /* + * Rather than go directly to scan state, try to send a + * directed probe request first. If that fails then the + * state machine will drop us into scanning after timing + * out waiting for a probe response. + */ + IEEE80211_SEND_MGMT(ic, ic->ic_bss, + IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); + } +} + +void +qwz_roam_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct wmi_roam_event roam_ev = {}; + + if (qwz_pull_roam_ev(sc, m, &roam_ev) != 0) { + printf("%s: failed to extract roam event\n", + sc->sc_dev.dv_xname); + return; + } + + DNPRINTF(QWZ_D_WMI, "%s: event roam vdev %u reason 0x%08x rssi %d\n", + __func__, roam_ev.vdev_id, roam_ev.reason, roam_ev.rssi); + + if (roam_ev.reason >= WMI_ROAM_REASON_MAX) + return; + + switch (roam_ev.reason) { + case WMI_ROAM_REASON_BEACON_MISS: + qwz_mac_handle_beacon_miss(sc, roam_ev.vdev_id); + break; + case WMI_ROAM_REASON_BETTER_AP: + case WMI_ROAM_REASON_LOW_RSSI: + case WMI_ROAM_REASON_SUITABLE_AP_FOUND: + case WMI_ROAM_REASON_HO_FAILED: + break; + } +} + +int +qwz_pull_vdev_install_key_compl_ev(struct qwz_softc *sc, struct mbuf *m, + struct wmi_vdev_install_key_complete_arg *arg) +{ + const void **tb; + const struct wmi_vdev_install_key_compl_event *ev; + int ret; + + tb = qwz_wmi_tlv_parse_alloc(sc, mtod(m, void *), m->m_pkthdr.len); + if (tb == NULL) { + ret = ENOMEM; + printf("%s: failed to parse tlv: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ev = tb[WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT]; + if (!ev) { + printf("%s: failed to fetch vdev install key compl ev\n", + sc->sc_dev.dv_xname); + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return EPROTO; + } + + arg->vdev_id = ev->vdev_id; + arg->macaddr = ev->peer_macaddr.addr; + arg->key_idx = ev->key_idx; + arg->key_flags = ev->key_flags; + arg->status = ev->status; + + free(tb, M_DEVBUF, WMI_TAG_MAX * sizeof(*tb)); + return 0; +} + +void +qwz_vdev_install_key_compl_event(struct qwz_softc *sc, struct mbuf *m) +{ + struct wmi_vdev_install_key_complete_arg install_key_compl = { 0 }; + struct qwz_vif *arvif; + + if (qwz_pull_vdev_install_key_compl_ev(sc, m, + &install_key_compl) != 0) { + printf("%s: failed to extract install key compl event\n", + sc->sc_dev.dv_xname); + return; + } + + DNPRINTF(QWZ_D_WMI, "%s: event vdev install key ev idx %d flags %08x " + "macaddr %s status %d\n", __func__, install_key_compl.key_idx, + install_key_compl.key_flags, + ether_sprintf((u_char *)install_key_compl.macaddr), + install_key_compl.status); + + TAILQ_FOREACH(arvif, &sc->vif_list, entry) { + if (arvif->vdev_id == install_key_compl.vdev_id) + break; + } + if (!arvif) { + printf("%s: invalid vdev id in install key compl ev %d\n", + sc->sc_dev.dv_xname, install_key_compl.vdev_id); + return; + } + + sc->install_key_status = 0; + + if (install_key_compl.status != + WMI_VDEV_INSTALL_KEY_COMPL_STATUS_SUCCESS) { + printf("%s: install key failed for %s status %d\n", + sc->sc_dev.dv_xname, + ether_sprintf((u_char *)install_key_compl.macaddr), + install_key_compl.status); + sc->install_key_status = install_key_compl.status; + } + + sc->install_key_done = 1; + wakeup(&sc->install_key_done); +} + +void +qwz_wmi_tlv_op_rx(struct qwz_softc *sc, struct mbuf *m) +{ + struct wmi_cmd_hdr *cmd_hdr; + enum wmi_tlv_event_id id; + + cmd_hdr = mtod(m, struct wmi_cmd_hdr *); + id = FIELD_GET(WMI_CMD_HDR_CMD_ID, (cmd_hdr->cmd_id)); + + m_adj(m, sizeof(struct wmi_cmd_hdr)); + + switch (id) { + /* Process all the WMI events here */ + case WMI_SERVICE_READY_EVENTID: + qwz_service_ready_event(sc, m); + break; + case WMI_SERVICE_READY_EXT_EVENTID: + qwz_service_ready_ext_event(sc, m); + break; + case WMI_SERVICE_READY_EXT2_EVENTID: + qwz_service_ready_ext2_event(sc, m); + break; + case WMI_REG_CHAN_LIST_CC_EVENTID: + qwz_reg_chan_list_event(sc, m, WMI_REG_CHAN_LIST_CC_ID); + break; + case WMI_REG_CHAN_LIST_CC_EXT_EVENTID: + qwz_reg_chan_list_event(sc, m, WMI_REG_CHAN_LIST_CC_EXT_ID); + break; + case WMI_READY_EVENTID: + qwz_ready_event(sc, m); + break; + case WMI_PEER_DELETE_RESP_EVENTID: + qwz_peer_delete_resp_event(sc, m); + break; + case WMI_VDEV_START_RESP_EVENTID: + qwz_vdev_start_resp_event(sc, m); + break; +#if 0 + case WMI_OFFLOAD_BCN_TX_STATUS_EVENTID: + ath12k_bcn_tx_status_event(ab, skb); + break; +#endif + case WMI_VDEV_STOPPED_EVENTID: + qwz_vdev_stopped_event(sc, m); + break; + case WMI_MGMT_RX_EVENTID: + qwz_mgmt_rx_event(sc, m); + /* mgmt_rx_event() owns the skb now! */ + return; + case WMI_MGMT_TX_COMPLETION_EVENTID: + qwz_mgmt_tx_compl_event(sc, m); + break; + case WMI_SCAN_EVENTID: + qwz_scan_event(sc, m); + break; +#if 0 + case WMI_PEER_STA_KICKOUT_EVENTID: + ath12k_peer_sta_kickout_event(ab, skb); + break; +#endif + case WMI_ROAM_EVENTID: + qwz_roam_event(sc, m); + break; + case WMI_CHAN_INFO_EVENTID: + qwz_chan_info_event(sc, m); + break; +#if 0 + case WMI_PDEV_BSS_CHAN_INFO_EVENTID: + ath12k_pdev_bss_chan_info_event(ab, skb); + break; +#endif + case WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID: + qwz_vdev_install_key_compl_event(sc, m); + break; + case WMI_SERVICE_AVAILABLE_EVENTID: + qwz_service_available_event(sc, m); + break; + case WMI_PEER_ASSOC_CONF_EVENTID: + qwz_peer_assoc_conf_event(sc, m); + break; + case WMI_UPDATE_STATS_EVENTID: + /* ignore */ + break; +#if 0 + case WMI_PDEV_CTL_FAILSAFE_CHECK_EVENTID: + ath12k_pdev_ctl_failsafe_check_event(ab, skb); + break; + case WMI_PDEV_CSA_SWITCH_COUNT_STATUS_EVENTID: + ath12k_wmi_pdev_csa_switch_count_status_event(ab, skb); + break; + case WMI_PDEV_UTF_EVENTID: + ath12k_tm_wmi_event(ab, id, skb); + break; + case WMI_PDEV_TEMPERATURE_EVENTID: + ath12k_wmi_pdev_temperature_event(ab, skb); + break; + case WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID: + ath12k_wmi_pdev_dma_ring_buf_release_event(ab, skb); + break; + case WMI_HOST_FILS_DISCOVERY_EVENTID: + ath12k_fils_discovery_event(ab, skb); + break; + case WMI_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID: + ath12k_probe_resp_tx_status_event(ab, skb); + break; + case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID: + ath12k_wmi_obss_color_collision_event(ab, skb); + break; + case WMI_TWT_ADD_DIALOG_EVENTID: + ath12k_wmi_twt_add_dialog_event(ab, skb); + break; + case WMI_PDEV_DFS_RADAR_DETECTION_EVENTID: + ath12k_wmi_pdev_dfs_radar_detected_event(ab, skb); + break; + case WMI_VDEV_DELETE_RESP_EVENTID: + ath12k_vdev_delete_resp_event(ab, skb); + break; + case WMI_WOW_WAKEUP_HOST_EVENTID: + ath12k_wmi_event_wow_wakeup_host(ab, skb); + break; + case WMI_11D_NEW_COUNTRY_EVENTID: + ath12k_reg_11d_new_cc_event(ab, skb); + break; +#endif + case WMI_DIAG_EVENTID: + /* Ignore. These events trigger tracepoints in Linux. */ + break; +#if 0 + case WMI_PEER_STA_PS_STATECHG_EVENTID: + ath12k_wmi_event_peer_sta_ps_state_chg(ab, skb); + break; + case WMI_GTK_OFFLOAD_STATUS_EVENTID: + ath12k_wmi_gtk_offload_status_event(ab, skb); + break; +#endif + case WMI_UPDATE_FW_MEM_DUMP_EVENTID: + DPRINTF("%s: 0x%x: update fw mem dump\n", __func__, id); + break; + case WMI_PDEV_SET_HW_MODE_RESP_EVENTID: + DPRINTF("%s: 0x%x: set HW mode response event\n", __func__, id); + break; + case WMI_WLAN_FREQ_AVOID_EVENTID: + DPRINTF("%s: 0x%x: wlan freq avoid event\n", __func__, id); + break; + default: + DPRINTF("%s: unsupported event id 0x%x\n", __func__, id); + break; + } + + m_freem(m); +} + +void +qwz_wmi_op_ep_tx_credits(struct qwz_softc *sc) +{ + struct qwz_htc *htc = &sc->htc; + int i; + + /* try to send pending beacons first. they take priority */ + sc->wmi.tx_credits = 1; + wakeup(&sc->wmi.tx_credits); + + if (!sc->hw_params.credit_flow) + return; + + for (i = ATH12K_HTC_EP_0; i < ATH12K_HTC_EP_COUNT; i++) { + struct qwz_htc_ep *ep = &htc->endpoint[i]; + if (ep->tx_credit_flow_enabled && ep->tx_credits > 0) + wakeup(&ep->tx_credits); + } +} + +int +qwz_connect_pdev_htc_service(struct qwz_softc *sc, uint32_t pdev_idx) +{ + int status; + uint32_t svc_id[] = { ATH12K_HTC_SVC_ID_WMI_CONTROL, + ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1, + ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2 }; + struct qwz_htc_svc_conn_req conn_req; + struct qwz_htc_svc_conn_resp conn_resp; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + /* these fields are the same for all service endpoints */ + conn_req.ep_ops.ep_tx_complete = qwz_wmi_htc_tx_complete; + conn_req.ep_ops.ep_rx_complete = qwz_wmi_tlv_op_rx; + conn_req.ep_ops.ep_tx_credits = qwz_wmi_op_ep_tx_credits; + + /* connect to control service */ + conn_req.service_id = svc_id[pdev_idx]; + + status = qwz_htc_connect_service(&sc->htc, &conn_req, &conn_resp); + if (status) { + printf("%s: failed to connect to WMI CONTROL service " + "status: %d\n", sc->sc_dev.dv_xname, status); + return status; + } + + sc->wmi.wmi_endpoint_id[pdev_idx] = conn_resp.eid; + sc->wmi.wmi[pdev_idx].eid = conn_resp.eid; + sc->wmi.max_msg_len[pdev_idx] = conn_resp.max_msg_len; + sc->wmi.wmi[pdev_idx].tx_ce_desc = 0; + + return 0; +} + +int +qwz_wmi_connect(struct qwz_softc *sc) +{ + uint32_t i; + uint8_t wmi_ep_count; + + wmi_ep_count = sc->htc.wmi_ep_count; + if (wmi_ep_count > sc->hw_params.max_radios) + return -1; + + for (i = 0; i < wmi_ep_count; i++) + qwz_connect_pdev_htc_service(sc, i); + + return 0; +} + +void +qwz_htc_reset_endpoint_states(struct qwz_htc *htc) +{ + struct qwz_htc_ep *ep; + int i; + + for (i = ATH12K_HTC_EP_0; i < ATH12K_HTC_EP_COUNT; i++) { + ep = &htc->endpoint[i]; + ep->service_id = ATH12K_HTC_SVC_ID_UNUSED; + ep->max_ep_message_len = 0; + ep->max_tx_queue_depth = 0; + ep->eid = i; + ep->htc = htc; + ep->tx_credit_flow_enabled = 1; + } +} + +void +qwz_htc_control_tx_complete(struct qwz_softc *sc, struct mbuf *m) +{ + printf("%s: not implemented\n", __func__); + + m_freem(m); +} + +void +qwz_htc_control_rx_complete(struct qwz_softc *sc, struct mbuf *m) +{ + printf("%s: not implemented\n", __func__); + + m_freem(m); +} + +uint8_t +qwz_htc_get_credit_allocation(struct qwz_htc *htc, uint16_t service_id) +{ + uint8_t i, allocation = 0; + + for (i = 0; i < ATH12K_HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) { + if (htc->service_alloc_table[i].service_id == service_id) { + allocation = + htc->service_alloc_table[i].credit_allocation; + } + } + + return allocation; +} + +const char * +qwz_htc_service_name(enum ath12k_htc_svc_id id) +{ + switch (id) { + case ATH12K_HTC_SVC_ID_RESERVED: + return "Reserved"; + case ATH12K_HTC_SVC_ID_RSVD_CTRL: + return "Control"; + case ATH12K_HTC_SVC_ID_WMI_CONTROL: + return "WMI"; + case ATH12K_HTC_SVC_ID_WMI_DATA_BE: + return "DATA BE"; + case ATH12K_HTC_SVC_ID_WMI_DATA_BK: + return "DATA BK"; + case ATH12K_HTC_SVC_ID_WMI_DATA_VI: + return "DATA VI"; + case ATH12K_HTC_SVC_ID_WMI_DATA_VO: + return "DATA VO"; + case ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1: + return "WMI MAC1"; + case ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2: + return "WMI MAC2"; + case ATH12K_HTC_SVC_ID_NMI_CONTROL: + return "NMI Control"; + case ATH12K_HTC_SVC_ID_NMI_DATA: + return "NMI Data"; + case ATH12K_HTC_SVC_ID_HTT_DATA_MSG: + return "HTT Data"; + case ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS: + return "RAW"; + case ATH12K_HTC_SVC_ID_IPA_TX: + return "IPA TX"; + case ATH12K_HTC_SVC_ID_PKT_LOG: + return "PKT LOG"; + } + + return "Unknown"; +} + +struct mbuf * +qwz_htc_alloc_mbuf(size_t payload_size) +{ + struct mbuf *m; + size_t size = sizeof(struct ath12k_htc_hdr) + payload_size; + + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) + return NULL; + + if (size <= MCLBYTES) + MCLGET(m, M_DONTWAIT); + else + MCLGETL(m, M_DONTWAIT, size); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return NULL; + } + + m->m_len = m->m_pkthdr.len = size; + memset(mtod(m, void *), 0, size); + + return m; +} + +struct mbuf * +qwz_htc_build_tx_ctrl_mbuf(void) +{ + size_t size; + + size = ATH12K_HTC_CONTROL_BUFFER_SIZE - sizeof(struct ath12k_htc_hdr); + + return qwz_htc_alloc_mbuf(size); +} + +void +qwz_htc_prepare_tx_mbuf(struct qwz_htc_ep *ep, struct mbuf *m) +{ + struct ath12k_htc_hdr *hdr; + + hdr = mtod(m, struct ath12k_htc_hdr *); + + memset(hdr, 0, sizeof(*hdr)); + hdr->htc_info = FIELD_PREP(HTC_HDR_ENDPOINTID, ep->eid) | + FIELD_PREP(HTC_HDR_PAYLOADLEN, (m->m_pkthdr.len - sizeof(*hdr))); + + if (ep->tx_credit_flow_enabled) + hdr->htc_info |= FIELD_PREP(HTC_HDR_FLAGS, + ATH12K_HTC_FLAG_NEED_CREDIT_UPDATE); +#ifdef notyet + spin_lock_bh(&ep->htc->tx_lock); +#endif + hdr->ctrl_info = FIELD_PREP(HTC_HDR_CONTROLBYTES1, ep->seq_no++); +#ifdef notyet + spin_unlock_bh(&ep->htc->tx_lock); +#endif +} + +int +qwz_htc_send(struct qwz_htc *htc, enum ath12k_htc_ep_id eid, struct mbuf *m) +{ + struct qwz_htc_ep *ep = &htc->endpoint[eid]; + struct qwz_softc *sc = htc->sc; + struct qwz_ce_pipe *pipe = &sc->ce.ce_pipe[ep->ul_pipe_id]; + void *ctx; + struct qwz_tx_data *tx_data; + int credits = 0; + int ret; + int credit_flow_enabled = (sc->hw_params.credit_flow && + ep->tx_credit_flow_enabled); + + if (eid >= ATH12K_HTC_EP_COUNT) { + printf("%s: Invalid endpoint id: %d\n", __func__, eid); + return ENOENT; + } + + if (credit_flow_enabled) { + credits = howmany(m->m_pkthdr.len, htc->target_credit_size); +#ifdef notyet + spin_lock_bh(&htc->tx_lock); +#endif + if (ep->tx_credits < credits) { + DNPRINTF(QWZ_D_HTC, + "%s: ep %d insufficient credits required %d " + "total %d\n", __func__, eid, credits, + ep->tx_credits); +#ifdef notyet + spin_unlock_bh(&htc->tx_lock); +#endif + return EAGAIN; + } + ep->tx_credits -= credits; + DNPRINTF(QWZ_D_HTC, "%s: ep %d credits consumed %d total %d\n", + __func__, eid, credits, ep->tx_credits); +#ifdef notyet + spin_unlock_bh(&htc->tx_lock); +#endif + } + + qwz_htc_prepare_tx_mbuf(ep, m); + + ctx = pipe->src_ring->per_transfer_context[pipe->src_ring->write_index]; + tx_data = (struct qwz_tx_data *)ctx; + + tx_data->eid = eid; + ret = bus_dmamap_load_mbuf(sc->sc_dmat, tx_data->map, + m, BUS_DMA_WRITE | BUS_DMA_NOWAIT); + if (ret) { + printf("%s: can't map mbuf (error %d)\n", + sc->sc_dev.dv_xname, ret); + if (ret != ENOBUFS) + m_freem(m); + goto err_credits; + } + + DNPRINTF(QWZ_D_HTC, "%s: tx mbuf %p eid %d paddr %lx\n", + __func__, m, tx_data->eid, tx_data->map->dm_segs[0].ds_addr); +#ifdef QWZ_DEBUG + { + int i; + uint8_t *p = mtod(m, uint8_t *); + DNPRINTF(QWZ_D_HTC, "%s message buffer:", __func__); + for (i = 0; i < m->m_pkthdr.len; i++) { + DNPRINTF(QWZ_D_HTC, "%s %.2x", + i % 16 == 0 ? "\n" : "", p[i]); + } + if (i % 16) + DNPRINTF(QWZ_D_HTC, "\n"); + } +#endif + ret = qwz_ce_send(htc->sc, m, ep->ul_pipe_id, ep->eid); + if (ret) + goto err_unmap; + + return 0; + +err_unmap: + bus_dmamap_unload(sc->sc_dmat, tx_data->map); +err_credits: + if (credit_flow_enabled) { +#ifdef notyet + spin_lock_bh(&htc->tx_lock); +#endif + ep->tx_credits += credits; + DNPRINTF(QWZ_D_HTC, "%s: ep %d credits reverted %d total %d\n", + __func__, eid, credits, ep->tx_credits); +#ifdef notyet + spin_unlock_bh(&htc->tx_lock); +#endif + + if (ep->ep_ops.ep_tx_credits) + ep->ep_ops.ep_tx_credits(htc->sc); + } + return ret; +} + +int +qwz_htc_connect_service(struct qwz_htc *htc, + struct qwz_htc_svc_conn_req *conn_req, + struct qwz_htc_svc_conn_resp *conn_resp) +{ + struct qwz_softc *sc = htc->sc; + struct ath12k_htc_conn_svc *req_msg; + struct ath12k_htc_conn_svc_resp resp_msg_dummy; + struct ath12k_htc_conn_svc_resp *resp_msg = &resp_msg_dummy; + enum ath12k_htc_ep_id assigned_eid = ATH12K_HTC_EP_COUNT; + struct qwz_htc_ep *ep; + struct mbuf *m; + unsigned int max_msg_size = 0; + int length, status = 0; + int disable_credit_flow_ctrl = 0; + uint16_t flags = 0; + uint16_t message_id, service_id; + uint8_t tx_alloc = 0; + + /* special case for HTC pseudo control service */ + if (conn_req->service_id == ATH12K_HTC_SVC_ID_RSVD_CTRL) { + disable_credit_flow_ctrl = 1; + assigned_eid = ATH12K_HTC_EP_0; + max_msg_size = ATH12K_HTC_MAX_CTRL_MSG_LEN; + memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy)); + goto setup; + } + + tx_alloc = qwz_htc_get_credit_allocation(htc, conn_req->service_id); + if (!tx_alloc) + DNPRINTF(QWZ_D_HTC, + "%s: htc service %s does not allocate target credits\n", + sc->sc_dev.dv_xname, + qwz_htc_service_name(conn_req->service_id)); + + m = qwz_htc_build_tx_ctrl_mbuf(); + if (!m) { + printf("%s: Failed to allocate HTC packet\n", + sc->sc_dev.dv_xname); + return ENOMEM; + } + + length = sizeof(*req_msg); + m->m_len = m->m_pkthdr.len = sizeof(struct ath12k_htc_hdr) + length; + + req_msg = (struct ath12k_htc_conn_svc *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr)); + memset(req_msg, 0, length); + req_msg->msg_svc_id = FIELD_PREP(HTC_MSG_MESSAGEID, + ATH12K_HTC_MSG_CONNECT_SERVICE_ID); + + flags |= FIELD_PREP(ATH12K_HTC_CONN_FLAGS_RECV_ALLOC, tx_alloc); + + /* Only enable credit flow control for WMI ctrl service */ + if (!(conn_req->service_id == ATH12K_HTC_SVC_ID_WMI_CONTROL || + conn_req->service_id == ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1 || + conn_req->service_id == ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2)) { + flags |= ATH12K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL; + disable_credit_flow_ctrl = 1; + } + + if (!sc->hw_params.credit_flow) { + flags |= ATH12K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL; + disable_credit_flow_ctrl = 1; + } + + req_msg->flags_len = FIELD_PREP(HTC_SVC_MSG_CONNECTIONFLAGS, flags); + req_msg->msg_svc_id |= FIELD_PREP(HTC_SVC_MSG_SERVICE_ID, + conn_req->service_id); + + sc->ctl_resp = 0; + + status = qwz_htc_send(htc, ATH12K_HTC_EP_0, m); + if (status) { + if (status != ENOBUFS) + m_freem(m); + return status; + } + + while (!sc->ctl_resp) { + int ret = tsleep_nsec(&sc->ctl_resp, 0, "qwzhtcinit", + SEC_TO_NSEC(1)); + if (ret) { + printf("%s: Service connect timeout\n", + sc->sc_dev.dv_xname); + return ret; + } + } + + /* we controlled the buffer creation, it's aligned */ + resp_msg = (struct ath12k_htc_conn_svc_resp *)htc->control_resp_buffer; + message_id = FIELD_GET(HTC_MSG_MESSAGEID, resp_msg->msg_svc_id); + service_id = FIELD_GET(HTC_SVC_RESP_MSG_SERVICEID, + resp_msg->msg_svc_id); + if ((message_id != ATH12K_HTC_MSG_CONNECT_SERVICE_RESP_ID) || + (htc->control_resp_len < sizeof(*resp_msg))) { + printf("%s: Invalid resp message ID 0x%x", __func__, + message_id); + return EPROTO; + } + + DNPRINTF(QWZ_D_HTC, "%s: service %s connect response status 0x%lx " + "assigned ep 0x%lx\n", __func__, qwz_htc_service_name(service_id), + FIELD_GET(HTC_SVC_RESP_MSG_STATUS, resp_msg->flags_len), + FIELD_GET(HTC_SVC_RESP_MSG_ENDPOINTID, resp_msg->flags_len)); + + conn_resp->connect_resp_code = FIELD_GET(HTC_SVC_RESP_MSG_STATUS, + resp_msg->flags_len); + + /* check response status */ + if (conn_resp->connect_resp_code != + ATH12K_HTC_CONN_SVC_STATUS_SUCCESS) { + printf("%s: HTC Service %s connect request failed: 0x%x)\n", + __func__, qwz_htc_service_name(service_id), + conn_resp->connect_resp_code); + return EPROTO; + } + + assigned_eid = (enum ath12k_htc_ep_id)FIELD_GET( + HTC_SVC_RESP_MSG_ENDPOINTID, resp_msg->flags_len); + + max_msg_size = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE, + resp_msg->flags_len); +setup: + if (assigned_eid >= ATH12K_HTC_EP_COUNT) + return EPROTO; + + if (max_msg_size == 0) + return EPROTO; + + ep = &htc->endpoint[assigned_eid]; + ep->eid = assigned_eid; + + if (ep->service_id != ATH12K_HTC_SVC_ID_UNUSED) + return EPROTO; + + /* return assigned endpoint to caller */ + conn_resp->eid = assigned_eid; + conn_resp->max_msg_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE, + resp_msg->flags_len); + + /* setup the endpoint */ + ep->service_id = conn_req->service_id; + ep->max_tx_queue_depth = conn_req->max_send_queue_depth; + ep->max_ep_message_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE, + resp_msg->flags_len); + ep->tx_credits = tx_alloc; + + /* copy all the callbacks */ + ep->ep_ops = conn_req->ep_ops; + + status = sc->ops.map_service_to_pipe(htc->sc, ep->service_id, + &ep->ul_pipe_id, &ep->dl_pipe_id); + if (status) + return status; + + DNPRINTF(QWZ_D_HTC, + "%s: htc service '%s' ul pipe %d dl pipe %d eid %d ready\n", + __func__, qwz_htc_service_name(ep->service_id), ep->ul_pipe_id, + ep->dl_pipe_id, ep->eid); + + if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) { + ep->tx_credit_flow_enabled = 0; + DNPRINTF(QWZ_D_HTC, + "%s: htc service '%s' eid %d tx flow control disabled\n", + __func__, qwz_htc_service_name(ep->service_id), + assigned_eid); + } + + return status; +} + +int +qwz_htc_start(struct qwz_htc *htc) +{ + struct mbuf *m; + int status = 0; + struct qwz_softc *sc = htc->sc; + struct ath12k_htc_setup_complete_extended *msg; + + m = qwz_htc_build_tx_ctrl_mbuf(); + if (!m) + return ENOMEM; + + m->m_len = m->m_pkthdr.len = sizeof(struct ath12k_htc_hdr) + + sizeof(*msg); + + msg = (struct ath12k_htc_setup_complete_extended *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr)); + msg->msg_id = FIELD_PREP(HTC_MSG_MESSAGEID, + ATH12K_HTC_MSG_SETUP_COMPLETE_EX_ID); + + if (sc->hw_params.credit_flow) + DNPRINTF(QWZ_D_HTC, "%s: using tx credit flow control\n", + __func__); + else + msg->flags |= ATH12K_GLOBAL_DISABLE_CREDIT_FLOW; + + status = qwz_htc_send(htc, ATH12K_HTC_EP_0, m); + if (status) { + m_freem(m); + return status; + } + + return 0; +} + +int +qwz_htc_init(struct qwz_softc *sc) +{ + struct qwz_htc *htc = &sc->htc; + struct qwz_htc_svc_conn_req conn_req; + struct qwz_htc_svc_conn_resp conn_resp; + int ret; +#ifdef notyet + spin_lock_init(&htc->tx_lock); +#endif + qwz_htc_reset_endpoint_states(htc); + + htc->sc = sc; + + switch (sc->wmi.preferred_hw_mode) { + case WMI_HOST_HW_MODE_SINGLE: + htc->wmi_ep_count = 1; + break; + case WMI_HOST_HW_MODE_DBS: + case WMI_HOST_HW_MODE_DBS_OR_SBS: + htc->wmi_ep_count = 2; + break; + case WMI_HOST_HW_MODE_DBS_SBS: + htc->wmi_ep_count = 3; + break; + default: + htc->wmi_ep_count = sc->hw_params.max_radios; + break; + } + + /* setup our pseudo HTC control endpoint connection */ + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + conn_req.ep_ops.ep_tx_complete = qwz_htc_control_tx_complete; + conn_req.ep_ops.ep_rx_complete = qwz_htc_control_rx_complete; + conn_req.max_send_queue_depth = ATH12K_NUM_CONTROL_TX_BUFFERS; + conn_req.service_id = ATH12K_HTC_SVC_ID_RSVD_CTRL; + + /* connect fake service */ + ret = qwz_htc_connect_service(htc, &conn_req, &conn_resp); + if (ret) { + printf("%s: could not connect to htc service (%d)\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + return 0; +} + +int +qwz_htc_setup_target_buffer_assignments(struct qwz_htc *htc) +{ + struct qwz_htc_svc_tx_credits *serv_entry; + uint32_t svc_id[] = { + ATH12K_HTC_SVC_ID_WMI_CONTROL, + ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1, + ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2, + }; + int i, credits; + + credits = htc->total_transmit_credits; + serv_entry = htc->service_alloc_table; + + if ((htc->wmi_ep_count == 0) || + (htc->wmi_ep_count > nitems(svc_id))) + return EINVAL; + + /* Divide credits among number of endpoints for WMI */ + credits = credits / htc->wmi_ep_count; + for (i = 0; i < htc->wmi_ep_count; i++) { + serv_entry[i].service_id = svc_id[i]; + serv_entry[i].credit_allocation = credits; + } + + return 0; +} + +int +qwz_htc_wait_target(struct qwz_softc *sc) +{ + struct qwz_htc *htc = &sc->htc; + int polling = 0, ret; + uint16_t i; + struct ath12k_htc_ready *ready; + uint16_t message_id; + uint16_t credit_count; + uint16_t credit_size; + + sc->ctl_resp = 0; + while (!sc->ctl_resp) { + ret = tsleep_nsec(&sc->ctl_resp, 0, "qwzhtcinit", + SEC_TO_NSEC(1)); + if (ret) { + if (ret != EWOULDBLOCK) + return ret; + + if (polling) { + printf("%s: failed to receive control response " + "completion\n", sc->sc_dev.dv_xname); + return ret; + } + + printf("%s: failed to receive control response " + "completion, polling...\n", sc->sc_dev.dv_xname); + polling = 1; + + for (i = 0; i < sc->hw_params.ce_count; i++) + qwz_ce_per_engine_service(sc, i); + } + } + + if (htc->control_resp_len < sizeof(*ready)) { + printf("%s: Invalid HTC ready msg len:%d\n", __func__, + htc->control_resp_len); + return EINVAL; + } + + ready = (struct ath12k_htc_ready *)htc->control_resp_buffer; + message_id = FIELD_GET(HTC_MSG_MESSAGEID, ready->id_credit_count); + credit_count = FIELD_GET(HTC_READY_MSG_CREDITCOUNT, + ready->id_credit_count); + credit_size = FIELD_GET(HTC_READY_MSG_CREDITSIZE, ready->size_ep); + + if (message_id != ATH12K_HTC_MSG_READY_ID) { + printf("%s: Invalid HTC ready msg: 0x%x\n", __func__, + message_id); + return EINVAL; + } + + htc->total_transmit_credits = credit_count; + htc->target_credit_size = credit_size; + + DNPRINTF(QWZ_D_HTC, "%s: target ready total_transmit_credits %d " + "target_credit_size %d\n", __func__, + htc->total_transmit_credits, htc->target_credit_size); + + if ((htc->total_transmit_credits == 0) || + (htc->target_credit_size == 0)) { + printf("%s: Invalid credit size received\n", __func__); + return EINVAL; + } + + /* For QCA6390, wmi endpoint uses 1 credit to avoid + * back-to-back write. + */ + if (sc->hw_params.supports_shadow_regs) + htc->total_transmit_credits = 1; + + qwz_htc_setup_target_buffer_assignments(htc); + + return 0; +} + +void +qwz_dp_htt_htc_tx_complete(struct qwz_softc *sc, struct mbuf *m) +{ + /* Just free the mbuf, no further action required. */ + m_freem(m); +} + +static inline void +qwz_dp_get_mac_addr(uint32_t addr_l32, uint16_t addr_h16, uint8_t *addr) +{ +#if 0 /* Not needed on OpenBSD? We do swapping in sofware... */ + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) { + addr_l32 = swab32(addr_l32); + addr_h16 = swab16(addr_h16); + } +#endif + uint32_t val32; + uint16_t val16; + + val32 = le32toh(addr_l32); + memcpy(addr, &val32, 4); + val16 = le16toh(addr_h16); + memcpy(addr + 4, &val16, IEEE80211_ADDR_LEN - 4); +} + +void +qwz_peer_map_event(struct qwz_softc *sc, uint8_t vdev_id, uint16_t peer_id, + uint8_t *mac_addr, uint16_t ast_hash, uint16_t hw_peer_id) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct qwz_node *nq; + struct ath12k_peer *peer; +#ifdef notyet + spin_lock_bh(&ab->base_lock); +#endif + ni = ieee80211_find_node(ic, mac_addr); + if (ni == NULL) + return; + nq = (struct qwz_node *)ni; + peer = &nq->peer; + + peer->vdev_id = vdev_id; + peer->peer_id = peer_id; + peer->ast_hash = ast_hash; + peer->hw_peer_id = hw_peer_id; +#if 0 + ether_addr_copy(peer->addr, mac_addr); + list_add(&peer->list, &ab->peers); +#endif + sc->peer_mapped = 1; + wakeup(&sc->peer_mapped); + + DNPRINTF(QWZ_D_HTT, "%s: peer map vdev %d peer %s id %d\n", + __func__, vdev_id, ether_sprintf(mac_addr), peer_id); +#ifdef notyet + spin_unlock_bh(&ab->base_lock); +#endif +} + +struct ieee80211_node * +qwz_peer_find_by_id(struct qwz_softc *sc, uint16_t peer_id) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = NULL; + int s; + + s = splnet(); + RBT_FOREACH(ni, ieee80211_tree, &ic->ic_tree) { + struct qwz_node *nq = (struct qwz_node *)ni; + if (nq->peer.peer_id == peer_id) + break; + } + splx(s); + + return ni; +} + +void +qwz_peer_unmap_event(struct qwz_softc *sc, uint16_t peer_id) +{ + struct ieee80211_node *ni; +#ifdef notyet + spin_lock_bh(&ab->base_lock); +#endif + ni = qwz_peer_find_by_id(sc, peer_id); + if (!ni) { + printf("%s: peer-unmap-event: unknown peer id %d\n", + sc->sc_dev.dv_xname, peer_id); + goto exit; + } + + DNPRINTF(QWZ_D_HTT, "%s: peer unmap peer %s id %d\n", + __func__, ether_sprintf(ni->ni_macaddr), peer_id); +#if 0 + list_del(&peer->list); + kfree(peer); +#endif + sc->peer_mapped = 1; + wakeup(&sc->peer_mapped); +exit: +#ifdef notyet + spin_unlock_bh(&ab->base_lock); +#endif + return; +} + +void +qwz_dp_htt_htc_t2h_msg_handler(struct qwz_softc *sc, struct mbuf *m) +{ + struct qwz_dp *dp = &sc->dp; + struct htt_resp_msg *resp = mtod(m, struct htt_resp_msg *); + enum htt_t2h_msg_type type = FIELD_GET(HTT_T2H_MSG_TYPE, + *(uint32_t *)resp); + uint16_t peer_id; + uint8_t vdev_id; + uint8_t mac_addr[IEEE80211_ADDR_LEN]; + uint16_t peer_mac_h16; + uint16_t ast_hash; + uint16_t hw_peer_id; + + DPRINTF("%s: dp_htt rx msg type: 0x%0x\n", __func__, type); + + switch (type) { + case HTT_T2H_MSG_TYPE_VERSION_CONF: + dp->htt_tgt_ver_major = FIELD_GET(HTT_T2H_VERSION_CONF_MAJOR, + resp->version_msg.version); + dp->htt_tgt_ver_minor = FIELD_GET(HTT_T2H_VERSION_CONF_MINOR, + resp->version_msg.version); + dp->htt_tgt_version_received = 1; + wakeup(&dp->htt_tgt_version_received); + break; + case HTT_T2H_MSG_TYPE_PEER_MAP: + vdev_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_VDEV_ID, + resp->peer_map_ev.info); + peer_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_PEER_ID, + resp->peer_map_ev.info); + peer_mac_h16 = FIELD_GET(HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16, + resp->peer_map_ev.info1); + qwz_dp_get_mac_addr(resp->peer_map_ev.mac_addr_l32, + peer_mac_h16, mac_addr); + qwz_peer_map_event(sc, vdev_id, peer_id, mac_addr, 0, 0); + break; + case HTT_T2H_MSG_TYPE_PEER_MAP2: + vdev_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_VDEV_ID, + resp->peer_map_ev.info); + peer_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_PEER_ID, + resp->peer_map_ev.info); + peer_mac_h16 = FIELD_GET(HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16, + resp->peer_map_ev.info1); + qwz_dp_get_mac_addr(resp->peer_map_ev.mac_addr_l32, + peer_mac_h16, mac_addr); + ast_hash = FIELD_GET(HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL, + resp->peer_map_ev.info2); + hw_peer_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO1_HW_PEER_ID, + resp->peer_map_ev.info1); + qwz_peer_map_event(sc, vdev_id, peer_id, mac_addr, ast_hash, + hw_peer_id); + break; + case HTT_T2H_MSG_TYPE_PEER_UNMAP: + case HTT_T2H_MSG_TYPE_PEER_UNMAP2: + peer_id = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO_PEER_ID, + resp->peer_unmap_ev.info); + qwz_peer_unmap_event(sc, peer_id); + break; +#if 0 + case HTT_T2H_MSG_TYPE_PPDU_STATS_IND: + ath12k_htt_pull_ppdu_stats(ab, skb); + break; + case HTT_T2H_MSG_TYPE_EXT_STATS_CONF: + ath12k_debugfs_htt_ext_stats_handler(ab, skb); + break; + case HTT_T2H_MSG_TYPE_PKTLOG: + ath12k_htt_pktlog(ab, skb); + break; + case HTT_T2H_MSG_TYPE_BKPRESSURE_EVENT_IND: + ath12k_htt_backpressure_event_handler(ab, skb); + break; +#endif + default: + printf("%s: htt event %d not handled\n", __func__, type); + break; + } + + m_freem(m); +} + +int +qwz_dp_htt_connect(struct qwz_dp *dp) +{ + struct qwz_htc_svc_conn_req conn_req; + struct qwz_htc_svc_conn_resp conn_resp; + int status; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + conn_req.ep_ops.ep_tx_complete = qwz_dp_htt_htc_tx_complete; + conn_req.ep_ops.ep_rx_complete = qwz_dp_htt_htc_t2h_msg_handler; + + /* connect to control service */ + conn_req.service_id = ATH12K_HTC_SVC_ID_HTT_DATA_MSG; + + status = qwz_htc_connect_service(&dp->sc->htc, &conn_req, &conn_resp); + + if (status) + return status; + + dp->eid = conn_resp.eid; + + return 0; +} + +void +qwz_dp_pdev_reo_cleanup(struct qwz_softc *sc) +{ + struct qwz_dp *dp = &sc->dp; + int i; + + for (i = 0; i < DP_REO_DST_RING_MAX; i++) + qwz_dp_srng_cleanup(sc, &dp->reo_dst_ring[i]); +} + +int +qwz_dp_pdev_reo_setup(struct qwz_softc *sc) +{ + struct qwz_dp *dp = &sc->dp; + int ret; + int i; + + for (i = 0; i < DP_REO_DST_RING_MAX; i++) { + ret = qwz_dp_srng_setup(sc, &dp->reo_dst_ring[i], + HAL_REO_DST, i, 0, DP_REO_DST_RING_SIZE); + if (ret) { + printf("%s: failed to setup reo_dst_ring\n", __func__); + qwz_dp_pdev_reo_cleanup(sc); + return ret; + } + } + + return 0; +} + +void +qwz_dp_rx_pdev_srng_free(struct qwz_softc *sc, int mac_id) +{ + struct qwz_pdev_dp *dp = &sc->pdev_dp; + int i; + + qwz_dp_srng_cleanup(sc, &dp->rx_refill_buf_ring.refill_buf_ring); + + for (i = 0; i < sc->hw_params.num_rxmda_per_pdev; i++) { + if (sc->hw_params.rx_mac_buf_ring) + qwz_dp_srng_cleanup(sc, &dp->rx_mac_buf_ring[i]); + + qwz_dp_srng_cleanup(sc, &dp->rxdma_err_dst_ring[i]); + qwz_dp_srng_cleanup(sc, + &dp->rx_mon_status_refill_ring[i].refill_buf_ring); + } + + qwz_dp_srng_cleanup(sc, &dp->rxdma_mon_buf_ring.refill_buf_ring); +} + +int +qwz_dp_rx_pdev_srng_alloc(struct qwz_softc *sc) +{ + struct qwz_pdev_dp *dp = &sc->pdev_dp; +#if 0 + struct dp_srng *srng = NULL; +#endif + int i; + int ret; + + ret = qwz_dp_srng_setup(sc, &dp->rx_refill_buf_ring.refill_buf_ring, + HAL_RXDMA_BUF, 0, dp->mac_id, DP_RXDMA_BUF_RING_SIZE); + if (ret) { + printf("%s: failed to setup rx_refill_buf_ring\n", + sc->sc_dev.dv_xname); + return ret; + } + + if (sc->hw_params.rx_mac_buf_ring) { + for (i = 0; i < sc->hw_params.num_rxmda_per_pdev; i++) { + ret = qwz_dp_srng_setup(sc, &dp->rx_mac_buf_ring[i], + HAL_RXDMA_BUF, 1, dp->mac_id + i, 1024); + if (ret) { + printf("%s: failed to setup " + "rx_mac_buf_ring %d\n", + sc->sc_dev.dv_xname, i); + return ret; + } + } + } + + for (i = 0; i < sc->hw_params.num_rxmda_per_pdev; i++) { + ret = qwz_dp_srng_setup(sc, &dp->rxdma_err_dst_ring[i], + HAL_RXDMA_DST, 0, dp->mac_id + i, + DP_RXDMA_ERR_DST_RING_SIZE); + if (ret) { + printf("%s: failed to setup rxdma_err_dst_ring %d\n", + sc->sc_dev.dv_xname, i); + return ret; + } + } +#if 0 + for (i = 0; i < sc->hw_params.num_rxmda_per_pdev; i++) { + srng = &dp->rx_mon_status_refill_ring[i].refill_buf_ring; + ret = qwz_dp_srng_setup(sc, srng, HAL_RXDMA_MONITOR_STATUS, 0, + dp->mac_id + i, DP_RXDMA_MON_STATUS_RING_SIZE); + if (ret) { + printf("%s: failed to setup " + "rx_mon_status_refill_ring %d\n", + sc->sc_dev.dv_xname, i); + return ret; + } + } +#endif + /* if rxdma1_enable is false, then it doesn't need + * to setup rxdam_mon_buf_ring, rxdma_mon_dst_ring + * and rxdma_mon_desc_ring. + * init reap timer for QCA6390. + */ + if (!sc->hw_params.rxdma1_enable) { + timeout_set(&sc->mon_reap_timer, qwz_dp_service_mon_ring, sc); + return 0; + } +#if 0 + ret = ath12k_dp_srng_setup(ar->ab, + &dp->rxdma_mon_buf_ring.refill_buf_ring, + HAL_RXDMA_MONITOR_BUF, 0, dp->mac_id, + DP_RXDMA_MONITOR_BUF_RING_SIZE); + if (ret) { + ath12k_warn(ar->ab, + "failed to setup HAL_RXDMA_MONITOR_BUF\n"); + return ret; + } + + ret = ath12k_dp_srng_setup(ar->ab, &dp->rxdma_mon_dst_ring, + HAL_RXDMA_MONITOR_DST, 0, dp->mac_id, + DP_RXDMA_MONITOR_DST_RING_SIZE); + if (ret) { + ath12k_warn(ar->ab, + "failed to setup HAL_RXDMA_MONITOR_DST\n"); + return ret; + } + + ret = ath12k_dp_srng_setup(ar->ab, &dp->rxdma_mon_desc_ring, + HAL_RXDMA_MONITOR_DESC, 0, dp->mac_id, + DP_RXDMA_MONITOR_DESC_RING_SIZE); + if (ret) { + ath12k_warn(ar->ab, + "failed to setup HAL_RXDMA_MONITOR_DESC\n"); + return ret; + } +#endif + return 0; +} + +void +qwz_dp_rxdma_buf_ring_free(struct qwz_softc *sc, struct dp_rxdma_ring *rx_ring) +{ + int i; + + for (i = 0; i < rx_ring->bufs_max; i++) { + struct qwz_rx_data *rx_data = &rx_ring->rx_data[i]; + + if (rx_data->map == NULL) + continue; + + if (rx_data->m) { + bus_dmamap_unload(sc->sc_dmat, rx_data->map); + m_free(rx_data->m); + rx_data->m = NULL; + } + + bus_dmamap_destroy(sc->sc_dmat, rx_data->map); + rx_data->map = NULL; + } + + free(rx_ring->rx_data, M_DEVBUF, + sizeof(rx_ring->rx_data[0]) * rx_ring->bufs_max); + rx_ring->rx_data = NULL; + rx_ring->bufs_max = 0; + memset(rx_ring->freemap, 0xff, sizeof(rx_ring->freemap)); +} + +void +qwz_dp_rxdma_pdev_buf_free(struct qwz_softc *sc, int mac_id) +{ + struct qwz_pdev_dp *dp = &sc->pdev_dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + int i; + + qwz_dp_rxdma_buf_ring_free(sc, rx_ring); + + rx_ring = &dp->rxdma_mon_buf_ring; + qwz_dp_rxdma_buf_ring_free(sc, rx_ring); + + for (i = 0; i < sc->hw_params.num_rxmda_per_pdev; i++) { + rx_ring = &dp->rx_mon_status_refill_ring[i]; + qwz_dp_rxdma_buf_ring_free(sc, rx_ring); + } +} + +void +qwz_hal_rx_buf_addr_info_set(void *desc, uint64_t paddr, uint32_t cookie, + uint8_t manager) +{ + struct ath12k_buffer_addr *binfo = (struct ath12k_buffer_addr *)desc; + uint32_t paddr_lo, paddr_hi; + + paddr_lo = paddr & 0xffffffff; + paddr_hi = paddr >> 32; + binfo->info0 = FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, paddr_lo); + binfo->info1 = FIELD_PREP(BUFFER_ADDR_INFO1_ADDR, paddr_hi) | + FIELD_PREP(BUFFER_ADDR_INFO1_SW_COOKIE, cookie) | + FIELD_PREP(BUFFER_ADDR_INFO1_RET_BUF_MGR, manager); +} + +void +qwz_hal_rx_buf_addr_info_get(void *desc, uint64_t *paddr, uint32_t *cookie, + uint8_t *rbm) +{ + struct ath12k_buffer_addr *binfo = (struct ath12k_buffer_addr *)desc; + + *paddr = (((uint64_t)FIELD_GET(BUFFER_ADDR_INFO1_ADDR, + binfo->info1)) << 32) | + FIELD_GET(BUFFER_ADDR_INFO0_ADDR, binfo->info0); + *cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, binfo->info1); + *rbm = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, binfo->info1); +} + +int +qwz_next_free_rxbuf_idx(struct dp_rxdma_ring *rx_ring) +{ + int i, idx; + + for (i = 0; i < nitems(rx_ring->freemap); i++) { + idx = ffs(rx_ring->freemap[i]); + if (idx > 0) + return ((idx - 1) + (i * 8)); + } + + return -1; +} + +int +qwz_dp_rxbufs_replenish(struct qwz_softc *sc, int mac_id, + struct dp_rxdma_ring *rx_ring, int req_entries, + enum hal_rx_buf_return_buf_manager mgr) +{ + struct hal_srng *srng; + uint32_t *desc; + struct mbuf *m; + int num_free; + int num_remain; + int ret, idx; + uint32_t cookie; + uint64_t paddr; + struct qwz_rx_data *rx_data; + + req_entries = MIN(req_entries, rx_ring->bufs_max); + + srng = &sc->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + + num_free = qwz_hal_srng_src_num_free(sc, srng, 1); + if (!req_entries && (num_free > (rx_ring->bufs_max * 3) / 4)) + req_entries = num_free; + + req_entries = MIN(num_free, req_entries); + num_remain = req_entries; + + while (num_remain > 0) { + const size_t size = DP_RX_BUFFER_SIZE; + + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) + goto fail_free_mbuf; + + if (size <= MCLBYTES) + MCLGET(m, M_DONTWAIT); + else + MCLGETL(m, M_DONTWAIT, size); + if ((m->m_flags & M_EXT) == 0) + goto fail_free_mbuf; + + m->m_len = m->m_pkthdr.len = size; + + idx = qwz_next_free_rxbuf_idx(rx_ring); + if (idx == -1) + goto fail_free_mbuf; + + rx_data = &rx_ring->rx_data[idx]; + if (rx_data->map == NULL) { + ret = bus_dmamap_create(sc->sc_dmat, size, 1, + size, 0, BUS_DMA_NOWAIT, &rx_data->map); + if (ret) + goto fail_free_mbuf; + } + + ret = bus_dmamap_load_mbuf(sc->sc_dmat, rx_data->map, m, + BUS_DMA_READ | BUS_DMA_NOWAIT); + if (ret) { + printf("%s: can't map mbuf (error %d)\n", + sc->sc_dev.dv_xname, ret); + goto fail_free_mbuf; + } + + desc = qwz_hal_srng_src_get_next_entry(sc, srng); + if (!desc) + goto fail_dma_unmap; + + rx_data->m = m; + m = NULL; + + cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, mac_id) | + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, idx); + + clrbit(rx_ring->freemap, idx); + num_remain--; + + paddr = rx_data->map->dm_segs[0].ds_addr; + qwz_hal_rx_buf_addr_info_set(desc, paddr, cookie, mgr); + } + + qwz_hal_srng_access_end(sc, srng); +#ifdef notyet + spin_unlock_bh(&srng->lock); +#endif + return 0; + +fail_dma_unmap: + bus_dmamap_unload(sc->sc_dmat, rx_data->map); +fail_free_mbuf: + m_free(m); + + qwz_hal_srng_access_end(sc, srng); +#ifdef notyet + spin_unlock_bh(&srng->lock); +#endif + return ENOBUFS; +} + +int +qwz_dp_rxdma_ring_buf_setup(struct qwz_softc *sc, + struct dp_rxdma_ring *rx_ring, uint32_t ringtype) +{ + struct qwz_pdev_dp *dp = &sc->pdev_dp; + int num_entries; + + num_entries = rx_ring->refill_buf_ring.size / + qwz_hal_srng_get_entrysize(sc, ringtype); + + KASSERT(rx_ring->rx_data == NULL); + rx_ring->rx_data = mallocarray(num_entries, sizeof(rx_ring->rx_data[0]), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (rx_ring->rx_data == NULL) + return ENOMEM; + + rx_ring->bufs_max = num_entries; + memset(rx_ring->freemap, 0xff, sizeof(rx_ring->freemap)); + + return qwz_dp_rxbufs_replenish(sc, dp->mac_id, rx_ring, num_entries, + sc->hw_params.hal_params->rx_buf_rbm); +} + +int +qwz_dp_rxdma_pdev_buf_setup(struct qwz_softc *sc) +{ + struct qwz_pdev_dp *dp = &sc->pdev_dp; + struct dp_rxdma_ring *rx_ring; + int ret; +#if 0 + int i; +#endif + + rx_ring = &dp->rx_refill_buf_ring; + ret = qwz_dp_rxdma_ring_buf_setup(sc, rx_ring, HAL_RXDMA_BUF); + if (ret) + return ret; + + if (sc->hw_params.rxdma1_enable) { + rx_ring = &dp->rxdma_mon_buf_ring; + ret = qwz_dp_rxdma_ring_buf_setup(sc, rx_ring, + HAL_RXDMA_MONITOR_BUF); + if (ret) + return ret; + } +#if 0 + for (i = 0; i < sc->hw_params.num_rxmda_per_pdev; i++) { + rx_ring = &dp->rx_mon_status_refill_ring[i]; + ret = qwz_dp_rxdma_ring_buf_setup(sc, rx_ring, + HAL_RXDMA_MONITOR_STATUS); + if (ret) + return ret; + } +#endif + return 0; +} + +void +qwz_dp_rx_pdev_free(struct qwz_softc *sc, int mac_id) +{ + qwz_dp_rx_pdev_srng_free(sc, mac_id); + qwz_dp_rxdma_pdev_buf_free(sc, mac_id); +} + +bus_addr_t +qwz_hal_srng_get_hp_addr(struct qwz_softc *sc, struct hal_srng *srng) +{ + if (!(srng->flags & HAL_SRNG_FLAGS_LMAC_RING)) + return 0; + + if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + return sc->hal.wrp.paddr + + ((unsigned long)srng->u.src_ring.hp_addr - + (unsigned long)sc->hal.wrp.vaddr); + } else { + return sc->hal.rdp.paddr + + ((unsigned long)srng->u.dst_ring.hp_addr - + (unsigned long)sc->hal.rdp.vaddr); + } +} + +bus_addr_t +qwz_hal_srng_get_tp_addr(struct qwz_softc *sc, struct hal_srng *srng) +{ + if (!(srng->flags & HAL_SRNG_FLAGS_LMAC_RING)) + return 0; + + if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + return sc->hal.rdp.paddr + + ((unsigned long)srng->u.src_ring.tp_addr - + (unsigned long)sc->hal.rdp.vaddr); + } else { + return sc->hal.wrp.paddr + + ((unsigned long)srng->u.dst_ring.tp_addr - + (unsigned long)sc->hal.wrp.vaddr); + } +} + +int +qwz_dp_tx_get_ring_id_type(struct qwz_softc *sc, int mac_id, uint32_t ring_id, + enum hal_ring_type ring_type, enum htt_srng_ring_type *htt_ring_type, + enum htt_srng_ring_id *htt_ring_id) +{ + int lmac_ring_id_offset = 0; + + switch (ring_type) { + case HAL_RXDMA_BUF: + lmac_ring_id_offset = mac_id * HAL_SRNG_RINGS_PER_LMAC; + + /* for QCA6390, host fills rx buffer to fw and fw fills to + * rxbuf ring for each rxdma + */ + if (!sc->hw_params.rx_mac_buf_ring) { + if (!(ring_id == (HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF + + lmac_ring_id_offset) || + ring_id == (HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_BUF + + lmac_ring_id_offset))) + return EINVAL; + *htt_ring_id = HTT_RXDMA_HOST_BUF_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + } else { + if (ring_id == HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF) { + *htt_ring_id = HTT_HOST1_TO_FW_RXBUF_RING; + *htt_ring_type = HTT_SW_TO_SW_RING; + } else { + *htt_ring_id = HTT_RXDMA_HOST_BUF_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + } + } + break; + case HAL_RXDMA_DST: + *htt_ring_id = HTT_RXDMA_NON_MONITOR_DEST_RING; + *htt_ring_type = HTT_HW_TO_SW_RING; + break; + case HAL_RXDMA_MONITOR_BUF: + *htt_ring_id = HTT_RXDMA_MONITOR_BUF_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + break; + case HAL_RXDMA_MONITOR_STATUS: + *htt_ring_id = HTT_RXDMA_MONITOR_STATUS_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + break; + case HAL_RXDMA_MONITOR_DST: + *htt_ring_id = HTT_RXDMA_MONITOR_DEST_RING; + *htt_ring_type = HTT_HW_TO_SW_RING; + break; + case HAL_RXDMA_MONITOR_DESC: + *htt_ring_id = HTT_RXDMA_MONITOR_DESC_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + break; + default: + printf("%s: Unsupported ring type in DP :%d\n", + sc->sc_dev.dv_xname, ring_type); + return EINVAL; + } + + return 0; +} + +int +qwz_dp_tx_htt_srng_setup(struct qwz_softc *sc, uint32_t ring_id, int mac_id, + enum hal_ring_type ring_type) +{ + struct htt_srng_setup_cmd *cmd; + struct hal_srng *srng = &sc->hal.srng_list[ring_id]; + struct hal_srng_params params; + struct mbuf *m; + uint32_t ring_entry_sz; + uint64_t hp_addr, tp_addr; + enum htt_srng_ring_type htt_ring_type; + enum htt_srng_ring_id htt_ring_id; + int ret; + + m = qwz_htc_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + memset(¶ms, 0, sizeof(params)); + qwz_hal_srng_get_params(sc, srng, ¶ms); + + hp_addr = qwz_hal_srng_get_hp_addr(sc, srng); + tp_addr = qwz_hal_srng_get_tp_addr(sc, srng); + + ret = qwz_dp_tx_get_ring_id_type(sc, mac_id, ring_id, + ring_type, &htt_ring_type, &htt_ring_id); + if (ret) + goto err_free; + + cmd = (struct htt_srng_setup_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr)); + cmd->info0 = FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_MSG_TYPE, + HTT_H2T_MSG_TYPE_SRING_SETUP); + if (htt_ring_type == HTT_SW_TO_HW_RING || + htt_ring_type == HTT_HW_TO_SW_RING) + cmd->info0 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_PDEV_ID, + DP_SW2HW_MACID(mac_id)); + else + cmd->info0 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_PDEV_ID, + mac_id); + cmd->info0 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_RING_TYPE, + htt_ring_type); + cmd->info0 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_RING_ID, htt_ring_id); + + cmd->ring_base_addr_lo = params.ring_base_paddr & HAL_ADDR_LSB_REG_MASK; + + cmd->ring_base_addr_hi = (uint64_t)params.ring_base_paddr >> + HAL_ADDR_MSB_REG_SHIFT; + + ring_entry_sz = qwz_hal_srng_get_entrysize(sc, ring_type); + + ring_entry_sz >>= 2; + cmd->info1 = FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO1_RING_ENTRY_SIZE, + ring_entry_sz); + cmd->info1 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO1_RING_SIZE, + params.num_entries * ring_entry_sz); + cmd->info1 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_MSI_SWAP, + !!(params.flags & HAL_SRNG_FLAGS_MSI_SWAP)); + cmd->info1 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_TLV_SWAP, + !!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP)); + cmd->info1 |= FIELD_PREP( + HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_HOST_FW_SWAP, + !!(params.flags & HAL_SRNG_FLAGS_RING_PTR_SWAP)); + if (htt_ring_type == HTT_SW_TO_HW_RING) + cmd->info1 |= HTT_SRNG_SETUP_CMD_INFO1_RING_LOOP_CNT_DIS; + + cmd->ring_head_off32_remote_addr_lo = hp_addr & HAL_ADDR_LSB_REG_MASK; + cmd->ring_head_off32_remote_addr_hi = hp_addr >> HAL_ADDR_MSB_REG_SHIFT; + + cmd->ring_tail_off32_remote_addr_lo = tp_addr & HAL_ADDR_LSB_REG_MASK; + cmd->ring_tail_off32_remote_addr_hi = tp_addr >> HAL_ADDR_MSB_REG_SHIFT; + + cmd->ring_msi_addr_lo = params.msi_addr & 0xffffffff; + cmd->ring_msi_addr_hi = 0; + cmd->msi_data = params.msi_data; + + cmd->intr_info = FIELD_PREP( + HTT_SRNG_SETUP_CMD_INTR_INFO_BATCH_COUNTER_THRESH, + params.intr_batch_cntr_thres_entries * ring_entry_sz); + cmd->intr_info |= FIELD_PREP( + HTT_SRNG_SETUP_CMD_INTR_INFO_INTR_TIMER_THRESH, + params.intr_timer_thres_us >> 3); + + cmd->info2 = 0; + if (params.flags & HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN) { + cmd->info2 = FIELD_PREP( + HTT_SRNG_SETUP_CMD_INFO2_INTR_LOW_THRESH, + params.low_threshold); + } + + DNPRINTF(QWZ_D_HTT, "%s: htt srng setup msi_addr_lo 0x%x " + "msi_addr_hi 0x%x msi_data 0x%x ring_id %d ring_type %d " + "intr_info 0x%x flags 0x%x\n", __func__, cmd->ring_msi_addr_lo, + cmd->ring_msi_addr_hi, cmd->msi_data, ring_id, ring_type, + cmd->intr_info, cmd->info2); + + ret = qwz_htc_send(&sc->htc, sc->dp.eid, m); + if (ret) + goto err_free; + + return 0; + +err_free: + m_freem(m); + + return ret; +} + +int +qwz_dp_tx_htt_h2t_ppdu_stats_req(struct qwz_softc *sc, uint32_t mask, + uint8_t pdev_id) +{ + struct qwz_dp *dp = &sc->dp; + struct mbuf *m; + struct htt_ppdu_stats_cfg_cmd *cmd; + int len = sizeof(*cmd); + uint8_t pdev_mask; + int ret; + int i; + + for (i = 0; i < sc->hw_params.num_rxmda_per_pdev; i++) { + m = qwz_htc_alloc_mbuf(len); + if (!m) + return ENOMEM; + + cmd = (struct htt_ppdu_stats_cfg_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr)); + cmd->msg = FIELD_PREP(HTT_PPDU_STATS_CFG_MSG_TYPE, + HTT_H2T_MSG_TYPE_PPDU_STATS_CFG); + + pdev_mask = 1 << (pdev_id + i); + cmd->msg |= FIELD_PREP(HTT_PPDU_STATS_CFG_PDEV_ID, pdev_mask); + cmd->msg |= FIELD_PREP(HTT_PPDU_STATS_CFG_TLV_TYPE_BITMASK, + mask); + + ret = qwz_htc_send(&sc->htc, dp->eid, m); + if (ret) { + m_freem(m); + return ret; + } + } + + return 0; +} + +int +qwz_dp_tx_htt_rx_filter_setup(struct qwz_softc *sc, uint32_t ring_id, + int mac_id, enum hal_ring_type ring_type, size_t rx_buf_size, + struct htt_rx_ring_tlv_filter *tlv_filter) +{ + struct htt_rx_ring_selection_cfg_cmd *cmd; + struct hal_srng *srng = &sc->hal.srng_list[ring_id]; + struct hal_srng_params params; + struct mbuf *m; + int len = sizeof(*cmd); + enum htt_srng_ring_type htt_ring_type; + enum htt_srng_ring_id htt_ring_id; + int ret; + + m = qwz_htc_alloc_mbuf(len); + if (!m) + return ENOMEM; + + memset(¶ms, 0, sizeof(params)); + qwz_hal_srng_get_params(sc, srng, ¶ms); + + ret = qwz_dp_tx_get_ring_id_type(sc, mac_id, ring_id, + ring_type, &htt_ring_type, &htt_ring_id); + if (ret) + goto err_free; + + cmd = (struct htt_rx_ring_selection_cfg_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr)); + cmd->info0 = FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_MSG_TYPE, + HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG); + if (htt_ring_type == HTT_SW_TO_HW_RING || + htt_ring_type == HTT_HW_TO_SW_RING) { + cmd->info0 |= + FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID, + DP_SW2HW_MACID(mac_id)); + } else { + cmd->info0 |= + FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID, + mac_id); + } + cmd->info0 |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID, + htt_ring_id); + cmd->info0 |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS, + !!(params.flags & HAL_SRNG_FLAGS_MSI_SWAP)); + cmd->info0 |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS, + !!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP)); + + cmd->info1 = FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE, + rx_buf_size); + cmd->pkt_type_en_flags0 = tlv_filter->pkt_filter_flags0; + cmd->pkt_type_en_flags1 = tlv_filter->pkt_filter_flags1; + cmd->pkt_type_en_flags2 = tlv_filter->pkt_filter_flags2; + cmd->pkt_type_en_flags3 = tlv_filter->pkt_filter_flags3; + cmd->rx_filter_tlv = tlv_filter->rx_filter; + + ret = qwz_htc_send(&sc->htc, sc->dp.eid, m); + if (ret) + goto err_free; + + return 0; + +err_free: + m_freem(m); + + return ret; +} + +int +qwz_dp_rx_pdev_alloc(struct qwz_softc *sc, int mac_id) +{ + struct qwz_pdev_dp *dp = &sc->pdev_dp; + uint32_t ring_id; + int i; + int ret; + + ret = qwz_dp_rx_pdev_srng_alloc(sc); + if (ret) { + printf("%s: failed to setup rx srngs: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = qwz_dp_rxdma_pdev_buf_setup(sc); + if (ret) { + printf("%s: failed to setup rxdma ring: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ring_id = dp->rx_refill_buf_ring.refill_buf_ring.ring_id; + ret = qwz_dp_tx_htt_srng_setup(sc, ring_id, mac_id, HAL_RXDMA_BUF); + if (ret) { + printf("%s: failed to configure rx_refill_buf_ring: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + if (sc->hw_params.rx_mac_buf_ring) { + for (i = 0; i < sc->hw_params.num_rxmda_per_pdev; i++) { + ring_id = dp->rx_mac_buf_ring[i].ring_id; + ret = qwz_dp_tx_htt_srng_setup(sc, ring_id, + mac_id + i, HAL_RXDMA_BUF); + if (ret) { + printf("%s: failed to configure " + "rx_mac_buf_ring%d: %d\n", + sc->sc_dev.dv_xname, i, ret); + return ret; + } + } + } + + for (i = 0; i < sc->hw_params.num_rxmda_per_pdev; i++) { + ring_id = dp->rxdma_err_dst_ring[i].ring_id; + ret = qwz_dp_tx_htt_srng_setup(sc, ring_id, mac_id + i, + HAL_RXDMA_DST); + if (ret) { + printf("%s: failed to configure " + "rxdma_err_dest_ring%d %d\n", + sc->sc_dev.dv_xname, i, ret); + return ret; + } + } + + if (!sc->hw_params.rxdma1_enable) + goto config_refill_ring; +#if 0 + ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id; + ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id, + mac_id, HAL_RXDMA_MONITOR_BUF); + if (ret) { + ath12k_warn(ab, "failed to configure rxdma_mon_buf_ring %d\n", + ret); + return ret; + } + ret = ath12k_dp_tx_htt_srng_setup(ab, + dp->rxdma_mon_dst_ring.ring_id, + mac_id, HAL_RXDMA_MONITOR_DST); + if (ret) { + ath12k_warn(ab, "failed to configure rxdma_mon_dst_ring %d\n", + ret); + return ret; + } + ret = ath12k_dp_tx_htt_srng_setup(ab, + dp->rxdma_mon_desc_ring.ring_id, + mac_id, HAL_RXDMA_MONITOR_DESC); + if (ret) { + ath12k_warn(ab, "failed to configure rxdma_mon_dst_ring %d\n", + ret); + return ret; + } +#endif +config_refill_ring: +#if 0 + for (i = 0; i < sc->hw_params.num_rxmda_per_pdev; i++) { + ret = qwz_dp_tx_htt_srng_setup(sc, + dp->rx_mon_status_refill_ring[i].refill_buf_ring.ring_id, + mac_id + i, HAL_RXDMA_MONITOR_STATUS); + if (ret) { + printf("%s: failed to configure " + "mon_status_refill_ring%d %d\n", + sc->sc_dev.dv_xname, i, ret); + return ret; + } + } +#endif + return 0; +} + +void +qwz_dp_pdev_free(struct qwz_softc *sc) +{ + int i; + + timeout_del(&sc->mon_reap_timer); + + for (i = 0; i < sc->num_radios; i++) + qwz_dp_rx_pdev_free(sc, i); +} + +int +qwz_dp_pdev_alloc(struct qwz_softc *sc) +{ + int ret; + int i; + + for (i = 0; i < sc->num_radios; i++) { + ret = qwz_dp_rx_pdev_alloc(sc, i); + if (ret) { + printf("%s: failed to allocate pdev rx " + "for pdev_id %d\n", sc->sc_dev.dv_xname, i); + goto err; + } + } + + return 0; + +err: + qwz_dp_pdev_free(sc); + + return ret; +} + +int +qwz_dp_tx_htt_h2t_ver_req_msg(struct qwz_softc *sc) +{ + struct qwz_dp *dp = &sc->dp; + struct mbuf *m; + struct htt_ver_req_cmd *cmd; + int len = sizeof(*cmd); + int ret; + + dp->htt_tgt_version_received = 0; + + m = qwz_htc_alloc_mbuf(len); + if (!m) + return ENOMEM; + + cmd = (struct htt_ver_req_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr)); + cmd->ver_reg_info = FIELD_PREP(HTT_VER_REQ_INFO_MSG_ID, + HTT_H2T_MSG_TYPE_VERSION_REQ); + + ret = qwz_htc_send(&sc->htc, dp->eid, m); + if (ret) { + m_freem(m); + return ret; + } + + while (!dp->htt_tgt_version_received) { + ret = tsleep_nsec(&dp->htt_tgt_version_received, 0, + "qwztgtver", SEC_TO_NSEC(3)); + if (ret) + return ETIMEDOUT; + } + + if (dp->htt_tgt_ver_major != HTT_TARGET_VERSION_MAJOR) { + printf("%s: unsupported htt major version %d " + "supported version is %d\n", __func__, + dp->htt_tgt_ver_major, HTT_TARGET_VERSION_MAJOR); + return ENOTSUP; + } + + return 0; +} + +void +qwz_dp_update_vdev_search(struct qwz_softc *sc, struct qwz_vif *arvif) +{ + /* When v2_map_support is true:for STA mode, enable address + * search index, tcl uses ast_hash value in the descriptor. + * When v2_map_support is false: for STA mode, don't enable + * address search index. + */ + switch (arvif->vdev_type) { + case WMI_VDEV_TYPE_STA: + if (sc->hw_params.htt_peer_map_v2) { + arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX; + } else { + arvif->hal_addr_search_flags = HAL_TX_ADDRY_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_DEFAULT; + } + break; + case WMI_VDEV_TYPE_AP: + case WMI_VDEV_TYPE_IBSS: + arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_DEFAULT; + break; + case WMI_VDEV_TYPE_MONITOR: + default: + return; + } +} + +void +qwz_dp_vdev_tx_attach(struct qwz_softc *sc, struct qwz_pdev *pdev, + struct qwz_vif *arvif) +{ + arvif->tcl_metadata |= FIELD_PREP(HTT_TCL_META_DATA_TYPE, 1) | + FIELD_PREP(HTT_TCL_META_DATA_VDEV_ID, arvif->vdev_id) | + FIELD_PREP(HTT_TCL_META_DATA_PDEV_ID, pdev->pdev_id); + + /* set HTT extension valid bit to 0 by default */ + arvif->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT; + + qwz_dp_update_vdev_search(sc, arvif); +} + +void +qwz_dp_tx_status_parse(struct qwz_softc *sc, struct hal_wbm_release_ring *desc, + struct hal_tx_status *ts) +{ + ts->buf_rel_source = FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, + desc->info0); + if (ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW && + ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM) + return; + + if (ts->buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) + return; + + ts->status = FIELD_GET(HAL_WBM_RELEASE_INFO0_TQM_RELEASE_REASON, + desc->info0); + ts->ppdu_id = FIELD_GET(HAL_WBM_RELEASE_INFO1_TQM_STATUS_NUMBER, + desc->info1); + ts->try_cnt = FIELD_GET(HAL_WBM_RELEASE_INFO1_TRANSMIT_COUNT, + desc->info1); + ts->ack_rssi = FIELD_GET(HAL_WBM_RELEASE_INFO2_ACK_FRAME_RSSI, + desc->info2); + if (desc->info2 & HAL_WBM_RELEASE_INFO2_FIRST_MSDU) + ts->flags |= HAL_TX_STATUS_FLAGS_FIRST_MSDU; + ts->peer_id = FIELD_GET(HAL_WBM_RELEASE_INFO3_PEER_ID, desc->info3); + ts->tid = FIELD_GET(HAL_WBM_RELEASE_INFO3_TID, desc->info3); + if (desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_VALID) + ts->rate_stats = desc->rate_stats.info0; + else + ts->rate_stats = 0; +} + +void +qwz_dp_tx_free_txbuf(struct qwz_softc *sc, int msdu_id, + struct dp_tx_ring *tx_ring) +{ + struct qwz_tx_data *tx_data; + + if (msdu_id >= sc->hw_params.tx_ring_size) + return; + + tx_data = &tx_ring->data[msdu_id]; + + bus_dmamap_unload(sc->sc_dmat, tx_data->map); + m_freem(tx_data->m); + tx_data->m = NULL; + + if (tx_ring->queued > 0) + tx_ring->queued--; +} + +void +qwz_dp_tx_htt_tx_complete_buf(struct qwz_softc *sc, struct dp_tx_ring *tx_ring, + struct qwz_dp_htt_wbm_tx_status *ts) +{ + /* Not using Tx status info for now. Just free the buffer. */ + qwz_dp_tx_free_txbuf(sc, ts->msdu_id, tx_ring); +} + +void +qwz_dp_tx_process_htt_tx_complete(struct qwz_softc *sc, void *desc, + uint8_t mac_id, uint32_t msdu_id, struct dp_tx_ring *tx_ring) +{ + struct htt_tx_wbm_completion *status_desc; + struct qwz_dp_htt_wbm_tx_status ts = {0}; + enum hal_wbm_htt_tx_comp_status wbm_status; + + status_desc = desc + HTT_TX_WBM_COMP_STATUS_OFFSET; + + wbm_status = FIELD_GET(HTT_TX_WBM_COMP_INFO0_STATUS, + status_desc->info0); + + switch (wbm_status) { + case HAL_WBM_REL_HTT_TX_COMP_STATUS_OK: + case HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP: + case HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL: + ts.acked = (wbm_status == HAL_WBM_REL_HTT_TX_COMP_STATUS_OK); + ts.msdu_id = msdu_id; + ts.ack_rssi = FIELD_GET(HTT_TX_WBM_COMP_INFO1_ACK_RSSI, + status_desc->info1); + + if (FIELD_GET(HTT_TX_WBM_COMP_INFO2_VALID, status_desc->info2)) + ts.peer_id = FIELD_GET(HTT_TX_WBM_COMP_INFO2_SW_PEER_ID, + status_desc->info2); + else + ts.peer_id = HTT_INVALID_PEER_ID; + + qwz_dp_tx_htt_tx_complete_buf(sc, tx_ring, &ts); + break; + case HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ: + case HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT: + qwz_dp_tx_free_txbuf(sc, msdu_id, tx_ring); + break; + case HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY: + /* This event is to be handled only when the driver decides to + * use WDS offload functionality. + */ + break; + default: + printf("%s: Unknown htt tx status %d\n", + sc->sc_dev.dv_xname, wbm_status); + break; + } +} + +int +qwz_mac_hw_ratecode_to_legacy_rate(struct ieee80211_node *ni, uint8_t hw_rc, + uint8_t preamble, uint8_t *rateidx, uint16_t *rate) +{ + struct ieee80211_rateset *rs = &ni->ni_rates; + int i; + + if (preamble == WMI_RATE_PREAMBLE_CCK) { + hw_rc &= ~ATH12k_HW_RATECODE_CCK_SHORT_PREAM_MASK; + switch (hw_rc) { + case ATH12K_HW_RATE_CCK_LP_1M: + *rate = 2; + break; + case ATH12K_HW_RATE_CCK_LP_2M: + case ATH12K_HW_RATE_CCK_SP_2M: + *rate = 4; + break; + case ATH12K_HW_RATE_CCK_LP_5_5M: + case ATH12K_HW_RATE_CCK_SP_5_5M: + *rate = 11; + break; + case ATH12K_HW_RATE_CCK_LP_11M: + case ATH12K_HW_RATE_CCK_SP_11M: + *rate = 22; + break; + default: + return EINVAL; + } + } else { + switch (hw_rc) { + case ATH12K_HW_RATE_OFDM_6M: + *rate = 12; + break; + case ATH12K_HW_RATE_OFDM_9M: + *rate = 18; + break; + case ATH12K_HW_RATE_OFDM_12M: + *rate = 24; + break; + case ATH12K_HW_RATE_OFDM_18M: + *rate = 36; + break; + case ATH12K_HW_RATE_OFDM_24M: + *rate = 48; + break; + case ATH12K_HW_RATE_OFDM_36M: + *rate = 72; + break; + case ATH12K_HW_RATE_OFDM_48M: + *rate = 96; + break; + case ATH12K_HW_RATE_OFDM_54M: + *rate = 104; + break; + default: + return EINVAL; + } + } + + for (i = 0; i < rs->rs_nrates; i++) { + uint8_t rval = rs->rs_rates[i] & IEEE80211_RATE_VAL; + if (rval == *rate) { + *rateidx = i; + return 0; + } + } + + return EINVAL; +} + +void +qwz_dp_tx_complete_msdu(struct qwz_softc *sc, struct dp_tx_ring *tx_ring, + uint32_t msdu_id, struct hal_tx_status *ts) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct qwz_tx_data *tx_data = &tx_ring->data[msdu_id]; + uint8_t pkt_type, mcs, rateidx; + uint16_t rate; + + if (ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM) { + /* Must not happen */ + return; + } + + bus_dmamap_unload(sc->sc_dmat, tx_data->map); + m_freem(tx_data->m); + tx_data->m = NULL; + + pkt_type = FIELD_GET(HAL_TX_RATE_STATS_INFO0_PKT_TYPE, ts->rate_stats); + mcs = FIELD_GET(HAL_TX_RATE_STATS_INFO0_MCS, ts->rate_stats); + if (qwz_mac_hw_ratecode_to_legacy_rate(tx_data->ni, mcs, pkt_type, + &rateidx, &rate) == 0) + tx_data->ni->ni_txrate = rateidx; + + ieee80211_release_node(ic, tx_data->ni); + tx_data->ni = NULL; + + if (tx_ring->queued > 0) + tx_ring->queued--; +} + +#define QWZ_TX_COMPL_NEXT(x) (((x) + 1) % DP_TX_COMP_RING_SIZE) + +int +qwz_dp_tx_completion_handler(struct qwz_softc *sc, int ring_id) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct qwz_dp *dp = &sc->dp; + int hal_ring_id = dp->tx_ring[ring_id].tcl_comp_ring.ring_id; + struct hal_srng *status_ring = &sc->hal.srng_list[hal_ring_id]; + struct hal_tx_status ts = { 0 }; + struct dp_tx_ring *tx_ring = &dp->tx_ring[ring_id]; + uint32_t *desc; + uint32_t msdu_id; + uint8_t mac_id; +#ifdef notyet + spin_lock_bh(&status_ring->lock); +#endif + qwz_hal_srng_access_begin(sc, status_ring); + + while ((QWZ_TX_COMPL_NEXT(tx_ring->tx_status_head) != + tx_ring->tx_status_tail) && + (desc = qwz_hal_srng_dst_get_next_entry(sc, status_ring))) { + memcpy(&tx_ring->tx_status[tx_ring->tx_status_head], desc, + sizeof(struct hal_wbm_release_ring)); + tx_ring->tx_status_head = + QWZ_TX_COMPL_NEXT(tx_ring->tx_status_head); + } +#if 0 + if (unlikely((ath12k_hal_srng_dst_peek(ab, status_ring) != NULL) && + (ATH12K_TX_COMPL_NEXT(tx_ring->tx_status_head) == + tx_ring->tx_status_tail))) { + /* TODO: Process pending tx_status messages when kfifo_is_full() */ + ath12k_warn(ab, "Unable to process some of the tx_status ring desc because status_fifo is full\n"); + } +#endif + qwz_hal_srng_access_end(sc, status_ring); +#ifdef notyet + spin_unlock_bh(&status_ring->lock); +#endif + while (QWZ_TX_COMPL_NEXT(tx_ring->tx_status_tail) != + tx_ring->tx_status_head) { + struct hal_wbm_release_ring *tx_status; + uint32_t desc_id; + + tx_ring->tx_status_tail = + QWZ_TX_COMPL_NEXT(tx_ring->tx_status_tail); + tx_status = &tx_ring->tx_status[tx_ring->tx_status_tail]; + qwz_dp_tx_status_parse(sc, tx_status, &ts); + + desc_id = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + tx_status->buf_addr_info.info1); + mac_id = FIELD_GET(DP_TX_DESC_ID_MAC_ID, desc_id); + if (mac_id >= MAX_RADIOS) + continue; + msdu_id = FIELD_GET(DP_TX_DESC_ID_MSDU_ID, desc_id); + if (msdu_id >= sc->hw_params.tx_ring_size) + continue; + + if (ts.buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) { + qwz_dp_tx_process_htt_tx_complete(sc, + (void *)tx_status, mac_id, msdu_id, tx_ring); + continue; + } +#if 0 + spin_lock(&tx_ring->tx_idr_lock); + msdu = idr_remove(&tx_ring->txbuf_idr, msdu_id); + if (unlikely(!msdu)) { + ath12k_warn(ab, "tx completion for unknown msdu_id %d\n", + msdu_id); + spin_unlock(&tx_ring->tx_idr_lock); + continue; + } + + spin_unlock(&tx_ring->tx_idr_lock); + ar = ab->pdevs[mac_id].ar; + + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); +#endif + qwz_dp_tx_complete_msdu(sc, tx_ring, msdu_id, &ts); + } + + if (tx_ring->queued < sc->hw_params.tx_ring_size - 1) { + sc->qfullmsk &= ~(1 << ring_id); + if (sc->qfullmsk == 0 && ifq_is_oactive(&ifp->if_snd)) { + ifq_clr_oactive(&ifp->if_snd); + (*ifp->if_start)(ifp); + } + } + + return 0; +} + +void +qwz_hal_rx_reo_ent_paddr_get(struct qwz_softc *sc, void *desc, uint64_t *paddr, + uint32_t *desc_bank) +{ + struct ath12k_buffer_addr *buff_addr = desc; + + *paddr = ((uint64_t)(FIELD_GET(BUFFER_ADDR_INFO1_ADDR, + buff_addr->info1)) << 32) | + FIELD_GET(BUFFER_ADDR_INFO0_ADDR, buff_addr->info0); + + *desc_bank = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, buff_addr->info1); +} + +int +qwz_hal_desc_reo_parse_err(struct qwz_softc *sc, uint32_t *rx_desc, + uint64_t *paddr, uint32_t *desc_bank) +{ + struct hal_reo_dest_ring *desc = (struct hal_reo_dest_ring *)rx_desc; + enum hal_reo_dest_ring_push_reason push_reason; + enum hal_reo_dest_ring_error_code err_code; + + push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, + desc->info0); + err_code = FIELD_GET(HAL_REO_DEST_RING_INFO0_ERROR_CODE, + desc->info0); +#if 0 + ab->soc_stats.reo_error[err_code]++; +#endif + if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED && + push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { + printf("%s: expected error push reason code, received %d\n", + sc->sc_dev.dv_xname, push_reason); + return EINVAL; + } + + if (FIELD_GET(HAL_REO_DEST_RING_INFO0_BUFFER_TYPE, desc->info0) != + HAL_REO_DEST_RING_BUFFER_TYPE_LINK_DESC) { + printf("%s: expected buffer type link_desc", + sc->sc_dev.dv_xname); + return EINVAL; + } + + qwz_hal_rx_reo_ent_paddr_get(sc, rx_desc, paddr, desc_bank); + + return 0; +} + +void +qwz_hal_rx_msdu_link_info_get(void *link_desc, uint32_t *num_msdus, + uint32_t *msdu_cookies, enum hal_rx_buf_return_buf_manager *rbm) +{ + struct hal_rx_msdu_link *link = (struct hal_rx_msdu_link *)link_desc; + struct hal_rx_msdu_details *msdu; + int i; + + *num_msdus = HAL_NUM_RX_MSDUS_PER_LINK_DESC; + + msdu = &link->msdu_link[0]; + *rbm = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, + msdu->buf_addr_info.info1); + + for (i = 0; i < *num_msdus; i++) { + msdu = &link->msdu_link[i]; + + if (!FIELD_GET(BUFFER_ADDR_INFO0_ADDR, + msdu->buf_addr_info.info0)) { + *num_msdus = i; + break; + } + *msdu_cookies = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + msdu->buf_addr_info.info1); + msdu_cookies++; + } +} + +void +qwz_hal_rx_msdu_link_desc_set(struct qwz_softc *sc, void *desc, + void *link_desc, enum hal_wbm_rel_bm_act action) +{ + struct hal_wbm_release_ring *dst_desc = desc; + struct hal_wbm_release_ring *src_desc = link_desc; + + dst_desc->buf_addr_info = src_desc->buf_addr_info; + dst_desc->info0 |= FIELD_PREP(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, + HAL_WBM_REL_SRC_MODULE_SW) | + FIELD_PREP(HAL_WBM_RELEASE_INFO0_BM_ACTION, action) | + FIELD_PREP(HAL_WBM_RELEASE_INFO0_DESC_TYPE, + HAL_WBM_REL_DESC_TYPE_MSDU_LINK); +} + +int +qwz_dp_rx_link_desc_return(struct qwz_softc *sc, uint32_t *link_desc, + enum hal_wbm_rel_bm_act action) +{ + struct qwz_dp *dp = &sc->dp; + struct hal_srng *srng; + uint32_t *desc; + int ret = 0; + + srng = &sc->hal.srng_list[dp->wbm_desc_rel_ring.ring_id]; +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + + desc = qwz_hal_srng_src_get_next_entry(sc, srng); + if (!desc) { + ret = ENOBUFS; + goto exit; + } + + qwz_hal_rx_msdu_link_desc_set(sc, (void *)desc, (void *)link_desc, + action); + +exit: + qwz_hal_srng_access_end(sc, srng); +#ifdef notyet + spin_unlock_bh(&srng->lock); +#endif + return ret; +} + +int +qwz_dp_rx_frag_h_mpdu(struct qwz_softc *sc, struct mbuf *m, + uint32_t *ring_desc) +{ + printf("%s: not implemented\n", __func__); + return ENOTSUP; +} + +static inline uint16_t +qwz_dp_rx_h_msdu_start_msdu_len(struct qwz_softc *sc, struct hal_rx_desc *desc) +{ + return sc->hw_params.hw_ops->rx_desc_get_msdu_len(desc); +} + +void +qwz_dp_process_rx_err_buf(struct qwz_softc *sc, uint32_t *ring_desc, + int buf_id, int drop) +{ + struct qwz_pdev_dp *dp = &sc->pdev_dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + struct mbuf *m; + struct qwz_rx_data *rx_data; + struct hal_rx_desc *rx_desc; + uint16_t msdu_len; + uint32_t hal_rx_desc_sz = sc->hw_params.hal_desc_sz; + + if (buf_id >= rx_ring->bufs_max || isset(rx_ring->freemap, buf_id)) + return; + + rx_data = &rx_ring->rx_data[buf_id]; + bus_dmamap_unload(sc->sc_dmat, rx_data->map); + m = rx_data->m; + rx_data->m = NULL; + setbit(rx_ring->freemap, buf_id); + + if (drop) { + m_freem(m); + return; + } + + rx_desc = mtod(m, struct hal_rx_desc *); + msdu_len = qwz_dp_rx_h_msdu_start_msdu_len(sc, rx_desc); + if ((msdu_len + hal_rx_desc_sz) > DP_RX_BUFFER_SIZE) { +#if 0 + uint8_t *hdr_status = ath12k_dp_rx_h_80211_hdr(ar->ab, rx_desc); + ath12k_warn(ar->ab, "invalid msdu leng %u", msdu_len); + ath12k_dbg_dump(ar->ab, ATH12K_DBG_DATA, NULL, "", hdr_status, + sizeof(struct ieee80211_hdr)); + ath12k_dbg_dump(ar->ab, ATH12K_DBG_DATA, NULL, "", rx_desc, + sizeof(struct hal_rx_desc)); +#endif + m_freem(m); + return; + } + + if (qwz_dp_rx_frag_h_mpdu(sc, m, ring_desc)) { + qwz_dp_rx_link_desc_return(sc, ring_desc, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + } + + m_freem(m); +} + +int +qwz_dp_process_rx_err(struct qwz_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + uint32_t msdu_cookies[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; + struct dp_link_desc_bank *link_desc_banks; + enum hal_rx_buf_return_buf_manager rbm; + int tot_n_bufs_reaped, ret, i; + int n_bufs_reaped[MAX_RADIOS] = {0}; + struct dp_rxdma_ring *rx_ring; + struct dp_srng *reo_except; + uint32_t desc_bank, num_msdus; + struct hal_srng *srng; + struct qwz_dp *dp; + void *link_desc_va; + int buf_id, mac_id; + uint64_t paddr; + uint32_t *desc; + int is_frag; + uint8_t drop = 0; + + tot_n_bufs_reaped = 0; + + dp = &sc->dp; + reo_except = &dp->reo_except_ring; + link_desc_banks = dp->link_desc_banks; + + srng = &sc->hal.srng_list[reo_except->ring_id]; +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + + while ((desc = qwz_hal_srng_dst_get_next_entry(sc, srng))) { + struct hal_reo_dest_ring *reo_desc = + (struct hal_reo_dest_ring *)desc; +#if 0 + ab->soc_stats.err_ring_pkts++; +#endif + ret = qwz_hal_desc_reo_parse_err(sc, desc, &paddr, &desc_bank); + if (ret) { + printf("%s: failed to parse error reo desc %d\n", + sc->sc_dev.dv_xname, ret); + continue; + } + link_desc_va = link_desc_banks[desc_bank].vaddr + + (paddr - link_desc_banks[desc_bank].paddr); + qwz_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, + msdu_cookies, &rbm); + if (rbm != HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST && + rbm != HAL_RX_BUF_RBM_SW3_BM) { +#if 0 + ab->soc_stats.invalid_rbm++; +#endif + printf("%s: invalid return buffer manager %d\n", + sc->sc_dev.dv_xname, rbm); + qwz_dp_rx_link_desc_return(sc, desc, + HAL_WBM_REL_BM_ACT_REL_MSDU); + continue; + } + + is_frag = !!(reo_desc->rx_mpdu_info.info0 & + RX_MPDU_DESC_INFO0_FRAG_FLAG); + + /* Process only rx fragments with one msdu per link desc below, + * and drop msdu's indicated due to error reasons. + */ + if (!is_frag || num_msdus > 1) { + drop = 1; + /* Return the link desc back to wbm idle list */ + qwz_dp_rx_link_desc_return(sc, desc, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + } + + for (i = 0; i < num_msdus; i++) { + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, + msdu_cookies[i]); + + mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, + msdu_cookies[i]); + + qwz_dp_process_rx_err_buf(sc, desc, buf_id, drop); + n_bufs_reaped[mac_id]++; + tot_n_bufs_reaped++; + } + } + + qwz_hal_srng_access_end(sc, srng); +#ifdef notyet + spin_unlock_bh(&srng->lock); +#endif + for (i = 0; i < sc->num_radios; i++) { + if (!n_bufs_reaped[i]) + continue; + + rx_ring = &sc->pdev_dp.rx_refill_buf_ring; + + qwz_dp_rxbufs_replenish(sc, i, rx_ring, n_bufs_reaped[i], + sc->hw_params.hal_params->rx_buf_rbm); + } + + ifp->if_ierrors += tot_n_bufs_reaped; + + return tot_n_bufs_reaped; +} + +int +qwz_hal_wbm_desc_parse_err(void *desc, struct hal_rx_wbm_rel_info *rel_info) +{ + struct hal_wbm_release_ring *wbm_desc = desc; + enum hal_wbm_rel_desc_type type; + enum hal_wbm_rel_src_module rel_src; + enum hal_rx_buf_return_buf_manager ret_buf_mgr; + + type = FIELD_GET(HAL_WBM_RELEASE_INFO0_DESC_TYPE, wbm_desc->info0); + + /* We expect only WBM_REL buffer type */ + if (type != HAL_WBM_REL_DESC_TYPE_REL_MSDU) + return -EINVAL; + + rel_src = FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, + wbm_desc->info0); + if (rel_src != HAL_WBM_REL_SRC_MODULE_RXDMA && + rel_src != HAL_WBM_REL_SRC_MODULE_REO) + return EINVAL; + + ret_buf_mgr = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, + wbm_desc->buf_addr_info.info1); + if (ret_buf_mgr != HAL_RX_BUF_RBM_SW3_BM) { +#if 0 + ab->soc_stats.invalid_rbm++; +#endif + return EINVAL; + } + + rel_info->cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + wbm_desc->buf_addr_info.info1); + rel_info->err_rel_src = rel_src; + if (rel_src == HAL_WBM_REL_SRC_MODULE_REO) { + rel_info->push_reason = FIELD_GET( + HAL_WBM_RELEASE_INFO0_REO_PUSH_REASON, wbm_desc->info0); + rel_info->err_code = FIELD_GET( + HAL_WBM_RELEASE_INFO0_REO_ERROR_CODE, wbm_desc->info0); + } else { + rel_info->push_reason = FIELD_GET( + HAL_WBM_RELEASE_INFO0_RXDMA_PUSH_REASON, wbm_desc->info0); + rel_info->err_code = FIELD_GET( + HAL_WBM_RELEASE_INFO0_RXDMA_ERROR_CODE, wbm_desc->info0); + } + + rel_info->first_msdu = FIELD_GET(HAL_WBM_RELEASE_INFO2_FIRST_MSDU, + wbm_desc->info2); + rel_info->last_msdu = FIELD_GET(HAL_WBM_RELEASE_INFO2_LAST_MSDU, + wbm_desc->info2); + + return 0; +} + +int +qwz_dp_rx_h_null_q_desc(struct qwz_softc *sc, struct qwz_rx_msdu *msdu, + struct qwz_rx_msdu_list *msdu_list) +{ + printf("%s: not implemented\n", __func__); + return ENOTSUP; +} + +int +qwz_dp_rx_h_reo_err(struct qwz_softc *sc, struct qwz_rx_msdu *msdu, + struct qwz_rx_msdu_list *msdu_list) +{ + int drop = 0; +#if 0 + ar->ab->soc_stats.reo_error[rxcb->err_code]++; +#endif + switch (msdu->err_code) { + case HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO: + if (qwz_dp_rx_h_null_q_desc(sc, msdu, msdu_list)) + drop = 1; + break; + case HAL_REO_DEST_RING_ERROR_CODE_PN_CHECK_FAILED: + /* TODO: Do not drop PN failed packets in the driver; + * instead, it is good to drop such packets in mac80211 + * after incrementing the replay counters. + */ + /* fallthrough */ + default: + /* TODO: Review other errors and process them to mac80211 + * as appropriate. + */ + drop = 1; + break; + } + + return drop; +} + +int +qwz_dp_rx_h_rxdma_err(struct qwz_softc *sc, struct qwz_rx_msdu *msdu) +{ + struct ieee80211com *ic = &sc->sc_ic; + int drop = 0; +#if 0 + ar->ab->soc_stats.rxdma_error[rxcb->err_code]++; +#endif + switch (msdu->err_code) { + case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR: + ic->ic_stats.is_rx_locmicfail++; + drop = 1; + break; + default: + /* TODO: Review other rxdma error code to check if anything is + * worth reporting to mac80211 + */ + drop = 1; + break; + } + + return drop; +} + +void +qwz_dp_rx_wbm_err(struct qwz_softc *sc, struct qwz_rx_msdu *msdu, + struct qwz_rx_msdu_list *msdu_list) +{ + int drop = 1; + + switch (msdu->err_rel_src) { + case HAL_WBM_REL_SRC_MODULE_REO: + drop = qwz_dp_rx_h_reo_err(sc, msdu, msdu_list); + break; + case HAL_WBM_REL_SRC_MODULE_RXDMA: + drop = qwz_dp_rx_h_rxdma_err(sc, msdu); + break; + default: + /* msdu will get freed */ + break; + } + + if (drop) { + m_freem(msdu->m); + msdu->m = NULL; + return; + } + + qwz_dp_rx_deliver_msdu(sc, msdu); +} + +int +qwz_dp_rx_process_wbm_err(struct qwz_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct qwz_dp *dp = &sc->dp; + struct dp_rxdma_ring *rx_ring; + struct hal_rx_wbm_rel_info err_info; + struct hal_srng *srng; + struct qwz_rx_msdu_list msdu_list[MAX_RADIOS]; + struct qwz_rx_msdu *msdu; + struct mbuf *m; + struct qwz_rx_data *rx_data; + uint32_t *rx_desc; + int idx, mac_id; + int num_buffs_reaped[MAX_RADIOS] = {0}; + int total_num_buffs_reaped = 0; + int ret, i; + + for (i = 0; i < sc->num_radios; i++) + TAILQ_INIT(&msdu_list[i]); + + srng = &sc->hal.srng_list[dp->rx_rel_ring.ring_id]; +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + + while ((rx_desc = qwz_hal_srng_dst_get_next_entry(sc, srng))) { + ret = qwz_hal_wbm_desc_parse_err(rx_desc, &err_info); + if (ret) { + printf("%s: failed to parse rx error in wbm_rel " + "ring desc %d\n", sc->sc_dev.dv_xname, ret); + continue; + } + + idx = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, err_info.cookie); + mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, err_info.cookie); + + if (mac_id >= MAX_RADIOS) + continue; + + rx_ring = &sc->pdev_dp.rx_refill_buf_ring; + if (idx >= rx_ring->bufs_max || isset(rx_ring->freemap, idx)) + continue; + + rx_data = &rx_ring->rx_data[idx]; + bus_dmamap_unload(sc->sc_dmat, rx_data->map); + m = rx_data->m; + rx_data->m = NULL; + setbit(rx_ring->freemap, idx); + + num_buffs_reaped[mac_id]++; + total_num_buffs_reaped++; + + if (err_info.push_reason != + HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED) { + m_freem(m); + continue; + } + + msdu = &rx_data->rx_msdu; + memset(&msdu->rxi, 0, sizeof(msdu->rxi)); + msdu->m = m; + msdu->err_rel_src = err_info.err_rel_src; + msdu->err_code = err_info.err_code; + msdu->rx_desc = mtod(m, struct hal_rx_desc *); + TAILQ_INSERT_TAIL(&msdu_list[mac_id], msdu, entry); + } + + qwz_hal_srng_access_end(sc, srng); +#ifdef notyet + spin_unlock_bh(&srng->lock); +#endif + if (!total_num_buffs_reaped) + goto done; + + for (i = 0; i < sc->num_radios; i++) { + if (!num_buffs_reaped[i]) + continue; + + rx_ring = &sc->pdev_dp.rx_refill_buf_ring; + qwz_dp_rxbufs_replenish(sc, i, rx_ring, num_buffs_reaped[i], + sc->hw_params.hal_params->rx_buf_rbm); + } + + for (i = 0; i < sc->num_radios; i++) { + while ((msdu = TAILQ_FIRST(msdu_list))) { + TAILQ_REMOVE(msdu_list, msdu, entry); + if (test_bit(ATH12K_CAC_RUNNING, sc->sc_flags)) { + m_freem(msdu->m); + msdu->m = NULL; + continue; + } + qwz_dp_rx_wbm_err(sc, msdu, &msdu_list[i]); + msdu->m = NULL; + } + } +done: + ifp->if_ierrors += total_num_buffs_reaped; + + return total_num_buffs_reaped; +} + +struct qwz_rx_msdu * +qwz_dp_rx_get_msdu_last_buf(struct qwz_rx_msdu_list *msdu_list, + struct qwz_rx_msdu *first) +{ + struct qwz_rx_msdu *msdu; + + if (!first->is_continuation) + return first; + + TAILQ_FOREACH(msdu, msdu_list, entry) { + if (!msdu->is_continuation) + return msdu; + } + + return NULL; +} + +static inline void * +qwz_dp_rx_get_attention(struct qwz_softc *sc, struct hal_rx_desc *desc) +{ + return sc->hw_params.hw_ops->rx_desc_get_attention(desc); +} + +int +qwz_dp_rx_h_attn_is_mcbc(struct qwz_softc *sc, struct hal_rx_desc *desc) +{ + struct rx_attention *attn = qwz_dp_rx_get_attention(sc, desc); + + return qwz_dp_rx_h_msdu_end_first_msdu(sc, desc) && + (!!FIELD_GET(RX_ATTENTION_INFO1_MCAST_BCAST, + le32toh(attn->info1))); +} + +static inline uint8_t +qwz_dp_rx_h_msdu_end_l3pad(struct qwz_softc *sc, struct hal_rx_desc *desc) +{ + return sc->hw_params.hw_ops->rx_desc_get_l3_pad_bytes(desc); +} + +static inline int +qwz_dp_rx_h_attn_msdu_done(struct rx_attention *attn) +{ + return !!FIELD_GET(RX_ATTENTION_INFO2_MSDU_DONE, le32toh(attn->info2)); +} + +static inline uint32_t +qwz_dp_rx_h_msdu_start_freq(struct qwz_softc *sc, struct hal_rx_desc *desc) +{ + return sc->hw_params.hw_ops->rx_desc_get_msdu_freq(desc); +} + +uint32_t +qwz_dp_rx_h_attn_mpdu_err(struct rx_attention *attn) +{ + uint32_t info = le32toh(attn->info1); + uint32_t errmap = 0; + + if (info & RX_ATTENTION_INFO1_FCS_ERR) + errmap |= DP_RX_MPDU_ERR_FCS; + + if (info & RX_ATTENTION_INFO1_DECRYPT_ERR) + errmap |= DP_RX_MPDU_ERR_DECRYPT; + + if (info & RX_ATTENTION_INFO1_TKIP_MIC_ERR) + errmap |= DP_RX_MPDU_ERR_TKIP_MIC; + + if (info & RX_ATTENTION_INFO1_A_MSDU_ERROR) + errmap |= DP_RX_MPDU_ERR_AMSDU_ERR; + + if (info & RX_ATTENTION_INFO1_OVERFLOW_ERR) + errmap |= DP_RX_MPDU_ERR_OVERFLOW; + + if (info & RX_ATTENTION_INFO1_MSDU_LEN_ERR) + errmap |= DP_RX_MPDU_ERR_MSDU_LEN; + + if (info & RX_ATTENTION_INFO1_MPDU_LEN_ERR) + errmap |= DP_RX_MPDU_ERR_MPDU_LEN; + + return errmap; +} + +int +qwz_dp_rx_h_attn_msdu_len_err(struct qwz_softc *sc, struct hal_rx_desc *desc) +{ + struct rx_attention *rx_attention; + uint32_t errmap; + + rx_attention = qwz_dp_rx_get_attention(sc, desc); + errmap = qwz_dp_rx_h_attn_mpdu_err(rx_attention); + + return errmap & DP_RX_MPDU_ERR_MSDU_LEN; +} + +int +qwz_dp_rx_h_attn_is_decrypted(struct rx_attention *attn) +{ + return (FIELD_GET(RX_ATTENTION_INFO2_DCRYPT_STATUS_CODE, + le32toh(attn->info2)) == RX_DESC_DECRYPT_STATUS_CODE_OK); +} + +int +qwz_dp_rx_msdu_coalesce(struct qwz_softc *sc, struct qwz_rx_msdu_list *msdu_list, + struct qwz_rx_msdu *first, struct qwz_rx_msdu *last, uint8_t l3pad_bytes, + int msdu_len) +{ + printf("%s: not implemented\n", __func__); + return ENOTSUP; +} + +void +qwz_dp_rx_h_rate(struct qwz_softc *sc, struct hal_rx_desc *rx_desc, + struct ieee80211_rxinfo *rxi) +{ + /* TODO */ +} + +void +qwz_dp_rx_h_ppdu(struct qwz_softc *sc, struct hal_rx_desc *rx_desc, + struct ieee80211_rxinfo *rxi) +{ + uint8_t channel_num; + uint32_t meta_data; + + meta_data = qwz_dp_rx_h_msdu_start_freq(sc, rx_desc); + channel_num = meta_data & 0xff; + + rxi->rxi_chan = channel_num; + + qwz_dp_rx_h_rate(sc, rx_desc, rxi); +} + +void +qwz_dp_rx_h_undecap_nwifi(struct qwz_softc *sc, struct qwz_rx_msdu *msdu, + uint8_t *first_hdr, enum hal_encrypt_type enctype) +{ + /* + * This function will need to do some work once we are receiving + * aggregated frames. For now, it needs to do nothing. + */ + + if (!msdu->is_first_msdu) + printf("%s: not implemented\n", __func__); +} + +void +qwz_dp_rx_h_undecap_raw(struct qwz_softc *sc, struct qwz_rx_msdu *msdu, + enum hal_encrypt_type enctype, int decrypted) +{ +#if 0 + struct ieee80211_hdr *hdr; + size_t hdr_len; + size_t crypto_len; +#endif + + if (!msdu->is_first_msdu || + !(msdu->is_first_msdu && msdu->is_last_msdu)) + return; + + m_adj(msdu->m, -IEEE80211_CRC_LEN); +#if 0 + if (!decrypted) + return; + + hdr = (void *)msdu->data; + + /* Tail */ + if (status->flag & RX_FLAG_IV_STRIPPED) { + skb_trim(msdu, msdu->len - + ath12k_dp_rx_crypto_mic_len(ar, enctype)); + + skb_trim(msdu, msdu->len - + ath12k_dp_rx_crypto_icv_len(ar, enctype)); + } else { + /* MIC */ + if (status->flag & RX_FLAG_MIC_STRIPPED) + skb_trim(msdu, msdu->len - + ath12k_dp_rx_crypto_mic_len(ar, enctype)); + + /* ICV */ + if (status->flag & RX_FLAG_ICV_STRIPPED) + skb_trim(msdu, msdu->len - + ath12k_dp_rx_crypto_icv_len(ar, enctype)); + } + + /* MMIC */ + if ((status->flag & RX_FLAG_MMIC_STRIPPED) && + !ieee80211_has_morefrags(hdr->frame_control) && + enctype == HAL_ENCRYPT_TYPE_TKIP_MIC) + skb_trim(msdu, msdu->len - IEEE80211_CCMP_MIC_LEN); + + /* Head */ + if (status->flag & RX_FLAG_IV_STRIPPED) { + hdr_len = ieee80211_hdrlen(hdr->frame_control); + crypto_len = ath12k_dp_rx_crypto_param_len(ar, enctype); + + memmove((void *)msdu->data + crypto_len, + (void *)msdu->data, hdr_len); + skb_pull(msdu, crypto_len); + } +#endif +} + +static inline uint8_t * +qwz_dp_rx_h_80211_hdr(struct qwz_softc *sc, struct hal_rx_desc *desc) +{ + return sc->hw_params.hw_ops->rx_desc_get_hdr_status(desc); +} + +static inline enum hal_encrypt_type +qwz_dp_rx_h_mpdu_start_enctype(struct qwz_softc *sc, struct hal_rx_desc *desc) +{ + if (!sc->hw_params.hw_ops->rx_desc_encrypt_valid(desc)) + return HAL_ENCRYPT_TYPE_OPEN; + + return sc->hw_params.hw_ops->rx_desc_get_encrypt_type(desc); +} + +static inline uint8_t +qwz_dp_rx_h_msdu_start_decap_type(struct qwz_softc *sc, struct hal_rx_desc *desc) +{ + return sc->hw_params.hw_ops->rx_desc_get_decap_type(desc); +} + +void +qwz_dp_rx_h_undecap(struct qwz_softc *sc, struct qwz_rx_msdu *msdu, + struct hal_rx_desc *rx_desc, enum hal_encrypt_type enctype, + int decrypted) +{ + uint8_t *first_hdr; + uint8_t decap; + + first_hdr = qwz_dp_rx_h_80211_hdr(sc, rx_desc); + decap = qwz_dp_rx_h_msdu_start_decap_type(sc, rx_desc); + + switch (decap) { + case DP_RX_DECAP_TYPE_NATIVE_WIFI: + qwz_dp_rx_h_undecap_nwifi(sc, msdu, first_hdr, enctype); + break; + case DP_RX_DECAP_TYPE_RAW: + qwz_dp_rx_h_undecap_raw(sc, msdu, enctype, decrypted); + break; +#if 0 + case DP_RX_DECAP_TYPE_ETHERNET2_DIX: + ehdr = (struct ethhdr *)msdu->data; + + /* mac80211 allows fast path only for authorized STA */ + if (ehdr->h_proto == cpu_to_be16(ETH_P_PAE)) { + ATH12K_SKB_RXCB(msdu)->is_eapol = true; + ath12k_dp_rx_h_undecap_eth(ar, msdu, first_hdr, + enctype, status); + break; + } + + /* PN for mcast packets will be validated in mac80211; + * remove eth header and add 802.11 header. + */ + if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) + ath12k_dp_rx_h_undecap_eth(ar, msdu, first_hdr, + enctype, status); + break; + case DP_RX_DECAP_TYPE_8023: + /* TODO: Handle undecap for these formats */ + break; +#endif + } +} + +int +qwz_dp_rx_h_mpdu(struct qwz_softc *sc, struct qwz_rx_msdu *msdu, + struct hal_rx_desc *rx_desc) +{ + struct ieee80211com *ic = &sc->sc_ic; + int fill_crypto_hdr = 0; + enum hal_encrypt_type enctype; + int is_decrypted = 0; +#if 0 + struct ath12k_skb_rxcb *rxcb; +#endif + struct ieee80211_frame *wh; +#if 0 + struct ath12k_peer *peer; +#endif + struct rx_attention *rx_attention; + uint32_t err_bitmap; + + /* PN for multicast packets will be checked in net80211 */ + fill_crypto_hdr = qwz_dp_rx_h_attn_is_mcbc(sc, rx_desc); + msdu->is_mcbc = fill_crypto_hdr; +#if 0 + if (rxcb->is_mcbc) { + rxcb->peer_id = ath12k_dp_rx_h_mpdu_start_peer_id(ar->ab, rx_desc); + rxcb->seq_no = ath12k_dp_rx_h_mpdu_start_seq_no(ar->ab, rx_desc); + } + + spin_lock_bh(&ar->ab->base_lock); + peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu); + if (peer) { + if (rxcb->is_mcbc) + enctype = peer->sec_type_grp; + else + enctype = peer->sec_type; + } else { +#endif + enctype = qwz_dp_rx_h_mpdu_start_enctype(sc, rx_desc); +#if 0 + } + spin_unlock_bh(&ar->ab->base_lock); +#endif + rx_attention = qwz_dp_rx_get_attention(sc, rx_desc); + err_bitmap = qwz_dp_rx_h_attn_mpdu_err(rx_attention); + if (enctype != HAL_ENCRYPT_TYPE_OPEN && !err_bitmap) + is_decrypted = qwz_dp_rx_h_attn_is_decrypted(rx_attention); +#if 0 + /* Clear per-MPDU flags while leaving per-PPDU flags intact */ + rx_status->flag &= ~(RX_FLAG_FAILED_FCS_CRC | + RX_FLAG_MMIC_ERROR | + RX_FLAG_DECRYPTED | + RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED); + +#endif + if (err_bitmap & DP_RX_MPDU_ERR_FCS) { + if (ic->ic_flags & IEEE80211_F_RSNON) + ic->ic_stats.is_rx_decryptcrc++; + else + ic->ic_stats.is_rx_decap++; + } + + /* XXX Trusting firmware to handle Michael MIC counter-measures... */ + if (err_bitmap & DP_RX_MPDU_ERR_TKIP_MIC) + ic->ic_stats.is_rx_locmicfail++; + + if (err_bitmap & DP_RX_MPDU_ERR_DECRYPT) + ic->ic_stats.is_rx_wepfail++; + + if (is_decrypted) { +#if 0 + rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED; + + if (fill_crypto_hdr) + rx_status->flag |= RX_FLAG_MIC_STRIPPED | + RX_FLAG_ICV_STRIPPED; + else + rx_status->flag |= RX_FLAG_IV_STRIPPED | + RX_FLAG_PN_VALIDATED; +#endif + msdu->rxi.rxi_flags |= IEEE80211_RXI_HWDEC; + } +#if 0 + ath12k_dp_rx_h_csum_offload(ar, msdu); +#endif + qwz_dp_rx_h_undecap(sc, msdu, rx_desc, enctype, is_decrypted); + + if (is_decrypted && !fill_crypto_hdr && + qwz_dp_rx_h_msdu_start_decap_type(sc, rx_desc) != + DP_RX_DECAP_TYPE_ETHERNET2_DIX) { + /* Hardware has stripped the IV. */ + wh = mtod(msdu->m, struct ieee80211_frame *); + wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; + } + + return err_bitmap ? EIO : 0; +} + +int +qwz_dp_rx_process_msdu(struct qwz_softc *sc, struct qwz_rx_msdu *msdu, + struct qwz_rx_msdu_list *msdu_list) +{ + struct hal_rx_desc *rx_desc, *lrx_desc; + struct rx_attention *rx_attention; + struct qwz_rx_msdu *last_buf; + uint8_t l3_pad_bytes; + uint16_t msdu_len; + int ret; + uint32_t hal_rx_desc_sz = sc->hw_params.hal_desc_sz; + + last_buf = qwz_dp_rx_get_msdu_last_buf(msdu_list, msdu); + if (!last_buf) { + DPRINTF("%s: No valid Rx buffer to access " + "Atten/MSDU_END/MPDU_END tlvs\n", __func__); + return EIO; + } + + rx_desc = mtod(msdu->m, struct hal_rx_desc *); + if (qwz_dp_rx_h_attn_msdu_len_err(sc, rx_desc)) { + DPRINTF("%s: msdu len not valid\n", __func__); + return EIO; + } + + lrx_desc = mtod(last_buf->m, struct hal_rx_desc *); + rx_attention = qwz_dp_rx_get_attention(sc, lrx_desc); + if (!qwz_dp_rx_h_attn_msdu_done(rx_attention)) { + DPRINTF("%s: msdu_done bit in attention is not set\n", + __func__); + return EIO; + } + + msdu->rx_desc = rx_desc; + msdu_len = qwz_dp_rx_h_msdu_start_msdu_len(sc, rx_desc); + l3_pad_bytes = qwz_dp_rx_h_msdu_end_l3pad(sc, lrx_desc); + + if (msdu->is_frag) { + m_adj(msdu->m, hal_rx_desc_sz); + msdu->m->m_len = msdu->m->m_pkthdr.len = msdu_len; + } else if (!msdu->is_continuation) { + if ((msdu_len + hal_rx_desc_sz) > DP_RX_BUFFER_SIZE) { +#if 0 + uint8_t *hdr_status; + + hdr_status = ath12k_dp_rx_h_80211_hdr(ab, rx_desc); +#endif + DPRINTF("%s: invalid msdu len %u\n", + __func__, msdu_len); +#if 0 + ath12k_dbg_dump(ab, ATH12K_DBG_DATA, NULL, "", hdr_status, + sizeof(struct ieee80211_hdr)); + ath12k_dbg_dump(ab, ATH12K_DBG_DATA, NULL, "", rx_desc, + sizeof(struct hal_rx_desc)); +#endif + return EINVAL; + } + m_adj(msdu->m, hal_rx_desc_sz + l3_pad_bytes); + msdu->m->m_len = msdu->m->m_pkthdr.len = msdu_len; + } else { + ret = qwz_dp_rx_msdu_coalesce(sc, msdu_list, msdu, last_buf, + l3_pad_bytes, msdu_len); + if (ret) { + DPRINTF("%s: failed to coalesce msdu rx buffer%d\n", + __func__, ret); + return ret; + } + } + + memset(&msdu->rxi, 0, sizeof(msdu->rxi)); + qwz_dp_rx_h_ppdu(sc, rx_desc, &msdu->rxi); + + return qwz_dp_rx_h_mpdu(sc, msdu, rx_desc); +} + +void +qwz_dp_rx_deliver_msdu(struct qwz_softc *sc, struct qwz_rx_msdu *msdu) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + + wh = mtod(msdu->m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, wh); + +#if NBPFILTER > 0 + if (sc->sc_drvbpf != NULL) { + struct qwz_rx_radiotap_header *tap = &sc->sc_rxtap; + + bpf_mtap_hdr(sc->sc_drvbpf, tap, sc->sc_rxtap_len, + msdu->m, BPF_DIRECTION_IN); + } +#endif + ieee80211_input(ifp, msdu->m, ni, &msdu->rxi); + ieee80211_release_node(ic, ni); +} + +void +qwz_dp_rx_process_received_packets(struct qwz_softc *sc, + struct qwz_rx_msdu_list *msdu_list, int mac_id) +{ + struct qwz_rx_msdu *msdu; + int ret; + + while ((msdu = TAILQ_FIRST(msdu_list))) { + TAILQ_REMOVE(msdu_list, msdu, entry); + ret = qwz_dp_rx_process_msdu(sc, msdu, msdu_list); + if (ret) { + DNPRINTF(QWZ_D_MAC, "Unable to process msdu: %d", ret); + m_freem(msdu->m); + msdu->m = NULL; + continue; + } + + qwz_dp_rx_deliver_msdu(sc, msdu); + msdu->m = NULL; + } +} + +int +qwz_dp_process_rx(struct qwz_softc *sc, int ring_id) +{ + struct qwz_dp *dp = &sc->dp; + struct qwz_pdev_dp *pdev_dp = &sc->pdev_dp; + struct dp_rxdma_ring *rx_ring; + int num_buffs_reaped[MAX_RADIOS] = {0}; + struct qwz_rx_msdu_list msdu_list[MAX_RADIOS]; + struct qwz_rx_msdu *msdu; + struct mbuf *m; + struct qwz_rx_data *rx_data; + int total_msdu_reaped = 0; + struct hal_srng *srng; + int done = 0; + int idx; + unsigned int mac_id; + struct hal_reo_dest_ring *desc; + enum hal_reo_dest_ring_push_reason push_reason; + uint32_t cookie; + int i; + + for (i = 0; i < MAX_RADIOS; i++) + TAILQ_INIT(&msdu_list[i]); + + srng = &sc->hal.srng_list[dp->reo_dst_ring[ring_id].ring_id]; +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif +try_again: + qwz_hal_srng_access_begin(sc, srng); + + while ((desc = (struct hal_reo_dest_ring *) + qwz_hal_srng_dst_get_next_entry(sc, srng))) { + cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + desc->buf_addr_info.info1); + idx = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie); + mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, cookie); + + if (mac_id >= MAX_RADIOS) + continue; + + rx_ring = &pdev_dp->rx_refill_buf_ring; + if (idx >= rx_ring->bufs_max || isset(rx_ring->freemap, idx)) + continue; + + rx_data = &rx_ring->rx_data[idx]; + bus_dmamap_unload(sc->sc_dmat, rx_data->map); + m = rx_data->m; + rx_data->m = NULL; + setbit(rx_ring->freemap, idx); + + num_buffs_reaped[mac_id]++; + + push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, + desc->info0); + if (push_reason != + HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { + m_freem(m); +#if 0 + sc->soc_stats.hal_reo_error[ + dp->reo_dst_ring[ring_id].ring_id]++; +#endif + continue; + } + + msdu = &rx_data->rx_msdu; + msdu->m = m; + msdu->is_first_msdu = !!(desc->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); + msdu->is_last_msdu = !!(desc->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); + msdu->is_continuation = !!(desc->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); + msdu->peer_id = FIELD_GET(RX_MPDU_DESC_META_DATA_PEER_ID, + desc->rx_mpdu_info.meta_data); + msdu->seq_no = FIELD_GET(RX_MPDU_DESC_INFO0_SEQ_NUM, + desc->rx_mpdu_info.info0); + msdu->tid = FIELD_GET(HAL_REO_DEST_RING_INFO0_RX_QUEUE_NUM, + desc->info0); + + msdu->mac_id = mac_id; + TAILQ_INSERT_TAIL(&msdu_list[mac_id], msdu, entry); + + if (msdu->is_continuation) { + done = 0; + } else { + total_msdu_reaped++; + done = 1; + } + } + + /* Hw might have updated the head pointer after we cached it. + * In this case, even though there are entries in the ring we'll + * get rx_desc NULL. Give the read another try with updated cached + * head pointer so that we can reap complete MPDU in the current + * rx processing. + */ + if (!done && qwz_hal_srng_dst_num_free(sc, srng, 1)) { + qwz_hal_srng_access_end(sc, srng); + goto try_again; + } + + qwz_hal_srng_access_end(sc, srng); +#ifdef notyet + spin_unlock_bh(&srng->lock); +#endif + if (!total_msdu_reaped) + goto exit; + + for (i = 0; i < sc->num_radios; i++) { + if (!num_buffs_reaped[i]) + continue; + + qwz_dp_rx_process_received_packets(sc, &msdu_list[i], i); + + rx_ring = &sc->pdev_dp.rx_refill_buf_ring; + + qwz_dp_rxbufs_replenish(sc, i, rx_ring, num_buffs_reaped[i], + sc->hw_params.hal_params->rx_buf_rbm); + } +exit: + return total_msdu_reaped; +} + +struct mbuf * +qwz_dp_rx_alloc_mon_status_buf(struct qwz_softc *sc, + struct dp_rxdma_ring *rx_ring, int *buf_idx) +{ + struct mbuf *m; + struct qwz_rx_data *rx_data; + const size_t size = DP_RX_BUFFER_SIZE; + int ret, idx; + + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) + return NULL; + + if (size <= MCLBYTES) + MCLGET(m, M_DONTWAIT); + else + MCLGETL(m, M_DONTWAIT, size); + if ((m->m_flags & M_EXT) == 0) + goto fail_free_mbuf; + + m->m_len = m->m_pkthdr.len = size; + idx = qwz_next_free_rxbuf_idx(rx_ring); + if (idx == -1) + goto fail_free_mbuf; + + rx_data = &rx_ring->rx_data[idx]; + if (rx_data->m != NULL) + goto fail_free_mbuf; + + if (rx_data->map == NULL) { + ret = bus_dmamap_create(sc->sc_dmat, size, 1, + size, 0, BUS_DMA_NOWAIT, &rx_data->map); + if (ret) + goto fail_free_mbuf; + } + + ret = bus_dmamap_load_mbuf(sc->sc_dmat, rx_data->map, m, + BUS_DMA_READ | BUS_DMA_NOWAIT); + if (ret) { + printf("%s: can't map mbuf (error %d)\n", + sc->sc_dev.dv_xname, ret); + goto fail_free_mbuf; + } + + *buf_idx = idx; + rx_data->m = m; + clrbit(rx_ring->freemap, idx); + return m; + +fail_free_mbuf: + m_freem(m); + return NULL; +} + +int +qwz_dp_rx_reap_mon_status_ring(struct qwz_softc *sc, int mac_id, + struct mbuf_list *ml) +{ + const struct ath12k_hw_hal_params *hal_params; + struct qwz_pdev_dp *dp; + struct dp_rxdma_ring *rx_ring; + struct qwz_mon_data *pmon; + struct hal_srng *srng; + void *rx_mon_status_desc; + struct mbuf *m; + struct qwz_rx_data *rx_data; + struct hal_tlv_hdr *tlv; + uint32_t cookie; + int buf_idx, srng_id; + uint64_t paddr; + uint8_t rbm; + int num_buffs_reaped = 0; + + dp = &sc->pdev_dp; + pmon = &dp->mon_data; + + srng_id = sc->hw_params.hw_ops->mac_id_to_srng_id(&sc->hw_params, + mac_id); + rx_ring = &dp->rx_mon_status_refill_ring[srng_id]; + + srng = &sc->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + while (1) { + rx_mon_status_desc = qwz_hal_srng_src_peek(sc, srng); + if (!rx_mon_status_desc) { + pmon->buf_state = DP_MON_STATUS_REPLINISH; + break; + } + + qwz_hal_rx_buf_addr_info_get(rx_mon_status_desc, &paddr, + &cookie, &rbm); + if (paddr) { + buf_idx = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie); + if (buf_idx >= rx_ring->bufs_max || + isset(rx_ring->freemap, buf_idx)) { + pmon->buf_state = DP_MON_STATUS_REPLINISH; + goto move_next; + } + + rx_data = &rx_ring->rx_data[buf_idx]; + + bus_dmamap_sync(sc->sc_dmat, rx_data->map, 0, + rx_data->m->m_pkthdr.len, BUS_DMASYNC_POSTREAD); + + tlv = mtod(rx_data->m, struct hal_tlv_hdr *); + if (FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl) != + HAL_RX_STATUS_BUFFER_DONE) { + /* If done status is missing, hold onto status + * ring until status is done for this status + * ring buffer. + * Keep HP in mon_status_ring unchanged, + * and break from here. + * Check status for same buffer for next time + */ + pmon->buf_state = DP_MON_STATUS_NO_DMA; + break; + } + + bus_dmamap_unload(sc->sc_dmat, rx_data->map); + m = rx_data->m; + rx_data->m = NULL; + setbit(rx_ring->freemap, buf_idx); +#if 0 + if (ab->hw_params.full_monitor_mode) { + ath12k_dp_rx_mon_update_status_buf_state(pmon, tlv); + if (paddr == pmon->mon_status_paddr) + pmon->buf_state = DP_MON_STATUS_MATCH; + } +#endif + ml_enqueue(ml, m); + } else { + pmon->buf_state = DP_MON_STATUS_REPLINISH; + } +move_next: + m = qwz_dp_rx_alloc_mon_status_buf(sc, rx_ring, &buf_idx); + if (!m) { + hal_params = sc->hw_params.hal_params; + qwz_hal_rx_buf_addr_info_set(rx_mon_status_desc, 0, 0, + hal_params->rx_buf_rbm); + num_buffs_reaped++; + break; + } + rx_data = &rx_ring->rx_data[buf_idx]; + + cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, mac_id) | + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_idx); + + paddr = rx_data->map->dm_segs[0].ds_addr; + qwz_hal_rx_buf_addr_info_set(rx_mon_status_desc, paddr, + cookie, sc->hw_params.hal_params->rx_buf_rbm); + qwz_hal_srng_src_get_next_entry(sc, srng); + num_buffs_reaped++; + } + qwz_hal_srng_access_end(sc, srng); +#ifdef notyet + spin_unlock_bh(&srng->lock); +#endif + return num_buffs_reaped; +} + +enum hal_rx_mon_status +qwz_hal_rx_parse_mon_status(struct qwz_softc *sc, + struct hal_rx_mon_ppdu_info *ppdu_info, struct mbuf *m) +{ + /* TODO */ + return HAL_RX_MON_STATUS_PPDU_NOT_DONE; +} + +int +qwz_dp_rx_process_mon_status(struct qwz_softc *sc, int mac_id) +{ + enum hal_rx_mon_status hal_status; + struct mbuf *m; + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); +#if 0 + struct ath12k_peer *peer; + struct ath12k_sta *arsta; +#endif + int num_buffs_reaped = 0; +#if 0 + uint32_t rx_buf_sz; + uint16_t log_type; +#endif + struct qwz_mon_data *pmon = (struct qwz_mon_data *)&sc->pdev_dp.mon_data; +#if 0 + struct qwz_pdev_mon_stats *rx_mon_stats = &pmon->rx_mon_stats; +#endif + struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; + + num_buffs_reaped = qwz_dp_rx_reap_mon_status_ring(sc, mac_id, &ml); + if (!num_buffs_reaped) + goto exit; + + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; + + while ((m = ml_dequeue(&ml))) { +#if 0 + if (ath12k_debugfs_is_pktlog_lite_mode_enabled(ar)) { + log_type = ATH12K_PKTLOG_TYPE_LITE_RX; + rx_buf_sz = DP_RX_BUFFER_SIZE_LITE; + } else if (ath12k_debugfs_is_pktlog_rx_stats_enabled(ar)) { + log_type = ATH12K_PKTLOG_TYPE_RX_STATBUF; + rx_buf_sz = DP_RX_BUFFER_SIZE; + } else { + log_type = ATH12K_PKTLOG_TYPE_INVALID; + rx_buf_sz = 0; + } + + if (log_type != ATH12K_PKTLOG_TYPE_INVALID) + trace_ath12k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); +#endif + + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; + hal_status = qwz_hal_rx_parse_mon_status(sc, ppdu_info, m); +#if 0 + if (test_bit(ATH12K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && + pmon->mon_ppdu_status == DP_PPDU_STATUS_START && + hal_status == HAL_TLV_STATUS_PPDU_DONE) { + rx_mon_stats->status_ppdu_done++; + pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; + ath12k_dp_rx_mon_dest_process(ar, mac_id, budget, napi); + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + } +#endif + if (ppdu_info->peer_id == HAL_INVALID_PEERID || + hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { + m_freem(m); + continue; + } +#if 0 + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id); + + if (!peer || !peer->sta) { + ath12k_dbg(ab, ATH12K_DBG_DATA, + "failed to find the peer with peer_id %d\n", + ppdu_info->peer_id); + goto next_skb; + } + + arsta = (struct ath12k_sta *)peer->sta->drv_priv; + ath12k_dp_rx_update_peer_stats(arsta, ppdu_info); + + if (ath12k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) + trace_ath12k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + +next_skb: + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + + dev_kfree_skb_any(skb); + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; +#endif + } +exit: + return num_buffs_reaped; +} + +int +qwz_dp_rx_process_mon_rings(struct qwz_softc *sc, int mac_id) +{ + int ret = 0; +#if 0 + if (test_bit(ATH12K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && + ab->hw_params.full_monitor_mode) + ret = ath12k_dp_full_mon_process_rx(ab, mac_id, napi, budget); + else +#endif + ret = qwz_dp_rx_process_mon_status(sc, mac_id); + + return ret; +} + +void +qwz_dp_service_mon_ring(void *arg) +{ + struct qwz_softc *sc = arg; + int i; + + for (i = 0; i < sc->hw_params.num_rxmda_per_pdev; i++) + qwz_dp_rx_process_mon_rings(sc, i); + + timeout_add(&sc->mon_reap_timer, ATH12K_MON_TIMER_INTERVAL); +} + +int +qwz_dp_process_rxdma_err(struct qwz_softc *sc, int mac_id) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct dp_srng *err_ring; + struct dp_rxdma_ring *rx_ring; + struct dp_link_desc_bank *link_desc_banks = sc->dp.link_desc_banks; + struct hal_srng *srng; + uint32_t msdu_cookies[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; + enum hal_rx_buf_return_buf_manager rbm; + enum hal_reo_entr_rxdma_ecode rxdma_err_code; + struct qwz_rx_data *rx_data; + struct hal_reo_entrance_ring *entr_ring; + void *desc; + int num_buf_freed = 0; + uint64_t paddr; + uint32_t desc_bank; + void *link_desc_va; + int num_msdus; + int i, idx, srng_id; + + srng_id = sc->hw_params.hw_ops->mac_id_to_srng_id(&sc->hw_params, + mac_id); + err_ring = &sc->pdev_dp.rxdma_err_dst_ring[srng_id]; + rx_ring = &sc->pdev_dp.rx_refill_buf_ring; + + srng = &sc->hal.srng_list[err_ring->ring_id]; +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + + while ((desc = qwz_hal_srng_dst_get_next_entry(sc, srng))) { + qwz_hal_rx_reo_ent_paddr_get(sc, desc, &paddr, &desc_bank); + + entr_ring = (struct hal_reo_entrance_ring *)desc; + rxdma_err_code = FIELD_GET( + HAL_REO_ENTR_RING_INFO1_RXDMA_ERROR_CODE, + entr_ring->info1); +#if 0 + ab->soc_stats.rxdma_error[rxdma_err_code]++; +#endif + link_desc_va = link_desc_banks[desc_bank].vaddr + + (paddr - link_desc_banks[desc_bank].paddr); + qwz_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, + msdu_cookies, &rbm); + + for (i = 0; i < num_msdus; i++) { + idx = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, + msdu_cookies[i]); + if (idx >= rx_ring->bufs_max || + isset(rx_ring->freemap, idx)) + continue; + + rx_data = &rx_ring->rx_data[idx]; + + bus_dmamap_unload(sc->sc_dmat, rx_data->map); + m_freem(rx_data->m); + rx_data->m = NULL; + setbit(rx_ring->freemap, idx); + + num_buf_freed++; + } + + qwz_dp_rx_link_desc_return(sc, desc, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + } + + qwz_hal_srng_access_end(sc, srng); +#ifdef notyet + spin_unlock_bh(&srng->lock); +#endif + if (num_buf_freed) + qwz_dp_rxbufs_replenish(sc, mac_id, rx_ring, num_buf_freed, + sc->hw_params.hal_params->rx_buf_rbm); + + ifp->if_ierrors += num_buf_freed; + + return num_buf_freed; +} + +void +qwz_hal_reo_status_queue_stats(struct qwz_softc *sc, uint32_t *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_get_queue_stats_status *desc = + (struct hal_reo_get_queue_stats_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, desc->hdr.info0); +#if 0 + ath12k_dbg(ab, ATH12K_DBG_HAL, "Queue stats status:\n"); + ath12k_dbg(ab, ATH12K_DBG_HAL, "header: cmd_num %d status %d\n", + status->uniform_hdr.cmd_num, + status->uniform_hdr.cmd_status); + ath12k_dbg(ab, ATH12K_DBG_HAL, "ssn %ld cur_idx %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_SSN, + desc->info0), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_CUR_IDX, + desc->info0)); + ath12k_dbg(ab, ATH12K_DBG_HAL, "pn = [%08x, %08x, %08x, %08x]\n", + desc->pn[0], desc->pn[1], desc->pn[2], desc->pn[3]); + ath12k_dbg(ab, ATH12K_DBG_HAL, + "last_rx: enqueue_tstamp %08x dequeue_tstamp %08x\n", + desc->last_rx_enqueue_timestamp, + desc->last_rx_dequeue_timestamp); + ath12k_dbg(ab, ATH12K_DBG_HAL, + "rx_bitmap [%08x %08x %08x %08x %08x %08x %08x %08x]\n", + desc->rx_bitmap[0], desc->rx_bitmap[1], desc->rx_bitmap[2], + desc->rx_bitmap[3], desc->rx_bitmap[4], desc->rx_bitmap[5], + desc->rx_bitmap[6], desc->rx_bitmap[7]); + ath12k_dbg(ab, ATH12K_DBG_HAL, "count: cur_mpdu %ld cur_msdu %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MPDU_COUNT, + desc->info1), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MSDU_COUNT, + desc->info1)); + ath12k_dbg(ab, ATH12K_DBG_HAL, "fwd_timeout %ld fwd_bar %ld dup_count %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_TIMEOUT_COUNT, + desc->info2), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_FDTB_COUNT, + desc->info2), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_DUPLICATE_COUNT, + desc->info2)); + ath12k_dbg(ab, ATH12K_DBG_HAL, "frames_in_order %ld bar_rcvd %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_FIO_COUNT, + desc->info3), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_BAR_RCVD_CNT, + desc->info3)); + ath12k_dbg(ab, ATH12K_DBG_HAL, "num_mpdus %d num_msdus %d total_bytes %d\n", + desc->num_mpdu_frames, desc->num_msdu_frames, + desc->total_bytes); + ath12k_dbg(ab, ATH12K_DBG_HAL, "late_rcvd %ld win_jump_2k %ld hole_cnt %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_LATE_RX_MPDU, + desc->info4), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_WINDOW_JMP2K, + desc->info4), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_HOLE_COUNT, + desc->info4)); + ath12k_dbg(ab, ATH12K_DBG_HAL, "looping count %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO5_LOOPING_CNT, + desc->info5)); +#endif +} + +void +qwz_hal_reo_flush_queue_status(struct qwz_softc *sc, uint32_t *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_flush_queue_status *desc = + (struct hal_reo_flush_queue_status *)tlv->value; + + status->uniform_hdr.cmd_num = FIELD_GET( + HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, desc->hdr.info0); + status->uniform_hdr.cmd_status = FIELD_GET( + HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, desc->hdr.info0); + status->u.flush_queue.err_detected = FIELD_GET( + HAL_REO_FLUSH_QUEUE_INFO0_ERR_DETECTED, desc->info0); +} + +void +qwz_hal_reo_flush_cache_status(struct qwz_softc *sc, uint32_t *reo_desc, + struct hal_reo_status *status) +{ + struct ath12k_hal *hal = &sc->hal; + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_flush_cache_status *desc = + (struct hal_reo_flush_cache_status *)tlv->value; + + status->uniform_hdr.cmd_num = FIELD_GET( + HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, desc->hdr.info0); + status->uniform_hdr.cmd_status = FIELD_GET( + HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, desc->hdr.info0); + + status->u.flush_cache.err_detected = FIELD_GET( + HAL_REO_FLUSH_CACHE_STATUS_INFO0_IS_ERR, desc->info0); + status->u.flush_cache.err_code = FIELD_GET( + HAL_REO_FLUSH_CACHE_STATUS_INFO0_BLOCK_ERR_CODE, desc->info0); + if (!status->u.flush_cache.err_code) + hal->avail_blk_resource |= BIT(hal->current_blk_index); + + status->u.flush_cache.cache_controller_flush_status_hit = FIELD_GET( + HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_STATUS_HIT, desc->info0); + + status->u.flush_cache.cache_controller_flush_status_desc_type = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_DESC_TYPE, + desc->info0); + status->u.flush_cache.cache_controller_flush_status_client_id = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_CLIENT_ID, + desc->info0); + status->u.flush_cache.cache_controller_flush_status_err = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_ERR, + desc->info0); + status->u.flush_cache.cache_controller_flush_status_cnt = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_COUNT, + desc->info0); +} + +void +qwz_hal_reo_unblk_cache_status(struct qwz_softc *sc, uint32_t *reo_desc, + struct hal_reo_status *status) +{ + struct ath12k_hal *hal = &sc->hal; + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_unblock_cache_status *desc = + (struct hal_reo_unblock_cache_status *)tlv->value; + + status->uniform_hdr.cmd_num = FIELD_GET( + HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, desc->hdr.info0); + status->uniform_hdr.cmd_status = FIELD_GET( + HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, desc->hdr.info0); + + status->u.unblock_cache.err_detected = FIELD_GET( + HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_IS_ERR, desc->info0); + status->u.unblock_cache.unblock_type = FIELD_GET( + HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_TYPE, desc->info0); + + if (!status->u.unblock_cache.err_detected && + status->u.unblock_cache.unblock_type == + HAL_REO_STATUS_UNBLOCK_BLOCKING_RESOURCE) + hal->avail_blk_resource &= ~BIT(hal->current_blk_index); +} + +void +qwz_hal_reo_flush_timeout_list_status(struct qwz_softc *ab, uint32_t *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_flush_timeout_list_status *desc = + (struct hal_reo_flush_timeout_list_status *)tlv->value; + + status->uniform_hdr.cmd_num = FIELD_GET( + HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, desc->hdr.info0); + status->uniform_hdr.cmd_status = FIELD_GET( + HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, desc->hdr.info0); + + status->u.timeout_list.err_detected = FIELD_GET( + HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_IS_ERR, desc->info0); + status->u.timeout_list.list_empty = FIELD_GET( + HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_LIST_EMPTY, desc->info0); + + status->u.timeout_list.release_desc_cnt = FIELD_GET( + HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_REL_DESC_COUNT, desc->info1); + status->u.timeout_list.fwd_buf_cnt = FIELD_GET( + HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_FWD_BUF_COUNT, desc->info1); +} + +void +qwz_hal_reo_desc_thresh_reached_status(struct qwz_softc *sc, uint32_t *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_desc_thresh_reached_status *desc = + (struct hal_reo_desc_thresh_reached_status *)tlv->value; + + status->uniform_hdr.cmd_num = FIELD_GET( + HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, desc->hdr.info0); + status->uniform_hdr.cmd_status = FIELD_GET( + HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, desc->hdr.info0); + + status->u.desc_thresh_reached.threshold_idx = FIELD_GET( + HAL_REO_DESC_THRESH_STATUS_INFO0_THRESH_INDEX, desc->info0); + + status->u.desc_thresh_reached.link_desc_counter0 = FIELD_GET( + HAL_REO_DESC_THRESH_STATUS_INFO1_LINK_DESC_COUNTER0, desc->info1); + + status->u.desc_thresh_reached.link_desc_counter1 = FIELD_GET( + HAL_REO_DESC_THRESH_STATUS_INFO2_LINK_DESC_COUNTER1, desc->info2); + + status->u.desc_thresh_reached.link_desc_counter2 = FIELD_GET( + HAL_REO_DESC_THRESH_STATUS_INFO3_LINK_DESC_COUNTER2, desc->info3); + + status->u.desc_thresh_reached.link_desc_counter_sum = FIELD_GET( + HAL_REO_DESC_THRESH_STATUS_INFO4_LINK_DESC_COUNTER_SUM, + desc->info4); +} + +void +qwz_hal_reo_update_rx_reo_queue_status(struct qwz_softc *ab, uint32_t *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_status_hdr *desc = + (struct hal_reo_status_hdr *)tlv->value; + + status->uniform_hdr.cmd_num = FIELD_GET( + HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, desc->info0); + status->uniform_hdr.cmd_status = FIELD_GET( + HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, desc->info0); +} + +int +qwz_dp_process_reo_status(struct qwz_softc *sc) +{ + struct qwz_dp *dp = &sc->dp; + struct hal_srng *srng; + struct dp_reo_cmd *cmd, *tmp; + int found = 0, ret = 0; + uint32_t *reo_desc; + uint16_t tag; + struct hal_reo_status reo_status; + + srng = &sc->hal.srng_list[dp->reo_status_ring.ring_id]; + memset(&reo_status, 0, sizeof(reo_status)); +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + + while ((reo_desc = qwz_hal_srng_dst_get_next_entry(sc, srng))) { + ret = 1; + + tag = FIELD_GET(HAL_SRNG_TLV_HDR_TAG, *reo_desc); + switch (tag) { + case HAL_REO_GET_QUEUE_STATS_STATUS: + qwz_hal_reo_status_queue_stats(sc, reo_desc, + &reo_status); + break; + case HAL_REO_FLUSH_QUEUE_STATUS: + qwz_hal_reo_flush_queue_status(sc, reo_desc, + &reo_status); + break; + case HAL_REO_FLUSH_CACHE_STATUS: + qwz_hal_reo_flush_cache_status(sc, reo_desc, + &reo_status); + break; + case HAL_REO_UNBLOCK_CACHE_STATUS: + qwz_hal_reo_unblk_cache_status(sc, reo_desc, + &reo_status); + break; + case HAL_REO_FLUSH_TIMEOUT_LIST_STATUS: + qwz_hal_reo_flush_timeout_list_status(sc, reo_desc, + &reo_status); + break; + case HAL_REO_DESCRIPTOR_THRESHOLD_REACHED_STATUS: + qwz_hal_reo_desc_thresh_reached_status(sc, reo_desc, + &reo_status); + break; + case HAL_REO_UPDATE_RX_REO_QUEUE_STATUS: + qwz_hal_reo_update_rx_reo_queue_status(sc, reo_desc, + &reo_status); + break; + default: + printf("%s: Unknown reo status type %d\n", + sc->sc_dev.dv_xname, tag); + continue; + } +#ifdef notyet + spin_lock_bh(&dp->reo_cmd_lock); +#endif + TAILQ_FOREACH_SAFE(cmd, &dp->reo_cmd_list, entry, tmp) { + if (reo_status.uniform_hdr.cmd_num == cmd->cmd_num) { + found = 1; + TAILQ_REMOVE(&dp->reo_cmd_list, cmd, entry); + break; + } + } +#ifdef notyet + spin_unlock_bh(&dp->reo_cmd_lock); +#endif + if (found) { + cmd->handler(dp, (void *)&cmd->data, + reo_status.uniform_hdr.cmd_status); + free(cmd, M_DEVBUF, sizeof(*cmd)); + } + found = 0; + } + + qwz_hal_srng_access_end(sc, srng); +#ifdef notyet + spin_unlock_bh(&srng->lock); +#endif + return ret; +} + +int +qwz_dp_service_srng(struct qwz_softc *sc, int grp_id) +{ + struct qwz_pdev_dp *dp = &sc->pdev_dp; + int i, j, ret = 0; + + for (i = 0; i < sc->hw_params.max_tx_ring; i++) { + const struct ath12k_hw_tcl2wbm_rbm_map *map; + + map = &sc->hw_params.hal_params->tcl2wbm_rbm_map[i]; + if ((sc->hw_params.ring_mask->tx[grp_id]) & + (1 << (map->wbm_ring_num)) && + qwz_dp_tx_completion_handler(sc, i)) + ret = 1; + } + + if (sc->hw_params.ring_mask->rx_err[grp_id] && + qwz_dp_process_rx_err(sc)) + ret = 1; + + if (sc->hw_params.ring_mask->rx_wbm_rel[grp_id] && + qwz_dp_rx_process_wbm_err(sc)) + ret = 1; + + if (sc->hw_params.ring_mask->rx[grp_id]) { + i = fls(sc->hw_params.ring_mask->rx[grp_id]) - 1; + if (qwz_dp_process_rx(sc, i)) + ret = 1; + } + + for (i = 0; i < sc->num_radios; i++) { + for (j = 0; j < sc->hw_params.num_rxmda_per_pdev; j++) { + int id = i * sc->hw_params.num_rxmda_per_pdev + j; + + if ((sc->hw_params.ring_mask->rx_mon_status[grp_id] & + (1 << id)) == 0) + continue; + + if (qwz_dp_rx_process_mon_rings(sc, id)) + ret = 1; + } + } + + if (sc->hw_params.ring_mask->reo_status[grp_id] && + qwz_dp_process_reo_status(sc)) + ret = 1; + + for (i = 0; i < sc->num_radios; i++) { + for (j = 0; j < sc->hw_params.num_rxmda_per_pdev; j++) { + int id = i * sc->hw_params.num_rxmda_per_pdev + j; + + if (sc->hw_params.ring_mask->rxdma2host[grp_id] & + (1 << (id))) { + if (qwz_dp_process_rxdma_err(sc, id)) + ret = 1; + } + + if (sc->hw_params.ring_mask->host2rxdma[grp_id] & + (1 << id)) { + qwz_dp_rxbufs_replenish(sc, id, + &dp->rx_refill_buf_ring, 0, + sc->hw_params.hal_params->rx_buf_rbm); + } + } + } + + return ret; +} + +int +qwz_wmi_wait_for_service_ready(struct qwz_softc *sc) +{ + int ret; + + while (!sc->wmi.service_ready) { + ret = tsleep_nsec(&sc->wmi.service_ready, 0, "qwzwmirdy", + SEC_TO_NSEC(5)); + if (ret) + return -1; + } + + return 0; +} + +void +qwz_fill_band_to_mac_param(struct qwz_softc *sc, + struct wmi_host_pdev_band_to_mac *band_to_mac) +{ + uint8_t i; + struct ath12k_hal_reg_capabilities_ext *hal_reg_cap; + struct qwz_pdev *pdev; + + for (i = 0; i < sc->num_radios; i++) { + pdev = &sc->pdevs[i]; + hal_reg_cap = &sc->hal_reg_cap[i]; + band_to_mac[i].pdev_id = pdev->pdev_id; + + switch (pdev->cap.supported_bands) { + case WMI_HOST_WLAN_2G_5G_CAP: + band_to_mac[i].start_freq = hal_reg_cap->low_2ghz_chan; + band_to_mac[i].end_freq = hal_reg_cap->high_5ghz_chan; + break; + case WMI_HOST_WLAN_2G_CAP: + band_to_mac[i].start_freq = hal_reg_cap->low_2ghz_chan; + band_to_mac[i].end_freq = hal_reg_cap->high_2ghz_chan; + break; + case WMI_HOST_WLAN_5G_CAP: + band_to_mac[i].start_freq = hal_reg_cap->low_5ghz_chan; + band_to_mac[i].end_freq = hal_reg_cap->high_5ghz_chan; + break; + default: + break; + } + } +} + +struct mbuf * +qwz_wmi_alloc_mbuf(size_t len) +{ + struct mbuf *m; + uint32_t round_len = roundup(len, 4); + + m = qwz_htc_alloc_mbuf(sizeof(struct wmi_cmd_hdr) + round_len); + if (!m) + return NULL; + + return m; +} + +int +qwz_wmi_cmd_send_nowait(struct qwz_pdev_wmi *wmi, struct mbuf *m, + uint32_t cmd_id) +{ + struct qwz_softc *sc = wmi->wmi->sc; + struct wmi_cmd_hdr *cmd_hdr; + uint32_t cmd = 0; + + cmd |= FIELD_PREP(WMI_CMD_HDR_CMD_ID, cmd_id); + + cmd_hdr = (struct wmi_cmd_hdr *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr)); + cmd_hdr->cmd_id = htole32(cmd); + + DNPRINTF(QWZ_D_WMI, "%s: sending WMI command 0x%u\n", __func__, cmd); + return qwz_htc_send(&sc->htc, wmi->eid, m); +} + +int +qwz_wmi_cmd_send(struct qwz_pdev_wmi *wmi, struct mbuf *m, uint32_t cmd_id) +{ + struct qwz_wmi_base *wmi_sc = wmi->wmi; + int ret = EOPNOTSUPP; + struct qwz_softc *sc = wmi_sc->sc; +#ifdef notyet + might_sleep(); +#endif + if (sc->hw_params.credit_flow) { + struct qwz_htc *htc = &sc->htc; + struct qwz_htc_ep *ep = &htc->endpoint[wmi->eid]; + + while (!ep->tx_credits) { + ret = tsleep_nsec(&ep->tx_credits, 0, "qwztxcrd", + SEC_TO_NSEC(3)); + if (ret) { + printf("%s: tx credits timeout\n", + sc->sc_dev.dv_xname); + if (test_bit(ATH12K_FLAG_CRASH_FLUSH, + sc->sc_flags)) + return ESHUTDOWN; + else + return EAGAIN; + } + } + } else { + while (!wmi->tx_ce_desc) { + ret = tsleep_nsec(&wmi->tx_ce_desc, 0, "qwztxce", + SEC_TO_NSEC(3)); + if (ret) { + printf("%s: tx ce desc timeout\n", + sc->sc_dev.dv_xname); + if (test_bit(ATH12K_FLAG_CRASH_FLUSH, + sc->sc_flags)) + return ESHUTDOWN; + else + return EAGAIN; + } + } + } + + ret = qwz_wmi_cmd_send_nowait(wmi, m, cmd_id); + + if (ret == EAGAIN) + printf("%s: wmi command %d timeout\n", + sc->sc_dev.dv_xname, cmd_id); + + if (ret == ENOBUFS) + printf("%s: ce desc not available for wmi command %d\n", + sc->sc_dev.dv_xname, cmd_id); + + return ret; +} + +int +qwz_wmi_pdev_set_param(struct qwz_softc *sc, uint32_t param_id, + uint32_t param_value, uint8_t pdev_id) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_pdev_set_param_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_pdev_set_param_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SET_PARAM_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->pdev_id = pdev_id; + cmd->param_id = param_id; + cmd->param_value = param_value; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_PDEV_SET_PARAM_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send WMI_PDEV_SET_PARAM cmd\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd pdev set param %d pdev id %d value %d\n", + __func__, param_id, pdev_id, param_value); + + return 0; +} + +int +qwz_wmi_pdev_lro_cfg(struct qwz_softc *sc, uint8_t pdev_id) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct ath12k_wmi_pdev_lro_config_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct ath12k_wmi_pdev_lro_config_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_LRO_INFO_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + arc4random_buf(cmd->th_4, sizeof(uint32_t) * ATH12K_IPV4_TH_SEED_SIZE); + arc4random_buf(cmd->th_6, sizeof(uint32_t) * ATH12K_IPV6_TH_SEED_SIZE); + + cmd->pdev_id = pdev_id; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_LRO_CONFIG_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send lro cfg req wmi cmd\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd lro config pdev_id 0x%x\n", + __func__, pdev_id); + + return 0; +} + +int +qwz_wmi_pdev_set_ps_mode(struct qwz_softc *sc, int vdev_id, uint8_t pdev_id, + enum wmi_sta_ps_mode psmode) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_pdev_set_ps_mode_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_pdev_set_ps_mode_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_STA_POWERSAVE_MODE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->sta_ps_mode = psmode; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_STA_POWERSAVE_MODE_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send WMI_PDEV_SET_PARAM cmd\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd sta powersave mode psmode %d vdev id %d\n", + __func__, psmode, vdev_id); + + return 0; +} + +int +qwz_wmi_scan_prob_req_oui(struct qwz_softc *sc, const uint8_t *mac_addr, + uint8_t pdev_id) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct mbuf *m; + struct wmi_scan_prob_req_oui_cmd *cmd; + uint32_t prob_req_oui; + int len, ret; + + prob_req_oui = (((uint32_t)mac_addr[0]) << 16) | + (((uint32_t)mac_addr[1]) << 8) | mac_addr[2]; + + len = sizeof(*cmd); + m = qwz_wmi_alloc_mbuf(len); + if (!m) + return ENOMEM; + + cmd = (struct wmi_scan_prob_req_oui_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_SCAN_PROB_REQ_OUI_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->prob_req_oui = prob_req_oui; + + DNPRINTF(QWZ_D_WMI, "%s: scan prob req oui %d\n", __func__, + prob_req_oui); + + ret = qwz_wmi_cmd_send(wmi, m, WMI_SCAN_PROB_REQ_OUI_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send WMI_SCAN_PROB_REQ_OUI cmd\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + return 0; +} + +int +qwz_wmi_send_dfs_phyerr_offload_enable_cmd(struct qwz_softc *sc, uint32_t pdev_id) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_dfs_phyerr_offload_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_dfs_phyerr_offload_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = pdev_id; + + ret = qwz_wmi_cmd_send(wmi, m, + WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send " + "WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE cmd\n", + sc->sc_dev.dv_xname); + } + m_free(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd pdev dfs phyerr offload enable " + "pdev id %d\n", __func__, pdev_id); + + return 0; +} + +int +qwz_wmi_send_scan_chan_list_cmd(struct qwz_softc *sc, uint8_t pdev_id, + struct scan_chan_list_params *chan_list) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_scan_chan_list_cmd *cmd; + struct mbuf *m; + struct wmi_channel *chan_info; + struct channel_param *tchan_info; + struct wmi_tlv *tlv; + void *ptr; + int i, ret, len; + uint16_t num_send_chans, num_sends = 0, max_chan_limit = 0; + uint32_t *reg1, *reg2; + + tchan_info = chan_list->ch_param; + while (chan_list->nallchans) { + len = sizeof(*cmd) + TLV_HDR_SIZE; + max_chan_limit = (wmi->wmi->max_msg_len[pdev_id] - len) / + sizeof(*chan_info); + + if (chan_list->nallchans > max_chan_limit) + num_send_chans = max_chan_limit; + else + num_send_chans = chan_list->nallchans; + + chan_list->nallchans -= num_send_chans; + len += sizeof(*chan_info) * num_send_chans; + + m = qwz_wmi_alloc_mbuf(len); + if (!m) + return ENOMEM; + + cmd = (struct wmi_scan_chan_list_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_SCAN_CHAN_LIST_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->pdev_id = chan_list->pdev_id; + cmd->num_scan_chans = num_send_chans; + if (num_sends) + cmd->flags |= WMI_APPEND_TO_EXISTING_CHAN_LIST_FLAG; + + DNPRINTF(QWZ_D_WMI, "%s: no.of chan = %d len = %d " + "pdev_id = %d num_sends = %d\n", __func__, num_send_chans, + len, cmd->pdev_id, num_sends); + + ptr = (void *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr) + + sizeof(*cmd)); + + len = sizeof(*chan_info) * num_send_chans; + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + ptr += TLV_HDR_SIZE; + + for (i = 0; i < num_send_chans; ++i) { + chan_info = ptr; + memset(chan_info, 0, sizeof(*chan_info)); + len = sizeof(*chan_info); + chan_info->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_CHANNEL) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + + reg1 = &chan_info->reg_info_1; + reg2 = &chan_info->reg_info_2; + chan_info->mhz = tchan_info->mhz; + chan_info->band_center_freq1 = tchan_info->cfreq1; + chan_info->band_center_freq2 = tchan_info->cfreq2; + + if (tchan_info->is_chan_passive) + chan_info->info |= WMI_CHAN_INFO_PASSIVE; + if (tchan_info->allow_he) + chan_info->info |= WMI_CHAN_INFO_ALLOW_HE; + else if (tchan_info->allow_vht) + chan_info->info |= WMI_CHAN_INFO_ALLOW_VHT; + else if (tchan_info->allow_ht) + chan_info->info |= WMI_CHAN_INFO_ALLOW_HT; + if (tchan_info->half_rate) + chan_info->info |= WMI_CHAN_INFO_HALF_RATE; + if (tchan_info->quarter_rate) + chan_info->info |= WMI_CHAN_INFO_QUARTER_RATE; + if (tchan_info->psc_channel) + chan_info->info |= WMI_CHAN_INFO_PSC; + if (tchan_info->dfs_set) + chan_info->info |= WMI_CHAN_INFO_DFS; + + chan_info->info |= FIELD_PREP(WMI_CHAN_INFO_MODE, + tchan_info->phy_mode); + *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MIN_PWR, + tchan_info->minpower); + *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_PWR, + tchan_info->maxpower); + *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_REG_PWR, + tchan_info->maxregpower); + *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_REG_CLS, + tchan_info->reg_class_id); + *reg2 |= FIELD_PREP(WMI_CHAN_REG_INFO2_ANT_MAX, + tchan_info->antennamax); + *reg2 |= FIELD_PREP(WMI_CHAN_REG_INFO2_MAX_TX_PWR, + tchan_info->maxregpower); + + DNPRINTF(QWZ_D_WMI, "%s: chan scan list " + "chan[%d] = %u, chan_info->info %8x\n", + __func__, i, chan_info->mhz, chan_info->info); + + ptr += sizeof(*chan_info); + + tchan_info++; + } + + ret = qwz_wmi_cmd_send(wmi, m, WMI_SCAN_CHAN_LIST_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send WMI_SCAN_CHAN_LIST " + "cmd\n", sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd scan chan list channels %d\n", + __func__, num_send_chans); + + num_sends++; + } + + return 0; +} + +int +qwz_wmi_send_11d_scan_start_cmd(struct qwz_softc *sc, + struct wmi_11d_scan_start_params *param, uint8_t pdev_id) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_11d_scan_start_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_11d_scan_start_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_11D_SCAN_START_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = param->vdev_id; + cmd->scan_period_msec = param->scan_period_msec; + cmd->start_interval_msec = param->start_interval_msec; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_11D_SCAN_START_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send WMI_11D_SCAN_START_CMDID: " + "%d\n", sc->sc_dev.dv_xname, ret); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd 11d scan start vdev id %d period %d " + "ms internal %d ms\n", __func__, cmd->vdev_id, + cmd->scan_period_msec, cmd->start_interval_msec); + + return 0; +} + +static inline void +qwz_wmi_copy_scan_event_cntrl_flags(struct wmi_start_scan_cmd *cmd, + struct scan_req_params *param) +{ + /* Scan events subscription */ + if (param->scan_ev_started) + cmd->notify_scan_events |= WMI_SCAN_EVENT_STARTED; + if (param->scan_ev_completed) + cmd->notify_scan_events |= WMI_SCAN_EVENT_COMPLETED; + if (param->scan_ev_bss_chan) + cmd->notify_scan_events |= WMI_SCAN_EVENT_BSS_CHANNEL; + if (param->scan_ev_foreign_chan) + cmd->notify_scan_events |= WMI_SCAN_EVENT_FOREIGN_CHAN; + if (param->scan_ev_dequeued) + cmd->notify_scan_events |= WMI_SCAN_EVENT_DEQUEUED; + if (param->scan_ev_preempted) + cmd->notify_scan_events |= WMI_SCAN_EVENT_PREEMPTED; + if (param->scan_ev_start_failed) + cmd->notify_scan_events |= WMI_SCAN_EVENT_START_FAILED; + if (param->scan_ev_restarted) + cmd->notify_scan_events |= WMI_SCAN_EVENT_RESTARTED; + if (param->scan_ev_foreign_chn_exit) + cmd->notify_scan_events |= WMI_SCAN_EVENT_FOREIGN_CHAN_EXIT; + if (param->scan_ev_suspended) + cmd->notify_scan_events |= WMI_SCAN_EVENT_SUSPENDED; + if (param->scan_ev_resumed) + cmd->notify_scan_events |= WMI_SCAN_EVENT_RESUMED; + + /** Set scan control flags */ + cmd->scan_ctrl_flags = 0; + if (param->scan_f_passive) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE; + if (param->scan_f_strict_passive_pch) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_STRICT_PASSIVE_ON_PCHN; + if (param->scan_f_promisc_mode) + cmd->scan_ctrl_flags |= WMI_SCAN_FILTER_PROMISCUOS; + if (param->scan_f_capture_phy_err) + cmd->scan_ctrl_flags |= WMI_SCAN_CAPTURE_PHY_ERROR; + if (param->scan_f_half_rate) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_HALF_RATE_SUPPORT; + if (param->scan_f_quarter_rate) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_QUARTER_RATE_SUPPORT; + if (param->scan_f_cck_rates) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_CCK_RATES; + if (param->scan_f_ofdm_rates) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_OFDM_RATES; + if (param->scan_f_chan_stat_evnt) + cmd->scan_ctrl_flags |= WMI_SCAN_CHAN_STAT_EVENT; + if (param->scan_f_filter_prb_req) + cmd->scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ; + if (param->scan_f_bcast_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_BCAST_PROBE_REQ; + if (param->scan_f_offchan_mgmt_tx) + cmd->scan_ctrl_flags |= WMI_SCAN_OFFCHAN_MGMT_TX; + if (param->scan_f_offchan_data_tx) + cmd->scan_ctrl_flags |= WMI_SCAN_OFFCHAN_DATA_TX; + if (param->scan_f_force_active_dfs_chn) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_FORCE_ACTIVE_ON_DFS; + if (param->scan_f_add_tpc_ie_in_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_TPC_IE_IN_PROBE_REQ; + if (param->scan_f_add_ds_ie_in_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_DS_IE_IN_PROBE_REQ; + if (param->scan_f_add_spoofed_mac_in_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_SPOOF_MAC_IN_PROBE_REQ; + if (param->scan_f_add_rand_seq_in_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_RANDOM_SEQ_NO_IN_PROBE_REQ; + if (param->scan_f_en_ie_whitelist_in_probe) + cmd->scan_ctrl_flags |= + WMI_SCAN_ENABLE_IE_WHTELIST_IN_PROBE_REQ; + + /* for adaptive scan mode using 3 bits (21 - 23 bits) */ + WMI_SCAN_SET_DWELL_MODE(cmd->scan_ctrl_flags, + param->adaptive_dwell_time_mode); + + cmd->scan_ctrl_flags_ext = param->scan_ctrl_flags_ext; +} + +int +qwz_wmi_send_scan_start_cmd(struct qwz_softc *sc, + struct scan_req_params *params) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[params->pdev_id]; + struct wmi_start_scan_cmd *cmd; + struct wmi_ssid *ssid = NULL; + struct wmi_mac_addr *bssid; + struct mbuf *m; + struct wmi_tlv *tlv; + void *ptr; + int i, ret, len; + uint32_t *tmp_ptr; + uint16_t extraie_len_with_pad = 0; + struct hint_short_ssid *s_ssid = NULL; + struct hint_bssid *hint_bssid = NULL; + + len = sizeof(*cmd); + + len += TLV_HDR_SIZE; + if (params->num_chan) + len += params->num_chan * sizeof(uint32_t); + + len += TLV_HDR_SIZE; + if (params->num_ssids) + len += params->num_ssids * sizeof(*ssid); + + len += TLV_HDR_SIZE; + if (params->num_bssid) + len += sizeof(*bssid) * params->num_bssid; + + len += TLV_HDR_SIZE; + if (params->extraie.len && params->extraie.len <= 0xFFFF) { + extraie_len_with_pad = roundup(params->extraie.len, + sizeof(uint32_t)); + } + len += extraie_len_with_pad; + + if (params->num_hint_bssid) { + len += TLV_HDR_SIZE + + params->num_hint_bssid * sizeof(struct hint_bssid); + } + + if (params->num_hint_s_ssid) { + len += TLV_HDR_SIZE + + params->num_hint_s_ssid * sizeof(struct hint_short_ssid); + } + + m = qwz_wmi_alloc_mbuf(len); + if (!m) + return ENOMEM; + + ptr = (void *)(mtod(m, uint8_t *) + sizeof(struct ath12k_htc_hdr) + + sizeof(struct wmi_cmd_hdr)); + + cmd = ptr; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_START_SCAN_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->scan_id = params->scan_id; + cmd->scan_req_id = params->scan_req_id; + cmd->vdev_id = params->vdev_id; + cmd->scan_priority = params->scan_priority; + cmd->notify_scan_events = params->notify_scan_events; + + qwz_wmi_copy_scan_event_cntrl_flags(cmd, params); + + cmd->dwell_time_active = params->dwell_time_active; + cmd->dwell_time_active_2g = params->dwell_time_active_2g; + cmd->dwell_time_passive = params->dwell_time_passive; + cmd->dwell_time_active_6g = params->dwell_time_active_6g; + cmd->dwell_time_passive_6g = params->dwell_time_passive_6g; + cmd->min_rest_time = params->min_rest_time; + cmd->max_rest_time = params->max_rest_time; + cmd->repeat_probe_time = params->repeat_probe_time; + cmd->probe_spacing_time = params->probe_spacing_time; + cmd->idle_time = params->idle_time; + cmd->max_scan_time = params->max_scan_time; + cmd->probe_delay = params->probe_delay; + cmd->burst_duration = params->burst_duration; + cmd->num_chan = params->num_chan; + cmd->num_bssid = params->num_bssid; + cmd->num_ssids = params->num_ssids; + cmd->ie_len = params->extraie.len; + cmd->n_probes = params->n_probes; + IEEE80211_ADDR_COPY(cmd->mac_addr.addr, params->mac_addr.addr); + IEEE80211_ADDR_COPY(cmd->mac_mask.addr, params->mac_mask.addr); + + ptr += sizeof(*cmd); + + len = params->num_chan * sizeof(uint32_t); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_UINT32) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE; + tmp_ptr = (uint32_t *)ptr; + + for (i = 0; i < params->num_chan; ++i) + tmp_ptr[i] = params->chan_list[i]; + + ptr += len; + + len = params->num_ssids * sizeof(*ssid); + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + + ptr += TLV_HDR_SIZE; + + if (params->num_ssids) { + ssid = ptr; + for (i = 0; i < params->num_ssids; ++i) { + ssid->ssid_len = params->ssid[i].length; + memcpy(ssid->ssid, params->ssid[i].ssid, + params->ssid[i].length); + ssid++; + } + } + + ptr += (params->num_ssids * sizeof(*ssid)); + len = params->num_bssid * sizeof(*bssid); + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + + ptr += TLV_HDR_SIZE; + bssid = ptr; + + if (params->num_bssid) { + for (i = 0; i < params->num_bssid; ++i) { + IEEE80211_ADDR_COPY(bssid->addr, + params->bssid_list[i].addr); + bssid++; + } + } + + ptr += params->num_bssid * sizeof(*bssid); + + len = extraie_len_with_pad; + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE; + + if (extraie_len_with_pad) + memcpy(ptr, params->extraie.ptr, params->extraie.len); + + ptr += extraie_len_with_pad; + + if (params->num_hint_s_ssid) { + len = params->num_hint_s_ssid * sizeof(struct hint_short_ssid); + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_ARRAY_FIXED_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE; + s_ssid = ptr; + for (i = 0; i < params->num_hint_s_ssid; ++i) { + s_ssid->freq_flags = params->hint_s_ssid[i].freq_flags; + s_ssid->short_ssid = params->hint_s_ssid[i].short_ssid; + s_ssid++; + } + ptr += len; + } + + if (params->num_hint_bssid) { + len = params->num_hint_bssid * sizeof(struct hint_bssid); + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_ARRAY_FIXED_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE; + hint_bssid = ptr; + for (i = 0; i < params->num_hint_bssid; ++i) { + hint_bssid->freq_flags = + params->hint_bssid[i].freq_flags; + IEEE80211_ADDR_COPY( + ¶ms->hint_bssid[i].bssid.addr[0], + &hint_bssid->bssid.addr[0]); + hint_bssid++; + } + } + + ret = qwz_wmi_cmd_send(wmi, m, WMI_START_SCAN_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send WMI_START_SCAN_CMDID\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd start scan", __func__); + + return 0; +} + +int +qwz_wmi_send_scan_stop_cmd(struct qwz_softc *sc, + struct scan_cancel_param *param) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[param->pdev_id]; + struct wmi_stop_scan_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_stop_scan_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_STOP_SCAN_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = param->vdev_id; + cmd->requestor = param->requester; + cmd->scan_id = param->scan_id; + cmd->pdev_id = param->pdev_id; + /* stop the scan with the corresponding scan_id */ + if (param->req_type == WLAN_SCAN_CANCEL_PDEV_ALL) { + /* Cancelling all scans */ + cmd->req_type = WMI_SCAN_STOP_ALL; + } else if (param->req_type == WLAN_SCAN_CANCEL_VDEV_ALL) { + /* Cancelling VAP scans */ + cmd->req_type = WMI_SCN_STOP_VAP_ALL; + } else if (param->req_type == WLAN_SCAN_CANCEL_SINGLE) { + /* Cancelling specific scan */ + cmd->req_type = WMI_SCAN_STOP_ONE; + } else { + printf("%s: invalid scan cancel param %d\n", + sc->sc_dev.dv_xname, param->req_type); + m_freem(m); + return EINVAL; + } + + ret = qwz_wmi_cmd_send(wmi, m, WMI_STOP_SCAN_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send WMI_STOP_SCAN_CMDID\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd stop scan\n", __func__); + return ret; +} + +int +qwz_wmi_send_peer_create_cmd(struct qwz_softc *sc, uint8_t pdev_id, + struct peer_create_params *param) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_peer_create_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_peer_create_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_CREATE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + IEEE80211_ADDR_COPY(cmd->peer_macaddr.addr, param->peer_addr); + cmd->peer_type = param->peer_type; + cmd->vdev_id = param->vdev_id; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_PEER_CREATE_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to submit WMI_PEER_CREATE cmd\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd peer create vdev_id %d peer_addr %s\n", + __func__, param->vdev_id, ether_sprintf(param->peer_addr)); + + return ret; +} + +int +qwz_wmi_send_peer_delete_cmd(struct qwz_softc *sc, const uint8_t *peer_addr, + uint8_t vdev_id, uint8_t pdev_id) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_peer_delete_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_peer_delete_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_DELETE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + IEEE80211_ADDR_COPY(cmd->peer_macaddr.addr, peer_addr); + cmd->vdev_id = vdev_id; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_PEER_DELETE_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send WMI_PEER_DELETE cmd\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd peer delete vdev_id %d peer_addr %pM\n", + __func__, vdev_id, peer_addr); + + return 0; +} + +int +qwz_wmi_vdev_install_key(struct qwz_softc *sc, + struct wmi_vdev_install_key_arg *arg, uint8_t pdev_id) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_vdev_install_key_cmd *cmd; + struct wmi_tlv *tlv; + struct mbuf *m; + int ret, len; + int key_len_aligned = roundup(arg->key_len, sizeof(uint32_t)); + + len = sizeof(*cmd) + TLV_HDR_SIZE + key_len_aligned; + + m = qwz_wmi_alloc_mbuf(len); + if (m == NULL) + return -ENOMEM; + + cmd = (struct wmi_vdev_install_key_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_VDEV_INSTALL_KEY_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = arg->vdev_id; + IEEE80211_ADDR_COPY(cmd->peer_macaddr.addr, arg->macaddr); + cmd->key_idx = arg->key_idx; + cmd->key_flags = arg->key_flags; + cmd->key_cipher = arg->key_cipher; + cmd->key_len = arg->key_len; + cmd->key_txmic_len = arg->key_txmic_len; + cmd->key_rxmic_len = arg->key_rxmic_len; + + if (arg->key_rsc_counter) + memcpy(&cmd->key_rsc_counter, &arg->key_rsc_counter, + sizeof(struct wmi_key_seq_counter)); + + tlv = (struct wmi_tlv *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr) + + sizeof(*cmd)); + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, key_len_aligned); + if (arg->key_data) + memcpy(tlv->value, (uint8_t *)arg->key_data, + key_len_aligned); + + ret = qwz_wmi_cmd_send(wmi, m, WMI_VDEV_INSTALL_KEY_CMDID); + if (ret) { + printf("%s: failed to send WMI_VDEV_INSTALL_KEY cmd\n", + sc->sc_dev.dv_xname); + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, + "%s: cmd vdev install key idx %d cipher %d len %d\n", + __func__, arg->key_idx, arg->key_cipher, arg->key_len); + + return ret; +} + +void +qwz_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd, + struct peer_assoc_params *param, int hw_crypto_disabled) +{ + cmd->peer_flags = 0; + + if (param->is_wme_set) { + if (param->qos_flag) + cmd->peer_flags |= WMI_PEER_QOS; + if (param->apsd_flag) + cmd->peer_flags |= WMI_PEER_APSD; + if (param->ht_flag) + cmd->peer_flags |= WMI_PEER_HT; + if (param->bw_40) + cmd->peer_flags |= WMI_PEER_40MHZ; + if (param->bw_80) + cmd->peer_flags |= WMI_PEER_80MHZ; + if (param->bw_160) + cmd->peer_flags |= WMI_PEER_160MHZ; + + /* Typically if STBC is enabled for VHT it should be enabled + * for HT as well + **/ + if (param->stbc_flag) + cmd->peer_flags |= WMI_PEER_STBC; + + /* Typically if LDPC is enabled for VHT it should be enabled + * for HT as well + **/ + if (param->ldpc_flag) + cmd->peer_flags |= WMI_PEER_LDPC; + + if (param->static_mimops_flag) + cmd->peer_flags |= WMI_PEER_STATIC_MIMOPS; + if (param->dynamic_mimops_flag) + cmd->peer_flags |= WMI_PEER_DYN_MIMOPS; + if (param->spatial_mux_flag) + cmd->peer_flags |= WMI_PEER_SPATIAL_MUX; + if (param->vht_flag) + cmd->peer_flags |= WMI_PEER_VHT; + if (param->he_flag) + cmd->peer_flags |= WMI_PEER_HE; + if (param->twt_requester) + cmd->peer_flags |= WMI_PEER_TWT_REQ; + if (param->twt_responder) + cmd->peer_flags |= WMI_PEER_TWT_RESP; + } + + /* Suppress authorization for all AUTH modes that need 4-way handshake + * (during re-association). + * Authorization will be done for these modes on key installation. + */ + if (param->auth_flag) + cmd->peer_flags |= WMI_PEER_AUTH; + if (param->need_ptk_4_way) { + cmd->peer_flags |= WMI_PEER_NEED_PTK_4_WAY; + if (!hw_crypto_disabled && param->is_assoc) + cmd->peer_flags &= ~WMI_PEER_AUTH; + } + if (param->need_gtk_2_way) + cmd->peer_flags |= WMI_PEER_NEED_GTK_2_WAY; + /* safe mode bypass the 4-way handshake */ + if (param->safe_mode_enabled) + cmd->peer_flags &= ~(WMI_PEER_NEED_PTK_4_WAY | + WMI_PEER_NEED_GTK_2_WAY); + + if (param->is_pmf_enabled) + cmd->peer_flags |= WMI_PEER_PMF; + + /* Disable AMSDU for station transmit, if user configures it */ + /* Disable AMSDU for AP transmit to 11n Stations, if user configures + * it + * if (param->amsdu_disable) Add after FW support + **/ + + /* Target asserts if node is marked HT and all MCS is set to 0. + * Mark the node as non-HT if all the mcs rates are disabled through + * iwpriv + **/ + if (param->peer_ht_rates.num_rates == 0) + cmd->peer_flags &= ~WMI_PEER_HT; +} + +int +qwz_wmi_send_peer_assoc_cmd(struct qwz_softc *sc, uint8_t pdev_id, + struct peer_assoc_params *param) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_peer_assoc_complete_cmd *cmd; + struct wmi_vht_rate_set *mcs; + struct wmi_he_rate_set *he_mcs; + struct mbuf *m; + struct wmi_tlv *tlv; + void *ptr; + uint32_t peer_legacy_rates_align; + uint32_t peer_ht_rates_align; + int i, ret, len; + + peer_legacy_rates_align = roundup(param->peer_legacy_rates.num_rates, + sizeof(uint32_t)); + peer_ht_rates_align = roundup(param->peer_ht_rates.num_rates, + sizeof(uint32_t)); + + len = sizeof(*cmd) + + TLV_HDR_SIZE + (peer_legacy_rates_align * sizeof(uint8_t)) + + TLV_HDR_SIZE + (peer_ht_rates_align * sizeof(uint8_t)) + + sizeof(*mcs) + TLV_HDR_SIZE + + (sizeof(*he_mcs) * param->peer_he_mcs_count); + + m = qwz_wmi_alloc_mbuf(len); + if (!m) + return ENOMEM; + + ptr = (void *)(mtod(m, uint8_t *) + sizeof(struct ath12k_htc_hdr) + + sizeof(struct wmi_cmd_hdr)); + + cmd = ptr; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PEER_ASSOC_COMPLETE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = param->vdev_id; + + cmd->peer_new_assoc = param->peer_new_assoc; + cmd->peer_associd = param->peer_associd; + + qwz_wmi_copy_peer_flags(cmd, param, + test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags)); + + IEEE80211_ADDR_COPY(cmd->peer_macaddr.addr, param->peer_mac); + + cmd->peer_rate_caps = param->peer_rate_caps; + cmd->peer_caps = param->peer_caps; + cmd->peer_listen_intval = param->peer_listen_intval; + cmd->peer_ht_caps = param->peer_ht_caps; + cmd->peer_max_mpdu = param->peer_max_mpdu; + cmd->peer_mpdu_density = param->peer_mpdu_density; + cmd->peer_vht_caps = param->peer_vht_caps; + cmd->peer_phymode = param->peer_phymode; + + /* Update 11ax capabilities */ + cmd->peer_he_cap_info = param->peer_he_cap_macinfo[0]; + cmd->peer_he_cap_info_ext = param->peer_he_cap_macinfo[1]; + cmd->peer_he_cap_info_internal = param->peer_he_cap_macinfo_internal; + cmd->peer_he_caps_6ghz = param->peer_he_caps_6ghz; + cmd->peer_he_ops = param->peer_he_ops; + memcpy(&cmd->peer_he_cap_phy, ¶m->peer_he_cap_phyinfo, + sizeof(param->peer_he_cap_phyinfo)); + memcpy(&cmd->peer_ppet, ¶m->peer_ppet, + sizeof(param->peer_ppet)); + + /* Update peer legacy rate information */ + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, peer_legacy_rates_align); + + ptr += TLV_HDR_SIZE; + + cmd->num_peer_legacy_rates = param->peer_legacy_rates.num_rates; + memcpy(ptr, param->peer_legacy_rates.rates, + param->peer_legacy_rates.num_rates); + + /* Update peer HT rate information */ + ptr += peer_legacy_rates_align; + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, peer_ht_rates_align); + ptr += TLV_HDR_SIZE; + cmd->num_peer_ht_rates = param->peer_ht_rates.num_rates; + memcpy(ptr, param->peer_ht_rates.rates, + param->peer_ht_rates.num_rates); + + /* VHT Rates */ + ptr += peer_ht_rates_align; + + mcs = ptr; + + mcs->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VHT_RATE_SET) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*mcs) - TLV_HDR_SIZE); + + cmd->peer_nss = param->peer_nss; + + /* Update bandwidth-NSS mapping */ + cmd->peer_bw_rxnss_override = 0; + cmd->peer_bw_rxnss_override |= param->peer_bw_rxnss_override; + + if (param->vht_capable) { + mcs->rx_max_rate = param->rx_max_rate; + mcs->rx_mcs_set = param->rx_mcs_set; + mcs->tx_max_rate = param->tx_max_rate; + mcs->tx_mcs_set = param->tx_mcs_set; + } + + /* HE Rates */ + cmd->peer_he_mcs = param->peer_he_mcs_count; + cmd->min_data_rate = param->min_data_rate; + + ptr += sizeof(*mcs); + + len = param->peer_he_mcs_count * sizeof(*he_mcs); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE; + + /* Loop through the HE rate set */ + for (i = 0; i < param->peer_he_mcs_count; i++) { + he_mcs = ptr; + he_mcs->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_HE_RATE_SET) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*he_mcs) - TLV_HDR_SIZE); + + he_mcs->rx_mcs_set = param->peer_he_tx_mcs_set[i]; + he_mcs->tx_mcs_set = param->peer_he_rx_mcs_set[i]; + ptr += sizeof(*he_mcs); + } + + ret = qwz_wmi_cmd_send(wmi, m, WMI_PEER_ASSOC_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send WMI_PEER_ASSOC_CMDID\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd peer assoc vdev id %d assoc id %d " + "peer mac %s peer_flags %x rate_caps %x peer_caps %x " + "listen_intval %d ht_caps %x max_mpdu %d nss %d phymode %d " + "peer_mpdu_density %d vht_caps %x he cap_info %x he ops %x " + "he cap_info_ext %x he phy %x %x %x peer_bw_rxnss_override %x\n", + __func__, cmd->vdev_id, cmd->peer_associd, + ether_sprintf(param->peer_mac), + cmd->peer_flags, cmd->peer_rate_caps, cmd->peer_caps, + cmd->peer_listen_intval, cmd->peer_ht_caps, + cmd->peer_max_mpdu, cmd->peer_nss, cmd->peer_phymode, + cmd->peer_mpdu_density, cmd->peer_vht_caps, cmd->peer_he_cap_info, + cmd->peer_he_ops, cmd->peer_he_cap_info_ext, + cmd->peer_he_cap_phy[0], cmd->peer_he_cap_phy[1], + cmd->peer_he_cap_phy[2], cmd->peer_bw_rxnss_override); + + return 0; +} + +void +qwz_wmi_copy_resource_config(struct wmi_resource_config *wmi_cfg, + struct target_resource_config *tg_cfg) +{ + wmi_cfg->num_vdevs = tg_cfg->num_vdevs; + wmi_cfg->num_peers = tg_cfg->num_peers; + wmi_cfg->num_offload_peers = tg_cfg->num_offload_peers; + wmi_cfg->num_offload_reorder_buffs = tg_cfg->num_offload_reorder_buffs; + wmi_cfg->num_peer_keys = tg_cfg->num_peer_keys; + wmi_cfg->num_tids = tg_cfg->num_tids; + wmi_cfg->ast_skid_limit = tg_cfg->ast_skid_limit; + wmi_cfg->tx_chain_mask = tg_cfg->tx_chain_mask; + wmi_cfg->rx_chain_mask = tg_cfg->rx_chain_mask; + wmi_cfg->rx_timeout_pri[0] = tg_cfg->rx_timeout_pri[0]; + wmi_cfg->rx_timeout_pri[1] = tg_cfg->rx_timeout_pri[1]; + wmi_cfg->rx_timeout_pri[2] = tg_cfg->rx_timeout_pri[2]; + wmi_cfg->rx_timeout_pri[3] = tg_cfg->rx_timeout_pri[3]; + wmi_cfg->rx_decap_mode = tg_cfg->rx_decap_mode; + wmi_cfg->scan_max_pending_req = tg_cfg->scan_max_pending_req; + wmi_cfg->bmiss_offload_max_vdev = tg_cfg->bmiss_offload_max_vdev; + wmi_cfg->roam_offload_max_vdev = tg_cfg->roam_offload_max_vdev; + wmi_cfg->roam_offload_max_ap_profiles = + tg_cfg->roam_offload_max_ap_profiles; + wmi_cfg->num_mcast_groups = tg_cfg->num_mcast_groups; + wmi_cfg->num_mcast_table_elems = tg_cfg->num_mcast_table_elems; + wmi_cfg->mcast2ucast_mode = tg_cfg->mcast2ucast_mode; + wmi_cfg->tx_dbg_log_size = tg_cfg->tx_dbg_log_size; + wmi_cfg->num_wds_entries = tg_cfg->num_wds_entries; + wmi_cfg->dma_burst_size = tg_cfg->dma_burst_size; + wmi_cfg->mac_aggr_delim = tg_cfg->mac_aggr_delim; + wmi_cfg->rx_skip_defrag_timeout_dup_detection_check = + tg_cfg->rx_skip_defrag_timeout_dup_detection_check; + wmi_cfg->vow_config = tg_cfg->vow_config; + wmi_cfg->gtk_offload_max_vdev = tg_cfg->gtk_offload_max_vdev; + wmi_cfg->num_msdu_desc = tg_cfg->num_msdu_desc; + wmi_cfg->max_frag_entries = tg_cfg->max_frag_entries; + wmi_cfg->num_tdls_vdevs = tg_cfg->num_tdls_vdevs; + wmi_cfg->num_tdls_conn_table_entries = + tg_cfg->num_tdls_conn_table_entries; + wmi_cfg->beacon_tx_offload_max_vdev = + tg_cfg->beacon_tx_offload_max_vdev; + wmi_cfg->num_multicast_filter_entries = + tg_cfg->num_multicast_filter_entries; + wmi_cfg->num_wow_filters = tg_cfg->num_wow_filters; + wmi_cfg->num_keep_alive_pattern = tg_cfg->num_keep_alive_pattern; + wmi_cfg->keep_alive_pattern_size = tg_cfg->keep_alive_pattern_size; + wmi_cfg->max_tdls_concurrent_sleep_sta = + tg_cfg->max_tdls_concurrent_sleep_sta; + wmi_cfg->max_tdls_concurrent_buffer_sta = + tg_cfg->max_tdls_concurrent_buffer_sta; + wmi_cfg->wmi_send_separate = tg_cfg->wmi_send_separate; + wmi_cfg->num_ocb_vdevs = tg_cfg->num_ocb_vdevs; + wmi_cfg->num_ocb_channels = tg_cfg->num_ocb_channels; + wmi_cfg->num_ocb_schedules = tg_cfg->num_ocb_schedules; + wmi_cfg->bpf_instruction_size = tg_cfg->bpf_instruction_size; + wmi_cfg->max_bssid_rx_filters = tg_cfg->max_bssid_rx_filters; + wmi_cfg->use_pdev_id = tg_cfg->use_pdev_id; + wmi_cfg->flag1 = tg_cfg->flag1; + wmi_cfg->peer_map_unmap_v2_support = tg_cfg->peer_map_unmap_v2_support; + wmi_cfg->sched_params = tg_cfg->sched_params; + wmi_cfg->twt_ap_pdev_count = tg_cfg->twt_ap_pdev_count; + wmi_cfg->twt_ap_sta_count = tg_cfg->twt_ap_sta_count; +#ifdef notyet /* 6 GHz support */ + wmi_cfg->host_service_flags &= + ~(1 << WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT); + wmi_cfg->host_service_flags |= (tg_cfg->is_reg_cc_ext_event_supported << + WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT); + wmi_cfg->flags2 = WMI_RSRC_CFG_FLAG2_CALC_NEXT_DTIM_COUNT_SET; + wmi_cfg->ema_max_vap_cnt = tg_cfg->ema_max_vap_cnt; + wmi_cfg->ema_max_profile_period = tg_cfg->ema_max_profile_period; +#endif +} + +int +qwz_init_cmd_send(struct qwz_pdev_wmi *wmi, struct wmi_init_cmd_param *param) +{ + struct mbuf *m; + struct wmi_init_cmd *cmd; + struct wmi_resource_config *cfg; + struct wmi_pdev_set_hw_mode_cmd_param *hw_mode; + struct wmi_pdev_band_to_mac *band_to_mac; + struct wlan_host_mem_chunk *host_mem_chunks; + struct wmi_tlv *tlv; + size_t ret, len; + void *ptr; + uint32_t hw_mode_len = 0; + uint16_t idx; + + if (param->hw_mode_id != WMI_HOST_HW_MODE_MAX) + hw_mode_len = sizeof(*hw_mode) + TLV_HDR_SIZE + + (param->num_band_to_mac * sizeof(*band_to_mac)); + + len = sizeof(*cmd) + TLV_HDR_SIZE + sizeof(*cfg) + hw_mode_len + + (param->num_mem_chunks ? + (sizeof(*host_mem_chunks) * WMI_MAX_MEM_REQS) : 0); + + m = qwz_wmi_alloc_mbuf(len); + if (!m) + return ENOMEM; + + cmd = (struct wmi_init_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_INIT_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ptr = mtod(m, uint8_t *) + sizeof(struct ath12k_htc_hdr) + + sizeof(struct wmi_cmd_hdr) + sizeof(*cmd); + cfg = ptr; + + qwz_wmi_copy_resource_config(cfg, param->res_cfg); + + cfg->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_RESOURCE_CONFIG) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cfg) - TLV_HDR_SIZE); + + ptr += sizeof(*cfg); + host_mem_chunks = ptr + TLV_HDR_SIZE; + len = sizeof(struct wlan_host_mem_chunk); + + for (idx = 0; idx < param->num_mem_chunks; ++idx) { + host_mem_chunks[idx].tlv_header = + FIELD_PREP(WMI_TLV_TAG, WMI_TAG_WLAN_HOST_MEMORY_CHUNK) | + FIELD_PREP(WMI_TLV_LEN, len); + + host_mem_chunks[idx].ptr = param->mem_chunks[idx].paddr; + host_mem_chunks[idx].size = param->mem_chunks[idx].len; + host_mem_chunks[idx].req_id = param->mem_chunks[idx].req_id; + + DNPRINTF(QWZ_D_WMI, + "%s: host mem chunk req_id %d paddr 0x%llx len %d\n", + __func__, param->mem_chunks[idx].req_id, + (uint64_t)param->mem_chunks[idx].paddr, + param->mem_chunks[idx].len); + } + cmd->num_host_mem_chunks = param->num_mem_chunks; + len = sizeof(struct wlan_host_mem_chunk) * param->num_mem_chunks; + + /* num_mem_chunks is zero */ + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE + len; + + if (param->hw_mode_id != WMI_HOST_HW_MODE_MAX) { + hw_mode = (struct wmi_pdev_set_hw_mode_cmd_param *)ptr; + hw_mode->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_SET_HW_MODE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*hw_mode) - TLV_HDR_SIZE); + + hw_mode->hw_mode_index = param->hw_mode_id; + hw_mode->num_band_to_mac = param->num_band_to_mac; + + ptr += sizeof(*hw_mode); + + len = param->num_band_to_mac * sizeof(*band_to_mac); + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + + ptr += TLV_HDR_SIZE; + len = sizeof(*band_to_mac); + + for (idx = 0; idx < param->num_band_to_mac; idx++) { + band_to_mac = (void *)ptr; + + band_to_mac->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_BAND_TO_MAC) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + band_to_mac->pdev_id = param->band_to_mac[idx].pdev_id; + band_to_mac->start_freq = + param->band_to_mac[idx].start_freq; + band_to_mac->end_freq = + param->band_to_mac[idx].end_freq; + ptr += sizeof(*band_to_mac); + } + } + + ret = qwz_wmi_cmd_send(wmi, m, WMI_INIT_CMDID); + if (ret) { + if (ret != ESHUTDOWN) + printf("%s: failed to send WMI_INIT_CMDID\n", __func__); + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd wmi init\n", __func__); + + return 0; +} + +int +qwz_wmi_cmd_init(struct qwz_softc *sc) +{ + struct qwz_wmi_base *wmi_sc = &sc->wmi; + struct wmi_init_cmd_param init_param; + struct target_resource_config config; + + memset(&init_param, 0, sizeof(init_param)); + memset(&config, 0, sizeof(config)); + + sc->hw_params.hw_ops->wmi_init_config(sc, &config); + + if (isset(sc->wmi.svc_map, WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT)) + config.is_reg_cc_ext_event_supported = 1; + + memcpy(&wmi_sc->wlan_resource_config, &config, sizeof(config)); + + init_param.res_cfg = &wmi_sc->wlan_resource_config; + init_param.num_mem_chunks = wmi_sc->num_mem_chunks; + init_param.hw_mode_id = wmi_sc->preferred_hw_mode; + init_param.mem_chunks = wmi_sc->mem_chunks; + + if (sc->hw_params.single_pdev_only) + init_param.hw_mode_id = WMI_HOST_HW_MODE_MAX; + + init_param.num_band_to_mac = sc->num_radios; + qwz_fill_band_to_mac_param(sc, init_param.band_to_mac); + + return qwz_init_cmd_send(&wmi_sc->wmi[0], &init_param); +} + +int +qwz_wmi_wait_for_unified_ready(struct qwz_softc *sc) +{ + int ret; + + while (!sc->wmi.unified_ready) { + ret = tsleep_nsec(&sc->wmi.unified_ready, 0, "qwzunfrdy", + SEC_TO_NSEC(5)); + if (ret) + return -1; + } + + return 0; +} + +int +qwz_wmi_set_hw_mode(struct qwz_softc *sc, + enum wmi_host_hw_mode_config_type mode) +{ + struct wmi_pdev_set_hw_mode_cmd_param *cmd; + struct mbuf *m; + struct qwz_wmi_base *wmi = &sc->wmi; + int len; + int ret; + + len = sizeof(*cmd); + + m = qwz_wmi_alloc_mbuf(len); + if (!m) + return ENOMEM; + + cmd = (struct wmi_pdev_set_hw_mode_cmd_param *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SET_HW_MODE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = WMI_PDEV_ID_SOC; + cmd->hw_mode_index = mode; + + ret = qwz_wmi_cmd_send(&wmi->wmi[0], m, WMI_PDEV_SET_HW_MODE_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send " + "WMI_PDEV_SET_HW_MODE_CMDID\n", __func__); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd pdev set hw mode %d\n", __func__, + cmd->hw_mode_index); + + return 0; +} + +int +qwz_wmi_set_sta_ps_param(struct qwz_softc *sc, uint32_t vdev_id, + uint8_t pdev_id, uint32_t param, uint32_t param_value) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_sta_powersave_param_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_sta_powersave_param_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_STA_POWERSAVE_PARAM_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = vdev_id; + cmd->param = param; + cmd->value = param_value; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_STA_POWERSAVE_PARAM_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send " + "WMI_STA_POWERSAVE_PARAM_CMDID", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd set powersave param vdev_id %d param %d " + "value %d\n", __func__, vdev_id, param, param_value); + + return 0; +} + +int +qwz_wmi_mgmt_send(struct qwz_softc *sc, struct qwz_vif *arvif, uint8_t pdev_id, + uint32_t buf_id, struct mbuf *frame, struct qwz_tx_data *tx_data) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_mgmt_send_cmd *cmd; + struct wmi_tlv *frame_tlv; + struct mbuf *m; + uint32_t buf_len; + int ret, len; + uint64_t paddr; + + paddr = tx_data->map->dm_segs[0].ds_addr; + + buf_len = frame->m_pkthdr.len < WMI_MGMT_SEND_DOWNLD_LEN ? + frame->m_pkthdr.len : WMI_MGMT_SEND_DOWNLD_LEN; + + len = sizeof(*cmd) + sizeof(*frame_tlv) + roundup(buf_len, 4); + + m = qwz_wmi_alloc_mbuf(len); + if (!m) + return ENOMEM; + + cmd = (struct wmi_mgmt_send_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_MGMT_TX_SEND_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = arvif->vdev_id; + cmd->desc_id = buf_id; + cmd->chanfreq = 0; + cmd->paddr_lo = paddr & 0xffffffff; + cmd->paddr_hi = paddr >> 32; + cmd->frame_len = frame->m_pkthdr.len; + cmd->buf_len = buf_len; + cmd->tx_params_valid = 0; + + frame_tlv = (struct wmi_tlv *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr) + + sizeof(*cmd)); + frame_tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, buf_len); + + memcpy(frame_tlv->value, mtod(frame, void *), buf_len); +#if 0 /* Not needed on OpenBSD? */ + ath12k_ce_byte_swap(frame_tlv->value, buf_len); +#endif + ret = qwz_wmi_cmd_send(wmi, m, WMI_MGMT_TX_SEND_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to submit " + "WMI_MGMT_TX_SEND_CMDID cmd\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd mgmt tx send", __func__); + + tx_data->m = frame; + return 0; +} + +int +qwz_wmi_vdev_create(struct qwz_softc *sc, uint8_t *macaddr, + struct vdev_create_params *param) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[param->pdev_id]; + struct wmi_vdev_create_cmd *cmd; + struct mbuf *m; + struct wmi_vdev_txrx_streams *txrx_streams; + struct wmi_tlv *tlv; + int ret, len; + void *ptr; + + /* It can be optimized my sending tx/rx chain configuration + * only for supported bands instead of always sending it for + * both the bands. + */ + len = sizeof(*cmd) + TLV_HDR_SIZE + + (WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams)); + + m = qwz_wmi_alloc_mbuf(len); + if (!m) + return ENOMEM; + + cmd = (struct wmi_vdev_create_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_CREATE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = param->if_id; + cmd->vdev_type = param->type; + cmd->vdev_subtype = param->subtype; + cmd->num_cfg_txrx_streams = WMI_NUM_SUPPORTED_BAND_MAX; + cmd->pdev_id = param->pdev_id; + cmd->mbssid_flags = param->mbssid_flags; + cmd->mbssid_tx_vdev_id = param->mbssid_tx_vdev_id; + + IEEE80211_ADDR_COPY(cmd->vdev_macaddr.addr, macaddr); + + ptr = (void *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr) + + sizeof(*cmd)); + len = WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + + ptr += TLV_HDR_SIZE; + txrx_streams = ptr; + len = sizeof(*txrx_streams); + txrx_streams->tlv_header = + FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_TXRX_STREAMS) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + txrx_streams->band = WMI_TPC_CHAINMASK_CONFIG_BAND_2G; + txrx_streams->supported_tx_streams = param->chains[0].tx; + txrx_streams->supported_rx_streams = param->chains[0].rx; + + txrx_streams++; + txrx_streams->tlv_header = + FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_TXRX_STREAMS) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + txrx_streams->band = WMI_TPC_CHAINMASK_CONFIG_BAND_5G; + txrx_streams->supported_tx_streams = param->chains[1].tx; + txrx_streams->supported_rx_streams = param->chains[1].rx; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_VDEV_CREATE_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to submit WMI_VDEV_CREATE_CMDID\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd vdev create id %d type %d subtype %d " + "macaddr %s pdevid %d\n", __func__, param->if_id, param->type, + param->subtype, ether_sprintf(macaddr), param->pdev_id); + + return ret; +} + +int +qwz_wmi_vdev_set_param_cmd(struct qwz_softc *sc, uint32_t vdev_id, + uint8_t pdev_id, uint32_t param_id, uint32_t param_value) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_vdev_set_param_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_vdev_set_param_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_SET_PARAM_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = vdev_id; + cmd->param_id = param_id; + cmd->param_value = param_value; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_VDEV_SET_PARAM_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send WMI_VDEV_SET_PARAM_CMDID\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd vdev set param vdev 0x%x param %d " + "value %d\n", __func__, vdev_id, param_id, param_value); + + return 0; +} + +int +qwz_wmi_vdev_up(struct qwz_softc *sc, uint32_t vdev_id, uint32_t pdev_id, + uint32_t aid, const uint8_t *bssid, uint8_t *tx_bssid, + uint32_t nontx_profile_idx, uint32_t nontx_profile_cnt) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_vdev_up_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_vdev_up_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_UP_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->vdev_assoc_id = aid; + + IEEE80211_ADDR_COPY(cmd->vdev_bssid.addr, bssid); + + cmd->nontx_profile_idx = nontx_profile_idx; + cmd->nontx_profile_cnt = nontx_profile_cnt; + if (tx_bssid) + IEEE80211_ADDR_COPY(cmd->tx_vdev_bssid.addr, tx_bssid); +#if 0 + if (arvif && arvif->vif->type == NL80211_IFTYPE_STATION) { + bss_conf = &arvif->vif->bss_conf; + + if (bss_conf->nontransmitted) { + ether_addr_copy(cmd->tx_vdev_bssid.addr, + bss_conf->transmitter_bssid); + cmd->nontx_profile_idx = bss_conf->bssid_index; + cmd->nontx_profile_cnt = bss_conf->bssid_indicator; + } + } +#endif + ret = qwz_wmi_cmd_send(wmi, m, WMI_VDEV_UP_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to submit WMI_VDEV_UP cmd\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd vdev up id 0x%x assoc id %d bssid %s\n", + __func__, vdev_id, aid, ether_sprintf((u_char *)bssid)); + + return 0; +} + +int +qwz_wmi_vdev_down(struct qwz_softc *sc, uint32_t vdev_id, uint8_t pdev_id) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_vdev_down_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_vdev_down_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_DOWN_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_VDEV_DOWN_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to submit WMI_VDEV_DOWN cmd\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd vdev down id 0x%x\n", __func__, vdev_id); + + return 0; +} + +void +qwz_wmi_put_wmi_channel(struct wmi_channel *chan, + struct wmi_vdev_start_req_arg *arg) +{ + uint32_t center_freq1 = arg->channel.band_center_freq1; + + memset(chan, 0, sizeof(*chan)); + + chan->mhz = arg->channel.freq; + chan->band_center_freq1 = arg->channel.band_center_freq1; + + if (arg->channel.mode == MODE_11AX_HE160) { + if (arg->channel.freq > arg->channel.band_center_freq1) + chan->band_center_freq1 = center_freq1 + 40; + else + chan->band_center_freq1 = center_freq1 - 40; + + chan->band_center_freq2 = arg->channel.band_center_freq1; + } else if ((arg->channel.mode == MODE_11AC_VHT80_80) || + (arg->channel.mode == MODE_11AX_HE80_80)) { + chan->band_center_freq2 = arg->channel.band_center_freq2; + } else + chan->band_center_freq2 = 0; + + chan->info |= FIELD_PREP(WMI_CHAN_INFO_MODE, arg->channel.mode); + if (arg->channel.passive) + chan->info |= WMI_CHAN_INFO_PASSIVE; + if (arg->channel.allow_ibss) + chan->info |= WMI_CHAN_INFO_ADHOC_ALLOWED; + if (arg->channel.allow_ht) + chan->info |= WMI_CHAN_INFO_ALLOW_HT; + if (arg->channel.allow_vht) + chan->info |= WMI_CHAN_INFO_ALLOW_VHT; + if (arg->channel.allow_he) + chan->info |= WMI_CHAN_INFO_ALLOW_HE; + if (arg->channel.ht40plus) + chan->info |= WMI_CHAN_INFO_HT40_PLUS; + if (arg->channel.chan_radar) + chan->info |= WMI_CHAN_INFO_DFS; + if (arg->channel.freq2_radar) + chan->info |= WMI_CHAN_INFO_DFS_FREQ2; + + chan->reg_info_1 = FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_PWR, + arg->channel.max_power) | + FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_REG_PWR, + arg->channel.max_reg_power); + + chan->reg_info_2 = FIELD_PREP(WMI_CHAN_REG_INFO2_ANT_MAX, + arg->channel.max_antenna_gain) | + FIELD_PREP(WMI_CHAN_REG_INFO2_MAX_TX_PWR, + arg->channel.max_power); +} + +int +qwz_wmi_vdev_stop(struct qwz_softc *sc, uint8_t vdev_id, uint8_t pdev_id) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_vdev_stop_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_vdev_stop_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_STOP_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_VDEV_STOP_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to submit WMI_VDEV_STOP cmd\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd vdev stop id 0x%x\n", __func__, vdev_id); + + return ret; +} + +int +qwz_wmi_vdev_start(struct qwz_softc *sc, struct wmi_vdev_start_req_arg *arg, + int pdev_id, int restart) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_vdev_start_request_cmd *cmd; + struct mbuf *m; + struct wmi_channel *chan; + struct wmi_tlv *tlv; + void *ptr; + int ret, len; + + if (arg->ssid_len > sizeof(cmd->ssid.ssid)) + return EINVAL; + + len = sizeof(*cmd) + sizeof(*chan) + TLV_HDR_SIZE; + + m = qwz_wmi_alloc_mbuf(len); + if (!m) + return ENOMEM; + + cmd = (struct wmi_vdev_start_request_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_VDEV_START_REQUEST_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = arg->vdev_id; + cmd->beacon_interval = arg->bcn_intval; + cmd->bcn_tx_rate = arg->bcn_tx_rate; + cmd->dtim_period = arg->dtim_period; + cmd->num_noa_descriptors = arg->num_noa_descriptors; + cmd->preferred_rx_streams = arg->pref_rx_streams; + cmd->preferred_tx_streams = arg->pref_tx_streams; + cmd->cac_duration_ms = arg->cac_duration_ms; + cmd->regdomain = arg->regdomain; + cmd->he_ops = arg->he_ops; + cmd->mbssid_flags = arg->mbssid_flags; + cmd->mbssid_tx_vdev_id = arg->mbssid_tx_vdev_id; + + if (!restart) { + if (arg->ssid) { + cmd->ssid.ssid_len = arg->ssid_len; + memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len); + } + if (arg->hidden_ssid) + cmd->flags |= WMI_VDEV_START_HIDDEN_SSID; + if (arg->pmf_enabled) + cmd->flags |= WMI_VDEV_START_PMF_ENABLED; + } + + cmd->flags |= WMI_VDEV_START_LDPC_RX_ENABLED; + if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags)) + cmd->flags |= WMI_VDEV_START_HW_ENCRYPTION_DISABLED; + + ptr = mtod(m, void *) + sizeof(struct ath12k_htc_hdr) + + sizeof(struct wmi_cmd_hdr) + sizeof(*cmd); + chan = ptr; + + qwz_wmi_put_wmi_channel(chan, arg); + + chan->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_CHANNEL) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*chan) - TLV_HDR_SIZE); + ptr += sizeof(*chan); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, 0); + + /* Note: This is a nested TLV containing: + * [wmi_tlv][wmi_p2p_noa_descriptor][wmi_tlv].. + */ + + ptr += sizeof(*tlv); + + ret = qwz_wmi_cmd_send(wmi, m, restart ? + WMI_VDEV_RESTART_REQUEST_CMDID : WMI_VDEV_START_REQUEST_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to submit vdev_%s cmd\n", + sc->sc_dev.dv_xname, restart ? "restart" : "start"); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd vdev %s id 0x%x freq %u mode 0x%x\n", + __func__, restart ? "restart" : "start", arg->vdev_id, + arg->channel.freq, arg->channel.mode); + + return ret; +} + +int +qwz_core_start(struct qwz_softc *sc) +{ + int ret; + + ret = qwz_wmi_attach(sc); + if (ret) { + printf("%s: failed to attach wmi: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = qwz_htc_init(sc); + if (ret) { + printf("%s: failed to init htc: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_wmi_detach; + } + + ret = sc->ops.start(sc); + if (ret) { + printf("%s: failed to start host interface: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_wmi_detach; + } + + ret = qwz_htc_wait_target(sc); + if (ret) { + printf("%s: failed to connect to HTC: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_hif_stop; + } + + ret = qwz_dp_htt_connect(&sc->dp); + if (ret) { + printf("%s: failed to connect to HTT: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_hif_stop; + } + + ret = qwz_wmi_connect(sc); + if (ret) { + printf("%s: failed to connect wmi: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_hif_stop; + } + + sc->wmi.service_ready = 0; + + ret = qwz_htc_start(&sc->htc); + if (ret) { + printf("%s: failed to start HTC: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_hif_stop; + } + + ret = qwz_wmi_wait_for_service_ready(sc); + if (ret) { + printf("%s: failed to receive wmi service ready event: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_hif_stop; + } +#if 0 + ret = ath12k_mac_allocate(ab); + if (ret) { + ath12k_err(ab, "failed to create new hw device with mac80211 :%d\n", + ret); + goto err_hif_stop; + } + ath12k_dp_pdev_pre_alloc(sc); +#endif + ret = qwz_dp_pdev_reo_setup(sc); + if (ret) { + printf("%s: failed to initialize reo destination rings: %d\n", + __func__, ret); + goto err_mac_destroy; + } + + ret = qwz_wmi_cmd_init(sc); + if (ret) { + printf("%s: failed to send wmi init cmd: %d\n", __func__, ret); + goto err_reo_cleanup; + } + + ret = qwz_wmi_wait_for_unified_ready(sc); + if (ret) { + printf("%s: failed to receive wmi unified ready event: %d\n", + __func__, ret); + goto err_reo_cleanup; + } + + /* put hardware to DBS mode */ + if (sc->hw_params.single_pdev_only && + sc->hw_params.num_rxmda_per_pdev > 1) { + ret = qwz_wmi_set_hw_mode(sc, WMI_HOST_HW_MODE_DBS); + if (ret) { + printf("%s: failed to send dbs mode: %d\n", + __func__, ret); + goto err_hif_stop; + } + } + + ret = qwz_dp_tx_htt_h2t_ver_req_msg(sc); + if (ret) { + if (ret != ENOTSUP) { + printf("%s: failed to send htt version " + "request message: %d\n", __func__, ret); + } + goto err_reo_cleanup; + } + + return 0; +err_reo_cleanup: + qwz_dp_pdev_reo_cleanup(sc); +err_mac_destroy: +#if 0 + ath12k_mac_destroy(ab); +#endif +err_hif_stop: + sc->ops.stop(sc); +err_wmi_detach: + qwz_wmi_detach(sc); + return ret; +} + +void +qwz_core_stop(struct qwz_softc *sc) +{ + if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags)) + qwz_qmi_firmware_stop(sc); + + sc->ops.stop(sc); + qwz_wmi_detach(sc); + qwz_dp_pdev_reo_cleanup(sc); +} + +void +qwz_core_pdev_destroy(struct qwz_softc *sc) +{ + qwz_dp_pdev_free(sc); +} + +int +qwz_core_pdev_create(struct qwz_softc *sc) +{ + int ret; + + ret = qwz_dp_pdev_alloc(sc); + if (ret) { + printf("%s: failed to attach DP pdev: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = qwz_mac_register(sc); + if (ret) { + printf("%s: failed register the radio with mac80211: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_dp_pdev_free; + } +#if 0 + + ret = ath12k_thermal_register(ab); + if (ret) { + ath12k_err(ab, "could not register thermal device: %d\n", + ret); + goto err_mac_unregister; + } + + ret = ath12k_spectral_init(ab); + if (ret) { + ath12k_err(ab, "failed to init spectral %d\n", ret); + goto err_thermal_unregister; + } +#endif + return 0; +#if 0 +err_thermal_unregister: + ath12k_thermal_unregister(ab); +err_mac_unregister: + ath12k_mac_unregister(ab); +#endif +err_dp_pdev_free: + qwz_dp_pdev_free(sc); +#if 0 +err_pdev_debug: + ath12k_debugfs_pdev_destroy(ab); +#endif + return ret; +} + +void +qwz_core_deinit(struct qwz_softc *sc) +{ + struct ath12k_hal *hal = &sc->hal; + int s = splnet(); + +#ifdef notyet + mutex_lock(&ab->core_lock); +#endif + sc->ops.irq_disable(sc); + + qwz_core_stop(sc); + qwz_core_pdev_destroy(sc); +#ifdef notyet + mutex_unlock(&ab->core_lock); +#endif + sc->ops.power_down(sc); +#if 0 + ath12k_mac_destroy(ab); + ath12k_debugfs_soc_destroy(ab); +#endif + qwz_dp_free(sc); +#if 0 + ath12k_reg_free(ab); +#endif + qwz_qmi_deinit_service(sc); + + hal->num_shadow_reg_configured = 0; + + splx(s); +} + +int +qwz_core_qmi_firmware_ready(struct qwz_softc *sc) +{ + int ret; + + ret = qwz_core_start_firmware(sc, sc->fw_mode); + if (ret) { + printf("%s: failed to start firmware: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = qwz_ce_init_pipes(sc); + if (ret) { + printf("%s: failed to initialize CE: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_firmware_stop; + } + + ret = qwz_dp_alloc(sc); + if (ret) { + printf("%s: failed to init DP: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_firmware_stop; + } + + switch (sc->crypto_mode) { + case ATH12K_CRYPT_MODE_SW: + set_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags); + set_bit(ATH12K_FLAG_RAW_MODE, sc->sc_flags); + break; + case ATH12K_CRYPT_MODE_HW: + clear_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags); + clear_bit(ATH12K_FLAG_RAW_MODE, sc->sc_flags); + break; + default: + printf("%s: invalid crypto_mode: %d\n", + sc->sc_dev.dv_xname, sc->crypto_mode); + return EINVAL; + } + + if (sc->frame_mode == ATH12K_HW_TXRX_RAW) + set_bit(ATH12K_FLAG_RAW_MODE, sc->sc_flags); +#if 0 + mutex_lock(&ab->core_lock); +#endif + ret = qwz_core_start(sc); + if (ret) { + printf("%s: failed to start core: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_dp_free; + } + + if (!sc->attached) { + printf("%s: %s fw 0x%x address %s\n", sc->sc_dev.dv_xname, + sc->hw_params.name, sc->qmi_target.fw_version, + ether_sprintf(sc->mac_addr)); + } + + ret = qwz_core_pdev_create(sc); + if (ret) { + printf("%s: failed to create pdev core: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_core_stop; + } + +#if 0 /* TODO: Is this in the right spot for OpenBSD? */ + sc->ops.irq_enable(sc); +#endif + +#if 0 + mutex_unlock(&ab->core_lock); +#endif + + return 0; +err_core_stop: + qwz_core_stop(sc); +#if 0 + ath12k_mac_destroy(ab); +#endif +err_dp_free: + qwz_dp_free(sc); +#if 0 + mutex_unlock(&ab->core_lock); +#endif +err_firmware_stop: + qwz_qmi_firmware_stop(sc); + + return ret; +} + +void +qwz_qmi_fw_init_done(struct qwz_softc *sc) +{ + int ret = 0; + + clear_bit(ATH12K_FLAG_QMI_FAIL, sc->sc_flags); + + if (sc->qmi_cal_done == 0 && sc->hw_params.cold_boot_calib) { + qwz_qmi_process_coldboot_calibration(sc); + } else { + clear_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags); + clear_bit(ATH12K_FLAG_RECOVERY, sc->sc_flags); + ret = qwz_core_qmi_firmware_ready(sc); + if (ret) { + set_bit(ATH12K_FLAG_QMI_FAIL, sc->sc_flags); + return; + } + } +} + +int +qwz_qmi_event_server_arrive(struct qwz_softc *sc) +{ + int ret; + + sc->fw_init_done = 0; + sc->expect_fwmem_req = 1; + + ret = qwz_qmi_fw_ind_register_send(sc); + if (ret < 0) { + printf("%s: failed to send qmi firmware indication: %d\n", + sc->sc_dev.dv_xname, ret); + sc->expect_fwmem_req = 0; + return ret; + } + + ret = qwz_qmi_host_cap_send(sc); + if (ret < 0) { + printf("%s: failed to send qmi host cap: %d\n", + sc->sc_dev.dv_xname, ret); + sc->expect_fwmem_req = 0; + return ret; + } + + ret = qwz_qmi_mem_seg_send(sc); + if (ret == EBUSY) + ret = qwz_qmi_mem_seg_send(sc); + sc->expect_fwmem_req = 0; + if (ret) { + printf("%s: failed to send qmi memory segments: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = qwz_qmi_event_load_bdf(sc); + if (ret < 0) { + printf("%s: qmi failed to download BDF:%d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = qwz_qmi_wlanfw_m3_info_send(sc); + if (ret) { + printf("%s: qmi m3 info send failed:%d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + while (!sc->fw_init_done) { + ret = tsleep_nsec(&sc->fw_init_done, 0, "qwzfwinit", + SEC_TO_NSEC(10)); + if (ret) { + printf("%s: fw init timeout\n", sc->sc_dev.dv_xname); + return -1; + } + } + + qwz_qmi_fw_init_done(sc); + return 0; +} + +int +qwz_core_init(struct qwz_softc *sc) +{ + int error; + + error = qwz_qmi_init_service(sc); + if (error) { + printf("failed to initialize qmi :%d\n", error); + return error; + } + + error = sc->ops.power_up(sc); + if (error) + qwz_qmi_deinit_service(sc); + + return error; +} + +int +qwz_init_hw_params(struct qwz_softc *sc) +{ + const struct ath12k_hw_params *hw_params = NULL; + int i; + + for (i = 0; i < nitems(ath12k_hw_params); i++) { + hw_params = &ath12k_hw_params[i]; + + if (hw_params->hw_rev == sc->sc_hw_rev) + break; + } + + if (i == nitems(ath12k_hw_params)) { + printf("%s: Unsupported hardware version: 0x%x\n", + sc->sc_dev.dv_xname, sc->sc_hw_rev); + return EINVAL; + } + + sc->hw_params = *hw_params; + + DPRINTF("%s: %s\n", sc->sc_dev.dv_xname, sc->hw_params.name); + + return 0; +} + +static const struct hal_srng_config hw_srng_config_templ[QWZ_NUM_SRNG_CFG] = { + /* TODO: max_rings can populated by querying HW capabilities */ + { /* REO_DST */ + .start_ring_id = HAL_SRNG_RING_ID_REO2SW1, + .max_rings = 4, + .entry_size = sizeof(struct hal_reo_dest_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .max_size = HAL_REO_REO2SW1_RING_BASE_MSB_RING_SIZE, + }, + + { /* REO_EXCEPTION */ + /* Designating REO2TCL ring as exception ring. This ring is + * similar to other REO2SW rings though it is named as REO2TCL. + * Any of theREO2SW rings can be used as exception ring. + */ + .start_ring_id = HAL_SRNG_RING_ID_REO2TCL, + .max_rings = 1, + .entry_size = sizeof(struct hal_reo_dest_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .max_size = HAL_REO_REO2TCL_RING_BASE_MSB_RING_SIZE, + }, + { /* REO_REINJECT */ + .start_ring_id = HAL_SRNG_RING_ID_SW2REO, + .max_rings = 1, + .entry_size = sizeof(struct hal_reo_entrance_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_REO_SW2REO_RING_BASE_MSB_RING_SIZE, + }, + { /* REO_CMD */ + .start_ring_id = HAL_SRNG_RING_ID_REO_CMD, + .max_rings = 1, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_reo_get_queue_stats)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_REO_CMD_RING_BASE_MSB_RING_SIZE, + }, + { /* REO_STATUS */ + .start_ring_id = HAL_SRNG_RING_ID_REO_STATUS, + .max_rings = 1, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_reo_get_queue_stats_status)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .max_size = HAL_REO_STATUS_RING_BASE_MSB_RING_SIZE, + }, + { /* TCL_DATA */ + .start_ring_id = HAL_SRNG_RING_ID_SW2TCL1, + .max_rings = 3, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_tcl_data_cmd)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_SW2TCL1_RING_BASE_MSB_RING_SIZE, + }, + { /* TCL_CMD */ + .start_ring_id = HAL_SRNG_RING_ID_SW2TCL_CMD, + .max_rings = 1, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_tcl_gse_cmd)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_SW2TCL1_CMD_RING_BASE_MSB_RING_SIZE, + }, + { /* TCL_STATUS */ + .start_ring_id = HAL_SRNG_RING_ID_TCL_STATUS, + .max_rings = 1, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_tcl_status_ring)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .max_size = HAL_TCL_STATUS_RING_BASE_MSB_RING_SIZE, + }, + { /* CE_SRC */ + .start_ring_id = HAL_SRNG_RING_ID_CE0_SRC, + .max_rings = 12, + .entry_size = sizeof(struct hal_ce_srng_src_desc) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_CE_SRC_RING_BASE_MSB_RING_SIZE, + }, + { /* CE_DST */ + .start_ring_id = HAL_SRNG_RING_ID_CE0_DST, + .max_rings = 12, + .entry_size = sizeof(struct hal_ce_srng_dest_desc) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_CE_DST_RING_BASE_MSB_RING_SIZE, + }, + { /* CE_DST_STATUS */ + .start_ring_id = HAL_SRNG_RING_ID_CE0_DST_STATUS, + .max_rings = 12, + .entry_size = sizeof(struct hal_ce_srng_dst_status_desc) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .max_size = HAL_CE_DST_STATUS_RING_BASE_MSB_RING_SIZE, + }, + { /* WBM_IDLE_LINK */ + .start_ring_id = HAL_SRNG_RING_ID_WBM_IDLE_LINK, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_link_desc) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_WBM_IDLE_LINK_RING_BASE_MSB_RING_SIZE, + }, + { /* SW2WBM_RELEASE */ + .start_ring_id = HAL_SRNG_RING_ID_WBM_SW_RELEASE, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_release_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_SW2WBM_RELEASE_RING_BASE_MSB_RING_SIZE, + }, + { /* WBM2SW_RELEASE */ + .start_ring_id = HAL_SRNG_RING_ID_WBM2SW0_RELEASE, + .max_rings = 5, + .entry_size = sizeof(struct hal_wbm_release_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .max_size = HAL_WBM2SW_RELEASE_RING_BASE_MSB_RING_SIZE, + }, + { /* RXDMA_BUF */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF, + .max_rings = 2, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_DST */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_RXDMA2SW0, + .max_rings = 1, + .entry_size = sizeof(struct hal_reo_entrance_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_DST, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_MONITOR_BUF */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA2_BUF, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_MONITOR_STATUS */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_MONITOR_DST */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1, + .max_rings = 1, + .entry_size = sizeof(struct hal_reo_entrance_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_DST, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_MONITOR_DESC */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_DESC, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA DIR BUF */ + .start_ring_id = HAL_SRNG_RING_ID_RXDMA_DIR_BUF, + .max_rings = 1, + .entry_size = 8 >> 2, /* TODO: Define the struct */ + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, +}; + +int +qwz_hal_srng_create_config(struct qwz_softc *sc) +{ + struct ath12k_hal *hal = &sc->hal; + struct hal_srng_config *s; + + memcpy(hal->srng_config, hw_srng_config_templ, + sizeof(hal->srng_config)); + + s = &hal->srng_config[HAL_REO_DST]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_RING_BASE_LSB(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_RING_HP(sc); + s->reg_size[0] = HAL_REO2_RING_BASE_LSB(sc) - HAL_REO1_RING_BASE_LSB(sc); + s->reg_size[1] = HAL_REO2_RING_HP(sc) - HAL_REO1_RING_HP(sc); + + s = &hal->srng_config[HAL_REO_EXCEPTION]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_TCL_RING_BASE_LSB(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_TCL_RING_HP(sc); + + s = &hal->srng_config[HAL_REO_REINJECT]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_BASE_LSB(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_HP(sc); + + s = &hal->srng_config[HAL_REO_CMD]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_RING_BASE_LSB(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_HP(sc); + + s = &hal->srng_config[HAL_REO_STATUS]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_RING_BASE_LSB(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_HP(sc); + + s = &hal->srng_config[HAL_TCL_DATA]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_HP; + s->reg_size[0] = HAL_TCL2_RING_BASE_LSB(sc) - HAL_TCL1_RING_BASE_LSB(sc); + s->reg_size[1] = HAL_TCL2_RING_HP - HAL_TCL1_RING_HP; + + s = &hal->srng_config[HAL_TCL_CMD]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_RING_BASE_LSB(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_RING_HP; + + s = &hal->srng_config[HAL_TCL_STATUS]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_BASE_LSB(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP; + + s = &hal->srng_config[HAL_CE_SRC]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(sc) + HAL_CE_DST_RING_BASE_LSB + + ATH12K_CE_OFFSET(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(sc) + HAL_CE_DST_RING_HP + + ATH12K_CE_OFFSET(sc); + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(sc) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(sc); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(sc) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(sc); + + s = &hal->srng_config[HAL_CE_DST]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(sc) + HAL_CE_DST_RING_BASE_LSB + + ATH12K_CE_OFFSET(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(sc) + HAL_CE_DST_RING_HP + + ATH12K_CE_OFFSET(sc); + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(sc) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(sc); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(sc) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(sc); + + s = &hal->srng_config[HAL_CE_DST_STATUS]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(sc) + + HAL_CE_DST_STATUS_RING_BASE_LSB + ATH12K_CE_OFFSET(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(sc) + HAL_CE_DST_STATUS_RING_HP + + ATH12K_CE_OFFSET(sc); + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(sc) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(sc); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(sc) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(sc); + + s = &hal->srng_config[HAL_WBM_IDLE_LINK]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_IDLE_LINK_RING_BASE_LSB(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_IDLE_LINK_RING_HP; + + s = &hal->srng_config[HAL_SW2WBM_RELEASE]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_RELEASE_RING_BASE_LSB(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_RELEASE_RING_HP; + + s = &hal->srng_config[HAL_WBM2SW_RELEASE]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM0_RELEASE_RING_BASE_LSB(sc); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM0_RELEASE_RING_HP; + s->reg_size[0] = HAL_WBM1_RELEASE_RING_BASE_LSB(sc) - + HAL_WBM0_RELEASE_RING_BASE_LSB(sc); + s->reg_size[1] = HAL_WBM1_RELEASE_RING_HP - HAL_WBM0_RELEASE_RING_HP; + + return 0; +} + +int +qwz_hal_srng_get_ring_id(struct qwz_softc *sc, + enum hal_ring_type type, int ring_num, int mac_id) +{ + struct hal_srng_config *srng_config = &sc->hal.srng_config[type]; + int ring_id; + + if (ring_num >= srng_config->max_rings) { + printf("%s: invalid ring number :%d\n", __func__, ring_num); + return -1; + } + + ring_id = srng_config->start_ring_id + ring_num; + if (srng_config->lmac_ring) + ring_id += mac_id * HAL_SRNG_RINGS_PER_LMAC; + + if (ring_id >= HAL_SRNG_RING_ID_MAX) { + printf("%s: invalid ring ID :%d\n", __func__, ring_id); + return -1; + } + + return ring_id; +} + +void +qwz_hal_srng_update_hp_tp_addr(struct qwz_softc *sc, int shadow_cfg_idx, + enum hal_ring_type ring_type, int ring_num) +{ + struct hal_srng *srng; + struct ath12k_hal *hal = &sc->hal; + int ring_id; + struct hal_srng_config *srng_config = &hal->srng_config[ring_type]; + + ring_id = qwz_hal_srng_get_ring_id(sc, ring_type, ring_num, 0); + if (ring_id < 0) + return; + + srng = &hal->srng_list[ring_id]; + + if (srng_config->ring_dir == HAL_SRNG_DIR_DST) + srng->u.dst_ring.tp_addr = (uint32_t *)( + HAL_SHADOW_REG(sc, shadow_cfg_idx) + + (unsigned long)sc->mem); + else + srng->u.src_ring.hp_addr = (uint32_t *)( + HAL_SHADOW_REG(sc, shadow_cfg_idx) + + (unsigned long)sc->mem); +} + +void +qwz_hal_srng_shadow_update_hp_tp(struct qwz_softc *sc, struct hal_srng *srng) +{ +#ifdef notyet + lockdep_assert_held(&srng->lock); +#endif + /* Update the shadow HP if the ring isn't empty. */ + if (srng->ring_dir == HAL_SRNG_DIR_SRC && + *srng->u.src_ring.tp_addr != srng->u.src_ring.hp) + qwz_hal_srng_access_end(sc, srng); +} + +int +qwz_hal_srng_update_shadow_config(struct qwz_softc *sc, + enum hal_ring_type ring_type, int ring_num) +{ + struct ath12k_hal *hal = &sc->hal; + struct hal_srng_config *srng_config = &hal->srng_config[ring_type]; + int shadow_cfg_idx = hal->num_shadow_reg_configured; + uint32_t target_reg; + + if (shadow_cfg_idx >= HAL_SHADOW_NUM_REGS) + return EINVAL; + + hal->num_shadow_reg_configured++; + + target_reg = srng_config->reg_start[HAL_HP_OFFSET_IN_REG_START]; + target_reg += srng_config->reg_size[HAL_HP_OFFSET_IN_REG_START] * + ring_num; + + /* For destination ring, shadow the TP */ + if (srng_config->ring_dir == HAL_SRNG_DIR_DST) + target_reg += HAL_OFFSET_FROM_HP_TO_TP; + + hal->shadow_reg_addr[shadow_cfg_idx] = target_reg; + + /* update hp/tp addr to hal structure*/ + qwz_hal_srng_update_hp_tp_addr(sc, shadow_cfg_idx, ring_type, ring_num); + + DPRINTF("%s: target_reg %x, shadow reg 0x%x shadow_idx 0x%x, " + "ring_type %d, ring num %d\n", __func__, target_reg, + HAL_SHADOW_REG(sc, shadow_cfg_idx), shadow_cfg_idx, + ring_type, ring_num); + + return 0; +} + +void +qwz_hal_srng_shadow_config(struct qwz_softc *sc) +{ + struct ath12k_hal *hal = &sc->hal; + int ring_type, ring_num; + struct hal_srng_config *cfg; + + /* update all the non-CE srngs. */ + for (ring_type = 0; ring_type < HAL_MAX_RING_TYPES; ring_type++) { + cfg = &hal->srng_config[ring_type]; + + if (ring_type == HAL_CE_SRC || + ring_type == HAL_CE_DST || + ring_type == HAL_CE_DST_STATUS) + continue; + + if (cfg->lmac_ring) + continue; + + for (ring_num = 0; ring_num < cfg->max_rings; ring_num++) { + qwz_hal_srng_update_shadow_config(sc, ring_type, + ring_num); + } + } +} + +void +qwz_hal_srng_get_shadow_config(struct qwz_softc *sc, uint32_t **cfg, + uint32_t *len) +{ + struct ath12k_hal *hal = &sc->hal; + + *len = hal->num_shadow_reg_configured; + *cfg = hal->shadow_reg_addr; +} + +int +qwz_hal_alloc_cont_rdp(struct qwz_softc *sc) +{ + struct ath12k_hal *hal = &sc->hal; + size_t size = sizeof(uint32_t) * HAL_SRNG_RING_ID_MAX; + + if (hal->rdpmem == NULL) { + hal->rdpmem = qwz_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE); + if (hal->rdpmem == NULL) { + printf("%s: could not allocate RDP DMA memory\n", + sc->sc_dev.dv_xname); + return ENOMEM; + + } + } + + hal->rdp.vaddr = QWZ_DMA_KVA(hal->rdpmem); + hal->rdp.paddr = QWZ_DMA_DVA(hal->rdpmem); + return 0; +} + +void +qwz_hal_free_cont_rdp(struct qwz_softc *sc) +{ + struct ath12k_hal *hal = &sc->hal; + + if (hal->rdpmem == NULL) + return; + + hal->rdp.vaddr = NULL; + hal->rdp.paddr = 0L; + qwz_dmamem_free(sc->sc_dmat, hal->rdpmem); + hal->rdpmem = NULL; +} + +int +qwz_hal_alloc_cont_wrp(struct qwz_softc *sc) +{ + struct ath12k_hal *hal = &sc->hal; + size_t size = sizeof(uint32_t) * HAL_SRNG_NUM_LMAC_RINGS; + + if (hal->wrpmem == NULL) { + hal->wrpmem = qwz_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE); + if (hal->wrpmem == NULL) { + printf("%s: could not allocate WDP DMA memory\n", + sc->sc_dev.dv_xname); + return ENOMEM; + + } + } + + hal->wrp.vaddr = QWZ_DMA_KVA(hal->wrpmem); + hal->wrp.paddr = QWZ_DMA_DVA(hal->wrpmem); + return 0; +} + +void +qwz_hal_free_cont_wrp(struct qwz_softc *sc) +{ + struct ath12k_hal *hal = &sc->hal; + + if (hal->wrpmem == NULL) + return; + + hal->wrp.vaddr = NULL; + hal->wrp.paddr = 0L; + qwz_dmamem_free(sc->sc_dmat, hal->wrpmem); + hal->wrpmem = NULL; +} + +int +qwz_hal_srng_init(struct qwz_softc *sc) +{ + struct ath12k_hal *hal = &sc->hal; + int ret; + + memset(hal, 0, sizeof(*hal)); + + ret = qwz_hal_srng_create_config(sc); + if (ret) + goto err_hal; + + ret = qwz_hal_alloc_cont_rdp(sc); + if (ret) + goto err_hal; + + ret = qwz_hal_alloc_cont_wrp(sc); + if (ret) + goto err_free_cont_rdp; + +#ifdef notyet + qwz_hal_register_srng_key(sc); +#endif + + return 0; +err_free_cont_rdp: + qwz_hal_free_cont_rdp(sc); + +err_hal: + return ret; +} + +void +qwz_hal_srng_dst_hw_init(struct qwz_softc *sc, struct hal_srng *srng) +{ + struct ath12k_hal *hal = &sc->hal; + uint32_t val; + uint64_t hp_addr; + uint32_t reg_base; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; + + if (srng->flags & HAL_SRNG_FLAGS_MSI_INTR) { + sc->ops.write32(sc, + reg_base + HAL_REO1_RING_MSI1_BASE_LSB_OFFSET(sc), + srng->msi_addr); + + val = FIELD_PREP(HAL_REO1_RING_MSI1_BASE_MSB_ADDR, + ((uint64_t)srng->msi_addr >> HAL_ADDR_MSB_REG_SHIFT)) | + HAL_REO1_RING_MSI1_BASE_MSB_MSI1_ENABLE; + sc->ops.write32(sc, + reg_base + HAL_REO1_RING_MSI1_BASE_MSB_OFFSET(sc), val); + + sc->ops.write32(sc, + reg_base + HAL_REO1_RING_MSI1_DATA_OFFSET(sc), + srng->msi_data); + } + + sc->ops.write32(sc, reg_base, srng->ring_base_paddr); + + val = FIELD_PREP(HAL_REO1_RING_BASE_MSB_RING_BASE_ADDR_MSB, + ((uint64_t)srng->ring_base_paddr >> HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_REO1_RING_BASE_MSB_RING_SIZE, + (srng->entry_size * srng->num_entries)); + sc->ops.write32(sc, + reg_base + HAL_REO1_RING_BASE_MSB_OFFSET(sc), val); + + val = FIELD_PREP(HAL_REO1_RING_ID_RING_ID, srng->ring_id) | + FIELD_PREP(HAL_REO1_RING_ID_ENTRY_SIZE, srng->entry_size); + sc->ops.write32(sc, reg_base + HAL_REO1_RING_ID_OFFSET(sc), val); + + /* interrupt setup */ + val = FIELD_PREP(HAL_REO1_RING_PRDR_INT_SETUP_INTR_TMR_THOLD, + (srng->intr_timer_thres_us >> 3)); + + val |= FIELD_PREP(HAL_REO1_RING_PRDR_INT_SETUP_BATCH_COUNTER_THOLD, + (srng->intr_batch_cntr_thres_entries * srng->entry_size)); + + sc->ops.write32(sc, + reg_base + HAL_REO1_RING_PRODUCER_INT_SETUP_OFFSET(sc), val); + + hp_addr = hal->rdp.paddr + ((unsigned long)srng->u.dst_ring.hp_addr - + (unsigned long)hal->rdp.vaddr); + sc->ops.write32(sc, reg_base + HAL_REO1_RING_HP_ADDR_LSB_OFFSET(sc), + hp_addr & HAL_ADDR_LSB_REG_MASK); + sc->ops.write32(sc, reg_base + HAL_REO1_RING_HP_ADDR_MSB_OFFSET(sc), + hp_addr >> HAL_ADDR_MSB_REG_SHIFT); + + /* Initialize head and tail pointers to indicate ring is empty */ + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R2]; + sc->ops.write32(sc, reg_base, 0); + sc->ops.write32(sc, reg_base + HAL_REO1_RING_TP_OFFSET(sc), 0); + *srng->u.dst_ring.hp_addr = 0; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; + val = 0; + if (srng->flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP) + val |= HAL_REO1_RING_MISC_DATA_TLV_SWAP; + if (srng->flags & HAL_SRNG_FLAGS_RING_PTR_SWAP) + val |= HAL_REO1_RING_MISC_HOST_FW_SWAP; + if (srng->flags & HAL_SRNG_FLAGS_MSI_SWAP) + val |= HAL_REO1_RING_MISC_MSI_SWAP; + val |= HAL_REO1_RING_MISC_SRNG_ENABLE; + + sc->ops.write32(sc, reg_base + HAL_REO1_RING_MISC_OFFSET(sc), val); +} + +void +qwz_hal_srng_src_hw_init(struct qwz_softc *sc, struct hal_srng *srng) +{ + struct ath12k_hal *hal = &sc->hal; + uint32_t val; + uint64_t tp_addr; + uint32_t reg_base; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; + + if (srng->flags & HAL_SRNG_FLAGS_MSI_INTR) { + sc->ops.write32(sc, + reg_base + HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET(sc), + srng->msi_addr); + + val = FIELD_PREP(HAL_TCL1_RING_MSI1_BASE_MSB_ADDR, + ((uint64_t)srng->msi_addr >> HAL_ADDR_MSB_REG_SHIFT)) | + HAL_TCL1_RING_MSI1_BASE_MSB_MSI1_ENABLE; + sc->ops.write32(sc, + reg_base + HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET(sc), + val); + + sc->ops.write32(sc, + reg_base + HAL_TCL1_RING_MSI1_DATA_OFFSET(sc), + srng->msi_data); + } + + sc->ops.write32(sc, reg_base, srng->ring_base_paddr); + + val = FIELD_PREP(HAL_TCL1_RING_BASE_MSB_RING_BASE_ADDR_MSB, + ((uint64_t)srng->ring_base_paddr >> HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_TCL1_RING_BASE_MSB_RING_SIZE, + (srng->entry_size * srng->num_entries)); + sc->ops.write32(sc, reg_base + HAL_TCL1_RING_BASE_MSB_OFFSET(sc), val); + + val = FIELD_PREP(HAL_REO1_RING_ID_ENTRY_SIZE, srng->entry_size); + sc->ops.write32(sc, reg_base + HAL_TCL1_RING_ID_OFFSET(sc), val); + + if (srng->ring_id == HAL_SRNG_RING_ID_WBM_IDLE_LINK) { + sc->ops.write32(sc, reg_base, (uint32_t)srng->ring_base_paddr); + val = FIELD_PREP(HAL_TCL1_RING_BASE_MSB_RING_BASE_ADDR_MSB, + ((uint64_t)srng->ring_base_paddr >> + HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_TCL1_RING_BASE_MSB_RING_SIZE, + (srng->entry_size * srng->num_entries)); + sc->ops.write32(sc, + reg_base + HAL_TCL1_RING_BASE_MSB_OFFSET(sc), val); + } + + /* interrupt setup */ + /* NOTE: IPQ8074 v2 requires the interrupt timer threshold in the + * unit of 8 usecs instead of 1 usec (as required by v1). + */ + val = FIELD_PREP(HAL_TCL1_RING_CONSR_INT_SETUP_IX0_INTR_TMR_THOLD, + srng->intr_timer_thres_us); + + val |= FIELD_PREP(HAL_TCL1_RING_CONSR_INT_SETUP_IX0_BATCH_COUNTER_THOLD, + (srng->intr_batch_cntr_thres_entries * srng->entry_size)); + + sc->ops.write32(sc, + reg_base + HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET(sc), val); + + val = 0; + if (srng->flags & HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN) { + val |= FIELD_PREP(HAL_TCL1_RING_CONSR_INT_SETUP_IX1_LOW_THOLD, + srng->u.src_ring.low_threshold); + } + sc->ops.write32(sc, + reg_base + HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET(sc), val); + + if (srng->ring_id != HAL_SRNG_RING_ID_WBM_IDLE_LINK) { + tp_addr = hal->rdp.paddr + + ((unsigned long)srng->u.src_ring.tp_addr - + (unsigned long)hal->rdp.vaddr); + sc->ops.write32(sc, + reg_base + HAL_TCL1_RING_TP_ADDR_LSB_OFFSET(sc), + tp_addr & HAL_ADDR_LSB_REG_MASK); + sc->ops.write32(sc, + reg_base + HAL_TCL1_RING_TP_ADDR_MSB_OFFSET(sc), + tp_addr >> HAL_ADDR_MSB_REG_SHIFT); + } + + /* Initialize head and tail pointers to indicate ring is empty */ + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R2]; + sc->ops.write32(sc, reg_base, 0); + sc->ops.write32(sc, reg_base + HAL_TCL1_RING_TP_OFFSET, 0); + *srng->u.src_ring.tp_addr = 0; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; + val = 0; + if (srng->flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP) + val |= HAL_TCL1_RING_MISC_DATA_TLV_SWAP; + if (srng->flags & HAL_SRNG_FLAGS_RING_PTR_SWAP) + val |= HAL_TCL1_RING_MISC_HOST_FW_SWAP; + if (srng->flags & HAL_SRNG_FLAGS_MSI_SWAP) + val |= HAL_TCL1_RING_MISC_MSI_SWAP; + + /* Loop count is not used for SRC rings */ + val |= HAL_TCL1_RING_MISC_MSI_LOOPCNT_DISABLE; + + val |= HAL_TCL1_RING_MISC_SRNG_ENABLE; + + sc->ops.write32(sc, reg_base + HAL_TCL1_RING_MISC_OFFSET(sc), val); +} + +void +qwz_hal_srng_hw_init(struct qwz_softc *sc, struct hal_srng *srng) +{ + if (srng->ring_dir == HAL_SRNG_DIR_SRC) + qwz_hal_srng_src_hw_init(sc, srng); + else + qwz_hal_srng_dst_hw_init(sc, srng); +} + +void +qwz_hal_ce_dst_setup(struct qwz_softc *sc, struct hal_srng *srng, int ring_num) +{ + struct hal_srng_config *srng_config = &sc->hal.srng_config[HAL_CE_DST]; + uint32_t addr; + uint32_t val; + + addr = HAL_CE_DST_RING_CTRL + + srng_config->reg_start[HAL_SRNG_REG_GRP_R0] + + ring_num * srng_config->reg_size[HAL_SRNG_REG_GRP_R0]; + + val = sc->ops.read32(sc, addr); + val &= ~HAL_CE_DST_R0_DEST_CTRL_MAX_LEN; + val |= FIELD_PREP(HAL_CE_DST_R0_DEST_CTRL_MAX_LEN, + srng->u.dst_ring.max_buffer_length); + sc->ops.write32(sc, addr, val); +} + +void +qwz_hal_ce_src_set_desc(void *buf, uint64_t paddr, uint32_t len, uint32_t id, + uint8_t byte_swap_data) +{ + struct hal_ce_srng_src_desc *desc = (struct hal_ce_srng_src_desc *)buf; + + desc->buffer_addr_low = paddr & HAL_ADDR_LSB_REG_MASK; + desc->buffer_addr_info = FIELD_PREP(HAL_CE_SRC_DESC_ADDR_INFO_ADDR_HI, + (paddr >> HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_CE_SRC_DESC_ADDR_INFO_BYTE_SWAP, + byte_swap_data) | + FIELD_PREP(HAL_CE_SRC_DESC_ADDR_INFO_GATHER, 0) | + FIELD_PREP(HAL_CE_SRC_DESC_ADDR_INFO_LEN, len); + desc->meta_info = FIELD_PREP(HAL_CE_SRC_DESC_META_INFO_DATA, id); +} + +void +qwz_hal_ce_dst_set_desc(void *buf, uint64_t paddr) +{ + struct hal_ce_srng_dest_desc *desc = + (struct hal_ce_srng_dest_desc *)buf; + + desc->buffer_addr_low = htole32(paddr & HAL_ADDR_LSB_REG_MASK); + desc->buffer_addr_info = htole32(FIELD_PREP( + HAL_CE_DEST_DESC_ADDR_INFO_ADDR_HI, + (paddr >> HAL_ADDR_MSB_REG_SHIFT))); +} + +uint32_t +qwz_hal_ce_dst_status_get_length(void *buf) +{ + struct hal_ce_srng_dst_status_desc *desc = + (struct hal_ce_srng_dst_status_desc *)buf; + uint32_t len; + + len = FIELD_GET(HAL_CE_DST_STATUS_DESC_FLAGS_LEN, desc->flags); + desc->flags &= ~HAL_CE_DST_STATUS_DESC_FLAGS_LEN; + + return len; +} + + +int +qwz_hal_srng_setup(struct qwz_softc *sc, enum hal_ring_type type, + int ring_num, int mac_id, struct hal_srng_params *params) +{ + struct ath12k_hal *hal = &sc->hal; + struct hal_srng_config *srng_config = &sc->hal.srng_config[type]; + struct hal_srng *srng; + int ring_id; + uint32_t lmac_idx; + int i; + uint32_t reg_base; + + ring_id = qwz_hal_srng_get_ring_id(sc, type, ring_num, mac_id); + if (ring_id < 0) + return ring_id; + + srng = &hal->srng_list[ring_id]; + + srng->ring_id = ring_id; + srng->ring_dir = srng_config->ring_dir; + srng->ring_base_paddr = params->ring_base_paddr; + srng->ring_base_vaddr = params->ring_base_vaddr; + srng->entry_size = srng_config->entry_size; + srng->num_entries = params->num_entries; + srng->ring_size = srng->entry_size * srng->num_entries; + srng->intr_batch_cntr_thres_entries = + params->intr_batch_cntr_thres_entries; + srng->intr_timer_thres_us = params->intr_timer_thres_us; + srng->flags = params->flags; + srng->msi_addr = params->msi_addr; + srng->msi_data = params->msi_data; + srng->initialized = 1; +#if 0 + spin_lock_init(&srng->lock); + lockdep_set_class(&srng->lock, hal->srng_key + ring_id); +#endif + + for (i = 0; i < HAL_SRNG_NUM_REG_GRP; i++) { + srng->hwreg_base[i] = srng_config->reg_start[i] + + (ring_num * srng_config->reg_size[i]); + } + + memset(srng->ring_base_vaddr, 0, + (srng->entry_size * srng->num_entries) << 2); + +#if 0 /* Not needed on OpenBSD? We do swapping in sofware... */ + /* TODO: Add comments on these swap configurations */ + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + srng->flags |= HAL_SRNG_FLAGS_MSI_SWAP | HAL_SRNG_FLAGS_DATA_TLV_SWAP | + HAL_SRNG_FLAGS_RING_PTR_SWAP; +#endif + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R2]; + + if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + srng->u.src_ring.hp = 0; + srng->u.src_ring.cached_tp = 0; + srng->u.src_ring.reap_hp = srng->ring_size - srng->entry_size; + srng->u.src_ring.tp_addr = (void *)(hal->rdp.vaddr + ring_id); + srng->u.src_ring.low_threshold = params->low_threshold * + srng->entry_size; + if (srng_config->lmac_ring) { + lmac_idx = ring_id - HAL_SRNG_RING_ID_LMAC1_ID_START; + srng->u.src_ring.hp_addr = (void *)(hal->wrp.vaddr + + lmac_idx); + srng->flags |= HAL_SRNG_FLAGS_LMAC_RING; + } else { + if (!sc->hw_params.supports_shadow_regs) + srng->u.src_ring.hp_addr = + (uint32_t *)((unsigned long)sc->mem + + reg_base); + else + DPRINTF("%s: type %d ring_num %d reg_base " + "0x%x shadow 0x%lx\n", + sc->sc_dev.dv_xname, type, ring_num, reg_base, + (unsigned long)srng->u.src_ring.hp_addr - + (unsigned long)sc->mem); + } + } else { + /* During initialization loop count in all the descriptors + * will be set to zero, and HW will set it to 1 on completing + * descriptor update in first loop, and increments it by 1 on + * subsequent loops (loop count wraps around after reaching + * 0xffff). The 'loop_cnt' in SW ring state is the expected + * loop count in descriptors updated by HW (to be processed + * by SW). + */ + srng->u.dst_ring.loop_cnt = 1; + srng->u.dst_ring.tp = 0; + srng->u.dst_ring.cached_hp = 0; + srng->u.dst_ring.hp_addr = (void *)(hal->rdp.vaddr + ring_id); + if (srng_config->lmac_ring) { + /* For LMAC rings, tail pointer updates will be done + * through FW by writing to a shared memory location + */ + lmac_idx = ring_id - HAL_SRNG_RING_ID_LMAC1_ID_START; + srng->u.dst_ring.tp_addr = (void *)(hal->wrp.vaddr + + lmac_idx); + srng->flags |= HAL_SRNG_FLAGS_LMAC_RING; + } else { + if (!sc->hw_params.supports_shadow_regs) + srng->u.dst_ring.tp_addr = + (uint32_t *)((unsigned long)sc->mem + + reg_base + (HAL_REO1_RING_TP(sc) - + HAL_REO1_RING_HP(sc))); + else + DPRINTF("%s: type %d ring_num %d target_reg " + "0x%x shadow 0x%lx\n", sc->sc_dev.dv_xname, + type, ring_num, + reg_base + (HAL_REO1_RING_TP(sc) - + HAL_REO1_RING_HP(sc)), + (unsigned long)srng->u.dst_ring.tp_addr - + (unsigned long)sc->mem); + } + } + + if (srng_config->lmac_ring) + return ring_id; + + qwz_hal_srng_hw_init(sc, srng); + + if (type == HAL_CE_DST) { + srng->u.dst_ring.max_buffer_length = params->max_buffer_len; + qwz_hal_ce_dst_setup(sc, srng, ring_num); + } + + return ring_id; +} + +size_t +qwz_hal_ce_get_desc_size(enum hal_ce_desc type) +{ + switch (type) { + case HAL_CE_DESC_SRC: + return sizeof(struct hal_ce_srng_src_desc); + case HAL_CE_DESC_DST: + return sizeof(struct hal_ce_srng_dest_desc); + case HAL_CE_DESC_DST_STATUS: + return sizeof(struct hal_ce_srng_dst_status_desc); + } + + return 0; +} + +void +qwz_htc_tx_completion_handler(struct qwz_softc *sc, struct mbuf *m) +{ + printf("%s: not implemented\n", __func__); +} + +struct qwz_tx_data * +qwz_ce_completed_send_next(struct qwz_ce_pipe *pipe) +{ + struct qwz_softc *sc = pipe->sc; + struct hal_srng *srng; + unsigned int sw_index; + unsigned int nentries_mask; + void *ctx; + struct qwz_tx_data *tx_data = NULL; + uint32_t *desc; +#ifdef notyet + spin_lock_bh(&ab->ce.ce_lock); +#endif + sw_index = pipe->src_ring->sw_index; + nentries_mask = pipe->src_ring->nentries_mask; + + srng = &sc->hal.srng_list[pipe->src_ring->hal_ring_id]; +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + + desc = qwz_hal_srng_src_reap_next(sc, srng); + if (!desc) + goto err_unlock; + + ctx = pipe->src_ring->per_transfer_context[sw_index]; + tx_data = (struct qwz_tx_data *)ctx; + + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + pipe->src_ring->sw_index = sw_index; + +err_unlock: +#ifdef notyet + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); +#endif + return tx_data; +} + +int +qwz_ce_tx_process_cb(struct qwz_ce_pipe *pipe) +{ + struct qwz_softc *sc = pipe->sc; + struct qwz_tx_data *tx_data; + struct mbuf *m; + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); + int ret = 0; + + while ((tx_data = qwz_ce_completed_send_next(pipe)) != NULL) { + bus_dmamap_unload(sc->sc_dmat, tx_data->map); + m = tx_data->m; + tx_data->m = NULL; + + if ((!pipe->send_cb) || sc->hw_params.credit_flow) { + m_freem(m); + continue; + } + + ml_enqueue(&ml, m); + ret = 1; + } + + while ((m = ml_dequeue(&ml))) { + DNPRINTF(QWZ_D_CE, "%s: tx ce pipe %d len %d\n", __func__, + pipe->pipe_num, m->m_len); + pipe->send_cb(sc, m); + } + + return ret; +} + +void +qwz_ce_poll_send_completed(struct qwz_softc *sc, uint8_t pipe_id) +{ + struct qwz_ce_pipe *pipe = &sc->ce.ce_pipe[pipe_id]; + const struct ce_attr *attr = &sc->hw_params.host_ce_config[pipe_id]; + + if ((pipe->attr_flags & CE_ATTR_DIS_INTR) && attr->src_nentries) + qwz_ce_tx_process_cb(pipe); +} + +void +qwz_htc_process_credit_report(struct qwz_htc *htc, + const struct ath12k_htc_credit_report *report, int len, + enum ath12k_htc_ep_id eid) +{ + struct qwz_softc *sc = htc->sc; + struct qwz_htc_ep *ep; + int i, n_reports; + + if (len % sizeof(*report)) + printf("%s: Uneven credit report len %d", __func__, len); + + n_reports = len / sizeof(*report); +#ifdef notyet + spin_lock_bh(&htc->tx_lock); +#endif + for (i = 0; i < n_reports; i++, report++) { + if (report->eid >= ATH12K_HTC_EP_COUNT) + break; + + ep = &htc->endpoint[report->eid]; + ep->tx_credits += report->credits; + + DNPRINTF(QWZ_D_HTC, "%s: ep %d credits got %d total %d\n", + __func__, report->eid, report->credits, ep->tx_credits); + + if (ep->ep_ops.ep_tx_credits) { +#ifdef notyet + spin_unlock_bh(&htc->tx_lock); +#endif + ep->ep_ops.ep_tx_credits(sc); +#ifdef notyet + spin_lock_bh(&htc->tx_lock); +#endif + } + } +#ifdef notyet + spin_unlock_bh(&htc->tx_lock); +#endif +} + +int +qwz_htc_process_trailer(struct qwz_htc *htc, uint8_t *buffer, int length, + enum ath12k_htc_ep_id src_eid) +{ + struct qwz_softc *sc = htc->sc; + int status = 0; + struct ath12k_htc_record *record; + size_t len; + + while (length > 0) { + record = (struct ath12k_htc_record *)buffer; + + if (length < sizeof(record->hdr)) { + status = EINVAL; + break; + } + + if (record->hdr.len > length) { + /* no room left in buffer for record */ + printf("%s: Invalid record length: %d\n", + __func__, record->hdr.len); + status = EINVAL; + break; + } + + if (sc->hw_params.credit_flow) { + switch (record->hdr.id) { + case ATH12K_HTC_RECORD_CREDITS: + len = sizeof(struct ath12k_htc_credit_report); + if (record->hdr.len < len) { + printf("%s: Credit report too long\n", + __func__); + status = EINVAL; + break; + } + qwz_htc_process_credit_report(htc, + record->credit_report, + record->hdr.len, src_eid); + break; + default: + printf("%s: unhandled record: id:%d length:%d\n", + __func__, record->hdr.id, record->hdr.len); + break; + } + } + + if (status) + break; + + /* multiple records may be present in a trailer */ + buffer += sizeof(record->hdr) + record->hdr.len; + length -= sizeof(record->hdr) + record->hdr.len; + } + + return status; +} + +void +qwz_htc_suspend_complete(struct qwz_softc *sc, int ack) +{ + printf("%s: not implemented\n", __func__); +} + +void +qwz_htc_wakeup_from_suspend(struct qwz_softc *sc) +{ + /* TODO This is really all the Linux driver does here... silence it? */ + printf("%s: wakeup from suspend received\n", __func__); +} + +void +qwz_htc_rx_completion_handler(struct qwz_softc *sc, struct mbuf *m) +{ + struct qwz_htc *htc = &sc->htc; + struct ath12k_htc_hdr *hdr; + struct qwz_htc_ep *ep; + uint16_t payload_len; + uint32_t message_id, trailer_len = 0; + uint8_t eid; + int trailer_present; + + m = m_pullup(m, sizeof(struct ath12k_htc_hdr)); + if (m == NULL) { + printf("%s: m_pullup failed\n", __func__); + m = NULL; /* already freed */ + goto out; + } + + hdr = mtod(m, struct ath12k_htc_hdr *); + + eid = FIELD_GET(HTC_HDR_ENDPOINTID, hdr->htc_info); + + if (eid >= ATH12K_HTC_EP_COUNT) { + printf("%s: HTC Rx: invalid eid %d\n", __func__, eid); + printf("%s: HTC info: 0x%x\n", __func__, hdr->htc_info); + printf("%s: CTRL info: 0x%x\n", __func__, hdr->ctrl_info); + goto out; + } + + ep = &htc->endpoint[eid]; + + payload_len = FIELD_GET(HTC_HDR_PAYLOADLEN, hdr->htc_info); + + if (payload_len + sizeof(*hdr) > ATH12K_HTC_MAX_LEN) { + printf("%s: HTC rx frame too long, len: %zu\n", __func__, + payload_len + sizeof(*hdr)); + goto out; + } + + if (m->m_pkthdr.len < payload_len) { + printf("%s: HTC Rx: insufficient length, got %d, " + "expected %d\n", __func__, m->m_pkthdr.len, payload_len); + goto out; + } + + /* get flags to check for trailer */ + trailer_present = (FIELD_GET(HTC_HDR_FLAGS, hdr->htc_info)) & + ATH12K_HTC_FLAG_TRAILER_PRESENT; + + DNPRINTF(QWZ_D_HTC, "%s: rx ep %d mbuf %p trailer_present %d\n", + __func__, eid, m, trailer_present); + + if (trailer_present) { + int status = 0; + uint8_t *trailer; + int trim; + size_t min_len; + + trailer_len = FIELD_GET(HTC_HDR_CONTROLBYTES0, hdr->ctrl_info); + min_len = sizeof(struct ath12k_htc_record_hdr); + + if ((trailer_len < min_len) || + (trailer_len > payload_len)) { + printf("%s: Invalid trailer length: %d\n", __func__, + trailer_len); + goto out; + } + + trailer = (uint8_t *)hdr; + trailer += sizeof(*hdr); + trailer += payload_len; + trailer -= trailer_len; + status = qwz_htc_process_trailer(htc, trailer, + trailer_len, eid); + if (status) + goto out; + + trim = trailer_len; + m_adj(m, -trim); + } + + if (trailer_len >= payload_len) + /* zero length packet with trailer data, just drop these */ + goto out; + + m_adj(m, sizeof(*hdr)); + + if (eid == ATH12K_HTC_EP_0) { + struct ath12k_htc_msg *msg; + + msg = mtod(m, struct ath12k_htc_msg *); + message_id = FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id); + + DNPRINTF(QWZ_D_HTC, "%s: rx ep %d mbuf %p message_id %d\n", + __func__, eid, m, message_id); + + switch (message_id) { + case ATH12K_HTC_MSG_READY_ID: + case ATH12K_HTC_MSG_CONNECT_SERVICE_RESP_ID: + /* handle HTC control message */ + if (sc->ctl_resp) { + /* this is a fatal error, target should not be + * sending unsolicited messages on the ep 0 + */ + printf("%s: HTC rx ctrl still processing\n", + __func__); + goto out; + } + + htc->control_resp_len = + MIN(m->m_pkthdr.len, ATH12K_HTC_MAX_CTRL_MSG_LEN); + + m_copydata(m, 0, htc->control_resp_len, + htc->control_resp_buffer); + + sc->ctl_resp = 1; + wakeup(&sc->ctl_resp); + break; + case ATH12K_HTC_MSG_SEND_SUSPEND_COMPLETE: + qwz_htc_suspend_complete(sc, 1); + break; + case ATH12K_HTC_MSG_NACK_SUSPEND: + qwz_htc_suspend_complete(sc, 0); + break; + case ATH12K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID: + qwz_htc_wakeup_from_suspend(sc); + break; + default: + printf("%s: ignoring unsolicited htc ep0 event %ld\n", + __func__, + FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id)); + break; + } + goto out; + } + + DNPRINTF(QWZ_D_HTC, "%s: rx ep %d mbuf %p\n", __func__, eid, m); + + ep->ep_ops.ep_rx_complete(sc, m); + + /* poll tx completion for interrupt disabled CE's */ + qwz_ce_poll_send_completed(sc, ep->ul_pipe_id); + + /* mbuf is now owned by the rx completion handler */ + m = NULL; +out: + m_freem(m); +} + +void +qwz_ce_free_ring(struct qwz_softc *sc, struct qwz_ce_ring *ring) +{ + bus_size_t dsize; + size_t size; + + if (ring == NULL) + return; + + if (ring->base_addr) { + dsize = ring->nentries * ring->desc_sz; + bus_dmamem_unmap(sc->sc_dmat, ring->base_addr, dsize); + } + if (ring->nsegs) + bus_dmamem_free(sc->sc_dmat, &ring->dsegs, ring->nsegs); + if (ring->dmap) + bus_dmamap_destroy(sc->sc_dmat, ring->dmap); + + size = sizeof(*ring) + (ring->nentries * + sizeof(ring->per_transfer_context[0])); + free(ring, M_DEVBUF, size); +} + +static inline int +qwz_ce_need_shadow_fix(int ce_id) +{ + /* only ce4 needs shadow workaround */ + return (ce_id == 4); +} + +void +qwz_ce_stop_shadow_timers(struct qwz_softc *sc) +{ + int i; + + if (!sc->hw_params.supports_shadow_regs) + return; + + for (i = 0; i < sc->hw_params.ce_count; i++) + if (qwz_ce_need_shadow_fix(i)) + qwz_dp_shadow_stop_timer(sc, &sc->ce.hp_timer[i]); +} + +void +qwz_ce_free_pipes(struct qwz_softc *sc) +{ + struct qwz_ce_pipe *pipe; + int i; + + for (i = 0; i < sc->hw_params.ce_count; i++) { + pipe = &sc->ce.ce_pipe[i]; + if (qwz_ce_need_shadow_fix(i)) + qwz_dp_shadow_stop_timer(sc, &sc->ce.hp_timer[i]); + if (pipe->src_ring) { + qwz_ce_free_ring(sc, pipe->src_ring); + pipe->src_ring = NULL; + } + + if (pipe->dest_ring) { + qwz_ce_free_ring(sc, pipe->dest_ring); + pipe->dest_ring = NULL; + } + + if (pipe->status_ring) { + qwz_ce_free_ring(sc, pipe->status_ring); + pipe->status_ring = NULL; + } + } +} + +int +qwz_ce_alloc_src_ring_transfer_contexts(struct qwz_ce_pipe *pipe, + const struct ce_attr *attr) +{ + struct qwz_softc *sc = pipe->sc; + struct qwz_tx_data *txdata; + size_t size; + int ret, i; + + /* Allocate an array of qwz_tx_data structures. */ + txdata = mallocarray(pipe->src_ring->nentries, sizeof(*txdata), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (txdata == NULL) + return ENOMEM; + + size = sizeof(*txdata) * pipe->src_ring->nentries; + + /* Create per-transfer DMA maps. */ + for (i = 0; i < pipe->src_ring->nentries; i++) { + struct qwz_tx_data *ctx = &txdata[i]; + ret = bus_dmamap_create(sc->sc_dmat, attr->src_sz_max, 1, + attr->src_sz_max, 0, BUS_DMA_NOWAIT, &ctx->map); + if (ret) { + int j; + for (j = 0; j < i; j++) { + struct qwz_tx_data *ctx = &txdata[j]; + bus_dmamap_destroy(sc->sc_dmat, ctx->map); + } + free(txdata, M_DEVBUF, size); + return ret; + } + pipe->src_ring->per_transfer_context[i] = ctx; + } + + return 0; +} + +int +qwz_ce_alloc_dest_ring_transfer_contexts(struct qwz_ce_pipe *pipe, + const struct ce_attr *attr) +{ + struct qwz_softc *sc = pipe->sc; + struct qwz_rx_data *rxdata; + size_t size; + int ret, i; + + /* Allocate an array of qwz_rx_data structures. */ + rxdata = mallocarray(pipe->dest_ring->nentries, sizeof(*rxdata), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (rxdata == NULL) + return ENOMEM; + + size = sizeof(*rxdata) * pipe->dest_ring->nentries; + + /* Create per-transfer DMA maps. */ + for (i = 0; i < pipe->dest_ring->nentries; i++) { + struct qwz_rx_data *ctx = &rxdata[i]; + ret = bus_dmamap_create(sc->sc_dmat, attr->src_sz_max, 1, + attr->src_sz_max, 0, BUS_DMA_NOWAIT, &ctx->map); + if (ret) { + int j; + for (j = 0; j < i; j++) { + struct qwz_rx_data *ctx = &rxdata[j]; + bus_dmamap_destroy(sc->sc_dmat, ctx->map); + } + free(rxdata, M_DEVBUF, size); + return ret; + } + pipe->dest_ring->per_transfer_context[i] = ctx; + } + + return 0; +} + +struct qwz_ce_ring * +qwz_ce_alloc_ring(struct qwz_softc *sc, int nentries, size_t desc_sz) +{ + struct qwz_ce_ring *ce_ring; + size_t size = sizeof(*ce_ring) + + (nentries * sizeof(ce_ring->per_transfer_context[0])); + bus_size_t dsize; + + ce_ring = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); + if (ce_ring == NULL) + return NULL; + + ce_ring->nentries = nentries; + ce_ring->nentries_mask = nentries - 1; + ce_ring->desc_sz = desc_sz; + + dsize = nentries * desc_sz; + if (bus_dmamap_create(sc->sc_dmat, dsize, 1, dsize, 0, BUS_DMA_NOWAIT, + &ce_ring->dmap)) { + free(ce_ring, M_DEVBUF, size); + return NULL; + } + + if (bus_dmamem_alloc(sc->sc_dmat, dsize, CE_DESC_RING_ALIGN, 0, + &ce_ring->dsegs, 1, &ce_ring->nsegs, + BUS_DMA_NOWAIT | BUS_DMA_ZERO)) { + qwz_ce_free_ring(sc, ce_ring); + return NULL; + } + + if (bus_dmamem_map(sc->sc_dmat, &ce_ring->dsegs, 1, dsize, + &ce_ring->base_addr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) { + qwz_ce_free_ring(sc, ce_ring); + return NULL; + } + + if (bus_dmamap_load(sc->sc_dmat, ce_ring->dmap, ce_ring->base_addr, + dsize, NULL, BUS_DMA_NOWAIT)) { + qwz_ce_free_ring(sc, ce_ring); + return NULL; + } + + return ce_ring; +} + +int +qwz_ce_alloc_pipe(struct qwz_softc *sc, int ce_id) +{ + struct qwz_ce_pipe *pipe = &sc->ce.ce_pipe[ce_id]; + const struct ce_attr *attr = &sc->hw_params.host_ce_config[ce_id]; + struct qwz_ce_ring *ring; + int nentries; + size_t desc_sz; + + pipe->attr_flags = attr->flags; + + if (attr->src_nentries) { + pipe->send_cb = attr->send_cb; + nentries = qwz_roundup_pow_of_two(attr->src_nentries); + desc_sz = qwz_hal_ce_get_desc_size(HAL_CE_DESC_SRC); + ring = qwz_ce_alloc_ring(sc, nentries, desc_sz); + if (ring == NULL) + return ENOMEM; + pipe->src_ring = ring; + if (qwz_ce_alloc_src_ring_transfer_contexts(pipe, attr)) + return ENOMEM; + } + + if (attr->dest_nentries) { + pipe->recv_cb = attr->recv_cb; + nentries = qwz_roundup_pow_of_two(attr->dest_nentries); + desc_sz = qwz_hal_ce_get_desc_size(HAL_CE_DESC_DST); + ring = qwz_ce_alloc_ring(sc, nentries, desc_sz); + if (ring == NULL) + return ENOMEM; + pipe->dest_ring = ring; + if (qwz_ce_alloc_dest_ring_transfer_contexts(pipe, attr)) + return ENOMEM; + + desc_sz = qwz_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS); + ring = qwz_ce_alloc_ring(sc, nentries, desc_sz); + if (ring == NULL) + return ENOMEM; + pipe->status_ring = ring; + } + + return 0; +} + +void +qwz_ce_rx_pipe_cleanup(struct qwz_ce_pipe *pipe) +{ + struct qwz_softc *sc = pipe->sc; + struct qwz_ce_ring *ring = pipe->dest_ring; + void *ctx; + struct qwz_rx_data *rx_data; + int i; + + if (!(ring && pipe->buf_sz)) + return; + + for (i = 0; i < ring->nentries; i++) { + ctx = ring->per_transfer_context[i]; + if (!ctx) + continue; + + rx_data = (struct qwz_rx_data *)ctx; + if (rx_data->m) { + bus_dmamap_unload(sc->sc_dmat, rx_data->map); + m_freem(rx_data->m); + rx_data->m = NULL; + } + } +} + +void +qwz_ce_shadow_config(struct qwz_softc *sc) +{ + int i; + + for (i = 0; i < sc->hw_params.ce_count; i++) { + if (sc->hw_params.host_ce_config[i].src_nentries) + qwz_hal_srng_update_shadow_config(sc, HAL_CE_SRC, i); + + if (sc->hw_params.host_ce_config[i].dest_nentries) { + qwz_hal_srng_update_shadow_config(sc, HAL_CE_DST, i); + + qwz_hal_srng_update_shadow_config(sc, + HAL_CE_DST_STATUS, i); + } + } +} + +void +qwz_ce_get_shadow_config(struct qwz_softc *sc, uint32_t **shadow_cfg, + uint32_t *shadow_cfg_len) +{ + if (!sc->hw_params.supports_shadow_regs) + return; + + qwz_hal_srng_get_shadow_config(sc, shadow_cfg, shadow_cfg_len); + + /* shadow is already configured */ + if (*shadow_cfg_len) + return; + + /* shadow isn't configured yet, configure now. + * non-CE srngs are configured firstly, then + * all CE srngs. + */ + qwz_hal_srng_shadow_config(sc); + qwz_ce_shadow_config(sc); + + /* get the shadow configuration */ + qwz_hal_srng_get_shadow_config(sc, shadow_cfg, shadow_cfg_len); +} + +void +qwz_ce_cleanup_pipes(struct qwz_softc *sc) +{ + struct qwz_ce_pipe *pipe; + int pipe_num; + + qwz_ce_stop_shadow_timers(sc); + + for (pipe_num = 0; pipe_num < sc->hw_params.ce_count; pipe_num++) { + pipe = &sc->ce.ce_pipe[pipe_num]; + qwz_ce_rx_pipe_cleanup(pipe); + + /* Cleanup any src CE's which have interrupts disabled */ + qwz_ce_poll_send_completed(sc, pipe_num); + } +} + +int +qwz_ce_alloc_pipes(struct qwz_softc *sc) +{ + struct qwz_ce_pipe *pipe; + int i; + int ret; + const struct ce_attr *attr; + + for (i = 0; i < sc->hw_params.ce_count; i++) { + attr = &sc->hw_params.host_ce_config[i]; + pipe = &sc->ce.ce_pipe[i]; + pipe->pipe_num = i; + pipe->sc = sc; + pipe->buf_sz = attr->src_sz_max; + + ret = qwz_ce_alloc_pipe(sc, i); + if (ret) { + /* Free any partial successful allocation */ + qwz_ce_free_pipes(sc); + return ret; + } + } + + return 0; +} + +void +qwz_get_ce_msi_idx(struct qwz_softc *sc, uint32_t ce_id, + uint32_t *msi_data_idx) +{ + *msi_data_idx = ce_id; +} + +void +qwz_ce_srng_msi_ring_params_setup(struct qwz_softc *sc, uint32_t ce_id, + struct hal_srng_params *ring_params) +{ + uint32_t msi_data_start = 0; + uint32_t msi_data_count = 1, msi_data_idx; + uint32_t msi_irq_start = 0; + uint32_t addr_lo; + uint32_t addr_hi; + int ret; + + ret = sc->ops.get_user_msi_vector(sc, "CE", + &msi_data_count, &msi_data_start, &msi_irq_start); + if (ret) + return; + + qwz_get_msi_address(sc, &addr_lo, &addr_hi); + qwz_get_ce_msi_idx(sc, ce_id, &msi_data_idx); + + ring_params->msi_addr = addr_lo; + ring_params->msi_addr |= (((uint64_t)addr_hi) << 32); + ring_params->msi_data = (msi_data_idx % msi_data_count) + msi_data_start; + ring_params->flags |= HAL_SRNG_FLAGS_MSI_INTR; +} + +int +qwz_ce_init_ring(struct qwz_softc *sc, struct qwz_ce_ring *ce_ring, + int ce_id, enum hal_ring_type type) +{ + struct hal_srng_params params = { 0 }; + int ret; + + params.ring_base_paddr = ce_ring->dmap->dm_segs[0].ds_addr; + params.ring_base_vaddr = (uint32_t *)ce_ring->base_addr; + params.num_entries = ce_ring->nentries; + + if (!(CE_ATTR_DIS_INTR & sc->hw_params.host_ce_config[ce_id].flags)) + qwz_ce_srng_msi_ring_params_setup(sc, ce_id, ¶ms); + + switch (type) { + case HAL_CE_SRC: + if (!(CE_ATTR_DIS_INTR & + sc->hw_params.host_ce_config[ce_id].flags)) + params.intr_batch_cntr_thres_entries = 1; + break; + case HAL_CE_DST: + params.max_buffer_len = + sc->hw_params.host_ce_config[ce_id].src_sz_max; + if (!(sc->hw_params.host_ce_config[ce_id].flags & + CE_ATTR_DIS_INTR)) { + params.intr_timer_thres_us = 1024; + params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; + params.low_threshold = ce_ring->nentries - 3; + } + break; + case HAL_CE_DST_STATUS: + if (!(sc->hw_params.host_ce_config[ce_id].flags & + CE_ATTR_DIS_INTR)) { + params.intr_batch_cntr_thres_entries = 1; + params.intr_timer_thres_us = 0x1000; + } + break; + default: + printf("%s: Invalid CE ring type %d\n", + sc->sc_dev.dv_xname, type); + return EINVAL; + } + + /* TODO: Init other params needed by HAL to init the ring */ + + ret = qwz_hal_srng_setup(sc, type, ce_id, 0, ¶ms); + if (ret < 0) { + printf("%s: failed to setup srng: ring_id %d ce_id %d\n", + sc->sc_dev.dv_xname, ret, ce_id); + return ret; + } + + ce_ring->hal_ring_id = ret; + + if (sc->hw_params.supports_shadow_regs && + qwz_ce_need_shadow_fix(ce_id)) + qwz_dp_shadow_init_timer(sc, &sc->ce.hp_timer[ce_id], + ATH12K_SHADOW_CTRL_TIMER_INTERVAL, ce_ring->hal_ring_id); + + return 0; +} + +int +qwz_ce_init_pipes(struct qwz_softc *sc) +{ + struct qwz_ce_pipe *pipe; + int i; + int ret; + + for (i = 0; i < sc->hw_params.ce_count; i++) { + pipe = &sc->ce.ce_pipe[i]; + + if (pipe->src_ring) { + ret = qwz_ce_init_ring(sc, pipe->src_ring, i, + HAL_CE_SRC); + if (ret) { + printf("%s: failed to init src ring: %d\n", + sc->sc_dev.dv_xname, ret); + /* Should we clear any partial init */ + return ret; + } + + pipe->src_ring->write_index = 0; + pipe->src_ring->sw_index = 0; + } + + if (pipe->dest_ring) { + ret = qwz_ce_init_ring(sc, pipe->dest_ring, i, + HAL_CE_DST); + if (ret) { + printf("%s: failed to init dest ring: %d\n", + sc->sc_dev.dv_xname, ret); + /* Should we clear any partial init */ + return ret; + } + + pipe->rx_buf_needed = pipe->dest_ring->nentries ? + pipe->dest_ring->nentries - 2 : 0; + + pipe->dest_ring->write_index = 0; + pipe->dest_ring->sw_index = 0; + } + + if (pipe->status_ring) { + ret = qwz_ce_init_ring(sc, pipe->status_ring, i, + HAL_CE_DST_STATUS); + if (ret) { + printf("%s: failed to init status ring: %d\n", + sc->sc_dev.dv_xname, ret); + /* Should we clear any partial init */ + return ret; + } + + pipe->status_ring->write_index = 0; + pipe->status_ring->sw_index = 0; + } + } + + return 0; +} + +int +qwz_hal_srng_src_num_free(struct qwz_softc *sc, struct hal_srng *srng, + int sync_hw_ptr) +{ + uint32_t tp, hp; +#ifdef notyet + lockdep_assert_held(&srng->lock); +#endif + hp = srng->u.src_ring.hp; + + if (sync_hw_ptr) { + tp = *srng->u.src_ring.tp_addr; + srng->u.src_ring.cached_tp = tp; + } else { + tp = srng->u.src_ring.cached_tp; + } + + if (tp > hp) + return ((tp - hp) / srng->entry_size) - 1; + else + return ((srng->ring_size - hp + tp) / srng->entry_size) - 1; +} + +int +qwz_ce_rx_buf_enqueue_pipe(struct qwz_ce_pipe *pipe, bus_dmamap_t map) +{ + struct qwz_softc *sc = pipe->sc; + struct qwz_ce_ring *ring = pipe->dest_ring; + struct hal_srng *srng; + unsigned int write_index; + unsigned int nentries_mask = ring->nentries_mask; + uint32_t *desc; + uint64_t paddr; + int ret; +#ifdef notyet + lockdep_assert_held(&ab->ce.ce_lock); +#endif + write_index = ring->write_index; + + srng = &sc->hal.srng_list[ring->hal_ring_id]; +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + bus_dmamap_sync(sc->sc_dmat, map, 0, + srng->entry_size * sizeof(uint32_t), BUS_DMASYNC_POSTREAD); + + if (qwz_hal_srng_src_num_free(sc, srng, 0) < 1) { + ret = ENOSPC; + goto exit; + } + + desc = qwz_hal_srng_src_get_next_entry(sc, srng); + if (!desc) { + ret = ENOSPC; + goto exit; + } + + paddr = map->dm_segs[0].ds_addr; + qwz_hal_ce_dst_set_desc(desc, paddr); + + write_index = CE_RING_IDX_INCR(nentries_mask, write_index); + ring->write_index = write_index; + + pipe->rx_buf_needed--; + + ret = 0; +exit: + qwz_hal_srng_access_end(sc, srng); + bus_dmamap_sync(sc->sc_dmat, map, 0, + srng->entry_size * sizeof(uint32_t), BUS_DMASYNC_PREREAD); +#ifdef notyet + spin_unlock_bh(&srng->lock); +#endif + return ret; +} + +int +qwz_ce_rx_post_pipe(struct qwz_ce_pipe *pipe) +{ + struct qwz_softc *sc = pipe->sc; + int ret = 0; + unsigned int idx; + void *ctx; + struct qwz_rx_data *rx_data; + struct mbuf *m; + + if (!pipe->dest_ring) + return 0; + +#ifdef notyet + spin_lock_bh(&ab->ce.ce_lock); +#endif + while (pipe->rx_buf_needed) { + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) { + ret = ENOBUFS; + goto done; + } + + if (pipe->buf_sz <= MCLBYTES) + MCLGET(m, M_DONTWAIT); + else + MCLGETL(m, M_DONTWAIT, pipe->buf_sz); + if ((m->m_flags & M_EXT) == 0) { + ret = ENOBUFS; + goto done; + } + + idx = pipe->dest_ring->write_index; + ctx = pipe->dest_ring->per_transfer_context[idx]; + rx_data = (struct qwz_rx_data *)ctx; + + m->m_len = m->m_pkthdr.len = pipe->buf_sz; + ret = bus_dmamap_load_mbuf(sc->sc_dmat, rx_data->map, + m, BUS_DMA_READ | BUS_DMA_NOWAIT); + if (ret) { + printf("%s: can't map mbuf (error %d)\n", + sc->sc_dev.dv_xname, ret); + m_freem(m); + goto done; + } + + ret = qwz_ce_rx_buf_enqueue_pipe(pipe, rx_data->map); + if (ret) { + printf("%s: failed to enqueue rx buf: %d\n", + sc->sc_dev.dv_xname, ret); + bus_dmamap_unload(sc->sc_dmat, rx_data->map); + m_freem(m); + break; + } else + rx_data->m = m; + } + +done: +#ifdef notyet + spin_unlock_bh(&ab->ce.ce_lock); +#endif + return ret; +} + +void +qwz_ce_rx_post_buf(struct qwz_softc *sc) +{ + struct qwz_ce_pipe *pipe; + int i; + int ret; + + for (i = 0; i < sc->hw_params.ce_count; i++) { + pipe = &sc->ce.ce_pipe[i]; + ret = qwz_ce_rx_post_pipe(pipe); + if (ret) { + if (ret == ENOSPC) + continue; + + printf("%s: failed to post rx buf to pipe: %d err: %d\n", + sc->sc_dev.dv_xname, i, ret); +#ifdef notyet + mod_timer(&ab->rx_replenish_retry, + jiffies + ATH12K_CE_RX_POST_RETRY_JIFFIES); +#endif + + return; + } + } +} + +int +qwz_ce_completed_recv_next(struct qwz_ce_pipe *pipe, + void **per_transfer_contextp, int *nbytes) +{ + struct qwz_softc *sc = pipe->sc; + struct hal_srng *srng; + unsigned int sw_index; + unsigned int nentries_mask; + uint32_t *desc; + int ret = 0; +#ifdef notyet + spin_lock_bh(&ab->ce.ce_lock); +#endif + sw_index = pipe->dest_ring->sw_index; + nentries_mask = pipe->dest_ring->nentries_mask; + + srng = &sc->hal.srng_list[pipe->status_ring->hal_ring_id]; +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + + desc = qwz_hal_srng_dst_get_next_entry(sc, srng); + if (!desc) { + ret = EIO; + goto err; + } + + *nbytes = qwz_hal_ce_dst_status_get_length(desc); + if (*nbytes == 0) { + ret = EIO; + goto err; + } + + if (per_transfer_contextp) { + *per_transfer_contextp = + pipe->dest_ring->per_transfer_context[sw_index]; + } + + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + pipe->dest_ring->sw_index = sw_index; + + pipe->rx_buf_needed++; +err: + qwz_hal_srng_access_end(sc, srng); +#ifdef notyet + spin_unlock_bh(&srng->lock); + spin_unlock_bh(&ab->ce.ce_lock); +#endif + return ret; +} + +int +qwz_ce_recv_process_cb(struct qwz_ce_pipe *pipe) +{ + struct qwz_softc *sc = pipe->sc; + struct mbuf *m; + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); + void *transfer_context; + unsigned int nbytes, max_nbytes; + int ret = 0, err; + + while (qwz_ce_completed_recv_next(pipe, &transfer_context, + &nbytes) == 0) { + struct qwz_rx_data *rx_data = transfer_context; + + bus_dmamap_unload(sc->sc_dmat, rx_data->map); + m = rx_data->m; + rx_data->m = NULL; + + max_nbytes = m->m_pkthdr.len; + if (max_nbytes < nbytes) { + printf("%s: received more than expected (nbytes %d, " + "max %d)", __func__, nbytes, max_nbytes); + m_freem(m); + continue; + } + m->m_len = m->m_pkthdr.len = nbytes; + ml_enqueue(&ml, m); + ret = 1; + } + + while ((m = ml_dequeue(&ml))) { + DNPRINTF(QWZ_D_CE, "%s: rx ce pipe %d len %d\n", __func__, + pipe->pipe_num, m->m_len); + pipe->recv_cb(sc, m); + } + + err = qwz_ce_rx_post_pipe(pipe); + if (err && err != ENOSPC) { + printf("%s: failed to post rx buf to pipe: %d err: %d\n", + __func__, pipe->pipe_num, err); +#ifdef notyet + mod_timer(&ab->rx_replenish_retry, + jiffies + ATH12K_CE_RX_POST_RETRY_JIFFIES); +#endif + } + + return ret; +} + +int +qwz_ce_per_engine_service(struct qwz_softc *sc, uint16_t ce_id) +{ + struct qwz_ce_pipe *pipe = &sc->ce.ce_pipe[ce_id]; + const struct ce_attr *attr = &sc->hw_params.host_ce_config[ce_id]; + int ret = 0; + + if (attr->src_nentries) { + if (qwz_ce_tx_process_cb(pipe)) + ret = 1; + } + + if (pipe->recv_cb) { + if (qwz_ce_recv_process_cb(pipe)) + ret = 1; + } + + return ret; +} + +int +qwz_ce_send(struct qwz_softc *sc, struct mbuf *m, uint8_t pipe_id, + uint16_t transfer_id) +{ + struct qwz_ce_pipe *pipe = &sc->ce.ce_pipe[pipe_id]; + struct hal_srng *srng; + uint32_t *desc; + unsigned int write_index, sw_index; + unsigned int nentries_mask; + int ret = 0; + uint8_t byte_swap_data = 0; + int num_used; + uint64_t paddr; + void *ctx; + struct qwz_tx_data *tx_data; + + /* Check if some entries could be regained by handling tx completion if + * the CE has interrupts disabled and the used entries is more than the + * defined usage threshold. + */ + if (pipe->attr_flags & CE_ATTR_DIS_INTR) { +#ifdef notyet + spin_lock_bh(&ab->ce.ce_lock); +#endif + write_index = pipe->src_ring->write_index; + + sw_index = pipe->src_ring->sw_index; + + if (write_index >= sw_index) + num_used = write_index - sw_index; + else + num_used = pipe->src_ring->nentries - sw_index + + write_index; +#ifdef notyet + spin_unlock_bh(&ab->ce.ce_lock); +#endif + if (num_used > ATH12K_CE_USAGE_THRESHOLD) + qwz_ce_poll_send_completed(sc, pipe->pipe_num); + } + + if (test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags)) + return ESHUTDOWN; +#ifdef notyet + spin_lock_bh(&ab->ce.ce_lock); +#endif + write_index = pipe->src_ring->write_index; + nentries_mask = pipe->src_ring->nentries_mask; + + srng = &sc->hal.srng_list[pipe->src_ring->hal_ring_id]; +#ifdef notyet + spin_lock_bh(&srng->lock); +#endif + qwz_hal_srng_access_begin(sc, srng); + + if (qwz_hal_srng_src_num_free(sc, srng, 0) < 1) { + qwz_hal_srng_access_end(sc, srng); + ret = ENOBUFS; + goto err_unlock; + } + + desc = qwz_hal_srng_src_get_next_reaped(sc, srng); + if (!desc) { + qwz_hal_srng_access_end(sc, srng); + ret = ENOBUFS; + goto err_unlock; + } + + if (pipe->attr_flags & CE_ATTR_BYTE_SWAP_DATA) + byte_swap_data = 1; + + ctx = pipe->src_ring->per_transfer_context[write_index]; + tx_data = (struct qwz_tx_data *)ctx; + + paddr = tx_data->map->dm_segs[0].ds_addr; + qwz_hal_ce_src_set_desc(desc, paddr, m->m_pkthdr.len, + transfer_id, byte_swap_data); + + pipe->src_ring->write_index = CE_RING_IDX_INCR(nentries_mask, + write_index); + + qwz_hal_srng_access_end(sc, srng); + + if (qwz_ce_need_shadow_fix(pipe_id)) + qwz_dp_shadow_start_timer(sc, srng, &sc->ce.hp_timer[pipe_id]); + +err_unlock: +#ifdef notyet + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); +#endif + return ret; +} + +int +qwz_get_num_chains(uint32_t mask) +{ + int num_chains = 0; + + while (mask) { + if (mask & 0x1) + num_chains++; + mask >>= 1; + } + + return num_chains; +} + +int +qwz_set_antenna(struct qwz_pdev *pdev, uint32_t tx_ant, uint32_t rx_ant) +{ + struct qwz_softc *sc = pdev->sc; + int ret; +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); +#endif + sc->cfg_tx_chainmask = tx_ant; + sc->cfg_rx_chainmask = rx_ant; +#if 0 + if (ar->state != ATH12K_STATE_ON && + ar->state != ATH12K_STATE_RESTARTED) + return 0; +#endif + ret = qwz_wmi_pdev_set_param(sc, WMI_PDEV_PARAM_TX_CHAIN_MASK, + tx_ant, pdev->pdev_id); + if (ret) { + printf("%s: failed to set tx-chainmask: %d, req 0x%x\n", + sc->sc_dev.dv_xname, ret, tx_ant); + return ret; + } + + sc->num_tx_chains = qwz_get_num_chains(tx_ant); + + ret = qwz_wmi_pdev_set_param(sc, WMI_PDEV_PARAM_RX_CHAIN_MASK, + rx_ant, pdev->pdev_id); + if (ret) { + printf("%s: failed to set rx-chainmask: %d, req 0x%x\n", + sc->sc_dev.dv_xname, ret, rx_ant); + return ret; + } + + sc->num_rx_chains = qwz_get_num_chains(rx_ant); +#if 0 + /* Reload HT/VHT/HE capability */ + ath12k_mac_setup_ht_vht_cap(ar, &ar->pdev->cap, NULL); + ath12k_mac_setup_he_cap(ar, &ar->pdev->cap); +#endif + return 0; +} + +int +qwz_reg_update_chan_list(struct qwz_softc *sc, uint8_t pdev_id) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct scan_chan_list_params *params; + struct ieee80211_channel *channel, *lastc; + struct channel_param *ch; + int num_channels = 0; + size_t params_size; + int ret; +#if 0 + if (ar->state == ATH12K_STATE_RESTARTING) + return 0; +#endif + lastc = &ic->ic_channels[IEEE80211_CHAN_MAX]; + for (channel = &ic->ic_channels[1]; channel <= lastc; channel++) { + if (channel->ic_flags == 0) + continue; + num_channels++; + } + + if (!num_channels) + return EINVAL; + + params_size = sizeof(*params) + + num_channels * sizeof(*params->ch_param); + + /* + * TODO: This is a temporary list for qwz_wmi_send_scan_chan_list_cmd + * to loop over. Could that function loop over ic_channels directly? + */ + params = malloc(params_size, M_DEVBUF, M_NOWAIT | M_ZERO); + if (!params) + return ENOMEM; + + params->pdev_id = pdev_id; + params->nallchans = num_channels; + + ch = params->ch_param; + lastc = &ic->ic_channels[IEEE80211_CHAN_MAX]; + for (channel = &ic->ic_channels[1]; channel <= lastc; channel++) { + if (channel->ic_flags == 0) + continue; +#ifdef notyet + /* TODO: Set to true/false based on some condition? */ + ch->allow_ht = true; + ch->allow_vht = true; + ch->allow_he = true; +#endif + ch->dfs_set = !!(IEEE80211_IS_CHAN_5GHZ(channel) && + (channel->ic_flags & IEEE80211_CHAN_PASSIVE)); + ch->is_chan_passive = !!(channel->ic_flags & + IEEE80211_CHAN_PASSIVE); + ch->is_chan_passive |= ch->dfs_set; + ch->mhz = ieee80211_ieee2mhz(ieee80211_chan2ieee(ic, channel), + channel->ic_flags); + ch->cfreq1 = ch->mhz; + ch->minpower = 0; + ch->maxpower = 40; /* XXX from Linux debug trace */ + ch->maxregpower = ch->maxpower; + ch->antennamax = 0; + + /* TODO: Use appropriate phymodes */ + if (IEEE80211_IS_CHAN_A(channel)) + ch->phy_mode = MODE_11A; + else if (IEEE80211_IS_CHAN_G(channel)) + ch->phy_mode = MODE_11G; + else + ch->phy_mode = MODE_11B; +#ifdef notyet + if (channel->band == NL80211_BAND_6GHZ && + cfg80211_channel_is_psc(channel)) + ch->psc_channel = true; +#endif + DNPRINTF(QWZ_D_WMI, "%s: mac channel freq %d maxpower %d " + "regpower %d antenna %d mode %d\n", __func__, + ch->mhz, ch->maxpower, ch->maxregpower, + ch->antennamax, ch->phy_mode); + + ch++; + /* TODO: use quarrter/half rate, cfreq12, dfs_cfreq2 + * set_agile, reg_class_idx + */ + } + + ret = qwz_wmi_send_scan_chan_list_cmd(sc, pdev_id, params); + free(params, M_DEVBUF, params_size); + + return ret; +} + +static const struct htt_rx_ring_tlv_filter qwz_mac_mon_status_filter_default = { + .rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START | + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE, + .pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0, + .pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1, + .pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2, + .pkt_filter_flags3 = HTT_RX_FP_DATA_FILTER_FLASG3 | + HTT_RX_FP_CTRL_FILTER_FLASG3 +}; + +int +qwz_mac_register(struct qwz_softc *sc) +{ + /* Initialize channel counters frequency value in hertz */ + sc->cc_freq_hz = IPQ8074_CC_FREQ_HERTZ; + + sc->free_vdev_map = (1U << (sc->num_radios * TARGET_NUM_VDEVS(sc))) - 1; + + if (IEEE80211_ADDR_EQ(etheranyaddr, sc->sc_ic.ic_myaddr)) + IEEE80211_ADDR_COPY(sc->sc_ic.ic_myaddr, sc->mac_addr); + + return 0; +} + +int +qwz_mac_config_mon_status_default(struct qwz_softc *sc, int enable) +{ + struct htt_rx_ring_tlv_filter tlv_filter = { 0 }; + int ret = 0; +#if 0 + int i; + struct dp_rxdma_ring *ring; +#endif + + if (enable) + tlv_filter = qwz_mac_mon_status_filter_default; +#if 0 /* mon status info is not useful and the code triggers mbuf corruption */ + for (i = 0; i < sc->hw_params.num_rxmda_per_pdev; i++) { + ring = &sc->pdev_dp.rx_mon_status_refill_ring[i]; + ret = qwz_dp_tx_htt_rx_filter_setup(sc, + ring->refill_buf_ring.ring_id, sc->pdev_dp.mac_id + i, + HAL_RXDMA_MONITOR_STATUS, DP_RX_BUFFER_SIZE, &tlv_filter); + if (ret) + return ret; + } + + if (enable && !sc->hw_params.rxdma1_enable) { + timeout_add_msec(&sc->mon_reap_timer, + ATH12K_MON_TIMER_INTERVAL); + } +#endif + return ret; +} + +int +qwz_mac_txpower_recalc(struct qwz_softc *sc, struct qwz_pdev *pdev) +{ + struct qwz_vif *arvif; + int ret, txpower = -1; + uint32_t param; + uint32_t min_tx_power = sc->target_caps.hw_min_tx_power; + uint32_t max_tx_power = sc->target_caps.hw_max_tx_power; +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); +#endif + TAILQ_FOREACH(arvif, &sc->vif_list, entry) { + if (arvif->txpower <= 0) + continue; + + if (txpower == -1) + txpower = arvif->txpower; + else + txpower = MIN(txpower, arvif->txpower); + } + + if (txpower == -1) + return 0; + + /* txpwr is set as 2 units per dBm in FW*/ + txpower = MIN(MAX(min_tx_power, txpower), max_tx_power) * 2; + DNPRINTF(QWZ_D_MAC, "txpower to set in hw %d\n", txpower / 2); + + if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) { + param = WMI_PDEV_PARAM_TXPOWER_LIMIT2G; + ret = qwz_wmi_pdev_set_param(sc, param, txpower, + pdev->pdev_id); + if (ret) + goto fail; + } + + if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) { + param = WMI_PDEV_PARAM_TXPOWER_LIMIT5G; + ret = qwz_wmi_pdev_set_param(sc, param, txpower, + pdev->pdev_id); + if (ret) + goto fail; + } + + return 0; + +fail: + DNPRINTF(QWZ_D_MAC, "%s: failed to recalc txpower limit %d " + "using pdev param %d: %d\n", sc->sc_dev.dv_xname, txpower / 2, + param, ret); + + return ret; +} + +int +qwz_mac_op_start(struct qwz_pdev *pdev) +{ + struct qwz_softc *sc = pdev->sc; + struct ieee80211com *ic = &sc->sc_ic; + int ret; + + ret = qwz_wmi_pdev_set_param(sc, WMI_PDEV_PARAM_PMF_QOS, 1, + pdev->pdev_id); + if (ret) { + printf("%s: failed to enable PMF QOS for pdev %d: %d\n", + sc->sc_dev.dv_xname, pdev->pdev_id, ret); + goto err; + } + + ret = qwz_wmi_pdev_set_param(sc, WMI_PDEV_PARAM_DYNAMIC_BW, 1, + pdev->pdev_id); + if (ret) { + printf("%s: failed to enable dynamic bw for pdev %d: %d\n", + sc->sc_dev.dv_xname, pdev->pdev_id, ret); + goto err; + } + + if (isset(sc->wmi.svc_map, WMI_TLV_SERVICE_SPOOF_MAC_SUPPORT)) { + ret = qwz_wmi_scan_prob_req_oui(sc, ic->ic_myaddr, + pdev->pdev_id); + if (ret) { + printf("%s: failed to set prob req oui for " + "pdev %d: %i\n", sc->sc_dev.dv_xname, + pdev->pdev_id, ret); + goto err; + } + } + + ret = qwz_wmi_pdev_set_param(sc, WMI_PDEV_PARAM_ARP_AC_OVERRIDE, 0, + pdev->pdev_id); + if (ret) { + printf("%s: failed to set ac override for ARP for " + "pdev %d: %d\n", sc->sc_dev.dv_xname, pdev->pdev_id, ret); + goto err; + } + + ret = qwz_wmi_send_dfs_phyerr_offload_enable_cmd(sc, pdev->pdev_id); + if (ret) { + printf("%s: failed to offload radar detection for " + "pdev %d: %d\n", sc->sc_dev.dv_xname, pdev->pdev_id, ret); + goto err; + } + + ret = qwz_dp_tx_htt_h2t_ppdu_stats_req(sc, HTT_PPDU_STATS_TAG_DEFAULT, + pdev->pdev_id); + if (ret) { + printf("%s: failed to req ppdu stats for pdev %d: %d\n", + sc->sc_dev.dv_xname, pdev->pdev_id, ret); + goto err; + } + + ret = qwz_wmi_pdev_set_param(sc, WMI_PDEV_PARAM_MESH_MCAST_ENABLE, 1, + pdev->pdev_id); + if (ret) { + printf("%s: failed to enable MESH MCAST ENABLE for " + "pdev %d: %d\n", sc->sc_dev.dv_xname, pdev->pdev_id, ret); + goto err; + } + + qwz_set_antenna(pdev, pdev->cap.tx_chain_mask, pdev->cap.rx_chain_mask); + + /* TODO: Do we need to enable ANI? */ + + ret = qwz_reg_update_chan_list(sc, pdev->pdev_id); + if (ret) { + printf("%s: failed to update channel list for pdev %d: %d\n", + sc->sc_dev.dv_xname, pdev->pdev_id, ret); + goto err; + } + + sc->num_started_vdevs = 0; + sc->num_created_vdevs = 0; + sc->num_peers = 0; + sc->allocated_vdev_map = 0; + + /* Configure monitor status ring with default rx_filter to get rx status + * such as rssi, rx_duration. + */ + ret = qwz_mac_config_mon_status_default(sc, 1); + if (ret) { + printf("%s: failed to configure monitor status ring " + "with default rx_filter: (%d)\n", + sc->sc_dev.dv_xname, ret); + goto err; + } + + /* Configure the hash seed for hash based reo dest ring selection */ + qwz_wmi_pdev_lro_cfg(sc, pdev->pdev_id); + + /* allow device to enter IMPS */ + if (sc->hw_params.idle_ps) { + ret = qwz_wmi_pdev_set_param(sc, WMI_PDEV_PARAM_IDLE_PS_CONFIG, + 1, pdev->pdev_id); + if (ret) { + printf("%s: failed to enable idle ps: %d\n", + sc->sc_dev.dv_xname, ret); + goto err; + } + } +#ifdef notyet + mutex_unlock(&ar->conf_mutex); +#endif + sc->pdevs_active |= (1 << pdev->pdev_id); + return 0; +err: +#ifdef notyet + ar->state = ATH12K_STATE_OFF; + mutex_unlock(&ar->conf_mutex); +#endif + return ret; +} + +int +qwz_mac_setup_vdev_params_mbssid(struct qwz_vif *arvif, + uint32_t *flags, uint32_t *tx_vdev_id) +{ + *tx_vdev_id = 0; + *flags = WMI_HOST_VDEV_FLAGS_NON_MBSSID_AP; + return 0; +} + +int +qwz_mac_setup_vdev_create_params(struct qwz_vif *arvif, struct qwz_pdev *pdev, + struct vdev_create_params *params) +{ + struct qwz_softc *sc = arvif->sc; + int ret; + + params->if_id = arvif->vdev_id; + params->type = arvif->vdev_type; + params->subtype = arvif->vdev_subtype; + params->pdev_id = pdev->pdev_id; + params->mbssid_flags = 0; + params->mbssid_tx_vdev_id = 0; + + if (!isset(sc->wmi.svc_map, + WMI_TLV_SERVICE_MBSS_PARAM_IN_VDEV_START_SUPPORT)) { + ret = qwz_mac_setup_vdev_params_mbssid(arvif, + ¶ms->mbssid_flags, ¶ms->mbssid_tx_vdev_id); + if (ret) + return ret; + } + + if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) { + params->chains[0].tx = sc->num_tx_chains; + params->chains[0].rx = sc->num_rx_chains; + } + if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) { + params->chains[1].tx = sc->num_tx_chains; + params->chains[1].rx = sc->num_rx_chains; + } +#if 0 + if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP && + ar->supports_6ghz) { + params->chains[NL80211_BAND_6GHZ].tx = ar->num_tx_chains; + params->chains[NL80211_BAND_6GHZ].rx = ar->num_rx_chains; + } +#endif + return 0; +} + +int +qwz_mac_op_update_vif_offload(struct qwz_softc *sc, struct qwz_pdev *pdev, + struct qwz_vif *arvif) +{ + uint32_t param_id, param_value; + int ret; + + param_id = WMI_VDEV_PARAM_TX_ENCAP_TYPE; + if (test_bit(ATH12K_FLAG_RAW_MODE, sc->sc_flags)) + param_value = ATH12K_HW_TXRX_RAW; + else + param_value = ATH12K_HW_TXRX_NATIVE_WIFI; + + ret = qwz_wmi_vdev_set_param_cmd(sc, arvif->vdev_id, pdev->pdev_id, + param_id, param_value); + if (ret) { + printf("%s: failed to set vdev %d tx encap mode: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + return ret; + } + + param_id = WMI_VDEV_PARAM_RX_DECAP_TYPE; + if (test_bit(ATH12K_FLAG_RAW_MODE, sc->sc_flags)) + param_value = ATH12K_HW_TXRX_RAW; + else + param_value = ATH12K_HW_TXRX_NATIVE_WIFI; + + ret = qwz_wmi_vdev_set_param_cmd(sc, arvif->vdev_id, pdev->pdev_id, + param_id, param_value); + if (ret) { + printf("%s: failed to set vdev %d rx decap mode: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + return ret; + } + + return 0; +} + +void +qwz_mac_vdev_delete(struct qwz_softc *sc, struct qwz_vif *arvif) +{ + printf("%s: not implemented\n", __func__); +} + +int +qwz_mac_vdev_setup_sync(struct qwz_softc *sc) +{ + int ret; + +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); +#endif + if (test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags)) + return ESHUTDOWN; + + while (!sc->vdev_setup_done) { + ret = tsleep_nsec(&sc->vdev_setup_done, 0, "qwzvdev", + SEC_TO_NSEC(1)); + if (ret) { + printf("%s: vdev start timeout\n", + sc->sc_dev.dv_xname); + return ret; + } + } + + return 0; +} + +int +qwz_mac_set_txbf_conf(struct qwz_vif *arvif) +{ + /* TX beamforming is not yet supported. */ + return 0; +} + +int +qwz_mac_vdev_stop(struct qwz_softc *sc, struct qwz_vif *arvif, int pdev_id) +{ + int ret; +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); +#endif +#if 0 + reinit_completion(&ar->vdev_setup_done); +#endif + sc->vdev_setup_done = 0; + ret = qwz_wmi_vdev_stop(sc, arvif->vdev_id, pdev_id); + if (ret) { + printf("%s: failed to stop WMI vdev %i: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + return ret; + } + + ret = qwz_mac_vdev_setup_sync(sc); + if (ret) { + printf("%s: failed to synchronize setup for vdev %i: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + return ret; + } + + if (sc->num_started_vdevs > 0) + sc->num_started_vdevs--; + + DNPRINTF(QWZ_D_MAC, "%s: vdev vdev_id %d stopped\n", __func__, + arvif->vdev_id); + + if (test_bit(ATH12K_CAC_RUNNING, sc->sc_flags)) { + clear_bit(ATH12K_CAC_RUNNING, sc->sc_flags); + DNPRINTF(QWZ_D_MAC, "%s: CAC Stopped for vdev %d\n", __func__, + arvif->vdev_id); + } + + return 0; +} + +int +qwz_mac_vdev_start_restart(struct qwz_softc *sc, struct qwz_vif *arvif, + int pdev_id, int restart) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_channel *chan = ic->ic_bss->ni_chan; + struct wmi_vdev_start_req_arg arg = {}; + int ret = 0; +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); +#endif +#if 0 + reinit_completion(&ar->vdev_setup_done); +#endif + arg.vdev_id = arvif->vdev_id; + arg.dtim_period = ic->ic_dtim_period; + arg.bcn_intval = ic->ic_lintval; + + arg.channel.freq = chan->ic_freq; + arg.channel.band_center_freq1 = chan->ic_freq; + arg.channel.band_center_freq2 = chan->ic_freq; + + switch (ic->ic_curmode) { + case IEEE80211_MODE_11A: + arg.channel.mode = MODE_11A; + break; + case IEEE80211_MODE_11B: + arg.channel.mode = MODE_11B; + break; + case IEEE80211_MODE_11G: + arg.channel.mode = MODE_11G; + break; + default: + printf("%s: unsupported phy mode %d\n", + sc->sc_dev.dv_xname, ic->ic_curmode); + return ENOTSUP; + } + + arg.channel.min_power = 0; + arg.channel.max_power = 20; /* XXX */ + arg.channel.max_reg_power = 20; /* XXX */ + arg.channel.max_antenna_gain = 0; /* XXX */ + + arg.pref_tx_streams = 1; + arg.pref_rx_streams = 1; + + arg.mbssid_flags = 0; + arg.mbssid_tx_vdev_id = 0; + if (isset(sc->wmi.svc_map, + WMI_TLV_SERVICE_MBSS_PARAM_IN_VDEV_START_SUPPORT)) { + ret = qwz_mac_setup_vdev_params_mbssid(arvif, + &arg.mbssid_flags, &arg.mbssid_tx_vdev_id); + if (ret) + return ret; + } +#if 0 + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + arg.ssid = arvif->u.ap.ssid; + arg.ssid_len = arvif->u.ap.ssid_len; + arg.hidden_ssid = arvif->u.ap.hidden_ssid; + + /* For now allow DFS for AP mode */ + arg.channel.chan_radar = + !!(chandef->chan->flags & IEEE80211_CHAN_RADAR); + + arg.channel.freq2_radar = ctx->radar_enabled; + + arg.channel.passive = arg.channel.chan_radar; + + spin_lock_bh(&ab->base_lock); + arg.regdomain = ar->ab->dfs_region; + spin_unlock_bh(&ab->base_lock); + } +#endif + /* XXX */ + arg.channel.passive |= !!(ieee80211_chan2ieee(ic, chan) >= 52); + + DNPRINTF(QWZ_D_MAC, "%s: vdev %d start center_freq %d phymode %s\n", + __func__, arg.vdev_id, arg.channel.freq, + qwz_wmi_phymode_str(arg.channel.mode)); + + sc->vdev_setup_done = 0; + ret = qwz_wmi_vdev_start(sc, &arg, pdev_id, restart); + if (ret) { + printf("%s: failed to %s WMI vdev %i\n", sc->sc_dev.dv_xname, + restart ? "restart" : "start", arg.vdev_id); + return ret; + } + + ret = qwz_mac_vdev_setup_sync(sc); + if (ret) { + printf("%s: failed to synchronize setup for vdev %i %s: %d\n", + sc->sc_dev.dv_xname, arg.vdev_id, + restart ? "restart" : "start", ret); + return ret; + } + + if (!restart) + sc->num_started_vdevs++; + + DNPRINTF(QWZ_D_MAC, "%s: vdev %d started\n", __func__, arvif->vdev_id); + + /* Enable CAC Flag in the driver by checking the channel DFS cac time, + * i.e dfs_cac_ms value which will be valid only for radar channels + * and state as NL80211_DFS_USABLE which indicates CAC needs to be + * done before channel usage. This flags is used to drop rx packets. + * during CAC. + */ + /* TODO Set the flag for other interface types as required */ +#if 0 + if (arvif->vdev_type == WMI_VDEV_TYPE_AP && + chandef->chan->dfs_cac_ms && + chandef->chan->dfs_state == NL80211_DFS_USABLE) { + set_bit(ATH12K_CAC_RUNNING, &ar->dev_flags); + ath12k_dbg(ab, ATH12K_DBG_MAC, + "CAC Started in chan_freq %d for vdev %d\n", + arg.channel.freq, arg.vdev_id); + } +#endif + ret = qwz_mac_set_txbf_conf(arvif); + if (ret) + printf("%s: failed to set txbf conf for vdev %d: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + + return 0; +} + +int +qwz_mac_vdev_restart(struct qwz_softc *sc, struct qwz_vif *arvif, int pdev_id) +{ + return qwz_mac_vdev_start_restart(sc, arvif, pdev_id, 1); +} + +int +qwz_mac_vdev_start(struct qwz_softc *sc, struct qwz_vif *arvif, int pdev_id) +{ + return qwz_mac_vdev_start_restart(sc, arvif, pdev_id, 0); +} + +void +qwz_vif_free(struct qwz_softc *sc, struct qwz_vif *arvif) +{ + struct qwz_txmgmt_queue *txmgmt; + int i; + + if (arvif == NULL) + return; + + txmgmt = &arvif->txmgmt; + for (i = 0; i < nitems(txmgmt->data); i++) { + struct qwz_tx_data *tx_data = &txmgmt->data[i]; + + if (tx_data->m) { + m_freem(tx_data->m); + tx_data->m = NULL; + } + if (tx_data->map) { + bus_dmamap_destroy(sc->sc_dmat, tx_data->map); + tx_data->map = NULL; + } + } + + free(arvif, M_DEVBUF, sizeof(*arvif)); +} + +struct qwz_vif * +qwz_vif_alloc(struct qwz_softc *sc) +{ + struct qwz_vif *arvif; + struct qwz_txmgmt_queue *txmgmt; + int i, ret = 0; + const bus_size_t size = IEEE80211_MAX_LEN; + + arvif = malloc(sizeof(*arvif), M_DEVBUF, M_NOWAIT | M_ZERO); + if (arvif == NULL) + return NULL; + + txmgmt = &arvif->txmgmt; + for (i = 0; i < nitems(txmgmt->data); i++) { + struct qwz_tx_data *tx_data = &txmgmt->data[i]; + + ret = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &tx_data->map); + if (ret) { + qwz_vif_free(sc, arvif); + return NULL; + } + } + + arvif->sc = sc; + + return arvif; +} + +int +qwz_mac_op_add_interface(struct qwz_pdev *pdev) +{ + struct qwz_softc *sc = pdev->sc; + struct ieee80211com *ic = &sc->sc_ic; + struct qwz_vif *arvif = NULL; + struct vdev_create_params vdev_param = { 0 }; +#if 0 + struct peer_create_params peer_param; +#endif + uint32_t param_id, param_value; + uint16_t nss; +#if 0 + int i; + int fbret; +#endif + int ret, bit; +#ifdef notyet + mutex_lock(&ar->conf_mutex); +#endif +#if 0 + if (vif->type == NL80211_IFTYPE_AP && + ar->num_peers > (ar->max_num_peers - 1)) { + ath12k_warn(ab, "failed to create vdev due to insufficient peer entry resource in firmware\n"); + ret = -ENOBUFS; + goto err; + } +#endif + if (sc->num_created_vdevs > (TARGET_NUM_VDEVS(sc) - 1)) { + printf("%s: failed to create vdev %u, reached vdev limit %d\n", + sc->sc_dev.dv_xname, sc->num_created_vdevs, + TARGET_NUM_VDEVS(sc)); + ret = EBUSY; + goto err; + } + + arvif = qwz_vif_alloc(sc); + if (arvif == NULL) { + ret = ENOMEM; + goto err; + } +#if 0 + INIT_DELAYED_WORK(&arvif->connection_loss_work, + ath12k_mac_vif_sta_connection_loss_work); + for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { + arvif->bitrate_mask.control[i].legacy = 0xffffffff; + arvif->bitrate_mask.control[i].gi = 0; + memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].ht_mcs)); + memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].vht_mcs)); + memset(arvif->bitrate_mask.control[i].he_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].he_mcs)); + } +#endif + + if (sc->free_vdev_map == 0) { + printf("%s: cannot add interface; all vdevs are busy\n", + sc->sc_dev.dv_xname); + ret = EBUSY; + goto err; + } + bit = ffs(sc->free_vdev_map) - 1; + + arvif->vdev_id = bit; + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; + + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + arvif->vdev_type = WMI_VDEV_TYPE_STA; + break; +#if 0 + case NL80211_IFTYPE_MESH_POINT: + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_MESH_11S; + fallthrough; + case NL80211_IFTYPE_AP: + arvif->vdev_type = WMI_VDEV_TYPE_AP; + break; + case NL80211_IFTYPE_MONITOR: + arvif->vdev_type = WMI_VDEV_TYPE_MONITOR; + ar->monitor_vdev_id = bit; + break; +#endif + default: + printf("%s: invalid operating mode %d\n", + sc->sc_dev.dv_xname, ic->ic_opmode); + ret = EINVAL; + goto err; + } + + DNPRINTF(QWZ_D_MAC, + "%s: add interface id %d type %d subtype %d map 0x%x\n", + __func__, arvif->vdev_id, arvif->vdev_type, + arvif->vdev_subtype, sc->free_vdev_map); + + ret = qwz_mac_setup_vdev_create_params(arvif, pdev, &vdev_param); + if (ret) { + printf("%s: failed to create vdev parameters %d: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + goto err; + } + + ret = qwz_wmi_vdev_create(sc, ic->ic_myaddr, &vdev_param); + if (ret) { + printf("%s: failed to create WMI vdev %d %s: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, + ether_sprintf(ic->ic_myaddr), ret); + goto err; + } + + sc->num_created_vdevs++; + DNPRINTF(QWZ_D_MAC, "%s: vdev %s created, vdev_id %d\n", __func__, + ether_sprintf(ic->ic_myaddr), arvif->vdev_id); + sc->allocated_vdev_map |= 1U << arvif->vdev_id; + sc->free_vdev_map &= ~(1U << arvif->vdev_id); +#ifdef notyet + spin_lock_bh(&ar->data_lock); +#endif + TAILQ_INSERT_TAIL(&sc->vif_list, arvif, entry); +#ifdef notyet + spin_unlock_bh(&ar->data_lock); +#endif + ret = qwz_mac_op_update_vif_offload(sc, pdev, arvif); + if (ret) + goto err_vdev_del; + + nss = qwz_get_num_chains(sc->cfg_tx_chainmask) ? : 1; + ret = qwz_wmi_vdev_set_param_cmd(sc, arvif->vdev_id, pdev->pdev_id, + WMI_VDEV_PARAM_NSS, nss); + if (ret) { + printf("%s: failed to set vdev %d chainmask 0x%x, nss %d: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, sc->cfg_tx_chainmask, + nss, ret); + goto err_vdev_del; + } + + switch (arvif->vdev_type) { +#if 0 + case WMI_VDEV_TYPE_AP: + peer_param.vdev_id = arvif->vdev_id; + peer_param.peer_addr = vif->addr; + peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; + ret = ath12k_peer_create(ar, arvif, NULL, &peer_param); + if (ret) { + ath12k_warn(ab, "failed to vdev %d create peer for AP: %d\n", + arvif->vdev_id, ret); + goto err_vdev_del; + } + + ret = ath12k_mac_set_kickout(arvif); + if (ret) { + ath12k_warn(ar->ab, "failed to set vdev %i kickout parameters: %d\n", + arvif->vdev_id, ret); + goto err_peer_del; + } + + ath12k_mac_11d_scan_stop_all(ar->ab); + break; +#endif + case WMI_VDEV_TYPE_STA: + param_id = WMI_STA_PS_PARAM_RX_WAKE_POLICY; + param_value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; + ret = qwz_wmi_set_sta_ps_param(sc, arvif->vdev_id, + pdev->pdev_id, param_id, param_value); + if (ret) { + printf("%s: failed to set vdev %d RX wake policy: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + goto err_peer_del; + } + + param_id = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD; + param_value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; + ret = qwz_wmi_set_sta_ps_param(sc, arvif->vdev_id, + pdev->pdev_id, param_id, param_value); + if (ret) { + printf("%s: failed to set vdev %d " + "TX wake threshold: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + goto err_peer_del; + } + + param_id = WMI_STA_PS_PARAM_PSPOLL_COUNT; + param_value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; + ret = qwz_wmi_set_sta_ps_param(sc, arvif->vdev_id, + pdev->pdev_id, param_id, param_value); + if (ret) { + printf("%s: failed to set vdev %d pspoll count: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + goto err_peer_del; + } + + ret = qwz_wmi_pdev_set_ps_mode(sc, arvif->vdev_id, + pdev->pdev_id, WMI_STA_PS_MODE_DISABLED); + if (ret) { + printf("%s: failed to disable vdev %d ps mode: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + goto err_peer_del; + } + + if (isset(sc->wmi.svc_map, WMI_TLV_SERVICE_11D_OFFLOAD)) { + sc->completed_11d_scan = 0; + sc->state_11d = ATH12K_11D_PREPARING; + } + break; +#if 0 + case WMI_VDEV_TYPE_MONITOR: + set_bit(ATH12K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); + break; +#endif + default: + printf("%s: invalid vdev type %d\n", + sc->sc_dev.dv_xname, arvif->vdev_type); + ret = EINVAL; + goto err; + } + + arvif->txpower = 40; + ret = qwz_mac_txpower_recalc(sc, pdev); + if (ret) + goto err_peer_del; + + param_id = WMI_VDEV_PARAM_RTS_THRESHOLD; + param_value = ic->ic_rtsthreshold; + ret = qwz_wmi_vdev_set_param_cmd(sc, arvif->vdev_id, pdev->pdev_id, + param_id, param_value); + if (ret) { + printf("%s: failed to set rts threshold for vdev %d: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + goto err_peer_del; + } + + qwz_dp_vdev_tx_attach(sc, pdev, arvif); +#if 0 + if (vif->type != NL80211_IFTYPE_MONITOR && + test_bit(ATH12K_FLAG_MONITOR_CONF_ENABLED, &ar->monitor_flags)) { + ret = ath12k_mac_monitor_vdev_create(ar); + if (ret) + ath12k_warn(ar->ab, "failed to create monitor vdev during add interface: %d", + ret); + } + + mutex_unlock(&ar->conf_mutex); +#endif + return 0; + +err_peer_del: +#if 0 + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + fbret = qwz_peer_delete(sc, arvif->vdev_id, vif->addr); + if (fbret) { + printf("%s: fallback fail to delete peer addr %pM " + "vdev_id %d ret %d\n", sc->sc_dev.dv_xname, + vif->addr, arvif->vdev_id, fbret); + goto err; + } + } +#endif +err_vdev_del: + qwz_mac_vdev_delete(sc, arvif); +#ifdef notyet + spin_lock_bh(&ar->data_lock); +#endif + TAILQ_REMOVE(&sc->vif_list, arvif, entry); +#ifdef notyet + spin_unlock_bh(&ar->data_lock); +#endif + +err: +#ifdef notyet + mutex_unlock(&ar->conf_mutex); +#endif + qwz_vif_free(sc, arvif); + return ret; +} + +int +qwz_mac_start(struct qwz_softc *sc) +{ + struct qwz_pdev *pdev; + int i, error; + + for (i = 0; i < sc->num_radios; i++) { + pdev = &sc->pdevs[i]; + error = qwz_mac_op_start(pdev); + if (error) + return error; + + error = qwz_mac_op_add_interface(pdev); + if (error) + return error; + } + + return 0; +} + +void +qwz_init_task(void *arg) +{ + struct qwz_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ic.ic_if; + int s = splnet(); + rw_enter_write(&sc->ioctl_rwl); + + if (ifp->if_flags & IFF_RUNNING) + qwz_stop(ifp); + + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP) + qwz_init(ifp); + + rw_exit(&sc->ioctl_rwl); + splx(s); +} + +void +qwz_mac_11d_scan_start(struct qwz_softc *sc, struct qwz_vif *arvif) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct wmi_11d_scan_start_params param; + int ret; +#ifdef notyet + mutex_lock(&ar->ab->vdev_id_11d_lock); +#endif + DNPRINTF(QWZ_D_MAC, "%s: vdev id for 11d scan %d\n", __func__, + sc->vdev_id_11d_scan); +#if 0 + if (ar->regdom_set_by_user) + goto fin; +#endif + if (sc->vdev_id_11d_scan != QWZ_11D_INVALID_VDEV_ID) + goto fin; + + if (!isset(sc->wmi.svc_map, WMI_TLV_SERVICE_11D_OFFLOAD)) + goto fin; + + if (ic->ic_opmode != IEEE80211_M_STA) + goto fin; + + param.vdev_id = arvif->vdev_id; + param.start_interval_msec = 0; + param.scan_period_msec = QWZ_SCAN_11D_INTERVAL; + + DNPRINTF(QWZ_D_MAC, "%s: start 11d scan\n", __func__); + + ret = qwz_wmi_send_11d_scan_start_cmd(sc, ¶m, + 0 /* TODO: derive pdev ID from arvif somehow? */); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to start 11d scan; vdev: %d " + "ret: %d\n", sc->sc_dev.dv_xname, + arvif->vdev_id, ret); + } + } else { + sc->vdev_id_11d_scan = arvif->vdev_id; + if (sc->state_11d == ATH12K_11D_PREPARING) + sc->state_11d = ATH12K_11D_RUNNING; + } +fin: + if (sc->state_11d == ATH12K_11D_PREPARING) { + sc->state_11d = ATH12K_11D_IDLE; + sc->completed_11d_scan = 0; + } +#ifdef notyet + mutex_unlock(&ar->ab->vdev_id_11d_lock); +#endif +} + +void +qwz_mac_scan_finish(struct qwz_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + enum ath12k_scan_state ostate; + +#ifdef notyet + lockdep_assert_held(&ar->data_lock); +#endif + ostate = sc->scan.state; + switch (ostate) { + case ATH12K_SCAN_IDLE: + break; + case ATH12K_SCAN_RUNNING: + case ATH12K_SCAN_ABORTING: +#if 0 + if (ar->scan.is_roc && ar->scan.roc_notify) + ieee80211_remain_on_channel_expired(ar->hw); + fallthrough; +#endif + case ATH12K_SCAN_STARTING: + sc->scan.state = ATH12K_SCAN_IDLE; + sc->scan_channel = 0; + sc->scan.roc_freq = 0; + + timeout_del(&sc->scan.timeout); + if (!sc->scan.is_roc) + ieee80211_end_scan(ifp); +#if 0 + complete_all(&ar->scan.completed); +#endif + break; + } +} + +int +qwz_mac_get_rate_hw_value(struct ieee80211com *ic, + struct ieee80211_node *ni, int bitrate) +{ + uint32_t preamble; + uint16_t hw_value; + int shortpre = 0; + + if (IEEE80211_IS_CHAN_CCK(ni->ni_chan)) + preamble = WMI_RATE_PREAMBLE_CCK; + else + preamble = WMI_RATE_PREAMBLE_OFDM; + + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + shortpre = 1; + + switch (bitrate) { + case 2: + hw_value = ATH12K_HW_RATE_CCK_LP_1M; + break; + case 4: + if (shortpre) + hw_value = ATH12K_HW_RATE_CCK_SP_2M; + else + hw_value = ATH12K_HW_RATE_CCK_LP_2M; + break; + case 11: + if (shortpre) + hw_value = ATH12K_HW_RATE_CCK_SP_5_5M; + else + hw_value = ATH12K_HW_RATE_CCK_LP_5_5M; + break; + case 22: + if (shortpre) + hw_value = ATH12K_HW_RATE_CCK_SP_11M; + else + hw_value = ATH12K_HW_RATE_CCK_LP_11M; + break; + case 12: + hw_value = ATH12K_HW_RATE_OFDM_6M; + break; + case 18: + hw_value = ATH12K_HW_RATE_OFDM_9M; + break; + case 24: + hw_value = ATH12K_HW_RATE_OFDM_12M; + break; + case 36: + hw_value = ATH12K_HW_RATE_OFDM_18M; + break; + case 48: + hw_value = ATH12K_HW_RATE_OFDM_24M; + break; + case 72: + hw_value = ATH12K_HW_RATE_OFDM_36M; + break; + case 96: + hw_value = ATH12K_HW_RATE_OFDM_48M; + break; + case 108: + hw_value = ATH12K_HW_RATE_OFDM_54M; + break; + default: + return -1; + } + + return ATH12K_HW_RATE_CODE(hw_value, 0, preamble); +} + +int +qwz_peer_delete(struct qwz_softc *sc, uint32_t vdev_id, uint8_t pdev_id, + uint8_t *addr) +{ + int ret; + + sc->peer_mapped = 0; + sc->peer_delete_done = 0; + + ret = qwz_wmi_send_peer_delete_cmd(sc, addr, vdev_id, pdev_id); + if (ret) { + printf("%s: failed to delete peer vdev_id %d addr %s ret %d\n", + sc->sc_dev.dv_xname, vdev_id, ether_sprintf(addr), ret); + return ret; + } + + while (!sc->peer_mapped) { + ret = tsleep_nsec(&sc->peer_mapped, 0, "qwzpeer", + SEC_TO_NSEC(3)); + if (ret) { + printf("%s: peer delete unmap timeout\n", + sc->sc_dev.dv_xname); + return ret; + } + } + + while (!sc->peer_delete_done) { + ret = tsleep_nsec(&sc->peer_delete_done, 0, "qwzpeerd", + SEC_TO_NSEC(3)); + if (ret) { + printf("%s: peer delete command timeout\n", + sc->sc_dev.dv_xname); + return ret; + } + } + + sc->num_peers--; + return 0; +} + +int +qwz_peer_create(struct qwz_softc *sc, struct qwz_vif *arvif, uint8_t pdev_id, + struct ieee80211_node *ni, struct peer_create_params *param) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct qwz_node *nq = (struct qwz_node *)ni; + struct ath12k_peer *peer; + int ret; +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); +#endif + if (sc->num_peers > (TARGET_NUM_PEERS_PDEV(sc) - 1)) { + DPRINTF("%s: failed to create peer due to insufficient " + "peer entry resource in firmware\n", __func__); + return ENOBUFS; + } +#ifdef notyet + mutex_lock(&ar->ab->tbl_mtx_lock); + spin_lock_bh(&ar->ab->base_lock); +#endif + peer = &nq->peer; + if (peer) { + if (peer->peer_id != HAL_INVALID_PEERID && + peer->vdev_id == param->vdev_id) { +#ifdef notyet + spin_unlock_bh(&ar->ab->base_lock); + mutex_unlock(&ar->ab->tbl_mtx_lock); +#endif + return EINVAL; + } +#if 0 + /* Assume sta is transitioning to another band. + * Remove here the peer from rhash. + */ + ath12k_peer_rhash_delete(ar->ab, peer); +#endif + } +#ifdef notyet + spin_unlock_bh(&ar->ab->base_lock); + mutex_unlock(&ar->ab->tbl_mtx_lock); +#endif + sc->peer_mapped = 0; + + ret = qwz_wmi_send_peer_create_cmd(sc, pdev_id, param); + if (ret) { + printf("%s: failed to send peer create vdev_id %d ret %d\n", + sc->sc_dev.dv_xname, param->vdev_id, ret); + return ret; + } + + while (!sc->peer_mapped) { + ret = tsleep_nsec(&sc->peer_mapped, 0, "qwzpeer", + SEC_TO_NSEC(3)); + if (ret) { + printf("%s: peer create command timeout\n", + sc->sc_dev.dv_xname); + return ret; + } + } + +#ifdef notyet + mutex_lock(&ar->ab->tbl_mtx_lock); + spin_lock_bh(&ar->ab->base_lock); +#endif +#if 0 + peer = ath12k_peer_find(ar->ab, param->vdev_id, param->peer_addr); + if (!peer) { + spin_unlock_bh(&ar->ab->base_lock); + mutex_unlock(&ar->ab->tbl_mtx_lock); + ath12k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n", + param->peer_addr, param->vdev_id); + + ret = -ENOENT; + goto cleanup; + } + + ret = ath12k_peer_rhash_add(ar->ab, peer); + if (ret) { + spin_unlock_bh(&ar->ab->base_lock); + mutex_unlock(&ar->ab->tbl_mtx_lock); + goto cleanup; + } +#endif + peer->pdev_id = pdev_id; +#if 0 + peer->sta = sta; +#endif + if (ic->ic_opmode == IEEE80211_M_STA) { + arvif->ast_hash = peer->ast_hash; + arvif->ast_idx = peer->hw_peer_id; + } +#if 0 + peer->sec_type = HAL_ENCRYPT_TYPE_OPEN; + peer->sec_type_grp = HAL_ENCRYPT_TYPE_OPEN; + + if (sta) { + struct ath12k_sta *arsta = (struct ath12k_sta *)sta->drv_priv; + arsta->tcl_metadata |= FIELD_PREP(HTT_TCL_META_DATA_TYPE, 0) | + FIELD_PREP(HTT_TCL_META_DATA_PEER_ID, + peer->peer_id); + + /* set HTT extension valid bit to 0 by default */ + arsta->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT; + } +#endif + sc->num_peers++; +#ifdef notyet + spin_unlock_bh(&ar->ab->base_lock); + mutex_unlock(&ar->ab->tbl_mtx_lock); +#endif + return 0; +#if 0 +cleanup: + int fbret = qwz_peer_delete(sc, param->vdev_id, param->peer_addr); + if (fbret) { + printf("%s: failed peer %s delete vdev_id %d fallback ret %d\n", + sc->sc_dev.dv_xname, ether_sprintf(ni->ni_macaddr), + param->vdev_id, fbret); + } + + return ret; +#endif +} + +int +qwz_dp_tx_send_reo_cmd(struct qwz_softc *sc, struct dp_rx_tid *rx_tid, + enum hal_reo_cmd_type type, struct ath12k_hal_reo_cmd *cmd, + void (*cb)(struct qwz_dp *, void *, enum hal_reo_cmd_status)) +{ + struct qwz_dp *dp = &sc->dp; + struct dp_reo_cmd *dp_cmd; + struct hal_srng *cmd_ring; + int cmd_num; + + if (test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags)) + return ESHUTDOWN; + + cmd_ring = &sc->hal.srng_list[dp->reo_cmd_ring.ring_id]; + cmd_num = qwz_hal_reo_cmd_send(sc, cmd_ring, type, cmd); + /* cmd_num should start from 1, during failure return the error code */ + if (cmd_num < 0) + return cmd_num; + + /* reo cmd ring descriptors has cmd_num starting from 1 */ + if (cmd_num == 0) + return EINVAL; + + if (!cb) + return 0; + + /* Can this be optimized so that we keep the pending command list only + * for tid delete command to free up the resource on the command status + * indication? + */ + dp_cmd = malloc(sizeof(*dp_cmd), M_DEVBUF, M_ZERO | M_NOWAIT); + if (!dp_cmd) + return ENOMEM; + + memcpy(&dp_cmd->data, rx_tid, sizeof(struct dp_rx_tid)); + dp_cmd->cmd_num = cmd_num; + dp_cmd->handler = cb; +#ifdef notyet + spin_lock_bh(&dp->reo_cmd_lock); +#endif + TAILQ_INSERT_TAIL(&dp->reo_cmd_list, dp_cmd, entry); +#ifdef notyet + spin_unlock_bh(&dp->reo_cmd_lock); +#endif + return 0; +} + +uint32_t +qwz_hal_reo_qdesc_size(uint32_t ba_window_size, uint8_t tid) +{ + uint32_t num_ext_desc; + + if (ba_window_size <= 1) { + if (tid != HAL_DESC_REO_NON_QOS_TID) + num_ext_desc = 1; + else + num_ext_desc = 0; + } else if (ba_window_size <= 105) { + num_ext_desc = 1; + } else if (ba_window_size <= 210) { + num_ext_desc = 2; + } else { + num_ext_desc = 3; + } + + return sizeof(struct hal_rx_reo_queue) + + (num_ext_desc * sizeof(struct hal_rx_reo_queue_ext)); +} + +void +qwz_hal_reo_set_desc_hdr(struct hal_desc_header *hdr, uint8_t owner, uint8_t buffer_type, uint32_t magic) +{ + hdr->info0 = FIELD_PREP(HAL_DESC_HDR_INFO0_OWNER, owner) | + FIELD_PREP(HAL_DESC_HDR_INFO0_BUF_TYPE, buffer_type); + + /* Magic pattern in reserved bits for debugging */ + hdr->info0 |= FIELD_PREP(HAL_DESC_HDR_INFO0_DBG_RESERVED, magic); +} + +void +qwz_hal_reo_qdesc_setup(void *vaddr, int tid, uint32_t ba_window_size, + uint32_t start_seq, enum hal_pn_type type) +{ + struct hal_rx_reo_queue *qdesc = (struct hal_rx_reo_queue *)vaddr; + struct hal_rx_reo_queue_ext *ext_desc; + + memset(qdesc, 0, sizeof(*qdesc)); + + qwz_hal_reo_set_desc_hdr(&qdesc->desc_hdr, HAL_DESC_REO_OWNED, + HAL_DESC_REO_QUEUE_DESC, REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_0); + + qdesc->rx_queue_num = FIELD_PREP(HAL_RX_REO_QUEUE_RX_QUEUE_NUMBER, tid); + + qdesc->info0 = FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_VLD, 1) | + FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_ASSOC_LNK_DESC_COUNTER, 1) | + FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_AC, qwz_tid_to_ac(tid)); + + if (ba_window_size < 1) + ba_window_size = 1; + + if (ba_window_size == 1 && tid != HAL_DESC_REO_NON_QOS_TID) + ba_window_size++; + + if (ba_window_size == 1) + qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_RETRY, 1); + + qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_BA_WINDOW_SIZE, + ba_window_size - 1); + switch (type) { + case HAL_PN_TYPE_NONE: + case HAL_PN_TYPE_WAPI_EVEN: + case HAL_PN_TYPE_WAPI_UNEVEN: + break; + case HAL_PN_TYPE_WPA: + qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_PN_CHECK, 1) | + FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_PN_SIZE, + HAL_RX_REO_QUEUE_PN_SIZE_48); + break; + } + + /* TODO: Set Ignore ampdu flags based on BA window size and/or + * AMPDU capabilities + */ + qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_IGNORE_AMPDU_FLG, 1); + + qdesc->info1 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO1_SVLD, 0); + + if (start_seq <= 0xfff) + qdesc->info1 = FIELD_PREP(HAL_RX_REO_QUEUE_INFO1_SSN, + start_seq); + + if (tid == HAL_DESC_REO_NON_QOS_TID) + return; + + ext_desc = qdesc->ext_desc; + + /* TODO: HW queue descriptors are currently allocated for max BA + * window size for all QOS TIDs so that same descriptor can be used + * later when ADDBA request is received. This should be changed to + * allocate HW queue descriptors based on BA window size being + * negotiated (0 for non BA cases), and reallocate when BA window + * size changes and also send WMI message to FW to change the REO + * queue descriptor in Rx peer entry as part of dp_rx_tid_update. + */ + memset(ext_desc, 0, sizeof(*ext_desc)); + qwz_hal_reo_set_desc_hdr(&ext_desc->desc_hdr, HAL_DESC_REO_OWNED, + HAL_DESC_REO_QUEUE_EXT_DESC, REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_1); + ext_desc++; + memset(ext_desc, 0, sizeof(*ext_desc)); + qwz_hal_reo_set_desc_hdr(&ext_desc->desc_hdr, HAL_DESC_REO_OWNED, + HAL_DESC_REO_QUEUE_EXT_DESC, REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_2); + ext_desc++; + memset(ext_desc, 0, sizeof(*ext_desc)); + qwz_hal_reo_set_desc_hdr(&ext_desc->desc_hdr, HAL_DESC_REO_OWNED, + HAL_DESC_REO_QUEUE_EXT_DESC, REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_3); +} + +void +qwz_dp_reo_cmd_free(struct qwz_dp *dp, void *ctx, + enum hal_reo_cmd_status status) +{ + struct qwz_softc *sc = dp->sc; + struct dp_rx_tid *rx_tid = ctx; + + if (status != HAL_REO_CMD_SUCCESS) + printf("%s: failed to flush rx tid hw desc, tid %d status %d\n", + sc->sc_dev.dv_xname, rx_tid->tid, status); + + if (rx_tid->mem) { + qwz_dmamem_free(sc->sc_dmat, rx_tid->mem); + rx_tid->mem = NULL; + rx_tid->vaddr = NULL; + rx_tid->paddr = 0ULL; + rx_tid->size = 0; + } +} + +void +qwz_dp_reo_cache_flush(struct qwz_softc *sc, struct dp_rx_tid *rx_tid) +{ + struct ath12k_hal_reo_cmd cmd = {0}; + unsigned long tot_desc_sz, desc_sz; + int ret; + + tot_desc_sz = rx_tid->size; + desc_sz = qwz_hal_reo_qdesc_size(0, HAL_DESC_REO_NON_QOS_TID); + + while (tot_desc_sz > desc_sz) { + tot_desc_sz -= desc_sz; + cmd.addr_lo = (rx_tid->paddr + tot_desc_sz) & 0xffffffff; + cmd.addr_hi = rx_tid->paddr >> 32; + ret = qwz_dp_tx_send_reo_cmd(sc, rx_tid, + HAL_REO_CMD_FLUSH_CACHE, &cmd, NULL); + if (ret) { + printf("%s: failed to send HAL_REO_CMD_FLUSH_CACHE, " + "tid %d (%d)\n", sc->sc_dev.dv_xname, rx_tid->tid, + ret); + } + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.addr_lo = rx_tid->paddr & 0xffffffff; + cmd.addr_hi = rx_tid->paddr >> 32; + cmd.flag |= HAL_REO_CMD_FLG_NEED_STATUS; + ret = qwz_dp_tx_send_reo_cmd(sc, rx_tid, HAL_REO_CMD_FLUSH_CACHE, + &cmd, qwz_dp_reo_cmd_free); + if (ret) { + printf("%s: failed to send HAL_REO_CMD_FLUSH_CACHE cmd, " + "tid %d (%d)\n", sc->sc_dev.dv_xname, rx_tid->tid, ret); + if (rx_tid->mem) { + qwz_dmamem_free(sc->sc_dmat, rx_tid->mem); + rx_tid->mem = NULL; + rx_tid->vaddr = NULL; + rx_tid->paddr = 0ULL; + rx_tid->size = 0; + } + } +} + +void +qwz_dp_rx_tid_del_func(struct qwz_dp *dp, void *ctx, + enum hal_reo_cmd_status status) +{ + struct qwz_softc *sc = dp->sc; + struct dp_rx_tid *rx_tid = ctx; + struct dp_reo_cache_flush_elem *elem, *tmp; + uint64_t now; + + if (status == HAL_REO_CMD_DRAIN) { + goto free_desc; + } else if (status != HAL_REO_CMD_SUCCESS) { + /* Shouldn't happen! Cleanup in case of other failure? */ + printf("%s: failed to delete rx tid %d hw descriptor %d\n", + sc->sc_dev.dv_xname, rx_tid->tid, status); + return; + } + + elem = malloc(sizeof(*elem), M_DEVBUF, M_ZERO | M_NOWAIT); + if (!elem) + goto free_desc; + + now = getnsecuptime(); + elem->ts = now; + memcpy(&elem->data, rx_tid, sizeof(*rx_tid)); + + rx_tid->mem = NULL; + rx_tid->vaddr = NULL; + rx_tid->paddr = 0ULL; + rx_tid->size = 0; + +#ifdef notyet + spin_lock_bh(&dp->reo_cmd_lock); +#endif + TAILQ_INSERT_TAIL(&dp->reo_cmd_cache_flush_list, elem, entry); + dp->reo_cmd_cache_flush_count++; + + /* Flush and invalidate aged REO desc from HW cache */ + TAILQ_FOREACH_SAFE(elem, &dp->reo_cmd_cache_flush_list, entry, tmp) { + if (dp->reo_cmd_cache_flush_count > DP_REO_DESC_FREE_THRESHOLD || + now >= elem->ts + MSEC_TO_NSEC(DP_REO_DESC_FREE_TIMEOUT_MS)) { + TAILQ_REMOVE(&dp->reo_cmd_cache_flush_list, elem, entry); + dp->reo_cmd_cache_flush_count--; +#ifdef notyet + spin_unlock_bh(&dp->reo_cmd_lock); +#endif + qwz_dp_reo_cache_flush(sc, &elem->data); + free(elem, M_DEVBUF, sizeof(*elem)); +#ifdef notyet + spin_lock_bh(&dp->reo_cmd_lock); +#endif + } + } +#ifdef notyet + spin_unlock_bh(&dp->reo_cmd_lock); +#endif + return; +free_desc: + if (rx_tid->mem) { + qwz_dmamem_free(sc->sc_dmat, rx_tid->mem); + rx_tid->mem = NULL; + rx_tid->vaddr = NULL; + rx_tid->paddr = 0ULL; + rx_tid->size = 0; + } +} + +void +qwz_peer_rx_tid_delete(struct qwz_softc *sc, struct ath12k_peer *peer, + uint8_t tid) +{ + struct ath12k_hal_reo_cmd cmd = {0}; + struct dp_rx_tid *rx_tid = &peer->rx_tid[tid]; + int ret; + + if (!rx_tid->active) + return; + + rx_tid->active = 0; + + cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; + cmd.addr_lo = rx_tid->paddr & 0xffffffff; + cmd.addr_hi = rx_tid->paddr >> 32; + cmd.upd0 |= HAL_REO_CMD_UPD0_VLD; + ret = qwz_dp_tx_send_reo_cmd(sc, rx_tid, HAL_REO_CMD_UPDATE_RX_QUEUE, + &cmd, qwz_dp_rx_tid_del_func); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send " + "HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", + sc->sc_dev.dv_xname, tid, ret); + } + + if (rx_tid->mem) { + qwz_dmamem_free(sc->sc_dmat, rx_tid->mem); + rx_tid->mem = NULL; + rx_tid->vaddr = NULL; + rx_tid->paddr = 0ULL; + rx_tid->size = 0; + } + } +} + +void +qwz_dp_rx_frags_cleanup(struct qwz_softc *sc, struct dp_rx_tid *rx_tid, + int rel_link_desc) +{ +#ifdef notyet + lockdep_assert_held(&ab->base_lock); +#endif +#if 0 + if (rx_tid->dst_ring_desc) { + if (rel_link_desc) + ath12k_dp_rx_link_desc_return(ab, (u32 *)rx_tid->dst_ring_desc, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + kfree(rx_tid->dst_ring_desc); + rx_tid->dst_ring_desc = NULL; + } +#endif + rx_tid->cur_sn = 0; + rx_tid->last_frag_no = 0; + rx_tid->rx_frag_bitmap = 0; +#if 0 + __skb_queue_purge(&rx_tid->rx_frags); +#endif +} + +void +qwz_peer_frags_flush(struct qwz_softc *sc, struct ath12k_peer *peer) +{ + struct dp_rx_tid *rx_tid; + int i; +#ifdef notyet + lockdep_assert_held(&ar->ab->base_lock); +#endif + for (i = 0; i < IEEE80211_NUM_TID; i++) { + rx_tid = &peer->rx_tid[i]; + + qwz_dp_rx_frags_cleanup(sc, rx_tid, 1); +#if 0 + spin_unlock_bh(&ar->ab->base_lock); + del_timer_sync(&rx_tid->frag_timer); + spin_lock_bh(&ar->ab->base_lock); +#endif + } +} + +void +qwz_peer_rx_tid_cleanup(struct qwz_softc *sc, struct ath12k_peer *peer) +{ + struct dp_rx_tid *rx_tid; + int i; +#ifdef notyet + lockdep_assert_held(&ar->ab->base_lock); +#endif + for (i = 0; i < IEEE80211_NUM_TID; i++) { + rx_tid = &peer->rx_tid[i]; + + qwz_peer_rx_tid_delete(sc, peer, i); + qwz_dp_rx_frags_cleanup(sc, rx_tid, 1); +#if 0 + spin_unlock_bh(&ar->ab->base_lock); + del_timer_sync(&rx_tid->frag_timer); + spin_lock_bh(&ar->ab->base_lock); +#endif + } +} + +int +qwz_peer_rx_tid_reo_update(struct qwz_softc *sc, struct ath12k_peer *peer, + struct dp_rx_tid *rx_tid, uint32_t ba_win_sz, uint16_t ssn, + int update_ssn) +{ + struct ath12k_hal_reo_cmd cmd = {0}; + int ret; + + cmd.addr_lo = rx_tid->paddr & 0xffffffff; + cmd.addr_hi = rx_tid->paddr >> 32; + cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; + cmd.upd0 = HAL_REO_CMD_UPD0_BA_WINDOW_SIZE; + cmd.ba_window_size = ba_win_sz; + + if (update_ssn) { + cmd.upd0 |= HAL_REO_CMD_UPD0_SSN; + cmd.upd2 = FIELD_PREP(HAL_REO_CMD_UPD2_SSN, ssn); + } + + ret = qwz_dp_tx_send_reo_cmd(sc, rx_tid, HAL_REO_CMD_UPDATE_RX_QUEUE, + &cmd, NULL); + if (ret) { + printf("%s: failed to update rx tid queue, tid %d (%d)\n", + sc->sc_dev.dv_xname, rx_tid->tid, ret); + return ret; + } + + rx_tid->ba_win_sz = ba_win_sz; + + return 0; +} + +void +qwz_dp_rx_tid_mem_free(struct qwz_softc *sc, struct ieee80211_node *ni, + int vdev_id, uint8_t tid) +{ + struct qwz_node *nq = (struct qwz_node *)ni; + struct ath12k_peer *peer = &nq->peer; + struct dp_rx_tid *rx_tid; +#ifdef notyet + spin_lock_bh(&ab->base_lock); +#endif + rx_tid = &peer->rx_tid[tid]; + + if (rx_tid->mem) { + qwz_dmamem_free(sc->sc_dmat, rx_tid->mem); + rx_tid->mem = NULL; + rx_tid->vaddr = NULL; + rx_tid->paddr = 0ULL; + rx_tid->size = 0; + } + + rx_tid->active = 0; +#ifdef notyet + spin_unlock_bh(&ab->base_lock); +#endif +} + +int +qwz_peer_rx_tid_setup(struct qwz_softc *sc, struct ieee80211_node *ni, + int vdev_id, int pdev_id, uint8_t tid, uint32_t ba_win_sz, uint16_t ssn, + enum hal_pn_type pn_type) +{ + struct qwz_node *nq = (struct qwz_node *)ni; + struct ath12k_peer *peer = &nq->peer; + struct dp_rx_tid *rx_tid; + uint32_t hw_desc_sz; + void *vaddr; + uint64_t paddr; + int ret; +#ifdef notyet + spin_lock_bh(&ab->base_lock); +#endif + rx_tid = &peer->rx_tid[tid]; + /* Update the tid queue if it is already setup */ + if (rx_tid->active) { + paddr = rx_tid->paddr; + ret = qwz_peer_rx_tid_reo_update(sc, peer, rx_tid, + ba_win_sz, ssn, 1); +#ifdef notyet + spin_unlock_bh(&ab->base_lock); +#endif + if (ret) { + printf("%s: failed to update reo for peer %s " + "rx tid %d\n: %d", sc->sc_dev.dv_xname, + ether_sprintf(ni->ni_macaddr), tid, ret); + return ret; + } + + ret = qwz_wmi_peer_rx_reorder_queue_setup(sc, vdev_id, + pdev_id, ni->ni_macaddr, paddr, tid, 1, ba_win_sz); + if (ret) + printf("%s: failed to send wmi rx reorder queue " + "for peer %s tid %d: %d\n", sc->sc_dev.dv_xname, + ether_sprintf(ni->ni_macaddr), tid, ret); + return ret; + } + + rx_tid->tid = tid; + + rx_tid->ba_win_sz = ba_win_sz; + + /* TODO: Optimize the memory allocation for qos tid based on + * the actual BA window size in REO tid update path. + */ + if (tid == HAL_DESC_REO_NON_QOS_TID) + hw_desc_sz = qwz_hal_reo_qdesc_size(ba_win_sz, tid); + else + hw_desc_sz = qwz_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); + + rx_tid->mem = qwz_dmamem_alloc(sc->sc_dmat, hw_desc_sz, + HAL_LINK_DESC_ALIGN); + if (rx_tid->mem == NULL) { +#ifdef notyet + spin_unlock_bh(&ab->base_lock); +#endif + return ENOMEM; + } + + vaddr = QWZ_DMA_KVA(rx_tid->mem); + + qwz_hal_reo_qdesc_setup(vaddr, tid, ba_win_sz, ssn, pn_type); + + paddr = QWZ_DMA_DVA(rx_tid->mem); + + rx_tid->vaddr = vaddr; + rx_tid->paddr = paddr; + rx_tid->size = hw_desc_sz; + rx_tid->active = 1; +#ifdef notyet + spin_unlock_bh(&ab->base_lock); +#endif + ret = qwz_wmi_peer_rx_reorder_queue_setup(sc, vdev_id, pdev_id, + ni->ni_macaddr, paddr, tid, 1, ba_win_sz); + if (ret) { + printf("%s: failed to setup rx reorder queue for peer %s " + "tid %d: %d\n", sc->sc_dev.dv_xname, + ether_sprintf(ni->ni_macaddr), tid, ret); + qwz_dp_rx_tid_mem_free(sc, ni, vdev_id, tid); + } + + return ret; +} + +int +qwz_peer_rx_frag_setup(struct qwz_softc *sc, struct ieee80211_node *ni, + int vdev_id) +{ + struct qwz_node *nq = (struct qwz_node *)ni; + struct ath12k_peer *peer = &nq->peer; + struct dp_rx_tid *rx_tid; + int i; +#ifdef notyet + spin_lock_bh(&ab->base_lock); +#endif + for (i = 0; i <= nitems(peer->rx_tid); i++) { + rx_tid = &peer->rx_tid[i]; +#if 0 + rx_tid->ab = ab; + timer_setup(&rx_tid->frag_timer, ath12k_dp_rx_frag_timer, 0); +#endif + } +#if 0 + peer->dp_setup_done = true; +#endif +#ifdef notyet + spin_unlock_bh(&ab->base_lock); +#endif + return 0; +} + +int +qwz_dp_peer_setup(struct qwz_softc *sc, int vdev_id, int pdev_id, + struct ieee80211_node *ni) +{ + struct qwz_node *nq = (struct qwz_node *)ni; + struct ath12k_peer *peer = &nq->peer; + uint32_t reo_dest; + int ret = 0, tid; + + /* reo_dest ring id starts from 1 unlike mac_id which starts from 0 */ + reo_dest = sc->pdev_dp.mac_id + 1; + ret = qwz_wmi_set_peer_param(sc, ni->ni_macaddr, vdev_id, pdev_id, + WMI_PEER_SET_DEFAULT_ROUTING, DP_RX_HASH_ENABLE | (reo_dest << 1)); + if (ret) { + printf("%s: failed to set default routing %d peer %s " + "vdev_id %d\n", sc->sc_dev.dv_xname, ret, + ether_sprintf(ni->ni_macaddr), vdev_id); + return ret; + } + + for (tid = 0; tid < IEEE80211_NUM_TID; tid++) { + ret = qwz_peer_rx_tid_setup(sc, ni, vdev_id, pdev_id, + tid, 1, 0, HAL_PN_TYPE_NONE); + if (ret) { + printf("%s: failed to setup rxd tid queue for tid %d: %d\n", + sc->sc_dev.dv_xname, tid, ret); + goto peer_clean; + } + } + + ret = qwz_peer_rx_frag_setup(sc, ni, vdev_id); + if (ret) { + printf("%s: failed to setup rx defrag context\n", + sc->sc_dev.dv_xname); + tid--; + goto peer_clean; + } + + /* TODO: Setup other peer specific resource used in data path */ + + return 0; + +peer_clean: +#ifdef notyet + spin_lock_bh(&ab->base_lock); +#endif +#if 0 + peer = ath12k_peer_find(ab, vdev_id, addr); + if (!peer) { + ath12k_warn(ab, "failed to find the peer to del rx tid\n"); + spin_unlock_bh(&ab->base_lock); + return -ENOENT; + } +#endif + for (; tid >= 0; tid--) + qwz_peer_rx_tid_delete(sc, peer, tid); +#ifdef notyet + spin_unlock_bh(&ab->base_lock); +#endif + return ret; +} + +int +qwz_dp_peer_rx_pn_replay_config(struct qwz_softc *sc, struct qwz_vif *arvif, + struct ieee80211_node *ni, struct ieee80211_key *k, int delete_key) +{ + struct ath12k_hal_reo_cmd cmd = {0}; + struct qwz_node *nq = (struct qwz_node *)ni; + struct ath12k_peer *peer = &nq->peer; + struct dp_rx_tid *rx_tid; + uint8_t tid; + int ret = 0; + + /* + * NOTE: Enable PN/TSC replay check offload only for unicast frames. + * We use net80211 PN/TSC replay check functionality for bcast/mcast + * for now. + */ + if (k->k_flags & IEEE80211_KEY_GROUP) + return 0; + + cmd.flag |= HAL_REO_CMD_FLG_NEED_STATUS; + cmd.upd0 |= HAL_REO_CMD_UPD0_PN | + HAL_REO_CMD_UPD0_PN_SIZE | + HAL_REO_CMD_UPD0_PN_VALID | + HAL_REO_CMD_UPD0_PN_CHECK | + HAL_REO_CMD_UPD0_SVLD; + + switch (k->k_cipher) { + case IEEE80211_CIPHER_TKIP: + case IEEE80211_CIPHER_CCMP: +#if 0 + case WLAN_CIPHER_SUITE_CCMP_256: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: +#endif + if (!delete_key) { + cmd.upd1 |= HAL_REO_CMD_UPD1_PN_CHECK; + cmd.pn_size = 48; + } + break; + default: + printf("%s: cipher %u is not supported\n", + sc->sc_dev.dv_xname, k->k_cipher); + return EOPNOTSUPP; + } + + for (tid = 0; tid < IEEE80211_NUM_TID; tid++) { + rx_tid = &peer->rx_tid[tid]; + if (!rx_tid->active) + continue; + cmd.addr_lo = rx_tid->paddr & 0xffffffff; + cmd.addr_hi = (rx_tid->paddr >> 32); + ret = qwz_dp_tx_send_reo_cmd(sc, rx_tid, + HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, NULL); + if (ret) { + printf("%s: failed to configure rx tid %d queue " + "for pn replay detection %d\n", + sc->sc_dev.dv_xname, tid, ret); + break; + } + } + + return ret; +} + +enum hal_tcl_encap_type +qwz_dp_tx_get_encap_type(struct qwz_softc *sc) +{ + if (test_bit(ATH12K_FLAG_RAW_MODE, sc->sc_flags)) + return HAL_TCL_ENCAP_TYPE_RAW; +#if 0 + if (tx_info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) + return HAL_TCL_ENCAP_TYPE_ETHERNET; +#endif + return HAL_TCL_ENCAP_TYPE_NATIVE_WIFI; +} + +uint8_t +qwz_dp_tx_get_tid(struct mbuf *m) +{ + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); + uint16_t qos = ieee80211_get_qos(wh); + uint8_t tid = qos & IEEE80211_QOS_TID; + + return tid; +} + +void +qwz_hal_tx_cmd_desc_setup(struct qwz_softc *sc, void *cmd, + struct hal_tx_info *ti) +{ + struct hal_tcl_data_cmd *tcl_cmd = (struct hal_tcl_data_cmd *)cmd; + + tcl_cmd->buf_addr_info.info0 = FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + ti->paddr); + tcl_cmd->buf_addr_info.info1 = FIELD_PREP(BUFFER_ADDR_INFO1_ADDR, + ((uint64_t)ti->paddr >> HAL_ADDR_MSB_REG_SHIFT)); + tcl_cmd->buf_addr_info.info1 |= FIELD_PREP( + BUFFER_ADDR_INFO1_RET_BUF_MGR, ti->rbm_id) | + FIELD_PREP(BUFFER_ADDR_INFO1_SW_COOKIE, ti->desc_id); + + tcl_cmd->info0 = + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_DESC_TYPE, ti->type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ENCAP_TYPE, ti->encap_type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ENCRYPT_TYPE, ti->encrypt_type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_SEARCH_TYPE, ti->search_type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ADDR_EN, ti->addr_search_flags) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_CMD_NUM, ti->meta_data_flags); + + tcl_cmd->info1 = ti->flags0 | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_DATA_LEN, ti->data_len) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_PKT_OFFSET, ti->pkt_offset); + + tcl_cmd->info2 = ti->flags1 | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_TID, ti->tid) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_LMAC_ID, ti->lmac_id); + + tcl_cmd->info3 = FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_DSCP_TID_TABLE_IDX, + ti->dscp_tid_tbl_idx) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_SEARCH_INDEX, ti->bss_ast_idx) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_CACHE_SET_NUM, ti->bss_ast_hash); + tcl_cmd->info4 = 0; +#ifdef notyet + if (ti->enable_mesh) + ab->hw_params.hw_ops->tx_mesh_enable(ab, tcl_cmd); +#endif +} + +int +qwz_dp_tx(struct qwz_softc *sc, struct qwz_vif *arvif, uint8_t pdev_id, + struct ieee80211_node *ni, struct mbuf *m) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct qwz_dp *dp = &sc->dp; + struct hal_tx_info ti = {0}; + struct qwz_tx_data *tx_data; + struct hal_srng *tcl_ring; + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); + struct ieee80211_key *k = NULL; + struct dp_tx_ring *tx_ring; + void *hal_tcl_desc; + uint8_t pool_id; + uint8_t hal_ring_id; + int ret, msdu_id, off; + uint32_t ring_selector = 0; + uint8_t ring_map = 0; + + if (test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags)) { + m_freem(m); + return ESHUTDOWN; + } +#if 0 + if (unlikely(!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && + !ieee80211_is_data(hdr->frame_control))) + return -ENOTSUPP; +#endif + pool_id = 0; + ring_selector = 0; + + ti.ring_id = ring_selector % sc->hw_params.max_tx_ring; + ti.rbm_id = sc->hw_params.hal_params->tcl2wbm_rbm_map[ti.ring_id].rbm_id; + + ring_map |= (1 << ti.ring_id); + + tx_ring = &dp->tx_ring[ti.ring_id]; + + if (tx_ring->queued >= sc->hw_params.tx_ring_size) { + m_freem(m); + return ENOSPC; + } + + msdu_id = tx_ring->cur; + tx_data = &tx_ring->data[msdu_id]; + if (tx_data->m != NULL) { + m_freem(m); + return ENOSPC; + } + + ti.desc_id = FIELD_PREP(DP_TX_DESC_ID_MAC_ID, pdev_id) | + FIELD_PREP(DP_TX_DESC_ID_MSDU_ID, msdu_id) | + FIELD_PREP(DP_TX_DESC_ID_POOL_ID, pool_id); + ti.encap_type = qwz_dp_tx_get_encap_type(sc); + + ti.meta_data_flags = arvif->tcl_metadata; + + if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && + ti.encap_type == HAL_TCL_ENCAP_TYPE_RAW) { + k = ieee80211_get_txkey(ic, wh, ni); + if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, sc->sc_flags)) { + ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN; + } else { + switch (k->k_cipher) { + case IEEE80211_CIPHER_CCMP: + ti.encrypt_type = HAL_ENCRYPT_TYPE_CCMP_128; + if (m_makespace(m, m->m_pkthdr.len, + IEEE80211_CCMP_MICLEN, &off) == NULL) { + m_freem(m); + return ENOSPC; + } + break; + case IEEE80211_CIPHER_TKIP: + ti.encrypt_type = HAL_ENCRYPT_TYPE_TKIP_MIC; + if (m_makespace(m, m->m_pkthdr.len, + IEEE80211_TKIP_MICLEN, &off) == NULL) { + m_freem(m); + return ENOSPC; + } + break; + default: + ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN; + break; + } + } + + if (ti.encrypt_type == HAL_ENCRYPT_TYPE_OPEN) { + /* Using software crypto. */ + if ((m = ieee80211_encrypt(ic, m, k)) == NULL) + return ENOBUFS; + /* 802.11 header may have moved. */ + wh = mtod(m, struct ieee80211_frame *); + } + } + + ti.addr_search_flags = arvif->hal_addr_search_flags; + ti.search_type = arvif->search_type; + ti.type = HAL_TCL_DESC_TYPE_BUFFER; + ti.pkt_offset = 0; + ti.lmac_id = qwz_hw_get_mac_from_pdev_id(sc, pdev_id); + ti.bss_ast_hash = arvif->ast_hash; + ti.bss_ast_idx = arvif->ast_idx; + ti.dscp_tid_tbl_idx = 0; +#if 0 + if (likely(skb->ip_summed == CHECKSUM_PARTIAL && + ti.encap_type != HAL_TCL_ENCAP_TYPE_RAW)) { + ti.flags0 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_IP4_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_UDP4_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_UDP6_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_TCP4_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_TCP6_CKSUM_EN, 1); + } + + if (ieee80211_vif_is_mesh(arvif->vif)) + ti.enable_mesh = true; +#endif + ti.flags1 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE, 1); + + ti.tid = qwz_dp_tx_get_tid(m); +#if 0 + switch (ti.encap_type) { + case HAL_TCL_ENCAP_TYPE_NATIVE_WIFI: + ath12k_dp_tx_encap_nwifi(skb); + break; + case HAL_TCL_ENCAP_TYPE_RAW: + if (!test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags)) { + ret = -EINVAL; + goto fail_remove_idr; + } + break; + case HAL_TCL_ENCAP_TYPE_ETHERNET: + /* no need to encap */ + break; + case HAL_TCL_ENCAP_TYPE_802_3: + default: + /* TODO: Take care of other encap modes as well */ + ret = -EINVAL; + atomic_inc(&ab->soc_stats.tx_err.misc_fail); + goto fail_remove_idr; + } +#endif + ret = bus_dmamap_load_mbuf(sc->sc_dmat, tx_data->map, + m, BUS_DMA_WRITE | BUS_DMA_NOWAIT); + if (ret && ret != EFBIG) { + printf("%s: failed to map Tx buffer: %d\n", + sc->sc_dev.dv_xname, ret); + m_freem(m); + return ret; + } + if (ret) { + /* Too many DMA segments, linearize mbuf. */ + if (m_defrag(m, M_DONTWAIT)) { + m_freem(m); + return ENOBUFS; + } + ret = bus_dmamap_load_mbuf(sc->sc_dmat, tx_data->map, m, + BUS_DMA_NOWAIT | BUS_DMA_WRITE); + if (ret) { + printf("%s: failed to map Tx buffer: %d\n", + sc->sc_dev.dv_xname, ret); + m_freem(m); + return ret; + } + } + ti.paddr = tx_data->map->dm_segs[0].ds_addr; + + ti.data_len = m->m_pkthdr.len; + + hal_ring_id = tx_ring->tcl_data_ring.ring_id; + tcl_ring = &sc->hal.srng_list[hal_ring_id]; +#ifdef notyet + spin_lock_bh(&tcl_ring->lock); +#endif + qwz_hal_srng_access_begin(sc, tcl_ring); + + hal_tcl_desc = (void *)qwz_hal_srng_src_get_next_entry(sc, tcl_ring); + if (!hal_tcl_desc) { + /* NOTE: It is highly unlikely we'll be running out of tcl_ring + * desc because the desc is directly enqueued onto hw queue. + */ + qwz_hal_srng_access_end(sc, tcl_ring); +#if 0 + ab->soc_stats.tx_err.desc_na[ti.ring_id]++; +#endif +#ifdef notyet + spin_unlock_bh(&tcl_ring->lock); +#endif + bus_dmamap_unload(sc->sc_dmat, tx_data->map); + m_freem(m); + return ENOMEM; + } + + tx_data->m = m; + tx_data->ni = ni; + + qwz_hal_tx_cmd_desc_setup(sc, + hal_tcl_desc + sizeof(struct hal_tlv_hdr), &ti); + + qwz_hal_srng_access_end(sc, tcl_ring); + + qwz_dp_shadow_start_timer(sc, tcl_ring, &dp->tx_ring_timer[ti.ring_id]); +#ifdef notyet + spin_unlock_bh(&tcl_ring->lock); +#endif + tx_ring->queued++; + tx_ring->cur = (tx_ring->cur + 1) % sc->hw_params.tx_ring_size; + + if (tx_ring->queued >= sc->hw_params.tx_ring_size - 1) + sc->qfullmsk |= (1 << ti.ring_id); + + return 0; +} + +int +qwz_mac_station_remove(struct qwz_softc *sc, struct qwz_vif *arvif, + uint8_t pdev_id, struct ieee80211_node *ni) +{ + struct qwz_node *nq = (struct qwz_node *)ni; + struct ath12k_peer *peer = &nq->peer; + int ret; + + qwz_peer_rx_tid_cleanup(sc, peer); + + ret = qwz_peer_delete(sc, arvif->vdev_id, pdev_id, ni->ni_macaddr); + if (ret) { + printf("%s: unable to delete BSS peer: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + return 0; +} + +int +qwz_mac_station_add(struct qwz_softc *sc, struct qwz_vif *arvif, + uint8_t pdev_id, struct ieee80211_node *ni) +{ + struct peer_create_params peer_param; + int ret; +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); +#endif + peer_param.vdev_id = arvif->vdev_id; + peer_param.peer_addr = ni->ni_macaddr; + peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; + + ret = qwz_peer_create(sc, arvif, pdev_id, ni, &peer_param); + if (ret) { + printf("%s: Failed to add peer: %s for VDEV: %d\n", + sc->sc_dev.dv_xname, ether_sprintf(ni->ni_macaddr), + arvif->vdev_id); + return ret; + } + + DNPRINTF(QWZ_D_MAC, "%s: Added peer: %s for VDEV: %d\n", __func__, + ether_sprintf(ni->ni_macaddr), arvif->vdev_id); + + ret = qwz_dp_peer_setup(sc, arvif->vdev_id, pdev_id, ni); + if (ret) { + printf("%s: failed to setup dp for peer %s on vdev %d (%d)\n", + sc->sc_dev.dv_xname, ether_sprintf(ni->ni_macaddr), + arvif->vdev_id, ret); + goto free_peer; + } + + return 0; + +free_peer: + qwz_peer_delete(sc, arvif->vdev_id, pdev_id, ni->ni_macaddr); + return ret; +} + +int +qwz_mac_mgmt_tx_wmi(struct qwz_softc *sc, struct qwz_vif *arvif, + uint8_t pdev_id, struct ieee80211_node *ni, struct mbuf *m) +{ + struct qwz_txmgmt_queue *txmgmt = &arvif->txmgmt; + struct qwz_tx_data *tx_data; + int buf_id; + int ret; + + buf_id = txmgmt->cur; + + DNPRINTF(QWZ_D_MAC, "%s: tx mgmt frame, buf id %d\n", __func__, buf_id); + + if (txmgmt->queued >= nitems(txmgmt->data)) + return ENOSPC; + + tx_data = &txmgmt->data[buf_id]; +#if 0 + if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) { + if ((ieee80211_is_action(hdr->frame_control) || + ieee80211_is_deauth(hdr->frame_control) || + ieee80211_is_disassoc(hdr->frame_control)) && + ieee80211_has_protected(hdr->frame_control)) { + skb_put(skb, IEEE80211_CCMP_MIC_LEN); + } + } +#endif + ret = bus_dmamap_load_mbuf(sc->sc_dmat, tx_data->map, + m, BUS_DMA_WRITE | BUS_DMA_NOWAIT); + if (ret && ret != EFBIG) { + printf("%s: failed to map mgmt Tx buffer: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + if (ret) { + /* Too many DMA segments, linearize mbuf. */ + if (m_defrag(m, M_DONTWAIT)) { + m_freem(m); + return ENOBUFS; + } + ret = bus_dmamap_load_mbuf(sc->sc_dmat, tx_data->map, m, + BUS_DMA_NOWAIT | BUS_DMA_WRITE); + if (ret) { + printf("%s: failed to map mgmt Tx buffer: %d\n", + sc->sc_dev.dv_xname, ret); + m_freem(m); + return ret; + } + } + + ret = qwz_wmi_mgmt_send(sc, arvif, pdev_id, buf_id, m, tx_data); + if (ret) { + printf("%s: failed to send mgmt frame: %d\n", + sc->sc_dev.dv_xname, ret); + goto err_unmap_buf; + } + tx_data->ni = ni; + + txmgmt->cur = (txmgmt->cur + 1) % nitems(txmgmt->data); + txmgmt->queued++; + + if (txmgmt->queued >= nitems(txmgmt->data) - 1) + sc->qfullmsk |= (1U << QWZ_MGMT_QUEUE_ID); + + return 0; + +err_unmap_buf: + bus_dmamap_unload(sc->sc_dmat, tx_data->map); + return ret; +} + +void +qwz_wmi_start_scan_init(struct qwz_softc *sc, struct scan_req_params *arg) +{ + /* setup commonly used values */ + arg->scan_req_id = 1; + if (sc->state_11d == ATH12K_11D_PREPARING) + arg->scan_priority = WMI_SCAN_PRIORITY_MEDIUM; + else + arg->scan_priority = WMI_SCAN_PRIORITY_LOW; + arg->dwell_time_active = 50; + arg->dwell_time_active_2g = 0; + arg->dwell_time_passive = 150; + arg->dwell_time_active_6g = 40; + arg->dwell_time_passive_6g = 30; + arg->min_rest_time = 50; + arg->max_rest_time = 500; + arg->repeat_probe_time = 0; + arg->probe_spacing_time = 0; + arg->idle_time = 0; + arg->max_scan_time = 20000; + arg->probe_delay = 5; + arg->notify_scan_events = WMI_SCAN_EVENT_STARTED | + WMI_SCAN_EVENT_COMPLETED | WMI_SCAN_EVENT_BSS_CHANNEL | + WMI_SCAN_EVENT_FOREIGN_CHAN | WMI_SCAN_EVENT_DEQUEUED; + arg->scan_flags |= WMI_SCAN_CHAN_STAT_EVENT; + + if (isset(sc->wmi.svc_map, + WMI_TLV_SERVICE_PASSIVE_SCAN_START_TIME_ENHANCE)) + arg->scan_ctrl_flags_ext |= + WMI_SCAN_FLAG_EXT_PASSIVE_SCAN_START_TIME_ENHANCE; + + arg->num_bssid = 1; + + /* fill bssid_list[0] with 0xff, otherwise bssid and RA will be + * ZEROs in probe request + */ + IEEE80211_ADDR_COPY(arg->bssid_list[0].addr, etheranyaddr); +} + +int +qwz_wmi_set_peer_param(struct qwz_softc *sc, uint8_t *peer_addr, + uint32_t vdev_id, uint32_t pdev_id, uint32_t param_id, uint32_t param_val) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_peer_set_param_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_peer_set_param_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_SET_PARAM_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + IEEE80211_ADDR_COPY(cmd->peer_macaddr.addr, peer_addr); + cmd->vdev_id = vdev_id; + cmd->param_id = param_id; + cmd->param_value = param_val; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_PEER_SET_PARAM_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send WMI_PEER_SET_PARAM cmd\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + return ret; + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd peer set param vdev %d peer %s " + "set param %d value %d\n", __func__, vdev_id, + ether_sprintf(peer_addr), param_id, param_val); + + return 0; +} + +int +qwz_wmi_peer_rx_reorder_queue_setup(struct qwz_softc *sc, int vdev_id, + int pdev_id, uint8_t *addr, uint64_t paddr, uint8_t tid, + uint8_t ba_window_size_valid, uint32_t ba_window_size) +{ + struct qwz_pdev_wmi *wmi = &sc->wmi.wmi[pdev_id]; + struct wmi_peer_reorder_queue_setup_cmd *cmd; + struct mbuf *m; + int ret; + + m = qwz_wmi_alloc_mbuf(sizeof(*cmd)); + if (!m) + return ENOMEM; + + cmd = (struct wmi_peer_reorder_queue_setup_cmd *)(mtod(m, uint8_t *) + + sizeof(struct ath12k_htc_hdr) + sizeof(struct wmi_cmd_hdr)); + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_REORDER_QUEUE_SETUP_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + IEEE80211_ADDR_COPY(cmd->peer_macaddr.addr, addr); + cmd->vdev_id = vdev_id; + cmd->tid = tid; + cmd->queue_ptr_lo = paddr & 0xffffffff; + cmd->queue_ptr_hi = paddr >> 32; + cmd->queue_no = tid; + cmd->ba_window_size_valid = ba_window_size_valid; + cmd->ba_window_size = ba_window_size; + + ret = qwz_wmi_cmd_send(wmi, m, WMI_PEER_REORDER_QUEUE_SETUP_CMDID); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to send " + "WMI_PEER_REORDER_QUEUE_SETUP\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + } + + DNPRINTF(QWZ_D_WMI, "%s: cmd peer reorder queue setup addr %s " + "vdev_id %d tid %d\n", __func__, ether_sprintf(addr), vdev_id, tid); + + return ret; +} + +enum ath12k_spectral_mode +qwz_spectral_get_mode(struct qwz_softc *sc) +{ +#if 0 + if (sc->spectral.enabled) + return ar->spectral.mode; + else +#endif + return ATH12K_SPECTRAL_DISABLED; +} + +void +qwz_spectral_reset_buffer(struct qwz_softc *sc) +{ + printf("%s: not implemented\n", __func__); +} + +int +qwz_scan_stop(struct qwz_softc *sc) +{ + struct scan_cancel_param arg = { + .req_type = WLAN_SCAN_CANCEL_SINGLE, + .scan_id = ATH12K_SCAN_ID, + }; + int ret; +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); +#endif + /* TODO: Fill other STOP Params */ + arg.pdev_id = 0; /* TODO: derive pdev ID somehow? */ + arg.vdev_id = sc->scan.vdev_id; + + ret = qwz_wmi_send_scan_stop_cmd(sc, &arg); + if (ret) { + printf("%s: failed to stop wmi scan: %d\n", + sc->sc_dev.dv_xname, ret); + goto out; + } + + while (sc->scan.state != ATH12K_SCAN_IDLE) { + ret = tsleep_nsec(&sc->scan.state, 0, "qwzscstop", + SEC_TO_NSEC(3)); + if (ret) { + printf("%s: scan stop timeout\n", sc->sc_dev.dv_xname); + break; + } + } +out: + /* Scan state should be updated upon scan completion but in case + * firmware fails to deliver the event (for whatever reason) it is + * desired to clean up scan state anyway. Firmware may have just + * dropped the scan completion event delivery due to transport pipe + * being overflown with data and/or it can recover on its own before + * next scan request is submitted. + */ +#ifdef notyet + spin_lock_bh(&ar->data_lock); +#endif + if (sc->scan.state != ATH12K_SCAN_IDLE) + qwz_mac_scan_finish(sc); +#ifdef notyet + spin_unlock_bh(&ar->data_lock); +#endif + return ret; +} + +void +qwz_scan_timeout(void *arg) +{ + struct qwz_softc *sc = arg; + int s = splnet(); + +#ifdef notyet + mutex_lock(&ar->conf_mutex); +#endif + printf("%s\n", __func__); + qwz_scan_abort(sc); +#ifdef notyet + mutex_unlock(&ar->conf_mutex); +#endif + splx(s); +} + +int +qwz_start_scan(struct qwz_softc *sc, struct scan_req_params *arg) +{ + int ret; + unsigned long timeout = 1; +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); +#endif + if (qwz_spectral_get_mode(sc) == ATH12K_SPECTRAL_BACKGROUND) + qwz_spectral_reset_buffer(sc); + + ret = qwz_wmi_send_scan_start_cmd(sc, arg); + if (ret) + return ret; + + if (isset(sc->wmi.svc_map, WMI_TLV_SERVICE_11D_OFFLOAD)) { + timeout = 5; +#if 0 + if (ar->supports_6ghz) + timeout += 5 * HZ; +#endif + } + + while (sc->scan.state == ATH12K_SCAN_STARTING) { + ret = tsleep_nsec(&sc->scan.state, 0, "qwzscan", + SEC_TO_NSEC(timeout)); + if (ret) { + printf("%s: scan start timeout\n", sc->sc_dev.dv_xname); + qwz_scan_stop(sc); + break; + } + } + +#ifdef notyet + spin_lock_bh(&ar->data_lock); + spin_unlock_bh(&ar->data_lock); +#endif + return ret; +} + +#define ATH12K_MAC_SCAN_CMD_EVT_OVERHEAD 200 /* in msecs */ + +int +qwz_scan(struct qwz_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct qwz_vif *arvif = TAILQ_FIRST(&sc->vif_list); + struct scan_req_params *arg = NULL; + struct ieee80211_channel *chan, *lastc; + int ret = 0, num_channels, i; + uint32_t scan_timeout; + + if (arvif == NULL) { + printf("%s: no vdev found\n", sc->sc_dev.dv_xname); + return EINVAL; + } + + /* + * TODO Will we need separate scan iterations on devices with + * multiple radios? + */ + if (sc->num_radios > 1) + printf("%s: TODO: only scanning with first vdev\n", __func__); + + /* Firmwares advertising the support of triggering 11D algorithm + * on the scan results of a regular scan expects driver to send + * WMI_11D_SCAN_START_CMDID before sending WMI_START_SCAN_CMDID. + * With this feature, separate 11D scan can be avoided since + * regdomain can be determined with the scan results of the + * regular scan. + */ + if (sc->state_11d == ATH12K_11D_PREPARING && + isset(sc->wmi.svc_map, WMI_TLV_SERVICE_SUPPORT_11D_FOR_HOST_SCAN)) + qwz_mac_11d_scan_start(sc, arvif); +#ifdef notyet + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); +#endif + switch (sc->scan.state) { + case ATH12K_SCAN_IDLE: + sc->scan.started = 0; + sc->scan.completed = 0; + sc->scan.state = ATH12K_SCAN_STARTING; + sc->scan.is_roc = 0; + sc->scan.vdev_id = arvif->vdev_id; + ret = 0; + break; + case ATH12K_SCAN_STARTING: + case ATH12K_SCAN_RUNNING: + case ATH12K_SCAN_ABORTING: + ret = EBUSY; + break; + } +#ifdef notyet + spin_unlock_bh(&ar->data_lock); +#endif + if (ret) + goto exit; + + arg = malloc(sizeof(*arg), M_DEVBUF, M_ZERO | M_NOWAIT); + if (!arg) { + ret = ENOMEM; + goto exit; + } + + qwz_wmi_start_scan_init(sc, arg); + arg->vdev_id = arvif->vdev_id; + arg->scan_id = ATH12K_SCAN_ID; + + if (ic->ic_des_esslen != 0) { + arg->num_ssids = 1; + arg->ssid[0].length = ic->ic_des_esslen; + memcpy(&arg->ssid[0].ssid, ic->ic_des_essid, + ic->ic_des_esslen); + } else + arg->scan_flags |= WMI_SCAN_FLAG_PASSIVE; + + lastc = &ic->ic_channels[IEEE80211_CHAN_MAX]; + num_channels = 0; + for (chan = &ic->ic_channels[1]; chan <= lastc; chan++) { + if (chan->ic_flags == 0) + continue; + num_channels++; + } + if (num_channels) { + arg->num_chan = num_channels; + arg->chan_list = mallocarray(arg->num_chan, + sizeof(*arg->chan_list), M_DEVBUF, M_NOWAIT | M_ZERO); + + if (!arg->chan_list) { + ret = ENOMEM; + goto exit; + } + + i = 0; + for (chan = &ic->ic_channels[1]; chan <= lastc; chan++) { + if (chan->ic_flags == 0) + continue; + if (isset(sc->wmi.svc_map, + WMI_TLV_SERVICE_SCAN_CONFIG_PER_CHANNEL)) { + arg->chan_list[i++] = chan->ic_freq & + WMI_SCAN_CONFIG_PER_CHANNEL_MASK; +#if 0 + /* If NL80211_SCAN_FLAG_COLOCATED_6GHZ is set in scan + * flags, then scan all PSC channels in 6 GHz band and + * those non-PSC channels where RNR IE is found during + * the legacy 2.4/5 GHz scan. + * If NL80211_SCAN_FLAG_COLOCATED_6GHZ is not set, + * then all channels in 6 GHz will be scanned. + */ + if (req->channels[i]->band == NL80211_BAND_6GHZ && + req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ && + !cfg80211_channel_is_psc(req->channels[i])) + arg->chan_list[i] |= + WMI_SCAN_CH_FLAG_SCAN_ONLY_IF_RNR_FOUND; +#endif + } else { + arg->chan_list[i++] = chan->ic_freq; + } + } + } +#if 0 + if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + arg->scan_f_add_spoofed_mac_in_probe = 1; + ether_addr_copy(arg->mac_addr.addr, req->mac_addr); + ether_addr_copy(arg->mac_mask.addr, req->mac_addr_mask); + } +#endif + scan_timeout = 5000; + + /* Add a margin to account for event/command processing */ + scan_timeout += ATH12K_MAC_SCAN_CMD_EVT_OVERHEAD; + + ret = qwz_start_scan(sc, arg); + if (ret) { + if (ret != ESHUTDOWN) { + printf("%s: failed to start hw scan: %d\n", + sc->sc_dev.dv_xname, ret); + } +#ifdef notyet + spin_lock_bh(&ar->data_lock); +#endif + sc->scan.state = ATH12K_SCAN_IDLE; +#ifdef notyet + spin_unlock_bh(&ar->data_lock); +#endif + } else { + /* + * The current mode might have been fixed during association. + * Ensure all channels get scanned. + */ + if (IFM_SUBTYPE(ic->ic_media.ifm_cur->ifm_media) == IFM_AUTO) + ieee80211_setmode(ic, IEEE80211_MODE_AUTO); + } +#if 0 + timeout_add_msec(&sc->scan.timeout, scan_timeout); +#endif +exit: + if (arg) { + free(arg->chan_list, M_DEVBUF, + arg->num_chan * sizeof(*arg->chan_list)); +#if 0 + kfree(arg->extraie.ptr); +#endif + free(arg, M_DEVBUF, sizeof(*arg)); + } +#ifdef notyet + mutex_unlock(&ar->conf_mutex); +#endif + if (sc->state_11d == ATH12K_11D_PREPARING) + qwz_mac_11d_scan_start(sc, arvif); + + return ret; +} + +void +qwz_scan_abort(struct qwz_softc *sc) +{ + int ret; +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); +#endif + switch (sc->scan.state) { + case ATH12K_SCAN_IDLE: + /* This can happen if timeout worker kicked in and called + * abortion while scan completion was being processed. + */ + break; + case ATH12K_SCAN_STARTING: + case ATH12K_SCAN_ABORTING: + printf("%s: refusing scan abortion due to invalid " + "scan state: %d\n", sc->sc_dev.dv_xname, sc->scan.state); + break; + case ATH12K_SCAN_RUNNING: + sc->scan.state = ATH12K_SCAN_ABORTING; +#ifdef notyet + spin_unlock_bh(&ar->data_lock); +#endif + ret = qwz_scan_stop(sc); + if (ret) + printf("%s: failed to abort scan: %d\n", + sc->sc_dev.dv_xname, ret); +#ifdef notyet + spin_lock_bh(&ar->data_lock); +#endif + break; + } +#ifdef notyet + spin_unlock_bh(&ar->data_lock); +#endif +} + +/* + * Find a pdev which corresponds to a given channel. + * This doesn't exactly match the semantics of the Linux driver + * but because OpenBSD does not (yet) implement multi-bss mode + * we can assume that only one PHY will be active in either the + * 2 GHz or the 5 GHz band. + */ +struct qwz_pdev * +qwz_get_pdev_for_chan(struct qwz_softc *sc, struct ieee80211_channel *chan) +{ + struct qwz_pdev *pdev; + int i; + + for (i = 0; i < sc->num_radios; i++) { + if ((sc->pdevs_active & (1 << i)) == 0) + continue; + + pdev = &sc->pdevs[i]; + if (IEEE80211_IS_CHAN_2GHZ(chan) && + (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP)) + return pdev; + if (IEEE80211_IS_CHAN_5GHZ(chan) && + (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP)) + return pdev; + } + + return NULL; +} + +void +qwz_recalculate_mgmt_rate(struct qwz_softc *sc, struct ieee80211_node *ni, + uint32_t vdev_id, uint32_t pdev_id) +{ + struct ieee80211com *ic = &sc->sc_ic; + int hw_rate_code; + uint32_t vdev_param; + int bitrate; + int ret; +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); +#endif + bitrate = ieee80211_min_basic_rate(ic); + hw_rate_code = qwz_mac_get_rate_hw_value(ic, ni, bitrate); + if (hw_rate_code < 0) { + DPRINTF("%s: bitrate not supported %d\n", + sc->sc_dev.dv_xname, bitrate); + return; + } + + vdev_param = WMI_VDEV_PARAM_MGMT_RATE; + ret = qwz_wmi_vdev_set_param_cmd(sc, vdev_id, pdev_id, + vdev_param, hw_rate_code); + if (ret) + printf("%s: failed to set mgmt tx rate\n", + sc->sc_dev.dv_xname); +#if 0 + /* For WCN6855, firmware will clear this param when vdev starts, hence + * cache it here so that we can reconfigure it once vdev starts. + */ + ab->hw_rate_code = hw_rate_code; +#endif + vdev_param = WMI_VDEV_PARAM_BEACON_RATE; + ret = qwz_wmi_vdev_set_param_cmd(sc, vdev_id, pdev_id, vdev_param, + hw_rate_code); + if (ret) + printf("%s: failed to set beacon tx rate\n", + sc->sc_dev.dv_xname); +} + +int +qwz_auth(struct qwz_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = ic->ic_bss; + uint32_t param_id; + struct qwz_vif *arvif; + struct qwz_pdev *pdev; + int ret; + + arvif = TAILQ_FIRST(&sc->vif_list); + if (arvif == NULL) { + printf("%s: no vdev found\n", sc->sc_dev.dv_xname); + return EINVAL; + } + + pdev = qwz_get_pdev_for_chan(sc, ni->ni_chan); + if (pdev == NULL) { + printf("%s: no pdev found for channel %d\n", + sc->sc_dev.dv_xname, ieee80211_chan2ieee(ic, ni->ni_chan)); + return EINVAL; + } + + param_id = WMI_VDEV_PARAM_BEACON_INTERVAL; + ret = qwz_wmi_vdev_set_param_cmd(sc, arvif->vdev_id, pdev->pdev_id, + param_id, ni->ni_intval); + if (ret) { + printf("%s: failed to set beacon interval for VDEV: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id); + return ret; + } + + qwz_recalculate_mgmt_rate(sc, ni, arvif->vdev_id, pdev->pdev_id); + ni->ni_txrate = 0; + + ret = qwz_mac_station_add(sc, arvif, pdev->pdev_id, ni); + if (ret) + return ret; + + /* Start vdev. */ + ret = qwz_mac_vdev_start(sc, arvif, pdev->pdev_id); + if (ret) { + printf("%s: failed to start MAC for VDEV: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id); + return ret; + } + + /* + * WCN6855 firmware clears basic-rate parameters when vdev starts. + * Set it once more. + */ + qwz_recalculate_mgmt_rate(sc, ni, arvif->vdev_id, pdev->pdev_id); + + return ret; +} + +int +qwz_deauth(struct qwz_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = ic->ic_bss; + struct qwz_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */ + uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */ + int ret; + + ret = qwz_mac_vdev_stop(sc, arvif, pdev_id); + if (ret) { + printf("%s: unable to stop vdev vdev_id %d: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + return ret; + } + + ret = qwz_wmi_set_peer_param(sc, ni->ni_macaddr, arvif->vdev_id, + pdev_id, WMI_PEER_AUTHORIZE, 0); + if (ret) { + printf("%s: unable to deauthorize BSS peer: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + ret = qwz_mac_station_remove(sc, arvif, pdev_id, ni); + if (ret) + return ret; + + DNPRINTF(QWZ_D_MAC, "%s: disassociated from bssid %s aid %d\n", + __func__, ether_sprintf(ni->ni_bssid), arvif->aid); + + return 0; +} + +void +qwz_peer_assoc_h_basic(struct qwz_softc *sc, struct qwz_vif *arvif, + struct ieee80211_node *ni, struct peer_assoc_params *arg) +{ +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); +#endif + + IEEE80211_ADDR_COPY(arg->peer_mac, ni->ni_macaddr); + arg->vdev_id = arvif->vdev_id; + arg->peer_associd = ni->ni_associd; + arg->auth_flag = 1; + arg->peer_listen_intval = ni->ni_intval; + arg->peer_nss = 1; + arg->peer_caps = ni->ni_capinfo; +} + +void +qwz_peer_assoc_h_crypto(struct qwz_softc *sc, struct qwz_vif *arvif, + struct ieee80211_node *ni, struct peer_assoc_params *arg) +{ + struct ieee80211com *ic = &sc->sc_ic; + + if (ic->ic_flags & IEEE80211_F_RSNON) { + arg->need_ptk_4_way = 1; + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) + arg->need_gtk_2_way = 1; + } +#if 0 + if (sta->mfp) { + /* TODO: Need to check if FW supports PMF? */ + arg->is_pmf_enabled = true; + } +#endif +} + +int +qwz_mac_rate_is_cck(uint8_t rate) +{ + return (rate == 2 || rate == 4 || rate == 11 || rate == 22); +} + +void +qwz_peer_assoc_h_rates(struct ieee80211_node *ni, struct peer_assoc_params *arg) +{ + struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates; + struct ieee80211_rateset *rs = &ni->ni_rates; + int i; + + for (i = 0, rateset->num_rates = 0; + i < rs->rs_nrates && rateset->num_rates < nitems(rateset->rates); + i++, rateset->num_rates++) { + uint8_t rate = rs->rs_rates[i] & IEEE80211_RATE_VAL; + if (qwz_mac_rate_is_cck(rate)) + rate |= 0x80; + rateset->rates[rateset->num_rates] = rate; + } +} + +void +qwz_peer_assoc_h_phymode(struct qwz_softc *sc, struct ieee80211_node *ni, + struct peer_assoc_params *arg) +{ + struct ieee80211com *ic = &sc->sc_ic; + enum wmi_phy_mode phymode; + + switch (ic->ic_curmode) { + case IEEE80211_MODE_11A: + phymode = MODE_11A; + break; + case IEEE80211_MODE_11B: + phymode = MODE_11B; + break; + case IEEE80211_MODE_11G: + phymode = MODE_11G; + break; + default: + phymode = MODE_UNKNOWN; + break; + } + + DNPRINTF(QWZ_D_MAC, "%s: peer %s phymode %s\n", __func__, + ether_sprintf(ni->ni_macaddr), qwz_wmi_phymode_str(phymode)); + + arg->peer_phymode = phymode; +} + +void +qwz_peer_assoc_prepare(struct qwz_softc *sc, struct qwz_vif *arvif, + struct ieee80211_node *ni, struct peer_assoc_params *arg, int reassoc) +{ + memset(arg, 0, sizeof(*arg)); + + arg->peer_new_assoc = !reassoc; + qwz_peer_assoc_h_basic(sc, arvif, ni, arg); + qwz_peer_assoc_h_crypto(sc, arvif, ni, arg); + qwz_peer_assoc_h_rates(ni, arg); + qwz_peer_assoc_h_phymode(sc, ni, arg); +#if 0 + qwz_peer_assoc_h_ht(sc, arvif, ni, arg); + qwz_peer_assoc_h_vht(sc, arvif, ni, arg); + qwz_peer_assoc_h_he(sc, arvif, ni, arg); + qwz_peer_assoc_h_he_6ghz(sc, arvif, ni, arg); + qwz_peer_assoc_h_qos(sc, arvif, ni, arg); + qwz_peer_assoc_h_smps(ni, arg); +#endif +#if 0 + arsta->peer_nss = arg->peer_nss; +#endif + /* TODO: amsdu_disable req? */ +} + +int +qwz_run(struct qwz_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = ic->ic_bss; + struct qwz_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */ + uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */ + struct peer_assoc_params peer_arg; + int ret; +#ifdef notyet + lockdep_assert_held(&ar->conf_mutex); +#endif + + DNPRINTF(QWZ_D_MAC, "%s: vdev %i assoc bssid %pM aid %d\n", + __func__, arvif->vdev_id, arvif->bssid, arvif->aid); + + qwz_peer_assoc_prepare(sc, arvif, ni, &peer_arg, 0); + + peer_arg.is_assoc = 1; + + sc->peer_assoc_done = 0; + ret = qwz_wmi_send_peer_assoc_cmd(sc, pdev_id, &peer_arg); + if (ret) { + printf("%s: failed to run peer assoc for %s vdev %i: %d\n", + sc->sc_dev.dv_xname, ether_sprintf(ni->ni_macaddr), + arvif->vdev_id, ret); + return ret; + } + + while (!sc->peer_assoc_done) { + ret = tsleep_nsec(&sc->peer_assoc_done, 0, "qwzassoc", + SEC_TO_NSEC(1)); + if (ret) { + printf("%s: failed to get peer assoc conf event " + "for %s vdev %i\n", sc->sc_dev.dv_xname, + ether_sprintf(ni->ni_macaddr), arvif->vdev_id); + return ret; + } + } +#if 0 + ret = ath12k_setup_peer_smps(ar, arvif, sta->addr, + &sta->deflink.ht_cap, + le16_to_cpu(sta->deflink.he_6ghz_capa.capa)); + if (ret) { + ath12k_warn(ar->ab, "failed to setup peer SMPS for vdev %d: %d\n", + arvif->vdev_id, ret); + return ret; + } + + if (!ath12k_mac_vif_recalc_sta_he_txbf(ar, vif, &he_cap)) { + ath12k_warn(ar->ab, "failed to recalc he txbf for vdev %i on bss %pM\n", + arvif->vdev_id, bss_conf->bssid); + return; + } + + WARN_ON(arvif->is_up); +#endif + + arvif->aid = ni->ni_associd; + IEEE80211_ADDR_COPY(arvif->bssid, ni->ni_bssid); + + ret = qwz_wmi_vdev_up(sc, arvif->vdev_id, pdev_id, arvif->aid, + arvif->bssid, NULL, 0, 0); + if (ret) { + printf("%s: failed to set vdev %d up: %d\n", + sc->sc_dev.dv_xname, arvif->vdev_id, ret); + return ret; + } + + arvif->is_up = 1; +#if 0 + arvif->rekey_data.enable_offload = 0; +#endif + + DNPRINTF(QWZ_D_MAC, "%s: vdev %d up (associated) bssid %s aid %d\n", + __func__, arvif->vdev_id, ether_sprintf(ni->ni_bssid), arvif->aid); + + ret = qwz_wmi_set_peer_param(sc, ni->ni_macaddr, arvif->vdev_id, + pdev_id, WMI_PEER_AUTHORIZE, 1); + if (ret) { + printf("%s: unable to authorize BSS peer: %d\n", + sc->sc_dev.dv_xname, ret); + return ret; + } + + /* Enable "ext" IRQs for datapath. */ + sc->ops.irq_enable(sc); + + return 0; +} + +int +qwz_run_stop(struct qwz_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct qwz_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */ + uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */ + struct qwz_node *nq = (void *)ic->ic_bss; + int ret; + + sc->ops.irq_disable(sc); + + if (ic->ic_opmode == IEEE80211_M_STA) { + ic->ic_bss->ni_txrate = 0; + nq->flags = 0; + } + + ret = qwz_wmi_vdev_down(sc, arvif->vdev_id, pdev_id); + if (ret) + return ret; + + arvif->is_up = 0; + + DNPRINTF(QWZ_D_MAC, "%s: vdev %d down\n", __func__, arvif->vdev_id); + + return 0; +} + +#if NBPFILTER > 0 +void +qwz_radiotap_attach(struct qwz_softc *sc) +{ + bpfattach(&sc->sc_drvbpf, &sc->sc_ic.ic_if, DLT_IEEE802_11_RADIO, + sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN); + + sc->sc_rxtap_len = sizeof(sc->sc_rxtapu); + sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); + sc->sc_rxtap.wr_ihdr.it_present = htole32(IWX_RX_RADIOTAP_PRESENT); + + sc->sc_txtap_len = sizeof(sc->sc_txtapu); + sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); + sc->sc_txtap.wt_ihdr.it_present = htole32(IWX_TX_RADIOTAP_PRESENT); +} +#endif + +int +qwz_attach(struct qwz_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + int error, i; + + task_set(&sc->init_task, qwz_init_task, sc); + task_set(&sc->newstate_task, qwz_newstate_task, sc); + task_set(&sc->setkey_task, qwz_setkey_task, sc); + timeout_set_proc(&sc->scan.timeout, qwz_scan_timeout, sc); +#if NBPFILTER > 0 + qwz_radiotap_attach(sc); +#endif + for (i = 0; i < nitems(sc->pdevs); i++) + sc->pdevs[i].sc = sc; + + TAILQ_INIT(&sc->vif_list); + + error = qwz_init(ifp); + if (error) + return error; + + /* Turn device off until interface comes up. */ + qwz_core_deinit(sc); + + return 0; +} + +void +qwz_detach(struct qwz_softc *sc) +{ + if (sc->fwmem) { + qwz_dmamem_free(sc->sc_dmat, sc->fwmem); + sc->fwmem = NULL; + } + + if (sc->m3_mem) { + qwz_dmamem_free(sc->sc_dmat, sc->m3_mem); + sc->m3_mem = NULL; + } + + qwz_free_firmware(sc); +} + +struct qwz_dmamem * +qwz_dmamem_alloc(bus_dma_tag_t dmat, bus_size_t size, bus_size_t align) +{ + struct qwz_dmamem *adm; + int nsegs; + + adm = malloc(sizeof(*adm), M_DEVBUF, M_NOWAIT | M_ZERO); + if (adm == NULL) + return NULL; + adm->size = size; + + if (bus_dmamap_create(dmat, size, 1, size, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &adm->map) != 0) + goto admfree; + + if (bus_dmamem_alloc_range(dmat, size, align, 0, &adm->seg, 1, + &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO, 0, 0xffffffff) != 0) + goto destroy; + + if (bus_dmamem_map(dmat, &adm->seg, nsegs, size, + &adm->kva, BUS_DMA_NOWAIT | BUS_DMA_COHERENT) != 0) + goto free; + + if (bus_dmamap_load_raw(dmat, adm->map, &adm->seg, nsegs, size, + BUS_DMA_NOWAIT) != 0) + goto unmap; + + bzero(adm->kva, size); + + return adm; + +unmap: + bus_dmamem_unmap(dmat, adm->kva, size); +free: + bus_dmamem_free(dmat, &adm->seg, 1); +destroy: + bus_dmamap_destroy(dmat, adm->map); +admfree: + free(adm, M_DEVBUF, sizeof(*adm)); + + return NULL; +} + +void +qwz_dmamem_free(bus_dma_tag_t dmat, struct qwz_dmamem *adm) +{ + bus_dmamem_unmap(dmat, adm->kva, adm->size); + bus_dmamem_free(dmat, &adm->seg, 1); + bus_dmamap_destroy(dmat, adm->map); + free(adm, M_DEVBUF, sizeof(*adm)); +} + +int +qwz_activate(struct device *self, int act) +{ + struct qwz_softc *sc = (struct qwz_softc *)self; + struct ifnet *ifp = &sc->sc_ic.ic_if; + int err = 0; + + switch (act) { + case DVACT_QUIESCE: + if (ifp->if_flags & IFF_RUNNING) { + rw_enter_write(&sc->ioctl_rwl); + qwz_stop(ifp); + rw_exit(&sc->ioctl_rwl); + } + break; + case DVACT_RESUME: + break; + case DVACT_WAKEUP: + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP) { + err = qwz_init(ifp); + if (err) + printf("%s: could not initialize hardware\n", + sc->sc_dev.dv_xname); + } + break; + } + + return 0; +} diff --git a/sys/dev/ic/qwzreg.h b/sys/dev/ic/qwzreg.h new file mode 100644 index 000000000..e0cf2645d --- /dev/null +++ b/sys/dev/ic/qwzreg.h @@ -0,0 +1,13253 @@ +/* $OpenBSD: qwzreg.h,v 1.1 2024/08/14 14:40:46 patrick Exp $ */ + +/* + * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. + * Copyright (c) 2018-2021 The Linux Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of [Owner Organization] nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * core.h + */ + +#define ATH12K_TX_MGMT_NUM_PENDING_MAX 512 + +#define ATH12K_TX_MGMT_TARGET_MAX_SUPPORT_WMI 64 + +/* Pending management packets threshold for dropping probe responses */ +#define ATH12K_PRB_RSP_DROP_THRESHOLD ((ATH12K_TX_MGMT_TARGET_MAX_SUPPORT_WMI * 3) / 4) + +#define ATH12K_INVALID_HW_MAC_ID 0xFF +#define ATH12K_CONNECTION_LOSS_HZ (3 * HZ) + +enum ath12k_hw_rev { + ATH12K_HW_IPQ8074, + ATH12K_HW_QCA6390_HW20, + ATH12K_HW_IPQ6018_HW10, + ATH12K_HW_QCN9074_HW10, + ATH12K_HW_WCN6855_HW20, + ATH12K_HW_WCN6855_HW21, + ATH12K_HW_WCN6750_HW10, +}; + +enum ath12k_firmware_mode { + /* the default mode, standard 802.11 functionality */ + ATH12K_FIRMWARE_MODE_NORMAL, + + /* factory tests etc */ + ATH12K_FIRMWARE_MODE_FTM, + + /* Cold boot calibration */ + ATH12K_FIRMWARE_MODE_COLD_BOOT = 7, +}; + +enum ath12k_crypt_mode { + /* Only use hardware crypto engine */ + ATH12K_CRYPT_MODE_HW, + /* Only use software crypto */ + ATH12K_CRYPT_MODE_SW, +}; + +/* IPQ8074 HW channel counters frequency value in hertz */ +#define IPQ8074_CC_FREQ_HERTZ 320000 + +#define ATH12K_MIN_5G_FREQ 4150 +#define ATH12K_MIN_6G_FREQ 5925 +#define ATH12K_MAX_6G_FREQ 7115 +#define ATH12K_NUM_CHANS 102 +#define ATH12K_MAX_5G_CHAN 177 + +/* Antenna noise floor */ +#define ATH12K_DEFAULT_NOISE_FLOOR -95 + +/* + * wmi.h + */ + +#define PSOC_HOST_MAX_NUM_SS (8) + +/* defines to set Packet extension values which can be 0 us, 8 usec or 16 usec */ +#define MAX_HE_NSS 8 +#define MAX_HE_MODULATION 8 +#define MAX_HE_RU 4 +#define HE_MODULATION_NONE 7 +#define HE_PET_0_USEC 0 +#define HE_PET_8_USEC 1 +#define HE_PET_16_USEC 2 + +#define WMI_MAX_CHAINS 8 + +#define WMI_MAX_NUM_SS MAX_HE_NSS +#define WMI_MAX_NUM_RU MAX_HE_RU + +#define WMI_TLV_CMD(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_TLV_EV(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_TLV_CMD_UNSUPPORTED 0 +#define WMI_TLV_PDEV_PARAM_UNSUPPORTED 0 +#define WMI_TLV_VDEV_PARAM_UNSUPPORTED 0 + +struct wmi_cmd_hdr { + uint32_t cmd_id; +} __packed; + +struct wmi_tlv { + uint32_t header; + uint8_t value[]; +} __packed; + +#define WMI_TLV_LEN GENMASK(15, 0) +#define WMI_TLV_TAG GENMASK(31, 16) +#define TLV_HDR_SIZE sizeof(uint32_t) /* wmi_tlv.header */ + +#define WMI_CMD_HDR_CMD_ID GENMASK(23, 0) +#define WMI_MAX_MEM_REQS 32 +#define ATH12K_MAX_HW_LISTEN_INTERVAL 5 + +#define WLAN_SCAN_MAX_HINT_S_SSID 10 +#define WLAN_SCAN_MAX_HINT_BSSID 10 +#define MAX_RNR_BSS 5 + +#define WLAN_SCAN_MAX_HINT_S_SSID 10 +#define WLAN_SCAN_MAX_HINT_BSSID 10 +#define MAX_RNR_BSS 5 + +#define WLAN_SCAN_PARAMS_MAX_SSID 16 +#define WLAN_SCAN_PARAMS_MAX_BSSID 4 +#define WLAN_SCAN_PARAMS_MAX_IE_LEN 256 + +#define WMI_APPEND_TO_EXISTING_CHAN_LIST_FLAG 1 + +#define MAX_WMI_UTF_LEN 252 +#define WMI_BA_MODE_BUFFER_SIZE_256 3 + +/* + * HW mode config type replicated from FW header + * @WMI_HOST_HW_MODE_SINGLE: Only one PHY is active. + * @WMI_HOST_HW_MODE_DBS: Both PHYs are active in different bands, + * one in 2G and another in 5G. + * @WMI_HOST_HW_MODE_SBS_PASSIVE: Both PHYs are in passive mode (only rx) in + * same band; no tx allowed. + * @WMI_HOST_HW_MODE_SBS: Both PHYs are active in the same band. + * Support for both PHYs within one band is planned + * for 5G only(as indicated in WMI_MAC_PHY_CAPABILITIES), + * but could be extended to other bands in the future. + * The separation of the band between the two PHYs needs + * to be communicated separately. + * @WMI_HOST_HW_MODE_DBS_SBS: 3 PHYs, with 2 on the same band doing SBS + * as in WMI_HW_MODE_SBS, and 3rd on the other band + * @WMI_HOST_HW_MODE_DBS_OR_SBS: Two PHY with one PHY capabale of both 2G and + * 5G. It can support SBS (5G + 5G) OR DBS (5G + 2G). + * @WMI_HOST_HW_MODE_MAX: Max hw_mode_id. Used to indicate invalid mode. + */ +enum wmi_host_hw_mode_config_type { + WMI_HOST_HW_MODE_SINGLE = 0, + WMI_HOST_HW_MODE_DBS = 1, + WMI_HOST_HW_MODE_SBS_PASSIVE = 2, + WMI_HOST_HW_MODE_SBS = 3, + WMI_HOST_HW_MODE_DBS_SBS = 4, + WMI_HOST_HW_MODE_DBS_OR_SBS = 5, + + /* keep last */ + WMI_HOST_HW_MODE_MAX +}; + +/* HW mode priority values used to detect the preferred HW mode + * on the available modes. + */ +enum wmi_host_hw_mode_priority { + WMI_HOST_HW_MODE_DBS_SBS_PRI, + WMI_HOST_HW_MODE_DBS_PRI, + WMI_HOST_HW_MODE_DBS_OR_SBS_PRI, + WMI_HOST_HW_MODE_SBS_PRI, + WMI_HOST_HW_MODE_SBS_PASSIVE_PRI, + WMI_HOST_HW_MODE_SINGLE_PRI, + + /* keep last the lowest priority */ + WMI_HOST_HW_MODE_MAX_PRI +}; + +enum WMI_HOST_WLAN_BAND { + WMI_HOST_WLAN_2G_CAP = 0x1, + WMI_HOST_WLAN_5G_CAP = 0x2, + WMI_HOST_WLAN_2G_5G_CAP = WMI_HOST_WLAN_2G_CAP | WMI_HOST_WLAN_5G_CAP, +}; + +/* Parameters used for WMI_VDEV_PARAM_AUTORATE_MISC_CFG command. + * Used only for HE auto rate mode. + */ +enum { + /* HE LTF related configuration */ + WMI_HE_AUTORATE_LTF_1X = BIT(0), + WMI_HE_AUTORATE_LTF_2X = BIT(1), + WMI_HE_AUTORATE_LTF_4X = BIT(2), + + /* HE GI related configuration */ + WMI_AUTORATE_400NS_GI = BIT(8), + WMI_AUTORATE_800NS_GI = BIT(9), + WMI_AUTORATE_1600NS_GI = BIT(10), + WMI_AUTORATE_3200NS_GI = BIT(11), +}; + +enum { + WMI_HOST_VDEV_FLAGS_NON_MBSSID_AP = 0x00000001, + WMI_HOST_VDEV_FLAGS_TRANSMIT_AP = 0x00000002, + WMI_HOST_VDEV_FLAGS_NON_TRANSMIT_AP = 0x00000004, + WMI_HOST_VDEV_FLAGS_EMA_MODE = 0x00000008, + WMI_HOST_VDEV_FLAGS_SCAN_MODE_VAP = 0x00000010, +}; + +/* + * wmi command groups. + */ +enum wmi_cmd_group { + /* 0 to 2 are reserved */ + WMI_GRP_START = 0x3, + WMI_GRP_SCAN = WMI_GRP_START, + WMI_GRP_PDEV = 0x4, + WMI_GRP_VDEV = 0x5, + WMI_GRP_PEER = 0x6, + WMI_GRP_MGMT = 0x7, + WMI_GRP_BA_NEG = 0x8, + WMI_GRP_STA_PS = 0x9, + WMI_GRP_DFS = 0xa, + WMI_GRP_ROAM = 0xb, + WMI_GRP_OFL_SCAN = 0xc, + WMI_GRP_P2P = 0xd, + WMI_GRP_AP_PS = 0xe, + WMI_GRP_RATE_CTRL = 0xf, + WMI_GRP_PROFILE = 0x10, + WMI_GRP_SUSPEND = 0x11, + WMI_GRP_BCN_FILTER = 0x12, + WMI_GRP_WOW = 0x13, + WMI_GRP_RTT = 0x14, + WMI_GRP_SPECTRAL = 0x15, + WMI_GRP_STATS = 0x16, + WMI_GRP_ARP_NS_OFL = 0x17, + WMI_GRP_NLO_OFL = 0x18, + WMI_GRP_GTK_OFL = 0x19, + WMI_GRP_CSA_OFL = 0x1a, + WMI_GRP_CHATTER = 0x1b, + WMI_GRP_TID_ADDBA = 0x1c, + WMI_GRP_MISC = 0x1d, + WMI_GRP_GPIO = 0x1e, + WMI_GRP_FWTEST = 0x1f, + WMI_GRP_TDLS = 0x20, + WMI_GRP_RESMGR = 0x21, + WMI_GRP_STA_SMPS = 0x22, + WMI_GRP_WLAN_HB = 0x23, + WMI_GRP_RMC = 0x24, + WMI_GRP_MHF_OFL = 0x25, + WMI_GRP_LOCATION_SCAN = 0x26, + WMI_GRP_OEM = 0x27, + WMI_GRP_NAN = 0x28, + WMI_GRP_COEX = 0x29, + WMI_GRP_OBSS_OFL = 0x2a, + WMI_GRP_LPI = 0x2b, + WMI_GRP_EXTSCAN = 0x2c, + WMI_GRP_DHCP_OFL = 0x2d, + WMI_GRP_IPA = 0x2e, + WMI_GRP_MDNS_OFL = 0x2f, + WMI_GRP_SAP_OFL = 0x30, + WMI_GRP_OCB = 0x31, + WMI_GRP_SOC = 0x32, + WMI_GRP_PKT_FILTER = 0x33, + WMI_GRP_MAWC = 0x34, + WMI_GRP_PMF_OFFLOAD = 0x35, + WMI_GRP_BPF_OFFLOAD = 0x36, + WMI_GRP_NAN_DATA = 0x37, + WMI_GRP_PROTOTYPE = 0x38, + WMI_GRP_MONITOR = 0x39, + WMI_GRP_REGULATORY = 0x3a, + WMI_GRP_HW_DATA_FILTER = 0x3b, + WMI_GRP_WLM = 0x3c, + WMI_GRP_11K_OFFLOAD = 0x3d, + WMI_GRP_TWT = 0x3e, + WMI_GRP_MOTION_DET = 0x3f, + WMI_GRP_SPATIAL_REUSE = 0x40, +}; + + +#define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1) + +#define WMI_CMD_UNSUPPORTED 0 + +enum wmi_tlv_cmd_id { + WMI_INIT_CMDID = 0x1, + WMI_START_SCAN_CMDID = WMI_TLV_CMD(WMI_GRP_SCAN), + WMI_STOP_SCAN_CMDID, + WMI_SCAN_CHAN_LIST_CMDID, + WMI_SCAN_SCH_PRIO_TBL_CMDID, + WMI_SCAN_UPDATE_REQUEST_CMDID, + WMI_SCAN_PROB_REQ_OUI_CMDID, + WMI_SCAN_ADAPTIVE_DWELL_CONFIG_CMDID, + WMI_PDEV_SET_REGDOMAIN_CMDID = WMI_TLV_CMD(WMI_GRP_PDEV), + WMI_PDEV_SET_CHANNEL_CMDID, + WMI_PDEV_SET_PARAM_CMDID, + WMI_PDEV_PKTLOG_ENABLE_CMDID, + WMI_PDEV_PKTLOG_DISABLE_CMDID, + WMI_PDEV_SET_WMM_PARAMS_CMDID, + WMI_PDEV_SET_HT_CAP_IE_CMDID, + WMI_PDEV_SET_VHT_CAP_IE_CMDID, + WMI_PDEV_SET_DSCP_TID_MAP_CMDID, + WMI_PDEV_SET_QUIET_MODE_CMDID, + WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID, + WMI_PDEV_GET_TPC_CONFIG_CMDID, + WMI_PDEV_SET_BASE_MACADDR_CMDID, + WMI_PDEV_DUMP_CMDID, + WMI_PDEV_SET_LED_CONFIG_CMDID, + WMI_PDEV_GET_TEMPERATURE_CMDID, + WMI_PDEV_SET_LED_FLASHING_CMDID, + WMI_PDEV_SMART_ANT_ENABLE_CMDID, + WMI_PDEV_SMART_ANT_SET_RX_ANTENNA_CMDID, + WMI_PDEV_SET_ANTENNA_SWITCH_TABLE_CMDID, + WMI_PDEV_SET_CTL_TABLE_CMDID, + WMI_PDEV_SET_MIMOGAIN_TABLE_CMDID, + WMI_PDEV_FIPS_CMDID, + WMI_PDEV_GET_ANI_CCK_CONFIG_CMDID, + WMI_PDEV_GET_ANI_OFDM_CONFIG_CMDID, + WMI_PDEV_GET_NFCAL_POWER_CMDID, + WMI_PDEV_GET_TPC_CMDID, + WMI_MIB_STATS_ENABLE_CMDID, + WMI_PDEV_SET_PCL_CMDID, + WMI_PDEV_SET_HW_MODE_CMDID, + WMI_PDEV_SET_MAC_CONFIG_CMDID, + WMI_PDEV_SET_ANTENNA_MODE_CMDID, + WMI_SET_PERIODIC_CHANNEL_STATS_CONFIG_CMDID, + WMI_PDEV_WAL_POWER_DEBUG_CMDID, + WMI_PDEV_SET_REORDER_TIMEOUT_VAL_CMDID, + WMI_PDEV_SET_WAKEUP_CONFIG_CMDID, + WMI_PDEV_GET_ANTDIV_STATUS_CMDID, + WMI_PDEV_GET_CHIP_POWER_STATS_CMDID, + WMI_PDEV_SET_STATS_THRESHOLD_CMDID, + WMI_PDEV_MULTIPLE_VDEV_RESTART_REQUEST_CMDID, + WMI_PDEV_UPDATE_PKT_ROUTING_CMDID, + WMI_PDEV_CHECK_CAL_VERSION_CMDID, + WMI_PDEV_SET_DIVERSITY_GAIN_CMDID, + WMI_PDEV_DIV_GET_RSSI_ANTID_CMDID, + WMI_PDEV_BSS_CHAN_INFO_REQUEST_CMDID, + WMI_PDEV_UPDATE_PMK_CACHE_CMDID, + WMI_PDEV_UPDATE_FILS_HLP_PKT_CMDID, + WMI_PDEV_UPDATE_CTLTABLE_REQUEST_CMDID, + WMI_PDEV_CONFIG_VENDOR_OUI_ACTION_CMDID, + WMI_PDEV_SET_AC_TX_QUEUE_OPTIMIZED_CMDID, + WMI_PDEV_SET_RX_FILTER_PROMISCUOUS_CMDID, + WMI_PDEV_DMA_RING_CFG_REQ_CMDID, + WMI_PDEV_HE_TB_ACTION_FRM_CMDID, + WMI_PDEV_PKTLOG_FILTER_CMDID, + WMI_PDEV_SET_RAP_CONFIG_CMDID, + WMI_PDEV_DSM_FILTER_CMDID, + WMI_PDEV_FRAME_INJECT_CMDID, + WMI_PDEV_TBTT_OFFSET_SYNC_CMDID, + WMI_PDEV_SET_SRG_BSS_COLOR_BITMAP_CMDID, + WMI_PDEV_SET_SRG_PARTIAL_BSSID_BITMAP_CMDID, + WMI_PDEV_SET_SRG_OBSS_COLOR_ENABLE_BITMAP_CMDID, + WMI_PDEV_SET_SRG_OBSS_BSSID_ENABLE_BITMAP_CMDID, + WMI_PDEV_SET_NON_SRG_OBSS_COLOR_ENABLE_BITMAP_CMDID, + WMI_PDEV_SET_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMDID, + WMI_PDEV_GET_TPC_STATS_CMDID, + WMI_PDEV_ENABLE_DURATION_BASED_TX_MODE_SELECTION_CMDID, + WMI_PDEV_GET_DPD_STATUS_CMDID, + WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID, + WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID, + WMI_VDEV_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_VDEV), + WMI_VDEV_DELETE_CMDID, + WMI_VDEV_START_REQUEST_CMDID, + WMI_VDEV_RESTART_REQUEST_CMDID, + WMI_VDEV_UP_CMDID, + WMI_VDEV_STOP_CMDID, + WMI_VDEV_DOWN_CMDID, + WMI_VDEV_SET_PARAM_CMDID, + WMI_VDEV_INSTALL_KEY_CMDID, + WMI_VDEV_WNM_SLEEPMODE_CMDID, + WMI_VDEV_WMM_ADDTS_CMDID, + WMI_VDEV_WMM_DELTS_CMDID, + WMI_VDEV_SET_WMM_PARAMS_CMDID, + WMI_VDEV_SET_GTX_PARAMS_CMDID, + WMI_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMDID, + WMI_VDEV_PLMREQ_START_CMDID, + WMI_VDEV_PLMREQ_STOP_CMDID, + WMI_VDEV_TSF_TSTAMP_ACTION_CMDID, + WMI_VDEV_SET_IE_CMDID, + WMI_VDEV_RATEMASK_CMDID, + WMI_VDEV_ATF_REQUEST_CMDID, + WMI_VDEV_SET_DSCP_TID_MAP_CMDID, + WMI_VDEV_FILTER_NEIGHBOR_RX_PACKETS_CMDID, + WMI_VDEV_SET_QUIET_MODE_CMDID, + WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID, + WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID, + WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID, + WMI_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_PEER), + WMI_PEER_DELETE_CMDID, + WMI_PEER_FLUSH_TIDS_CMDID, + WMI_PEER_SET_PARAM_CMDID, + WMI_PEER_ASSOC_CMDID, + WMI_PEER_ADD_WDS_ENTRY_CMDID, + WMI_PEER_REMOVE_WDS_ENTRY_CMDID, + WMI_PEER_MCAST_GROUP_CMDID, + WMI_PEER_INFO_REQ_CMDID, + WMI_PEER_GET_ESTIMATED_LINKSPEED_CMDID, + WMI_PEER_SET_RATE_REPORT_CONDITION_CMDID, + WMI_PEER_UPDATE_WDS_ENTRY_CMDID, + WMI_PEER_ADD_PROXY_STA_ENTRY_CMDID, + WMI_PEER_SMART_ANT_SET_TX_ANTENNA_CMDID, + WMI_PEER_SMART_ANT_SET_TRAIN_INFO_CMDID, + WMI_PEER_SMART_ANT_SET_NODE_CONFIG_OPS_CMDID, + WMI_PEER_ATF_REQUEST_CMDID, + WMI_PEER_BWF_REQUEST_CMDID, + WMI_PEER_REORDER_QUEUE_SETUP_CMDID, + WMI_PEER_REORDER_QUEUE_REMOVE_CMDID, + WMI_PEER_SET_RX_BLOCKSIZE_CMDID, + WMI_PEER_ANTDIV_INFO_REQ_CMDID, + WMI_BCN_TX_CMDID = WMI_TLV_CMD(WMI_GRP_MGMT), + WMI_PDEV_SEND_BCN_CMDID, + WMI_BCN_TMPL_CMDID, + WMI_BCN_FILTER_RX_CMDID, + WMI_PRB_REQ_FILTER_RX_CMDID, + WMI_MGMT_TX_CMDID, + WMI_PRB_TMPL_CMDID, + WMI_MGMT_TX_SEND_CMDID, + WMI_OFFCHAN_DATA_TX_SEND_CMDID, + WMI_PDEV_SEND_FD_CMDID, + WMI_BCN_OFFLOAD_CTRL_CMDID, + WMI_BSS_COLOR_CHANGE_ENABLE_CMDID, + WMI_VDEV_BCN_OFFLOAD_QUIET_CONFIG_CMDID, + WMI_FILS_DISCOVERY_TMPL_CMDID, + WMI_ADDBA_CLEAR_RESP_CMDID = WMI_TLV_CMD(WMI_GRP_BA_NEG), + WMI_ADDBA_SEND_CMDID, + WMI_ADDBA_STATUS_CMDID, + WMI_DELBA_SEND_CMDID, + WMI_ADDBA_SET_RESP_CMDID, + WMI_SEND_SINGLEAMSDU_CMDID, + WMI_STA_POWERSAVE_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_STA_PS), + WMI_STA_POWERSAVE_PARAM_CMDID, + WMI_STA_MIMO_PS_MODE_CMDID, + WMI_PDEV_DFS_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_DFS), + WMI_PDEV_DFS_DISABLE_CMDID, + WMI_DFS_PHYERR_FILTER_ENA_CMDID, + WMI_DFS_PHYERR_FILTER_DIS_CMDID, + WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMDID, + WMI_PDEV_DFS_PHYERR_OFFLOAD_DISABLE_CMDID, + WMI_VDEV_ADFS_CH_CFG_CMDID, + WMI_VDEV_ADFS_OCAC_ABORT_CMDID, + WMI_ROAM_SCAN_MODE = WMI_TLV_CMD(WMI_GRP_ROAM), + WMI_ROAM_SCAN_RSSI_THRESHOLD, + WMI_ROAM_SCAN_PERIOD, + WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_ROAM_AP_PROFILE, + WMI_ROAM_CHAN_LIST, + WMI_ROAM_SCAN_CMD, + WMI_ROAM_SYNCH_COMPLETE, + WMI_ROAM_SET_RIC_REQUEST_CMDID, + WMI_ROAM_INVOKE_CMDID, + WMI_ROAM_FILTER_CMDID, + WMI_ROAM_SUBNET_CHANGE_CONFIG_CMDID, + WMI_ROAM_CONFIGURE_MAWC_CMDID, + WMI_ROAM_SET_MBO_PARAM_CMDID, + WMI_ROAM_PER_CONFIG_CMDID, + WMI_ROAM_BTM_CONFIG_CMDID, + WMI_ENABLE_FILS_CMDID, + WMI_OFL_SCAN_ADD_AP_PROFILE = WMI_TLV_CMD(WMI_GRP_OFL_SCAN), + WMI_OFL_SCAN_REMOVE_AP_PROFILE, + WMI_OFL_SCAN_PERIOD, + WMI_P2P_DEV_SET_DEVICE_INFO = WMI_TLV_CMD(WMI_GRP_P2P), + WMI_P2P_DEV_SET_DISCOVERABILITY, + WMI_P2P_GO_SET_BEACON_IE, + WMI_P2P_GO_SET_PROBE_RESP_IE, + WMI_P2P_SET_VENDOR_IE_DATA_CMDID, + WMI_P2P_DISC_OFFLOAD_CONFIG_CMDID, + WMI_P2P_DISC_OFFLOAD_APPIE_CMDID, + WMI_P2P_DISC_OFFLOAD_PATTERN_CMDID, + WMI_P2P_SET_OPPPS_PARAM_CMDID, + WMI_P2P_LISTEN_OFFLOAD_START_CMDID, + WMI_P2P_LISTEN_OFFLOAD_STOP_CMDID, + WMI_AP_PS_PEER_PARAM_CMDID = WMI_TLV_CMD(WMI_GRP_AP_PS), + WMI_AP_PS_PEER_UAPSD_COEX_CMDID, + WMI_AP_PS_EGAP_PARAM_CMDID, + WMI_PEER_RATE_RETRY_SCHED_CMDID = WMI_TLV_CMD(WMI_GRP_RATE_CTRL), + WMI_WLAN_PROFILE_TRIGGER_CMDID = WMI_TLV_CMD(WMI_GRP_PROFILE), + WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + WMI_PDEV_SUSPEND_CMDID = WMI_TLV_CMD(WMI_GRP_SUSPEND), + WMI_PDEV_RESUME_CMDID, + WMI_ADD_BCN_FILTER_CMDID = WMI_TLV_CMD(WMI_GRP_BCN_FILTER), + WMI_RMV_BCN_FILTER_CMDID, + WMI_WOW_ADD_WAKE_PATTERN_CMDID = WMI_TLV_CMD(WMI_GRP_WOW), + WMI_WOW_DEL_WAKE_PATTERN_CMDID, + WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + WMI_WOW_ENABLE_CMDID, + WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + WMI_WOW_IOAC_ADD_KEEPALIVE_CMDID, + WMI_WOW_IOAC_DEL_KEEPALIVE_CMDID, + WMI_WOW_IOAC_ADD_WAKE_PATTERN_CMDID, + WMI_WOW_IOAC_DEL_WAKE_PATTERN_CMDID, + WMI_D0_WOW_ENABLE_DISABLE_CMDID, + WMI_EXTWOW_ENABLE_CMDID, + WMI_EXTWOW_SET_APP_TYPE1_PARAMS_CMDID, + WMI_EXTWOW_SET_APP_TYPE2_PARAMS_CMDID, + WMI_WOW_ENABLE_ICMPV6_NA_FLT_CMDID, + WMI_WOW_UDP_SVC_OFLD_CMDID, + WMI_WOW_HOSTWAKEUP_GPIO_PIN_PATTERN_CONFIG_CMDID, + WMI_WOW_SET_ACTION_WAKE_UP_CMDID, + WMI_RTT_MEASREQ_CMDID = WMI_TLV_CMD(WMI_GRP_RTT), + WMI_RTT_TSF_CMDID, + WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID = WMI_TLV_CMD(WMI_GRP_SPECTRAL), + WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + WMI_REQUEST_STATS_CMDID = WMI_TLV_CMD(WMI_GRP_STATS), + WMI_MCC_SCHED_TRAFFIC_STATS_CMDID, + WMI_REQUEST_STATS_EXT_CMDID, + WMI_REQUEST_LINK_STATS_CMDID, + WMI_START_LINK_STATS_CMDID, + WMI_CLEAR_LINK_STATS_CMDID, + WMI_GET_FW_MEM_DUMP_CMDID, + WMI_DEBUG_MESG_FLUSH_CMDID, + WMI_DIAG_EVENT_LOG_CONFIG_CMDID, + WMI_REQUEST_WLAN_STATS_CMDID, + WMI_REQUEST_RCPI_CMDID, + WMI_REQUEST_PEER_STATS_INFO_CMDID, + WMI_REQUEST_RADIO_CHAN_STATS_CMDID, + WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_ARP_NS_OFL), + WMI_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID, + WMI_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID, + WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_NLO_OFL), + WMI_APFIND_CMDID, + WMI_PASSPOINT_LIST_CONFIG_CMDID, + WMI_NLO_CONFIGURE_MAWC_CMDID, + WMI_GTK_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_GTK_OFL), + WMI_CSA_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_CSA_OFL), + WMI_CSA_OFFLOAD_CHANSWITCH_CMDID, + WMI_CHATTER_SET_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_CHATTER), + WMI_CHATTER_ADD_COALESCING_FILTER_CMDID, + WMI_CHATTER_DELETE_COALESCING_FILTER_CMDID, + WMI_CHATTER_COALESCING_QUERY_CMDID, + WMI_PEER_TID_ADDBA_CMDID = WMI_TLV_CMD(WMI_GRP_TID_ADDBA), + WMI_PEER_TID_DELBA_CMDID, + WMI_STA_DTIM_PS_METHOD_CMDID, + WMI_STA_UAPSD_AUTO_TRIG_CMDID, + WMI_STA_KEEPALIVE_CMDID, + WMI_BA_REQ_SSN_CMDID, + WMI_ECHO_CMDID = WMI_TLV_CMD(WMI_GRP_MISC), + WMI_PDEV_UTF_CMDID, + WMI_DBGLOG_CFG_CMDID, + WMI_PDEV_QVIT_CMDID, + WMI_PDEV_FTM_INTG_CMDID, + WMI_VDEV_SET_KEEPALIVE_CMDID, + WMI_VDEV_GET_KEEPALIVE_CMDID, + WMI_FORCE_FW_HANG_CMDID, + WMI_SET_MCASTBCAST_FILTER_CMDID, + WMI_THERMAL_MGMT_CMDID, + WMI_HOST_AUTO_SHUTDOWN_CFG_CMDID, + WMI_TPC_CHAINMASK_CONFIG_CMDID, + WMI_SET_ANTENNA_DIVERSITY_CMDID, + WMI_OCB_SET_SCHED_CMDID, + WMI_RSSI_BREACH_MONITOR_CONFIG_CMDID, + WMI_LRO_CONFIG_CMDID, + WMI_TRANSFER_DATA_TO_FLASH_CMDID, + WMI_CONFIG_ENHANCED_MCAST_FILTER_CMDID, + WMI_VDEV_WISA_CMDID, + WMI_DBGLOG_TIME_STAMP_SYNC_CMDID, + WMI_SET_MULTIPLE_MCAST_FILTER_CMDID, + WMI_READ_DATA_FROM_FLASH_CMDID, + WMI_THERM_THROT_SET_CONF_CMDID, + WMI_RUNTIME_DPD_RECAL_CMDID, + WMI_GET_TPC_POWER_CMDID, + WMI_IDLE_TRIGGER_MONITOR_CMDID, + WMI_GPIO_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_GPIO), + WMI_GPIO_OUTPUT_CMDID, + WMI_TXBF_CMDID, + WMI_FWTEST_VDEV_MCC_SET_TBTT_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_FWTEST), + WMI_FWTEST_P2P_SET_NOA_PARAM_CMDID, + WMI_UNIT_TEST_CMDID, + WMI_FWTEST_CMDID, + WMI_QBOOST_CFG_CMDID, + WMI_TDLS_SET_STATE_CMDID = WMI_TLV_CMD(WMI_GRP_TDLS), + WMI_TDLS_PEER_UPDATE_CMDID, + WMI_TDLS_SET_OFFCHAN_MODE_CMDID, + WMI_RESMGR_ADAPTIVE_OCS_EN_DIS_CMDID = WMI_TLV_CMD(WMI_GRP_RESMGR), + WMI_RESMGR_SET_CHAN_TIME_QUOTA_CMDID, + WMI_RESMGR_SET_CHAN_LATENCY_CMDID, + WMI_STA_SMPS_FORCE_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_STA_SMPS), + WMI_STA_SMPS_PARAM_CMDID, + WMI_HB_SET_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_WLAN_HB), + WMI_HB_SET_TCP_PARAMS_CMDID, + WMI_HB_SET_TCP_PKT_FILTER_CMDID, + WMI_HB_SET_UDP_PARAMS_CMDID, + WMI_HB_SET_UDP_PKT_FILTER_CMDID, + WMI_RMC_SET_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_RMC), + WMI_RMC_SET_ACTION_PERIOD_CMDID, + WMI_RMC_CONFIG_CMDID, + WMI_RMC_SET_MANUAL_LEADER_CMDID, + WMI_MHF_OFFLOAD_SET_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_MHF_OFL), + WMI_MHF_OFFLOAD_PLUMB_ROUTING_TBL_CMDID, + WMI_BATCH_SCAN_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_LOCATION_SCAN), + WMI_BATCH_SCAN_DISABLE_CMDID, + WMI_BATCH_SCAN_TRIGGER_RESULT_CMDID, + WMI_OEM_REQ_CMDID = WMI_TLV_CMD(WMI_GRP_OEM), + WMI_OEM_REQUEST_CMDID, + WMI_LPI_OEM_REQ_CMDID, + WMI_NAN_CMDID = WMI_TLV_CMD(WMI_GRP_NAN), + WMI_MODEM_POWER_STATE_CMDID = WMI_TLV_CMD(WMI_GRP_COEX), + WMI_CHAN_AVOID_UPDATE_CMDID, + WMI_COEX_CONFIG_CMDID, + WMI_CHAN_AVOID_RPT_ALLOW_CMDID, + WMI_COEX_GET_ANTENNA_ISOLATION_CMDID, + WMI_SAR_LIMITS_CMDID, + WMI_OBSS_SCAN_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_OBSS_OFL), + WMI_OBSS_SCAN_DISABLE_CMDID, + WMI_OBSS_COLOR_COLLISION_DET_CONFIG_CMDID, + WMI_LPI_MGMT_SNOOPING_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_LPI), + WMI_LPI_START_SCAN_CMDID, + WMI_LPI_STOP_SCAN_CMDID, + WMI_EXTSCAN_START_CMDID = WMI_TLV_CMD(WMI_GRP_EXTSCAN), + WMI_EXTSCAN_STOP_CMDID, + WMI_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMDID, + WMI_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMDID, + WMI_EXTSCAN_GET_CACHED_RESULTS_CMDID, + WMI_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMDID, + WMI_EXTSCAN_SET_CAPABILITIES_CMDID, + WMI_EXTSCAN_GET_CAPABILITIES_CMDID, + WMI_EXTSCAN_CONFIGURE_HOTLIST_SSID_MONITOR_CMDID, + WMI_EXTSCAN_CONFIGURE_MAWC_CMDID, + WMI_SET_DHCP_SERVER_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_DHCP_OFL), + WMI_IPA_OFFLOAD_ENABLE_DISABLE_CMDID = WMI_TLV_CMD(WMI_GRP_IPA), + WMI_MDNS_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_MDNS_OFL), + WMI_MDNS_SET_FQDN_CMDID, + WMI_MDNS_SET_RESPONSE_CMDID, + WMI_MDNS_GET_STATS_CMDID, + WMI_SAP_OFL_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_SAP_OFL), + WMI_SAP_SET_BLACKLIST_PARAM_CMDID, + WMI_OCB_SET_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_OCB), + WMI_OCB_SET_UTC_TIME_CMDID, + WMI_OCB_START_TIMING_ADVERT_CMDID, + WMI_OCB_STOP_TIMING_ADVERT_CMDID, + WMI_OCB_GET_TSF_TIMER_CMDID, + WMI_DCC_GET_STATS_CMDID, + WMI_DCC_CLEAR_STATS_CMDID, + WMI_DCC_UPDATE_NDL_CMDID, + WMI_SOC_SET_PCL_CMDID = WMI_TLV_CMD(WMI_GRP_SOC), + WMI_SOC_SET_HW_MODE_CMDID, + WMI_SOC_SET_DUAL_MAC_CONFIG_CMDID, + WMI_SOC_SET_ANTENNA_MODE_CMDID, + WMI_PACKET_FILTER_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_PKT_FILTER), + WMI_PACKET_FILTER_ENABLE_CMDID, + WMI_MAWC_SENSOR_REPORT_IND_CMDID = WMI_TLV_CMD(WMI_GRP_MAWC), + WMI_PMF_OFFLOAD_SET_SA_QUERY_CMDID = WMI_TLV_CMD(WMI_GRP_PMF_OFFLOAD), + WMI_BPF_GET_CAPABILITY_CMDID = WMI_TLV_CMD(WMI_GRP_BPF_OFFLOAD), + WMI_BPF_GET_VDEV_STATS_CMDID, + WMI_BPF_SET_VDEV_INSTRUCTIONS_CMDID, + WMI_BPF_DEL_VDEV_INSTRUCTIONS_CMDID, + WMI_BPF_SET_VDEV_ACTIVE_MODE_CMDID, + WMI_MNT_FILTER_CMDID = WMI_TLV_CMD(WMI_GRP_MONITOR), + WMI_SET_CURRENT_COUNTRY_CMDID = WMI_TLV_CMD(WMI_GRP_REGULATORY), + WMI_11D_SCAN_START_CMDID, + WMI_11D_SCAN_STOP_CMDID, + WMI_SET_INIT_COUNTRY_CMDID, + WMI_NDI_GET_CAP_REQ_CMDID = WMI_TLV_CMD(WMI_GRP_PROTOTYPE), + WMI_NDP_INITIATOR_REQ_CMDID, + WMI_NDP_RESPONDER_REQ_CMDID, + WMI_NDP_END_REQ_CMDID, + WMI_HW_DATA_FILTER_CMDID = WMI_TLV_CMD(WMI_GRP_HW_DATA_FILTER), + WMI_TWT_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_TWT), + WMI_TWT_DISABLE_CMDID, + WMI_TWT_ADD_DIALOG_CMDID, + WMI_TWT_DEL_DIALOG_CMDID, + WMI_TWT_PAUSE_DIALOG_CMDID, + WMI_TWT_RESUME_DIALOG_CMDID, + WMI_PDEV_OBSS_PD_SPATIAL_REUSE_CMDID = + WMI_TLV_CMD(WMI_GRP_SPATIAL_REUSE), + WMI_PDEV_OBSS_PD_SPATIAL_REUSE_SET_DEF_OBSS_THRESH_CMDID, +}; + +enum wmi_tlv_event_id { + WMI_SERVICE_READY_EVENTID = 0x1, + WMI_READY_EVENTID, + WMI_SERVICE_AVAILABLE_EVENTID, + WMI_SCAN_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_SCAN), + WMI_PDEV_TPC_CONFIG_EVENTID = WMI_TLV_CMD(WMI_GRP_PDEV), + WMI_CHAN_INFO_EVENTID, + WMI_PHYERR_EVENTID, + WMI_PDEV_DUMP_EVENTID, + WMI_TX_PAUSE_EVENTID, + WMI_DFS_RADAR_EVENTID, + WMI_PDEV_L1SS_TRACK_EVENTID, + WMI_PDEV_TEMPERATURE_EVENTID, + WMI_SERVICE_READY_EXT_EVENTID, + WMI_PDEV_FIPS_EVENTID, + WMI_PDEV_CHANNEL_HOPPING_EVENTID, + WMI_PDEV_ANI_CCK_LEVEL_EVENTID, + WMI_PDEV_ANI_OFDM_LEVEL_EVENTID, + WMI_PDEV_TPC_EVENTID, + WMI_PDEV_NFCAL_POWER_ALL_CHANNELS_EVENTID, + WMI_PDEV_SET_HW_MODE_RESP_EVENTID, + WMI_PDEV_HW_MODE_TRANSITION_EVENTID, + WMI_PDEV_SET_MAC_CONFIG_RESP_EVENTID, + WMI_PDEV_ANTDIV_STATUS_EVENTID, + WMI_PDEV_CHIP_POWER_STATS_EVENTID, + WMI_PDEV_CHIP_POWER_SAVE_FAILURE_DETECTED_EVENTID, + WMI_PDEV_CSA_SWITCH_COUNT_STATUS_EVENTID, + WMI_PDEV_CHECK_CAL_VERSION_EVENTID, + WMI_PDEV_DIV_RSSI_ANTID_EVENTID, + WMI_PDEV_BSS_CHAN_INFO_EVENTID, + WMI_PDEV_UPDATE_CTLTABLE_EVENTID, + WMI_PDEV_DMA_RING_CFG_RSP_EVENTID, + WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID, + WMI_PDEV_CTL_FAILSAFE_CHECK_EVENTID, + WMI_PDEV_CSC_SWITCH_COUNT_STATUS_EVENTID, + WMI_PDEV_COLD_BOOT_CAL_DATA_EVENTID, + WMI_PDEV_RAP_INFO_EVENTID, + WMI_CHAN_RF_CHARACTERIZATION_INFO_EVENTID, + WMI_SERVICE_READY_EXT2_EVENTID, + WMI_VDEV_START_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_VDEV), + WMI_VDEV_STOPPED_EVENTID, + WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID, + WMI_VDEV_MCC_BCN_INTERVAL_CHANGE_REQ_EVENTID, + WMI_VDEV_TSF_REPORT_EVENTID, + WMI_VDEV_DELETE_RESP_EVENTID, + WMI_VDEV_ENCRYPT_DECRYPT_DATA_RESP_EVENTID, + WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID, + WMI_PEER_STA_KICKOUT_EVENTID = WMI_TLV_CMD(WMI_GRP_PEER), + WMI_PEER_INFO_EVENTID, + WMI_PEER_TX_FAIL_CNT_THR_EVENTID, + WMI_PEER_ESTIMATED_LINKSPEED_EVENTID, + WMI_PEER_STATE_EVENTID, + WMI_PEER_ASSOC_CONF_EVENTID, + WMI_PEER_DELETE_RESP_EVENTID, + WMI_PEER_RATECODE_LIST_EVENTID, + WMI_WDS_PEER_EVENTID, + WMI_PEER_STA_PS_STATECHG_EVENTID, + WMI_PEER_ANTDIV_INFO_EVENTID, + WMI_PEER_RESERVED0_EVENTID, + WMI_PEER_RESERVED1_EVENTID, + WMI_PEER_RESERVED2_EVENTID, + WMI_PEER_RESERVED3_EVENTID, + WMI_PEER_RESERVED4_EVENTID, + WMI_PEER_RESERVED5_EVENTID, + WMI_PEER_RESERVED6_EVENTID, + WMI_PEER_RESERVED7_EVENTID, + WMI_PEER_RESERVED8_EVENTID, + WMI_PEER_RESERVED9_EVENTID, + WMI_PEER_RESERVED10_EVENTID, + WMI_PEER_OPER_MODE_CHANGE_EVENTID, + WMI_PEER_TX_PN_RESPONSE_EVENTID, + WMI_PEER_CFR_CAPTURE_EVENTID, + WMI_PEER_CREATE_CONF_EVENTID, + WMI_MGMT_RX_EVENTID = WMI_TLV_CMD(WMI_GRP_MGMT), + WMI_HOST_SWBA_EVENTID, + WMI_TBTTOFFSET_UPDATE_EVENTID, + WMI_OFFLOAD_BCN_TX_STATUS_EVENTID, + WMI_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID, + WMI_MGMT_TX_COMPLETION_EVENTID, + WMI_MGMT_TX_BUNDLE_COMPLETION_EVENTID, + WMI_TBTTOFFSET_EXT_UPDATE_EVENTID, + WMI_OFFCHAN_DATA_TX_COMPLETION_EVENTID, + WMI_HOST_FILS_DISCOVERY_EVENTID, + WMI_TX_DELBA_COMPLETE_EVENTID = WMI_TLV_CMD(WMI_GRP_BA_NEG), + WMI_TX_ADDBA_COMPLETE_EVENTID, + WMI_BA_RSP_SSN_EVENTID, + WMI_AGGR_STATE_TRIG_EVENTID, + WMI_ROAM_EVENTID = WMI_TLV_CMD(WMI_GRP_ROAM), + WMI_PROFILE_MATCH, + WMI_ROAM_SYNCH_EVENTID, + WMI_P2P_DISC_EVENTID = WMI_TLV_CMD(WMI_GRP_P2P), + WMI_P2P_NOA_EVENTID, + WMI_P2P_LISTEN_OFFLOAD_STOPPED_EVENTID, + WMI_AP_PS_EGAP_INFO_EVENTID = WMI_TLV_CMD(WMI_GRP_AP_PS), + WMI_PDEV_RESUME_EVENTID = WMI_TLV_CMD(WMI_GRP_SUSPEND), + WMI_WOW_WAKEUP_HOST_EVENTID = WMI_TLV_CMD(WMI_GRP_WOW), + WMI_D0_WOW_DISABLE_ACK_EVENTID, + WMI_WOW_INITIAL_WAKEUP_EVENTID, + WMI_RTT_MEASUREMENT_REPORT_EVENTID = WMI_TLV_CMD(WMI_GRP_RTT), + WMI_TSF_MEASUREMENT_REPORT_EVENTID, + WMI_RTT_ERROR_REPORT_EVENTID, + WMI_STATS_EXT_EVENTID = WMI_TLV_CMD(WMI_GRP_STATS), + WMI_IFACE_LINK_STATS_EVENTID, + WMI_PEER_LINK_STATS_EVENTID, + WMI_RADIO_LINK_STATS_EVENTID, + WMI_UPDATE_FW_MEM_DUMP_EVENTID, + WMI_DIAG_EVENT_LOG_SUPPORTED_EVENTID, + WMI_INST_RSSI_STATS_EVENTID, + WMI_RADIO_TX_POWER_LEVEL_STATS_EVENTID, + WMI_REPORT_STATS_EVENTID, + WMI_UPDATE_RCPI_EVENTID, + WMI_PEER_STATS_INFO_EVENTID, + WMI_RADIO_CHAN_STATS_EVENTID, + WMI_NLO_MATCH_EVENTID = WMI_TLV_CMD(WMI_GRP_NLO_OFL), + WMI_NLO_SCAN_COMPLETE_EVENTID, + WMI_APFIND_EVENTID, + WMI_PASSPOINT_MATCH_EVENTID, + WMI_GTK_OFFLOAD_STATUS_EVENTID = WMI_TLV_CMD(WMI_GRP_GTK_OFL), + WMI_GTK_REKEY_FAIL_EVENTID, + WMI_CSA_HANDLING_EVENTID = WMI_TLV_CMD(WMI_GRP_CSA_OFL), + WMI_CHATTER_PC_QUERY_EVENTID = WMI_TLV_CMD(WMI_GRP_CHATTER), + WMI_PDEV_DFS_RADAR_DETECTION_EVENTID = WMI_TLV_CMD(WMI_GRP_DFS), + WMI_VDEV_DFS_CAC_COMPLETE_EVENTID, + WMI_VDEV_ADFS_OCAC_COMPLETE_EVENTID, + WMI_ECHO_EVENTID = WMI_TLV_CMD(WMI_GRP_MISC), + WMI_PDEV_UTF_EVENTID, + WMI_DEBUG_MESG_EVENTID, + WMI_UPDATE_STATS_EVENTID, + WMI_DEBUG_PRINT_EVENTID, + WMI_DCS_INTERFERENCE_EVENTID, + WMI_PDEV_QVIT_EVENTID, + WMI_WLAN_PROFILE_DATA_EVENTID, + WMI_PDEV_FTM_INTG_EVENTID, + WMI_WLAN_FREQ_AVOID_EVENTID, + WMI_VDEV_GET_KEEPALIVE_EVENTID, + WMI_THERMAL_MGMT_EVENTID, + WMI_DIAG_DATA_CONTAINER_EVENTID, + WMI_HOST_AUTO_SHUTDOWN_EVENTID, + WMI_UPDATE_WHAL_MIB_STATS_EVENTID, + WMI_UPDATE_VDEV_RATE_STATS_EVENTID, + WMI_DIAG_EVENTID, + WMI_OCB_SET_SCHED_EVENTID, + WMI_DEBUG_MESG_FLUSH_COMPLETE_EVENTID, + WMI_RSSI_BREACH_EVENTID, + WMI_TRANSFER_DATA_TO_FLASH_COMPLETE_EVENTID, + WMI_PDEV_UTF_SCPC_EVENTID, + WMI_READ_DATA_FROM_FLASH_EVENTID, + WMI_REPORT_RX_AGGR_FAILURE_EVENTID, + WMI_PKGID_EVENTID, + WMI_GPIO_INPUT_EVENTID = WMI_TLV_CMD(WMI_GRP_GPIO), + WMI_UPLOADH_EVENTID, + WMI_CAPTUREH_EVENTID, + WMI_RFKILL_STATE_CHANGE_EVENTID, + WMI_TDLS_PEER_EVENTID = WMI_TLV_CMD(WMI_GRP_TDLS), + WMI_STA_SMPS_FORCE_MODE_COMPL_EVENTID = WMI_TLV_CMD(WMI_GRP_STA_SMPS), + WMI_BATCH_SCAN_ENABLED_EVENTID = WMI_TLV_CMD(WMI_GRP_LOCATION_SCAN), + WMI_BATCH_SCAN_RESULT_EVENTID, + WMI_OEM_CAPABILITY_EVENTID = WMI_TLV_CMD(WMI_GRP_OEM), + WMI_OEM_MEASUREMENT_REPORT_EVENTID, + WMI_OEM_ERROR_REPORT_EVENTID, + WMI_OEM_RESPONSE_EVENTID, + WMI_NAN_EVENTID = WMI_TLV_CMD(WMI_GRP_NAN), + WMI_NAN_DISC_IFACE_CREATED_EVENTID, + WMI_NAN_DISC_IFACE_DELETED_EVENTID, + WMI_NAN_STARTED_CLUSTER_EVENTID, + WMI_NAN_JOINED_CLUSTER_EVENTID, + WMI_COEX_REPORT_ANTENNA_ISOLATION_EVENTID = WMI_TLV_CMD(WMI_GRP_COEX), + WMI_LPI_RESULT_EVENTID = WMI_TLV_CMD(WMI_GRP_LPI), + WMI_LPI_STATUS_EVENTID, + WMI_LPI_HANDOFF_EVENTID, + WMI_EXTSCAN_START_STOP_EVENTID = WMI_TLV_CMD(WMI_GRP_EXTSCAN), + WMI_EXTSCAN_OPERATION_EVENTID, + WMI_EXTSCAN_TABLE_USAGE_EVENTID, + WMI_EXTSCAN_CACHED_RESULTS_EVENTID, + WMI_EXTSCAN_WLAN_CHANGE_RESULTS_EVENTID, + WMI_EXTSCAN_HOTLIST_MATCH_EVENTID, + WMI_EXTSCAN_CAPABILITIES_EVENTID, + WMI_EXTSCAN_HOTLIST_SSID_MATCH_EVENTID, + WMI_MDNS_STATS_EVENTID = WMI_TLV_CMD(WMI_GRP_MDNS_OFL), + WMI_SAP_OFL_ADD_STA_EVENTID = WMI_TLV_CMD(WMI_GRP_SAP_OFL), + WMI_SAP_OFL_DEL_STA_EVENTID, + WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID = + WMI_EVT_GRP_START_ID(WMI_GRP_OBSS_OFL), + WMI_OCB_SET_CONFIG_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_OCB), + WMI_OCB_GET_TSF_TIMER_RESP_EVENTID, + WMI_DCC_GET_STATS_RESP_EVENTID, + WMI_DCC_UPDATE_NDL_RESP_EVENTID, + WMI_DCC_STATS_EVENTID, + WMI_SOC_SET_HW_MODE_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_SOC), + WMI_SOC_HW_MODE_TRANSITION_EVENTID, + WMI_SOC_SET_DUAL_MAC_CONFIG_RESP_EVENTID, + WMI_MAWC_ENABLE_SENSOR_EVENTID = WMI_TLV_CMD(WMI_GRP_MAWC), + WMI_BPF_CAPABILIY_INFO_EVENTID = WMI_TLV_CMD(WMI_GRP_BPF_OFFLOAD), + WMI_BPF_VDEV_STATS_INFO_EVENTID, + WMI_RMC_NEW_LEADER_EVENTID = WMI_TLV_CMD(WMI_GRP_RMC), + WMI_REG_CHAN_LIST_CC_EVENTID = WMI_TLV_CMD(WMI_GRP_REGULATORY), + WMI_11D_NEW_COUNTRY_EVENTID, + WMI_REG_CHAN_LIST_CC_EXT_EVENTID, + WMI_NDI_CAP_RSP_EVENTID = WMI_TLV_CMD(WMI_GRP_PROTOTYPE), + WMI_NDP_INITIATOR_RSP_EVENTID, + WMI_NDP_RESPONDER_RSP_EVENTID, + WMI_NDP_END_RSP_EVENTID, + WMI_NDP_INDICATION_EVENTID, + WMI_NDP_CONFIRM_EVENTID, + WMI_NDP_END_INDICATION_EVENTID, + + WMI_TWT_ENABLE_EVENTID = WMI_TLV_CMD(WMI_GRP_TWT), + WMI_TWT_DISABLE_EVENTID, + WMI_TWT_ADD_DIALOG_EVENTID, + WMI_TWT_DEL_DIALOG_EVENTID, + WMI_TWT_PAUSE_DIALOG_EVENTID, + WMI_TWT_RESUME_DIALOG_EVENTID, +}; + +enum wmi_tlv_pdev_param { + WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1, + WMI_PDEV_PARAM_RX_CHAIN_MASK, + WMI_PDEV_PARAM_TXPOWER_LIMIT2G, + WMI_PDEV_PARAM_TXPOWER_LIMIT5G, + WMI_PDEV_PARAM_TXPOWER_SCALE, + WMI_PDEV_PARAM_BEACON_GEN_MODE, + WMI_PDEV_PARAM_BEACON_TX_MODE, + WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + WMI_PDEV_PARAM_PROTECTION_MODE, + WMI_PDEV_PARAM_DYNAMIC_BW, + WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + WMI_PDEV_PARAM_AGG_SW_RETRY_TH, + WMI_PDEV_PARAM_STA_KICKOUT_TH, + WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING, + WMI_PDEV_PARAM_LTR_ENABLE, + WMI_PDEV_PARAM_LTR_AC_LATENCY_BE, + WMI_PDEV_PARAM_LTR_AC_LATENCY_BK, + WMI_PDEV_PARAM_LTR_AC_LATENCY_VI, + WMI_PDEV_PARAM_LTR_AC_LATENCY_VO, + WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + WMI_PDEV_PARAM_LTR_RX_OVERRIDE, + WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + WMI_PDEV_PARAM_L1SS_ENABLE, + WMI_PDEV_PARAM_DSLEEP_ENABLE, + WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH, + WMI_PDEV_PARAM_PCIELP_TXBUF_WATERMARK, + WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE, + WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_PMF_QOS, + WMI_PDEV_PARAM_ARP_AC_OVERRIDE, + WMI_PDEV_PARAM_DCS, + WMI_PDEV_PARAM_ANI_ENABLE, + WMI_PDEV_PARAM_ANI_POLL_PERIOD, + WMI_PDEV_PARAM_ANI_LISTEN_PERIOD, + WMI_PDEV_PARAM_ANI_OFDM_LEVEL, + WMI_PDEV_PARAM_ANI_CCK_LEVEL, + WMI_PDEV_PARAM_DYNTXCHAIN, + WMI_PDEV_PARAM_PROXY_STA, + WMI_PDEV_PARAM_IDLE_PS_CONFIG, + WMI_PDEV_PARAM_POWER_GATING_SLEEP, + WMI_PDEV_PARAM_RFKILL_ENABLE, + WMI_PDEV_PARAM_BURST_DUR, + WMI_PDEV_PARAM_BURST_ENABLE, + WMI_PDEV_PARAM_HW_RFKILL_CONFIG, + WMI_PDEV_PARAM_LOW_POWER_RF_ENABLE, + WMI_PDEV_PARAM_L1SS_TRACK, + WMI_PDEV_PARAM_HYST_EN, + WMI_PDEV_PARAM_POWER_COLLAPSE_ENABLE, + WMI_PDEV_PARAM_LED_SYS_STATE, + WMI_PDEV_PARAM_LED_ENABLE, + WMI_PDEV_PARAM_AUDIO_OVER_WLAN_LATENCY, + WMI_PDEV_PARAM_AUDIO_OVER_WLAN_ENABLE, + WMI_PDEV_PARAM_WHAL_MIB_STATS_UPDATE_ENABLE, + WMI_PDEV_PARAM_VDEV_RATE_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_CTS_CBW, + WMI_PDEV_PARAM_WNTS_CONFIG, + WMI_PDEV_PARAM_ADAPTIVE_EARLY_RX_ENABLE, + WMI_PDEV_PARAM_ADAPTIVE_EARLY_RX_MIN_SLEEP_SLOP, + WMI_PDEV_PARAM_ADAPTIVE_EARLY_RX_INC_DEC_STEP, + WMI_PDEV_PARAM_EARLY_RX_FIX_SLEEP_SLOP, + WMI_PDEV_PARAM_BMISS_BASED_ADAPTIVE_BTO_ENABLE, + WMI_PDEV_PARAM_BMISS_BTO_MIN_BCN_TIMEOUT, + WMI_PDEV_PARAM_BMISS_BTO_INC_DEC_STEP, + WMI_PDEV_PARAM_BTO_FIX_BCN_TIMEOUT, + WMI_PDEV_PARAM_CE_BASED_ADAPTIVE_BTO_ENABLE, + WMI_PDEV_PARAM_CE_BTO_COMBO_CE_VALUE, + WMI_PDEV_PARAM_TX_CHAIN_MASK_2G, + WMI_PDEV_PARAM_RX_CHAIN_MASK_2G, + WMI_PDEV_PARAM_TX_CHAIN_MASK_5G, + WMI_PDEV_PARAM_RX_CHAIN_MASK_5G, + WMI_PDEV_PARAM_TX_CHAIN_MASK_CCK, + WMI_PDEV_PARAM_TX_CHAIN_MASK_1SS, + WMI_PDEV_PARAM_CTS2SELF_FOR_P2P_GO_CONFIG, + WMI_PDEV_PARAM_TXPOWER_DECR_DB, + WMI_PDEV_PARAM_AGGR_BURST, + WMI_PDEV_PARAM_RX_DECAP_MODE, + WMI_PDEV_PARAM_FAST_CHANNEL_RESET, + WMI_PDEV_PARAM_SMART_ANTENNA_DEFAULT_ANTENNA, + WMI_PDEV_PARAM_ANTENNA_GAIN, + WMI_PDEV_PARAM_RX_FILTER, + WMI_PDEV_SET_MCAST_TO_UCAST_TID, + WMI_PDEV_PARAM_PROXY_STA_MODE, + WMI_PDEV_PARAM_SET_MCAST2UCAST_MODE, + WMI_PDEV_PARAM_SET_MCAST2UCAST_BUFFER, + WMI_PDEV_PARAM_REMOVE_MCAST2UCAST_BUFFER, + WMI_PDEV_PEER_STA_PS_STATECHG_ENABLE, + WMI_PDEV_PARAM_IGMPMLD_AC_OVERRIDE, + WMI_PDEV_PARAM_BLOCK_INTERBSS, + WMI_PDEV_PARAM_SET_DISABLE_RESET_CMDID, + WMI_PDEV_PARAM_SET_MSDU_TTL_CMDID, + WMI_PDEV_PARAM_SET_PPDU_DURATION_CMDID, + WMI_PDEV_PARAM_TXBF_SOUND_PERIOD_CMDID, + WMI_PDEV_PARAM_SET_PROMISC_MODE_CMDID, + WMI_PDEV_PARAM_SET_BURST_MODE_CMDID, + WMI_PDEV_PARAM_EN_STATS, + WMI_PDEV_PARAM_MU_GROUP_POLICY, + WMI_PDEV_PARAM_NOISE_DETECTION, + WMI_PDEV_PARAM_NOISE_THRESHOLD, + WMI_PDEV_PARAM_DPD_ENABLE, + WMI_PDEV_PARAM_SET_MCAST_BCAST_ECHO, + WMI_PDEV_PARAM_ATF_STRICT_SCH, + WMI_PDEV_PARAM_ATF_SCHED_DURATION, + WMI_PDEV_PARAM_ANT_PLZN, + WMI_PDEV_PARAM_MGMT_RETRY_LIMIT, + WMI_PDEV_PARAM_SENSITIVITY_LEVEL, + WMI_PDEV_PARAM_SIGNED_TXPOWER_2G, + WMI_PDEV_PARAM_SIGNED_TXPOWER_5G, + WMI_PDEV_PARAM_ENABLE_PER_TID_AMSDU, + WMI_PDEV_PARAM_ENABLE_PER_TID_AMPDU, + WMI_PDEV_PARAM_CCA_THRESHOLD, + WMI_PDEV_PARAM_RTS_FIXED_RATE, + WMI_PDEV_PARAM_PDEV_RESET, + WMI_PDEV_PARAM_WAPI_MBSSID_OFFSET, + WMI_PDEV_PARAM_ARP_DBG_SRCADDR, + WMI_PDEV_PARAM_ARP_DBG_DSTADDR, + WMI_PDEV_PARAM_ATF_OBSS_NOISE_SCH, + WMI_PDEV_PARAM_ATF_OBSS_NOISE_SCALING_FACTOR, + WMI_PDEV_PARAM_CUST_TXPOWER_SCALE, + WMI_PDEV_PARAM_ATF_DYNAMIC_ENABLE, + WMI_PDEV_PARAM_CTRL_RETRY_LIMIT, + WMI_PDEV_PARAM_PROPAGATION_DELAY, + WMI_PDEV_PARAM_ENA_ANT_DIV, + WMI_PDEV_PARAM_FORCE_CHAIN_ANT, + WMI_PDEV_PARAM_ANT_DIV_SELFTEST, + WMI_PDEV_PARAM_ANT_DIV_SELFTEST_INTVL, + WMI_PDEV_PARAM_STATS_OBSERVATION_PERIOD, + WMI_PDEV_PARAM_TX_PPDU_DELAY_BIN_SIZE_MS, + WMI_PDEV_PARAM_TX_PPDU_DELAY_ARRAY_LEN, + WMI_PDEV_PARAM_TX_MPDU_AGGR_ARRAY_LEN, + WMI_PDEV_PARAM_RX_MPDU_AGGR_ARRAY_LEN, + WMI_PDEV_PARAM_TX_SCH_DELAY, + WMI_PDEV_PARAM_ENABLE_RTS_SIFS_BURSTING, + WMI_PDEV_PARAM_MAX_MPDUS_IN_AMPDU, + WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE, + WMI_PDEV_PARAM_FAST_PWR_TRANSITION, + WMI_PDEV_PARAM_RADIO_CHAN_STATS_ENABLE, + WMI_PDEV_PARAM_RADIO_DIAGNOSIS_ENABLE, + WMI_PDEV_PARAM_MESH_MCAST_ENABLE, + WMI_PDEV_PARAM_SET_CMD_OBSS_PD_THRESHOLD = 0xbc, + WMI_PDEV_PARAM_SET_CMD_OBSS_PD_PER_AC = 0xbe, + WMI_PDEV_PARAM_ENABLE_SR_PROHIBIT = 0xc6, +}; + +enum wmi_tlv_vdev_param { + WMI_VDEV_PARAM_RTS_THRESHOLD = 0x1, + WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + WMI_VDEV_PARAM_BEACON_INTERVAL, + WMI_VDEV_PARAM_LISTEN_INTERVAL, + WMI_VDEV_PARAM_MULTICAST_RATE, + WMI_VDEV_PARAM_MGMT_TX_RATE, + WMI_VDEV_PARAM_SLOT_TIME, + WMI_VDEV_PARAM_PREAMBLE, + WMI_VDEV_PARAM_SWBA_TIME, + WMI_VDEV_STATS_UPDATE_PERIOD, + WMI_VDEV_PWRSAVE_AGEOUT_TIME, + WMI_VDEV_HOST_SWBA_INTERVAL, + WMI_VDEV_PARAM_DTIM_PERIOD, + WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + WMI_VDEV_PARAM_WDS, + WMI_VDEV_PARAM_ATIM_WINDOW, + WMI_VDEV_PARAM_BMISS_COUNT_MAX, + WMI_VDEV_PARAM_BMISS_FIRST_BCNT, + WMI_VDEV_PARAM_BMISS_FINAL_BCNT, + WMI_VDEV_PARAM_FEATURE_WMM, + WMI_VDEV_PARAM_CHWIDTH, + WMI_VDEV_PARAM_CHEXTOFFSET, + WMI_VDEV_PARAM_DISABLE_HTPROTECTION, + WMI_VDEV_PARAM_STA_QUICKKICKOUT, + WMI_VDEV_PARAM_MGMT_RATE, + WMI_VDEV_PARAM_PROTECTION_MODE, + WMI_VDEV_PARAM_FIXED_RATE, + WMI_VDEV_PARAM_SGI, + WMI_VDEV_PARAM_LDPC, + WMI_VDEV_PARAM_TX_STBC, + WMI_VDEV_PARAM_RX_STBC, + WMI_VDEV_PARAM_INTRA_BSS_FWD, + WMI_VDEV_PARAM_DEF_KEYID, + WMI_VDEV_PARAM_NSS, + WMI_VDEV_PARAM_BCAST_DATA_RATE, + WMI_VDEV_PARAM_MCAST_DATA_RATE, + WMI_VDEV_PARAM_MCAST_INDICATE, + WMI_VDEV_PARAM_DHCP_INDICATE, + WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + WMI_VDEV_PARAM_AP_ENABLE_NAWDS, + WMI_VDEV_PARAM_ENABLE_RTSCTS, + WMI_VDEV_PARAM_TXBF, + WMI_VDEV_PARAM_PACKET_POWERSAVE, + WMI_VDEV_PARAM_DROP_UNENCRY, + WMI_VDEV_PARAM_TX_ENCAP_TYPE, + WMI_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, + WMI_VDEV_PARAM_EARLY_RX_ADJUST_ENABLE, + WMI_VDEV_PARAM_EARLY_RX_TGT_BMISS_NUM, + WMI_VDEV_PARAM_EARLY_RX_BMISS_SAMPLE_CYCLE, + WMI_VDEV_PARAM_EARLY_RX_SLOP_STEP, + WMI_VDEV_PARAM_EARLY_RX_INIT_SLOP, + WMI_VDEV_PARAM_EARLY_RX_ADJUST_PAUSE, + WMI_VDEV_PARAM_TX_PWRLIMIT, + WMI_VDEV_PARAM_SNR_NUM_FOR_CAL, + WMI_VDEV_PARAM_ROAM_FW_OFFLOAD, + WMI_VDEV_PARAM_ENABLE_RMC, + WMI_VDEV_PARAM_IBSS_MAX_BCN_LOST_MS, + WMI_VDEV_PARAM_MAX_RATE, + WMI_VDEV_PARAM_EARLY_RX_DRIFT_SAMPLE, + WMI_VDEV_PARAM_SET_IBSS_TX_FAIL_CNT_THR, + WMI_VDEV_PARAM_EBT_RESYNC_TIMEOUT, + WMI_VDEV_PARAM_AGGR_TRIG_EVENT_ENABLE, + WMI_VDEV_PARAM_IS_IBSS_POWER_SAVE_ALLOWED, + WMI_VDEV_PARAM_IS_POWER_COLLAPSE_ALLOWED, + WMI_VDEV_PARAM_IS_AWAKE_ON_TXRX_ENABLED, + WMI_VDEV_PARAM_INACTIVITY_CNT, + WMI_VDEV_PARAM_TXSP_END_INACTIVITY_TIME_MS, + WMI_VDEV_PARAM_DTIM_POLICY, + WMI_VDEV_PARAM_IBSS_PS_WARMUP_TIME_SECS, + WMI_VDEV_PARAM_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_ENABLE, + WMI_VDEV_PARAM_RX_LEAK_WINDOW, + WMI_VDEV_PARAM_STATS_AVG_FACTOR, + WMI_VDEV_PARAM_DISCONNECT_TH, + WMI_VDEV_PARAM_RTSCTS_RATE, + WMI_VDEV_PARAM_MCC_RTSCTS_PROTECTION_ENABLE, + WMI_VDEV_PARAM_MCC_BROADCAST_PROBE_ENABLE, + WMI_VDEV_PARAM_TXPOWER_SCALE, + WMI_VDEV_PARAM_TXPOWER_SCALE_DECR_DB, + WMI_VDEV_PARAM_MCAST2UCAST_SET, + WMI_VDEV_PARAM_RC_NUM_RETRIES, + WMI_VDEV_PARAM_CABQ_MAXDUR, + WMI_VDEV_PARAM_MFPTEST_SET, + WMI_VDEV_PARAM_RTS_FIXED_RATE, + WMI_VDEV_PARAM_VHT_SGIMASK, + WMI_VDEV_PARAM_VHT80_RATEMASK, + WMI_VDEV_PARAM_PROXY_STA, + WMI_VDEV_PARAM_VIRTUAL_CELL_MODE, + WMI_VDEV_PARAM_RX_DECAP_TYPE, + WMI_VDEV_PARAM_BW_NSS_RATEMASK, + WMI_VDEV_PARAM_SENSOR_AP, + WMI_VDEV_PARAM_BEACON_RATE, + WMI_VDEV_PARAM_DTIM_ENABLE_CTS, + WMI_VDEV_PARAM_STA_KICKOUT, + WMI_VDEV_PARAM_CAPABILITIES, + WMI_VDEV_PARAM_TSF_INCREMENT, + WMI_VDEV_PARAM_AMPDU_PER_AC, + WMI_VDEV_PARAM_RX_FILTER, + WMI_VDEV_PARAM_MGMT_TX_POWER, + WMI_VDEV_PARAM_NON_AGG_SW_RETRY_TH, + WMI_VDEV_PARAM_AGG_SW_RETRY_TH, + WMI_VDEV_PARAM_DISABLE_DYN_BW_RTS, + WMI_VDEV_PARAM_ATF_SSID_SCHED_POLICY, + WMI_VDEV_PARAM_HE_DCM, + WMI_VDEV_PARAM_HE_RANGE_EXT, + WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE, + WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME, + WMI_VDEV_PARAM_HE_LTF = 0x74, + WMI_VDEV_PARAM_ENABLE_DISABLE_RTT_RESPONDER_ROLE = 0x7d, + WMI_VDEV_PARAM_BA_MODE = 0x7e, + WMI_VDEV_PARAM_AUTORATE_MISC_CFG = 0x80, + WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE = 0x87, + WMI_VDEV_PARAM_6GHZ_PARAMS = 0x99, + WMI_VDEV_PARAM_PROTOTYPE = 0x8000, + WMI_VDEV_PARAM_BSS_COLOR, + WMI_VDEV_PARAM_SET_HEMU_MODE, + WMI_VDEV_PARAM_HEOPS_0_31 = 0x8003, +}; + +enum wmi_tlv_peer_flags { + WMI_TLV_PEER_AUTH = 0x00000001, + WMI_TLV_PEER_QOS = 0x00000002, + WMI_TLV_PEER_NEED_PTK_4_WAY = 0x00000004, + WMI_TLV_PEER_NEED_GTK_2_WAY = 0x00000010, + WMI_TLV_PEER_APSD = 0x00000800, + WMI_TLV_PEER_HT = 0x00001000, + WMI_TLV_PEER_40MHZ = 0x00002000, + WMI_TLV_PEER_STBC = 0x00008000, + WMI_TLV_PEER_LDPC = 0x00010000, + WMI_TLV_PEER_DYN_MIMOPS = 0x00020000, + WMI_TLV_PEER_STATIC_MIMOPS = 0x00040000, + WMI_TLV_PEER_SPATIAL_MUX = 0x00200000, + WMI_TLV_PEER_VHT = 0x02000000, + WMI_TLV_PEER_80MHZ = 0x04000000, + WMI_TLV_PEER_PMF = 0x08000000, + WMI_PEER_IS_P2P_CAPABLE = 0x20000000, + WMI_PEER_160MHZ = 0x40000000, + WMI_PEER_SAFEMODE_EN = 0x80000000, + +}; + +/** Enum list of TLV Tags for each parameter structure type. */ +enum wmi_tlv_tag { + WMI_TAG_LAST_RESERVED = 15, + WMI_TAG_FIRST_ARRAY_ENUM, + WMI_TAG_ARRAY_UINT32 = WMI_TAG_FIRST_ARRAY_ENUM, + WMI_TAG_ARRAY_BYTE, + WMI_TAG_ARRAY_STRUCT, + WMI_TAG_ARRAY_FIXED_STRUCT, + WMI_TAG_LAST_ARRAY_ENUM = 31, + WMI_TAG_SERVICE_READY_EVENT, + WMI_TAG_HAL_REG_CAPABILITIES, + WMI_TAG_WLAN_HOST_MEM_REQ, + WMI_TAG_READY_EVENT, + WMI_TAG_SCAN_EVENT, + WMI_TAG_PDEV_TPC_CONFIG_EVENT, + WMI_TAG_CHAN_INFO_EVENT, + WMI_TAG_COMB_PHYERR_RX_HDR, + WMI_TAG_VDEV_START_RESPONSE_EVENT, + WMI_TAG_VDEV_STOPPED_EVENT, + WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT, + WMI_TAG_PEER_STA_KICKOUT_EVENT, + WMI_TAG_MGMT_RX_HDR, + WMI_TAG_TBTT_OFFSET_EVENT, + WMI_TAG_TX_DELBA_COMPLETE_EVENT, + WMI_TAG_TX_ADDBA_COMPLETE_EVENT, + WMI_TAG_ROAM_EVENT, + WMI_TAG_WOW_EVENT_INFO, + WMI_TAG_WOW_EVENT_INFO_SECTION_BITMAP, + WMI_TAG_RTT_EVENT_HEADER, + WMI_TAG_RTT_ERROR_REPORT_EVENT, + WMI_TAG_RTT_MEAS_EVENT, + WMI_TAG_ECHO_EVENT, + WMI_TAG_FTM_INTG_EVENT, + WMI_TAG_VDEV_GET_KEEPALIVE_EVENT, + WMI_TAG_GPIO_INPUT_EVENT, + WMI_TAG_CSA_EVENT, + WMI_TAG_GTK_OFFLOAD_STATUS_EVENT, + WMI_TAG_IGTK_INFO, + WMI_TAG_DCS_INTERFERENCE_EVENT, + WMI_TAG_ATH_DCS_CW_INT, + WMI_TAG_WLAN_DCS_CW_INT = /* ALIAS */ + WMI_TAG_ATH_DCS_CW_INT, + WMI_TAG_ATH_DCS_WLAN_INT_STAT, + WMI_TAG_WLAN_DCS_IM_TGT_STATS_T = /* ALIAS */ + WMI_TAG_ATH_DCS_WLAN_INT_STAT, + WMI_TAG_WLAN_PROFILE_CTX_T, + WMI_TAG_WLAN_PROFILE_T, + WMI_TAG_PDEV_QVIT_EVENT, + WMI_TAG_HOST_SWBA_EVENT, + WMI_TAG_TIM_INFO, + WMI_TAG_P2P_NOA_INFO, + WMI_TAG_STATS_EVENT, + WMI_TAG_AVOID_FREQ_RANGES_EVENT, + WMI_TAG_AVOID_FREQ_RANGE_DESC, + WMI_TAG_GTK_REKEY_FAIL_EVENT, + WMI_TAG_INIT_CMD, + WMI_TAG_RESOURCE_CONFIG, + WMI_TAG_WLAN_HOST_MEMORY_CHUNK, + WMI_TAG_START_SCAN_CMD, + WMI_TAG_STOP_SCAN_CMD, + WMI_TAG_SCAN_CHAN_LIST_CMD, + WMI_TAG_CHANNEL, + WMI_TAG_PDEV_SET_REGDOMAIN_CMD, + WMI_TAG_PDEV_SET_PARAM_CMD, + WMI_TAG_PDEV_SET_WMM_PARAMS_CMD, + WMI_TAG_WMM_PARAMS, + WMI_TAG_PDEV_SET_QUIET_CMD, + WMI_TAG_VDEV_CREATE_CMD, + WMI_TAG_VDEV_DELETE_CMD, + WMI_TAG_VDEV_START_REQUEST_CMD, + WMI_TAG_P2P_NOA_DESCRIPTOR, + WMI_TAG_P2P_GO_SET_BEACON_IE, + WMI_TAG_GTK_OFFLOAD_CMD, + WMI_TAG_VDEV_UP_CMD, + WMI_TAG_VDEV_STOP_CMD, + WMI_TAG_VDEV_DOWN_CMD, + WMI_TAG_VDEV_SET_PARAM_CMD, + WMI_TAG_VDEV_INSTALL_KEY_CMD, + WMI_TAG_PEER_CREATE_CMD, + WMI_TAG_PEER_DELETE_CMD, + WMI_TAG_PEER_FLUSH_TIDS_CMD, + WMI_TAG_PEER_SET_PARAM_CMD, + WMI_TAG_PEER_ASSOC_COMPLETE_CMD, + WMI_TAG_VHT_RATE_SET, + WMI_TAG_BCN_TMPL_CMD, + WMI_TAG_PRB_TMPL_CMD, + WMI_TAG_BCN_PRB_INFO, + WMI_TAG_PEER_TID_ADDBA_CMD, + WMI_TAG_PEER_TID_DELBA_CMD, + WMI_TAG_STA_POWERSAVE_MODE_CMD, + WMI_TAG_STA_POWERSAVE_PARAM_CMD, + WMI_TAG_STA_DTIM_PS_METHOD_CMD, + WMI_TAG_ROAM_SCAN_MODE, + WMI_TAG_ROAM_SCAN_RSSI_THRESHOLD, + WMI_TAG_ROAM_SCAN_PERIOD, + WMI_TAG_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_TAG_PDEV_SUSPEND_CMD, + WMI_TAG_PDEV_RESUME_CMD, + WMI_TAG_ADD_BCN_FILTER_CMD, + WMI_TAG_RMV_BCN_FILTER_CMD, + WMI_TAG_WOW_ENABLE_CMD, + WMI_TAG_WOW_HOSTWAKEUP_FROM_SLEEP_CMD, + WMI_TAG_STA_UAPSD_AUTO_TRIG_CMD, + WMI_TAG_STA_UAPSD_AUTO_TRIG_PARAM, + WMI_TAG_SET_ARP_NS_OFFLOAD_CMD, + WMI_TAG_ARP_OFFLOAD_TUPLE, + WMI_TAG_NS_OFFLOAD_TUPLE, + WMI_TAG_FTM_INTG_CMD, + WMI_TAG_STA_KEEPALIVE_CMD, + WMI_TAG_STA_KEEPALIVE_ARP_RESPONSE, + WMI_TAG_P2P_SET_VENDOR_IE_DATA_CMD, + WMI_TAG_AP_PS_PEER_CMD, + WMI_TAG_PEER_RATE_RETRY_SCHED_CMD, + WMI_TAG_WLAN_PROFILE_TRIGGER_CMD, + WMI_TAG_WLAN_PROFILE_SET_HIST_INTVL_CMD, + WMI_TAG_WLAN_PROFILE_GET_PROF_DATA_CMD, + WMI_TAG_WLAN_PROFILE_ENABLE_PROFILE_ID_CMD, + WMI_TAG_WOW_DEL_PATTERN_CMD, + WMI_TAG_WOW_ADD_DEL_EVT_CMD, + WMI_TAG_RTT_MEASREQ_HEAD, + WMI_TAG_RTT_MEASREQ_BODY, + WMI_TAG_RTT_TSF_CMD, + WMI_TAG_VDEV_SPECTRAL_CONFIGURE_CMD, + WMI_TAG_VDEV_SPECTRAL_ENABLE_CMD, + WMI_TAG_REQUEST_STATS_CMD, + WMI_TAG_NLO_CONFIG_CMD, + WMI_TAG_NLO_CONFIGURED_PARAMETERS, + WMI_TAG_CSA_OFFLOAD_ENABLE_CMD, + WMI_TAG_CSA_OFFLOAD_CHANSWITCH_CMD, + WMI_TAG_CHATTER_SET_MODE_CMD, + WMI_TAG_ECHO_CMD, + WMI_TAG_VDEV_SET_KEEPALIVE_CMD, + WMI_TAG_VDEV_GET_KEEPALIVE_CMD, + WMI_TAG_FORCE_FW_HANG_CMD, + WMI_TAG_GPIO_CONFIG_CMD, + WMI_TAG_GPIO_OUTPUT_CMD, + WMI_TAG_PEER_ADD_WDS_ENTRY_CMD, + WMI_TAG_PEER_REMOVE_WDS_ENTRY_CMD, + WMI_TAG_BCN_TX_HDR, + WMI_TAG_BCN_SEND_FROM_HOST_CMD, + WMI_TAG_MGMT_TX_HDR, + WMI_TAG_ADDBA_CLEAR_RESP_CMD, + WMI_TAG_ADDBA_SEND_CMD, + WMI_TAG_DELBA_SEND_CMD, + WMI_TAG_ADDBA_SETRESPONSE_CMD, + WMI_TAG_SEND_SINGLEAMSDU_CMD, + WMI_TAG_PDEV_PKTLOG_ENABLE_CMD, + WMI_TAG_PDEV_PKTLOG_DISABLE_CMD, + WMI_TAG_PDEV_SET_HT_IE_CMD, + WMI_TAG_PDEV_SET_VHT_IE_CMD, + WMI_TAG_PDEV_SET_DSCP_TID_MAP_CMD, + WMI_TAG_PDEV_GREEN_AP_PS_ENABLE_CMD, + WMI_TAG_PDEV_GET_TPC_CONFIG_CMD, + WMI_TAG_PDEV_SET_BASE_MACADDR_CMD, + WMI_TAG_PEER_MCAST_GROUP_CMD, + WMI_TAG_ROAM_AP_PROFILE, + WMI_TAG_AP_PROFILE, + WMI_TAG_SCAN_SCH_PRIORITY_TABLE_CMD, + WMI_TAG_PDEV_DFS_ENABLE_CMD, + WMI_TAG_PDEV_DFS_DISABLE_CMD, + WMI_TAG_WOW_ADD_PATTERN_CMD, + WMI_TAG_WOW_BITMAP_PATTERN_T, + WMI_TAG_WOW_IPV4_SYNC_PATTERN_T, + WMI_TAG_WOW_IPV6_SYNC_PATTERN_T, + WMI_TAG_WOW_MAGIC_PATTERN_CMD, + WMI_TAG_SCAN_UPDATE_REQUEST_CMD, + WMI_TAG_CHATTER_PKT_COALESCING_FILTER, + WMI_TAG_CHATTER_COALESCING_ADD_FILTER_CMD, + WMI_TAG_CHATTER_COALESCING_DELETE_FILTER_CMD, + WMI_TAG_CHATTER_COALESCING_QUERY_CMD, + WMI_TAG_TXBF_CMD, + WMI_TAG_DEBUG_LOG_CONFIG_CMD, + WMI_TAG_NLO_EVENT, + WMI_TAG_CHATTER_QUERY_REPLY_EVENT, + WMI_TAG_UPLOAD_H_HDR, + WMI_TAG_CAPTURE_H_EVENT_HDR, + WMI_TAG_VDEV_WNM_SLEEPMODE_CMD, + WMI_TAG_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMD, + WMI_TAG_VDEV_WMM_ADDTS_CMD, + WMI_TAG_VDEV_WMM_DELTS_CMD, + WMI_TAG_VDEV_SET_WMM_PARAMS_CMD, + WMI_TAG_TDLS_SET_STATE_CMD, + WMI_TAG_TDLS_PEER_UPDATE_CMD, + WMI_TAG_TDLS_PEER_EVENT, + WMI_TAG_TDLS_PEER_CAPABILITIES, + WMI_TAG_VDEV_MCC_SET_TBTT_MODE_CMD, + WMI_TAG_ROAM_CHAN_LIST, + WMI_TAG_VDEV_MCC_BCN_INTVL_CHANGE_EVENT, + WMI_TAG_RESMGR_ADAPTIVE_OCS_ENABLE_DISABLE_CMD, + WMI_TAG_RESMGR_SET_CHAN_TIME_QUOTA_CMD, + WMI_TAG_RESMGR_SET_CHAN_LATENCY_CMD, + WMI_TAG_BA_REQ_SSN_CMD, + WMI_TAG_BA_RSP_SSN_EVENT, + WMI_TAG_STA_SMPS_FORCE_MODE_CMD, + WMI_TAG_SET_MCASTBCAST_FILTER_CMD, + WMI_TAG_P2P_SET_OPPPS_CMD, + WMI_TAG_P2P_SET_NOA_CMD, + WMI_TAG_BA_REQ_SSN_CMD_SUB_STRUCT_PARAM, + WMI_TAG_BA_REQ_SSN_EVENT_SUB_STRUCT_PARAM, + WMI_TAG_STA_SMPS_PARAM_CMD, + WMI_TAG_VDEV_SET_GTX_PARAMS_CMD, + WMI_TAG_MCC_SCHED_TRAFFIC_STATS_CMD, + WMI_TAG_MCC_SCHED_STA_TRAFFIC_STATS, + WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT, + WMI_TAG_P2P_NOA_EVENT, + WMI_TAG_HB_SET_ENABLE_CMD, + WMI_TAG_HB_SET_TCP_PARAMS_CMD, + WMI_TAG_HB_SET_TCP_PKT_FILTER_CMD, + WMI_TAG_HB_SET_UDP_PARAMS_CMD, + WMI_TAG_HB_SET_UDP_PKT_FILTER_CMD, + WMI_TAG_HB_IND_EVENT, + WMI_TAG_TX_PAUSE_EVENT, + WMI_TAG_RFKILL_EVENT, + WMI_TAG_DFS_RADAR_EVENT, + WMI_TAG_DFS_PHYERR_FILTER_ENA_CMD, + WMI_TAG_DFS_PHYERR_FILTER_DIS_CMD, + WMI_TAG_BATCH_SCAN_RESULT_SCAN_LIST, + WMI_TAG_BATCH_SCAN_RESULT_NETWORK_INFO, + WMI_TAG_BATCH_SCAN_ENABLE_CMD, + WMI_TAG_BATCH_SCAN_DISABLE_CMD, + WMI_TAG_BATCH_SCAN_TRIGGER_RESULT_CMD, + WMI_TAG_BATCH_SCAN_ENABLED_EVENT, + WMI_TAG_BATCH_SCAN_RESULT_EVENT, + WMI_TAG_VDEV_PLMREQ_START_CMD, + WMI_TAG_VDEV_PLMREQ_STOP_CMD, + WMI_TAG_THERMAL_MGMT_CMD, + WMI_TAG_THERMAL_MGMT_EVENT, + WMI_TAG_PEER_INFO_REQ_CMD, + WMI_TAG_PEER_INFO_EVENT, + WMI_TAG_PEER_INFO, + WMI_TAG_PEER_TX_FAIL_CNT_THR_EVENT, + WMI_TAG_RMC_SET_MODE_CMD, + WMI_TAG_RMC_SET_ACTION_PERIOD_CMD, + WMI_TAG_RMC_CONFIG_CMD, + WMI_TAG_MHF_OFFLOAD_SET_MODE_CMD, + WMI_TAG_MHF_OFFLOAD_PLUMB_ROUTING_TABLE_CMD, + WMI_TAG_ADD_PROACTIVE_ARP_RSP_PATTERN_CMD, + WMI_TAG_DEL_PROACTIVE_ARP_RSP_PATTERN_CMD, + WMI_TAG_NAN_CMD_PARAM, + WMI_TAG_NAN_EVENT_HDR, + WMI_TAG_PDEV_L1SS_TRACK_EVENT, + WMI_TAG_DIAG_DATA_CONTAINER_EVENT, + WMI_TAG_MODEM_POWER_STATE_CMD_PARAM, + WMI_TAG_PEER_GET_ESTIMATED_LINKSPEED_CMD, + WMI_TAG_PEER_ESTIMATED_LINKSPEED_EVENT, + WMI_TAG_AGGR_STATE_TRIG_EVENT, + WMI_TAG_MHF_OFFLOAD_ROUTING_TABLE_ENTRY, + WMI_TAG_ROAM_SCAN_CMD, + WMI_TAG_REQ_STATS_EXT_CMD, + WMI_TAG_STATS_EXT_EVENT, + WMI_TAG_OBSS_SCAN_ENABLE_CMD, + WMI_TAG_OBSS_SCAN_DISABLE_CMD, + WMI_TAG_OFFLOAD_PRB_RSP_TX_STATUS_EVENT, + WMI_TAG_PDEV_SET_LED_CONFIG_CMD, + WMI_TAG_HOST_AUTO_SHUTDOWN_CFG_CMD, + WMI_TAG_HOST_AUTO_SHUTDOWN_EVENT, + WMI_TAG_UPDATE_WHAL_MIB_STATS_EVENT, + WMI_TAG_CHAN_AVOID_UPDATE_CMD_PARAM, + WMI_TAG_WOW_IOAC_PKT_PATTERN_T, + WMI_TAG_WOW_IOAC_TMR_PATTERN_T, + WMI_TAG_WOW_IOAC_ADD_KEEPALIVE_CMD, + WMI_TAG_WOW_IOAC_DEL_KEEPALIVE_CMD, + WMI_TAG_WOW_IOAC_KEEPALIVE_T, + WMI_TAG_WOW_IOAC_ADD_PATTERN_CMD, + WMI_TAG_WOW_IOAC_DEL_PATTERN_CMD, + WMI_TAG_START_LINK_STATS_CMD, + WMI_TAG_CLEAR_LINK_STATS_CMD, + WMI_TAG_REQUEST_LINK_STATS_CMD, + WMI_TAG_IFACE_LINK_STATS_EVENT, + WMI_TAG_RADIO_LINK_STATS_EVENT, + WMI_TAG_PEER_STATS_EVENT, + WMI_TAG_CHANNEL_STATS, + WMI_TAG_RADIO_LINK_STATS, + WMI_TAG_RATE_STATS, + WMI_TAG_PEER_LINK_STATS, + WMI_TAG_WMM_AC_STATS, + WMI_TAG_IFACE_LINK_STATS, + WMI_TAG_LPI_MGMT_SNOOPING_CONFIG_CMD, + WMI_TAG_LPI_START_SCAN_CMD, + WMI_TAG_LPI_STOP_SCAN_CMD, + WMI_TAG_LPI_RESULT_EVENT, + WMI_TAG_PEER_STATE_EVENT, + WMI_TAG_EXTSCAN_BUCKET_CMD, + WMI_TAG_EXTSCAN_BUCKET_CHANNEL_EVENT, + WMI_TAG_EXTSCAN_START_CMD, + WMI_TAG_EXTSCAN_STOP_CMD, + WMI_TAG_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMD, + WMI_TAG_EXTSCAN_WLAN_CHANGE_BSSID_PARAM_CMD, + WMI_TAG_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMD, + WMI_TAG_EXTSCAN_GET_CACHED_RESULTS_CMD, + WMI_TAG_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMD, + WMI_TAG_EXTSCAN_SET_CAPABILITIES_CMD, + WMI_TAG_EXTSCAN_GET_CAPABILITIES_CMD, + WMI_TAG_EXTSCAN_OPERATION_EVENT, + WMI_TAG_EXTSCAN_START_STOP_EVENT, + WMI_TAG_EXTSCAN_TABLE_USAGE_EVENT, + WMI_TAG_EXTSCAN_WLAN_DESCRIPTOR_EVENT, + WMI_TAG_EXTSCAN_RSSI_INFO_EVENT, + WMI_TAG_EXTSCAN_CACHED_RESULTS_EVENT, + WMI_TAG_EXTSCAN_WLAN_CHANGE_RESULTS_EVENT, + WMI_TAG_EXTSCAN_WLAN_CHANGE_RESULT_BSSID_EVENT, + WMI_TAG_EXTSCAN_HOTLIST_MATCH_EVENT, + WMI_TAG_EXTSCAN_CAPABILITIES_EVENT, + WMI_TAG_EXTSCAN_CACHE_CAPABILITIES_EVENT, + WMI_TAG_EXTSCAN_WLAN_CHANGE_MONITOR_CAPABILITIES_EVENT, + WMI_TAG_EXTSCAN_HOTLIST_MONITOR_CAPABILITIES_EVENT, + WMI_TAG_D0_WOW_ENABLE_DISABLE_CMD, + WMI_TAG_D0_WOW_DISABLE_ACK_EVENT, + WMI_TAG_UNIT_TEST_CMD, + WMI_TAG_ROAM_OFFLOAD_TLV_PARAM, + WMI_TAG_ROAM_11I_OFFLOAD_TLV_PARAM, + WMI_TAG_ROAM_11R_OFFLOAD_TLV_PARAM, + WMI_TAG_ROAM_ESE_OFFLOAD_TLV_PARAM, + WMI_TAG_ROAM_SYNCH_EVENT, + WMI_TAG_ROAM_SYNCH_COMPLETE, + WMI_TAG_EXTWOW_ENABLE_CMD, + WMI_TAG_EXTWOW_SET_APP_TYPE1_PARAMS_CMD, + WMI_TAG_EXTWOW_SET_APP_TYPE2_PARAMS_CMD, + WMI_TAG_LPI_STATUS_EVENT, + WMI_TAG_LPI_HANDOFF_EVENT, + WMI_TAG_VDEV_RATE_STATS_EVENT, + WMI_TAG_VDEV_RATE_HT_INFO, + WMI_TAG_RIC_REQUEST, + WMI_TAG_PDEV_GET_TEMPERATURE_CMD, + WMI_TAG_PDEV_TEMPERATURE_EVENT, + WMI_TAG_SET_DHCP_SERVER_OFFLOAD_CMD, + WMI_TAG_TPC_CHAINMASK_CONFIG_CMD, + WMI_TAG_RIC_TSPEC, + WMI_TAG_TPC_CHAINMASK_CONFIG, + WMI_TAG_IPA_OFFLOAD_ENABLE_DISABLE_CMD, + WMI_TAG_SCAN_PROB_REQ_OUI_CMD, + WMI_TAG_KEY_MATERIAL, + WMI_TAG_TDLS_SET_OFFCHAN_MODE_CMD, + WMI_TAG_SET_LED_FLASHING_CMD, + WMI_TAG_MDNS_OFFLOAD_CMD, + WMI_TAG_MDNS_SET_FQDN_CMD, + WMI_TAG_MDNS_SET_RESP_CMD, + WMI_TAG_MDNS_GET_STATS_CMD, + WMI_TAG_MDNS_STATS_EVENT, + WMI_TAG_ROAM_INVOKE_CMD, + WMI_TAG_PDEV_RESUME_EVENT, + WMI_TAG_PDEV_SET_ANTENNA_DIVERSITY_CMD, + WMI_TAG_SAP_OFL_ENABLE_CMD, + WMI_TAG_SAP_OFL_ADD_STA_EVENT, + WMI_TAG_SAP_OFL_DEL_STA_EVENT, + WMI_TAG_APFIND_CMD_PARAM, + WMI_TAG_APFIND_EVENT_HDR, + WMI_TAG_OCB_SET_SCHED_CMD, + WMI_TAG_OCB_SET_SCHED_EVENT, + WMI_TAG_OCB_SET_CONFIG_CMD, + WMI_TAG_OCB_SET_CONFIG_RESP_EVENT, + WMI_TAG_OCB_SET_UTC_TIME_CMD, + WMI_TAG_OCB_START_TIMING_ADVERT_CMD, + WMI_TAG_OCB_STOP_TIMING_ADVERT_CMD, + WMI_TAG_OCB_GET_TSF_TIMER_CMD, + WMI_TAG_OCB_GET_TSF_TIMER_RESP_EVENT, + WMI_TAG_DCC_GET_STATS_CMD, + WMI_TAG_DCC_CHANNEL_STATS_REQUEST, + WMI_TAG_DCC_GET_STATS_RESP_EVENT, + WMI_TAG_DCC_CLEAR_STATS_CMD, + WMI_TAG_DCC_UPDATE_NDL_CMD, + WMI_TAG_DCC_UPDATE_NDL_RESP_EVENT, + WMI_TAG_DCC_STATS_EVENT, + WMI_TAG_OCB_CHANNEL, + WMI_TAG_OCB_SCHEDULE_ELEMENT, + WMI_TAG_DCC_NDL_STATS_PER_CHANNEL, + WMI_TAG_DCC_NDL_CHAN, + WMI_TAG_QOS_PARAMETER, + WMI_TAG_DCC_NDL_ACTIVE_STATE_CONFIG, + WMI_TAG_ROAM_SCAN_EXTENDED_THRESHOLD_PARAM, + WMI_TAG_ROAM_FILTER, + WMI_TAG_PASSPOINT_CONFIG_CMD, + WMI_TAG_PASSPOINT_EVENT_HDR, + WMI_TAG_EXTSCAN_CONFIGURE_HOTLIST_SSID_MONITOR_CMD, + WMI_TAG_EXTSCAN_HOTLIST_SSID_MATCH_EVENT, + WMI_TAG_VDEV_TSF_TSTAMP_ACTION_CMD, + WMI_TAG_VDEV_TSF_REPORT_EVENT, + WMI_TAG_GET_FW_MEM_DUMP, + WMI_TAG_UPDATE_FW_MEM_DUMP, + WMI_TAG_FW_MEM_DUMP_PARAMS, + WMI_TAG_DEBUG_MESG_FLUSH, + WMI_TAG_DEBUG_MESG_FLUSH_COMPLETE, + WMI_TAG_PEER_SET_RATE_REPORT_CONDITION, + WMI_TAG_ROAM_SUBNET_CHANGE_CONFIG, + WMI_TAG_VDEV_SET_IE_CMD, + WMI_TAG_RSSI_BREACH_MONITOR_CONFIG, + WMI_TAG_RSSI_BREACH_EVENT, + WMI_TAG_WOW_EVENT_INITIAL_WAKEUP, + WMI_TAG_SOC_SET_PCL_CMD, + WMI_TAG_SOC_SET_HW_MODE_CMD, + WMI_TAG_SOC_SET_HW_MODE_RESPONSE_EVENT, + WMI_TAG_SOC_HW_MODE_TRANSITION_EVENT, + WMI_TAG_VDEV_TXRX_STREAMS, + WMI_TAG_SOC_SET_HW_MODE_RESPONSE_VDEV_MAC_ENTRY, + WMI_TAG_SOC_SET_DUAL_MAC_CONFIG_CMD, + WMI_TAG_SOC_SET_DUAL_MAC_CONFIG_RESPONSE_EVENT, + WMI_TAG_WOW_IOAC_SOCK_PATTERN_T, + WMI_TAG_WOW_ENABLE_ICMPV6_NA_FLT_CMD, + WMI_TAG_DIAG_EVENT_LOG_CONFIG, + WMI_TAG_DIAG_EVENT_LOG_SUPPORTED_EVENT_FIXED_PARAMS, + WMI_TAG_PACKET_FILTER_CONFIG, + WMI_TAG_PACKET_FILTER_ENABLE, + WMI_TAG_SAP_SET_BLACKLIST_PARAM_CMD, + WMI_TAG_MGMT_TX_SEND_CMD, + WMI_TAG_MGMT_TX_COMPL_EVENT, + WMI_TAG_SOC_SET_ANTENNA_MODE_CMD, + WMI_TAG_WOW_UDP_SVC_OFLD_CMD, + WMI_TAG_LRO_INFO_CMD, + WMI_TAG_ROAM_EARLYSTOP_RSSI_THRES_PARAM, + WMI_TAG_SERVICE_READY_EXT_EVENT, + WMI_TAG_MAWC_SENSOR_REPORT_IND_CMD, + WMI_TAG_MAWC_ENABLE_SENSOR_EVENT, + WMI_TAG_ROAM_CONFIGURE_MAWC_CMD, + WMI_TAG_NLO_CONFIGURE_MAWC_CMD, + WMI_TAG_EXTSCAN_CONFIGURE_MAWC_CMD, + WMI_TAG_PEER_ASSOC_CONF_EVENT, + WMI_TAG_WOW_HOSTWAKEUP_GPIO_PIN_PATTERN_CONFIG_CMD, + WMI_TAG_AP_PS_EGAP_PARAM_CMD, + WMI_TAG_AP_PS_EGAP_INFO_EVENT, + WMI_TAG_PMF_OFFLOAD_SET_SA_QUERY_CMD, + WMI_TAG_TRANSFER_DATA_TO_FLASH_CMD, + WMI_TAG_TRANSFER_DATA_TO_FLASH_COMPLETE_EVENT, + WMI_TAG_SCPC_EVENT, + WMI_TAG_AP_PS_EGAP_INFO_CHAINMASK_LIST, + WMI_TAG_STA_SMPS_FORCE_MODE_COMPLETE_EVENT, + WMI_TAG_BPF_GET_CAPABILITY_CMD, + WMI_TAG_BPF_CAPABILITY_INFO_EVT, + WMI_TAG_BPF_GET_VDEV_STATS_CMD, + WMI_TAG_BPF_VDEV_STATS_INFO_EVT, + WMI_TAG_BPF_SET_VDEV_INSTRUCTIONS_CMD, + WMI_TAG_BPF_DEL_VDEV_INSTRUCTIONS_CMD, + WMI_TAG_VDEV_DELETE_RESP_EVENT, + WMI_TAG_PEER_DELETE_RESP_EVENT, + WMI_TAG_ROAM_DENSE_THRES_PARAM, + WMI_TAG_ENLO_CANDIDATE_SCORE_PARAM, + WMI_TAG_PEER_UPDATE_WDS_ENTRY_CMD, + WMI_TAG_VDEV_CONFIG_RATEMASK, + WMI_TAG_PDEV_FIPS_CMD, + WMI_TAG_PDEV_SMART_ANT_ENABLE_CMD, + WMI_TAG_PDEV_SMART_ANT_SET_RX_ANTENNA_CMD, + WMI_TAG_PEER_SMART_ANT_SET_TX_ANTENNA_CMD, + WMI_TAG_PEER_SMART_ANT_SET_TRAIN_ANTENNA_CMD, + WMI_TAG_PEER_SMART_ANT_SET_NODE_CONFIG_OPS_CMD, + WMI_TAG_PDEV_SET_ANT_SWITCH_TBL_CMD, + WMI_TAG_PDEV_SET_CTL_TABLE_CMD, + WMI_TAG_PDEV_SET_MIMOGAIN_TABLE_CMD, + WMI_TAG_FWTEST_SET_PARAM_CMD, + WMI_TAG_PEER_ATF_REQUEST, + WMI_TAG_VDEV_ATF_REQUEST, + WMI_TAG_PDEV_GET_ANI_CCK_CONFIG_CMD, + WMI_TAG_PDEV_GET_ANI_OFDM_CONFIG_CMD, + WMI_TAG_INST_RSSI_STATS_RESP, + WMI_TAG_MED_UTIL_REPORT_EVENT, + WMI_TAG_PEER_STA_PS_STATECHANGE_EVENT, + WMI_TAG_WDS_ADDR_EVENT, + WMI_TAG_PEER_RATECODE_LIST_EVENT, + WMI_TAG_PDEV_NFCAL_POWER_ALL_CHANNELS_EVENT, + WMI_TAG_PDEV_TPC_EVENT, + WMI_TAG_ANI_OFDM_EVENT, + WMI_TAG_ANI_CCK_EVENT, + WMI_TAG_PDEV_CHANNEL_HOPPING_EVENT, + WMI_TAG_PDEV_FIPS_EVENT, + WMI_TAG_ATF_PEER_INFO, + WMI_TAG_PDEV_GET_TPC_CMD, + WMI_TAG_VDEV_FILTER_NRP_CONFIG_CMD, + WMI_TAG_QBOOST_CFG_CMD, + WMI_TAG_PDEV_SMART_ANT_GPIO_HANDLE, + WMI_TAG_PEER_SMART_ANT_SET_TX_ANTENNA_SERIES, + WMI_TAG_PEER_SMART_ANT_SET_TRAIN_ANTENNA_PARAM, + WMI_TAG_PDEV_SET_ANT_CTRL_CHAIN, + WMI_TAG_PEER_CCK_OFDM_RATE_INFO, + WMI_TAG_PEER_MCS_RATE_INFO, + WMI_TAG_PDEV_NFCAL_POWER_ALL_CHANNELS_NFDBR, + WMI_TAG_PDEV_NFCAL_POWER_ALL_CHANNELS_NFDBM, + WMI_TAG_PDEV_NFCAL_POWER_ALL_CHANNELS_FREQNUM, + WMI_TAG_MU_REPORT_TOTAL_MU, + WMI_TAG_VDEV_SET_DSCP_TID_MAP_CMD, + WMI_TAG_ROAM_SET_MBO, + WMI_TAG_MIB_STATS_ENABLE_CMD, + WMI_TAG_NAN_DISC_IFACE_CREATED_EVENT, + WMI_TAG_NAN_DISC_IFACE_DELETED_EVENT, + WMI_TAG_NAN_STARTED_CLUSTER_EVENT, + WMI_TAG_NAN_JOINED_CLUSTER_EVENT, + WMI_TAG_NDI_GET_CAP_REQ, + WMI_TAG_NDP_INITIATOR_REQ, + WMI_TAG_NDP_RESPONDER_REQ, + WMI_TAG_NDP_END_REQ, + WMI_TAG_NDI_CAP_RSP_EVENT, + WMI_TAG_NDP_INITIATOR_RSP_EVENT, + WMI_TAG_NDP_RESPONDER_RSP_EVENT, + WMI_TAG_NDP_END_RSP_EVENT, + WMI_TAG_NDP_INDICATION_EVENT, + WMI_TAG_NDP_CONFIRM_EVENT, + WMI_TAG_NDP_END_INDICATION_EVENT, + WMI_TAG_VDEV_SET_QUIET_CMD, + WMI_TAG_PDEV_SET_PCL_CMD, + WMI_TAG_PDEV_SET_HW_MODE_CMD, + WMI_TAG_PDEV_SET_MAC_CONFIG_CMD, + WMI_TAG_PDEV_SET_ANTENNA_MODE_CMD, + WMI_TAG_PDEV_SET_HW_MODE_RESPONSE_EVENT, + WMI_TAG_PDEV_HW_MODE_TRANSITION_EVENT, + WMI_TAG_PDEV_SET_HW_MODE_RESPONSE_VDEV_MAC_ENTRY, + WMI_TAG_PDEV_SET_MAC_CONFIG_RESPONSE_EVENT, + WMI_TAG_COEX_CONFIG_CMD, + WMI_TAG_CONFIG_ENHANCED_MCAST_FILTER, + WMI_TAG_CHAN_AVOID_RPT_ALLOW_CMD, + WMI_TAG_SET_PERIODIC_CHANNEL_STATS_CONFIG, + WMI_TAG_VDEV_SET_CUSTOM_AGGR_SIZE_CMD, + WMI_TAG_PDEV_WAL_POWER_DEBUG_CMD, + WMI_TAG_MAC_PHY_CAPABILITIES, + WMI_TAG_HW_MODE_CAPABILITIES, + WMI_TAG_SOC_MAC_PHY_HW_MODE_CAPS, + WMI_TAG_HAL_REG_CAPABILITIES_EXT, + WMI_TAG_SOC_HAL_REG_CAPABILITIES, + WMI_TAG_VDEV_WISA_CMD, + WMI_TAG_TX_POWER_LEVEL_STATS_EVT, + WMI_TAG_SCAN_ADAPTIVE_DWELL_PARAMETERS_TLV, + WMI_TAG_SCAN_ADAPTIVE_DWELL_CONFIG, + WMI_TAG_WOW_SET_ACTION_WAKE_UP_CMD, + WMI_TAG_NDP_END_RSP_PER_NDI, + WMI_TAG_PEER_BWF_REQUEST, + WMI_TAG_BWF_PEER_INFO, + WMI_TAG_DBGLOG_TIME_STAMP_SYNC_CMD, + WMI_TAG_RMC_SET_LEADER_CMD, + WMI_TAG_RMC_MANUAL_LEADER_EVENT, + WMI_TAG_PER_CHAIN_RSSI_STATS, + WMI_TAG_RSSI_STATS, + WMI_TAG_P2P_LO_START_CMD, + WMI_TAG_P2P_LO_STOP_CMD, + WMI_TAG_P2P_LO_STOPPED_EVENT, + WMI_TAG_REORDER_QUEUE_SETUP_CMD, + WMI_TAG_REORDER_QUEUE_REMOVE_CMD, + WMI_TAG_SET_MULTIPLE_MCAST_FILTER_CMD, + WMI_TAG_MGMT_TX_COMPL_BUNDLE_EVENT, + WMI_TAG_READ_DATA_FROM_FLASH_CMD, + WMI_TAG_READ_DATA_FROM_FLASH_EVENT, + WMI_TAG_PDEV_SET_REORDER_TIMEOUT_VAL_CMD, + WMI_TAG_PEER_SET_RX_BLOCKSIZE_CMD, + WMI_TAG_PDEV_SET_WAKEUP_CONFIG_CMDID, + WMI_TAG_TLV_BUF_LEN_PARAM, + WMI_TAG_SERVICE_AVAILABLE_EVENT, + WMI_TAG_PEER_ANTDIV_INFO_REQ_CMD, + WMI_TAG_PEER_ANTDIV_INFO_EVENT, + WMI_TAG_PEER_ANTDIV_INFO, + WMI_TAG_PDEV_GET_ANTDIV_STATUS_CMD, + WMI_TAG_PDEV_ANTDIV_STATUS_EVENT, + WMI_TAG_MNT_FILTER_CMD, + WMI_TAG_GET_CHIP_POWER_STATS_CMD, + WMI_TAG_PDEV_CHIP_POWER_STATS_EVENT, + WMI_TAG_COEX_GET_ANTENNA_ISOLATION_CMD, + WMI_TAG_COEX_REPORT_ISOLATION_EVENT, + WMI_TAG_CHAN_CCA_STATS, + WMI_TAG_PEER_SIGNAL_STATS, + WMI_TAG_TX_STATS, + WMI_TAG_PEER_AC_TX_STATS, + WMI_TAG_RX_STATS, + WMI_TAG_PEER_AC_RX_STATS, + WMI_TAG_REPORT_STATS_EVENT, + WMI_TAG_CHAN_CCA_STATS_THRESH, + WMI_TAG_PEER_SIGNAL_STATS_THRESH, + WMI_TAG_TX_STATS_THRESH, + WMI_TAG_RX_STATS_THRESH, + WMI_TAG_PDEV_SET_STATS_THRESHOLD_CMD, + WMI_TAG_REQUEST_WLAN_STATS_CMD, + WMI_TAG_RX_AGGR_FAILURE_EVENT, + WMI_TAG_RX_AGGR_FAILURE_INFO, + WMI_TAG_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMD, + WMI_TAG_VDEV_ENCRYPT_DECRYPT_DATA_RESP_EVENT, + WMI_TAG_PDEV_BAND_TO_MAC, + WMI_TAG_TBTT_OFFSET_INFO, + WMI_TAG_TBTT_OFFSET_EXT_EVENT, + WMI_TAG_SAR_LIMITS_CMD, + WMI_TAG_SAR_LIMIT_CMD_ROW, + WMI_TAG_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMD, + WMI_TAG_PDEV_DFS_PHYERR_OFFLOAD_DISABLE_CMD, + WMI_TAG_VDEV_ADFS_CH_CFG_CMD, + WMI_TAG_VDEV_ADFS_OCAC_ABORT_CMD, + WMI_TAG_PDEV_DFS_RADAR_DETECTION_EVENT, + WMI_TAG_VDEV_ADFS_OCAC_COMPLETE_EVENT, + WMI_TAG_VDEV_DFS_CAC_COMPLETE_EVENT, + WMI_TAG_VENDOR_OUI, + WMI_TAG_REQUEST_RCPI_CMD, + WMI_TAG_UPDATE_RCPI_EVENT, + WMI_TAG_REQUEST_PEER_STATS_INFO_CMD, + WMI_TAG_PEER_STATS_INFO, + WMI_TAG_PEER_STATS_INFO_EVENT, + WMI_TAG_PKGID_EVENT, + WMI_TAG_CONNECTED_NLO_RSSI_PARAMS, + WMI_TAG_SET_CURRENT_COUNTRY_CMD, + WMI_TAG_REGULATORY_RULE_STRUCT, + WMI_TAG_REG_CHAN_LIST_CC_EVENT, + WMI_TAG_11D_SCAN_START_CMD, + WMI_TAG_11D_SCAN_STOP_CMD, + WMI_TAG_11D_NEW_COUNTRY_EVENT, + WMI_TAG_REQUEST_RADIO_CHAN_STATS_CMD, + WMI_TAG_RADIO_CHAN_STATS, + WMI_TAG_RADIO_CHAN_STATS_EVENT, + WMI_TAG_ROAM_PER_CONFIG, + WMI_TAG_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMD, + WMI_TAG_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENT, + WMI_TAG_BPF_SET_VDEV_ACTIVE_MODE_CMD, + WMI_TAG_HW_DATA_FILTER_CMD, + WMI_TAG_CONNECTED_NLO_BSS_BAND_RSSI_PREF, + WMI_TAG_PEER_OPER_MODE_CHANGE_EVENT, + WMI_TAG_CHIP_POWER_SAVE_FAILURE_DETECTED, + WMI_TAG_PDEV_MULTIPLE_VDEV_RESTART_REQUEST_CMD, + WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT, + WMI_TAG_PDEV_UPDATE_PKT_ROUTING_CMD, + WMI_TAG_PDEV_CHECK_CAL_VERSION_CMD, + WMI_TAG_PDEV_CHECK_CAL_VERSION_EVENT, + WMI_TAG_PDEV_SET_DIVERSITY_GAIN_CMD, + WMI_TAG_MAC_PHY_CHAINMASK_COMBO, + WMI_TAG_MAC_PHY_CHAINMASK_CAPABILITY, + WMI_TAG_VDEV_SET_ARP_STATS_CMD, + WMI_TAG_VDEV_GET_ARP_STATS_CMD, + WMI_TAG_VDEV_GET_ARP_STATS_EVENT, + WMI_TAG_IFACE_OFFLOAD_STATS, + WMI_TAG_REQUEST_STATS_CMD_SUB_STRUCT_PARAM, + WMI_TAG_RSSI_CTL_EXT, + WMI_TAG_SINGLE_PHYERR_EXT_RX_HDR, + WMI_TAG_COEX_BT_ACTIVITY_EVENT, + WMI_TAG_VDEV_GET_TX_POWER_CMD, + WMI_TAG_VDEV_TX_POWER_EVENT, + WMI_TAG_OFFCHAN_DATA_TX_COMPL_EVENT, + WMI_TAG_OFFCHAN_DATA_TX_SEND_CMD, + WMI_TAG_TX_SEND_PARAMS, + WMI_TAG_HE_RATE_SET, + WMI_TAG_CONGESTION_STATS, + WMI_TAG_SET_INIT_COUNTRY_CMD, + WMI_TAG_SCAN_DBS_DUTY_CYCLE, + WMI_TAG_SCAN_DBS_DUTY_CYCLE_PARAM_TLV, + WMI_TAG_PDEV_DIV_GET_RSSI_ANTID, + WMI_TAG_THERM_THROT_CONFIG_REQUEST, + WMI_TAG_THERM_THROT_LEVEL_CONFIG_INFO, + WMI_TAG_THERM_THROT_STATS_EVENT, + WMI_TAG_THERM_THROT_LEVEL_STATS_INFO, + WMI_TAG_PDEV_DIV_RSSI_ANTID_EVENT, + WMI_TAG_OEM_DMA_RING_CAPABILITIES, + WMI_TAG_OEM_DMA_RING_CFG_REQ, + WMI_TAG_OEM_DMA_RING_CFG_RSP, + WMI_TAG_OEM_INDIRECT_DATA, + WMI_TAG_OEM_DMA_BUF_RELEASE, + WMI_TAG_OEM_DMA_BUF_RELEASE_ENTRY, + WMI_TAG_PDEV_BSS_CHAN_INFO_REQUEST, + WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT, + WMI_TAG_ROAM_LCA_DISALLOW_CONFIG, + WMI_TAG_VDEV_LIMIT_OFFCHAN_CMD, + WMI_TAG_ROAM_RSSI_REJECTION_OCE_CONFIG, + WMI_TAG_UNIT_TEST_EVENT, + WMI_TAG_ROAM_FILS_OFFLOAD, + WMI_TAG_PDEV_UPDATE_PMK_CACHE_CMD, + WMI_TAG_PMK_CACHE, + WMI_TAG_PDEV_UPDATE_FILS_HLP_PKT_CMD, + WMI_TAG_ROAM_FILS_SYNCH, + WMI_TAG_GTK_OFFLOAD_EXTENDED, + WMI_TAG_ROAM_BG_SCAN_ROAMING, + WMI_TAG_OIC_PING_OFFLOAD_PARAMS_CMD, + WMI_TAG_OIC_PING_OFFLOAD_SET_ENABLE_CMD, + WMI_TAG_OIC_PING_HANDOFF_EVENT, + WMI_TAG_DHCP_LEASE_RENEW_OFFLOAD_CMD, + WMI_TAG_DHCP_LEASE_RENEW_EVENT, + WMI_TAG_BTM_CONFIG, + WMI_TAG_DEBUG_MESG_FW_DATA_STALL, + WMI_TAG_WLM_CONFIG_CMD, + WMI_TAG_PDEV_UPDATE_CTLTABLE_REQUEST, + WMI_TAG_PDEV_UPDATE_CTLTABLE_EVENT, + WMI_TAG_ROAM_CND_SCORING_PARAM, + WMI_TAG_PDEV_CONFIG_VENDOR_OUI_ACTION, + WMI_TAG_VENDOR_OUI_EXT, + WMI_TAG_ROAM_SYNCH_FRAME_EVENT, + WMI_TAG_FD_SEND_FROM_HOST_CMD, + WMI_TAG_ENABLE_FILS_CMD, + WMI_TAG_HOST_SWFDA_EVENT, + WMI_TAG_BCN_OFFLOAD_CTRL_CMD, + WMI_TAG_PDEV_SET_AC_TX_QUEUE_OPTIMIZED_CMD, + WMI_TAG_STATS_PERIOD, + WMI_TAG_NDL_SCHEDULE_UPDATE, + WMI_TAG_PEER_TID_MSDUQ_QDEPTH_THRESH_UPDATE_CMD, + WMI_TAG_MSDUQ_QDEPTH_THRESH_UPDATE, + WMI_TAG_PDEV_SET_RX_FILTER_PROMISCUOUS_CMD, + WMI_TAG_SAR2_RESULT_EVENT, + WMI_TAG_SAR_CAPABILITIES, + WMI_TAG_SAP_OBSS_DETECTION_CFG_CMD, + WMI_TAG_SAP_OBSS_DETECTION_INFO_EVT, + WMI_TAG_DMA_RING_CAPABILITIES, + WMI_TAG_DMA_RING_CFG_REQ, + WMI_TAG_DMA_RING_CFG_RSP, + WMI_TAG_DMA_BUF_RELEASE, + WMI_TAG_DMA_BUF_RELEASE_ENTRY, + WMI_TAG_SAR_GET_LIMITS_CMD, + WMI_TAG_SAR_GET_LIMITS_EVENT, + WMI_TAG_SAR_GET_LIMITS_EVENT_ROW, + WMI_TAG_OFFLOAD_11K_REPORT, + WMI_TAG_INVOKE_NEIGHBOR_REPORT, + WMI_TAG_NEIGHBOR_REPORT_OFFLOAD, + WMI_TAG_VDEV_SET_CONNECTIVITY_CHECK_STATS, + WMI_TAG_VDEV_GET_CONNECTIVITY_CHECK_STATS, + WMI_TAG_BPF_SET_VDEV_ENABLE_CMD, + WMI_TAG_BPF_SET_VDEV_WORK_MEMORY_CMD, + WMI_TAG_BPF_GET_VDEV_WORK_MEMORY_CMD, + WMI_TAG_BPF_GET_VDEV_WORK_MEMORY_RESP_EVT, + WMI_TAG_PDEV_GET_NFCAL_POWER, + WMI_TAG_BSS_COLOR_CHANGE_ENABLE, + WMI_TAG_OBSS_COLOR_COLLISION_DET_CONFIG, + WMI_TAG_OBSS_COLOR_COLLISION_EVT, + WMI_TAG_RUNTIME_DPD_RECAL_CMD, + WMI_TAG_TWT_ENABLE_CMD, + WMI_TAG_TWT_DISABLE_CMD, + WMI_TAG_TWT_ADD_DIALOG_CMD, + WMI_TAG_TWT_DEL_DIALOG_CMD, + WMI_TAG_TWT_PAUSE_DIALOG_CMD, + WMI_TAG_TWT_RESUME_DIALOG_CMD, + WMI_TAG_TWT_ENABLE_COMPLETE_EVENT, + WMI_TAG_TWT_DISABLE_COMPLETE_EVENT, + WMI_TAG_TWT_ADD_DIALOG_COMPLETE_EVENT, + WMI_TAG_TWT_DEL_DIALOG_COMPLETE_EVENT, + WMI_TAG_TWT_PAUSE_DIALOG_COMPLETE_EVENT, + WMI_TAG_TWT_RESUME_DIALOG_COMPLETE_EVENT, + WMI_TAG_REQUEST_ROAM_SCAN_STATS_CMD, + WMI_TAG_ROAM_SCAN_STATS_EVENT, + WMI_TAG_PEER_TID_CONFIGURATIONS_CMD, + WMI_TAG_VDEV_SET_CUSTOM_SW_RETRY_TH_CMD, + WMI_TAG_GET_TPC_POWER_CMD, + WMI_TAG_GET_TPC_POWER_EVENT, + WMI_TAG_DMA_BUF_RELEASE_SPECTRAL_META_DATA, + WMI_TAG_MOTION_DET_CONFIG_PARAMS_CMD, + WMI_TAG_MOTION_DET_BASE_LINE_CONFIG_PARAMS_CMD, + WMI_TAG_MOTION_DET_START_STOP_CMD, + WMI_TAG_MOTION_DET_BASE_LINE_START_STOP_CMD, + WMI_TAG_MOTION_DET_EVENT, + WMI_TAG_MOTION_DET_BASE_LINE_EVENT, + WMI_TAG_NDP_TRANSPORT_IP, + WMI_TAG_OBSS_SPATIAL_REUSE_SET_CMD, + WMI_TAG_ESP_ESTIMATE_EVENT, + WMI_TAG_NAN_HOST_CONFIG, + WMI_TAG_SPECTRAL_BIN_SCALING_PARAMS, + WMI_TAG_PEER_CFR_CAPTURE_CMD, + WMI_TAG_PEER_CHAN_WIDTH_SWITCH_CMD, + WMI_TAG_CHAN_WIDTH_PEER_LIST, + WMI_TAG_OBSS_SPATIAL_REUSE_SET_DEF_OBSS_THRESH_CMD, + WMI_TAG_PDEV_HE_TB_ACTION_FRM_CMD, + WMI_TAG_PEER_EXTD2_STATS, + WMI_TAG_HPCS_PULSE_START_CMD, + WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT, + WMI_TAG_VDEV_CHAINMASK_CONFIG_CMD, + WMI_TAG_VDEV_BCN_OFFLOAD_QUIET_CONFIG_CMD, + WMI_TAG_NAN_EVENT_INFO, + WMI_TAG_NDP_CHANNEL_INFO, + WMI_TAG_NDP_CMD, + WMI_TAG_NDP_EVENT, + WMI_TAG_PDEV_PEER_PKTLOG_FILTER_CMD = 0x301, + WMI_TAG_PDEV_PEER_PKTLOG_FILTER_INFO, + WMI_TAG_FILS_DISCOVERY_TMPL_CMD = 0x344, + WMI_TAG_PDEV_SRG_BSS_COLOR_BITMAP_CMD = 0x37b, + WMI_TAG_PDEV_SRG_PARTIAL_BSSID_BITMAP_CMD, + WMI_TAG_PDEV_SRG_OBSS_COLOR_ENABLE_BITMAP_CMD = 0x381, + WMI_TAG_PDEV_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD, + WMI_TAG_PDEV_NON_SRG_OBSS_COLOR_ENABLE_BITMAP_CMD, + WMI_TAG_PDEV_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD, + WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9, + WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT, + WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8, + WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD, + WMI_TAG_MAX +}; + +enum wmi_tlv_service { + WMI_TLV_SERVICE_BEACON_OFFLOAD = 0, + WMI_TLV_SERVICE_SCAN_OFFLOAD = 1, + WMI_TLV_SERVICE_ROAM_SCAN_OFFLOAD = 2, + WMI_TLV_SERVICE_BCN_MISS_OFFLOAD = 3, + WMI_TLV_SERVICE_STA_PWRSAVE = 4, + WMI_TLV_SERVICE_STA_ADVANCED_PWRSAVE = 5, + WMI_TLV_SERVICE_AP_UAPSD = 6, + WMI_TLV_SERVICE_AP_DFS = 7, + WMI_TLV_SERVICE_11AC = 8, + WMI_TLV_SERVICE_BLOCKACK = 9, + WMI_TLV_SERVICE_PHYERR = 10, + WMI_TLV_SERVICE_BCN_FILTER = 11, + WMI_TLV_SERVICE_RTT = 12, + WMI_TLV_SERVICE_WOW = 13, + WMI_TLV_SERVICE_RATECTRL_CACHE = 14, + WMI_TLV_SERVICE_IRAM_TIDS = 15, + WMI_TLV_SERVICE_ARPNS_OFFLOAD = 16, + WMI_TLV_SERVICE_NLO = 17, + WMI_TLV_SERVICE_GTK_OFFLOAD = 18, + WMI_TLV_SERVICE_SCAN_SCH = 19, + WMI_TLV_SERVICE_CSA_OFFLOAD = 20, + WMI_TLV_SERVICE_CHATTER = 21, + WMI_TLV_SERVICE_COEX_FREQAVOID = 22, + WMI_TLV_SERVICE_PACKET_POWER_SAVE = 23, + WMI_TLV_SERVICE_FORCE_FW_HANG = 24, + WMI_TLV_SERVICE_GPIO = 25, + WMI_TLV_SERVICE_STA_DTIM_PS_MODULATED_DTIM = 26, + WMI_STA_UAPSD_BASIC_AUTO_TRIG = 27, + WMI_STA_UAPSD_VAR_AUTO_TRIG = 28, + WMI_TLV_SERVICE_STA_KEEP_ALIVE = 29, + WMI_TLV_SERVICE_TX_ENCAP = 30, + WMI_TLV_SERVICE_AP_PS_DETECT_OUT_OF_SYNC = 31, + WMI_TLV_SERVICE_EARLY_RX = 32, + WMI_TLV_SERVICE_STA_SMPS = 33, + WMI_TLV_SERVICE_FWTEST = 34, + WMI_TLV_SERVICE_STA_WMMAC = 35, + WMI_TLV_SERVICE_TDLS = 36, + WMI_TLV_SERVICE_BURST = 37, + WMI_TLV_SERVICE_MCC_BCN_INTERVAL_CHANGE = 38, + WMI_TLV_SERVICE_ADAPTIVE_OCS = 39, + WMI_TLV_SERVICE_BA_SSN_SUPPORT = 40, + WMI_TLV_SERVICE_FILTER_IPSEC_NATKEEPALIVE = 41, + WMI_TLV_SERVICE_WLAN_HB = 42, + WMI_TLV_SERVICE_LTE_ANT_SHARE_SUPPORT = 43, + WMI_TLV_SERVICE_BATCH_SCAN = 44, + WMI_TLV_SERVICE_QPOWER = 45, + WMI_TLV_SERVICE_PLMREQ = 46, + WMI_TLV_SERVICE_THERMAL_MGMT = 47, + WMI_TLV_SERVICE_RMC = 48, + WMI_TLV_SERVICE_MHF_OFFLOAD = 49, + WMI_TLV_SERVICE_COEX_SAR = 50, + WMI_TLV_SERVICE_BCN_TXRATE_OVERRIDE = 51, + WMI_TLV_SERVICE_NAN = 52, + WMI_TLV_SERVICE_L1SS_STAT = 53, + WMI_TLV_SERVICE_ESTIMATE_LINKSPEED = 54, + WMI_TLV_SERVICE_OBSS_SCAN = 55, + WMI_TLV_SERVICE_TDLS_OFFCHAN = 56, + WMI_TLV_SERVICE_TDLS_UAPSD_BUFFER_STA = 57, + WMI_TLV_SERVICE_TDLS_UAPSD_SLEEP_STA = 58, + WMI_TLV_SERVICE_IBSS_PWRSAVE = 59, + WMI_TLV_SERVICE_LPASS = 60, + WMI_TLV_SERVICE_EXTSCAN = 61, + WMI_TLV_SERVICE_D0WOW = 62, + WMI_TLV_SERVICE_HSOFFLOAD = 63, + WMI_TLV_SERVICE_ROAM_HO_OFFLOAD = 64, + WMI_TLV_SERVICE_RX_FULL_REORDER = 65, + WMI_TLV_SERVICE_DHCP_OFFLOAD = 66, + WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT = 67, + WMI_TLV_SERVICE_MDNS_OFFLOAD = 68, + WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD = 69, + WMI_TLV_SERVICE_DUAL_BAND_SIMULTANEOUS_SUPPORT = 70, + WMI_TLV_SERVICE_OCB = 71, + WMI_TLV_SERVICE_AP_ARPNS_OFFLOAD = 72, + WMI_TLV_SERVICE_PER_BAND_CHAINMASK_SUPPORT = 73, + WMI_TLV_SERVICE_PACKET_FILTER_OFFLOAD = 74, + WMI_TLV_SERVICE_MGMT_TX_HTT = 75, + WMI_TLV_SERVICE_MGMT_TX_WMI = 76, + WMI_TLV_SERVICE_EXT_MSG = 77, + WMI_TLV_SERVICE_MAWC = 78, + WMI_TLV_SERVICE_PEER_ASSOC_CONF = 79, + WMI_TLV_SERVICE_EGAP = 80, + WMI_TLV_SERVICE_STA_PMF_OFFLOAD = 81, + WMI_TLV_SERVICE_UNIFIED_WOW_CAPABILITY = 82, + WMI_TLV_SERVICE_ENHANCED_PROXY_STA = 83, + WMI_TLV_SERVICE_ATF = 84, + WMI_TLV_SERVICE_COEX_GPIO = 85, + WMI_TLV_SERVICE_AUX_SPECTRAL_INTF = 86, + WMI_TLV_SERVICE_AUX_CHAN_LOAD_INTF = 87, + WMI_TLV_SERVICE_BSS_CHANNEL_INFO_64 = 88, + WMI_TLV_SERVICE_ENTERPRISE_MESH = 89, + WMI_TLV_SERVICE_RESTRT_CHNL_SUPPORT = 90, + WMI_TLV_SERVICE_BPF_OFFLOAD = 91, + WMI_TLV_SERVICE_SYNC_DELETE_CMDS = 92, + WMI_TLV_SERVICE_SMART_ANTENNA_SW_SUPPORT = 93, + WMI_TLV_SERVICE_SMART_ANTENNA_HW_SUPPORT = 94, + WMI_TLV_SERVICE_RATECTRL_LIMIT_MAX_MIN_RATES = 95, + WMI_TLV_SERVICE_NAN_DATA = 96, + WMI_TLV_SERVICE_NAN_RTT = 97, + WMI_TLV_SERVICE_11AX = 98, + WMI_TLV_SERVICE_DEPRECATED_REPLACE = 99, + WMI_TLV_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE = 100, + WMI_TLV_SERVICE_ENHANCED_MCAST_FILTER = 101, + WMI_TLV_SERVICE_PERIODIC_CHAN_STAT_SUPPORT = 102, + WMI_TLV_SERVICE_MESH_11S = 103, + WMI_TLV_SERVICE_HALF_RATE_QUARTER_RATE_SUPPORT = 104, + WMI_TLV_SERVICE_VDEV_RX_FILTER = 105, + WMI_TLV_SERVICE_P2P_LISTEN_OFFLOAD_SUPPORT = 106, + WMI_TLV_SERVICE_MARK_FIRST_WAKEUP_PACKET = 107, + WMI_TLV_SERVICE_MULTIPLE_MCAST_FILTER_SET = 108, + WMI_TLV_SERVICE_HOST_MANAGED_RX_REORDER = 109, + WMI_TLV_SERVICE_FLASH_RDWR_SUPPORT = 110, + WMI_TLV_SERVICE_WLAN_STATS_REPORT = 111, + WMI_TLV_SERVICE_TX_MSDU_ID_NEW_PARTITION_SUPPORT = 112, + WMI_TLV_SERVICE_DFS_PHYERR_OFFLOAD = 113, + WMI_TLV_SERVICE_RCPI_SUPPORT = 114, + WMI_TLV_SERVICE_FW_MEM_DUMP_SUPPORT = 115, + WMI_TLV_SERVICE_PEER_STATS_INFO = 116, + WMI_TLV_SERVICE_REGULATORY_DB = 117, + WMI_TLV_SERVICE_11D_OFFLOAD = 118, + WMI_TLV_SERVICE_HW_DATA_FILTERING = 119, + WMI_TLV_SERVICE_MULTIPLE_VDEV_RESTART = 120, + WMI_TLV_SERVICE_PKT_ROUTING = 121, + WMI_TLV_SERVICE_CHECK_CAL_VERSION = 122, + WMI_TLV_SERVICE_OFFCHAN_TX_WMI = 123, + WMI_TLV_SERVICE_8SS_TX_BFEE = 124, + WMI_TLV_SERVICE_EXTENDED_NSS_SUPPORT = 125, + WMI_TLV_SERVICE_ACK_TIMEOUT = 126, + WMI_TLV_SERVICE_PDEV_BSS_CHANNEL_INFO_64 = 127, + + /* The first 128 bits */ + WMI_MAX_SERVICE = 128, + + WMI_TLV_SERVICE_CHAN_LOAD_INFO = 128, + WMI_TLV_SERVICE_TX_PPDU_INFO_STATS_SUPPORT = 129, + WMI_TLV_SERVICE_VDEV_LIMIT_OFFCHAN_SUPPORT = 130, + WMI_TLV_SERVICE_FILS_SUPPORT = 131, + WMI_TLV_SERVICE_WLAN_OIC_PING_OFFLOAD = 132, + WMI_TLV_SERVICE_WLAN_DHCP_RENEW = 133, + WMI_TLV_SERVICE_MAWC_SUPPORT = 134, + WMI_TLV_SERVICE_VDEV_LATENCY_CONFIG = 135, + WMI_TLV_SERVICE_PDEV_UPDATE_CTLTABLE_SUPPORT = 136, + WMI_TLV_SERVICE_PKTLOG_SUPPORT_OVER_HTT = 137, + WMI_TLV_SERVICE_VDEV_MULTI_GROUP_KEY_SUPPORT = 138, + WMI_TLV_SERVICE_SCAN_PHYMODE_SUPPORT = 139, + WMI_TLV_SERVICE_THERM_THROT = 140, + WMI_TLV_SERVICE_BCN_OFFLOAD_START_STOP_SUPPORT = 141, + WMI_TLV_SERVICE_WOW_WAKEUP_BY_TIMER_PATTERN = 142, + WMI_TLV_SERVICE_PEER_MAP_UNMAP_V2_SUPPORT = 143, + WMI_TLV_SERVICE_OFFCHAN_DATA_TID_SUPPORT = 144, + WMI_TLV_SERVICE_RX_PROMISC_ENABLE_SUPPORT = 145, + WMI_TLV_SERVICE_SUPPORT_DIRECT_DMA = 146, + WMI_TLV_SERVICE_AP_OBSS_DETECTION_OFFLOAD = 147, + WMI_TLV_SERVICE_11K_NEIGHBOUR_REPORT_SUPPORT = 148, + WMI_TLV_SERVICE_LISTEN_INTERVAL_OFFLOAD_SUPPORT = 149, + WMI_TLV_SERVICE_BSS_COLOR_OFFLOAD = 150, + WMI_TLV_SERVICE_RUNTIME_DPD_RECAL = 151, + WMI_TLV_SERVICE_STA_TWT = 152, + WMI_TLV_SERVICE_AP_TWT = 153, + WMI_TLV_SERVICE_GMAC_OFFLOAD_SUPPORT = 154, + WMI_TLV_SERVICE_SPOOF_MAC_SUPPORT = 155, + WMI_TLV_SERVICE_PEER_TID_CONFIGS_SUPPORT = 156, + WMI_TLV_SERVICE_VDEV_SWRETRY_PER_AC_CONFIG_SUPPORT = 157, + WMI_TLV_SERVICE_DUAL_BEACON_ON_SINGLE_MAC_SCC_SUPPORT = 158, + WMI_TLV_SERVICE_DUAL_BEACON_ON_SINGLE_MAC_MCC_SUPPORT = 159, + WMI_TLV_SERVICE_MOTION_DET = 160, + WMI_TLV_SERVICE_INFRA_MBSSID = 161, + WMI_TLV_SERVICE_OBSS_SPATIAL_REUSE = 162, + WMI_TLV_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT = 163, + WMI_TLV_SERVICE_NAN_DBS_SUPPORT = 164, + WMI_TLV_SERVICE_NDI_DBS_SUPPORT = 165, + WMI_TLV_SERVICE_NAN_SAP_SUPPORT = 166, + WMI_TLV_SERVICE_NDI_SAP_SUPPORT = 167, + WMI_TLV_SERVICE_CFR_CAPTURE_SUPPORT = 168, + WMI_TLV_SERVICE_CFR_CAPTURE_IND_MSG_TYPE_1 = 169, + WMI_TLV_SERVICE_ESP_SUPPORT = 170, + WMI_TLV_SERVICE_PEER_CHWIDTH_CHANGE = 171, + WMI_TLV_SERVICE_WLAN_HPCS_PULSE = 172, + WMI_TLV_SERVICE_PER_VDEV_CHAINMASK_CONFIG_SUPPORT = 173, + WMI_TLV_SERVICE_TX_DATA_MGMT_ACK_RSSI = 174, + WMI_TLV_SERVICE_NAN_DISABLE_SUPPORT = 175, + WMI_TLV_SERVICE_HTT_H2T_NO_HTC_HDR_LEN_IN_MSG_LEN = 176, + WMI_TLV_SERVICE_COEX_SUPPORT_UNEQUAL_ISOLATION = 177, + WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT = 178, + WMI_TLV_SERVICE_SUPPORT_EXTEND_ADDRESS = 179, + WMI_TLV_SERVICE_BEACON_RECEPTION_STATS = 180, + WMI_TLV_SERVICE_FETCH_TX_PN = 181, + WMI_TLV_SERVICE_PEER_UNMAP_RESPONSE_SUPPORT = 182, + WMI_TLV_SERVICE_TX_PER_PEER_AMPDU_SIZE = 183, + WMI_TLV_SERVICE_BSS_COLOR_SWITCH_COUNT = 184, + WMI_TLV_SERVICE_HTT_PEER_STATS_SUPPORT = 185, + WMI_TLV_SERVICE_UL_RU26_ALLOWED = 186, + WMI_TLV_SERVICE_GET_MWS_COEX_STATE = 187, + WMI_TLV_SERVICE_GET_MWS_DPWB_STATE = 188, + WMI_TLV_SERVICE_GET_MWS_TDM_STATE = 189, + WMI_TLV_SERVICE_GET_MWS_IDRX_STATE = 190, + WMI_TLV_SERVICE_GET_MWS_ANTENNA_SHARING_STATE = 191, + WMI_TLV_SERVICE_ENHANCED_TPC_CONFIG_EVENT = 192, + WMI_TLV_SERVICE_WLM_STATS_REQUEST = 193, + WMI_TLV_SERVICE_EXT_PEER_TID_CONFIGS_SUPPORT = 194, + WMI_TLV_SERVICE_WPA3_FT_SAE_SUPPORT = 195, + WMI_TLV_SERVICE_WPA3_FT_SUITE_B_SUPPORT = 196, + WMI_TLV_SERVICE_VOW_ENABLE = 197, + WMI_TLV_SERVICE_CFR_CAPTURE_IND_EVT_TYPE_1 = 198, + WMI_TLV_SERVICE_BROADCAST_TWT = 199, + WMI_TLV_SERVICE_RAP_DETECTION_SUPPORT = 200, + WMI_TLV_SERVICE_PS_TDCC = 201, + WMI_TLV_SERVICE_THREE_WAY_COEX_CONFIG_LEGACY = 202, + WMI_TLV_SERVICE_THREE_WAY_COEX_CONFIG_OVERRIDE = 203, + WMI_TLV_SERVICE_TX_PWR_PER_PEER = 204, + WMI_TLV_SERVICE_STA_PLUS_STA_SUPPORT = 205, + WMI_TLV_SERVICE_WPA3_FT_FILS = 206, + WMI_TLV_SERVICE_ADAPTIVE_11R_ROAM = 207, + WMI_TLV_SERVICE_CHAN_RF_CHARACTERIZATION_INFO = 208, + WMI_TLV_SERVICE_FW_IFACE_COMBINATION_SUPPORT = 209, + WMI_TLV_SERVICE_TX_COMPL_TSF64 = 210, + WMI_TLV_SERVICE_DSM_ROAM_FILTER = 211, + WMI_TLV_SERVICE_PACKET_CAPTURE_SUPPORT = 212, + WMI_TLV_SERVICE_PER_PEER_HTT_STATS_RESET = 213, + WMI_TLV_SERVICE_FREQINFO_IN_METADATA = 219, + WMI_TLV_SERVICE_EXT2_MSG = 220, + WMI_TLV_SERVICE_PEER_POWER_SAVE_DURATION_SUPPORT = 246, + WMI_TLV_SERVICE_SRG_SRP_SPATIAL_REUSE_SUPPORT = 249, + WMI_TLV_SERVICE_MBSS_PARAM_IN_VDEV_START_SUPPORT = 253, + WMI_TLV_SERVICE_PASSIVE_SCAN_START_TIME_ENHANCE = 263, + + /* The second 128 bits */ + WMI_MAX_EXT_SERVICE = 256, + WMI_TLV_SERVICE_SCAN_CONFIG_PER_CHANNEL = 265, + WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281, + WMI_TLV_SERVICE_BIOS_SAR_SUPPORT = 326, + WMI_TLV_SERVICE_SUPPORT_11D_FOR_HOST_SCAN = 357, + + /* The third 128 bits */ + WMI_MAX_EXT2_SERVICE = 384 +}; + +enum { + WMI_SMPS_FORCED_MODE_NONE = 0, + WMI_SMPS_FORCED_MODE_DISABLED, + WMI_SMPS_FORCED_MODE_STATIC, + WMI_SMPS_FORCED_MODE_DYNAMIC +}; + +#define WMI_TPC_CHAINMASK_CONFIG_BAND_2G 0 +#define WMI_TPC_CHAINMASK_CONFIG_BAND_5G 1 +#define WMI_NUM_SUPPORTED_BAND_MAX 2 + +#define WMI_PEER_MIMO_PS_STATE 0x1 +#define WMI_PEER_AMPDU 0x2 +#define WMI_PEER_AUTHORIZE 0x3 +#define WMI_PEER_CHWIDTH 0x4 +#define WMI_PEER_NSS 0x5 +#define WMI_PEER_USE_4ADDR 0x6 +#define WMI_PEER_MEMBERSHIP 0x7 +#define WMI_PEER_USERPOS 0x8 +#define WMI_PEER_CRIT_PROTO_HINT_ENABLED 0x9 +#define WMI_PEER_TX_FAIL_CNT_THR 0xA +#define WMI_PEER_SET_HW_RETRY_CTS2S 0xB +#define WMI_PEER_IBSS_ATIM_WINDOW_LENGTH 0xC +#define WMI_PEER_PHYMODE 0xD +#define WMI_PEER_USE_FIXED_PWR 0xE +#define WMI_PEER_PARAM_FIXED_RATE 0xF +#define WMI_PEER_SET_MU_WHITELIST 0x10 +#define WMI_PEER_SET_MAX_TX_RATE 0x11 +#define WMI_PEER_SET_MIN_TX_RATE 0x12 +#define WMI_PEER_SET_DEFAULT_ROUTING 0x13 + +/* slot time long */ +#define WMI_VDEV_SLOT_TIME_LONG 0x1 +/* slot time short */ +#define WMI_VDEV_SLOT_TIME_SHORT 0x2 +/* preablbe long */ +#define WMI_VDEV_PREAMBLE_LONG 0x1 +/* preablbe short */ +#define WMI_VDEV_PREAMBLE_SHORT 0x2 + +enum wmi_peer_smps_state { + WMI_PEER_SMPS_PS_NONE = 0x0, + WMI_PEER_SMPS_STATIC = 0x1, + WMI_PEER_SMPS_DYNAMIC = 0x2 +}; + +enum wmi_peer_chwidth { + WMI_PEER_CHWIDTH_20MHZ = 0, + WMI_PEER_CHWIDTH_40MHZ = 1, + WMI_PEER_CHWIDTH_80MHZ = 2, + WMI_PEER_CHWIDTH_160MHZ = 3, +}; + +enum wmi_beacon_gen_mode { + WMI_BEACON_STAGGERED_MODE = 0, + WMI_BEACON_BURST_MODE = 1 +}; + +enum wmi_direct_buffer_module { + WMI_DIRECT_BUF_SPECTRAL = 0, + WMI_DIRECT_BUF_CFR = 1, + + /* keep it last */ + WMI_DIRECT_BUF_MAX +}; + +/* enum wmi_nss_ratio - NSS ratio received from FW during service ready ext + * event + * WMI_NSS_RATIO_1BY2_NSS -Max nss of 160MHz is equals to half of the max nss + * of 80MHz + * WMI_NSS_RATIO_3BY4_NSS - Max nss of 160MHz is equals to 3/4 of the max nss + * of 80MHz + * WMI_NSS_RATIO_1_NSS - Max nss of 160MHz is equals to the max nss of 80MHz + * WMI_NSS_RATIO_2_NSS - Max nss of 160MHz is equals to two times the max + * nss of 80MHz + */ + +enum wmi_nss_ratio { + WMI_NSS_RATIO_1BY2_NSS = 0x0, + WMI_NSS_RATIO_3BY4_NSS = 0x1, + WMI_NSS_RATIO_1_NSS = 0x2, + WMI_NSS_RATIO_2_NSS = 0x3, +}; + +enum wmi_dtim_policy { + WMI_DTIM_POLICY_IGNORE = 1, + WMI_DTIM_POLICY_NORMAL = 2, + WMI_DTIM_POLICY_STICK = 3, + WMI_DTIM_POLICY_AUTO = 4, +}; + +struct wmi_host_pdev_band_to_mac { + uint32_t pdev_id; + uint32_t start_freq; + uint32_t end_freq; +}; + +struct ath12k_ppe_threshold { + uint32_t numss_m1; + uint32_t ru_bit_mask; + uint32_t ppet16_ppet8_ru3_ru0[PSOC_HOST_MAX_NUM_SS]; +}; + +struct ath12k_service_ext_param { + uint32_t default_conc_scan_config_bits; + uint32_t default_fw_config_bits; + struct ath12k_ppe_threshold ppet; + uint32_t he_cap_info; + uint32_t mpdu_density; + uint32_t max_bssid_rx_filters; + uint32_t num_hw_modes; + uint32_t num_phy; +}; + +struct ath12k_hw_mode_caps { + uint32_t hw_mode_id; + uint32_t phy_id_map; + uint32_t hw_mode_config_type; +}; + +#define PSOC_HOST_MAX_PHY_SIZE (3) +#define ATH12K_11B_SUPPORT BIT(0) +#define ATH12K_11G_SUPPORT BIT(1) +#define ATH12K_11A_SUPPORT BIT(2) +#define ATH12K_11N_SUPPORT BIT(3) +#define ATH12K_11AC_SUPPORT BIT(4) +#define ATH12K_11AX_SUPPORT BIT(5) + +struct ath12k_hal_reg_capabilities_ext { + uint32_t phy_id; + uint32_t eeprom_reg_domain; + uint32_t eeprom_reg_domain_ext; + uint32_t regcap1; + uint32_t regcap2; + uint32_t wireless_modes; + uint32_t low_2ghz_chan; + uint32_t high_2ghz_chan; + uint32_t low_5ghz_chan; + uint32_t high_5ghz_chan; +}; + +#define WMI_HOST_MAX_PDEV 3 + +struct wlan_host_mem_chunk { + uint32_t tlv_header; + uint32_t req_id; + uint32_t ptr; + uint32_t size; +} __packed; + +struct wmi_host_mem_chunk { + void *vaddr; + bus_addr_t paddr; + uint32_t len; + uint32_t req_id; +}; + +struct wmi_init_cmd_param { + uint32_t tlv_header; + struct target_resource_config *res_cfg; + uint8_t num_mem_chunks; + struct wmi_host_mem_chunk *mem_chunks; + uint32_t hw_mode_id; + uint32_t num_band_to_mac; + struct wmi_host_pdev_band_to_mac band_to_mac[WMI_HOST_MAX_PDEV]; +}; + +struct wmi_pdev_band_to_mac { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t start_freq; + uint32_t end_freq; +} __packed; + +struct wmi_pdev_set_hw_mode_cmd_param { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t hw_mode_index; + uint32_t num_band_to_mac; +} __packed; + +struct wmi_ppe_threshold { + uint32_t numss_m1; /** NSS - 1*/ + union { + uint32_t ru_count; + uint32_t ru_mask; + } __packed; + uint32_t ppet16_ppet8_ru3_ru0[WMI_MAX_NUM_SS]; +} __packed; + +#define HW_BD_INFO_SIZE 5 + +struct wmi_abi_version { + uint32_t abi_version_0; + uint32_t abi_version_1; + uint32_t abi_version_ns_0; + uint32_t abi_version_ns_1; + uint32_t abi_version_ns_2; + uint32_t abi_version_ns_3; +} __packed; + +struct wmi_init_cmd { + uint32_t tlv_header; + struct wmi_abi_version host_abi_vers; + uint32_t num_host_mem_chunks; +} __packed; + +#define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) +#define WMI_RSRC_CFG_FLAG2_CALC_NEXT_DTIM_COUNT_SET BIT(9) +#define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18) + +#define WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT 4 + +struct wmi_resource_config { + uint32_t tlv_header; + uint32_t num_vdevs; + uint32_t num_peers; + uint32_t num_offload_peers; + uint32_t num_offload_reorder_buffs; + uint32_t num_peer_keys; + uint32_t num_tids; + uint32_t ast_skid_limit; + uint32_t tx_chain_mask; + uint32_t rx_chain_mask; + uint32_t rx_timeout_pri[4]; + uint32_t rx_decap_mode; + uint32_t scan_max_pending_req; + uint32_t bmiss_offload_max_vdev; + uint32_t roam_offload_max_vdev; + uint32_t roam_offload_max_ap_profiles; + uint32_t num_mcast_groups; + uint32_t num_mcast_table_elems; + uint32_t mcast2ucast_mode; + uint32_t tx_dbg_log_size; + uint32_t num_wds_entries; + uint32_t dma_burst_size; + uint32_t mac_aggr_delim; + uint32_t rx_skip_defrag_timeout_dup_detection_check; + uint32_t vow_config; + uint32_t gtk_offload_max_vdev; + uint32_t num_msdu_desc; + uint32_t max_frag_entries; + uint32_t num_tdls_vdevs; + uint32_t num_tdls_conn_table_entries; + uint32_t beacon_tx_offload_max_vdev; + uint32_t num_multicast_filter_entries; + uint32_t num_wow_filters; + uint32_t num_keep_alive_pattern; + uint32_t keep_alive_pattern_size; + uint32_t max_tdls_concurrent_sleep_sta; + uint32_t max_tdls_concurrent_buffer_sta; + uint32_t wmi_send_separate; + uint32_t num_ocb_vdevs; + uint32_t num_ocb_channels; + uint32_t num_ocb_schedules; + uint32_t flag1; + uint32_t smart_ant_cap; + uint32_t bk_minfree; + uint32_t be_minfree; + uint32_t vi_minfree; + uint32_t vo_minfree; + uint32_t alloc_frag_desc_for_data_pkt; + uint32_t num_ns_ext_tuples_cfg; + uint32_t bpf_instruction_size; + uint32_t max_bssid_rx_filters; + uint32_t use_pdev_id; + uint32_t max_num_dbs_scan_duty_cycle; + uint32_t max_num_group_keys; + uint32_t peer_map_unmap_v2_support; + uint32_t sched_params; + uint32_t twt_ap_pdev_count; + uint32_t twt_ap_sta_count; +#ifdef notyet /* 6 GHz support */ + uint32_t max_nlo_ssids; + uint32_t num_pkt_filters; + uint32_t num_max_sta_vdevs; + uint32_t max_bssid_indicator; + uint32_t ul_resp_config; + uint32_t msdu_flow_override_config0; + uint32_t msdu_flow_override_config1; + uint32_t flags2; + uint32_t host_service_flags; + uint32_t max_rnr_neighbours; + uint32_t ema_max_vap_cnt; + uint32_t ema_max_profile_period; +#endif +} __packed; + +struct wmi_service_ready_event { + uint32_t fw_build_vers; + struct wmi_abi_version fw_abi_vers; + uint32_t phy_capability; + uint32_t max_frag_entry; + uint32_t num_rf_chains; + uint32_t ht_cap_info; + uint32_t vht_cap_info; + uint32_t vht_supp_mcs; + uint32_t hw_min_tx_power; + uint32_t hw_max_tx_power; + uint32_t sys_cap_info; + uint32_t min_pkt_size_enable; + uint32_t max_bcn_ie_size; + uint32_t num_mem_reqs; + uint32_t max_num_scan_channels; + uint32_t hw_bd_id; + uint32_t hw_bd_info[HW_BD_INFO_SIZE]; + uint32_t max_supported_macs; + uint32_t wmi_fw_sub_feat_caps; + uint32_t num_dbs_hw_modes; + /* txrx_chainmask + * [7:0] - 2G band tx chain mask + * [15:8] - 2G band rx chain mask + * [23:16] - 5G band tx chain mask + * [31:24] - 5G band rx chain mask + */ + uint32_t txrx_chainmask; + uint32_t default_dbs_hw_mode_index; + uint32_t num_msdu_desc; +} __packed; + +#define WMI_SERVICE_BM_SIZE ((WMI_MAX_SERVICE + sizeof(uint32_t) - 1) / sizeof(uint32_t)) + +#define WMI_SERVICE_SEGMENT_BM_SIZE32 4 /* 4x uint32_t = 128 bits */ +#define WMI_SERVICE_EXT_BM_SIZE (WMI_SERVICE_SEGMENT_BM_SIZE32 * sizeof(uint32_t)) +#define WMI_AVAIL_SERVICE_BITS_IN_SIZE32 32 +#define WMI_SERVICE_BITS_IN_SIZE32 4 + +struct wmi_service_ready_ext_event { + uint32_t default_conc_scan_config_bits; + uint32_t default_fw_config_bits; + struct wmi_ppe_threshold ppet; + uint32_t he_cap_info; + uint32_t mpdu_density; + uint32_t max_bssid_rx_filters; + uint32_t fw_build_vers_ext; + uint32_t max_nlo_ssids; + uint32_t max_bssid_indicator; + uint32_t he_cap_info_ext; +} __packed; + +struct wmi_soc_mac_phy_hw_mode_caps { + uint32_t num_hw_modes; + uint32_t num_chainmask_tables; +} __packed; + +struct wmi_hw_mode_capabilities { + uint32_t tlv_header; + uint32_t hw_mode_id; + uint32_t phy_id_map; + uint32_t hw_mode_config_type; +} __packed; + +#define WMI_MAX_HECAP_PHY_SIZE (3) +#define WMI_NSS_RATIO_ENABLE_DISABLE_BITPOS BIT(0) +#define WMI_NSS_RATIO_ENABLE_DISABLE_GET(_val) \ + FIELD_GET(WMI_NSS_RATIO_ENABLE_DISABLE_BITPOS, _val) +#define WMI_NSS_RATIO_INFO_BITPOS GENMASK(4, 1) +#define WMI_NSS_RATIO_INFO_GET(_val) \ + FIELD_GET(WMI_NSS_RATIO_INFO_BITPOS, _val) + +struct wmi_mac_phy_capabilities { + uint32_t hw_mode_id; + uint32_t pdev_id; + uint32_t phy_id; + uint32_t supported_flags; + uint32_t supported_bands; + uint32_t ampdu_density; + uint32_t max_bw_supported_2g; + uint32_t ht_cap_info_2g; + uint32_t vht_cap_info_2g; + uint32_t vht_supp_mcs_2g; + uint32_t he_cap_info_2g; + uint32_t he_supp_mcs_2g; + uint32_t tx_chain_mask_2g; + uint32_t rx_chain_mask_2g; + uint32_t max_bw_supported_5g; + uint32_t ht_cap_info_5g; + uint32_t vht_cap_info_5g; + uint32_t vht_supp_mcs_5g; + uint32_t he_cap_info_5g; + uint32_t he_supp_mcs_5g; + uint32_t tx_chain_mask_5g; + uint32_t rx_chain_mask_5g; + uint32_t he_cap_phy_info_2g[WMI_MAX_HECAP_PHY_SIZE]; + uint32_t he_cap_phy_info_5g[WMI_MAX_HECAP_PHY_SIZE]; + struct wmi_ppe_threshold he_ppet2g; + struct wmi_ppe_threshold he_ppet5g; + uint32_t chainmask_table_id; + uint32_t lmac_id; + uint32_t he_cap_info_2g_ext; + uint32_t he_cap_info_5g_ext; + uint32_t he_cap_info_internal; + uint32_t wireless_modes; + uint32_t low_2ghz_chan_freq; + uint32_t high_2ghz_chan_freq; + uint32_t low_5ghz_chan_freq; + uint32_t high_5ghz_chan_freq; + uint32_t nss_ratio; +} __packed; + +struct wmi_hal_reg_capabilities_ext { + uint32_t tlv_header; + uint32_t phy_id; + uint32_t eeprom_reg_domain; + uint32_t eeprom_reg_domain_ext; + uint32_t regcap1; + uint32_t regcap2; + uint32_t wireless_modes; + uint32_t low_2ghz_chan; + uint32_t high_2ghz_chan; + uint32_t low_5ghz_chan; + uint32_t high_5ghz_chan; +} __packed; + +struct wmi_soc_hal_reg_capabilities { + uint32_t num_phy; +} __packed; + +/* 2 word representation of MAC addr */ +struct wmi_mac_addr { + union { + uint8_t addr[6]; + struct { + uint32_t word0; + uint32_t word1; + } __packed; + } __packed; +} __packed; + +struct wmi_dma_ring_capabilities { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t module_id; + uint32_t min_elem; + uint32_t min_buf_sz; + uint32_t min_buf_align; +} __packed; + +struct wmi_ready_event_min { + struct wmi_abi_version fw_abi_vers; + struct wmi_mac_addr mac_addr; + uint32_t status; + uint32_t num_dscp_table; + uint32_t num_extra_mac_addr; + uint32_t num_total_peers; + uint32_t num_extra_peers; +} __packed; + +struct wmi_ready_event { + struct wmi_ready_event_min ready_event_min; + uint32_t max_ast_index; + uint32_t pktlog_defs_checksum; +} __packed; + +struct wmi_service_available_event { + uint32_t wmi_service_segment_offset; + uint32_t wmi_service_segment_bitmap[WMI_SERVICE_SEGMENT_BM_SIZE32]; +} __packed; + +struct vdev_create_params { + uint8_t if_id; + uint32_t type; + uint32_t subtype; + struct { + uint8_t tx; + uint8_t rx; + } chains[2]; + uint32_t pdev_id; + uint32_t mbssid_flags; + uint32_t mbssid_tx_vdev_id; +}; + +struct wmi_vdev_create_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t vdev_type; + uint32_t vdev_subtype; + struct wmi_mac_addr vdev_macaddr; + uint32_t num_cfg_txrx_streams; + uint32_t pdev_id; + uint32_t mbssid_flags; + uint32_t mbssid_tx_vdev_id; +} __packed; + +struct wmi_vdev_txrx_streams { + uint32_t tlv_header; + uint32_t band; + uint32_t supported_tx_streams; + uint32_t supported_rx_streams; +} __packed; + +struct wmi_vdev_delete_cmd { + uint32_t tlv_header; + uint32_t vdev_id; +} __packed; + +struct wmi_vdev_up_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t vdev_assoc_id; + struct wmi_mac_addr vdev_bssid; + struct wmi_mac_addr tx_vdev_bssid; + uint32_t nontx_profile_idx; + uint32_t nontx_profile_cnt; +} __packed; + +struct wmi_vdev_stop_cmd { + uint32_t tlv_header; + uint32_t vdev_id; +} __packed; + +struct wmi_vdev_down_cmd { + uint32_t tlv_header; + uint32_t vdev_id; +} __packed; + +#define WMI_VDEV_START_HIDDEN_SSID BIT(0) +#define WMI_VDEV_START_PMF_ENABLED BIT(1) +#define WMI_VDEV_START_LDPC_RX_ENABLED BIT(3) +#define WMI_VDEV_START_HW_ENCRYPTION_DISABLED BIT(4) + +struct wmi_ssid { + uint32_t ssid_len; + uint32_t ssid[8]; +} __packed; + +#define ATH12K_VDEV_SETUP_TIMEOUT_HZ (1 * HZ) + +struct wmi_vdev_start_request_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t requestor_id; + uint32_t beacon_interval; + uint32_t dtim_period; + uint32_t flags; + struct wmi_ssid ssid; + uint32_t bcn_tx_rate; + uint32_t bcn_txpower; + uint32_t num_noa_descriptors; + uint32_t disable_hw_ack; + uint32_t preferred_tx_streams; + uint32_t preferred_rx_streams; + uint32_t he_ops; + uint32_t cac_duration_ms; + uint32_t regdomain; + uint32_t min_data_rate; + uint32_t mbssid_flags; + uint32_t mbssid_tx_vdev_id; +} __packed; + +#define MGMT_TX_DL_FRM_LEN 64 +#define WMI_MAC_MAX_SSID_LENGTH 32 +struct mac_ssid { + uint8_t length; + uint8_t mac_ssid[WMI_MAC_MAX_SSID_LENGTH]; +} __packed; + +struct wmi_p2p_noa_descriptor { + uint32_t type_count; + uint32_t duration; + uint32_t interval; + uint32_t start_time; +}; + +struct channel_param { + uint8_t chan_id; + uint8_t pwr; + uint32_t mhz; + uint32_t half_rate:1, + quarter_rate:1, + dfs_set:1, + dfs_set_cfreq2:1, + is_chan_passive:1, + allow_ht:1, + allow_vht:1, + allow_he:1, + set_agile:1, + psc_channel:1; + uint32_t phy_mode; + uint32_t cfreq1; + uint32_t cfreq2; + char maxpower; + char minpower; + char maxregpower; + uint8_t antennamax; + uint8_t reg_class_id; +} __packed; + +enum wmi_phy_mode { + MODE_11A = 0, + MODE_11G = 1, /* 11b/g Mode */ + MODE_11B = 2, /* 11b Mode */ + MODE_11GONLY = 3, /* 11g only Mode */ + MODE_11NA_HT20 = 4, + MODE_11NG_HT20 = 5, + MODE_11NA_HT40 = 6, + MODE_11NG_HT40 = 7, + MODE_11AC_VHT20 = 8, + MODE_11AC_VHT40 = 9, + MODE_11AC_VHT80 = 10, + MODE_11AC_VHT20_2G = 11, + MODE_11AC_VHT40_2G = 12, + MODE_11AC_VHT80_2G = 13, + MODE_11AC_VHT80_80 = 14, + MODE_11AC_VHT160 = 15, + MODE_11AX_HE20 = 16, + MODE_11AX_HE40 = 17, + MODE_11AX_HE80 = 18, + MODE_11AX_HE80_80 = 19, + MODE_11AX_HE160 = 20, + MODE_11AX_HE20_2G = 21, + MODE_11AX_HE40_2G = 22, + MODE_11AX_HE80_2G = 23, + MODE_UNKNOWN = 24, + MODE_MAX = 24 +}; + +static inline const char *qwz_wmi_phymode_str(enum wmi_phy_mode mode) +{ + switch (mode) { + case MODE_11A: + return "11a"; + case MODE_11G: + return "11g"; + case MODE_11B: + return "11b"; + case MODE_11GONLY: + return "11gonly"; + case MODE_11NA_HT20: + return "11na-ht20"; + case MODE_11NG_HT20: + return "11ng-ht20"; + case MODE_11NA_HT40: + return "11na-ht40"; + case MODE_11NG_HT40: + return "11ng-ht40"; + case MODE_11AC_VHT20: + return "11ac-vht20"; + case MODE_11AC_VHT40: + return "11ac-vht40"; + case MODE_11AC_VHT80: + return "11ac-vht80"; + case MODE_11AC_VHT160: + return "11ac-vht160"; + case MODE_11AC_VHT80_80: + return "11ac-vht80+80"; + case MODE_11AC_VHT20_2G: + return "11ac-vht20-2g"; + case MODE_11AC_VHT40_2G: + return "11ac-vht40-2g"; + case MODE_11AC_VHT80_2G: + return "11ac-vht80-2g"; + case MODE_11AX_HE20: + return "11ax-he20"; + case MODE_11AX_HE40: + return "11ax-he40"; + case MODE_11AX_HE80: + return "11ax-he80"; + case MODE_11AX_HE80_80: + return "11ax-he80+80"; + case MODE_11AX_HE160: + return "11ax-he160"; + case MODE_11AX_HE20_2G: + return "11ax-he20-2g"; + case MODE_11AX_HE40_2G: + return "11ax-he40-2g"; + case MODE_11AX_HE80_2G: + return "11ax-he80-2g"; + case MODE_UNKNOWN: + /* skip */ + break; + + /* no default handler to allow compiler to check that the + * enum is fully handled + */ + } + + return ""; +} + +struct wmi_channel_arg { + uint32_t freq; + uint32_t band_center_freq1; + uint32_t band_center_freq2; + bool passive; + bool allow_ibss; + bool allow_ht; + bool allow_vht; + bool ht40plus; + bool chan_radar; + bool freq2_radar; + bool allow_he; + uint32_t min_power; + uint32_t max_power; + uint32_t max_reg_power; + uint32_t max_antenna_gain; + enum wmi_phy_mode mode; +}; + +struct wmi_vdev_start_req_arg { + uint32_t vdev_id; + struct wmi_channel_arg channel; + uint32_t bcn_intval; + uint32_t dtim_period; + uint8_t *ssid; + uint32_t ssid_len; + uint32_t bcn_tx_rate; + uint32_t bcn_tx_power; + bool disable_hw_ack; + bool hidden_ssid; + bool pmf_enabled; + uint32_t he_ops; + uint32_t cac_duration_ms; + uint32_t regdomain; + uint32_t pref_rx_streams; + uint32_t pref_tx_streams; + uint32_t num_noa_descriptors; + uint32_t min_data_rate; + uint32_t mbssid_flags; + uint32_t mbssid_tx_vdev_id; +}; + +struct peer_create_params { + uint8_t *peer_addr; + uint32_t peer_type; + uint32_t vdev_id; +}; + +struct peer_delete_params { + uint8_t vdev_id; +}; + +struct peer_flush_params { + uint32_t peer_tid_bitmap; + uint8_t vdev_id; +}; + +struct pdev_set_regdomain_params { + uint16_t current_rd_in_use; + uint16_t current_rd_2g; + uint16_t current_rd_5g; + uint32_t ctl_2g; + uint32_t ctl_5g; + uint8_t dfs_domain; + uint32_t pdev_id; +}; + +struct rx_reorder_queue_remove_params { + uint8_t *peer_macaddr; + uint16_t vdev_id; + uint32_t peer_tid_bitmap; +}; + +#define WMI_HOST_PDEV_ID_SOC 0xFF +#define WMI_HOST_PDEV_ID_0 0 +#define WMI_HOST_PDEV_ID_1 1 +#define WMI_HOST_PDEV_ID_2 2 + +#define WMI_PDEV_ID_SOC 0 +#define WMI_PDEV_ID_1ST 1 +#define WMI_PDEV_ID_2ND 2 +#define WMI_PDEV_ID_3RD 3 + +/* Freq units in MHz */ +#define REG_RULE_START_FREQ 0x0000ffff +#define REG_RULE_END_FREQ 0xffff0000 +#define REG_RULE_FLAGS 0x0000ffff +#define REG_RULE_MAX_BW 0x0000ffff +#define REG_RULE_REG_PWR 0x00ff0000 +#define REG_RULE_ANT_GAIN 0xff000000 +#define REG_RULE_PSD_INFO BIT(0) +#define REG_RULE_PSD_EIRP 0xff0000 + +#define WMI_VDEV_PARAM_TXBF_SU_TX_BFEE BIT(0) +#define WMI_VDEV_PARAM_TXBF_MU_TX_BFEE BIT(1) +#define WMI_VDEV_PARAM_TXBF_SU_TX_BFER BIT(2) +#define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3) + +#define HE_PHYCAP_BYTE_0 0 +#define HE_PHYCAP_BYTE_1 1 +#define HE_PHYCAP_BYTE_2 2 +#define HE_PHYCAP_BYTE_3 3 +#define HE_PHYCAP_BYTE_4 4 + +#define HECAP_PHY_SU_BFER BIT(7) +#define HECAP_PHY_SU_BFEE BIT(0) +#define HECAP_PHY_MU_BFER BIT(1) +#define HECAP_PHY_UL_MUMIMO BIT(6) +#define HECAP_PHY_UL_MUOFDMA BIT(7) + +#define HECAP_PHY_SUBFMR_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_SU_BFER, hecap_phy[HE_PHYCAP_BYTE_3]) + +#define HECAP_PHY_SUBFME_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_SU_BFEE, hecap_phy[HE_PHYCAP_BYTE_4]) + +#define HECAP_PHY_MUBFMR_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_MU_BFER, hecap_phy[HE_PHYCAP_BYTE_4]) + +#define HECAP_PHY_ULMUMIMO_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_UL_MUMIMO, hecap_phy[HE_PHYCAP_BYTE_2]) + +#define HECAP_PHY_ULOFDMA_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_UL_MUOFDMA, hecap_phy[HE_PHYCAP_BYTE_2]) + +#define HE_MODE_SU_TX_BFEE BIT(0) +#define HE_MODE_SU_TX_BFER BIT(1) +#define HE_MODE_MU_TX_BFEE BIT(2) +#define HE_MODE_MU_TX_BFER BIT(3) +#define HE_MODE_DL_OFDMA BIT(4) +#define HE_MODE_UL_OFDMA BIT(5) +#define HE_MODE_UL_MUMIMO BIT(6) + +#define HE_DL_MUOFDMA_ENABLE 1 +#define HE_UL_MUOFDMA_ENABLE 1 +#define HE_DL_MUMIMO_ENABLE 1 +#define HE_UL_MUMIMO_ENABLE 1 +#define HE_MU_BFEE_ENABLE 1 +#define HE_SU_BFEE_ENABLE 1 +#define HE_MU_BFER_ENABLE 1 +#define HE_SU_BFER_ENABLE 1 + +#define HE_VHT_SOUNDING_MODE_ENABLE 1 +#define HE_SU_MU_SOUNDING_MODE_ENABLE 1 +#define HE_TRIG_NONTRIG_SOUNDING_MODE_ENABLE 1 + +/* HE or VHT Sounding */ +#define HE_VHT_SOUNDING_MODE BIT(0) +/* SU or MU Sounding */ +#define HE_SU_MU_SOUNDING_MODE BIT(2) +/* Trig or Non-Trig Sounding */ +#define HE_TRIG_NONTRIG_SOUNDING_MODE BIT(3) + +#define WMI_TXBF_STS_CAP_OFFSET_LSB 4 +#define WMI_TXBF_STS_CAP_OFFSET_MASK 0x70 +#define WMI_BF_SOUND_DIM_OFFSET_LSB 8 +#define WMI_BF_SOUND_DIM_OFFSET_MASK 0x700 + +struct pdev_params { + uint32_t param_id; + uint32_t param_value; +}; + +enum wmi_peer_type { + WMI_PEER_TYPE_DEFAULT = 0, + WMI_PEER_TYPE_BSS = 1, + WMI_PEER_TYPE_TDLS = 2, +}; + +struct wmi_peer_create_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t peer_type; +} __packed; + +struct wmi_peer_delete_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_peer_reorder_queue_setup_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t tid; + uint32_t queue_ptr_lo; + uint32_t queue_ptr_hi; + uint32_t queue_no; + uint32_t ba_window_size_valid; + uint32_t ba_window_size; +} __packed; + +struct wmi_peer_reorder_queue_remove_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t tid_mask; +} __packed; + +struct gpio_config_params { + uint32_t gpio_num; + uint32_t input; + uint32_t pull_type; + uint32_t intr_mode; +}; + +enum wmi_gpio_type { + WMI_GPIO_PULL_NONE, + WMI_GPIO_PULL_UP, + WMI_GPIO_PULL_DOWN +}; + +enum wmi_gpio_intr_type { + WMI_GPIO_INTTYPE_DISABLE, + WMI_GPIO_INTTYPE_RISING_EDGE, + WMI_GPIO_INTTYPE_FALLING_EDGE, + WMI_GPIO_INTTYPE_BOTH_EDGE, + WMI_GPIO_INTTYPE_LEVEL_LOW, + WMI_GPIO_INTTYPE_LEVEL_HIGH +}; + +enum wmi_bss_chan_info_req_type { + WMI_BSS_SURVEY_REQ_TYPE_READ = 1, + WMI_BSS_SURVEY_REQ_TYPE_READ_CLEAR, +}; + +struct wmi_gpio_config_cmd_param { + uint32_t tlv_header; + uint32_t gpio_num; + uint32_t input; + uint32_t pull_type; + uint32_t intr_mode; +}; + +struct gpio_output_params { + uint32_t gpio_num; + uint32_t set; +}; + +struct wmi_gpio_output_cmd_param { + uint32_t tlv_header; + uint32_t gpio_num; + uint32_t set; +}; + +struct set_fwtest_params { + uint32_t arg; + uint32_t value; +}; + +struct wmi_fwtest_set_param_cmd_param { + uint32_t tlv_header; + uint32_t param_id; + uint32_t param_value; +}; + +struct wmi_pdev_set_param_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t param_id; + uint32_t param_value; +} __packed; + +struct wmi_pdev_set_ps_mode_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t sta_ps_mode; +} __packed; + +struct wmi_pdev_suspend_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t suspend_opt; +} __packed; + +struct wmi_pdev_resume_cmd { + uint32_t tlv_header; + uint32_t pdev_id; +} __packed; + +struct wmi_pdev_bss_chan_info_req_cmd { + uint32_t tlv_header; + /* ref wmi_bss_chan_info_req_type */ + uint32_t req_type; + uint32_t pdev_id; +} __packed; + +struct wmi_ap_ps_peer_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t param; + uint32_t value; +} __packed; + +struct wmi_sta_powersave_param_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t param; + uint32_t value; +} __packed; + +struct wmi_pdev_set_regdomain_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t reg_domain; + uint32_t reg_domain_2g; + uint32_t reg_domain_5g; + uint32_t conformance_test_limit_2g; + uint32_t conformance_test_limit_5g; + uint32_t dfs_domain; +} __packed; + +struct wmi_peer_set_param_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t param_id; + uint32_t param_value; +} __packed; + +struct wmi_peer_flush_tids_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t peer_tid_bitmap; +} __packed; + +struct wmi_dfs_phyerr_offload_cmd { + uint32_t tlv_header; + uint32_t pdev_id; +} __packed; + +struct wmi_bcn_offload_ctrl_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t bcn_ctrl_op; +} __packed; + +enum scan_dwelltime_adaptive_mode { + SCAN_DWELL_MODE_DEFAULT = 0, + SCAN_DWELL_MODE_CONSERVATIVE = 1, + SCAN_DWELL_MODE_MODERATE = 2, + SCAN_DWELL_MODE_AGGRESSIVE = 3, + SCAN_DWELL_MODE_STATIC = 4 +}; + +#define WLAN_SSID_MAX_LEN 32 + +struct element_info { + uint32_t len; + uint8_t *ptr; +}; + +struct wlan_ssid { + uint8_t length; + uint8_t ssid[WLAN_SSID_MAX_LEN]; +}; + +#define WMI_IE_BITMAP_SIZE 8 + +/* prefix used by scan requestor ids on the host */ +#define WMI_HOST_SCAN_REQUESTOR_ID_PREFIX 0xA000 + +/* prefix used by scan request ids generated on the host */ +/* host cycles through the lower 12 bits to generate ids */ +#define WMI_HOST_SCAN_REQ_ID_PREFIX 0xA000 + +/* Values lower than this may be refused by some firmware revisions with a scan + * completion with a timedout reason. + */ +#define WMI_SCAN_CHAN_MIN_TIME_MSEC 40 + +/* Scan priority numbers must be sequential, starting with 0 */ +enum wmi_scan_priority { + WMI_SCAN_PRIORITY_VERY_LOW = 0, + WMI_SCAN_PRIORITY_LOW, + WMI_SCAN_PRIORITY_MEDIUM, + WMI_SCAN_PRIORITY_HIGH, + WMI_SCAN_PRIORITY_VERY_HIGH, + WMI_SCAN_PRIORITY_COUNT /* number of priorities supported */ +}; + +enum wmi_scan_event_type { + WMI_SCAN_EVENT_STARTED = BIT(0), + WMI_SCAN_EVENT_COMPLETED = BIT(1), + WMI_SCAN_EVENT_BSS_CHANNEL = BIT(2), + WMI_SCAN_EVENT_FOREIGN_CHAN = BIT(3), + WMI_SCAN_EVENT_DEQUEUED = BIT(4), + /* possibly by high-prio scan */ + WMI_SCAN_EVENT_PREEMPTED = BIT(5), + WMI_SCAN_EVENT_START_FAILED = BIT(6), + WMI_SCAN_EVENT_RESTARTED = BIT(7), + WMI_SCAN_EVENT_FOREIGN_CHAN_EXIT = BIT(8), + WMI_SCAN_EVENT_SUSPENDED = BIT(9), + WMI_SCAN_EVENT_RESUMED = BIT(10), + WMI_SCAN_EVENT_MAX = BIT(15), +}; + +enum wmi_scan_completion_reason { + WMI_SCAN_REASON_COMPLETED, + WMI_SCAN_REASON_CANCELLED, + WMI_SCAN_REASON_PREEMPTED, + WMI_SCAN_REASON_TIMEDOUT, + WMI_SCAN_REASON_INTERNAL_FAILURE, + WMI_SCAN_REASON_MAX, +}; + +struct wmi_start_scan_cmd { + uint32_t tlv_header; + uint32_t scan_id; + uint32_t scan_req_id; + uint32_t vdev_id; + uint32_t scan_priority; + uint32_t notify_scan_events; + uint32_t dwell_time_active; + uint32_t dwell_time_passive; + uint32_t min_rest_time; + uint32_t max_rest_time; + uint32_t repeat_probe_time; + uint32_t probe_spacing_time; + uint32_t idle_time; + uint32_t max_scan_time; + uint32_t probe_delay; + uint32_t scan_ctrl_flags; + uint32_t burst_duration; + uint32_t num_chan; + uint32_t num_bssid; + uint32_t num_ssids; + uint32_t ie_len; + uint32_t n_probes; + struct wmi_mac_addr mac_addr; + struct wmi_mac_addr mac_mask; + uint32_t ie_bitmap[WMI_IE_BITMAP_SIZE]; + uint32_t num_vendor_oui; + uint32_t scan_ctrl_flags_ext; + uint32_t dwell_time_active_2g; + uint32_t dwell_time_active_6g; + uint32_t dwell_time_passive_6g; + uint32_t scan_start_offset; +} __packed; + +#define WMI_SCAN_FLAG_PASSIVE 0x1 +#define WMI_SCAN_ADD_BCAST_PROBE_REQ 0x2 +#define WMI_SCAN_ADD_CCK_RATES 0x4 +#define WMI_SCAN_ADD_OFDM_RATES 0x8 +#define WMI_SCAN_CHAN_STAT_EVENT 0x10 +#define WMI_SCAN_FILTER_PROBE_REQ 0x20 +#define WMI_SCAN_BYPASS_DFS_CHN 0x40 +#define WMI_SCAN_CONTINUE_ON_ERROR 0x80 +#define WMI_SCAN_FILTER_PROMISCUOS 0x100 +#define WMI_SCAN_FLAG_FORCE_ACTIVE_ON_DFS 0x200 +#define WMI_SCAN_ADD_TPC_IE_IN_PROBE_REQ 0x400 +#define WMI_SCAN_ADD_DS_IE_IN_PROBE_REQ 0x800 +#define WMI_SCAN_ADD_SPOOF_MAC_IN_PROBE_REQ 0x1000 +#define WMI_SCAN_OFFCHAN_MGMT_TX 0x2000 +#define WMI_SCAN_OFFCHAN_DATA_TX 0x4000 +#define WMI_SCAN_CAPTURE_PHY_ERROR 0x8000 +#define WMI_SCAN_FLAG_STRICT_PASSIVE_ON_PCHN 0x10000 +#define WMI_SCAN_FLAG_HALF_RATE_SUPPORT 0x20000 +#define WMI_SCAN_FLAG_QUARTER_RATE_SUPPORT 0x40000 +#define WMI_SCAN_RANDOM_SEQ_NO_IN_PROBE_REQ 0x80000 +#define WMI_SCAN_ENABLE_IE_WHTELIST_IN_PROBE_REQ 0x100000 + +#define WMI_SCAN_DWELL_MODE_MASK 0x00E00000 +#define WMI_SCAN_DWELL_MODE_SHIFT 21 +#define WMI_SCAN_FLAG_EXT_PASSIVE_SCAN_START_TIME_ENHANCE 0x00000800 + +#define WMI_SCAN_CONFIG_PER_CHANNEL_MASK GENMASK(19, 0) +#define WMI_SCAN_CH_FLAG_SCAN_ONLY_IF_RNR_FOUND BIT(20) + +enum { + WMI_SCAN_DWELL_MODE_DEFAULT = 0, + WMI_SCAN_DWELL_MODE_CONSERVATIVE = 1, + WMI_SCAN_DWELL_MODE_MODERATE = 2, + WMI_SCAN_DWELL_MODE_AGGRESSIVE = 3, + WMI_SCAN_DWELL_MODE_STATIC = 4, +}; + +#define WMI_SCAN_SET_DWELL_MODE(flag, mode) \ + ((flag) |= (((mode) << WMI_SCAN_DWELL_MODE_SHIFT) & \ + WMI_SCAN_DWELL_MODE_MASK)) + +struct hint_short_ssid { + uint32_t freq_flags; + uint32_t short_ssid; +}; + +struct hint_bssid { + uint32_t freq_flags; + struct wmi_mac_addr bssid; +}; + +struct scan_req_params { + uint32_t scan_id; + uint32_t scan_req_id; + uint32_t vdev_id; + uint32_t pdev_id; + enum wmi_scan_priority scan_priority; + union { + struct { + uint32_t scan_ev_started:1, + scan_ev_completed:1, + scan_ev_bss_chan:1, + scan_ev_foreign_chan:1, + scan_ev_dequeued:1, + scan_ev_preempted:1, + scan_ev_start_failed:1, + scan_ev_restarted:1, + scan_ev_foreign_chn_exit:1, + scan_ev_invalid:1, + scan_ev_gpio_timeout:1, + scan_ev_suspended:1, + scan_ev_resumed:1; + }; + uint32_t scan_events; + }; + uint32_t scan_ctrl_flags_ext; + uint32_t dwell_time_active; + uint32_t dwell_time_active_2g; + uint32_t dwell_time_passive; + uint32_t dwell_time_active_6g; + uint32_t dwell_time_passive_6g; + uint32_t min_rest_time; + uint32_t max_rest_time; + uint32_t repeat_probe_time; + uint32_t probe_spacing_time; + uint32_t idle_time; + uint32_t max_scan_time; + uint32_t probe_delay; + union { + struct { + uint32_t scan_f_passive:1, + scan_f_bcast_probe:1, + scan_f_cck_rates:1, + scan_f_ofdm_rates:1, + scan_f_chan_stat_evnt:1, + scan_f_filter_prb_req:1, + scan_f_bypass_dfs_chn:1, + scan_f_continue_on_err:1, + scan_f_offchan_mgmt_tx:1, + scan_f_offchan_data_tx:1, + scan_f_promisc_mode:1, + scan_f_capture_phy_err:1, + scan_f_strict_passive_pch:1, + scan_f_half_rate:1, + scan_f_quarter_rate:1, + scan_f_force_active_dfs_chn:1, + scan_f_add_tpc_ie_in_probe:1, + scan_f_add_ds_ie_in_probe:1, + scan_f_add_spoofed_mac_in_probe:1, + scan_f_add_rand_seq_in_probe:1, + scan_f_en_ie_whitelist_in_probe:1, + scan_f_forced:1, + scan_f_2ghz:1, + scan_f_5ghz:1, + scan_f_80mhz:1; + }; + uint32_t scan_flags; + }; + enum scan_dwelltime_adaptive_mode adaptive_dwell_time_mode; + uint32_t burst_duration; + uint32_t num_chan; + uint32_t num_bssid; + uint32_t num_ssids; + uint32_t n_probes; + uint32_t *chan_list; + uint32_t notify_scan_events; + struct wlan_ssid ssid[WLAN_SCAN_PARAMS_MAX_SSID]; + struct wmi_mac_addr bssid_list[WLAN_SCAN_PARAMS_MAX_BSSID]; + struct element_info extraie; + struct element_info htcap; + struct element_info vhtcap; + uint32_t num_hint_s_ssid; + uint32_t num_hint_bssid; + struct hint_short_ssid hint_s_ssid[WLAN_SCAN_MAX_HINT_S_SSID]; + struct hint_bssid hint_bssid[WLAN_SCAN_MAX_HINT_BSSID]; + struct wmi_mac_addr mac_addr; + struct wmi_mac_addr mac_mask; +}; + +struct wmi_ssid_arg { + int len; + const uint8_t *ssid; +}; + +struct wmi_bssid_arg { + const uint8_t *bssid; +}; + +struct wmi_start_scan_arg { + uint32_t scan_id; + uint32_t scan_req_id; + uint32_t vdev_id; + uint32_t scan_priority; + uint32_t notify_scan_events; + uint32_t dwell_time_active; + uint32_t dwell_time_passive; + uint32_t min_rest_time; + uint32_t max_rest_time; + uint32_t repeat_probe_time; + uint32_t probe_spacing_time; + uint32_t idle_time; + uint32_t max_scan_time; + uint32_t probe_delay; + uint32_t scan_ctrl_flags; + + uint32_t ie_len; + uint32_t n_channels; + uint32_t n_ssids; + uint32_t n_bssids; + + uint8_t ie[WLAN_SCAN_PARAMS_MAX_IE_LEN]; + uint32_t channels[64]; + struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID]; + struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID]; +}; + +#define WMI_SCAN_STOP_ONE 0x00000000 +#define WMI_SCN_STOP_VAP_ALL 0x01000000 +#define WMI_SCAN_STOP_ALL 0x04000000 + +/* Prefix 0xA000 indicates that the scan request + * is trigger by HOST + */ +#define ATH12K_SCAN_ID 0xA000 + +enum scan_cancel_req_type { + WLAN_SCAN_CANCEL_SINGLE = 1, + WLAN_SCAN_CANCEL_VDEV_ALL, + WLAN_SCAN_CANCEL_PDEV_ALL, +}; + +struct scan_cancel_param { + uint32_t requester; + uint32_t scan_id; + enum scan_cancel_req_type req_type; + uint32_t vdev_id; + uint32_t pdev_id; +}; + +struct wmi_bcn_send_from_host_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t data_len; + union { + uint32_t frag_ptr; + uint32_t frag_ptr_lo; + }; + uint32_t frame_ctrl; + uint32_t dtim_flag; + uint32_t bcn_antenna; + uint32_t frag_ptr_hi; +}; + +#define WMI_CHAN_INFO_MODE GENMASK(5, 0) +#define WMI_CHAN_INFO_HT40_PLUS BIT(6) +#define WMI_CHAN_INFO_PASSIVE BIT(7) +#define WMI_CHAN_INFO_ADHOC_ALLOWED BIT(8) +#define WMI_CHAN_INFO_AP_DISABLED BIT(9) +#define WMI_CHAN_INFO_DFS BIT(10) +#define WMI_CHAN_INFO_ALLOW_HT BIT(11) +#define WMI_CHAN_INFO_ALLOW_VHT BIT(12) +#define WMI_CHAN_INFO_CHAN_CHANGE_CAUSE_CSA BIT(13) +#define WMI_CHAN_INFO_HALF_RATE BIT(14) +#define WMI_CHAN_INFO_QUARTER_RATE BIT(15) +#define WMI_CHAN_INFO_DFS_FREQ2 BIT(16) +#define WMI_CHAN_INFO_ALLOW_HE BIT(17) +#define WMI_CHAN_INFO_PSC BIT(18) + +#define WMI_CHAN_REG_INFO1_MIN_PWR GENMASK(7, 0) +#define WMI_CHAN_REG_INFO1_MAX_PWR GENMASK(15, 8) +#define WMI_CHAN_REG_INFO1_MAX_REG_PWR GENMASK(23, 16) +#define WMI_CHAN_REG_INFO1_REG_CLS GENMASK(31, 24) + +#define WMI_CHAN_REG_INFO2_ANT_MAX GENMASK(7, 0) +#define WMI_CHAN_REG_INFO2_MAX_TX_PWR GENMASK(15, 8) + +struct wmi_channel { + uint32_t tlv_header; + uint32_t mhz; + uint32_t band_center_freq1; + uint32_t band_center_freq2; + uint32_t info; + uint32_t reg_info_1; + uint32_t reg_info_2; +} __packed; + +struct wmi_mgmt_params { + void *tx_frame; + uint16_t frm_len; + uint8_t vdev_id; + uint16_t chanfreq; + void *pdata; + uint16_t desc_id; + uint8_t *macaddr; +}; + +enum wmi_sta_ps_mode { + WMI_STA_PS_MODE_DISABLED = 0, + WMI_STA_PS_MODE_ENABLED = 1, +}; + +#define WMI_SMPS_MASK_LOWER_16BITS 0xFF +#define WMI_SMPS_MASK_UPPER_3BITS 0x7 +#define WMI_SMPS_PARAM_VALUE_SHIFT 29 + +#define ATH12K_WMI_FW_HANG_ASSERT_TYPE 1 +#define ATH12K_WMI_FW_HANG_DELAY 0 + +/* type, 0:unused 1: ASSERT 2: not respond detect command + * delay_time_ms, the simulate will delay time + */ + +struct wmi_force_fw_hang_cmd { + uint32_t tlv_header; + uint32_t type; + uint32_t delay_time_ms; +}; + +struct wmi_vdev_set_param_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t param_id; + uint32_t param_value; +} __packed; + +enum wmi_stats_id { + WMI_REQUEST_PEER_STAT = BIT(0), + WMI_REQUEST_AP_STAT = BIT(1), + WMI_REQUEST_PDEV_STAT = BIT(2), + WMI_REQUEST_VDEV_STAT = BIT(3), + WMI_REQUEST_BCNFLT_STAT = BIT(4), + WMI_REQUEST_VDEV_RATE_STAT = BIT(5), + WMI_REQUEST_INST_STAT = BIT(6), + WMI_REQUEST_MIB_STAT = BIT(7), + WMI_REQUEST_RSSI_PER_CHAIN_STAT = BIT(8), + WMI_REQUEST_CONGESTION_STAT = BIT(9), + WMI_REQUEST_PEER_EXTD_STAT = BIT(10), + WMI_REQUEST_BCN_STAT = BIT(11), + WMI_REQUEST_BCN_STAT_RESET = BIT(12), + WMI_REQUEST_PEER_EXTD2_STAT = BIT(13), +}; + +struct wmi_request_stats_cmd { + uint32_t tlv_header; + enum wmi_stats_id stats_id; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t pdev_id; +} __packed; + +struct wmi_get_pdev_temperature_cmd { + uint32_t tlv_header; + uint32_t param; + uint32_t pdev_id; +} __packed; + +struct wmi_ftm_seg_hdr { + uint32_t len; + uint32_t msgref; + uint32_t segmentinfo; + uint32_t pdev_id; +} __packed; + +struct wmi_ftm_cmd { + uint32_t tlv_header; + struct wmi_ftm_seg_hdr seg_hdr; + uint8_t data[]; +} __packed; + +struct wmi_ftm_event_msg { + struct wmi_ftm_seg_hdr seg_hdr; + uint8_t data[]; +} __packed; + +#define WMI_BEACON_TX_BUFFER_SIZE 512 + +#define WMI_EMA_TMPL_IDX_SHIFT 8 +#define WMI_EMA_FIRST_TMPL_SHIFT 16 +#define WMI_EMA_LAST_TMPL_SHIFT 24 + +struct wmi_bcn_tmpl_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t tim_ie_offset; + uint32_t buf_len; + uint32_t csa_switch_count_offset; + uint32_t ext_csa_switch_count_offset; + uint32_t csa_event_bitmap; + uint32_t mbssid_ie_offset; + uint32_t esp_ie_offset; + uint32_t csc_switch_count_offset; + uint32_t csc_event_bitmap; + uint32_t mu_edca_ie_offset; + uint32_t feature_enable_bitmap; + uint32_t ema_params; +} __packed; + +struct wmi_key_seq_counter { + uint32_t key_seq_counter_l; + uint32_t key_seq_counter_h; +} __packed; + +struct wmi_vdev_install_key_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t key_idx; + uint32_t key_flags; + uint32_t key_cipher; + struct wmi_key_seq_counter key_rsc_counter; + struct wmi_key_seq_counter key_global_rsc_counter; + struct wmi_key_seq_counter key_tsc_counter; + uint8_t wpi_key_rsc_counter[16]; + uint8_t wpi_key_tsc_counter[16]; + uint32_t key_len; + uint32_t key_txmic_len; + uint32_t key_rxmic_len; + uint32_t is_group_key_id_valid; + uint32_t group_key_id; + + /* Followed by key_data containing key followed by + * tx mic and then rx mic + */ +} __packed; + +struct wmi_vdev_install_key_arg { + uint32_t vdev_id; + const uint8_t *macaddr; + uint32_t key_idx; + uint32_t key_flags; + uint32_t key_cipher; + uint32_t key_len; + uint32_t key_txmic_len; + uint32_t key_rxmic_len; + uint64_t key_rsc_counter; + const void *key_data; +}; + +#define WMI_MAX_SUPPORTED_RATES 128 +#define WMI_HOST_MAX_HECAP_PHY_SIZE 3 +#define WMI_HOST_MAX_HE_RATE_SET 3 +#define WMI_HECAP_TXRX_MCS_NSS_IDX_80 0 +#define WMI_HECAP_TXRX_MCS_NSS_IDX_160 1 +#define WMI_HECAP_TXRX_MCS_NSS_IDX_80_80 2 + +struct wmi_rate_set_arg { + uint32_t num_rates; + uint8_t rates[WMI_MAX_SUPPORTED_RATES]; +}; + +struct peer_assoc_params { + struct wmi_mac_addr peer_macaddr; + uint32_t vdev_id; + uint32_t peer_new_assoc; + uint32_t peer_associd; + uint32_t peer_flags; + uint32_t peer_caps; + uint32_t peer_listen_intval; + uint32_t peer_ht_caps; + uint32_t peer_max_mpdu; + uint32_t peer_mpdu_density; + uint32_t peer_rate_caps; + uint32_t peer_nss; + uint32_t peer_vht_caps; + uint32_t peer_phymode; + uint32_t peer_ht_info[2]; + struct wmi_rate_set_arg peer_legacy_rates; + struct wmi_rate_set_arg peer_ht_rates; + uint32_t rx_max_rate; + uint32_t rx_mcs_set; + uint32_t tx_max_rate; + uint32_t tx_mcs_set; + uint8_t vht_capable; + uint8_t min_data_rate; + uint32_t tx_max_mcs_nss; + uint32_t peer_bw_rxnss_override; + bool is_pmf_enabled; + bool is_wme_set; + bool qos_flag; + bool apsd_flag; + bool ht_flag; + bool bw_40; + bool bw_80; + bool bw_160; + bool stbc_flag; + bool ldpc_flag; + bool static_mimops_flag; + bool dynamic_mimops_flag; + bool spatial_mux_flag; + bool vht_flag; + bool vht_ng_flag; + bool need_ptk_4_way; + bool need_gtk_2_way; + bool auth_flag; + bool safe_mode_enabled; + bool amsdu_disable; + /* Use common structure */ + uint8_t peer_mac[IEEE80211_ADDR_LEN]; + + bool he_flag; + uint32_t peer_he_cap_macinfo[2]; + uint32_t peer_he_cap_macinfo_internal; + uint32_t peer_he_caps_6ghz; + uint32_t peer_he_ops; + uint32_t peer_he_cap_phyinfo[WMI_HOST_MAX_HECAP_PHY_SIZE]; + uint32_t peer_he_mcs_count; + uint32_t peer_he_rx_mcs_set[WMI_HOST_MAX_HE_RATE_SET]; + uint32_t peer_he_tx_mcs_set[WMI_HOST_MAX_HE_RATE_SET]; + bool twt_responder; + bool twt_requester; + bool is_assoc; + struct ath12k_ppe_threshold peer_ppet; +}; + +struct wmi_peer_assoc_complete_cmd { + uint32_t tlv_header; + struct wmi_mac_addr peer_macaddr; + uint32_t vdev_id; + uint32_t peer_new_assoc; + uint32_t peer_associd; + uint32_t peer_flags; + uint32_t peer_caps; + uint32_t peer_listen_intval; + uint32_t peer_ht_caps; + uint32_t peer_max_mpdu; + uint32_t peer_mpdu_density; + uint32_t peer_rate_caps; + uint32_t peer_nss; + uint32_t peer_vht_caps; + uint32_t peer_phymode; + uint32_t peer_ht_info[2]; + uint32_t num_peer_legacy_rates; + uint32_t num_peer_ht_rates; + uint32_t peer_bw_rxnss_override; + struct wmi_ppe_threshold peer_ppet; + uint32_t peer_he_cap_info; + uint32_t peer_he_ops; + uint32_t peer_he_cap_phy[WMI_MAX_HECAP_PHY_SIZE]; + uint32_t peer_he_mcs; + uint32_t peer_he_cap_info_ext; + uint32_t peer_he_cap_info_internal; + uint32_t min_data_rate; + uint32_t peer_he_caps_6ghz; +} __packed; + +struct wmi_stop_scan_cmd { + uint32_t tlv_header; + uint32_t requestor; + uint32_t scan_id; + uint32_t req_type; + uint32_t vdev_id; + uint32_t pdev_id; +}; + +struct scan_chan_list_params { + uint32_t pdev_id; + uint16_t nallchans; + struct channel_param ch_param[]; +}; + +struct wmi_scan_chan_list_cmd { + uint32_t tlv_header; + uint32_t num_scan_chans; + uint32_t flags; + uint32_t pdev_id; +} __packed; + +struct wmi_scan_prob_req_oui_cmd { + uint32_t tlv_header; + uint32_t prob_req_oui; +} __packed; + +#define WMI_MGMT_SEND_DOWNLD_LEN 64 + +#define WMI_TX_PARAMS_DWORD0_POWER GENMASK(7, 0) +#define WMI_TX_PARAMS_DWORD0_MCS_MASK GENMASK(19, 8) +#define WMI_TX_PARAMS_DWORD0_NSS_MASK GENMASK(27, 20) +#define WMI_TX_PARAMS_DWORD0_RETRY_LIMIT GENMASK(31, 28) + +#define WMI_TX_PARAMS_DWORD1_CHAIN_MASK GENMASK(7, 0) +#define WMI_TX_PARAMS_DWORD1_BW_MASK GENMASK(14, 8) +#define WMI_TX_PARAMS_DWORD1_PREAMBLE_TYPE GENMASK(19, 15) +#define WMI_TX_PARAMS_DWORD1_FRAME_TYPE BIT(20) +#define WMI_TX_PARAMS_DWORD1_RSVD GENMASK(31, 21) + +struct wmi_mgmt_send_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t desc_id; + uint32_t chanfreq; + uint32_t paddr_lo; + uint32_t paddr_hi; + uint32_t frame_len; + uint32_t buf_len; + uint32_t tx_params_valid; + + /* + * Followed by struct wmi_tlv and buf_len bytes of frame data with + * buf_len <= WMI_MGMT_SEND_DOWNLD_LEN, which may be exceeded by + * frame_len. The full frame is mapped at paddr_lo/hi. + * Presumably the idea is that small frames can skip the extra DMA + * transfer of frame data after the command has been transferred. + */ +} __packed; + +struct wmi_sta_powersave_mode_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t sta_ps_mode; +}; + +struct wmi_sta_smps_force_mode_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t forced_mode; +}; + +struct wmi_sta_smps_param_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t param; + uint32_t value; +}; + +struct wmi_bcn_prb_info { + uint32_t tlv_header; + uint32_t caps; + uint32_t erp; +} __packed; + +enum { + WMI_PDEV_SUSPEND, + WMI_PDEV_SUSPEND_AND_DISABLE_INTR, +}; + +struct green_ap_ps_params { + uint32_t value; +}; + +struct wmi_pdev_green_ap_ps_enable_cmd_param { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t enable; +}; + +struct ap_ps_params { + uint32_t vdev_id; + uint32_t param; + uint32_t value; +}; + +struct vdev_set_params { + uint32_t if_id; + uint32_t param_id; + uint32_t param_value; +}; + +struct stats_request_params { + uint32_t stats_id; + uint32_t vdev_id; + uint32_t pdev_id; +}; + +struct wmi_set_current_country_params { + uint8_t alpha2[3]; +}; + +struct wmi_set_current_country_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t new_alpha2; +} __packed; + +enum set_init_cc_type { + WMI_COUNTRY_INFO_TYPE_ALPHA, + WMI_COUNTRY_INFO_TYPE_COUNTRY_CODE, + WMI_COUNTRY_INFO_TYPE_REGDOMAIN, +}; + +enum set_init_cc_flags { + INVALID_CC, + CC_IS_SET, + REGDMN_IS_SET, + ALPHA_IS_SET, +}; + +struct wmi_init_country_params { + union { + uint16_t country_code; + uint16_t regdom_id; + uint8_t alpha2[3]; + } cc_info; + enum set_init_cc_flags flags; +}; + +struct wmi_init_country_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t init_cc_type; + union { + uint32_t country_code; + uint32_t regdom_id; + uint32_t alpha2; + } cc_info; +} __packed; + +struct wmi_11d_scan_start_params { + uint32_t vdev_id; + uint32_t scan_period_msec; + uint32_t start_interval_msec; +}; + +struct wmi_11d_scan_start_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t scan_period_msec; + uint32_t start_interval_msec; +} __packed; + +struct wmi_11d_scan_stop_cmd { + uint32_t tlv_header; + uint32_t vdev_id; +} __packed; + +struct wmi_11d_new_cc_ev { + uint32_t new_alpha2; +} __packed; + +#define THERMAL_LEVELS 1 +struct tt_level_config { + uint32_t tmplwm; + uint32_t tmphwm; + uint32_t dcoffpercent; + uint32_t priority; +}; + +struct thermal_mitigation_params { + uint32_t pdev_id; + uint32_t enable; + uint32_t dc; + uint32_t dc_per_event; + struct tt_level_config levelconf[THERMAL_LEVELS]; +}; + +struct wmi_therm_throt_config_request_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t enable; + uint32_t dc; + uint32_t dc_per_event; + uint32_t therm_throt_levels; +} __packed; + +struct wmi_therm_throt_level_config_info { + uint32_t tlv_header; + uint32_t temp_lwm; + uint32_t temp_hwm; + uint32_t dc_off_percent; + uint32_t prio; +} __packed; + +struct wmi_delba_send_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t tid; + uint32_t initiator; + uint32_t reasoncode; +} __packed; + +struct wmi_addba_setresponse_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t tid; + uint32_t statuscode; +} __packed; + +struct wmi_addba_send_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t tid; + uint32_t buffersize; +} __packed; + +struct wmi_addba_clear_resp_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_pdev_pktlog_filter_info { + uint32_t tlv_header; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_pdev_pktlog_filter_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t enable; + uint32_t filter_type; + uint32_t num_mac; +} __packed; + +enum ath12k_wmi_pktlog_enable { + ATH12K_WMI_PKTLOG_ENABLE_AUTO = 0, + ATH12K_WMI_PKTLOG_ENABLE_FORCE = 1, +}; + +struct wmi_pktlog_enable_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t evlist; /* WMI_PKTLOG_EVENT */ + uint32_t enable; +} __packed; + +struct wmi_pktlog_disable_cmd { + uint32_t tlv_header; + uint32_t pdev_id; +} __packed; + +#define DFS_PHYERR_UNIT_TEST_CMD 0 +#define DFS_UNIT_TEST_MODULE 0x2b +#define DFS_UNIT_TEST_TOKEN 0xAA + +enum dfs_test_args_idx { + DFS_TEST_CMDID = 0, + DFS_TEST_PDEV_ID, + DFS_TEST_RADAR_PARAM, + DFS_MAX_TEST_ARGS, +}; + +struct wmi_dfs_unit_test_arg { + uint32_t cmd_id; + uint32_t pdev_id; + uint32_t radar_param; +}; + +struct wmi_unit_test_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t module_id; + uint32_t num_args; + uint32_t diag_token; + /* Followed by test args*/ +} __packed; + +#define MAX_SUPPORTED_RATES 128 + +#define WMI_PEER_AUTH 0x00000001 +#define WMI_PEER_QOS 0x00000002 +#define WMI_PEER_NEED_PTK_4_WAY 0x00000004 +#define WMI_PEER_NEED_GTK_2_WAY 0x00000010 +#define WMI_PEER_HE 0x00000400 +#define WMI_PEER_APSD 0x00000800 +#define WMI_PEER_HT 0x00001000 +#define WMI_PEER_40MHZ 0x00002000 +#define WMI_PEER_STBC 0x00008000 +#define WMI_PEER_LDPC 0x00010000 +#define WMI_PEER_DYN_MIMOPS 0x00020000 +#define WMI_PEER_STATIC_MIMOPS 0x00040000 +#define WMI_PEER_SPATIAL_MUX 0x00200000 +#define WMI_PEER_TWT_REQ 0x00400000 +#define WMI_PEER_TWT_RESP 0x00800000 +#define WMI_PEER_VHT 0x02000000 +#define WMI_PEER_80MHZ 0x04000000 +#define WMI_PEER_PMF 0x08000000 +/* TODO: Place holder for WLAN_PEER_F_PS_PRESEND_REQUIRED = 0x10000000. + * Need to be cleaned up + */ +#define WMI_PEER_IS_P2P_CAPABLE 0x20000000 +#define WMI_PEER_160MHZ 0x40000000 +#define WMI_PEER_SAFEMODE_EN 0x80000000 + +struct beacon_tmpl_params { + uint8_t vdev_id; + uint32_t tim_ie_offset; + uint32_t tmpl_len; + uint32_t tmpl_len_aligned; + uint32_t csa_switch_count_offset; + uint32_t ext_csa_switch_count_offset; + uint8_t *frm; +}; + +struct wmi_rate_set { + uint32_t num_rates; + uint32_t rates[(MAX_SUPPORTED_RATES / 4) + 1]; +}; + +struct wmi_vht_rate_set { + uint32_t tlv_header; + uint32_t rx_max_rate; + uint32_t rx_mcs_set; + uint32_t tx_max_rate; + uint32_t tx_mcs_set; + uint32_t tx_max_mcs_nss; +} __packed; + +struct wmi_he_rate_set { + uint32_t tlv_header; + + /* MCS at which the peer can receive */ + uint32_t rx_mcs_set; + + /* MCS at which the peer can transmit */ + uint32_t tx_mcs_set; +} __packed; + +#define MAX_REG_RULES 10 +#define REG_ALPHA2_LEN 2 +#define MAX_6GHZ_REG_RULES 5 + +enum wmi_start_event_param { + WMI_VDEV_START_RESP_EVENT = 0, + WMI_VDEV_RESTART_RESP_EVENT, +}; + +struct wmi_vdev_start_resp_event { + uint32_t vdev_id; + uint32_t requestor_id; + enum wmi_start_event_param resp_type; + uint32_t status; + uint32_t chain_mask; + uint32_t smps_mode; + union { + uint32_t mac_id; + uint32_t pdev_id; + }; + uint32_t cfgd_tx_streams; + uint32_t cfgd_rx_streams; +} __packed; + +/* VDEV start response status codes */ +enum wmi_vdev_start_resp_status_code { + WMI_VDEV_START_RESPONSE_STATUS_SUCCESS = 0, + WMI_VDEV_START_RESPONSE_INVALID_VDEVID = 1, + WMI_VDEV_START_RESPONSE_NOT_SUPPORTED = 2, + WMI_VDEV_START_RESPONSE_DFS_VIOLATION = 3, + WMI_VDEV_START_RESPONSE_INVALID_REGDOMAIN = 4, +}; + +/* Regaulatory Rule Flags Passed by FW */ +#define REGULATORY_CHAN_DISABLED BIT(0) +#define REGULATORY_CHAN_NO_IR BIT(1) +#define REGULATORY_CHAN_RADAR BIT(3) +#define REGULATORY_CHAN_NO_OFDM BIT(6) +#define REGULATORY_CHAN_INDOOR_ONLY BIT(9) + +#define REGULATORY_CHAN_NO_HT40 BIT(4) +#define REGULATORY_CHAN_NO_80MHZ BIT(7) +#define REGULATORY_CHAN_NO_160MHZ BIT(8) +#define REGULATORY_CHAN_NO_20MHZ BIT(11) +#define REGULATORY_CHAN_NO_10MHZ BIT(12) + +enum wmi_reg_chan_list_cmd_type { + WMI_REG_CHAN_LIST_CC_ID = 0, + WMI_REG_CHAN_LIST_CC_EXT_ID = 1, +}; + +enum wmi_reg_cc_setting_code { + WMI_REG_SET_CC_STATUS_PASS = 0, + WMI_REG_CURRENT_ALPHA2_NOT_FOUND = 1, + WMI_REG_INIT_ALPHA2_NOT_FOUND = 2, + WMI_REG_SET_CC_CHANGE_NOT_ALLOWED = 3, + WMI_REG_SET_CC_STATUS_NO_MEMORY = 4, + WMI_REG_SET_CC_STATUS_FAIL = 5, + + /* add new setting code above, update in + * @enum cc_setting_code as well. + * Also handle it in ath12k_wmi_cc_setting_code_to_reg() + */ +}; + +enum cc_setting_code { + REG_SET_CC_STATUS_PASS = 0, + REG_CURRENT_ALPHA2_NOT_FOUND = 1, + REG_INIT_ALPHA2_NOT_FOUND = 2, + REG_SET_CC_CHANGE_NOT_ALLOWED = 3, + REG_SET_CC_STATUS_NO_MEMORY = 4, + REG_SET_CC_STATUS_FAIL = 5, + + /* add new setting code above, update in + * @enum wmi_reg_cc_setting_code as well. + * Also handle it in ath12k_cc_status_to_str() + */ +}; + +static inline enum cc_setting_code +qwz_wmi_cc_setting_code_to_reg(enum wmi_reg_cc_setting_code status_code) +{ + switch (status_code) { + case WMI_REG_SET_CC_STATUS_PASS: + return REG_SET_CC_STATUS_PASS; + case WMI_REG_CURRENT_ALPHA2_NOT_FOUND: + return REG_CURRENT_ALPHA2_NOT_FOUND; + case WMI_REG_INIT_ALPHA2_NOT_FOUND: + return REG_INIT_ALPHA2_NOT_FOUND; + case WMI_REG_SET_CC_CHANGE_NOT_ALLOWED: + return REG_SET_CC_CHANGE_NOT_ALLOWED; + case WMI_REG_SET_CC_STATUS_NO_MEMORY: + return REG_SET_CC_STATUS_NO_MEMORY; + case WMI_REG_SET_CC_STATUS_FAIL: + return REG_SET_CC_STATUS_FAIL; + } + + return REG_SET_CC_STATUS_FAIL; +} + +static inline const char * +qwz_cc_status_to_str(enum cc_setting_code code) +{ + switch (code) { + case REG_SET_CC_STATUS_PASS: + return "REG_SET_CC_STATUS_PASS"; + case REG_CURRENT_ALPHA2_NOT_FOUND: + return "REG_CURRENT_ALPHA2_NOT_FOUND"; + case REG_INIT_ALPHA2_NOT_FOUND: + return "REG_INIT_ALPHA2_NOT_FOUND"; + case REG_SET_CC_CHANGE_NOT_ALLOWED: + return "REG_SET_CC_CHANGE_NOT_ALLOWED"; + case REG_SET_CC_STATUS_NO_MEMORY: + return "REG_SET_CC_STATUS_NO_MEMORY"; + case REG_SET_CC_STATUS_FAIL: + return "REG_SET_CC_STATUS_FAIL"; + } + + return "Unknown CC status"; +} + +enum wmi_reg_6ghz_ap_type { + WMI_REG_INDOOR_AP = 0, + WMI_REG_STANDARD_POWER_AP = 1, + WMI_REG_VERY_LOW_POWER_AP = 2, + + /* add AP type above, handle in ath12k_6ghz_ap_type_to_str() + */ + WMI_REG_CURRENT_MAX_AP_TYPE, + WMI_REG_MAX_AP_TYPE = 7, +}; + +static inline const char * +qwz_6ghz_ap_type_to_str(enum wmi_reg_6ghz_ap_type type) +{ + switch (type) { + case WMI_REG_INDOOR_AP: + return "INDOOR AP"; + case WMI_REG_STANDARD_POWER_AP: + return "STANDARD POWER AP"; + case WMI_REG_VERY_LOW_POWER_AP: + return "VERY LOW POWER AP"; + case WMI_REG_CURRENT_MAX_AP_TYPE: + return "CURRENT_MAX_AP_TYPE"; + case WMI_REG_MAX_AP_TYPE: + return "MAX_AP_TYPE"; + } + + return "unknown 6 GHz AP type"; +} + +enum wmi_reg_6ghz_client_type { + WMI_REG_DEFAULT_CLIENT = 0, + WMI_REG_SUBORDINATE_CLIENT = 1, + WMI_REG_MAX_CLIENT_TYPE = 2, + + /* add client type above, handle it in + * ath12k_6ghz_client_type_to_str() + */ +}; + +static inline const char * +qwz_6ghz_client_type_to_str(enum wmi_reg_6ghz_client_type type) +{ + switch (type) { + case WMI_REG_DEFAULT_CLIENT: + return "DEFAULT CLIENT"; + case WMI_REG_SUBORDINATE_CLIENT: + return "SUBORDINATE CLIENT"; + case WMI_REG_MAX_CLIENT_TYPE: + return "MAX_CLIENT_TYPE"; + } + + return "unknown 6 GHz client type"; +} + +enum reg_subdomains_6ghz { + EMPTY_6GHZ = 0x0, + FCC1_CLIENT_LPI_REGULAR_6GHZ = 0x01, + FCC1_CLIENT_SP_6GHZ = 0x02, + FCC1_AP_LPI_6GHZ = 0x03, + FCC1_CLIENT_LPI_SUBORDINATE = FCC1_AP_LPI_6GHZ, + FCC1_AP_SP_6GHZ = 0x04, + ETSI1_LPI_6GHZ = 0x10, + ETSI1_VLP_6GHZ = 0x11, + ETSI2_LPI_6GHZ = 0x12, + ETSI2_VLP_6GHZ = 0x13, + APL1_LPI_6GHZ = 0x20, + APL1_VLP_6GHZ = 0x21, + + /* add sub-domain above, handle it in + * ath12k_sub_reg_6ghz_to_str() + */ +}; + +static inline const char * +qwz_sub_reg_6ghz_to_str(enum reg_subdomains_6ghz sub_id) +{ + switch (sub_id) { + case EMPTY_6GHZ: + return "N/A"; + case FCC1_CLIENT_LPI_REGULAR_6GHZ: + return "FCC1_CLIENT_LPI_REGULAR_6GHZ"; + case FCC1_CLIENT_SP_6GHZ: + return "FCC1_CLIENT_SP_6GHZ"; + case FCC1_AP_LPI_6GHZ: + return "FCC1_AP_LPI_6GHZ/FCC1_CLIENT_LPI_SUBORDINATE"; + case FCC1_AP_SP_6GHZ: + return "FCC1_AP_SP_6GHZ"; + case ETSI1_LPI_6GHZ: + return "ETSI1_LPI_6GHZ"; + case ETSI1_VLP_6GHZ: + return "ETSI1_VLP_6GHZ"; + case ETSI2_LPI_6GHZ: + return "ETSI2_LPI_6GHZ"; + case ETSI2_VLP_6GHZ: + return "ETSI2_VLP_6GHZ"; + case APL1_LPI_6GHZ: + return "APL1_LPI_6GHZ"; + case APL1_VLP_6GHZ: + return "APL1_VLP_6GHZ"; + } + + return "unknown sub reg id"; +} + +enum reg_super_domain_6ghz { + FCC1_6GHZ = 0x01, + ETSI1_6GHZ = 0x02, + ETSI2_6GHZ = 0x03, + APL1_6GHZ = 0x04, + FCC1_6GHZ_CL = 0x05, + + /* add super domain above, handle it in + * ath12k_super_reg_6ghz_to_str() + */ +}; + +static inline const char * +qwz_super_reg_6ghz_to_str(enum reg_super_domain_6ghz domain_id) +{ + switch (domain_id) { + case FCC1_6GHZ: + return "FCC1_6GHZ"; + case ETSI1_6GHZ: + return "ETSI1_6GHZ"; + case ETSI2_6GHZ: + return "ETSI2_6GHZ"; + case APL1_6GHZ: + return "APL1_6GHZ"; + case FCC1_6GHZ_CL: + return "FCC1_6GHZ_CL"; + } + + return "unknown domain id"; +} + +struct cur_reg_rule { + uint16_t start_freq; + uint16_t end_freq; + uint16_t max_bw; + uint8_t reg_power; + uint8_t ant_gain; + uint16_t flags; + bool psd_flag; + int8_t psd_eirp; +}; + +struct cur_regulatory_info { + enum cc_setting_code status_code; + uint8_t num_phy; + uint8_t phy_id; + uint16_t reg_dmn_pair; + uint16_t ctry_code; + uint8_t alpha2[REG_ALPHA2_LEN + 1]; + uint32_t dfs_region; + uint32_t phybitmap; + uint32_t min_bw_2ghz; + uint32_t max_bw_2ghz; + uint32_t min_bw_5ghz; + uint32_t max_bw_5ghz; + uint32_t num_2ghz_reg_rules; + uint32_t num_5ghz_reg_rules; + struct cur_reg_rule *reg_rules_2ghz_ptr; + struct cur_reg_rule *reg_rules_5ghz_ptr; + bool is_ext_reg_event; + enum wmi_reg_6ghz_client_type client_type; + bool rnr_tpe_usable; + bool unspecified_ap_usable; + uint8_t domain_code_6ghz_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + uint8_t domain_code_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + uint32_t domain_code_6ghz_super_id; + uint32_t min_bw_6ghz_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + uint32_t max_bw_6ghz_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + uint32_t min_bw_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + uint32_t max_bw_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + uint32_t num_6ghz_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + uint32_t num_6ghz_rules_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + struct cur_reg_rule *reg_rules_6ghz_ap_ptr[WMI_REG_CURRENT_MAX_AP_TYPE]; + struct cur_reg_rule *reg_rules_6ghz_client_ptr + [WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; +}; + +struct wmi_reg_chan_list_cc_event { + uint32_t status_code; + uint32_t phy_id; + uint32_t alpha2; + uint32_t num_phy; + uint32_t country_id; + uint32_t domain_code; + uint32_t dfs_region; + uint32_t phybitmap; + uint32_t min_bw_2ghz; + uint32_t max_bw_2ghz; + uint32_t min_bw_5ghz; + uint32_t max_bw_5ghz; + uint32_t num_2ghz_reg_rules; + uint32_t num_5ghz_reg_rules; +} __packed; + +struct wmi_regulatory_rule_struct { + uint32_t tlv_header; + uint32_t freq_info; + uint32_t bw_pwr_info; + uint32_t flag_info; +}; + +#define WMI_REG_CLIENT_MAX 4 + +struct wmi_reg_chan_list_cc_ext_event { + uint32_t status_code; + uint32_t phy_id; + uint32_t alpha2; + uint32_t num_phy; + uint32_t country_id; + uint32_t domain_code; + uint32_t dfs_region; + uint32_t phybitmap; + uint32_t min_bw_2ghz; + uint32_t max_bw_2ghz; + uint32_t min_bw_5ghz; + uint32_t max_bw_5ghz; + uint32_t num_2ghz_reg_rules; + uint32_t num_5ghz_reg_rules; + uint32_t client_type; + uint32_t rnr_tpe_usable; + uint32_t unspecified_ap_usable; + uint32_t domain_code_6ghz_ap_lpi; + uint32_t domain_code_6ghz_ap_sp; + uint32_t domain_code_6ghz_ap_vlp; + uint32_t domain_code_6ghz_client_lpi[WMI_REG_CLIENT_MAX]; + uint32_t domain_code_6ghz_client_sp[WMI_REG_CLIENT_MAX]; + uint32_t domain_code_6ghz_client_vlp[WMI_REG_CLIENT_MAX]; + uint32_t domain_code_6ghz_super_id; + uint32_t min_bw_6ghz_ap_sp; + uint32_t max_bw_6ghz_ap_sp; + uint32_t min_bw_6ghz_ap_lpi; + uint32_t max_bw_6ghz_ap_lpi; + uint32_t min_bw_6ghz_ap_vlp; + uint32_t max_bw_6ghz_ap_vlp; + uint32_t min_bw_6ghz_client_sp[WMI_REG_CLIENT_MAX]; + uint32_t max_bw_6ghz_client_sp[WMI_REG_CLIENT_MAX]; + uint32_t min_bw_6ghz_client_lpi[WMI_REG_CLIENT_MAX]; + uint32_t max_bw_6ghz_client_lpi[WMI_REG_CLIENT_MAX]; + uint32_t min_bw_6ghz_client_vlp[WMI_REG_CLIENT_MAX]; + uint32_t max_bw_6ghz_client_vlp[WMI_REG_CLIENT_MAX]; + uint32_t num_6ghz_reg_rules_ap_sp; + uint32_t num_6ghz_reg_rules_ap_lpi; + uint32_t num_6ghz_reg_rules_ap_vlp; + uint32_t num_6ghz_reg_rules_client_sp[WMI_REG_CLIENT_MAX]; + uint32_t num_6ghz_reg_rules_client_lpi[WMI_REG_CLIENT_MAX]; + uint32_t num_6ghz_reg_rules_client_vlp[WMI_REG_CLIENT_MAX]; +} __packed; + +struct wmi_regulatory_ext_rule { + uint32_t tlv_header; + uint32_t freq_info; + uint32_t bw_pwr_info; + uint32_t flag_info; + uint32_t psd_power_info; +} __packed; + +struct wmi_vdev_delete_resp_event { + uint32_t vdev_id; +} __packed; + +struct wmi_peer_delete_resp_event { + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_bcn_tx_status_event { + uint32_t vdev_id; + uint32_t tx_status; +} __packed; + +struct wmi_vdev_stopped_event { + uint32_t vdev_id; +} __packed; + +struct wmi_pdev_bss_chan_info_event { + uint32_t freq; /* Units in MHz */ + uint32_t noise_floor; /* units are dBm */ + /* rx clear - how often the channel was unused */ + uint32_t rx_clear_count_low; + uint32_t rx_clear_count_high; + /* cycle count - elapsed time during measured period, in clock ticks */ + uint32_t cycle_count_low; + uint32_t cycle_count_high; + /* tx cycle count - elapsed time spent in tx, in clock ticks */ + uint32_t tx_cycle_count_low; + uint32_t tx_cycle_count_high; + /* rx cycle count - elapsed time spent in rx, in clock ticks */ + uint32_t rx_cycle_count_low; + uint32_t rx_cycle_count_high; + /*rx_cycle cnt for my bss in 64bits format */ + uint32_t rx_bss_cycle_count_low; + uint32_t rx_bss_cycle_count_high; + uint32_t pdev_id; +} __packed; + +#define WMI_VDEV_INSTALL_KEY_COMPL_STATUS_SUCCESS 0 + +struct wmi_vdev_install_key_compl_event { + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t key_idx; + uint32_t key_flags; + uint32_t status; +} __packed; + +struct wmi_vdev_install_key_complete_arg { + uint32_t vdev_id; + const uint8_t *macaddr; + uint32_t key_idx; + uint32_t key_flags; + uint32_t status; +}; + +struct wmi_peer_assoc_conf_event { + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_peer_assoc_conf_arg { + uint32_t vdev_id; + const uint8_t *macaddr; +}; + +struct wmi_fils_discovery_event { + uint32_t vdev_id; + uint32_t fils_tt; + uint32_t tbtt; +} __packed; + +struct wmi_probe_resp_tx_status_event { + uint32_t vdev_id; + uint32_t tx_status; +} __packed; + +/* + * PDEV statistics + */ +struct wmi_pdev_stats_base { + int32_t chan_nf; + uint32_t tx_frame_count; /* Cycles spent transmitting frames */ + uint32_t rx_frame_count; /* Cycles spent receiving frames */ + uint32_t rx_clear_count; /* Total channel busy time, evidently */ + uint32_t cycle_count; /* Total on-channel time */ + uint32_t phy_err_count; + uint32_t chan_tx_pwr; +} __packed; + +struct wmi_pdev_stats_extra { + uint32_t ack_rx_bad; + uint32_t rts_bad; + uint32_t rts_good; + uint32_t fcs_bad; + uint32_t no_beacons; + uint32_t mib_int_count; +} __packed; + +struct wmi_pdev_stats_tx { + /* Num HTT cookies queued to dispatch list */ + int32_t comp_queued; + + /* Num HTT cookies dispatched */ + int32_t comp_delivered; + + /* Num MSDU queued to WAL */ + int32_t msdu_enqued; + + /* Num MPDU queue to WAL */ + int32_t mpdu_enqued; + + /* Num MSDUs dropped by WMM limit */ + int32_t wmm_drop; + + /* Num Local frames queued */ + int32_t local_enqued; + + /* Num Local frames done */ + int32_t local_freed; + + /* Num queued to HW */ + int32_t hw_queued; + + /* Num PPDU reaped from HW */ + int32_t hw_reaped; + + /* Num underruns */ + int32_t underrun; + + /* Num hw paused */ + uint32_t hw_paused; + + /* Num PPDUs cleaned up in TX abort */ + int32_t tx_abort; + + /* Num MPDUs requeued by SW */ + int32_t mpdus_requeued; + + /* excessive retries */ + uint32_t tx_ko; + + uint32_t tx_xretry; + + /* data hw rate code */ + uint32_t data_rc; + + /* Scheduler self triggers */ + uint32_t self_triggers; + + /* frames dropped due to excessive sw retries */ + uint32_t sw_retry_failure; + + /* illegal rate phy errors */ + uint32_t illgl_rate_phy_err; + + /* wal pdev continuous xretry */ + uint32_t pdev_cont_xretry; + + /* wal pdev tx timeouts */ + uint32_t pdev_tx_timeout; + + /* wal pdev resets */ + uint32_t pdev_resets; + + /* frames dropped due to non-availability of stateless TIDs */ + uint32_t stateless_tid_alloc_failure; + + /* PhY/BB underrun */ + uint32_t phy_underrun; + + /* MPDU is more than txop limit */ + uint32_t txop_ovf; + + /* Num sequences posted */ + uint32_t seq_posted; + + /* Num sequences failed in queueing */ + uint32_t seq_failed_queueing; + + /* Num sequences completed */ + uint32_t seq_completed; + + /* Num sequences restarted */ + uint32_t seq_restarted; + + /* Num of MU sequences posted */ + uint32_t mu_seq_posted; + + /* Num MPDUs flushed by SW, HWPAUSED, SW TXABORT + * (Reset,channel change) + */ + int32_t mpdus_sw_flush; + + /* Num MPDUs filtered by HW, all filter condition (TTL expired) */ + int32_t mpdus_hw_filter; + + /* Num MPDUs truncated by PDG (TXOP, TBTT, + * PPDU_duration based on rate, dyn_bw) + */ + int32_t mpdus_truncated; + + /* Num MPDUs that was tried but didn't receive ACK or BA */ + int32_t mpdus_ack_failed; + + /* Num MPDUs that was dropped du to expiry. */ + int32_t mpdus_expired; +} __packed; + +struct wmi_pdev_stats_rx { + /* Cnts any change in ring routing mid-ppdu */ + int32_t mid_ppdu_route_change; + + /* Total number of statuses processed */ + int32_t status_rcvd; + + /* Extra frags on rings 0-3 */ + int32_t r0_frags; + int32_t r1_frags; + int32_t r2_frags; + int32_t r3_frags; + + /* MSDUs / MPDUs delivered to HTT */ + int32_t htt_msdus; + int32_t htt_mpdus; + + /* MSDUs / MPDUs delivered to local stack */ + int32_t loc_msdus; + int32_t loc_mpdus; + + /* AMSDUs that have more MSDUs than the status ring size */ + int32_t oversize_amsdu; + + /* Number of PHY errors */ + int32_t phy_errs; + + /* Number of PHY errors drops */ + int32_t phy_err_drop; + + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + int32_t mpdu_errs; + + /* Num overflow errors */ + int32_t rx_ovfl_errs; +} __packed; + +struct wmi_pdev_stats { + struct wmi_pdev_stats_base base; + struct wmi_pdev_stats_tx tx; + struct wmi_pdev_stats_rx rx; +} __packed; + +#define WLAN_MAX_AC 4 +#define MAX_TX_RATE_VALUES 10 +#define MAX_TX_RATE_VALUES 10 + +struct wmi_vdev_stats { + uint32_t vdev_id; + uint32_t beacon_snr; + uint32_t data_snr; + uint32_t num_tx_frames[WLAN_MAX_AC]; + uint32_t num_rx_frames; + uint32_t num_tx_frames_retries[WLAN_MAX_AC]; + uint32_t num_tx_frames_failures[WLAN_MAX_AC]; + uint32_t num_rts_fail; + uint32_t num_rts_success; + uint32_t num_rx_err; + uint32_t num_rx_discard; + uint32_t num_tx_not_acked; + uint32_t tx_rate_history[MAX_TX_RATE_VALUES]; + uint32_t beacon_rssi_history[MAX_TX_RATE_VALUES]; +} __packed; + +struct wmi_bcn_stats { + uint32_t vdev_id; + uint32_t tx_bcn_succ_cnt; + uint32_t tx_bcn_outage_cnt; +} __packed; + +struct wmi_stats_event { + uint32_t stats_id; + uint32_t num_pdev_stats; + uint32_t num_vdev_stats; + uint32_t num_peer_stats; + uint32_t num_bcnflt_stats; + uint32_t num_chan_stats; + uint32_t num_mib_stats; + uint32_t pdev_id; + uint32_t num_bcn_stats; + uint32_t num_peer_extd_stats; + uint32_t num_peer_extd2_stats; +} __packed; + +struct wmi_rssi_stats { + uint32_t vdev_id; + uint32_t rssi_avg_beacon[WMI_MAX_CHAINS]; + uint32_t rssi_avg_data[WMI_MAX_CHAINS]; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_per_chain_rssi_stats { + uint32_t num_per_chain_rssi_stats; +} __packed; + +struct wmi_pdev_ctl_failsafe_chk_event { + uint32_t pdev_id; + uint32_t ctl_failsafe_status; +} __packed; + +struct wmi_pdev_csa_switch_ev { + uint32_t pdev_id; + uint32_t current_switch_count; + uint32_t num_vdevs; +} __packed; + +struct wmi_pdev_radar_ev { + uint32_t pdev_id; + uint32_t detection_mode; + uint32_t chan_freq; + uint32_t chan_width; + uint32_t detector_id; + uint32_t segment_id; + uint32_t timestamp; + uint32_t is_chirp; + int32_t freq_offset; + int32_t sidx; +} __packed; + +struct wmi_pdev_temperature_event { + /* temperature value in Celsius degree */ + int32_t temp; + uint32_t pdev_id; +} __packed; + +#define WMI_RX_STATUS_OK 0x00 +#define WMI_RX_STATUS_ERR_CRC 0x01 +#define WMI_RX_STATUS_ERR_DECRYPT 0x08 +#define WMI_RX_STATUS_ERR_MIC 0x10 +#define WMI_RX_STATUS_ERR_KEY_CACHE_MISS 0x20 + +#define WLAN_MGMT_TXRX_HOST_MAX_ANTENNA 4 + +struct mgmt_rx_event_params { + uint32_t chan_freq; + uint32_t channel; + uint32_t snr; + uint8_t rssi_ctl[WLAN_MGMT_TXRX_HOST_MAX_ANTENNA]; + uint32_t rate; + enum wmi_phy_mode phy_mode; + uint32_t buf_len; + int status; + uint32_t flags; + int rssi; + uint32_t tsf_delta; + uint8_t pdev_id; +}; + +#define ATH_MAX_ANTENNA 4 + +struct wmi_mgmt_rx_hdr { + uint32_t channel; + uint32_t snr; + uint32_t rate; + uint32_t phy_mode; + uint32_t buf_len; + uint32_t status; + uint32_t rssi_ctl[ATH_MAX_ANTENNA]; + uint32_t flags; + int rssi; + uint32_t tsf_delta; + uint32_t rx_tsf_l32; + uint32_t rx_tsf_u32; + uint32_t pdev_id; + uint32_t chan_freq; +} __packed; + +#define MAX_ANTENNA_EIGHT 8 + +struct wmi_rssi_ctl_ext { + uint32_t tlv_header; + uint32_t rssi_ctl_ext[MAX_ANTENNA_EIGHT - ATH_MAX_ANTENNA]; +}; + +struct wmi_mgmt_tx_compl_event { + uint32_t desc_id; + uint32_t status; + uint32_t pdev_id; + uint32_t ppdu_id; + uint32_t ack_rssi; +} __packed; + +struct wmi_scan_event { + uint32_t event_type; /* %WMI_SCAN_EVENT_ */ + uint32_t reason; /* %WMI_SCAN_REASON_ */ + uint32_t channel_freq; /* only valid for WMI_SCAN_EVENT_FOREIGN_CHANNEL */ + uint32_t scan_req_id; + uint32_t scan_id; + uint32_t vdev_id; + /* TSF Timestamp when the scan event (%WMI_SCAN_EVENT_) is completed + * In case of AP it is TSF of the AP vdev + * In case of STA connected state, this is the TSF of the AP + * In case of STA not connected, it will be the free running HW timer + */ + uint32_t tsf_timestamp; +} __packed; + +struct wmi_peer_sta_kickout_arg { + const uint8_t *mac_addr; +}; + +struct wmi_peer_sta_kickout_event { + struct wmi_mac_addr peer_macaddr; +} __packed; + +enum wmi_roam_reason { + WMI_ROAM_REASON_BETTER_AP = 1, + WMI_ROAM_REASON_BEACON_MISS = 2, + WMI_ROAM_REASON_LOW_RSSI = 3, + WMI_ROAM_REASON_SUITABLE_AP_FOUND = 4, + WMI_ROAM_REASON_HO_FAILED = 5, + + /* keep last */ + WMI_ROAM_REASON_MAX, +}; + +struct wmi_roam_event { + uint32_t vdev_id; + uint32_t reason; + uint32_t rssi; +} __packed; + +#define WMI_CHAN_INFO_START_RESP 0 +#define WMI_CHAN_INFO_END_RESP 1 + +struct wmi_chan_info_event { + uint32_t err_code; + uint32_t freq; + uint32_t cmd_flags; + uint32_t noise_floor; + uint32_t rx_clear_count; + uint32_t cycle_count; + uint32_t chan_tx_pwr_range; + uint32_t chan_tx_pwr_tp; + uint32_t rx_frame_count; + uint32_t my_bss_rx_cycle_count; + uint32_t rx_11b_mode_data_duration; + uint32_t tx_frame_cnt; + uint32_t mac_clk_mhz; + uint32_t vdev_id; +} __packed; + +struct ath12k_targ_cap { + uint32_t phy_capability; + uint32_t max_frag_entry; + uint32_t num_rf_chains; + uint32_t ht_cap_info; + uint32_t vht_cap_info; + uint32_t vht_supp_mcs; + uint32_t hw_min_tx_power; + uint32_t hw_max_tx_power; + uint32_t sys_cap_info; + uint32_t min_pkt_size_enable; + uint32_t max_bcn_ie_size; + uint32_t max_num_scan_channels; + uint32_t max_supported_macs; + uint32_t wmi_fw_sub_feat_caps; + uint32_t txrx_chainmask; + uint32_t default_dbs_hw_mode_index; + uint32_t num_msdu_desc; +}; + +enum wmi_vdev_type { + WMI_VDEV_TYPE_AP = 1, + WMI_VDEV_TYPE_STA = 2, + WMI_VDEV_TYPE_IBSS = 3, + WMI_VDEV_TYPE_MONITOR = 4, +}; + +enum wmi_vdev_subtype { + WMI_VDEV_SUBTYPE_NONE, + WMI_VDEV_SUBTYPE_P2P_DEVICE, + WMI_VDEV_SUBTYPE_P2P_CLIENT, + WMI_VDEV_SUBTYPE_P2P_GO, + WMI_VDEV_SUBTYPE_PROXY_STA, + WMI_VDEV_SUBTYPE_MESH_NON_11S, + WMI_VDEV_SUBTYPE_MESH_11S, +}; + +enum wmi_sta_powersave_param { + WMI_STA_PS_PARAM_RX_WAKE_POLICY = 0, + WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD = 1, + WMI_STA_PS_PARAM_PSPOLL_COUNT = 2, + WMI_STA_PS_PARAM_INACTIVITY_TIME = 3, + WMI_STA_PS_PARAM_UAPSD = 4, +}; + +#define WMI_UAPSD_AC_TYPE_DELI 0 +#define WMI_UAPSD_AC_TYPE_TRIG 1 + +#define WMI_UAPSD_AC_BIT_MASK(ac, type) \ + ((type == WMI_UAPSD_AC_TYPE_DELI) ? \ + (1 << (ac << 1)) : (1 << ((ac << 1) + 1))) + +enum wmi_sta_ps_param_uapsd { + WMI_STA_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0), + WMI_STA_PS_UAPSD_AC0_TRIGGER_EN = (1 << 1), + WMI_STA_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2), + WMI_STA_PS_UAPSD_AC1_TRIGGER_EN = (1 << 3), + WMI_STA_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4), + WMI_STA_PS_UAPSD_AC2_TRIGGER_EN = (1 << 5), + WMI_STA_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6), + WMI_STA_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7), +}; + +#define WMI_STA_UAPSD_MAX_INTERVAL_MSEC UINT_MAX + +struct wmi_sta_uapsd_auto_trig_param { + uint32_t wmm_ac; + uint32_t user_priority; + uint32_t service_interval; + uint32_t suspend_interval; + uint32_t delay_interval; +}; + +struct wmi_sta_uapsd_auto_trig_cmd_fixed_param { + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t num_ac; +}; + +struct wmi_sta_uapsd_auto_trig_arg { + uint32_t wmm_ac; + uint32_t user_priority; + uint32_t service_interval; + uint32_t suspend_interval; + uint32_t delay_interval; +}; + +enum wmi_sta_ps_param_tx_wake_threshold { + WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER = 0, + WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS = 1, + + /* Values greater than one indicate that many TX attempts per beacon + * interval before the STA will wake up + */ +}; + +/* The maximum number of PS-Poll frames the FW will send in response to + * traffic advertised in TIM before waking up (by sending a null frame with PS + * = 0). Value 0 has a special meaning: there is no maximum count and the FW + * will send as many PS-Poll as are necessary to retrieve buffered BU. This + * parameter is used when the RX wake policy is + * WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD and ignored when the RX wake + * policy is WMI_STA_PS_RX_WAKE_POLICY_WAKE. + */ +enum wmi_sta_ps_param_pspoll_count { + WMI_STA_PS_PSPOLL_COUNT_NO_MAX = 0, + /* Values greater than 0 indicate the maximum number of PS-Poll frames + * FW will send before waking up. + */ +}; + +/* U-APSD configuration of peer station from (re)assoc request and TSPECs */ +enum wmi_ap_ps_param_uapsd { + WMI_AP_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0), + WMI_AP_PS_UAPSD_AC0_TRIGGER_EN = (1 << 1), + WMI_AP_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2), + WMI_AP_PS_UAPSD_AC1_TRIGGER_EN = (1 << 3), + WMI_AP_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4), + WMI_AP_PS_UAPSD_AC2_TRIGGER_EN = (1 << 5), + WMI_AP_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6), + WMI_AP_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7), +}; + +/* U-APSD maximum service period of peer station */ +enum wmi_ap_ps_peer_param_max_sp { + WMI_AP_PS_PEER_PARAM_MAX_SP_UNLIMITED = 0, + WMI_AP_PS_PEER_PARAM_MAX_SP_2 = 1, + WMI_AP_PS_PEER_PARAM_MAX_SP_4 = 2, + WMI_AP_PS_PEER_PARAM_MAX_SP_6 = 3, + MAX_WMI_AP_PS_PEER_PARAM_MAX_SP, +}; + +enum wmi_ap_ps_peer_param { + /** Set uapsd configuration for a given peer. + * + * This include the delivery and trigger enabled state for each AC. + * The host MLME needs to set this based on AP capability and stations + * request Set in the association request received from the station. + * + * Lower 8 bits of the value specify the UAPSD configuration. + * + * (see enum wmi_ap_ps_param_uapsd) + * The default value is 0. + */ + WMI_AP_PS_PEER_PARAM_UAPSD = 0, + + /** + * Set the service period for a UAPSD capable station + * + * The service period from wme ie in the (re)assoc request frame. + * + * (see enum wmi_ap_ps_peer_param_max_sp) + */ + WMI_AP_PS_PEER_PARAM_MAX_SP = 1, + + /** Time in seconds for aging out buffered frames + * for STA in power save + */ + WMI_AP_PS_PEER_PARAM_AGEOUT_TIME = 2, + + /** Specify frame types that are considered SIFS + * RESP trigger frame + */ + WMI_AP_PS_PEER_PARAM_SIFS_RESP_FRMTYPE = 3, + + /** Specifies the trigger state of TID. + * Valid only for UAPSD frame type + */ + WMI_AP_PS_PEER_PARAM_SIFS_RESP_UAPSD = 4, + + /* Specifies the WNM sleep state of a STA */ + WMI_AP_PS_PEER_PARAM_WNM_SLEEP = 5, +}; + +#define DISABLE_SIFS_RESPONSE_TRIGGER 0 + +#define WMI_MAX_KEY_INDEX 3 +#define WMI_MAX_KEY_LEN 32 + +#define WMI_KEY_PAIRWISE 0x00 +#define WMI_KEY_GROUP 0x01 + +#define WMI_CIPHER_NONE 0x0 /* clear key */ +#define WMI_CIPHER_WEP 0x1 +#define WMI_CIPHER_TKIP 0x2 +#define WMI_CIPHER_AES_OCB 0x3 +#define WMI_CIPHER_AES_CCM 0x4 +#define WMI_CIPHER_WAPI 0x5 +#define WMI_CIPHER_CKIP 0x6 +#define WMI_CIPHER_AES_CMAC 0x7 +#define WMI_CIPHER_ANY 0x8 +#define WMI_CIPHER_AES_GCM 0x9 +#define WMI_CIPHER_AES_GMAC 0xa + +/* Value to disable fixed rate setting */ +#define WMI_FIXED_RATE_NONE (0xffff) + +#define ATH12K_RC_VERSION_OFFSET 28 +#define ATH12K_RC_PREAMBLE_OFFSET 8 +#define ATH12K_RC_NSS_OFFSET 5 + +#define ATH12K_HW_RATE_CODE(rate, nss, preamble) \ + ((1 << ATH12K_RC_VERSION_OFFSET) | \ + ((nss) << ATH12K_RC_NSS_OFFSET) | \ + ((preamble) << ATH12K_RC_PREAMBLE_OFFSET) | \ + (rate)) + +/* Preamble types to be used with VDEV fixed rate configuration */ +enum wmi_rate_preamble { + WMI_RATE_PREAMBLE_OFDM, + WMI_RATE_PREAMBLE_CCK, + WMI_RATE_PREAMBLE_HT, + WMI_RATE_PREAMBLE_VHT, + WMI_RATE_PREAMBLE_HE, +}; + +/** + * enum wmi_rtscts_prot_mode - Enable/Disable RTS/CTS and CTS2Self Protection. + * @WMI_RTS_CTS_DISABLED: RTS/CTS protection is disabled. + * @WMI_USE_RTS_CTS: RTS/CTS Enabled. + * @WMI_USE_CTS2SELF: CTS to self protection Enabled. + */ +enum wmi_rtscts_prot_mode { + WMI_RTS_CTS_DISABLED = 0, + WMI_USE_RTS_CTS = 1, + WMI_USE_CTS2SELF = 2, +}; + +/** + * enum wmi_rtscts_profile - Selection of RTS CTS profile along with enabling + * protection mode. + * @WMI_RTSCTS_FOR_NO_RATESERIES: Neither of rate-series should use RTS-CTS + * @WMI_RTSCTS_FOR_SECOND_RATESERIES: Only second rate-series will use RTS-CTS + * @WMI_RTSCTS_ACROSS_SW_RETRIES: Only the second rate-series will use RTS-CTS, + * but if there's a sw retry, both the rate + * series will use RTS-CTS. + * @WMI_RTSCTS_ERP: RTS/CTS used for ERP protection for every PPDU. + * @WMI_RTSCTS_FOR_ALL_RATESERIES: Enable RTS-CTS for all rate series. + */ +enum wmi_rtscts_profile { + WMI_RTSCTS_FOR_NO_RATESERIES = 0, + WMI_RTSCTS_FOR_SECOND_RATESERIES = 1, + WMI_RTSCTS_ACROSS_SW_RETRIES = 2, + WMI_RTSCTS_ERP = 3, + WMI_RTSCTS_FOR_ALL_RATESERIES = 4, +}; + +struct ath12k_hal_reg_cap { + uint32_t eeprom_rd; + uint32_t eeprom_rd_ext; + uint32_t regcap1; + uint32_t regcap2; + uint32_t wireless_modes; + uint32_t low_2ghz_chan; + uint32_t high_2ghz_chan; + uint32_t low_5ghz_chan; + uint32_t high_5ghz_chan; +}; + +struct ath12k_mem_chunk { + void *vaddr; + bus_addr_t paddr; + uint32_t len; + uint32_t req_id; +}; + +enum wmi_sta_ps_param_rx_wake_policy { + WMI_STA_PS_RX_WAKE_POLICY_WAKE = 0, + WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD = 1, +}; + +/* Do not change existing values! Used by ath12k_frame_mode parameter + * module parameter. + */ +enum ath12k_hw_txrx_mode { + ATH12K_HW_TXRX_RAW = 0, + ATH12K_HW_TXRX_NATIVE_WIFI = 1, + ATH12K_HW_TXRX_ETHERNET = 2, +}; + +struct wmi_wmm_params { + uint32_t tlv_header; + uint32_t cwmin; + uint32_t cwmax; + uint32_t aifs; + uint32_t txoplimit; + uint32_t acm; + uint32_t no_ack; +} __packed; + +struct wmi_wmm_params_arg { + uint8_t acm; + uint8_t aifs; + uint16_t cwmin; + uint16_t cwmax; + uint16_t txop; + uint8_t no_ack; +}; + +struct wmi_vdev_set_wmm_params_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_wmm_params wmm_params[4]; + uint32_t wmm_param_type; +} __packed; + +struct wmi_wmm_params_all_arg { + struct wmi_wmm_params_arg ac_be; + struct wmi_wmm_params_arg ac_bk; + struct wmi_wmm_params_arg ac_vi; + struct wmi_wmm_params_arg ac_vo; +}; + +#define ATH12K_TWT_DEF_STA_CONG_TIMER_MS 5000 +#define ATH12K_TWT_DEF_DEFAULT_SLOT_SIZE 10 +#define ATH12K_TWT_DEF_CONGESTION_THRESH_SETUP 50 +#define ATH12K_TWT_DEF_CONGESTION_THRESH_TEARDOWN 20 +#define ATH12K_TWT_DEF_CONGESTION_THRESH_CRITICAL 100 +#define ATH12K_TWT_DEF_INTERFERENCE_THRESH_TEARDOWN 80 +#define ATH12K_TWT_DEF_INTERFERENCE_THRESH_SETUP 50 +#define ATH12K_TWT_DEF_MIN_NO_STA_SETUP 10 +#define ATH12K_TWT_DEF_MIN_NO_STA_TEARDOWN 2 +#define ATH12K_TWT_DEF_NO_OF_BCAST_MCAST_SLOTS 2 +#define ATH12K_TWT_DEF_MIN_NO_TWT_SLOTS 2 +#define ATH12K_TWT_DEF_MAX_NO_STA_TWT 500 +#define ATH12K_TWT_DEF_MODE_CHECK_INTERVAL 10000 +#define ATH12K_TWT_DEF_ADD_STA_SLOT_INTERVAL 1000 +#define ATH12K_TWT_DEF_REMOVE_STA_SLOT_INTERVAL 5000 + +struct wmi_twt_enable_params { + uint32_t sta_cong_timer_ms; + uint32_t mbss_support; + uint32_t default_slot_size; + uint32_t congestion_thresh_setup; + uint32_t congestion_thresh_teardown; + uint32_t congestion_thresh_critical; + uint32_t interference_thresh_teardown; + uint32_t interference_thresh_setup; + uint32_t min_no_sta_setup; + uint32_t min_no_sta_teardown; + uint32_t no_of_bcast_mcast_slots; + uint32_t min_no_twt_slots; + uint32_t max_no_sta_twt; + uint32_t mode_check_interval; + uint32_t add_sta_slot_interval; + uint32_t remove_sta_slot_interval; +}; + +struct wmi_twt_enable_params_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t sta_cong_timer_ms; + uint32_t mbss_support; + uint32_t default_slot_size; + uint32_t congestion_thresh_setup; + uint32_t congestion_thresh_teardown; + uint32_t congestion_thresh_critical; + uint32_t interference_thresh_teardown; + uint32_t interference_thresh_setup; + uint32_t min_no_sta_setup; + uint32_t min_no_sta_teardown; + uint32_t no_of_bcast_mcast_slots; + uint32_t min_no_twt_slots; + uint32_t max_no_sta_twt; + uint32_t mode_check_interval; + uint32_t add_sta_slot_interval; + uint32_t remove_sta_slot_interval; +} __packed; + +struct wmi_twt_disable_params_cmd { + uint32_t tlv_header; + uint32_t pdev_id; +} __packed; + +enum WMI_HOST_TWT_COMMAND { + WMI_HOST_TWT_COMMAND_REQUEST_TWT = 0, + WMI_HOST_TWT_COMMAND_SUGGEST_TWT, + WMI_HOST_TWT_COMMAND_DEMAND_TWT, + WMI_HOST_TWT_COMMAND_TWT_GROUPING, + WMI_HOST_TWT_COMMAND_ACCEPT_TWT, + WMI_HOST_TWT_COMMAND_ALTERNATE_TWT, + WMI_HOST_TWT_COMMAND_DICTATE_TWT, + WMI_HOST_TWT_COMMAND_REJECT_TWT, +}; + +#define WMI_TWT_ADD_DIALOG_FLAG_BCAST BIT(8) +#define WMI_TWT_ADD_DIALOG_FLAG_TRIGGER BIT(9) +#define WMI_TWT_ADD_DIALOG_FLAG_FLOW_TYPE BIT(10) +#define WMI_TWT_ADD_DIALOG_FLAG_PROTECTION BIT(11) + +struct wmi_twt_add_dialog_params_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t dialog_id; + uint32_t wake_intvl_us; + uint32_t wake_intvl_mantis; + uint32_t wake_dura_us; + uint32_t sp_offset_us; + uint32_t flags; +} __packed; + +struct wmi_twt_add_dialog_params { + uint32_t vdev_id; + uint8_t peer_macaddr[IEEE80211_ADDR_LEN]; + uint32_t dialog_id; + uint32_t wake_intvl_us; + uint32_t wake_intvl_mantis; + uint32_t wake_dura_us; + uint32_t sp_offset_us; + uint8_t twt_cmd; + uint8_t flag_bcast; + uint8_t flag_trigger; + uint8_t flag_flow_type; + uint8_t flag_protection; +} __packed; + +enum wmi_twt_add_dialog_status { + WMI_ADD_TWT_STATUS_OK, + WMI_ADD_TWT_STATUS_TWT_NOT_ENABLED, + WMI_ADD_TWT_STATUS_USED_DIALOG_ID, + WMI_ADD_TWT_STATUS_INVALID_PARAM, + WMI_ADD_TWT_STATUS_NOT_READY, + WMI_ADD_TWT_STATUS_NO_RESOURCE, + WMI_ADD_TWT_STATUS_NO_ACK, + WMI_ADD_TWT_STATUS_NO_RESPONSE, + WMI_ADD_TWT_STATUS_DENIED, + WMI_ADD_TWT_STATUS_UNKNOWN_ERROR, +}; + +struct wmi_twt_add_dialog_event { + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t dialog_id; + uint32_t status; +} __packed; + +struct wmi_twt_del_dialog_params { + uint32_t vdev_id; + uint8_t peer_macaddr[IEEE80211_ADDR_LEN]; + uint32_t dialog_id; +} __packed; + +struct wmi_twt_del_dialog_params_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t dialog_id; +} __packed; + +struct wmi_twt_pause_dialog_params { + uint32_t vdev_id; + uint8_t peer_macaddr[IEEE80211_ADDR_LEN]; + uint32_t dialog_id; +} __packed; + +struct wmi_twt_pause_dialog_params_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t dialog_id; +} __packed; + +struct wmi_twt_resume_dialog_params { + uint32_t vdev_id; + uint8_t peer_macaddr[IEEE80211_ADDR_LEN]; + uint32_t dialog_id; + uint32_t sp_offset_us; + uint32_t next_twt_size; +} __packed; + +struct wmi_twt_resume_dialog_params_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + struct wmi_mac_addr peer_macaddr; + uint32_t dialog_id; + uint32_t sp_offset_us; + uint32_t next_twt_size; +} __packed; + +struct wmi_obss_spatial_reuse_params_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t enable; + int32_t obss_min; + int32_t obss_max; + uint32_t vdev_id; +} __packed; + +struct wmi_pdev_obss_pd_bitmap_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t bitmap[2]; +} __packed; + +#define ATH12K_BSS_COLOR_COLLISION_SCAN_PERIOD_MS 200 +#define ATH12K_OBSS_COLOR_COLLISION_DETECTION_DISABLE 0 +#define ATH12K_OBSS_COLOR_COLLISION_DETECTION 1 + +#define ATH12K_BSS_COLOR_COLLISION_DETECTION_STA_PERIOD_MS 10000 +#define ATH12K_BSS_COLOR_COLLISION_DETECTION_AP_PERIOD_MS 5000 + +enum wmi_bss_color_collision { + WMI_BSS_COLOR_COLLISION_DISABLE = 0, + WMI_BSS_COLOR_COLLISION_DETECTION, + WMI_BSS_COLOR_FREE_SLOT_TIMER_EXPIRY, + WMI_BSS_COLOR_FREE_SLOT_AVAILABLE, +}; + +struct wmi_obss_color_collision_cfg_params_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t flags; + uint32_t evt_type; + uint32_t current_bss_color; + uint32_t detection_period_ms; + uint32_t scan_period_ms; + uint32_t free_slot_expiry_time_ms; +} __packed; + +struct wmi_bss_color_change_enable_params_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t enable; +} __packed; + +struct wmi_obss_color_collision_event { + uint32_t vdev_id; + uint32_t evt_type; + uint64_t obss_color_bitmap; +} __packed; + +#define ATH12K_IPV4_TH_SEED_SIZE 5 +#define ATH12K_IPV6_TH_SEED_SIZE 11 + +struct ath12k_wmi_pdev_lro_config_cmd { + uint32_t tlv_header; + uint32_t lro_enable; + uint32_t res; + uint32_t th_4[ATH12K_IPV4_TH_SEED_SIZE]; + uint32_t th_6[ATH12K_IPV6_TH_SEED_SIZE]; + uint32_t pdev_id; +} __packed; + +#define ATH12K_WMI_SPECTRAL_COUNT_DEFAULT 0 +#define ATH12K_WMI_SPECTRAL_PERIOD_DEFAULT 224 +#define ATH12K_WMI_SPECTRAL_PRIORITY_DEFAULT 1 +#define ATH12K_WMI_SPECTRAL_FFT_SIZE_DEFAULT 7 +#define ATH12K_WMI_SPECTRAL_GC_ENA_DEFAULT 1 +#define ATH12K_WMI_SPECTRAL_RESTART_ENA_DEFAULT 0 +#define ATH12K_WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT -96 +#define ATH12K_WMI_SPECTRAL_INIT_DELAY_DEFAULT 80 +#define ATH12K_WMI_SPECTRAL_NB_TONE_THR_DEFAULT 12 +#define ATH12K_WMI_SPECTRAL_STR_BIN_THR_DEFAULT 8 +#define ATH12K_WMI_SPECTRAL_WB_RPT_MODE_DEFAULT 0 +#define ATH12K_WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT 0 +#define ATH12K_WMI_SPECTRAL_RSSI_THR_DEFAULT 0xf0 +#define ATH12K_WMI_SPECTRAL_PWR_FORMAT_DEFAULT 0 +#define ATH12K_WMI_SPECTRAL_RPT_MODE_DEFAULT 2 +#define ATH12K_WMI_SPECTRAL_BIN_SCALE_DEFAULT 1 +#define ATH12K_WMI_SPECTRAL_DBM_ADJ_DEFAULT 1 +#define ATH12K_WMI_SPECTRAL_CHN_MASK_DEFAULT 1 + +struct ath12k_wmi_vdev_spectral_conf_param { + uint32_t vdev_id; + uint32_t scan_count; + uint32_t scan_period; + uint32_t scan_priority; + uint32_t scan_fft_size; + uint32_t scan_gc_ena; + uint32_t scan_restart_ena; + uint32_t scan_noise_floor_ref; + uint32_t scan_init_delay; + uint32_t scan_nb_tone_thr; + uint32_t scan_str_bin_thr; + uint32_t scan_wb_rpt_mode; + uint32_t scan_rssi_rpt_mode; + uint32_t scan_rssi_thr; + uint32_t scan_pwr_format; + uint32_t scan_rpt_mode; + uint32_t scan_bin_scale; + uint32_t scan_dbm_adj; + uint32_t scan_chn_mask; +} __packed; + +struct ath12k_wmi_vdev_spectral_conf_cmd { + uint32_t tlv_header; + struct ath12k_wmi_vdev_spectral_conf_param param; +} __packed; + +#define ATH12K_WMI_SPECTRAL_TRIGGER_CMD_TRIGGER 1 +#define ATH12K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR 2 +#define ATH12K_WMI_SPECTRAL_ENABLE_CMD_ENABLE 1 +#define ATH12K_WMI_SPECTRAL_ENABLE_CMD_DISABLE 2 + +struct ath12k_wmi_vdev_spectral_enable_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t trigger_cmd; + uint32_t enable_cmd; +} __packed; + +struct ath12k_wmi_pdev_dma_ring_cfg_req_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t module_id; /* see enum wmi_direct_buffer_module */ + uint32_t base_paddr_lo; + uint32_t base_paddr_hi; + uint32_t head_idx_paddr_lo; + uint32_t head_idx_paddr_hi; + uint32_t tail_idx_paddr_lo; + uint32_t tail_idx_paddr_hi; + uint32_t num_elems; /* Number of elems in the ring */ + uint32_t buf_size; /* size of allocated buffer in bytes */ + + /* Number of wmi_dma_buf_release_entry packed together */ + uint32_t num_resp_per_event; + + /* Target should timeout and send whatever resp + * it has if this time expires, units in milliseconds + */ + uint32_t event_timeout_ms; +} __packed; + +struct ath12k_wmi_dma_buf_release_fixed_param { + uint32_t pdev_id; + uint32_t module_id; + uint32_t num_buf_release_entry; + uint32_t num_meta_data_entry; +} __packed; + +struct wmi_dma_buf_release_entry { + uint32_t tlv_header; + uint32_t paddr_lo; + + /* Bits 11:0: address of data + * Bits 31:12: host context data + */ + uint32_t paddr_hi; +} __packed; + +#define WMI_SPECTRAL_META_INFO1_FREQ1 GENMASK(15, 0) +#define WMI_SPECTRAL_META_INFO1_FREQ2 GENMASK(31, 16) + +#define WMI_SPECTRAL_META_INFO2_CHN_WIDTH GENMASK(7, 0) + +struct wmi_dma_buf_release_meta_data { + uint32_t tlv_header; + int32_t noise_floor[WMI_MAX_CHAINS]; + uint32_t reset_delay; + uint32_t freq1; + uint32_t freq2; + uint32_t ch_width; +} __packed; + +enum wmi_fils_discovery_cmd_type { + WMI_FILS_DISCOVERY_CMD, + WMI_UNSOL_BCAST_PROBE_RESP, +}; + +struct wmi_fils_discovery_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t interval; + uint32_t config; /* enum wmi_fils_discovery_cmd_type */ +} __packed; + +struct wmi_fils_discovery_tmpl_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t buf_len; +} __packed; + +struct wmi_probe_tmpl_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t buf_len; +} __packed; + +struct target_resource_config { + uint32_t num_vdevs; + uint32_t num_peers; + uint32_t num_active_peers; + uint32_t num_offload_peers; + uint32_t num_offload_reorder_buffs; + uint32_t num_peer_keys; + uint32_t num_tids; + uint32_t ast_skid_limit; + uint32_t tx_chain_mask; + uint32_t rx_chain_mask; + uint32_t rx_timeout_pri[4]; + uint32_t rx_decap_mode; + uint32_t scan_max_pending_req; + uint32_t bmiss_offload_max_vdev; + uint32_t roam_offload_max_vdev; + uint32_t roam_offload_max_ap_profiles; + uint32_t num_mcast_groups; + uint32_t num_mcast_table_elems; + uint32_t mcast2ucast_mode; + uint32_t tx_dbg_log_size; + uint32_t num_wds_entries; + uint32_t dma_burst_size; + uint32_t mac_aggr_delim; + uint32_t rx_skip_defrag_timeout_dup_detection_check; + uint32_t vow_config; + uint32_t gtk_offload_max_vdev; + uint32_t num_msdu_desc; + uint32_t max_frag_entries; + uint32_t max_peer_ext_stats; + uint32_t smart_ant_cap; + uint32_t bk_minfree; + uint32_t be_minfree; + uint32_t vi_minfree; + uint32_t vo_minfree; + uint32_t rx_batchmode; + uint32_t tt_support; + uint32_t flag1; + uint32_t iphdr_pad_config; + uint32_t qwrap_config:16, + alloc_frag_desc_for_data_pkt:16; + uint32_t num_tdls_vdevs; + uint32_t num_tdls_conn_table_entries; + uint32_t beacon_tx_offload_max_vdev; + uint32_t num_multicast_filter_entries; + uint32_t num_wow_filters; + uint32_t num_keep_alive_pattern; + uint32_t keep_alive_pattern_size; + uint32_t max_tdls_concurrent_sleep_sta; + uint32_t max_tdls_concurrent_buffer_sta; + uint32_t wmi_send_separate; + uint32_t num_ocb_vdevs; + uint32_t num_ocb_channels; + uint32_t num_ocb_schedules; + uint32_t num_ns_ext_tuples_cfg; + uint32_t bpf_instruction_size; + uint32_t max_bssid_rx_filters; + uint32_t use_pdev_id; + uint32_t peer_map_unmap_v2_support; + uint32_t sched_params; + uint32_t twt_ap_pdev_count; + uint32_t twt_ap_sta_count; + uint8_t is_reg_cc_ext_event_supported; + uint32_t ema_max_vap_cnt; + uint32_t ema_max_profile_period; +}; + +enum wmi_debug_log_param { + WMI_DEBUG_LOG_PARAM_LOG_LEVEL = 0x1, + WMI_DEBUG_LOG_PARAM_VDEV_ENABLE, + WMI_DEBUG_LOG_PARAM_VDEV_DISABLE, + WMI_DEBUG_LOG_PARAM_VDEV_ENABLE_BITMAP, + WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP, + WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP, +}; + +struct wmi_debug_log_config_cmd_fixed_param { + uint32_t tlv_header; + uint32_t dbg_log_param; + uint32_t value; +} __packed; + +#define WMI_MAX_MEM_REQS 32 + +#define MAX_RADIOS 3 + +#define WMI_SERVICE_READY_TIMEOUT_HZ (5 * HZ) +#define WMI_SEND_TIMEOUT_HZ (3 * HZ) + +enum ath12k_wmi_peer_ps_state { + WMI_PEER_PS_STATE_OFF, + WMI_PEER_PS_STATE_ON, + WMI_PEER_PS_STATE_DISABLED, +}; + +enum wmi_peer_ps_supported_bitmap { + /* Used to indicate that power save state change is valid */ + WMI_PEER_PS_VALID = 0x1, + WMI_PEER_PS_STATE_TIMESTAMP = 0x2, +}; + +struct wmi_peer_sta_ps_state_chg_event { + struct wmi_mac_addr peer_macaddr; + uint32_t peer_ps_state; + uint32_t ps_supported_bitmap; + uint32_t peer_ps_valid; + uint32_t peer_ps_timestamp; +} __packed; + +/* Definition of HW data filtering */ +enum hw_data_filter_type { + WMI_HW_DATA_FILTER_DROP_NON_ARP_BC = BIT(0), + WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC = BIT(1), +}; + +struct wmi_hw_data_filter_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t enable; + uint32_t hw_filter_bitmap; +} __packed; + +/* WOW structures */ +enum wmi_wow_wakeup_event { + WOW_BMISS_EVENT = 0, + WOW_BETTER_AP_EVENT, + WOW_DEAUTH_RECVD_EVENT, + WOW_MAGIC_PKT_RECVD_EVENT, + WOW_GTK_ERR_EVENT, + WOW_FOURWAY_HSHAKE_EVENT, + WOW_EAPOL_RECVD_EVENT, + WOW_NLO_DETECTED_EVENT, + WOW_DISASSOC_RECVD_EVENT, + WOW_PATTERN_MATCH_EVENT, + WOW_CSA_IE_EVENT, + WOW_PROBE_REQ_WPS_IE_EVENT, + WOW_AUTH_REQ_EVENT, + WOW_ASSOC_REQ_EVENT, + WOW_HTT_EVENT, + WOW_RA_MATCH_EVENT, + WOW_HOST_AUTO_SHUTDOWN_EVENT, + WOW_IOAC_MAGIC_EVENT, + WOW_IOAC_SHORT_EVENT, + WOW_IOAC_EXTEND_EVENT, + WOW_IOAC_TIMER_EVENT, + WOW_DFS_PHYERR_RADAR_EVENT, + WOW_BEACON_EVENT, + WOW_CLIENT_KICKOUT_EVENT, + WOW_EVENT_MAX, +}; + +enum wmi_wow_interface_cfg { + WOW_IFACE_PAUSE_ENABLED, + WOW_IFACE_PAUSE_DISABLED +}; + +#define C2S(x) case x: return #x + +static inline const char *wow_wakeup_event(enum wmi_wow_wakeup_event ev) +{ + switch (ev) { + C2S(WOW_BMISS_EVENT); + C2S(WOW_BETTER_AP_EVENT); + C2S(WOW_DEAUTH_RECVD_EVENT); + C2S(WOW_MAGIC_PKT_RECVD_EVENT); + C2S(WOW_GTK_ERR_EVENT); + C2S(WOW_FOURWAY_HSHAKE_EVENT); + C2S(WOW_EAPOL_RECVD_EVENT); + C2S(WOW_NLO_DETECTED_EVENT); + C2S(WOW_DISASSOC_RECVD_EVENT); + C2S(WOW_PATTERN_MATCH_EVENT); + C2S(WOW_CSA_IE_EVENT); + C2S(WOW_PROBE_REQ_WPS_IE_EVENT); + C2S(WOW_AUTH_REQ_EVENT); + C2S(WOW_ASSOC_REQ_EVENT); + C2S(WOW_HTT_EVENT); + C2S(WOW_RA_MATCH_EVENT); + C2S(WOW_HOST_AUTO_SHUTDOWN_EVENT); + C2S(WOW_IOAC_MAGIC_EVENT); + C2S(WOW_IOAC_SHORT_EVENT); + C2S(WOW_IOAC_EXTEND_EVENT); + C2S(WOW_IOAC_TIMER_EVENT); + C2S(WOW_DFS_PHYERR_RADAR_EVENT); + C2S(WOW_BEACON_EVENT); + C2S(WOW_CLIENT_KICKOUT_EVENT); + C2S(WOW_EVENT_MAX); + default: + return NULL; + } +} + +enum wmi_wow_wake_reason { + WOW_REASON_UNSPECIFIED = -1, + WOW_REASON_NLOD = 0, + WOW_REASON_AP_ASSOC_LOST, + WOW_REASON_LOW_RSSI, + WOW_REASON_DEAUTH_RECVD, + WOW_REASON_DISASSOC_RECVD, + WOW_REASON_GTK_HS_ERR, + WOW_REASON_EAP_REQ, + WOW_REASON_FOURWAY_HS_RECV, + WOW_REASON_TIMER_INTR_RECV, + WOW_REASON_PATTERN_MATCH_FOUND, + WOW_REASON_RECV_MAGIC_PATTERN, + WOW_REASON_P2P_DISC, + WOW_REASON_WLAN_HB, + WOW_REASON_CSA_EVENT, + WOW_REASON_PROBE_REQ_WPS_IE_RECV, + WOW_REASON_AUTH_REQ_RECV, + WOW_REASON_ASSOC_REQ_RECV, + WOW_REASON_HTT_EVENT, + WOW_REASON_RA_MATCH, + WOW_REASON_HOST_AUTO_SHUTDOWN, + WOW_REASON_IOAC_MAGIC_EVENT, + WOW_REASON_IOAC_SHORT_EVENT, + WOW_REASON_IOAC_EXTEND_EVENT, + WOW_REASON_IOAC_TIMER_EVENT, + WOW_REASON_ROAM_HO, + WOW_REASON_DFS_PHYERR_RADADR_EVENT, + WOW_REASON_BEACON_RECV, + WOW_REASON_CLIENT_KICKOUT_EVENT, + WOW_REASON_PAGE_FAULT = 0x3a, + WOW_REASON_DEBUG_TEST = 0xFF, +}; + +static inline const char *wow_reason(enum wmi_wow_wake_reason reason) +{ + switch (reason) { + C2S(WOW_REASON_UNSPECIFIED); + C2S(WOW_REASON_NLOD); + C2S(WOW_REASON_AP_ASSOC_LOST); + C2S(WOW_REASON_LOW_RSSI); + C2S(WOW_REASON_DEAUTH_RECVD); + C2S(WOW_REASON_DISASSOC_RECVD); + C2S(WOW_REASON_GTK_HS_ERR); + C2S(WOW_REASON_EAP_REQ); + C2S(WOW_REASON_FOURWAY_HS_RECV); + C2S(WOW_REASON_TIMER_INTR_RECV); + C2S(WOW_REASON_PATTERN_MATCH_FOUND); + C2S(WOW_REASON_RECV_MAGIC_PATTERN); + C2S(WOW_REASON_P2P_DISC); + C2S(WOW_REASON_WLAN_HB); + C2S(WOW_REASON_CSA_EVENT); + C2S(WOW_REASON_PROBE_REQ_WPS_IE_RECV); + C2S(WOW_REASON_AUTH_REQ_RECV); + C2S(WOW_REASON_ASSOC_REQ_RECV); + C2S(WOW_REASON_HTT_EVENT); + C2S(WOW_REASON_RA_MATCH); + C2S(WOW_REASON_HOST_AUTO_SHUTDOWN); + C2S(WOW_REASON_IOAC_MAGIC_EVENT); + C2S(WOW_REASON_IOAC_SHORT_EVENT); + C2S(WOW_REASON_IOAC_EXTEND_EVENT); + C2S(WOW_REASON_IOAC_TIMER_EVENT); + C2S(WOW_REASON_ROAM_HO); + C2S(WOW_REASON_DFS_PHYERR_RADADR_EVENT); + C2S(WOW_REASON_BEACON_RECV); + C2S(WOW_REASON_CLIENT_KICKOUT_EVENT); + C2S(WOW_REASON_PAGE_FAULT); + C2S(WOW_REASON_DEBUG_TEST); + default: + return NULL; + } +} + +#undef C2S + +struct wmi_wow_ev_arg { + uint32_t vdev_id; + uint32_t flag; + enum wmi_wow_wake_reason wake_reason; + uint32_t data_len; +}; + +enum wmi_tlv_pattern_type { + WOW_PATTERN_MIN = 0, + WOW_BITMAP_PATTERN = WOW_PATTERN_MIN, + WOW_IPV4_SYNC_PATTERN, + WOW_IPV6_SYNC_PATTERN, + WOW_WILD_CARD_PATTERN, + WOW_TIMER_PATTERN, + WOW_MAGIC_PATTERN, + WOW_IPV6_RA_PATTERN, + WOW_IOAC_PKT_PATTERN, + WOW_IOAC_TMR_PATTERN, + WOW_PATTERN_MAX +}; + +#define WOW_DEFAULT_BITMAP_PATTERN_SIZE 148 +#define WOW_DEFAULT_BITMASK_SIZE 148 + +#define WOW_MIN_PATTERN_SIZE 1 +#define WOW_MAX_PATTERN_SIZE 148 +#define WOW_MAX_PKT_OFFSET 128 +#define WOW_HDR_LEN (sizeof(struct ieee80211_hdr_3addr) + \ + sizeof(struct rfc1042_hdr)) +#define WOW_MAX_REDUCE (WOW_HDR_LEN - sizeof(struct ethhdr) - \ + offsetof(struct ieee80211_hdr_3addr, addr1)) + +struct wmi_wow_add_del_event_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t is_add; + uint32_t event_bitmap; +} __packed; + +struct wmi_wow_enable_cmd { + uint32_t tlv_header; + uint32_t enable; + uint32_t pause_iface_config; + uint32_t flags; +} __packed; + +struct wmi_wow_host_wakeup_ind { + uint32_t tlv_header; + uint32_t reserved; +} __packed; + +struct wmi_tlv_wow_event_info { + uint32_t vdev_id; + uint32_t flag; + uint32_t wake_reason; + uint32_t data_len; +} __packed; + +struct wmi_wow_bitmap_pattern { + uint32_t tlv_header; + uint8_t patternbuf[WOW_DEFAULT_BITMAP_PATTERN_SIZE]; + uint8_t bitmaskbuf[WOW_DEFAULT_BITMASK_SIZE]; + uint32_t pattern_offset; + uint32_t pattern_len; + uint32_t bitmask_len; + uint32_t pattern_id; +} __packed; + +struct wmi_wow_add_pattern_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t pattern_id; + uint32_t pattern_type; +} __packed; + +struct wmi_wow_del_pattern_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t pattern_id; + uint32_t pattern_type; +} __packed; + +#define WMI_PNO_MAX_SCHED_SCAN_PLANS 2 +#define WMI_PNO_MAX_SCHED_SCAN_PLAN_INT 7200 +#define WMI_PNO_MAX_SCHED_SCAN_PLAN_ITRNS 100 +#define WMI_PNO_MAX_NETW_CHANNELS 26 +#define WMI_PNO_MAX_NETW_CHANNELS_EX 60 +#define WMI_PNO_MAX_SUPP_NETWORKS WLAN_SCAN_PARAMS_MAX_SSID +#define WMI_PNO_MAX_IE_LENGTH WLAN_SCAN_PARAMS_MAX_IE_LEN + +/* size based of dot11 declaration without extra IEs as we will not carry those for PNO */ +#define WMI_PNO_MAX_PB_REQ_SIZE 450 + +#define WMI_PNO_24G_DEFAULT_CH 1 +#define WMI_PNO_5G_DEFAULT_CH 36 + +#define WMI_ACTIVE_MAX_CHANNEL_TIME 40 +#define WMI_PASSIVE_MAX_CHANNEL_TIME 110 + +/* SSID broadcast type */ +enum wmi_ssid_bcast_type { + BCAST_UNKNOWN = 0, + BCAST_NORMAL = 1, + BCAST_HIDDEN = 2, +}; + +#define WMI_NLO_MAX_SSIDS 16 +#define WMI_NLO_MAX_CHAN 48 + +#define WMI_NLO_CONFIG_STOP BIT(0) +#define WMI_NLO_CONFIG_START BIT(1) +#define WMI_NLO_CONFIG_RESET BIT(2) +#define WMI_NLO_CONFIG_SLOW_SCAN BIT(4) +#define WMI_NLO_CONFIG_FAST_SCAN BIT(5) +#define WMI_NLO_CONFIG_SSID_HIDE_EN BIT(6) + +/* This bit is used to indicate if EPNO or supplicant PNO is enabled. + * Only one of them can be enabled at a given time + */ +#define WMI_NLO_CONFIG_ENLO BIT(7) +#define WMI_NLO_CONFIG_SCAN_PASSIVE BIT(8) +#define WMI_NLO_CONFIG_ENLO_RESET BIT(9) +#define WMI_NLO_CONFIG_SPOOFED_MAC_IN_PROBE_REQ BIT(10) +#define WMI_NLO_CONFIG_RANDOM_SEQ_NO_IN_PROBE_REQ BIT(11) +#define WMI_NLO_CONFIG_ENABLE_IE_WHITELIST_IN_PROBE_REQ BIT(12) +#define WMI_NLO_CONFIG_ENABLE_CNLO_RSSI_CONFIG BIT(13) + +struct wmi_nlo_ssid_param { + uint32_t valid; + struct wmi_ssid ssid; +} __packed; + +struct wmi_nlo_enc_param { + uint32_t valid; + uint32_t enc_type; +} __packed; + +struct wmi_nlo_auth_param { + uint32_t valid; + uint32_t auth_type; +} __packed; + +struct wmi_nlo_bcast_nw_param { + uint32_t valid; + uint32_t bcast_nw_type; +} __packed; + +struct wmi_nlo_rssi_param { + uint32_t valid; + int32_t rssi; +} __packed; + +struct nlo_configured_parameters { + /* TLV tag and len;*/ + uint32_t tlv_header; + struct wmi_nlo_ssid_param ssid; + struct wmi_nlo_enc_param enc_type; + struct wmi_nlo_auth_param auth_type; + struct wmi_nlo_rssi_param rssi_cond; + + /* indicates if the SSID is hidden or not */ + struct wmi_nlo_bcast_nw_param bcast_nw_type; +} __packed; + +struct wmi_network_type { + struct wmi_ssid ssid; + uint32_t authentication; + uint32_t encryption; + uint32_t bcast_nw_type; + uint8_t channel_count; + uint16_t channels[WMI_PNO_MAX_NETW_CHANNELS_EX]; + int32_t rssi_threshold; +}; + +struct wmi_pno_scan_req { + uint8_t enable; + uint8_t vdev_id; + uint8_t uc_networks_count; + struct wmi_network_type a_networks[WMI_PNO_MAX_SUPP_NETWORKS]; + uint32_t fast_scan_period; + uint32_t slow_scan_period; + uint8_t fast_scan_max_cycles; + + bool do_passive_scan; + + uint32_t delay_start_time; + uint32_t active_min_time; + uint32_t active_max_time; + uint32_t passive_min_time; + uint32_t passive_max_time; + + /* mac address randomization attributes */ + uint32_t enable_pno_scan_randomization; + uint8_t mac_addr[IEEE80211_ADDR_LEN]; + uint8_t mac_addr_mask[IEEE80211_ADDR_LEN]; +}; + +struct wmi_wow_nlo_config_cmd { + uint32_t tlv_header; + uint32_t flags; + uint32_t vdev_id; + uint32_t fast_scan_max_cycles; + uint32_t active_dwell_time; + uint32_t passive_dwell_time; + uint32_t probe_bundle_size; + + /* ART = IRT */ + uint32_t rest_time; + + /* Max value that can be reached after SBM */ + uint32_t max_rest_time; + + /* SBM */ + uint32_t scan_backoff_multiplier; + + /* SCBM */ + uint32_t fast_scan_period; + + /* specific to windows */ + uint32_t slow_scan_period; + + uint32_t no_of_ssids; + + uint32_t num_of_channels; + + /* NLO scan start delay time in milliseconds */ + uint32_t delay_start_time; + + /* MAC Address to use in Probe Req as SA */ + struct wmi_mac_addr mac_addr; + + /* Mask on which MAC has to be randomized */ + struct wmi_mac_addr mac_mask; + + /* IE bitmap to use in Probe Req */ + uint32_t ie_bitmap[8]; + + /* Number of vendor OUIs. In the TLV vendor_oui[] */ + uint32_t num_vendor_oui; + + /* Number of connected NLO band preferences */ + uint32_t num_cnlo_band_pref; + + /* The TLVs will follow. + * nlo_configured_parameters nlo_list[]; + * uint32_t channel_list[num_of_channels]; + */ +} __packed; + +#define WMI_MAX_NS_OFFLOADS 2 +#define WMI_MAX_ARP_OFFLOADS 2 + +#define WMI_ARPOL_FLAGS_VALID BIT(0) +#define WMI_ARPOL_FLAGS_MAC_VALID BIT(1) +#define WMI_ARPOL_FLAGS_REMOTE_IP_VALID BIT(2) + +struct wmi_arp_offload_tuple { + uint32_t tlv_header; + uint32_t flags; + uint8_t target_ipaddr[4]; + uint8_t remote_ipaddr[4]; + struct wmi_mac_addr target_mac; +} __packed; + +#define WMI_NSOL_FLAGS_VALID BIT(0) +#define WMI_NSOL_FLAGS_MAC_VALID BIT(1) +#define WMI_NSOL_FLAGS_REMOTE_IP_VALID BIT(2) +#define WMI_NSOL_FLAGS_IS_IPV6_ANYCAST BIT(3) + +#define WMI_NSOL_MAX_TARGET_IPS 2 + +struct wmi_ns_offload_tuple { + uint32_t tlv_header; + uint32_t flags; + uint8_t target_ipaddr[WMI_NSOL_MAX_TARGET_IPS][16]; + uint8_t solicitation_ipaddr[16]; + uint8_t remote_ipaddr[16]; + struct wmi_mac_addr target_mac; +} __packed; + +struct wmi_set_arp_ns_offload_cmd { + uint32_t tlv_header; + uint32_t flags; + uint32_t vdev_id; + uint32_t num_ns_ext_tuples; + /* The TLVs follow: + * wmi_ns_offload_tuple ns_tuples[WMI_MAX_NS_OFFLOADS]; + * wmi_arp_offload_tuple arp_tuples[WMI_MAX_ARP_OFFLOADS]; + * wmi_ns_offload_tuple ns_ext_tuples[num_ns_ext_tuples]; + */ +} __packed; + +#define GTK_OFFLOAD_OPCODE_MASK 0xFF000000 +#define GTK_OFFLOAD_ENABLE_OPCODE 0x01000000 +#define GTK_OFFLOAD_DISABLE_OPCODE 0x02000000 +#define GTK_OFFLOAD_REQUEST_STATUS_OPCODE 0x04000000 + +#define GTK_OFFLOAD_KEK_BYTES 16 +#define GTK_OFFLOAD_KCK_BYTES 16 +#define GTK_REPLAY_COUNTER_BYTES 8 +#define WMI_MAX_KEY_LEN 32 +#define IGTK_PN_SIZE 6 + +struct wmi_replayc_cnt { + union { + uint8_t counter[GTK_REPLAY_COUNTER_BYTES]; + struct { + uint32_t word0; + uint32_t word1; + } __packed; + } __packed; +} __packed; + +struct wmi_gtk_offload_status_event { + uint32_t vdev_id; + uint32_t flags; + uint32_t refresh_cnt; + struct wmi_replayc_cnt replay_ctr; + uint8_t igtk_key_index; + uint8_t igtk_key_length; + uint8_t igtk_key_rsc[IGTK_PN_SIZE]; + uint8_t igtk_key[WMI_MAX_KEY_LEN]; + uint8_t gtk_key_index; + uint8_t gtk_key_length; + uint8_t gtk_key_rsc[GTK_REPLAY_COUNTER_BYTES]; + uint8_t gtk_key[WMI_MAX_KEY_LEN]; +} __packed; + +struct wmi_gtk_rekey_offload_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t flags; + uint8_t kek[GTK_OFFLOAD_KEK_BYTES]; + uint8_t kck[GTK_OFFLOAD_KCK_BYTES]; + uint8_t replay_ctr[GTK_REPLAY_COUNTER_BYTES]; +} __packed; + +#define BIOS_SAR_TABLE_LEN (22) +#define BIOS_SAR_RSVD1_LEN (6) +#define BIOS_SAR_RSVD2_LEN (18) + +struct wmi_pdev_set_sar_table_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t sar_len; + uint32_t rsvd_len; +} __packed; + +struct wmi_pdev_set_geo_table_cmd { + uint32_t tlv_header; + uint32_t pdev_id; + uint32_t rsvd_len; +} __packed; + +struct wmi_sta_keepalive_cmd { + uint32_t tlv_header; + uint32_t vdev_id; + uint32_t enabled; + + /* WMI_STA_KEEPALIVE_METHOD_ */ + uint32_t method; + + /* in seconds */ + uint32_t interval; + + /* following this structure is the TLV for struct + * wmi_sta_keepalive_arp_resp + */ +} __packed; + +struct wmi_sta_keepalive_arp_resp { + uint32_t tlv_header; + uint32_t src_ip4_addr; + uint32_t dest_ip4_addr; + struct wmi_mac_addr dest_mac_addr; +} __packed; + +struct wmi_sta_keepalive_arg { + uint32_t vdev_id; + uint32_t enabled; + uint32_t method; + uint32_t interval; + uint32_t src_ip4_addr; + uint32_t dest_ip4_addr; + const uint8_t dest_mac_addr[IEEE80211_ADDR_LEN]; +}; + +enum wmi_sta_keepalive_method { + WMI_STA_KEEPALIVE_METHOD_NULL_FRAME = 1, + WMI_STA_KEEPALIVE_METHOD_UNSOLICITED_ARP_RESPONSE = 2, + WMI_STA_KEEPALIVE_METHOD_ETHERNET_LOOPBACK = 3, + WMI_STA_KEEPALIVE_METHOD_GRATUITOUS_ARP_REQUEST = 4, + WMI_STA_KEEPALIVE_METHOD_MGMT_VENDOR_ACTION = 5, +}; + +#define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30 +#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0 + + +/* + * qrtr.h + */ + +#define QRTR_PROTO_VER_1 1 +#define QRTR_PROTO_VER_2 3 /* (sic!) */ + +struct qrtr_hdr_v1 { + uint32_t version; + uint32_t type; + uint32_t src_node_id; + uint32_t src_port_id; + uint32_t confirm_rx; + uint32_t size; + uint32_t dst_node_id; + uint32_t dst_port_id; +} __packed; + +struct qrtr_hdr_v2 { + uint8_t version; + uint8_t type; + uint8_t flags; + uint8_t optlen; + uint32_t size; + uint16_t src_node_id; + uint16_t src_port_id; + uint16_t dst_node_id; + uint16_t dst_port_id; +}; + +struct qrtr_ctrl_pkt { + uint32_t cmd; + + union { + struct { + uint32_t service; + uint32_t instance; + uint32_t node; + uint32_t port; + } server; + struct { + uint32_t node; + uint32_t port; + } client; + }; +} __packed; + +#define QRTR_TYPE_DATA 1 +#define QRTR_TYPE_HELLO 2 +#define QRTR_TYPE_BYE 3 +#define QRTR_TYPE_NEW_SERVER 4 +#define QRTR_TYPE_DEL_SERVER 5 +#define QRTR_TYPE_DEL_CLIENT 6 +#define QRTR_TYPE_RESUME_TX 7 +#define QRTR_TYPE_EXIT 8 +#define QRTR_TYPE_PING 9 +#define QRTR_TYPE_NEW_LOOKUP 10 +#define QRTR_TYPE_DEL_LOOKUP 11 + +#define QRTR_FLAGS_CONFIRM_RX (1 << 0) + +#define QRTR_NODE_BCAST 0xffffffffU +#define QRTR_PORT_CTRL 0xfffffffeU + +/* + * qmi.h + */ + +#define QMI_REQUEST 0 +#define QMI_RESPONSE 2 +#define QMI_INDICATION 4 + +struct qmi_header { + uint8_t type; + uint16_t txn_id; + uint16_t msg_id; + uint16_t msg_len; +} __packed; + +#define QMI_COMMON_TLV_TYPE 0 + +enum qmi_elem_type { + QMI_EOTI, + QMI_OPT_FLAG, + QMI_DATA_LEN, + QMI_UNSIGNED_1_BYTE, + QMI_UNSIGNED_2_BYTE, + QMI_UNSIGNED_4_BYTE, + QMI_UNSIGNED_8_BYTE, + QMI_SIGNED_2_BYTE_ENUM, + QMI_SIGNED_4_BYTE_ENUM, + QMI_STRUCT, + QMI_STRING, + QMI_NUM_DATA_TYPES +}; + +enum qmi_array_type { + NO_ARRAY, + STATIC_ARRAY, + VAR_LEN_ARRAY, +}; + +struct qmi_elem_info { + enum qmi_elem_type data_type; + uint32_t elem_len; + uint32_t elem_size; + enum qmi_array_type array_type; + uint8_t tlv_type; + uint32_t offset; + const struct qmi_elem_info *ei_array; +}; + +#define QMI_RESULT_SUCCESS_V01 0 +#define QMI_RESULT_FAILURE_V01 1 + +#define QMI_ERR_NONE_V01 0 +#define QMI_ERR_MALFORMED_MSG_V01 1 +#define QMI_ERR_NO_MEMORY_V01 2 +#define QMI_ERR_INTERNAL_V01 3 +#define QMI_ERR_CLIENT_IDS_EXHAUSTED_V01 5 +#define QMI_ERR_INVALID_ID_V01 41 +#define QMI_ERR_ENCODING_V01 58 +#define QMI_ERR_DISABLED_V01 69 +#define QMI_ERR_INCOMPATIBLE_STATE_V01 90 +#define QMI_ERR_NOT_SUPPORTED_V01 94 + +struct qmi_response_type_v01 { + uint16_t result; + uint16_t error; +}; + +#define QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN 54 +#define QMI_WLANFW_IND_REGISTER_REQ_V01 0x0020 +#define QMI_WLANFW_IND_REGISTER_RESP_MSG_V01_MAX_LEN 18 +#define QMI_WLANFW_IND_REGISTER_RESP_V01 0x0020 +#define QMI_WLANFW_CLIENT_ID 0x4b4e454c + +struct qmi_wlanfw_ind_register_req_msg_v01 { + uint8_t fw_ready_enable_valid; + uint8_t fw_ready_enable; + uint8_t initiate_cal_download_enable_valid; + uint8_t initiate_cal_download_enable; + uint8_t initiate_cal_update_enable_valid; + uint8_t initiate_cal_update_enable; + uint8_t msa_ready_enable_valid; + uint8_t msa_ready_enable; + uint8_t pin_connect_result_enable_valid; + uint8_t pin_connect_result_enable; + uint8_t client_id_valid; + uint32_t client_id; + uint8_t request_mem_enable_valid; + uint8_t request_mem_enable; + uint8_t fw_mem_ready_enable_valid; + uint8_t fw_mem_ready_enable; + uint8_t fw_init_done_enable_valid; + uint8_t fw_init_done_enable; + uint8_t rejuvenate_enable_valid; + uint32_t rejuvenate_enable; + uint8_t xo_cal_enable_valid; + uint8_t xo_cal_enable; + uint8_t cal_done_enable_valid; + uint8_t cal_done_enable; +}; + +struct qmi_wlanfw_ind_register_resp_msg_v01 { + struct qmi_response_type_v01 resp; + uint8_t fw_status_valid; + uint64_t fw_status; +}; + +#define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN 261 +#define QMI_WLANFW_HOST_CAP_REQ_V01 0x0034 +#define QMI_WLANFW_HOST_CAP_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLFW_HOST_CAP_RESP_V01 0x0034 +#define QMI_WLFW_MAX_NUM_GPIO_V01 32 +#define QMI_IPQ8074_FW_MEM_MODE 0xFF +#define HOST_DDR_REGION_TYPE 0x1 +#define BDF_MEM_REGION_TYPE 0x2 +#define M3_DUMP_REGION_TYPE 0x3 +#define CALDB_MEM_REGION_TYPE 0x4 + +struct qmi_wlanfw_host_cap_req_msg_v01 { + uint8_t num_clients_valid; + uint32_t num_clients; + uint8_t wake_msi_valid; + uint32_t wake_msi; + uint8_t gpios_valid; + uint32_t gpios_len; + uint32_t gpios[QMI_WLFW_MAX_NUM_GPIO_V01]; + uint8_t nm_modem_valid; + uint8_t nm_modem; + uint8_t bdf_support_valid; + uint8_t bdf_support; + uint8_t bdf_cache_support_valid; + uint8_t bdf_cache_support; + uint8_t m3_support_valid; + uint8_t m3_support; + uint8_t m3_cache_support_valid; + uint8_t m3_cache_support; + uint8_t cal_filesys_support_valid; + uint8_t cal_filesys_support; + uint8_t cal_cache_support_valid; + uint8_t cal_cache_support; + uint8_t cal_done_valid; + uint8_t cal_done; + uint8_t mem_bucket_valid; + uint32_t mem_bucket; + uint8_t mem_cfg_mode_valid; + uint8_t mem_cfg_mode; +}; + +struct qmi_wlanfw_host_cap_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define ATH12K_HOST_VERSION_STRING "WIN" +#define ATH12K_QMI_WLANFW_TIMEOUT_MS 10000 +#define ATH12K_QMI_MAX_BDF_FILE_NAME_SIZE 64 +#define ATH12K_QMI_CALDB_ADDRESS 0x4BA00000 +#define ATH12K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128 +#define ATH12K_QMI_WLFW_SERVICE_ID_V01 0x45 +#define ATH12K_QMI_WLFW_SERVICE_VERS_V01 0x01 +#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01 0x02 +#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390 0x01 +#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074 0x02 +#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9074 0x07 +#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN6750 0x03 +#define ATH12K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32 + +#define ATH12K_QMI_RESP_LEN_MAX 8192 +#define ATH12K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52 +#define ATH12K_QMI_CALDB_SIZE 0x480000 +#define ATH12K_QMI_BDF_EXT_STR_LENGTH 0x20 +#define ATH12K_QMI_FW_MEM_REQ_SEGMENT_CNT 5 + +#define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035 +#define QMI_WLFW_RESPOND_MEM_RESP_V01 0x0036 +#define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037 +#define QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01 0x003E +#define QMI_WLFW_FW_READY_IND_V01 0x0021 +#define QMI_WLFW_FW_INIT_DONE_IND_V01 0x0038 + +#define QMI_WLANFW_MAX_DATA_SIZE_V01 6144 +#define ATH12K_FIRMWARE_MODE_OFF 4 +#define ATH12K_COLD_BOOT_FW_RESET_DELAY (40 * HZ) + +#define QMI_WLANFW_REQUEST_MEM_IND_MSG_V01_MAX_LEN 1824 +#define QMI_WLANFW_RESPOND_MEM_REQ_MSG_V01_MAX_LEN 888 +#define QMI_WLANFW_RESPOND_MEM_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_REQUEST_MEM_IND_V01 0x0035 +#define QMI_WLANFW_RESPOND_MEM_REQ_V01 0x0036 +#define QMI_WLANFW_RESPOND_MEM_RESP_V01 0x0036 +#define QMI_WLANFW_MAX_NUM_MEM_CFG_V01 2 + +struct qmi_wlanfw_mem_cfg_s_v01 { + uint64_t offset; + uint32_t size; + uint8_t secure_flag; +}; + +enum qmi_wlanfw_mem_type_enum_v01 { + WLANFW_MEM_TYPE_ENUM_MIN_VAL_V01 = INT_MIN, + QMI_WLANFW_MEM_TYPE_MSA_V01 = 0, + QMI_WLANFW_MEM_TYPE_DDR_V01 = 1, + QMI_WLANFW_MEM_BDF_V01 = 2, + QMI_WLANFW_MEM_M3_V01 = 3, + QMI_WLANFW_MEM_CAL_V01 = 4, + QMI_WLANFW_MEM_DPD_V01 = 5, + WLANFW_MEM_TYPE_ENUM_MAX_VAL_V01 = INT_MAX, +}; + +struct qmi_wlanfw_mem_seg_s_v01 { + uint32_t size; + enum qmi_wlanfw_mem_type_enum_v01 type; + uint32_t mem_cfg_len; + struct qmi_wlanfw_mem_cfg_s_v01 mem_cfg[QMI_WLANFW_MAX_NUM_MEM_CFG_V01]; +}; + +struct qmi_wlanfw_request_mem_ind_msg_v01 { + uint32_t mem_seg_len; + struct qmi_wlanfw_mem_seg_s_v01 mem_seg[ATH12K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; +}; + +struct qmi_wlanfw_mem_seg_resp_s_v01 { + uint64_t addr; + uint32_t size; + enum qmi_wlanfw_mem_type_enum_v01 type; + uint8_t restore; +}; + +struct qmi_wlanfw_respond_mem_req_msg_v01 { + uint32_t mem_seg_len; + struct qmi_wlanfw_mem_seg_resp_s_v01 mem_seg[ATH12K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; +}; + +struct qmi_wlanfw_respond_mem_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +struct qmi_wlanfw_fw_mem_ready_ind_msg_v01 { + char placeholder; +}; + +struct qmi_wlanfw_fw_ready_ind_msg_v01 { + char placeholder; +}; + +struct qmi_wlanfw_fw_cold_cal_done_ind_msg_v01 { + char placeholder; +}; + +struct qmi_wlfw_fw_init_done_ind_msg_v01 { + char placeholder; +}; + +#define QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN 0 +#define QMI_WLANFW_CAP_RESP_MSG_V01_MAX_LEN 235 +#define QMI_WLANFW_CAP_REQ_V01 0x0024 +#define QMI_WLANFW_CAP_RESP_V01 0x0024 +#define QMI_WLANFW_DEVICE_INFO_REQ_V01 0x004C +#define QMI_WLANFW_DEVICE_INFO_REQ_MSG_V01_MAX_LEN 0 + +enum qmi_wlanfw_pipedir_enum_v01 { + QMI_WLFW_PIPEDIR_NONE_V01 = 0, + QMI_WLFW_PIPEDIR_IN_V01 = 1, + QMI_WLFW_PIPEDIR_OUT_V01 = 2, + QMI_WLFW_PIPEDIR_INOUT_V01 = 3, +}; + +struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01 { + uint32_t pipe_num; + uint32_t pipe_dir; + uint32_t nentries; + uint32_t nbytes_max; + uint32_t flags; +}; + +struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01 { + uint32_t service_id; + uint32_t pipe_dir; + uint32_t pipe_num; +}; + +struct qmi_wlanfw_shadow_reg_cfg_s_v01 { + uint16_t id; + uint16_t offset; +}; + +struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01 { + uint32_t addr; +}; + +struct qmi_wlanfw_memory_region_info_s_v01 { + uint64_t region_addr; + uint32_t size; + uint8_t secure_flag; +}; + +struct qmi_wlanfw_rf_chip_info_s_v01 { + uint32_t chip_id; + uint32_t chip_family; +}; + +struct qmi_wlanfw_rf_board_info_s_v01 { + uint32_t board_id; +}; + +struct qmi_wlanfw_soc_info_s_v01 { + uint32_t soc_id; +}; + +struct qmi_wlanfw_fw_version_info_s_v01 { + uint32_t fw_version; + char fw_build_timestamp[ATH12K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1]; +}; + +enum qmi_wlanfw_cal_temp_id_enum_v01 { + QMI_WLANFW_CAL_TEMP_IDX_0_V01 = 0, + QMI_WLANFW_CAL_TEMP_IDX_1_V01 = 1, + QMI_WLANFW_CAL_TEMP_IDX_2_V01 = 2, + QMI_WLANFW_CAL_TEMP_IDX_3_V01 = 3, + QMI_WLANFW_CAL_TEMP_IDX_4_V01 = 4, + QMI_WLANFW_CAL_TEMP_ID_MAX_V01 = 0xFF, +}; + +struct qmi_wlanfw_cap_resp_msg_v01 { + struct qmi_response_type_v01 resp; + uint8_t chip_info_valid; + struct qmi_wlanfw_rf_chip_info_s_v01 chip_info; + uint8_t board_info_valid; + struct qmi_wlanfw_rf_board_info_s_v01 board_info; + uint8_t soc_info_valid; + struct qmi_wlanfw_soc_info_s_v01 soc_info; + uint8_t fw_version_info_valid; + struct qmi_wlanfw_fw_version_info_s_v01 fw_version_info; + uint8_t fw_build_id_valid; + char fw_build_id[ATH12K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1]; + uint8_t num_macs_valid; + uint8_t num_macs; + uint8_t voltage_mv_valid; + uint32_t voltage_mv; + uint8_t time_freq_hz_valid; + uint32_t time_freq_hz; + uint8_t otp_version_valid; + uint32_t otp_version; + uint8_t eeprom_read_timeout_valid; + uint32_t eeprom_read_timeout; +}; + +struct qmi_wlanfw_cap_req_msg_v01 { + char placeholder; +}; + +#define QMI_WLANFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_LEN 6182 +#define QMI_WLANFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_BDF_DOWNLOAD_RESP_V01 0x0025 +#define QMI_WLANFW_BDF_DOWNLOAD_REQ_V01 0x0025 +/* TODO: Need to check with MCL and FW team that data can be pointer and + * can be last element in structure + */ +struct qmi_wlanfw_bdf_download_req_msg_v01 { + uint8_t valid; + uint8_t file_id_valid; + enum qmi_wlanfw_cal_temp_id_enum_v01 file_id; + uint8_t total_size_valid; + uint32_t total_size; + uint8_t seg_id_valid; + uint32_t seg_id; + uint8_t data_valid; + uint32_t data_len; + uint8_t data[QMI_WLANFW_MAX_DATA_SIZE_V01]; + uint8_t end_valid; + uint8_t end; + uint8_t bdf_type_valid; + uint8_t bdf_type; +}; + +struct qmi_wlanfw_bdf_download_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define QMI_WLANFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN 18 +#define QMI_WLANFW_M3_INFO_RESP_MSG_V01_MAX_MSG_LEN 7 +#define QMI_WLANFW_M3_INFO_RESP_V01 0x003c +#define QMI_WLANFW_M3_INFO_REQ_V01 0x003c + +struct qmi_wlanfw_m3_info_req_msg_v01 { + uint64_t addr; + uint32_t size; +}; + +struct qmi_wlanfw_m3_info_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define QMI_WLANFW_WLAN_MODE_REQ_MSG_V01_MAX_LEN 11 +#define QMI_WLANFW_WLAN_MODE_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN 803 +#define QMI_WLANFW_WLAN_CFG_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_WLAN_INI_REQ_MSG_V01_MAX_LEN 4 +#define QMI_WLANFW_WLAN_MODE_REQ_V01 0x0022 +#define QMI_WLANFW_WLAN_MODE_RESP_V01 0x0022 +#define QMI_WLANFW_WLAN_CFG_REQ_V01 0x0023 +#define QMI_WLANFW_WLAN_CFG_RESP_V01 0x0023 +#define QMI_WLANFW_WLAN_INI_REQ_V01 0x002f +#define QMI_WLANFW_WLAN_INI_RESP_V01 0x002f +#define QMI_WLANFW_MAX_STR_LEN_V01 16 +#define QMI_WLANFW_MAX_NUM_CE_V01 12 +#define QMI_WLANFW_MAX_NUM_SVC_V01 24 +#define QMI_WLANFW_MAX_NUM_SHADOW_REG_V01 24 +#define QMI_WLANFW_MAX_NUM_SHADOW_REG_V2_V01 36 + +struct qmi_wlanfw_wlan_mode_req_msg_v01 { + uint32_t mode; + uint8_t hw_debug_valid; + uint8_t hw_debug; +}; + +struct qmi_wlanfw_wlan_mode_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +struct qmi_wlanfw_wlan_cfg_req_msg_v01 { + uint8_t host_version_valid; + char host_version[QMI_WLANFW_MAX_STR_LEN_V01 + 1]; + uint8_t tgt_cfg_valid; + uint32_t tgt_cfg_len; + struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01 + tgt_cfg[QMI_WLANFW_MAX_NUM_CE_V01]; + uint8_t svc_cfg_valid; + uint32_t svc_cfg_len; + struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01 + svc_cfg[QMI_WLANFW_MAX_NUM_SVC_V01]; + uint8_t shadow_reg_valid; + uint32_t shadow_reg_len; + struct qmi_wlanfw_shadow_reg_cfg_s_v01 + shadow_reg[QMI_WLANFW_MAX_NUM_SHADOW_REG_V01]; + uint8_t shadow_reg_v2_valid; + uint32_t shadow_reg_v2_len; + struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01 + shadow_reg_v2[QMI_WLANFW_MAX_NUM_SHADOW_REG_V2_V01]; +}; + +struct qmi_wlanfw_wlan_cfg_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +struct qmi_wlanfw_wlan_ini_req_msg_v01 { + /* Must be set to true if enablefwlog is being passed */ + uint8_t enablefwlog_valid; + uint8_t enablefwlog; +}; + +struct qmi_wlanfw_wlan_ini_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +enum ath12k_qmi_file_type { + ATH12K_QMI_FILE_TYPE_BDF_GOLDEN, + ATH12K_QMI_FILE_TYPE_CALDATA = 2, + ATH12K_QMI_FILE_TYPE_EEPROM, + ATH12K_QMI_MAX_FILE_TYPE, +}; + +enum ath12k_qmi_bdf_type { + ATH12K_QMI_BDF_TYPE_BIN = 0, + ATH12K_QMI_BDF_TYPE_ELF = 1, + ATH12K_QMI_BDF_TYPE_REGDB = 4, +}; + +#define HAL_LINK_DESC_SIZE (32 << 2) +#define HAL_LINK_DESC_ALIGN 128 +#define HAL_NUM_MPDUS_PER_LINK_DESC 6 +#define HAL_NUM_TX_MSDUS_PER_LINK_DESC 7 +#define HAL_NUM_RX_MSDUS_PER_LINK_DESC 6 +#define HAL_NUM_MPDU_LINKS_PER_QUEUE_DESC 12 +#define HAL_MAX_AVAIL_BLK_RES 3 + +#define HAL_RING_BASE_ALIGN 8 + +#define HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX 32704 +/* TODO: Check with hw team on the supported scatter buf size */ +#define HAL_WBM_IDLE_SCATTER_NEXT_PTR_SIZE 8 +#define HAL_WBM_IDLE_SCATTER_BUF_SIZE (HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX - \ + HAL_WBM_IDLE_SCATTER_NEXT_PTR_SIZE) + +#define HAL_DSCP_TID_MAP_TBL_NUM_ENTRIES_MAX 48 +#define HAL_DSCP_TID_TBL_SIZE 24 + +/* calculate the register address from bar0 of shadow register x */ +#define HAL_SHADOW_BASE_ADDR(sc) \ + (sc->hw_params.regs->hal_shadow_base_addr) +#define HAL_SHADOW_NUM_REGS 36 +#define HAL_HP_OFFSET_IN_REG_START 1 +#define HAL_OFFSET_FROM_HP_TO_TP 4 + +#define HAL_SHADOW_REG(sc, x) (HAL_SHADOW_BASE_ADDR(sc) + (4 * (x))) + +enum hal_srng_ring_id { + HAL_SRNG_RING_ID_REO2SW1 = 0, + HAL_SRNG_RING_ID_REO2SW2, + HAL_SRNG_RING_ID_REO2SW3, + HAL_SRNG_RING_ID_REO2SW4, + HAL_SRNG_RING_ID_REO2TCL, + HAL_SRNG_RING_ID_SW2REO, + + HAL_SRNG_RING_ID_REO_CMD = 8, + HAL_SRNG_RING_ID_REO_STATUS, + + HAL_SRNG_RING_ID_SW2TCL1 = 16, + HAL_SRNG_RING_ID_SW2TCL2, + HAL_SRNG_RING_ID_SW2TCL3, + HAL_SRNG_RING_ID_SW2TCL4, + + HAL_SRNG_RING_ID_SW2TCL_CMD = 24, + HAL_SRNG_RING_ID_TCL_STATUS, + + HAL_SRNG_RING_ID_CE0_SRC = 32, + HAL_SRNG_RING_ID_CE1_SRC, + HAL_SRNG_RING_ID_CE2_SRC, + HAL_SRNG_RING_ID_CE3_SRC, + HAL_SRNG_RING_ID_CE4_SRC, + HAL_SRNG_RING_ID_CE5_SRC, + HAL_SRNG_RING_ID_CE6_SRC, + HAL_SRNG_RING_ID_CE7_SRC, + HAL_SRNG_RING_ID_CE8_SRC, + HAL_SRNG_RING_ID_CE9_SRC, + HAL_SRNG_RING_ID_CE10_SRC, + HAL_SRNG_RING_ID_CE11_SRC, + + HAL_SRNG_RING_ID_CE0_DST = 56, + HAL_SRNG_RING_ID_CE1_DST, + HAL_SRNG_RING_ID_CE2_DST, + HAL_SRNG_RING_ID_CE3_DST, + HAL_SRNG_RING_ID_CE4_DST, + HAL_SRNG_RING_ID_CE5_DST, + HAL_SRNG_RING_ID_CE6_DST, + HAL_SRNG_RING_ID_CE7_DST, + HAL_SRNG_RING_ID_CE8_DST, + HAL_SRNG_RING_ID_CE9_DST, + HAL_SRNG_RING_ID_CE10_DST, + HAL_SRNG_RING_ID_CE11_DST, + + HAL_SRNG_RING_ID_CE0_DST_STATUS = 80, + HAL_SRNG_RING_ID_CE1_DST_STATUS, + HAL_SRNG_RING_ID_CE2_DST_STATUS, + HAL_SRNG_RING_ID_CE3_DST_STATUS, + HAL_SRNG_RING_ID_CE4_DST_STATUS, + HAL_SRNG_RING_ID_CE5_DST_STATUS, + HAL_SRNG_RING_ID_CE6_DST_STATUS, + HAL_SRNG_RING_ID_CE7_DST_STATUS, + HAL_SRNG_RING_ID_CE8_DST_STATUS, + HAL_SRNG_RING_ID_CE9_DST_STATUS, + HAL_SRNG_RING_ID_CE10_DST_STATUS, + HAL_SRNG_RING_ID_CE11_DST_STATUS, + + HAL_SRNG_RING_ID_WBM_IDLE_LINK = 104, + HAL_SRNG_RING_ID_WBM_SW_RELEASE, + HAL_SRNG_RING_ID_WBM2SW0_RELEASE, + HAL_SRNG_RING_ID_WBM2SW1_RELEASE, + HAL_SRNG_RING_ID_WBM2SW2_RELEASE, + HAL_SRNG_RING_ID_WBM2SW3_RELEASE, + HAL_SRNG_RING_ID_WBM2SW4_RELEASE, + + HAL_SRNG_RING_ID_UMAC_ID_END = 127, + HAL_SRNG_RING_ID_LMAC1_ID_START, + + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF = HAL_SRNG_RING_ID_LMAC1_ID_START, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_BUF, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA2_BUF, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_STATBUF, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF, + HAL_SRNG_RING_ID_WMAC1_RXDMA2SW0, + HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_DESC, + HAL_SRNG_RING_ID_RXDMA_DIR_BUF, + + HAL_SRNG_RING_ID_LMAC1_ID_END = 143 +}; + +/* SRNG registers are split into two groups R0 and R2 */ +#define HAL_SRNG_REG_GRP_R0 0 +#define HAL_SRNG_REG_GRP_R2 1 +#define HAL_SRNG_NUM_REG_GRP 2 + +#define HAL_SRNG_NUM_LMACS 3 +#define HAL_SRNG_REO_EXCEPTION HAL_SRNG_RING_ID_REO2SW1 +#define HAL_SRNG_RINGS_PER_LMAC (HAL_SRNG_RING_ID_LMAC1_ID_END - \ + HAL_SRNG_RING_ID_LMAC1_ID_START) +#define HAL_SRNG_NUM_LMAC_RINGS (HAL_SRNG_NUM_LMACS * HAL_SRNG_RINGS_PER_LMAC) +#define HAL_SRNG_RING_ID_MAX (HAL_SRNG_RING_ID_UMAC_ID_END + \ + HAL_SRNG_NUM_LMAC_RINGS) + +#define HAL_RX_MAX_BA_WINDOW 256 + +#define HAL_DEFAULT_REO_TIMEOUT_USEC (40 * 1000) + +/** + * enum hal_reo_cmd_type: Enum for REO command type + * @HAL_REO_CMD_GET_QUEUE_STATS: Get REO queue status/stats + * @HAL_REO_CMD_FLUSH_QUEUE: Flush all frames in REO queue + * @HAL_REO_CMD_FLUSH_CACHE: Flush descriptor entries in the cache + * @HAL_REO_CMD_UNBLOCK_CACHE: Unblock a descriptor's address that was blocked + * earlier with a 'REO_FLUSH_CACHE' command + * @HAL_REO_CMD_FLUSH_TIMEOUT_LIST: Flush buffers/descriptors from timeout list + * @HAL_REO_CMD_UPDATE_RX_QUEUE: Update REO queue settings + */ +enum hal_reo_cmd_type { + HAL_REO_CMD_GET_QUEUE_STATS = 0, + HAL_REO_CMD_FLUSH_QUEUE = 1, + HAL_REO_CMD_FLUSH_CACHE = 2, + HAL_REO_CMD_UNBLOCK_CACHE = 3, + HAL_REO_CMD_FLUSH_TIMEOUT_LIST = 4, + HAL_REO_CMD_UPDATE_RX_QUEUE = 5, +}; + +/** + * enum hal_reo_cmd_status: Enum for execution status of REO command + * @HAL_REO_CMD_SUCCESS: Command has successfully executed + * @HAL_REO_CMD_BLOCKED: Command could not be executed as the queue + * or cache was blocked + * @HAL_REO_CMD_FAILED: Command execution failed, could be due to + * invalid queue desc + * @HAL_REO_CMD_RESOURCE_BLOCKED: + * @HAL_REO_CMD_DRAIN: + */ +enum hal_reo_cmd_status { + HAL_REO_CMD_SUCCESS = 0, + HAL_REO_CMD_BLOCKED = 1, + HAL_REO_CMD_FAILED = 2, + HAL_REO_CMD_RESOURCE_BLOCKED = 3, + HAL_REO_CMD_DRAIN = 0xff, +}; + +/* Interrupt mitigation - Batch threshold in terms of number of frames */ +#define HAL_SRNG_INT_BATCH_THRESHOLD_TX 256 +#define HAL_SRNG_INT_BATCH_THRESHOLD_RX 128 +#define HAL_SRNG_INT_BATCH_THRESHOLD_OTHER 1 + +/* Interrupt mitigation - timer threshold in us */ +#define HAL_SRNG_INT_TIMER_THRESHOLD_TX 1000 +#define HAL_SRNG_INT_TIMER_THRESHOLD_RX 500 +#define HAL_SRNG_INT_TIMER_THRESHOLD_OTHER 256 + +/* WCSS Relative address */ +#define HAL_SEQ_WCSS_UMAC_OFFSET 0x00a00000 +#define HAL_SEQ_WCSS_UMAC_REO_REG 0x00a38000 +#define HAL_SEQ_WCSS_UMAC_TCL_REG 0x00a44000 +#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(sc) \ + (sc->hw_params.regs->hal_seq_wcss_umac_ce0_src_reg) +#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG(sc) \ + (sc->hw_params.regs->hal_seq_wcss_umac_ce0_dst_reg) +#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(sc) \ + (sc->hw_params.regs->hal_seq_wcss_umac_ce1_src_reg) +#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG(sc) \ + (sc->hw_params.regs->hal_seq_wcss_umac_ce1_dst_reg) +#define HAL_SEQ_WCSS_UMAC_WBM_REG 0x00a34000 + +#define HAL_CE_WFSS_CE_REG_BASE 0x01b80000 +#define HAL_WLAON_REG_BASE 0x01f80000 + +/* SW2TCL(x) R0 ring configuration address */ +#define HAL_TCL1_RING_CMN_CTRL_REG 0x00000014 +#define HAL_TCL1_RING_DSCP_TID_MAP 0x0000002c +#define HAL_TCL1_RING_BASE_LSB(sc) \ + (sc->hw_params.regs->hal_tcl1_ring_base_lsb) +#define HAL_TCL1_RING_BASE_MSB(sc) \ + (sc->hw_params.regs->hal_tcl1_ring_base_msb) +#define HAL_TCL1_RING_ID(sc) \ + (sc->hw_params.regs->hal_tcl1_ring_id) +#define HAL_TCL1_RING_MISC(sc) \ + (sc->hw_params.regs->hal_tcl1_ring_misc) +#define HAL_TCL1_RING_TP_ADDR_LSB(sc) \ + (sc->hw_params.regs->hal_tcl1_ring_tp_addr_lsb) +#define HAL_TCL1_RING_TP_ADDR_MSB(sc) \ + (sc->hw_params.regs->hal_tcl1_ring_tp_addr_msb) +#define HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0(sc) \ + (sc->hw_params.regs->hal_tcl1_ring_consumer_int_setup_ix0) +#define HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1(sc) \ + (sc->hw_params.regs->hal_tcl1_ring_consumer_int_setup_ix1) +#define HAL_TCL1_RING_MSI1_BASE_LSB(sc) \ + (sc->hw_params.regs->hal_tcl1_ring_msi1_base_lsb) +#define HAL_TCL1_RING_MSI1_BASE_MSB(sc) \ + (sc->hw_params.regs->hal_tcl1_ring_msi1_base_msb) +#define HAL_TCL1_RING_MSI1_DATA(sc) \ + (sc->hw_params.regs->hal_tcl1_ring_msi1_data) +#define HAL_TCL2_RING_BASE_LSB(sc) \ + (sc->hw_params.regs->hal_tcl2_ring_base_lsb) +#define HAL_TCL_RING_BASE_LSB(sc) \ + (sc->hw_params.regs->hal_tcl_ring_base_lsb) + +#define HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET(sc) \ + (HAL_TCL1_RING_MSI1_BASE_LSB(sc) - HAL_TCL1_RING_BASE_LSB(sc)) +#define HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET(sc) \ + (HAL_TCL1_RING_MSI1_BASE_MSB(sc) - HAL_TCL1_RING_BASE_LSB(sc)) +#define HAL_TCL1_RING_MSI1_DATA_OFFSET(sc) \ + (HAL_TCL1_RING_MSI1_DATA(sc) - HAL_TCL1_RING_BASE_LSB(sc)) +#define HAL_TCL1_RING_BASE_MSB_OFFSET(sc) \ + (HAL_TCL1_RING_BASE_MSB(sc) - HAL_TCL1_RING_BASE_LSB(sc)) +#define HAL_TCL1_RING_ID_OFFSET(sc) \ + (HAL_TCL1_RING_ID(sc) - HAL_TCL1_RING_BASE_LSB(sc)) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET(sc) \ + (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0(sc) - HAL_TCL1_RING_BASE_LSB(sc)) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET(sc) \ + (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1(sc) - \ + HAL_TCL1_RING_BASE_LSB(sc)) +#define HAL_TCL1_RING_TP_ADDR_LSB_OFFSET(sc) \ + (HAL_TCL1_RING_TP_ADDR_LSB(sc) - HAL_TCL1_RING_BASE_LSB(sc)) +#define HAL_TCL1_RING_TP_ADDR_MSB_OFFSET(sc) \ + (HAL_TCL1_RING_TP_ADDR_MSB(sc) - HAL_TCL1_RING_BASE_LSB(sc)) +#define HAL_TCL1_RING_MISC_OFFSET(sc) \ + (HAL_TCL1_RING_MISC(sc) - HAL_TCL1_RING_BASE_LSB(sc)) + +/* SW2TCL(x) R2 ring pointers (head/tail) address */ +#define HAL_TCL1_RING_HP 0x00002000 +#define HAL_TCL1_RING_TP 0x00002004 +#define HAL_TCL2_RING_HP 0x00002008 +#define HAL_TCL_RING_HP 0x00002018 + +#define HAL_TCL1_RING_TP_OFFSET \ + (HAL_TCL1_RING_TP - HAL_TCL1_RING_HP) + +/* TCL STATUS ring address */ +#define HAL_TCL_STATUS_RING_BASE_LSB(sc) \ + (sc->hw_params.regs->hal_tcl_status_ring_base_lsb) +#define HAL_TCL_STATUS_RING_HP 0x00002030 + +/* REO2SW(x) R0 ring configuration address */ +#define HAL_REO1_GEN_ENABLE 0x00000000 +#define HAL_REO1_DEST_RING_CTRL_IX_0 0x00000004 +#define HAL_REO1_DEST_RING_CTRL_IX_1 0x00000008 +#define HAL_REO1_DEST_RING_CTRL_IX_2 0x0000000c +#define HAL_REO1_DEST_RING_CTRL_IX_3 0x00000010 +#define HAL_REO1_MISC_CTL(sc) \ + (sc->hw_params.regs->hal_reo1_misc_ctl) +#define HAL_REO1_RING_BASE_LSB(sc) \ + (sc->hw_params.regs->hal_reo1_ring_base_lsb) +#define HAL_REO1_RING_BASE_MSB(sc) \ + (sc->hw_params.regs->hal_reo1_ring_base_msb) +#define HAL_REO1_RING_ID(sc) \ + (sc->hw_params.regs->hal_reo1_ring_id) +#define HAL_REO1_RING_MISC(sc) \ + (sc->hw_params.regs->hal_reo1_ring_misc) +#define HAL_REO1_RING_HP_ADDR_LSB(sc) \ + (sc->hw_params.regs->hal_reo1_ring_hp_addr_lsb) +#define HAL_REO1_RING_HP_ADDR_MSB(sc) \ + (sc->hw_params.regs->hal_reo1_ring_hp_addr_msb) +#define HAL_REO1_RING_PRODUCER_INT_SETUP(sc) \ + (sc->hw_params.regs->hal_reo1_ring_producer_int_setup) +#define HAL_REO1_RING_MSI1_BASE_LSB(sc) \ + (sc->hw_params.regs->hal_reo1_ring_msi1_base_lsb) +#define HAL_REO1_RING_MSI1_BASE_MSB(sc) \ + (sc->hw_params.regs->hal_reo1_ring_msi1_base_msb) +#define HAL_REO1_RING_MSI1_DATA(sc) \ + (sc->hw_params.regs->hal_reo1_ring_msi1_data) +#define HAL_REO2_RING_BASE_LSB(sc) \ + (sc->hw_params.regs->hal_reo2_ring_base_lsb) +#define HAL_REO1_AGING_THRESH_IX_0(sc) \ + (sc->hw_params.regs->hal_reo1_aging_thresh_ix_0) +#define HAL_REO1_AGING_THRESH_IX_1(sc) \ + (sc->hw_params.regs->hal_reo1_aging_thresh_ix_1) +#define HAL_REO1_AGING_THRESH_IX_2(sc) \ + (sc->hw_params.regs->hal_reo1_aging_thresh_ix_2) +#define HAL_REO1_AGING_THRESH_IX_3(sc) \ + (sc->hw_params.regs->hal_reo1_aging_thresh_ix_3) + +#define HAL_REO1_RING_MSI1_BASE_LSB_OFFSET(sc) \ + (HAL_REO1_RING_MSI1_BASE_LSB(sc) - HAL_REO1_RING_BASE_LSB(sc)) +#define HAL_REO1_RING_MSI1_BASE_MSB_OFFSET(sc) \ + (HAL_REO1_RING_MSI1_BASE_MSB(sc) - HAL_REO1_RING_BASE_LSB(sc)) +#define HAL_REO1_RING_MSI1_DATA_OFFSET(sc) \ + (HAL_REO1_RING_MSI1_DATA(sc) - HAL_REO1_RING_BASE_LSB(sc)) +#define HAL_REO1_RING_BASE_MSB_OFFSET(sc) \ + (HAL_REO1_RING_BASE_MSB(sc) - HAL_REO1_RING_BASE_LSB(sc)) +#define HAL_REO1_RING_ID_OFFSET(sc) (HAL_REO1_RING_ID(sc) - \ + HAL_REO1_RING_BASE_LSB(sc)) +#define HAL_REO1_RING_PRODUCER_INT_SETUP_OFFSET(sc) \ + (HAL_REO1_RING_PRODUCER_INT_SETUP(sc) - \ + HAL_REO1_RING_BASE_LSB(sc)) +#define HAL_REO1_RING_HP_ADDR_LSB_OFFSET(sc) \ + (HAL_REO1_RING_HP_ADDR_LSB(sc) - HAL_REO1_RING_BASE_LSB(sc)) +#define HAL_REO1_RING_HP_ADDR_MSB_OFFSET(sc) \ + (HAL_REO1_RING_HP_ADDR_MSB(sc) - HAL_REO1_RING_BASE_LSB(sc)) +#define HAL_REO1_RING_MISC_OFFSET(sc) \ + (HAL_REO1_RING_MISC(sc) - HAL_REO1_RING_BASE_LSB(sc)) + +/* REO2SW(x) R2 ring pointers (head/tail) address */ +#define HAL_REO1_RING_HP(sc) \ + (sc->hw_params.regs->hal_reo1_ring_hp) +#define HAL_REO1_RING_TP(sc) \ + (sc->hw_params.regs->hal_reo1_ring_tp) +#define HAL_REO2_RING_HP(sc) \ + (sc->hw_params.regs->hal_reo2_ring_hp) + +#define HAL_REO1_RING_TP_OFFSET(sc) \ + (HAL_REO1_RING_TP(sc) - HAL_REO1_RING_HP(sc)) + +/* REO2TCL R0 ring configuration address */ +#define HAL_REO_TCL_RING_BASE_LSB(sc) \ + (sc->hw_params.regs->hal_reo_tcl_ring_base_lsb) + +/* REO2TCL R2 ring pointer (head/tail) address */ +#define HAL_REO_TCL_RING_HP(sc) \ + (sc->hw_params.regs->hal_reo_tcl_ring_hp) + +/* REO CMD R0 address */ +#define HAL_REO_CMD_RING_BASE_LSB(sc) \ + (sc->hw_params.regs->hal_reo_cmd_ring_base_lsb) + +/* REO CMD R2 address */ +#define HAL_REO_CMD_HP(sc) \ + (sc->hw_params.regs->hal_reo_cmd_ring_hp) + +/* SW2REO R0 address */ +#define HAL_SW2REO_RING_BASE_LSB(sc) \ + (sc->hw_params.regs->hal_sw2reo_ring_base_lsb) + +/* SW2REO R2 address */ +#define HAL_SW2REO_RING_HP(sc) \ + (sc->hw_params.regs->hal_sw2reo_ring_hp) + +/* CE ring R0 address */ +#define HAL_CE_DST_RING_BASE_LSB 0x00000000 +#define HAL_CE_DST_STATUS_RING_BASE_LSB 0x00000058 +#define HAL_CE_DST_RING_CTRL 0x000000b0 + +/* CE ring R2 address */ +#define HAL_CE_DST_RING_HP 0x00000400 +#define HAL_CE_DST_STATUS_RING_HP 0x00000408 + +/* REO status address */ +#define HAL_REO_STATUS_RING_BASE_LSB(sc) \ + (sc->hw_params.regs->hal_reo_status_ring_base_lsb) +#define HAL_REO_STATUS_HP(sc) \ + (sc->hw_params.regs->hal_reo_status_hp) + +/* WBM Idle R0 address */ +#define HAL_WBM_IDLE_LINK_RING_BASE_LSB(x) \ + (sc->hw_params.regs->hal_wbm_idle_link_ring_base_lsb) +#define HAL_WBM_IDLE_LINK_RING_MISC_ADDR(x) \ + (sc->hw_params.regs->hal_wbm_idle_link_ring_misc) +#define HAL_WBM_R0_IDLE_LIST_CONTROL_ADDR 0x00000048 +#define HAL_WBM_R0_IDLE_LIST_SIZE_ADDR 0x0000004c +#define HAL_WBM_SCATTERED_RING_BASE_LSB 0x00000058 +#define HAL_WBM_SCATTERED_RING_BASE_MSB 0x0000005c +#define HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX0 0x00000068 +#define HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX1 0x0000006c +#define HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX0 0x00000078 +#define HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX1 0x0000007c +#define HAL_WBM_SCATTERED_DESC_PTR_HP_ADDR 0x00000084 + +/* WBM Idle R2 address */ +#define HAL_WBM_IDLE_LINK_RING_HP 0x000030b0 + +/* SW2WBM R0 release address */ +#define HAL_WBM_RELEASE_RING_BASE_LSB(x) \ + (sc->hw_params.regs->hal_wbm_release_ring_base_lsb) + +/* SW2WBM R2 release address */ +#define HAL_WBM_RELEASE_RING_HP 0x00003018 + +/* WBM2SW R0 release address */ +#define HAL_WBM0_RELEASE_RING_BASE_LSB(x) \ + (sc->hw_params.regs->hal_wbm0_release_ring_base_lsb) +#define HAL_WBM1_RELEASE_RING_BASE_LSB(x) \ + (sc->hw_params.regs->hal_wbm1_release_ring_base_lsb) + +/* WBM2SW R2 release address */ +#define HAL_WBM0_RELEASE_RING_HP 0x000030c0 +#define HAL_WBM1_RELEASE_RING_HP 0x000030c8 + +/* TCL ring field mask and offset */ +#define HAL_TCL1_RING_BASE_MSB_RING_SIZE GENMASK(27, 8) +#define HAL_TCL1_RING_BASE_MSB_RING_BASE_ADDR_MSB GENMASK(7, 0) +#define HAL_TCL1_RING_ID_ENTRY_SIZE GENMASK(7, 0) +#define HAL_TCL1_RING_MISC_MSI_LOOPCNT_DISABLE BIT(1) +#define HAL_TCL1_RING_MISC_MSI_SWAP BIT(3) +#define HAL_TCL1_RING_MISC_HOST_FW_SWAP BIT(4) +#define HAL_TCL1_RING_MISC_DATA_TLV_SWAP BIT(5) +#define HAL_TCL1_RING_MISC_SRNG_ENABLE BIT(6) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_INTR_TMR_THOLD GENMASK(31, 16) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_BATCH_COUNTER_THOLD GENMASK(14, 0) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_LOW_THOLD GENMASK(15, 0) +#define HAL_TCL1_RING_MSI1_BASE_MSB_MSI1_ENABLE BIT(8) +#define HAL_TCL1_RING_MSI1_BASE_MSB_ADDR GENMASK(7, 0) +#define HAL_TCL1_RING_CMN_CTRL_DSCP_TID_MAP_PROG_EN BIT(17) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP GENMASK(31, 0) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP0 GENMASK(2, 0) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP1 GENMASK(5, 3) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP2 GENMASK(8, 6) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP3 GENMASK(11, 9) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP4 GENMASK(14, 12) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP5 GENMASK(17, 15) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP6 GENMASK(20, 18) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP7 GENMASK(23, 21) + +/* REO ring field mask and offset */ +#define HAL_REO1_RING_BASE_MSB_RING_SIZE GENMASK(27, 8) +#define HAL_REO1_RING_BASE_MSB_RING_BASE_ADDR_MSB GENMASK(7, 0) +#define HAL_REO1_RING_ID_RING_ID GENMASK(15, 8) +#define HAL_REO1_RING_ID_ENTRY_SIZE GENMASK(7, 0) +#define HAL_REO1_RING_MISC_MSI_SWAP BIT(3) +#define HAL_REO1_RING_MISC_HOST_FW_SWAP BIT(4) +#define HAL_REO1_RING_MISC_DATA_TLV_SWAP BIT(5) +#define HAL_REO1_RING_MISC_SRNG_ENABLE BIT(6) +#define HAL_REO1_RING_PRDR_INT_SETUP_INTR_TMR_THOLD GENMASK(31, 16) +#define HAL_REO1_RING_PRDR_INT_SETUP_BATCH_COUNTER_THOLD GENMASK(14, 0) +#define HAL_REO1_RING_MSI1_BASE_MSB_MSI1_ENABLE BIT(8) +#define HAL_REO1_RING_MSI1_BASE_MSB_ADDR GENMASK(7, 0) +#define HAL_REO1_GEN_ENABLE_FRAG_DST_RING GENMASK(25, 23) +#define HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE BIT(2) +#define HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE BIT(3) +#define HAL_REO1_MISC_CTL_FRAGMENT_DST_RING GENMASK(20, 17) + +/* CE ring bit field mask and shift */ +#define HAL_CE_DST_R0_DEST_CTRL_MAX_LEN GENMASK(15, 0) + +#define HAL_ADDR_LSB_REG_MASK 0xffffffff + +#define HAL_ADDR_MSB_REG_SHIFT 32 + +/* WBM ring bit field mask and shift */ +#define HAL_WBM_LINK_DESC_IDLE_LIST_MODE BIT(1) +#define HAL_WBM_SCATTER_BUFFER_SIZE GENMASK(10, 2) +#define HAL_WBM_SCATTER_RING_SIZE_OF_IDLE_LINK_DESC_LIST GENMASK(31, 16) +#define HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32 GENMASK(7, 0) +#define HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_MATCH_TAG GENMASK(31, 8) + +#define HAL_WBM_SCATTERED_DESC_HEAD_P_OFFSET_IX1 GENMASK(20, 8) +#define HAL_WBM_SCATTERED_DESC_TAIL_P_OFFSET_IX1 GENMASK(20, 8) + +#define BASE_ADDR_MATCH_TAG_VAL 0x5 + +#define HAL_REO_REO2SW1_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_REO_REO2TCL_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_REO_SW2REO_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_REO_CMD_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_REO_STATUS_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_SW2TCL1_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_SW2TCL1_CMD_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_TCL_STATUS_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_CE_SRC_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_CE_DST_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_CE_DST_STATUS_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_WBM_IDLE_LINK_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_SW2WBM_RELEASE_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_WBM2SW_RELEASE_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_RXDMA_RING_MAX_SIZE 0x0000ffff + +/* IPQ5018 ce registers */ +#define HAL_IPQ5018_CE_WFSS_REG_BASE 0x08400000 +#define HAL_IPQ5018_CE_SIZE 0x200000 + +#define BUFFER_ADDR_INFO0_ADDR GENMASK(31, 0) + +#define BUFFER_ADDR_INFO1_ADDR GENMASK(7, 0) +#define BUFFER_ADDR_INFO1_RET_BUF_MGR GENMASK(10, 8) +#define BUFFER_ADDR_INFO1_SW_COOKIE GENMASK(31, 11) + +struct ath12k_buffer_addr { + uint32_t info0; + uint32_t info1; +} __packed; + +/* ath12k_buffer_addr + * + * info0 + * Address (lower 32 bits) of the msdu buffer or msdu extension + * descriptor or Link descriptor + * + * addr + * Address (upper 8 bits) of the msdu buffer or msdu extension + * descriptor or Link descriptor + * + * return_buffer_manager (RBM) + * Consumer: WBM + * Producer: SW/FW + * Indicates to which buffer manager the buffer or MSDU_EXTENSION + * descriptor or link descriptor that is being pointed to shall be + * returned after the frame has been processed. It is used by WBM + * for routing purposes. + * + * Values are defined in enum %HAL_RX_BUF_RBM_ + * + * sw_buffer_cookie + * Cookie field exclusively used by SW. HW ignores the contents, + * accept that it passes the programmed value on to other + * descriptors together with the physical address. + * + * Field can be used by SW to for example associate the buffers + * physical address with the virtual address. + */ + +enum hal_tlv_tag { + HAL_MACTX_CBF_START = 0 /* 0x0 */, + HAL_PHYRX_DATA = 1 /* 0x1 */, + HAL_PHYRX_CBF_DATA_RESP = 2 /* 0x2 */, + HAL_PHYRX_ABORT_REQUEST = 3 /* 0x3 */, + HAL_PHYRX_USER_ABORT_NOTIFICATION = 4 /* 0x4 */, + HAL_MACTX_DATA_RESP = 5 /* 0x5 */, + HAL_MACTX_CBF_DATA = 6 /* 0x6 */, + HAL_MACTX_CBF_DONE = 7 /* 0x7 */, + HAL_MACRX_CBF_READ_REQUEST = 8 /* 0x8 */, + HAL_MACRX_CBF_DATA_REQUEST = 9 /* 0x9 */, + HAL_MACRX_EXPECT_NDP_RECEPTION = 10 /* 0xa */, + HAL_MACRX_FREEZE_CAPTURE_CHANNEL = 11 /* 0xb */, + HAL_MACRX_NDP_TIMEOUT = 12 /* 0xc */, + HAL_MACRX_ABORT_ACK = 13 /* 0xd */, + HAL_MACRX_REQ_IMPLICIT_FB = 14 /* 0xe */, + HAL_MACRX_CHAIN_MASK = 15 /* 0xf */, + HAL_MACRX_NAP_USER = 16 /* 0x10 */, + HAL_MACRX_ABORT_REQUEST = 17 /* 0x11 */, + HAL_PHYTX_OTHER_TRANSMIT_INFO16 = 18 /* 0x12 */, + HAL_PHYTX_ABORT_ACK = 19 /* 0x13 */, + HAL_PHYTX_ABORT_REQUEST = 20 /* 0x14 */, + HAL_PHYTX_PKT_END = 21 /* 0x15 */, + HAL_PHYTX_PPDU_HEADER_INFO_REQUEST = 22 /* 0x16 */, + HAL_PHYTX_REQUEST_CTRL_INFO = 23 /* 0x17 */, + HAL_PHYTX_DATA_REQUEST = 24 /* 0x18 */, + HAL_PHYTX_BF_CV_LOADING_DONE = 25 /* 0x19 */, + HAL_PHYTX_NAP_ACK = 26 /* 0x1a */, + HAL_PHYTX_NAP_DONE = 27 /* 0x1b */, + HAL_PHYTX_OFF_ACK = 28 /* 0x1c */, + HAL_PHYTX_ON_ACK = 29 /* 0x1d */, + HAL_PHYTX_SYNTH_OFF_ACK = 30 /* 0x1e */, + HAL_PHYTX_DEBUG16 = 31 /* 0x1f */, + HAL_MACTX_ABORT_REQUEST = 32 /* 0x20 */, + HAL_MACTX_ABORT_ACK = 33 /* 0x21 */, + HAL_MACTX_PKT_END = 34 /* 0x22 */, + HAL_MACTX_PRE_PHY_DESC = 35 /* 0x23 */, + HAL_MACTX_BF_PARAMS_COMMON = 36 /* 0x24 */, + HAL_MACTX_BF_PARAMS_PER_USER = 37 /* 0x25 */, + HAL_MACTX_PREFETCH_CV = 38 /* 0x26 */, + HAL_MACTX_USER_DESC_COMMON = 39 /* 0x27 */, + HAL_MACTX_USER_DESC_PER_USER = 40 /* 0x28 */, + HAL_EXAMPLE_USER_TLV_16 = 41 /* 0x29 */, + HAL_EXAMPLE_TLV_16 = 42 /* 0x2a */, + HAL_MACTX_PHY_OFF = 43 /* 0x2b */, + HAL_MACTX_PHY_ON = 44 /* 0x2c */, + HAL_MACTX_SYNTH_OFF = 45 /* 0x2d */, + HAL_MACTX_EXPECT_CBF_COMMON = 46 /* 0x2e */, + HAL_MACTX_EXPECT_CBF_PER_USER = 47 /* 0x2f */, + HAL_MACTX_PHY_DESC = 48 /* 0x30 */, + HAL_MACTX_L_SIG_A = 49 /* 0x31 */, + HAL_MACTX_L_SIG_B = 50 /* 0x32 */, + HAL_MACTX_HT_SIG = 51 /* 0x33 */, + HAL_MACTX_VHT_SIG_A = 52 /* 0x34 */, + HAL_MACTX_VHT_SIG_B_SU20 = 53 /* 0x35 */, + HAL_MACTX_VHT_SIG_B_SU40 = 54 /* 0x36 */, + HAL_MACTX_VHT_SIG_B_SU80 = 55 /* 0x37 */, + HAL_MACTX_VHT_SIG_B_SU160 = 56 /* 0x38 */, + HAL_MACTX_VHT_SIG_B_MU20 = 57 /* 0x39 */, + HAL_MACTX_VHT_SIG_B_MU40 = 58 /* 0x3a */, + HAL_MACTX_VHT_SIG_B_MU80 = 59 /* 0x3b */, + HAL_MACTX_VHT_SIG_B_MU160 = 60 /* 0x3c */, + HAL_MACTX_SERVICE = 61 /* 0x3d */, + HAL_MACTX_HE_SIG_A_SU = 62 /* 0x3e */, + HAL_MACTX_HE_SIG_A_MU_DL = 63 /* 0x3f */, + HAL_MACTX_HE_SIG_A_MU_UL = 64 /* 0x40 */, + HAL_MACTX_HE_SIG_B1_MU = 65 /* 0x41 */, + HAL_MACTX_HE_SIG_B2_MU = 66 /* 0x42 */, + HAL_MACTX_HE_SIG_B2_OFDMA = 67 /* 0x43 */, + HAL_MACTX_DELETE_CV = 68 /* 0x44 */, + HAL_MACTX_MU_UPLINK_COMMON = 69 /* 0x45 */, + HAL_MACTX_MU_UPLINK_USER_SETUP = 70 /* 0x46 */, + HAL_MACTX_OTHER_TRANSMIT_INFO = 71 /* 0x47 */, + HAL_MACTX_PHY_NAP = 72 /* 0x48 */, + HAL_MACTX_DEBUG = 73 /* 0x49 */, + HAL_PHYRX_ABORT_ACK = 74 /* 0x4a */, + HAL_PHYRX_GENERATED_CBF_DETAILS = 75 /* 0x4b */, + HAL_PHYRX_RSSI_LEGACY = 76 /* 0x4c */, + HAL_PHYRX_RSSI_HT = 77 /* 0x4d */, + HAL_PHYRX_USER_INFO = 78 /* 0x4e */, + HAL_PHYRX_PKT_END = 79 /* 0x4f */, + HAL_PHYRX_DEBUG = 80 /* 0x50 */, + HAL_PHYRX_CBF_TRANSFER_DONE = 81 /* 0x51 */, + HAL_PHYRX_CBF_TRANSFER_ABORT = 82 /* 0x52 */, + HAL_PHYRX_L_SIG_A = 83 /* 0x53 */, + HAL_PHYRX_L_SIG_B = 84 /* 0x54 */, + HAL_PHYRX_HT_SIG = 85 /* 0x55 */, + HAL_PHYRX_VHT_SIG_A = 86 /* 0x56 */, + HAL_PHYRX_VHT_SIG_B_SU20 = 87 /* 0x57 */, + HAL_PHYRX_VHT_SIG_B_SU40 = 88 /* 0x58 */, + HAL_PHYRX_VHT_SIG_B_SU80 = 89 /* 0x59 */, + HAL_PHYRX_VHT_SIG_B_SU160 = 90 /* 0x5a */, + HAL_PHYRX_VHT_SIG_B_MU20 = 91 /* 0x5b */, + HAL_PHYRX_VHT_SIG_B_MU40 = 92 /* 0x5c */, + HAL_PHYRX_VHT_SIG_B_MU80 = 93 /* 0x5d */, + HAL_PHYRX_VHT_SIG_B_MU160 = 94 /* 0x5e */, + HAL_PHYRX_HE_SIG_A_SU = 95 /* 0x5f */, + HAL_PHYRX_HE_SIG_A_MU_DL = 96 /* 0x60 */, + HAL_PHYRX_HE_SIG_A_MU_UL = 97 /* 0x61 */, + HAL_PHYRX_HE_SIG_B1_MU = 98 /* 0x62 */, + HAL_PHYRX_HE_SIG_B2_MU = 99 /* 0x63 */, + HAL_PHYRX_HE_SIG_B2_OFDMA = 100 /* 0x64 */, + HAL_PHYRX_OTHER_RECEIVE_INFO = 101 /* 0x65 */, + HAL_PHYRX_COMMON_USER_INFO = 102 /* 0x66 */, + HAL_PHYRX_DATA_DONE = 103 /* 0x67 */, + HAL_RECEIVE_RSSI_INFO = 104 /* 0x68 */, + HAL_RECEIVE_USER_INFO = 105 /* 0x69 */, + HAL_MIMO_CONTROL_INFO = 106 /* 0x6a */, + HAL_RX_LOCATION_INFO = 107 /* 0x6b */, + HAL_COEX_TX_REQ = 108 /* 0x6c */, + HAL_DUMMY = 109 /* 0x6d */, + HAL_RX_TIMING_OFFSET_INFO = 110 /* 0x6e */, + HAL_EXAMPLE_TLV_32_NAME = 111 /* 0x6f */, + HAL_MPDU_LIMIT = 112 /* 0x70 */, + HAL_NA_LENGTH_END = 113 /* 0x71 */, + HAL_OLE_BUF_STATUS = 114 /* 0x72 */, + HAL_PCU_PPDU_SETUP_DONE = 115 /* 0x73 */, + HAL_PCU_PPDU_SETUP_END = 116 /* 0x74 */, + HAL_PCU_PPDU_SETUP_INIT = 117 /* 0x75 */, + HAL_PCU_PPDU_SETUP_START = 118 /* 0x76 */, + HAL_PDG_FES_SETUP = 119 /* 0x77 */, + HAL_PDG_RESPONSE = 120 /* 0x78 */, + HAL_PDG_TX_REQ = 121 /* 0x79 */, + HAL_SCH_WAIT_INSTR = 122 /* 0x7a */, + HAL_SCHEDULER_TLV = 123 /* 0x7b */, + HAL_TQM_FLOW_EMPTY_STATUS = 124 /* 0x7c */, + HAL_TQM_FLOW_NOT_EMPTY_STATUS = 125 /* 0x7d */, + HAL_TQM_GEN_MPDU_LENGTH_LIST = 126 /* 0x7e */, + HAL_TQM_GEN_MPDU_LENGTH_LIST_STATUS = 127 /* 0x7f */, + HAL_TQM_GEN_MPDUS = 128 /* 0x80 */, + HAL_TQM_GEN_MPDUS_STATUS = 129 /* 0x81 */, + HAL_TQM_REMOVE_MPDU = 130 /* 0x82 */, + HAL_TQM_REMOVE_MPDU_STATUS = 131 /* 0x83 */, + HAL_TQM_REMOVE_MSDU = 132 /* 0x84 */, + HAL_TQM_REMOVE_MSDU_STATUS = 133 /* 0x85 */, + HAL_TQM_UPDATE_TX_MPDU_COUNT = 134 /* 0x86 */, + HAL_TQM_WRITE_CMD = 135 /* 0x87 */, + HAL_OFDMA_TRIGGER_DETAILS = 136 /* 0x88 */, + HAL_TX_DATA = 137 /* 0x89 */, + HAL_TX_FES_SETUP = 138 /* 0x8a */, + HAL_RX_PACKET = 139 /* 0x8b */, + HAL_EXPECTED_RESPONSE = 140 /* 0x8c */, + HAL_TX_MPDU_END = 141 /* 0x8d */, + HAL_TX_MPDU_START = 142 /* 0x8e */, + HAL_TX_MSDU_END = 143 /* 0x8f */, + HAL_TX_MSDU_START = 144 /* 0x90 */, + HAL_TX_SW_MODE_SETUP = 145 /* 0x91 */, + HAL_TXPCU_BUFFER_STATUS = 146 /* 0x92 */, + HAL_TXPCU_USER_BUFFER_STATUS = 147 /* 0x93 */, + HAL_DATA_TO_TIME_CONFIG = 148 /* 0x94 */, + HAL_EXAMPLE_USER_TLV_32 = 149 /* 0x95 */, + HAL_MPDU_INFO = 150 /* 0x96 */, + HAL_PDG_USER_SETUP = 151 /* 0x97 */, + HAL_TX_11AH_SETUP = 152 /* 0x98 */, + HAL_REO_UPDATE_RX_REO_QUEUE_STATUS = 153 /* 0x99 */, + HAL_TX_PEER_ENTRY = 154 /* 0x9a */, + HAL_TX_RAW_OR_NATIVE_FRAME_SETUP = 155 /* 0x9b */, + HAL_EXAMPLE_STRUCT_NAME = 156 /* 0x9c */, + HAL_PCU_PPDU_SETUP_END_INFO = 157 /* 0x9d */, + HAL_PPDU_RATE_SETTING = 158 /* 0x9e */, + HAL_PROT_RATE_SETTING = 159 /* 0x9f */, + HAL_RX_MPDU_DETAILS = 160 /* 0xa0 */, + HAL_EXAMPLE_USER_TLV_42 = 161 /* 0xa1 */, + HAL_RX_MSDU_LINK = 162 /* 0xa2 */, + HAL_RX_REO_QUEUE = 163 /* 0xa3 */, + HAL_ADDR_SEARCH_ENTRY = 164 /* 0xa4 */, + HAL_SCHEDULER_CMD = 165 /* 0xa5 */, + HAL_TX_FLUSH = 166 /* 0xa6 */, + HAL_TQM_ENTRANCE_RING = 167 /* 0xa7 */, + HAL_TX_DATA_WORD = 168 /* 0xa8 */, + HAL_TX_MPDU_DETAILS = 169 /* 0xa9 */, + HAL_TX_MPDU_LINK = 170 /* 0xaa */, + HAL_TX_MPDU_LINK_PTR = 171 /* 0xab */, + HAL_TX_MPDU_QUEUE_HEAD = 172 /* 0xac */, + HAL_TX_MPDU_QUEUE_EXT = 173 /* 0xad */, + HAL_TX_MPDU_QUEUE_EXT_PTR = 174 /* 0xae */, + HAL_TX_MSDU_DETAILS = 175 /* 0xaf */, + HAL_TX_MSDU_EXTENSION = 176 /* 0xb0 */, + HAL_TX_MSDU_FLOW = 177 /* 0xb1 */, + HAL_TX_MSDU_LINK = 178 /* 0xb2 */, + HAL_TX_MSDU_LINK_ENTRY_PTR = 179 /* 0xb3 */, + HAL_RESPONSE_RATE_SETTING = 180 /* 0xb4 */, + HAL_TXPCU_BUFFER_BASICS = 181 /* 0xb5 */, + HAL_UNIFORM_DESCRIPTOR_HEADER = 182 /* 0xb6 */, + HAL_UNIFORM_TQM_CMD_HEADER = 183 /* 0xb7 */, + HAL_UNIFORM_TQM_STATUS_HEADER = 184 /* 0xb8 */, + HAL_USER_RATE_SETTING = 185 /* 0xb9 */, + HAL_WBM_BUFFER_RING = 186 /* 0xba */, + HAL_WBM_LINK_DESCRIPTOR_RING = 187 /* 0xbb */, + HAL_WBM_RELEASE_RING = 188 /* 0xbc */, + HAL_TX_FLUSH_REQ = 189 /* 0xbd */, + HAL_RX_MSDU_DETAILS = 190 /* 0xbe */, + HAL_TQM_WRITE_CMD_STATUS = 191 /* 0xbf */, + HAL_TQM_GET_MPDU_QUEUE_STATS = 192 /* 0xc0 */, + HAL_TQM_GET_MSDU_FLOW_STATS = 193 /* 0xc1 */, + HAL_EXAMPLE_USER_CTLV_32 = 194 /* 0xc2 */, + HAL_TX_FES_STATUS_START = 195 /* 0xc3 */, + HAL_TX_FES_STATUS_USER_PPDU = 196 /* 0xc4 */, + HAL_TX_FES_STATUS_USER_RESPONSE = 197 /* 0xc5 */, + HAL_TX_FES_STATUS_END = 198 /* 0xc6 */, + HAL_RX_TRIG_INFO = 199 /* 0xc7 */, + HAL_RXPCU_TX_SETUP_CLEAR = 200 /* 0xc8 */, + HAL_RX_FRAME_BITMAP_REQ = 201 /* 0xc9 */, + HAL_RX_FRAME_BITMAP_ACK = 202 /* 0xca */, + HAL_COEX_RX_STATUS = 203 /* 0xcb */, + HAL_RX_START_PARAM = 204 /* 0xcc */, + HAL_RX_PPDU_START = 205 /* 0xcd */, + HAL_RX_PPDU_END = 206 /* 0xce */, + HAL_RX_MPDU_START = 207 /* 0xcf */, + HAL_RX_MPDU_END = 208 /* 0xd0 */, + HAL_RX_MSDU_START = 209 /* 0xd1 */, + HAL_RX_MSDU_END = 210 /* 0xd2 */, + HAL_RX_ATTENTION = 211 /* 0xd3 */, + HAL_RECEIVED_RESPONSE_INFO = 212 /* 0xd4 */, + HAL_RX_PHY_SLEEP = 213 /* 0xd5 */, + HAL_RX_HEADER = 214 /* 0xd6 */, + HAL_RX_PEER_ENTRY = 215 /* 0xd7 */, + HAL_RX_FLUSH = 216 /* 0xd8 */, + HAL_RX_RESPONSE_REQUIRED_INFO = 217 /* 0xd9 */, + HAL_RX_FRAMELESS_BAR_DETAILS = 218 /* 0xda */, + HAL_TQM_GET_MPDU_QUEUE_STATS_STATUS = 219 /* 0xdb */, + HAL_TQM_GET_MSDU_FLOW_STATS_STATUS = 220 /* 0xdc */, + HAL_TX_CBF_INFO = 221 /* 0xdd */, + HAL_PCU_PPDU_SETUP_USER = 222 /* 0xde */, + HAL_RX_MPDU_PCU_START = 223 /* 0xdf */, + HAL_RX_PM_INFO = 224 /* 0xe0 */, + HAL_RX_USER_PPDU_END = 225 /* 0xe1 */, + HAL_RX_PRE_PPDU_START = 226 /* 0xe2 */, + HAL_RX_PREAMBLE = 227 /* 0xe3 */, + HAL_TX_FES_SETUP_COMPLETE = 228 /* 0xe4 */, + HAL_TX_LAST_MPDU_FETCHED = 229 /* 0xe5 */, + HAL_TXDMA_STOP_REQUEST = 230 /* 0xe6 */, + HAL_RXPCU_SETUP = 231 /* 0xe7 */, + HAL_RXPCU_USER_SETUP = 232 /* 0xe8 */, + HAL_TX_FES_STATUS_ACK_OR_BA = 233 /* 0xe9 */, + HAL_TQM_ACKED_MPDU = 234 /* 0xea */, + HAL_COEX_TX_RESP = 235 /* 0xeb */, + HAL_COEX_TX_STATUS = 236 /* 0xec */, + HAL_MACTX_COEX_PHY_CTRL = 237 /* 0xed */, + HAL_COEX_STATUS_BROADCAST = 238 /* 0xee */, + HAL_RESPONSE_START_STATUS = 239 /* 0xef */, + HAL_RESPONSE_END_STATUS = 240 /* 0xf0 */, + HAL_CRYPTO_STATUS = 241 /* 0xf1 */, + HAL_RECEIVED_TRIGGER_INFO = 242 /* 0xf2 */, + HAL_REO_ENTRANCE_RING = 243 /* 0xf3 */, + HAL_RX_MPDU_LINK = 244 /* 0xf4 */, + HAL_COEX_TX_STOP_CTRL = 245 /* 0xf5 */, + HAL_RX_PPDU_ACK_REPORT = 246 /* 0xf6 */, + HAL_RX_PPDU_NO_ACK_REPORT = 247 /* 0xf7 */, + HAL_SCH_COEX_STATUS = 248 /* 0xf8 */, + HAL_SCHEDULER_COMMAND_STATUS = 249 /* 0xf9 */, + HAL_SCHEDULER_RX_PPDU_NO_RESPONSE_STATUS = 250 /* 0xfa */, + HAL_TX_FES_STATUS_PROT = 251 /* 0xfb */, + HAL_TX_FES_STATUS_START_PPDU = 252 /* 0xfc */, + HAL_TX_FES_STATUS_START_PROT = 253 /* 0xfd */, + HAL_TXPCU_PHYTX_DEBUG32 = 254 /* 0xfe */, + HAL_TXPCU_PHYTX_OTHER_TRANSMIT_INFO32 = 255 /* 0xff */, + HAL_TX_MPDU_COUNT_TRANSFER_END = 256 /* 0x100 */, + HAL_WHO_ANCHOR_OFFSET = 257 /* 0x101 */, + HAL_WHO_ANCHOR_VALUE = 258 /* 0x102 */, + HAL_WHO_CCE_INFO = 259 /* 0x103 */, + HAL_WHO_COMMIT = 260 /* 0x104 */, + HAL_WHO_COMMIT_DONE = 261 /* 0x105 */, + HAL_WHO_FLUSH = 262 /* 0x106 */, + HAL_WHO_L2_LLC = 263 /* 0x107 */, + HAL_WHO_L2_PAYLOAD = 264 /* 0x108 */, + HAL_WHO_L3_CHECKSUM = 265 /* 0x109 */, + HAL_WHO_L3_INFO = 266 /* 0x10a */, + HAL_WHO_L4_CHECKSUM = 267 /* 0x10b */, + HAL_WHO_L4_INFO = 268 /* 0x10c */, + HAL_WHO_MSDU = 269 /* 0x10d */, + HAL_WHO_MSDU_MISC = 270 /* 0x10e */, + HAL_WHO_PACKET_DATA = 271 /* 0x10f */, + HAL_WHO_PACKET_HDR = 272 /* 0x110 */, + HAL_WHO_PPDU_END = 273 /* 0x111 */, + HAL_WHO_PPDU_START = 274 /* 0x112 */, + HAL_WHO_TSO = 275 /* 0x113 */, + HAL_WHO_WMAC_HEADER_PV0 = 276 /* 0x114 */, + HAL_WHO_WMAC_HEADER_PV1 = 277 /* 0x115 */, + HAL_WHO_WMAC_IV = 278 /* 0x116 */, + HAL_MPDU_INFO_END = 279 /* 0x117 */, + HAL_MPDU_INFO_BITMAP = 280 /* 0x118 */, + HAL_TX_QUEUE_EXTENSION = 281 /* 0x119 */, + HAL_RX_PEER_ENTRY_DETAILS = 282 /* 0x11a */, + HAL_RX_REO_QUEUE_REFERENCE = 283 /* 0x11b */, + HAL_RX_REO_QUEUE_EXT = 284 /* 0x11c */, + HAL_SCHEDULER_SELFGEN_RESPONSE_STATUS = 285 /* 0x11d */, + HAL_TQM_UPDATE_TX_MPDU_COUNT_STATUS = 286 /* 0x11e */, + HAL_TQM_ACKED_MPDU_STATUS = 287 /* 0x11f */, + HAL_TQM_ADD_MSDU_STATUS = 288 /* 0x120 */, + HAL_RX_MPDU_LINK_PTR = 289 /* 0x121 */, + HAL_REO_DESTINATION_RING = 290 /* 0x122 */, + HAL_TQM_LIST_GEN_DONE = 291 /* 0x123 */, + HAL_WHO_TERMINATE = 292 /* 0x124 */, + HAL_TX_LAST_MPDU_END = 293 /* 0x125 */, + HAL_TX_CV_DATA = 294 /* 0x126 */, + HAL_TCL_ENTRANCE_FROM_PPE_RING = 295 /* 0x127 */, + HAL_PPDU_TX_END = 296 /* 0x128 */, + HAL_PROT_TX_END = 297 /* 0x129 */, + HAL_PDG_RESPONSE_RATE_SETTING = 298 /* 0x12a */, + HAL_MPDU_INFO_GLOBAL_END = 299 /* 0x12b */, + HAL_TQM_SCH_INSTR_GLOBAL_END = 300 /* 0x12c */, + HAL_RX_PPDU_END_USER_STATS = 301 /* 0x12d */, + HAL_RX_PPDU_END_USER_STATS_EXT = 302 /* 0x12e */, + HAL_NO_ACK_REPORT = 303 /* 0x12f */, + HAL_ACK_REPORT = 304 /* 0x130 */, + HAL_UNIFORM_REO_CMD_HEADER = 305 /* 0x131 */, + HAL_REO_GET_QUEUE_STATS = 306 /* 0x132 */, + HAL_REO_FLUSH_QUEUE = 307 /* 0x133 */, + HAL_REO_FLUSH_CACHE = 308 /* 0x134 */, + HAL_REO_UNBLOCK_CACHE = 309 /* 0x135 */, + HAL_UNIFORM_REO_STATUS_HEADER = 310 /* 0x136 */, + HAL_REO_GET_QUEUE_STATS_STATUS = 311 /* 0x137 */, + HAL_REO_FLUSH_QUEUE_STATUS = 312 /* 0x138 */, + HAL_REO_FLUSH_CACHE_STATUS = 313 /* 0x139 */, + HAL_REO_UNBLOCK_CACHE_STATUS = 314 /* 0x13a */, + HAL_TQM_FLUSH_CACHE = 315 /* 0x13b */, + HAL_TQM_UNBLOCK_CACHE = 316 /* 0x13c */, + HAL_TQM_FLUSH_CACHE_STATUS = 317 /* 0x13d */, + HAL_TQM_UNBLOCK_CACHE_STATUS = 318 /* 0x13e */, + HAL_RX_PPDU_END_STATUS_DONE = 319 /* 0x13f */, + HAL_RX_STATUS_BUFFER_DONE = 320 /* 0x140 */, + HAL_BUFFER_ADDR_INFO = 321 /* 0x141 */, + HAL_RX_MSDU_DESC_INFO = 322 /* 0x142 */, + HAL_RX_MPDU_DESC_INFO = 323 /* 0x143 */, + HAL_TCL_DATA_CMD = 324 /* 0x144 */, + HAL_TCL_GSE_CMD = 325 /* 0x145 */, + HAL_TCL_EXIT_BASE = 326 /* 0x146 */, + HAL_TCL_COMPACT_EXIT_RING = 327 /* 0x147 */, + HAL_TCL_REGULAR_EXIT_RING = 328 /* 0x148 */, + HAL_TCL_EXTENDED_EXIT_RING = 329 /* 0x149 */, + HAL_UPLINK_COMMON_INFO = 330 /* 0x14a */, + HAL_UPLINK_USER_SETUP_INFO = 331 /* 0x14b */, + HAL_TX_DATA_SYNC = 332 /* 0x14c */, + HAL_PHYRX_CBF_READ_REQUEST_ACK = 333 /* 0x14d */, + HAL_TCL_STATUS_RING = 334 /* 0x14e */, + HAL_TQM_GET_MPDU_HEAD_INFO = 335 /* 0x14f */, + HAL_TQM_SYNC_CMD = 336 /* 0x150 */, + HAL_TQM_GET_MPDU_HEAD_INFO_STATUS = 337 /* 0x151 */, + HAL_TQM_SYNC_CMD_STATUS = 338 /* 0x152 */, + HAL_TQM_THRESHOLD_DROP_NOTIFICATION_STATUS = 339 /* 0x153 */, + HAL_TQM_DESCRIPTOR_THRESHOLD_REACHED_STATUS = 340 /* 0x154 */, + HAL_REO_FLUSH_TIMEOUT_LIST = 341 /* 0x155 */, + HAL_REO_FLUSH_TIMEOUT_LIST_STATUS = 342 /* 0x156 */, + HAL_REO_TO_PPE_RING = 343 /* 0x157 */, + HAL_RX_MPDU_INFO = 344 /* 0x158 */, + HAL_REO_DESCRIPTOR_THRESHOLD_REACHED_STATUS = 345 /* 0x159 */, + HAL_SCHEDULER_RX_SIFS_RESPONSE_TRIGGER_STATUS = 346 /* 0x15a */, + HAL_EXAMPLE_USER_TLV_32_NAME = 347 /* 0x15b */, + HAL_RX_PPDU_START_USER_INFO = 348 /* 0x15c */, + HAL_RX_RXPCU_CLASSIFICATION_OVERVIEW = 349 /* 0x15d */, + HAL_RX_RING_MASK = 350 /* 0x15e */, + HAL_WHO_CLASSIFY_INFO = 351 /* 0x15f */, + HAL_TXPT_CLASSIFY_INFO = 352 /* 0x160 */, + HAL_RXPT_CLASSIFY_INFO = 353 /* 0x161 */, + HAL_TX_FLOW_SEARCH_ENTRY = 354 /* 0x162 */, + HAL_RX_FLOW_SEARCH_ENTRY = 355 /* 0x163 */, + HAL_RECEIVED_TRIGGER_INFO_DETAILS = 356 /* 0x164 */, + HAL_COEX_MAC_NAP = 357 /* 0x165 */, + HAL_MACRX_ABORT_REQUEST_INFO = 358 /* 0x166 */, + HAL_MACTX_ABORT_REQUEST_INFO = 359 /* 0x167 */, + HAL_PHYRX_ABORT_REQUEST_INFO = 360 /* 0x168 */, + HAL_PHYTX_ABORT_REQUEST_INFO = 361 /* 0x169 */, + HAL_RXPCU_PPDU_END_INFO = 362 /* 0x16a */, + HAL_WHO_MESH_CONTROL = 363 /* 0x16b */, + HAL_L_SIG_A_INFO = 364 /* 0x16c */, + HAL_L_SIG_B_INFO = 365 /* 0x16d */, + HAL_HT_SIG_INFO = 366 /* 0x16e */, + HAL_VHT_SIG_A_INFO = 367 /* 0x16f */, + HAL_VHT_SIG_B_SU20_INFO = 368 /* 0x170 */, + HAL_VHT_SIG_B_SU40_INFO = 369 /* 0x171 */, + HAL_VHT_SIG_B_SU80_INFO = 370 /* 0x172 */, + HAL_VHT_SIG_B_SU160_INFO = 371 /* 0x173 */, + HAL_VHT_SIG_B_MU20_INFO = 372 /* 0x174 */, + HAL_VHT_SIG_B_MU40_INFO = 373 /* 0x175 */, + HAL_VHT_SIG_B_MU80_INFO = 374 /* 0x176 */, + HAL_VHT_SIG_B_MU160_INFO = 375 /* 0x177 */, + HAL_SERVICE_INFO = 376 /* 0x178 */, + HAL_HE_SIG_A_SU_INFO = 377 /* 0x179 */, + HAL_HE_SIG_A_MU_DL_INFO = 378 /* 0x17a */, + HAL_HE_SIG_A_MU_UL_INFO = 379 /* 0x17b */, + HAL_HE_SIG_B1_MU_INFO = 380 /* 0x17c */, + HAL_HE_SIG_B2_MU_INFO = 381 /* 0x17d */, + HAL_HE_SIG_B2_OFDMA_INFO = 382 /* 0x17e */, + HAL_PDG_SW_MODE_BW_START = 383 /* 0x17f */, + HAL_PDG_SW_MODE_BW_END = 384 /* 0x180 */, + HAL_PDG_WAIT_FOR_MAC_REQUEST = 385 /* 0x181 */, + HAL_PDG_WAIT_FOR_PHY_REQUEST = 386 /* 0x182 */, + HAL_SCHEDULER_END = 387 /* 0x183 */, + HAL_PEER_TABLE_ENTRY = 388 /* 0x184 */, + HAL_SW_PEER_INFO = 389 /* 0x185 */, + HAL_RXOLE_CCE_CLASSIFY_INFO = 390 /* 0x186 */, + HAL_TCL_CCE_CLASSIFY_INFO = 391 /* 0x187 */, + HAL_RXOLE_CCE_INFO = 392 /* 0x188 */, + HAL_TCL_CCE_INFO = 393 /* 0x189 */, + HAL_TCL_CCE_SUPERRULE = 394 /* 0x18a */, + HAL_CCE_RULE = 395 /* 0x18b */, + HAL_RX_PPDU_START_DROPPED = 396 /* 0x18c */, + HAL_RX_PPDU_END_DROPPED = 397 /* 0x18d */, + HAL_RX_PPDU_END_STATUS_DONE_DROPPED = 398 /* 0x18e */, + HAL_RX_MPDU_START_DROPPED = 399 /* 0x18f */, + HAL_RX_MSDU_START_DROPPED = 400 /* 0x190 */, + HAL_RX_MSDU_END_DROPPED = 401 /* 0x191 */, + HAL_RX_MPDU_END_DROPPED = 402 /* 0x192 */, + HAL_RX_ATTENTION_DROPPED = 403 /* 0x193 */, + HAL_TXPCU_USER_SETUP = 404 /* 0x194 */, + HAL_RXPCU_USER_SETUP_EXT = 405 /* 0x195 */, + HAL_CE_SRC_DESC = 406 /* 0x196 */, + HAL_CE_STAT_DESC = 407 /* 0x197 */, + HAL_RXOLE_CCE_SUPERRULE = 408 /* 0x198 */, + HAL_TX_RATE_STATS_INFO = 409 /* 0x199 */, + HAL_CMD_PART_0_END = 410 /* 0x19a */, + HAL_MACTX_SYNTH_ON = 411 /* 0x19b */, + HAL_SCH_CRITICAL_TLV_REFERENCE = 412 /* 0x19c */, + HAL_TQM_MPDU_GLOBAL_START = 413 /* 0x19d */, + HAL_EXAMPLE_TLV_32 = 414 /* 0x19e */, + HAL_TQM_UPDATE_TX_MSDU_FLOW = 415 /* 0x19f */, + HAL_TQM_UPDATE_TX_MPDU_QUEUE_HEAD = 416 /* 0x1a0 */, + HAL_TQM_UPDATE_TX_MSDU_FLOW_STATUS = 417 /* 0x1a1 */, + HAL_TQM_UPDATE_TX_MPDU_QUEUE_HEAD_STATUS = 418 /* 0x1a2 */, + HAL_REO_UPDATE_RX_REO_QUEUE = 419 /* 0x1a3 */, + HAL_CE_DST_DESC = 420 /* 0x1a4 */, + HAL_TLV_BASE = 511 /* 0x1ff */, +}; + +#define HAL_TLV_HDR_TAG GENMASK(9, 1) +#define HAL_TLV_HDR_LEN GENMASK(25, 10) +#define HAL_TLV_USR_ID GENMASK(31, 26) + +#define HAL_TLV_ALIGN 4 + +struct hal_tlv_hdr { + uint32_t tl; + uint8_t value[]; +} __packed; + +#define RX_MPDU_DESC_INFO0_MSDU_COUNT 0xff +#define RX_MPDU_DESC_INFO0_SEQ_NUM 0xfff00 +#define RX_MPDU_DESC_INFO0_FRAG_FLAG (1 << 20) +#define RX_MPDU_DESC_INFO0_MPDU_RETRY (1 << 21) +#define RX_MPDU_DESC_INFO0_AMPDU_FLAG (1 << 22) +#define RX_MPDU_DESC_INFO0_BAR_FRAME (1 << 23) +#define RX_MPDU_DESC_INFO0_VALID_PN (1 << 24) +#define RX_MPDU_DESC_INFO0_VALID_SA (1 << 25) +#define RX_MPDU_DESC_INFO0_SA_IDX_TIMEOUT (1 << 26) +#define RX_MPDU_DESC_INFO0_VALID_DA (1 << 27) +#define RX_MPDU_DESC_INFO0_DA_MCBC (1 << 28) +#define RX_MPDU_DESC_INFO0_DA_IDX_TIMEOUT (1 << 29) +#define RX_MPDU_DESC_INFO0_RAW_MPDU (1 << 30) + +#define RX_MPDU_DESC_META_DATA_PEER_ID 0xffff + +struct rx_mpdu_desc { + uint32_t info0; /* %RX_MPDU_DESC_INFO */ + uint32_t meta_data; +} __packed; + +/* rx_mpdu_desc + * Producer: RXDMA + * Consumer: REO/SW/FW + * + * msdu_count + * The number of MSDUs within the MPDU + * + * mpdu_sequence_number + * The field can have two different meanings based on the setting + * of field 'bar_frame'. If 'bar_frame' is set, it means the MPDU + * start sequence number from the BAR frame otherwise it means + * the MPDU sequence number of the received frame. + * + * fragment_flag + * When set, this MPDU is a fragment and REO should forward this + * fragment MPDU to the REO destination ring without any reorder + * checks, pn checks or bitmap update. This implies that REO is + * forwarding the pointer to the MSDU link descriptor. + * + * mpdu_retry_bit + * The retry bit setting from the MPDU header of the received frame + * + * ampdu_flag + * Indicates the MPDU was received as part of an A-MPDU. + * + * bar_frame + * Indicates the received frame is a BAR frame. After processing, + * this frame shall be pushed to SW or deleted. + * + * valid_pn + * When not set, REO will not perform a PN sequence number check. + * + * valid_sa + * Indicates OLE found a valid SA entry for all MSDUs in this MPDU. + * + * sa_idx_timeout + * Indicates, at least 1 MSDU within the MPDU has an unsuccessful + * MAC source address search due to the expiration of search timer. + * + * valid_da + * When set, OLE found a valid DA entry for all MSDUs in this MPDU. + * + * da_mcbc + * Field Only valid if valid_da is set. Indicates at least one of + * the DA addresses is a Multicast or Broadcast address. + * + * da_idx_timeout + * Indicates, at least 1 MSDU within the MPDU has an unsuccessful + * MAC destination address search due to the expiration of search + * timer. + * + * raw_mpdu + * Field only valid when first_msdu_in_mpdu_flag is set. Indicates + * the contents in the MSDU buffer contains a 'RAW' MPDU. + */ + +enum hal_rx_msdu_desc_reo_dest_ind { + HAL_RX_MSDU_DESC_REO_DEST_IND_TCL, + HAL_RX_MSDU_DESC_REO_DEST_IND_SW1, + HAL_RX_MSDU_DESC_REO_DEST_IND_SW2, + HAL_RX_MSDU_DESC_REO_DEST_IND_SW3, + HAL_RX_MSDU_DESC_REO_DEST_IND_SW4, + HAL_RX_MSDU_DESC_REO_DEST_IND_RELEASE, + HAL_RX_MSDU_DESC_REO_DEST_IND_FW, +}; + +#define RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU (1 << 0) +#define RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU (1 << 1) +#define RX_MSDU_DESC_INFO0_MSDU_CONTINUATION (1 << 2) +#define RX_MSDU_DESC_INFO0_MSDU_LENGTH GENMASK(16, 3) +#define RX_MSDU_DESC_INFO0_REO_DEST_IND GENMASK(21, 17) +#define RX_MSDU_DESC_INFO0_MSDU_DROP (1 << 22) +#define RX_MSDU_DESC_INFO0_VALID_SA (1 << 23) +#define RX_MSDU_DESC_INFO0_SA_IDX_TIMEOUT (1 << 24) +#define RX_MSDU_DESC_INFO0_VALID_DA (1 << 25) +#define RX_MSDU_DESC_INFO0_DA_MCBC (1 << 26) +#define RX_MSDU_DESC_INFO0_DA_IDX_TIMEOUT (1 << 27) + +#define HAL_RX_MSDU_PKT_LENGTH_GET(val) \ + (FIELD_GET(RX_MSDU_DESC_INFO0_MSDU_LENGTH, (val))) + +struct rx_msdu_desc { + uint32_t info0; + uint32_t rsvd0; +} __packed; + +/* rx_msdu_desc + * + * first_msdu_in_mpdu + * Indicates first msdu in mpdu. + * + * last_msdu_in_mpdu + * Indicates last msdu in mpdu. This flag can be true only when + * 'Msdu_continuation' set to 0. This implies that when an msdu + * is spread out over multiple buffers and thus msdu_continuation + * is set, only for the very last buffer of the msdu, can the + * 'last_msdu_in_mpdu' be set. + * + * When both first_msdu_in_mpdu and last_msdu_in_mpdu are set, + * the MPDU that this MSDU belongs to only contains a single MSDU. + * + * msdu_continuation + * When set, this MSDU buffer was not able to hold the entire MSDU. + * The next buffer will therefore contain additional information + * related to this MSDU. + * + * msdu_length + * Field is only valid in combination with the 'first_msdu_in_mpdu' + * being set. Full MSDU length in bytes after decapsulation. This + * field is still valid for MPDU frames without A-MSDU. It still + * represents MSDU length after decapsulation Or in case of RAW + * MPDUs, it indicates the length of the entire MPDU (without FCS + * field). + * + * reo_destination_indication + * The id of the reo exit ring where the msdu frame shall push + * after (MPDU level) reordering has finished. Values are defined + * in enum %HAL_RX_MSDU_DESC_REO_DEST_IND_. + * + * msdu_drop + * Indicates that REO shall drop this MSDU and not forward it to + * any other ring. + * + * valid_sa + * Indicates OLE found a valid SA entry for this MSDU. + * + * sa_idx_timeout + * Indicates, an unsuccessful MAC source address search due to + * the expiration of search timer for this MSDU. + * + * valid_da + * When set, OLE found a valid DA entry for this MSDU. + * + * da_mcbc + * Field Only valid if valid_da is set. Indicates the DA address + * is a Multicast or Broadcast address for this MSDU. + * + * da_idx_timeout + * Indicates, an unsuccessful MAC destination address search due + * to the expiration of search timer for this MSDU. + */ + +enum hal_reo_dest_ring_buffer_type { + HAL_REO_DEST_RING_BUFFER_TYPE_MSDU, + HAL_REO_DEST_RING_BUFFER_TYPE_LINK_DESC, +}; + +enum hal_reo_dest_ring_push_reason { + HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED, + HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION, +}; + +enum hal_reo_dest_ring_error_code { + HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO, + HAL_REO_DEST_RING_ERROR_CODE_DESC_INVALID, + HAL_REO_DEST_RING_ERROR_CODE_AMPDU_IN_NON_BA, + HAL_REO_DEST_RING_ERROR_CODE_NON_BA_DUPLICATE, + HAL_REO_DEST_RING_ERROR_CODE_BA_DUPLICATE, + HAL_REO_DEST_RING_ERROR_CODE_FRAME_2K_JUMP, + HAL_REO_DEST_RING_ERROR_CODE_BAR_2K_JUMP, + HAL_REO_DEST_RING_ERROR_CODE_FRAME_OOR, + HAL_REO_DEST_RING_ERROR_CODE_BAR_OOR, + HAL_REO_DEST_RING_ERROR_CODE_NO_BA_SESSION, + HAL_REO_DEST_RING_ERROR_CODE_FRAME_SN_EQUALS_SSN, + HAL_REO_DEST_RING_ERROR_CODE_PN_CHECK_FAILED, + HAL_REO_DEST_RING_ERROR_CODE_2K_ERR_FLAG_SET, + HAL_REO_DEST_RING_ERROR_CODE_PN_ERR_FLAG_SET, + HAL_REO_DEST_RING_ERROR_CODE_DESC_BLOCKED, + HAL_REO_DEST_RING_ERROR_CODE_MAX, +}; + +#define HAL_REO_DEST_RING_INFO0_QUEUE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_DEST_RING_INFO0_BUFFER_TYPE (1 << 8) +#define HAL_REO_DEST_RING_INFO0_PUSH_REASON GENMASK(10, 9) +#define HAL_REO_DEST_RING_INFO0_ERROR_CODE GENMASK(15, 11) +#define HAL_REO_DEST_RING_INFO0_RX_QUEUE_NUM GENMASK(31, 16) + +#define HAL_REO_DEST_RING_INFO1_REORDER_INFO_VALID (1 << 0) +#define HAL_REO_DEST_RING_INFO1_REORDER_OPCODE GENMASK(4, 1) +#define HAL_REO_DEST_RING_INFO1_REORDER_SLOT_IDX GENMASK(12, 5) + +#define HAL_REO_DEST_RING_INFO2_RING_ID GENMASK(27, 20) +#define HAL_REO_DEST_RING_INFO2_LOOPING_COUNT GENMASK(31, 28) + +struct hal_reo_dest_ring { + struct ath12k_buffer_addr buf_addr_info; + struct rx_mpdu_desc rx_mpdu_info; + struct rx_msdu_desc rx_msdu_info; + uint32_t queue_addr_lo; + uint32_t info0; /* %HAL_REO_DEST_RING_INFO0_ */ + uint32_t info1; /* %HAL_REO_DEST_RING_INFO1_ */ + uint32_t rsvd0; + uint32_t rsvd1; + uint32_t rsvd2; + uint32_t rsvd3; + uint32_t rsvd4; + uint32_t rsvd5; + uint32_t info2; /* %HAL_REO_DEST_RING_INFO2_ */ +} __packed; + +/* hal_reo_dest_ring + * + * Producer: RXDMA + * Consumer: REO/SW/FW + * + * buf_addr_info + * Details of the physical address of a buffer or MSDU + * link descriptor. + * + * rx_mpdu_info + * General information related to the MPDU that is passed + * on from REO entrance ring to the REO destination ring. + * + * rx_msdu_info + * General information related to the MSDU that is passed + * on from RXDMA all the way to the REO destination ring. + * + * queue_addr_lo + * Address (lower 32 bits) of the REO queue descriptor. + * + * queue_addr_hi + * Address (upper 8 bits) of the REO queue descriptor. + * + * buffer_type + * Indicates the type of address provided in the buf_addr_info. + * Values are defined in enum %HAL_REO_DEST_RING_BUFFER_TYPE_. + * + * push_reason + * Reason for pushing this frame to this exit ring. Values are + * defined in enum %HAL_REO_DEST_RING_PUSH_REASON_. + * + * error_code + * Valid only when 'push_reason' is set. All error codes are + * defined in enum %HAL_REO_DEST_RING_ERROR_CODE_. + * + * rx_queue_num + * Indicates the REO MPDU reorder queue id from which this frame + * originated. + * + * reorder_info_valid + * When set, REO has been instructed to not perform the actual + * re-ordering of frames for this queue, but just to insert + * the reorder opcodes. + * + * reorder_opcode + * Field is valid when 'reorder_info_valid' is set. This field is + * always valid for debug purpose as well. + * + * reorder_slot_idx + * Valid only when 'reorder_info_valid' is set. + * + * ring_id + * The buffer pointer ring id. + * 0 - Idle ring + * 1 - N refers to other rings. + * + * looping_count + * Indicates the number of times the producer of entries into + * this ring has looped around the ring. + */ + +enum hal_reo_entr_rxdma_ecode { + HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MPDU_LEN_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_FCS_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_UNECRYPTED_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MSDU_LEN_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MSDU_LIMIT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_WIFI_PARSE_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_PARSE_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_SA_TIMEOUT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_DA_TIMEOUT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_FLOW_TIMEOUT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MAX, +}; + +#define HAL_REO_ENTR_RING_INFO0_QUEUE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_ENTR_RING_INFO0_MPDU_BYTE_COUNT GENMASK(21, 8) +#define HAL_REO_ENTR_RING_INFO0_DEST_IND GENMASK(26, 22) +#define HAL_REO_ENTR_RING_INFO0_FRAMELESS_BAR BIT(27) + +#define HAL_REO_ENTR_RING_INFO1_RXDMA_PUSH_REASON GENMASK(1, 0) +#define HAL_REO_ENTR_RING_INFO1_RXDMA_ERROR_CODE GENMASK(6, 2) + +struct hal_reo_entrance_ring { + struct ath12k_buffer_addr buf_addr_info; + struct rx_mpdu_desc rx_mpdu_info; + uint32_t queue_addr_lo; + uint32_t info0; /* %HAL_REO_ENTR_RING_INFO0_ */ + uint32_t info1; /* %HAL_REO_ENTR_RING_INFO1_ */ + uint32_t info2; /* %HAL_REO_DEST_RING_INFO2_ */ + +} __packed; + +/* hal_reo_entrance_ring + * + * Producer: RXDMA + * Consumer: REO + * + * buf_addr_info + * Details of the physical address of a buffer or MSDU + * link descriptor. + * + * rx_mpdu_info + * General information related to the MPDU that is passed + * on from REO entrance ring to the REO destination ring. + * + * queue_addr_lo + * Address (lower 32 bits) of the REO queue descriptor. + * + * queue_addr_hi + * Address (upper 8 bits) of the REO queue descriptor. + * + * mpdu_byte_count + * An approximation of the number of bytes received in this MPDU. + * Used to keeps stats on the amount of data flowing + * through a queue. + * + * reo_destination_indication + * The id of the reo exit ring where the msdu frame shall push + * after (MPDU level) reordering has finished. Values are defined + * in enum %HAL_RX_MSDU_DESC_REO_DEST_IND_. + * + * frameless_bar + * Indicates that this REO entrance ring struct contains BAR info + * from a multi TID BAR frame. The original multi TID BAR frame + * itself contained all the REO info for the first TID, but all + * the subsequent TID info and their linkage to the REO descriptors + * is passed down as 'frameless' BAR info. + * + * The only fields valid in this descriptor when this bit is set + * are queue_addr_lo, queue_addr_hi, mpdu_sequence_number, + * bar_frame and peer_meta_data. + * + * rxdma_push_reason + * Reason for pushing this frame to this exit ring. Values are + * defined in enum %HAL_REO_DEST_RING_PUSH_REASON_. + * + * rxdma_error_code + * Valid only when 'push_reason' is set. All error codes are + * defined in enum %HAL_REO_ENTR_RING_RXDMA_ECODE_. + * + * ring_id + * The buffer pointer ring id. + * 0 - Idle ring + * 1 - N refers to other rings. + * + * looping_count + * Indicates the number of times the producer of entries into + * this ring has looped around the ring. + */ + +#define HAL_SW_MON_RING_INFO0_RXDMA_PUSH_REASON GENMASK(1, 0) +#define HAL_SW_MON_RING_INFO0_RXDMA_ERROR_CODE GENMASK(6, 2) +#define HAL_SW_MON_RING_INFO0_MPDU_FRAG_NUMBER GENMASK(10, 7) +#define HAL_SW_MON_RING_INFO0_FRAMELESS_BAR BIT(11) +#define HAL_SW_MON_RING_INFO0_STATUS_BUF_CNT GENMASK(15, 12) +#define HAL_SW_MON_RING_INFO0_END_OF_PPDU BIT(16) + +#define HAL_SW_MON_RING_INFO1_PHY_PPDU_ID GENMASK(15, 0) +#define HAL_SW_MON_RING_INFO1_RING_ID GENMASK(27, 20) +#define HAL_SW_MON_RING_INFO1_LOOPING_COUNT GENMASK(31, 28) + +struct hal_sw_monitor_ring { + struct ath12k_buffer_addr buf_addr_info; + struct rx_mpdu_desc rx_mpdu_info; + struct ath12k_buffer_addr status_buf_addr_info; + uint32_t info0; + uint32_t info1; +} __packed; + +#define HAL_REO_CMD_HDR_INFO0_CMD_NUMBER GENMASK(15, 0) +#define HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED BIT(16) + +struct hal_reo_cmd_hdr { + uint32_t info0; +} __packed; + + +#define HAL_SRNG_DESC_LOOP_CNT 0xf0000000 + +#define HAL_REO_CMD_FLG_NEED_STATUS BIT(0) +#define HAL_REO_CMD_FLG_STATS_CLEAR BIT(1) +#define HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER BIT(2) +#define HAL_REO_CMD_FLG_FLUSH_RELEASE_BLOCKING BIT(3) +#define HAL_REO_CMD_FLG_FLUSH_NO_INVAL BIT(4) +#define HAL_REO_CMD_FLG_FLUSH_FWD_ALL_MPDUS BIT(5) +#define HAL_REO_CMD_FLG_FLUSH_ALL BIT(6) +#define HAL_REO_CMD_FLG_UNBLK_RESOURCE BIT(7) +#define HAL_REO_CMD_FLG_UNBLK_CACHE BIT(8) + +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO0_UPD_* feilds */ +#define HAL_REO_CMD_UPD0_RX_QUEUE_NUM BIT(8) +#define HAL_REO_CMD_UPD0_VLD BIT(9) +#define HAL_REO_CMD_UPD0_ALDC BIT(10) +#define HAL_REO_CMD_UPD0_DIS_DUP_DETECTION BIT(11) +#define HAL_REO_CMD_UPD0_SOFT_REORDER_EN BIT(12) +#define HAL_REO_CMD_UPD0_AC BIT(13) +#define HAL_REO_CMD_UPD0_BAR BIT(14) +#define HAL_REO_CMD_UPD0_RETRY BIT(15) +#define HAL_REO_CMD_UPD0_CHECK_2K_MODE BIT(16) +#define HAL_REO_CMD_UPD0_OOR_MODE BIT(17) +#define HAL_REO_CMD_UPD0_BA_WINDOW_SIZE BIT(18) +#define HAL_REO_CMD_UPD0_PN_CHECK BIT(19) +#define HAL_REO_CMD_UPD0_EVEN_PN BIT(20) +#define HAL_REO_CMD_UPD0_UNEVEN_PN BIT(21) +#define HAL_REO_CMD_UPD0_PN_HANDLE_ENABLE BIT(22) +#define HAL_REO_CMD_UPD0_PN_SIZE BIT(23) +#define HAL_REO_CMD_UPD0_IGNORE_AMPDU_FLG BIT(24) +#define HAL_REO_CMD_UPD0_SVLD BIT(25) +#define HAL_REO_CMD_UPD0_SSN BIT(26) +#define HAL_REO_CMD_UPD0_SEQ_2K_ERR BIT(27) +#define HAL_REO_CMD_UPD0_PN_ERR BIT(28) +#define HAL_REO_CMD_UPD0_PN_VALID BIT(29) +#define HAL_REO_CMD_UPD0_PN BIT(30) + +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO1_* feilds */ +#define HAL_REO_CMD_UPD1_VLD BIT(16) +#define HAL_REO_CMD_UPD1_ALDC GENMASK(18, 17) +#define HAL_REO_CMD_UPD1_DIS_DUP_DETECTION BIT(19) +#define HAL_REO_CMD_UPD1_SOFT_REORDER_EN BIT(20) +#define HAL_REO_CMD_UPD1_AC GENMASK(22, 21) +#define HAL_REO_CMD_UPD1_BAR BIT(23) +#define HAL_REO_CMD_UPD1_RETRY BIT(24) +#define HAL_REO_CMD_UPD1_CHECK_2K_MODE BIT(25) +#define HAL_REO_CMD_UPD1_OOR_MODE BIT(26) +#define HAL_REO_CMD_UPD1_PN_CHECK BIT(27) +#define HAL_REO_CMD_UPD1_EVEN_PN BIT(28) +#define HAL_REO_CMD_UPD1_UNEVEN_PN BIT(29) +#define HAL_REO_CMD_UPD1_PN_HANDLE_ENABLE BIT(30) +#define HAL_REO_CMD_UPD1_IGNORE_AMPDU_FLG BIT(31) + +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO2_* feilds */ +#define HAL_REO_CMD_UPD2_SVLD BIT(10) +#define HAL_REO_CMD_UPD2_SSN GENMASK(22, 11) +#define HAL_REO_CMD_UPD2_SEQ_2K_ERR BIT(23) +#define HAL_REO_CMD_UPD2_PN_ERR BIT(24) + +#define HAL_REO_DEST_RING_CTRL_HASH_RING_MAP GENMASK(31, 8) + +struct ath12k_hal_reo_cmd { + uint32_t addr_lo; + uint32_t flag; + uint32_t upd0; + uint32_t upd1; + uint32_t upd2; + uint32_t pn[4]; + uint16_t rx_queue_num; + uint16_t min_rel; + uint16_t min_fwd; + uint8_t addr_hi; + uint8_t ac_list; + uint8_t blocking_idx; + uint16_t ba_window_size; + uint8_t pn_size; +}; + +#define HAL_REO_GET_QUEUE_STATS_INFO0_QUEUE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_GET_QUEUE_STATS_INFO0_CLEAR_STATS BIT(8) + +struct hal_reo_get_queue_stats { + struct hal_reo_cmd_hdr cmd; + uint32_t queue_addr_lo; + uint32_t info0; + uint32_t rsvd0[6]; +} __packed; + +/* hal_reo_get_queue_stats + * Producer: SW + * Consumer: REO + * + * cmd + * Details for command execution tracking purposes. + * + * queue_addr_lo + * Address (lower 32 bits) of the REO queue descriptor. + * + * queue_addr_hi + * Address (upper 8 bits) of the REO queue descriptor. + * + * clear_stats + * Clear stats settings. When set, Clear the stats after + * generating the status. + * + * Following stats will be cleared. + * Timeout_count + * Forward_due_to_bar_count + * Duplicate_count + * Frames_in_order_count + * BAR_received_count + * MPDU_Frames_processed_count + * MSDU_Frames_processed_count + * Total_processed_byte_count + * Late_receive_MPDU_count + * window_jump_2k + * Hole_count + */ + +#define HAL_REO_FLUSH_QUEUE_INFO0_DESC_ADDR_HI GENMASK(7, 0) +#define HAL_REO_FLUSH_QUEUE_INFO0_BLOCK_DESC_ADDR BIT(8) +#define HAL_REO_FLUSH_QUEUE_INFO0_BLOCK_RESRC_IDX GENMASK(10, 9) + +struct hal_reo_flush_queue { + struct hal_reo_cmd_hdr cmd; + uint32_t desc_addr_lo; + uint32_t info0; + uint32_t rsvd0[6]; +} __packed; + +#define HAL_REO_FLUSH_CACHE_INFO0_CACHE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_FLUSH_CACHE_INFO0_FWD_ALL_MPDUS BIT(8) +#define HAL_REO_FLUSH_CACHE_INFO0_RELEASE_BLOCK_IDX BIT(9) +#define HAL_REO_FLUSH_CACHE_INFO0_BLOCK_RESRC_IDX GENMASK(11, 10) +#define HAL_REO_FLUSH_CACHE_INFO0_FLUSH_WO_INVALIDATE BIT(12) +#define HAL_REO_FLUSH_CACHE_INFO0_BLOCK_CACHE_USAGE BIT(13) +#define HAL_REO_FLUSH_CACHE_INFO0_FLUSH_ALL BIT(14) + +struct hal_reo_flush_cache { + struct hal_reo_cmd_hdr cmd; + uint32_t cache_addr_lo; + uint32_t info0; + uint32_t rsvd0[6]; +} __packed; + +#define HAL_TCL_DATA_CMD_INFO0_DESC_TYPE BIT(0) +#define HAL_TCL_DATA_CMD_INFO0_EPD BIT(1) +#define HAL_TCL_DATA_CMD_INFO0_ENCAP_TYPE GENMASK(3, 2) +#define HAL_TCL_DATA_CMD_INFO0_ENCRYPT_TYPE GENMASK(7, 4) +#define HAL_TCL_DATA_CMD_INFO0_SRC_BUF_SWAP BIT(8) +#define HAL_TCL_DATA_CMD_INFO0_LNK_META_SWAP BIT(9) +#define HAL_TCL_DATA_CMD_INFO0_SEARCH_TYPE GENMASK(13, 12) +#define HAL_TCL_DATA_CMD_INFO0_ADDR_EN GENMASK(15, 14) +#define HAL_TCL_DATA_CMD_INFO0_CMD_NUM GENMASK(31, 16) + +#define HAL_TCL_DATA_CMD_INFO1_DATA_LEN GENMASK(15, 0) +#define HAL_TCL_DATA_CMD_INFO1_IP4_CKSUM_EN BIT(16) +#define HAL_TCL_DATA_CMD_INFO1_UDP4_CKSUM_EN BIT(17) +#define HAL_TCL_DATA_CMD_INFO1_UDP6_CKSUM_EN BIT(18) +#define HAL_TCL_DATA_CMD_INFO1_TCP4_CKSUM_EN BIT(19) +#define HAL_TCL_DATA_CMD_INFO1_TCP6_CKSUM_EN BIT(20) +#define HAL_TCL_DATA_CMD_INFO1_TO_FW BIT(21) +#define HAL_TCL_DATA_CMD_INFO1_PKT_OFFSET GENMASK(31, 23) + +#define HAL_TCL_DATA_CMD_INFO2_BUF_TIMESTAMP GENMASK(18, 0) +#define HAL_TCL_DATA_CMD_INFO2_BUF_T_VALID BIT(19) +#define HAL_IPQ8074_TCL_DATA_CMD_INFO2_MESH_ENABLE BIT(20) +#define HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE BIT(21) +#define HAL_TCL_DATA_CMD_INFO2_TID GENMASK(25, 22) +#define HAL_TCL_DATA_CMD_INFO2_LMAC_ID GENMASK(27, 26) + +#define HAL_TCL_DATA_CMD_INFO3_DSCP_TID_TABLE_IDX GENMASK(5, 0) +#define HAL_TCL_DATA_CMD_INFO3_SEARCH_INDEX GENMASK(25, 6) +#define HAL_TCL_DATA_CMD_INFO3_CACHE_SET_NUM GENMASK(29, 26) +#define HAL_QCN9074_TCL_DATA_CMD_INFO3_MESH_ENABLE GENMASK(31, 30) + +#define HAL_TCL_DATA_CMD_INFO4_RING_ID GENMASK(27, 20) +#define HAL_TCL_DATA_CMD_INFO4_LOOPING_COUNT GENMASK(31, 28) + +enum hal_encrypt_type { + HAL_ENCRYPT_TYPE_WEP_40, + HAL_ENCRYPT_TYPE_WEP_104, + HAL_ENCRYPT_TYPE_TKIP_NO_MIC, + HAL_ENCRYPT_TYPE_WEP_128, + HAL_ENCRYPT_TYPE_TKIP_MIC, + HAL_ENCRYPT_TYPE_WAPI, + HAL_ENCRYPT_TYPE_CCMP_128, + HAL_ENCRYPT_TYPE_OPEN, + HAL_ENCRYPT_TYPE_CCMP_256, + HAL_ENCRYPT_TYPE_GCMP_128, + HAL_ENCRYPT_TYPE_AES_GCMP_256, + HAL_ENCRYPT_TYPE_WAPI_GCM_SM4, +}; + +enum hal_tcl_encap_type { + HAL_TCL_ENCAP_TYPE_RAW, + HAL_TCL_ENCAP_TYPE_NATIVE_WIFI, + HAL_TCL_ENCAP_TYPE_ETHERNET, + HAL_TCL_ENCAP_TYPE_802_3 = 3, +}; + +enum hal_tcl_desc_type { + HAL_TCL_DESC_TYPE_BUFFER, + HAL_TCL_DESC_TYPE_EXT_DESC, +}; + +enum hal_wbm_htt_tx_comp_status { + HAL_WBM_REL_HTT_TX_COMP_STATUS_OK, + HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP, + HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL, + HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ, + HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT, + HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY, +}; + +struct hal_tcl_data_cmd { + struct ath12k_buffer_addr buf_addr_info; + uint32_t info0; + uint32_t info1; + uint32_t info2; + uint32_t info3; + uint32_t info4; +} __packed; + +/* hal_tcl_data_cmd + * + * buf_addr_info + * Details of the physical address of a buffer or MSDU + * link descriptor. + * + * desc_type + * Indicates the type of address provided in the buf_addr_info. + * Values are defined in enum %HAL_REO_DEST_RING_BUFFER_TYPE_. + * + * epd + * When this bit is set then input packet is an EPD type. + * + * encap_type + * Indicates the encapsulation that HW will perform. Values are + * defined in enum %HAL_TCL_ENCAP_TYPE_. + * + * encrypt_type + * Field only valid for encap_type: RAW + * Values are defined in enum %HAL_ENCRYPT_TYPE_. + * + * src_buffer_swap + * Treats source memory (packet buffer) organization as big-endian. + * 1'b0: Source memory is little endian + * 1'b1: Source memory is big endian + * + * link_meta_swap + * Treats link descriptor and Metadata as big-endian. + * 1'b0: memory is little endian + * 1'b1: memory is big endian + * + * search_type + * Search type select + * 0 - Normal search, 1 - Index based address search, + * 2 - Index based flow search + * + * addrx_en + * addry_en + * Address X/Y search enable in ASE correspondingly. + * 1'b0: Search disable + * 1'b1: Search Enable + * + * cmd_num + * This number can be used to match against status. + * + * data_length + * MSDU length in case of direct descriptor. Length of link + * extension descriptor in case of Link extension descriptor. + * + * *_checksum_en + * Enable checksum replacement for ipv4, udp_over_ipv4, ipv6, + * udp_over_ipv6, tcp_over_ipv4 and tcp_over_ipv6. + * + * to_fw + * Forward packet to FW along with classification result. The + * packet will not be forward to TQM when this bit is set. + * 1'b0: Use classification result to forward the packet. + * 1'b1: Override classification result & forward packet only to fw + * + * packet_offset + * Packet offset from Metadata in case of direct buffer descriptor. + * + * buffer_timestamp + * buffer_timestamp_valid + * Frame system entrance timestamp. It shall be filled by first + * module (SW, TCL or TQM) that sees the frames first. + * + * mesh_enable + * For raw WiFi frames, this indicates transmission to a mesh STA, + * enabling the interpretation of the 'Mesh Control Present' bit + * (bit 8) of QoS Control. + * For native WiFi frames, this indicates that a 'Mesh Control' + * field is present between the header and the LLC. + * + * hlos_tid_overwrite + * + * When set, TCL shall ignore the IP DSCP and VLAN PCP + * fields and use HLOS_TID as the final TID. Otherwise TCL + * shall consider the DSCP and PCP fields as well as HLOS_TID + * and choose a final TID based on the configured priority + * + * hlos_tid + * HLOS MSDU priority + * Field is used when HLOS_TID_overwrite is set. + * + * lmac_id + * TCL uses this LMAC_ID in address search, i.e, while + * finding matching entry for the packet in AST corresponding + * to given LMAC_ID + * + * If LMAC ID is all 1s (=> value 3), it indicates wildcard + * match for any MAC + * + * dscp_tid_table_num + * DSCP to TID mapping table number that need to be used + * for the MSDU. + * + * search_index + * The index that will be used for index based address or + * flow search. The field is valid when 'search_type' is 1 or 2. + * + * cache_set_num + * + * Cache set number that should be used to cache the index + * based search results, for address and flow search. This + * value should be equal to LSB four bits of the hash value of + * match data, in case of search index points to an entry which + * may be used in content based search also. The value can be + * anything when the entry pointed by search index will not be + * used for content based search. + * + * ring_id + * The buffer pointer ring ID. + * 0 refers to the IDLE ring + * 1 - N refers to other rings + * + * looping_count + * + * A count value that indicates the number of times the + * producer of entries into the Ring has looped around the + * ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +#define HAL_TCL_DESC_LEN sizeof(struct hal_tcl_data_cmd) + +enum hal_tcl_gse_ctrl { + HAL_TCL_GSE_CTRL_RD_STAT, + HAL_TCL_GSE_CTRL_SRCH_DIS, + HAL_TCL_GSE_CTRL_WR_BK_SINGLE, + HAL_TCL_GSE_CTRL_WR_BK_ALL, + HAL_TCL_GSE_CTRL_INVAL_SINGLE, + HAL_TCL_GSE_CTRL_INVAL_ALL, + HAL_TCL_GSE_CTRL_WR_BK_INVAL_SINGLE, + HAL_TCL_GSE_CTRL_WR_BK_INVAL_ALL, + HAL_TCL_GSE_CTRL_CLR_STAT_SINGLE, +}; + +/* hal_tcl_gse_ctrl + * + * rd_stat + * Report or Read statistics + * srch_dis + * Search disable. Report only Hash. + * wr_bk_single + * Write Back single entry + * wr_bk_all + * Write Back entire cache entry + * inval_single + * Invalidate single cache entry + * inval_all + * Invalidate entire cache + * wr_bk_inval_single + * Write back and invalidate single entry in cache + * wr_bk_inval_all + * Write back and invalidate entire cache + * clr_stat_single + * Clear statistics for single entry + */ + +#define HAL_TCL_GSE_CMD_INFO0_CTRL_BUF_ADDR_HI GENMASK(7, 0) +#define HAL_TCL_GSE_CMD_INFO0_GSE_CTRL GENMASK(11, 8) +#define HAL_TCL_GSE_CMD_INFO0_GSE_SEL BIT(12) +#define HAL_TCL_GSE_CMD_INFO0_STATUS_DEST_RING_ID BIT(13) +#define HAL_TCL_GSE_CMD_INFO0_SWAP BIT(14) + +#define HAL_TCL_GSE_CMD_INFO1_RING_ID GENMASK(27, 20) +#define HAL_TCL_GSE_CMD_INFO1_LOOPING_COUNT GENMASK(31, 28) + +struct hal_tcl_gse_cmd { + uint32_t ctrl_buf_addr_lo; + uint32_t info0; + uint32_t meta_data[2]; + uint32_t rsvd0[2]; + uint32_t info1; +} __packed; + +/* hal_tcl_gse_cmd + * + * ctrl_buf_addr_lo, ctrl_buf_addr_hi + * Address of a control buffer containing additional info needed + * for this command execution. + * + * gse_ctrl + * GSE control operations. This includes cache operations and table + * entry statistics read/clear operation. Values are defined in + * enum %HAL_TCL_GSE_CTRL. + * + * gse_sel + * To select the ASE/FSE to do the operation mention by GSE_ctrl. + * 0: FSE select 1: ASE select + * + * status_destination_ring_id + * TCL status ring to which the GSE status needs to be send. + * + * swap + * Bit to enable byte swapping of contents of buffer. + * + * meta_data + * Meta data to be returned in the status descriptor + */ + +enum hal_tcl_cache_op_res { + HAL_TCL_CACHE_OP_RES_DONE, + HAL_TCL_CACHE_OP_RES_NOT_FOUND, + HAL_TCL_CACHE_OP_RES_TIMEOUT, +}; + +#define HAL_TCL_STATUS_RING_INFO0_GSE_CTRL GENMASK(3, 0) +#define HAL_TCL_STATUS_RING_INFO0_GSE_SEL BIT(4) +#define HAL_TCL_STATUS_RING_INFO0_CACHE_OP_RES GENMASK(6, 5) +#define HAL_TCL_STATUS_RING_INFO0_MSDU_CNT GENMASK(31, 8) + +#define HAL_TCL_STATUS_RING_INFO1_HASH_IDX GENMASK(19, 0) + +#define HAL_TCL_STATUS_RING_INFO2_RING_ID GENMASK(27, 20) +#define HAL_TCL_STATUS_RING_INFO2_LOOPING_COUNT GENMASK(31, 28) + +struct hal_tcl_status_ring { + uint32_t info0; + uint32_t msdu_byte_count; + uint32_t msdu_timestamp; + uint32_t meta_data[2]; + uint32_t info1; + uint32_t rsvd0; + uint32_t info2; +} __packed; + +/* hal_tcl_status_ring + * + * gse_ctrl + * GSE control operations. This includes cache operations and table + * entry statistics read/clear operation. Values are defined in + * enum %HAL_TCL_GSE_CTRL. + * + * gse_sel + * To select the ASE/FSE to do the operation mention by GSE_ctrl. + * 0: FSE select 1: ASE select + * + * cache_op_res + * Cache operation result. Values are defined in enum + * %HAL_TCL_CACHE_OP_RES_. + * + * msdu_cnt + * msdu_byte_count + * MSDU count of Entry and MSDU byte count for entry 1. + * + * hash_indx + * Hash value of the entry in case of search failed or disabled. + */ + +#define HAL_CE_SRC_DESC_ADDR_INFO_ADDR_HI GENMASK(7, 0) +#define HAL_CE_SRC_DESC_ADDR_INFO_HASH_EN BIT(8) +#define HAL_CE_SRC_DESC_ADDR_INFO_BYTE_SWAP BIT(9) +#define HAL_CE_SRC_DESC_ADDR_INFO_DEST_SWAP BIT(10) +#define HAL_CE_SRC_DESC_ADDR_INFO_GATHER BIT(11) +#define HAL_CE_SRC_DESC_ADDR_INFO_LEN GENMASK(31, 16) + +#define HAL_CE_SRC_DESC_META_INFO_DATA GENMASK(15, 0) + +#define HAL_CE_SRC_DESC_FLAGS_RING_ID GENMASK(27, 20) +#define HAL_CE_SRC_DESC_FLAGS_LOOP_CNT HAL_SRNG_DESC_LOOP_CNT + +struct hal_ce_srng_src_desc { + uint32_t buffer_addr_low; + uint32_t buffer_addr_info; /* %HAL_CE_SRC_DESC_ADDR_INFO_ */ + uint32_t meta_info; /* %HAL_CE_SRC_DESC_META_INFO_ */ + uint32_t flags; /* %HAL_CE_SRC_DESC_FLAGS_ */ +} __packed; + +/* + * hal_ce_srng_src_desc + * + * buffer_addr_lo + * LSB 32 bits of the 40 Bit Pointer to the source buffer + * + * buffer_addr_hi + * MSB 8 bits of the 40 Bit Pointer to the source buffer + * + * toeplitz_en + * Enable generation of 32-bit Toeplitz-LFSR hash for + * data transfer. In case of gather field in first source + * ring entry of the gather copy cycle in taken into account. + * + * src_swap + * Treats source memory organization as big-endian. For + * each dword read (4 bytes), the byte 0 is swapped with byte 3 + * and byte 1 is swapped with byte 2. + * In case of gather field in first source ring entry of + * the gather copy cycle in taken into account. + * + * dest_swap + * Treats destination memory organization as big-endian. + * For each dword write (4 bytes), the byte 0 is swapped with + * byte 3 and byte 1 is swapped with byte 2. + * In case of gather field in first source ring entry of + * the gather copy cycle in taken into account. + * + * gather + * Enables gather of multiple copy engine source + * descriptors to one destination. + * + * ce_res_0 + * Reserved + * + * + * length + * Length of the buffer in units of octets of the current + * descriptor + * + * fw_metadata + * Meta data used by FW. + * In case of gather field in first source ring entry of + * the gather copy cycle in taken into account. + * + * ce_res_1 + * Reserved + * + * ce_res_2 + * Reserved + * + * ring_id + * The buffer pointer ring ID. + * 0 refers to the IDLE ring + * 1 - N refers to other rings + * Helps with debugging when dumping ring contents. + * + * looping_count + * A count value that indicates the number of times the + * producer of entries into the Ring has looped around the + * ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +#define HAL_CE_DEST_DESC_ADDR_INFO_ADDR_HI GENMASK(7, 0) +#define HAL_CE_DEST_DESC_ADDR_INFO_RING_ID GENMASK(27, 20) +#define HAL_CE_DEST_DESC_ADDR_INFO_LOOP_CNT HAL_SRNG_DESC_LOOP_CNT + +struct hal_ce_srng_dest_desc { + uint32_t buffer_addr_low; + uint32_t buffer_addr_info; /* %HAL_CE_DEST_DESC_ADDR_INFO_ */ +} __packed; + +/* hal_ce_srng_dest_desc + * + * dst_buffer_low + * LSB 32 bits of the 40 Bit Pointer to the Destination + * buffer + * + * dst_buffer_high + * MSB 8 bits of the 40 Bit Pointer to the Destination + * buffer + * + * ce_res_4 + * Reserved + * + * ring_id + * The buffer pointer ring ID. + * 0 refers to the IDLE ring + * 1 - N refers to other rings + * Helps with debugging when dumping ring contents. + * + * looping_count + * A count value that indicates the number of times the + * producer of entries into the Ring has looped around the + * ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +#define HAL_CE_DST_STATUS_DESC_FLAGS_HASH_EN BIT(8) +#define HAL_CE_DST_STATUS_DESC_FLAGS_BYTE_SWAP BIT(9) +#define HAL_CE_DST_STATUS_DESC_FLAGS_DEST_SWAP BIT(10) +#define HAL_CE_DST_STATUS_DESC_FLAGS_GATHER BIT(11) +#define HAL_CE_DST_STATUS_DESC_FLAGS_LEN GENMASK(31, 16) + +#define HAL_CE_DST_STATUS_DESC_META_INFO_DATA GENMASK(15, 0) +#define HAL_CE_DST_STATUS_DESC_META_INFO_RING_ID GENMASK(27, 20) +#define HAL_CE_DST_STATUS_DESC_META_INFO_LOOP_CNT HAL_SRNG_DESC_LOOP_CNT + +struct hal_ce_srng_dst_status_desc { + uint32_t flags; /* %HAL_CE_DST_STATUS_DESC_FLAGS_ */ + uint32_t toeplitz_hash0; + uint32_t toeplitz_hash1; + uint32_t meta_info; /* HAL_CE_DST_STATUS_DESC_META_INFO_ */ +} __packed; + +/* hal_ce_srng_dst_status_desc + * + * ce_res_5 + * Reserved + * + * toeplitz_en + * + * src_swap + * Source memory buffer swapped + * + * dest_swap + * Destination memory buffer swapped + * + * gather + * Gather of multiple copy engine source descriptors to one + * destination enabled + * + * ce_res_6 + * Reserved + * + * length + * Sum of all the Lengths of the source descriptor in the + * gather chain + * + * toeplitz_hash_0 + * 32 LS bits of 64 bit Toeplitz LFSR hash result + * + * toeplitz_hash_1 + * 32 MS bits of 64 bit Toeplitz LFSR hash result + * + * fw_metadata + * Meta data used by FW + * In case of gather field in first source ring entry of + * the gather copy cycle in taken into account. + * + * ce_res_7 + * Reserved + * + * ring_id + * The buffer pointer ring ID. + * 0 refers to the IDLE ring + * 1 - N refers to other rings + * Helps with debugging when dumping ring contents. + * + * looping_count + * A count value that indicates the number of times the + * producer of entries into the Ring has looped around the + * ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +#define HAL_TX_RATE_STATS_INFO0_VALID BIT(0) +#define HAL_TX_RATE_STATS_INFO0_BW GENMASK(2, 1) +#define HAL_TX_RATE_STATS_INFO0_PKT_TYPE GENMASK(6, 3) +#define HAL_TX_RATE_STATS_INFO0_STBC BIT(7) +#define HAL_TX_RATE_STATS_INFO0_LDPC BIT(8) +#define HAL_TX_RATE_STATS_INFO0_SGI GENMASK(10, 9) +#define HAL_TX_RATE_STATS_INFO0_MCS GENMASK(14, 11) +#define HAL_TX_RATE_STATS_INFO0_OFDMA_TX BIT(15) +#define HAL_TX_RATE_STATS_INFO0_TONES_IN_RU GENMASK(27, 16) + +enum hal_tx_rate_stats_bw { + HAL_TX_RATE_STATS_BW_20, + HAL_TX_RATE_STATS_BW_40, + HAL_TX_RATE_STATS_BW_80, + HAL_TX_RATE_STATS_BW_160, +}; + +enum hal_tx_rate_stats_pkt_type { + HAL_TX_RATE_STATS_PKT_TYPE_11A, + HAL_TX_RATE_STATS_PKT_TYPE_11B, + HAL_TX_RATE_STATS_PKT_TYPE_11N, + HAL_TX_RATE_STATS_PKT_TYPE_11AC, + HAL_TX_RATE_STATS_PKT_TYPE_11AX, +}; + +enum hal_tx_rate_stats_sgi { + HAL_TX_RATE_STATS_SGI_08US, + HAL_TX_RATE_STATS_SGI_04US, + HAL_TX_RATE_STATS_SGI_16US, + HAL_TX_RATE_STATS_SGI_32US, +}; + +struct hal_tx_rate_stats { + uint32_t info0; + uint32_t tsf; +} __packed; + +struct hal_wbm_link_desc { + struct ath12k_buffer_addr buf_addr_info; +} __packed; + +/* hal_wbm_link_desc + * + * Producer: WBM + * Consumer: WBM + * + * buf_addr_info + * Details of the physical address of a buffer or MSDU + * link descriptor. + */ + +enum hal_wbm_rel_src_module { + HAL_WBM_REL_SRC_MODULE_TQM, + HAL_WBM_REL_SRC_MODULE_RXDMA, + HAL_WBM_REL_SRC_MODULE_REO, + HAL_WBM_REL_SRC_MODULE_FW, + HAL_WBM_REL_SRC_MODULE_SW, +}; + +enum hal_wbm_rel_desc_type { + HAL_WBM_REL_DESC_TYPE_REL_MSDU, + HAL_WBM_REL_DESC_TYPE_MSDU_LINK, + HAL_WBM_REL_DESC_TYPE_MPDU_LINK, + HAL_WBM_REL_DESC_TYPE_MSDU_EXT, + HAL_WBM_REL_DESC_TYPE_QUEUE_EXT, +}; + +/* hal_wbm_rel_desc_type + * + * msdu_buffer + * The address points to an MSDU buffer + * + * msdu_link_descriptor + * The address points to an Tx MSDU link descriptor + * + * mpdu_link_descriptor + * The address points to an MPDU link descriptor + * + * msdu_ext_descriptor + * The address points to an MSDU extension descriptor + * + * queue_ext_descriptor + * The address points to an TQM queue extension descriptor. WBM should + * treat this is the same way as a link descriptor. + */ + +enum hal_wbm_rel_bm_act { + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE, + HAL_WBM_REL_BM_ACT_REL_MSDU, +}; + +/* hal_wbm_rel_bm_act + * + * put_in_idle_list + * Put the buffer or descriptor back in the idle list. In case of MSDU or + * MDPU link descriptor, BM does not need to check to release any + * individual MSDU buffers. + * + * release_msdu_list + * This BM action can only be used in combination with desc_type being + * msdu_link_descriptor. Field first_msdu_index points out which MSDU + * pointer in the MSDU link descriptor is the first of an MPDU that is + * released. BM shall release all the MSDU buffers linked to this first + * MSDU buffer pointer. All related MSDU buffer pointer entries shall be + * set to value 0, which represents the 'NULL' pointer. When all MSDU + * buffer pointers in the MSDU link descriptor are 'NULL', the MSDU link + * descriptor itself shall also be released. + */ + +#define HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE GENMASK(2, 0) +#define HAL_WBM_RELEASE_INFO0_BM_ACTION GENMASK(5, 3) +#define HAL_WBM_RELEASE_INFO0_DESC_TYPE GENMASK(8, 6) +#define HAL_WBM_RELEASE_INFO0_FIRST_MSDU_IDX GENMASK(12, 9) +#define HAL_WBM_RELEASE_INFO0_TQM_RELEASE_REASON GENMASK(16, 13) +#define HAL_WBM_RELEASE_INFO0_RXDMA_PUSH_REASON GENMASK(18, 17) +#define HAL_WBM_RELEASE_INFO0_RXDMA_ERROR_CODE GENMASK(23, 19) +#define HAL_WBM_RELEASE_INFO0_REO_PUSH_REASON GENMASK(25, 24) +#define HAL_WBM_RELEASE_INFO0_REO_ERROR_CODE GENMASK(30, 26) +#define HAL_WBM_RELEASE_INFO0_WBM_INTERNAL_ERROR BIT(31) + +#define HAL_WBM_RELEASE_INFO1_TQM_STATUS_NUMBER GENMASK(23, 0) +#define HAL_WBM_RELEASE_INFO1_TRANSMIT_COUNT GENMASK(30, 24) + +#define HAL_WBM_RELEASE_INFO2_ACK_FRAME_RSSI GENMASK(7, 0) +#define HAL_WBM_RELEASE_INFO2_SW_REL_DETAILS_VALID BIT(8) +#define HAL_WBM_RELEASE_INFO2_FIRST_MSDU BIT(9) +#define HAL_WBM_RELEASE_INFO2_LAST_MSDU BIT(10) +#define HAL_WBM_RELEASE_INFO2_MSDU_IN_AMSDU BIT(11) +#define HAL_WBM_RELEASE_INFO2_FW_TX_NOTIF_FRAME BIT(12) +#define HAL_WBM_RELEASE_INFO2_BUFFER_TIMESTAMP GENMASK(31, 13) + +#define HAL_WBM_RELEASE_INFO3_PEER_ID GENMASK(15, 0) +#define HAL_WBM_RELEASE_INFO3_TID GENMASK(19, 16) +#define HAL_WBM_RELEASE_INFO3_RING_ID GENMASK(27, 20) +#define HAL_WBM_RELEASE_INFO3_LOOPING_COUNT GENMASK(31, 28) + +#define HAL_WBM_REL_HTT_TX_COMP_INFO0_STATUS GENMASK(12, 9) +#define HAL_WBM_REL_HTT_TX_COMP_INFO0_REINJ_REASON GENMASK(16, 13) +#define HAL_WBM_REL_HTT_TX_COMP_INFO0_EXP_FRAME BIT(17) + +struct hal_wbm_release_ring { + struct ath12k_buffer_addr buf_addr_info; + uint32_t info0; + uint32_t info1; + uint32_t info2; + struct hal_tx_rate_stats rate_stats; + uint32_t info3; +} __packed; + +/* hal_wbm_release_ring + * + * Producer: SW/TQM/RXDMA/REO/SWITCH + * Consumer: WBM/SW/FW + * + * HTT tx status is overlaid on wbm_release ring on 4-byte words 2, 3, 4 and 5 + * for software based completions. + * + * buf_addr_info + * Details of the physical address of the buffer or link descriptor. + * + * release_source_module + * Indicates which module initiated the release of this buffer/descriptor. + * Values are defined in enum %HAL_WBM_REL_SRC_MODULE_. + * + * bm_action + * Field only valid when the field return_buffer_manager in + * Released_buff_or_desc_addr_info indicates: + * WBM_IDLE_BUF_LIST / WBM_IDLE_DESC_LIST + * Values are defined in enum %HAL_WBM_REL_BM_ACT_. + * + * buffer_or_desc_type + * Field only valid when WBM is marked as the return_buffer_manager in + * the Released_Buffer_address_info. Indicates that type of buffer or + * descriptor is being released. Values are in enum %HAL_WBM_REL_DESC_TYPE. + * + * first_msdu_index + * Field only valid for the bm_action release_msdu_list. The index of the + * first MSDU in an MSDU link descriptor all belonging to the same MPDU. + * + * tqm_release_reason + * Field only valid when Release_source_module is set to release_source_TQM + * Release reasons are defined in enum %HAL_WBM_TQM_REL_REASON_. + * + * rxdma_push_reason + * reo_push_reason + * Indicates why rxdma/reo pushed the frame to this ring and values are + * defined in enum %HAL_REO_DEST_RING_PUSH_REASON_. + * + * rxdma_error_code + * Field only valid when 'rxdma_push_reason' set to 'error_detected'. + * Values are defined in enum %HAL_REO_ENTR_RING_RXDMA_ECODE_. + * + * reo_error_code + * Field only valid when 'reo_push_reason' set to 'error_detected'. Values + * are defined in enum %HAL_REO_DEST_RING_ERROR_CODE_. + * + * wbm_internal_error + * Is set when WBM got a buffer pointer but the action was to push it to + * the idle link descriptor ring or do link related activity OR + * Is set when WBM got a link buffer pointer but the action was to push it + * to the buffer descriptor ring. + * + * tqm_status_number + * The value in this field is equal to tqm_cmd_number in TQM command. It is + * used to correlate the statu with TQM commands. Only valid when + * release_source_module is TQM. + * + * transmit_count + * The number of times the frame has been transmitted, valid only when + * release source in TQM. + * + * ack_frame_rssi + * This field is only valid when the source is TQM. If this frame is + * removed as the result of the reception of an ACK or BA, this field + * indicates the RSSI of the received ACK or BA frame. + * + * sw_release_details_valid + * This is set when WMB got a 'release_msdu_list' command from TQM and + * return buffer manager is not WMB. WBM will then de-aggregate all MSDUs + * and pass them one at a time on to the 'buffer owner'. + * + * first_msdu + * Field only valid when SW_release_details_valid is set. + * When set, this MSDU is the first MSDU pointed to in the + * 'release_msdu_list' command. + * + * last_msdu + * Field only valid when SW_release_details_valid is set. + * When set, this MSDU is the last MSDU pointed to in the + * 'release_msdu_list' command. + * + * msdu_part_of_amsdu + * Field only valid when SW_release_details_valid is set. + * When set, this MSDU was part of an A-MSDU in MPDU + * + * fw_tx_notify_frame + * Field only valid when SW_release_details_valid is set. + * + * buffer_timestamp + * Field only valid when SW_release_details_valid is set. + * This is the Buffer_timestamp field from the + * Timestamp in units of 1024 us + * + * struct hal_tx_rate_stats rate_stats + * Details for command execution tracking purposes. + * + * sw_peer_id + * tid + * Field only valid when Release_source_module is set to + * release_source_TQM + * + * 1) Release of msdu buffer due to drop_frame = 1. Flow is + * not fetched and hence sw_peer_id and tid = 0 + * + * buffer_or_desc_type = e_num 0 + * MSDU_rel_buffertqm_release_reason = e_num 1 + * tqm_rr_rem_cmd_rem + * + * 2) Release of msdu buffer due to Flow is not fetched and + * hence sw_peer_id and tid = 0 + * + * buffer_or_desc_type = e_num 0 + * MSDU_rel_buffertqm_release_reason = e_num 1 + * tqm_rr_rem_cmd_rem + * + * 3) Release of msdu link due to remove_mpdu or acked_mpdu + * command. + * + * buffer_or_desc_type = e_num1 + * msdu_link_descriptortqm_release_reason can be:e_num 1 + * tqm_rr_rem_cmd_reme_num 2 tqm_rr_rem_cmd_tx + * e_num 3 tqm_rr_rem_cmd_notxe_num 4 tqm_rr_rem_cmd_aged + * + * This field represents the TID from the TX_MSDU_FLOW + * descriptor or TX_MPDU_QUEUE descriptor + * + * rind_id + * For debugging. + * This field is filled in by the SRNG module. + * It help to identify the ring that is being looked + * + * looping_count + * A count value that indicates the number of times the + * producer of entries into the Buffer Manager Ring has looped + * around the ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +/** + * enum hal_wbm_tqm_rel_reason - TQM release reason code + * @HAL_WBM_TQM_REL_REASON_FRAME_ACKED: ACK or BACK received for the frame + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_MPDU: Command remove_mpdus initiated by SW + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX: Command remove transmitted_mpdus + * initiated by sw. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_NOTX: Command remove untransmitted_mpdus + * initiated by sw. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_AGED_FRAMES: Command remove aged msdus or + * mpdus. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON1: Remove command initiated by + * fw with fw_reason1. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON2: Remove command initiated by + * fw with fw_reason2. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON3: Remove command initiated by + * fw with fw_reason3. + */ +enum hal_wbm_tqm_rel_reason { + HAL_WBM_TQM_REL_REASON_FRAME_ACKED, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_MPDU, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_NOTX, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_AGED_FRAMES, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON1, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON2, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON3, +}; + +struct hal_wbm_buffer_ring { + struct ath12k_buffer_addr buf_addr_info; +}; + +enum hal_desc_owner { + HAL_DESC_OWNER_WBM, + HAL_DESC_OWNER_SW, + HAL_DESC_OWNER_TQM, + HAL_DESC_OWNER_RXDMA, + HAL_DESC_OWNER_REO, + HAL_DESC_OWNER_SWITCH, +}; + +enum hal_desc_buf_type { + HAL_DESC_BUF_TYPE_TX_MSDU_LINK, + HAL_DESC_BUF_TYPE_TX_MPDU_LINK, + HAL_DESC_BUF_TYPE_TX_MPDU_QUEUE_HEAD, + HAL_DESC_BUF_TYPE_TX_MPDU_QUEUE_EXT, + HAL_DESC_BUF_TYPE_TX_FLOW, + HAL_DESC_BUF_TYPE_TX_BUFFER, + HAL_DESC_BUF_TYPE_RX_MSDU_LINK, + HAL_DESC_BUF_TYPE_RX_MPDU_LINK, + HAL_DESC_BUF_TYPE_RX_REO_QUEUE, + HAL_DESC_BUF_TYPE_RX_REO_QUEUE_EXT, + HAL_DESC_BUF_TYPE_RX_BUFFER, + HAL_DESC_BUF_TYPE_IDLE_LINK, +}; + +#define HAL_DESC_REO_OWNED 4 +#define HAL_DESC_REO_QUEUE_DESC 8 +#define HAL_DESC_REO_QUEUE_EXT_DESC 9 +#define HAL_DESC_REO_NON_QOS_TID 16 + +#define HAL_DESC_HDR_INFO0_OWNER GENMASK(3, 0) +#define HAL_DESC_HDR_INFO0_BUF_TYPE GENMASK(7, 4) +#define HAL_DESC_HDR_INFO0_DBG_RESERVED GENMASK(31, 8) + +struct hal_desc_header { + uint32_t info0; +} __packed; + +struct hal_rx_mpdu_link_ptr { + struct ath12k_buffer_addr addr_info; +} __packed; + +struct hal_rx_msdu_details { + struct ath12k_buffer_addr buf_addr_info; + struct rx_msdu_desc rx_msdu_info; +} __packed; + +#define HAL_RX_MSDU_LNK_INFO0_RX_QUEUE_NUMBER GENMASK(15, 0) +#define HAL_RX_MSDU_LNK_INFO0_FIRST_MSDU_LNK BIT(16) + +struct hal_rx_msdu_link { + struct hal_desc_header desc_hdr; + struct ath12k_buffer_addr buf_addr_info; + uint32_t info0; + uint32_t pn[4]; + struct hal_rx_msdu_details msdu_link[6]; +} __packed; + +struct hal_rx_reo_queue_ext { + struct hal_desc_header desc_hdr; + uint32_t rsvd; + struct hal_rx_mpdu_link_ptr mpdu_link[15]; +} __packed; + +/* hal_rx_reo_queue_ext + * Consumer: REO + * Producer: REO + * + * descriptor_header + * Details about which module owns this struct. + * + * mpdu_link + * Pointer to the next MPDU_link descriptor in the MPDU queue. + */ + +enum hal_rx_reo_queue_pn_size { + HAL_RX_REO_QUEUE_PN_SIZE_24, + HAL_RX_REO_QUEUE_PN_SIZE_48, + HAL_RX_REO_QUEUE_PN_SIZE_128, +}; + +#define HAL_RX_REO_QUEUE_RX_QUEUE_NUMBER GENMASK(15, 0) + +#define HAL_RX_REO_QUEUE_INFO0_VLD BIT(0) +#define HAL_RX_REO_QUEUE_INFO0_ASSOC_LNK_DESC_COUNTER GENMASK(2, 1) +#define HAL_RX_REO_QUEUE_INFO0_DIS_DUP_DETECTION BIT(3) +#define HAL_RX_REO_QUEUE_INFO0_SOFT_REORDER_EN BIT(4) +#define HAL_RX_REO_QUEUE_INFO0_AC GENMASK(6, 5) +#define HAL_RX_REO_QUEUE_INFO0_BAR BIT(7) +#define HAL_RX_REO_QUEUE_INFO0_RETRY BIT(8) +#define HAL_RX_REO_QUEUE_INFO0_CHECK_2K_MODE BIT(9) +#define HAL_RX_REO_QUEUE_INFO0_OOR_MODE BIT(10) +#define HAL_RX_REO_QUEUE_INFO0_BA_WINDOW_SIZE GENMASK(18, 11) +#define HAL_RX_REO_QUEUE_INFO0_PN_CHECK BIT(19) +#define HAL_RX_REO_QUEUE_INFO0_EVEN_PN BIT(20) +#define HAL_RX_REO_QUEUE_INFO0_UNEVEN_PN BIT(21) +#define HAL_RX_REO_QUEUE_INFO0_PN_HANDLE_ENABLE BIT(22) +#define HAL_RX_REO_QUEUE_INFO0_PN_SIZE GENMASK(24, 23) +#define HAL_RX_REO_QUEUE_INFO0_IGNORE_AMPDU_FLG BIT(25) + +#define HAL_RX_REO_QUEUE_INFO1_SVLD BIT(0) +#define HAL_RX_REO_QUEUE_INFO1_SSN GENMASK(12, 1) +#define HAL_RX_REO_QUEUE_INFO1_CURRENT_IDX GENMASK(20, 13) +#define HAL_RX_REO_QUEUE_INFO1_SEQ_2K_ERR BIT(21) +#define HAL_RX_REO_QUEUE_INFO1_PN_ERR BIT(22) +#define HAL_RX_REO_QUEUE_INFO1_PN_VALID BIT(31) + +#define HAL_RX_REO_QUEUE_INFO2_MPDU_COUNT GENMASK(6, 0) +#define HAL_RX_REO_QUEUE_INFO2_MSDU_COUNT (31, 7) + +#define HAL_RX_REO_QUEUE_INFO3_TIMEOUT_COUNT GENMASK(9, 4) +#define HAL_RX_REO_QUEUE_INFO3_FWD_DUE_TO_BAR_CNT GENMASK(15, 10) +#define HAL_RX_REO_QUEUE_INFO3_DUPLICATE_COUNT GENMASK(31, 16) + +#define HAL_RX_REO_QUEUE_INFO4_FRAME_IN_ORD_COUNT GENMASK(23, 0) +#define HAL_RX_REO_QUEUE_INFO4_BAR_RECVD_COUNT GENMASK(31, 24) + +#define HAL_RX_REO_QUEUE_INFO5_LATE_RX_MPDU_COUNT GENMASK(11, 0) +#define HAL_RX_REO_QUEUE_INFO5_WINDOW_JUMP_2K GENMASK(15, 12) +#define HAL_RX_REO_QUEUE_INFO5_HOLE_COUNT GENMASK(31, 16) + +struct hal_rx_reo_queue { + struct hal_desc_header desc_hdr; + uint32_t rx_queue_num; + uint32_t info0; + uint32_t info1; + uint32_t pn[4]; + uint32_t last_rx_enqueue_timestamp; + uint32_t last_rx_dequeue_timestamp; + uint32_t next_aging_queue[2]; + uint32_t prev_aging_queue[2]; + uint32_t rx_bitmap[8]; + uint32_t info2; + uint32_t info3; + uint32_t info4; + uint32_t processed_mpdus; + uint32_t processed_msdus; + uint32_t processed_total_bytes; + uint32_t info5; + uint32_t rsvd[3]; + struct hal_rx_reo_queue_ext ext_desc[]; +} __packed; + +/* hal_rx_reo_queue + * + * descriptor_header + * Details about which module owns this struct. Note that sub field + * Buffer_type shall be set to receive_reo_queue_descriptor. + * + * receive_queue_number + * Indicates the MPDU queue ID to which this MPDU link descriptor belongs. + * + * vld + * Valid bit indicating a session is established and the queue descriptor + * is valid. + * associated_link_descriptor_counter + * Indicates which of the 3 link descriptor counters shall be incremented + * or decremented when link descriptors are added or removed from this + * flow queue. + * disable_duplicate_detection + * When set, do not perform any duplicate detection. + * soft_reorder_enable + * When set, REO has been instructed to not perform the actual re-ordering + * of frames for this queue, but just to insert the reorder opcodes. + * ac + * Indicates the access category of the queue descriptor. + * bar + * Indicates if BAR has been received. + * retry + * Retry bit is checked if this bit is set. + * chk_2k_mode + * Indicates what type of operation is expected from Reo when the received + * frame SN falls within the 2K window. + * oor_mode + * Indicates what type of operation is expected when the received frame + * falls within the OOR window. + * ba_window_size + * Indicates the negotiated (window size + 1). Max of 256 bits. + * + * A value 255 means 256 bitmap, 63 means 64 bitmap, 0 (means non-BA + * session, with window size of 0). The 3 values here are the main values + * validated, but other values should work as well. + * + * A BA window size of 0 (=> one frame entry bitmat), means that there is + * no additional rx_reo_queue_ext desc. following rx_reo_queue in memory. + * A BA window size of 1 - 105, means that there is 1 rx_reo_queue_ext. + * A BA window size of 106 - 210, means that there are 2 rx_reo_queue_ext. + * A BA window size of 211 - 256, means that there are 3 rx_reo_queue_ext. + * pn_check_needed, pn_shall_be_even, pn_shall_be_uneven, pn_handling_enable, + * pn_size + * REO shall perform the PN increment check, even number check, uneven + * number check, PN error check and size of the PN field check. + * ignore_ampdu_flag + * REO shall ignore the ampdu_flag on entrance descriptor for this queue. + * + * svld + * Sequence number in next field is valid one. + * ssn + * Starting Sequence number of the session. + * current_index + * Points to last forwarded packet + * seq_2k_error_detected_flag + * REO has detected a 2k error jump in the sequence number and from that + * moment forward, all new frames are forwarded directly to FW, without + * duplicate detect, reordering, etc. + * pn_error_detected_flag + * REO has detected a PN error. + */ + +#define HAL_REO_UPD_RX_QUEUE_INFO0_QUEUE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RX_QUEUE_NUM BIT(8) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_VLD BIT(9) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_ASSOC_LNK_DESC_CNT BIT(10) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_DIS_DUP_DETECTION BIT(11) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SOFT_REORDER_EN BIT(12) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_AC BIT(13) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BAR BIT(14) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RETRY BIT(15) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_CHECK_2K_MODE BIT(16) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_OOR_MODE BIT(17) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BA_WINDOW_SIZE BIT(18) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_CHECK BIT(19) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_EVEN_PN BIT(20) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_UNEVEN_PN BIT(21) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_HANDLE_ENABLE BIT(22) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_SIZE BIT(23) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_IGNORE_AMPDU_FLG BIT(24) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SVLD BIT(25) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SSN BIT(26) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SEQ_2K_ERR BIT(27) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_ERR BIT(28) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_VALID BIT(29) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN BIT(30) + +#define HAL_REO_UPD_RX_QUEUE_INFO1_RX_QUEUE_NUMBER GENMASK(15, 0) +#define HAL_REO_UPD_RX_QUEUE_INFO1_VLD BIT(16) +#define HAL_REO_UPD_RX_QUEUE_INFO1_ASSOC_LNK_DESC_COUNTER GENMASK(18, 17) +#define HAL_REO_UPD_RX_QUEUE_INFO1_DIS_DUP_DETECTION BIT(19) +#define HAL_REO_UPD_RX_QUEUE_INFO1_SOFT_REORDER_EN BIT(20) +#define HAL_REO_UPD_RX_QUEUE_INFO1_AC GENMASK(22, 21) +#define HAL_REO_UPD_RX_QUEUE_INFO1_BAR BIT(23) +#define HAL_REO_UPD_RX_QUEUE_INFO1_RETRY BIT(24) +#define HAL_REO_UPD_RX_QUEUE_INFO1_CHECK_2K_MODE BIT(25) +#define HAL_REO_UPD_RX_QUEUE_INFO1_OOR_MODE BIT(26) +#define HAL_REO_UPD_RX_QUEUE_INFO1_PN_CHECK BIT(27) +#define HAL_REO_UPD_RX_QUEUE_INFO1_EVEN_PN BIT(28) +#define HAL_REO_UPD_RX_QUEUE_INFO1_UNEVEN_PN BIT(29) +#define HAL_REO_UPD_RX_QUEUE_INFO1_PN_HANDLE_ENABLE BIT(30) +#define HAL_REO_UPD_RX_QUEUE_INFO1_IGNORE_AMPDU_FLG BIT(31) + +#define HAL_REO_UPD_RX_QUEUE_INFO2_BA_WINDOW_SIZE GENMASK(7, 0) +#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_SIZE GENMASK(9, 8) +#define HAL_REO_UPD_RX_QUEUE_INFO2_SVLD BIT(10) +#define HAL_REO_UPD_RX_QUEUE_INFO2_SSN GENMASK(22, 11) +#define HAL_REO_UPD_RX_QUEUE_INFO2_SEQ_2K_ERR BIT(23) +#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_ERR BIT(24) +#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_VALID BIT(25) + +struct hal_reo_update_rx_queue { + struct hal_reo_cmd_hdr cmd; + uint32_t queue_addr_lo; + uint32_t info0; + uint32_t info1; + uint32_t info2; + uint32_t pn[4]; +} __packed; + +#define HAL_REO_UNBLOCK_CACHE_INFO0_UNBLK_CACHE BIT(0) +#define HAL_REO_UNBLOCK_CACHE_INFO0_RESOURCE_IDX GENMASK(2, 1) + +struct hal_reo_unblock_cache { + struct hal_reo_cmd_hdr cmd; + uint32_t info0; + uint32_t rsvd[7]; +} __packed; + +enum hal_reo_exec_status { + HAL_REO_EXEC_STATUS_SUCCESS, + HAL_REO_EXEC_STATUS_BLOCKED, + HAL_REO_EXEC_STATUS_FAILED, + HAL_REO_EXEC_STATUS_RESOURCE_BLOCKED, +}; + +#define HAL_REO_STATUS_HDR_INFO0_STATUS_NUM GENMASK(15, 0) +#define HAL_REO_STATUS_HDR_INFO0_EXEC_TIME GENMASK(25, 16) +#define HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS GENMASK(27, 26) + +#define HAL_HASH_ROUTING_RING_TCL 0 +#define HAL_HASH_ROUTING_RING_SW1 1 +#define HAL_HASH_ROUTING_RING_SW2 2 +#define HAL_HASH_ROUTING_RING_SW3 3 +#define HAL_HASH_ROUTING_RING_SW4 4 +#define HAL_HASH_ROUTING_RING_REL 5 +#define HAL_HASH_ROUTING_RING_FW 6 + +struct hal_reo_status_hdr { + uint32_t info0; + uint32_t timestamp; +} __packed; + +/* hal_reo_status_hdr + * Producer: REO + * Consumer: SW + * + * status_num + * The value in this field is equal to value of the reo command + * number. This field helps to correlate the statuses with the REO + * commands. + * + * execution_time (in us) + * The amount of time REO took to execute the command. Note that + * this time does not include the duration of the command waiting + * in the command ring, before the execution started. + * + * execution_status + * Execution status of the command. Values are defined in + * enum %HAL_REO_EXEC_STATUS_. + */ +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_SSN GENMASK(11, 0) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_CUR_IDX GENMASK(19, 12) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MPDU_COUNT GENMASK(6, 0) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MSDU_COUNT GENMASK(31, 7) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_TIMEOUT_COUNT GENMASK(9, 4) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_FDTB_COUNT GENMASK(15, 10) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_DUPLICATE_COUNT GENMASK(31, 16) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_FIO_COUNT GENMASK(23, 0) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_BAR_RCVD_CNT GENMASK(31, 24) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_LATE_RX_MPDU GENMASK(11, 0) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_WINDOW_JMP2K GENMASK(15, 12) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_HOLE_COUNT GENMASK(31, 16) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO5_LOOPING_CNT GENMASK(31, 28) + +struct hal_reo_get_queue_stats_status { + struct hal_reo_status_hdr hdr; + uint32_t info0; + uint32_t pn[4]; + uint32_t last_rx_enqueue_timestamp; + uint32_t last_rx_dequeue_timestamp; + uint32_t rx_bitmap[8]; + uint32_t info1; + uint32_t info2; + uint32_t info3; + uint32_t num_mpdu_frames; + uint32_t num_msdu_frames; + uint32_t total_bytes; + uint32_t info4; + uint32_t info5; +} __packed; + +/* hal_reo_get_queue_stats_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * ssn + * Starting Sequence number of the session, this changes whenever + * window moves (can be filled by SW then maintained by REO). + * + * current_index + * Points to last forwarded packet. + * + * pn + * Bits of the PN number. + * + * last_rx_enqueue_timestamp + * last_rx_dequeue_timestamp + * Timestamp of arrival of the last MPDU for this queue and + * Timestamp of forwarding an MPDU accordingly. + * + * rx_bitmap + * When a bit is set, the corresponding frame is currently held + * in the re-order queue. The bitmap is Fully managed by HW. + * + * current_mpdu_count + * current_msdu_count + * The number of MPDUs and MSDUs in the queue. + * + * timeout_count + * The number of times REO started forwarding frames even though + * there is a hole in the bitmap. Forwarding reason is timeout. + * + * forward_due_to_bar_count + * The number of times REO started forwarding frames even though + * there is a hole in the bitmap. Fwd reason is reception of BAR. + * + * duplicate_count + * The number of duplicate frames that have been detected. + * + * frames_in_order_count + * The number of frames that have been received in order (without + * a hole that prevented them from being forwarded immediately). + * + * bar_received_count + * The number of times a BAR frame is received. + * + * mpdu_frames_processed_count + * msdu_frames_processed_count + * The total number of MPDU/MSDU frames that have been processed. + * + * total_bytes + * An approximation of the number of bytes received for this queue. + * + * late_receive_mpdu_count + * The number of MPDUs received after the window had already moved + * on. The 'late' sequence window is defined as + * (Window SSN - 256) - (Window SSN - 1). + * + * window_jump_2k + * The number of times the window moved more than 2K + * + * hole_count + * The number of times a hole was created in the receive bitmap. + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_STATUS_LOOP_CNT GENMASK(31, 28) + +#define HAL_REO_FLUSH_QUEUE_INFO0_ERR_DETECTED BIT(0) +#define HAL_REO_FLUSH_QUEUE_INFO0_RSVD GENMASK(31, 1) +#define HAL_REO_FLUSH_QUEUE_INFO1_RSVD GENMASK(27, 0) + +struct hal_reo_flush_queue_status { + struct hal_reo_status_hdr hdr; + uint32_t info0; + uint32_t rsvd0[21]; + uint32_t info1; +} __packed; + +/* hal_reo_flush_queue_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * error_detected + * Status of blocking resource + * + * 0 - No error has been detected while executing this command + * 1 - Error detected. The resource to be used for blocking was + * already in use. + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_IS_ERR BIT(0) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_BLOCK_ERR_CODE GENMASK(2, 1) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_STATUS_HIT BIT(8) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_DESC_TYPE GENMASK(11, 9) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_CLIENT_ID GENMASK(15, 12) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_ERR GENMASK(17, 16) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_COUNT GENMASK(25, 18) + +struct hal_reo_flush_cache_status { + struct hal_reo_status_hdr hdr; + uint32_t info0; + uint32_t rsvd0[21]; + uint32_t info1; +} __packed; + +/* hal_reo_flush_cache_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * error_detected + * Status for blocking resource handling + * + * 0 - No error has been detected while executing this command + * 1 - An error in the blocking resource management was detected + * + * block_error_details + * only valid when error_detected is set + * + * 0 - No blocking related errors found + * 1 - Blocking resource is already in use + * 2 - Resource requested to be unblocked, was not blocked + * + * cache_controller_flush_status_hit + * The status that the cache controller returned on executing the + * flush command. + * + * 0 - miss; 1 - hit + * + * cache_controller_flush_status_desc_type + * Flush descriptor type + * + * cache_controller_flush_status_client_id + * Module who made the flush request + * + * In REO, this is always 0 + * + * cache_controller_flush_status_error + * Error condition + * + * 0 - No error found + * 1 - HW interface is still busy + * 2 - Line currently locked. Used for one line flush command + * 3 - At least one line is still locked. + * Used for cache flush command. + * + * cache_controller_flush_count + * The number of lines that were actually flushed out + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_IS_ERR BIT(0) +#define HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_TYPE BIT(1) + +struct hal_reo_unblock_cache_status { + struct hal_reo_status_hdr hdr; + uint32_t info0; + uint32_t rsvd0[21]; + uint32_t info1; +} __packed; + +/* hal_reo_unblock_cache_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * error_detected + * 0 - No error has been detected while executing this command + * 1 - The blocking resource was not in use, and therefore it could + * not be unblocked. + * + * unblock_type + * Reference to the type of unblock command + * 0 - Unblock a blocking resource + * 1 - The entire cache usage is unblock + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_IS_ERR BIT(0) +#define HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_LIST_EMPTY BIT(1) + +#define HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_REL_DESC_COUNT GENMASK(15, 0) +#define HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_FWD_BUF_COUNT GENMASK(31, 16) + +struct hal_reo_flush_timeout_list_status { + struct hal_reo_status_hdr hdr; + uint32_t info0; + uint32_t info1; + uint32_t rsvd0[20]; + uint32_t info2; +} __packed; + +/* hal_reo_flush_timeout_list_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * error_detected + * 0 - No error has been detected while executing this command + * 1 - Command not properly executed and returned with error + * + * timeout_list_empty + * When set, REO has depleted the timeout list and all entries are + * gone. + * + * release_desc_count + * Producer: SW; Consumer: REO + * The number of link descriptor released + * + * forward_buf_count + * Producer: SW; Consumer: REO + * The number of buffers forwarded to the REO destination rings + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_DESC_THRESH_STATUS_INFO0_THRESH_INDEX GENMASK(1, 0) +#define HAL_REO_DESC_THRESH_STATUS_INFO1_LINK_DESC_COUNTER0 GENMASK(23, 0) +#define HAL_REO_DESC_THRESH_STATUS_INFO2_LINK_DESC_COUNTER1 GENMASK(23, 0) +#define HAL_REO_DESC_THRESH_STATUS_INFO3_LINK_DESC_COUNTER2 GENMASK(23, 0) +#define HAL_REO_DESC_THRESH_STATUS_INFO4_LINK_DESC_COUNTER_SUM GENMASK(25, 0) + +struct hal_reo_desc_thresh_reached_status { + struct hal_reo_status_hdr hdr; + uint32_t info0; + uint32_t info1; + uint32_t info2; + uint32_t info3; + uint32_t info4; + uint32_t rsvd0[17]; + uint32_t info5; +} __packed; + +/* hal_reo_desc_thresh_reached_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * threshold_index + * The index of the threshold register whose value got reached + * + * link_descriptor_counter0 + * link_descriptor_counter1 + * link_descriptor_counter2 + * link_descriptor_counter_sum + * Value of the respective counters at generation of this message + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_0 0xDDBEEF +#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_1 0xADBEEF +#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_2 0xBDBEEF +#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_3 0xCDBEEF + +#define HAL_TX_ADDRX_EN 1 +#define HAL_TX_ADDRY_EN 2 + +#define HAL_TX_ADDR_SEARCH_DEFAULT 0 +#define HAL_TX_ADDR_SEARCH_INDEX 1 + +/* + * Copy Engine + */ + +#define CE_COUNT_MAX 12 + +/* Byte swap data words */ +#define CE_ATTR_BYTE_SWAP_DATA 2 + +/* no interrupt on copy completion */ +#define CE_ATTR_DIS_INTR 8 + +/* Host software's Copy Engine configuration. */ +#ifdef __BIG_ENDIAN +#define CE_ATTR_FLAGS CE_ATTR_BYTE_SWAP_DATA +#else +#define CE_ATTR_FLAGS 0 +#endif + +/* Threshold to poll for tx completion in case of Interrupt disabled CE's */ +#define ATH12K_CE_USAGE_THRESHOLD 32 + +/* + * Directions for interconnect pipe configuration. + * These definitions may be used during configuration and are shared + * between Host and Target. + * + * Pipe Directions are relative to the Host, so PIPEDIR_IN means + * "coming IN over air through Target to Host" as with a WiFi Rx operation. + * Conversely, PIPEDIR_OUT means "going OUT from Host through Target over air" + * as with a WiFi Tx operation. This is somewhat awkward for the "middle-man" + * Target since things that are "PIPEDIR_OUT" are coming IN to the Target + * over the interconnect. + */ +#define PIPEDIR_NONE 0 +#define PIPEDIR_IN 1 /* Target-->Host, WiFi Rx direction */ +#define PIPEDIR_OUT 2 /* Host->Target, WiFi Tx direction */ +#define PIPEDIR_INOUT 3 /* bidirectional */ +#define PIPEDIR_INOUT_H2H 4 /* bidirectional, host to host */ + +/* CE address/mask */ +#define CE_HOST_IE_ADDRESS 0x00A1803C +#define CE_HOST_IE_2_ADDRESS 0x00A18040 +#define CE_HOST_IE_3_ADDRESS CE_HOST_IE_ADDRESS + +/* CE IE registers are different for IPQ5018 */ +#define CE_HOST_IPQ5018_IE_ADDRESS 0x0841804C +#define CE_HOST_IPQ5018_IE_2_ADDRESS 0x08418050 +#define CE_HOST_IPQ5018_IE_3_ADDRESS CE_HOST_IPQ5018_IE_ADDRESS + +#define CE_HOST_IE_3_SHIFT 0xC + +#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask)) + +/* + * Establish a mapping between a service/direction and a pipe. + * Configuration information for a Copy Engine pipe and services. + * Passed from Host to Target through QMI message and must be in + * little endian format. + */ +struct service_to_pipe { + uint32_t service_id; + uint32_t pipedir; + uint32_t pipenum; +}; + +/* + * Configuration information for a Copy Engine pipe. + * Passed from Host to Target through QMI message during startup (one per CE). + * + * NOTE: Structure is shared between Host software and Target firmware! + */ +struct ce_pipe_config { + uint32_t pipenum; + uint32_t pipedir; + uint32_t nentries; + uint32_t nbytes_max; + uint32_t flags; + uint32_t reserved; +}; + +/* + * HTC + */ + +#define HTC_HDR_ENDPOINTID GENMASK(7, 0) +#define HTC_HDR_FLAGS GENMASK(15, 8) +#define HTC_HDR_PAYLOADLEN GENMASK(31, 16) +#define HTC_HDR_CONTROLBYTES0 GENMASK(7, 0) +#define HTC_HDR_CONTROLBYTES1 GENMASK(15, 8) +#define HTC_HDR_RESERVED GENMASK(31, 16) + +#define HTC_SVC_MSG_SERVICE_ID GENMASK(31, 16) +#define HTC_SVC_MSG_CONNECTIONFLAGS GENMASK(15, 0) +#define HTC_SVC_MSG_SERVICEMETALENGTH GENMASK(23, 16) +#define HTC_READY_MSG_CREDITCOUNT GENMASK(31, 16) +#define HTC_READY_MSG_CREDITSIZE GENMASK(15, 0) +#define HTC_READY_MSG_MAXENDPOINTS GENMASK(23, 16) + +#define HTC_READY_EX_MSG_HTCVERSION GENMASK(7, 0) +#define HTC_READY_EX_MSG_MAXMSGSPERHTCBUNDLE GENMASK(15, 8) + +#define HTC_SVC_RESP_MSG_SERVICEID GENMASK(31, 16) +#define HTC_SVC_RESP_MSG_STATUS GENMASK(7, 0) +#define HTC_SVC_RESP_MSG_ENDPOINTID GENMASK(15, 8) +#define HTC_SVC_RESP_MSG_MAXMSGSIZE GENMASK(31, 16) +#define HTC_SVC_RESP_MSG_SERVICEMETALENGTH GENMASK(7, 0) + +#define HTC_MSG_MESSAGEID GENMASK(15, 0) +#define HTC_SETUP_COMPLETE_EX_MSG_SETUPFLAGS GENMASK(31, 0) +#define HTC_SETUP_COMPLETE_EX_MSG_MAXMSGSPERBUNDLEDRECV GENMASK(7, 0) +#define HTC_SETUP_COMPLETE_EX_MSG_RSVD0 GENMASK(15, 8) +#define HTC_SETUP_COMPLETE_EX_MSG_RSVD1 GENMASK(23, 16) +#define HTC_SETUP_COMPLETE_EX_MSG_RSVD2 GENMASK(31, 24) + +enum ath12k_htc_tx_flags { + ATH12K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01, + ATH12K_HTC_FLAG_SEND_BUNDLE = 0x02 +}; + +enum ath12k_htc_rx_flags { + ATH12K_HTC_FLAG_TRAILER_PRESENT = 0x02, + ATH12K_HTC_FLAG_BUNDLE_MASK = 0xF0 +}; + + +struct ath12k_htc_hdr { + uint32_t htc_info; + uint32_t ctrl_info; +} __packed __aligned(4); + +enum ath12k_htc_msg_id { + ATH12K_HTC_MSG_READY_ID = 1, + ATH12K_HTC_MSG_CONNECT_SERVICE_ID = 2, + ATH12K_HTC_MSG_CONNECT_SERVICE_RESP_ID = 3, + ATH12K_HTC_MSG_SETUP_COMPLETE_ID = 4, + ATH12K_HTC_MSG_SETUP_COMPLETE_EX_ID = 5, + ATH12K_HTC_MSG_SEND_SUSPEND_COMPLETE = 6, + ATH12K_HTC_MSG_NACK_SUSPEND = 7, + ATH12K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID = 8, +}; + +enum ath12k_htc_version { + ATH12K_HTC_VERSION_2P0 = 0x00, /* 2.0 */ + ATH12K_HTC_VERSION_2P1 = 0x01, /* 2.1 */ +}; + +#define ATH12K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_MASK GENMASK(1, 0) +#define ATH12K_HTC_CONN_FLAGS_RECV_ALLOC GENMASK(15, 8) + +enum ath12k_htc_conn_flags { + ATH12K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH = 0x0, + ATH12K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF = 0x1, + ATH12K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS = 0x2, + ATH12K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY = 0x3, + ATH12K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE = 0x4, + ATH12K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 0x8, +}; + +enum ath12k_htc_conn_svc_status { + ATH12K_HTC_CONN_SVC_STATUS_SUCCESS = 0, + ATH12K_HTC_CONN_SVC_STATUS_NOT_FOUND = 1, + ATH12K_HTC_CONN_SVC_STATUS_FAILED = 2, + ATH12K_HTC_CONN_SVC_STATUS_NO_RESOURCES = 3, + ATH12K_HTC_CONN_SVC_STATUS_NO_MORE_EP = 4 +}; + +struct ath12k_htc_ready { + uint32_t id_credit_count; + uint32_t size_ep; +} __packed; + +struct ath12k_htc_ready_extended { + struct ath12k_htc_ready base; + uint32_t ver_bundle; +} __packed; + +struct ath12k_htc_conn_svc { + uint32_t msg_svc_id; + uint32_t flags_len; +} __packed; + +struct ath12k_htc_conn_svc_resp { + uint32_t msg_svc_id; + uint32_t flags_len; + uint32_t svc_meta_pad; +} __packed; + +#define ATH12K_GLOBAL_DISABLE_CREDIT_FLOW BIT(1) + +struct ath12k_htc_setup_complete_extended { + uint32_t msg_id; + uint32_t flags; + uint32_t max_msgs_per_bundled_recv; +} __packed; + +struct ath12k_htc_msg { + uint32_t msg_svc_id; + uint32_t flags_len; +} __packed __aligned(4); + +enum ath12k_htc_record_id { + ATH12K_HTC_RECORD_NULL = 0, + ATH12K_HTC_RECORD_CREDITS = 1 +}; + +struct ath12k_htc_record_hdr { + uint8_t id; /* @enum ath12k_htc_record_id */ + uint8_t len; + uint8_t pad0; + uint8_t pad1; +} __packed; + +struct ath12k_htc_credit_report { + uint8_t eid; /* @enum ath12k_htc_ep_id */ + uint8_t credits; + uint8_t pad0; + uint8_t pad1; +} __packed; + +struct ath12k_htc_record { + struct ath12k_htc_record_hdr hdr; + union { + struct ath12k_htc_credit_report credit_report[0]; + uint8_t payload[0]; + }; +} __packed __aligned(4); + +/* note: the trailer offset is dynamic depending + * on payload length. this is only a struct layout draft + */ +struct ath12k_htc_frame { + struct ath12k_htc_hdr hdr; + union { + struct ath12k_htc_msg msg; + uint8_t payload[0]; + }; + struct ath12k_htc_record trailer[0]; +} __packed __aligned(4); + +enum ath12k_htc_svc_gid { + ATH12K_HTC_SVC_GRP_RSVD = 0, + ATH12K_HTC_SVC_GRP_WMI = 1, + ATH12K_HTC_SVC_GRP_NMI = 2, + ATH12K_HTC_SVC_GRP_HTT = 3, + ATH12K_HTC_SVC_GRP_CFG = 4, + ATH12K_HTC_SVC_GRP_IPA = 5, + ATH12K_HTC_SVC_GRP_PKTLOG = 6, + + ATH12K_HTC_SVC_GRP_TEST = 254, + ATH12K_HTC_SVC_GRP_LAST = 255, +}; + +#define SVC(group, idx) \ + (int)(((int)(group) << 8) | (int)(idx)) + +enum ath12k_htc_svc_id { + /* NOTE: service ID of 0x0000 is reserved and should never be used */ + ATH12K_HTC_SVC_ID_RESERVED = 0x0000, + ATH12K_HTC_SVC_ID_UNUSED = ATH12K_HTC_SVC_ID_RESERVED, + + ATH12K_HTC_SVC_ID_RSVD_CTRL = SVC(ATH12K_HTC_SVC_GRP_RSVD, 1), + ATH12K_HTC_SVC_ID_WMI_CONTROL = SVC(ATH12K_HTC_SVC_GRP_WMI, 0), + ATH12K_HTC_SVC_ID_WMI_DATA_BE = SVC(ATH12K_HTC_SVC_GRP_WMI, 1), + ATH12K_HTC_SVC_ID_WMI_DATA_BK = SVC(ATH12K_HTC_SVC_GRP_WMI, 2), + ATH12K_HTC_SVC_ID_WMI_DATA_VI = SVC(ATH12K_HTC_SVC_GRP_WMI, 3), + ATH12K_HTC_SVC_ID_WMI_DATA_VO = SVC(ATH12K_HTC_SVC_GRP_WMI, 4), + ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1 = SVC(ATH12K_HTC_SVC_GRP_WMI, 5), + ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2 = SVC(ATH12K_HTC_SVC_GRP_WMI, 6), + + ATH12K_HTC_SVC_ID_NMI_CONTROL = SVC(ATH12K_HTC_SVC_GRP_NMI, 0), + ATH12K_HTC_SVC_ID_NMI_DATA = SVC(ATH12K_HTC_SVC_GRP_NMI, 1), + + ATH12K_HTC_SVC_ID_HTT_DATA_MSG = SVC(ATH12K_HTC_SVC_GRP_HTT, 0), + + /* raw stream service (i.e. flash, tcmd, calibration apps) */ + ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH12K_HTC_SVC_GRP_TEST, 0), + ATH12K_HTC_SVC_ID_IPA_TX = SVC(ATH12K_HTC_SVC_GRP_IPA, 0), + ATH12K_HTC_SVC_ID_PKT_LOG = SVC(ATH12K_HTC_SVC_GRP_PKTLOG, 0), +}; + +#undef SVC + +enum ath12k_htc_ep_id { + ATH12K_HTC_EP_UNUSED = -1, + ATH12K_HTC_EP_0 = 0, + ATH12K_HTC_EP_1 = 1, + ATH12K_HTC_EP_2, + ATH12K_HTC_EP_3, + ATH12K_HTC_EP_4, + ATH12K_HTC_EP_5, + ATH12K_HTC_EP_6, + ATH12K_HTC_EP_7, + ATH12K_HTC_EP_8, + ATH12K_HTC_EP_COUNT, +}; + +/* + * hw.h + */ + +/* Target configuration defines */ + +/* Num VDEVS per radio */ +#define TARGET_NUM_VDEVS(sc) (sc->hw_params.num_vdevs) + +#define TARGET_NUM_PEERS_PDEV(sc) (sc->hw_params.num_peers + TARGET_NUM_VDEVS(sc)) + +/* Num of peers for Single Radio mode */ +#define TARGET_NUM_PEERS_SINGLE(sc) (TARGET_NUM_PEERS_PDEV(sc)) + +/* Num of peers for DBS */ +#define TARGET_NUM_PEERS_DBS(sc) (2 * TARGET_NUM_PEERS_PDEV(sc)) + +/* Num of peers for DBS_SBS */ +#define TARGET_NUM_PEERS_DBS_SBS(sc) (3 * TARGET_NUM_PEERS_PDEV(sc)) + +/* Max num of stations (per radio) */ +#define TARGET_NUM_STATIONS(sc) (sc->hw_params.num_peers) + +#define TARGET_NUM_PEERS(sc, x) TARGET_NUM_PEERS_##x(sc) +#define TARGET_NUM_PEER_KEYS 2 +#define TARGET_NUM_TIDS(sc, x) (2 * TARGET_NUM_PEERS(sc, x) + \ + 4 * TARGET_NUM_VDEVS(sc) + 8) + +#define TARGET_AST_SKID_LIMIT 16 +#define TARGET_NUM_OFFLD_PEERS 4 +#define TARGET_NUM_OFFLD_REORDER_BUFFS 4 + +#define TARGET_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(4)) +#define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(4)) +#define TARGET_RX_TIMEOUT_LO_PRI 100 +#define TARGET_RX_TIMEOUT_HI_PRI 40 + +#define TARGET_DECAP_MODE_RAW 0 +#define TARGET_DECAP_MODE_NATIVE_WIFI 1 +#define TARGET_DECAP_MODE_ETH 2 + +#define TARGET_SCAN_MAX_PENDING_REQS 4 +#define TARGET_BMISS_OFFLOAD_MAX_VDEV 3 +#define TARGET_ROAM_OFFLOAD_MAX_VDEV 3 +#define TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES 8 +#define TARGET_GTK_OFFLOAD_MAX_VDEV 3 +#define TARGET_NUM_MCAST_GROUPS 12 +#define TARGET_NUM_MCAST_TABLE_ELEMS 64 +#define TARGET_MCAST2UCAST_MODE 2 +#define TARGET_TX_DBG_LOG_SIZE 1024 +#define TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1 +#define TARGET_VOW_CONFIG 0 +#define TARGET_NUM_MSDU_DESC (2500) +#define TARGET_MAX_FRAG_ENTRIES 6 +#define TARGET_MAX_BCN_OFFLD 16 +#define TARGET_NUM_WDS_ENTRIES 32 +#define TARGET_DMA_BURST_SIZE 1 +#define TARGET_RX_BATCHMODE 1 +#define TARGET_EMA_MAX_PROFILE_PERIOD 8 + +#define ATH12K_HW_MAX_QUEUES 4 +#define ATH12K_QUEUE_LEN 4096 + +#define ATH12k_HW_RATECODE_CCK_SHORT_PREAM_MASK 0x4 + +enum ath12k_hw_rate_cck { + ATH12K_HW_RATE_CCK_LP_11M = 0, + ATH12K_HW_RATE_CCK_LP_5_5M, + ATH12K_HW_RATE_CCK_LP_2M, + ATH12K_HW_RATE_CCK_LP_1M, + ATH12K_HW_RATE_CCK_SP_11M, + ATH12K_HW_RATE_CCK_SP_5_5M, + ATH12K_HW_RATE_CCK_SP_2M, +}; + +enum ath12k_hw_rate_ofdm { + ATH12K_HW_RATE_OFDM_48M = 0, + ATH12K_HW_RATE_OFDM_24M, + ATH12K_HW_RATE_OFDM_12M, + ATH12K_HW_RATE_OFDM_6M, + ATH12K_HW_RATE_OFDM_54M, + ATH12K_HW_RATE_OFDM_36M, + ATH12K_HW_RATE_OFDM_18M, + ATH12K_HW_RATE_OFDM_9M, +}; + +enum ath12k_bus { + ATH12K_BUS_AHB, + ATH12K_BUS_PCI, +}; + +#define ATH12K_EXT_IRQ_GRP_NUM_MAX 11 + +/* + * rx_desc.h + */ + +enum rx_desc_rxpcu_filter { + RX_DESC_RXPCU_FILTER_PASS, + RX_DESC_RXPCU_FILTER_MONITOR_CLIENT, + RX_DESC_RXPCU_FILTER_MONITOR_OTHER, +}; + +/* rxpcu_filter_pass + * This MPDU passed the normal frame filter programming of rxpcu. + * + * rxpcu_filter_monitor_client + * This MPDU did not pass the regular frame filter and would + * have been dropped, were it not for the frame fitting into the + * 'monitor_client' category. + * + * rxpcu_filter_monitor_other + * This MPDU did not pass the regular frame filter and also did + * not pass the rxpcu_monitor_client filter. It would have been + * dropped accept that it did pass the 'monitor_other' category. + */ + +#define RX_DESC_INFO0_RXPCU_MPDU_FITLER GENMASK(1, 0) +#define RX_DESC_INFO0_SW_FRAME_GRP_ID GENMASK(8, 2) + +enum rx_desc_sw_frame_grp_id { + RX_DESC_SW_FRAME_GRP_ID_NDP_FRAME, + RX_DESC_SW_FRAME_GRP_ID_MCAST_DATA, + RX_DESC_SW_FRAME_GRP_ID_UCAST_DATA, + RX_DESC_SW_FRAME_GRP_ID_NULL_DATA, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0000, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0001, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0010, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0011, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0100, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0101, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0110, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0111, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1000, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1001, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1010, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1011, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1100, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1101, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1110, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1111, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0000, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0001, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0010, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0011, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0100, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0101, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0110, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0111, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1000, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1001, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1010, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1011, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1100, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1101, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1110, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1111, + RX_DESC_SW_FRAME_GRP_ID_UNSUPPORTED, + RX_DESC_SW_FRAME_GRP_ID_PHY_ERR, +}; + +#define DP_MAX_NWIFI_HDR_LEN 30 + +#define DP_RX_MPDU_ERR_FCS BIT(0) +#define DP_RX_MPDU_ERR_DECRYPT BIT(1) +#define DP_RX_MPDU_ERR_TKIP_MIC BIT(2) +#define DP_RX_MPDU_ERR_AMSDU_ERR BIT(3) +#define DP_RX_MPDU_ERR_OVERFLOW BIT(4) +#define DP_RX_MPDU_ERR_MSDU_LEN BIT(5) +#define DP_RX_MPDU_ERR_MPDU_LEN BIT(6) +#define DP_RX_MPDU_ERR_UNENCRYPTED_FRAME BIT(7) + +enum dp_rx_decap_type { + DP_RX_DECAP_TYPE_RAW, + DP_RX_DECAP_TYPE_NATIVE_WIFI, + DP_RX_DECAP_TYPE_ETHERNET2_DIX, + DP_RX_DECAP_TYPE_8023, +}; + +enum rx_desc_decap_type { + RX_DESC_DECAP_TYPE_RAW, + RX_DESC_DECAP_TYPE_NATIVE_WIFI, + RX_DESC_DECAP_TYPE_ETHERNET2_DIX, + RX_DESC_DECAP_TYPE_8023, +}; + +enum rx_desc_decrypt_status_code { + RX_DESC_DECRYPT_STATUS_CODE_OK, + RX_DESC_DECRYPT_STATUS_CODE_UNPROTECTED_FRAME, + RX_DESC_DECRYPT_STATUS_CODE_DATA_ERR, + RX_DESC_DECRYPT_STATUS_CODE_KEY_INVALID, + RX_DESC_DECRYPT_STATUS_CODE_PEER_ENTRY_INVALID, + RX_DESC_DECRYPT_STATUS_CODE_OTHER, +}; + +#define RX_ATTENTION_INFO1_FIRST_MPDU BIT(0) +#define RX_ATTENTION_INFO1_RSVD_1A BIT(1) +#define RX_ATTENTION_INFO1_MCAST_BCAST BIT(2) +#define RX_ATTENTION_INFO1_AST_IDX_NOT_FOUND BIT(3) +#define RX_ATTENTION_INFO1_AST_IDX_TIMEDOUT BIT(4) +#define RX_ATTENTION_INFO1_POWER_MGMT BIT(5) +#define RX_ATTENTION_INFO1_NON_QOS BIT(6) +#define RX_ATTENTION_INFO1_NULL_DATA BIT(7) +#define RX_ATTENTION_INFO1_MGMT_TYPE BIT(8) +#define RX_ATTENTION_INFO1_CTRL_TYPE BIT(9) +#define RX_ATTENTION_INFO1_MORE_DATA BIT(10) +#define RX_ATTENTION_INFO1_EOSP BIT(11) +#define RX_ATTENTION_INFO1_A_MSDU_ERROR BIT(12) +#define RX_ATTENTION_INFO1_FRAGMENT BIT(13) +#define RX_ATTENTION_INFO1_ORDER BIT(14) +#define RX_ATTENTION_INFO1_CCE_MATCH BIT(15) +#define RX_ATTENTION_INFO1_OVERFLOW_ERR BIT(16) +#define RX_ATTENTION_INFO1_MSDU_LEN_ERR BIT(17) +#define RX_ATTENTION_INFO1_TCP_UDP_CKSUM_FAIL BIT(18) +#define RX_ATTENTION_INFO1_IP_CKSUM_FAIL BIT(19) +#define RX_ATTENTION_INFO1_SA_IDX_INVALID BIT(20) +#define RX_ATTENTION_INFO1_DA_IDX_INVALID BIT(21) +#define RX_ATTENTION_INFO1_RSVD_1B BIT(22) +#define RX_ATTENTION_INFO1_RX_IN_TX_DECRYPT_BYP BIT(23) +#define RX_ATTENTION_INFO1_ENCRYPT_REQUIRED BIT(24) +#define RX_ATTENTION_INFO1_DIRECTED BIT(25) +#define RX_ATTENTION_INFO1_BUFFER_FRAGMENT BIT(26) +#define RX_ATTENTION_INFO1_MPDU_LEN_ERR BIT(27) +#define RX_ATTENTION_INFO1_TKIP_MIC_ERR BIT(28) +#define RX_ATTENTION_INFO1_DECRYPT_ERR BIT(29) +#define RX_ATTENTION_INFO1_UNDECRYPT_FRAME_ERR BIT(30) +#define RX_ATTENTION_INFO1_FCS_ERR BIT(31) + +#define RX_ATTENTION_INFO2_FLOW_IDX_TIMEOUT BIT(0) +#define RX_ATTENTION_INFO2_FLOW_IDX_INVALID BIT(1) +#define RX_ATTENTION_INFO2_WIFI_PARSER_ERR BIT(2) +#define RX_ATTENTION_INFO2_AMSDU_PARSER_ERR BIT(3) +#define RX_ATTENTION_INFO2_SA_IDX_TIMEOUT BIT(4) +#define RX_ATTENTION_INFO2_DA_IDX_TIMEOUT BIT(5) +#define RX_ATTENTION_INFO2_MSDU_LIMIT_ERR BIT(6) +#define RX_ATTENTION_INFO2_DA_IS_VALID BIT(7) +#define RX_ATTENTION_INFO2_DA_IS_MCBC BIT(8) +#define RX_ATTENTION_INFO2_SA_IS_VALID BIT(9) +#define RX_ATTENTION_INFO2_DCRYPT_STATUS_CODE GENMASK(12, 10) +#define RX_ATTENTION_INFO2_RX_BITMAP_NOT_UPDED BIT(13) +#define RX_ATTENTION_INFO2_MSDU_DONE BIT(31) + +struct rx_attention { + uint16_t info0; + uint16_t phy_ppdu_id; + uint32_t info1; + uint32_t info2; +} __packed; + +/* rx_attention + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * first_mpdu + * Indicates the first MSDU of the PPDU. If both first_mpdu + * and last_mpdu are set in the MSDU then this is a not an + * A-MPDU frame but a stand alone MPDU. Interior MPDU in an + * A-MPDU shall have both first_mpdu and last_mpdu bits set to + * 0. The PPDU start status will only be valid when this bit + * is set. + * + * mcast_bcast + * Multicast / broadcast indicator. Only set when the MAC + * address 1 bit 0 is set indicating mcast/bcast and the BSSID + * matches one of the 4 BSSID registers. Only set when + * first_msdu is set. + * + * ast_index_not_found + * Only valid when first_msdu is set. Indicates no AST matching + * entries within the max search count. + * + * ast_index_timeout + * Only valid when first_msdu is set. Indicates an unsuccessful + * search in the address search table due to timeout. + * + * power_mgmt + * Power management bit set in the 802.11 header. Only set + * when first_msdu is set. + * + * non_qos + * Set if packet is not a non-QoS data frame. Only set when + * first_msdu is set. + * + * null_data + * Set if frame type indicates either null data or QoS null + * data format. Only set when first_msdu is set. + * + * mgmt_type + * Set if packet is a management packet. Only set when + * first_msdu is set. + * + * ctrl_type + * Set if packet is a control packet. Only set when first_msdu + * is set. + * + * more_data + * Set if more bit in frame control is set. Only set when + * first_msdu is set. + * + * eosp + * Set if the EOSP (end of service period) bit in the QoS + * control field is set. Only set when first_msdu is set. + * + * a_msdu_error + * Set if number of MSDUs in A-MSDU is above a threshold or if the + * size of the MSDU is invalid. This receive buffer will contain + * all of the remainder of MSDUs in this MPDU w/o decapsulation. + * + * fragment + * Indicates that this is an 802.11 fragment frame. This is + * set when either the more_frag bit is set in the frame + * control or the fragment number is not zero. Only set when + * first_msdu is set. + * + * order + * Set if the order bit in the frame control is set. Only set + * when first_msdu is set. + * + * cce_match + * Indicates that this status has a corresponding MSDU that + * requires FW processing. The OLE will have classification + * ring mask registers which will indicate the ring(s) for + * packets and descriptors which need FW attention. + * + * overflow_err + * PCU Receive FIFO does not have enough space to store the + * full receive packet. Enough space is reserved in the + * receive FIFO for the status is written. This MPDU remaining + * packets in the PPDU will be filtered and no Ack response + * will be transmitted. + * + * msdu_length_err + * Indicates that the MSDU length from the 802.3 encapsulated + * length field extends beyond the MPDU boundary. + * + * tcp_udp_chksum_fail + * Indicates that the computed checksum (tcp_udp_chksum) did + * not match the checksum in the TCP/UDP header. + * + * ip_chksum_fail + * Indicates that the computed checksum did not match the + * checksum in the IP header. + * + * sa_idx_invalid + * Indicates no matching entry was found in the address search + * table for the source MAC address. + * + * da_idx_invalid + * Indicates no matching entry was found in the address search + * table for the destination MAC address. + * + * rx_in_tx_decrypt_byp + * Indicates that RX packet is not decrypted as Crypto is busy + * with TX packet processing. + * + * encrypt_required + * Indicates that this data type frame is not encrypted even if + * the policy for this MPDU requires encryption as indicated in + * the peer table key type. + * + * directed + * MPDU is a directed packet which means that the RA matched + * our STA addresses. In proxySTA it means that the TA matched + * an entry in our address search table with the corresponding + * 'no_ack' bit is the address search entry cleared. + * + * buffer_fragment + * Indicates that at least one of the rx buffers has been + * fragmented. If set the FW should look at the rx_frag_info + * descriptor described below. + * + * mpdu_length_err + * Indicates that the MPDU was pre-maturely terminated + * resulting in a truncated MPDU. Don't trust the MPDU length + * field. + * + * tkip_mic_err + * Indicates that the MPDU Michael integrity check failed + * + * decrypt_err + * Indicates that the MPDU decrypt integrity check failed + * + * fcs_err + * Indicates that the MPDU FCS check failed + * + * flow_idx_timeout + * Indicates an unsuccessful flow search due to the expiring of + * the search timer. + * + * flow_idx_invalid + * flow id is not valid. + * + * amsdu_parser_error + * A-MSDU could not be properly de-agregated. + * + * sa_idx_timeout + * Indicates an unsuccessful search for the source MAC address + * due to the expiring of the search timer. + * + * da_idx_timeout + * Indicates an unsuccessful search for the destination MAC + * address due to the expiring of the search timer. + * + * msdu_limit_error + * Indicates that the MSDU threshold was exceeded and thus + * all the rest of the MSDUs will not be scattered and will not + * be decasulated but will be DMA'ed in RAW format as a single + * MSDU buffer. + * + * da_is_valid + * Indicates that OLE found a valid DA entry. + * + * da_is_mcbc + * Field Only valid if da_is_valid is set. Indicates the DA address + * was a Multicast or Broadcast address. + * + * sa_is_valid + * Indicates that OLE found a valid SA entry. + * + * decrypt_status_code + * Field provides insight into the decryption performed. Values are + * defined in enum %RX_DESC_DECRYPT_STATUS_CODE*. + * + * rx_bitmap_not_updated + * Frame is received, but RXPCU could not update the receive bitmap + * due to (temporary) fifo constraints. + * + * msdu_done + * If set indicates that the RX packet data, RX header data, RX + * PPDU start descriptor, RX MPDU start/end descriptor, RX MSDU + * start/end descriptors and RX Attention descriptor are all + * valid. This bit must be in the last octet of the + * descriptor. + */ + +#define RX_MPDU_START_INFO0_NDP_FRAME BIT(9) +#define RX_MPDU_START_INFO0_PHY_ERR BIT(10) +#define RX_MPDU_START_INFO0_PHY_ERR_MPDU_HDR BIT(11) +#define RX_MPDU_START_INFO0_PROTO_VER_ERR BIT(12) +#define RX_MPDU_START_INFO0_AST_LOOKUP_VALID BIT(13) + +#define RX_MPDU_START_INFO1_MPDU_FCTRL_VALID BIT(0) +#define RX_MPDU_START_INFO1_MPDU_DUR_VALID BIT(1) +#define RX_MPDU_START_INFO1_MAC_ADDR1_VALID BIT(2) +#define RX_MPDU_START_INFO1_MAC_ADDR2_VALID BIT(3) +#define RX_MPDU_START_INFO1_MAC_ADDR3_VALID BIT(4) +#define RX_MPDU_START_INFO1_MAC_ADDR4_VALID BIT(5) +#define RX_MPDU_START_INFO1_MPDU_SEQ_CTRL_VALID BIT(6) +#define RX_MPDU_START_INFO1_MPDU_QOS_CTRL_VALID BIT(7) +#define RX_MPDU_START_INFO1_MPDU_HT_CTRL_VALID BIT(8) +#define RX_MPDU_START_INFO1_ENCRYPT_INFO_VALID BIT(9) +#define RX_MPDU_START_INFO1_MPDU_FRAG_NUMBER GENMASK(13, 10) +#define RX_MPDU_START_INFO1_MORE_FRAG_FLAG BIT(14) +#define RX_MPDU_START_INFO1_FROM_DS BIT(16) +#define RX_MPDU_START_INFO1_TO_DS BIT(17) +#define RX_MPDU_START_INFO1_ENCRYPTED BIT(18) +#define RX_MPDU_START_INFO1_MPDU_RETRY BIT(19) +#define RX_MPDU_START_INFO1_MPDU_SEQ_NUM GENMASK(31, 20) + +#define RX_MPDU_START_INFO2_EPD_EN BIT(0) +#define RX_MPDU_START_INFO2_ALL_FRAME_ENCPD BIT(1) +#define RX_MPDU_START_INFO2_ENC_TYPE GENMASK(5, 2) +#define RX_MPDU_START_INFO2_VAR_WEP_KEY_WIDTH GENMASK(7, 6) +#define RX_MPDU_START_INFO2_MESH_STA BIT(8) +#define RX_MPDU_START_INFO2_BSSID_HIT BIT(9) +#define RX_MPDU_START_INFO2_BSSID_NUM GENMASK(13, 10) +#define RX_MPDU_START_INFO2_TID GENMASK(17, 14) +#define RX_MPDU_START_INFO2_TID_WCN6855 GENMASK(18, 15) + +#define RX_MPDU_START_INFO3_REO_DEST_IND GENMASK(4, 0) +#define RX_MPDU_START_INFO3_FLOW_ID_TOEPLITZ BIT(7) +#define RX_MPDU_START_INFO3_PKT_SEL_FP_UCAST_DATA BIT(8) +#define RX_MPDU_START_INFO3_PKT_SEL_FP_MCAST_DATA BIT(9) +#define RX_MPDU_START_INFO3_PKT_SEL_FP_CTRL_BAR BIT(10) +#define RX_MPDU_START_INFO3_RXDMA0_SRC_RING_SEL GENMASK(12, 11) +#define RX_MPDU_START_INFO3_RXDMA0_DST_RING_SEL GENMASK(14, 13) + +#define RX_MPDU_START_INFO4_REO_QUEUE_DESC_HI GENMASK(7, 0) +#define RX_MPDU_START_INFO4_RECV_QUEUE_NUM GENMASK(23, 8) +#define RX_MPDU_START_INFO4_PRE_DELIM_ERR_WARN BIT(24) +#define RX_MPDU_START_INFO4_FIRST_DELIM_ERR BIT(25) + +#define RX_MPDU_START_INFO5_KEY_ID GENMASK(7, 0) +#define RX_MPDU_START_INFO5_NEW_PEER_ENTRY BIT(8) +#define RX_MPDU_START_INFO5_DECRYPT_NEEDED BIT(9) +#define RX_MPDU_START_INFO5_DECAP_TYPE GENMASK(11, 10) +#define RX_MPDU_START_INFO5_VLAN_TAG_C_PADDING BIT(12) +#define RX_MPDU_START_INFO5_VLAN_TAG_S_PADDING BIT(13) +#define RX_MPDU_START_INFO5_STRIP_VLAN_TAG_C BIT(14) +#define RX_MPDU_START_INFO5_STRIP_VLAN_TAG_S BIT(15) +#define RX_MPDU_START_INFO5_PRE_DELIM_COUNT GENMASK(27, 16) +#define RX_MPDU_START_INFO5_AMPDU_FLAG BIT(28) +#define RX_MPDU_START_INFO5_BAR_FRAME BIT(29) + +#define RX_MPDU_START_INFO6_MPDU_LEN GENMASK(13, 0) +#define RX_MPDU_START_INFO6_FIRST_MPDU BIT(14) +#define RX_MPDU_START_INFO6_MCAST_BCAST BIT(15) +#define RX_MPDU_START_INFO6_AST_IDX_NOT_FOUND BIT(16) +#define RX_MPDU_START_INFO6_AST_IDX_TIMEOUT BIT(17) +#define RX_MPDU_START_INFO6_POWER_MGMT BIT(18) +#define RX_MPDU_START_INFO6_NON_QOS BIT(19) +#define RX_MPDU_START_INFO6_NULL_DATA BIT(20) +#define RX_MPDU_START_INFO6_MGMT_TYPE BIT(21) +#define RX_MPDU_START_INFO6_CTRL_TYPE BIT(22) +#define RX_MPDU_START_INFO6_MORE_DATA BIT(23) +#define RX_MPDU_START_INFO6_EOSP BIT(24) +#define RX_MPDU_START_INFO6_FRAGMENT BIT(25) +#define RX_MPDU_START_INFO6_ORDER BIT(26) +#define RX_MPDU_START_INFO6_UAPSD_TRIGGER BIT(27) +#define RX_MPDU_START_INFO6_ENCRYPT_REQUIRED BIT(28) +#define RX_MPDU_START_INFO6_DIRECTED BIT(29) + +#define RX_MPDU_START_RAW_MPDU BIT(0) + +struct rx_mpdu_start_ipq8074 { + uint16_t info0; + uint16_t phy_ppdu_id; + uint16_t ast_index; + uint16_t sw_peer_id; + uint32_t info1; + uint32_t info2; + uint32_t pn[4]; + uint32_t peer_meta_data; + uint32_t info3; + uint32_t reo_queue_desc_lo; + uint32_t info4; + uint32_t info5; + uint32_t info6; + uint16_t frame_ctrl; + uint16_t duration; + uint8_t addr1[IEEE80211_ADDR_LEN]; + uint8_t addr2[IEEE80211_ADDR_LEN]; + uint8_t addr3[IEEE80211_ADDR_LEN]; + uint16_t seq_ctrl; + uint8_t addr4[IEEE80211_ADDR_LEN]; + uint16_t qos_ctrl; + uint32_t ht_ctrl; + uint32_t raw; +} __packed; + +#define RX_MPDU_START_INFO7_REO_DEST_IND GENMASK(4, 0) +#define RX_MPDU_START_INFO7_LMAC_PEER_ID_MSB GENMASK(6, 5) +#define RX_MPDU_START_INFO7_FLOW_ID_TOEPLITZ BIT(7) +#define RX_MPDU_START_INFO7_PKT_SEL_FP_UCAST_DATA BIT(8) +#define RX_MPDU_START_INFO7_PKT_SEL_FP_MCAST_DATA BIT(9) +#define RX_MPDU_START_INFO7_PKT_SEL_FP_CTRL_BAR BIT(10) +#define RX_MPDU_START_INFO7_RXDMA0_SRC_RING_SEL GENMASK(12, 11) +#define RX_MPDU_START_INFO7_RXDMA0_DST_RING_SEL GENMASK(14, 13) + +#define RX_MPDU_START_INFO8_REO_QUEUE_DESC_HI GENMASK(7, 0) +#define RX_MPDU_START_INFO8_RECV_QUEUE_NUM GENMASK(23, 8) +#define RX_MPDU_START_INFO8_PRE_DELIM_ERR_WARN BIT(24) +#define RX_MPDU_START_INFO8_FIRST_DELIM_ERR BIT(25) + +#define RX_MPDU_START_INFO9_EPD_EN BIT(0) +#define RX_MPDU_START_INFO9_ALL_FRAME_ENCPD BIT(1) +#define RX_MPDU_START_INFO9_ENC_TYPE GENMASK(5, 2) +#define RX_MPDU_START_INFO9_VAR_WEP_KEY_WIDTH GENMASK(7, 6) +#define RX_MPDU_START_INFO9_MESH_STA GENMASK(9, 8) +#define RX_MPDU_START_INFO9_BSSID_HIT BIT(10) +#define RX_MPDU_START_INFO9_BSSID_NUM GENMASK(14, 11) +#define RX_MPDU_START_INFO9_TID GENMASK(18, 15) + +#define RX_MPDU_START_INFO10_RXPCU_MPDU_FLTR GENMASK(1, 0) +#define RX_MPDU_START_INFO10_SW_FRAME_GRP_ID GENMASK(8, 2) +#define RX_MPDU_START_INFO10_NDP_FRAME BIT(9) +#define RX_MPDU_START_INFO10_PHY_ERR BIT(10) +#define RX_MPDU_START_INFO10_PHY_ERR_MPDU_HDR BIT(11) +#define RX_MPDU_START_INFO10_PROTO_VER_ERR BIT(12) +#define RX_MPDU_START_INFO10_AST_LOOKUP_VALID BIT(13) + +#define RX_MPDU_START_INFO11_MPDU_FCTRL_VALID BIT(0) +#define RX_MPDU_START_INFO11_MPDU_DUR_VALID BIT(1) +#define RX_MPDU_START_INFO11_MAC_ADDR1_VALID BIT(2) +#define RX_MPDU_START_INFO11_MAC_ADDR2_VALID BIT(3) +#define RX_MPDU_START_INFO11_MAC_ADDR3_VALID BIT(4) +#define RX_MPDU_START_INFO11_MAC_ADDR4_VALID BIT(5) +#define RX_MPDU_START_INFO11_MPDU_SEQ_CTRL_VALID BIT(6) +#define RX_MPDU_START_INFO11_MPDU_QOS_CTRL_VALID BIT(7) +#define RX_MPDU_START_INFO11_MPDU_HT_CTRL_VALID BIT(8) +#define RX_MPDU_START_INFO11_ENCRYPT_INFO_VALID BIT(9) +#define RX_MPDU_START_INFO11_MPDU_FRAG_NUMBER GENMASK(13, 10) +#define RX_MPDU_START_INFO11_MORE_FRAG_FLAG BIT(14) +#define RX_MPDU_START_INFO11_FROM_DS BIT(16) +#define RX_MPDU_START_INFO11_TO_DS BIT(17) +#define RX_MPDU_START_INFO11_ENCRYPTED BIT(18) +#define RX_MPDU_START_INFO11_MPDU_RETRY BIT(19) +#define RX_MPDU_START_INFO11_MPDU_SEQ_NUM GENMASK(31, 20) + +#define RX_MPDU_START_INFO12_KEY_ID GENMASK(7, 0) +#define RX_MPDU_START_INFO12_NEW_PEER_ENTRY BIT(8) +#define RX_MPDU_START_INFO12_DECRYPT_NEEDED BIT(9) +#define RX_MPDU_START_INFO12_DECAP_TYPE GENMASK(11, 10) +#define RX_MPDU_START_INFO12_VLAN_TAG_C_PADDING BIT(12) +#define RX_MPDU_START_INFO12_VLAN_TAG_S_PADDING BIT(13) +#define RX_MPDU_START_INFO12_STRIP_VLAN_TAG_C BIT(14) +#define RX_MPDU_START_INFO12_STRIP_VLAN_TAG_S BIT(15) +#define RX_MPDU_START_INFO12_PRE_DELIM_COUNT GENMASK(27, 16) +#define RX_MPDU_START_INFO12_AMPDU_FLAG BIT(28) +#define RX_MPDU_START_INFO12_BAR_FRAME BIT(29) +#define RX_MPDU_START_INFO12_RAW_MPDU BIT(30) + +#define RX_MPDU_START_INFO13_MPDU_LEN GENMASK(13, 0) +#define RX_MPDU_START_INFO13_FIRST_MPDU BIT(14) +#define RX_MPDU_START_INFO13_MCAST_BCAST BIT(15) +#define RX_MPDU_START_INFO13_AST_IDX_NOT_FOUND BIT(16) +#define RX_MPDU_START_INFO13_AST_IDX_TIMEOUT BIT(17) +#define RX_MPDU_START_INFO13_POWER_MGMT BIT(18) +#define RX_MPDU_START_INFO13_NON_QOS BIT(19) +#define RX_MPDU_START_INFO13_NULL_DATA BIT(20) +#define RX_MPDU_START_INFO13_MGMT_TYPE BIT(21) +#define RX_MPDU_START_INFO13_CTRL_TYPE BIT(22) +#define RX_MPDU_START_INFO13_MORE_DATA BIT(23) +#define RX_MPDU_START_INFO13_EOSP BIT(24) +#define RX_MPDU_START_INFO13_FRAGMENT BIT(25) +#define RX_MPDU_START_INFO13_ORDER BIT(26) +#define RX_MPDU_START_INFO13_UAPSD_TRIGGER BIT(27) +#define RX_MPDU_START_INFO13_ENCRYPT_REQUIRED BIT(28) +#define RX_MPDU_START_INFO13_DIRECTED BIT(29) +#define RX_MPDU_START_INFO13_AMSDU_PRESENT BIT(30) + +struct rx_mpdu_start_qcn9074 { + uint32_t info7; + uint32_t reo_queue_desc_lo; + uint32_t info8; + uint32_t pn[4]; + uint32_t info9; + uint32_t peer_meta_data; + uint16_t info10; + uint16_t phy_ppdu_id; + uint16_t ast_index; + uint16_t sw_peer_id; + uint32_t info11; + uint32_t info12; + uint32_t info13; + uint16_t frame_ctrl; + uint16_t duration; + uint8_t addr1[IEEE80211_ADDR_LEN]; + uint8_t addr2[IEEE80211_ADDR_LEN]; + uint8_t addr3[IEEE80211_ADDR_LEN]; + uint16_t seq_ctrl; + uint8_t addr4[IEEE80211_ADDR_LEN]; + uint16_t qos_ctrl; + uint32_t ht_ctrl; +} __packed; + +struct rx_mpdu_start_wcn6855 { + uint32_t info3; + uint32_t reo_queue_desc_lo; + uint32_t info4; + uint32_t pn[4]; + uint32_t info2; + uint32_t peer_meta_data; + uint16_t info0; + uint16_t phy_ppdu_id; + uint16_t ast_index; + uint16_t sw_peer_id; + uint32_t info1; + uint32_t info5; + uint32_t info6; + uint16_t frame_ctrl; + uint16_t duration; + uint8_t addr1[IEEE80211_ADDR_LEN]; + uint8_t addr2[IEEE80211_ADDR_LEN]; + uint8_t addr3[IEEE80211_ADDR_LEN]; + uint16_t seq_ctrl; + uint8_t addr4[IEEE80211_ADDR_LEN]; + uint16_t qos_ctrl; + uint32_t ht_ctrl; +} __packed; + +/* rx_mpdu_start + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * Note: for ndp frame, if it was expected because the preceding + * NDPA was filter_pass, the setting rxpcu_filter_pass will be + * used. This setting will also be used for every ndp frame in + * case Promiscuous mode is enabled. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * ndp_frame + * Indicates that the received frame was an NDP frame. + * + * phy_err + * Indicates that PHY error was received before MAC received data. + * + * phy_err_during_mpdu_header + * PHY error was received before MAC received the complete MPDU + * header which was needed for proper decoding. + * + * protocol_version_err + * RXPCU detected a version error in the frame control field. + * + * ast_based_lookup_valid + * AST based lookup for this frame has found a valid result. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * ast_index + * This field indicates the index of the AST entry corresponding + * to this MPDU. It is provided by the GSE module instantiated in + * RXPCU. A value of 0xFFFF indicates an invalid AST index. + * + * sw_peer_id + * This field indicates a unique peer identifier. It is set equal + * to field 'sw_peer_id' from the AST entry. + * + * mpdu_frame_control_valid, mpdu_duration_valid, mpdu_qos_control_valid, + * mpdu_ht_control_valid, frame_encryption_info_valid + * Indicates that each fields have valid entries. + * + * mac_addr_adx_valid + * Corresponding mac_addr_adx_{lo/hi} has valid entries. + * + * from_ds, to_ds + * Valid only when mpdu_frame_control_valid is set. Indicates that + * frame is received from DS and sent to DS. + * + * encrypted + * Protected bit from the frame control. + * + * mpdu_retry + * Retry bit from frame control. Only valid when first_msdu is set. + * + * mpdu_sequence_number + * The sequence number from the 802.11 header. + * + * epd_en + * If set, use EPD instead of LPD. + * + * all_frames_shall_be_encrypted + * If set, all frames (data only?) shall be encrypted. If not, + * RX CRYPTO shall set an error flag. + * + * encrypt_type + * Values are defined in enum %HAL_ENCRYPT_TYPE_. + * + * mesh_sta + * Indicates a Mesh (11s) STA. + * + * bssid_hit + * BSSID of the incoming frame matched one of the 8 BSSID + * register values. + * + * bssid_number + * This number indicates which one out of the 8 BSSID register + * values matched the incoming frame. + * + * tid + * TID field in the QoS control field + * + * pn + * The PN number. + * + * peer_meta_data + * Meta data that SW has programmed in the Peer table entry + * of the transmitting STA. + * + * rx_reo_queue_desc_addr_lo + * Address (lower 32 bits) of the REO queue descriptor. + * + * rx_reo_queue_desc_addr_hi + * Address (upper 8 bits) of the REO queue descriptor. + * + * receive_queue_number + * Indicates the MPDU queue ID to which this MPDU link + * descriptor belongs. + * + * pre_delim_err_warning + * Indicates that a delimiter FCS error was found in between the + * previous MPDU and this MPDU. Note that this is just a warning, + * and does not mean that this MPDU is corrupted in any way. If + * it is, there will be other errors indicated such as FCS or + * decrypt errors. + * + * first_delim_err + * Indicates that the first delimiter had a FCS failure. + * + * key_id + * The key ID octet from the IV. + * + * new_peer_entry + * Set if new RX_PEER_ENTRY TLV follows. If clear, RX_PEER_ENTRY + * doesn't follow so RX DECRYPTION module either uses old peer + * entry or not decrypt. + * + * decrypt_needed + * When RXPCU sets bit 'ast_index_not_found or ast_index_timeout', + * RXPCU will also ensure that this bit is NOT set. CRYPTO for that + * reason only needs to evaluate this bit and non of the other ones + * + * decap_type + * Used by the OLE during decapsulation. Values are defined in + * enum %MPDU_START_DECAP_TYPE_*. + * + * rx_insert_vlan_c_tag_padding + * rx_insert_vlan_s_tag_padding + * Insert 4 byte of all zeros as VLAN tag or double VLAN tag if + * the rx payload does not have VLAN. + * + * strip_vlan_c_tag_decap + * strip_vlan_s_tag_decap + * Strip VLAN or double VLAN during decapsulation. + * + * pre_delim_count + * The number of delimiters before this MPDU. Note that this + * number is cleared at PPDU start. If this MPDU is the first + * received MPDU in the PPDU and this MPDU gets filtered-in, + * this field will indicate the number of delimiters located + * after the last MPDU in the previous PPDU. + * + * If this MPDU is located after the first received MPDU in + * an PPDU, this field will indicate the number of delimiters + * located between the previous MPDU and this MPDU. + * + * ampdu_flag + * Received frame was part of an A-MPDU. + * + * bar_frame + * Received frame is a BAR frame + * + * mpdu_length + * MPDU length before decapsulation. + * + * first_mpdu..directed + * See definition in RX attention descriptor + * + */ + +enum rx_msdu_start_pkt_type { + RX_MSDU_START_PKT_TYPE_11A, + RX_MSDU_START_PKT_TYPE_11B, + RX_MSDU_START_PKT_TYPE_11N, + RX_MSDU_START_PKT_TYPE_11AC, + RX_MSDU_START_PKT_TYPE_11AX, +}; + +enum rx_msdu_start_sgi { + RX_MSDU_START_SGI_0_8_US, + RX_MSDU_START_SGI_0_4_US, + RX_MSDU_START_SGI_1_6_US, + RX_MSDU_START_SGI_3_2_US, +}; + +enum rx_msdu_start_recv_bw { + RX_MSDU_START_RECV_BW_20MHZ, + RX_MSDU_START_RECV_BW_40MHZ, + RX_MSDU_START_RECV_BW_80MHZ, + RX_MSDU_START_RECV_BW_160MHZ, +}; + +enum rx_msdu_start_reception_type { + RX_MSDU_START_RECEPTION_TYPE_SU, + RX_MSDU_START_RECEPTION_TYPE_DL_MU_MIMO, + RX_MSDU_START_RECEPTION_TYPE_DL_MU_OFDMA, + RX_MSDU_START_RECEPTION_TYPE_DL_MU_OFDMA_MIMO, + RX_MSDU_START_RECEPTION_TYPE_UL_MU_MIMO, + RX_MSDU_START_RECEPTION_TYPE_UL_MU_OFDMA, + RX_MSDU_START_RECEPTION_TYPE_UL_MU_OFDMA_MIMO, +}; + +#define RX_MSDU_START_INFO1_MSDU_LENGTH GENMASK(13, 0) +#define RX_MSDU_START_INFO1_RSVD_1A BIT(14) +#define RX_MSDU_START_INFO1_IPSEC_ESP BIT(15) +#define RX_MSDU_START_INFO1_L3_OFFSET GENMASK(22, 16) +#define RX_MSDU_START_INFO1_IPSEC_AH BIT(23) +#define RX_MSDU_START_INFO1_L4_OFFSET GENMASK(31, 24) + +#define RX_MSDU_START_INFO2_MSDU_NUMBER GENMASK(7, 0) +#define RX_MSDU_START_INFO2_DECAP_TYPE GENMASK(9, 8) +#define RX_MSDU_START_INFO2_IPV4 BIT(10) +#define RX_MSDU_START_INFO2_IPV6 BIT(11) +#define RX_MSDU_START_INFO2_TCP BIT(12) +#define RX_MSDU_START_INFO2_UDP BIT(13) +#define RX_MSDU_START_INFO2_IP_FRAG BIT(14) +#define RX_MSDU_START_INFO2_TCP_ONLY_ACK BIT(15) +#define RX_MSDU_START_INFO2_DA_IS_BCAST_MCAST BIT(16) +#define RX_MSDU_START_INFO2_SELECTED_TOEPLITZ_HASH GENMASK(18, 17) +#define RX_MSDU_START_INFO2_IP_FIXED_HDR_VALID BIT(19) +#define RX_MSDU_START_INFO2_IP_EXTN_HDR_VALID BIT(20) +#define RX_MSDU_START_INFO2_IP_TCP_UDP_HDR_VALID BIT(21) +#define RX_MSDU_START_INFO2_MESH_CTRL_PRESENT BIT(22) +#define RX_MSDU_START_INFO2_LDPC BIT(23) +#define RX_MSDU_START_INFO2_IP4_IP6_NXT_HDR GENMASK(31, 24) +#define RX_MSDU_START_INFO2_DECAP_FORMAT GENMASK(9, 8) + +#define RX_MSDU_START_INFO3_USER_RSSI GENMASK(7, 0) +#define RX_MSDU_START_INFO3_PKT_TYPE GENMASK(11, 8) +#define RX_MSDU_START_INFO3_STBC BIT(12) +#define RX_MSDU_START_INFO3_SGI GENMASK(14, 13) +#define RX_MSDU_START_INFO3_RATE_MCS GENMASK(18, 15) +#define RX_MSDU_START_INFO3_RECV_BW GENMASK(20, 19) +#define RX_MSDU_START_INFO3_RECEPTION_TYPE GENMASK(23, 21) +#define RX_MSDU_START_INFO3_MIMO_SS_BITMAP GENMASK(31, 24) + +struct rx_msdu_start_ipq8074 { + uint16_t info0; + uint16_t phy_ppdu_id; + uint32_t info1; + uint32_t info2; + uint32_t toeplitz_hash; + uint32_t flow_id_toeplitz; + uint32_t info3; + uint32_t ppdu_start_timestamp; + uint32_t phy_meta_data; +} __packed; + +struct rx_msdu_start_qcn9074 { + uint16_t info0; + uint16_t phy_ppdu_id; + uint32_t info1; + uint32_t info2; + uint32_t toeplitz_hash; + uint32_t flow_id_toeplitz; + uint32_t info3; + uint32_t ppdu_start_timestamp; + uint32_t phy_meta_data; + uint16_t vlan_ctag_c1; + uint16_t vlan_stag_c1; +} __packed; + +struct rx_msdu_start_wcn6855 { + uint16_t info0; + uint16_t phy_ppdu_id; + uint32_t info1; + uint32_t info2; + uint32_t toeplitz_hash; + uint32_t flow_id_toeplitz; + uint32_t info3; + uint32_t ppdu_start_timestamp; + uint32_t phy_meta_data; + uint16_t vlan_ctag_ci; + uint16_t vlan_stag_ci; +} __packed; + +/* rx_msdu_start + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * msdu_length + * MSDU length in bytes after decapsulation. + * + * ipsec_esp + * Set if IPv4/v6 packet is using IPsec ESP. + * + * l3_offset + * Depending upon mode bit, this field either indicates the + * L3 offset in bytes from the start of the RX_HEADER or the IP + * offset in bytes from the start of the packet after + * decapsulation. The latter is only valid if ipv4_proto or + * ipv6_proto is set. + * + * ipsec_ah + * Set if IPv4/v6 packet is using IPsec AH + * + * l4_offset + * Depending upon mode bit, this field either indicates the + * L4 offset in bytes from the start of RX_HEADER (only valid + * if either ipv4_proto or ipv6_proto is set to 1) or indicates + * the offset in bytes to the start of TCP or UDP header from + * the start of the IP header after decapsulation (Only valid if + * tcp_proto or udp_proto is set). The value 0 indicates that + * the offset is longer than 127 bytes. + * + * msdu_number + * Indicates the MSDU number within a MPDU. This value is + * reset to zero at the start of each MPDU. If the number of + * MSDU exceeds 255 this number will wrap using modulo 256. + * + * decap_type + * Indicates the format after decapsulation. Values are defined in + * enum %MPDU_START_DECAP_TYPE_*. + * + * ipv4_proto + * Set if L2 layer indicates IPv4 protocol. + * + * ipv6_proto + * Set if L2 layer indicates IPv6 protocol. + * + * tcp_proto + * Set if the ipv4_proto or ipv6_proto are set and the IP protocol + * indicates TCP. + * + * udp_proto + * Set if the ipv4_proto or ipv6_proto are set and the IP protocol + * indicates UDP. + * + * ip_frag + * Indicates that either the IP More frag bit is set or IP frag + * number is non-zero. If set indicates that this is a fragmented + * IP packet. + * + * tcp_only_ack + * Set if only the TCP Ack bit is set in the TCP flags and if + * the TCP payload is 0. + * + * da_is_bcast_mcast + * The destination address is broadcast or multicast. + * + * toeplitz_hash + * Actual chosen Hash. + * 0 - Toeplitz hash of 2-tuple (IP source address, IP + * destination address) + * 1 - Toeplitz hash of 4-tuple (IP source address, + * IP destination address, L4 (TCP/UDP) source port, + * L4 (TCP/UDP) destination port) + * 2 - Toeplitz of flow_id + * 3 - Zero is used + * + * ip_fixed_header_valid + * Fixed 20-byte IPv4 header or 40-byte IPv6 header parsed + * fully within first 256 bytes of the packet + * + * ip_extn_header_valid + * IPv6/IPv6 header, including IPv4 options and + * recognizable extension headers parsed fully within first 256 + * bytes of the packet + * + * tcp_udp_header_valid + * Fixed 20-byte TCP (excluding TCP options) or 8-byte UDP + * header parsed fully within first 256 bytes of the packet + * + * mesh_control_present + * When set, this MSDU includes the 'Mesh Control' field + * + * ldpc + * + * ip4_protocol_ip6_next_header + * For IPv4, this is the 8 bit protocol field set). For IPv6 this + * is the 8 bit next_header field. + * + * toeplitz_hash_2_or_4 + * Controlled by RxOLE register - If register bit set to 0, + * Toeplitz hash is computed over 2-tuple IPv4 or IPv6 src/dest + * addresses; otherwise, toeplitz hash is computed over 4-tuple + * IPv4 or IPv6 src/dest addresses and src/dest ports. + * + * flow_id_toeplitz + * Toeplitz hash of 5-tuple + * {IP source address, IP destination address, IP source port, IP + * destination port, L4 protocol} in case of non-IPSec. + * + * In case of IPSec - Toeplitz hash of 4-tuple + * {IP source address, IP destination address, SPI, L4 protocol} + * + * The relevant Toeplitz key registers are provided in RxOLE's + * instance of common parser module. These registers are separate + * from the Toeplitz keys used by ASE/FSE modules inside RxOLE. + * The actual value will be passed on from common parser module + * to RxOLE in one of the WHO_* TLVs. + * + * user_rssi + * RSSI for this user + * + * pkt_type + * Values are defined in enum %RX_MSDU_START_PKT_TYPE_*. + * + * stbc + * When set, use STBC transmission rates. + * + * sgi + * Field only valid when pkt type is HT, VHT or HE. Values are + * defined in enum %RX_MSDU_START_SGI_*. + * + * rate_mcs + * MCS Rate used. + * + * receive_bandwidth + * Full receive Bandwidth. Values are defined in enum + * %RX_MSDU_START_RECV_*. + * + * reception_type + * Indicates what type of reception this is and defined in enum + * %RX_MSDU_START_RECEPTION_TYPE_*. + * + * mimo_ss_bitmap + * Field only valid when + * Reception_type is RX_MSDU_START_RECEPTION_TYPE_DL_MU_MIMO or + * RX_MSDU_START_RECEPTION_TYPE_DL_MU_OFDMA_MIMO. + * + * Bitmap, with each bit indicating if the related spatial + * stream is used for this STA + * + * LSB related to SS 0 + * + * 0 - spatial stream not used for this reception + * 1 - spatial stream used for this reception + * + * ppdu_start_timestamp + * Timestamp that indicates when the PPDU that contained this MPDU + * started on the medium. + * + * phy_meta_data + * SW programmed Meta data provided by the PHY. Can be used for SW + * to indicate the channel the device is on. + */ + +#define RX_MSDU_END_INFO0_RXPCU_MPDU_FITLER GENMASK(1, 0) +#define RX_MSDU_END_INFO0_SW_FRAME_GRP_ID GENMASK(8, 2) + +#define RX_MSDU_END_INFO1_KEY_ID GENMASK(7, 0) +#define RX_MSDU_END_INFO1_CCE_SUPER_RULE GENMASK(13, 8) +#define RX_MSDU_END_INFO1_CCND_TRUNCATE BIT(14) +#define RX_MSDU_END_INFO1_CCND_CCE_DIS BIT(15) +#define RX_MSDU_END_INFO1_EXT_WAPI_PN GENMASK(31, 16) + +#define RX_MSDU_END_INFO2_REPORTED_MPDU_LEN GENMASK(13, 0) +#define RX_MSDU_END_INFO2_FIRST_MSDU BIT(14) +#define RX_MSDU_END_INFO2_FIRST_MSDU_WCN6855 BIT(28) +#define RX_MSDU_END_INFO2_LAST_MSDU BIT(15) +#define RX_MSDU_END_INFO2_LAST_MSDU_WCN6855 BIT(29) +#define RX_MSDU_END_INFO2_SA_IDX_TIMEOUT BIT(16) +#define RX_MSDU_END_INFO2_DA_IDX_TIMEOUT BIT(17) +#define RX_MSDU_END_INFO2_MSDU_LIMIT_ERR BIT(18) +#define RX_MSDU_END_INFO2_FLOW_IDX_TIMEOUT BIT(19) +#define RX_MSDU_END_INFO2_FLOW_IDX_INVALID BIT(20) +#define RX_MSDU_END_INFO2_WIFI_PARSER_ERR BIT(21) +#define RX_MSDU_END_INFO2_AMSDU_PARSET_ERR BIT(22) +#define RX_MSDU_END_INFO2_SA_IS_VALID BIT(23) +#define RX_MSDU_END_INFO2_DA_IS_VALID BIT(24) +#define RX_MSDU_END_INFO2_DA_IS_MCBC BIT(25) +#define RX_MSDU_END_INFO2_L3_HDR_PADDING GENMASK(27, 26) + +#define RX_MSDU_END_INFO3_TCP_FLAG GENMASK(8, 0) +#define RX_MSDU_END_INFO3_LRO_ELIGIBLE BIT(9) + +#define RX_MSDU_END_INFO4_DA_OFFSET GENMASK(5, 0) +#define RX_MSDU_END_INFO4_SA_OFFSET GENMASK(11, 6) +#define RX_MSDU_END_INFO4_DA_OFFSET_VALID BIT(12) +#define RX_MSDU_END_INFO4_SA_OFFSET_VALID BIT(13) +#define RX_MSDU_END_INFO4_L3_TYPE GENMASK(31, 16) + +#define RX_MSDU_END_INFO5_MSDU_DROP BIT(0) +#define RX_MSDU_END_INFO5_REO_DEST_IND GENMASK(5, 1) +#define RX_MSDU_END_INFO5_FLOW_IDX GENMASK(25, 6) + +struct rx_msdu_end_ipq8074 { + uint16_t info0; + uint16_t phy_ppdu_id; + uint16_t ip_hdr_cksum; + uint16_t tcp_udp_cksum; + uint32_t info1; + uint32_t ext_wapi_pn[2]; + uint32_t info2; + uint32_t ipv6_options_crc; + uint32_t tcp_seq_num; + uint32_t tcp_ack_num; + uint16_t info3; + uint16_t window_size; + uint32_t info4; + uint32_t rule_indication[2]; + uint16_t sa_idx; + uint16_t da_idx; + uint32_t info5; + uint32_t fse_metadata; + uint16_t cce_metadata; + uint16_t sa_sw_peer_id; +} __packed; + +struct rx_msdu_end_wcn6855 { + uint16_t info0; + uint16_t phy_ppdu_id; + uint16_t ip_hdr_cksum; + uint16_t reported_mpdu_len; + uint32_t info1; + uint32_t ext_wapi_pn[2]; + uint32_t info4; + uint32_t ipv6_options_crc; + uint32_t tcp_seq_num; + uint32_t tcp_ack_num; + uint16_t info3; + uint16_t window_size; + uint32_t info2; + uint16_t sa_idx; + uint16_t da_idx; + uint32_t info5; + uint32_t fse_metadata; + uint16_t cce_metadata; + uint16_t sa_sw_peer_id; + uint32_t rule_indication[2]; + uint32_t info6; + uint32_t info7; +} __packed; + +#define RX_MSDU_END_MPDU_LENGTH_INFO GENMASK(13, 0) + +#define RX_MSDU_END_INFO2_DA_OFFSET GENMASK(5, 0) +#define RX_MSDU_END_INFO2_SA_OFFSET GENMASK(11, 6) +#define RX_MSDU_END_INFO2_DA_OFFSET_VALID BIT(12) +#define RX_MSDU_END_INFO2_SA_OFFSET_VALID BIT(13) +#define RX_MSDU_END_INFO2_L3_TYPE GENMASK(31, 16) + +#define RX_MSDU_END_INFO4_SA_IDX_TIMEOUT BIT(0) +#define RX_MSDU_END_INFO4_DA_IDX_TIMEOUT BIT(1) +#define RX_MSDU_END_INFO4_MSDU_LIMIT_ERR BIT(2) +#define RX_MSDU_END_INFO4_FLOW_IDX_TIMEOUT BIT(3) +#define RX_MSDU_END_INFO4_FLOW_IDX_INVALID BIT(4) +#define RX_MSDU_END_INFO4_WIFI_PARSER_ERR BIT(5) +#define RX_MSDU_END_INFO4_AMSDU_PARSER_ERR BIT(6) +#define RX_MSDU_END_INFO4_SA_IS_VALID BIT(7) +#define RX_MSDU_END_INFO4_DA_IS_VALID BIT(8) +#define RX_MSDU_END_INFO4_DA_IS_MCBC BIT(9) +#define RX_MSDU_END_INFO4_L3_HDR_PADDING GENMASK(11, 10) +#define RX_MSDU_END_INFO4_FIRST_MSDU BIT(12) +#define RX_MSDU_END_INFO4_LAST_MSDU BIT(13) + +#define RX_MSDU_END_INFO6_AGGR_COUNT GENMASK(7, 0) +#define RX_MSDU_END_INFO6_FLOW_AGGR_CONTN BIT(8) +#define RX_MSDU_END_INFO6_FISA_TIMEOUT BIT(9) + +struct rx_msdu_end_qcn9074 { + uint16_t info0; + uint16_t phy_ppdu_id; + uint16_t ip_hdr_cksum; + uint16_t mpdu_length_info; + uint32_t info1; + uint32_t rule_indication[2]; + uint32_t info2; + uint32_t ipv6_options_crc; + uint32_t tcp_seq_num; + uint32_t tcp_ack_num; + uint16_t info3; + uint16_t window_size; + uint16_t tcp_udp_cksum; + uint16_t info4; + uint16_t sa_idx; + uint16_t da_idx; + uint32_t info5; + uint32_t fse_metadata; + uint16_t cce_metadata; + uint16_t sa_sw_peer_id; + uint32_t info6; + uint16_t cum_l4_cksum; + uint16_t cum_ip_length; +} __packed; + +/* rx_msdu_end + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * ip_hdr_cksum + * This can include the IP header checksum or the pseudo + * header checksum used by TCP/UDP checksum. + * + * tcp_udp_chksum + * The value of the computed TCP/UDP checksum. A mode bit + * selects whether this checksum is the full checksum or the + * partial checksum which does not include the pseudo header. + * + * key_id + * The key ID octet from the IV. Only valid when first_msdu is set. + * + * cce_super_rule + * Indicates the super filter rule. + * + * cce_classify_not_done_truncate + * Classification failed due to truncated frame. + * + * cce_classify_not_done_cce_dis + * Classification failed due to CCE global disable + * + * ext_wapi_pn* + * Extension PN (packet number) which is only used by WAPI. + * + * reported_mpdu_length + * MPDU length before decapsulation. Only valid when first_msdu is + * set. This field is taken directly from the length field of the + * A-MPDU delimiter or the preamble length field for non-A-MPDU + * frames. + * + * first_msdu + * Indicates the first MSDU of A-MSDU. If both first_msdu and + * last_msdu are set in the MSDU then this is a non-aggregated MSDU + * frame: normal MPDU. Interior MSDU in an A-MSDU shall have both + * first_mpdu and last_mpdu bits set to 0. + * + * last_msdu + * Indicates the last MSDU of the A-MSDU. MPDU end status is only + * valid when last_msdu is set. + * + * sa_idx_timeout + * Indicates an unsuccessful MAC source address search due to the + * expiring of the search timer. + * + * da_idx_timeout + * Indicates an unsuccessful MAC destination address search due to + * the expiring of the search timer. + * + * msdu_limit_error + * Indicates that the MSDU threshold was exceeded and thus all the + * rest of the MSDUs will not be scattered and will not be + * decapsulated but will be DMA'ed in RAW format as a single MSDU. + * + * flow_idx_timeout + * Indicates an unsuccessful flow search due to the expiring of + * the search timer. + * + * flow_idx_invalid + * flow id is not valid. + * + * amsdu_parser_error + * A-MSDU could not be properly de-agregated. + * + * sa_is_valid + * Indicates that OLE found a valid SA entry. + * + * da_is_valid + * Indicates that OLE found a valid DA entry. + * + * da_is_mcbc + * Field Only valid if da_is_valid is set. Indicates the DA address + * was a Multicast of Broadcast address. + * + * l3_header_padding + * Number of bytes padded to make sure that the L3 header will + * always start of a Dword boundary. + * + * ipv6_options_crc + * 32 bit CRC computed out of IP v6 extension headers. + * + * tcp_seq_number + * TCP sequence number. + * + * tcp_ack_number + * TCP acknowledge number. + * + * tcp_flag + * TCP flags {NS, CWR, ECE, URG, ACK, PSH, RST, SYN, FIN}. + * + * lro_eligible + * Computed out of TCP and IP fields to indicate that this + * MSDU is eligible for LRO. + * + * window_size + * TCP receive window size. + * + * da_offset + * Offset into MSDU buffer for DA. + * + * sa_offset + * Offset into MSDU buffer for SA. + * + * da_offset_valid + * da_offset field is valid. This will be set to 0 in case + * of a dynamic A-MSDU when DA is compressed. + * + * sa_offset_valid + * sa_offset field is valid. This will be set to 0 in case + * of a dynamic A-MSDU when SA is compressed. + * + * l3_type + * The 16-bit type value indicating the type of L3 later + * extracted from LLC/SNAP, set to zero if SNAP is not + * available. + * + * rule_indication + * Bitmap indicating which of rules have matched. + * + * sa_idx + * The offset in the address table which matches MAC source address + * + * da_idx + * The offset in the address table which matches MAC destination + * address. + * + * msdu_drop + * REO shall drop this MSDU and not forward it to any other ring. + * + * reo_destination_indication + * The id of the reo exit ring where the msdu frame shall push + * after (MPDU level) reordering has finished. Values are defined + * in enum %HAL_RX_MSDU_DESC_REO_DEST_IND_. + * + * flow_idx + * Flow table index. + * + * fse_metadata + * FSE related meta data. + * + * cce_metadata + * CCE related meta data. + * + * sa_sw_peer_id + * sw_peer_id from the address search entry corresponding to the + * source address of the MSDU. + */ + +enum rx_mpdu_end_rxdma_dest_ring { + RX_MPDU_END_RXDMA_DEST_RING_RELEASE, + RX_MPDU_END_RXDMA_DEST_RING_FW, + RX_MPDU_END_RXDMA_DEST_RING_SW, + RX_MPDU_END_RXDMA_DEST_RING_REO, +}; + +#define RX_MPDU_END_INFO1_UNSUP_KTYPE_SHORT_FRAME BIT(11) +#define RX_MPDU_END_INFO1_RX_IN_TX_DECRYPT_BYT BIT(12) +#define RX_MPDU_END_INFO1_OVERFLOW_ERR BIT(13) +#define RX_MPDU_END_INFO1_MPDU_LEN_ERR BIT(14) +#define RX_MPDU_END_INFO1_TKIP_MIC_ERR BIT(15) +#define RX_MPDU_END_INFO1_DECRYPT_ERR BIT(16) +#define RX_MPDU_END_INFO1_UNENCRYPTED_FRAME_ERR BIT(17) +#define RX_MPDU_END_INFO1_PN_FIELDS_VALID BIT(18) +#define RX_MPDU_END_INFO1_FCS_ERR BIT(19) +#define RX_MPDU_END_INFO1_MSDU_LEN_ERR BIT(20) +#define RX_MPDU_END_INFO1_RXDMA0_DEST_RING GENMASK(22, 21) +#define RX_MPDU_END_INFO1_RXDMA1_DEST_RING GENMASK(24, 23) +#define RX_MPDU_END_INFO1_DECRYPT_STATUS_CODE GENMASK(27, 25) +#define RX_MPDU_END_INFO1_RX_BITMAP_NOT_UPD BIT(28) + +struct rx_mpdu_end { + uint16_t info0; + uint16_t phy_ppdu_id; + uint32_t info1; +} __packed; + +/* rx_mpdu_end + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * unsup_ktype_short_frame + * This bit will be '1' when WEP or TKIP or WAPI key type is + * received for 11ah short frame. Crypto will bypass the received + * packet without decryption to RxOLE after setting this bit. + * + * rx_in_tx_decrypt_byp + * Indicates that RX packet is not decrypted as Crypto is + * busy with TX packet processing. + * + * overflow_err + * RXPCU Receive FIFO ran out of space to receive the full MPDU. + * Therefore this MPDU is terminated early and is thus corrupted. + * + * This MPDU will not be ACKed. + * + * RXPCU might still be able to correctly receive the following + * MPDUs in the PPDU if enough fifo space became available in time. + * + * mpdu_length_err + * Set by RXPCU if the expected MPDU length does not correspond + * with the actually received number of bytes in the MPDU. + * + * tkip_mic_err + * Set by Rx crypto when crypto detected a TKIP MIC error for + * this MPDU. + * + * decrypt_err + * Set by RX CRYPTO when CRYPTO detected a decrypt error for this + * MPDU or CRYPTO received an encrypted frame, but did not get a + * valid corresponding key id in the peer entry. + * + * unencrypted_frame_err + * Set by RX CRYPTO when CRYPTO detected an unencrypted frame while + * in the peer entry field 'All_frames_shall_be_encrypted' is set. + * + * pn_fields_contain_valid_info + * Set by RX CRYPTO to indicate that there is a valid PN field + * present in this MPDU. + * + * fcs_err + * Set by RXPCU when there is an FCS error detected for this MPDU. + * + * msdu_length_err + * Set by RXOLE when there is an msdu length error detected + * in at least 1 of the MSDUs embedded within the MPDU. + * + * rxdma0_destination_ring + * rxdma1_destination_ring + * The ring to which RXDMA0/1 shall push the frame, assuming + * no MPDU level errors are detected. In case of MPDU level + * errors, RXDMA0/1 might change the RXDMA0/1 destination. Values + * are defined in %enum RX_MPDU_END_RXDMA_DEST_RING_*. + * + * decrypt_status_code + * Field provides insight into the decryption performed. Values + * are defined in enum %RX_DESC_DECRYPT_STATUS_CODE_*. + * + * rx_bitmap_not_updated + * Frame is received, but RXPCU could not update the receive bitmap + * due to (temporary) fifo constraints. + */ + +/* Padding bytes to avoid TLV's spanning across 128 byte boundary */ +#define HAL_RX_DESC_PADDING0_BYTES 4 +#define HAL_RX_DESC_PADDING1_BYTES 16 + +#define HAL_RX_DESC_HDR_STATUS_LEN 120 + +struct hal_rx_desc_ipq8074 { + uint32_t msdu_end_tag; + struct rx_msdu_end_ipq8074 msdu_end; + uint32_t rx_attn_tag; + struct rx_attention attention; + uint32_t msdu_start_tag; + struct rx_msdu_start_ipq8074 msdu_start; + uint8_t rx_padding0[HAL_RX_DESC_PADDING0_BYTES]; + uint32_t mpdu_start_tag; + struct rx_mpdu_start_ipq8074 mpdu_start; + uint32_t mpdu_end_tag; + struct rx_mpdu_end mpdu_end; + uint8_t rx_padding1[HAL_RX_DESC_PADDING1_BYTES]; + uint32_t hdr_status_tag; + uint32_t phy_ppdu_id; + uint8_t hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; + uint8_t msdu_payload[]; +} __packed; + +struct hal_rx_desc_qcn9074 { + uint32_t msdu_end_tag; + struct rx_msdu_end_qcn9074 msdu_end; + uint32_t rx_attn_tag; + struct rx_attention attention; + uint32_t msdu_start_tag; + struct rx_msdu_start_qcn9074 msdu_start; + uint8_t rx_padding0[HAL_RX_DESC_PADDING0_BYTES]; + uint32_t mpdu_start_tag; + struct rx_mpdu_start_qcn9074 mpdu_start; + uint32_t mpdu_end_tag; + struct rx_mpdu_end mpdu_end; + uint8_t rx_padding1[HAL_RX_DESC_PADDING1_BYTES]; + uint32_t hdr_status_tag; + uint32_t phy_ppdu_id; + uint8_t hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; + uint8_t msdu_payload[]; +} __packed; + +struct hal_rx_desc_wcn6855 { + uint32_t msdu_end_tag; + struct rx_msdu_end_wcn6855 msdu_end; + uint32_t rx_attn_tag; + struct rx_attention attention; + uint32_t msdu_start_tag; + struct rx_msdu_start_wcn6855 msdu_start; + uint8_t rx_padding0[HAL_RX_DESC_PADDING0_BYTES]; + uint32_t mpdu_start_tag; + struct rx_mpdu_start_wcn6855 mpdu_start; + uint32_t mpdu_end_tag; + struct rx_mpdu_end mpdu_end; + uint8_t rx_padding1[HAL_RX_DESC_PADDING1_BYTES]; + uint32_t hdr_status_tag; + uint32_t phy_ppdu_id; + uint8_t hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; + uint8_t msdu_payload[]; +} __packed; + +struct hal_rx_desc { + union { + struct hal_rx_desc_ipq8074 ipq8074; + struct hal_rx_desc_qcn9074 qcn9074; + struct hal_rx_desc_wcn6855 wcn6855; + } u; +} __packed; + +#define HAL_RX_RU_ALLOC_TYPE_MAX 6 +#define RU_26 1 +#define RU_52 2 +#define RU_106 4 +#define RU_242 9 +#define RU_484 18 +#define RU_996 37 + +/* + * dp.h + */ + +/* HTT definitions */ + +#define HTT_TCL_META_DATA_TYPE BIT(0) +#define HTT_TCL_META_DATA_VALID_HTT BIT(1) + +/* vdev meta data */ +#define HTT_TCL_META_DATA_VDEV_ID GENMASK(9, 2) +#define HTT_TCL_META_DATA_PDEV_ID GENMASK(11, 10) +#define HTT_TCL_META_DATA_HOST_INSPECTED BIT(12) + +/* peer meta data */ +#define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 2) + +#define HTT_TX_WBM_COMP_STATUS_OFFSET 8 + +#define HTT_INVALID_PEER_ID 0xffff + +/* HTT tx completion is overlaid in wbm_release_ring */ +#define HTT_TX_WBM_COMP_INFO0_STATUS GENMASK(12, 9) +#define HTT_TX_WBM_COMP_INFO0_REINJECT_REASON GENMASK(16, 13) +#define HTT_TX_WBM_COMP_INFO0_REINJECT_REASON GENMASK(16, 13) + +#define HTT_TX_WBM_COMP_INFO1_ACK_RSSI GENMASK(31, 24) +#define HTT_TX_WBM_COMP_INFO2_SW_PEER_ID GENMASK(15, 0) +#define HTT_TX_WBM_COMP_INFO2_VALID BIT(21) + +struct htt_tx_wbm_completion { + uint32_t info0; + uint32_t info1; + uint32_t info2; + uint32_t info3; +} __packed; + +enum htt_h2t_msg_type { + HTT_H2T_MSG_TYPE_VERSION_REQ = 0, + HTT_H2T_MSG_TYPE_SRING_SETUP = 0xb, + HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG = 0xc, + HTT_H2T_MSG_TYPE_EXT_STATS_CFG = 0x10, + HTT_H2T_MSG_TYPE_PPDU_STATS_CFG = 0x11, + HTT_H2T_MSG_TYPE_RX_FULL_MONITOR_MODE = 0x17, +}; + +#define HTT_VER_REQ_INFO_MSG_ID GENMASK(7, 0) + +struct htt_ver_req_cmd { + uint32_t ver_reg_info; +} __packed; + +enum htt_srng_ring_type { + HTT_HW_TO_SW_RING, + HTT_SW_TO_HW_RING, + HTT_SW_TO_SW_RING, +}; + +enum htt_srng_ring_id { + HTT_RXDMA_HOST_BUF_RING, + HTT_RXDMA_MONITOR_STATUS_RING, + HTT_RXDMA_MONITOR_BUF_RING, + HTT_RXDMA_MONITOR_DESC_RING, + HTT_RXDMA_MONITOR_DEST_RING, + HTT_HOST1_TO_FW_RXBUF_RING, + HTT_HOST2_TO_FW_RXBUF_RING, + HTT_RXDMA_NON_MONITOR_DEST_RING, +}; + +/* host -> target HTT_SRING_SETUP message + * + * After target is booted up, Host can send SRING setup message for + * each host facing LMAC SRING. Target setups up HW registers based + * on setup message and confirms back to Host if response_required is set. + * Host should wait for confirmation message before sending new SRING + * setup message + * + * The message would appear as follows: + * + * |31 24|23 20|19|18 16|15|14 8|7 0| + * |--------------- +-----------------+----------------+------------------| + * | ring_type | ring_id | pdev_id | msg_type | + * |----------------------------------------------------------------------| + * | ring_base_addr_lo | + * |----------------------------------------------------------------------| + * | ring_base_addr_hi | + * |----------------------------------------------------------------------| + * |ring_misc_cfg_flag|ring_entry_size| ring_size | + * |----------------------------------------------------------------------| + * | ring_head_offset32_remote_addr_lo | + * |----------------------------------------------------------------------| + * | ring_head_offset32_remote_addr_hi | + * |----------------------------------------------------------------------| + * | ring_tail_offset32_remote_addr_lo | + * |----------------------------------------------------------------------| + * | ring_tail_offset32_remote_addr_hi | + * |----------------------------------------------------------------------| + * | ring_msi_addr_lo | + * |----------------------------------------------------------------------| + * | ring_msi_addr_hi | + * |----------------------------------------------------------------------| + * | ring_msi_data | + * |----------------------------------------------------------------------| + * | intr_timer_th |IM| intr_batch_counter_th | + * |----------------------------------------------------------------------| + * | reserved |RR|PTCF| intr_low_threshold | + * |----------------------------------------------------------------------| + * Where + * IM = sw_intr_mode + * RR = response_required + * PTCF = prefetch_timer_cfg + * + * The message is interpreted as follows: + * dword0 - b'0:7 - msg_type: This will be set to + * HTT_H2T_MSG_TYPE_SRING_SETUP + * b'8:15 - pdev_id: + * 0 (for rings at SOC/UMAC level), + * 1/2/3 mac id (for rings at LMAC level) + * b'16:23 - ring_id: identify which ring is to setup, + * more details can be got from enum htt_srng_ring_id + * b'24:31 - ring_type: identify type of host rings, + * more details can be got from enum htt_srng_ring_type + * dword1 - b'0:31 - ring_base_addr_lo: Lower 32bits of ring base address + * dword2 - b'0:31 - ring_base_addr_hi: Upper 32bits of ring base address + * dword3 - b'0:15 - ring_size: size of the ring in unit of 4-bytes words + * b'16:23 - ring_entry_size: Size of each entry in 4-byte word units + * b'24:31 - ring_misc_cfg_flag: Valid only for HW_TO_SW_RING and + * SW_TO_HW_RING. + * Refer to HTT_SRING_SETUP_RING_MISC_CFG_RING defs. + * dword4 - b'0:31 - ring_head_off32_remote_addr_lo: + * Lower 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the head + * element within the ring. + * (The head offset variable has type uint32_t.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword5 - b'0:31 - ring_head_off32_remote_addr_hi: + * Upper 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the head + * element within the ring. + * (The head offset variable has type uint32_t.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword6 - b'0:31 - ring_tail_off32_remote_addr_lo: + * Lower 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the tail + * element within the ring. + * (The tail offset variable has type uint32_t.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword7 - b'0:31 - ring_tail_off32_remote_addr_hi: + * Upper 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the tail + * element within the ring. + * (The tail offset variable has type uint32_t.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword8 - b'0:31 - ring_msi_addr_lo: Lower 32bits of MSI cfg address + * valid only for HW_TO_SW_RING and SW_TO_HW_RING + * dword9 - b'0:31 - ring_msi_addr_hi: Upper 32bits of MSI cfg address + * valid only for HW_TO_SW_RING and SW_TO_HW_RING + * dword10 - b'0:31 - ring_msi_data: MSI data + * Refer to HTT_SRING_SETUP_RING_MSC_CFG_xxx defs + * valid only for HW_TO_SW_RING and SW_TO_HW_RING + * dword11 - b'0:14 - intr_batch_counter_th: + * batch counter threshold is in units of 4-byte words. + * HW internally maintains and increments batch count. + * (see SRING spec for detail description). + * When batch count reaches threshold value, an interrupt + * is generated by HW. + * b'15 - sw_intr_mode: + * This configuration shall be static. + * Only programmed at power up. + * 0: generate pulse style sw interrupts + * 1: generate level style sw interrupts + * b'16:31 - intr_timer_th: + * The timer init value when timer is idle or is + * initialized to start downcounting. + * In 8us units (to cover a range of 0 to 524 ms) + * dword12 - b'0:15 - intr_low_threshold: + * Used only by Consumer ring to generate ring_sw_int_p. + * Ring entries low threshold water mark, that is used + * in combination with the interrupt timer as well as + * the clearing of the level interrupt. + * b'16:18 - prefetch_timer_cfg: + * Used only by Consumer ring to set timer mode to + * support Application prefetch handling. + * The external tail offset/pointer will be updated + * at following intervals: + * 3'b000: (Prefetch feature disabled; used only for debug) + * 3'b001: 1 usec + * 3'b010: 4 usec + * 3'b011: 8 usec (default) + * 3'b100: 16 usec + * Others: Reserved + * b'19 - response_required: + * Host needs HTT_T2H_MSG_TYPE_SRING_SETUP_DONE as response + * b'20:31 - reserved: reserved for future use + */ + +#define HTT_SRNG_SETUP_CMD_INFO0_MSG_TYPE GENMASK(7, 0) +#define HTT_SRNG_SETUP_CMD_INFO0_PDEV_ID GENMASK(15, 8) +#define HTT_SRNG_SETUP_CMD_INFO0_RING_ID GENMASK(23, 16) +#define HTT_SRNG_SETUP_CMD_INFO0_RING_TYPE GENMASK(31, 24) + +#define HTT_SRNG_SETUP_CMD_INFO1_RING_SIZE GENMASK(15, 0) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_ENTRY_SIZE GENMASK(23, 16) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_LOOP_CNT_DIS BIT(25) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_MSI_SWAP BIT(27) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_HOST_FW_SWAP BIT(28) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_TLV_SWAP BIT(29) + +#define HTT_SRNG_SETUP_CMD_INTR_INFO_BATCH_COUNTER_THRESH GENMASK(14, 0) +#define HTT_SRNG_SETUP_CMD_INTR_INFO_SW_INTR_MODE BIT(15) +#define HTT_SRNG_SETUP_CMD_INTR_INFO_INTR_TIMER_THRESH GENMASK(31, 16) + +#define HTT_SRNG_SETUP_CMD_INFO2_INTR_LOW_THRESH GENMASK(15, 0) +#define HTT_SRNG_SETUP_CMD_INFO2_PRE_FETCH_TIMER_CFG BIT(16) +#define HTT_SRNG_SETUP_CMD_INFO2_RESPONSE_REQUIRED BIT(19) + +struct htt_srng_setup_cmd { + uint32_t info0; + uint32_t ring_base_addr_lo; + uint32_t ring_base_addr_hi; + uint32_t info1; + uint32_t ring_head_off32_remote_addr_lo; + uint32_t ring_head_off32_remote_addr_hi; + uint32_t ring_tail_off32_remote_addr_lo; + uint32_t ring_tail_off32_remote_addr_hi; + uint32_t ring_msi_addr_lo; + uint32_t ring_msi_addr_hi; + uint32_t msi_data; + uint32_t intr_info; + uint32_t info2; +} __packed; + +/* host -> target FW PPDU_STATS config message + * + * @details + * The following field definitions describe the format of the HTT host + * to target FW for PPDU_STATS_CFG msg. + * The message allows the host to configure the PPDU_STATS_IND messages + * produced by the target. + * + * |31 24|23 16|15 8|7 0| + * |-----------------------------------------------------------| + * | REQ bit mask | pdev_mask | msg type | + * |-----------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this is a req to configure ppdu_stats_ind from target + * Value: 0x11 + * - PDEV_MASK + * Bits 8:15 + * Purpose: identifies which pdevs this PPDU stats configuration applies to + * Value: This is a overloaded field, refer to usage and interpretation of + * PDEV in interface document. + * Bit 8 : Reserved for SOC stats + * Bit 9 - 15 : Indicates PDEV_MASK in DBDC + * Indicates MACID_MASK in DBS + * - REQ_TLV_BIT_MASK + * Bits 16:31 + * Purpose: each set bit indicates the corresponding PPDU stats TLV type + * needs to be included in the target's PPDU_STATS_IND messages. + * Value: refer htt_ppdu_stats_tlv_tag_t << 30 bits + * Refer to PKT_TYPE_ENABLE_FLAG0_xxx_MGMT_xxx defs + * dword3 - b'0:31 - packet_type_enable_flags_1: + * Enable MGMT packet from 0b1010 to 0b1111 + * bits from low to high: FP, MD, MO - 3 bits + * Refer to PKT_TYPE_ENABLE_FLAG1_xxx_MGMT_xxx defs + * dword4 - b'0:31 - packet_type_enable_flags_2: + * Enable CTRL packet from 0b0000 to 0b1001 + * bits from low to high: FP, MD, MO - 3 bits + * Refer to PKT_TYPE_ENABLE_FLAG2_xxx_CTRL_xxx defs + * dword5 - b'0:31 - packet_type_enable_flags_3: + * Enable CTRL packet from 0b1010 to 0b1111, + * MCAST_DATA, UCAST_DATA, NULL_DATA + * bits from low to high: FP, MD, MO - 3 bits + * Refer to PKT_TYPE_ENABLE_FLAG3_xxx_CTRL_xxx defs + * dword6 - b'0:31 - tlv_filter_in_flags: + * Filter in Attention/MPDU/PPDU/Header/User tlvs + * Refer to CFG_TLV_FILTER_IN_FLAG defs + */ + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_MSG_TYPE GENMASK(7, 0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID GENMASK(15, 8) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID GENMASK(23, 16) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS BIT(24) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS BIT(25) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0) + +enum htt_rx_filter_tlv_flags { + HTT_RX_FILTER_TLV_FLAGS_MPDU_START = BIT(0), + HTT_RX_FILTER_TLV_FLAGS_MSDU_START = BIT(1), + HTT_RX_FILTER_TLV_FLAGS_RX_PACKET = BIT(2), + HTT_RX_FILTER_TLV_FLAGS_MSDU_END = BIT(3), + HTT_RX_FILTER_TLV_FLAGS_MPDU_END = BIT(4), + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER = BIT(5), + HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER = BIT(6), + HTT_RX_FILTER_TLV_FLAGS_ATTENTION = BIT(7), + HTT_RX_FILTER_TLV_FLAGS_PPDU_START = BIT(8), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END = BIT(9), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS = BIT(10), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT = BIT(11), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE = BIT(12), +}; + +enum htt_rx_mgmt_pkt_filter_tlv_flags0 { + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ = BIT(0), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ = BIT(1), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ = BIT(2), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP = BIT(3), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP = BIT(4), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP = BIT(5), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ = BIT(6), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ = BIT(7), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ = BIT(8), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP = BIT(9), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP = BIT(10), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP = BIT(11), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ = BIT(12), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ = BIT(13), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ = BIT(14), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP = BIT(15), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP = BIT(16), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP = BIT(17), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV = BIT(18), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV = BIT(19), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV = BIT(20), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7 = BIT(21), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7 = BIT(22), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7 = BIT(23), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON = BIT(24), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON = BIT(25), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON = BIT(26), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM = BIT(27), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM = BIT(28), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM = BIT(29), +}; + +enum htt_rx_mgmt_pkt_filter_tlv_flags1 { + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC = BIT(0), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC = BIT(1), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC = BIT(2), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH = BIT(3), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH = BIT(4), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH = BIT(5), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH = BIT(6), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH = BIT(7), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH = BIT(8), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION = BIT(9), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION = BIT(10), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION = BIT(11), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK = BIT(12), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK = BIT(13), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK = BIT(14), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15 = BIT(15), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15 = BIT(16), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15 = BIT(17), +}; + +enum htt_rx_ctrl_pkt_filter_tlv_flags2 { + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 = BIT(0), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 = BIT(1), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 = BIT(2), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 = BIT(3), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 = BIT(4), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 = BIT(5), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER = BIT(6), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER = BIT(7), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER = BIT(8), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 = BIT(9), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 = BIT(10), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 = BIT(11), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL = BIT(12), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL = BIT(13), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL = BIT(14), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP = BIT(15), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP = BIT(16), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP = BIT(17), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT = BIT(18), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT = BIT(19), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT = BIT(20), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER = BIT(21), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER = BIT(22), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER = BIT(23), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BAR = BIT(24), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BAR = BIT(25), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BAR = BIT(26), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BA = BIT(27), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BA = BIT(28), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BA = BIT(29), +}; + +enum htt_rx_ctrl_pkt_filter_tlv_flags3 { + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL = BIT(0), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL = BIT(1), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL = BIT(2), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_RTS = BIT(3), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_RTS = BIT(4), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_RTS = BIT(5), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CTS = BIT(6), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CTS = BIT(7), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CTS = BIT(8), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_ACK = BIT(9), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_ACK = BIT(10), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_ACK = BIT(11), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND = BIT(12), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND = BIT(13), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND = BIT(14), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK = BIT(15), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK = BIT(16), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK = BIT(17), +}; + +enum htt_rx_data_pkt_filter_tlv_flasg3 { + HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_MCAST = BIT(18), + HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_MCAST = BIT(19), + HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_MCAST = BIT(20), + HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_UCAST = BIT(21), + HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_UCAST = BIT(22), + HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_UCAST = BIT(23), + HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA = BIT(24), + HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA = BIT(25), + HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA = BIT(26), +}; + +#define HTT_RX_FP_MGMT_FILTER_FLAGS0 \ + (HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM) + +#define HTT_RX_MD_MGMT_FILTER_FLAGS0 \ + (HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM) + +#define HTT_RX_MO_MGMT_FILTER_FLAGS0 \ + (HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM) + +#define HTT_RX_FP_MGMT_FILTER_FLAGS1 (HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK) + +#define HTT_RX_MD_MGMT_FILTER_FLAGS1 (HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK) + +#define HTT_RX_MO_MGMT_FILTER_FLAGS1 (HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK) + +#define HTT_RX_FP_CTRL_FILTER_FLASG2 (HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BAR \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BA) + +#define HTT_RX_MD_CTRL_FILTER_FLASG2 (HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BAR \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BA) + +#define HTT_RX_MO_CTRL_FILTER_FLASG2 (HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BAR \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BA) + +#define HTT_RX_FP_CTRL_FILTER_FLASG3 (HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_RTS \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CTS \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_ACK \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK) + +#define HTT_RX_MD_CTRL_FILTER_FLASG3 (HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_RTS \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CTS \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_ACK \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK) + +#define HTT_RX_MO_CTRL_FILTER_FLASG3 (HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_RTS \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CTS \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_ACK \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK) + +#define HTT_RX_FP_DATA_FILTER_FLASG3 (HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_MCAST \ + | HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_UCAST \ + | HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA) + +#define HTT_RX_MD_DATA_FILTER_FLASG3 (HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_MCAST \ + | HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_UCAST \ + | HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA) + +#define HTT_RX_MO_DATA_FILTER_FLASG3 (HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_MCAST \ + | HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_UCAST \ + | HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA) + +#define HTT_RX_MON_FP_MGMT_FILTER_FLAGS0 \ + (HTT_RX_FP_MGMT_FILTER_FLAGS0 | \ + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7) + +#define HTT_RX_MON_MO_MGMT_FILTER_FLAGS0 \ + (HTT_RX_MO_MGMT_FILTER_FLAGS0 | \ + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7) + +#define HTT_RX_MON_FP_MGMT_FILTER_FLAGS1 \ + (HTT_RX_FP_MGMT_FILTER_FLAGS1 | \ + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15) + +#define HTT_RX_MON_MO_MGMT_FILTER_FLAGS1 \ + (HTT_RX_MO_MGMT_FILTER_FLAGS1 | \ + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15) + +#define HTT_RX_MON_FP_CTRL_FILTER_FLASG2 \ + (HTT_RX_FP_CTRL_FILTER_FLASG2 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT) + +#define HTT_RX_MON_MO_CTRL_FILTER_FLASG2 \ + (HTT_RX_MO_CTRL_FILTER_FLASG2 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT) + +#define HTT_RX_MON_FP_CTRL_FILTER_FLASG3 HTT_RX_FP_CTRL_FILTER_FLASG3 + +#define HTT_RX_MON_MO_CTRL_FILTER_FLASG3 HTT_RX_MO_CTRL_FILTER_FLASG3 + +#define HTT_RX_MON_FP_DATA_FILTER_FLASG3 HTT_RX_FP_DATA_FILTER_FLASG3 + +#define HTT_RX_MON_MO_DATA_FILTER_FLASG3 HTT_RX_MO_DATA_FILTER_FLASG3 + +#define HTT_RX_MON_FILTER_TLV_FLAGS \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE) + +#define HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE) + +#define HTT_RX_MON_FILTER_TLV_FLAGS_MON_BUF_RING \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_RX_PACKET | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_MPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_ATTENTION) + +struct htt_rx_ring_selection_cfg_cmd { + uint32_t info0; + uint32_t info1; + uint32_t pkt_type_en_flags0; + uint32_t pkt_type_en_flags1; + uint32_t pkt_type_en_flags2; + uint32_t pkt_type_en_flags3; + uint32_t rx_filter_tlv; +} __packed; + +struct htt_rx_ring_tlv_filter { + uint32_t rx_filter; /* see htt_rx_filter_tlv_flags */ + uint32_t pkt_filter_flags0; /* MGMT */ + uint32_t pkt_filter_flags1; /* MGMT */ + uint32_t pkt_filter_flags2; /* CTRL */ + uint32_t pkt_filter_flags3; /* DATA */ +}; + +#define HTT_RX_FULL_MON_MODE_CFG_CMD_INFO0_MSG_TYPE GENMASK(7, 0) +#define HTT_RX_FULL_MON_MODE_CFG_CMD_INFO0_PDEV_ID GENMASK(15, 8) + +#define HTT_RX_FULL_MON_MODE_CFG_CMD_CFG_ENABLE BIT(0) +#define HTT_RX_FULL_MON_MODE_CFG_CMD_CFG_ZERO_MPDUS_END BIT(1) +#define HTT_RX_FULL_MON_MODE_CFG_CMD_CFG_NON_ZERO_MPDUS_END BIT(2) +#define HTT_RX_FULL_MON_MODE_CFG_CMD_CFG_RELEASE_RING GENMASK(10, 3) + +/* Enumeration for full monitor mode destination ring select + * 0 - REO destination ring select + * 1 - FW destination ring select + * 2 - SW destination ring select + * 3 - Release destination ring select + */ +enum htt_rx_full_mon_release_ring { + HTT_RX_MON_RING_REO, + HTT_RX_MON_RING_FW, + HTT_RX_MON_RING_SW, + HTT_RX_MON_RING_RELEASE, +}; + +struct htt_rx_full_monitor_mode_cfg_cmd { + uint32_t info0; + uint32_t cfg; +} __packed; + +/* HTT message target->host */ + +enum htt_t2h_msg_type { + HTT_T2H_MSG_TYPE_VERSION_CONF, + HTT_T2H_MSG_TYPE_PEER_MAP = 0x3, + HTT_T2H_MSG_TYPE_PEER_UNMAP = 0x4, + HTT_T2H_MSG_TYPE_RX_ADDBA = 0x5, + HTT_T2H_MSG_TYPE_PKTLOG = 0x8, + HTT_T2H_MSG_TYPE_SEC_IND = 0xb, + HTT_T2H_MSG_TYPE_PEER_MAP2 = 0x1e, + HTT_T2H_MSG_TYPE_PEER_UNMAP2 = 0x1f, + HTT_T2H_MSG_TYPE_PPDU_STATS_IND = 0x1d, + HTT_T2H_MSG_TYPE_EXT_STATS_CONF = 0x1c, + HTT_T2H_MSG_TYPE_BKPRESSURE_EVENT_IND = 0x24, +}; + +#define HTT_TARGET_VERSION_MAJOR 3 + +#define HTT_T2H_MSG_TYPE GENMASK(7, 0) +#define HTT_T2H_VERSION_CONF_MINOR GENMASK(15, 8) +#define HTT_T2H_VERSION_CONF_MAJOR GENMASK(23, 16) + +struct htt_t2h_version_conf_msg { + uint32_t version; +} __packed; + +#define HTT_T2H_PEER_MAP_INFO_VDEV_ID GENMASK(15, 8) +#define HTT_T2H_PEER_MAP_INFO_PEER_ID GENMASK(31, 16) +#define HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16 GENMASK(15, 0) +#define HTT_T2H_PEER_MAP_INFO1_HW_PEER_ID GENMASK(31, 16) +#define HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL GENMASK(15, 0) +#define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M BIT(16) +#define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S 16 + +struct htt_t2h_peer_map_event { + uint32_t info; + uint32_t mac_addr_l32; + uint32_t info1; + uint32_t info2; +} __packed; + +#define HTT_T2H_PEER_UNMAP_INFO_VDEV_ID HTT_T2H_PEER_MAP_INFO_VDEV_ID +#define HTT_T2H_PEER_UNMAP_INFO_PEER_ID HTT_T2H_PEER_MAP_INFO_PEER_ID +#define HTT_T2H_PEER_UNMAP_INFO1_MAC_ADDR_H16 \ + HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16 +#define HTT_T2H_PEER_MAP_INFO1_NEXT_HOP_M HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M +#define HTT_T2H_PEER_MAP_INFO1_NEXT_HOP_S HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S + +struct htt_t2h_peer_unmap_event { + uint32_t info; + uint32_t mac_addr_l32; + uint32_t info1; +} __packed; + +struct htt_resp_msg { + union { + struct htt_t2h_version_conf_msg version_msg; + struct htt_t2h_peer_map_event peer_map_ev; + struct htt_t2h_peer_unmap_event peer_unmap_ev; + }; +} __packed; + +#define HTT_BACKPRESSURE_EVENT_PDEV_ID_M GENMASK(15, 8) +#define HTT_BACKPRESSURE_EVENT_RING_TYPE_M GENMASK(23, 16) +#define HTT_BACKPRESSURE_EVENT_RING_ID_M GENMASK(31, 24) + +#define HTT_BACKPRESSURE_EVENT_HP_M GENMASK(15, 0) +#define HTT_BACKPRESSURE_EVENT_TP_M GENMASK(31, 16) + +#define HTT_BACKPRESSURE_UMAC_RING_TYPE 0 +#define HTT_BACKPRESSURE_LMAC_RING_TYPE 1 + +enum htt_backpressure_umac_ringid { + HTT_SW_RING_IDX_REO_REO2SW1_RING, + HTT_SW_RING_IDX_REO_REO2SW2_RING, + HTT_SW_RING_IDX_REO_REO2SW3_RING, + HTT_SW_RING_IDX_REO_REO2SW4_RING, + HTT_SW_RING_IDX_REO_WBM2REO_LINK_RING, + HTT_SW_RING_IDX_REO_REO2TCL_RING, + HTT_SW_RING_IDX_REO_REO2FW_RING, + HTT_SW_RING_IDX_REO_REO_RELEASE_RING, + HTT_SW_RING_IDX_WBM_PPE_RELEASE_RING, + HTT_SW_RING_IDX_TCL_TCL2TQM_RING, + HTT_SW_RING_IDX_WBM_TQM_RELEASE_RING, + HTT_SW_RING_IDX_WBM_REO_RELEASE_RING, + HTT_SW_RING_IDX_WBM_WBM2SW0_RELEASE_RING, + HTT_SW_RING_IDX_WBM_WBM2SW1_RELEASE_RING, + HTT_SW_RING_IDX_WBM_WBM2SW2_RELEASE_RING, + HTT_SW_RING_IDX_WBM_WBM2SW3_RELEASE_RING, + HTT_SW_RING_IDX_REO_REO_CMD_RING, + HTT_SW_RING_IDX_REO_REO_STATUS_RING, + HTT_SW_UMAC_RING_IDX_MAX, +}; + +enum htt_backpressure_lmac_ringid { + HTT_SW_RING_IDX_FW2RXDMA_BUF_RING, + HTT_SW_RING_IDX_FW2RXDMA_STATUS_RING, + HTT_SW_RING_IDX_FW2RXDMA_LINK_RING, + HTT_SW_RING_IDX_SW2RXDMA_BUF_RING, + HTT_SW_RING_IDX_WBM2RXDMA_LINK_RING, + HTT_SW_RING_IDX_RXDMA2FW_RING, + HTT_SW_RING_IDX_RXDMA2SW_RING, + HTT_SW_RING_IDX_RXDMA2RELEASE_RING, + HTT_SW_RING_IDX_RXDMA2REO_RING, + HTT_SW_RING_IDX_MONITOR_STATUS_RING, + HTT_SW_RING_IDX_MONITOR_BUF_RING, + HTT_SW_RING_IDX_MONITOR_DESC_RING, + HTT_SW_RING_IDX_MONITOR_DEST_RING, + HTT_SW_LMAC_RING_IDX_MAX, +}; + +/* ppdu stats + * + * @details + * The following field definitions describe the format of the HTT target + * to host ppdu stats indication message. + * + * + * |31 16|15 12|11 10|9 8|7 0 | + * |----------------------------------------------------------------------| + * | payload_size | rsvd |pdev_id|mac_id | msg type | + * |----------------------------------------------------------------------| + * | ppdu_id | + * |----------------------------------------------------------------------| + * | Timestamp in us | + * |----------------------------------------------------------------------| + * | reserved | + * |----------------------------------------------------------------------| + * | type-specific stats info | + * | (see htt_ppdu_stats.h) | + * |----------------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: Identifies this is a PPDU STATS indication + * message. + * Value: 0x1d + * - mac_id + * Bits 9:8 + * Purpose: mac_id of this ppdu_id + * Value: 0-3 + * - pdev_id + * Bits 11:10 + * Purpose: pdev_id of this ppdu_id + * Value: 0-3 + * 0 (for rings at SOC level), + * 1/2/3 PDEV -> 0/1/2 + * - payload_size + * Bits 31:16 + * Purpose: total tlv size + * Value: payload_size in bytes + */ + +#define HTT_T2H_PPDU_STATS_INFO_PDEV_ID GENMASK(11, 10) +#define HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE GENMASK(31, 16) + +struct ath12k_htt_ppdu_stats_msg { + uint32_t info; + uint32_t ppdu_id; + uint32_t timestamp; + uint32_t rsvd; + uint8_t data[]; +} __packed; + +struct htt_tlv { + uint32_t header; + uint8_t *value; +} __packed; + +#define HTT_TLV_TAG GENMASK(11, 0) +#define HTT_TLV_LEN GENMASK(23, 12) + +enum HTT_PPDU_STATS_BW { + HTT_PPDU_STATS_BANDWIDTH_5MHZ = 0, + HTT_PPDU_STATS_BANDWIDTH_10MHZ = 1, + HTT_PPDU_STATS_BANDWIDTH_20MHZ = 2, + HTT_PPDU_STATS_BANDWIDTH_40MHZ = 3, + HTT_PPDU_STATS_BANDWIDTH_80MHZ = 4, + HTT_PPDU_STATS_BANDWIDTH_160MHZ = 5, /* includes 80+80 */ + HTT_PPDU_STATS_BANDWIDTH_DYN = 6, +}; + +#define HTT_PPDU_STATS_CMN_FLAGS_FRAME_TYPE_M GENMASK(7, 0) +#define HTT_PPDU_STATS_CMN_FLAGS_QUEUE_TYPE_M GENMASK(15, 8) +/* bw - HTT_PPDU_STATS_BW */ +#define HTT_PPDU_STATS_CMN_FLAGS_BW_M GENMASK(19, 16) + +struct htt_ppdu_stats_common { + uint32_t ppdu_id; + uint16_t sched_cmdid; + uint8_t ring_id; + uint8_t num_users; + uint32_t flags; /* %HTT_PPDU_STATS_COMMON_FLAGS_*/ + uint32_t chain_mask; + uint32_t fes_duration_us; /* frame exchange sequence */ + uint32_t ppdu_sch_eval_start_tstmp_us; + uint32_t ppdu_sch_end_tstmp_us; + uint32_t ppdu_start_tstmp_us; + /* BIT [15 : 0] - phy mode (WLAN_PHY_MODE) with which ppdu was transmitted + * BIT [31 : 16] - bandwidth (in MHz) with which ppdu was transmitted + */ + uint16_t phy_mode; + uint16_t bw_mhz; +} __packed; + +enum htt_ppdu_stats_gi { + HTT_PPDU_STATS_SGI_0_8_US, + HTT_PPDU_STATS_SGI_0_4_US, + HTT_PPDU_STATS_SGI_1_6_US, + HTT_PPDU_STATS_SGI_3_2_US, +}; + +#define HTT_PPDU_STATS_USER_RATE_INFO0_USER_POS_M GENMASK(3, 0) +#define HTT_PPDU_STATS_USER_RATE_INFO0_MU_GROUP_ID_M GENMASK(11, 4) + +#define HTT_PPDU_STATS_USER_RATE_INFO1_RESP_TYPE_VALD_M BIT(0) +#define HTT_PPDU_STATS_USER_RATE_INFO1_PPDU_TYPE_M GENMASK(5, 1) + +#define HTT_PPDU_STATS_USER_RATE_FLAGS_LTF_SIZE_M GENMASK(1, 0) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_STBC_M BIT(2) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_HE_RE_M BIT(3) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_TXBF_M GENMASK(7, 4) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_BW_M GENMASK(11, 8) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_NSS_M GENMASK(15, 12) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_MCS_M GENMASK(19, 16) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_PREAMBLE_M GENMASK(23, 20) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_GI_M GENMASK(27, 24) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_DCM_M BIT(28) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_LDPC_M BIT(29) + +#define HTT_USR_RATE_PREAMBLE(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_PREAMBLE_M, _val) +#define HTT_USR_RATE_BW(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_BW_M, _val) +#define HTT_USR_RATE_NSS(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_NSS_M, _val) +#define HTT_USR_RATE_MCS(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_MCS_M, _val) +#define HTT_USR_RATE_GI(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_GI_M, _val) +#define HTT_USR_RATE_DCM(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_DCM_M, _val) + +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_LTF_SIZE_M GENMASK(1, 0) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_STBC_M BIT(2) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_HE_RE_M BIT(3) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_TXBF_M GENMASK(7, 4) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_BW_M GENMASK(11, 8) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_NSS_M GENMASK(15, 12) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_MCS_M GENMASK(19, 16) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_PREAMBLE_M GENMASK(23, 20) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_GI_M GENMASK(27, 24) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_DCM_M BIT(28) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_LDPC_M BIT(29) + +struct htt_ppdu_stats_user_rate { + uint8_t tid_num; + uint8_t reserved0; + uint16_t sw_peer_id; + uint32_t info0; /* %HTT_PPDU_STATS_USER_RATE_INFO0_*/ + uint16_t ru_end; + uint16_t ru_start; + uint16_t resp_ru_end; + uint16_t resp_ru_start; + uint32_t info1; /* %HTT_PPDU_STATS_USER_RATE_INFO1_ */ + uint32_t rate_flags; /* %HTT_PPDU_STATS_USER_RATE_FLAGS_ */ + /* Note: resp_rate_info is only valid for if resp_type is UL */ + uint32_t resp_rate_flags; /* %HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_ */ +} __packed; + +#define HTT_PPDU_STATS_TX_INFO_FLAGS_RATECODE_M GENMASK(7, 0) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_IS_AMPDU_M BIT(8) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_BA_ACK_FAILED_M GENMASK(10, 9) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_BW_M GENMASK(13, 11) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_SGI_M BIT(14) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_PEERID_M GENMASK(31, 16) + +#define HTT_TX_INFO_IS_AMSDU(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_IS_AMPDU_M, _flags) +#define HTT_TX_INFO_BA_ACK_FAILED(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_BA_ACK_FAILED_M, _flags) +#define HTT_TX_INFO_RATECODE(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_RATECODE_M, _flags) +#define HTT_TX_INFO_PEERID(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_PEERID_M, _flags) + +struct htt_tx_ppdu_stats_info { + struct htt_tlv tlv_hdr; + uint32_t tx_success_bytes; + uint32_t tx_retry_bytes; + uint32_t tx_failed_bytes; + uint32_t flags; /* %HTT_PPDU_STATS_TX_INFO_FLAGS_ */ + uint16_t tx_success_msdus; + uint16_t tx_retry_msdus; + uint16_t tx_failed_msdus; + uint16_t tx_duration; /* united in us */ +} __packed; + +enum htt_ppdu_stats_usr_compln_status { + HTT_PPDU_STATS_USER_STATUS_OK, + HTT_PPDU_STATS_USER_STATUS_FILTERED, + HTT_PPDU_STATS_USER_STATUS_RESP_TIMEOUT, + HTT_PPDU_STATS_USER_STATUS_RESP_MISMATCH, + HTT_PPDU_STATS_USER_STATUS_ABORT, +}; + +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_LONG_RETRY_M GENMASK(3, 0) +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_SHORT_RETRY_M GENMASK(7, 4) +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_IS_AMPDU_M BIT(8) +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_RESP_TYPE_M GENMASK(12, 9) + +#define HTT_USR_CMPLTN_IS_AMPDU(_val) \ + FIELD_GET(HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_IS_AMPDU_M, _val) +#define HTT_USR_CMPLTN_LONG_RETRY(_val) \ + FIELD_GET(HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_LONG_RETRY_M, _val) +#define HTT_USR_CMPLTN_SHORT_RETRY(_val) \ + FIELD_GET(HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_SHORT_RETRY_M, _val) + +struct htt_ppdu_stats_usr_cmpltn_cmn { + uint8_t status; + uint8_t tid_num; + uint16_t sw_peer_id; + /* RSSI value of last ack packet (units = dB above noise floor) */ + uint32_t ack_rssi; + uint16_t mpdu_tried; + uint16_t mpdu_success; + uint32_t flags; /* %HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_LONG_RETRIES*/ +} __packed; + +#define HTT_PPDU_STATS_ACK_BA_INFO_NUM_MPDU_M GENMASK(8, 0) +#define HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M GENMASK(24, 9) +#define HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM GENMASK(31, 25) + +#define HTT_PPDU_STATS_NON_QOS_TID 16 + +struct htt_ppdu_stats_usr_cmpltn_ack_ba_status { + uint32_t ppdu_id; + uint16_t sw_peer_id; + uint16_t reserved0; + uint32_t info; /* %HTT_PPDU_STATS_USR_CMPLTN_CMN_INFO_ */ + uint16_t current_seq; + uint16_t start_seq; + uint32_t success_bytes; +} __packed; + +struct htt_ppdu_stats_usr_cmn_array { + struct htt_tlv tlv_hdr; + uint32_t num_ppdu_stats; + /* tx_ppdu_stats_info is filled by multiple struct htt_tx_ppdu_stats_info + * elements. + * tx_ppdu_stats_info is variable length, with length = + * number_of_ppdu_stats * sizeof (struct htt_tx_ppdu_stats_info) + */ + struct htt_tx_ppdu_stats_info tx_ppdu_info[]; +} __packed; + +struct htt_ppdu_user_stats { + uint16_t peer_id; + uint32_t tlv_flags; + bool is_valid_peer_id; + struct htt_ppdu_stats_user_rate rate; + struct htt_ppdu_stats_usr_cmpltn_cmn cmpltn_cmn; + struct htt_ppdu_stats_usr_cmpltn_ack_ba_status ack_ba; +}; + +#define HTT_PPDU_STATS_MAX_USERS 8 +#define HTT_PPDU_DESC_MAX_DEPTH 16 + +struct htt_ppdu_stats { + struct htt_ppdu_stats_common common; + struct htt_ppdu_user_stats user_stats[HTT_PPDU_STATS_MAX_USERS]; +}; + +struct htt_ppdu_stats_info { + uint32_t ppdu_id; + struct htt_ppdu_stats ppdu_stats; +#if 0 + struct list_head list; +#endif +}; + +/* @brief target -> host packet log message + * + * @details + * The following field definitions describe the format of the packet log + * message sent from the target to the host. + * The message consists of a 4-octet header,followed by a variable number + * of 32-bit character values. + * + * |31 16|15 12|11 10|9 8|7 0| + * |------------------------------------------------------------------| + * | payload_size | rsvd |pdev_id|mac_id| msg type | + * |------------------------------------------------------------------| + * | payload | + * |------------------------------------------------------------------| + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this as a pktlog message + * Value: HTT_T2H_MSG_TYPE_PKTLOG + * - mac_id + * Bits 9:8 + * Purpose: identifies which MAC/PHY instance generated this pktlog info + * Value: 0-3 + * - pdev_id + * Bits 11:10 + * Purpose: pdev_id + * Value: 0-3 + * 0 (for rings at SOC level), + * 1/2/3 PDEV -> 0/1/2 + * - payload_size + * Bits 31:16 + * Purpose: explicitly specify the payload size + * Value: payload size in bytes (payload size is a multiple of 4 bytes) + */ +struct htt_pktlog_msg { + uint32_t hdr; + uint8_t payload[]; +}; + +/* @brief host -> target FW extended statistics retrieve + * + * @details + * The following field definitions describe the format of the HTT host + * to target FW extended stats retrieve message. + * The message specifies the type of stats the host wants to retrieve. + * + * |31 24|23 16|15 8|7 0| + * |-----------------------------------------------------------| + * | reserved | stats type | pdev_mask | msg type | + * |-----------------------------------------------------------| + * | config param [0] | + * |-----------------------------------------------------------| + * | config param [1] | + * |-----------------------------------------------------------| + * | config param [2] | + * |-----------------------------------------------------------| + * | config param [3] | + * |-----------------------------------------------------------| + * | reserved | + * |-----------------------------------------------------------| + * | cookie LSBs | + * |-----------------------------------------------------------| + * | cookie MSBs | + * |-----------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this is a extended stats upload request message + * Value: 0x10 + * - PDEV_MASK + * Bits 8:15 + * Purpose: identifies the mask of PDEVs to retrieve stats from + * Value: This is a overloaded field, refer to usage and interpretation of + * PDEV in interface document. + * Bit 8 : Reserved for SOC stats + * Bit 9 - 15 : Indicates PDEV_MASK in DBDC + * Indicates MACID_MASK in DBS + * - STATS_TYPE + * Bits 23:16 + * Purpose: identifies which FW statistics to upload + * Value: Defined by htt_dbg_ext_stats_type (see htt_stats.h) + * - Reserved + * Bits 31:24 + * - CONFIG_PARAM [0] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - CONFIG_PARAM [1] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - CONFIG_PARAM [2] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - CONFIG_PARAM [3] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - Reserved [31:0] for future use. + * - COOKIE_LSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: LSBs of the opaque cookie specified by the host-side requestor + * - COOKIE_MSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + */ + +struct htt_ext_stats_cfg_hdr { + uint8_t msg_type; + uint8_t pdev_mask; + uint8_t stats_type; + uint8_t reserved; +} __packed; + +struct htt_ext_stats_cfg_cmd { + struct htt_ext_stats_cfg_hdr hdr; + uint32_t cfg_param0; + uint32_t cfg_param1; + uint32_t cfg_param2; + uint32_t cfg_param3; + uint32_t reserved; + uint32_t cookie_lsb; + uint32_t cookie_msb; +} __packed; + +/* htt stats config default params */ +#define HTT_STAT_DEFAULT_RESET_START_OFFSET 0 +#define HTT_STAT_DEFAULT_CFG0_ALL_HWQS 0xffffffff +#define HTT_STAT_DEFAULT_CFG0_ALL_TXQS 0xffffffff +#define HTT_STAT_DEFAULT_CFG0_ALL_CMDQS 0xffff +#define HTT_STAT_DEFAULT_CFG0_ALL_RINGS 0xffff +#define HTT_STAT_DEFAULT_CFG0_ACTIVE_PEERS 0xff +#define HTT_STAT_DEFAULT_CFG0_CCA_CUMULATIVE 0x00 +#define HTT_STAT_DEFAULT_CFG0_ACTIVE_VDEVS 0x00 + +/* HTT_DBG_EXT_STATS_PEER_INFO + * PARAMS: + * @config_param0: + * [Bit0] - [0] for sw_peer_id, [1] for mac_addr based request + * [Bit15 : Bit 1] htt_peer_stats_req_mode_t + * [Bit31 : Bit16] sw_peer_id + * @config_param1: + * peer_stats_req_type_mask:32 (enum htt_peer_stats_tlv_enum) + * 0 bit htt_peer_stats_cmn_tlv + * 1 bit htt_peer_details_tlv + * 2 bit htt_tx_peer_rate_stats_tlv + * 3 bit htt_rx_peer_rate_stats_tlv + * 4 bit htt_tx_tid_stats_tlv/htt_tx_tid_stats_v1_tlv + * 5 bit htt_rx_tid_stats_tlv + * 6 bit htt_msdu_flow_stats_tlv + * @config_param2: [Bit31 : Bit0] mac_addr31to0 + * @config_param3: [Bit15 : Bit0] mac_addr47to32 + * [Bit31 : Bit16] reserved + */ +#define HTT_STAT_PEER_INFO_MAC_ADDR BIT(0) +#define HTT_STAT_DEFAULT_PEER_REQ_TYPE 0x7f + +/* Used to set different configs to the specified stats type.*/ +struct htt_ext_stats_cfg_params { + uint32_t cfg0; + uint32_t cfg1; + uint32_t cfg2; + uint32_t cfg3; +}; + +/* @brief target -> host extended statistics upload + * + * @details + * The following field definitions describe the format of the HTT target + * to host stats upload confirmation message. + * The message contains a cookie echoed from the HTT host->target stats + * upload request, which identifies which request the confirmation is + * for, and a single stats can span over multiple HTT stats indication + * due to the HTT message size limitation so every HTT ext stats indication + * will have tag-length-value stats information elements. + * The tag-length header for each HTT stats IND message also includes a + * status field, to indicate whether the request for the stat type in + * question was fully met, partially met, unable to be met, or invalid + * (if the stat type in question is disabled in the target). + * A Done bit 1's indicate the end of the of stats info elements. + * + * + * |31 16|15 12|11|10 8|7 5|4 0| + * |--------------------------------------------------------------| + * | reserved | msg type | + * |--------------------------------------------------------------| + * | cookie LSBs | + * |--------------------------------------------------------------| + * | cookie MSBs | + * |--------------------------------------------------------------| + * | stats entry length | rsvd | D| S | stat type | + * |--------------------------------------------------------------| + * | type-specific stats info | + * | (see htt_stats.h) | + * |--------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: Identifies this is a extended statistics upload confirmation + * message. + * Value: 0x1c + * - COOKIE_LSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: LSBs of the opaque cookie specified by the host-side requestor + * - COOKIE_MSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + * + * Stats Information Element tag-length header fields: + * - STAT_TYPE + * Bits 7:0 + * Purpose: identifies the type of statistics info held in the + * following information element + * Value: htt_dbg_ext_stats_type + * - STATUS + * Bits 10:8 + * Purpose: indicate whether the requested stats are present + * Value: htt_dbg_ext_stats_status + * - DONE + * Bits 11 + * Purpose: + * Indicates the completion of the stats entry, this will be the last + * stats conf HTT segment for the requested stats type. + * Value: + * 0 -> the stats retrieval is ongoing + * 1 -> the stats retrieval is complete + * - LENGTH + * Bits 31:16 + * Purpose: indicate the stats information size + * Value: This field specifies the number of bytes of stats information + * that follows the element tag-length header. + * It is expected but not required that this length is a multiple of + * 4 bytes. + */ + +#define HTT_T2H_EXT_STATS_INFO1_DONE BIT(11) +#define HTT_T2H_EXT_STATS_INFO1_LENGTH GENMASK(31, 16) + +struct ath12k_htt_extd_stats_msg { + uint32_t info0; + uint64_t cookie; + uint32_t info1; + uint8_t data[]; +} __packed; + +#define HTT_MAC_ADDR_L32_0 GENMASK(7, 0) +#define HTT_MAC_ADDR_L32_1 GENMASK(15, 8) +#define HTT_MAC_ADDR_L32_2 GENMASK(23, 16) +#define HTT_MAC_ADDR_L32_3 GENMASK(31, 24) +#define HTT_MAC_ADDR_H16_0 GENMASK(7, 0) +#define HTT_MAC_ADDR_H16_1 GENMASK(15, 8) diff --git a/sys/dev/ic/qwzvar.h b/sys/dev/ic/qwzvar.h new file mode 100644 index 000000000..ee27c3f30 --- /dev/null +++ b/sys/dev/ic/qwzvar.h @@ -0,0 +1,2031 @@ +/* $OpenBSD: qwzvar.h,v 1.1 2024/08/14 14:40:46 patrick Exp $ */ + +/* + * Copyright (c) 2018-2019 The Linux Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of [Owner Organization] nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef QWZ_DEBUG +#define DPRINTF(x...) do { if (qwz_debug) printf(x); } while(0) +#define DNPRINTF(n,x...) do { if (qwz_debug & n) printf(x); } while(0) +#define QWZ_D_MISC 0x00000001 +#define QWZ_D_MHI 0x00000002 +#define QWZ_D_QMI 0x00000004 +#define QWZ_D_WMI 0x00000008 +#define QWZ_D_HTC 0x00000010 +#define QWZ_D_HTT 0x00000020 +#define QWZ_D_MAC 0x00000040 +#define QWZ_D_MGMT 0x00000080 +#define QWZ_D_CE 0x00000100 +extern uint32_t qwz_debug; +#else +#define DPRINTF(x...) +#define DNPRINTF(n,x...) +#endif + +struct qwz_softc; + +#define ATH12K_EXT_IRQ_GRP_NUM_MAX 11 + +struct ath12k_hw_ring_mask { + uint8_t tx[ATH12K_EXT_IRQ_GRP_NUM_MAX]; + uint8_t rx_mon_status[ATH12K_EXT_IRQ_GRP_NUM_MAX]; + uint8_t rx[ATH12K_EXT_IRQ_GRP_NUM_MAX]; + uint8_t rx_err[ATH12K_EXT_IRQ_GRP_NUM_MAX]; + uint8_t rx_wbm_rel[ATH12K_EXT_IRQ_GRP_NUM_MAX]; + uint8_t reo_status[ATH12K_EXT_IRQ_GRP_NUM_MAX]; + uint8_t rxdma2host[ATH12K_EXT_IRQ_GRP_NUM_MAX]; + uint8_t host2rxdma[ATH12K_EXT_IRQ_GRP_NUM_MAX]; +}; + +#define ATH12K_FW_DIR "qwz" + +#define ATH12K_BOARD_MAGIC "QCA-ATH12K-BOARD" +#define ATH12K_BOARD_API2_FILE "board-2" +#define ATH12K_DEFAULT_BOARD_FILE "board" +#define ATH12K_DEFAULT_CAL_FILE "caldata" +#define ATH12K_AMSS_FILE "amss" +#define ATH12K_M3_FILE "m3" +#define ATH12K_REGDB_FILE "regdb" + +#define QWZ_FW_BUILD_ID_MASK "QC_IMAGE_VERSION_STRING=" + +struct ath12k_hw_tcl2wbm_rbm_map { + uint8_t tcl_ring_num; + uint8_t wbm_ring_num; + uint8_t rbm_id; +}; + +/** + * enum hal_rx_buf_return_buf_manager + * + * @HAL_RX_BUF_RBM_WBM_IDLE_BUF_LIST: Buffer returned to WBM idle buffer list + * @HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST: Descriptor returned to WBM idle + * descriptor list. + * @HAL_RX_BUF_RBM_FW_BM: Buffer returned to FW + * @HAL_RX_BUF_RBM_SW0_BM: For Tx completion -- returned to host + * @HAL_RX_BUF_RBM_SW1_BM: For Tx completion -- returned to host + * @HAL_RX_BUF_RBM_SW2_BM: For Tx completion -- returned to host + * @HAL_RX_BUF_RBM_SW3_BM: For Rx release -- returned to host + */ + +enum hal_rx_buf_return_buf_manager { + HAL_RX_BUF_RBM_WBM_IDLE_BUF_LIST, + HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST, + HAL_RX_BUF_RBM_FW_BM, + HAL_RX_BUF_RBM_SW0_BM, + HAL_RX_BUF_RBM_SW1_BM, + HAL_RX_BUF_RBM_SW2_BM, + HAL_RX_BUF_RBM_SW3_BM, + HAL_RX_BUF_RBM_SW4_BM, +}; + +struct ath12k_hw_hal_params { + enum hal_rx_buf_return_buf_manager rx_buf_rbm; + const struct ath12k_hw_tcl2wbm_rbm_map *tcl2wbm_rbm_map; +}; + +struct hal_tx_info { + uint16_t meta_data_flags; /* %HAL_TCL_DATA_CMD_INFO0_META_ */ + uint8_t ring_id; + uint32_t desc_id; + enum hal_tcl_desc_type type; + enum hal_tcl_encap_type encap_type; + uint64_t paddr; + uint32_t data_len; + uint32_t pkt_offset; + enum hal_encrypt_type encrypt_type; + uint32_t flags0; /* %HAL_TCL_DATA_CMD_INFO1_ */ + uint32_t flags1; /* %HAL_TCL_DATA_CMD_INFO2_ */ + uint16_t addr_search_flags; /* %HAL_TCL_DATA_CMD_INFO0_ADDR(X/Y)_ */ + uint16_t bss_ast_hash; + uint16_t bss_ast_idx; + uint8_t tid; + uint8_t search_type; /* %HAL_TX_ADDR_SEARCH_ */ + uint8_t lmac_id; + uint8_t dscp_tid_tbl_idx; + bool enable_mesh; + uint8_t rbm_id; +}; + +/* TODO: Check if the actual desc macros can be used instead */ +#define HAL_TX_STATUS_FLAGS_FIRST_MSDU BIT(0) +#define HAL_TX_STATUS_FLAGS_LAST_MSDU BIT(1) +#define HAL_TX_STATUS_FLAGS_MSDU_IN_AMSDU BIT(2) +#define HAL_TX_STATUS_FLAGS_RATE_STATS_VALID BIT(3) +#define HAL_TX_STATUS_FLAGS_RATE_LDPC BIT(4) +#define HAL_TX_STATUS_FLAGS_RATE_STBC BIT(5) +#define HAL_TX_STATUS_FLAGS_OFDMA BIT(6) + +#define HAL_TX_STATUS_DESC_LEN sizeof(struct hal_wbm_release_ring) + +/* Tx status parsed from srng desc */ +struct hal_tx_status { + enum hal_wbm_rel_src_module buf_rel_source; + enum hal_wbm_tqm_rel_reason status; + uint8_t ack_rssi; + uint32_t flags; /* %HAL_TX_STATUS_FLAGS_ */ + uint32_t ppdu_id; + uint8_t try_cnt; + uint8_t tid; + uint16_t peer_id; + uint32_t rate_stats; +}; + +struct ath12k_hw_params { + const char *name; + uint16_t hw_rev; + uint8_t max_radios; + uint32_t bdf_addr; + + struct { + const char *dir; + size_t board_size; + size_t cal_offset; + } fw; + + const struct ath12k_hw_ops *hw_ops; + const struct ath12k_hw_ring_mask *ring_mask; + + bool internal_sleep_clock; + + const struct ath12k_hw_regs *regs; + uint32_t qmi_service_ins_id; + const struct ce_attr *host_ce_config; + uint32_t ce_count; + const struct ce_pipe_config *target_ce_config; + uint32_t target_ce_count; + const struct service_to_pipe *svc_to_ce_map; + uint32_t svc_to_ce_map_len; + + bool single_pdev_only; + + bool rxdma1_enable; + int num_rxmda_per_pdev; + bool rx_mac_buf_ring; + bool vdev_start_delay; + bool htt_peer_map_v2; +#if notyet + struct { + uint8_t fft_sz; + uint8_t fft_pad_sz; + uint8_t summary_pad_sz; + uint8_t fft_hdr_len; + uint16_t max_fft_bins; + bool fragment_160mhz; + } spectral; + + uint16_t interface_modes; + bool supports_monitor; + bool full_monitor_mode; +#endif + bool supports_shadow_regs; + bool idle_ps; + bool supports_sta_ps; + bool cold_boot_calib; + bool cbcal_restart_fw; + int fw_mem_mode; + uint32_t num_vdevs; + uint32_t num_peers; + bool supports_suspend; + uint32_t hal_desc_sz; + bool supports_regdb; + bool fix_l1ss; + bool credit_flow; + uint8_t max_tx_ring; + const struct ath12k_hw_hal_params *hal_params; +#if notyet + bool supports_dynamic_smps_6ghz; + bool alloc_cacheable_memory; + bool supports_rssi_stats; +#endif + bool fw_wmi_diag_event; + bool current_cc_support; + bool dbr_debug_support; + bool global_reset; +#ifdef notyet + const struct cfg80211_sar_capa *bios_sar_capa; +#endif + bool m3_fw_support; + bool fixed_bdf_addr; + bool fixed_mem_region; + bool static_window_map; + bool hybrid_bus_type; + bool fixed_fw_mem; +#if notyet + bool support_off_channel_tx; + bool supports_multi_bssid; + + struct { + uint32_t start; + uint32_t end; + } sram_dump; + + bool tcl_ring_retry; +#endif + uint32_t tx_ring_size; + bool smp2p_wow_exit; +}; + +struct ath12k_hw_ops { + uint8_t (*get_hw_mac_from_pdev_id)(int pdev_id); + void (*wmi_init_config)(struct qwz_softc *sc, + struct target_resource_config *config); + int (*mac_id_to_pdev_id)(struct ath12k_hw_params *hw, int mac_id); + int (*mac_id_to_srng_id)(struct ath12k_hw_params *hw, int mac_id); +#if notyet + void (*tx_mesh_enable)(struct ath12k_base *ab, + struct hal_tcl_data_cmd *tcl_cmd); +#endif + int (*rx_desc_get_first_msdu)(struct hal_rx_desc *desc); +#if notyet + bool (*rx_desc_get_last_msdu)(struct hal_rx_desc *desc); +#endif + uint8_t (*rx_desc_get_l3_pad_bytes)(struct hal_rx_desc *desc); + uint8_t *(*rx_desc_get_hdr_status)(struct hal_rx_desc *desc); + int (*rx_desc_encrypt_valid)(struct hal_rx_desc *desc); + uint32_t (*rx_desc_get_encrypt_type)(struct hal_rx_desc *desc); + uint8_t (*rx_desc_get_decap_type)(struct hal_rx_desc *desc); +#ifdef notyet + uint8_t (*rx_desc_get_mesh_ctl)(struct hal_rx_desc *desc); + bool (*rx_desc_get_ldpc_support)(struct hal_rx_desc *desc); + bool (*rx_desc_get_mpdu_seq_ctl_vld)(struct hal_rx_desc *desc); + bool (*rx_desc_get_mpdu_fc_valid)(struct hal_rx_desc *desc); + uint16_t (*rx_desc_get_mpdu_start_seq_no)(struct hal_rx_desc *desc); +#endif + uint16_t (*rx_desc_get_msdu_len)(struct hal_rx_desc *desc); +#ifdef notyet + uint8_t (*rx_desc_get_msdu_sgi)(struct hal_rx_desc *desc); + uint8_t (*rx_desc_get_msdu_rate_mcs)(struct hal_rx_desc *desc); + uint8_t (*rx_desc_get_msdu_rx_bw)(struct hal_rx_desc *desc); +#endif + uint32_t (*rx_desc_get_msdu_freq)(struct hal_rx_desc *desc); +#ifdef notyet + uint8_t (*rx_desc_get_msdu_pkt_type)(struct hal_rx_desc *desc); + uint8_t (*rx_desc_get_msdu_nss)(struct hal_rx_desc *desc); + uint8_t (*rx_desc_get_mpdu_tid)(struct hal_rx_desc *desc); + uint16_t (*rx_desc_get_mpdu_peer_id)(struct hal_rx_desc *desc); + void (*rx_desc_copy_attn_end_tlv)(struct hal_rx_desc *fdesc, + struct hal_rx_desc *ldesc); + uint32_t (*rx_desc_get_mpdu_start_tag)(struct hal_rx_desc *desc); + uint32_t (*rx_desc_get_mpdu_ppdu_id)(struct hal_rx_desc *desc); + void (*rx_desc_set_msdu_len)(struct hal_rx_desc *desc, uint16_t len); +#endif + struct rx_attention *(*rx_desc_get_attention)(struct hal_rx_desc *desc); +#ifdef notyet + uint8_t *(*rx_desc_get_msdu_payload)(struct hal_rx_desc *desc); +#endif + void (*reo_setup)(struct qwz_softc *); +#ifdef notyet + uint16_t (*mpdu_info_get_peerid)(uint8_t *tlv_data); + bool (*rx_desc_mac_addr2_valid)(struct hal_rx_desc *desc); + uint8_t* (*rx_desc_mpdu_start_addr2)(struct hal_rx_desc *desc); + uint32_t (*get_ring_selector)(struct sk_buff *skb); +#endif +}; + +extern const struct ath12k_hw_ops ipq8074_ops; +extern const struct ath12k_hw_ops ipq6018_ops; +extern const struct ath12k_hw_ops qca6390_ops; +extern const struct ath12k_hw_ops qcn9074_ops; +extern const struct ath12k_hw_ops wcn6855_ops; +extern const struct ath12k_hw_ops wcn6750_ops; + +extern const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_ipq8074; +extern const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qca6390; +extern const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9074; +extern const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_wcn6750; + +struct ath12k_hw_regs { + uint32_t hal_tcl1_ring_base_lsb; + uint32_t hal_tcl1_ring_base_msb; + uint32_t hal_tcl1_ring_id; + uint32_t hal_tcl1_ring_misc; + uint32_t hal_tcl1_ring_tp_addr_lsb; + uint32_t hal_tcl1_ring_tp_addr_msb; + uint32_t hal_tcl1_ring_consumer_int_setup_ix0; + uint32_t hal_tcl1_ring_consumer_int_setup_ix1; + uint32_t hal_tcl1_ring_msi1_base_lsb; + uint32_t hal_tcl1_ring_msi1_base_msb; + uint32_t hal_tcl1_ring_msi1_data; + uint32_t hal_tcl2_ring_base_lsb; + uint32_t hal_tcl_ring_base_lsb; + + uint32_t hal_tcl_status_ring_base_lsb; + + uint32_t hal_reo1_ring_base_lsb; + uint32_t hal_reo1_ring_base_msb; + uint32_t hal_reo1_ring_id; + uint32_t hal_reo1_ring_misc; + uint32_t hal_reo1_ring_hp_addr_lsb; + uint32_t hal_reo1_ring_hp_addr_msb; + uint32_t hal_reo1_ring_producer_int_setup; + uint32_t hal_reo1_ring_msi1_base_lsb; + uint32_t hal_reo1_ring_msi1_base_msb; + uint32_t hal_reo1_ring_msi1_data; + uint32_t hal_reo2_ring_base_lsb; + uint32_t hal_reo1_aging_thresh_ix_0; + uint32_t hal_reo1_aging_thresh_ix_1; + uint32_t hal_reo1_aging_thresh_ix_2; + uint32_t hal_reo1_aging_thresh_ix_3; + + uint32_t hal_reo1_ring_hp; + uint32_t hal_reo1_ring_tp; + uint32_t hal_reo2_ring_hp; + + uint32_t hal_reo_tcl_ring_base_lsb; + uint32_t hal_reo_tcl_ring_hp; + + uint32_t hal_reo_status_ring_base_lsb; + uint32_t hal_reo_status_hp; + + uint32_t hal_reo_cmd_ring_base_lsb; + uint32_t hal_reo_cmd_ring_hp; + + uint32_t hal_sw2reo_ring_base_lsb; + uint32_t hal_sw2reo_ring_hp; + + uint32_t hal_seq_wcss_umac_ce0_src_reg; + uint32_t hal_seq_wcss_umac_ce0_dst_reg; + uint32_t hal_seq_wcss_umac_ce1_src_reg; + uint32_t hal_seq_wcss_umac_ce1_dst_reg; + + uint32_t hal_wbm_idle_link_ring_base_lsb; + uint32_t hal_wbm_idle_link_ring_misc; + + uint32_t hal_wbm_release_ring_base_lsb; + + uint32_t hal_wbm0_release_ring_base_lsb; + uint32_t hal_wbm1_release_ring_base_lsb; + + uint32_t pcie_qserdes_sysclk_en_sel; + uint32_t pcie_pcs_osc_dtct_config_base; + + uint32_t hal_shadow_base_addr; + uint32_t hal_reo1_misc_ctl; +}; + +extern const struct ath12k_hw_regs ipq8074_regs; +extern const struct ath12k_hw_regs qca6390_regs; +extern const struct ath12k_hw_regs qcn9074_regs; +extern const struct ath12k_hw_regs wcn6855_regs; +extern const struct ath12k_hw_regs wcn6750_regs; + +enum ath12k_dev_flags { + ATH12K_CAC_RUNNING, + ATH12K_FLAG_CORE_REGISTERED, + ATH12K_FLAG_CRASH_FLUSH, + ATH12K_FLAG_RAW_MODE, + ATH12K_FLAG_HW_CRYPTO_DISABLED, + ATH12K_FLAG_BTCOEX, + ATH12K_FLAG_RECOVERY, + ATH12K_FLAG_UNREGISTERING, + ATH12K_FLAG_REGISTERED, + ATH12K_FLAG_QMI_FAIL, + ATH12K_FLAG_HTC_SUSPEND_COMPLETE, + ATH12K_FLAG_CE_IRQ_ENABLED, + ATH12K_FLAG_EXT_IRQ_ENABLED, + ATH12K_FLAG_FIXED_MEM_RGN, + ATH12K_FLAG_DEVICE_INIT_DONE, + ATH12K_FLAG_MULTI_MSI_VECTORS, +}; + +enum ath12k_scan_state { + ATH12K_SCAN_IDLE, + ATH12K_SCAN_STARTING, + ATH12K_SCAN_RUNNING, + ATH12K_SCAN_ABORTING, +}; + +enum ath12k_11d_state { + ATH12K_11D_IDLE, + ATH12K_11D_PREPARING, + ATH12K_11D_RUNNING, +}; + +/* enum ath12k_spectral_mode: + * + * @SPECTRAL_DISABLED: spectral mode is disabled + * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with + * something else. + * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples + * is performed manually. + */ +enum ath12k_spectral_mode { + ATH12K_SPECTRAL_DISABLED = 0, + ATH12K_SPECTRAL_BACKGROUND, + ATH12K_SPECTRAL_MANUAL, +}; + +#define QWZ_SCAN_11D_INTERVAL 600000 +#define QWZ_11D_INVALID_VDEV_ID 0xFFFF + +struct qwz_ops { + uint32_t (*read32)(struct qwz_softc *, uint32_t); + void (*write32)(struct qwz_softc *, uint32_t, uint32_t); + int (*start)(struct qwz_softc *); + void (*stop)(struct qwz_softc *); + int (*power_up)(struct qwz_softc *); + void (*power_down)(struct qwz_softc *); + int (*submit_xfer)(struct qwz_softc *, struct mbuf *); + void (*irq_enable)(struct qwz_softc *sc); + void (*irq_disable)(struct qwz_softc *sc); + int (*map_service_to_pipe)(struct qwz_softc *, uint16_t, + uint8_t *, uint8_t *); + int (*get_user_msi_vector)(struct qwz_softc *, char *, + int *, uint32_t *, uint32_t *); +}; + +struct qwz_dmamem { + bus_dmamap_t map; + bus_dma_segment_t seg; + size_t size; + caddr_t kva; +}; + +struct qwz_dmamem *qwz_dmamem_alloc(bus_dma_tag_t, bus_size_t, bus_size_t); +void qwz_dmamem_free(bus_dma_tag_t, struct qwz_dmamem *); + +#define QWZ_DMA_MAP(_adm) ((_adm)->map) +#define QWZ_DMA_LEN(_adm) ((_adm)->size) +#define QWZ_DMA_DVA(_adm) ((_adm)->map->dm_segs[0].ds_addr) +#define QWZ_DMA_KVA(_adm) ((void *)(_adm)->kva) + +struct hal_srng_params { + bus_addr_t ring_base_paddr; + uint32_t *ring_base_vaddr; + int num_entries; + uint32_t intr_batch_cntr_thres_entries; + uint32_t intr_timer_thres_us; + uint32_t flags; + uint32_t max_buffer_len; + uint32_t low_threshold; + uint64_t msi_addr; + uint32_t msi_data; + + /* Add more params as needed */ +}; + +enum hal_srng_dir { + HAL_SRNG_DIR_SRC, + HAL_SRNG_DIR_DST +}; + +/* srng flags */ +#define HAL_SRNG_FLAGS_MSI_SWAP 0x00000008 +#define HAL_SRNG_FLAGS_RING_PTR_SWAP 0x00000010 +#define HAL_SRNG_FLAGS_DATA_TLV_SWAP 0x00000020 +#define HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN 0x00010000 +#define HAL_SRNG_FLAGS_MSI_INTR 0x00020000 +#define HAL_SRNG_FLAGS_CACHED 0x20000000 +#define HAL_SRNG_FLAGS_LMAC_RING 0x80000000 +#define HAL_SRNG_FLAGS_REMAP_CE_RING 0x10000000 + +#define HAL_SRNG_TLV_HDR_TAG GENMASK(9, 1) +#define HAL_SRNG_TLV_HDR_LEN GENMASK(25, 10) + +/* Common SRNG ring structure for source and destination rings */ +struct hal_srng { + /* Unique SRNG ring ID */ + uint8_t ring_id; + + /* Ring initialization done */ + uint8_t initialized; + + /* Interrupt/MSI value assigned to this ring */ + int irq; + + /* Physical base address of the ring */ + bus_addr_t ring_base_paddr; + + /* Virtual base address of the ring */ + uint32_t *ring_base_vaddr; + + /* Number of entries in ring */ + uint32_t num_entries; + + /* Ring size */ + uint32_t ring_size; + + /* Ring size mask */ + uint32_t ring_size_mask; + + /* Size of ring entry */ + uint32_t entry_size; + + /* Interrupt timer threshold - in micro seconds */ + uint32_t intr_timer_thres_us; + + /* Interrupt batch counter threshold - in number of ring entries */ + uint32_t intr_batch_cntr_thres_entries; + + /* MSI Address */ + bus_addr_t msi_addr; + + /* MSI data */ + uint32_t msi_data; + + /* Misc flags */ + uint32_t flags; +#ifdef notyet + /* Lock for serializing ring index updates */ + spinlock_t lock; +#endif + /* Start offset of SRNG register groups for this ring + * TBD: See if this is required - register address can be derived + * from ring ID + */ + uint32_t hwreg_base[HAL_SRNG_NUM_REG_GRP]; + + uint64_t timestamp; + + /* Source or Destination ring */ + enum hal_srng_dir ring_dir; + + union { + struct { + /* SW tail pointer */ + uint32_t tp; + + /* Shadow head pointer location to be updated by HW */ + volatile uint32_t *hp_addr; + + /* Cached head pointer */ + uint32_t cached_hp; + + /* Tail pointer location to be updated by SW - This + * will be a register address and need not be + * accessed through SW structure + */ + uint32_t *tp_addr; + + /* Current SW loop cnt */ + uint32_t loop_cnt; + + /* max transfer size */ + uint16_t max_buffer_length; + + /* head pointer at access end */ + uint32_t last_hp; + } dst_ring; + + struct { + /* SW head pointer */ + uint32_t hp; + + /* SW reap head pointer */ + uint32_t reap_hp; + + /* Shadow tail pointer location to be updated by HW */ + uint32_t *tp_addr; + + /* Cached tail pointer */ + uint32_t cached_tp; + + /* Head pointer location to be updated by SW - This + * will be a register address and need not be accessed + * through SW structure + */ + uint32_t *hp_addr; + + /* Low threshold - in number of ring entries */ + uint32_t low_threshold; + + /* tail pointer at access end */ + uint32_t last_tp; + } src_ring; + } u; +}; + +enum hal_ring_type { + HAL_REO_DST, + HAL_REO_EXCEPTION, + HAL_REO_REINJECT, + HAL_REO_CMD, + HAL_REO_STATUS, + HAL_TCL_DATA, + HAL_TCL_CMD, + HAL_TCL_STATUS, + HAL_CE_SRC, + HAL_CE_DST, + HAL_CE_DST_STATUS, + HAL_WBM_IDLE_LINK, + HAL_SW2WBM_RELEASE, + HAL_WBM2SW_RELEASE, + HAL_RXDMA_BUF, + HAL_RXDMA_DST, + HAL_RXDMA_MONITOR_BUF, + HAL_RXDMA_MONITOR_STATUS, + HAL_RXDMA_MONITOR_DST, + HAL_RXDMA_MONITOR_DESC, + HAL_RXDMA_DIR_BUF, + HAL_MAX_RING_TYPES, +}; + +/* HW SRNG configuration table */ +struct hal_srng_config { + int start_ring_id; + uint16_t max_rings; + uint16_t entry_size; + uint32_t reg_start[HAL_SRNG_NUM_REG_GRP]; + uint16_t reg_size[HAL_SRNG_NUM_REG_GRP]; + uint8_t lmac_ring; + enum hal_srng_dir ring_dir; + uint32_t max_size; +}; + +#define QWZ_NUM_SRNG_CFG 21 + +struct hal_reo_status_header { + uint16_t cmd_num; + enum hal_reo_cmd_status cmd_status; + uint16_t cmd_exe_time; + uint32_t timestamp; +}; + +struct hal_reo_status_queue_stats { + uint16_t ssn; + uint16_t curr_idx; + uint32_t pn[4]; + uint32_t last_rx_queue_ts; + uint32_t last_rx_dequeue_ts; + uint32_t rx_bitmap[8]; /* Bitmap from 0-255 */ + uint32_t curr_mpdu_cnt; + uint32_t curr_msdu_cnt; + uint16_t fwd_due_to_bar_cnt; + uint16_t dup_cnt; + uint32_t frames_in_order_cnt; + uint32_t num_mpdu_processed_cnt; + uint32_t num_msdu_processed_cnt; + uint32_t total_num_processed_byte_cnt; + uint32_t late_rx_mpdu_cnt; + uint32_t reorder_hole_cnt; + uint8_t timeout_cnt; + uint8_t bar_rx_cnt; + uint8_t num_window_2k_jump_cnt; +}; + +struct hal_reo_status_flush_queue { + bool err_detected; +}; + +enum hal_reo_status_flush_cache_err_code { + HAL_REO_STATUS_FLUSH_CACHE_ERR_CODE_SUCCESS, + HAL_REO_STATUS_FLUSH_CACHE_ERR_CODE_IN_USE, + HAL_REO_STATUS_FLUSH_CACHE_ERR_CODE_NOT_FOUND, +}; + +struct hal_reo_status_flush_cache { + bool err_detected; + enum hal_reo_status_flush_cache_err_code err_code; + bool cache_controller_flush_status_hit; + uint8_t cache_controller_flush_status_desc_type; + uint8_t cache_controller_flush_status_client_id; + uint8_t cache_controller_flush_status_err; + uint8_t cache_controller_flush_status_cnt; +}; + +enum hal_reo_status_unblock_cache_type { + HAL_REO_STATUS_UNBLOCK_BLOCKING_RESOURCE, + HAL_REO_STATUS_UNBLOCK_ENTIRE_CACHE_USAGE, +}; + +struct hal_reo_status_unblock_cache { + bool err_detected; + enum hal_reo_status_unblock_cache_type unblock_type; +}; + +struct hal_reo_status_flush_timeout_list { + bool err_detected; + bool list_empty; + uint16_t release_desc_cnt; + uint16_t fwd_buf_cnt; +}; + +enum hal_reo_threshold_idx { + HAL_REO_THRESHOLD_IDX_DESC_COUNTER0, + HAL_REO_THRESHOLD_IDX_DESC_COUNTER1, + HAL_REO_THRESHOLD_IDX_DESC_COUNTER2, + HAL_REO_THRESHOLD_IDX_DESC_COUNTER_SUM, +}; + +struct hal_reo_status_desc_thresh_reached { + enum hal_reo_threshold_idx threshold_idx; + uint32_t link_desc_counter0; + uint32_t link_desc_counter1; + uint32_t link_desc_counter2; + uint32_t link_desc_counter_sum; +}; + +struct hal_reo_status { + struct hal_reo_status_header uniform_hdr; + uint8_t loop_cnt; + union { + struct hal_reo_status_queue_stats queue_stats; + struct hal_reo_status_flush_queue flush_queue; + struct hal_reo_status_flush_cache flush_cache; + struct hal_reo_status_unblock_cache unblock_cache; + struct hal_reo_status_flush_timeout_list timeout_list; + struct hal_reo_status_desc_thresh_reached desc_thresh_reached; + } u; +}; + +/* HAL context to be used to access SRNG APIs (currently used by data path + * and transport (CE) modules) + */ +struct ath12k_hal { + /* HAL internal state for all SRNG rings. + */ + struct hal_srng srng_list[HAL_SRNG_RING_ID_MAX]; + + /* SRNG configuration table */ + struct hal_srng_config srng_config[QWZ_NUM_SRNG_CFG]; + + /* Remote pointer memory for HW/FW updates */ + struct qwz_dmamem *rdpmem; + struct { + uint32_t *vaddr; + bus_addr_t paddr; + } rdp; + + /* Shared memory for ring pointer updates from host to FW */ + struct qwz_dmamem *wrpmem; + struct { + uint32_t *vaddr; + bus_addr_t paddr; + } wrp; + + /* Available REO blocking resources bitmap */ + uint8_t avail_blk_resource; + + uint8_t current_blk_index; + + /* shadow register configuration */ + uint32_t shadow_reg_addr[HAL_SHADOW_NUM_REGS]; + int num_shadow_reg_configured; +#ifdef notyet + struct lock_class_key srng_key[HAL_SRNG_RING_ID_MAX]; +#endif +}; + +enum hal_pn_type { + HAL_PN_TYPE_NONE, + HAL_PN_TYPE_WPA, + HAL_PN_TYPE_WAPI_EVEN, + HAL_PN_TYPE_WAPI_UNEVEN, +}; + +enum hal_ce_desc { + HAL_CE_DESC_SRC, + HAL_CE_DESC_DST, + HAL_CE_DESC_DST_STATUS, +}; + +struct ce_ie_addr { + uint32_t ie1_reg_addr; + uint32_t ie2_reg_addr; + uint32_t ie3_reg_addr; +}; + +struct ce_remap { + uint32_t base; + uint32_t size; +}; + +struct ce_attr { + /* CE_ATTR_* values */ + unsigned int flags; + + /* #entries in source ring - Must be a power of 2 */ + unsigned int src_nentries; + + /* + * Max source send size for this CE. + * This is also the minimum size of a destination buffer. + */ + unsigned int src_sz_max; + + /* #entries in destination ring - Must be a power of 2 */ + unsigned int dest_nentries; + + void (*recv_cb)(struct qwz_softc *, struct mbuf *); + void (*send_cb)(struct qwz_softc *, struct mbuf *); +}; + +#define CE_DESC_RING_ALIGN 8 + +struct qwz_rx_msdu { + TAILQ_ENTRY(qwz_rx_msdu) entry; + struct mbuf *m; + struct ieee80211_rxinfo rxi; + int is_first_msdu; + int is_last_msdu; + int is_continuation; + int is_mcbc; + int is_eapol; + struct hal_rx_desc *rx_desc; + uint8_t err_rel_src; + uint8_t err_code; + uint8_t mac_id; + uint8_t unmapped; + uint8_t is_frag; + uint8_t tid; + uint16_t peer_id; + uint16_t seq_no; +}; + +TAILQ_HEAD(qwz_rx_msdu_list, qwz_rx_msdu); + +struct qwz_rx_data { + struct mbuf *m; + bus_dmamap_t map; + struct qwz_rx_msdu rx_msdu; +}; + +struct qwz_tx_data { + struct ieee80211_node *ni; + struct mbuf *m; + bus_dmamap_t map; + uint8_t eid; + uint8_t flags; + uint32_t cipher; +}; + +struct qwz_ce_ring { + /* Number of entries in this ring; must be power of 2 */ + unsigned int nentries; + unsigned int nentries_mask; + + /* For dest ring, this is the next index to be processed + * by software after it was/is received into. + * + * For src ring, this is the last descriptor that was sent + * and completion processed by software. + * + * Regardless of src or dest ring, this is an invariant + * (modulo ring size): + * write index >= read index >= sw_index + */ + unsigned int sw_index; + /* cached copy */ + unsigned int write_index; + + /* Start of DMA-coherent area reserved for descriptors */ + /* Host address space */ + caddr_t base_addr; + + /* DMA map for Tx/Rx descriptors. */ + bus_dmamap_t dmap; + bus_dma_segment_t dsegs; + int nsegs; + size_t desc_sz; + + /* HAL ring id */ + uint32_t hal_ring_id; + + /* + * Per-transfer data. + * Size and type of this data depends on how the ring is used. + * + * For transfers using DMA, the context contains pointers to + * struct qwz_rx_data if this ring is a dest ring, or struct + * qwz_tx_data if this ring is a src ring. DMA maps are allocated + * when the device is started via sc->ops.start, and will be used + * to load mbufs for DMA transfers. + * In this case, the pointers MUST NOT be cleared until the device + * is stopped. Otherwise we'd lose track of our DMA mappings! + * The Linux ath12k driver works differently because it can store + * DMA mapping information in a Linux socket buffer structure, which + * is not possible with mbufs. + * + * Keep last. + */ + void *per_transfer_context[0]; +}; + +void qwz_htc_tx_completion_handler(struct qwz_softc *, struct mbuf *); +void qwz_htc_rx_completion_handler(struct qwz_softc *, struct mbuf *); +void qwz_dp_htt_htc_t2h_msg_handler(struct qwz_softc *, struct mbuf *); + +struct qwz_dp; + +struct qwz_dp_htt_wbm_tx_status { + uint32_t msdu_id; + int acked; + int ack_rssi; + uint16_t peer_id; +}; + +#define DP_NUM_CLIENTS_MAX 64 +#define DP_AVG_TIDS_PER_CLIENT 2 +#define DP_NUM_TIDS_MAX (DP_NUM_CLIENTS_MAX * DP_AVG_TIDS_PER_CLIENT) +#define DP_AVG_MSDUS_PER_FLOW 128 +#define DP_AVG_FLOWS_PER_TID 2 +#define DP_AVG_MPDUS_PER_TID_MAX 128 +#define DP_AVG_MSDUS_PER_MPDU 4 + +#define DP_RX_HASH_ENABLE 1 /* Enable hash based Rx steering */ + +#define DP_BA_WIN_SZ_MAX 256 + +#define DP_TCL_NUM_RING_MAX 3 +#define DP_TCL_NUM_RING_MAX_QCA6390 1 + +#define DP_IDLE_SCATTER_BUFS_MAX 16 + +#define DP_WBM_RELEASE_RING_SIZE 64 +#define DP_TCL_DATA_RING_SIZE 512 +#define DP_TCL_DATA_RING_SIZE_WCN6750 2048 +#define DP_TX_COMP_RING_SIZE 32768 +#define DP_TX_IDR_SIZE DP_TX_COMP_RING_SIZE +#define DP_TCL_CMD_RING_SIZE 32 +#define DP_TCL_STATUS_RING_SIZE 32 +#define DP_REO_DST_RING_MAX 4 +#define DP_REO_DST_RING_SIZE 2048 +#define DP_REO_REINJECT_RING_SIZE 32 +#define DP_RX_RELEASE_RING_SIZE 1024 +#define DP_REO_EXCEPTION_RING_SIZE 128 +#define DP_REO_CMD_RING_SIZE 256 +#define DP_REO_STATUS_RING_SIZE 2048 +#define DP_RXDMA_BUF_RING_SIZE 4096 +#define DP_RXDMA_REFILL_RING_SIZE 2048 +#define DP_RXDMA_ERR_DST_RING_SIZE 1024 +#define DP_RXDMA_MON_STATUS_RING_SIZE 1024 +#define DP_RXDMA_MONITOR_BUF_RING_SIZE 4096 +#define DP_RXDMA_MONITOR_DST_RING_SIZE 2048 +#define DP_RXDMA_MONITOR_DESC_RING_SIZE 4096 + +#define DP_RX_RELEASE_RING_NUM 3 + +#define DP_RX_BUFFER_SIZE 2048 +#define DP_RX_BUFFER_SIZE_LITE 1024 +#define DP_RX_BUFFER_ALIGN_SIZE 128 + +#define DP_RXDMA_BUF_COOKIE_BUF_ID GENMASK(17, 0) +#define DP_RXDMA_BUF_COOKIE_PDEV_ID GENMASK(20, 18) + +#define DP_HW2SW_MACID(mac_id) ((mac_id) ? ((mac_id) - 1) : 0) +#define DP_SW2HW_MACID(mac_id) ((mac_id) + 1) + +#define DP_TX_DESC_ID_MAC_ID GENMASK(1, 0) +#define DP_TX_DESC_ID_MSDU_ID GENMASK(18, 2) +#define DP_TX_DESC_ID_POOL_ID GENMASK(20, 19) + +struct qwz_hp_update_timer { + struct timeout timer; + int started; + int init; + uint32_t tx_num; + uint32_t timer_tx_num; + uint32_t ring_id; + uint32_t interval; + struct qwz_softc *sc; +}; + +struct dp_rx_tid { + uint8_t tid; + struct qwz_dmamem *mem; + uint32_t *vaddr; + uint64_t paddr; + uint32_t size; + uint32_t ba_win_sz; + int active; + + /* Info related to rx fragments */ + uint32_t cur_sn; + uint16_t last_frag_no; + uint16_t rx_frag_bitmap; +#if 0 + struct sk_buff_head rx_frags; + struct hal_reo_dest_ring *dst_ring_desc; + + /* Timer info related to fragments */ + struct timer_list frag_timer; + struct ath12k_base *ab; +#endif +}; + +#define DP_REO_DESC_FREE_THRESHOLD 64 +#define DP_REO_DESC_FREE_TIMEOUT_MS 1000 +#define DP_MON_PURGE_TIMEOUT_MS 100 +#define DP_MON_SERVICE_BUDGET 128 + +struct dp_reo_cache_flush_elem { + TAILQ_ENTRY(dp_reo_cache_flush_elem) entry; + struct dp_rx_tid data; + uint64_t ts; +}; + +TAILQ_HEAD(dp_reo_cmd_cache_flush_head, dp_reo_cache_flush_elem); + +struct dp_reo_cmd { + TAILQ_ENTRY(dp_reo_cmd) entry; + struct dp_rx_tid data; + int cmd_num; + void (*handler)(struct qwz_dp *, void *, + enum hal_reo_cmd_status status); +}; + +TAILQ_HEAD(dp_reo_cmd_head, dp_reo_cmd); + +struct dp_srng { + struct qwz_dmamem *mem; + uint32_t *vaddr; + bus_addr_t paddr; + int size; + uint32_t ring_id; + uint8_t cached; +}; + +struct dp_tx_ring { + uint8_t tcl_data_ring_id; + struct dp_srng tcl_data_ring; + struct dp_srng tcl_comp_ring; + int cur; + int queued; + struct qwz_tx_data *data; + struct hal_wbm_release_ring *tx_status; + int tx_status_head; + int tx_status_tail; +}; + + +struct dp_link_desc_bank { + struct qwz_dmamem *mem; + caddr_t *vaddr; + bus_addr_t paddr; + uint32_t size; +}; + +/* Size to enforce scatter idle list mode */ +#define DP_LINK_DESC_ALLOC_SIZE_THRESH 0x200000 +#define DP_LINK_DESC_BANKS_MAX 8 + +struct hal_wbm_idle_scatter_list { + struct qwz_dmamem *mem; + bus_addr_t paddr; + struct hal_wbm_link_desc *vaddr; +}; + +struct qwz_dp { + struct qwz_softc *sc; + enum ath12k_htc_ep_id eid; + int htt_tgt_version_received; + uint8_t htt_tgt_ver_major; + uint8_t htt_tgt_ver_minor; + struct dp_link_desc_bank link_desc_banks[DP_LINK_DESC_BANKS_MAX]; + struct dp_srng wbm_idle_ring; + struct dp_srng wbm_desc_rel_ring; + struct dp_srng tcl_cmd_ring; + struct dp_srng tcl_status_ring; + struct dp_srng reo_reinject_ring; + struct dp_srng rx_rel_ring; + struct dp_srng reo_except_ring; + struct dp_srng reo_cmd_ring; + struct dp_srng reo_status_ring; + struct dp_srng reo_dst_ring[DP_REO_DST_RING_MAX]; + struct dp_tx_ring tx_ring[DP_TCL_NUM_RING_MAX]; + struct hal_wbm_idle_scatter_list scatter_list[DP_IDLE_SCATTER_BUFS_MAX]; + struct dp_reo_cmd_head reo_cmd_list; + struct dp_reo_cmd_cache_flush_head reo_cmd_cache_flush_list; +#if 0 + struct list_head dp_full_mon_mpdu_list; +#endif + uint32_t reo_cmd_cache_flush_count; +#if 0 + /** + * protects access to below fields, + * - reo_cmd_list + * - reo_cmd_cache_flush_list + * - reo_cmd_cache_flush_count + */ + spinlock_t reo_cmd_lock; +#endif + struct qwz_hp_update_timer reo_cmd_timer; + struct qwz_hp_update_timer tx_ring_timer[DP_TCL_NUM_RING_MAX]; +}; + +#define ATH12K_SHADOW_DP_TIMER_INTERVAL 20 +#define ATH12K_SHADOW_CTRL_TIMER_INTERVAL 10 + +struct qwz_ce_pipe { + struct qwz_softc *sc; + uint16_t pipe_num; + unsigned int attr_flags; + unsigned int buf_sz; + unsigned int rx_buf_needed; + + void (*send_cb)(struct qwz_softc *, struct mbuf *); + void (*recv_cb)(struct qwz_softc *, struct mbuf *); + +#ifdef notyet + struct tasklet_struct intr_tq; +#endif + struct qwz_ce_ring *src_ring; + struct qwz_ce_ring *dest_ring; + struct qwz_ce_ring *status_ring; + uint64_t timestamp; +}; + +struct qwz_ce { + struct qwz_ce_pipe ce_pipe[CE_COUNT_MAX]; +#ifdef notyet + /* Protects rings of all ce pipes */ + spinlock_t ce_lock; +#endif + struct qwz_hp_update_timer hp_timer[CE_COUNT_MAX]; +}; + + +/* XXX This may be non-zero on AHB but is always zero on PCI. */ +#define ATH12K_CE_OFFSET(sc) (0) + +struct qwz_qmi_ce_cfg { + const uint8_t *shadow_reg; + int shadow_reg_len; + uint32_t *shadow_reg_v2; + uint32_t shadow_reg_v2_len; +}; + +struct qwz_qmi_target_info { + uint32_t chip_id; + uint32_t chip_family; + uint32_t board_id; + uint32_t soc_id; + uint32_t fw_version; + uint32_t eeprom_caldata; + char fw_build_timestamp[ATH12K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1]; + char fw_build_id[ATH12K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1]; + char bdf_ext[ATH12K_QMI_BDF_EXT_STR_LENGTH]; +}; + +enum ath12k_bdf_search { + ATH12K_BDF_SEARCH_DEFAULT, + ATH12K_BDF_SEARCH_BUS_AND_BOARD, +}; + +struct qwz_device_id { + enum ath12k_bdf_search bdf_search; + uint32_t vendor; + uint32_t device; + uint32_t subsystem_vendor; + uint32_t subsystem_device; +}; + +struct qwz_wmi_base; + +struct qwz_pdev_wmi { + struct qwz_wmi_base *wmi; + enum ath12k_htc_ep_id eid; + const struct wmi_peer_flags_map *peer_flags; + uint32_t rx_decap_mode; + int tx_ce_desc; +}; + +#define QWZ_MAX_RADIOS 3 + +struct qwz_wmi_base { + struct qwz_softc *sc; + struct qwz_pdev_wmi wmi[QWZ_MAX_RADIOS]; + enum ath12k_htc_ep_id wmi_endpoint_id[QWZ_MAX_RADIOS]; + uint32_t max_msg_len[QWZ_MAX_RADIOS]; + int service_ready; + int unified_ready; + uint8_t svc_map[howmany(WMI_MAX_EXT2_SERVICE, 8)]; + int tx_credits; + const struct wmi_peer_flags_map *peer_flags; + uint32_t num_mem_chunks; + uint32_t rx_decap_mode; + struct wmi_host_mem_chunk mem_chunks[WMI_MAX_MEM_REQS]; + enum wmi_host_hw_mode_config_type preferred_hw_mode; + struct target_resource_config wlan_resource_config; + struct ath12k_targ_cap *targ_cap; +}; + +struct wmi_tlv_policy { + size_t min_len; +}; + +struct wmi_tlv_svc_ready_parse { + int wmi_svc_bitmap_done; +}; + +struct wmi_tlv_dma_ring_caps_parse { + struct wmi_dma_ring_capabilities *dma_ring_caps; + uint32_t n_dma_ring_caps; +}; + +struct wmi_tlv_svc_rdy_ext_parse { + struct ath12k_service_ext_param param; + struct wmi_soc_mac_phy_hw_mode_caps *hw_caps; + struct wmi_hw_mode_capabilities *hw_mode_caps; + uint32_t n_hw_mode_caps; + uint32_t tot_phy_id; + struct wmi_hw_mode_capabilities pref_hw_mode_caps; + struct wmi_mac_phy_capabilities *mac_phy_caps; + size_t mac_phy_caps_size; + uint32_t n_mac_phy_caps; + struct wmi_soc_hal_reg_capabilities *soc_hal_reg_caps; + struct wmi_hal_reg_capabilities_ext *ext_hal_reg_caps; + uint32_t n_ext_hal_reg_caps; + struct wmi_tlv_dma_ring_caps_parse dma_caps_parse; + int hw_mode_done; + int mac_phy_done; + int ext_hal_reg_done; + int mac_phy_chainmask_combo_done; + int mac_phy_chainmask_cap_done; + int oem_dma_ring_cap_done; + int dma_ring_cap_done; +}; + +struct wmi_tlv_svc_rdy_ext2_parse { + struct wmi_tlv_dma_ring_caps_parse dma_caps_parse; + bool dma_ring_cap_done; +}; + +struct wmi_tlv_rdy_parse { + uint32_t num_extra_mac_addr; +}; + +struct wmi_tlv_dma_buf_release_parse { + struct ath12k_wmi_dma_buf_release_fixed_param fixed; + struct wmi_dma_buf_release_entry *buf_entry; + struct wmi_dma_buf_release_meta_data *meta_data; + uint32_t num_buf_entry; + uint32_t num_meta; + bool buf_entry_done; + bool meta_data_done; +}; + +struct wmi_tlv_fw_stats_parse { + const struct wmi_stats_event *ev; + const struct wmi_per_chain_rssi_stats *rssi; + struct ath12k_fw_stats *stats; + int rssi_num; + bool chain_rssi_done; +}; + +struct wmi_tlv_mgmt_rx_parse { + const struct wmi_mgmt_rx_hdr *fixed; + const uint8_t *frame_buf; + bool frame_buf_done; +}; + +struct qwz_htc; + +struct qwz_htc_ep_ops { + void (*ep_tx_complete)(struct qwz_softc *, struct mbuf *); + void (*ep_rx_complete)(struct qwz_softc *, struct mbuf *); + void (*ep_tx_credits)(struct qwz_softc *); +}; + +/* service connection information */ +struct qwz_htc_svc_conn_req { + uint16_t service_id; + struct qwz_htc_ep_ops ep_ops; + int max_send_queue_depth; +}; + +/* service connection response information */ +struct qwz_htc_svc_conn_resp { + uint8_t buffer_len; + uint8_t actual_len; + enum ath12k_htc_ep_id eid; + unsigned int max_msg_len; + uint8_t connect_resp_code; +}; + +#define ATH12K_NUM_CONTROL_TX_BUFFERS 2 +#define ATH12K_HTC_MAX_LEN 4096 +#define ATH12K_HTC_MAX_CTRL_MSG_LEN 256 +#define ATH12K_HTC_WAIT_TIMEOUT_HZ (1 * HZ) +#define ATH12K_HTC_CONTROL_BUFFER_SIZE (ATH12K_HTC_MAX_CTRL_MSG_LEN + \ + sizeof(struct ath12k_htc_hdr)) +#define ATH12K_HTC_CONN_SVC_TIMEOUT_HZ (1 * HZ) +#define ATH12K_HTC_MAX_SERVICE_ALLOC_ENTRIES 8 + +struct qwz_htc_ep { + struct qwz_htc *htc; + enum ath12k_htc_ep_id eid; + enum ath12k_htc_svc_id service_id; + struct qwz_htc_ep_ops ep_ops; + + int max_tx_queue_depth; + int max_ep_message_len; + uint8_t ul_pipe_id; + uint8_t dl_pipe_id; + + uint8_t seq_no; /* for debugging */ + int tx_credits; + bool tx_credit_flow_enabled; +}; + +struct qwz_htc_svc_tx_credits { + uint16_t service_id; + uint8_t credit_allocation; +}; + +struct qwz_htc { + struct qwz_softc *sc; + struct qwz_htc_ep endpoint[ATH12K_HTC_EP_COUNT]; +#ifdef notyet + /* protects endpoints */ + spinlock_t tx_lock; +#endif + uint8_t control_resp_buffer[ATH12K_HTC_MAX_CTRL_MSG_LEN]; + int control_resp_len; + + int ctl_resp; + + int total_transmit_credits; + struct qwz_htc_svc_tx_credits + service_alloc_table[ATH12K_HTC_MAX_SERVICE_ALLOC_ENTRIES]; + int target_credit_size; + uint8_t wmi_ep_count; +}; + +struct qwz_msi_user { + char *name; + int num_vectors; + uint32_t base_vector; +}; + +struct qwz_msi_config { + int total_vectors; + int total_users; + struct qwz_msi_user *users; + uint16_t hw_rev; +}; + +struct ath12k_band_cap { + uint32_t phy_id; + uint32_t max_bw_supported; + uint32_t ht_cap_info; + uint32_t he_cap_info[2]; + uint32_t he_mcs; + uint32_t he_cap_phy_info[PSOC_HOST_MAX_PHY_SIZE]; + struct ath12k_ppe_threshold he_ppet; + uint16_t he_6ghz_capa; +}; + +struct ath12k_pdev_cap { + uint32_t supported_bands; + uint32_t ampdu_density; + uint32_t vht_cap; + uint32_t vht_mcs; + uint32_t he_mcs; + uint32_t tx_chain_mask; + uint32_t rx_chain_mask; + uint32_t tx_chain_mask_shift; + uint32_t rx_chain_mask_shift; + struct ath12k_band_cap band[WMI_NUM_SUPPORTED_BAND_MAX]; + int nss_ratio_enabled; + uint8_t nss_ratio_info; +}; + +struct qwz_pdev { + struct qwz_softc *sc; + uint32_t pdev_id; + struct ath12k_pdev_cap cap; + uint8_t mac_addr[IEEE80211_ADDR_LEN]; +}; + +struct qwz_dbring_cap { + uint32_t pdev_id; + enum wmi_direct_buffer_module id; + uint32_t min_elem; + uint32_t min_buf_sz; + uint32_t min_buf_align; +}; + +struct dp_rxdma_ring { + struct dp_srng refill_buf_ring; +#if 0 + struct idr bufs_idr; + /* Protects bufs_idr */ + spinlock_t idr_lock; +#else + struct qwz_rx_data *rx_data; +#endif + int bufs_max; + uint8_t freemap[howmany(DP_RXDMA_BUF_RING_SIZE, 8)]; +}; + +enum hal_rx_mon_status { + HAL_RX_MON_STATUS_PPDU_NOT_DONE, + HAL_RX_MON_STATUS_PPDU_DONE, + HAL_RX_MON_STATUS_BUF_DONE, +}; + +struct hal_rx_user_status { + uint32_t mcs:4, + nss:3, + ofdma_info_valid:1, + dl_ofdma_ru_start_index:7, + dl_ofdma_ru_width:7, + dl_ofdma_ru_size:8; + uint32_t ul_ofdma_user_v0_word0; + uint32_t ul_ofdma_user_v0_word1; + uint32_t ast_index; + uint32_t tid; + uint16_t tcp_msdu_count; + uint16_t udp_msdu_count; + uint16_t other_msdu_count; + uint16_t frame_control; + uint8_t frame_control_info_valid; + uint8_t data_sequence_control_info_valid; + uint16_t first_data_seq_ctrl; + uint32_t preamble_type; + uint16_t ht_flags; + uint16_t vht_flags; + uint16_t he_flags; + uint8_t rs_flags; + uint32_t mpdu_cnt_fcs_ok; + uint32_t mpdu_cnt_fcs_err; + uint32_t mpdu_fcs_ok_bitmap[8]; + uint32_t mpdu_ok_byte_count; + uint32_t mpdu_err_byte_count; +}; + +struct hal_rx_wbm_rel_info { + uint32_t cookie; + enum hal_wbm_rel_src_module err_rel_src; + enum hal_reo_dest_ring_push_reason push_reason; + uint32_t err_code; + int first_msdu; + int last_msdu; +}; + +#define HAL_INVALID_PEERID 0xffff +#define VHT_SIG_SU_NSS_MASK 0x7 + +#define HAL_RX_MAX_MCS 12 +#define HAL_RX_MAX_NSS 8 + +#define HAL_TLV_STATUS_PPDU_NOT_DONE HAL_RX_MON_STATUS_PPDU_NOT_DONE +#define HAL_TLV_STATUS_PPDU_DONE HAL_RX_MON_STATUS_PPDU_DONE +#define HAL_TLV_STATUS_BUF_DONE HAL_RX_MON_STATUS_BUF_DONE + +struct hal_rx_mon_ppdu_info { + uint32_t ppdu_id; + uint32_t ppdu_ts; + uint32_t num_mpdu_fcs_ok; + uint32_t num_mpdu_fcs_err; + uint32_t preamble_type; + uint16_t chan_num; + uint16_t tcp_msdu_count; + uint16_t tcp_ack_msdu_count; + uint16_t udp_msdu_count; + uint16_t other_msdu_count; + uint16_t peer_id; + uint8_t rate; + uint8_t mcs; + uint8_t nss; + uint8_t bw; + uint8_t vht_flag_values1; + uint8_t vht_flag_values2; + uint8_t vht_flag_values3[4]; + uint8_t vht_flag_values4; + uint8_t vht_flag_values5; + uint16_t vht_flag_values6; + uint8_t is_stbc; + uint8_t gi; + uint8_t ldpc; + uint8_t beamformed; + uint8_t rssi_comb; + uint8_t rssi_chain_pri20[HAL_RX_MAX_NSS]; + uint8_t tid; + uint16_t ht_flags; + uint16_t vht_flags; + uint16_t he_flags; + uint16_t he_mu_flags; + uint8_t dcm; + uint8_t ru_alloc; + uint8_t reception_type; + uint64_t tsft; + uint64_t rx_duration; + uint16_t frame_control; + uint32_t ast_index; + uint8_t rs_fcs_err; + uint8_t rs_flags; + uint8_t cck_flag; + uint8_t ofdm_flag; + uint8_t ulofdma_flag; + uint8_t frame_control_info_valid; + uint16_t he_per_user_1; + uint16_t he_per_user_2; + uint8_t he_per_user_position; + uint8_t he_per_user_known; + uint16_t he_flags1; + uint16_t he_flags2; + uint8_t he_RU[4]; + uint16_t he_data1; + uint16_t he_data2; + uint16_t he_data3; + uint16_t he_data4; + uint16_t he_data5; + uint16_t he_data6; + uint32_t ppdu_len; + uint32_t prev_ppdu_id; + uint32_t device_id; + uint16_t first_data_seq_ctrl; + uint8_t monitor_direct_used; + uint8_t data_sequence_control_info_valid; + uint8_t ltf_size; + uint8_t rxpcu_filter_pass; + char rssi_chain[8][8]; + struct hal_rx_user_status userstats; +}; + +enum dp_mon_status_buf_state { + /* PPDU id matches in dst ring and status ring */ + DP_MON_STATUS_MATCH, + /* status ring dma is not done */ + DP_MON_STATUS_NO_DMA, + /* status ring is lagging, reap status ring */ + DP_MON_STATUS_LAG, + /* status ring is leading, reap dst ring and drop */ + DP_MON_STATUS_LEAD, + /* replinish monitor status ring */ + DP_MON_STATUS_REPLINISH, +}; + +struct qwz_pdev_mon_stats { + uint32_t status_ppdu_state; + uint32_t status_ppdu_start; + uint32_t status_ppdu_end; + uint32_t status_ppdu_compl; + uint32_t status_ppdu_start_mis; + uint32_t status_ppdu_end_mis; + uint32_t status_ppdu_done; + uint32_t dest_ppdu_done; + uint32_t dest_mpdu_done; + uint32_t dest_mpdu_drop; + uint32_t dup_mon_linkdesc_cnt; + uint32_t dup_mon_buf_cnt; + uint32_t dest_mon_stuck; + uint32_t dest_mon_not_reaped; +}; + +struct qwz_mon_data { + struct dp_link_desc_bank link_desc_banks[DP_LINK_DESC_BANKS_MAX]; + struct hal_rx_mon_ppdu_info mon_ppdu_info; + + uint32_t mon_ppdu_status; + uint32_t mon_last_buf_cookie; + uint64_t mon_last_linkdesc_paddr; + uint16_t chan_noise_floor; + bool hold_mon_dst_ring; + enum dp_mon_status_buf_state buf_state; + bus_addr_t mon_status_paddr; + struct dp_full_mon_mpdu *mon_mpdu; +#ifdef notyet + struct hal_sw_mon_ring_entries sw_mon_entries; +#endif + struct qwz_pdev_mon_stats rx_mon_stats; +#ifdef notyet + /* lock for monitor data */ + spinlock_t mon_lock; + struct sk_buff_head rx_status_q; +#endif +}; + + +#define MAX_RXDMA_PER_PDEV 2 + +struct qwz_pdev_dp { + uint32_t mac_id; + uint32_t mon_dest_ring_stuck_cnt; +#if 0 + atomic_t num_tx_pending; + wait_queue_head_t tx_empty_waitq; +#endif + struct dp_rxdma_ring rx_refill_buf_ring; + struct dp_srng rx_mac_buf_ring[MAX_RXDMA_PER_PDEV]; + struct dp_srng rxdma_err_dst_ring[MAX_RXDMA_PER_PDEV]; + struct dp_srng rxdma_mon_dst_ring; + struct dp_srng rxdma_mon_desc_ring; + struct dp_rxdma_ring rxdma_mon_buf_ring; + struct dp_rxdma_ring rx_mon_status_refill_ring[MAX_RXDMA_PER_PDEV]; +#if 0 + struct ieee80211_rx_status rx_status; +#endif + struct qwz_mon_data mon_data; +}; + +struct qwz_txmgmt_queue { + struct qwz_tx_data data[8]; + int cur; + int queued; +}; + +struct qwz_vif { + uint32_t vdev_id; + enum wmi_vdev_type vdev_type; + enum wmi_vdev_subtype vdev_subtype; + uint32_t beacon_interval; + uint32_t dtim_period; + uint16_t ast_hash; + uint16_t ast_idx; + uint16_t tcl_metadata; + uint8_t hal_addr_search_flags; + uint8_t search_type; + + struct qwz_softc *sc; + + uint16_t tx_seq_no; + struct wmi_wmm_params_all_arg wmm_params; + TAILQ_ENTRY(qwz_vif) entry; + union { + struct { + uint32_t uapsd; + } sta; + struct { + /* 127 stations; wmi limit */ + uint8_t tim_bitmap[16]; + uint8_t tim_len; + uint32_t ssid_len; + uint8_t ssid[IEEE80211_NWID_LEN]; + bool hidden_ssid; + /* P2P_IE with NoA attribute for P2P_GO case */ + uint32_t noa_len; + uint8_t *noa_data; + } ap; + } u; + + bool is_started; + bool is_up; + bool ftm_responder; + bool spectral_enabled; + bool ps; + uint32_t aid; + uint8_t bssid[IEEE80211_ADDR_LEN]; +#if 0 + struct cfg80211_bitrate_mask bitrate_mask; + struct delayed_work connection_loss_work; +#endif + int num_legacy_stations; + int rtscts_prot_mode; + int txpower; + bool rsnie_present; + bool wpaie_present; + bool bcca_zero_sent; + bool do_not_send_tmpl; + struct ieee80211_channel *chan; +#if 0 + struct ath12k_arp_ns_offload arp_ns_offload; + struct ath12k_rekey_data rekey_data; +#endif +#ifdef CONFIG_ATH12K_DEBUGFS + struct dentry *debugfs_twt; +#endif /* CONFIG_ATH12K_DEBUGFS */ + + struct qwz_txmgmt_queue txmgmt; +}; + +TAILQ_HEAD(qwz_vif_list, qwz_vif); + +struct qwz_survey_info { + int8_t noise; + uint64_t time; + uint64_t time_busy; +}; + +#define ATH12K_IRQ_NUM_MAX 52 +#define ATH12K_EXT_IRQ_NUM_MAX 16 + +struct qwz_ext_irq_grp { + struct qwz_softc *sc; + uint32_t irqs[ATH12K_EXT_IRQ_NUM_MAX]; + uint32_t num_irq; + uint32_t grp_id; + uint64_t timestamp; +#if 0 + bool napi_enabled; + struct napi_struct napi; + struct net_device napi_ndev; +#endif +}; + +struct qwz_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; +} __packed; + +#define IWX_RX_RADIOTAP_PRESENT 0 /* TODO add more information */ + +struct qwz_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; +} __packed; + +#define IWX_TX_RADIOTAP_PRESENT 0 /* TODO add more information */ + +struct qwz_setkey_task_arg { + struct ieee80211_node *ni; + struct ieee80211_key *k; + int cmd; +#define QWZ_ADD_KEY 1 +#define QWZ_DEL_KEY 2 +}; + +struct qwz_softc { + struct device sc_dev; + struct ieee80211com sc_ic; + uint32_t sc_flags; + int sc_node; + + int (*sc_newstate)(struct ieee80211com *, enum ieee80211_state, int); + + struct rwlock ioctl_rwl; + + struct task init_task; /* NB: not reference-counted */ + struct refcnt task_refs; + struct taskq *sc_nswq; + struct task newstate_task; + enum ieee80211_state ns_nstate; + int ns_arg; + + /* Task for setting encryption keys and its arguments. */ + struct task setkey_task; + /* + * At present we need to process at most two keys at once: + * Our pairwise key and a group key. + * When hostap mode is implemented this array needs to grow or + * it might become a bottleneck for associations that occur at + * roughly the same time. + */ + struct qwz_setkey_task_arg setkey_arg[2]; + int setkey_cur; + int setkey_tail; + int setkey_nkeys; + + int install_key_done; + int install_key_status; + + enum ath12k_11d_state state_11d; + int completed_11d_scan; + uint32_t vdev_id_11d_scan; + struct { + int started; + int completed; + int on_channel; + struct timeout timeout; + enum ath12k_scan_state state; + int vdev_id; + int is_roc; + int roc_freq; + int roc_notify; + } scan; + u_int scan_channel; + struct qwz_survey_info survey[IEEE80211_CHAN_MAX]; + + int attached; + struct { + u_char *data; + size_t size; + } fw_img[4]; +#define QWZ_FW_AMSS 0 +#define QWZ_FW_BOARD 1 +#define QWZ_FW_M3 2 +#define QWZ_FW_REGDB 3 + + int sc_tx_timer; + uint32_t qfullmsk; +#define QWZ_MGMT_QUEUE_ID 31 + + bus_addr_t mem; + struct ath12k_hw_params hw_params; + struct ath12k_hal hal; + struct qwz_ce ce; + struct qwz_dp dp; + struct qwz_pdev_dp pdev_dp; + struct qwz_wmi_base wmi; + struct qwz_htc htc; + + enum ath12k_firmware_mode fw_mode; + enum ath12k_crypt_mode crypto_mode; + enum ath12k_hw_txrx_mode frame_mode; + + struct qwz_ext_irq_grp ext_irq_grp[ATH12K_EXT_IRQ_GRP_NUM_MAX]; + + uint16_t qmi_txn_id; + int qmi_cal_done; + struct qwz_qmi_ce_cfg qmi_ce_cfg; + struct qwz_qmi_target_info qmi_target; + struct ath12k_targ_cap target_caps; + int num_radios; + uint32_t cc_freq_hz; + uint32_t cfg_tx_chainmask; + uint32_t cfg_rx_chainmask; + int num_tx_chains; + int num_rx_chains; + int num_created_vdevs; + int num_started_vdevs; + uint32_t allocated_vdev_map; + uint32_t free_vdev_map; + int num_peers; + int peer_mapped; + int peer_delete_done; + int vdev_setup_done; + int peer_assoc_done; + + struct qwz_dbring_cap *db_caps; + uint32_t num_db_cap; + + uint8_t mac_addr[IEEE80211_ADDR_LEN]; + int wmi_ready; + uint32_t wlan_init_status; + + uint32_t pktlog_defs_checksum; + + struct qwz_vif_list vif_list; + struct qwz_pdev pdevs[MAX_RADIOS]; + struct { + enum WMI_HOST_WLAN_BAND supported_bands; + uint32_t pdev_id; + } target_pdev_ids[MAX_RADIOS]; + uint8_t target_pdev_count; + uint32_t pdevs_active; + int pdevs_macaddr_valid; + struct ath12k_hal_reg_capabilities_ext hal_reg_cap[MAX_RADIOS]; + + struct { + uint32_t service; + uint32_t instance; + uint32_t node; + uint32_t port; + } qrtr_server; + + struct qmi_response_type_v01 qmi_resp; + + struct qwz_dmamem *fwmem; + int expect_fwmem_req; + int fwmem_ready; + int fw_init_done; + + int ctl_resp; + + struct qwz_dmamem *m3_mem; + + struct timeout mon_reap_timer; +#define ATH12K_MON_TIMER_INTERVAL 10 + + /* Provided by attachment driver: */ + struct qwz_ops ops; + bus_dma_tag_t sc_dmat; + enum ath12k_hw_rev sc_hw_rev; + struct qwz_device_id id; + char sc_bus_str[4]; /* "pci" or "ahb" */ + int num_msivec; + uint32_t msi_addr_lo; + uint32_t msi_addr_hi; + uint32_t msi_data_start; + const struct qwz_msi_config *msi_cfg; + uint32_t msi_ce_irqmask; + + struct qmi_wlanfw_request_mem_ind_msg_v01 *sc_req_mem_ind; + + caddr_t sc_drvbpf; + + union { + struct qwz_rx_radiotap_header th; + uint8_t pad[IEEE80211_RADIOTAP_HDRLEN]; + } sc_rxtapu; +#define sc_rxtap sc_rxtapu.th + int sc_rxtap_len; + + union { + struct qwz_tx_radiotap_header th; + uint8_t pad[IEEE80211_RADIOTAP_HDRLEN]; + } sc_txtapu; +#define sc_txtap sc_txtapu.th + int sc_txtap_len; +}; + +int qwz_ce_intr(void *); +int qwz_ext_intr(void *); +int qwz_dp_service_srng(struct qwz_softc *, int); + +int qwz_init_hw_params(struct qwz_softc *); +int qwz_attach(struct qwz_softc *); +void qwz_detach(struct qwz_softc *); +int qwz_activate(struct device *, int); + +void qwz_core_deinit(struct qwz_softc *); +void qwz_ce_cleanup_pipes(struct qwz_softc *); + +int qwz_ioctl(struct ifnet *, u_long, caddr_t); +void qwz_start(struct ifnet *); +void qwz_watchdog(struct ifnet *); +int qwz_media_change(struct ifnet *); +void qwz_init_task(void *); +int qwz_newstate(struct ieee80211com *, enum ieee80211_state, int); +void qwz_newstate_task(void *); + +struct ath12k_peer { +#if 0 + struct list_head list; + struct ieee80211_sta *sta; +#endif + int vdev_id; +#if 0 + u8 addr[ETH_ALEN]; +#endif + int peer_id; + uint16_t ast_hash; + uint8_t pdev_id; + uint16_t hw_peer_id; +#if 0 + /* protected by ab->data_lock */ + struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; +#endif + struct dp_rx_tid rx_tid[IEEE80211_NUM_TID + 1]; +#if 0 + /* peer id based rhashtable list pointer */ + struct rhash_head rhash_id; + /* peer addr based rhashtable list pointer */ + struct rhash_head rhash_addr; + + /* Info used in MMIC verification of + * RX fragments + */ + struct crypto_shash *tfm_mmic; + u8 mcast_keyidx; + u8 ucast_keyidx; + u16 sec_type; + u16 sec_type_grp; + bool is_authorized; + bool dp_setup_done; +#endif +}; + +struct qwz_node { + struct ieee80211_node ni; + struct ath12k_peer peer; + unsigned int flags; +#define QWZ_NODE_FLAG_HAVE_PAIRWISE_KEY 0x01 +#define QWZ_NODE_FLAG_HAVE_GROUP_KEY 0x02 +}; + +struct ieee80211_node *qwz_node_alloc(struct ieee80211com *); +int qwz_set_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); +void qwz_delete_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); + +void qwz_qrtr_recv_msg(struct qwz_softc *, struct mbuf *); + +int qwz_hal_srng_init(struct qwz_softc *); + +int qwz_ce_alloc_pipes(struct qwz_softc *); +void qwz_ce_free_pipes(struct qwz_softc *); +void qwz_ce_rx_post_buf(struct qwz_softc *); +void qwz_ce_get_shadow_config(struct qwz_softc *, uint32_t **, uint32_t *); + +static inline unsigned int +qwz_roundup_pow_of_two(unsigned int i) +{ + return (powerof2(i) ? i : (1 << (fls(i) - 1))); +} + +static inline unsigned int +qwz_ce_get_attr_flags(struct qwz_softc *sc, int ce_id) +{ + KASSERT(ce_id < sc->hw_params.ce_count); + return sc->hw_params.host_ce_config[ce_id].flags; +} + +static inline enum ieee80211_edca_ac qwz_tid_to_ac(uint32_t tid) +{ + return (((tid == 0) || (tid == 3)) ? EDCA_AC_BE : + ((tid == 1) || (tid == 2)) ? EDCA_AC_BK : + ((tid == 4) || (tid == 5)) ? EDCA_AC_VI : + EDCA_AC_VO); +} diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index a0d7d4058..76cecece3 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.365 2024/04/09 14:58:41 mglocker Exp $ +# $OpenBSD: files.pci,v 1.366 2024/08/14 14:40:46 patrick Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -559,6 +559,10 @@ file dev/pci/if_iwx.c iwx attach qwx at pci with qwx_pci file dev/pci/if_qwx_pci.c qwx_pci +# Qualcomm 802.11be +attach qwz at pci with qwz_pci +file dev/pci/if_qwz_pci.c qwz_pci + # C-Media CMI8x38 Audio Chip device cmpci {}: audio attach cmpci at pci diff --git a/sys/dev/pci/if_qwz_pci.c b/sys/dev/pci/if_qwz_pci.c new file mode 100644 index 000000000..3a45b27fb --- /dev/null +++ b/sys/dev/pci/if_qwz_pci.c @@ -0,0 +1,4142 @@ +/* $OpenBSD: if_qwz_pci.c,v 1.1 2024/08/14 14:40:46 patrick Exp $ */ + +/* + * Copyright 2023 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. + * Copyright (c) 2018-2021 The Linux Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of [Owner Organization] nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "bpfilter.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +/* XXX linux porting goo */ +#ifdef __LP64__ +#define BITS_PER_LONG 64 +#else +#define BITS_PER_LONG 32 +#endif +#define GENMASK(h, l) (((~0UL) >> (BITS_PER_LONG - (h) - 1)) & ((~0UL) << (l))) +#define __bf_shf(x) (__builtin_ffsll(x) - 1) +#define FIELD_GET(_m, _v) ((typeof(_m))(((_v) & (_m)) >> __bf_shf(_m))) +#define BIT(x) (1UL << (x)) +#define test_bit(i, a) ((a) & (1 << (i))) +#define clear_bit(i, a) ((a)) &= ~(1 << (i)) +#define set_bit(i, a) ((a)) |= (1 << (i)) + +/* #define QWZ_DEBUG */ + +#include +#include + +#ifdef QWZ_DEBUG +/* Headers needed for RDDM dump */ +#include +#include +#include +#include +#include +#include +#endif + +#define ATH12K_PCI_IRQ_CE0_OFFSET 3 +#define ATH12K_PCI_IRQ_DP_OFFSET 14 + +#define ATH12K_PCI_CE_WAKE_IRQ 2 + +#define ATH12K_PCI_WINDOW_ENABLE_BIT 0x40000000 +#define ATH12K_PCI_WINDOW_REG_ADDRESS 0x310c +#define ATH12K_PCI_WINDOW_VALUE_MASK GENMASK(24, 19) +#define ATH12K_PCI_WINDOW_START 0x80000 +#define ATH12K_PCI_WINDOW_RANGE_MASK GENMASK(18, 0) + +/* BAR0 + 4k is always accessible, and no need to force wakeup. */ +#define ATH12K_PCI_ACCESS_ALWAYS_OFF 0xFE0 /* 4K - 32 = 0xFE0 */ + +#define TCSR_SOC_HW_VERSION 0x0224 +#define TCSR_SOC_HW_VERSION_MAJOR_MASK GENMASK(11, 8) +#define TCSR_SOC_HW_VERSION_MINOR_MASK GENMASK(7, 0) + +/* + * pci.h + */ +#define PCIE_SOC_GLOBAL_RESET 0x3008 +#define PCIE_SOC_GLOBAL_RESET_V 1 + +#define WLAON_WARM_SW_ENTRY 0x1f80504 +#define WLAON_SOC_RESET_CAUSE_REG 0x01f8060c + +#define PCIE_Q6_COOKIE_ADDR 0x01f80500 +#define PCIE_Q6_COOKIE_DATA 0xc0000000 + +/* register to wake the UMAC from power collapse */ +#define PCIE_SCRATCH_0_SOC_PCIE_REG 0x4040 + +/* register used for handshake mechanism to validate UMAC is awake */ +#define PCIE_SOC_WAKE_PCIE_LOCAL_REG 0x3004 + +#define PCIE_PCIE_PARF_LTSSM 0x1e081b0 +#define PARM_LTSSM_VALUE 0x111 + +#define GCC_GCC_PCIE_HOT_RST 0x1e402bc +#define GCC_GCC_PCIE_HOT_RST_VAL 0x10 + +#define PCIE_PCIE_INT_ALL_CLEAR 0x1e08228 +#define PCIE_SMLH_REQ_RST_LINK_DOWN 0x2 +#define PCIE_INT_CLEAR_ALL 0xffffffff + +#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG(sc) \ + (sc->hw_params.regs->pcie_qserdes_sysclk_en_sel) +#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_VAL 0x10 +#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_MSK 0xffffffff +#define PCIE_PCS_OSC_DTCT_CONFIG1_REG(sc) \ + (sc->hw_params.regs->pcie_pcs_osc_dtct_config_base) +#define PCIE_PCS_OSC_DTCT_CONFIG1_VAL 0x02 +#define PCIE_PCS_OSC_DTCT_CONFIG2_REG(sc) \ + (sc->hw_params.regs->pcie_pcs_osc_dtct_config_base + 0x4) +#define PCIE_PCS_OSC_DTCT_CONFIG2_VAL 0x52 +#define PCIE_PCS_OSC_DTCT_CONFIG4_REG(sc) \ + (sc->hw_params.regs->pcie_pcs_osc_dtct_config_base + 0xc) +#define PCIE_PCS_OSC_DTCT_CONFIG4_VAL 0xff +#define PCIE_PCS_OSC_DTCT_CONFIG_MSK 0x000000ff + +#define WLAON_QFPROM_PWR_CTRL_REG 0x01f8031c +#define QFPROM_PWR_CTRL_VDD4BLOW_MASK 0x4 + +/* + * mhi.h + */ +#define PCIE_TXVECDB 0x360 +#define PCIE_TXVECSTATUS 0x368 +#define PCIE_RXVECDB 0x394 +#define PCIE_RXVECSTATUS 0x39C + +#define MHI_CHAN_CTX_CHSTATE_MASK GENMASK(7, 0) +#define MHI_CHAN_CTX_CHSTATE_DISABLED 0 +#define MHI_CHAN_CTX_CHSTATE_ENABLED 1 +#define MHI_CHAN_CTX_CHSTATE_RUNNING 2 +#define MHI_CHAN_CTX_CHSTATE_SUSPENDED 3 +#define MHI_CHAN_CTX_CHSTATE_STOP 4 +#define MHI_CHAN_CTX_CHSTATE_ERROR 5 +#define MHI_CHAN_CTX_BRSTMODE_MASK GENMASK(9, 8) +#define MHI_CHAN_CTX_BRSTMODE_SHFT 8 +#define MHI_CHAN_CTX_BRSTMODE_DISABLE 2 +#define MHI_CHAN_CTX_BRSTMODE_ENABLE 3 +#define MHI_CHAN_CTX_POLLCFG_MASK GENMASK(15, 10) +#define MHI_CHAN_CTX_RESERVED_MASK GENMASK(31, 16) + +#define QWZ_MHI_CONFIG_QCA6390_MAX_CHANNELS 128 +#define QWZ_MHI_CONFIG_QCA6390_TIMEOUT_MS 2000 +#define QWZ_MHI_CONFIG_QCA9074_MAX_CHANNELS 30 + +#define MHI_CHAN_TYPE_INVALID 0 +#define MHI_CHAN_TYPE_OUTBOUND 1 /* to device */ +#define MHI_CHAN_TYPE_INBOUND 2 /* from device */ +#define MHI_CHAN_TYPE_INBOUND_COALESCED 3 + +#define MHI_EV_CTX_RESERVED_MASK GENMASK(7, 0) +#define MHI_EV_CTX_INTMODC_MASK GENMASK(15, 8) +#define MHI_EV_CTX_INTMODT_MASK GENMASK(31, 16) +#define MHI_EV_CTX_INTMODT_SHFT 16 + +#define MHI_ER_TYPE_INVALID 0 +#define MHI_ER_TYPE_VALID 1 + +#define MHI_ER_DATA 0 +#define MHI_ER_CTRL 1 + +#define MHI_CH_STATE_DISABLED 0 +#define MHI_CH_STATE_ENABLED 1 +#define MHI_CH_STATE_RUNNING 2 +#define MHI_CH_STATE_SUSPENDED 3 +#define MHI_CH_STATE_STOP 4 +#define MHI_CH_STATE_ERROR 5 + +#define QWZ_NUM_EVENT_CTX 2 + +/* Event context. Shared with device. */ +struct qwz_mhi_event_ctxt { + uint32_t intmod; + uint32_t ertype; + uint32_t msivec; + + uint64_t rbase; + uint64_t rlen; + uint64_t rp; + uint64_t wp; +} __packed; + +/* Channel context. Shared with device. */ +struct qwz_mhi_chan_ctxt { + uint32_t chcfg; + uint32_t chtype; + uint32_t erindex; + + uint64_t rbase; + uint64_t rlen; + uint64_t rp; + uint64_t wp; +} __packed; + +/* Command context. Shared with device. */ +struct qwz_mhi_cmd_ctxt { + uint32_t reserved0; + uint32_t reserved1; + uint32_t reserved2; + + uint64_t rbase; + uint64_t rlen; + uint64_t rp; + uint64_t wp; +} __packed; + +struct qwz_mhi_ring_element { + uint64_t ptr; + uint32_t dword[2]; +}; + +struct qwz_xfer_data { + bus_dmamap_t map; + struct mbuf *m; +}; + +#define QWZ_PCI_XFER_MAX_DATA_SIZE 0xffff +#define QWZ_PCI_XFER_RING_MAX_ELEMENTS 64 + +struct qwz_pci_xfer_ring { + struct qwz_dmamem *dmamem; + bus_size_t size; + uint32_t mhi_chan_id; + uint32_t mhi_chan_state; + uint32_t mhi_chan_direction; + uint32_t mhi_chan_event_ring_index; + uint32_t db_addr; + uint32_t cmd_status; + int num_elements; + int queued; + struct qwz_xfer_data data[QWZ_PCI_XFER_RING_MAX_ELEMENTS]; + uint64_t rp; + uint64_t wp; + struct qwz_mhi_chan_ctxt *chan_ctxt; +}; + + +#define QWZ_PCI_EVENT_RING_MAX_ELEMENTS 256 + +struct qwz_pci_event_ring { + struct qwz_dmamem *dmamem; + bus_size_t size; + uint32_t mhi_er_type; + uint32_t mhi_er_irq; + uint32_t mhi_er_irq_moderation_ms; + uint32_t db_addr; + int num_elements; + uint64_t rp; + uint64_t wp; + struct qwz_mhi_event_ctxt *event_ctxt; +}; + +struct qwz_cmd_data { + bus_dmamap_t map; + struct mbuf *m; +}; + +#define QWZ_PCI_CMD_RING_MAX_ELEMENTS 128 + +struct qwz_pci_cmd_ring { + struct qwz_dmamem *dmamem; + bus_size_t size; + uint64_t rp; + uint64_t wp; + int num_elements; + int queued; +}; + +struct qwz_pci_ops; +struct qwz_msi_config; + +#define QWZ_NUM_MSI_VEC 32 + +struct qwz_pci_softc { + struct qwz_softc sc_sc; + pci_chipset_tag_t sc_pc; + pcitag_t sc_tag; + int sc_cap_off; + int sc_msi_off; + pcireg_t sc_msi_cap; + void *sc_ih[QWZ_NUM_MSI_VEC]; + char sc_ivname[QWZ_NUM_MSI_VEC][16]; + struct qwz_ext_irq_grp ext_irq_grp[ATH12K_EXT_IRQ_GRP_NUM_MAX]; + int mhi_irq[2]; + bus_space_tag_t sc_st; + bus_space_handle_t sc_sh; + bus_addr_t sc_map; + bus_size_t sc_mapsize; + + pcireg_t sc_lcsr; + uint32_t sc_flags; +#define ATH12K_PCI_ASPM_RESTORE 1 + + uint32_t register_window; + const struct qwz_pci_ops *sc_pci_ops; + + uint32_t bhi_off; + uint32_t bhi_ee; + uint32_t bhie_off; + uint32_t mhi_state; + uint32_t max_chan; + + uint64_t wake_db; + + /* + * DMA memory for AMSS.bin firmware image. + * This memory must remain available to the device until + * the device is powered down. + */ + struct qwz_dmamem *amss_data; + struct qwz_dmamem *amss_vec; + + struct qwz_dmamem *rddm_vec; + struct qwz_dmamem *rddm_data; + int rddm_triggered; + struct task rddm_task; +#define QWZ_RDDM_DUMP_SIZE 0x420000 + + struct qwz_dmamem *chan_ctxt; + struct qwz_dmamem *event_ctxt; + struct qwz_dmamem *cmd_ctxt; + + + struct qwz_pci_xfer_ring xfer_rings[4]; +#define QWZ_PCI_XFER_RING_LOOPBACK_OUTBOUND 0 +#define QWZ_PCI_XFER_RING_LOOPBACK_INBOUND 1 +#define QWZ_PCI_XFER_RING_IPCR_OUTBOUND 2 +#define QWZ_PCI_XFER_RING_IPCR_INBOUND 3 + struct qwz_pci_event_ring event_rings[QWZ_NUM_EVENT_CTX]; + struct qwz_pci_cmd_ring cmd_ring; +}; + +int qwz_pci_match(struct device *, void *, void *); +void qwz_pci_attach(struct device *, struct device *, void *); +int qwz_pci_detach(struct device *, int); +void qwz_pci_attach_hook(struct device *); +void qwz_pci_free_xfer_rings(struct qwz_pci_softc *); +int qwz_pci_alloc_xfer_ring(struct qwz_softc *, struct qwz_pci_xfer_ring *, + uint32_t, uint32_t, uint32_t, size_t); +int qwz_pci_alloc_xfer_rings_qca6390(struct qwz_pci_softc *); +int qwz_pci_alloc_xfer_rings_qcn9074(struct qwz_pci_softc *); +void qwz_pci_free_event_rings(struct qwz_pci_softc *); +int qwz_pci_alloc_event_ring(struct qwz_softc *, + struct qwz_pci_event_ring *, uint32_t, uint32_t, uint32_t, size_t); +int qwz_pci_alloc_event_rings(struct qwz_pci_softc *); +void qwz_pci_free_cmd_ring(struct qwz_pci_softc *); +int qwz_pci_init_cmd_ring(struct qwz_softc *, struct qwz_pci_cmd_ring *); +uint32_t qwz_pci_read(struct qwz_softc *, uint32_t); +void qwz_pci_write(struct qwz_softc *, uint32_t, uint32_t); + +void qwz_pci_read_hw_version(struct qwz_softc *, uint32_t *, uint32_t *); +uint32_t qwz_pcic_read32(struct qwz_softc *, uint32_t); +void qwz_pcic_write32(struct qwz_softc *, uint32_t, uint32_t); + +void qwz_pcic_ext_irq_enable(struct qwz_softc *); +void qwz_pcic_ext_irq_disable(struct qwz_softc *); +int qwz_pcic_config_irq(struct qwz_softc *, struct pci_attach_args *); + +int qwz_pci_start(struct qwz_softc *); +void qwz_pci_stop(struct qwz_softc *); +void qwz_pci_aspm_disable(struct qwz_softc *); +void qwz_pci_aspm_restore(struct qwz_softc *); +int qwz_pci_power_up(struct qwz_softc *); +void qwz_pci_power_down(struct qwz_softc *); + +int qwz_pci_bus_wake_up(struct qwz_softc *); +void qwz_pci_bus_release(struct qwz_softc *); +void qwz_pci_window_write32(struct qwz_softc *, uint32_t, uint32_t); +uint32_t qwz_pci_window_read32(struct qwz_softc *, uint32_t); + +int qwz_mhi_register(struct qwz_softc *); +void qwz_mhi_unregister(struct qwz_softc *); +void qwz_mhi_ring_doorbell(struct qwz_softc *sc, uint64_t, uint64_t); +void qwz_mhi_device_wake(struct qwz_softc *); +void qwz_mhi_device_zzz(struct qwz_softc *); +int qwz_mhi_wake_db_clear_valid(struct qwz_softc *); +void qwz_mhi_init_xfer_rings(struct qwz_pci_softc *); +void qwz_mhi_init_event_rings(struct qwz_pci_softc *); +void qwz_mhi_init_cmd_ring(struct qwz_pci_softc *); +void qwz_mhi_init_dev_ctxt(struct qwz_pci_softc *); +int qwz_mhi_send_cmd(struct qwz_pci_softc *psc, uint32_t, uint32_t); +void * qwz_pci_xfer_ring_get_elem(struct qwz_pci_xfer_ring *, uint64_t); +struct qwz_xfer_data *qwz_pci_xfer_ring_get_data(struct qwz_pci_xfer_ring *, + uint64_t); +int qwz_mhi_submit_xfer(struct qwz_softc *sc, struct mbuf *m); +int qwz_mhi_start_channel(struct qwz_pci_softc *, + struct qwz_pci_xfer_ring *); +int qwz_mhi_start_channels(struct qwz_pci_softc *); +int qwz_mhi_start(struct qwz_pci_softc *); +void qwz_mhi_stop(struct qwz_softc *); +int qwz_mhi_reset_device(struct qwz_softc *, int); +void qwz_mhi_clear_vector(struct qwz_softc *); +int qwz_mhi_fw_load_handler(struct qwz_pci_softc *); +int qwz_mhi_await_device_reset(struct qwz_softc *); +int qwz_mhi_await_device_ready(struct qwz_softc *); +void qwz_mhi_ready_state_transition(struct qwz_pci_softc *); +void qwz_mhi_mission_mode_state_transition(struct qwz_pci_softc *); +void qwz_mhi_low_power_mode_state_transition(struct qwz_pci_softc *); +void qwz_mhi_set_state(struct qwz_softc *, uint32_t); +void qwz_mhi_init_mmio(struct qwz_pci_softc *); +int qwz_mhi_fw_load_bhi(struct qwz_pci_softc *, uint8_t *, size_t); +int qwz_mhi_fw_load_bhie(struct qwz_pci_softc *, uint8_t *, size_t); +void qwz_rddm_prepare(struct qwz_pci_softc *); +#ifdef QWZ_DEBUG +void qwz_rddm_task(void *); +#endif +void * qwz_pci_event_ring_get_elem(struct qwz_pci_event_ring *, uint64_t); +void qwz_pci_intr_ctrl_event_mhi(struct qwz_pci_softc *, uint32_t); +void qwz_pci_intr_ctrl_event_ee(struct qwz_pci_softc *, uint32_t); +void qwz_pci_intr_ctrl_event_cmd_complete(struct qwz_pci_softc *, + uint64_t, uint32_t); +int qwz_pci_intr_ctrl_event(struct qwz_pci_softc *, + struct qwz_pci_event_ring *); +void qwz_pci_intr_data_event_tx(struct qwz_pci_softc *, + struct qwz_mhi_ring_element *); +int qwz_pci_intr_data_event(struct qwz_pci_softc *, + struct qwz_pci_event_ring *); +int qwz_pci_intr_mhi_ctrl(void *); +int qwz_pci_intr_mhi_data(void *); +int qwz_pci_intr(void *); + +struct qwz_pci_ops { + int (*wakeup)(struct qwz_softc *); + void (*release)(struct qwz_softc *); + int (*get_msi_irq)(struct qwz_softc *, unsigned int); + void (*window_write32)(struct qwz_softc *, uint32_t, uint32_t); + uint32_t (*window_read32)(struct qwz_softc *, uint32_t); + int (*alloc_xfer_rings)(struct qwz_pci_softc *); +}; + + +static const struct qwz_pci_ops qwz_pci_ops_qca6390 = { + .wakeup = qwz_pci_bus_wake_up, + .release = qwz_pci_bus_release, +#if notyet + .get_msi_irq = qwz_pci_get_msi_irq, +#endif + .window_write32 = qwz_pci_window_write32, + .window_read32 = qwz_pci_window_read32, + .alloc_xfer_rings = qwz_pci_alloc_xfer_rings_qca6390, +}; + +static const struct qwz_pci_ops qwz_pci_ops_qcn9074 = { + .wakeup = NULL, + .release = NULL, +#if notyet + .get_msi_irq = qwz_pci_get_msi_irq, +#endif + .window_write32 = qwz_pci_window_write32, + .window_read32 = qwz_pci_window_read32, + .alloc_xfer_rings = qwz_pci_alloc_xfer_rings_qcn9074, +}; + +const struct cfattach qwz_pci_ca = { + sizeof(struct qwz_pci_softc), + qwz_pci_match, + qwz_pci_attach, + qwz_pci_detach, + qwz_activate +}; + +/* XXX pcidev */ +#define PCI_PRODUCT_QUALCOMM_QCA6390 0x1101 +#define PCI_PRODUCT_QUALCOMM_QCN9074 0x1104 + +static const struct pci_matchid qwz_pci_devices[] = { +#if notyet + { PCI_VENDOR_QUALCOMM, PCI_PRODUCT_QUALCOMM_QCA6390 }, + { PCI_VENDOR_QUALCOMM, PCI_PRODUCT_QUALCOMM_QCN9074 }, +#endif + { PCI_VENDOR_QUALCOMM, PCI_PRODUCT_QUALCOMM_QCNFA765 } +}; + +int +qwz_pci_match(struct device *parent, void *match, void *aux) +{ + return pci_matchbyid(aux, qwz_pci_devices, nitems(qwz_pci_devices)); +} + +void +qwz_pci_init_qmi_ce_config(struct qwz_softc *sc) +{ + struct qwz_qmi_ce_cfg *cfg = &sc->qmi_ce_cfg; + + qwz_ce_get_shadow_config(sc, &cfg->shadow_reg_v2, + &cfg->shadow_reg_v2_len); +} + +const struct qwz_msi_config qwz_msi_config_one_msi = { + .total_vectors = 1, + .total_users = 4, + .users = (struct qwz_msi_user[]) { + { .name = "MHI", .num_vectors = 1, .base_vector = 0 }, + { .name = "CE", .num_vectors = 1, .base_vector = 0 }, + { .name = "WAKE", .num_vectors = 1, .base_vector = 0 }, + { .name = "DP", .num_vectors = 1, .base_vector = 0 }, + }, +}; + +const struct qwz_msi_config qwz_msi_config[] = { + { + .total_vectors = 32, + .total_users = 4, + .users = (struct qwz_msi_user[]) { + { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, + { .name = "CE", .num_vectors = 10, .base_vector = 3 }, + { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, + { .name = "DP", .num_vectors = 18, .base_vector = 14 }, + }, + .hw_rev = ATH12K_HW_QCA6390_HW20, + }, + { + .total_vectors = 16, + .total_users = 3, + .users = (struct qwz_msi_user[]) { + { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, + { .name = "CE", .num_vectors = 5, .base_vector = 3 }, + { .name = "DP", .num_vectors = 8, .base_vector = 8 }, + }, + .hw_rev = ATH12K_HW_QCN9074_HW10, + }, + { + .total_vectors = 32, + .total_users = 4, + .users = (struct qwz_msi_user[]) { + { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, + { .name = "CE", .num_vectors = 10, .base_vector = 3 }, + { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, + { .name = "DP", .num_vectors = 18, .base_vector = 14 }, + }, + .hw_rev = ATH12K_HW_WCN6855_HW20, + }, + { + .total_vectors = 32, + .total_users = 4, + .users = (struct qwz_msi_user[]) { + { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, + { .name = "CE", .num_vectors = 10, .base_vector = 3 }, + { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, + { .name = "DP", .num_vectors = 18, .base_vector = 14 }, + }, + .hw_rev = ATH12K_HW_WCN6855_HW21, + }, + { + .total_vectors = 28, + .total_users = 2, + .users = (struct qwz_msi_user[]) { + { .name = "CE", .num_vectors = 10, .base_vector = 0 }, + { .name = "DP", .num_vectors = 18, .base_vector = 10 }, + }, + .hw_rev = ATH12K_HW_WCN6750_HW10, + }, +}; + +int +qwz_pcic_init_msi_config(struct qwz_softc *sc) +{ + const struct qwz_msi_config *msi_config; + int i; + + if (!test_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags)) { + sc->msi_cfg = &qwz_msi_config_one_msi; + return 0; + } + for (i = 0; i < nitems(qwz_msi_config); i++) { + msi_config = &qwz_msi_config[i]; + + if (msi_config->hw_rev == sc->sc_hw_rev) + break; + } + + if (i == nitems(qwz_msi_config)) { + printf("%s: failed to fetch msi config, " + "unsupported hw version: 0x%x\n", + sc->sc_dev.dv_xname, sc->sc_hw_rev); + return EINVAL; + } + + sc->msi_cfg = msi_config; + return 0; +} + +int +qwz_pci_alloc_msi(struct qwz_softc *sc) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + uint64_t addr; + pcireg_t data; + + if (psc->sc_msi_cap & PCI_MSI_MC_C64) { + uint64_t addr_hi; + pcireg_t addr_lo; + + addr_lo = pci_conf_read(psc->sc_pc, psc->sc_tag, + psc->sc_msi_off + PCI_MSI_MA); + addr_hi = pci_conf_read(psc->sc_pc, psc->sc_tag, + psc->sc_msi_off + PCI_MSI_MAU32); + addr = addr_hi << 32 | addr_lo; + data = pci_conf_read(psc->sc_pc, psc->sc_tag, + psc->sc_msi_off + PCI_MSI_MD64); + } else { + addr = pci_conf_read(psc->sc_pc, psc->sc_tag, + psc->sc_msi_off + PCI_MSI_MA); + data = pci_conf_read(psc->sc_pc, psc->sc_tag, + psc->sc_msi_off + PCI_MSI_MD32); + } + + sc->msi_addr_lo = addr & 0xffffffff; + sc->msi_addr_hi = ((uint64_t)addr) >> 32; + sc->msi_data_start = data; + + DPRINTF("%s: MSI addr: 0x%llx MSI data: 0x%x\n", sc->sc_dev.dv_xname, + addr, data); + + return 0; +} + +int +qwz_pcic_map_service_to_pipe(struct qwz_softc *sc, uint16_t service_id, + uint8_t *ul_pipe, uint8_t *dl_pipe) +{ + const struct service_to_pipe *entry; + int ul_set = 0, dl_set = 0; + int i; + + for (i = 0; i < sc->hw_params.svc_to_ce_map_len; i++) { + entry = &sc->hw_params.svc_to_ce_map[i]; + + if (le32toh(entry->service_id) != service_id) + continue; + + switch (le32toh(entry->pipedir)) { + case PIPEDIR_NONE: + break; + case PIPEDIR_IN: + *dl_pipe = le32toh(entry->pipenum); + dl_set = 1; + break; + case PIPEDIR_OUT: + *ul_pipe = le32toh(entry->pipenum); + ul_set = 1; + break; + case PIPEDIR_INOUT: + *dl_pipe = le32toh(entry->pipenum); + *ul_pipe = le32toh(entry->pipenum); + dl_set = 1; + ul_set = 1; + break; + } + } + + if (!ul_set || !dl_set) { + DPRINTF("%s: found no uplink and no downlink\n", __func__); + return ENOENT; + } + + return 0; +} + +int +qwz_pcic_get_user_msi_vector(struct qwz_softc *sc, char *user_name, + int *num_vectors, uint32_t *user_base_data, uint32_t *base_vector) +{ + const struct qwz_msi_config *msi_config = sc->msi_cfg; + int idx; + + for (idx = 0; idx < msi_config->total_users; idx++) { + if (strcmp(user_name, msi_config->users[idx].name) == 0) { + *num_vectors = msi_config->users[idx].num_vectors; + *base_vector = msi_config->users[idx].base_vector; + *user_base_data = *base_vector + sc->msi_data_start; + + DPRINTF("%s: MSI assignment %s num_vectors %d " + "user_base_data %u base_vector %u\n", __func__, + user_name, *num_vectors, *user_base_data, + *base_vector); + return 0; + } + } + + DPRINTF("%s: Failed to find MSI assignment for %s\n", + sc->sc_dev.dv_xname, user_name); + + return EINVAL; +} + +void +qwz_pci_attach(struct device *parent, struct device *self, void *aux) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)self; + struct qwz_softc *sc = &psc->sc_sc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + uint32_t soc_hw_version_major, soc_hw_version_minor; + const struct qwz_pci_ops *pci_ops; + struct pci_attach_args *pa = aux; + pci_intr_handle_t ih; + pcireg_t memtype, reg; + const char *intrstr; + int error; + pcireg_t sreg; + + sc->sc_dmat = pa->pa_dmat; + psc->sc_pc = pa->pa_pc; + psc->sc_tag = pa->pa_tag; + +#ifdef __HAVE_FDT + sc->sc_node = PCITAG_NODE(pa->pa_tag); +#endif + + rw_init(&sc->ioctl_rwl, "qwzioctl"); + + sreg = pci_conf_read(psc->sc_pc, psc->sc_tag, PCI_SUBSYS_ID_REG); + sc->id.bdf_search = ATH12K_BDF_SEARCH_DEFAULT; + sc->id.vendor = PCI_VENDOR(pa->pa_id); + sc->id.device = PCI_PRODUCT(pa->pa_id); + sc->id.subsystem_vendor = PCI_VENDOR(sreg); + sc->id.subsystem_device = PCI_PRODUCT(sreg); + + strlcpy(sc->sc_bus_str, "pci", sizeof(sc->sc_bus_str)); + + sc->ops.read32 = qwz_pcic_read32; + sc->ops.write32 = qwz_pcic_write32; + sc->ops.start = qwz_pci_start; + sc->ops.stop = qwz_pci_stop; + sc->ops.power_up = qwz_pci_power_up; + sc->ops.power_down = qwz_pci_power_down; + sc->ops.submit_xfer = qwz_mhi_submit_xfer; + sc->ops.irq_enable = qwz_pcic_ext_irq_enable; + sc->ops.irq_disable = qwz_pcic_ext_irq_disable; + sc->ops.map_service_to_pipe = qwz_pcic_map_service_to_pipe; + sc->ops.get_user_msi_vector = qwz_pcic_get_user_msi_vector; + + if (pci_get_capability(psc->sc_pc, psc->sc_tag, PCI_CAP_PCIEXPRESS, + &psc->sc_cap_off, NULL) == 0) { + printf(": can't find PCIe capability structure\n"); + return; + } + + if (pci_get_capability(psc->sc_pc, psc->sc_tag, PCI_CAP_MSI, + &psc->sc_msi_off, &psc->sc_msi_cap) == 0) { + printf(": can't find MSI capability structure\n"); + return; + } + + reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + reg |= PCI_COMMAND_MASTER_ENABLE; + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, reg); + + memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START); + if (pci_mapreg_map(pa, PCI_MAPREG_START, memtype, 0, + &psc->sc_st, &psc->sc_sh, &psc->sc_map, &psc->sc_mapsize, 0)) { + printf(": can't map mem space\n"); + return; + } + + sc->mem = psc->sc_map; + + sc->num_msivec = 32; + if (pci_intr_enable_msivec(pa, sc->num_msivec) != 0) { + sc->num_msivec = 1; + if (pci_intr_map_msi(pa, &ih) != 0) { + printf(": can't map interrupt\n"); + return; + } + clear_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags); + } else { + if (pci_intr_map_msivec(pa, 0, &ih) != 0 && + pci_intr_map_msi(pa, &ih) != 0) { + printf(": can't map interrupt\n"); + return; + } + set_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags); + psc->mhi_irq[MHI_ER_CTRL] = 1; + psc->mhi_irq[MHI_ER_DATA] = 2; + } + + intrstr = pci_intr_string(psc->sc_pc, ih); + snprintf(psc->sc_ivname[0], sizeof(psc->sc_ivname[0]), "%s:bhi", + sc->sc_dev.dv_xname); + psc->sc_ih[0] = pci_intr_establish(psc->sc_pc, ih, IPL_NET, + qwz_pci_intr, psc, psc->sc_ivname[0]); + if (psc->sc_ih[0] == NULL) { + printf(": can't establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + printf(": %s\n", intrstr); + + if (test_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags)) { + int msivec; + + msivec = psc->mhi_irq[MHI_ER_CTRL]; + if (pci_intr_map_msivec(pa, msivec, &ih) != 0 && + pci_intr_map_msi(pa, &ih) != 0) { + printf(": can't map interrupt\n"); + return; + } + snprintf(psc->sc_ivname[msivec], + sizeof(psc->sc_ivname[msivec]), + "%s:mhic", sc->sc_dev.dv_xname); + psc->sc_ih[msivec] = pci_intr_establish(psc->sc_pc, ih, + IPL_NET, qwz_pci_intr_mhi_ctrl, psc, + psc->sc_ivname[msivec]); + if (psc->sc_ih[msivec] == NULL) { + printf("%s: can't establish interrupt\n", + sc->sc_dev.dv_xname); + return; + } + + msivec = psc->mhi_irq[MHI_ER_DATA]; + if (pci_intr_map_msivec(pa, msivec, &ih) != 0 && + pci_intr_map_msi(pa, &ih) != 0) { + printf(": can't map interrupt\n"); + return; + } + snprintf(psc->sc_ivname[msivec], + sizeof(psc->sc_ivname[msivec]), + "%s:mhid", sc->sc_dev.dv_xname); + psc->sc_ih[msivec] = pci_intr_establish(psc->sc_pc, ih, + IPL_NET, qwz_pci_intr_mhi_data, psc, + psc->sc_ivname[msivec]); + if (psc->sc_ih[msivec] == NULL) { + printf("%s: can't establish interrupt\n", + sc->sc_dev.dv_xname); + return; + } + } + + pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D0); + + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_QUALCOMM_QCA6390: + qwz_pci_read_hw_version(sc, &soc_hw_version_major, + &soc_hw_version_minor); + switch (soc_hw_version_major) { + case 2: + sc->sc_hw_rev = ATH12K_HW_QCA6390_HW20; + break; + default: + printf(": unsupported QCA6390 SOC version: %d %d\n", + soc_hw_version_major, soc_hw_version_minor); + return; + } + + pci_ops = &qwz_pci_ops_qca6390; + psc->max_chan = QWZ_MHI_CONFIG_QCA6390_MAX_CHANNELS; + break; + case PCI_PRODUCT_QUALCOMM_QCN9074: + pci_ops = &qwz_pci_ops_qcn9074; + sc->sc_hw_rev = ATH12K_HW_QCN9074_HW10; + psc->max_chan = QWZ_MHI_CONFIG_QCA9074_MAX_CHANNELS; + break; + case PCI_PRODUCT_QUALCOMM_QCNFA765: + sc->id.bdf_search = ATH12K_BDF_SEARCH_BUS_AND_BOARD; + qwz_pci_read_hw_version(sc, &soc_hw_version_major, + &soc_hw_version_minor); + switch (soc_hw_version_major) { + case 2: + switch (soc_hw_version_minor) { + case 0x00: + case 0x01: + sc->sc_hw_rev = ATH12K_HW_WCN6855_HW20; + break; + case 0x10: + case 0x11: + sc->sc_hw_rev = ATH12K_HW_WCN6855_HW21; + break; + default: + goto unsupported_wcn6855_soc; + } + break; + default: +unsupported_wcn6855_soc: + printf(": unsupported WCN6855 SOC version: %d %d\n", + soc_hw_version_major, soc_hw_version_minor); + return; + } + + pci_ops = &qwz_pci_ops_qca6390; + psc->max_chan = QWZ_MHI_CONFIG_QCA6390_MAX_CHANNELS; + break; + default: + printf(": unsupported chip\n"); + return; + } + + /* register PCI ops */ + psc->sc_pci_ops = pci_ops; + + error = qwz_pcic_init_msi_config(sc); + if (error) + goto err_pci_free_region; + + error = qwz_pci_alloc_msi(sc); + if (error) { + printf("%s: failed to enable msi: %d\n", sc->sc_dev.dv_xname, + error); + goto err_pci_free_region; + } + + error = qwz_init_hw_params(sc); + if (error) + goto err_pci_disable_msi; + + psc->chan_ctxt = qwz_dmamem_alloc(sc->sc_dmat, + sizeof(struct qwz_mhi_chan_ctxt) * psc->max_chan, 0); + if (psc->chan_ctxt == NULL) { + printf("%s: could not allocate channel context array\n", + sc->sc_dev.dv_xname); + goto err_pci_disable_msi; + } + + if (psc->sc_pci_ops->alloc_xfer_rings(psc)) { + printf("%s: could not allocate transfer rings\n", + sc->sc_dev.dv_xname); + goto err_pci_free_chan_ctxt; + } + + psc->event_ctxt = qwz_dmamem_alloc(sc->sc_dmat, + sizeof(struct qwz_mhi_event_ctxt) * QWZ_NUM_EVENT_CTX, 0); + if (psc->event_ctxt == NULL) { + printf("%s: could not allocate event context array\n", + sc->sc_dev.dv_xname); + goto err_pci_free_xfer_rings; + } + + if (qwz_pci_alloc_event_rings(psc)) { + printf("%s: could not allocate event rings\n", + sc->sc_dev.dv_xname); + goto err_pci_free_event_ctxt; + } + + psc->cmd_ctxt = qwz_dmamem_alloc(sc->sc_dmat, + sizeof(struct qwz_mhi_cmd_ctxt), 0); + if (psc->cmd_ctxt == NULL) { + printf("%s: could not allocate command context array\n", + sc->sc_dev.dv_xname); + goto err_pci_free_event_rings; + } + + if (qwz_pci_init_cmd_ring(sc, &psc->cmd_ring)) { + printf("%s: could not allocate command ring\n", + sc->sc_dev.dv_xname); + goto err_pci_free_cmd_ctxt; + } + + error = qwz_mhi_register(sc); + if (error) { + printf(": failed to register mhi: %d\n", error); + goto err_pci_free_cmd_ring; + } + + error = qwz_hal_srng_init(sc); + if (error) + goto err_mhi_unregister; + + error = qwz_ce_alloc_pipes(sc); + if (error) { + printf(": failed to allocate ce pipes: %d\n", error); + goto err_hal_srng_deinit; + } + + sc->sc_nswq = taskq_create("qwzns", 1, IPL_NET, 0); + if (sc->sc_nswq == NULL) + goto err_ce_free; + + qwz_pci_init_qmi_ce_config(sc); + + error = qwz_pcic_config_irq(sc, pa); + if (error) { + printf("%s: failed to config irq: %d\n", + sc->sc_dev.dv_xname, error); + goto err_ce_free; + } +#if notyet + ret = ath12k_pci_set_irq_affinity_hint(ab_pci, cpumask_of(0)); + if (ret) { + ath12k_err(ab, "failed to set irq affinity %d\n", ret); + goto err_free_irq; + } + + /* kernel may allocate a dummy vector before request_irq and + * then allocate a real vector when request_irq is called. + * So get msi_data here again to avoid spurious interrupt + * as msi_data will configured to srngs. + */ + ret = ath12k_pci_config_msi_data(ab_pci); + if (ret) { + ath12k_err(ab, "failed to config msi_data: %d\n", ret); + goto err_irq_affinity_cleanup; + } +#endif +#ifdef QWZ_DEBUG + task_set(&psc->rddm_task, qwz_rddm_task, psc); +#endif + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + ic->ic_state = IEEE80211_S_INIT; + + /* Set device capabilities. */ + ic->ic_caps = +#if 0 + IEEE80211_C_QOS | IEEE80211_C_TX_AMPDU | /* A-MPDU */ +#endif + IEEE80211_C_ADDBA_OFFLOAD | /* device sends ADDBA/DELBA frames */ + IEEE80211_C_WEP | /* WEP */ + IEEE80211_C_RSN | /* WPA/RSN */ + IEEE80211_C_SCANALL | /* device scans all channels at once */ + IEEE80211_C_SCANALLBAND | /* device scans all bands at once */ +#if 0 + IEEE80211_C_MONITOR | /* monitor mode supported */ +#endif + IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_SHPREAMBLE; /* short preamble supported */ + + ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a; + ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; + ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g; + + /* IBSS channel undefined for now. */ + ic->ic_ibss_chan = &ic->ic_channels[1]; + + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = qwz_ioctl; + ifp->if_start = qwz_start; + ifp->if_watchdog = qwz_watchdog; + memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ); + if_attach(ifp); + ieee80211_ifattach(ifp); + ieee80211_media_init(ifp, qwz_media_change, ieee80211_media_status); + + ic->ic_node_alloc = qwz_node_alloc; + + /* Override 802.11 state transition machine. */ + sc->sc_newstate = ic->ic_newstate; + ic->ic_newstate = qwz_newstate; + ic->ic_set_key = qwz_set_key; + ic->ic_delete_key = qwz_delete_key; +#if 0 + ic->ic_updatechan = qwz_updatechan; + ic->ic_updateprot = qwz_updateprot; + ic->ic_updateslot = qwz_updateslot; + ic->ic_updateedca = qwz_updateedca; + ic->ic_updatedtim = qwz_updatedtim; +#endif + /* + * We cannot read the MAC address without loading the + * firmware from disk. Postpone until mountroot is done. + */ + config_mountroot(self, qwz_pci_attach_hook); + return; + +err_ce_free: + qwz_ce_free_pipes(sc); +err_hal_srng_deinit: +err_mhi_unregister: +err_pci_free_cmd_ring: + qwz_pci_free_cmd_ring(psc); +err_pci_free_cmd_ctxt: + qwz_dmamem_free(sc->sc_dmat, psc->cmd_ctxt); + psc->cmd_ctxt = NULL; +err_pci_free_event_rings: + qwz_pci_free_event_rings(psc); +err_pci_free_event_ctxt: + qwz_dmamem_free(sc->sc_dmat, psc->event_ctxt); + psc->event_ctxt = NULL; +err_pci_free_xfer_rings: + qwz_pci_free_xfer_rings(psc); +err_pci_free_chan_ctxt: + qwz_dmamem_free(sc->sc_dmat, psc->chan_ctxt); + psc->chan_ctxt = NULL; +err_pci_disable_msi: +err_pci_free_region: + pci_intr_disestablish(psc->sc_pc, psc->sc_ih[0]); + return; +} + +int +qwz_pci_detach(struct device *self, int flags) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)self; + struct qwz_softc *sc = &psc->sc_sc; + + if (psc->sc_ih[0]) { + pci_intr_disestablish(psc->sc_pc, psc->sc_ih[0]); + psc->sc_ih[0] = NULL; + } + + qwz_detach(sc); + + qwz_pci_free_event_rings(psc); + qwz_pci_free_xfer_rings(psc); + qwz_pci_free_cmd_ring(psc); + + if (psc->event_ctxt) { + qwz_dmamem_free(sc->sc_dmat, psc->event_ctxt); + psc->event_ctxt = NULL; + } + if (psc->chan_ctxt) { + qwz_dmamem_free(sc->sc_dmat, psc->chan_ctxt); + psc->chan_ctxt = NULL; + } + if (psc->cmd_ctxt) { + qwz_dmamem_free(sc->sc_dmat, psc->cmd_ctxt); + psc->cmd_ctxt = NULL; + } + + if (psc->amss_data) { + qwz_dmamem_free(sc->sc_dmat, psc->amss_data); + psc->amss_data = NULL; + } + if (psc->amss_vec) { + qwz_dmamem_free(sc->sc_dmat, psc->amss_vec); + psc->amss_vec = NULL; + } + + return 0; +} + +void +qwz_pci_attach_hook(struct device *self) +{ + struct qwz_softc *sc = (void *)self; + int s = splnet(); + + qwz_attach(sc); + + splx(s); +} + +void +qwz_pci_free_xfer_rings(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + int i; + + for (i = 0; i < nitems(psc->xfer_rings); i++) { + struct qwz_pci_xfer_ring *ring = &psc->xfer_rings[i]; + if (ring->dmamem) { + qwz_dmamem_free(sc->sc_dmat, ring->dmamem); + ring->dmamem = NULL; + } + memset(ring, 0, sizeof(*ring)); + } +} + +int +qwz_pci_alloc_xfer_ring(struct qwz_softc *sc, struct qwz_pci_xfer_ring *ring, + uint32_t id, uint32_t direction, uint32_t event_ring_index, + size_t num_elements) +{ + bus_size_t size; + int i, err; + + memset(ring, 0, sizeof(*ring)); + + size = sizeof(struct qwz_mhi_ring_element) * num_elements; + /* Hardware requires that rings are aligned to ring size. */ + ring->dmamem = qwz_dmamem_alloc(sc->sc_dmat, size, size); + if (ring->dmamem == NULL) + return ENOMEM; + + ring->size = size; + ring->mhi_chan_id = id; + ring->mhi_chan_state = MHI_CH_STATE_DISABLED; + ring->mhi_chan_direction = direction; + ring->mhi_chan_event_ring_index = event_ring_index; + ring->num_elements = num_elements; + + memset(ring->data, 0, sizeof(ring->data)); + for (i = 0; i < ring->num_elements; i++) { + struct qwz_xfer_data *xfer = &ring->data[i]; + + err = bus_dmamap_create(sc->sc_dmat, QWZ_PCI_XFER_MAX_DATA_SIZE, + 1, QWZ_PCI_XFER_MAX_DATA_SIZE, 0, BUS_DMA_NOWAIT, + &xfer->map); + if (err) { + printf("%s: could not create xfer DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + if (direction == MHI_CHAN_TYPE_INBOUND) { + struct mbuf *m; + + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) { + err = ENOBUFS; + goto fail; + } + + MCLGETL(m, M_DONTWAIT, QWZ_PCI_XFER_MAX_DATA_SIZE); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + err = ENOBUFS; + goto fail; + } + + m->m_len = m->m_pkthdr.len = QWZ_PCI_XFER_MAX_DATA_SIZE; + err = bus_dmamap_load_mbuf(sc->sc_dmat, xfer->map, + m, BUS_DMA_READ | BUS_DMA_NOWAIT); + if (err) { + printf("%s: can't map mbuf (error %d)\n", + sc->sc_dev.dv_xname, err); + m_freem(m); + goto fail; + } + + bus_dmamap_sync(sc->sc_dmat, xfer->map, 0, + QWZ_PCI_XFER_MAX_DATA_SIZE, BUS_DMASYNC_PREREAD); + xfer->m = m; + } + } + + return 0; +fail: + for (i = 0; i < ring->num_elements; i++) { + struct qwz_xfer_data *xfer = &ring->data[i]; + + if (xfer->map) { + bus_dmamap_sync(sc->sc_dmat, xfer->map, 0, + xfer->map->dm_mapsize, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_dmat, xfer->map); + bus_dmamap_destroy(sc->sc_dmat, xfer->map); + xfer->map = NULL; + } + + if (xfer->m) { + m_freem(xfer->m); + xfer->m = NULL; + } + } + return 1; +} + +int +qwz_pci_alloc_xfer_rings_qca6390(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + int ret; + + ret = qwz_pci_alloc_xfer_ring(sc, + &psc->xfer_rings[QWZ_PCI_XFER_RING_LOOPBACK_OUTBOUND], + 0, MHI_CHAN_TYPE_OUTBOUND, 0, 32); + if (ret) + goto fail; + + ret = qwz_pci_alloc_xfer_ring(sc, + &psc->xfer_rings[QWZ_PCI_XFER_RING_LOOPBACK_INBOUND], + 1, MHI_CHAN_TYPE_INBOUND, 0, 32); + if (ret) + goto fail; + + ret = qwz_pci_alloc_xfer_ring(sc, + &psc->xfer_rings[QWZ_PCI_XFER_RING_IPCR_OUTBOUND], + 20, MHI_CHAN_TYPE_OUTBOUND, 1, 64); + if (ret) + goto fail; + + ret = qwz_pci_alloc_xfer_ring(sc, + &psc->xfer_rings[QWZ_PCI_XFER_RING_IPCR_INBOUND], + 21, MHI_CHAN_TYPE_INBOUND, 1, 64); + if (ret) + goto fail; + + return 0; +fail: + qwz_pci_free_xfer_rings(psc); + return ret; +} + +int +qwz_pci_alloc_xfer_rings_qcn9074(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + int ret; + + ret = qwz_pci_alloc_xfer_ring(sc, + &psc->xfer_rings[QWZ_PCI_XFER_RING_LOOPBACK_OUTBOUND], + 0, MHI_CHAN_TYPE_OUTBOUND, 1, 32); + if (ret) + goto fail; + + ret = qwz_pci_alloc_xfer_ring(sc, + &psc->xfer_rings[QWZ_PCI_XFER_RING_LOOPBACK_INBOUND], + 1, MHI_CHAN_TYPE_INBOUND, 1, 32); + if (ret) + goto fail; + + ret = qwz_pci_alloc_xfer_ring(sc, + &psc->xfer_rings[QWZ_PCI_XFER_RING_IPCR_OUTBOUND], + 20, MHI_CHAN_TYPE_OUTBOUND, 1, 32); + if (ret) + goto fail; + + ret = qwz_pci_alloc_xfer_ring(sc, + &psc->xfer_rings[QWZ_PCI_XFER_RING_IPCR_INBOUND], + 21, MHI_CHAN_TYPE_INBOUND, 1, 32); + if (ret) + goto fail; + + return 0; +fail: + qwz_pci_free_xfer_rings(psc); + return ret; +} + +void +qwz_pci_free_event_rings(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + int i; + + for (i = 0; i < nitems(psc->event_rings); i++) { + struct qwz_pci_event_ring *ring = &psc->event_rings[i]; + if (ring->dmamem) { + qwz_dmamem_free(sc->sc_dmat, ring->dmamem); + ring->dmamem = NULL; + } + memset(ring, 0, sizeof(*ring)); + } +} + +int +qwz_pci_alloc_event_ring(struct qwz_softc *sc, struct qwz_pci_event_ring *ring, + uint32_t type, uint32_t irq, uint32_t intmod, size_t num_elements) +{ + bus_size_t size; + + memset(ring, 0, sizeof(*ring)); + + size = sizeof(struct qwz_mhi_ring_element) * num_elements; + /* Hardware requires that rings are aligned to ring size. */ + ring->dmamem = qwz_dmamem_alloc(sc->sc_dmat, size, size); + if (ring->dmamem == NULL) + return ENOMEM; + + ring->size = size; + ring->mhi_er_type = type; + ring->mhi_er_irq = irq; + ring->mhi_er_irq_moderation_ms = intmod; + ring->num_elements = num_elements; + return 0; +} + +int +qwz_pci_alloc_event_rings(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + int ret; + + ret = qwz_pci_alloc_event_ring(sc, &psc->event_rings[0], + MHI_ER_CTRL, psc->mhi_irq[MHI_ER_CTRL], 0, 32); + if (ret) + goto fail; + + ret = qwz_pci_alloc_event_ring(sc, &psc->event_rings[1], + MHI_ER_DATA, psc->mhi_irq[MHI_ER_DATA], 1, 256); + if (ret) + goto fail; + + return 0; +fail: + qwz_pci_free_event_rings(psc); + return ret; +} + +void +qwz_pci_free_cmd_ring(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + struct qwz_pci_cmd_ring *ring = &psc->cmd_ring; + + if (ring->dmamem) + qwz_dmamem_free(sc->sc_dmat, ring->dmamem); + + memset(ring, 0, sizeof(*ring)); +} + +int +qwz_pci_init_cmd_ring(struct qwz_softc *sc, struct qwz_pci_cmd_ring *ring) +{ + memset(ring, 0, sizeof(*ring)); + + ring->num_elements = QWZ_PCI_CMD_RING_MAX_ELEMENTS; + ring->size = sizeof(struct qwz_mhi_ring_element) * ring->num_elements; + + /* Hardware requires that rings are aligned to ring size. */ + ring->dmamem = qwz_dmamem_alloc(sc->sc_dmat, ring->size, ring->size); + if (ring->dmamem == NULL) + return ENOMEM; + + return 0; +} + +uint32_t +qwz_pci_read(struct qwz_softc *sc, uint32_t addr) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + + return (bus_space_read_4(psc->sc_st, psc->sc_sh, addr)); +} + +void +qwz_pci_write(struct qwz_softc *sc, uint32_t addr, uint32_t val) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + + bus_space_write_4(psc->sc_st, psc->sc_sh, addr, val); +} + +void +qwz_pci_read_hw_version(struct qwz_softc *sc, uint32_t *major, + uint32_t *minor) +{ + uint32_t soc_hw_version; + + soc_hw_version = qwz_pcic_read32(sc, TCSR_SOC_HW_VERSION); + *major = FIELD_GET(TCSR_SOC_HW_VERSION_MAJOR_MASK, soc_hw_version); + *minor = FIELD_GET(TCSR_SOC_HW_VERSION_MINOR_MASK, soc_hw_version); + DPRINTF("%s: pci tcsr_soc_hw_version major %d minor %d\n", + sc->sc_dev.dv_xname, *major, *minor); +} + +uint32_t +qwz_pcic_read32(struct qwz_softc *sc, uint32_t offset) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + int ret = 0; + uint32_t val; + bool wakeup_required; + + /* for offset beyond BAR + 4K - 32, may + * need to wakeup the device to access. + */ + wakeup_required = test_bit(ATH12K_FLAG_DEVICE_INIT_DONE, sc->sc_flags) + && offset >= ATH12K_PCI_ACCESS_ALWAYS_OFF; + if (wakeup_required && psc->sc_pci_ops->wakeup) + ret = psc->sc_pci_ops->wakeup(sc); + + if (offset < ATH12K_PCI_WINDOW_START) + val = qwz_pci_read(sc, offset); + else + val = psc->sc_pci_ops->window_read32(sc, offset); + + if (wakeup_required && !ret && psc->sc_pci_ops->release) + psc->sc_pci_ops->release(sc); + + return val; +} + +void +qwz_pcic_write32(struct qwz_softc *sc, uint32_t offset, uint32_t value) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + int ret = 0; + bool wakeup_required; + + /* for offset beyond BAR + 4K - 32, may + * need to wakeup the device to access. + */ + wakeup_required = test_bit(ATH12K_FLAG_DEVICE_INIT_DONE, sc->sc_flags) + && offset >= ATH12K_PCI_ACCESS_ALWAYS_OFF; + if (wakeup_required && psc->sc_pci_ops->wakeup) + ret = psc->sc_pci_ops->wakeup(sc); + + if (offset < ATH12K_PCI_WINDOW_START) + qwz_pci_write(sc, offset, value); + else + psc->sc_pci_ops->window_write32(sc, offset, value); + + if (wakeup_required && !ret && psc->sc_pci_ops->release) + psc->sc_pci_ops->release(sc); +} + +void +qwz_pcic_ext_irq_disable(struct qwz_softc *sc) +{ + clear_bit(ATH12K_FLAG_EXT_IRQ_ENABLED, sc->sc_flags); + + /* In case of one MSI vector, we handle irq enable/disable in a + * uniform way since we only have one irq + */ + if (!test_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags)) + return; + + DPRINTF("%s not implemented\n", __func__); +} + +void +qwz_pcic_ext_irq_enable(struct qwz_softc *sc) +{ + set_bit(ATH12K_FLAG_EXT_IRQ_ENABLED, sc->sc_flags); + + /* In case of one MSI vector, we handle irq enable/disable in a + * uniform way since we only have one irq + */ + if (!test_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags)) + return; + + DPRINTF("%s not implemented\n", __func__); +} + +void +qwz_pcic_ce_irq_enable(struct qwz_softc *sc, uint16_t ce_id) +{ + /* In case of one MSI vector, we handle irq enable/disable in a + * uniform way since we only have one irq + */ + if (!test_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags)) + return; + + /* OpenBSD PCI stack does not yet implement MSI interrupt masking. */ + sc->msi_ce_irqmask |= (1U << ce_id); +} + +void +qwz_pcic_ce_irq_disable(struct qwz_softc *sc, uint16_t ce_id) +{ + /* In case of one MSI vector, we handle irq enable/disable in a + * uniform way since we only have one irq + */ + if (!test_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags)) + return; + + /* OpenBSD PCI stack does not yet implement MSI interrupt masking. */ + sc->msi_ce_irqmask &= ~(1U << ce_id); +} + +void +qwz_pcic_ext_grp_disable(struct qwz_ext_irq_grp *irq_grp) +{ + struct qwz_softc *sc = irq_grp->sc; + + /* In case of one MSI vector, we handle irq enable/disable + * in a uniform way since we only have one irq + */ + if (!test_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags)) + return; +} + +int +qwz_pcic_ext_irq_config(struct qwz_softc *sc, struct pci_attach_args *pa) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + int i, ret, num_vectors = 0; + uint32_t msi_data_start = 0; + uint32_t base_vector = 0; + + if (!test_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags)) + return 0; + + ret = qwz_pcic_get_user_msi_vector(sc, "DP", &num_vectors, + &msi_data_start, &base_vector); + if (ret < 0) + return ret; + + for (i = 0; i < nitems(sc->ext_irq_grp); i++) { + struct qwz_ext_irq_grp *irq_grp = &sc->ext_irq_grp[i]; + uint32_t num_irq = 0; + + irq_grp->sc = sc; + irq_grp->grp_id = i; +#if 0 + init_dummy_netdev(&irq_grp->napi_ndev); + netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, + ath12k_pcic_ext_grp_napi_poll); +#endif + if (sc->hw_params.ring_mask->tx[i] || + sc->hw_params.ring_mask->rx[i] || + sc->hw_params.ring_mask->rx_err[i] || + sc->hw_params.ring_mask->rx_wbm_rel[i] || + sc->hw_params.ring_mask->reo_status[i] || + sc->hw_params.ring_mask->rxdma2host[i] || + sc->hw_params.ring_mask->host2rxdma[i] || + sc->hw_params.ring_mask->rx_mon_status[i]) { + num_irq = 1; + } + + irq_grp->num_irq = num_irq; + irq_grp->irqs[0] = ATH12K_PCI_IRQ_DP_OFFSET + i; + + if (num_irq) { + int irq_idx = irq_grp->irqs[0]; + pci_intr_handle_t ih; + + if (pci_intr_map_msivec(pa, irq_idx, &ih) != 0 && + pci_intr_map(pa, &ih) != 0) { + printf("%s: can't map interrupt\n", + sc->sc_dev.dv_xname); + return EIO; + } + + snprintf(psc->sc_ivname[irq_idx], sizeof(psc->sc_ivname[0]), + "%s:ex%d", sc->sc_dev.dv_xname, i); + psc->sc_ih[irq_idx] = pci_intr_establish(psc->sc_pc, ih, + IPL_NET, qwz_ext_intr, irq_grp, psc->sc_ivname[irq_idx]); + if (psc->sc_ih[irq_idx] == NULL) { + printf("%s: failed to request irq %d\n", + sc->sc_dev.dv_xname, irq_idx); + return EIO; + } + } + + qwz_pcic_ext_grp_disable(irq_grp); + } + + return 0; +} + +int +qwz_pcic_config_irq(struct qwz_softc *sc, struct pci_attach_args *pa) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + struct qwz_ce_pipe *ce_pipe; + uint32_t msi_data_start; + uint32_t msi_data_count, msi_data_idx; + uint32_t msi_irq_start; + int i, ret, irq_idx; + pci_intr_handle_t ih; + + if (!test_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags)) + return 0; + + ret = qwz_pcic_get_user_msi_vector(sc, "CE", &msi_data_count, + &msi_data_start, &msi_irq_start); + if (ret) + return ret; + + /* Configure CE irqs */ + for (i = 0, msi_data_idx = 0; i < sc->hw_params.ce_count; i++) { + if (qwz_ce_get_attr_flags(sc, i) & CE_ATTR_DIS_INTR) + continue; + + ce_pipe = &sc->ce.ce_pipe[i]; + irq_idx = ATH12K_PCI_IRQ_CE0_OFFSET + i; + + if (pci_intr_map_msivec(pa, irq_idx, &ih) != 0 && + pci_intr_map(pa, &ih) != 0) { + printf("%s: can't map interrupt\n", + sc->sc_dev.dv_xname); + return EIO; + } + + snprintf(psc->sc_ivname[irq_idx], sizeof(psc->sc_ivname[0]), + "%s:ce%d", sc->sc_dev.dv_xname, ce_pipe->pipe_num); + psc->sc_ih[irq_idx] = pci_intr_establish(psc->sc_pc, ih, + IPL_NET, qwz_ce_intr, ce_pipe, psc->sc_ivname[irq_idx]); + if (psc->sc_ih[irq_idx] == NULL) { + printf("%s: failed to request irq %d\n", + sc->sc_dev.dv_xname, irq_idx); + return EIO; + } + + msi_data_idx++; + + qwz_pcic_ce_irq_disable(sc, i); + } + + ret = qwz_pcic_ext_irq_config(sc, pa); + if (ret) + return ret; + + return 0; +} + +void +qwz_pcic_ce_irqs_enable(struct qwz_softc *sc) +{ + int i; + + set_bit(ATH12K_FLAG_CE_IRQ_ENABLED, sc->sc_flags); + + for (i = 0; i < sc->hw_params.ce_count; i++) { + if (qwz_ce_get_attr_flags(sc, i) & CE_ATTR_DIS_INTR) + continue; + qwz_pcic_ce_irq_enable(sc, i); + } +} + +void +qwz_pcic_ce_irqs_disable(struct qwz_softc *sc) +{ + int i; + + clear_bit(ATH12K_FLAG_CE_IRQ_ENABLED, sc->sc_flags); + + for (i = 0; i < sc->hw_params.ce_count; i++) { + if (qwz_ce_get_attr_flags(sc, i) & CE_ATTR_DIS_INTR) + continue; + qwz_pcic_ce_irq_disable(sc, i); + } +} + +int +qwz_pci_start(struct qwz_softc *sc) +{ + /* TODO: for now don't restore ASPM in case of single MSI + * vector as MHI register reading in M2 causes system hang. + */ + if (test_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags)) + qwz_pci_aspm_restore(sc); + else + DPRINTF("%s: leaving PCI ASPM disabled to avoid MHI M2 problems" + "\n", sc->sc_dev.dv_xname); + + set_bit(ATH12K_FLAG_DEVICE_INIT_DONE, sc->sc_flags); + + qwz_ce_rx_post_buf(sc); + qwz_pcic_ce_irqs_enable(sc); + + return 0; +} + +void +qwz_pcic_ce_irq_disable_sync(struct qwz_softc *sc) +{ + qwz_pcic_ce_irqs_disable(sc); +#if 0 + ath12k_pcic_sync_ce_irqs(ab); + ath12k_pcic_kill_tasklets(ab); +#endif +} + +void +qwz_pci_stop(struct qwz_softc *sc) +{ + qwz_pcic_ce_irq_disable_sync(sc); + qwz_ce_cleanup_pipes(sc); +} + +int +qwz_pci_bus_wake_up(struct qwz_softc *sc) +{ + if (qwz_mhi_wake_db_clear_valid(sc)) + qwz_mhi_device_wake(sc); + + return 0; +} + +void +qwz_pci_bus_release(struct qwz_softc *sc) +{ + if (qwz_mhi_wake_db_clear_valid(sc)) + qwz_mhi_device_zzz(sc); +} + +uint32_t +qwz_pci_get_window_start(struct qwz_softc *sc, uint32_t offset) +{ + if (!sc->hw_params.static_window_map) + return ATH12K_PCI_WINDOW_START; + + if ((offset ^ HAL_SEQ_WCSS_UMAC_OFFSET) < ATH12K_PCI_WINDOW_RANGE_MASK) + /* if offset lies within DP register range, use 3rd window */ + return 3 * ATH12K_PCI_WINDOW_START; + else if ((offset ^ HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(sc)) < + ATH12K_PCI_WINDOW_RANGE_MASK) + /* if offset lies within CE register range, use 2nd window */ + return 2 * ATH12K_PCI_WINDOW_START; + else + return ATH12K_PCI_WINDOW_START; +} + +void +qwz_pci_select_window(struct qwz_softc *sc, uint32_t offset) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + uint32_t window = FIELD_GET(ATH12K_PCI_WINDOW_VALUE_MASK, offset); + +#if notyet + lockdep_assert_held(&ab_pci->window_lock); +#endif + + if (window != psc->register_window) { + qwz_pci_write(sc, ATH12K_PCI_WINDOW_REG_ADDRESS, + ATH12K_PCI_WINDOW_ENABLE_BIT | window); + (void) qwz_pci_read(sc, ATH12K_PCI_WINDOW_REG_ADDRESS); + psc->register_window = window; + } +} + +void +qwz_pci_window_write32(struct qwz_softc *sc, uint32_t offset, uint32_t value) +{ + uint32_t window_start; + + window_start = qwz_pci_get_window_start(sc, offset); + + if (window_start == ATH12K_PCI_WINDOW_START) { +#if notyet + spin_lock_bh(&ab_pci->window_lock); +#endif + qwz_pci_select_window(sc, offset); + qwz_pci_write(sc, window_start + + (offset & ATH12K_PCI_WINDOW_RANGE_MASK), value); +#if notyet + spin_unlock_bh(&ab_pci->window_lock); +#endif + } else { + qwz_pci_write(sc, window_start + + (offset & ATH12K_PCI_WINDOW_RANGE_MASK), value); + } +} + +uint32_t +qwz_pci_window_read32(struct qwz_softc *sc, uint32_t offset) +{ + uint32_t window_start, val; + + window_start = qwz_pci_get_window_start(sc, offset); + + if (window_start == ATH12K_PCI_WINDOW_START) { +#if notyet + spin_lock_bh(&ab_pci->window_lock); +#endif + qwz_pci_select_window(sc, offset); + val = qwz_pci_read(sc, window_start + + (offset & ATH12K_PCI_WINDOW_RANGE_MASK)); +#if notyet + spin_unlock_bh(&ab_pci->window_lock); +#endif + } else { + val = qwz_pci_read(sc, window_start + + (offset & ATH12K_PCI_WINDOW_RANGE_MASK)); + } + + return val; +} + +void +qwz_pci_select_static_window(struct qwz_softc *sc) +{ + uint32_t umac_window; + uint32_t ce_window; + uint32_t window; + + umac_window = FIELD_GET(ATH12K_PCI_WINDOW_VALUE_MASK, HAL_SEQ_WCSS_UMAC_OFFSET); + ce_window = FIELD_GET(ATH12K_PCI_WINDOW_VALUE_MASK, HAL_CE_WFSS_CE_REG_BASE); + window = (umac_window << 12) | (ce_window << 6); + + qwz_pci_write(sc, ATH12K_PCI_WINDOW_REG_ADDRESS, + ATH12K_PCI_WINDOW_ENABLE_BIT | window); +} + +void +qwz_pci_soc_global_reset(struct qwz_softc *sc) +{ + uint32_t val, msecs; + + val = qwz_pcic_read32(sc, PCIE_SOC_GLOBAL_RESET); + + val |= PCIE_SOC_GLOBAL_RESET_V; + + qwz_pcic_write32(sc, PCIE_SOC_GLOBAL_RESET, val); + + /* TODO: exact time to sleep is uncertain */ + msecs = 10; + DELAY(msecs * 1000); + + /* Need to toggle V bit back otherwise stuck in reset status */ + val &= ~PCIE_SOC_GLOBAL_RESET_V; + + qwz_pcic_write32(sc, PCIE_SOC_GLOBAL_RESET, val); + + DELAY(msecs * 1000); + + val = qwz_pcic_read32(sc, PCIE_SOC_GLOBAL_RESET); + if (val == 0xffffffff) + printf("%s: link down error during global reset\n", + sc->sc_dev.dv_xname); +} + +void +qwz_pci_clear_dbg_registers(struct qwz_softc *sc) +{ + uint32_t val; + + /* read cookie */ + val = qwz_pcic_read32(sc, PCIE_Q6_COOKIE_ADDR); + DPRINTF("%s: cookie:0x%x\n", sc->sc_dev.dv_xname, val); + + val = qwz_pcic_read32(sc, WLAON_WARM_SW_ENTRY); + DPRINTF("%s: WLAON_WARM_SW_ENTRY 0x%x\n", sc->sc_dev.dv_xname, val); + + /* TODO: exact time to sleep is uncertain */ + DELAY(10 * 1000); + + /* write 0 to WLAON_WARM_SW_ENTRY to prevent Q6 from + * continuing warm path and entering dead loop. + */ + qwz_pcic_write32(sc, WLAON_WARM_SW_ENTRY, 0); + DELAY(10 * 1000); + + val = qwz_pcic_read32(sc, WLAON_WARM_SW_ENTRY); + DPRINTF("%s: WLAON_WARM_SW_ENTRY 0x%x\n", sc->sc_dev.dv_xname, val); + + /* A read clear register. clear the register to prevent + * Q6 from entering wrong code path. + */ + val = qwz_pcic_read32(sc, WLAON_SOC_RESET_CAUSE_REG); + DPRINTF("%s: soc reset cause:%d\n", sc->sc_dev.dv_xname, val); +} + +int +qwz_pci_set_link_reg(struct qwz_softc *sc, uint32_t offset, uint32_t value, + uint32_t mask) +{ + uint32_t v; + int i; + + v = qwz_pcic_read32(sc, offset); + if ((v & mask) == value) + return 0; + + for (i = 0; i < 10; i++) { + qwz_pcic_write32(sc, offset, (v & ~mask) | value); + + v = qwz_pcic_read32(sc, offset); + if ((v & mask) == value) + return 0; + + delay((2 * 1000)); + } + + DPRINTF("failed to set pcie link register 0x%08x: 0x%08x != 0x%08x\n", + offset, v & mask, value); + + return ETIMEDOUT; +} + +int +qwz_pci_fix_l1ss(struct qwz_softc *sc) +{ + int ret; + + ret = qwz_pci_set_link_reg(sc, + PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG(sc), + PCIE_QSERDES_COM_SYSCLK_EN_SEL_VAL, + PCIE_QSERDES_COM_SYSCLK_EN_SEL_MSK); + if (ret) { + DPRINTF("failed to set sysclk: %d\n", ret); + return ret; + } + + ret = qwz_pci_set_link_reg(sc, + PCIE_PCS_OSC_DTCT_CONFIG1_REG(sc), + PCIE_PCS_OSC_DTCT_CONFIG1_VAL, + PCIE_PCS_OSC_DTCT_CONFIG_MSK); + if (ret) { + DPRINTF("failed to set dtct config1 error: %d\n", ret); + return ret; + } + + ret = qwz_pci_set_link_reg(sc, + PCIE_PCS_OSC_DTCT_CONFIG2_REG(sc), + PCIE_PCS_OSC_DTCT_CONFIG2_VAL, + PCIE_PCS_OSC_DTCT_CONFIG_MSK); + if (ret) { + DPRINTF("failed to set dtct config2: %d\n", ret); + return ret; + } + + ret = qwz_pci_set_link_reg(sc, + PCIE_PCS_OSC_DTCT_CONFIG4_REG(sc), + PCIE_PCS_OSC_DTCT_CONFIG4_VAL, + PCIE_PCS_OSC_DTCT_CONFIG_MSK); + if (ret) { + DPRINTF("failed to set dtct config4: %d\n", ret); + return ret; + } + + return 0; +} + +void +qwz_pci_enable_ltssm(struct qwz_softc *sc) +{ + uint32_t val; + int i; + + val = qwz_pcic_read32(sc, PCIE_PCIE_PARF_LTSSM); + + /* PCIE link seems very unstable after the Hot Reset*/ + for (i = 0; val != PARM_LTSSM_VALUE && i < 5; i++) { + if (val == 0xffffffff) + DELAY(5 * 1000); + + qwz_pcic_write32(sc, PCIE_PCIE_PARF_LTSSM, PARM_LTSSM_VALUE); + val = qwz_pcic_read32(sc, PCIE_PCIE_PARF_LTSSM); + } + + DPRINTF("%s: pci ltssm 0x%x\n", sc->sc_dev.dv_xname, val); + + val = qwz_pcic_read32(sc, GCC_GCC_PCIE_HOT_RST); + val |= GCC_GCC_PCIE_HOT_RST_VAL; + qwz_pcic_write32(sc, GCC_GCC_PCIE_HOT_RST, val); + val = qwz_pcic_read32(sc, GCC_GCC_PCIE_HOT_RST); + + DPRINTF("%s: pci pcie_hot_rst 0x%x\n", sc->sc_dev.dv_xname, val); + + DELAY(5 * 1000); +} + +void +qwz_pci_clear_all_intrs(struct qwz_softc *sc) +{ + /* This is a WAR for PCIE Hotreset. + * When target receive Hotreset, but will set the interrupt. + * So when download SBL again, SBL will open Interrupt and + * receive it, and crash immediately. + */ + qwz_pcic_write32(sc, PCIE_PCIE_INT_ALL_CLEAR, PCIE_INT_CLEAR_ALL); +} + +void +qwz_pci_set_wlaon_pwr_ctrl(struct qwz_softc *sc) +{ + uint32_t val; + + val = qwz_pcic_read32(sc, WLAON_QFPROM_PWR_CTRL_REG); + val &= ~QFPROM_PWR_CTRL_VDD4BLOW_MASK; + qwz_pcic_write32(sc, WLAON_QFPROM_PWR_CTRL_REG, val); +} + +void +qwz_pci_force_wake(struct qwz_softc *sc) +{ + qwz_pcic_write32(sc, PCIE_SOC_WAKE_PCIE_LOCAL_REG, 1); + DELAY(5 * 1000); +} + +void +qwz_pci_sw_reset(struct qwz_softc *sc, bool power_on) +{ + DELAY(100 * 1000); /* msecs */ + + if (power_on) { + qwz_pci_enable_ltssm(sc); + qwz_pci_clear_all_intrs(sc); + qwz_pci_set_wlaon_pwr_ctrl(sc); + if (sc->hw_params.fix_l1ss) + qwz_pci_fix_l1ss(sc); + } + + qwz_mhi_clear_vector(sc); + qwz_pci_clear_dbg_registers(sc); + qwz_pci_soc_global_reset(sc); + qwz_mhi_reset_device(sc, 0); +} + +void +qwz_pci_msi_config(struct qwz_softc *sc, bool enable) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + uint32_t val; + + val = pci_conf_read(psc->sc_pc, psc->sc_tag, + psc->sc_msi_off + PCI_MSI_MC); + + if (enable) + val |= PCI_MSI_MC_MSIE; + else + val &= ~PCI_MSI_MC_MSIE; + + pci_conf_write(psc->sc_pc, psc->sc_tag, psc->sc_msi_off + PCI_MSI_MC, + val); +} + +void +qwz_pci_msi_enable(struct qwz_softc *sc) +{ + qwz_pci_msi_config(sc, true); +} + +void +qwz_pci_msi_disable(struct qwz_softc *sc) +{ + qwz_pci_msi_config(sc, false); +} + +void +qwz_pci_aspm_disable(struct qwz_softc *sc) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + + psc->sc_lcsr = pci_conf_read(psc->sc_pc, psc->sc_tag, + psc->sc_cap_off + PCI_PCIE_LCSR); + + DPRINTF("%s: pci link_ctl 0x%04x L0s %d L1 %d\n", sc->sc_dev.dv_xname, + (uint16_t)psc->sc_lcsr, (psc->sc_lcsr & PCI_PCIE_LCSR_ASPM_L0S), + (psc->sc_lcsr & PCI_PCIE_LCSR_ASPM_L1)); + + /* disable L0s and L1 */ + pci_conf_write(psc->sc_pc, psc->sc_tag, psc->sc_cap_off + PCI_PCIE_LCSR, + psc->sc_lcsr & ~(PCI_PCIE_LCSR_ASPM_L0S | PCI_PCIE_LCSR_ASPM_L1)); + + psc->sc_flags |= ATH12K_PCI_ASPM_RESTORE; +} + +void +qwz_pci_aspm_restore(struct qwz_softc *sc) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + + if (psc->sc_flags & ATH12K_PCI_ASPM_RESTORE) { + pci_conf_write(psc->sc_pc, psc->sc_tag, + psc->sc_cap_off + PCI_PCIE_LCSR, psc->sc_lcsr); + psc->sc_flags &= ~ATH12K_PCI_ASPM_RESTORE; + } +} + +int +qwz_pci_power_up(struct qwz_softc *sc) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + int error; + + psc->register_window = 0; + clear_bit(ATH12K_FLAG_DEVICE_INIT_DONE, sc->sc_flags); + + qwz_pci_sw_reset(sc, true); + + /* Disable ASPM during firmware download due to problems switching + * to AMSS state. + */ + qwz_pci_aspm_disable(sc); + + qwz_pci_msi_enable(sc); + + error = qwz_mhi_start(psc); + if (error) + return error; + + if (sc->hw_params.static_window_map) + qwz_pci_select_static_window(sc); + + return 0; +} + +void +qwz_pci_power_down(struct qwz_softc *sc) +{ + /* restore aspm in case firmware bootup fails */ + qwz_pci_aspm_restore(sc); + + qwz_pci_force_wake(sc); + + qwz_pci_msi_disable(sc); + + qwz_mhi_stop(sc); + clear_bit(ATH12K_FLAG_DEVICE_INIT_DONE, sc->sc_flags); + qwz_pci_sw_reset(sc, false); +} + +/* + * MHI + */ +int +qwz_mhi_register(struct qwz_softc *sc) +{ + DNPRINTF(QWZ_D_MHI, "%s: STUB %s()\n", sc->sc_dev.dv_xname, __func__); + return 0; +} + +void +qwz_mhi_unregister(struct qwz_softc *sc) +{ + DNPRINTF(QWZ_D_MHI, "%s: STUB %s()\n", sc->sc_dev.dv_xname, __func__); +} + +// XXX MHI is GPLd - we provide a compatible bare-bones implementation +#define MHI_CFG 0x10 +#define MHI_CFG_NHWER_MASK GENMASK(31, 24) +#define MHI_CFG_NHWER_SHFT 24 +#define MHI_CFG_NER_MASK GENMASK(23, 16) +#define MHI_CFG_NER_SHFT 16 +#define MHI_CFG_NHWCH_MASK GENMASK(15, 8) +#define MHI_CFG_NHWCH_SHFT 8 +#define MHI_CFG_NCH_MASK GENMASK(7, 0) +#define MHI_CHDBOFF 0x18 +#define MHI_DEV_WAKE_DB 127 +#define MHI_ERDBOFF 0x20 +#define MHI_BHI_OFFSET 0x28 +#define MHI_BHI_IMGADDR_LOW 0x08 +#define MHI_BHI_IMGADDR_HIGH 0x0c +#define MHI_BHI_IMGSIZE 0x10 +#define MHI_BHI_IMGTXDB 0x18 +#define MHI_BHI_INTVEC 0x20 +#define MHI_BHI_EXECENV 0x28 +#define MHI_BHI_STATUS 0x2c +#define MHI_BHI_SERIALNU 0x40 +#define MHI_BHIE_OFFSET 0x2c +#define MHI_BHIE_TXVECADDR_LOW_OFFS 0x2c +#define MHI_BHIE_TXVECADDR_HIGH_OFFS 0x30 +#define MHI_BHIE_TXVECSIZE_OFFS 0x34 +#define MHI_BHIE_TXVECDB_OFFS 0x3c +#define MHI_BHIE_TXVECSTATUS_OFFS 0x44 +#define MHI_BHIE_RXVECADDR_LOW_OFFS 0x60 +#define MHI_BHIE_RXVECSTATUS_OFFS 0x78 +#define MHI_CTRL 0x38 +#define MHI_CTRL_READY_MASK 0x1 +#define MHI_CTRL_RESET_MASK 0x2 +#define MHI_CTRL_MHISTATE_MASK GENMASK(15, 8) +#define MHI_CTRL_MHISTATE_SHFT 8 +#define MHI_STATUS 0x48 +#define MHI_STATUS_MHISTATE_MASK GENMASK(15, 8) +#define MHI_STATUS_MHISTATE_SHFT 8 +#define MHI_STATE_RESET 0x0 +#define MHI_STATE_READY 0x1 +#define MHI_STATE_M0 0x2 +#define MHI_STATE_M1 0x3 +#define MHI_STATE_M2 0x4 +#define MHI_STATE_M3 0x5 +#define MHI_STATE_M3_FAST 0x6 +#define MHI_STATE_BHI 0x7 +#define MHI_STATE_SYS_ERR 0xff +#define MHI_STATUS_READY_MASK 0x1 +#define MHI_STATUS_SYSERR_MASK 0x4 +#define MHI_CCABAP_LOWER 0x58 +#define MHI_CCABAP_HIGHER 0x5c +#define MHI_ECABAP_LOWER 0x60 +#define MHI_ECABAP_HIGHER 0x64 +#define MHI_CRCBAP_LOWER 0x68 +#define MHI_CRCBAP_HIGHER 0x6c +#define MHI_CRDB_LOWER 0x70 +#define MHI_CRDB_HIGHER 0x74 +#define MHI_CTRLBASE_LOWER 0x80 +#define MHI_CTRLBASE_HIGHER 0x84 +#define MHI_CTRLLIMIT_LOWER 0x88 +#define MHI_CTRLLIMIT_HIGHER 0x8c +#define MHI_DATABASE_LOWER 0x98 +#define MHI_DATABASE_HIGHER 0x9c +#define MHI_DATALIMIT_LOWER 0xa0 +#define MHI_DATALIMIT_HIGHER 0xa4 + +#define MHI_EE_PBL 0x0 /* Primary Bootloader */ +#define MHI_EE_SBL 0x1 /* Secondary Bootloader */ +#define MHI_EE_AMSS 0x2 /* Modem, aka the primary runtime EE */ +#define MHI_EE_RDDM 0x3 /* Ram dump download mode */ +#define MHI_EE_WFW 0x4 /* WLAN firmware mode */ +#define MHI_EE_PTHRU 0x5 /* Passthrough */ +#define MHI_EE_EDL 0x6 /* Embedded downloader */ +#define MHI_EE_FP 0x7 /* Flash Programmer Environment */ + +#define MHI_IN_PBL(e) (e == MHI_EE_PBL || e == MHI_EE_PTHRU || e == MHI_EE_EDL) +#define MHI_POWER_UP_CAPABLE(e) (MHI_IN_PBL(e) || e == MHI_EE_AMSS) +#define MHI_IN_MISSION_MODE(e) \ + (e == MHI_EE_AMSS || e == MHI_EE_WFW || e == MHI_EE_FP) + +/* BHI register bits */ +#define MHI_BHI_TXDB_SEQNUM_BMSK GENMASK(29, 0) +#define MHI_BHI_TXDB_SEQNUM_SHFT 0 +#define MHI_BHI_STATUS_MASK GENMASK(31, 30) +#define MHI_BHI_STATUS_SHFT 30 +#define MHI_BHI_STATUS_ERROR 0x03 +#define MHI_BHI_STATUS_SUCCESS 0x02 +#define MHI_BHI_STATUS_RESET 0x00 + +/* MHI BHIE registers */ +#define MHI_BHIE_MSMSOCID_OFFS 0x00 +#define MHI_BHIE_RXVECADDR_LOW_OFFS 0x60 +#define MHI_BHIE_RXVECADDR_HIGH_OFFS 0x64 +#define MHI_BHIE_RXVECSIZE_OFFS 0x68 +#define MHI_BHIE_RXVECDB_OFFS 0x70 +#define MHI_BHIE_RXVECSTATUS_OFFS 0x78 + +/* BHIE register bits */ +#define MHI_BHIE_TXVECDB_SEQNUM_BMSK GENMASK(29, 0) +#define MHI_BHIE_TXVECDB_SEQNUM_SHFT 0 +#define MHI_BHIE_TXVECSTATUS_SEQNUM_BMSK GENMASK(29, 0) +#define MHI_BHIE_TXVECSTATUS_SEQNUM_SHFT 0 +#define MHI_BHIE_TXVECSTATUS_STATUS_BMSK GENMASK(31, 30) +#define MHI_BHIE_TXVECSTATUS_STATUS_SHFT 30 +#define MHI_BHIE_TXVECSTATUS_STATUS_RESET 0x00 +#define MHI_BHIE_TXVECSTATUS_STATUS_XFER_COMPL 0x02 +#define MHI_BHIE_TXVECSTATUS_STATUS_ERROR 0x03 +#define MHI_BHIE_RXVECDB_SEQNUM_BMSK GENMASK(29, 0) +#define MHI_BHIE_RXVECDB_SEQNUM_SHFT 0 +#define MHI_BHIE_RXVECSTATUS_SEQNUM_BMSK GENMASK(29, 0) +#define MHI_BHIE_RXVECSTATUS_SEQNUM_SHFT 0 +#define MHI_BHIE_RXVECSTATUS_STATUS_BMSK GENMASK(31, 30) +#define MHI_BHIE_RXVECSTATUS_STATUS_SHFT 30 +#define MHI_BHIE_RXVECSTATUS_STATUS_RESET 0x00 +#define MHI_BHIE_RXVECSTATUS_STATUS_XFER_COMPL 0x02 +#define MHI_BHIE_RXVECSTATUS_STATUS_ERROR 0x03 + +#define MHI_EV_CC_INVALID 0x0 +#define MHI_EV_CC_SUCCESS 0x1 +#define MHI_EV_CC_EOT 0x2 +#define MHI_EV_CC_OVERFLOW 0x3 +#define MHI_EV_CC_EOB 0x4 +#define MHI_EV_CC_OOB 0x5 +#define MHI_EV_CC_DB_MODE 0x6 +#define MHI_EV_CC_UNDEFINED_ERR 0x10 +#define MHI_EV_CC_BAD_TRE 0x11 + +#define MHI_CMD_NOP 01 +#define MHI_CMD_RESET_CHAN 16 +#define MHI_CMD_STOP_CHAN 17 +#define MHI_CMD_START_CHAN 18 + +#define MHI_TRE_CMD_CHID_MASK GENMASK(31, 24) +#define MHI_TRE_CMD_CHID_SHFT 24 +#define MHI_TRE_CMD_CMDID_MASK GENMASK(23, 16) +#define MHI_TRE_CMD_CMDID_SHFT 16 + +#define MHI_TRE0_EV_LEN_MASK GENMASK(15, 0) +#define MHI_TRE0_EV_LEN_SHFT 0 +#define MHI_TRE0_EV_CODE_MASK GENMASK(31, 24) +#define MHI_TRE0_EV_CODE_SHFT 24 +#define MHI_TRE1_EV_TYPE_MASK GENMASK(23, 16) +#define MHI_TRE1_EV_TYPE_SHFT 16 +#define MHI_TRE1_EV_CHID_MASK GENMASK(31, 24) +#define MHI_TRE1_EV_CHID_SHFT 24 + +#define MHI_TRE0_DATA_LEN_MASK GENMASK(15, 0) +#define MHI_TRE0_DATA_LEN_SHFT 0 +#define MHI_TRE1_DATA_CHAIN (1 << 0) +#define MHI_TRE1_DATA_IEOB (1 << 8) +#define MHI_TRE1_DATA_IEOT (1 << 9) +#define MHI_TRE1_DATA_BEI (1 << 10) +#define MHI_TRE1_DATA_TYPE_MASK GENMASK(23, 16) +#define MHI_TRE1_DATA_TYPE_SHIFT 16 +#define MHI_TRE1_DATA_TYPE_TRANSFER 0x2 + +#define MHI_PKT_TYPE_INVALID 0x00 +#define MHI_PKT_TYPE_NOOP_CMD 0x01 +#define MHI_PKT_TYPE_TRANSFER 0x02 +#define MHI_PKT_TYPE_COALESCING 0x08 +#define MHI_PKT_TYPE_RESET_CHAN_CMD 0x10 +#define MHI_PKT_TYPE_STOP_CHAN_CMD 0x11 +#define MHI_PKT_TYPE_START_CHAN_CMD 0x12 +#define MHI_PKT_TYPE_STATE_CHANGE_EVENT 0x20 +#define MHI_PKT_TYPE_CMD_COMPLETION_EVENT 0x21 +#define MHI_PKT_TYPE_TX_EVENT 0x22 +#define MHI_PKT_TYPE_RSC_TX_EVENT 0x28 +#define MHI_PKT_TYPE_EE_EVENT 0x40 +#define MHI_PKT_TYPE_TSYNC_EVENT 0x48 +#define MHI_PKT_TYPE_BW_REQ_EVENT 0x50 + + +#define MHI_DMA_VEC_CHUNK_SIZE 524288 /* 512 KB */ +struct qwz_dma_vec_entry { + uint64_t paddr; + uint64_t size; +}; + +void +qwz_mhi_ring_doorbell(struct qwz_softc *sc, uint64_t db_addr, uint64_t val) +{ + qwz_pci_write(sc, db_addr + 4, val >> 32); + qwz_pci_write(sc, db_addr, val & 0xffffffff); +} + +void +qwz_mhi_device_wake(struct qwz_softc *sc) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + + /* + * Device wake is async only for now because we do not + * keep track of PM state in software. + */ + qwz_mhi_ring_doorbell(sc, psc->wake_db, 1); +} + +void +qwz_mhi_device_zzz(struct qwz_softc *sc) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + + qwz_mhi_ring_doorbell(sc, psc->wake_db, 0); +} + +int +qwz_mhi_wake_db_clear_valid(struct qwz_softc *sc) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + + return (psc->mhi_state == MHI_STATE_M0); /* TODO other states? */ +} + +void +qwz_mhi_init_xfer_rings(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + int i; + uint32_t chcfg; + struct qwz_pci_xfer_ring *ring; + struct qwz_mhi_chan_ctxt *cbase, *c; + + cbase = (struct qwz_mhi_chan_ctxt *)QWZ_DMA_KVA(psc->chan_ctxt); + for (i = 0; i < psc->max_chan; i++) { + c = &cbase[i]; + chcfg = le32toh(c->chcfg); + chcfg &= ~(MHI_CHAN_CTX_CHSTATE_MASK | + MHI_CHAN_CTX_BRSTMODE_MASK | + MHI_CHAN_CTX_POLLCFG_MASK); + chcfg |= (MHI_CHAN_CTX_CHSTATE_DISABLED | + (MHI_CHAN_CTX_BRSTMODE_DISABLE << + MHI_CHAN_CTX_BRSTMODE_SHFT)); + c->chcfg = htole32(chcfg); + c->chtype = htole32(MHI_CHAN_TYPE_INVALID); + c->erindex = 0; + } + + for (i = 0; i < nitems(psc->xfer_rings); i++) { + ring = &psc->xfer_rings[i]; + KASSERT(ring->mhi_chan_id < psc->max_chan); + c = &cbase[ring->mhi_chan_id]; + c->chtype = htole32(ring->mhi_chan_direction); + c->erindex = htole32(ring->mhi_chan_event_ring_index); + ring->chan_ctxt = c; + } + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(psc->chan_ctxt), 0, + QWZ_DMA_LEN(psc->chan_ctxt), BUS_DMASYNC_PREWRITE); +} + +void +qwz_mhi_init_event_rings(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + int i; + uint32_t intmod; + uint64_t paddr, len; + struct qwz_pci_event_ring *ring; + struct qwz_mhi_event_ctxt *c; + + c = (struct qwz_mhi_event_ctxt *)QWZ_DMA_KVA(psc->event_ctxt); + for (i = 0; i < nitems(psc->event_rings); i++, c++) { + ring = &psc->event_rings[i]; + + ring->event_ctxt = c; + + intmod = le32toh(c->intmod); + intmod &= ~(MHI_EV_CTX_INTMODC_MASK | MHI_EV_CTX_INTMODT_MASK); + intmod |= (ring->mhi_er_irq_moderation_ms << + MHI_EV_CTX_INTMODT_SHFT) & MHI_EV_CTX_INTMODT_MASK; + c->intmod = htole32(intmod); + + c->ertype = htole32(MHI_ER_TYPE_VALID); + c->msivec = htole32(ring->mhi_er_irq); + + paddr = QWZ_DMA_DVA(ring->dmamem); + ring->rp = paddr; + ring->wp = paddr + ring->size - + sizeof(struct qwz_mhi_ring_element); + c->rbase = htole64(paddr); + c->rp = htole64(ring->rp); + c->wp = htole64(ring->wp); + + len = sizeof(struct qwz_mhi_ring_element) * ring->num_elements; + c->rlen = htole64(len); + } + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(psc->event_ctxt), 0, + QWZ_DMA_LEN(psc->event_ctxt), BUS_DMASYNC_PREWRITE); +} + +void +qwz_mhi_init_cmd_ring(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + struct qwz_pci_cmd_ring *ring = &psc->cmd_ring; + struct qwz_mhi_cmd_ctxt *c; + uint64_t paddr, len; + + paddr = QWZ_DMA_DVA(ring->dmamem); + len = ring->size; + + ring->rp = ring->wp = paddr; + + c = (struct qwz_mhi_cmd_ctxt *)QWZ_DMA_KVA(psc->cmd_ctxt); + c->rbase = htole64(paddr); + c->rp = htole64(paddr); + c->wp = htole64(paddr); + c->rlen = htole64(len); + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(psc->cmd_ctxt), 0, + QWZ_DMA_LEN(psc->cmd_ctxt), BUS_DMASYNC_PREWRITE); +} + +void +qwz_mhi_init_dev_ctxt(struct qwz_pci_softc *psc) +{ + qwz_mhi_init_xfer_rings(psc); + qwz_mhi_init_event_rings(psc); + qwz_mhi_init_cmd_ring(psc); +} + +void * +qwz_pci_cmd_ring_get_elem(struct qwz_pci_cmd_ring *ring, uint64_t ptr) +{ + uint64_t base = QWZ_DMA_DVA(ring->dmamem), offset; + + if (ptr < base || ptr >= base + ring->size) + return NULL; + + offset = ptr - base; + if (offset >= ring->size) + return NULL; + + return QWZ_DMA_KVA(ring->dmamem) + offset; +} + +int +qwz_mhi_cmd_ring_submit(struct qwz_pci_softc *psc, + struct qwz_pci_cmd_ring *ring) +{ + struct qwz_softc *sc = &psc->sc_sc; + uint64_t base = QWZ_DMA_DVA(ring->dmamem); + struct qwz_mhi_cmd_ctxt *c; + + if (ring->queued >= ring->num_elements) + return 1; + + if (ring->wp + sizeof(struct qwz_mhi_ring_element) >= base + ring->size) + ring->wp = base; + else + ring->wp += sizeof(struct qwz_mhi_ring_element); + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(psc->cmd_ctxt), 0, + QWZ_DMA_LEN(psc->cmd_ctxt), BUS_DMASYNC_POSTREAD); + + c = (struct qwz_mhi_cmd_ctxt *)QWZ_DMA_KVA(psc->cmd_ctxt); + c->wp = htole64(ring->wp); + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(psc->cmd_ctxt), 0, + QWZ_DMA_LEN(psc->cmd_ctxt), BUS_DMASYNC_PREWRITE); + + ring->queued++; + qwz_mhi_ring_doorbell(sc, MHI_CRDB_LOWER, ring->wp); + return 0; +} + +int +qwz_mhi_send_cmd(struct qwz_pci_softc *psc, uint32_t cmd, uint32_t chan) +{ + struct qwz_softc *sc = &psc->sc_sc; + struct qwz_pci_cmd_ring *ring = &psc->cmd_ring; + struct qwz_mhi_ring_element *e; + + if (ring->queued >= ring->num_elements) { + printf("%s: command ring overflow\n", sc->sc_dev.dv_xname); + return 1; + } + + e = qwz_pci_cmd_ring_get_elem(ring, ring->wp); + if (e == NULL) + return 1; + + e->ptr = 0ULL; + e->dword[0] = 0; + e->dword[1] = htole32( + ((chan << MHI_TRE_CMD_CHID_SHFT) & MHI_TRE_CMD_CHID_MASK) | + ((cmd << MHI_TRE_CMD_CMDID_SHFT) & MHI_TRE_CMD_CMDID_MASK)); + + return qwz_mhi_cmd_ring_submit(psc, ring); +} + +void * +qwz_pci_xfer_ring_get_elem(struct qwz_pci_xfer_ring *ring, uint64_t wp) +{ + uint64_t base = QWZ_DMA_DVA(ring->dmamem), offset; + void *addr = QWZ_DMA_KVA(ring->dmamem); + + if (wp < base) + return NULL; + + offset = wp - base; + if (offset >= ring->size) + return NULL; + + return addr + offset; +} + +struct qwz_xfer_data * +qwz_pci_xfer_ring_get_data(struct qwz_pci_xfer_ring *ring, uint64_t wp) +{ + uint64_t base = QWZ_DMA_DVA(ring->dmamem), offset; + + if (wp < base) + return NULL; + + offset = wp - base; + if (offset >= ring->size) + return NULL; + + return &ring->data[offset / sizeof(ring->data[0])]; +} + +int +qwz_mhi_submit_xfer(struct qwz_softc *sc, struct mbuf *m) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + struct qwz_pci_xfer_ring *ring; + struct qwz_mhi_ring_element *e; + struct qwz_xfer_data *xfer; + uint64_t paddr, base; + int err; + + ring = &psc->xfer_rings[QWZ_PCI_XFER_RING_IPCR_OUTBOUND]; + + if (ring->queued >= ring->num_elements) + return 1; + + if (m->m_pkthdr.len > QWZ_PCI_XFER_MAX_DATA_SIZE) { + /* TODO: chunk xfers */ + printf("%s: xfer too large: %d bytes\n", __func__, m->m_pkthdr.len); + return 1; + + } + + e = qwz_pci_xfer_ring_get_elem(ring, ring->wp); + if (e == NULL) + return 1; + + xfer = qwz_pci_xfer_ring_get_data(ring, ring->wp); + if (xfer == NULL || xfer->m != NULL) + return 1; + + err = bus_dmamap_load_mbuf(sc->sc_dmat, xfer->map, m, + BUS_DMA_NOWAIT | BUS_DMA_WRITE); + if (err && err != EFBIG) { + printf("%s: can't map mbuf (error %d)\n", + sc->sc_dev.dv_xname, err); + return err; + } + if (err) { + /* Too many DMA segments, linearize mbuf. */ + if (m_defrag(m, M_DONTWAIT)) + return ENOBUFS; + err = bus_dmamap_load_mbuf(sc->sc_dmat, xfer->map, m, + BUS_DMA_NOWAIT | BUS_DMA_WRITE); + if (err) { + printf("%s: can't map mbuf (error %d)\n", + sc->sc_dev.dv_xname, err); + return err; + } + } + + bus_dmamap_sync(sc->sc_dmat, xfer->map, 0, m->m_pkthdr.len, + BUS_DMASYNC_PREWRITE); + + xfer->m = m; + paddr = xfer->map->dm_segs[0].ds_addr; + + e->ptr = htole64(paddr); + e->dword[0] = htole32((m->m_pkthdr.len << MHI_TRE0_DATA_LEN_SHFT) & + MHI_TRE0_DATA_LEN_MASK); + e->dword[1] = htole32(MHI_TRE1_DATA_IEOT | + MHI_TRE1_DATA_TYPE_TRANSFER << MHI_TRE1_DATA_TYPE_SHIFT); + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(ring->dmamem), + 0, QWZ_DMA_LEN(ring->dmamem), BUS_DMASYNC_PREWRITE); + + base = QWZ_DMA_DVA(ring->dmamem); + if (ring->wp + sizeof(struct qwz_mhi_ring_element) >= base + ring->size) + ring->wp = base; + else + ring->wp += sizeof(struct qwz_mhi_ring_element); + ring->queued++; + + ring->chan_ctxt->wp = htole64(ring->wp); + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(psc->chan_ctxt), 0, + QWZ_DMA_LEN(psc->chan_ctxt), BUS_DMASYNC_PREWRITE); + + qwz_mhi_ring_doorbell(sc, ring->db_addr, ring->wp); + return 0; +} + +int +qwz_mhi_start_channel(struct qwz_pci_softc *psc, + struct qwz_pci_xfer_ring *ring) +{ + struct qwz_softc *sc = &psc->sc_sc; + struct qwz_mhi_chan_ctxt *c; + int ret = 0; + uint32_t chcfg; + uint64_t paddr, len; + + DNPRINTF(QWZ_D_MHI, "%s: start MHI channel %d in state %d\n", __func__, + ring->mhi_chan_id, ring->mhi_chan_state); + + c = ring->chan_ctxt; + + chcfg = le32toh(c->chcfg); + chcfg &= ~MHI_CHAN_CTX_CHSTATE_MASK; + chcfg |= MHI_CHAN_CTX_CHSTATE_ENABLED; + c->chcfg = htole32(chcfg); + + paddr = QWZ_DMA_DVA(ring->dmamem); + ring->rp = ring->wp = paddr; + c->rbase = htole64(paddr); + c->rp = htole64(ring->rp); + c->wp = htole64(ring->wp); + len = sizeof(struct qwz_mhi_ring_element) * ring->num_elements; + c->rlen = htole64(len); + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(psc->chan_ctxt), 0, + QWZ_DMA_LEN(psc->chan_ctxt), BUS_DMASYNC_PREWRITE); + + ring->cmd_status = MHI_EV_CC_INVALID; + if (qwz_mhi_send_cmd(psc, MHI_CMD_START_CHAN, ring->mhi_chan_id)) + return 1; + + while (ring->cmd_status != MHI_EV_CC_SUCCESS) { + ret = tsleep_nsec(&ring->cmd_status, 0, "qwzcmd", + SEC_TO_NSEC(5)); + if (ret) + break; + } + + if (ret) { + printf("%s: could not start MHI channel %d in state %d: status 0x%x\n", + sc->sc_dev.dv_xname, ring->mhi_chan_id, + ring->mhi_chan_state, ring->cmd_status); + return 1; + } + + if (ring->mhi_chan_direction == MHI_CHAN_TYPE_INBOUND) { + uint64_t wp = QWZ_DMA_DVA(ring->dmamem); + int i; + + for (i = 0; i < ring->num_elements; i++) { + struct qwz_mhi_ring_element *e; + struct qwz_xfer_data *xfer; + uint64_t paddr; + + e = qwz_pci_xfer_ring_get_elem(ring, wp); + xfer = qwz_pci_xfer_ring_get_data(ring, wp); + paddr = xfer->map->dm_segs[0].ds_addr; + + e->ptr = htole64(paddr); + e->dword[0] = htole32((QWZ_PCI_XFER_MAX_DATA_SIZE << + MHI_TRE0_DATA_LEN_SHFT) & + MHI_TRE0_DATA_LEN_MASK); + e->dword[1] = htole32(MHI_TRE1_DATA_IEOT | + MHI_TRE1_DATA_BEI | + MHI_TRE1_DATA_TYPE_TRANSFER << + MHI_TRE1_DATA_TYPE_SHIFT); + + ring->wp = wp; + wp += sizeof(*e); + } + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(ring->dmamem), 0, + QWZ_DMA_LEN(ring->dmamem), BUS_DMASYNC_PREWRITE); + + qwz_mhi_ring_doorbell(sc, ring->db_addr, ring->wp); + } + + return 0; +} + +int +qwz_mhi_start_channels(struct qwz_pci_softc *psc) +{ + struct qwz_pci_xfer_ring *ring; + int ret = 0; + + qwz_mhi_device_wake(&psc->sc_sc); + + ring = &psc->xfer_rings[QWZ_PCI_XFER_RING_IPCR_OUTBOUND]; + if (qwz_mhi_start_channel(psc, ring)) { + ret = 1; + goto done; + } + + ring = &psc->xfer_rings[QWZ_PCI_XFER_RING_IPCR_INBOUND]; + if (qwz_mhi_start_channel(psc, ring)) + ret = 1; +done: + qwz_mhi_device_zzz(&psc->sc_sc); + return ret; +} + +int +qwz_mhi_start(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + uint32_t off; + uint32_t ee, state; + int ret; + + qwz_mhi_init_dev_ctxt(psc); + + psc->bhi_off = qwz_pci_read(sc, MHI_BHI_OFFSET); + DNPRINTF(QWZ_D_MHI, "%s: BHI offset 0x%x\n", __func__, psc->bhi_off); + + psc->bhie_off = qwz_pci_read(sc, MHI_BHIE_OFFSET); + DNPRINTF(QWZ_D_MHI, "%s: BHIE offset 0x%x\n", __func__, psc->bhie_off); + + /* Clean BHIE RX registers */ + for (off = MHI_BHIE_RXVECADDR_LOW_OFFS; + off < (MHI_BHIE_RXVECSTATUS_OFFS - 4); + off += 4) + qwz_pci_write(sc, psc->bhie_off + off, 0x0); + + qwz_rddm_prepare(psc); + + /* Program BHI INTVEC */ + qwz_pci_write(sc, psc->bhi_off + MHI_BHI_INTVEC, 0x00); + + /* + * Get BHI execution environment and confirm that it is valid + * for power on. + */ + ee = qwz_pci_read(sc, psc->bhi_off + MHI_BHI_EXECENV); + if (!MHI_POWER_UP_CAPABLE(ee)) { + printf("%s: invalid EE for power on: 0x%x\n", + sc->sc_dev.dv_xname, ee); + return 1; + } + + /* + * Get MHI state of the device and reset it if it is in system + * error. + */ + state = qwz_pci_read(sc, MHI_STATUS); + DNPRINTF(QWZ_D_MHI, "%s: MHI power on with EE: 0x%x, status: 0x%x\n", + sc->sc_dev.dv_xname, ee, state); + state = (state & MHI_STATUS_MHISTATE_MASK) >> MHI_STATUS_MHISTATE_SHFT; + if (state == MHI_STATE_SYS_ERR) { + if (qwz_mhi_reset_device(sc, 0)) + return 1; + state = qwz_pci_read(sc, MHI_STATUS); + DNPRINTF(QWZ_D_MHI, "%s: MHI state after reset: 0x%x\n", + sc->sc_dev.dv_xname, state); + state = (state & MHI_STATUS_MHISTATE_MASK) >> + MHI_STATUS_MHISTATE_SHFT; + if (state == MHI_STATE_SYS_ERR) { + printf("%s: MHI stuck in system error state\n", + sc->sc_dev.dv_xname); + return 1; + } + } + + psc->bhi_ee = ee; + psc->mhi_state = state; + +#if notyet + /* Enable IRQs */ + // XXX todo? +#endif + + /* Transition to primary runtime. */ + if (MHI_IN_PBL(ee)) { + ret = qwz_mhi_fw_load_handler(psc); + if (ret) + return ret; + + /* XXX without this delay starting the channels may fail */ + delay(1000); + qwz_mhi_start_channels(psc); + } else { + /* XXX Handle partially initialized device...?!? */ + ee = qwz_pci_read(sc, psc->bhi_off + MHI_BHI_EXECENV); + if (!MHI_IN_MISSION_MODE(ee)) { + printf("%s: failed to power up MHI, ee=0x%x\n", + sc->sc_dev.dv_xname, ee); + return EIO; + } + } + + return 0; +} + +void +qwz_mhi_stop(struct qwz_softc *sc) +{ + qwz_mhi_reset_device(sc, 1); +} + +int +qwz_mhi_reset_device(struct qwz_softc *sc, int force) +{ + struct qwz_pci_softc *psc = (struct qwz_pci_softc *)sc; + uint32_t reg; + int ret = 0; + + reg = qwz_pcic_read32(sc, MHI_STATUS); + + DNPRINTF(QWZ_D_MHI, "%s: MHISTATUS 0x%x\n", sc->sc_dev.dv_xname, reg); + /* + * Observed on QCA6390 that after SOC_GLOBAL_RESET, MHISTATUS + * has SYSERR bit set and thus need to set MHICTRL_RESET + * to clear SYSERR. + */ + if (force || (reg & MHI_STATUS_SYSERR_MASK)) { + /* Trigger MHI Reset in device. */ + qwz_pcic_write32(sc, MHI_CTRL, MHI_CTRL_RESET_MASK); + + /* Wait for the reset bit to be cleared by the device. */ + ret = qwz_mhi_await_device_reset(sc); + if (ret) + return ret; + + if (psc->bhi_off == 0) + psc->bhi_off = qwz_pci_read(sc, MHI_BHI_OFFSET); + + /* Device clear BHI INTVEC so re-program it. */ + qwz_pci_write(sc, psc->bhi_off + MHI_BHI_INTVEC, 0x00); + } + + return 0; +} + +static inline void +qwz_mhi_reset_txvecdb(struct qwz_softc *sc) +{ + qwz_pcic_write32(sc, PCIE_TXVECDB, 0); +} + +static inline void +qwz_mhi_reset_txvecstatus(struct qwz_softc *sc) +{ + qwz_pcic_write32(sc, PCIE_TXVECSTATUS, 0); +} + +static inline void +qwz_mhi_reset_rxvecdb(struct qwz_softc *sc) +{ + qwz_pcic_write32(sc, PCIE_RXVECDB, 0); +} + +static inline void +qwz_mhi_reset_rxvecstatus(struct qwz_softc *sc) +{ + qwz_pcic_write32(sc, PCIE_RXVECSTATUS, 0); +} + +void +qwz_mhi_clear_vector(struct qwz_softc *sc) +{ + qwz_mhi_reset_txvecdb(sc); + qwz_mhi_reset_txvecstatus(sc); + qwz_mhi_reset_rxvecdb(sc); + qwz_mhi_reset_rxvecstatus(sc); +} + +int +qwz_mhi_fw_load_handler(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + int ret; + char amss_path[PATH_MAX]; + u_char *data; + size_t len; + + if (sc->fw_img[QWZ_FW_AMSS].data) { + data = sc->fw_img[QWZ_FW_AMSS].data; + len = sc->fw_img[QWZ_FW_AMSS].size; + } else { + ret = snprintf(amss_path, sizeof(amss_path), "%s-%s-%s", + ATH12K_FW_DIR, sc->hw_params.fw.dir, ATH12K_AMSS_FILE); + if (ret < 0 || ret >= sizeof(amss_path)) + return ENOSPC; + + ret = loadfirmware(amss_path, &data, &len); + if (ret) { + printf("%s: could not read %s (error %d)\n", + sc->sc_dev.dv_xname, amss_path, ret); + return ret; + } + + if (len < MHI_DMA_VEC_CHUNK_SIZE) { + printf("%s: %s is too short, have only %zu bytes\n", + sc->sc_dev.dv_xname, amss_path, len); + free(data, M_DEVBUF, len); + return EINVAL; + } + + sc->fw_img[QWZ_FW_AMSS].data = data; + sc->fw_img[QWZ_FW_AMSS].size = len; + } + + /* Second-stage boot loader sits in the first 512 KB of image. */ + ret = qwz_mhi_fw_load_bhi(psc, data, MHI_DMA_VEC_CHUNK_SIZE); + if (ret != 0) { + printf("%s: could not load firmware %s\n", + sc->sc_dev.dv_xname, amss_path); + return ret; + } + + /* Now load the full image. */ + ret = qwz_mhi_fw_load_bhie(psc, data, len); + if (ret != 0) { + printf("%s: could not load firmware %s\n", + sc->sc_dev.dv_xname, amss_path); + return ret; + } + + while (psc->bhi_ee < MHI_EE_AMSS) { + ret = tsleep_nsec(&psc->bhi_ee, 0, "qwzamss", + SEC_TO_NSEC(5)); + if (ret) + break; + } + if (ret != 0) { + printf("%s: device failed to enter AMSS EE\n", + sc->sc_dev.dv_xname); + } + + return ret; +} + +int +qwz_mhi_await_device_reset(struct qwz_softc *sc) +{ + const uint32_t msecs = 24, retries = 2; + uint32_t reg; + int timeout; + + /* Poll for CTRL RESET to clear. */ + timeout = retries; + while (timeout > 0) { + reg = qwz_pci_read(sc, MHI_CTRL); + DNPRINTF(QWZ_D_MHI, "%s: MHI_CTRL is 0x%x\n", __func__, reg); + if ((reg & MHI_CTRL_RESET_MASK) == 0) + break; + DELAY((msecs / retries) * 1000); + timeout--; + } + if (timeout == 0) { + DNPRINTF(QWZ_D_MHI, "%s: MHI reset failed\n", __func__); + return ETIMEDOUT; + } + + return 0; +} + +int +qwz_mhi_await_device_ready(struct qwz_softc *sc) +{ + uint32_t reg; + int timeout; + const uint32_t msecs = 2000, retries = 4; + + + /* Poll for READY to be set. */ + timeout = retries; + while (timeout > 0) { + reg = qwz_pci_read(sc, MHI_STATUS); + DNPRINTF(QWZ_D_MHI, "%s: MHI_STATUS is 0x%x\n", __func__, reg); + if (reg & MHI_STATUS_READY_MASK) { + reg &= ~MHI_STATUS_READY_MASK; + qwz_pci_write(sc, MHI_STATUS, reg); + break; + } + DELAY((msecs / retries) * 1000); + timeout--; + } + if (timeout == 0) { + printf("%s: MHI not ready\n", sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + + return 0; +} + +void +qwz_mhi_ready_state_transition(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + int ret, i; + + ret = qwz_mhi_await_device_reset(sc); + if (ret) + return; + + ret = qwz_mhi_await_device_ready(sc); + if (ret) + return; + + /* Set up memory-mapped IO for channels, events, etc. */ + qwz_mhi_init_mmio(psc); + + /* Notify event rings. */ + for (i = 0; i < nitems(psc->event_rings); i++) { + struct qwz_pci_event_ring *ring = &psc->event_rings[i]; + qwz_mhi_ring_doorbell(sc, ring->db_addr, ring->wp); + } + + /* + * Set the device into M0 state. The device will transition + * into M0 and the execution environment will switch to SBL. + */ + qwz_mhi_set_state(sc, MHI_STATE_M0); +} + +void +qwz_mhi_mission_mode_state_transition(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + int i; + + qwz_mhi_device_wake(sc); + + /* Notify event rings. */ + for (i = 0; i < nitems(psc->event_rings); i++) { + struct qwz_pci_event_ring *ring = &psc->event_rings[i]; + qwz_mhi_ring_doorbell(sc, ring->db_addr, ring->wp); + } + + /* TODO: Notify transfer/command rings? */ + + qwz_mhi_device_zzz(sc); +} + +void +qwz_mhi_low_power_mode_state_transition(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + + qwz_mhi_set_state(sc, MHI_STATE_M2); +} + +void +qwz_mhi_set_state(struct qwz_softc *sc, uint32_t state) +{ + uint32_t reg; + + reg = qwz_pci_read(sc, MHI_CTRL); + + if (state != MHI_STATE_RESET) { + reg &= ~MHI_CTRL_MHISTATE_MASK; + reg |= (state << MHI_CTRL_MHISTATE_SHFT) & MHI_CTRL_MHISTATE_MASK; + } else + reg |= MHI_CTRL_RESET_MASK; + + qwz_pci_write(sc, MHI_CTRL, reg); +} + +void +qwz_mhi_init_mmio(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + uint64_t paddr; + uint32_t reg; + int i; + + reg = qwz_pci_read(sc, MHI_CHDBOFF); + + /* Set device wake doorbell address. */ + psc->wake_db = reg + 8 * MHI_DEV_WAKE_DB; + + /* Set doorbell address for each transfer ring. */ + for (i = 0; i < nitems(psc->xfer_rings); i++) { + struct qwz_pci_xfer_ring *ring = &psc->xfer_rings[i]; + ring->db_addr = reg + (8 * ring->mhi_chan_id); + } + + reg = qwz_pci_read(sc, MHI_ERDBOFF); + /* Set doorbell address for each event ring. */ + for (i = 0; i < nitems(psc->event_rings); i++) { + struct qwz_pci_event_ring *ring = &psc->event_rings[i]; + ring->db_addr = reg + (8 * i); + } + + paddr = QWZ_DMA_DVA(psc->chan_ctxt); + qwz_pci_write(sc, MHI_CCABAP_HIGHER, paddr >> 32); + qwz_pci_write(sc, MHI_CCABAP_LOWER, paddr & 0xffffffff); + + paddr = QWZ_DMA_DVA(psc->event_ctxt); + qwz_pci_write(sc, MHI_ECABAP_HIGHER, paddr >> 32); + qwz_pci_write(sc, MHI_ECABAP_LOWER, paddr & 0xffffffff); + + paddr = QWZ_DMA_DVA(psc->cmd_ctxt); + qwz_pci_write(sc, MHI_CRCBAP_HIGHER, paddr >> 32); + qwz_pci_write(sc, MHI_CRCBAP_LOWER, paddr & 0xffffffff); + + /* Not (yet?) using fixed memory space from a device-tree. */ + qwz_pci_write(sc, MHI_CTRLBASE_HIGHER, 0); + qwz_pci_write(sc, MHI_CTRLBASE_LOWER, 0); + qwz_pci_write(sc, MHI_DATABASE_HIGHER, 0); + qwz_pci_write(sc, MHI_DATABASE_LOWER, 0); + qwz_pci_write(sc, MHI_CTRLLIMIT_HIGHER, 0x0); + qwz_pci_write(sc, MHI_CTRLLIMIT_LOWER, 0xffffffff); + qwz_pci_write(sc, MHI_DATALIMIT_HIGHER, 0x0); + qwz_pci_write(sc, MHI_DATALIMIT_LOWER, 0xffffffff); + + reg = qwz_pci_read(sc, MHI_CFG); + reg &= ~(MHI_CFG_NER_MASK | MHI_CFG_NHWER_MASK); + reg |= QWZ_NUM_EVENT_CTX << MHI_CFG_NER_SHFT; + qwz_pci_write(sc, MHI_CFG, reg); +} + +int +qwz_mhi_fw_load_bhi(struct qwz_pci_softc *psc, uint8_t *data, size_t len) +{ + struct qwz_softc *sc = &psc->sc_sc; + struct qwz_dmamem *data_adm; + uint32_t seq, reg, status = MHI_BHI_STATUS_RESET; + uint64_t paddr; + int ret; + + data_adm = qwz_dmamem_alloc(sc->sc_dmat, len, 0); + if (data_adm == NULL) { + printf("%s: could not allocate BHI DMA data buffer\n", + sc->sc_dev.dv_xname); + return 1; + } + + /* Copy firmware image to DMA memory. */ + memcpy(QWZ_DMA_KVA(data_adm), data, len); + + qwz_pci_write(sc, psc->bhi_off + MHI_BHI_STATUS, 0); + + /* Set data physical address and length. */ + paddr = QWZ_DMA_DVA(data_adm); + qwz_pci_write(sc, psc->bhi_off + MHI_BHI_IMGADDR_HIGH, paddr >> 32); + qwz_pci_write(sc, psc->bhi_off + MHI_BHI_IMGADDR_LOW, + paddr & 0xffffffff); + qwz_pci_write(sc, psc->bhi_off + MHI_BHI_IMGSIZE, len); + + /* Set a random transaction sequence number. */ + do { + seq = arc4random_uniform(MHI_BHI_TXDB_SEQNUM_BMSK); + } while (seq == 0); + qwz_pci_write(sc, psc->bhi_off + MHI_BHI_IMGTXDB, seq); + + /* Wait for completion. */ + ret = 0; + while (status != MHI_BHI_STATUS_SUCCESS && psc->bhi_ee < MHI_EE_SBL) { + ret = tsleep_nsec(&psc->bhi_ee, 0, "qwzbhi", SEC_TO_NSEC(5)); + if (ret) + break; + reg = qwz_pci_read(sc, psc->bhi_off + MHI_BHI_STATUS); + status = (reg & MHI_BHI_STATUS_MASK) >> MHI_BHI_STATUS_SHFT; + } + + if (ret) { + printf("%s: BHI load timeout\n", sc->sc_dev.dv_xname); + reg = qwz_pci_read(sc, psc->bhi_off + MHI_BHI_STATUS); + status = (reg & MHI_BHI_STATUS_MASK) >> MHI_BHI_STATUS_SHFT; + DNPRINTF(QWZ_D_MHI, "%s: BHI status is 0x%x EE is 0x%x\n", + __func__, status, psc->bhi_ee); + } + + qwz_dmamem_free(sc->sc_dmat, data_adm); + return ret; +} + +int +qwz_mhi_fw_load_bhie(struct qwz_pci_softc *psc, uint8_t *data, size_t len) +{ + struct qwz_softc *sc = &psc->sc_sc; + struct qwz_dma_vec_entry *vec; + uint32_t seq, reg, state = MHI_BHIE_TXVECSTATUS_STATUS_RESET; + uint64_t paddr; + const size_t chunk_size = MHI_DMA_VEC_CHUNK_SIZE; + size_t nseg, remain, vec_size; + int i, ret; + + nseg = howmany(len, chunk_size); + if (nseg == 0) { + printf("%s: BHIE data too short, have only %zu bytes\n", + sc->sc_dev.dv_xname, len); + return 1; + } + + if (psc->amss_data == NULL || QWZ_DMA_LEN(psc->amss_data) < len) { + if (psc->amss_data) + qwz_dmamem_free(sc->sc_dmat, psc->amss_data); + psc->amss_data = qwz_dmamem_alloc(sc->sc_dmat, len, 0); + if (psc->amss_data == NULL) { + printf("%s: could not allocate BHIE DMA data buffer\n", + sc->sc_dev.dv_xname); + return 1; + } + } + + vec_size = nseg * sizeof(*vec); + if (psc->amss_vec == NULL || QWZ_DMA_LEN(psc->amss_vec) < vec_size) { + if (psc->amss_vec) + qwz_dmamem_free(sc->sc_dmat, psc->amss_vec); + psc->amss_vec = qwz_dmamem_alloc(sc->sc_dmat, vec_size, 0); + if (psc->amss_vec == NULL) { + printf("%s: could not allocate BHIE DMA vec buffer\n", + sc->sc_dev.dv_xname); + qwz_dmamem_free(sc->sc_dmat, psc->amss_data); + psc->amss_data = NULL; + return 1; + } + } + + /* Copy firmware image to DMA memory. */ + memcpy(QWZ_DMA_KVA(psc->amss_data), data, len); + + /* Create vector which controls chunk-wise DMA copy in hardware. */ + paddr = QWZ_DMA_DVA(psc->amss_data); + vec = QWZ_DMA_KVA(psc->amss_vec); + remain = len; + for (i = 0; i < nseg; i++) { + vec[i].paddr = paddr; + if (remain >= chunk_size) { + vec[i].size = chunk_size; + remain -= chunk_size; + paddr += chunk_size; + } else + vec[i].size = remain; + } + + /* Set vector physical address and length. */ + paddr = QWZ_DMA_DVA(psc->amss_vec); + qwz_pci_write(sc, psc->bhie_off + MHI_BHIE_TXVECADDR_HIGH_OFFS, + paddr >> 32); + qwz_pci_write(sc, psc->bhie_off + MHI_BHIE_TXVECADDR_LOW_OFFS, + paddr & 0xffffffff); + qwz_pci_write(sc, psc->bhie_off + MHI_BHIE_TXVECSIZE_OFFS, vec_size); + + /* Set a random transaction sequence number. */ + do { + seq = arc4random_uniform(MHI_BHIE_TXVECSTATUS_SEQNUM_BMSK); + } while (seq == 0); + reg = qwz_pci_read(sc, psc->bhie_off + MHI_BHIE_TXVECDB_OFFS); + reg &= ~MHI_BHIE_TXVECDB_SEQNUM_BMSK; + reg |= seq << MHI_BHIE_TXVECDB_SEQNUM_SHFT; + qwz_pci_write(sc, psc->bhie_off + MHI_BHIE_TXVECDB_OFFS, reg); + + /* Wait for completion. */ + ret = 0; + while (state != MHI_BHIE_TXVECSTATUS_STATUS_XFER_COMPL) { + ret = tsleep_nsec(&psc->bhie_off, 0, "qwzbhie", + SEC_TO_NSEC(5)); + if (ret) + break; + reg = qwz_pci_read(sc, + psc->bhie_off + MHI_BHIE_TXVECSTATUS_OFFS); + state = (reg & MHI_BHIE_TXVECSTATUS_STATUS_BMSK) >> + MHI_BHIE_TXVECSTATUS_STATUS_SHFT; + DNPRINTF(QWZ_D_MHI, "%s: txvec state is 0x%x\n", __func__, + state); + } + + if (ret) { + printf("%s: BHIE load timeout\n", sc->sc_dev.dv_xname); + return ret; + } + return 0; +} + +void +qwz_rddm_prepare(struct qwz_pci_softc *psc) +{ + struct qwz_softc *sc = &psc->sc_sc; + struct qwz_dma_vec_entry *vec; + struct qwz_dmamem *data_adm, *vec_adm; + uint32_t seq, reg; + uint64_t paddr; + const size_t len = QWZ_RDDM_DUMP_SIZE; + const size_t chunk_size = MHI_DMA_VEC_CHUNK_SIZE; + size_t nseg, remain, vec_size; + int i; + + nseg = howmany(len, chunk_size); + if (nseg == 0) { + printf("%s: RDDM data too short, have only %zu bytes\n", + sc->sc_dev.dv_xname, len); + return; + } + + data_adm = qwz_dmamem_alloc(sc->sc_dmat, len, 0); + if (data_adm == NULL) { + printf("%s: could not allocate BHIE DMA data buffer\n", + sc->sc_dev.dv_xname); + return; + } + + vec_size = nseg * sizeof(*vec); + vec_adm = qwz_dmamem_alloc(sc->sc_dmat, vec_size, 0); + if (vec_adm == NULL) { + printf("%s: could not allocate BHIE DMA vector buffer\n", + sc->sc_dev.dv_xname); + qwz_dmamem_free(sc->sc_dmat, data_adm); + return; + } + + /* Create vector which controls chunk-wise DMA copy from hardware. */ + paddr = QWZ_DMA_DVA(data_adm); + vec = QWZ_DMA_KVA(vec_adm); + remain = len; + for (i = 0; i < nseg; i++) { + vec[i].paddr = paddr; + if (remain >= chunk_size) { + vec[i].size = chunk_size; + remain -= chunk_size; + paddr += chunk_size; + } else + vec[i].size = remain; + } + + /* Set vector physical address and length. */ + paddr = QWZ_DMA_DVA(vec_adm); + qwz_pci_write(sc, psc->bhie_off + MHI_BHIE_RXVECADDR_HIGH_OFFS, + paddr >> 32); + qwz_pci_write(sc, psc->bhie_off + MHI_BHIE_RXVECADDR_LOW_OFFS, + paddr & 0xffffffff); + qwz_pci_write(sc, psc->bhie_off + MHI_BHIE_RXVECSIZE_OFFS, vec_size); + + /* Set a random transaction sequence number. */ + do { + seq = arc4random_uniform(MHI_BHIE_RXVECSTATUS_SEQNUM_BMSK); + } while (seq == 0); + + reg = qwz_pci_read(sc, psc->bhie_off + MHI_BHIE_RXVECDB_OFFS); + reg &= ~MHI_BHIE_RXVECDB_SEQNUM_BMSK; + reg |= seq << MHI_BHIE_RXVECDB_SEQNUM_SHFT; + qwz_pci_write(sc, psc->bhie_off + MHI_BHIE_RXVECDB_OFFS, reg); + + psc->rddm_data = data_adm; + psc->rddm_vec = vec_adm; +} + +#ifdef QWZ_DEBUG +void +qwz_rddm_task(void *arg) +{ + struct qwz_pci_softc *psc = arg; + struct qwz_softc *sc = &psc->sc_sc; + uint32_t reg, state = MHI_BHIE_RXVECSTATUS_STATUS_RESET; + const size_t len = QWZ_RDDM_DUMP_SIZE; + int i, timeout; + const uint32_t msecs = 100, retries = 20; + uint8_t *rddm; + struct nameidata nd; + struct vnode *vp = NULL; + struct iovec iov[3]; + struct uio uio; + char path[PATH_MAX]; + int error = 0; + + if (psc->rddm_data == NULL) { + DPRINTF("%s: RDDM not prepared\n", __func__); + return; + } + + /* Poll for completion */ + timeout = retries; + while (timeout > 0 && state != MHI_BHIE_RXVECSTATUS_STATUS_XFER_COMPL) { + reg = qwz_pci_read(sc, + psc->bhie_off + MHI_BHIE_RXVECSTATUS_OFFS); + state = (reg & MHI_BHIE_RXVECSTATUS_STATUS_BMSK) >> + MHI_BHIE_RXVECSTATUS_STATUS_SHFT; + DPRINTF("%s: txvec state is 0x%x\n", __func__, state); + DELAY((msecs / retries) * 1000); + timeout--; + } + + if (timeout == 0) { + DPRINTF("%s: RDDM dump failed\n", sc->sc_dev.dv_xname); + return; + } + + rddm = QWZ_DMA_KVA(psc->rddm_data); + DPRINTF("%s: RDDM snippet:\n", __func__); + for (i = 0; i < MIN(64, len); i++) { + DPRINTF("%s %.2x", i % 16 == 0 ? "\n" : "", rddm[i]); + } + DPRINTF("\n"); + + DPRINTF("%s: sleeping for 30 seconds to allow userland to boot\n", __func__); + tsleep_nsec(&psc->rddm_data, 0, "qwzrddm", SEC_TO_NSEC(30)); + + snprintf(path, sizeof(path), "/root/%s-rddm.bin", sc->sc_dev.dv_xname); + DPRINTF("%s: saving RDDM to %s\n", __func__, path); + NDINIT(&nd, 0, 0, UIO_SYSSPACE, path, curproc); + nd.ni_pledge = PLEDGE_CPATH | PLEDGE_WPATH; + nd.ni_unveil = UNVEIL_CREATE | UNVEIL_WRITE; + error = vn_open(&nd, FWRITE | O_CREAT | O_NOFOLLOW | O_TRUNC, + S_IRUSR | S_IWUSR); + if (error) { + DPRINTF("%s: vn_open: error %d\n", __func__, error); + goto done; + } + vp = nd.ni_vp; + VOP_UNLOCK(vp); + + iov[0].iov_base = (void *)rddm; + iov[0].iov_len = len; + iov[1].iov_len = 0; + uio.uio_iov = &iov[0]; + uio.uio_offset = 0; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_WRITE; + uio.uio_resid = len; + uio.uio_iovcnt = 1; + uio.uio_procp = curproc; + error = vget(vp, LK_EXCLUSIVE | LK_RETRY); + if (error) { + DPRINTF("%s: vget: error %d\n", __func__, error); + goto done; + } + error = VOP_WRITE(vp, &uio, IO_UNIT|IO_APPEND, curproc->p_ucred); + vput(vp); + if (error) + DPRINTF("%s: VOP_WRITE: error %d\n", __func__, error); + #if 0 + error = vn_close(vp, FWRITE, curproc->p_ucred, curproc); + if (error) + DPRINTF("%s: vn_close: error %d\n", __func__, error); + #endif +done: + qwz_dmamem_free(sc->sc_dmat, psc->rddm_data); + qwz_dmamem_free(sc->sc_dmat, psc->rddm_vec); + psc->rddm_data = NULL; + psc->rddm_vec = NULL; + DPRINTF("%s: done, error %d\n", __func__, error); +} +#endif + +void * +qwz_pci_event_ring_get_elem(struct qwz_pci_event_ring *ring, uint64_t rp) +{ + uint64_t base = QWZ_DMA_DVA(ring->dmamem), offset; + void *addr = QWZ_DMA_KVA(ring->dmamem); + + if (rp < base) + return NULL; + + offset = rp - base; + if (offset >= ring->size) + return NULL; + + return addr + offset; +} + +void +qwz_mhi_state_change(struct qwz_pci_softc *psc, int ee, int mhi_state) +{ + struct qwz_softc *sc = &psc->sc_sc; + uint32_t old_ee = psc->bhi_ee; + uint32_t old_mhi_state = psc->mhi_state; + + if (ee != -1 && psc->bhi_ee != ee) { + switch (ee) { + case MHI_EE_PBL: + DNPRINTF(QWZ_D_MHI, "%s: new EE PBL\n", + sc->sc_dev.dv_xname); + psc->bhi_ee = ee; + break; + case MHI_EE_SBL: + psc->bhi_ee = ee; + DNPRINTF(QWZ_D_MHI, "%s: new EE SBL\n", + sc->sc_dev.dv_xname); + break; + case MHI_EE_AMSS: + DNPRINTF(QWZ_D_MHI, "%s: new EE AMSS\n", + sc->sc_dev.dv_xname); + psc->bhi_ee = ee; + /* Wake thread loading the full AMSS image. */ + wakeup(&psc->bhie_off); + break; + case MHI_EE_WFW: + DNPRINTF(QWZ_D_MHI, "%s: new EE WFW\n", + sc->sc_dev.dv_xname); + psc->bhi_ee = ee; + break; + default: + printf("%s: unhandled EE change to %x\n", + sc->sc_dev.dv_xname, ee); + break; + } + } + + if (mhi_state != -1 && psc->mhi_state != mhi_state) { + switch (mhi_state) { + case -1: + break; + case MHI_STATE_RESET: + DNPRINTF(QWZ_D_MHI, "%s: new MHI state RESET\n", + sc->sc_dev.dv_xname); + psc->mhi_state = mhi_state; + break; + case MHI_STATE_READY: + DNPRINTF(QWZ_D_MHI, "%s: new MHI state READY\n", + sc->sc_dev.dv_xname); + psc->mhi_state = mhi_state; + qwz_mhi_ready_state_transition(psc); + break; + case MHI_STATE_M0: + DNPRINTF(QWZ_D_MHI, "%s: new MHI state M0\n", + sc->sc_dev.dv_xname); + psc->mhi_state = mhi_state; + qwz_mhi_mission_mode_state_transition(psc); + break; + case MHI_STATE_M1: + DNPRINTF(QWZ_D_MHI, "%s: new MHI state M1\n", + sc->sc_dev.dv_xname); + psc->mhi_state = mhi_state; + qwz_mhi_low_power_mode_state_transition(psc); + break; + case MHI_STATE_SYS_ERR: + DNPRINTF(QWZ_D_MHI, + "%s: new MHI state SYS ERR\n", + sc->sc_dev.dv_xname); + psc->mhi_state = mhi_state; + break; + default: + printf("%s: unhandled MHI state change to %x\n", + sc->sc_dev.dv_xname, mhi_state); + break; + } + } + + if (old_ee != psc->bhi_ee) + wakeup(&psc->bhi_ee); + if (old_mhi_state != psc->mhi_state) + wakeup(&psc->mhi_state); +} + +void +qwz_pci_intr_ctrl_event_mhi(struct qwz_pci_softc *psc, uint32_t mhi_state) +{ + DNPRINTF(QWZ_D_MHI, "%s: MHI state change 0x%x -> 0x%x\n", __func__, + psc->mhi_state, mhi_state); + + if (psc->mhi_state != mhi_state) + qwz_mhi_state_change(psc, -1, mhi_state); +} + +void +qwz_pci_intr_ctrl_event_ee(struct qwz_pci_softc *psc, uint32_t ee) +{ + DNPRINTF(QWZ_D_MHI, "%s: EE change 0x%x to 0x%x\n", __func__, + psc->bhi_ee, ee); + + if (psc->bhi_ee != ee) + qwz_mhi_state_change(psc, ee, -1); +} + +void +qwz_pci_intr_ctrl_event_cmd_complete(struct qwz_pci_softc *psc, + uint64_t ptr, uint32_t cmd_status) +{ + struct qwz_pci_cmd_ring *cmd_ring = &psc->cmd_ring; + uint64_t base = QWZ_DMA_DVA(cmd_ring->dmamem); + struct qwz_pci_xfer_ring *xfer_ring = NULL; + struct qwz_mhi_ring_element *e; + uint32_t tre1, chid; + size_t i; + + e = qwz_pci_cmd_ring_get_elem(cmd_ring, ptr); + if (e == NULL) + return; + + tre1 = le32toh(e->dword[1]); + chid = (tre1 & MHI_TRE1_EV_CHID_MASK) >> MHI_TRE1_EV_CHID_SHFT; + + for (i = 0; i < nitems(psc->xfer_rings); i++) { + if (psc->xfer_rings[i].mhi_chan_id == chid) { + xfer_ring = &psc->xfer_rings[i]; + break; + } + } + if (xfer_ring == NULL) { + printf("%s: no transfer ring found for command completion " + "on channel %u\n", __func__, chid); + return; + } + + xfer_ring->cmd_status = cmd_status; + wakeup(&xfer_ring->cmd_status); + + if (cmd_ring->rp + sizeof(*e) >= base + cmd_ring->size) + cmd_ring->rp = base; + else + cmd_ring->rp += sizeof(*e); +} + +int +qwz_pci_intr_ctrl_event(struct qwz_pci_softc *psc, struct qwz_pci_event_ring *ring) +{ + struct qwz_softc *sc = &psc->sc_sc; + struct qwz_mhi_event_ctxt *c; + uint64_t rp, wp, base; + struct qwz_mhi_ring_element *e; + uint32_t tre0, tre1, type, code, chid, len; + + c = ring->event_ctxt; + if (c == NULL) { + /* + * Interrupts can trigger before mhi_init_event_rings() + * if the device is still active after a warm reboot. + */ + return 0; + } + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(psc->event_ctxt), 0, + QWZ_DMA_LEN(psc->event_ctxt), BUS_DMASYNC_POSTREAD); + + rp = le64toh(c->rp); + wp = le64toh(c->wp); + + DNPRINTF(QWZ_D_MHI, "%s: kernel rp=0x%llx\n", __func__, ring->rp); + DNPRINTF(QWZ_D_MHI, "%s: device rp=0x%llx\n", __func__, rp); + DNPRINTF(QWZ_D_MHI, "%s: kernel wp=0x%llx\n", __func__, ring->wp); + DNPRINTF(QWZ_D_MHI, "%s: device wp=0x%llx\n", __func__, wp); + + base = QWZ_DMA_DVA(ring->dmamem); + if (ring->rp == rp || rp < base || rp >= base + ring->size) + return 0; + if (wp < base || wp >= base + ring->size) + return 0; + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(ring->dmamem), + 0, QWZ_DMA_LEN(ring->dmamem), BUS_DMASYNC_POSTREAD); + + while (ring->rp != rp) { + e = qwz_pci_event_ring_get_elem(ring, ring->rp); + if (e == NULL) + return 0; + + tre0 = le32toh(e->dword[0]); + tre1 = le32toh(e->dword[1]); + + len = (tre0 & MHI_TRE0_EV_LEN_MASK) >> MHI_TRE0_EV_LEN_SHFT; + code = (tre0 & MHI_TRE0_EV_CODE_MASK) >> MHI_TRE0_EV_CODE_SHFT; + type = (tre1 & MHI_TRE1_EV_TYPE_MASK) >> MHI_TRE1_EV_TYPE_SHFT; + chid = (tre1 & MHI_TRE1_EV_CHID_MASK) >> MHI_TRE1_EV_CHID_SHFT; + DNPRINTF(QWZ_D_MHI, "%s: len=%u code=0x%x type=0x%x chid=%d\n", + __func__, len, code, type, chid); + + switch (type) { + case MHI_PKT_TYPE_STATE_CHANGE_EVENT: + qwz_pci_intr_ctrl_event_mhi(psc, code); + break; + case MHI_PKT_TYPE_EE_EVENT: + qwz_pci_intr_ctrl_event_ee(psc, code); + break; + case MHI_PKT_TYPE_CMD_COMPLETION_EVENT: + qwz_pci_intr_ctrl_event_cmd_complete(psc, + le64toh(e->ptr), code); + break; + default: + printf("%s: unhandled event type 0x%x\n", + __func__, type); + break; + } + + if (ring->rp + sizeof(*e) >= base + ring->size) + ring->rp = base; + else + ring->rp += sizeof(*e); + + if (ring->wp + sizeof(*e) >= base + ring->size) + ring->wp = base; + else + ring->wp += sizeof(*e); + } + + c->wp = htole64(ring->wp); + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(psc->event_ctxt), 0, + QWZ_DMA_LEN(psc->event_ctxt), BUS_DMASYNC_PREWRITE); + + qwz_mhi_ring_doorbell(sc, ring->db_addr, ring->wp); + return 1; +} + +void +qwz_pci_intr_data_event_tx(struct qwz_pci_softc *psc, struct qwz_mhi_ring_element *e) +{ + struct qwz_softc *sc = &psc->sc_sc; + struct qwz_pci_xfer_ring *ring; + struct qwz_xfer_data *xfer; + uint64_t rp, evrp, base, paddr; + uint32_t tre0, tre1, code, chid, evlen, len; + int i; + + tre0 = le32toh(e->dword[0]); + tre1 = le32toh(e->dword[1]); + + evlen = (tre0 & MHI_TRE0_EV_LEN_MASK) >> MHI_TRE0_EV_LEN_SHFT; + code = (tre0 & MHI_TRE0_EV_CODE_MASK) >> MHI_TRE0_EV_CODE_SHFT; + chid = (tre1 & MHI_TRE1_EV_CHID_MASK) >> MHI_TRE1_EV_CHID_SHFT; + + switch (code) { + case MHI_EV_CC_EOT: + for (i = 0; i < nitems(psc->xfer_rings); i++) { + ring = &psc->xfer_rings[i]; + if (ring->mhi_chan_id == chid) + break; + } + if (i == nitems(psc->xfer_rings)) { + printf("%s: unhandled channel 0x%x\n", + __func__, chid); + break; + } + base = QWZ_DMA_DVA(ring->dmamem); + /* PTR contains the entry that was last written */ + evrp = letoh64(e->ptr); + rp = evrp; + if (rp < base || rp >= base + ring->size) { + printf("%s: invalid ptr 0x%llx\n", + __func__, rp); + break; + } + /* Point rp to next empty slot */ + if (rp + sizeof(*e) >= base + ring->size) + rp = base; + else + rp += sizeof(*e); + /* Parse until next empty slot */ + while (ring->rp != rp) { + DNPRINTF(QWZ_D_MHI, "%s:%d: ring->rp 0x%llx " + "ring->wp 0x%llx rp 0x%llx\n", __func__, + __LINE__, ring->rp, ring->wp, rp); + e = qwz_pci_xfer_ring_get_elem(ring, ring->rp); + xfer = qwz_pci_xfer_ring_get_data(ring, ring->rp); + + if (ring->rp == evrp) + len = evlen; + else + len = xfer->m->m_pkthdr.len; + + bus_dmamap_sync(sc->sc_dmat, xfer->map, 0, + xfer->m->m_pkthdr.len, BUS_DMASYNC_POSTREAD); +#ifdef QWZ_DEBUG + { + int i; + DNPRINTF(QWZ_D_MHI, "%s: chan %u data (len %u): ", + __func__, + ring->mhi_chan_id, len); + for (i = 0; i < MIN(32, len); i++) { + DNPRINTF(QWZ_D_MHI, "%02x ", + (unsigned char)mtod(xfer->m, caddr_t)[i]); + } + if (i < len) + DNPRINTF(QWZ_D_MHI, "..."); + DNPRINTF(QWZ_D_MHI, "\n"); + } +#endif + if (ring->mhi_chan_direction == MHI_CHAN_TYPE_INBOUND) { + /* Save m_data as upper layers use m_adj(9) */ + void *o_data = xfer->m->m_data; + + /* Pass mbuf to upper layers */ + qwz_qrtr_recv_msg(sc, xfer->m); + + /* Reset RX mbuf instead of free/alloc */ + KASSERT(xfer->m->m_next == NULL); + xfer->m->m_data = o_data; + xfer->m->m_len = xfer->m->m_pkthdr.len = + QWZ_PCI_XFER_MAX_DATA_SIZE; + + paddr = xfer->map->dm_segs[0].ds_addr; + + e->ptr = htole64(paddr); + e->dword[0] = htole32(( + QWZ_PCI_XFER_MAX_DATA_SIZE << + MHI_TRE0_DATA_LEN_SHFT) & + MHI_TRE0_DATA_LEN_MASK); + e->dword[1] = htole32(MHI_TRE1_DATA_IEOT | + MHI_TRE1_DATA_BEI | + MHI_TRE1_DATA_TYPE_TRANSFER << + MHI_TRE1_DATA_TYPE_SHIFT); + + if (ring->wp + sizeof(*e) >= base + ring->size) + ring->wp = base; + else + ring->wp += sizeof(*e); + } else { + /* Unload and free TX mbuf */ + bus_dmamap_unload(sc->sc_dmat, xfer->map); + m_freem(xfer->m); + xfer->m = NULL; + ring->queued--; + } + + if (ring->rp + sizeof(*e) >= base + ring->size) + ring->rp = base; + else + ring->rp += sizeof(*e); + } + + if (ring->mhi_chan_direction == MHI_CHAN_TYPE_INBOUND) { + ring->chan_ctxt->wp = htole64(ring->wp); + + bus_dmamap_sync(sc->sc_dmat, + QWZ_DMA_MAP(psc->chan_ctxt), 0, + QWZ_DMA_LEN(psc->chan_ctxt), + BUS_DMASYNC_PREWRITE); + + qwz_mhi_ring_doorbell(sc, ring->db_addr, ring->wp); + } + break; + default: + printf("%s: unhandled event code 0x%x\n", + __func__, code); + } +} + +int +qwz_pci_intr_data_event(struct qwz_pci_softc *psc, struct qwz_pci_event_ring *ring) +{ + struct qwz_softc *sc = &psc->sc_sc; + struct qwz_mhi_event_ctxt *c; + uint64_t rp, wp, base; + struct qwz_mhi_ring_element *e; + uint32_t tre0, tre1, type, code, chid, len; + + c = ring->event_ctxt; + if (c == NULL) { + /* + * Interrupts can trigger before mhi_init_event_rings() + * if the device is still active after a warm reboot. + */ + return 0; + } + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(psc->event_ctxt), 0, + QWZ_DMA_LEN(psc->event_ctxt), BUS_DMASYNC_POSTREAD); + + rp = le64toh(c->rp); + wp = le64toh(c->wp); + + DNPRINTF(QWZ_D_MHI, "%s: kernel rp=0x%llx\n", __func__, ring->rp); + DNPRINTF(QWZ_D_MHI, "%s: device rp=0x%llx\n", __func__, rp); + DNPRINTF(QWZ_D_MHI, "%s: kernel wp=0x%llx\n", __func__, ring->wp); + DNPRINTF(QWZ_D_MHI, "%s: device wp=0x%llx\n", __func__, wp); + + base = QWZ_DMA_DVA(ring->dmamem); + if (ring->rp == rp || rp < base || rp >= base + ring->size) + return 0; + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(ring->dmamem), + 0, QWZ_DMA_LEN(ring->dmamem), BUS_DMASYNC_POSTREAD); + + while (ring->rp != rp) { + e = qwz_pci_event_ring_get_elem(ring, ring->rp); + if (e == NULL) + return 0; + + tre0 = le32toh(e->dword[0]); + tre1 = le32toh(e->dword[1]); + + len = (tre0 & MHI_TRE0_EV_LEN_MASK) >> MHI_TRE0_EV_LEN_SHFT; + code = (tre0 & MHI_TRE0_EV_CODE_MASK) >> MHI_TRE0_EV_CODE_SHFT; + type = (tre1 & MHI_TRE1_EV_TYPE_MASK) >> MHI_TRE1_EV_TYPE_SHFT; + chid = (tre1 & MHI_TRE1_EV_CHID_MASK) >> MHI_TRE1_EV_CHID_SHFT; + DNPRINTF(QWZ_D_MHI, "%s: len=%u code=0x%x type=0x%x chid=%d\n", + __func__, len, code, type, chid); + + switch (type) { + case MHI_PKT_TYPE_TX_EVENT: + qwz_pci_intr_data_event_tx(psc, e); + break; + default: + printf("%s: unhandled event type 0x%x\n", + __func__, type); + break; + } + + if (ring->rp + sizeof(*e) >= base + ring->size) + ring->rp = base; + else + ring->rp += sizeof(*e); + + if (ring->wp + sizeof(*e) >= base + ring->size) + ring->wp = base; + else + ring->wp += sizeof(*e); + } + + c->wp = htole64(ring->wp); + + bus_dmamap_sync(sc->sc_dmat, QWZ_DMA_MAP(psc->event_ctxt), 0, + QWZ_DMA_LEN(psc->event_ctxt), BUS_DMASYNC_PREWRITE); + + qwz_mhi_ring_doorbell(sc, ring->db_addr, ring->wp); + return 1; +} + +int +qwz_pci_intr_mhi_ctrl(void *arg) +{ + struct qwz_pci_softc *psc = arg; + + if (qwz_pci_intr_ctrl_event(psc, &psc->event_rings[0])) + return 1; + + return 0; +} + +int +qwz_pci_intr_mhi_data(void *arg) +{ + struct qwz_pci_softc *psc = arg; + + if (qwz_pci_intr_data_event(psc, &psc->event_rings[1])) + return 1; + + return 0; +} + +int +qwz_pci_intr(void *arg) +{ + struct qwz_pci_softc *psc = arg; + struct qwz_softc *sc = (void *)psc; + uint32_t ee, state; + int ret = 0; + + /* + * Interrupts can trigger before mhi_start() during boot if the device + * is still active after a warm reboot. + */ + if (psc->bhi_off == 0) + psc->bhi_off = qwz_pci_read(sc, MHI_BHI_OFFSET); + + ee = qwz_pci_read(sc, psc->bhi_off + MHI_BHI_EXECENV); + state = qwz_pci_read(sc, MHI_STATUS); + state = (state & MHI_STATUS_MHISTATE_MASK) >> + MHI_STATUS_MHISTATE_SHFT; + + DNPRINTF(QWZ_D_MHI, + "%s: BHI interrupt with EE: 0x%x -> 0x%x state: 0x%x -> 0x%x\n", + sc->sc_dev.dv_xname, psc->bhi_ee, ee, psc->mhi_state, state); + + if (ee == MHI_EE_RDDM) { + /* Firmware crash, e.g. due to invalid DMA memory access. */ + psc->bhi_ee = ee; +#ifdef QWZ_DEBUG + if (!psc->rddm_triggered) { + /* Write fw memory dump to root's home directory. */ + task_add(systq, &psc->rddm_task); + psc->rddm_triggered = 1; + } +#else + printf("%s: fatal firmware error\n", + sc->sc_dev.dv_xname); + if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags) && + (sc->sc_ic.ic_if.if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) { + /* Try to reset the device. */ + set_bit(ATH12K_FLAG_CRASH_FLUSH, sc->sc_flags); + task_add(systq, &sc->init_task); + } +#endif + return 1; + } else if (psc->bhi_ee == MHI_EE_PBL || psc->bhi_ee == MHI_EE_SBL) { + int new_ee = -1, new_mhi_state = -1; + + if (psc->bhi_ee != ee) + new_ee = ee; + + if (psc->mhi_state != state) + new_mhi_state = state; + + if (new_ee != -1 || new_mhi_state != -1) + qwz_mhi_state_change(psc, new_ee, new_mhi_state); + + ret = 1; + } + + if (!test_bit(ATH12K_FLAG_MULTI_MSI_VECTORS, sc->sc_flags)) { + int i; + + if (qwz_pci_intr_ctrl_event(psc, &psc->event_rings[0])) + ret = 1; + if (qwz_pci_intr_data_event(psc, &psc->event_rings[1])) + ret = 1; + + for (i = 0; i < sc->hw_params.ce_count; i++) { + struct qwz_ce_pipe *ce_pipe = &sc->ce.ce_pipe[i]; + + if (qwz_ce_intr(ce_pipe)) + ret = 1; + } + + if (test_bit(ATH12K_FLAG_EXT_IRQ_ENABLED, sc->sc_flags)) { + for (i = 0; i < nitems(sc->ext_irq_grp); i++) { + if (qwz_dp_service_srng(sc, i)) + ret = 1; + } + } + } + + return ret; +} diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index c000c90c6..77609f052 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sysctl.c,v 1.437 2024/08/11 15:10:53 mvs Exp $ */ +/* $OpenBSD: kern_sysctl.c,v 1.439 2024/08/14 17:52:47 mvs Exp $ */ /* $NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $ */ /*- @@ -252,6 +252,7 @@ sys_sysctl(struct proc *p, void *v, register_t *retval) fn = uvm_sysctl; break; case CTL_NET: + dolock = 0; fn = net_sysctl; break; case CTL_FS: @@ -306,7 +307,7 @@ char hostname[MAXHOSTNAMELEN]; int hostnamelen; char domainname[MAXHOSTNAMELEN]; int domainnamelen; -long hostid; +int hostid; char *disknames = NULL; size_t disknameslen; struct diskstats *diskstats = NULL; @@ -507,6 +508,8 @@ kern_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, return (sysctl_rdstring(oldp, oldlenp, newp, version)); case KERN_NUMVNODES: /* XXX numvnodes is a long */ return (sysctl_rdint(oldp, oldlenp, newp, numvnodes)); + case KERN_HOSTID: + return (sysctl_int(oldp, oldlenp, newp, newlen, &hostid)); case KERN_CLOCKRATE: return (sysctl_clockrate(oldp, oldlenp, newp)); case KERN_BOOTTIME: { @@ -585,7 +588,7 @@ int kern_sysctl_locked(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen, struct proc *p) { - int error, level, inthostid, stackgap; + int error, level, stackgap; dev_t dev; extern int pool_debug; @@ -623,11 +626,6 @@ kern_sysctl_locked(int *name, u_int namelen, void *oldp, size_t *oldlenp, if (newp && !error) domainnamelen = newlen; return (error); - case KERN_HOSTID: - inthostid = hostid; /* XXX assumes sizeof long <= sizeof int */ - error = sysctl_int(oldp, oldlenp, newp, newlen, &inthostid); - hostid = inthostid; - return (error); case KERN_CONSBUF: if ((error = suser(p))) return (error); @@ -1055,17 +1053,36 @@ int sysctl_int_lower(void *oldp, size_t *oldlenp, void *newp, size_t newlen, int *valp) { - unsigned int oval = *valp, val = *valp; + unsigned int oldval, newval; int error; - if (newp == NULL) - return (sysctl_rdint(oldp, oldlenp, newp, val)); + if (oldp && *oldlenp < sizeof(int)) + return (ENOMEM); + if (newp && newlen != sizeof(int)) + return (EINVAL); + *oldlenp = sizeof(int); + + if (newp) { + if ((error = copyin(newp, &newval, sizeof(int)))) + return (error); + do { + oldval = atomic_load_int(valp); + if (oldval < (unsigned int)newval) + return (EPERM); /* do not allow raising */ + } while (atomic_cas_uint(valp, oldval, newval) != oldval); + + if (oldp) { + /* new value has been set although user gets error */ + if ((error = copyout(&oldval, oldp, sizeof(int)))) + return (error); + } + } else if (oldp) { + oldval = atomic_load_int(valp); + + if ((error = copyout(&oldval, oldp, sizeof(int)))) + return (error); + } - if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val))) - return (error); - if (val > oval) - return (EPERM); /* do not allow raising */ - *(unsigned int *)valp = val; return (0); } @@ -1076,18 +1093,8 @@ sysctl_int_lower(void *oldp, size_t *oldlenp, void *newp, size_t newlen, int sysctl_int(void *oldp, size_t *oldlenp, void *newp, size_t newlen, int *valp) { - int error = 0; - - if (oldp && *oldlenp < sizeof(int)) - return (ENOMEM); - if (newp && newlen != sizeof(int)) - return (EINVAL); - *oldlenp = sizeof(int); - if (oldp) - error = copyout(valp, oldp, sizeof(int)); - if (error == 0 && newp) - error = copyin(newp, valp, sizeof(int)); - return (error); + return (sysctl_int_bounded(oldp, oldlenp, newp, newlen, valp, + INT_MIN, INT_MAX)); } /* diff --git a/sys/kern/uipc_domain.c b/sys/kern/uipc_domain.c index d96641af6..1c314806b 100644 --- a/sys/kern/uipc_domain.c +++ b/sys/kern/uipc_domain.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_domain.c,v 1.66 2024/08/12 11:25:27 bluhm Exp $ */ +/* $OpenBSD: uipc_domain.c,v 1.67 2024/08/14 17:52:47 mvs Exp $ */ /* $NetBSD: uipc_domain.c,v 1.14 1996/02/09 19:00:44 christos Exp $ */ /* @@ -236,9 +236,18 @@ net_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, return (EISDIR); /* overloaded */ protocol = name[1]; for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) - if (pr->pr_protocol == protocol && pr->pr_sysctl) - return ((*pr->pr_sysctl)(name + 2, namelen - 2, - oldp, oldlenp, newp, newlen)); + if (pr->pr_protocol == protocol && pr->pr_sysctl) { + size_t savelen = *oldlenp; + int error; + + if ((error = sysctl_vslock(oldp, savelen))) + return (error); + error = (*pr->pr_sysctl)(name + 2, namelen - 2, + oldp, oldlenp, newp, newlen); + sysctl_vsunlock(oldp, savelen); + + return (error); + } return (ENOPROTOOPT); } diff --git a/sys/netmpls/mpls_raw.c b/sys/netmpls/mpls_raw.c index ec81c0708..56a817602 100644 --- a/sys/netmpls/mpls_raw.c +++ b/sys/netmpls/mpls_raw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mpls_raw.c,v 1.20 2024/04/29 00:29:48 jsg Exp $ */ +/* $OpenBSD: mpls_raw.c,v 1.21 2024/08/14 17:52:47 mvs Exp $ */ /* * Copyright (C) 1999, 2000 and 2001 AYAME Project, WIDE Project. @@ -58,6 +58,12 @@ int mpls_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - return sysctl_bounded_arr(mplsctl_vars, nitems(mplsctl_vars), + int error; + + KERNEL_LOCK(); + error = sysctl_bounded_arr(mplsctl_vars, nitems(mplsctl_vars), name, namelen, oldp, oldlenp, newp, newlen); + KERNEL_UNLOCK(); + + return error; } diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h index 0147fde22..41f4f4083 100644 --- a/sys/sys/kernel.h +++ b/sys/sys/kernel.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kernel.h,v 1.26 2023/03/03 20:16:44 cheloha Exp $ */ +/* $OpenBSD: kernel.h,v 1.27 2024/08/14 13:54:08 mvs Exp $ */ /* $NetBSD: kernel.h,v 1.11 1995/03/03 01:24:16 cgd Exp $ */ /*- @@ -40,7 +40,7 @@ /* Global variables for the kernel. */ /* 1.1 */ -extern long hostid; +extern int hostid; extern char hostname[MAXHOSTNAMELEN]; extern int hostnamelen; extern char domainname[MAXHOSTNAMELEN]; diff --git a/usr.bin/dig/lib/lwres/include/lwres/lwres.h b/usr.bin/dig/lib/lwres/include/lwres/lwres.h index 74e492116..62af04e5b 100644 --- a/usr.bin/dig/lib/lwres/include/lwres/lwres.h +++ b/usr.bin/dig/lib/lwres/include/lwres/lwres.h @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: lwres.h,v 1.3 2020/02/12 13:05:04 jsg Exp $ */ +/* $Id: lwres.h,v 1.6 2024/08/14 17:38:57 florian Exp $ */ #ifndef LWRES_LWRES_H #define LWRES_LWRES_H 1 @@ -101,34 +101,20 @@ struct lwres_addr { */ #define LWRES_CONFMAXNAMESERVERS 3 /*%< max 3 "nameserver" entries */ -#define LWRES_CONFMAXLWSERVERS 1 /*%< max 1 "lwserver" entry */ #define LWRES_CONFMAXSEARCH 8 /*%< max 8 domains in "search" entry */ #define LWRES_CONFMAXLINELEN 256 /*%< max size of a line */ -#define LWRES_CONFMAXSORTLIST 10 /*%< max 10 */ /*% lwres_conf_t */ typedef struct { lwres_addr_t nameservers[LWRES_CONFMAXNAMESERVERS]; uint8_t nsnext; /*%< index for next free slot */ - lwres_addr_t lwservers[LWRES_CONFMAXLWSERVERS]; - uint8_t lwnext; /*%< index for next free slot */ - char *domainname; char *search[LWRES_CONFMAXSEARCH]; uint8_t searchnxt; /*%< index for next free slot */ - struct { - lwres_addr_t addr; - /*% mask has a non-zero 'family' and 'length' if set */ - lwres_addr_t mask; - } sortlist[LWRES_CONFMAXSORTLIST]; - uint8_t sortlistnxt; - - uint8_t resdebug; /*%< non-zero if 'options debug' set */ uint8_t ndots; /*%< set to n in 'options ndots:n' */ - uint8_t no_tld_query; /*%< non-zero if 'options no_tld_query' */ int flags; } lwres_conf_t; diff --git a/usr.bin/dig/lib/lwres/lwconfig.c b/usr.bin/dig/lib/lwres/lwconfig.c index d94be2486..8bc130ca0 100644 --- a/usr.bin/dig/lib/lwres/lwconfig.c +++ b/usr.bin/dig/lib/lwres/lwconfig.c @@ -56,18 +56,12 @@ static lwres_result_t lwres_conf_parsenameserver(lwres_conf_t *confdata, FILE *fp); -static lwres_result_t -lwres_conf_parselwserver(lwres_conf_t *confdata, FILE *fp); - static lwres_result_t lwres_conf_parsedomain(lwres_conf_t *confdata, FILE *fp); static lwres_result_t lwres_conf_parsesearch(lwres_conf_t *confdata, FILE *fp); -static lwres_result_t -lwres_conf_parsesortlist(lwres_conf_t *confdata, FILE *fp); - static lwres_result_t lwres_conf_parseoption(lwres_conf_t *confdata, FILE *fp); @@ -159,13 +153,9 @@ lwres_conf_init(lwres_conf_t *confdata, int lwresflags) { int i; confdata->nsnext = 0; - confdata->lwnext = 0; confdata->domainname = NULL; confdata->searchnxt = 0; - confdata->sortlistnxt = 0; - confdata->resdebug = 0; confdata->ndots = 1; - confdata->no_tld_query = 0; confdata->flags = lwresflags; for (i = 0; i < LWRES_CONFMAXNAMESERVERS; i++) @@ -174,10 +164,6 @@ lwres_conf_init(lwres_conf_t *confdata, int lwresflags) { for (i = 0; i < LWRES_CONFMAXSEARCH; i++) confdata->search[i] = NULL; - for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) { - lwres_resetaddr(&confdata->sortlist[i].addr); - lwres_resetaddr(&confdata->sortlist[i].mask); - } } /*% Frees up all the internal memory used by the config data structure, returning it to the lwres_context_t. */ @@ -196,19 +182,10 @@ lwres_conf_clear(lwres_conf_t *confdata) { confdata->search[i] = NULL; } - for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) { - lwres_resetaddr(&confdata->sortlist[i].addr); - lwres_resetaddr(&confdata->sortlist[i].mask); - } - confdata->nsnext = 0; - confdata->lwnext = 0; confdata->domainname = NULL; confdata->searchnxt = 0; - confdata->sortlistnxt = 0; - confdata->resdebug = 0; confdata->ndots = 1; - confdata->no_tld_query = 0; } static lwres_result_t @@ -241,31 +218,6 @@ lwres_conf_parsenameserver(lwres_conf_t *confdata, FILE *fp) { return (LWRES_R_SUCCESS); } -static lwres_result_t -lwres_conf_parselwserver(lwres_conf_t *confdata, FILE *fp) { - char word[LWRES_CONFMAXLINELEN]; - int res; - - if (confdata->lwnext == LWRES_CONFMAXLWSERVERS) - return (LWRES_R_SUCCESS); - - res = getword(fp, word, sizeof(word)); - if (strlen(word) == 0U) - return (LWRES_R_FAILURE); /* Nothing on line. */ - else if (res == ' ' || res == '\t') - res = eatwhite(fp); - - if (res != EOF && res != '\n') - return (LWRES_R_FAILURE); /* Extra junk on line. */ - - res = lwres_create_addr(word, - &confdata->lwservers[confdata->lwnext++], 1); - if (res != LWRES_R_SUCCESS) - return (res); - - return (LWRES_R_SUCCESS); -} - static lwres_result_t lwres_conf_parsedomain(lwres_conf_t *confdata, FILE *fp) { char word[LWRES_CONFMAXLINELEN]; @@ -398,57 +350,6 @@ lwres_create_addr(const char *buffer, lwres_addr_t *addr, int convert_zero) { return (LWRES_R_SUCCESS); } -static lwres_result_t -lwres_conf_parsesortlist(lwres_conf_t *confdata, FILE *fp) { - int delim, res, idx; - char word[LWRES_CONFMAXLINELEN]; - char *p; - - delim = getword(fp, word, sizeof(word)); - if (strlen(word) == 0U) - return (LWRES_R_FAILURE); /* Empty line after keyword. */ - - while (strlen(word) > 0U) { - if (confdata->sortlistnxt == LWRES_CONFMAXSORTLIST) - return (LWRES_R_FAILURE); /* Too many values. */ - - p = strchr(word, '/'); - if (p != NULL) - *p++ = '\0'; - - idx = confdata->sortlistnxt; - res = lwres_create_addr(word, &confdata->sortlist[idx].addr, 1); - if (res != LWRES_R_SUCCESS) - return (res); - - if (p != NULL) { - res = lwres_create_addr(p, - &confdata->sortlist[idx].mask, - 0); - if (res != LWRES_R_SUCCESS) - return (res); - } else { - /* - * Make up a mask. - */ - confdata->sortlist[idx].mask = - confdata->sortlist[idx].addr; - - memset(&confdata->sortlist[idx].mask.address, 0xff, - confdata->sortlist[idx].addr.length); - } - - confdata->sortlistnxt++; - - if (delim == EOF || delim == '\n') - break; - else - delim = getword(fp, word, sizeof(word)); - } - - return (LWRES_R_SUCCESS); -} - static lwres_result_t lwres_conf_parseoption(lwres_conf_t *confdata, FILE *fp) { int delim; @@ -461,11 +362,7 @@ lwres_conf_parseoption(lwres_conf_t *confdata, FILE *fp) { return (LWRES_R_FAILURE); /* Empty line after keyword. */ while (strlen(word) > 0U) { - if (strcmp("debug", word) == 0) { - confdata->resdebug = 1; - } else if (strcmp("no_tld_query", word) == 0) { - confdata->no_tld_query = 1; - } else if (strncmp("ndots:", word, 6) == 0) { + if (strncmp("ndots:", word, 6) == 0) { ndots = strtol(word + 6, &p, 10); if (*p != '\0') /* Bad string. */ return (LWRES_R_FAILURE); @@ -509,14 +406,10 @@ lwres_conf_parse(lwres_conf_t *confdata, const char *filename) { rval = LWRES_R_SUCCESS; else if (strcmp(word, "nameserver") == 0) rval = lwres_conf_parsenameserver(confdata, fp); - else if (strcmp(word, "lwserver") == 0) - rval = lwres_conf_parselwserver(confdata, fp); else if (strcmp(word, "domain") == 0) rval = lwres_conf_parsedomain(confdata, fp); else if (strcmp(word, "search") == 0) rval = lwres_conf_parsesearch(confdata, fp); - else if (strcmp(word, "sortlist") == 0) - rval = lwres_conf_parsesortlist(confdata, fp); else if (strcmp(word, "options") == 0) rval = lwres_conf_parseoption(confdata, fp); else { diff --git a/usr.bin/ssh/cipher.c b/usr.bin/ssh/cipher.c index 33f559ae7..56be78769 100644 --- a/usr.bin/ssh/cipher.c +++ b/usr.bin/ssh/cipher.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cipher.c,v 1.121 2024/05/17 02:39:11 jsg Exp $ */ +/* $OpenBSD: cipher.c,v 1.122 2024/08/14 15:42:18 tobias Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -249,7 +249,7 @@ cipher_init(struct sshcipher_ctx **ccp, const struct sshcipher *cipher, #endif *ccp = NULL; - if ((cc = calloc(sizeof(*cc), 1)) == NULL) + if ((cc = calloc(1, sizeof(*cc))) == NULL) return SSH_ERR_ALLOC_FAIL; cc->plaintext = (cipher->flags & CFLAG_NONE) != 0; diff --git a/usr.bin/ssh/sshbuf.c b/usr.bin/ssh/sshbuf.c index b5cbfda84..668480a34 100644 --- a/usr.bin/ssh/sshbuf.c +++ b/usr.bin/ssh/sshbuf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshbuf.c,v 1.19 2022/12/02 04:40:27 djm Exp $ */ +/* $OpenBSD: sshbuf.c,v 1.23 2024/08/14 15:42:18 tobias Exp $ */ /* * Copyright (c) 2011 Damien Miller * @@ -55,6 +55,7 @@ sshbuf_check_sanity(const struct sshbuf *buf) SSHBUF_TELL("sanity"); if (__predict_false(buf == NULL || (!buf->readonly && buf->d != buf->cd) || + buf->parent == buf || buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX || buf->cd == NULL || buf->max_size > SSHBUF_SIZE_MAX || @@ -91,7 +92,7 @@ sshbuf_new(void) { struct sshbuf *ret; - if ((ret = calloc(sizeof(*ret), 1)) == NULL) + if ((ret = calloc(1, sizeof(*ret))) == NULL) return NULL; ret->alloc = SSHBUF_SIZE_INIT; ret->max_size = SSHBUF_SIZE_MAX; @@ -111,7 +112,7 @@ sshbuf_from(const void *blob, size_t len) struct sshbuf *ret; if (blob == NULL || len > SSHBUF_SIZE_MAX || - (ret = calloc(sizeof(*ret), 1)) == NULL) + (ret = calloc(1, sizeof(*ret))) == NULL) return NULL; ret->alloc = ret->size = ret->max_size = len; ret->readonly = 1; @@ -130,7 +131,8 @@ sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent) if ((r = sshbuf_check_sanity(child)) != 0 || (r = sshbuf_check_sanity(parent)) != 0) return r; - if (child->parent != NULL && child->parent != parent) + if ((child->parent != NULL && child->parent != parent) || + child == parent) return SSH_ERR_INTERNAL_ERROR; child->parent = parent; child->parent->refcount++; @@ -177,16 +179,14 @@ sshbuf_free(struct sshbuf *buf) return; /* - * If we are a child, the free our parent to decrement its reference + * If we are a child, then free our parent to decrement its reference * count and possibly free it. */ sshbuf_free(buf->parent); buf->parent = NULL; - if (!buf->readonly) { - explicit_bzero(buf->d, buf->alloc); - free(buf->d); - } + if (!buf->readonly) + freezero(buf->d, buf->alloc); freezero(buf, sizeof(*buf)); } diff --git a/usr.sbin/bgpctl/bgpctl.8 b/usr.sbin/bgpctl/bgpctl.8 index 4f344f4d0..a1bd36367 100644 --- a/usr.sbin/bgpctl/bgpctl.8 +++ b/usr.sbin/bgpctl/bgpctl.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: bgpctl.8,v 1.111 2023/05/09 13:26:27 claudio Exp $ +.\" $OpenBSD: bgpctl.8,v 1.112 2024/08/14 19:10:51 claudio Exp $ .\" .\" Copyright (c) 2003 Henning Brauer .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 9 2023 $ +.Dd $Mdocdate: August 14 2024 $ .Dt BGPCTL 8 .Os .Sh NAME @@ -416,6 +416,11 @@ Show only routes which are not eligible. Show only prefixes which are marked invalid and were treated as withdrawn. .It Ar family Limit the output to the given address family. +.It Cm filtered +Show only routes which were filtered out. +Requires +.Ic rde rib Loc-RIB include filtered +to be set in the config. .It Cm in Show routes from the unfiltered Adj-RIB-In. The diff --git a/usr.sbin/bgpctl/bgpctl.c b/usr.sbin/bgpctl/bgpctl.c index 1dd4c7a67..d8becf6f8 100644 --- a/usr.sbin/bgpctl/bgpctl.c +++ b/usr.sbin/bgpctl/bgpctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpctl.c,v 1.306 2024/05/22 08:42:34 claudio Exp $ */ +/* $OpenBSD: bgpctl.c,v 1.307 2024/08/14 19:10:51 claudio Exp $ */ /* * Copyright (c) 2003 Henning Brauer @@ -745,6 +745,8 @@ fmt_flags(uint32_t flags, int sum) char *p = flagstr; if (sum) { + if (flags & F_PREF_FILTERED) + *p++ = 'F'; if (flags & F_PREF_INVALID) *p++ = 'E'; if (flags & F_PREF_OTC_LEAK) @@ -771,6 +773,8 @@ fmt_flags(uint32_t flags, int sum) else strlcpy(buf, "external", sizeof(buf)); + if (flags & F_PREF_FILTERED) + strlcat(buf, ", filtered", sizeof(buf)); if (flags & F_PREF_INVALID) strlcat(buf, ", invalid", sizeof(buf)); if (flags & F_PREF_OTC_LEAK) diff --git a/usr.sbin/bgpctl/output.c b/usr.sbin/bgpctl/output.c index e75ec7a22..8f72fecc8 100644 --- a/usr.sbin/bgpctl/output.c +++ b/usr.sbin/bgpctl/output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: output.c,v 1.52 2024/08/12 09:05:28 claudio Exp $ */ +/* $OpenBSD: output.c,v 1.53 2024/08/14 19:10:51 claudio Exp $ */ /* * Copyright (c) 2003 Henning Brauer @@ -66,7 +66,7 @@ show_head(struct parse_result *res) break; printf("flags: " "* = Valid, > = Selected, I = via IBGP, A = Announced,\n" - " S = Stale, E = Error\n"); + " S = Stale, E = Error, F = Filtered\n"); printf("origin validation state: " "N = not-found, V = valid, ! = invalid\n"); printf("aspa validation state: " diff --git a/usr.sbin/bgpctl/output_json.c b/usr.sbin/bgpctl/output_json.c index daf865a1b..8a2e26ccf 100644 --- a/usr.sbin/bgpctl/output_json.c +++ b/usr.sbin/bgpctl/output_json.c @@ -1,4 +1,4 @@ -/* $OpenBSD: output_json.c,v 1.45 2024/08/12 09:05:28 claudio Exp $ */ +/* $OpenBSD: output_json.c,v 1.46 2024/08/14 19:10:51 claudio Exp $ */ /* * Copyright (c) 2020 Claudio Jeker @@ -834,6 +834,8 @@ json_rib(struct ctl_show_rib *r, struct ibuf *asbuf, struct parse_result *res) /* flags */ json_do_bool("valid", r->flags & F_PREF_ELIGIBLE); + if (r->flags & F_PREF_FILTERED) + json_do_bool("filtered", 1); if (r->flags & F_PREF_BEST) json_do_bool("best", 1); if (r->flags & F_PREF_ECMP) diff --git a/usr.sbin/bgpctl/parser.c b/usr.sbin/bgpctl/parser.c index 56be82df7..7654051b8 100644 --- a/usr.sbin/bgpctl/parser.c +++ b/usr.sbin/bgpctl/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.134 2023/11/20 14:18:21 claudio Exp $ */ +/* $OpenBSD: parser.c,v 1.135 2024/08/14 19:10:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -183,6 +183,7 @@ static const struct token t_show_rib[] = { { ASTYPE, "empty-as", AS_EMPTY, t_show_rib}, { FLAG, "error", F_CTL_INVALID, t_show_rib}, { EXTCOMMUNITY, "ext-community", NONE, t_show_rib}, + { FLAG, "filtered", F_CTL_FILTERED, t_show_rib}, { FLAG, "in", F_CTL_ADJ_IN, t_show_rib}, { LRGCOMMUNITY, "large-community", NONE, t_show_rib}, { FLAG, "leaked", F_CTL_LEAKED, t_show_rib}, diff --git a/usr.sbin/bgpd/bgpd.conf.5 b/usr.sbin/bgpd/bgpd.conf.5 index e01abf88b..d4b3fb71c 100644 --- a/usr.sbin/bgpd/bgpd.conf.5 +++ b/usr.sbin/bgpd/bgpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: bgpd.conf.5,v 1.241 2024/08/12 09:04:23 claudio Exp $ +.\" $OpenBSD: bgpd.conf.5,v 1.242 2024/08/14 19:09:51 claudio Exp $ .\" .\" Copyright (c) 2004 Claudio Jeker .\" Copyright (c) 2003, 2004 Henning Brauer @@ -16,7 +16,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: August 12 2024 $ +.Dd $Mdocdate: August 14 2024 $ .Dt BGPD.CONF 5 .Os .Sh NAME @@ -325,6 +325,13 @@ and .Ic Loc-RIB , which are created automatically and used by default. .Pp +.It Ic rde rib Loc-RIB include filtered +Include filtered prefixes in the +.Ic Loc-RIB . +Filtered prefixes are not eligible by the decision process but can be +displayed by +.Xr bgpctl 8 . +.Pp .It Xo .Ic rde .Ic route-age diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index a4d2a106b..78e245b5d 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.494 2024/08/12 09:04:23 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.495 2024/08/14 19:09:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -101,6 +101,7 @@ #define F_CTL_AVS_VALID 0x1000000 #define F_CTL_AVS_INVALID 0x2000000 #define F_CTL_AVS_UNKNOWN 0x4000000 +#define F_CTL_FILTERED 0x8000000 /* only set on requests */ #define F_CTL_SSV 0x80000000 /* only used by bgpctl */ #define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \ @@ -317,6 +318,7 @@ struct bgpd_config { uint16_t min_holdtime; uint16_t connectretry; uint8_t fib_priority; + uint8_t filtered_in_locrib; }; extern int cmd_opts; @@ -888,6 +890,7 @@ struct ctl_neighbor { #define F_PREF_OTC_LEAK 0x080 #define F_PREF_ECMP 0x100 #define F_PREF_AS_WIDE 0x200 +#define F_PREF_FILTERED 0x400 struct ctl_show_rib { struct bgpd_addr true_nexthop; diff --git a/usr.sbin/bgpd/config.c b/usr.sbin/bgpd/config.c index 789431986..bf93a521b 100644 --- a/usr.sbin/bgpd/config.c +++ b/usr.sbin/bgpd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.109 2024/05/22 08:41:14 claudio Exp $ */ +/* $OpenBSD: config.c,v 1.110 2024/08/14 19:09:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004, 2005 Henning Brauer @@ -87,6 +87,7 @@ copy_config(struct bgpd_config *to, struct bgpd_config *from) to->min_holdtime = from->min_holdtime; to->connectretry = from->connectretry; to->fib_priority = from->fib_priority; + to->filtered_in_locrib = from->filtered_in_locrib; } void diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index bffd40948..1621c8a1d 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.464 2024/08/12 09:04:23 claudio Exp $ */ +/* $OpenBSD: parse.y,v 1.465 2024/08/14 19:09:51 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer @@ -250,7 +250,7 @@ typedef struct { %token SEND RECV PLUS POLICY ROLE %token DEMOTE ENFORCE NEIGHBORAS ASOVERRIDE REFLECTOR DEPEND DOWN %token DUMP IN OUT SOCKET RESTRICTED -%token LOG TRANSPARENT +%token LOG TRANSPARENT FILTERED %token TCP MD5SIG PASSWORD KEY TTLSECURITY %token ALLOW DENY MATCH %token QUICK @@ -941,6 +941,14 @@ conf_main : AS as4number { } free($3); } + | RDE RIB STRING INCLUDE FILTERED { + if (strcmp($3, "Loc-RIB") != 0) { + yyerror("include filtered only supported in " + "Loc-RIB"); + YYERROR; + } + conf->filtered_in_locrib = 1; + } | NEXTHOP QUALIFY VIA STRING { if (!strcmp($4, "bgp")) conf->flags |= BGPD_FLAG_NEXTHOP_BGP; @@ -3551,6 +3559,7 @@ lookup(char *s) { "ext-community", EXTCOMMUNITY}, { "fib-priority", FIBPRIORITY}, { "fib-update", FIBUPDATE}, + { "filtered", FILTERED}, { "flags", FLAGS}, { "flowspec", FLOWSPEC}, { "fragment", FRAGMENT}, diff --git a/usr.sbin/bgpd/printconf.c b/usr.sbin/bgpd/printconf.c index 13a73208c..8aeeeeeee 100644 --- a/usr.sbin/bgpd/printconf.c +++ b/usr.sbin/bgpd/printconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: printconf.c,v 1.173 2024/05/22 08:41:14 claudio Exp $ */ +/* $OpenBSD: printconf.c,v 1.174 2024/08/14 19:09:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -1276,6 +1276,8 @@ print_config(struct bgpd_config *conf, struct rib_names *rib_l) SIMPLEQ_FOREACH(vpn, &conf->l3vpns, entry) print_l3vpn(vpn); printf("\n"); + if (conf->filtered_in_locrib) + printf("rde rib Loc-RIB include filtered\n"); SIMPLEQ_FOREACH(rr, rib_l, entry) { if (rr->flags & F_RIB_NOEVALUATE) printf("rde rib %s no evaluate\n", rr->name); diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index 558d851f5..c4c286e4f 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.625 2024/05/22 08:41:14 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.626 2024/08/14 19:09:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -1854,7 +1854,7 @@ rde_update_update(struct rde_peer *peer, uint32_t path_id, path_id_tx = pathid_assign(peer, path_id, prefix, prefixlen); /* add original path to the Adj-RIB-In */ if (prefix_update(rib_byid(RIB_ADJ_IN), peer, path_id, path_id_tx, - in, prefix, prefixlen) == 1) + in, 0, prefix, prefixlen) == 1) peer->stats.prefix_cnt++; /* max prefix checker */ @@ -1883,11 +1883,16 @@ rde_update_update(struct rde_peer *peer, uint32_t path_id, &state.nexthop->exit_nexthop, prefix, prefixlen); prefix_update(rib, peer, path_id, path_id_tx, &state, - prefix, prefixlen); - } else if (prefix_withdraw(rib, peer, path_id, prefix, - prefixlen)) { - rde_update_log(wmsg, i, peer, - NULL, prefix, prefixlen); + 0, prefix, prefixlen); + } else if (conf->filtered_in_locrib && i == RIB_LOC_START) { + rde_update_log(wmsg, i, peer, NULL, prefix, prefixlen); + prefix_update(rib, peer, path_id, path_id_tx, &state, + 1, prefix, prefixlen); + } else { + if (prefix_withdraw(rib, peer, path_id, prefix, + prefixlen)) + rde_update_log(wmsg, i, peer, + NULL, prefix, prefixlen); } rde_filterstate_clean(&state); @@ -2738,7 +2743,7 @@ rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags, rib.aspa_validation_state = prefix_aspa_vstate(p); rib.dmetric = p->dmetric; rib.flags = 0; - if (!adjout) { + if (!adjout && prefix_eligible(p)) { re = prefix_re(p); TAILQ_FOREACH(xp, &re->prefix_h, entry.list.rib) { switch (xp->dmetric) { @@ -2768,6 +2773,8 @@ rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags, rib.flags |= F_PREF_ANNOUNCE; if (prefix_eligible(p)) rib.flags |= F_PREF_ELIGIBLE; + if (prefix_filtered(p)) + rib.flags |= F_PREF_FILTERED; /* otc loop includes parse err so skip the latter if the first is set */ if (asp->flags & F_ATTR_OTC_LEAK) rib.flags |= F_PREF_OTC_LEAK; @@ -2854,6 +2861,8 @@ rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req, int adjout) if ((req->flags & F_CTL_INVALID) && (asp->flags & F_ATTR_PARSE_ERR) == 0) return; + if ((req->flags & F_CTL_FILTERED) && !prefix_filtered(p)) + return; if ((req->flags & F_CTL_INELIGIBLE) && prefix_eligible(p)) return; if ((req->flags & F_CTL_LEAKED) && @@ -3557,7 +3566,7 @@ rde_reload_done(void) struct rde_prefixset_head originsets_old; struct as_set_head as_sets_old; uint16_t rid; - int reload = 0; + int reload = 0, force_locrib = 0; softreconfig = 0; @@ -3568,6 +3577,12 @@ rde_reload_done(void) SIMPLEQ_CONCAT(&originsets_old, &conf->rde_originsets); SIMPLEQ_CONCAT(&as_sets_old, &conf->as_sets); + /* run softreconfig in if filter mode changed */ + if (conf->filtered_in_locrib != nconf->filtered_in_locrib) { + log_debug("filter mode changed, reloading Loc-Rib"); + force_locrib = 1; + } + /* merge the main config */ copy_config(conf, nconf); @@ -3688,7 +3703,7 @@ rde_reload_done(void) } /* bring ribs in sync */ - for (rid = 0; rid < rib_size; rid++) { + for (rid = RIB_LOC_START; rid < rib_size; rid++) { struct rib *rib = rib_byid(rid); if (rib == NULL) continue; @@ -3734,10 +3749,11 @@ rde_reload_done(void) rib->state = RECONF_KEEP; /* FALLTHROUGH */ case RECONF_KEEP: - if (rde_filter_equal(rib->in_rules, rib->in_rules_tmp)) + if (!(force_locrib && rid == RIB_LOC_START) && + rde_filter_equal(rib->in_rules, rib->in_rules_tmp)) /* rib is in sync */ break; - log_debug("in filter change: reloading RIB %s", + log_debug("filter change: reloading RIB %s", rib->name); rib->state = RECONF_RELOAD; reload++; @@ -3935,9 +3951,14 @@ rde_softreconfig_in(struct rib_entry *re, void *bula) if (action == ACTION_ALLOW) { /* update Local-RIB */ prefix_update(rib, peer, p->path_id, - p->path_id_tx, &state, + p->path_id_tx, &state, 0, &prefix, pt->prefixlen); - } else if (action == ACTION_DENY) { + } else if (conf->filtered_in_locrib && + i == RIB_LOC_START) { + prefix_update(rib, peer, p->path_id, + p->path_id_tx, &state, 1, + &prefix, pt->prefixlen); + } else { /* remove from Local-RIB */ prefix_withdraw(rib, peer, p->path_id, &prefix, pt->prefixlen); @@ -4084,9 +4105,14 @@ rde_rpki_softreload(struct rib_entry *re, void *bula) if (action == ACTION_ALLOW) { /* update Local-RIB */ prefix_update(rib, peer, p->path_id, - p->path_id_tx, &state, + p->path_id_tx, &state, 0, &prefix, pt->prefixlen); - } else if (action == ACTION_DENY) { + } else if (conf->filtered_in_locrib && + i == RIB_LOC_START) { + prefix_update(rib, peer, p->path_id, + p->path_id_tx, &state, 1, + &prefix, pt->prefixlen); + } else { /* remove from Local-RIB */ prefix_withdraw(rib, peer, p->path_id, &prefix, pt->prefixlen); @@ -4365,7 +4391,7 @@ network_add(struct network_config *nc, struct filterstate *state) path_id_tx = pathid_assign(peerself, 0, &nc->prefix, nc->prefixlen); if (prefix_update(rib_byid(RIB_ADJ_IN), peerself, 0, path_id_tx, - state, &nc->prefix, nc->prefixlen) == 1) + state, 0, &nc->prefix, nc->prefixlen) == 1) peerself->stats.prefix_cnt++; for (i = RIB_LOC_START; i < rib_size; i++) { struct rib *rib = rib_byid(i); @@ -4374,8 +4400,8 @@ network_add(struct network_config *nc, struct filterstate *state) rde_update_log("announce", i, peerself, state->nexthop ? &state->nexthop->exit_nexthop : NULL, &nc->prefix, nc->prefixlen); - prefix_update(rib, peerself, 0, path_id_tx, state, &nc->prefix, - nc->prefixlen); + prefix_update(rib, peerself, 0, path_id_tx, state, 0, + &nc->prefix, nc->prefixlen); } filterset_free(&nc->attrset); } diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index 5be48fd4c..899833da0 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.303 2024/05/29 10:36:32 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.304 2024/08/14 19:09:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker and @@ -281,19 +281,21 @@ struct prefix { time_t lastchange; uint32_t path_id; uint32_t path_id_tx; + uint16_t flags; uint8_t validation_state; uint8_t nhflags; int8_t dmetric; /* decision metric */ - uint8_t flags; -#define PREFIX_FLAG_WITHDRAW 0x01 /* enqueued on withdraw queue */ -#define PREFIX_FLAG_UPDATE 0x02 /* enqueued on update queue */ -#define PREFIX_FLAG_DEAD 0x04 /* locked but removed */ -#define PREFIX_FLAG_STALE 0x08 /* stale entry (graceful reload) */ -#define PREFIX_FLAG_MASK 0x0f /* mask for the prefix types */ -#define PREFIX_FLAG_ADJOUT 0x10 /* prefix is in the adj-out rib */ -#define PREFIX_FLAG_EOR 0x20 /* prefix is EoR */ -#define PREFIX_NEXTHOP_LINKED 0x40 /* prefix is linked onto nexthop list */ -#define PREFIX_FLAG_LOCKED 0x80 /* locked by rib walker */ +}; +#define PREFIX_FLAG_WITHDRAW 0x0001 /* enqueued on withdraw queue */ +#define PREFIX_FLAG_UPDATE 0x0002 /* enqueued on update queue */ +#define PREFIX_FLAG_DEAD 0x0004 /* locked but removed */ +#define PREFIX_FLAG_STALE 0x0008 /* stale entry (graceful reload) */ +#define PREFIX_FLAG_MASK 0x000f /* mask for the prefix types */ +#define PREFIX_FLAG_ADJOUT 0x0010 /* prefix is in the adj-out rib */ +#define PREFIX_FLAG_EOR 0x0020 /* prefix is EoR */ +#define PREFIX_NEXTHOP_LINKED 0x0040 /* prefix is linked onto nexthop list */ +#define PREFIX_FLAG_LOCKED 0x0080 /* locked by rib walker */ +#define PREFIX_FLAG_FILTERED 0x0100 /* prefix is filtered (ineligible) */ #define PREFIX_DMETRIC_NONE 0 #define PREFIX_DMETRIC_INVALID 1 @@ -301,7 +303,6 @@ struct prefix { #define PREFIX_DMETRIC_AS_WIDE 3 #define PREFIX_DMETRIC_ECMP 4 #define PREFIX_DMETRIC_BEST 5 -}; /* possible states for nhflags */ #define NEXTHOP_SELF 0x01 @@ -579,7 +580,8 @@ struct prefix *prefix_adjout_lookup(struct rde_peer *, struct bgpd_addr *, int); struct prefix *prefix_adjout_match(struct rde_peer *, struct bgpd_addr *); int prefix_update(struct rib *, struct rde_peer *, uint32_t, - uint32_t, struct filterstate *, struct bgpd_addr *, int); + uint32_t, struct filterstate *, int, struct bgpd_addr *, + int); int prefix_withdraw(struct rib *, struct rde_peer *, uint32_t, struct bgpd_addr *, int); int prefix_flowspec_update(struct rde_peer *, struct filterstate *, @@ -669,6 +671,12 @@ prefix_re(struct prefix *p) return (p->entry.list.re); } +static inline int +prefix_filtered(struct prefix *p) +{ + return ((p->flags & PREFIX_FLAG_FILTERED) != 0); +} + void nexthop_shutdown(void); int nexthop_pending(void); void nexthop_runner(void); diff --git a/usr.sbin/bgpd/rde_decide.c b/usr.sbin/bgpd/rde_decide.c index 5ebe4050d..2697f06df 100644 --- a/usr.sbin/bgpd/rde_decide.c +++ b/usr.sbin/bgpd/rde_decide.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_decide.c,v 1.102 2023/10/12 14:22:08 claudio Exp $ */ +/* $OpenBSD: rde_decide.c,v 1.103 2024/08/14 19:09:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker @@ -486,6 +486,10 @@ prefix_eligible(struct prefix *p) { struct rde_aspath *asp = prefix_aspath(p); + /* prefix itself is marked ineligible */ + if (prefix_filtered(p)) + return 0; + /* The aspath needs to be loop and error free */ if (asp == NULL || asp->flags & (F_ATTR_LOOP|F_ATTR_OTC_LEAK|F_ATTR_PARSE_ERR)) diff --git a/usr.sbin/bgpd/rde_rib.c b/usr.sbin/bgpd/rde_rib.c index 3caaa3e9e..d595a0e2f 100644 --- a/usr.sbin/bgpd/rde_rib.c +++ b/usr.sbin/bgpd/rde_rib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_rib.c,v 1.262 2024/05/29 10:34:56 claudio Exp $ */ +/* $OpenBSD: rde_rib.c,v 1.263 2024/08/14 19:09:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker @@ -801,10 +801,10 @@ path_put(struct rde_aspath *asp) static int prefix_add(struct bgpd_addr *, int, struct rib *, struct rde_peer *, uint32_t, uint32_t, struct rde_aspath *, struct rde_community *, struct nexthop *, - uint8_t, uint8_t); + uint8_t, uint8_t, int); static int prefix_move(struct prefix *, struct rde_peer *, struct rde_aspath *, struct rde_community *, - struct nexthop *, uint8_t, uint8_t); + struct nexthop *, uint8_t, uint8_t, int); static void prefix_link(struct prefix *, struct rib_entry *, struct pt_entry *, struct rde_peer *, uint32_t, uint32_t, @@ -967,8 +967,8 @@ prefix_adjout_match(struct rde_peer *peer, struct bgpd_addr *addr) */ int prefix_update(struct rib *rib, struct rde_peer *peer, uint32_t path_id, - uint32_t path_id_tx, struct filterstate *state, struct bgpd_addr *prefix, - int prefixlen) + uint32_t path_id_tx, struct filterstate *state, int filtered, + struct bgpd_addr *prefix, int prefixlen) { struct rde_aspath *asp, *nasp = &state->aspath; struct rde_community *comm, *ncomm = &state->communities; @@ -987,6 +987,10 @@ prefix_update(struct rib *rib, struct rde_peer *peer, uint32_t path_id, /* no change, update last change */ p->lastchange = getmonotime(); p->validation_state = state->vstate; + if (filtered) + p->flags |= PREFIX_FLAG_FILTERED; + else + p->flags &= ~PREFIX_FLAG_FILTERED; return (0); } } @@ -1010,11 +1014,11 @@ prefix_update(struct rib *rib, struct rde_peer *peer, uint32_t path_id, /* If the prefix was found move it else add it to the RIB. */ if (p != NULL) return (prefix_move(p, peer, asp, comm, state->nexthop, - state->nhflags, state->vstate)); + state->nhflags, state->vstate, filtered)); else return (prefix_add(prefix, prefixlen, rib, peer, path_id, path_id_tx, asp, comm, state->nexthop, state->nhflags, - state->vstate)); + state->vstate, filtered)); } /* @@ -1024,7 +1028,7 @@ static int prefix_add(struct bgpd_addr *prefix, int prefixlen, struct rib *rib, struct rde_peer *peer, uint32_t path_id, uint32_t path_id_tx, struct rde_aspath *asp, struct rde_community *comm, - struct nexthop *nexthop, uint8_t nhflags, uint8_t vstate) + struct nexthop *nexthop, uint8_t nhflags, uint8_t vstate, int filtered) { struct pt_entry *pte; struct prefix *p; @@ -1041,6 +1045,9 @@ prefix_add(struct bgpd_addr *prefix, int prefixlen, struct rib *rib, prefix_link(p, re, re->prefix, peer, path_id, path_id_tx, asp, comm, nexthop, nhflags, vstate); + if (filtered) + p->flags |= PREFIX_FLAG_FILTERED; + /* add possible pftable reference form aspath */ if (asp && asp->pftableid) rde_pftable_add(asp->pftableid, p); @@ -1055,7 +1062,7 @@ prefix_add(struct bgpd_addr *prefix, int prefixlen, struct rib *rib, static int prefix_move(struct prefix *p, struct rde_peer *peer, struct rde_aspath *asp, struct rde_community *comm, - struct nexthop *nexthop, uint8_t nhflags, uint8_t vstate) + struct nexthop *nexthop, uint8_t nhflags, uint8_t vstate, int filtered) { struct prefix *np; @@ -1070,6 +1077,9 @@ prefix_move(struct prefix *p, struct rde_peer *peer, prefix_link(np, prefix_re(p), p->pt, peer, p->path_id, p->path_id_tx, asp, comm, nexthop, nhflags, vstate); + if (filtered) + np->flags |= PREFIX_FLAG_FILTERED; + /* add possible pftable reference from new aspath */ if (asp && asp->pftableid) rde_pftable_add(asp->pftableid, np); diff --git a/usr.sbin/radiusd/radiusd.c b/usr.sbin/radiusd/radiusd.c index 571d18dd1..3b5ffb2f9 100644 --- a/usr.sbin/radiusd/radiusd.c +++ b/usr.sbin/radiusd/radiusd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: radiusd.c,v 1.52 2024/07/22 09:27:16 yasuoka Exp $ */ +/* $OpenBSD: radiusd.c,v 1.55 2024/08/14 07:06:50 yasuoka Exp $ */ /* * Copyright (c) 2013, 2023 Internet Initiative Japan Inc. @@ -518,10 +518,10 @@ radiusd_listen_handle_packet(struct radiusd_listen *listn, break; /* found it */ } if (q != NULL) { - log_info("Received %s(code=%d) from %s id=%d: duplicate " - "request by q=%u", radius_code_string(req_code), req_code, + log_info("Received %s(code=%d) from %s id=%d: duplicated " + "with q=%u", radius_code_string(req_code), req_code, peerstr, req_id, q->id); - /* XXX RFC 5080 suggests to answer the cached result */ + q = NULL; goto on_error; } @@ -708,9 +708,11 @@ radius_query_access_response(struct radius_query *q) goto on_error; q0 = q; q = q->prev; + /* dissolve the relation */ + q0->prev = NULL; + q->hasnext = false; radiusd_module_next_response(q->authen->auth->module, q, q_last->res); - q0->prev = NULL; radiusd_access_request_aborted(q0); return; } @@ -864,6 +866,7 @@ radiusd_access_request_next(struct radius_query *q, RADIUS_PACKET *pkt) radius_get_authenticator(pkt, q_next->req_auth); q_next->authen = authen; q_next->prev = q; + q->hasnext = true; strlcpy(q_next->username, username, sizeof(q_next->username)); TAILQ_INSERT_TAIL(&q->radiusd->query, q_next, next); @@ -878,8 +881,12 @@ radiusd_access_request_next(struct radius_query *q, RADIUS_PACKET *pkt) void radiusd_access_request_aborted(struct radius_query *q) { - if (q->prev != NULL) + if (q->hasnext) /* don't abort if filtering */ + return; + if (q->prev != NULL) { + q->prev->hasnext = false; radiusd_access_request_aborted(q->prev); + } if (q->req != NULL) radius_delete_packet(q->req); if (q->res != NULL) @@ -1398,6 +1405,7 @@ radiusd_module_imsg_read(struct radiusd_module *module) if (n == 0) return (0); radiusd_module_imsg(module, &imsg); + imsg_free(&imsg); } return (0); diff --git a/usr.sbin/radiusd/radiusd_ipcp.c b/usr.sbin/radiusd/radiusd_ipcp.c index 407af6d6c..3410aa412 100644 --- a/usr.sbin/radiusd/radiusd_ipcp.c +++ b/usr.sbin/radiusd/radiusd_ipcp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: radiusd_ipcp.c,v 1.8 2024/08/01 00:58:14 yasuoka Exp $ */ +/* $OpenBSD: radiusd_ipcp.c,v 1.9 2024/08/14 04:47:08 yasuoka Exp $ */ /* * Copyright (c) 2024 Internet Initiative Japan Inc. @@ -485,6 +485,8 @@ ipcp_config_set(void *ctx, const char *name, int argc, char * const * argv) } } } else if (strcmp(name, "dae") == 0) { + memset(&dae, 0, sizeof(dae)); + dae.sock = -1; if (!(argc >= 1 || strcmp(argv[1], "server") == 0)) { module_send_message(module->base, IMSG_NG, "`%s' is unknown", argv[1]); diff --git a/usr.sbin/radiusd/radiusd_local.h b/usr.sbin/radiusd/radiusd_local.h index 35a1da9e4..d58c6c7c6 100644 --- a/usr.sbin/radiusd/radiusd_local.h +++ b/usr.sbin/radiusd/radiusd_local.h @@ -1,4 +1,4 @@ -/* $OpenBSD: radiusd_local.h,v 1.16 2024/07/17 11:31:46 yasuoka Exp $ */ +/* $OpenBSD: radiusd_local.h,v 1.17 2024/08/14 07:04:54 yasuoka Exp $ */ /* * Copyright (c) 2013 Internet Initiative Japan Inc. @@ -131,6 +131,7 @@ struct radius_query { struct sockaddr_storage clientaddr; int clientaddrlen; int req_id; + bool hasnext; u_char req_auth[16]; struct radiusd_listen *listen; struct radiusd_client *client;