linux/drivers/pci/hotplug/pciehp_core.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/moduleparam.h>
  32#include <linux/kernel.h>
  33#include <linux/slab.h>
  34#include <linux/types.h>
  35#include <linux/pci.h>
  36#include "pciehp.h"
  37#include <linux/interrupt.h>
  38#include <linux/time.h>
  39
  40/* Global variables */
  41bool pciehp_debug;
  42bool pciehp_poll_mode;
  43int pciehp_poll_time;
  44static bool pciehp_force;
  45
  46#define DRIVER_VERSION  "0.4"
  47#define DRIVER_AUTHOR   "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
  48#define DRIVER_DESC     "PCI Express Hot Plug Controller Driver"
  49
  50MODULE_AUTHOR(DRIVER_AUTHOR);
  51MODULE_DESCRIPTION(DRIVER_DESC);
  52MODULE_LICENSE("GPL");
  53
  54module_param(pciehp_debug, bool, 0644);
  55module_param(pciehp_poll_mode, bool, 0644);
  56module_param(pciehp_poll_time, int, 0644);
  57module_param(pciehp_force, bool, 0644);
  58MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
  59MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
  60MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
  61MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if OSHP is missing");
  62
  63#define PCIE_MODULE_NAME "pciehp"
  64
  65static int set_attention_status(struct hotplug_slot *slot, u8 value);
  66static int enable_slot(struct hotplug_slot *slot);
  67static int disable_slot(struct hotplug_slot *slot);
  68static int get_power_status(struct hotplug_slot *slot, u8 *value);
  69static int get_attention_status(struct hotplug_slot *slot, u8 *value);
  70static int get_latch_status(struct hotplug_slot *slot, u8 *value);
  71static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
  72static int reset_slot(struct hotplug_slot *slot, int probe);
  73
  74/**
  75 * release_slot - free up the memory used by a slot
  76 * @hotplug_slot: slot to free
  77 */
  78static void release_slot(struct hotplug_slot *hotplug_slot)
  79{
  80        kfree(hotplug_slot->ops);
  81        kfree(hotplug_slot->info);
  82        kfree(hotplug_slot);
  83}
  84
  85static int init_slot(struct controller *ctrl)
  86{
  87        struct slot *slot = ctrl->slot;
  88        struct hotplug_slot *hotplug = NULL;
  89        struct hotplug_slot_info *info = NULL;
  90        struct hotplug_slot_ops *ops = NULL;
  91        char name[SLOT_NAME_SIZE];
  92        int retval = -ENOMEM;
  93
  94        hotplug = kzalloc(sizeof(*hotplug), GFP_KERNEL);
  95        if (!hotplug)
  96                goto out;
  97
  98        info = kzalloc(sizeof(*info), GFP_KERNEL);
  99        if (!info)
 100                goto out;
 101
 102        /* Setup hotplug slot ops */
 103        ops = kzalloc(sizeof(*ops), GFP_KERNEL);
 104        if (!ops)
 105                goto out;
 106
 107        ops->enable_slot = enable_slot;
 108        ops->disable_slot = disable_slot;
 109        ops->get_power_status = get_power_status;
 110        ops->get_adapter_status = get_adapter_status;
 111        ops->reset_slot = reset_slot;
 112        if (MRL_SENS(ctrl))
 113                ops->get_latch_status = get_latch_status;
 114        if (ATTN_LED(ctrl)) {
 115                ops->get_attention_status = get_attention_status;
 116                ops->set_attention_status = set_attention_status;
 117        }
 118
 119        /* register this slot with the hotplug pci core */
 120        hotplug->info = info;
 121        hotplug->private = slot;
 122        hotplug->release = &release_slot;
 123        hotplug->ops = ops;
 124        slot->hotplug_slot = hotplug;
 125        snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));
 126
 127        retval = pci_hp_register(hotplug,
 128                                 ctrl->pcie->port->subordinate, 0, name);
 129        if (retval)
 130                ctrl_err(ctrl, "pci_hp_register failed: error %d\n", retval);
 131out:
 132        if (retval) {
 133                kfree(ops);
 134                kfree(info);
 135                kfree(hotplug);
 136        }
 137        return retval;
 138}
 139
 140static void cleanup_slot(struct controller *ctrl)
 141{
 142        pci_hp_deregister(ctrl->slot->hotplug_slot);
 143}
 144
 145/*
 146 * set_attention_status - Turns the Amber LED for a slot on, off or blink
 147 */
 148static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
 149{
 150        struct slot *slot = hotplug_slot->private;
 151
 152        pciehp_set_attention_status(slot, status);
 153        return 0;
 154}
 155
 156
 157static int enable_slot(struct hotplug_slot *hotplug_slot)
 158{
 159        struct slot *slot = hotplug_slot->private;
 160
 161        return pciehp_sysfs_enable_slot(slot);
 162}
 163
 164
 165static int disable_slot(struct hotplug_slot *hotplug_slot)
 166{
 167        struct slot *slot = hotplug_slot->private;
 168
 169        return pciehp_sysfs_disable_slot(slot);
 170}
 171
 172static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 173{
 174        struct slot *slot = hotplug_slot->private;
 175
 176        pciehp_get_power_status(slot, value);
 177        return 0;
 178}
 179
 180static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 181{
 182        struct slot *slot = hotplug_slot->private;
 183
 184        pciehp_get_attention_status(slot, value);
 185        return 0;
 186}
 187
 188static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 189{
 190        struct slot *slot = hotplug_slot->private;
 191
 192        pciehp_get_latch_status(slot, value);
 193        return 0;
 194}
 195
 196static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 197{
 198        struct slot *slot = hotplug_slot->private;
 199
 200        pciehp_get_adapter_status(slot, value);
 201        return 0;
 202}
 203
 204static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
 205{
 206        struct slot *slot = hotplug_slot->private;
 207
 208        return pciehp_reset_slot(slot, probe);
 209}
 210
 211static int pciehp_probe(struct pcie_device *dev)
 212{
 213        int rc;
 214        struct controller *ctrl;
 215        struct slot *slot;
 216        u8 occupied, poweron;
 217
 218        /* If this is not a "hotplug" service, we have no business here. */
 219        if (dev->service != PCIE_PORT_SERVICE_HP)
 220                return -ENODEV;
 221
 222        if (!dev->port->subordinate) {
 223                /* Can happen if we run out of bus numbers during probe */
 224                dev_err(&dev->device,
 225                        "Hotplug bridge without secondary bus, ignoring\n");
 226                return -ENODEV;
 227        }
 228
 229        ctrl = pcie_init(dev);
 230        if (!ctrl) {
 231                dev_err(&dev->device, "Controller initialization failed\n");
 232                return -ENODEV;
 233        }
 234        set_service_data(dev, ctrl);
 235
 236        /* Setup the slot information structures */
 237        rc = init_slot(ctrl);
 238        if (rc) {
 239                if (rc == -EBUSY)
 240                        ctrl_warn(ctrl, "Slot already registered by another hotplug driver\n");
 241                else
 242                        ctrl_err(ctrl, "Slot initialization failed (%d)\n", rc);
 243                goto err_out_release_ctlr;
 244        }
 245
 246        /* Enable events after we have setup the data structures */
 247        rc = pcie_init_notification(ctrl);
 248        if (rc) {
 249                ctrl_err(ctrl, "Notification initialization failed (%d)\n", rc);
 250                goto err_out_free_ctrl_slot;
 251        }
 252
 253        /* Check if slot is occupied */
 254        slot = ctrl->slot;
 255        pciehp_get_adapter_status(slot, &occupied);
 256        pciehp_get_power_status(slot, &poweron);
 257        if (occupied && pciehp_force) {
 258                mutex_lock(&slot->hotplug_lock);
 259                pciehp_enable_slot(slot);
 260                mutex_unlock(&slot->hotplug_lock);
 261        }
 262        /* If empty slot's power status is on, turn power off */
 263        if (!occupied && poweron && POWER_CTRL(ctrl))
 264                pciehp_power_off_slot(slot);
 265
 266        return 0;
 267
 268err_out_free_ctrl_slot:
 269        cleanup_slot(ctrl);
 270err_out_release_ctlr:
 271        pciehp_release_ctrl(ctrl);
 272        return -ENODEV;
 273}
 274
 275static void pciehp_remove(struct pcie_device *dev)
 276{
 277        struct controller *ctrl = get_service_data(dev);
 278
 279        cleanup_slot(ctrl);
 280        pciehp_release_ctrl(ctrl);
 281}
 282
 283#ifdef CONFIG_PM
 284static int pciehp_suspend(struct pcie_device *dev)
 285{
 286        return 0;
 287}
 288
 289static int pciehp_resume(struct pcie_device *dev)
 290{
 291        struct controller *ctrl;
 292        struct slot *slot;
 293        u8 status;
 294
 295        ctrl = get_service_data(dev);
 296
 297        /* reinitialize the chipset's event detection logic */
 298        pcie_enable_notification(ctrl);
 299
 300        slot = ctrl->slot;
 301
 302        /* Check if slot is occupied */
 303        pciehp_get_adapter_status(slot, &status);
 304        mutex_lock(&slot->hotplug_lock);
 305        if (status)
 306                pciehp_enable_slot(slot);
 307        else
 308                pciehp_disable_slot(slot);
 309        mutex_unlock(&slot->hotplug_lock);
 310        return 0;
 311}
 312#endif /* PM */
 313
 314static struct pcie_port_service_driver hpdriver_portdrv = {
 315        .name           = PCIE_MODULE_NAME,
 316        .port_type      = PCIE_ANY_PORT,
 317        .service        = PCIE_PORT_SERVICE_HP,
 318
 319        .probe          = pciehp_probe,
 320        .remove         = pciehp_remove,
 321
 322#ifdef  CONFIG_PM
 323        .suspend        = pciehp_suspend,
 324        .resume         = pciehp_resume,
 325#endif  /* PM */
 326};
 327
 328static int __init pcied_init(void)
 329{
 330        int retval = 0;
 331
 332        retval = pcie_port_service_register(&hpdriver_portdrv);
 333        dbg("pcie_port_service_register = %d\n", retval);
 334        info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
 335        if (retval)
 336                dbg("Failure to register service\n");
 337
 338        return retval;
 339}
 340
 341static void __exit pcied_cleanup(void)
 342{
 343        dbg("unload_pciehpd()\n");
 344        pcie_port_service_unregister(&hpdriver_portdrv);
 345        info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
 346}
 347
 348module_init(pcied_init);
 349module_exit(pcied_cleanup);
 350
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.