6db4831e98
Android 14
457 lines
12 KiB
C
457 lines
12 KiB
C
/*
|
|
* Copyright (C) 2014-2020 NXP Semiconductors, All Rights Reserved.
|
|
* Copyright 2020 GOODIX, 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 version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
*/
|
|
|
|
#include "inc/dbgprint.h"
|
|
#include "inc/tfa_service.h"
|
|
#include "inc/tfa_internal.h"
|
|
#include "inc/tfa_container.h"
|
|
#include "inc/tfa98xx_tfafieldnames.h"
|
|
|
|
/* The CurrentSense4 registers are not in the datasheet */
|
|
#define TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF (1 << 2)
|
|
#define TFA98XX_CURRENTSENSE4 0x49
|
|
|
|
/*********************/
|
|
/* GLOBAL (Defaults) */
|
|
/*********************/
|
|
|
|
static bool ipc_loaded;
|
|
|
|
void tfa_set_ipc_loaded(int status)
|
|
{
|
|
pr_info("set ipc_loaded %d for tfadsp\n", status);
|
|
ipc_loaded = (status) ? true : false;
|
|
}
|
|
|
|
int tfa_get_ipc_loaded(void)
|
|
{
|
|
pr_info("get ipc_loaded %d for tfadsp\n", ipc_loaded);
|
|
return ipc_loaded ? 1 : 0;
|
|
}
|
|
|
|
static enum tfa98xx_error no_overload_function_available
|
|
(struct tfa_device *tfa, int not_used)
|
|
{
|
|
(void)tfa;
|
|
(void)not_used;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
static enum tfa98xx_error no_overload_function_available2
|
|
(struct tfa_device *tfa)
|
|
{
|
|
(void)tfa;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
/* tfa98xx_dsp_system_stable
|
|
* return: *ready = 1 when clocks are stable to allow DSP subsystem access
|
|
*/
|
|
static enum tfa98xx_error tfa_dsp_system_stable
|
|
(struct tfa_device *tfa, int *ready)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned short status;
|
|
int value;
|
|
|
|
/* check the contents of the STATUS register */
|
|
value = TFA_READ_REG(tfa, AREFS);
|
|
if (value < 0) {
|
|
error = -value;
|
|
*ready = 0;
|
|
_ASSERT(error); /* an error here can be fatal */
|
|
return error;
|
|
}
|
|
status = (unsigned short)value;
|
|
|
|
/* check AREFS and CLKS: not ready if either is clear */
|
|
*ready = !((TFA_GET_BF_VALUE(tfa, AREFS, status) == 0)
|
|
|| (TFA_GET_BF_VALUE(tfa, CLKS, status) == 0));
|
|
|
|
return error;
|
|
}
|
|
|
|
/* tfa98xx_toggle_mtp_clock
|
|
* Allows to stop clock for MTP/FAim needed for PLMA5505
|
|
*/
|
|
static enum tfa98xx_error tfa_faim_protect(struct tfa_device *tfa, int state)
|
|
{
|
|
(void)tfa;
|
|
(void)state;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
/** Set internal oscillator into power down mode.
|
|
*
|
|
* This function is a worker for tfa98xx_set_osc_powerdown().
|
|
*
|
|
* @param[in] tfa device description structure
|
|
* @param[in] state new state 0 - oscillator is on, 1 oscillator is off.
|
|
*
|
|
* @return TFA98XX_ERROR_OK when successful, error otherwise.
|
|
*/
|
|
static enum tfa98xx_error tfa_set_osc_powerdown
|
|
(struct tfa_device *tfa, int state)
|
|
{
|
|
/* This function has no effect in general case, only for tfa9912 */
|
|
(void)tfa;
|
|
(void)state;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa_update_lpm(struct tfa_device *tfa, int state)
|
|
{
|
|
/* This function has no effect in general case, only for tfa9912 */
|
|
(void)tfa;
|
|
(void)state;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa_dsp_reset(struct tfa_device *tfa, int state)
|
|
{
|
|
/* generic function */
|
|
TFA_SET_BF_VOLATILE(tfa, RST, (uint16_t)state);
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
int tfa_set_swprofile(struct tfa_device *tfa, unsigned short new_value)
|
|
{
|
|
int mtpk, active_value = tfa->profile;
|
|
|
|
/* Also set the new value in the struct */
|
|
tfa->profile = new_value - 1;
|
|
|
|
/* for TFA1 devices */
|
|
/* it's in MTP shadow, so unlock if not done already */
|
|
mtpk = TFA_GET_BF(tfa, MTPK); /* get current key */
|
|
TFA_SET_BF_VOLATILE(tfa, MTPK, 0x5a);
|
|
TFA_SET_BF_VOLATILE(tfa, SWPROFIL, new_value); /* set current profile */
|
|
TFA_SET_BF_VOLATILE(tfa, MTPK, (uint16_t)mtpk); /* restore key */
|
|
|
|
return active_value;
|
|
}
|
|
|
|
static int tfa_get_swprofile(struct tfa_device *tfa)
|
|
{
|
|
/* return TFA_GET_BF(tfa, SWPROFIL) - 1; */
|
|
return tfa->profile;
|
|
}
|
|
|
|
static int tfa_set_swvstep(struct tfa_device *tfa, unsigned short new_value)
|
|
{
|
|
int mtpk, active_value = tfa->vstep;
|
|
|
|
/* Also set the new value in the struct */
|
|
tfa->vstep = new_value - 1;
|
|
|
|
/* for TFA1 devices */
|
|
/* it's in MTP shadow, so unlock if not done already */
|
|
mtpk = TFA_GET_BF(tfa, MTPK); /* get current key */
|
|
TFA_SET_BF_VOLATILE(tfa, MTPK, 0x5a);
|
|
TFA_SET_BF_VOLATILE(tfa, SWVSTEP, new_value); /* set current vstep */
|
|
TFA_SET_BF_VOLATILE(tfa, MTPK, (uint16_t)mtpk); /* restore key */
|
|
|
|
return active_value;
|
|
}
|
|
|
|
static int tfa_get_swvstep(struct tfa_device *tfa)
|
|
{
|
|
int value = 0;
|
|
/* Set the new value in the hw register */
|
|
value = TFA_GET_BF(tfa, SWVSTEP);
|
|
|
|
/* Also set the new value in the struct */
|
|
tfa->vstep = value - 1;
|
|
|
|
return value - 1; /* invalid if 0 */
|
|
}
|
|
|
|
static int tfa_get_mtpb(struct tfa_device *tfa)
|
|
{
|
|
|
|
int value = 0;
|
|
|
|
/* Set the new value in the hw register */
|
|
value = TFA_GET_BF(tfa, MTPB);
|
|
|
|
return value;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa_set_mute_nodsp(struct tfa_device *tfa, int mute)
|
|
{
|
|
(void)tfa;
|
|
(void)mute;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
static int tfa_set_bitfield(struct tfa_device *tfa,
|
|
uint16_t bitfield, uint16_t value)
|
|
{
|
|
return tfa_set_bf(tfa, (uint16_t)bitfield, value);
|
|
}
|
|
|
|
void set_ops_defaults(struct tfa_device_ops *ops)
|
|
{
|
|
/* defaults */
|
|
ops->reg_read = tfa98xx_read_register16;
|
|
ops->reg_write = tfa98xx_write_register16;
|
|
ops->mem_read = tfa98xx_dsp_read_mem;
|
|
ops->mem_write = tfa98xx_dsp_write_mem_word;
|
|
if (!ipc_loaded) {
|
|
ops->dsp_msg = tfa_dsp_msg_rpc;
|
|
ops->dsp_msg_read = tfa_dsp_msg_read_rpc;
|
|
}
|
|
ops->dsp_write_tables = no_overload_function_available;
|
|
ops->dsp_reset = tfa_dsp_reset;
|
|
ops->dsp_system_stable = tfa_dsp_system_stable;
|
|
ops->auto_copy_mtp_to_iic = no_overload_function_available2;
|
|
ops->factory_trimmer = no_overload_function_available2;
|
|
ops->set_swprof = tfa_set_swprofile;
|
|
ops->get_swprof = tfa_get_swprofile;
|
|
ops->set_swvstep = tfa_set_swvstep;
|
|
ops->get_swvstep = tfa_get_swvstep;
|
|
ops->get_mtpb = tfa_get_mtpb;
|
|
ops->set_mute = tfa_set_mute_nodsp;
|
|
ops->faim_protect = tfa_faim_protect;
|
|
ops->set_osc_powerdown = tfa_set_osc_powerdown;
|
|
ops->update_lpm = tfa_update_lpm;
|
|
ops->set_bitfield = tfa_set_bitfield;
|
|
}
|
|
|
|
/****************************/
|
|
/* no TFA
|
|
* external DSP SB instance
|
|
****************************/
|
|
static short tfanone_swvstep, swprof; /* TODO emulate in hal plugin */
|
|
static enum tfa98xx_error tfanone_dsp_system_stable
|
|
(struct tfa_device *tfa, int *ready)
|
|
{
|
|
(void)tfa; /* suppress warning */
|
|
*ready = 1; /* assume always ready */
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
static int tfanone_set_swprofile
|
|
(struct tfa_device *tfa, unsigned short new_value)
|
|
{
|
|
int active_value = tfa_dev_get_swprof(tfa);
|
|
|
|
/* Set the new value in the struct */
|
|
tfa->profile = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
swprof = new_value;
|
|
|
|
return active_value;
|
|
}
|
|
|
|
static int tfanone_get_swprofile(struct tfa_device *tfa)
|
|
{
|
|
(void)tfa; /* suppress warning */
|
|
|
|
return swprof;
|
|
}
|
|
|
|
static int tfanone_set_swvstep
|
|
(struct tfa_device *tfa, unsigned short new_value)
|
|
{
|
|
/* Set the new value in the struct */
|
|
tfa->vstep = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
tfanone_swvstep = new_value;
|
|
|
|
return new_value;
|
|
}
|
|
|
|
static int tfanone_get_swvstep(struct tfa_device *tfa)
|
|
{
|
|
(void)tfa; /* suppress warning */
|
|
|
|
return tfanone_swvstep;
|
|
}
|
|
|
|
void tfanone_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->dsp_system_stable = tfanone_dsp_system_stable;
|
|
ops->set_swprof = tfanone_set_swprofile;
|
|
ops->get_swprof = tfanone_get_swprofile;
|
|
ops->set_swvstep = tfanone_set_swvstep;
|
|
ops->get_swvstep = tfanone_get_swvstep;
|
|
|
|
}
|
|
|
|
/***********/
|
|
/* TFA9878 */
|
|
/***********/
|
|
static enum tfa98xx_error tfa9878_faim_protect(struct tfa_device *tfa,
|
|
int status)
|
|
{
|
|
enum tfa98xx_error ret = TFA98XX_ERROR_OK;
|
|
|
|
/* 0b = FAIM protection enabled 1b = FAIM protection disabled*/
|
|
ret = tfa_set_bf_volatile(tfa, TFA9878_BF_OPENMTP, (uint16_t)(status));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9878_specific(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned short value, xor;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
/* Unlock key 1 and 2 */
|
|
error = tfa_reg_write(tfa, 0x0F, 0x5A6B);
|
|
error = tfa_reg_read(tfa, 0xFB, &value);
|
|
xor = value ^ 0x005A;
|
|
error = tfa_reg_write(tfa, 0xA0, xor);
|
|
tfa98xx_key2(tfa, 0);
|
|
|
|
switch (tfa->rev) {
|
|
case 0x0a78: /* Initial revision ID */
|
|
/* ----- generated code start ----- */
|
|
/* ----- version 28 ----- */
|
|
tfa_reg_write(tfa, 0x01, 0x2e18); /* POR=0x2e88 */
|
|
tfa_reg_write(tfa, 0x02, 0x0628); /* POR=0x0008 */
|
|
tfa_reg_write(tfa, 0x04, 0x0240); /* POR=0x0340 */
|
|
tfa_reg_write(tfa, 0x52, 0x587c); /* POR=0x57dc */
|
|
tfa_reg_write(tfa, 0x61, 0x0183); /* POR=0x0a82 */
|
|
tfa_reg_write(tfa, 0x63, 0x055a); /* POR=0x0a9a */
|
|
tfa_reg_write(tfa, 0x65, 0x0542); /* POR=0x0a82 */
|
|
tfa_reg_write(tfa, 0x71, 0x303e); /* POR=0x307e */
|
|
tfa_reg_write(tfa, 0x83, 0x009a); /* POR=0x0799 */
|
|
/* ----- generated code end ----- */
|
|
break;
|
|
|
|
case 0x1a78: /* Initial revision ID */
|
|
/* ----- generated code start ----- */
|
|
/* ----- version 12 ----- */
|
|
tfa_reg_write(tfa, 0x01, 0x2e18); /* POR=0x2e88 */
|
|
tfa_reg_write(tfa, 0x02, 0x0628); /* POR=0x0008 */
|
|
tfa_reg_write(tfa, 0x04, 0x0241); /* POR=0x0340 */
|
|
tfa_reg_write(tfa, 0x52, 0x587c); /* POR=0x57dc */
|
|
tfa_reg_write(tfa, 0x61, 0x0183); /* POR=0x0a82 */
|
|
tfa_reg_write(tfa, 0x63, 0x055a); /* POR=0x0a9a */
|
|
tfa_reg_write(tfa, 0x65, 0x0542); /* POR=0x0a82 */
|
|
tfa_reg_write(tfa, 0x70, 0xb7ff); /* POR=0x37ff */
|
|
tfa_reg_write(tfa, 0x71, 0x303e); /* POR=0x307e */
|
|
tfa_reg_write(tfa, 0x83, 0x009a); /* POR=0x0799 */
|
|
tfa_reg_write(tfa, 0x84, 0x0211); /* POR=0x0011 */
|
|
tfa_reg_write(tfa, 0x8c, 0x0210); /* POR=0x0010 */
|
|
tfa_reg_write(tfa, 0xce, 0x2202); /* POR=0xa202 */
|
|
tfa_reg_write(tfa, 0xd5, 0x0000); /* POR=0x0100 */
|
|
/* ----- generated code end ----- */
|
|
break;
|
|
|
|
default:
|
|
pr_info("\nWarning: Optimal settings not found for device with revid = 0x%x\n",
|
|
tfa->rev);
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static int tfa9878_set_swprofile(struct tfa_device *tfa,
|
|
unsigned short new_value)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
int active_value = tfa_dev_get_swprof(tfa);
|
|
|
|
/* Set the new value in the struct */
|
|
tfa->profile = new_value - 1;
|
|
|
|
if (tfa->swprof != tfa->profile)
|
|
/* Set the new value in the hw register */
|
|
err = tfa_set_bf_volatile(tfa,
|
|
TFA9878_BF_SWPROFIL, new_value);
|
|
|
|
if (err == TFA98XX_ERROR_OK)
|
|
tfa->swprof = tfa->profile;
|
|
|
|
return active_value;
|
|
}
|
|
|
|
static int tfa9878_get_swprofile(struct tfa_device *tfa)
|
|
{
|
|
if (tfa->swprof == -1)
|
|
tfa->swprof = tfa_get_bf(tfa, TFA9878_BF_SWPROFIL) - 1;
|
|
|
|
return tfa->swprof;
|
|
}
|
|
|
|
static int tfa9878_set_swvstep(struct tfa_device *tfa,
|
|
unsigned short new_value)
|
|
{
|
|
/* Set the new value in the struct */
|
|
tfa->vstep = new_value - 1;
|
|
|
|
return new_value;
|
|
}
|
|
|
|
static int tfa9878_get_swvstep(struct tfa_device *tfa)
|
|
{
|
|
return tfa->vstep; /* vstep is not used */
|
|
}
|
|
|
|
/* tfa98xx_dsp_system_stable
|
|
* return: *ready = 1 when clocks are stable to allow DSP subsystem access
|
|
*/
|
|
static enum tfa98xx_error tfa9878_dsp_system_stable(struct tfa_device *tfa,
|
|
int *ready)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
|
|
/* check CLKS: ready if set */
|
|
*ready = tfa_get_bf(tfa, TFA9878_BF_CLKS) == 1;
|
|
|
|
return error;
|
|
}
|
|
|
|
static int tfa9878_get_mtpb(struct tfa_device *tfa)
|
|
{
|
|
int value;
|
|
|
|
value = tfa_get_bf(tfa, TFA9878_BF_MTPB);
|
|
|
|
return value;
|
|
}
|
|
|
|
void tfa9878_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->tfa_init = tfa9878_specific;
|
|
ops->set_swprof = tfa9878_set_swprofile;
|
|
ops->get_swprof = tfa9878_get_swprofile;
|
|
ops->set_swvstep = tfa9878_set_swvstep;
|
|
ops->get_swvstep = tfa9878_get_swvstep;
|
|
ops->dsp_system_stable = tfa9878_dsp_system_stable;
|
|
ops->faim_protect = tfa9878_faim_protect;
|
|
ops->get_mtpb = tfa9878_get_mtpb;
|
|
ops->set_mute = tfa_set_mute_nodsp;
|
|
}
|
|
|