LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 08/15] powerpc/mm: Add new hash_page_mm()
From: Aneesh Kumar K.V @ 2014-09-29  8:50 UTC (permalink / raw)
  To: Michael Neuling, greg, arnd, mpe, benh
  Cc: cbe-oss-dev, mikey, linux-kernel, linuxppc-dev, jk, imunsie,
	anton
In-Reply-To: <1411028820-29933-9-git-send-email-mikey@neuling.org>

Michael Neuling <mikey@neuling.org> writes:

> From: Ian Munsie <imunsie@au1.ibm.com>
>
> This adds a new function hash_page_mm() based on the existing hash_page().
> This version allows any struct mm to be passed in, rather than assuming
> current.  This is useful for servicing co-processor faults which are not in the
> context of the current running process.
>
> We need to be careful here as the current hash_page() assumes current in a few
> places.

Can you also explain calling semantics. ie, why would we want to call
this with anything other than current ? Should we flush slb now or
should it be skipped ? so what would happen if the new hash page can
result in segment demotion ?  You don't put that under if (mm ==
current->mm). is that ok ?

	if ((pte_val(*ptep) & _PAGE_4K_PFN) && psize == MMU_PAGE_64K) {
		demote_segment_4k(mm, ea);
		psize = MMU_PAGE_4K;
	}

We also update paca context there

	if (get_paca_psize(addr) != MMU_PAGE_4K) {
		get_paca()->context = mm->context;
		slb_flush_and_rebolt();
	}


You also added code to handle KERNEL_REGION_ID in
[PATCH 02/15] powerpc/cell: Move data segment faulting code out of cell
platform. do we need to handle that here ?


