1 /* 2 * PCI Express Hot Plug Controller Driver 3 * 4 * Copyright (C) 1995,2001 Compaq Computer Corporation 5 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) 6 * Copyright (C) 2001 IBM Corp. 7 * Copyright (C) 2003-2004 Intel Corporation 8 * 9 * All rights reserved. 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or (at 14 * your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, but 17 * WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 19 * NON INFRINGEMENT. See the GNU General Public License for more 20 * details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program; if not, write to the Free Software 24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 25 * 26 * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> 27 * 28 */ 29 30 #include <linux/module.h> 31 #include <linux/kernel.h> 32 #include <linux/types.h> 33 #include <linux/slab.h> 34 #include <linux/pm_runtime.h> 35 #include <linux/pci.h> 36 #include "../pci.h" 37 #include "pciehp.h" 38 39 static void interrupt_event_handler(struct work_struct *work); 40 41 void pciehp_queue_interrupt_event(struct slot *p_slot, u32 event_type) 42 { 43 struct event_info *info; 44 45 info = kmalloc(sizeof(*info), GFP_ATOMIC); 46 if (!info) { 47 ctrl_err(p_slot->ctrl, "dropped event %d (ENOMEM)\n", event_type); 48 return; 49 } 50 51 INIT_WORK(&info->work, interrupt_event_handler); 52 info->event_type = event_type; 53 info->p_slot = p_slot; 54 queue_work(p_slot->wq, &info->work); 55 } 56 57 /* The following routines constitute the bulk of the 58 hotplug controller logic 59 */ 60 61 static void set_slot_off(struct controller *ctrl, struct slot *pslot) 62 { 63 /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ 64 if (POWER_CTRL(ctrl)) { 65 pciehp_power_off_slot(pslot); 66 67 /* 68 * After turning power off, we must wait for at least 1 second 69 * before taking any action that relies on power having been 70 * removed from the slot/adapter. 71 */ 72 msleep(1000); 73 } 74 75 pciehp_green_led_off(pslot); 76 pciehp_set_attention_status(pslot, 1); 77 } 78 79 /** 80 * board_added - Called after a board has been added to the system. 81 * @p_slot: &slot where board is added 82 * 83 * Turns power on for the board. 84 * Configures board. 85 */ 86 static int board_added(struct slot *p_slot) 87 { 88 int retval = 0; 89 struct controller *ctrl = p_slot->ctrl; 90 struct pci_bus *parent = ctrl->pcie->port->subordinate; 91 92 if (POWER_CTRL(ctrl)) { 93 /* Power on slot */ 94 retval = pciehp_power_on_slot(p_slot); 95 if (retval) 96 return retval; 97 } 98 99 pciehp_green_led_blink(p_slot); 100 101 /* Check link training status */ 102 pm_runtime_get_sync(&ctrl->pcie->port->dev); 103 retval = pciehp_check_link_status(ctrl); 104 if (retval) { 105 ctrl_err(ctrl, "Failed to check link status\n"); 106 goto err_exit; 107 } 108 109 /* Check for a power fault */ 110 if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) { 111 ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(p_slot)); 112 retval = -EIO; 113 goto err_exit; 114 } 115 116 retval = pciehp_configure_device(p_slot); 117 if (retval) { 118 ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", 119 pci_domain_nr(parent), parent->number); 120 if (retval != -EEXIST) 121 goto err_exit; 122 } 123 pm_runtime_put(&ctrl->pcie->port->dev); 124 125 pciehp_green_led_on(p_slot); 126 pciehp_set_attention_status(p_slot, 0); 127 return 0; 128 129 err_exit: 130 pm_runtime_put(&ctrl->pcie->port->dev); 131 set_slot_off(ctrl, p_slot); 132 return retval; 133 } 134 135 /** 136 * remove_board - Turns off slot and LEDs 137 * @p_slot: slot where board is being removed 138 */ 139 static int remove_board(struct slot *p_slot) 140 { 141 int retval; 142 struct controller *ctrl = p_slot->ctrl; 143 144 pm_runtime_get_sync(&ctrl->pcie->port->dev); 145 retval = pciehp_unconfigure_device(p_slot); 146 pm_runtime_put(&ctrl->pcie->port->dev); 147 if (retval) 148 return retval; 149 150 if (POWER_CTRL(ctrl)) { 151 pciehp_power_off_slot(p_slot); 152 153 /* 154 * After turning power off, we must wait for at least 1 second 155 * before taking any action that relies on power having been 156 * removed from the slot/adapter. 157 */ 158 msleep(1000); 159 } 160 161 /* turn off Green LED */ 162 pciehp_green_led_off(p_slot); 163 return 0; 164 } 165 166 struct power_work_info { 167 struct slot *p_slot; 168 struct work_struct work; 169 unsigned int req; 170 #define DISABLE_REQ 0 171 #define ENABLE_REQ 1 172 }; 173 174 /** 175 * pciehp_power_thread - handle pushbutton events 176 * @work: &struct work_struct describing work to be done 177 * 178 * Scheduled procedure to handle blocking stuff for the pushbuttons. 179 * Handles all pending events and exits. 180 */ 181 static void pciehp_power_thread(struct work_struct *work) 182 { 183 struct power_work_info *info = 184 container_of(work, struct power_work_info, work); 185 struct slot *p_slot = info->p_slot; 186 int ret; 187 188 switch (info->req) { 189 case DISABLE_REQ: 190 mutex_lock(&p_slot->hotplug_lock); 191 pciehp_disable_slot(p_slot); 192 mutex_unlock(&p_slot->hotplug_lock); 193 mutex_lock(&p_slot->lock); 194 p_slot->state = STATIC_STATE; 195 mutex_unlock(&p_slot->lock); 196 break; 197 case ENABLE_REQ: 198 mutex_lock(&p_slot->hotplug_lock); 199 ret = pciehp_enable_slot(p_slot); 200 mutex_unlock(&p_slot->hotplug_lock); 201 if (ret) 202 pciehp_green_led_off(p_slot); 203 mutex_lock(&p_slot->lock); 204 p_slot->state = STATIC_STATE; 205 mutex_unlock(&p_slot->lock); 206 break; 207 default: 208 break; 209 } 210 211 kfree(info); 212 } 213 214 static void pciehp_queue_power_work(struct slot *p_slot, int req) 215 { 216 struct power_work_info *info; 217 218 p_slot->state = (req == ENABLE_REQ) ? POWERON_STATE : POWEROFF_STATE; 219 220 info = kmalloc(sizeof(*info), GFP_KERNEL); 221 if (!info) { 222 ctrl_err(p_slot->ctrl, "no memory to queue %s request\n", 223 (req == ENABLE_REQ) ? "poweron" : "poweroff"); 224 return; 225 } 226 info->p_slot = p_slot; 227 INIT_WORK(&info->work, pciehp_power_thread); 228 info->req = req; 229 queue_work(p_slot->wq, &info->work); 230 } 231 232 void pciehp_queue_pushbutton_work(struct work_struct *work) 233 { 234 struct slot *p_slot = container_of(work, struct slot, work.work); 235 236 mutex_lock(&p_slot->lock); 237 switch (p_slot->state) { 238 case BLINKINGOFF_STATE: 239 pciehp_queue_power_work(p_slot, DISABLE_REQ); 240 break; 241 case BLINKINGON_STATE: 242 pciehp_queue_power_work(p_slot, ENABLE_REQ); 243 break; 244 default: 245 break; 246 } 247 mutex_unlock(&p_slot->lock); 248 } 249 250 /* 251 * Note: This function must be called with slot->lock held 252 */ 253 static void handle_button_press_event(struct slot *p_slot) 254 { 255 struct controller *ctrl = p_slot->ctrl; 256 u8 getstatus; 257 258 switch (p_slot->state) { 259 case STATIC_STATE: 260 pciehp_get_power_status(p_slot, &getstatus); 261 if (getstatus) { 262 p_slot->state = BLINKINGOFF_STATE; 263 ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n", 264 slot_name(p_slot)); 265 } else { 266 p_slot->state = BLINKINGON_STATE; 267 ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n", 268 slot_name(p_slot)); 269 } 270 /* blink green LED and turn off amber */ 271 pciehp_green_led_blink(p_slot); 272 pciehp_set_attention_status(p_slot, 0); 273 queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); 274 break; 275 case BLINKINGOFF_STATE: 276 case BLINKINGON_STATE: 277 /* 278 * Cancel if we are still blinking; this means that we 279 * press the attention again before the 5 sec. limit 280 * expires to cancel hot-add or hot-remove 281 */ 282 ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot)); 283 cancel_delayed_work(&p_slot->work); 284 if (p_slot->state == BLINKINGOFF_STATE) 285 pciehp_green_led_on(p_slot); 286 else 287 pciehp_green_led_off(p_slot); 288 pciehp_set_attention_status(p_slot, 0); 289 ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n", 290 slot_name(p_slot)); 291 p_slot->state = STATIC_STATE; 292 break; 293 case POWEROFF_STATE: 294 case POWERON_STATE: 295 /* 296 * Ignore if the slot is on power-on or power-off state; 297 * this means that the previous attention button action 298 * to hot-add or hot-remove is undergoing 299 */ 300 ctrl_info(ctrl, "Slot(%s): Button ignored\n", 301 slot_name(p_slot)); 302 break; 303 default: 304 ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", 305 slot_name(p_slot), p_slot->state); 306 break; 307 } 308 } 309 310 /* 311 * Note: This function must be called with slot->lock held 312 */ 313 static void handle_link_event(struct slot *p_slot, u32 event) 314 { 315 struct controller *ctrl = p_slot->ctrl; 316 317 switch (p_slot->state) { 318 case BLINKINGON_STATE: 319 case BLINKINGOFF_STATE: 320 cancel_delayed_work(&p_slot->work); 321 /* Fall through */ 322 case STATIC_STATE: 323 pciehp_queue_power_work(p_slot, event == INT_LINK_UP ? 324 ENABLE_REQ : DISABLE_REQ); 325 break; 326 case POWERON_STATE: 327 if (event == INT_LINK_UP) { 328 ctrl_info(ctrl, "Slot(%s): Link Up event ignored; already powering on\n", 329 slot_name(p_slot)); 330 } else { 331 ctrl_info(ctrl, "Slot(%s): Link Down event queued; currently getting powered on\n", 332 slot_name(p_slot)); 333 pciehp_queue_power_work(p_slot, DISABLE_REQ); 334 } 335 break; 336 case POWEROFF_STATE: 337 if (event == INT_LINK_UP) { 338 ctrl_info(ctrl, "Slot(%s): Link Up event queued; currently getting powered off\n", 339 slot_name(p_slot)); 340 pciehp_queue_power_work(p_slot, ENABLE_REQ); 341 } else { 342 ctrl_info(ctrl, "Slot(%s): Link Down event ignored; already powering off\n", 343 slot_name(p_slot)); 344 } 345 break; 346 default: 347 ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", 348 slot_name(p_slot), p_slot->state); 349 break; 350 } 351 } 352 353 static void interrupt_event_handler(struct work_struct *work) 354 { 355 struct event_info *info = container_of(work, struct event_info, work); 356 struct slot *p_slot = info->p_slot; 357 struct controller *ctrl = p_slot->ctrl; 358 359 mutex_lock(&p_slot->lock); 360 switch (info->event_type) { 361 case INT_BUTTON_PRESS: 362 handle_button_press_event(p_slot); 363 break; 364 case INT_POWER_FAULT: 365 if (!POWER_CTRL(ctrl)) 366 break; 367 pciehp_set_attention_status(p_slot, 1); 368 pciehp_green_led_off(p_slot); 369 break; 370 case INT_PRESENCE_ON: 371 pciehp_queue_power_work(p_slot, ENABLE_REQ); 372 break; 373 case INT_PRESENCE_OFF: 374 /* 375 * Regardless of surprise capability, we need to 376 * definitely remove a card that has been pulled out! 377 */ 378 pciehp_queue_power_work(p_slot, DISABLE_REQ); 379 break; 380 case INT_LINK_UP: 381 case INT_LINK_DOWN: 382 handle_link_event(p_slot, info->event_type); 383 break; 384 default: 385 break; 386 } 387 mutex_unlock(&p_slot->lock); 388 389 kfree(info); 390 } 391 392 /* 393 * Note: This function must be called with slot->hotplug_lock held 394 */ 395 int pciehp_enable_slot(struct slot *p_slot) 396 { 397 u8 getstatus = 0; 398 struct controller *ctrl = p_slot->ctrl; 399 400 pciehp_get_adapter_status(p_slot, &getstatus); 401 if (!getstatus) { 402 ctrl_info(ctrl, "Slot(%s): No adapter\n", slot_name(p_slot)); 403 return -ENODEV; 404 } 405 if (MRL_SENS(p_slot->ctrl)) { 406 pciehp_get_latch_status(p_slot, &getstatus); 407 if (getstatus) { 408 ctrl_info(ctrl, "Slot(%s): Latch open\n", 409 slot_name(p_slot)); 410 return -ENODEV; 411 } 412 } 413 414 if (POWER_CTRL(p_slot->ctrl)) { 415 pciehp_get_power_status(p_slot, &getstatus); 416 if (getstatus) { 417 ctrl_info(ctrl, "Slot(%s): Already enabled\n", 418 slot_name(p_slot)); 419 return 0; 420 } 421 } 422 423 return board_added(p_slot); 424 } 425 426 /* 427 * Note: This function must be called with slot->hotplug_lock held 428 */ 429 int pciehp_disable_slot(struct slot *p_slot) 430 { 431 u8 getstatus = 0; 432 struct controller *ctrl = p_slot->ctrl; 433 434 if (!p_slot->ctrl) 435 return 1; 436 437 if (POWER_CTRL(p_slot->ctrl)) { 438 pciehp_get_power_status(p_slot, &getstatus); 439 if (!getstatus) { 440 ctrl_info(ctrl, "Slot(%s): Already disabled\n", 441 slot_name(p_slot)); 442 return -EINVAL; 443 } 444 } 445 446 return remove_board(p_slot); 447 } 448 449 int pciehp_sysfs_enable_slot(struct slot *p_slot) 450 { 451 int retval = -ENODEV; 452 struct controller *ctrl = p_slot->ctrl; 453 454 mutex_lock(&p_slot->lock); 455 switch (p_slot->state) { 456 case BLINKINGON_STATE: 457 cancel_delayed_work(&p_slot->work); 458 case STATIC_STATE: 459 p_slot->state = POWERON_STATE; 460 mutex_unlock(&p_slot->lock); 461 mutex_lock(&p_slot->hotplug_lock); 462 retval = pciehp_enable_slot(p_slot); 463 mutex_unlock(&p_slot->hotplug_lock); 464 mutex_lock(&p_slot->lock); 465 p_slot->state = STATIC_STATE; 466 break; 467 case POWERON_STATE: 468 ctrl_info(ctrl, "Slot(%s): Already in powering on state\n", 469 slot_name(p_slot)); 470 break; 471 case BLINKINGOFF_STATE: 472 case POWEROFF_STATE: 473 ctrl_info(ctrl, "Slot(%s): Already enabled\n", 474 slot_name(p_slot)); 475 break; 476 default: 477 ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", 478 slot_name(p_slot), p_slot->state); 479 break; 480 } 481 mutex_unlock(&p_slot->lock); 482 483 return retval; 484 } 485 486 int pciehp_sysfs_disable_slot(struct slot *p_slot) 487 { 488 int retval = -ENODEV; 489 struct controller *ctrl = p_slot->ctrl; 490 491 mutex_lock(&p_slot->lock); 492 switch (p_slot->state) { 493 case BLINKINGOFF_STATE: 494 cancel_delayed_work(&p_slot->work); 495 case STATIC_STATE: 496 p_slot->state = POWEROFF_STATE; 497 mutex_unlock(&p_slot->lock); 498 mutex_lock(&p_slot->hotplug_lock); 499 retval = pciehp_disable_slot(p_slot); 500 mutex_unlock(&p_slot->hotplug_lock); 501 mutex_lock(&p_slot->lock); 502 p_slot->state = STATIC_STATE; 503 break; 504 case POWEROFF_STATE: 505 ctrl_info(ctrl, "Slot(%s): Already in powering off state\n", 506 slot_name(p_slot)); 507 break; 508 case BLINKINGON_STATE: 509 case POWERON_STATE: 510 ctrl_info(ctrl, "Slot(%s): Already disabled\n", 511 slot_name(p_slot)); 512 break; 513 default: 514 ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", 515 slot_name(p_slot), p_slot->state); 516 break; 517 } 518 mutex_unlock(&p_slot->lock); 519 520 return retval; 521 } 522