lineage_kernel_xcoverpro/drivers/misc/modem_v1/modem_ctrl_ss310ap.c

903 lines
22 KiB
C
Executable File

/*
* Copyright (C) 2010 Samsung Electronics.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 the
* GNU General Public License for more details.
*
*/
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/mcu_ipc.h>
#include <linux/shm_ipc.h>
#include <linux/smc.h>
#include <linux/modem_notifier.h>
#include <soc/samsung/exynos-pmu.h>
#include "modem_prj.h"
#include "modem_utils.h"
#include "link_device_memory.h"
#include "uart_switch.h"
#include <soc/samsung/cal-if.h>
#ifdef CONFIG_EXYNOS_BUSMONITOR
#include <linux/exynos-busmon.h>
#endif
#define MIF_INIT_TIMEOUT (15 * HZ)
#define AP2CP_CFG_DONE BIT(0)
#define AP2CP_CFG_ADDR 0x14000000
#ifdef CONFIG_REGULATOR_S2MPU01A
#include <linux/mfd/samsung/s2mpu01a.h>
static inline int change_cp_pmu_manual_reset(void)
{
return change_mr_reset();
}
#else
static inline int change_cp_pmu_manual_reset(void) {return 0; }
#endif
#ifdef CONFIG_UART_SEL
extern void cp_recheck_uart_dir(void);
#endif
static struct modem_ctl *g_mc;
static irqreturn_t cp_wdt_handler(int irq, void *arg)
{
struct modem_ctl *mc = (struct modem_ctl *)arg;
struct io_device *iod;
enum modem_state new_state;
mif_disable_irq(&mc->irq_cp_wdt);
mif_err("%s: ERR! CP_WDOG occurred\n", mc->name);
if (mc->phone_state == STATE_ONLINE)
modem_notify_event(MODEM_EVENT_WATCHDOG);
/* Disable debug Snapshot */
mif_set_snapshot(false);
#ifdef CONFIG_CP_PMUCAL
cal_cp_reset_req_clear();
#else
exynos_clear_cp_reset();
#endif
new_state = STATE_CRASH_WATCHDOG;
mif_err("new_state = %s\n", cp_state_str(new_state));
list_for_each_entry(iod, &mc->modem_state_notify_list, list)
iod->modem_state_changed(iod, new_state);
return IRQ_HANDLED;
}
static void cp_active_handler(void *arg)
{
struct modem_ctl *mc = (struct modem_ctl *)arg;
struct io_device *iod;
#ifdef CONFIG_CP_PMUCAL
int cp_on = cal_cp_status();
#else
int cp_on = exynos_get_cp_power_status();
#endif
int cp_active = mbox_extract_value(MCU_CP, mc->mbx_cp_status,
mc->sbi_lte_active_mask, mc->sbi_lte_active_pos);
enum modem_state old_state = mc->phone_state;
enum modem_state new_state = mc->phone_state;
mif_err("old_state:%s cp_on:%d cp_active:%d\n",
cp_state_str(old_state), cp_on, cp_active);
if (!cp_active) {
if (cp_on > 0) {
new_state = STATE_OFFLINE;
complete_all(&mc->off_cmpl);
} else {
mif_err("don't care!!!\n");
}
}
if (old_state != new_state) {
mif_err("new_state = %s\n", cp_state_str(new_state));
if (old_state == STATE_ONLINE)
modem_notify_event(MODEM_EVENT_EXIT);
list_for_each_entry(iod, &mc->modem_state_notify_list, list)
iod->modem_state_changed(iod, new_state);
}
}
static int get_system_rev(struct device_node *np)
{
int value, cnt, gpio_cnt;
unsigned int gpio_hw_rev, hw_rev = 0;
gpio_cnt = of_gpio_count(np);
if (gpio_cnt < 0) {
mif_err("failed to get gpio_count from DT(%d)\n", gpio_cnt);
return gpio_cnt;
}
for (cnt = 0; cnt < gpio_cnt; cnt++) {
gpio_hw_rev = of_get_gpio(np, cnt);
if (!gpio_is_valid(gpio_hw_rev)) {
mif_err("gpio_hw_rev%d: Invalied gpio\n", cnt);
return -EINVAL;
}
value = gpio_get_value(gpio_hw_rev);
hw_rev |= (value & 0x1) << cnt;
}
return hw_rev;
}
static int ds_detect = 2;
static ssize_t ds_detect_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", ds_detect);
}
#ifndef CONFIG_GPIO_DS_DETECT
static ssize_t ds_detect_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
int value;
ret = sscanf(buf, "%d", &value);
if (ret != 1 || value > 2 || value < 0) {
mif_err("invalid value:%d with %d\n", value, ret);
return -EINVAL;
}
ds_detect = value;
mif_info("set ds_detect: %d\n", ds_detect);
return count;
}
module_param(ds_detect, int, S_IRUGO | S_IWUSR | S_IWGRP | S_IRGRP);
MODULE_PARM_DESC(ds_detect, "Dual SIM detect");
static DEVICE_ATTR_RW(ds_detect);
#else
static DEVICE_ATTR_RO(ds_detect);
#endif
static struct attribute *sim_attrs[] = {
&dev_attr_ds_detect.attr,
NULL,
};
static const struct attribute_group sim_group = { \
.attrs = sim_attrs, \
.name = "sim",
};
#ifdef CONFIG_GPIO_DS_DETECT
static int get_ds_detect(struct device_node *np)
{
unsigned gpio_ds_det;
gpio_ds_det = of_get_named_gpio(np, "mif,gpio_ds_det", 0);
if (!gpio_is_valid(gpio_ds_det)) {
mif_err("gpio_ds_det: Invalid gpio\n");
return 1;
}
return gpio_get_value(gpio_ds_det);
}
#else
static int get_ds_detect(struct device_node *np)
{
mif_info("Dual SIM detect = %d\n", ds_detect);
return ds_detect - 1;
}
#endif
static int init_mailbox_regs(struct modem_ctl *mc)
{
struct platform_device *pdev = to_platform_device(mc->dev);
struct device_node *np = pdev->dev.of_node;
unsigned int mbx_ap_status;
unsigned int sbi_ds_det_mask, sbi_ds_det_pos;
unsigned int sbi_sys_rev_mask, sbi_sys_rev_pos;
int sys_rev, ds_det, i;
#ifdef CONFIG_CP_RAM_LOGGING
unsigned int sbi_ext_backtrace_mask, sbi_ext_backtrace_pos;
#endif
for (i = 0; i < MAX_MBOX_NUM; i++)
mbox_set_value(MCU_CP, i, 0);
if (np) {
mif_dt_read_u32(np, "mbx_ap2cp_united_status", mbx_ap_status);
mif_dt_read_u32(np, "sbi_sys_rev_mask", sbi_sys_rev_mask);
mif_dt_read_u32(np, "sbi_sys_rev_pos", sbi_sys_rev_pos);
mif_dt_read_u32(np, "sbi_ds_det_mask", sbi_ds_det_mask);
mif_dt_read_u32(np, "sbi_ds_det_pos", sbi_ds_det_pos);
#if 0 /* There is no get_lcd_info() function in SLSI branch */
/*#if defined(CONFIG_SOC_EXYNOS8895) && defined(CONFIG_EXYNOS_DECON_FB)*/
mif_dt_read_u32(np, "sbi_device_type_mask", sbi_device_type_mask);
mif_dt_read_u32(np, "sbi_device_type_pos", sbi_device_type_pos);
dsp_connected = get_lcd_info("connected");
if (dsp_connected < 0) {
mif_err("Failed to get dsp_info(%d)\n", dsp_connected);
value = DEFAULT_DSP_TYPE;
} else {
/* 1: dsp_connect, 0: dsp_disconnect */
if (dsp_connected) {
dsp_type = get_lcd_info("id");
if (dsp_type < 0) {
mif_err("Failed to get dsp_type(%d)\n", dsp_type);
value = DEFAULT_DSP_TYPE;
} else {
/* [3:0] display type, [4] connected or not */
value |= ((dsp_connected & 0x1) << 4);
/* DSP ID1 value is located in [19:16] */
value |= ((dsp_type >> 16) & 0xf);
}
} else {
mif_err("display disconnected, set default value\n");
value = DEFAULT_DSP_TYPE;
}
}
mbox_update_value(MCU_CP, mbx_ap_status, value,
sbi_device_type_mask, sbi_device_type_pos);
mif_info("dsp_type:0x%x, conn:0x%x, get_val: 0x%x\n",
dsp_type, dsp_connected,
mbox_get_value(MCU_CP, mbx_ap_status));
#endif
sys_rev = get_system_rev(np);
if (sys_rev >= 0) {
mbox_update_value(MCU_CP, mbx_ap_status, sys_rev,
sbi_sys_rev_mask, sbi_sys_rev_pos);
} else {
mif_err("get_hw_rev() ERROR\n");
}
ds_det = get_ds_detect(np);
if (ds_det >= 0) {
mbox_update_value(MCU_CP, mbx_ap_status, ds_det,
sbi_ds_det_mask, sbi_ds_det_pos);
} else {
mif_err("get_sim_socket_detection() ERROR\n");
}
mif_err("System Revision %d\n", sys_rev);
mif_err("SIM Socket Detection %d\n", ds_det);
#ifdef CONFIG_CP_RAM_LOGGING
mif_dt_read_u32(np, "sbi_ext_backtrace_mask",
sbi_ext_backtrace_mask);
mif_dt_read_u32(np, "sbi_ext_backtrace_pos",
sbi_ext_backtrace_pos);
mbox_update_value(MCU_CP, mbx_ap_status, shm_get_cplog_flag(),
sbi_ext_backtrace_mask, sbi_ext_backtrace_pos);
#endif
} else {
mif_info("non-DT project, can't set system_rev\n");
}
return 0;
}
static int ss310ap_on(struct modem_ctl *mc)
{
int ret;
int cp_active = mbox_extract_value(MCU_CP, mc->mbx_cp_status,
mc->sbi_lte_active_mask, mc->sbi_lte_active_pos);
int cp_status = mbox_extract_value(MCU_CP, mc->mbx_cp_status,
mc->sbi_cp_status_mask, mc->sbi_cp_status_pos);
mif_err("+++\n");
mif_err("cp_active:%d cp_status:%d\n", cp_active, cp_status);
mc->receive_first_ipc = 0;
#ifndef CONFIG_CP_SECURE_BOOT
exynos_cp_init();
#endif
/* Enable debug Snapshot */
mif_set_snapshot(true);
mc->phone_state = STATE_OFFLINE;
if (init_mailbox_regs(mc))
mif_err("Failed to initialize mbox regs\n");
mbox_update_value(MCU_CP, mc->mbx_ap_status, 1,
mc->sbi_pda_active_mask, mc->sbi_pda_active_pos);
if (IS_ENABLED(CONFIG_SOC_EXYNOS9810) ||
IS_ENABLED(CONFIG_SOC_EXYNOS9110) ||
IS_ENABLED(CONFIG_SOC_EXYNOS9610)) {
void __iomem *AP2CP_CFG;
/* CP_INIT, AP_BAAW setting is executed in el3_mon when coldboot
* If AP2CP_CFG is set, then CP_CPU will work
*/
AP2CP_CFG = devm_ioremap(mc->dev, AP2CP_CFG_ADDR, SZ_64);
if (AP2CP_CFG == NULL) {
mif_err("%s: AP2CP_CFG ioremap failed.\n", __func__);
ret = -EACCES;
return ret;
}
mif_err("__raw_readl(AP2CP_CFG): 0x%08x\n", __raw_readl(AP2CP_CFG));
__raw_writel(AP2CP_CFG_DONE, AP2CP_CFG);
mif_err("__raw_readl(AP2CP_CFG): 0x%08x\n", __raw_readl(AP2CP_CFG));
mif_err("CP_CPU will work right now!!!\n");
devm_iounmap(mc->dev, AP2CP_CFG);
} else {
#ifdef CONFIG_CP_PMUCAL
if (cal_cp_status()) {
mif_err("CP aleady Init, Just reset release!\n");
cal_cp_reset_release();
} else {
mif_err("CP first Init!\n");
cal_cp_init();
}
#else
if (exynos_get_cp_power_status() > 0) {
mif_err("CP aleady Power on, Just start!\n");
exynos_cp_release();
} else {
exynos_set_cp_power_onoff(CP_POWER_ON);
}
#endif
}
msleep(300);
ret = change_cp_pmu_manual_reset();
mif_err("change_mr_reset -> %d\n", ret);
#ifdef CONFIG_UART_SWITCH
mif_err("Recheck UART direction.\n");
cp_recheck_uart_dir();
#endif
mif_info("---\n");
return 0;
}
static int ss310ap_off(struct modem_ctl *mc)
{
mif_err("+++\n");
mbox_set_interrupt(MCU_CP, mc->int_cp_wakeup);
msleep(5);
#ifndef CONFIG_CP_PMUCAL
exynos_set_cp_power_onoff(CP_POWER_OFF);
#endif
mif_err("---\n");
return 0;
}
static int ss310ap_shutdown(struct modem_ctl *mc)
{
struct io_device *iod;
unsigned long timeout = msecs_to_jiffies(3000);
unsigned long remain;
mif_err("+++\n");
#ifdef CONFIG_CP_PMUCAL
if (mc->phone_state == STATE_OFFLINE || cal_cp_status() == 0)
#else
if (mc->phone_state == STATE_OFFLINE
|| exynos_get_cp_power_status() <= 0)
#endif
goto exit;
reinit_completion(&mc->off_cmpl);
remain = wait_for_completion_timeout(&mc->off_cmpl, timeout);
if (remain == 0) {
mif_err("T-I-M-E-O-U-T\n");
mc->phone_state = STATE_OFFLINE;
list_for_each_entry(iod, &mc->modem_state_notify_list, list)
iod->modem_state_changed(iod, STATE_OFFLINE);
}
exit:
mbox_set_interrupt(MCU_CP, mc->int_cp_wakeup);
msleep(5);
#ifndef CONFIG_CP_PMUCAL
exynos_set_cp_power_onoff(CP_POWER_OFF);
#endif
mif_err("---\n");
return 0;
}
static int ss310ap_reset(struct modem_ctl *mc)
{
void __iomem *base = shm_get_ipc_region();
mif_err("+++\n");
mc->receive_first_ipc = 0;
/* mc->phone_state = STATE_OFFLINE; */
if (mc->phone_state == STATE_OFFLINE)
return 0;
/* FIXME: For CP debug */
if (base) {
if (*(unsigned int *)(base + 0xF80) == 0xDEB)
return 0;
}
if (mc->phone_state == STATE_ONLINE)
modem_notify_event(MODEM_EVENT_RESET);
/* Change phone state to OFFLINE */
mc->phone_state = STATE_OFFLINE;
#ifdef CONFIG_CP_PMUCAL
if (cal_cp_status()) {
mif_err("CP aleady Init, try reset\n");
#else
if (exynos_get_cp_power_status() > 0) {
mif_err("CP aleady Power on, try reset\n");
#endif
mbox_set_interrupt(MCU_CP, mc->int_cp_wakeup);
msleep(5);
#ifdef CONFIG_CP_PMUCAL
cal_cp_enable_dump_pc_no_pg();
cal_cp_reset_assert();
#if defined(CONFIG_SOC_EXYNOS9810) || defined(CONFIG_SOC_EXYNOS9610) || defined(CONFIG_SOC_EXYNOS9110)
cal_cp_reset_release();
#endif
#else
exynos_cp_reset();
#endif
mbox_sw_reset(MCU_CP);
}
mif_err("---\n");
return 0;
}
static int ss310ap_boot_on(struct modem_ctl *mc)
{
struct link_device *ld = get_current_link(mc->iod);
struct io_device *iod;
int cnt = 100;
mif_info("+++\n");
if (ld->boot_on)
ld->boot_on(ld, mc->bootd);
list_for_each_entry(iod, &mc->modem_state_notify_list, list)
iod->modem_state_changed(iod, STATE_BOOTING);
/* notify current modem state */
modem_notify_event(MODEM_EVENT_BOOTING);
while (mbox_extract_value(MCU_CP, mc->mbx_cp_status,
mc->sbi_cp_status_mask, mc->sbi_cp_status_pos) == 0) {
if (--cnt > 0)
usleep_range(10000, 20000);
else
return -EACCES;
}
mif_disable_irq(&mc->irq_cp_wdt);
mif_info("cp_status=%u\n",
mbox_extract_value(MCU_CP, mc->mbx_cp_status,
mc->sbi_cp_status_mask, mc->sbi_cp_status_pos));
mif_info("---\n");
return 0;
}
static int ss310ap_boot_off(struct modem_ctl *mc)
{
struct link_device *ld = get_current_link(mc->bootd);
struct io_device *iod;
unsigned long remain;
int err = 0;
mif_info("+++\n");
#ifdef CONFIG_CP_PMUCAL
cal_cp_disable_dump_pc_no_pg();
#endif
reinit_completion(&mc->init_cmpl);
remain = wait_for_completion_timeout(&mc->init_cmpl, MIF_INIT_TIMEOUT);
if (remain == 0) {
mif_err("T-I-M-E-O-U-T\n");
err = -EAGAIN;
goto exit;
}
mif_enable_irq(&mc->irq_cp_wdt);
list_for_each_entry(iod, &mc->modem_state_notify_list, list)
iod->modem_state_changed(iod, STATE_ONLINE);
mif_add_timer(&ld->cplog_timer, (10 * HZ), ld->pr_cplog, (unsigned long)ld);
mif_info("---\n");
exit:
return err;
}
static int ss310ap_boot_done(struct modem_ctl *mc)
{
mif_info("+++\n");
mif_info("---\n");
return 0;
}
static int ss310ap_force_crash_exit(struct modem_ctl *mc)
{
struct link_device *ld = get_current_link(mc->bootd);
mif_err("+++\n");
/* Make DUMP start */
ld->force_dump(ld, mc->bootd);
mif_err("---\n");
return 0;
}
int modem_force_crash_exit_ext(void)
{
if (g_mc) {
mif_err("Make forced crash exit\n");
ss310ap_force_crash_exit(g_mc);
}
return 0;
}
EXPORT_SYMBOL(modem_force_crash_exit_ext);
int modem_send_panic_noti_ext(void)
{
struct modem_data *modem;
if (g_mc) {
modem = g_mc->mdm_data;
if (modem->mld) {
mif_err("Send CMD_KERNEL_PANIC message to CP\n");
send_ipc_irq(modem->mld, cmd2int(CMD_KERNEL_PANIC));
}
}
return 0;
}
EXPORT_SYMBOL(modem_send_panic_noti_ext);
#ifdef CONFIG_CP_UART_NOTI
void send_uart_noti_to_modem(int val)
{
unsigned int __maybe_unused tmp = 0;
if (g_mc) {
mif_err("Send Uart noti to modem(%d) AP : 0 / CP : 1\n", val);
#ifdef CONFIG_PMU_UART_SWITCH
if (val == MODEM_CTRL_UART_CP) {
mif_info("SEL_TXD_GPIO_0 to CP UART TXD\n");
exynos_pmu_update(EXYNOS_PMU_UART_IO_SHARE_CTRL, 0x3 << 24, 1 << 24); /* CP UART TXD */
exynos_pmu_update(EXYNOS_PMU_UART_IO_SHARE_CTRL, 0x3 << 16, 0x0 << 16); /* SEL_RXD_AP_UART => disable */
exynos_pmu_update(EXYNOS_PMU_UART_IO_SHARE_CTRL, 0x3 << 12, 0x2 << 12); /* SEL_RXD_CP_UART => GPIO #0 path */
} else {
mif_info("SEL_TXD_GPIO_0 to AP UART TXD\n");
exynos_pmu_update(EXYNOS_PMU_UART_IO_SHARE_CTRL, 0x3 << 24, 0 << 24); /* AP UART TXD */
exynos_pmu_update(EXYNOS_PMU_UART_IO_SHARE_CTRL, 0x3 << 16, 0x2 << 16); /* SEL_RXD_AP_UART => GPIO #0 path */
exynos_pmu_update(EXYNOS_PMU_UART_IO_SHARE_CTRL, 0x3 << 12, 0x0 << 12); /* SEL_RXD_CP_UART => disable */
}
exynos_pmu_read(EXYNOS_PMU_UART_IO_SHARE_CTRL, &tmp);
mif_info("EXYNOS_PMU_UART_IO_SHARE_CTRL: 0x%08x\n", tmp);
#endif
mbox_update_value(MCU_CP, g_mc->mbx_ap_status, val,
g_mc->sbi_uart_noti_mask, g_mc->sbi_uart_noti_pos);
if (val == MODEM_CTRL_UART_CP)
mbox_set_interrupt(MCU_CP, g_mc->int_uart_noti);
} else {
mif_err("g_mc is NULL!\n");
}
}
EXPORT_SYMBOL(send_uart_noti_to_modem);
#else
void send_uart_noti_to_modem(int val)
{
return;
}
EXPORT_SYMBOL(send_uart_noti_to_modem);
#endif
static int ss310ap_dump_start(struct modem_ctl *mc)
{
int err;
struct link_device *ld = get_current_link(mc->bootd);
int cnt = 100;
mif_err("+++\n");
if (!ld->dump_start) {
mif_err("ERR! %s->dump_start not exist\n", ld->name);
return -EFAULT;
}
/* Change phone state to CRASH_EXIT */
mc->phone_state = STATE_CRASH_EXIT;
err = ld->dump_start(ld, mc->bootd);
if (err)
return err;
if (IS_ENABLED(CONFIG_SOC_EXYNOS9810) ||
IS_ENABLED(CONFIG_SOC_EXYNOS9110) ||
IS_ENABLED(CONFIG_SOC_EXYNOS9610)) {
void __iomem *AP2CP_CFG;
/* CP_INIT, AP_BAAW setting is executed in el3_mon when coldboot
* If AP2CP_CFG is set, then CP_CPU will work
*/
AP2CP_CFG = devm_ioremap(mc->dev, AP2CP_CFG_ADDR, SZ_64);
if (AP2CP_CFG == NULL) {
mif_err("%s: AP2CP_CFG ioremap failed.\n", __func__);
return -EACCES;
}
mif_err("__raw_readl(AP2CP_CFG): 0x%08x\n", __raw_readl(AP2CP_CFG));
__raw_writel(AP2CP_CFG_DONE, AP2CP_CFG);
mif_err("__raw_readl(AP2CP_CFG): 0x%08x\n", __raw_readl(AP2CP_CFG));
mif_err("CP_CPU will work right now!!!\n");
devm_iounmap(mc->dev, AP2CP_CFG);
} else {
#ifdef CONFIG_CP_PMUCAL
cal_cp_reset_release();
#else
exynos_cp_release();
#endif
}
while (mbox_extract_value(MCU_CP, mc->mbx_cp_status,
mc->sbi_cp_status_mask, mc->sbi_cp_status_pos) == 0) {
if(--cnt > 0)
usleep_range(10000, 20000);
else
return -EACCES;
}
mbox_update_value(MCU_CP, mc->mbx_ap_status, 1,
mc->sbi_ap_status_mask, mc->sbi_ap_status_pos);
mif_err("---\n");
return err;
}
static void ss310ap_modem_boot_confirm(struct modem_ctl *mc)
{
mbox_update_value(MCU_CP,mc->mbx_ap_status, 1,
mc->sbi_ap_status_mask, mc->sbi_ap_status_pos);
mif_info("ap_status=%u\n",
mbox_extract_value(MCU_CP, mc->mbx_ap_status,
mc->sbi_ap_status_mask, mc->sbi_ap_status_pos));
}
static void ss310ap_get_ops(struct modem_ctl *mc)
{
mc->ops.modem_on = ss310ap_on;
mc->ops.modem_off = ss310ap_off;
mc->ops.modem_shutdown = ss310ap_shutdown;
mc->ops.modem_reset = ss310ap_reset;
mc->ops.modem_boot_on = ss310ap_boot_on;
mc->ops.modem_boot_off = ss310ap_boot_off;
mc->ops.modem_boot_done = ss310ap_boot_done;
mc->ops.modem_force_crash_exit = ss310ap_force_crash_exit;
mc->ops.modem_dump_start = ss310ap_dump_start;
mc->ops.modem_boot_confirm = ss310ap_modem_boot_confirm;
}
static void ss310ap_get_pdata(struct modem_ctl *mc, struct modem_data *modem)
{
struct modem_mbox *mbx = modem->mbx;
mc->int_pda_active = mbx->int_ap2cp_active;
mc->int_cp_wakeup = mbx->int_ap2cp_wakeup;
mc->irq_phone_active = mbx->irq_cp2ap_active;
mc->mbx_ap_status = mbx->mbx_ap2cp_status;
mc->mbx_cp_status = mbx->mbx_cp2ap_status;
mc->mbx_perf_req = mbx->mbx_cp2ap_perf_req;
mc->irq_perf_req = mbx->irq_cp2ap_perf_req;
mc->int_uart_noti = mbx->int_ap2cp_uart_noti;
mc->sbi_lte_active_mask = mbx->sbi_lte_active_mask;
mc->sbi_lte_active_pos = mbx->sbi_lte_active_pos;
mc->sbi_cp_status_mask = mbx->sbi_cp_status_mask;
mc->sbi_cp_status_pos = mbx->sbi_cp_status_pos;
mc->sbi_pda_active_mask = mbx->sbi_pda_active_mask;
mc->sbi_pda_active_pos = mbx->sbi_pda_active_pos;
mc->sbi_ap_status_mask = mbx->sbi_ap_status_mask;
mc->sbi_ap_status_pos = mbx->sbi_ap_status_pos;
mc->sbi_uart_noti_mask = mbx->sbi_uart_noti_mask;
mc->sbi_uart_noti_pos = mbx->sbi_uart_noti_pos;
}
#ifdef CONFIG_EXYNOS_BUSMONITOR
static int ss310ap_busmon_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct busmon_notifier *info = (struct busmon_notifier *)data;
char *init_desc = info->init_desc;
if (init_desc != NULL &&
(strncmp(init_desc, "CP", strlen(init_desc)) == 0 ||
strncmp(init_desc, "APB_CORE_CP", strlen(init_desc)) == 0 ||
strncmp(init_desc, "MIF_CP", strlen(init_desc)) == 0)) {
struct modem_ctl *mc =
container_of(nb, struct modem_ctl, busmon_nfb);
ss310ap_force_crash_exit(mc);
}
return 0;
}
#endif
/*
int test_pmu_setting(struct modem_ctl *mc)
{
#define PMU_HW_AKQ_ADDR 0x11860080
int ret = 0;
void __iomem *PMU_HW_AKQ;
PMU_HW_AKQ = devm_ioremap(mc->dev, PMU_HW_AKQ_ADDR, SZ_64);
if (PMU_HW_AKQ == NULL) {
mif_err("%s: PMU_HW_AKQ ioremap failed.\n", __func__);
ret = -EACCES;
return ret;
} else {
mif_err("PMU_HW_AKQ : 0x%pK\n", PMU_HW_AKQ);
}
mif_err("__raw_readl(PMU_HW_AKQ): 0x%08x\n", __raw_readl(PMU_HW_AKQ));
__raw_writel(0xC0, PMU_HW_AKQ);
mif_err("__raw_readl(PMU_HW_AKQ): 0x%08x\n", __raw_readl(PMU_HW_AKQ));
mif_err("PMU_HQ_AKQ will work right now!!!\n");
devm_iounmap(mc->dev, PMU_HW_AKQ);
return 0;
}
*/
int ss310ap_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
{
struct platform_device *pdev = to_platform_device(mc->dev);
struct device_node *np = pdev->dev.of_node;
int ret = 0;
unsigned int irq_num;
#ifdef CONFIG_SOC_EXYNOS8890
struct resource *sysram_alive;
#endif
unsigned long flags = IRQF_NO_SUSPEND | IRQF_NO_THREAD;
unsigned int cp_rst_n ;
mif_err("+++\n");
/*test_pmu_setting(mc);*/
g_mc = mc;
ss310ap_get_ops(mc);
ss310ap_get_pdata(mc, pdata);
dev_set_drvdata(mc->dev, mc);
/*
** Register CP_WDT interrupt handler
*/
irq_num = platform_get_irq(pdev, 0);
mif_init_irq(&mc->irq_cp_wdt, irq_num, "cp_wdt", flags);
ret = mif_request_irq(&mc->irq_cp_wdt, cp_wdt_handler, mc);
if (ret) {
mif_err("Failed to request_irq with(%d)", ret);
return ret;
}
/* CP_WDT interrupt must be enabled only after CP booting */
mc->irq_cp_wdt.active = true;
mif_disable_irq(&mc->irq_cp_wdt);
#ifdef CONFIG_SOC_EXYNOS8890
sysram_alive = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mc->sysram_alive = devm_ioremap_resource(&pdev->dev, sysram_alive);
if (IS_ERR(mc->sysram_alive)) {
ret = PTR_ERR(mc->sysram_alive);
return ret;
}
#endif
/*
** Register LTE_ACTIVE MBOX interrupt handler
*/
ret = mbox_request_irq(MCU_CP, mc->irq_phone_active, cp_active_handler, mc);
if (ret) {
mif_err("Failed to mbox_request_irq %u with(%d)",
mc->irq_phone_active, ret);
return ret;
}
init_completion(&mc->init_cmpl);
init_completion(&mc->off_cmpl);
/*
** Get/set CP_RST_N
*/
if (np) {
cp_rst_n = of_get_named_gpio(np, "modem_ctrl,gpio_cp_rst_n", 0);
if (gpio_is_valid(cp_rst_n)) {
mif_err("cp_rst_n: %d\n", cp_rst_n);
ret = gpio_request(cp_rst_n, "CP_RST_N");
if (ret) {
mif_err("fail req gpio %s:%d\n", "CP_RST_N", ret);
return -ENODEV;
}
gpio_direction_output(cp_rst_n, 1);
} else {
mif_err("cp_rst_n: Invalied gpio pins\n");
}
} else {
mif_err("cannot find device_tree for pmu_cu!\n");
return -ENODEV;
}
#ifdef CONFIG_EXYNOS_BUSMONITOR
/*
** Register BUS Mon notifier
*/
mc->busmon_nfb.notifier_call = ss310ap_busmon_notifier;
busmon_notifier_chain_register(&mc->busmon_nfb);
#endif
#ifdef CONFIG_GPIO_DS_DETECT
ret = get_ds_detect(np);
mif_err("read DS_DETECT_GPIO : %d\n", ret);
ds_detect = ret + 1;
#endif
if (sysfs_create_group(&pdev->dev.kobj, &sim_group))
mif_err("failed to create sysfs node related sim\n");
mif_err("---\n");
return 0;
}