^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * arch/arm/mach-socfpga/pm.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2014-2015 Altera Corporation. All rights reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * with code from pm-imx6.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Copyright 2011-2014 Freescale Semiconductor, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * Copyright 2011 Linaro Ltd.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/bitops.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/genalloc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/of_platform.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/suspend.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <asm/suspend.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <asm/fncpy.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include "core.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) /* Pointer to function copied to ocram */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) static u32 (*socfpga_sdram_self_refresh_in_ocram)(u32 sdr_base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) static int socfpga_setup_ocram_self_refresh(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) struct platform_device *pdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) phys_addr_t ocram_pbase;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) struct device_node *np;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) struct gen_pool *ocram_pool;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) unsigned long ocram_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) void __iomem *suspend_ocram_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) np = of_find_compatible_node(NULL, NULL, "mmio-sram");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) if (!np) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) pr_err("%s: Unable to find mmio-sram in dtb\n", __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) pdev = of_find_device_by_node(np);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) if (!pdev) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) pr_warn("%s: failed to find ocram device!\n", __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) ret = -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) goto put_node;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) ocram_pool = gen_pool_get(&pdev->dev, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) if (!ocram_pool) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) pr_warn("%s: ocram pool unavailable!\n", __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) ret = -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) goto put_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) ocram_base = gen_pool_alloc(ocram_pool, socfpga_sdram_self_refresh_sz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) if (!ocram_base) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) pr_warn("%s: unable to alloc ocram!\n", __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) ret = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) goto put_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) suspend_ocram_base = __arm_ioremap_exec(ocram_pbase,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) socfpga_sdram_self_refresh_sz,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) false);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) if (!suspend_ocram_base) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) pr_warn("%s: __arm_ioremap_exec failed!\n", __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) ret = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) goto put_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) /* Copy the code that puts DDR in self refresh to ocram */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) socfpga_sdram_self_refresh_in_ocram =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) (void *)fncpy(suspend_ocram_base,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) &socfpga_sdram_self_refresh,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) socfpga_sdram_self_refresh_sz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) WARN(!socfpga_sdram_self_refresh_in_ocram,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) "could not copy function to ocram");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) if (!socfpga_sdram_self_refresh_in_ocram)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) ret = -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) put_device:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) put_device(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) put_node:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) of_node_put(np);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) static int socfpga_pm_suspend(unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) u32 ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) if (!sdr_ctl_base_addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) ret = socfpga_sdram_self_refresh_in_ocram((u32)sdr_ctl_base_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) pr_debug("%s self-refresh loops request=%d exit=%d\n", __func__,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) ret & 0xffff, (ret >> 16) & 0xffff);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) static int socfpga_pm_enter(suspend_state_t state)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) switch (state) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) case PM_SUSPEND_MEM:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) outer_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) cpu_suspend(0, socfpga_pm_suspend);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) outer_resume();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) static const struct platform_suspend_ops socfpga_pm_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) .valid = suspend_valid_only_mem,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) .enter = socfpga_pm_enter,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) static int __init socfpga_pm_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) ret = socfpga_setup_ocram_self_refresh();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) suspend_set_ops(&socfpga_pm_ops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) pr_info("SoCFPGA initialized for DDR self-refresh during suspend.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) arch_initcall(socfpga_pm_init);