root/drivers/pci/hotplug/shpchp_core.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. init_slots
  2. cleanup_slots
  3. set_attention_status
  4. enable_slot
  5. disable_slot
  6. get_power_status
  7. get_attention_status
  8. get_latch_status
  9. get_adapter_status
  10. shpc_capable
  11. shpc_probe
  12. shpc_remove
  13. shpcd_init
  14. shpcd_cleanup

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Standard 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) 2003-2004 Intel Corporation
   9  *
  10  * All rights reserved.
  11  *
  12  * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
  13  *
  14  */
  15 
  16 #include <linux/module.h>
  17 #include <linux/moduleparam.h>
  18 #include <linux/kernel.h>
  19 #include <linux/types.h>
  20 #include <linux/slab.h>
  21 #include <linux/pci.h>
  22 #include "shpchp.h"
  23 
  24 /* Global variables */
  25 bool shpchp_debug;
  26 bool shpchp_poll_mode;
  27 int shpchp_poll_time;
  28 
  29 #define DRIVER_VERSION  "0.4"
  30 #define DRIVER_AUTHOR   "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
  31 #define DRIVER_DESC     "Standard Hot Plug PCI Controller Driver"
  32 
  33 MODULE_AUTHOR(DRIVER_AUTHOR);
  34 MODULE_DESCRIPTION(DRIVER_DESC);
  35 MODULE_LICENSE("GPL");
  36 
  37 module_param(shpchp_debug, bool, 0644);
  38 module_param(shpchp_poll_mode, bool, 0644);
  39 module_param(shpchp_poll_time, int, 0644);
  40 MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not");
  41 MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not");
  42 MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds");
  43 
  44 #define SHPC_MODULE_NAME "shpchp"
  45 
  46 static int set_attention_status(struct hotplug_slot *slot, u8 value);
  47 static int enable_slot(struct hotplug_slot *slot);
  48 static int disable_slot(struct hotplug_slot *slot);
  49 static int get_power_status(struct hotplug_slot *slot, u8 *value);
  50 static int get_attention_status(struct hotplug_slot *slot, u8 *value);
  51 static int get_latch_status(struct hotplug_slot *slot, u8 *value);
  52 static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
  53 
  54 static const struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
  55         .set_attention_status = set_attention_status,
  56         .enable_slot =          enable_slot,
  57         .disable_slot =         disable_slot,
  58         .get_power_status =     get_power_status,
  59         .get_attention_status = get_attention_status,
  60         .get_latch_status =     get_latch_status,
  61         .get_adapter_status =   get_adapter_status,
  62 };
  63 
  64 static int init_slots(struct controller *ctrl)
  65 {
  66         struct slot *slot;
  67         struct hotplug_slot *hotplug_slot;
  68         char name[SLOT_NAME_SIZE];
  69         int retval;
  70         int i;
  71 
  72         for (i = 0; i < ctrl->num_slots; i++) {
  73                 slot = kzalloc(sizeof(*slot), GFP_KERNEL);
  74                 if (!slot) {
  75                         retval = -ENOMEM;
  76                         goto error;
  77                 }
  78 
  79                 hotplug_slot = &slot->hotplug_slot;
  80 
  81                 slot->hp_slot = i;
  82                 slot->ctrl = ctrl;
  83                 slot->bus = ctrl->pci_dev->subordinate->number;
  84                 slot->device = ctrl->slot_device_offset + i;
  85                 slot->hpc_ops = ctrl->hpc_ops;
  86                 slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i);
  87 
  88                 slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number);
  89                 if (!slot->wq) {
  90                         retval = -ENOMEM;
  91                         goto error_slot;
  92                 }
  93 
  94                 mutex_init(&slot->lock);
  95                 INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work);
  96 
  97                 /* register this slot with the hotplug pci core */
  98                 snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
  99                 hotplug_slot->ops = &shpchp_hotplug_slot_ops;
 100 
 101                 ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x hp_slot=%x sun=%x slot_device_offset=%x\n",
 102                          pci_domain_nr(ctrl->pci_dev->subordinate),
 103                          slot->bus, slot->device, slot->hp_slot, slot->number,
 104                          ctrl->slot_device_offset);
 105                 retval = pci_hp_register(hotplug_slot,
 106                                 ctrl->pci_dev->subordinate, slot->device, name);
 107                 if (retval) {
 108                         ctrl_err(ctrl, "pci_hp_register failed with error %d\n",
 109                                  retval);
 110                         goto error_slotwq;
 111                 }
 112 
 113                 get_power_status(hotplug_slot, &slot->pwr_save);
 114                 get_attention_status(hotplug_slot, &slot->attention_save);
 115                 get_latch_status(hotplug_slot, &slot->latch_save);
 116                 get_adapter_status(hotplug_slot, &slot->presence_save);
 117 
 118                 list_add(&slot->slot_list, &ctrl->slot_list);
 119         }
 120 
 121         return 0;
 122 error_slotwq:
 123         destroy_workqueue(slot->wq);
 124 error_slot:
 125         kfree(slot);
 126 error:
 127         return retval;
 128 }
 129 
 130 void cleanup_slots(struct controller *ctrl)
 131 {
 132         struct slot *slot, *next;
 133 
 134         list_for_each_entry_safe(slot, next, &ctrl->slot_list, slot_list) {
 135                 list_del(&slot->slot_list);
 136                 cancel_delayed_work(&slot->work);
 137                 destroy_workqueue(slot->wq);
 138                 pci_hp_deregister(&slot->hotplug_slot);
 139                 kfree(slot);
 140         }
 141 }
 142 
 143 /*
 144  * set_attention_status - Turns the Amber LED for a slot on, off or blink
 145  */
 146 static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
 147 {
 148         struct slot *slot = get_slot(hotplug_slot);
 149 
 150         ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 151                  __func__, slot_name(slot));
 152 
 153         slot->attention_save = status;
 154         slot->hpc_ops->set_attention_status(slot, status);
 155 
 156         return 0;
 157 }
 158 
 159 static int enable_slot(struct hotplug_slot *hotplug_slot)
 160 {
 161         struct slot *slot = get_slot(hotplug_slot);
 162 
 163         ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 164                  __func__, slot_name(slot));
 165 
 166         return shpchp_sysfs_enable_slot(slot);
 167 }
 168 
 169 static int disable_slot(struct hotplug_slot *hotplug_slot)
 170 {
 171         struct slot *slot = get_slot(hotplug_slot);
 172 
 173         ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 174                  __func__, slot_name(slot));
 175 
 176         return shpchp_sysfs_disable_slot(slot);
 177 }
 178 
 179 static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 180 {
 181         struct slot *slot = get_slot(hotplug_slot);
 182         int retval;
 183 
 184         ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 185                  __func__, slot_name(slot));
 186 
 187         retval = slot->hpc_ops->get_power_status(slot, value);
 188         if (retval < 0)
 189                 *value = slot->pwr_save;
 190 
 191         return 0;
 192 }
 193 
 194 static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 195 {
 196         struct slot *slot = get_slot(hotplug_slot);
 197         int retval;
 198 
 199         ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 200                  __func__, slot_name(slot));
 201 
 202         retval = slot->hpc_ops->get_attention_status(slot, value);
 203         if (retval < 0)
 204                 *value = slot->attention_save;
 205 
 206         return 0;
 207 }
 208 
 209 static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 210 {
 211         struct slot *slot = get_slot(hotplug_slot);
 212         int retval;
 213 
 214         ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 215                  __func__, slot_name(slot));
 216 
 217         retval = slot->hpc_ops->get_latch_status(slot, value);
 218         if (retval < 0)
 219                 *value = slot->latch_save;
 220 
 221         return 0;
 222 }
 223 
 224 static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 225 {
 226         struct slot *slot = get_slot(hotplug_slot);
 227         int retval;
 228 
 229         ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 230                  __func__, slot_name(slot));
 231 
 232         retval = slot->hpc_ops->get_adapter_status(slot, value);
 233         if (retval < 0)
 234                 *value = slot->presence_save;
 235 
 236         return 0;
 237 }
 238 
 239 static bool shpc_capable(struct pci_dev *bridge)
 240 {
 241         /*
 242          * It is assumed that AMD GOLAM chips support SHPC but they do not
 243          * have SHPC capability.
 244          */
 245         if (bridge->vendor == PCI_VENDOR_ID_AMD &&
 246             bridge->device == PCI_DEVICE_ID_AMD_GOLAM_7450)
 247                 return true;
 248 
 249         if (pci_find_capability(bridge, PCI_CAP_ID_SHPC))
 250                 return true;
 251 
 252         return false;
 253 }
 254 
 255 static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 256 {
 257         int rc;
 258         struct controller *ctrl;
 259 
 260         if (!shpc_capable(pdev))
 261                 return -ENODEV;
 262 
 263         if (acpi_get_hp_hw_control_from_firmware(pdev))
 264                 return -ENODEV;
 265 
 266         ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
 267         if (!ctrl)
 268                 goto err_out_none;
 269 
 270         INIT_LIST_HEAD(&ctrl->slot_list);
 271 
 272         rc = shpc_init(ctrl, pdev);
 273         if (rc) {
 274                 ctrl_dbg(ctrl, "Controller initialization failed\n");
 275                 goto err_out_free_ctrl;
 276         }
 277 
 278         pci_set_drvdata(pdev, ctrl);
 279 
 280         /* Setup the slot information structures */
 281         rc = init_slots(ctrl);
 282         if (rc) {
 283                 ctrl_err(ctrl, "Slot initialization failed\n");
 284                 goto err_out_release_ctlr;
 285         }
 286 
 287         rc = shpchp_create_ctrl_files(ctrl);
 288         if (rc)
 289                 goto err_cleanup_slots;
 290 
 291         pdev->shpc_managed = 1;
 292         return 0;
 293 
 294 err_cleanup_slots:
 295         cleanup_slots(ctrl);
 296 err_out_release_ctlr:
 297         ctrl->hpc_ops->release_ctlr(ctrl);
 298 err_out_free_ctrl:
 299         kfree(ctrl);
 300 err_out_none:
 301         return -ENODEV;
 302 }
 303 
 304 static void shpc_remove(struct pci_dev *dev)
 305 {
 306         struct controller *ctrl = pci_get_drvdata(dev);
 307 
 308         dev->shpc_managed = 0;
 309         shpchp_remove_ctrl_files(ctrl);
 310         ctrl->hpc_ops->release_ctlr(ctrl);
 311         kfree(ctrl);
 312 }
 313 
 314 static const struct pci_device_id shpcd_pci_tbl[] = {
 315         {PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0)},
 316         { /* end: all zeroes */ }
 317 };
 318 MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl);
 319 
 320 static struct pci_driver shpc_driver = {
 321         .name =         SHPC_MODULE_NAME,
 322         .id_table =     shpcd_pci_tbl,
 323         .probe =        shpc_probe,
 324         .remove =       shpc_remove,
 325 };
 326 
 327 static int __init shpcd_init(void)
 328 {
 329         int retval;
 330 
 331         retval = pci_register_driver(&shpc_driver);
 332         dbg("%s: pci_register_driver = %d\n", __func__, retval);
 333         info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
 334 
 335         return retval;
 336 }
 337 
 338 static void __exit shpcd_cleanup(void)
 339 {
 340         dbg("unload_shpchpd()\n");
 341         pci_unregister_driver(&shpc_driver);
 342         info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
 343 }
 344 
 345 module_init(shpcd_init);
 346 module_exit(shpcd_cleanup);

/* [<][>][^][v][top][bottom][index][help] */