aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorGavin Shan <shangw@linux.vnet.ibm.com>2013-06-20 06:13:22 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2013-06-21 02:01:50 -0400
commit1bc98de26d3f270e004421685a8e698e91cf95ca (patch)
treed7d6c70bff77b07b804824496cd65d044b65f44d /arch
parentb95cd2cd44b39cf11087b15f74e29ef9f2c6bf0f (diff)
powernv/opal: Notifier for OPAL events
This patch implements a notifier to receive a notification on OPAL event mask changes. The notifier is only called as a result of an OPAL interrupt, which will happen upon reception of FSP messages or PCI errors. Any event mask change detected as a result of opal_poll_events() will not result in a notifier call. [benh: changelog] Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/include/asm/opal.h5
-rw-r--r--arch/powerpc/platforms/powernv/opal.c69
2 files changed, 73 insertions, 1 deletions
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 28807978bb46..029fe85722aa 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -644,6 +644,11 @@ extern void hvc_opal_init_early(void);
644extern int early_init_dt_scan_opal(unsigned long node, const char *uname, 644extern int early_init_dt_scan_opal(unsigned long node, const char *uname,
645 int depth, void *data); 645 int depth, void *data);
646 646
647extern int opal_notifier_register(struct notifier_block *nb);
648extern void opal_notifier_enable(void);
649extern void opal_notifier_disable(void);
650extern void opal_notifier_update_evt(uint64_t evt_mask, uint64_t evt_val);
651
647extern int opal_get_chars(uint32_t vtermno, char *buf, int count); 652extern int opal_get_chars(uint32_t vtermno, char *buf, int count);
648extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len); 653extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len);
649 654
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 628c564ceadb..106301fd2fa5 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -15,6 +15,7 @@
15#include <linux/of.h> 15#include <linux/of.h>
16#include <linux/of_platform.h> 16#include <linux/of_platform.h>
17#include <linux/interrupt.h> 17#include <linux/interrupt.h>
18#include <linux/notifier.h>
18#include <linux/slab.h> 19#include <linux/slab.h>
19#include <asm/opal.h> 20#include <asm/opal.h>
20#include <asm/firmware.h> 21#include <asm/firmware.h>
@@ -31,6 +32,10 @@ static DEFINE_SPINLOCK(opal_write_lock);
31extern u64 opal_mc_secondary_handler[]; 32extern u64 opal_mc_secondary_handler[];
32static unsigned int *opal_irqs; 33static unsigned int *opal_irqs;
33static unsigned int opal_irq_count; 34static unsigned int opal_irq_count;
35static ATOMIC_NOTIFIER_HEAD(opal_notifier_head);
36static DEFINE_SPINLOCK(opal_notifier_lock);
37static uint64_t last_notified_mask = 0x0ul;
38static atomic_t opal_notifier_hold = ATOMIC_INIT(0);
34 39
35int __init early_init_dt_scan_opal(unsigned long node, 40int __init early_init_dt_scan_opal(unsigned long node,
36 const char *uname, int depth, void *data) 41 const char *uname, int depth, void *data)
@@ -95,6 +100,68 @@ static int __init opal_register_exception_handlers(void)
95 100
96early_initcall(opal_register_exception_handlers); 101early_initcall(opal_register_exception_handlers);
97 102
103int opal_notifier_register(struct notifier_block *nb)
104{
105 if (!nb) {
106 pr_warning("%s: Invalid argument (%p)\n",
107 __func__, nb);
108 return -EINVAL;
109 }
110
111 atomic_notifier_chain_register(&opal_notifier_head, nb);
112 return 0;
113}
114
115static void opal_do_notifier(uint64_t events)
116{
117 unsigned long flags;
118 uint64_t changed_mask;
119
120 if (atomic_read(&opal_notifier_hold))
121 return;
122
123 spin_lock_irqsave(&opal_notifier_lock, flags);
124 changed_mask = last_notified_mask ^ events;
125 last_notified_mask = events;
126 spin_unlock_irqrestore(&opal_notifier_lock, flags);
127
128 /*
129 * We feed with the event bits and changed bits for
130 * enough information to the callback.
131 */
132 atomic_notifier_call_chain(&opal_notifier_head,
133 events, (void *)changed_mask);
134}
135
136void opal_notifier_update_evt(uint64_t evt_mask,
137 uint64_t evt_val)
138{
139 unsigned long flags;
140
141 spin_lock_irqsave(&opal_notifier_lock, flags);
142 last_notified_mask &= ~evt_mask;
143 last_notified_mask |= evt_val;
144 spin_unlock_irqrestore(&opal_notifier_lock, flags);
145}
146
147void opal_notifier_enable(void)
148{
149 int64_t rc;
150 uint64_t evt = 0;
151
152 atomic_set(&opal_notifier_hold, 0);
153
154 /* Process pending events */
155 rc = opal_poll_events(&evt);
156 if (rc == OPAL_SUCCESS && evt)
157 opal_do_notifier(evt);
158}
159
160void opal_notifier_disable(void)
161{
162 atomic_set(&opal_notifier_hold, 1);
163}
164
98int opal_get_chars(uint32_t vtermno, char *buf, int count) 165int opal_get_chars(uint32_t vtermno, char *buf, int count)
99{ 166{
100 s64 len, rc; 167 s64 len, rc;
@@ -297,7 +364,7 @@ static irqreturn_t opal_interrupt(int irq, void *data)
297 364
298 opal_handle_interrupt(virq_to_hw(irq), &events); 365 opal_handle_interrupt(virq_to_hw(irq), &events);
299 366
300 /* XXX TODO: Do something with the events */ 367 opal_do_notifier(events);
301 368
302 return IRQ_HANDLED; 369 return IRQ_HANDLED;
303} 370}