diff options
Diffstat (limited to 'sys/dev/pci/if_bwfm_pci.c')
-rw-r--r-- | sys/dev/pci/if_bwfm_pci.c | 1434 |
1 files changed, 1434 insertions, 0 deletions
diff --git a/sys/dev/pci/if_bwfm_pci.c b/sys/dev/pci/if_bwfm_pci.c new file mode 100644 index 00000000000..84fdcb36ce0 --- /dev/null +++ b/sys/dev/pci/if_bwfm_pci.c @@ -0,0 +1,1434 @@ +/* $OpenBSD: if_bwfm_pci.c,v 1.1 2017/12/24 19:50:56 patrick Exp $ */ +/* + * Copyright (c) 2010-2016 Broadcom Corporation + * Copyright (c) 2017 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and/or 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. + */ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/queue.h> +#include <sys/socket.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <net80211/ieee80211_var.h> + +#include <machine/bus.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#include <dev/ic/bwfmvar.h> +#include <dev/ic/bwfmreg.h> +#include <dev/pci/if_bwfm_pci.h> + +#define BWFM_DMA_D2H_SCRATCH_BUF_LEN 8 +#define BWFM_DMA_D2H_RINGUPD_BUF_LEN 1024 +#define BWFM_DMA_H2D_IOCTL_BUF_LEN ETHER_MAX_LEN + +#define BWFM_NUM_TX_MSGRINGS 2 +#define BWFM_NUM_RX_MSGRINGS 3 + +#define BWFM_NUM_TX_PKTIDS 2048 +#define BWFM_NUM_RX_PKTIDS 1024 + +#define BWFM_NUM_TX_DESCS 1 +#define BWFM_NUM_RX_DESCS 1 + +#ifdef BWFM_DEBUG +#define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0) +#define DPRINTFN(n, x) do { if (bwfm_debug >= (n)) printf x; } while (0) +static int bwfm_debug = 2; +#else +#define DPRINTF(x) do { ; } while (0) +#define DPRINTFN(n, x) do { ; } while (0) +#endif + +#define DEVNAME(sc) ((sc)->sc_sc.sc_dev.dv_xname) + +struct bwfm_pci_msgring { + uint32_t w_idx_addr; + uint32_t r_idx_addr; + uint32_t w_ptr; + uint32_t r_ptr; + int nitem; + int itemsz; + struct bwfm_pci_dmamem *ring; +}; + +struct bwfm_pci_buf { + bus_dmamap_t bb_map; + struct mbuf *bb_m; +}; + +struct bwfm_pci_pkts { + struct bwfm_pci_buf *pkts; + uint32_t npkt; + int last; +}; + +struct bwfm_pci_softc { + struct bwfm_softc sc_sc; + pci_chipset_tag_t sc_pc; + pcitag_t sc_tag; + pcireg_t sc_id; + void *sc_ih; + + bus_space_tag_t sc_reg_iot; + bus_space_handle_t sc_reg_ioh; + bus_size_t sc_reg_ios; + + bus_space_tag_t sc_tcm_iot; + bus_space_handle_t sc_tcm_ioh; + bus_size_t sc_tcm_ios; + + bus_dma_tag_t sc_dmat; + + uint32_t sc_shared_address; + uint32_t sc_shared_flags; + uint8_t sc_shared_version; + + uint8_t sc_dma_idx_sz; + struct bwfm_pci_dmamem *sc_dma_idx_buf; + size_t sc_dma_idx_bufsz; + + uint16_t sc_max_rxbufpost; + uint32_t sc_rx_dataoffset; + uint32_t sc_htod_mb_data_addr; + uint32_t sc_dtoh_mb_data_addr; + uint32_t sc_ring_info_addr; + + uint32_t sc_console_base_addr; + uint32_t sc_console_buf_addr; + uint32_t sc_console_buf_size; + + uint16_t sc_max_flowrings; + uint16_t sc_max_submissionrings; + uint16_t sc_max_completionrings; + + struct bwfm_pci_msgring sc_ctrl_submit; + struct bwfm_pci_msgring sc_rxpost_submit; + struct bwfm_pci_msgring sc_ctrl_complete; + struct bwfm_pci_msgring sc_tx_complete; + struct bwfm_pci_msgring sc_rx_complete; + struct bwfm_pci_msgring *sc_flowrings; + + struct bwfm_pci_dmamem *sc_scratch_buf; + struct bwfm_pci_dmamem *sc_ringupd_buf; + + struct bwfm_pci_dmamem *sc_ioctl_buf; + int sc_ioctl_reqid; + uint32_t sc_ioctl_resp_pktid; + uint32_t sc_ioctl_resp_ret_len; + uint32_t sc_ioctl_resp_status; + int sc_ioctl_poll; + + struct if_rxring sc_ioctl_ring; + struct if_rxring sc_event_ring; + struct if_rxring sc_rxbuf_ring; + + struct bwfm_pci_pkts sc_tx_pkts; + struct bwfm_pci_pkts sc_rx_pkts; +}; + +struct bwfm_pci_dmamem { + bus_dmamap_t bdm_map; + bus_dma_segment_t bdm_seg; + size_t bdm_size; + caddr_t bdm_kva; +}; + +#define BWFM_PCI_DMA_MAP(_bdm) ((_bdm)->bdm_map) +#define BWFM_PCI_DMA_LEN(_bdm) ((_bdm)->bdm_size) +#define BWFM_PCI_DMA_DVA(_bdm) ((_bdm)->bdm_map->dm_segs[0].ds_addr) +#define BWFM_PCI_DMA_KVA(_bdm) ((void *)(_bdm)->bdm_kva) + +int bwfm_pci_match(struct device *, void *, void *); +void bwfm_pci_attachhook(struct device *); +void bwfm_pci_attach(struct device *, struct device *, void *); +int bwfm_pci_detach(struct device *, int); + +int bwfm_pci_intr(void *); +void bwfm_pci_intr_enable(struct bwfm_pci_softc *); +void bwfm_pci_intr_disable(struct bwfm_pci_softc *); +int bwfm_pci_load_microcode(struct bwfm_pci_softc *, const u_char *, + size_t); +void bwfm_pci_select_core(struct bwfm_pci_softc *, int ); + +struct bwfm_pci_dmamem * + bwfm_pci_dmamem_alloc(struct bwfm_pci_softc *, bus_size_t, + bus_size_t); +void bwfm_pci_dmamem_free(struct bwfm_pci_softc *, struct bwfm_pci_dmamem *); +int bwfm_pci_pktid_new(struct bwfm_pci_softc *, + struct bwfm_pci_pkts *, struct mbuf *, + uint32_t *, paddr_t *); +struct mbuf * bwfm_pci_pktid_free(struct bwfm_pci_softc *, + struct bwfm_pci_pkts *, uint32_t); +void bwfm_pci_fill_rx_ioctl_ring(struct bwfm_pci_softc *, + struct if_rxring *, uint32_t); +void bwfm_pci_fill_rx_buf_ring(struct bwfm_pci_softc *); +void bwfm_pci_fill_rx_rings(struct bwfm_pci_softc *); +int bwfm_pci_setup_ring(struct bwfm_pci_softc *, struct bwfm_pci_msgring *, + int, size_t, uint32_t, uint32_t, int, uint32_t, uint32_t *); + +void bwfm_pci_ring_bell(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_update_rptr(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_update_wptr(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_write_rptr(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_write_wptr(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void * bwfm_pci_ring_write_reserve(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void * bwfm_pci_ring_write_reserve_multi(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *, int, int *); +void * bwfm_pci_ring_read_avail(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *, int *); +void bwfm_pci_ring_read_commit(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *, int); +void bwfm_pci_ring_write_commit(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_write_cancel(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *, int); + +void bwfm_pci_ring_rx(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_msg_rx(struct bwfm_pci_softc *, void *); + +uint32_t bwfm_pci_buscore_read(struct bwfm_softc *, uint32_t); +void bwfm_pci_buscore_write(struct bwfm_softc *, uint32_t, + uint32_t); +int bwfm_pci_buscore_prepare(struct bwfm_softc *); +int bwfm_pci_buscore_reset(struct bwfm_softc *); +void bwfm_pci_buscore_activate(struct bwfm_softc *, uint32_t); + +int bwfm_pci_txdata(struct bwfm_softc *, struct mbuf *); + +int bwfm_pci_msgbuf_query_dcmd(struct bwfm_softc *, int, + int, char *, size_t *); +int bwfm_pci_msgbuf_set_dcmd(struct bwfm_softc *, int, + int, char *, size_t); + +struct bwfm_buscore_ops bwfm_pci_buscore_ops = { + .bc_read = bwfm_pci_buscore_read, + .bc_write = bwfm_pci_buscore_write, + .bc_prepare = bwfm_pci_buscore_prepare, + .bc_reset = bwfm_pci_buscore_reset, + .bc_setup = NULL, + .bc_activate = bwfm_pci_buscore_activate, +}; + +struct bwfm_bus_ops bwfm_pci_bus_ops = { + .bs_init = NULL, + .bs_stop = NULL, + .bs_txdata = bwfm_pci_txdata, + .bs_txctl = NULL, + .bs_rxctl = NULL, +}; + +struct bwfm_proto_ops bwfm_pci_msgbuf_ops = { + .proto_query_dcmd = bwfm_pci_msgbuf_query_dcmd, + .proto_set_dcmd = bwfm_pci_msgbuf_set_dcmd, +}; + +struct cfattach bwfm_pci_ca = { + sizeof(struct bwfm_pci_softc), + bwfm_pci_match, + bwfm_pci_attach, + bwfm_pci_detach, +}; + +static const struct pci_matchid bwfm_pci_devices[] = { + { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM43602 }, +}; + +int +bwfm_pci_match(struct device *parent, void *match, void *aux) +{ + return (pci_matchbyid(aux, bwfm_pci_devices, + nitems(bwfm_pci_devices))); +} + +void +bwfm_pci_attach(struct device *parent, struct device *self, void *aux) +{ + struct bwfm_pci_softc *sc = (struct bwfm_pci_softc *)self; + struct pci_attach_args *pa = (struct pci_attach_args *)aux; + const char *intrstr; + pci_intr_handle_t ih; + + if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x00, + PCI_MAPREG_MEM_TYPE_64BIT, 0, &sc->sc_reg_iot, &sc->sc_reg_ioh, + NULL, &sc->sc_reg_ios, 0)) { + printf(": can't map bar0\n"); + return; + } + + if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x08, + PCI_MAPREG_MEM_TYPE_64BIT, 0, &sc->sc_tcm_iot, &sc->sc_tcm_ioh, + NULL, &sc->sc_tcm_ios, 0)) { + printf(": can't map bar1\n"); + goto bar0; + } + + sc->sc_pc = pa->pa_pc; + sc->sc_tag = pa->pa_tag; + sc->sc_id = pa->pa_id; + sc->sc_dmat = pa->pa_dmat; + + /* Map and establish the interrupt. */ + if (pci_intr_map_msi(pa, &ih) != 0 && pci_intr_map(pa, &ih) != 0) { + printf(": couldn't map interrupt\n"); + goto bar1; + } + intrstr = pci_intr_string(pa->pa_pc, ih); + + sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_NET | IPL_MPSAFE, + bwfm_pci_intr, sc, DEVNAME(sc)); + if (sc->sc_ih == NULL) { + printf(": couldn't establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + goto bar1; + } + printf(": %s\n", intrstr); + + config_mountroot(self, bwfm_pci_attachhook); + return; + +bar1: + bus_space_unmap(sc->sc_tcm_iot, sc->sc_tcm_ioh, sc->sc_tcm_ios); +bar0: + bus_space_unmap(sc->sc_reg_iot, sc->sc_reg_ioh, sc->sc_reg_ios); +} + +void +bwfm_pci_attachhook(struct device *self) +{ + struct bwfm_pci_softc *sc = (struct bwfm_pci_softc *)self; + struct bwfm_softc *bwfm = (void *)sc; + struct bwfm_pci_ringinfo ringinfo; + const char *name = NULL; + u_char *ucode; size_t size; + uint32_t d2h_w_idx_ptr, d2h_r_idx_ptr; + uint32_t h2d_w_idx_ptr, h2d_r_idx_ptr; + uint32_t idx_offset, reg; + int i; + + sc->sc_sc.sc_buscore_ops = &bwfm_pci_buscore_ops; + if (bwfm_chip_attach(&sc->sc_sc) != 0) { + printf("%s: cannot attach chip\n", DEVNAME(sc)); + return; + } + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGADDR, 0x4e0); + reg = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGDATA); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGDATA, reg); + + switch (bwfm->sc_chip.ch_chip) + { + case BRCM_CC_43602_CHIP_ID: + name = "brcmfmac43602-pcie.bin"; + break; + default: + break; + } + + if (name == NULL) { + printf("%s: unknown firmware\n", DEVNAME(sc)); + return; + } + + if (loadfirmware(name, &ucode, &size) != 0) { + printf("%s: failed loadfirmware of file %s\n", + DEVNAME(sc), name); + return; + } + + /* Retrieve RAM size from firmware. */ + if (size >= BWFM_RAMSIZE + 8) { + uint32_t *ramsize = (uint32_t *)&ucode[BWFM_RAMSIZE]; + if (letoh32(ramsize[0]) == BWFM_RAMSIZE_MAGIC) + bwfm->sc_chip.ch_ramsize = letoh32(ramsize[1]); + } + + if (bwfm_pci_load_microcode(sc, ucode, size) != 0) { + printf("%s: could not load microcode\n", + DEVNAME(sc)); + free(ucode, M_DEVBUF, size); + return; + } + free(ucode, M_DEVBUF, size); + + sc->sc_shared_flags = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_INFO); + sc->sc_shared_version = sc->sc_shared_flags; + if (sc->sc_shared_version > BWFM_SHARED_INFO_MAX_VERSION || + sc->sc_shared_version < BWFM_SHARED_INFO_MIN_VERSION) { + printf("%s: PCIe version %d unsupported\n", + DEVNAME(sc), sc->sc_shared_version); + return; + } + + if (sc->sc_shared_flags & BWFM_SHARED_INFO_DMA_INDEX) { + if (sc->sc_shared_flags & BWFM_SHARED_INFO_DMA_2B_IDX) + sc->sc_dma_idx_sz = sizeof(uint16_t); + else + sc->sc_dma_idx_sz = sizeof(uint32_t); + } + + /* Maximum RX data buffers in the ring. */ + sc->sc_max_rxbufpost = bus_space_read_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_MAX_RXBUFPOST); + if (sc->sc_max_rxbufpost == 0) + sc->sc_max_rxbufpost = BWFM_SHARED_MAX_RXBUFPOST_DEFAULT; + + /* Alternative offset of data in a packet */ + sc->sc_rx_dataoffset = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_RX_DATAOFFSET); + + /* For Power Management */ + sc->sc_htod_mb_data_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_HTOD_MB_DATA_ADDR); + sc->sc_dtoh_mb_data_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DTOH_MB_DATA_ADDR); + + /* Ring information */ + sc->sc_ring_info_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_RING_INFO_ADDR); + + /* Firmware's "dmesg" */ + sc->sc_console_base_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_CONSOLE_ADDR); + sc->sc_console_buf_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_console_base_addr + BWFM_CONSOLE_BUFADDR); + sc->sc_console_buf_size = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_console_base_addr + BWFM_CONSOLE_BUFSIZE); + + /* Read ring information. */ + bus_space_read_region_1(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_ring_info_addr, (void *)&ringinfo, sizeof(ringinfo)); + + if (sc->sc_shared_version >= 6) { + sc->sc_max_submissionrings = le16toh(ringinfo.max_submissionrings); + sc->sc_max_flowrings = le16toh(ringinfo.max_flowrings); + sc->sc_max_completionrings = le16toh(ringinfo.max_completionrings); + } else { + sc->sc_max_submissionrings = le16toh(ringinfo.max_flowrings); + sc->sc_max_flowrings = sc->sc_max_submissionrings - + BWFM_NUM_TX_MSGRINGS; + sc->sc_max_completionrings = BWFM_NUM_RX_MSGRINGS; + } + + if (sc->sc_dma_idx_sz == 0) { + d2h_w_idx_ptr = letoh32(ringinfo.d2h_w_idx_ptr); + d2h_r_idx_ptr = letoh32(ringinfo.d2h_r_idx_ptr); + h2d_w_idx_ptr = letoh32(ringinfo.h2d_w_idx_ptr); + h2d_r_idx_ptr = letoh32(ringinfo.h2d_r_idx_ptr); + idx_offset = sizeof(uint32_t); + } else { + uint64_t address; + + /* Each TX/RX Ring has a Read and Write Ptr */ + sc->sc_dma_idx_bufsz = (sc->sc_max_submissionrings + + sc->sc_max_completionrings) * sc->sc_dma_idx_sz * 2; + sc->sc_dma_idx_buf = bwfm_pci_dmamem_alloc(sc, + sc->sc_dma_idx_bufsz, 8); + if (sc->sc_dma_idx_buf == NULL) { + /* XXX: Fallback to TCM? */ + printf("%s: cannot allocate idx buf\n", + DEVNAME(sc)); + return; + } + + idx_offset = sc->sc_dma_idx_sz; + h2d_w_idx_ptr = 0; + address = BWFM_PCI_DMA_DVA(sc->sc_dma_idx_buf); + ringinfo.h2d_w_idx_hostaddr_low = + htole32(address & 0xffffffff); + ringinfo.h2d_w_idx_hostaddr_high = + htole32(address >> 32); + + h2d_r_idx_ptr = h2d_w_idx_ptr + + sc->sc_max_submissionrings * idx_offset; + address += sc->sc_max_submissionrings * idx_offset; + ringinfo.h2d_r_idx_hostaddr_low = + htole32(address & 0xffffffff); + ringinfo.h2d_r_idx_hostaddr_high = + htole32(address >> 32); + + d2h_w_idx_ptr = h2d_r_idx_ptr + + sc->sc_max_submissionrings * idx_offset; + address += sc->sc_max_submissionrings * idx_offset; + ringinfo.d2h_w_idx_hostaddr_low = + htole32(address & 0xffffffff); + ringinfo.d2h_w_idx_hostaddr_high = + htole32(address >> 32); + + d2h_r_idx_ptr = d2h_w_idx_ptr + + sc->sc_max_completionrings * idx_offset; + address += sc->sc_max_completionrings * idx_offset; + ringinfo.d2h_r_idx_hostaddr_low = + htole32(address & 0xffffffff); + ringinfo.d2h_r_idx_hostaddr_high = + htole32(address >> 32); + + bus_space_write_region_1(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_ring_info_addr, (void *)&ringinfo, sizeof(ringinfo)); + } + + uint32_t ring_mem_ptr = letoh32(ringinfo.ringmem); + /* TX ctrl ring: Send ctrl buffers, send IOCTLs */ + if (bwfm_pci_setup_ring(sc, &sc->sc_ctrl_submit, 64, 40, + h2d_w_idx_ptr, h2d_r_idx_ptr, 0, idx_offset, + &ring_mem_ptr)) + goto cleanup; + /* TX rxpost ring: Send clean data mbufs for RX */ + if (bwfm_pci_setup_ring(sc, &sc->sc_rxpost_submit, 512, 32, + h2d_w_idx_ptr, h2d_r_idx_ptr, 1, idx_offset, + &ring_mem_ptr)) + goto cleanup; + /* RX completion rings: recv our filled buffers back */ + if (bwfm_pci_setup_ring(sc, &sc->sc_ctrl_complete, 64, 24, + d2h_w_idx_ptr, d2h_r_idx_ptr, 0, idx_offset, + &ring_mem_ptr)) + goto cleanup; + if (bwfm_pci_setup_ring(sc, &sc->sc_tx_complete, 1024, 16, + d2h_w_idx_ptr, d2h_r_idx_ptr, 1, idx_offset, + &ring_mem_ptr)) + goto cleanup; + if (bwfm_pci_setup_ring(sc, &sc->sc_rx_complete, 512, 32, + d2h_w_idx_ptr, d2h_r_idx_ptr, 2, idx_offset, + &ring_mem_ptr)) + goto cleanup; + +#if 0 + /* Dynamic TX rings for actual data */ + sc->sc_flowrings = malloc(sc->sc_max_flowrings * + sizeof(struct bwfm_pci_msgring), M_DEVBUF, M_WAITOK | M_ZERO); +#endif + + /* Scratch and ring update buffers for firmware */ + if ((sc->sc_scratch_buf = bwfm_pci_dmamem_alloc(sc, + BWFM_DMA_D2H_SCRATCH_BUF_LEN, 8)) == NULL) + goto cleanup; + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_SCRATCH_ADDR_LOW, + BWFM_PCI_DMA_DVA(sc->sc_scratch_buf) & 0xffffffff); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_SCRATCH_ADDR_HIGH, + BWFM_PCI_DMA_DVA(sc->sc_scratch_buf) >> 32); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_SCRATCH_LEN, + BWFM_DMA_D2H_SCRATCH_BUF_LEN); + + if ((sc->sc_ringupd_buf = bwfm_pci_dmamem_alloc(sc, + BWFM_DMA_D2H_RINGUPD_BUF_LEN, 8)) == NULL) + goto cleanup; + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_RINGUPD_ADDR_LOW, + BWFM_PCI_DMA_DVA(sc->sc_ringupd_buf) & 0xffffffff); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_RINGUPD_ADDR_HIGH, + BWFM_PCI_DMA_DVA(sc->sc_ringupd_buf) >> 32); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_RINGUPD_LEN, + BWFM_DMA_D2H_RINGUPD_BUF_LEN); + + if ((sc->sc_ioctl_buf = bwfm_pci_dmamem_alloc(sc, + BWFM_DMA_H2D_IOCTL_BUF_LEN, 8)) == NULL) + goto cleanup; + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2); + bwfm_pci_intr_enable(sc); + + /* Maps RX mbufs to a packet id and back. */ + sc->sc_rx_pkts.npkt = BWFM_NUM_RX_PKTIDS; + sc->sc_rx_pkts.pkts = malloc(BWFM_NUM_RX_PKTIDS * + sizeof(struct bwfm_pci_buf), M_DEVBUF, M_WAITOK | M_ZERO); + for (i = 0; i < BWFM_NUM_RX_PKTIDS; i++) + bus_dmamap_create(sc->sc_dmat, MSGBUF_MAX_PKT_SIZE, + BWFM_NUM_RX_DESCS, MSGBUF_MAX_PKT_SIZE, 0, BUS_DMA_WAITOK, + &sc->sc_rx_pkts.pkts[i].bb_map); + sc->sc_tx_pkts.npkt = BWFM_NUM_TX_PKTIDS; + + /* Maps TX mbufs to a packet id and back. */ + sc->sc_tx_pkts.pkts = malloc(BWFM_NUM_TX_PKTIDS + * sizeof(struct bwfm_pci_buf), M_DEVBUF, M_WAITOK | M_ZERO); + for (i = 0; i < BWFM_NUM_TX_PKTIDS; i++) + bus_dmamap_create(sc->sc_dmat, MSGBUF_MAX_PKT_SIZE, + BWFM_NUM_TX_DESCS, MSGBUF_MAX_PKT_SIZE, 0, BUS_DMA_WAITOK, + &sc->sc_tx_pkts.pkts[i].bb_map); + + if_rxr_init(&sc->sc_ioctl_ring, 8, 8); + if_rxr_init(&sc->sc_event_ring, 8, 8); + if_rxr_init(&sc->sc_rxbuf_ring, 2, sc->sc_max_rxbufpost); + bwfm_pci_fill_rx_rings(sc); + + sc->sc_ioctl_poll = 1; + sc->sc_sc.sc_bus_ops = &bwfm_pci_bus_ops; + sc->sc_sc.sc_proto_ops = &bwfm_pci_msgbuf_ops; + bwfm_attach(&sc->sc_sc); + sc->sc_ioctl_poll = 0; + return; + +cleanup: + if (sc->sc_ioctl_buf) + bwfm_pci_dmamem_free(sc, sc->sc_ioctl_buf); + if (sc->sc_ringupd_buf) + bwfm_pci_dmamem_free(sc, sc->sc_ringupd_buf); + if (sc->sc_scratch_buf) + bwfm_pci_dmamem_free(sc, sc->sc_scratch_buf); + if (sc->sc_rx_complete.ring) + bwfm_pci_dmamem_free(sc, sc->sc_rx_complete.ring); + if (sc->sc_tx_complete.ring) + bwfm_pci_dmamem_free(sc, sc->sc_tx_complete.ring); + if (sc->sc_ctrl_complete.ring) + bwfm_pci_dmamem_free(sc, sc->sc_ctrl_complete.ring); + if (sc->sc_rxpost_submit.ring) + bwfm_pci_dmamem_free(sc, sc->sc_rxpost_submit.ring); + if (sc->sc_ctrl_submit.ring) + bwfm_pci_dmamem_free(sc, sc->sc_ctrl_submit.ring); + if (sc->sc_dma_idx_buf) + bwfm_pci_dmamem_free(sc, sc->sc_dma_idx_buf); +} + +int +bwfm_pci_load_microcode(struct bwfm_pci_softc *sc, const u_char *ucode, size_t size) +{ + struct bwfm_softc *bwfm = (void *)sc; + struct bwfm_core *core; + uint32_t shared; + int i; + + if (bwfm->sc_chip.ch_chip == BRCM_CC_43602_CHIP_ID) { + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_ARM_CR4); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_ARMCR4REG_BANKIDX, 5); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_ARMCR4REG_BANKPDA, 0); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_ARMCR4REG_BANKIDX, 7); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_ARMCR4REG_BANKPDA, 0); + } + + for (i = 0; i < size; i++) + bus_space_write_1(sc->sc_tcm_iot, sc->sc_tcm_ioh, + bwfm->sc_chip.ch_rambase + i, ucode[i]); + + /* Firmware replaces this with a pointer once up. */ + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize - 4, 0); + + /* TODO: restore NVRAM */ + + /* Load reset vector from firmware and kickstart core. */ + core = bwfm_chip_get_core(bwfm, BWFM_AGENT_INTERNAL_MEM); + bwfm->sc_chip.ch_core_reset(bwfm, core, 0, 0, 0); + bwfm_chip_set_active(bwfm, *(uint32_t *)ucode); + + for (i = 0; i < 40; i++) { + delay(50 * 1000); + shared = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize - 4); + if (shared) + break; + } + if (!shared) { + printf("%s: firmware did not come up\n", DEVNAME(sc)); + return 1; + } + + sc->sc_shared_address = shared; + return 0; +} + +int +bwfm_pci_detach(struct device *self, int flags) +{ + struct bwfm_pci_softc *sc = (struct bwfm_pci_softc *)self; + + bwfm_detach(&sc->sc_sc, flags); + + /* FIXME: free RX buffers */ + /* FIXME: free TX buffers */ + /* FIXME: free more memory */ + + bwfm_pci_dmamem_free(sc, sc->sc_ioctl_buf); + bwfm_pci_dmamem_free(sc, sc->sc_ringupd_buf); + bwfm_pci_dmamem_free(sc, sc->sc_scratch_buf); + bwfm_pci_dmamem_free(sc, sc->sc_rx_complete.ring); + bwfm_pci_dmamem_free(sc, sc->sc_tx_complete.ring); + bwfm_pci_dmamem_free(sc, sc->sc_ctrl_complete.ring); + bwfm_pci_dmamem_free(sc, sc->sc_rxpost_submit.ring); + bwfm_pci_dmamem_free(sc, sc->sc_ctrl_submit.ring); + bwfm_pci_dmamem_free(sc, sc->sc_dma_idx_buf); + return 0; +} + +/* DMA code */ +struct bwfm_pci_dmamem * +bwfm_pci_dmamem_alloc(struct bwfm_pci_softc *sc, bus_size_t size, bus_size_t align) +{ + struct bwfm_pci_dmamem *bdm; + int nsegs; + + bdm = malloc(sizeof(*bdm), M_DEVBUF, M_WAITOK | M_ZERO); + bdm->bdm_size = size; + + if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &bdm->bdm_map) != 0) + goto bdmfree; + + if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &bdm->bdm_seg, 1, + &nsegs, BUS_DMA_WAITOK) != 0) + goto destroy; + + if (bus_dmamem_map(sc->sc_dmat, &bdm->bdm_seg, nsegs, size, + &bdm->bdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0) + goto free; + + if (bus_dmamap_load(sc->sc_dmat, bdm->bdm_map, bdm->bdm_kva, size, + NULL, BUS_DMA_WAITOK) != 0) + goto unmap; + + bzero(bdm->bdm_kva, size); + + return (bdm); + +unmap: + bus_dmamem_unmap(sc->sc_dmat, bdm->bdm_kva, size); +free: + bus_dmamem_free(sc->sc_dmat, &bdm->bdm_seg, 1); +destroy: + bus_dmamap_destroy(sc->sc_dmat, bdm->bdm_map); +bdmfree: + free(bdm, M_DEVBUF, 0); + + return (NULL); +} + +void +bwfm_pci_dmamem_free(struct bwfm_pci_softc *sc, struct bwfm_pci_dmamem *bdm) +{ + bus_dmamem_unmap(sc->sc_dmat, bdm->bdm_kva, bdm->bdm_size); + bus_dmamem_free(sc->sc_dmat, &bdm->bdm_seg, 1); + bus_dmamap_destroy(sc->sc_dmat, bdm->bdm_map); + free(bdm, M_DEVBUF, 0); +} + +/* + * We need a simple mapping from a packet ID to mbufs, because when + * a transfer completed, we only know the ID so we have to look up + * the memory for the ID. This simply looks for an empty slot. + */ +int +bwfm_pci_pktid_new(struct bwfm_pci_softc *sc, struct bwfm_pci_pkts *pkts, + struct mbuf *m, uint32_t *pktid, paddr_t *paddr) +{ + int i, idx; + + idx = pkts->last + 1; + for (i = 0; i < pkts->npkt; i++) { + if (idx == pkts->npkt) + idx = 0; + if (pkts->pkts[idx].bb_m == NULL) { + if (bus_dmamap_load_mbuf(sc->sc_dmat, + pkts->pkts[idx].bb_map, m, BUS_DMA_NOWAIT) != 0) { + printf("%s: could not load mbuf DMA map", + DEVNAME(sc)); + return 1; + } + pkts->last = idx; + pkts->pkts[idx].bb_m = m; + *pktid = idx; + *paddr = pkts->pkts[idx].bb_map->dm_segs[0].ds_addr; + return 0; + } + idx++; + } + return 1; +} + +struct mbuf * +bwfm_pci_pktid_free(struct bwfm_pci_softc *sc, struct bwfm_pci_pkts *pkts, + uint32_t pktid) +{ + struct mbuf *m; + + if (pktid >= pkts->npkt || pkts->pkts[pktid].bb_m == NULL) + return NULL; + bus_dmamap_unload(sc->sc_dmat, pkts->pkts[pktid].bb_map); + m = pkts->pkts[pktid].bb_m; + pkts->pkts[pktid].bb_m = NULL; + return m; +} + +void +bwfm_pci_fill_rx_rings(struct bwfm_pci_softc *sc) +{ + bwfm_pci_fill_rx_ioctl_ring(sc, &sc->sc_ioctl_ring, + MSGBUF_TYPE_IOCTLRESP_BUF_POST); + bwfm_pci_fill_rx_ioctl_ring(sc, &sc->sc_event_ring, + MSGBUF_TYPE_EVENT_BUF_POST); + bwfm_pci_fill_rx_buf_ring(sc); +} + +void +bwfm_pci_fill_rx_ioctl_ring(struct bwfm_pci_softc *sc, struct if_rxring *rxring, + uint32_t msgtype) +{ + struct msgbuf_rx_ioctl_resp_or_event *req; + struct mbuf *m; + uint32_t pktid; + paddr_t paddr; + int s, slots; + + s = splnet(); + for (slots = if_rxr_get(rxring, 8); slots > 0; slots--) { + req = bwfm_pci_ring_write_reserve(sc, &sc->sc_ctrl_submit); + if (req == NULL) + break; + m = MCLGETI(NULL, M_DONTWAIT, NULL, MSGBUF_MAX_PKT_SIZE); + if (m == NULL) { + bwfm_pci_ring_write_cancel(sc, &sc->sc_ctrl_submit, 1); + break; + } + m->m_len = m->m_pkthdr.len = MSGBUF_MAX_PKT_SIZE; + if (bwfm_pci_pktid_new(sc, &sc->sc_rx_pkts, m, &pktid, &paddr)) { + bwfm_pci_ring_write_cancel(sc, &sc->sc_ctrl_submit, 1); + m_freem(m); + break; + } + memset(req, 0, sizeof(*req)); + req->msg.msgtype = msgtype; + req->msg.request_id = htole32(pktid); + req->host_buf_len = htole16(MSGBUF_MAX_PKT_SIZE); + req->host_buf_addr.high_addr = htole32(paddr >> 32); + req->host_buf_addr.low_addr = htole32(paddr & 0xffffffff); + bwfm_pci_ring_write_commit(sc, &sc->sc_ctrl_submit); + } + if_rxr_put(rxring, slots); + splx(s); +} + +void +bwfm_pci_fill_rx_buf_ring(struct bwfm_pci_softc *sc) +{ + struct msgbuf_rx_bufpost *req; + struct mbuf *m; + uint32_t pktid; + paddr_t paddr; + int s, slots; + + s = splnet(); + for (slots = if_rxr_get(&sc->sc_rxbuf_ring, sc->sc_max_rxbufpost); + slots > 0; slots--) { + req = bwfm_pci_ring_write_reserve(sc, &sc->sc_rxpost_submit); + if (req == NULL) + break; + m = MCLGETI(NULL, M_DONTWAIT, NULL, MSGBUF_MAX_PKT_SIZE); + if (m == NULL) { + bwfm_pci_ring_write_cancel(sc, &sc->sc_rxpost_submit, 1); + break; + } + m->m_len = m->m_pkthdr.len = MSGBUF_MAX_PKT_SIZE; + if (bwfm_pci_pktid_new(sc, &sc->sc_rx_pkts, m, &pktid, &paddr)) { + bwfm_pci_ring_write_cancel(sc, &sc->sc_rxpost_submit, 1); + m_freem(m); + break; + } + memset(req, 0, sizeof(*req)); + req->msg.msgtype = MSGBUF_TYPE_RXBUF_POST; + req->msg.request_id = htole32(pktid); + req->data_buf_len = htole16(MSGBUF_MAX_PKT_SIZE); + req->data_buf_addr.high_addr = htole32(paddr >> 32); + req->data_buf_addr.low_addr = htole32(paddr & 0xffffffff); + bwfm_pci_ring_write_commit(sc, &sc->sc_rxpost_submit); + } + if_rxr_put(&sc->sc_rxbuf_ring, slots); + splx(s); +} + +int +bwfm_pci_setup_ring(struct bwfm_pci_softc *sc, struct bwfm_pci_msgring *ring, + int nitem, size_t itemsz, uint32_t w_idx, uint32_t r_idx, + int idx, uint32_t idx_off, uint32_t *ring_mem) +{ + ring->w_idx_addr = w_idx + idx * idx_off; + ring->r_idx_addr = r_idx + idx * idx_off; + ring->nitem = nitem; + ring->itemsz = itemsz; + bwfm_pci_ring_write_rptr(sc, ring); + bwfm_pci_ring_write_wptr(sc, ring); + + ring->ring = bwfm_pci_dmamem_alloc(sc, nitem * itemsz, 8); + if (ring->ring == NULL) + return ENOMEM; + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + *ring_mem + BWFM_RING_MEM_BASE_ADDR_LOW, + BWFM_PCI_DMA_DVA(ring->ring) & 0xffffffff); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + *ring_mem + BWFM_RING_MEM_BASE_ADDR_HIGH, + BWFM_PCI_DMA_DVA(ring->ring) >> 32); + bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + *ring_mem + BWFM_RING_MAX_ITEM, nitem); + bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + *ring_mem + BWFM_RING_LEN_ITEMS, itemsz); + *ring_mem = *ring_mem + BWFM_RING_MEM_SZ; + return 0; +} + +/* Ring helpers */ +void +bwfm_pci_ring_bell(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_H2D_MAILBOX, 1); +} + +void +bwfm_pci_ring_update_rptr(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + if (sc->sc_dma_idx_sz == 0) { + ring->r_ptr = bus_space_read_2(sc->sc_tcm_iot, + sc->sc_tcm_ioh, ring->r_idx_addr); + } else { + ring->r_ptr = *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf) + + ring->r_idx_addr); + } +} + +void +bwfm_pci_ring_update_wptr(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + if (sc->sc_dma_idx_sz == 0) { + ring->w_ptr = bus_space_read_2(sc->sc_tcm_iot, + sc->sc_tcm_ioh, ring->w_idx_addr); + } else { + ring->w_ptr = *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf) + + ring->w_idx_addr); + } +} + +void +bwfm_pci_ring_write_rptr(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + if (sc->sc_dma_idx_sz == 0) { + bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + ring->r_idx_addr, ring->r_ptr); + } else { + *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf) + + ring->r_idx_addr) = ring->r_ptr; + } +} + +void +bwfm_pci_ring_write_wptr(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + if (sc->sc_dma_idx_sz == 0) { + bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + ring->w_idx_addr, ring->w_ptr); + } else { + *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf) + + ring->w_idx_addr) = ring->w_ptr; + } +} + +/* + * Retrieve a free descriptor to put new stuff in, but don't commit + * to it yet so we can rollback later if any error occurs. + */ +void * +bwfm_pci_ring_write_reserve(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + int available; + char *ret; + + bwfm_pci_ring_update_rptr(sc, ring); + + if (ring->r_ptr > ring->w_ptr) + available = ring->r_ptr - ring->w_ptr; + else + available = ring->r_ptr + (ring->nitem - ring->w_ptr); + + if (available < 1) + return NULL; + + ret = BWFM_PCI_DMA_KVA(ring->ring) + (ring->w_ptr * ring->itemsz); + ring->w_ptr += 1; + if (ring->w_ptr == ring->nitem) + ring->w_ptr = 0; + return ret; +} + +void * +bwfm_pci_ring_write_reserve_multi(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring, int count, int *avail) +{ + int available; + char *ret; + + bwfm_pci_ring_update_rptr(sc, ring); + + if (ring->r_ptr > ring->w_ptr) + available = ring->r_ptr - ring->w_ptr; + else + available = ring->r_ptr + (ring->nitem - ring->w_ptr); + + if (available < 1) + return NULL; + + ret = BWFM_PCI_DMA_KVA(ring->ring) + (ring->w_ptr * ring->itemsz); + *avail = min(count, available - 1); + if (*avail + ring->w_ptr > ring->nitem) + *avail = ring->nitem - ring->w_ptr; + ring->w_ptr += *avail; + if (ring->w_ptr == ring->nitem) + ring->w_ptr = 0; + return ret; +} + +/* + * Read number of descriptors available (submitted by the firmware) + * and retrieve pointer to first descriptor. + */ +void * +bwfm_pci_ring_read_avail(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring, int *avail) +{ + bwfm_pci_ring_update_wptr(sc, ring); + + if (ring->w_ptr >= ring->r_ptr) + *avail = ring->w_ptr - ring->r_ptr; + else + *avail = ring->nitem - ring->r_ptr; + + if (*avail == 0) + return NULL; + + return BWFM_PCI_DMA_KVA(ring->ring) + (ring->r_ptr * ring->itemsz); +} + +/* + * Let firmware know we read N descriptors. + */ +void +bwfm_pci_ring_read_commit(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring, int nitem) +{ + ring->r_ptr += nitem; + if (ring->r_ptr == ring->nitem) + ring->r_ptr = 0; + bwfm_pci_ring_write_rptr(sc, ring); +} + +/* + * Let firmware know that we submitted some descriptors. + */ +void +bwfm_pci_ring_write_commit(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + bwfm_pci_ring_write_wptr(sc, ring); + bwfm_pci_ring_bell(sc, ring); +} + +/* + * Rollback N descriptors in case we don't actually want + * to commit to it. + */ +void +bwfm_pci_ring_write_cancel(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring, int nitem) +{ + if (ring->w_ptr == 0) + ring->w_ptr = ring->nitem - nitem; + else + ring->w_ptr -= nitem; +} + +/* + * Foreach written descriptor on the ring, pass the descriptor to + * a message handler and let the firmware know we handled it. + */ +void +bwfm_pci_ring_rx(struct bwfm_pci_softc *sc, struct bwfm_pci_msgring *ring) +{ + void *buf; + int avail, processed; + +again: + buf = bwfm_pci_ring_read_avail(sc, ring, &avail); + if (buf == NULL) + return; + + processed = 0; + while (avail) { + bwfm_pci_msg_rx(sc, buf + sc->sc_rx_dataoffset); + buf += ring->itemsz; + processed++; + if (processed == 48) { + bwfm_pci_ring_read_commit(sc, ring, processed); + processed = 0; + } + avail--; + } + if (processed) + bwfm_pci_ring_read_commit(sc, ring, processed); + if (ring->r_ptr == 0) + goto again; +} + +void +bwfm_pci_msg_rx(struct bwfm_pci_softc *sc, void *buf) +{ + struct msgbuf_ioctl_resp_hdr *resp; + struct msgbuf_rx_event *event; + struct msgbuf_common_hdr *msg; + struct mbuf *m; + + msg = (struct msgbuf_common_hdr *)buf; + switch (msg->msgtype) + { + case MSGBUF_TYPE_IOCTLPTR_REQ_ACK: + break; + case MSGBUF_TYPE_IOCTL_CMPLT: + resp = (struct msgbuf_ioctl_resp_hdr *)buf; + sc->sc_ioctl_resp_pktid = letoh32(resp->msg.request_id); + sc->sc_ioctl_resp_ret_len = letoh16(resp->resp_len); + sc->sc_ioctl_resp_status = letoh16(resp->compl_hdr.status); + if_rxr_put(&sc->sc_ioctl_ring, 1); + bwfm_pci_fill_rx_rings(sc); + wakeup(&sc->sc_ioctl_buf); + break; + case MSGBUF_TYPE_WL_EVENT: + event = (struct msgbuf_rx_event *)buf; + m = bwfm_pci_pktid_free(sc, &sc->sc_rx_pkts, + letoh32(event->msg.request_id)); + if (m == NULL) + break; + m_adj(m, sc->sc_rx_dataoffset); + bwfm_rx(&sc->sc_sc, mtod(m, char *), letoh16(event->event_data_len)); + m_freem(m); + if_rxr_put(&sc->sc_event_ring, 1); + bwfm_pci_fill_rx_rings(sc); + break; + default: + printf("%s: msgtype 0x%08x\n", __func__, msg->msgtype); + break; + } +} + +/* Bus core helpers */ +void +bwfm_pci_select_core(struct bwfm_pci_softc *sc, int id) +{ + struct bwfm_softc *bwfm = (void *)sc; + struct bwfm_core *core; + + core = bwfm_chip_get_core(bwfm, id); + if (core == NULL) { + printf("%s: could not find core to select", DEVNAME(sc)); + return; + } + + pci_conf_write(sc->sc_pc, sc->sc_tag, + BWFM_PCI_BAR0_WINDOW, core->co_base); + if (pci_conf_read(sc->sc_pc, sc->sc_tag, + BWFM_PCI_BAR0_WINDOW) != core->co_base) + pci_conf_write(sc->sc_pc, sc->sc_tag, + BWFM_PCI_BAR0_WINDOW, core->co_base); +} + +uint32_t +bwfm_pci_buscore_read(struct bwfm_softc *bwfm, uint32_t reg) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + uint32_t page, offset; + + page = reg & ~(BWFM_PCI_BAR0_REG_SIZE - 1); + offset = reg & (BWFM_PCI_BAR0_REG_SIZE - 1); + pci_conf_write(sc->sc_pc, sc->sc_tag, BWFM_PCI_BAR0_WINDOW, page); + return bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, offset); +} + +void +bwfm_pci_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + uint32_t page, offset; + + page = reg & ~(BWFM_PCI_BAR0_REG_SIZE - 1); + offset = reg & (BWFM_PCI_BAR0_REG_SIZE - 1); + pci_conf_write(sc->sc_pc, sc->sc_tag, BWFM_PCI_BAR0_WINDOW, page); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, offset, val); +} + +int +bwfm_pci_buscore_prepare(struct bwfm_softc *bwfm) +{ + return 0; +} + +int +bwfm_pci_buscore_reset(struct bwfm_softc *bwfm) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + struct bwfm_core *core; + uint32_t reg; + int i; + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2); + reg = pci_conf_read(sc->sc_pc, sc->sc_tag, + BWFM_PCI_CFGREG_LINK_STATUS_CTRL); + pci_conf_write(sc->sc_pc, sc->sc_tag, BWFM_PCI_CFGREG_LINK_STATUS_CTRL, + reg & ~BWFM_PCI_CFGREG_LINK_STATUS_CTRL_ASPM_ENAB); + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_CHIPCOMMON); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_CHIP_REG_WATCHDOG, 4); + delay(100 * 1000); + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2); + pci_conf_write(sc->sc_pc, sc->sc_tag, + BWFM_PCI_CFGREG_LINK_STATUS_CTRL, reg); + + core = bwfm_chip_get_core(bwfm, BWFM_AGENT_CORE_PCIE2); + if (core->co_rev <= 13) { + uint16_t cfg_offset[] = { + BWFM_PCI_CFGREG_STATUS_CMD, + BWFM_PCI_CFGREG_PM_CSR, + BWFM_PCI_CFGREG_MSI_CAP, + BWFM_PCI_CFGREG_MSI_ADDR_L, + BWFM_PCI_CFGREG_MSI_ADDR_H, + BWFM_PCI_CFGREG_MSI_DATA, + BWFM_PCI_CFGREG_LINK_STATUS_CTRL2, + BWFM_PCI_CFGREG_RBAR_CTRL, + BWFM_PCI_CFGREG_PML1_SUB_CTRL1, + BWFM_PCI_CFGREG_REG_BAR2_CONFIG, + BWFM_PCI_CFGREG_REG_BAR3_CONFIG, + }; + + for (i = 0; i < nitems(cfg_offset); i++) { + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGADDR, cfg_offset[i]); + reg = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGDATA); + DPRINTFN(3, ("%s: config offset 0x%04x, value 0x%04x\n", + DEVNAME(sc), cfg_offset[i], reg)); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGDATA, reg); + } + } + + reg = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXINT); + if (reg != 0xffffffff) + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXINT, reg); + + return 0; +} + +void +bwfm_pci_buscore_activate(struct bwfm_softc *bwfm, uint32_t rstvec) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, 0, rstvec); +} + +int +bwfm_pci_txdata(struct bwfm_softc *bwfm, struct mbuf *m) +{ +#ifdef BWFM_DEBUG + struct bwfm_pci_softc *sc = (void *)bwfm; + DPRINTF(("%s: %s\n", DEVNAME(sc), __func__)); +#endif + return ENOBUFS; +} + +int +bwfm_pci_intr(void *v) +{ + struct bwfm_pci_softc *sc = (void *)v; + uint32_t status; + + if ((status = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXINT)) == 0) + return 0; + + bwfm_pci_intr_disable(sc); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXINT, status); + + if (status & (BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_0 | + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_1)) + printf("%s: handle MB data\n", __func__); + + if (status & BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H_DB) { + bwfm_pci_ring_rx(sc, &sc->sc_rx_complete); + bwfm_pci_ring_rx(sc, &sc->sc_tx_complete); + bwfm_pci_ring_rx(sc, &sc->sc_ctrl_complete); + } + +#if 0 + { + uint32_t newidx = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_console_base_addr + BWFM_CONSOLE_WRITEIDX); + uint32_t readidx = 0; + + while (newidx != readidx) { + uint8_t ch = bus_space_read_1(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_console_buf_addr + readidx); + readidx++; + if (readidx == sc->sc_console_buf_size) + readidx = 0; + if (ch == '\r') + continue; + printf("%c", ch); + } + } +#endif + + bwfm_pci_intr_enable(sc); + return 1; +} + +void +bwfm_pci_intr_enable(struct bwfm_pci_softc *sc) +{ + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXMASK, + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_0 | + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_1 | + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H_DB); +} + +void +bwfm_pci_intr_disable(struct bwfm_pci_softc *sc) +{ + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXMASK, 0); +} + +/* Msgbuf protocol implementation */ +int +bwfm_pci_msgbuf_query_dcmd(struct bwfm_softc *bwfm, int ifidx, + int cmd, char *buf, size_t *len) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + struct msgbuf_ioctl_req_hdr *req; + struct mbuf *m; + size_t buflen; + int s; + + s = splnet(); + sc->sc_ioctl_resp_pktid = -1; + req = bwfm_pci_ring_write_reserve(sc, &sc->sc_ctrl_submit); + if (req == NULL) { + printf("%s: cannot reserve for write\n", DEVNAME(sc)); + splx(s); + return 1; + } + req->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ; + req->msg.ifidx = 0; + req->msg.flags = 0; + req->msg.request_id = htole32(MSGBUF_IOCTL_REQ_PKTID); + req->cmd = htole32(cmd); + req->output_buf_len = htole16(*len); + req->trans_id = htole16(sc->sc_ioctl_reqid++); + + buflen = min(*len, BWFM_DMA_H2D_IOCTL_BUF_LEN); + req->input_buf_len = htole16(buflen); + req->req_buf_addr.high_addr = + htole32((uint64_t)BWFM_PCI_DMA_DVA(sc->sc_ioctl_buf) >> 32); + req->req_buf_addr.low_addr = + htole32((uint64_t)BWFM_PCI_DMA_DVA(sc->sc_ioctl_buf) & 0xffffffff); + if (buf) + memcpy(BWFM_PCI_DMA_KVA(sc->sc_ioctl_buf), buf, buflen); + else + memset(BWFM_PCI_DMA_KVA(sc->sc_ioctl_buf), 0, buflen); + + bwfm_pci_ring_write_commit(sc, &sc->sc_ctrl_submit); + splx(s); + + if (sc->sc_ioctl_poll) { + int i; + for (i = 0; i < 100; i++) { + if (sc->sc_ioctl_resp_pktid != -1) + break; + delay(10 * 1000); + } + if (i == 100) { + printf("%s: timeout waiting for ioctl response\n", + DEVNAME(sc)); + return 1; + } + } else if (tsleep(&sc->sc_ioctl_buf, PCATCH, "bwfm", hz)) { + printf("%s: timeout waiting for ioctl response\n", + DEVNAME(sc)); + return 1; + } + + m = bwfm_pci_pktid_free(sc, &sc->sc_rx_pkts, sc->sc_ioctl_resp_pktid); + if (m == NULL) + return 1; + + *len = min(buflen, sc->sc_ioctl_resp_ret_len); + if (buf) + memcpy(buf, mtod(m, char *), *len); + m_freem(m); + splx(s); + + return 0; +} + +int +bwfm_pci_msgbuf_set_dcmd(struct bwfm_softc *bwfm, int ifidx, + int cmd, char *buf, size_t len) +{ + return bwfm_pci_msgbuf_query_dcmd(bwfm, ifidx, cmd, buf, &len); +} |