1
0
Fork 0

powerpc/pm: add sleep and deep sleep on QorIQ SoCs

In sleep mode, the clocks of CPU core and unused IP blocks are turned
off (IP blocks allowed to wake up system will running).

Some QorIQ SoCs like MPC8536, P1022 and T104x, have deep sleep PM mode
in addtion to the sleep PM mode. While in deep sleep mode,
additionally, the power supply is removed from CPU core and most IP
blocks. Only the blocks needed to wake up the chip out of deep sleep
are ON.

This feature supports 32-bit and 36-bit address space.

The sleep mode is equal to the Standby state in Linux. The deep sleep
mode is equal to the Suspend-to-RAM state of Linux Power Management.
    Command to enter sleep mode.
        echo standby > /sys/power/state
    Command to enter deep sleep mode.
        echo mem > /sys/power/state

Signed-off-by: Dave Liu <daveliu@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
Signed-off-by: Jin Qing <b24347@freescale.com>
Signed-off-by: Jerry Huang <Chang-Ming.Huang@freescale.com>
Signed-off-by: Ramneek Mehresh <ramneek.mehresh@freescale.com>
Signed-off-by: Zhao Chenhui <chenhui.zhao@freescale.com>
Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
Signed-off-by: Tang Yuantian <Yuantian.Tang@freescale.com>
Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
Signed-off-by: Zhao Qiang <B45475@freescale.com>
Signed-off-by: Shengzhou Liu <Shengzhou.Liu@freescale.com>
Signed-off-by: Ran Wang <ran.wang_1@nxp.com>
5.4-rM2-2.2.x-imx-squashed
Ran Wang 2018-03-28 16:32:08 +08:00 committed by Dong Aisheng
parent ffb8e12c2e
commit dc640f653a
15 changed files with 2077 additions and 20 deletions

View File

@ -42,6 +42,13 @@ extern void flush_dcache_page(struct page *page);
#define flush_dcache_mmap_lock(mapping) do { } while (0)
#define flush_dcache_mmap_unlock(mapping) do { } while (0)
extern void __flush_disable_L1(void);
#ifdef CONFIG_FSL_SOC_BOOKE
extern void flush_dcache_L1(void);
#else
#define flush_dcache_L1() do { } while (0)
#endif
extern void flush_icache_range(unsigned long, unsigned long);
extern void flush_icache_user_range(struct vm_area_struct *vma,
struct page *page, unsigned long addr,

View File

@ -7,6 +7,9 @@
#ifndef __PPC_FSL_PM_H
#define __PPC_FSL_PM_H
#ifndef __ASSEMBLY__
#include <linux/suspend.h>
#define E500_PM_PH10 1
#define E500_PM_PH15 2
#define E500_PM_PH20 3
@ -42,6 +45,34 @@ struct fsl_pm_ops {
extern const struct fsl_pm_ops *qoriq_pm_ops;
struct fsm_reg_vals {
u32 offset;
u32 value;
};
void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val);
void fsl_epu_setup_default(void __iomem *epu_base);
void fsl_npc_setup_default(void __iomem *npc_base);
void fsl_fsm_clean(void __iomem *base, struct fsm_reg_vals *val);
void fsl_epu_clean_default(void __iomem *epu_base);
extern int fsl_dp_iomap(void);
extern void fsl_dp_iounmap(void);
extern int fsl_enter_epu_deepsleep(void);
extern void fsl_dp_enter_low(void __iomem *ccsr_base, void __iomem *dcsr_base,
void __iomem *pld_base, int pld_flag);
extern void fsl_booke_deep_sleep_resume(void);
int __init fsl_rcpm_init(void);
void set_pm_suspend_state(suspend_state_t state);
suspend_state_t pm_suspend_state(void);
void fsl_set_power_except(struct device *dev, int on);
#endif /* __ASSEMBLY__ */
#define T1040QDS_TETRA_FLAG 1
#define T104xRDB_CPLD_FLAG 2
#endif /* __PPC_FSL_PM_H */

View File

@ -81,6 +81,7 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
ifneq ($(CONFIG_FA_DUMP)$(CONFIG_PRESERVE_FA_DUMP),)
obj-y += fadump.o
endif
obj-$(CONFIG_FSL_SOC) += fsl_pm.o
ifdef CONFIG_PPC32
obj-$(CONFIG_E500) += idle_e500.o
endif

View File

@ -174,6 +174,10 @@ skpinv: addi r6,r6,1 /* Increment */
lis r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, M_IF_NEEDED)@h
ori r6,r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, M_IF_NEEDED)@l
mtspr SPRN_MAS2,r6
#ifdef ENTRY_DEEPSLEEP_SETUP
LOAD_REG_IMMEDIATE(r8, MEMORY_START)
ori r8,r8,(MAS3_SX|MAS3_SW|MAS3_SR)
#endif
mtspr SPRN_MAS3,r8
tlbwe
@ -216,12 +220,18 @@ next_tlb_setup:
#error You need to specify the mapping or not use this at all.
#endif
#ifdef ENTRY_DEEPSLEEP_SETUP
LOAD_REG_ADDR(r6, 2f)
mfmsr r7
rlwinm r7,r7,0,~(MSR_IS|MSR_DS)
#else
lis r7,MSR_KERNEL@h
ori r7,r7,MSR_KERNEL@l
bl 1f /* Find our address */
1: mflr r9
rlwimi r6,r9,0,20,31
addi r6,r6,(2f - 1b)
#endif
mtspr SPRN_SRR0,r6
mtspr SPRN_SRR1,r7
rfi /* start execution out of TLB1[0] entry */

