On Thu, 2011-05-05 at 17:32 -0500, Tseng-Hui (Frank) Lin wrote: > From: Tseng-Hui (Frank) Lin <th...@linux.vnet.ibm.com> > > 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. > > Change log: > - Fixed compilation errors > - Fix comments to be docbook compliant > - Fix some code format > > Signed-off-by: Tseng-Hui (Frank) Lin <th...@us.ibm.com> > Signed-off-by: Benjamin Herrenschmidt <b...@kernel.crashing.org> > ---
Next time, please put the change log -after- the "---" so it is not part of what gets committed. I've fixed it up by hand for now. Cheers, Ben. > arch/powerpc/include/asm/io_event_irq.h | 54 ++++++ > arch/powerpc/platforms/pseries/Kconfig | 18 ++ > arch/powerpc/platforms/pseries/Makefile | 1 + > arch/powerpc/platforms/pseries/io_event_irq.c | 231 > +++++++++++++++++++++++++ > 4 files changed, 304 insertions(+), 0 deletions(-) > > diff --git a/arch/powerpc/include/asm/io_event_irq.h > b/arch/powerpc/include/asm/io_event_irq.h > new file mode 100644 > index 0000000..b1a9a1b > --- /dev/null > +++ b/arch/powerpc/include/asm/io_event_irq.h > @@ -0,0 +1,54 @@ > +/* > + * Copyright 2010, 2011 Mark Nelson and Tseng-Hui (Frank) Lin, IBM > Corporation > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version > + * 2 of the License, or (at your option) any later version. > + */ > + > +#ifndef _ASM_POWERPC_IO_EVENT_IRQ_H > +#define _ASM_POWERPC_IO_EVENT_IRQ_H > + > +#include <linux/types.h> > +#include <linux/notifier.h> > + > +#define PSERIES_IOEI_RPC_MAX_LEN 216 > + > +#define PSERIES_IOEI_TYPE_ERR_DETECTED 0x01 > +#define PSERIES_IOEI_TYPE_ERR_RECOVERED 0x02 > +#define PSERIES_IOEI_TYPE_EVENT 0x03 > +#define PSERIES_IOEI_TYPE_RPC_PASS_THRU 0x04 > + > +#define PSERIES_IOEI_SUBTYPE_NOT_APP 0x00 > +#define PSERIES_IOEI_SUBTYPE_REBALANCE_REQ 0x01 > +#define PSERIES_IOEI_SUBTYPE_NODE_ONLINE 0x03 > +#define PSERIES_IOEI_SUBTYPE_NODE_OFFLINE 0x04 > +#define PSERIES_IOEI_SUBTYPE_DUMP_SIZE_CHANGE 0x05 > +#define PSERIES_IOEI_SUBTYPE_TORRENT_IRV_UPDATE 0x06 > +#define PSERIES_IOEI_SUBTYPE_TORRENT_HFI_CFGED 0x07 > + > +#define PSERIES_IOEI_SCOPE_NOT_APP 0x00 > +#define PSERIES_IOEI_SCOPE_RIO_HUB 0x36 > +#define PSERIES_IOEI_SCOPE_RIO_BRIDGE 0x37 > +#define PSERIES_IOEI_SCOPE_PHB 0x38 > +#define PSERIES_IOEI_SCOPE_EADS_GLOBAL 0x39 > +#define PSERIES_IOEI_SCOPE_EADS_SLOT 0x3A > +#define PSERIES_IOEI_SCOPE_TORRENT_HUB 0x3B > +#define PSERIES_IOEI_SCOPE_SERVICE_PROC 0x51 > + > +/* Platform Event Log Format, Version 6, data portition of IO event section > */ > +struct pseries_io_event { > + uint8_t event_type; /* 0x00 IO-Event Type */ > + uint8_t rpc_data_len; /* 0x01 RPC data length */ > + uint8_t scope; /* 0x02 Error/Event Scope */ > + uint8_t event_subtype; /* 0x03 I/O-Event Sub-Type */ > + uint32_t drc_index; /* 0x04 DRC Index */ > + uint8_t rpc_data[PSERIES_IOEI_RPC_MAX_LEN]; > + /* 0x08 RPC Data (0-216 bytes, */ > + /* padded to 4 bytes alignment) */ > +}; > + > +extern struct atomic_notifier_head pseries_ioei_notifier_list; > + > +#endif /* _ASM_POWERPC_IO_EVENT_IRQ_H */ > diff --git a/arch/powerpc/platforms/pseries/Kconfig > b/arch/powerpc/platforms/pseries/Kconfig > index b044922..71af4c5 100644 > --- a/arch/powerpc/platforms/pseries/Kconfig > +++ b/arch/powerpc/platforms/pseries/Kconfig > @@ -50,6 +50,24 @@ config SCANLOG > tristate "Scanlog dump interface" > depends on RTAS_PROC && PPC_PSERIES > > +config IO_EVENT_IRQ > + bool "IO Event Interrupt support" > + depends on PPC_PSERIES > + default y > + help > + Select this option, if you want to enable support for IO Event > + interrupts. IO event interrupt is a mechanism provided by RTAS > + to return information about hardware error and non-error events > + which may need OS attention. RTAS returns events for multiple > + event types and scopes. Device drivers can register their handlers > + to receive events. > + > + This option will only enable the IO event platform code. You > + will still need to enable or compile the actual drivers > + that use this infrastruture to handle IO event interrupts. > + > + Say Y if you are unsure. > + > config LPARCFG > bool "LPAR Configuration Data" > depends on PPC_PSERIES || PPC_ISERIES > diff --git a/arch/powerpc/platforms/pseries/Makefile > b/arch/powerpc/platforms/pseries/Makefile > index 4cfefba..3556e40 100644 > --- a/arch/powerpc/platforms/pseries/Makefile > +++ b/arch/powerpc/platforms/pseries/Makefile > @@ -21,6 +21,7 @@ obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o > obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o > obj-$(CONFIG_CMM) += cmm.o > obj-$(CONFIG_DTL) += dtl.o > +obj-$(CONFIG_IO_EVENT_IRQ) += io_event_irq.o > > ifeq ($(CONFIG_PPC_PSERIES),y) > obj-$(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 0000000..c829e60 > --- /dev/null > +++ b/arch/powerpc/platforms/pseries/io_event_irq.c > @@ -0,0 +1,231 @@ > +/* > + * Copyright 2010 2011 Mark Nelson and Tseng-Hui (Frank) Lin, IBM Corporation > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version > + * 2 of the License, or (at your option) any later version. > + */ > + > +#include <linux/errno.h> > +#include <linux/slab.h> > +#include <linux/module.h> > +#include <linux/irq.h> > +#include <linux/interrupt.h> > +#include <linux/of.h> > +#include <linux/list.h> > +#include <linux/notifier.h> > + > +#include <asm/machdep.h> > +#include <asm/rtas.h> > +#include <asm/irq.h> > +#include <asm/io_event_irq.h> > + > +#include "pseries.h" > + > +/* > + * IO event interrupt is a mechanism provided by RTAS to return > + * information about hardware error and non-error events. Device > + * drivers can register their event handlers to receive events. > + * Device drivers are expected to use atomic_notifier_chain_register() > + * and atomic_notifier_chain_unregister() to register and unregister > + * their event handlers. Since multiple IO event types and scopes > + * share an IO event interrupt, the event handlers are called one > + * by one until the IO event is claimed by one of the handlers. > + * The event handlers are expected to return NOTIFY_OK if the > + * event is handled by the event handler or NOTIFY_DONE if the > + * event does not belong to the handler. > + * > + * Usage: > + * > + * Notifier function: > + * #include <asm/io_event_irq.h> > + * int event_handler(struct notifier_block *nb, unsigned long val, void > *data) { > + * p = (struct pseries_io_event_sect_data *) data; > + * if (! is_my_event(p->scope, p->event_type)) return NOTIFY_DONE; > + * : > + * : > + * return NOTIFY_OK; > + * } > + * struct notifier_block event_nb = { > + * .notifier_call = event_handler, > + * } > + * > + * Registration: > + * atomic_notifier_chain_register(&pseries_ioei_notifier_list, &event_nb); > + * > + * Unregistration: > + * atomic_notifier_chain_unregister(&pseries_ioei_notifier_list, &event_nb); > + */ > + > +ATOMIC_NOTIFIER_HEAD(pseries_ioei_notifier_list); > +EXPORT_SYMBOL_GPL(pseries_ioei_notifier_list); > + > +static int ioei_check_exception_token; > + > +/* pSeries event log format */ > + > +/* Two bytes ASCII section IDs */ > +#define PSERIES_ELOG_SECT_ID_PRIV_HDR (('P' << 8) | 'H') > +#define PSERIES_ELOG_SECT_ID_USER_HDR (('U' << 8) | 'H') > +#define PSERIES_ELOG_SECT_ID_PRIMARY_SRC (('P' << 8) | 'S') > +#define PSERIES_ELOG_SECT_ID_EXTENDED_UH (('E' << 8) | 'H') > +#define PSERIES_ELOG_SECT_ID_FAILING_MTMS (('M' << 8) | 'T') > +#define PSERIES_ELOG_SECT_ID_SECONDARY_SRC (('S' << 8) | 'S') > +#define PSERIES_ELOG_SECT_ID_DUMP_LOCATOR (('D' << 8) | 'H') > +#define PSERIES_ELOG_SECT_ID_FW_ERROR (('S' << 8) | 'W') > +#define PSERIES_ELOG_SECT_ID_IMPACT_PART_ID (('L' << 8) | 'P') > +#define PSERIES_ELOG_SECT_ID_LOGIC_RESOURCE_ID (('L' << 8) | 'R') > +#define PSERIES_ELOG_SECT_ID_HMC_ID (('H' << 8) | 'M') > +#define PSERIES_ELOG_SECT_ID_EPOW (('E' << 8) | 'P') > +#define PSERIES_ELOG_SECT_ID_IO_EVENT (('I' << 8) | 'E') > +#define PSERIES_ELOG_SECT_ID_MANUFACT_INFO (('M' << 8) | 'I') > +#define PSERIES_ELOG_SECT_ID_CALL_HOME (('C' << 8) | 'H') > +#define PSERIES_ELOG_SECT_ID_USER_DEF (('U' << 8) | 'D') > + > +/* Vendor specific Platform Event Log Format, Version 6, section header */ > +struct pseries_elog_section { > + uint16_t id; /* 0x00 2-byte ASCII section ID */ > + uint16_t length; /* 0x02 Section length in bytes */ > + uint8_t version; /* 0x04 Section version */ > + uint8_t subtype; /* 0x05 Section subtype */ > + uint16_t creator_component; /* 0x06 Creator component ID */ > + uint8_t data[]; /* 0x08 Start of section data */ > +}; > + > +static char ioei_rtas_buf[RTAS_DATA_BUF_SIZE] __cacheline_aligned; > + > +/** > + * Find data portion of a specific section in RTAS extended event log. > + * @elog: RTAS error/event log. > + * @sect_id: secsion ID. > + * > + * Return: > + * pointer to the section data of the specified section > + * NULL if not found > + */ > +static struct pseries_elog_section *find_xelog_section(struct rtas_error_log > *elog, > + uint16_t sect_id) > +{ > + struct rtas_ext_event_log_v6 *xelog = > + (struct rtas_ext_event_log_v6 *) elog->buffer; > + struct pseries_elog_section *sect; > + unsigned char *p, *log_end; > + > + /* Check that we understand the format */ > + if (elog->extended_log_length < sizeof(struct rtas_ext_event_log_v6) || > + xelog->log_format != RTAS_V6EXT_LOG_FORMAT_EVENT_LOG || > + xelog->company_id != RTAS_V6EXT_COMPANY_ID_IBM) > + return NULL; > + > + log_end = elog->buffer + elog->extended_log_length; > + p = xelog->vendor_log; > + while (p < log_end) { > + sect = (struct pseries_elog_section *)p; > + if (sect->id == sect_id) > + return sect; > + p += sect->length; > + } > + return NULL; > +} > + > +/** > + * Find the data portion of an IO Event section from event log. > + * @elog: RTAS error/event log. > + * > + * Return: > + * pointer to a valid IO event section data. NULL if not found. > + */ > +static struct pseries_io_event * ioei_find_event(struct rtas_error_log *elog) > +{ > + struct pseries_elog_section *sect; > + > + /* We should only ever get called for io-event interrupts, but if > + * we do get called for another type then something went wrong so > + * make some noise about it. > + * RTAS_TYPE_IO only exists in extended event log version 6 or later. > + * No need to check event log version. > + */ > + if (unlikely(elog->type != RTAS_TYPE_IO)) { > + printk_once(KERN_WARNING "io_event_irq: Unexpected event type > %d", > + elog->type); > + return NULL; > + } > + > + sect = find_xelog_section(elog, PSERIES_ELOG_SECT_ID_IO_EVENT); > + if (unlikely(!sect)) { > + printk_once(KERN_WARNING "io_event_irq: RTAS extended event " > + "log does not contain an IO Event section. " > + "Could be a bug in system firmware!\n"); > + return NULL; > + } > + return (struct pseries_io_event *) §->data; > +} > + > +/* > + * PAPR: > + * - check-exception returns the first found error or event and clear that > + * error or event so it is reported once. > + * - Each interrupt returns one event. If a plateform chooses to report > + * multiple events through a single interrupt, it must ensure that the > + * interrupt remains asserted until check-exception has been used to > + * process all out-standing events for that interrupt. > + * > + * Implementation notes: > + * - Events must be processed in the order they are returned. Hence, > + * sequential in nature. > + * - The owner of an event is determined by combinations of scope, > + * event type, and sub-type. There is no easy way to pre-sort clients > + * by scope or event type alone. For example, Torrent ISR route change > + * event is reported with scope 0x00 (Not Applicatable) rather than > + * 0x3B (Torrent-hub). It is better to let the clients to identify > + * who owns the the event. > + */ > + > +static irqreturn_t ioei_interrupt(int irq, void *dev_id) > +{ > + struct pseries_io_event *event; > + int rtas_rc; > + > + for (;;) { > + rtas_rc = rtas_call(ioei_check_exception_token, 6, 1, NULL, > + RTAS_VECTOR_EXTERNAL_INTERRUPT, > + virq_to_hw(irq), > + RTAS_IO_EVENTS, 1 /* Time Critical */, > + __pa(ioei_rtas_buf), > + RTAS_DATA_BUF_SIZE); > + if (rtas_rc != 0) > + break; > + > + event = ioei_find_event((struct rtas_error_log *)ioei_rtas_buf); > + if (!event) > + continue; > + > + atomic_notifier_call_chain(&pseries_ioei_notifier_list, > + 0, event); > + } > + return IRQ_HANDLED; > +} > + > +static int __init ioei_init(void) > +{ > + struct device_node *np; > + > + ioei_check_exception_token = rtas_token("check-exception"); > + if (ioei_check_exception_token == RTAS_UNKNOWN_SERVICE) { > + pr_warning("IO Event IRQ not supported on this system !\n"); > + return -ENODEV; > + } > + np = of_find_node_by_path("/event-sources/ibm,io-events"); > + if (np) { > + request_event_sources_irqs(np, ioei_interrupt, "IO_EVENT"); > + of_node_put(np); > + } else { > + pr_err("io_event_irq: No ibm,io-events on system! " > + "IO Event interrupt disabled.\n"); > + return -ENODEV; > + } > + return 0; > +} > +machine_subsys_initcall(pseries, ioei_init); > + > _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev