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> << 13 12 14 #include "internals.h" 13 #include "internals.h" 15 14 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 /** 15 /** 116 * suspend_device_irqs - disable all currently 16 * suspend_device_irqs - disable all currently enabled interrupt lines 117 * 17 * 118 * During system-wide suspend or hibernation d !! 18 * During system-wide suspend or hibernation device drivers need to be prevented 119 * prevented from receiving interrupts and thi !! 19 * from receiving interrupts and this function is provided for this purpose. 120 * for this purpose. !! 20 * It marks all interrupt lines in use, except for the timer ones, as disabled 121 * !! 21 * and sets the IRQ_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 */ 22 */ 131 void suspend_device_irqs(void) 23 void suspend_device_irqs(void) 132 { 24 { 133 struct irq_desc *desc; 25 struct irq_desc *desc; 134 int irq; 26 int irq; 135 27 136 for_each_irq_desc(irq, desc) { 28 for_each_irq_desc(irq, desc) { 137 unsigned long flags; 29 unsigned long flags; 138 bool sync; << 139 30 140 if (irq_settings_is_nested_thr !! 31 spin_lock_irqsave(&desc->lock, flags); 141 continue; !! 32 __disable_irq(desc, irq, true); 142 raw_spin_lock_irqsave(&desc->l !! 33 spin_unlock_irqrestore(&desc->lock, flags); 143 sync = suspend_device_irq(desc << 144 raw_spin_unlock_irqrestore(&de << 145 << 146 if (sync) << 147 synchronize_irq(irq); << 148 } 34 } 149 } << 150 35 151 static void resume_irq(struct irq_desc *desc) !! 36 for_each_irq_desc(irq, desc) 152 { !! 37 if (desc->status & IRQ_SUSPENDED) 153 struct irq_data *irqd = &desc->irq_dat !! 38 synchronize_irq(irq); 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 } << 166 << 167 if (desc->istate & IRQS_SUSPENDED) << 168 goto resume; << 169 << 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 } 39 } >> 40 EXPORT_SYMBOL_GPL(suspend_device_irqs); 182 41 183 static void resume_irqs(bool want_early) 42 static void resume_irqs(bool want_early) 184 { 43 { 185 struct irq_desc *desc; 44 struct irq_desc *desc; 186 int irq; 45 int irq; 187 46 188 for_each_irq_desc(irq, desc) { 47 for_each_irq_desc(irq, desc) { 189 unsigned long flags; 48 unsigned long flags; 190 bool is_early = desc->action & 49 bool is_early = desc->action && 191 desc->action->flags & 50 desc->action->flags & IRQF_EARLY_RESUME; 192 51 193 if (!is_early && want_early) !! 52 if (is_early != want_early) 194 continue; << 195 if (irq_settings_is_nested_thr << 196 continue; 53 continue; 197 54 198 raw_spin_lock_irqsave(&desc->l !! 55 spin_lock_irqsave(&desc->lock, flags); 199 resume_irq(desc); !! 56 __enable_irq(desc, irq, true); 200 raw_spin_unlock_irqrestore(&de !! 57 spin_unlock_irqrestore(&desc->lock, flags); 201 } 58 } 202 } 59 } 203 60 204 /** 61 /** 205 * rearm_wake_irq - rearm a wakeup interrupt l !! 62 * 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 * 63 * 231 * Enable all interrupt lines with %IRQF_EARLY 64 * Enable all interrupt lines with %IRQF_EARLY_RESUME set. 232 */ 65 */ 233 static void irq_pm_syscore_resume(void) !! 66 void irq_pm_syscore_resume(void) 234 { 67 { 235 resume_irqs(true); 68 resume_irqs(true); 236 } 69 } 237 70 238 static struct syscore_ops irq_pm_syscore_ops = << 239 .resume = irq_pm_syscore_resum << 240 }; << 241 << 242 static int __init irq_pm_init_ops(void) << 243 { << 244 register_syscore_ops(&irq_pm_syscore_o << 245 return 0; << 246 } << 247 << 248 device_initcall(irq_pm_init_ops); << 249 << 250 /** 71 /** 251 * resume_device_irqs - enable interrupt lines 72 * resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs() 252 * 73 * 253 * Enable all non-%IRQF_EARLY_RESUME interrupt 74 * Enable all non-%IRQF_EARLY_RESUME interrupt lines previously 254 * disabled by suspend_device_irqs() that have 75 * disabled by suspend_device_irqs() that have the IRQS_SUSPENDED flag 255 * set as well as those with %IRQF_FORCE_RESUM 76 * set as well as those with %IRQF_FORCE_RESUME. 256 */ 77 */ 257 void resume_device_irqs(void) 78 void resume_device_irqs(void) 258 { 79 { 259 resume_irqs(false); 80 resume_irqs(false); >> 81 } >> 82 EXPORT_SYMBOL_GPL(resume_device_irqs); >> 83 >> 84 /** >> 85 * check_wakeup_irqs - check if any wake-up interrupts are pending >> 86 */ >> 87 int check_wakeup_irqs(void) >> 88 { >> 89 struct irq_desc *desc; >> 90 int irq; >> 91 >> 92 for_each_irq_desc(irq, desc) >> 93 if ((desc->status & IRQ_WAKEUP) && (desc->status & IRQ_PENDING)) >> 94 return -EBUSY; >> 95 >> 96 return 0; 260 } 97 } 261 98
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.