>
> Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
> Signed-off-by: Michael Neuling <mikey@neuling.org>
> ---
>  arch/powerpc/include/asm/mmu-hash64.h |  1 +
>  arch/powerpc/mm/hash_utils_64.c       | 20 +++++++++++++-------
>  2 files changed, 14 insertions(+), 7 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/mmu-hash64.h b/arch/powerpc/include/asm/mmu-hash64.h
> index fd19a53..a3b85e9 100644
> --- a/arch/powerpc/include/asm/mmu-hash64.h
> +++ b/arch/powerpc/include/asm/mmu-hash64.h
> @@ -319,6 +319,7 @@ extern int __hash_page_64K(unsigned long ea, unsigned long access,
>  			   unsigned int local, int ssize);
>  struct mm_struct;
>  unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap);
> +extern int hash_page_mm(struct mm_struct *mm, unsigned long ea, unsigned long access, unsigned long trap);
>  extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap);
>  int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid,
>  		     pte_t *ptep, unsigned long trap, int local, int ssize,
> diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
> index 0f73367..66071af 100644
> --- a/arch/powerpc/mm/hash_utils_64.c
> +++ b/arch/powerpc/mm/hash_utils_64.c
> @@ -991,26 +991,24 @@ static void check_paca_psize(unsigned long ea, struct mm_struct *mm,
>   * -1 - critical hash insertion error
>   * -2 - access not permitted by subpage protection mechanism
>   */
> -int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
> +int hash_page_mm(struct mm_struct *mm, unsigned long ea, unsigned long access, unsigned long trap)
>  {
>  	enum ctx_state prev_state = exception_enter();
>  	pgd_t *pgdir;
>  	unsigned long vsid;
> -	struct mm_struct *mm;
>  	pte_t *ptep;
>  	unsigned hugeshift;
>  	const struct cpumask *tmp;
>  	int rc, user_region = 0, local = 0;
>  	int psize, ssize;
>  
> -	DBG_LOW("hash_page(ea=%016lx, access=%lx, trap=%lx\n",
> -		ea, access, trap);
> +	DBG_LOW("%s(ea=%016lx, access=%lx, trap=%lx\n",
> +		__func__, ea, access, trap);
>  
>  	/* Get region & vsid */
>   	switch (REGION_ID(ea)) {
>  	case USER_REGION_ID:
>  		user_region = 1;
> -		mm = current->mm;
>  		if (! mm) {
>  			DBG_LOW(" user region with no mm !\n");
>  			rc = 1;
> @@ -1106,7 +1104,8 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
>  			WARN_ON(1);
>  		}
>  #endif
> -		check_paca_psize(ea, mm, psize, user_region);
> +		if (current->mm == mm)
> +			check_paca_psize(ea, mm, psize, user_region);
>  
>  		goto bail;
>  	}
> @@ -1149,7 +1148,8 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
>  		}
>  	}
>  
> -	check_paca_psize(ea, mm, psize, user_region);
> +	if (current->mm == mm)
> +		check_paca_psize(ea, mm, psize, user_region);
>  #endif /* CONFIG_PPC_64K_PAGES */
>  
>  #ifdef CONFIG_PPC_HAS_HASH_64K
> @@ -1184,6 +1184,12 @@ bail:
>  	exception_exit(prev_state);
>  	return rc;
>  }
> +EXPORT_SYMBOL_GPL(hash_page_mm);
> +
> +int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
> +{
> +	return hash_page_mm(current->mm, ea, access, trap);
> +}
>  EXPORT_SYMBOL_GPL(hash_page);
>  
>  void hash_preload(struct mm_struct *mm, unsigned long ea,
> -- 
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply

* Re: [PATCH 10/15] powerpc/mm: Add hooks for cxl
From: Aneesh Kumar K.V @ 2014-09-29  9:10 UTC (permalink / raw)
  To: Michael Neuling, greg, arnd, mpe, benh
  Cc: cbe-oss-dev, mikey, linux-kernel, linuxppc-dev, jk, imunsie,
	anton
In-Reply-To: <1411028820-29933-11-git-send-email-mikey@neuling.org>

Michael Neuling <mikey@neuling.org> writes:

> From: Ian Munsie <imunsie@au1.ibm.com>
>
> This add a hook into tlbie() so that we use global invalidations when there are
> cxl contexts active.
>
> Normally cxl snoops broadcast tlbie.  cxl can have TLB entries invalidated via
> MMIO, but we aren't doing that yet.  So for now we are just disabling local
> tlbies when cxl contexts are active.  In future we can make tlbie() local mode
> smarter so that it invalidates cxl contexts explicitly when it needs to.
>
> This also adds a hooks for when SLBs are invalidated to ensure any
> corresponding SLBs in cxl are also invalidated at the same time.

We are not really invalidating cx1 SLB's when we are doing
slb_flush_and_rebolt(). May be add some code documentation around to
explain when we are invalidating cx1 slb here. ?

>
> Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
> Signed-off-by: Michael Neuling <mikey@neuling.org>
> ---
>  arch/powerpc/mm/hash_native_64.c | 6 +++++-
>  arch/powerpc/mm/hash_utils_64.c  | 3 +++
>  arch/powerpc/mm/slice.c          | 3 +++
>  3 files changed, 11 insertions(+), 1 deletion(-)
>
> diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c
> index afc0a82..ae4962a 100644
> --- a/arch/powerpc/mm/hash_native_64.c
> +++ b/arch/powerpc/mm/hash_native_64.c
> @@ -29,6 +29,8 @@
>  #include <asm/kexec.h>
>  #include <asm/ppc-opcode.h>
>  
> +#include <misc/cxl.h>
> +
>  #ifdef DEBUG_LOW
>  #define DBG_LOW(fmt...) udbg_printf(fmt)
>  #else
> @@ -149,9 +151,11 @@ static inline void __tlbiel(unsigned long vpn, int psize, int apsize, int ssize)
>  static inline void tlbie(unsigned long vpn, int psize, int apsize,
>  			 int ssize, int local)
>  {
> -	unsigned int use_local = local && mmu_has_feature(MMU_FTR_TLBIEL);
> +	unsigned int use_local;
>  	int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
>  
> +	use_local = local && mmu_has_feature(MMU_FTR_TLBIEL) && !cxl_ctx_in_use();
> +
>  	if (use_local)
>  		use_local = mmu_psize_defs[psize].tlbiel;
>  	if (lock_tlbie && !use_local)
> diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
> index 66071af..be40ff7 100644
> --- a/arch/powerpc/mm/hash_utils_64.c
> +++ b/arch/powerpc/mm/hash_utils_64.c
> @@ -34,6 +34,7 @@
>  #include <linux/signal.h>
>  #include <linux/memblock.h>
>  #include <linux/context_tracking.h>
> +#include <misc/cxl.h>
>  
>  #include <asm/processor.h>
>  #include <asm/pgtable.h>
> @@ -906,6 +907,7 @@ void demote_segment_4k(struct mm_struct *mm, unsigned long addr)
>  #ifdef CONFIG_SPU_BASE
>  	spu_flush_all_slbs(mm);
>  #endif
> +	cxl_slbia(mm);
>  	if (get_paca_psize(addr) != MMU_PAGE_4K) {
>  		get_paca()->context = mm->context;
>  		slb_flush_and_rebolt();
> @@ -1145,6 +1147,7 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea, unsigned long access, u
>  #ifdef CONFIG_SPU_BASE
>  			spu_flush_all_slbs(mm);
>  #endif
> +			cxl_slbia(mm);
>  		}
>  	}
>  
> diff --git a/arch/powerpc/mm/slice.c b/arch/powerpc/mm/slice.c
> index b0c75cc..4d3a34b 100644
> --- a/arch/powerpc/mm/slice.c
> +++ b/arch/powerpc/mm/slice.c
> @@ -30,6 +30,7 @@
>  #include <linux/err.h>
>  #include <linux/spinlock.h>
>  #include <linux/export.h>
> +#include <misc/cxl.h>
>  #include <asm/mman.h>
>  #include <asm/mmu.h>
>  #include <asm/spu.h>
> @@ -235,6 +236,7 @@ static void slice_convert(struct mm_struct *mm, struct slice_mask mask, int psiz
>  #ifdef CONFIG_SPU_BASE
>  	spu_flush_all_slbs(mm);
>  #endif
> +	cxl_slbia(mm);
>  }
>  
>  /*
> @@ -674,6 +676,7 @@ void slice_set_psize(struct mm_struct *mm, unsigned long address,
>  #ifdef CONFIG_SPU_BASE
>  	spu_flush_all_slbs(mm);
>  #endif
> +	cxl_slbia(mm);
>  }
>  
>  void slice_set_range_psize(struct mm_struct *mm, unsigned long start,
> -- 
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply

* Re: [PATCH v2 00/22] Use MSI chip framework to configure MSI/MSI-X in all platforms
From: Liviu Dudau @ 2014-09-29  9:26 UTC (permalink / raw)
  To: Yijing Wang
  Cc: linux-mips, linux-ia64, linux-pci, Bharat.Bhushan, Thierry Reding,
	sparclinux, linux-arch, linux-s390, Russell King, Joerg Roedel,
	x86, Sebastian Ott, xen-devel, arnab.basu, Arnd Bergmann,
	Konrad Rzeszutek Wilk, Chris Metcalf, Bjorn Helgaas,
	Thomas Gleixner, linux-arm-kernel, Thomas Petazzoni, Xinwei Hu,
	Tony Luck, Sergei Shtylyov, linux-kernel, Ralf Baechle, iommu,
	David Vrabel, Wuyun, linuxppc-dev, David S. Miller, Lucas Stach
In-Reply-To: <5428B971.50101@huawei.com>

On Mon, Sep 29, 2014 at 09:44:17AM +0800, Yijing Wang wrote:
> On 2014/9/28 19:21, Liviu Dudau wrote:
> > On Sun, Sep 28, 2014 at 10:16:12AM +0800, Yijing Wang wrote:
> >>>>>> What I would like to see is a way of creating the pci_host_bridge structure outside
> >>>>>> the pci_create_root_bus(). That would then allow us to pass this sort of platform
> >>>>>> details like associated msi_chip into the host bridge and the child busses will
> >>>>>> have an easy way of finding the information needed by finding the root bus and then
> >>>>>> the host bridge structure. Then the generic pci_scan_root_bus() can be used by (mostly)
> >>>>>> everyone and the drivers can remove their kludges that try to work around the
> >>>>>> current limitations.
> >>>>
> >>>> So I think maybe save msi chip in PCI arch sysdata is a good candidate.
> >>>
> >>> Except that arch sysdata at the moment is an opaque pointer. I am all in favour in
> >>> changing the type of sysdata from void* into pci_host_bridge* and arches can wrap their old
> >>> sysdata around the pci_host_bridge*.
> >>
> >> I inspected every arch and found there are almost no common stuff,
> > 
> > I will disagree here. Most (all?) of the structures that are passed as sysdata argument to
> 
> Most.
> 
> > pci_create_root_bus() or pci_scan_root_bus() have a set of resources for storing the MEM and
> > IO ranges, which struct pci_host_bridge already has. So that can be factored out of the
> > arch code. Same for pci_domain_nr. Then there are some variables that are used for communication
> > with the platform code due to convoluted way(s) in which PCI code gets instantiated.
> 
> Yes, currently some archs store MEM and IO resource in pci sysdata, and others not, move the MEM and IO
> resource to pci_host_bride could make code become simple, we can clean up the resource list argument in
> pci scan functions.
> 
> > 
> > What I am arguing here is not that the arch equivalent of pci_host_bridge structure is already
> > common, but that by moving the members that are common out of arch sysdata into pci_host_bridge
> > we will have more commonality and it will be easier to re-factor the code.
> 
> Now, I got it, thanks!
> 
> > 
> >> and generic data struct should
> >> be created in generic PCI code.
> > 
> > Not necessarily. What I have in mind is something like this:
> 
> This is a good idea, what I'm worried is this series is already large, so I think we need to post
> another series to do it.

I wasn't asking to do it here, I was just offering a suggestion (and sharing some experience) when
it comes to handling msi chip in an arch independent way.

> 
> 
> > 
> >  - drivers/pci/ exports pci_init_host_bridge() that does the initialisation of bridge->windows
> >    and anything else that is needed (like find_pci_host_bridge() function).
> >  - arch code does:
> > 
> > 	struct pci_controller {
> > 		struct pci_host_bridge bridge;
> > 		.....
> > 	};
> > 
> > 	#define to_pci_controller(bridge)	container_of(bridge, struct pci_controller, bridge)
> > 
> > 	static inline struct pci_controller *get_host_controller(const struct pci_bus *bus)
> > 	{
> > 		struct pci_host_bridge *bridge = find_pci_host_bridge(bus);
> > 		if (bridge)
> > 			return to_pci_controller(bridge);
> > 
> > 		return NULL;
> > 	}
> > 
> > 	int arch_pci_init(....)
> > 	{
> > 		struct pci_controller *hose;
> > 		....
> > 		hose = kzalloc(sizeof(*hose), GFP_KERNEL);
> > 		pci_init_host_bridge(&hose->bridge);
> > 		....
> > 		pci_scan_root_bus(...., &hose->bridge, &resources);
> > 		....
> > 		return 0;
> > 	}
> > 
> > Then finding the right structure will be easy.
> > 
> >> Another, I don't like associate msi chip and every PCI device, further more,
> >> almost all platforms except arm have only one MSI controller, and currently, PCI enumerating code doesn't need
> >> to know the MSI chip details, like for legacy IRQ, PCI device doesn't need to know which IRQ controller they
> >> should deliver IRQ to. I would think more about it, and hope other PCI guys can give some comments, especially from Bjorn.
> >>
> > 
> > I wasn't suggesing to associate an msi chip with every PCI device, but with the pci_host_bridge.
> > I don't expect a host bridge to have more than one msi chip, so that should be OK. Also, I'm
> > thinking that getting the associated msi chip should be some sort of pci_host_bridge ops function,
> > and for arches that don't care about MSI it doesn't get implemented.
> 
> Currently, a property "msi-parent" was introduced in arm, and all msi chip integrated in irq chip controller will
> be added to of_pci_msi_chip_list. PCI host driver find the match msi chip by its of_node.

OK. But as you might have seen that still implies open coding a separate version of pci_scan_root_bus().

Best regards,
Liviu

> 
> Thanks!
> Yijing.
> 
> > 
> > Best regards,
> > Liviu
> > 
> >  
> >> Thanks!
> >> Yijing.
> >>
> >>>
> >>> Best regards,
> >>> Liviu
> >>>
> >>>>
> >>>>>
> >>>>> I think both issues are orthogonal. Last time I checked a lot of work
> >>>>> was still necessary to unify host bridges enough so that it could be
> >>>>> shared across architectures. But perhaps some of that work has
> >>>>> happened in the meantime.
> >>>>>
> >>>>> But like I said, when you create the root bus, you can easily attach the
> >>>>> MSI chip to it.
> >>>>>
> >>>>> Thierry
> >>>>>
> >>>>
> >>>>
> >>>> -- 
> >>>> Thanks!
> >>>> Yijing
> >>>>
> >>>>
> >>>
> >>
> >>
> >> -- 
> >> Thanks!
> >> Yijing
> >>
> >>
> > 
> 
> 
> -- 
> Thanks!
> Yijing
> 
> 

-- 
-------------------
   .oooO
   (   )
    \ (  Oooo.
     \_) (   )
          ) /
         (_/

 One small step
   for me ...

^ permalink raw reply

* [PATCH] powerpc, powernv: Add OPAL platform event driver
From: Anshuman Khandual @ 2014-09-29 10:12 UTC (permalink / raw)
  To: linuxppc-dev

This patch creates a new OPAL platform event character driver
which will give userspace clients the access to these events
and process them effectively. Following platforms events are
currently supported with this platform driver.

	(1) Environmental and Power Warning (EPOW)
	(2) Delayed Power Off (DPO)

The user interface for this driver is /dev/opal_event character
device file where the user space clients can poll and read for
new opal platform events. The expected sequence of events driven
from user space should be like the following.

	(1) Open the character device file
	(2) Poll on the file for POLLIN event
	(3) When unblocked, must attempt to read PLAT_EVENT_MAX_SIZE size
	(4) Kernel driver will pass at most one opal_plat_event structure
	(5) Poll again for more new events

The driver registers for OPAL messages notifications corresponding to
individual OPAL events. When any of those event messages arrive in the
kernel, the callbacks are called to process them which in turn unblocks
the polling thread on the character device file. The driver also registers
a timer function which will be called after a threshold amount of time to
shutdown the system. The user space client receives the timeout value for
all individual OPAL platform events and hence must prepare the system and
eventually shutdown. In case the user client does not shutdown the system,
the timer function will be called after the threshold and shutdown the
system explicitly.

Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/opal.h                    |  45 +-
 .../include/uapi/asm/opal_platform_events.h        |  90 +++
 arch/powerpc/platforms/powernv/Makefile            |   2 +-
 .../platforms/powernv/opal-platform-events.c       | 737 +++++++++++++++++++++
 arch/powerpc/platforms/powernv/opal-wrappers.S     |   1 +
 arch/powerpc/platforms/powernv/opal.c              |   8 +-
 6 files changed, 880 insertions(+), 3 deletions(-)
 create mode 100644 arch/powerpc/include/uapi/asm/opal_platform_events.h
 create mode 100644 arch/powerpc/platforms/powernv/opal-platform-events.c

diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 86055e5..c134137 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -151,6 +151,7 @@ struct opal_sg_list {
 #define OPAL_HANDLE_HMI				98
 #define OPAL_REGISTER_DUMP_REGION		101
 #define OPAL_UNREGISTER_DUMP_REGION		102
+#define OPAL_GET_DPO_STATUS			105
 
 #ifndef __ASSEMBLY__
 
@@ -249,6 +250,7 @@ enum OpalMessageType {
 	OPAL_MSG_EPOW,
 	OPAL_MSG_SHUTDOWN,
 	OPAL_MSG_HMI_EVT,
+	OPAL_MSG_DPO,
 	OPAL_MSG_TYPE_MAX,
 };
 
@@ -417,6 +419,46 @@ struct opal_msg {
 	__be64 params[8];
 };
 
+/*
+ * EPOW status sharing (OPAL and the host)
+ *
+ * The host will pass on OPAL, a buffer of length OPAL_SYSEPOW_MAX
+ * with individual elements being 16 bits wide to fetch the system
+ * wide EPOW status. Each element in the buffer will contain the
+ * EPOW status in it's bit representation for a particular EPOW sub
+ * class as defiend here. So multiple detailed EPOW status bits
+ * specific for any sub class can be represented in a single buffer
+ * element as it's bit representation.
+ */
+
+/* System EPOW type */
+enum OpalSysEpow {
+	OPAL_SYSEPOW_POWER	= 0,	/* Power EPOW */
+	OPAL_SYSEPOW_TEMP	= 1,	/* Temperature EPOW */
+	OPAL_SYSEPOW_COOLING	= 2,	/* Cooling EPOW */
+	OPAL_SYSEPOW_MAX	= 3,	/* Max EPOW categories */
+};
+
+/* Power EPOW */
+enum OpalSysPower {
+	OPAL_SYSPOWER_UPS	= 0x0001, /* System on UPS power */
+	OPAL_SYSPOWER_CHNG	= 0x0002, /* System power config change */
+	OPAL_SYSPOWER_FAIL	= 0x0004, /* System impending power failure */
+	OPAL_SYSPOWER_INCL	= 0x0008, /* System incomplete power */
+};
+
+/* Temperature EPOW */
+enum OpalSysTemp {
+	OPAL_SYSTEMP_AMB	= 0x0001, /* System over ambient temperature */
+	OPAL_SYSTEMP_INT	= 0x0002, /* System over internal temperature */
+	OPAL_SYSTEMP_HMD	= 0x0004, /* System over ambient humidity */
+};
+
+/* Cooling EPOW */
+enum OpalSysCooling {
+	OPAL_SYSCOOL_INSF	= 0x0001, /* System insufficient cooling */
+};
+
 struct opal_machine_check_event {
 	enum OpalMCE_Version	version:8;	/* 0x00 */
 	uint8_t			in_use;		/* 0x01 */
@@ -881,7 +923,7 @@ int64_t opal_pci_fence_phb(uint64_t phb_id);
 int64_t opal_pci_reinit(uint64_t phb_id, uint64_t reinit_scope, uint64_t data);
 int64_t opal_pci_mask_pe_error(uint64_t phb_id, uint16_t pe_number, uint8_t error_type, uint8_t mask_action);
 int64_t opal_set_slot_led_status(uint64_t phb_id, uint64_t slot_id, uint8_t led_type, uint8_t led_action);
-int64_t opal_get_epow_status(__be64 *status);
+int64_t opal_get_epow_status(uint16_t *status, uint16_t *length);
 int64_t opal_set_system_attention_led(uint8_t led_action);
 int64_t opal_pci_next_error(uint64_t phb_id, __be64 *first_frozen_pe,
 			    __be16 *pci_error_type, __be16 *severity);
@@ -924,6 +966,7 @@ int64_t opal_sensor_read(uint32_t sensor_hndl, int token, __be32 *sensor_data);
 int64_t opal_handle_hmi(void);
 int64_t opal_register_dump_region(uint32_t id, uint64_t start, uint64_t end);
 int64_t opal_unregister_dump_region(uint32_t id);
+int64_t opal_get_dpo_status(int64_t *dpo_timeout);
 
 /* Internal functions */
 extern int early_init_dt_scan_opal(unsigned long node, const char *uname,
diff --git a/arch/powerpc/include/uapi/asm/opal_platform_events.h b/arch/powerpc/include/uapi/asm/opal_platform_events.h
new file mode 100644
index 0000000..6d2e13e
--- /dev/null
+++ b/arch/powerpc/include/uapi/asm/opal_platform_events.h
@@ -0,0 +1,90 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2014
+ *
+ * Author: Anshuman Khandual <khandual@linux.vnet.ibm.com>
+ */
+#ifndef __LINUX_OPAL_PLATFORM_EVENTS_H
+#define __LINUX_OPAL_PLATFORM_EVENTS_H
+
+#include <linux/types.h>
+
+/* EPOW classification */
+enum epow_condition {
+	EPOW_SYSPOWER_CHNG	= 0,	/* Power */
+	EPOW_SYSPOWER_FAIL	= 1,
+	EPOW_SYSPOWER_INCL	= 2,
+	EPOW_SYSPOWER_UPS	= 3,
+	EPOW_SYSTEMP_AMB	= 4,	/* Temperature */
+	EPOW_SYSTEMP_INT	= 5,
+	EPOW_SYSTEMP_HMD	= 6,
+	EPOW_SYSCOOL_INSF	= 7,	/* Cooling */
+	EPOW_MAX = 8,
+};
+
+/* OPAL EPOW event */
+struct epow_event {
+	__u64	epow[EPOW_MAX];		/* Detailed system EPOW status */
+	__u64	timeout;		/* Timeout to shutdown in secs */
+};
+
+/* OPAL DPO event */
+struct dpo_event {
+	__u64	orig_timeout;		/* Platform provided timeout in secs */
+	__u64	remain_timeout;		/* Timeout to shutdown in secs */
+};
+
+/* OPAL event */
+struct opal_plat_event {
+	__u32	type;			/* Type of OPAL platform event */
+#define OPAL_PLAT_EVENT_TYPE_EPOW	0
+#define OPAL_PLAT_EVENT_TYPE_DPO	1
+#define OPAL_PLAT_EVENT_TYPE_MAX	2
+	__u32	size;			/* Size of OPAL platform event */
+	union {
+		struct epow_event epow;	/* EPOW platform event */
+		struct dpo_event  dpo;	/* DPO platform event */
+	};
+};
+
+/*
+ * Suggested read size
+ *
+ * The user space client should attempt to read OPAL_PLAT_EVENT_READ_SIZE
+ * amount of data from the character device file '/dev/opal_event' at any
+ * point of time. The kernel driver will pass an entire opal_plat_event
+ * structure in every read. This ensures that minium data the user space
+ * client gets from the kernel is one opal_plat_event structure.
+ */
+#define	PLAT_EVENT_MAX_SIZE	4096
+
+/*
+ * Suggested user operation
+ *
+ * The user space client must follow these steps in order to be able to
+ * exploit the features exported through the OPAL platform event driver.
+ *
+ *	(1) Open the character device file
+ *	(2) Poll on the file for POLLIN
+ *	(3) When unblocked, must attempt to read PLAT_EVENT_MAX_SIZE size
+ *	(4) Kernel driver will pass one opal_plat_event structure
+ *	(5) Poll again for more new events
+ *
+ * The character device file (/dev/opal_event) must be opened and operated by
+ * only one user space client at any point of time. Other attempts to open the
+ * file will be returned by the driver as EBUSY.
+ */
+
+#endif /* __LINUX_OPAL_PLATFORM_EVENTS_H */
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index f241acc..c4b3b9b 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,7 +1,7 @@
 obj-y			+= setup.o opal-wrappers.o opal.o opal-async.o
 obj-y			+= opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
 obj-y			+= rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o
-obj-y			+= opal-msglog.o opal-hmi.o
+obj-y			+= opal-msglog.o opal-hmi.o opal-platform-events.o
 
 obj-$(CONFIG_SMP)	+= smp.o subcore.o subcore-asm.o
 obj-$(CONFIG_PCI)	+= pci.o pci-p5ioc2.o pci-ioda.o
diff --git a/arch/powerpc/platforms/powernv/opal-platform-events.c b/arch/powerpc/platforms/powernv/opal-platform-events.c
new file mode 100644
index 0000000..5e87d2b
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-platform-events.c
@@ -0,0 +1,737 @@
+/*
+ * IBM PowerNV OPAL platform events driver
+ *
+ * Copyright IBM Corporation 2014
+ *
+ * Author: Anshuman Khandual <khandual@linux.vnet.ibm.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+#define PREFIX		"OPAL_EVENT"
+#define pr_fmt(fmt)	PREFIX ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/timer.h>
+#include <linux/reboot.h>
+#include <asm/uaccess.h>
+#include <asm/opal.h>
+#include <asm/opal_platform_events.h>
+
+/* Platform events driver */
+static dev_t opal_event_dev;
+static struct cdev opal_event_cdev;
+static struct class *opal_event_class;
+static bool opal_event_open_flag;
+
+#define OPAL_EVENT_MAX_DEVS	1
+
+/* Platform events timers */
+static struct timer_list opal_event_timer;
+
+static DECLARE_WAIT_QUEUE_HEAD(opal_plat_evt_wait);
+static DECLARE_WAIT_QUEUE_HEAD(opal_plat_open_wait);
+static DEFINE_SPINLOCK(opal_plat_evt_spinlock);
+static DEFINE_SPINLOCK(opal_plat_timer_spinlock);
+static DEFINE_MUTEX(opal_plat_evt_mutex);
+
+/*
+ * Platform timeout values
+ *
+ * XXX: The default timeout value is 5 minutes. In future this
+ * should be communicated from the platform firmware through
+ * device tree attributes.
+ */
+#define OPAL_EPOW_TIMEOUT	300
+
+struct opal_platform_evt {
+	struct opal_plat_event opal_event;
+	struct list_head link;
+};
+static LIST_HEAD(opal_event_queue);
+static unsigned long opal_dpo_target;
+static bool opal_event_probe_finished;
+
+/*
+ * OPAL event map
+ *
+ * Converts OPAL event type into it's description.
+ */
+static const char *opal_event_map[OPAL_PLAT_EVENT_TYPE_MAX] = {
+	"OPAL_PLAT_EVENT_TYPE_EPOW", "OPAL_PLAT_EVENT_TYPE_DPO"
+};
+
+/*
+ * opal_event_timeout
+ *
+ * This is the actual timer handler. If the any of the timers
+ * expire, this function will be called to shutdown the system
+ * gracefully.
+ */
+static void opal_event_timeout(unsigned long data)
+{
+	orderly_poweroff(1);
+}
+
+/*
+ * opal_event_start_timer
+ *
+ * This will start opal event timer with given timeout value as the expiry
+ * if either the timer is not active or the expiry value of the already
+ * activated timer is at a later point of time in the future compared to the
+ * timeout value for this given new event. The function mod_timer takes care
+ * all the cases whether the opal event timer is already active or not.
+ */
+static void opal_event_start_timer(unsigned long event, u64 timeout)
+{
+	unsigned long flags;
+
+	/* Timer active with earlier timeout */
+	spin_lock_irqsave(&opal_plat_timer_spinlock, flags);
+	if (timer_pending(&opal_event_timer) &&
+			(opal_event_timer.expires < (jiffies + timeout * HZ))) {
+		spin_unlock_irqrestore(&opal_plat_timer_spinlock, flags);
+		pr_info("Timer for %s event active with an earlier timeout\n",
+					opal_event_map[opal_event_timer.data]);
+		return;
+	}
+	opal_event_timer.data = event;
+	mod_timer(&opal_event_timer, jiffies + timeout * HZ);
+	spin_unlock_irqrestore(&opal_plat_timer_spinlock, flags);
+	pr_info("Timer activated for %s event\n", opal_event_map[event]);
+}
+
+/*
+ * opal_event_stop_timer
+ *
+ * This will attempt to stop opal_event_timer if it is already enabled.
+ */
+static void opal_event_stop_timer(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&opal_plat_timer_spinlock, flags);
+	del_timer_sync(&opal_event_timer);
+	spin_unlock_irqrestore(&opal_plat_timer_spinlock, flags);
+	pr_info("Timer deactivated\n");
+}
+
+/*
+ * opal_event_read
+ *
+ * User client needs to attempt to read PLAT_EVENT_MAX_SIZE amount of data
+ * from the file descriptor at a time. The driver will pass a single node
+ * from the list if available at a time and then delete the node from the list.
+ */
+static ssize_t opal_event_read(struct file *filep,
+			char __user *buf, size_t len, loff_t *off)
+{
+	struct opal_platform_evt *evt;
+	unsigned long flags;
+
+	if (len < sizeof(struct opal_plat_event))
+		return -EINVAL;
+
+	/* Fetch the first node on the list */
+	spin_lock_irqsave(&opal_plat_evt_spinlock, flags);
+	if (list_empty(&opal_event_queue)) {
+		spin_unlock_irqrestore(&opal_plat_evt_spinlock, flags);
+		return 0;
+	}
+
+	/* Fetch and delete from the list */
+	evt = list_first_entry(&opal_event_queue,
+					struct opal_platform_evt, link);
+	list_del(&evt->link);
+
+	/*
+	 * Update the remaining timeout for DPO event.
+	 * This can only be updated during the read time.
+	 */
+	if (evt->opal_event.type == OPAL_PLAT_EVENT_TYPE_DPO) {
+		unsigned long timeout;
+
+		if (opal_dpo_target &&
+				evt->opal_event.dpo.orig_timeout) {
+			timeout = (opal_dpo_target - jiffies) / HZ;
+			evt->opal_event.dpo.remain_timeout = timeout;
+		}
+	}
+	spin_unlock_irqrestore(&opal_plat_evt_spinlock, flags);
+
+	if (copy_to_user(buf, &evt->opal_event,
+		sizeof(struct opal_plat_event))) {
+
+		/*
+		 * Copy to user has failed. The event node had
+		 * been deleted from the list. Lets add it back
+		 * there.
+		 */
+		spin_lock_irqsave(&opal_plat_evt_spinlock, flags);
+		list_add_tail(&evt->link, &opal_event_queue);
+		spin_unlock_irqrestore(&opal_plat_evt_spinlock, flags);
+		return -EFAULT;
+	}
+
+	kfree(evt);
+	return sizeof(struct opal_plat_event);
+}
+
+/*
+ * opal_event_poll
+ *
+ * Poll is unblocked right away with POLLIN when data is available.
+ * When data is not available, the process will have to block till
+ * it gets waked up and data is available to read.
+ */
+static unsigned int opal_event_poll(struct file *file, poll_table *wait)
+{
+	poll_wait(file, &opal_plat_evt_wait, wait);
+	if (!list_empty(&opal_event_queue))
+		return POLLIN;
+	return 0;
+}
+
+/*
+ * opal_event_open
+ *
+ * This makes sure that only one process can open the
+ * character device file at any point of time. Others
+ * attempting to open the file descriptor will either
+ * get EBUSY (with O_NONBLOCK flag) or wait for the
+ * other process to close the file descriptor.
+ */
+static int opal_event_open(struct inode *inode, struct file *file)
+{
+	int err;
+
+	mutex_lock(&opal_plat_evt_mutex);
+	while (opal_event_open_flag) {
+		mutex_unlock(&opal_plat_evt_mutex);
+		if (file->f_flags & O_NONBLOCK)
+			return -EBUSY;
+		err = wait_event_interruptible(opal_plat_open_wait,
+							!opal_event_open_flag);
+		if (err)
+			return -ERESTARTSYS;
+		mutex_lock(&opal_plat_evt_mutex);
+	}
+	opal_event_open_flag = true;
+	mutex_unlock(&opal_plat_evt_mutex);
+	return 0;
+}
+
+/*
+ * opal_event_release
+ *
+ * Releases the file descriptor for the device file.
+ */
+static int opal_event_release(struct inode *inode, struct file *file)
+{
+	mutex_lock(&opal_plat_evt_mutex);
+	if (opal_event_open_flag) {
+		opal_event_open_flag = false;
+		wake_up_interruptible(&opal_plat_open_wait);
+	}
+	mutex_unlock(&opal_plat_evt_mutex);
+	return 0;
+}
+
+/* Defined file operation */
+static const struct file_operations fops = {
+	.owner	= THIS_MODULE,
+	.open		= opal_event_open,
+	.release	= opal_event_release,
+	.read		= opal_event_read,
+	.poll		= opal_event_poll,
+};
+
+/* Process the received EPOW information */
+void process_epow(__u64 *epow, int16_t *epow_status, int max_epow_class)
+{
+	/*
+	 * Platform might have returned less number of EPOW
+	 * subclass status than asked for. This situation
+	 * happens when the platform firmware is older compared
+	 * to the kernel.
+	 */
+
+	if (!max_epow_class) {
+		pr_warn("EPOW: OPAL_SYSEPOW_POWER subclass not present\n");
+		return;
+	}
+
+	/* Power */
+	max_epow_class--;
+	if (epow_status[OPAL_SYSEPOW_POWER] & OPAL_SYSPOWER_CHNG) {
+		pr_info("EPOW: Power configuration changed\n");
+		epow[EPOW_SYSPOWER_CHNG] = 1;
+	}
+
+	if (epow_status[OPAL_SYSEPOW_POWER] & OPAL_SYSPOWER_FAIL) {
+		pr_info("EPOW: Impending system power failure\n");
+		epow[EPOW_SYSPOWER_FAIL] = 1;
+	}
+
+	if (epow_status[OPAL_SYSEPOW_POWER] & OPAL_SYSPOWER_INCL) {
+		pr_info("EPOW: Incomplete system power\n");
+		epow[EPOW_SYSPOWER_INCL] = 1;
+	}
+
+	if (epow_status[OPAL_SYSEPOW_POWER] & OPAL_SYSPOWER_UPS) {
+		pr_info("EPOW: System on UPS power\n");
+		epow[EPOW_SYSPOWER_UPS] = 1;
+	}
+
+	if (!max_epow_class) {
+		pr_warn("EPOW: OPAL_SYSEPOW_TEMP subclass not present\n");
+		return;
+	}
+
+	/* Temperature */
+	max_epow_class--;
+	if (epow_status[OPAL_SYSEPOW_TEMP] & OPAL_SYSTEMP_AMB) {
+		pr_info("EPOW: Over ambient temperature\n");
+		epow[EPOW_SYSTEMP_AMB] = 1;
+	}
+
+	if (epow_status[OPAL_SYSEPOW_TEMP] & OPAL_SYSTEMP_INT) {
+		pr_info("EPOW: Over internal temperature\n");
+		epow[EPOW_SYSTEMP_INT] = 1;
+	}
+
+	if (epow_status[OPAL_SYSEPOW_TEMP] & OPAL_SYSTEMP_HMD) {
+		pr_info("EPOW: Over internal humidity\n");
+		epow[EPOW_SYSTEMP_HMD] = 1;
+	}
+
+	if (!max_epow_class) {
+		pr_warn("EPOW: OPAL_SYSEPOW_COOLING subclass not present\n");
+		return;
+	}
+
+	/* Cooling */
+	max_epow_class--;
+	if (epow_status[OPAL_SYSEPOW_COOLING] & OPAL_SYSCOOL_INSF) {
+		pr_info("EPOW: Insufficient cooling\n");
+		epow[EPOW_SYSCOOL_INSF] = 1;
+	}
+}
+
+/*
+ * fetch_epow_status
+ *
+ * Fetch the system EPOW status through an OPAL call and
+ * validate the number of EPOW sub class status received.
+ */
+static void fetch_epow_status(int16_t *epow_status, int16_t *n_epow)
+{
+	int rc;
+
+	memset(epow_status, 0, sizeof(int16_t) * OPAL_SYSEPOW_MAX);
+	*n_epow = OPAL_SYSEPOW_MAX;
+	rc = opal_get_epow_status(epow_status, n_epow);
+	if (rc != OPAL_SUCCESS) {
+		pr_err("EPOW: OPAL call failed\n");
+		memset(epow_status, 0, sizeof(int16_t) * OPAL_SYSEPOW_MAX);
+		*n_epow = 0;
+		return;
+	}
+	if (!(*n_epow))
+		pr_err("EPOW: No subclass status received\n");
+}
+
+/*
+ * fetch_dpo_timeout
+ *
+ * Fetch the system DPO timeout status through an OPAL call.
+ */
+static void fetch_dpo_timeout(int64_t *dpo_timeout)
+{
+	int rc;
+
+	rc = opal_get_dpo_status(dpo_timeout);
+	if (rc != OPAL_SUCCESS) {
+		pr_err("DPO: OPAL call failed\n");
+		*dpo_timeout = 0;
+		return;
+	}
+}
+
+/*
+ * valid_epow
+ *
+ * Validate the received EPOW event status. This ensures
+ * that there are valid status for various EPOW sub classes
+ * and their individual events.
+ */
+static bool valid_epow(int16_t *epow_status, int16_t n_epow)
+{
+	int i;
+
+	/* EPOW sub classes present */
+	if (!n_epow)
+		return false;
+
+	/* EPOW events present */
+	for (i = 0; i < n_epow; i++) {
+		if (epow_status[i])
+			return true;
+	}
+	return false;
+}
+
+/*
+ * epow_exclude
+ *
+ * XXX: EPOW events on the action exclude list. System shutdown
+ * would not be scheduled for all these platform events. In future
+ * this should be communicated from the platform firmware through
+ * device tree attributes.
+ */
+static bool epow_exclude(int epow_event)
+{
+	switch (epow_event) {
+	case EPOW_SYSPOWER_CHNG:
+		return true;
+	case EPOW_SYSPOWER_INCL:
+		return true;
+	case EPOW_SYSPOWER_UPS:
+		return true;
+	case EPOW_SYSTEMP_HMD:
+		return true;
+	case EPOW_SYSCOOL_INSF:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/*
+ * actionable_epow
+ *
+ * There are some EPOW events for which the user client must receive
+ * their status but the driver would not schedule a timer for that
+ * event as the platform would not force shutdown the system because
+ * of this event. This filters only the actionable EPOW events for
+ * which shutdown timer need to be scheduled.
+ */
+static bool actionable_epow(__u64 *epow)
+{
+	int i;
+
+	for (i = 0; i < EPOW_MAX; i++) {
+		if (!epow_exclude(i) && epow[i])
+			return true;
+	}
+	return false;
+}
+
+/*
+ * opal_event_handle_basic
+ *
+ * Sets up the basic information for an opal platform event,
+ * activates the timer, adds to the list and wakes up waiting
+ * threads on the character device.
+ */
+static void opal_event_handle_basic(struct opal_platform_evt *evt,
+				unsigned long type, unsigned long timeout)
+{
+	unsigned long flags;
+
+	evt->opal_event.type = type;
+	switch (type) {
+	case OPAL_PLAT_EVENT_TYPE_EPOW:
+		evt->opal_event.size = sizeof(struct epow_event);
+		evt->opal_event.epow.timeout = timeout;
+		if (actionable_epow(evt->opal_event.epow.epow))
+			opal_event_start_timer(OPAL_PLAT_EVENT_TYPE_EPOW,
+							OPAL_EPOW_TIMEOUT);
+		break;
+	case  OPAL_PLAT_EVENT_TYPE_DPO:
+		evt->opal_event.size = sizeof(struct dpo_event);
+		evt->opal_event.dpo.orig_timeout = timeout;
+		opal_event_start_timer(OPAL_PLAT_EVENT_TYPE_DPO, timeout);
+		break;
+	default:
+		pr_err("Unknown event type\n");
+		break;
+	}
+	spin_lock_irqsave(&opal_plat_evt_spinlock, flags);
+	list_add_tail(&evt->link, &opal_event_queue);
+	spin_unlock_irqrestore(&opal_plat_evt_spinlock, flags);
+	wake_up_interruptible(&opal_plat_evt_wait);
+}
+
+/*
+ * opal_event_existing_status
+ *
+ * Fetch and process existing opal platform event conditions
+ * present on the system. If events detected, add them to the
+ * list which can be consumed by the user space right away.
+ */
+static void opal_event_existing_status(void)
+{
+	struct opal_platform_evt *evt;
+	int64_t dpo_timeout;
+	int16_t	epow_status[OPAL_SYSEPOW_MAX], n_epow;
+
+	fetch_epow_status(epow_status, &n_epow);
+	if (valid_epow(epow_status, n_epow)) {
+		evt = kzalloc(sizeof(struct opal_platform_evt), GFP_KERNEL);
+		if (!evt) {
+			pr_err("EPOW: Memory allocation for event failed\n");
+			return;
+		}
+		process_epow(evt->opal_event.epow.epow, epow_status, n_epow);
+		opal_event_handle_basic(evt, OPAL_PLAT_EVENT_TYPE_EPOW,
+							OPAL_EPOW_TIMEOUT);
+	}
+
+	fetch_dpo_timeout(&dpo_timeout);
+	if (dpo_timeout) {
+		evt = kzalloc(sizeof(struct opal_platform_evt), GFP_KERNEL);
+		if (!evt) {
+			pr_err("DPO: Memory allocation for event failed\n");
+			return;
+		}
+		opal_dpo_target = jiffies + dpo_timeout * HZ;
+		opal_event_handle_basic(evt, OPAL_PLAT_EVENT_TYPE_DPO,
+								dpo_timeout);
+	}
+}
+
+/* Platform EPOW message received */
+static int opal_epow_event(struct notifier_block *nb,
+				unsigned long msg_type, void *msg)
+{
+	struct opal_platform_evt *evt;
+	int16_t	epow_status[OPAL_SYSEPOW_MAX], n_epow;
+
+	if (msg_type != OPAL_MSG_EPOW)
+		return 0;
+
+	fetch_epow_status(epow_status, &n_epow);
+	if (!valid_epow(epow_status, n_epow))
+		return -EINVAL;
+
+	pr_debug("EPOW event: Power(%x) Thermal(%x) Cooling(%x)\n",
+			epow_status[0], epow_status[1], epow_status[2]);
+	evt = kzalloc(sizeof(struct opal_platform_evt), GFP_KERNEL);
+	if (!evt) {
+		pr_err("EPOW: Memory allocation for event failed\n");
+		return -ENOMEM;
+	}
+	process_epow(evt->opal_event.epow.epow, epow_status, n_epow);
+	opal_event_handle_basic(evt,
+				OPAL_PLAT_EVENT_TYPE_EPOW, OPAL_EPOW_TIMEOUT);
+	return 0;
+}
+
+/* Platform DPO message received */
+static int opal_dpo_event(struct notifier_block *nb,
+				unsigned long msg_type, void *msg)
+{
+	struct opal_platform_evt *evt;
+	int64_t dpo_timeout;
+
+	if (msg_type != OPAL_MSG_DPO)
+		return 0;
+
+	fetch_dpo_timeout(&dpo_timeout);
+	if (!dpo_timeout)
+		return -EINVAL;
+
+	pr_debug("DPO event: Timeout:%llu\n", dpo_timeout);
+	evt = kzalloc(sizeof(struct opal_platform_evt), GFP_KERNEL);
+	if (!evt) {
+		pr_err("DPO: Memory allocation for event failed\n");
+		return -ENOMEM;
+	}
+	opal_dpo_target = jiffies + dpo_timeout * HZ;
+	opal_event_handle_basic(evt, OPAL_PLAT_EVENT_TYPE_DPO, dpo_timeout);
+	return 0;
+}
+
+/* OPAL EPOW event notifier block */
+static struct notifier_block opal_epow_nb = {
+	.notifier_call  = opal_epow_event,
+	.next           = NULL,
+	.priority       = 0,
+};
+
+/* OPAL DPO event notifier block */
+static struct notifier_block opal_dpo_nb = {
+	.notifier_call  = opal_dpo_event,
+	.next           = NULL,
+	.priority       = 0,
+};
+
+/* Platform driver probe */
+static int opal_event_probe(struct platform_device *pdev)
+{
+	struct device *dev;
+	int ret;
+
+	if (opal_event_probe_finished) {
+		pr_err("%s getting called once again\n", __func__);
+		return 0;
+	}
+	opal_event_probe_finished = true;
+
+	init_timer(&opal_event_timer);
+	opal_event_timer.function = opal_event_timeout;
+	opal_event_open_flag = false;
+	opal_dpo_target = 0;
+
+	ret = alloc_chrdev_region(&opal_event_dev, 0,
+					OPAL_EVENT_MAX_DEVS, "opal_event");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "aloc_chrdev_region failed\n");
+		return ret;
+	}
+
+	opal_event_class = class_create(THIS_MODULE, "opal_event");
+	if (IS_ERR(opal_event_class)) {
+		ret = PTR_ERR(opal_event_class);
+		dev_err(&pdev->dev, "class_create failed with %d\n", ret);
+		goto fail_chrdev;
+	}
+
+	dev = device_create(opal_event_class, &pdev->dev,
+					opal_event_dev, NULL, "opal_event");
+	if (IS_ERR(dev)) {
+		ret = PTR_ERR(dev);
+		dev_err(&pdev->dev, "device_create failed with %d\n", ret);
+		goto fail_class;
+	}
+
+	cdev_init(&opal_event_cdev, &fops);
+	ret = cdev_add(&opal_event_cdev, opal_event_dev, OPAL_EVENT_MAX_DEVS);
+	if (ret < 0) {
+		dev_err(dev, "cdev_add failed\n");
+		ret = -EINVAL;
+		goto fail_device;
+	}
+
+	ret = opal_message_notifier_register(OPAL_MSG_EPOW, &opal_epow_nb);
+	if (ret) {
+		pr_err("EPOW: Platform event message notifier failed\n");
+		goto fail_cdev;
+	}
+
+	ret = opal_message_notifier_register(OPAL_MSG_DPO, &opal_dpo_nb);
+	if (ret) {
+		pr_err("DPO: Platform event message notifier failed\n");
+		opal_notifier_unregister(&opal_epow_nb);
+		goto fail_cdev;
+	}
+
+	/*
+	 * During the system boot, reboot and kexecs, the host can miss
+	 * some of the EPOW or DPO messages sent from OPAL. This ensures
+	 * that the current status of EPOW or DPO if any, is fetched and
+	 * then updated correctly. The user space needs to first read the
+	 * existing system status before entering into the poll/read loop.
+	 *
+	 */
+	opal_event_existing_status();
+	pr_info("OPAL platform event driver initialized\n");
+	return 0;
+fail_cdev:
+	cdev_del(&opal_event_cdev);
+fail_device:
+	device_destroy(opal_event_class, opal_event_dev);
+fail_class:
+	class_destroy(opal_event_class);
+fail_chrdev:
+	unregister_chrdev_region(opal_event_dev, OPAL_EVENT_MAX_DEVS);
+	return ret;
+}
+
+/* Platform driver remove */
+static int opal_event_remove(struct platform_device *pdev)
+{
+	struct opal_platform_evt *evt;
+
+	/* OPAL notifiers */
+	opal_notifier_unregister(&opal_dpo_nb);
+	opal_notifier_unregister(&opal_epow_nb);
+
+	/* Devices */
+	cdev_del(&opal_event_cdev);
+	device_destroy(opal_event_class, opal_event_dev);
+	class_destroy(opal_event_class);
+	unregister_chrdev_region(opal_event_dev, OPAL_EVENT_MAX_DEVS);
+
+	/* Timers */
+	opal_event_stop_timer();
+
+	/* Flush the list */
+	while (!list_empty(&opal_event_queue)) {
+		evt = list_first_entry(&opal_event_queue,
+					struct opal_platform_evt, link);
+		list_del(&evt->link);
+		kfree(evt);
+	}
+
+	pr_info("OPAL platform event driver exited\n");
+	return 0;
+}
+
+/* Platform driver property match */
+static struct of_device_id opal_event_match[] = {
+	{
+		.compatible	= "ibm,opal-v3-epow",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, opal_event_match);
+
+static struct platform_driver opal_event_driver = {
+	.probe	= opal_event_probe,
+	.remove = opal_event_remove,
+	.driver = {
+		.name = "opal-platform-event-driver",
+		.owner = THIS_MODULE,
+		.of_match_table = opal_event_match,
+	},
+};
+
+static int __init opal_platform_event_init(void)
+{
+	opal_event_probe_finished = false;
+	return platform_driver_register(&opal_event_driver);
+}
+
+static void __exit opal_platform_event_exit(void)
+{
+	platform_driver_unregister(&opal_event_driver);
+}
+module_init(opal_platform_event_init);
+module_exit(opal_platform_event_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anshuman Khandual <khandual@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("PowerNV OPAL platform events driver");
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index 2e6ce1b..2d1dc99 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -247,3 +247,4 @@ OPAL_CALL(opal_set_param,			OPAL_SET_PARAM);
 OPAL_CALL(opal_handle_hmi,			OPAL_HANDLE_HMI);
 OPAL_CALL(opal_register_dump_region,		OPAL_REGISTER_DUMP_REGION);
 OPAL_CALL(opal_unregister_dump_region,		OPAL_UNREGISTER_DUMP_REGION);
+OPAL_CALL(opal_get_dpo_status,			OPAL_GET_DPO_STATUS);
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index b44eec3..869dd45 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -625,7 +625,7 @@ static void __init opal_dump_region_init(void)
 }
 static int __init opal_init(void)
 {
-	struct device_node *np, *consoles;
+	struct device_node *np, *consoles, *epow;
 	const __be32 *irqs;
 	int rc, i, irqlen;
 
@@ -649,6 +649,12 @@ static int __init opal_init(void)
 		of_node_put(consoles);
 	}
 
+	epow = of_find_node_by_path("/ibm,opal/epow");
+	if (epow) {
+		of_platform_device_create(epow, "opal_event", NULL);
+		of_node_put(epow);
+	}
+
 	/* Find all OPAL interrupts and request them */
 	irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
 	pr_debug("opal: Found %d interrupts reserved for OPAL\n",
-- 
1.9.3

^ permalink raw reply related

* Re: [PATCH v2 00/22] Use MSI chip framework to configure MSI/MSI-X in all platforms
From: Yijing Wang @ 2014-09-29 10:12 UTC (permalink / raw)
  To: Liviu Dudau
  Cc: linux-mips, linux-ia64, linux-pci, Bharat.Bhushan, Thierry Reding,
	sparclinux, linux-arch, linux-s390, Russell King, Joerg Roedel,
	x86, Sebastian Ott, xen-devel, arnab.basu, Arnd Bergmann,
	Konrad Rzeszutek Wilk, Chris Metcalf, Bjorn Helgaas,
	Thomas Gleixner, linux-arm-kernel, Thomas Petazzoni, Xinwei Hu,
	Tony Luck, Sergei Shtylyov, linux-kernel, Ralf Baechle, iommu,
	David Vrabel, Wuyun, linuxppc-dev, David S. Miller, Lucas Stach
In-Reply-To: <20140929092600.GA15854@bart.dudau.co.uk>

>>> Not necessarily. What I have in mind is something like this:
>>
>> This is a good idea, what I'm worried is this series is already large, so I think we need to post
>> another series to do it.
> 
> I wasn't asking to do it here, I was just offering a suggestion (and sharing some experience) when
> it comes to handling msi chip in an arch independent way.

It's a good suggestion, thanks! :)

> 
>>
>>

^ permalink raw reply

* Re: [PATCH v2 00/22] Use MSI chip framework to configure MSI/MSI-X in all platforms
From: Yijing Wang @ 2014-09-29 10:13 UTC (permalink / raw)
  To: Lucas Stach
  Cc: linux-mips, linux-ia64, linux-pci, Xinwei Hu, Thierry Reding,
	sparclinux, linux-arch, linux-s390, Russell King, Joerg Roedel,
	x86, Sebastian Ott, Bharat.Bhushan, xen-devel, arnab.basu,
	Arnd Bergmann, Konrad Rzeszutek Wilk, Chris Metcalf,
	Bjorn Helgaas, Thomas Gleixner, linux-arm-kernel,
	Thomas Petazzoni, Liviu Dudau, Tony Luck, Sergei Shtylyov,
	linux-kernel, Ralf Baechle, iommu, David Vrabel, Wuyun,
	linuxppc-dev, David S. Miller
In-Reply-To: <1411979850.2625.7.camel@pengutronix.de>

On 2014/9/29 16:37, Lucas Stach wrote:
> Am Sonntag, den 28.09.2014, 14:11 +0800 schrieb Yijing Wang:
>> On 2014/9/28 10:32, Yijing Wang wrote:
>>> On 2014/9/26 17:05, Thierry Reding wrote:
>>>> On Fri, Sep 26, 2014 at 10:54:32AM +0200, Thierry Reding wrote:
>>>> [...]
>>>>> At least for Tegra it's trivial to just hook it up in tegra_pcie_scan_bus()
>>>>> directly (patch attached).
>>>>
>>>> Really attached this time.
>>>>
>>>> Thierry
>>>>
>>>
>>> It looks good to me, so I will update the arm pci hostbridge driver to assign
>>> pci root bus the msi chip instead of current pcibios_add_bus(). But for other
>>> platforms which only have a one msi chip, I will kept the arch_find_msi_chip()
>>> temporarily for more comments, especially from Bjorn.
>>
>> Oh, sorry, I found designware and rcar use pci_scan_root_bus(), so we can not simply
>> assign msi chip to root bus in all host drivers's scan functions.
> 
> Designware will switch away from pci_scan_root_bus() in the 3.18 cycle
> and I would think it would be no problem to to the same with rcar.

Good.

> 
> Regards,
> Lucas
> 


-- 
Thanks!
Yijing

^ permalink raw reply

* Re: [PATCH 0/9] powerpc/powernv: Support for fastsleep and winkle
From: Shreyas B Prabhu @ 2014-09-29 10:23 UTC (permalink / raw)
  To: linux-kernel, Michael Ellerman
  Cc: devicetree, Srivatsa S. Bhat, linux-pm, Rafael J. Wysocki,
	Grant Likely, Rob Herring, Paul Mackerras, Preeti U. Murthy,
	linuxppc-dev
In-Reply-To: <541A4D4F.1090307@linux.vnet.ibm.com>

Hi,
Any updates on this patch series?

On Thursday 18 September 2014 08:41 AM, Shreyas B Prabhu wrote:
> Hi,
> 
> In this patch series we use winkle for offlined cores. I successfully
> tested the working of this with subcore functionality.
> 
> Test scenario was as follows:
> 1. Set SMT mode to 1, Set subores-per-core to 1
> 2. Offline a core, in this case cpu 32 (sending it to winkle)
> 3. Set subcores-per-core to 4
> 4. Online the core
> 5. Start a guest (Topology 1 core 2 threads) on a subcore, in this case
> on cpu 36
> 
> This works without any glitch.
> 
> Thanks,
> Shreyas
> 
> On Monday 25 August 2014 11:31 PM, Shreyas B. Prabhu wrote:
>> Fast sleep is an idle state, where the core and the L1 and L2
>> caches are brought down to a threshold voltage. This also means that
>> the communication between L2 and L3 caches have to be fenced. However
>> the current P8 chips have a bug wherein this fencing between L2 and
>> L3 caches get delayed by a cpu cycle. This can delay L3 response to
>> the other cpus if they request for data during this time. Thus they
>> would fetch the same data from the memory which could lead to data
>> corruption if L3 cache is not flushed.
>> Patch 4 adds support to work around this.
>>
>> 'Deep Winkle' is a deeper idle state where core and private L2 are powered
>> off. While it offers higher power savings, it is at the cost of losing
>> hypervisor register state and higher latency.
>> Patch 5-9 adds support for winkle and uses it for offline cpus.
>>
>> Patch 1 - Moves parameters required discover idle states to a location 
>> common to both cpuidle driver and powernv core code
>> Patch 2 - Populates idle state details from device tree
>> Patch 3 - Enables cpus to run guest after waking up from fastsleep/winkle
>>
>>
>> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>> Cc: Paul Mackerras <paulus@samba.org>
>> Cc: Michael Ellerman <mpe@ellerman.id.au>
>> Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
>> Cc: Srivatsa S. Bhat <srivatsa@MIT.EDU>
>> Cc: Preeti U. Murthy <preeti@linux.vnet.ibm.com>
>> Cc: Vaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>
>> Cc: Rob Herring <robh+dt@kernel.org>
>> Cc: Grant Likely <grant.likely@linaro.org>
>> Cc: devicetree@vger.kernel.org
>> Cc: linux-pm@vger.kernel.org
>> Cc: linuxppc-dev@lists.ozlabs.org
>>
>> Preeti U Murthy (2):
>>   cpuidle/powernv: Populate cpuidle state details by querying the
>>     device-tree
>>   powerpc/powernv/cpuidle: Add workaround to enable fastsleep
>>
>> Shreyas B. Prabhu (6):
>>   powerpc/kvm/book3s_hv: Enable CPUs to run guest after waking up from
>>     fast-sleep
>>   powerpc/powernv: Add OPAL call to save and restore
>>   powerpc: Adding macro for accessing Thread Switch Control Register
>>   powerpc/powernv: Add winkle infrastructure
>>   powerpc/powernv: Discover and enable winkle
>>   powerpc/powernv: Enter deepest supported idle state in offline
>>
>> Srivatsa S. Bhat (1):
>>   powerpc/powernv: Enable Offline CPUs to enter deep idle states
>>
>>  arch/powerpc/include/asm/machdep.h             |   4 +
>>  arch/powerpc/include/asm/opal.h                |  10 ++
>>  arch/powerpc/include/asm/paca.h                |   3 +
>>  arch/powerpc/include/asm/ppc-opcode.h          |   2 +
>>  arch/powerpc/include/asm/processor.h           |   6 +-
>>  arch/powerpc/include/asm/reg.h                 |   1 +
>>  arch/powerpc/kernel/asm-offsets.c              |   1 +
>>  arch/powerpc/kernel/exceptions-64s.S           |  37 ++---
>>  arch/powerpc/kernel/idle.c                     |  30 ++++
>>  arch/powerpc/kernel/idle_power7.S              |  83 +++++++++-
>>  arch/powerpc/platforms/powernv/opal-wrappers.S |   2 +
>>  arch/powerpc/platforms/powernv/powernv.h       |   8 +
>>  arch/powerpc/platforms/powernv/setup.c         | 217 +++++++++++++++++++++++++
>>  arch/powerpc/platforms/powernv/smp.c           |  13 +-
>>  arch/powerpc/platforms/powernv/subcore.c       |  15 ++
>>  drivers/cpuidle/cpuidle-powernv.c              |  40 ++++-
>>  16 files changed, 439 insertions(+), 33 deletions(-)
>>

^ permalink raw reply

* [PATCH] powerpc, powernv: Add OPAL platform event driver
From: Anshuman Khandual @ 2014-09-29 10:23 UTC (permalink / raw)
  To: linuxppc-dev

This patch creates a new OPAL platform event character driver
which will give userspace clients the access to these events
and process them effectively. Following platforms events are
currently supported with this platform driver.

	(1) Environmental and Power Warning (EPOW)
	(2) Delayed Power Off (DPO)

The user interface for this driver is /dev/opal_event character
device file where the user space clients can poll and read for
new opal platform events. The expected sequence of events driven
from user space should be like the following.

	(1) Open the character device file
	(2) Poll on the file for POLLIN event
	(3) When unblocked, must attempt to read PLAT_EVENT_MAX_SIZE size
	(4) Kernel driver will pass at most one opal_plat_event structure
	(5) Poll again for more new events

The driver registers for OPAL messages notifications corresponding to
individual OPAL events. When any of those event messages arrive in the
kernel, the callbacks are called to process them which in turn unblocks
the polling thread on the character device file. The driver also registers
a timer function which will be called after a threshold amount of time to
shutdown the system. The user space client receives the timeout value for
all individual OPAL platform events and hence must prepare the system and
eventually shutdown. In case the user client does not shutdown the system,
the timer function will be called after the threshold and shutdown the
system explicitly.

Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/opal.h                    |  45 +-
 .../include/uapi/asm/opal_platform_events.h        |  90 +++
 arch/powerpc/platforms/powernv/Makefile            |   2 +-
 .../platforms/powernv/opal-platform-events.c       | 737 +++++++++++++++++++++
 arch/powerpc/platforms/powernv/opal-wrappers.S     |   1 +
 arch/powerpc/platforms/powernv/opal.c              |   8 +-
 6 files changed, 880 insertions(+), 3 deletions(-)
 create mode 100644 arch/powerpc/include/uapi/asm/opal_platform_events.h
 create mode 100644 arch/powerpc/platforms/powernv/opal-platform-events.c

diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 86055e5..c134137 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -151,6 +151,7 @@ struct opal_sg_list {
 #define OPAL_HANDLE_HMI				98
 #define OPAL_REGISTER_DUMP_REGION		101
 #define OPAL_UNREGISTER_DUMP_REGION		102
+#define OPAL_GET_DPO_STATUS			105
 
 #ifndef __ASSEMBLY__
 
@@ -249,6 +250,7 @@ enum OpalMessageType {
 	OPAL_MSG_EPOW,
 	OPAL_MSG_SHUTDOWN,
 	OPAL_MSG_HMI_EVT,
+	OPAL_MSG_DPO,
 	OPAL_MSG_TYPE_MAX,
 };
 
@@ -417,6 +419,46 @@ struct opal_msg {
 	__be64 params[8];
 };
 
+/*
+ * EPOW status sharing (OPAL and the host)
+ *
+ * The host will pass on OPAL, a buffer of length OPAL_SYSEPOW_MAX
+ * with individual elements being 16 bits wide to fetch the system
+ * wide EPOW status. Each element in the buffer will contain the
+ * EPOW status in it's bit representation for a particular EPOW sub
+ * class as defiend here. So multiple detailed EPOW status bits
+ * specific for any sub class can be represented in a single buffer
+ * element as it's bit representation.
+ */
+
+/* System EPOW type */
+enum OpalSysEpow {
+	OPAL_SYSEPOW_POWER	= 0,	/* Power EPOW */
+	OPAL_SYSEPOW_TEMP	= 1,	/* Temperature EPOW */
+	OPAL_SYSEPOW_COOLING	= 2,	/* Cooling EPOW */
+	OPAL_SYSEPOW_MAX	= 3,	/* Max EPOW categories */
+};
+
+/* Power EPOW */
+enum OpalSysPower {
+	OPAL_SYSPOWER_UPS	= 0x0001, /* System on UPS power */
+	OPAL_SYSPOWER_CHNG	= 0x0002, /* System power config change */
+	OPAL_SYSPOWER_FAIL	= 0x0004, /* System impending power failure */
+	OPAL_SYSPOWER_INCL	= 0x0008, /* System incomplete power */
+};
+
+/* Temperature EPOW */
+enum OpalSysTemp {
+	OPAL_SYSTEMP_AMB	= 0x0001, /* System over ambient temperature */
+	OPAL_SYSTEMP_INT	= 0x0002, /* System over internal temperature */
+	OPAL_SYSTEMP_HMD	= 0x0004, /* System over ambient humidity */
+};
+
+/* Cooling EPOW */
+enum OpalSysCooling {
+	OPAL_SYSCOOL_INSF	= 0x0001, /* System insufficient cooling */
+};
+
 struct opal_machine_check_event {
 	enum OpalMCE_Version	version:8;	/* 0x00 */
 	uint8_t			in_use;		/* 0x01 */
@@ -881,7 +923,7 @@ int64_t opal_pci_fence_phb(uint64_t phb_id);
 int64_t opal_pci_reinit(uint64_t phb_id, uint64_t reinit_scope, uint64_t data);
 int64_t opal_pci_mask_pe_error(uint64_t phb_id, uint16_t pe_number, uint8_t error_type, uint8_t mask_action);
 int64_t opal_set_slot_led_status(uint64_t phb_id, uint64_t slot_id, uint8_t led_type, uint8_t led_action);
-int64_t opal_get_epow_status(__be64 *status);
+int64_t opal_get_epow_status(uint16_t *status, uint16_t *length);
 int64_t opal_set_system_attention_led(uint8_t led_action);
 int64_t opal_pci_next_error(uint64_t phb_id, __be64 *first_frozen_pe,
 			    __be16 *pci_error_type, __be16 *severity);
@@ -924,6 +966,7 @@ int64_t opal_sensor_read(uint32_t sensor_hndl, int token, __be32 *sensor_data);
 int64_t opal_handle_hmi(void);
 int64_t opal_register_dump_region(uint32_t id, uint64_t start, uint64_t end);
 int64_t opal_unregister_dump_region(uint32_t id);
+int64_t opal_get_dpo_status(int64_t *dpo_timeout);
 
 /* Internal functions */
 extern int early_init_dt_scan_opal(unsigned long node, const char *uname,
diff --git a/arch/powerpc/include/uapi/asm/opal_platform_events.h b/arch/powerpc/include/uapi/asm/opal_platform_events.h
new file mode 100644
index 0000000..6d2e13e
--- /dev/null
+++ b/arch/powerpc/include/uapi/asm/opal_platform_events.h
@@ -0,0 +1,90 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2014
+ *
+ * Author: Anshuman Khandual <khandual@linux.vnet.ibm.com>
+ */
+#ifndef __LINUX_OPAL_PLATFORM_EVENTS_H
+#define __LINUX_OPAL_PLATFORM_EVENTS_H
+
+#include <linux/types.h>
+
+/* EPOW classification */
+enum epow_condition {
+	EPOW_SYSPOWER_CHNG	= 0,	/* Power */
+	EPOW_SYSPOWER_FAIL	= 1,
+	EPOW_SYSPOWER_INCL	= 2,
+	EPOW_SYSPOWER_UPS	= 3,
+	EPOW_SYSTEMP_AMB	= 4,	/* Temperature */
+	EPOW_SYSTEMP_INT	= 5,
+	EPOW_SYSTEMP_HMD	= 6,
+	EPOW_SYSCOOL_INSF	= 7,	/* Cooling */
+	EPOW_MAX = 8,
+};
+
+/* OPAL EPOW event */
+struct epow_event {
+	__u64	epow[EPOW_MAX];		/* Detailed system EPOW status */
+	__u64	timeout;		/* Timeout to shutdown in secs */
+};
+
+/* OPAL DPO event */
+struct dpo_event {
+	__u64	orig_timeout;		/* Platform provided timeout in secs */
+	__u64	remain_timeout;		/* Timeout to shutdown in secs */
+};
+
+/* OPAL event */
+struct opal_plat_event {
+	__u32	type;			/* Type of OPAL platform event */
+#define OPAL_PLAT_EVENT_TYPE_EPOW	0
+#define OPAL_PLAT_EVENT_TYPE_DPO	1
+#define OPAL_PLAT_EVENT_TYPE_MAX	2
+	__u32	size;			/* Size of OPAL platform event */
+	union {
+		struct epow_event epow;	/* EPOW platform event */
+		struct dpo_event  dpo;	/* DPO platform event */
+	};
+};
+
+/*
+ * Suggested read size
+ *
+ * The user space client should attempt to read OPAL_PLAT_EVENT_READ_SIZE
+ * amount of data from the character device file '/dev/opal_event' at any
+ * point of time. The kernel driver will pass an entire opal_plat_event
+ * structure in every read. This ensures that minium data the user space
+ * client gets from the kernel is one opal_plat_event structure.
+ */
+#define	PLAT_EVENT_MAX_SIZE	4096
+
+/*
+ * Suggested user operation
+ *
+ * The user space client must follow these steps in order to be able to
+ * exploit the features exported through the OPAL platform event driver.
+ *
+ *	(1) Open the character device file
+ *	(2) Poll on the file for POLLIN
+ *	(3) When unblocked, must attempt to read PLAT_EVENT_MAX_SIZE size
+ *	(4) Kernel driver will pass one opal_plat_event structure
+ *	(5) Poll again for more new events
+ *
+ * The character device file (/dev/opal_event) must be opened and operated by
+ * only one user space client at any point of time. Other attempts to open the
+ * file will be returned by the driver as EBUSY.
+ */
+
+#endif /* __LINUX_OPAL_PLATFORM_EVENTS_H */
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index f241acc..c4b3b9b 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,7 +1,7 @@
 obj-y			+= setup.o opal-wrappers.o opal.o opal-async.o
 obj-y			+= opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
 obj-y			+= rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o
-obj-y			+= opal-msglog.o opal-hmi.o
+obj-y			+= opal-msglog.o opal-hmi.o opal-platform-events.o
 
 obj-$(CONFIG_SMP)	+= smp.o subcore.o subcore-asm.o
 obj-$(CONFIG_PCI)	+= pci.o pci-p5ioc2.o pci-ioda.o
diff --git a/arch/powerpc/platforms/powernv/opal-platform-events.c b/arch/powerpc/platforms/powernv/opal-platform-events.c
new file mode 100644
index 0000000..5e87d2b
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-platform-events.c
@@ -0,0 +1,737 @@
+/*
+ * IBM PowerNV OPAL platform events driver
+ *
+ * Copyright IBM Corporation 2014
+ *
+ * Author: Anshuman Khandual <khandual@linux.vnet.ibm.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+#define PREFIX		"OPAL_EVENT"
+#define pr_fmt(fmt)	PREFIX ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/timer.h>
+#include <linux/reboot.h>
+#include <asm/uaccess.h>
+#include <asm/opal.h>
+#include <asm/opal_platform_events.h>
+
+/* Platform events driver */
+static dev_t opal_event_dev;
+static struct cdev opal_event_cdev;
+static struct class *opal_event_class;
+static bool opal_event_open_flag;
+
+#define OPAL_EVENT_MAX_DEVS	1
+
+/* Platform events timers */
+static struct timer_list opal_event_timer;
+
+static DECLARE_WAIT_QUEUE_HEAD(opal_plat_evt_wait);
+static DECLARE_WAIT_QUEUE_HEAD(opal_plat_open_wait);
+static DEFINE_SPINLOCK(opal_plat_evt_spinlock);
+static DEFINE_SPINLOCK(opal_plat_timer_spinlock);
+static DEFINE_MUTEX(opal_plat_evt_mutex);
+
+/*
+ * Platform timeout values
+ *
+ * XXX: The default timeout value is 5 minutes. In future this
+ * should be communicated from the platform firmware through
+ * device tree attributes.
+ */
+#define OPAL_EPOW_TIMEOUT	300
+
+struct opal_platform_evt {
+	struct opal_plat_event opal_event;
+	struct list_head link;
+};
+static LIST_HEAD(opal_event_queue);
+static unsigned long opal_dpo_target;
+static bool opal_event_probe_finished;
+
+/*
+ * OPAL event map
+ *
+ * Converts OPAL event type into it's description.
+ */
+static const char *opal_event_map[OPAL_PLAT_EVENT_TYPE_MAX] = {
+	"OPAL_PLAT_EVENT_TYPE_EPOW", "OPAL_PLAT_EVENT_TYPE_DPO"
+};
+
+/*
+ * opal_event_timeout
+ *
+ * This is the actual timer handler. If the any of the timers
+ * expire, this function will be called to shutdown the system
+ * gracefully.
+ */
+static void opal_event_timeout(unsigned long data)
+{
+	orderly_poweroff(1);
+}
+
+/*
+ * opal_event_start_timer
+ *
+ * This will start opal event timer with given timeout value as the expiry
+ * if either the timer is not active or the expiry value of the already
+ * activated timer is at a later point of time in the future compared to the
+ * timeout value for this given new event. The function mod_timer takes care
+ * all the cases whether the opal event timer is already active or not.
+ */
+static void opal_event_start_timer(unsigned long event, u64 timeout)
+{
+	unsigned long flags;
+
+	/* Timer active with earlier timeout */
+	spin_lock_irqsave(&opal_plat_timer_spinlock, flags);
+	if (timer_pending(&opal_event_timer) &&
+			(opal_event_timer.expires < (jiffies + timeout * HZ))) {
+		spin_unlock_irqrestore(&opal_plat_timer_spinlock, flags);
+		pr_info("Timer for %s event active with an earlier timeout\n",
+					opal_event_map[opal_event_timer.data]);
+		return;
+	}
+	opal_event_timer.data = event;
+	mod_timer(&opal_event_timer, jiffies + timeout * HZ);
+	spin_unlock_irqrestore(&opal_plat_timer_spinlock, flags);
+	pr_info("Timer activated for %s event\n", opal_event_map[event]);
+}
+
+/*
+ * opal_event_stop_timer
+ *
+ * This will attempt to stop opal_event_timer if it is already enabled.
+ */
+static void opal_event_stop_timer(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&opal_plat_timer_spinlock, flags);
+	del_timer_sync(&opal_event_timer);
+	spin_unlock_irqrestore(&opal_plat_timer_spinlock, flags);
+	pr_info("Timer deactivated\n");
+}
+
+/*
+ * opal_event_read
+ *
+ * User client needs to attempt to read PLAT_EVENT_MAX_SIZE amount of data
+ * from the file descriptor at a time. The driver will pass a single node
+ * from the list if available at a time and then delete the node from the list.
+ */
+static ssize_t opal_event_read(struct file *filep,
+			char __user *buf, size_t len, loff_t *off)
+{
+	struct opal_platform_evt *evt;
+	unsigned long flags;
+
+	if (len < sizeof(struct opal_plat_event))
+		return -EINVAL;
+
+	/* Fetch the first node on the list */
+	spin_lock_irqsave(&opal_plat_evt_spinlock, flags);
+	if (list_empty(&opal_event_queue)) {
+		spin_unlock_irqrestore(&opal_plat_evt_spinlock, flags);
+		return 0;
+	}
+
+	/* Fetch and delete from the list */
+	evt = list_first_entry(&opal_event_queue,
+					struct opal_platform_evt, link);
+	list_del(&evt->link);
+
+	/*
+	 * Update the remaining timeout for DPO event.
+	 * This can only be updated during the read time.
+	 */
+	if (evt->opal_event.type == OPAL_PLAT_EVENT_TYPE_DPO) {
+		unsigned long timeout;
+
+		if (opal_dpo_target &&
+				evt->opal_event.dpo.orig_timeout) {
+			timeout = (opal_dpo_target - jiffies) / HZ;
+			evt->opal_event.dpo.remain_timeout = timeout;
+		}
+	}
+	spin_unlock_irqrestore(&opal_plat_evt_spinlock, flags);
+
+	if (copy_to_user(buf, &evt->opal_event,
+		sizeof(struct opal_plat_event))) {
+
+		/*
+		 * Copy to user has failed. The event node had
+		 * been deleted from the list. Lets add it back
+		 * there.
+		 */
+		spin_lock_irqsave(&opal_plat_evt_spinlock, flags);
+		list_add_tail(&evt->link, &opal_event_queue);
+		spin_unlock_irqrestore(&opal_plat_evt_spinlock, flags);
+		return -EFAULT;
+	}
+
+	kfree(evt);
+	return sizeof(struct opal_plat_event);
+}
+
+/*
+ * opal_event_poll
+ *
+ * Poll is unblocked right away with POLLIN when data is available.
+ * When data is not available, the process will have to block till
+ * it gets waked up and data is available to read.
+ */
+static unsigned int opal_event_poll(struct file *file, poll_table *wait)
+{
+	poll_wait(file, &opal_plat_evt_wait, wait);
+	if (!list_empty(&opal_event_queue))
+		return POLLIN;
+	return 0;
+}
+
+/*
+ * opal_event_open
+ *
+ * This makes sure that only one process can open the
+ * character device file at any point of time. Others
+ * attempting to open the file descriptor will either
+ * get EBUSY (with O_NONBLOCK flag) or wait for the
+ * other process to close the file descriptor.
+ */
+static int opal_event_open(struct inode *inode, struct file *file)
+{
+	int err;
+
+	mutex_lock(&opal_plat_evt_mutex);
+	while (opal_event_open_flag) {
+		mutex_unlock(&opal_plat_evt_mutex);
+		if (file->f_flags & O_NONBLOCK)
+			return -EBUSY;
+		err = wait_event_interruptible(opal_plat_open_wait,
+							!opal_event_open_flag);
+		if (err)
+			return -ERESTARTSYS;
+		mutex_lock(&opal_plat_evt_mutex);
+	}
+	opal_event_open_flag = true;
+	mutex_unlock(&opal_plat_evt_mutex);
+	return 0;
+}
+
+/*
+ * opal_event_release
+ *
+ * Releases the file descriptor for the device file.
+ */
+static int opal_event_release(struct inode *inode, struct file *file)
+{
+	mutex_lock(&opal_plat_evt_mutex);
+	if (opal_event_open_flag) {
+		opal_event_open_flag = false;
+		wake_up_interruptible(&opal_plat_open_wait);
+	}
+	mutex_unlock(&opal_plat_evt_mutex);
+	return 0;
+}
+
+/* Defined file operation */
+static const struct file_operations fops = {
+	.owner	= THIS_MODULE,
+	.open		= opal_event_open,
+	.release	= opal_event_release,
+	.read		= opal_event_read,
+	.poll		= opal_event_poll,
+};
+
+/* Process the received EPOW information */
+void process_epow(__u64 *epow, int16_t *epow_status, int max_epow_class)
+{
+	/*
+	 * Platform might have returned less number of EPOW
+	 * subclass status than asked for. This situation
+	 * happens when the platform firmware is older compared
+	 * to the kernel.
+	 */
+
+	if (!max_epow_class) {
+		pr_warn("EPOW: OPAL_SYSEPOW_POWER subclass not present\n");
+		return;
+	}
+
+	/* Power */
+	max_epow_class--;
+	if (epow_status[OPAL_SYSEPOW_POWER] & OPAL_SYSPOWER_CHNG) {
+		pr_info("EPOW: Power configuration changed\n");
+		epow[EPOW_SYSPOWER_CHNG] = 1;
+	}
+
+	if (epow_status[OPAL_SYSEPOW_POWER] & OPAL_SYSPOWER_FAIL) {
+		pr_info("EPOW: Impending system power failure\n");
+		epow[EPOW_SYSPOWER_FAIL] = 1;
+	}
+
+	if (epow_status[OPAL_SYSEPOW_POWER] & OPAL_SYSPOWER_INCL) {
+		pr_info("EPOW: Incomplete system power\n");
+		epow[EPOW_SYSPOWER_INCL] = 1;
+	}
+
+	if (epow_status[OPAL_SYSEPOW_POWER] & OPAL_SYSPOWER_UPS) {
+		pr_info("EPOW: System on UPS power\n");
+		epow[EPOW_SYSPOWER_UPS] = 1;
+	}
+
+	if (!max_epow_class) {
+		pr_warn("EPOW: OPAL_SYSEPOW_TEMP subclass not present\n");
+		return;
+	}
+
+	/* Temperature */
+	max_epow_class--;
+	if (epow_status[OPAL_SYSEPOW_TEMP] & OPAL_SYSTEMP_AMB) {
+		pr_info("EPOW: Over ambient temperature\n");
+		epow[EPOW_SYSTEMP_AMB] = 1;
+	}
+
+	if (epow_status[OPAL_SYSEPOW_TEMP] & OPAL_SYSTEMP_INT) {
+		pr_info("EPOW: Over internal temperature\n");
+		epow[EPOW_SYSTEMP_INT] = 1;
+	}
+
+	if (epow_status[OPAL_SYSEPOW_TEMP] & OPAL_SYSTEMP_HMD) {
+		pr_info("EPOW: Over internal humidity\n");
+		epow[EPOW_SYSTEMP_HMD] = 1;
+	}
+
+	if (!max_epow_class) {
+		pr_warn("EPOW: OPAL_SYSEPOW_COOLING subclass not present\n");
+		return;
+	}
+
+	/* Cooling */
+	max_epow_class--;
+	if (epow_status[OPAL_SYSEPOW_COOLING] & OPAL_SYSCOOL_INSF) {
+		pr_info("EPOW: Insufficient cooling\n");
+		epow[EPOW_SYSCOOL_INSF] = 1;
+	}
+}
+
+/*
+ * fetch_epow_status
+ *
+ * Fetch the system EPOW status through an OPAL call and
+ * validate the number of EPOW sub class status received.
+ */
+static void fetch_epow_status(int16_t *epow_status, int16_t *n_epow)
+{
+	int rc;
+
+	memset(epow_status, 0, sizeof(int16_t) * OPAL_SYSEPOW_MAX);
+	*n_epow = OPAL_SYSEPOW_MAX;
+	rc = opal_get_epow_status(epow_status, n_epow);
+	if (rc != OPAL_SUCCESS) {
+		pr_err("EPOW: OPAL call failed\n");
+		memset(epow_status, 0, sizeof(int16_t) * OPAL_SYSEPOW_MAX);
+		*n_epow = 0;
+		return;
+	}
+	if (!(*n_epow))
+		pr_err("EPOW: No subclass status received\n");
+}
+
+/*
+ * fetch_dpo_timeout
+ *
+ * Fetch the system DPO timeout status through an OPAL call.
+ */
+static void fetch_dpo_timeout(int64_t *dpo_timeout)
+{
+	int rc;
+
+	rc = opal_get_dpo_status(dpo_timeout);
+	if (rc != OPAL_SUCCESS) {
+		pr_err("DPO: OPAL call failed\n");
+		*dpo_timeout = 0;
+		return;
+	}
+}
+
+/*
+ * valid_epow
+ *
+ * Validate the received EPOW event status. This ensures
+ * that there are valid status for various EPOW sub classes
+ * and their individual events.
+ */
+static bool valid_epow(int16_t *epow_status, int16_t n_epow)
+{
+	int i;
+
+	/* EPOW sub classes present */
+	if (!n_epow)
+		return false;
+
+	/* EPOW events present */
+	for (i = 0; i < n_epow; i++) {
+		if (epow_status[i])
+			return true;
+	}
+	return false;
+}
+
+/*
+ * epow_exclude
+ *
+ * XXX: EPOW events on the action exclude list. System shutdown
+ * would not be scheduled for all these platform events. In future
+ * this should be communicated from the platform firmware through
+ * device tree attributes.
+ */
+static bool epow_exclude(int epow_event)
+{
+	switch (epow_event) {
+	case EPOW_SYSPOWER_CHNG:
+		return true;
+	case EPOW_SYSPOWER_INCL:
+		return true;
+	case EPOW_SYSPOWER_UPS:
+		return true;
+	case EPOW_SYSTEMP_HMD:
+		return true;
+	case EPOW_SYSCOOL_INSF:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/*
+ * actionable_epow
+ *
+ * There are some EPOW events for which the user client must receive
+ * their status but the driver would not schedule a timer for that
+ * event as the platform would not force shutdown the system because
+ * of this event. This filters only the actionable EPOW events for
+ * which shutdown timer need to be scheduled.
+ */
+static bool actionable_epow(__u64 *epow)
+{
+	int i;
+
+	for (i = 0; i < EPOW_MAX; i++) {
+		if (!epow_exclude(i) && epow[i])
+			return true;
+	}
+	return false;
+}
+
+/*
+ * opal_event_handle_basic
+ *
+ * Sets up the basic information for an opal platform event,
+ * activates the timer, adds to the list and wakes up waiting
+ * threads on the character device.
+ */
+static void opal_event_handle_basic(struct opal_platform_evt *evt,
+				unsigned long type, unsigned long timeout)
+{
+	unsigned long flags;
+
+	evt->opal_event.type = type;
+	switch (type) {
+	case OPAL_PLAT_EVENT_TYPE_EPOW:
+		evt->opal_event.size = sizeof(struct epow_event);
+		evt->opal_event.epow.timeout = timeout;
+		if (actionable_epow(evt->opal_event.epow.epow))
+			opal_event_start_timer(OPAL_PLAT_EVENT_TYPE_EPOW,
+							OPAL_EPOW_TIMEOUT);
+		break;
+	case  OPAL_PLAT_EVENT_TYPE_DPO:
+		evt->opal_event.size = sizeof(struct dpo_event);
+		evt->opal_event.dpo.orig_timeout = timeout;
+		opal_event_start_timer(OPAL_PLAT_EVENT_TYPE_DPO, timeout);
+		break;
+	default:
+		pr_err("Unknown event type\n");
+		break;
+	}
+	spin_lock_irqsave(&opal_plat_evt_spinlock, flags);
+	list_add_tail(&evt->link, &opal_event_queue);
+	spin_unlock_irqrestore(&opal_plat_evt_spinlock, flags);
+	wake_up_interruptible(&opal_plat_evt_wait);
+}
+
+/*
+ * opal_event_existing_status
+ *
+ * Fetch and process existing opal platform event conditions
+ * present on the system. If events detected, add them to the
+ * list which can be consumed by the user space right away.
+ */
+static void opal_event_existing_status(void)
+{
+	struct opal_platform_evt *evt;
+	int64_t dpo_timeout;
+	int16_t	epow_status[OPAL_SYSEPOW_MAX], n_epow;
+
+	fetch_epow_status(epow_status, &n_epow);
+	if (valid_epow(epow_status, n_epow)) {
+		evt = kzalloc(sizeof(struct opal_platform_evt), GFP_KERNEL);
+		if (!evt) {
+			pr_err("EPOW: Memory allocation for event failed\n");
+			return;
+		}
+		process_epow(evt->opal_event.epow.epow, epow_status, n_epow);
+		opal_event_handle_basic(evt, OPAL_PLAT_EVENT_TYPE_EPOW,
+							OPAL_EPOW_TIMEOUT);
+	}
+
+	fetch_dpo_timeout(&dpo_timeout);
+	if (dpo_timeout) {
+		evt = kzalloc(sizeof(struct opal_platform_evt), GFP_KERNEL);
+		if (!evt) {
+			pr_err("DPO: Memory allocation for event failed\n");
+			return;
+		}
+		opal_dpo_target = jiffies + dpo_timeout * HZ;
+		opal_event_handle_basic(evt, OPAL_PLAT_EVENT_TYPE_DPO,
+								dpo_timeout);
+	}
+}
+
+/* Platform EPOW message received */
+static int opal_epow_event(struct notifier_block *nb,
+				unsigned long msg_type, void *msg)
+{
+	struct opal_platform_evt *evt;
+	int16_t	epow_status[OPAL_SYSEPOW_MAX], n_epow;
+
+	if (msg_type != OPAL_MSG_EPOW)
+		return 0;
+
+	fetch_epow_status(epow_status, &n_epow);
+	if (!valid_epow(epow_status, n_epow))
+		return -EINVAL;
+
+	pr_debug("EPOW event: Power(%x) Thermal(%x) Cooling(%x)\n",
+			epow_status[0], epow_status[1], epow_status[2]);
+	evt = kzalloc(sizeof(struct opal_platform_evt), GFP_KERNEL);
+	if (!evt) {
+		pr_err("EPOW: Memory allocation for event failed\n");
+		return -ENOMEM;
+	}
+	process_epow(evt->opal_event.epow.epow, epow_status, n_epow);
+	opal_event_handle_basic(evt,
+				OPAL_PLAT_EVENT_TYPE_EPOW, OPAL_EPOW_TIMEOUT);
+	return 0;
+}
+
+/* Platform DPO message received */
+static int opal_dpo_event(struct notifier_block *nb,
+				unsigned long msg_type, void *msg)
+{
+	struct opal_platform_evt *evt;
+	int64_t dpo_timeout;
+
+	if (msg_type != OPAL_MSG_DPO)
+		return 0;
+
+	fetch_dpo_timeout(&dpo_timeout);
+	if (!dpo_timeout)
+		return -EINVAL;
+
+	pr_debug("DPO event: Timeout:%llu\n", dpo_timeout);
+	evt = kzalloc(sizeof(struct opal_platform_evt), GFP_KERNEL);
+	if (!evt) {
+		pr_err("DPO: Memory allocation for event failed\n");
+		return -ENOMEM;
+	}
+	opal_dpo_target = jiffies + dpo_timeout * HZ;
+	opal_event_handle_basic(evt, OPAL_PLAT_EVENT_TYPE_DPO, dpo_timeout);
+	return 0;
+}
+
+/* OPAL EPOW event notifier block */
+static struct notifier_block opal_epow_nb = {
+	.notifier_call  = opal_epow_event,
+	.next           = NULL,
+	.priority       = 0,
+};
+
+/* OPAL DPO event notifier block */
+static struct notifier_block opal_dpo_nb = {
+	.notifier_call  = opal_dpo_event,
+	.next           = NULL,
+	.priority       = 0,
+};
+
+/* Platform driver probe */
+static int opal_event_probe(struct platform_device *pdev)
+{
+	struct device *dev;
+	int ret;
+
+	if (opal_event_probe_finished) {
+		pr_err("%s getting called once again\n", __func__);
+		return 0;
+	}
+	opal_event_probe_finished = true;
+
+	init_timer(&opal_event_timer);
+	opal_event_timer.function = opal_event_timeout;
+	opal_event_open_flag = false;
+	opal_dpo_target = 0;
+
+	ret = alloc_chrdev_region(&opal_event_dev, 0,
+					OPAL_EVENT_MAX_DEVS, "opal_event");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "aloc_chrdev_region failed\n");
+		return ret;
+	}
+
+	opal_event_class = class_create(THIS_MODULE, "opal_event");
+	if (IS_ERR(opal_event_class)) {
+		ret = PTR_ERR(opal_event_class);
+		dev_err(&pdev->dev, "class_create failed with %d\n", ret);
+		goto fail_chrdev;
+	}
+
+	dev = device_create(opal_event_class, &pdev->dev,
+					opal_event_dev, NULL, "opal_event");
+	if (IS_ERR(dev)) {
+		ret = PTR_ERR(dev);
+		dev_err(&pdev->dev, "device_create failed with %d\n", ret);
+		goto fail_class;
+	}
+
+	cdev_init(&opal_event_cdev, &fops);
+	ret = cdev_add(&opal_event_cdev, opal_event_dev, OPAL_EVENT_MAX_DEVS);
+	if (ret < 0) {
+		dev_err(dev, "cdev_add failed\n");
+		ret = -EINVAL;
+		goto fail_device;
+	}
+
+	ret = opal_message_notifier_register(OPAL_MSG_EPOW, &opal_epow_nb);
+	if (ret) {
+		pr_err("EPOW: Platform event message notifier failed\n");
+		goto fail_cdev;
+	}
+
+	ret = opal_message_notifier_register(OPAL_MSG_DPO, &opal_dpo_nb);
+	if (ret) {
+		pr_err("DPO: Platform event message notifier failed\n");
+		opal_notifier_unregister(&opal_epow_nb);
+		goto fail_cdev;
+	}
+
+	/*
+	 * During the system boot, reboot and kexecs, the host can miss
+	 * some of the EPOW or DPO messages sent from OPAL. This ensures
+	 * that the current status of EPOW or DPO if any, is fetched and
+	 * then updated correctly. The user space needs to first read the
+	 * existing system status before entering into the poll/read loop.
+	 *
+	 */
+	opal_event_existing_status();
+	pr_info("OPAL platform event driver initialized\n");
+	return 0;
+fail_cdev:
+	cdev_del(&opal_event_cdev);
+fail_device:
+	device_destroy(opal_event_class, opal_event_dev);
+fail_class:
+	class_destroy(opal_event_class);
+fail_chrdev:
+	unregister_chrdev_region(opal_event_dev, OPAL_EVENT_MAX_DEVS);
+	return ret;
+}
+
+/* Platform driver remove */
+static int opal_event_remove(struct platform_device *pdev)
+{
+	struct opal_platform_evt *evt;
+
+	/* OPAL notifiers */
+	opal_notifier_unregister(&opal_dpo_nb);
+	opal_notifier_unregister(&opal_epow_nb);
+
+	/* Devices */
+	cdev_del(&opal_event_cdev);
+	device_destroy(opal_event_class, opal_event_dev);
+	class_destroy(opal_event_class);
+	unregister_chrdev_region(opal_event_dev, OPAL_EVENT_MAX_DEVS);
+
+	/* Timers */
+	opal_event_stop_timer();
+
+	/* Flush the list */
+	while (!list_empty(&opal_event_queue)) {
+		evt = list_first_entry(&opal_event_queue,
+					struct opal_platform_evt, link);
+		list_del(&evt->link);
+		kfree(evt);
+	}
+
+	pr_info("OPAL platform event driver exited\n");
+	return 0;
+}
+
+/* Platform driver property match */
+static struct of_device_id opal_event_match[] = {
+	{
+		.compatible	= "ibm,opal-v3-epow",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, opal_event_match);
+
+static struct platform_driver opal_event_driver = {
+	.probe	= opal_event_probe,
+	.remove = opal_event_remove,
+	.driver = {
+		.name = "opal-platform-event-driver",
+		.owner = THIS_MODULE,
+		.of_match_table = opal_event_match,
+	},
+};
+
+static int __init opal_platform_event_init(void)
+{
+	opal_event_probe_finished = false;
+	return platform_driver_register(&opal_event_driver);
+}
+
+static void __exit opal_platform_event_exit(void)
+{
+	platform_driver_unregister(&opal_event_driver);
+}
+module_init(opal_platform_event_init);
+module_exit(opal_platform_event_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anshuman Khandual <khandual@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("PowerNV OPAL platform events driver");
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index 2e6ce1b..2d1dc99 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -247,3 +247,4 @@ OPAL_CALL(opal_set_param,			OPAL_SET_PARAM);
 OPAL_CALL(opal_handle_hmi,			OPAL_HANDLE_HMI);
 OPAL_CALL(opal_register_dump_region,		OPAL_REGISTER_DUMP_REGION);
 OPAL_CALL(opal_unregister_dump_region,		OPAL_UNREGISTER_DUMP_REGION);
+OPAL_CALL(opal_get_dpo_status,			OPAL_GET_DPO_STATUS);
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index b44eec3..869dd45 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -625,7 +625,7 @@ static void __init opal_dump_region_init(void)
 }
 static int __init opal_init(void)
 {
-	struct device_node *np, *consoles;
+	struct device_node *np, *consoles, *epow;
 	const __be32 *irqs;
 	int rc, i, irqlen;
 
@@ -649,6 +649,12 @@ static int __init opal_init(void)
 		of_node_put(consoles);
 	}
 
+	epow = of_find_node_by_path("/ibm,opal/epow");
+	if (epow) {
+		of_platform_device_create(epow, "opal_event", NULL);
+		of_node_put(epow);
+	}
+
 	/* Find all OPAL interrupts and request them */
 	irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
 	pr_debug("opal: Found %d interrupts reserved for OPAL\n",
-- 
1.9.3

^ permalink raw reply related

* Re: [PATCH] KVM: PPC: BOOK3S: HV: CMA: Reserve cma region only in hypervisor mode
From: Paolo Bonzini @ 2014-09-29 11:48 UTC (permalink / raw)
  To: Alexander Graf, Aneesh Kumar K.V, benh, paulus; +Cc: linuxppc-dev, kvm, kvm-ppc
In-Reply-To: <54291841.5040903@suse.de>

Il 29/09/2014 10:28, Alexander Graf ha scritto:
> 
> 
> On 29.09.14 10:02, Aneesh Kumar K.V wrote:
>> We use cma reserved area for creating guest hash page table.
>> Don't do the reservation in non-hypervisor mode. This avoids unnecessary
>> CMA reservation when booting with limited memory configs like
>> fadump and kdump.
>>
>> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
> 
> Thanks, applied to kvm-ppc-queue.

Would you like this in 3.18?

Paolo

^ permalink raw reply

* Re: [PATCH] KVM: PPC: BOOK3S: HV: CMA: Reserve cma region only in hypervisor mode
From: Alexander Graf @ 2014-09-29 11:57 UTC (permalink / raw)
  To: Paolo Bonzini, Aneesh Kumar K.V, benh, paulus; +Cc: linuxppc-dev, kvm, kvm-ppc
In-Reply-To: <54294717.7040706@redhat.com>



On 29.09.14 13:48, Paolo Bonzini wrote:
> Il 29/09/2014 10:28, Alexander Graf ha scritto:
>>
>>
>> On 29.09.14 10:02, Aneesh Kumar K.V wrote:
>>> We use cma reserved area for creating guest hash page table.
>>> Don't do the reservation in non-hypervisor mode. This avoids unnecessary
>>> CMA reservation when booting with limited memory configs like
>>> fadump and kdump.
>>>
>>> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
>>
>> Thanks, applied to kvm-ppc-queue.
> 
> Would you like this in 3.18?

Yes, can you please directly apply it with my SoB (or Reviewed-by if you
prefer)?


Alex

^ permalink raw reply

* Re: [PATCH] KVM: PPC: BOOK3S: HV: CMA: Reserve cma region only in hypervisor mode
From: Paolo Bonzini @ 2014-09-29 12:22 UTC (permalink / raw)
  To: Alexander Graf, Aneesh Kumar K.V, benh, paulus; +Cc: linuxppc-dev, kvm, kvm-ppc
In-Reply-To: <5429492C.4020908@suse.de>

Il 29/09/2014 13:57, Alexander Graf ha scritto:
> 
> 
> On 29.09.14 13:48, Paolo Bonzini wrote:
>> Il 29/09/2014 10:28, Alexander Graf ha scritto:
>>>
>>>
>>> On 29.09.14 10:02, Aneesh Kumar K.V wrote:
>>>> We use cma reserved area for creating guest hash page table.
>>>> Don't do the reservation in non-hypervisor mode. This avoids unnecessary
>>>> CMA reservation when booting with limited memory configs like
>>>> fadump and kdump.
>>>>
>>>> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
>>>
>>> Thanks, applied to kvm-ppc-queue.
>>
>> Would you like this in 3.18?
> 
> Yes, can you please directly apply it with my SoB (or Reviewed-by if you
> prefer)?

Ok, will do.

Paolo

^ permalink raw reply

* [PATCH] powerpc/fsl: Add support for pci(e) machine check exception on E500MC / E5500
From: Guenter Roeck @ 2014-09-29 16:48 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: linux-kernel, Guenter Roeck, Paul Mackerras, Scott Wood,
	linuxppc-dev, Guenter Roeck, Jojy G Varghese

From: Jojy G Varghese <jojyv@juniper.net>

For E500MC and E5500, a machine check exception in pci(e) memory space
crashes the kernel.

Testing shows that the MCAR(U) register is zero on a MC exception for the
E5500 core. At the same time, DEAR register has been found to have the
address of the faulty load address during an MC exception for this core.

This fix changes the current behavior to fixup the result register
and instruction pointers in the case of a load operation on a faulty
PCI address.

The changes are:
- Added the hook to pci machine check handing to the e500mc machine check
  exception handler.
- For the E5500 core, load faulting address from SPRN_DEAR register.
  As mentioned above, this is necessary because the E5500 core does not
  report the fault address in the MCAR register.

Cc: Scott Wood <scottwood@freescale.com>
Signed-off-by: Jojy G Varghese <jojyv@juniper.net>
[Guenter Roeck: updated description]
Signed-off-by: Guenter Roeck <groeck@juniper.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 arch/powerpc/kernel/traps.c   | 3 ++-
 arch/powerpc/sysdev/fsl_pci.c | 5 +++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 0dc43f9..ecb709b 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -494,7 +494,8 @@ int machine_check_e500mc(struct pt_regs *regs)
 	int recoverable = 1;
 
 	if (reason & MCSR_LD) {
-		recoverable = fsl_rio_mcheck_exception(regs);
+		recoverable = fsl_rio_mcheck_exception(regs) ||
+			fsl_pci_mcheck_exception(regs);
 		if (recoverable == 1)
 			goto silent_out;
 	}
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index c507767..bdb956b 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -1021,6 +1021,11 @@ int fsl_pci_mcheck_exception(struct pt_regs *regs)
 #endif
 	addr += mfspr(SPRN_MCAR);
 
+#ifdef CONFIG_E5500_CPU
+	if (mfspr(SPRN_EPCR) & SPRN_EPCR_ICM)
+		addr = PFN_PHYS(vmalloc_to_pfn((void *)mfspr(SPRN_DEAR)));
+#endif
+
 	if (is_in_pci_mem_space(addr)) {
 		if (user_mode(regs)) {
 			pagefault_disable();
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH] powerpc/fsl: Add support for pci(e) machine check exception on E500MC / E5500
From: Scott Wood @ 2014-09-29 18:36 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: hongtao.jia, linux-kernel, Guenter Roeck, Paul Mackerras,
	linuxppc-dev, Jojy G Varghese
In-Reply-To: <1412009312-7400-1-git-send-email-linux@roeck-us.net>

On Mon, 2014-09-29 at 09:48 -0700, Guenter Roeck wrote:
> From: Jojy G Varghese <jojyv@juniper.net>
> 
> For E500MC and E5500, a machine check exception in pci(e) memory space
> crashes the kernel.
> 
> Testing shows that the MCAR(U) register is zero on a MC exception for the
> E5500 core. At the same time, DEAR register has been found to have the
> address of the faulty load address during an MC exception for this core.
> 
> This fix changes the current behavior to fixup the result register
> and instruction pointers in the case of a load operation on a faulty
> PCI address.
> 
> The changes are:
> - Added the hook to pci machine check handing to the e500mc machine check
>   exception handler.
> - For the E5500 core, load faulting address from SPRN_DEAR register.
>   As mentioned above, this is necessary because the E5500 core does not
>   report the fault address in the MCAR register.
> 
> Cc: Scott Wood <scottwood@freescale.com>
> Signed-off-by: Jojy G Varghese <jojyv@juniper.net>
> [Guenter Roeck: updated description]
> Signed-off-by: Guenter Roeck <groeck@juniper.net>
> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
> ---
>  arch/powerpc/kernel/traps.c   | 3 ++-
>  arch/powerpc/sysdev/fsl_pci.c | 5 +++++
>  2 files changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
> index 0dc43f9..ecb709b 100644
> --- a/arch/powerpc/kernel/traps.c
> +++ b/arch/powerpc/kernel/traps.c
> @@ -494,7 +494,8 @@ int machine_check_e500mc(struct pt_regs *regs)
>  	int recoverable = 1;
>  
>  	if (reason & MCSR_LD) {
> -		recoverable = fsl_rio_mcheck_exception(regs);
> +		recoverable = fsl_rio_mcheck_exception(regs) ||
> +			fsl_pci_mcheck_exception(regs);
>  		if (recoverable == 1)
>  			goto silent_out;
>  	}
> diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
> index c507767..bdb956b 100644
> --- a/arch/powerpc/sysdev/fsl_pci.c
> +++ b/arch/powerpc/sysdev/fsl_pci.c
> @@ -1021,6 +1021,11 @@ int fsl_pci_mcheck_exception(struct pt_regs *regs)
>  #endif
>  	addr += mfspr(SPRN_MCAR);
>  
> +#ifdef CONFIG_E5500_CPU
> +	if (mfspr(SPRN_EPCR) & SPRN_EPCR_ICM)
> +		addr = PFN_PHYS(vmalloc_to_pfn((void *)mfspr(SPRN_DEAR)));
> +#endif

Kconfig tells you what hardware is supported, not what hardware you're
actually running on.

Jia Hongtao, do you know anything about this issue?  Is there an
erratum?  What chips are affected by the the erratum covered by
<http://patchwork.ozlabs.org/patch/240239/>?

Can we rely on DEAR or is this just a side effect of likely having taken
a TLB miss for the address recently?  Perhaps we should use the
instruction emulation to determine the effective address instead.

Guenter, is this patch intended to deal with an erratum or are you
covering up legitimate errors?

-Scott

^ permalink raw reply

* Re: [PATCH] powerpc/fsl: Add support for pci(e) machine check exception on E500MC / E5500
From: Guenter Roeck @ 2014-09-29 19:06 UTC (permalink / raw)
  To: Scott Wood
  Cc: hongtao.jia, linux-kernel, Guenter Roeck, Paul Mackerras,
	linuxppc-dev, Jojy G Varghese
In-Reply-To: <1412015766.13320.253.camel@snotra.buserror.net>

On Mon, Sep 29, 2014 at 01:36:06PM -0500, Scott Wood wrote:
> On Mon, 2014-09-29 at 09:48 -0700, Guenter Roeck wrote:
> > From: Jojy G Varghese <jojyv@juniper.net>
> > 
> > For E500MC and E5500, a machine check exception in pci(e) memory space
> > crashes the kernel.
> > 
> > Testing shows that the MCAR(U) register is zero on a MC exception for the
> > E5500 core. At the same time, DEAR register has been found to have the
> > address of the faulty load address during an MC exception for this core.
> > 
> > This fix changes the current behavior to fixup the result register
> > and instruction pointers in the case of a load operation on a faulty
> > PCI address.
> > 
> > The changes are:
> > - Added the hook to pci machine check handing to the e500mc machine check
> >   exception handler.
> > - For the E5500 core, load faulting address from SPRN_DEAR register.
> >   As mentioned above, this is necessary because the E5500 core does not
> >   report the fault address in the MCAR register.
> > 
> > Cc: Scott Wood <scottwood@freescale.com>
> > Signed-off-by: Jojy G Varghese <jojyv@juniper.net>
> > [Guenter Roeck: updated description]
> > Signed-off-by: Guenter Roeck <groeck@juniper.net>
> > Signed-off-by: Guenter Roeck <linux@roeck-us.net>
> > ---
> >  arch/powerpc/kernel/traps.c   | 3 ++-
> >  arch/powerpc/sysdev/fsl_pci.c | 5 +++++
> >  2 files changed, 7 insertions(+), 1 deletion(-)
> > 
> > diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
> > index 0dc43f9..ecb709b 100644
> > --- a/arch/powerpc/kernel/traps.c
> > +++ b/arch/powerpc/kernel/traps.c
> > @@ -494,7 +494,8 @@ int machine_check_e500mc(struct pt_regs *regs)
> >  	int recoverable = 1;
> >  
> >  	if (reason & MCSR_LD) {
> > -		recoverable = fsl_rio_mcheck_exception(regs);
> > +		recoverable = fsl_rio_mcheck_exception(regs) ||
> > +			fsl_pci_mcheck_exception(regs);
> >  		if (recoverable == 1)
> >  			goto silent_out;
> >  	}
> > diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
> > index c507767..bdb956b 100644
> > --- a/arch/powerpc/sysdev/fsl_pci.c
> > +++ b/arch/powerpc/sysdev/fsl_pci.c
> > @@ -1021,6 +1021,11 @@ int fsl_pci_mcheck_exception(struct pt_regs *regs)
> >  #endif
> >  	addr += mfspr(SPRN_MCAR);
> >  
> > +#ifdef CONFIG_E5500_CPU
> > +	if (mfspr(SPRN_EPCR) & SPRN_EPCR_ICM)
> > +		addr = PFN_PHYS(vmalloc_to_pfn((void *)mfspr(SPRN_DEAR)));
> > +#endif
> 
> Kconfig tells you what hardware is supported, not what hardware you're
> actually running on.
> 
Hi Scott,

Good point. Jojy, guess we'll have to check if the CPU is actually an E5500.
Can you look into that ?

> Jia Hongtao, do you know anything about this issue?  Is there an
> erratum?  What chips are affected by the the erratum covered by
> <http://patchwork.ozlabs.org/patch/240239/>?
> 
We already have and use the above patch(es) in our kernel. It works fine
for E500 (P2020), but does not address E5500 (P5020/P5040).

> Can we rely on DEAR or is this just a side effect of likely having taken
> a TLB miss for the address recently?  Perhaps we should use the
> instruction emulation to determine the effective address instead.
> 
> Guenter, is this patch intended to deal with an erratum or are you
> covering up legitimate errors?
> 
Those are errors related to PCIe hotplug, and are seen with unexpected PCIe
device removals (triggered, for example, by removing power from a PCIe adapter).
The behavior we see on E5500 is quite similar to the same behavior on E500:
If unhandled, the CPU keeps executing the same instruction over and over again
if there is an error on a PCIe access and thus stalls. I don't know if this
is considered an erratum or expected behavior, but it is one we have to address
since we have to be able to handle that condition. Ultimately, we'll want to
implement PCIe error handlers for the affected drivers, but that will be a next
step.

Please let me know if you have a better solution to address this problem.

Thanks,
Guenter

^ permalink raw reply

* Re: [PATCH] powerpc/fsl: Add support for pci(e) machine check exception on E500MC / E5500
From: Scott Wood @ 2014-09-29 23:31 UTC (permalink / raw)
  To: Jojy Varghese
  Cc: hongtao.jia@freescale.com, linux-kernel@vger.kernel.org,
	Guenter Roeck, Paul Mackerras, linuxppc-dev@lists.ozlabs.org,
	Guenter Roeck
In-Reply-To: <D04F3201.10DEB%jojyv@juniper.net>

On Mon, 2014-09-29 at 23:03 +0000, Jojy Varghese wrote:
> 
> On 9/29/14 12:06 PM, "Guenter Roeck" <linux@roeck-us.net> wrote:
> 
> >On Mon, Sep 29, 2014 at 01:36:06PM -0500, Scott Wood wrote:
> >> On Mon, 2014-09-29 at 09:48 -0700, Guenter Roeck wrote:
> >> > From: Jojy G Varghese <jojyv@juniper.net>
> >> > 
> >> > For E500MC and E5500, a machine check exception in pci(e) memory space
> >> > crashes the kernel.
> >> > 
> >> > Testing shows that the MCAR(U) register is zero on a MC exception for
> >>the
> >> > E5500 core. At the same time, DEAR register has been found to have the
> >> > address of the faulty load address during an MC exception for this
> >>core.
> >> > 
> >> > This fix changes the current behavior to fixup the result register
> >> > and instruction pointers in the case of a load operation on a faulty
> >> > PCI address.
> >> > 
> >> > The changes are:
> >> > - Added the hook to pci machine check handing to the e500mc machine
> >>check
> >> >   exception handler.
> >> > - For the E5500 core, load faulting address from SPRN_DEAR register.
> >> >   As mentioned above, this is necessary because the E5500 core does
> >>not
> >> >   report the fault address in the MCAR register.
> >> > 
> >> > Cc: Scott Wood <scottwood@freescale.com>
> >> > Signed-off-by: Jojy G Varghese <jojyv@juniper.net>
> >> > [Guenter Roeck: updated description]
> >> > Signed-off-by: Guenter Roeck <groeck@juniper.net>
> >> > Signed-off-by: Guenter Roeck <linux@roeck-us.net>
> >> > ---
> >> >  arch/powerpc/kernel/traps.c   | 3 ++-
> >> >  arch/powerpc/sysdev/fsl_pci.c | 5 +++++
> >> >  2 files changed, 7 insertions(+), 1 deletion(-)
> >> > 
> >> > diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
> >> > index 0dc43f9..ecb709b 100644
> >> > --- a/arch/powerpc/kernel/traps.c
> >> > +++ b/arch/powerpc/kernel/traps.c
> >> > @@ -494,7 +494,8 @@ int machine_check_e500mc(struct pt_regs *regs)
> >> >  	int recoverable = 1;
> >> >  
> >> >  	if (reason & MCSR_LD) {
> >> > -		recoverable = fsl_rio_mcheck_exception(regs);
> >> > +		recoverable = fsl_rio_mcheck_exception(regs) ||
> >> > +			fsl_pci_mcheck_exception(regs);
> >> >  		if (recoverable == 1)
> >> >  			goto silent_out;
> >> >  	}
> >> > diff --git a/arch/powerpc/sysdev/fsl_pci.c
> >>b/arch/powerpc/sysdev/fsl_pci.c
> >> > index c507767..bdb956b 100644
> >> > --- a/arch/powerpc/sysdev/fsl_pci.c
> >> > +++ b/arch/powerpc/sysdev/fsl_pci.c
> >> > @@ -1021,6 +1021,11 @@ int fsl_pci_mcheck_exception(struct pt_regs
> >>*regs)
> >> >  #endif
> >> >  	addr += mfspr(SPRN_MCAR);
> >> >  
> >> > +#ifdef CONFIG_E5500_CPU
> >> > +	if (mfspr(SPRN_EPCR) & SPRN_EPCR_ICM)
> >> > +		addr = PFN_PHYS(vmalloc_to_pfn((void *)mfspr(SPRN_DEAR)));
> >> > +#endif
> >> 
> >> Kconfig tells you what hardware is supported, not what hardware you're
> >> actually running on.

Plus, CONFIG_E5500_CPU may not even be set when running on an e5500, as
it is used for selecting GCC optimization settings.  You could have
CONFIG_GENERIC_CPU instead.

And the subject says "E500MC / E5500", not just "E5500". :-)

> >Hi Scott,
> >
> >Good point. Jojy, guess we'll have to check if the CPU is actually an
> >E5500.
> >Can you look into that ?
> 
> 
> "/proc/cpuinfo" shows the cpu as "e5500". Scott, are you suggesting that
> we use a runtime method of determining the cpu type (cpu_spec's cpu_name
> for
> example).  

Yes, if there's a bug to be worked around, and we don't want to apply
the workaround unconditionally, you should use PVR to determine whether
you're running on an affected core.

> >> Can we rely on DEAR or is this just a side effect of likely having taken
> >> a TLB miss for the address recently?  Perhaps we should use the
> >> instruction emulation to determine the effective address instead.
> >> 
> >> Guenter, is this patch intended to deal with an erratum or are you
> >> covering up legitimate errors?
> >> 
>
> >Those are errors related to PCIe hotplug, and are seen with unexpected
> >PCIe
> >device removals (triggered, for example, by removing power from a PCIe
> >adapter).
> >The behavior we see on E5500 is quite similar to the same behavior on
> >E500:
> >If unhandled, the CPU keeps executing the same instruction over and over
> >again
> >if there is an error on a PCIe access and thus stalls. I don't know if
> >this
> >is considered an erratum or expected behavior, but it is one we have to
> >address
> >since we have to be able to handle that condition. 

The reason I ask is that the handling for e500 was described as an
erratum workaround.  If it is an erratum it would be nice to know the
erratum number and the full list of affected chips.

> >Ultimately, we'll want
> >to
> >implement PCIe error handlers for the affected drivers, but that will be
> >a next
> >step.

For now can we at least print a ratelimited error message?  I don't like
the idea of silently ignoring these errors.  I suppose it's a separate
issue from extending the workaround to cover e500mc, though.

> According to the spec, we MCAR is supposed to hold the faulty data address
> but for 5500 core, we found that MCAR is zero.

Which specific chip and revision did you see this on?  What is the value
in MCSR?

> You are right that DEAR entry could
> be a resultOf a TLB miss but that¹s the register we could rely on.

If it's the result of a previous TLB miss then we can't rely on it.  The
translation might have been loaded into the TLB before the hotplug
event, or there might have been an interrupt between loading the
translation into the TLB and using the translation.

> What do you mean by "instruction emulation"? 

mcheck_handle_load()

> Are you suggesting that we
> examine the RD, RS 
> registers for the instruction?

Yes, if we don't have a simpler reliable source of the address.

-Scott

^ permalink raw reply

* Re: [PATCH][V2] Freescale Frame Manager Device Tree binding document
From: Scott Wood @ 2014-09-29 23:33 UTC (permalink / raw)
  To: Bob Cochran
  Cc: Igal.Liberman, netdev, linuxppc-dev, Emilian.Medve, devicetree
In-Reply-To: <5425962F.7090602@mindchasers.com>

On Fri, 2014-09-26 at 12:37 -0400, Bob Cochran wrote:
> On 09/17/2014 07:08 AM, Igal.Liberman wrote:
> > From: Igal Liberman <Igal.Liberman@freescale.com>
> >
> > The Frame Manager (FMan) combines the Ethernet network interfaces with packet
> > distribution logic to provide intelligent distribution and queuing decisions
> > for incoming traffic at line rate.
> >
> > This binding document describes Freescale's Frame Manager hardware attributes
> > that are used by the Frame Manager driver for its basic initialization and
> > configuration.
> >
> > Difference between [V1] and [V2]:
> > Addressed all comments recieved from Scott in [V1]
> >
> > Signed-off-by: Igal Liberman <Igal.Liberman@freescale.com>
> 
> 
> Is there a reference (FMAN) dts file I can compare this against?  I was 
> reviewing this against my (combined) dts file for my t1040rdb built with 
> either FSL's SDK1.6 or Yocto Project meta-fsl-ppc master branch, and I'm 
> not finding a good match between the nodes / properties in my dts file 
> and what you have here.
> 
> For example, I don't find the required "clocks" or 
> "fsl,qman-channel-range" properties under my <fman0: fman@400000 ...> 
> node.
> 	
> I'm wondering if this document is trailing or leading the source in the 
> latest published FSL SDK (1.6).

The SDK's device tree binding was not considered suitable and we've
started from scratch for upstream.

-Scott

^ permalink raw reply

* Re: [PATCH 0/9] powerpc/powernv: Support for fastsleep and winkle
From: Rafael J. Wysocki @ 2014-09-29 23:28 UTC (permalink / raw)
  To: Shreyas B Prabhu
  Cc: devicetree, Srivatsa S. Bhat, linux-pm, linux-kernel,
	Grant Likely, Rob Herring, Paul Mackerras, Preeti U. Murthy,
	linuxppc-dev
In-Reply-To: <5429330A.7000103@linux.vnet.ibm.com>

On Monday, September 29, 2014 03:53:06 PM Shreyas B Prabhu wrote:
> Hi,
> Any updates on this patch series?

I have a couple of patches from there in my tree it seems.  Please have a look
at linux-pm.git/linux-next and please let me know if that's the case.


> On Thursday 18 September 2014 08:41 AM, Shreyas B Prabhu wrote:
> > Hi,
> > 
> > In this patch series we use winkle for offlined cores. I successfully
> > tested the working of this with subcore functionality.
> > 
> > Test scenario was as follows:
> > 1. Set SMT mode to 1, Set subores-per-core to 1
> > 2. Offline a core, in this case cpu 32 (sending it to winkle)
> > 3. Set subcores-per-core to 4
> > 4. Online the core
> > 5. Start a guest (Topology 1 core 2 threads) on a subcore, in this case
> > on cpu 36
> > 
> > This works without any glitch.
> > 
> > Thanks,
> > Shreyas
> > 
> > On Monday 25 August 2014 11:31 PM, Shreyas B. Prabhu wrote:
> >> Fast sleep is an idle state, where the core and the L1 and L2
> >> caches are brought down to a threshold voltage. This also means that
> >> the communication between L2 and L3 caches have to be fenced. However
> >> the current P8 chips have a bug wherein this fencing between L2 and
> >> L3 caches get delayed by a cpu cycle. This can delay L3 response to
> >> the other cpus if they request for data during this time. Thus they
> >> would fetch the same data from the memory which could lead to data
> >> corruption if L3 cache is not flushed.
> >> Patch 4 adds support to work around this.
> >>
> >> 'Deep Winkle' is a deeper idle state where core and private L2 are powered
> >> off. While it offers higher power savings, it is at the cost of losing
> >> hypervisor register state and higher latency.
> >> Patch 5-9 adds support for winkle and uses it for offline cpus.
> >>
> >> Patch 1 - Moves parameters required discover idle states to a location 
> >> common to both cpuidle driver and powernv core code
> >> Patch 2 - Populates idle state details from device tree
> >> Patch 3 - Enables cpus to run guest after waking up from fastsleep/winkle
> >>
> >>
> >> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> >> Cc: Paul Mackerras <paulus@samba.org>
> >> Cc: Michael Ellerman <mpe@ellerman.id.au>
> >> Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
> >> Cc: Srivatsa S. Bhat <srivatsa@MIT.EDU>
> >> Cc: Preeti U. Murthy <preeti@linux.vnet.ibm.com>
> >> Cc: Vaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>
> >> Cc: Rob Herring <robh+dt@kernel.org>
> >> Cc: Grant Likely <grant.likely@linaro.org>
> >> Cc: devicetree@vger.kernel.org
> >> Cc: linux-pm@vger.kernel.org
> >> Cc: linuxppc-dev@lists.ozlabs.org
> >>
> >> Preeti U Murthy (2):
> >>   cpuidle/powernv: Populate cpuidle state details by querying the
> >>     device-tree
> >>   powerpc/powernv/cpuidle: Add workaround to enable fastsleep
> >>
> >> Shreyas B. Prabhu (6):
> >>   powerpc/kvm/book3s_hv: Enable CPUs to run guest after waking up from
> >>     fast-sleep
> >>   powerpc/powernv: Add OPAL call to save and restore
> >>   powerpc: Adding macro for accessing Thread Switch Control Register
> >>   powerpc/powernv: Add winkle infrastructure
> >>   powerpc/powernv: Discover and enable winkle
> >>   powerpc/powernv: Enter deepest supported idle state in offline
> >>
> >> Srivatsa S. Bhat (1):
> >>   powerpc/powernv: Enable Offline CPUs to enter deep idle states
> >>
> >>  arch/powerpc/include/asm/machdep.h             |   4 +
> >>  arch/powerpc/include/asm/opal.h                |  10 ++
> >>  arch/powerpc/include/asm/paca.h                |   3 +
> >>  arch/powerpc/include/asm/ppc-opcode.h          |   2 +
> >>  arch/powerpc/include/asm/processor.h           |   6 +-
> >>  arch/powerpc/include/asm/reg.h                 |   1 +
> >>  arch/powerpc/kernel/asm-offsets.c              |   1 +
> >>  arch/powerpc/kernel/exceptions-64s.S           |  37 ++---
> >>  arch/powerpc/kernel/idle.c                     |  30 ++++
> >>  arch/powerpc/kernel/idle_power7.S              |  83 +++++++++-
> >>  arch/powerpc/platforms/powernv/opal-wrappers.S |   2 +
> >>  arch/powerpc/platforms/powernv/powernv.h       |   8 +
> >>  arch/powerpc/platforms/powernv/setup.c         | 217 +++++++++++++++++++++++++
> >>  arch/powerpc/platforms/powernv/smp.c           |  13 +-
> >>  arch/powerpc/platforms/powernv/subcore.c       |  15 ++
> >>  drivers/cpuidle/cpuidle-powernv.c              |  40 ++++-
> >>  16 files changed, 439 insertions(+), 33 deletions(-)
> >>
> 

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply

* Re: [PATCH] powerpc/fsl: Add support for pci(e) machine check exception on E500MC / E5500
From: Jojy Varghese @ 2014-09-29 23:03 UTC (permalink / raw)
  To: Guenter Roeck, Scott Wood
  Cc: hongtao.jia@freescale.com, Guenter Roeck,
	linux-kernel@vger.kernel.org, Paul Mackerras,
	linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20140929190643.GA20189@roeck-us.net>

DQoNCk9uIDkvMjkvMTQgMTI6MDYgUE0sICJHdWVudGVyIFJvZWNrIiA8bGludXhAcm9lY2stdXMu
bmV0PiB3cm90ZToNCg0KPk9uIE1vbiwgU2VwIDI5LCAyMDE0IGF0IDAxOjM2OjA2UE0gLTA1MDAs
IFNjb3R0IFdvb2Qgd3JvdGU6DQo+PiBPbiBNb24sIDIwMTQtMDktMjkgYXQgMDk6NDggLTA3MDAs
IEd1ZW50ZXIgUm9lY2sgd3JvdGU6DQo+PiA+IEZyb206IEpvankgRyBWYXJnaGVzZSA8am9qeXZA
anVuaXBlci5uZXQ+DQo+PiA+IA0KPj4gPiBGb3IgRTUwME1DIGFuZCBFNTUwMCwgYSBtYWNoaW5l
IGNoZWNrIGV4Y2VwdGlvbiBpbiBwY2koZSkgbWVtb3J5IHNwYWNlDQo+PiA+IGNyYXNoZXMgdGhl
IGtlcm5lbC4NCj4+ID4gDQo+PiA+IFRlc3Rpbmcgc2hvd3MgdGhhdCB0aGUgTUNBUihVKSByZWdp
c3RlciBpcyB6ZXJvIG9uIGEgTUMgZXhjZXB0aW9uIGZvcg0KPj50aGUNCj4+ID4gRTU1MDAgY29y
ZS4gQXQgdGhlIHNhbWUgdGltZSwgREVBUiByZWdpc3RlciBoYXMgYmVlbiBmb3VuZCB0byBoYXZl
IHRoZQ0KPj4gPiBhZGRyZXNzIG9mIHRoZSBmYXVsdHkgbG9hZCBhZGRyZXNzIGR1cmluZyBhbiBN
QyBleGNlcHRpb24gZm9yIHRoaXMNCj4+Y29yZS4NCj4+ID4gDQo+PiA+IFRoaXMgZml4IGNoYW5n
ZXMgdGhlIGN1cnJlbnQgYmVoYXZpb3IgdG8gZml4dXAgdGhlIHJlc3VsdCByZWdpc3Rlcg0KPj4g
PiBhbmQgaW5zdHJ1Y3Rpb24gcG9pbnRlcnMgaW4gdGhlIGNhc2Ugb2YgYSBsb2FkIG9wZXJhdGlv
biBvbiBhIGZhdWx0eQ0KPj4gPiBQQ0kgYWRkcmVzcy4NCj4+ID4gDQo+PiA+IFRoZSBjaGFuZ2Vz
IGFyZToNCj4+ID4gLSBBZGRlZCB0aGUgaG9vayB0byBwY2kgbWFjaGluZSBjaGVjayBoYW5kaW5n
IHRvIHRoZSBlNTAwbWMgbWFjaGluZQ0KPj5jaGVjaw0KPj4gPiAgIGV4Y2VwdGlvbiBoYW5kbGVy
Lg0KPj4gPiAtIEZvciB0aGUgRTU1MDAgY29yZSwgbG9hZCBmYXVsdGluZyBhZGRyZXNzIGZyb20g
U1BSTl9ERUFSIHJlZ2lzdGVyLg0KPj4gPiAgIEFzIG1lbnRpb25lZCBhYm92ZSwgdGhpcyBpcyBu
ZWNlc3NhcnkgYmVjYXVzZSB0aGUgRTU1MDAgY29yZSBkb2VzDQo+Pm5vdA0KPj4gPiAgIHJlcG9y
dCB0aGUgZmF1bHQgYWRkcmVzcyBpbiB0aGUgTUNBUiByZWdpc3Rlci4NCj4+ID4gDQo+PiA+IENj
OiBTY290dCBXb29kIDxzY290dHdvb2RAZnJlZXNjYWxlLmNvbT4NCj4+ID4gU2lnbmVkLW9mZi1i
eTogSm9qeSBHIFZhcmdoZXNlIDxqb2p5dkBqdW5pcGVyLm5ldD4NCj4+ID4gW0d1ZW50ZXIgUm9l
Y2s6IHVwZGF0ZWQgZGVzY3JpcHRpb25dDQo+PiA+IFNpZ25lZC1vZmYtYnk6IEd1ZW50ZXIgUm9l
Y2sgPGdyb2Vja0BqdW5pcGVyLm5ldD4NCj4+ID4gU2lnbmVkLW9mZi1ieTogR3VlbnRlciBSb2Vj
ayA8bGludXhAcm9lY2stdXMubmV0Pg0KPj4gPiAtLS0NCj4+ID4gIGFyY2gvcG93ZXJwYy9rZXJu
ZWwvdHJhcHMuYyAgIHwgMyArKy0NCj4+ID4gIGFyY2gvcG93ZXJwYy9zeXNkZXYvZnNsX3BjaS5j
IHwgNSArKysrKw0KPj4gPiAgMiBmaWxlcyBjaGFuZ2VkLCA3IGluc2VydGlvbnMoKyksIDEgZGVs
ZXRpb24oLSkNCj4+ID4gDQo+PiA+IGRpZmYgLS1naXQgYS9hcmNoL3Bvd2VycGMva2VybmVsL3Ry
YXBzLmMgYi9hcmNoL3Bvd2VycGMva2VybmVsL3RyYXBzLmMNCj4+ID4gaW5kZXggMGRjNDNmOS4u
ZWNiNzA5YiAxMDA2NDQNCj4+ID4gLS0tIGEvYXJjaC9wb3dlcnBjL2tlcm5lbC90cmFwcy5jDQo+
PiA+ICsrKyBiL2FyY2gvcG93ZXJwYy9rZXJuZWwvdHJhcHMuYw0KPj4gPiBAQCAtNDk0LDcgKzQ5
NCw4IEBAIGludCBtYWNoaW5lX2NoZWNrX2U1MDBtYyhzdHJ1Y3QgcHRfcmVncyAqcmVncykNCj4+
ID4gIAlpbnQgcmVjb3ZlcmFibGUgPSAxOw0KPj4gPiAgDQo+PiA+ICAJaWYgKHJlYXNvbiAmIE1D
U1JfTEQpIHsNCj4+ID4gLQkJcmVjb3ZlcmFibGUgPSBmc2xfcmlvX21jaGVja19leGNlcHRpb24o
cmVncyk7DQo+PiA+ICsJCXJlY292ZXJhYmxlID0gZnNsX3Jpb19tY2hlY2tfZXhjZXB0aW9uKHJl
Z3MpIHx8DQo+PiA+ICsJCQlmc2xfcGNpX21jaGVja19leGNlcHRpb24ocmVncyk7DQo+PiA+ICAJ
CWlmIChyZWNvdmVyYWJsZSA9PSAxKQ0KPj4gPiAgCQkJZ290byBzaWxlbnRfb3V0Ow0KPj4gPiAg
CX0NCj4+ID4gZGlmZiAtLWdpdCBhL2FyY2gvcG93ZXJwYy9zeXNkZXYvZnNsX3BjaS5jDQo+PmIv
YXJjaC9wb3dlcnBjL3N5c2Rldi9mc2xfcGNpLmMNCj4+ID4gaW5kZXggYzUwNzc2Ny4uYmRiOTU2
YiAxMDA2NDQNCj4+ID4gLS0tIGEvYXJjaC9wb3dlcnBjL3N5c2Rldi9mc2xfcGNpLmMNCj4+ID4g
KysrIGIvYXJjaC9wb3dlcnBjL3N5c2Rldi9mc2xfcGNpLmMNCj4+ID4gQEAgLTEwMjEsNiArMTAy
MSwxMSBAQCBpbnQgZnNsX3BjaV9tY2hlY2tfZXhjZXB0aW9uKHN0cnVjdCBwdF9yZWdzDQo+Pipy
ZWdzKQ0KPj4gPiAgI2VuZGlmDQo+PiA+ICAJYWRkciArPSBtZnNwcihTUFJOX01DQVIpOw0KPj4g
PiAgDQo+PiA+ICsjaWZkZWYgQ09ORklHX0U1NTAwX0NQVQ0KPj4gPiArCWlmIChtZnNwcihTUFJO
X0VQQ1IpICYgU1BSTl9FUENSX0lDTSkNCj4+ID4gKwkJYWRkciA9IFBGTl9QSFlTKHZtYWxsb2Nf
dG9fcGZuKCh2b2lkICopbWZzcHIoU1BSTl9ERUFSKSkpOw0KPj4gPiArI2VuZGlmDQo+PiANCj4+
IEtjb25maWcgdGVsbHMgeW91IHdoYXQgaGFyZHdhcmUgaXMgc3VwcG9ydGVkLCBub3Qgd2hhdCBo
YXJkd2FyZSB5b3UncmUNCj4+IGFjdHVhbGx5IHJ1bm5pbmcgb24uDQo+PiANCj5IaSBTY290dCwN
Cj4NCj5Hb29kIHBvaW50LiBKb2p5LCBndWVzcyB3ZSdsbCBoYXZlIHRvIGNoZWNrIGlmIHRoZSBD
UFUgaXMgYWN0dWFsbHkgYW4NCj5FNTUwMC4NCj5DYW4geW91IGxvb2sgaW50byB0aGF0ID8NCg0K
DQoiL3Byb2MvY3B1aW5mbyIgc2hvd3MgdGhlIGNwdSBhcyAiZTU1MDAiLiBTY290dCwgYXJlIHlv
dSBzdWdnZXN0aW5nIHRoYXQNCndlIHVzZSBhIHJ1bnRpbWUgbWV0aG9kIG9mIGRldGVybWluaW5n
IHRoZSBjcHUgdHlwZSAoY3B1X3NwZWMncyBjcHVfbmFtZQ0KZm9yDQpleGFtcGxlKS4gIA0KDQoN
Cj4NCj4+IEppYSBIb25ndGFvLCBkbyB5b3Uga25vdyBhbnl0aGluZyBhYm91dCB0aGlzIGlzc3Vl
PyAgSXMgdGhlcmUgYW4NCj4+IGVycmF0dW0/ICBXaGF0IGNoaXBzIGFyZSBhZmZlY3RlZCBieSB0
aGUgdGhlIGVycmF0dW0gY292ZXJlZCBieQ0KPj4gPGh0dHA6Ly9wYXRjaHdvcmsub3psYWJzLm9y
Zy9wYXRjaC8yNDAyMzkvPj8NCj4+IA0KPldlIGFscmVhZHkgaGF2ZSBhbmQgdXNlIHRoZSBhYm92
ZSBwYXRjaChlcykgaW4gb3VyIGtlcm5lbC4gSXQgd29ya3MgZmluZQ0KPmZvciBFNTAwIChQMjAy
MCksIGJ1dCBkb2VzIG5vdCBhZGRyZXNzIEU1NTAwIChQNTAyMC9QNTA0MCkuDQo+DQo+PiBDYW4g
d2UgcmVseSBvbiBERUFSIG9yIGlzIHRoaXMganVzdCBhIHNpZGUgZWZmZWN0IG9mIGxpa2VseSBo
YXZpbmcgdGFrZW4NCj4+IGEgVExCIG1pc3MgZm9yIHRoZSBhZGRyZXNzIHJlY2VudGx5PyAgUGVy
aGFwcyB3ZSBzaG91bGQgdXNlIHRoZQ0KPj4gaW5zdHJ1Y3Rpb24gZW11bGF0aW9uIHRvIGRldGVy
bWluZSB0aGUgZWZmZWN0aXZlIGFkZHJlc3MgaW5zdGVhZC4NCj4+IA0KPj4gR3VlbnRlciwgaXMg
dGhpcyBwYXRjaCBpbnRlbmRlZCB0byBkZWFsIHdpdGggYW4gZXJyYXR1bSBvciBhcmUgeW91DQo+
PiBjb3ZlcmluZyB1cCBsZWdpdGltYXRlIGVycm9ycz8NCj4+IA0KPlRob3NlIGFyZSBlcnJvcnMg
cmVsYXRlZCB0byBQQ0llIGhvdHBsdWcsIGFuZCBhcmUgc2VlbiB3aXRoIHVuZXhwZWN0ZWQNCj5Q
Q0llDQo+ZGV2aWNlIHJlbW92YWxzICh0cmlnZ2VyZWQsIGZvciBleGFtcGxlLCBieSByZW1vdmlu
ZyBwb3dlciBmcm9tIGEgUENJZQ0KPmFkYXB0ZXIpLg0KPlRoZSBiZWhhdmlvciB3ZSBzZWUgb24g
RTU1MDAgaXMgcXVpdGUgc2ltaWxhciB0byB0aGUgc2FtZSBiZWhhdmlvciBvbg0KPkU1MDA6DQo+
SWYgdW5oYW5kbGVkLCB0aGUgQ1BVIGtlZXBzIGV4ZWN1dGluZyB0aGUgc2FtZSBpbnN0cnVjdGlv
biBvdmVyIGFuZCBvdmVyDQo+YWdhaW4NCj5pZiB0aGVyZSBpcyBhbiBlcnJvciBvbiBhIFBDSWUg
YWNjZXNzIGFuZCB0aHVzIHN0YWxscy4gSSBkb24ndCBrbm93IGlmDQo+dGhpcw0KPmlzIGNvbnNp
ZGVyZWQgYW4gZXJyYXR1bSBvciBleHBlY3RlZCBiZWhhdmlvciwgYnV0IGl0IGlzIG9uZSB3ZSBo
YXZlIHRvDQo+YWRkcmVzcw0KPnNpbmNlIHdlIGhhdmUgdG8gYmUgYWJsZSB0byBoYW5kbGUgdGhh
dCBjb25kaXRpb24uIFVsdGltYXRlbHksIHdlJ2xsIHdhbnQNCj50bw0KPmltcGxlbWVudCBQQ0ll
IGVycm9yIGhhbmRsZXJzIGZvciB0aGUgYWZmZWN0ZWQgZHJpdmVycywgYnV0IHRoYXQgd2lsbCBi
ZQ0KPmEgbmV4dA0KPnN0ZXAuDQoNCkFjY29yZGluZyB0byB0aGUgc3BlYywgd2UgTUNBUiBpcyBz
dXBwb3NlZCB0byBob2xkIHRoZSBmYXVsdHkgZGF0YSBhZGRyZXNzDQpidXQgZm9yIDU1MDAgY29y
ZSwgd2UgZm91bmQgdGhhdCBNQ0FSIGlzIHplcm8uIFlvdSBhcmUgcmlnaHQgdGhhdCBERUFSDQpl
bnRyeSBjb3VsZA0KYmUgYSByZXN1bHRPZiBhIFRMQiBtaXNzIGJ1dCB0aGF0qfZzIHRoZSByZWdp
c3RlciB3ZSBjb3VsZCByZWx5IG9uLg0KDQpXaGF0IGRvIHlvdSBtZWFuIGJ5ICJpbnN0cnVjdGlv
biBlbXVsYXRpb24iPyBBcmUgeW91IHN1Z2dlc3RpbmcgdGhhdCB3ZQ0KZXhhbWluZSB0aGUgUkQs
IFJTIA0KcmVnaXN0ZXJzIGZvciB0aGUgaW5zdHJ1Y3Rpb24/DQoNCg0KDQo+DQo+UGxlYXNlIGxl
dCBtZSBrbm93IGlmIHlvdSBoYXZlIGEgYmV0dGVyIHNvbHV0aW9uIHRvIGFkZHJlc3MgdGhpcyBw
cm9ibGVtLg0KPg0KPlRoYW5rcywNCj5HdWVudGVyDQoNCg0KVGhhbmtzDQpKb2p5DQoNCg==

^ permalink raw reply

* Re: [PATCHv4] clk: ppc-corenet: rename to ppc-qoriq and add CLK_OF_DECLARE support
From: Scott Wood @ 2014-09-29 23:57 UTC (permalink / raw)
  To: Tang Yuantian-B29983
  Cc: linuxppc-dev@lists.ozlabs.org, Mike Turquette,
	linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, Lu Jingchang-B35083
In-Reply-To: <d637914864824bda8d0dd62639e9097b@DM2PR03MB574.namprd03.prod.outlook.com>

On Sat, 2014-09-27 at 21:18 -0500, Tang Yuantian-B29983 wrote:
> > -----Original Message-----
> > From: Linuxppc-dev
> > [mailto:linuxppc-dev-bounces+b29983=freescale.com@lists.ozlabs.org] On
> > Behalf Of Mike Turquette
> > Sent: Saturday, September 27, 2014 7:29 AM
> > To: Wood Scott-B07421
> > Cc: linuxppc-dev@lists.ozlabs.org; linux-kernel@vger.kernel.org;
> > linux-arm-kernel@lists.infradead.org; Lu Jingchang-B35083
> > Subject: Re: [PATCHv4] clk: ppc-corenet: rename to ppc-qoriq and add
> > CLK_OF_DECLARE support
> > 
> > Quoting Scott Wood (2014-09-25 15:56:20)
> > > On Thu, 2014-09-25 at 15:54 -0700, Mike Turquette wrote:
> > > > Quoting Scott Wood (2014-09-25 13:08:00)
> > > > > Well, like I said, I'd rather see the CLK_OF_DECLARE stuff be made
> > > > > to work on PPC rather than have the driver carry around two
> > > > > binding methods.
> > > >
> > > > I guess that is an existing problem, and not related directly to
> > > > this patch? This patch is essentially just renames (though the
> > > > V1.0/V2.0 stuff seems weird).
> > >
> > > This patch is adding CLK_OF_DECLARE.
> > 
> > I'm fine taking this patch but your comments are still unresolved. What do you
> > think needs to be done to fix the problems that you see?
> > 
> CLK_OF_DECLARE is totally worked on PPC. I will do it in a separate patch.
> Regarding V1.0 and V2.0, it is not wired just same for now. But we are not sure if it is same for v3.0 in the future.
> 
> Besides updating drivers/cpufreq/Kconfig.powerpc, there is one more thing I am not comfortable with:
> This patch uses " fixed-clock" as sysclk's compatible string, while on PPC we treated it as " fsl,qoriq-sysclk-[1-2].0".
> That's inconsistent on both ARM and PPC platforms, neither did on bindings.

fsl,qoriq-sysclk-XXX is the way it is because of compatibility with the
fixups in existing U-Boots.  It shouldn't be used as a model.

That said, I don't think you really mean "this patch", as it doesn't
contain the device tree updates, and "fixed-clock" does not appear.

-Scott

^ permalink raw reply

* [PATCH 02/21] powerpc/eeh: Add eeh_pe_state sysfs entry
From: Gavin Shan @ 2014-09-30  2:38 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1412044750-24460-1-git-send-email-gwshan@linux.vnet.ibm.com>

The patch adds sysfs entry "eeh_pe_state". Reading on it returns
the PE's state while writing to it clears the frozen state. It's
used to check or clear the PE frozen state from userland for
debugging purpose.

The patch also replaces printk(KERN_WARNING ...) with pr_warn() in
eeh_sysfs.c

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
v2: Not output PE number and return error from the sysfs entry
    if necessary
---
 arch/powerpc/kernel/eeh_sysfs.c | 60 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 59 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/kernel/eeh_sysfs.c b/arch/powerpc/kernel/eeh_sysfs.c
index e2595ba..eb15be4 100644
--- a/arch/powerpc/kernel/eeh_sysfs.c
+++ b/arch/powerpc/kernel/eeh_sysfs.c
@@ -54,6 +54,62 @@ EEH_SHOW_ATTR(eeh_mode,            mode,            "0x%x");
 EEH_SHOW_ATTR(eeh_config_addr,     config_addr,     "0x%x");
 EEH_SHOW_ATTR(eeh_pe_config_addr,  pe_config_addr,  "0x%x");
 
+static ssize_t eeh_pe_state_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
+	int state;
+
+	if (!edev || !edev->pe)
+		return -ENODEV;
+
+	state = eeh_ops->get_state(edev->pe, NULL);
+	return sprintf(buf, "%08x %08x\n",
+		       state, edev->pe->state);
+}
+
+static ssize_t eeh_pe_state_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
+	int ret;
+
+	if (!edev || !edev->pe)
+		return -ENODEV;
+
+	/* Nothing to do if it's not frozen */
+	if (!(edev->pe->state & EEH_PE_ISOLATED))
+		return count;
+
+	/* Enable MMIO */
+	ret = eeh_pci_enable(edev->pe, EEH_OPT_THAW_MMIO);
+	if (ret) {
+		pr_warn("%s: Failure %d enabling MMIO for PHB#%d-PE#%d\n",
+			__func__, ret, edev->pe->phb->global_number,
+			edev->pe->addr);
+		return -EIO;
+	}
+
+	/* Enable DMA */
+	ret = eeh_pci_enable(edev->pe, EEH_OPT_THAW_DMA);
+	if (ret) {
+		pr_warn("%s: Failure %d enabling DMA for PHB#%d-PE#%d\n",
+			__func__, ret, edev->pe->phb->global_number,
+			edev->pe->addr);
+		return -EIO;
+	}
+
+	/* Clear software state */
+	eeh_pe_state_clear(edev->pe, EEH_PE_ISOLATED);
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(eeh_pe_state);
+
 void eeh_sysfs_add_device(struct pci_dev *pdev)
 {
 	struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
@@ -68,9 +124,10 @@ void eeh_sysfs_add_device(struct pci_dev *pdev)
 	rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode);
 	rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr);
 	rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
+	rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_state);
 
 	if (rc)
-		printk(KERN_WARNING "EEH: Unable to create sysfs entries\n");
+		pr_warn("EEH: Unable to create sysfs entries\n");
 	else if (edev)
 		edev->mode |= EEH_DEV_SYSFS;
 }
@@ -92,6 +149,7 @@ void eeh_sysfs_remove_device(struct pci_dev *pdev)
 	device_remove_file(&pdev->dev, &dev_attr_eeh_mode);
 	device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr);
 	device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
+	device_remove_file(&pdev->dev, &dev_attr_eeh_pe_state);
 
 	if (edev)
 		edev->mode &= ~EEH_DEV_SYSFS;
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH 01/21] powerpc/eeh: Drop unused argument in eeh_check_failure()
From: Gavin Shan @ 2014-09-30  2:38 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Vishal Mansur, Gavin Shan

eeh_check_failure() is used to check frozen state of the PE which
owns the indicated I/O address. The argument "val" of the function
isn't used. The patch drops it and return the frozen state of the
PE as expected.

Cc: Vishal Mansur <vmansur@linux.vnet.ibm.com>
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/eeh.h | 29 ++++++++++++++---------------
 arch/powerpc/kernel/eeh.c      | 15 ++++++---------
 2 files changed, 20 insertions(+), 24 deletions(-)

diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 9983c3d..adcddb1 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -269,8 +269,7 @@ void eeh_dev_phb_init_dynamic(struct pci_controller *phb);
 int eeh_init(void);
 int __init eeh_ops_register(struct eeh_ops *ops);
 int __exit eeh_ops_unregister(const char *name);
-unsigned long eeh_check_failure(const volatile void __iomem *token,
-				unsigned long val);
+int eeh_check_failure(const volatile void __iomem *token);
 int eeh_dev_check_failure(struct eeh_dev *edev);
 void eeh_addr_cache_build(void);
 void eeh_add_device_early(struct device_node *);
@@ -321,9 +320,9 @@ static inline void *eeh_dev_init(struct device_node *dn, void *data)
 
 static inline void eeh_dev_phb_init_dynamic(struct pci_controller *phb) { }
 
-static inline unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
+static inline int eeh_check_failure(const volatile void __iomem *token)
 {
-	return val;
+	return 0;
 }
 
 #define eeh_dev_check_failure(x) (0)
@@ -354,7 +353,7 @@ static inline u8 eeh_readb(const volatile void __iomem *addr)
 {
 	u8 val = in_8(addr);
 	if (EEH_POSSIBLE_ERROR(val, u8))
-		return eeh_check_failure(addr, val);
+		eeh_check_failure(addr);
 	return val;
 }
 
@@ -362,7 +361,7 @@ static inline u16 eeh_readw(const volatile void __iomem *addr)
 {
 	u16 val = in_le16(addr);
 	if (EEH_POSSIBLE_ERROR(val, u16))
-		return eeh_check_failure(addr, val);
+		eeh_check_failure(addr);
 	return val;
 }
 
@@ -370,7 +369,7 @@ static inline u32 eeh_readl(const volatile void __iomem *addr)
 {
 	u32 val = in_le32(addr);
 	if (EEH_POSSIBLE_ERROR(val, u32))
-		return eeh_check_failure(addr, val);
+		eeh_check_failure(addr);
 	return val;
 }
 
@@ -378,7 +377,7 @@ static inline u64 eeh_readq(const volatile void __iomem *addr)
 {
 	u64 val = in_le64(addr);
 	if (EEH_POSSIBLE_ERROR(val, u64))
-		return eeh_check_failure(addr, val);
+		eeh_check_failure(addr);
 	return val;
 }
 
@@ -386,7 +385,7 @@ static inline u16 eeh_readw_be(const volatile void __iomem *addr)
 {
 	u16 val = in_be16(addr);
 	if (EEH_POSSIBLE_ERROR(val, u16))
-		return eeh_check_failure(addr, val);
+		eeh_check_failure(addr);
 	return val;
 }
 
@@ -394,7 +393,7 @@ static inline u32 eeh_readl_be(const volatile void __iomem *addr)
 {
 	u32 val = in_be32(addr);
 	if (EEH_POSSIBLE_ERROR(val, u32))
-		return eeh_check_failure(addr, val);
+		eeh_check_failure(addr);
 	return val;
 }
 
@@ -402,7 +401,7 @@ static inline u64 eeh_readq_be(const volatile void __iomem *addr)
 {
 	u64 val = in_be64(addr);
 	if (EEH_POSSIBLE_ERROR(val, u64))
-		return eeh_check_failure(addr, val);
+		eeh_check_failure(addr);
 	return val;
 }
 
@@ -416,7 +415,7 @@ static inline void eeh_memcpy_fromio(void *dest, const
 	 * were copied. Check all four bytes.
 	 */
 	if (n >= 4 && EEH_POSSIBLE_ERROR(*((u32 *)(dest + n - 4)), u32))
-		eeh_check_failure(src, *((u32 *)(dest + n - 4)));
+		eeh_check_failure(src);
 }
 
 /* in-string eeh macros */
@@ -425,7 +424,7 @@ static inline void eeh_readsb(const volatile void __iomem *addr, void * buf,
 {
 	_insb(addr, buf, ns);
 	if (EEH_POSSIBLE_ERROR((*(((u8*)buf)+ns-1)), u8))
-		eeh_check_failure(addr, *(u8*)buf);
+		eeh_check_failure(addr);
 }
 
 static inline void eeh_readsw(const volatile void __iomem *addr, void * buf,
@@ -433,7 +432,7 @@ static inline void eeh_readsw(const volatile void __iomem *addr, void * buf,
 {
 	_insw(addr, buf, ns);
 	if (EEH_POSSIBLE_ERROR((*(((u16*)buf)+ns-1)), u16))
-		eeh_check_failure(addr, *(u16*)buf);
+		eeh_check_failure(addr);
 }
 
 static inline void eeh_readsl(const volatile void __iomem *addr, void * buf,
@@ -441,7 +440,7 @@ static inline void eeh_readsl(const volatile void __iomem *addr, void * buf,
 {
 	_insl(addr, buf, nl);
 	if (EEH_POSSIBLE_ERROR((*(((u32*)buf)+nl-1)), u32))
-		eeh_check_failure(addr, *(u32*)buf);
+		eeh_check_failure(addr);
 }
 
 #endif /* CONFIG_PPC64 */
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 59a64f8..17cf52ba 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -542,17 +542,16 @@ EXPORT_SYMBOL_GPL(eeh_dev_check_failure);
 
 /**
  * eeh_check_failure - Check if all 1's data is due to EEH slot freeze
- * @token: I/O token, should be address in the form 0xA....
- * @val: value, should be all 1's (XXX why do we need this arg??)
+ * @token: I/O address
  *
- * Check for an EEH failure at the given token address.  Call this
+ * Check for an EEH failure at the given I/O address. Call this
  * routine if the result of a read was all 0xff's and you want to
- * find out if this is due to an EEH slot freeze event.  This routine
+ * find out if this is due to an EEH slot freeze event. This routine
  * will query firmware for the EEH status.
  *
  * Note this routine is safe to call in an interrupt context.
  */
-unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
+int eeh_check_failure(const volatile void __iomem *token)
 {
 	unsigned long addr;
 	struct eeh_dev *edev;
@@ -562,13 +561,11 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
 	edev = eeh_addr_cache_get_dev(addr);
 	if (!edev) {
 		eeh_stats.no_device++;
-		return val;
+		return 0;
 	}
 
-	eeh_dev_check_failure(edev);
-	return val;
+	return eeh_dev_check_failure(edev);
 }
-
 EXPORT_SYMBOL(eeh_check_failure);
 
 
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH 09/21] powerpc/powernv: Clear PAPR error injection registers
From: Gavin Shan @ 2014-09-30  2:38 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1412044750-24460-1-git-send-email-gwshan@linux.vnet.ibm.com>

The frozen state on one specific PE is probably caused by error
injection, which is done with help of PAPR error injection registers.
According to the hardware spec, those registers should be cleared
automatically after one-shot frozen PE. However, that's not always
true, at least on P7IOC of Firebird-L. So we have to clear them
before doing PE reset to avoid recursive EEH errors at recovery
stage.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/powernv/eeh-ioda.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c
index 3389cf0..e516b7f 100644
--- a/arch/powerpc/platforms/powernv/eeh-ioda.c
+++ b/arch/powerpc/platforms/powernv/eeh-ioda.c
@@ -682,6 +682,31 @@ static int ioda_eeh_reset(struct eeh_pe *pe, int option)
 	if (pe->type & EEH_PE_PHB) {
 		ret = ioda_eeh_phb_reset(hose, option);
 	} else {
+		struct pnv_phb *phb;
+		s64 rc;
+
+		/*
+		 * The frozen PE might be caused by PAPR error injection
+		 * registers, which are expected to be cleared after hitting
+		 * frozen PE as stated in the hardware spec. Unfortunately,
+		 * that's not true on P7IOC. So we have to clear it manually
+		 * to avoid recursive EEH errors during recovery.
+		 */
+		phb = hose->private_data;
+		if (phb->model == PNV_PHB_MODEL_P7IOC &&
+		    (option == EEH_RESET_HOT ||
+		    option == EEH_RESET_FUNDAMENTAL)) {
+			rc = opal_pci_reset(phb->opal_id,
+					    OPAL_PHB_ERROR,
+					    OPAL_ASSERT_RESET);
+			if (rc != OPAL_SUCCESS) {
+				pr_warn("%s: Failure %lld clearing "
+					"error injection registers\n",
+					__func__, rc);
+				return -EIO;
+			}
+		}
+
 		bus = eeh_pe_bus_get(pe);
 		if (pci_is_root_bus(bus) ||
 		    pci_is_root_bus(bus->parent))
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH 05/21] powerpc/eeh: Clear frozen state on passing device
From: Gavin Shan @ 2014-09-30  2:38 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1412044750-24460-1-git-send-email-gwshan@linux.vnet.ibm.com>

When passing through device, its PE might have been put into frozen
state. One obvious example would be: the passed PE is forced to be
offline because of hitting maximal allowed EEH errors in userland.
In that case, the frozen state won't be cleared and then the PE is
returned back to host, which might not have chance detecting and
recovering from it.

The patch adds more check when passing through device and clear the
PE frozen state if necessary.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 arch/powerpc/kernel/eeh.c | 36 +++++++++++++++++++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index db2841c..e436d5e 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -1150,6 +1150,8 @@ void eeh_remove_device(struct pci_dev *dev)
 int eeh_dev_open(struct pci_dev *pdev)
 {
 	struct eeh_dev *edev;
+	int flag = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
+	int ret = -ENODEV;
 
 	mutex_lock(&eeh_dev_mutex);
 
@@ -1162,6 +1164,38 @@ int eeh_dev_open(struct pci_dev *pdev)
 	if (!edev || !edev->pe)
 		goto out;
 
+	/*
+	 * The PE might have been put into frozen state, but we
+	 * didn't detect that yet. The passed through PCI devices
+	 * in frozen PE won't work properly. Clear the frozen state
+	 * in advance.
+	 */
+	ret = eeh_ops->get_state(edev->pe, NULL);
+	if (ret > 0 && ret != EEH_STATE_NOT_SUPPORT &&
+	    (ret & flag) != flag) {
+		ret = eeh_ops->set_option(edev->pe, EEH_OPT_THAW_MMIO);
+		if (ret) {
+			pr_warn("%s: Failure %d enabling MMIO "
+				"for PHB#%x-PE#%x\n",
+				__func__, ret, edev->phb->global_number,
+				edev->pe->addr);
+			goto out;
+		}
+
+		ret = eeh_ops->set_option(edev->pe, EEH_OPT_THAW_DMA);
+		if (ret) {
+			pr_warn("%s: Failure %d enabling DMA "
+				"for PHB#%x-PE#%x\n",
+				__func__, ret, edev->phb->global_number,
+				edev->pe->addr);
+			goto out;
+		}
+	}
+
+	/* Clear software isolated state */
+	if (edev->pe->state & EEH_PE_ISOLATED)
+		eeh_pe_state_clear(edev->pe, EEH_PE_ISOLATED);
+
 	/* Increase PE's pass through count */
 	atomic_inc(&edev->pe->pass_dev_cnt);
 	mutex_unlock(&eeh_dev_mutex);
@@ -1169,7 +1203,7 @@ int eeh_dev_open(struct pci_dev *pdev)
 	return 0;
 out:
 	mutex_unlock(&eeh_dev_mutex);
-	return -ENODEV;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(eeh_dev_open);
 
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH 03/21] powerpc/eeh: Freeze PE before PE reset
From: Gavin Shan @ 2014-09-30  2:38 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1412044750-24460-1-git-send-email-gwshan@linux.vnet.ibm.com>

The patch adds one more option (EEH_OPT_FREEZE_PE) to set_option()
method to proactively freeze PE, which will be issued before resetting
pass-throughed PE to drop MMIO access during reset because it's
always contributing to recursive EEH error.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/eeh.h               |  1 +
 arch/powerpc/kernel/eeh.c                    |  7 +++++
 arch/powerpc/platforms/powernv/eeh-ioda.c    | 43 +++++++++++++++++++++-------
 arch/powerpc/platforms/pseries/eeh_pseries.c |  4 ++-
 4 files changed, 44 insertions(+), 11 deletions(-)

diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index adcddb1..f98b1b5 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -167,6 +167,7 @@ enum {
 #define EEH_OPT_ENABLE		1	/* EEH enable	*/
 #define EEH_OPT_THAW_MMIO	2	/* MMIO enable	*/
 #define EEH_OPT_THAW_DMA	3	/* DMA enable	*/
+#define EEH_OPT_FREEZE_PE	4	/* Freeze PE	*/
 #define EEH_STATE_UNAVAILABLE	(1 << 0)	/* State unavailable	*/
 #define EEH_STATE_NOT_SUPPORT	(1 << 1)	/* EEH not supported	*/
 #define EEH_STATE_RESET_ACTIVE	(1 << 2)	/* Active reset		*/
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 17cf52ba..898c75f 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -1382,6 +1382,13 @@ int eeh_pe_reset(struct eeh_pe *pe, int option)
 		break;
 	case EEH_RESET_HOT:
 	case EEH_RESET_FUNDAMENTAL:
+		/*
+		 * Proactively freeze the PE to drop all MMIO access
+		 * during reset, which should be banned as it's always
+		 * cause recursive EEH error.
+		 */
+		eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE);
+
 		ret = eeh_ops->reset(pe, option);
 		break;
 	default:
diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c
index c945bed..f3027b9 100644
--- a/arch/powerpc/platforms/powernv/eeh-ioda.c
+++ b/arch/powerpc/platforms/powernv/eeh-ioda.c
@@ -189,6 +189,7 @@ static int ioda_eeh_set_option(struct eeh_pe *pe, int option)
 {
 	struct pci_controller *hose = pe->phb;
 	struct pnv_phb *phb = hose->private_data;
+	bool freeze_pe = false;
 	int enable, ret = 0;
 	s64 rc;
 
@@ -212,6 +213,10 @@ static int ioda_eeh_set_option(struct eeh_pe *pe, int option)
 	case EEH_OPT_THAW_DMA:
 		enable = OPAL_EEH_ACTION_CLEAR_FREEZE_DMA;
 		break;
+	case EEH_OPT_FREEZE_PE:
+		freeze_pe = true;
+		enable = OPAL_EEH_ACTION_SET_FREEZE_ALL;
+		break;
 	default:
 		pr_warn("%s: Invalid option %d\n",
 			__func__, option);
@@ -219,17 +224,35 @@ static int ioda_eeh_set_option(struct eeh_pe *pe, int option)
 	}
 
 	/* If PHB supports compound PE, to handle it */
-	if (phb->unfreeze_pe) {
-		ret = phb->unfreeze_pe(phb, pe->addr, enable);
+	if (freeze_pe) {
+		if (phb->freeze_pe) {
+			phb->freeze_pe(phb, pe->addr);
+		} else {
+			rc = opal_pci_eeh_freeze_set(phb->opal_id,
+						     pe->addr,
+						     enable);
+			if (rc != OPAL_SUCCESS) {
+				pr_warn("%s: Failure %lld freezing "
+					"PHB#%x-PE#%x\n",
+					__func__, rc,
+					phb->hose->global_number, pe->addr);
+				ret = -EIO;
+			}
+		}
 	} else {
-		rc = opal_pci_eeh_freeze_clear(phb->opal_id,
-					       pe->addr,
-					       enable);
-		if (rc != OPAL_SUCCESS) {
-			pr_warn("%s: Failure %lld enable %d for PHB#%x-PE#%x\n",
-				__func__, rc, option, phb->hose->global_number,
-				pe->addr);
-			ret = -EIO;
+		if (phb->unfreeze_pe) {
+			ret = phb->unfreeze_pe(phb, pe->addr, enable);
+		} else {
+			rc = opal_pci_eeh_freeze_clear(phb->opal_id,
+						       pe->addr,
+						       enable);
+			if (rc != OPAL_SUCCESS) {
+				pr_warn("%s: Failure %lld enable %d "
+					"for PHB#%x-PE#%x\n",
+					__func__, rc, option,
+					phb->hose->global_number, pe->addr);
+				ret = -EIO;
+			}
 		}
 	}
 
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index b080538..b645dc6 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -349,7 +349,9 @@ static int pseries_eeh_set_option(struct eeh_pe *pe, int option)
 		if (pe->addr)
 			config_addr = pe->addr;
 		break;
-
+	case EEH_OPT_FREEZE_PE:
+		/* Not support */
+		return 0;
 	default:
 		pr_err("%s: Invalid option %d\n",
 			__func__, option);
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH 15/21] powerpc/pseries: Decrease message level on EEH initialization
From: Gavin Shan @ 2014-09-30  2:39 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1412044750-24460-1-git-send-email-gwshan@linux.vnet.ibm.com>

As Anton suggested, the patch decreases the message level on EEH
initialization to avoid unnecessary messages if required. Also,
we have unified hint if any of needful RTAS calls is missed, and
then we can check /proc/device-tree to figure out the missed RTAS
calls.

Suggested-by: Anton Blanchard <anton@samba.org>
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/pseries/eeh_pseries.c | 35 ++++++++--------------------
 1 file changed, 10 insertions(+), 25 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index 4fc5ff9..a6c7e19 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -88,29 +88,14 @@ static int pseries_eeh_init(void)
 	 * and its variant since the old firmware probably support address
 	 * of domain/bus/slot/function for EEH RTAS operations.
 	 */
-	if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE) {
-		pr_warn("%s: RTAS service <ibm,set-eeh-option> invalid\n",
-			__func__);
-		return -EINVAL;
-	} else if (ibm_set_slot_reset == RTAS_UNKNOWN_SERVICE) {
-		pr_warn("%s: RTAS service <ibm,set-slot-reset> invalid\n",
-			__func__);
-		return -EINVAL;
-	} else if (ibm_read_slot_reset_state2 == RTAS_UNKNOWN_SERVICE &&
-		   ibm_read_slot_reset_state == RTAS_UNKNOWN_SERVICE) {
-		pr_warn("%s: RTAS service <ibm,read-slot-reset-state2> and "
-			"<ibm,read-slot-reset-state> invalid\n",
-			__func__);
-		return -EINVAL;
-	} else if (ibm_slot_error_detail == RTAS_UNKNOWN_SERVICE) {
-		pr_warn("%s: RTAS service <ibm,slot-error-detail> invalid\n",
-			__func__);
-		return -EINVAL;
-	} else if (ibm_configure_pe == RTAS_UNKNOWN_SERVICE &&
-		   ibm_configure_bridge == RTAS_UNKNOWN_SERVICE) {
-		pr_warn("%s: RTAS service <ibm,configure-pe> and "
-			"<ibm,configure-bridge> invalid\n",
-			__func__);
+	if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE		||
+	    ibm_set_slot_reset == RTAS_UNKNOWN_SERVICE		||
+	    (ibm_read_slot_reset_state2 == RTAS_UNKNOWN_SERVICE &&
+	     ibm_read_slot_reset_state == RTAS_UNKNOWN_SERVICE)	||
+	    ibm_slot_error_detail == RTAS_UNKNOWN_SERVICE	||
+	    (ibm_configure_pe == RTAS_UNKNOWN_SERVICE		&&
+	     ibm_configure_bridge == RTAS_UNKNOWN_SERVICE)) {
+		pr_info("EEH functionality not supported\n");
 		return -EINVAL;
 	}
 
@@ -118,11 +103,11 @@ static int pseries_eeh_init(void)
 	spin_lock_init(&slot_errbuf_lock);
 	eeh_error_buf_size = rtas_token("rtas-error-log-max");
 	if (eeh_error_buf_size == RTAS_UNKNOWN_SERVICE) {
-		pr_warn("%s: unknown EEH error log size\n",
+		pr_info("%s: unknown EEH error log size\n",
 			__func__);
 		eeh_error_buf_size = 1024;
 	} else if (eeh_error_buf_size > RTAS_ERROR_LOG_MAX) {
-		pr_warn("%s: EEH error log size %d exceeds the maximal %d\n",
+		pr_info("%s: EEH error log size %d exceeds the maximal %d\n",
 			__func__, eeh_error_buf_size, RTAS_ERROR_LOG_MAX);
 		eeh_error_buf_size = RTAS_ERROR_LOG_MAX;
 	}
-- 
1.8.3.2

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox