Add kernel Hibernation code for porter board. 51/9451/5
authorYuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
Sun, 21 May 2017 15:15:23 +0000 (00:15 +0900)
committerJan-Simon Moeller <jsmoeller@linuxfoundation.org>
Thu, 25 May 2017 13:47:13 +0000 (13:47 +0000)
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: Ic64c9494a4bbd2518ef1aa334325b96eb7a9479e
Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
Reviewed-on: https://gerrit.automotivelinux.org/gerrit/9451
Tested-by: Jenkins Job builder account <agl-jobbuilder@automotivelinux.org>
ci-image-build: Jenkins Job builder account <agl-jobbuilder@automotivelinux.org>
Reviewed-by: Jan-Simon Moeller <jsmoeller@linuxfoundation.org>
17 files changed:
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux-renesas_%.bbappend [changed mode: 0644->0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0001-Add-Hibernation-kernel-base-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0002-Add-Hibernation-arch-code-Only-R-CAR-M2W.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0003-Add-sata-hibernation-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0004-Add-firmware-hibernation-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0005-Add-rcar-dma-hibernation-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0006-Add-rcar-du-hibernation-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0007-Add-rcar-i2c-hibernation-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0008-Add-rcar-mmc-hibernation-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0009-Add-hibernation-store-area.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0010-Add-rcar-eth-hibernation-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0011-Add-rcar-pci-hibernation-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0012-Add-rcar-gpio-hibernation-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0013-Add-rcar-spi-hibernation-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0014-Add-rcar-sci-hibernation-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0015-Add-rcar-usbphy-hibernation-code.patch [new file with mode: 0755]
meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/hibernation.cfg [new file with mode: 0755]

old mode 100644 (file)
new mode 100755 (executable)
index 29dcc75..35b2253
@@ -16,3 +16,23 @@ SRC_URI += " file://disable_delay_printk.patch \
             "
 
 KERNEL_CONFIG_FRAGMENTS_append = " ${WORKDIR}/ath9k_htc.cfg ${WORKDIR}/rtl_sdr.cfg"
+
+SRC_URI_append_agl-porter-hibernate = " file://hibernation/0001-Add-Hibernation-kernel-base-code.patch \
+                                        file://hibernation/0002-Add-Hibernation-arch-code-Only-R-CAR-M2W.patch \
+                                        file://hibernation/0003-Add-sata-hibernation-code.patch \
+                                        file://hibernation/0004-Add-firmware-hibernation-code.patch \
+                                        file://hibernation/0005-Add-rcar-dma-hibernation-code.patch \
+                                        file://hibernation/0006-Add-rcar-du-hibernation-code.patch \
+                                        file://hibernation/0007-Add-rcar-i2c-hibernation-code.patch \
+                                        file://hibernation/0008-Add-rcar-mmc-hibernation-code.patch \
+                                        file://hibernation/0009-Add-hibernation-store-area.patch \
+                                        file://hibernation/0010-Add-rcar-eth-hibernation-code.patch \
+                                        file://hibernation/0011-Add-rcar-pci-hibernation-code.patch \
+                                        file://hibernation/0012-Add-rcar-gpio-hibernation-code.patch \
+                                        file://hibernation/0013-Add-rcar-spi-hibernation-code.patch \
+                                        file://hibernation/0014-Add-rcar-sci-hibernation-code.patch \ 
+                                        file://hibernation/0015-Add-rcar-usbphy-hibernation-code.patch \
+                                        file://hibernation/hibernation.cfg \
+                                       "
+
+KERNEL_CONFIG_FRAGMENTS_append_agl-porter-hibernate += " ${WORKDIR}/hibernation/hibernation.cfg"
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0001-Add-Hibernation-kernel-base-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0001-Add-Hibernation-kernel-base-code.patch
new file mode 100755 (executable)
index 0000000..87cd286
--- /dev/null
@@ -0,0 +1,853 @@
+From 60123966221b74199e4cf0c18d43396b4f00a94a Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 16:44:34 +0900
+Subject: [PATCH 01/15] Add Hibernation kernel base code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ include/linux/sched.h    |   1 +
+ include/linux/suspend.h  |   2 +
+ kernel/auditfilter.c     |   2 +-
+ kernel/power/console.c   |   2 +
+ kernel/power/hibernate.c | 146 +++++++++++++++++++++++++++++++----------------
+ kernel/power/main.c      |   4 --
+ kernel/power/power.h     |   8 ++-
+ kernel/power/process.c   |  35 ++++++++----
+ kernel/power/snapshot.c  |  33 +++++++----
+ kernel/power/suspend.c   |   4 +-
+ kernel/power/swap.c      |  50 ++++++++++++++--
+ 11 files changed, 201 insertions(+), 86 deletions(-)
+
+diff --git a/include/linux/sched.h b/include/linux/sched.h
+index f87e9a8..8e3270c 100644
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -1644,6 +1644,7 @@ extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut,
+ #define PF_MEMPOLICY  0x10000000      /* Non-default NUMA mempolicy */
+ #define PF_MUTEX_TESTER       0x20000000      /* Thread belongs to the rt mutex tester */
+ #define PF_FREEZER_SKIP       0x40000000      /* Freezer should not count it as freezable */
++#define PF_SUSPEND_TASK 0x80000000      /* this thread called freeze_processes and should not be frozen */
+ /*
+  * Only the _current_ task can read/write to tsk->flags, but other
+diff --git a/include/linux/suspend.h b/include/linux/suspend.h
+index d4e3f16..243ff56 100644
+--- a/include/linux/suspend.h
++++ b/include/linux/suspend.h
+@@ -320,6 +320,8 @@ extern unsigned long get_safe_page(gfp_t gfp_mask);
+ extern void hibernation_set_ops(const struct platform_hibernation_ops *ops);
+ extern int hibernate(void);
+ extern bool system_entering_hibernation(void);
++asmlinkage int swsusp_save(void);
++extern struct pbe *restore_pblist;
+ #else /* CONFIG_HIBERNATION */
+ static inline void register_nosave_region(unsigned long b, unsigned long e) {}
+ static inline void register_nosave_region_late(unsigned long b, unsigned long e) {}
+diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
+index 6bd4a90..ac08a9a 100644
+--- a/kernel/auditfilter.c
++++ b/kernel/auditfilter.c
+@@ -423,7 +423,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
+               f->lsm_rule = NULL;
+               /* Support legacy tests for a valid loginuid */
+-              if ((f->type == AUDIT_LOGINUID) && (f->val == 4294967295)) {
++              if ((f->type == AUDIT_LOGINUID) && (f->val == 0xFFFFFFFF)) {
+                       f->type = AUDIT_LOGINUID_SET;
+                       f->val = 0;
+               }
+diff --git a/kernel/power/console.c b/kernel/power/console.c
+index 463aa67..aba9c54 100644
+--- a/kernel/power/console.c
++++ b/kernel/power/console.c
+@@ -9,6 +9,7 @@
+ #include <linux/kbd_kern.h>
+ #include <linux/vt.h>
+ #include <linux/module.h>
++#include <linux/slab.h>
+ #include "power.h"
+ #define SUSPEND_CONSOLE       (MAX_NR_CONSOLES-1)
+@@ -81,6 +82,7 @@ void pm_vt_switch_unregister(struct device *dev)
+       list_for_each_entry(tmp, &pm_vt_switch_list, head) {
+               if (tmp->dev == dev) {
+                       list_del(&tmp->head);
++                      kfree(tmp);
+                       break;
+               }
+       }
+diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
+index b26f5f1..524dcf5 100644
+--- a/kernel/power/hibernate.c
++++ b/kernel/power/hibernate.c
+@@ -34,12 +34,13 @@
+ static int nocompress;
+ static int noresume;
++static int nohibernate;
+ static int resume_wait;
+-static int resume_delay;
++static unsigned int resume_delay;
+ static char resume_file[256] = CONFIG_PM_STD_PARTITION;
+ dev_t swsusp_resume_device;
+ sector_t swsusp_resume_block;
+-int in_suspend __nosavedata;
++__visible int in_suspend __nosavedata;
+ enum {
+       HIBERNATION_INVALID,
+@@ -61,6 +62,11 @@ bool freezer_test_done;
+ static const struct platform_hibernation_ops *hibernation_ops;
++bool hibernation_available(void)
++{
++      return (nohibernate == 0);
++}
++
+ /**
+  * hibernation_set_ops - Set the global hibernate operations.
+  * @ops: Hibernation operations to use in subsequent hibernation transitions.
+@@ -82,6 +88,7 @@ void hibernation_set_ops(const struct platform_hibernation_ops *ops)
+       unlock_system_sleep();
+ }
++EXPORT_SYMBOL_GPL(hibernation_set_ops);
+ static bool entering_platform_hibernation;
+@@ -227,19 +234,23 @@ static void platform_recover(int platform_mode)
+ void swsusp_show_speed(struct timeval *start, struct timeval *stop,
+                       unsigned nr_pages, char *msg)
+ {
+-      s64 elapsed_centisecs64;
+-      int centisecs;
+-      int k;
+-      int kps;
++      u64 elapsed_centisecs64;
++      unsigned int centisecs;
++      unsigned int k;
++      unsigned int kps;
+       elapsed_centisecs64 = timeval_to_ns(stop) - timeval_to_ns(start);
++      /*
++       * If "(s64)elapsed_centisecs64 < 0", it will print long elapsed time,
++       * it is obvious enough for what went wrong.
++       */
+       do_div(elapsed_centisecs64, NSEC_PER_SEC / 100);
+       centisecs = elapsed_centisecs64;
+       if (centisecs == 0)
+               centisecs = 1;  /* avoid div-by-zero */
+       k = nr_pages * (PAGE_SIZE / 1024);
+       kps = (k * 100) / centisecs;
+-      printk(KERN_INFO "PM: %s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n",
++      pr_info("PM: %s %u kbytes in %u.%02u seconds (%u.%02u MB/s)\n",
+                       msg, k,
+                       centisecs / 100, centisecs % 100,
+                       kps / 1000, (kps % 1000) / 10);
+@@ -293,10 +304,10 @@ static int create_image(int platform_mode)
+                       error);
+       /* Restore control flow magically appears here */
+       restore_processor_state();
+-      if (!in_suspend) {
++      if (!in_suspend)
+               events_check_enabled = false;
+-              platform_leave(platform_mode);
+-      }
++
++      platform_leave(platform_mode);
+  Power_up:
+       syscore_resume();
+@@ -594,7 +605,8 @@ static void power_down(void)
+       case HIBERNATION_PLATFORM:
+               hibernation_platform_enter();
+       case HIBERNATION_SHUTDOWN:
+-              kernel_power_off();
++              if (pm_power_off)
++                      kernel_power_off();
+               break;
+ #ifdef CONFIG_SUSPEND
+       case HIBERNATION_SUSPEND:
+@@ -622,7 +634,8 @@ static void power_down(void)
+        * corruption after resume.
+        */
+       printk(KERN_CRIT "PM: Please power down manually\n");
+-      while(1);
++      while (1)
++              cpu_relax();
+ }
+ /**
+@@ -632,6 +645,11 @@ int hibernate(void)
+ {
+       int error;
++      if (!hibernation_available()) {
++              pr_debug("PM: Hibernation not available.\n");
++              return -EPERM;
++      }
++
+       lock_system_sleep();
+       /* The snapshot device should not be opened while we're running */
+       if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
+@@ -644,22 +662,22 @@ int hibernate(void)
+       if (error)
+               goto Exit;
+-      /* Allocate memory management structures */
+-      error = create_basic_memory_bitmaps();
+-      if (error)
+-              goto Exit;
+-
+       printk(KERN_INFO "PM: Syncing filesystems ... ");
+       sys_sync();
+       printk("done.\n");
+       error = freeze_processes();
+       if (error)
+-              goto Free_bitmaps;
++              goto Exit;
++
++      /* Allocate memory management structures */
++      error = create_basic_memory_bitmaps();
++      if (error)
++              goto Thaw;
+       error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
+       if (error || freezer_test_done)
+-              goto Thaw;
++              goto Free_bitmaps;
+       if (in_suspend) {
+               unsigned int flags = 0;
+@@ -682,14 +700,13 @@ int hibernate(void)
+               pr_debug("PM: Image restored successfully.\n");
+       }
++ Free_bitmaps:
++      free_basic_memory_bitmaps();
+  Thaw:
+       thaw_processes();
+       /* Don't bother checking whether freezer_test_done is true */
+       freezer_test_done = false;
+-
+- Free_bitmaps:
+-      free_basic_memory_bitmaps();
+  Exit:
+       pm_notifier_call_chain(PM_POST_HIBERNATION);
+       pm_restore_console();
+@@ -723,7 +740,7 @@ static int software_resume(void)
+       /*
+        * If the user said "noresume".. bail out early.
+        */
+-      if (noresume)
++      if (noresume || !hibernation_available())
+               return 0;
+       /*
+@@ -806,21 +823,19 @@ static int software_resume(void)
+       pm_prepare_console();
+       error = pm_notifier_call_chain(PM_RESTORE_PREPARE);
+       if (error)
+-              goto close_finish;
+-
+-      error = create_basic_memory_bitmaps();
+-      if (error)
+-              goto close_finish;
++              goto Close_Finish;
+       pr_debug("PM: Preparing processes for restore.\n");
+       error = freeze_processes();
+-      if (error) {
+-              swsusp_close(FMODE_READ);
+-              goto Done;
+-      }
++      if (error)
++              goto Close_Finish;
+       pr_debug("PM: Loading hibernation image.\n");
++      error = create_basic_memory_bitmaps();
++      if (error)
++              goto Thaw;
++
+       error = swsusp_read(&flags);
+       swsusp_close(FMODE_READ);
+       if (!error)
+@@ -828,9 +843,9 @@ static int software_resume(void)
+       printk(KERN_ERR "PM: Failed to load hibernation image, recovering.\n");
+       swsusp_free();
+-      thaw_processes();
+- Done:
+       free_basic_memory_bitmaps();
++ Thaw:
++      thaw_processes();
+  Finish:
+       pm_notifier_call_chain(PM_POST_RESTORE);
+       pm_restore_console();
+@@ -840,12 +855,12 @@ static int software_resume(void)
+       mutex_unlock(&pm_mutex);
+       pr_debug("PM: Hibernation image not present or could not be loaded.\n");
+       return error;
+-close_finish:
++ Close_Finish:
+       swsusp_close(FMODE_READ);
+       goto Finish;
+ }
+-late_initcall(software_resume);
++late_initcall_sync(software_resume);
+ static const char * const hibernation_modes[] = {
+@@ -889,6 +904,9 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr,
+       int i;
+       char *start = buf;
++      if (!hibernation_available())
++              return sprintf(buf, "[disabled]\n");
++
+       for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) {
+               if (!hibernation_modes[i])
+                       continue;
+@@ -923,6 +941,9 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
+       char *p;
+       int mode = HIBERNATION_INVALID;
++      if (!hibernation_available())
++              return -EPERM;
++
+       p = memchr(buf, '\n', n);
+       len = p ? p - buf : n;
+@@ -971,16 +992,20 @@ static ssize_t resume_show(struct kobject *kobj, struct kobj_attribute *attr,
+ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr,
+                           const char *buf, size_t n)
+ {
+-      unsigned int maj, min;
+       dev_t res;
+-      int ret = -EINVAL;
++      int len = n;
++      char *name;
+-      if (sscanf(buf, "%u:%u", &maj, &min) != 2)
+-              goto out;
++      if (len && buf[len-1] == '\n')
++              len--;
++      name = kstrndup(buf, len, GFP_KERNEL);
++      if (!name)
++              return -ENOMEM;
+-      res = MKDEV(maj,min);
+-      if (maj != MAJOR(res) || min != MINOR(res))
+-              goto out;
++      res = name_to_dev_t(name);
++      kfree(name);
++      if (!res)
++              return -EINVAL;
+       lock_system_sleep();
+       swsusp_resume_device = res;
+@@ -988,20 +1013,20 @@ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr,
+       printk(KERN_INFO "PM: Starting manual resume from disk\n");
+       noresume = 0;
+       software_resume();
+-      ret = n;
+- out:
+-      return ret;
++      return n;
+ }
+ power_attr(resume);
+-static ssize_t image_size_show(struct kobject *kobj, struct kobj_attribute *attr,
++static ssize_t image_size_show(struct kobject *kobj,
++                             struct kobj_attribute *attr,
+                              char *buf)
+ {
+       return sprintf(buf, "%lu\n", image_size);
+ }
+-static ssize_t image_size_store(struct kobject *kobj, struct kobj_attribute *attr,
++static ssize_t image_size_store(struct kobject *kobj,
++                              struct kobj_attribute *attr,
+                               const char *buf, size_t n)
+ {
+       unsigned long size;
+@@ -1065,7 +1090,7 @@ static int __init resume_setup(char *str)
+       if (noresume)
+               return 1;
+-      strncpy( resume_file, str, 255 );
++      strncpy(resume_file, str, 255);
+       return 1;
+ }
+@@ -1088,6 +1113,10 @@ static int __init hibernate_setup(char *str)
+               noresume = 1;
+       else if (!strncmp(str, "nocompress", 10))
+               nocompress = 1;
++      else if (!strncmp(str, "no", 2)) {
++              noresume = 1;
++              nohibernate = 1;
++      }
+       return 1;
+ }
+@@ -1105,13 +1134,30 @@ static int __init resumewait_setup(char *str)
+ static int __init resumedelay_setup(char *str)
+ {
+-      resume_delay = simple_strtoul(str, NULL, 0);
++      int rc = kstrtouint(str, 0, &resume_delay);
++
++      if (rc)
++              return rc;
++      return 1;
++}
++
++static int __init nohibernate_setup(char *str)
++{
++      noresume = 1;
++      nohibernate = 1;
+       return 1;
+ }
++static int __init kaslr_nohibernate_setup(char *str)
++{
++      return nohibernate_setup(str);
++}
++
+ __setup("noresume", noresume_setup);
+ __setup("resume_offset=", resume_offset_setup);
+ __setup("resume=", resume_setup);
+ __setup("hibernate=", hibernate_setup);
+ __setup("resumewait", resumewait_setup);
+ __setup("resumedelay=", resumedelay_setup);
++__setup("nohibernate", nohibernate_setup);
++__setup("kaslr", kaslr_nohibernate_setup);
+diff --git a/kernel/power/main.c b/kernel/power/main.c
+index d77663b..ac615e4 100644
+--- a/kernel/power/main.c
++++ b/kernel/power/main.c
+@@ -610,7 +610,6 @@ static struct attribute_group attr_group = {
+       .attrs = g,
+ };
+-#ifdef CONFIG_PM_RUNTIME
+ struct workqueue_struct *pm_wq;
+ EXPORT_SYMBOL_GPL(pm_wq);
+@@ -620,9 +619,6 @@ static int __init pm_start_workqueue(void)
+       return pm_wq ? 0 : -ENOMEM;
+ }
+-#else
+-static inline int pm_start_workqueue(void) { return 0; }
+-#endif
+ static int __init pm_init(void)
+ {
+diff --git a/kernel/power/power.h b/kernel/power/power.h
+index 7d4b7ff..c5821ca 100644
+--- a/kernel/power/power.h
++++ b/kernel/power/power.h
+@@ -2,6 +2,7 @@
+ #include <linux/suspend_ioctls.h>
+ #include <linux/utsname.h>
+ #include <linux/freezer.h>
++#include <linux/compiler.h>
+ struct swsusp_info {
+       struct new_utsname      uts;
+@@ -11,7 +12,8 @@ struct swsusp_info {
+       unsigned long           image_pages;
+       unsigned long           pages;
+       unsigned long           size;
+-} __attribute__((aligned(PAGE_SIZE)));
++      char                    archdata[1024];
++} __aligned(PAGE_SIZE);
+ #ifdef CONFIG_HIBERNATION
+ /* kernel/power/snapshot.c */
+@@ -37,6 +39,8 @@ static inline char *check_image_kernel(struct swsusp_info *info)
+ }
+ #endif /* CONFIG_ARCH_HIBERNATION_HEADER */
++extern void __weak swsusp_arch_add_info(char *archdata);
++
+ /*
+  * Keep some memory free so that I/O operations can succeed without paging
+  * [Might this be more than 4 MB?]
+@@ -49,6 +53,8 @@ static inline char *check_image_kernel(struct swsusp_info *info)
+  */
+ #define SPARE_PAGES   ((1024 * 1024) >> PAGE_SHIFT)
++asmlinkage int swsusp_save(void);
++
+ /* kernel/power/hibernate.c */
+ extern bool freezer_test_done;
+diff --git a/kernel/power/process.c b/kernel/power/process.c
+index 0695319..04559b4 100644
+--- a/kernel/power/process.c
++++ b/kernel/power/process.c
+@@ -30,9 +30,10 @@ static int try_to_freeze_tasks(bool user_only)
+       unsigned int todo;
+       bool wq_busy = false;
+       struct timeval start, end;
+-      u64 elapsed_csecs64;
+-      unsigned int elapsed_csecs;
++      u64 elapsed_msecs64;
++      unsigned int elapsed_msecs;
+       bool wakeup = false;
++      int sleep_usecs = USEC_PER_MSEC;
+       do_gettimeofday(&start);
+@@ -68,22 +69,25 @@ static int try_to_freeze_tasks(bool user_only)
+               /*
+                * We need to retry, but first give the freezing tasks some
+-               * time to enter the refrigerator.
++               * time to enter the refrigerator.  Start with an initial
++               * 1 ms sleep followed by exponential backoff until 8 ms.
+                */
+-              msleep(10);
++              usleep_range(sleep_usecs / 2, sleep_usecs);
++              if (sleep_usecs < 8 * USEC_PER_MSEC)
++                      sleep_usecs *= 2;
+       }
+       do_gettimeofday(&end);
+-      elapsed_csecs64 = timeval_to_ns(&end) - timeval_to_ns(&start);
+-      do_div(elapsed_csecs64, NSEC_PER_SEC / 100);
+-      elapsed_csecs = elapsed_csecs64;
++      elapsed_msecs64 = timeval_to_ns(&end) - timeval_to_ns(&start);
++      do_div(elapsed_msecs64, NSEC_PER_MSEC);
++      elapsed_msecs = elapsed_msecs64;
+       if (todo) {
+               printk("\n");
+-              printk(KERN_ERR "Freezing of tasks %s after %d.%02d seconds "
++              printk(KERN_ERR "Freezing of tasks %s after %d.%03d seconds "
+                      "(%d tasks refusing to freeze, wq_busy=%d):\n",
+                      wakeup ? "aborted" : "failed",
+-                     elapsed_csecs / 100, elapsed_csecs % 100,
++                     elapsed_msecs / 1000, elapsed_msecs % 1000,
+                      todo - wq_busy, wq_busy);
+               if (!wakeup) {
+@@ -96,8 +100,8 @@ static int try_to_freeze_tasks(bool user_only)
+                       read_unlock(&tasklist_lock);
+               }
+       } else {
+-              printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100,
+-                      elapsed_csecs % 100);
++              printk("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000,
++                      elapsed_msecs % 1000);
+       }
+       return todo ? -EBUSY : 0;
+@@ -139,6 +143,9 @@ int freeze_processes(void)
+       if (error)
+               return error;
++      /* Make sure this task doesn't get frozen */
++      current->flags |= PF_SUSPEND_TASK;
++
+       if (!pm_freezing)
+               atomic_inc(&system_freezing_cnt);
+@@ -202,6 +209,7 @@ int freeze_kernel_threads(void)
+ void thaw_processes(void)
+ {
+       struct task_struct *g, *p;
++      struct task_struct *curr = current;
+       if (pm_freezing)
+               atomic_dec(&system_freezing_cnt);
+@@ -217,10 +225,15 @@ void thaw_processes(void)
+       read_lock(&tasklist_lock);
+       do_each_thread(g, p) {
++              /* No other threads should have PF_SUSPEND_TASK set */
++              WARN_ON((p != curr) && (p->flags & PF_SUSPEND_TASK));
+               __thaw_task(p);
+       } while_each_thread(g, p);
+       read_unlock(&tasklist_lock);
++      WARN_ON(!(curr->flags & PF_SUSPEND_TASK));
++      curr->flags &= ~PF_SUSPEND_TASK;
++
+       usermodehelper_enable();
+       schedule();
+diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
+index 91c04f1..a673f7b 100644
+--- a/kernel/power/snapshot.c
++++ b/kernel/power/snapshot.c
+@@ -27,6 +27,7 @@
+ #include <linux/highmem.h>
+ #include <linux/list.h>
+ #include <linux/slab.h>
++#include <linux/compiler.h>
+ #include <asm/uaccess.h>
+ #include <asm/mmu_context.h>
+@@ -155,7 +156,7 @@ static inline void free_image_page(void *addr, int clear_nosave_free)
+ struct linked_page {
+       struct linked_page *next;
+       char data[LINKED_PAGE_DATA_SIZE];
+-} __attribute__((packed));
++} __packed;
+ static inline void
+ free_list_of_pages(struct linked_page *list, int clear_page_nosave)
+@@ -352,7 +353,7 @@ static int create_mem_extents(struct list_head *list, gfp_t gfp_mask)
+               struct mem_extent *ext, *cur, *aux;
+               zone_start = zone->zone_start_pfn;
+-              zone_end = zone->zone_start_pfn + zone->spanned_pages;
++              zone_end = zone_end_pfn(zone);
+               list_for_each_entry(ext, list, hook)
+                       if (zone_start <= ext->end)
+@@ -642,8 +643,9 @@ __register_nosave_region(unsigned long start_pfn, unsigned long end_pfn,
+       region->end_pfn = end_pfn;
+       list_add_tail(&region->list, &nosave_regions);
+  Report:
+-      printk(KERN_INFO "PM: Registered nosave memory: %016lx - %016lx\n",
+-              start_pfn << PAGE_SHIFT, end_pfn << PAGE_SHIFT);
++      printk(KERN_INFO "PM: Registered nosave memory: [mem %#010llx-%#010llx]\n",
++              (unsigned long long) start_pfn << PAGE_SHIFT,
++              ((unsigned long long) end_pfn << PAGE_SHIFT) - 1);
+ }
+ /*
+@@ -742,7 +744,10 @@ int create_basic_memory_bitmaps(void)
+       struct memory_bitmap *bm1, *bm2;
+       int error = 0;
+-      BUG_ON(forbidden_pages_map || free_pages_map);
++      if (forbidden_pages_map && free_pages_map)
++              return 0;
++      else
++              BUG_ON(forbidden_pages_map || free_pages_map);
+       bm1 = kzalloc(sizeof(struct memory_bitmap), GFP_KERNEL);
+       if (!bm1)
+@@ -788,7 +793,8 @@ void free_basic_memory_bitmaps(void)
+ {
+       struct memory_bitmap *bm1, *bm2;
+-      BUG_ON(!(forbidden_pages_map && free_pages_map));
++      if (WARN_ON(!(forbidden_pages_map && free_pages_map)))
++              return;
+       bm1 = forbidden_pages_map;
+       bm2 = free_pages_map;
+@@ -883,7 +889,7 @@ static unsigned int count_highmem_pages(void)
+                       continue;
+               mark_free_pages(zone);
+-              max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
++              max_zone_pfn = zone_end_pfn(zone);
+               for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
+                       if (saveable_highmem_page(zone, pfn))
+                               n++;
+@@ -947,7 +953,7 @@ static unsigned int count_data_pages(void)
+                       continue;
+               mark_free_pages(zone);
+-              max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
++              max_zone_pfn = zone_end_pfn(zone);
+               for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
+                       if (saveable_page(zone, pfn))
+                               n++;
+@@ -1040,7 +1046,7 @@ copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
+               unsigned long max_zone_pfn;
+               mark_free_pages(zone);
+-              max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
++              max_zone_pfn = zone_end_pfn(zone);
+               for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
+                       if (page_is_saveable(zone, pfn))
+                               memory_bm_set_bit(orig_bm, pfn);
+@@ -1092,7 +1098,7 @@ void swsusp_free(void)
+       unsigned long pfn, max_zone_pfn;
+       for_each_populated_zone(zone) {
+-              max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
++              max_zone_pfn = zone_end_pfn(zone);
+               for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
+                       if (pfn_valid(pfn)) {
+                               struct page *page = pfn_to_page(pfn);
+@@ -1580,7 +1586,7 @@ swsusp_alloc(struct memory_bitmap *orig_bm, struct memory_bitmap *copy_bm,
+       return -ENOMEM;
+ }
+-asmlinkage int swsusp_save(void)
++asmlinkage __visible int swsusp_save(void)
+ {
+       unsigned int nr_pages, nr_highmem;
+@@ -1628,6 +1634,7 @@ static int init_header_complete(struct swsusp_info *info)
+ {
+       memcpy(&info->uts, init_utsname(), sizeof(struct new_utsname));
+       info->version_code = LINUX_VERSION_CODE;
++      swsusp_arch_add_info(info->archdata);
+       return 0;
+ }
+@@ -1647,6 +1654,8 @@ static char *check_image_kernel(struct swsusp_info *info)
+ }
+ #endif /* CONFIG_ARCH_HIBERNATION_HEADER */
++void __weak swsusp_arch_add_info(char *archdata) {}
++
+ unsigned long snapshot_get_image_size(void)
+ {
+       return nr_copy_pages + nr_meta_pages + 1;
+@@ -1758,7 +1767,7 @@ static int mark_unsafe_pages(struct memory_bitmap *bm)
+       /* Clear page flags */
+       for_each_populated_zone(zone) {
+-              max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
++              max_zone_pfn = zone_end_pfn(zone);
+               for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
+                       if (pfn_valid(pfn))
+                               swsusp_unset_page_free(pfn_to_page(pfn));
+diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
+index bef86d1..deec937 100644
+--- a/kernel/power/suspend.c
++++ b/kernel/power/suspend.c
+@@ -156,13 +156,13 @@ static int suspend_prepare(suspend_state_t state)
+ }
+ /* default implementation */
+-void __attribute__ ((weak)) arch_suspend_disable_irqs(void)
++void __weak arch_suspend_disable_irqs(void)
+ {
+       local_irq_disable();
+ }
+ /* default implementation */
+-void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
++void __weak arch_suspend_enable_irqs(void)
+ {
+       local_irq_enable();
+ }
+diff --git a/kernel/power/swap.c b/kernel/power/swap.c
+index 7c33ed2..a6a1c55 100644
+--- a/kernel/power/swap.c
++++ b/kernel/power/swap.c
+@@ -91,17 +91,28 @@ struct swap_map_handle {
+       unsigned int k;
+       unsigned long reqd_free_pages;
+       u32 crc32;
++#ifdef CONFIG_ARCH_SHMOBILE
++      unsigned int img_size; /* add */
++#endif
+ };
+ struct swsusp_header {
++#ifdef CONFIG_ARCH_SHMOBILE
++      char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - sizeof(int) -
++                    sizeof(u32) - (sizeof(unsigned int)*4) - sizeof(u32)];
++      unsigned int comp_crc32[4];
++      u32 img_size;
++#else
+       char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - sizeof(int) -
+-                    sizeof(u32)];
++                    sizeof(u32) - sizeof(u32)];
++      u32     comp_crc32;
++#endif
+       u32     crc32;
+       sector_t image;
+       unsigned int flags;     /* Flags to pass to the "boot" kernel */
+       char    orig_sig[10];
+       char    sig[10];
+-} __attribute__((packed));
++} __packed;
+ static struct swsusp_header *swsusp_header;
+@@ -230,6 +241,11 @@ static int mark_swapfiles(struct swap_map_handle *handle, unsigned int flags)
+               swsusp_header->flags = flags;
+               if (flags & SF_CRC32_MODE)
+                       swsusp_header->crc32 = handle->crc32;
++
++#ifdef CONFIG_ARCH_SHMOBILE
++              swsusp_header->img_size = handle->img_size;
++#endif
++
+               error = hib_bio_write_page(swsusp_resume_block,
+                                       swsusp_header, NULL);
+       } else {
+@@ -587,7 +603,11 @@ static int save_image_lzo(struct swap_map_handle *handle,
+       unsigned char *page = NULL;
+       struct cmp_data *data = NULL;
+       struct crc_data *crc = NULL;
++      int compr = 0;
++#ifdef CONFIG_ARCH_SHMOBILE
++      unsigned int comp_imgtotal = 0;
++#endif
+       /*
+        * We'll limit the number of threads for compression to limit memory
+        * footprint.
+@@ -733,7 +753,12 @@ static int save_image_lzo(struct swap_map_handle *handle,
+                       }
+                       *(size_t *)data[thr].cmp = data[thr].cmp_len;
+-
++                      compr += data[thr].cmp_len;
++#ifdef CONFIG_ARCH_SHMOBILE
++                      comp_imgtotal += (data[thr].cmp_len
++                                      + LZO_HEADER + (PAGE_SIZE - 1))
++                              & ~(PAGE_SIZE - 1);
++#endif
+                       /*
+                        * Given we are writing one page at a time to disk, we
+                        * copy that much from the buffer, although the last
+@@ -746,7 +771,6 @@ static int save_image_lzo(struct swap_map_handle *handle,
+                            off < LZO_HEADER + data[thr].cmp_len;
+                            off += PAGE_SIZE) {
+                               memcpy(page, data[thr].cmp + off, PAGE_SIZE);
+-
+                               ret = swap_write_page(handle, page, &bio);
+                               if (ret)
+                                       goto out_finish;
+@@ -762,8 +786,24 @@ out_finish:
+       do_gettimeofday(&stop);
+       if (!ret)
+               ret = err2;
+-      if (!ret)
++      if (!ret) {
++#ifdef CONFIG_ARCH_SHMOBILE
++              const unsigned int ds = comp_imgtotal +
++                      ((comp_imgtotal
++                        / ((2 * 1024 * 1024)
++                                - PAGE_SIZE)) * PAGE_SIZE);
++              const unsigned int swaped =
++                      (swp_offset(get_swap_page_of_type(root_swap))
++                       - 2) * PAGE_SIZE;
++              if (ds < swaped)
++                      handle->img_size = swaped;
++              else
++                      handle->img_size = ds;
++#endif
+               printk(KERN_INFO "PM: Image saving done.\n");
++              printk(KERN_INFO "PM: Compressed output size: %d [%d] (imgsize=%d/swaped size=%d)\n",
++                              compr, handle->img_size, ds, swaped);
++      }
+       swsusp_show_speed(&start, &stop, nr_to_write, "Wrote");
+ out_clean:
+       if (crc) {
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0002-Add-Hibernation-arch-code-Only-R-CAR-M2W.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0002-Add-Hibernation-arch-code-Only-R-CAR-M2W.patch
new file mode 100755 (executable)
index 0000000..4db90e4
--- /dev/null
@@ -0,0 +1,1529 @@
+From 34a419b3fd88a2275ca681c99a5787b937e0f39d Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 16:47:29 +0900
+Subject: [PATCH 02/15] Add Hibernation arch code(Only R-CAR M2W)
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ arch/arm/Kconfig                         |   5 +
+ arch/arm/include/asm/memory.h            |   1 +
+ arch/arm/include/asm/smp.h               |   4 +
+ arch/arm/include/asm/suspend.h           |   3 +
+ arch/arm/kernel/Makefile                 |   1 +
+ arch/arm/kernel/hibernate.c              | 139 ++++++++++++++
+ arch/arm/kernel/process.c                |   7 +-
+ arch/arm/kernel/sleep.S                  |   8 +
+ arch/arm/kernel/smp.c                    |   6 +
+ arch/arm/kernel/suspend.c                |  64 ++++---
+ arch/arm/mach-shmobile/Kconfig           |  65 +++++++
+ arch/arm/mach-shmobile/Makefile          |   1 +
+ arch/arm/mach-shmobile/common.h          |   8 +
+ arch/arm/mach-shmobile/crc32_word4.c     | 299 +++++++++++++++++++++++++++++++
+ arch/arm/mach-shmobile/crc32_word4.h     |  23 +++
+ arch/arm/mach-shmobile/headsmp.S         |   2 -
+ arch/arm/mach-shmobile/hibernation.c     | 243 +++++++++++++++++++++++++
+ arch/arm/mach-shmobile/platsmp-apmu.c    |  13 +-
+ arch/arm/mach-shmobile/platsmp-rst.c     |   3 +-
+ arch/arm/mach-shmobile/pm-r8a7791.c      |  27 +--
+ arch/arm/mach-shmobile/rcar-gen2.h       |  39 ++++
+ arch/arm/mach-shmobile/setup-r8a7791.c   |  10 +-
+ arch/arm/mach-shmobile/setup-rcar-gen2.c |  14 +-
+ arch/arm/mach-shmobile/smp-r8a7791.c     |   8 +-
+ arch/arm/mm/proc-v7.S                    |  31 ++--
+ 25 files changed, 947 insertions(+), 77 deletions(-)
+ create mode 100644 arch/arm/kernel/hibernate.c
+ create mode 100644 arch/arm/mach-shmobile/crc32_word4.c
+ create mode 100644 arch/arm/mach-shmobile/crc32_word4.h
+ create mode 100644 arch/arm/mach-shmobile/hibernation.c
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index 4dd95dd..eb76182 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -2232,6 +2232,11 @@ config ARCH_SUSPEND_POSSIBLE
+ config ARM_CPU_SUSPEND
+       def_bool PM_SLEEP
++config ARCH_HIBERNATION_POSSIBLE
++      bool
++      depends on MMU
++      default y if ARCH_SUSPEND_POSSIBLE
++
+ endmenu
+ source "net/Kconfig"
+diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
+index 48cb2b3..01158e7 100644
+--- a/arch/arm/include/asm/memory.h
++++ b/arch/arm/include/asm/memory.h
+@@ -241,6 +241,7 @@ static inline void *phys_to_virt(phys_addr_t x)
+ #define __pa(x)                       __virt_to_phys((unsigned long)(x))
+ #define __va(x)                       ((void *)__phys_to_virt((phys_addr_t)(x)))
+ #define pfn_to_kaddr(pfn)     __va((pfn) << PAGE_SHIFT)
++#define virt_to_pfn(kaddr)      (__pa(kaddr) >> PAGE_SHIFT)
+ /*
+  * Virtual <-> DMA view memory address translations
+diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h
+index d3a22be..b718040 100644
+--- a/arch/arm/include/asm/smp.h
++++ b/arch/arm/include/asm/smp.h
+@@ -42,6 +42,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs);
+  */
+ extern void smp_init_cpus(void);
++/*
++ * Provide a function to call machine specific cpu initialization sequence
++ */
++extern void arch_smp_prepare_cpus(unsigned int max_cpus);
+ /*
+  * Provide a function to raise an IPI cross call on CPUs in callmap.
+diff --git a/arch/arm/include/asm/suspend.h b/arch/arm/include/asm/suspend.h
+index 1c0a551..709afa4 100644
+--- a/arch/arm/include/asm/suspend.h
++++ b/arch/arm/include/asm/suspend.h
+@@ -3,5 +3,8 @@
+ extern void cpu_resume(void);
+ extern int cpu_suspend(unsigned long, int (*)(unsigned long));
++extern const void __nosave_begin, __nosave_end;
++extern void cpu_resume_restore_nosave(u32, u32, u32);
++extern void call_with_stack(void (*fn)(void *), void *arg, void *sp);
+ #endif
+diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
+index 5f3338e..70f439f 100644
+--- a/arch/arm/kernel/Makefile
++++ b/arch/arm/kernel/Makefile
+@@ -32,6 +32,7 @@ obj-$(CONFIG_ARTHUR)         += arthur.o
+ obj-$(CONFIG_ISA_DMA)         += dma-isa.o
+ obj-$(CONFIG_PCI)             += bios32.o isa.o
+ obj-$(CONFIG_ARM_CPU_SUSPEND) += sleep.o suspend.o
++obj-$(CONFIG_HIBERNATION)             += hibernate.o
+ obj-$(CONFIG_SMP)             += smp.o smp_tlb.o
+ obj-$(CONFIG_HAVE_ARM_SCU)    += smp_scu.o
+ obj-$(CONFIG_HAVE_ARM_TWD)    += smp_twd.o
+diff --git a/arch/arm/kernel/hibernate.c b/arch/arm/kernel/hibernate.c
+new file mode 100644
+index 0000000..9380fe2
+--- /dev/null
++++ b/arch/arm/kernel/hibernate.c
+@@ -0,0 +1,139 @@
++/*
++ * Hibernation support specific for ARM
++ *
++ * Derived from work on ARM hibernation support by:
++ *
++ * Ubuntu project, hibernation support for mach-dove
++ * Copyright (C) 2010 Nokia Corporation (Hiroshi Doyu)
++ * Copyright (C) 2010 Texas Instruments, Inc. (Teerth Reddy et al.)
++ *  https://lkml.org/lkml/2010/6/18/4
++ *  https://lists.linux-foundation.org/pipermail/linux-pm/2010-June/027422.html
++ *  https://patchwork.kernel.org/patch/96442/
++ *
++ * Copyright (C) 2006 Rafael J. Wysocki <rjw at sisk.pl>
++ *
++ * License terms: GNU General Public License (GPL) version 2
++ */
++
++#include <linux/mm.h>
++#include <linux/suspend.h>
++#include <asm/system_misc.h>
++#include <asm/idmap.h>
++#include <asm/suspend.h>
++#include <asm/memory.h>
++
++struct swsusp_archdata {
++      u32 nosave_backup_phys;
++      u32 nosave_begin_phys;
++      u32 nosave_end_phys;
++      /* Function pointer */
++      u32 cpu_resume_restore_nosave;
++};
++
++static struct swsusp_archdata __archdata;
++
++void swsusp_arch_add_info(char *archdata)
++{
++      memcpy((void *)archdata, (void *)&__archdata,
++                      sizeof(struct swsusp_archdata));
++}
++
++int pfn_is_nosave(unsigned long pfn)
++{
++      unsigned long nosave_begin_pfn = virt_to_pfn(&__nosave_begin);
++      unsigned long nosave_end_pfn = virt_to_pfn(&__nosave_end - 1);
++
++      return (pfn >= nosave_begin_pfn) && (pfn <= nosave_end_pfn);
++}
++
++void notrace save_processor_state(void)
++{
++      WARN_ON(num_online_cpus() != 1);
++      local_fiq_disable();
++}
++
++void notrace restore_processor_state(void)
++{
++      local_fiq_enable();
++}
++
++/*
++ * Snapshot kernel memory and reset the system.
++ *
++ * swsusp_save() is executed in the suspend finisher so that the CPU
++ * context pointer and memory are part of the saved image, which is
++ * required by the resume kernel image to restart execution from
++ * swsusp_arch_suspend().
++ *
++ * soft_restart is not technically needed, but is used to get success
++ * returned from cpu_suspend.
++ *
++ * When soft reboot completes, the hibernation snapshot is written out.
++ */
++static int notrace arch_save_image(unsigned long unused)
++{
++      int ret;
++      ret = swsusp_save();
++      if (ret == 0)
++              soft_restart(virt_to_phys(cpu_resume));
++      return ret;
++}
++
++/*
++ * Save the current CPU state before suspend / poweroff.
++ */
++int notrace swsusp_arch_suspend(void)
++{
++      return cpu_suspend(0, arch_save_image);
++}
++
++/*
++ * Restore page contents for physical pages that were in use during loading
++ * hibernation image.  Switch to idmap_pgd so the physical page tables
++ * are overwritten with the same contents.
++ */
++static void notrace arch_restore_image(void *unused)
++{
++      struct pbe *pbe;
++
++
++      cpu_switch_mm(idmap_pgd, &init_mm);
++      for (pbe = restore_pblist; pbe; pbe = pbe->next)
++              copy_page(pbe->orig_address, pbe->address);
++
++      soft_restart(virt_to_phys(cpu_resume));
++}
++static u64 resume_stack[PAGE_SIZE/2/sizeof(u64)] __nosavedata;
++
++/*
++ * Resume from the hibernation image.
++ * Due to the kernel heap / data restore, stack contents change underneath
++ * and that would make function calls impossible; switch to a temporary
++ * stack within the nosave region to avoid that problem.
++ */
++int swsusp_arch_resume(void)
++{
++      call_with_stack(arch_restore_image, 0,
++              resume_stack + ARRAY_SIZE(resume_stack));
++      return 0;
++}
++
++static int __init swsusp_arch_init(void)
++{
++      char *backup;
++      size_t len;
++
++      len = &__nosave_end - &__nosave_begin;
++      backup = kmalloc(len, GFP_KERNEL);
++      if (backup)
++              memcpy(backup, &__nosave_begin, len);
++
++      __archdata.nosave_backup_phys = virt_to_phys(backup);
++      __archdata.nosave_begin_phys = virt_to_phys(&__nosave_begin);
++      __archdata.nosave_end_phys = virt_to_phys(&__nosave_end);
++      __archdata.cpu_resume_restore_nosave =
++              virt_to_phys(cpu_resume_restore_nosave);
++
++      return 0;
++}
++late_initcall(swsusp_arch_init);
+diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
+index 7927629..ae56f0b 100644
+--- a/arch/arm/kernel/process.c
++++ b/arch/arm/kernel/process.c
+@@ -98,7 +98,7 @@ void soft_restart(unsigned long addr)
+       u64 *stack = soft_restart_stack + ARRAY_SIZE(soft_restart_stack);
+       /* Disable interrupts first */
+-      local_irq_disable();
++      raw_local_irq_disable();
+       local_fiq_disable();
+       /* Disable the L2 if we're the last man standing. */
+@@ -284,12 +284,17 @@ void __show_regs(struct pt_regs *regs)
+       buf[3] = flags & PSR_V_BIT ? 'V' : 'v';
+       buf[4] = '\0';
++#ifndef CONFIG_CPU_V7M
+       printk("Flags: %s  IRQs o%s  FIQs o%s  Mode %s  ISA %s  Segment %s\n",
+               buf, interrupts_enabled(regs) ? "n" : "ff",
+               fast_interrupts_enabled(regs) ? "n" : "ff",
+               processor_modes[processor_mode(regs)],
+               isa_modes[isa_mode(regs)],
+               get_fs() == get_ds() ? "kernel" : "user");
++#else
++      printk("xPSR: %08lx\n", regs->ARM_cpsr);
++#endif
++
+ #ifdef CONFIG_CPU_CP15
+       {
+               unsigned int ctrl;
+diff --git a/arch/arm/kernel/sleep.S b/arch/arm/kernel/sleep.S
+index 987dcf3..e4d092f 100644
+--- a/arch/arm/kernel/sleep.S
++++ b/arch/arm/kernel/sleep.S
+@@ -98,6 +98,14 @@ THUMB(      mov     sp, r2                  )
+ THUMB(        bx      r3                      )
+ ENDPROC(cpu_resume)
++      .align
++ENTRY(cpu_resume_restore_nosave)
++1:    ldmia   r0!, {r3-r10}
++      stmia   r1!, {r3-r10}
++      cmp     r1, r2
++      bne     1b
++      b       cpu_resume
++
+ sleep_save_sp:
+       .rept   CONFIG_NR_CPUS
+       .long   0                               @ preserve stack phys ptr here
+diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
+index 5919eb4..c9a2991 100644
+--- a/arch/arm/kernel/smp.c
++++ b/arch/arm/kernel/smp.c
+@@ -125,6 +125,12 @@ void __init smp_init_cpus(void)
+               smp_ops.smp_init_cpus();
+ }
++void arch_smp_prepare_cpus(unsigned int max_cpus)
++{
++      if (smp_ops.smp_prepare_cpus)
++              smp_ops.smp_prepare_cpus(max_cpus);
++}
++
+ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
+ {
+       if (smp_ops.smp_boot_secondary)
+diff --git a/arch/arm/kernel/suspend.c b/arch/arm/kernel/suspend.c
+index c59c97e..38a5067 100644
+--- a/arch/arm/kernel/suspend.c
++++ b/arch/arm/kernel/suspend.c
+@@ -10,6 +10,42 @@
+ extern int __cpu_suspend(unsigned long, int (*)(unsigned long));
+ extern void cpu_resume_mmu(void);
++#ifdef CONFIG_MMU
++/*
++ * Hide the first two arguments to __cpu_suspend - these are an implementation
++ * detail which platform code shouldn't have to know about.
++ */
++int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
++{
++      struct mm_struct *mm = current->active_mm;
++      int ret;
++
++      if (!idmap_pgd)
++              return -EINVAL;
++
++      /*
++       * Provide a temporary page table with an identity mapping for
++       * the MMU-enable code, required for resuming.  On successful
++       * resume (indicated by a zero return code), we need to switch
++       * back to the correct page tables.
++       */
++      ret = __cpu_suspend(arg, fn);
++      if (ret == 0) {
++              cpu_switch_mm(mm->pgd, mm);
++              local_flush_bp_all();
++              local_flush_tlb_all();
++      }
++
++      return ret;
++}
++#else
++int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
++{
++      return __cpu_suspend(arg, fn);
++}
++#define       idmap_pgd       NULL
++#endif
++
+ /*
+  * This is called by __cpu_suspend() to save the state, and do whatever
+  * flushing is required to ensure that when the CPU goes to sleep we have
+@@ -46,31 +82,3 @@ void __cpu_suspend_save(u32 *ptr, u32 ptrsz, u32 sp, u32 *save_ptr)
+       outer_clean_range(virt_to_phys(save_ptr),
+                         virt_to_phys(save_ptr) + sizeof(*save_ptr));
+ }
+-
+-/*
+- * Hide the first two arguments to __cpu_suspend - these are an implementation
+- * detail which platform code shouldn't have to know about.
+- */
+-int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
+-{
+-      struct mm_struct *mm = current->active_mm;
+-      int ret;
+-
+-      if (!idmap_pgd)
+-              return -EINVAL;
+-
+-      /*
+-       * Provide a temporary page table with an identity mapping for
+-       * the MMU-enable code, required for resuming.  On successful
+-       * resume (indicated by a zero return code), we need to switch
+-       * back to the correct page tables.
+-       */
+-      ret = __cpu_suspend(arg, fn);
+-      if (ret == 0) {
+-              cpu_switch_mm(mm->pgd, mm);
+-              local_flush_bp_all();
+-              local_flush_tlb_all();
+-      }
+-
+-      return ret;
+-}
+diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
+index 7c15245..73371de 100644
+--- a/arch/arm/mach-shmobile/Kconfig
++++ b/arch/arm/mach-shmobile/Kconfig
+@@ -64,6 +64,25 @@ config MACH_KOELSCH
+       select MICREL_PHY if SH_ETH
+       select SND_SOC_AK4642 if SND_SIMPLE_CARD
++config MACH_KOELSCH_FTEN
++      bool "FTEN spf development environment"
++      depends on MACH_KOELSCH
++
++config MACH_FTEN
++       bool
++
++config MACH_FTEN_DT
++      bool
++
++config MACH_FTEN_M2W
++      bool "FTEN R-Car M2W board"
++      depends on ARCH_R8A7791
++      select MACH_FTEN
++      select MACH_FTEN_DT
++      select HAVE_IDE
++      select FIQ
++      select SND_SOC_DIRANA3 if SND_SIMPLE_CARD
++
+ config MACH_LAGER
+       bool "Lager board"
+       depends on ARCH_R8A7790
+@@ -76,6 +95,15 @@ config MACH_GOSE
+       select MICREL_PHY if SH_ETH
+       select SND_SOC_AK4642 if SND_SIMPLE_CARD
++config MACH_FTEN_M2N
++      bool "FTEN R-Car M2N board"
++      depends on ARCH_R8A7793
++      select MACH_FTEN
++      select MACH_FTEN_DT
++      select HAVE_IDE
++      select FIQ
++      select SND_SOC_DIRANA3 if SND_SIMPLE_CARD
++
+ config MACH_ALT
+       bool "Alt board"
+       depends on ARCH_R8A7794
+@@ -287,6 +315,19 @@ config MACH_KOELSCH
+       select USE_OF
+       select MICREL_PHY if SH_ETH
++config MACH_FTEN
++      bool "FTEN strawberry board"
++      depends on ARCH_R8A7791
++      select USE_OF
++      select HAVE_IDE
++
++config MACH_FTEN_DT
++      bool "FTEN strawberry board - Device Tree Implementation"
++      depends on ARCH_R8A7791
++      select USE_OF
++      select HAVE_IDE
++      select SND_SOC_DIRANA3 if SND_SIMPLE_CARD
++
+ config MACH_KZM9G
+       bool "KZM-A9-GT board"
+       depends on ARCH_SH73A0
+@@ -360,4 +401,28 @@ config EM_TIMER_STI
+ endmenu
++if HIBERNATION
++
++menu "Hibernation area parameters"
++
++config SWSUSP_AREA
++      hex "RAM hibernation area address"
++      default "0x44000000"
++      depends on HIBERNATION
++      ---help---
++          RAM hibernation area address, this is required for CRC
++          calculation of final compressed hibernation image
++
++config SWSUSP_AREA_SIZE
++      hex "RAM hibernation area size"
++      default "0x4000000"
++      depends on HIBERNATION
++      ---help---
++          RAM hibernation area size, this is required for CRC
++          calculation of final compressed hibernation image
++
++endmenu
++
++endif
++
+ endif
+diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
+index 43b4025..71cfcfa 100644
+--- a/arch/arm/mach-shmobile/Makefile
++++ b/arch/arm/mach-shmobile/Makefile
+@@ -55,6 +55,7 @@ smp-$(CONFIG_ARCH_EMEV2)     += smp-emev2.o headsmp-scu.o platsmp-scu.o
+ # PM objects
+ obj-$(CONFIG_SUSPEND)         += suspend.o
++obj-$(CONFIG_HIBERNATION)     += hibernation.o
+ obj-$(CONFIG_CPU_IDLE)                += cpuidle.o
+ obj-$(CONFIG_CPU_FREQ)                += cpufreq.o
+ obj-$(CONFIG_ARCH_SH7372)     += pm-sh7372.o sleep-sh7372.o pm-rmobile.o
+diff --git a/arch/arm/mach-shmobile/common.h b/arch/arm/mach-shmobile/common.h
+index 95a77a0..37c7f87 100644
+--- a/arch/arm/mach-shmobile/common.h
++++ b/arch/arm/mach-shmobile/common.h
+@@ -25,6 +25,7 @@ struct clk;
+ extern int shmobile_clk_init(void);
+ extern void shmobile_handle_irq_intc(struct pt_regs *);
+ extern struct platform_suspend_ops shmobile_suspend_ops;
++extern const struct platform_hibernation_ops shmobile_hibernation_ops;
+ struct cpuidle_driver;
+ extern void shmobile_cpuidle_set_driver(struct cpuidle_driver *drv);
+ extern void shmobile_smp_apmu_enter_cpuidle(void);
+@@ -37,6 +38,12 @@ static inline int shmobile_suspend_init(void) { return 0; }
+ static inline void shmobile_smp_apmu_suspend_init(void) { }
+ #endif
++#ifdef CONFIG_HIBERNATION
++int shmobile_hibernation_init(void);
++#else
++static inline int shmobile_hibernation_init(void) { return 0; }
++#endif
++
+ #ifdef CONFIG_CPU_IDLE
+ int shmobile_cpuidle_init(void);
+ #else
+@@ -59,6 +66,7 @@ extern unsigned int l2actlr_value;
+ static inline void __init shmobile_init_late(void)
+ {
+       shmobile_suspend_init();
++      shmobile_hibernation_init();
+       shmobile_cpuidle_init();
+       shmobile_cpufreq_init();
+ }
+diff --git a/arch/arm/mach-shmobile/crc32_word4.c b/arch/arm/mach-shmobile/crc32_word4.c
+new file mode 100644
+index 0000000..8aaefc6
+--- /dev/null
++++ b/arch/arm/mach-shmobile/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
++#endif
++
++#include "crc32_word4.h"
++
++#define CRC_INIT_VALUE  (-1)
++#define CRC_FIX(_crc32) (~(_crc32))
++
++/* #define HWDPLS_ENABLE */
++#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/mach-shmobile/crc32_word4.h b/arch/arm/mach-shmobile/crc32_word4.h
+new file mode 100644
+index 0000000..6c04878
+--- /dev/null
++++ b/arch/arm/mach-shmobile/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/mach-shmobile/headsmp.S b/arch/arm/mach-shmobile/headsmp.S
+index debf271..f99f8b2 100644
+--- a/arch/arm/mach-shmobile/headsmp.S
++++ b/arch/arm/mach-shmobile/headsmp.S
+@@ -16,8 +16,6 @@
+ #include <linux/threads.h>
+ #include <asm/memory.h>
+-      __CPUINIT
+-
+ #ifdef CONFIG_SMP
+ ENTRY(shmobile_invalidate_start)
+       bl      v7_invalidate_l1
+diff --git a/arch/arm/mach-shmobile/hibernation.c b/arch/arm/mach-shmobile/hibernation.c
+new file mode 100644
+index 0000000..94fa78b
+--- /dev/null
++++ b/arch/arm/mach-shmobile/hibernation.c
+@@ -0,0 +1,243 @@
++/*
++ * Suspend-to-RAM support code for SH-Mobile ARM
++ *
++ *  Copyright (C) 2011 Magnus Damm
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License.  See the file "COPYING" in the main directory of this archive
++ * for more details.
++ */
++#include <linux/pm.h>
++#include <linux/suspend.h>
++#include <linux/module.h>
++#include <linux/err.h>
++#include <linux/cpu.h>
++#include <linux/smp.h>
++
++#include <linux/io.h>
++#include <asm/system_misc.h>
++#include <asm/page.h>
++#include <asm/smp_plat.h>
++
++#include "common.h"
++#include "rcar-gen2.h"
++
++#include <linux/clk.h>
++
++#include "crc32_word4.c"
++#include "pm-rcar.h"
++
++
++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;   /* add.  see. kernel/power/swap.c */
++      u32     crc32;
++      sector_t image;
++      unsigned int flags;     /* Flags to pass to the "boot" kernel */
++      char    orig_sig[10];
++      char    sig[10];
++} __packed;
++static unsigned long swsusp_area = CONFIG_SWSUSP_AREA;
++static unsigned long swsusp_area_size = CONFIG_SWSUSP_AREA_SIZE;
++
++enum {
++      MSTP00, MSTP01, MSTP02, MSTP03, MSTP04, MSTP05,
++      MSTP07, MSTP08, MSTP09, MSTP10, MSTP11,
++      MSTP_NR,
++};
++
++static struct {
++      u32 s_offset;
++      u32 s_val;
++      u32 r_offset;
++      u32 r_val;
++} mstp_regs[] = {
++      [MSTP00] = { SMSTPCR0,  0,
++                   RMSTPCR0,  0},
++      [MSTP01] = { SMSTPCR1,  0,
++                   RMSTPCR1,  0},
++      [MSTP02] = { SMSTPCR2,  0,
++                   RMSTPCR2,  0},
++      [MSTP03] = { SMSTPCR3,  0,
++                   RMSTPCR3,  0},
++      [MSTP04] = { SMSTPCR4,  0,
++                   RMSTPCR4,  0},
++      [MSTP05] = { SMSTPCR5,  0,
++                   RMSTPCR5,  0},
++      [MSTP07] = { SMSTPCR7,  0,
++                   RMSTPCR7,  0},
++      [MSTP08] = { SMSTPCR8,  0,
++                   RMSTPCR8,  0},
++      [MSTP09] = { SMSTPCR9,  0,
++                   RMSTPCR9,  0},
++      [MSTP10] = { SMSTPCR10, 0,
++                   RMSTPCR10, 0},
++      [MSTP11] = { SMSTPCR11, 0,
++                   RMSTPCR11, 0},
++};
++
++static void save_mstp_regs(void)
++{
++      int i;
++      void *m = ioremap(CPG_BASE, CPG_LEN);
++      for (i = MSTP00; i < MSTP_NR; i++) {
++              mstp_regs[i].s_val = ioread32(m +mstp_regs[i].s_offset);
++              mstp_regs[i].r_val = ioread32(m +mstp_regs[i].r_offset);
++      }
++      iounmap(m);
++}
++
++static void restore_mstp_regs(void)
++{
++      int i;
++      void *m = ioremap(CPG_BASE, CPG_LEN);
++      for (i = MSTP00; i < MSTP_NR; i++) {
++              iowrite32(mstp_regs[i].s_val, m +mstp_regs[i].s_offset);
++              iowrite32(mstp_regs[i].r_val, m +mstp_regs[i].r_offset);
++      }
++      iounmap(m);
++}
++
++static int shmobile_hibernation_begin(void)
++{
++      save_mstp_regs();
++      return 0;
++}
++
++static void shmobile_hibernation_end(void)
++{
++}
++
++static int shmobile_hibernation_pre_snapshot(void)
++{
++      return 0;
++}
++
++
++static void shmobile_hibernation_finish(void)
++{
++}
++
++static int shmobile_hibernation_prepare(void)
++{
++      return 0;
++}
++
++static int shmobile_hibernation_enter(void)
++{
++      void *m, *l;
++      struct swsusp_header *h;
++      unsigned int calc_sz;
++      if (swsusp_area_size > 0) {
++              h = m = ioremap(swsusp_area, swsusp_area_size);
++              if (h) {
++                      if ((h->img_size > PAGE_SIZE)
++                          && (h->img_size < (swsusp_area_size - PAGE_SIZE)))
++                              calc_sz = h->img_size;
++                      else
++                              calc_sz = swsusp_area_size - PAGE_SIZE;
++                      memset(&h->comp_crc32, 0, sizeof(h->comp_crc32));
++                      calc_crc32x4(m + PAGE_SIZE, calc_sz, &h->comp_crc32);
++                      mb();
++                      iounmap(m);
++              }
++      }
++      /* Resetting FDP0 */
++      l = ioremap(0xfe940000, 0x4000);
++      writel(1, l + 0x1c);
++      mb();
++      iounmap(l);
++      /* Resetting FDP1 */
++      l = ioremap(0xfe944000, 0x4000);
++      writel(1, l + 0x1c);
++      mb();
++      iounmap(l);
++      /* Doing board reset */
++      l = ioremap(0xe6300200, 4);
++      writel(0xa1b20001, l);
++      mb();
++      iounmap(l);
++
++      return 0;
++}
++
++char *clks[] = {
++      "ehci", "hsusb", "dmal", "dmah", "sys-dmac1",
++      "sys-dmac0", "ssp", "ssp_dev", "ipmmu_gp",
++      "audmac0", "audmac1",
++};
++
++static int shmobile_hibernation_pre_restore(void)
++{
++      return 0;
++}
++
++
++static void shmobile_hibernation_restore_cleanup(void)
++{
++}
++
++extern int in_suspend;
++
++static void shmobile_hibernation_leave(void)
++{
++      int restore_highmem(void);
++      struct clk *clk;
++      unsigned int i;
++
++      if (!in_suspend) {
++#ifdef CONFIG_SMP
++              if (is_smp())
++                      arch_smp_prepare_cpus(setup_max_cpus);
++#endif
++
++#ifdef CONFIG_HIGHMEM
++              restore_highmem();
++#endif
++              restore_mstp_regs();
++      }
++
++      for (i = 0; i < ARRAY_SIZE(clks); ++i) {
++              clk = clk_get(NULL, clks[i]);
++              if (!IS_ERR(clk)) {
++                      clk_prepare_enable(clk);
++                      clk_put(clk);
++              }
++      }
++}
++
++const struct platform_hibernation_ops shmobile_hibernation_ops = {
++      .leave = shmobile_hibernation_leave,
++      .begin = shmobile_hibernation_begin,
++      .end = shmobile_hibernation_end,
++      .pre_snapshot = shmobile_hibernation_pre_snapshot,
++      .finish = shmobile_hibernation_finish,
++      .prepare = shmobile_hibernation_prepare,
++      .enter = shmobile_hibernation_enter,
++      .pre_restore = shmobile_hibernation_pre_restore,
++      .restore_cleanup = shmobile_hibernation_restore_cleanup,
++};
++
++int __init shmobile_hibernation_init(void)
++{
++      hibernation_set_ops(&shmobile_hibernation_ops);
++      return 0;
++}
++static int setup_swsusp_area(char *s)
++{
++      long tmp = 0;
++      char *p;
++      if (!kstrtol(s, 0, &tmp) && tmp > 0)
++              swsusp_area = tmp;
++      p = strchr(s, ',');
++      if (!p)
++              goto out;
++      if (!kstrtol(p, 0, &tmp) && tmp > 0)
++              swsusp_area_size = tmp;
++out:
++      return 1;
++}
++__setup("swsusp_area=", setup_swsusp_area);
++
+diff --git a/arch/arm/mach-shmobile/platsmp-apmu.c b/arch/arm/mach-shmobile/platsmp-apmu.c
+index cff7a25..e382e26 100644
+--- a/arch/arm/mach-shmobile/platsmp-apmu.c
++++ b/arch/arm/mach-shmobile/platsmp-apmu.c
+@@ -33,7 +33,10 @@
+ /* only enable the cluster that includes the boot CPU by default */
+ static bool enable_multicluster = false;
++#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_SUSPEND) || \
++defined(CONFIG_CPU_IDLE)
+ static bool is_last_cpu;
++#endif
+ static __init int apmu_setup(char *opt)
+ {
+@@ -71,12 +74,15 @@ static int __maybe_unused apmu_power_on(void __iomem *p, int bit)
+       return 0;
+ }
++#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_SUSPEND) || \
++defined(CONFIG_CPU_IDLE)
+ static int apmu_power_off(void __iomem *p, int bit)
+ {
+       /* request Core Standby for next WFI */
+       writel_relaxed(3, p + CPUNCR_OFFS(bit));
+       return 0;
+ }
++#endif
+ static int __maybe_unused apmu_power_off_poll(void __iomem *p, int bit)
+ {
+@@ -92,12 +98,15 @@ static int __maybe_unused apmu_power_off_poll(void __iomem *p, int bit)
+       return 0;
+ }
++#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_SUSPEND) || \
++defined(CONFIG_CPU_IDLE) || defined(CONFIG_SMP)
+ static int apmu_wrap(int cpu, int (*fn)(void __iomem *p, int cpu))
+ {
+       void __iomem *p = apmu_cpus[cpu].iomem;
+       return p ? fn(p, apmu_cpus[cpu].bit) : -EINVAL;
+ }
++#endif
+ static void apmu_init_cpu(struct resource *res, int cpu, int bit)
+ {
+@@ -141,7 +150,7 @@ static void apmu_parse_cfg(void (*fn)(struct resource *res, int cpu, int bit),
+       }
+ }
+-void __init shmobile_smp_apmu_prepare_cpus(unsigned int max_cpus,
++void shmobile_smp_apmu_prepare_cpus(unsigned int max_cpus,
+                                          struct rcar_apmu_config *apmu_config,
+                                          int num)
+ {
+@@ -328,7 +337,7 @@ static int __cpuinit shmobile_smp_apmu_enter_suspend(suspend_state_t state)
+       return 0;
+ }
+-void __init shmobile_smp_apmu_suspend_init(void)
++void shmobile_smp_apmu_suspend_init(void)
+ {
+       cpucmcr_ca7  = ioremap_nocache(CPUCMCR_CA7, 0x4);
+       cpucmcr_ca15 = ioremap_nocache(CPUCMCR_CA15, 0x4);
+diff --git a/arch/arm/mach-shmobile/platsmp-rst.c b/arch/arm/mach-shmobile/platsmp-rst.c
+index 70a2b6c..7ba9eeb 100644
+--- a/arch/arm/mach-shmobile/platsmp-rst.c
++++ b/arch/arm/mach-shmobile/platsmp-rst.c
+@@ -11,8 +11,7 @@
+ #include <linux/io.h>
+ #include <asm/smp_plat.h>
+ #include <mach/platsmp-rst.h>
+-
+-#define RST           0xe6160000
++#include "rcar-gen2.h"
+ #define r8a779x_clst_id(cpu) (cpu_logical_map((cpu)) >> 8)
+ #define r8a779x_cpu_id(cpu) (cpu_logical_map((cpu)) & 0xff)
+diff --git a/arch/arm/mach-shmobile/pm-r8a7791.c b/arch/arm/mach-shmobile/pm-r8a7791.c
+index f0ed98c..a13da84 100644
+--- a/arch/arm/mach-shmobile/pm-r8a7791.c
++++ b/arch/arm/mach-shmobile/pm-r8a7791.c
+@@ -21,16 +21,9 @@
+ #include <asm/io.h>
+ #include "common.h"
+ #include "pm-rcar.h"
++#include "rcar-gen2.h"
+ #include "r8a7791.h"
+-#define RST           0xe6160000
+-#define CA15BAR               0x0020
+-#define RAM           0xe63c0000
+-
+-/* SYSC */
+-#define SYSCIER 0x0c
+-#define SYSCIMR 0x10
+-
+ struct r8a7791_pm_domain {
+       struct generic_pm_domain genpd;
+       struct rcar_sysc_ch ch;
+@@ -43,13 +36,14 @@ static inline struct rcar_sysc_ch *to_r8a7791_ch(struct generic_pm_domain *d)
+ #if defined(CONFIG_PM) || defined(CONFIG_SMP)
+-static void __init r8a7791_sysc_init(void)
++static void r8a7791_sysc_init(void)
+ {
+-      void __iomem *base = rcar_sysc_init(0xe6180000);
++      void __iomem *base = rcar_sysc_init(SYSC_BASE);
+       /* enable all interrupt sources, but do not use interrupt handler */
+       iowrite32(0x0131000e, base + SYSCIER);
+-      iowrite32(0, base + SYSCIMR);
++      /* keep reserved bits as they are in TRM */
++      iowrite32(0x012001ec, base + SYSCIMR);
+ }
+ #else /* CONFIG_PM || CONFIG_SMP */
+@@ -60,9 +54,6 @@ static inline void r8a7791_sysc_init(void) {}
+ #ifdef CONFIG_PM
+-#define CPG_BASE 0xe6150000
+-#define CPG_LEN 0x1000
+-
+ /* Software Reset */
+ #define SRCR0         0x00a0
+ #define SRCR1         0x00a8
+@@ -243,14 +234,10 @@ static struct notifier_block platform_nb = {
+ #endif /* CONFIG_PM */
+-void __init r8a7791_pm_init(void)
++void r8a7791_pm_init(void)
+ {
+       void __iomem *p;
+       u32 bar;
+-      static int once;
+-
+-      if (once++)
+-              return;
+       /* RAM for jump stub, because BAR requires 256KB aligned address */
+       p = ioremap_nocache(RAM, shmobile_boot_size);
+@@ -258,7 +245,7 @@ void __init r8a7791_pm_init(void)
+       iounmap(p);
+       /* setup reset vectors */
+-      p = ioremap_nocache(RST, 0x63);
++      p = ioremap_nocache(RST, RST_LEN);
+       bar = (RAM >> 8) & 0xfffffc00;
+       writel_relaxed(bar, p + CA15BAR);
+       writel_relaxed(bar | 0x10, p + CA15BAR);
+diff --git a/arch/arm/mach-shmobile/rcar-gen2.h b/arch/arm/mach-shmobile/rcar-gen2.h
+index ce53cb5..df7201a 100644
+--- a/arch/arm/mach-shmobile/rcar-gen2.h
++++ b/arch/arm/mach-shmobile/rcar-gen2.h
+@@ -1,6 +1,45 @@
+ #ifndef __ASM_RCAR_GEN2_H__
+ #define __ASM_RCAR_GEN2_H__
++#define CPG_BASE      0xe6150000
++#define CPG_LEN               0x1000
++#define RMSTPCR0      0x110
++#define RMSTPCR1      0x114
++#define RMSTPCR2      0x118
++#define RMSTPCR3      0x11c
++#define RMSTPCR4      0x120
++#define RMSTPCR5      0x124
++#define RMSTPCR7      0x12c
++#define RMSTPCR8      0x980
++#define RMSTPCR9      0x984
++#define RMSTPCR10     0x988
++#define RMSTPCR11     0x98c
++#define SMSTPCR0      0x130
++#define SMSTPCR1      0x134
++#define SMSTPCR2      0x138
++#define SMSTPCR3      0x13c
++#define SMSTPCR4      0x140
++#define SMSTPCR5      0x144
++#define SMSTPCR7      0x14c
++#define SMSTPCR8      0x990
++#define SMSTPCR9      0x994
++#define SMSTPCR10     0x998
++#define SMSTPCR11     0x99c
++
++#define SYSC_BASE     0xe6180000
++#define SYSCIER               0x0c
++#define SYSCIMR               0x10
++
++#define RST           0xe6160000
++#define RST_LEN               0x64
++
++#define CA15BAR               0x0020
++#define CA7BAR                0x0030
++#define RAM           0xe63c0000
++
++#define CNTCR 0
++#define CNTFID0 0x20
++
+ void rcar_gen2_timer_init(void);
+ #define MD(nr) BIT(nr)
+ u32 rcar_gen2_read_mode_pins(void);
+diff --git a/arch/arm/mach-shmobile/setup-r8a7791.c b/arch/arm/mach-shmobile/setup-r8a7791.c
+index 2aa431a..c48c6a9 100644
+--- a/arch/arm/mach-shmobile/setup-r8a7791.c
++++ b/arch/arm/mach-shmobile/setup-r8a7791.c
+@@ -29,6 +29,7 @@
+ #include <linux/sh_timer.h>
+ #include <linux/spi/sh_msiof.h>
+ #include <asm/mach/arch.h>
++#include <asm/smp_plat.h>
+ #include "common.h"
+ #include "dma-register.h"
+@@ -243,8 +244,6 @@ static const struct resource powervr_resources[] __initconst = {
+                                       powervr_resources,              \
+                                       ARRAY_SIZE(powervr_resources))
+-#define CPG_BASE      0xe6150000
+-#define CPG_LEN               0x1000
+ #define RGXCR         0x0B4
+ void __init r8a7791_register_pvrsrvkm(void)
+@@ -271,7 +270,12 @@ void __init r8a7791_register_ssp(void)
+ void __init r8a7791_add_dt_devices(void)
+ {
+-      r8a7791_pm_init();
++#ifdef CONFIG_SMP
++      /* In case of SMP config pm_init already called from smp_prepare_cpus.
++       * It is still needed to call pm_init if 'nosmp' was given */
++      if (!setup_max_cpus)
++#endif
++              r8a7791_pm_init();
+       r8a7791_init_pm_domains();
+       r8a7791_register_cmt(00);
+       r8a7791_register_pvrsrvkm();
+diff --git a/arch/arm/mach-shmobile/setup-rcar-gen2.c b/arch/arm/mach-shmobile/setup-rcar-gen2.c
+index da16ebd..641ee1d 100644
+--- a/arch/arm/mach-shmobile/setup-rcar-gen2.c
++++ b/arch/arm/mach-shmobile/setup-rcar-gen2.c
+@@ -47,9 +47,6 @@ u32 rcar_gen2_read_mode_pins(void)
+       return mode;
+ }
+-#define CNTCR 0
+-#define CNTFID0 0x20
+-
+ void __init rcar_gen2_timer_init(void)
+ {
+ #if defined(CONFIG_ARM_ARCH_TIMER) || defined(CONFIG_COMMON_CLK)
+@@ -58,7 +55,7 @@ void __init rcar_gen2_timer_init(void)
+ #ifdef CONFIG_ARM_ARCH_TIMER
+       void __iomem *base;
+       int extal_mhz = 0;
+-      u32 freq;
++      u32 rcar_gen2_archtimer_freq;
+       /* At Linux boot time the r8a7790 arch timer comes up
+        * with the counter disabled. Moreover, it may also report
+@@ -83,7 +80,7 @@ void __init rcar_gen2_timer_init(void)
+       }
+       /* The arch timer frequency equals EXTAL / 2 */
+-      freq = extal_mhz * (1000000 / 2);
++      rcar_gen2_archtimer_freq = extal_mhz * (1000000 / 2);
+       /* Remap "armgcnt address map" space */
+       base = ioremap(0xe6080000, PAGE_SIZE);
+@@ -96,10 +93,11 @@ void __init rcar_gen2_timer_init(void)
+        */
+       if ((ioread32(base + CNTCR) & 1) == 0 ||
+-          ioread32(base + CNTFID0) != freq) {
++          ioread32(base + CNTFID0) != rcar_gen2_archtimer_freq) {
+               /* Update registers with correct frequency */
+-              iowrite32(freq, base + CNTFID0);
+-              asm volatile("mcr p15, 0, %0, c14, c0, 0" : : "r" (freq));
++              iowrite32(rcar_gen2_archtimer_freq, base + CNTFID0);
++              asm volatile("mcr p15, 0, %0, c14, c0, 0" : : "r"
++                              (rcar_gen2_archtimer_freq));
+               /* make sure arch timer is started by setting bit 0 of CNTCR */
+               iowrite32(1, base + CNTCR);
+diff --git a/arch/arm/mach-shmobile/smp-r8a7791.c b/arch/arm/mach-shmobile/smp-r8a7791.c
+index 24cad9f..4583cb6 100644
+--- a/arch/arm/mach-shmobile/smp-r8a7791.c
++++ b/arch/arm/mach-shmobile/smp-r8a7791.c
+@@ -33,6 +33,11 @@
+ #define CA15RESCNT    0x0040
++static struct rcar_sysc_ch r8a7791_ca15_scu = {
++      .chan_offs = 0x180, /* PWRSR5 .. PWRER5 */
++      .isr_bit = 12, /* CA15-SCU */
++};
++
+ static struct rcar_apmu_config r8a7791_apmu_config[] = {
+       {
+               .iomem = DEFINE_RES_MEM(0xe6152000, 0x88),
+@@ -47,7 +52,7 @@ static struct rcar_rst_config r8a7791_rst_config[] = {
+       }
+ };
+-static void __init r8a7791_smp_prepare_cpus(unsigned int max_cpus)
++static void r8a7791_smp_prepare_cpus(unsigned int max_cpus)
+ {
+       void __iomem *p;
+       u32 val;
+@@ -67,6 +72,7 @@ static void __init r8a7791_smp_prepare_cpus(unsigned int max_cpus)
+       }
+       r8a7791_pm_init();
++      rcar_sysc_power_up(&r8a7791_ca15_scu);
+       /* keep secondary CPU cores in reset */
+       r8a779x_init_reset(r8a7791_rst_config);
+diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
+index 19da841..35c9048 100644
+--- a/arch/arm/mm/proc-v7.S
++++ b/arch/arm/mm/proc-v7.S
+@@ -92,48 +92,59 @@ ENDPROC(cpu_v7_dcache_clean_area)
+ /* Suspend/resume support: derived from arch/arm/mach-s5pv210/sleep.S */
+ .globl        cpu_v7_suspend_size
+-.equ  cpu_v7_suspend_size, 4 * 8
++.equ  cpu_v7_suspend_size, 4 * 9
+ #ifdef CONFIG_ARM_CPU_SUSPEND
+ ENTRY(cpu_v7_do_suspend)
+       stmfd   sp!, {r4 - r10, lr}
+       mrc     p15, 0, r4, c13, c0, 0  @ FCSE/PID
+       mrc     p15, 0, r5, c13, c0, 3  @ User r/o thread ID
+       stmia   r0!, {r4 - r5}
++#ifdef CONFIG_MMU
+       mrc     p15, 0, r6, c3, c0, 0   @ Domain ID
++#ifdef CONFIG_ARM_LPAE
++      mrrc    p15, 1, r5, r7, c2      @ TTB 1
++#else
+       mrc     p15, 0, r7, c2, c0, 1   @ TTB 1
++#endif
+       mrc     p15, 0, r11, c2, c0, 2  @ TTB control register
++#endif
+       mrc     p15, 0, r8, c1, c0, 0   @ Control register
+       mrc     p15, 0, r9, c1, c0, 1   @ Auxiliary control register
+       mrc     p15, 0, r10, c1, c0, 2  @ Co-processor access control
+-      stmia   r0, {r6 - r11}
++      stmia   r0, {r5 - r11}
+       ldmfd   sp!, {r4 - r10, pc}
+ ENDPROC(cpu_v7_do_suspend)
+ ENTRY(cpu_v7_do_resume)
+       mov     ip, #0
+-      mcr     p15, 0, ip, c8, c7, 0   @ invalidate TLBs
+       mcr     p15, 0, ip, c7, c5, 0   @ invalidate I cache
+       mcr     p15, 0, ip, c13, c0, 1  @ set reserved context ID
+       ldmia   r0!, {r4 - r5}
+       mcr     p15, 0, r4, c13, c0, 0  @ FCSE/PID
+       mcr     p15, 0, r5, c13, c0, 3  @ User r/o thread ID
+-      ldmia   r0, {r6 - r11}
++      ldmia   r0, {r5 - r11}
++#ifdef CONFIG_MMU
++      mcr     p15, 0, ip, c8, c7, 0   @ invalidate TLBs
+       mcr     p15, 0, r6, c3, c0, 0   @ Domain ID
+-#ifndef CONFIG_ARM_LPAE
++#ifdef CONFIG_ARM_LPAE
++      mcrr    p15, 0, r1, ip, c2      @ TTB 0
++      mcrr    p15, 1, r5, r7, c2      @ TTB 1
++#else
+       ALT_SMP(orr     r1, r1, #TTB_FLAGS_SMP)
+       ALT_UP(orr      r1, r1, #TTB_FLAGS_UP)
+-#endif
+       mcr     p15, 0, r1, c2, c0, 0   @ TTB 0
+       mcr     p15, 0, r7, c2, c0, 1   @ TTB 1
++#endif
+       mcr     p15, 0, r11, c2, c0, 2  @ TTB control register
+-      mrc     p15, 0, r4, c1, c0, 1   @ Read Auxiliary control register
+-      teq     r4, r9                  @ Is it already set?
+-      mcrne   p15, 0, r9, c1, c0, 1   @ No, so write it
+-      mcr     p15, 0, r10, c1, c0, 2  @ Co-processor access control
+       ldr     r4, =PRRR               @ PRRR
+       ldr     r5, =NMRR               @ NMRR
+       mcr     p15, 0, r4, c10, c2, 0  @ write PRRR
+       mcr     p15, 0, r5, c10, c2, 1  @ write NMRR
++#endif        /* CONFIG_MMU */
++      mrc     p15, 0, r4, c1, c0, 1   @ Read Auxiliary control register
++      teq     r4, r9                  @ Is it already set?
++      mcrne   p15, 0, r9, c1, c0, 1   @ No, so write it
++      mcr     p15, 0, r10, c1, c0, 2  @ Co-processor access control
+       isb
+       dsb
+       mov     r0, r8                  @ control register
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0003-Add-sata-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0003-Add-sata-hibernation-code.patch
new file mode 100755 (executable)
index 0000000..fd0dfb6
--- /dev/null
@@ -0,0 +1,56 @@
+From 5d87144a96085d74b6002bd6d8c093c37bf128b7 Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:04:33 +0900
+Subject: [PATCH 03/15] Add sata hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/ata/sata_rcar.c | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c
+index 92abfdd..4c82b5e 100644
+--- a/drivers/ata/sata_rcar.c
++++ b/drivers/ata/sata_rcar.c
+@@ -1003,9 +1003,38 @@ static int sata_rcar_resume(struct device *dev)
+       return 0;
+ }
++static int sata_rcar_restore(struct device *dev)
++{
++      struct ata_host *host = dev_get_drvdata(dev);
++      struct sata_rcar_priv *priv = host->private_data;
++      int ret;
++
++      clk_prepare_enable(priv->clk);
++
++      ret = sata_rcar_setup_port(host);
++      if (ret)
++              goto cleanup;
++
++      /* initialize host controller */
++      sata_rcar_init_controller(host);
++
++      ata_host_resume(host);
++
++      return 0;
++
++cleanup:
++      clk_disable_unprepare(priv->clk);
++
++      return ret;
++}
++
+ static const struct dev_pm_ops sata_rcar_pm_ops = {
+       .suspend        = sata_rcar_suspend,
+       .resume         = sata_rcar_resume,
++      .freeze         = sata_rcar_suspend,
++      .restore        = sata_rcar_restore,
++      .thaw           = sata_rcar_resume,
++      .poweroff       = sata_rcar_suspend
+ };
+ #endif
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0004-Add-firmware-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0004-Add-firmware-hibernation-code.patch
new file mode 100755 (executable)
index 0000000..d025076
--- /dev/null
@@ -0,0 +1,25 @@
+From 33d4c0afe2a4e39c0afdc993f28a8d2d6228df01 Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:31:24 +0900
+Subject: [PATCH 04/15] Add firmware hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/base/firmware_class.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
+index 01e2103..6123148 100644
+--- a/drivers/base/firmware_class.c
++++ b/drivers/base/firmware_class.c
+@@ -1464,6 +1464,7 @@ static int fw_pm_notify(struct notifier_block *notify_block,
+       switch (mode) {
+       case PM_HIBERNATION_PREPARE:
+       case PM_SUSPEND_PREPARE:
++      case PM_RESTORE_PREPARE:
+               device_cache_fw_images();
+               break;
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0005-Add-rcar-dma-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0005-Add-rcar-dma-hibernation-code.patch
new file mode 100755 (executable)
index 0000000..b446fa3
--- /dev/null
@@ -0,0 +1,113 @@
+From c094e905cb0f542acdeb5d7009ab9edc812897f7 Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:32:30 +0900
+Subject: [PATCH 05/15] Add rcar-dma hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/dma/sh/rcar-dmac.c | 27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c
+index e5e60ee..3b4a684 100644
+--- a/drivers/dma/sh/rcar-dmac.c
++++ b/drivers/dma/sh/rcar-dmac.c
+@@ -121,6 +121,7 @@ struct rcar_dmac_desc_page {
+  * struct rcar_dmac_chan - R-Car Gen2 DMA Controller Channel
+  * @chan: base DMA channel object
+  * @iomem: channel I/O memory base
++ * @backup: channel I/O memory backup base
+  * @index: index of this channel in the controller
+  * @src_xfer_size: size (in bytes) of hardware transfers on the source side
+  * @dst_xfer_size: size (in bytes) of hardware transfers on the destination side
+@@ -140,6 +141,7 @@ struct rcar_dmac_desc_page {
+ struct rcar_dmac_chan {
+       struct dma_chan chan;
+       void __iomem *iomem;
++      void *backup;
+       unsigned int index;
+       unsigned int src_xfer_size;
+@@ -171,6 +173,7 @@ struct rcar_dmac_chan {
+  * @engine: base DMA engine object
+  * @dev: the hardware device
+  * @iomem: remapped I/O memory base
++ * @backup: remapped I/O memory backup base
+  * @n_channels: number of available channels
+  * @channels: array of DMAC channels
+  * @modules: bitmask of client modules in use
+@@ -179,6 +182,7 @@ struct rcar_dmac {
+       struct dma_device engine;
+       struct device *dev;
+       void __iomem *iomem;
++      void *backup;
+       unsigned int n_channels;
+       struct rcar_dmac_chan *channels;
+@@ -277,6 +281,7 @@ static void rcar_dmac_write(struct rcar_dmac *dmac, u32 reg, u32 data)
+               writew(data, dmac->iomem + reg);
+       else
+               writel(data, dmac->iomem + reg);
++      writel(data, dmac->backup + reg);
+ }
+ static u32 rcar_dmac_read(struct rcar_dmac *dmac, u32 reg)
+@@ -301,6 +306,7 @@ static void rcar_dmac_chan_write(struct rcar_dmac_chan *chan, u32 reg, u32 data)
+               writew(data, chan->iomem + reg);
+       else
+               writel(data, chan->iomem + reg);
++      writel(data, chan->backup + reg);
+ }
+ /* -----------------------------------------------------------------------------
+@@ -1548,10 +1554,25 @@ static int rcar_dmac_runtime_resume(struct device *dev)
+ }
+ #endif
++static int rcar_dmac_freeze(struct device *dev)
++{
++      return 0;
++}
++
++static int rcar_dmac_restore(struct device *dev)
++{
++      int ret;
++      struct rcar_dmac *dmac = dev_get_drvdata(dev);
++      ret = rcar_dmac_init(dmac);
++      return ret;
++}
++
+ static const struct dev_pm_ops rcar_dmac_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(rcar_dmac_sleep_suspend, rcar_dmac_sleep_resume)
+       SET_RUNTIME_PM_OPS(rcar_dmac_runtime_suspend, rcar_dmac_runtime_resume,
+                          NULL)
++      .freeze = rcar_dmac_freeze,
++      .restore = rcar_dmac_restore,
+ };
+ /* -----------------------------------------------------------------------------
+@@ -1571,6 +1592,7 @@ static int rcar_dmac_chan_probe(struct rcar_dmac *dmac,
+       rchan->index = index;
+       rchan->iomem = dmac->iomem + RCAR_DMAC_CHAN_OFFSET(index);
++      rchan->backup = dmac->backup + RCAR_DMAC_CHAN_OFFSET(index);
+       rchan->mid_rid = -EINVAL;
+       spin_lock_init(&rchan->lock);
+@@ -1657,8 +1679,13 @@ static int rcar_dmac_probe(struct platform_device *pdev)
+       /* Request resources. */
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       dmac->iomem = devm_ioremap_resource(&pdev->dev, mem);
++      dmac->backup = devm_kzalloc(&pdev->dev, resource_size(mem), GFP_KERNEL);
+       if (IS_ERR(dmac->iomem))
+               return PTR_ERR(dmac->iomem);
++      dmac->backup = devm_kzalloc(&pdev->dev, resource_size(mem), GFP_KERNEL);
++      if (IS_ERR(dmac->backup)) {
++              return PTR_ERR(dmac->backup);
++      }
+       irq = platform_get_irq_byname(pdev, "error");
+       if (irq < 0) {
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0006-Add-rcar-du-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0006-Add-rcar-du-hibernation-code.patch
new file mode 100755 (executable)
index 0000000..8942ed4
--- /dev/null
@@ -0,0 +1,127 @@
+From 4a9a11deb2e83549d2e77cac129f879a0000ef7e Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:33:54 +0900
+Subject: [PATCH 06/15] Add rcar-du hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/gpu/drm/rcar-du/rcar_du_drv.c | 68 ++++++++++++++++++++++++++++++++++-
+ 1 file changed, 67 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+index 53f1f6a..fbb212c 100644
+--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
++++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+@@ -442,6 +442,15 @@ static int rcar_du_pm_suspend(struct device *dev)
+       drm_kms_helper_poll_disable(rcdu->ddev);
++#ifdef CONFIG_MACH_FTEN
++      list_for_each_entry(encoder,
++                       &rcdu->ddev->mode_config.encoder_list, head) {
++              if ((encoder->encoder_type == DRM_MODE_ENCODER_TVDAC) &&
++                      (get_rcar_slave_funcs(encoder)->dpms))
++                      get_rcar_slave_funcs(encoder)->dpms(encoder,
++                                               DRM_MODE_DPMS_SUSPEND);
++      }
++#else
+ #if defined(CONFIG_DRM_ADV7511) || defined(CONFIG_DRM_ADV7511_MODULE)
+       list_for_each_entry(encoder,
+                        &rcdu->ddev->mode_config.encoder_list, head) {
+@@ -451,6 +460,8 @@ static int rcar_du_pm_suspend(struct device *dev)
+                                                DRM_MODE_DPMS_OFF);
+       }
+ #endif
++#endif
++
+ #ifdef CONFIG_DRM_RCAR_LVDS
+       for (i = 0; i < rcdu->info->num_lvds; ++i) {
+               if (rcdu->lvds[i])
+@@ -483,6 +494,15 @@ static int rcar_du_pm_resume(struct device *dev)
+       }
+ #endif
++#ifdef CONFIG_MACH_FTEN
++      list_for_each_entry(encoder,
++                       &rcdu->ddev->mode_config.encoder_list, head) {
++              if ((encoder->encoder_type == DRM_MODE_ENCODER_TVDAC) &&
++                      (get_rcar_slave_funcs(encoder)->dpms))
++                      get_rcar_slave_funcs(encoder)->dpms(encoder,
++                                               DRM_MODE_DPMS_ON);
++      }
++#else
+ #if defined(CONFIG_DRM_ADV7511) || defined(CONFIG_DRM_ADV7511_MODULE)
+       list_for_each_entry(encoder,
+                        &rcdu->ddev->mode_config.encoder_list, head) {
+@@ -492,14 +512,53 @@ static int rcar_du_pm_resume(struct device *dev)
+                                                DRM_MODE_DPMS_ON);
+       }
+ #endif
++#endif
+       drm_kms_helper_poll_enable(rcdu->ddev);
+       return 0;
+ }
+-#endif
++#ifdef CONFIG_MACH_FTEN
++static int rcar_du_pm_freeze(struct device *dev)
++{
++      int ret;
++
++      ret = rcar_du_pm_suspend(dev);
++      return ret;
++}
++
++static int rcar_du_pm_thaw(struct device *dev)
++{
++      int ret;
++      ret = rcar_du_pm_resume(dev);
++      return ret;
++}
++
++static int rcar_du_pm_restore(struct device *dev)
++{
++      int i, ret;
++      struct rcar_du_device *rcdu = dev_get_drvdata(dev);
++
++      ret = rcar_du_pm_resume(dev);
++      for (i = 0; i < rcdu->pdata->num_crtcs; ++i)
++              rcar_du_crtc_enable_vblank(&rcdu->crtcs[i],
++                      rcdu->crtcs[i].vblank_enable);
++      return ret;
++}
++#endif
++#endif
+ static const struct dev_pm_ops rcar_du_pm_ops = {
++#if defined(CONFIG_MACH_FTEN) && defined(CONFIG_HIBERNATION) && \
++      defined(CONFIG_PM_SLEEP)
++      .suspend = rcar_du_pm_suspend,
++      .resume = rcar_du_pm_resume,
++      .freeze = rcar_du_pm_freeze,
++      .thaw = rcar_du_pm_thaw,
++      .poweroff = rcar_du_pm_suspend,
++      .restore = rcar_du_pm_restore,
++#else
+       SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume)
++#endif
+ };
+ /* -----------------------------------------------------------------------------
+@@ -620,6 +679,13 @@ static const struct rcar_du_device_info rcar_du_r8a7791_info = {
+                       .possible_clones = 0,
+                       .encoder_type = DRM_MODE_ENCODER_NONE,
+               },
++#if defined(CONFIG_MACH_FTEN)
++              [RCAR_DU_OUTPUT_COMPOSITE] = {
++                      .possible_crtcs = BIT(1),
++                      .possible_clones = 0,
++                      .encoder_type = DRM_MODE_ENCODER_TVDAC,
++              },
++#endif
+       },
+       .num_lvds = 1,
+       .drgbs_bit = 1,
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0007-Add-rcar-i2c-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0007-Add-rcar-i2c-hibernation-code.patch
new file mode 100755 (executable)
index 0000000..bba1eb4
--- /dev/null
@@ -0,0 +1,69 @@
+From 6c133013b75d88d5b4514dfecb3089f830b82d65 Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:35:37 +0900
+Subject: [PATCH 07/15] Add rcar-i2c hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/i2c/busses/i2c-rcar.c | 38 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 38 insertions(+)
+
+diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
+index 8242002..c6a5a4b 100644
+--- a/drivers/i2c/busses/i2c-rcar.c
++++ b/drivers/i2c/busses/i2c-rcar.c
+@@ -754,6 +754,43 @@ static int rcar_i2c_probe(struct platform_device *pdev)
+       return 0;
+ }
++static int rcar_i2c_suspend(struct device *dev)
++{
++      struct platform_device *pdev = to_platform_device(dev);
++      struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
++      pr_debug("suspend: i2c adapter name %s", priv->adap.name);
++      pr_debug("suspend: ICSCR: %08x\n", readl(priv->io + ICSCR));
++      pr_debug("suspend: ICMCR: %08x\n", readl(priv->io + ICMCR));
++      pr_debug("suspend: ICSSR: %08x\n", readl(priv->io + ICSSR));
++      pr_debug("suspend: ICMSR: %08x\n", readl(priv->io + ICMSR));
++      pr_debug("suspend: ICSIER: %08x\n", readl(priv->io + ICSIER));
++      pr_debug("suspend: ICMIER: %08x\n", readl(priv->io + ICMIER));
++      pr_debug("suspend: ICCCR: %08x\n", readl(priv->io + ICCCR));
++      pr_debug("suspend: ICSAR: %08x\n", readl(priv->io + ICSAR));
++      pr_debug("suspend: ICMAR: %08x\n", readl(priv->io + ICMAR));
++      clk_disable(priv->clk);
++      return 0;
++}
++static int rcar_i2c_resume(struct device *dev)
++{
++      struct platform_device *pdev = to_platform_device(dev);
++      struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
++      clk_enable(priv->clk);
++      pr_debug("resume: i2c adapter name %s", priv->adap.name);
++      pr_debug("resume: ICSCR: %08x\n", readl(priv->io + ICSCR));
++      pr_debug("resume: ICMCR: %08x\n", readl(priv->io + ICMCR));
++      pr_debug("resume: ICSSR: %08x\n", readl(priv->io + ICSSR));
++      pr_debug("resume: ICMSR: %08x\n", readl(priv->io + ICMSR));
++      pr_debug("resume: ICSIER: %08x\n", readl(priv->io + ICSIER));
++      pr_debug("resume: ICMIER: %08x\n", readl(priv->io + ICMIER));
++      pr_debug("resume: ICCCR: %08x\n", readl(priv->io + ICCCR));
++      pr_debug("resume: ICSAR: %08x\n", readl(priv->io + ICSAR));
++      pr_debug("resume: ICMAR: %08x\n", readl(priv->io + ICMAR));
++      return 0;
++}
++static const struct dev_pm_ops rcar_i2c_pm_ops = {
++      SET_SYSTEM_SLEEP_PM_OPS(rcar_i2c_suspend, rcar_i2c_resume)
++};
+ static int rcar_i2c_remove(struct platform_device *pdev)
+ {
+@@ -780,6 +817,7 @@ static struct platform_driver rcar_i2c_driver = {
+               .name   = "i2c-rcar",
+               .owner  = THIS_MODULE,
+               .of_match_table = rcar_i2c_dt_ids,
++              .pm = &rcar_i2c_pm_ops,
+       },
+       .probe          = rcar_i2c_probe,
+       .remove         = rcar_i2c_remove,
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0008-Add-rcar-mmc-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0008-Add-rcar-mmc-hibernation-code.patch
new file mode 100755 (executable)
index 0000000..34b40a1
--- /dev/null
@@ -0,0 +1,414 @@
+From 9d1d9be70ed3cf6670ae12a1caed337833f7bba8 Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:38:11 +0900
+Subject: [PATCH 08/15] Add rcar mmc hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/mmc/host/sh_mmcif.c       |  65 +++++++++++++++++++++-
+ drivers/mmc/host/sh_mobile_sdhi.c | 112 +++++++++++++++++++++++++++++++++++++-
+ drivers/mmc/host/tmio_mmc.h       |   1 +
+ drivers/mmc/host/tmio_mmc_pio.c   |  49 ++++++++++++-----
+ 4 files changed, 210 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
+index 7290e6e..4ecf62c 100644
+--- a/drivers/mmc/host/sh_mmcif.c
++++ b/drivers/mmc/host/sh_mmcif.c
+@@ -232,6 +232,7 @@ struct sh_mmcif_host {
+       struct platform_device *pd;
+       struct clk *hclk;
+       unsigned int clk;
++      int clkrate;
+       int bus_width;
+       unsigned char timing;
+       bool sd_error;
+@@ -257,6 +258,8 @@ struct sh_mmcif_host {
+       struct dma_chan         *chan_tx;
+       struct completion       dma_complete;
+       bool                    dma_active;
++#define N_REGS                10
++      u32                     regs[N_REGS];
+ };
+ static inline void sh_mmcif_bitset(struct sh_mmcif_host *host,
+@@ -1457,6 +1460,8 @@ static int sh_mmcif_probe(struct platform_device *pdev)
+               }
+       }
++      host->clkrate = clk_get_rate(host->hclk);
++
+       ret = sh_mmcif_clk_update(host);
+       if (ret < 0)
+               goto eclkupdate;
+@@ -1503,6 +1508,7 @@ static int sh_mmcif_probe(struct platform_device *pdev)
+       dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION);
+       dev_dbg(&pdev->dev, "chip ver H'%04x\n",
+               sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff);
++      device_enable_async_suspend(&pdev->dev);
+       return ret;
+ emmcaddh:
+@@ -1574,15 +1580,68 @@ static int sh_mmcif_suspend(struct device *dev)
+       sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
+       pm_runtime_put(dev);
+-      return 0;
++      return mmc_suspend_host(host->mmc);
+ }
+ static int sh_mmcif_resume(struct device *dev)
+ {
+-      return 0;
++      struct sh_mmcif_host *host = dev_get_drvdata(dev);
++      return mmc_resume_host(host->mmc);
++}
++#endif
++
++#ifdef CONFIG_PM
++static int sh_mmcif_restore(struct device *dev)
++{
++      struct sh_mmcif_host *host = dev_get_drvdata(dev);
++      int ret;
++      ret = clk_set_rate(host->hclk, host->clkrate);
++      if (ret < 0)
++              goto eclkupdate;
++      ret = sh_mmcif_clk_update(host);
++      if (ret < 0)
++              goto eclkupdate;
++      ret = pm_runtime_resume(dev);
++      if (ret < 0)
++              goto eresume;
++      sh_mmcif_sync_reset(host);
++#ifdef CONFIG_MACH_FTEN
++      sh_mmcif_writel(host->addr, 0x00000080, 0x00000100);
++#endif
++      sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
++      clk_disable_unprepare(host->hclk);
++      dev_info(dev, "restore: chip ver H'%04x\n",
++              sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff);
++      sh_mmcif_writel(host->addr, MMCIF_CE_CMD_CTRL, host->regs[0]);
++      sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, host->regs[1]);
++      sh_mmcif_writel(host->addr, MMCIF_CE_CLK_CTRL, host->regs[2]);
++      sh_mmcif_writel(host->addr, MMCIF_CE_BUF_ACC, host->regs[3]);
++      sh_mmcif_release_dma(host);
++      return mmc_resume_host(host->mmc);
++eclkupdate:
++      pr_info("Can't set clock\n");
++      return -EINVAL;
++eresume:
++      pr_info("Can't resume PM\n");
++      return -ENODEV;
+ }
++
++static int sh_mmcif_freeze(struct device *dev)
++{
++      struct sh_mmcif_host *host = dev_get_drvdata(dev);
++      int ret = mmc_suspend_host(host->mmc);
++      host->regs[0] = sh_mmcif_readl(host->addr, MMCIF_CE_CMD_CTRL);
++      host->regs[1] = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET);
++      host->regs[2] = sh_mmcif_readl(host->addr, MMCIF_CE_CLK_CTRL);
++      host->regs[3] = sh_mmcif_readl(host->addr, MMCIF_CE_BUF_ACC);
++      return ret;
++}
++#else
++#define sh_mmcif_restore NULL
++#define sh_mmcif_freeze NULL
+ #endif
++
+ static const struct of_device_id mmcif_of_match[] = {
+       { .compatible = "renesas,sh-mmcif" },
+       { }
+@@ -1591,6 +1650,8 @@ MODULE_DEVICE_TABLE(of, mmcif_of_match);
+ static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume)
++      .restore = sh_mmcif_restore,
++      .freeze = sh_mmcif_freeze,
+ };
+ static struct platform_driver sh_mmcif_driver = {
+diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
+index 1b59cdf..c7f3abf 100644
+--- a/drivers/mmc/host/sh_mobile_sdhi.c
++++ b/drivers/mmc/host/sh_mobile_sdhi.c
+@@ -156,6 +156,8 @@ struct sh_mobile_sdhi {
+       struct tmio_mmc_dma dma_priv;
+       unsigned int type;
+       struct sh_mobile_sdhi_vlt vlt;
++      int wifi_xrst;
++      int save_clk_rate;
+ };
+ static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f)
+@@ -647,6 +649,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
+                       if (ret < 0)
+                               dev_err(&pdev->dev,
+                                       "cannot set clock rate: %d\n", ret);
++                      priv->save_clk_rate = clk_rate;
+                       clk_disable_unprepare(priv->clk);
+               }
+@@ -841,6 +844,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
+               }
+       }
++      device_enable_async_suspend(&pdev->dev);
+       dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
+                mmc_hostname(host->mmc), (unsigned long)
+                (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
+@@ -865,17 +869,123 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
+       struct mmc_host *mmc = platform_get_drvdata(pdev);
+       struct tmio_mmc_host *host = mmc_priv(mmc);
+       struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
++#ifdef CONFIG_MACH_FTEN_DT
++      int ret;
++      struct sh_mobile_sdhi *priv = container_of(host->pdata,
++                                                 struct sh_mobile_sdhi,
++                                                 mmc_data);
++#endif
+       tmio_mmc_host_remove(host);
+       if (p && p->cleanup)
+               p->cleanup(pdev);
++#ifdef CONFIG_MACH_FTEN_DT
++      ret = gpio_request(priv->wifi_xrst, "sh_mobile_sdhi");
++      if (ret != 0) {
++              dev_err(&pdev->dev,
++                      "gpio_request(%d) failed(%d) remove\n",
++                      priv->wifi_xrst, ret);
++              goto skip_wifi;
++      }
++      ret = gpio_direction_output(priv->wifi_xrst, 0);
++      if (ret != 0) {
++              dev_err(&pdev->dev,
++                      "gpio_direction_output(%d) failed(%d) remove\n",
++                      priv->wifi_xrst, ret);
++      }
++      gpio_free(priv->wifi_xrst);
++skip_wifi:
++#endif
++
++      return 0;
++}
++
++static int sh_mobile_sdhi_restore_noirq(struct device *dev)
++{
++      struct mmc_host *mmc = dev_get_drvdata(dev);
++      struct tmio_mmc_host *host = mmc_priv(mmc);
++
++      sd_ctrl_write32(host, CTL_IRQ_MASK, 0x8b7f031d);
++      sd_ctrl_write32(host, CTL_STATUS, 0);
++#if 0
++      sh_mobile_sdhi_enable_sdbuf_acc32(host, false);
++      /* FIXME - should we set stop clock reg here */
++      sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
++      /* implicit BUG_ON(!res) */
++      if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
++              sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
++      msleep(2);
++      sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
++      if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
++              sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
++      msleep(2);
++      sd_ctrl_write32(host, CTL_IRQ_MASK, 0x8b7f031d);
++      sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0040);
++      sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80E0);
++      sd_ctrl_write16(host, CTL_DMA_ENABLE, 0x1002);
++#endif
++      return 0;
++}
++
++static int sh_mobile_sdhi_restore(struct device *dev)
++{
++      struct mmc_host *mmc = dev_get_drvdata(dev);
++      struct tmio_mmc_host *host = mmc_priv(mmc);
++      struct sh_mobile_sdhi *priv = container_of(host->pdata,
++                                                 struct sh_mobile_sdhi,
++                                                 mmc_data);
++#if defined(CONFIG_MACH_FTEN_DT) || defined(CONFIG_PM_SLEEP)
++      int ret;
++#endif
++      int dma_size;
++      host->restore = true;
++
++#ifdef CONFIG_MACH_FTEN_DT
++      /* priv->wifi_xrst is 0 or more. */
++      if (priv->wifi_xrst >= 0) {
++              ret = gpio_request(priv->wifi_xrst, "sh_mobile_sdhi");
++              if (ret != 0) {
++                      dev_err(dev, "gpio_request(%d) failed(%d) restore\n",
++                              priv->wifi_xrst, ret);
++                      goto skip_wifi;
++              }
++              ret = gpio_direction_output(priv->wifi_xrst, 1);
++              if (ret != 0) {
++                      dev_err(dev, "gpio_direction_output(%d) failed(%d) restore\n",
++                              priv->wifi_xrst, ret);
++              }
++              gpio_free(priv->wifi_xrst);
++      }
++skip_wifi:
++#endif
++
++      dma_size = sh_mobile_sdhi_get_xmit_size(priv->type,
++                              priv->dma_priv.alignment_shift);
++
++      sd_ctrl_write16(host, SD_DMACR(priv->type), dma_size);
++
++#ifdef CONFIG_PM_SLEEP
++      ret = tmio_mmc_host_resume(dev);
++      host->restore = false;
++      return ret;
++#else
++      host->restore = false;
+       return 0;
++#endif
+ }
+ static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
+-      SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_host_suspend, tmio_mmc_host_resume)
++#ifdef CONFIG_PM_SLEEP
++      .suspend        = tmio_mmc_host_suspend,
++      .resume         = tmio_mmc_host_resume,
++      .freeze         = tmio_mmc_host_suspend,
++      .thaw           = tmio_mmc_host_resume,
++      .poweroff       = tmio_mmc_host_suspend,
++#endif
++      .restore        = sh_mobile_sdhi_restore,
++      .restore_noirq  = sh_mobile_sdhi_restore_noirq,
+       SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
+                       tmio_mmc_host_runtime_resume,
+                       NULL)
+diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
+index c5b12ad..3efe03d 100644
+--- a/drivers/mmc/host/tmio_mmc.h
++++ b/drivers/mmc/host/tmio_mmc.h
+@@ -104,6 +104,7 @@ struct tmio_mmc_host {
+       bool                    resuming;
+       bool                    done_tuning;
+       struct completion       completion;
++      bool                    restore;
+ };
+ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
+diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
+index 09c0c08..514af15 100644
+--- a/drivers/mmc/host/tmio_mmc_pio.c
++++ b/drivers/mmc/host/tmio_mmc_pio.c
+@@ -167,8 +167,20 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
+       if (host->set_clk_div)
+               host->set_clk_div(host->pdev, (clk>>22) & 1);
++#ifdef CONFIG_MACH_FTEN
++      clk |= sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL) & 0x0100;
++      if (host->pdata->flags & TMIO_MMC_SDCLK_AUTO_CONTROL &&
++          new_clock > host->mmc->f_init)
++              clk |= SDCLKOFFEN;
++      dev_dbg(&host->pdev->dev,
++              "clock=%d, clk=%08x, new_clock=%d, f_init=%d\n",
++              clock, clk, new_clock, host->mmc->f_init);
++      sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & 0x3ff);
++#else
+       sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & 0x1ff);
+-      msleep(10);
++#endif
++      if (!host->restore)
++              msleep(2);
+ }
+ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
+@@ -176,13 +188,15 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
+       /* implicit BUG_ON(!res) */
+       if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
+               sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
+-              if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP))
++              if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP)
++                      && !host->restore)
+                       msleep(10);
+       }
+       sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
+               sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+-      if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP))
++      if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP)
++              && !host->restore)
+               msleep(10);
+ }
+@@ -190,14 +204,16 @@ static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
+ {
+       sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
+               sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+-      if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP))
+-              msleep(10);
++      if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP)
++              && !host->restore)
++              msleep(2);
+       /* implicit BUG_ON(!res) */
+       if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
+               sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
+-              if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP))
+-                      msleep(10);
++              if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP)
++                      && !host->restore)
++                      msleep(2);
+       }
+ }
+@@ -208,11 +224,11 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host)
+       /* implicit BUG_ON(!res) */
+       if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
+               sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
+-      msleep(10);
++      msleep(2);
+       sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
+       if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
+               sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
+-      msleep(10);
++      msleep(2);
+ }
+ static void tmio_mmc_reset_work(struct work_struct *work)
+@@ -1134,16 +1150,21 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+        * is kept positive, so no suspending actually takes place.
+        */
+       if (ios->power_mode == MMC_POWER_ON && ios->clock) {
++              int reset_needed = 0;
+               if (host->power != TMIO_MMC_ON_RUN) {
+                       tmio_mmc_clk_update(mmc);
+                       pm_runtime_get_sync(dev);
+-                      if (host->resuming) {
+-                              tmio_mmc_reset(host);
+-                              host->resuming = false;
+-                      }
++                      if (host->resuming)
++                              reset_needed = 1;
+               }
++
+               if (host->power == TMIO_MMC_OFF_STOP)
++                      reset_needed = 1;
++              if (reset_needed) {
+                       tmio_mmc_reset(host);
++                      if (host->resuming)
++                              host->resuming = false;
++              }
+               tmio_mmc_set_clock(host, ios->clock);
+               if (host->power == TMIO_MMC_OFF_STOP)
+                       /* power up SD card and the bus */
+@@ -1497,7 +1518,7 @@ int tmio_mmc_host_resume(struct device *dev)
+       /* The MMC core will perform the complete set up */
+       host->resuming = true;
+-      return mmc_resume_host(mmc);
++      return  mmc_resume_host(mmc);
+ }
+ EXPORT_SYMBOL(tmio_mmc_host_resume);
+ #endif
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0009-Add-hibernation-store-area.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0009-Add-hibernation-store-area.patch
new file mode 100755 (executable)
index 0000000..a3495e6
--- /dev/null
@@ -0,0 +1,62 @@
+From 5509937666792520b755ed61a110c956478d089d Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:41:19 +0900
+Subject: [PATCH 09/15] Add hibernation store area
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/mtd/Makefile         | 3 ++-
+ drivers/mtd/devices/Makefile | 4 +++-
+ drivers/mtd/devices/phram.c  | 5 ++++-
+ 3 files changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
+index 99bb9a1..b48049c 100644
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -30,7 +30,8 @@ obj-$(CONFIG_MTD_SWAP)               += mtdswap.o
+ nftl-objs             := nftlcore.o nftlmount.o
+ inftl-objs            := inftlcore.o inftlmount.o
++obj-$(CONFIG_MTD_SPI_NOR)     += spi-nor/
++
+ obj-y         += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
+-obj-$(CONFIG_MTD_SPI_NOR)     += spi-nor/
+ obj-$(CONFIG_MTD_UBI)         += ubi/
+diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
+index d83bd73..969f0e8 100644
+--- a/drivers/mtd/devices/Makefile
++++ b/drivers/mtd/devices/Makefile
+@@ -3,8 +3,10 @@
+ #
+ obj-$(CONFIG_MTD_DOCG3)               += docg3.o
+-obj-$(CONFIG_MTD_SLRAM)               += slram.o
++# obj-$(CONFIG_MTD_SLRAM)             += slram.o
++# obj-$(CONFIG_MTD_PHRAM)             += phram.o
+ obj-$(CONFIG_MTD_PHRAM)               += phram.o
++obj-$(CONFIG_MTD_SLRAM)               += slram.o
+ obj-$(CONFIG_MTD_PMC551)      += pmc551.o
+ obj-$(CONFIG_MTD_MS02NV)      += ms02-nv.o
+ obj-$(CONFIG_MTD_MTDRAM)      += mtdram.o
+diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c
+index 67823de..f05947f 100644
+--- a/drivers/mtd/devices/phram.c
++++ b/drivers/mtd/devices/phram.c
+@@ -293,8 +293,11 @@ static void __exit cleanup_phram(void)
+ {
+       unregister_devices();
+ }
+-
++#ifdef __MODULE__
+ module_init(init_phram);
++#else
++late_initcall(init_phram);
++#endif
+ module_exit(cleanup_phram);
+ MODULE_LICENSE("GPL");
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0010-Add-rcar-eth-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0010-Add-rcar-eth-hibernation-code.patch
new file mode 100755 (executable)
index 0000000..55d1216
--- /dev/null
@@ -0,0 +1,238 @@
+From 1d20d3bd16eac561e14513c9e6cac543fab5a3f0 Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:42:33 +0900
+Subject: [PATCH 10/15] Add rcar-eth hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/net/ethernet/renesas/sh_eth.c | 57 +++++++++++++++++++++++++++++++++--
+ drivers/net/phy/phy_device.c          | 41 +++++++++++++++++++++++++
+ 2 files changed, 95 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
+index 991fa1e..7e91b26 100644
+--- a/drivers/net/ethernet/renesas/sh_eth.c
++++ b/drivers/net/ethernet/renesas/sh_eth.c
+@@ -33,6 +33,7 @@
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+ #include <linux/of_irq.h>
++#include <linux/of_gpio.h>
+ #include <linux/of_net.h>
+ #include <linux/phy.h>
+ #include <linux/cache.h>
+@@ -999,6 +1000,7 @@ static unsigned long sh_eth_get_edtrr_trns(struct sh_eth_private *mdp)
+ struct bb_info {
+       void (*set_gate)(void *addr);
+       struct mdiobb_ctrl ctrl;
++      struct sh_eth_private *mdp;
+       void *addr;
+       u32 mmd_msk;/* MMD */
+       u32 mdo_msk;
+@@ -1029,6 +1031,8 @@ static void sh_mmd_ctrl(struct mdiobb_ctrl *ctrl, int bit)
+ {
+       struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
++      pm_runtime_get_sync(&bitbang->mdp->pdev->dev);
++
+       if (bitbang->set_gate)
+               bitbang->set_gate(bitbang->addr);
+@@ -1036,6 +1040,8 @@ static void sh_mmd_ctrl(struct mdiobb_ctrl *ctrl, int bit)
+               bb_set(bitbang->addr, bitbang->mmd_msk);
+       else
+               bb_clr(bitbang->addr, bitbang->mmd_msk);
++
++      pm_runtime_put_sync(&bitbang->mdp->pdev->dev);
+ }
+ /* Set bit data*/
+@@ -1043,6 +1049,8 @@ static void sh_set_mdio(struct mdiobb_ctrl *ctrl, int bit)
+ {
+       struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
++      pm_runtime_get_sync(&bitbang->mdp->pdev->dev);
++
+       if (bitbang->set_gate)
+               bitbang->set_gate(bitbang->addr);
+@@ -1050,17 +1058,26 @@ static void sh_set_mdio(struct mdiobb_ctrl *ctrl, int bit)
+               bb_set(bitbang->addr, bitbang->mdo_msk);
+       else
+               bb_clr(bitbang->addr, bitbang->mdo_msk);
++
++      pm_runtime_put_sync(&bitbang->mdp->pdev->dev);
+ }
+ /* Get bit data*/
+ static int sh_get_mdio(struct mdiobb_ctrl *ctrl)
+ {
+       struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
++      unsigned int ret;
++
++      pm_runtime_get_sync(&bitbang->mdp->pdev->dev);
+       if (bitbang->set_gate)
+               bitbang->set_gate(bitbang->addr);
+-      return bb_read(bitbang->addr, bitbang->mdi_msk);
++      ret = bb_read(bitbang->addr, bitbang->mdi_msk);
++
++      pm_runtime_put_sync(&bitbang->mdp->pdev->dev);
++
++      return ret;
+ }
+ /* MDC pin control */
+@@ -1068,6 +1085,8 @@ static void sh_mdc_ctrl(struct mdiobb_ctrl *ctrl, int bit)
+ {
+       struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
++      pm_runtime_get_sync(&bitbang->mdp->pdev->dev);
++
+       if (bitbang->set_gate)
+               bitbang->set_gate(bitbang->addr);
+@@ -1075,6 +1094,8 @@ static void sh_mdc_ctrl(struct mdiobb_ctrl *ctrl, int bit)
+               bb_set(bitbang->addr, bitbang->mdc_msk);
+       else
+               bb_clr(bitbang->addr, bitbang->mdc_msk);
++
++      pm_runtime_put_sync(&bitbang->mdp->pdev->dev);
+ }
+ /* mdio bus control struct */
+@@ -2664,6 +2685,7 @@ static int sh_mdio_init(struct sh_eth_private *mdp,
+       bitbang->mdo_msk = PIR_MDO;
+       bitbang->mmd_msk = PIR_MMD;
+       bitbang->mdc_msk = PIR_MDC;
++      bitbang->mdp = mdp;
+       bitbang->ctrl.ops = &bb_ops;
+       /* MII controller setting */
+@@ -3002,9 +3024,38 @@ static int sh_eth_runtime_nop(struct device *dev)
+       return 0;
+ }
++static int sh_eth_suspend(struct device *dev)
++{
++      int ret = 0;
++      struct net_device *ndev = dev_get_drvdata(dev);
++
++      if (netif_running(ndev)) {
++              netif_device_detach(ndev);
++              ret = sh_eth_close(ndev);
++      }
++
++      return ret;
++}
++
++static int sh_eth_resume(struct device *dev)
++{
++      int ret = 0;
++      struct net_device *ndev = dev_get_drvdata(dev);
++
++      if (netif_running(ndev)) {
++              ret = sh_eth_open(ndev);
++              if (ret < 0)
++                      goto err;
++              netif_device_attach(ndev);
++      }
++
++err:
++      return ret;
++}
++
+ static const struct dev_pm_ops sh_eth_dev_pm_ops = {
+-      .runtime_suspend = sh_eth_runtime_nop,
+-      .runtime_resume = sh_eth_runtime_nop,
++      SET_RUNTIME_PM_OPS(sh_eth_runtime_nop, sh_eth_runtime_nop, NULL)
++      SET_SYSTEM_SLEEP_PM_OPS(sh_eth_suspend, sh_eth_resume)
+ };
+ #define SH_ETH_PM_OPS (&sh_eth_dev_pm_ops)
+ #else
+diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
+index 3657b4a..3ceb4f9 100644
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -510,6 +510,32 @@ int phy_init_hw(struct phy_device *phydev)
+       return phydev->drv->config_init(phydev);
+ }
++int phy_suspend(struct phy_device *phydev)
++{
++      struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
++      struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
++
++      /* If the device has WOL enabled, we cannot suspend the PHY */
++      phy_ethtool_get_wol(phydev, &wol);
++      if (wol.wolopts)
++              return -EBUSY;
++
++      if (phydrv->suspend)
++              return phydrv->suspend(phydev);
++      return 0;
++}
++EXPORT_SYMBOL(phy_suspend);
++
++int phy_resume(struct phy_device *phydev)
++{
++      struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
++
++      if (phydrv->resume)
++              return phydrv->resume(phydev);
++      return 0;
++}
++EXPORT_SYMBOL(phy_resume);
++
+ /**
+  * phy_attach_direct - attach a network device to a given PHY device pointer
+  * @dev: network device to attach
+@@ -528,6 +554,7 @@ static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
+                            u32 flags, phy_interface_t interface)
+ {
+       struct device *d = &phydev->dev;
++      struct module *bus_module;
+       int err;
+       /* Assume that if there is no driver, that it doesn't
+@@ -553,6 +580,14 @@ static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
+               return -EBUSY;
+       }
++      /* Increment the bus module reference count */
++      bus_module = phydev->bus->dev.driver ?
++                   phydev->bus->dev.driver->owner : NULL;
++      if (!try_module_get(bus_module)) {
++              dev_err(&dev->dev, "failed to get the bus module\n");
++              return -EIO;
++      }
++
+       phydev->attached_dev = dev;
+       dev->phydev = phydev;
+@@ -568,6 +603,8 @@ static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
+       err = phy_init_hw(phydev);
+       if (err)
+               phy_detach(phydev);
++      else
++              phy_resume(phydev);
+       return err;
+ }
+@@ -612,8 +649,12 @@ EXPORT_SYMBOL(phy_attach);
+  */
+ void phy_detach(struct phy_device *phydev)
+ {
++      if (phydev->bus->dev.driver)
++              module_put(phydev->bus->dev.driver->owner);
++
+       phydev->attached_dev->phydev = NULL;
+       phydev->attached_dev = NULL;
++      phy_suspend(phydev);
+       /* If the device had no specific driver before (i.e. - it
+        * was using the generic driver), we unbind the device
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0011-Add-rcar-pci-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0011-Add-rcar-pci-hibernation-code.patch
new file mode 100755 (executable)
index 0000000..bdc9555
--- /dev/null
@@ -0,0 +1,375 @@
+From f8691a62199319d9e37cd451a9b8364aa640c4cb Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:45:19 +0900
+Subject: [PATCH 11/15] Add rcar-pci hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/pci/host/pci-rcar-gen2.c | 281 ++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 266 insertions(+), 15 deletions(-)
+
+diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c
+index 57b6572..4cb9693 100644
+--- a/drivers/pci/host/pci-rcar-gen2.c
++++ b/drivers/pci/host/pci-rcar-gen2.c
+@@ -23,9 +23,12 @@
+ #include <linux/sizes.h>
+ #include <linux/slab.h>
+ #include <linux/usb/phy.h>
++#include <linux/clk.h>
+ /* AHB-PCI Bridge PCI communication registers */
+ #define RCAR_AHBPCI_PCICOM_OFFSET     0x800
++#define RCAR_PCICONF_OHCI             0x0
++#define RCAR_PCICONF_EHCI             0x100
+ #define RCAR_PCIAHB_WIN1_CTR_REG      (RCAR_AHBPCI_PCICOM_OFFSET + 0x00)
+ #define RCAR_PCIAHB_WIN2_CTR_REG      (RCAR_AHBPCI_PCICOM_OFFSET + 0x04)
+@@ -104,6 +107,14 @@ struct rcar_pci_priv {
+       int domain;
+       int irq;
+       unsigned long window_size;
++      void __iomem *ohci_memdata;
++      void __iomem *ehci_memdata;
++#ifndef       MCCILDK_CHANGE_DISABLE
++      u32 store_cfg[12];
++#else
++      u32 store_cfg[9];
++#endif
++      struct usb_phy *phy;
+ };
+ /* PCI configuration space operations */
+@@ -276,12 +287,6 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys)
+       /* Configure AHB master and slave modes */
+       iowrite32(RCAR_AHB_BUS_MODE, reg + RCAR_AHB_BUS_CTR_REG);
+-      /* Configure PCI arbiter */
+-      val = ioread32(reg + RCAR_PCI_ARBITER_CTR_REG);
+-      val |= RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 |
+-             RCAR_PCI_ARBITER_PCIBP_MODE;
+-      iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG);
+-
+       /* PCI-AHB mapping: 0x40000000 base */
+       iowrite32(0x40000000 | RCAR_PCIAHB_PREFETCH16,
+                 reg + RCAR_PCIAHB_WIN1_CTR_REG);
+@@ -290,9 +295,25 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys)
+       val = priv->mem_res.start | RCAR_AHBPCI_WIN_CTR_MEM;
+       iowrite32(val, reg + RCAR_AHBPCI_WIN2_CTR_REG);
++      /* Enable PCI interrupts */
++      iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME,
++                reg + RCAR_PCI_INT_ENABLE_REG);
++
++      /* Configure PCI arbiter */
++      val = ioread32(reg + RCAR_PCI_ARBITER_CTR_REG);
++      val |= RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 |
++             RCAR_PCI_ARBITER_PCIBP_MODE;
++      iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG);
++
+       /* Enable AHB-PCI bridge PCI configuration access */
+       iowrite32(RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG,
+                 reg + RCAR_AHBPCI_WIN1_CTR_REG);
++      val = ioread32(reg + PCI_COMMAND);
++
++      val |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
++             PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
++      iowrite32(val, reg + PCI_COMMAND);
++
+       /* Set PCI-AHB Window1 address */
+       iowrite32(0x40000000 | PCI_BASE_ADDRESS_MEM_PREFETCH,
+                 reg + PCI_BASE_ADDRESS_1);
+@@ -300,15 +321,6 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys)
+       val = priv->cfg_res->start + RCAR_AHBPCI_PCICOM_OFFSET;
+       iowrite32(val, reg + PCI_BASE_ADDRESS_0);
+-      val = ioread32(reg + PCI_COMMAND);
+-      val |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
+-             PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+-      iowrite32(val, reg + PCI_COMMAND);
+-
+-      /* Enable PCI interrupts */
+-      iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME,
+-                reg + RCAR_PCI_INT_ENABLE_REG);
+-
+       if (priv->irq > 0)
+               rcar_pci_setup_errirq(priv);
+@@ -326,6 +338,8 @@ static struct pci_ops rcar_pci_ops = {
+       .write  = rcar_pci_write_config,
+ };
++#define RCAR_MAX_PCI_HOSTS    2
++static struct rcar_pci_priv *keep_priv[RCAR_MAX_PCI_HOSTS];
+ static int rcar_pci_probe(struct platform_device *pdev)
+ {
+       struct resource *cfg_res, *mem_res;
+@@ -350,6 +364,7 @@ static int rcar_pci_probe(struct platform_device *pdev)
+               return -ENOMEM;
+       priv->mem_res = *mem_res;
++      keep_priv[pdev->id] = priv;
+       /*
+        * The controller does not support/use port I/O,
+        * so setup a dummy port I/O region here.
+@@ -378,6 +393,7 @@ static int rcar_pci_probe(struct platform_device *pdev)
+               return PTR_ERR(phy);
+       usb_phy_init(phy);
++      priv->phy = phy;
+       hw_private[0] = priv;
+       memset(&hw, 0, sizeof(hw));
+@@ -390,14 +406,249 @@ static int rcar_pci_probe(struct platform_device *pdev)
+       hw.domain = priv->domain;
+ #endif
+       pci_common_init_dev(&pdev->dev, &hw);
++      priv->ohci_memdata = ioremap(cfg_res->start - 0x10000, 0x1000);
++      priv->ehci_memdata = ioremap(cfg_res->start - 0x10000 + 0x1000, 0x1000);
++      return 0;
++}
++
++static int rcar_pci_suspend(struct device *dev)
++{
++      struct clk *clk;
++      clk = clk_get(NULL, "ehci");
++      clk_disable_unprepare(clk);
++      clk_put(clk);
++      return 0;
++}
++static int rcar_pci_resume(struct device *dev)
++{
++      struct clk *clk;
++      clk = clk_get(NULL, "ehci");
++      clk_prepare_enable(clk);
++      clk_put(clk);
++      return 0;
++}
++static u32 rcar_pci_get_conf(struct rcar_pci_priv *priv, int id, int offset)
++{
++      u32 val, kpt;
++      void __iomem *data;
++      kpt = ioread32(priv->reg + RCAR_AHBPCI_WIN1_CTR_REG);
++      val = id ? RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG :
++                   RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG;
++
++      iowrite32(val, priv->reg + RCAR_AHBPCI_WIN1_CTR_REG);
++      data = priv->reg + (id >> 1) * 0x100;
++      val = ioread32(data + offset);
++      iowrite32(kpt, priv->reg + RCAR_AHBPCI_WIN1_CTR_REG);
++      return val;
++}
++
++static void rcar_pci_set_conf(struct rcar_pci_priv *priv,
++              int id, int offset, u32 d)
++{
++      u32 val, kpt;
++      void __iomem *data;
++      kpt = ioread32(priv->reg + RCAR_AHBPCI_WIN1_CTR_REG);
++      val = id ? RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG :
++                   RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG;
++
++      iowrite32(val, priv->reg + RCAR_AHBPCI_WIN1_CTR_REG);
++      data = priv->reg + (id >> 1) * 0x100;
++      iowrite32(d, data + offset);
++      iowrite32(kpt, priv->reg + RCAR_AHBPCI_WIN1_CTR_REG);
++}
++
++
++static int rcar_pci_freeze(struct device *dev)
++{
++      struct rcar_pci_priv *priv = keep_priv[to_platform_device(dev)->id];
++      struct clk *clk;
++      clk = clk_get(NULL, "ehci");
++      clk_disable_unprepare(clk);
++      clk_put(clk);
++
++#ifndef       MCCILDK_CHANGE_DISABLE
++      priv->store_cfg[0] = rcar_pci_get_conf(priv, 0, PCI_COMMAND);
++      priv->store_cfg[1] = rcar_pci_get_conf(priv, 1, PCI_COMMAND);
++      priv->store_cfg[2] = rcar_pci_get_conf(priv, 2, PCI_COMMAND);
++      priv->store_cfg[3] = rcar_pci_get_conf(priv, 0, PCI_CACHE_LINE_SIZE);
++      priv->store_cfg[4] = rcar_pci_get_conf(priv, 1, PCI_CACHE_LINE_SIZE);
++      priv->store_cfg[5] = rcar_pci_get_conf(priv, 2, PCI_CACHE_LINE_SIZE);
++      priv->store_cfg[6] = rcar_pci_get_conf(priv, 0, PCI_INTERRUPT_LINE);
++      priv->store_cfg[7] = rcar_pci_get_conf(priv, 1, PCI_INTERRUPT_LINE);
++      priv->store_cfg[8] = rcar_pci_get_conf(priv, 2, PCI_INTERRUPT_LINE);
++      priv->store_cfg[9] =  rcar_pci_get_conf(priv, 0, PCI_BASE_ADDRESS_0);
++      priv->store_cfg[10] = rcar_pci_get_conf(priv, 1, PCI_BASE_ADDRESS_0);
++      priv->store_cfg[11] = rcar_pci_get_conf(priv, 2, PCI_BASE_ADDRESS_0);
++#else
++      priv->store_cfg[0] = rcar_pci_get_conf(priv, 0, 0x04);
++      priv->store_cfg[1] = rcar_pci_get_conf(priv, 1, 0x04);
++      priv->store_cfg[2] = rcar_pci_get_conf(priv, 2, 0x04);
++      priv->store_cfg[3] = rcar_pci_get_conf(priv, 0, 0x0c);
++      priv->store_cfg[4] = rcar_pci_get_conf(priv, 1, 0x0c);
++      priv->store_cfg[5] = rcar_pci_get_conf(priv, 2, 0x0c);
++      priv->store_cfg[6] = rcar_pci_get_conf(priv, 0, 0x3c);
++      priv->store_cfg[7] = rcar_pci_get_conf(priv, 1, 0x3c);
++      priv->store_cfg[8] = rcar_pci_get_conf(priv, 2, 0x3c);
++#endif
++      pm_runtime_disable(priv->dev);
++      return 0;
++}
++
++static int rcar_pci_restore(struct device *dev)
++{
++      struct clk *clk;
++      void *m;
++      u32 val;
++      struct rcar_pci_priv *priv = keep_priv[to_platform_device(dev)->id];
++      void __iomem *reg = priv->reg;
++      int id = to_platform_device(dev)->id;
++
++      pm_runtime_enable(priv->dev);
++      pm_runtime_get_sync(priv->dev);
++
++      clk = clk_get(NULL, "ehci");
++      clk_prepare_enable(clk);
++      clk_put(clk);
++      clk = clk_get(NULL, "hsusb");
++      clk_prepare_enable(clk);
++      clk_put(clk);
++      usb_phy_set_suspend(priv->phy, 0);
++      m = ioremap(0xe61501c4, 4);
++      val = readl(m);
++      iounmap(m);
++      m = ioremap(0xe615014c, 4);
++      writel(val & ~(3 << 3), m);
++      iounmap(m);
++      val = ioread32(reg + RCAR_PCI_UNIT_REV_REG);
++      dev_info(priv->dev, "PCI: bus%u revision %x\n", id, val);
++
++      /* Disable Direct Power Down State and assert reset */
++      val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD;
++#ifndef       MCCILDK_CHANGE_DISABLE
++      val |= RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST;
++#else
++      val |= RCAR_USBCTR_USBH_RST;
++#endif
++      iowrite32(val, reg + RCAR_USBCTR_REG);
++      udelay(4);
++      /* De-assert reset */
++#ifndef       MCCILDK_CHANGE_DISABLE
++      val &= ~(RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST
++                      | RCAR_USBCTR_PCICLK_MASK);
++      iowrite32(val, reg + RCAR_USBCTR_REG);
++      /* reset PCIAHB window size */
++      val &= ~RCAR_USBCTR_PCIAHB_WIN1_MASK;
++      val |= RCAR_USBCTR_PCIAHB_WIN1_1G;
++      iowrite32(val, reg + RCAR_USBCTR_REG);
++#else
++      val &=  RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST
++              | RCAR_USBCTR_PCICLK_MASK;
++      iowrite32(val, reg + RCAR_USBCTR_REG);
++      val &=  RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST
++              | RCAR_USBCTR_PCICLK_MASK;
++      iowrite32(val, reg + RCAR_USBCTR_REG);
++      /* reset PCIAHB window size */
++      val &= RCAR_USBCTR_PCIAHB_WIN1_MASK;
++      val |= RCAR_USBCTR_PCIAHB_WIN1_1G;
++      iowrite32(val, reg + RCAR_USBCTR_REG);
++#endif
++
++      /* Configure AHB master and slave modes */
++      iowrite32(RCAR_AHB_BUS_MODE, reg + RCAR_AHB_BUS_CTR_REG);
++
++      /* PCI-AHB mapping: 0x40000000 base */
++      iowrite32(0x40000000 | RCAR_PCIAHB_PREFETCH16,
++                reg + RCAR_PCIAHB_WIN1_CTR_REG);
++
++      /* AHB-PCI mapping: OHCI/EHCI registers */
++      val = priv->mem_res.start | RCAR_AHBPCI_WIN_CTR_MEM;
++      iowrite32(val, reg + RCAR_AHBPCI_WIN2_CTR_REG);
++
++      /* Enable PCI interrupts */
++      iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME,
++                reg + RCAR_PCI_INT_ENABLE_REG);
++
++      /* Configure PCI arbiter */
++      val = ioread32(reg + RCAR_PCI_ARBITER_CTR_REG);
++      val |= RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 |
++             RCAR_PCI_ARBITER_PCIBP_MODE;
++      iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG);
++
++      /* Enable AHB-PCI bridge PCI configuration access */
++      iowrite32(RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG,
++                reg + RCAR_AHBPCI_WIN1_CTR_REG);
++
++      val = ioread32(reg + PCI_COMMAND);
++      val |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
++             PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
++      iowrite32(val, reg + PCI_COMMAND);
++
++      /* Set PCI-AHB Window1 address */
++      iowrite32(0x40000000 | PCI_BASE_ADDRESS_MEM_PREFETCH,
++                reg + PCI_BASE_ADDRESS_1);
++      /* Set AHB-PCI bridge PCI communication area address */
++      val = priv->cfg_res->start + RCAR_AHBPCI_PCICOM_OFFSET;
++      iowrite32(val, reg + PCI_BASE_ADDRESS_0);
++
++      if (priv->irq > 0)
++              rcar_pci_setup_errirq(priv);
++#ifndef       MCCILDK_CHANGE_DISABLE
++      rcar_pci_set_conf(priv, 0, PCI_COMMAND, priv->store_cfg[0]);
++      rcar_pci_set_conf(priv, 1, PCI_COMMAND, priv->store_cfg[1]);
++      rcar_pci_set_conf(priv, 2, PCI_COMMAND, priv->store_cfg[2]);
++      rcar_pci_set_conf(priv, 0, PCI_CACHE_LINE_SIZE, priv->store_cfg[3]);
++      rcar_pci_set_conf(priv, 1, PCI_CACHE_LINE_SIZE, priv->store_cfg[4]);
++      rcar_pci_set_conf(priv, 2, PCI_CACHE_LINE_SIZE, priv->store_cfg[5]);
++      rcar_pci_set_conf(priv, 0, PCI_INTERRUPT_LINE, priv->store_cfg[6]);
++      rcar_pci_set_conf(priv, 1, PCI_INTERRUPT_LINE, priv->store_cfg[7]);
++      rcar_pci_set_conf(priv, 2, PCI_INTERRUPT_LINE, priv->store_cfg[8]);
++      rcar_pci_set_conf(priv, 1, PCI_BASE_ADDRESS_0, priv->store_cfg[10]);
++      rcar_pci_set_conf(priv, 2, PCI_BASE_ADDRESS_0, priv->store_cfg[11]);
++#else
++      rcar_pci_set_conf(priv, 1, PCI_COMMAND, PCI_COMMAND_SERR
++                      | PCI_COMMAND_PARITY | PCI_COMMAND_MEMORY
++                      | PCI_COMMAND_MASTER);
++      rcar_pci_set_conf(priv, 1, PCI_BASE_ADDRESS_0
++                      priv->cfg_res->start - 0x10000);
++      rcar_pci_set_conf(priv, 2, PCI_COMMAND, PCI_COMMAND_SERR
++                      | PCI_COMMAND_PARITY | PCI_COMMAND_MEMORY
++                      | PCI_COMMAND_MASTER);
++      rcar_pci_set_conf(priv, 2, PCI_BASE_ADDRESS_0,
++                      priv->cfg_res->start - 0x10000 + 0x1000);
++      rcar_pci_set_conf(priv, 0, PCI_CACHE_LINE_SIZE, priv->store_cfg[3]);
++      rcar_pci_set_conf(priv, 1, PCI_CACHE_LINE_SIZE, priv->store_cfg[4]);
++      rcar_pci_set_conf(priv, 2, PCI_CACHE_LINE_SIZE, priv->store_cfg[5]);
++      rcar_pci_set_conf(priv, 0, PCI_INTERRUPT_LINE, 0x00020100);
++      rcar_pci_set_conf(priv, 1, PCI_INTERRUPT_LINE, 0x2a010100);
++      rcar_pci_set_conf(priv, 2, PCI_INTERRUPT_LINE, 0x22100200);
++      val = RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG;
++      iowrite32(val, priv->reg + RCAR_AHBPCI_WIN1_CTR_REG);
++      val = ioread32(priv->reg + 0x04);
++      iowrite32(val | (1 << 1), priv->reg + 0x04);
++      val = ioread32(priv->reg + 0x104);
++      iowrite32(val | (1 << 1), priv->reg + 0x104);
++
++      val = RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG;
++      iowrite32(val, priv->reg + RCAR_AHBPCI_WIN1_CTR_REG);
++#endif
+       return 0;
+ }
++static const struct dev_pm_ops rcar_pci_pm_ops = {
++      .suspend        = rcar_pci_suspend,
++      .resume         = rcar_pci_resume,
++      .freeze_noirq   = rcar_pci_freeze,
++      .restore_noirq  = rcar_pci_restore,
++      .thaw           = rcar_pci_resume,
++      .poweroff       = rcar_pci_suspend
++};
++
+ static struct platform_driver rcar_pci_driver = {
+       .driver = {
+               .name = "pci-rcar-gen2",
+               .owner = THIS_MODULE,
+               .suppress_bind_attrs = true,
++              .pm = &rcar_pci_pm_ops,
+       },
+       .probe = rcar_pci_probe,
+ };
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0012-Add-rcar-gpio-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0012-Add-rcar-gpio-hibernation-code.patch
new file mode 100755 (executable)
index 0000000..9b2aff4
--- /dev/null
@@ -0,0 +1,230 @@
+From bf20be14fc1b3f7e096bdac9c5ff67362b391479 Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:46:24 +0900
+Subject: [PATCH 12/15] Add rcar-gpio hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/pinctrl/sh-pfc/core.c | 141 +++++++++++++++++++++++++++++++++++++++---
+ drivers/pinctrl/sh-pfc/core.h |   4 ++
+ 2 files changed, 138 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c
+index b9e025d..c37418e 100644
+--- a/drivers/pinctrl/sh-pfc/core.c
++++ b/drivers/pinctrl/sh-pfc/core.c
+@@ -24,6 +24,7 @@
+ #include <linux/pinctrl/machine.h>
+ #include <linux/platform_device.h>
+ #include <linux/slab.h>
++#include <linux/cpu_pm.h>
+ #include "core.h"
+@@ -201,19 +202,117 @@ static void sh_pfc_config_reg_helper(struct sh_pfc *pfc,
+       }
+ }
++#ifdef CONFIG_CPU_PM
++struct reg_record {
++      void __iomem *reg;
++      unsigned long width;
++      unsigned long data;
++};
++
++struct reg_config {
++      bool unlock;
++      struct reg_record unlock_reg;
++      struct reg_record actual_reg;
++      struct list_head list;
++};
++
++static struct reg_config *regs_list;
++
++struct reg_range {
++      int start;
++      int end;
++};
++
++static int sh_pfc_cpu_pm_notify(struct notifier_block *self,
++                                  unsigned long action, void *hcpu)
++{
++      struct reg_config  *tmp = NULL;
++      struct sh_pfc *pfc = container_of(self, struct sh_pfc, pm_notify);
++      /* We don't setup pinmux in kernel - store all registers */
++      struct reg_range ranges[] = {
++              {0x0, 0x5c}, {0x160, 0x160}, {0x90, 0x98},
++              {0x100, 0x118}, {0x70, 0x70}, {0x60, 0x64},
++              {0x84, 0x8c}, {0x240, 0x248},
++      };
++
++      if (action == CPU_PM_ENTER) {
++              if (!regs_list) {
++                      /* No pinmux configuration, storing all registers */
++                      int store_cnt = 0;
++                      int i;
++                      for (i = 0; i < ARRAY_SIZE(ranges); i++) {
++                              int j;
++                              for (j = ranges[i].start; j <= ranges[i].end; j += sizeof(u32)) {
++                                      pfc->stored_regs[store_cnt] =
++                                              sh_pfc_read_raw_reg(sh_pfc_phys_to_virt(pfc, 0xe6060000 + j), 32);
++                                      pr_debug("PFC: %08x => %08x\n", 0xe6060000 + j, pfc->stored_regs[store_cnt]);
++                                      store_cnt++;
++                                      if (store_cnt >= ARRAY_SIZE(pfc->stored_regs)) {
++                                              pr_err("read: Register store overflow\n");
++                                              goto out;
++                                      }
++                              }
++                      }
++              }
++      } else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT) {
++              if (!regs_list) {
++                      /* No list, restoring all registers */
++                      int store_cnt = 0;
++                      int i;
++                      for (i = 0; i < ARRAY_SIZE(ranges); i++) {
++                              int j;
++                              for (j = ranges[i].start; j <= ranges[i].end; j += sizeof(u32)) {
++                                      sh_pfc_write_raw_reg(sh_pfc_phys_to_virt(pfc, 0xe6060000 + j), 32,
++                                              pfc->stored_regs[store_cnt]);
++                                      pr_debug("PFC: %08x => %08x\n", 0xe6060000 + j, pfc->stored_regs[store_cnt]);
++                                      store_cnt++;
++                                      if (store_cnt >= ARRAY_SIZE(pfc->stored_regs)) {
++                                              pr_err("write: Register store overflow\n");
++                                              goto out;
++                                      }
++                              }
++                      }
++                      goto out;
++              }
++              list_for_each_entry(tmp , &(regs_list->list), list) {
++              if (tmp->unlock)
++                      sh_pfc_write_raw_reg(tmp->unlock_reg.reg,
++                                           tmp->unlock_reg.width,
++                                           tmp->unlock_reg.data);
++              sh_pfc_write_raw_reg(tmp->actual_reg.reg,
++                                   tmp->actual_reg.width,
++                                   tmp->actual_reg.data);
++              }
++      }
++out:
++      return NOTIFY_OK;
++}
++
++static int __init sh_pfc_cpu_pm_init(struct sh_pfc *pfc)
++{
++      memset(&pfc->pm_notify, 0, sizeof(pfc->pm_notify));
++      pfc->pm_notify.notifier_call = sh_pfc_cpu_pm_notify;
++      return cpu_pm_register_notifier(&pfc->pm_notify);
++}
++#else
++static int __init sh_pfc_cpu_pm_init(struct sh_pfc *pfc)
++{
++      return 0;
++}
++#endif
++
++
+ static void sh_pfc_write_config_reg(struct sh_pfc *pfc,
+                                   const struct pinmux_cfg_reg *crp,
+                                   unsigned long field, unsigned long value)
+ {
+       void __iomem *mapped_reg;
+       unsigned long mask, pos, data;
+-
++#ifdef CONFIG_CPU_PM
++      struct reg_config *tmp;
++#endif
+       sh_pfc_config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos);
+-      dev_dbg(pfc->dev, "write_reg addr = %lx, value = %ld, field = %ld, "
+-              "r_width = %ld, f_width = %ld\n",
+-              crp->reg, value, field, crp->reg_width, crp->field_width);
+-
+       mask = ~(mask << pos);
+       value = value << pos;
+@@ -221,14 +320,39 @@ static void sh_pfc_write_config_reg(struct sh_pfc *pfc,
+       data &= mask;
+       data |= value;
+-      if (pfc->info->unlock_reg)
++#ifdef CONFIG_CPU_PM
++      tmp = kzalloc(sizeof(struct reg_config), GFP_KERNEL);
++      BUG_ON(!tmp);
++
++      if (!regs_list) {
++              regs_list = tmp;
++              INIT_LIST_HEAD(&regs_list->list);
++      }
++#endif
++
++      if (pfc->info->unlock_reg) {
++#ifdef CONFIG_CPU_PM
++              tmp->unlock = true;
++              tmp->unlock_reg.reg = sh_pfc_phys_to_virt(pfc,
++                              pfc->info->unlock_reg);
++              tmp->unlock_reg.width = 32;
++              tmp->unlock_reg.data = ~data;
++#endif
+               sh_pfc_write_raw_reg(
+                       sh_pfc_phys_to_virt(pfc, pfc->info->unlock_reg), 32,
+                       ~data);
++      }
++
++#ifdef CONFIG_CPU_PM
++      tmp->actual_reg.reg = mapped_reg;
++      tmp->actual_reg.width = crp->reg_width;
++      tmp->actual_reg.data = data;
++
++      list_add(&tmp->list, &regs_list->list);
++#endif
+       sh_pfc_write_raw_reg(mapped_reg, crp->reg_width, data);
+ }
+-
+ static int sh_pfc_get_config_reg(struct sh_pfc *pfc, u16 enum_id,
+                                const struct pinmux_cfg_reg **crp, int *fieldp,
+                                int *valuep)
+@@ -574,6 +698,8 @@ static int sh_pfc_probe(struct platform_device *pdev)
+       platform_set_drvdata(pdev, pfc);
++      sh_pfc_cpu_pm_init(pfc);
++
+       dev_info(pfc->dev, "%s support registered\n", info->name);
+       return 0;
+@@ -596,6 +722,7 @@ static int sh_pfc_remove(struct platform_device *pdev)
+       if (pfc->info->ops && pfc->info->ops->exit)
+               pfc->info->ops->exit(pfc);
++
+       return 0;
+ }
+diff --git a/drivers/pinctrl/sh-pfc/core.h b/drivers/pinctrl/sh-pfc/core.h
+index 75ecb67..5471a6c 100644
+--- a/drivers/pinctrl/sh-pfc/core.h
++++ b/drivers/pinctrl/sh-pfc/core.h
+@@ -14,6 +14,7 @@
+ #include <linux/compiler.h>
+ #include <linux/spinlock.h>
+ #include <linux/types.h>
++#include <linux/notifier.h>
+ #include "sh_pfc.h"
+@@ -51,6 +52,9 @@ struct sh_pfc {
+       struct sh_pfc_chip *func;
+       struct sh_pfc_pinctrl *pinctrl;
++      struct notifier_block pm_notify;
++#define STORE_REGS_COUNT      50
++      u32 stored_regs[STORE_REGS_COUNT];
+ };
+ int sh_pfc_register_gpiochip(struct sh_pfc *pfc);
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0013-Add-rcar-spi-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0013-Add-rcar-spi-hibernation-code.patch
new file mode 100755 (executable)
index 0000000..515b08b
--- /dev/null
@@ -0,0 +1,168 @@
+From c1b129172a91046a7555a3c198b49eb1b45aafd7 Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:58:28 +0900
+Subject: [PATCH 13/15] Add rcar-spi hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/spi/spi-rspi.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 108 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c
+index 215be3b..a2432de 100644
+--- a/drivers/spi/spi-rspi.c
++++ b/drivers/spi/spi-rspi.c
+@@ -38,6 +38,7 @@
+ #include <linux/sh_dma.h>
+ #include <linux/spi/spi.h>
+ #include <linux/spi/rspi.h>
++#include <linux/delay.h>
+ #define RSPI_SPCR             0x00    /* Control Register */
+ #define RSPI_SSLP             0x01    /* Slave Select Polarity Register */
+@@ -208,6 +209,12 @@ struct rspi_data {
+       u8 sppcr;
+       int rx_irq, tx_irq;
+       const struct spi_ops *ops;
++      u32 save_spbmul0;
++      u32 save_spbmul1;
++      u32 save_spbmul2;
++      u32 save_spbmul3;
++      u8 save_spbfcr;
++      u8 save_spscr;
+       unsigned dma_callbacked:1;
+       unsigned byte_access:1;
+@@ -238,6 +245,11 @@ static u16 rspi_read16(const struct rspi_data *rspi, u16 offset)
+       return ioread16(rspi->addr + offset);
+ }
++static u16 rspi_read32(const struct rspi_data *rspi, u16 offset)
++{
++      return ioread32(rspi->addr + offset);
++}
++
+ #define rspi_update8(spi, mask, val, reg) \
+       rspi_write8(spi, (rspi_read8(spi, reg) & ~mask) | val, reg);
+@@ -504,7 +516,6 @@ static int rspi_pio_transfer_in(struct rspi_data *rspi, u8 *rx, unsigned int n)
+       if (!rx)
+               return 0;
+-
+       while (n > 0) {
+               count = min(n, SPI_BUFFER_SIZE);
+               if (count >= SPI_BUFFER_SIZE) {
+@@ -1278,6 +1289,101 @@ error1:
+       return ret;
+ }
++int rspi_suspend(struct device *dev)
++{
++      struct rspi_data *rspi = platform_get_drvdata(to_platform_device(dev));
++      clk_disable_unprepare(rspi->clk);
++      return 0;
++}
++
++int rspi_resume(struct device *dev)
++{
++      struct rspi_data *rspi = platform_get_drvdata(to_platform_device(dev));
++      clk_prepare_enable(rspi->clk);
++      return 0;
++}
++
++#define PR_REG8(dev, rspi, reg) \
++      dev_dbg(dev, "QSPI REG: " #reg " = %08x\n", \
++                      rspi_read8(rspi, reg))
++#define PR_REG16(dev, rspi, reg) \
++      dev_dbg(dev, "QSPI REG: " #reg " = %08x\n", \
++                      rspi_read16(rspi, reg))
++#define PR_REG32(dev, rspi, reg) \
++      dev_dbg(dev, "QSPI REG: " #reg " = %08x\n", \
++                      rspi_read32(rspi, reg))
++
++#ifdef DEBUG
++static void pr_regs(struct device *dev)
++{
++      struct rspi_data *rspi = platform_get_drvdata(to_platform_device(dev));
++      PR_REG8(dev, rspi, RSPI_SPCR);
++      PR_REG8(dev, rspi, RSPI_SSLP);
++      PR_REG8(dev, rspi, RSPI_SPPCR);
++      PR_REG8(dev, rspi, RSPI_SPDR);
++      PR_REG8(dev, rspi, RSPI_SPSCR);
++      PR_REG8(dev, rspi, RSPI_SPBR);
++      PR_REG8(dev, rspi, RSPI_SPDCR);
++      PR_REG8(dev, rspi, RSPI_SPCKD);
++      PR_REG8(dev, rspi, RSPI_SSLND);
++      PR_REG8(dev, rspi, RSPI_SPND);
++      PR_REG16(dev, rspi, RSPI_SPCMD0);
++      PR_REG16(dev, rspi, RSPI_SPCMD1);
++      PR_REG16(dev, rspi, RSPI_SPCMD2);
++      PR_REG16(dev, rspi, RSPI_SPCMD3);
++      PR_REG8(dev, rspi, QSPI_SPBFCR);
++      PR_REG16(dev, rspi, QSPI_SPBDCR);
++      PR_REG32(dev, rspi, QSPI_SPBMUL0);
++      PR_REG32(dev, rspi, QSPI_SPBMUL1);
++      PR_REG32(dev, rspi, QSPI_SPBMUL2);
++      PR_REG32(dev, rspi, QSPI_SPBMUL3);
++}
++#endif
++
++int rspi_freeze(struct device *dev)
++{
++      struct rspi_data *rspi = platform_get_drvdata(to_platform_device(dev));
++      rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR);
++      rspi->save_spbmul0 = rspi_read32(rspi, QSPI_SPBMUL0);
++      rspi->save_spbmul1 = rspi_read32(rspi, QSPI_SPBMUL1);
++      rspi->save_spbmul2 = rspi_read32(rspi, QSPI_SPBMUL2);
++      rspi->save_spbmul3 = rspi_read32(rspi, QSPI_SPBMUL3);
++      rspi->save_spbfcr = rspi_read8(rspi, QSPI_SPBFCR);
++      rspi->save_spscr = rspi_read8(rspi, RSPI_SPSCR);
++      dev_info(dev, "freeze\n");
++#ifdef DEBUG
++      pr_regs(dev);
++#endif
++      return 0;
++}
++
++
++int rspi_restore(struct device *dev)
++{
++      struct rspi_data *rspi = platform_get_drvdata(to_platform_device(dev));
++      clk_prepare_enable(rspi->clk);
++      udelay(16);
++      set_config_register(rspi, 8);
++      rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR);
++      rspi_write8(rspi, rspi->save_spscr, RSPI_SPSCR);
++      rspi_write8(rspi, rspi->save_spbfcr, QSPI_SPBFCR);
++      rspi_write32(rspi, rspi->save_spbmul3, QSPI_SPBMUL3);
++      rspi_write32(rspi, rspi->save_spbmul2, QSPI_SPBMUL2);
++      rspi_write32(rspi, rspi->save_spbmul1, QSPI_SPBMUL1);
++      rspi_write32(rspi, rspi->save_spbmul0, QSPI_SPBMUL0);
++      dev_info(dev, "restore\n");
++#ifdef DEBUG
++      pr_regs(dev);
++#endif
++      return 0;
++}
++
++const struct dev_pm_ops rspi_pm_ops = {
++      SET_SYSTEM_SLEEP_PM_OPS(rspi_suspend, rspi_resume)
++      .restore = rspi_restore,
++      .freeze = rspi_freeze,
++};
++
+ static struct platform_device_id spi_driver_ids[] = {
+       { "rspi",       (kernel_ulong_t)&rspi_ops },
+       { "rspi-rz",    (kernel_ulong_t)&rspi_rz_ops },
+@@ -1295,6 +1401,7 @@ static struct platform_driver rspi_driver = {
+               .name = "renesas_spi",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(rspi_of_match),
++              .pm = &rspi_pm_ops,
+       },
+ };
+ module_platform_driver(rspi_driver);
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0014-Add-rcar-sci-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0014-Add-rcar-sci-hibernation-code.patch
new file mode 100755 (executable)
index 0000000..c70f515
--- /dev/null
@@ -0,0 +1,41 @@
+From 947b9e15ff36a9dcd517bb932303cc32f8356550 Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:59:40 +0900
+Subject: [PATCH 14/15] Add rcar-sci hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/tty/serial/sh-sci.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
+index e3abfb7..2f0dc7a 100644
+--- a/drivers/tty/serial/sh-sci.c
++++ b/drivers/tty/serial/sh-sci.c
+@@ -2852,6 +2852,7 @@ static int sci_probe(struct platform_device *dev)
+       return 0;
+ }
++#ifdef CONFIG_PM_SLEEP
+ static int sci_suspend(struct device *dev)
+ {
+       struct sci_port *sport = dev_get_drvdata(dev);
+@@ -2871,10 +2872,13 @@ static int sci_resume(struct device *dev)
+       return 0;
+ }
++#else
++#define sci_suspend NULL
++#define sci_resume NULL
++#endif
+ static const struct dev_pm_ops sci_dev_pm_ops = {
+-      .suspend        = sci_suspend,
+-      .resume         = sci_resume,
++      SET_SYSTEM_SLEEP_PM_OPS(sci_suspend, sci_resume)
+ };
+ static struct platform_driver sci_driver = {
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0015-Add-rcar-usbphy-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0015-Add-rcar-usbphy-hibernation-code.patch
new file mode 100755 (executable)
index 0000000..c0c2b16
--- /dev/null
@@ -0,0 +1,83 @@
+From 28393daa686ef43966e3fa1652bcd8d860698ef4 Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 18:00:39 +0900
+Subject: [PATCH 15/15] Add rcar-usbphy hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/usb/phy/phy-rcar-gen2-usb.c | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c
+index 9e7205d..05849e7 100644
+--- a/drivers/usb/phy/phy-rcar-gen2-usb.c
++++ b/drivers/usb/phy/phy-rcar-gen2-usb.c
+@@ -148,6 +148,7 @@ static int rcar_gen2_usb_phy_set_suspend(struct usb_phy *phy, int suspend)
+       devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
+       devm_iounmap(&pdev->dev, priv->base);
++      priv->base = NULL;
+       spin_unlock_irqrestore(&priv->lock, flags);
+@@ -178,6 +179,7 @@ static int rcar_gen2_usb_phy_init(struct usb_phy *phy)
+               devm_release_mem_region(&pdev->dev, res->start,
+                                                       resource_size(res));
+               devm_iounmap(&pdev->dev, priv->base);
++              priv->base = NULL;
+               spin_unlock_irqrestore(&priv->lock, flags);
+       }
+       return 0;
+@@ -209,6 +211,7 @@ static void rcar_gen2_usb_phy_shutdown(struct usb_phy *phy)
+               devm_release_mem_region(&pdev->dev, res->start,
+                                                       resource_size(res));
+               devm_iounmap(&pdev->dev, priv->base);
++              priv->base = NULL;
+       }
+ out:
+       spin_unlock_irqrestore(&priv->lock, flags);
+@@ -431,9 +434,41 @@ static int phy_rcar_gen2_pm_resume(struct device *dev)
+       return 0;
+ }
++static int phy_rcar_gen2_pm_freeze(struct device *dev)
++{
++      struct rcar_gen2_usb_phy_priv *priv = dev_get_drvdata(dev);
++      pr_info("freeze: %p\n", priv->base);
++
++      return phy_rcar_gen2_pm_suspend(dev);
++}
++
++static int phy_rcar_gen2_pm_restore(struct device *dev)
++{
++      struct rcar_gen2_usb_phy_priv *priv = dev_get_drvdata(dev);
++      struct resource *res;
++
++      res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 0);
++      priv->base = devm_ioremap_resource(dev, res);
++      if (IS_ERR(priv->base)) {
++              pr_info("restore: pointer error %ld\n", PTR_ERR(priv->base));
++              return PTR_ERR(priv->base);
++      }
++      pr_info("restore: %p\n", priv->base);
++      __rcar_gen2_usb_phy_init(priv);
++      devm_release_mem_region(dev, res->start,
++                                              resource_size(res));
++      devm_iounmap(dev, priv->base);
++      priv->base = NULL;
++      return phy_rcar_gen2_pm_resume(dev);
++}
++
+ static const struct dev_pm_ops phy_rcar_gen2_dev_pm_ops = {
+       .suspend        = phy_rcar_gen2_pm_suspend,
+       .resume         = phy_rcar_gen2_pm_resume,
++      .freeze_noirq   = phy_rcar_gen2_pm_freeze,
++      .restore        = phy_rcar_gen2_pm_restore,
++      .thaw           = phy_rcar_gen2_pm_resume,
++      .poweroff       = phy_rcar_gen2_pm_suspend,
+ };
+ #endif
+-- 
+1.8.3.1
+
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/hibernation.cfg b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/hibernation.cfg
new file mode 100755 (executable)
index 0000000..45521d2
--- /dev/null
@@ -0,0 +1,10 @@
+CONFIG_SWSUSP_AREA=0x7A000000
+CONFIG_SWSUSP_AREA_SIZE=0x4000000
+CONFIG_HIBERNATE_CALLBACKS=y
+CONFIG_HIBERNATION=y
+CONFIG_PM_STD_PARTITION=""
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_MTD_SWAP=y
+CONFIG_MTD_PHRAM=y
+CONFIG_MMC_UNSAFE_RESUME=y
+