linux/drivers/pci/hotplug/pciehp_ctrl.c
<<
>>
Prefs
   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/pci.h>
  35#include "../pci.h"
  36#include "pciehp.h"
  37
  38static void interrupt_event_handler(struct work_struct *work);
  39
  40static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
  41{
  42        struct event_info *info;
  43
  44        info = kmalloc(sizeof(*info), GFP_ATOMIC);
  45        if (!info)
  46                return -ENOMEM;
  47
  48        info->event_type = event_type;
  49        info->p_slot = p_slot;
  50        INIT_WORK(&info->work, interrupt_event_handler);
  51
  52        queue_work(p_slot->wq, &info->work);
  53
  54        return 0;
  55}
  56
  57u8 pciehp_handle_attention_button(struct slot *p_slot)
  58{
  59        u32 event_type;
  60        struct controller *ctrl = p_slot->ctrl;
  61
  62        /* Attention Button Change */
  63        ctrl_dbg(ctrl, "Attention button interrupt received\n");
  64
  65        /*
  66         *  Button pressed - See if need to TAKE ACTION!!!
  67         */
  68        ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot));
  69        event_type = INT_BUTTON_PRESS;
  70
  71        queue_interrupt_event(p_slot, event_type);
  72
  73        return 0;
  74}
  75
  76u8 pciehp_handle_switch_change(struct slot *p_slot)
  77{
  78        u8 getstatus;
  79        u32 event_type;
  80        struct controller *ctrl = p_slot->ctrl;
  81
  82        /* Switch Change */
  83        ctrl_dbg(ctrl, "Switch interrupt received\n");
  84
  85        pciehp_get_latch_status(p_slot, &getstatus);
  86        if (getstatus) {
  87                /*
  88                 * Switch opened
  89                 */
  90                ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot));
  91                event_type = INT_SWITCH_OPEN;
  92        } else {
  93                /*
  94                 *  Switch closed
  95                 */
  96                ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot));
  97                event_type = INT_SWITCH_CLOSE;
  98        }
  99
 100        queue_interrupt_event(p_slot, event_type);
 101
 102        return 1;
 103}
 104
 105u8 pciehp_handle_presence_change(struct slot *p_slot)
 106{
 107        u32 event_type;
 108        u8 presence_save;
 109        struct controller *ctrl = p_slot->ctrl;
 110
 111        /* Presence Change */
 112        ctrl_dbg(ctrl, "Presence/Notify input change\n");
 113
 114        /* Switch is open, assume a presence change
 115         * Save the presence state
 116         */
 117        pciehp_get_adapter_status(p_slot, &presence_save);
 118        if (presence_save) {
 119                /*
 120                 * Card Present
 121                 */
 122                ctrl_info(ctrl, "Card present on Slot(%s)\n", slot_name(p_slot));
 123                event_type = INT_PRESENCE_ON;
 124        } else {
 125                /*
 126                 * Not Present
 127                 */
 128                ctrl_info(ctrl, "Card not present on Slot(%s)\n",
 129                          slot_name(p_slot));
 130                event_type = INT_PRESENCE_OFF;
 131        }
 132
 133        queue_interrupt_event(p_slot, event_type);
 134
 135        return 1;
 136}
 137
 138u8 pciehp_handle_power_fault(struct slot *p_slot)
 139{
 140        u32 event_type;
 141        struct controller *ctrl = p_slot->ctrl;
 142
 143        /* power fault */
 144        ctrl_dbg(ctrl, "Power fault interrupt received\n");
 145        ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
 146        event_type = INT_POWER_FAULT;
 147        ctrl_info(ctrl, "Power fault bit %x set\n", 0);
 148        queue_interrupt_event(p_slot, event_type);
 149
 150        return 1;
 151}
 152
 153/* The following routines constitute the bulk of the
 154   hotplug controller logic
 155 */
 156
 157static void set_slot_off(struct controller *ctrl, struct slot * pslot)
 158{
 159        /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
 160        if (POWER_CTRL(ctrl)) {
 161                if (pciehp_power_off_slot(pslot)) {
 162                        ctrl_err(ctrl,
 163                                 "Issue of Slot Power Off command failed\n");
 164                        return;
 165                }
 166                /*
 167                 * After turning power off, we must wait for at least 1 second
 168                 * before taking any action that relies on power having been
 169                 * removed from the slot/adapter.
 170                 */
 171                msleep(1000);
 172        }
 173
 174        if (PWR_LED(ctrl))
 175                pciehp_green_led_off(pslot);
 176
 177        if (ATTN_LED(ctrl)) {
 178                if (pciehp_set_attention_status(pslot, 1)) {
 179                        ctrl_err(ctrl,
 180                                 "Issue of Set Attention Led command failed\n");
 181                        return;
 182                }
 183        }
 184}
 185
 186/**
 187 * board_added - Called after a board has been added to the system.
 188 * @p_slot: &slot where board is added
 189 *
 190 * Turns power on for the board.
 191 * Configures board.
 192 */
 193static int board_added(struct slot *p_slot)
 194{
 195        int retval = 0;
 196        struct controller *ctrl = p_slot->ctrl;
 197        struct pci_bus *parent = ctrl->pcie->port->subordinate;
 198
 199        if (POWER_CTRL(ctrl)) {
 200                /* Power on slot */
 201                retval = pciehp_power_on_slot(p_slot);
 202                if (retval)
 203                        return retval;
 204        }
 205
 206        if (PWR_LED(ctrl))
 207                pciehp_green_led_blink(p_slot);
 208
 209        /* Check link training status */
 210        retval = pciehp_check_link_status(ctrl);
 211        if (retval) {
 212                ctrl_err(ctrl, "Failed to check link status\n");
 213                goto err_exit;
 214        }
 215
 216        /* Check for a power fault */
 217        if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) {
 218                ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
 219                retval = -EIO;
 220                goto err_exit;
 221        }
 222
 223        retval = pciehp_configure_device(p_slot);
 224        if (retval) {
 225                ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
 226                         pci_domain_nr(parent), parent->number);
 227                goto err_exit;
 228        }
 229
 230        if (PWR_LED(ctrl))
 231                pciehp_green_led_on(p_slot);
 232
 233        return 0;
 234
 235err_exit:
 236        set_slot_off(ctrl, p_slot);
 237        return retval;
 238}
 239
 240/**
 241 * remove_board - Turns off slot and LEDs
 242 * @p_slot: slot where board is being removed
 243 */
 244static int remove_board(struct slot *p_slot)
 245{
 246        int retval = 0;
 247        struct controller *ctrl = p_slot->ctrl;
 248
 249        retval = pciehp_unconfigure_device(p_slot);
 250        if (retval)
 251                return retval;
 252
 253        if (POWER_CTRL(ctrl)) {
 254                /* power off slot */
 255                retval = pciehp_power_off_slot(p_slot);
 256                if (retval) {
 257                        ctrl_err(ctrl,
 258                                 "Issue of Slot Disable command failed\n");
 259                        return retval;
 260                }
 261                /*
 262                 * After turning power off, we must wait for at least 1 second
 263                 * before taking any action that relies on power having been
 264                 * removed from the slot/adapter.
 265                 */
 266                msleep(1000);
 267        }
 268
 269        /* turn off Green LED */
 270        if (PWR_LED(ctrl))
 271                pciehp_green_led_off(p_slot);
 272
 273        return 0;
 274}
 275
 276struct power_work_info {
 277        struct slot *p_slot;
 278        struct work_struct work;
 279};
 280
 281/**
 282 * pciehp_power_thread - handle pushbutton events
 283 * @work: &struct work_struct describing work to be done
 284 *
 285 * Scheduled procedure to handle blocking stuff for the pushbuttons.
 286 * Handles all pending events and exits.
 287 */
 288static void pciehp_power_thread(struct work_struct *work)
 289{
 290        struct power_work_info *info =
 291                container_of(work, struct power_work_info, work);
 292        struct slot *p_slot = info->p_slot;
 293
 294        mutex_lock(&p_slot->lock);
 295        switch (p_slot->state) {
 296        case POWEROFF_STATE:
 297                mutex_unlock(&p_slot->lock);
 298                ctrl_dbg(p_slot->ctrl,
 299                         "Disabling domain:bus:device=%04x:%02x:00\n",
 300                         pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
 301                         p_slot->ctrl->pcie->port->subordinate->number);
 302                pciehp_disable_slot(p_slot);
 303                mutex_lock(&p_slot->lock);
 304                p_slot->state = STATIC_STATE;
 305                break;
 306        case POWERON_STATE:
 307                mutex_unlock(&p_slot->lock);
 308                if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl))
 309                        pciehp_green_led_off(p_slot);
 310                mutex_lock(&p_slot->lock);
 311                p_slot->state = STATIC_STATE;
 312                break;
 313        default:
 314                break;
 315        }
 316        mutex_unlock(&p_slot->lock);
 317
 318        kfree(info);
 319}
 320
 321void pciehp_queue_pushbutton_work(struct work_struct *work)
 322{
 323        struct slot *p_slot = container_of(work, struct slot, work.work);
 324        struct power_work_info *info;
 325
 326        info = kmalloc(sizeof(*info), GFP_KERNEL);
 327        if (!info) {
 328                ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
 329                         __func__);
 330                return;
 331        }
 332        info->p_slot = p_slot;
 333        INIT_WORK(&info->work, pciehp_power_thread);
 334
 335        mutex_lock(&p_slot->lock);
 336        switch (p_slot->state) {
 337        case BLINKINGOFF_STATE:
 338                p_slot->state = POWEROFF_STATE;
 339                break;
 340        case BLINKINGON_STATE:
 341                p_slot->state = POWERON_STATE;
 342                break;
 343        default:
 344                kfree(info);
 345                goto out;
 346        }
 347        queue_work(p_slot->wq, &info->work);
 348 out:
 349        mutex_unlock(&p_slot->lock);
 350}
 351
 352/*
 353 * Note: This function must be called with slot->lock held
 354 */
 355static void handle_button_press_event(struct slot *p_slot)
 356{
 357        struct controller *ctrl = p_slot->ctrl;
 358        u8 getstatus;
 359
 360        switch (p_slot->state) {
 361        case STATIC_STATE:
 362                pciehp_get_power_status(p_slot, &getstatus);
 363                if (getstatus) {
 364                        p_slot->state = BLINKINGOFF_STATE;
 365                        ctrl_info(ctrl,
 366                                  "PCI slot #%s - powering off due to button "
 367                                  "press.\n", slot_name(p_slot));
 368                } else {
 369                        p_slot->state = BLINKINGON_STATE;
 370                        ctrl_info(ctrl,
 371                                  "PCI slot #%s - powering on due to button "
 372                                  "press.\n", slot_name(p_slot));
 373                }
 374                /* blink green LED and turn off amber */
 375                if (PWR_LED(ctrl))
 376                        pciehp_green_led_blink(p_slot);
 377                if (ATTN_LED(ctrl))
 378                        pciehp_set_attention_status(p_slot, 0);
 379
 380                queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);
 381                break;
 382        case BLINKINGOFF_STATE:
 383        case BLINKINGON_STATE:
 384                /*
 385                 * Cancel if we are still blinking; this means that we
 386                 * press the attention again before the 5 sec. limit
 387                 * expires to cancel hot-add or hot-remove
 388                 */
 389                ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot));
 390                cancel_delayed_work(&p_slot->work);
 391                if (p_slot->state == BLINKINGOFF_STATE) {
 392                        if (PWR_LED(ctrl))
 393                                pciehp_green_led_on(p_slot);
 394                } else {
 395                        if (PWR_LED(ctrl))
 396                                pciehp_green_led_off(p_slot);
 397                }
 398                if (ATTN_LED(ctrl))
 399                        pciehp_set_attention_status(p_slot, 0);
 400                ctrl_info(ctrl, "PCI slot #%s - action canceled "
 401                          "due to button press\n", slot_name(p_slot));
 402                p_slot->state = STATIC_STATE;
 403                break;
 404        case POWEROFF_STATE:
 405        case POWERON_STATE:
 406                /*
 407                 * Ignore if the slot is on power-on or power-off state;
 408                 * this means that the previous attention button action
 409                 * to hot-add or hot-remove is undergoing
 410                 */
 411                ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot));
 412                break;
 413        default:
 414                ctrl_warn(ctrl, "Not a valid state\n");
 415                break;
 416        }
 417}
 418
 419/*
 420 * Note: This function must be called with slot->lock held
 421 */
 422static void handle_surprise_event(struct slot *p_slot)
 423{
 424        u8 getstatus;
 425        struct power_work_info *info;
 426
 427        info = kmalloc(sizeof(*info), GFP_KERNEL);
 428        if (!info) {
 429                ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
 430                         __func__);
 431                return;
 432        }
 433        info->p_slot = p_slot;
 434        INIT_WORK(&info->work, pciehp_power_thread);
 435
 436        pciehp_get_adapter_status(p_slot, &getstatus);
 437        if (!getstatus)
 438                p_slot->state = POWEROFF_STATE;
 439        else
 440                p_slot->state = POWERON_STATE;
 441
 442        queue_work(p_slot->wq, &info->work);
 443}
 444
 445static void interrupt_event_handler(struct work_struct *work)
 446{
 447        struct event_info *info = container_of(work, struct event_info, work);
 448        struct slot *p_slot = info->p_slot;
 449        struct controller *ctrl = p_slot->ctrl;
 450
 451        mutex_lock(&p_slot->lock);
 452        switch (info->event_type) {
 453        case INT_BUTTON_PRESS:
 454                handle_button_press_event(p_slot);
 455                break;
 456        case INT_POWER_FAULT:
 457                if (!POWER_CTRL(ctrl))
 458                        break;
 459                if (ATTN_LED(ctrl))
 460                        pciehp_set_attention_status(p_slot, 1);
 461                if (PWR_LED(ctrl))
 462                        pciehp_green_led_off(p_slot);
 463                break;
 464        case INT_PRESENCE_ON:
 465        case INT_PRESENCE_OFF:
 466                if (!HP_SUPR_RM(ctrl))
 467                        break;
 468                ctrl_dbg(ctrl, "Surprise Removal\n");
 469                handle_surprise_event(p_slot);
 470                break;
 471        default:
 472                break;
 473        }
 474        mutex_unlock(&p_slot->lock);
 475
 476        kfree(info);
 477}
 478
 479int pciehp_enable_slot(struct slot *p_slot)
 480{
 481        u8 getstatus = 0;
 482        int rc;
 483        struct controller *ctrl = p_slot->ctrl;
 484
 485        rc = pciehp_get_adapter_status(p_slot, &getstatus);
 486        if (rc || !getstatus) {
 487                ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
 488                return -ENODEV;
 489        }
 490        if (MRL_SENS(p_slot->ctrl)) {
 491                rc = pciehp_get_latch_status(p_slot, &getstatus);
 492                if (rc || getstatus) {
 493                        ctrl_info(ctrl, "Latch open on slot(%s)\n",
 494                                  slot_name(p_slot));
 495                        return -ENODEV;
 496                }
 497        }
 498
 499        if (POWER_CTRL(p_slot->ctrl)) {
 500                rc = pciehp_get_power_status(p_slot, &getstatus);
 501                if (rc || getstatus) {
 502                        ctrl_info(ctrl, "Already enabled on slot(%s)\n",
 503                                  slot_name(p_slot));
 504                        return -EINVAL;
 505                }
 506        }
 507
 508        pciehp_get_latch_status(p_slot, &getstatus);
 509
 510        rc = board_added(p_slot);
 511        if (rc) {
 512                pciehp_get_latch_status(p_slot, &getstatus);
 513        }
 514        return rc;
 515}
 516
 517
 518int pciehp_disable_slot(struct slot *p_slot)
 519{
 520        u8 getstatus = 0;
 521        int ret = 0;
 522        struct controller *ctrl = p_slot->ctrl;
 523
 524        if (!p_slot->ctrl)
 525                return 1;
 526
 527        if (!HP_SUPR_RM(p_slot->ctrl)) {
 528                ret = pciehp_get_adapter_status(p_slot, &getstatus);
 529                if (ret || !getstatus) {
 530                        ctrl_info(ctrl, "No adapter on slot(%s)\n",
 531                                  slot_name(p_slot));
 532                        return -ENODEV;
 533                }
 534        }
 535
 536        if (MRL_SENS(p_slot->ctrl)) {
 537                ret = pciehp_get_latch_status(p_slot, &getstatus);
 538                if (ret || getstatus) {
 539                        ctrl_info(ctrl, "Latch open on slot(%s)\n",
 540                                  slot_name(p_slot));
 541                        return -ENODEV;
 542                }
 543        }
 544
 545        if (POWER_CTRL(p_slot->ctrl)) {
 546                ret = pciehp_get_power_status(p_slot, &getstatus);
 547                if (ret || !getstatus) {
 548                        ctrl_info(ctrl, "Already disabled on slot(%s)\n",
 549                                  slot_name(p_slot));
 550                        return -EINVAL;
 551                }
 552        }
 553
 554        return remove_board(p_slot);
 555}
 556
 557int pciehp_sysfs_enable_slot(struct slot *p_slot)
 558{
 559        int retval = -ENODEV;
 560        struct controller *ctrl = p_slot->ctrl;
 561
 562        mutex_lock(&p_slot->lock);
 563        switch (p_slot->state) {
 564        case BLINKINGON_STATE:
 565                cancel_delayed_work(&p_slot->work);
 566        case STATIC_STATE:
 567                p_slot->state = POWERON_STATE;
 568                mutex_unlock(&p_slot->lock);
 569                retval = pciehp_enable_slot(p_slot);
 570                mutex_lock(&p_slot->lock);
 571                p_slot->state = STATIC_STATE;
 572                break;
 573        case POWERON_STATE:
 574                ctrl_info(ctrl, "Slot %s is already in powering on state\n",
 575                          slot_name(p_slot));
 576                break;
 577        case BLINKINGOFF_STATE:
 578        case POWEROFF_STATE:
 579                ctrl_info(ctrl, "Already enabled on slot %s\n",
 580                          slot_name(p_slot));
 581                break;
 582        default:
 583                ctrl_err(ctrl, "Not a valid state on slot %s\n",
 584                         slot_name(p_slot));
 585                break;
 586        }
 587        mutex_unlock(&p_slot->lock);
 588
 589        return retval;
 590}
 591
 592int pciehp_sysfs_disable_slot(struct slot *p_slot)
 593{
 594        int retval = -ENODEV;
 595        struct controller *ctrl = p_slot->ctrl;
 596
 597        mutex_lock(&p_slot->lock);
 598        switch (p_slot->state) {
 599        case BLINKINGOFF_STATE:
 600                cancel_delayed_work(&p_slot->work);
 601        case STATIC_STATE:
 602                p_slot->state = POWEROFF_STATE;
 603                mutex_unlock(&p_slot->lock);
 604                retval = pciehp_disable_slot(p_slot);
 605                mutex_lock(&p_slot->lock);
 606                p_slot->state = STATIC_STATE;
 607                break;
 608        case POWEROFF_STATE:
 609                ctrl_info(ctrl, "Slot %s is already in powering off state\n",
 610                          slot_name(p_slot));
 611                break;
 612        case BLINKINGON_STATE:
 613        case POWERON_STATE:
 614                ctrl_info(ctrl, "Already disabled on slot %s\n",
 615                          slot_name(p_slot));
 616                break;
 617        default:
 618                ctrl_err(ctrl, "Not a valid state on slot %s\n",
 619                         slot_name(p_slot));
 620                break;
 621        }
 622        mutex_unlock(&p_slot->lock);
 623
 624        return retval;
 625}
 626