linux/drivers/pci/hotplug/acpiphp_core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * ACPI PCI 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) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
   9 * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
  10 * Copyright (C) 2002,2003 NEC Corporation
  11 * Copyright (C) 2003-2005 Matthew Wilcox (matthew.wilcox@hp.com)
  12 * Copyright (C) 2003-2005 Hewlett Packard
  13 *
  14 * All rights reserved.
  15 *
  16 * Send feedback to <kristen.c.accardi@intel.com>
  17 *
  18 */
  19
  20#define pr_fmt(fmt) "acpiphp: " fmt
  21
  22#include <linux/init.h>
  23#include <linux/module.h>
  24#include <linux/moduleparam.h>
  25
  26#include <linux/kernel.h>
  27#include <linux/pci.h>
  28#include <linux/pci-acpi.h>
  29#include <linux/pci_hotplug.h>
  30#include <linux/slab.h>
  31#include <linux/smp.h>
  32#include "acpiphp.h"
  33
  34/* name size which is used for entries in pcihpfs */
  35#define SLOT_NAME_SIZE  21              /* {_SUN} */
  36
  37bool acpiphp_disabled;
  38
  39/* local variables */
  40static struct acpiphp_attention_info *attention_info;
  41
  42#define DRIVER_VERSION  "0.5"
  43#define DRIVER_AUTHOR   "Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>, Matthew Wilcox <willy@hp.com>"
  44#define DRIVER_DESC     "ACPI Hot Plug PCI Controller Driver"
  45
  46MODULE_AUTHOR(DRIVER_AUTHOR);
  47MODULE_DESCRIPTION(DRIVER_DESC);
  48MODULE_LICENSE("GPL");
  49MODULE_PARM_DESC(disable, "disable acpiphp driver");
  50module_param_named(disable, acpiphp_disabled, bool, 0444);
  51
  52static int enable_slot(struct hotplug_slot *slot);
  53static int disable_slot(struct hotplug_slot *slot);
  54static int set_attention_status(struct hotplug_slot *slot, u8 value);
  55static int get_power_status(struct hotplug_slot *slot, u8 *value);
  56static int get_attention_status(struct hotplug_slot *slot, u8 *value);
  57static int get_latch_status(struct hotplug_slot *slot, u8 *value);
  58static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
  59
  60static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
  61        .enable_slot            = enable_slot,
  62        .disable_slot           = disable_slot,
  63        .set_attention_status   = set_attention_status,
  64        .get_power_status       = get_power_status,
  65        .get_attention_status   = get_attention_status,
  66        .get_latch_status       = get_latch_status,
  67        .get_adapter_status     = get_adapter_status,
  68};
  69
  70/**
  71 * acpiphp_register_attention - set attention LED callback
  72 * @info: must be completely filled with LED callbacks
  73 *
  74 * Description: This is used to register a hardware specific ACPI
  75 * driver that manipulates the attention LED.  All the fields in
  76 * info must be set.
  77 */
  78int acpiphp_register_attention(struct acpiphp_attention_info *info)
  79{
  80        int retval = -EINVAL;
  81
  82        if (info && info->owner && info->set_attn &&
  83                        info->get_attn && !attention_info) {
  84                retval = 0;
  85                attention_info = info;
  86        }
  87        return retval;
  88}
  89EXPORT_SYMBOL_GPL(acpiphp_register_attention);
  90
  91
  92/**
  93 * acpiphp_unregister_attention - unset attention LED callback
  94 * @info: must match the pointer used to register
  95 *
  96 * Description: This is used to un-register a hardware specific acpi
  97 * driver that manipulates the attention LED.  The pointer to the
  98 * info struct must be the same as the one used to set it.
  99 */
 100int acpiphp_unregister_attention(struct acpiphp_attention_info *info)
 101{
 102        int retval = -EINVAL;
 103
 104        if (info && attention_info == info) {
 105                attention_info = NULL;
 106                retval = 0;
 107        }
 108        return retval;
 109}
 110EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
 111
 112
 113/**
 114 * enable_slot - power on and enable a slot
 115 * @hotplug_slot: slot to enable
 116 *
 117 * Actual tasks are done in acpiphp_enable_slot()
 118 */
 119static int enable_slot(struct hotplug_slot *hotplug_slot)
 120{
 121        struct slot *slot = hotplug_slot->private;
 122
 123        pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
 124
 125        /* enable the specified slot */
 126        return acpiphp_enable_slot(slot->acpi_slot);
 127}
 128
 129
 130/**
 131 * disable_slot - disable and power off a slot
 132 * @hotplug_slot: slot to disable
 133 *
 134 * Actual tasks are done in acpiphp_disable_slot()
 135 */
 136static int disable_slot(struct hotplug_slot *hotplug_slot)
 137{
 138        struct slot *slot = hotplug_slot->private;
 139
 140        pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
 141
 142        /* disable the specified slot */
 143        return acpiphp_disable_slot(slot->acpi_slot);
 144}
 145
 146
 147/**
 148 * set_attention_status - set attention LED
 149 * @hotplug_slot: slot to set attention LED on
 150 * @status: value to set attention LED to (0 or 1)
 151 *
 152 * attention status LED, so we use a callback that
 153 * was registered with us.  This allows hardware specific
 154 * ACPI implementations to blink the light for us.
 155 */
 156static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
 157{
 158        int retval = -ENODEV;
 159
 160        pr_debug("%s - physical_slot = %s\n", __func__,
 161                hotplug_slot_name(hotplug_slot));
 162
 163        if (attention_info && try_module_get(attention_info->owner)) {
 164                retval = attention_info->set_attn(hotplug_slot, status);
 165                module_put(attention_info->owner);
 166        } else
 167                attention_info = NULL;
 168        return retval;
 169}
 170
 171
 172/**
 173 * get_power_status - get power status of a slot
 174 * @hotplug_slot: slot to get status
 175 * @value: pointer to store status
 176 *
 177 * Some platforms may not implement _STA method properly.
 178 * In that case, the value returned may not be reliable.
 179 */
 180static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 181{
 182        struct slot *slot = hotplug_slot->private;
 183
 184        pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
 185
 186        *value = acpiphp_get_power_status(slot->acpi_slot);
 187
 188        return 0;
 189}
 190
 191
 192/**
 193 * get_attention_status - get attention LED status
 194 * @hotplug_slot: slot to get status from
 195 * @value: returns with value of attention LED
 196 *
 197 * ACPI doesn't have known method to determine the state
 198 * of the attention status LED, so we use a callback that
 199 * was registered with us.  This allows hardware specific
 200 * ACPI implementations to determine its state.
 201 */
 202static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 203{
 204        int retval = -EINVAL;
 205
 206        pr_debug("%s - physical_slot = %s\n", __func__,
 207                hotplug_slot_name(hotplug_slot));
 208
 209        if (attention_info && try_module_get(attention_info->owner)) {
 210                retval = attention_info->get_attn(hotplug_slot, value);
 211                module_put(attention_info->owner);
 212        } else
 213                attention_info = NULL;
 214        return retval;
 215}
 216
 217
 218/**
 219 * get_latch_status - get latch status of a slot
 220 * @hotplug_slot: slot to get status
 221 * @value: pointer to store status
 222 *
 223 * ACPI doesn't provide any formal means to access latch status.
 224 * Instead, we fake latch status from _STA.
 225 */
 226static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 227{
 228        struct slot *slot = hotplug_slot->private;
 229
 230        pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
 231
 232        *value = acpiphp_get_latch_status(slot->acpi_slot);
 233
 234        return 0;
 235}
 236
 237
 238/**
 239 * get_adapter_status - get adapter status of a slot
 240 * @hotplug_slot: slot to get status
 241 * @value: pointer to store status
 242 *
 243 * ACPI doesn't provide any formal means to access adapter status.
 244 * Instead, we fake adapter status from _STA.
 245 */
 246static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 247{
 248        struct slot *slot = hotplug_slot->private;
 249
 250        pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
 251
 252        *value = acpiphp_get_adapter_status(slot->acpi_slot);
 253
 254        return 0;
 255}
 256
 257/* callback routine to initialize 'struct slot' for each slot */
 258int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
 259                                  unsigned int sun)
 260{
 261        struct slot *slot;
 262        int retval = -ENOMEM;
 263        char name[SLOT_NAME_SIZE];
 264
 265        slot = kzalloc(sizeof(*slot), GFP_KERNEL);
 266        if (!slot)
 267                goto error;
 268
 269        slot->hotplug_slot = kzalloc(sizeof(*slot->hotplug_slot), GFP_KERNEL);
 270        if (!slot->hotplug_slot)
 271                goto error_slot;
 272
 273        slot->hotplug_slot->info = &slot->info;
 274
 275        slot->hotplug_slot->private = slot;
 276        slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
 277
 278        slot->acpi_slot = acpiphp_slot;
 279        slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot);
 280        slot->hotplug_slot->info->attention_status = 0;
 281        slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot);
 282        slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
 283
 284        acpiphp_slot->slot = slot;
 285        slot->sun = sun;
 286        snprintf(name, SLOT_NAME_SIZE, "%u", sun);
 287
 288        retval = pci_hp_register(slot->hotplug_slot, acpiphp_slot->bus,
 289                                 acpiphp_slot->device, name);
 290        if (retval == -EBUSY)
 291                goto error_hpslot;
 292        if (retval) {
 293                pr_err("pci_hp_register failed with error %d\n", retval);
 294                goto error_hpslot;
 295        }
 296
 297        pr_info("Slot [%s] registered\n", slot_name(slot));
 298
 299        return 0;
 300error_hpslot:
 301        kfree(slot->hotplug_slot);
 302error_slot:
 303        kfree(slot);
 304error:
 305        return retval;
 306}
 307
 308
 309void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
 310{
 311        struct slot *slot = acpiphp_slot->slot;
 312
 313        pr_info("Slot [%s] unregistered\n", slot_name(slot));
 314
 315        pci_hp_deregister(slot->hotplug_slot);
 316        kfree(slot->hotplug_slot);
 317        kfree(slot);
 318}
 319
 320
 321void __init acpiphp_init(void)
 322{
 323        pr_info(DRIVER_DESC " version: " DRIVER_VERSION "%s\n",
 324                acpiphp_disabled ? ", disabled by user; please report a bug"
 325                                 : "");
 326}
 327