Path: blob/master/arch/powerpc/platforms/pseries/io_event_irq.c
10818 views
/*1* Copyright 2010 2011 Mark Nelson and Tseng-Hui (Frank) Lin, IBM Corporation2*3* This program is free software; you can redistribute it and/or4* modify it under the terms of the GNU General Public License5* as published by the Free Software Foundation; either version6* 2 of the License, or (at your option) any later version.7*/89#include <linux/errno.h>10#include <linux/slab.h>11#include <linux/module.h>12#include <linux/irq.h>13#include <linux/interrupt.h>14#include <linux/of.h>15#include <linux/list.h>16#include <linux/notifier.h>1718#include <asm/machdep.h>19#include <asm/rtas.h>20#include <asm/irq.h>21#include <asm/io_event_irq.h>2223#include "pseries.h"2425/*26* IO event interrupt is a mechanism provided by RTAS to return27* information about hardware error and non-error events. Device28* drivers can register their event handlers to receive events.29* Device drivers are expected to use atomic_notifier_chain_register()30* and atomic_notifier_chain_unregister() to register and unregister31* their event handlers. Since multiple IO event types and scopes32* share an IO event interrupt, the event handlers are called one33* by one until the IO event is claimed by one of the handlers.34* The event handlers are expected to return NOTIFY_OK if the35* event is handled by the event handler or NOTIFY_DONE if the36* event does not belong to the handler.37*38* Usage:39*40* Notifier function:41* #include <asm/io_event_irq.h>42* int event_handler(struct notifier_block *nb, unsigned long val, void *data) {43* p = (struct pseries_io_event_sect_data *) data;44* if (! is_my_event(p->scope, p->event_type)) return NOTIFY_DONE;45* :46* :47* return NOTIFY_OK;48* }49* struct notifier_block event_nb = {50* .notifier_call = event_handler,51* }52*53* Registration:54* atomic_notifier_chain_register(&pseries_ioei_notifier_list, &event_nb);55*56* Unregistration:57* atomic_notifier_chain_unregister(&pseries_ioei_notifier_list, &event_nb);58*/5960ATOMIC_NOTIFIER_HEAD(pseries_ioei_notifier_list);61EXPORT_SYMBOL_GPL(pseries_ioei_notifier_list);6263static int ioei_check_exception_token;6465/* pSeries event log format */6667/* Two bytes ASCII section IDs */68#define PSERIES_ELOG_SECT_ID_PRIV_HDR (('P' << 8) | 'H')69#define PSERIES_ELOG_SECT_ID_USER_HDR (('U' << 8) | 'H')70#define PSERIES_ELOG_SECT_ID_PRIMARY_SRC (('P' << 8) | 'S')71#define PSERIES_ELOG_SECT_ID_EXTENDED_UH (('E' << 8) | 'H')72#define PSERIES_ELOG_SECT_ID_FAILING_MTMS (('M' << 8) | 'T')73#define PSERIES_ELOG_SECT_ID_SECONDARY_SRC (('S' << 8) | 'S')74#define PSERIES_ELOG_SECT_ID_DUMP_LOCATOR (('D' << 8) | 'H')75#define PSERIES_ELOG_SECT_ID_FW_ERROR (('S' << 8) | 'W')76#define PSERIES_ELOG_SECT_ID_IMPACT_PART_ID (('L' << 8) | 'P')77#define PSERIES_ELOG_SECT_ID_LOGIC_RESOURCE_ID (('L' << 8) | 'R')78#define PSERIES_ELOG_SECT_ID_HMC_ID (('H' << 8) | 'M')79#define PSERIES_ELOG_SECT_ID_EPOW (('E' << 8) | 'P')80#define PSERIES_ELOG_SECT_ID_IO_EVENT (('I' << 8) | 'E')81#define PSERIES_ELOG_SECT_ID_MANUFACT_INFO (('M' << 8) | 'I')82#define PSERIES_ELOG_SECT_ID_CALL_HOME (('C' << 8) | 'H')83#define PSERIES_ELOG_SECT_ID_USER_DEF (('U' << 8) | 'D')8485/* Vendor specific Platform Event Log Format, Version 6, section header */86struct pseries_elog_section {87uint16_t id; /* 0x00 2-byte ASCII section ID */88uint16_t length; /* 0x02 Section length in bytes */89uint8_t version; /* 0x04 Section version */90uint8_t subtype; /* 0x05 Section subtype */91uint16_t creator_component; /* 0x06 Creator component ID */92uint8_t data[]; /* 0x08 Start of section data */93};9495static char ioei_rtas_buf[RTAS_DATA_BUF_SIZE] __cacheline_aligned;9697/**98* Find data portion of a specific section in RTAS extended event log.99* @elog: RTAS error/event log.100* @sect_id: secsion ID.101*102* Return:103* pointer to the section data of the specified section104* NULL if not found105*/106static struct pseries_elog_section *find_xelog_section(struct rtas_error_log *elog,107uint16_t sect_id)108{109struct rtas_ext_event_log_v6 *xelog =110(struct rtas_ext_event_log_v6 *) elog->buffer;111struct pseries_elog_section *sect;112unsigned char *p, *log_end;113114/* Check that we understand the format */115if (elog->extended_log_length < sizeof(struct rtas_ext_event_log_v6) ||116xelog->log_format != RTAS_V6EXT_LOG_FORMAT_EVENT_LOG ||117xelog->company_id != RTAS_V6EXT_COMPANY_ID_IBM)118return NULL;119120log_end = elog->buffer + elog->extended_log_length;121p = xelog->vendor_log;122while (p < log_end) {123sect = (struct pseries_elog_section *)p;124if (sect->id == sect_id)125return sect;126p += sect->length;127}128return NULL;129}130131/**132* Find the data portion of an IO Event section from event log.133* @elog: RTAS error/event log.134*135* Return:136* pointer to a valid IO event section data. NULL if not found.137*/138static struct pseries_io_event * ioei_find_event(struct rtas_error_log *elog)139{140struct pseries_elog_section *sect;141142/* We should only ever get called for io-event interrupts, but if143* we do get called for another type then something went wrong so144* make some noise about it.145* RTAS_TYPE_IO only exists in extended event log version 6 or later.146* No need to check event log version.147*/148if (unlikely(elog->type != RTAS_TYPE_IO)) {149printk_once(KERN_WARNING "io_event_irq: Unexpected event type %d",150elog->type);151return NULL;152}153154sect = find_xelog_section(elog, PSERIES_ELOG_SECT_ID_IO_EVENT);155if (unlikely(!sect)) {156printk_once(KERN_WARNING "io_event_irq: RTAS extended event "157"log does not contain an IO Event section. "158"Could be a bug in system firmware!\n");159return NULL;160}161return (struct pseries_io_event *) §->data;162}163164/*165* PAPR:166* - check-exception returns the first found error or event and clear that167* error or event so it is reported once.168* - Each interrupt returns one event. If a plateform chooses to report169* multiple events through a single interrupt, it must ensure that the170* interrupt remains asserted until check-exception has been used to171* process all out-standing events for that interrupt.172*173* Implementation notes:174* - Events must be processed in the order they are returned. Hence,175* sequential in nature.176* - The owner of an event is determined by combinations of scope,177* event type, and sub-type. There is no easy way to pre-sort clients178* by scope or event type alone. For example, Torrent ISR route change179* event is reported with scope 0x00 (Not Applicatable) rather than180* 0x3B (Torrent-hub). It is better to let the clients to identify181* who owns the the event.182*/183184static irqreturn_t ioei_interrupt(int irq, void *dev_id)185{186struct pseries_io_event *event;187int rtas_rc;188189for (;;) {190rtas_rc = rtas_call(ioei_check_exception_token, 6, 1, NULL,191RTAS_VECTOR_EXTERNAL_INTERRUPT,192virq_to_hw(irq),193RTAS_IO_EVENTS, 1 /* Time Critical */,194__pa(ioei_rtas_buf),195RTAS_DATA_BUF_SIZE);196if (rtas_rc != 0)197break;198199event = ioei_find_event((struct rtas_error_log *)ioei_rtas_buf);200if (!event)201continue;202203atomic_notifier_call_chain(&pseries_ioei_notifier_list,2040, event);205}206return IRQ_HANDLED;207}208209static int __init ioei_init(void)210{211struct device_node *np;212213ioei_check_exception_token = rtas_token("check-exception");214if (ioei_check_exception_token == RTAS_UNKNOWN_SERVICE) {215pr_warning("IO Event IRQ not supported on this system !\n");216return -ENODEV;217}218np = of_find_node_by_path("/event-sources/ibm,io-events");219if (np) {220request_event_sources_irqs(np, ioei_interrupt, "IO_EVENT");221of_node_put(np);222} else {223pr_err("io_event_irq: No ibm,io-events on system! "224"IO Event interrupt disabled.\n");225return -ENODEV;226}227return 0;228}229machine_subsys_initcall(pseries, ioei_init);230231232233