blob: a4f4051522b66683cbf484bc41f6de34d19cc1bc [file] [log] [blame]
/*
* drivers/amlogic/pm/m8b_pm.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/pm.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/fs.h>
#include <asm/cacheflush.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/amlogic/iomap.h>
#include <linux/init.h>
#include <linux/of.h>
#include <asm/compiler.h>
#include <linux/errno.h>
#include <linux/suspend.h>
#include <asm/suspend.h>
#include <linux/of_address.h>
#include <linux/input.h>
#include <linux/arm-smccc.h>
#include <linux/amlogic/pm.h>
#include <linux/kobject.h>
#include <../kernel/power/power.h>
#include <linux/of_reserved_mem.h>
#include <../mach-meson/platsmp.h>
#include <linux/amlogic/pm.h>
#ifdef CONFIG_MESON_TRUSTZONE
#include <mach/meson-secure.h>
#endif
#ifdef CONFIG_SUSPEND_WATCHDOG
#include <mach/watchdog.h>
#endif /* CONFIG_SUSPEND_WATCHDOG */
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
static struct early_suspend early_suspend;
static int early_suspend_flag;
#endif
//#define CONFIG_AO_TRIG_CLK 1
#ifdef CONFIG_AO_TRIG_CLK
#include "arc_trig_clk.h"
#endif
#define ON 1
#define OFF 0
//appf functions
#define APPF_INITIALIZE 0
#define APPF_POWER_DOWN_CPU 1
#define APPF_POWER_UP_CPUS 2
//appf flags
#define APPF_SAVE_PMU (1<<0)
#define APPF_SAVE_TIMERS (1<<1)
#define APPF_SAVE_VFP (1<<2)
#define APPF_SAVE_DEBUG (1<<3)
#define APPF_SAVE_L2 (1<<4)
/******************
***You need sync this param struct with arc_pwr.h is suspend firmware.
***1st word is used for arc output control: serial_disable.
***2nd word...
***
***If you need transfer more params,
***you need sync the struck define in arc_pwr.h
*******************/
unsigned int arc_serial_disable;
static ulong suspend_firm_addr;
static ulong suspend_firm_size;
static suspend_state_t pm_state;
#if 0
static void ao_uart_change_buad(unsigned int reg, unsigned int clk_rate)
{
aml_aobus_update_bits(reg, 0x7FFFFF, 0);
aml_aobus_update_bits(reg, 0xffffff,
(((clk_rate / (115200 * 4)) - 1) & 0x7fffff)|(1<<23));
}
#endif
static void wait_uart_empty(void)
{
do {
udelay(100);
} while ((aml_read_aobus(AO_UART_STATUS) & (1 << 22)) == 0);
}
void clk_switch(int flag)
{
if (flag) {
//uart_rate_clk = clk_get_rate(clk81);
wait_uart_empty();
//gate on pll
aml_cbus_update_bits(HHI_MPEG_CLK_CNTL, 1<<7, 1<<7);
udelay(10);
//switch to pll
aml_cbus_update_bits(HHI_MPEG_CLK_CNTL, 1<<8, 1<<8);
udelay(10);
// ao_uart_change_buad(AO_UART_REG5,167*1000*1000);
} else {
//uart_rate_clk = clk_get_rate(clkxtal);
wait_uart_empty();
// gate off from pll
aml_cbus_update_bits(HHI_MPEG_CLK_CNTL, 1<<8, 0);
udelay(10);
// switch to 24M
aml_cbus_update_bits(HHI_MPEG_CLK_CNTL, 1<<7, 0);
udelay(10);
// ao_uart_change_buad(AO_UART_REG5,24*1000*1000);
}
}
EXPORT_SYMBOL(clk_switch);
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
static void meson_system_early_suspend(struct early_suspend *h)
{
if (!early_suspend_flag)
early_suspend_flag = 1;
}
static void meson_system_late_resume(struct early_suspend *h)
{
if (early_suspend_flag)
early_suspend_flag = 0;
}
#endif
unsigned int get_resume_method(void)
{
return 0;
}
EXPORT_SYMBOL(get_resume_method);
int meson_power_suspend(ulong addr)
{
static int test_flag;
void (*pwrtest_entry)(unsigned int,
unsigned int, unsigned int, unsigned int);
// check_in_param();
flush_cache_all();
pwrtest_entry = (void (*)(unsigned int,
unsigned int, unsigned int, unsigned int))addr;
if (test_flag != 1234) {
test_flag = 1234;
pr_info("initial appf\n");
pr_info("entry=0x%x 0x%x\n",
*(unsigned int *)(addr), *(unsigned int *)(addr+4));
pwrtest_entry(APPF_INITIALIZE,
0, 0, IO_PL310_BASE & 0xffff0000);
}
#ifdef CONFIG_SUSPEND_WATCHDOG
DISABLE_SUSPEND_WATCHDOG;
#endif
pr_info("power down cpu --\n");
pwrtest_entry(APPF_POWER_DOWN_CPU, 0, 0,
APPF_SAVE_PMU | APPF_SAVE_VFP | APPF_SAVE_L2
| (IO_PL310_BASE & 0xffff0000));
#ifdef CONFIG_SUSPEND_WATCHDOG
ENABLE_SUSPEND_WATCHDOG;
#endif
return 0;
}
static void meson_pm_suspend(void)
{
pr_info("enter meson_pm_suspend!\n");
#ifdef CONFIG_SUSPEND_WATCHDOG
ENABLE_SUSPEND_WATCHDOG;
#endif
clk_switch(OFF);
pr_info("sleep ...\n");
//switch A9 clock to xtal 24MHz
aml_cbus_update_bits(HHI_SYS_CPU_CLK_CNTL, 1 << 7, 0);
//disable sys pll
aml_cbus_update_bits(HHI_SYS_PLL_CNTL, 1 << 30, 0);
#ifdef CONFIG_MESON_TRUSTZONE
meson_suspend_firmware();
#else
meson_power_suspend(0xc4d04400);
#endif
//enable sys pll
aml_cbus_update_bits(HHI_SYS_PLL_CNTL, 1 << 30, 1 << 30);
pr_info("... wake up\n");
if (aml_read_aobus(AO_RTC_ADDR1) & (1<<12)) {
/* Woke from alarm, not power button.
*Set flag to inform key_input driver.
*/
aml_write_aobus(AO_RTI_STATUS_REG2, FLAG_WAKEUP_ALARM);
}
/* clear RTC interrupt*/
aml_write_aobus(AO_RTC_ADDR1,
aml_read_aobus(AO_RTC_ADDR1) | (0xf000));
pr_info("RTCADD3=0x%x\n", aml_read_aobus(AO_RTC_ADDR3));
if (aml_read_aobus(AO_RTC_ADDR3) | (1<<29)) {
aml_write_aobus(AO_RTC_ADDR3,
aml_read_aobus(AO_RTC_ADDR3) & (~(1<<29)));
udelay(1000);
}
pr_info("RTCADD3=0x%x\n", aml_read_aobus(AO_RTC_ADDR3));
/*wait_uart_empty();*/
//a9 use pll
aml_cbus_update_bits(HHI_SYS_CPU_CLK_CNTL, 1 << 7, 1 << 7);
clk_switch(ON);
#ifdef CONFIG_AO_TRIG_CLK
run_arc_program();
#endif
}
static int meson_pm_prepare(void)
{
pr_info("enter meson_pm_prepare!\n");
return 0;
}
static int meson_pm_enter(suspend_state_t state)
{
int ret = 0;
switch (state) {
case PM_SUSPEND_STANDBY:
case PM_SUSPEND_MEM:
meson_pm_suspend();
break;
default:
ret = -EINVAL;
}
return ret;
}
static void meson_pm_finish(void)
{
pr_info("enter meson_pm_finish!\n");
}
static const struct platform_suspend_ops meson_pm_ops = {
.enter = meson_pm_enter,
.prepare = meson_pm_prepare,
.finish = meson_pm_finish,
.valid = suspend_valid_only_mem,
};
unsigned int is_pm_freeze_mode(void)
{
if (pm_state == PM_SUSPEND_FREEZE)
return 1;
else
return 0;
}
EXPORT_SYMBOL(is_pm_freeze_mode);
static int frz_begin(void)
{
pm_state = PM_SUSPEND_FREEZE;
return 0;
}
static void frz_end(void)
{
pm_state = PM_SUSPEND_ON;
}
static const struct platform_freeze_ops meson_m8b_frz_ops = {
.begin = frz_begin,
.end = frz_end,
};
static int __init meson_pm_probe(struct platform_device *pdev)
{
pr_info("enter meson_pm_probe!\n");
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
early_suspend.suspend = meson_system_early_suspend;
early_suspend.resume = meson_system_late_resume;
register_early_suspend(&early_suspend);
#endif
//ioremap_page_range(0xc4d00000,
//0xc4d00000+suspend_firm_addr, suspend_firm_addr, vm_page_prot);
suspend_set_ops(&meson_pm_ops);
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
if (lgcy_early_suspend_init())
return -1;
#endif
freeze_set_ops(&meson_m8b_frz_ops);
pr_info("meson_pm_probe done !\n");
#ifdef CONFIG_AO_TRIG_CLK
return run_arc_program();
#else
return 0;
#endif
}
static int __init rmem_pm_setup(struct reserved_mem *rmem)
{
suspend_firm_addr = (ulong)rmem->base;
suspend_firm_size = (ulong)rmem->size;
return 0;
}
RESERVEDMEM_OF_DECLARE(pm_meson, "amlogic, pm-m8b-reserve", rmem_pm_setup);
static int __exit meson_pm_remove(struct platform_device *pdev)
{
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
unregister_early_suspend(&early_suspend);
#endif
return 0;
}
static const struct of_device_id amlogic_pm_dt_match[] = {
{ .compatible = "amlogic, pm-m8b",
},
};
static struct platform_driver meson_pm_driver = {
.driver = {
.name = "pm-meson",
.owner = THIS_MODULE,
.of_match_table = amlogic_pm_dt_match,
},
.remove = __exit_p(meson_pm_remove),
};
static int __init meson_pm_init(void)
{
pr_info("enter %s\n", __func__);
return platform_driver_probe(&meson_pm_driver, meson_pm_probe);
}
late_initcall(meson_pm_init);