ide-floppy: start DMA engine in idefloppy_transfer_pc1()
[linux-2.6.git] / drivers / edac / edac_pci.c
index 677c603..22ec9d5 100644 (file)
 #include "edac_module.h"
 
 static DEFINE_MUTEX(edac_pci_ctls_mutex);
-static struct list_head edac_pci_list = LIST_HEAD_INIT(edac_pci_list);
-
-static inline void edac_lock_pci_list(void)
-{
-       mutex_lock(&edac_pci_ctls_mutex);
-}
-
-static inline void edac_unlock_pci_list(void)
-{
-       mutex_unlock(&edac_pci_ctls_mutex);
-}
+static LIST_HEAD(edac_pci_list);
 
 /*
- * The alloc() and free() functions for the 'edac_pci' control info
- * structure. The chip driver will allocate one of these for each
- * edac_pci it is going to control/register with the EDAC CORE.
+ * edac_pci_alloc_ctl_info
+ *
+ *     The alloc() function for the 'edac_pci' control info
+ *     structure. The chip driver will allocate one of these for each
+ *     edac_pci it is going to control/register with the EDAC CORE.
  */
-struct edac_pci_ctl_info * edac_pci_alloc_ctl_info(
-       unsigned int sz_pvt,
-       const char *edac_pci_name)
+struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt,
+                                               const char *edac_pci_name)
 {
        struct edac_pci_ctl_info *pci;
        void *pvt;
        unsigned int size;
 
+       debugf1("%s()\n", __func__);
+
        pci = (struct edac_pci_ctl_info *)0;
        pvt = edac_align_ptr(&pci[1], sz_pvt);
        size = ((unsigned long)pvt) + sz_pvt;
 
-       if ((pci = kzalloc(size, GFP_KERNEL)) == NULL)
+       /* Alloc the needed control struct memory */
+       pci = kzalloc(size, GFP_KERNEL);
+       if (pci  == NULL)
                return NULL;
 
+       /* Now much private space */
        pvt = sz_pvt ? ((char *)pci) + ((unsigned long)pvt) : NULL;
 
        pci->pvt_info = pvt;
-
        pci->op_state = OP_ALLOC;
 
-       snprintf(pci->name, strlen(edac_pci_name)+1, "%s", edac_pci_name);
+       snprintf(pci->name, strlen(edac_pci_name) + 1, "%s", edac_pci_name);
 
        return pci;
 }
@@ -75,24 +70,34 @@ EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info);
 
 /*
  * edac_pci_free_ctl_info()
- *     frees the memory allocated by edac_pci_alloc_ctl_info() function
+ *
+ *     Last action on the pci control structure.
+ *
+ *     call the remove sysfs information, which will unregister
+ *     this control struct's kobj. When that kobj's ref count
+ *     goes to zero, its release function will be call and then
+ *     kfree() the memory.
  */
 void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci)
 {
-       kfree(pci);
+       debugf1("%s()\n", __func__);
+
+       edac_pci_remove_sysfs(pci);
 }
 EXPORT_SYMBOL_GPL(edac_pci_free_ctl_info);
 
 /*
  * find_edac_pci_by_dev()
  *     scans the edac_pci list for a specific 'struct device *'
+ *
+ *     return NULL if not found, or return control struct pointer
  */
