yuezonghe | 824eb0c | 2024-06-27 02:32:26 -0700 | [diff] [blame] | 1 | /* drivers/base/power/zx-suspend.c
|
| 2 | *
|
| 3 | * ZTE suspend manager
|
| 4 | *
|
| 5 | * Copyright (C) 2013 ZTE Ltd.
|
| 6 | * by zxp
|
| 7 | *
|
| 8 | */
|
| 9 |
|
| 10 | #include <linux/module.h>
|
| 11 | #include <linux/platform_device.h>
|
| 12 | #include <linux/rtc.h>
|
| 13 | #include <linux/suspend.h>
|
| 14 | #include <linux/syscalls.h> /* sys_sync */
|
| 15 | #include <linux/wakelock.h>
|
| 16 | #include "power.h"
|
| 17 |
|
| 18 | //#define CONFIG_LINUX_AUTOSLEEP /* may like MTK code to use linux autosleep interface */
|
| 19 |
|
| 20 | static struct workqueue_struct *sync_work_queue;
|
| 21 | static struct wake_lock sync_wake_lock;
|
| 22 |
|
| 23 | static struct workqueue_struct *suspend_work_queue;
|
| 24 | static struct wake_lock main_wake_lock;
|
| 25 | static struct wake_lock wakeup_irq_lock;
|
| 26 | static suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
|
| 27 | static bool zx_pm_init = false;
|
| 28 | static unsigned int suspend_state = 0;
|
| 29 | static DEFINE_MUTEX(suspend_lock);
|
| 30 | static DEFINE_SPINLOCK(zx_suspend_lock);
|
| 31 |
|
| 32 |
|
| 33 | extern void pm_debug_wakelocks(void);
|
| 34 | extern bool pm_disable_suspend(void);
|
| 35 |
|
| 36 | enum {
|
| 37 | DEBUG_EXIT_SUSPEND = 1U << 0,
|
| 38 | DEBUG_WAKE_LOCK = 1U << 1,
|
| 39 | DEBUG_SUSPEND = 1U << 2,
|
| 40 | };
|
| 41 |
|
| 42 | static int debug_mask = DEBUG_EXIT_SUSPEND | /* DEBUG_WAKE_LOCK |*/ DEBUG_SUSPEND;
|
| 43 | /* /sys/module/zx-suspend/parameters/debug_mask */
|
| 44 | module_param(debug_mask, int, 0644);
|
| 45 |
|
| 46 |
|
| 47 | static void sync_system(struct work_struct *work)
|
| 48 | {
|
| 49 | sys_sync();
|
| 50 | wake_unlock(&sync_wake_lock);
|
| 51 |
|
| 52 | if (debug_mask & DEBUG_SUSPEND)
|
| 53 | pr_info("[SLP]: sync done\n");
|
| 54 | }
|
| 55 |
|
| 56 | static DECLARE_WORK(sync_system_work, sync_system);
|
| 57 |
|
| 58 | void test_wakelock(void)
|
| 59 | {
|
| 60 | #ifndef CONFIG_ZX_AUTOSLEEP
|
| 61 | wake_lock(&sync_wake_lock);
|
| 62 | queue_work(sync_work_queue, &sync_system_work);
|
| 63 | #endif
|
| 64 |
|
| 65 | wake_unlock(&main_wake_lock);
|
| 66 | }
|
| 67 |
|
| 68 | #ifdef CONFIG_ZX_AUTOSLEEP
|
| 69 | void app_start_done(void)
|
| 70 | {
|
| 71 | wake_unlock(&main_wake_lock);
|
| 72 | }
|
| 73 | EXPORT_SYMBOL_GPL(app_start_done);
|
| 74 | #endif
|
| 75 |
|
| 76 | extern volatile int wakelock_current_event_num;
|
| 77 | static void suspend(struct work_struct *work)
|
| 78 | {
|
| 79 | int ret;
|
| 80 | int entry_event_num;
|
| 81 | struct timespec ts_entry, ts_exit;
|
| 82 |
|
| 83 | mutex_lock(&suspend_lock);
|
| 84 | suspend_state = 1;
|
| 85 | entry_event_num = wakelock_current_event_num;
|
| 86 | #ifdef CONFIG_ZX_AUTOSLEEP
|
| 87 | sys_sync();
|
| 88 | #endif
|
| 89 |
|
| 90 | getnstimeofday(&ts_entry);
|
| 91 | ret = pm_suspend(requested_suspend_state);
|
| 92 | getnstimeofday(&ts_exit);
|
| 93 |
|
| 94 | if (debug_mask & DEBUG_EXIT_SUSPEND) {
|
| 95 | struct rtc_time tm;
|
| 96 | rtc_time_to_tm(ts_exit.tv_sec, &tm);
|
| 97 | pr_info("[SLP]: exit suspend, ret = %d "
|
| 98 | "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
|
| 99 | tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
| 100 | tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec);
|
| 101 | }
|
| 102 | #ifdef CONFIG_ZX_AUTOSLEEP
|
| 103 | // wake_lock_timeout(&main_wake_lock, 2 ); /* 2 jiffies */
|
| 104 | if(wakelock_current_event_num == entry_event_num)
|
| 105 | wake_lock_timeout(&main_wake_lock, msecs_to_jiffies(1*1000));
|
| 106 | #else
|
| 107 | wake_lock(&main_wake_lock);
|
| 108 | #endif
|
| 109 |
|
| 110 | suspend_state = 0;
|
| 111 | mutex_unlock(&suspend_lock);
|
| 112 |
|
| 113 | }
|
| 114 |
|
| 115 | static DECLARE_WORK(suspend_work, suspend);
|
| 116 |
|
| 117 | /**
|
| 118 | * zx_pm_suspend
|
| 119 | *
|
| 120 | * try suspend when wakelock is unlocked or expired anytime.
|
| 121 | *
|
| 122 | */
|
| 123 | void zx_pm_suspend(void)
|
| 124 | {
|
| 125 |
|
| 126 | #if 1
|
| 127 | //if(!zx_pm_init)
|
| 128 | return ;
|
| 129 | #else
|
| 130 | unsigned int temp_count;
|
| 131 | unsigned long flags;
|
| 132 |
|
| 133 | if(!zx_pm_init)
|
| 134 | return ;
|
| 135 |
|
| 136 | if(pm_disable_suspend())
|
| 137 | return;
|
| 138 |
|
| 139 | if (debug_mask & DEBUG_WAKE_LOCK)
|
| 140 | pm_debug_wakelocks();
|
| 141 |
|
| 142 | spin_lock_irqsave(&zx_suspend_lock, flags);
|
| 143 | if(pm_get_wakeup_count(&temp_count, false))
|
| 144 | {
|
| 145 | if (pm_save_wakeup_count(temp_count))
|
| 146 | {
|
| 147 | spin_unlock_irqrestore(&zx_suspend_lock, flags);
|
| 148 | if (!suspend_state)
|
| 149 | queue_work(suspend_work_queue, &suspend_work);
|
| 150 |
|
| 151 | pm_get_wakeup_count(&temp_count, false);
|
| 152 | }
|
| 153 | else
|
| 154 | {
|
| 155 | spin_unlock_irqrestore(&zx_suspend_lock, flags);
|
| 156 | //error save the count, why?
|
| 157 | if (debug_mask & DEBUG_SUSPEND)
|
| 158 | {
|
| 159 | pr_info("suspend: error save wakeup_count: %d ", temp_count);
|
| 160 | }
|
| 161 | }
|
| 162 | }
|
| 163 | else
|
| 164 | {
|
| 165 | spin_unlock_irqrestore(&zx_suspend_lock, flags);
|
| 166 | }
|
| 167 | #endif
|
| 168 |
|
| 169 | }
|
| 170 | EXPORT_SYMBOL_GPL(zx_pm_suspend);
|
| 171 |
|
| 172 | void zx_pm_wakeup_irq_timeout(void)
|
| 173 | {
|
| 174 | if (!zx_pm_init)
|
| 175 | return;
|
| 176 | wake_lock_timeout(&wakeup_irq_lock, msecs_to_jiffies(1*1000));
|
| 177 | }
|
| 178 | EXPORT_SYMBOL_GPL(zx_pm_wakeup_irq_timeout);
|
| 179 | static int __init zx_suspend_init(void)
|
| 180 | {
|
| 181 | int ret;
|
| 182 |
|
| 183 | /* we init a main wakelock for maintain system on until app start ok! */
|
| 184 | wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");
|
| 185 | wake_lock_init(&sync_wake_lock, WAKE_LOCK_SUSPEND, "sync_system");
|
| 186 | wake_lock_init(&wakeup_irq_lock, WAKE_LOCK_SUSPEND, "wakeup_irq");
|
| 187 | // wake_lock(&main_wake_lock);
|
| 188 | wake_lock_timeout(&main_wake_lock, msecs_to_jiffies(60*1000));
|
| 189 |
|
| 190 | suspend_work_queue = create_singlethread_workqueue("suspend");
|
| 191 | if (suspend_work_queue == NULL)
|
| 192 | {
|
| 193 | ret = -ENOMEM;
|
| 194 | goto err_suspend_work_queue;
|
| 195 | }
|
| 196 |
|
| 197 | sync_work_queue = create_singlethread_workqueue("sync_system_work");
|
| 198 | if (sync_work_queue == NULL) {
|
| 199 | pr_err("%s: failed to create sync_work_queue\n", __func__);
|
| 200 | ret = -ENOMEM;
|
| 201 | goto err_sync_work_queue;
|
| 202 | }
|
| 203 |
|
| 204 | zx_pm_init = true;
|
| 205 |
|
| 206 | return 0;
|
| 207 |
|
| 208 | err_sync_work_queue:
|
| 209 | destroy_workqueue(suspend_work_queue);
|
| 210 |
|
| 211 | err_suspend_work_queue:
|
| 212 | wake_lock_destroy(&main_wake_lock);
|
| 213 | wake_lock_destroy(&sync_wake_lock);
|
| 214 |
|
| 215 | return ret;
|
| 216 | }
|
| 217 |
|
| 218 | static void __exit zx_suspend_exit(void)
|
| 219 | {
|
| 220 | destroy_workqueue(sync_work_queue);
|
| 221 | destroy_workqueue(suspend_work_queue);
|
| 222 |
|
| 223 | wake_lock_destroy(&sync_wake_lock);
|
| 224 | wake_lock_destroy(&main_wake_lock);
|
| 225 | }
|
| 226 |
|
| 227 | late_initcall(zx_suspend_init);
|
| 228 | module_exit(zx_suspend_exit);
|
| 229 |
|