kernel_samsung_a53x/drivers/soc/samsung/cpif/modem_ctrl_s5100.c
Sultan Alsawaf 7a02051d8c soc/google/cpif: Don't pin workers onto CPU2
There's no reason to pin workers onto CPU2. Instead, allow the workers to
run where the workqueue sees fit, most likely on the CPU which enqueues the
worker, which is better than always dumping the workers onto CPU2.

Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com>
2025-01-23 19:49:06 +01:00

1756 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)
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(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(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(mc->wakeup_wq, &mc->call_off_work);
break;
case ABOX_CALL_EVENT_ON:
mc->pcie_voice_call_on = true;
queue_work(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;
}