Merge branch 'socfpga/soc' into next/soc

* socfpga/soc:
  ARM: socfpga: support suspend to ram
  ARM: socfpga: add CPU_METHOD_OF_DECLARE for Arria 10
  ARM: socfpga: use CPU_METHOD_OF_DECLARE for socfpga_cyclone5
This commit is contained in:
Kevin Hilman 2015-06-10 16:04:48 -07:00
commit 2f69b1a4e8
7 changed files with 342 additions and 6 deletions

View file

@ -1,4 +1,4 @@
config ARCH_SOCFPGA
menuconfig ARCH_SOCFPGA
bool "Altera SOCFPGA family" if ARCH_MULTI_V7
select ARCH_SUPPORTS_BIG_ENDIAN
select ARM_AMBA
@ -9,3 +9,11 @@ config ARCH_SOCFPGA
select HAVE_ARM_SCU
select HAVE_ARM_TWD if SMP
select MFD_SYSCON
if ARCH_SOCFPGA
config SOCFPGA_SUSPEND
bool "Suspend to RAM on SOCFPGA"
help
Select this if you want to enable Suspend-to-RAM on SOCFPGA
platforms.
endif

View file

@ -4,3 +4,4 @@
obj-y := socfpga.o
obj-$(CONFIG_SMP) += headsmp.o platsmp.o
obj-$(CONFIG_SOCFPGA_SUSPEND) += pm.o self-refresh.o

View file

@ -1,6 +1,6 @@
/*
* Copyright 2012 Pavel Machek <pavel@denx.de>
* Copyright (C) 2012 Altera Corporation
* Copyright (C) 2012-2015 Altera Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -25,6 +25,8 @@
#define SOCFPGA_RSTMGR_MODPERRST 0x14
#define SOCFPGA_RSTMGR_BRGMODRST 0x1c
#define SOCFPGA_A10_RSTMGR_MODMPURST 0x20
/* System Manager bits */
#define RSTMGR_CTRL_SWCOLDRSTREQ 0x1 /* Cold Reset */
#define RSTMGR_CTRL_SWWARMRSTREQ 0x2 /* Warm Reset */
@ -38,8 +40,11 @@ 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;
u32 socfpga_sdram_self_refresh(u32 sdr_base);
extern unsigned int socfpga_sdram_self_refresh_sz;
extern struct smp_operations socfpga_smp_ops;
extern char secondary_trampoline, secondary_trampoline_end;
extern unsigned long socfpga_cpu1start_addr;

View file

