Squashed backup commit (no shallow-clone history issues) of xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream commit 9eece662e) plus all fixes applied to get everpal_defconfig building clean against a modern (GCC 15.2.0) toolchain -- see the previous commit message on the lineage-23.2 branch (also in this repo history is not available here since this is a squashed snapshot; full incremental history lives in the local DL380 checkout and the upstream GitHub remote). Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
1957 lines
48 KiB
C
1957 lines
48 KiB
C
/*
|
|
* Copyright (C) 2019 MediaTek Inc.
|
|
*
|
|
* 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.
|
|
*
|
|
* 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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
|
|
*/
|
|
|
|
#include "plat.h"
|
|
|
|
#define MAX_SET_OWN_COUNT 1000
|
|
#define FM_POLLING_LIMIT 100
|
|
|
|
#if CFG_FM_CONNAC2
|
|
static int (*whole_chip_reset)(signed int sta);
|
|
|
|
static int fm_pre_whole_chip_rst(enum consys_drv_type drv, char *reason)
|
|
{
|
|
WCN_DBG(FM_WAR | LINK, "FM pre whole chip rst!\n");
|
|
if (whole_chip_reset)
|
|
whole_chip_reset(1);
|
|
return 0;
|
|
}
|
|
|
|
static int fm_post_whole_chip_rst(void)
|
|
{
|
|
WCN_DBG(FM_WAR | LINK, "FM post whole chip rst!\n");
|
|
if (whole_chip_reset)
|
|
whole_chip_reset(0);
|
|
return 0;
|
|
}
|
|
|
|
static struct sub_drv_ops_cb fm_drv_cbs = {
|
|
.rst_cb.pre_whole_chip_rst = fm_pre_whole_chip_rst,
|
|
.rst_cb.post_whole_chip_rst = fm_post_whole_chip_rst,
|
|
.pre_cal_cb.pwr_on_cb = NULL,
|
|
.pre_cal_cb.do_cal_cb = NULL,
|
|
};
|
|
|
|
static int drv_sys_spi_read(
|
|
struct fm_spi_interface *si, unsigned int subsystem,
|
|
unsigned int addr, unsigned int *data)
|
|
{
|
|
WCN_DBG(FM_DBG | CHIP, "[0x%08x]=[0x%08x]\n", addr, *data);
|
|
return conninfra_spi_read(subsystem, addr, data);
|
|
}
|
|
|
|
static int drv_sys_spi_write(
|
|
struct fm_spi_interface *si, unsigned int subsystem,
|
|
unsigned int addr, unsigned int data)
|
|
{
|
|
WCN_DBG(FM_DBG | CHIP, "[0x%08x]=[0x%08x]\n", addr, data);
|
|
return conninfra_spi_write(subsystem, addr, data);
|
|
}
|
|
#else /* CFG_FM_CONNAC2 */
|
|
static int sys_spi_wait(unsigned int spi_busy)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
unsigned int spi_count, rdata;
|
|
|
|
/* It needs to prevent infinite loop */
|
|
for (spi_count = 0; spi_count < FM_SPI_COUNT_LIMIT; spi_count++) {
|
|
si->spi_read(si, SYS_SPI_STA, &rdata);
|
|
if ((rdata & spi_busy) == 0)
|
|
break;
|
|
}
|
|
if (spi_count == FM_SPI_COUNT_LIMIT) {
|
|
WCN_DBG(FM_WAR | CHIP,
|
|
"SPI busy[0x%08x], retry count reached maximum.\n",
|
|
rdata);
|
|
return FM_SYS_SPI_BUSY;
|
|
}
|
|
return FM_SYS_SPI_OK;
|
|
}
|
|
|
|
static int fm_sys_spi_read(
|
|
struct fm_spi_interface *si, unsigned int subsystem,
|
|
unsigned int addr, unsigned int *data)
|
|
{
|
|
unsigned int spi_busy, spi_addr, spi_mask, spi_wdat, spi_rdat, rdata;
|
|
|
|
if (!si->spi_read || !si->spi_write) {
|
|
WCN_DBG(FM_ERR | CHIP, "spi api is NULL.\n");
|
|
return FM_SYS_SPI_ERR;
|
|
}
|
|
|
|
switch (subsystem) {
|
|
case SYS_SPI_WF:
|
|
spi_busy = 1 << SYS_SPI_STA_WF_BUSY_SHFT;
|
|
spi_addr = SYS_SPI_WF_ADDR_ADDR;
|
|
spi_mask = SYS_SPI_WF_RDAT_MASK;
|
|
spi_wdat = SYS_SPI_WF_WDAT_ADDR;
|
|
spi_rdat = SYS_SPI_WF_RDAT_ADDR;
|
|
addr = SYS_SPI_ADDR_CR_READ | SYS_SPI_ADDR_CR_WF | (addr & SYS_SPI_ADDR_CR_MASK);
|
|
break;
|
|
case SYS_SPI_BT:
|
|
spi_busy = 1 << SYS_SPI_STA_BT_BUSY_SHFT;
|
|
spi_addr = SYS_SPI_BT_ADDR_ADDR;
|
|
spi_mask = SYS_SPI_BT_RDAT_MASK;
|
|
spi_wdat = SYS_SPI_BT_WDAT_ADDR;
|
|
spi_rdat = SYS_SPI_BT_RDAT_ADDR;
|
|
addr = SYS_SPI_ADDR_CR_READ | SYS_SPI_ADDR_CR_BT | (addr & SYS_SPI_ADDR_CR_MASK);
|
|
break;
|
|
case SYS_SPI_FM:
|
|
spi_busy = 1 << SYS_SPI_STA_FM_BUSY_SHFT;
|
|
spi_addr = SYS_SPI_FM_ADDR_ADDR;
|
|
spi_mask = SYS_SPI_FM_RDAT_MASK;
|
|
spi_wdat = SYS_SPI_FM_WDAT_ADDR;
|
|
spi_rdat = SYS_SPI_FM_RDAT_ADDR;
|
|
addr = SYS_SPI_ADDR_CR_READ | SYS_SPI_ADDR_CR_FM | (addr & SYS_SPI_ADDR_CR_MASK);
|
|
break;
|
|
case SYS_SPI_GPS:
|
|
spi_busy = 1 << SYS_SPI_STA_GPS_BUSY_SHFT;
|
|
spi_addr = SYS_SPI_GPS_ADDR_ADDR;
|
|
spi_mask = SYS_SPI_GPS_RDAT_MASK;
|
|
spi_wdat = SYS_SPI_GPS_WDAT_ADDR;
|
|
spi_rdat = SYS_SPI_GPS_RDAT_ADDR;
|
|
addr = SYS_SPI_ADDR_CR_READ | SYS_SPI_ADDR_CR_GPS | (addr & SYS_SPI_ADDR_CR_MASK);
|
|
break;
|
|
case SYS_SPI_TOP:
|
|
spi_busy = 1 << SYS_SPI_STA_TOP_BUSY_SHFT;
|
|
spi_addr = SYS_SPI_TOP_ADDR_ADDR;
|
|
spi_mask = SYS_SPI_TOP_RDAT_MASK;
|
|
spi_wdat = SYS_SPI_TOP_WDAT_ADDR;
|
|
spi_rdat = SYS_SPI_TOP_RDAT_ADDR;
|
|
addr = SYS_SPI_ADDR_CR_READ | SYS_SPI_ADDR_CR_TOP | (addr & SYS_SPI_ADDR_CR_MASK);
|
|
break;
|
|
case SYS_SPI_WF1:
|
|
spi_busy = 1 << SYS_SPI_STA_WF_BUSY_SHFT;
|
|
spi_addr = SYS_SPI_WF_ADDR_ADDR;
|
|
spi_mask = SYS_SPI_WF_RDAT_MASK;
|
|
spi_wdat = SYS_SPI_WF_WDAT_ADDR;
|
|
spi_rdat = SYS_SPI_WF_RDAT_ADDR;
|
|
addr = SYS_SPI_ADDR_CR_READ | SYS_SPI_ADDR_CR_WF1 | (addr & SYS_SPI_ADDR_CR_MASK);
|
|
break;
|
|
default:
|
|
return FM_SYS_SPI_ERR;
|
|
}
|
|
|
|
if (sys_spi_wait(spi_busy) == FM_SYS_SPI_BUSY)
|
|
return FM_SYS_SPI_BUSY;
|
|
|
|
si->spi_write(si, spi_addr, addr);
|
|
si->spi_write(si, spi_wdat, 0);
|
|
|
|
sys_spi_wait(spi_busy);
|
|
|
|
si->spi_read(si, spi_rdat, &rdata);
|
|
*data = rdata & spi_mask;
|
|
|
|
WCN_DBG(FM_DBG | CHIP, "[0x%08x]=[0x%08x]\n", addr, *data);
|
|
|
|
return FM_SYS_SPI_OK;
|
|
}
|
|
|
|
static int fm_sys_spi_write(
|
|
struct fm_spi_interface *si, unsigned int subsystem,
|
|
unsigned int addr, unsigned int data)
|
|
{
|
|
unsigned int spi_busy, spi_addr, spi_mask, spi_wdat;
|
|
|
|
if (!si->spi_read || !si->spi_write) {
|
|
WCN_DBG(FM_ERR | CHIP, "spi api is NULL.\n");
|
|
return FM_SYS_SPI_ERR;
|
|
}
|
|
|
|
switch (subsystem) {
|
|
case SYS_SPI_WF:
|
|
spi_busy = 1 << SYS_SPI_STA_WF_BUSY_SHFT;
|
|
spi_addr = SYS_SPI_WF_ADDR_ADDR;
|
|
spi_mask = SYS_SPI_WF_WDAT_MASK;
|
|
spi_wdat = SYS_SPI_WF_WDAT_ADDR;
|
|
addr = SYS_SPI_ADDR_CR_WRITE | SYS_SPI_ADDR_CR_WF | (addr & SYS_SPI_ADDR_CR_MASK);
|
|
break;
|
|
case SYS_SPI_BT:
|
|
spi_busy = 1 << SYS_SPI_STA_BT_BUSY_SHFT;
|
|
spi_addr = SYS_SPI_BT_ADDR_ADDR;
|
|
spi_mask = SYS_SPI_BT_WDAT_MASK;
|
|
spi_wdat = SYS_SPI_BT_WDAT_ADDR;
|
|
addr = SYS_SPI_ADDR_CR_WRITE | SYS_SPI_ADDR_CR_BT | (addr & SYS_SPI_ADDR_CR_MASK);
|
|
break;
|
|
case SYS_SPI_FM:
|
|
spi_busy = 1 << SYS_SPI_STA_FM_BUSY_SHFT;
|
|
spi_addr = SYS_SPI_FM_ADDR_ADDR;
|
|
spi_mask = SYS_SPI_FM_WDAT_MASK;
|
|
spi_wdat = SYS_SPI_FM_WDAT_ADDR;
|
|
addr = SYS_SPI_ADDR_CR_WRITE | SYS_SPI_ADDR_CR_FM | (addr & SYS_SPI_ADDR_CR_MASK);
|
|
break;
|
|
case SYS_SPI_TOP:
|
|
spi_busy = 1 << SYS_SPI_STA_TOP_BUSY_SHFT;
|
|
spi_addr = SYS_SPI_TOP_ADDR_ADDR;
|
|
spi_mask = SYS_SPI_TOP_WDAT_MASK;
|
|
spi_wdat = SYS_SPI_TOP_WDAT_ADDR;
|
|
addr = SYS_SPI_ADDR_CR_WRITE | SYS_SPI_ADDR_CR_TOP | (addr & SYS_SPI_ADDR_CR_MASK);
|
|
break;
|
|
case SYS_SPI_WF1:
|
|
spi_busy = 1 << SYS_SPI_STA_WF_BUSY_SHFT;
|
|
spi_addr = SYS_SPI_WF_ADDR_ADDR;
|
|
spi_mask = SYS_SPI_WF_WDAT_MASK;
|
|
spi_wdat = SYS_SPI_WF_WDAT_ADDR;
|
|
addr = SYS_SPI_ADDR_CR_WRITE | SYS_SPI_ADDR_CR_WF1 | (addr & SYS_SPI_ADDR_CR_MASK);
|
|
break;
|
|
default:
|
|
return FM_SYS_SPI_ERR;
|
|
}
|
|
|
|
if (sys_spi_wait(spi_busy) == FM_SYS_SPI_BUSY)
|
|
return FM_SYS_SPI_BUSY;
|
|
|
|
si->spi_write(si, spi_addr, addr);
|
|
si->spi_write(si, spi_wdat, (data & spi_mask));
|
|
|
|
sys_spi_wait(spi_busy);
|
|
|
|
WCN_DBG(FM_DBG | CHIP, "[0x%08x]=[0x%08x]\n", addr, data);
|
|
|
|
return FM_SYS_SPI_OK;
|
|
}
|
|
#endif /* CFG_FM_CONNAC2 */
|
|
|
|
static void drv_spi_read(
|
|
struct fm_spi_interface *si, unsigned int addr, unsigned int *val)
|
|
{
|
|
*val = readl(si->info.spi_vir_addr + addr);
|
|
}
|
|
|
|
static void drv_spi_write(
|
|
struct fm_spi_interface *si, unsigned int addr, unsigned int val)
|
|
{
|
|
writel(val, si->info.spi_vir_addr + addr);
|
|
}
|
|
|
|
static void drv_top_read(
|
|
struct fm_spi_interface *si, unsigned int addr, unsigned int *val)
|
|
{
|
|
*val = readl(si->info.top_vir_addr + addr);
|
|
}
|
|
|
|
static void drv_top_write(
|
|
struct fm_spi_interface *si, unsigned int addr, unsigned int val)
|
|
{
|
|
writel(val, si->info.top_vir_addr + addr);
|
|
}
|
|
|
|
static void drv_mcu_read(
|
|
struct fm_spi_interface *si, unsigned int addr, unsigned int *val)
|
|
{
|
|
*val = readl(si->info.mcu_vir_addr + addr);
|
|
}
|
|
|
|
static void drv_mcu_write(
|
|
struct fm_spi_interface *si, unsigned int addr, unsigned int val)
|
|
{
|
|
writel(val, si->info.mcu_vir_addr + addr);
|
|
}
|
|
|
|
static void drv_host_read(
|
|
struct fm_spi_interface *si, unsigned int addr, unsigned int *data)
|
|
{
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
unsigned new_addr = addr;
|
|
|
|
if (addr >= ei->base_addr[CONN_RF_SPI_MST_REG] &&
|
|
addr <= ei->base_addr[CONN_RF_SPI_MST_REG] +
|
|
ei->base_size[CONN_RF_SPI_MST_REG]) {
|
|
new_addr = addr - ei->base_addr[CONN_RF_SPI_MST_REG];
|
|
drv_spi_read(si, new_addr, data);
|
|
} else if (addr >= ei->base_addr[MCU_CFG_CONSYS] &&
|
|
addr <= ei->base_addr[MCU_CFG_CONSYS] +
|
|
ei->base_size[MCU_CFG_CONSYS]) {
|
|
new_addr = addr - ei->base_addr[MCU_CFG_CONSYS];
|
|
drv_mcu_read(si, new_addr, data);
|
|
} else if (addr >= ei->base_addr[CONN_HOST_CSR_TOP] &&
|
|
addr <= ei->base_addr[CONN_HOST_CSR_TOP] +
|
|
ei->base_size[CONN_HOST_CSR_TOP]) {
|
|
new_addr = addr - ei->base_addr[CONN_HOST_CSR_TOP];
|
|
drv_top_read(si, new_addr, data);
|
|
} else {
|
|
WCN_DBG(FM_WAR | CHIP, "not support addr[0x%08x].\n",
|
|
addr);
|
|
return;
|
|
}
|
|
|
|
WCN_DBG(FM_DBG | CHIP, "read [0x%08x]=[0x%08x]\n",
|
|
new_addr, addr, *data);
|
|
}
|
|
|
|
static void drv_host_write(
|
|
struct fm_spi_interface *si, unsigned int addr, unsigned int data)
|
|
{
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
unsigned new_addr = addr;
|
|
|
|
if (addr >= ei->base_addr[CONN_RF_SPI_MST_REG] &&
|
|
addr < ei->base_addr[CONN_RF_SPI_MST_REG] +
|
|
ei->base_size[CONN_RF_SPI_MST_REG]) {
|
|
new_addr = addr - ei->base_addr[CONN_RF_SPI_MST_REG];
|
|
drv_spi_write(si, new_addr, data);
|
|
} else if (addr >= ei->base_addr[MCU_CFG_CONSYS] &&
|
|
addr < ei->base_addr[MCU_CFG_CONSYS] +
|
|
ei->base_size[MCU_CFG_CONSYS]) {
|
|
new_addr = addr - ei->base_addr[MCU_CFG_CONSYS];
|
|
drv_mcu_write(si, new_addr, data);
|
|
} else if (addr >= ei->base_addr[CONN_HOST_CSR_TOP] &&
|
|
addr < ei->base_addr[CONN_HOST_CSR_TOP] +
|
|
ei->base_size[CONN_HOST_CSR_TOP]) {
|
|
new_addr = addr - ei->base_addr[CONN_HOST_CSR_TOP];
|
|
drv_top_write(si, new_addr, data);
|
|
} else {
|
|
WCN_DBG(FM_WAR | CHIP, "not support addr[0x%08x].\n",
|
|
addr);
|
|
return;
|
|
}
|
|
|
|
WCN_DBG(FM_DBG | CHIP, "write [0x%08x/0x%08x]=[0x%08x]\n",
|
|
new_addr, addr, data);
|
|
}
|
|
|
|
/**
|
|
* Send TX data via STP format
|
|
*
|
|
* @param None
|
|
*
|
|
* @return None
|
|
*/
|
|
static void fm_tx(unsigned char *buf, unsigned short length)
|
|
{
|
|
if (FM_LOCK(fm_wcn_ops.tx_lock))
|
|
return;
|
|
|
|
if (length == 0xFFFF) {
|
|
length = (unsigned short)buf[2] +
|
|
(unsigned short)(buf[3] << 8) + FM_HDR_SIZE;
|
|
}
|
|
|
|
memset(fm_wcn_ops.rx_buf, 0, RX_BUF_SIZE);
|
|
memcpy(fm_wcn_ops.rx_buf, buf, length);
|
|
fm_wcn_ops.rx_len = length;
|
|
fm_event_parser(fm_rds_parser);
|
|
|
|
FM_UNLOCK(fm_wcn_ops.tx_lock);
|
|
}
|
|
|
|
static void fm_stc_done_rechandler(void)
|
|
{
|
|
unsigned short i, reg_value, freq, isr_value;
|
|
|
|
fw_spi_read(FM_MAIN_CTRL, &isr_value);
|
|
|
|
WCN_DBG(FM_NTC | CHIP, "isr_value[%04x]\n", isr_value);
|
|
|
|
if (isr_value & FM_MAIN_CTRL_SEEK) {
|
|
unsigned char data[6];
|
|
|
|
fw_spi_read(FM_MAIN_CHANDETSTAT, ®_value);
|
|
|
|
/* freq's unit is 10kHz */
|
|
freq = ((((reg_value & 0x3ff0) >> 4) >> 1) + 640) * 10;
|
|
|
|
data[0] = FM_TASK_EVENT_PKT_TYPE;
|
|
data[1] = FM_SEEK_OPCODE;
|
|
/* payload length */
|
|
data[2] = 2;
|
|
data[3] = 0x00;
|
|
data[4] = (unsigned char)freq & 0xFF;
|
|
data[5] = (unsigned char)(freq >> 8);
|
|
|
|
fm_tx(data, 0xFFFF);
|
|
} else if (isr_value & FM_MAIN_CTRL_SCAN) {
|
|
unsigned char data[36];
|
|
|
|
for (i = 0; i <= SCAN_BUFF_LEN; i++) {
|
|
fw_spi_read(RDS_DATA_REG, ®_value);
|
|
data[4 + (i * 2)] = reg_value & 0xff;
|
|
data[4 + (i * 2) + 1] = (reg_value & 0xff00) >> 8;
|
|
}
|
|
|
|
data[0] = FM_TASK_EVENT_PKT_TYPE;
|
|
data[1] = FM_SCAN_OPCODE;
|
|
/* payload length */
|
|
data[2] = 32;
|
|
data[3] = 0x00;
|
|
|
|
fm_tx(data, 0xFFFF);
|
|
} else if (isr_value & FM_MAIN_CTRL_TUNE) {
|
|
unsigned char data[5];
|
|
|
|
data[0] = FM_TASK_EVENT_PKT_TYPE;
|
|
data[1] = FM_TUNE_OPCODE;
|
|
/* payload length */
|
|
data[2] = 0x01;
|
|
data[3] = 0x00;
|
|
data[4] = 0x01;
|
|
fm_tx(data, 0x0005);
|
|
}
|
|
}
|
|
|
|
static void fm_rds_rechandler(void)
|
|
{
|
|
unsigned short fifo_cnt, reg_value, rds_data[6], reg_sin, reg_cos;
|
|
unsigned short crc = 0, corr_cnt = 0, rds_info;
|
|
unsigned short output_point, temp_data;
|
|
unsigned short i = 0;
|
|
unsigned char data[128];
|
|
|
|
do {
|
|
fw_spi_read(RDS_FIFO_STATUS0, &fifo_cnt);
|
|
fifo_cnt = (fifo_cnt & RDS_DCO_FIFO_OFST) >>
|
|
RDS_DCO_FIFO_OFST_SHFT;
|
|
|
|
/* block A data and info handling */
|
|
fw_spi_read(RDS_INFO_REG, &rds_info);
|
|
crc |= (rds_info & RDS_CRC_INFO) << 3;
|
|
corr_cnt |= ((rds_info & RDS_CRC_CORR_CNT) << 11);
|
|
fw_spi_read(RDS_DATA_REG, &(rds_data[0]));
|
|
|
|
/* block B data and info handling */
|
|
fw_spi_read(RDS_INFO_REG, &rds_info);
|
|
crc |= (rds_info & RDS_CRC_INFO) << 2;
|
|
corr_cnt |= ((rds_info & RDS_CRC_CORR_CNT) << 7);
|
|
fw_spi_read(RDS_DATA_REG, &(rds_data[1]));
|
|
|
|
/* block C data and info handling */
|
|
fw_spi_read(RDS_INFO_REG, &rds_info);
|
|
crc |= (rds_info & RDS_CRC_INFO) << 1;
|
|
corr_cnt |= ((rds_info & RDS_CRC_CORR_CNT) << 3);
|
|
fw_spi_read(RDS_DATA_REG, &(rds_data[2]));
|
|
|
|
/* block D data and info handling */
|
|
fw_spi_read(RDS_INFO_REG, &rds_info);
|
|
crc |= (rds_info & RDS_CRC_INFO);
|
|
corr_cnt |= ((rds_info & RDS_CRC_CORR_CNT) >> 1);
|
|
fw_spi_read(RDS_DATA_REG, &(rds_data[3]));
|
|
|
|
|
|
rds_data[4] = corr_cnt;
|
|
rds_data[5] = crc;
|
|
|
|
/* -1 due to H/W behavior */
|
|
if (fifo_cnt > 0)
|
|
fifo_cnt--;
|
|
|
|
/* RDS recovery start */
|
|
/* check if reading doesn't start at block A */
|
|
fw_spi_read(RDS_POINTER, &output_point);
|
|
while (output_point & 0x3) {
|
|
fw_spi_read(RDS_DATA_REG, &temp_data);
|
|
fw_spi_read(RDS_POINTER, &output_point);
|
|
}
|
|
/* RDS recovery end */
|
|
|
|
fm_set_u16_to_auc(&(data[8 + 12 * i]), rds_data[0]);
|
|
fm_set_u16_to_auc(&(data[10 + 12 * i]), rds_data[1]);
|
|
fm_set_u16_to_auc(&(data[12 + 12 * i]), rds_data[2]);
|
|
fm_set_u16_to_auc(&(data[14 + 12 * i]), rds_data[3]);
|
|
fm_set_u16_to_auc(&(data[16 + 12 * i]), rds_data[4]);
|
|
fm_set_u16_to_auc(&(data[18 + 12 * i]), rds_data[5]);
|
|
|
|
i++;
|
|
} while ((i < 10) && ((fifo_cnt & 0x1F) > 0));
|
|
|
|
if (i > 0) {
|
|
fw_spi_read(RDS_SIN_REG, ®_sin);
|
|
fw_spi_read(RDS_COS_REG, ®_cos);
|
|
|
|
data[0] = FM_TASK_EVENT_PKT_TYPE;
|
|
data[1] = RDS_RX_DATA_OPCODE;
|
|
/* payload length */
|
|
data[2] = (unsigned char)(12 * i + 4);
|
|
data[3] = 0x00;
|
|
fm_set_u16_to_auc(&(data[4]), reg_sin);
|
|
fm_set_u16_to_auc(&(data[6]), reg_cos);
|
|
fm_tx(data, 0xFFFF);
|
|
}
|
|
|
|
fw_spi_read(FM_MAIN_EXTINTRMASK, ®_value);
|
|
reg_value |= (FM_INTR_RDS << 8);
|
|
fw_spi_write(FM_MAIN_EXTINTRMASK, reg_value);
|
|
}
|
|
|
|
static void fm_fifo_rechandler(unsigned short intr)
|
|
{
|
|
unsigned short reg_value;
|
|
|
|
/* Handle channel info. data in FIFO if RDS interrupt
|
|
* and CQI interrupt arise simultaneously
|
|
*/
|
|
if ((intr & 0x0001) && (intr & 0x0020)) {
|
|
unsigned short i;
|
|
unsigned char data[100];
|
|
|
|
for (i = 0; i < FIFO_LEN; i++) {
|
|
fw_spi_read(RDS_DATA_REG, ®_value);
|
|
data[4 + (i * 2)] = reg_value & 0xff;
|
|
data[4 + (i * 2) + 1] = (reg_value & 0xff00) >> 8;
|
|
}
|
|
|
|
data[0] = FM_TASK_EVENT_PKT_TYPE;
|
|
data[1] = FM_SCAN_OPCODE;
|
|
/* payload length */
|
|
data[2] = (unsigned char)(FIFO_LEN<<1);
|
|
data[3] = 0x00;
|
|
|
|
fm_tx(data, 0xFFFF);
|
|
}
|
|
/* Handle RDS data in FIFO, while only RDS interrupt issues */
|
|
else if (intr & 0x0020) {
|
|
|
|
fw_spi_read(FM_MAIN_EXTINTRMASK, ®_value);
|
|
reg_value &= ~(FM_INTR_RDS << 8);
|
|
fw_spi_write(FM_MAIN_EXTINTRMASK, reg_value);
|
|
|
|
fm_rds_rechandler();
|
|
}
|
|
|
|
}
|
|
|
|
static void fm_softmute_tune(unsigned short freq, unsigned char *pos)
|
|
{
|
|
unsigned short rdata;
|
|
unsigned int i = 0, PRX = 0, ATEDV = 0, PR = 0;
|
|
int RSSI = 0, PAMD = 0, FPAMD = 0, MR = 0, ATDC = 0;
|
|
struct fm_full_cqi *p_cqi = (struct fm_full_cqi *)pos;
|
|
|
|
p_cqi->ch = freq;
|
|
|
|
/* soft mute tune */
|
|
fw_bop_modify(FM_MAIN_CG2_CTRL, 0xBFFF, 0x4000);
|
|
/* disable interrupt */
|
|
fw_spi_write(FM_MAIN_INTRMASK, 0x0000);
|
|
fw_spi_write(FM_MAIN_EXTINTRMASK, 0x0000);
|
|
/* ramp down */
|
|
fw_bop_modify(FM_MAIN_CTRL, 0xFFF0, 0x0000);
|
|
/* Set DSP ramp down state */
|
|
fw_bop_modify(FM_MAIN_CTRL, 0xFFFF, 0x0100);
|
|
|
|
fw_bop_rd_until(FM_MAIN_INTR, 0x0001, 0x0001);
|
|
|
|
fw_bop_modify(FM_MAIN_CTRL, 0xFEFF, 0x0000);
|
|
fw_bop_modify(FM_MAIN_INTR, 0xFFFF, 0x0001);
|
|
|
|
/* tune */
|
|
freq = freq / 5 - 1280;
|
|
fw_bop_modify(0x65, 0xFC00, freq);
|
|
fw_bop_modify(FM_MAIN_CTRL, 0xFFFF, 0x0001);
|
|
fw_bop_rd_until(FM_MAIN_INTR, 0x0001, 0x0001);
|
|
fw_bop_modify(FM_MAIN_INTR, 0xFFFF, 0x0001);
|
|
|
|
/* get CQI */
|
|
fw_bop_udelay(9000);
|
|
for (i = 0; i < 8; i++) {
|
|
/* RSSI */
|
|
fw_spi_read(0x6C, &rdata);
|
|
RSSI += ((rdata & 0x3FF) >= 512) ?
|
|
((rdata & 0x3FF) - 1024) : (rdata & 0x3FF);
|
|
|
|
/* PAMD */
|
|
fw_spi_read(0xB4, &rdata);
|
|
PAMD += ((rdata & 0x1FF) >= 256) ?
|
|
((rdata & 0x1FF) - 512) : (rdata & 0x1FF);
|
|
|
|
/* PR */
|
|
fw_spi_read(0xB5, &rdata);
|
|
PR += (rdata & 0x3FF);
|
|
|
|
/* FPAMD */
|
|
fw_spi_read(0xBC, &rdata);
|
|
FPAMD += ((rdata & 0xFFF) >= 2048) ?
|
|
((rdata & 0xFFF) - 4096) : (rdata & 0xFFF);
|
|
|
|
/* MR */
|
|
fw_spi_read(0xBD, &rdata);
|
|
MR += ((rdata & 0x1FF) >= 256) ?
|
|
((rdata & 0x1FF) - 512) : (rdata & 0x1FF);
|
|
|
|
/* ATDC */
|
|
fw_spi_read(0x83, &rdata);
|
|
ATDC += (rdata >= 32768) ? (65536 - rdata) : rdata;
|
|
|
|
/* PRX */
|
|
fw_spi_read(0x84, &rdata);
|
|
PRX += rdata & 0xFF;
|
|
|
|
/* ATDEV */
|
|
fw_spi_read(0x85, &rdata);
|
|
ATEDV += rdata;
|
|
|
|
fw_bop_udelay(2250);
|
|
}
|
|
|
|
RSSI = (RSSI + 4) / 8;
|
|
p_cqi->rssi = RSSI & 0x03FF;
|
|
PAMD = (PAMD + 4) / 8;
|
|
p_cqi->pamd = PAMD & 0x01FF;
|
|
PR = (PR + 4) / 8;
|
|
p_cqi->pr = PR & 0x03FF;
|
|
FPAMD = (FPAMD + 4) / 8;
|
|
p_cqi->fpamd = FPAMD & 0x0FFF;
|
|
MR = (MR + 4) / 8;
|
|
p_cqi->mr = MR & 0x01FF;
|
|
ATDC = (ATDC + 4) / 8;
|
|
p_cqi->atdc = ATDC & 0xFFFF;
|
|
PRX = (PRX + 4) / 8;
|
|
p_cqi->prx = PRX & 0x00FF;
|
|
ATEDV = (ATEDV + 4) / 8;
|
|
p_cqi->atdev = ATEDV & 0xFFFF;
|
|
|
|
/* Soft_mute Gain */
|
|
fw_spi_read(0x86, &rdata);
|
|
p_cqi->smg = rdata;
|
|
|
|
/* delta RSSI */
|
|
fw_spi_read(0x88, &rdata);
|
|
p_cqi->drssi = rdata;
|
|
|
|
/* clear soft mute tune */
|
|
fw_bop_modify(FM_MAIN_CG2_CTRL, 0xBFFF, 0x0000);
|
|
|
|
WCN_DBG(FM_DBG | CHIP,
|
|
"freq %d, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x\n",
|
|
p_cqi->ch, p_cqi->rssi, p_cqi->pamd,
|
|
p_cqi->pr, p_cqi->fpamd, p_cqi->mr,
|
|
p_cqi->atdc, p_cqi->prx, p_cqi->atdev,
|
|
p_cqi->smg, p_cqi->drssi);
|
|
}
|
|
|
|
static void fm_dsp_download(
|
|
unsigned char target, unsigned short length,
|
|
unsigned char total_seg, unsigned char current_seg,
|
|
unsigned char *data)
|
|
{
|
|
unsigned int control_code = 0;
|
|
unsigned short i;
|
|
|
|
switch (target) {
|
|
case DSP_ROM:
|
|
case DSP_PATCH:
|
|
control_code = 0x10;
|
|
break;
|
|
case DSP_COEFF:
|
|
control_code = 0xe;
|
|
break;
|
|
case DSP_HWCOEFF:
|
|
control_code = 0xd;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (current_seg == 0) {
|
|
fw_spi_write(CONTROL_REG, 0);
|
|
/* Start address */
|
|
fw_spi_write(OFFSET_REG, data[1] << 8 | data[0]);
|
|
/* Reset download control */
|
|
fw_spi_write(CONTROL_REG, 0x40);
|
|
/* Set download control */
|
|
fw_spi_write(CONTROL_REG, control_code);
|
|
data += 4;
|
|
length -= 4;
|
|
}
|
|
|
|
if (length > 0) {
|
|
for (i = 0; i < (length >> 1); i++)
|
|
fw_spi_write(DATA_REG, data[2 * i + 1] << 8 | data[2 * i]);
|
|
} else {
|
|
WCN_DBG(FM_ERR | CHIP, "incorrect length[%d].\n", length);
|
|
}
|
|
}
|
|
|
|
static void fm_task_rx_basic_op(
|
|
unsigned char bop, unsigned char length, unsigned char *buf)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
|
|
switch (bop) {
|
|
case FM_WRITE_BASIC_OP:
|
|
fw_spi_write(buf[0], fm_get_u16_from_auc(&buf[1]));
|
|
break;
|
|
|
|
case FM_UDELAY_BASIC_OP:
|
|
fw_bop_udelay(fm_get_u32_from_auc(&buf[0]));
|
|
break;
|
|
|
|
case FM_RD_UNTIL_BASIC_OP:
|
|
fw_bop_rd_until(buf[0], fm_get_u16_from_auc(&buf[1]),
|
|
fm_get_u16_from_auc(&buf[3]));
|
|
break;
|
|
|
|
case FM_MODIFY_BASIC_OP:
|
|
fw_bop_modify(buf[0], fm_get_u16_from_auc(&buf[1]),
|
|
fm_get_u16_from_auc(&buf[3]));
|
|
break;
|
|
|
|
case FM_MSLEEP_BASIC_OP:
|
|
fm_delayms(fm_get_u32_from_auc(&buf[0]));
|
|
break;
|
|
|
|
case FM_WRITE_SPI_BASIC_OP:
|
|
si->sys_spi_write(si, buf[0], fm_get_u16_from_auc(&buf[1]),
|
|
fm_get_u32_from_auc(&buf[3]));
|
|
break;
|
|
case FM_RD_SPI_UNTIL_BASIC_OP:
|
|
fw_bop_spi_rd_until(buf[0], fm_get_u16_from_auc(&buf[1]),
|
|
fm_get_u32_from_auc(&buf[3]),
|
|
fm_get_u32_from_auc(&buf[7]));
|
|
break;
|
|
case FM_MODIFY_SPI_BASIC_OP:
|
|
fw_bop_spi_modify(buf[0], fm_get_u16_from_auc(&buf[1]),
|
|
fm_get_u32_from_auc(&buf[3]),
|
|
fm_get_u32_from_auc(&buf[7]));
|
|
break;
|
|
case FM_COPY_BY_MASK_BASIC_OP:
|
|
fw_bop_copy_by_mask(buf[0], buf[1],
|
|
fm_get_u16_from_auc(&buf[2]));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* FM task rx dispatcher default functions for basic operation processing
|
|
*
|
|
* @param opcode opcode for different packet type
|
|
* @param length the length of parameters
|
|
* @param buf the parameters for different packet type
|
|
*
|
|
* @return None
|
|
*/
|
|
static void fm_task_rx_dispatcher_default(
|
|
unsigned char opcode, unsigned short length, unsigned char *buf)
|
|
{
|
|
int unused_op_size = length;
|
|
unsigned short used_op_size = 0;
|
|
|
|
while (unused_op_size > 0) {
|
|
unsigned char basic_op = buf[used_op_size];
|
|
unsigned char basic_op_length = buf[used_op_size + 1];
|
|
unsigned char *basic_op_buf = &buf[used_op_size + 2];
|
|
|
|
fm_task_rx_basic_op(basic_op, basic_op_length,
|
|
basic_op_buf);
|
|
unused_op_size -= (int)(basic_op_length + 2);
|
|
used_op_size += (unsigned short)(basic_op_length + 2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* FM task rx dispatcher functions for different packet type
|
|
*
|
|
* @param opcode opcode for different packet type
|
|
* @param length the length of parameters
|
|
* @param buf the parameters for different packet type
|
|
*
|
|
* @return None
|
|
*/
|
|
static void fm_task_rx_dispatcher(
|
|
unsigned char opcode, unsigned short length, unsigned char *buf)
|
|
{
|
|
unsigned char event[28];
|
|
|
|
/* Prepare FM event packet, default no payload */
|
|
event[0] = FM_TASK_EVENT_PKT_TYPE;
|
|
event[1] = opcode;
|
|
event[2] = 0x0;
|
|
event[3] = 0x0;
|
|
|
|
switch (opcode) {
|
|
case FM_STP_TEST_OPCODE:
|
|
event[2] = 0x3;
|
|
event[3] = 0x0;
|
|
event[4] = 0x0;
|
|
event[5] = 0x1;
|
|
event[6] = 0x2;
|
|
break;
|
|
case FSPI_ENABLE_OPCODE:
|
|
break;
|
|
case FSPI_MUX_SEL_OPCODE:
|
|
break;
|
|
case FSPI_READ_OPCODE:
|
|
{
|
|
unsigned short data;
|
|
|
|
fw_spi_read(buf[0], &data);
|
|
event[2] = 0x2;
|
|
event[3] = 0x0;
|
|
fm_set_u16_to_auc(&event[4], data);
|
|
break;
|
|
}
|
|
case FSPI_WRITE_OPCODE:
|
|
fw_spi_write(buf[0], fm_get_u16_from_auc(&buf[1]));
|
|
break;
|
|
|
|
case FM_PATCH_DOWNLOAD_OPCODE:
|
|
fm_dsp_download(DSP_PATCH, length - 2, buf[0], buf[1], &buf[2]);
|
|
break;
|
|
|
|
case FM_COEFF_DOWNLOAD_OPCODE:
|
|
fm_dsp_download(DSP_COEFF, length - 2, buf[0], buf[1], &buf[2]);
|
|
break;
|
|
|
|
case FM_HWCOEFF_DOWNLOAD_OPCODE:
|
|
fm_dsp_download(DSP_HWCOEFF, length - 2, buf[0], buf[1], &buf[2]);
|
|
break;
|
|
|
|
case FM_ROM_DOWNLOAD_OPCODE:
|
|
fm_dsp_download(DSP_ROM, length - 2, buf[0], buf[1], &buf[2]);
|
|
break;
|
|
case FM_SOFT_MUTE_TUNE_OPCODE:
|
|
{
|
|
switch (buf[0]) {
|
|
case 1:
|
|
{
|
|
unsigned short freq = 0;
|
|
|
|
fm_set_u16_to_auc(&event[2],
|
|
(FM_SOFTMUTE_TUNE_CQI_SIZE + 2));
|
|
event[4] = FM_SOFTMUTE_TUNE_CQI_SIZE;
|
|
event[5] = 0x01;
|
|
freq = fm_get_u16_from_auc(&buf[1]);
|
|
fm_softmute_tune(freq, &event[6]);
|
|
break;
|
|
}
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
default:
|
|
{
|
|
event[2] = 0x00;
|
|
event[3] = 0x00;
|
|
memcpy(&event[2], buf, 3);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case FM_HOST_READ_OPCODE:
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
unsigned int addr, data;
|
|
|
|
addr = fm_get_u32_from_auc(&buf[0]);
|
|
si->host_read(si, addr, &data);
|
|
event[2] = 0x4;
|
|
event[3] = 0x0;
|
|
fm_set_u32_to_auc(&event[4], data);
|
|
}
|
|
break;
|
|
case FM_HOST_WRITE_OPCODE:
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
unsigned int addr, data;
|
|
|
|
addr = fm_get_u32_from_auc(&buf[0]);
|
|
data = fm_get_u32_from_auc(&buf[4]);
|
|
si->host_write(si, addr, data);
|
|
|
|
break;
|
|
}
|
|
case CSPI_WRITE_OPCODE:
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
|
|
si->sys_spi_write(si, buf[0], fm_get_u16_from_auc(&buf[1]),
|
|
fm_get_u32_from_auc(&buf[3]));
|
|
break;
|
|
}
|
|
case CSPI_READ_OPCODE:
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
unsigned int data;
|
|
unsigned short ret;
|
|
|
|
ret = si->sys_spi_read(
|
|
si, buf[0], fm_get_u16_from_auc(&buf[1]), &data);
|
|
|
|
if (ret != 0)
|
|
data = (unsigned short)ret;
|
|
|
|
event[2] = 0x4;
|
|
event[3] = 0x0;
|
|
fm_set_u32_to_auc(&event[4], data);
|
|
break;
|
|
}
|
|
case FM_HOST_MODIFY_OPCODE:
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
unsigned int addr, data;
|
|
|
|
addr = fm_get_u32_from_auc(&buf[0]);
|
|
si->host_read(si, addr, &data);
|
|
data &= fm_get_u32_from_auc(&buf[4]);
|
|
data |= fm_get_u32_from_auc(&buf[8]);
|
|
si->host_write(si, addr, data);
|
|
break;
|
|
}
|
|
default:
|
|
fm_task_rx_dispatcher_default(opcode, length, buf);
|
|
break;
|
|
}
|
|
fm_tx(event, 0xFFFF);
|
|
}
|
|
|
|
static bool drv_set_own(void)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
unsigned int val, hw_id, tmp1, tmp2, i;
|
|
int ret = 0;
|
|
|
|
ret = FM_LOCK(fm_wcn_ops.own_lock);
|
|
for (i = 0; ret && i < MAX_SET_OWN_COUNT; i++) {
|
|
fm_delayms(2);
|
|
ret = FM_LOCK(fm_wcn_ops.own_lock);
|
|
}
|
|
|
|
/* get lock fail */
|
|
if (i == MAX_SET_OWN_COUNT) {
|
|
WCN_DBG(FM_ERR | CHIP, "get own lock fail[%d]\n", ret);
|
|
return false;
|
|
}
|
|
|
|
/* wakeup conninfra */
|
|
drv_host_write(si, ei->reg_map[CONN_INFRA_WAKEPU_FM], 0x1);
|
|
|
|
/* polling chipid */
|
|
for (i = 0; i < MAX_SET_OWN_COUNT; i++) {
|
|
drv_host_read(si, ei->reg_map[CONN_HW_VER], &val);
|
|
hw_id = val >> 16;
|
|
if (hw_id == ei->conn_id)
|
|
break;
|
|
fm_delayus(5000);
|
|
}
|
|
|
|
if (ei->family_id == 0x6983) {
|
|
/* polling conninfra_ready */
|
|
for (i = 0; i < MAX_SET_OWN_COUNT; i++) {
|
|
drv_host_read(si, ei->reg_map[CONN_INFRA_CFG_PWRCTRL1], &val);
|
|
if ((val & 0x10000) == 0x10000)
|
|
break;
|
|
fm_delayus(5000);
|
|
}
|
|
|
|
if (i == MAX_SET_OWN_COUNT) {
|
|
WCN_DBG(FM_ERR | CHIP,
|
|
"%s: conninfra_ready fail!!! 0x%08x:0x%08x\n",
|
|
__func__,
|
|
ei->reg_map[CONN_INFRA_CFG_PWRCTRL1], val);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* polling fail */
|
|
if (i == MAX_SET_OWN_COUNT) {
|
|
WCN_DBG(FM_ERR | CHIP,
|
|
"%s: invalid hw_id: %d!!!\n", __func__, val);
|
|
|
|
/* unlock if set own fail */
|
|
drv_host_read(si, ei->reg_map[CONN_INFRA_WAKEPU_FM], &tmp1);
|
|
drv_host_read(si, ei->reg_map[OSC_MASK], &tmp2);
|
|
WCN_DBG(FM_ERR | CHIP,
|
|
"polling chip id fail [0x%08x]=[0x%08x], [0x%08x]=[0x%08x]\n",
|
|
ei->reg_map[CONN_INFRA_WAKEPU_FM], tmp1,
|
|
ei->reg_map[OSC_MASK], tmp2);
|
|
FM_UNLOCK(fm_wcn_ops.own_lock);
|
|
return false;
|
|
}
|
|
|
|
if (ei->is_bus_hang && ei->is_bus_hang()) {
|
|
FM_UNLOCK(fm_wcn_ops.own_lock);
|
|
return false;
|
|
}
|
|
|
|
/* conn_infra bus debug function setting */
|
|
/* This is a software workaround for mt6885, mt6891 and mt6893 */
|
|
if (ei->family_id == 0x6885)
|
|
conninfra_config_setup();
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool drv_clr_own(void)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
|
|
drv_host_write(si, ei->reg_map[CONN_INFRA_WAKEPU_FM], 0x0);
|
|
|
|
FM_UNLOCK(fm_wcn_ops.own_lock);
|
|
|
|
return true;
|
|
}
|
|
|
|
static int drv_stp_send_data(unsigned char *buf, unsigned int len)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
|
|
if (len < 4) {
|
|
WCN_DBG(FM_ERR | CHIP, "buf length error[%d].\n", len);
|
|
return -1;
|
|
}
|
|
|
|
if (si->set_own && !si->set_own()) {
|
|
WCN_DBG(FM_ERR | CHIP, "set_own fail\n");
|
|
return -1;
|
|
}
|
|
|
|
fm_task_rx_dispatcher(buf[1], len - 4, buf + 4);
|
|
|
|
if (si->clr_own)
|
|
si->clr_own();
|
|
|
|
WCN_DBG(FM_DBG | CHIP, "buffer: %02x %02x %02x %02x %02x %02x\n",
|
|
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int drv_stp_recv_data(unsigned char *buf, unsigned int len)
|
|
{
|
|
unsigned int length = fm_wcn_ops.rx_len;
|
|
|
|
if (length > len)
|
|
length = len;
|
|
|
|
memcpy(buf, fm_wcn_ops.rx_buf, length);
|
|
|
|
return length;
|
|
}
|
|
|
|
static void drv_host_reg_dump(void)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
unsigned int host_reg[3] = {0};
|
|
|
|
if (si->set_own && !si->set_own()) {
|
|
WCN_DBG(FM_ERR | CHIP, "set_own fail\n");
|
|
return;
|
|
}
|
|
|
|
drv_host_read(si, ei->reg_map[FM_CTRL], &host_reg[0]);
|
|
drv_host_read(si, ei->reg_map[ADIE_CTL], &host_reg[1]);
|
|
drv_host_read(si, ei->reg_map[CONN_TCR_CKMCTL], &host_reg[2]);
|
|
|
|
WCN_DBG(FM_ALT | CHIP,
|
|
"host read 0x%08x:0x%08x, 0x%08x:0x%08x, 0x%08x:0x%08x\n",
|
|
ei->reg_map[FM_CTRL], host_reg[0],
|
|
ei->reg_map[ADIE_CTL], host_reg[1],
|
|
ei->reg_map[CONN_TCR_CKMCTL], host_reg[2]);
|
|
|
|
if (si->clr_own)
|
|
si->clr_own();
|
|
}
|
|
|
|
static int drv_host_pre_on(void)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
unsigned int tem = 0, count = 0;
|
|
int ret = 0;
|
|
|
|
if (si->set_own && !si->set_own()) {
|
|
WCN_DBG(FM_ERR | CHIP, "set_own fail\n");
|
|
return -1;
|
|
}
|
|
|
|
/* enable osc_en to conn_infra_cfg */
|
|
if (ei->family_id == 0x6885 || ei->family_id == 0x6877) {
|
|
drv_host_write(si, ei->reg_map[CONN_TCR_CKMCTL],
|
|
0x00000001);
|
|
} else if (ei->family_id == 0x6983) {
|
|
drv_host_read(si, ei->reg_map[CONN_INFRA_CFG_FM_PWRCTRL0],
|
|
&tem);
|
|
tem |= 0x00000001;
|
|
drv_host_write(si, ei->reg_map[CONN_INFRA_CFG_FM_PWRCTRL0],
|
|
tem);
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
/* polling 26M rdy, max 5 times */
|
|
do {
|
|
fm_delayus(1000);
|
|
drv_host_read(si, ei->reg_map[CONN_INFRA_CFG_RC_STATUS],
|
|
&tem);
|
|
WCN_DBG(FM_NTC | CHIP, "26M_rdy 0x%08x:0x%08x\n",
|
|
ei->reg_map[CONN_INFRA_CFG_RC_STATUS], tem);
|
|
count++;
|
|
} while ((tem & 0x80000) == 0 && count < FM_POLLING_LIMIT);
|
|
|
|
if (count >= FM_POLLING_LIMIT) {
|
|
WCN_DBG(FM_ERR | CHIP, "polling 26M rdy failed\n");
|
|
ret = -1;
|
|
}
|
|
|
|
/* Wholechip FM Power Up: step 1, set common SPI parameter */
|
|
drv_host_write(si, ei->reg_map[FM_CTRL], 0x0000801F);
|
|
|
|
if (si->clr_own)
|
|
si->clr_own();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int drv_host_post_on(void)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
int ret = 0;
|
|
|
|
if (si->set_own && !si->set_own()) {
|
|
WCN_DBG(FM_ERR | CHIP, "set_own fail\n");
|
|
return -1;
|
|
}
|
|
|
|
/* power on FM memory in D-DIE chip */
|
|
/* G2: Release fmsys memory power down*/
|
|
drv_host_write(si, ei->reg_map[FM_CTRL_MEM_SWCTL_PDN], 0x00000000);
|
|
|
|
/* G3: Enable FMAUD trigger, 20170119 */
|
|
drv_host_write(si, ei->reg_map[CONN_TCR_FMAUD_SET], 0x888100C3);
|
|
/* G4: Enable aon_osc_clk_cg */
|
|
drv_host_write(si, ei->reg_map[CONN_TCR_FI2SCK_DIV_CNT],
|
|
0x00000014);
|
|
|
|
if (si->clr_own)
|
|
si->clr_own();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int drv_host_pre_off(void)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
int ret = 0;
|
|
|
|
if (si->set_own && !si->set_own()) {
|
|
WCN_DBG(FM_ERR | CHIP, "set_own fail\n");
|
|
return -1;
|
|
}
|
|
|
|
/* A0.1. Disable aon_osc_clk_cg */
|
|
drv_host_write(si, ei->reg_map[CONN_TCR_FI2SCK_DIV_CNT],
|
|
0x00000004);
|
|
|
|
/* A0.1. Disable FMAUD trigger */
|
|
drv_host_write(si, ei->reg_map[CONN_TCR_FMAUD_SET], 0x88800000);
|
|
|
|
/* A1. power off FM memory in D-DIE chip */
|
|
drv_host_write(si, ei->reg_map[FM_CTRL_MEM_SWCTL_PDN], 0x00000001);
|
|
|
|
if (si->clr_own)
|
|
si->clr_own();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int drv_host_post_off(void)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
unsigned int tem = 0;
|
|
int ret = 0;
|
|
|
|
if (si->set_own && !si->set_own()) {
|
|
WCN_DBG(FM_ERR | CHIP, "set_own fail\n");
|
|
return -1;
|
|
}
|
|
|
|
/* set common spi fm parameter */
|
|
drv_host_read(si, ei->reg_map[FM_CTRL], &tem);
|
|
tem = tem & 0xFFFF0000; /* D15:D0 */
|
|
drv_host_write(si, ei->reg_map[FM_CTRL], tem);
|
|
|
|
/* clear 26M crystal sleep */
|
|
WCN_DBG(FM_DBG | CHIP,
|
|
"Enable 26M crystal sleep, Set 0x%08x[0]=0x0\n",
|
|
ei->reg_map[CONN_TCR_CKMCTL]);
|
|
drv_host_read(si, ei->reg_map[CONN_TCR_CKMCTL], &tem);
|
|
tem = tem & 0xFFFFFFFE;
|
|
drv_host_write(si, ei->reg_map[CONN_TCR_CKMCTL], tem);
|
|
|
|
if (si->clr_own)
|
|
si->clr_own();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int drv_spi_hopping(void)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
int ret = 0, i = 0;
|
|
unsigned int val = 0;
|
|
|
|
if (si->set_own && !si->set_own()) {
|
|
WCN_DBG(FM_ERR | CHIP, "set_own fail\n");
|
|
return -1;
|
|
}
|
|
|
|
if (ei->family_id == 0x6885) {
|
|
/* enable 'rf_spi_div_en' */
|
|
drv_host_read(si, ei->reg_map[CKGEN_BUS], &val);
|
|
drv_host_write(si, ei->reg_map[CKGEN_BUS],
|
|
val | (0x1 << 28));
|
|
|
|
/* lock 64M */
|
|
drv_host_read(si, ei->reg_map[RG_DIG_EN_02], &val);
|
|
drv_host_write(si, ei->reg_map[RG_DIG_EN_02],
|
|
val | (0x1 << 15));
|
|
|
|
/*rd 0x18001810 until D1 == 1*/
|
|
for (i = 0; i < FM_POLLING_LIMIT; i++) {
|
|
drv_host_read(si, ei->reg_map[PLL_STATUS], &val);
|
|
if (val & 0x00000002) {
|
|
WCN_DBG(FM_NTC | CHIP,
|
|
"%s: POLLING PLL_RDY success !\n",
|
|
__func__);
|
|
/* switch SPI clock to 64MHz */
|
|
if (conninfra_spi_clock_switch(
|
|
CONNSYS_SPI_SPEED_64M) == -1) {
|
|
WCN_DBG(FM_ERR | CHIP,
|
|
"conninfra clock switch 64M fail.\n");
|
|
ret = -1;
|
|
}
|
|
break;
|
|
}
|
|
fm_delayus(10);
|
|
}
|
|
} else if (ei->family_id == 0x6877 || ei->family_id == 0x6983) {
|
|
/* TODO: add 0x6983 */
|
|
/* switch SPI clock to 64MHz */
|
|
if (conninfra_spi_clock_switch(CONNSYS_SPI_SPEED_64M)
|
|
== -1) {
|
|
WCN_DBG(FM_ERR | CHIP,
|
|
"conninfra clock switch 64M fail.\n");
|
|
ret = -1;
|
|
}
|
|
} else {
|
|
WCN_DBG(FM_ERR | CHIP,
|
|
"%s: invalid family_id:0x%04x!!!\n",
|
|
__func__, ei->family_id);
|
|
}
|
|
|
|
if (i == FM_POLLING_LIMIT) {
|
|
ret = -1;
|
|
WCN_DBG(FM_ERR | CHIP,
|
|
"%s: Polling to read rd 0x%08x[1]==0x1 failed !\n",
|
|
__func__, ei->reg_map[PLL_STATUS]);
|
|
}
|
|
|
|
if (si->clr_own)
|
|
si->clr_own();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int drv_disable_spi_hopping(void)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
int ret = 0;
|
|
unsigned int val = 0;
|
|
|
|
if (si->set_own && !si->set_own()) {
|
|
WCN_DBG(FM_ERR | CHIP, "set_own fail\n");
|
|
return -1;
|
|
}
|
|
|
|
/* switch SPI clock to 26MHz */
|
|
if (conninfra_spi_clock_switch(CONNSYS_SPI_SPEED_26M) == -1) {
|
|
WCN_DBG(FM_ERR | CHIP,
|
|
"conninfra clock switch 26M fail.\n");
|
|
ret = -1;
|
|
}
|
|
|
|
if (ei->family_id == 0x6885) {
|
|
/* unlock 64M */
|
|
drv_host_read(si, ei->reg_map[RG_DIG_EN_02], &val);
|
|
drv_host_write(si, ei->reg_map[RG_DIG_EN_02],
|
|
val & (~(0x1 << 15)));
|
|
|
|
/* disable 'rf_spi_div_en' */
|
|
drv_host_read(si, ei->reg_map[CKGEN_BUS], &val);
|
|
drv_host_write(si, ei->reg_map[CKGEN_BUS],
|
|
val | (0x1 << 28));
|
|
} else if (ei->family_id == 0x6877 || ei->family_id == 0x6983) {
|
|
/* no need to do anything */
|
|
/* conninfra do the spi hopping control */
|
|
} else {
|
|
WCN_DBG(FM_ERR | CHIP,
|
|
"%s: invalid family_id:0x%04x!!!\n",
|
|
__func__, ei->family_id);
|
|
}
|
|
|
|
if (si->clr_own)
|
|
si->clr_own();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void drv_enable_eint(void)
|
|
{
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
|
|
enable_irq(ei->irq_id);
|
|
}
|
|
|
|
static void drv_disable_eint(void)
|
|
{
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
|
|
disable_irq_nosync(ei->irq_id);
|
|
}
|
|
|
|
static void drv_eint_handler(void)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
unsigned short main_isr;
|
|
|
|
if (si->set_own && !si->set_own()) {
|
|
WCN_DBG(FM_ERR | CHIP, "set_own fail\n");
|
|
return;
|
|
}
|
|
|
|
fw_spi_read(FM_MAIN_INTR, &main_isr);
|
|
main_isr &= FM_INTR_MASK;
|
|
|
|
WCN_DBG(FM_NTC | CHIP, "interrupt[%04x]\n", main_isr);
|
|
|
|
if (main_isr & FM_INTR_STC_DONE)
|
|
fm_stc_done_rechandler();
|
|
|
|
if (main_isr & FM_INTR_RDS)
|
|
fm_fifo_rechandler(main_isr);
|
|
|
|
fw_spi_write(FM_MAIN_INTR, main_isr);
|
|
|
|
if (si->clr_own)
|
|
si->clr_own();
|
|
}
|
|
|
|
static int drv_do_ioremap(void)
|
|
{
|
|
struct fm_spi_interface *interface = &fm_wcn_ops.si;
|
|
struct fm_wcn_reg_info *info = &interface->info;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
|
|
info->spi_phy_addr = ei->base_addr[CONN_RF_SPI_MST_REG];
|
|
info->spi_size = ei->base_size[CONN_RF_SPI_MST_REG];
|
|
request_mem_region(info->spi_phy_addr, info->spi_size, "FM_SPI");
|
|
info->spi_vir_addr = ioremap(
|
|
info->spi_phy_addr, info->spi_size);
|
|
if (info->spi_vir_addr == NULL) {
|
|
WCN_DBG(FM_ERR | CHIP, "[FM_SPI] Cannot remap address.\n");
|
|
return -1;
|
|
} else {
|
|
WCN_DBG(FM_NTC | CHIP, "[FM_SPI] 0x%llx:0x%08x\n",
|
|
info->spi_phy_addr, info->spi_size);
|
|
}
|
|
|
|
info->top_phy_addr = ei->base_addr[CONN_HOST_CSR_TOP];
|
|
info->top_size = ei->base_size[CONN_HOST_CSR_TOP];
|
|
request_mem_region(info->top_phy_addr, info->top_size, "FM_TOP");
|
|
info->top_vir_addr = ioremap(
|
|
info->top_phy_addr, info->top_size);
|
|
if (info->top_vir_addr == NULL) {
|
|
WCN_DBG(FM_ERR | CHIP, "[FM_TOP] Cannot remap address.\n");
|
|
return -1;
|
|
} else {
|
|
WCN_DBG(FM_NTC | CHIP, "[FM_TOP] 0x%llx:0x%08x\n",
|
|
info->top_phy_addr, info->top_size);
|
|
}
|
|
|
|
info->mcu_phy_addr = ei->base_addr[MCU_CFG_CONSYS];
|
|
info->mcu_size = ei->base_size[MCU_CFG_CONSYS];
|
|
request_mem_region(info->mcu_phy_addr, info->mcu_size, "FM_MCU");
|
|
info->mcu_vir_addr = ioremap(
|
|
info->mcu_phy_addr, info->mcu_size);
|
|
if (info->mcu_vir_addr == NULL) {
|
|
WCN_DBG(FM_ERR | CHIP, "[FM_MCU] Cannot remap address.\n");
|
|
return -1;
|
|
} else {
|
|
WCN_DBG(FM_NTC | CHIP, "[FM_MCU] 0x%llx:0x%08x\n",
|
|
info->mcu_phy_addr, info->mcu_size);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int drv_interface_init(void)
|
|
{
|
|
struct fm_spi_interface *interface = &fm_wcn_ops.si;
|
|
|
|
interface->spi_read = drv_spi_read;
|
|
interface->spi_write = drv_spi_write;
|
|
interface->host_read = drv_host_read;
|
|
interface->host_write = drv_host_write;
|
|
#if CFG_FM_CONNAC2
|
|
interface->sys_spi_read = drv_sys_spi_read;
|
|
interface->sys_spi_write = drv_sys_spi_write;
|
|
interface->set_own = drv_set_own;
|
|
interface->clr_own = drv_clr_own;
|
|
#else
|
|
interface->sys_spi_read = fm_sys_spi_read;
|
|
interface->sys_spi_write = fm_sys_spi_write;
|
|
interface->set_own = NULL;
|
|
interface->clr_own = NULL;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int drv_interface_uninit(void)
|
|
{
|
|
struct fm_spi_interface *interface = &fm_wcn_ops.si;
|
|
struct fm_wcn_reg_info *info = &interface->info;
|
|
|
|
if (info->spi_vir_addr) {
|
|
iounmap(info->spi_vir_addr);
|
|
release_mem_region(info->spi_phy_addr, info->spi_size);
|
|
}
|
|
|
|
if (info->top_vir_addr) {
|
|
iounmap(info->top_vir_addr);
|
|
release_mem_region(info->top_phy_addr, info->top_size);
|
|
}
|
|
|
|
if (info->mcu_vir_addr) {
|
|
iounmap(info->mcu_vir_addr);
|
|
release_mem_region(info->mcu_phy_addr, info->mcu_size);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int drv_get_hw_version(void)
|
|
{
|
|
return FM_CONNAC_2_1;
|
|
}
|
|
|
|
static unsigned char drv_get_top_index(void)
|
|
{
|
|
return SYS_SPI_TOP;
|
|
}
|
|
|
|
static unsigned int drv_get_get_adie(void)
|
|
{
|
|
return 0x6635;
|
|
}
|
|
|
|
#if CFG_FM_CONNAC2
|
|
|
|
static int fm_conninfra_stp_register_event_cb(void *cb)
|
|
{
|
|
fm_wcn_ops.ei.eint_cb = (void (*)(void))cb;
|
|
return 0;
|
|
}
|
|
|
|
static int fm_conninfra_msgcb_reg(void *data)
|
|
{
|
|
/* get whole chip reset cb */
|
|
whole_chip_reset = data;
|
|
return conninfra_sub_drv_ops_register(
|
|
CONNDRV_TYPE_FM, &fm_drv_cbs);
|
|
}
|
|
|
|
static int fm_conninfra_set_reg_top_ck_en(unsigned int val)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
int ret = 0;
|
|
int i = 0;
|
|
unsigned int tem = 0;
|
|
int check = 0;
|
|
|
|
/* set 'reg_top_ck_en_4' to initial Power and Clock in A-DIE chip before FM power on */
|
|
drv_host_read(si, ei->reg_map[WB_SLP_TOP_CK_4], &tem);
|
|
tem = ((tem & 0xFFFFFFFE) | val);
|
|
drv_host_write(si, ei->reg_map[WB_SLP_TOP_CK_4], tem);
|
|
|
|
/* polling initial command done */
|
|
for (i = 0; i < FM_POLLING_LIMIT; i++) {
|
|
fm_delayus(5000);
|
|
drv_host_read(si, ei->reg_map[WB_SLP_TOP_CK_4], &tem);
|
|
check = (tem & 0x2) >> 1;
|
|
WCN_DBG(FM_NTC | CHIP, "%s, 0x%08x[1]:%d\n", __func__,
|
|
ei->reg_map[WB_SLP_TOP_CK_4], check);
|
|
if (check == 0x0)
|
|
break;
|
|
}
|
|
|
|
if (i == FM_POLLING_LIMIT)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int fm_conninfra_func_on(void)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
int ret = 0;
|
|
|
|
ret = conninfra_pwr_on(CONNDRV_TYPE_FM);
|
|
if (ret == -1) {
|
|
WCN_DBG(FM_ERR | CHIP, "conninfra power on fail.\n");
|
|
return 0;
|
|
}
|
|
|
|
if (si->set_own && !si->set_own()) {
|
|
WCN_DBG(FM_ERR | CHIP, "set_own fail\n");
|
|
return 0;
|
|
}
|
|
|
|
if (ei->family_id == 0x6885) {
|
|
/* set top_ck_en_adie */
|
|
ret = conninfra_adie_top_ck_en_on(
|
|
CONNSYS_ADIE_CTL_HOST_FM);
|
|
} else if (ei->family_id == 0x6877 || ei->family_id == 0x6983)
|
|
ret = fm_conninfra_set_reg_top_ck_en(0x1);
|
|
else {
|
|
WCN_DBG(FM_ERR | CHIP,
|
|
"%s: invalid family_id:0x%04x!!!\n",
|
|
__func__, ei->family_id);
|
|
}
|
|
|
|
if (si->clr_own)
|
|
si->clr_own();
|
|
|
|
if (ret == -1) {
|
|
WCN_DBG(FM_ERR | CHIP, "top ck en fail.\n");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int fm_conninfra_func_off(void)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
int ret = 0;
|
|
|
|
if (si->set_own && !si->set_own()) {
|
|
WCN_DBG(FM_ERR | CHIP, "set_own fail\n");
|
|
return 0;
|
|
}
|
|
|
|
if (ei->family_id == 0x6885) {
|
|
/* clear top_clk_en_adie */
|
|
ret = conninfra_adie_top_ck_en_off(
|
|
CONNSYS_ADIE_CTL_HOST_FM);
|
|
} else if (ei->family_id == 0x6877 || ei->family_id == 0x6983)
|
|
ret = fm_conninfra_set_reg_top_ck_en(0x0);
|
|
else {
|
|
WCN_DBG(FM_ERR | CHIP,
|
|
"%s: invalid family_id:0x%04x!!!\n",
|
|
__func__, ei->family_id);
|
|
}
|
|
|
|
if (si->clr_own)
|
|
si->clr_own();
|
|
|
|
if (ret == -1) {
|
|
WCN_DBG(FM_ERR | CHIP, "top ck en off fail.\n");
|
|
return 0;
|
|
}
|
|
|
|
ret = conninfra_pwr_off(CONNDRV_TYPE_FM);
|
|
if (ret == -1) {
|
|
WCN_DBG(FM_ERR | CHIP, "conninfra power off fail.\n");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int fm_conninfra_chipid_query(void)
|
|
{
|
|
#ifdef CFG_FM_CHIP_ID
|
|
return CFG_FM_CHIP_ID;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static int fm_conninfra_spi_clock_switch(enum fm_spi_speed speed)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
enum connsys_spi_speed_type sp_type = CONNSYS_SPI_SPEED_26M;
|
|
int ret = 0;
|
|
|
|
if (si->set_own && !si->set_own()) {
|
|
WCN_DBG(FM_ERR | CHIP, "set_own fail\n");
|
|
return -2;
|
|
}
|
|
|
|
switch (speed) {
|
|
case FM_SPI_SPEED_26M:
|
|
sp_type = CONNSYS_SPI_SPEED_26M;
|
|
break;
|
|
case FM_SPI_SPEED_64M:
|
|
sp_type = CONNSYS_SPI_SPEED_64M;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ret = conninfra_spi_clock_switch(sp_type);
|
|
|
|
if (si->clr_own)
|
|
si->clr_own();
|
|
|
|
if (ret == -1) {
|
|
WCN_DBG(FM_ERR | CHIP, "conninfra clock switch fail.\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool fm_conninfra_is_bus_hang(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (conninfra_reg_readable())
|
|
return false;
|
|
|
|
/* Check conninfra bus before accessing BGF's CR */
|
|
ret = conninfra_is_bus_hang();
|
|
if (ret > 0) {
|
|
WCN_DBG(FM_ERR | CHIP, "conninfra bus is hang[0x%x]\n", ret);
|
|
conninfra_trigger_whole_chip_rst(CONNDRV_TYPE_FM, "bus hang");
|
|
return true;
|
|
}
|
|
|
|
WCN_DBG(FM_ERR | CHIP,
|
|
"conninfra not readable, but not bus hang ret = %d", ret);
|
|
|
|
return false;
|
|
}
|
|
|
|
#else /* CFG_FM_CONNAC2 */
|
|
|
|
|
|
static int fm_stp_register_event_cb(void *cb)
|
|
{
|
|
fm_wcn_ops.ei.eint_cb = (void (*)(void))cb;
|
|
return mtk_wcn_stp_register_event_cb(FM_TASK_INDX, cb);
|
|
}
|
|
|
|
static int fm_wmt_msgcb_reg(void *data)
|
|
{
|
|
/* get whole chip reset cb */
|
|
return 0;
|
|
}
|
|
|
|
static int fm_wmt_func_on(void)
|
|
{
|
|
return mtk_wcn_wmt_func_on(WMTDRV_TYPE_FM) != MTK_WCN_BOOL_FALSE;
|
|
}
|
|
|
|
static int fm_wmt_func_off(void)
|
|
{
|
|
return mtk_wcn_wmt_func_off(WMTDRV_TYPE_FM) != MTK_WCN_BOOL_FALSE;
|
|
}
|
|
|
|
static unsigned int fm_wmt_ic_info_get(void)
|
|
{
|
|
return mtk_wcn_wmt_ic_info_get(1);
|
|
}
|
|
|
|
static int fm_wmt_chipid_query(void)
|
|
{
|
|
return mtk_wcn_wmt_chipid_query();
|
|
}
|
|
|
|
static int fm_wmt_spi_clock_switch(enum fm_spi_speed speed)
|
|
{
|
|
struct fm_spi_interface *si = &fm_wcn_ops.si;
|
|
unsigned int reg_val = 0;
|
|
|
|
switch (speed) {
|
|
case FM_SPI_SPEED_26M:
|
|
si->host_read(si, ei->reg_map[SPI_CRTL], ®_val);
|
|
reg_val &= 0xFFFFFFFE;
|
|
si->host_write(si, ei->reg_map[SPI_CTRL], reg_val);
|
|
break;
|
|
case FM_SPI_SPEED_64M:
|
|
si->host_read(si, ei->reg_map[SPI_CTRL], ®_val);
|
|
reg_val |= 0x00000001;
|
|
si->host_write(si, ei->reg_map[SPI_CTRL], reg_val);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CFG_FM_CONNAC2 */
|
|
|
|
static irqreturn_t fm_isr(int irq, void *dev)
|
|
{
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
|
|
if (!ei->eint_cb) {
|
|
WCN_DBG(FM_WAR | CHIP, "fm eint cb is NULL\n");
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
if (ei->disable_eint)
|
|
ei->disable_eint();
|
|
|
|
ei->eint_cb();
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
int fm_register_irq(struct platform_driver *drv, unsigned int irq_num)
|
|
{
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
int ret = 0;
|
|
|
|
ei->drv = drv;
|
|
ei->irq_id = irq_num;
|
|
|
|
if (ei->irq_id != 0) {
|
|
WCN_DBG(FM_NTC | CHIP, "%s irq_num(%d)\n",
|
|
__func__, ei->irq_id);
|
|
ret = request_irq(ei->irq_id, fm_isr, IRQF_SHARED,
|
|
FM_NAME, drv);
|
|
if (ret != 0)
|
|
WCN_DBG(FM_ERR | CHIP, "request_irq ERROR(%d)\n",
|
|
ret);
|
|
} else {
|
|
WCN_DBG(FM_ERR | CHIP, "%s invalid irq_num(%d)\n",
|
|
__func__, ei->irq_id);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int fm_register_plat(unsigned int family_id, unsigned int conn_id)
|
|
{
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
int i = 0;
|
|
|
|
ei->family_id = family_id;
|
|
ei->conn_id = conn_id;
|
|
|
|
for (i = 0; i < FM_REG_MAP_MAX; i++)
|
|
ei->reg_map[i] = FM_REG_NO_USED;
|
|
|
|
if (ei->family_id == 0x6885) {
|
|
/* base address */
|
|
ei->base_addr[CONN_RF_SPI_MST_REG] = 0x18004000;
|
|
ei->base_size[CONN_RF_SPI_MST_REG] = 0x00001000;
|
|
ei->base_addr[CONN_HOST_CSR_TOP] = 0x18060000;
|
|
ei->base_size[CONN_HOST_CSR_TOP] = 0x00001000;
|
|
ei->base_addr[MCU_CFG_CONSYS] = 0x18000000;
|
|
ei->base_size[MCU_CFG_CONSYS] = 0x00010000;
|
|
|
|
/* conn_fm_ctrl */
|
|
ei->reg_map[CONN_TCR_CKMCTL] = 0x18008040;
|
|
ei->reg_map[CONN_TCR_FMAUD_SET] = 0x18008058;
|
|
ei->reg_map[CONN_TCR_FI2SCK_DIV_CNT] = 0x18008064;
|
|
|
|
/* conn_host_csr_top */
|
|
ei->reg_map[CONN_INFRA_WAKEPU_FM] =
|
|
ei->base_addr[CONN_HOST_CSR_TOP] + 0x1B0;
|
|
|
|
/* conn_infra_cfg */
|
|
ei->reg_map[CONN_HW_VER] = 0x18001000;
|
|
ei->reg_map[OSC_MASK] = 0x18001808;
|
|
ei->reg_map[PLL_STATUS] = 0x18001810;
|
|
ei->reg_map[CONN_INFRA_CFG_RC_STATUS] = 0x18001830;
|
|
ei->reg_map[ADIE_CTL] = 0x18001900;
|
|
ei->reg_map[CKGEN_BUS] = 0x18001A00;
|
|
|
|
/* conn_infra_rgu */
|
|
ei->reg_map[FM_CTRL_MEM_SWCTL_PDN] = 0x18000070;
|
|
|
|
/* conn_afe_ctl */
|
|
ei->reg_map[RG_DIG_EN_02] = 0x18003004;
|
|
|
|
/* conn_rf_spi_mst_reg */
|
|
ei->reg_map[SPI_CRTL] =
|
|
ei->base_addr[CONN_RF_SPI_MST_REG] + 0x004;
|
|
ei->reg_map[FM_CTRL] =
|
|
ei->base_addr[CONN_RF_SPI_MST_REG] + 0x00C;
|
|
|
|
} else if (ei->family_id == 0x6877) {
|
|
/* base address */
|
|
ei->base_addr[CONN_RF_SPI_MST_REG] = 0x18004000;
|
|
ei->base_size[CONN_RF_SPI_MST_REG] = 0x00001000;
|
|
ei->base_addr[CONN_HOST_CSR_TOP] = 0x18060000;
|
|
ei->base_size[CONN_HOST_CSR_TOP] = 0x00001000;
|
|
ei->base_addr[MCU_CFG_CONSYS] = 0x18000000;
|
|
ei->base_size[MCU_CFG_CONSYS] = 0x00010000;
|
|
|
|
/* conn_fm_ctrl */
|
|
ei->reg_map[CONN_TCR_CKMCTL] = 0x18008040;
|
|
ei->reg_map[CONN_TCR_FMAUD_SET] = 0x18008058;
|
|
ei->reg_map[CONN_TCR_FI2SCK_DIV_CNT] = 0x18008064;
|
|
|
|
/* conn_host_csr_top */
|
|
ei->reg_map[CONN_INFRA_WAKEPU_FM] =
|
|
ei->base_addr[CONN_HOST_CSR_TOP] + 0x1B0;
|
|
|
|
/* conn_infra_cfg */
|
|
ei->reg_map[CONN_HW_VER] = 0x18001000;
|
|
ei->reg_map[OSC_MASK] = 0x18001808;
|
|
ei->reg_map[CONN_INFRA_CFG_RC_STATUS] = 0x18001384; /*!*/
|
|
ei->reg_map[ADIE_CTL] = 0x18001900;
|
|
|
|
/* conn_infra_rgu */
|
|
ei->reg_map[FM_CTRL_MEM_SWCTL_PDN] = 0x18000098; /*!*/
|
|
|
|
/* conn_wt_slp_ctl_reg */
|
|
ei->reg_map[WB_SLP_TOP_CK_4] = 0x18005130;
|
|
|
|
/* conn_rf_spi_mst_reg */
|
|
ei->reg_map[SPI_CRTL] =
|
|
ei->base_addr[CONN_RF_SPI_MST_REG] + 0x004;
|
|
ei->reg_map[FM_CTRL] =
|
|
ei->base_addr[CONN_RF_SPI_MST_REG] + 0x00C;
|
|
|
|
} else if (ei->family_id == 0x6983) {
|
|
/* base address */
|
|
ei->base_addr[CONN_RF_SPI_MST_REG] = 0x18042000;
|
|
ei->base_size[CONN_RF_SPI_MST_REG] = 0x00001000;
|
|
ei->base_addr[CONN_HOST_CSR_TOP] = 0x18060000;
|
|
ei->base_size[CONN_HOST_CSR_TOP] = 0x00001000;
|
|
ei->base_addr[MCU_CFG_CONSYS] = 0x18000000;
|
|
ei->base_size[MCU_CFG_CONSYS] = 0x00020000;
|
|
|
|
/* conn_fm_ctrl */
|
|
ei->reg_map[CONN_TCR_FMAUD_SET] = 0x18017058; /*!*/
|
|
ei->reg_map[CONN_TCR_FI2SCK_DIV_CNT] = 0x18017064; /*!*/
|
|
|
|
/* conn_host_csr_top */
|
|
ei->reg_map[CONN_INFRA_WAKEPU_FM] =
|
|
ei->base_addr[CONN_HOST_CSR_TOP] + 0x1B0;
|
|
|
|
/* conn_infra_cfg */
|
|
ei->reg_map[CONN_HW_VER] = 0x18011000; /*!*/
|
|
|
|
/* conn_infra_cfg_on */
|
|
ei->reg_map[OSC_MASK] = 0x18001308;
|
|
/* replace CONN_TCR_CKMCTL */
|
|
ei->reg_map[CONN_INFRA_CFG_FM_PWRCTRL0] = 0x18001204; /*!*/
|
|
ei->reg_map[CONN_INFRA_CFG_RC_STATUS] = 0x18001344; /*!*/
|
|
ei->reg_map[ADIE_CTL] = 0x18001010; /*!*/
|
|
ei->reg_map[CONN_INFRA_CFG_PWRCTRL1] = 0x18001210; /*!*/
|
|
|
|
/* conn_infra_rgu */
|
|
ei->reg_map[FM_CTRL_MEM_SWCTL_PDN] = 0x18000098;
|
|
|
|
/* conn_wt_slp_ctl_reg */
|
|
ei->reg_map[WB_SLP_TOP_CK_4] = 0x18003130;
|
|
|
|
/* conn_rf_spi_mst_reg */
|
|
ei->reg_map[SPI_CRTL] =
|
|
ei->base_addr[CONN_RF_SPI_MST_REG] + 0x004;
|
|
ei->reg_map[FM_CTRL] =
|
|
ei->base_addr[CONN_RF_SPI_MST_REG] + 0x00C;
|
|
}
|
|
|
|
return drv_do_ioremap();
|
|
}
|
|
|
|
static void register_drv_ops_init(void)
|
|
{
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
|
|
drv_interface_init();
|
|
ei->eint_handler = drv_eint_handler;
|
|
ei->stp_send_data = drv_stp_send_data;
|
|
ei->stp_recv_data = drv_stp_recv_data;
|
|
ei->get_hw_version = drv_get_hw_version;
|
|
ei->get_top_index = drv_get_top_index;
|
|
ei->get_get_adie = drv_get_get_adie;
|
|
|
|
#if CFG_FM_CONNAC2
|
|
ei->enable_eint = drv_enable_eint;
|
|
ei->disable_eint = drv_disable_eint;
|
|
ei->stp_register_event_cb = fm_conninfra_stp_register_event_cb;
|
|
ei->wmt_msgcb_reg = fm_conninfra_msgcb_reg;
|
|
ei->wmt_func_on = fm_conninfra_func_on;
|
|
ei->wmt_func_off = fm_conninfra_func_off;
|
|
ei->wmt_ic_info_get = NULL;
|
|
ei->wmt_chipid_query = fm_conninfra_chipid_query;
|
|
ei->spi_clock_switch = fm_conninfra_spi_clock_switch;
|
|
ei->is_bus_hang = fm_conninfra_is_bus_hang;
|
|
ei->spi_hopping = drv_spi_hopping;
|
|
ei->disable_spi_hopping = drv_disable_spi_hopping;
|
|
ei->host_reg_dump = drv_host_reg_dump;
|
|
ei->host_pre_on = drv_host_pre_on;
|
|
ei->host_post_on = drv_host_post_on;
|
|
ei->host_pre_off = drv_host_pre_off;
|
|
ei->host_post_off = drv_host_post_off;
|
|
#else
|
|
ei->enable_eint = NULL;
|
|
ei->disable_eint = NULL;
|
|
ei->stp_register_event_cb = fm_stp_register_event_cb;
|
|
ei->wmt_msgcb_reg = fm_wmt_msgcb_reg;
|
|
ei->wmt_func_on = fm_wmt_func_on;
|
|
ei->wmt_func_off = fm_wmt_func_off;
|
|
ei->wmt_ic_info_get = fm_wmt_ic_info_get;
|
|
ei->wmt_chipid_query = fm_wmt_chipid_query;
|
|
ei->spi_clock_switch = fm_wmt_spi_clock_switch;
|
|
ei->is_bus_hang = NULL;
|
|
ei->spi_hopping = NULL;
|
|
ei->disable_spi_hopping = NULL;
|
|
ei->host_reg_dump = NULL;
|
|
ei->host_pre_on = NULL;
|
|
ei->host_post_on = NULL;
|
|
ei->host_pre_off = NULL;
|
|
ei->host_post_off = NULL;
|
|
#endif
|
|
ei->low_ops_register = connac2x_fm_low_ops_register;
|
|
ei->low_ops_unregister = connac2x_fm_low_ops_unregister;
|
|
ei->rds_ops_register = mt6635_fm_rds_ops_register;
|
|
ei->rds_ops_unregister = mt6635_fm_rds_ops_unregister;
|
|
}
|
|
|
|
static void register_drv_ops_uninit(void)
|
|
{
|
|
struct fm_ext_interface *ei = &fm_wcn_ops.ei;
|
|
|
|
if (ei->irq_id)
|
|
free_irq(ei->irq_id, ei->drv);
|
|
drv_interface_uninit();
|
|
fm_memset(&fm_wcn_ops, 0, sizeof(struct fm_wcn_reg_ops));
|
|
}
|
|
|
|
int fm_wcn_ops_register(void)
|
|
{
|
|
register_drv_ops_init();
|
|
return 0;
|
|
}
|
|
|
|
int fm_wcn_ops_unregister(void)
|
|
{
|
|
register_drv_ops_uninit();
|
|
return 0;
|
|
}
|