Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Standard 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) 2003-2004 Intel Corporation
0009  *
0010  * All rights reserved.
0011  *
0012  * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
0013  *
0014  */
0015 
0016 #include <linux/module.h>
0017 #include <linux/moduleparam.h>
0018 #include <linux/kernel.h>
0019 #include <linux/types.h>
0020 #include <linux/slab.h>
0021 #include <linux/pci.h>
0022 #include "shpchp.h"
0023 
0024 /* Global variables */
0025 bool shpchp_debug;
0026 bool shpchp_poll_mode;
0027 int shpchp_poll_time;
0028 
0029 #define DRIVER_VERSION  "0.4"
0030 #define DRIVER_AUTHOR   "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
0031 #define DRIVER_DESC "Standard Hot Plug PCI Controller Driver"
0032 
0033 MODULE_AUTHOR(DRIVER_AUTHOR);
0034 MODULE_DESCRIPTION(DRIVER_DESC);
0035 MODULE_LICENSE("GPL");
0036 
0037 module_param(shpchp_debug, bool, 0644);
0038 module_param(shpchp_poll_mode, bool, 0644);
0039 module_param(shpchp_poll_time, int, 0644);
0040 MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not");
0041 MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not");
0042 MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds");
0043 
0044 #define SHPC_MODULE_NAME "shpchp"
0045 
0046 static int set_attention_status(struct hotplug_slot *slot, u8 value);
0047 static int enable_slot(struct hotplug_slot *slot);
0048 static int disable_slot(struct hotplug_slot *slot);
0049 static int get_power_status(struct hotplug_slot *slot, u8 *value);
0050 static int get_attention_status(struct hotplug_slot *slot, u8 *value);
0051 static int get_latch_status(struct hotplug_slot *slot, u8 *value);
0052 static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
0053 
0054 static const struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
0055     .set_attention_status = set_attention_status,
0056     .enable_slot =      enable_slot,
0057     .disable_slot =     disable_slot,
0058     .get_power_status = get_power_status,
0059     .get_attention_status = get_attention_status,
0060     .get_latch_status = get_latch_status,
0061     .get_adapter_status =   get_adapter_status,
0062 };
0063 
0064 static int init_slots(struct controller *ctrl)
0065 {
0066     struct slot *slot;
0067     struct hotplug_slot *hotplug_slot;
0068     char name[SLOT_NAME_SIZE];
0069     int retval;
0070     int i;
0071 
0072     for (i = 0; i < ctrl->num_slots; i++) {
0073         slot = kzalloc(sizeof(*slot), GFP_KERNEL);
0074         if (!slot) {
0075             retval = -ENOMEM;
0076             goto error;
0077         }
0078 
0079         hotplug_slot = &slot->hotplug_slot;
0080 
0081         slot->hp_slot = i;
0082         slot->ctrl = ctrl;
0083         slot->bus = ctrl->pci_dev->subordinate->number;
0084         slot->device = ctrl->slot_device_offset + i;
0085         slot->hpc_ops = ctrl->hpc_ops;
0086         slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i);
0087 
0088         slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number);
0089         if (!slot->wq) {
0090             retval = -ENOMEM;
0091             goto error_slot;
0092         }
0093 
0094         mutex_init(&slot->lock);
0095         INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work);
0096 
0097         /* register this slot with the hotplug pci core */
0098         snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
0099         hotplug_slot->ops = &shpchp_hotplug_slot_ops;
0100 
0101         ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x hp_slot=%x sun=%x slot_device_offset=%x\n",
0102              pci_domain_nr(ctrl->pci_dev->subordinate),
0103              slot->bus, slot->device, slot->hp_slot, slot->number,
0104              ctrl->slot_device_offset);
0105         retval = pci_hp_register(hotplug_slot,
0106                 ctrl->pci_dev->subordinate, slot->device, name);
0107         if (retval) {
0108             ctrl_err(ctrl, "pci_hp_register failed with error %d\n",
0109                  retval);
0110             goto error_slotwq;
0111         }
0112 
0113         get_power_status(hotplug_slot, &slot->pwr_save);
0114         get_attention_status(hotplug_slot, &slot->attention_save);
0115         get_latch_status(hotplug_slot, &slot->latch_save);
0116         get_adapter_status(hotplug_slot, &slot->presence_save);
0117 
0118         list_add(&slot->slot_list, &ctrl->slot_list);
0119     }
0120 
0121     return 0;
0122 error_slotwq:
0123     destroy_workqueue(slot->wq);
0124 error_slot:
0125     kfree(slot);
0126 error:
0127     return retval;
0128 }
0129 
0130 void cleanup_slots(struct controller *ctrl)
0131 {
0132     struct slot *slot, *next;
0133 
0134     list_for_each_entry_safe(slot, next, &ctrl->slot_list, slot_list) {
0135         list_del(&slot->slot_list);
0136         cancel_delayed_work(&slot->work);
0137         destroy_workqueue(slot->wq);
0138         pci_hp_deregister(&slot->hotplug_slot);
0139         kfree(slot);
0140     }
0141 }
0142 
0143 /*
0144  * set_attention_status - Turns the Amber LED for a slot on, off or blink
0145  */
0146 static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
0147 {
0148     struct slot *slot = get_slot(hotplug_slot);
0149 
0150     ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
0151          __func__, slot_name(slot));
0152 
0153     slot->attention_save = status;
0154     slot->hpc_ops->set_attention_status(slot, status);
0155 
0156     return 0;
0157 }
0158 
0159 static int enable_slot(struct hotplug_slot *hotplug_slot)
0160 {
0161     struct slot *slot = get_slot(hotplug_slot);
0162 
0163     ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
0164          __func__, slot_name(slot));
0165 
0166     return shpchp_sysfs_enable_slot(slot);
0167 }
0168 
0169 static int disable_slot(struct hotplug_slot *hotplug_slot)
0170 {
0171     struct slot *slot = get_slot(hotplug_slot);
0172 
0173     ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
0174          __func__, slot_name(slot));
0175 
0176     return shpchp_sysfs_disable_slot(slot);
0177 }
0178 
0179 static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
0180 {
0181     struct slot *slot = get_slot(hotplug_slot);
0182     int retval;
0183 
0184     ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
0185          __func__, slot_name(slot));
0186 
0187     retval = slot->hpc_ops->get_power_status(slot, value);
0188     if (retval < 0)
0189         *value = slot->pwr_save;
0190 
0191     return 0;
0192 }
0193 
0194 static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
0195 {
0196     struct slot *slot = get_slot(hotplug_slot);
0197     int retval;
0198 
0199     ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
0200          __func__, slot_name(slot));
0201 
0202     retval = slot->hpc_ops->get_attention_status(slot, value);
0203     if (retval < 0)
0204         *value = slot->attention_save;
0205 
0206     return 0;
0207 }
0208 
0209 static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
0210 {
0211     struct slot *slot = get_slot(hotplug_slot);
0212     int retval;
0213 
0214     ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
0215          __func__, slot_name(slot));
0216 
0217     retval = slot->hpc_ops->get_latch_status(slot, value);
0218     if (retval < 0)
0219         *value = slot->latch_save;
0220 
0221     return 0;
0222 }
0223 
0224 static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
0225 {
0226     struct slot *slot = get_slot(hotplug_slot);
0227     int retval;
0228 
0229     ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
0230          __func__, slot_name(slot));
0231 
0232     retval = slot->hpc_ops->get_adapter_status(slot, value);
0233     if (retval < 0)
0234         *value = slot->presence_save;
0235 
0236     return 0;
0237 }
0238 
0239 static bool shpc_capable(struct pci_dev *bridge)
0240 {
0241     /*
0242      * It is assumed that AMD GOLAM chips support SHPC but they do not
0243      * have SHPC capability.
0244      */
0245     if (bridge->vendor == PCI_VENDOR_ID_AMD &&
0246         bridge->device == PCI_DEVICE_ID_AMD_GOLAM_7450)
0247         return true;
0248 
0249     if (pci_find_capability(bridge, PCI_CAP_ID_SHPC))
0250         return true;
0251 
0252     return false;
0253 }
0254 
0255 static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
0256 {
0257     int rc;
0258     struct controller *ctrl;
0259 
0260     if (!shpc_capable(pdev))
0261         return -ENODEV;
0262 
0263     if (acpi_get_hp_hw_control_from_firmware(pdev))
0264         return -ENODEV;
0265 
0266     ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
0267     if (!ctrl)
0268         goto err_out_none;
0269 
0270     INIT_LIST_HEAD(&ctrl->slot_list);
0271 
0272     rc = shpc_init(ctrl, pdev);
0273     if (rc) {
0274         ctrl_dbg(ctrl, "Controller initialization failed\n");
0275         goto err_out_free_ctrl;
0276     }
0277 
0278     pci_set_drvdata(pdev, ctrl);
0279 
0280     /* Setup the slot information structures */
0281     rc = init_slots(ctrl);
0282     if (rc) {
0283         ctrl_err(ctrl, "Slot initialization failed\n");
0284         goto err_out_release_ctlr;
0285     }
0286 
0287     rc = shpchp_create_ctrl_files(ctrl);
0288     if (rc)
0289         goto err_cleanup_slots;
0290 
0291     pdev->shpc_managed = 1;
0292     return 0;
0293 
0294 err_cleanup_slots:
0295     cleanup_slots(ctrl);
0296 err_out_release_ctlr:
0297     ctrl->hpc_ops->release_ctlr(ctrl);
0298 err_out_free_ctrl:
0299     kfree(ctrl);
0300 err_out_none:
0301     return -ENODEV;
0302 }
0303 
0304 static void shpc_remove(struct pci_dev *dev)
0305 {
0306     struct controller *ctrl = pci_get_drvdata(dev);
0307 
0308     dev->shpc_managed = 0;
0309     shpchp_remove_ctrl_files(ctrl);
0310     ctrl->hpc_ops->release_ctlr(ctrl);
0311     kfree(ctrl);
0312 }
0313 
0314 static const struct pci_device_id shpcd_pci_tbl[] = {
0315     {PCI_DEVICE_CLASS(PCI_CLASS_BRIDGE_PCI_NORMAL, ~0)},
0316     { /* end: all zeroes */ }
0317 };
0318 MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl);
0319 
0320 static struct pci_driver shpc_driver = {
0321     .name =     SHPC_MODULE_NAME,
0322     .id_table = shpcd_pci_tbl,
0323     .probe =    shpc_probe,
0324     .remove =   shpc_remove,
0325 };
0326 
0327 static int __init shpcd_init(void)
0328 {
0329     int retval;
0330 
0331     retval = pci_register_driver(&shpc_driver);
0332     dbg("%s: pci_register_driver = %d\n", __func__, retval);
0333     info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
0334 
0335     return retval;
0336 }
0337 
0338 static void __exit shpcd_cleanup(void)
0339 {
0340     dbg("unload_shpchpd()\n");
0341     pci_unregister_driver(&shpc_driver);
0342     info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
0343 }
0344 
0345 module_init(shpcd_init);
0346 module_exit(shpcd_cleanup);