581 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * PCI HotPlug Controller Core4 *5 * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)6 * Copyright (C) 2001-2002 IBM Corp.7 *8 * All rights reserved.9 *10 * Send feedback to <kristen.c.accardi@intel.com>11 *12 * Authors:13 * Greg Kroah-Hartman <greg@kroah.com>14 * Scott Murray <scottm@somanetworks.com>15 */16 17#include <linux/module.h> /* try_module_get & module_put */18#include <linux/moduleparam.h>19#include <linux/kernel.h>20#include <linux/types.h>21#include <linux/list.h>22#include <linux/kobject.h>23#include <linux/sysfs.h>24#include <linux/pagemap.h>25#include <linux/init.h>26#include <linux/mount.h>27#include <linux/namei.h>28#include <linux/mutex.h>29#include <linux/pci.h>30#include <linux/pci_hotplug.h>31#include <linux/uaccess.h>32#include "../pci.h"33#include "cpci_hotplug.h"34 35#define MY_NAME "pci_hotplug"36 37#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0)38#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg)39#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg)40#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)41 42/* local variables */43static bool debug;44 45static LIST_HEAD(pci_hotplug_slot_list);46static DEFINE_MUTEX(pci_hp_mutex);47 48/* Weee, fun with macros... */49#define GET_STATUS(name, type) \50static int get_##name(struct hotplug_slot *slot, type *value) \51{ \52 const struct hotplug_slot_ops *ops = slot->ops; \53 int retval = 0; \54 if (!try_module_get(slot->owner)) \55 return -ENODEV; \56 if (ops->get_##name) \57 retval = ops->get_##name(slot, value); \58 module_put(slot->owner); \59 return retval; \60}61 62GET_STATUS(power_status, u8)63GET_STATUS(attention_status, u8)64GET_STATUS(latch_status, u8)65GET_STATUS(adapter_status, u8)66 67static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf)68{69 int retval;70 u8 value;71 72 retval = get_power_status(pci_slot->hotplug, &value);73 if (retval)74 return retval;75 76 return sysfs_emit(buf, "%d\n", value);77}78 79static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,80 size_t count)81{82 struct hotplug_slot *slot = pci_slot->hotplug;83 unsigned long lpower;84 u8 power;85 int retval = 0;86 87 lpower = simple_strtoul(buf, NULL, 10);88 power = (u8)(lpower & 0xff);89 dbg("power = %d\n", power);90 91 if (!try_module_get(slot->owner)) {92 retval = -ENODEV;93 goto exit;94 }95 switch (power) {96 case 0:97 if (slot->ops->disable_slot)98 retval = slot->ops->disable_slot(slot);99 break;100 101 case 1:102 if (slot->ops->enable_slot)103 retval = slot->ops->enable_slot(slot);104 break;105 106 default:107 err("Illegal value specified for power\n");108 retval = -EINVAL;109 }110 module_put(slot->owner);111 112exit:113 if (retval)114 return retval;115 return count;116}117 118static struct pci_slot_attribute hotplug_slot_attr_power = {119 .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},120 .show = power_read_file,121 .store = power_write_file122};123 124static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf)125{126 int retval;127 u8 value;128 129 retval = get_attention_status(pci_slot->hotplug, &value);130 if (retval)131 return retval;132 133 return sysfs_emit(buf, "%d\n", value);134}135 136static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,137 size_t count)138{139 struct hotplug_slot *slot = pci_slot->hotplug;140 const struct hotplug_slot_ops *ops = slot->ops;141 unsigned long lattention;142 u8 attention;143 int retval = 0;144 145 lattention = simple_strtoul(buf, NULL, 10);146 attention = (u8)(lattention & 0xff);147 dbg(" - attention = %d\n", attention);148 149 if (!try_module_get(slot->owner)) {150 retval = -ENODEV;151 goto exit;152 }153 if (ops->set_attention_status)154 retval = ops->set_attention_status(slot, attention);155 module_put(slot->owner);156 157exit:158 if (retval)159 return retval;160 return count;161}162 163static struct pci_slot_attribute hotplug_slot_attr_attention = {164 .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},165 .show = attention_read_file,166 .store = attention_write_file167};168 169static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf)170{171 int retval;172 u8 value;173 174 retval = get_latch_status(pci_slot->hotplug, &value);175 if (retval)176 return retval;177 178 return sysfs_emit(buf, "%d\n", value);179}180 181static struct pci_slot_attribute hotplug_slot_attr_latch = {182 .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},183 .show = latch_read_file,184};185 186static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf)187{188 int retval;189 u8 value;190 191 retval = get_adapter_status(pci_slot->hotplug, &value);192 if (retval)193 return retval;194 195 return sysfs_emit(buf, "%d\n", value);196}197 198static struct pci_slot_attribute hotplug_slot_attr_presence = {199 .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},200 .show = presence_read_file,201};202 203static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,204 size_t count)205{206 struct hotplug_slot *slot = pci_slot->hotplug;207 unsigned long ltest;208 u32 test;209 int retval = 0;210 211 ltest = simple_strtoul(buf, NULL, 10);212 test = (u32)(ltest & 0xffffffff);213 dbg("test = %d\n", test);214 215 if (!try_module_get(slot->owner)) {216 retval = -ENODEV;217 goto exit;218 }219 if (slot->ops->hardware_test)220 retval = slot->ops->hardware_test(slot, test);221 module_put(slot->owner);222 223exit:224 if (retval)225 return retval;226 return count;227}228 229static struct pci_slot_attribute hotplug_slot_attr_test = {230 .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},231 .store = test_write_file232};233 234static bool has_power_file(struct pci_slot *pci_slot)235{236 struct hotplug_slot *slot = pci_slot->hotplug;237 238 if ((!slot) || (!slot->ops))239 return false;240 if ((slot->ops->enable_slot) ||241 (slot->ops->disable_slot) ||242 (slot->ops->get_power_status))243 return true;244 return false;245}246 247static bool has_attention_file(struct pci_slot *pci_slot)248{249 struct hotplug_slot *slot = pci_slot->hotplug;250 251 if ((!slot) || (!slot->ops))252 return false;253 if ((slot->ops->set_attention_status) ||254 (slot->ops->get_attention_status))255 return true;256 return false;257}258 259static bool has_latch_file(struct pci_slot *pci_slot)260{261 struct hotplug_slot *slot = pci_slot->hotplug;262 263 if ((!slot) || (!slot->ops))264 return false;265 if (slot->ops->get_latch_status)266 return true;267 return false;268}269 270static bool has_adapter_file(struct pci_slot *pci_slot)271{272 struct hotplug_slot *slot = pci_slot->hotplug;273 274 if ((!slot) || (!slot->ops))275 return false;276 if (slot->ops->get_adapter_status)277 return true;278 return false;279}280 281static bool has_test_file(struct pci_slot *pci_slot)282{283 struct hotplug_slot *slot = pci_slot->hotplug;284 285 if ((!slot) || (!slot->ops))286 return false;287 if (slot->ops->hardware_test)288 return true;289 return false;290}291 292static int fs_add_slot(struct pci_slot *pci_slot)293{294 int retval = 0;295 296 /* Create symbolic link to the hotplug driver module */297 pci_hp_create_module_link(pci_slot);298 299 if (has_power_file(pci_slot)) {300 retval = sysfs_create_file(&pci_slot->kobj,301 &hotplug_slot_attr_power.attr);302 if (retval)303 goto exit_power;304 }305 306 if (has_attention_file(pci_slot)) {307 retval = sysfs_create_file(&pci_slot->kobj,308 &hotplug_slot_attr_attention.attr);309 if (retval)310 goto exit_attention;311 }312 313 if (has_latch_file(pci_slot)) {314 retval = sysfs_create_file(&pci_slot->kobj,315 &hotplug_slot_attr_latch.attr);316 if (retval)317 goto exit_latch;318 }319 320 if (has_adapter_file(pci_slot)) {321 retval = sysfs_create_file(&pci_slot->kobj,322 &hotplug_slot_attr_presence.attr);323 if (retval)324 goto exit_adapter;325 }326 327 if (has_test_file(pci_slot)) {328 retval = sysfs_create_file(&pci_slot->kobj,329 &hotplug_slot_attr_test.attr);330 if (retval)331 goto exit_test;332 }333 334 goto exit;335 336exit_test:337 if (has_adapter_file(pci_slot))338 sysfs_remove_file(&pci_slot->kobj,339 &hotplug_slot_attr_presence.attr);340exit_adapter:341 if (has_latch_file(pci_slot))342 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);343exit_latch:344 if (has_attention_file(pci_slot))345 sysfs_remove_file(&pci_slot->kobj,346 &hotplug_slot_attr_attention.attr);347exit_attention:348 if (has_power_file(pci_slot))349 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);350exit_power:351 pci_hp_remove_module_link(pci_slot);352exit:353 return retval;354}355 356static void fs_remove_slot(struct pci_slot *pci_slot)357{358 if (has_power_file(pci_slot))359 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);360 361 if (has_attention_file(pci_slot))362 sysfs_remove_file(&pci_slot->kobj,363 &hotplug_slot_attr_attention.attr);364 365 if (has_latch_file(pci_slot))366 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);367 368 if (has_adapter_file(pci_slot))369 sysfs_remove_file(&pci_slot->kobj,370 &hotplug_slot_attr_presence.attr);371 372 if (has_test_file(pci_slot))373 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr);374 375 pci_hp_remove_module_link(pci_slot);376}377 378static struct hotplug_slot *get_slot_from_name(const char *name)379{380 struct hotplug_slot *slot;381 382 list_for_each_entry(slot, &pci_hotplug_slot_list, slot_list) {383 if (strcmp(hotplug_slot_name(slot), name) == 0)384 return slot;385 }386 return NULL;387}388 389/**390 * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem391 * @bus: bus this slot is on392 * @slot: pointer to the &struct hotplug_slot to register393 * @devnr: device number394 * @name: name registered with kobject core395 * @owner: caller module owner396 * @mod_name: caller module name397 *398 * Prepares a hotplug slot for in-kernel use and immediately publishes it to399 * user space in one go. Drivers may alternatively carry out the two steps400 * separately by invoking pci_hp_initialize() and pci_hp_add().401 *402 * Returns 0 if successful, anything else for an error.403 */404int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,405 int devnr, const char *name,406 struct module *owner, const char *mod_name)407{408 int result;409 410 result = __pci_hp_initialize(slot, bus, devnr, name, owner, mod_name);411 if (result)412 return result;413 414 result = pci_hp_add(slot);415 if (result)416 pci_hp_destroy(slot);417 418 return result;419}420EXPORT_SYMBOL_GPL(__pci_hp_register);421 422/**423 * __pci_hp_initialize - prepare hotplug slot for in-kernel use424 * @slot: pointer to the &struct hotplug_slot to initialize425 * @bus: bus this slot is on426 * @devnr: slot number427 * @name: name registered with kobject core428 * @owner: caller module owner429 * @mod_name: caller module name430 *431 * Allocate and fill in a PCI slot for use by a hotplug driver. Once this has432 * been called, the driver may invoke hotplug_slot_name() to get the slot's433 * unique name. The driver must be prepared to handle a ->reset_slot callback434 * from this point on.435 *436 * Returns 0 on success or a negative int on error.437 */438int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus,439 int devnr, const char *name, struct module *owner,440 const char *mod_name)441{442 struct pci_slot *pci_slot;443 444 if (slot == NULL)445 return -ENODEV;446 if (slot->ops == NULL)447 return -EINVAL;448 449 slot->owner = owner;450 slot->mod_name = mod_name;451 452 /*453 * No problems if we call this interface from both ACPI_PCI_SLOT454 * driver and call it here again. If we've already created the455 * pci_slot, the interface will simply bump the refcount.456 */457 pci_slot = pci_create_slot(bus, devnr, name, slot);458 if (IS_ERR(pci_slot))459 return PTR_ERR(pci_slot);460 461 slot->pci_slot = pci_slot;462 pci_slot->hotplug = slot;463 return 0;464}465EXPORT_SYMBOL_GPL(__pci_hp_initialize);466 467/**468 * pci_hp_add - publish hotplug slot to user space469 * @slot: pointer to the &struct hotplug_slot to publish470 *471 * Make a hotplug slot's sysfs interface available and inform user space of its472 * addition by sending a uevent. The hotplug driver must be prepared to handle473 * all &struct hotplug_slot_ops callbacks from this point on.474 *475 * Returns 0 on success or a negative int on error.476 */477int pci_hp_add(struct hotplug_slot *slot)478{479 struct pci_slot *pci_slot = slot->pci_slot;480 int result;481 482 result = fs_add_slot(pci_slot);483 if (result)484 return result;485 486 kobject_uevent(&pci_slot->kobj, KOBJ_ADD);487 mutex_lock(&pci_hp_mutex);488 list_add(&slot->slot_list, &pci_hotplug_slot_list);489 mutex_unlock(&pci_hp_mutex);490 dbg("Added slot %s to the list\n", hotplug_slot_name(slot));491 return 0;492}493EXPORT_SYMBOL_GPL(pci_hp_add);494 495/**496 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem497 * @slot: pointer to the &struct hotplug_slot to deregister498 *499 * The @slot must have been registered with the pci hotplug subsystem500 * previously with a call to pci_hp_register().501 *502 * Returns 0 if successful, anything else for an error.503 */504void pci_hp_deregister(struct hotplug_slot *slot)505{506 pci_hp_del(slot);507 pci_hp_destroy(slot);508}509EXPORT_SYMBOL_GPL(pci_hp_deregister);510 511/**512 * pci_hp_del - unpublish hotplug slot from user space513 * @slot: pointer to the &struct hotplug_slot to unpublish514 *515 * Remove a hotplug slot's sysfs interface.516 *517 * Returns 0 on success or a negative int on error.518 */519void pci_hp_del(struct hotplug_slot *slot)520{521 struct hotplug_slot *temp;522 523 if (WARN_ON(!slot))524 return;525 526 mutex_lock(&pci_hp_mutex);527 temp = get_slot_from_name(hotplug_slot_name(slot));528 if (WARN_ON(temp != slot)) {529 mutex_unlock(&pci_hp_mutex);530 return;531 }532 533 list_del(&slot->slot_list);534 mutex_unlock(&pci_hp_mutex);535 dbg("Removed slot %s from the list\n", hotplug_slot_name(slot));536 fs_remove_slot(slot->pci_slot);537}538EXPORT_SYMBOL_GPL(pci_hp_del);539 540/**541 * pci_hp_destroy - remove hotplug slot from in-kernel use542 * @slot: pointer to the &struct hotplug_slot to destroy543 *544 * Destroy a PCI slot used by a hotplug driver. Once this has been called,545 * the driver may no longer invoke hotplug_slot_name() to get the slot's546 * unique name. The driver no longer needs to handle a ->reset_slot callback547 * from this point on.548 *549 * Returns 0 on success or a negative int on error.550 */551void pci_hp_destroy(struct hotplug_slot *slot)552{553 struct pci_slot *pci_slot = slot->pci_slot;554 555 slot->pci_slot = NULL;556 pci_slot->hotplug = NULL;557 pci_destroy_slot(pci_slot);558}559EXPORT_SYMBOL_GPL(pci_hp_destroy);560 561static int __init pci_hotplug_init(void)562{563 int result;564 565 result = cpci_hotplug_init(debug);566 if (result) {567 err("cpci_hotplug_init with error %d\n", result);568 return result;569 }570 571 return result;572}573device_initcall(pci_hotplug_init);574 575/*576 * not really modular, but the easiest way to keep compat with existing577 * bootargs behaviour is to continue using module_param here.578 */579module_param(debug, bool, 0644);580MODULE_PARM_DESC(debug, "Debugging mode enabled or not");581