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