kernel_samsung_a53x/drivers/soc/samsung/cpif/modem_ctrl_s5100.c

1763 lines
46 KiB
C
Raw Normal View History

2024-06-15 16:02:09 -03:00
// 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;
}