diff options
Diffstat (limited to 'drivers/pci/hotplug')
| -rw-r--r-- | drivers/pci/hotplug/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/pci/hotplug/Makefile | 1 | ||||
| -rw-r--r-- | drivers/pci/hotplug/cpcihp_zt5550.c | 4 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pciehp.h | 2 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 11 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pciehp_ctrl.c | 8 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 11 | ||||
| -rw-r--r-- | drivers/pci/hotplug/s390_pci_hpc.c | 252 | ||||
| -rw-r--r-- | drivers/pci/hotplug/shpchp.h | 3 | ||||
| -rw-r--r-- | drivers/pci/hotplug/shpchp_core.c | 36 | ||||
| -rw-r--r-- | drivers/pci/hotplug/shpchp_ctrl.c | 6 |
11 files changed, 301 insertions, 44 deletions
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index b0e46dede1a9..13e9e63a7266 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -151,4 +151,15 @@ config HOTPLUG_PCI_SGI When in doubt, say N. +config HOTPLUG_PCI_S390 + tristate "System z PCI Hotplug Support" + depends on S390 && 64BIT + help + Say Y here if you want to use the System z PCI Hotplug + driver for PCI devices. Without this driver it is not + possible to access stand-by PCI functions nor to deconfigure + PCI functions. + + When in doubt, say Y. + endif # HOTPLUG_PCI diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index c459cd4e39c2..47ec8c80e16d 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o +obj-$(CONFIG_HOTPLUG_PCI_S390) += s390_pci_hpc.o # acpiphp_ibm extends acpiphp, so should be linked afterwards. diff --git a/drivers/pci/hotplug/cpcihp_zt5550.c b/drivers/pci/hotplug/cpcihp_zt5550.c index 6bf8d2ab164f..449b4bbc8301 100644 --- a/drivers/pci/hotplug/cpcihp_zt5550.c +++ b/drivers/pci/hotplug/cpcihp_zt5550.c @@ -271,7 +271,7 @@ init_hc_error: } -static void __devexit zt5550_hc_remove_one(struct pci_dev *pdev) +static void zt5550_hc_remove_one(struct pci_dev *pdev) { cpci_hp_stop(); cpci_hp_unregister_bus(bus0); @@ -290,7 +290,7 @@ static struct pci_driver zt5550_hc_driver = { .name = "zt5550_hc", .id_table = zt5550_hc_pci_tbl, .probe = zt5550_hc_init_one, - .remove = __devexit_p(zt5550_hc_remove_one), + .remove = zt5550_hc_remove_one, }; static int __init zt5550_init(void) diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 26ffd3e3fb74..2c113de94323 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -44,7 +44,6 @@ extern bool pciehp_poll_mode; extern int pciehp_poll_time; extern bool pciehp_debug; extern bool pciehp_force; -extern struct workqueue_struct *pciehp_wq; #define dbg(format, arg...) \ do { \ @@ -78,6 +77,7 @@ struct slot { struct hotplug_slot *hotplug_slot; struct delayed_work work; /* work for button event */ struct mutex lock; + struct workqueue_struct *wq; }; struct event_info { diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 916bf4f53aba..939bd1d4b5b1 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -42,7 +42,6 @@ bool pciehp_debug; bool pciehp_poll_mode; int pciehp_poll_time; bool pciehp_force; -struct workqueue_struct *pciehp_wq; #define DRIVER_VERSION "0.4" #define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>" @@ -340,18 +339,13 @@ static int __init pcied_init(void) { int retval = 0; - pciehp_wq = alloc_workqueue("pciehp", 0, 0); - if (!pciehp_wq) - return -ENOMEM; - pciehp_firmware_init(); retval = pcie_port_service_register(&hpdriver_portdrv); dbg("pcie_port_service_register = %d\n", retval); info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - if (retval) { - destroy_workqueue(pciehp_wq); + if (retval) dbg("Failure to register service\n"); - } + return retval; } @@ -359,7 +353,6 @@ static void __exit pcied_cleanup(void) { dbg("unload_pciehpd()\n"); pcie_port_service_unregister(&hpdriver_portdrv); - destroy_workqueue(pciehp_wq); info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); } diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 27f44295a657..38f018679175 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -49,7 +49,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type) info->p_slot = p_slot; INIT_WORK(&info->work, interrupt_event_handler); - queue_work(pciehp_wq, &info->work); + queue_work(p_slot->wq, &info->work); return 0; } @@ -344,7 +344,7 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) kfree(info); goto out; } - queue_work(pciehp_wq, &info->work); + queue_work(p_slot->wq, &info->work); out: mutex_unlock(&p_slot->lock); } @@ -377,7 +377,7 @@ static void handle_button_press_event(struct slot *p_slot) if (ATTN_LED(ctrl)) pciehp_set_attention_status(p_slot, 0); - queue_delayed_work(pciehp_wq, &p_slot->work, 5*HZ); + queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); break; case BLINKINGOFF_STATE: case BLINKINGON_STATE: @@ -439,7 +439,7 @@ static void handle_surprise_event(struct slot *p_slot) else p_slot->state = POWERON_STATE; - queue_work(pciehp_wq, &info->work); + queue_work(p_slot->wq, &info->work); } static void interrupt_event_handler(struct work_struct *work) diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 13b2eaf7ba43..5127f3f41821 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -773,23 +773,32 @@ static void pcie_shutdown_notification(struct controller *ctrl) static int pcie_init_slot(struct controller *ctrl) { struct slot *slot; + char name[32]; slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) return -ENOMEM; + snprintf(name, sizeof(name), "pciehp-%u", PSN(ctrl)); + slot->wq = alloc_workqueue(name, 0, 0); + if (!slot->wq) + goto abort; + slot->ctrl = ctrl; mutex_init(&slot->lock); INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); ctrl->slot = slot; return 0; +abort: + kfree(slot); + return -ENOMEM; } static void pcie_cleanup_slot(struct controller *ctrl) { struct slot *slot = ctrl->slot; cancel_delayed_work(&slot->work); - flush_workqueue(pciehp_wq); + destroy_workqueue(slot->wq); kfree(slot); } diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c new file mode 100644 index 000000000000..dee68e0698e1 --- /dev/null +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -0,0 +1,252 @@ +/* + * PCI Hot Plug Controller Driver for System z + * + * Copyright 2012 IBM Corp. + * + * Author(s): + * Jan Glauber <jang@linux.vnet.ibm.com> + */ + +#define COMPONENT "zPCI hpc" +#define pr_fmt(fmt) COMPONENT ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/pci_hotplug.h> +#include <linux/init.h> +#include <asm/sclp.h> + +#define SLOT_NAME_SIZE 10 +static LIST_HEAD(s390_hotplug_slot_list); + +MODULE_AUTHOR("Jan Glauber <jang@linux.vnet.ibm.com"); +MODULE_DESCRIPTION("Hot Plug PCI Controller for System z"); +MODULE_LICENSE("GPL"); + +static int zpci_fn_configured(enum zpci_state state) +{ + return state == ZPCI_FN_STATE_CONFIGURED || + state == ZPCI_FN_STATE_ONLINE; +} + +/* + * struct slot - slot information for each *physical* slot + */ +struct slot { + struct list_head slot_list; + struct hotplug_slot *hotplug_slot; + struct zpci_dev *zdev; +}; + +static int enable_slot(struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = hotplug_slot->private; + int rc; + + if (slot->zdev->state != ZPCI_FN_STATE_STANDBY) + return -EIO; + + rc = sclp_pci_configure(slot->zdev->fid); + if (!rc) { + slot->zdev->state = ZPCI_FN_STATE_CONFIGURED; + /* automatically scan the device after is was configured */ + zpci_enable_device(slot->zdev); + zpci_scan_device(slot->zdev); + } + return rc; +} + +static int disable_slot(struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = hotplug_slot->private; + int rc; + + if (!zpci_fn_configured(slot->zdev->state)) + return -EIO; + + /* TODO: we rely on the user to unbind/remove the device, is that plausible + * or do we need to trigger that here? + */ + rc = sclp_pci_deconfigure(slot->zdev->fid); + if (!rc) { + /* Fixme: better call List-PCI to find the disabled FH + for the FID since the FH should be opaque... */ + slot->zdev->fh &= 0x7fffffff; + slot->zdev->state = ZPCI_FN_STATE_STANDBY; + } + return rc; +} + +static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = hotplug_slot->private; + + switch (slot->zdev->state) { + case ZPCI_FN_STATE_STANDBY: + *value = 0; + break; + default: + *value = 1; + break; + } + return 0; +} + +static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) +{ + /* if the slot exits it always contains a function */ + *value = 1; + return 0; +} + +static void release_slot(struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = hotplug_slot->private; + + pr_debug("%s - physical_slot = %s\n", __func__, hotplug_slot_name(hotplug_slot)); + kfree(slot->hotplug_slot->info); + kfree(slot->hotplug_slot); + kfree(slot); +} + +static struct hotplug_slot_ops s390_hotplug_slot_ops = { + .enable_slot = enable_slot, + .disable_slot = disable_slot, + .get_power_status = get_power_status, + .get_adapter_status = get_adapter_status, +}; + +static int init_pci_slot(struct zpci_dev *zdev) +{ + struct hotplug_slot *hotplug_slot; + struct hotplug_slot_info *info; + char name[SLOT_NAME_SIZE]; + struct slot *slot; + int rc; + + if (!zdev) + return 0; + + slot = kzalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) + goto error; + + hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL); + if (!hotplug_slot) + goto error_hp; + hotplug_slot->private = slot; + + slot->hotplug_slot = hotplug_slot; + slot->zdev = zdev; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + goto error_info; + hotplug_slot->info = info; + + hotplug_slot->ops = &s390_hotplug_slot_ops; + hotplug_slot->release = &release_slot; + + get_power_status(hotplug_slot, &info->power_status); + get_adapter_status(hotplug_slot, &info->adapter_status); + + snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid); + rc = pci_hp_register(slot->hotplug_slot, zdev->bus, + ZPCI_DEVFN, name); + if (rc) { + pr_err("pci_hp_register failed with error %d\n", rc); + goto error_reg; + } + list_add(&slot->slot_list, &s390_hotplug_slot_list); + return 0; + +error_reg: + kfree(info); +error_info: + kfree(hotplug_slot); +error_hp: + kfree(slot); +error: + return -ENOMEM; +} + +static int __init init_pci_slots(void) +{ + struct zpci_dev *zdev; + int device = 0; + + /* + * Create a structure for each slot, and register that slot + * with the pci_hotplug subsystem. + */ + mutex_lock(&zpci_list_lock); + list_for_each_entry(zdev, &zpci_list, entry) { + init_pci_slot(zdev); + device++; + } + + mutex_unlock(&zpci_list_lock); + return (device) ? 0 : -ENODEV; +} + +static void exit_pci_slot(struct zpci_dev *zdev) +{ + struct list_head *tmp, *n; + struct slot *slot; + + list_for_each_safe(tmp, n, &s390_hotplug_slot_list) { + slot = list_entry(tmp, struct slot, slot_list); + if (slot->zdev != zdev) + continue; + list_del(&slot->slot_list); + pci_hp_deregister(slot->hotplug_slot); + } +} + +static void __exit exit_pci_slots(void) +{ + struct list_head *tmp, *n; + struct slot *slot; + + /* + * Unregister all of our slots with the pci_hotplug subsystem. + * Memory will be freed in release_slot() callback after slot's + * lifespan is finished. + */ + list_for_each_safe(tmp, n, &s390_hotplug_slot_list) { + slot = list_entry(tmp, struct slot, slot_list); + list_del(&slot->slot_list); + pci_hp_deregister(slot->hotplug_slot); + } +} + +static int __init pci_hotplug_s390_init(void) +{ + /* + * Do specific initialization stuff for your driver here + * like initializing your controller hardware (if any) and + * determining the number of slots you have in the system + * right now. + */ + + if (!pci_probe) + return -EOPNOTSUPP; + + /* register callbacks for slot handling from arch code */ + mutex_lock(&zpci_list_lock); + hotplug_ops.create_slot = init_pci_slot; + hotplug_ops.remove_slot = exit_pci_slot; + mutex_unlock(&zpci_list_lock); + pr_info("registered hotplug slot callbacks\n"); + return init_pci_slots(); +} + +static void __exit pci_hotplug_s390_exit(void) +{ + exit_pci_slots(); +} + +module_init(pci_hotplug_s390_init); +module_exit(pci_hotplug_s390_exit); diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index ca64932e658b..b849f995075a 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -46,8 +46,6 @@ extern bool shpchp_poll_mode; extern int shpchp_poll_time; extern bool shpchp_debug; -extern struct workqueue_struct *shpchp_wq; -extern struct workqueue_struct *shpchp_ordered_wq; #define dbg(format, arg...) \ do { \ @@ -91,6 +89,7 @@ struct slot { struct list_head slot_list; struct delayed_work work; /* work for button event */ struct mutex lock; + struct workqueue_struct *wq; u8 hp_slot; }; diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index b6de307248e4..3100c52c837c 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -39,8 +39,6 @@ bool shpchp_debug; bool shpchp_poll_mode; int shpchp_poll_time; -struct workqueue_struct *shpchp_wq; -struct workqueue_struct *shpchp_ordered_wq; #define DRIVER_VERSION "0.4" #define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>" @@ -129,6 +127,14 @@ static int init_slots(struct controller *ctrl) slot->device = ctrl->slot_device_offset + i; slot->hpc_ops = ctrl->hpc_ops; slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i); + + snprintf(name, sizeof(name), "shpchp-%d", slot->number); + slot->wq = alloc_workqueue(name, 0, 0); + if (!slot->wq) { + retval = -ENOMEM; + goto error_info; + } + mutex_init(&slot->lock); INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work); @@ -148,7 +154,7 @@ static int init_slots(struct controller *ctrl) if (retval) { ctrl_err(ctrl, "pci_hp_register failed with error %d\n", retval); - goto error_info; + goto error_slotwq; } get_power_status(hotplug_slot, &info->power_status); @@ -160,6 +166,8 @@ static int init_slots(struct controller *ctrl) } return 0; +error_slotwq: + destroy_workqueue(slot->wq); error_info: kfree(info); error_hpslot: @@ -180,8 +188,7 @@ void cleanup_slots(struct controller *ctrl) slot = list_entry(tmp, struct slot, slot_list); list_del(&slot->slot_list); cancel_delayed_work(&slot->work); - flush_workqueue(shpchp_wq); - flush_workqueue(shpchp_ordered_wq); + destroy_workqueue(slot->wq); pci_hp_deregister(slot->hotplug_slot); } } @@ -364,25 +371,12 @@ static struct pci_driver shpc_driver = { static int __init shpcd_init(void) { - int retval = 0; - - shpchp_wq = alloc_ordered_workqueue("shpchp", 0); - if (!shpchp_wq) - return -ENOMEM; - - shpchp_ordered_wq = alloc_ordered_workqueue("shpchp_ordered", 0); - if (!shpchp_ordered_wq) { - destroy_workqueue(shpchp_wq); - return -ENOMEM; - } + int retval; retval = pci_register_driver(&shpc_driver); dbg("%s: pci_register_driver = %d\n", __func__, retval); info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - if (retval) { - destroy_workqueue(shpchp_ordered_wq); - destroy_workqueue(shpchp_wq); - } + return retval; } @@ -390,8 +384,6 @@ static void __exit shpcd_cleanup(void) { dbg("unload_shpchpd()\n"); pci_unregister_driver(&shpc_driver); - destroy_workqueue(shpchp_ordered_wq); - destroy_workqueue(shpchp_wq); info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); } diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c index f9b5a52e4115..58499277903a 100644 --- a/drivers/pci/hotplug/shpchp_ctrl.c +++ b/drivers/pci/hotplug/shpchp_ctrl.c @@ -51,7 +51,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type) info->p_slot = p_slot; INIT_WORK(&info->work, interrupt_event_handler); - queue_work(shpchp_wq, &info->work); + queue_work(p_slot->wq, &info->work); return 0; } @@ -453,7 +453,7 @@ void shpchp_queue_pushbutton_work(struct work_struct *work) kfree(info); goto out; } - queue_work(shpchp_ordered_wq, &info->work); + queue_work(p_slot->wq, &info->work); out: mutex_unlock(&p_slot->lock); } @@ -501,7 +501,7 @@ static void handle_button_press_event(struct slot *p_slot) p_slot->hpc_ops->green_led_blink(p_slot); p_slot->hpc_ops->set_attention_status(p_slot, 0); - queue_delayed_work(shpchp_wq, &p_slot->work, 5*HZ); + queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); break; case BLINKINGOFF_STATE: case BLINKINGON_STATE: |

