Files
android_kernel_samsung_sm86…/hal/wifi3.0/hal_srng.c
Balamurugan Mahalingam d0159640ea qcacmn: Separate hal for qca6290 and qca8074
Create separate individual hal_srng_table and hal register
offset in target specific source files. Create separate
functions for qca6290 and qca8074 for few hal rx tx
functions as the macro value differs between the chipsets.

Assign target specific hal tx, rx ops as part of hal_attach
and call respective hal tx, rx ops through callbacks.

Change-Id: Ibbf490c678c39fdd9d54191aad7aaec786db30ec
2018-07-21 00:03:20 -07:00

928 lines
28 KiB
C

/*
* Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted 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 The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "hal_hw_headers.h"
#include "hal_api.h"
#include "target_type.h"
#include "wcss_version.h"
#include "qdf_module.h"
#ifdef QCA_WIFI_QCA8074
void hal_qca6290_attach(struct hal_soc *hal);
#endif
#ifdef QCA_WIFI_QCA8074
void hal_qca8074_attach(struct hal_soc *hal);
#endif
/**
* hal_get_srng_ring_id() - get the ring id of a descriped ring
* @hal: hal_soc data structure
* @ring_type: type enum describing the ring
* @ring_num: which ring of the ring type
* @mac_id: which mac does the ring belong to (or 0 for non-lmac rings)
*
* Return: the ring id or -EINVAL if the ring does not exist.
*/
static int hal_get_srng_ring_id(struct hal_soc *hal, int ring_type,
int ring_num, int mac_id)
{
struct hal_hw_srng_config *ring_config =
HAL_SRNG_CONFIG(hal, ring_type);
int ring_id;
if (ring_num >= ring_config->max_rings) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: ring_num exceeded maximum no. of supported rings\n",
__func__);
/* TODO: This is a programming error. Assert if this happens */
return -EINVAL;
}
if (ring_config->lmac_ring) {
ring_id = ring_config->start_ring_id + ring_num +
(mac_id * HAL_MAX_RINGS_PER_LMAC);
} else {
ring_id = ring_config->start_ring_id + ring_num;
}
return ring_id;
}
static struct hal_srng *hal_get_srng(struct hal_soc *hal, int ring_id)
{
/* TODO: Should we allocate srng structures dynamically? */
return &(hal->srng_list[ring_id]);
}
#define HP_OFFSET_IN_REG_START 1
#define OFFSET_FROM_HP_TO_TP 4
static void hal_update_srng_hp_tp_address(void *hal_soc,
int shadow_config_index,
int ring_type,
int ring_num)
{
struct hal_srng *srng;
struct hal_soc *hal = (struct hal_soc *)hal_soc;
int ring_id;
ring_id = hal_get_srng_ring_id(hal_soc, ring_type, ring_num, 0);
if (ring_id < 0)
return;
srng = hal_get_srng(hal_soc, ring_id);
if (srng->ring_dir == HAL_SRNG_DST_RING)
srng->u.dst_ring.tp_addr = SHADOW_REGISTER(shadow_config_index)
+ hal->dev_base_addr;
else
srng->u.src_ring.hp_addr = SHADOW_REGISTER(shadow_config_index)
+ hal->dev_base_addr;
}
QDF_STATUS hal_set_one_shadow_config(void *hal_soc,
int ring_type,
int ring_num)
{
uint32_t target_register;
struct hal_soc *hal = (struct hal_soc *)hal_soc;
struct hal_hw_srng_config *srng_config = &hal->hw_srng_table[ring_type];
int shadow_config_index = hal->num_shadow_registers_configured;
if (shadow_config_index >= MAX_SHADOW_REGISTERS) {
QDF_ASSERT(0);
return QDF_STATUS_E_RESOURCES;
}
hal->num_shadow_registers_configured++;
target_register = srng_config->reg_start[HP_OFFSET_IN_REG_START];
target_register += (srng_config->reg_size[HP_OFFSET_IN_REG_START]
*ring_num);
/* if the ring is a dst ring, we need to shadow the tail pointer */
if (srng_config->ring_dir == HAL_SRNG_DST_RING)
target_register += OFFSET_FROM_HP_TO_TP;
hal->shadow_config[shadow_config_index].addr = target_register;
/* update hp/tp addr in the hal_soc structure*/
hal_update_srng_hp_tp_address(hal_soc, shadow_config_index, ring_type,
ring_num);
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO,
"%s: target_reg %x, shadow_index %x, ring_type %d, ring num %d\n",
__func__, target_register, shadow_config_index,
ring_type, ring_num);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS hal_construct_shadow_config(void *hal_soc)
{
int ring_type, ring_num;
struct hal_soc *hal = (struct hal_soc *)hal_soc;
for (ring_type = 0; ring_type < MAX_RING_TYPES; ring_type++) {
struct hal_hw_srng_config *srng_config =
&hal->hw_srng_table[ring_type];
if (ring_type == CE_SRC ||
ring_type == CE_DST ||
ring_type == CE_DST_STATUS)
continue;
if (srng_config->lmac_ring)
continue;
for (ring_num = 0; ring_num < srng_config->max_rings;
ring_num++)
hal_set_one_shadow_config(hal_soc, ring_type, ring_num);
}
return QDF_STATUS_SUCCESS;
}
void hal_get_shadow_config(void *hal_soc,
struct pld_shadow_reg_v2_cfg **shadow_config,
int *num_shadow_registers_configured)
{
struct hal_soc *hal = (struct hal_soc *)hal_soc;
*shadow_config = hal->shadow_config;
*num_shadow_registers_configured =
hal->num_shadow_registers_configured;
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s\n", __func__);
}
static void hal_validate_shadow_register(struct hal_soc *hal,
uint32_t *destination,
uint32_t *shadow_address)
{
unsigned int index;
uint32_t *shadow_0_offset = SHADOW_REGISTER(0) + hal->dev_base_addr;
int destination_ba_offset =
((char *)destination) - (char *)hal->dev_base_addr;
index = shadow_address - shadow_0_offset;
if (index >= MAX_SHADOW_REGISTERS) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: index %x out of bounds\n", __func__, index);
goto error;
} else if (hal->shadow_config[index].addr != destination_ba_offset) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: sanity check failure, expected %x, found %x\n",
__func__, destination_ba_offset,
hal->shadow_config[index].addr);
goto error;
}
return;
error:
qdf_print("%s: baddr %pK, desination %pK, shadow_address %pK s0offset %pK index %x",
__func__, hal->dev_base_addr, destination, shadow_address,
shadow_0_offset, index);
QDF_BUG(0);
return;
}
static void hal_target_based_configure(struct hal_soc *hal)
{
switch (hal->target_type) {
#ifdef QCA_WIFI_QCA6290
case TARGET_TYPE_QCA6290:
hal->use_register_windowing = true;
hal_qca6290_attach(hal);
break;
#endif
#ifdef QCA_WIFI_QCA8074
case TARGET_TYPE_QCA8074:
hal_qca8074_attach(hal);
break;
#endif
default:
break;
}
}
uint32_t hal_get_target_type(struct hal_soc *hal)
{
struct hif_target_info *tgt_info =
hif_get_target_info_handle(hal->hif_handle);
return tgt_info->target_type;
}
qdf_export_symbol(hal_get_target_type);
/**
* hal_attach - Initialize HAL layer
* @hif_handle: Opaque HIF handle
* @qdf_dev: QDF device
*
* Return: Opaque HAL SOC handle
* NULL on failure (if given ring is not available)
*
* This function should be called as part of HIF initialization (for accessing
* copy engines). DP layer will get hal_soc handle using hif_get_hal_handle()
*
*/
void *hal_attach(void *hif_handle, qdf_device_t qdf_dev)
{
struct hal_soc *hal;
int i;
hal = qdf_mem_malloc(sizeof(*hal));
if (!hal) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: hal_soc allocation failed\n", __func__);
goto fail0;
}
hal->hif_handle = hif_handle;
hal->dev_base_addr = hif_get_dev_ba(hif_handle);
hal->qdf_dev = qdf_dev;
hal->shadow_rdptr_mem_vaddr = (uint32_t *)qdf_mem_alloc_consistent(
qdf_dev, qdf_dev->dev, sizeof(*(hal->shadow_rdptr_mem_vaddr)) *
HAL_SRNG_ID_MAX, &(hal->shadow_rdptr_mem_paddr));
if (!hal->shadow_rdptr_mem_paddr) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: hal->shadow_rdptr_mem_paddr allocation failed\n",
__func__);
goto fail1;
}
hal->shadow_wrptr_mem_vaddr =
(uint32_t *)qdf_mem_alloc_consistent(qdf_dev, qdf_dev->dev,
sizeof(*(hal->shadow_wrptr_mem_vaddr)) * HAL_MAX_LMAC_RINGS,
&(hal->shadow_wrptr_mem_paddr));
if (!hal->shadow_wrptr_mem_vaddr) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: hal->shadow_wrptr_mem_vaddr allocation failed\n",
__func__);
goto fail2;
}
for (i = 0; i < HAL_SRNG_ID_MAX; i++) {
hal->srng_list[i].initialized = 0;
hal->srng_list[i].ring_id = i;
}
qdf_spinlock_create(&hal->register_access_lock);
hal->register_window = 0;
hal->target_type = hal_get_target_type(hal);
hal_target_based_configure(hal);
return (void *)hal;
fail2:
qdf_mem_free_consistent(qdf_dev, qdf_dev->dev,
sizeof(*(hal->shadow_rdptr_mem_vaddr)) * HAL_SRNG_ID_MAX,
hal->shadow_rdptr_mem_vaddr, hal->shadow_rdptr_mem_paddr, 0);
fail1:
qdf_mem_free(hal);
fail0:
return NULL;
}
qdf_export_symbol(hal_attach);
/**
* hal_mem_info - Retrieve hal memory base address
*
* @hal_soc: Opaque HAL SOC handle
* @mem: pointer to structure to be updated with hal mem info
*/
void hal_get_meminfo(void *hal_soc, struct hal_mem_info *mem )
{
struct hal_soc *hal = (struct hal_soc *)hal_soc;
mem->dev_base_addr = (void *)hal->dev_base_addr;
mem->shadow_rdptr_mem_vaddr = (void *)hal->shadow_rdptr_mem_vaddr;
mem->shadow_wrptr_mem_vaddr = (void *)hal->shadow_wrptr_mem_vaddr;
mem->shadow_rdptr_mem_paddr = (void *)hal->shadow_rdptr_mem_paddr;
mem->shadow_wrptr_mem_paddr = (void *)hal->shadow_wrptr_mem_paddr;
hif_read_phy_mem_base(hal->hif_handle, (qdf_dma_addr_t *)&mem->dev_base_paddr);
return;
}
qdf_export_symbol(hal_get_meminfo);
/**
* hal_detach - Detach HAL layer
* @hal_soc: HAL SOC handle
*
* Return: Opaque HAL SOC handle
* NULL on failure (if given ring is not available)
*
* This function should be called as part of HIF initialization (for accessing
* copy engines). DP layer will get hal_soc handle using hif_get_hal_handle()
*
*/
extern void hal_detach(void *hal_soc)
{
struct hal_soc *hal = (struct hal_soc *)hal_soc;
qdf_mem_free_consistent(hal->qdf_dev, hal->qdf_dev->dev,
sizeof(*(hal->shadow_rdptr_mem_vaddr)) * HAL_SRNG_ID_MAX,
hal->shadow_rdptr_mem_vaddr, hal->shadow_rdptr_mem_paddr, 0);
qdf_mem_free_consistent(hal->qdf_dev, hal->qdf_dev->dev,
sizeof(*(hal->shadow_wrptr_mem_vaddr)) * HAL_MAX_LMAC_RINGS,
hal->shadow_wrptr_mem_vaddr, hal->shadow_wrptr_mem_paddr, 0);
qdf_mem_free(hal);
return;
}
qdf_export_symbol(hal_detach);
/**
* hal_srng_src_hw_init - Private function to initialize SRNG
* source ring HW
* @hal_soc: HAL SOC handle
* @srng: SRNG ring pointer
*/
static inline void hal_srng_src_hw_init(struct hal_soc *hal,
struct hal_srng *srng)
{
uint32_t reg_val = 0;
uint64_t tp_addr = 0;
HIF_DBG("%s: hw_init srng %d", __func__, srng->ring_id);
if (srng->flags & HAL_SRNG_MSI_INTR) {
SRNG_SRC_REG_WRITE(srng, MSI1_BASE_LSB,
srng->msi_addr & 0xffffffff);
reg_val = SRNG_SM(SRNG_SRC_FLD(MSI1_BASE_MSB, ADDR),
(uint64_t)(srng->msi_addr) >> 32) |
SRNG_SM(SRNG_SRC_FLD(MSI1_BASE_MSB,
MSI1_ENABLE), 1);
SRNG_SRC_REG_WRITE(srng, MSI1_BASE_MSB, reg_val);
SRNG_SRC_REG_WRITE(srng, MSI1_DATA, srng->msi_data);
}
SRNG_SRC_REG_WRITE(srng, BASE_LSB, srng->ring_base_paddr & 0xffffffff);
reg_val = SRNG_SM(SRNG_SRC_FLD(BASE_MSB, RING_BASE_ADDR_MSB),
((uint64_t)(srng->ring_base_paddr) >> 32)) |
SRNG_SM(SRNG_SRC_FLD(BASE_MSB, RING_SIZE),
srng->entry_size * srng->num_entries);
SRNG_SRC_REG_WRITE(srng, BASE_MSB, reg_val);
#if defined(WCSS_VERSION) && \
((defined(CONFIG_WIN) && (WCSS_VERSION > 81)) || \
(defined(CONFIG_MCL) && (WCSS_VERSION >= 72)))
reg_val = SRNG_SM(SRNG_SRC_FLD(ID, ENTRY_SIZE), srng->entry_size);
#else
reg_val = SRNG_SM(SRNG_SRC_FLD(ID, RING_ID), srng->ring_id) |
SRNG_SM(SRNG_SRC_FLD(ID, ENTRY_SIZE), srng->entry_size);
#endif
SRNG_SRC_REG_WRITE(srng, ID, reg_val);
/**
* Interrupt setup:
* Default interrupt mode is 'pulse'. Need to setup SW_INTERRUPT_MODE
* if level mode is required
*/
reg_val = 0;
/*
* WAR - Hawkeye v1 has a hardware bug which requires timer value to be
* programmed in terms of 1us resolution instead of 8us resolution as
* given in MLD.
*/
if (srng->intr_timer_thres_us) {
reg_val |= SRNG_SM(SRNG_SRC_FLD(CONSUMER_INT_SETUP_IX0,
INTERRUPT_TIMER_THRESHOLD),
srng->intr_timer_thres_us);
/* For HK v2 this should be (srng->intr_timer_thres_us >> 3) */
}
if (srng->intr_batch_cntr_thres_entries) {
reg_val |= SRNG_SM(SRNG_SRC_FLD(CONSUMER_INT_SETUP_IX0,
BATCH_COUNTER_THRESHOLD),
srng->intr_batch_cntr_thres_entries *
srng->entry_size);
}
SRNG_SRC_REG_WRITE(srng, CONSUMER_INT_SETUP_IX0, reg_val);
reg_val = 0;
if (srng->flags & HAL_SRNG_LOW_THRES_INTR_ENABLE) {
reg_val |= SRNG_SM(SRNG_SRC_FLD(CONSUMER_INT_SETUP_IX1,
LOW_THRESHOLD), srng->u.src_ring.low_threshold);
}
SRNG_SRC_REG_WRITE(srng, CONSUMER_INT_SETUP_IX1, reg_val);
/* As per HW team, TP_ADDR and HP_ADDR for Idle link ring should
* remain 0 to avoid some WBM stability issues. Remote head/tail
* pointers are not required since this ring is completely managed
* by WBM HW */
if (srng->ring_id != HAL_SRNG_WBM_IDLE_LINK) {
tp_addr = (uint64_t)(hal->shadow_rdptr_mem_paddr +
((unsigned long)(srng->u.src_ring.tp_addr) -
(unsigned long)(hal->shadow_rdptr_mem_vaddr)));
SRNG_SRC_REG_WRITE(srng, TP_ADDR_LSB, tp_addr & 0xffffffff);
SRNG_SRC_REG_WRITE(srng, TP_ADDR_MSB, tp_addr >> 32);
}
/* Initilaize head and tail pointers to indicate ring is empty */
SRNG_SRC_REG_WRITE(srng, HP, 0);
SRNG_SRC_REG_WRITE(srng, TP, 0);
*(srng->u.src_ring.tp_addr) = 0;
reg_val = ((srng->flags & HAL_SRNG_DATA_TLV_SWAP) ?
SRNG_SM(SRNG_SRC_FLD(MISC, DATA_TLV_SWAP_BIT), 1) : 0) |
((srng->flags & HAL_SRNG_RING_PTR_SWAP) ?
SRNG_SM(SRNG_SRC_FLD(MISC, HOST_FW_SWAP_BIT), 1) : 0) |
((srng->flags & HAL_SRNG_MSI_SWAP) ?
SRNG_SM(SRNG_SRC_FLD(MISC, MSI_SWAP_BIT), 1) : 0);
/* Loop count is not used for SRC rings */
reg_val |= SRNG_SM(SRNG_SRC_FLD(MISC, LOOPCNT_DISABLE), 1);
/*
* reg_val |= SRNG_SM(SRNG_SRC_FLD(MISC, SRNG_ENABLE), 1);
* todo: update fw_api and replace with above line
* (when SRNG_ENABLE field for the MISC register is available in fw_api)
* (WCSS_UMAC_CE_0_SRC_WFSS_CE_CHANNEL_SRC_R0_SRC_RING_MISC)
*/
reg_val |= 0x40;
SRNG_SRC_REG_WRITE(srng, MISC, reg_val);
}
/**
* hal_ce_dst_setup - Initialize CE destination ring registers
* @hal_soc: HAL SOC handle
* @srng: SRNG ring pointer
*/
static inline void hal_ce_dst_setup(struct hal_soc *hal, struct hal_srng *srng,
int ring_num)
{
uint32_t reg_val = 0;
uint32_t reg_addr;
struct hal_hw_srng_config *ring_config =
HAL_SRNG_CONFIG(hal, CE_DST);
/* set DEST_MAX_LENGTH according to ce assignment */
reg_addr = HWIO_WFSS_CE_CHANNEL_DST_R0_DEST_CTRL_ADDR(
ring_config->reg_start[R0_INDEX] +
(ring_num * ring_config->reg_size[R0_INDEX]));
reg_val = HAL_REG_READ(hal, reg_addr);
reg_val &= ~HWIO_WFSS_CE_CHANNEL_DST_R0_DEST_CTRL_DEST_MAX_LENGTH_BMSK;
reg_val |= srng->u.dst_ring.max_buffer_length &
HWIO_WFSS_CE_CHANNEL_DST_R0_DEST_CTRL_DEST_MAX_LENGTH_BMSK;
HAL_REG_WRITE(hal, reg_addr, reg_val);
}
/**
* hal_reo_remap_IX0 - Remap REO ring destination
* @hal: HAL SOC handle
* @remap_val: Remap value
*/
void hal_reo_remap_IX0(struct hal_soc *hal, uint32_t remap_val)
{
uint32_t reg_offset = HWIO_REO_R0_DESTINATION_RING_CTRL_IX_0_ADDR(
SEQ_WCSS_UMAC_REO_REG_OFFSET);
HAL_REG_WRITE(hal, reg_offset, remap_val);
}
/**
* hal_srng_dst_set_hp_paddr() - Set physical address to dest ring head pointer
* @srng: sring pointer
* @paddr: physical address
*/
void hal_srng_dst_set_hp_paddr(struct hal_srng *srng,
uint64_t paddr)
{
SRNG_DST_REG_WRITE(srng, HP_ADDR_LSB,
paddr & 0xffffffff);
SRNG_DST_REG_WRITE(srng, HP_ADDR_MSB,
paddr >> 32);
}
/**
* hal_srng_dst_init_hp() - Initilaize destination ring head pointer
* @srng: sring pointer
* @vaddr: virtual address
*/
void hal_srng_dst_init_hp(struct hal_srng *srng,
uint32_t *vaddr)
{
srng->u.dst_ring.hp_addr = vaddr;
SRNG_DST_REG_WRITE(srng, HP, srng->u.dst_ring.cached_hp);
*(srng->u.dst_ring.hp_addr) = srng->u.dst_ring.cached_hp;
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
"hp_addr=%pK, cached_hp=%d, hp=%d\n",
(void *)srng->u.dst_ring.hp_addr, srng->u.dst_ring.cached_hp,
*(srng->u.dst_ring.hp_addr));
}
/**
* hal_srng_dst_hw_init - Private function to initialize SRNG
* destination ring HW
* @hal_soc: HAL SOC handle
* @srng: SRNG ring pointer
*/
static inline void hal_srng_dst_hw_init(struct hal_soc *hal,
struct hal_srng *srng)
{
uint32_t reg_val = 0;
uint64_t hp_addr = 0;
HIF_DBG("%s: hw_init srng %d", __func__, srng->ring_id);
if (srng->flags & HAL_SRNG_MSI_INTR) {
SRNG_DST_REG_WRITE(srng, MSI1_BASE_LSB,
srng->msi_addr & 0xffffffff);
reg_val = SRNG_SM(SRNG_DST_FLD(MSI1_BASE_MSB, ADDR),
(uint64_t)(srng->msi_addr) >> 32) |
SRNG_SM(SRNG_DST_FLD(MSI1_BASE_MSB,
MSI1_ENABLE), 1);
SRNG_DST_REG_WRITE(srng, MSI1_BASE_MSB, reg_val);
SRNG_DST_REG_WRITE(srng, MSI1_DATA, srng->msi_data);
}
SRNG_DST_REG_WRITE(srng, BASE_LSB, srng->ring_base_paddr & 0xffffffff);
reg_val = SRNG_SM(SRNG_DST_FLD(BASE_MSB, RING_BASE_ADDR_MSB),
((uint64_t)(srng->ring_base_paddr) >> 32)) |
SRNG_SM(SRNG_DST_FLD(BASE_MSB, RING_SIZE),
srng->entry_size * srng->num_entries);
SRNG_DST_REG_WRITE(srng, BASE_MSB, reg_val);
reg_val = SRNG_SM(SRNG_DST_FLD(ID, RING_ID), srng->ring_id) |
SRNG_SM(SRNG_DST_FLD(ID, ENTRY_SIZE), srng->entry_size);
SRNG_DST_REG_WRITE(srng, ID, reg_val);
/**
* Interrupt setup:
* Default interrupt mode is 'pulse'. Need to setup SW_INTERRUPT_MODE
* if level mode is required
*/
reg_val = 0;
if (srng->intr_timer_thres_us) {
reg_val |= SRNG_SM(SRNG_DST_FLD(PRODUCER_INT_SETUP,
INTERRUPT_TIMER_THRESHOLD),
srng->intr_timer_thres_us >> 3);
}
if (srng->intr_batch_cntr_thres_entries) {
reg_val |= SRNG_SM(SRNG_DST_FLD(PRODUCER_INT_SETUP,
BATCH_COUNTER_THRESHOLD),
srng->intr_batch_cntr_thres_entries *
srng->entry_size);
}
SRNG_DST_REG_WRITE(srng, PRODUCER_INT_SETUP, reg_val);
hp_addr = (uint64_t)(hal->shadow_rdptr_mem_paddr +
((unsigned long)(srng->u.dst_ring.hp_addr) -
(unsigned long)(hal->shadow_rdptr_mem_vaddr)));
SRNG_DST_REG_WRITE(srng, HP_ADDR_LSB, hp_addr & 0xffffffff);
SRNG_DST_REG_WRITE(srng, HP_ADDR_MSB, hp_addr >> 32);
/* Initilaize head and tail pointers to indicate ring is empty */
SRNG_DST_REG_WRITE(srng, HP, 0);
SRNG_DST_REG_WRITE(srng, TP, 0);
*(srng->u.dst_ring.hp_addr) = 0;
reg_val = ((srng->flags & HAL_SRNG_DATA_TLV_SWAP) ?
SRNG_SM(SRNG_DST_FLD(MISC, DATA_TLV_SWAP_BIT), 1) : 0) |
((srng->flags & HAL_SRNG_RING_PTR_SWAP) ?
SRNG_SM(SRNG_DST_FLD(MISC, HOST_FW_SWAP_BIT), 1) : 0) |
((srng->flags & HAL_SRNG_MSI_SWAP) ?
SRNG_SM(SRNG_DST_FLD(MISC, MSI_SWAP_BIT), 1) : 0);
/*
* reg_val |= SRNG_SM(SRNG_SRC_FLD(MISC, SRNG_ENABLE), 1);
* todo: update fw_api and replace with above line
* (when SRNG_ENABLE field for the MISC register is available in fw_api)
* (WCSS_UMAC_CE_0_SRC_WFSS_CE_CHANNEL_SRC_R0_SRC_RING_MISC)
*/
reg_val |= 0x40;
SRNG_DST_REG_WRITE(srng, MISC, reg_val);
}
/**
* hal_srng_hw_init - Private function to initialize SRNG HW
* @hal_soc: HAL SOC handle
* @srng: SRNG ring pointer
*/
static inline void hal_srng_hw_init(struct hal_soc *hal,
struct hal_srng *srng)
{
if (srng->ring_dir == HAL_SRNG_SRC_RING)
hal_srng_src_hw_init(hal, srng);
else
hal_srng_dst_hw_init(hal, srng);
}
#ifdef CONFIG_SHADOW_V2
#define ignore_shadow false
#define CHECK_SHADOW_REGISTERS true
#else
#define ignore_shadow true
#define CHECK_SHADOW_REGISTERS false
#endif
/**
* hal_srng_setup - Initialize HW SRNG ring.
* @hal_soc: Opaque HAL SOC handle
* @ring_type: one of the types from hal_ring_type
* @ring_num: Ring number if there are multiple rings of same type (staring
* from 0)
* @mac_id: valid MAC Id should be passed if ring type is one of lmac rings
* @ring_params: SRNG ring params in hal_srng_params structure.
* Callers are expected to allocate contiguous ring memory of size
* 'num_entries * entry_size' bytes and pass the physical and virtual base
* addresses through 'ring_base_paddr' and 'ring_base_vaddr' in
* hal_srng_params structure. Ring base address should be 8 byte aligned
* and size of each ring entry should be queried using the API
* hal_srng_get_entrysize
*
* Return: Opaque pointer to ring on success
* NULL on failure (if given ring is not available)
*/
void *hal_srng_setup(void *hal_soc, int ring_type, int ring_num,
int mac_id, struct hal_srng_params *ring_params)
{
int ring_id;
struct hal_soc *hal = (struct hal_soc *)hal_soc;
struct hal_srng *srng;
struct hal_hw_srng_config *ring_config =
HAL_SRNG_CONFIG(hal, ring_type);
void *dev_base_addr;
int i;
ring_id = hal_get_srng_ring_id(hal_soc, ring_type, ring_num, mac_id);
if (ring_id < 0)
return NULL;
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: mac_id %d ring_id %d\n",
__func__, mac_id, ring_id);
srng = hal_get_srng(hal_soc, ring_id);
if (srng->initialized) {
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
"%s: Ring (ring_type, ring_num) already initialized\n",
__func__);
return NULL;
}
dev_base_addr = hal->dev_base_addr;
srng->ring_id = ring_id;
srng->ring_dir = ring_config->ring_dir;
srng->ring_base_paddr = ring_params->ring_base_paddr;
srng->ring_base_vaddr = ring_params->ring_base_vaddr;
srng->entry_size = ring_config->entry_size;
srng->num_entries = ring_params->num_entries;
srng->ring_size = srng->num_entries * srng->entry_size;
srng->ring_size_mask = srng->ring_size - 1;
srng->msi_addr = ring_params->msi_addr;
srng->msi_data = ring_params->msi_data;
srng->intr_timer_thres_us = ring_params->intr_timer_thres_us;
srng->intr_batch_cntr_thres_entries =
ring_params->intr_batch_cntr_thres_entries;
srng->hal_soc = hal_soc;
for (i = 0 ; i < MAX_SRNG_REG_GROUPS; i++) {
srng->hwreg_base[i] = dev_base_addr + ring_config->reg_start[i]
+ (ring_num * ring_config->reg_size[i]);
}
/* Zero out the entire ring memory */
qdf_mem_zero(srng->ring_base_vaddr, (srng->entry_size *
srng->num_entries) << 2);
srng->flags = ring_params->flags;
#ifdef BIG_ENDIAN_HOST
/* TODO: See if we should we get these flags from caller */
srng->flags |= HAL_SRNG_DATA_TLV_SWAP;
srng->flags |= HAL_SRNG_MSI_SWAP;
srng->flags |= HAL_SRNG_RING_PTR_SWAP;
#endif
if (srng->ring_dir == HAL_SRNG_SRC_RING) {
srng->u.src_ring.hp = 0;
srng->u.src_ring.reap_hp = srng->ring_size -
srng->entry_size;
srng->u.src_ring.tp_addr =
&(hal->shadow_rdptr_mem_vaddr[ring_id]);
srng->u.src_ring.low_threshold =
ring_params->low_threshold * srng->entry_size;
if (ring_config->lmac_ring) {
/* For LMAC rings, head pointer updates will be done
* through FW by writing to a shared memory location
*/
srng->u.src_ring.hp_addr =
&(hal->shadow_wrptr_mem_vaddr[ring_id -
HAL_SRNG_LMAC1_ID_START]);
srng->flags |= HAL_SRNG_LMAC_RING;
} else if (ignore_shadow || (srng->u.src_ring.hp_addr == 0)) {
srng->u.src_ring.hp_addr = SRNG_SRC_ADDR(srng, HP);
if (CHECK_SHADOW_REGISTERS) {
QDF_TRACE(QDF_MODULE_ID_TXRX,
QDF_TRACE_LEVEL_ERROR,
"%s: Ring (%d, %d) missing shadow config\n",
__func__, ring_type, ring_num);
}
} else {
hal_validate_shadow_register(hal,
SRNG_SRC_ADDR(srng, HP),
srng->u.src_ring.hp_addr);
}
} 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.hp_addr =
&(hal->shadow_rdptr_mem_vaddr[ring_id]);
if (ring_config->lmac_ring) {
/* For LMAC rings, tail pointer updates will be done
* through FW by writing to a shared memory location
*/
srng->u.dst_ring.tp_addr =
&(hal->shadow_wrptr_mem_vaddr[ring_id -
HAL_SRNG_LMAC1_ID_START]);
srng->flags |= HAL_SRNG_LMAC_RING;
} else if (ignore_shadow || srng->u.dst_ring.tp_addr == 0) {
srng->u.dst_ring.tp_addr = SRNG_DST_ADDR(srng, TP);
if (CHECK_SHADOW_REGISTERS) {
QDF_TRACE(QDF_MODULE_ID_TXRX,
QDF_TRACE_LEVEL_ERROR,
"%s: Ring (%d, %d) missing shadow config\n",
__func__, ring_type, ring_num);
}
} else {
hal_validate_shadow_register(hal,
SRNG_DST_ADDR(srng, TP),
srng->u.dst_ring.tp_addr);
}
}
if (!(ring_config->lmac_ring)) {
hal_srng_hw_init(hal, srng);
if (ring_type == CE_DST) {
srng->u.dst_ring.max_buffer_length = ring_params->max_buffer_length;
hal_ce_dst_setup(hal, srng, ring_num);
}
}
SRNG_LOCK_INIT(&srng->lock);
srng->initialized = true;
return (void *)srng;
}
qdf_export_symbol(hal_srng_setup);
/**
* hal_srng_cleanup - Deinitialize HW SRNG ring.
* @hal_soc: Opaque HAL SOC handle
* @hal_srng: Opaque HAL SRNG pointer
*/
void hal_srng_cleanup(void *hal_soc, void *hal_srng)
{
struct hal_srng *srng = (struct hal_srng *)hal_srng;
SRNG_LOCK_DESTROY(&srng->lock);
srng->initialized = 0;
}
qdf_export_symbol(hal_srng_cleanup);
/**
* hal_srng_get_entrysize - Returns size of ring entry in bytes
* @hal_soc: Opaque HAL SOC handle
* @ring_type: one of the types from hal_ring_type
*
*/
uint32_t hal_srng_get_entrysize(void *hal_soc, int ring_type)
{
struct hal_soc *hal = (struct hal_soc *)hal_soc;
struct hal_hw_srng_config *ring_config =
HAL_SRNG_CONFIG(hal, ring_type);
return ring_config->entry_size << 2;
}
qdf_export_symbol(hal_srng_get_entrysize);
/**
* hal_srng_max_entries - Returns maximum possible number of ring entries
* @hal_soc: Opaque HAL SOC handle
* @ring_type: one of the types from hal_ring_type
*
* Return: Maximum number of entries for the given ring_type
*/
uint32_t hal_srng_max_entries(void *hal_soc, int ring_type)
{
struct hal_soc *hal = (struct hal_soc *)hal_soc;
struct hal_hw_srng_config *ring_config =
HAL_SRNG_CONFIG(hal, ring_type);
return ring_config->max_size / ring_config->entry_size;
}
qdf_export_symbol(hal_srng_max_entries);
enum hal_srng_dir hal_srng_get_dir(void *hal_soc, int ring_type)
{
struct hal_soc *hal = (struct hal_soc *)hal_soc;
struct hal_hw_srng_config *ring_config =
HAL_SRNG_CONFIG(hal, ring_type);
return ring_config->ring_dir;
}
/**
* hal_srng_dump - Dump ring status
* @srng: hal srng pointer
*/
void hal_srng_dump(struct hal_srng *srng)
{
if (srng->ring_dir == HAL_SRNG_SRC_RING) {
qdf_print("=== SRC RING %d ===", srng->ring_id);
qdf_print("hp %u, reap_hp %u, tp %u, cached tp %u",
srng->u.src_ring.hp,
srng->u.src_ring.reap_hp,
*srng->u.src_ring.tp_addr,
srng->u.src_ring.cached_tp);
} else {
qdf_print("=== DST RING %d ===", srng->ring_id);
qdf_print("tp %u, hp %u, cached tp %u, loop_cnt %u",
srng->u.dst_ring.tp,
*srng->u.dst_ring.hp_addr,
srng->u.dst_ring.cached_hp,
srng->u.dst_ring.loop_cnt);
}
}
/**
* hal_get_srng_params - Retrieve SRNG parameters for a given ring from HAL
*
* @hal_soc: Opaque HAL SOC handle
* @hal_ring: Ring pointer (Source or Destination ring)
* @ring_params: SRNG parameters will be returned through this structure
*/
extern void hal_get_srng_params(void *hal_soc, void *hal_ring,
struct hal_srng_params *ring_params)
{
struct hal_srng *srng = (struct hal_srng *)hal_ring;
int i =0;
ring_params->ring_id = srng->ring_id;
ring_params->ring_dir = srng->ring_dir;
ring_params->entry_size = srng->entry_size;
ring_params->ring_base_paddr = srng->ring_base_paddr;
ring_params->ring_base_vaddr = srng->ring_base_vaddr;
ring_params->num_entries = srng->num_entries;
ring_params->msi_addr = srng->msi_addr;
ring_params->msi_data = srng->msi_data;
ring_params->intr_timer_thres_us = srng->intr_timer_thres_us;
ring_params->intr_batch_cntr_thres_entries =
srng->intr_batch_cntr_thres_entries;
ring_params->low_threshold = srng->u.src_ring.low_threshold;
ring_params->flags = srng->flags;
ring_params->ring_id = srng->ring_id;
for (i = 0 ; i < MAX_SRNG_REG_GROUPS; i++)
ring_params->hwreg_base[i] = srng->hwreg_base[i];
}
qdf_export_symbol(hal_get_srng_params);