@ -54,6 +54,29 @@ static int socfpga_boot_secondary(unsigned int cpu, struct task_struct *idle)
return 0;
}
static int socfpga_a10_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
int trampoline_size = &secondary_trampoline_end - &secondary_trampoline;
if (socfpga_cpu1start_addr) {
writel(RSTMGR_MPUMODRST_CPU1, rst_manager_base_addr +
SOCFPGA_A10_RSTMGR_MODMPURST);
memcpy(phys_to_virt(0), &secondary_trampoline, trampoline_size);
writel(virt_to_phys(socfpga_secondary_startup),
sys_manager_base_addr + (socfpga_cpu1start_addr & 0x00000fff));
flush_cache_all();
smp_wmb();
outer_clean_range(0, trampoline_size);
/* This will release CPU #1 out of reset. */
writel(0, rst_manager_base_addr + SOCFPGA_A10_RSTMGR_MODMPURST);
}
return 0;
}
static void __init socfpga_smp_prepare_cpus(unsigned int max_cpus)
{
struct device_node *np;
@ -83,10 +106,21 @@ static void socfpga_cpu_die(unsigned int cpu)
cpu_do_idle();
}
struct smp_operations socfpga_smp_ops __initdata = {
static struct smp_operations socfpga_smp_ops __initdata = {
.smp_prepare_cpus = socfpga_smp_prepare_cpus,
.smp_boot_secondary = socfpga_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_die = socfpga_cpu_die,
#endif
};
static struct smp_operations socfpga_a10_smp_ops __initdata = {
.smp_prepare_cpus = socfpga_smp_prepare_cpus,
.smp_boot_secondary = socfpga_a10_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_die = socfpga_cpu_die,
#endif
};
CPU_METHOD_OF_DECLARE(socfpga_smp, "altr,socfpga-smp", &socfpga_smp_ops);
CPU_METHOD_OF_DECLARE(socfpga_a10_smp, "altr,socfpga-a10-smp", &socfpga_a10_smp_ops);

149
arch/arm/mach-socfpga/pm.c Normal file
View file

@ -0,0 +1,149 @@
/*
* arch/arm/mach-socfpga/pm.c
*
* Copyright (C) 2014-2015 Altera Corporation. All rights reserved.
*
* with code from pm-imx6.c
* Copyright 2011-2014 Freescale Semiconductor, Inc.
* Copyright 2011 Linaro Ltd.
*
* 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/genalloc.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);
static int socfpga_setup_ocram_self_refresh(void)
{
struct platform_device *pdev;
phys_addr_t ocram_pbase;
struct device_node *np;
struct gen_pool *ocram_pool;
unsigned long ocram_base;
void __iomem *suspend_ocram_base;
int ret = 0;
np = of_find_compatible_node(NULL, NULL, "mmio-sram");
if (!np) {
pr_err("%s: Unable to find mmio-sram in dtb\n", __func__);
return -ENODEV;
}
pdev = of_find_device_by_node(np);
if (!pdev) {
pr_warn("%s: failed to find ocram device!\n", __func__);
ret = -ENODEV;
goto put_node;
}
ocram_pool = dev_get_gen_pool(&pdev->dev);
if (!ocram_pool) {
pr_warn("%s: ocram pool unavailable!\n", __func__);
ret = -ENODEV;
goto put_node;
}
ocram_base = gen_pool_alloc(ocram_pool, socfpga_sdram_self_refresh_sz);
if (!ocram_base) {
pr_warn("%s: unable to alloc ocram!\n", __func__);
ret = -ENOMEM;
goto put_node;
}
ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base);
suspend_ocram_base = __arm_ioremap_exec(ocram_pbase,
socfpga_sdram_self_refresh_sz,
false);
if (!suspend_ocram_base) {
pr_warn("%s: __arm_ioremap_exec failed!\n", __func__);
ret = -ENOMEM;
goto put_node;
}
/* Copy the code that puts DDR in self refresh to ocram */
socfpga_sdram_self_refresh_in_ocram =
(void *)fncpy(suspend_ocram_base,
&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)
ret = -EFAULT;
put_node:
of_node_put(np);
return ret;
}
static int socfpga_pm_suspend(unsigned long arg)
{
u32 ret;
if (!sdr_ctl_base_addr)
return -EFAULT;
ret = socfpga_sdram_self_refresh_in_ocram((u32)sdr_ctl_base_addr);
pr_debug("%s self-refresh loops request=%d exit=%d\n", __func__,
ret & 0xffff, (ret >> 16) & 0xffff);
return 0;
}
static int socfpga_pm_enter(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_STANDBY:
case PM_SUSPEND_MEM:
outer_disable();
cpu_suspend(0, socfpga_pm_suspend);
outer_resume();
break;
default:
return -EINVAL;
}
return 0;
}
static const struct platform_suspend_ops socfpga_pm_ops = {
.valid = suspend_valid_only_mem,
.enter = socfpga_pm_enter,
};
static int __init socfpga_pm_init(void)
{
int ret;
ret = socfpga_setup_ocram_self_refresh();
if (ret)
return ret;
suspend_set_ops(&socfpga_pm_ops);
pr_info("SoCFPGA initialized for DDR self-refresh during suspend.\n");
return 0;
}
arch_initcall(socfpga_pm_init);

View file

