1 // SPDX-License-Identifier: GPL-2.0 << 2 /* 1 /* >> 2 * linux/kernel/irq/pm.c >> 3 * 3 * Copyright (C) 2009 Rafael J. Wysocki <rjw@s 4 * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. 4 * 5 * 5 * This file contains power management functio 6 * This file contains power management functions related to interrupts. 6 */ 7 */ 7 8 8 #include <linux/irq.h> 9 #include <linux/irq.h> 9 #include <linux/module.h> 10 #include <linux/module.h> 10 #include <linux/interrupt.h> 11 #include <linux/interrupt.h> 11 #include <linux/suspend.h> << 12 #include <linux/syscore_ops.h> 12 #include <linux/syscore_ops.h> 13 13 14 #include "internals.h" 14 #include "internals.h" 15 15 16 bool irq_pm_check_wakeup(struct irq_desc *desc << 17 { << 18 if (irqd_is_wakeup_armed(&desc->irq_da << 19 irqd_clear(&desc->irq_data, IR << 20 desc->istate |= IRQS_SUSPENDED << 21 desc->depth++; << 22 irq_disable(desc); << 23 pm_system_irq_wakeup(irq_desc_ << 24 return true; << 25 } << 26 return false; << 27 } << 28 << 29 /* << 30 * Called from __setup_irq() with desc->lock h << 31 * been installed in the action chain. << 32 */ << 33 void irq_pm_install_action(struct irq_desc *de << 34 { << 35 desc->nr_actions++; << 36 << 37 if (action->flags & IRQF_FORCE_RESUME) << 38 desc->force_resume_depth++; << 39 << 40 WARN_ON_ONCE(desc->force_resume_depth << 41 desc->force_resume_depth << 42 << 43 if (action->flags & IRQF_NO_SUSPEND) << 44 desc->no_suspend_depth++; << 45 else if (action->flags & IRQF_COND_SUS << 46 desc->cond_suspend_depth++; << 47 << 48 WARN_ON_ONCE(desc->no_suspend_depth && << 49 (desc->no_suspend_depth + << 50 desc->cond_suspend_dep << 51 } << 52 << 53 /* << 54 * Called from __free_irq() with desc->lock he << 55 * been removed from the action chain. << 56 */ << 57 void irq_pm_remove_action(struct irq_desc *des << 58 { << 59 desc->nr_actions--; << 60 << 61 if (action->flags & IRQF_FORCE_RESUME) << 62 desc->force_resume_depth--; << 63 << 64 if (action->flags & IRQF_NO_SUSPEND) << 65 desc->no_suspend_depth--; << 66 else if (action->flags & IRQF_COND_SUS << 67 desc->cond_suspend_depth--; << 68 } << 69 << 70 static bool suspend_device_irq(struct irq_desc << 71 { << 72 unsigned long chipflags = irq_desc_get << 73 struct irq_data *irqd = &desc->irq_dat << 74 << 75 if (!desc->action || irq_desc_is_chain << 76 desc->no_suspend_depth) << 77 return false; << 78 << 79 if (irqd_is_wakeup_set(irqd)) { << 80 irqd_set(irqd, IRQD_WAKEUP_ARM << 81 << 82 if ((chipflags & IRQCHIP_ENABL << 83 irqd_irq_disabled(irqd)) << 84 /* << 85 * Interrupt marked fo << 86 * Enable interrupt he << 87 * to be able to resum << 88 */ << 89 __enable_irq(desc); << 90 irqd_set(irqd, IRQD_IR << 91 } << 92 /* << 93 * We return true here to forc << 94 * synchronize_irq(). We need << 95 * IRQD_WAKEUP_ARMED is visibl << 96 * suspend_device_irqs(). << 97 */ << 98 return true; << 99 } << 100 << 101 desc->istate |= IRQS_SUSPENDED; << 102 __disable_irq(desc); << 103 << 104 /* << 105 * Hardware which has no wakeup source << 106 * requires that the non wakeup interr << 107 * chip level. The chip implementation << 108 * IRQCHIP_MASK_ON_SUSPEND. << 109 */ << 110 if (chipflags & IRQCHIP_MASK_ON_SUSPEN << 111 mask_irq(desc); << 112 return true; << 113 } << 114 << 115 /** 16 /** 116 * suspend_device_irqs - disable all currently 17 * suspend_device_irqs - disable all currently enabled interrupt lines 117 * 18 * 118 * During system-wide suspend or hibernation d !! 19 * During system-wide suspend or hibernation device drivers need to be prevented 119 * prevented from receiving interrupts and thi !! 20 * from receiving interrupts and this function is provided for this purpose. 120 * for this purpose. !! 21 * It marks all interrupt lines in use, except for the timer ones, as disabled 121 * !! 22 * and sets the IRQS_SUSPENDED flag for each of them. 122 * So we disable all interrupts and mark them << 123 * for those which are unused, those which are << 124 * suspendable via an interrupt request with t << 125 * set and those which are marked as active wa << 126 * << 127 * The active wakeup sources are handled by th << 128 * code which checks for the IRQD_WAKEUP_ARMED << 129 * interrupt and notifies the pm core about th << 130 */ 23 */ 131 void suspend_device_irqs(void) 24 void suspend_device_irqs(void) 132 { 25 { 133 struct irq_desc *desc; 26 struct irq_desc *desc; 134 int irq; 27 int irq; 135 28 136 for_each_irq_desc(irq, desc) { 29 for_each_irq_desc(irq, desc) { 137 unsigned long flags; 30 unsigned long flags; 138 bool sync; << 139 31 140 if (irq_settings_is_nested_thr << 141 continue; << 142 raw_spin_lock_irqsave(&desc->l 32 raw_spin_lock_irqsave(&desc->lock, flags); 143 sync = suspend_device_irq(desc !! 33 __disable_irq(desc, irq, true); 144 raw_spin_unlock_irqrestore(&de 34 raw_spin_unlock_irqrestore(&desc->lock, flags); 145 << 146 if (sync) << 147 synchronize_irq(irq); << 148 } << 149 } << 150 << 151 static void resume_irq(struct irq_desc *desc) << 152 { << 153 struct irq_data *irqd = &desc->irq_dat << 154 << 155 irqd_clear(irqd, IRQD_WAKEUP_ARMED); << 156 << 157 if (irqd_is_enabled_on_suspend(irqd)) << 158 /* << 159 * Interrupt marked for wakeup << 160 * entry. Disable such interru << 161 * original state. << 162 */ << 163 __disable_irq(desc); << 164 irqd_clear(irqd, IRQD_IRQ_ENAB << 165 } 35 } 166 36 167 if (desc->istate & IRQS_SUSPENDED) !! 37 for_each_irq_desc(irq, desc) 168 goto resume; !! 38 if (desc->istate & IRQS_SUSPENDED) 169 !! 39 synchronize_irq(irq); 170 /* Force resume the interrupt? */ << 171 if (!desc->force_resume_depth) << 172 return; << 173 << 174 /* Pretend that it got disabled ! */ << 175 desc->depth++; << 176 irq_state_set_disabled(desc); << 177 irq_state_set_masked(desc); << 178 resume: << 179 desc->istate &= ~IRQS_SUSPENDED; << 180 __enable_irq(desc); << 181 } 40 } >> 41 EXPORT_SYMBOL_GPL(suspend_device_irqs); 182 42 183 static void resume_irqs(bool want_early) 43 static void resume_irqs(bool want_early) 184 { 44 { 185 struct irq_desc *desc; 45 struct irq_desc *desc; 186 int irq; 46 int irq; 187 47 188 for_each_irq_desc(irq, desc) { 48 for_each_irq_desc(irq, desc) { 189 unsigned long flags; 49 unsigned long flags; 190 bool is_early = desc->action & 50 bool is_early = desc->action && 191 desc->action->flags & 51 desc->action->flags & IRQF_EARLY_RESUME; 192 52 193 if (!is_early && want_early) 53 if (!is_early && want_early) 194 continue; 54 continue; 195 if (irq_settings_is_nested_thr << 196 continue; << 197 55 198 raw_spin_lock_irqsave(&desc->l 56 raw_spin_lock_irqsave(&desc->lock, flags); 199 resume_irq(desc); !! 57 __enable_irq(desc, irq, true); 200 raw_spin_unlock_irqrestore(&de 58 raw_spin_unlock_irqrestore(&desc->lock, flags); 201 } 59 } 202 } 60 } 203 61 204 /** 62 /** 205 * rearm_wake_irq - rearm a wakeup interrupt l !! 63 * irq_pm_syscore_ops - enable interrupt lines early 206 * @irq: Interrupt to rearm << 207 */ << 208 void rearm_wake_irq(unsigned int irq) << 209 { << 210 unsigned long flags; << 211 struct irq_desc *desc = irq_get_desc_b << 212 << 213 if (!desc) << 214 return; << 215 << 216 if (!(desc->istate & IRQS_SUSPENDED) | << 217 !irqd_is_wakeup_set(&desc->irq_dat << 218 goto unlock; << 219 << 220 desc->istate &= ~IRQS_SUSPENDED; << 221 irqd_set(&desc->irq_data, IRQD_WAKEUP_ << 222 __enable_irq(desc); << 223 << 224 unlock: << 225 irq_put_desc_busunlock(desc, flags); << 226 } << 227 << 228 /** << 229 * irq_pm_syscore_resume - enable interrupt li << 230 * 64 * 231 * Enable all interrupt lines with %IRQF_EARLY 65 * Enable all interrupt lines with %IRQF_EARLY_RESUME set. 232 */ 66 */ 233 static void irq_pm_syscore_resume(void) 67 static void irq_pm_syscore_resume(void) 234 { 68 { 235 resume_irqs(true); 69 resume_irqs(true); 236 } 70 } 237 71 238 static struct syscore_ops irq_pm_syscore_ops = 72 static struct syscore_ops irq_pm_syscore_ops = { 239 .resume = irq_pm_syscore_resum 73 .resume = irq_pm_syscore_resume, 240 }; 74 }; 241 75 242 static int __init irq_pm_init_ops(void) 76 static int __init irq_pm_init_ops(void) 243 { 77 { 244 register_syscore_ops(&irq_pm_syscore_o 78 register_syscore_ops(&irq_pm_syscore_ops); 245 return 0; 79 return 0; 246 } 80 } 247 81 248 device_initcall(irq_pm_init_ops); 82 device_initcall(irq_pm_init_ops); 249 83 250 /** 84 /** 251 * resume_device_irqs - enable interrupt lines 85 * resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs() 252 * 86 * 253 * Enable all non-%IRQF_EARLY_RESUME interrupt 87 * Enable all non-%IRQF_EARLY_RESUME interrupt lines previously 254 * disabled by suspend_device_irqs() that have 88 * disabled by suspend_device_irqs() that have the IRQS_SUSPENDED flag 255 * set as well as those with %IRQF_FORCE_RESUM 89 * set as well as those with %IRQF_FORCE_RESUME. 256 */ 90 */ 257 void resume_device_irqs(void) 91 void resume_device_irqs(void) 258 { 92 { 259 resume_irqs(false); 93 resume_irqs(false); >> 94 } >> 95 EXPORT_SYMBOL_GPL(resume_device_irqs); >> 96 >> 97 /** >> 98 * check_wakeup_irqs - check if any wake-up interrupts are pending >> 99 */ >> 100 int check_wakeup_irqs(void) >> 101 { >> 102 struct irq_desc *desc; >> 103 int irq; >> 104 >> 105 for_each_irq_desc(irq, desc) { >> 106 /* >> 107 * Only interrupts which are marked as wakeup source >> 108 * and have not been disabled before the suspend check >> 109 * can abort suspend. >> 110 */ >> 111 if (irqd_is_wakeup_set(&desc->irq_data)) { >> 112 if (desc->depth == 1 && desc->istate & IRQS_PENDING) >> 113 return -EBUSY; >> 114 continue; >> 115 } >> 116 /* >> 117 * Check the non wakeup interrupts whether they need >> 118 * to be masked before finally going into suspend >> 119 * state. That's for hardware which has no wakeup >> 120 * source configuration facility. The chip >> 121 * implementation indicates that with >> 122 * IRQCHIP_MASK_ON_SUSPEND. >> 123 */ >> 124 if (desc->istate & IRQS_SUSPENDED && >> 125 irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND) >> 126 mask_irq(desc); >> 127 } >> 128 >> 129 return 0; 260 } 130 } 261 131
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.