diff options
author | Gavin Shan <shangw@linux.vnet.ibm.com> | 2013-06-20 06:13:22 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-06-21 02:01:50 -0400 |
commit | 1bc98de26d3f270e004421685a8e698e91cf95ca (patch) | |
tree | d7d6c70bff77b07b804824496cd65d044b65f44d /arch | |
parent | b95cd2cd44b39cf11087b15f74e29ef9f2c6bf0f (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.h | 5 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal.c | 69 |
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); | |||
644 | extern int early_init_dt_scan_opal(unsigned long node, const char *uname, | 644 | extern int early_init_dt_scan_opal(unsigned long node, const char *uname, |
645 | int depth, void *data); | 645 | int depth, void *data); |
646 | 646 | ||
647 | extern int opal_notifier_register(struct notifier_block *nb); | ||
648 | extern void opal_notifier_enable(void); | ||
649 | extern void opal_notifier_disable(void); | ||
650 | extern void opal_notifier_update_evt(uint64_t evt_mask, uint64_t evt_val); | ||
651 | |||
647 | extern int opal_get_chars(uint32_t vtermno, char *buf, int count); | 652 | extern int opal_get_chars(uint32_t vtermno, char *buf, int count); |
648 | extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len); | 653 | extern 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); | |||
31 | extern u64 opal_mc_secondary_handler[]; | 32 | extern u64 opal_mc_secondary_handler[]; |
32 | static unsigned int *opal_irqs; | 33 | static unsigned int *opal_irqs; |
33 | static unsigned int opal_irq_count; | 34 | static unsigned int opal_irq_count; |
35 | static ATOMIC_NOTIFIER_HEAD(opal_notifier_head); | ||
36 | static DEFINE_SPINLOCK(opal_notifier_lock); | ||
37 | static uint64_t last_notified_mask = 0x0ul; | ||
38 | static atomic_t opal_notifier_hold = ATOMIC_INIT(0); | ||
34 | 39 | ||
35 | int __init early_init_dt_scan_opal(unsigned long node, | 40 | int __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 | ||
96 | early_initcall(opal_register_exception_handlers); | 101 | early_initcall(opal_register_exception_handlers); |
97 | 102 | ||
103 | int 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 | |||
115 | static 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 | |||
136 | void 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 | |||
147 | void 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 | |||
160 | void opal_notifier_disable(void) | ||
161 | { | ||
162 | atomic_set(&opal_notifier_hold, 1); | ||
163 | } | ||
164 | |||
98 | int opal_get_chars(uint32_t vtermno, char *buf, int count) | 165 | int 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 | } |