/*
* CompactPCI Hot Plug Driver
*
- * Copyright (C) 2002,2005 SOMA Networks, Inc.
+ * Copyright (C) 2002 SOMA Networks, Inc.
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
*
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/smp_lock.h>
-#include <asm/atomic.h>
#include <linux/delay.h>
#include "pci_hotplug.h"
#include "cpci_hotplug.h"
+#define DRIVER_VERSION "0.2"
#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
#define DRIVER_DESC "CompactPCI Hot Plug Core"
#define dbg(format, arg...) \
do { \
- if (cpci_debug) \
+ if(cpci_debug) \
printk (KERN_DEBUG "%s: " format "\n", \
MY_NAME , ## arg); \
- } while (0)
+ } while(0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
/* local variables */
-static DECLARE_RWSEM(list_rwsem);
+static spinlock_t list_lock;
static LIST_HEAD(slot_list);
static int slots;
-static atomic_t extracting;
int cpci_debug;
static struct cpci_hp_controller *controller;
static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */
static int set_attention_status(struct hotplug_slot *slot, u8 value);
static int get_power_status(struct hotplug_slot *slot, u8 * value);
static int get_attention_status(struct hotplug_slot *slot, u8 * value);
-static int get_adapter_status(struct hotplug_slot *slot, u8 * value);
-static int get_latch_status(struct hotplug_slot *slot, u8 * value);
static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
.owner = THIS_MODULE,
.set_attention_status = set_attention_status,
.get_power_status = get_power_status,
.get_attention_status = get_attention_status,
- .get_adapter_status = get_adapter_status,
- .get_latch_status = get_latch_status,
};
static int
dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
- if (controller->ops->set_power)
+ if(controller->ops->set_power) {
retval = controller->ops->set_power(slot, 1);
+ }
+
return retval;
}
dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
- down_write(&list_rwsem);
-
/* Unconfigure device */
dbg("%s - unconfiguring slot %s",
__FUNCTION__, slot->hotplug_slot->name);
- if ((retval = cpci_unconfigure_slot(slot))) {
+ if((retval = cpci_unconfigure_slot(slot))) {
err("%s - could not unconfigure slot %s",
__FUNCTION__, slot->hotplug_slot->name);
- goto disable_error;
+ return retval;
}
dbg("%s - finished unconfiguring slot %s",
__FUNCTION__, slot->hotplug_slot->name);
/* Clear EXT (by setting it) */
- if (cpci_clear_ext(slot)) {
+ if(cpci_clear_ext(slot)) {
err("%s - could not clear EXT for slot %s",
__FUNCTION__, slot->hotplug_slot->name);
retval = -ENODEV;
- goto disable_error;
}
cpci_led_on(slot);
- if (controller->ops->set_power)
- if ((retval = controller->ops->set_power(slot, 0)))
- goto disable_error;
+ if(controller->ops->set_power) {
+ retval = controller->ops->set_power(slot, 0);
+ }
- if (update_adapter_status(slot->hotplug_slot, 0))
+ if(update_adapter_status(slot->hotplug_slot, 0)) {
warn("failure to update adapter file");
-
- if (slot->extracting) {
- slot->extracting = 0;
- atomic_dec(&extracting);
}
-disable_error:
- up_write(&list_rwsem);
+
+ slot->extracting = 0;
+
return retval;
}
{
u8 power = 1;
- if (controller->ops->get_power)
+ if(controller->ops->get_power) {
power = controller->ops->get_power(slot);
+ }
return power;
}
return cpci_set_attention_status(hotplug_slot->private, status);
}
-static int
-get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
-{
- *value = hotplug_slot->info->adapter_status;
- return 0;
-}
-
-static int
-get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value)
-{
- *value = hotplug_slot->info->latch_status;
- return 0;
-}
-
static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot->name);
kfree(slot->hotplug_slot);
- if (slot->dev)
- pci_dev_put(slot->dev);
kfree(slot);
}
int status = -ENOMEM;
int i;
- if (!(controller && bus))
+ if(!(controller && bus)) {
return -ENODEV;
+ }
/*
* Create a structure for each slot, and register that slot
}
/* Add slot to our internal list */
- down_write(&list_rwsem);
+ spin_lock(&list_lock);
list_add(&slot->slot_list, &slot_list);
slots++;
- up_write(&list_rwsem);
+ spin_unlock(&list_lock);
}
return 0;
error_name:
cpci_hp_unregister_bus(struct pci_bus *bus)
{
struct slot *slot;
- struct slot *tmp;
- int status = 0;
+ struct list_head *tmp;
+ struct list_head *next;
+ int status;
- down_write(&list_rwsem);
- if (!slots) {
- up_write(&list_rwsem);
+ spin_lock(&list_lock);
+ if(!slots) {
+ spin_unlock(&list_lock);
return -1;
}
- list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
- if (slot->bus == bus) {
- list_del(&slot->slot_list);
- slots--;
-
+ list_for_each_safe(tmp, next, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ if(slot->bus == bus) {
dbg("deregistering slot %s", slot->hotplug_slot->name);
status = pci_hp_deregister(slot->hotplug_slot);
- if (status) {
+ if(status) {
err("pci_hp_deregister failed with error %d",
status);
- break;
+ return status;
}
+
+ list_del(&slot->slot_list);
+ slots--;
}
}
- up_write(&list_rwsem);
- return status;
+ spin_unlock(&list_lock);
+ return 0;
}
/* This is the interrupt mode interrupt handler */
dbg("entered cpci_hp_intr");
/* Check to see if it was our interrupt */
- if ((controller->irq_flags & SA_SHIRQ) &&
+ if((controller->irq_flags & SA_SHIRQ) &&
!controller->ops->check_irq(controller->dev_id)) {
dbg("exited cpci_hp_intr, not our interrupt");
return IRQ_NONE;
}
/*
- * According to PICMG 2.1 R2.0, section 6.3.2, upon
+ * According to PICMG 2.12 R2.0, section 6.3.2, upon
* initialization, the system driver shall clear the
* INS bits of the cold-inserted devices.
*/
static int
-init_slots(int clear_ins)
+init_slots(void)
{
struct slot *slot;
+ struct list_head *tmp;
struct pci_dev* dev;
dbg("%s - enter", __FUNCTION__);
- down_read(&list_rwsem);
- if (!slots) {
- up_read(&list_rwsem);
+ spin_lock(&list_lock);
+ if(!slots) {
+ spin_unlock(&list_lock);
return -1;
}
- list_for_each_entry(slot, &slot_list, slot_list) {
+ list_for_each(tmp, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
dbg("%s - looking at slot %s",
__FUNCTION__, slot->hotplug_slot->name);
- if (clear_ins && cpci_check_and_clear_ins(slot))
+ if(cpci_check_and_clear_ins(slot)) {
dbg("%s - cleared INS for slot %s",
__FUNCTION__, slot->hotplug_slot->name);
- dev = pci_get_slot(slot->bus, PCI_DEVFN(slot->number, 0));
- if (dev) {
- if (update_adapter_status(slot->hotplug_slot, 1))
- warn("failure to update adapter file");
- if (update_latch_status(slot->hotplug_slot, 1))
- warn("failure to update latch file");
- slot->dev = dev;
+ dev = pci_find_slot(slot->bus->number, PCI_DEVFN(slot->number, 0));
+ if(dev) {
+ if(update_adapter_status(slot->hotplug_slot, 1)) {
+ warn("failure to update adapter file");
+ }
+ if(update_latch_status(slot->hotplug_slot, 1)) {
+ warn("failure to update latch file");
+ }
+ slot->dev = dev;
+ } else {
+ err("%s - no driver attached to device in slot %s",
+ __FUNCTION__, slot->hotplug_slot->name);
+ }
}
}
- up_read(&list_rwsem);
+ spin_unlock(&list_lock);
dbg("%s - exit", __FUNCTION__);
return 0;
}
check_slots(void)
{
struct slot *slot;
+ struct list_head *tmp;
int extracted;
int inserted;
- u16 hs_csr;
- down_read(&list_rwsem);
- if (!slots) {
- up_read(&list_rwsem);
+ spin_lock(&list_lock);
+ if(!slots) {
+ spin_unlock(&list_lock);
err("no slots registered, shutting down");
return -1;
}
extracted = inserted = 0;
- list_for_each_entry(slot, &slot_list, slot_list) {
+ list_for_each(tmp, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
dbg("%s - looking at slot %s",
__FUNCTION__, slot->hotplug_slot->name);
- if (cpci_check_and_clear_ins(slot)) {
- /*
- * Some broken hardware (e.g. PLX 9054AB) asserts
- * ENUM# twice...
- */
- if (slot->dev) {
- warn("slot %s already inserted",
- slot->hotplug_slot->name);
+ if(cpci_check_and_clear_ins(slot)) {
+ u16 hs_csr;
+
+ /* Some broken hardware (e.g. PLX 9054AB) asserts ENUM# twice... */
+ if(slot->dev) {
+ warn("slot %s already inserted", slot->hotplug_slot->name);
inserted++;
continue;
}
/* Configure device */
dbg("%s - configuring slot %s",
__FUNCTION__, slot->hotplug_slot->name);
- if (cpci_configure_slot(slot)) {
+ if(cpci_configure_slot(slot)) {
err("%s - could not configure slot %s",
__FUNCTION__, slot->hotplug_slot->name);
continue;
dbg("%s - slot %s HS_CSR (2) = %04x",
__FUNCTION__, slot->hotplug_slot->name, hs_csr);
- if (update_latch_status(slot->hotplug_slot, 1))
+ if(update_latch_status(slot->hotplug_slot, 1)) {
warn("failure to update latch file");
+ }
- if (update_adapter_status(slot->hotplug_slot, 1))
+ if(update_adapter_status(slot->hotplug_slot, 1)) {
warn("failure to update adapter file");
+ }
cpci_led_off(slot);
__FUNCTION__, slot->hotplug_slot->name, hs_csr);
inserted++;
- } else if (cpci_check_ext(slot)) {
+ } else if(cpci_check_ext(slot)) {
+ u16 hs_csr;
+
/* Process extraction request */
dbg("%s - slot %s extracted",
__FUNCTION__, slot->hotplug_slot->name);
dbg("%s - slot %s HS_CSR = %04x",
__FUNCTION__, slot->hotplug_slot->name, hs_csr);
- if (!slot->extracting) {
- if (update_latch_status(slot->hotplug_slot, 0)) {
+ if(!slot->extracting) {
+ if(update_latch_status(slot->hotplug_slot, 0)) {
warn("failure to update latch file");
}
slot->extracting = 1;
- atomic_inc(&extracting);
}
extracted++;
- } else if (slot->extracting) {
- hs_csr = cpci_get_hs_csr(slot);
- if (hs_csr == 0xffff) {
- /*
- * Hmmm, we're likely hosed at this point, should we
- * bother trying to tell the driver or not?
- */
- err("card in slot %s was improperly removed",
- slot->hotplug_slot->name);
- if (update_adapter_status(slot->hotplug_slot, 0))
- warn("failure to update adapter file");
- slot->extracting = 0;
- atomic_dec(&extracting);
- }
}
}
- up_read(&list_rwsem);
- dbg("inserted=%d, extracted=%d, extracting=%d",
- inserted, extracted, atomic_read(&extracting));
- if (inserted || extracted)
+ spin_unlock(&list_lock);
+ if(inserted || extracted) {
return extracted;
- else if (!atomic_read(&extracting)) {
+ }
+ else {
err("cannot find ENUM# source, shutting down");
return -1;
}
- return 0;
}
/* This is the interrupt mode worker thread body */
event_thread(void *data)
{
int rc;
+ struct slot *slot;
+ struct list_head *tmp;
lock_kernel();
daemonize("cpci_hp_eventd");
unlock_kernel();
dbg("%s - event thread started", __FUNCTION__);
- while (1) {
+ while(1) {
dbg("event thread sleeping");
down_interruptible(&event_semaphore);
dbg("event thread woken, thread_finished = %d",
thread_finished);
- if (thread_finished || signal_pending(current))
+ if(thread_finished || signal_pending(current))
break;
- do {
+ while(controller->ops->query_enum()) {
rc = check_slots();
- if (rc > 0) {
+ if (rc > 0)
/* Give userspace a chance to handle extraction */
msleep(500);
- } else if (rc < 0) {
+ else if (rc < 0) {
dbg("%s - error checking slots", __FUNCTION__);
thread_finished = 1;
break;
}
- } while (atomic_read(&extracting) && !thread_finished);
- if (thread_finished)
- break;
+ }
+ /* Check for someone yanking out a board */
+ list_for_each(tmp, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ if(slot->extracting) {
+ /*
+ * Hmmm, we're likely hosed at this point, should we
+ * bother trying to tell the driver or not?
+ */
+ err("card in slot %s was improperly removed",
+ slot->hotplug_slot->name);
+ if(update_adapter_status(slot->hotplug_slot, 0)) {
+ warn("failure to update adapter file");
+ }
+ slot->extracting = 0;
+ }
+ }
/* Re-enable ENUM# interrupt */
dbg("%s - re-enabling irq", __FUNCTION__);
controller->ops->enable_irq();
}
+
dbg("%s - event thread signals exit", __FUNCTION__);
up(&thread_exit);
return 0;
poll_thread(void *data)
{
int rc;
+ struct slot *slot;
+ struct list_head *tmp;
lock_kernel();
daemonize("cpci_hp_polld");
unlock_kernel();
- while (1) {
- if (thread_finished || signal_pending(current))
+ while(1) {
+ if(thread_finished || signal_pending(current))
break;
- if (controller->ops->query_enum()) {
- do {
- rc = check_slots();
- if (rc > 0) {
- /* Give userspace a chance to handle extraction */
- msleep(500);
- } else if (rc < 0) {
- dbg("%s - error checking slots", __FUNCTION__);
- thread_finished = 1;
- break;
+
+ while(controller->ops->query_enum()) {
+ rc = check_slots();
+ if(rc > 0)
+ /* Give userspace a chance to handle extraction */
+ msleep(500);
+ else if (rc < 0) {
+ dbg("%s - error checking slots", __FUNCTION__);
+ thread_finished = 1;
+ break;
+ }
+ }
+ /* Check for someone yanking out a board */
+ list_for_each(tmp, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ if(slot->extracting) {
+ /*
+ * Hmmm, we're likely hosed at this point, should we
+ * bother trying to tell the driver or not?
+ */
+ err("card in slot %s was improperly removed",
+ slot->hotplug_slot->name);
+ if(update_adapter_status(slot->hotplug_slot, 0)) {
+ warn("failure to update adapter file");
}
- } while (atomic_read(&extracting) && !thread_finished);
+ slot->extracting = 0;
+ }
}
+
msleep(100);
}
dbg("poll thread signals exit");
init_MUTEX_LOCKED(&thread_exit);
thread_finished = 0;
- if (controller->irq)
+ if(controller->irq) {
pid = kernel_thread(event_thread, NULL, 0);
- else
+ } else {
pid = kernel_thread(poll_thread, NULL, 0);
- if (pid < 0) {
+ }
+ if(pid < 0) {
err("Can't start up our thread");
return -1;
}
{
thread_finished = 1;
dbg("thread finish command given");
- if (controller->irq)
+ if(controller->irq) {
up(&event_semaphore);
+ }
dbg("wait for thread to exit");
down(&thread_exit);
}
{
int status = 0;
- if (controller)
- return -1;
- if (!(new_controller && new_controller->ops))
- return -EINVAL;
- if (new_controller->irq) {
- if (!(new_controller->ops->enable_irq &&
- new_controller->ops->disable_irq))
- status = -EINVAL;
- if (request_irq(new_controller->irq,
- cpci_hp_intr,
- new_controller->irq_flags,
- MY_NAME,
- new_controller->dev_id)) {
- err("Can't get irq %d for the hotplug cPCI controller",
- new_controller->irq);
- status = -ENODEV;
+ if(!controller) {
+ controller = new_controller;
+ if(controller->irq) {
+ if(request_irq(controller->irq,
+ cpci_hp_intr,
+ controller->irq_flags,
+ MY_NAME, controller->dev_id)) {
+ err("Can't get irq %d for the hotplug cPCI controller", controller->irq);
+ status = -ENODEV;
+ }
+ dbg("%s - acquired controller irq %d", __FUNCTION__,
+ controller->irq);
}
- dbg("%s - acquired controller irq %d",
- __FUNCTION__, new_controller->irq);
+ } else {
+ err("cPCI hotplug controller already registered");
+ status = -1;
}
- if (!status)
- controller = new_controller;
return status;
}
-static void
-cleanup_slots(void)
-{
- struct slot *slot;
- struct slot *tmp;
-
- /*
- * Unregister all of our slots with the pci_hotplug subsystem,
- * and free up all memory that we had allocated.
- */
- down_write(&list_rwsem);
- if (!slots)
- goto cleanup_null;
- list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
- list_del(&slot->slot_list);
- pci_hp_deregister(slot->hotplug_slot);
- }
-cleanup_null:
- up_write(&list_rwsem);
- return;
-}
-
int
cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller)
{
int status = 0;
- if (controller) {
- if (!thread_finished)
+ if(controller) {
+ if(!thread_finished) {
cpci_stop_thread();
- if (controller->irq)
+ }
+ if(controller->irq) {
free_irq(controller->irq, controller->dev_id);
+ }
controller = NULL;
- cleanup_slots();
- } else
+ } else {
status = -ENODEV;
+ }
return status;
}
int status;
dbg("%s - enter", __FUNCTION__);
- if (!controller)
+ if(!controller) {
return -ENODEV;
+ }
- down_read(&list_rwsem);
- if (list_empty(&slot_list)) {
- up_read(&list_rwsem);
+ spin_lock(&list_lock);
+ if(!slots) {
+ spin_unlock(&list_lock);
return -ENODEV;
}
- up_read(&list_rwsem);
+ spin_unlock(&list_lock);
- status = init_slots(first);
- if (first)
+ if(first) {
+ status = init_slots();
+ if(status) {
+ return status;
+ }
first = 0;
- if (status)
- return status;
+ }
status = cpci_start_thread();
- if (status)
+ if(status) {
return status;
+ }
dbg("%s - thread started", __FUNCTION__);
- if (controller->irq) {
+ if(controller->irq) {
/* Start enum interrupt processing */
dbg("%s - enabling irq", __FUNCTION__);
controller->ops->enable_irq();
int
cpci_hp_stop(void)
{
- if (!controller)
+ if(!controller) {
return -ENODEV;
- if (controller->irq) {
+ }
+
+ if(controller->irq) {
/* Stop enum interrupt processing */
dbg("%s - disabling irq", __FUNCTION__);
controller->ops->disable_irq();
return 0;
}
+static void __exit
+cleanup_slots(void)
+{
+ struct list_head *tmp;
+ struct slot *slot;
+
+ /*
+ * Unregister all of our slots with the pci_hotplug subsystem,
+ * and free up all memory that we had allocated.
+ */
+ spin_lock(&list_lock);
+ if(!slots) {
+ goto null_cleanup;
+ }
+ list_for_each(tmp, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ list_del(&slot->slot_list);
+ pci_hp_deregister(slot->hotplug_slot);
+ kfree(slot->hotplug_slot->info);
+ kfree(slot->hotplug_slot->name);
+ kfree(slot->hotplug_slot);
+ kfree(slot);
+ }
+ null_cleanup:
+ spin_unlock(&list_lock);
+ return;
+}
+
int __init
cpci_hotplug_init(int debug)
{
+ spin_lock_init(&list_lock);
cpci_debug = debug;
+
+ info(DRIVER_DESC " version: " DRIVER_VERSION);
return 0;
}
/*
* Clean everything up.
*/
- cpci_hp_stop();
- cpci_hp_unregister_controller(controller);
+ cleanup_slots();
}
EXPORT_SYMBOL_GPL(cpci_hp_register_controller);