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