blob: a119142978f4733d2d67c76953b48fd37098eff7 [file] [log] [blame]
/**
* Copyright (c) 2008 - 2013 Quantenna Communications Inc
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/device.h>
#include <linux/version.h>
#include <asm/io.h>
#include <asm/board/soc.h>
#include "qdrv_mac.h"
#include "qdrv_soc.h"
#include "qdrv_debug.h"
#include "qdrv_auc.h"
#include "qdrv_hal.h"
#include "qdrv_wlan.h"
#include "qdrv_vap.h"
#include "qdrv_fw.h"
#include <qtn/topaz_tqe.h>
static qtn_shared_node_stats_t *s_per_node_stats_ptr = NULL;
static qtn_shared_vap_stats_t *s_per_vap_stats_ptr = NULL;
static size_t auc_get_sram_size(void)
{
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
if (sp->fw_no_mu)
return CONFIG_ARC_AUC_NOMU_SRAM_SIZE;
else
return CONFIG_ARC_AUC_MU_SRAM_SIZE;
}
static void auc_clear_addr_range(unsigned long physaddr, unsigned long size)
{
void *vaddr = ioremap_nocache(physaddr, size);
if (!vaddr) {
DBGPRINTF_E("0x%lx, 0x%lx cannot be mapped\n", physaddr, size);
} else {
qdrv_fw_auc_memzero(vaddr, size, physaddr);
iounmap(vaddr);
}
}
static void auc_clear_mem(void)
{
auc_clear_addr_range(TOPAZ_AUC_IMEM_ADDR, TOPAZ_AUC_IMEM_SIZE);
auc_clear_addr_range(TOPAZ_AUC_DMEM_ADDR, TOPAZ_AUC_DMEM_SIZE);
auc_clear_addr_range(RUBY_DRAM_BEGIN + CONFIG_ARC_AUC_BASE, CONFIG_ARC_AUC_SIZE);
auc_clear_addr_range(RUBY_SRAM_BEGIN + CONFIG_ARC_AUC_SRAM_BASE, auc_get_sram_size());
}
void qdrv_auc_stats_setup(void)
{
unsigned long phyaddr;
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
if (unlikely(!sp || !sp->auc.node_stats || !sp->auc.vap_stats)) {
DBGPRINTF(DBG_LL_ERR, QDRV_LF_TRACE, "Stats setup: failed\n");
return;
}
if (!s_per_node_stats_ptr) {
phyaddr = qdrv_fw_auc_to_host_addr((unsigned long)sp->auc.node_stats);
s_per_node_stats_ptr = ioremap_nocache(phyaddr, QTN_NCIDX_MAX * sizeof(qtn_shared_node_stats_t));
}
if (!s_per_vap_stats_ptr) {
phyaddr = qdrv_fw_auc_to_host_addr((unsigned long)sp->auc.vap_stats);
s_per_vap_stats_ptr = ioremap_nocache(phyaddr, QTN_MAX_VAPS * sizeof(qtn_shared_vap_stats_t));
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_TRACE, "Stats setup: Node : %p - %p\n"
" Vap : %p - %p\n",
sp->auc.node_stats,
s_per_node_stats_ptr,
sp->auc.vap_stats,
s_per_vap_stats_ptr);
}
void qdrv_auc_stats_unmap(void)
{
if (s_per_node_stats_ptr)
iounmap(s_per_node_stats_ptr);
if (s_per_vap_stats_ptr)
iounmap(s_per_vap_stats_ptr);
}
qtn_shared_node_stats_t* qdrv_auc_get_node_stats(uint8_t node)
{
return (s_per_node_stats_ptr) ? (s_per_node_stats_ptr + node) : NULL;
}
qtn_shared_vap_stats_t* qdrv_auc_get_vap_stats(uint8_t vapid)
{
return (s_per_vap_stats_ptr) ? (s_per_vap_stats_ptr + vapid) : NULL;
}
void qdrv_auc_update_multicast_stats(void *ctx, uint8_t nid)
{
uint8_t vapid;
struct ieee80211com *ic = (struct ieee80211com *)ctx;
struct ieee80211_node *node;
struct ieee80211vap *vap;
struct qdrv_vap * qv;
qtn_shared_node_stats_t *nstats;
qtn_shared_vap_stats_t *vstats;
if (!ctx)
return;
node = ic->ic_node_idx_ni[nid];
if (unlikely(!node))
return;
vap = node->ni_vap;
qv = container_of(vap, struct qdrv_vap, iv);
vapid = QDRV_WLANID_FROM_DEVID(qv->devid);
nstats = qdrv_auc_get_node_stats(nid);
vstats = qdrv_auc_get_vap_stats(vapid);
if (unlikely(!nstats || !vstats))
return;
nstats->qtn_tx_mcast++;
vstats->qtn_tx_mcast++;
}
void qdrv_auc_print_memory_map(void)
{
int bank = 0;
u_int32_t auc_sram_start, auc_sram_end, auc_sram_size, auc_sram_bank_end;
struct shared_params *sp = qtn_mproc_sync_shared_params_get();
auc_sram_size = auc_get_sram_size();
auc_sram_start = RUBY_SRAM_BEGIN + CONFIG_ARC_AUC_SRAM_BASE;
auc_sram_end = auc_sram_start + auc_sram_size;
printk("AuC SRAM start 0x%08x end 0x%08x size %d\n",
auc_sram_start, auc_sram_end, auc_sram_size);
if (sp->fw_no_mu)
printk("AuC is configured for non-MU SRAM layout\n");
else
printk("AuC is configured for MU-enabled SRAM layout\n");
auc_sram_bank_end = auc_sram_start + RUBY_SRAM_BANK_SIZE;
while (auc_sram_start < auc_sram_end) {
printk("AuC SRAM bank %d start 0x%08x end 0x%08x\n",
bank++, auc_sram_start, auc_sram_bank_end);
auc_sram_start = auc_sram_bank_end;
auc_sram_bank_end += RUBY_SRAM_BANK_SIZE;
}
}
int qdrv_auc_init(struct qdrv_cb *qcb)
{
u32 auc_start_addr = 0;
struct qdrv_wlan *qw = qcb->macs[0].data;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter");
qtn_mproc_sync_shared_params_get()->auc.auc_config = global_auc_config;
qdrv_auc_print_memory_map();
auc_clear_mem();
if (qdrv_fw_load_auc(qcb->dev, qcb->auc_firmware, &auc_start_addr) < 0) {
DBGPRINTF_E("AuC load firmware failed\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
DBGPRINTF(DBG_LL_INFO, QDRV_LF_DSP, "Firmware start address is %x\n", auc_start_addr);
hal_enable_auc();
tqe_reg_multicast_tx_stats(qdrv_auc_update_multicast_stats, &qw->ic);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}
int qdrv_auc_exit(struct qdrv_cb *qcb)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter");
qdrv_auc_stats_unmap();
hal_disable_auc();
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}