1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * PCI Express Hot Plug Controller Driver 4 * 5 * Copyright (C) 1995,2001 Compaq Computer Corporation 6 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) 7 * Copyright (C) 2001 IBM Corp. 8 * Copyright (C) 2003-2004 Intel Corporation 9 * 10 * All rights reserved. 11 * 12 * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> 13 * 14 */ 15 16#define dev_fmt(fmt) "pciehp: " fmt 17 18#include <linux/kernel.h> 19#include <linux/types.h> 20#include <linux/pm_runtime.h> 21#include <linux/pci.h> 22#include "pciehp.h" 23 24/* The following routines constitute the bulk of the 25 hotplug controller logic 26 */ 27 28#define SAFE_REMOVAL true 29#define SURPRISE_REMOVAL false 30 31static void set_slot_off(struct controller *ctrl) 32{ 33 /* 34 * Turn off slot, turn on attention indicator, turn off power 35 * indicator 36 */ 37 if (POWER_CTRL(ctrl)) { 38 pciehp_power_off_slot(ctrl); 39 40 /* 41 * After turning power off, we must wait for at least 1 second 42 * before taking any action that relies on power having been 43 * removed from the slot/adapter. 44 */ 45 msleep(1000); 46 } 47 48 pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, 49 PCI_EXP_SLTCTL_ATTN_IND_ON); 50} 51 52/** 53 * board_added - Called after a board has been added to the system. 54 * @ctrl: PCIe hotplug controller where board is added 55 * 56 * Turns power on for the board. 57 * Configures board. 58 */ 59static int board_added(struct controller *ctrl) 60{ 61 int retval = 0; 62 struct pci_bus *parent = ctrl->pcie->port->subordinate; 63 64 if (POWER_CTRL(ctrl)) { 65 /* Power on slot */ 66 retval = pciehp_power_on_slot(ctrl); 67 if (retval) 68 return retval; 69 } 70 71 pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, 72 INDICATOR_NOOP); 73 74 /* Check link training status */ 75 retval = pciehp_check_link_status(ctrl); 76 if (retval) 77 goto err_exit; 78 79 /* Check for a power fault */ 80 if (ctrl->power_fault_detected || pciehp_query_power_fault(ctrl)) { 81 ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl)); 82 retval = -EIO; 83 goto err_exit; 84 } 85 86 retval = pciehp_configure_device(ctrl); 87 if (retval) { 88 if (retval != -EEXIST) { 89 ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", 90 pci_domain_nr(parent), parent->number); 91 goto err_exit; 92 } 93 } 94 95 pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, 96 PCI_EXP_SLTCTL_ATTN_IND_OFF); 97 return 0; 98 99err_exit: 100 set_slot_off(ctrl); 101 return retval; 102} 103 104/** 105 * remove_board - Turn off slot and Power Indicator 106 * @ctrl: PCIe hotplug controller where board is being removed 107 * @safe_removal: whether the board is safely removed (versus surprise removed) 108 */ 109static void remove_board(struct controller *ctrl, bool safe_removal) 110{ 111 pciehp_unconfigure_device(ctrl, safe_removal); 112 113 if (POWER_CTRL(ctrl)) { 114 pciehp_power_off_slot(ctrl); 115 116 /* 117 * After turning power off, we must wait for at least 1 second 118 * before taking any action that relies on power having been 119 * removed from the slot/adapter. 120 */ 121 msleep(1000); 122 123 /* Ignore link or presence changes caused by power off */ 124 atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC), 125 &ctrl->pending_events); 126 } 127 128 pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, 129 INDICATOR_NOOP); 130} 131 132static int pciehp_enable_slot(struct controller *ctrl); 133static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal); 134 135void pciehp_request(struct controller *ctrl, int action) 136{ 137 atomic_or(action, &ctrl->pending_events); 138 if (!pciehp_poll_mode) 139 irq_wake_thread(ctrl->pcie->irq, ctrl); 140} 141 142void pciehp_queue_pushbutton_work(struct work_struct *work) 143{ 144 struct controller *ctrl = container_of(work, struct controller, 145 button_work.work); 146 147 mutex_lock(&ctrl->state_lock); 148 switch (ctrl->state) { 149 case BLINKINGOFF_STATE: 150 pciehp_request(ctrl, DISABLE_SLOT); 151 break; 152 case BLINKINGON_STATE: 153 pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); 154 break; 155 default: 156 break; 157 } 158 mutex_unlock(&ctrl->state_lock); 159} 160 161void pciehp_handle_button_press(struct controller *ctrl) 162{ 163 mutex_lock(&ctrl->state_lock); 164 switch (ctrl->state) { 165 case OFF_STATE: 166 case ON_STATE: 167 if (ctrl->state == ON_STATE) { 168 ctrl->state = BLINKINGOFF_STATE; 169 ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n", 170 slot_name(ctrl)); 171 } else { 172 ctrl->state = BLINKINGON_STATE; 173 ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n", 174 slot_name(ctrl)); 175 } 176 /* blink power indicator and turn off attention */ 177 pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, 178 PCI_EXP_SLTCTL_ATTN_IND_OFF); 179 schedule_delayed_work(&ctrl->button_work, 5 * HZ); 180 break; 181 case BLINKINGOFF_STATE: 182 case BLINKINGON_STATE: 183 /* 184 * Cancel if we are still blinking; this means that we 185 * press the attention again before the 5 sec. limit 186 * expires to cancel hot-add or hot-remove 187 */ 188 ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl)); 189 cancel_delayed_work(&ctrl->button_work); 190 if (ctrl->state == BLINKINGOFF_STATE) { 191 ctrl->state = ON_STATE; 192 pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, 193 PCI_EXP_SLTCTL_ATTN_IND_OFF); 194 } else { 195 ctrl->state = OFF_STATE; 196 pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, 197 PCI_EXP_SLTCTL_ATTN_IND_OFF); 198 } 199 ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n", 200 slot_name(ctrl)); 201 break; 202 default: 203 ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", 204 slot_name(ctrl), ctrl->state); 205 break; 206 } 207 mutex_unlock(&ctrl->state_lock); 208} 209 210void pciehp_handle_disable_request(struct controller *ctrl) 211{ 212 mutex_lock(&ctrl->state_lock); 213 switch (ctrl->state) { 214 case BLINKINGON_STATE: 215 case BLINKINGOFF_STATE: 216 cancel_delayed_work(&ctrl->button_work); 217 break; 218 } 219 ctrl->state = POWEROFF_STATE; 220 mutex_unlock(&ctrl->state_lock); 221 222 ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL); 223} 224 225void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) 226{ 227 int present, link_active; 228 229 /* 230 * If the slot is on and presence or link has changed, turn it off. 231 * Even if it's occupied again, we cannot assume the card is the same. 232 */ 233 mutex_lock(&ctrl->state_lock); 234 switch (ctrl->state) { 235 case BLINKINGOFF_STATE: 236 cancel_delayed_work(&ctrl->button_work); 237 fallthrough; 238 case ON_STATE: 239 ctrl->state = POWEROFF_STATE; 240 mutex_unlock(&ctrl->state_lock); 241 if (events & PCI_EXP_SLTSTA_DLLSC) 242 ctrl_info(ctrl, "Slot(%s): Link Down\n", 243 slot_name(ctrl)); 244 if (events & PCI_EXP_SLTSTA_PDC) 245 ctrl_info(ctrl, "Slot(%s): Card not present\n", 246 slot_name(ctrl)); 247 pciehp_disable_slot(ctrl, SURPRISE_REMOVAL); 248 break; 249 default: 250 mutex_unlock(&ctrl->state_lock); 251 break; 252 } 253 254 /* Turn the slot on if it's occupied or link is up */ 255 mutex_lock(&ctrl->state_lock); 256 present = pciehp_card_present(ctrl); 257 link_active = pciehp_check_link_active(ctrl); 258 if (present <= 0 && link_active <= 0) { 259 if (ctrl->state == BLINKINGON_STATE) { 260 ctrl->state = OFF_STATE; 261 cancel_delayed_work(&ctrl->button_work); 262 pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, 263 INDICATOR_NOOP); 264 ctrl_info(ctrl, "Slot(%s): Card not present\n", 265 slot_name(ctrl)); 266 } 267 mutex_unlock(&ctrl->state_lock); 268 return; 269 } 270 271 switch (ctrl->state) { 272 case BLINKINGON_STATE: 273 cancel_delayed_work(&ctrl->button_work); 274 fallthrough; 275 case OFF_STATE: 276 ctrl->state = POWERON_STATE; 277 mutex_unlock(&ctrl->state_lock); 278 if (present) 279 ctrl_info(ctrl, "Slot(%s): Card present\n", 280 slot_name(ctrl)); 281 if (link_active) 282 ctrl_info(ctrl, "Slot(%s): Link Up\n", 283 slot_name(ctrl)); 284 ctrl->request_result = pciehp_enable_slot(ctrl); 285 break; 286 default: 287 mutex_unlock(&ctrl->state_lock); 288 break; 289 } 290} 291 292static int __pciehp_enable_slot(struct controller *ctrl) 293{ 294 u8 getstatus = 0; 295 296 if (MRL_SENS(ctrl)) { 297 pciehp_get_latch_status(ctrl, &getstatus); 298 if (getstatus) { 299 ctrl_info(ctrl, "Slot(%s): Latch open\n", 300 slot_name(ctrl)); 301 return -ENODEV; 302 } 303 } 304 305 if (POWER_CTRL(ctrl)) { 306 pciehp_get_power_status(ctrl, &getstatus); 307 if (getstatus) { 308 ctrl_info(ctrl, "Slot(%s): Already enabled\n", 309 slot_name(ctrl)); 310 return 0; 311 } 312 } 313 314 return board_added(ctrl); 315} 316 317static int pciehp_enable_slot(struct controller *ctrl) 318{ 319 int ret; 320 321 pm_runtime_get_sync(&ctrl->pcie->port->dev); 322 ret = __pciehp_enable_slot(ctrl); 323 if (ret && ATTN_BUTTN(ctrl)) 324 /* may be blinking */ 325 pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, 326 INDICATOR_NOOP); 327 pm_runtime_put(&ctrl->pcie->port->dev); 328 329 mutex_lock(&ctrl->state_lock); 330 ctrl->state = ret ? OFF_STATE : ON_STATE; 331 mutex_unlock(&ctrl->state_lock); 332 333 return ret; 334} 335 336static int __pciehp_disable_slot(struct controller *ctrl, bool safe_removal) 337{ 338 u8 getstatus = 0; 339 340 if (POWER_CTRL(ctrl)) { 341 pciehp_get_power_status(ctrl, &getstatus); 342 if (!getstatus) { 343 ctrl_info(ctrl, "Slot(%s): Already disabled\n", 344 slot_name(ctrl)); 345 return -EINVAL; 346 } 347 } 348 349 remove_board(ctrl, safe_removal); 350 return 0; 351} 352 353static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal) 354{ 355 int ret; 356 357 pm_runtime_get_sync(&ctrl->pcie->port->dev); 358 ret = __pciehp_disable_slot(ctrl, safe_removal); 359 pm_runtime_put(&ctrl->pcie->port->dev); 360 361 mutex_lock(&ctrl->state_lock); 362 ctrl->state = OFF_STATE; 363 mutex_unlock(&ctrl->state_lock); 364 365 return ret; 366} 367 368int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) 369{ 370 struct controller *ctrl = to_ctrl(hotplug_slot); 371 372 mutex_lock(&ctrl->state_lock); 373 switch (ctrl->state) { 374 case BLINKINGON_STATE: 375 case OFF_STATE: 376 mutex_unlock(&ctrl->state_lock); 377 /* 378 * The IRQ thread becomes a no-op if the user pulls out the 379 * card before the thread wakes up, so initialize to -ENODEV. 380 */ 381 ctrl->request_result = -ENODEV; 382 pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); 383 wait_event(ctrl->requester, 384 !atomic_read(&ctrl->pending_events) && 385 !ctrl->ist_running); 386 return ctrl->request_result; 387 case POWERON_STATE: 388 ctrl_info(ctrl, "Slot(%s): Already in powering on state\n", 389 slot_name(ctrl)); 390 break; 391 case BLINKINGOFF_STATE: 392 case ON_STATE: 393 case POWEROFF_STATE: 394 ctrl_info(ctrl, "Slot(%s): Already enabled\n", 395 slot_name(ctrl)); 396 break; 397 default: 398 ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", 399 slot_name(ctrl), ctrl->state); 400 break; 401 } 402 mutex_unlock(&ctrl->state_lock); 403 404 return -ENODEV; 405} 406 407int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) 408{ 409 struct controller *ctrl = to_ctrl(hotplug_slot); 410 411 mutex_lock(&ctrl->state_lock); 412 switch (ctrl->state) { 413 case BLINKINGOFF_STATE: 414 case ON_STATE: 415 mutex_unlock(&ctrl->state_lock); 416 pciehp_request(ctrl, DISABLE_SLOT); 417 wait_event(ctrl->requester, 418 !atomic_read(&ctrl->pending_events) && 419 !ctrl->ist_running); 420 return ctrl->request_result; 421 case POWEROFF_STATE: 422 ctrl_info(ctrl, "Slot(%s): Already in powering off state\n", 423 slot_name(ctrl)); 424 break; 425 case BLINKINGON_STATE: 426 case OFF_STATE: 427 case POWERON_STATE: 428 ctrl_info(ctrl, "Slot(%s): Already disabled\n", 429 slot_name(ctrl)); 430 break; 431 default: 432 ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", 433 slot_name(ctrl), ctrl->state); 434 break; 435 } 436 mutex_unlock(&ctrl->state_lock); 437 438 return -ENODEV; 439} 440