linux/arch/powerpc/kernel/eeh_sysfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Sysfs entries for PCI Error Recovery for PAPR-compliant platform.
   4 * Copyright IBM Corporation 2007
   5 * Copyright Linas Vepstas <linas@austin.ibm.com> 2007
   6 *
   7 * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
   8 */
   9#include <linux/pci.h>
  10#include <linux/stat.h>
  11#include <asm/ppc-pci.h>
  12#include <asm/pci-bridge.h>
  13
  14/**
  15 * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic
  16 * @_name: name of file in sysfs directory
  17 * @_memb: name of member in struct eeh_dev to access
  18 * @_format: printf format for display
  19 *
  20 * All of the attributes look very similar, so just
  21 * auto-gen a cut-n-paste routine to display them.
  22 */
  23#define EEH_SHOW_ATTR(_name,_memb,_format)               \
  24static ssize_t eeh_show_##_name(struct device *dev,      \
  25                struct device_attribute *attr, char *buf)          \
  26{                                                        \
  27        struct pci_dev *pdev = to_pci_dev(dev);               \
  28        struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);      \
  29                                                              \
  30        if (!edev)                                            \
  31                return 0;                                     \
  32                                                              \
  33        return sprintf(buf, _format "\n", edev->_memb);       \
  34}                                                        \
  35static DEVICE_ATTR(_name, 0444, eeh_show_##_name, NULL);
  36
  37EEH_SHOW_ATTR(eeh_mode,            mode,            "0x%x");
  38EEH_SHOW_ATTR(eeh_pe_config_addr,  pe_config_addr,  "0x%x");
  39
  40static ssize_t eeh_pe_state_show(struct device *dev,
  41                                 struct device_attribute *attr, char *buf)
  42{
  43        struct pci_dev *pdev = to_pci_dev(dev);
  44        struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
  45        int state;
  46
  47        if (!edev || !edev->pe)
  48                return -ENODEV;
  49
  50        state = eeh_ops->get_state(edev->pe, NULL);
  51        return sprintf(buf, "0x%08x 0x%08x\n",
  52                       state, edev->pe->state);
  53}
  54
  55static ssize_t eeh_pe_state_store(struct device *dev,
  56                                  struct device_attribute *attr,
  57                                  const char *buf, size_t count)
  58{
  59        struct pci_dev *pdev = to_pci_dev(dev);
  60        struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
  61
  62        if (!edev || !edev->pe)
  63                return -ENODEV;
  64
  65        /* Nothing to do if it's not frozen */
  66        if (!(edev->pe->state & EEH_PE_ISOLATED))
  67                return count;
  68
  69        if (eeh_unfreeze_pe(edev->pe))
  70                return -EIO;
  71        eeh_pe_state_clear(edev->pe, EEH_PE_ISOLATED, true);
  72
  73        return count;
  74}
  75
  76static DEVICE_ATTR_RW(eeh_pe_state);
  77
  78#if defined(CONFIG_PCI_IOV) && defined(CONFIG_PPC_PSERIES)
  79static ssize_t eeh_notify_resume_show(struct device *dev,
  80                                      struct device_attribute *attr, char *buf)
  81{
  82        struct pci_dev *pdev = to_pci_dev(dev);
  83        struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
  84        struct pci_dn *pdn = pci_get_pdn(pdev);
  85
  86        if (!edev || !edev->pe)
  87                return -ENODEV;
  88
  89        return sprintf(buf, "%d\n", pdn->last_allow_rc);
  90}
  91
  92static ssize_t eeh_notify_resume_store(struct device *dev,
  93                                       struct device_attribute *attr,
  94                                       const char *buf, size_t count)
  95{
  96        struct pci_dev *pdev = to_pci_dev(dev);
  97        struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
  98
  99        if (!edev || !edev->pe || !eeh_ops->notify_resume)
 100                return -ENODEV;
 101
 102        if (eeh_ops->notify_resume(edev))
 103                return -EIO;
 104
 105        return count;
 106}
 107static DEVICE_ATTR_RW(eeh_notify_resume);
 108
 109static int eeh_notify_resume_add(struct pci_dev *pdev)
 110{
 111        struct device_node *np;
 112        int rc = 0;
 113
 114        np = pci_device_to_OF_node(pdev->is_physfn ? pdev : pdev->physfn);
 115
 116        if (of_property_read_bool(np, "ibm,is-open-sriov-pf"))
 117                rc = device_create_file(&pdev->dev, &dev_attr_eeh_notify_resume);
 118
 119        return rc;
 120}
 121
 122static void eeh_notify_resume_remove(struct pci_dev *pdev)
 123{
 124        struct device_node *np;
 125
 126        np = pci_device_to_OF_node(pdev->is_physfn ? pdev : pdev->physfn);
 127
 128        if (of_property_read_bool(np, "ibm,is-open-sriov-pf"))
 129                device_remove_file(&pdev->dev, &dev_attr_eeh_notify_resume);
 130}
 131#else
 132static inline int eeh_notify_resume_add(struct pci_dev *pdev) { return 0; }
 133static inline void eeh_notify_resume_remove(struct pci_dev *pdev) { }
 134#endif /* CONFIG_PCI_IOV && CONFIG PPC_PSERIES*/
 135
 136void eeh_sysfs_add_device(struct pci_dev *pdev)
 137{
 138        struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
 139        int rc=0;
 140
 141        if (!eeh_enabled())
 142                return;
 143
 144        if (edev && (edev->mode & EEH_DEV_SYSFS))
 145                return;
 146
 147        rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode);
 148        rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
 149        rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_state);
 150        rc += eeh_notify_resume_add(pdev);
 151
 152        if (rc)
 153                pr_warn("EEH: Unable to create sysfs entries\n");
 154        else if (edev)
 155                edev->mode |= EEH_DEV_SYSFS;
 156}
 157
 158void eeh_sysfs_remove_device(struct pci_dev *pdev)
 159{
 160        struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);
 161
 162        if (!edev) {
 163                WARN_ON(eeh_enabled());
 164                return;
 165        }
 166
 167        edev->mode &= ~EEH_DEV_SYSFS;
 168
 169        /*
 170         * The parent directory might have been removed. We needn't
 171         * continue for that case.
 172         */
 173        if (!pdev->dev.kobj.sd)
 174                return;
 175
 176        device_remove_file(&pdev->dev, &dev_attr_eeh_mode);
 177        device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
 178        device_remove_file(&pdev->dev, &dev_attr_eeh_pe_state);
 179
 180        eeh_notify_resume_remove(pdev);
 181}
 182