/* * File : pm-sun8iw10.c * Copyright (c) 2011-2020 yanggq.young@allwinnertech.com * * 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 "pm_o.h" standby_space_cfg_t standby_space; __u32 debug_mask = PM_STANDBY_TEST; /* | PM_STANDBY_PRINT_STANDBY | PM_STANDBY_PRINT_RESUME| PM_STANDBY_ENABLE_JTAG; */ int suspend_freq = SUSPEND_FREQ; int suspend_delay_ms = SUSPEND_DELAY_MS; unsigned long time_to_wakeup; #ifdef GET_CYCLE_CNT static int start; static int resume0_period; static int resume1_period; static int pm_start; static int invalidate_data_time; static int invalidate_instruct_time; static int before_restore_processor; static int after_restore_process; /*static int restore_runtime_peroid;*/ /*late_resume timing*/ static int late_resume_start; static int backup_area_start; static int backup_area1_start; static int backup_area2_start; static int clk_restore_start; static int gpio_restore_start; static int twi_restore_start; static int int_restore_start; static int tmr_restore_start; static int sram_restore_start; static int late_resume_end; #endif struct aw_mem_para mem_para_info; struct super_standby_para super_standby_para_info; const extended_standby_manager_t *extended_standby_manager_id; standby_type_e standby_type = NON_STANDBY; EXPORT_SYMBOL(standby_type); standby_level_e standby_level = STANDBY_INITIAL; EXPORT_SYMBOL(standby_level); /*static volatile int enter_flag;*/ int standby_mode; __mem_tmr_reg_t saved_tmr_state; struct aw_pm_info standby_info = { .standby_para = { .event = CPU0_MEM_WAKEUP, .axp_event = CPUS_MEM_WAKEUP, .timeout = 0, }, .pmu_arg = { .twi_port = 0, .dev_addr = 10, }, }; void aw_pm_dump_regs(const char *str, u32 *bottom, u32 *top) { u32 *first; int i; pr_info("%s reg state: (0x%p to 0x%p)", str, bottom, top); for (first = bottom; first < top; first += 4) { u32 *p; char str[sizeof(" 0x12345678") * 4 + 1]; memset(str, ' ', sizeof(str)); str[sizeof(str) - 1] = '\0'; for (p = first, i = 0; i < 4 && p < top; i++, p++) { if (p >= bottom && p < top) { sprintf(str + i * 11, " 0x%08x", *(volatile u32 *)p); } } pr_info("0x%p:%s\n", first, str); } } void aw_pm_dev_status(char *name, int mask) { u32 *base = 0; u32 len = 0; if (unlikely(debug_mask & mask)) { pm_get_dev_info(name, 0, &base, &len); len /= sizeof(u32); aw_pm_dump_regs(name, base, base + len); if (!strncmp(name, "pio", 3)) sunxi_pinctrl_state_show(); } return; } void aw_pm_show_dev_status(void) { aw_pm_dev_status("pio", PM_STANDBY_PRINT_IO_STATUS); aw_pm_dev_status("r_pio", PM_STANDBY_PRINT_CPUS_IO_STATUS); aw_pm_dev_status("clocks", PM_STANDBY_PRINT_CCU_STATUS); return; } static char *parse_debug_mask(unsigned int bitmap, char *s, char *end) { int i = 0; int counted = 0; int count = 0; unsigned int bit_event = 0; for (i = 0; i < 32; i++) { bit_event = (1 << i & bitmap); switch (bit_event) { case 0: break; case PM_STANDBY_PRINT_STANDBY: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_PRINT_STANDBY ", PM_STANDBY_PRINT_STANDBY); count++; break; case PM_STANDBY_PRINT_RESUME: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_PRINT_RESUME ", PM_STANDBY_PRINT_RESUME); count++; break; case PM_STANDBY_ENABLE_JTAG: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_ENABLE_JTAG ", PM_STANDBY_ENABLE_JTAG); count++; break; case PM_STANDBY_PRINT_PORT: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_PRINT_PORT ", PM_STANDBY_PRINT_PORT); count++; break; case PM_STANDBY_PRINT_IO_STATUS: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_PRINT_IO_STATUS ", PM_STANDBY_PRINT_IO_STATUS); count++; break; case PM_STANDBY_PRINT_CACHE_TLB_MISS: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_PRINT_CACHE_TLB_MISS ", PM_STANDBY_PRINT_CACHE_TLB_MISS); count++; break; case PM_STANDBY_PRINT_CCU_STATUS: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_PRINT_CCU_STATUS ", PM_STANDBY_PRINT_CCU_STATUS); count++; break; case PM_STANDBY_PRINT_PWR_STATUS: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_PRINT_PWR_STATUS ", PM_STANDBY_PRINT_PWR_STATUS); count++; break; case PM_STANDBY_PRINT_CPUS_IO_STATUS: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_PRINT_CPUS_IO_STATUS ", PM_STANDBY_PRINT_CPUS_IO_STATUS); count++; break; case PM_STANDBY_PRINT_CCI400_REG: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_PRINT_CCI400_REG ", PM_STANDBY_PRINT_CCI400_REG); count++; break; case PM_STANDBY_PRINT_GTBUS_REG: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_PRINT_GTBUS_REG ", PM_STANDBY_PRINT_GTBUS_REG); count++; break; case PM_STANDBY_TEST: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_TEST ", PM_STANDBY_TEST); count++; break; case PM_STANDBY_PRINT_RESUME_IO_STATUS: s += scnprintf(s, end - s, "%-34s bit 0x%x\t", "PM_STANDBY_PRINT_RESUME_IO_STATUS", PM_STANDBY_PRINT_RESUME_IO_STATUS); count++; break; default: break; } if (counted != count && 0 == count % 2) { counted = count; s += scnprintf(s, end - s, "\n"); } } s += scnprintf(s, end - s, "\n"); return s; } ssize_t debug_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { char *s = buf; char *end = buf + PAGE_SIZE; s += sprintf(buf, "0x%x\n", debug_mask); s = parse_debug_mask(debug_mask, s, end); s += sprintf(s, "%s\n", "debug_mask usage help info:"); s += sprintf(s, "%s\n", "target: for enable checking the io, ccu... suspended status."); s += sprintf(s, "%s\n", "bitmap: each bit corresponding one module, as follow:"); s = parse_debug_mask(0xffff, s, end); return s - buf; } #define TMPBUFLEN 22 static int get_long(const char **buf, size_t *size, unsigned long *val, bool *neg) { size_t len; char *p, tmp[TMPBUFLEN]; if (!*size) return -EINVAL; len = *size; if (len > TMPBUFLEN - 1) len = TMPBUFLEN - 1; memcpy(tmp, *buf, len); tmp[len] = 0; p = tmp; if (*p == '-' && *size > 1) { *neg = true; p++; } else *neg = false; if (!isdigit(*p)) return -EINVAL; *val = simple_strtoul(p, &p, 0); len = p - tmp; /* We don't know if the next char is whitespace thus we may accept * invalid integers (e.g. 1234...a) or two integers instead of one * (e.g. 123...1). So lets not allow such large numbers. */ if (len == TMPBUFLEN - 1) return -EINVAL; return 0; } ssize_t debug_mask_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long data = 0; bool neg = false; if (!get_long(&buf, &count, &data, &neg)) { if (true != neg) { debug_mask = (unsigned int)data; } else { printk("%s\n", "minus is Illegal. "); return -EINVAL; } } else { printk("%s\n", "non-digital is Illegal. "); return -EINVAL; } return count; } ssize_t parse_status_code_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned int status_code = 0; unsigned int index = 0; sscanf(buf, "%x %x\n", &status_code, &index); parse_status_code(status_code, index); return size; } ssize_t parse_status_code_show(struct device *dev, struct device_attribute *attr, char *buf) { char *s = buf; show_mem_status(); return s - buf; } #if (defined(CONFIG_ARCH_SUN8IW8P1) || \ defined(CONFIG_ARCH_SUN8IW17P1) || \ defined(CONFIG_ARCH_SUN8IW6P1) || \ defined(CONFIG_ARCH_SUN50IW1P1) || \ defined(CONFIG_ARCH_SUN50IW2P1) || \ defined(CONFIG_ARCH_SUN50IW3P1) || \ defined(CONFIG_ARCH_SUN50IW6P1)) void init_wakeup_src(unsigned int event, unsigned int gpio_enable_bitmap, unsigned int cpux_gpiog_bitmap) { /*config int src. */ mem_int_save(); mem_tmr_init(); mem_tmr_save(&(saved_tmr_state)); if (event & CPU0_WAKEUP_TIMEOUT) { printk("enable CPUS_WAKEUP_TIMEOUT. \n"); /* set timer for power off */ if (super_standby_para_info.timeout) { mem_tmr_set(super_standby_para_info.timeout); mem_enable_int(INT_SOURCE_TIMER1); } } return; } void exit_wakeup_src(unsigned int event, unsigned int gpio_enable_bitmap, unsigned int cpux_gpiog_bitmap) { mem_tmr_restore(&(saved_tmr_state)); mem_tmr_exit(); mem_int_restore(); return; } #endif #if (defined(CONFIG_ARCH_SUN8IW8P1) || \ defined(CONFIG_ARCH_SUN8IW6P1) || \ defined(CONFIG_ARCH_SUN8IW10P1) || \ defined(CONFIG_ARCH_SUN8IW11P1) || \ defined(CONFIG_ARCH_SUN8IW17P1) || \ defined(CONFIG_ARCH_SUN50IW1P1) || \ defined(CONFIG_ARCH_SUN50IW2P1) || \ defined(CONFIG_ARCH_SUN50IW3P1) || \ defined(CONFIG_ARCH_SUN50IW6P1)) && \ defined(CONFIG_AW_AXP) static unsigned int pwr_dm_mask_saved; static int save_sys_pwr_state(const char *id) { int bitmap = 0; bitmap = is_sys_pwr_dm_id(id); if ((-1) != bitmap) { pwr_dm_mask_saved |= (0x1 << bitmap); } else { pm_printk(PM_STANDBY_PRINT_PWR_STATUS, "%s: is not sys\n", id); } return 0; } int resume_sys_pwr_state(void) { int i = 0, ret = -1; char *sys_name = NULL; for (i = 0; i < 32; i++) { if (pwr_dm_mask_saved & (0x1 << i)) { sys_name = get_sys_pwr_dm_id(i); if (NULL != sys_name) { ret = add_sys_pwr_dm(sys_name); if (ret < 0) { pm_printk(PM_STANDBY_PRINT_PWR_STATUS, "%s: resume failed\n", sys_name); } } } } pwr_dm_mask_saved = 0; return 0; } static int check_sys_pwr_dm_status(char *pwr_dm) { char ldo_name[20] = "\0"; char enable_id[20] = "\0"; int ret = 0; int i = 0; ret = get_ldo_name(pwr_dm, ldo_name); if (ret < 0) { pm_printk(PM_STANDBY_PRINT_PWR_STATUS, " %s: get %s failed. ret = %d\n", __func__, pwr_dm, ret); return -1; } ret = get_enable_id_count(ldo_name); if (0 == ret) { pm_printk(PM_STANDBY_PRINT_PWR_STATUS, "%s: no child, use by %s, property: sys.\n", ldo_name, pwr_dm); } else { for (i = 0; i < ret; i++) { get_enable_id(ldo_name, i, (char *)enable_id); printk(KERN_INFO "%s: active child id %d is: %s. ", ldo_name, i, enable_id); /*need to check all enabled id is belong to sys_pwr_dm. */ if ((-1) != is_sys_pwr_dm_id(enable_id)) { pm_printk(PM_STANDBY_PRINT_PWR_STATUS, "property: sys\n"); } else { pm_printk(PM_STANDBY_PRINT_PWR_STATUS, "property: module\n"); del_sys_pwr_dm(pwr_dm); save_sys_pwr_state(pwr_dm); break; } } } return 0; } int check_pwr_status(void) { check_sys_pwr_dm_status("vdd-cpua"); check_sys_pwr_dm_status("vdd-cpub"); check_sys_pwr_dm_status("vcc-dram"); check_sys_pwr_dm_status("vdd-gpu"); check_sys_pwr_dm_status("vdd-sys"); check_sys_pwr_dm_status("vdd-vpu"); check_sys_pwr_dm_status("vdd-cpus"); check_sys_pwr_dm_status("vdd-drampll"); check_sys_pwr_dm_status("vcc-lpddr"); check_sys_pwr_dm_status("vcc-adc"); check_sys_pwr_dm_status("vcc-pl"); check_sys_pwr_dm_status("vcc-pm"); check_sys_pwr_dm_status("vcc-io"); check_sys_pwr_dm_status("vcc-cpvdd"); check_sys_pwr_dm_status("vcc-ldoin"); check_sys_pwr_dm_status("vcc-pll"); check_sys_pwr_dm_status("vcc-pc"); return 0; } int init_sys_pwr_dm(void) { unsigned int sys_mask = 0; add_sys_pwr_dm("vdd-cpua"); /*add_sys_pwr_dm("vdd-cpub"); */ add_sys_pwr_dm("vcc-dram"); /*add_sys_pwr_dm("vdd-gpu"); */ add_sys_pwr_dm("vdd-sys"); /*add_sys_pwr_dm("vdd-vpu"); */ add_sys_pwr_dm("vdd-cpus"); /*add_sys_pwr_dm("vdd-drampll"); */ add_sys_pwr_dm("vcc-lpddr"); /*add_sys_pwr_dm("vcc-adc"); */ add_sys_pwr_dm("vcc-pl"); /*add_sys_pwr_dm("vcc-pm"); */ add_sys_pwr_dm("vcc-io"); /*add_sys_pwr_dm("vcc-cpvdd"); */ #if defined CONFIG_ARCH_SUN50IW6P1 add_sys_pwr_dm("vcc-ldoin"); #endif add_sys_pwr_dm("vcc-pll"); add_sys_pwr_dm("vcc-pc"); sys_mask = get_sys_pwr_dm_mask(); printk(KERN_INFO "after inited: sys_mask config = 0x%x. \n", sys_mask); return 0; } #endif #if (defined(CONFIG_ARCH_SUN50IW1P1) || \ defined(CONFIG_ARCH_SUN50IW2P1) || \ defined(CONFIG_ARCH_SUN50IW3P1) || \ defined(CONFIG_ARCH_SUN50IW6P1)) && \ defined(CONFIG_AW_AXP) static int config_pmux_para(unsigned num) { #define PM_NAME_LEN (25) char name[PM_NAME_LEN] = "\0"; int enable = 0; int pmux_id = 0; int pmux_twi_id = 0; int pmux_twi_addr = 0; struct device_node *np; sprintf(name, "pmic%d", num); printk("pmu name: %s .\n", name); /*get pmu config */ np = of_find_node_by_type(NULL, name); if (NULL == np) { printk(KERN_ERR "Warning: can not find np for %s. \n", name); } else { if (!of_device_is_available(np)) { enable = 0; } else { enable = 1; } printk(KERN_INFO "%s_enable = 0x%x. \n", name, enable); if (1 == enable) { if (of_property_read_u32(np, "pmu_id", &pmux_id)) { pr_err("Warning: %s fetch pmu_id err. \n", __func__); pmux_id = AXP_22X_ID; } printk(KERN_INFO "pmux_id = 0x%x. \n", pmux_id); extended_standby_set_pmu_id(num, pmux_id); if (of_property_read_u32 (np, "pmu_twi_id", &pmux_twi_id)) { pr_err ("Warning: %s fetch pmu_twi_id err. \n", __func__); standby_info.pmu_arg.twi_port = 0; } else { standby_info.pmu_arg.twi_port = pmux_twi_id; } printk(KERN_INFO "pmux_twi_id = 0x%x. \n", standby_info.pmu_arg.twi_port); if (of_property_read_u32 (np, "pmu_twi_addr", &pmux_twi_addr)) { pr_err ("Warning: %s: fetch pmu_twi_addr err. \n", __func__); standby_info.pmu_arg.dev_addr = 0x34; } else { standby_info.pmu_arg.dev_addr = pmux_twi_addr; } printk(KERN_INFO "pmux_twi_addr = 0x%x. \n", standby_info.pmu_arg.dev_addr); } } return 0; } int config_pmu_para(void) { config_pmux_para(0); config_pmux_para(1); return 0; } #endif #if (defined(CONFIG_ARCH_SUN8IW8P1) || defined(CONFIG_ARCH_SUN8IW6P1) \ || defined(CONFIG_ARCH_SUN8IW10P1) || defined(CONFIG_ARCH_SUN8IW11P1) \ || defined(CONFIG_ARCH_SUN8IW17P1)) && defined(CONFIG_AW_AXP) extern int config_pmux_para(int num, struct aw_pm_info *api, int *pmu_id); int config_pmu_para(void) { int pmux_id = 0; if (0 == config_pmux_para(0, &standby_info, &pmux_id)) extended_standby_set_pmu_id(0, pmux_id); else pr_info("pmu0 does not exist. \n"); if (0 == config_pmux_para(1, &standby_info, &pmux_id)) extended_standby_set_pmu_id(1, pmux_id); else pr_info("pmu1 does not exist. \n"); return 0; } #endif #if (defined(CONFIG_ARCH_SUN8IW8P1) || \ defined(CONFIG_ARCH_SUN8IW6P1) || \ defined(CONFIG_ARCH_SUN8IW10P1) || \ defined(CONFIG_ARCH_SUN8IW11P1) || \ defined(CONFIG_ARCH_SUN8IW17P1) || \ defined(CONFIG_ARCH_SUN50IW1P1) || \ defined(CONFIG_ARCH_SUN50IW2P1) || \ defined(CONFIG_ARCH_SUN50IW3P1) || \ defined(CONFIG_ARCH_SUN50IW6P1)) && \ defined(CONFIG_AW_AXP) int config_dynamic_standby(void) { aw_power_scene_e type = SCENE_DYNAMIC_STANDBY; scene_extended_standby_t *local_standby; int enable = 0; int dram_selfresh_flag = 1; unsigned int vdd_cpua_vol = 0; unsigned int vdd_sys_vol = 0; struct device_node *np; char *name = "dynamic_standby_para"; int i = 0; int ret = 0; /*get customer customized dynamic_standby config */ np = of_find_node_by_type(NULL, name); if (NULL == np) { printk(KERN_ERR "Warning: can not find np for %s. \n", name); } else { if (!of_device_is_available(np)) { enable = 0; } else { enable = 1; } printk(KERN_INFO "Warning: %s_enable = 0x%x. \n", name, enable); if (1 == enable) { for (i = 0; i < extended_standby_cnt; i++) { if (type == extended_standby[i].scene_type) { /*config dram_selfresh flag; */ local_standby = &(extended_standby[i]); if (of_property_read_u32 (np, "dram_selfresh_flag", &dram_selfresh_flag)) { printk(KERN_ERR "%s: fetch dram_selfresh_flag err. \n", __func__); dram_selfresh_flag = 1; } printk(KERN_INFO "dynamic_standby dram selfresh flag = 0x%x. \n", dram_selfresh_flag); if (0 == dram_selfresh_flag) { local_standby-> soc_pwr_dep.soc_dram_state.selfresh_flag = dram_selfresh_flag; local_standby-> soc_pwr_dep.soc_pwr_dm_state.state |= BITMAP(VDD_SYS_BIT); local_standby->soc_pwr_dep.cpux_clk_state.osc_en |= 0xf; /* mean all osc is on. */ /*mean pll5 is shutdowned & open by dram driver. */ /*hsic can't closed. */ /*periph is needed. */ local_standby-> soc_pwr_dep.cpux_clk_state.init_pll_dis |= (BITMAP(PM_PLL_HSIC) | BITMAP(PM_PLL_PERIPH) | BITMAP(PM_PLL_DRAM)); } /*config other flag? */ } /*config other extended_standby? */ } if (of_property_read_u32 (np, "vdd_cpua_vol", &vdd_cpua_vol)) { } else { printk(KERN_INFO "vdd_cpua_vol = 0x%x. \n", vdd_cpua_vol); ret = scene_set_volt(SCENE_DYNAMIC_STANDBY, VDD_CPUA_BIT, vdd_cpua_vol); if (ret < 0) printk(KERN_ERR "%s: set vdd_cpua volt failed\n", __func__); } if (of_property_read_u32 (np, "vdd_sys_vol", &vdd_sys_vol)) { } else { printk(KERN_INFO "vdd_sys_vol = 0x%x. \n", vdd_sys_vol); ret = scene_set_volt(SCENE_DYNAMIC_STANDBY, VDD_SYS_BIT, vdd_sys_vol); if (ret < 0) printk(KERN_ERR "%s: set vdd_sys volt failed\n", __func__); } printk(KERN_INFO "enable dynamic_standby by customer.\n"); scene_lock_store(NULL, NULL, "dynamic_standby", 0); } } return 0; } static int config_sys_pwr_dm(struct device_node *np, char *pwr_dm) { int dm_enable = 0; if (of_property_read_u32(np, pwr_dm, &dm_enable)) { } else { printk("%s: dm_enalbe: %d. \n", pwr_dm, dm_enable); if (0 == dm_enable) { del_sys_pwr_dm(pwr_dm); save_sys_pwr_state(pwr_dm); } else { add_sys_pwr_dm(pwr_dm); } } return 0; } int config_sys_pwr(void) { unsigned int sys_mask = 0; struct device_node *np; char *name = "sys_pwr_dm_para"; np = of_find_node_by_type(NULL, name); if (NULL == np) { printk(KERN_INFO "info: can not find np for %s. \n", name); } else { config_sys_pwr_dm(np, "vdd-cpua"); config_sys_pwr_dm(np, "vdd-cpub"); config_sys_pwr_dm(np, "vcc-dram"); config_sys_pwr_dm(np, "vdd-gpu"); config_sys_pwr_dm(np, "vdd-sys"); config_sys_pwr_dm(np, "vdd-vpu"); config_sys_pwr_dm(np, "vdd-cpus"); config_sys_pwr_dm(np, "vdd-drampll"); config_sys_pwr_dm(np, "vcc-lpddr"); config_sys_pwr_dm(np, "vcc-adc"); config_sys_pwr_dm(np, "vcc-pl"); config_sys_pwr_dm(np, "vcc-pm"); config_sys_pwr_dm(np, "vcc-io"); config_sys_pwr_dm(np, "vcc-cpvdd"); config_sys_pwr_dm(np, "vcc-ldoin"); config_sys_pwr_dm(np, "vcc-pll"); } sys_mask = get_sys_pwr_dm_mask(); printk(KERN_INFO "after customized: sys_mask config = 0x%x. \n", sys_mask); return 0; } #endif