On 09/24/2014 03:27 PM, at...@opensource.altera.com wrote: > From: Alan Tull <at...@opensource.altera.com> > > Add code that requests that the sdr controller go into > self-refresh mode. This code is run from ocram. > > This patch assumes that u-boot has already configured sdr: > sdr.ctrlcfg.lowpwreq.selfrfshmask = 3 > sdr.ctrlcfg.lowpwrtiming.clkdisablecycles = 8 > sdr.ctrlcfg.dramtiming4.selfrfshexit = 512 > > How to suspend to ram: > $ echo enabled > \ > /sys/devices/soc/ffc02000.serial0/tty/ttyS0/power/wakeup > > $ echo -n mem > /sys/power/state > > Signed-off-by: Alan Tull <at...@opensource.altera.com> > --- > arch/arm/mach-socfpga/Makefile | 1 + > arch/arm/mach-socfpga/core.h | 4 + > arch/arm/mach-socfpga/pm.c | 141 ++++++++++++++++++++++++++++++++ > arch/arm/mach-socfpga/self-refresh.S | 148 > ++++++++++++++++++++++++++++++++++ > arch/arm/mach-socfpga/socfpga.c | 10 +++ > 5 files changed, 304 insertions(+) > create mode 100644 arch/arm/mach-socfpga/pm.c > create mode 100644 arch/arm/mach-socfpga/self-refresh.S > > diff --git a/arch/arm/mach-socfpga/Makefile b/arch/arm/mach-socfpga/Makefile > index 6dd7a93..0591927 100644 > --- a/arch/arm/mach-socfpga/Makefile > +++ b/arch/arm/mach-socfpga/Makefile > @@ -4,3 +4,4 @@ > > obj-y := socfpga.o > obj-$(CONFIG_SMP) += headsmp.o platsmp.o > +obj-$(CONFIG_SUSPEND) += pm.o self-refresh.o > diff --git a/arch/arm/mach-socfpga/core.h b/arch/arm/mach-socfpga/core.h > index c4a0929..cc1a2fb 100644 > --- a/arch/arm/mach-socfpga/core.h > +++ b/arch/arm/mach-socfpga/core.h > @@ -38,6 +38,7 @@ extern void socfpga_sysmgr_init(void); > > extern void __iomem *sys_manager_base_addr; > extern void __iomem *rst_manager_base_addr; > +extern void __iomem *sdr_ctl_base_addr; > > extern struct smp_operations socfpga_smp_ops; > extern char secondary_trampoline, secondary_trampoline_end; > @@ -46,4 +47,7 @@ extern unsigned long cpu1start_addr; > > #define SOCFPGA_SCU_VIRT_BASE 0xfffec000 > > +u32 socfpga_sdram_self_refresh(u32 sdr_base, u32 scu_base); > +extern unsigned int socfpga_sdram_self_refresh_sz; > + > #endif > diff --git a/arch/arm/mach-socfpga/pm.c b/arch/arm/mach-socfpga/pm.c > new file mode 100644 > index 0000000..02c3719 > --- /dev/null > +++ b/arch/arm/mach-socfpga/pm.c > @@ -0,0 +1,141 @@ > +/* > + * arch/arm/mach-socfpga/pm.c > + * > + * Copyright (C) 2014 Altera Corporation. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along > with > + * this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/bitops.h> > +#include <linux/init.h> > +#include <linux/io.h> > +#include <linux/of_platform.h> > +#include <linux/suspend.h> > +#include <asm/suspend.h> > +#include <asm/fncpy.h> > +#include "core.h" > + > +/* Pointer to function copied to ocram */ > +static u32 (*socfpga_sdram_self_refresh_in_ocram)(u32 sdr_base, u32 > scu_base); > + > +/* Round up a pointer address to fix aligment for fncpy() */ > +static void *fncpy_align(void *ptr) > +{ > + u32 value = (u32)ptr; > + > + if ((value & (FNCPY_ALIGN - 1)) != 0) > + value = ((value & ~(FNCPY_ALIGN - 1)) + FNCPY_ALIGN); > + > + return (void *)value; > +} > + > +static void *socfpga_init_ocram_exec(void) > +{ > + struct device_node *np; > + const __be32 *prop; > + u32 ocram_hwaddr, len; > + void __iomem *iomem_exec_ocram; > + size_t size; > + > + np = of_find_compatible_node(NULL, NULL, "mmio-sram"); > + if (!np) { > + pr_err("SOCFPGA: Unable to find mmio-sram in dtb\n"); > + return 0; > + } > + > + /* Determine the OCRAM address and size */ > + prop = of_get_property(np, "reg", &size); > + ocram_hwaddr = be32_to_cpup(prop++); > + len = be32_to_cpup(prop); > + > + if (!prop || size < sizeof(*prop)) { > + pr_err("SOCFPGA: Unable to find OCRAM mapping in dtb\n"); > + return 0; > + } > + > + iomem_exec_ocram = __arm_ioremap_exec(ocram_hwaddr, len, 0);
The call to __arm_ioremap_exec can fail. Also, this doesn't seem right, what if a good chunk of OCRAM has been used by some other driver in the system? From what I've seen, OCRAM should be using the generic allocator framework. > + > + /* Fix alignment to work with fncpy */ > + iomem_exec_ocram = fncpy_align(iomem_exec_ocram); > + > + return iomem_exec_ocram; > +} > + > +static int socfpga_setup_ocram_self_refresh(void) > +{ > + void *ocram_addr; > + > + /* Configure ocram and make it executable */ > + ocram_addr = socfpga_init_ocram_exec(); > + WARN(!ocram_addr, "Unable to initialize ocram for pm"); > + if (!ocram_addr) > + return -EFAULT; > + > + /* Copy the code that puts DDR in self refresh to ocram */ > + socfpga_sdram_self_refresh_in_ocram = > + (void *)fncpy((void *)ocram_addr, > + &socfpga_sdram_self_refresh, > + socfpga_sdram_self_refresh_sz); > + > + WARN(!socfpga_sdram_self_refresh_in_ocram, > + "could not copy function to ocram"); > + if (!socfpga_sdram_self_refresh_in_ocram) > + return -EFAULT; > + > + return 0; > +} > + > +static int socfpga_pm_suspend(unsigned long arg) > +{ > + u32 ret; > + > + ret = socfpga_sdram_self_refresh_in_ocram((u32)sdr_ctl_base_addr, > + (u32)socfpga_scu_base_addr); What if sdr_ctl_base_addr is not valid? BR, Dinh -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/