694 lines
18 KiB
C
694 lines
18 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* Copyright (C) 2016 MediaTek Inc.
|
||
|
*/
|
||
|
|
||
|
#include <linux/list.h>
|
||
|
#include <linux/device.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/err.h>
|
||
|
#include <linux/kdev_t.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/skbuff.h>
|
||
|
#include <linux/wait.h>
|
||
|
#include <linux/sched/clock.h> /* local_clock() */
|
||
|
#include <linux/kthread.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/irq.h>
|
||
|
#include <linux/timer.h>
|
||
|
#include <linux/fs.h>
|
||
|
#include <linux/netdevice.h>
|
||
|
#include <linux/random.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/of_fdt.h>
|
||
|
#include <linux/of_irq.h>
|
||
|
#include <linux/of_address.h>
|
||
|
#include "ccci_config.h"
|
||
|
#include "ccci_common_config.h"
|
||
|
#include "ccci_core.h"
|
||
|
#include "ccci_modem.h"
|
||
|
#include "ccci_bm.h"
|
||
|
#include "ccci_platform.h"
|
||
|
|
||
|
#ifdef MODEM_SYS1_CONFIG_FILE
|
||
|
#if (MD_GENERATION <= 6292)
|
||
|
#include <mach/mtk_pbm.h>
|
||
|
#include "ccif_c2k_platform.h"
|
||
|
#include "ccci_hif.h"
|
||
|
#include "hif/ccci_hif_ccif.h"
|
||
|
|
||
|
#define TAG "c2k"
|
||
|
|
||
|
static irqreturn_t md_cd_wdt_isr(int irq, void *data)
|
||
|
{
|
||
|
struct ccci_modem *md = (struct ccci_modem *)data;
|
||
|
|
||
|
/*1. disable MD WDT */
|
||
|
#ifdef ENABLE_MD_WDT_DBG
|
||
|
unsigned int state;
|
||
|
|
||
|
state = ccif_read32(md->md_rgu_base, C2K_WDT_MD_STA);
|
||
|
ccif_write32(md->md_rgu_base, C2K_WDT_MD_MODE, C2K_WDT_MD_MODE_KEY);
|
||
|
CCCI_NORMAL_LOG(md->index, TAG,
|
||
|
"WDT IRQ disabled for debug, state=%X\n", state);
|
||
|
#endif
|
||
|
CCCI_NORMAL_LOG(md->index, TAG, "MD WDT IRQ\n");
|
||
|
ccci_event_log("md%d: MD WDT IRQ\n", md->index);
|
||
|
|
||
|
ccci_fsm_recv_md_interrupt(md->index, MD_IRQ_WDT);
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
static void md_ccif_exception(struct ccci_modem *md, enum HIF_EX_STAGE stage)
|
||
|
{
|
||
|
CCCI_NORMAL_LOG(md->index, TAG, "MD exception HIF %d\n", stage);
|
||
|
switch (stage) {
|
||
|
case HIF_EX_INIT:
|
||
|
/* Rx dispatch does NOT depend on queue index
|
||
|
* in port structure, so it still can find right port.
|
||
|
*/
|
||
|
md_ccif_send(CCIF_HIF_ID, H2D_EXCEPTION_ACK);
|
||
|
break;
|
||
|
case HIF_EX_INIT_DONE:
|
||
|
break;
|
||
|
case HIF_EX_CLEARQ_DONE:
|
||
|
ccci_hif_dump_status(md->hif_flag, DUMP_FLAG_CCIF, NULL, 0);
|
||
|
md_ccif_reset_queue(CCIF_HIF_ID, 0);
|
||
|
md_ccif_send(CCIF_HIF_ID, H2D_EXCEPTION_CLEARQ_ACK);
|
||
|
break;
|
||
|
case HIF_EX_ALLQ_RESET:
|
||
|
md->per_md_data.is_in_ee_dump = 1;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
static int md_ccif_ee_handshake(struct ccci_modem *md, int timeout)
|
||
|
{
|
||
|
/* seems sometime MD send D2H_EXCEPTION_INIT_DONE and
|
||
|
* D2H_EXCEPTION_CLEARQ_DONE together
|
||
|
*/
|
||
|
/* polling_ready(md_ctrl, D2H_EXCEPTION_INIT); */
|
||
|
md_ccif_exception(md, HIF_EX_INIT);
|
||
|
ccif_polling_ready(CCIF_HIF_ID, D2H_EXCEPTION_INIT_DONE);
|
||
|
md_ccif_exception(md, HIF_EX_INIT_DONE);
|
||
|
|
||
|
ccif_polling_ready(CCIF_HIF_ID, D2H_EXCEPTION_CLEARQ_DONE);
|
||
|
md_ccif_exception(md, HIF_EX_CLEARQ_DONE);
|
||
|
|
||
|
ccif_polling_ready(CCIF_HIF_ID, D2H_EXCEPTION_ALLQ_RESET);
|
||
|
md_ccif_exception(md, HIF_EX_ALLQ_RESET);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int md_ccif_op_init(struct ccci_modem *md)
|
||
|
{
|
||
|
CCCI_NORMAL_LOG(md->index, TAG, "CCIF modem is initializing\n");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
/*used for throttling feature - end*/
|
||
|
static int md_ccif_op_start(struct ccci_modem *md)
|
||
|
{
|
||
|
char img_err_str[IMG_ERR_STR_LEN];
|
||
|
struct ccci_modem *md1 = NULL;
|
||
|
int ret = 0;
|
||
|
unsigned int retry_cnt = 0;
|
||
|
struct ccci_per_md *per_md_data = ccci_get_per_md_data(md->index);
|
||
|
|
||
|
/*something do once*/
|
||
|
if (md->per_md_data.config.setting & MD_SETTING_FIRST_BOOT) {
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG, "CCIF modem is first boot\n");
|
||
|
ccci_md_clear_smem(md->index, 1);
|
||
|
md1 = ccci_md_get_modem_by_id(MD_SYS1);
|
||
|
if (md1) {
|
||
|
while (md1->per_md_data.config.setting &
|
||
|
MD_SETTING_FIRST_BOOT) {
|
||
|
msleep(20);
|
||
|
if (retry_cnt++ > 1000) {
|
||
|
CCCI_ERROR_LOG(md->index, TAG,
|
||
|
"wait MD1 start time out\n");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"wait for MD1 starting done\n");
|
||
|
} else
|
||
|
CCCI_ERROR_LOG(md->index, TAG,
|
||
|
"get MD1 modem struct fail\n");
|
||
|
md_ccif_ring_buf_init(CCIF_HIF_ID);
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"modem capability 0x%x\n",
|
||
|
md->per_md_data.md_capability);
|
||
|
md->per_md_data.config.setting &= ~MD_SETTING_FIRST_BOOT;
|
||
|
} else {
|
||
|
ccci_md_clear_smem(md->index, 0);
|
||
|
}
|
||
|
|
||
|
#ifdef FEATURE_BSI_BPI_SRAM_CFG
|
||
|
ccci_set_bsi_bpi_SRAM_cfg(md, 1, MD_FLIGHT_MODE_NONE);
|
||
|
#endif
|
||
|
|
||
|
/*enable ccif clk*/
|
||
|
md->hw_info->plat_ptr->set_clk_cg(md, 1);
|
||
|
/* 0. init security, as security depends on dummy_char,
|
||
|
* which is ready very late.
|
||
|
*/
|
||
|
ccci_init_security();
|
||
|
md_ccif_sram_reset(CCIF_HIF_ID);
|
||
|
md_ccif_reset_queue(CCIF_HIF_ID, 1);
|
||
|
per_md_data->data_usb_bypass = 0;
|
||
|
md->per_md_data.is_in_ee_dump = 0;
|
||
|
md->is_force_asserted = 0;
|
||
|
CCCI_NORMAL_LOG(md->index, TAG, "CCIF modem is starting\n");
|
||
|
/*1. load modem image */
|
||
|
if (!modem_run_env_ready(md->index)) {
|
||
|
if (md->per_md_data.config.setting & MD_SETTING_FIRST_BOOT
|
||
|
|| md->per_md_data.config.setting & MD_SETTING_RELOAD) {
|
||
|
ret =
|
||
|
ccci_load_firmware(md->index,
|
||
|
&md->per_md_data.img_info[IMG_MD],
|
||
|
img_err_str, md->per_md_data.img_post_fix,
|
||
|
&md->plat_dev->dev);
|
||
|
if (ret < 0) {
|
||
|
CCCI_ERROR_LOG(md->index, TAG,
|
||
|
"load firmware fail, %s\n",
|
||
|
img_err_str);
|
||
|
goto out;
|
||
|
}
|
||
|
/*load_std_firmware returns MD image size */
|
||
|
ret = 0;
|
||
|
md->per_md_data.config.setting &= ~MD_SETTING_RELOAD;
|
||
|
}
|
||
|
} else {
|
||
|
CCCI_NORMAL_LOG(md->index, TAG,
|
||
|
"C2K modem image ready, bypass load\n");
|
||
|
ret = ccci_get_md_check_hdr_inf(md->index,
|
||
|
&md->per_md_data.img_info[IMG_MD],
|
||
|
md->per_md_data.img_post_fix);
|
||
|
if (ret < 0) {
|
||
|
CCCI_NORMAL_LOG(md->index, TAG,
|
||
|
"partition read fail(%d)\n", ret);
|
||
|
/*goto out; */
|
||
|
} else
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"partition read success\n");
|
||
|
}
|
||
|
md->per_md_data.config.setting &= ~MD_SETTING_FIRST_BOOT;
|
||
|
|
||
|
/*2. enable MPU */
|
||
|
|
||
|
/*3. power on modem, do NOT touch MD register before this */
|
||
|
ret = md_ccif_power_on(md);
|
||
|
if (ret) {
|
||
|
CCCI_ERROR_LOG(md->index, TAG, "power on MD fail %d\n", ret);
|
||
|
goto out;
|
||
|
}
|
||
|
/*4. update mutex */
|
||
|
atomic_set(&md->reset_on_going, 0);
|
||
|
|
||
|
/*5. let modem go */
|
||
|
md_ccif_let_md_go(md);
|
||
|
enable_irq(md->md_wdt_irq_id);
|
||
|
out:
|
||
|
CCCI_NORMAL_LOG(md->index, TAG, "ccif modem started %d\n", ret);
|
||
|
/*used for throttling feature - start */
|
||
|
/*ccci_modem_boot_count[md->index]++;*/
|
||
|
/*used for throttling feature - end */
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int md_ccif_op_stop(struct ccci_modem *md, unsigned int stop_type)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
CCCI_NORMAL_LOG(md->index, TAG,
|
||
|
"ccif modem is power off, stop_type=%d\n", stop_type);
|
||
|
ret = md_ccif_power_off(md,
|
||
|
stop_type == MD_FLIGHT_MODE_ENTER ? 100 : 0);
|
||
|
CCCI_NORMAL_LOG(md->index, TAG,
|
||
|
"ccif modem is power off done, %d\n", ret);
|
||
|
|
||
|
/*disable ccif clk*/
|
||
|
md->hw_info->plat_ptr->set_clk_cg(md, 0);
|
||
|
|
||
|
#ifdef FEATURE_BSI_BPI_SRAM_CFG
|
||
|
ccci_set_bsi_bpi_SRAM_cfg(md, 0, stop_type);
|
||
|
#endif
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int md_ccif_op_pre_stop(struct ccci_modem *md, unsigned int stop_type)
|
||
|
{
|
||
|
/*1. mutex check */
|
||
|
if (atomic_inc_return(&md->reset_on_going) > 1) {
|
||
|
CCCI_NORMAL_LOG(md->index, TAG, "One reset flow is on-going\n");
|
||
|
return -CCCI_ERR_MD_IN_RESET;
|
||
|
}
|
||
|
|
||
|
CCCI_NORMAL_LOG(md->index, TAG, "ccif modem is resetting\n");
|
||
|
/*2. disable IRQ (use nosync) */
|
||
|
disable_irq_nosync(md->md_wdt_irq_id);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void dump_runtime_data(struct ccci_modem *md,
|
||
|
struct ap_query_md_feature *ap_feature)
|
||
|
{
|
||
|
u8 i = 0;
|
||
|
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"head_pattern 0x%x\n", ap_feature->head_pattern);
|
||
|
|
||
|
for (i = AT_CHANNEL_NUM; i < AP_RUNTIME_FEATURE_ID_MAX; i++) {
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"ap query md feature %u: mask %u, version %u\n",
|
||
|
i, ap_feature->feature_set[i].support_mask,
|
||
|
ap_feature->feature_set[i].version);
|
||
|
}
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"share_memory_support 0x%x\n",
|
||
|
ap_feature->share_memory_support);
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"ap_runtime_data_addr 0x%x\n",
|
||
|
ap_feature->ap_runtime_data_addr);
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"ap_runtime_data_size 0x%x\n",
|
||
|
ap_feature->ap_runtime_data_size);
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"md_runtime_data_addr 0x%x\n",
|
||
|
ap_feature->md_runtime_data_addr);
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"md_runtime_data_size 0x%x\n",
|
||
|
ap_feature->md_runtime_data_size);
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"set_md_mpu_start_addr 0x%x\n",
|
||
|
ap_feature->set_md_mpu_start_addr);
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"set_md_mpu_total_size 0x%x\n",
|
||
|
ap_feature->set_md_mpu_total_size);
|
||
|
CCCI_BOOTUP_LOG(md->index, TAG,
|
||
|
"tail_pattern 0x%x\n",
|
||
|
ap_feature->tail_pattern);
|
||
|
}
|
||
|
|
||
|
static void md_ccif_smem_sub_region_init(struct ccci_modem *md)
|
||
|
{
|
||
|
int __iomem *addr;
|
||
|
int i;
|
||
|
struct ccci_smem_region *dbm =
|
||
|
ccci_md_get_smem_by_user_id(md->index, SMEM_USER_RAW_DBM);
|
||
|
|
||
|
/*Region 0, dbm */
|
||
|
addr = (int __iomem *)dbm->base_ap_view_vir;
|
||
|
addr[0] = 0x44444444; /*Guard pattern 1 header */
|
||
|
addr[1] = 0x44444444; /*Guard pattern 2 header */
|
||
|
#ifdef DISABLE_PBM_FEATURE
|
||
|
for (i = 2; i < (CCCI_SMEM_SIZE_DBM/4 + 2); i++)
|
||
|
addr[i] = 0xFFFFFFFF;
|
||
|
#else
|
||
|
for (i = 2; i < (CCCI_SMEM_SIZE_DBM/4 + 2); i++)
|
||
|
addr[i] = 0x00000000;
|
||
|
#endif
|
||
|
addr[i++] = 0x44444444; /*Guard pattern 1 tail */
|
||
|
addr[i++] = 0x44444444; /*Guard pattern 2 tail */
|
||
|
|
||
|
/*Notify PBM */
|
||
|
#ifndef DISABLE_PBM_FEATURE
|
||
|
init_md_section_level(KR_MD3);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void config_ap_runtime_data(struct ccci_modem *md,
|
||
|
struct ap_query_md_feature *ap_rt_data)
|
||
|
{
|
||
|
struct ccci_feature_support s_info[4];
|
||
|
struct ccci_smem_region *runtime_data =
|
||
|
ccci_md_get_smem_by_user_id(md->index,
|
||
|
SMEM_USER_RAW_RUNTIME_DATA);
|
||
|
struct ccci_smem_region *md2md =
|
||
|
ccci_md_get_smem_by_user_id(md->index,
|
||
|
SMEM_USER_RAW_MD2MD);
|
||
|
|
||
|
/* Notice: ccif_write8 is invalid,
|
||
|
* so must write 4 features at the same time
|
||
|
*/
|
||
|
s_info[0].version = 0; /*AT_CHANNEL_NUM*/
|
||
|
/*CCCI_FEATURE_OPTIONAL_SUPPORT;*/
|
||
|
s_info[0].support_mask = CCCI_FEATURE_OPTIONAL_SUPPORT;
|
||
|
s_info[1].version = 0;
|
||
|
s_info[1].support_mask = 0;
|
||
|
s_info[2].version = 0;
|
||
|
s_info[2].support_mask = 0;
|
||
|
s_info[3].version = 0;
|
||
|
s_info[3].support_mask = 0;
|
||
|
ccif_write32(&ap_rt_data->feature_set[0], 0,
|
||
|
s_info[0].version << 4 | s_info[0].support_mask);
|
||
|
|
||
|
ccif_write32(&ap_rt_data->head_pattern, 0,
|
||
|
AP_FEATURE_QUERY_PATTERN);
|
||
|
|
||
|
ccif_write32(&ap_rt_data->share_memory_support, 0,
|
||
|
INTERNAL_MODEM);
|
||
|
|
||
|
ccif_write32(&ap_rt_data->ap_runtime_data_addr, 0,
|
||
|
runtime_data->base_md_view_phy);
|
||
|
ccif_write32(&ap_rt_data->ap_runtime_data_size, 0,
|
||
|
CCCI_SMEM_SIZE_RUNTIME_AP);
|
||
|
|
||
|
ccif_write32(&ap_rt_data->md_runtime_data_addr, 0,
|
||
|
runtime_data->base_md_view_phy + CCCI_SMEM_SIZE_RUNTIME_AP);
|
||
|
ccif_write32(&ap_rt_data->md_runtime_data_size, 0,
|
||
|
CCCI_SMEM_SIZE_RUNTIME_MD);
|
||
|
|
||
|
ccif_write32(&ap_rt_data->set_md_mpu_start_addr, 0,
|
||
|
md->mem_layout.md_bank4_noncacheable_total.base_md_view_phy
|
||
|
+ md2md->size);
|
||
|
ccif_write32(&ap_rt_data->set_md_mpu_total_size, 0,
|
||
|
md->mem_layout.md_bank4_noncacheable_total.size
|
||
|
- md2md->size);
|
||
|
|
||
|
ccif_write32(&ap_rt_data->tail_pattern, 0,
|
||
|
AP_FEATURE_QUERY_PATTERN);
|
||
|
}
|
||
|
|
||
|
static int md_ccif_op_send_runtime_data(struct ccci_modem *md,
|
||
|
unsigned int tx_ch, unsigned int txqno, int skb_from_pool)
|
||
|
{
|
||
|
int packet_size =
|
||
|
sizeof(struct ap_query_md_feature)
|
||
|
+ sizeof(struct ccci_header);
|
||
|
struct ap_query_md_feature *ap_rt_data = NULL;
|
||
|
int ret;
|
||
|
|
||
|
ap_rt_data =
|
||
|
(struct ap_query_md_feature *)ccif_hif_fill_rt_header(CCIF_HIF_ID,
|
||
|
packet_size, tx_ch, txqno);
|
||
|
|
||
|
config_ap_runtime_data(md, ap_rt_data);
|
||
|
|
||
|
dump_runtime_data(md, ap_rt_data);
|
||
|
|
||
|
md_ccif_smem_sub_region_init(md);
|
||
|
|
||
|
ret = md_ccif_send(CCIF_HIF_ID, H2D_SRAM);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int md_ccif_op_force_assert(struct ccci_modem *md,
|
||
|
enum MD_COMM_TYPE type)
|
||
|
{
|
||
|
CCCI_NORMAL_LOG(md->index, TAG, "force assert MD using %d\n", type);
|
||
|
if (type == CCIF_INTERRUPT)
|
||
|
md_ccif_send(CCIF_HIF_ID, AP_MD_SEQ_ERROR);
|
||
|
return 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
static inline void clear_md1_md3_smem(struct ccci_modem *md)
|
||
|
{
|
||
|
struct ccci_smem_region *region;
|
||
|
|
||
|
CCCI_NORMAL_LOG(md->index, TAG, "%s start\n", __func__);
|
||
|
region = ccci_md_get_smem_by_user_id(md->index, SMEM_USER_RAW_MD2MD);
|
||
|
|
||
|
if (!region) {
|
||
|
CCCI_NORMAL_LOG(md->index, TAG, "%s error\n", __func__);
|
||
|
return;
|
||
|
}
|
||
|
memset_io(region->base_ap_view_vir, 0, region->size);
|
||
|
}
|
||
|
|
||
|
static int md_ccif_op_reset_pccif(struct ccci_modem *md)
|
||
|
{
|
||
|
reset_md1_md3_pccif(md);
|
||
|
clear_md1_md3_smem(md);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int md_ccif_dump_info(struct ccci_modem *md, enum MODEM_DUMP_FLAG flag,
|
||
|
void *buff, int length)
|
||
|
{
|
||
|
/*normal EE */
|
||
|
if (flag & DUMP_FLAG_MD_WDT)
|
||
|
dump_c2k_register(md, 1);
|
||
|
/*MD boot fail EE */
|
||
|
if (flag & DUMP_FLAG_CCIF_REG)
|
||
|
dump_c2k_register(md, 2);
|
||
|
/*runtime data, boot, long time no response EE */
|
||
|
ccci_hif_dump_status(md->hif_flag, flag, NULL, length);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int md_ccif_ee_callback(struct ccci_modem *md, enum MODEM_EE_FLAG flag)
|
||
|
{
|
||
|
if (flag & EE_FLAG_ENABLE_WDT)
|
||
|
enable_irq(md->md_wdt_irq_id);
|
||
|
|
||
|
if (flag & EE_FLAG_DISABLE_WDT)
|
||
|
disable_irq_nosync(md->md_wdt_irq_id);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct ccci_modem_ops md_ccif_ops = {
|
||
|
.init = &md_ccif_op_init,
|
||
|
.start = &md_ccif_op_start,
|
||
|
.stop = &md_ccif_op_stop,
|
||
|
.pre_stop = &md_ccif_op_pre_stop,
|
||
|
.send_runtime_data = &md_ccif_op_send_runtime_data,
|
||
|
.ee_handshake = &md_ccif_ee_handshake,
|
||
|
.force_assert = &md_ccif_op_force_assert,
|
||
|
.dump_info = &md_ccif_dump_info,
|
||
|
.ee_callback = &md_ccif_ee_callback,
|
||
|
.reset_pccif = &md_ccif_op_reset_pccif,
|
||
|
};
|
||
|
|
||
|
static void md_ccif_hw_init(struct ccci_modem *md)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
md_ccif_io_remap_md_side_register(md);
|
||
|
|
||
|
/*request IRQ */
|
||
|
md->md_wdt_irq_id = md->hw_info->md_wdt_irq_id;
|
||
|
ret = request_irq(md->md_wdt_irq_id, md_cd_wdt_isr,
|
||
|
md->md_wdt_irq_flags, "MD2_WDT", md);
|
||
|
if (ret) {
|
||
|
CCCI_ERROR_LOG(md->index, TAG,
|
||
|
"request MD_WDT IRQ(%d) error %d\n",
|
||
|
md->md_wdt_irq_id, ret);
|
||
|
return;
|
||
|
}
|
||
|
/*to balance the first start */
|
||
|
disable_irq_nosync(md->md_wdt_irq_id);
|
||
|
}
|
||
|
|
||
|
static int md_ccif_probe(struct platform_device *dev)
|
||
|
{
|
||
|
struct ccci_modem *md;
|
||
|
int md_id, ret;
|
||
|
struct ccci_dev_cfg dev_cfg;
|
||
|
struct md_hw_info *md_hw;
|
||
|
|
||
|
/*Allocate modem hardware info structure memory */
|
||
|
md_hw = kzalloc(sizeof(struct md_hw_info), GFP_KERNEL);
|
||
|
if (md_hw == NULL) {
|
||
|
CCCI_ERROR_LOG(-1, TAG,
|
||
|
"%s:alloc md hw mem fail\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
ret = md_ccif_get_modem_hw_info(dev, &dev_cfg, md_hw);
|
||
|
if (ret != 0) {
|
||
|
CCCI_ERROR_LOG(-1, TAG,
|
||
|
"%s:get hw info fail(%d)\n", __func__, ret);
|
||
|
kfree(md_hw);
|
||
|
md_hw = NULL;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (!get_modem_is_enabled(dev_cfg.index)) {
|
||
|
CCCI_ERROR_LOG(dev_cfg.index, TAG,
|
||
|
"modem %d not enable\n", dev_cfg.index + 1);
|
||
|
kfree(md_hw);
|
||
|
md_hw = NULL;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*Allocate md ctrl memory and do initialize */
|
||
|
md = ccci_md_alloc(sizeof(struct md_sys3_info));
|
||
|
if (md == NULL) {
|
||
|
CCCI_ERROR_LOG(-1, TAG,
|
||
|
"%s:alloc modem ctrl mem fail\n", __func__);
|
||
|
kfree(md_hw);
|
||
|
md_hw = NULL;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
md->index = md_id = dev_cfg.index;
|
||
|
md->per_md_data.md_capability = dev_cfg.capability;
|
||
|
md->plat_dev = dev;
|
||
|
md->hw_info = md_hw;
|
||
|
|
||
|
CCCI_INIT_LOG(md_id, TAG, "modem ccif module probe...\n");
|
||
|
snprintf(md->trm_wakelock_name, sizeof(md->trm_wakelock_name),
|
||
|
"md%d_ccif_trm", md->index + 1);
|
||
|
md->trm_wake_lock = wakeup_source_register(NULL, md->trm_wakelock_name);
|
||
|
if (!md->trm_wake_lock) {
|
||
|
CCCI_ERROR_LOG(md_id, TAG,
|
||
|
"%s %d: init wakeup source fail",
|
||
|
__func__, __LINE__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*init modem structure */
|
||
|
md->ops = &md_ccif_ops;
|
||
|
CCCI_INIT_LOG(md_id, TAG,
|
||
|
"%s:md_ccif=%p,md_ctrl=%p\n", __func__,
|
||
|
md, md->private_data);
|
||
|
|
||
|
/*register modem */
|
||
|
ccci_md_register(md);
|
||
|
|
||
|
/* init modem private data */
|
||
|
md_ccif_hw_init(md);
|
||
|
|
||
|
md->hif_flag = 1 << CCIF_HIF_ID;
|
||
|
ccci_hif_init(md->index, md->hif_flag);
|
||
|
|
||
|
/*hoop up to device */
|
||
|
dev->dev.platform_data = md;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int md_ccif_remove(struct platform_device *dev)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void md_ccif_shutdown(struct platform_device *dev)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
int md_ccif_suspend(struct platform_device *dev, pm_message_t state)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int md_ccif_resume(struct platform_device *dev)
|
||
|
{
|
||
|
/*
|
||
|
* struct ccci_modem *md = (struct ccci_modem *)dev->dev.platform_data;
|
||
|
* struct md_ccif_ctrl *md_ctrl = (struct md_ccif_ctrl *)md->private_data;
|
||
|
*
|
||
|
* CCCI_DEBUG_LOG(md->index, TAG,
|
||
|
* "md_ccif_resume,md=0x%p,md_ctrl=0x%p\n", md, md_ctrl);
|
||
|
* ccif_write32(md_ctrl->ccif_ap_base, APCCIF_CON, 0x01);
|
||
|
*/
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int md_ccif_pm_suspend(struct device *device)
|
||
|
{
|
||
|
struct platform_device *pdev = to_platform_device(device);
|
||
|
|
||
|
if (pdev == NULL) {
|
||
|
CCCI_ERROR_LOG(MD_SYS3, TAG, "%s pdev == NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
return md_ccif_suspend(pdev, PMSG_SUSPEND);
|
||
|
}
|
||
|
|
||
|
int md_ccif_pm_resume(struct device *device)
|
||
|
{
|
||
|
struct platform_device *pdev = to_platform_device(device);
|
||
|
|
||
|
if (pdev == NULL) {
|
||
|
CCCI_ERROR_LOG(MD_SYS3, TAG, "%s pdev == NULL\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
return md_ccif_resume(pdev);
|
||
|
}
|
||
|
|
||
|
int md_ccif_pm_restore_noirq(struct device *device)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct ccci_modem *md = (struct ccci_modem *)device->platform_data;
|
||
|
|
||
|
CCCI_DEBUG_LOG(md->index, TAG, "%s\n", __func__);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_PM
|
||
|
static const struct dev_pm_ops md_ccif_pm_ops = {
|
||
|
.suspend = md_ccif_pm_suspend,
|
||
|
.resume = md_ccif_pm_resume,
|
||
|
.freeze = md_ccif_pm_suspend,
|
||
|
.thaw = md_ccif_pm_resume,
|
||
|
.poweroff = md_ccif_pm_suspend,
|
||
|
.restore = md_ccif_pm_resume,
|
||
|
.restore_noirq = md_ccif_pm_restore_noirq,
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static struct platform_driver modem_ccif_driver = {
|
||
|
.driver = {
|
||
|
.name = "ccif_modem",
|
||
|
#ifdef CONFIG_PM
|
||
|
.pm = &md_ccif_pm_ops,
|
||
|
#endif
|
||
|
},
|
||
|
.probe = md_ccif_probe,
|
||
|
.remove = md_ccif_remove,
|
||
|
.shutdown = md_ccif_shutdown,
|
||
|
.suspend = md_ccif_suspend,
|
||
|
.resume = md_ccif_resume,
|
||
|
};
|
||
|
|
||
|
#ifdef CONFIG_OF
|
||
|
static const struct of_device_id ccif_of_ids[] = {
|
||
|
/*{.compatible = "mediatek,AP_CCIF1",}, */
|
||
|
{.compatible = "mediatek,ap2c2k_ccif",},
|
||
|
{}
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static int __init md_ccif_init(void)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
#ifdef CONFIG_OF
|
||
|
modem_ccif_driver.driver.of_match_table = ccif_of_ids;
|
||
|
#endif
|
||
|
|
||
|
ret = platform_driver_register(&modem_ccif_driver);
|
||
|
if (ret) {
|
||
|
CCCI_ERROR_LOG(-1, TAG,
|
||
|
"CCIF modem platform driver register fail(%d)\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
CCCI_INIT_LOG(-1, TAG,
|
||
|
"CCIF C2K modem platform driver register success\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
module_init(md_ccif_init);
|
||
|
|
||
|
MODULE_AUTHOR("Yanbin Ren <Yanbin.Ren@mediatek.com>");
|
||
|
MODULE_DESCRIPTION("CCIF modem driver v0.1");
|
||
|
MODULE_LICENSE("GPL");
|
||
|
#endif
|
||
|
#endif
|