1 /* 2 * arch/arm/mach-socfpga/pm.c 3 * 4 * Copyright (C) 2014-2015 Altera Corporation. All rights reserved. 5 * 6 * with code from pm-imx6.c 7 * Copyright 2011-2014 Freescale Semiconductor, Inc. 8 * Copyright 2011 Linaro Ltd. 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms and conditions of the GNU General Public License, 12 * version 2, as published by the Free Software Foundation. 13 * 14 * This program is distributed in the hope it will be useful, but WITHOUT 15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 * more details. 18 * 19 * You should have received a copy of the GNU General Public License along with 20 * this program. If not, see <http://www.gnu.org/licenses/>. 21 */ 22 23 #include <linux/bitops.h> 24 #include <linux/genalloc.h> 25 #include <linux/init.h> 26 #include <linux/io.h> 27 #include <linux/of_platform.h> 28 #include <linux/suspend.h> 29 #include <asm/suspend.h> 30 #include <asm/fncpy.h> 31 #include "core.h" 32 33 /* Pointer to function copied to ocram */ 34 static u32 (*socfpga_sdram_self_refresh_in_ocram)(u32 sdr_base); 35 36 static int socfpga_setup_ocram_self_refresh(void) 37 { 38 struct platform_device *pdev; 39 phys_addr_t ocram_pbase; 40 struct device_node *np; 41 struct gen_pool *ocram_pool; 42 unsigned long ocram_base; 43 void __iomem *suspend_ocram_base; 44 int ret = 0; 45 46 np = of_find_compatible_node(NULL, NULL, "mmio-sram"); 47 if (!np) { 48 pr_err("%s: Unable to find mmio-sram in dtb\n", __func__); 49 return -ENODEV; 50 } 51 52 pdev = of_find_device_by_node(np); 53 if (!pdev) { 54 pr_warn("%s: failed to find ocram device!\n", __func__); 55 ret = -ENODEV; 56 goto put_node; 57 } 58 59 ocram_pool = gen_pool_get(&pdev->dev); 60 if (!ocram_pool) { 61 pr_warn("%s: ocram pool unavailable!\n", __func__); 62 ret = -ENODEV; 63 goto put_node; 64 } 65 66 ocram_base = gen_pool_alloc(ocram_pool, socfpga_sdram_self_refresh_sz); 67 if (!ocram_base) { 68 pr_warn("%s: unable to alloc ocram!\n", __func__); 69 ret = -ENOMEM; 70 goto put_node; 71 } 72 73 ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base); 74 75 suspend_ocram_base = __arm_ioremap_exec(ocram_pbase, 76 socfpga_sdram_self_refresh_sz, 77 false); 78 if (!suspend_ocram_base) { 79 pr_warn("%s: __arm_ioremap_exec failed!\n", __func__); 80 ret = -ENOMEM; 81 goto put_node; 82 } 83 84 /* Copy the code that puts DDR in self refresh to ocram */ 85 socfpga_sdram_self_refresh_in_ocram = 86 (void *)fncpy(suspend_ocram_base, 87 &socfpga_sdram_self_refresh, 88 socfpga_sdram_self_refresh_sz); 89 90 WARN(!socfpga_sdram_self_refresh_in_ocram, 91 "could not copy function to ocram"); 92 if (!socfpga_sdram_self_refresh_in_ocram) 93 ret = -EFAULT; 94 95 put_node: 96 of_node_put(np); 97 98 return ret; 99 } 100 101 static int socfpga_pm_suspend(unsigned long arg) 102 { 103 u32 ret; 104 105 if (!sdr_ctl_base_addr) 106 return -EFAULT; 107 108 ret = socfpga_sdram_self_refresh_in_ocram((u32)sdr_ctl_base_addr); 109 110 pr_debug("%s self-refresh loops request=%d exit=%d\n", __func__, 111 ret & 0xffff, (ret >> 16) & 0xffff); 112 113 return 0; 114 } 115 116 static int socfpga_pm_enter(suspend_state_t state) 117 { 118 switch (state) { 119 case PM_SUSPEND_STANDBY: 120 case PM_SUSPEND_MEM: 121 outer_disable(); 122 cpu_suspend(0, socfpga_pm_suspend); 123 outer_resume(); 124 break; 125 default: 126 return -EINVAL; 127 } 128 return 0; 129 } 130 131 static const struct platform_suspend_ops socfpga_pm_ops = { 132 .valid = suspend_valid_only_mem, 133 .enter = socfpga_pm_enter, 134 }; 135 136 static int __init socfpga_pm_init(void) 137 { 138 int ret; 139 140 ret = socfpga_setup_ocram_self_refresh(); 141 if (ret) 142 return ret; 143 144 suspend_set_ops(&socfpga_pm_ops); 145 pr_info("SoCFPGA initialized for DDR self-refresh during suspend.\n"); 146 147 return 0; 148 } 149 arch_initcall(socfpga_pm_init); 150