linux/drivers/fpga/dfl-afu-error.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Driver for FPGA Accelerated Function Unit (AFU) Error Reporting
   4 *
   5 * Copyright 2019 Intel Corporation, Inc.
   6 *
   7 * Authors:
   8 *   Wu Hao <hao.wu@linux.intel.com>
   9 *   Xiao Guangrong <guangrong.xiao@linux.intel.com>
  10 *   Joseph Grecco <joe.grecco@intel.com>
  11 *   Enno Luebbers <enno.luebbers@intel.com>
  12 *   Tim Whisonant <tim.whisonant@intel.com>
  13 *   Ananda Ravuri <ananda.ravuri@intel.com>
  14 *   Mitchel Henry <henry.mitchel@intel.com>
  15 */
  16
  17#include <linux/uaccess.h>
  18
  19#include "dfl-afu.h"
  20
  21#define PORT_ERROR_MASK         0x8
  22#define PORT_ERROR              0x10
  23#define PORT_FIRST_ERROR        0x18
  24#define PORT_MALFORMED_REQ0     0x20
  25#define PORT_MALFORMED_REQ1     0x28
  26
  27#define ERROR_MASK              GENMASK_ULL(63, 0)
  28
  29/* mask or unmask port errors by the error mask register. */
  30static void __afu_port_err_mask(struct device *dev, bool mask)
  31{
  32        void __iomem *base;
  33
  34        base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
  35
  36        writeq(mask ? ERROR_MASK : 0, base + PORT_ERROR_MASK);
  37}
  38
  39static void afu_port_err_mask(struct device *dev, bool mask)
  40{
  41        struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
  42
  43        mutex_lock(&pdata->lock);
  44        __afu_port_err_mask(dev, mask);
  45        mutex_unlock(&pdata->lock);
  46}
  47
  48/* clear port errors. */
  49static int afu_port_err_clear(struct device *dev, u64 err)
  50{
  51        struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
  52        struct platform_device *pdev = to_platform_device(dev);
  53        void __iomem *base_err, *base_hdr;
  54        int ret = -EBUSY;
  55        u64 v;
  56
  57        base_err = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
  58        base_hdr = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
  59
  60        mutex_lock(&pdata->lock);
  61
  62        /*
  63         * clear Port Errors
  64         *
  65         * - Check for AP6 State
  66         * - Halt Port by keeping Port in reset
  67         * - Set PORT Error mask to all 1 to mask errors
  68         * - Clear all errors
  69         * - Set Port mask to all 0 to enable errors
  70         * - All errors start capturing new errors
  71         * - Enable Port by pulling the port out of reset
  72         */
  73
  74        /* if device is still in AP6 power state, can not clear any error. */
  75        v = readq(base_hdr + PORT_HDR_STS);
  76        if (FIELD_GET(PORT_STS_PWR_STATE, v) == PORT_STS_PWR_STATE_AP6) {
  77                dev_err(dev, "Could not clear errors, device in AP6 state.\n");
  78                goto done;
  79        }
  80
  81        /* Halt Port by keeping Port in reset */
  82        ret = __afu_port_disable(pdev);
  83        if (ret)
  84                goto done;
  85
  86        /* Mask all errors */
  87        __afu_port_err_mask(dev, true);
  88
  89        /* Clear errors if err input matches with current port errors.*/
  90        v = readq(base_err + PORT_ERROR);
  91
  92        if (v == err) {
  93                writeq(v, base_err + PORT_ERROR);
  94
  95                v = readq(base_err + PORT_FIRST_ERROR);
  96                writeq(v, base_err + PORT_FIRST_ERROR);
  97        } else {
  98                ret = -EINVAL;
  99        }
 100
 101        /* Clear mask */
 102        __afu_port_err_mask(dev, false);
 103
 104        /* Enable the Port by clear the reset */
 105        __afu_port_enable(pdev);
 106
 107done:
 108        mutex_unlock(&pdata->lock);
 109        return ret;
 110}
 111
 112static ssize_t errors_show(struct device *dev, struct device_attribute *attr,
 113                           char *buf)
 114{
 115        struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
 116        void __iomem *base;
 117        u64 error;
 118
 119        base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
 120
 121        mutex_lock(&pdata->lock);
 122        error = readq(base + PORT_ERROR);
 123        mutex_unlock(&pdata->lock);
 124
 125        return sprintf(buf, "0x%llx\n", (unsigned long long)error);
 126}
 127
 128static ssize_t errors_store(struct device *dev, struct device_attribute *attr,
 129                            const char *buff, size_t count)
 130{
 131        u64 value;
 132        int ret;
 133
 134        if (kstrtou64(buff, 0, &value))
 135                return -EINVAL;
 136
 137        ret = afu_port_err_clear(dev, value);
 138
 139        return ret ? ret : count;
 140}
 141static DEVICE_ATTR_RW(errors);
 142
 143static ssize_t first_error_show(struct device *dev,
 144                                struct device_attribute *attr, char *buf)
 145{
 146        struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
 147        void __iomem *base;
 148        u64 error;
 149
 150        base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
 151
 152        mutex_lock(&pdata->lock);
 153        error = readq(base + PORT_FIRST_ERROR);
 154        mutex_unlock(&pdata->lock);
 155
 156        return sprintf(buf, "0x%llx\n", (unsigned long long)error);
 157}
 158static DEVICE_ATTR_RO(first_error);
 159
 160static ssize_t first_malformed_req_show(struct device *dev,
 161                                        struct device_attribute *attr,
 162                                        char *buf)
 163{
 164        struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
 165        void __iomem *base;
 166        u64 req0, req1;
 167
 168        base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
 169
 170        mutex_lock(&pdata->lock);
 171        req0 = readq(base + PORT_MALFORMED_REQ0);
 172        req1 = readq(base + PORT_MALFORMED_REQ1);
 173        mutex_unlock(&pdata->lock);
 174
 175        return sprintf(buf, "0x%016llx%016llx\n",
 176                       (unsigned long long)req1, (unsigned long long)req0);
 177}
 178static DEVICE_ATTR_RO(first_malformed_req);
 179
 180static struct attribute *port_err_attrs[] = {
 181        &dev_attr_errors.attr,
 182        &dev_attr_first_error.attr,
 183        &dev_attr_first_malformed_req.attr,
 184        NULL,
 185};
 186
 187static umode_t port_err_attrs_visible(struct kobject *kobj,
 188                                      struct attribute *attr, int n)
 189{
 190        struct device *dev = kobj_to_dev(kobj);
 191
 192        /*
 193         * sysfs entries are visible only if related private feature is
 194         * enumerated.
 195         */
 196        if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_ERROR))
 197                return 0;
 198
 199        return attr->mode;
 200}
 201
 202const struct attribute_group port_err_group = {
 203        .name       = "errors",
 204        .attrs      = port_err_attrs,
 205        .is_visible = port_err_attrs_visible,
 206};
 207
 208static int port_err_init(struct platform_device *pdev,
 209                         struct dfl_feature *feature)
 210{
 211        afu_port_err_mask(&pdev->dev, false);
 212
 213        return 0;
 214}
 215
 216static void port_err_uinit(struct platform_device *pdev,
 217                           struct dfl_feature *feature)
 218{
 219        afu_port_err_mask(&pdev->dev, true);
 220}
 221
 222const struct dfl_feature_id port_err_id_table[] = {
 223        {.id = PORT_FEATURE_ID_ERROR,},
 224        {0,}
 225};
 226
 227const struct dfl_feature_ops port_err_ops = {
 228        .init = port_err_init,
 229        .uinit = port_err_uinit,
 230};
 231