diff options
author | Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com> | 2017-05-22 00:15:23 +0900 |
---|---|---|
committer | Jan-Simon Moeller <jsmoeller@linuxfoundation.org> | 2017-05-25 13:47:13 +0000 |
commit | a14e289caaae4c342c2bc686bd5d327ed612b0fc (patch) | |
tree | 87cc82526cadd31c77f72a208421e44ee97e7a3a | |
parent | 5b5a54b60f45e67d647cf6cc0fe2b879b2bb8229 (diff) |
Add kernel Hibernation code for porter board.
This patch set is a support to Hibernation for a porter board.
I've commit with Hibernation Off patch, because it depends strongly on user land.
If you can use Hibernation, Please add local.conf agl-porter-hibernate.
OVERRIDES .= ":agl-porter-hibernate"
DISTRO_FEATURES_append = " agl-porter-hibernate"
Change-Id: 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, 4413 insertions, 0 deletions
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux-renesas_%.bbappend b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux-renesas_%.bbappend index 29dcc75cb..35b225354 100644..100755 --- a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux-renesas_%.bbappend +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux-renesas_%.bbappend @@ -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 index 000000000..87cd2863a --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0001-Add-Hibernation-kernel-base-code.patch @@ -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(®ion->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 index 000000000..4db90e4e0 --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0002-Add-Hibernation-arch-code-Only-R-CAR-M2W.patch @@ -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 index 000000000..fd0dfb66f --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0003-Add-sata-hibernation-code.patch @@ -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 index 000000000..d0250762e --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0004-Add-firmware-hibernation-code.patch @@ -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 index 000000000..b446fa301 --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0005-Add-rcar-dma-hibernation-code.patch @@ -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 index 000000000..8942ed44e --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0006-Add-rcar-du-hibernation-code.patch @@ -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 index 000000000..bba1eb401 --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0007-Add-rcar-i2c-hibernation-code.patch @@ -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 index 000000000..34b40a147 --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0008-Add-rcar-mmc-hibernation-code.patch @@ -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 index 000000000..a3495e650 --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0009-Add-hibernation-store-area.patch @@ -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 index 000000000..55d1216fe --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0010-Add-rcar-eth-hibernation-code.patch @@ -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 index 000000000..bdc9555c8 --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0011-Add-rcar-pci-hibernation-code.patch @@ -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 index 000000000..9b2aff4f9 --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0012-Add-rcar-gpio-hibernation-code.patch @@ -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(®s_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, ®s_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 index 000000000..515b08b12 --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0013-Add-rcar-spi-hibernation-code.patch @@ -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 index 000000000..c70f515a2 --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0014-Add-rcar-sci-hibernation-code.patch @@ -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 index 000000000..c0c2b1675 --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0015-Add-rcar-usbphy-hibernation-code.patch @@ -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 index 000000000..45521d25d --- /dev/null +++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/hibernation.cfg @@ -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 + |