diff options
author | Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com> | 2017-05-22 00:11:15 +0900 |
---|---|---|
committer | Jan-Simon Moeller <jsmoeller@linuxfoundation.org> | 2017-05-25 13:47:36 +0000 |
commit | 3bc8101a0775d37da28672b2dbdfe806c961a52c (patch) | |
tree | d5a7793037e00c8c78409c654dc9cc8ab804a94e | |
parent | a14e289caaae4c342c2bc686bd5d327ed612b0fc (diff) |
Add u-boot Hibernation code for porter board.
This patch set is a support to Hibernation for a porter board.
I've commit with Hibernation Off patch, because it depends strongly on user land.
If you can use Hibernation, Please add local.conf agl-porter-hibernate.
OVERRIDES .= ":agl-porter-hibernate"
DISTRO_FEATURES_append = " agl-porter-hibernate"
Change-Id: I3f0560074b710c27f49a73ca871038246d222b73
Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
Reviewed-on: https://gerrit.automotivelinux.org/gerrit/9449
Tested-by: Jenkins Job builder account <agl-jobbuilder@automotivelinux.org>
ci-image-build: Jenkins Job builder account <agl-jobbuilder@automotivelinux.org>
ci-image-boot-test: Jenkins Job builder account <agl-jobbuilder@automotivelinux.org>
Reviewed-by: Jan-Simon Moeller <jsmoeller@linuxfoundation.org>
5 files changed, 3976 insertions, 0 deletions
diff --git a/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot/hibernation/0001-Add-rcar-sdhi-DMA-support.patch b/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot/hibernation/0001-Add-rcar-sdhi-DMA-support.patch new file mode 100755 index 000000000..c5226d4a2 --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot/hibernation/0001-Add-rcar-sdhi-DMA-support.patch @@ -0,0 +1,650 @@ +From 0aae8f3fefc67bc07b7e4e42f885ef661f0921ab Mon Sep 17 00:00:00 2001 +From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com> +Date: Fri, 19 May 2017 14:25:38 +0900 +Subject: [PATCH 1/4] Add rcar-sdhi DMA support + +Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com> +--- + drivers/dma/Makefile | 1 + + drivers/dma/sh_dma.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++ + drivers/mmc/sh_sdhi.c | 158 +++++++++++++++++++++++++- + drivers/mmc/sh_sdhi.h | 5 + + include/sh_dma.h | 58 ++++++++++ + 5 files changed, 524 insertions(+), 4 deletions(-) + create mode 100644 drivers/dma/sh_dma.c + create mode 100644 include/sh_dma.h + +diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile +index 5d864b5..1129fc3 100644 +--- a/drivers/dma/Makefile ++++ b/drivers/dma/Makefile +@@ -29,6 +29,7 @@ COBJS-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o + COBJS-$(CONFIG_APBH_DMA) += apbh_dma.o + COBJS-$(CONFIG_FSL_DMA) += fsl_dma.o + COBJS-$(CONFIG_OMAP3_DMA) += omap3_dma.o ++COBJS-$(CONFIG_SH_DMA) += sh_dma.o + + COBJS := $(COBJS-y) + SRCS := $(COBJS:.o=.c) +diff --git a/drivers/dma/sh_dma.c b/drivers/dma/sh_dma.c +new file mode 100644 +index 0000000..0af2480 +--- /dev/null ++++ b/drivers/dma/sh_dma.c +@@ -0,0 +1,306 @@ ++/* ++ * SH SYS-DMA driver ++ * ++ * Copyright (C) 2014 Cogent Embedded, Inc. ++ * ++ * 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 the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ * 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. ++ */ ++ ++#include <common.h> ++#include <malloc.h> ++#include <asm/io.h> ++#include <linux/list.h> ++#include <sh_dma.h> ++ ++struct sh_dma { ++ u32 base; ++ u32 mask; ++ u32 nch; ++ struct list_head list; ++}; ++ ++struct sh_dma_chan { ++ struct sh_dma *dma; ++ u32 base; ++ u32 num; ++ u32 ts; ++ u32 bs; ++ u32 rounds; ++}; ++ ++#define SH_DMA_MAX_TC 0x1000000 ++#define SH_DMA_MAX_CHAN 32 ++#define SH_DMA_CHAN_OFFSET 0x8000 ++#define SH_DMA_CHAN_SIZE 0x80 ++ ++/* Global registers */ ++#define SH_DMAISTA 0x20 ++#define SH_DMASEC 0x30 ++#define SH_DMAOR 0x60 ++#define SH_DMACHCL 0x80 ++#define SH_DMADPSEC 0xA0 ++ ++/* DMA operation register bits */ ++#define SH_DMAOR_DME (0x1 << 0) ++ ++/* Channel registers */ ++#define SH_DMASAR 0x00 ++#define SH_DMADAR 0x04 ++#define SH_DMATCR 0x08 ++#define SH_DMACHCR 0x0C ++#define SH_DMATSR 0x28 ++#define SH_DMATCRB 0x18 ++#define SH_DMATSRB 0x38 ++#define SH_DMACHCRB 0x1C ++#define SH_DMARS 0x40 ++#define SH_DMABUFCR 0x48 ++#define SH_DMADPBASE 0x50 ++#define SH_DMADPCR 0x54 ++#define SH_DMAFIXSAR 0x10 ++#define SH_DMAFIXDAR 0x14 ++#define SH_DMAFIXDPBASE 0x60 ++ ++/* Channel control register bits */ ++#define SH_DMACHCR_SM(v) (((v) & 0x3) << 12) ++#define SH_DMACHCR_SM_MASK (0x3 << 12) ++#define SH_DMACHCR_DM(v) (((v) & 0x3) << 14) ++#define SH_DMACHCR_DM_MASK (0x3 << 14) ++#define SH_DMACHCR_TS_1 (0x0 << 3 | 0x0 << 20) ++#define SH_DMACHCR_TS_2 (0x1 << 3 | 0x0 << 20) ++#define SH_DMACHCR_TS_4 (0x2 << 3 | 0x0 << 20) ++#define SH_DMACHCR_TS_8 (0x3 << 3 | 0x1 << 20) ++#define SH_DMACHCR_TS_16 (0x3 << 3 | 0x0 << 20) ++#define SH_DMACHCR_TS_32 (0x0 << 3 | 0x1 << 20) ++#define SH_DMACHCR_TS_64 (0x1 << 3 | 0x1 << 20) ++#define SH_DMACHCR_TS_MASK (0x3 << 3 | 0x3 << 20) ++#define SH_DMACHCR_RS_AUTO (0x4 << 8) ++#define SH_DMACHCR_RS_SEL (0x8 << 8) ++#define SH_DMACHCR_RS_MASK (0xf << 8) ++#define SH_DMACHCR_CAE (0x1 << 31) ++#define SH_DMACHCR_TE (0x1 << 1) ++#define SH_DMACHCR_DE (0x1 << 0) ++ ++#define sh_dma_writel(d, r, v) writel((v), (d)->base + (r)) ++#define sh_dma_readl(d, r) readl((d)->base + (r)) ++#define sh_dma_writew(d, r, v) writew((v), (d)->base + (r)) ++#define sh_dma_readw(d, r) readw((d)->base + (r)) ++ ++static LIST_HEAD(sh_dma_list); ++ ++struct sh_dma *sh_dma_add(u32 base, u32 nch) ++{ ++ struct list_head *entry; ++ struct sh_dma *dma; ++ u32 mask; ++ ++ if (nch > SH_DMA_MAX_CHAN) ++ return NULL; ++ ++ mask = (1 << nch) - 1; ++ list_for_each(entry, &sh_dma_list) { ++ dma = list_entry(entry, struct sh_dma, list); ++ if (dma->base == base) { ++ if (nch > dma->nch) { ++ mask &= ~((1 << dma->nch) - 1); ++ sh_dma_writel(dma, SH_DMACHCL, mask); ++ dma->nch = nch; ++ } ++ return dma; ++ } ++ } ++ ++ dma = malloc(sizeof(*dma)); ++ if (!dma) ++ return NULL; ++ ++ dma->base = base; ++ dma->mask = 0; ++ dma->nch = nch; ++ sh_dma_writel(dma, SH_DMACHCL, mask); ++ sh_dma_writew(dma, SH_DMAOR, SH_DMAOR_DME); ++ list_add(&dma->list, &sh_dma_list); ++ return dma; ++} ++ ++void sh_dma_chan_src(struct sh_dma_chan *chan, u32 src) ++{ ++ sh_dma_writel(chan, SH_DMASAR, src); ++} ++ ++void sh_dma_chan_dst(struct sh_dma_chan *chan, u32 dst) ++{ ++ sh_dma_writel(chan, SH_DMADAR, dst); ++} ++ ++void sh_dma_chan_cfg(struct sh_dma_chan *chan, u8 midrid, u8 sm, u8 dm) ++{ ++ u32 val; ++ ++ sh_dma_writew(chan, SH_DMARS, midrid); ++ val = sh_dma_readl(chan, SH_DMACHCR); ++ val &= ~(SH_DMACHCR_RS_MASK | ++ SH_DMACHCR_SM_MASK | SH_DMACHCR_DM_MASK); ++ val |= midrid ? SH_DMACHCR_RS_SEL : SH_DMACHCR_RS_AUTO; ++ val |= SH_DMACHCR_SM(sm) | SH_DMACHCR_DM(dm); ++ sh_dma_writel(chan, SH_DMACHCR, val); ++} ++ ++void sh_dma_chan_start(struct sh_dma_chan *chan, u32 ts, u8 bs) ++{ ++ u32 val; ++ ++ if (!ts) ++ return; ++ ++ val = (ts + (1 << bs) - 1) >> bs; ++ val = val < SH_DMA_MAX_TC ? val : 0x0; ++ sh_dma_writel(chan, SH_DMATCR, val); ++ ++ chan->ts = ts; ++ chan->bs = bs; ++ chan->rounds = val; ++ ++ val = sh_dma_readl(chan, SH_DMACHCR); ++ ++ val &= ~(SH_DMACHCR_TE | SH_DMACHCR_TS_MASK); ++ val |= SH_DMACHCR_DE; ++ switch (bs) { ++ default: ++ case 0: ++ val |= SH_DMACHCR_TS_1; ++ break; ++ case 1: ++ val |= SH_DMACHCR_TS_2; ++ break; ++ case 2: ++ val |= SH_DMACHCR_TS_4; ++ break; ++ case 3: ++ val |= SH_DMACHCR_TS_8; ++ break; ++ case 4: ++ val |= SH_DMACHCR_TS_16; ++ break; ++ case 5: ++ val |= SH_DMACHCR_TS_32; ++ break; ++ case 6: ++ val |= SH_DMACHCR_TS_64; ++ break; ++ } ++ ++ sh_dma_writel(chan, SH_DMACHCR, val); ++} ++ ++void sh_dma_chan_stop(struct sh_dma_chan *chan) ++{ ++ u32 val; ++ ++ chan->ts = 0; ++ chan->bs = 0; ++ chan->rounds = 0; ++ ++ val = sh_dma_readl(chan, SH_DMACHCR); ++ val &= ~(SH_DMACHCR_CAE | SH_DMACHCR_TE | SH_DMACHCR_DE); ++ sh_dma_writel(chan, SH_DMACHCR, val); ++ do { ++ val = sh_dma_readl(chan, SH_DMACHCR); ++ } while (val & SH_DMACHCR_DE); ++} ++ ++int sh_dma_chan_wait(struct sh_dma_chan *chan) ++{ ++ u32 val; ++ u32 timeout = 10000000; ++ int retval = 0; ++ ++ do { ++ val = sh_dma_readl(chan, SH_DMACHCR); ++ val &= SH_DMACHCR_CAE | SH_DMACHCR_TE | SH_DMACHCR_DE; ++ if (val == (SH_DMACHCR_TE | SH_DMACHCR_DE)) ++ break; ++ ++ if (!timeout) ++ return -ETIMEDOUT; ++ ++ timeout--; ++ udelay(1); ++ } while (1); ++ ++ if (!(val & SH_DMACHCR_DE)) ++ return chan->ts ? -EINTR : 0; ++ ++ if (val & SH_DMACHCR_CAE) { ++ retval = -EFAULT; ++ goto out; ++ } ++ ++ val = chan->rounds < SH_DMA_MAX_TC ? chan->rounds : SH_DMA_MAX_TC; ++ val = chan->rounds - val; ++ if (val) { ++ puts("abnormal end\n"); ++ sh_dma_chan_start(chan, val << chan->bs, 0); ++ return -EAGAIN; ++ } ++ ++out: ++ sh_dma_chan_stop(chan); ++ return retval; ++} ++ ++void sh_dma_chan_clr(struct sh_dma_chan *chan) ++{ ++ chan->ts = 0; ++ chan->bs = 0; ++ chan->rounds = 0; ++ ++ sh_dma_writel(chan->dma, SH_DMACHCL, 1 << chan->num); ++} ++ ++struct sh_dma_chan *sh_dma_chan_init(struct sh_dma *dma, int ch) ++{ ++ struct sh_dma_chan *chan; ++ u32 mask; ++ ++ if (ch < 0) { ++ if (!~dma->mask) ++ return NULL; ++ ++ ch = ffz(dma->mask); ++ } ++ ++ if (!dma || ch > dma->nch) ++ return NULL; ++ ++ mask = 1 << ch; ++ if (dma->mask & mask) ++ return NULL; ++ ++ chan = malloc(sizeof(*chan)); ++ if (!chan) ++ return NULL; ++ ++ dma->mask |= mask; ++ chan->dma = dma; ++ chan->base = dma->base + SH_DMA_CHAN_OFFSET + ch * SH_DMA_CHAN_SIZE; ++ chan->num = ch; ++ sh_dma_chan_clr(chan); ++ ++ return chan; ++} ++ ++void sh_dma_chan_release(struct sh_dma_chan *chan) ++{ ++ struct sh_dma *dma = chan->dma; ++ ++ dma->mask &= ~(1 << chan->num); ++ free(chan); ++} +diff --git a/drivers/mmc/sh_sdhi.c b/drivers/mmc/sh_sdhi.c +index ddad43a..80dc7a8 100644 +--- a/drivers/mmc/sh_sdhi.c ++++ b/drivers/mmc/sh_sdhi.c +@@ -17,7 +17,6 @@ + #include <command.h> + #include <mmc.h> + #include <malloc.h> +-#include <mmc.h> + #include <asm/errno.h> + #include <asm/io.h> + +@@ -33,6 +32,111 @@ + + #define DRIVER_NAME "sh-sdhi" + ++#ifdef CONFIG_SH_DMA ++ ++#ifdef CONFIG_SYS_DCACHE_OFF ++static inline void sh_sdhi_invalidate_dcache(u32 addr, int len) { } ++#else /* CONFIG_SYS_DCACHE_OFF */ ++#define DCACHE_LINE_MASK (ARCH_DMA_MINALIGN - 1) ++ ++static void sh_sdhi_invalidate_dcache(u32 addr, int len) ++{ ++ u32 start, end; ++ ++ start = addr & ~DCACHE_LINE_MASK; ++ if (start != addr) { ++ end = start + ARCH_DMA_MINALIGN; ++ flush_dcache_range(start, end); ++ ++ len -= end - addr; ++ start = end; ++ } ++ ++ if (len >= ARCH_DMA_MINALIGN) { ++ end = (start + len) & ~DCACHE_LINE_MASK; ++ invalidate_dcache_range(start, end); ++ ++ len &= DCACHE_LINE_MASK; ++ start = end; ++ } ++ ++ if (len > 0) { ++ end = start + ARCH_DMA_MINALIGN; ++ flush_dcache_range(start, end); ++ } ++} ++#endif /* CONFIG_SYS_DCACHE_OFF */ ++ ++static void sh_sdhi_dma_init(struct sdhi_host *host) ++{ ++ struct sh_dma *dma; ++ ++ dma = sh_dma_add(CONFIG_SH_SYS_DMAL_BASE, CONFIG_SH_SYS_DMAL_NCH); ++ if (!dma) ++ return; ++ ++ host->dma_rx = sh_dma_chan_init(dma, 1); ++ if (!host->dma_rx) ++ return; ++ ++ sh_dma_chan_cfg(host->dma_rx, SH_DMA_SDHI0_RX, ++ SH_DMA_AM_FIX, SH_DMA_AM_INC); ++ sh_dma_chan_src(host->dma_rx, ++ host->addr + (SDHI_BUF0 << host->bus_shift) + ++ 0x2000); ++} ++ ++static void sh_sdhi_dma_release(struct sdhi_host *host) ++{ ++ if (host->dma_rx) { ++ sh_dma_chan_release(host->dma_rx); ++ host->dma_rx = NULL; ++ } ++} ++ ++static void sh_sdhi_start_dma_rx(struct sdhi_host *host, ++ struct mmc_data *data) ++{ ++ int ret; ++ u32 blocksize = data->blocksize; ++ sh_sdhi_dma_init(host); ++ sdhi_writew(host, SDHI_SD_DMACR, 0xa0); ++ sdhi_writew(host, SDHI_CC_EXT_MODE, (1 << 1)); ++ ++ sh_sdhi_invalidate_dcache((u32)data->dest, blocksize); ++ ++ sh_dma_chan_dst(host->dma_rx, (u32)data->dest); ++ ++ /* sh_sdhi_bitset(BUF_ACC_DMAREN, &host->regs->ce_buf_acc); */ ++ ++ /* MMCIF is capable to transfer only 4 bytes at a time, ++ * provide size order as a param */ ++ blocksize = sdhi_readw(host, SDHI_SIZE); ++ sh_dma_chan_start(host->dma_rx, blocksize, 1); ++ ++ do { ++ ret = sh_dma_chan_wait(host->dma_rx); ++ } while (ret == -EAGAIN); ++ sdhi_writew(host, SDHI_CC_EXT_MODE, 0x0); ++ sh_dma_chan_clr(host->dma_rx); ++ sh_sdhi_dma_release(host); ++} ++ ++static void sdhi_dma_transfer(struct sdhi_host *host, ++ struct mmc_data *data) ++{ ++ sh_sdhi_start_dma_rx(host, data); ++} ++ ++ ++#else /* CONFIG_SH_DMA */ ++static inline void sh_sdhi_dma_init(struct sdhi_host *host) { } ++static inline void sh_sdhi_dma_release(struct sdhi_host *host) { } ++static inline void sh_sdhi_start_dma_rx(struct sdhi_host *host, ++ struct mmc_data *data) { } ++ ++#endif /* CONFIG_SH_DMA */ ++ + static void *mmc_priv(struct mmc *mmc) + { + return (void *)mmc->priv; +@@ -253,7 +357,9 @@ static int sdhi_single_read(struct sdhi_host *host, struct mmc_data *data) + { + int ch = host->ch; + long time; ++#ifndef CONFIG_SH_DMA + unsigned short blocksize, i; ++#endif + unsigned short *p = (unsigned short *)data->dest; + + if ((unsigned long)p & 0x00000001) { +@@ -272,10 +378,14 @@ static int sdhi_single_read(struct sdhi_host *host, struct mmc_data *data) + return sdhi_error_manage(host); + + g_wait_int[ch] = 0; ++#ifdef CONFIG_SH_DMA ++ sdhi_dma_transfer(host, data); ++#else + blocksize = sdhi_readw(host, SDHI_SIZE); + for (i = 0; i < blocksize / 2; i++) + *p++ = sdhi_readw(host, SDHI_BUF0); + ++#endif + time = sdhi_wait_interrupt_flag(host); + if (time == 0 || g_sd_error[ch] != 0) + return sdhi_error_manage(host); +@@ -537,7 +647,6 @@ static int sdhi_start_cmd(struct sdhi_host *host, + ; + + sdhi_writew(host, SDHI_CMD, (unsigned short)(opc & CMD_MASK)); +- + g_wait_int[host->ch] = 0; + sdhi_writew(host, SDHI_INFO1_MASK, + ~INFO1M_RESP_END & sdhi_readw(host, SDHI_INFO1_MASK)); +@@ -546,7 +655,6 @@ static int sdhi_start_cmd(struct sdhi_host *host, + INFO2M_END_ERROR | INFO2M_TIMEOUT | + INFO2M_RESP_TIMEOUT | INFO2M_ILA) & + sdhi_readw(host, SDHI_INFO2_MASK)); +- + time = sdhi_wait_interrupt_flag(host); + if (time == 0) + return sdhi_error_manage(host); +@@ -578,7 +686,6 @@ static int sdhi_start_cmd(struct sdhi_host *host, + } + if (host->data) + ret = sdhi_data_trans(host, data, opc); +- + pr_debug("ret = %d, resp = %08x, %08x, %08x, %08x\n", + ret, cmd->response[0], cmd->response[1], + cmd->response[2], cmd->response[3]); +@@ -697,3 +804,46 @@ int sdhi_mmc_init(unsigned long addr, int ch) + return ret; + } + ++ ++int sdhi_warmup_sdio(struct mmc *mmc) ++{ ++ struct mmc_cmd cmd; ++ int err; ++ int32_t ocr; ++ ++ udelay(10); ++ ++ mmc->bus_width = 1; ++ mmc->clock = mmc->f_min; ++ sdhi_set_ios(mmc); ++ udelay(10); ++ ++ cmd.cmdidx = MMC_CMD_GO_IDLE_STATE; ++ cmd.resp_type = MMC_RSP_NONE; ++ cmd.cmdarg = 0; ++ err = sdhi_request(mmc, &cmd, NULL); ++ if (err) ++ goto err_out; ++ cmd.cmdidx = 0x5; ++ cmd.resp_type = MMC_RSP_R4; ++ cmd.cmdarg = 0; ++ err = sdhi_request(mmc, &cmd, NULL); ++ if (err) ++ goto err_out; ++ ocr = cmd.response[0]; ++ ocr |= (1 << 24); ++ cmd.cmdidx = 0x05; ++ cmd.resp_type = MMC_RSP_R4; ++ cmd.cmdarg = ocr; ++ err = sdhi_request(mmc, &cmd, NULL); ++ if (err) ++ goto err_out; ++ printf("SDIO OCR:%08x\n", cmd.response[0]); ++ return 0; ++err_out: ++ printf("cmd: CMD%02d err = %d, resp = %08x, %08x, %08x, %08x\n", ++ err, cmd.cmdidx, cmd.response[0], cmd.response[1], ++ cmd.response[2], cmd.response[3]); ++ return err; ++} ++ +diff --git a/drivers/mmc/sh_sdhi.h b/drivers/mmc/sh_sdhi.h +index 4deded2..7b5d421 100644 +--- a/drivers/mmc/sh_sdhi.h ++++ b/drivers/mmc/sh_sdhi.h +@@ -15,6 +15,8 @@ + #ifndef _SH_SDHI_H_ + #define _SH_SDHI_H_ + ++#include <sh_dma.h> ++ + #define SDHI_CMD (0x0000 >> 1) + #define SDHI_PORTSEL (0x0004 >> 1) + #define SDHI_ARG0 (0x0008 >> 1) +@@ -181,6 +183,9 @@ struct sdhi_host { + unsigned int power_mode; + int ch; + int bus_shift; ++#ifdef CONFIG_SH_DMA ++ struct sh_dma_chan *dma_rx; ++#endif + }; + + static unsigned short g_wait_int[CONFIG_MMC_SH_SDHI_NR_CHANNEL]; +diff --git a/include/sh_dma.h b/include/sh_dma.h +new file mode 100644 +index 0000000..3f35c3a +--- /dev/null ++++ b/include/sh_dma.h +@@ -0,0 +1,58 @@ ++#ifndef __SH_DMA_H__ ++#define __SH_DMA_H__ ++ ++#include <asm/types.h> ++#include <errno.h> ++ ++#define SH_DMA_MMCIF0_RX 0xD2 ++#define SH_DMA_SDHI0_RX 0xCE ++ ++/* Address mode */ ++#define SH_DMA_AM_FIX 0 ++#define SH_DMA_AM_INC 1 ++#define SH_DMA_AM_DEC 2 ++ ++struct sh_dma; ++struct sh_dma_chan; ++ ++#ifdef CONFIG_SH_DMA ++struct sh_dma *sh_dma_add(u32 base, u32 nch); ++struct sh_dma_chan *sh_dma_chan_init(struct sh_dma *dma, int ch); ++void sh_dma_chan_release(struct sh_dma_chan *chan); ++ ++void sh_dma_chan_src(struct sh_dma_chan *chan, u32 src); ++void sh_dma_chan_dst(struct sh_dma_chan *chan, u32 dst); ++void sh_dma_chan_cfg(struct sh_dma_chan *chan, u8 midrid, u8 sm, u8 dm); ++void sh_dma_chan_start(struct sh_dma_chan *chan, u32 ts, u8 bs); ++void sh_dma_chan_stop(struct sh_dma_chan *chan); ++int sh_dma_chan_wait(struct sh_dma_chan *chan); ++void sh_dma_chan_clr(struct sh_dma_chan *chan); ++#else ++static inline struct sh_dma *sh_dma_add(u32 base, u32 nch) ++{ ++ return NULL; ++} ++ ++static inline struct sh_dma_chan *sh_dma_chan_init(struct sh_dma *dma, ++ int ch) ++{ ++ return NULL; ++} ++ ++static inline void sh_dma_chan_release(struct sh_dma_chan *chan) { } ++static inline void sh_dma_chan_src(struct sh_dma_chan *chan, u32 src) { } ++static inline void sh_dma_chan_dst(struct sh_dma_chan *chan, u32 dst) { } ++static inline void sh_dma_chan_cfg(struct sh_dma_chan *chan, ++ u8 midrid, u8 sm, u8 dm) { } ++static inline void sh_dma_chan_start(struct sh_dma_chan *chan, ++ u32 ts, u8 bs) { } ++static inline void sh_dma_chan_stop(struct sh_dma_chan *chan) { } ++static inline int sh_dma_chan_wait(struct sh_dma_chan *chan) ++{ ++ return -ENOSYS; ++} ++ ++static inline void sh_dma_chan_clr(struct sh_dma_chan *chan) { } ++#endif ++ ++#endif /* __SH_DMA_H__ */ +-- +1.8.3.1 + diff --git a/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot/hibernation/0002-Add-Hibernation-swsusp-command-support.patch b/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot/hibernation/0002-Add-Hibernation-swsusp-command-support.patch new file mode 100755 index 000000000..7c4c65658 --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot/hibernation/0002-Add-Hibernation-swsusp-command-support.patch @@ -0,0 +1,909 @@ +From 45b3abc592bd685726a6b55693ab95e4cb6065fc Mon Sep 17 00:00:00 2001 +From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com> +Date: Fri, 19 May 2017 14:27:46 +0900 +Subject: [PATCH 2/4] Add Hibernation swsusp command support + +Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com> +--- + common/cmd_swsusp.c | 889 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 889 insertions(+) + create mode 100644 common/cmd_swsusp.c + +diff --git a/common/cmd_swsusp.c b/common/cmd_swsusp.c +new file mode 100644 +index 0000000..ba05aa4 +--- /dev/null ++++ b/common/cmd_swsusp.c +@@ -0,0 +1,889 @@ ++/* ++ * 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 ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include <common.h> ++#include <command.h> ++#include <part.h> ++#include <malloc.h> ++ ++#include <linux/lzo.h> ++#include "../arch/arm/cpu/armv7/rmobile/crc32_word4.h" ++#include <swsuspmem.h> ++ ++/* Note for Renesas--based boards: ++ * We have the following memory split here: ++ * 0x40000000 - u_boot_lowest - used to store pfns at physical addresses ++ * u_boot_lowest - 0x8000000 - pfns are relocated, and then later put ++ * on physical addresses (swsusp_finish) ++ * 0x8000000 - 0xc0000000 - used to store pfns with physical address ++ * of 0x200000000 (long address), we have to change offset for them. ++ * Any pfn with address > 0x8000000 but less than 0x200000000 ++ * is an error. ++ * For boards which do not have memory above first GB, that will ++ * still work, as they won't have anything above 0x80000000 ++ * in their image, so for standard 2GB setup ou should put ++ * your secong GB in 0x80000000-0xC0000000 range, you can ++ * use MMU for that or if your RAM is continous, it will ++ * naturally be there. */ ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++/* #define PAGEMAP_DEBUG */ ++ ++#ifdef PAGEMAP_DEBUG ++#define SWSUSP_DEBUG_INFO ++#endif ++ ++#define SWSUSP_KEEP_IMAGE ++ ++#ifndef likely ++# define likely(x) __builtin_expect(!!(x), 1) ++# define unlikely(x) __builtin_expect(!!(x), 0) ++#endif ++ ++#define HIBERNATE_SIG "S1SUSPEND" ++#define PAGE_SIZE 4096 ++ ++/* Define depending on CONFIG_LBDAF in kernel */ ++typedef u64 sector_t; ++ ++ ++struct swsusp_header { ++ char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - ++ sizeof(int) - sizeof(u32) - ++ sizeof(CRC32_WORD4_t) - sizeof(u32)]; ++ CRC32_WORD4_t comp_crc32; ++ u32 img_size; /* append */ ++ u32 crc32; ++ sector_t image; ++ unsigned int flags; ++ char orig_sig[10]; ++ char sig[10]; ++} __packed; ++ ++#define __NEW_UTS_LEN 64 ++ ++struct new_utsname { ++ char sysname[__NEW_UTS_LEN + 1]; ++ char nodename[__NEW_UTS_LEN + 1]; ++ char release[__NEW_UTS_LEN + 1]; ++ char version[__NEW_UTS_LEN + 1]; ++ char machine[__NEW_UTS_LEN + 1]; ++ char domainname[__NEW_UTS_LEN + 1]; ++}; ++ ++struct swsusp_archdata { ++ u32 nosave_backup_phys; ++ u32 nosave_begin_phys; ++ u32 nosave_end_phys; ++ void (*cpu_resume_restore_nosave)(u32, u32, u32); ++}; ++ ++struct swsusp_info { ++ struct new_utsname uts; ++ u32 version_code; ++ unsigned long num_physpages; ++ int cpus; ++ unsigned long image_pages; ++ unsigned long pages; ++ unsigned long size; ++ char archdata[1024]; ++}; ++ ++struct swap_map_page { ++ u64 entries[PAGE_SIZE / sizeof(u64) - 1]; ++ u64 next_swap; ++}; ++ ++struct swsusp_finish_context { ++ void *remap_orig_page; ++ void *remap_temp_page; ++ struct swsusp_archdata archdata; ++}; ++ ++/* Do not specially exclude any bottom area */ ++#define USED_ADDRESS_TOP (CONFIG_SYS_SDRAM_BASE) ++#define USED_ADDRESS_END (CONFIG_SYS_SDRAM_BASE) ++ ++#define PG_UB2ZERO(pg) (pg - CONFIG_SYS_SDRAM_BASE / PAGE_SIZE) ++static u32 const exclude_min_page = ++ (USED_ADDRESS_TOP) / PAGE_SIZE; ++static u32 const exclude_max_page = ++ (USED_ADDRESS_END - 1) / PAGE_SIZE; ++static u32 const exclude_min_page_ub = ++ PG_UB2ZERO((USED_ADDRESS_TOP) / PAGE_SIZE); ++static u32 const exclude_max_page_ub = ++ PG_UB2ZERO((USED_ADDRESS_END-1) / PAGE_SIZE); ++#define SF_NOCOMPRESS_MODE 2 ++ ++#define LZO_HEADER sizeof(size_t) ++ ++/* Number of pages/bytes we'll compress at one time. */ ++#define LZO_UNC_PAGES 32 ++#define LZO_UNC_SIZE (LZO_UNC_PAGES * PAGE_SIZE) ++ ++/* Number of pages/bytes we need for compressed data (worst case). */ ++#define LZO_CMP_PAGES DIV_ROUND_UP(lzo1x_worst_compress(LZO_UNC_SIZE) + \ ++ LZO_HEADER, PAGE_SIZE) ++#define LZO_CMP_SIZE (LZO_CMP_PAGES * PAGE_SIZE) ++ ++static block_dev_desc_t *swap_dev; ++static disk_partition_t swap_info; ++ ++static struct swap_map_page *meta_map; ++static u64 meta_map_next; ++static u64 meta_map_curr; ++static u64 meta_map_start; ++static int meta_idx; ++ ++#ifdef PAGEMAP_DEBUG ++static int debugout; ++static int _last_read_pages; ++#define PAGEMAP_INFO(_msg, ...) do { if (debugout == 1) \ ++ printf(_msg, ## __VA_ARGS__); } while (0) ++#endif ++ ++#define HIGHMEM_PHYS_ADDR 0x200000000ULL ++#define HIGHMEM_VA 0x80000000UL ++#define HIGHMEM_PFN (HIGHMEM_PHYS_ADDR / PAGE_SIZE) ++#define LOW_TOP 0x80000000 ++#define LOW_TOP_PFN (LOW_TOP / PAGE_SIZE) ++#define LOW_BOTTOM CONFIG_SYS_SDRAM_BASE ++#define LOW_BOTTOM_PFN (LOW_BOTTOM / PAGE_SIZE) ++#define TOP_ADDRESS 0x240000000ULL ++ ++static int get_meta(void); ++ ++static inline int pfn_is_low(u32 pfn) ++{ ++ return ((pfn >= LOW_BOTTOM_PFN) && (pfn < LOW_TOP_PFN)); ++} ++ ++static inline int pfn_is_high(u32 pfn) ++{ ++ return (pfn >= HIGHMEM_PFN); ++} ++ ++#define pfn_is_valid(p) (pfn_is_low(p) || pfn_is_high(p)) ++ ++static inline int pfn_is_excluded(u32 pfn) ++{ ++ /* Allow bottom 2 pages for exception vectors */ ++ if (pfn < (LOW_BOTTOM_PFN + 2)) ++ return 0; ++ else if (exclude_min_page >= exclude_max_page) ++ return 0; ++ else ++ return (pfn >= exclude_min_page) && (pfn <= exclude_max_page); ++} ++ ++/* PFN to zero-counted page */ ++static inline u32 pg_ub2zero(u32 pg) ++{ ++ return pg - LOW_BOTTOM_PFN; ++} ++ ++/* zero-counted page to PFN */ ++static inline u32 pg_zero2ub(u32 pg) ++{ ++ return pg + LOW_BOTTOM_PFN; ++} ++ ++/* PFN to physical address (64-bit (40-bit)) */ ++static inline u64 pg2phys(u32 page) ++{ ++ return (u64) page * PAGE_SIZE; ++} ++ ++/* PFN to virtual address */ ++static inline void *pg2addr(u32 page) ++{ ++ void *addr; ++ if (page >= HIGHMEM_PFN) ++ addr = (void *) (u32)(pg2phys(page - HIGHMEM_PFN) + HIGHMEM_VA); ++ else ++ addr = (void *) (u32)pg2phys(page); ++ ++ return addr; ++} ++ ++#ifdef CONFIG_SH_DMA ++static inline void *malloc_aligned(u32 size, u32 align) ++{ ++ return (void *)(((u32)malloc(size + align) + align - 1) & ~(align - 1)); ++} ++ ++#endif ++ ++static int page_read(u32 page, void *addr) ++{ ++ __u32 cnt; ++ int blk_per_page; ++ ++ blk_per_page = PAGE_SIZE / swap_dev->blksz; ++ cnt = swap_dev->block_read(swap_dev->dev, ++ swap_info.start + (page * blk_per_page), ++ blk_per_page, addr); ++ return cnt != blk_per_page; ++} ++ ++#ifndef SWSUSP_KEEP_IMAGE ++static int page_write(u32 page, void *addr) ++{ ++ __u32 cnt; ++ int blk_per_page; ++ ++ blk_per_page = PAGE_SIZE / swap_dev->blksz; ++ cnt = swap_dev->block_write(swap_dev->dev, ++ swap_info.start + (page * blk_per_page), ++ blk_per_page, addr); ++ return cnt != blk_per_page; ++} ++#endif ++ ++#define FAST_COPY ++void __attribute__((section(".rodata"))) ++ __attribute__((optimize("O6", "unroll-loops"))) ++swsusp_finish(void *userdata) ++{ ++ struct swsusp_finish_context *context = userdata; ++ u32 **remap_orig; ++ u32 **remap_temp; ++ int idx = 0; ++ const int lastidx = PAGE_SIZE / sizeof(u32) - 1; ++ ++ remap_orig = context->remap_orig_page; ++ remap_temp = context->remap_temp_page; ++ ++ __asm__ volatile ("" : : : "memory"); ++ for (;;) { ++ u32 *orig, *temp; ++ int count; ++ ++ /* Linked list to next page */ ++ if (idx == lastidx) { ++ remap_orig = (u32 **)remap_orig[idx]; ++ remap_temp = (u32 **)remap_temp[idx]; ++ idx = 0; ++ } ++ if (unlikely(!remap_orig || remap_orig[idx] == (u32 *)~0UL)) ++ break; ++ orig = remap_orig[idx]; ++ temp = remap_temp[idx]; ++#ifdef FAST_COPY ++ count = PAGE_SIZE / sizeof(u32) / 32; ++ __asm__ volatile ( ++ "1:\n" ++ "ldmia %[rtemp]!, {r0-r7}\n" ++ "stmia %[rorig]!, {r0-r7}\n" ++ "ldmia %[rtemp]!, {r0-r7}\n" ++ "stmia %[rorig]!, {r0-r7}\n" ++ "ldmia %[rtemp]!, {r0-r7}\n" ++ "stmia %[rorig]!, {r0-r7}\n" ++ "ldmia %[rtemp]!, {r0-r7}\n" ++ "subs %[count], %[count], #1\n" ++ "stmia %[rorig]!, {r0-r7}\n" ++ "bgt 1b\n" ++ : /* No outputs */ ++ : ++ [rorig]"h" (orig), ++ [rtemp]"h" (temp), ++ [count]"h" (count) ++ : "r0", "r1", "r2", ++ "r3", "r4", "r5", ++ "r6", "r7", "cc", "memory" ++ ); ++#else ++ count = PAGE_SIZE / sizeof(u32); ++ while (count--) ++ *orig++ = *temp++; ++#endif ++#ifdef SWSUSP_CHECK_COPY_RESULT ++ count = PAGE_SIZE / sizeof(u32); ++ orig = remap_orig[idx]; ++ temp = remap_temp[idx]; ++ __asm__ volatile ( ++ "1:\n" ++ "ldr r3, [%[rorig]]\n" ++ "ldr r4, [%[rtemp]]\n" ++ "cmp r3, r4\n" ++ "bne 2f\n" ++ "add %[rorig], %[rorig], #4\n" ++ "add %[rtemp], %[rtemp], #4\n" ++ "subs %[count], %[count], #1\n" ++ "bgt 1b\n" ++ "b 3f\n" ++ "2:b 2b\n" ++ "3:\n" ++ : ++ [rorig]"+r" (orig), ++ [rtemp]"+r" (temp), ++ [count]"+r" (count) ++ : ++ : "r3", "r4", "cc", "memory" ++ ); ++#endif ++ idx++; ++ } ++ context->archdata.cpu_resume_restore_nosave( ++ context->archdata.nosave_backup_phys, ++ context->archdata.nosave_begin_phys, ++ context->archdata.nosave_end_phys); ++} ++ ++static int raw_page_init(u64 start) ++{ ++#ifdef CONFIG_SH_DMA ++ meta_map = malloc_aligned(PAGE_SIZE, ARCH_DMA_MINALIGN); ++#else ++ meta_map = malloc(PAGE_SIZE); ++#endif ++ if (!meta_map) ++ return -1; ++ meta_map_next = 0; ++ meta_map_curr = 0; ++ meta_map_start = start; ++ return 0; ++} ++ ++static void raw_page_start(void) ++{ ++ meta_idx = ARRAY_SIZE(meta_map->entries); ++ meta_map_next = meta_map_start; ++} ++ ++static int get_meta(void) ++{ ++ if (meta_idx == ARRAY_SIZE(meta_map->entries)) { ++ if (!meta_map_next) ++ return 0; ++ if (meta_map_curr != meta_map_next) { ++#ifdef PAGEMAP_DEBUG ++ PAGEMAP_INFO("META: %d (%08x)\n", ++ (int)meta_map_next, ++ (unsigned int) ++ (meta_map_next * PAGE_SIZE)); ++#endif ++ if (page_read(meta_map_next, meta_map)) ++ return -1; ++ meta_map_curr = meta_map_next; ++ meta_map_next = meta_map->next_swap; ++ } ++ meta_idx = 0; ++ } ++#ifdef PAGEMAP_DEBUG ++ { ++ static unsigned int pre; ++ if ((pre+1) != meta_map->entries[meta_idx]) { ++ PAGEMAP_INFO("DATA-Skipped: %d->%d (%08x->%08x)\n", ++ pre, ++ (unsigned int)meta_map->entries[meta_idx], ++ pre*PAGE_SIZE, ++ (unsigned int) ++ (meta_map->entries[meta_idx] * ++ PAGE_SIZE)); ++ } ++ pre = meta_map->entries[meta_idx]; ++ _last_read_pages = pre; ++ } ++#endif ++ return 1; ++} ++ ++static int raw_page_get_next(void *buffer) ++{ ++ if (!get_meta()) ++ return 0; ++ ++ if (page_read(meta_map->entries[meta_idx++], buffer)) ++ return -1; ++ ++ return 1; ++} ++ ++static void raw_page_exit(void) ++{ ++ free(meta_map); ++ meta_map = NULL; ++} ++ ++static int image_compressed; ++static int image_pages_avail; ++static unsigned char *unc_buf; ++static unsigned char *cmp_buf; ++static int unc_offset; ++ ++static int image_page_init(int compressed) ++{ ++ if (!compressed) ++ return 1; ++ ++#ifdef CONFIG_SH_DMA ++ cmp_buf = malloc_aligned(LZO_CMP_SIZE, ARCH_DMA_MINALIGN); ++#else ++ cmp_buf = malloc(LZO_CMP_SIZE); ++#endif ++ unc_buf = malloc(LZO_UNC_SIZE); ++ if (!unc_buf || !cmp_buf) { ++ printf("not enogh memory\n"); ++ return 1; ++ } ++ image_compressed = compressed; ++ return 0; ++} ++ ++static void image_page_start(void) ++{ ++ image_pages_avail = 0; ++} ++ ++static int image_page_get_next(void *buffer) ++{ ++ if (image_compressed) { ++#ifdef CONFIG_LZO ++ if (!image_pages_avail) { ++ int ret; ++ size_t unc_len, cmp_len, cmp_avail; ++ ++ ret = raw_page_get_next(cmp_buf); ++ if (ret <= 0) ++ return ret; ++ ++ cmp_len = *(size_t *) cmp_buf; ++ cmp_avail = PAGE_SIZE; ++ ++ while (cmp_avail < cmp_len + LZO_HEADER) { ++ ret = raw_page_get_next(cmp_buf + cmp_avail); ++ if (unlikely(ret <= 0)) ++ return ret; ++ cmp_avail += PAGE_SIZE; ++ } ++ unc_len = LZO_UNC_SIZE; ++ ret = lzo1x_decompress_safe(cmp_buf + LZO_HEADER, ++ cmp_len, unc_buf, &unc_len); ++ if (unlikely(ret != LZO_E_OK)) { ++ printf("Decompression failure:\n"); ++ printf("ret = %d\n", ret); ++ printf("cmp_buf = %p\n", cmp_buf + LZO_HEADER); ++ printf("cmp_len = %zu\n", cmp_len); ++ printf("unc_len = %zu\n", unc_len); ++ return ret; ++ } ++ image_pages_avail = unc_len / PAGE_SIZE; ++ unc_offset = 0; ++ } ++ ++ memcpy(buffer, unc_buf + unc_offset, PAGE_SIZE); ++ unc_offset += PAGE_SIZE; ++ image_pages_avail--; ++ return 1; ++#else ++ printf("No LZO support in u-boot.\n"); ++ return -1; ++#endif ++ } else { ++ return raw_page_get_next(buffer); ++ } ++} ++ ++static void image_page_exit(void) ++{ ++ free(unc_buf); ++ free(cmp_buf); ++ unc_buf = cmp_buf = NULL; ++} ++ ++static void bitmap_set(u32 *bm, unsigned int bit) ++{ ++ bm[bit >> 5] |= (1 << (bit & 0x1f)); ++} ++ ++static int bitmap_is_set(u32 *bm, unsigned int bit) ++{ ++ return !!(bm[bit >> 5] & (1 << (bit & 0x1f))); ++} ++ ++static u32 *used_bitmap; ++static u32 next_free_page; ++static u32 total_pages; ++ ++static int free_page_init(void) ++{ ++ total_pages = (u32)((TOP_ADDRESS - ++ LOW_BOTTOM) / PAGE_SIZE); /* 2GB */ ++ used_bitmap = malloc(total_pages * sizeof(u32) / 32); ++ if (!used_bitmap) ++ return -1; ++ return 0; ++} ++ ++static void free_page_start(int offset) ++{ ++ memset(used_bitmap, 0, total_pages * sizeof(u32) / 32); ++ next_free_page = pg_ub2zero(offset); ++} ++ ++static void free_page_mark_used(u32 page); ++/* Returns full-address based pages */ ++static int free_page_get_next(void) ++{ ++ while (bitmap_is_set(used_bitmap, next_free_page)) ++ next_free_page++; ++ free_page_mark_used(next_free_page); ++ return pg_zero2ub(next_free_page++); ++} ++ ++static void free_page_mark_used(u32 page) ++{ ++ bitmap_set(used_bitmap, page); ++} ++ ++static void free_page_exit(void) ++{ ++ free(used_bitmap); ++ used_bitmap = NULL; ++} ++ ++int do_swsusp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ int ret; ++ __u32 offset = 0; ++ void *spare_page = NULL; ++ struct swsusp_header *swsusp_header; ++ struct swsusp_info *swsusp_info; ++ struct swsusp_finish_context *context; ++ int max_page; ++ int i; ++ u32 nr_pfn_pages; ++ u32 **pfn_pages = NULL; ++ u32 *remap_orig_page; ++ u32 *remap_temp_page; ++ u32 **remap_orig; ++ u32 **remap_temp; ++ int remap_idx; ++ int m; ++ void (*swsusp_finish_copy)(void *); ++ char *data_page; ++ char *stack_addr; ++ int high_page; ++#ifdef USE_CRC_32x4 ++ CRC32_WORD4_t calc_crc; ++#endif ++#ifdef PAGEMAP_DEBUG ++ int high_page_images = 0; ++ int total_remap = 0; ++ if (getenv("hybdebug") != NULL) ++ debugout = 1; ++#endif ++ /* Address hack */ ++ void *swsusp_finish_p = (void *)((u32)swsusp_finish & ~0x1); ++ ++ if (argc < 2) { ++ printf("usage: swsusp <interface> " ++ "[<dev[:part]>] [<offset>]\n"); ++ return 0; ++ } ++ ++ if (argc == 4) { ++ char *ep; ++ offset = simple_strtoul(argv[3], &ep, 16); ++ if (*ep) { ++ printf("Invalid block offset\n"); ++ return 1; ++ } ++ } ++ ++ /* Allow for 32 pages of stack */ ++ max_page = gd->start_addr_sp / PAGE_SIZE - 32; ++ high_page = (((gd->relocaddr ++ + _bss_end_ofs)+(PAGE_SIZE-1)) / PAGE_SIZE) + 1; ++#define pfn_is_occupied(pfn) (page > max_page && page <= high_page) ++#ifdef PAGEMAP_DEBUG ++ PAGEMAP_INFO(" *gd->start_addr_sp:%p\n", (void *)gd->start_addr_sp); ++ PAGEMAP_INFO(" *gd->relocaddr:%p\n", (void *)gd->relocaddr); ++ PAGEMAP_INFO(" *bss_start_offset:%d bss_end_offset:%d\n", ++ (int)_bss_start_ofs, (int)_bss_end_ofs); ++ PAGEMAP_INFO(" UBOOT own memory [%p-%p]\n", ++ pg2addr(max_page), pg2addr(high_page)); ++#endif ++ ++ if (free_page_init()) ++ goto mem_err; ++ free_page_start(exclude_max_page + 1); ++ ++#ifdef CONFIG_SH_DMA ++ spare_page = malloc_aligned(PAGE_SIZE, ARCH_DMA_MINALIGN); ++#else ++ spare_page = malloc(PAGE_SIZE); ++#endif ++ if (!spare_page) ++ goto mem_err; ++ ++ ret = get_device_and_partition(argv[1], argv[2], &swap_dev, &swap_info, ++ 1); ++ if (ret < 0) ++ goto err; ++ ++ swsusp_header = spare_page; ++ if (page_read(offset, swsusp_header)) ++ goto read_err; ++ ++#ifdef SWSUSP_DEBUG_INFO ++ PAGEMAP_INFO("swssp_header:\n"); ++ PAGEMAP_INFO(" comp_crc: <snip>\n"); ++ PAGEMAP_INFO(" img_size: %d\n", swsusp_header->img_size); ++ PAGEMAP_INFO(" image(swap firest sector): %08x\n", ++ (unsigned int)swsusp_header->image); ++ PAGEMAP_INFO(" flags: %08x\n", swsusp_header->flags); ++ PAGEMAP_INFO(" orig_sig:%s\n", swsusp_header->orig_sig); ++ PAGEMAP_INFO(" sig:%s\n", swsusp_header->sig); ++#endif /* SWSUSP_DEBUG_INFO */ ++ ++ if (memcmp(HIBERNATE_SIG, swsusp_header->sig, 10)) { ++ printf("No hibernation image present\n"); ++ return 0; ++ } ++ ++#ifdef USE_CRC_32x4 ++ memset(&calc_crc, 0, sizeof(calc_crc)); ++ calc_crc32x4((u8 *)((unsigned long)offt_addr + PAGE_SIZE), ++ swsusp_header->img_size, &calc_crc); ++ ++ if (memcmp(&calc_crc, &swsusp_header->comp_crc32, ++ sizeof(CRC32_WORD4_t))) { ++ printf("Bad CRC for image, image: %08x:%08x:%08x:%08x, calc: %08x:%08x:%08x:%08x\n", ++ swsusp_header->comp_crc32.crc_w[0], ++ swsusp_header->comp_crc32.crc_w[1], ++ swsusp_header->comp_crc32.crc_w[2], ++ swsusp_header->comp_crc32.crc_w[3], ++ calc_crc.crc_w[0], calc_crc.crc_w[1], ++ calc_crc.crc_w[2], calc_crc.crc_w[3]); ++ return 0; ++ } ++#endif ++ ++ /* Overwrite header if necessary */ ++#ifndef SWSUSP_KEEP_IMAGE ++ if (memcmp(swsusp_header->sig, swsusp_header->orig_sig, 10)) { ++ memcpy(swsusp_header->sig, swsusp_header->orig_sig, 10); ++ if (page_write(offset, swsusp_header)) ++ printf("Write error resetting header\n"); ++ } ++#endif ++ ++ if (raw_page_init(swsusp_header->image)) ++ goto mem_err; ++ raw_page_start(); ++ ++ if (image_page_init(!(swsusp_header->flags & SF_NOCOMPRESS_MODE))) ++ goto mem_err; ++ image_page_start(); ++ ++ swsusp_info = spare_page; ++ if (raw_page_get_next(swsusp_info) <= 0) ++ goto read_err; ++ ++#ifdef SWSUSP_DEBUG_INFO ++ PAGEMAP_INFO("swsup_info:\n"); ++ PAGEMAP_INFO(" utsname.sysname:%s\n", swsusp_info->uts.sysname); ++ PAGEMAP_INFO(" nodename:%s\n", swsusp_info->uts.nodename); ++ PAGEMAP_INFO(" release:%s\n", swsusp_info->uts.release); ++ PAGEMAP_INFO(" version:%s\n", swsusp_info->uts.version); ++ PAGEMAP_INFO(" machine:%s\n", swsusp_info->uts.machine); ++ PAGEMAP_INFO(" vesion_code:%#08x\n", ++ (unsigned int)swsusp_info->version_code); ++ PAGEMAP_INFO(" num_physpages:%d\n", ++ (unsigned int)swsusp_info->num_physpages); ++ PAGEMAP_INFO(" pages :%d\n", ++ (unsigned int)swsusp_info->pages); ++ PAGEMAP_INFO(" size :%d\n", ++ (unsigned int)swsusp_info->size); ++#endif ++ ++ nr_pfn_pages = (swsusp_info->image_pages * 4 + PAGE_SIZE - 1) / ++ PAGE_SIZE; ++ pfn_pages = malloc(nr_pfn_pages * sizeof(u32 *)); ++ if (!pfn_pages) ++ goto mem_err; ++ memset(pfn_pages, 0, nr_pfn_pages * sizeof(u32 *)); ++ ++ /* UBOOT using memory */ ++ for (i = max_page; i <= high_page; i++) ++ free_page_mark_used(pg_ub2zero(i)); ++ ++ printf("Allocating %u bytes (nr_pfn_pages %u)\n", ++ nr_pfn_pages * PAGE_SIZE, nr_pfn_pages); ++ ++ for (i = 0; i < nr_pfn_pages; i++) { ++ u32 idx; ++#ifdef CONFIG_SH_DMA ++ pfn_pages[i] = malloc_aligned(PAGE_SIZE, ARCH_DMA_MINALIGN); ++#else ++ pfn_pages[i] = malloc(PAGE_SIZE); ++#endif ++ if (unlikely(!pfn_pages[i])) ++ goto mem_err; ++ if (unlikely(image_page_get_next(pfn_pages[i]) <= 0)) ++ goto read_err; ++ for (idx = 0; idx < PAGE_SIZE / sizeof(u32); idx++) { ++ u32 page = pfn_pages[i][idx]; ++ if (page == ~0UL) ++ break; ++ free_page_mark_used(pg_ub2zero(page)); ++ } ++ } ++ ++ printf("Loading image data pages (%lu pages)\n", ++ swsusp_info->image_pages); ++ ++ remap_orig_page = pg2addr(free_page_get_next()); ++ remap_temp_page = pg2addr(free_page_get_next()); ++ ++ remap_orig = (u32 **)remap_orig_page; ++ remap_temp = (u32 **)remap_temp_page; ++ remap_idx = 0; ++ ++ m = (swsusp_info->image_pages / 10) ? : 1; ++ for (i = 0; i < swsusp_info->image_pages; i++) { ++ u32 page = pfn_pages[i >> 10][i & 0x3ff]; ++ if (unlikely(!pfn_is_valid(page))) { ++ printf("Attempt to restore invalid address %llx\n", ++ pg2phys(page)); ++ continue; ++ } else if (unlikely(pfn_is_excluded(page))) { ++ printf("Attempt to restore excluded address %llx\n", ++ pg2phys(page)); ++ continue; ++ } else if (unlikely(pfn_is_low(page) && ++ pfn_is_occupied(page))) { ++ remap_orig[remap_idx] = pg2addr(page); ++ page = free_page_get_next(); ++ remap_temp[remap_idx] = pg2addr(page); ++ remap_idx++; ++#ifdef PAGEMAP_DEBUG ++ ++total_remap; ++#endif ++ /* If we fill our current page, allocate a new one */ ++ if (remap_idx + 1 == PAGE_SIZE / sizeof(u32)) { ++ u32 *next; ++ ++ next = pg2addr(free_page_get_next()); ++ remap_orig[remap_idx] = next; ++ remap_orig = (u32 **)next; ++ ++ next = pg2addr(free_page_get_next()); ++ remap_temp[remap_idx] = next; ++ remap_temp = (u32 **)next; ++ ++ remap_idx = 0; ++ } ++ } ++ if (image_page_get_next(pg2addr(page)) <= 0) ++ goto read_err; ++ if (!(i % m)) ++ printf("Image loading progress: %3d%%\n", 10 * i / m); ++ } ++ ++ printf("Image loading done.\n"); ++ invalidate_icache_all(); ++ ++ /* put end markers on the remap list */ ++ remap_orig[remap_idx] = (void *) ~0UL; ++ remap_temp[remap_idx] = (void *) ~0UL; ++ ++#ifdef PAGEMAP_DEBUG ++ PAGEMAP_INFO("Number of remap pages:%d\n", ++ total_remap); ++ PAGEMAP_INFO("Number of high pages:%d\n", ++ high_page_images); ++ PAGEMAP_INFO("Last read page %d (%08x)\n", ++ _last_read_pages, ++ _last_read_pages * PAGE_SIZE); ++#endif ++ remap_orig = (u32 **)remap_orig_page; ++ remap_temp = (u32 **)remap_temp_page; ++ ++ /* Make a copy of swsusp_finish in a free data page */ ++ data_page = pg2addr(free_page_get_next()); ++ memcpy(data_page, swsusp_finish_p, PAGE_SIZE); ++ swsusp_finish_copy = (void *) data_page; ++ ++ /* Setup context for swsusp_finish at the end of the data_page */ ++ context = (struct swsusp_finish_context *) (data_page + PAGE_SIZE - ++ sizeof(struct swsusp_finish_context)); ++ context->remap_orig_page = remap_orig_page; ++ context->remap_temp_page = remap_temp_page; ++ memcpy((void *)&context->archdata, (void *)swsusp_info->archdata, ++ sizeof(struct swsusp_archdata)); ++ ++ /* Get a stack pointer for swsusp_finish, growing down from context */ ++ stack_addr = (char *) context; ++ ++#ifdef CONFIG_NETCONSOLE ++ /* ++ * Stop the ethernet stack if NetConsole could have ++ * left it up ++ */ ++ eth_halt(); ++#endif ++ ++#ifdef CONFIG_USB_DEVICE ++ udc_disconnect(); ++#endif ++#ifdef PAGEMAP_DEBUG ++ PAGEMAP_INFO("Execution routine: %08x\n", ++ (u32)context->archdata.cpu_resume_restore_nosave); ++#endif ++ arch_preboot_os(); ++ cleanup_before_linux(); ++ ++ /* Copy the final data from a safe place */ ++ call_with_stack(swsusp_finish_copy, context, stack_addr); ++ ++ return 0; ++ ++mem_err: ++ printf("Not enough memory.\n"); ++ goto err; ++ ++read_err: ++ printf("Read error while restoring image.\n"); ++ ++err: ++ __asm__ volatile ( ++ "mov r0, #0\n" ++ "mcr p15, 0, r0, c7, c5, 0 @ invalidate icache\n" ++ "mcr p15, 0, r0, c7, c10, 4 @ DSB\n" ++ "mcr p15, 0, r0, c7, c5, 4 @ ISB\n" ++ : : : "r0", "memory"); ++ ++ raw_page_exit(); ++ image_page_exit(); ++ free_page_exit(); ++ if (pfn_pages) { ++ for (i = 0; i < nr_pfn_pages; i++) ++ free(pfn_pages[i]); ++ free(pfn_pages); ++ } ++ free(spare_page); ++ ++ return 1; ++} ++ ++U_BOOT_CMD(swsusp, 4, 0, do_swsusp, ++ "Restore SWSUSP hibernation image", ++ "<interface> [<dev[:part]>] [<offset>]" ++); +-- +1.8.3.1 + diff --git a/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot/hibernation/0003-Add-Hibernation-swsuspmem-command-support.patch b/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot/hibernation/0003-Add-Hibernation-swsuspmem-command-support.patch new file mode 100755 index 000000000..8bfcccbbf --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot/hibernation/0003-Add-Hibernation-swsuspmem-command-support.patch @@ -0,0 +1,1058 @@ +From 4ce00daa904a40701ab6bed44506fe97b8f1da47 Mon Sep 17 00:00:00 2001 +From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com> +Date: Fri, 19 May 2017 14:48:38 +0900 +Subject: [PATCH 3/4] Add Hibernation swsuspmem command support + +Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com> +--- + common/Makefile | 2 + + common/cmd_swsuspmem.c | 944 +++++++++++++++++++++++++++++++++++++++++++++ + include/swsuspmem.h | 24 ++ + lib/lzo/lzo1x_decompress.c | 12 +- + 4 files changed, 980 insertions(+), 2 deletions(-) + create mode 100644 common/cmd_swsuspmem.c + create mode 100644 include/swsuspmem.h + +diff --git a/common/Makefile b/common/Makefile +index 54fcc81..7a18486 100644 +--- a/common/Makefile ++++ b/common/Makefile +@@ -160,6 +160,8 @@ COBJS-$(CONFIG_CMD_SETEXPR) += cmd_setexpr.o + COBJS-$(CONFIG_CMD_SPI) += cmd_spi.o + COBJS-$(CONFIG_CMD_SPIBOOTLDR) += cmd_spibootldr.o + COBJS-$(CONFIG_CMD_STRINGS) += cmd_strings.o ++COBJS-$(CONFIG_CMD_SWSUSP) += cmd_swsusp.o ++COBJS-$(CONFIG_CMD_SWSUSPMEM) += cmd_swsuspmem.o + COBJS-$(CONFIG_CMD_TERMINAL) += cmd_terminal.o + COBJS-$(CONFIG_CMD_TIME) += cmd_time.o + COBJS-$(CONFIG_SYS_HUSH_PARSER) += cmd_test.o +diff --git a/common/cmd_swsuspmem.c b/common/cmd_swsuspmem.c +new file mode 100644 +index 0000000..6980aaf +--- /dev/null ++++ b/common/cmd_swsuspmem.c +@@ -0,0 +1,944 @@ ++/* ++ * 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 ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include <common.h> ++#include <command.h> ++#include <part.h> ++#include <malloc.h> ++ ++#include <linux/lzo.h> ++#include "../arch/arm/cpu/armv7/rmobile/crc32_word4.h" ++#include <swsuspmem.h> ++ ++/* Note for Renesas--based boards: ++ * We have the following memory split here: ++ * 0x40000000 - u_boot_lowest - used to store pfns at physical addresses ++ * u_boot_lowest - 0x8000000 - pfns are relocated, and then later put ++ * on physical addresses (swsusp_finish) ++ * 0x8000000 - 0xc0000000 - used to store pfns with physical address ++ * of 0x200000000 (long address), we have to change offset for them. ++ * Any pfn with address > 0x8000000 but less than 0x200000000 ++ * is an error. ++ * For boards which do not have memory above first GB, that will ++ * still work, as they won't have anything above 0x80000000 ++ * in their image, so for standard 2GB setup ou should put ++ * your secong GB in 0x80000000-0xC0000000 range, you can ++ * use MMU for that or if your RAM is continous, it will ++ * naturally be there. */ ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++/* #define PAGEMAP_DEBUG */ ++ ++#ifdef PAGEMAP_DEBUG ++#define SWSUSP_DEBUG_INFO ++#endif ++ ++#define SWSUSP_KEEP_IMAGE ++ ++#ifndef likely ++# define likely(x) __builtin_expect(!!(x), 1) ++# define unlikely(x) __builtin_expect(!!(x), 0) ++#endif ++ ++#define HIBERNATE_SIG "S1SUSPEND" ++#define PAGE_SIZE (4096) ++/* Define depending on CONFIG_LBDAF in kernel */ ++ ++typedef u64 sector_t; ++ ++struct swsusp_header { ++ char reserved[PAGE_SIZE - 20 ++ - sizeof(sector_t) - sizeof(int) - sizeof(u32) ++ - sizeof(CRC32_WORD4_t) - sizeof(u32)]; ++ CRC32_WORD4_t comp_crc32; ++ u32 img_size; /* append */ ++ u32 crc32; ++ sector_t image; ++ unsigned int flags; ++ char orig_sig[10]; ++ char sig[10]; ++} __packed; ++ ++#define __NEW_UTS_LEN 64 ++ ++struct new_utsname { ++ char sysname[__NEW_UTS_LEN + 1]; ++ char nodename[__NEW_UTS_LEN + 1]; ++ char release[__NEW_UTS_LEN + 1]; ++ char version[__NEW_UTS_LEN + 1]; ++ char machine[__NEW_UTS_LEN + 1]; ++ char domainname[__NEW_UTS_LEN + 1]; ++}; ++ ++struct swsusp_archdata { ++ u32 nosave_backup_phys; ++ u32 nosave_begin_phys; ++ u32 nosave_end_phys; ++ void (*cpu_resume_restore_nosave)(u32, u32, u32); ++}; ++ ++struct swsusp_info { ++ struct new_utsname uts; ++ u32 version_code; ++ unsigned long num_physpages; ++ int cpus; ++ unsigned long image_pages; ++ unsigned long pages; ++ unsigned long size; ++ char archdata[1024]; ++}; ++ ++struct swap_map_page { ++ u64 entries[PAGE_SIZE / sizeof(u64) - 1]; ++ u64 next_swap; ++}; ++ ++struct swsusp_finish_context { ++ void *remap_orig_page; ++ void *remap_temp_page; ++ struct swsusp_archdata archdata; ++}; ++#ifdef FTEN_SPF_SDRAM_BASE ++#define USED_ADDRESS_TOP (CONFIG_SYS_SDRAM_BASE) ++#define USED_ADDRESS_END (CONFIG_SYS_SDRAM_BASE + CONFIG_SYS_LOAD_OFFSET) ++#else ++#define USED_ADDRESS_TOP (0x40000000) ++#define USED_ADDRESS_END (0x48000000) ++#endif ++#define PG_UB2ZERO(pg) ((pg) - CONFIG_SYS_SDRAM_BASE / PAGE_SIZE) ++static u32 const exclude_min_page = ++ (USED_ADDRESS_TOP) / PAGE_SIZE; ++static u32 const exclude_max_page = ++ (USED_ADDRESS_END - 1) / PAGE_SIZE; ++static u32 const exclude_min_page_ub = ++ PG_UB2ZERO((USED_ADDRESS_TOP) / PAGE_SIZE); ++static u32 const exclude_max_page_ub = ++ PG_UB2ZERO((USED_ADDRESS_END-1) / PAGE_SIZE); ++ ++/* ++ #define SD_PLATFORM_MODE 1 ++ #define SD_CRC32_MODE 4 ++ */ ++#define SF_NOCOMPRESS_MODE 2 ++ ++#define LZO_HEADER sizeof(size_t) ++ ++/* Number of pages/bytes we'll compress at one time. */ ++#define LZO_UNC_PAGES 32 ++#define LZO_UNC_SIZE (LZO_UNC_PAGES * PAGE_SIZE) ++ ++/* Number of pages/bytes we need for compressed data (worst case). */ ++#define LZO_CMP_PAGES DIV_ROUND_UP(lzo1x_worst_compress(LZO_UNC_SIZE) + \ ++ LZO_HEADER, PAGE_SIZE) ++#define LZO_CMP_SIZE (LZO_CMP_PAGES * PAGE_SIZE) ++ ++static struct swsuspmem_hook *_hook; ++ ++#define CALL_HOOK(f, param) \ ++ do { \ ++ if (_hook != NULL) { \ ++ if (_hook->f != NULL) \ ++ _hook->f(param); \ ++ } \ ++ } while (0) ++ ++#ifdef PAGEMAP_DEBUG ++static int debugout; ++static int _last_read_pages; ++#define PAGEMAP_INFO(_msg, ...) \ ++ do { \ ++ if (debugout == 1) \ ++ printf(_msg, ## __VA_ARGS__); \ ++ } while (0) ++#endif ++ ++#define HIGHMEM_PHYS_ADDR 0x200000000ULL ++#define HIGHMEM_VA 0x80000000UL ++#define HIGHMEM_PFN (HIGHMEM_PHYS_ADDR / PAGE_SIZE) ++#define LOW_TOP 0x80000000 ++#define LOW_TOP_PFN (LOW_TOP / PAGE_SIZE) ++#define LOW_BOTTOM CONFIG_SYS_SDRAM_BASE ++#define LOW_BOTTOM_PFN (LOW_BOTTOM / PAGE_SIZE) ++#define TOP_ADDRESS 0x240000000ULL ++ ++static inline int pfn_is_low(u32 pfn) ++{ ++ return ((pfn >= LOW_BOTTOM_PFN) && (pfn < LOW_TOP_PFN)); ++} ++ ++static inline int pfn_is_high(u32 pfn) ++{ ++ return (pfn >= HIGHMEM_PFN); ++} ++ ++#define pfn_is_valid(p) (pfn_is_low(p) || pfn_is_high(p)) ++ ++static inline int pfn_is_excluded(u32 pfn) ++{ ++ /* Allow bottom 2 pages for exception vectors */ ++ if (pfn < (LOW_BOTTOM_PFN + 2)) ++ return 0; ++ else if (exclude_min_page >= exclude_max_page) ++ return 0; ++ else ++ return (pfn >= exclude_min_page) && (pfn <= exclude_max_page); ++} ++/* PFN to zero-counted page */ ++static inline u32 pg_ub2zero(u32 pg) ++{ ++ return pg - LOW_BOTTOM_PFN; ++} ++ ++/* zero-counted page to PFN */ ++static inline u32 pg_zero2ub(u32 pg) ++{ ++ return pg + LOW_BOTTOM_PFN; ++} ++ ++/* PFN to physical address (64-bit (40-bit)) */ ++static inline u64 pg2phys(u32 page) ++{ ++ return (u64) page * PAGE_SIZE; ++} ++ ++/* PFN to virtual address */ ++static inline void *pg2addr(u32 page) ++{ ++ void *addr; ++ if (page >= HIGHMEM_PFN) ++ addr = (void *) (u32)(pg2phys(page - HIGHMEM_PFN) + HIGHMEM_VA); ++ else ++ addr = (void *) (u32)pg2phys(page); ++ ++ return addr; ++} ++/* Virtual address to PFN */ ++static inline u32 addr2pg(void *addr) ++{ ++ return ((u32)(addr)) / PAGE_SIZE; ++} ++static void *offt_addr = (void *)0x44000000; ++static int page_read_mem(u64 page, void *addr) ++{ ++ memcpy(addr, (u8 *)offt_addr + page * PAGE_SIZE, PAGE_SIZE); ++ return 0; ++} ++ ++#ifndef SWSUSP_KEEP_IMAGE ++static int page_write_mem(u32 page, void *addr) ++{ ++ memcpy((u8 *)offt_addr + page * PAGE_SIZE, addr, PAGE_SIZE); ++ return 0; ++} ++#endif ++ ++#define FAST_COPY ++static void __attribute__((section(".rodata"))) ++ __attribute__((optimize("O6", "unroll-loops"))) ++swsusp_finish(void *userdata) ++{ ++ struct swsusp_finish_context *context = userdata; ++ u32 **remap_orig; ++ u32 **remap_temp; ++ int idx = 0; ++ const int lastidx = PAGE_SIZE / sizeof(u32) - 1; ++ ++ remap_orig = context->remap_orig_page; ++ remap_temp = context->remap_temp_page; ++ ++ __asm__ volatile ("" : : : "memory"); ++ for (;;) { ++ u32 *orig, *temp; ++ int count; ++ ++ /* Linked list to next page */ ++ if (idx == lastidx) { ++ remap_orig = (u32 **)remap_orig[idx]; ++ remap_temp = (u32 **)remap_temp[idx]; ++ idx = 0; ++ } ++ if (unlikely(!remap_orig || remap_orig[idx] == (u32 *)~0UL)) ++ break; ++ orig = remap_orig[idx]; ++ temp = remap_temp[idx]; ++#ifdef FAST_COPY ++ count = PAGE_SIZE / sizeof(u32) / 32; ++ __asm__ volatile ( ++ "1:\n" ++ "ldmia %[rtemp]!, {r0-r7}\n" ++ "stmia %[rorig]!, {r0-r7}\n" ++ "ldmia %[rtemp]!, {r0-r7}\n" ++ "stmia %[rorig]!, {r0-r7}\n" ++ "ldmia %[rtemp]!, {r0-r7}\n" ++ "stmia %[rorig]!, {r0-r7}\n" ++ "ldmia %[rtemp]!, {r0-r7}\n" ++ "subs %[count], %[count], #1\n" ++ "stmia %[rorig]!, {r0-r7}\n" ++ "bgt 1b\n" ++ : /* No outputs */ ++ : ++ [rorig]"h" (orig), ++ [rtemp]"h" (temp), ++ [count]"h" (count) ++ : "r0", "r1", "r2", "r3", ++ "r4", "r5", "r6", "r7", ++ "cc", "memory" ++ ); ++#else ++ count = PAGE_SIZE / sizeof(u32); ++ while (count--) ++ *orig++ = *temp++; ++#endif ++#ifdef SWSUSP_CHECK_COPY_RESULT ++ count = PAGE_SIZE / sizeof(u32); ++ orig = remap_orig[idx]; ++ temp = remap_temp[idx]; ++ __asm__ volatile ( ++ "1:\n" ++ "ldr r3, [%[rorig]]\n" ++ "ldr r4, [%[rtemp]]\n" ++ "cmp r3, r4\n" ++ "bne 2f\n" ++ "add %[rorig], %[rorig], #4\n" ++ "add %[rtemp], %[rtemp], #4\n" ++ "subs %[count], %[count], #1\n" ++ "bgt 1b\n" ++ "b 3f\n" ++ "2:b 2b\n" ++ "3:\n" ++ : ++ [rorig]"+r" (orig), ++ [rtemp]"+r" (temp), ++ [count]"+r" (count) ++ : ++ : "r3", "r4", "cc", "memory" ++ ); ++#endif ++ idx++; ++ } ++ context->archdata.cpu_resume_restore_nosave( ++ context->archdata.nosave_backup_phys, ++ context->archdata.nosave_begin_phys, ++ context->archdata.nosave_end_phys); ++} ++ ++static struct swap_map_page *meta_map; ++static u64 meta_map_next; ++static u64 meta_map_curr; ++static u64 meta_map_start; ++static int meta_idx; ++ ++static int raw_page_init(u64 start) ++{ ++ meta_map = malloc(PAGE_SIZE); ++ if (!meta_map) ++ return -1; ++ meta_map_next = 0; ++ meta_map_curr = 0; ++ meta_map_start = start; ++ return 0; ++} ++ ++static void raw_page_start(void) ++{ ++ meta_idx = ARRAY_SIZE(meta_map->entries); ++ meta_map_next = meta_map_start; ++} ++ ++static int raw_page_get_next(void *buffer) ++{ ++ if (meta_idx == ARRAY_SIZE(meta_map->entries)) { ++ if (!meta_map_next) ++ return 0; ++ if (meta_map_curr != meta_map_next) { ++#ifdef PAGEMAP_DEBUG ++ PAGEMAP_INFO("META: %d (%08x)\n", (int)meta_map_next, ++ (unsigned int)(meta_map_next ++ * PAGE_SIZE)); ++#endif ++ if (page_read_mem(meta_map_next, meta_map)) ++ return -1; ++ meta_map_curr = meta_map_next; ++ meta_map_next = meta_map->next_swap; ++ } ++ meta_idx = 0; ++ } ++#ifdef PAGEMAP_DEBUG ++ { ++ static unsigned int pre; ++ if ((pre + 1) != meta_map->entries[meta_idx]) { ++ PAGEMAP_INFO("DATA-Skiped: %d->%d (%08x->%08x)\n", ++ pre, (unsigned int)meta_map->entries[meta_idx], ++ pre * PAGE_SIZE, ++ (unsigned int)(meta_map->entries[meta_idx] ++ * PAGE_SIZE)); ++ } ++ pre = meta_map->entries[meta_idx]; ++ _last_read_pages = pre; ++ } ++#endif ++ if (page_read_mem(meta_map->entries[meta_idx++], buffer)) ++ return -1; ++ ++ return 1; ++} ++ ++static void raw_page_exit(void) ++{ ++ free(meta_map); ++ meta_map = NULL; ++} ++ ++static int image_pages_avail; ++static unsigned char *unc_buf, *cmp_buf; ++static int unc_offset; ++ ++static int image_page_init(int compressed) ++{ ++ if (!compressed) ++ return 1; ++ ++ unc_buf = malloc(LZO_UNC_SIZE); ++ cmp_buf = malloc(LZO_CMP_SIZE); ++ if (!unc_buf || !cmp_buf) { ++ printf("not enogh memory\n"); ++ return 1; ++ } ++ return 0; ++} ++ ++static void image_page_start(void) ++{ ++ image_pages_avail = 0; ++} ++ ++static int image_page_get_next(void *buffer) ++{ ++#ifdef CONFIG_LZO ++ if (!image_pages_avail) { ++ int ret; ++ size_t unc_len, cmp_len, cmp_avail; ++ ++ ret = raw_page_get_next(cmp_buf); ++ if (ret <= 0) ++ return ret; ++ ++ cmp_len = *(size_t *) cmp_buf; ++ cmp_avail = PAGE_SIZE; ++ ++ while (cmp_avail < cmp_len + LZO_HEADER) { ++ ret = raw_page_get_next(cmp_buf + cmp_avail); ++ if (unlikely(ret <= 0)) ++ return ret; ++ cmp_avail += PAGE_SIZE; ++ } ++ ++ unc_len = LZO_UNC_SIZE; ++ ret = lzo1x_decompress_safe(cmp_buf + LZO_HEADER, ++ cmp_len, unc_buf, &unc_len); ++ if (unlikely(ret != LZO_E_OK)) { ++ printf("Decompression failure: %d," ++ " cmp_buf = %p," ++ " cmp_len = %d, unc_len = %d\n", ++ ret, cmp_buf + LZO_HEADER, cmp_len, ++ unc_len); ++ return ret; ++ } ++ image_pages_avail = unc_len / PAGE_SIZE; ++ unc_offset = 0; ++ } ++ ++ memcpy(buffer, unc_buf + unc_offset, PAGE_SIZE); ++ unc_offset += PAGE_SIZE; ++ image_pages_avail--; ++ return 1; ++#else ++ printf("No LZO support in u-boot.\n"); ++ return -1; ++#endif ++} ++ ++static void image_page_exit(void) ++{ ++ free(unc_buf); ++ free(cmp_buf); ++ unc_buf = cmp_buf = NULL; ++} ++ ++static void bitmap_set(u32 *bm, unsigned int bit) ++{ ++ bm[bit >> 5] |= (1 << (bit & 0x1f)); ++} ++ ++static int bitmap_is_set(u32 *bm, unsigned int bit) ++{ ++ return !!(bm[bit >> 5] & (1 << (bit & 0x1f))); ++} ++ ++static u32 *used_bitmap; ++static u32 next_free_page; ++static u32 total_pages; ++ ++static int free_page_init(void) ++{ ++ total_pages = (u32)((TOP_ADDRESS - ++ LOW_BOTTOM) / PAGE_SIZE); /* 2GB */ ++ used_bitmap = malloc(total_pages * sizeof(u32) / 32); ++ if (!used_bitmap) ++ return -1; ++ return 0; ++} ++ ++static void free_page_start(int offset) ++{ ++ memset(used_bitmap, 0, total_pages * sizeof(u32) / 32); ++ next_free_page = pg_ub2zero(offset); ++} ++ ++static void free_page_mark_used(u32 page); ++/* Returns full-address based pages */ ++static int free_page_get_next(void) ++{ ++ while (bitmap_is_set(used_bitmap, next_free_page)) ++ next_free_page++; ++ free_page_mark_used(next_free_page); ++ return pg_zero2ub(next_free_page++); ++} ++ ++static void free_page_mark_used(u32 page) ++{ ++ bitmap_set(used_bitmap, page); ++} ++ ++static void free_page_exit(void) ++{ ++ free(used_bitmap); ++ used_bitmap = NULL; ++} ++ ++void set_swsuspmem_hook(struct swsuspmem_hook *hook) ++{ ++ _hook = hook; ++} ++ ++/* ++ * rtn = 1 : Hibernation image OK. ++ * rtn = 0 : Hibernation image NG. ++ * */ ++int do_checksnapimage(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ __u32 offset = 0; ++ void *spare_page = NULL; ++ struct swsusp_header *swsusp_header; ++ CRC32_WORD4_t calc_crc; ++ ++ /* Address hack */ ++ if (argc > 1) { ++ char *ep; ++ offt_addr = (void *)simple_strtoul(argv[1], &ep, 16); ++ if (*ep) { ++ printf("Invalid address\n"); ++ return 0; ++ } ++ } ++ ++ spare_page = malloc(PAGE_SIZE); ++ if (!spare_page) ++ goto mem_err; ++ ++ swsusp_header = spare_page; ++ if (page_read_mem(offset, swsusp_header)) ++ goto read_err; ++ ++#ifdef SWSUSP_DEBUG_INFO ++ PAGEMAP_INFO("swssp_header:%x\n", swsusp_header); ++ PAGEMAP_INFO(" comp_crc: <snip>\n"); ++ PAGEMAP_INFO(" img_size: %d\n", swsusp_header->img_size); ++ PAGEMAP_INFO(" image(swap firest sector): %08x\n", ++ (unsigned int)swsusp_header->image); ++ PAGEMAP_INFO(" flags: %08x\n", swsusp_header->flags); ++ PAGEMAP_INFO(" orig_sig:%s\n", swsusp_header->orig_sig); ++ PAGEMAP_INFO(" sig:%s\n", swsusp_header->sig); ++#endif /* SWSUSP_DEBUG_INFO */ ++ ++ if (memcmp(HIBERNATE_SIG, swsusp_header->sig, 10) ++ || (swsusp_header->img_size == 0) ++ || (swsusp_header->img_size > 0x03fff000)) { ++ printf("No hibernation image present\n"); ++ CALL_HOOK(err_hook, SWSUSPMEM_BROKENIMAGE); ++ return 0; ++ } ++ memset(&calc_crc, 0, sizeof(calc_crc)); ++ ++ calc_crc32x4((u8 *)((unsigned long)offt_addr + PAGE_SIZE), ++ swsusp_header->img_size, &calc_crc); ++ ++ if (memcmp(&calc_crc, &swsusp_header->comp_crc32, ++ sizeof(CRC32_WORD4_t))) { ++ printf("Bad CRC for image, image: %08x:%08x:" ++ "%08x:%08x, calc: %08x:%08x:%08x:%08x\n", ++ swsusp_header->comp_crc32.crc_w[0], ++ swsusp_header->comp_crc32.crc_w[1], ++ swsusp_header->comp_crc32.crc_w[2], ++ swsusp_header->comp_crc32.crc_w[3], ++ calc_crc.crc_w[0], calc_crc.crc_w[1], ++ calc_crc.crc_w[2], calc_crc.crc_w[3]); ++ CALL_HOOK(err_hook, SWSUSPMEM_BROKENIMAGE); ++ return 0; ++ } ++ free(spare_page); ++ printf("Hibernation image OK!.\n"); ++ ++ return 1; ++ ++mem_err: ++ printf("Not enough memory.\n"); ++ CALL_HOOK(err_hook, SWSUSPMEM_ENOMEM); ++ goto err; ++ ++read_err: ++ printf("Read error while restoring image.\n"); ++ ++err: ++ __asm__ volatile ( ++ "mov r0, #0\n" ++ "mcr p15, 0, r0, c7, c5, 0 @ invalidate icache\n" ++ "mcr p15, 0, r0, c7, c10, 4 @ DSB\n" ++ "mcr p15, 0, r0, c7, c5, 4 @ ISB\n" ++ : : : "r0", "memory"); ++ ++ free(spare_page); ++ ++ CALL_HOOK(err_hook, SWSUSPMEM_RESTOREFAIL); ++ return 0; ++} ++ ++U_BOOT_CMD(checksnapimage, 2, 2, do_checksnapimage, ++ "Check hibernation image data from memory", ++ "<address>]" ++); ++ ++int do_swsuspmem(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ __u32 offset = 0; ++ void *spare_page = NULL; ++ struct swsusp_header *swsusp_header; ++ struct swsusp_info *swsusp_info; ++ struct swsusp_finish_context *context; ++ int max_page; ++ int i; ++ u32 nr_pfn_pages; ++ u32 **pfn_pages = NULL; ++ u32 *remap_orig_page; ++ u32 *remap_temp_page; ++ u32 **remap_orig; ++ u32 **remap_temp; ++ int remap_idx; ++ void (*swsusp_finish_copy)(void *); ++ char *data_page; ++ char *stack_addr; ++ CRC32_WORD4_t calc_crc; ++ int high_page; ++ ++#ifdef PAGEMAP_DEBUG ++ int high_page_images = 0; ++ int total_remap = 0; ++ if (getenv("hybdebug") != NULL) ++ debugout = 1; ++#endif ++ /* Address hack */ ++ void *swsusp_finish_p = (void *)((u32)swsusp_finish & ~0x1); ++ if (argc > 1) { ++ char *ep; ++ offt_addr = (void *)simple_strtoul(argv[1], &ep, 16); ++ if (*ep) { ++ printf("Invalid address\n"); ++ return 1; ++ } ++ } ++ ++ /* Allow for 16 pages of stack */ ++ max_page = gd->start_addr_sp / PAGE_SIZE - 32; ++ high_page = (((gd->relocaddr + _bss_end_ofs) ++ + (PAGE_SIZE - 1)) / PAGE_SIZE) + 1; ++#define pfn_is_occupied(pfn) (page > max_page && page <= high_page) ++#ifdef PAGEMAP_DEBUG ++ PAGEMAP_INFO(" *gd->start_addr_sp:%p\n", ++ (void *)gd->start_addr_sp); ++ PAGEMAP_INFO(" *gd->relocaddr:%p\n", ++ (void *)gd->relocaddr); ++ PAGEMAP_INFO(" *bss_start_offset:%d bss_end_offset:%d\n", ++ (int)_bss_start_ofs, (int)_bss_end_ofs); ++ PAGEMAP_INFO(" UBOOT own memory [%p-%p]\n", ++ pg2addr(max_page), pg2addr(high_page)); ++#endif ++ if (free_page_init()) ++ goto mem_err; ++ free_page_start(exclude_max_page + 1); ++ ++ spare_page = malloc(PAGE_SIZE); ++ if (!spare_page) ++ goto mem_err; ++ ++ swsusp_header = spare_page; ++ if (page_read_mem(offset, swsusp_header)) ++ goto read_err; ++ ++#ifdef SWSUSP_DEBUG_INFO ++ PAGEMAP_INFO("swssp_header:\n"); ++ PAGEMAP_INFO(" comp_crc: <snip>\n"); ++ PAGEMAP_INFO(" img_size: %d\n", swsusp_header->img_size); ++ PAGEMAP_INFO(" image(swap firest sector): %08x\n", ++ (unsigned int)swsusp_header->image); ++ PAGEMAP_INFO(" flags: %08x\n", swsusp_header->flags); ++ PAGEMAP_INFO(" orig_sig:%s\n", swsusp_header->orig_sig); ++ PAGEMAP_INFO(" sig:%s\n", swsusp_header->sig); ++#endif /* SWSUSP_DEBUG_INFO */ ++ ++ if (memcmp(HIBERNATE_SIG, swsusp_header->sig, 10) ++ || (swsusp_header->img_size == 0) ++ || (swsusp_header->img_size > 0x03fff000)) { ++ printf("No hibernation image present\n"); ++ CALL_HOOK(err_hook, SWSUSPMEM_BROKENIMAGE); ++ return 0; ++ } ++ memset(&calc_crc, 0, sizeof(calc_crc)); ++ ++ calc_crc32x4((u8 *)((unsigned long)offt_addr + PAGE_SIZE), ++ swsusp_header->img_size, &calc_crc); ++ ++ if (memcmp(&calc_crc, &swsusp_header->comp_crc32, ++ sizeof(CRC32_WORD4_t))) { ++ printf("Bad CRC for image, image: %08x:%08x:" ++ "%08x:%08x, calc: %08x:%08x:%08x:%08x\n", ++ swsusp_header->comp_crc32.crc_w[0], ++ swsusp_header->comp_crc32.crc_w[1], ++ swsusp_header->comp_crc32.crc_w[2], ++ swsusp_header->comp_crc32.crc_w[3], ++ calc_crc.crc_w[0], calc_crc.crc_w[1], ++ calc_crc.crc_w[2], calc_crc.crc_w[3]); ++ CALL_HOOK(err_hook, SWSUSPMEM_BROKENIMAGE); ++ return 0; ++ } ++ ++ /* Overwrite header if necessary */ ++#ifndef SWSUSP_KEEP_IMAGE ++ if (memcmp(swsusp_header->sig, swsusp_header->orig_sig, 10)) { ++ memcpy(swsusp_header->sig, swsusp_header->orig_sig, 10); ++ if (page_write_mem(offset, swsusp_header)) ++ printf("Write error resetting header\n"); ++ } ++#endif ++ ++ if (raw_page_init(swsusp_header->image)) ++ goto mem_err; ++ raw_page_start(); ++ ++ if (image_page_init(!(swsusp_header->flags & SF_NOCOMPRESS_MODE))) ++ goto mem_err; ++ image_page_start(); ++ ++ swsusp_info = spare_page; ++ if (raw_page_get_next(swsusp_info) <= 0) ++ goto read_err; ++ ++#ifdef SWSUSP_DEBUG_INFO ++ PAGEMAP_INFO("swsup_info:\n"); ++ PAGEMAP_INFO(" utsname.sysname:%s\n", ++ swsusp_info->uts.sysname); ++ PAGEMAP_INFO(" nodename:%s\n", ++ swsusp_info->uts.nodename); ++ PAGEMAP_INFO(" release:%s\n", ++ swsusp_info->uts.release); ++ PAGEMAP_INFO(" version:%s\n", ++ swsusp_info->uts.version); ++ PAGEMAP_INFO(" machine:%s\n", ++ swsusp_info->uts.machine); ++ PAGEMAP_INFO(" vesion_code:%#08x\n", ++ (unsigned int)swsusp_info->version_code); ++ PAGEMAP_INFO(" num_physpages:%d\n", ++ (unsigned int)swsusp_info->num_physpages); ++ PAGEMAP_INFO(" pages :%d\n", ++ (unsigned int)swsusp_info->pages); ++ PAGEMAP_INFO(" size :%d\n", ++ (unsigned int)swsusp_info->size); ++#endif ++ ++ nr_pfn_pages = (swsusp_info->image_pages * 4 + PAGE_SIZE - 1) / ++ PAGE_SIZE; ++ pfn_pages = malloc(nr_pfn_pages * sizeof(u32 *)); ++ if (!pfn_pages) ++ goto mem_err; ++ memset(pfn_pages, 0, nr_pfn_pages * sizeof(u32 *)); ++ ++ /* UBOOT using memory */ ++ for (i = max_page; i <= high_page; i++) ++ free_page_mark_used(pg_ub2zero(i)); ++ ++ printf("Allocating %u bytes (nr_pfn_pages %u)\n", ++ nr_pfn_pages * PAGE_SIZE, nr_pfn_pages); ++ ++ for (i = 0; i < nr_pfn_pages; i++) { ++ u32 idx; ++ pfn_pages[i] = malloc(PAGE_SIZE); ++ memset(pfn_pages[i], 0xff, PAGE_SIZE); ++ if (unlikely(!pfn_pages[i])) ++ goto mem_err; ++ if (unlikely(image_page_get_next(pfn_pages[i]) <= 0)) ++ goto read_err; ++ for (idx = 0; idx < PAGE_SIZE / sizeof(u32); idx++) { ++ u32 page = pfn_pages[i][idx]; ++ if (page == ~0UL) /* End of list of pages */ ++ break; ++ free_page_mark_used(pg_ub2zero(page)); ++ } ++ } ++ ++ printf("Loading image data pages (%lu pages)\n", ++ swsusp_info->image_pages); ++ ++ remap_orig_page = pg2addr(free_page_get_next()); ++ remap_temp_page = pg2addr(free_page_get_next()); ++ ++ remap_orig = (u32 **)remap_orig_page; ++ remap_temp = (u32 **)remap_temp_page; ++ remap_idx = 0; ++ ++ for (i = 0; i < swsusp_info->image_pages; i++) { ++ u32 page = pfn_pages[i >> 10][i & 0x3ff]; ++ if (unlikely(!pfn_is_valid(page))) { ++ printf("Attempt to restore invalid address %llx\n", ++ pg2phys(page)); ++ continue; ++ } else if (unlikely(pfn_is_excluded(page))) { ++ printf("Attempt to restore excluded address %llx\n", ++ pg2phys(page)); ++ continue; ++ } else if (unlikely(pfn_is_low(page) && ++ pfn_is_occupied(page))) { ++ remap_orig[remap_idx] = pg2addr(page); ++ page = free_page_get_next(); ++ remap_temp[remap_idx] = pg2addr(page); ++ remap_idx++; ++#ifdef PAGEMAP_DEBUG ++ ++total_remap; ++#endif ++ /* If we fill our current page, allocate a new one */ ++ if (remap_idx + 1 == PAGE_SIZE / sizeof(u32)) { ++ u32 *next; ++ ++ next = pg2addr(free_page_get_next()); ++ remap_orig[remap_idx] = next; ++ remap_orig = (u32 **)next; ++ ++ next = pg2addr(free_page_get_next()); ++ remap_temp[remap_idx] = next; ++ remap_temp = (u32 **)next; ++ ++ remap_idx = 0; ++ } ++ } ++ if (image_page_get_next(pg2addr(page)) <= 0) ++ goto read_err; ++ } ++ ++ printf("Image loading done.\n"); ++ invalidate_icache_all(); ++ ++ CALL_HOOK(resume_boot, SWSUSPMEM_IMAGEDONE); ++ /* put end markers on the remap list */ ++ remap_orig[remap_idx] = (void *) ~0UL; ++ remap_temp[remap_idx] = (void *) ~0UL; ++ ++#ifdef PAGEMAP_DEBUG ++ PAGEMAP_INFO("Number of remap pages:%d\n", total_remap); ++ PAGEMAP_INFO("Number of high pages:%d\n", high_page_images); ++ PAGEMAP_INFO("Last read page %d (%08x)\n", ++ _last_read_pages, _last_read_pages * PAGE_SIZE); ++#endif ++ remap_orig = (u32 **)remap_orig_page; ++ remap_temp = (u32 **)remap_temp_page; ++ ++ /* Make a copy of swsusp_finish in a free data page */ ++ data_page = pg2addr(free_page_get_next()); ++ memcpy(data_page, swsusp_finish_p, PAGE_SIZE); ++ swsusp_finish_copy = (void *) data_page; ++ ++ /* Setup context for swsusp_finish at the end of the data_page */ ++ context = (struct swsusp_finish_context *) (data_page + PAGE_SIZE - ++ sizeof(struct swsusp_finish_context)); ++ context->remap_orig_page = remap_orig_page; ++ context->remap_temp_page = remap_temp_page; ++ memcpy((void *)&context->archdata, (void *)swsusp_info->archdata, ++ sizeof(struct swsusp_archdata)); ++ ++ /* Get a stack pointer for swsusp_finish, growing down from context */ ++ stack_addr = (char *) context; ++ ++#ifdef CONFIG_NETCONSOLE ++ /* ++ * Stop the ethernet stack if NetConsole could have ++ * left it up ++ */ ++ eth_halt(); ++#endif ++#ifdef CONFIG_USB_DEVICE ++ udc_disconnect(); ++#endif ++ arch_preboot_os(); ++ cleanup_before_linux(); ++ ++ CALL_HOOK(resume_boot, SWSUSPMEM_RESUME); ++ /* Copy the final data from a safe place */ ++ call_with_stack(swsusp_finish_copy, context, stack_addr); ++ ++ return 0; ++ ++mem_err: ++ printf("Not enough memory.\n"); ++ CALL_HOOK(err_hook, SWSUSPMEM_ENOMEM); ++ goto err; ++ ++read_err: ++ printf("Read error while restoring image.\n"); ++ ++err: ++ __asm__ volatile ( ++ "mov r0, #0\n" ++ "mcr p15, 0, r0, c7, c5, 0 @ invalidate icache\n" ++ "mcr p15, 0, r0, c7, c10, 4 @ DSB\n" ++ "mcr p15, 0, r0, c7, c5, 4 @ ISB\n" ++ : : : "r0", "memory"); ++ ++ raw_page_exit(); ++ image_page_exit(); ++ free_page_exit(); ++ if (pfn_pages) { ++ for (i = 0; i < nr_pfn_pages; i++) ++ free(pfn_pages[i]); ++ free(pfn_pages); ++ } ++ free(spare_page); ++ ++ CALL_HOOK(err_hook, SWSUSPMEM_RESTOREFAIL); ++ return 1; ++} ++ ++U_BOOT_CMD(swsuspmem, 2, 2, do_swsuspmem, ++ "Restore SWSUSP hibernation image from memory", ++ "<address>]" ++); +diff --git a/include/swsuspmem.h b/include/swsuspmem.h +new file mode 100644 +index 0000000..3b353ea +--- /dev/null ++++ b/include/swsuspmem.h +@@ -0,0 +1,24 @@ ++#ifndef _SWSUSPMEM_H_ ++#define _SWSUSPMEM_H_ ++ ++enum { SWSUSPMEM_NORM = 0, ++ SWSUSPMEM_NOIMAGE = 0x01, ++ SWSUSPMEM_BROKENIMAGE = 0x02, ++ SWSUSPMEM_ENOMEM = 0x80, ++ SWSUSPMEM_RESTOREFAIL = 0x81, ++}; ++ ++enum { SWSUSPMEM_IMAGEDONE = 0x01, ++ SWSUSPMEM_RESUME = 0x02 ++}; ++ ++struct swsuspmem_hook { ++ void (*err_hook)(int errcode); ++ void (*resume_boot)(int param); ++}; ++ ++void set_swsuspmem_hook(struct swsuspmem_hook *hook); ++void arch_preboot_os(void); ++void call_with_stack(void (*fn)(void *), ++ void *userdata, void *stack); ++#endif +diff --git a/lib/lzo/lzo1x_decompress.c b/lib/lzo/lzo1x_decompress.c +index e6ff708..ebdf10b 100644 +--- a/lib/lzo/lzo1x_decompress.c ++++ b/lib/lzo/lzo1x_decompress.c +@@ -68,13 +68,14 @@ int lzop_decompress(const unsigned char *src, size_t src_len, + unsigned char *start = dst; + const unsigned char *send = src + src_len; + u32 slen, dlen; +- size_t tmp; ++ size_t tmp, remaining; + int r; + + src = parse_header(src); + if (!src) + return LZO_E_ERROR; + ++ remaining = *dst_len; + while (src < send) { + /* read uncompressed block size */ + dlen = get_unaligned_be32(src); +@@ -93,18 +94,25 @@ int lzop_decompress(const unsigned char *src, size_t src_len, + if (slen <= 0 || slen > dlen) + return LZO_E_ERROR; + ++ /* abort if buffer ran out of room */ ++ if (dlen > remaining) ++ return LZO_E_OUTPUT_OVERRUN; ++ + /* decompress */ + tmp = dlen; + r = lzo1x_decompress_safe((u8 *) src, slen, dst, &tmp); + +- if (r != LZO_E_OK) ++ if (r != LZO_E_OK) { ++ *dst_len = dst - start; + return r; ++ } + + if (dlen != tmp) + return LZO_E_ERROR; + + src += slen; + dst += dlen; ++ remaining -= dlen; + } + + return LZO_E_INPUT_OVERRUN; +-- +1.8.3.1 + diff --git a/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot/hibernation/0004-Add-porter-board-Hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot/hibernation/0004-Add-porter-board-Hibernation-code.patch new file mode 100755 index 000000000..1b2278b2e --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot/hibernation/0004-Add-porter-board-Hibernation-code.patch @@ -0,0 +1,1352 @@ +From 80d5c1269bd16fedce41611e45f25d156425b0c9 Mon Sep 17 00:00:00 2001 +From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com> +Date: Fri, 19 May 2017 16:16:18 +0900 +Subject: [PATCH 4/4] Add porter board Hibernation code + +Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com> +--- + arch/arm/cpu/armv7/Makefile | 4 + + arch/arm/cpu/armv7/arch_timer.c | 58 ++++++ + arch/arm/cpu/armv7/cache_v7.c | 14 +- + arch/arm/cpu/armv7/rmobile/Makefile | 4 + + arch/arm/cpu/armv7/rmobile/arm_arch_timer.c | 61 ++++++ + arch/arm/cpu/armv7/rmobile/crc32_word4.c | 299 ++++++++++++++++++++++++++++ + arch/arm/cpu/armv7/rmobile/crc32_word4.h | 23 +++ + arch/arm/cpu/armv7/rmobile/sh_timer.c | 209 +++++++++++++++++++ + arch/arm/include/asm/arch-rmobile/rmobile.h | 2 + + arch/arm/include/asm/armv7.h | 16 +- + arch/arm/include/asm/system.h | 136 ++++++++++++- + arch/arm/lib/Makefile | 2 + + arch/arm/lib/board.c | 2 +- + arch/arm/lib/cache-cp15.c | 123 +++++++++++- + arch/arm/lib/call_with_stack.S | 20 ++ + board/renesas/porter/porter.c | 10 + + include/configs/porter.h | 19 +- + 17 files changed, 977 insertions(+), 25 deletions(-) + create mode 100644 arch/arm/cpu/armv7/arch_timer.c + create mode 100644 arch/arm/cpu/armv7/rmobile/arm_arch_timer.c + create mode 100644 arch/arm/cpu/armv7/rmobile/crc32_word4.c + create mode 100644 arch/arm/cpu/armv7/rmobile/crc32_word4.h + create mode 100644 arch/arm/cpu/armv7/rmobile/sh_timer.c + create mode 100644 arch/arm/lib/call_with_stack.S + +diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile +index 4fdbee4..f68ce5c 100644 +--- a/arch/arm/cpu/armv7/Makefile ++++ b/arch/arm/cpu/armv7/Makefile +@@ -32,6 +32,10 @@ COBJS += cache_v7.o + COBJS += cpu.o + COBJS += syslib.o + ++ifneq ($(CONFIG_SYS_ARCH_TIMER),) ++COBJS += arch_timer.o ++endif ++ + ifneq ($(CONFIG_AM33XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX)$(CONFIG_TEGRA20),) + SOBJS += lowlevel_init.o + endif +diff --git a/arch/arm/cpu/armv7/arch_timer.c b/arch/arm/cpu/armv7/arch_timer.c +new file mode 100644 +index 0000000..747b6e9 +--- /dev/null ++++ b/arch/arm/cpu/armv7/arch_timer.c +@@ -0,0 +1,58 @@ ++/* ++ * (C) Copyright 2012-2014 ++ * Texas Instruments Incorporated, <www.ti.com> ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include <common.h> ++#include <asm/io.h> ++#include <div64.h> ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++int timer_init(void) ++{ ++ gd->tbl = 0; ++ gd->tbu = 0; ++ ++ gd->timer_rate_hz = CONFIG_SYS_HZ_CLOCK / CONFIG_SYS_HZ; ++ ++ return 0; ++} ++ ++unsigned long long get_ticks(void) ++{ ++ ulong nowl, nowu; ++ ++ asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (nowl), "=r" (nowu)); ++ ++ gd->tbl = nowl; ++ gd->tbu = nowu; ++ ++ return (((unsigned long long)gd->tbu) << 32) | gd->tbl; ++} ++ ++ ++ulong get_timer(ulong base) ++{ ++ return lldiv(get_ticks(), gd->timer_rate_hz) - base; ++} ++ ++void __udelay(unsigned long usec) ++{ ++ unsigned long long endtime; ++ ++ endtime = lldiv((unsigned long long)usec * gd->timer_rate_hz, ++ 1000UL); ++ ++ endtime += get_ticks(); ++ ++ while (get_ticks() < endtime) ++ ; ++} ++ ++ulong get_tbclk(void) ++{ ++ return gd->timer_rate_hz; ++} +diff --git a/arch/arm/cpu/armv7/cache_v7.c b/arch/arm/cpu/armv7/cache_v7.c +index 5f6d039..5a0bdb8 100644 +--- a/arch/arm/cpu/armv7/cache_v7.c ++++ b/arch/arm/cpu/armv7/cache_v7.c +@@ -82,7 +82,7 @@ static void v7_inval_dcache_level_setway(u32 level, u32 num_sets, + } + } + /* DSB to make sure the operation is complete */ +- CP15DSB; ++ DSB(); + } + + static void v7_clean_inval_dcache_level_setway(u32 level, u32 num_sets, +@@ -109,7 +109,7 @@ static void v7_clean_inval_dcache_level_setway(u32 level, u32 num_sets, + } + } + /* DSB to make sure the operation is complete */ +- CP15DSB; ++ DSB(); + } + + static void v7_maint_dcache_level_setway(u32 level, u32 operation) +@@ -230,7 +230,7 @@ static void v7_dcache_maint_range(u32 start, u32 stop, u32 range_op) + } + + /* DSB to make sure the operation is complete */ +- CP15DSB; ++ DSB(); + } + + /* Invalidate TLB */ +@@ -243,9 +243,9 @@ static void v7_inval_tlb(void) + /* Invalidate entire instruction TLB */ + asm volatile ("mcr p15, 0, %0, c8, c5, 0" : : "r" (0)); + /* Full system DSB - make sure that the invalidation is complete */ +- CP15DSB; ++ DSB(); + /* Full system ISB - make sure the instruction stream sees it */ +- CP15ISB; ++ ISB(); + } + + void invalidate_dcache_all(void) +@@ -356,10 +356,10 @@ void invalidate_icache_all(void) + asm volatile ("mcr p15, 0, %0, c7, c5, 6" : : "r" (0)); + + /* Full system DSB - make sure that the invalidation is complete */ +- CP15DSB; ++ DSB(); + + /* ISB - make sure the instruction stream sees it */ +- CP15ISB; ++ ISB(); + } + #else + void invalidate_icache_all(void) +diff --git a/arch/arm/cpu/armv7/rmobile/Makefile b/arch/arm/cpu/armv7/rmobile/Makefile +index b8c04c6..0a3623a 100644 +--- a/arch/arm/cpu/armv7/rmobile/Makefile ++++ b/arch/arm/cpu/armv7/rmobile/Makefile +@@ -46,6 +46,10 @@ COBJS-$(CONFIG_R8A7740) += pfc-r8a7740.o + COBJS-$(CONFIG_SH73A0) += cpu_info-sh73a0.o + COBJS-$(CONFIG_SH73A0) += pfc-sh73a0.o + COBJS_LN-$(CONFIG_TMU_TIMER) += sh_timer.o ++COBJS-$(CONFIG_SYS_ARCH_TIMER) += arm_arch_timer.o ++ifeq ($(CONFIG_CMD_SWSUSP),y) ++COBJS-y += crc32_word4.o ++endif + + COBJS := $(COBJS-y) + SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +diff --git a/arch/arm/cpu/armv7/rmobile/arm_arch_timer.c b/arch/arm/cpu/armv7/rmobile/arm_arch_timer.c +new file mode 100644 +index 0000000..a499e71 +--- /dev/null ++++ b/arch/arm/cpu/armv7/rmobile/arm_arch_timer.c +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (C) 2014 Cogent Embedded Inc. ++ * ++ * Licensed under the GPL-2 or later. ++ */ ++ ++#include <common.h> ++#include <asm/io.h> ++ ++#define MODEMR 0xe6160060 ++#define MD(x) (1 << (x)) ++#define CNTCR 0xe6080000 ++#define CNTFID0 0xe6080020 ++ ++void arm_arch_timer_init(void) ++{ ++ u32 mode = readl(MODEMR); ++ u32 freq; ++ ++ switch (mode & (MD(14) | MD(13))) { ++ case MD(13): ++ freq = 20; ++ break; ++ case MD(14): ++ freq = 26; ++ break; ++ case MD(13) | MD(14): ++ freq = 30; ++ break; ++ default: ++ freq = 15; ++ break; ++ } ++ ++ freq *= (1000000 / 2); ++ ++#ifdef CONFIG_VE_ENABLED ++ /* CNTVOFF has to be initialized either from non-secure Hypervisor ++ * mode or secure Monitor mode with SCR.NS==1. If TrustZone is enabled ++ * then it should be handled by the secure code ++ */ ++ asm volatile( ++ " cps 0x16\n" ++ " mrc p15, 0, r1, c1, c1, 0\n" ++ " orr r0, r1, #1\n" ++ " mcr p15, 0, r0, c1, c1, 0\n" ++ " isb\n" ++ " mov r0, #0\n" ++ " mcrr p15, 4, r0, r0, c14\n" ++ " isb\n" ++ " mcr p15, 0, r1, c1, c1, 0\n" ++ " isb\n" ++ " cps 0x13\n" ++ : : : "r0", "r1"); ++#endif ++ ++ /* Start Generic ARM timer */ ++ writel(freq, CNTFID0); ++ asm volatile("mcr p15, 0, %0, c14, c0, 0" : : "r" (freq)); ++ writel(1, CNTCR); ++} +diff --git a/arch/arm/cpu/armv7/rmobile/crc32_word4.c b/arch/arm/cpu/armv7/rmobile/crc32_word4.c +new file mode 100644 +index 0000000..b813899 +--- /dev/null ++++ b/arch/arm/cpu/armv7/rmobile/crc32_word4.c +@@ -0,0 +1,299 @@ ++/************************************************************************* ++ * crc32_word4.c: rapid CRC32 ++ * Coptright (C) FUJITSUTEN Limited, 2015 All Rights Reserved. ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ *************************************************************************/ ++#ifdef OWNTEST ++#include <stdio.h> ++#include <stdlib.h> ++#include <string.h> ++#include <asm/types.h> ++typedef unsigned int u_int32_t; ++#else ++#include <common.h> ++#endif ++ ++#include "crc32_word4.h" ++ ++#define CRC_INIT_VALUE (-1) ++#define CRC_FIX(_crc32) (~(_crc32)) ++ ++#define __HWDTPLS_OUT() ++#define MEASURE(msg) ++ ++/**** calc_crc32.c *****/ ++ ++/* ++ * CRC32は、ISO 3309 で規程され ++ * そのサンプルは ++ * RFC 2083 :PNG(Poratble Network Graphics ++ * で公になっています。本プログラムは、RFC2083 で掲示された ++ * CRC32を独自に最適化したプログラムです。 ++ */ ++const static u_int32_t CRC_Table[256] = { ++ 0x00000000 , 0x77073096 , 0xee0e612c , 0x990951ba , 0x076dc419 , 0x706af48f , 0xe963a535 , 0x9e6495a3 , ++ 0x0edb8832 , 0x79dcb8a4 , 0xe0d5e91e , 0x97d2d988 , 0x09b64c2b , 0x7eb17cbd , 0xe7b82d07 , 0x90bf1d91 , ++ 0x1db71064 , 0x6ab020f2 , 0xf3b97148 , 0x84be41de , 0x1adad47d , 0x6ddde4eb , 0xf4d4b551 , 0x83d385c7 , ++ 0x136c9856 , 0x646ba8c0 , 0xfd62f97a , 0x8a65c9ec , 0x14015c4f , 0x63066cd9 , 0xfa0f3d63 , 0x8d080df5 , ++ 0x3b6e20c8 , 0x4c69105e , 0xd56041e4 , 0xa2677172 , 0x3c03e4d1 , 0x4b04d447 , 0xd20d85fd , 0xa50ab56b , ++ 0x35b5a8fa , 0x42b2986c , 0xdbbbc9d6 , 0xacbcf940 , 0x32d86ce3 , 0x45df5c75 , 0xdcd60dcf , 0xabd13d59 , ++ 0x26d930ac , 0x51de003a , 0xc8d75180 , 0xbfd06116 , 0x21b4f4b5 , 0x56b3c423 , 0xcfba9599 , 0xb8bda50f , ++ 0x2802b89e , 0x5f058808 , 0xc60cd9b2 , 0xb10be924 , 0x2f6f7c87 , 0x58684c11 , 0xc1611dab , 0xb6662d3d , ++ 0x76dc4190 , 0x01db7106 , 0x98d220bc , 0xefd5102a , 0x71b18589 , 0x06b6b51f , 0x9fbfe4a5 , 0xe8b8d433 , ++ 0x7807c9a2 , 0x0f00f934 , 0x9609a88e , 0xe10e9818 , 0x7f6a0dbb , 0x086d3d2d , 0x91646c97 , 0xe6635c01 , ++ 0x6b6b51f4 , 0x1c6c6162 , 0x856530d8 , 0xf262004e , 0x6c0695ed , 0x1b01a57b , 0x8208f4c1 , 0xf50fc457 , ++ 0x65b0d9c6 , 0x12b7e950 , 0x8bbeb8ea , 0xfcb9887c , 0x62dd1ddf , 0x15da2d49 , 0x8cd37cf3 , 0xfbd44c65 , ++ 0x4db26158 , 0x3ab551ce , 0xa3bc0074 , 0xd4bb30e2 , 0x4adfa541 , 0x3dd895d7 , 0xa4d1c46d , 0xd3d6f4fb , ++ 0x4369e96a , 0x346ed9fc , 0xad678846 , 0xda60b8d0 , 0x44042d73 , 0x33031de5 , 0xaa0a4c5f , 0xdd0d7cc9 , ++ 0x5005713c , 0x270241aa , 0xbe0b1010 , 0xc90c2086 , 0x5768b525 , 0x206f85b3 , 0xb966d409 , 0xce61e49f , ++ 0x5edef90e , 0x29d9c998 , 0xb0d09822 , 0xc7d7a8b4 , 0x59b33d17 , 0x2eb40d81 , 0xb7bd5c3b , 0xc0ba6cad , ++ 0xedb88320 , 0x9abfb3b6 , 0x03b6e20c , 0x74b1d29a , 0xead54739 , 0x9dd277af , 0x04db2615 , 0x73dc1683 , ++ 0xe3630b12 , 0x94643b84 , 0x0d6d6a3e , 0x7a6a5aa8 , 0xe40ecf0b , 0x9309ff9d , 0x0a00ae27 , 0x7d079eb1 , ++ 0xf00f9344 , 0x8708a3d2 , 0x1e01f268 , 0x6906c2fe , 0xf762575d , 0x806567cb , 0x196c3671 , 0x6e6b06e7 , ++ 0xfed41b76 , 0x89d32be0 , 0x10da7a5a , 0x67dd4acc , 0xf9b9df6f , 0x8ebeeff9 , 0x17b7be43 , 0x60b08ed5 , ++ 0xd6d6a3e8 , 0xa1d1937e , 0x38d8c2c4 , 0x4fdff252 , 0xd1bb67f1 , 0xa6bc5767 , 0x3fb506dd , 0x48b2364b , ++ 0xd80d2bda , 0xaf0a1b4c , 0x36034af6 , 0x41047a60 , 0xdf60efc3 , 0xa867df55 , 0x316e8eef , 0x4669be79 , ++ 0xcb61b38c , 0xbc66831a , 0x256fd2a0 , 0x5268e236 , 0xcc0c7795 , 0xbb0b4703 , 0x220216b9 , 0x5505262f , ++ 0xc5ba3bbe , 0xb2bd0b28 , 0x2bb45a92 , 0x5cb36a04 , 0xc2d7ffa7 , 0xb5d0cf31 , 0x2cd99e8b , 0x5bdeae1d , ++ 0x9b64c2b0 , 0xec63f226 , 0x756aa39c , 0x026d930a , 0x9c0906a9 , 0xeb0e363f , 0x72076785 , 0x05005713 , ++ 0x95bf4a82 , 0xe2b87a14 , 0x7bb12bae , 0x0cb61b38 , 0x92d28e9b , 0xe5d5be0d , 0x7cdcefb7 , 0x0bdbdf21 , ++ 0x86d3d2d4 , 0xf1d4e242 , 0x68ddb3f8 , 0x1fda836e , 0x81be16cd , 0xf6b9265b , 0x6fb077e1 , 0x18b74777 , ++ 0x88085ae6 , 0xff0f6a70 , 0x66063bca , 0x11010b5c , 0x8f659eff , 0xf862ae69 , 0x616bffd3 , 0x166ccf45 , ++ 0xa00ae278 , 0xd70dd2ee , 0x4e048354 , 0x3903b3c2 , 0xa7672661 , 0xd06016f7 , 0x4969474d , 0x3e6e77db , ++ 0xaed16a4a , 0xd9d65adc , 0x40df0b66 , 0x37d83bf0 , 0xa9bcae53 , 0xdebb9ec5 , 0x47b2cf7f , 0x30b5ffe9 , ++ 0xbdbdf21c , 0xcabac28a , 0x53b39330 , 0x24b4a3a6 , 0xbad03605 , 0xcdd70693 , 0x54de5729 , 0x23d967bf , ++ 0xb3667a2e , 0xc4614ab8 , 0x5d681b02 , 0x2a6f2b94 , 0xb40bbe37 , 0xc30c8ea1 , 0x5a05df1b , 0x2d02ef8d , ++}; ++ ++/*** ++ * CRC Table creater. ++ * ++void make_crc_table(void) { ++ u_int32_t c; ++ u_int32_t n, k; ++ for (n = 0; n < 256; n++) ++ { ++ c = (u_int32_t) n; ++ for (k = 0; k < 8; k++) ++ { ++ if (c & 1) ++ c = 0xedb88320L ^ (c >> 1); ++ else ++ c = c >> 1; ++ } ++ CRC_Table[n] = c; ++ } ++} ++***/ ++#define NEXT_PTR (4) ++ ++static __inline__ ++u_int32_t _update_crc(u_int32_t crc, unsigned char *buf, size_t len) ++{ ++ u_int32_t c = crc; ++ size_t n; ++ for (n = 0; n < len; n++) ++ c = CRC_Table[(c ^ buf[n]) & 0xff] ^ (c >> 8); ++ return c; ++} ++/********************************************************************* ++ * update_crc4x4()() ++ * calc_crc32() をベースに、4 ワード毎に個別に CRC32 を計算する方法 ++ * ++ * +0 +1 +2 +3 ++ * +0x00 AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD ++ * +0x04 EEEEEEEE FFFFFFFF 00000000 11111111 ++ * : : : : ++ * CRC32 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx ++ * ++ *********************************************************************/ ++ ++static __inline__ ++void update_crc4x4(u_int32_t crc[4], unsigned char *buf) ++{ ++ u_int32_t c1, c2, c3, c4; ++ u_int32_t *p = (void *)buf; ++ ++ c1 = crc[0] ^ p[0]; ++ c2 = crc[1] ^ p[1]; ++ c3 = crc[2] ^ p[2]; ++ c4 = crc[3] ^ p[3]; ++ ++ c1 = CRC_Table[c1 & 0xff] ^ (c1 >> 8); ++ c2 = CRC_Table[c2 & 0xff] ^ (c2 >> 8); ++ c3 = CRC_Table[c3 & 0xff] ^ (c3 >> 8); ++ c4 = CRC_Table[c4 & 0xff] ^ (c4 >> 8); ++ ++ c1 = CRC_Table[c1 & 0xff] ^ (c1 >> 8); ++ c2 = CRC_Table[c2 & 0xff] ^ (c2 >> 8); ++ c3 = CRC_Table[c3 & 0xff] ^ (c3 >> 8); ++ c4 = CRC_Table[c4 & 0xff] ^ (c4 >> 8); ++ ++ c1 = CRC_Table[c1 & 0xff] ^ (c1 >> 8); ++ c2 = CRC_Table[c2 & 0xff] ^ (c2 >> 8); ++ c3 = CRC_Table[c3 & 0xff] ^ (c3 >> 8); ++ c4 = CRC_Table[c4 & 0xff] ^ (c4 >> 8); ++ ++ c1 = CRC_Table[c1 & 0xff] ^ (c1 >> 8); ++ c2 = CRC_Table[c2 & 0xff] ^ (c2 >> 8); ++ c3 = CRC_Table[c3 & 0xff] ^ (c3 >> 8); ++ c4 = CRC_Table[c4 & 0xff] ^ (c4 >> 8); ++ ++ crc[0] = c1; ++ crc[1] = c2; ++ crc[2] = c3; ++ crc[3] = c4; ++} ++ ++ ++void calc_crc32x4(unsigned char *buf, size_t len, CRC32_WORD4_t *result) ++{ ++ unsigned int crc_tmp[4] = {CRC_INIT_VALUE, CRC_INIT_VALUE, CRC_INIT_VALUE, CRC_INIT_VALUE}; ++ u_int32_t i; ++ int res; ++ u_int32_t n4; ++ int xlen = len; ++#ifdef HWDPLS_ENABLE ++ unsigned long plstout = 60; ++ unsigned long plsstart = 0; ++ if ((unsigned long)CONFIG_SYS_HZ > 100000) ++ plstout *= (unsigned long)CONFIG_SYS_HZ / 1000; ++ else ++ plstout = DIV_ROUND_UP(plstout * (unsigned long)CONFIG_SYS_HZ, 1000); ++#endif ++ ++ /** ++ * 4バイト境界に合わない開始アドレスの場合 ++ * 境界までのCRCを crc_tmp[0] に求める。 ++ */ ++ if ((unsigned long)buf & 3) { ++ crc_tmp[0] = _update_crc(crc_tmp[0], buf, (unsigned long)buf & 3); ++ buf = (unsigned char *)((unsigned long)buf & ~3); ++ xlen -= (unsigned long)buf & 3; ++ } ++ ++ n4 = xlen/(NEXT_PTR*4); ++ /** ++ * 4バイト境界に合わない開始アドレスの場合 ++ * 境界までのCRCを crc_tmp[0] に求める。 ++ */ ++#ifdef HWDPLS_ENABLE ++ reset_timer(); ++ plsstart = get_timer(0); ++#endif ++ for (i = 0; i < n4 ; i++) { ++ update_crc4x4(crc_tmp, buf); ++ buf += NEXT_PTR * 4; ++#ifdef HWDPLS_ENABLE ++ /** ++ * WDを考慮 ++ */ ++ if (__builtin_expect((int)((i & 0x1f) == 0), 0)) { ++ if ((get_timer(plsstart)) > plstout) { ++ __HWDTPLS_OUT(); ++ MEASURE("crc plsout") ++ plsstart += plstout; ++ } ++ } ++#endif /*HWPLS_ENABLE*/ ++ } ++ ++ res = xlen % (NEXT_PTR * 4); ++ if (res > 0) ++ crc_tmp[3] = _update_crc(crc_tmp[3], buf, res); ++ ++ result->crc_w[0] = CRC_FIX(crc_tmp[0]); ++ result->crc_w[1] = CRC_FIX(crc_tmp[1]); ++ result->crc_w[2] = CRC_FIX(crc_tmp[2]); ++ result->crc_w[3] = CRC_FIX(crc_tmp[3]); ++ ++ MEASURE("calc_crc32x4 finish") ++} ++ ++#if defined(OWNTEST) ++#define BUFSIZE (2 * 1024 * 1024) ++#include <sys/time.h> ++#include <malloc.h> ++ ++int main() ++{ ++ unsigned char *buf, *buf2; ++ struct timeval start, end; ++ unsigned long long diff; ++ int i; ++ ++ CRC32_WORD4_t result = { .crc_w = {0, 0, 0, 0 } }; ++ CRC32_WORD4_t result2 = { .crc_w = {0, 0, 0, 0 } }; ++ ++ buf = malloc(BUFSIZE); ++ if (!buf) { ++ perror("malloc"); ++ return 1; ++ } ++ printf("Generate %dMB random data..\n", BUFSIZE / 1024 / 1024); ++ srand(0); ++ for (i = 0; i < BUFSIZE / 4; i++) ++ ((int *)buf)[i] = rand(); ++ ++ /* Memory dup */ ++ buf2 = memalign(NEXT_PTR, BUFSIZE); ++ if (!buf2) { ++ perror("malloc"); ++ return 1; ++ } ++ memcpy(buf2, buf, BUFSIZE); ++ ++ ++ gettimeofday(&start, NULL); ++ calc_crc32x4(buf, BUFSIZE, &result); ++ gettimeofday(&end, NULL); ++ ++ diff = (end.tv_sec - start.tv_sec) * 1000000; ++ diff += end.tv_usec - start.tv_usec; ++ ++ printf("time=%lluus\n", diff); ++ printf(" result.word[0] = %x\n", result.crc_w[0]); ++ printf(" result.word[1] = %x\n", result.crc_w[1]); ++ printf(" result.word[2] = %x\n", result.crc_w[2]); ++ printf(" result.word[3] = %x\n", result.crc_w[3]); ++ ++ /* Broken test */ ++#if 0 /* Destory test */ ++ buf[rand() % BUFSIZE] ^= 1 << (rand()%7); ++#endif ++ for (i = 0; i < BUFSIZE ; i++) { ++ if (buf[i] != buf2[i]) ++ printf("buf[%d] %02x : %02x\n", i, buf[i], buf2[i]); ++ } ++ ++ gettimeofday(&start, NULL); ++ calc_crc32x4(buf, BUFSIZE, &result2); ++ gettimeofday(&end, NULL); ++ ++ diff = (end.tv_sec - start.tv_sec) * 1000000; ++ diff += end.tv_usec - start.tv_usec; ++ ++ printf("time=%lluus\n", diff); ++ printf(" result.word[0] = %x:%s\n", result2.crc_w[0] , ++ result.crc_w[0] == result2.crc_w[0] ? "OK" : "NG"); ++ printf(" result.word[1] = %x:%s\n", result2.crc_w[1] , ++ result.crc_w[1] == result2.crc_w[1] ? "OK" : "NG"); ++ printf(" result.word[2] = %x:%s\n", result2.crc_w[2] , ++ result.crc_w[2] == result2.crc_w[2] ? "OK" : "NG"); ++ printf(" result.word[3] = %x:%s\n", result2.crc_w[3] , ++ result.crc_w[3] == result2.crc_w[3] ? "OK" : "NG"); ++ return 0; ++} ++#endif /* TEST */ +diff --git a/arch/arm/cpu/armv7/rmobile/crc32_word4.h b/arch/arm/cpu/armv7/rmobile/crc32_word4.h +new file mode 100644 +index 0000000..2b64218 +--- /dev/null ++++ b/arch/arm/cpu/armv7/rmobile/crc32_word4.h +@@ -0,0 +1,23 @@ ++/************************************************************************* ++ * Coptright (C) FUJITSUTEN Limited, 2012 All Rights Reserved. ++ * ++ *************************************************************************/ ++#ifndef __CRC32_WORD4_H__ ++#define __CRC32_WORD4_H__ ++ ++typedef struct { ++ unsigned int crc_w[4]; ++} CRC32_WORD4_t; ++ ++void calc_crc32x4(unsigned char *buf, size_t len, CRC32_WORD4_t *result); ++ ++typedef struct { ++ unsigned int size; ++ CRC32_WORD4_t chksum; ++ unsigned int dummy[3]; ++} CRC32_WORD4_TICKET_t; ++ ++#define IS_CRC_WORD4_OK(_res1, _res2) (!memcmp((_res1), (_res2), sizeof(CRC32_WORD4_t))) ++#define IS_CRC_WORD4_ZERO(_w4) (((_w4)->crc_w[0] == 0) && ((_w4)->crc_w[1] == 0) && ((_w4)->crc_w[2] == 0) && ((_w4)->crc_w[3] == 0)) ++#define IS_CRC_WORD4_ALL_F(_w4) (((_w4)->crc_w[0] == 0xffffffff) && ((_w4)->crc_w[1] == 0xffffffff) && ((_w4)->crc_w[2] == 0xffffffff) && ((_w4)->crc_w[3] == 0xffffffff)) ++#endif +diff --git a/arch/arm/cpu/armv7/rmobile/sh_timer.c b/arch/arm/cpu/armv7/rmobile/sh_timer.c +new file mode 100644 +index 0000000..1c64950 +--- /dev/null ++++ b/arch/arm/cpu/armv7/rmobile/sh_timer.c +@@ -0,0 +1,209 @@ ++/* ++ * Copyright (C) 2013-2014 Renesas Electronics Corporation ++ * ++ * (C) Copyright 2009 ++ * Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> ++ * ++ * (C) Copyright 2007-2012 ++ * Nobobuhiro Iwamatsu <iwamatsu@nigauri.org> ++ * ++ * (C) Copyright 2003 ++ * Wolfgang Denk, DENX Software Engineering, wd@denx.de. ++ * ++ * See file CREDITS for list of people who contributed to this ++ * project. ++ * ++ * 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 the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, ++ * MA 02111-1307 USA ++ */ ++ ++#include <common.h> ++#include <div64.h> ++#include <asm/processor.h> ++#include <asm/io.h> ++#include <sh_tmu.h> ++ ++#if defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ ++ defined(CONFIG_R8A7793) || defined(CONFIG_R8A7794) ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++#endif ++ ++static struct tmu_regs *tmu = (struct tmu_regs *)TMU_BASE; ++ ++static u16 bit; ++static unsigned long last_tcnt; ++static unsigned long long overflow_ticks; ++ ++#if defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ ++ defined(CONFIG_R8A7793) || defined(CONFIG_R8A7794) ++ ++unsigned long get_tbclk(void) ++{ ++ if (gd->flags & GD_FLG_RELOC) ++ return get_tmu0_clk_rate() >> ((bit + 1) * 2); ++ else { ++ u16 bit; ++ ++ bit = (ffs(CONFIG_SYS_TMU_CLK_DIV) >> 1) - 1; ++ return get_tmu0_clk_rate() >> ((bit + 1) * 2); ++ } ++} ++ ++#else ++ ++unsigned long get_tbclk(void) ++{ ++ return get_tmu0_clk_rate() >> ((bit + 1) * 2); ++} ++ ++#endif ++ ++static inline unsigned long long tick_to_time(unsigned long long tick) ++{ ++ tick *= CONFIG_SYS_HZ; ++ do_div(tick, get_tbclk()); ++ ++ return tick; ++} ++ ++static inline unsigned long long usec_to_tick(unsigned long long usec) ++{ ++ usec *= get_tbclk(); ++ do_div(usec, 1000000); ++ ++ return usec; ++} ++ ++static void tmu_timer_start(unsigned int timer) ++{ ++ if (timer > 2) ++ return; ++ writeb(readb(&tmu->tstr) | (1 << timer), &tmu->tstr); ++} ++ ++static void tmu_timer_stop(unsigned int timer) ++{ ++ if (timer > 2) ++ return; ++ writeb(readb(&tmu->tstr) & ~(1 << timer), &tmu->tstr); ++} ++ ++#if defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ ++ defined(CONFIG_R8A7793) || defined(CONFIG_R8A7794) ++ ++int sh_timer_init(void) ++{ ++ bit = (ffs(CONFIG_SYS_TMU_CLK_DIV) >> 1) - 1; ++ writew((readw(&tmu->tcr0) & ~0x7) | bit, &tmu->tcr0); ++ ++ tmu_timer_stop(0); ++ tmu_timer_start(0); ++ ++ last_tcnt = 0; ++ overflow_ticks = 0; ++ ++ return 0; ++} ++ ++int timer_init(void) ++{ ++ tmu_timer_stop(0); ++ tmu_timer_start(0); ++ ++ return 0; ++} ++ ++#else ++ ++int timer_init(void) ++{ ++ bit = (ffs(CONFIG_SYS_TMU_CLK_DIV) >> 1) - 1; ++ writew((readw(&tmu->tcr0) & ~0x7) | bit, &tmu->tcr0); ++ ++ tmu_timer_stop(0); ++ tmu_timer_start(0); ++ ++ last_tcnt = 0; ++ overflow_ticks = 0; ++ ++ return 0; ++} ++ ++#endif ++ ++#if defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ ++ defined(CONFIG_R8A7793) || defined(CONFIG_R8A7794) ++ ++unsigned long long get_ticks(void) ++{ ++ unsigned long tcnt = 0 - readl(&tmu->tcnt0); ++ ++ if (gd->flags & GD_FLG_RELOC) { ++ if (last_tcnt > tcnt) /* overflow */ ++ overflow_ticks++; ++ last_tcnt = tcnt; ++ ++ return (overflow_ticks << 32) | tcnt; ++ } ++ else ++ return tcnt; ++} ++ ++#else ++ ++unsigned long long get_ticks(void) ++{ ++ unsigned long tcnt = 0 - readl(&tmu->tcnt0); ++ ++ if (last_tcnt > tcnt) /* overflow */ ++ overflow_ticks++; ++ last_tcnt = tcnt; ++ ++ return (overflow_ticks << 32) | tcnt; ++} ++ ++#endif ++ ++void __udelay(unsigned long usec) ++{ ++ unsigned long long tmp; ++ ulong tmo; ++ ++ tmo = usec_to_tick(usec); ++ tmp = get_ticks() + tmo; /* get current timestamp */ ++ ++ while (get_ticks() < tmp) /* loop till event */ ++ /*NOP*/; ++} ++ ++unsigned long get_timer(unsigned long base) ++{ ++ /* return msec */ ++ return tick_to_time(get_ticks()) - base; ++} ++ ++void set_timer(unsigned long t) ++{ ++ writel((0 - t), &tmu->tcnt0); ++} ++ ++void reset_timer(void) ++{ ++ tmu_timer_stop(0); ++ set_timer(0); ++ tmu_timer_start(0); ++} +diff --git a/arch/arm/include/asm/arch-rmobile/rmobile.h b/arch/arm/include/asm/arch-rmobile/rmobile.h +index 33a302e..12276e0 100644 +--- a/arch/arm/include/asm/arch-rmobile/rmobile.h ++++ b/arch/arm/include/asm/arch-rmobile/rmobile.h +@@ -26,6 +26,8 @@ u32 rmobile_get_cpu_type(void); + u32 rmobile_get_cpu_rev_integer(void); + u32 rmobile_get_cpu_rev_fraction(void); + ++void arm_arch_timer_init(void); ++ + #endif /* __ASSEMBLY__ */ + + #endif /* __KERNEL__ */ +diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h +index ad9a875..aad5bf7 100644 +--- a/arch/arm/include/asm/armv7.h ++++ b/arch/arm/include/asm/armv7.h +@@ -62,9 +62,19 @@ + * However, we use the CP15 based instructtions because we use + * -march=armv5 in U-Boot + */ +-#define CP15ISB asm volatile ("mcr p15, 0, %0, c7, c5, 4" : : "r" (0)) +-#define CP15DSB asm volatile ("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)) +-#define CP15DMB asm volatile ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0)) ++#define CP15ISB() asm volatile ("mcr p15, 0, %0, c7, c5, 4" : : "r" (0)) ++#define CP15DSB() asm volatile ("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)) ++#define CP15DMB() asm volatile ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0)) ++ ++#ifdef __ARM_ARCH_7A__ ++#define ISB() asm volatile ("isb" : : : "memory") ++#define DSB() asm volatile ("dsb" : : : "memory") ++#define DMB() asm volatile ("dmb" : : : "memory") ++#else ++#define ISB() CP15ISB ++#define DSB() CP15DSB ++#define DMB() CP15DMB ++#endif + + void v7_outer_cache_enable(void); + void v7_outer_cache_disable(void); +diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h +index cd0de6b..3906646 100644 +--- a/arch/arm/include/asm/system.h ++++ b/arch/arm/include/asm/system.h +@@ -45,6 +45,12 @@ + #define CR_AFE (1 << 29) /* Access flag enable */ + #define CR_TE (1 << 30) /* Thumb exception enable */ + ++#if defined(CONFIG_ARMV7_LPAE) && !defined(PGTABLE_SIZE) ++#define PGTABLE_SIZE (4096 * 5) ++#elif !defined(PGTABLE_SIZE) ++#define PGTABLE_SIZE (4096 * 4) ++#endif ++ + /* + * This is used to ensure the compiler did actually allocate the register we + * asked it for some inline assembly sequences. Apparently we can't trust +@@ -61,17 +67,50 @@ + + #define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t"); + ++static inline unsigned long get_cpsr(void) ++{ ++ unsigned long cpsr; ++ ++ asm volatile("mrs %0, cpsr" : "=r"(cpsr): ); ++ return cpsr; ++} ++ ++static inline int is_hyp(void) ++{ ++#ifdef CONFIG_ARMV7_LPAE ++ /* HYP mode requires LPAE ... */ ++ return ((get_cpsr() & 0x1f) == 0x1a); ++#else ++ /* ... so without LPAE support we can optimize all hyp code away */ ++ return 0; ++#endif ++} ++ + static inline unsigned int get_cr(void) + { + unsigned int val; +- asm("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val) : : "cc"); ++ ++ if (is_hyp()) ++ asm volatile("mrc p15, 4, %0, c1, c0, 0 @ get CR" : "=r" (val) ++ : ++ : "cc"); ++ else ++ asm volatile("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val) ++ : ++ : "cc"); + return val; + } + + static inline void set_cr(unsigned int val) + { +- asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR" +- : : "r" (val) : "cc"); ++ if (is_hyp()) ++ asm volatile("mcr p15, 4, %0, c1, c0, 0 @ set CR" : ++ : "r" (val) ++ : "cc"); ++ else ++ asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR" : ++ : "r" (val) ++ : "cc"); + isb(); + } + +@@ -105,19 +144,108 @@ static inline void set_actlr(unsigned int val) + isb(); + } + ++#ifdef CONFIG_ARMV7_LPAE ++/* Long-Descriptor Translation Table Level 1/2 Bits */ ++#define TTB_SECT_XN_MASK (1ULL << 54) ++#define TTB_SECT_NG_MASK (1 << 11) ++#define TTB_SECT_AF (1 << 10) ++#define TTB_SECT_SH_MASK (3 << 8) ++#define TTB_SECT_NS_MASK (1 << 5) ++#define TTB_SECT_AP (1 << 6) ++/* Note: TTB AP bits are set elsewhere */ ++#define TTB_SECT_MAIR(x) ((x & 0x7) << 2) /* Index into MAIR */ ++#define TTB_SECT (1 << 0) ++#define TTB_PAGETABLE (3 << 0) ++ ++/* TTBCR flags */ ++#define TTBCR_EAE (1 << 31) ++#define TTBCR_T0SZ(x) ((x) << 0) ++#define TTBCR_T1SZ(x) ((x) << 16) ++#define TTBCR_USING_TTBR0 (TTBCR_T0SZ(0) | TTBCR_T1SZ(0)) ++#define TTBCR_IRGN0_NC (0 << 8) ++#define TTBCR_IRGN0_WBWA (1 << 8) ++#define TTBCR_IRGN0_WT (2 << 8) ++#define TTBCR_IRGN0_WBNWA (3 << 8) ++#define TTBCR_IRGN0_MASK (3 << 8) ++#define TTBCR_ORGN0_NC (0 << 10) ++#define TTBCR_ORGN0_WBWA (1 << 10) ++#define TTBCR_ORGN0_WT (2 << 10) ++#define TTBCR_ORGN0_WBNWA (3 << 10) ++#define TTBCR_ORGN0_MASK (3 << 10) ++#define TTBCR_SHARED_NON (0 << 12) ++#define TTBCR_SHARED_OUTER (2 << 12) ++#define TTBCR_SHARED_INNER (3 << 12) ++#define TTBCR_EPD0 (0 << 7) ++ ++/* ++ * Memory types ++ */ ++#define MEMORY_ATTRIBUTES ((0x00 << (0 * 8)) | (0x88 << (1 * 8)) | \ ++ (0xcc << (2 * 8)) | (0xff << (3 * 8))) ++ ++/* options available for data cache on each page */ ++enum dcache_option { ++ DCACHE_OFF = TTB_SECT | TTB_SECT_MAIR(0), ++ DCACHE_WRITETHROUGH = TTB_SECT | TTB_SECT_MAIR(1), ++ DCACHE_WRITEBACK = TTB_SECT | TTB_SECT_MAIR(2), ++ DCACHE_WRITEALLOC = TTB_SECT | TTB_SECT_MAIR(3), ++}; ++#elif defined(CONFIG_ARMV7) ++/* Short-Descriptor Translation Table Level 1 Bits */ ++#define TTB_SECT_NS_MASK (1 << 19) ++#define TTB_SECT_NG_MASK (1 << 17) ++#define TTB_SECT_S_MASK (1 << 16) ++/* Note: TTB AP bits are set elsewhere */ ++#define TTB_SECT_AP (3 << 10) ++#define TTB_SECT_TEX(x) ((x & 0x7) << 12) ++#define TTB_SECT_DOMAIN(x) ((x & 0xf) << 5) ++#define TTB_SECT_XN_MASK (1 << 4) ++#define TTB_SECT_C_MASK (1 << 3) ++#define TTB_SECT_B_MASK (1 << 2) ++#define TTB_SECT (2 << 0) ++ ++/* options available for data cache on each page */ ++enum dcache_option { ++ DCACHE_OFF = TTB_SECT_DOMAIN(0) | TTB_SECT_XN_MASK | TTB_SECT, ++ DCACHE_WRITETHROUGH = DCACHE_OFF | TTB_SECT_C_MASK, ++ DCACHE_WRITEBACK = DCACHE_WRITETHROUGH | TTB_SECT_B_MASK, ++ DCACHE_WRITEALLOC = DCACHE_WRITEBACK | TTB_SECT_TEX(1), ++}; ++#else ++#define TTB_SECT_AP (3 << 10) + /* options available for data cache on each page */ + enum dcache_option { + DCACHE_OFF = 0x12, + DCACHE_WRITETHROUGH = 0x1a, + DCACHE_WRITEBACK = 0x1e, ++ DCACHE_WRITEALLOC = 0x16, + }; ++#endif + + /* Size of an MMU section */ + enum { +- MMU_SECTION_SHIFT = 20, ++#ifdef CONFIG_ARMV7_LPAE ++ MMU_SECTION_SHIFT = 21, /* 2MB */ ++#else ++ MMU_SECTION_SHIFT = 20, /* 1MB */ ++#endif + MMU_SECTION_SIZE = 1 << MMU_SECTION_SHIFT, + }; + ++#ifdef CONFIG_ARMV7 ++/* TTBR0 bits */ ++#define TTBR0_BASE_ADDR_MASK 0xFFFFC000 ++#define TTBR0_RGN_NC (0 << 3) ++#define TTBR0_RGN_WBWA (1 << 3) ++#define TTBR0_RGN_WT (2 << 3) ++#define TTBR0_RGN_WB (3 << 3) ++/* TTBR0[6] is IRGN[0] and TTBR[0] is IRGN[1] */ ++#define TTBR0_IRGN_NC (0 << 0 | 0 << 6) ++#define TTBR0_IRGN_WBWA (0 << 0 | 1 << 6) ++#define TTBR0_IRGN_WT (1 << 0 | 0 << 6) ++#define TTBR0_IRGN_WB (1 << 0 | 1 << 6) ++#endif ++ + /** + * Change the cache settings for a region. + * +diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile +index 57111af..d8634be 100644 +--- a/arch/arm/lib/Makefile ++++ b/arch/arm/lib/Makefile +@@ -54,6 +54,8 @@ COBJS-y += reset.o + COBJS-y += cache.o + COBJS-y += cache-cp15.o + ++COBJS-y += call_with_stack.o ++ + SRCS := $(GLSOBJS:.o=.S) $(GLCOBJS:.o=.c) \ + $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c) + OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y)) +diff --git a/arch/arm/lib/board.c b/arch/arm/lib/board.c +index 9f861cc..3c2bf55 100644 +--- a/arch/arm/lib/board.c ++++ b/arch/arm/lib/board.c +@@ -355,7 +355,7 @@ void board_init_f(ulong bootflag) + + #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) + /* reserve TLB table */ +- gd->tlb_size = 4096 * 4; ++ gd->tlb_size = PGTABLE_SIZE; + addr -= gd->tlb_size; + + /* round down to next 64 kB limit */ +diff --git a/arch/arm/lib/cache-cp15.c b/arch/arm/lib/cache-cp15.c +index 75cf7b1..aefa2ae 100644 +--- a/arch/arm/lib/cache-cp15.c ++++ b/arch/arm/lib/cache-cp15.c +@@ -44,13 +44,50 @@ static void cp_delay (void) + asm volatile("" : : : "memory"); + } + ++#ifdef CONFIG_ARMV7_LPAE ++struct special_addr { ++ u32 page; ++ u32 size; ++ u64 addr; ++}; ++ ++/* This hack is for 2GB board with second GB attached ++ * to LPAE-only address at 0x200000000ULL */ ++#define SDRAM2_ADDR 0x200000000ULL ++#define SDRAM2_SIZE 0x40000000 /* 1G */ ++static struct special_addr offsets[] = { ++ { 0x80000000 >> MMU_SECTION_SHIFT, SDRAM2_SIZE >> MMU_SECTION_SHIFT, SDRAM2_ADDR, }, ++}; ++#endif ++ + void set_section_dcache(int section, enum dcache_option option) + { ++#ifdef CONFIG_ARMV7_LPAE ++ int i; ++ u64 *page_table = (u64 *)gd->tlb_addr; ++ /* Need to set the access flag to not fault */ ++ u64 value = TTB_SECT_AP | TTB_SECT_AF; ++#else + u32 *page_table = (u32 *)gd->tlb_addr; +- u32 value; ++ u32 value = TTB_SECT_AP; ++#endif ++ ++ /* Add the page offset */ ++#ifdef CONFIG_ARMV7_LPAE ++ for (i = 0; i < ARRAY_SIZE(offsets); i++) ++ if (section >= offsets[i].page && ++ section < offsets[i].page + offsets[i].size) ++ value |= offsets[i].addr + ((section - offsets[i].page) << MMU_SECTION_SHIFT); ++ else ++ value |= ((u32)section << MMU_SECTION_SHIFT); ++#else ++ value |= ((u32)section << MMU_SECTION_SHIFT); ++#endif + +- value = (section << MMU_SECTION_SHIFT) | (3 << 10); ++ /* Add caching bits */ + value |= option; ++ ++ /* Set PTE */ + page_table[section] = value; + } + +@@ -66,11 +103,11 @@ void mmu_set_region_dcache_behaviour(u32 start, int size, + enum dcache_option option) + { + u32 *page_table = (u32 *)gd->tlb_addr; +- u32 upto, end; ++ unsigned long upto, end; + + end = ALIGN(start + size, MMU_SECTION_SIZE) >> MMU_SECTION_SHIFT; + start = start >> MMU_SECTION_SHIFT; +- debug("%s: start=%x, size=%x, option=%d\n", __func__, start, size, ++ debug("%s: start=%pa, size=%zu, option=%d\n", __func__, &start, size, + option); + for (upto = start; upto < end; upto++) + set_section_dcache(upto, option); +@@ -83,11 +120,14 @@ static inline void dram_bank_mmu_setup(int bank) + int i; + + debug("%s: bank: %d\n", __func__, bank); +- for (i = bd->bi_dram[bank].start >> 20; +- i < (bd->bi_dram[bank].start + bd->bi_dram[bank].size) >> 20; ++ for (i = bd->bi_dram[bank].start >> MMU_SECTION_SHIFT; ++ i < (bd->bi_dram[bank].start >> MMU_SECTION_SHIFT) + ++ (bd->bi_dram[bank].size >> MMU_SECTION_SHIFT); + i++) { + #if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH) + set_section_dcache(i, DCACHE_WRITETHROUGH); ++#elif defined(CONFIG_SYS_ARM_CACHE_WRITEALLOC) ++ set_section_dcache(i, DCACHE_WRITEALLOC); + #else + set_section_dcache(i, DCACHE_WRITEBACK); + #endif +@@ -102,19 +142,88 @@ static inline void mmu_setup(void) + + arm_init_before_mmu(); + /* Set up an identity-mapping for all 4GB, rw for everyone */ +- for (i = 0; i < 4096; i++) ++ for (i = 0; i < ((4096ULL * 1024 * 1024) >> MMU_SECTION_SHIFT); i++) + set_section_dcache(i, DCACHE_OFF); + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + dram_bank_mmu_setup(i); + } ++ /* Enabling d-cache for remapped region of memory ++ * ++ */ ++ for (i = (0x80000000 >> MMU_SECTION_SHIFT); ++ i < 0xc0000000 >> MMU_SECTION_SHIFT; i++) ++#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH) ++ set_section_dcache(i, DCACHE_WRITETHROUGH); ++#elif defined(CONFIG_SYS_ARM_CACHE_WRITEALLOC) ++ set_section_dcache(i, DCACHE_WRITEALLOC); ++#else ++ set_section_dcache(i, DCACHE_WRITEBACK); ++#endif ++ ++#ifdef CONFIG_ARMV7_LPAE ++ /* Set up 4 PTE entries pointing to our 4 1GB page tables */ ++ for (i = 0; i < 4; i++) { ++ u64 *page_table = (u64 *)(gd->tlb_addr + (4096 * 4)); ++ u64 tpt = gd->tlb_addr + (4096 * i); ++ page_table[i] = tpt | TTB_PAGETABLE; ++ } + ++ reg = TTBCR_EAE; ++#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH) ++ reg |= TTBCR_ORGN0_WT | TTBCR_IRGN0_WT; ++#elif defined(CONFIG_SYS_ARM_CACHE_WRITEALLOC) ++ reg |= TTBCR_ORGN0_WBWA | TTBCR_IRGN0_WBWA; ++#else ++ reg |= TTBCR_ORGN0_WBNWA | TTBCR_IRGN0_WBNWA; ++#endif ++ ++ if (is_hyp()) { ++ /* Set HCTR to enable LPAE */ ++ asm volatile("mcr p15, 4, %0, c2, c0, 2" ++ : : "r" (reg) : "memory"); ++ /* Set HTTBR0 */ ++ asm volatile("mcrr p15, 4, %0, %1, c2" ++ : ++ : "r"(gd->tlb_addr + (4096 * 4)), "r"(0) ++ : "memory"); ++ /* Set HMAIR */ ++ asm volatile("mcr p15, 4, %0, c10, c2, 0" ++ : : "r" (MEMORY_ATTRIBUTES) : "memory"); ++ } else { ++ /* Set TTBCR to enable LPAE */ ++ asm volatile("mcr p15, 0, %0, c2, c0, 2" ++ : : "r" (reg) : "memory"); ++ /* Set 64-bit TTBR0 */ ++ asm volatile("mcrr p15, 0, %0, %1, c2" ++ : ++ : "r"(gd->tlb_addr + (4096 * 4)), "r"(0) ++ : "memory"); ++ /* Set MAIR */ ++ asm volatile("mcr p15, 0, %0, c10, c2, 0" ++ : : "r" (MEMORY_ATTRIBUTES) : "memory"); ++ } ++#elif defined(CONFIG_ARMV7) ++ /* Set TTBR0 */ ++ reg = gd->tlb_addr & TTBR0_BASE_ADDR_MASK; ++#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH) ++ reg |= TTBR0_RGN_WT | TTBR0_IRGN_WT; ++#elif defined(CONFIG_SYS_ARM_CACHE_WRITEALLOC) ++ reg |= TTBR0_RGN_WBWA | TTBR0_IRGN_WBWA; ++#else ++ reg |= TTBR0_RGN_WB | TTBR0_IRGN_WB; ++#endif ++ asm volatile("mcr p15, 0, %0, c2, c0, 0" ++ : : "r" (reg) : "memory"); ++#else + /* Copy the page table address to cp15 */ + asm volatile("mcr p15, 0, %0, c2, c0, 0" + : : "r" (gd->tlb_addr) : "memory"); ++#endif + /* Set the access control to all-supervisor */ + asm volatile("mcr p15, 0, %0, c3, c0, 0" + : : "r" (~0)); ++ + /* and enable the mmu */ + reg = get_cr(); /* get control reg. */ + cp_delay(); +diff --git a/arch/arm/lib/call_with_stack.S b/arch/arm/lib/call_with_stack.S +new file mode 100644 +index 0000000..651d869 +--- /dev/null ++++ b/arch/arm/lib/call_with_stack.S +@@ -0,0 +1,20 @@ ++.globl call_with_stack ++.syntax unified /* use unified assembler syntax */ ++#ifdef __thumb__ ++.thumb /* assemble in Thumb-2 (.thumb" can also be used) */ ++#endif ++call_with_stack: ++ str sp, [r2, #-4]! ++ str lr, [r2, #-4]! ++ ++ mov sp, r2 ++ mov r2, r0 ++ mov r0, r1 ++ ++ adr lr, 1f ++ mov pc, r2 ++ ++1: ldr lr, [sp] ++ ldr sp, [sp, #4] ++ mov pc, lr ++ +diff --git a/board/renesas/porter/porter.c b/board/renesas/porter/porter.c +index 71836e2..6c4fd1a 100644 +--- a/board/renesas/porter/porter.c ++++ b/board/renesas/porter/porter.c +@@ -42,6 +42,10 @@ void s_init(void) + struct r8a7791_swdt *swdt = (struct r8a7791_swdt *)SWDT_BASE; + u32 val; + ++#ifdef CONFIG_SYS_ARCH_TIMER ++ arm_arch_timer_init(); ++#endif ++ + /* Watchdog init */ + writel(0xA5A5A500, &rwdt->rwtcsra); + writel(0xA5A5A500, &swdt->swtcsra); +@@ -71,10 +75,12 @@ int board_early_init_f(void) + { + u32 val; + ++#ifdef CONFIG_TMU_TIMER + /* TMU0 */ + val = readl(MSTPSR1); + val &= ~TMU0_MSTP125; + writel(val, SMSTPCR1); ++#endif + + val = readl(MSTPSR7); + val &= ~SCIF0_MSTP721; +@@ -114,7 +120,9 @@ int board_init(void) + /* Init PFC controller */ + r8a7791_pinmux_init(); + ++#ifdef CONFIG_TMU_TIMER + sh_timer_init(); ++#endif + + /* ETHER Enable */ + gpio_request(GPIO_FN_ETH_CRS_DV, NULL); +@@ -288,10 +296,12 @@ void arch_preboot_os() + u32 val; + int i; + ++#ifdef CONFIG_TMU_TIMER + /* stop TMU0 */ + val = readb(TMU_BASE + TSTR0); + val &= ~TSTR0_STR0; + writeb(val, TMU_BASE + TSTR0); ++#endif + + /* stop all module clock*/ + for (i = MSTP00; i < MSTP_NR; i++) { +diff --git a/include/configs/porter.h b/include/configs/porter.h +index 7ab0643..5567c7c 100644 +--- a/include/configs/porter.h ++++ b/include/configs/porter.h +@@ -53,6 +53,9 @@ + #define CONFIG_CMD_EXT4_WRITE + #define CONFIG_CMD_SF + #define CONFIG_CMD_SPI ++#define CONFIG_CMD_SWSUSP ++#define CONFIG_CMD_SWSUSPMEM ++#define CONFIG_LZO + + #define CONFIG_CMDLINE_TAG + #define CONFIG_SETUP_MEMORY_TAGS +@@ -75,7 +78,6 @@ + #define CONFIG_BOARD_EARLY_INIT_F + #define CONFIG_USE_ARCH_MEMSET + #define CONFIG_USE_ARCH_MEMCPY +-#define CONFIG_TMU_TIMER + + /* STACK */ + #if defined(CONFIG_EXTRAM_BOOT) +@@ -89,8 +91,8 @@ + + /* MEMORY */ + #define PORTER_SDRAM_BASE 0x40000000 +-#define PORTER_SDRAM_SIZE 0x40000000 +-#define PORTER_UBOOT_SDRAM_SIZE 0x20000000 ++#define PORTER_SDRAM_SIZE 0x48000000 ++#define PORTER_UBOOT_SDRAM_SIZE 0x40000000 + + #define CONFIG_SYS_LONGHELP + #define CONFIG_SYS_PROMPT "=> " +@@ -203,4 +205,15 @@ + #define CONFIG_USB_HOST_ETHER /* Enable USB Ethernet adapters */ + #define CONFIG_USB_ETHER_ASIX /* Asix, or whatever driver(s) you want */ + ++#define CONFIG_ARMV7_LPAE /* 64-bit MMU descriptors */ ++#define CONFIG_SYS_ARM_CACHE_WRITEALLOC /* Make memory operations faster */ ++ ++#define CONFIG_SYS_ARCH_TIMER /* Init arch timer */ ++#define CONFIG_VE_ENABLED /* Virtualization Extensions are enabled*/ ++#define CONFIG_SYS_HZ_CLOCK CONFIG_SYS_CLK_FREQ ++ ++#define CONFIG_SH_DMA ++#define CONFIG_SH_SYS_DMAL_BASE 0xE6700000 ++#define CONFIG_SH_SYS_DMAL_NCH 15 ++ + #endif /* __PORTER_H */ +-- +1.8.3.1 + diff --git a/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot_2013.01.01.bbappend b/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot_2013.01.01.bbappend index ab77427fd..9a489e398 100644 --- a/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot_2013.01.01.bbappend +++ b/meta-agl-bsp/meta-renesas/recipes-bsp/u-boot/u-boot_2013.01.01.bbappend @@ -1,3 +1,10 @@ FILESEXTRAPATHS_append := ":${THISDIR}/${PN}" SRC_URI_append_porter_sota = "file://0001-Autoload-uEnv.txt-on-boot.patch" + +SRC_URI_append_agl-porter-hibernate = " file://hibernation/0001-Add-rcar-sdhi-DMA-support.patch \ + file://hibernation/0002-Add-Hibernation-swsusp-command-support.patch \ + file://hibernation/0003-Add-Hibernation-swsuspmem-command-support.patch \ + file://hibernation/0004-Add-porter-board-Hibernation-code.patch \ + " + |