View File

@ -0,0 +1,49 @@
/*
* Freescale General Power Management Implementation
*
* Copyright 2018 NXP
* Author: Wang Dongsheng <dongsheng.wang@freescale.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the above-listed copyright holders nor the
* names of any contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation, either version 2 of that License or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/suspend.h>
#include <asm/fsl_pm.h>
static suspend_state_t pm_state;
void set_pm_suspend_state(suspend_state_t state)
{
pm_state = state;
}
suspend_state_t pm_suspend_state(void)
{
return pm_state;
}

View File

@ -860,7 +860,7 @@ _GLOBAL(start_secondary_resume)
/*
* This subroutine clobbers r11 and r12
*/
enable_64b_mode:
_GLOBAL(enable_64b_mode)
mfmsr r11 /* grab the current MSR */
#ifdef CONFIG_PPC_BOOK3E
oris r11,r11,0x8000 /* CM bit set, we'll set ICM later */

View File

@ -10,6 +10,8 @@ menuconfig FSL_SOC_BOOKE
select SERIAL_8250_EXTENDED if SERIAL_8250
select SERIAL_8250_SHARE_IRQ if SERIAL_8250
select FSL_CORENET_RCPM if PPC_E500MC
select FSL_QORIQ_PM if SUSPEND && PPC_E500MC
select FSL_PMC if SUSPEND && !PPC_E500MC
default y
if FSL_SOC_BOOKE
@ -292,3 +294,7 @@ endif # FSL_SOC_BOOKE
config TQM85xx
bool
config FSL_QORIQ_PM
bool
select FSL_SLEEP_FSM

View File

@ -3,7 +3,9 @@
# Makefile for the PowerPC 85xx linux kernel.
#
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_SUSPEND) += sleep.o
obj-$(CONFIG_FSL_PMC) += mpc85xx_pm_ops.o
obj-$(CONFIG_FSL_QORIQ_PM) += qoriq_pm.o deepsleep.o
obj-y += common.o

View File