@ -0,0 +1,136 @@
/*
* Copyright (C) 2014-2015 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/linkage.h>
#include <asm/assembler.h>
#define MAX_LOOP_COUNT 1000
/* Register offset */
#define SDR_CTRLGRP_LOWPWREQ_ADDR 0x54
#define SDR_CTRLGRP_LOWPWRACK_ADDR 0x58
/* Bitfield positions */
#define SELFRSHREQ_POS 3
#define SELFRSHREQ_MASK 0x8
#define SELFRFSHACK_POS 1
#define SELFRFSHACK_MASK 0x2
/*
* This code assumes that when the bootloader configured
* the sdram controller for the DDR on the board it
* configured the following fields depending on the DDR
* vendor/configuration:
*
* sdr.ctrlcfg.lowpwreq.selfrfshmask
* sdr.ctrlcfg.lowpwrtiming.clkdisablecycles
* sdr.ctrlcfg.dramtiming4.selfrfshexit
*/
.arch armv7-a
.text
.align 3
/*
* socfpga_sdram_self_refresh
*
* r0 : sdr_ctl_base_addr
* r1 : temp storage of return value
* r2 : temp storage of register values
* r3 : loop counter
*
* return value: lower 16 bits: loop count going into self refresh
* upper 16 bits: loop count exiting self refresh
*/
ENTRY(socfpga_sdram_self_refresh)
/* Enable dynamic clock gating in the Power Control Register. */
mrc p15, 0, r2, c15, c0, 0
orr r2, r2, #1
mcr p15, 0, r2, c15, c0, 0
/* Enable self refresh: set sdr.ctrlgrp.lowpwreq.selfrshreq = 1 */
ldr r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
orr r2, r2, #SELFRSHREQ_MASK
str r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
/* Poll until sdr.ctrlgrp.lowpwrack.selfrfshack == 1 or hit max loops */
mov r3, #0
while_ack_0:
ldr r2, [r0, #SDR_CTRLGRP_LOWPWRACK_ADDR]
and r2, r2, #SELFRFSHACK_MASK
cmp r2, #SELFRFSHACK_MASK
beq ack_1
add r3, #1
cmp r3, #MAX_LOOP_COUNT
bne while_ack_0
ack_1:
mov r1, r3
/*
* Execute an ISB instruction to ensure that all of the
* CP15 register changes have been committed.
*/
isb
/*
* Execute a barrier instruction to ensure that all cache,
* TLB and branch predictor maintenance operations issued
* by any CPU in the cluster have completed.
*/
dsb
dmb
wfi
/* Disable self-refresh: set sdr.ctrlgrp.lowpwreq.selfrshreq = 0 */
ldr r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
bic r2, r2, #SELFRSHREQ_MASK
str r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
/* Poll until sdr.ctrlgrp.lowpwrack.selfrfshack == 0 or hit max loops */
mov r3, #0
while_ack_1:
ldr r2, [r0, #SDR_CTRLGRP_LOWPWRACK_ADDR]
and r2, r2, #SELFRFSHACK_MASK
cmp r2, #SELFRFSHACK_MASK
bne ack_0
add r3, #1
cmp r3, #MAX_LOOP_COUNT
bne while_ack_1
ack_0:
/*
* Prepare return value:
* Shift loop count for exiting self refresh into upper 16 bits.
* Leave loop count for requesting self refresh in lower 16 bits.
*/
mov r3, r3, lsl #16
add r1, r1, r3
/* Disable dynamic clock gating in the Power Control Register. */
mrc p15, 0, r2, c15, c0, 0
bic r2, r2, #1
mcr p15, 0, r2, c15, c0, 0
mov r0, r1 @ return value
bx lr @ return
ENDPROC(socfpga_sdram_self_refresh)
ENTRY(socfpga_sdram_self_refresh_sz)
.word . - socfpga_sdram_self_refresh

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2012 Altera Corporation
* Copyright (C) 2012-2015 Altera Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -29,6 +29,7 @@
void __iomem *sys_manager_base_addr;
void __iomem *rst_manager_base_addr;
void __iomem *sdr_ctl_base_addr;
unsigned long socfpga_cpu1start_addr;
void __init socfpga_sysmgr_init(void)
@ -49,6 +50,9 @@ void __init socfpga_sysmgr_init(void)
np = of_find_compatible_node(NULL, NULL, "altr,rst-mgr");
rst_manager_base_addr = of_iomap(np, 0);
np = of_find_compatible_node(NULL, NULL, "altr,sdr-ctl");
sdr_ctl_base_addr = of_iomap(np, 0);
}
static void __init socfpga_init_irq(void)
@ -78,7 +82,6 @@ static const char *altera_dt_match[] = {
DT_MACHINE_START(SOCFPGA, "Altera SOCFPGA")
.l2c_aux_val = 0,
.l2c_aux_mask = ~0,
.smp = smp_ops(socfpga_smp_ops),
.init_irq = socfpga_init_irq,
.restart = socfpga_cyclone5_restart,
.dt_compat = altera_dt_match,