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