xref: /openbmc/linux/arch/arm/mach-socfpga/pm.c (revision 37e2d7d2)
19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
244fd8c7dSAlan Tull /*
344fd8c7dSAlan Tull  *  arch/arm/mach-socfpga/pm.c
444fd8c7dSAlan Tull  *
544fd8c7dSAlan Tull  * Copyright (C) 2014-2015 Altera Corporation. All rights reserved.
644fd8c7dSAlan Tull  *
744fd8c7dSAlan Tull  * with code from pm-imx6.c
844fd8c7dSAlan Tull  * Copyright 2011-2014 Freescale Semiconductor, Inc.
944fd8c7dSAlan Tull  * Copyright 2011 Linaro Ltd.
1044fd8c7dSAlan Tull  */
1144fd8c7dSAlan Tull 
1244fd8c7dSAlan Tull #include <linux/bitops.h>
1344fd8c7dSAlan Tull #include <linux/genalloc.h>
1444fd8c7dSAlan Tull #include <linux/init.h>
1544fd8c7dSAlan Tull #include <linux/io.h>
16*37e2d7d2SRob Herring #include <linux/of.h>
1744fd8c7dSAlan Tull #include <linux/of_platform.h>
18*37e2d7d2SRob Herring #include <linux/platform_device.h>
1944fd8c7dSAlan Tull #include <linux/suspend.h>
2044fd8c7dSAlan Tull #include <asm/suspend.h>
2144fd8c7dSAlan Tull #include <asm/fncpy.h>
2244fd8c7dSAlan Tull #include "core.h"
2344fd8c7dSAlan Tull 
2444fd8c7dSAlan Tull /* Pointer to function copied to ocram */
2544fd8c7dSAlan Tull static u32 (*socfpga_sdram_self_refresh_in_ocram)(u32 sdr_base);
2644fd8c7dSAlan Tull 
socfpga_setup_ocram_self_refresh(void)2744fd8c7dSAlan Tull static int socfpga_setup_ocram_self_refresh(void)
2844fd8c7dSAlan Tull {
2944fd8c7dSAlan Tull 	struct platform_device *pdev;
3044fd8c7dSAlan Tull 	phys_addr_t ocram_pbase;
3144fd8c7dSAlan Tull 	struct device_node *np;
3244fd8c7dSAlan Tull 	struct gen_pool *ocram_pool;
3344fd8c7dSAlan Tull 	unsigned long ocram_base;
3444fd8c7dSAlan Tull 	void __iomem *suspend_ocram_base;
3544fd8c7dSAlan Tull 	int ret = 0;
3644fd8c7dSAlan Tull 
3744fd8c7dSAlan Tull 	np = of_find_compatible_node(NULL, NULL, "mmio-sram");
3844fd8c7dSAlan Tull 	if (!np) {
3944fd8c7dSAlan Tull 		pr_err("%s: Unable to find mmio-sram in dtb\n", __func__);
4044fd8c7dSAlan Tull 		return -ENODEV;
4144fd8c7dSAlan Tull 	}
4244fd8c7dSAlan Tull 
4344fd8c7dSAlan Tull 	pdev = of_find_device_by_node(np);
4444fd8c7dSAlan Tull 	if (!pdev) {
4544fd8c7dSAlan Tull 		pr_warn("%s: failed to find ocram device!\n", __func__);
4644fd8c7dSAlan Tull 		ret = -ENODEV;
4744fd8c7dSAlan Tull 		goto put_node;
4844fd8c7dSAlan Tull 	}
4944fd8c7dSAlan Tull 
5073858173SVladimir Zapolskiy 	ocram_pool = gen_pool_get(&pdev->dev, NULL);
5144fd8c7dSAlan Tull 	if (!ocram_pool) {
5244fd8c7dSAlan Tull 		pr_warn("%s: ocram pool unavailable!\n", __func__);
5344fd8c7dSAlan Tull 		ret = -ENODEV;
543ad7b4e8SYu Kuai 		goto put_device;
5544fd8c7dSAlan Tull 	}
5644fd8c7dSAlan Tull 
5744fd8c7dSAlan Tull 	ocram_base = gen_pool_alloc(ocram_pool, socfpga_sdram_self_refresh_sz);
5844fd8c7dSAlan Tull 	if (!ocram_base) {
5944fd8c7dSAlan Tull 		pr_warn("%s: unable to alloc ocram!\n", __func__);
6044fd8c7dSAlan Tull 		ret = -ENOMEM;
613ad7b4e8SYu Kuai 		goto put_device;
6244fd8c7dSAlan Tull 	}
6344fd8c7dSAlan Tull 
6444fd8c7dSAlan Tull 	ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base);
6544fd8c7dSAlan Tull 
6644fd8c7dSAlan Tull 	suspend_ocram_base = __arm_ioremap_exec(ocram_pbase,
6744fd8c7dSAlan Tull 						socfpga_sdram_self_refresh_sz,
6844fd8c7dSAlan Tull 						false);
6944fd8c7dSAlan Tull 	if (!suspend_ocram_base) {
7044fd8c7dSAlan Tull 		pr_warn("%s: __arm_ioremap_exec failed!\n", __func__);
7144fd8c7dSAlan Tull 		ret = -ENOMEM;
723ad7b4e8SYu Kuai 		goto put_device;
7344fd8c7dSAlan Tull 	}
7444fd8c7dSAlan Tull 
7544fd8c7dSAlan Tull 	/* Copy the code that puts DDR in self refresh to ocram */
7644fd8c7dSAlan Tull 	socfpga_sdram_self_refresh_in_ocram =
7744fd8c7dSAlan Tull 		(void *)fncpy(suspend_ocram_base,
7844fd8c7dSAlan Tull 			      &socfpga_sdram_self_refresh,
7944fd8c7dSAlan Tull 			      socfpga_sdram_self_refresh_sz);
8044fd8c7dSAlan Tull 
8144fd8c7dSAlan Tull 	WARN(!socfpga_sdram_self_refresh_in_ocram,
8244fd8c7dSAlan Tull 	     "could not copy function to ocram");
8344fd8c7dSAlan Tull 	if (!socfpga_sdram_self_refresh_in_ocram)
8444fd8c7dSAlan Tull 		ret = -EFAULT;
8544fd8c7dSAlan Tull 
863ad7b4e8SYu Kuai put_device:
873ad7b4e8SYu Kuai 	put_device(&pdev->dev);
8844fd8c7dSAlan Tull put_node:
8944fd8c7dSAlan Tull 	of_node_put(np);
9044fd8c7dSAlan Tull 
9144fd8c7dSAlan Tull 	return ret;
9244fd8c7dSAlan Tull }
9344fd8c7dSAlan Tull 
socfpga_pm_suspend(unsigned long arg)9444fd8c7dSAlan Tull static int socfpga_pm_suspend(unsigned long arg)
9544fd8c7dSAlan Tull {
9644fd8c7dSAlan Tull 	u32 ret;
9744fd8c7dSAlan Tull 
9844fd8c7dSAlan Tull 	if (!sdr_ctl_base_addr)
9944fd8c7dSAlan Tull 		return -EFAULT;
10044fd8c7dSAlan Tull 
10144fd8c7dSAlan Tull 	ret = socfpga_sdram_self_refresh_in_ocram((u32)sdr_ctl_base_addr);
10244fd8c7dSAlan Tull 
10344fd8c7dSAlan Tull 	pr_debug("%s self-refresh loops request=%d exit=%d\n", __func__,
10444fd8c7dSAlan Tull 		 ret & 0xffff, (ret >> 16) & 0xffff);
10544fd8c7dSAlan Tull 
10644fd8c7dSAlan Tull 	return 0;
10744fd8c7dSAlan Tull }
10844fd8c7dSAlan Tull 
socfpga_pm_enter(suspend_state_t state)10944fd8c7dSAlan Tull static int socfpga_pm_enter(suspend_state_t state)
11044fd8c7dSAlan Tull {
11144fd8c7dSAlan Tull 	switch (state) {
11244fd8c7dSAlan Tull 	case PM_SUSPEND_MEM:
11344fd8c7dSAlan Tull 		outer_disable();
11444fd8c7dSAlan Tull 		cpu_suspend(0, socfpga_pm_suspend);
11544fd8c7dSAlan Tull 		outer_resume();
11644fd8c7dSAlan Tull 		break;
11744fd8c7dSAlan Tull 	default:
11844fd8c7dSAlan Tull 		return -EINVAL;
11944fd8c7dSAlan Tull 	}
12044fd8c7dSAlan Tull 	return 0;
12144fd8c7dSAlan Tull }
12244fd8c7dSAlan Tull 
12344fd8c7dSAlan Tull static const struct platform_suspend_ops socfpga_pm_ops = {
12444fd8c7dSAlan Tull 	.valid	= suspend_valid_only_mem,
12544fd8c7dSAlan Tull 	.enter	= socfpga_pm_enter,
12644fd8c7dSAlan Tull };
12744fd8c7dSAlan Tull 
socfpga_pm_init(void)12844fd8c7dSAlan Tull static int __init socfpga_pm_init(void)
12944fd8c7dSAlan Tull {
13044fd8c7dSAlan Tull 	int ret;
13144fd8c7dSAlan Tull 
13244fd8c7dSAlan Tull 	ret = socfpga_setup_ocram_self_refresh();
13344fd8c7dSAlan Tull 	if (ret)
13444fd8c7dSAlan Tull 		return ret;
13544fd8c7dSAlan Tull 
13644fd8c7dSAlan Tull 	suspend_set_ops(&socfpga_pm_ops);
13744fd8c7dSAlan Tull 	pr_info("SoCFPGA initialized for DDR self-refresh during suspend.\n");
13844fd8c7dSAlan Tull 
13944fd8c7dSAlan Tull 	return 0;
14044fd8c7dSAlan Tull }
14144fd8c7dSAlan Tull arch_initcall(socfpga_pm_init);
142