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