/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2013 Adrian Chadd <[email protected]>4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer,11* without modification.12* 2. Redistributions in binary form must reproduce at minimum a disclaimer13* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any14* redistribution must be conditioned upon including a substantially15* similar Disclaimer requirement for further binary redistribution.16*17* NO WARRANTY18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS19* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT20* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY21* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL22* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,23* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF24* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS25* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER26* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)27* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF28* THE POSSIBILITY OF SUCH DAMAGES.29*/30#include <sys/cdefs.h>31/*32* This implements some very basic bluetooth coexistence methods for33* the ath(4) hardware.34*/35#include "opt_ath.h"36#include "opt_inet.h"37#include "opt_wlan.h"3839#include <sys/param.h>40#include <sys/systm.h>41#include <sys/sysctl.h>42#include <sys/kernel.h>43#include <sys/lock.h>44#include <sys/malloc.h>45#include <sys/mutex.h>46#include <sys/errno.h>47#include <machine/bus.h>48#include <machine/resource.h>4950#include <sys/bus.h>5152#include <sys/socket.h>5354#include <net/if.h>55#include <net/if_var.h>56#include <net/if_media.h>57#include <net/if_arp.h>58#include <net/ethernet.h> /* XXX for ether_sprintf */5960#include <net80211/ieee80211_var.h>6162#include <net/bpf.h>6364#ifdef INET65#include <netinet/in.h>66#include <netinet/if_ether.h>67#endif6869#include <dev/ath/if_athvar.h>70#include <dev/ath/if_ath_btcoex.h>71#include <dev/ath/if_ath_btcoex_mci.h>7273MALLOC_DECLARE(M_ATHDEV);7475/*76* Initial AR9285 / (WB195) bluetooth coexistence settings,77* just for experimentation.78*79* Return 0 for OK; errno for error.80*81* XXX TODO: There needs to be a PCIe workaround to disable ASPM if82* bluetooth coexistence is enabled.83*/84static int85ath_btcoex_cfg_wb195(struct ath_softc *sc)86{87HAL_BT_COEX_INFO btinfo;88HAL_BT_COEX_CONFIG btconfig;89struct ath_hal *ah = sc->sc_ah;9091if (! ath_hal_btcoex_supported(ah))92return (EINVAL);9394bzero(&btinfo, sizeof(btinfo));95bzero(&btconfig, sizeof(btconfig));9697device_printf(sc->sc_dev, "Enabling WB195 BTCOEX\n");9899btinfo.bt_module = HAL_BT_MODULE_JANUS;100btinfo.bt_coex_config = HAL_BT_COEX_CFG_3WIRE;101/*102* These are the three GPIO pins hooked up between the AR9285 and103* the AR3011.104*/105btinfo.bt_gpio_bt_active = 6;106btinfo.bt_gpio_bt_priority = 7;107btinfo.bt_gpio_wlan_active = 5;108btinfo.bt_active_polarity = 1; /* XXX not used */109btinfo.bt_single_ant = 1; /* 1 antenna on ar9285 ? */110btinfo.bt_isolation = 0; /* in dB, not used */111112ath_hal_btcoex_set_info(ah, &btinfo);113114btconfig.bt_time_extend = 0;115btconfig.bt_txstate_extend = 1; /* true */116btconfig.bt_txframe_extend = 1; /* true */117btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;118btconfig.bt_quiet_collision = 1; /* true */119btconfig.bt_rxclear_polarity = 1; /* true */120btconfig.bt_priority_time = 2;121btconfig.bt_first_slot_time = 5;122btconfig.bt_hold_rxclear = 1; /* true */123124ath_hal_btcoex_set_config(ah, &btconfig);125126/*127* Enable antenna diversity.128*/129ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1);130131return (0);132}133134/*135* Initial AR9485 / (WB225) bluetooth coexistence settings,136* just for experimentation.137*138* Return 0 for OK; errno for error.139*/140static int141ath_btcoex_cfg_wb225(struct ath_softc *sc)142{143HAL_BT_COEX_INFO btinfo;144HAL_BT_COEX_CONFIG btconfig;145struct ath_hal *ah = sc->sc_ah;146147if (! ath_hal_btcoex_supported(ah))148return (EINVAL);149150bzero(&btinfo, sizeof(btinfo));151bzero(&btconfig, sizeof(btconfig));152153device_printf(sc->sc_dev, "Enabling WB225 BTCOEX\n");154155btinfo.bt_module = HAL_BT_MODULE_JANUS; /* XXX not used? */156btinfo.bt_coex_config = HAL_BT_COEX_CFG_3WIRE;157/*158* These are the three GPIO pins hooked up between the AR9485 and159* the bluetooth module.160*/161btinfo.bt_gpio_bt_active = 4;162btinfo.bt_gpio_bt_priority = 8;163btinfo.bt_gpio_wlan_active = 5;164165btinfo.bt_active_polarity = 1; /* XXX not used */166btinfo.bt_single_ant = 1; /* 1 antenna on ar9285 ? */167btinfo.bt_isolation = 0; /* in dB, not used */168169ath_hal_btcoex_set_info(ah, &btinfo);170171btconfig.bt_time_extend = 0;172btconfig.bt_txstate_extend = 1; /* true */173btconfig.bt_txframe_extend = 1; /* true */174btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;175btconfig.bt_quiet_collision = 1; /* true */176btconfig.bt_rxclear_polarity = 1; /* true */177btconfig.bt_priority_time = 2;178btconfig.bt_first_slot_time = 5;179btconfig.bt_hold_rxclear = 1; /* true */180181ath_hal_btcoex_set_config(ah, &btconfig);182183/*184* Enable antenna diversity.185*/186ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1);187188return (0);189}190191static int192ath_btcoex_cfg_mci(struct ath_softc *sc, uint32_t mci_cfg, int do_btdiv)193{194HAL_BT_COEX_INFO btinfo;195HAL_BT_COEX_CONFIG btconfig;196struct ath_hal *ah = sc->sc_ah;197198if (! ath_hal_btcoex_supported(ah))199return (EINVAL);200201bzero(&btinfo, sizeof(btinfo));202bzero(&btconfig, sizeof(btconfig));203204sc->sc_ah->ah_config.ath_hal_mci_config = mci_cfg;205206if (ath_btcoex_mci_attach(sc) != 0) {207device_printf(sc->sc_dev, "Failed to setup btcoex\n");208return (EINVAL);209}210211btinfo.bt_module = HAL_BT_MODULE_JANUS; /* XXX not used? */212btinfo.bt_coex_config = HAL_BT_COEX_CFG_MCI;213214/*215* MCI uses a completely different interface to speak216* to the bluetooth module - it's a command based217* thing over a serial line, rather than218* state pins to/from the bluetooth module.219*220* So, the GPIO configuration, polarity, etc221* doesn't matter on MCI devices; it's just222* completely ignored by the HAL.223*/224btinfo.bt_gpio_bt_active = 4;225btinfo.bt_gpio_bt_priority = 8;226btinfo.bt_gpio_wlan_active = 5;227228btinfo.bt_active_polarity = 1; /* XXX not used */229btinfo.bt_single_ant = 0; /* 2 antenna on WB335 */230btinfo.bt_isolation = 0; /* in dB, not used */231232ath_hal_btcoex_set_info(ah, &btinfo);233234btconfig.bt_time_extend = 0;235btconfig.bt_txstate_extend = 1; /* true */236btconfig.bt_txframe_extend = 1; /* true */237btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;238btconfig.bt_quiet_collision = 1; /* true */239btconfig.bt_rxclear_polarity = 1; /* true */240btconfig.bt_priority_time = 2;241btconfig.bt_first_slot_time = 5;242btconfig.bt_hold_rxclear = 1; /* true */243244ath_hal_btcoex_set_config(ah, &btconfig);245246/* Enable */247ath_hal_btcoex_enable(sc->sc_ah);248249/* Stomp */250ath_hal_btcoex_set_weights(ah, HAL_BT_COEX_STOMP_NONE);251252/*253* Enable antenna diversity.254*/255ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY,256do_btdiv);257258return (0);259}260261/*262* Initial AR9462 / (WB222) bluetooth coexistence settings.263*264* Return 0 for OK; errno for error.265*/266static int267ath_btcoex_cfg_wb222(struct ath_softc *sc)268{269270device_printf(sc->sc_dev, "Enabling WB222 BTCOEX\n");271/* XXX from ath9k */272return (ath_btcoex_cfg_mci(sc, 0x2201, 1));273}274275/*276* Initial QCA9565 / (WB335B) bluetooth coexistence settings.277*278* Return 0 for OK; errno for error.279*/280static int281ath_btcoex_cfg_wb335b(struct ath_softc *sc)282{283uint32_t flags;284int do_btdiv = 0;285286/* ath9k default */287flags = 0xa4c1;288289/* 1-ant and 2-ant AR9565 */290/*291* XXX TODO: ensure these actually make it down to the292* HAL correctly!293*/294if (sc->sc_pci_devinfo & ATH_PCI_AR9565_1ANT) {295flags &= ~ATH_MCI_CONFIG_ANT_ARCH;296flags |= ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_SHARED <<297ATH_MCI_CONFIG_ANT_ARCH_S;298} else if (sc->sc_pci_devinfo & ATH_PCI_AR9565_2ANT) {299flags &= ~ATH_MCI_CONFIG_ANT_ARCH;300flags |= ATH_MCI_ANT_ARCH_2_ANT_PA_LNA_NON_SHARED <<301ATH_MCI_CONFIG_ANT_ARCH_S;302}303304if (sc->sc_pci_devinfo & ATH_PCI_BT_ANT_DIV) {305do_btdiv = 1;306}307308device_printf(sc->sc_dev, "Enabling WB335 BTCOEX\n");309/* XXX from ath9k */310return (ath_btcoex_cfg_mci(sc, flags, do_btdiv));311}312313#if 0314/*315* When using bluetooth coexistence, ASPM needs to be disabled316* otherwise the sleeping interferes with the bluetooth (USB)317* operation and the MAC sleep/wakeup hardware.318*319* The PCIe powersave routine also needs to not be called320* by the driver during suspend/resume, else things will get321* a little odd. Check Linux ath9k for more details.322*/323static int324ath_btcoex_aspm_wb195(struct ath_softc *sc)325{326327/* XXX TODO: clear device ASPM L0S and L1 */328/* XXX TODO: clear _parent_ ASPM L0S and L1 */329}330#endif331332/*333* Methods which are required334*/335336/*337* Attach btcoex to the given interface338*/339int340ath_btcoex_attach(struct ath_softc *sc)341{342int ret;343struct ath_hal *ah = sc->sc_ah;344const char *profname;345346/*347* No chipset bluetooth coexistence? Then do nothing.348*/349if (! ath_hal_btcoex_supported(ah))350return (0);351352/*353* Look at the hints to determine which bluetooth354* profile to configure.355*/356ret = resource_string_value(device_get_name(sc->sc_dev),357device_get_unit(sc->sc_dev),358"btcoex_profile",359&profname);360if (ret != 0) {361/* nothing to do */362return (0);363}364365if (strncmp(profname, "wb195", 5) == 0) {366ret = ath_btcoex_cfg_wb195(sc);367} else if (strncmp(profname, "wb222", 5) == 0) {368ret = ath_btcoex_cfg_wb222(sc);369} else if (strncmp(profname, "wb225", 5) == 0) {370ret = ath_btcoex_cfg_wb225(sc);371} else if (strncmp(profname, "wb335", 5) == 0) {372ret = ath_btcoex_cfg_wb335b(sc);373} else {374return (0);375}376377/*378* Propagate up failure from the actual attach phase.379*/380if (ret != 0)381return (ret);382383return (0);384}385386/*387* Detach btcoex from the given interface388*/389int390ath_btcoex_detach(struct ath_softc *sc)391{392if (sc->sc_btcoex_mci) {393ath_btcoex_mci_detach(sc);394}395396return (0);397}398399/*400* Configure or disable bluetooth coexistence on the given channel.401*402* For AR9285/AR9287/AR9485, we'll never see a 5GHz channel, so we just403* assume bluetooth coexistence is always on.404*405* For AR9462, we may see a 5GHz channel; bluetooth coexistence should406* not be enabled on those channels.407*/408int409ath_btcoex_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)410{411if (sc->sc_btcoex_mci) {412ath_btcoex_mci_enable(sc, chan);413}414415return (0);416}417418/*419* Handle ioctl requests from the diagnostic interface.420*421* The initial part of this code resembles ath_ioctl_diag();422* it's likely a good idea to reduce duplication between423* these two routines.424*/425int426ath_btcoex_ioctl(struct ath_softc *sc, struct ath_diag *ad)427{428unsigned int id = ad->ad_id & ATH_DIAG_ID;429void *indata = NULL;430void *outdata = NULL;431u_int32_t insize = ad->ad_in_size;432u_int32_t outsize = ad->ad_out_size;433int error = 0;434// int val;435436if (ad->ad_id & ATH_DIAG_IN) {437/*438* Copy in data.439*/440indata = malloc(insize, M_TEMP, M_NOWAIT);441if (indata == NULL) {442error = ENOMEM;443goto bad;444}445error = copyin(ad->ad_in_data, indata, insize);446if (error)447goto bad;448}449if (ad->ad_id & ATH_DIAG_DYN) {450/*451* Allocate a buffer for the results (otherwise the HAL452* returns a pointer to a buffer where we can read the453* results). Note that we depend on the HAL leaving this454* pointer for us to use below in reclaiming the buffer;455* may want to be more defensive.456*/457outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO);458if (outdata == NULL) {459error = ENOMEM;460goto bad;461}462}463switch (id) {464default:465error = EINVAL;466goto bad;467}468if (outsize < ad->ad_out_size)469ad->ad_out_size = outsize;470if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))471error = EFAULT;472bad:473if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)474free(indata, M_TEMP);475if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)476free(outdata, M_TEMP);477return (error);478}479480481