From f7391f5325ea744f0632f7ef39a90085162743ac Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Tue, 21 Feb 2006 15:45:45 -0800 Subject: [PATCH] [PATCH] shpchp: event handling rework The event handler of SHPCHP driver is unnecessarily very complex. In addition, current event handler can only a fixed number of events at the same time, and some of events would be lost if several number of events happened at the same time. This patch simplify the event handler by using 'work queue', and it also fix the above-mentioned issue. Signed-off-by: Kenji Kaneshige Signed-off-by: Kristen Carlson Accardi Signed-off-by: Greg Kroah-Hartman --- drivers/pci/hotplug/shpchp.h | 13 +- drivers/pci/hotplug/shpchp_core.c | 49 ++--- drivers/pci/hotplug/shpchp_ctrl.c | 313 +++++++++--------------------- drivers/pci/hotplug/shpchp_hpc.c | 10 + 4 files changed, 119 insertions(+), 266 deletions(-) diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index 6e1fb1bdc24..87db07cfebd 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -46,6 +46,7 @@ extern int shpchp_poll_mode; extern int shpchp_poll_time; extern int shpchp_debug; +extern struct workqueue_struct *shpchp_wq; /*#define dbg(format, arg...) do { if (shpchp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/ #define dbg(format, arg...) do { if (shpchp_debug) printk("%s: " format, MY_NAME , ## arg); } while (0) @@ -70,11 +71,13 @@ struct slot { struct hotplug_slot *hotplug_slot; struct list_head slot_list; char name[SLOT_NAME_SIZE]; + struct work_struct work; /* work for button event */ }; struct event_info { u32 event_type; - u8 hp_slot; + struct slot *p_slot; + struct work_struct work; }; struct controller { @@ -85,11 +88,9 @@ struct controller { int num_slots; /* Number of slots on ctlr */ int slot_num_inc; /* 1 or -1 */ struct pci_dev *pci_dev; - struct event_info event_queue[10]; struct list_head slot_list; struct hpc_ops *hpc_ops; wait_queue_head_t queue; /* sleep & wake process */ - u8 next_event; u8 bus; u8 device; u8 function; @@ -180,9 +181,6 @@ struct hotplug_params { /* sysfs functions for the hotplug controller info */ extern void shpchp_create_ctrl_files (struct controller *ctrl); -/* controller functions */ -extern int shpchp_event_start_thread(void); -extern void shpchp_event_stop_thread(void); extern int shpchp_enable_slot(struct slot *slot); extern int shpchp_disable_slot(struct slot *slot); @@ -201,7 +199,8 @@ extern void get_hp_params_from_firmware(struct pci_dev *dev, extern int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum); extern void shpchp_remove_ctrl_files(struct controller *ctrl); - +extern void cleanup_slots(struct controller *ctrl); +extern void shpchp_pushbutton_thread(void *data); /* Global variables */ extern struct list_head shpchp_ctrl_list; diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index 8b21fc20671..5de659d23d1 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "shpchp.h" /* Global variables */ @@ -39,6 +40,7 @@ int shpchp_debug; int shpchp_poll_mode; int shpchp_poll_time; LIST_HEAD(shpchp_ctrl_list); +struct workqueue_struct *shpchp_wq; #define DRIVER_VERSION "0.4" #define DRIVER_AUTHOR "Dan Zink , Greg Kroah-Hartman , Dely Sy " @@ -57,7 +59,6 @@ MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds"); #define SHPC_MODULE_NAME "shpchp" -static int shpc_start_thread (void); static int set_attention_status (struct hotplug_slot *slot, u8 value); static int enable_slot (struct hotplug_slot *slot); static int disable_slot (struct hotplug_slot *slot); @@ -141,6 +142,7 @@ static int init_slots(struct controller *ctrl) goto error_info; slot->number = sun; + INIT_WORK(&slot->work, shpchp_pushbutton_thread, slot); /* register this slot with the hotplug pci core */ hotplug_slot->private = slot; @@ -176,7 +178,7 @@ error: return retval; } -static void cleanup_slots(struct controller *ctrl) +void cleanup_slots(struct controller *ctrl) { struct list_head *tmp; struct list_head *next; @@ -185,6 +187,8 @@ static void cleanup_slots(struct controller *ctrl) list_for_each_safe(tmp, next, &ctrl->slot_list) { slot = list_entry(tmp, struct slot, slot_list); list_del(&slot->slot_list); + cancel_delayed_work(&slot->work); + flush_workqueue(shpchp_wq); pci_hp_deregister(slot->hotplug_slot); } } @@ -400,7 +404,7 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) rc = get_ctlr_slot_config(ctrl); if (rc) { err(msg_initialization_err, rc); - goto err_out_unmap_mmio_region; + goto err_out_release_ctlr; } first_device_num = ctrl->slot_device_offset; num_ctlr_slots = ctrl->num_slots; @@ -411,7 +415,7 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) rc = init_slots(ctrl); if (rc) { err(msg_initialization_err, 6); - goto err_out_free_ctrl_slot; + goto err_out_release_ctlr; } /* Now hpc_functions (slot->hpc_ops->functions) are ready */ @@ -427,18 +431,13 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ctrl->speed = PCI_SPEED_33MHz; } - /* Finish setting up the hot plug ctrl device */ - ctrl->next_event = 0; - list_add(&ctrl->ctrl_list, &shpchp_ctrl_list); shpchp_create_ctrl_files(ctrl); return 0; -err_out_free_ctrl_slot: - cleanup_slots(ctrl); -err_out_unmap_mmio_region: +err_out_release_ctlr: ctrl->hpc_ops->release_ctlr(ctrl); err_out_free_ctrl: kfree(ctrl); @@ -446,21 +445,6 @@ err_out_none: return -ENODEV; } -static int shpc_start_thread(void) -{ - int retval = 0; - - dbg("Initialize + Start the notification/polling mechanism \n"); - - retval = shpchp_event_start_thread(); - if (retval) { - dbg("shpchp_event_start_thread() failed\n"); - return retval; - } - - return retval; -} - static void __exit unload_shpchpd(void) { struct list_head *tmp; @@ -470,14 +454,11 @@ static void __exit unload_shpchpd(void) list_for_each_safe(tmp, next, &shpchp_ctrl_list) { ctrl = list_entry(tmp, struct controller, ctrl_list); shpchp_remove_ctrl_files(ctrl); - cleanup_slots(ctrl); ctrl->hpc_ops->release_ctlr(ctrl); kfree(ctrl); } - /* Stop the notification mechanism */ - shpchp_event_stop_thread(); - + destroy_workqueue(shpchp_wq); } static struct pci_device_id shpcd_pci_tbl[] = { @@ -501,17 +482,15 @@ static int __init shpcd_init(void) shpchp_poll_mode = 1; #endif - retval = shpc_start_thread(); - if (retval) - goto error_hpc_init; + shpchp_wq = create_singlethread_workqueue("shpchpd"); + if (!shpchp_wq) + return -ENOMEM; retval = pci_register_driver(&shpc_driver); dbg("%s: pci_register_driver = %d\n", __FUNCTION__, retval); info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - -error_hpc_init: if (retval) { - shpchp_event_stop_thread(); + destroy_workqueue(shpchp_wq); } return retval; } diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c index b709328a414..2411f3bd08d 100644 --- a/drivers/pci/hotplug/shpchp_ctrl.c +++ b/drivers/pci/hotplug/shpchp_ctrl.c @@ -32,44 +32,46 @@ #include #include #include +#include #include "../pci.h" #include "shpchp.h" -static void interrupt_event_handler(struct controller *ctrl); +static void interrupt_event_handler(void *data); -static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ -static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */ -static int event_finished; -static unsigned long pushbutton_pending; /* = 0 */ +static int queue_interrupt_event(struct slot *p_slot, u32 event_type) +{ + struct event_info *info; + + info = kmalloc(sizeof(*info), GFP_ATOMIC); + if (!info) + return -ENOMEM; + + info->event_type = event_type; + info->p_slot = p_slot; + INIT_WORK(&info->work, interrupt_event_handler, info); + + queue_work(shpchp_wq, &info->work); + + return 0; +} u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id) { struct controller *ctrl = (struct controller *) inst_id; struct slot *p_slot; - u8 rc = 0; - u8 getstatus; - struct event_info *taskInfo; + u32 event_type; /* Attention Button Change */ dbg("shpchp: Attention button interrupt received.\n"); - /* This is the structure that tells the worker thread what to do */ - taskInfo = &(ctrl->event_queue[ctrl->next_event]); p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); - p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); - p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); - - ctrl->next_event = (ctrl->next_event + 1) % 10; - taskInfo->hp_slot = hp_slot; - - rc++; /* * Button pressed - See if need to TAKE ACTION!!! */ info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot); - taskInfo->event_type = INT_BUTTON_PRESS; + event_type = INT_BUTTON_PRESS; if ((p_slot->state == BLINKINGON_STATE) || (p_slot->state == BLINKINGOFF_STATE)) { @@ -77,7 +79,7 @@ u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id) * attention again before the 5 sec. limit expires to cancel hot-add * or hot-remove */ - taskInfo->event_type = INT_BUTTON_CANCEL; + event_type = INT_BUTTON_CANCEL; info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot); } else if ((p_slot->state == POWERON_STATE) || (p_slot->state == POWEROFF_STATE)) { @@ -85,12 +87,11 @@ u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id) * means that the previous attention button action to hot-add or * hot-remove is undergoing */ - taskInfo->event_type = INT_BUTTON_IGNORE; + event_type = INT_BUTTON_IGNORE; info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot); } - if (rc) - up(&event_semaphore); /* signal event thread that new event is posted */ + queue_interrupt_event(p_slot, event_type); return 0; @@ -100,21 +101,12 @@ u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id) { struct controller *ctrl = (struct controller *) inst_id; struct slot *p_slot; - u8 rc = 0; u8 getstatus; - struct event_info *taskInfo; + u32 event_type; /* Switch Change */ dbg("shpchp: Switch interrupt received.\n"); - /* This is the structure that tells the worker thread - * what to do - */ - taskInfo = &(ctrl->event_queue[ctrl->next_event]); - ctrl->next_event = (ctrl->next_event + 1) % 10; - taskInfo->hp_slot = hp_slot; - - rc++; p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); @@ -126,9 +118,9 @@ u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id) * Switch opened */ info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot); - taskInfo->event_type = INT_SWITCH_OPEN; + event_type = INT_SWITCH_OPEN; if (p_slot->pwr_save && p_slot->presence_save) { - taskInfo->event_type = INT_POWER_FAULT; + event_type = INT_POWER_FAULT; err("Surprise Removal of card\n"); } } else { @@ -136,34 +128,23 @@ u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id) * Switch closed */ info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot); - taskInfo->event_type = INT_SWITCH_CLOSE; + event_type = INT_SWITCH_CLOSE; } - if (rc) - up(&event_semaphore); /* signal event thread that new event is posted */ + queue_interrupt_event(p_slot, event_type); - return rc; + return 1; } u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id) { struct controller *ctrl = (struct controller *) inst_id; struct slot *p_slot; - u8 rc = 0; - /*u8 temp_byte;*/ - struct event_info *taskInfo; + u32 event_type; /* Presence Change */ dbg("shpchp: Presence/Notify input change.\n"); - /* This is the structure that tells the worker thread - * what to do - */ - taskInfo = &(ctrl->event_queue[ctrl->next_event]); - ctrl->next_event = (ctrl->next_event + 1) % 10; - taskInfo->hp_slot = hp_slot; - - rc++; p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); /* @@ -175,39 +156,29 @@ u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id) * Card Present */ info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot); - taskInfo->event_type = INT_PRESENCE_ON; + event_type = INT_PRESENCE_ON; } else { /* * Not Present */ info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot); - taskInfo->event_type = INT_PRESENCE_OFF; + event_type = INT_PRESENCE_OFF; } - if (rc) - up(&event_semaphore); /* signal event thread that new event is posted */ + queue_interrupt_event(p_slot, event_type); - return rc; + return 1; } u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id) { struct controller *ctrl = (struct controller *) inst_id; struct slot *p_slot; - u8 rc = 0; - struct event_info *taskInfo; + u32 event_type; /* Power fault */ dbg("shpchp: Power fault interrupt received.\n"); - /* This is the structure that tells the worker thread - * what to do - */ - taskInfo = &(ctrl->event_queue[ctrl->next_event]); - ctrl->next_event = (ctrl->next_event + 1) % 10; - taskInfo->hp_slot = hp_slot; - - rc++; p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { @@ -216,21 +187,21 @@ u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id) */ info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot); p_slot->status = 0x00; - taskInfo->event_type = INT_POWER_FAULT_CLEAR; + event_type = INT_POWER_FAULT_CLEAR; } else { /* * Power fault */ info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot); - taskInfo->event_type = INT_POWER_FAULT; + event_type = INT_POWER_FAULT; /* set power fault status for this board */ p_slot->status = 0xFF; info("power fault bit %x set\n", hp_slot); } - if (rc) - up(&event_semaphore); /* signal event thread that new event is posted */ - return rc; + queue_interrupt_event(p_slot, event_type); + + return 1; } /* The following routines constitute the bulk of the @@ -521,14 +492,6 @@ static int remove_board(struct slot *p_slot) } -static void pushbutton_helper_thread (unsigned long data) -{ - pushbutton_pending = data; - - up(&event_semaphore); -} - - /** * shpchp_pushbutton_thread * @@ -536,90 +499,24 @@ static void pushbutton_helper_thread (unsigned long data) * Handles all pending events and exits. * */ -static void shpchp_pushbutton_thread (unsigned long slot) +void shpchp_pushbutton_thread(void *data) { - struct slot *p_slot = (struct slot *) slot; + struct slot *p_slot = data; u8 getstatus; - - pushbutton_pending = 0; - - if (!p_slot) { - dbg("%s: Error! slot NULL\n", __FUNCTION__); - return; - } p_slot->hpc_ops->get_power_status(p_slot, &getstatus); if (getstatus) { p_slot->state = POWEROFF_STATE; - shpchp_disable_slot(p_slot); p_slot->state = STATIC_STATE; } else { p_slot->state = POWERON_STATE; - if (shpchp_enable_slot(p_slot)) p_slot->hpc_ops->green_led_off(p_slot); - p_slot->state = STATIC_STATE; } - - return; -} - - -/* this is the main worker thread */ -static int event_thread(void* data) -{ - struct controller *ctrl; - lock_kernel(); - daemonize("shpchpd_event"); - unlock_kernel(); - - while (1) { - dbg("!!!!event_thread sleeping\n"); - down_interruptible (&event_semaphore); - dbg("event_thread woken finished = %d\n", event_finished); - if (event_finished || signal_pending(current)) - break; - /* Do stuff here */ - if (pushbutton_pending) - shpchp_pushbutton_thread(pushbutton_pending); - else - list_for_each_entry(ctrl, &shpchp_ctrl_list, ctrl_list) - interrupt_event_handler(ctrl); - } - dbg("event_thread signals exit\n"); - up(&event_exit); - return 0; -} - -int shpchp_event_start_thread (void) -{ - int pid; - - /* initialize our semaphores */ - init_MUTEX_LOCKED(&event_exit); - event_finished=0; - - init_MUTEX_LOCKED(&event_semaphore); - pid = kernel_thread(event_thread, NULL, 0); - - if (pid < 0) { - err ("Can't start up our event thread\n"); - return -1; - } - return 0; -} - - -void shpchp_event_stop_thread (void) -{ - event_finished = 1; - up(&event_semaphore); - down(&event_exit); } - static int update_slot_info (struct slot *slot) { struct hotplug_slot_info *info; @@ -639,91 +536,59 @@ static int update_slot_info (struct slot *slot) return result; } -static void interrupt_event_handler(struct controller *ctrl) +static void interrupt_event_handler(void *data) { - int loop = 0; - int change = 1; - u8 hp_slot; + struct event_info *info = data; + struct slot *p_slot = info->p_slot; u8 getstatus; - struct slot *p_slot; - while (change) { - change = 0; - - for (loop = 0; loop < 10; loop++) { - if (ctrl->event_queue[loop].event_type != 0) { - dbg("%s:loop %x event_type %x\n", __FUNCTION__, loop, - ctrl->event_queue[loop].event_type); - hp_slot = ctrl->event_queue[loop].hp_slot; - - p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); - - if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) { - dbg("%s: button cancel\n", __FUNCTION__); - del_timer(&p_slot->task_event); - - switch (p_slot->state) { - case BLINKINGOFF_STATE: - p_slot->hpc_ops->green_led_on(p_slot); - p_slot->hpc_ops->set_attention_status(p_slot, 0); - break; - case BLINKINGON_STATE: - p_slot->hpc_ops->green_led_off(p_slot); - p_slot->hpc_ops->set_attention_status(p_slot, 0); - break; - default: - warn("Not a valid state\n"); - return; - } - info(msg_button_cancel, p_slot->number); - p_slot->state = STATIC_STATE; - } else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { - /* Button Pressed (No action on 1st press...) */ - dbg("%s: Button pressed\n", __FUNCTION__); - - p_slot->hpc_ops->get_power_status(p_slot, &getstatus); - if (getstatus) { - /* slot is on */ - dbg("%s: slot is on\n", __FUNCTION__); - p_slot->state = BLINKINGOFF_STATE; - info(msg_button_off, p_slot->number); - } else { - /* slot is off */ - dbg("%s: slot is off\n", __FUNCTION__); - p_slot->state = BLINKINGON_STATE; - info(msg_button_on, p_slot->number); - } - - /* blink green LED and turn off amber */ - p_slot->hpc_ops->green_led_blink(p_slot); - p_slot->hpc_ops->set_attention_status(p_slot, 0); - - init_timer(&p_slot->task_event); - p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */ - p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread; - p_slot->task_event.data = (unsigned long) p_slot; - - dbg("%s: add_timer p_slot = %p\n", __FUNCTION__,(void *) p_slot); - add_timer(&p_slot->task_event); - } else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) { - /***********POWER FAULT********************/ - dbg("%s: power fault\n", __FUNCTION__); - p_slot->hpc_ops->set_attention_status(p_slot, 1); - p_slot->hpc_ops->green_led_off(p_slot); - } else { - /* refresh notification */ - if (p_slot) - update_slot_info(p_slot); - } - - ctrl->event_queue[loop].event_type = 0; + switch (info->event_type) { + case INT_BUTTON_CANCEL: + dbg("%s: button cancel\n", __FUNCTION__); + cancel_delayed_work(&p_slot->work); + switch (p_slot->state) { + case BLINKINGOFF_STATE: + p_slot->hpc_ops->green_led_on(p_slot); + p_slot->hpc_ops->set_attention_status(p_slot, 0); + break; + case BLINKINGON_STATE: + p_slot->hpc_ops->green_led_off(p_slot); + p_slot->hpc_ops->set_attention_status(p_slot, 0); + break; + default: + warn("Not a valid state\n"); + return; + } + info(msg_button_cancel, p_slot->number); + p_slot->state = STATIC_STATE; + break; + case INT_BUTTON_PRESS: + dbg("%s: Button pressed\n", __FUNCTION__); + p_slot->hpc_ops->get_power_status(p_slot, &getstatus); + if (getstatus) { + p_slot->state = BLINKINGOFF_STATE; + info(msg_button_off, p_slot->number); + } else { + p_slot->state = BLINKINGON_STATE; + info(msg_button_on, p_slot->number); + } + /* blink green LED and turn off amber */ + p_slot->hpc_ops->green_led_blink(p_slot); + p_slot->hpc_ops->set_attention_status(p_slot, 0); - change = 1; - } - } /* End of FOR loop */ + queue_delayed_work(shpchp_wq, &p_slot->work, 5*HZ); + break; + case INT_POWER_FAULT: + dbg("%s: power fault\n", __FUNCTION__); + p_slot->hpc_ops->set_attention_status(p_slot, 1); + p_slot->hpc_ops->green_led_off(p_slot); + break; + default: + update_slot_info(p_slot); + break; } - return; + kfree(info); } diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c index 943ed2b4dab..b392606a905 100644 --- a/drivers/pci/hotplug/shpchp_hpc.c +++ b/drivers/pci/hotplug/shpchp_hpc.c @@ -813,6 +813,7 @@ static void hpc_release_ctlr(struct controller *ctrl) { struct php_ctlr_state_s *php_ctlr = ctrl->hpc_ctlr_handle; struct php_ctlr_state_s *p, *p_prev; + int i; DBG_ENTER_ROUTINE @@ -821,6 +822,14 @@ static void hpc_release_ctlr(struct controller *ctrl) return ; } + /* + * Mask all slot event interrupts + */ + for (i = 0; i < ctrl->num_slots; i++) + writel(0xffff3fff, php_ctlr->creg + SLOT1 + (4 * i)); + + cleanup_slots(ctrl); + if (shpchp_poll_mode) { del_timer(&php_ctlr->int_poll_timer); } else { @@ -830,6 +839,7 @@ static void hpc_release_ctlr(struct controller *ctrl) pci_disable_msi(php_ctlr->pci_dev); } } + if (php_ctlr->pci_dev) { iounmap(php_ctlr->creg); release_mem_region(ctrl->mmio_base, ctrl->mmio_size); -- 2.20.1