@ -0,0 +1,349 @@
/*
* Support deep sleep feature for T104x
*
* Copyright 2018 NXP
* Author: Chenhui Zhao <chenhui.zhao@freescale.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the above-listed copyright holders nor the
* names of any contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation, either version 2 of that License or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/kernel.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <sysdev/fsl_soc.h>
#include <asm/machdep.h>
#include <asm/fsl_pm.h>
#define SIZE_1MB 0x100000
#define SIZE_2MB 0x200000
#define CPC_CPCHDBCR0 0x10f00
#define CPC_CPCHDBCR0_SPEC_DIS 0x08000000
#define CCSR_SCFG_DPSLPCR 0xfc000
#define CCSR_SCFG_DPSLPCR_WDRR_EN 0x1
#define CCSR_SCFG_SPARECR2 0xfc504
#define CCSR_SCFG_SPARECR3 0xfc508
#define CCSR_GPIO1_GPDIR 0x130000
#define CCSR_GPIO1_GPODR 0x130004
#define CCSR_GPIO1_GPDAT 0x130008
#define CCSR_GPIO1_GPDIR_29 0x4
#define RCPM_BLOCK_OFFSET 0x00022000
#define EPU_BLOCK_OFFSET 0x00000000
#define NPC_BLOCK_OFFSET 0x00001000
#define CSTTACR0 0xb00
#define CG1CR0 0x31c
#define CCSR_LAW_BASE 0xC00
#define DCFG_BRR 0xE4 /* boot release register */
#define LCC_BSTRH 0x20 /* Boot space translation register high */
#define LCC_BSTRL 0x24 /* Boot space translation register low */
#define LCC_BSTAR 0x28 /* Boot space translation attribute register */
#define RCPM_PCTBENR 0x1A0 /* Physical Core Timebase Enable Register */
#define RCPM_BASE 0xE2000
#define DCFG_BASE 0xE0000
/* 128 bytes buffer for restoring data broke by DDR training initialization */
#define DDR_BUF_SIZE 128
static u8 ddr_buff[DDR_BUF_SIZE] __aligned(64);
static void *dcsr_base, *ccsr_base, *pld_base;
static int pld_flag;
/* for law */
struct fsl_law {
u32 lawbarh; /* LAWn base address high */
u32 lawbarl; /* LAWn base address low */
u32 lawar; /* LAWn attributes */
u32 reserved;
};
struct fsl_law *saved_law;
static u32 num_laws;
/* for nonboot cpu */
struct fsl_bstr {
u32 bstrh;
u32 bstrl;
u32 bstar;
u32 cpu_mask;
};
static struct fsl_bstr saved_bstr;
int fsl_dp_iomap(void)
{
struct device_node *np;
int ret = 0;
phys_addr_t ccsr_phy_addr, dcsr_phy_addr;
saved_law = NULL;
ccsr_base = NULL;
dcsr_base = NULL;
pld_base = NULL;
ccsr_phy_addr = get_immrbase();
if (ccsr_phy_addr == -1) {
pr_err("%s: Can't get the address of CCSR\n", __func__);
ret = -EINVAL;
goto ccsr_err;
}
ccsr_base = ioremap(ccsr_phy_addr, SIZE_2MB);
if (!ccsr_base) {
ret = -ENOMEM;
goto ccsr_err;
}
dcsr_phy_addr = get_dcsrbase();
if (dcsr_phy_addr == -1) {
pr_err("%s: Can't get the address of DCSR\n", __func__);
ret = -EINVAL;
goto dcsr_err;
}
dcsr_base = ioremap(dcsr_phy_addr, SIZE_1MB);
if (!dcsr_base) {
ret = -ENOMEM;
goto dcsr_err;
}
np = of_find_compatible_node(NULL, NULL, "fsl,tetra-fpga");
if (np) {
pld_flag = T1040QDS_TETRA_FLAG;
} else {
np = of_find_compatible_node(NULL, NULL, "fsl,deepsleep-cpld");
if (np) {
pld_flag = T104xRDB_CPLD_FLAG;
} else {
pr_err("%s: Can't find the FPGA/CPLD node\n",
__func__);
ret = -EINVAL;
goto pld_err;
}
}
pld_base = of_iomap(np, 0);
of_node_put(np);
np = of_find_compatible_node(NULL, NULL, "fsl,corenet-law");
if (!np) {
pr_err("%s: Can't find the node of \"law\"\n", __func__);
ret = -EINVAL;
goto alloc_err;
}
ret = of_property_read_u32(np, "fsl,num-laws", &num_laws);
if (ret) {
ret = -EINVAL;
goto alloc_err;
}
saved_law = kzalloc(sizeof(*saved_law) * num_laws, GFP_KERNEL);
if (!saved_law) {
ret = -ENOMEM;
goto alloc_err;
}
of_node_put(np);
return 0;
alloc_err:
iounmap(pld_base);
pld_base = NULL;
pld_err:
iounmap(dcsr_base);
dcsr_base = NULL;
dcsr_err:
iounmap(ccsr_base);
ccsr_base = NULL;
ccsr_err:
return ret;
}
void fsl_dp_iounmap(void)
{
if (dcsr_base) {
iounmap(dcsr_base);
dcsr_base = NULL;
}
if (ccsr_base) {
iounmap(ccsr_base);
ccsr_base = NULL;
}
if (pld_base) {
iounmap(pld_base);
pld_base = NULL;
}
kfree(saved_law);
saved_law = NULL;
}
static void fsl_dp_ddr_save(void *ccsr_base)
{
u32 ddr_buff_addr;
/*
* DDR training initialization will break 128 bytes at the beginning
* of DDR, therefore, save them so that the bootloader will restore
* them. Assume that DDR is mapped to the address space started with
* CONFIG_PAGE_OFFSET.
*/
memcpy(ddr_buff, (void *)CONFIG_PAGE_OFFSET, DDR_BUF_SIZE);
/* assume ddr_buff is in the physical address space of 4GB */
ddr_buff_addr = (u32)(__pa(ddr_buff) & 0xffffffff);
/*
* the bootloader will restore the first 128 bytes of DDR from
* the location indicated by the register SPARECR3
*/
out_be32(ccsr_base + CCSR_SCFG_SPARECR3, ddr_buff_addr);
}
static void fsl_dp_mp_save(void *ccsr)
{
struct fsl_bstr *dst = &saved_bstr;
dst->bstrh = in_be32(ccsr + LCC_BSTRH);
dst->bstrl = in_be32(ccsr + LCC_BSTRL);
dst->bstar = in_be32(ccsr + LCC_BSTAR);
dst->cpu_mask = in_be32(ccsr + DCFG_BASE + DCFG_BRR);
}
static void fsl_dp_mp_restore(void *ccsr)
{
struct fsl_bstr *src = &saved_bstr;
out_be32(ccsr + LCC_BSTRH, src->bstrh);
out_be32(ccsr + LCC_BSTRL, src->bstrl);
out_be32(ccsr + LCC_BSTAR, src->bstar);
/* release the nonboot cpus */
out_be32(ccsr + DCFG_BASE + DCFG_BRR, src->cpu_mask);
/* enable the time base */
out_be32(ccsr + RCPM_BASE + RCPM_PCTBENR, src->cpu_mask);
/* read back to sync write */
in_be32(ccsr + RCPM_BASE + RCPM_PCTBENR);
}
static void fsl_dp_law_save(void *ccsr)
{
int i;
struct fsl_law *dst = saved_law;
struct fsl_law *src = (void *)(ccsr + CCSR_LAW_BASE);
for (i = 0; i < num_laws; i++) {
dst->lawbarh = in_be32(&src->lawbarh);
dst->lawbarl = in_be32(&src->lawbarl);
dst->lawar = in_be32(&src->lawar);
dst++;
src++;
}
}
static void fsl_dp_law_restore(void *ccsr)
{
int i;
struct fsl_law *src = saved_law;
struct fsl_law *dst = (void *)(ccsr + CCSR_LAW_BASE);
for (i = 0; i < num_laws - 1; i++) {
out_be32(&dst->lawar, 0);
out_be32(&dst->lawbarl, src->lawbarl);
out_be32(&dst->lawbarh, src->lawbarh);
out_be32(&dst->lawar, src->lawar);
/* Read back so that we sync the writes */
in_be32(&dst->lawar);
src++;
dst++;
}
}
static void fsl_dp_set_resume_pointer(void *ccsr_base)
{
u32 resume_addr;
/* the bootloader will finally jump to this address to return kernel */
#ifdef CONFIG_PPC32
resume_addr = (u32)(__pa(fsl_booke_deep_sleep_resume));
#else
resume_addr = (u32)(__pa(*(u64 *)fsl_booke_deep_sleep_resume)
& 0xffffffff);
#endif
/* use the register SPARECR2 to save the resume address */
out_be32(ccsr_base + CCSR_SCFG_SPARECR2, resume_addr);
}
int fsl_enter_epu_deepsleep(void)
{
fsl_dp_ddr_save(ccsr_base);
fsl_dp_set_resume_pointer(ccsr_base);
fsl_dp_mp_save(ccsr_base);
fsl_dp_law_save(ccsr_base);
/* enable Warm Device Reset request. */
setbits32(ccsr_base + CCSR_SCFG_DPSLPCR, CCSR_SCFG_DPSLPCR_WDRR_EN);
/* set GPIO1_29 as an output pin (not open-drain), and output 0 */
clrbits32(ccsr_base + CCSR_GPIO1_GPDAT, CCSR_GPIO1_GPDIR_29);
clrbits32(ccsr_base + CCSR_GPIO1_GPODR, CCSR_GPIO1_GPDIR_29);
setbits32(ccsr_base + CCSR_GPIO1_GPDIR, CCSR_GPIO1_GPDIR_29);
/*
* Disable CPC speculation to avoid deep sleep hang, especially
* in secure boot mode. This bit will be cleared automatically
* when resuming from deep sleep.
*/
setbits32(ccsr_base + CPC_CPCHDBCR0, CPC_CPCHDBCR0_SPEC_DIS);
fsl_epu_setup_default(dcsr_base + EPU_BLOCK_OFFSET);
fsl_npc_setup_default(dcsr_base + NPC_BLOCK_OFFSET);
out_be32(dcsr_base + RCPM_BLOCK_OFFSET + CSTTACR0, 0x00001001);
out_be32(dcsr_base + RCPM_BLOCK_OFFSET + CG1CR0, 0x00000001);
fsl_dp_enter_low(ccsr_base, dcsr_base, pld_base, pld_flag);
fsl_dp_law_restore(ccsr_base);
fsl_dp_mp_restore(ccsr_base);
/* disable Warm Device Reset request */
clrbits32(ccsr_base + CCSR_SCFG_DPSLPCR, CCSR_SCFG_DPSLPCR_WDRR_EN);
fsl_epu_clean_default(dcsr_base + EPU_BLOCK_OFFSET);
return 0;
}

