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/pci.h>
  34#include <linux/workqueue.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        schedule_work(&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
 146        if (!pciehp_query_power_fault(p_slot)) {
 147                /*
 148                 * power fault Cleared
 149                 */
 150                ctrl_info(ctrl, "Power fault cleared on Slot(%s)\n",
 151                          slot_name(p_slot));
 152                event_type = INT_POWER_FAULT_CLEAR;
 153        } else {
 154                /*
 155                 *   power fault
 156                 */
 157                ctrl_info(ctrl, "Power fault on Slot(%s)\n", slot_name(p_slot));
 158                event_type = INT_POWER_FAULT;
 159                ctrl_info(ctrl, "Power fault bit %x set\n", 0);
 160        }
 161
 162        queue_interrupt_event(p_slot, event_type);
 163
 164        return 1;
 165}
 166
 167/* The following routines constitute the bulk of the
 168   hotplug controller logic
 169 */
 170
 171static void set_slot_off(struct controller *ctrl, struct slot * pslot)
 172{
 173        /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
 174        if (POWER_CTRL(ctrl)) {
 175                if (pciehp_power_off_slot(pslot)) {
 176                        ctrl_err(ctrl,
 177                                 "Issue of Slot Power Off command failed\n");
 178                        return;
 179                }
 180                /*
 181                 * After turning power off, we must wait for at least 1 second
 182                 * before taking any action that relies on power having been
 183                 * removed from the slot/adapter.
 184                 */
 185                msleep(1000);
 186        }
 187
 188        if (PWR_LED(ctrl))
 189                pciehp_green_led_off(pslot);
 190
 191        if (ATTN_LED(ctrl)) {
 192                if (pciehp_set_attention_status(pslot, 1)) {
 193                        ctrl_err(ctrl,
 194                                 "Issue of Set Attention Led command failed\n");
 195                        return;
 196                }
 197        }
 198}
 199
 200/**
 201 * board_added - Called after a board has been added to the system.
 202 * @p_slot: &slot where board is added
 203 *
 204 * Turns power on for the board.
 205 * Configures board.
 206 */
 207static int board_added(struct slot *p_slot)
 208{
 209        int retval = 0;
 210        struct controller *ctrl = p_slot->ctrl;
 211        struct pci_bus *parent = ctrl->pcie->port->subordinate;
 212
 213        if (POWER_CTRL(ctrl)) {
 214                /* Power on slot */
 215                retval = pciehp_power_on_slot(p_slot);
 216                if (retval)
 217                        return retval;
 218        }
 219
 220        if (PWR_LED(ctrl))
 221                pciehp_green_led_blink(p_slot);
 222
 223        /* Check link training status */
 224        retval = pciehp_check_link_status(ctrl);
 225        if (retval) {
 226                ctrl_err(ctrl, "Failed to check link status\n");
 227                set_slot_off(ctrl, p_slot);
 228                return retval;
 229        }
 230
 231        /* Check for a power fault */
 232        if (pciehp_query_power_fault(p_slot)) {
 233                ctrl_dbg(ctrl, "Power fault detected\n");
 234                retval = -EIO;
 235                goto err_exit;
 236        }
 237
 238        retval = pciehp_configure_device(p_slot);
 239        if (retval) {
 240                ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
 241                         pci_domain_nr(parent), parent->number);
 242                goto err_exit;
 243        }
 244
 245        if (PWR_LED(ctrl))
 246                pciehp_green_led_on(p_slot);
 247
 248        return 0;
 249
 250err_exit:
 251        set_slot_off(ctrl, p_slot);
 252        return retval;
 253}
 254
 255/**
 256 * remove_board - Turns off slot and LEDs
 257 * @p_slot: slot where board is being removed
 258 */
 259static int remove_board(struct slot *p_slot)
 260{
 261        int retval = 0;
 262        struct controller *ctrl = p_slot->ctrl;
 263
 264        retval = pciehp_unconfigure_device(p_slot);
 265        if (retval)
 266                return retval;
 267
 268        if (POWER_CTRL(ctrl)) {
 269                /* power off slot */
 270                retval = pciehp_power_off_slot(p_slot);
 271                if (retval) {
 272                        ctrl_err(ctrl,
 273                                 "Issue of Slot Disable command failed\n");
 274                        return retval;
 275                }
 276                /*
 277                 * After turning power off, we must wait for at least 1 second
 278                 * before taking any action that relies on power having been
 279                 * removed from the slot/adapter.
 280                 */
 281                msleep(1000);
 282        }
 283
 284        /* turn off Green LED */
 285        if (PWR_LED(ctrl))
 286                pciehp_green_led_off(p_slot);
 287
 288        return 0;
 289}
 290
 291struct power_work_info {
 292        struct slot *p_slot;
 293        struct work_struct work;
 294};
 295
 296/**
 297 * pciehp_power_thread - handle pushbutton events
 298 * @work: &struct work_struct describing work to be done
 299 *
 300 * Scheduled procedure to handle blocking stuff for the pushbuttons.
 301 * Handles all pending events and exits.
 302 */
 303static void pciehp_power_thread(struct work_struct *work)
 304{
 305        struct power_work_info *info =
 306                container_of(work, struct power_work_info, work);
 307        struct slot *p_slot = info->p_slot;
 308
 309        mutex_lock(&p_slot->lock);
 310        switch (p_slot->state) {
 311        case POWEROFF_STATE:
 312                mutex_unlock(&p_slot->lock);
 313                ctrl_dbg(p_slot->ctrl,
 314                         "Disabling domain:bus:device=%04x:%02x:00\n",
 315                         pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
 316                         p_slot->ctrl->pcie->port->subordinate->number);
 317                pciehp_disable_slot(p_slot);
 318                mutex_lock(&p_slot->lock);
 319                p_slot->state = STATIC_STATE;
 320                break;
 321        case POWERON_STATE:
 322                mutex_unlock(&p_slot->lock);
 323                if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl))
 324                        pciehp_green_led_off(p_slot);
 325                mutex_lock(&p_slot->lock);
 326                p_slot->state = STATIC_STATE;
 327                break;
 328        default:
 329                break;
 330        }
 331        mutex_unlock(&p_slot->lock);
 332
 333        kfree(info);
 334}
 335
 336void pciehp_queue_pushbutton_work(struct work_struct *work)
 337{
 338        struct slot *p_slot = container_of(work, struct slot, work.work);
 339        struct power_work_info *info;
 340
 341        info = kmalloc(sizeof(*info), GFP_KERNEL);
 342        if (!info) {
 343                ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
 344                         __func__);
 345                return;
 346        }
 347        info->p_slot = p_slot;
 348        INIT_WORK(&info->work, pciehp_power_thread);
 349
 350        mutex_lock(&p_slot->lock);
 351        switch (p_slot->state) {
 352        case BLINKINGOFF_STATE:
 353                p_slot->state = POWEROFF_STATE;
 354                break;
 355        case BLINKINGON_STATE:
 356                p_slot->state = POWERON_STATE;
 357                break;
 358        default:
 359                goto out;
 360        }
 361        queue_work(pciehp_wq, &info->work);
 362 out:
 363        mutex_unlock(&p_slot->lock);
 364}
 365
 366static int update_slot_info(struct slot *slot)
 367{
 368        struct hotplug_slot_info *info;
 369        int result;
 370
 371        info = kmalloc(sizeof(*info), GFP_KERNEL);
 372        if (!info)
 373                return -ENOMEM;
 374
 375        pciehp_get_power_status(slot, &info->power_status);
 376        pciehp_get_attention_status(slot, &info->attention_status);
 377        pciehp_get_latch_status(slot, &info->latch_status);
 378        pciehp_get_adapter_status(slot, &info->adapter_status);
 379
 380        result = pci_hp_change_slot_info(slot->hotplug_slot, info);
 381        kfree (info);
 382        return result;
 383}
 384
 385/*
 386 * Note: This function must be called with slot->lock held
 387 */
 388static void handle_button_press_event(struct slot *p_slot)
 389{
 390        struct controller *ctrl = p_slot->ctrl;
 391        u8 getstatus;
 392
 393        switch (p_slot->state) {
 394        case STATIC_STATE:
 395                pciehp_get_power_status(p_slot, &getstatus);
 396                if (getstatus) {
 397                        p_slot->state = BLINKINGOFF_STATE;
 398                        ctrl_info(ctrl,
 399                                  "PCI slot #%s - powering off due to button "
 400                                  "press.\n", slot_name(p_slot));
 401                } else {
 402                        p_slot->state = BLINKINGON_STATE;
 403                        ctrl_info(ctrl,
 404                                  "PCI slot #%s - powering on due to button "
 405                                  "press.\n", slot_name(p_slot));
 406                }
 407                /* blink green LED and turn off amber */
 408                if (PWR_LED(ctrl))
 409                        pciehp_green_led_blink(p_slot);
 410                if (ATTN_LED(ctrl))
 411                        pciehp_set_attention_status(p_slot, 0);
 412
 413                schedule_delayed_work(&p_slot->work, 5*HZ);
 414                break;
 415        case BLINKINGOFF_STATE:
 416        case BLINKINGON_STATE:
 417                /*
 418                 * Cancel if we are still blinking; this means that we
 419                 * press the attention again before the 5 sec. limit
 420                 * expires to cancel hot-add or hot-remove
 421                 */
 422                ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot));
 423                cancel_delayed_work(&p_slot->work);
 424                if (p_slot->state == BLINKINGOFF_STATE) {
 425                        if (PWR_LED(ctrl))
 426                                pciehp_green_led_on(p_slot);
 427                } else {
 428                        if (PWR_LED(ctrl))
 429                                pciehp_green_led_off(p_slot);
 430                }
 431                if (ATTN_LED(ctrl))
 432                        pciehp_set_attention_status(p_slot, 0);
 433                ctrl_info(ctrl, "PCI slot #%s - action canceled "
 434                          "due to button press\n", slot_name(p_slot));
 435                p_slot->state = STATIC_STATE;
 436                break;
 437        case POWEROFF_STATE:
 438        case POWERON_STATE:
 439                /*
 440                 * Ignore if the slot is on power-on or power-off state;
 441                 * this means that the previous attention button action
 442                 * to hot-add or hot-remove is undergoing
 443                 */
 444                ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot));
 445                update_slot_info(p_slot);
 446                break;
 447        default:
 448                ctrl_warn(ctrl, "Not a valid state\n");
 449                break;
 450        }
 451}
 452
 453/*
 454 * Note: This function must be called with slot->lock held
 455 */
 456static void handle_surprise_event(struct slot *p_slot)
 457{
 458        u8 getstatus;
 459        struct power_work_info *info;
 460
 461        info = kmalloc(sizeof(*info), GFP_KERNEL);
 462        if (!info) {
 463                ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
 464                         __func__);
 465                return;
 466        }
 467        info->p_slot = p_slot;
 468        INIT_WORK(&info->work, pciehp_power_thread);
 469
 470        pciehp_get_adapter_status(p_slot, &getstatus);
 471        if (!getstatus)
 472                p_slot->state = POWEROFF_STATE;
 473        else
 474                p_slot->state = POWERON_STATE;
 475
 476        queue_work(pciehp_wq, &info->work);
 477}
 478
 479static void interrupt_event_handler(struct work_struct *work)
 480{
 481        struct event_info *info = container_of(work, struct event_info, work);
 482        struct slot *p_slot = info->p_slot;
 483        struct controller *ctrl = p_slot->ctrl;
 484
 485        mutex_lock(&p_slot->lock);
 486        switch (info->event_type) {
 487        case INT_BUTTON_PRESS:
 488                handle_button_press_event(p_slot);
 489                break;
 490        case INT_POWER_FAULT:
 491                if (!POWER_CTRL(ctrl))
 492                        break;
 493                if (ATTN_LED(ctrl))
 494                        pciehp_set_attention_status(p_slot, 1);
 495                if (PWR_LED(ctrl))
 496                        pciehp_green_led_off(p_slot);
 497                break;
 498        case INT_PRESENCE_ON:
 499        case INT_PRESENCE_OFF:
 500                if (!HP_SUPR_RM(ctrl))
 501                        break;
 502                ctrl_dbg(ctrl, "Surprise Removal\n");
 503                update_slot_info(p_slot);
 504                handle_surprise_event(p_slot);
 505                break;
 506        default:
 507                update_slot_info(p_slot);
 508                break;
 509        }
 510        mutex_unlock(&p_slot->lock);
 511
 512        kfree(info);
 513}
 514
 515int pciehp_enable_slot(struct slot *p_slot)
 516{
 517        u8 getstatus = 0;
 518        int rc;
 519        struct controller *ctrl = p_slot->ctrl;
 520
 521        rc = pciehp_get_adapter_status(p_slot, &getstatus);
 522        if (rc || !getstatus) {
 523                ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
 524                return -ENODEV;
 525        }
 526        if (MRL_SENS(p_slot->ctrl)) {
 527                rc = pciehp_get_latch_status(p_slot, &getstatus);
 528                if (rc || getstatus) {
 529                        ctrl_info(ctrl, "Latch open on slot(%s)\n",
 530                                  slot_name(p_slot));
 531                        return -ENODEV;
 532                }
 533        }
 534
 535        if (POWER_CTRL(p_slot->ctrl)) {
 536                rc = pciehp_get_power_status(p_slot, &getstatus);
 537                if (rc || getstatus) {
 538                        ctrl_info(ctrl, "Already enabled on slot(%s)\n",
 539                                  slot_name(p_slot));
 540                        return -EINVAL;
 541                }
 542        }
 543
 544        pciehp_get_latch_status(p_slot, &getstatus);
 545
 546        rc = board_added(p_slot);
 547        if (rc) {
 548                pciehp_get_latch_status(p_slot, &getstatus);
 549        }
 550
 551        update_slot_info(p_slot);
 552
 553        return rc;
 554}
 555
 556
 557int pciehp_disable_slot(struct slot *p_slot)
 558{
 559        u8 getstatus = 0;
 560        int ret = 0;
 561        struct controller *ctrl = p_slot->ctrl;
 562
 563        if (!p_slot->ctrl)
 564                return 1;
 565
 566        if (!HP_SUPR_RM(p_slot->ctrl)) {
 567                ret = pciehp_get_adapter_status(p_slot, &getstatus);
 568                if (ret || !getstatus) {
 569                        ctrl_info(ctrl, "No adapter on slot(%s)\n",
 570                                  slot_name(p_slot));
 571                        return -ENODEV;
 572                }
 573        }
 574
 575        if (MRL_SENS(p_slot->ctrl)) {
 576                ret = pciehp_get_latch_status(p_slot, &getstatus);
 577                if (ret || getstatus) {
 578                        ctrl_info(ctrl, "Latch open on slot(%s)\n",
 579                                  slot_name(p_slot));
 580                        return -ENODEV;
 581                }
 582        }
 583
 584        if (POWER_CTRL(p_slot->ctrl)) {
 585                ret = pciehp_get_power_status(p_slot, &getstatus);
 586                if (ret || !getstatus) {
 587                        ctrl_info(ctrl, "Already disabled on slot(%s)\n",
 588                                  slot_name(p_slot));
 589                        return -EINVAL;
 590                }
 591        }
 592
 593        ret = remove_board(p_slot);
 594        update_slot_info(p_slot);
 595
 596        return ret;
 597}
 598
 599int pciehp_sysfs_enable_slot(struct slot *p_slot)
 600{
 601        int retval = -ENODEV;
 602        struct controller *ctrl = p_slot->ctrl;
 603
 604        mutex_lock(&p_slot->lock);
 605        switch (p_slot->state) {
 606        case BLINKINGON_STATE:
 607                cancel_delayed_work(&p_slot->work);
 608        case STATIC_STATE:
 609                p_slot->state = POWERON_STATE;
 610                mutex_unlock(&p_slot->lock);
 611                retval = pciehp_enable_slot(p_slot);
 612                mutex_lock(&p_slot->lock);
 613                p_slot->state = STATIC_STATE;
 614                break;
 615        case POWERON_STATE:
 616                ctrl_info(ctrl, "Slot %s is already in powering on state\n",
 617                          slot_name(p_slot));
 618                break;
 619        case BLINKINGOFF_STATE:
 620        case POWEROFF_STATE:
 621                ctrl_info(ctrl, "Already enabled on slot %s\n",
 622                          slot_name(p_slot));
 623                break;
 624        default:
 625                ctrl_err(ctrl, "Not a valid state on slot %s\n",
 626                         slot_name(p_slot));
 627                break;
 628        }
 629        mutex_unlock(&p_slot->lock);
 630
 631        return retval;
 632}
 633
 634int pciehp_sysfs_disable_slot(struct slot *p_slot)
 635{
 636        int retval = -ENODEV;
 637        struct controller *ctrl = p_slot->ctrl;
 638
 639        mutex_lock(&p_slot->lock);
 640        switch (p_slot->state) {
 641        case BLINKINGOFF_STATE:
 642                cancel_delayed_work(&p_slot->work);
 643        case STATIC_STATE:
 644                p_slot->state = POWEROFF_STATE;
 645                mutex_unlock(&p_slot->lock);
 646                retval = pciehp_disable_slot(p_slot);
 647                mutex_lock(&p_slot->lock);
 648                p_slot->state = STATIC_STATE;
 649                break;
 650        case POWEROFF_STATE:
 651                ctrl_info(ctrl, "Slot %s is already in powering off state\n",
 652                          slot_name(p_slot));
 653                break;
 654        case BLINKINGON_STATE:
 655        case POWERON_STATE:
 656                ctrl_info(ctrl, "Already disabled on slot %s\n",
 657                          slot_name(p_slot));
 658                break;
 659        default:
 660                ctrl_err(ctrl, "Not a valid state on slot %s\n",
 661                         slot_name(p_slot));
 662                break;
 663        }
 664        mutex_unlock(&p_slot->lock);
 665
 666        return retval;
 667}
 668