Path: blob/main/sys/contrib/dev/broadcom/brcm80211/brcmfmac/common.c
178665 views
// SPDX-License-Identifier: ISC1/*2* Copyright (c) 2010 Broadcom Corporation3*/45#if defined(__FreeBSD__)6#define LINUXKPI_PARAM_PREFIX brcmfmac_7#endif89#include <linux/kernel.h>10#include <linux/string.h>11#include <linux/netdevice.h>12#include <linux/module.h>13#include <linux/firmware.h>14#include <brcmu_wifi.h>15#include <brcmu_utils.h>16#include "core.h"17#include "bus.h"18#include "debug.h"19#include "fwil.h"20#include "fwil_types.h"21#include "tracepoint.h"22#include "common.h"23#include "of.h"24#include "firmware.h"25#include "chip.h"2627MODULE_AUTHOR("Broadcom Corporation");28MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");29MODULE_LICENSE("Dual BSD/GPL");30#if defined(__FreeBSD__)31MODULE_DEPEND(brcmfmac, brcmutil, 1, 1, 1);32MODULE_DEPEND(brcmfmac, linuxkpi, 1, 1, 1);33MODULE_DEPEND(brcmfmac, linuxkpi_wlan, 1, 1, 1);34MODULE_DEPEND(brcmfmac, lindebugfs, 1, 1, 1); /* XXX-BZ someone should fix this */35#endif3637#define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 4038#define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 403940/* default boost value for RSSI_DELTA in preferred join selection */41#define BRCMF_JOIN_PREF_RSSI_BOOST 84243#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */4445static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE;46module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0);47MODULE_PARM_DESC(txglomsz, "Maximum tx packet chain size [SDIO]");4849/* Debug level configuration. See debug.h for bits, sysfs modifiable */50int brcmf_msg_level;51module_param_named(debug, brcmf_msg_level, int, 0600);52MODULE_PARM_DESC(debug, "Level of debug output");5354static int brcmf_p2p_enable;55module_param_named(p2pon, brcmf_p2p_enable, int, 0);56MODULE_PARM_DESC(p2pon, "Enable legacy p2p management functionality");5758static int brcmf_feature_disable;59module_param_named(feature_disable, brcmf_feature_disable, int, 0);60MODULE_PARM_DESC(feature_disable, "Disable features");6162static char brcmf_firmware_path[BRCMF_FW_ALTPATH_LEN];63module_param_string(alternative_fw_path, brcmf_firmware_path,64BRCMF_FW_ALTPATH_LEN, 0400);65MODULE_PARM_DESC(alternative_fw_path, "Alternative firmware path");6667static int brcmf_fcmode;68module_param_named(fcmode, brcmf_fcmode, int, 0);69MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control");7071static int brcmf_roamoff;72module_param_named(roamoff, brcmf_roamoff, int, 0400);73MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine");7475static int brcmf_iapp_enable;76module_param_named(iapp, brcmf_iapp_enable, int, 0);77MODULE_PARM_DESC(iapp, "Enable partial support for the obsoleted Inter-Access Point Protocol");7879#ifdef DEBUG80/* always succeed brcmf_bus_started() */81static int brcmf_ignore_probe_fail;82module_param_named(ignore_probe_fail, brcmf_ignore_probe_fail, int, 0);83MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging");84#endif8586static struct brcmfmac_platform_data *brcmfmac_pdata;87struct brcmf_mp_global_t brcmf_mp_global;8889void brcmf_c_set_joinpref_default(struct brcmf_if *ifp)90{91struct brcmf_pub *drvr = ifp->drvr;92struct brcmf_join_pref_params join_pref_params[2];93int err;9495/* Setup join_pref to select target by RSSI (boost on 5GHz) */96join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA;97join_pref_params[0].len = 2;98join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST;99join_pref_params[0].band = WLC_BAND_5G;100101join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI;102join_pref_params[1].len = 2;103join_pref_params[1].rssi_gain = 0;104join_pref_params[1].band = 0;105err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,106sizeof(join_pref_params));107if (err)108bphy_err(drvr, "Set join_pref error (%d)\n", err);109}110111static int brcmf_c_download(struct brcmf_if *ifp, u16 flag,112struct brcmf_dload_data_le *dload_buf,113u32 len, const char *var)114{115s32 err;116117flag |= (DLOAD_HANDLER_VER << DLOAD_FLAG_VER_SHIFT);118dload_buf->flag = cpu_to_le16(flag);119dload_buf->dload_type = cpu_to_le16(DL_TYPE_CLM);120dload_buf->len = cpu_to_le32(len);121dload_buf->crc = cpu_to_le32(0);122123err = brcmf_fil_iovar_data_set(ifp, var, dload_buf,124struct_size(dload_buf, data, len));125126return err;127}128129static int brcmf_c_download_blob(struct brcmf_if *ifp,130#if defined(__linux__)131const void *data, size_t size,132#elif defined(__FreeBSD__)133const u8 *data, size_t size,134#endif135const char *loadvar, const char *statvar)136{137struct brcmf_pub *drvr = ifp->drvr;138struct brcmf_dload_data_le *chunk_buf;139u32 chunk_len;140u32 datalen;141u32 cumulative_len;142u16 dl_flag = DL_BEGIN;143u32 status;144s32 err;145146brcmf_dbg(TRACE, "Enter\n");147148chunk_buf = kzalloc(struct_size(chunk_buf, data, MAX_CHUNK_LEN),149GFP_KERNEL);150if (!chunk_buf) {151err = -ENOMEM;152return -ENOMEM;153}154155datalen = size;156cumulative_len = 0;157do {158if (datalen > MAX_CHUNK_LEN) {159chunk_len = MAX_CHUNK_LEN;160} else {161chunk_len = datalen;162dl_flag |= DL_END;163}164memcpy(chunk_buf->data, data + cumulative_len, chunk_len);165166err = brcmf_c_download(ifp, dl_flag, chunk_buf, chunk_len,167loadvar);168169dl_flag &= ~DL_BEGIN;170171cumulative_len += chunk_len;172datalen -= chunk_len;173} while ((datalen > 0) && (err == 0));174175if (err) {176bphy_err(drvr, "%s (%zu byte file) failed (%d)\n",177loadvar, size, err);178/* Retrieve status and print */179err = brcmf_fil_iovar_int_get(ifp, statvar, &status);180if (err)181bphy_err(drvr, "get %s failed (%d)\n", statvar, err);182else183brcmf_dbg(INFO, "%s=%d\n", statvar, status);184err = -EIO;185}186187kfree(chunk_buf);188return err;189}190191static int brcmf_c_process_clm_blob(struct brcmf_if *ifp)192{193struct brcmf_pub *drvr = ifp->drvr;194struct brcmf_bus *bus = drvr->bus_if;195const struct firmware *fw = NULL;196s32 err;197198brcmf_dbg(TRACE, "Enter\n");199200err = brcmf_bus_get_blob(bus, &fw, BRCMF_BLOB_CLM);201if (err || !fw) {202brcmf_info("no clm_blob available (err=%d), device may have limited channels available\n",203err);204return 0;205}206207err = brcmf_c_download_blob(ifp, fw->data, fw->size,208"clmload", "clmload_status");209210release_firmware(fw);211return err;212}213214static int brcmf_c_process_txcap_blob(struct brcmf_if *ifp)215{216struct brcmf_pub *drvr = ifp->drvr;217struct brcmf_bus *bus = drvr->bus_if;218const struct firmware *fw = NULL;219s32 err;220221brcmf_dbg(TRACE, "Enter\n");222223err = brcmf_bus_get_blob(bus, &fw, BRCMF_BLOB_TXCAP);224if (err || !fw) {225brcmf_info("no txcap_blob available (err=%d)\n", err);226return 0;227}228229brcmf_info("TxCap blob found, loading\n");230err = brcmf_c_download_blob(ifp, fw->data, fw->size,231"txcapload", "txcapload_status");232233release_firmware(fw);234return err;235}236237int brcmf_c_set_cur_etheraddr(struct brcmf_if *ifp, const u8 *addr)238{239s32 err;240241err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", addr, ETH_ALEN);242if (err < 0)243bphy_err(ifp->drvr, "Setting cur_etheraddr failed, %d\n", err);244245return err;246}247248/* On some boards there is no eeprom to hold the nvram, in this case instead249* a board specific nvram is loaded from /lib/firmware. On most boards the250* macaddr setting in the /lib/firmware nvram file is ignored because the251* wifibt chip has a unique MAC programmed into the chip itself.252* But in some cases the actual MAC from the /lib/firmware nvram file gets253* used, leading to MAC conflicts.254* The MAC addresses in the troublesome nvram files seem to all come from255* the same nvram file template, so we only need to check for 1 known256* address to detect this.257*/258static const u8 brcmf_default_mac_address[ETH_ALEN] = {2590x00, 0x90, 0x4c, 0xc5, 0x12, 0x38260};261262static int brcmf_c_process_cal_blob(struct brcmf_if *ifp)263{264struct brcmf_pub *drvr = ifp->drvr;265struct brcmf_mp_device *settings = drvr->settings;266s32 err;267268brcmf_dbg(TRACE, "Enter\n");269270if (!settings->cal_blob || !settings->cal_size)271return 0;272273brcmf_info("Calibration blob provided by platform, loading\n");274err = brcmf_c_download_blob(ifp, settings->cal_blob, settings->cal_size,275"calload", "calload_status");276return err;277}278279int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)280{281struct brcmf_pub *drvr = ifp->drvr;282struct brcmf_fweh_info *fweh = drvr->fweh;283u8 buf[BRCMF_DCMD_SMLEN];284struct brcmf_bus *bus;285struct brcmf_rev_info_le revinfo;286struct brcmf_rev_info *ri;287char *clmver;288char *ptr;289s32 err;290291if (is_valid_ether_addr(ifp->mac_addr)) {292/* set mac address */293err = brcmf_c_set_cur_etheraddr(ifp, ifp->mac_addr);294if (err < 0)295goto done;296} else {297/* retrieve mac address */298err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,299sizeof(ifp->mac_addr));300if (err < 0) {301bphy_err(drvr, "Retrieving cur_etheraddr failed, %d\n", err);302goto done;303}304305if (ether_addr_equal_unaligned(ifp->mac_addr, brcmf_default_mac_address)) {306bphy_err(drvr, "Default MAC is used, replacing with random MAC to avoid conflicts\n");307eth_random_addr(ifp->mac_addr);308ifp->ndev->addr_assign_type = NET_ADDR_RANDOM;309err = brcmf_c_set_cur_etheraddr(ifp, ifp->mac_addr);310if (err < 0)311goto done;312}313}314315memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac));316memcpy(ifp->drvr->wiphy->perm_addr, ifp->drvr->mac, ETH_ALEN);317318bus = ifp->drvr->bus_if;319ri = &ifp->drvr->revinfo;320321err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO,322&revinfo, sizeof(revinfo));323if (err < 0) {324bphy_err(drvr, "retrieving revision info failed, %d\n", err);325strscpy(ri->chipname, "UNKNOWN", sizeof(ri->chipname));326} else {327ri->vendorid = le32_to_cpu(revinfo.vendorid);328ri->deviceid = le32_to_cpu(revinfo.deviceid);329ri->radiorev = le32_to_cpu(revinfo.radiorev);330ri->corerev = le32_to_cpu(revinfo.corerev);331ri->boardid = le32_to_cpu(revinfo.boardid);332ri->boardvendor = le32_to_cpu(revinfo.boardvendor);333ri->boardrev = le32_to_cpu(revinfo.boardrev);334ri->driverrev = le32_to_cpu(revinfo.driverrev);335ri->ucoderev = le32_to_cpu(revinfo.ucoderev);336ri->bus = le32_to_cpu(revinfo.bus);337ri->phytype = le32_to_cpu(revinfo.phytype);338ri->phyrev = le32_to_cpu(revinfo.phyrev);339ri->anarev = le32_to_cpu(revinfo.anarev);340ri->chippkg = le32_to_cpu(revinfo.chippkg);341ri->nvramrev = le32_to_cpu(revinfo.nvramrev);342343/* use revinfo if not known yet */344if (!bus->chip) {345bus->chip = le32_to_cpu(revinfo.chipnum);346bus->chiprev = le32_to_cpu(revinfo.chiprev);347}348}349ri->result = err;350351if (bus->chip)352brcmf_chip_name(bus->chip, bus->chiprev,353ri->chipname, sizeof(ri->chipname));354355/* Do any CLM downloading */356err = brcmf_c_process_clm_blob(ifp);357if (err < 0) {358bphy_err(drvr, "download CLM blob file failed, %d\n", err);359goto done;360}361362/* Do TxCap downloading, if needed */363err = brcmf_c_process_txcap_blob(ifp);364if (err < 0) {365bphy_err(drvr, "download TxCap blob file failed, %d\n", err);366goto done;367}368369/* Download external calibration blob, if available */370err = brcmf_c_process_cal_blob(ifp);371if (err < 0) {372bphy_err(drvr, "download calibration blob file failed, %d\n", err);373goto done;374}375376/* query for 'ver' to get version info from firmware */377memset(buf, 0, sizeof(buf));378err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf));379if (err < 0) {380bphy_err(drvr, "Retrieving version information failed, %d\n",381err);382goto done;383}384buf[sizeof(buf) - 1] = '\0';385ptr = (char *)buf;386strsep(&ptr, "\n");387388/* Print fw version info */389brcmf_info("Firmware: %s %s\n", ri->chipname, buf);390391/* locate firmware version number for ethtool */392ptr = strrchr(buf, ' ');393if (!ptr) {394bphy_err(drvr, "Retrieving version number failed");395goto done;396}397strscpy(ifp->drvr->fwver, ptr + 1, sizeof(ifp->drvr->fwver));398399/* Query for 'clmver' to get CLM version info from firmware */400memset(buf, 0, sizeof(buf));401err = brcmf_fil_iovar_data_get(ifp, "clmver", buf, sizeof(buf));402if (err) {403brcmf_dbg(TRACE, "retrieving clmver failed, %d\n", err);404} else {405buf[sizeof(buf) - 1] = '\0';406clmver = (char *)buf;407408/* Replace all newline/linefeed characters with space409* character410*/411strreplace(clmver, '\n', ' ');412413/* store CLM version for adding it to revinfo debugfs file */414memcpy(ifp->drvr->clmver, clmver, sizeof(ifp->drvr->clmver));415416brcmf_dbg(INFO, "CLM version = %s\n", clmver);417}418419/* set mpc */420err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);421if (err) {422bphy_err(drvr, "failed setting mpc\n");423goto done;424}425426brcmf_c_set_joinpref_default(ifp);427428/* Setup event_msgs, enable E_IF */429err = brcmf_fil_iovar_data_get(ifp, "event_msgs", fweh->event_mask,430fweh->event_mask_len);431if (err) {432bphy_err(drvr, "Get event_msgs error (%d)\n", err);433goto done;434}435/*436* BRCMF_E_IF can safely be used to set the appropriate bit437* in the event_mask as the firmware event code is guaranteed438* to match the value of BRCMF_E_IF because it is old cruft439* that all vendors have.440*/441setbit(fweh->event_mask, BRCMF_E_IF);442err = brcmf_fil_iovar_data_set(ifp, "event_msgs", fweh->event_mask,443fweh->event_mask_len);444if (err) {445bphy_err(drvr, "Set event_msgs error (%d)\n", err);446goto done;447}448449/* Setup default scan channel time */450err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,451BRCMF_DEFAULT_SCAN_CHANNEL_TIME);452if (err) {453bphy_err(drvr, "BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n",454err);455goto done;456}457458/* Setup default scan unassoc time */459err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,460BRCMF_DEFAULT_SCAN_UNASSOC_TIME);461if (err) {462bphy_err(drvr, "BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n",463err);464goto done;465}466467/* Enable tx beamforming, errors can be ignored (not supported) */468(void)brcmf_fil_iovar_int_set(ifp, "txbf", 1);469done:470return err;471}472473#ifndef CONFIG_BRCM_TRACING474void __brcmf_err(struct brcmf_bus *bus, const char *func, const char *fmt, ...)475{476struct va_format vaf;477va_list args;478479va_start(args, fmt);480481vaf.fmt = fmt;482vaf.va = &args;483#if defined(__linux__)484if (bus)485dev_err(bus->dev, "%s: %pV", func, &vaf);486else487pr_err("%s: %pV", func, &vaf);488#elif defined(__FreeBSD__)489{490char *str;491vasprintf(&str, M_KMALLOC, vaf.fmt, args);492if (bus)493dev_err(bus->dev, "ERROR: %s: %s", func, str);494else495pr_err("ERROR: %s: %s", func, str);496free(str, M_KMALLOC);497}498#endif499500va_end(args);501}502#endif503504#if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG)505void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...)506{507struct va_format vaf = {508.fmt = fmt,509};510va_list args;511512va_start(args, fmt);513vaf.va = &args;514if (brcmf_msg_level & level)515#if defined(__linux__)516pr_debug("%s %pV", func, &vaf);517#elif defined(__FreeBSD__)518{519char *str;520vasprintf(&str, M_KMALLOC, vaf.fmt, args);521pr_debug("%s %s", func, str);522free(str, M_KMALLOC);523}524#endif525trace_brcmf_dbg(level, func, &vaf);526va_end(args);527}528BRCMF_EXPORT_SYMBOL_GPL(__brcmf_dbg);529#endif530531static void brcmf_mp_attach(void)532{533/* If module param firmware path is set then this will always be used,534* if not set then if available use the platform data version. To make535* sure it gets initialized at all, always copy the module param version536*/537strscpy(brcmf_mp_global.firmware_path, brcmf_firmware_path,538BRCMF_FW_ALTPATH_LEN);539if ((brcmfmac_pdata) && (brcmfmac_pdata->fw_alternative_path) &&540(brcmf_mp_global.firmware_path[0] == '\0')) {541strscpy(brcmf_mp_global.firmware_path,542brcmfmac_pdata->fw_alternative_path,543BRCMF_FW_ALTPATH_LEN);544}545}546547struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,548enum brcmf_bus_type bus_type,549u32 chip, u32 chiprev)550{551struct brcmf_mp_device *settings;552struct brcmfmac_pd_device *device_pd;553bool found;554int i;555556brcmf_dbg(INFO, "Enter, bus=%d, chip=%d, rev=%d\n", bus_type, chip,557chiprev);558settings = kzalloc(sizeof(*settings), GFP_ATOMIC);559if (!settings)560return NULL;561562/* start by using the module parameters */563settings->p2p_enable = !!brcmf_p2p_enable;564settings->feature_disable = brcmf_feature_disable;565settings->fcmode = brcmf_fcmode;566settings->roamoff = !!brcmf_roamoff;567settings->iapp = !!brcmf_iapp_enable;568#ifdef DEBUG569settings->ignore_probe_fail = !!brcmf_ignore_probe_fail;570#endif571572if (bus_type == BRCMF_BUSTYPE_SDIO)573settings->bus.sdio.txglomsz = brcmf_sdiod_txglomsz;574575/* See if there is any device specific platform data configured */576found = false;577if (brcmfmac_pdata) {578for (i = 0; i < brcmfmac_pdata->device_count; i++) {579device_pd = &brcmfmac_pdata->devices[i];580if ((device_pd->bus_type == bus_type) &&581(device_pd->id == chip) &&582((device_pd->rev == chiprev) ||583(device_pd->rev == -1))) {584brcmf_dbg(INFO, "Platform data for device found\n");585settings->country_codes =586device_pd->country_codes;587if (device_pd->bus_type == BRCMF_BUSTYPE_SDIO)588memcpy(&settings->bus.sdio,589&device_pd->bus.sdio,590sizeof(settings->bus.sdio));591found = true;592break;593}594}595}596if (!found) {597/* No platform data for this device, try OF and DMI data */598brcmf_dmi_probe(settings, chip, chiprev);599if (brcmf_of_probe(dev, bus_type, settings) == -EPROBE_DEFER) {600kfree(settings);601return ERR_PTR(-EPROBE_DEFER);602}603brcmf_acpi_probe(dev, bus_type, settings);604}605return settings;606}607608void brcmf_release_module_param(struct brcmf_mp_device *module_param)609{610kfree(module_param);611}612613static int __init brcmf_common_pd_probe(struct platform_device *pdev)614{615brcmf_dbg(INFO, "Enter\n");616617brcmfmac_pdata = dev_get_platdata(&pdev->dev);618619if (brcmfmac_pdata->power_on)620brcmfmac_pdata->power_on();621622return 0;623}624625static void brcmf_common_pd_remove(struct platform_device *pdev)626{627brcmf_dbg(INFO, "Enter\n");628629if (brcmfmac_pdata->power_off)630brcmfmac_pdata->power_off();631}632633static struct platform_driver brcmf_pd = {634.remove = brcmf_common_pd_remove,635.driver = {636.name = BRCMFMAC_PDATA_NAME,637}638};639640static int __init brcmfmac_module_init(void)641{642int err;643644/* Get the platform data (if available) for our devices */645err = platform_driver_probe(&brcmf_pd, brcmf_common_pd_probe);646if (err == -ENODEV)647brcmf_dbg(INFO, "No platform data available.\n");648649/* Initialize global module parameters */650brcmf_mp_attach();651652/* Continue the initialization by registering the different busses */653err = brcmf_core_init();654if (err) {655if (brcmfmac_pdata)656platform_driver_unregister(&brcmf_pd);657}658659return err;660}661662static void __exit brcmfmac_module_exit(void)663{664brcmf_core_exit();665if (brcmfmac_pdata)666platform_driver_unregister(&brcmf_pd);667}668669module_init(brcmfmac_module_init);670module_exit(brcmfmac_module_exit);671672673674