378 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for FPGA Management Engine Error Management4 *5 * Copyright 2019 Intel Corporation, Inc.6 *7 * Authors:8 * Kang Luwei <luwei.kang@intel.com>9 * Xiao Guangrong <guangrong.xiao@linux.intel.com>10 * Wu Hao <hao.wu@intel.com>11 * Joseph Grecco <joe.grecco@intel.com>12 * Enno Luebbers <enno.luebbers@intel.com>13 * Tim Whisonant <tim.whisonant@intel.com>14 * Ananda Ravuri <ananda.ravuri@intel.com>15 * Mitchel, Henry <henry.mitchel@intel.com>16 */17 18#include <linux/fpga-dfl.h>19#include <linux/uaccess.h>20 21#include "dfl.h"22#include "dfl-fme.h"23 24#define FME_ERROR_MASK 0x825#define FME_ERROR 0x1026#define MBP_ERROR BIT_ULL(6)27#define PCIE0_ERROR_MASK 0x1828#define PCIE0_ERROR 0x2029#define PCIE1_ERROR_MASK 0x2830#define PCIE1_ERROR 0x3031#define FME_FIRST_ERROR 0x3832#define FME_NEXT_ERROR 0x4033#define RAS_NONFAT_ERROR_MASK 0x4834#define RAS_NONFAT_ERROR 0x5035#define RAS_CATFAT_ERROR_MASK 0x5836#define RAS_CATFAT_ERROR 0x6037#define RAS_ERROR_INJECT 0x6838#define INJECT_ERROR_MASK GENMASK_ULL(2, 0)39 40#define ERROR_MASK GENMASK_ULL(63, 0)41 42static ssize_t pcie0_errors_show(struct device *dev,43 struct device_attribute *attr, char *buf)44{45 struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);46 void __iomem *base;47 u64 value;48 49 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);50 51 mutex_lock(&pdata->lock);52 value = readq(base + PCIE0_ERROR);53 mutex_unlock(&pdata->lock);54 55 return sprintf(buf, "0x%llx\n", (unsigned long long)value);56}57 58static ssize_t pcie0_errors_store(struct device *dev,59 struct device_attribute *attr,60 const char *buf, size_t count)61{62 struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);63 void __iomem *base;64 int ret = 0;65 u64 v, val;66 67 if (kstrtou64(buf, 0, &val))68 return -EINVAL;69 70 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);71 72 mutex_lock(&pdata->lock);73 writeq(GENMASK_ULL(63, 0), base + PCIE0_ERROR_MASK);74 75 v = readq(base + PCIE0_ERROR);76 if (val == v)77 writeq(v, base + PCIE0_ERROR);78 else79 ret = -EINVAL;80 81 writeq(0ULL, base + PCIE0_ERROR_MASK);82 mutex_unlock(&pdata->lock);83 return ret ? ret : count;84}85static DEVICE_ATTR_RW(pcie0_errors);86 87static ssize_t pcie1_errors_show(struct device *dev,88 struct device_attribute *attr, char *buf)89{90 struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);91 void __iomem *base;92 u64 value;93 94 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);95 96 mutex_lock(&pdata->lock);97 value = readq(base + PCIE1_ERROR);98 mutex_unlock(&pdata->lock);99 100 return sprintf(buf, "0x%llx\n", (unsigned long long)value);101}102 103static ssize_t pcie1_errors_store(struct device *dev,104 struct device_attribute *attr,105 const char *buf, size_t count)106{107 struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);108 void __iomem *base;109 int ret = 0;110 u64 v, val;111 112 if (kstrtou64(buf, 0, &val))113 return -EINVAL;114 115 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);116 117 mutex_lock(&pdata->lock);118 writeq(GENMASK_ULL(63, 0), base + PCIE1_ERROR_MASK);119 120 v = readq(base + PCIE1_ERROR);121 if (val == v)122 writeq(v, base + PCIE1_ERROR);123 else124 ret = -EINVAL;125 126 writeq(0ULL, base + PCIE1_ERROR_MASK);127 mutex_unlock(&pdata->lock);128 return ret ? ret : count;129}130static DEVICE_ATTR_RW(pcie1_errors);131 132static ssize_t nonfatal_errors_show(struct device *dev,133 struct device_attribute *attr, char *buf)134{135 void __iomem *base;136 137 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);138 139 return sprintf(buf, "0x%llx\n",140 (unsigned long long)readq(base + RAS_NONFAT_ERROR));141}142static DEVICE_ATTR_RO(nonfatal_errors);143 144static ssize_t catfatal_errors_show(struct device *dev,145 struct device_attribute *attr, char *buf)146{147 void __iomem *base;148 149 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);150 151 return sprintf(buf, "0x%llx\n",152 (unsigned long long)readq(base + RAS_CATFAT_ERROR));153}154static DEVICE_ATTR_RO(catfatal_errors);155 156static ssize_t inject_errors_show(struct device *dev,157 struct device_attribute *attr, char *buf)158{159 struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);160 void __iomem *base;161 u64 v;162 163 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);164 165 mutex_lock(&pdata->lock);166 v = readq(base + RAS_ERROR_INJECT);167 mutex_unlock(&pdata->lock);168 169 return sprintf(buf, "0x%llx\n",170 (unsigned long long)FIELD_GET(INJECT_ERROR_MASK, v));171}172 173static ssize_t inject_errors_store(struct device *dev,174 struct device_attribute *attr,175 const char *buf, size_t count)176{177 struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);178 void __iomem *base;179 u8 inject_error;180 u64 v;181 182 if (kstrtou8(buf, 0, &inject_error))183 return -EINVAL;184 185 if (inject_error & ~INJECT_ERROR_MASK)186 return -EINVAL;187 188 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);189 190 mutex_lock(&pdata->lock);191 v = readq(base + RAS_ERROR_INJECT);192 v &= ~INJECT_ERROR_MASK;193 v |= FIELD_PREP(INJECT_ERROR_MASK, inject_error);194 writeq(v, base + RAS_ERROR_INJECT);195 mutex_unlock(&pdata->lock);196 197 return count;198}199static DEVICE_ATTR_RW(inject_errors);200 201static ssize_t fme_errors_show(struct device *dev,202 struct device_attribute *attr, char *buf)203{204 struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);205 void __iomem *base;206 u64 value;207 208 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);209 210 mutex_lock(&pdata->lock);211 value = readq(base + FME_ERROR);212 mutex_unlock(&pdata->lock);213 214 return sprintf(buf, "0x%llx\n", (unsigned long long)value);215}216 217static ssize_t fme_errors_store(struct device *dev,218 struct device_attribute *attr,219 const char *buf, size_t count)220{221 struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);222 void __iomem *base;223 u64 v, val;224 int ret = 0;225 226 if (kstrtou64(buf, 0, &val))227 return -EINVAL;228 229 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);230 231 mutex_lock(&pdata->lock);232 writeq(GENMASK_ULL(63, 0), base + FME_ERROR_MASK);233 234 v = readq(base + FME_ERROR);235 if (val == v)236 writeq(v, base + FME_ERROR);237 else238 ret = -EINVAL;239 240 /* Workaround: disable MBP_ERROR if feature revision is 0 */241 writeq(dfl_feature_revision(base) ? 0ULL : MBP_ERROR,242 base + FME_ERROR_MASK);243 mutex_unlock(&pdata->lock);244 return ret ? ret : count;245}246static DEVICE_ATTR_RW(fme_errors);247 248static ssize_t first_error_show(struct device *dev,249 struct device_attribute *attr, char *buf)250{251 struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);252 void __iomem *base;253 u64 value;254 255 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);256 257 mutex_lock(&pdata->lock);258 value = readq(base + FME_FIRST_ERROR);259 mutex_unlock(&pdata->lock);260 261 return sprintf(buf, "0x%llx\n", (unsigned long long)value);262}263static DEVICE_ATTR_RO(first_error);264 265static ssize_t next_error_show(struct device *dev,266 struct device_attribute *attr, char *buf)267{268 struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);269 void __iomem *base;270 u64 value;271 272 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);273 274 mutex_lock(&pdata->lock);275 value = readq(base + FME_NEXT_ERROR);276 mutex_unlock(&pdata->lock);277 278 return sprintf(buf, "0x%llx\n", (unsigned long long)value);279}280static DEVICE_ATTR_RO(next_error);281 282static struct attribute *fme_global_err_attrs[] = {283 &dev_attr_pcie0_errors.attr,284 &dev_attr_pcie1_errors.attr,285 &dev_attr_nonfatal_errors.attr,286 &dev_attr_catfatal_errors.attr,287 &dev_attr_inject_errors.attr,288 &dev_attr_fme_errors.attr,289 &dev_attr_first_error.attr,290 &dev_attr_next_error.attr,291 NULL,292};293 294static umode_t fme_global_err_attrs_visible(struct kobject *kobj,295 struct attribute *attr, int n)296{297 struct device *dev = kobj_to_dev(kobj);298 299 /*300 * sysfs entries are visible only if related private feature is301 * enumerated.302 */303 if (!dfl_get_feature_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR))304 return 0;305 306 return attr->mode;307}308 309const struct attribute_group fme_global_err_group = {310 .name = "errors",311 .attrs = fme_global_err_attrs,312 .is_visible = fme_global_err_attrs_visible,313};314 315static void fme_err_mask(struct device *dev, bool mask)316{317 struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);318 void __iomem *base;319 320 base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);321 322 mutex_lock(&pdata->lock);323 324 /* Workaround: keep MBP_ERROR always masked if revision is 0 */325 if (dfl_feature_revision(base))326 writeq(mask ? ERROR_MASK : 0, base + FME_ERROR_MASK);327 else328 writeq(mask ? ERROR_MASK : MBP_ERROR, base + FME_ERROR_MASK);329 330 writeq(mask ? ERROR_MASK : 0, base + PCIE0_ERROR_MASK);331 writeq(mask ? ERROR_MASK : 0, base + PCIE1_ERROR_MASK);332 writeq(mask ? ERROR_MASK : 0, base + RAS_NONFAT_ERROR_MASK);333 writeq(mask ? ERROR_MASK : 0, base + RAS_CATFAT_ERROR_MASK);334 335 mutex_unlock(&pdata->lock);336}337 338static int fme_global_err_init(struct platform_device *pdev,339 struct dfl_feature *feature)340{341 fme_err_mask(&pdev->dev, false);342 343 return 0;344}345 346static void fme_global_err_uinit(struct platform_device *pdev,347 struct dfl_feature *feature)348{349 fme_err_mask(&pdev->dev, true);350}351 352static long353fme_global_error_ioctl(struct platform_device *pdev,354 struct dfl_feature *feature,355 unsigned int cmd, unsigned long arg)356{357 switch (cmd) {358 case DFL_FPGA_FME_ERR_GET_IRQ_NUM:359 return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg);360 case DFL_FPGA_FME_ERR_SET_IRQ:361 return dfl_feature_ioctl_set_irq(pdev, feature, arg);362 default:363 dev_dbg(&pdev->dev, "%x cmd not handled", cmd);364 return -ENODEV;365 }366}367 368const struct dfl_feature_id fme_global_err_id_table[] = {369 {.id = FME_FEATURE_ID_GLOBAL_ERR,},370 {0,}371};372 373const struct dfl_feature_ops fme_global_err_ops = {374 .init = fme_global_err_init,375 .uinit = fme_global_err_uinit,376 .ioctl = fme_global_error_ioctl,377};378