1*bf3b1e0cSMichael J. Ruhl // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 2*bf3b1e0cSMichael J. Ruhl /* 3*bf3b1e0cSMichael J. Ruhl * Copyright(c) 2019 Intel Corporation. 4*bf3b1e0cSMichael J. Ruhl * 5*bf3b1e0cSMichael J. Ruhl */ 6*bf3b1e0cSMichael J. Ruhl 7*bf3b1e0cSMichael J. Ruhl #include "aspm.h" 8*bf3b1e0cSMichael J. Ruhl 9*bf3b1e0cSMichael J. Ruhl /* Time after which the timer interrupt will re-enable ASPM */ 10*bf3b1e0cSMichael J. Ruhl #define ASPM_TIMER_MS 1000 11*bf3b1e0cSMichael J. Ruhl /* Time for which interrupts are ignored after a timer has been scheduled */ 12*bf3b1e0cSMichael J. Ruhl #define ASPM_RESCHED_TIMER_MS (ASPM_TIMER_MS / 2) 13*bf3b1e0cSMichael J. Ruhl /* Two interrupts within this time trigger ASPM disable */ 14*bf3b1e0cSMichael J. Ruhl #define ASPM_TRIGGER_MS 1 15*bf3b1e0cSMichael J. Ruhl #define ASPM_TRIGGER_NS (ASPM_TRIGGER_MS * 1000 * 1000ull) 16*bf3b1e0cSMichael J. Ruhl #define ASPM_L1_SUPPORTED(reg) \ 17*bf3b1e0cSMichael J. Ruhl ((((reg) & PCI_EXP_LNKCAP_ASPMS) >> 10) & 0x2) 18*bf3b1e0cSMichael J. Ruhl 19*bf3b1e0cSMichael J. Ruhl uint aspm_mode = ASPM_MODE_DISABLED; 20*bf3b1e0cSMichael J. Ruhl module_param_named(aspm, aspm_mode, uint, 0444); 21*bf3b1e0cSMichael J. Ruhl MODULE_PARM_DESC(aspm, "PCIe ASPM: 0: disable, 1: enable, 2: dynamic"); 22*bf3b1e0cSMichael J. Ruhl 23*bf3b1e0cSMichael J. Ruhl static bool aspm_hw_l1_supported(struct hfi1_devdata *dd) 24*bf3b1e0cSMichael J. Ruhl { 25*bf3b1e0cSMichael J. Ruhl struct pci_dev *parent = dd->pcidev->bus->self; 26*bf3b1e0cSMichael J. Ruhl u32 up, dn; 27*bf3b1e0cSMichael J. Ruhl 28*bf3b1e0cSMichael J. Ruhl /* 29*bf3b1e0cSMichael J. Ruhl * If the driver does not have access to the upstream component, 30*bf3b1e0cSMichael J. Ruhl * it cannot support ASPM L1 at all. 31*bf3b1e0cSMichael J. Ruhl */ 32*bf3b1e0cSMichael J. Ruhl if (!parent) 33*bf3b1e0cSMichael J. Ruhl return false; 34*bf3b1e0cSMichael J. Ruhl 35*bf3b1e0cSMichael J. Ruhl pcie_capability_read_dword(dd->pcidev, PCI_EXP_LNKCAP, &dn); 36*bf3b1e0cSMichael J. Ruhl dn = ASPM_L1_SUPPORTED(dn); 37*bf3b1e0cSMichael J. Ruhl 38*bf3b1e0cSMichael J. Ruhl pcie_capability_read_dword(parent, PCI_EXP_LNKCAP, &up); 39*bf3b1e0cSMichael J. Ruhl up = ASPM_L1_SUPPORTED(up); 40*bf3b1e0cSMichael J. Ruhl 41*bf3b1e0cSMichael J. Ruhl /* ASPM works on A-step but is reported as not supported */ 42*bf3b1e0cSMichael J. Ruhl return (!!dn || is_ax(dd)) && !!up; 43*bf3b1e0cSMichael J. Ruhl } 44*bf3b1e0cSMichael J. Ruhl 45*bf3b1e0cSMichael J. Ruhl /* Set L1 entrance latency for slower entry to L1 */ 46*bf3b1e0cSMichael J. Ruhl static void aspm_hw_set_l1_ent_latency(struct hfi1_devdata *dd) 47*bf3b1e0cSMichael J. Ruhl { 48*bf3b1e0cSMichael J. Ruhl u32 l1_ent_lat = 0x4u; 49*bf3b1e0cSMichael J. Ruhl u32 reg32; 50*bf3b1e0cSMichael J. Ruhl 51*bf3b1e0cSMichael J. Ruhl pci_read_config_dword(dd->pcidev, PCIE_CFG_REG_PL3, ®32); 52*bf3b1e0cSMichael J. Ruhl reg32 &= ~PCIE_CFG_REG_PL3_L1_ENT_LATENCY_SMASK; 53*bf3b1e0cSMichael J. Ruhl reg32 |= l1_ent_lat << PCIE_CFG_REG_PL3_L1_ENT_LATENCY_SHIFT; 54*bf3b1e0cSMichael J. Ruhl pci_write_config_dword(dd->pcidev, PCIE_CFG_REG_PL3, reg32); 55*bf3b1e0cSMichael J. Ruhl } 56*bf3b1e0cSMichael J. Ruhl 57*bf3b1e0cSMichael J. Ruhl static void aspm_hw_enable_l1(struct hfi1_devdata *dd) 58*bf3b1e0cSMichael J. Ruhl { 59*bf3b1e0cSMichael J. Ruhl struct pci_dev *parent = dd->pcidev->bus->self; 60*bf3b1e0cSMichael J. Ruhl 61*bf3b1e0cSMichael J. Ruhl /* 62*bf3b1e0cSMichael J. Ruhl * If the driver does not have access to the upstream component, 63*bf3b1e0cSMichael J. Ruhl * it cannot support ASPM L1 at all. 64*bf3b1e0cSMichael J. Ruhl */ 65*bf3b1e0cSMichael J. Ruhl if (!parent) 66*bf3b1e0cSMichael J. Ruhl return; 67*bf3b1e0cSMichael J. Ruhl 68*bf3b1e0cSMichael J. Ruhl /* Enable ASPM L1 first in upstream component and then downstream */ 69*bf3b1e0cSMichael J. Ruhl pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, 70*bf3b1e0cSMichael J. Ruhl PCI_EXP_LNKCTL_ASPMC, 71*bf3b1e0cSMichael J. Ruhl PCI_EXP_LNKCTL_ASPM_L1); 72*bf3b1e0cSMichael J. Ruhl pcie_capability_clear_and_set_word(dd->pcidev, PCI_EXP_LNKCTL, 73*bf3b1e0cSMichael J. Ruhl PCI_EXP_LNKCTL_ASPMC, 74*bf3b1e0cSMichael J. Ruhl PCI_EXP_LNKCTL_ASPM_L1); 75*bf3b1e0cSMichael J. Ruhl } 76*bf3b1e0cSMichael J. Ruhl 77*bf3b1e0cSMichael J. Ruhl void aspm_hw_disable_l1(struct hfi1_devdata *dd) 78*bf3b1e0cSMichael J. Ruhl { 79*bf3b1e0cSMichael J. Ruhl struct pci_dev *parent = dd->pcidev->bus->self; 80*bf3b1e0cSMichael J. Ruhl 81*bf3b1e0cSMichael J. Ruhl /* Disable ASPM L1 first in downstream component and then upstream */ 82*bf3b1e0cSMichael J. Ruhl pcie_capability_clear_and_set_word(dd->pcidev, PCI_EXP_LNKCTL, 83*bf3b1e0cSMichael J. Ruhl PCI_EXP_LNKCTL_ASPMC, 0x0); 84*bf3b1e0cSMichael J. Ruhl if (parent) 85*bf3b1e0cSMichael J. Ruhl pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, 86*bf3b1e0cSMichael J. Ruhl PCI_EXP_LNKCTL_ASPMC, 0x0); 87*bf3b1e0cSMichael J. Ruhl } 88*bf3b1e0cSMichael J. Ruhl 89*bf3b1e0cSMichael J. Ruhl static void aspm_enable(struct hfi1_devdata *dd) 90*bf3b1e0cSMichael J. Ruhl { 91*bf3b1e0cSMichael J. Ruhl if (dd->aspm_enabled || aspm_mode == ASPM_MODE_DISABLED || 92*bf3b1e0cSMichael J. Ruhl !dd->aspm_supported) 93*bf3b1e0cSMichael J. Ruhl return; 94*bf3b1e0cSMichael J. Ruhl 95*bf3b1e0cSMichael J. Ruhl aspm_hw_enable_l1(dd); 96*bf3b1e0cSMichael J. Ruhl dd->aspm_enabled = true; 97*bf3b1e0cSMichael J. Ruhl } 98*bf3b1e0cSMichael J. Ruhl 99*bf3b1e0cSMichael J. Ruhl static void aspm_disable(struct hfi1_devdata *dd) 100*bf3b1e0cSMichael J. Ruhl { 101*bf3b1e0cSMichael J. Ruhl if (!dd->aspm_enabled || aspm_mode == ASPM_MODE_ENABLED) 102*bf3b1e0cSMichael J. Ruhl return; 103*bf3b1e0cSMichael J. Ruhl 104*bf3b1e0cSMichael J. Ruhl aspm_hw_disable_l1(dd); 105*bf3b1e0cSMichael J. Ruhl dd->aspm_enabled = false; 106*bf3b1e0cSMichael J. Ruhl } 107*bf3b1e0cSMichael J. Ruhl 108*bf3b1e0cSMichael J. Ruhl static void aspm_disable_inc(struct hfi1_devdata *dd) 109*bf3b1e0cSMichael J. Ruhl { 110*bf3b1e0cSMichael J. Ruhl unsigned long flags; 111*bf3b1e0cSMichael J. Ruhl 112*bf3b1e0cSMichael J. Ruhl spin_lock_irqsave(&dd->aspm_lock, flags); 113*bf3b1e0cSMichael J. Ruhl aspm_disable(dd); 114*bf3b1e0cSMichael J. Ruhl atomic_inc(&dd->aspm_disabled_cnt); 115*bf3b1e0cSMichael J. Ruhl spin_unlock_irqrestore(&dd->aspm_lock, flags); 116*bf3b1e0cSMichael J. Ruhl } 117*bf3b1e0cSMichael J. Ruhl 118*bf3b1e0cSMichael J. Ruhl static void aspm_enable_dec(struct hfi1_devdata *dd) 119*bf3b1e0cSMichael J. Ruhl { 120*bf3b1e0cSMichael J. Ruhl unsigned long flags; 121*bf3b1e0cSMichael J. Ruhl 122*bf3b1e0cSMichael J. Ruhl spin_lock_irqsave(&dd->aspm_lock, flags); 123*bf3b1e0cSMichael J. Ruhl if (atomic_dec_and_test(&dd->aspm_disabled_cnt)) 124*bf3b1e0cSMichael J. Ruhl aspm_enable(dd); 125*bf3b1e0cSMichael J. Ruhl spin_unlock_irqrestore(&dd->aspm_lock, flags); 126*bf3b1e0cSMichael J. Ruhl } 127*bf3b1e0cSMichael J. Ruhl 128*bf3b1e0cSMichael J. Ruhl /* ASPM processing for each receive context interrupt */ 129*bf3b1e0cSMichael J. Ruhl void __aspm_ctx_disable(struct hfi1_ctxtdata *rcd) 130*bf3b1e0cSMichael J. Ruhl { 131*bf3b1e0cSMichael J. Ruhl bool restart_timer; 132*bf3b1e0cSMichael J. Ruhl bool close_interrupts; 133*bf3b1e0cSMichael J. Ruhl unsigned long flags; 134*bf3b1e0cSMichael J. Ruhl ktime_t now, prev; 135*bf3b1e0cSMichael J. Ruhl 136*bf3b1e0cSMichael J. Ruhl spin_lock_irqsave(&rcd->aspm_lock, flags); 137*bf3b1e0cSMichael J. Ruhl /* PSM contexts are open */ 138*bf3b1e0cSMichael J. Ruhl if (!rcd->aspm_intr_enable) 139*bf3b1e0cSMichael J. Ruhl goto unlock; 140*bf3b1e0cSMichael J. Ruhl 141*bf3b1e0cSMichael J. Ruhl prev = rcd->aspm_ts_last_intr; 142*bf3b1e0cSMichael J. Ruhl now = ktime_get(); 143*bf3b1e0cSMichael J. Ruhl rcd->aspm_ts_last_intr = now; 144*bf3b1e0cSMichael J. Ruhl 145*bf3b1e0cSMichael J. Ruhl /* An interrupt pair close together in time */ 146*bf3b1e0cSMichael J. Ruhl close_interrupts = ktime_to_ns(ktime_sub(now, prev)) < ASPM_TRIGGER_NS; 147*bf3b1e0cSMichael J. Ruhl 148*bf3b1e0cSMichael J. Ruhl /* Don't push out our timer till this much time has elapsed */ 149*bf3b1e0cSMichael J. Ruhl restart_timer = ktime_to_ns(ktime_sub(now, rcd->aspm_ts_timer_sched)) > 150*bf3b1e0cSMichael J. Ruhl ASPM_RESCHED_TIMER_MS * NSEC_PER_MSEC; 151*bf3b1e0cSMichael J. Ruhl restart_timer = restart_timer && close_interrupts; 152*bf3b1e0cSMichael J. Ruhl 153*bf3b1e0cSMichael J. Ruhl /* Disable ASPM and schedule timer */ 154*bf3b1e0cSMichael J. Ruhl if (rcd->aspm_enabled && close_interrupts) { 155*bf3b1e0cSMichael J. Ruhl aspm_disable_inc(rcd->dd); 156*bf3b1e0cSMichael J. Ruhl rcd->aspm_enabled = false; 157*bf3b1e0cSMichael J. Ruhl restart_timer = true; 158*bf3b1e0cSMichael J. Ruhl } 159*bf3b1e0cSMichael J. Ruhl 160*bf3b1e0cSMichael J. Ruhl if (restart_timer) { 161*bf3b1e0cSMichael J. Ruhl mod_timer(&rcd->aspm_timer, 162*bf3b1e0cSMichael J. Ruhl jiffies + msecs_to_jiffies(ASPM_TIMER_MS)); 163*bf3b1e0cSMichael J. Ruhl rcd->aspm_ts_timer_sched = now; 164*bf3b1e0cSMichael J. Ruhl } 165*bf3b1e0cSMichael J. Ruhl unlock: 166*bf3b1e0cSMichael J. Ruhl spin_unlock_irqrestore(&rcd->aspm_lock, flags); 167*bf3b1e0cSMichael J. Ruhl } 168*bf3b1e0cSMichael J. Ruhl 169*bf3b1e0cSMichael J. Ruhl /* Timer function for re-enabling ASPM in the absence of interrupt activity */ 170*bf3b1e0cSMichael J. Ruhl static void aspm_ctx_timer_function(struct timer_list *t) 171*bf3b1e0cSMichael J. Ruhl { 172*bf3b1e0cSMichael J. Ruhl struct hfi1_ctxtdata *rcd = from_timer(rcd, t, aspm_timer); 173*bf3b1e0cSMichael J. Ruhl unsigned long flags; 174*bf3b1e0cSMichael J. Ruhl 175*bf3b1e0cSMichael J. Ruhl spin_lock_irqsave(&rcd->aspm_lock, flags); 176*bf3b1e0cSMichael J. Ruhl aspm_enable_dec(rcd->dd); 177*bf3b1e0cSMichael J. Ruhl rcd->aspm_enabled = true; 178*bf3b1e0cSMichael J. Ruhl spin_unlock_irqrestore(&rcd->aspm_lock, flags); 179*bf3b1e0cSMichael J. Ruhl } 180*bf3b1e0cSMichael J. Ruhl 181*bf3b1e0cSMichael J. Ruhl /* 182*bf3b1e0cSMichael J. Ruhl * Disable interrupt processing for verbs contexts when PSM or VNIC contexts 183*bf3b1e0cSMichael J. Ruhl * are open. 184*bf3b1e0cSMichael J. Ruhl */ 185*bf3b1e0cSMichael J. Ruhl void aspm_disable_all(struct hfi1_devdata *dd) 186*bf3b1e0cSMichael J. Ruhl { 187*bf3b1e0cSMichael J. Ruhl struct hfi1_ctxtdata *rcd; 188*bf3b1e0cSMichael J. Ruhl unsigned long flags; 189*bf3b1e0cSMichael J. Ruhl u16 i; 190*bf3b1e0cSMichael J. Ruhl 191*bf3b1e0cSMichael J. Ruhl for (i = 0; i < dd->first_dyn_alloc_ctxt; i++) { 192*bf3b1e0cSMichael J. Ruhl rcd = hfi1_rcd_get_by_index(dd, i); 193*bf3b1e0cSMichael J. Ruhl if (rcd) { 194*bf3b1e0cSMichael J. Ruhl del_timer_sync(&rcd->aspm_timer); 195*bf3b1e0cSMichael J. Ruhl spin_lock_irqsave(&rcd->aspm_lock, flags); 196*bf3b1e0cSMichael J. Ruhl rcd->aspm_intr_enable = false; 197*bf3b1e0cSMichael J. Ruhl spin_unlock_irqrestore(&rcd->aspm_lock, flags); 198*bf3b1e0cSMichael J. Ruhl hfi1_rcd_put(rcd); 199*bf3b1e0cSMichael J. Ruhl } 200*bf3b1e0cSMichael J. Ruhl } 201*bf3b1e0cSMichael J. Ruhl 202*bf3b1e0cSMichael J. Ruhl aspm_disable(dd); 203*bf3b1e0cSMichael J. Ruhl atomic_set(&dd->aspm_disabled_cnt, 0); 204*bf3b1e0cSMichael J. Ruhl } 205*bf3b1e0cSMichael J. Ruhl 206*bf3b1e0cSMichael J. Ruhl /* Re-enable interrupt processing for verbs contexts */ 207*bf3b1e0cSMichael J. Ruhl void aspm_enable_all(struct hfi1_devdata *dd) 208*bf3b1e0cSMichael J. Ruhl { 209*bf3b1e0cSMichael J. Ruhl struct hfi1_ctxtdata *rcd; 210*bf3b1e0cSMichael J. Ruhl unsigned long flags; 211*bf3b1e0cSMichael J. Ruhl u16 i; 212*bf3b1e0cSMichael J. Ruhl 213*bf3b1e0cSMichael J. Ruhl aspm_enable(dd); 214*bf3b1e0cSMichael J. Ruhl 215*bf3b1e0cSMichael J. Ruhl if (aspm_mode != ASPM_MODE_DYNAMIC) 216*bf3b1e0cSMichael J. Ruhl return; 217*bf3b1e0cSMichael J. Ruhl 218*bf3b1e0cSMichael J. Ruhl for (i = 0; i < dd->first_dyn_alloc_ctxt; i++) { 219*bf3b1e0cSMichael J. Ruhl rcd = hfi1_rcd_get_by_index(dd, i); 220*bf3b1e0cSMichael J. Ruhl if (rcd) { 221*bf3b1e0cSMichael J. Ruhl spin_lock_irqsave(&rcd->aspm_lock, flags); 222*bf3b1e0cSMichael J. Ruhl rcd->aspm_intr_enable = true; 223*bf3b1e0cSMichael J. Ruhl rcd->aspm_enabled = true; 224*bf3b1e0cSMichael J. Ruhl spin_unlock_irqrestore(&rcd->aspm_lock, flags); 225*bf3b1e0cSMichael J. Ruhl hfi1_rcd_put(rcd); 226*bf3b1e0cSMichael J. Ruhl } 227*bf3b1e0cSMichael J. Ruhl } 228*bf3b1e0cSMichael J. Ruhl } 229*bf3b1e0cSMichael J. Ruhl 230*bf3b1e0cSMichael J. Ruhl static void aspm_ctx_init(struct hfi1_ctxtdata *rcd) 231*bf3b1e0cSMichael J. Ruhl { 232*bf3b1e0cSMichael J. Ruhl spin_lock_init(&rcd->aspm_lock); 233*bf3b1e0cSMichael J. Ruhl timer_setup(&rcd->aspm_timer, aspm_ctx_timer_function, 0); 234*bf3b1e0cSMichael J. Ruhl rcd->aspm_intr_supported = rcd->dd->aspm_supported && 235*bf3b1e0cSMichael J. Ruhl aspm_mode == ASPM_MODE_DYNAMIC && 236*bf3b1e0cSMichael J. Ruhl rcd->ctxt < rcd->dd->first_dyn_alloc_ctxt; 237*bf3b1e0cSMichael J. Ruhl } 238*bf3b1e0cSMichael J. Ruhl 239*bf3b1e0cSMichael J. Ruhl void aspm_init(struct hfi1_devdata *dd) 240*bf3b1e0cSMichael J. Ruhl { 241*bf3b1e0cSMichael J. Ruhl struct hfi1_ctxtdata *rcd; 242*bf3b1e0cSMichael J. Ruhl u16 i; 243*bf3b1e0cSMichael J. Ruhl 244*bf3b1e0cSMichael J. Ruhl spin_lock_init(&dd->aspm_lock); 245*bf3b1e0cSMichael J. Ruhl dd->aspm_supported = aspm_hw_l1_supported(dd); 246*bf3b1e0cSMichael J. Ruhl 247*bf3b1e0cSMichael J. Ruhl for (i = 0; i < dd->first_dyn_alloc_ctxt; i++) { 248*bf3b1e0cSMichael J. Ruhl rcd = hfi1_rcd_get_by_index(dd, i); 249*bf3b1e0cSMichael J. Ruhl if (rcd) 250*bf3b1e0cSMichael J. Ruhl aspm_ctx_init(rcd); 251*bf3b1e0cSMichael J. Ruhl hfi1_rcd_put(rcd); 252*bf3b1e0cSMichael J. Ruhl } 253*bf3b1e0cSMichael J. Ruhl 254*bf3b1e0cSMichael J. Ruhl /* Start with ASPM disabled */ 255*bf3b1e0cSMichael J. Ruhl aspm_hw_set_l1_ent_latency(dd); 256*bf3b1e0cSMichael J. Ruhl dd->aspm_enabled = false; 257*bf3b1e0cSMichael J. Ruhl aspm_hw_disable_l1(dd); 258*bf3b1e0cSMichael J. Ruhl 259*bf3b1e0cSMichael J. Ruhl /* Now turn on ASPM if configured */ 260*bf3b1e0cSMichael J. Ruhl aspm_enable_all(dd); 261*bf3b1e0cSMichael J. Ruhl } 262*bf3b1e0cSMichael J. Ruhl 263*bf3b1e0cSMichael J. Ruhl void aspm_exit(struct hfi1_devdata *dd) 264*bf3b1e0cSMichael J. Ruhl { 265*bf3b1e0cSMichael J. Ruhl aspm_disable_all(dd); 266*bf3b1e0cSMichael J. Ruhl 267*bf3b1e0cSMichael J. Ruhl /* Turn on ASPM on exit to conserve power */ 268*bf3b1e0cSMichael J. Ruhl aspm_enable(dd); 269*bf3b1e0cSMichael J. Ruhl } 270*bf3b1e0cSMichael J. Ruhl 271