root/drivers/pci/hotplug/pciehp_ctrl.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_slot_off
  2. board_added
  3. remove_board
  4. pciehp_request
  5. pciehp_queue_pushbutton_work
  6. pciehp_handle_button_press
  7. pciehp_handle_disable_request
  8. pciehp_handle_presence_or_link_change
  9. __pciehp_enable_slot
  10. pciehp_enable_slot
  11. __pciehp_disable_slot
  12. pciehp_disable_slot
  13. pciehp_sysfs_enable_slot
  14. pciehp_sysfs_disable_slot

   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 
  31 static 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  */
  59 static 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                 ctrl_err(ctrl, "Failed to check link status\n");
  78                 goto err_exit;
  79         }
  80 
  81         /* Check for a power fault */
  82         if (ctrl->power_fault_detected || pciehp_query_power_fault(ctrl)) {
  83                 ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
  84                 retval = -EIO;
  85                 goto err_exit;
  86         }
  87 
  88         retval = pciehp_configure_device(ctrl);
  89         if (retval) {
  90                 if (retval != -EEXIST) {
  91                         ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
  92                                  pci_domain_nr(parent), parent->number);
  93                         goto err_exit;
  94                 }
  95         }
  96 
  97         pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
  98                               PCI_EXP_SLTCTL_ATTN_IND_OFF);
  99         return 0;
 100 
 101 err_exit:
 102         set_slot_off(ctrl);
 103         return retval;
 104 }
 105 
 106 /**
 107  * remove_board - Turn off slot and Power Indicator
 108  * @ctrl: PCIe hotplug controller where board is being removed
 109  * @safe_removal: whether the board is safely removed (versus surprise removed)
 110  */
 111 static void remove_board(struct controller *ctrl, bool safe_removal)
 112 {
 113         pciehp_unconfigure_device(ctrl, safe_removal);
 114 
 115         if (POWER_CTRL(ctrl)) {
 116                 pciehp_power_off_slot(ctrl);
 117 
 118                 /*
 119                  * After turning power off, we must wait for at least 1 second
 120                  * before taking any action that relies on power having been
 121                  * removed from the slot/adapter.
 122                  */
 123                 msleep(1000);
 124 
 125                 /* Ignore link or presence changes caused by power off */
 126                 atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC),
 127                            &ctrl->pending_events);
 128         }
 129 
 130         pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
 131                               INDICATOR_NOOP);
 132 }
 133 
 134 static int pciehp_enable_slot(struct controller *ctrl);
 135 static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal);
 136 
 137 void pciehp_request(struct controller *ctrl, int action)
 138 {
 139         atomic_or(action, &ctrl->pending_events);
 140         if (!pciehp_poll_mode)
 141                 irq_wake_thread(ctrl->pcie->irq, ctrl);
 142 }
 143 
 144 void pciehp_queue_pushbutton_work(struct work_struct *work)
 145 {
 146         struct controller *ctrl = container_of(work, struct controller,
 147                                                button_work.work);
 148 
 149         mutex_lock(&ctrl->state_lock);
 150         switch (ctrl->state) {
 151         case BLINKINGOFF_STATE:
 152                 pciehp_request(ctrl, DISABLE_SLOT);
 153                 break;
 154         case BLINKINGON_STATE:
 155                 pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
 156                 break;
 157         default:
 158                 break;
 159         }
 160         mutex_unlock(&ctrl->state_lock);
 161 }
 162 
 163 void pciehp_handle_button_press(struct controller *ctrl)
 164 {
 165         mutex_lock(&ctrl->state_lock);
 166         switch (ctrl->state) {
 167         case OFF_STATE:
 168         case ON_STATE:
 169                 if (ctrl->state == ON_STATE) {
 170                         ctrl->state = BLINKINGOFF_STATE;
 171                         ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
 172                                   slot_name(ctrl));
 173                 } else {
 174                         ctrl->state = BLINKINGON_STATE;
 175                         ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
 176                                   slot_name(ctrl));
 177                 }
 178                 /* blink power indicator and turn off attention */
 179                 pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
 180                                       PCI_EXP_SLTCTL_ATTN_IND_OFF);
 181                 schedule_delayed_work(&ctrl->button_work, 5 * HZ);
 182                 break;
 183         case BLINKINGOFF_STATE:
 184         case BLINKINGON_STATE:
 185                 /*
 186                  * Cancel if we are still blinking; this means that we
 187                  * press the attention again before the 5 sec. limit
 188                  * expires to cancel hot-add or hot-remove
 189                  */
 190                 ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl));
 191                 cancel_delayed_work(&ctrl->button_work);
 192                 if (ctrl->state == BLINKINGOFF_STATE) {
 193                         ctrl->state = ON_STATE;
 194                         pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
 195                                               PCI_EXP_SLTCTL_ATTN_IND_OFF);
 196                 } else {
 197                         ctrl->state = OFF_STATE;
 198                         pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
 199                                               PCI_EXP_SLTCTL_ATTN_IND_OFF);
 200                 }
 201                 ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
 202                           slot_name(ctrl));
 203                 break;
 204         default:
 205                 ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
 206                          slot_name(ctrl), ctrl->state);
 207                 break;
 208         }
 209         mutex_unlock(&ctrl->state_lock);
 210 }
 211 
 212 void pciehp_handle_disable_request(struct controller *ctrl)
 213 {
 214         mutex_lock(&ctrl->state_lock);
 215         switch (ctrl->state) {
 216         case BLINKINGON_STATE:
 217         case BLINKINGOFF_STATE:
 218                 cancel_delayed_work(&ctrl->button_work);
 219                 break;
 220         }
 221         ctrl->state = POWEROFF_STATE;
 222         mutex_unlock(&ctrl->state_lock);
 223 
 224         ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL);
 225 }
 226 
 227 void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
 228 {
 229         int present, link_active;
 230 
 231         /*
 232          * If the slot is on and presence or link has changed, turn it off.
 233          * Even if it's occupied again, we cannot assume the card is the same.
 234          */
 235         mutex_lock(&ctrl->state_lock);
 236         switch (ctrl->state) {
 237         case BLINKINGOFF_STATE:
 238                 cancel_delayed_work(&ctrl->button_work);
 239                 /* fall through */
 240         case ON_STATE:
 241                 ctrl->state = POWEROFF_STATE;
 242                 mutex_unlock(&ctrl->state_lock);
 243                 if (events & PCI_EXP_SLTSTA_DLLSC)
 244                         ctrl_info(ctrl, "Slot(%s): Link Down\n",
 245                                   slot_name(ctrl));
 246                 if (events & PCI_EXP_SLTSTA_PDC)
 247                         ctrl_info(ctrl, "Slot(%s): Card not present\n",
 248                                   slot_name(ctrl));
 249                 pciehp_disable_slot(ctrl, SURPRISE_REMOVAL);
 250                 break;
 251         default:
 252                 mutex_unlock(&ctrl->state_lock);
 253                 break;
 254         }
 255 
 256         /* Turn the slot on if it's occupied or link is up */
 257         mutex_lock(&ctrl->state_lock);
 258         present = pciehp_card_present(ctrl);
 259         link_active = pciehp_check_link_active(ctrl);
 260         if (present <= 0 && link_active <= 0) {
 261                 mutex_unlock(&ctrl->state_lock);
 262                 return;
 263         }
 264 
 265         switch (ctrl->state) {
 266         case BLINKINGON_STATE:
 267                 cancel_delayed_work(&ctrl->button_work);
 268                 /* fall through */
 269         case OFF_STATE:
 270                 ctrl->state = POWERON_STATE;
 271                 mutex_unlock(&ctrl->state_lock);
 272                 if (present)
 273                         ctrl_info(ctrl, "Slot(%s): Card present\n",
 274                                   slot_name(ctrl));
 275                 if (link_active)
 276                         ctrl_info(ctrl, "Slot(%s): Link Up\n",
 277                                   slot_name(ctrl));
 278                 ctrl->request_result = pciehp_enable_slot(ctrl);
 279                 break;
 280         default:
 281                 mutex_unlock(&ctrl->state_lock);
 282                 break;
 283         }
 284 }
 285 
 286 static int __pciehp_enable_slot(struct controller *ctrl)
 287 {
 288         u8 getstatus = 0;
 289 
 290         if (MRL_SENS(ctrl)) {
 291                 pciehp_get_latch_status(ctrl, &getstatus);
 292                 if (getstatus) {
 293                         ctrl_info(ctrl, "Slot(%s): Latch open\n",
 294                                   slot_name(ctrl));
 295                         return -ENODEV;
 296                 }
 297         }
 298 
 299         if (POWER_CTRL(ctrl)) {
 300                 pciehp_get_power_status(ctrl, &getstatus);
 301                 if (getstatus) {
 302                         ctrl_info(ctrl, "Slot(%s): Already enabled\n",
 303                                   slot_name(ctrl));
 304                         return 0;
 305                 }
 306         }
 307 
 308         return board_added(ctrl);
 309 }
 310 
 311 static int pciehp_enable_slot(struct controller *ctrl)
 312 {
 313         int ret;
 314 
 315         pm_runtime_get_sync(&ctrl->pcie->port->dev);
 316         ret = __pciehp_enable_slot(ctrl);
 317         if (ret && ATTN_BUTTN(ctrl))
 318                 /* may be blinking */
 319                 pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
 320                                       INDICATOR_NOOP);
 321         pm_runtime_put(&ctrl->pcie->port->dev);
 322 
 323         mutex_lock(&ctrl->state_lock);
 324         ctrl->state = ret ? OFF_STATE : ON_STATE;
 325         mutex_unlock(&ctrl->state_lock);
 326 
 327         return ret;
 328 }
 329 
 330 static int __pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
 331 {
 332         u8 getstatus = 0;
 333 
 334         if (POWER_CTRL(ctrl)) {
 335                 pciehp_get_power_status(ctrl, &getstatus);
 336                 if (!getstatus) {
 337                         ctrl_info(ctrl, "Slot(%s): Already disabled\n",
 338                                   slot_name(ctrl));
 339                         return -EINVAL;
 340                 }
 341         }
 342 
 343         remove_board(ctrl, safe_removal);
 344         return 0;
 345 }
 346 
 347 static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
 348 {
 349         int ret;
 350 
 351         pm_runtime_get_sync(&ctrl->pcie->port->dev);
 352         ret = __pciehp_disable_slot(ctrl, safe_removal);
 353         pm_runtime_put(&ctrl->pcie->port->dev);
 354 
 355         mutex_lock(&ctrl->state_lock);
 356         ctrl->state = OFF_STATE;
 357         mutex_unlock(&ctrl->state_lock);
 358 
 359         return ret;
 360 }
 361 
 362 int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot)
 363 {
 364         struct controller *ctrl = to_ctrl(hotplug_slot);
 365 
 366         mutex_lock(&ctrl->state_lock);
 367         switch (ctrl->state) {
 368         case BLINKINGON_STATE:
 369         case OFF_STATE:
 370                 mutex_unlock(&ctrl->state_lock);
 371                 /*
 372                  * The IRQ thread becomes a no-op if the user pulls out the
 373                  * card before the thread wakes up, so initialize to -ENODEV.
 374                  */
 375                 ctrl->request_result = -ENODEV;
 376                 pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
 377                 wait_event(ctrl->requester,
 378                            !atomic_read(&ctrl->pending_events) &&
 379                            !ctrl->ist_running);
 380                 return ctrl->request_result;
 381         case POWERON_STATE:
 382                 ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
 383                           slot_name(ctrl));
 384                 break;
 385         case BLINKINGOFF_STATE:
 386         case ON_STATE:
 387         case POWEROFF_STATE:
 388                 ctrl_info(ctrl, "Slot(%s): Already enabled\n",
 389                           slot_name(ctrl));
 390                 break;
 391         default:
 392                 ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
 393                          slot_name(ctrl), ctrl->state);
 394                 break;
 395         }
 396         mutex_unlock(&ctrl->state_lock);
 397 
 398         return -ENODEV;
 399 }
 400 
 401 int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot)
 402 {
 403         struct controller *ctrl = to_ctrl(hotplug_slot);
 404 
 405         mutex_lock(&ctrl->state_lock);
 406         switch (ctrl->state) {
 407         case BLINKINGOFF_STATE:
 408         case ON_STATE:
 409                 mutex_unlock(&ctrl->state_lock);
 410                 pciehp_request(ctrl, DISABLE_SLOT);
 411                 wait_event(ctrl->requester,
 412                            !atomic_read(&ctrl->pending_events) &&
 413                            !ctrl->ist_running);
 414                 return ctrl->request_result;
 415         case POWEROFF_STATE:
 416                 ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
 417                           slot_name(ctrl));
 418                 break;
 419         case BLINKINGON_STATE:
 420         case OFF_STATE:
 421         case POWERON_STATE:
 422                 ctrl_info(ctrl, "Slot(%s): Already disabled\n",
 423                           slot_name(ctrl));
 424                 break;
 425         default:
 426                 ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
 427                          slot_name(ctrl), ctrl->state);
 428                 break;
 429         }
 430         mutex_unlock(&ctrl->state_lock);
 431 
 432         return -ENODEV;
 433 }

/* [<][>][^][v][top][bottom][index][help] */