-static struct edac_pci_ctl_info * find_edac_pci_by_dev(struct device *dev)
+static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev)
 {
        struct edac_pci_ctl_info *pci;
        struct list_head *item;
 
-       debugf3("%s()\n", __func__);
+       debugf1("%s()\n", __func__);
 
        list_for_each(item, &edac_pci_list) {
                pci = list_entry(item, struct edac_pci_ctl_info, link);
@@ -117,10 +122,13 @@ static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci)
        struct list_head *item, *insert_before;
        struct edac_pci_ctl_info *rover;
 
+       debugf1("%s()\n", __func__);
+
        insert_before = &edac_pci_list;
 
        /* Determine if already on the list */
-       if (unlikely((rover = find_edac_pci_by_dev(pci->dev)) != NULL))
+       rover = find_edac_pci_by_dev(pci->dev);
+       if (unlikely(rover != NULL))
                goto fail0;
 
        /* Insert in ascending order by 'pci_idx', so find position */
@@ -142,19 +150,22 @@ static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci)
 fail0:
        edac_printk(KERN_WARNING, EDAC_PCI,
                "%s (%s) %s %s already assigned %d\n",
-               rover->dev->bus_id, dev_name(rover),
+               rover->dev->bus_id, edac_dev_name(rover),
                rover->mod_name, rover->ctl_name, rover->pci_idx);
        return 1;
 
 fail1:
        edac_printk(KERN_WARNING, EDAC_PCI,
                "but in low-level driver: attempt to assign\n"
-               "\tduplicate pci_idx %d in %s()\n", rover->pci_idx, __func__);
+               "\tduplicate pci_idx %d in %s()\n", rover->pci_idx,
+               __func__);
        return 1;
 }
 
 /*
  * complete_edac_pci_list_del
+ *
+ *     RCU completion callback to indicate item is deleted
  */
 static void complete_edac_pci_list_del(struct rcu_head *head)
 {
@@ -167,6 +178,8 @@ static void complete_edac_pci_list_del(struct rcu_head *head)
 
 /*
  * del_edac_pci_from_global_list
+ *
+ *     remove the PCI control struct from the global list
  */
 static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci)
 {
@@ -176,6 +189,9 @@ static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci)
        wait_for_completion(&pci->complete);
 }
 
+#if 0
+/* Older code, but might use in the future */
+
 /*
  * edac_pci_find()
  *     Search for an edac_pci_ctl_info structure whose index is 'idx'
@@ -185,7 +201,7 @@ static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci)
  *
  * Caller must hold pci_ctls_mutex.
  */
-struct edac_pci_ctl_info * edac_pci_find(int idx)
+struct edac_pci_ctl_info *edac_pci_find(int idx)
 {
        struct list_head *item;
        struct edac_pci_ctl_info *pci;
@@ -198,7 +214,7 @@ struct edac_pci_ctl_info * edac_pci_find(int idx)
                        if (pci->pci_idx == idx)
                                return pci;
 
-               /* not on list, so terminate early */
+                       /* not on list, so terminate early */
                        break;
                }
        }
@@ -206,54 +222,61 @@ struct edac_pci_ctl_info * edac_pci_find(int idx)
        return NULL;
 }
 EXPORT_SYMBOL_GPL(edac_pci_find);
+#endif
 
 /*
  * edac_pci_workq_function()
- *     performs the operation scheduled by a workq request
+ *
+ *     periodic function that performs the operation
+ *     scheduled by a workq request, for a given PCI control struct
  */
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))
 static void edac_pci_workq_function(struct work_struct *work_req)
 {
        struct delayed_work *d_work = (struct delayed_work *)work_req;
        struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work);
-#else
-static void edac_pci_workq_function(void *ptr)
-{
-       struct edac_pci_ctl_info *pci = ptr;
-#endif
+       int msec;
+       unsigned long delay;
 
-       edac_lock_pci_list();
+       debugf3("%s() checking\n", __func__);
 
-       if ((pci->op_state == OP_RUNNING_POLL) &&
-               (pci->edac_check != NULL) &&
-               (pci->check_parity_error))
-               pci->edac_check(pci);
+       mutex_lock(&edac_pci_ctls_mutex);
 
-       edac_unlock_pci_list();
+       if (pci->op_state == OP_RUNNING_POLL) {
+               /* we might be in POLL mode, but there may NOT be a poll func
+                */
+               if ((pci->edac_check != NULL) && edac_pci_get_check_errors())
+                       pci->edac_check(pci);
+
+               /* if we are on a one second period, then use round */
+               msec = edac_pci_get_poll_msec();
+               if (msec == 1000)
+                       delay = round_jiffies_relative(msecs_to_jiffies(msec));
+               else
+                       delay = msecs_to_jiffies(msec);
+
+               /* Reschedule only if we are in POLL mode */
+               queue_delayed_work(edac_workqueue, &pci->work, delay);
+       }
 
-       /* Reschedule */
-       queue_delayed_work(edac_workqueue, &pci->work, pci->delay);
+       mutex_unlock(&edac_pci_ctls_mutex);
 }
 
 /*
  * edac_pci_workq_setup()
  *     initialize a workq item for this edac_pci instance
  *     passing in the new delay period in msec
+ *
+ *     locking model:
+ *             called when 'edac_pci_ctls_mutex' is locked
  */
 static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci,
