diff options
Diffstat (limited to 'arch/powerpc/include/asm/eeh.h')
| -rw-r--r-- | arch/powerpc/include/asm/eeh.h | 302 | 
1 files changed, 263 insertions, 39 deletions
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 66ea9b8b95c..fab7743c264 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -1,6 +1,6 @@  /* - * eeh.h   * Copyright (C) 2001  Dave Engebretsen & Todd Inglett IBM Corporation. + * Copyright 2001-2012 IBM Corporation.   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@  #include <linux/init.h>  #include <linux/list.h>  #include <linux/string.h> +#include <linux/time.h>  struct pci_dev;  struct pci_bus; @@ -31,45 +32,246 @@ struct device_node;  #ifdef CONFIG_EEH -extern int eeh_subsystem_enabled; +/* EEH subsystem flags */ +#define EEH_ENABLED		0x1	/* EEH enabled		*/ +#define EEH_FORCE_DISABLED	0x2	/* EEH disabled		*/ +#define EEH_PROBE_MODE_DEV	0x4	/* From PCI device	*/ +#define EEH_PROBE_MODE_DEVTREE	0x8	/* From device tree	*/ -/* Values for eeh_mode bits in device_node */ -#define EEH_MODE_SUPPORTED     (1<<0) -#define EEH_MODE_NOCHECK       (1<<1) -#define EEH_MODE_ISOLATED      (1<<2) -#define EEH_MODE_RECOVERING    (1<<3) -#define EEH_MODE_IRQ_DISABLED  (1<<4) +/* + * Delay for PE reset, all in ms + * + * PCI specification has reset hold time of 100 milliseconds. + * We have 250 milliseconds here. The PCI bus settlement time + * is specified as 1.5 seconds and we have 1.8 seconds. + */ +#define EEH_PE_RST_HOLD_TIME		250 +#define EEH_PE_RST_SETTLE_TIME		1800 + +/* + * The struct is used to trace PE related EEH functionality. + * In theory, there will have one instance of the struct to + * be created against particular PE. In nature, PEs corelate + * to each other. the struct has to reflect that hierarchy in + * order to easily pick up those affected PEs when one particular + * PE has EEH errors. + * + * Also, one particular PE might be composed of PCI device, PCI + * bus and its subordinate components. The struct also need ship + * the information. Further more, one particular PE is only meaingful + * in the corresponding PHB. Therefore, the root PEs should be created + * against existing PHBs in on-to-one fashion. + */ +#define EEH_PE_INVALID	(1 << 0)	/* Invalid   */ +#define EEH_PE_PHB	(1 << 1)	/* PHB PE    */ +#define EEH_PE_DEVICE 	(1 << 2)	/* Device PE */ +#define EEH_PE_BUS	(1 << 3)	/* Bus PE    */ + +#define EEH_PE_ISOLATED		(1 << 0)	/* Isolated PE		*/ +#define EEH_PE_RECOVERING	(1 << 1)	/* Recovering PE	*/ +#define EEH_PE_RESET		(1 << 2)	/* PE reset in progress	*/ + +#define EEH_PE_KEEP		(1 << 8)	/* Keep PE on hotplug	*/ + +struct eeh_pe { +	int type;			/* PE type: PHB/Bus/Device	*/ +	int state;			/* PE EEH dependent mode	*/ +	int config_addr;		/* Traditional PCI address	*/ +	int addr;			/* PE configuration address	*/ +	struct pci_controller *phb;	/* Associated PHB		*/ +	struct pci_bus *bus;		/* Top PCI bus for bus PE	*/ +	int check_count;		/* Times of ignored error	*/ +	int freeze_count;		/* Times of froze up		*/ +	struct timeval tstamp;		/* Time on first-time freeze	*/ +	int false_positives;		/* Times of reported #ff's	*/ +	struct eeh_pe *parent;		/* Parent PE			*/ +	struct list_head child_list;	/* Link PE to the child list	*/ +	struct list_head edevs;		/* Link list of EEH devices	*/ +	struct list_head child;		/* Child PEs			*/ +}; + +#define eeh_pe_for_each_dev(pe, edev, tmp) \ +		list_for_each_entry_safe(edev, tmp, &pe->edevs, list) + +/* + * The struct is used to trace EEH state for the associated + * PCI device node or PCI device. In future, it might + * represent PE as well so that the EEH device to form + * another tree except the currently existing tree of PCI + * buses and PCI devices + */ +#define EEH_DEV_BRIDGE		(1 << 0)	/* PCI bridge		*/ +#define EEH_DEV_ROOT_PORT	(1 << 1)	/* PCIe root port	*/ +#define EEH_DEV_DS_PORT		(1 << 2)	/* Downstream port	*/ +#define EEH_DEV_IRQ_DISABLED	(1 << 3)	/* Interrupt disabled	*/ +#define EEH_DEV_DISCONNECTED	(1 << 4)	/* Removing from PE	*/ + +#define EEH_DEV_NO_HANDLER	(1 << 8)	/* No error handler	*/ +#define EEH_DEV_SYSFS		(1 << 9)	/* Sysfs created	*/ +#define EEH_DEV_REMOVED		(1 << 10)	/* Removed permanently	*/ + +struct eeh_dev { +	int mode;			/* EEH mode			*/ +	int class_code;			/* Class code of the device	*/ +	int config_addr;		/* Config address		*/ +	int pe_config_addr;		/* PE config address		*/ +	u32 config_space[16];		/* Saved PCI config space	*/ +	int pcix_cap;			/* Saved PCIx capability	*/ +	int pcie_cap;			/* Saved PCIe capability	*/ +	int aer_cap;			/* Saved AER capability		*/ +	struct eeh_pe *pe;		/* Associated PE		*/ +	struct list_head list;		/* Form link list in the PE	*/ +	struct pci_controller *phb;	/* Associated PHB		*/ +	struct device_node *dn;		/* Associated device node	*/ +	struct pci_dev *pdev;		/* Associated PCI device	*/ +	struct pci_bus *bus;		/* PCI bus for partial hotplug	*/ +}; + +static inline struct device_node *eeh_dev_to_of_node(struct eeh_dev *edev) +{ +	return edev ? edev->dn : NULL; +} + +static inline struct pci_dev *eeh_dev_to_pci_dev(struct eeh_dev *edev) +{ +	return edev ? edev->pdev : NULL; +} + +/* Return values from eeh_ops::next_error */ +enum { +	EEH_NEXT_ERR_NONE = 0, +	EEH_NEXT_ERR_INF, +	EEH_NEXT_ERR_FROZEN_PE, +	EEH_NEXT_ERR_FENCED_PHB, +	EEH_NEXT_ERR_DEAD_PHB, +	EEH_NEXT_ERR_DEAD_IOC +}; + +/* + * The struct is used to trace the registered EEH operation + * callback functions. Actually, those operation callback + * functions are heavily platform dependent. That means the + * platform should register its own EEH operation callback + * functions before any EEH further operations. + */ +#define EEH_OPT_DISABLE		0	/* EEH disable	*/ +#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_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		*/ +#define EEH_STATE_MMIO_ACTIVE	(1 << 3)	/* Active MMIO		*/ +#define EEH_STATE_DMA_ACTIVE	(1 << 4)	/* Active DMA		*/ +#define EEH_STATE_MMIO_ENABLED	(1 << 5)	/* MMIO enabled		*/ +#define EEH_STATE_DMA_ENABLED	(1 << 6)	/* DMA enabled		*/ +#define EEH_RESET_DEACTIVATE	0	/* Deactivate the PE reset	*/ +#define EEH_RESET_HOT		1	/* Hot reset			*/ +#define EEH_RESET_FUNDAMENTAL	3	/* Fundamental reset		*/ +#define EEH_LOG_TEMP		1	/* EEH temporary error log	*/ +#define EEH_LOG_PERM		2	/* EEH permanent error log	*/ + +struct eeh_ops { +	char *name; +	int (*init)(void); +	int (*post_init)(void); +	void* (*of_probe)(struct device_node *dn, void *flag); +	int (*dev_probe)(struct pci_dev *dev, void *flag); +	int (*set_option)(struct eeh_pe *pe, int option); +	int (*get_pe_addr)(struct eeh_pe *pe); +	int (*get_state)(struct eeh_pe *pe, int *state); +	int (*reset)(struct eeh_pe *pe, int option); +	int (*wait_state)(struct eeh_pe *pe, int max_wait); +	int (*get_log)(struct eeh_pe *pe, int severity, char *drv_log, unsigned long len); +	int (*configure_bridge)(struct eeh_pe *pe); +	int (*read_config)(struct device_node *dn, int where, int size, u32 *val); +	int (*write_config)(struct device_node *dn, int where, int size, u32 val); +	int (*next_error)(struct eeh_pe **pe); +	int (*restore_config)(struct device_node *dn); +}; + +extern int eeh_subsystem_flags; +extern struct eeh_ops *eeh_ops; +extern raw_spinlock_t confirm_error_lock; + +static inline bool eeh_enabled(void) +{ +	if ((eeh_subsystem_flags & EEH_FORCE_DISABLED) || +	    !(eeh_subsystem_flags & EEH_ENABLED)) +		return false; + +	return true; +} + +static inline void eeh_set_enable(bool mode) +{ +	if (mode) +		eeh_subsystem_flags |= EEH_ENABLED; +	else +		eeh_subsystem_flags &= ~EEH_ENABLED; +} + +static inline void eeh_probe_mode_set(int flag) +{ +	eeh_subsystem_flags |= flag; +} + +static inline int eeh_probe_mode_devtree(void) +{ +	return (eeh_subsystem_flags & EEH_PROBE_MODE_DEVTREE); +} + +static inline int eeh_probe_mode_dev(void) +{ +	return (eeh_subsystem_flags & EEH_PROBE_MODE_DEV); +} -/* Max number of EEH freezes allowed before we consider the device - * to be permanently disabled. */ +static inline void eeh_serialize_lock(unsigned long *flags) +{ +	raw_spin_lock_irqsave(&confirm_error_lock, *flags); +} + +static inline void eeh_serialize_unlock(unsigned long flags) +{ +	raw_spin_unlock_irqrestore(&confirm_error_lock, flags); +} + +/* + * Max number of EEH freezes allowed before we consider the device + * to be permanently disabled. + */  #define EEH_MAX_ALLOWED_FREEZES 5 -void __init eeh_init(void); +typedef void *(*eeh_traverse_func)(void *data, void *flag); +int eeh_phb_pe_create(struct pci_controller *phb); +struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb); +struct eeh_pe *eeh_pe_get(struct eeh_dev *edev); +int eeh_add_to_parent_pe(struct eeh_dev *edev); +int eeh_rmv_from_parent_pe(struct eeh_dev *edev); +void eeh_pe_update_time_stamp(struct eeh_pe *pe); +void *eeh_pe_traverse(struct eeh_pe *root, +		eeh_traverse_func fn, void *flag); +void *eeh_pe_dev_traverse(struct eeh_pe *root, +		eeh_traverse_func fn, void *flag); +void eeh_pe_restore_bars(struct eeh_pe *pe); +const char *eeh_pe_loc_get(struct eeh_pe *pe); +struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe); + +void *eeh_dev_init(struct device_node *dn, void *data); +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_dn_check_failure(struct device_node *dn, struct pci_dev *dev); -void __init pci_addr_cache_build(void); - -/** - * eeh_add_device_early - * eeh_add_device_late - * - * Perform eeh initialization for devices added after boot. - * Call eeh_add_device_early before doing any i/o to the - * device (including config space i/o).  Call eeh_add_device_late - * to finish the eeh setup for this device. - */ +int eeh_dev_check_failure(struct eeh_dev *edev); +void eeh_addr_cache_build(void); +void eeh_add_device_early(struct device_node *);  void eeh_add_device_tree_early(struct device_node *); +void eeh_add_device_late(struct pci_dev *);  void eeh_add_device_tree_late(struct pci_bus *); - -/** - * eeh_remove_device_recursive - undo EEH for device & children. - * @dev: pci device to be removed - * - * As above, this removes the device; it also removes child - * pci devices as well. - */ -void eeh_remove_bus_device(struct pci_dev *); +void eeh_add_sysfs_files(struct pci_bus *); +void eeh_remove_device(struct pci_dev *);  /**   * EEH_POSSIBLE_ERROR() -- test for possible MMIO failure. @@ -77,7 +279,7 @@ void eeh_remove_bus_device(struct pci_dev *);   * If this macro yields TRUE, the caller relays to eeh_check_failure()   * which does further tests out of line.   */ -#define EEH_POSSIBLE_ERROR(val, type)	((val) == (type)~0 && eeh_subsystem_enabled) +#define EEH_POSSIBLE_ERROR(val, type)	((val) == (type)~0 && eeh_enabled())  /*   * Reads from a device which has been isolated by EEH will return @@ -87,25 +289,47 @@ void eeh_remove_bus_device(struct pci_dev *);  #define EEH_IO_ERROR_VALUE(size)	(~0U >> ((4 - (size)) * 8))  #else /* !CONFIG_EEH */ -static inline void eeh_init(void) { } -static inline unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val) +static inline bool eeh_enabled(void)  { -	return val; +        return false;  } -static inline int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) +static inline void eeh_set_enable(bool mode) { } + +static inline int eeh_init(void)  {  	return 0;  } -static inline void pci_addr_cache_build(void) { } +static inline void *eeh_dev_init(struct device_node *dn, void *data) +{ +	return NULL; +} + +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) +{ +	return val; +} + +#define eeh_dev_check_failure(x) (0) + +static inline void eeh_addr_cache_build(void) { } + +static inline void eeh_add_device_early(struct device_node *dn) { }  static inline void eeh_add_device_tree_early(struct device_node *dn) { } +static inline void eeh_add_device_late(struct pci_dev *dev) { } +  static inline void eeh_add_device_tree_late(struct pci_bus *bus) { } -static inline void eeh_remove_bus_device(struct pci_dev *dev) { } +static inline void eeh_add_sysfs_files(struct pci_bus *bus) { } + +static inline void eeh_remove_device(struct pci_dev *dev) { } +  #define EEH_POSSIBLE_ERROR(val, type) (0)  #define EEH_IO_ERROR_VALUE(size) (-1UL)  #endif /* CONFIG_EEH */  | 
