blob: 0a47df982a868b55bc19267422cfca5d12d13e4f [file] [log] [blame]
/*
* This file is part of the UWB stack for linux.
*
* Copyright (c) 2020 Qorvo US, Inc.
*
* This software is provided under the GNU General Public License, version 2
* (GPLv2), as well as under a Qorvo commercial license.
*
* You may choose to use this software under the terms of the GPLv2 License,
* version 2 ("GPLv2"), as published by the Free Software Foundation.
* You should have received a copy of the GPLv2 along with this program. If
* not, see <http://www.gnu.org/licenses/>.
*
* This program is distributed under the GPLv2 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 GPLv2 for more
* details.
*
* If you cannot meet the requirements of the GPLv2, you may not use this
* software for any purpose without first obtaining a commercial license from
* Qorvo.
* Please contact Qorvo to inquire about licensing terms.
*/
#include "dw3000.h"
#include "dw3000_core.h"
#include "dw3000_core_reg.h"
#include "dw3000_ccc_mailbox.h"
static const struct dw3000_chip_register d0_registers[] = {
/* registres virtuels pour dump des fileID */
{ "GEN_CFG0 ", 0x00, 0x7e, 0, DW3000_CHIPREG_DUMP, NULL },
{ "GEN_CFG1 ", 0x01, 0x64, 0, DW3000_CHIPREG_DUMP, NULL },
{ "STS_CFG ", 0x02, 0x2c, 0, DW3000_CHIPREG_DUMP, NULL },
{ "RX_TUNE ", 0x03, 0x54, 0, DW3000_CHIPREG_DUMP, NULL },
{ "EXT_SYNC ", 0x04, 0x21, 0, DW3000_CHIPREG_DUMP, NULL },
{ "GPIO_CTRL ", 0x05, 0x2e, 0, DW3000_CHIPREG_DUMP, NULL },
{ "DRX ", 0x06, 0x2c, 0, DW3000_CHIPREG_DUMP, NULL },
{ "RF_CONF ", 0x07, 0x51, 0, DW3000_CHIPREG_DUMP, NULL },
{ "RF_CAL ", 0x08, 0x1e, 0, DW3000_CHIPREG_DUMP, NULL },
{ "FS_CTRL ", 0x09, 0x15, 0, DW3000_CHIPREG_DUMP, NULL },
{ "AON ", 0x0a, 0x15, 0, DW3000_CHIPREG_DUMP, NULL },
{ "OTP_IF ", 0x0b, 0x18, 0, DW3000_CHIPREG_DUMP, NULL },
{ "CIA_1 ", 0x0c, 0x6c, 0, DW3000_CHIPREG_DUMP, NULL },
{ "CIA_2 ", 0x0d, 0x6c, 0, DW3000_CHIPREG_DUMP, NULL },
{ "CIA_3 ", 0x0e, 0x20, 0, DW3000_CHIPREG_DUMP, NULL },
{ "DIG_DIAG ", 0x0f, 0x51, 0, DW3000_CHIPREG_DUMP, NULL },
{ "PMSC ", 0x11, 0x4a, 0, DW3000_CHIPREG_DUMP, NULL },
/* TODO: RX/TX buffers limited to first 128bytes only.
Shall we dump the whole 1024 bytes? */
{ "RX_BUFFER ", 0x12, 0x80, 0, DW3000_CHIPREG_DUMP, NULL },
{ "RX_BUFFER1 ", 0x13, 0x80, 0, DW3000_CHIPREG_DUMP, NULL },
/* No TX_BUFFER as read isn't supported */
/* CIR memory require 2 bits configured elsewhere, so removed. */
{ "SCRATCH_RAM", 0x16, 0x7f, 0, DW3000_CHIPREG_DUMP, NULL },
{ "AES RAM ", 0x17, 0x80, 0, DW3000_CHIPREG_DUMP, NULL },
{ "SET_X ", 0x18, 0x1d0, 0, DW3000_CHIPREG_DUMP, NULL },
{ "IN_PTR_CFG ", 0x1f, 0x12, 0, DW3000_CHIPREG_DUMP, NULL },
};
const struct dw3000_chip_register *dw3000_d0_get_registers(struct dw3000 *dw,
size_t *count)
{
*count = ARRAY_SIZE(d0_registers);
return d0_registers;
}
const u32 *dw3000_d0_get_config_mrxlut_chan(struct dw3000 *dw, u8 channel)
{
/* Lookup table default values for channel 5 */
static const u32 dw3000_d0_configmrxlut_ch5[DW3000_CONFIGMRXLUT_MAX] = {
0x1c0fd, 0x1c43e, 0x1c6be, 0x1c77e, 0x1cf36, 0x1cfb5, 0x1cff5
};
/* Lookup table default values for channel 9 */
static const u32 dw3000_d0_configmrxlut_ch9[DW3000_CONFIGMRXLUT_MAX] = {
0x2a8fe, 0x2ac36, 0x2a5fe, 0x2af3e, 0x2af7d, 0x2afb5, 0x2afb5
};
switch (channel) {
case 5:
return dw3000_d0_configmrxlut_ch5;
case 9:
return dw3000_d0_configmrxlut_ch9;
default:
return NULL;
}
}
/**
* dw3000_d0_softreset() - D0 chip specific software reset
* @dw: The DW device.
*
* Return: zero on success, else a negative error code.
*/
int dw3000_d0_softreset(struct dw3000 *dw)
{
/* D0 require a FAST command to start soft-reset */
return dw3000_write_fastcmd(dw, DW3000_CMD_SEMA_RESET);
}
/**
* dw3000_d0_init() - D0 chip specific initialisation
* @dw: The DW device.
*
* Note: Still used by dw3000_e0_init().
*
* Return: zero on success, else a negative error code.
*/
int dw3000_d0_init(struct dw3000 *dw)
{
int rc = 0;
if (dw->current_operational_state != DW3000_OP_STATE_DEEP_SLEEP) {
/* Disable CCC Mailbox only if the chip is not in deep sleep.
* Else, on wake up, the CCC state will be not detectable. */
rc = dw3000_nfcc_coex_disable(dw);
}
return rc;
}
/**
* dw3000_d0_coex_init() - Configure the device's WiFi coexistence GPIO
* @dw: The DW device.
*
* Note: Still used by dw3000_e0_coex_init() as GPIO pin need to be configured.
*
* Return: zero on success, else a negative error code.
*/
int dw3000_d0_coex_init(struct dw3000 *dw)
{
u32 modemask;
u16 dirmask;
int rc;
if (dw->coex_gpio < 0)
return 0;
/* Ensure selected GPIO is well configured */
modemask = DW3000_GPIO_MODE_MSGP0_MODE_BIT_MASK
<< (DW3000_GPIO_MODE_MSGP0_MODE_BIT_LEN * dw->coex_gpio);
rc = dw3000_set_gpio_mode(dw, modemask, 0);
if (rc)
return rc;
dirmask = DW3000_GPIO_DIR_GDP0_BIT_MASK
<< (DW3000_GPIO_DIR_GDP0_BIT_LEN * dw->coex_gpio);
rc = dw3000_set_gpio_dir(dw, dirmask, 0);
return rc;
}
/**
* dw3000_d0_coex_gpio() - Update the device's WiFi coexistence GPIO
* @dw: The DW device.
* @state: The WiFi coexistence GPIO state to apply.
* @delay_us: The delay in us before changing GPIO state.
*
* Return: zero on success, else a negative error code.
*/
static int dw3000_d0_coex_gpio(struct dw3000 *dw, bool state, int delay_us)
{
int offset;
/* /!\ could be called first with (true, 1000), then before end of 1000
microseconds could be called with (false, 0), should handle this case
with stopping the timer if any */
if (delay_us) {
/* Wait to ensure GPIO is toggle on time */
if (delay_us > 10)
usleep_range(delay_us - 10, delay_us);
else
udelay(delay_us);
}
offset = DW3000_GPIO_OUT_GOP0_BIT_LEN * dw->coex_gpio;
dw3000_set_gpio_out(dw, !state << offset, state << offset);
return 0;
}
/**
* dw3000_d0_prog_ldo_and_bias_tune() - Programs the device's LDO and BIAS tuning
* @dw: The DW device.
*
* Return: zero on success, else a negative error code.
*/
int dw3000_d0_prog_ldo_and_bias_tune(struct dw3000 *dw)
{
struct dw3000_local_data *local = &dw->data;
struct dw3000_otp_data *otp = &dw->otp_data;
if (otp->ldo_tune_lo && otp->ldo_tune_hi) {
dw3000_reg_or16(dw, DW3000_NVM_CFG_ID, 0, DW3000_LDO_BIAS_KICK);
/* Save the kicks for the on-wake configuration */
local->sleep_mode |= DW3000_LOADLDO;
}
/* Use DGC_CFG from OTP */
local->dgc_otp_set = otp->dgc_addr == DW3000_DGC_CFG0 ?
DW3000_DGC_LOAD_FROM_OTP :
DW3000_DGC_LOAD_FROM_SW;
return 0;
}
const struct dw3000_chip_ops dw3000_chip_d0_ops = {
.softreset = dw3000_d0_softreset,
.init = dw3000_d0_init,
.coex_init = dw3000_d0_coex_init,
.coex_gpio = dw3000_d0_coex_gpio,
.prog_ldo_and_bias_tune = dw3000_d0_prog_ldo_and_bias_tune,
.get_config_mrxlut_chan = dw3000_d0_get_config_mrxlut_chan,
.get_registers = dw3000_d0_get_registers,
};