-               unsigned int msec)
+                                unsigned int msec)
 {
        debugf0("%s()\n", __func__);
 
-       pci->poll_msec = msec;
-       edac_calc_delay(pci);
-
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))
        INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function);
-#else
-       INIT_WORK(&pci->work, edac_pci_workq_function, pci);
-#endif
-       queue_delayed_work(edac_workqueue, &pci->work, pci->delay);
+       queue_delayed_work(edac_workqueue, &pci->work,
+                       msecs_to_jiffies(edac_pci_get_poll_msec()));
 }
 
 /*
@@ -264,6 +287,8 @@ static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci)
 {
        int status;
 
+       debugf0("%s()\n", __func__);
+
        status = cancel_delayed_work(&pci->work);
        if (status == 0)
                flush_workqueue(edac_workqueue);
@@ -271,17 +296,24 @@ static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci)
 
 /*
  * edac_pci_reset_delay_period
+ *
+ *     called with a new period value for the workq period
+ *     a) stop current workq timer
+ *     b) restart workq timer with new value
  */
 void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci,
-               unsigned long value)
+                                unsigned long value)
 {
-       edac_lock_pci_list();
+       debugf0("%s()\n", __func__);
 
        edac_pci_workq_teardown(pci);
 
+       /* need to lock for the setup */
+       mutex_lock(&edac_pci_ctls_mutex);
+
        edac_pci_workq_setup(pci, value);
 
-       edac_unlock_pci_list();
+       mutex_unlock(&edac_pci_ctls_mutex);
 }
 EXPORT_SYMBOL_GPL(edac_pci_reset_delay_period);
 
@@ -302,14 +334,13 @@ int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx)
        debugf0("%s()\n", __func__);
 
        pci->pci_idx = edac_idx;
+       pci->start_time = jiffies;
 
-       edac_lock_pci_list();
+       mutex_lock(&edac_pci_ctls_mutex);
 
        if (add_edac_pci_to_global_list(pci))
                goto fail0;
 
-       pci->start_time = jiffies;
-
        if (edac_pci_create_sysfs(pci)) {
                edac_pci_printk(pci, KERN_WARNING,
                                "failed to create sysfs pci\n");
@@ -325,20 +356,20 @@ int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx)
        }
 
        edac_pci_printk(pci, KERN_INFO,
-               "Giving out device to module '%s' controller '%s':"
-               " DEV '%s' (%s)\n",
-               pci->mod_name,
-               pci->ctl_name,
-               dev_name(pci),
-               edac_op_state_toString(pci->op_state));
-
-       edac_unlock_pci_list();
+                       "Giving out device to module '%s' controller '%s':"
+                       " DEV '%s' (%s)\n",
+                       pci->mod_name,
+                       pci->ctl_name,
+                       edac_dev_name(pci), edac_op_state_to_string(pci->op_state));
+
+       mutex_unlock(&edac_pci_ctls_mutex);
        return 0;
 
+       /* error unwind stack */
 fail1:
        del_edac_pci_from_global_list(pci);
 fail0:
-       edac_unlock_pci_list();
+       mutex_unlock(&edac_pci_ctls_mutex);
        return 1;
 }
 EXPORT_SYMBOL_GPL(edac_pci_add_device);
@@ -356,64 +387,72 @@ EXPORT_SYMBOL_GPL(edac_pci_add_device);
  *     Pointer to removed edac_pci structure,
  *     or NULL if device not found
  */
-struct edac_pci_ctl_info * edac_pci_del_device(struct device *dev)
+struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev)
 {
        struct edac_pci_ctl_info *pci;
 
        debugf0("%s()\n", __func__);
 
-       edac_lock_pci_list();
+       mutex_lock(&edac_pci_ctls_mutex);
 
-       if ((pci = find_edac_pci_by_dev(dev)) == NULL) {
-               edac_unlock_pci_list();
+       /* ensure the control struct is on the global list
+        * if not, then leave
+        */
+       pci = find_edac_pci_by_dev(dev);
+       if (pci  == NULL) {
+               mutex_unlock(&edac_pci_ctls_mutex);
                return NULL;
        }
 
        pci->op_state = OP_OFFLINE;
 
-       edac_pci_workq_teardown(pci);
-
-       edac_pci_remove_sysfs(pci);
-
        del_edac_pci_from_global_list(pci);
 
-       edac_unlock_pci_list();
+       mutex_unlock(&edac_pci_ctls_mutex);
+
+       /* stop the workq timer */
+       edac_pci_workq_teardown(pci);
 
        edac_printk(KERN_INFO, EDAC_PCI,
                "Removed device %d for %s %s: DEV %s\n",
-               pci->pci_idx,
-               pci->mod_name,
-               pci->ctl_name,
-               dev_name(pci));
+               pci->pci_idx, pci->mod_name, pci->ctl_name, edac_dev_name(pci));
 
        return pci;
 }
 EXPORT_SYMBOL_GPL(edac_pci_del_device);
 
-static inline int edac_pci_get_log_pe(struct edac_pci_ctl_info *pci)
-{
-       return pci->log_parity_error;
-}
-
-static inline int edac_pci_get_panic_on_pe(struct edac_pci_ctl_info *pci)
-{
-       return pci->panic_on_pe;
-}
-
-void edac_pci_generic_check(struct edac_pci_ctl_info *pci)
+/*
+ * edac_pci_generic_check
+ *
+ *     a Generic parity check API
+ */
+static void edac_pci_generic_check(struct edac_pci_ctl_info *pci)
 {
+       debugf4("%s()\n", __func__);
        edac_pci_do_parity_check();
 }
 
-static int edac_pci_idx = 0;
+/* free running instance index counter */
+static int edac_pci_idx;
 #define EDAC_PCI_GENCTL_NAME   "EDAC PCI controller"
 
 struct edac_pci_gen_data {
        int edac_idx;
 };
 
-struct edac_pci_ctl_info *
-edac_pci_create_generic_ctl(struct device *dev, const char *mod_name)
+/*
+ * edac_pci_create_generic_ctl
+ *
+ *     A generic constructor for a PCI parity polling device
+ *     Some systems have more than one domain of PCI busses.
+ *     For systems with one domain, then this API will
+ *     provide for a generic poller.
+ *
+ *     This routine calls the edac_pci_alloc_ctl_info() for
+ *     the generic device, with default values
+ */
+struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev,
+                                               const char *mod_name)
 {
        struct edac_pci_ctl_info *pci;
        struct edac_pci_gen_data *pdata;
@@ -443,8 +482,15 @@ edac_pci_create_generic_ctl(struct device *dev, const char *mod_name)
 }
 EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl);
 
+/*
+ * edac_pci_release_generic_ctl
+ *
+ *     The release function of a generic EDAC PCI polling device
+ */
 void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci)
 {
+       debugf0("%s() pci mod=%s\n", __func__, pci->mod_name);
+
        edac_pci_del_device(pci->dev);
        edac_pci_free_ctl_info(pci);
 }