Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * ACPI PCI Hot Plug Controller Driver
0004  *
0005  * Copyright (C) 1995,2001 Compaq Computer Corporation
0006  * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
0007  * Copyright (C) 2001 IBM Corp.
0008  * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
0009  * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
0010  * Copyright (C) 2002,2003 NEC Corporation
0011  * Copyright (C) 2003-2005 Matthew Wilcox (willy@infradead.org)
0012  * Copyright (C) 2003-2005 Hewlett Packard
0013  *
0014  * All rights reserved.
0015  *
0016  * Send feedback to <kristen.c.accardi@intel.com>
0017  *
0018  */
0019 
0020 #define pr_fmt(fmt) "acpiphp: " fmt
0021 
0022 #include <linux/init.h>
0023 #include <linux/module.h>
0024 #include <linux/moduleparam.h>
0025 
0026 #include <linux/kernel.h>
0027 #include <linux/pci.h>
0028 #include <linux/pci-acpi.h>
0029 #include <linux/pci_hotplug.h>
0030 #include <linux/slab.h>
0031 #include <linux/smp.h>
0032 #include "acpiphp.h"
0033 
0034 /* name size which is used for entries in pcihpfs */
0035 #define SLOT_NAME_SIZE  21              /* {_SUN} */
0036 
0037 bool acpiphp_disabled;
0038 
0039 /* local variables */
0040 static struct acpiphp_attention_info *attention_info;
0041 
0042 #define DRIVER_VERSION  "0.5"
0043 #define DRIVER_AUTHOR   "Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>, Matthew Wilcox <willy@infradead.org>"
0044 #define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver"
0045 
0046 MODULE_AUTHOR(DRIVER_AUTHOR);
0047 MODULE_DESCRIPTION(DRIVER_DESC);
0048 MODULE_LICENSE("GPL");
0049 MODULE_PARM_DESC(disable, "disable acpiphp driver");
0050 module_param_named(disable, acpiphp_disabled, bool, 0444);
0051 
0052 static int enable_slot(struct hotplug_slot *slot);
0053 static int disable_slot(struct hotplug_slot *slot);
0054 static int set_attention_status(struct hotplug_slot *slot, u8 value);
0055 static int get_power_status(struct hotplug_slot *slot, u8 *value);
0056 static int get_attention_status(struct hotplug_slot *slot, u8 *value);
0057 static int get_latch_status(struct hotplug_slot *slot, u8 *value);
0058 static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
0059 
0060 static const struct hotplug_slot_ops acpi_hotplug_slot_ops = {
0061     .enable_slot        = enable_slot,
0062     .disable_slot       = disable_slot,
0063     .set_attention_status   = set_attention_status,
0064     .get_power_status   = get_power_status,
0065     .get_attention_status   = get_attention_status,
0066     .get_latch_status   = get_latch_status,
0067     .get_adapter_status = get_adapter_status,
0068 };
0069 
0070 /**
0071  * acpiphp_register_attention - set attention LED callback
0072  * @info: must be completely filled with LED callbacks
0073  *
0074  * Description: This is used to register a hardware specific ACPI
0075  * driver that manipulates the attention LED.  All the fields in
0076  * info must be set.
0077  */
0078 int acpiphp_register_attention(struct acpiphp_attention_info *info)
0079 {
0080     int retval = -EINVAL;
0081 
0082     if (info && info->owner && info->set_attn &&
0083             info->get_attn && !attention_info) {
0084         retval = 0;
0085         attention_info = info;
0086     }
0087     return retval;
0088 }
0089 EXPORT_SYMBOL_GPL(acpiphp_register_attention);
0090 
0091 
0092 /**
0093  * acpiphp_unregister_attention - unset attention LED callback
0094  * @info: must match the pointer used to register
0095  *
0096  * Description: This is used to un-register a hardware specific acpi
0097  * driver that manipulates the attention LED.  The pointer to the
0098  * info struct must be the same as the one used to set it.
0099  */
0100 int acpiphp_unregister_attention(struct acpiphp_attention_info *info)
0101 {
0102     int retval = -EINVAL;
0103 
0104     if (info && attention_info == info) {
0105         attention_info = NULL;
0106         retval = 0;
0107     }
0108     return retval;
0109 }
0110 EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
0111 
0112 
0113 /**
0114  * enable_slot - power on and enable a slot
0115  * @hotplug_slot: slot to enable
0116  *
0117  * Actual tasks are done in acpiphp_enable_slot()
0118  */
0119 static int enable_slot(struct hotplug_slot *hotplug_slot)
0120 {
0121     struct slot *slot = to_slot(hotplug_slot);
0122 
0123     pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
0124 
0125     /* enable the specified slot */
0126     return acpiphp_enable_slot(slot->acpi_slot);
0127 }
0128 
0129 
0130 /**
0131  * disable_slot - disable and power off a slot
0132  * @hotplug_slot: slot to disable
0133  *
0134  * Actual tasks are done in acpiphp_disable_slot()
0135  */
0136 static int disable_slot(struct hotplug_slot *hotplug_slot)
0137 {
0138     struct slot *slot = to_slot(hotplug_slot);
0139 
0140     pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
0141 
0142     /* disable the specified slot */
0143     return acpiphp_disable_slot(slot->acpi_slot);
0144 }
0145 
0146 
0147 /**
0148  * set_attention_status - set attention LED
0149  * @hotplug_slot: slot to set attention LED on
0150  * @status: value to set attention LED to (0 or 1)
0151  *
0152  * attention status LED, so we use a callback that
0153  * was registered with us.  This allows hardware specific
0154  * ACPI implementations to blink the light for us.
0155  */
0156 static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
0157 {
0158     int retval = -ENODEV;
0159 
0160     pr_debug("%s - physical_slot = %s\n", __func__,
0161         hotplug_slot_name(hotplug_slot));
0162 
0163     if (attention_info && try_module_get(attention_info->owner)) {
0164         retval = attention_info->set_attn(hotplug_slot, status);
0165         module_put(attention_info->owner);
0166     } else
0167         attention_info = NULL;
0168     return retval;
0169 }
0170 
0171 
0172 /**
0173  * get_power_status - get power status of a slot
0174  * @hotplug_slot: slot to get status
0175  * @value: pointer to store status
0176  *
0177  * Some platforms may not implement _STA method properly.
0178  * In that case, the value returned may not be reliable.
0179  */
0180 static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
0181 {
0182     struct slot *slot = to_slot(hotplug_slot);
0183 
0184     pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
0185 
0186     *value = acpiphp_get_power_status(slot->acpi_slot);
0187 
0188     return 0;
0189 }
0190 
0191 
0192 /**
0193  * get_attention_status - get attention LED status
0194  * @hotplug_slot: slot to get status from
0195  * @value: returns with value of attention LED
0196  *
0197  * ACPI doesn't have known method to determine the state
0198  * of the attention status LED, so we use a callback that
0199  * was registered with us.  This allows hardware specific
0200  * ACPI implementations to determine its state.
0201  */
0202 static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
0203 {
0204     int retval = -EINVAL;
0205 
0206     pr_debug("%s - physical_slot = %s\n", __func__,
0207         hotplug_slot_name(hotplug_slot));
0208 
0209     if (attention_info && try_module_get(attention_info->owner)) {
0210         retval = attention_info->get_attn(hotplug_slot, value);
0211         module_put(attention_info->owner);
0212     } else
0213         attention_info = NULL;
0214     return retval;
0215 }
0216 
0217 
0218 /**
0219  * get_latch_status - get latch status of a slot
0220  * @hotplug_slot: slot to get status
0221  * @value: pointer to store status
0222  *
0223  * ACPI doesn't provide any formal means to access latch status.
0224  * Instead, we fake latch status from _STA.
0225  */
0226 static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
0227 {
0228     struct slot *slot = to_slot(hotplug_slot);
0229 
0230     pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
0231 
0232     *value = acpiphp_get_latch_status(slot->acpi_slot);
0233 
0234     return 0;
0235 }
0236 
0237 
0238 /**
0239  * get_adapter_status - get adapter status of a slot
0240  * @hotplug_slot: slot to get status
0241  * @value: pointer to store status
0242  *
0243  * ACPI doesn't provide any formal means to access adapter status.
0244  * Instead, we fake adapter status from _STA.
0245  */
0246 static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
0247 {
0248     struct slot *slot = to_slot(hotplug_slot);
0249 
0250     pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
0251 
0252     *value = acpiphp_get_adapter_status(slot->acpi_slot);
0253 
0254     return 0;
0255 }
0256 
0257 /* callback routine to initialize 'struct slot' for each slot */
0258 int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
0259                   unsigned int sun)
0260 {
0261     struct slot *slot;
0262     int retval = -ENOMEM;
0263     char name[SLOT_NAME_SIZE];
0264 
0265     slot = kzalloc(sizeof(*slot), GFP_KERNEL);
0266     if (!slot)
0267         goto error;
0268 
0269     slot->hotplug_slot.ops = &acpi_hotplug_slot_ops;
0270 
0271     slot->acpi_slot = acpiphp_slot;
0272 
0273     acpiphp_slot->slot = slot;
0274     slot->sun = sun;
0275     snprintf(name, SLOT_NAME_SIZE, "%u", sun);
0276 
0277     retval = pci_hp_register(&slot->hotplug_slot, acpiphp_slot->bus,
0278                  acpiphp_slot->device, name);
0279     if (retval == -EBUSY)
0280         goto error_slot;
0281     if (retval) {
0282         pr_err("pci_hp_register failed with error %d\n", retval);
0283         goto error_slot;
0284     }
0285 
0286     pr_info("Slot [%s] registered\n", slot_name(slot));
0287 
0288     return 0;
0289 error_slot:
0290     kfree(slot);
0291 error:
0292     return retval;
0293 }
0294 
0295 
0296 void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
0297 {
0298     struct slot *slot = acpiphp_slot->slot;
0299 
0300     pr_info("Slot [%s] unregistered\n", slot_name(slot));
0301 
0302     pci_hp_deregister(&slot->hotplug_slot);
0303     kfree(slot);
0304 }
0305 
0306 
0307 void __init acpiphp_init(void)
0308 {
0309     pr_info(DRIVER_DESC " version: " DRIVER_VERSION "%s\n",
0310         acpiphp_disabled ? ", disabled by user; please report a bug"
0311                  : "");
0312 }