1762 lines
46 KiB
C
Executable file
1762 lines
46 KiB
C
Executable file
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2010 Samsung Electronics.
|
|
*
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/file.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <soc/samsung/acpm_mfd.h>
|
|
#include <linux/reboot.h>
|
|
#include <linux/suspend.h>
|
|
|
|
#include <linux/exynos-pci-ctrl.h>
|
|
#include <soc/samsung/shm_ipc.h>
|
|
|
|
#include "modem_prj.h"
|
|
#include "modem_utils.h"
|
|
#include "modem_ctrl.h"
|
|
#include "modem_notifier.h"
|
|
#include "link_device.h"
|
|
#include "link_device_memory.h"
|
|
#include "s51xx_pcie.h"
|
|
|
|
#if IS_ENABLED(CONFIG_SUSPEND_DURING_VOICE_CALL)
|
|
#include <sound/samsung/abox.h>
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_LINK_DEVICE_PCIE_S2MPU)
|
|
#include <soc/samsung/exynos-s2mpu.h>
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_LINK_DEVICE_PCIE_IOMMU)
|
|
#include "link_device_pcie_iommu.h"
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_CP_LCD_NOTIFIER)
|
|
#include "../../../video/fbdev/exynos/dpu30/decon.h"
|
|
static int s5100_lcd_notifier(struct notifier_block *notifier,
|
|
unsigned long event, void *v);
|
|
#endif /* CONFIG_CP_LCD_NOTIFIER */
|
|
|
|
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
|
|
|
|
#define RUNTIME_PM_AFFINITY_CORE 2
|
|
|
|
static struct modem_ctl *g_mc;
|
|
|
|
static int s5100_poweroff_pcie(struct modem_ctl *mc, bool force_off);
|
|
|
|
static int s5100_reboot_handler(struct notifier_block *nb,
|
|
unsigned long l, void *p)
|
|
{
|
|
struct modem_ctl *mc = container_of(nb, struct modem_ctl, reboot_nb);
|
|
|
|
mif_info("Now is device rebooting..\n");
|
|
|
|
mutex_lock(&mc->pcie_check_lock);
|
|
mc->device_reboot = true;
|
|
mutex_unlock(&mc->pcie_check_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void print_mc_state(struct modem_ctl *mc)
|
|
{
|
|
int pwr = mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_AP2CP_CP_PWR], false);
|
|
int reset = mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_AP2CP_NRESET], false);
|
|
int pshold = mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_PS_HOLD], false);
|
|
|
|
int ap_wakeup = mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_WAKEUP], false);
|
|
int cp_wakeup = mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_AP2CP_WAKEUP], false);
|
|
|
|
int dump = mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_AP2CP_DUMP_NOTI], false);
|
|
int ap_status = mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], false);
|
|
int phone_active = mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_CP_ACTIVE], false);
|
|
|
|
mif_info("%s: %ps:GPIO pwr:%d rst:%d phd:%d c2aw:%d a2cw:%d dmp:%d ap_act:%d cp_act:%d\n",
|
|
mc->name, CALLER, pwr, reset, pshold, ap_wakeup, cp_wakeup, dump,
|
|
ap_status, phone_active);
|
|
}
|
|
|
|
static void pcie_clean_dislink(struct modem_ctl *mc)
|
|
{
|
|
#if IS_ENABLED(CONFIG_SUSPEND_DURING_VOICE_CALL)
|
|
if (mc->pcie_voice_call_on) {
|
|
modem_notify_event(MODEM_EVENT_RESET, mc);
|
|
mc->pcie_voice_call_on = false;
|
|
} else {
|
|
modem_notify_event(MODEM_EVENT_OFFLINE, mc);
|
|
}
|
|
#endif
|
|
|
|
if (mc->pcie_powered_on)
|
|
s5100_poweroff_pcie(mc, true);
|
|
|
|
if (!mc->pcie_powered_on)
|
|
mif_err("Link is disconnected!!!\n");
|
|
}
|
|
|
|
static void cp2ap_wakeup_work(struct work_struct *work)
|
|
{
|
|
struct modem_ctl *mc = container_of(work, struct modem_ctl, wakeup_work);
|
|
|
|
if (mc->phone_state == STATE_CRASH_EXIT)
|
|
return;
|
|
|
|
s5100_poweron_pcie(mc, false);
|
|
}
|
|
|
|
static void cp2ap_suspend_work(struct work_struct *work)
|
|
{
|
|
struct modem_ctl *mc = container_of(work, struct modem_ctl, suspend_work);
|
|
|
|
if (mc->phone_state == STATE_CRASH_EXIT)
|
|
return;
|
|
|
|
s5100_poweroff_pcie(mc, false);
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_SUSPEND_DURING_VOICE_CALL)
|
|
static void voice_call_on_work(struct work_struct *work)
|
|
{
|
|
struct modem_ctl *mc = container_of(work, struct modem_ctl, call_on_work);
|
|
|
|
mutex_lock(&mc->pcie_check_lock);
|
|
if (!mc->pcie_voice_call_on)
|
|
goto exit;
|
|
|
|
if (mc->pcie_powered_on &&
|
|
(s51xx_check_pcie_link_status(mc->pcie_ch_num) != 0)) {
|
|
if (cpif_wake_lock_active(mc->ws))
|
|
cpif_wake_unlock(mc->ws);
|
|
}
|
|
|
|
modem_voice_call_notify_event(MODEM_VOICE_CALL_ON);
|
|
|
|
exit:
|
|
mif_info("wakelock active = %d, voice status = %d\n",
|
|
cpif_wake_lock_active(mc->ws), mc->pcie_voice_call_on);
|
|
mutex_unlock(&mc->pcie_check_lock);
|
|
}
|
|
|
|
static void voice_call_off_work(struct work_struct *work)
|
|
{
|
|
struct modem_ctl *mc = container_of(work, struct modem_ctl, call_off_work);
|
|
|
|
mutex_lock(&mc->pcie_check_lock);
|
|
if (mc->pcie_voice_call_on)
|
|
goto exit;
|
|
|
|
if (mc->pcie_powered_on &&
|
|
(s51xx_check_pcie_link_status(mc->pcie_ch_num) != 0)) {
|
|
if (!cpif_wake_lock_active(mc->ws))
|
|
cpif_wake_lock(mc->ws);
|
|
}
|
|
|
|
modem_voice_call_notify_event(MODEM_VOICE_CALL_OFF);
|
|
|
|
exit:
|
|
mif_info("wakelock active = %d, voice status = %d\n",
|
|
cpif_wake_lock_active(mc->ws), mc->pcie_voice_call_on);
|
|
mutex_unlock(&mc->pcie_check_lock);
|
|
}
|
|
#endif
|
|
|
|
/* It means initial GPIO level. */
|
|
static int check_link_order = 1;
|
|
static irqreturn_t ap_wakeup_handler(int irq, void *data)
|
|
{
|
|
struct modem_ctl *mc = (struct modem_ctl *)data;
|
|
int gpio_val = mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_WAKEUP], true);
|
|
unsigned long flags;
|
|
|
|
mif_disable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP]);
|
|
|
|
if (mc->device_reboot) {
|
|
mif_err("skip : device is rebooting..!!!\n");
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
if (gpio_val == check_link_order)
|
|
mif_err("cp2ap_wakeup val is the same with before : %d\n", gpio_val);
|
|
check_link_order = gpio_val;
|
|
|
|
spin_lock_irqsave(&mc->pcie_pm_lock, flags);
|
|
if (mc->pcie_pm_suspended) {
|
|
if (gpio_val == 1) {
|
|
/* try to block system suspend */
|
|
if (!cpif_wake_lock_active(mc->ws))
|
|
cpif_wake_lock(mc->ws);
|
|
}
|
|
|
|
mif_err("cp2ap_wakeup work pending. gpio_val : %d\n", gpio_val);
|
|
mc->pcie_pm_resume_wait = true;
|
|
mc->pcie_pm_resume_gpio_val = gpio_val;
|
|
|
|
spin_unlock_irqrestore(&mc->pcie_pm_lock, flags);
|
|
return IRQ_HANDLED;
|
|
}
|
|
spin_unlock_irqrestore(&mc->pcie_pm_lock, flags);
|
|
|
|
mc->apwake_irq_chip->irq_set_type(
|
|
irq_get_irq_data(mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP].num),
|
|
(gpio_val == 1 ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH));
|
|
mif_enable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP]);
|
|
|
|
queue_work_on(RUNTIME_PM_AFFINITY_CORE, mc->wakeup_wq,
|
|
(gpio_val == 1 ? &mc->wakeup_work : &mc->suspend_work));
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t cp_active_handler(int irq, void *data)
|
|
{
|
|
struct modem_ctl *mc = (struct modem_ctl *)data;
|
|
struct link_device *ld;
|
|
struct mem_link_device *mld;
|
|
int cp_active;
|
|
enum modem_state old_state;
|
|
enum modem_state new_state;
|
|
|
|
if (mc == NULL) {
|
|
mif_err_limited("modem_ctl is NOT initialized - IGNORING interrupt\n");
|
|
goto irq_done;
|
|
}
|
|
|
|
ld = get_current_link(mc->iod);
|
|
mld = to_mem_link_device(ld);
|
|
|
|
if (mc->s51xx_pdev == NULL) {
|
|
mif_err_limited("S5100 is NOT initialized - IGNORING interrupt\n");
|
|
goto irq_done;
|
|
}
|
|
|
|
if (mc->phone_state != STATE_ONLINE) {
|
|
mif_err_limited("Phone_state is NOT ONLINE - IGNORING interrupt\n");
|
|
goto irq_done;
|
|
}
|
|
|
|
cp_active = mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_CP_ACTIVE], true);
|
|
mif_err("[PHONE_ACTIVE Handler] state:%s cp_active:%d\n",
|
|
cp_state_str(mc->phone_state), cp_active);
|
|
|
|
if (cp_active == 1) {
|
|
mif_err("ERROR - cp_active is not low, state:%s cp_active:%d\n",
|
|
cp_state_str(mc->phone_state), cp_active);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
if (timer_pending(&mld->crash_ack_timer))
|
|
del_timer(&mld->crash_ack_timer);
|
|
|
|
mif_stop_logging();
|
|
|
|
old_state = mc->phone_state;
|
|
new_state = STATE_CRASH_EXIT;
|
|
|
|
if (ld->crash_reason.type == CRASH_REASON_NONE)
|
|
ld->crash_reason.type = CRASH_REASON_CP_ACT_CRASH;
|
|
|
|
mif_info("Set s5100_cp_reset_required to false\n");
|
|
mc->s5100_cp_reset_required = false;
|
|
|
|
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, mc);
|
|
|
|
change_modem_state(mc, new_state);
|
|
}
|
|
|
|
atomic_set(&mld->forced_cp_crash, 0);
|
|
|
|
irq_done:
|
|
mif_disable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_CP_ACTIVE]);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int register_phone_active_interrupt(struct modem_ctl *mc)
|
|
{
|
|
int ret;
|
|
|
|
if (mc == NULL)
|
|
return -EINVAL;
|
|
|
|
if (mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_CP_ACTIVE].registered)
|
|
return 0;
|
|
|
|
mif_info("Register PHONE ACTIVE interrupt.\n");
|
|
mif_init_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_CP_ACTIVE],
|
|
mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_CP_ACTIVE].num,
|
|
"phone_active", IRQF_TRIGGER_LOW);
|
|
|
|
ret = mif_request_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_CP_ACTIVE], cp_active_handler, mc);
|
|
if (ret) {
|
|
mif_err("%s: ERR! request_irq(%s#%d) fail (%d)\n",
|
|
mc->name, mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_CP_ACTIVE].name,
|
|
mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_CP_ACTIVE].num, ret);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int register_cp2ap_wakeup_interrupt(struct modem_ctl *mc)
|
|
{
|
|
int ret;
|
|
|
|
if (mc == NULL)
|
|
return -EINVAL;
|
|
|
|
if (mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP].registered) {
|
|
mif_info("Set IRQF_TRIGGER_LOW to cp2ap_wakeup gpio\n");
|
|
check_link_order = 1;
|
|
ret = mc->apwake_irq_chip->irq_set_type(
|
|
irq_get_irq_data(mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP].num),
|
|
IRQF_TRIGGER_LOW);
|
|
return ret;
|
|
}
|
|
|
|
mif_info("Register CP2AP WAKEUP interrupt.\n");
|
|
mif_init_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP],
|
|
mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP].num,
|
|
"cp2ap_wakeup", IRQF_TRIGGER_LOW);
|
|
|
|
ret = mif_request_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP], ap_wakeup_handler, mc);
|
|
if (ret) {
|
|
mif_err("%s: ERR! request_irq(%s#%d) fail (%d)\n",
|
|
mc->name, mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP].name,
|
|
mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP].num, ret);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ds_detect = 2;
|
|
module_param(ds_detect, int, 0664);
|
|
MODULE_PARM_DESC(ds_detect, "Dual SIM detect");
|
|
|
|
static ssize_t ds_detect_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", 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 = kstrtoint(buf, 0, &value);
|
|
if (ret != 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;
|
|
}
|
|
static DEVICE_ATTR_RW(ds_detect);
|
|
|
|
static struct attribute *sim_attrs[] = {
|
|
&dev_attr_ds_detect.attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group sim_group = {
|
|
.attrs = sim_attrs,
|
|
.name = "sim",
|
|
};
|
|
|
|
static int get_ds_detect(void)
|
|
{
|
|
if (ds_detect > 2 || ds_detect < 1)
|
|
ds_detect = 2;
|
|
|
|
mif_info("Dual SIM detect = %d\n", ds_detect);
|
|
return ds_detect - 1;
|
|
}
|
|
|
|
static int init_control_messages(struct modem_ctl *mc)
|
|
{
|
|
struct modem_data *modem = mc->mdm_data;
|
|
struct link_device *ld = get_current_link(mc->iod);
|
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
|
int ds_det;
|
|
|
|
if (modem->offset_cmsg_offset)
|
|
iowrite32(modem->cmsg_offset, mld->cmsg_offset);
|
|
if (modem->offset_srinfo_offset)
|
|
iowrite32(modem->srinfo_offset, mld->srinfo_offset);
|
|
if (ld->capability_check && modem->offset_capability_offset)
|
|
iowrite32(modem->capability_offset, mld->capability_offset);
|
|
|
|
set_ctrl_msg(&mld->ap2cp_united_status, 0);
|
|
set_ctrl_msg(&mld->cp2ap_united_status, 0);
|
|
|
|
if (ld->capability_check) {
|
|
int part;
|
|
|
|
for (part = 0; part < AP_CP_CAP_PARTS; part++) {
|
|
iowrite32(0, mld->ap_capability_offset[part]);
|
|
iowrite32(0, mld->cp_capability_offset[part]);
|
|
}
|
|
}
|
|
|
|
ds_det = get_ds_detect();
|
|
if (ds_det < 0) {
|
|
mif_err("ds_det error:%d\n", ds_det);
|
|
return -EINVAL;
|
|
}
|
|
|
|
update_ctrl_msg(&mld->ap2cp_united_status, ds_det, mc->sbi_ds_det_mask,
|
|
mc->sbi_ds_det_pos);
|
|
mif_info("ds_det:%d\n", ds_det);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int request_pcie_msi_int(struct link_device *ld,
|
|
struct platform_device *pdev)
|
|
{
|
|
int ret, base_irq;
|
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
|
struct device *dev = &pdev->dev;
|
|
struct modem_ctl *mc = ld->mc;
|
|
int irq_offset = 0;
|
|
#if IS_ENABLED(CONFIG_CP_PKTPROC)
|
|
struct pktproc_adaptor *ppa = &mld->pktproc;
|
|
int i;
|
|
#endif
|
|
|
|
mif_info("Request MSI interrupt.\n");
|
|
|
|
#if IS_ENABLED(CONFIG_CP_PKTPROC)
|
|
if (ppa->use_exclusive_irq)
|
|
base_irq = s51xx_pcie_request_msi_int(mc->s51xx_pdev, 5);
|
|
else
|
|
#endif
|
|
base_irq = s51xx_pcie_request_msi_int(mc->s51xx_pdev, 4);
|
|
|
|
mif_info("Request MSI interrupt. : BASE_IRQ(%d)\n", base_irq);
|
|
mld->msi_irq_base = base_irq;
|
|
|
|
if (base_irq <= 0) {
|
|
mif_err("Can't get MSI IRQ!!!\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
ret = devm_request_irq(dev, base_irq + irq_offset, shmem_irq_handler,
|
|
IRQF_SHARED, "mif_cp2ap_msg", mld);
|
|
if (ret) {
|
|
mif_err("Can't request cp2ap_msg interrupt!!!\n");
|
|
return -EIO;
|
|
}
|
|
irq_offset++;
|
|
|
|
ret = devm_request_irq(dev, base_irq + irq_offset, shmem_tx_state_handler,
|
|
IRQF_SHARED, "mif_cp2ap_status", mld);
|
|
if (ret) {
|
|
mif_err("Can't request cp2ap_status interrupt!!!\n");
|
|
return -EIO;
|
|
}
|
|
irq_offset++;
|
|
|
|
#if IS_ENABLED(CONFIG_CP_PKTPROC)
|
|
if (ppa->use_exclusive_irq) {
|
|
for (i = 0; i < ppa->num_queue; i++) {
|
|
struct pktproc_queue *q = ppa->q[i];
|
|
|
|
q->irq = mld->msi_irq_base + irq_offset + q->irq_idx;
|
|
ret = devm_request_irq(dev, q->irq, q->irq_handler,
|
|
IRQF_SHARED, "pktproc", q);
|
|
if (ret) {
|
|
mif_err("devm_request_irq() for pktproc%d error %d\n", i, ret);
|
|
return -EIO;
|
|
}
|
|
irq_offset++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
mld->msi_irq_base_enabled = 1;
|
|
|
|
return base_irq;
|
|
}
|
|
|
|
static int register_pcie(struct link_device *ld)
|
|
{
|
|
struct modem_ctl *mc = ld->mc;
|
|
struct platform_device *pdev = to_platform_device(mc->dev);
|
|
static int is_registered;
|
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
|
u32 cp_num = ld->mdm_data->cp_num;
|
|
|
|
mif_info("CP EP driver initialization start.\n");
|
|
|
|
if (!mld->msi_reg_base && (mld->attrs & LINK_ATTR_XMIT_BTDLR_PCIE))
|
|
mld->msi_reg_base = cp_shmem_get_region(cp_num, SHMEM_MSI);
|
|
|
|
#if IS_ENABLED(CONFIG_LINK_DEVICE_PCIE_S2MPU)
|
|
if (!mc->s5100_s2mpu_enabled) {
|
|
int ret;
|
|
u32 shmem_idx;
|
|
|
|
mc->s5100_s2mpu_enabled = true;
|
|
|
|
for (shmem_idx = 0 ; shmem_idx < MAX_CP_SHMEM ; shmem_idx++) {
|
|
if (shmem_idx == SHMEM_MSI && !(mld->attrs & LINK_ATTR_XMIT_BTDLR_PCIE))
|
|
continue;
|
|
|
|
if (cp_shmem_get_base(cp_num, shmem_idx)) {
|
|
ret = (int)exynos_set_dev_stage2_ap("hsi2", 0,
|
|
cp_shmem_get_base(cp_num, shmem_idx),
|
|
cp_shmem_get_size(cp_num, shmem_idx), ATTR_RW);
|
|
mif_info("pcie s2mpu idx:%d - addr:0x%08lx size:0x%08x ret:%d\n",
|
|
shmem_idx,
|
|
cp_shmem_get_base(cp_num, shmem_idx),
|
|
cp_shmem_get_size(cp_num, shmem_idx), ret);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_LINK_DEVICE_PCIE_IOMMU)
|
|
cpif_pcie_iommu_enable_regions(mld);
|
|
#endif
|
|
|
|
msleep(200);
|
|
|
|
s5100_poweron_pcie(mc, !!(mld->attrs & LINK_ATTR_XMIT_BTDLR_PCIE));
|
|
|
|
if (is_registered == 0) {
|
|
/* initialize the pci_dev for modem_ctl */
|
|
mif_info("s51xx_pcie_init start\n");
|
|
s51xx_pcie_init(mc);
|
|
if (!mc->s51xx_pdev) {
|
|
mif_err("s51xx_pdev is NULL. Check if CP wake up is done.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* debug: check MSI 32bit or 64bit - should be set as 32bit before this point*/
|
|
// debug: pci_read_config_dword(s51xx_pcie.s51xx_pdev, 0x50, &msi_val);
|
|
// debug: mif_err("MSI Control Reg : 0x%x\n", msi_val);
|
|
|
|
request_pcie_msi_int(ld, pdev);
|
|
first_save_s51xx_status(mc->s51xx_pdev);
|
|
|
|
is_registered = 1;
|
|
} else {
|
|
if (mc->phone_state == STATE_CRASH_RESET) {
|
|
print_msi_register(mc->s51xx_pdev);
|
|
enable_irq(mld->msi_irq_base);
|
|
}
|
|
}
|
|
|
|
print_msi_register(mc->s51xx_pdev);
|
|
mc->pcie_registered = true;
|
|
|
|
mif_info("CP EP driver initialization end.\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gpio_power_off_cp(struct modem_ctl *mc)
|
|
{
|
|
#if IS_ENABLED(CONFIG_CP_WRESET_WA)
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_NRESET], 0, 50);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_CP_PWR], 0, 0);
|
|
#else
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_NRESET], 0, 0);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_CP_WRST_N], 0, 0);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_CP_PWR], 0, 30);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_PM_WRST_N], 0, 50);
|
|
#endif
|
|
}
|
|
|
|
static void gpio_power_offon_cp(struct modem_ctl *mc)
|
|
{
|
|
gpio_power_off_cp(mc);
|
|
|
|
#if IS_ENABLED(CONFIG_CP_WRESET_WA)
|
|
udelay(50);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_CP_PWR], 1, 50);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_NRESET], 1, 50);
|
|
#else
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_PM_WRST_N], 1, 10);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_CP_PWR], 1, 10);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_NRESET], 1, 10);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_CP_WRST_N], 1, 0);
|
|
#endif
|
|
}
|
|
|
|
static int power_on_cp(struct modem_ctl *mc)
|
|
{
|
|
struct link_device *ld = get_current_link(mc->iod);
|
|
struct modem_data __maybe_unused *modem = mc->mdm_data;
|
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
|
|
|
mif_info("%s: +++\n", mc->name);
|
|
|
|
mif_disable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_CP_ACTIVE]);
|
|
mif_disable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP]);
|
|
drain_workqueue(mc->wakeup_wq);
|
|
|
|
print_mc_state(mc);
|
|
|
|
if (!cpif_wake_lock_active(mc->ws))
|
|
cpif_wake_lock(mc->ws);
|
|
|
|
mc->phone_state = STATE_OFFLINE;
|
|
pcie_clean_dislink(mc);
|
|
|
|
mc->pcie_registered = false;
|
|
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], 1, 0);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_DUMP_NOTI], 0, 0);
|
|
|
|
/* Clear shared memory */
|
|
init_ctrl_msg(&mld->ap2cp_msg);
|
|
init_ctrl_msg(&mld->cp2ap_msg);
|
|
|
|
print_mc_state(mc);
|
|
gpio_power_offon_cp(mc);
|
|
mif_info("GPIO status after S5100 Power on\n");
|
|
print_mc_state(mc);
|
|
|
|
mif_info("---\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int power_off_cp(struct modem_ctl *mc)
|
|
{
|
|
mif_info("%s: +++\n", mc->name);
|
|
|
|
if (mc->phone_state == STATE_OFFLINE)
|
|
goto exit;
|
|
|
|
change_modem_state(mc, STATE_OFFLINE);
|
|
|
|
pcie_clean_dislink(mc);
|
|
|
|
gpio_power_off_cp(mc);
|
|
print_mc_state(mc);
|
|
|
|
exit:
|
|
mif_info("---\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int power_shutdown_cp(struct modem_ctl *mc)
|
|
{
|
|
int i;
|
|
|
|
mif_err("%s: +++\n", mc->name);
|
|
|
|
if (mc->phone_state == STATE_OFFLINE)
|
|
goto exit;
|
|
|
|
mif_disable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_CP_ACTIVE]);
|
|
mif_disable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP]);
|
|
drain_workqueue(mc->wakeup_wq);
|
|
|
|
/* wait for cp_active for 3 seconds */
|
|
for (i = 0; i < 150; i++) {
|
|
if (mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_CP_ACTIVE], false) == 1) {
|
|
mif_err("PHONE_ACTIVE pin is HIGH...\n");
|
|
break;
|
|
}
|
|
msleep(20);
|
|
}
|
|
|
|
gpio_power_off_cp(mc);
|
|
print_mc_state(mc);
|
|
|
|
pcie_clean_dislink(mc);
|
|
|
|
exit:
|
|
mif_err("---\n");
|
|
return 0;
|
|
}
|
|
|
|
static int power_reset_dump_cp(struct modem_ctl *mc)
|
|
{
|
|
struct s51xx_pcie *s51xx_pcie = NULL;
|
|
struct link_device *ld = get_current_link(mc->iod);
|
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
|
|
|
mif_info("%s: +++\n", mc->name);
|
|
|
|
if (ld->sbd_ipc && hrtimer_active(&mld->sbd_print_timer))
|
|
hrtimer_cancel(&mld->sbd_print_timer);
|
|
|
|
mc->phone_state = STATE_CRASH_EXIT;
|
|
mif_disable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_CP_ACTIVE]);
|
|
mif_disable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP]);
|
|
drain_workqueue(mc->wakeup_wq);
|
|
pcie_clean_dislink(mc);
|
|
|
|
if (mc->s51xx_pdev != NULL)
|
|
s51xx_pcie = pci_get_drvdata(mc->s51xx_pdev);
|
|
|
|
if (s51xx_pcie && s51xx_pcie->link_status == 1) {
|
|
mif_info("link_satus:%d\n", s51xx_pcie->link_status);
|
|
s51xx_pcie_save_state(mc->s51xx_pdev);
|
|
pcie_clean_dislink(mc);
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_LINK_DEVICE_PCIE_GPIO_WA)
|
|
if (mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_DUMP_NOTI], 1, 10))
|
|
mif_gpio_toggle_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], 50);
|
|
#else
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_DUMP_NOTI], 1, 0);
|
|
#endif
|
|
|
|
mif_info("s5100_cp_reset_required:%d\n", mc->s5100_cp_reset_required);
|
|
if (mc->s5100_cp_reset_required) {
|
|
gpio_power_offon_cp(mc);
|
|
} else {
|
|
#if !IS_ENABLED(CONFIG_CP_WRESET_WA)
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_CP_WRST_N], 0, 50);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_PM_WRST_N], 0, 50);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_PM_WRST_N], 1, 50);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_CP_WRST_N], 1, 50);
|
|
#endif
|
|
}
|
|
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], 1, 0);
|
|
print_mc_state(mc);
|
|
|
|
mif_info("---\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int power_reset_cp(struct modem_ctl *mc)
|
|
{
|
|
struct s51xx_pcie *s51xx_pcie = NULL;
|
|
struct link_device *ld = get_current_link(mc->iod);
|
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
|
|
|
mif_info("%s: +++\n", mc->name);
|
|
|
|
if (ld->sbd_ipc && hrtimer_active(&mld->sbd_print_timer))
|
|
hrtimer_cancel(&mld->sbd_print_timer);
|
|
|
|
mc->phone_state = STATE_OFFLINE;
|
|
pcie_clean_dislink(mc);
|
|
|
|
if (mc->s51xx_pdev != NULL)
|
|
s51xx_pcie = pci_get_drvdata(mc->s51xx_pdev);
|
|
|
|
if (s51xx_pcie && s51xx_pcie->link_status == 1) {
|
|
/* save_s5100_status(); */
|
|
mif_info("link_satus:%d\n", s51xx_pcie->link_status);
|
|
pcie_clean_dislink(mc);
|
|
}
|
|
|
|
gpio_power_offon_cp(mc);
|
|
print_mc_state(mc);
|
|
|
|
mif_info("---\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int check_cp_status(struct modem_ctl *mc, unsigned int count, bool check_msi)
|
|
{
|
|
#define STATUS_NAME(msi) (msi ? "boot_stage" : "CP2AP_WAKEUP")
|
|
|
|
struct link_device *ld = get_current_link(mc->bootd);
|
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
|
bool check_done = false;
|
|
int cnt = 0;
|
|
int val;
|
|
|
|
do {
|
|
if (check_msi) {
|
|
val = (int)ioread32(mld->msi_reg_base +
|
|
offsetof(struct msi_reg_type, boot_stage));
|
|
if (val == BOOT_STAGE_DONE_MASK) {
|
|
check_done = true;
|
|
break;
|
|
}
|
|
} else {
|
|
val = mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_WAKEUP], false);
|
|
if (val == 1) {
|
|
check_done = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
mif_info_limited("%s == 0x%X (cnt %d)\n", STATUS_NAME(check_msi), val, cnt);
|
|
msleep(20);
|
|
} while (++cnt < count);
|
|
|
|
if (!check_done) {
|
|
mif_err("ERR! %s == 0x%X (cnt %d)\n", STATUS_NAME(check_msi), val, cnt);
|
|
return -EFAULT;
|
|
}
|
|
|
|
mif_info("%s == 0x%X (cnt %d)\n", STATUS_NAME(check_msi), val, cnt);
|
|
if (cnt == 0)
|
|
msleep(10);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int set_cp_rom_boot_img(struct mem_link_device *mld)
|
|
{
|
|
struct link_device *ld = &mld->link_dev;
|
|
struct modem_ctl *mc = ld->mc;
|
|
struct modem_data *modem = mc->mdm_data;
|
|
unsigned long boot_img_addr;
|
|
|
|
if (!(mld->attrs & LINK_ATTR_XMIT_BTDLR_PCIE)) {
|
|
mif_err("Invalid attr:0x%lx\n", mld->attrs);
|
|
return -EPERM;
|
|
}
|
|
|
|
if (!mld->msi_reg_base) {
|
|
mif_err("MSI region is not assigned yet\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
boot_img_addr = cp_shmem_get_base(modem->cp_num, SHMEM_IPC) + mld->boot_img_offset;
|
|
|
|
iowrite32(PADDR_LO(boot_img_addr),
|
|
mld->msi_reg_base + offsetof(struct msi_reg_type, img_addr_lo));
|
|
iowrite32(PADDR_HI(boot_img_addr),
|
|
mld->msi_reg_base + offsetof(struct msi_reg_type, img_addr_hi));
|
|
iowrite32(mld->boot_img_size,
|
|
mld->msi_reg_base + offsetof(struct msi_reg_type, img_size));
|
|
|
|
mif_info("boot_img addr:0x%lX size:0x%X\n", boot_img_addr, mld->boot_img_size);
|
|
|
|
s51xx_pcie_send_doorbell_int(mc->s51xx_pdev, mld->intval_ap2cp_msg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void debug_cp_rom_boot_img(struct mem_link_device *mld)
|
|
{
|
|
unsigned char str[64 * 3];
|
|
u8 __iomem *img_base;
|
|
u32 img_size;
|
|
|
|
if (!(mld->attrs & LINK_ATTR_XMIT_BTDLR_PCIE)) {
|
|
mif_err("Invalid attr:0x%lx\n", mld->attrs);
|
|
return;
|
|
}
|
|
|
|
img_base = mld->base + mld->boot_img_offset;
|
|
img_size = ioread32(mld->msi_reg_base + offsetof(struct msi_reg_type, img_size));
|
|
|
|
mif_err("boot_stage:0x%X err_report:0x%X img_lo:0x%X img_hi:0x%X img_size:0x%X\n",
|
|
ioread32(mld->msi_reg_base + offsetof(struct msi_reg_type, boot_stage)),
|
|
ioread32(mld->msi_reg_base + offsetof(struct msi_reg_type, err_report)),
|
|
ioread32(mld->msi_reg_base + offsetof(struct msi_reg_type, img_addr_lo)),
|
|
ioread32(mld->msi_reg_base + offsetof(struct msi_reg_type, img_addr_hi)),
|
|
img_size);
|
|
|
|
if (img_size > 64)
|
|
img_size = 64;
|
|
|
|
dump2hex(str, (img_size ? img_size * 3 : 1), img_base, img_size);
|
|
mif_err("img_content:%s\n", str);
|
|
}
|
|
|
|
static int start_normal_boot(struct modem_ctl *mc)
|
|
{
|
|
struct link_device *ld = get_current_link(mc->bootd);
|
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
|
int ret = 0;
|
|
|
|
mif_info("+++\n");
|
|
|
|
if (init_control_messages(mc))
|
|
mif_err("Failed to initialize control messages\n");
|
|
|
|
/* 2cp dump WA */
|
|
if (timer_pending(&mld->crash_ack_timer))
|
|
del_timer(&mld->crash_ack_timer);
|
|
atomic_set(&mld->forced_cp_crash, 0);
|
|
|
|
mif_info("Set link mode to LINK_MODE_BOOT.\n");
|
|
|
|
if (ld->link_prepare_normal_boot)
|
|
ld->link_prepare_normal_boot(ld, mc->bootd);
|
|
|
|
change_modem_state(mc, STATE_BOOTING);
|
|
|
|
mif_info("Disable phone actvie interrupt.\n");
|
|
mif_disable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_CP_ACTIVE]);
|
|
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], 1, 0);
|
|
mc->phone_state = STATE_BOOTING;
|
|
|
|
if (ld->link_start_normal_boot) {
|
|
mif_info("link_start_normal_boot\n");
|
|
ld->link_start_normal_boot(ld, mc->iod);
|
|
}
|
|
|
|
ret = modem_ctrl_check_offset_data(mc);
|
|
if (ret) {
|
|
mif_err("modem_ctrl_check_offset_data() error:%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (mld->attrs & LINK_ATTR_XMIT_BTDLR_PCIE) {
|
|
register_pcie(ld);
|
|
if (mc->s51xx_pdev && mc->pcie_registered)
|
|
set_cp_rom_boot_img(mld);
|
|
|
|
ret = check_cp_status(mc, 200, true);
|
|
if (ret < 0)
|
|
goto status_error;
|
|
|
|
s5100_poweroff_pcie(mc, false);
|
|
|
|
ret = check_cp_status(mc, 200, false);
|
|
if (ret < 0)
|
|
goto status_error;
|
|
|
|
s5100_poweron_pcie(mc, false);
|
|
} else {
|
|
ret = check_cp_status(mc, 200, false);
|
|
if (ret < 0)
|
|
goto status_error;
|
|
|
|
register_pcie(ld);
|
|
}
|
|
|
|
status_error:
|
|
if (ret < 0) {
|
|
mif_err("ERR! check_cp_status fail (err %d)\n", ret);
|
|
if (mld->attrs & LINK_ATTR_XMIT_BTDLR_PCIE)
|
|
debug_cp_rom_boot_img(mld);
|
|
if (cpif_wake_lock_active(mc->ws))
|
|
cpif_wake_unlock(mc->ws);
|
|
|
|
return ret;
|
|
}
|
|
|
|
mif_info("---\n");
|
|
return 0;
|
|
}
|
|
|
|
static int complete_normal_boot(struct modem_ctl *mc)
|
|
{
|
|
int err = 0;
|
|
unsigned long remain;
|
|
#if IS_ENABLED(CONFIG_CP_LCD_NOTIFIER)
|
|
int __maybe_unused ret;
|
|
struct modem_data __maybe_unused *modem = mc->mdm_data;
|
|
struct mem_link_device __maybe_unused *mld = modem->mld;
|
|
#endif
|
|
|
|
mif_info("+++\n");
|
|
|
|
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;
|
|
}
|
|
|
|
/* Enable L1.2 after CP boot */
|
|
s51xx_pcie_l1ss_ctrl(1);
|
|
|
|
/* Read cp_active before enabling irq */
|
|
mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_CP_ACTIVE], true);
|
|
|
|
err = register_phone_active_interrupt(mc);
|
|
if (err)
|
|
mif_err("Err: register_phone_active_interrupt:%d\n", err);
|
|
mif_enable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_CP_ACTIVE]);
|
|
|
|
err = register_cp2ap_wakeup_interrupt(mc);
|
|
if (err)
|
|
mif_err("Err: register_cp2ap_wakeup_interrupt:%d\n", err);
|
|
mif_enable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP]);
|
|
|
|
print_mc_state(mc);
|
|
|
|
mc->device_reboot = false;
|
|
|
|
change_modem_state(mc, STATE_ONLINE);
|
|
|
|
print_mc_state(mc);
|
|
|
|
#if IS_ENABLED(CONFIG_CP_LCD_NOTIFIER)
|
|
if (mc->lcd_notifier.notifier_call == NULL) {
|
|
mif_info("Register lcd notifier\n");
|
|
mc->lcd_notifier.notifier_call = s5100_lcd_notifier;
|
|
ret = register_lcd_status_notifier(&mc->lcd_notifier);
|
|
if (ret) {
|
|
mif_err("failed to register LCD notifier\n");
|
|
return ret;
|
|
}
|
|
}
|
|
#endif /* CONFIG_CP_LCD_NOTIFIER */
|
|
|
|
mif_info("---\n");
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static int trigger_cp_crash_internal(struct modem_ctl *mc)
|
|
{
|
|
struct link_device *ld = get_current_link(mc->bootd);
|
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
|
u32 crash_type;
|
|
|
|
if (ld->crash_reason.type == CRASH_REASON_NONE)
|
|
ld->crash_reason.type = CRASH_REASON_MIF_FORCED;
|
|
crash_type = ld->crash_reason.type;
|
|
|
|
mif_err("+++\n");
|
|
|
|
if (mc->device_reboot) {
|
|
mif_err("skip cp crash : device is rebooting..!!!\n");
|
|
goto exit;
|
|
}
|
|
|
|
print_mc_state(mc);
|
|
|
|
if (mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_CP_ACTIVE], true) == 1) {
|
|
#if IS_ENABLED(CONFIG_LINK_DEVICE_PCIE_GPIO_WA)
|
|
if (atomic_inc_return(&mc->dump_toggle_issued) > 1) {
|
|
atomic_dec(&mc->dump_toggle_issued);
|
|
goto exit;
|
|
}
|
|
|
|
if (mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_DUMP_NOTI], 1, 10))
|
|
mif_gpio_toggle_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], 50);
|
|
|
|
atomic_dec(&mc->dump_toggle_issued);
|
|
#else
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_DUMP_NOTI], 1, 0);
|
|
#endif
|
|
} else {
|
|
mif_err("do not need to set dump_noti\n");
|
|
}
|
|
|
|
if (ld->protocol == PROTOCOL_SIT &&
|
|
crash_type == CRASH_REASON_RIL_TRIGGER_CP_CRASH)
|
|
ld->link_trigger_cp_crash(mld, crash_type, ld->crash_reason.string);
|
|
else
|
|
ld->link_trigger_cp_crash(mld, crash_type, "Forced crash is called");
|
|
|
|
exit:
|
|
mif_err("---\n");
|
|
return 0;
|
|
}
|
|
|
|
static void trigger_cp_crash_work(struct work_struct *ws)
|
|
{
|
|
struct modem_ctl *mc = container_of(ws, struct modem_ctl, crash_work);
|
|
|
|
trigger_cp_crash_internal(mc);
|
|
}
|
|
|
|
static int trigger_cp_crash(struct modem_ctl *mc)
|
|
{
|
|
queue_work(mc->crash_wq, &mc->crash_work);
|
|
return 0;
|
|
}
|
|
|
|
int s5100_force_crash_exit_ext(void)
|
|
{
|
|
if (g_mc)
|
|
g_mc->ops.trigger_cp_crash(g_mc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int modem_force_crash_exit_ext(void)
|
|
{
|
|
return s5100_force_crash_exit_ext();
|
|
}
|
|
EXPORT_SYMBOL(modem_force_crash_exit_ext);
|
|
|
|
int s5100_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;
|
|
}
|
|
|
|
static int start_dump_boot(struct modem_ctl *mc)
|
|
{
|
|
struct link_device *ld = get_current_link(mc->bootd);
|
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
|
int err = 0;
|
|
|
|
mif_err("+++\n");
|
|
|
|
/* Change phone state to CRASH_EXIT */
|
|
mc->phone_state = STATE_CRASH_EXIT;
|
|
|
|
if (!ld->link_start_dump_boot) {
|
|
mif_err("%s: link_start_dump_boot is null\n", ld->name);
|
|
return -EFAULT;
|
|
}
|
|
|
|
err = ld->link_start_dump_boot(ld, mc->bootd);
|
|
if (err)
|
|
return err;
|
|
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], 1, 0);
|
|
/* do not handle cp2ap_wakeup irq during dump process */
|
|
mif_disable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP]);
|
|
|
|
if (mld->attrs & LINK_ATTR_XMIT_BTDLR_PCIE) {
|
|
register_pcie(ld);
|
|
if (mc->s51xx_pdev && mc->pcie_registered)
|
|
set_cp_rom_boot_img(mld);
|
|
|
|
err = check_cp_status(mc, 200, true);
|
|
if (err < 0)
|
|
goto status_error;
|
|
|
|
s5100_poweroff_pcie(mc, false);
|
|
|
|
err = check_cp_status(mc, 200, false);
|
|
if (err < 0)
|
|
goto status_error;
|
|
|
|
s5100_poweron_pcie(mc, false);
|
|
} else {
|
|
err = check_cp_status(mc, 200, false);
|
|
if (err < 0)
|
|
goto status_error;
|
|
|
|
register_pcie(ld);
|
|
}
|
|
|
|
status_error:
|
|
if (err < 0) {
|
|
mif_err("ERR! check_cp_status fail (err %d)\n", err);
|
|
if (mld->attrs & LINK_ATTR_XMIT_BTDLR_PCIE)
|
|
debug_cp_rom_boot_img(mld);
|
|
return err;
|
|
}
|
|
|
|
mif_err("---\n");
|
|
return err;
|
|
}
|
|
|
|
static int s5100_poweroff_pcie(struct modem_ctl *mc, bool force_off)
|
|
{
|
|
struct link_device *ld = get_current_link(mc->iod);
|
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
|
bool force_crash = false;
|
|
bool in_pcie_recovery = false;
|
|
unsigned long flags;
|
|
|
|
mutex_lock(&mc->pcie_onoff_lock);
|
|
mutex_lock(&mc->pcie_check_lock);
|
|
mif_info("+++\n");
|
|
|
|
if (!mc->pcie_powered_on &&
|
|
(s51xx_check_pcie_link_status(mc->pcie_ch_num) == 0)) {
|
|
mif_err("skip pci power off : already powered off\n");
|
|
goto exit;
|
|
}
|
|
|
|
/* CP reads Tx RP (or tail) after CP2AP_WAKEUP = 1.
|
|
* skip pci power off if CP2AP_WAKEUP = 1 or Tx pending.
|
|
*/
|
|
if (!force_off) {
|
|
spin_lock_irqsave(&mc->pcie_tx_lock, flags);
|
|
/* wait Tx done if it is running */
|
|
spin_unlock_irqrestore(&mc->pcie_tx_lock, flags);
|
|
msleep(30);
|
|
if (check_mem_link_tx_pending(mld) ||
|
|
mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_WAKEUP], true) == 1) {
|
|
mif_err("skip pci power off : condition not met\n");
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (mld->msi_irq_base_enabled == 1) {
|
|
disable_irq(mld->msi_irq_base);
|
|
mld->msi_irq_base_enabled = 0;
|
|
}
|
|
|
|
if (mc->device_reboot) {
|
|
mif_err("skip pci power off : device is rebooting..!!!\n");
|
|
goto exit;
|
|
}
|
|
|
|
/* recovery status is not valid after PCI link down requests from CP */
|
|
if (mc->pcie_cto_retry_cnt > 0) {
|
|
mif_info("clear cto_retry_cnt(%d)..!!!\n", mc->pcie_cto_retry_cnt);
|
|
mc->pcie_cto_retry_cnt = 0;
|
|
}
|
|
|
|
if (exynos_pcie_rc_get_cpl_timeout_state(mc->pcie_ch_num)) {
|
|
exynos_pcie_rc_set_cpl_timeout_state(mc->pcie_ch_num, false);
|
|
in_pcie_recovery = true;
|
|
}
|
|
|
|
mc->pcie_powered_on = false;
|
|
|
|
if (mc->s51xx_pdev != NULL && (mc->phone_state == STATE_ONLINE ||
|
|
mc->phone_state == STATE_BOOTING)) {
|
|
mif_info("save s5100_status - phone_state:%d\n",
|
|
mc->phone_state);
|
|
s51xx_pcie_save_state(mc->s51xx_pdev);
|
|
} else
|
|
mif_info("ignore save_s5100_status - phone_state:%d\n",
|
|
mc->phone_state);
|
|
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_WAKEUP], 0, 5);
|
|
print_mc_state(mc);
|
|
|
|
exynos_pcie_host_v1_poweroff(mc->pcie_ch_num);
|
|
|
|
if (cpif_wake_lock_active(mc->ws))
|
|
cpif_wake_unlock(mc->ws);
|
|
|
|
exit:
|
|
mif_info("---\n");
|
|
mutex_unlock(&mc->pcie_check_lock);
|
|
mutex_unlock(&mc->pcie_onoff_lock);
|
|
|
|
spin_lock_irqsave(&mc->pcie_tx_lock, flags);
|
|
if (in_pcie_recovery && !mc->reserve_doorbell_int && check_mem_link_tx_pending(mld))
|
|
mc->reserve_doorbell_int = true;
|
|
|
|
if ((mc->s51xx_pdev != NULL) && !mc->device_reboot && mc->reserve_doorbell_int) {
|
|
mif_info("DBG: doorbell_reserved = %d\n", mc->reserve_doorbell_int);
|
|
if (mc->pcie_powered_on) {
|
|
mc->reserve_doorbell_int = false;
|
|
if (s51xx_pcie_send_doorbell_int(mc->s51xx_pdev,
|
|
mld->intval_ap2cp_msg) != 0)
|
|
force_crash = true;
|
|
} else
|
|
s5100_try_gpio_cp_wakeup(mc);
|
|
}
|
|
spin_unlock_irqrestore(&mc->pcie_tx_lock, flags);
|
|
|
|
if (unlikely(force_crash))
|
|
s5100_force_crash_exit_ext();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int s5100_poweron_pcie(struct modem_ctl *mc, bool boot_on)
|
|
{
|
|
struct link_device *ld;
|
|
struct mem_link_device *mld;
|
|
bool force_crash = false;
|
|
unsigned long flags;
|
|
|
|
if (mc == NULL) {
|
|
mif_err("Skip pci power on : mc is NULL\n");
|
|
return 0;
|
|
}
|
|
|
|
ld = get_current_link(mc->iod);
|
|
mld = to_mem_link_device(ld);
|
|
|
|
if (mc->phone_state == STATE_OFFLINE) {
|
|
mif_err("Skip pci power on : phone_state is OFFLINE\n");
|
|
return 0;
|
|
}
|
|
|
|
mutex_lock(&mc->pcie_onoff_lock);
|
|
mutex_lock(&mc->pcie_check_lock);
|
|
mif_info("+++\n");
|
|
if (mc->pcie_powered_on &&
|
|
(s51xx_check_pcie_link_status(mc->pcie_ch_num) != 0)) {
|
|
mif_err("skip pci power on : already powered on\n");
|
|
goto exit;
|
|
}
|
|
|
|
if (!boot_on &&
|
|
mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_WAKEUP], true) == 0) {
|
|
mif_err("skip pci power on : condition not met\n");
|
|
goto exit;
|
|
}
|
|
|
|
if (mc->device_reboot) {
|
|
mif_err("skip pci power on : device is rebooting..!!!\n");
|
|
goto exit;
|
|
}
|
|
|
|
if (!cpif_wake_lock_active(mc->ws))
|
|
cpif_wake_lock(mc->ws);
|
|
|
|
if (!boot_on)
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_WAKEUP], 1, 5);
|
|
print_mc_state(mc);
|
|
|
|
spin_lock_irqsave(&mc->pcie_tx_lock, flags);
|
|
/* wait Tx done if it is running */
|
|
spin_unlock_irqrestore(&mc->pcie_tx_lock, flags);
|
|
|
|
if (exynos_pcie_rc_get_cpl_timeout_state(mc->pcie_ch_num))
|
|
exynos_pcie_set_ready_cto_recovery(mc->pcie_ch_num);
|
|
|
|
if (exynos_pcie_host_v1_poweron(mc->pcie_ch_num, (boot_on ? 1 : 3)) != 0)
|
|
goto exit;
|
|
|
|
mc->pcie_powered_on = true;
|
|
|
|
if (mc->s51xx_pdev != NULL) {
|
|
s51xx_pcie_restore_state(mc->s51xx_pdev);
|
|
|
|
/* DBG: check MSI sfr setting values */
|
|
print_msi_register(mc->s51xx_pdev);
|
|
} else {
|
|
mif_err("DBG: MSI sfr not set up, yet(s5100_pdev is NULL)");
|
|
}
|
|
|
|
if (mld->msi_irq_base_enabled == 0) {
|
|
enable_irq(mld->msi_irq_base);
|
|
mld->msi_irq_base_enabled = 1;
|
|
}
|
|
|
|
if ((mc->s51xx_pdev != NULL) && mc->pcie_registered) {
|
|
/* DBG */
|
|
mif_info("DBG: doorbell: pcie_registered = %d\n", mc->pcie_registered);
|
|
if (s51xx_pcie_send_doorbell_int(mc->s51xx_pdev, mc->int_pcie_link_ack) != 0) {
|
|
/* DBG */
|
|
mif_err("DBG: s5100pcie_send_doorbell_int() func. is failed !!!\n");
|
|
s5100_force_crash_exit_ext();
|
|
}
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_SUSPEND_DURING_VOICE_CALL)
|
|
if (mc->pcie_voice_call_on && (mc->phone_state != STATE_CRASH_EXIT)) {
|
|
if (cpif_wake_lock_active(mc->ws))
|
|
cpif_wake_unlock(mc->ws);
|
|
|
|
mif_info("wakelock active = %d, voice status = %d\n",
|
|
cpif_wake_lock_active(mc->ws), mc->pcie_voice_call_on);
|
|
}
|
|
#endif
|
|
|
|
/* using PCIE 1 lane at usual tput */
|
|
if (exynos_pcie_rc_lanechange(1, 1)) {
|
|
mif_info("fail to change PCI lane 1\n");
|
|
} else {
|
|
mif_info("change to PCI lane 1\n");
|
|
}
|
|
|
|
exit:
|
|
mif_info("---\n");
|
|
mutex_unlock(&mc->pcie_check_lock);
|
|
mutex_unlock(&mc->pcie_onoff_lock);
|
|
|
|
spin_lock_irqsave(&mc->pcie_tx_lock, flags);
|
|
if ((mc->s51xx_pdev != NULL) && mc->pcie_powered_on && mc->reserve_doorbell_int) {
|
|
mif_info("DBG: doorbell: doorbell_reserved = %d\n", mc->reserve_doorbell_int);
|
|
mc->reserve_doorbell_int = false;
|
|
if (s51xx_pcie_send_doorbell_int(mc->s51xx_pdev, mld->intval_ap2cp_msg) != 0)
|
|
force_crash = true;
|
|
}
|
|
spin_unlock_irqrestore(&mc->pcie_tx_lock, flags);
|
|
|
|
if (unlikely(force_crash))
|
|
s5100_force_crash_exit_ext();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int s5100_set_outbound_atu(struct modem_ctl *mc, struct cp_btl *btl, loff_t *pos, u32 map_size)
|
|
{
|
|
int ret = 0;
|
|
u32 atu_grp = (*pos) / map_size;
|
|
|
|
if (atu_grp != btl->last_pcie_atu_grp) {
|
|
ret = exynos_pcie_rc_set_outbound_atu(
|
|
mc->pcie_ch_num, btl->mem.cp_p_base, (atu_grp * map_size), map_size);
|
|
btl->last_pcie_atu_grp = atu_grp;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int suspend_cp(struct modem_ctl *mc)
|
|
{
|
|
if (!mc)
|
|
return 0;
|
|
|
|
do {
|
|
#if IS_ENABLED(CONFIG_SUSPEND_DURING_VOICE_CALL)
|
|
if (mc->pcie_voice_call_on)
|
|
break;
|
|
#endif
|
|
|
|
if (mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_WAKEUP], true) == 1) {
|
|
mif_err("abort suspend\n");
|
|
return -EBUSY;
|
|
}
|
|
} while (0);
|
|
|
|
#if !IS_ENABLED(CONFIG_CP_LCD_NOTIFIER)
|
|
modem_ctrl_set_kerneltime(mc);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], 0, 0);
|
|
mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], true);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int resume_cp(struct modem_ctl *mc)
|
|
{
|
|
if (!mc)
|
|
return 0;
|
|
|
|
#if !IS_ENABLED(CONFIG_CP_LCD_NOTIFIER)
|
|
modem_ctrl_set_kerneltime(mc);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], 1, 0);
|
|
mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], true);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int s5100_pm_notifier(struct notifier_block *notifier,
|
|
unsigned long pm_event, void *v)
|
|
{
|
|
struct modem_ctl *mc;
|
|
unsigned long flags;
|
|
int gpio_val;
|
|
|
|
mc = container_of(notifier, struct modem_ctl, pm_notifier);
|
|
|
|
switch (pm_event) {
|
|
case PM_SUSPEND_PREPARE:
|
|
mif_info("Suspend prepare\n");
|
|
|
|
spin_lock_irqsave(&mc->pcie_pm_lock, flags);
|
|
mc->pcie_pm_suspended = true;
|
|
spin_unlock_irqrestore(&mc->pcie_pm_lock, flags);
|
|
break;
|
|
case PM_POST_SUSPEND:
|
|
mif_info("Resume done\n");
|
|
|
|
spin_lock_irqsave(&mc->pcie_pm_lock, flags);
|
|
mc->pcie_pm_suspended = false;
|
|
if (mc->pcie_pm_resume_wait) {
|
|
mc->pcie_pm_resume_wait = false;
|
|
gpio_val = mc->pcie_pm_resume_gpio_val;
|
|
|
|
mif_err("cp2ap_wakeup work resume. gpio_val : %d\n", gpio_val);
|
|
|
|
mc->apwake_irq_chip->irq_set_type(
|
|
irq_get_irq_data(mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP].num),
|
|
(gpio_val == 1 ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH));
|
|
mif_enable_irq(&mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP]);
|
|
|
|
queue_work_on(RUNTIME_PM_AFFINITY_CORE, mc->wakeup_wq,
|
|
(gpio_val == 1 ? &mc->wakeup_work : &mc->suspend_work));
|
|
}
|
|
spin_unlock_irqrestore(&mc->pcie_pm_lock, flags);
|
|
break;
|
|
default:
|
|
mif_info("pm_event %lu\n", pm_event);
|
|
break;
|
|
}
|
|
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
int s5100_try_gpio_cp_wakeup(struct modem_ctl *mc)
|
|
{
|
|
if ((mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_AP2CP_WAKEUP], false) == 0) &&
|
|
(mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_CP2AP_WAKEUP], false) == 0) &&
|
|
(s51xx_check_pcie_link_status(mc->pcie_ch_num) == 0)) {
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_WAKEUP], 1, 0);
|
|
return 0;
|
|
}
|
|
return -EPERM;
|
|
}
|
|
|
|
static void s5100_get_ops(struct modem_ctl *mc)
|
|
{
|
|
mc->ops.power_on = power_on_cp;
|
|
mc->ops.power_off = power_off_cp;
|
|
mc->ops.power_shutdown = power_shutdown_cp;
|
|
mc->ops.power_reset = power_reset_cp;
|
|
mc->ops.power_reset_dump = power_reset_dump_cp;
|
|
|
|
mc->ops.start_normal_boot = start_normal_boot;
|
|
mc->ops.complete_normal_boot = complete_normal_boot;
|
|
|
|
mc->ops.start_dump_boot = start_dump_boot;
|
|
mc->ops.trigger_cp_crash = trigger_cp_crash;
|
|
|
|
mc->ops.suspend = suspend_cp;
|
|
mc->ops.resume = resume_cp;
|
|
}
|
|
|
|
static int s5100_get_pdata(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;
|
|
unsigned int i;
|
|
|
|
/* label */
|
|
mc->cp_gpio[CP_GPIO_AP2CP_CP_PWR].label = "AP2CP_CP_PWR";
|
|
mc->cp_gpio[CP_GPIO_AP2CP_NRESET].label = "AP2CP_NRESET";
|
|
mc->cp_gpio[CP_GPIO_AP2CP_WAKEUP].label = "AP2CP_WAKEUP";
|
|
mc->cp_gpio[CP_GPIO_AP2CP_DUMP_NOTI].label = "AP2CP_DUMP_NOTI";
|
|
mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE].label = "AP2CP_AP_ACTIVE";
|
|
#if !IS_ENABLED(CONFIG_CP_WRESET_WA)
|
|
mc->cp_gpio[CP_GPIO_AP2CP_CP_WRST_N].label = "AP2CP_CP_WRST_N";
|
|
mc->cp_gpio[CP_GPIO_AP2CP_PM_WRST_N].label = "AP2CP_PM_WRST_N";
|
|
#endif
|
|
mc->cp_gpio[CP_GPIO_CP2AP_PS_HOLD].label = "CP2AP_PS_HOLD";
|
|
mc->cp_gpio[CP_GPIO_CP2AP_WAKEUP].label = "CP2AP_WAKEUP";
|
|
mc->cp_gpio[CP_GPIO_CP2AP_CP_ACTIVE].label = "CP2AP_CP_ACTIVE";
|
|
|
|
/* node name */
|
|
mc->cp_gpio[CP_GPIO_AP2CP_CP_PWR].node_name = "gpio_ap2cp_cp_pwr_on";
|
|
mc->cp_gpio[CP_GPIO_AP2CP_NRESET].node_name = "gpio_ap2cp_nreset_n";
|
|
mc->cp_gpio[CP_GPIO_AP2CP_WAKEUP].node_name = "gpio_ap2cp_wake_up";
|
|
mc->cp_gpio[CP_GPIO_AP2CP_DUMP_NOTI].node_name = "gpio_ap2cp_dump_noti";
|
|
mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE].node_name = "gpio_ap2cp_pda_active";
|
|
#if !IS_ENABLED(CONFIG_CP_WRESET_WA)
|
|
mc->cp_gpio[CP_GPIO_AP2CP_CP_WRST_N].node_name = "gpio_ap2cp_cp_wrst_n";
|
|
mc->cp_gpio[CP_GPIO_AP2CP_PM_WRST_N].node_name = "gpio_ap2cp_pm_wrst_n";
|
|
#endif
|
|
mc->cp_gpio[CP_GPIO_CP2AP_PS_HOLD].node_name = "gpio_cp2ap_cp_ps_hold";
|
|
mc->cp_gpio[CP_GPIO_CP2AP_WAKEUP].node_name = "gpio_cp2ap_wake_up";
|
|
mc->cp_gpio[CP_GPIO_CP2AP_CP_ACTIVE].node_name = "gpio_cp2ap_phone_active";
|
|
|
|
/* irq */
|
|
mc->cp_gpio[CP_GPIO_CP2AP_WAKEUP].irq_type = CP_GPIO_IRQ_CP2AP_WAKEUP;
|
|
mc->cp_gpio[CP_GPIO_CP2AP_CP_ACTIVE].irq_type = CP_GPIO_IRQ_CP2AP_CP_ACTIVE;
|
|
|
|
/* gpio */
|
|
for (i = 0; i < CP_GPIO_MAX; i++) {
|
|
mc->cp_gpio[i].num =
|
|
of_get_named_gpio(np, mc->cp_gpio[i].node_name, 0);
|
|
|
|
if (!gpio_is_valid(mc->cp_gpio[i].num))
|
|
continue;
|
|
|
|
mc->cp_gpio[i].valid = true;
|
|
|
|
gpio_request(mc->cp_gpio[i].num, mc->cp_gpio[i].label);
|
|
if (!strncmp(mc->cp_gpio[i].label, "AP2CP", 5))
|
|
gpio_direction_output(mc->cp_gpio[i].num, 0);
|
|
else
|
|
gpio_direction_input(mc->cp_gpio[i].num);
|
|
|
|
if (mc->cp_gpio[i].irq_type != CP_GPIO_IRQ_NONE) {
|
|
mc->cp_gpio_irq[mc->cp_gpio[i].irq_type].num =
|
|
gpio_to_irq(mc->cp_gpio[i].num);
|
|
}
|
|
}
|
|
|
|
/* validate */
|
|
for (i = 0; i < CP_GPIO_MAX; i++) {
|
|
if (!mc->cp_gpio[i].valid) {
|
|
mif_err("Missing some of GPIOs\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
mif_dt_read_u32(np, "mif,int_ap2cp_pcie_link_ack", mc->int_pcie_link_ack);
|
|
mc->int_pcie_link_ack += DOORBELL_INT_ADD;
|
|
|
|
/* Get PCIe Channel Number */
|
|
mif_dt_read_u32(np, "pci_ch_num", mc->pcie_ch_num);
|
|
mif_info("PCIe Channel Number:%d\n", mc->pcie_ch_num);
|
|
|
|
mc->sbi_crash_type_mask = pdata->sbi_crash_type_mask;
|
|
mc->sbi_crash_type_pos = pdata->sbi_crash_type_pos;
|
|
|
|
mc->sbi_ds_det_mask = pdata->sbi_ds_det_mask;
|
|
mc->sbi_ds_det_pos = pdata->sbi_ds_det_pos;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int send_panic_to_cp_notifier(struct notifier_block *nb,
|
|
unsigned long action, void *nb_data)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = s5100_send_panic_noti_ext();
|
|
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_SUSPEND_DURING_VOICE_CALL)
|
|
static int s5100_abox_call_state_notifier(struct notifier_block *nb,
|
|
unsigned long action, void *nb_data)
|
|
{
|
|
struct modem_ctl *mc = container_of(nb, struct modem_ctl, abox_call_state_nb);
|
|
|
|
mif_info("call event = %lu\n", action);
|
|
|
|
switch (action) {
|
|
case ABOX_CALL_EVENT_OFF:
|
|
mc->pcie_voice_call_on = false;
|
|
queue_work_on(RUNTIME_PM_AFFINITY_CORE, mc->wakeup_wq,
|
|
&mc->call_off_work);
|
|
break;
|
|
case ABOX_CALL_EVENT_ON:
|
|
mc->pcie_voice_call_on = true;
|
|
queue_work_on(RUNTIME_PM_AFFINITY_CORE, mc->wakeup_wq,
|
|
&mc->call_on_work);
|
|
break;
|
|
default:
|
|
mif_err("undefined call event = %lu\n", action);
|
|
break;
|
|
}
|
|
|
|
return NOTIFY_DONE;
|
|
}
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_CP_LCD_NOTIFIER)
|
|
static int s5100_lcd_notifier(struct notifier_block *notifier,
|
|
unsigned long event, void *v)
|
|
{
|
|
struct modem_ctl *mc =
|
|
container_of(notifier, struct modem_ctl, lcd_notifier);
|
|
|
|
switch (event) {
|
|
case LCD_OFF:
|
|
mif_info("LCD_OFF Notification\n");
|
|
modem_ctrl_set_kerneltime(mc);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], 0, 0);
|
|
mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], true);
|
|
break;
|
|
|
|
case LCD_ON:
|
|
mif_info("LCD_ON Notification\n");
|
|
modem_ctrl_set_kerneltime(mc);
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], 1, 0);
|
|
mif_gpio_get_value(&mc->cp_gpio[CP_GPIO_AP2CP_AP_ACTIVE], true);
|
|
break;
|
|
|
|
default:
|
|
mif_info("lcd_event %ld\n", event);
|
|
break;
|
|
}
|
|
|
|
return NOTIFY_OK;
|
|
}
|
|
#endif /* CONFIG_CP_LCD_NOTIFIER */
|
|
|
|
int s5100_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
|
|
{
|
|
int ret = 0;
|
|
struct platform_device *pdev = to_platform_device(mc->dev);
|
|
|
|
mif_err("+++\n");
|
|
|
|
g_mc = mc;
|
|
|
|
s5100_get_ops(mc);
|
|
if (s5100_get_pdata(mc, pdata)) {
|
|
mif_err("DT error: failed to parse\n");
|
|
return -EINVAL;
|
|
}
|
|
dev_set_drvdata(mc->dev, mc);
|
|
|
|
mc->ws = cpif_wake_lock_register(&pdev->dev, "s5100_wake_lock");
|
|
if (mc->ws == NULL) {
|
|
mif_err("s5100_wake_lock: wakeup_source_register fail\n");
|
|
return -EINVAL;
|
|
}
|
|
mutex_init(&mc->pcie_onoff_lock);
|
|
mutex_init(&mc->pcie_check_lock);
|
|
spin_lock_init(&mc->pcie_tx_lock);
|
|
spin_lock_init(&mc->pcie_pm_lock);
|
|
#if IS_ENABLED(CONFIG_LINK_DEVICE_PCIE_GPIO_WA)
|
|
atomic_set(&mc->dump_toggle_issued, 0);
|
|
#endif
|
|
|
|
mif_gpio_set_value(&mc->cp_gpio[CP_GPIO_AP2CP_NRESET], 0, 0);
|
|
|
|
mif_info("Register GPIO interrupts\n");
|
|
mc->apwake_irq_chip = irq_get_chip(mc->cp_gpio_irq[CP_GPIO_IRQ_CP2AP_WAKEUP].num);
|
|
if (mc->apwake_irq_chip == NULL) {
|
|
mif_err("Can't get irq_chip structure!!!!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mc->wakeup_wq = create_singlethread_workqueue("cp2ap_wakeup_wq");
|
|
if (!mc->wakeup_wq) {
|
|
mif_err("%s: ERR! fail to create wakeup_wq\n", mc->name);
|
|
return -EINVAL;
|
|
}
|
|
INIT_WORK(&mc->wakeup_work, cp2ap_wakeup_work);
|
|
INIT_WORK(&mc->suspend_work, cp2ap_suspend_work);
|
|
|
|
mc->crash_wq = create_singlethread_workqueue("trigger_cp_crash_wq");
|
|
if (!mc->crash_wq) {
|
|
mif_err("%s: ERR! fail to create crash_wq\n", mc->name);
|
|
return -EINVAL;
|
|
}
|
|
INIT_WORK(&mc->crash_work, trigger_cp_crash_work);
|
|
|
|
mc->reboot_nb.notifier_call = s5100_reboot_handler;
|
|
register_reboot_notifier(&mc->reboot_nb);
|
|
|
|
/* Register PM notifier_call */
|
|
mc->pm_notifier.notifier_call = s5100_pm_notifier;
|
|
ret = register_pm_notifier(&mc->pm_notifier);
|
|
if (ret) {
|
|
mif_err("failed to register PM notifier_call\n");
|
|
return ret;
|
|
}
|
|
|
|
/* Register panic notifier_call*/
|
|
mc->send_panic_nb.notifier_call = send_panic_to_cp_notifier;
|
|
atomic_notifier_chain_register(&panic_notifier_list, &mc->send_panic_nb);
|
|
|
|
#if IS_ENABLED(CONFIG_SUSPEND_DURING_VOICE_CALL)
|
|
INIT_WORK(&mc->call_on_work, voice_call_on_work);
|
|
INIT_WORK(&mc->call_off_work, voice_call_off_work);
|
|
|
|
mc->abox_call_state_nb.notifier_call = s5100_abox_call_state_notifier;
|
|
register_abox_call_event_notifier(&mc->abox_call_state_nb);
|
|
#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;
|
|
}
|