From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756683AbcHBPMX (ORCPT ); Tue, 2 Aug 2016 11:12:23 -0400 Received: from mail-bn3nam01on0085.outbound.protection.outlook.com ([104.47.33.85]:59744 "EHLO NAM01-BN3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S965092AbcHBMfJ (ORCPT ); Tue, 2 Aug 2016 08:35:09 -0400 Authentication-Results: spf=fail (sender IP is 192.88.158.2) smtp.mailfrom=nxp.com; nxp.com; dkim=none (message not signed) header.d=none;nxp.com; dmarc=fail action=none header.from=nxp.com;nxp.com; dkim=none (message not signed) header.d=none; From: Chenhui Zhao To: , , CC: , , Chenhui Zhao Subject: [PATCH v3 4/5] powerpc/pm: support deep sleep feature on T104x Date: Tue, 2 Aug 2016 19:59:31 +0800 Message-ID: <1470139172-12699-5-git-send-email-chenhui.zhao@nxp.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1470139172-12699-1-git-send-email-chenhui.zhao@nxp.com> References: <1470139172-12699-1-git-send-email-chenhui.zhao@nxp.com> X-EOPAttributedMessage: 0 X-Matching-Connectors: 131146128147176837;(91ab9b29-cfa4-454e-5278-08d120cd25b8);() X-Forefront-Antispam-Report: CIP:192.88.158.2;IPV:NLI;CTRY:US;EFV:NLI;SFV:NSPM;SFS:(10009020)(979002)(6009001)(7916002)(2980300002)(1109001)(1110001)(339900001)(189002)(199003)(106466001)(189998001)(8666005)(33646002)(4326007)(356003)(19580395003)(36756003)(2906002)(92566002)(229853001)(68736007)(76176999)(50986999)(19580405001)(586003)(5001770100001)(69596002)(7846002)(104016004)(11100500001)(305945005)(97736004)(5003940100001)(85426001)(50226002)(86362001)(77096005)(2950100001)(575784001)(105606002)(48376002)(2201001)(47776003)(50466002)(49486002)(87936001)(8936002)(81166006)(81156014)(8676002)(7059030)(2004002)(2101003)(969003)(989001)(999001)(1009001)(1019001);DIR:OUT;SFP:1101;SCL:1;SRVR:CY1PR0301MB0698;H:az84smr01.freescale.net;FPR:;SPF:Fail;PTR:InfoDomainNonexistent;MX:1;A:1;LANG:en; X-Microsoft-Exchange-Diagnostics: 1;BL2FFO11FD040;1:vuEEoWVM74Te2ELZJ8ELa5Y9S+bB64DxTPQ6rc1JHwZOExV6ohGYGosMrMrR1/Uto6kCanAc7MyOCoVzNI2ySaXlm/nd5JQTi7EEUvEc2iG/3n2/AOYL4CIsfjLF47LZhmq1duj49EWWjkxVC6BYWWS25uABu3hWsg97rZe+MsI6NgJUdnrp5v/yyWJsUyV+Ie/gw/vTdnEu0ZsoCLLTBm0lZUy8S/8zf7tci1umOS7jaWMiYHbL5To98iiDv/pHOAfkYswhTEF0k6X0F5TRpj1qzh8E0irWJ1Gu/SM3q81VgXr/ou3r7CQAPWYTvbWSWO4ynNexxLsiIMbWlGshC4Bg0kIs8y7ZDlVZjN0W9CANEcNo5vX23bF1kZfaPHWvUN8znl07P2/2dzg7ixmQmRgBOUZFFm+VG1kZR7uoi4qry6voT6WbUKUxbkiUcwptXfqUikMWOK4ZR8uL2SHutwY3+OXgQviHDZ8Cw5E2cH1Llz8fAhqjFBvioJctg0ezvp060MxDNHDVxmLck/aBDGsbPLiOFbVsscpdtRb9eabbP6OR+jdF905eH4uuB9ERE9zY9tvwLninQ25OXVueo4CLJoeCEJOJu6o+VKorAjD55HThi0ivQiku1lNQGPV3ejFw2WDeXcRXh+pcCEQYO/3VFXCOzfGXVFKOL/GdEBBtUYL63a3+pZto77hlFZBZ/AuHm5dA+YR0AwetcsPdjSIZg5cFk2zmpfk1K8AaMos= MIME-Version: 1.0 Content-Type: text/plain X-MS-Office365-Filtering-Correlation-Id: 0717ebb2-657a-414e-fc23-08d3bacc9055 X-Microsoft-Exchange-Diagnostics: 1;CY1PR0301MB0698;2:iseqmOxyt3W9MQJHEYeH+z6sWC1uwJoPvkTf/mAdARsYQWO+ERMKL4FQn/aXFfPS9Atb/s3B5NccQjoZj3VO0wuDC5vZ3xYd0GPzh7psYCZaqZAu3Rv3v6woruGRJ7tkJb6r0RuNgTvbSUeyuBNjtqKmKwLWVp1ugVCEGY9s8UdwFrubw75Ul5qP+fmanfXS;3:xVXvkOLJwl7sqKDrTcpWQhQodzY9pr9LH3zxgsp3FjXLciiKH5FDdrbPWDzOCz8UXg7qGFEq4hQJefo1KLyriyTiT8JXnLgYgNkyVrpDTFl0WZPk/ZokgjokOrMfaI8mh96mjZZmgxOYa98x+mFmHuRmHcTDaJFpG95LOCsQ2qaICZIAG6JXKKp1qfy4Jq9eWLNrVw44JFYeaqG8Pnsr8b0qeT1HOQ9LwvZE7MyU14g= X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:CY1PR0301MB0698; X-Microsoft-Exchange-Diagnostics: 1;CY1PR0301MB0698;25:/XTHs4uc2eL2Rx5FXTTRvbHq4zU4jicc+2g++lCC38SCWTskiFU7RUcLcJ+0icxjwiKqfK51UN0QXaass1fDYa583lgmtkQOagNIlH8SUNuK3HqgDGcQIr7piEmM7mvIMaegy3RV4hyvRaM3pEVXyxqlb/PC+kf3dyo1nh9o54EeA5XXXhuvBkHyZO4bqlHrGpFqmfMH6/WmmcTpkh7HbeG/ZDU35ld9ksZ6DaFgxe+TBQbHR8L+wZIx+R/BHYJXi6dlPErJoCgKj31EDFE36K2ukdSRRjEOm+e0OWO7QLoXl3USxsUxSRTAmIn3SqohXsB7yEGipcETOML2lPps429ZyzLknrDoW5DMuAgLvZv0jqqIiCAvwWumE0TODFnPerpIlmnlU+7x0kPd5+7LEn8zyhCvFYGR4NSnCZFEqYnbTvS1a46mjw2QHA8uZxqO+Z9bbgeRhwxORk5W1C6A2Du+YdWsewLolGSk+NlWYOguu+bwPQQrsvkLKfCPIM0suGuhg51NsTxwUB1AFy5ppuFmrPqv00z8rcINw8LN3TiI2FxiNNQi7YNnAwLxoVTptgGePV0FcODE4HxEgCltTuqX7SjQsrREqCDmbziUHe7z4I6vbZs0tj9RyVOCe99z3u4w1KVAlh3X2xMj+Hxe21VEuBNfrvg3DQQ2TWyDlWiESgaESg2p/oR2jBCPR+8bast2WGPr9Tb4QaD6Med8YUeybD7VDN3q7kToI8RJ2YqjlnlocKIknTFemDJXWvh0jwW/45s/qF8OOQiquL5z9Q== X-Microsoft-Exchange-Diagnostics: 1;CY1PR0301MB0698;31:qfILDDQ+dK4tWnKznVk6FxArvjMboCFd2BRcQub1eFl7Z8YvUvhW2JR6ke0HayhW1OYbFxxADdUKALItwOc4Zpf3hOe//sJi4+NBcLDZwNgCiyPitE6sWM4cmzxWqo3yKBnQQ9IPQjgRB0x5svZ5My8hgTJtiwDQOIhbrQbSJ/NQaogEX6FRVXE118ggMfoq8vGu8+V3KFoSjBSRcJV1IxdSmFoEUf6x4Jey/jef6f0=;4:zTB1qob5y6Hye1jN9DptNfkhR7RCaFYjIKojUlOqjhfr7DHzC+/I6P24s8lC3TglOsptsfrL5QeGbalmiVVjcAHUAHZ35KOWK32iHrBxHoVqyI+GM1YVBpmLocKEQ/xIXLwsTIgWwr2Y2f7i/IDGW2Uo5kWY9YUQSOOipv7kDIa43t2ShuqgH1KB+XnPhbP1SEZAv4Zo4O8Py7bMpM+psaszbhaB1dJQ61O0DlVc3J3cH6caXRj3Pr25NEV5Xg9U21L6qP+Zj7CnS5Nozxcu21RF6MCqFmSsDQYZQtT0gAcB3vSF9yQABVWyFhA4sqL3fsc+L7iX/kvY7i9YQ7D+MU1lzq3yf5cnO+AYLo3wcGBQ1+c0f7ExJSnZkes1jxAxGcwICQH32EP3gAdY2f1zPvpKeD9MLHBZvzUjuOlllF+AxrAVbb7zi78qYjc8P7slwNi7IGym1kkF90+QBWwveeRfrCK6wQG8/61C+YKLppTjUS0+e6+1BOnbYmbhIBkwd2gmoOiTOxCjdWvhgmxik/0Qw1roVl9TU4IQjurltbj09zFbANAXxro2tJKsqqQd X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(185117386973197)(17755550239193); X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(601004)(2401047)(8121501046)(13018025)(5005006)(13024025)(13015025)(13023025)(13017025)(3002001)(10201501046)(6055026);SRVR:CY1PR0301MB0698;BCL:0;PCL:0;RULEID:(400006);SRVR:CY1PR0301MB0698; X-Forefront-PRVS: 0022134A87 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;CY1PR0301MB0698;23:sZ479fS/xQFpxDrafyTMPdVVn5WljP/Joo8KJf6?= =?us-ascii?Q?GRf6JetNJIbWao6G/33VyegPURQwJ/MjOKOzYmJ/Q3Nad2peae5QvOGvIXUU?= =?us-ascii?Q?zyecCl1JGhJfBGdNabt73P3RxCp7uFr5Zsgtb2qTzQsHXvAZzZcfycM26wMG?= =?us-ascii?Q?cRkADFTEM6IAzqzP53bm0bYft5g7uaP7xwRDolKay21eVgfvvEKLYCUG1hzO?= =?us-ascii?Q?YU+hewuNyWqAwW9DVmH+/VuL3PBuFG2f/RGVncU16EYvNCrx02J2+msL3Xn7?= =?us-ascii?Q?IJfvDJ+AQPkPYwNliFESBRDxnf6LWFFP/q/iNQ0wzRE6JdT7RYUfpTjJrHWI?= =?us-ascii?Q?YpyUdavbRQuxXgCCcwZptO49fTqQD67/t09L2gvdFyu7tsPy15gwHFjisiv6?= =?us-ascii?Q?3OFpGbS+m71/rAD95ujfvgzv5jUw95yvREEcpWOG4w+SYGf46ycgSyXEgn4w?= =?us-ascii?Q?29Uuc6JVoJhx5XNnx4e6PeZMSsPqT3C6ARkMTJ6Dut6Mums7GkROHtqyGZuE?= =?us-ascii?Q?pVduA/188tY0/B6haDvJkx+dIx5K4LVwtDQ3NTnVpoJAY7YGkwcQJsLLRhvh?= =?us-ascii?Q?PBV8x9f4uSpdsGwmyAKsgnPH0hFd2X76CmvkRCmLvOqzP1v/eQfkhEoVz9SE?= =?us-ascii?Q?ymGzem6+Qbw7VmMq2neBb7yQHUdf76HmrgMmjFiX2pRl3AsFQY+9U8sIhoii?= =?us-ascii?Q?MFisXgRYRF4Jm00eGn7yCReAmmwvAoHhYqqTaSO+cqKqgnYBFoLkkVrbeMX4?= =?us-ascii?Q?FYnIwMQGHsYjp5qfAqCYIVjzFU9shKUpSh5ykcY2DzDu0Oko1az9z937MPRj?= =?us-ascii?Q?8K0d3ud81prk9Pzf8qkNEu8sr4zDwYFCT2WYtP0CwQm/SScmG0P8C/GrzTU2?= =?us-ascii?Q?qLNCfTZxaKLup/19rupQd3DuL1xUrbbUxNJsltS1/hqO48bIHh+RJr3TqlTv?= =?us-ascii?Q?76xu1TtJZd22jx3da+6ctaQwescLr6PBAavVV6UBvRVSng8agCzr6IXrCB9N?= =?us-ascii?Q?7DKqDA89OafBm90czthpUF1Xw09cZfHUCHxEeGhmyrmlvCmiOcoJ5XJhI14i?= =?us-ascii?Q?VgpzmhqgIqsdSsKW+8qo31fx6LDTL+gtA05cqttwP8A3zWFYw38KIIksQkk9?= =?us-ascii?Q?cmsoSSq7hSelfGbFsuI13vlXMBzudKR3NPi9e8Gu97BK89Ng1ssHRswTHPN+?= =?us-ascii?Q?jHeiQUM0gCgSO50n3uzNVqwF8c1PMtgWj7d0a0VxHmp6onLKIfoa03un8+sm?= =?us-ascii?Q?QQpSBi/KPhblQ0Fyc0fngzqvbnMW9PWEkKCSB3Sp8L4wHslfUmk6fgCNpMVx?= =?us-ascii?Q?8LczJgR22uFQ4fGZGI6fksQu1BFGXY0nOjD9KpH6EZmxF?= X-Microsoft-Exchange-Diagnostics: 1;CY1PR0301MB0698;6:MfG1uqpu/qvXTQ5tqMp8mzAZdiHj9bGzv3Q8Sj/oOaeSfgBDwJgK5/CkAiPa1UGTc9POYEA/BCkN63coxUw/77dseFPahqncBKTL8sh+KUnf4De1BsnQvKcT0rGGnmDf6IhukacMyp8Ic9yHqG6gXwyTSmBBk2aLR2SKdj+EPsiS8zegUVUJhp5LRKmbr1AlqPHceKFg1TGuuqfBBTZtn+ZpSJq1cZTez/sO2XuYkVWztCz0qfYpDJvtGkTvi8Oa05WbwYMbhcJ1TwS61G8I5bJQZUOKC+lp4sCojLXZuIU=;5:p9dIRm4pFSE08quTfUV1BA8qeu2yAOlzTqFX38c8IpZ3LYKPp95eQzc7yBSMHDhQTnu49XY1dNjYSupoN4q1uzxdVuKj28hgoHUTNiX4oBeoCDD8uSXam0rMd0epWMbVi5zVyBjFUF/ArwNRAwObiqTwr3c24EDzSpAYr/mx8rg=;24:aKSSt2a6RMMkA/nJTECyo/0OY39NaGMLsjlXevhOsK8mF5g4Dyqj+8pepkAgGV6YakOsA4Mg0sUTqCTMSJt7Li73kHWPK1NrBSUScn6reCk=;7:L3cYrih7oE5Zsga6lkKvUl2yjg38Rv3QjumHLItsls+FDwXtGsghF4r4atSqzHFsU2hAqlIAxxpwCC/fGD+P0v1qLmazN1pV2k6E52nlwaBhDezLSJokjohEzCOIP7T1qJYkX5jLbvLPOur7oIe1o33gkmnvi7Ui74pfg1hdu/VRc7khEog6nYKlPwJzsZpC9RwqoIPVFMVDzh/unSNsYD9X48nJ+c1Mtpb8Vu7MHbc/qRvEi7QMZ8UnzA5AwS1h SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-MS-Exchange-CrossTenant-OriginalArrivalTime: 02 Aug 2016 12:00:12.3464 (UTC) X-MS-Exchange-CrossTenant-Id: 5afe0b00-7697-4969-b663-5eab37d5f47e X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=5afe0b00-7697-4969-b663-5eab37d5f47e;Ip=[192.88.158.2];Helo=[az84smr01.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: CY1PR0301MB0698 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org T104x has deep sleep feature, which can switch off most parts of the SoC when it is in deep sleep mode. This way, it becomes more energy-efficient. The DDR controller will also be powered off in deep sleep. Therefore, the last stage (the latter part of fsl_dp_enter_low) will run without DDR access. This piece of code and related TLBs are prefetched in advance. Due to the different initialization code between 32-bit and 64-bit, they have separate resume entry and precedure. The feature supports 32-bit and 64-bit kernel mode. Signed-off-by: Chenhui Zhao --- arch/powerpc/include/asm/fsl_pm.h | 24 ++ arch/powerpc/kernel/asm-offsets.c | 12 + arch/powerpc/kernel/fsl_booke_entry_mapping.S | 10 + arch/powerpc/kernel/head_64.S | 2 +- arch/powerpc/platforms/85xx/Makefile | 1 + arch/powerpc/platforms/85xx/deepsleep.c | 278 ++++++++++++++ arch/powerpc/platforms/85xx/qoriq_pm.c | 25 ++ arch/powerpc/platforms/85xx/t104x_deepsleep.S | 531 ++++++++++++++++++++++++++ arch/powerpc/sysdev/fsl_rcpm.c | 8 +- 9 files changed, 889 insertions(+), 2 deletions(-) create mode 100644 arch/powerpc/platforms/85xx/deepsleep.c create mode 100644 arch/powerpc/platforms/85xx/t104x_deepsleep.S diff --git a/arch/powerpc/include/asm/fsl_pm.h b/arch/powerpc/include/asm/fsl_pm.h index e05049b..48c2631 100644 --- a/arch/powerpc/include/asm/fsl_pm.h +++ b/arch/powerpc/include/asm/fsl_pm.h @@ -20,6 +20,7 @@ #define PLAT_PM_SLEEP 20 #define PLAT_PM_LPM20 30 +#define PLAT_PM_LPM35 40 #define FSL_PM_SLEEP (1 << 0) #define FSL_PM_DEEP_SLEEP (1 << 1) @@ -48,4 +49,27 @@ extern const struct fsl_pm_ops *qoriq_pm_ops; int __init fsl_rcpm_init(void); +#ifdef CONFIG_FSL_QORIQ_PM +int fsl_enter_deepsleep(void); +int fsl_deepsleep_init(void); +#else +static inline int fsl_enter_deepsleep(void) { return -1; } +static inline int fsl_deepsleep_init(void) { return -1; } +#endif + +extern void fsl_dp_enter_low(void *priv); +extern void fsl_booke_deep_sleep_resume(void); + +struct fsl_iomap { + void *ccsr_scfg_base; + void *ccsr_rcpm_base; + void *ccsr_ddr_base; + void *ccsr_gpio1_base; + void *ccsr_cpc_base; + void *dcsr_epu_base; + void *dcsr_npc_base; + void *dcsr_rcpm_base; + void *cpld_base; + void *fpga_base; +}; #endif /* __PPC_FSL_PM_H */ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 9ea0955..cc488f9 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -68,6 +68,10 @@ #include "../mm/mmu_decl.h" #endif +#ifdef CONFIG_FSL_QORIQ_PM +#include +#endif + int main(void) { DEFINE(THREAD, offsetof(struct task_struct, thread)); @@ -783,5 +787,13 @@ int main(void) DEFINE(PPC_DBELL_SERVER, PPC_DBELL_SERVER); +#ifdef CONFIG_FSL_QORIQ_PM + DEFINE(CCSR_CPC_BASE, offsetof(struct fsl_iomap, ccsr_cpc_base)); + DEFINE(CCSR_GPIO1_BASE, offsetof(struct fsl_iomap, ccsr_gpio1_base)); + DEFINE(CCSR_RCPM_BASE, offsetof(struct fsl_iomap, ccsr_rcpm_base)); + DEFINE(CCSR_DDR_BASE, offsetof(struct fsl_iomap, ccsr_ddr_base)); + DEFINE(CCSR_SCFG_BASE, offsetof(struct fsl_iomap, ccsr_scfg_base)); + DEFINE(DCSR_EPU_BASE, offsetof(struct fsl_iomap, dcsr_epu_base)); +#endif return 0; } diff --git a/arch/powerpc/kernel/fsl_booke_entry_mapping.S b/arch/powerpc/kernel/fsl_booke_entry_mapping.S index 83dd0f6..659b059 100644 --- a/arch/powerpc/kernel/fsl_booke_entry_mapping.S +++ b/arch/powerpc/kernel/fsl_booke_entry_mapping.S @@ -173,6 +173,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 @@ -215,12 +219,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 */ diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 2d14774..21fa124 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -829,7 +829,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 */ diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index 87fb847..a73d563 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_FSL_PMC) += mpc85xx_pm_ops.o obj-$(CONFIG_FSL_QORIQ_PM) += qoriq_pm.o sleep_fsm.o +obj-$(CONFIG_FSL_QORIQ_PM) += deepsleep.o t104x_deepsleep.o obj-y += common.o diff --git a/arch/powerpc/platforms/85xx/deepsleep.c b/arch/powerpc/platforms/85xx/deepsleep.c new file mode 100644 index 0000000..9521d99 --- /dev/null +++ b/arch/powerpc/platforms/85xx/deepsleep.c @@ -0,0 +1,278 @@ +/* + * Support deep sleep feature for T104x + * + * Copyright 2016 Freescale Semiconductor Inc. + * + * Author: Chenhui Zhao + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include + +#include "sleep_fsm.h" + +#define CPC_CPCHDBCR0 0x0f00 +#define CPC_CPCHDBCR0_SPEC_DIS 0x08000000 + +#define CCSR_SCFG_DPSLPCR 0x000 +#define CCSR_SCFG_DPSLPCR_WDRR_EN 0x1 +#define CCSR_SCFG_SPARECR2 0x504 +#define CCSR_SCFG_SPARECR3 0x508 + +#define CCSR_GPIO1_GPDIR 0x000 +#define CCSR_GPIO1_GPODR 0x004 +#define CCSR_GPIO1_GPDAT 0x008 + +#define QIXIS_PWR_CTL2 0x21 +#define QIXIS_PWR_CTL2_PCTL 0x2 + +#define QORIQ_CPLD_MISCCSR 0x17 +#define QORIQ_CPLD_MISCCSR_SLEEPEN 0x40 + +/* 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 int fsl_gpio_mcke; + +static void fsl_dp_iounmap(void); + +static struct fsl_iomap fsl_dp_priv; + +static const struct of_device_id fsl_dp_cpld_ids[] __initconst = { + { .compatible = "fsl,t1024-cpld", }, + { .compatible = "fsl,t1040rdb-cpld", }, + { .compatible = "fsl,t1042rdb-cpld", }, + { .compatible = "fsl,t1042rdb_pi-cpld", }, + { .compatible = "fsl,t1040d4rdb-cpld", }, + {} +}; + +static const struct of_device_id fsl_dp_fpga_ids[] __initconst = { + { .compatible = "fsl,fpga-qixis", }, + { .compatible = "fsl,tetra-fpga", }, + {} +}; + +static void fsl_dp_set_resume_pointer(void) +{ + 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(fsl_dp_priv.ccsr_scfg_base + CCSR_SCFG_SPARECR2, + resume_addr); +} + +static void fsl_dp_pins_setup(void) +{ + u32 mask = BIT(31 - fsl_gpio_mcke); + + /* set GPIO1_29 as an output pin (not open-drain), and output 0 */ + clrbits32(fsl_dp_priv.ccsr_gpio1_base + CCSR_GPIO1_GPDAT, mask); + clrbits32(fsl_dp_priv.ccsr_gpio1_base + CCSR_GPIO1_GPODR, mask); + setbits32(fsl_dp_priv.ccsr_gpio1_base + CCSR_GPIO1_GPDIR, mask); + + /* wait for the stabilization of GPIO1_29 */ + udelay(10); + + /* enable the functionality of pins relevant to deep sleep */ + if (fsl_dp_priv.cpld_base) { + setbits8(fsl_dp_priv.cpld_base + QORIQ_CPLD_MISCCSR, + QORIQ_CPLD_MISCCSR_SLEEPEN); + } else if (fsl_dp_priv.fpga_base) { + setbits8(fsl_dp_priv.fpga_base + QIXIS_PWR_CTL2, + QIXIS_PWR_CTL2_PCTL); + } +} + +static void fsl_dp_ddr_save(void *scfg_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(scfg_base + CCSR_SCFG_SPARECR3, ddr_buff_addr); +} + +int fsl_enter_deepsleep(void) +{ + fsl_dp_ddr_save(fsl_dp_priv.ccsr_scfg_base); + + fsl_dp_set_resume_pointer(); + + /* enable Warm Device Reset request. */ + setbits32(fsl_dp_priv.ccsr_scfg_base + CCSR_SCFG_DPSLPCR, + CCSR_SCFG_DPSLPCR_WDRR_EN); + + /* + * 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(fsl_dp_priv.ccsr_cpc_base + CPC_CPCHDBCR0, + CPC_CPCHDBCR0_SPEC_DIS); + + fsl_epu_setup_default(fsl_dp_priv.dcsr_epu_base); + fsl_npc_setup_default(fsl_dp_priv.dcsr_npc_base); + fsl_dcsr_rcpm_setup(fsl_dp_priv.dcsr_rcpm_base); + + fsl_dp_pins_setup(); + + fsl_dp_enter_low(&fsl_dp_priv); + + /* disable Warm Device Reset request */ + clrbits32(fsl_dp_priv.ccsr_scfg_base + CCSR_SCFG_DPSLPCR, + CCSR_SCFG_DPSLPCR_WDRR_EN); + + fsl_epu_clean_default(fsl_dp_priv.dcsr_epu_base); + + return 0; +} + +static void __init *fsl_of_iomap(char *comp) +{ + struct device_node *np; + void *addr; + + np = of_find_compatible_node(NULL, NULL, comp); + if (np) { + addr = of_iomap(np, 0); + of_node_put(np); + return addr; + } + + return NULL; +} + +static struct device_node __init *fsl_get_gpio_node(void) +{ + struct device_node *np; + u32 val[3]; + int ret; + + np = of_find_compatible_node(NULL, NULL, "fsl,qoriq-rcpm-2.1"); + ret = of_property_read_u32_array(np, "mcke-gpios", val, 3); + if (ret) + return NULL; + + fsl_gpio_mcke = val[1]; + np = of_find_node_by_phandle((phandle)val[0]); + if (!np) + return NULL; + + return np; +} + +static int __init fsl_dp_iomap(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, fsl_dp_cpld_ids); + if (np) { + fsl_dp_priv.cpld_base = of_iomap(np, 0); + of_node_put(np); + } else { + np = of_find_matching_node(NULL, fsl_dp_fpga_ids); + if (np) { + fsl_dp_priv.fpga_base = of_iomap(np, 0); + of_node_put(np); + } else + goto err; + } + + fsl_dp_priv.ccsr_scfg_base = fsl_of_iomap("fsl,t1040-scfg"); + if (!fsl_dp_priv.ccsr_scfg_base) { + fsl_dp_priv.ccsr_scfg_base = fsl_of_iomap("fsl,t1023-scfg"); + if (!fsl_dp_priv.ccsr_scfg_base) + goto err; + } + + fsl_dp_priv.ccsr_rcpm_base = fsl_of_iomap("fsl,qoriq-rcpm-2.1"); + if (!fsl_dp_priv.ccsr_rcpm_base) + goto err; + + fsl_dp_priv.ccsr_ddr_base = fsl_of_iomap("fsl,qoriq-memory-controller"); + if (!fsl_dp_priv.ccsr_ddr_base) + goto err; + + np = fsl_get_gpio_node(); + fsl_dp_priv.ccsr_gpio1_base = of_iomap(np, 0); + if (!fsl_dp_priv.ccsr_gpio1_base) + goto err; + + fsl_dp_priv.ccsr_cpc_base = + fsl_of_iomap("fsl,t1040-l3-cache-controller"); + if (!fsl_dp_priv.ccsr_cpc_base) { + fsl_dp_priv.ccsr_cpc_base = + fsl_of_iomap("fsl,t1023-l3-cache-controlle"); + if (!fsl_dp_priv.ccsr_cpc_base) + goto err; + } + + fsl_dp_priv.dcsr_epu_base = fsl_of_iomap("fsl,dcsr-epu"); + if (!fsl_dp_priv.dcsr_epu_base) + goto err; + + fsl_dp_priv.dcsr_npc_base = fsl_of_iomap("fsl,dcsr-cnpc"); + if (!fsl_dp_priv.dcsr_npc_base) + goto err; + + fsl_dp_priv.dcsr_rcpm_base = fsl_of_iomap("fsl,dcsr-rcpm"); + if (!fsl_dp_priv.dcsr_rcpm_base) + goto err; + + return 0; + +err: + fsl_dp_iounmap(); + return -1; +} + +static void __init fsl_dp_iounmap(void) +{ + void **p = (void *)&fsl_dp_priv; + int i; + + for (i = 0; i < sizeof(struct fsl_iomap)/sizeof(void *); i++) { + iounmap(*p); + *p = NULL; + p++; + } +} + +int __init fsl_deepsleep_init(void) +{ + return fsl_dp_iomap(); +} diff --git a/arch/powerpc/platforms/85xx/qoriq_pm.c b/arch/powerpc/platforms/85xx/qoriq_pm.c index c97ef8f..ff6e46a 100644 --- a/arch/powerpc/platforms/85xx/qoriq_pm.c +++ b/arch/powerpc/platforms/85xx/qoriq_pm.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -27,6 +28,11 @@ static int qoriq_suspend_enter(suspend_state_t state) case PM_SUSPEND_STANDBY: ret = qoriq_pm_ops->plat_enter_sleep(FSL_PM_SLEEP); break; + + case PM_SUSPEND_MEM: + ret = qoriq_pm_ops->plat_enter_sleep(FSL_PM_DEEP_SLEEP); + break; + default: ret = -EINVAL; } @@ -40,9 +46,24 @@ static int qoriq_suspend_valid(suspend_state_t state) if (state == PM_SUSPEND_STANDBY && (pm_modes & FSL_PM_SLEEP)) return 1; + if (state == PM_SUSPEND_MEM && (pm_modes & FSL_PM_DEEP_SLEEP)) + return 1; + return 0; } +static const char * const boards_deepsleep[] __initconst = { + "fsl,T1024QDS", + "fsl,T1024RDB", + "fsl,T1040QDS", + "fsl,T1040RDB", + "fsl,T1040D4RDB", + "fsl,T1042QDS", + "fsl,T1042D4RDB", + "fsl,T1042RDB", + "fsl,T1042RDB_PI", +}; + static const struct platform_suspend_ops qoriq_suspend_ops = { .valid = qoriq_suspend_valid, .enter = qoriq_suspend_enter, @@ -53,6 +74,10 @@ static int __init qoriq_suspend_init(void) /* support sleep by default */ pm_modes |= FSL_PM_SLEEP; + if (of_flat_dt_match(of_get_flat_dt_root(), boards_deepsleep) && + !fsl_deepsleep_init()) + pm_modes |= FSL_PM_DEEP_SLEEP; + suspend_set_ops(&qoriq_suspend_ops); return 0; } diff --git a/arch/powerpc/platforms/85xx/t104x_deepsleep.S b/arch/powerpc/platforms/85xx/t104x_deepsleep.S new file mode 100644 index 0000000..aefce20 --- /dev/null +++ b/arch/powerpc/platforms/85xx/t104x_deepsleep.S @@ -0,0 +1,531 @@ +/* + * Enter and resume from deep sleep state + * + * Copyright 2016 Freescale Semiconductor Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include + +/* + * the number of bytes occupied by one register + * the value of 8 is compatible with both 32-bit and 64-bit registers + */ +#define STRIDE_SIZE 8 + +/* GPR0 - GPR31 */ +#define BOOKE_GPR0_OFF 0x0000 +#define BOOKE_GPR_COUNT 32 +/* IVOR0 - IVOR42 */ +#define BOOKE_IVOR0_OFF (BOOKE_GPR0_OFF + BOOKE_GPR_COUNT * STRIDE_SIZE) +#define BOOKE_IVOR_COUNT 43 +/* SPRG0 - SPRG9 */ +#define BOOKE_SPRG0_OFF (BOOKE_IVOR0_OFF + BOOKE_IVOR_COUNT * STRIDE_SIZE) +#define BOOKE_SPRG_COUNT 10 +/* IVPR */ +#define BOOKE_IVPR_OFF (BOOKE_SPRG0_OFF + BOOKE_SPRG_COUNT * STRIDE_SIZE) + +#define BOOKE_LR_OFF (BOOKE_IVPR_OFF + STRIDE_SIZE) +#define BOOKE_MSR_OFF (BOOKE_LR_OFF + STRIDE_SIZE) +#define BOOKE_TBU_OFF (BOOKE_MSR_OFF + STRIDE_SIZE) +#define BOOKE_TBL_OFF (BOOKE_TBU_OFF + STRIDE_SIZE) +#define BOOKE_EPCR_OFF (BOOKE_TBL_OFF + STRIDE_SIZE) +#define BOOKE_HID0_OFF (BOOKE_EPCR_OFF + STRIDE_SIZE) +#define BOOKE_PIR_OFF (BOOKE_HID0_OFF + STRIDE_SIZE) +#define BOOKE_PID0_OFF (BOOKE_PIR_OFF + STRIDE_SIZE) +#define BOOKE_BUCSR_OFF (BOOKE_PID0_OFF + STRIDE_SIZE) + +#define BUFFER_SIZE (BOOKE_BUCSR_OFF + STRIDE_SIZE) + +#undef SAVE_GPR +#define SAVE_GPR(gpr, offset) \ + PPC_STL gpr, offset(r10) + +#define RESTORE_GPR(gpr, offset) \ + PPC_LL gpr, offset(r10) + +#define SAVE_SPR(spr, offset) \ + mfspr r0, spr; \ + PPC_STL r0, offset(r10) + +#define RESTORE_SPR(spr, offset) \ + PPC_LL r0, offset(r10); \ + mtspr spr, r0 + +#define SAVE_ALL_GPR \ + SAVE_GPR(r1, BOOKE_GPR0_OFF + STRIDE_SIZE * 1) ;\ + SAVE_GPR(r2, BOOKE_GPR0_OFF + STRIDE_SIZE * 2) ;\ + SAVE_GPR(r13, BOOKE_GPR0_OFF + STRIDE_SIZE * 13) ;\ + SAVE_GPR(r14, BOOKE_GPR0_OFF + STRIDE_SIZE * 14) ;\ + SAVE_GPR(r15, BOOKE_GPR0_OFF + STRIDE_SIZE * 15) ;\ + SAVE_GPR(r16, BOOKE_GPR0_OFF + STRIDE_SIZE * 16) ;\ + SAVE_GPR(r17, BOOKE_GPR0_OFF + STRIDE_SIZE * 17) ;\ + SAVE_GPR(r18, BOOKE_GPR0_OFF + STRIDE_SIZE * 18) ;\ + SAVE_GPR(r19, BOOKE_GPR0_OFF + STRIDE_SIZE * 19) ;\ + SAVE_GPR(r20, BOOKE_GPR0_OFF + STRIDE_SIZE * 20) ;\ + SAVE_GPR(r21, BOOKE_GPR0_OFF + STRIDE_SIZE * 21) ;\ + SAVE_GPR(r22, BOOKE_GPR0_OFF + STRIDE_SIZE * 22) ;\ + SAVE_GPR(r23, BOOKE_GPR0_OFF + STRIDE_SIZE * 23) ;\ + SAVE_GPR(r24, BOOKE_GPR0_OFF + STRIDE_SIZE * 24) ;\ + SAVE_GPR(r25, BOOKE_GPR0_OFF + STRIDE_SIZE * 25) ;\ + SAVE_GPR(r26, BOOKE_GPR0_OFF + STRIDE_SIZE * 26) ;\ + SAVE_GPR(r27, BOOKE_GPR0_OFF + STRIDE_SIZE * 27) ;\ + SAVE_GPR(r28, BOOKE_GPR0_OFF + STRIDE_SIZE * 28) ;\ + SAVE_GPR(r29, BOOKE_GPR0_OFF + STRIDE_SIZE * 29) ;\ + SAVE_GPR(r30, BOOKE_GPR0_OFF + STRIDE_SIZE * 30) ;\ + SAVE_GPR(r31, BOOKE_GPR0_OFF + STRIDE_SIZE * 31) + +#define RESTORE_ALL_GPR \ + RESTORE_GPR(r1, BOOKE_GPR0_OFF + STRIDE_SIZE * 1) ;\ + RESTORE_GPR(r2, BOOKE_GPR0_OFF + STRIDE_SIZE * 2) ;\ + RESTORE_GPR(r13, BOOKE_GPR0_OFF + STRIDE_SIZE * 13) ;\ + RESTORE_GPR(r14, BOOKE_GPR0_OFF + STRIDE_SIZE * 14) ;\ + RESTORE_GPR(r15, BOOKE_GPR0_OFF + STRIDE_SIZE * 15) ;\ + RESTORE_GPR(r16, BOOKE_GPR0_OFF + STRIDE_SIZE * 16) ;\ + RESTORE_GPR(r17, BOOKE_GPR0_OFF + STRIDE_SIZE * 17) ;\ + RESTORE_GPR(r18, BOOKE_GPR0_OFF + STRIDE_SIZE * 18) ;\ + RESTORE_GPR(r19, BOOKE_GPR0_OFF + STRIDE_SIZE * 19) ;\ + RESTORE_GPR(r20, BOOKE_GPR0_OFF + STRIDE_SIZE * 20) ;\ + RESTORE_GPR(r21, BOOKE_GPR0_OFF + STRIDE_SIZE * 21) ;\ + RESTORE_GPR(r22, BOOKE_GPR0_OFF + STRIDE_SIZE * 22) ;\ + RESTORE_GPR(r23, BOOKE_GPR0_OFF + STRIDE_SIZE * 23) ;\ + RESTORE_GPR(r24, BOOKE_GPR0_OFF + STRIDE_SIZE * 24) ;\ + RESTORE_GPR(r25, BOOKE_GPR0_OFF + STRIDE_SIZE * 25) ;\ + RESTORE_GPR(r26, BOOKE_GPR0_OFF + STRIDE_SIZE * 26) ;\ + RESTORE_GPR(r27, BOOKE_GPR0_OFF + STRIDE_SIZE * 27) ;\ + RESTORE_GPR(r28, BOOKE_GPR0_OFF + STRIDE_SIZE * 28) ;\ + RESTORE_GPR(r29, BOOKE_GPR0_OFF + STRIDE_SIZE * 29) ;\ + RESTORE_GPR(r30, BOOKE_GPR0_OFF + STRIDE_SIZE * 30) ;\ + RESTORE_GPR(r31, BOOKE_GPR0_OFF + STRIDE_SIZE * 31) + +#define SAVE_ALL_SPRG \ + SAVE_SPR(SPRN_SPRG0, BOOKE_SPRG0_OFF + STRIDE_SIZE * 0) ;\ + SAVE_SPR(SPRN_SPRG1, BOOKE_SPRG0_OFF + STRIDE_SIZE * 1) ;\ + SAVE_SPR(SPRN_SPRG2, BOOKE_SPRG0_OFF + STRIDE_SIZE * 2) ;\ + SAVE_SPR(SPRN_SPRG3, BOOKE_SPRG0_OFF + STRIDE_SIZE * 3) ;\ + SAVE_SPR(SPRN_SPRG4, BOOKE_SPRG0_OFF + STRIDE_SIZE * 4) ;\ + SAVE_SPR(SPRN_SPRG5, BOOKE_SPRG0_OFF + STRIDE_SIZE * 5) ;\ + SAVE_SPR(SPRN_SPRG6, BOOKE_SPRG0_OFF + STRIDE_SIZE * 6) ;\ + SAVE_SPR(SPRN_SPRG7, BOOKE_SPRG0_OFF + STRIDE_SIZE * 7) ;\ + SAVE_SPR(SPRN_SPRG8, BOOKE_SPRG0_OFF + STRIDE_SIZE * 8) ;\ + SAVE_SPR(SPRN_SPRG9, BOOKE_SPRG0_OFF + STRIDE_SIZE * 9) + +#define RESTORE_ALL_SPRG \ + RESTORE_SPR(SPRN_SPRG0, BOOKE_SPRG0_OFF + STRIDE_SIZE * 0) ;\ + RESTORE_SPR(SPRN_SPRG1, BOOKE_SPRG0_OFF + STRIDE_SIZE * 1) ;\ + RESTORE_SPR(SPRN_SPRG2, BOOKE_SPRG0_OFF + STRIDE_SIZE * 2) ;\ + RESTORE_SPR(SPRN_SPRG3, BOOKE_SPRG0_OFF + STRIDE_SIZE * 3) ;\ + RESTORE_SPR(SPRN_SPRG4, BOOKE_SPRG0_OFF + STRIDE_SIZE * 4) ;\ + RESTORE_SPR(SPRN_SPRG5, BOOKE_SPRG0_OFF + STRIDE_SIZE * 5) ;\ + RESTORE_SPR(SPRN_SPRG6, BOOKE_SPRG0_OFF + STRIDE_SIZE * 6) ;\ + RESTORE_SPR(SPRN_SPRG7, BOOKE_SPRG0_OFF + STRIDE_SIZE * 7) ;\ + RESTORE_SPR(SPRN_SPRG8, BOOKE_SPRG0_OFF + STRIDE_SIZE * 8) ;\ + RESTORE_SPR(SPRN_SPRG9, BOOKE_SPRG0_OFF + STRIDE_SIZE * 9) + +#define SAVE_ALL_IVOR \ + SAVE_SPR(SPRN_IVOR0, BOOKE_IVOR0_OFF + STRIDE_SIZE * 0) ;\ + SAVE_SPR(SPRN_IVOR1, BOOKE_IVOR0_OFF + STRIDE_SIZE * 1) ;\ + SAVE_SPR(SPRN_IVOR2, BOOKE_IVOR0_OFF + STRIDE_SIZE * 2) ;\ + SAVE_SPR(SPRN_IVOR3, BOOKE_IVOR0_OFF + STRIDE_SIZE * 3) ;\ + SAVE_SPR(SPRN_IVOR4, BOOKE_IVOR0_OFF + STRIDE_SIZE * 4) ;\ + SAVE_SPR(SPRN_IVOR5, BOOKE_IVOR0_OFF + STRIDE_SIZE * 5) ;\ + SAVE_SPR(SPRN_IVOR6, BOOKE_IVOR0_OFF + STRIDE_SIZE * 6) ;\ + SAVE_SPR(SPRN_IVOR7, BOOKE_IVOR0_OFF + STRIDE_SIZE * 7) ;\ + SAVE_SPR(SPRN_IVOR8, BOOKE_IVOR0_OFF + STRIDE_SIZE * 8) ;\ + SAVE_SPR(SPRN_IVOR9, BOOKE_IVOR0_OFF + STRIDE_SIZE * 9) ;\ + SAVE_SPR(SPRN_IVOR10, BOOKE_IVOR0_OFF + STRIDE_SIZE * 10) ;\ + SAVE_SPR(SPRN_IVOR11, BOOKE_IVOR0_OFF + STRIDE_SIZE * 11) ;\ + SAVE_SPR(SPRN_IVOR12, BOOKE_IVOR0_OFF + STRIDE_SIZE * 12) ;\ + SAVE_SPR(SPRN_IVOR13, BOOKE_IVOR0_OFF + STRIDE_SIZE * 13) ;\ + SAVE_SPR(SPRN_IVOR14, BOOKE_IVOR0_OFF + STRIDE_SIZE * 14) ;\ + SAVE_SPR(SPRN_IVOR15, BOOKE_IVOR0_OFF + STRIDE_SIZE * 15) ;\ + SAVE_SPR(SPRN_IVOR35, BOOKE_IVOR0_OFF + STRIDE_SIZE * 35) ;\ + SAVE_SPR(SPRN_IVOR36, BOOKE_IVOR0_OFF + STRIDE_SIZE * 36) ;\ + SAVE_SPR(SPRN_IVOR37, BOOKE_IVOR0_OFF + STRIDE_SIZE * 37) ;\ + SAVE_SPR(SPRN_IVOR38, BOOKE_IVOR0_OFF + STRIDE_SIZE * 38) ;\ + SAVE_SPR(SPRN_IVOR39, BOOKE_IVOR0_OFF + STRIDE_SIZE * 39) ;\ + SAVE_SPR(SPRN_IVOR40, BOOKE_IVOR0_OFF + STRIDE_SIZE * 40) ;\ + SAVE_SPR(SPRN_IVOR41, BOOKE_IVOR0_OFF + STRIDE_SIZE * 41) + +#define RESTORE_ALL_IVOR \ + RESTORE_SPR(SPRN_IVOR0, BOOKE_IVOR0_OFF + STRIDE_SIZE * 0) ;\ + RESTORE_SPR(SPRN_IVOR1, BOOKE_IVOR0_OFF + STRIDE_SIZE * 1) ;\ + RESTORE_SPR(SPRN_IVOR2, BOOKE_IVOR0_OFF + STRIDE_SIZE * 2) ;\ + RESTORE_SPR(SPRN_IVOR3, BOOKE_IVOR0_OFF + STRIDE_SIZE * 3) ;\ + RESTORE_SPR(SPRN_IVOR4, BOOKE_IVOR0_OFF + STRIDE_SIZE * 4) ;\ + RESTORE_SPR(SPRN_IVOR5, BOOKE_IVOR0_OFF + STRIDE_SIZE * 5) ;\ + RESTORE_SPR(SPRN_IVOR6, BOOKE_IVOR0_OFF + STRIDE_SIZE * 6) ;\ + RESTORE_SPR(SPRN_IVOR7, BOOKE_IVOR0_OFF + STRIDE_SIZE * 7) ;\ + RESTORE_SPR(SPRN_IVOR8, BOOKE_IVOR0_OFF + STRIDE_SIZE * 8) ;\ + RESTORE_SPR(SPRN_IVOR9, BOOKE_IVOR0_OFF + STRIDE_SIZE * 9) ;\ + RESTORE_SPR(SPRN_IVOR10, BOOKE_IVOR0_OFF + STRIDE_SIZE * 10) ;\ + RESTORE_SPR(SPRN_IVOR11, BOOKE_IVOR0_OFF + STRIDE_SIZE * 11) ;\ + RESTORE_SPR(SPRN_IVOR12, BOOKE_IVOR0_OFF + STRIDE_SIZE * 12) ;\ + RESTORE_SPR(SPRN_IVOR13, BOOKE_IVOR0_OFF + STRIDE_SIZE * 13) ;\ + RESTORE_SPR(SPRN_IVOR14, BOOKE_IVOR0_OFF + STRIDE_SIZE * 14) ;\ + RESTORE_SPR(SPRN_IVOR15, BOOKE_IVOR0_OFF + STRIDE_SIZE * 15) ;\ + RESTORE_SPR(SPRN_IVOR35, BOOKE_IVOR0_OFF + STRIDE_SIZE * 35) ;\ + RESTORE_SPR(SPRN_IVOR36, BOOKE_IVOR0_OFF + STRIDE_SIZE * 36) ;\ + RESTORE_SPR(SPRN_IVOR37, BOOKE_IVOR0_OFF + STRIDE_SIZE * 37) ;\ + RESTORE_SPR(SPRN_IVOR38, BOOKE_IVOR0_OFF + STRIDE_SIZE * 38) ;\ + RESTORE_SPR(SPRN_IVOR39, BOOKE_IVOR0_OFF + STRIDE_SIZE * 39) ;\ + RESTORE_SPR(SPRN_IVOR40, BOOKE_IVOR0_OFF + STRIDE_SIZE * 40) ;\ + RESTORE_SPR(SPRN_IVOR41, BOOKE_IVOR0_OFF + STRIDE_SIZE * 41) + +/* reset time base to prevent from overflow */ +#define DELAY(count) \ + li r3, count; \ + li r4, 0; \ + mtspr SPRN_TBWL, r4; \ +101: mfspr r4, SPRN_TBRL; \ + cmpw r4, r3; \ + blt 101b + +#define FSL_DIS_ALL_IRQ \ + mfmsr r8; \ + rlwinm r8, r8, 0, ~MSR_CE; \ + rlwinm r8, r8, 0, ~MSR_ME; \ + rlwinm r8, r8, 0, ~MSR_EE; \ + rlwinm r8, r8, 0, ~MSR_DE; \ + mtmsr r8; \ + isync + + .section .data + .align 6 +regs_buffer: + .space BUFFER_SIZE + + .section .text +/* + * Save CPU registers + * r3 : the base address of the buffer which stores the values of registers + */ +e5500_cpu_state_save: + /* store the base address to r10 */ + mr r10, r3 + + SAVE_ALL_GPR + SAVE_ALL_SPRG + SAVE_ALL_IVOR + + SAVE_SPR(SPRN_IVPR, BOOKE_IVPR_OFF) + SAVE_SPR(SPRN_PID0, BOOKE_PID0_OFF) + SAVE_SPR(SPRN_EPCR, BOOKE_EPCR_OFF) + SAVE_SPR(SPRN_HID0, BOOKE_HID0_OFF) + SAVE_SPR(SPRN_PIR, BOOKE_PIR_OFF) + SAVE_SPR(SPRN_BUCSR, BOOKE_BUCSR_OFF) +1: + mfspr r5, SPRN_TBRU + mfspr r4, SPRN_TBRL + SAVE_GPR(r5, BOOKE_TBU_OFF) + SAVE_GPR(r4, BOOKE_TBL_OFF) + mfspr r3, SPRN_TBRU + cmpw r3, r5 + bne 1b + + blr + +/* + * Restore CPU registers + * r3 : the base address of the buffer which stores the values of registers + */ +e5500_cpu_state_restore: + /* store the base address to r10 */ + mr r10, r3 + + RESTORE_ALL_GPR + RESTORE_ALL_SPRG + RESTORE_ALL_IVOR + + RESTORE_SPR(SPRN_IVPR, BOOKE_IVPR_OFF) + RESTORE_SPR(SPRN_PID0, BOOKE_PID0_OFF) + RESTORE_SPR(SPRN_EPCR, BOOKE_EPCR_OFF) + RESTORE_SPR(SPRN_HID0, BOOKE_HID0_OFF) + RESTORE_SPR(SPRN_PIR, BOOKE_PIR_OFF) + RESTORE_SPR(SPRN_BUCSR, BOOKE_BUCSR_OFF) + + li r0, 0 + mtspr SPRN_TBWL, r0 + RESTORE_SPR(SPRN_TBWU, BOOKE_TBU_OFF) + RESTORE_SPR(SPRN_TBWL, BOOKE_TBL_OFF) + + blr + +#define CPC_CPCCSR0 0x0 +#define CPC_CPCCSR0_CPCFL 0x800 + +/* + * Flush the CPC cache. + * r3 : the base address of CPC + */ +flush_cpc_cache: + lwz r6, CPC_CPCCSR0(r3) + ori r6, r6, CPC_CPCCSR0_CPCFL + stw r6, CPC_CPCCSR0(r3) + sync + + /* Wait until completing the flush */ +1: lwz r6, CPC_CPCCSR0(r3) + andi. r6, r6, CPC_CPCCSR0_CPCFL + bne 1b + + blr + +/* + * the last stage to enter deep sleep + */ + .align 6 +_GLOBAL(fsl_dp_enter_low) +deepsleep_start: + LOAD_REG_ADDR(r9, buf_tmp) + /* save the return address and MSR */ + mflr r8 + PPC_STL r8, 0(r9) + mfmsr r8 + PPC_STL r8, 8(r9) + mfspr r8, SPRN_TCR + PPC_STL r8, 16(r9) + mfcr r8 + PPC_STL r8, 24(r9) + + li r8, 0 + mtspr SPRN_TCR, r8 + + /* save the parameters */ + PPC_STL r3, 32(r9) + + LOAD_REG_ADDR(r3, regs_buffer) + bl e5500_cpu_state_save + + /* restore the parameters */ + LOAD_REG_ADDR(r9, buf_tmp) + PPC_LL r31, 32(r9) + + /* flush caches inside CPU */ + LOAD_REG_ADDR(r3, cur_cpu_spec) + PPC_LL r3, 0(r3) + PPC_LL r3, CPU_DOWN_FLUSH(r3) + PPC_LCMPI 0, r3, 0 + beq 10f +#ifdef CONFIG_PPC64 + PPC_LL r3, 0(r3) +#endif + mtctr r3 + bctrl +10: + /* Flush the CPC cache */ + PPC_LL r3, CCSR_CPC_BASE(r31) + bl flush_cpc_cache + + /* prefecth TLB */ +#define CCSR_GPIO1_GPDAT 0x0008 +#define CCSR_GPIO1_GPDAT_29 0x4 + PPC_LL r11, CCSR_GPIO1_BASE(r31) + addi r11, r11, CCSR_GPIO1_GPDAT + lwz r10, 0(r11) + +#define CCSR_RCPM_PCPH15SETR 0x0b4 +#define CCSR_RCPM_PCPH15SETR_CORE0 0x1 + PPC_LL r12, CCSR_RCPM_BASE(r31) + addi r12, r12, CCSR_RCPM_PCPH15SETR + lwz r10, 0(r12) + +#define CCSR_DDR_SDRAM_CFG_2 0x114 +#define CCSR_DDR_SDRAM_CFG_2_FRC_SR 0x80000000 + PPC_LL r13, CCSR_DDR_BASE(r31) + addi r13, r13, CCSR_DDR_SDRAM_CFG_2 + lwz r10, 0(r13) + +#define DCSR_EPU_EPGCR 0x000 +#define DCSR_EPU_EPGCR_GCE 0x80000000 + PPC_LL r14, DCSR_EPU_BASE(r31) + addi r14, r14, DCSR_EPU_EPGCR + lwz r10, 0(r14) + +#define DCSR_EPU_EPECR15 0x33C +#define DCSR_EPU_EPECR15_IC0 0x80000000 + PPC_LL r15, DCSR_EPU_BASE(r31) + addi r15, r15, DCSR_EPU_EPECR15 + lwz r10, 0(r15) + +#define CCSR_SCFG_QMIFRSTCR 0x40c +#define CCSR_SCFG_QMIFRSTCR_QMIFRST 0x80000000 + PPC_LL r16, CCSR_SCFG_BASE(r31) + addi r16, r16, CCSR_SCFG_QMIFRSTCR + lwz r10, 0(r16) + + LOAD_REG_ADDR(r8, deepsleep_start) + LOAD_REG_ADDR(r9, deepsleep_end) + + /* prefecth code to cache so that executing code after disable DDR */ +1: icbtls 2, 0, r8 + addi r8, r8, 64 + cmpw r8, r9 + blt 1b + sync + + FSL_DIS_ALL_IRQ + + /* + * Place DDR controller in self refresh mode. + * From here on, can't access DDR any more. + */ + lwz r10, 0(r13) + oris r10, r10, CCSR_DDR_SDRAM_CFG_2_FRC_SR@h + stw r10, 0(r13) + lwz r10, 0(r13) + sync + + DELAY(500) + + /* + * Set GPIO1_29 to lock the signal MCKE down during deep sleep. + * The bootloader will clear it when wakeup. + */ + lwz r10, 0(r11) + ori r10, r10, CCSR_GPIO1_GPDAT_29 + stw r10, 0(r11) + lwz r10, 0(r11) + + DELAY(100) + + /* Reset QMan system bus interface */ + lwz r10, 0(r16) + oris r10, r10, CCSR_SCFG_QMIFRSTCR_QMIFRST@h + stw r10, 0(r16) + lwz r10, 0(r16) + + /* Enable all EPU Counters */ + li r10, 0 + oris r10, r10, DCSR_EPU_EPGCR_GCE@h + stw r10, 0(r14) + lwz r10, 0(r14) + + /* Enable SCU15 to trigger on RCPM Concentrator 0 */ + lwz r10, 0(r15) + oris r10, r10, DCSR_EPU_EPECR15_IC0@h + stw r10, 0(r15) + lwz r10, 0(r15) + + /* put Core0 in PH15 mode, trigger EPU FSM */ + lwz r10, 0(r12) + ori r10, r10, CCSR_RCPM_PCPH15SETR_CORE0 + stw r10, 0(r12) +2: + b 2b + + /* + * Leave some space to prevent prefeching instruction + * beyond deepsleep_end. The space also can be used as heap. + */ +buf_tmp: + .space 128 + .align 6 +deepsleep_end: + + .align 12 +#ifdef CONFIG_PPC32 +_GLOBAL(fsl_booke_deep_sleep_resume) + /* disable interrupts */ + FSL_DIS_ALL_IRQ + +#define ENTRY_DEEPSLEEP_SETUP +#define ENTRY_MAPPING_BOOT_SETUP +#include <../../kernel/fsl_booke_entry_mapping.S> +#undef ENTRY_DEEPSLEEP_SETUP +#undef ENTRY_MAPPING_BOOT_SETUP + + li r3, 0 + mfspr r4, SPRN_PIR + bl call_setup_cpu + + /* Load each CAM entry */ + LOAD_REG_ADDR(r3, tlbcam_index) + lwz r3, 0(r3) + mtctr r3 + li r9, 0 +3: mr r3, r9 + bl loadcam_entry + addi r9, r9, 1 + bdnz 3b + + /* restore cpu registers */ + LOAD_REG_ADDR(r3, regs_buffer) + bl e5500_cpu_state_restore + + /* restore return address */ + LOAD_REG_ADDR(r3, buf_tmp) + lwz r4, 16(r3) + mtspr SPRN_TCR, r4 + lwz r4, 0(r3) + mtlr r4 + lwz r4, 8(r3) + mtmsr r4 + lwz r4, 24(r3) + mtcr r4 + + blr + +#else /* CONFIG_PPC32 */ + +_GLOBAL(fsl_booke_deep_sleep_resume) + /* disable interrupts */ + FSL_DIS_ALL_IRQ + + /* switch to 64-bit mode */ + bl .enable_64b_mode + + /* set TOC pointer */ + bl .relative_toc + + /* setup initial TLBs, switch to kernel space ... */ + bl .start_initialization_book3e + + /* address space changed, set TOC pointer again */ + bl .relative_toc + + /* call a cpu state restore handler */ + LOAD_REG_ADDR(r23, cur_cpu_spec) + ld r23,0(r23) + ld r23,CPU_SPEC_RESTORE(r23) + cmpdi 0,r23,0 + beq 1f + ld r23,0(r23) + mtctr r23 + bctrl +1: + LOAD_REG_ADDR(r3, regs_buffer) + bl e5500_cpu_state_restore + + /* Load each CAM entry */ + LOAD_REG_ADDR(r3, tlbcam_index) + lwz r3, 0(r3) + mtctr r3 + li r0, 0 +3: mr r3, r0 + bl loadcam_entry + addi r0, r0, 1 + bdnz 3b + + /* restore return address */ + LOAD_REG_ADDR(r3, buf_tmp) + ld r4, 16(r3) + mtspr SPRN_TCR, r4 + ld r4, 0(r3) + mtlr r4 + ld r4, 8(r3) + mtmsr r4 + ld r4, 24(r3) + mtcr r4 + + blr + +#endif /* CONFIG_PPC32 */ diff --git a/arch/powerpc/sysdev/fsl_rcpm.c b/arch/powerpc/sysdev/fsl_rcpm.c index e5447ac..c2d8d37 100644 --- a/arch/powerpc/sysdev/fsl_rcpm.c +++ b/arch/powerpc/sysdev/fsl_rcpm.c @@ -249,7 +249,7 @@ static int rcpm_v2_plat_enter_sleep(int state) { u32 *pmcsr_reg = &rcpm_v2_regs->powmgtcsr; int ret = 0; - int result; + int result, cpu; switch (state) { case FSL_PM_SLEEP: @@ -269,6 +269,12 @@ static int rcpm_v2_plat_enter_sleep(int state) ret = -ETIMEDOUT; } break; + case FSL_PM_DEEP_SLEEP: + cpu = smp_processor_id(); + rcpm_v2_irq_mask(cpu); + ret = fsl_enter_deepsleep(); + rcpm_v2_irq_unmask(cpu); + break; default: pr_warn("Unknown platform PM state (%d)\n", state); ret = -EINVAL; -- 1.9.1