aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/pseries
diff options
context:
space:
mode:
authorTseng-Hui (Frank) Lin <thlin@linux.vnet.ibm.com>2011-05-05 08:32:48 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-05-05 23:19:01 -0400
commit77eafe101a65b609b0693ee4eda381f60a4a5bab (patch)
treec7a04f2b9517cb64122987a401c77d86de9f1d33 /arch/powerpc/platforms/pseries
parent4cb4638079a487627232ffee5b48ca19c127aed8 (diff)
powerpc/pseries: Add support for IO event interrupts
This patch adds support for handling IO Event interrupts which come through at the /event-sources/ibm,io-events device tree node. The interrupts come through ibm,io-events device tree node are generated by the firmware to report IO events. The firmware uses the same interrupt to report multiple types of events for multiple devices. Each device may have its own event handler. This patch implements a plateform interrupt handler that is triggered by the IO event interrupts come through ibm,io-events device tree node, pull in the IO events from RTAS and call device event handlers registered in the notifier list. Device event handlers are expected to use atomic_notifier_chain_register() and atomic_notifier_chain_unregister() to register/unregister their event handler in pseries_ioei_notifier_list list with IO event interrupt. Device event handlers are responsible to identify if the event belongs to the device event handler. The device event handle should return NOTIFY_OK after the event is handled if the event belongs to the device event handler, or NOTIFY_DONE otherwise. Signed-off-by: Tseng-Hui (Frank) Lin <thlin@us.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/platforms/pseries')
-rw-r--r--arch/powerpc/platforms/pseries/Kconfig18
-rw-r--r--arch/powerpc/platforms/pseries/Makefile1
-rw-r--r--arch/powerpc/platforms/pseries/io_event_irq.c231
3 files changed, 250 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index b0449229836..71af4c5d6c0 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -50,6 +50,24 @@ config SCANLOG
50 tristate "Scanlog dump interface" 50 tristate "Scanlog dump interface"
51 depends on RTAS_PROC && PPC_PSERIES 51 depends on RTAS_PROC && PPC_PSERIES
52 52
53config IO_EVENT_IRQ
54 bool "IO Event Interrupt support"
55 depends on PPC_PSERIES
56 default y
57 help
58 Select this option, if you want to enable support for IO Event
59 interrupts. IO event interrupt is a mechanism provided by RTAS
60 to return information about hardware error and non-error events
61 which may need OS attention. RTAS returns events for multiple
62 event types and scopes. Device drivers can register their handlers
63 to receive events.
64
65 This option will only enable the IO event platform code. You
66 will still need to enable or compile the actual drivers
67 that use this infrastruture to handle IO event interrupts.
68
69 Say Y if you are unsure.
70
53config LPARCFG 71config LPARCFG
54 bool "LPAR Configuration Data" 72 bool "LPAR Configuration Data"
55 depends on PPC_PSERIES || PPC_ISERIES 73 depends on PPC_PSERIES || PPC_ISERIES
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 4cfefbaccd5..3556e402cbf 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o
21obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o 21obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o
22obj-$(CONFIG_CMM) += cmm.o 22obj-$(CONFIG_CMM) += cmm.o
23obj-$(CONFIG_DTL) += dtl.o 23obj-$(CONFIG_DTL) += dtl.o
24obj-$(CONFIG_IO_EVENT_IRQ) += io_event_irq.o
24 25
25ifeq ($(CONFIG_PPC_PSERIES),y) 26ifeq ($(CONFIG_PPC_PSERIES),y)
26obj-$(CONFIG_SUSPEND) += suspend.o 27obj-$(CONFIG_SUSPEND) += suspend.o
diff --git a/arch/powerpc/platforms/pseries/io_event_irq.c b/arch/powerpc/platforms/pseries/io_event_irq.c
new file mode 100644
index 00000000000..c829e6067d5
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/io_event_irq.c
@@ -0,0 +1,231 @@
1/*
2 * Copyright 2010 2011 Mark Nelson and Tseng-Hui (Frank) Lin, IBM Corporation
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10#include <linux/errno.h>
11#include <linux/slab.h>
12#include <linux/module.h>
13#include <linux/irq.h>
14#include <linux/interrupt.h>
15#include <linux/of.h>
16#include <linux/list.h>
17#include <linux/notifier.h>
18
19#include <asm/machdep.h>
20#include <asm/rtas.h>
21#include <asm/irq.h>
22#include <asm/io_event_irq.h>
23
24#include "pseries.h"
25
26/*
27 * IO event interrupt is a mechanism provided by RTAS to return
28 * information about hardware error and non-error events. Device
29 * drivers can register their event handlers to receive events.
30 * Device drivers are expected to use atomic_notifier_chain_register()
31 * and atomic_notifier_chain_unregister() to register and unregister
32 * their event handlers. Since multiple IO event types and scopes
33 * share an IO event interrupt, the event handlers are called one
34 * by one until the IO event is claimed by one of the handlers.
35 * The event handlers are expected to return NOTIFY_OK if the
36 * event is handled by the event handler or NOTIFY_DONE if the
37 * event does not belong to the handler.
38 *
39 * Usage:
40 *
41 * Notifier function:
42 * #include <asm/io_event_irq.h>
43 * int event_handler(struct notifier_block *nb, unsigned long val, void *data) {
44 * p = (struct pseries_io_event_sect_data *) data;
45 * if (! is_my_event(p->scope, p->event_type)) return NOTIFY_DONE;
46 * :
47 * :
48 * return NOTIFY_OK;
49 * }
50 * struct notifier_block event_nb = {
51 * .notifier_call = event_handler,
52 * }
53 *
54 * Registration:
55 * atomic_notifier_chain_register(&pseries_ioei_notifier_list, &event_nb);
56 *
57 * Unregistration:
58 * atomic_notifier_chain_unregister(&pseries_ioei_notifier_list, &event_nb);
59 */
60
61ATOMIC_NOTIFIER_HEAD(pseries_ioei_notifier_list);
62EXPORT_SYMBOL_GPL(pseries_ioei_notifier_list);
63
64static int ioei_check_exception_token;
65
66/* pSeries event log format */
67
68/* Two bytes ASCII section IDs */
69#define PSERIES_ELOG_SECT_ID_PRIV_HDR (('P' << 8) | 'H')
70#define PSERIES_ELOG_SECT_ID_USER_HDR (('U' << 8) | 'H')
71#define PSERIES_ELOG_SECT_ID_PRIMARY_SRC (('P' << 8) | 'S')
72#define PSERIES_ELOG_SECT_ID_EXTENDED_UH (('E' << 8) | 'H')
73#define PSERIES_ELOG_SECT_ID_FAILING_MTMS (('M' << 8) | 'T')
74#define PSERIES_ELOG_SECT_ID_SECONDARY_SRC (('S' << 8) | 'S')
75#define PSERIES_ELOG_SECT_ID_DUMP_LOCATOR (('D' << 8) | 'H')
76#define PSERIES_ELOG_SECT_ID_FW_ERROR (('S' << 8) | 'W')
77#define PSERIES_ELOG_SECT_ID_IMPACT_PART_ID (('L' << 8) | 'P')
78#define PSERIES_ELOG_SECT_ID_LOGIC_RESOURCE_ID (('L' << 8) | 'R')
79#define PSERIES_ELOG_SECT_ID_HMC_ID (('H' << 8) | 'M')
80#define PSERIES_ELOG_SECT_ID_EPOW (('E' << 8) | 'P')
81#define PSERIES_ELOG_SECT_ID_IO_EVENT (('I' << 8) | 'E')
82#define PSERIES_ELOG_SECT_ID_MANUFACT_INFO (('M' << 8) | 'I')
83#define PSERIES_ELOG_SECT_ID_CALL_HOME (('C' << 8) | 'H')
84#define PSERIES_ELOG_SECT_ID_USER_DEF (('U' << 8) | 'D')
85
86/* Vendor specific Platform Event Log Format, Version 6, section header */
87struct pseries_elog_section {
88 uint16_t id; /* 0x00 2-byte ASCII section ID */
89 uint16_t length; /* 0x02 Section length in bytes */
90 uint8_t version; /* 0x04 Section version */
91 uint8_t subtype; /* 0x05 Section subtype */
92 uint16_t creator_component; /* 0x06 Creator component ID */
93 uint8_t data[]; /* 0x08 Start of section data */
94};
95
96static char ioei_rtas_buf[RTAS_DATA_BUF_SIZE] __cacheline_aligned;
97
98/**
99 * Find data portion of a specific section in RTAS extended event log.
100 * @elog: RTAS error/event log.
101 * @sect_id: secsion ID.
102 *
103 * Return:
104 * pointer to the section data of the specified section
105 * NULL if not found
106 */
107static struct pseries_elog_section *find_xelog_section(struct rtas_error_log *elog,
108 uint16_t sect_id)
109{
110 struct rtas_ext_event_log_v6 *xelog =
111 (struct rtas_ext_event_log_v6 *) elog->buffer;
112 struct pseries_elog_section *sect;
113 unsigned char *p, *log_end;
114
115 /* Check that we understand the format */
116 if (elog->extended_log_length < sizeof(struct rtas_ext_event_log_v6) ||
117 xelog->log_format != RTAS_V6EXT_LOG_FORMAT_EVENT_LOG ||
118 xelog->company_id != RTAS_V6EXT_COMPANY_ID_IBM)
119 return NULL;
120
121 log_end = elog->buffer + elog->extended_log_length;
122 p = xelog->vendor_log;
123 while (p < log_end) {
124 sect = (struct pseries_elog_section *)p;
125 if (sect->id == sect_id)
126 return sect;
127 p += sect->length;
128 }
129 return NULL;
130}
131
132/**
133 * Find the data portion of an IO Event section from event log.
134 * @elog: RTAS error/event log.
135 *
136 * Return:
137 * pointer to a valid IO event section data. NULL if not found.
138 */
139static struct pseries_io_event * ioei_find_event(struct rtas_error_log *elog)
140{
141 struct pseries_elog_section *sect;
142
143 /* We should only ever get called for io-event interrupts, but if
144 * we do get called for another type then something went wrong so
145 * make some noise about it.
146 * RTAS_TYPE_IO only exists in extended event log version 6 or later.
147 * No need to check event log version.
148 */
149 if (unlikely(elog->type != RTAS_TYPE_IO)) {
150 printk_once(KERN_WARNING "io_event_irq: Unexpected event type %d",
151 elog->type);
152 return NULL;
153 }
154
155 sect = find_xelog_section(elog, PSERIES_ELOG_SECT_ID_IO_EVENT);
156 if (unlikely(!sect)) {
157 printk_once(KERN_WARNING "io_event_irq: RTAS extended event "
158 "log does not contain an IO Event section. "
159 "Could be a bug in system firmware!\n");
160 return NULL;
161 }
162 return (struct pseries_io_event *) &sect->data;
163}
164
165/*
166 * PAPR:
167 * - check-exception returns the first found error or event and clear that
168 * error or event so it is reported once.
169 * - Each interrupt returns one event. If a plateform chooses to report
170 * multiple events through a single interrupt, it must ensure that the
171 * interrupt remains asserted until check-exception has been used to
172 * process all out-standing events for that interrupt.
173 *
174 * Implementation notes:
175 * - Events must be processed in the order they are returned. Hence,
176 * sequential in nature.
177 * - The owner of an event is determined by combinations of scope,
178 * event type, and sub-type. There is no easy way to pre-sort clients
179 * by scope or event type alone. For example, Torrent ISR route change
180 * event is reported with scope 0x00 (Not Applicatable) rather than
181 * 0x3B (Torrent-hub). It is better to let the clients to identify
182 * who owns the the event.
183 */
184
185static irqreturn_t ioei_interrupt(int irq, void *dev_id)
186{
187 struct pseries_io_event *event;
188 int rtas_rc;
189
190 for (;;) {
191 rtas_rc = rtas_call(ioei_check_exception_token, 6, 1, NULL,
192 RTAS_VECTOR_EXTERNAL_INTERRUPT,
193 virq_to_hw(irq),
194 RTAS_IO_EVENTS, 1 /* Time Critical */,
195 __pa(ioei_rtas_buf),
196 RTAS_DATA_BUF_SIZE);
197 if (rtas_rc != 0)
198 break;
199
200 event = ioei_find_event((struct rtas_error_log *)ioei_rtas_buf);
201 if (!event)
202 continue;
203
204 atomic_notifier_call_chain(&pseries_ioei_notifier_list,
205 0, event);
206 }
207 return IRQ_HANDLED;
208}
209
210static int __init ioei_init(void)
211{
212 struct device_node *np;
213
214 ioei_check_exception_token = rtas_token("check-exception");
215 if (ioei_check_exception_token == RTAS_UNKNOWN_SERVICE) {
216 pr_warning("IO Event IRQ not supported on this system !\n");
217 return -ENODEV;
218 }
219 np = of_find_node_by_path("/event-sources/ibm,io-events");
220 if (np) {
221 request_event_sources_irqs(np, ioei_interrupt, "IO_EVENT");
222 of_node_put(np);
223 } else {
224 pr_err("io_event_irq: No ibm,io-events on system! "
225 "IO Event interrupt disabled.\n");
226 return -ENODEV;
227 }
228 return 0;
229}
230machine_subsys_initcall(pseries, ioei_init);
231