blob: 26828d0142168d7734c04d997859402fe4f3df3c [file] [log] [blame]
/*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/spinlock.h>
#include <linux/suspend.h>
#include <linux/wakeup_reason.h>
#include <linux/gpio.h>
#include <linux/syscore_ops.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/psci.h>
#include <linux/debugfs.h>
#include <asm/cpuidle.h>
#include <asm/smp_plat.h>
#include <soc/samsung/exynos-pm.h>
#include <soc/samsung/exynos-pmu.h>
#include <soc/samsung/exynos-powermode.h>
#ifdef CONFIG_SEC_DEBUG
#include <linux/sec_debug.h>
#define PMU_CP_STAT 0x0038
#define PMU_GNSS_STAT 0x0048
#define PMU_WIFI_STAT 0x0148
#define PMU_CONNECT_REQ_STATUS 0x00d4
#endif
#ifdef CONFIG_SEC_PM
#define WKUP_STAT_SIZE 32
#define WKUP_STAT_NAME_LENGTH 16
struct wakeup_stat {
char name[WKUP_STAT_NAME_LENGTH];
u32 addr;
unsigned long src_check_bit;
const char *wkup_src[WKUP_STAT_SIZE];
int eint_wkup_bit;
};
#endif
extern u32 exynos_eint_to_pin_num(int eint);
struct exynos_wkup_reason {
u32 wkstat_idx;
u32 wkstat_bit;
};
struct exynos_pm_info {
void __iomem *eint_base; /* GPIO_ALIVE base to check wkup reason */
void __iomem *gic_base; /* GICD_ISPENDRn base to check wkup reason */
unsigned int num_eint; /* Total number of EINT sources */
unsigned int num_gic; /* Total number of GIC sources */
bool is_early_wakeup;
bool is_cp_call;
unsigned int suspend_mode_idx; /* power mode to be used in suspend scenario */
unsigned int suspend_psci_idx; /* psci index to be used in suspend scenario */
unsigned int cp_call_mode_idx; /* power mode to be used in cp_call scenario */
unsigned int cp_call_psci_idx; /* psci index to be used in cp_call scenario */
u8 num_wkup_stats; /* Total number of wakeup_stat registers */
unsigned int *wkup_stats; /* Register addresses of WAKEUP_STAT_N registers */
struct exynos_wkup_reason by_eint;
struct exynos_wkup_reason by_rtc_alarm;
unsigned int *eint_wkup_masks; /* Register addresses of EINT_WAKEUP_MASK_N registers */
u8 num_eint_wkup_masks; /* Total number of EINT_WAKEUP_MASK_N registers */
unsigned int *eint_pends; /* Register addresses of EINT pending registers */
u8 num_eint_pends; /* Total number of EINT pending registers */
unsigned int conn_req_offset;
unsigned int prev_conn_req; /* TCXO, PWR, MIF request status of masters */
#ifdef CONFIG_SEC_PM
struct wakeup_stat *ws_extra;
bool print_by_extra;
#endif
};
static struct exynos_pm_info *pm_info;
struct exynos_pm_dbg {
u32 test_early_wakeup;
u32 test_cp_call;
};
static struct exynos_pm_dbg *pm_dbg;
static void exynos_show_wakeup_reason_eint(void)
{
int bit;
int i, size, cnt;
long unsigned int ext_int_pend;
u64 eint_wakeup_mask = 0;
bool found = 0;
unsigned int val;
for (i = 0; i < pm_info->num_eint_wkup_masks; i++) {
exynos_pmu_read(pm_info->eint_wkup_masks[i], &val);
eint_wakeup_mask |= (val << (32 * i));
}
for (i = 0, size = 8, cnt = 0; i < pm_info->num_eint; i += size, cnt++) {
ext_int_pend = __raw_readl(pm_info->eint_base + pm_info->eint_pends[cnt]);
for_each_set_bit(bit, &ext_int_pend, size) {
u32 gpio;
int irq;
if (eint_wakeup_mask & (1 << (i + bit)))
continue;
gpio = exynos_eint_to_pin_num(i + bit);
irq = gpio_to_irq(gpio);
#ifdef CONFIG_SUSPEND
log_wakeup_reason(irq);
#endif
found = 1;
}
}
if (!found)
pr_info("%s Resume caused by unknown EINT\n", EXYNOS_PM_PREFIX);
}
#ifdef CONFIG_SEC_PM
static void __exynos_show_wakeup_registers_extra(void)
{
int i, bit;
unsigned int wkup_stats;
bool is_eint_wkup = false;
for (i = 0; i < pm_info->num_wkup_stats; i++) {
exynos_pmu_read(pm_info->ws_extra[i].addr, &wkup_stats);
pr_info("%s : 0x%08x\n", pm_info->ws_extra[i].name, wkup_stats);
wkup_stats &= pm_info->ws_extra[i].src_check_bit;
if (pm_info->ws_extra[i].eint_wkup_bit >= 0 &&
wkup_stats & (1 << pm_info->ws_extra[i].eint_wkup_bit))
is_eint_wkup = true;
else {
for_each_set_bit(bit, (unsigned long *) &wkup_stats, WKUP_STAT_SIZE)
pr_info("%s Resume caused by %s\n", EXYNOS_PM_PREFIX,
pm_info->ws_extra[i].wkup_src[bit]);
}
}
if (is_eint_wkup) {
pr_info("EINT_PEND: ");
for (i = 0; i < pm_info->num_eint_pends; i++)
pr_info("0x%02x ", __raw_readl(pm_info->eint_base + pm_info->eint_pends[i]));
exynos_show_wakeup_reason_eint();
}
}
static int exynos_show_wakeup_registers_extra(void)
{
if (pm_info->print_by_extra) {
__exynos_show_wakeup_registers_extra();
return 1;
}
return 0;
}
#else
static inline int exynos_show_wakeup_registers_extra(void) { return 0; }
#endif /* !CONFIG_SEC_PM */
static void exynos_show_wakeup_registers(void)
{
int i;
int wkup_stats;
if (exynos_show_wakeup_registers_extra())
return;
pr_info("WAKEUP_STAT:\n");
for (i = 0; i < pm_info->num_wkup_stats; i++) {
exynos_pmu_read(pm_info->wkup_stats[i], &wkup_stats);
pr_info("0x%08x\n", wkup_stats);
if ((i == pm_info->by_eint.wkstat_idx) && (wkup_stats & (1 << pm_info->by_eint.wkstat_bit)))
exynos_show_wakeup_reason_eint();
else if ((i == pm_info->by_rtc_alarm.wkstat_idx) && (wkup_stats & (1 << pm_info->by_rtc_alarm.wkstat_bit)))
pr_info("%s Resume caused by RTC alarm\n", EXYNOS_PM_PREFIX);
}
pr_info("EINT_PEND: ");
for (i = 0; i < pm_info->num_eint_pends; i++)
pr_info("0x%02x ", __raw_readl(pm_info->eint_base + pm_info->eint_pends[i]));
}
static void exynos_show_wakeup_reason(bool sleep_abort)
{
int i;
pr_info("---------- wakeup ----------\n");
if (sleep_abort) {
pr_info("%s early wakeup! Dumping pending registers...\n", EXYNOS_PM_PREFIX);
pr_info("EINT_PEND:\n");
for (i = 0; i < pm_info->num_eint_pends; i++)
pr_info("0x%x\n", __raw_readl(pm_info->eint_base + pm_info->eint_pends[i]));
pr_info("GIC_PEND:\n");
for (i = 0; i < pm_info->num_gic; i++)
pr_info("GICD_ISPENDR[%d] = 0x%x\n", i, __raw_readl(pm_info->gic_base + i*4));
pr_info("%s done.\n", EXYNOS_PM_PREFIX);
return ;
}
exynos_show_wakeup_registers();
}
#ifdef CONFIG_CPU_IDLE
static DEFINE_RWLOCK(exynos_pm_notifier_lock);
static RAW_NOTIFIER_HEAD(exynos_pm_notifier_chain);
int exynos_pm_register_notifier(struct notifier_block *nb)
{
unsigned long flags;
int ret;
write_lock_irqsave(&exynos_pm_notifier_lock, flags);
ret = raw_notifier_chain_register(&exynos_pm_notifier_chain, nb);
write_unlock_irqrestore(&exynos_pm_notifier_lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(exynos_pm_register_notifier);
int exynos_pm_unregister_notifier(struct notifier_block *nb)
{
unsigned long flags;
int ret;
write_lock_irqsave(&exynos_pm_notifier_lock, flags);
ret = raw_notifier_chain_unregister(&exynos_pm_notifier_chain, nb);
write_unlock_irqrestore(&exynos_pm_notifier_lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(exynos_pm_unregister_notifier);
static int __exynos_pm_notify(enum exynos_pm_event event, int nr_to_call, int *nr_calls)
{
int ret;
ret = __raw_notifier_call_chain(&exynos_pm_notifier_chain, event, NULL,
nr_to_call, nr_calls);
return notifier_to_errno(ret);
}
int exynos_pm_notify(enum exynos_pm_event event)
{
int nr_calls;
int ret = 0;
read_lock(&exynos_pm_notifier_lock);
ret = __exynos_pm_notify(event, -1, &nr_calls);
read_unlock(&exynos_pm_notifier_lock);
return ret;
}
EXPORT_SYMBOL_GPL(exynos_pm_notify);
#endif /* CONFIG_CPU_IDLE */
#ifdef CONFIG_SND_SOC_SAMSUNG_VTS
extern bool vts_is_on(void);
#else
static inline bool vts_is_on(void)
{
return 0;
}
#endif
#ifdef CONFIG_SND_SOC_SAMSUNG_ABOX
extern bool abox_is_on(void);
#else
static inline bool abox_is_on(void)
{
return 0;
}
#endif
static int exynos_pm_syscore_suspend(void)
{
#ifdef CONFIG_SEC_DEBUG
unsigned int val;
if (sec_debug_get_debug_level() <= 1) {
exynos_pmu_read(PMU_CP_STAT, &val);
pr_info("CP_STAT (0x%x) = 0x%08x\n", PMU_CP_STAT, val);
exynos_pmu_read(PMU_GNSS_STAT, &val);
pr_info("GNSS_STAT (0x%x) = 0x%08x\n", PMU_GNSS_STAT, val);
exynos_pmu_read(PMU_WIFI_STAT, &val);
pr_info("WIFI_STAT (0x%x) = 0x%08x\n", PMU_WIFI_STAT, val);
exynos_pmu_read(PMU_CONNECT_REQ_STATUS, &val);
pr_info("PMU_CONNECT_REQ_STATUS (0x%x) = 0x%08x\n", PMU_CONNECT_REQ_STATUS, val);
}
#endif
pm_info->is_cp_call = abox_is_on();
if (pm_info->is_cp_call || pm_dbg->test_cp_call) {
exynos_prepare_sys_powerdown(pm_info->cp_call_mode_idx);
pr_info("%s %s: Enter CP Call scenario. (mode_idx = %d)\n",
EXYNOS_PM_PREFIX, __func__, pm_info->cp_call_mode_idx);
} else {
if (vts_is_on())
exynos_prepare_sys_powerdown(SYS_SLEEP_VTS_ON);
else
exynos_prepare_sys_powerdown(pm_info->suspend_mode_idx);
pr_info("%s %s: Enter Suspend scenario. (mode_idx = %d)\n",
EXYNOS_PM_PREFIX,__func__, pm_info->suspend_mode_idx);
}
return 0;
}
static void exynos_pm_syscore_resume(void)
{
if (pm_info->is_cp_call || pm_dbg->test_cp_call)
exynos_wakeup_sys_powerdown(pm_info->cp_call_mode_idx, pm_info->is_early_wakeup);
else {
if (vts_is_on())
exynos_wakeup_sys_powerdown(SYS_SLEEP_VTS_ON, pm_info->is_early_wakeup);
else
exynos_wakeup_sys_powerdown(pm_info->suspend_mode_idx, pm_info->is_early_wakeup);
}
exynos_show_wakeup_reason(pm_info->is_early_wakeup);
if (!pm_info->is_early_wakeup)
pr_debug("%s %s: post sleep, preparing to return\n",
EXYNOS_PM_PREFIX, __func__);
}
static struct syscore_ops exynos_pm_syscore_ops = {
.suspend = exynos_pm_syscore_suspend,
.resume = exynos_pm_syscore_resume,
};
#ifdef CONFIG_SEC_GPIO_DVS
extern void gpio_dvs_check_sleepgpio(void);
#endif
static int exynos_pm_enter(suspend_state_t state)
{
unsigned int psci_index;
unsigned int prev_mif = 0, post_mif = 0;
if (pm_info->is_cp_call || pm_dbg->test_cp_call)
psci_index = pm_info->cp_call_psci_idx;
else
psci_index = pm_info->suspend_psci_idx;
/* Send an IPI if test_early_wakeup flag is set */
if (pm_dbg->test_early_wakeup)
arch_send_call_function_single_ipi(0);
#ifdef CONFIG_SEC_GPIO_DVS
/************************ Caution !!! ****************************/
/* This function must be located in appropriate SLEEP position
* in accordance with the specification of each BB vendor.
*/
/************************ Caution !!! ****************************/
gpio_dvs_check_sleepgpio();
#endif
prev_mif = acpm_get_mifdn_count();
exynos_pmu_read(pm_info->conn_req_offset, &pm_info->prev_conn_req);
/* This will also act as our return point when
* we resume as it saves its own register state and restores it
* during the resume. */
pm_info->is_early_wakeup = (bool)arm_cpuidle_suspend(psci_index);
if (pm_info->is_early_wakeup)
pr_info("%s %s: return to originator\n",
EXYNOS_PM_PREFIX, __func__);
post_mif = acpm_get_mifdn_count();
if (post_mif == prev_mif)
pr_info("%s: MIF blocked. prev_conn_req: 0x%x\n", EXYNOS_PM_PREFIX, pm_info->prev_conn_req);
else
pr_info("%s: MIF down. cur_count: %d, acc_count: %d\n",
EXYNOS_PM_PREFIX, post_mif - prev_mif, post_mif);
return pm_info->is_early_wakeup;
}
static const struct platform_suspend_ops exynos_pm_ops = {
.enter = exynos_pm_enter,
.valid = suspend_valid_only_mem,
};
bool is_test_cp_call_set(void)
{
if (!pm_dbg)
return false;
return pm_dbg->test_cp_call;
}
EXPORT_SYMBOL_GPL(is_test_cp_call_set);
#ifdef CONFIG_DEBUG_FS
static void __init exynos_pm_debugfs_init(void)
{
struct dentry *root, *d;
root = debugfs_create_dir("exynos-pm", NULL);
if (!root) {
pr_err("%s %s: could't create debugfs dir\n", EXYNOS_PM_PREFIX, __func__);
return;
}
d = debugfs_create_u32("test_early_wakeup", 0644, root, &pm_dbg->test_early_wakeup);
if (!d) {
pr_err("%s %s: could't create debugfs test_early_wakeup\n",
EXYNOS_PM_PREFIX, __func__);
return;
}
d = debugfs_create_u32("test_cp_call", 0644, root, &pm_dbg->test_cp_call);
if (!d) {
pr_err("%s %s: could't create debugfs test_cp_call\n",
EXYNOS_PM_PREFIX, __func__);
return;
}
d = debugfs_create_x32("last_mif_blocker", 0444, root, &pm_info->prev_conn_req);
if (!d) {
pr_err("%s %s: could't create debugfs last_mif_blocker\n",
EXYNOS_PM_PREFIX, __func__);
return;
}
}
#endif
#if defined(CONFIG_SEC_FACTORY)
enum ids_info {
tg,
lg,
bg,
g3dg,
mifg,
bids,
gids,
};
extern int asv_ids_information(enum ids_info id);
static ssize_t show_asv_info(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int count = 0;
/* Set asv group info to buf */
count += sprintf(&buf[count], "%d ", asv_ids_information(tg));
count += sprintf(&buf[count], "%03x ", asv_ids_information(bg));
count += sprintf(&buf[count], "%03x ", asv_ids_information(g3dg));
count += sprintf(&buf[count], "%u ", asv_ids_information(bids));
count += sprintf(&buf[count], "%u ", asv_ids_information(gids));
count += sprintf(&buf[count], "\n");
return count;
}
static DEVICE_ATTR(asv_info, 0664, show_asv_info, NULL);
#endif /* CONFIG_SEC_FACTORY */
static __init int exynos_pm_drvinit(void)
{
int ret;
pm_info = kzalloc(sizeof(struct exynos_pm_info), GFP_KERNEL);
if (pm_info == NULL) {
pr_err("%s %s: failed to allocate memory for exynos_pm_info\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
pm_dbg = kzalloc(sizeof(struct exynos_pm_dbg), GFP_KERNEL);
if (pm_dbg == NULL) {
pr_err("%s %s: failed to allocate memory for exynos_pm_dbg\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
if (of_have_populated_dt()) {
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "samsung,exynos-pm");
if (!np) {
pr_err("%s %s: unabled to find compatible node (%s)\n",
EXYNOS_PM_PREFIX, __func__, "samsung,exynos-pm");
BUG();
}
pm_info->eint_base = of_iomap(np, 0);
if (!pm_info->eint_base) {
pr_err("%s %s: unabled to ioremap EINT base address\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
pm_info->gic_base = of_iomap(np, 1);
if (!pm_info->gic_base) {
pr_err("%s %s: unbaled to ioremap GIC base address\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
ret = of_property_read_u32(np, "num-eint", &pm_info->num_eint);
if (ret) {
pr_err("%s %s: unabled to get the number of eint from DT\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
ret = of_property_read_u32(np, "num-gic", &pm_info->num_gic);
if (ret) {
pr_err("%s %s: unabled to get the number of gic from DT\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
ret = of_property_read_u32(np, "suspend_mode_idx", &pm_info->suspend_mode_idx);
if (ret) {
pr_err("%s %s: unabled to get suspend_mode_idx from DT\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
ret = of_property_read_u32(np, "suspend_psci_idx", &pm_info->suspend_psci_idx);
if (ret) {
pr_err("%s %s: unabled to get suspend_psci_idx from DT\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
ret = of_property_read_u32(np, "cp_call_mode_idx", &pm_info->cp_call_mode_idx);
if (ret) {
pr_err("%s %s: unabled to get cp_call_mode_idx from DT\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
ret = of_property_read_u32(np, "cp_call_psci_idx", &pm_info->cp_call_psci_idx);
if (ret) {
pr_err("%s %s: unabled to get cp_call_psci_idx from DT\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
ret = of_property_count_u32_elems(np, "wkup_stats");
if (!ret) {
pr_err("%s %s: unabled to get wakeup_stat value from DT\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
} else {
pm_info->num_wkup_stats = ret;
pm_info->wkup_stats = kzalloc(sizeof(unsigned int) * ret, GFP_KERNEL);
if (pm_info->wkup_stats == NULL) {
pr_err("%s %s: failed to allocate memory for wkup_stats\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
of_property_read_u32_array(np, "wkup_stats", pm_info->wkup_stats, ret);
}
do {
u32 tmp[2];
ret = of_property_read_u32_array(np, "wkup_by_eint", tmp, 2);
if (!ret) {
pm_info->by_eint.wkstat_idx = tmp[0];
pm_info->by_eint.wkstat_bit = tmp[1];
}
ret = of_property_read_u32_array(np, "wkup_by_rtc_alarm", tmp, 2);
if (!ret) {
pm_info->by_rtc_alarm.wkstat_idx = tmp[0];
pm_info->by_rtc_alarm.wkstat_bit = tmp[1];
}
} while(0);
ret = of_property_count_u32_elems(np, "eint_wkup_masks");
if (!ret) {
pr_err("%s %s: unabled to get eint_wakeup_masks value from DT\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
} else {
pm_info->num_eint_wkup_masks = ret;
if (ret > 2) {
pr_err("%s %s: num_eint_wkup_masks should be less than 3.\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
pm_info->eint_wkup_masks = kzalloc(sizeof(unsigned int) * ret, GFP_KERNEL);
of_property_read_u32_array(np, "eint_wkup_masks", pm_info->eint_wkup_masks, ret);
}
ret = of_property_count_u32_elems(np, "eint_pends");
if (!ret) {
pr_err("%s %s: unabled to get eint_pends value from DT\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
} else {
pm_info->num_eint_pends = ret;
pm_info->eint_pends = kzalloc(sizeof(unsigned int) * ret, GFP_KERNEL);
of_property_read_u32_array(np, "eint_pends", pm_info->eint_pends, ret);
}
ret = of_property_read_u32(np, "conn_req_offset", &pm_info->conn_req_offset);
if (ret) {
pr_err("%s %s: unabled to get conn_req_offset value from DT\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
pm_info->prev_conn_req = 0;
} else {
pr_err("%s %s: failed to have populated device tree\n",
EXYNOS_PM_PREFIX, __func__);
BUG();
}
suspend_set_ops(&exynos_pm_ops);
register_syscore_ops(&exynos_pm_syscore_ops);
#ifdef CONFIG_DEBUG_FS
exynos_pm_debugfs_init();
#endif
#if defined(CONFIG_SEC_FACTORY)
/* create sysfs group */
ret = sysfs_create_file(power_kobj, &dev_attr_asv_info.attr);
if (ret)
pr_err("%s: failed to create exynos7885 asv attribute file\n", __func__);
#endif
return 0;
}
arch_initcall(exynos_pm_drvinit);
#ifdef CONFIG_SEC_PM
static int of_property_read_string_array_position(struct device_node *np,
const char *propname, const char **out_strs, int size, unsigned long pos)
{
struct property *prop = of_find_property(np, propname, NULL);
int bit, l = 0, count = 0;
const char *p, *end;
if (!prop)
return -EINVAL;
if (!prop->value)
return -ENODATA;
p = prop->value;
end = p + prop->length;
for_each_set_bit(bit, &pos, size) {
l = strnlen(p, end - p) + 1;
if (p + l > end)
return -EILSEQ;
*(out_strs + bit) = p;
p += l;
count++;
}
return count <= 0 ? -ENODATA : count;
}
static int __init exynos_pm_init_extra(void)
{
struct device_node *np, *w_np;
struct wakeup_stat *ws_data;
const char *buf;
int ret;
if (!of_have_populated_dt())
return 0;
np = of_find_compatible_node(NULL, NULL, "samsung,exynos-pm-extra");
if (!np) {
pr_err("%s %s: unabled to find compatible node (%s)\n",
EXYNOS_PM_PREFIX, __func__, "samsung,exynos-pm-extra");
return 0;
}
np = of_find_node_by_name(np, "wakeup_stats");
if (!np) {
pr_err("%s %s: unabled to get wkup_stats nodes from DT\n",
EXYNOS_PM_PREFIX, __func__);
return 0;
}
pm_info->num_wkup_stats = of_get_child_count(np);
ws_data = kzalloc(sizeof(*ws_data) * pm_info->num_wkup_stats, GFP_KERNEL);
if (ws_data == NULL) {
pr_err("%s %s: could not allocate memory for wakup_stat_data\n",
EXYNOS_PM_PREFIX, __func__);
return -ENOMEM;
}
pm_info->ws_extra = ws_data;
for_each_child_of_node(np, w_np) {
ret = of_property_read_u32(w_np, "addr", &ws_data->addr);
if (ret) {
pr_err("%s %s: unabled to get wkup_stat addr value from DT\n",
EXYNOS_PM_PREFIX, __func__);
return 0;
}
strncpy(ws_data->name, w_np->name, WKUP_STAT_NAME_LENGTH - 1);
ret = of_property_read_string(w_np, "bits_to_check", &buf);
if (ret)
pr_err("%s %s: unabled to get src_check_bit value from DT\n",
EXYNOS_PM_PREFIX, __func__);
else
bitmap_parselist(buf, &ws_data->src_check_bit, WKUP_STAT_SIZE);
ret = of_property_read_string_array_position(w_np, "wakeup_sources",
ws_data->wkup_src, WKUP_STAT_SIZE, ws_data->src_check_bit);
if (ret <= 0 || ret != hweight_long(ws_data->src_check_bit)) {
pr_err("%s %s: bits_to_check set_bits / wkup_src_num (%lu/%d)\n",
EXYNOS_PM_PREFIX, __func__,
hweight_long(ws_data->src_check_bit), ret);
ws_data->src_check_bit = 0;
}
ret = of_property_read_u32(w_np, "eint_wakeup_check_bit",
&ws_data->eint_wkup_bit);
if (ret)
ws_data->eint_wkup_bit = -1;
ws_data++;
}
pm_info->print_by_extra = true;
return 0;
}
subsys_initcall(exynos_pm_init_extra);
#endif /* CONFIG_SEC_PM */