Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tseng-Hui (Frank) Lin and committed by
Benjamin Herrenschmidt
77eafe10 4cb46380

+304
+54
arch/powerpc/include/asm/io_event_irq.h
··· 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 + #ifndef _ASM_POWERPC_IO_EVENT_IRQ_H 11 + #define _ASM_POWERPC_IO_EVENT_IRQ_H 12 + 13 + #include <linux/types.h> 14 + #include <linux/notifier.h> 15 + 16 + #define PSERIES_IOEI_RPC_MAX_LEN 216 17 + 18 + #define PSERIES_IOEI_TYPE_ERR_DETECTED 0x01 19 + #define PSERIES_IOEI_TYPE_ERR_RECOVERED 0x02 20 + #define PSERIES_IOEI_TYPE_EVENT 0x03 21 + #define PSERIES_IOEI_TYPE_RPC_PASS_THRU 0x04 22 + 23 + #define PSERIES_IOEI_SUBTYPE_NOT_APP 0x00 24 + #define PSERIES_IOEI_SUBTYPE_REBALANCE_REQ 0x01 25 + #define PSERIES_IOEI_SUBTYPE_NODE_ONLINE 0x03 26 + #define PSERIES_IOEI_SUBTYPE_NODE_OFFLINE 0x04 27 + #define PSERIES_IOEI_SUBTYPE_DUMP_SIZE_CHANGE 0x05 28 + #define PSERIES_IOEI_SUBTYPE_TORRENT_IRV_UPDATE 0x06 29 + #define PSERIES_IOEI_SUBTYPE_TORRENT_HFI_CFGED 0x07 30 + 31 + #define PSERIES_IOEI_SCOPE_NOT_APP 0x00 32 + #define PSERIES_IOEI_SCOPE_RIO_HUB 0x36 33 + #define PSERIES_IOEI_SCOPE_RIO_BRIDGE 0x37 34 + #define PSERIES_IOEI_SCOPE_PHB 0x38 35 + #define PSERIES_IOEI_SCOPE_EADS_GLOBAL 0x39 36 + #define PSERIES_IOEI_SCOPE_EADS_SLOT 0x3A 37 + #define PSERIES_IOEI_SCOPE_TORRENT_HUB 0x3B 38 + #define PSERIES_IOEI_SCOPE_SERVICE_PROC 0x51 39 + 40 + /* Platform Event Log Format, Version 6, data portition of IO event section */ 41 + struct pseries_io_event { 42 + uint8_t event_type; /* 0x00 IO-Event Type */ 43 + uint8_t rpc_data_len; /* 0x01 RPC data length */ 44 + uint8_t scope; /* 0x02 Error/Event Scope */ 45 + uint8_t event_subtype; /* 0x03 I/O-Event Sub-Type */ 46 + uint32_t drc_index; /* 0x04 DRC Index */ 47 + uint8_t rpc_data[PSERIES_IOEI_RPC_MAX_LEN]; 48 + /* 0x08 RPC Data (0-216 bytes, */ 49 + /* padded to 4 bytes alignment) */ 50 + }; 51 + 52 + extern struct atomic_notifier_head pseries_ioei_notifier_list; 53 + 54 + #endif /* _ASM_POWERPC_IO_EVENT_IRQ_H */
+18
arch/powerpc/platforms/pseries/Kconfig
··· 50 50 tristate "Scanlog dump interface" 51 51 depends on RTAS_PROC && PPC_PSERIES 52 52 53 + config 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 + 53 71 config LPARCFG 54 72 bool "LPAR Configuration Data" 55 73 depends on PPC_PSERIES || PPC_ISERIES
+1
arch/powerpc/platforms/pseries/Makefile
··· 21 21 obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o 22 22 obj-$(CONFIG_CMM) += cmm.o 23 23 obj-$(CONFIG_DTL) += dtl.o 24 + obj-$(CONFIG_IO_EVENT_IRQ) += io_event_irq.o 24 25 25 26 ifeq ($(CONFIG_PPC_PSERIES),y) 26 27 obj-$(CONFIG_SUSPEND) += suspend.o
+231
arch/powerpc/platforms/pseries/io_event_irq.c
··· 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 + 61 + ATOMIC_NOTIFIER_HEAD(pseries_ioei_notifier_list); 62 + EXPORT_SYMBOL_GPL(pseries_ioei_notifier_list); 63 + 64 + static 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 */ 87 + struct 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 + 96 + static 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 + */ 107 + static 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 + */ 139 + static 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 + 185 + static 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 + 210 + static 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 + } 230 + machine_subsys_initcall(pseries, ioei_init); 231 +