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 *) &sect->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

Reply via email to