View File

@ -0,0 +1,222 @@
/*
* Support Power Management feature
*
* Copyright 2018 NXP
* Author: Chenhui Zhao <chenhui.zhao@freescale.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the above-listed copyright holders nor the
* names of any contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation, either version 2 of that License or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/kernel.h>
#include <linux/suspend.h>
#include <linux/of_platform.h>
#include <linux/usb.h>
#include <asm/fsl_pm.h>
#define FSL_SLEEP 0x1
#define FSL_DEEP_SLEEP 0x2
int (*fsl_enter_deepsleep)(void);
/* specify the sleep state of the present platform */
unsigned int sleep_pm_state;
/* supported sleep modes by the present platform */
static unsigned int sleep_modes;
/**
* fsl_set_power_except - set which IP block is not powerdown when sleep,
* such as MAC, USB, etc.
*
* @dev: a pointer to the struct device
* @on: if 1, do not power down; if 0, power down.
*/
void fsl_set_power_except(struct device *dev, int on)
{
u32 value[2];
u32 pw_mask;
int ret;
struct device_node *mac_node;
const phandle *phandle_prop;
if (dev && !strncmp(dev->bus->name, "usb", 3)) {
struct usb_device *udev = container_of(dev,
struct usb_device, dev);
struct device *controller = udev->bus->controller;
ret = of_property_read_u32_array(controller->parent->of_node,
"sleep", value, 2);
} else
ret = of_property_read_u32_array(dev->of_node, "sleep",
value, 2);
if (ret) {
/* search fman mac node */
phandle_prop = of_get_property(dev->of_node, "fsl,fman-mac",
NULL);
if (phandle_prop == NULL)
goto err;
mac_node = of_find_node_by_phandle(*phandle_prop);
ret = of_property_read_u32_array(mac_node, "sleep", value, 2);
of_node_put(mac_node);
if (ret)
goto err;
}
/* get the second value, it is a mask */
pw_mask = value[1];
qoriq_pm_ops->set_ip_power(on, pw_mask);
return;
err:
dev_err(dev, "Can not set wakeup sources\n");
}
EXPORT_SYMBOL_GPL(fsl_set_power_except);
void qoriq_set_wakeup_source(struct device *dev, void *enable)
{
if (!device_may_wakeup(dev))
return;
fsl_set_power_except(dev, *((int *)enable));
}
static int qoriq_suspend_enter(suspend_state_t state)
{
int ret = 0;
int cpu;
switch (state) {
case PM_SUSPEND_STANDBY:
if (cur_cpu_spec->cpu_flush_caches)
cur_cpu_spec->cpu_flush_caches();
ret = qoriq_pm_ops->plat_enter_sleep();
break;
case PM_SUSPEND_MEM:
cpu = smp_processor_id();
qoriq_pm_ops->irq_mask(cpu);
ret = fsl_enter_deepsleep();
qoriq_pm_ops->irq_unmask(cpu);
break;
default:
ret = -EINVAL;
}
return ret;
}
static int qoriq_suspend_valid(suspend_state_t state)
{
set_pm_suspend_state(state);
if (state == PM_SUSPEND_STANDBY && (sleep_modes & FSL_SLEEP))
return 1;
if (state == PM_SUSPEND_MEM && (sleep_modes & FSL_DEEP_SLEEP))
return 1;
set_pm_suspend_state(PM_SUSPEND_ON);
return 0;
}
static int qoriq_suspend_begin(suspend_state_t state)
{
const int enable = 1;
dpm_for_each_dev((void *)&enable, qoriq_set_wakeup_source);
if (state == PM_SUSPEND_MEM)
return fsl_dp_iomap();
return 0;
}
static void qoriq_suspend_end(void)
{
const int enable = 0;
dpm_for_each_dev((void *)&enable, qoriq_set_wakeup_source);
set_pm_suspend_state(PM_SUSPEND_ON);
fsl_dp_iounmap();
}
static const struct platform_suspend_ops qoriq_suspend_ops = {
.valid = qoriq_suspend_valid,
.enter = qoriq_suspend_enter,
.begin = qoriq_suspend_begin,
.end = qoriq_suspend_end,
};
static const struct of_device_id deepsleep_matches[] = {
{
.compatible = "fsl,t1040-rcpm",
},
{
.compatible = "fsl,t1024-rcpm",
},
{
.compatible = "fsl,t1023-rcpm",
},
{},
};
static int __init qoriq_suspend_init(void)
{
struct device_node *np;
sleep_modes = FSL_SLEEP;
sleep_pm_state = PLAT_PM_SLEEP;
np = of_find_compatible_node(NULL, NULL, "fsl,qoriq-rcpm-2.0");
if (np)
sleep_pm_state = PLAT_PM_LPM20;
np = of_find_matching_node_and_match(NULL, deepsleep_matches, NULL);
if (np) {
fsl_enter_deepsleep = fsl_enter_epu_deepsleep;
sleep_modes |= FSL_DEEP_SLEEP;
}
suspend_set_ops(&qoriq_suspend_ops);
set_pm_suspend_state(PM_SUSPEND_ON);
return 0;
}
arch_initcall(qoriq_suspend_init);

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ menuconfig PPC_86xx
depends on PPC_BOOK3S_32
select FSL_SOC
select ALTIVEC
select FSL_PMC if SUSPEND
help
The Freescale E600 SoCs have 74xx cores.

View File

@ -16,54 +16,192 @@
#include <linux/device.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/pm.h>
#include <asm/cacheflush.h>
#include <sysdev/fsl_soc.h>
#include <asm/switch_to.h>
#include <asm/fsl_pm.h>
struct pmc_regs {
__be32 devdisr;
__be32 devdisr2;
__be32 :32;
__be32 :32;
__be32 pmcsr;
#define PMCSR_SLP (1 << 17)
__be32 res1;
__be32 res2;
__be32 powmgtcsr;
#define POWMGTCSR_SLP 0x00020000
#define POWMGTCSR_DPSLP 0x00100000
#define POWMGTCSR_LOSSLESS 0x00400000
__be32 res3[2];
__be32 pmcdr;
};
static struct device *pmc_dev;
static struct pmc_regs __iomem *pmc_regs;
static unsigned int pmc_flag;
#define PMC_SLEEP 0x1
#define PMC_DEEP_SLEEP 0x2
#define PMC_LOSSLESS 0x4
/**
* mpc85xx_pmc_set_wake - enable devices as wakeup event source
* @dev: a device affected
* @enable: True to enable event generation; false to disable
*
* This enables the device as a wakeup event source, or disables it.
*
* RETURN VALUE:
* 0 is returned on success.
* -EINVAL is returned if device is not supposed to wake up the system.
* -ENODEV is returned if PMC is unavailable.
* Error code depending on the platform is returned if both the platform and
* the native mechanism fail to enable the generation of wake-up events
*/
int mpc85xx_pmc_set_wake(struct device *dev, bool enable)
{
int ret = 0;
struct device_node *clk_np;
const u32 *prop;
u32 pmcdr_mask;
if (!pmc_regs) {
dev_err(dev, "%s: PMC is unavailable\n", __func__);
return -ENODEV;
}
if (enable && !device_may_wakeup(dev))
return -EINVAL;
clk_np = of_parse_phandle(dev->of_node, "fsl,pmc-handle", 0);
if (!clk_np)
return -EINVAL;
prop = of_get_property(clk_np, "fsl,pmcdr-mask", NULL);
if (!prop) {
ret = -EINVAL;
goto out;
}
pmcdr_mask = be32_to_cpup(prop);
if (enable)
/* clear to enable clock in low power mode */
clrbits32(&pmc_regs->pmcdr, pmcdr_mask);
else
setbits32(&pmc_regs->pmcdr, pmcdr_mask);
out:
of_node_put(clk_np);
return ret;
}
EXPORT_SYMBOL_GPL(mpc85xx_pmc_set_wake);
/**
* mpc85xx_pmc_set_lossless_ethernet - enable lossless ethernet
* in (deep) sleep mode
* @enable: True to enable event generation; false to disable
*/
void mpc85xx_pmc_set_lossless_ethernet(int enable)
{
if (pmc_flag & PMC_LOSSLESS) {
if (enable)
setbits32(&pmc_regs->powmgtcsr, POWMGTCSR_LOSSLESS);
else
clrbits32(&pmc_regs->powmgtcsr, POWMGTCSR_LOSSLESS);
}
}
EXPORT_SYMBOL_GPL(mpc85xx_pmc_set_lossless_ethernet);
static int pmc_suspend_enter(suspend_state_t state)
{
int ret;
int ret = 0;
int result;
setbits32(&pmc_regs->pmcsr, PMCSR_SLP);
/* At this point, the CPU is asleep. */
switch (state) {
#ifdef CONFIG_PPC_85xx
case PM_SUSPEND_MEM:
#ifdef CONFIG_SPE
enable_kernel_spe();
#endif
#ifdef CONFIG_PPC_FPU
enable_kernel_fp();
#endif
/* Upon resume, wait for SLP bit to be clear. */
ret = spin_event_timeout((in_be32(&pmc_regs->pmcsr) & PMCSR_SLP) == 0,
10000, 10) ? 0 : -ETIMEDOUT;
if (ret)
dev_err(pmc_dev, "tired waiting for SLP bit to clear\n");
pr_debug("%s: Entering deep sleep\n", __func__);
local_irq_disable();
mpc85xx_enter_deep_sleep(get_immrbase(), POWMGTCSR_DPSLP);
pr_debug("%s: Resumed from deep sleep\n", __func__);
break;
#endif
case PM_SUSPEND_STANDBY:
local_irq_disable();
flush_dcache_L1();
setbits32(&pmc_regs->powmgtcsr, POWMGTCSR_SLP);
/* At this point, the CPU is asleep. */
/* Upon resume, wait for SLP bit to be clear. */
result = spin_event_timeout(
(in_be32(&pmc_regs->powmgtcsr) & POWMGTCSR_SLP) == 0,
10000, 10);
if (!result) {
pr_err("%s: timeout waiting for SLP bit "
"to be cleared\n", __func__);
ret = -ETIMEDOUT;
}
break;
default:
ret = -EINVAL;
}
return ret;
}
static int pmc_suspend_valid(suspend_state_t state)
{
if (state != PM_SUSPEND_STANDBY)
return 0;
return 1;
set_pm_suspend_state(state);
if (((pmc_flag & PMC_SLEEP) && (state == PM_SUSPEND_STANDBY)) ||
((pmc_flag & PMC_DEEP_SLEEP) && (state == PM_SUSPEND_MEM)))
return 1;
set_pm_suspend_state(PM_SUSPEND_ON);
return 0;
}
static void pmc_suspend_end(void)
{
set_pm_suspend_state(PM_SUSPEND_ON);
}
static const struct platform_suspend_ops pmc_suspend_ops = {
.valid = pmc_suspend_valid,
.enter = pmc_suspend_enter,
.end = pmc_suspend_end,
};
static int pmc_probe(struct platform_device *ofdev)
static int pmc_probe(struct platform_device *pdev)
{
pmc_regs = of_iomap(ofdev->dev.of_node, 0);
struct device_node *np = pdev->dev.of_node;
pmc_regs = of_iomap(np, 0);
if (!pmc_regs)
return -ENOMEM;
pmc_dev = &ofdev->dev;
pmc_flag = PMC_SLEEP;
if (of_device_is_compatible(np, "fsl,mpc8536-pmc"))
pmc_flag |= PMC_DEEP_SLEEP;
if (of_device_is_compatible(np, "fsl,p1022-pmc"))
pmc_flag |= PMC_DEEP_SLEEP | PMC_LOSSLESS;
suspend_set_ops(&pmc_suspend_ops);
set_pm_suspend_state(PM_SUSPEND_ON);
pr_info("Freescale PMC driver\n");
return 0;
}

