obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o
obj-$(CONFIG_PM_SLEEP) += pm.o rk3288_ddr_suspend.o
obj-$(CONFIG_SMP) += headsmp.o platsmp.o
+
+obj-y += embedded/
--- /dev/null
+# Makefile for embedded code blobs for Rockchip SoCs
+#
+# These code blobs are emedded into vmlinux and copied into SRAM
+# at times when SDRAM is not available. Each blob is self contained.
+#
+# Some blobs may be linked to expect to run at a very specific address.
+# A good example is resume code blobs that always expect to run in a
+# very specific bit of SRAM that keeps power during sleep. This code
+# is also running with the cache off so it can predict the address it
+# will be at.
+#
+# Other blobs may be linked with -fpic (by adding CFLAGS_file.o := -fpic).
+# These can be located anywhere. I believe gcc will support this by
+# assuming that the .text and .data sections are relative to each other.
+#
+# That brings up the point that all blobs here:
+# - Are generally very small
+# - Generally have code and data jammed together in one blob.
+# - Generally have "parameters" at the beginning that are filled in by
+# the kernel.
+
+obj-$(CONFIG_PM_SLEEP) += rk3288_resume.bin.o
+
+targets := rk3288_resume.o rk3288_ddr_resume.o \
+ rk3288_resume.elf rk3288_resume.lds \
+ rk3288_resume.bin rk3288_resume.bin.o
+
+# Reset objcopy flags, ARM puts "-O binary" here.
+OBJCOPYFLAGS :=
+
+# Our embedded code can't handle this flag.
+ifeq ($(CONFIG_FUNCTION_TRACER),y)
+ORIG_CFLAGS := $(KBUILD_CFLAGS)
+KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
+endif
+
+KBUILD_CFLAGS += -fno-stack-protector -fPIC
+
+# This is the ELF for the embedded binary
+LDFLAGS_rk3288_resume.elf := -Bstatic -nostdlib -T
+$(obj)/rk3288_resume.elf: $(obj)/rk3288_resume.lds \
+ $(obj)/rk3288_resume.o \
+ $(obj)/rk3288_ddr_resume.o \
+ FORCE
+ $(call if_changed,ld)
+
+# Create binary data for the kernel
+OBJCOPYFLAGS_rk3288_resume.bin := -O binary
+$(obj)/rk3288_resume.bin: $(obj)/rk3288_resume.elf FORCE
+ $(call if_changed,objcopy)
+
+# Import the data into the kernel
+OBJCOPYFLAGS_rk3288_resume.bin.o += -B $(ARCH) -I binary -O elf32-littlearm
+$(obj)/rk3288_resume.bin.o: $(obj)/rk3288_resume.bin FORCE
+ $(call if_changed,objcopy)
--- /dev/null
+/*
+ * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Chris Zhong <zyw@rock-chips.com>
+ *
+ * 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.
+ */
+
+#ifndef __MACH_ROCKCHIP_RK3288_DDR_H
+#define __MACH_ROCKCHIP_RK3288_DDR_H
+
+/* DDR pctl register */
+#define DDR_PCTL_SCFG 0x0000
+#define DDR_PCTL_SCTL 0x0004
+#define DDR_PCTL_STAT 0x0008
+#define DDR_PCTL_MCMD 0x0040
+#define DDR_PCTL_POWCTL 0x0044
+#define DDR_PCTL_POWSTAT 0x0048
+#define DDR_PCTL_CMDTSTATEN 0x0050
+#define DDR_PCTL_MRRCFG0 0x0060
+#define DDR_PCTL_MRRSTAT0 0x0064
+#define DDR_PCTL_MRRSTAT1 0x0068
+#define DDR_PCTL_MCFG1 0x007c
+#define DDR_PCTL_MCFG 0x0080
+#define DDR_PCTL_PPCFG 0x0084
+#define DDR_PCTL_TOGCNT1U 0x00c0
+#define DDR_PCTL_TINIT 0x00c4
+#define DDR_PCTL_TRSTH 0x00c8
+#define DDR_PCTL_TOGCNT100N 0x00cc
+#define DDR_PCTL_TREFI 0x00d0
+#define DDR_PCTL_TMRD 0x00d4
+#define DDR_PCTL_TRFC 0x00d8
+#define DDR_PCTL_TRP 0x00dc
+#define DDR_PCTL_TRTW 0x00e0
+#define DDR_PCTL_TAL 0x00e4
+#define DDR_PCTL_TCL 0x00e8
+#define DDR_PCTL_TCWL 0x00ec
+#define DDR_PCTL_TRAS 0x00f0
+#define DDR_PCTL_TRC 0x00f4
+#define DDR_PCTL_TRCD 0x00f8
+#define DDR_PCTL_TRRD 0x00fc
+#define DDR_PCTL_TRTP 0x0100
+#define DDR_PCTL_TWR 0x0104
+#define DDR_PCTL_TWTR 0x0108
+#define DDR_PCTL_TEXSR 0x010c
+#define DDR_PCTL_TXP 0x0110
+#define DDR_PCTL_TXPDLL 0x0114
+#define DDR_PCTL_TZQCS 0x0118
+#define DDR_PCTL_TZQCSI 0x011c
+#define DDR_PCTL_TDQS 0x0120
+#define DDR_PCTL_TCKSRE 0x0124
+#define DDR_PCTL_TCKSRX 0x0128
+#define DDR_PCTL_TCKE 0x012c
+#define DDR_PCTL_TMOD 0x0130
+#define DDR_PCTL_TRSTL 0x0134
+#define DDR_PCTL_TZQCL 0x0138
+#define DDR_PCTL_TMRR 0x013c
+#define DDR_PCTL_TCKESR 0x0140
+#define DDR_PCTL_TDPD 0x0144
+#define DDR_PCTL_DFITCTRLDELAY 0x0240
+#define DDR_PCTL_DFIODTCFG 0x0244
+#define DDR_PCTL_DFIODTCFG1 0x0248
+#define DDR_PCTL_DFIODTRANKMAP 0x024c
+#define DDR_PCTL_DFITPHYWRDATA 0x0250
+#define DDR_PCTL_DFITPHYWRLAT 0x0254
+#define DDR_PCTL_DFITRDDATAEN 0x0260
+#define DDR_PCTL_DFITPHYRDLAT 0x0264
+#define DDR_PCTL_DFITPHYUPDTYPE0 0x0270
+#define DDR_PCTL_DFITPHYUPDTYPE1 0x0274
+#define DDR_PCTL_DFITPHYUPDTYPE2 0x0278
+#define DDR_PCTL_DFITPHYUPDTYPE3 0x027c
+#define DDR_PCTL_DFITCTRLUPDMIN 0x0280
+#define DDR_PCTL_DFITCTRLUPDMAX 0x0284
+#define DDR_PCTL_DFITCTRLUPDDLY 0x0288
+#define DDR_PCTL_DFIUPDCFG 0x0290
+#define DDR_PCTL_DFITREFMSKI 0x0294
+#define DDR_PCTL_DFITCTRLUPDI 0x0298
+#define DDR_PCTL_DFISTCFG0 0x02c4
+#define DDR_PCTL_DFISTCFG1 0x02c8
+#define DDR_PCTL_DFITDRAMCLKEN 0x02d0
+#define DDR_PCTL_DFITDRAMCLKDIS 0x02d4
+#define DDR_PCTL_DFISTCFG2 0x02d8
+#define DDR_PCTL_DFILPCFG0 0x02f0
+
+/* DDR phy register */
+#define DDR_PUBL_RIDR 0x0000
+#define DDR_PUBL_PIR 0x0004
+#define DDR_PUBL_PGCR 0x0008
+#define DDR_PUBL_PGSR 0x000c
+#define DDR_PUBL_DLLGCR 0x0010
+#define DDR_PUBL_ACDLLCR 0x0014
+#define DDR_PUBL_PTR0 0x0018
+#define DDR_PUBL_PTR1 0x001c
+#define DDR_PUBL_PTR2 0x0020
+#define DDR_PUBL_ACIOCR 0x0024
+#define DDR_PUBL_DXCCR 0x0028
+#define DDR_PUBL_DSGCR 0x002c
+#define DDR_PUBL_DCR 0x0030
+#define DDR_PUBL_DTPR0 0x0034
+#define DDR_PUBL_DTPR1 0x0038
+#define DDR_PUBL_DTPR2 0x003c
+#define DDR_PUBL_MR0 0x0040
+#define DDR_PUBL_MR1 0x0044
+#define DDR_PUBL_MR2 0x0048
+#define DDR_PUBL_MR3 0x004c
+#define DDR_PUBL_ODTCR 0x0050
+#define DDR_PUBL_DTAR 0x0054
+#define DDR_PUBL_ZQ0CR0 0x0180
+#define DDR_PUBL_ZQ0CR1 0x0184
+#define DDR_PUBL_ZQ1CR0 0x0190
+#define DDR_PUBL_DX0GCR 0x01c0
+#define DDR_PUBL_DX0GSR0 0x01c4
+#define DDR_PUBL_DX0GSR1 0x01c8
+#define DDR_PUBL_DX0DLLCR 0x01cc
+#define DDR_PUBL_DX0DQTR 0x01d0
+#define DDR_PUBL_DX0DQSTR 0x01d4
+#define DDR_PUBL_DX1GCR 0x0200
+#define DDR_PUBL_DX1GSR0 0x0204
+#define DDR_PUBL_DX1GSR1 0x0208
+#define DDR_PUBL_DX1DLLCR 0x020c
+#define DDR_PUBL_DX1DQTR 0x0210
+#define DDR_PUBL_DX1DQSTR 0x0214
+#define DDR_PUBL_DX2GCR 0x0240
+#define DDR_PUBL_DX2GSR0 0x0244
+#define DDR_PUBL_DX2GSR1 0x0248
+#define DDR_PUBL_DX2DLLCR 0x024c
+#define DDR_PUBL_DX2DQTR 0x0250
+#define DDR_PUBL_DX2DQSTR 0x0254
+#define DDR_PUBL_DX3GCR 0x0280
+#define DDR_PUBL_DX3GSR0 0x0284
+#define DDR_PUBL_DX3GSR1 0x0288
+#define DDR_PUBL_DX3DLLCR 0x028c
+#define DDR_PUBL_DX3DQTR 0x0290
+#define DDR_PUBL_DX3DQSTR 0x0294
+
+/* DDR msch register */
+#define DDR_MSCH_DDRCONF 0x0008
+#define DDR_MSCH_DDRTIMING 0x000c
+#define DDR_MSCH_DDRMODE 0x0010
+#define DDR_MSCH_READLATENCY 0x0014
+#define DDR_MSCH_ACTIVATE 0x0038
+#define DDR_MSCH_DEVTODEV 0x003c
+
+#define DLLSRST BIT(30)
+#define POWER_UP_START BIT(0)
+#define POWER_UP_DONE BIT(0)
+#define DDR0IO_RET_DE_REQ BIT(21)
+#define DDR0I1_RET_DE_REQ BIT(22)
+
+#define PCTL_STAT_MSK (7)
+#define LP_TRIG_VAL(n) (((n) >> 4) & 7)
+
+/* SCTL */
+#define INIT_STATE (0)
+#define CFG_STATE (1)
+#define GO_STATE (2)
+#define SLEEP_STATE (3)
+#define WAKEUP_STATE (4)
+
+/* STAT */
+#define LP_TRIG_VAL(n) (((n) >> 4) & 7)
+#define PCTL_STAT_MSK (7)
+#define INIT_MEM (0)
+#define CONFIG (1)
+#define CONFIG_REQ (2)
+#define ACCESS (3)
+#define ACCESS_REQ (4)
+#define LOW_POWER (5)
+#define LOW_POWER_ENTRY_REQ (6)
+#define LOW_POWER_EXIT_REQ (7)
+
+/* PGSR */
+#define PGSR_IDONE (1 << 0)
+#define PGSR_DLDONE (1 << 1)
+#define PGSR_ZCDONE (1 << 2)
+#define PGSR_DIDONE (1 << 3)
+#define PGSR_DTDONE (1 << 4)
+#define PGSR_DTERR (1 << 5)
+#define PGSR_DTIERR (1 << 6)
+#define PGSR_DFTERR (1 << 7)
+#define PGSR_RVERR (1 << 8)
+#define PGSR_RVEIRR (1 << 9)
+
+/* PIR */
+#define PIR_INIT (1 << 0)
+#define PIR_DLLSRST (1 << 1)
+#define PIR_DLLLOCK (1 << 2)
+#define PIR_ZCAL (1 << 3)
+#define PIR_ITMSRST (1 << 4)
+#define PIR_DRAMRST (1 << 5)
+#define PIR_DRAMINIT (1 << 6)
+#define PIR_QSTRN (1 << 7)
+#define PIR_RVTRN (1 << 8)
+#define PIR_ICPC (1 << 16)
+#define PIR_DLLBYP (1 << 17)
+#define PIR_CTLDINIT (1 << 18)
+#define PIR_CLRSR (1 << 28)
+#define PIR_LOCKBYP (1 << 29)
+#define PIR_ZCALBYP (1 << 30)
+#define PIR_INITBYP (1u << 31)
+
+#endif /* __MACH_ROCKCHIP_RK3288_DDR_H */
--- /dev/null
+/*
+ * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Chris Zhong <zyw@rock-chips.com>
+ *
+ * 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.
+ */
+
+#include <linux/bug.h> /* BUILD_BUG_ON */
+#include <linux/kernel.h>
+
+#include <asm/io.h>
+
+#include "rk3288_ddr.h"
+#include "rk3288_resume.h"
+#include "sram_delay.h"
+
+#include "../pm.h"
+
+#define PMU_ADDR 0xff730000
+#define PMU_PWRMODE_CON_ADDR ((void *)(PMU_ADDR + RK3288_PMU_PWRMODE_CON))
+#define DDR_PCTRL0_ADDR ((void *)0xff610000)
+#define DDR_PUBL0_ADDR ((void *)0xff620000) /* phy */
+#define DDR_PCTRL1_ADDR ((void *)0xff630000)
+#define DDR_PUBL1_ADDR ((void *)0xff640000) /* phy */
+
+#define MSCH0_ADDR ((void *)0xffac0000)
+#define MSCH1_ADDR ((void *)0xffac0080)
+
+static void * const pctrl_addrs[] = { DDR_PCTRL0_ADDR, DDR_PCTRL1_ADDR };
+static void * const phy_addrs[] = { DDR_PUBL0_ADDR, DDR_PUBL1_ADDR };
+static void * const msch_addrs[] = { MSCH0_ADDR, MSCH1_ADDR };
+
+static void reset_dll(void __iomem *phy_addr)
+{
+ static const u32 reg[] = { DDR_PUBL_ACDLLCR, DDR_PUBL_DX0DLLCR,
+ DDR_PUBL_DX1DLLCR, DDR_PUBL_DX2DLLCR,
+ DDR_PUBL_DX3DLLCR };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(reg); i++)
+ writel_relaxed(readl_relaxed(phy_addr + reg[i]) & ~DLLSRST,
+ phy_addr + reg[i]);
+
+ sram_udelay(10);
+
+ for (i = 0; i < ARRAY_SIZE(reg); i++)
+ writel_relaxed(readl_relaxed(phy_addr + reg[i]) | DLLSRST,
+ phy_addr + reg[i]);
+
+ sram_udelay(10);
+}
+
+static void phy_init(void __iomem *phy_addr)
+{
+ u32 val;
+
+ val = readl_relaxed(phy_addr + DDR_PUBL_PIR);
+
+ val |= PIR_INIT | PIR_DLLSRST | PIR_DLLLOCK |
+ PIR_ZCAL | PIR_ITMSRST | PIR_CLRSR;
+
+ writel_relaxed(val, phy_addr + DDR_PUBL_PIR);
+
+ sram_udelay(1);
+
+ while ((readl_relaxed(phy_addr + DDR_PUBL_PGSR) &
+ (PGSR_IDONE | PGSR_DLDONE | PGSR_ZCDONE)) !=
+ (PGSR_IDONE | PGSR_DLDONE | PGSR_ZCDONE))
+ ;
+}
+
+static void memory_init(void __iomem *phy_addr)
+{
+ u32 val;
+
+ val = readl_relaxed(phy_addr + DDR_PUBL_PIR);
+
+ val |= PIR_INIT | PIR_DRAMINIT | PIR_LOCKBYP |
+ PIR_ZCALBYP | PIR_CLRSR | PIR_ICPC;
+
+ writel_relaxed(val, phy_addr + DDR_PUBL_PIR);
+
+ sram_udelay(1);
+ while ((readl_relaxed(phy_addr + DDR_PUBL_PGSR) &
+ (PGSR_IDONE | PGSR_DLDONE)) != (PGSR_IDONE | PGSR_DLDONE))
+ ;
+}
+
+static void move_to_lowpower_state(void __iomem *pctrl_addr,
+ void __iomem *phy_addr)
+{
+ u32 state;
+
+ while (1) {
+ state = readl_relaxed(pctrl_addr +
+ DDR_PCTL_STAT) & PCTL_STAT_MSK;
+
+ switch (state) {
+ case INIT_MEM:
+ writel_relaxed(CFG_STATE, pctrl_addr + DDR_PCTL_SCTL);
+ while ((readl_relaxed(pctrl_addr + DDR_PCTL_STAT) &
+ PCTL_STAT_MSK) != CONFIG)
+ ;
+ /* no break */
+ case CONFIG:
+ writel_relaxed(GO_STATE, pctrl_addr + DDR_PCTL_SCTL);
+ while ((readl_relaxed(pctrl_addr + DDR_PCTL_STAT) &
+ PCTL_STAT_MSK) != ACCESS)
+ ;
+ /* no break */
+ case ACCESS:
+ writel_relaxed(SLEEP_STATE, pctrl_addr + DDR_PCTL_SCTL);
+ while ((readl_relaxed(pctrl_addr + DDR_PCTL_STAT) &
+ PCTL_STAT_MSK) != LOW_POWER)
+ ;
+ /* no break */
+ case LOW_POWER:
+ return;
+ default:
+ break;
+ }
+ }
+}
+
+static void move_to_access_state(void __iomem *pctrl_addr,
+ void __iomem *phy_addr)
+{
+ u32 state;
+
+ while (1) {
+ state = readl_relaxed(pctrl_addr +
+ DDR_PCTL_STAT) & PCTL_STAT_MSK;
+
+ switch (state) {
+ case LOW_POWER:
+ if (LP_TRIG_VAL(readl_relaxed(pctrl_addr +
+ DDR_PCTL_STAT)) == 1)
+ return;
+
+ writel_relaxed(WAKEUP_STATE,
+ pctrl_addr + DDR_PCTL_SCTL);
+ while ((readl_relaxed(pctrl_addr + DDR_PCTL_STAT) &
+ PCTL_STAT_MSK) != ACCESS)
+ ;
+
+ /* wait DLL lock */
+ while ((readl_relaxed(phy_addr +
+ DDR_PUBL_PGSR) & PGSR_DLDONE)
+ != PGSR_DLDONE)
+ ;
+
+ break;
+ case INIT_MEM:
+ writel_relaxed(CFG_STATE, pctrl_addr + DDR_PCTL_SCTL);
+ while ((readl_relaxed(pctrl_addr + DDR_PCTL_STAT) &
+ PCTL_STAT_MSK) != CONFIG)
+ ;
+ /* fallthrough here */
+ case CONFIG:
+ writel_relaxed(GO_STATE, pctrl_addr + DDR_PCTL_SCTL);
+ while ((readl_relaxed(pctrl_addr + DDR_PCTL_STAT) &
+ PCTL_STAT_MSK) == CONFIG)
+ ;
+ break;
+ case ACCESS:
+ return;
+ default:
+ break;
+ }
+ }
+}
+
+static void rk3288_ddr_reg_restore(void __iomem *regbase, const u32 reg_list[],
+ int num_reg, const u32 *vals)
+{
+ int i;
+
+ for (i = 0; i < num_reg && reg_list[i] != RK3288_BOGUS_OFFSET; i++)
+ writel_relaxed(vals[i], regbase + reg_list[i]);
+}
+
+void rk3288_ddr_resume_early(const struct rk3288_ddr_save_data *ddr_save_data)
+{
+ int ch;
+
+ /* PWM saves full address, so base is NULL */
+ rk3288_ddr_reg_restore(NULL, ddr_save_data->pwm_addrs,
+ RK3288_MAX_PWM_REGS, ddr_save_data->pwm_vals);
+
+ /*
+ * PWM never runs higher than 1.2V giving a 2000uV/us ramp delay since
+ * we start from 1V. This is a very conservative ramp delay for the
+ * regulator.
+ */
+ sram_udelay(100);
+
+ for (ch = 0; ch < ARRAY_SIZE(pctrl_addrs); ch++) {
+ /* DLL bypass */
+ rk3288_ddr_reg_restore(phy_addrs[ch],
+ ddr_save_data->phy_dll_offsets,
+ RK3288_MAX_DDR_PHY_DLL_REGS,
+ ddr_save_data->phy_dll_vals[ch]);
+
+ reset_dll(phy_addrs[ch]);
+
+ /* ddr ctrl restore; NOTE: both channels must be the same */
+ rk3288_ddr_reg_restore(pctrl_addrs[ch],
+ ddr_save_data->ctrl_offsets,
+ RK3288_MAX_DDR_CTRL_REGS,
+ ddr_save_data->ctrl_vals);
+
+ /* ddr phy restore */
+ rk3288_ddr_reg_restore(phy_addrs[ch],
+ ddr_save_data->phy_offsets,
+ RK3288_MAX_DDR_PHY_REGS,
+ ddr_save_data->phy_vals[ch]);
+
+ /* msch restore */
+ rk3288_ddr_reg_restore(msch_addrs[ch],
+ ddr_save_data->msch_offsets,
+ RK3288_MAX_DDR_MSCH_REGS,
+ ddr_save_data->msch_vals[ch]);
+
+ phy_init(phy_addrs[ch]);
+
+ /* power up ddr power */
+ writel_relaxed(POWER_UP_START,
+ pctrl_addrs[ch] + DDR_PCTL_POWCTL);
+ while (!(readl_relaxed(pctrl_addrs[ch] + DDR_PCTL_POWSTAT) &
+ POWER_UP_DONE))
+ ;
+
+ /* zqcr restore; NOTE: both channels must be the same */
+ rk3288_ddr_reg_restore(phy_addrs[ch],
+ ddr_save_data->phy_zqcr_offsets,
+ RK3288_MAX_DDR_PHY_ZQCR_REGS,
+ ddr_save_data->phy_zqcr_vals);
+
+ memory_init(phy_addrs[ch]);
+
+ move_to_lowpower_state(pctrl_addrs[ch], phy_addrs[ch]);
+ }
+
+ /* disable retention */
+ writel_relaxed(readl_relaxed(PMU_PWRMODE_CON_ADDR) |
+ DDR0IO_RET_DE_REQ | DDR0I1_RET_DE_REQ,
+ PMU_PWRMODE_CON_ADDR);
+
+ sram_udelay(1);
+
+ /* disable self-refresh */
+ for (ch = 0; ch < ARRAY_SIZE(pctrl_addrs); ch++)
+ move_to_access_state(pctrl_addrs[ch], phy_addrs[ch]);
+}
--- /dev/null
+/*
+ * Rockchip rk3288 resume code
+ *
+ * This code is intended to be linked into the embedded resume binary
+ * for the rk3288 SoC
+ *
+ * Copyright (c) 2014 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+
+#include "rk3288_resume.h"
+#include "rk3288_resume_embedded.h"
+
+#define INIT_CPSR (PSR_I_BIT | PSR_F_BIT | SVC_MODE)
+
+static __noreturn void rk3288_resume(void);
+
+/* Parameters of early board initialization in SPL */
+struct rk3288_resume_params rk3288_resume_params
+ __attribute__((section(".resume_params"))) = {
+ .resume_loc = rk3288_resume,
+};
+
+/**
+ * rk3288_resume_c - Main C entry point for rk3288 at resume time
+ *
+ * This function is called by rk3288_resume() and does the brunt of
+ * any resume processing. After it's done it will call the kernel's
+ * cpu_resume() function (which it finds through its params structure).
+ *
+ * At the time this function is called:
+ * - We know we're on CPU0.
+ * - Interrupts are disabled.
+ * - We've got a stack.
+ * - The cache is turned off, so all addresses are physical.
+ * - SDRAM hasn't been restored yet (if it was off).
+ *
+ * WARNING: This code, the stack and the params structure are all sitting in
+ * PMU SRAM. If you try to write to that memory using an 8-bit access (or even
+ * 16-bit) you'll get an imprecise data abort and it will be very hard to debug.
+ * Keep everything in here as 32-bit wide and aligned. YOU'VE BEEN WARNED.
+ */
+static void __noreturn rk3288_resume_c(void)
+{
+#ifdef CONFIG_ARM_ERRATA_818325
+ u32 val = 0;
+
+ asm("mrc p15, 0, %0, c15, c0, 1" : "=r" (val));
+ val |= BIT(12);
+ asm("mcr p15, 0, %0, c15, c0, 1" : : "r" (val));
+#endif
+
+ if (rk3288_resume_params.l2ctlr_f)
+ asm("mcr p15, 1, %0, c9, c0, 2" : :
+ "r" (rk3288_resume_params.l2ctlr));
+
+ if (rk3288_resume_params.ddr_resume_f)
+ rk3288_ddr_resume_early(&rk3288_resume_params.ddr_save_data);
+
+ rk3288_resume_params.cpu_resume();
+}
+
+/**
+ * rk3288_resume - First entry point for rk3288 at resume time
+ *
+ * A pointer to this function is stored in rk3288_resume_params. The
+ * kernel uses the pointer in that structure to find this function and
+ * to put its (physical) address in a location that it will get jumped
+ * to at resume time.
+ *
+ * There is no stack at the time this function is called, so this
+ * function is in charge of setting it up. We get to a function with
+ * a normal stack pointer ASAP.
+ */
+static void __naked __noreturn rk3288_resume(void)
+{
+ /* Make sure we're on CPU0, no IRQs and get a stack setup */
+ asm volatile (
+ "msr cpsr_cxf, %0\n"
+
+ /* Only cpu0 continues to run, the others halt here */
+ "mrc p15, 0, r1, c0, c0, 5\n"
+ "and r1, r1, #0xf\n"
+ "cmp r1, #0\n"
+ "beq cpu0run\n"
+ "secondary_loop:\n"
+ "wfe\n"
+ "b secondary_loop\n"
+
+ "cpu0run:\n"
+ "mov sp, %1\n"
+ :
+ : "i" (INIT_CPSR), "r" (&__stack_start)
+ : "cc", "r1", "sp");
+
+ /* Now get into a normal function that can use a stack */
+ rk3288_resume_c();
+}
--- /dev/null
+/*
+ * Rockchip resume header (API from kernel to embedded code)
+ *
+ * Copyright (c) 2014 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MACH_ROCKCHIP_RK3288_RESUME_H
+#define __MACH_ROCKCHIP_RK3288_RESUME_H
+
+#define RK3288_NUM_DDR_PORTS 2
+
+#define RK3288_MAX_PWM_REGS 3
+#define RK3288_MAX_DDR_PHY_DLL_REGS 7
+#define RK3288_MAX_DDR_CTRL_REGS 64
+#define RK3288_MAX_DDR_PHY_REGS 29
+#define RK3288_MAX_DDR_MSCH_REGS 6
+#define RK3288_MAX_DDR_PHY_ZQCR_REGS 2
+
+#define RK3288_BOGUS_OFFSET 0xffffffff
+
+/**
+ * rk3288_ddr_save - Parameters needed to reinit SDRAM after suspend
+ *
+ * This structure contains data needed to restore SDRAM after suspend.
+ * Generally:
+ * - There are two controllers and we need to save data for both. We save
+ * the same registers for both, so you see two sets of values and one sets
+ * of offsets (the register offset from the base of the controller).
+ * There are a few registers that are always the same for both controllers
+ * so we only save one set of values.
+ *
+ * Offsets are saved at init time and vals are saved on each suspend.
+ *
+ * NOTE: offsets are u32 values right now to keep everything 32-bit and avoid
+ * 8-bit and 16-bit access problems in PMU SRAM (see WARNING below).
+ * Technically, though, 8-bit and 16-bit _reads_ seem to work, so as long as
+ * we were careful in setting things up we could possibly save some memory by
+ * storing 16-bit offsets. We can investigate if we ever get that tight on
+ * space.
+ */
+struct rk3288_ddr_save_data {
+ u32 pwm_addrs[RK3288_MAX_PWM_REGS];
+ u32 pwm_vals[RK3288_MAX_PWM_REGS];
+
+ u32 phy_dll_offsets[RK3288_MAX_DDR_PHY_DLL_REGS];
+ u32 phy_dll_vals[RK3288_NUM_DDR_PORTS][RK3288_MAX_DDR_PHY_DLL_REGS];
+
+ u32 ctrl_offsets[RK3288_MAX_DDR_CTRL_REGS];
+ u32 ctrl_vals[RK3288_MAX_DDR_CTRL_REGS]; /* Both same */
+
+ u32 phy_offsets[RK3288_MAX_DDR_PHY_REGS];
+ u32 phy_vals[RK3288_NUM_DDR_PORTS][RK3288_MAX_DDR_PHY_REGS];
+
+ u32 msch_offsets[RK3288_MAX_DDR_MSCH_REGS];
+ u32 msch_vals[RK3288_NUM_DDR_PORTS][RK3288_MAX_DDR_MSCH_REGS];
+
+ u32 phy_zqcr_offsets[RK3288_MAX_DDR_PHY_ZQCR_REGS];
+ u32 phy_zqcr_vals[RK3288_MAX_DDR_PHY_ZQCR_REGS]; /* Both same */
+};
+
+/**
+ * rk3288_resume_params - Parameter space for the resume code
+ *
+ * This structure is at the start of the resume blob and is used to communicate
+ * between the resume blob and the callers.
+ *
+ * WARNING: This structure is sitting in PMU SRAM. If you try to write to that
+ * memory using an 8-bit access (or even 16-bit) you'll get an imprecise data
+ * abort and it will be very hard to debug. Keep everything in here as 32-bit
+ * wide and aligned. YOU'VE BEEN WARNED.
+ *
+ * @resume_loc: The value here should be the resume address that the CPU
+ * is programmed to go to at resume time.
+ *
+ * @l2ctlr_f: If non-zero we'll set l2ctlr at resume time.
+ * @l2ctlr: The value to set l2ctlr to at resume time.
+ *
+ * @ddr_resume_f True if we should resume DDR.
+ * @ddr_save_data: Data for save / restore of DDR.
+ *
+ * @cpu_resume: The function to jump to when we're all done.
+ */
+struct rk3288_resume_params {
+ /* This is compiled in and can be read to find the resume location */
+ __noreturn void (*resume_loc)(void);
+
+ /* Filled in by the client of the resume code */
+ u32 l2ctlr_f; /* u32 not bool to avoid 8-bit SRAM access */
+ u32 l2ctlr;
+
+ u32 ddr_resume_f; /* u32 not bool to avoid 8-bit SRAM access */
+ struct rk3288_ddr_save_data ddr_save_data;
+
+ __noreturn void (*cpu_resume)(void);
+};
+
+#endif /* __MACH_ROCKCHIP_RK3288_RESUME_H */
--- /dev/null
+MEMORY {
+ pmu_sram_code : ORIGIN = 0xff720000, LENGTH = 0xf00
+ pmu_sram_stack : ORIGIN = 0xff720f00, LENGTH = 0x100
+}
+
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+
+SECTIONS
+{
+ /* Don't need unwind tables */
+ /DISCARD/ : {
+ *(.ARM.exidx*)
+ *(.ARM.extab*)
+ }
+
+ /* Kernel code finds params because it knows they are first */
+ .params : { *(.resume_params*) } > pmu_sram_code
+ . = ALIGN(4);
+
+ .text : { *(.text*) } > pmu_sram_code
+ . = ALIGN(4);
+
+ .rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } > pmu_sram_code
+ . = ALIGN(4);
+
+ .data : {
+ *(SORT_BY_ALIGNMENT(.data*))
+ . = ALIGN(4);
+
+ /* We purposely put bss as part of data to avoid initting */
+ *(SORT_BY_ALIGNMENT(.bss*))
+ . = ALIGN(4);
+ } > pmu_sram_code
+
+ .stack : {
+ . += LENGTH(pmu_sram_stack) - 8;
+ __stack_start = .;
+ } > pmu_sram_stack
+}
--- /dev/null
+/*
+ * Rockchip resume header (API between files in embedded code)
+ *
+ * Copyright (c) 2014 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MACH_ROCKCHIP_RK3288_RESUME_EMBEDDED_H
+#define __MACH_ROCKCHIP_RK3288_RESUME_EMBEDDED_H
+
+/* Defined in the linker script */
+extern u32 *__stack_start;
+
+/* Defined by SDRAM code */
+void rk3288_ddr_resume_early(struct rk3288_ddr_save_data *ddr_save_data);
+
+#endif /* __MACH_ROCKCHIP_RK3288_RESUME_EMBEDDED_H */
--- /dev/null
+/*
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ */
+
+#ifndef __MACH_ROCKCHIP_SRAM_DELAY_H
+#define __MACH_ROCKCHIP_SRAM_DELAY_H
+
+#include <asm/arch_timer.h>
+
+/*
+ * Arch timer freq is present in dts. We could get it from there but
+ * really nobody is going to put anything but 24MHz here. If they do then
+ * we'll have to add this to the suspend data.
+ */
+#define ARCH_TIMER_FREQ 24000000
+#define ARCH_TIMER_TICKS_PER_US (ARCH_TIMER_FREQ / 1000000)
+
+static inline void sram_udelay(u32 us)
+{
+ u32 orig;
+ u32 to_wait = ARCH_TIMER_TICKS_PER_US * us;
+
+ /* Note: u32 math is way more than enough for our small delays */
+ orig = (u32) arch_counter_get_cntpct();
+ while ((u32) arch_counter_get_cntpct() - orig <= to_wait)
+ ;
+}
+
+#endif /* MACH_ROCKCHIP_SRAM_DELAY_H */
+++ /dev/null
-/*
- * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
- * Author: Tony Xie <tony.xie@rock-chips.com>
- *
- * 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.
- *
- */
-
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-#include <asm/memory.h>
-
-.data
-/*
- * this code will be copied from
- * ddr to sram for system resumeing.
- * so it is ".data section".
- */
-.align
-
-ENTRY(rockchip_slp_cpu_resume)
- setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r1 @ set svc, irqs off
- mrc p15, 0, r1, c0, c0, 5
- and r1, r1, #0xf
- cmp r1, #0
- /* olny cpu0 can continue to run, the others is halt here */
- beq cpu0run
-secondary_loop:
- wfe
- b secondary_loop
-cpu0run:
- ldr r3, rkpm_bootdata_l2ctlr_f
- cmp r3, #0
- beq sp_set
- ldr r3, rkpm_bootdata_l2ctlr
- mcr p15, 1, r3, c9, c0, 2
-sp_set:
- ldr sp, rkpm_bootdata_cpusp
- ldr r1, rkpm_bootdata_cpu_code
- bx r1
-ENDPROC(rockchip_slp_cpu_resume)
-
-/* Parameters filled in by the kernel */
-
-/* Flag for whether to restore L2CTLR on resume */
- .global rkpm_bootdata_l2ctlr_f
-rkpm_bootdata_l2ctlr_f:
- .long 0
-
-/* Saved L2CTLR to restore on resume */
- .global rkpm_bootdata_l2ctlr
-rkpm_bootdata_l2ctlr:
- .long 0
-
-/* CPU resume SP addr */
- .globl rkpm_bootdata_cpusp
-rkpm_bootdata_cpusp:
- .long 0
-
-/* CPU resume function (physical address) */
- .globl rkpm_bootdata_cpu_code
-rkpm_bootdata_cpu_code:
- .long 0
-
-ENTRY(rk3288_bootram_sz)
- .word . - rockchip_slp_cpu_resume