View File

@ -42,6 +42,37 @@ extern void init_fcc_ioports(struct fs_platform_info*);
extern void init_fec_ioports(struct fs_platform_info*);
extern void init_smc_ioports(struct fs_uart_platform_info*);
static phys_addr_t immrbase = -1;
static phys_addr_t dcsrbase = -1;
phys_addr_t get_dcsrbase(void)
{
struct device_node *np;
const __be32 *prop;
int size;
u32 naddr;
if (dcsrbase != -1)
return dcsrbase;
np = of_find_compatible_node(NULL, NULL, "fsl,dcsr");
if (!np)
return -1;
prop = of_get_property(np, "#address-cells", &size);
if (prop && size == 4)
naddr = be32_to_cpup(prop);
else
naddr = 2;
prop = of_get_property(np, "ranges", NULL);
if (prop)
dcsrbase = of_translate_address(np, prop + naddr);
of_node_put(np);
return dcsrbase;
}
EXPORT_SYMBOL(get_dcsrbase);
phys_addr_t get_immrbase(void)
{

View File

@ -7,6 +7,7 @@
struct spi_device;
extern phys_addr_t get_dcsrbase(void);
extern phys_addr_t get_immrbase(void);
#if defined(CONFIG_CPM) || defined(CONFIG_QUICC_ENGINE)
extern u32 get_brgfreq(void);
@ -44,5 +45,22 @@ extern struct platform_diu_data_ops diu_ops;
void __noreturn fsl_hv_restart(char *cmd);
void __noreturn fsl_hv_halt(void);
/*
* Cast the ccsrbar to 64-bit parameter so that the assembly
* code can be compatible with both 32-bit & 36-bit.
*/
extern void mpc85xx_enter_deep_sleep(u64 ccsrbar, u32 powmgtreq);
#ifdef CONFIG_FSL_PMC
int mpc85xx_pmc_set_wake(struct device *dev, bool enable);
void mpc85xx_pmc_set_lossless_ethernet(int enable);
#else
static inline int mpc85xx_pmc_set_wake(struct device *dev, bool enable)
{
return -ENODEV;
}
#define mpc85xx_pmc_set_lossless_ethernet(enable) do { } while (0)
#endif
#endif
#endif