Moritz writes: FPGA DFL Changes for 5.4 This pull-request contains the FPGA DFL changes for 5.4 - The first three patches are cleanup patches making use of dev_groups and making the init callback optional. - One patch adds userclock sysfs entries that are DFL specific - One patch exposes AFU port disable/enable functions - One patch adds error reporting - One patch adds AFU SignalTap support - One patch adds FME global error reporting - The final patch is a documentation patch that decribes the virtualization interfaces This patchset requires the 'dev_groups_all_drivers' tag from drivers core for the dev_groups refactoring as well as the DFL changes already in char-misc-next. Signed-off-by: Moritz Fischer <mdf@kernel.org> * tag 'fpga-dfl-for-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/mdf/linux-fpga: Documentation: fpga: dfl: add descriptions for virtualization and new interfaces. fpga: dfl: fme: add global error reporting support fpga: dfl: afu: add STP (SignalTap) support fpga: dfl: afu: add error reporting support. fpga: dfl: afu: expose __afu_port_enable/disable function. fpga: dfl: afu: add userclock sysfs interfaces. fpga: dfl: afu: convert platform_driver to use dev_groups fpga: dfl: fme: convert platform_driver to use dev_groups fpga: dfl: make init callback optional driver core: add dev_groups to all drivers
Unified · Split
| @@ -44,3 +44,65 @@ Description: Read-only. It returns socket_id to indicate which socket · 43 unmodified lines | ||
| 44 | 44 | this FPGA belongs to, only valid for integrated solution. |
| 45 | 45 | User only needs this information, in case standard numa node |
| 46 | 46 | can't provide correct information. |
| 47 | + | |
| 48 | +What: /sys/bus/platform/devices/dfl-fme.0/errors/pcie0_errors | |
| 49 | +Date: August 2019 | |
| 50 | +KernelVersion: 5.4 | |
| 51 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 52 | +Description: Read-Write. Read this file for errors detected on pcie0 link. | |
| 53 | + Write this file to clear errors logged in pcie0_errors. Write | |
| 54 | + fails with -EINVAL if input parsing fails or input error code | |
| 55 | + doesn't match. | |
| 56 | + | |
| 57 | +What: /sys/bus/platform/devices/dfl-fme.0/errors/pcie1_errors | |
| 58 | +Date: August 2019 | |
| 59 | +KernelVersion: 5.4 | |
| 60 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 61 | +Description: Read-Write. Read this file for errors detected on pcie1 link. | |
| 62 | + Write this file to clear errors logged in pcie1_errors. Write | |
| 63 | + fails with -EINVAL if input parsing fails or input error code | |
| 64 | + doesn't match. | |
| 65 | + | |
| 66 | +What: /sys/bus/platform/devices/dfl-fme.0/errors/nonfatal_errors | |
| 67 | +Date: August 2019 | |
| 68 | +KernelVersion: 5.4 | |
| 69 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 70 | +Description: Read-only. It returns non-fatal errors detected. | |
| 71 | + | |
| 72 | +What: /sys/bus/platform/devices/dfl-fme.0/errors/catfatal_errors | |
| 73 | +Date: August 2019 | |
| 74 | +KernelVersion: 5.4 | |
| 75 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 76 | +Description: Read-only. It returns catastrophic and fatal errors detected. | |
| 77 | + | |
| 78 | +What: /sys/bus/platform/devices/dfl-fme.0/errors/inject_errors | |
| 79 | +Date: August 2019 | |
| 80 | +KernelVersion: 5.4 | |
| 81 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 82 | +Description: Read-Write. Read this file to check errors injected. Write this | |
| 83 | + file to inject errors for testing purpose. Write fails with | |
| 84 | + -EINVAL if input parsing fails or input inject error code isn't | |
| 85 | + supported. | |
| 86 | + | |
| 87 | +What: /sys/bus/platform/devices/dfl-fme.0/errors/fme_errors | |
| 88 | +Date: August 2019 | |
| 89 | +KernelVersion: 5.4 | |
| 90 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 91 | +Description: Read-Write. Read this file to get errors detected on FME. | |
| 92 | + Write this file to clear errors logged in fme_errors. Write | |
| 93 | + fials with -EINVAL if input parsing fails or input error code | |
| 94 | + doesn't match. | |
| 95 | + | |
| 96 | +What: /sys/bus/platform/devices/dfl-fme.0/errors/first_error | |
| 97 | +Date: August 2019 | |
| 98 | +KernelVersion: 5.4 | |
| 99 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 100 | +Description: Read-only. Read this file to get the first error detected by | |
| 101 | + hardware. | |
| 102 | + | |
| 103 | +What: /sys/bus/platform/devices/dfl-fme.0/errors/next_error | |
| 104 | +Date: August 2019 | |
| 105 | +KernelVersion: 5.4 | |
| 106 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 107 | +Description: Read-only. Read this file to get the second error detected by | |
| 108 | + hardware. | |
| @@ -46,3 +46,56 @@ Contact: Wu Hao <hao.wu@intel.com> · 45 unmodified lines | ||
| 46 | 46 | Description: Read-write. Read or set AFU latency tolerance reporting value. |
| 47 | 47 | Set ltr to 1 if the AFU can tolerate latency >= 40us or set it |
| 48 | 48 | to 0 if it is latency sensitive. |
| 49 | + | |
| 50 | +What: /sys/bus/platform/devices/dfl-port.0/userclk_freqcmd | |
| 51 | +Date: August 2019 | |
| 52 | +KernelVersion: 5.4 | |
| 53 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 54 | +Description: Write-only. User writes command to this interface to set | |
| 55 | + userclock to AFU. | |
| 56 | + | |
| 57 | +What: /sys/bus/platform/devices/dfl-port.0/userclk_freqsts | |
| 58 | +Date: August 2019 | |
| 59 | +KernelVersion: 5.4 | |
| 60 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 61 | +Description: Read-only. Read this file to get the status of issued command | |
| 62 | + to userclck_freqcmd. | |
| 63 | + | |
| 64 | +What: /sys/bus/platform/devices/dfl-port.0/userclk_freqcntrcmd | |
| 65 | +Date: August 2019 | |
| 66 | +KernelVersion: 5.4 | |
| 67 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 68 | +Description: Write-only. User writes command to this interface to set | |
| 69 | + userclock counter. | |
| 70 | + | |
| 71 | +What: /sys/bus/platform/devices/dfl-port.0/userclk_freqcntrsts | |
| 72 | +Date: August 2019 | |
| 73 | +KernelVersion: 5.4 | |
| 74 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 75 | +Description: Read-only. Read this file to get the status of issued command | |
| 76 | + to userclck_freqcntrcmd. | |
| 77 | + | |
| 78 | +What: /sys/bus/platform/devices/dfl-port.0/errors/errors | |
| 79 | +Date: August 2019 | |
| 80 | +KernelVersion: 5.4 | |
| 81 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 82 | +Description: Read-Write. Read this file to get errors detected on port and | |
| 83 | + Accelerated Function Unit (AFU). Write error code to this file | |
| 84 | + to clear errors. Write fails with -EINVAL if input parsing | |
| 85 | + fails or input error code doesn't match. Write fails with | |
| 86 | + -EBUSY or -ETIMEDOUT if error can't be cleared as hardware | |
| 87 | + in low power state (-EBUSY) or not respoding (-ETIMEDOUT). | |
| 88 | + | |
| 89 | +What: /sys/bus/platform/devices/dfl-port.0/errors/first_error | |
| 90 | +Date: August 2019 | |
| 91 | +KernelVersion: 5.4 | |
| 92 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 93 | +Description: Read-only. Read this file to get the first error detected by | |
| 94 | + hardware. | |
| 95 | + | |
| 96 | +What: /sys/bus/platform/devices/dfl-port.0/errors/first_malformed_req | |
| 97 | +Date: August 2019 | |
| 98 | +KernelVersion: 5.4 | |
| 99 | +Contact: Wu Hao <hao.wu@intel.com> | |
| 100 | +Description: Read-only. Read this file to get the first malformed request | |
| 101 | + captured by hardware. | |
| @@ -87,6 +87,8 @@ The following functions are exposed through ioctls: · 86 unmodified lines | ||
| 87 | 87 | - Get driver API version (DFL_FPGA_GET_API_VERSION) |
| 88 | 88 | - Check for extensions (DFL_FPGA_CHECK_EXTENSION) |
| 89 | 89 | - Program bitstream (DFL_FPGA_FME_PORT_PR) |
| 90 | +- Assign port to PF (DFL_FPGA_FME_PORT_ASSIGN) | |
| 91 | +- Release port from PF (DFL_FPGA_FME_PORT_RELEASE) | |
| 90 | 92 | |
| 91 | 93 | More functions are exposed through sysfs |
| 92 | 94 | (/sys/class/fpga_region/regionX/dfl-fme.n/): |
| @@ -102,6 +104,10 @@ More functions are exposed through sysfs · 9 unmodified lines | ||
| 102 | 104 | one FPGA device may have more than one port, this sysfs interface indicates |
| 103 | 105 | how many ports the FPGA device has. |
| 104 | 106 | |
| 107 | + Global error reporting management (errors/) | |
| 108 | + error reporting sysfs interfaces allow user to read errors detected by the | |
| 109 | + hardware, and clear the logged errors. | |
| 110 | + | |
| 105 | 111 | |
| 106 | 112 | FIU - PORT |
| 107 | 113 | ========== |
| @@ -143,6 +149,10 @@ More functions are exposed through sysfs: · 35 unmodified lines | ||
| 143 | 149 | Read Accelerator GUID (afu_id) |
| 144 | 150 | afu_id indicates which PR bitstream is programmed to this AFU. |
| 145 | 151 | |
| 152 | + Error reporting (errors/) | |
| 153 | + error reporting sysfs interfaces allow user to read port/afu errors | |
| 154 | + detected by the hardware, and clear the logged errors. | |
| 155 | + | |
| 146 | 156 | |
| 147 | 157 | DFL Framework Overview |
| 148 | 158 | ====================== |
| @@ -218,6 +228,101 @@ the compat_id exposed by the target FPGA region. This check is usually done by · 69 unmodified lines | ||
| 218 | 228 | userspace before calling the reconfiguration IOCTL. |
| 219 | 229 | |
| 220 | 230 | |
| 231 | +FPGA virtualization - PCIe SRIOV | |
| 232 | +================================ | |
| 233 | +This section describes the virtualization support on DFL based FPGA device to | |
| 234 | +enable accessing an accelerator from applications running in a virtual machine | |
| 235 | +(VM). This section only describes the PCIe based FPGA device with SRIOV support. | |
| 236 | + | |
| 237 | +Features supported by the particular FPGA device are exposed through Device | |
| 238 | +Feature Lists, as illustrated below: | |
| 239 | + | |
| 240 | +:: | |
| 241 | + | |
| 242 | + +-------------------------------+ +-------------+ | |
| 243 | + | PF | | VF | | |
| 244 | + +-------------------------------+ +-------------+ | |
| 245 | + ^ ^ ^ ^ | |
| 246 | + | | | | | |
| 247 | + +-----|------------|---------|--------------|-------+ | |
| 248 | + | | | | | | | |
| 249 | + | +-----+ +-------+ +-------+ +-------+ | | |
| 250 | + | | FME | | Port0 | | Port1 | | Port2 | | | |
| 251 | + | +-----+ +-------+ +-------+ +-------+ | | |
| 252 | + | ^ ^ ^ | | |
| 253 | + | | | | | | |
| 254 | + | +-------+ +------+ +-------+ | | |
| 255 | + | | AFU | | AFU | | AFU | | | |
| 256 | + | +-------+ +------+ +-------+ | | |
| 257 | + | | | |
| 258 | + | DFL based FPGA PCIe Device | | |
| 259 | + +---------------------------------------------------+ | |
| 260 | + | |
| 261 | +FME is always accessed through the physical function (PF). | |
| 262 | + | |
| 263 | +Ports (and related AFUs) are accessed via PF by default, but could be exposed | |
| 264 | +through virtual function (VF) devices via PCIe SRIOV. Each VF only contains | |
| 265 | +1 Port and 1 AFU for isolation. Users could assign individual VFs (accelerators) | |
| 266 | +created via PCIe SRIOV interface, to virtual machines. | |
| 267 | + | |
| 268 | +The driver organization in virtualization case is illustrated below: | |
| 269 | +:: | |
| 270 | + | |
| 271 | + +-------++------++------+ | | |
| 272 | + | FME || FME || FME | | | |
| 273 | + | FPGA || FPGA || FPGA | | | |
| 274 | + |Manager||Bridge||Region| | | |
| 275 | + +-------++------++------+ | | |
| 276 | + +-----------------------+ +--------+ | +--------+ | |
| 277 | + | FME | | AFU | | | AFU | | |
| 278 | + | Module | | Module | | | Module | | |
| 279 | + +-----------------------+ +--------+ | +--------+ | |
| 280 | + +-----------------------+ | +-----------------------+ | |
| 281 | + | FPGA Container Device | | | FPGA Container Device | | |
| 282 | + | (FPGA Base Region) | | | (FPGA Base Region) | | |
| 283 | + +-----------------------+ | +-----------------------+ | |
| 284 | + +------------------+ | +------------------+ | |
| 285 | + | FPGA PCIE Module | | Virtual | FPGA PCIE Module | | |
| 286 | + +------------------+ Host | Machine +------------------+ | |
| 287 | + -------------------------------------- | ------------------------------ | |
| 288 | + +---------------+ | +---------------+ | |
| 289 | + | PCI PF Device | | | PCI VF Device | | |
| 290 | + +---------------+ | +---------------+ | |
| 291 | + | |
| 292 | +FPGA PCIe device driver is always loaded first once a FPGA PCIe PF or VF device | |
| 293 | +is detected. It: | |
| 294 | + | |
| 295 | +* Finishes enumeration on both FPGA PCIe PF and VF device using common | |
| 296 | + interfaces from DFL framework. | |
| 297 | +* Supports SRIOV. | |
| 298 | + | |
| 299 | +The FME device driver plays a management role in this driver architecture, it | |
| 300 | +provides ioctls to release Port from PF and assign Port to PF. After release | |
| 301 | +a port from PF, then it's safe to expose this port through a VF via PCIe SRIOV | |
| 302 | +sysfs interface. | |
| 303 | + | |
| 304 | +To enable accessing an accelerator from applications running in a VM, the | |
| 305 | +respective AFU's port needs to be assigned to a VF using the following steps: | |
| 306 | + | |
| 307 | +#. The PF owns all AFU ports by default. Any port that needs to be | |
| 308 | + reassigned to a VF must first be released through the | |
| 309 | + DFL_FPGA_FME_PORT_RELEASE ioctl on the FME device. | |
| 310 | + | |
| 311 | +#. Once N ports are released from PF, then user can use command below | |
| 312 | + to enable SRIOV and VFs. Each VF owns only one Port with AFU. | |
| 313 | + | |
| 314 | + :: | |
| 315 | + | |
| 316 | + echo N > $PCI_DEVICE_PATH/sriov_numvfs | |
| 317 | + | |
| 318 | +#. Pass through the VFs to VMs | |
| 319 | + | |
| 320 | +#. The AFU under VF is accessible from applications in VM (using the | |
| 321 | + same driver inside the VF). | |
| 322 | + | |
| 323 | +Note that an FME can't be assigned to a VF, thus PR and other management | |
| 324 | +functions are only available via the PF. | |
| 325 | + | |
| 221 | 326 | Device enumeration |
| 222 | 327 | ================== |
| 223 | 328 | This section introduces how applications enumerate the fpga device from |
| @@ -554,9 +554,16 @@ re_probe: · 553 unmodified lines | ||
| 554 | 554 | goto probe_failed; |
| 555 | 555 | } |
| 556 | 556 | |
| 557 | + if (device_add_groups(dev, drv->dev_groups)) { | |
| 558 | + dev_err(dev, "device_add_groups() failed\n"); | |
| 559 | + goto dev_groups_failed; | |
| 560 | + } | |
| 561 | + | |
| 557 | 562 | if (test_remove) { |
| 558 | 563 | test_remove = false; |
| 559 | 564 | |
| 565 | + device_remove_groups(dev, drv->dev_groups); | |
| 566 | + | |
| 560 | 567 | if (dev->bus->remove) |
| 561 | 568 | dev->bus->remove(dev); |
| 562 | 569 | else if (drv->remove) |
| @@ -584,6 +591,11 @@ re_probe: · 21 unmodified lines | ||
| 584 | 591 | drv->bus->name, __func__, dev_name(dev), drv->name); |
| 585 | 592 | goto done; |
| 586 | 593 | |
| 594 | +dev_groups_failed: | |
| 595 | + if (dev->bus->remove) | |
| 596 | + dev->bus->remove(dev); | |
| 597 | + else if (drv->remove) | |
| 598 | + drv->remove(dev); | |
| 587 | 599 | probe_failed: |
| 588 | 600 | if (dev->bus) |
| 589 | 601 | blocking_notifier_call_chain(&dev->bus->p->bus_notifier, |
| @@ -1114,6 +1126,8 @@ static void __device_release_driver(struct device *dev, struct device *parent) · 524 unmodified lines | ||
| 1114 | 1126 | |
| 1115 | 1127 | pm_runtime_put_sync(dev); |
| 1116 | 1128 | |
| 1129 | + device_remove_groups(dev, drv->dev_groups); | |
| 1130 | + | |
| 1117 | 1131 | if (dev->bus && dev->bus->remove) |
| 1118 | 1132 | dev->bus->remove(dev); |
| 1119 | 1133 | else if (drv->remove) |
| @@ -39,8 +39,9 @@ obj-$(CONFIG_FPGA_DFL_FME_BRIDGE) += dfl-fme-br.o · 38 unmodified lines | ||
| 39 | 39 | obj-$(CONFIG_FPGA_DFL_FME_REGION) += dfl-fme-region.o |
| 40 | 40 | obj-$(CONFIG_FPGA_DFL_AFU) += dfl-afu.o |
| 41 | 41 | |
| 42 | −dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o | |
| 42 | +dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o dfl-fme-error.o | |
| 43 | 43 | dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o dfl-afu-dma-region.o |
| 44 | +dfl-afu-objs += dfl-afu-error.o | |
| 44 | 45 | |
| 45 | 46 | # Drivers for FPGAs which implement DFL |
| 46 | 47 | obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o |
| @@ -0,0 +1,230 @@ | ||
| 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. */ | |
| 30 | +static 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 | + | |
| 39 | +static 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. */ | |
| 49 | +static 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 | + | |
| 107 | +done: | |
| 108 | + mutex_unlock(&pdata->lock); | |
| 109 | + return ret; | |
| 110 | +} | |
| 111 | + | |
| 112 | +static 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 | + | |
| 128 | +static 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 | +} | |
| 141 | +static DEVICE_ATTR_RW(errors); | |
| 142 | + | |
| 143 | +static 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 | +} | |
| 158 | +static DEVICE_ATTR_RO(first_error); | |
| 159 | + | |
| 160 | +static 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 | +} | |
| 178 | +static DEVICE_ATTR_RO(first_malformed_req); | |
| 179 | + | |
| 180 | +static 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 | + | |
| 187 | +static 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 | + | |
| 202 | +const struct attribute_group port_err_group = { | |
| 203 | + .name = "errors", | |
| 204 | + .attrs = port_err_attrs, | |
| 205 | + .is_visible = port_err_attrs_visible, | |
| 206 | +}; | |
| 207 | + | |
| 208 | +static 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 | + | |
| 216 | +static void port_err_uinit(struct platform_device *pdev, | |
| 217 | + struct dfl_feature *feature) | |
| 218 | +{ | |
| 219 | + afu_port_err_mask(&pdev->dev, true); | |
| 220 | +} | |
| 221 | + | |
| 222 | +const struct dfl_feature_id port_err_id_table[] = { | |
| 223 | + {.id = PORT_FEATURE_ID_ERROR,}, | |
| 224 | + {0,} | |
| 225 | +}; | |
| 226 | + | |
| 227 | +const struct dfl_feature_ops port_err_ops = { | |
| 228 | + .init = port_err_init, | |
| 229 | + .uinit = port_err_uinit, | |
| 230 | +}; | |
| @@ -22,14 +22,17 @@ · 21 unmodified lines | ||
| 22 | 22 | #include "dfl-afu.h" |
| 23 | 23 | |
| 24 | 24 | /** |
| 25 | − * port_enable - enable a port | |
| 25 | + * __afu_port_enable - enable a port by clear reset | |
| 26 | 26 | * @pdev: port platform device. |
| 27 | 27 | * |
| 28 | 28 | * Enable Port by clear the port soft reset bit, which is set by default. |
| 29 | 29 | * The AFU is unable to respond to any MMIO access while in reset. |
| 30 | − * port_enable function should only be used after port_disable function. | |
| 30 | + * __afu_port_enable function should only be used after __afu_port_disable | |
| 31 | + * function. | |
| 32 | + * | |
| 33 | + * The caller needs to hold lock for protection. | |
| 31 | 34 | */ |
| 32 | −static void port_enable(struct platform_device *pdev) | |
| 35 | +void __afu_port_enable(struct platform_device *pdev) | |
| 33 | 36 | { |
| 34 | 37 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); |
| 35 | 38 | void __iomem *base; |
| @@ -52,13 +55,14 @@ static void port_enable(struct platform_device *pdev) · 16 unmodified lines | ||
| 52 | 55 | #define RST_POLL_TIMEOUT 1000 /* us */ |
| 53 | 56 | |
| 54 | 57 | /** |
| 55 | − * port_disable - disable a port | |
| 58 | + * __afu_port_disable - disable a port by hold reset | |
| 56 | 59 | * @pdev: port platform device. |
| 57 | 60 | * |
| 58 | − * Disable Port by setting the port soft reset bit, it puts the port into | |
| 59 | − * reset. | |
| 61 | + * Disable Port by setting the port soft reset bit, it puts the port into reset. | |
| 62 | + * | |
| 63 | + * The caller needs to hold lock for protection. | |
| 60 | 64 | */ |
| 61 | −static int port_disable(struct platform_device *pdev) | |
| 65 | +int __afu_port_disable(struct platform_device *pdev) | |
| 62 | 66 | { |
| 63 | 67 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); |
| 64 | 68 | void __iomem *base; |
| @@ -104,9 +108,9 @@ static int __port_reset(struct platform_device *pdev) · 39 unmodified lines | ||
| 104 | 108 | { |
| 105 | 109 | int ret; |
| 106 | 110 | |
| 107 | − ret = port_disable(pdev); | |
| 111 | + ret = __afu_port_disable(pdev); | |
| 108 | 112 | if (!ret) |
| 109 | − port_enable(pdev); | |
| 113 | + __afu_port_enable(pdev); | |
| 110 | 114 | |
| 111 | 115 | return ret; |
| 112 | 116 | } |
| @@ -274,32 +278,134 @@ power_state_show(struct device *dev, struct device_attribute *attr, char *buf) · 161 unmodified lines | ||
| 274 | 278 | } |
| 275 | 279 | static DEVICE_ATTR_RO(power_state); |
| 276 | 280 | |
| 281 | +static ssize_t | |
| 282 | +userclk_freqcmd_store(struct device *dev, struct device_attribute *attr, | |
| 283 | + const char *buf, size_t count) | |
| 284 | +{ | |
| 285 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 286 | + u64 userclk_freq_cmd; | |
| 287 | + void __iomem *base; | |
| 288 | + | |
| 289 | + if (kstrtou64(buf, 0, &userclk_freq_cmd)) | |
| 290 | + return -EINVAL; | |
| 291 | + | |
| 292 | + base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
| 293 | + | |
| 294 | + mutex_lock(&pdata->lock); | |
| 295 | + writeq(userclk_freq_cmd, base + PORT_HDR_USRCLK_CMD0); | |
| 296 | + mutex_unlock(&pdata->lock); | |
| 297 | + | |
| 298 | + return count; | |
| 299 | +} | |
| 300 | +static DEVICE_ATTR_WO(userclk_freqcmd); | |
| 301 | + | |
| 302 | +static ssize_t | |
| 303 | +userclk_freqcntrcmd_store(struct device *dev, struct device_attribute *attr, | |
| 304 | + const char *buf, size_t count) | |
| 305 | +{ | |
| 306 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 307 | + u64 userclk_freqcntr_cmd; | |
| 308 | + void __iomem *base; | |
| 309 | + | |
| 310 | + if (kstrtou64(buf, 0, &userclk_freqcntr_cmd)) | |
| 311 | + return -EINVAL; | |
| 312 | + | |
| 313 | + base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
| 314 | + | |
| 315 | + mutex_lock(&pdata->lock); | |
| 316 | + writeq(userclk_freqcntr_cmd, base + PORT_HDR_USRCLK_CMD1); | |
| 317 | + mutex_unlock(&pdata->lock); | |
| 318 | + | |
| 319 | + return count; | |
| 320 | +} | |
| 321 | +static DEVICE_ATTR_WO(userclk_freqcntrcmd); | |
| 322 | + | |
| 323 | +static ssize_t | |
| 324 | +userclk_freqsts_show(struct device *dev, struct device_attribute *attr, | |
| 325 | + char *buf) | |
| 326 | +{ | |
| 327 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 328 | + u64 userclk_freqsts; | |
| 329 | + void __iomem *base; | |
| 330 | + | |
| 331 | + base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
| 332 | + | |
| 333 | + mutex_lock(&pdata->lock); | |
| 334 | + userclk_freqsts = readq(base + PORT_HDR_USRCLK_STS0); | |
| 335 | + mutex_unlock(&pdata->lock); | |
| 336 | + | |
| 337 | + return sprintf(buf, "0x%llx\n", (unsigned long long)userclk_freqsts); | |
| 338 | +} | |
| 339 | +static DEVICE_ATTR_RO(userclk_freqsts); | |
| 340 | + | |
| 341 | +static ssize_t | |
| 342 | +userclk_freqcntrsts_show(struct device *dev, struct device_attribute *attr, | |
| 343 | + char *buf) | |
| 344 | +{ | |
| 345 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 346 | + u64 userclk_freqcntrsts; | |
| 347 | + void __iomem *base; | |
| 348 | + | |
| 349 | + base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
| 350 | + | |
| 351 | + mutex_lock(&pdata->lock); | |
| 352 | + userclk_freqcntrsts = readq(base + PORT_HDR_USRCLK_STS1); | |
| 353 | + mutex_unlock(&pdata->lock); | |
| 354 | + | |
| 355 | + return sprintf(buf, "0x%llx\n", | |
| 356 | + (unsigned long long)userclk_freqcntrsts); | |
| 357 | +} | |
| 358 | +static DEVICE_ATTR_RO(userclk_freqcntrsts); | |
| 359 | + | |
| 277 | 360 | static struct attribute *port_hdr_attrs[] = { |
| 278 | 361 | &dev_attr_id.attr, |
| 279 | 362 | &dev_attr_ltr.attr, |
| 280 | 363 | &dev_attr_ap1_event.attr, |
| 281 | 364 | &dev_attr_ap2_event.attr, |
| 282 | 365 | &dev_attr_power_state.attr, |
| 366 | + &dev_attr_userclk_freqcmd.attr, | |
| 367 | + &dev_attr_userclk_freqcntrcmd.attr, | |
| 368 | + &dev_attr_userclk_freqsts.attr, | |
| 369 | + &dev_attr_userclk_freqcntrsts.attr, | |
| 283 | 370 | NULL, |
| 284 | 371 | }; |
| 285 | −ATTRIBUTE_GROUPS(port_hdr); | |
| 286 | 372 | |
| 287 | −static int port_hdr_init(struct platform_device *pdev, | |
| 288 | − struct dfl_feature *feature) | |
| 373 | +static umode_t port_hdr_attrs_visible(struct kobject *kobj, | |
| 374 | + struct attribute *attr, int n) | |
| 289 | 375 | { |
| 290 | − dev_dbg(&pdev->dev, "PORT HDR Init.\n"); | |
| 376 | + struct device *dev = kobj_to_dev(kobj); | |
| 377 | + umode_t mode = attr->mode; | |
| 378 | + void __iomem *base; | |
| 291 | 379 | |
| 292 | − port_reset(pdev); | |
| 380 | + base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
| 381 | + | |
| 382 | + if (dfl_feature_revision(base) > 0) { | |
| 383 | + /* | |
| 384 | + * userclk sysfs interfaces are only visible in case port | |
| 385 | + * revision is 0, as hardware with revision >0 doesn't | |
| 386 | + * support this. | |
| 387 | + */ | |
| 388 | + if (attr == &dev_attr_userclk_freqcmd.attr || | |
| 389 | + attr == &dev_attr_userclk_freqcntrcmd.attr || | |
| 390 | + attr == &dev_attr_userclk_freqsts.attr || | |
| 391 | + attr == &dev_attr_userclk_freqcntrsts.attr) | |
| 392 | + mode = 0; | |
| 393 | + } | |
| 293 | 394 | |
| 294 | − return device_add_groups(&pdev->dev, port_hdr_groups); | |
| 395 | + return mode; | |
| 295 | 396 | } |
| 296 | 397 | |
| 297 | −static void port_hdr_uinit(struct platform_device *pdev, | |
| 298 | − struct dfl_feature *feature) | |
| 398 | +static const struct attribute_group port_hdr_group = { | |
| 399 | + .attrs = port_hdr_attrs, | |
| 400 | + .is_visible = port_hdr_attrs_visible, | |
| 401 | +}; | |
| 402 | + | |
| 403 | +static int port_hdr_init(struct platform_device *pdev, | |
| 404 | + struct dfl_feature *feature) | |
| 299 | 405 | { |
| 300 | − dev_dbg(&pdev->dev, "PORT HDR UInit.\n"); | |
| 406 | + port_reset(pdev); | |
| 301 | 407 | |
| 302 | − device_remove_groups(&pdev->dev, port_hdr_groups); | |
| 408 | + return 0; | |
| 303 | 409 | } |
| 304 | 410 | |
| 305 | 411 | static long |
| @@ -330,7 +436,6 @@ static const struct dfl_feature_id port_hdr_id_table[] = { · 24 unmodified lines | ||
| 330 | 436 | |
| 331 | 437 | static const struct dfl_feature_ops port_hdr_ops = { |
| 332 | 438 | .init = port_hdr_init, |
| 333 | − .uinit = port_hdr_uinit, | |
| 334 | 439 | .ioctl = port_hdr_ioctl, |
| 335 | 440 | }; |
| 336 | 441 | |
| @@ -361,32 +466,37 @@ static struct attribute *port_afu_attrs[] = { · 24 unmodified lines | ||
| 361 | 466 | &dev_attr_afu_id.attr, |
| 362 | 467 | NULL |
| 363 | 468 | }; |
| 364 | −ATTRIBUTE_GROUPS(port_afu); | |
| 365 | 469 | |
| 366 | −static int port_afu_init(struct platform_device *pdev, | |
| 367 | − struct dfl_feature *feature) | |
| 470 | +static umode_t port_afu_attrs_visible(struct kobject *kobj, | |
| 471 | + struct attribute *attr, int n) | |
| 368 | 472 | { |
| 369 | − struct resource *res = &pdev->resource[feature->resource_index]; | |
| 370 | − int ret; | |
| 473 | + struct device *dev = kobj_to_dev(kobj); | |
| 371 | 474 | |
| 372 | − dev_dbg(&pdev->dev, "PORT AFU Init.\n"); | |
| 373 | − | |
| 374 | − ret = afu_mmio_region_add(dev_get_platdata(&pdev->dev), | |
| 375 | − DFL_PORT_REGION_INDEX_AFU, resource_size(res), | |
| 376 | − res->start, DFL_PORT_REGION_READ | | |
| 377 | − DFL_PORT_REGION_WRITE | DFL_PORT_REGION_MMAP); | |
| 378 | − if (ret) | |
| 379 | − return ret; | |
| 475 | + /* | |
| 476 | + * sysfs entries are visible only if related private feature is | |
| 477 | + * enumerated. | |
| 478 | + */ | |
| 479 | + if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_AFU)) | |
| 480 | + return 0; | |
| 380 | 481 | |
| 381 | − return device_add_groups(&pdev->dev, port_afu_groups); | |
| 482 | + return attr->mode; | |
| 382 | 483 | } |
| 383 | 484 | |
| 384 | −static void port_afu_uinit(struct platform_device *pdev, | |
| 385 | − struct dfl_feature *feature) | |
| 485 | +static const struct attribute_group port_afu_group = { | |
| 486 | + .attrs = port_afu_attrs, | |
| 487 | + .is_visible = port_afu_attrs_visible, | |
| 488 | +}; | |
| 489 | + | |
| 490 | +static int port_afu_init(struct platform_device *pdev, | |
| 491 | + struct dfl_feature *feature) | |
| 386 | 492 | { |
| 387 | − dev_dbg(&pdev->dev, "PORT AFU UInit.\n"); | |
| 493 | + struct resource *res = &pdev->resource[feature->resource_index]; | |
| 388 | 494 | |
| 389 | − device_remove_groups(&pdev->dev, port_afu_groups); | |
| 495 | + return afu_mmio_region_add(dev_get_platdata(&pdev->dev), | |
| 496 | + DFL_PORT_REGION_INDEX_AFU, | |
| 497 | + resource_size(res), res->start, | |
| 498 | + DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ | | |
| 499 | + DFL_PORT_REGION_WRITE); | |
| 390 | 500 | } |
| 391 | 501 | |
| 392 | 502 | static const struct dfl_feature_id port_afu_id_table[] = { |
| @@ -396,7 +506,27 @@ static const struct dfl_feature_id port_afu_id_table[] = { · 3 unmodified lines | ||
| 396 | 506 | |
| 397 | 507 | static const struct dfl_feature_ops port_afu_ops = { |
| 398 | 508 | .init = port_afu_init, |
| 399 | − .uinit = port_afu_uinit, | |
| 509 | +}; | |
| 510 | + | |
| 511 | +static int port_stp_init(struct platform_device *pdev, | |
| 512 | + struct dfl_feature *feature) | |
| 513 | +{ | |
| 514 | + struct resource *res = &pdev->resource[feature->resource_index]; | |
| 515 | + | |
| 516 | + return afu_mmio_region_add(dev_get_platdata(&pdev->dev), | |
| 517 | + DFL_PORT_REGION_INDEX_STP, | |
| 518 | + resource_size(res), res->start, | |
| 519 | + DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ | | |
| 520 | + DFL_PORT_REGION_WRITE); | |
| 521 | +} | |
| 522 | + | |
| 523 | +static const struct dfl_feature_id port_stp_id_table[] = { | |
| 524 | + {.id = PORT_FEATURE_ID_STP,}, | |
| 525 | + {0,} | |
| 526 | +}; | |
| 527 | + | |
| 528 | +static const struct dfl_feature_ops port_stp_ops = { | |
| 529 | + .init = port_stp_init, | |
| 400 | 530 | }; |
| 401 | 531 | |
| 402 | 532 | static struct dfl_feature_driver port_feature_drvs[] = { |
| @@ -408,6 +538,14 @@ static struct dfl_feature_driver port_feature_drvs[] = { · 5 unmodified lines | ||
| 408 | 538 | .id_table = port_afu_id_table, |
| 409 | 539 | .ops = &port_afu_ops, |
| 410 | 540 | }, |
| 541 | + { | |
| 542 | + .id_table = port_err_id_table, | |
| 543 | + .ops = &port_err_ops, | |
| 544 | + }, | |
| 545 | + { | |
| 546 | + .id_table = port_stp_id_table, | |
| 547 | + .ops = &port_stp_ops, | |
| 548 | + }, | |
| 411 | 549 | { |
| 412 | 550 | .ops = NULL, |
| 413 | 551 | } |
| @@ -694,9 +832,9 @@ static int port_enable_set(struct platform_device *pdev, bool enable) · 280 unmodified lines | ||
| 694 | 832 | |
| 695 | 833 | mutex_lock(&pdata->lock); |
| 696 | 834 | if (enable) |
| 697 | − port_enable(pdev); | |
| 835 | + __afu_port_enable(pdev); | |
| 698 | 836 | else |
| 699 | − ret = port_disable(pdev); | |
| 837 | + ret = __afu_port_disable(pdev); | |
| 700 | 838 | mutex_unlock(&pdata->lock); |
| 701 | 839 | |
| 702 | 840 | return ret; |
| @@ -748,9 +886,17 @@ static int afu_remove(struct platform_device *pdev) · 45 unmodified lines | ||
| 748 | 886 | return 0; |
| 749 | 887 | } |
| 750 | 888 | |
| 889 | +static const struct attribute_group *afu_dev_groups[] = { | |
| 890 | + &port_hdr_group, | |
| 891 | + &port_afu_group, | |
| 892 | + &port_err_group, | |
| 893 | + NULL | |
| 894 | +}; | |
| 895 | + | |
| 751 | 896 | static struct platform_driver afu_driver = { |
| 752 | 897 | .driver = { |
| 753 | − .name = DFL_FPGA_FEATURE_DEV_PORT, | |
| 898 | + .name = DFL_FPGA_FEATURE_DEV_PORT, | |
| 899 | + .dev_groups = afu_dev_groups, | |
| 754 | 900 | }, |
| 755 | 901 | .probe = afu_probe, |
| 756 | 902 | .remove = afu_remove, |
| @@ -79,6 +79,10 @@ struct dfl_afu { · 78 unmodified lines | ||
| 79 | 79 | struct dfl_feature_platform_data *pdata; |
| 80 | 80 | }; |
| 81 | 81 | |
| 82 | +/* hold pdata->lock when call __afu_port_enable/disable */ | |
| 83 | +void __afu_port_enable(struct platform_device *pdev); | |
| 84 | +int __afu_port_disable(struct platform_device *pdev); | |
| 85 | + | |
| 82 | 86 | void afu_mmio_region_init(struct dfl_feature_platform_data *pdata); |
| 83 | 87 | int afu_mmio_region_add(struct dfl_feature_platform_data *pdata, |
| 84 | 88 | u32 region_index, u64 region_size, u64 phys, u32 flags); |
| @@ -97,4 +101,9 @@ int afu_dma_unmap_region(struct dfl_feature_platform_data *pdata, u64 iova); · 12 unmodified lines | ||
| 97 | 101 | struct dfl_afu_dma_region * |
| 98 | 102 | afu_dma_region_find(struct dfl_feature_platform_data *pdata, |
| 99 | 103 | u64 iova, u64 size); |
| 104 | + | |
| 105 | +extern const struct dfl_feature_ops port_err_ops; | |
| 106 | +extern const struct dfl_feature_id port_err_id_table[]; | |
| 107 | +extern const struct attribute_group port_err_group; | |
| 108 | + | |
| 100 | 109 | #endif /* __DFL_AFU_H */ |
| @@ -0,0 +1,359 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Driver for FPGA Management Engine Error Management | |
| 4 | + * | |
| 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/uaccess.h> | |
| 19 | + | |
| 20 | +#include "dfl.h" | |
| 21 | +#include "dfl-fme.h" | |
| 22 | + | |
| 23 | +#define FME_ERROR_MASK 0x8 | |
| 24 | +#define FME_ERROR 0x10 | |
| 25 | +#define MBP_ERROR BIT_ULL(6) | |
| 26 | +#define PCIE0_ERROR_MASK 0x18 | |
| 27 | +#define PCIE0_ERROR 0x20 | |
| 28 | +#define PCIE1_ERROR_MASK 0x28 | |
| 29 | +#define PCIE1_ERROR 0x30 | |
| 30 | +#define FME_FIRST_ERROR 0x38 | |
| 31 | +#define FME_NEXT_ERROR 0x40 | |
| 32 | +#define RAS_NONFAT_ERROR_MASK 0x48 | |
| 33 | +#define RAS_NONFAT_ERROR 0x50 | |
| 34 | +#define RAS_CATFAT_ERROR_MASK 0x58 | |
| 35 | +#define RAS_CATFAT_ERROR 0x60 | |
| 36 | +#define RAS_ERROR_INJECT 0x68 | |
| 37 | +#define INJECT_ERROR_MASK GENMASK_ULL(2, 0) | |
| 38 | + | |
| 39 | +#define ERROR_MASK GENMASK_ULL(63, 0) | |
| 40 | + | |
| 41 | +static ssize_t pcie0_errors_show(struct device *dev, | |
| 42 | + struct device_attribute *attr, char *buf) | |
| 43 | +{ | |
| 44 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 45 | + void __iomem *base; | |
| 46 | + u64 value; | |
| 47 | + | |
| 48 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 49 | + | |
| 50 | + mutex_lock(&pdata->lock); | |
| 51 | + value = readq(base + PCIE0_ERROR); | |
| 52 | + mutex_unlock(&pdata->lock); | |
| 53 | + | |
| 54 | + return sprintf(buf, "0x%llx\n", (unsigned long long)value); | |
| 55 | +} | |
| 56 | + | |
| 57 | +static ssize_t pcie0_errors_store(struct device *dev, | |
| 58 | + struct device_attribute *attr, | |
| 59 | + const char *buf, size_t count) | |
| 60 | +{ | |
| 61 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 62 | + void __iomem *base; | |
| 63 | + int ret = 0; | |
| 64 | + u64 v, val; | |
| 65 | + | |
| 66 | + if (kstrtou64(buf, 0, &val)) | |
| 67 | + return -EINVAL; | |
| 68 | + | |
| 69 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 70 | + | |
| 71 | + mutex_lock(&pdata->lock); | |
| 72 | + writeq(GENMASK_ULL(63, 0), base + PCIE0_ERROR_MASK); | |
| 73 | + | |
| 74 | + v = readq(base + PCIE0_ERROR); | |
| 75 | + if (val == v) | |
| 76 | + writeq(v, base + PCIE0_ERROR); | |
| 77 | + else | |
| 78 | + ret = -EINVAL; | |
| 79 | + | |
| 80 | + writeq(0ULL, base + PCIE0_ERROR_MASK); | |
| 81 | + mutex_unlock(&pdata->lock); | |
| 82 | + return ret ? ret : count; | |
| 83 | +} | |
| 84 | +static DEVICE_ATTR_RW(pcie0_errors); | |
| 85 | + | |
| 86 | +static ssize_t pcie1_errors_show(struct device *dev, | |
| 87 | + struct device_attribute *attr, char *buf) | |
| 88 | +{ | |
| 89 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 90 | + void __iomem *base; | |
| 91 | + u64 value; | |
| 92 | + | |
| 93 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 94 | + | |
| 95 | + mutex_lock(&pdata->lock); | |
| 96 | + value = readq(base + PCIE1_ERROR); | |
| 97 | + mutex_unlock(&pdata->lock); | |
| 98 | + | |
| 99 | + return sprintf(buf, "0x%llx\n", (unsigned long long)value); | |
| 100 | +} | |
| 101 | + | |
| 102 | +static ssize_t pcie1_errors_store(struct device *dev, | |
| 103 | + struct device_attribute *attr, | |
| 104 | + const char *buf, size_t count) | |
| 105 | +{ | |
| 106 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 107 | + void __iomem *base; | |
| 108 | + int ret = 0; | |
| 109 | + u64 v, val; | |
| 110 | + | |
| 111 | + if (kstrtou64(buf, 0, &val)) | |
| 112 | + return -EINVAL; | |
| 113 | + | |
| 114 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 115 | + | |
| 116 | + mutex_lock(&pdata->lock); | |
| 117 | + writeq(GENMASK_ULL(63, 0), base + PCIE1_ERROR_MASK); | |
| 118 | + | |
| 119 | + v = readq(base + PCIE1_ERROR); | |
| 120 | + if (val == v) | |
| 121 | + writeq(v, base + PCIE1_ERROR); | |
| 122 | + else | |
| 123 | + ret = -EINVAL; | |
| 124 | + | |
| 125 | + writeq(0ULL, base + PCIE1_ERROR_MASK); | |
| 126 | + mutex_unlock(&pdata->lock); | |
| 127 | + return ret ? ret : count; | |
| 128 | +} | |
| 129 | +static DEVICE_ATTR_RW(pcie1_errors); | |
| 130 | + | |
| 131 | +static ssize_t nonfatal_errors_show(struct device *dev, | |
| 132 | + struct device_attribute *attr, char *buf) | |
| 133 | +{ | |
| 134 | + void __iomem *base; | |
| 135 | + | |
| 136 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 137 | + | |
| 138 | + return sprintf(buf, "0x%llx\n", | |
| 139 | + (unsigned long long)readq(base + RAS_NONFAT_ERROR)); | |
| 140 | +} | |
| 141 | +static DEVICE_ATTR_RO(nonfatal_errors); | |
| 142 | + | |
| 143 | +static ssize_t catfatal_errors_show(struct device *dev, | |
| 144 | + struct device_attribute *attr, char *buf) | |
| 145 | +{ | |
| 146 | + void __iomem *base; | |
| 147 | + | |
| 148 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 149 | + | |
| 150 | + return sprintf(buf, "0x%llx\n", | |
| 151 | + (unsigned long long)readq(base + RAS_CATFAT_ERROR)); | |
| 152 | +} | |
| 153 | +static DEVICE_ATTR_RO(catfatal_errors); | |
| 154 | + | |
| 155 | +static ssize_t inject_errors_show(struct device *dev, | |
| 156 | + struct device_attribute *attr, char *buf) | |
| 157 | +{ | |
| 158 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 159 | + void __iomem *base; | |
| 160 | + u64 v; | |
| 161 | + | |
| 162 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 163 | + | |
| 164 | + mutex_lock(&pdata->lock); | |
| 165 | + v = readq(base + RAS_ERROR_INJECT); | |
| 166 | + mutex_unlock(&pdata->lock); | |
| 167 | + | |
| 168 | + return sprintf(buf, "0x%llx\n", | |
| 169 | + (unsigned long long)FIELD_GET(INJECT_ERROR_MASK, v)); | |
| 170 | +} | |
| 171 | + | |
| 172 | +static ssize_t inject_errors_store(struct device *dev, | |
| 173 | + struct device_attribute *attr, | |
| 174 | + const char *buf, size_t count) | |
| 175 | +{ | |
| 176 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 177 | + void __iomem *base; | |
| 178 | + u8 inject_error; | |
| 179 | + u64 v; | |
| 180 | + | |
| 181 | + if (kstrtou8(buf, 0, &inject_error)) | |
| 182 | + return -EINVAL; | |
| 183 | + | |
| 184 | + if (inject_error & ~INJECT_ERROR_MASK) | |
| 185 | + return -EINVAL; | |
| 186 | + | |
| 187 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 188 | + | |
| 189 | + mutex_lock(&pdata->lock); | |
| 190 | + v = readq(base + RAS_ERROR_INJECT); | |
| 191 | + v &= ~INJECT_ERROR_MASK; | |
| 192 | + v |= FIELD_PREP(INJECT_ERROR_MASK, inject_error); | |
| 193 | + writeq(v, base + RAS_ERROR_INJECT); | |
| 194 | + mutex_unlock(&pdata->lock); | |
| 195 | + | |
| 196 | + return count; | |
| 197 | +} | |
| 198 | +static DEVICE_ATTR_RW(inject_errors); | |
| 199 | + | |
| 200 | +static ssize_t fme_errors_show(struct device *dev, | |
| 201 | + struct device_attribute *attr, char *buf) | |
| 202 | +{ | |
| 203 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 204 | + void __iomem *base; | |
| 205 | + u64 value; | |
| 206 | + | |
| 207 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 208 | + | |
| 209 | + mutex_lock(&pdata->lock); | |
| 210 | + value = readq(base + FME_ERROR); | |
| 211 | + mutex_unlock(&pdata->lock); | |
| 212 | + | |
| 213 | + return sprintf(buf, "0x%llx\n", (unsigned long long)value); | |
| 214 | +} | |
| 215 | + | |
| 216 | +static ssize_t fme_errors_store(struct device *dev, | |
| 217 | + struct device_attribute *attr, | |
| 218 | + const char *buf, size_t count) | |
| 219 | +{ | |
| 220 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 221 | + void __iomem *base; | |
| 222 | + u64 v, val; | |
| 223 | + int ret = 0; | |
| 224 | + | |
| 225 | + if (kstrtou64(buf, 0, &val)) | |
| 226 | + return -EINVAL; | |
| 227 | + | |
| 228 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 229 | + | |
| 230 | + mutex_lock(&pdata->lock); | |
| 231 | + writeq(GENMASK_ULL(63, 0), base + FME_ERROR_MASK); | |
| 232 | + | |
| 233 | + v = readq(base + FME_ERROR); | |
| 234 | + if (val == v) | |
| 235 | + writeq(v, base + FME_ERROR); | |
| 236 | + else | |
| 237 | + ret = -EINVAL; | |
| 238 | + | |
| 239 | + /* Workaround: disable MBP_ERROR if feature revision is 0 */ | |
| 240 | + writeq(dfl_feature_revision(base) ? 0ULL : MBP_ERROR, | |
| 241 | + base + FME_ERROR_MASK); | |
| 242 | + mutex_unlock(&pdata->lock); | |
| 243 | + return ret ? ret : count; | |
| 244 | +} | |
| 245 | +static DEVICE_ATTR_RW(fme_errors); | |
| 246 | + | |
| 247 | +static ssize_t first_error_show(struct device *dev, | |
| 248 | + struct device_attribute *attr, char *buf) | |
| 249 | +{ | |
| 250 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 251 | + void __iomem *base; | |
| 252 | + u64 value; | |
| 253 | + | |
| 254 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 255 | + | |
| 256 | + mutex_lock(&pdata->lock); | |
| 257 | + value = readq(base + FME_FIRST_ERROR); | |
| 258 | + mutex_unlock(&pdata->lock); | |
| 259 | + | |
| 260 | + return sprintf(buf, "0x%llx\n", (unsigned long long)value); | |
| 261 | +} | |
| 262 | +static DEVICE_ATTR_RO(first_error); | |
| 263 | + | |
| 264 | +static ssize_t next_error_show(struct device *dev, | |
| 265 | + struct device_attribute *attr, char *buf) | |
| 266 | +{ | |
| 267 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 268 | + void __iomem *base; | |
| 269 | + u64 value; | |
| 270 | + | |
| 271 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 272 | + | |
| 273 | + mutex_lock(&pdata->lock); | |
| 274 | + value = readq(base + FME_NEXT_ERROR); | |
| 275 | + mutex_unlock(&pdata->lock); | |
| 276 | + | |
| 277 | + return sprintf(buf, "0x%llx\n", (unsigned long long)value); | |
| 278 | +} | |
| 279 | +static DEVICE_ATTR_RO(next_error); | |
| 280 | + | |
| 281 | +static struct attribute *fme_global_err_attrs[] = { | |
| 282 | + &dev_attr_pcie0_errors.attr, | |
| 283 | + &dev_attr_pcie1_errors.attr, | |
| 284 | + &dev_attr_nonfatal_errors.attr, | |
| 285 | + &dev_attr_catfatal_errors.attr, | |
| 286 | + &dev_attr_inject_errors.attr, | |
| 287 | + &dev_attr_fme_errors.attr, | |
| 288 | + &dev_attr_first_error.attr, | |
| 289 | + &dev_attr_next_error.attr, | |
| 290 | + NULL, | |
| 291 | +}; | |
| 292 | + | |
| 293 | +static umode_t fme_global_err_attrs_visible(struct kobject *kobj, | |
| 294 | + struct attribute *attr, int n) | |
| 295 | +{ | |
| 296 | + struct device *dev = kobj_to_dev(kobj); | |
| 297 | + | |
| 298 | + /* | |
| 299 | + * sysfs entries are visible only if related private feature is | |
| 300 | + * enumerated. | |
| 301 | + */ | |
| 302 | + if (!dfl_get_feature_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR)) | |
| 303 | + return 0; | |
| 304 | + | |
| 305 | + return attr->mode; | |
| 306 | +} | |
| 307 | + | |
| 308 | +const struct attribute_group fme_global_err_group = { | |
| 309 | + .name = "errors", | |
| 310 | + .attrs = fme_global_err_attrs, | |
| 311 | + .is_visible = fme_global_err_attrs_visible, | |
| 312 | +}; | |
| 313 | + | |
| 314 | +static void fme_err_mask(struct device *dev, bool mask) | |
| 315 | +{ | |
| 316 | + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
| 317 | + void __iomem *base; | |
| 318 | + | |
| 319 | + base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); | |
| 320 | + | |
| 321 | + mutex_lock(&pdata->lock); | |
| 322 | + | |
| 323 | + /* Workaround: keep MBP_ERROR always masked if revision is 0 */ | |
| 324 | + if (dfl_feature_revision(base)) | |
| 325 | + writeq(mask ? ERROR_MASK : 0, base + FME_ERROR_MASK); | |
| 326 | + else | |
| 327 | + writeq(mask ? ERROR_MASK : MBP_ERROR, base + FME_ERROR_MASK); | |
| 328 | + | |
| 329 | + writeq(mask ? ERROR_MASK : 0, base + PCIE0_ERROR_MASK); | |
| 330 | + writeq(mask ? ERROR_MASK : 0, base + PCIE1_ERROR_MASK); | |
| 331 | + writeq(mask ? ERROR_MASK : 0, base + RAS_NONFAT_ERROR_MASK); | |
| 332 | + writeq(mask ? ERROR_MASK : 0, base + RAS_CATFAT_ERROR_MASK); | |
| 333 | + | |
| 334 | + mutex_unlock(&pdata->lock); | |
| 335 | +} | |
| 336 | + | |
| 337 | +static int fme_global_err_init(struct platform_device *pdev, | |
| 338 | + struct dfl_feature *feature) | |
| 339 | +{ | |
| 340 | + fme_err_mask(&pdev->dev, false); | |
| 341 | + | |
| 342 | + return 0; | |
| 343 | +} | |
| 344 | + | |
| 345 | +static void fme_global_err_uinit(struct platform_device *pdev, | |
| 346 | + struct dfl_feature *feature) | |
| 347 | +{ | |
| 348 | + fme_err_mask(&pdev->dev, true); | |
| 349 | +} | |
| 350 | + | |
| 351 | +const struct dfl_feature_id fme_global_err_id_table[] = { | |
| 352 | + {.id = FME_FEATURE_ID_GLOBAL_ERR,}, | |
| 353 | + {0,} | |
| 354 | +}; | |
| 355 | + | |
| 356 | +const struct dfl_feature_ops fme_global_err_ops = { | |
| 357 | + .init = fme_global_err_init, | |
| 358 | + .uinit = fme_global_err_uinit, | |
| 359 | +}; | |
| @@ -127,31 +127,10 @@ static struct attribute *fme_hdr_attrs[] = { · 126 unmodified lines | ||
| 127 | 127 | &dev_attr_socket_id.attr, |
| 128 | 128 | NULL, |
| 129 | 129 | }; |
| 130 | −ATTRIBUTE_GROUPS(fme_hdr); | |
| 131 | 130 | |
| 132 | −static int fme_hdr_init(struct platform_device *pdev, | |
| 133 | − struct dfl_feature *feature) | |
| 134 | −{ | |
| 135 | − void __iomem *base = feature->ioaddr; | |
| 136 | − int ret; | |
| 137 | − | |
| 138 | − dev_dbg(&pdev->dev, "FME HDR Init.\n"); | |
| 139 | − dev_dbg(&pdev->dev, "FME cap %llx.\n", | |
| 140 | − (unsigned long long)readq(base + FME_HDR_CAP)); | |
| 141 | − | |
| 142 | − ret = device_add_groups(&pdev->dev, fme_hdr_groups); | |
| 143 | − if (ret) | |
| 144 | − return ret; | |
| 145 | − | |
| 146 | − return 0; | |
| 147 | −} | |
| 148 | − | |
| 149 | −static void fme_hdr_uinit(struct platform_device *pdev, | |
| 150 | − struct dfl_feature *feature) | |
| 151 | −{ | |
| 152 | − dev_dbg(&pdev->dev, "FME HDR UInit.\n"); | |
| 153 | − device_remove_groups(&pdev->dev, fme_hdr_groups); | |
| 154 | −} | |
| 131 | +static const struct attribute_group fme_hdr_group = { | |
| 132 | + .attrs = fme_hdr_attrs, | |
| 133 | +}; | |
| 155 | 134 | |
| 156 | 135 | static long fme_hdr_ioctl_release_port(struct dfl_feature_platform_data *pdata, |
| 157 | 136 | unsigned long arg) |
| @@ -199,8 +178,6 @@ static const struct dfl_feature_id fme_hdr_id_table[] = { · 41 unmodified lines | ||
| 199 | 178 | }; |
| 200 | 179 | |
| 201 | 180 | static const struct dfl_feature_ops fme_hdr_ops = { |
| 202 | − .init = fme_hdr_init, | |
| 203 | − .uinit = fme_hdr_uinit, | |
| 204 | 181 | .ioctl = fme_hdr_ioctl, |
| 205 | 182 | }; |
| 206 | 183 | |
| @@ -213,6 +190,10 @@ static struct dfl_feature_driver fme_feature_drvs[] = { · 6 unmodified lines | ||
| 213 | 190 | .id_table = fme_pr_mgmt_id_table, |
| 214 | 191 | .ops = &fme_pr_mgmt_ops, |
| 215 | 192 | }, |
| 193 | + { | |
| 194 | + .id_table = fme_global_err_id_table, | |
| 195 | + .ops = &fme_global_err_ops, | |
| 196 | + }, | |
| 216 | 197 | { |
| 217 | 198 | .ops = NULL, |
| 218 | 199 | }, |
| @@ -359,9 +340,16 @@ static int fme_remove(struct platform_device *pdev) · 140 unmodified lines | ||
| 359 | 340 | return 0; |
| 360 | 341 | } |
| 361 | 342 | |
| 343 | +static const struct attribute_group *fme_dev_groups[] = { | |
| 344 | + &fme_hdr_group, | |
| 345 | + &fme_global_err_group, | |
| 346 | + NULL | |
| 347 | +}; | |
| 348 | + | |
| 362 | 349 | static struct platform_driver fme_driver = { |
| 363 | 350 | .driver = { |
| 364 | − .name = DFL_FPGA_FEATURE_DEV_FME, | |
| 351 | + .name = DFL_FPGA_FEATURE_DEV_FME, | |
| 352 | + .dev_groups = fme_dev_groups, | |
| 365 | 353 | }, |
| 366 | 354 | .probe = fme_probe, |
| 367 | 355 | .remove = fme_remove, |
| @@ -35,5 +35,8 @@ struct dfl_fme { · 34 unmodified lines | ||
| 35 | 35 | |
| 36 | 36 | extern const struct dfl_feature_ops fme_pr_mgmt_ops; |
| 37 | 37 | extern const struct dfl_feature_id fme_pr_mgmt_id_table[]; |
| 38 | +extern const struct dfl_feature_ops fme_global_err_ops; | |
| 39 | +extern const struct dfl_feature_id fme_global_err_id_table[]; | |
| 40 | +extern const struct attribute_group fme_global_err_group; | |
| 38 | 41 | |
| 39 | 42 | #endif /* __DFL_FME_H */ |
| @@ -271,11 +271,13 @@ static int dfl_feature_instance_init(struct platform_device *pdev, · 270 unmodified lines | ||
| 271 | 271 | struct dfl_feature *feature, |
| 272 | 272 | struct dfl_feature_driver *drv) |
| 273 | 273 | { |
| 274 | − int ret; | |
| 274 | + int ret = 0; | |
| 275 | 275 | |
| 276 | − ret = drv->ops->init(pdev, feature); | |
| 277 | − if (ret) | |
| 278 | − return ret; | |
| 276 | + if (drv->ops->init) { | |
| 277 | + ret = drv->ops->init(pdev, feature); | |
| 278 | + if (ret) | |
| 279 | + return ret; | |
| 280 | + } | |
| 279 | 281 | |
| 280 | 282 | feature->ops = drv->ops; |
| 281 | 283 | |
| @@ -120,6 +120,10 @@ · 119 unmodified lines | ||
| 120 | 120 | #define PORT_HDR_CAP 0x30 |
| 121 | 121 | #define PORT_HDR_CTRL 0x38 |
| 122 | 122 | #define PORT_HDR_STS 0x40 |
| 123 | +#define PORT_HDR_USRCLK_CMD0 0x50 | |
| 124 | +#define PORT_HDR_USRCLK_CMD1 0x58 | |
| 125 | +#define PORT_HDR_USRCLK_STS0 0x60 | |
| 126 | +#define PORT_HDR_USRCLK_STS1 0x68 | |
| 123 | 127 | |
| 124 | 128 | /* Port Capability Register Bitfield */ |
| 125 | 129 | #define PORT_CAP_PORT_NUM GENMASK_ULL(1, 0) /* ID of this port */ |
| @@ -355,6 +359,11 @@ static inline bool dfl_feature_is_port(void __iomem *base) · 229 unmodified lines | ||
| 355 | 359 | (FIELD_GET(DFH_ID, v) == DFH_ID_FIU_PORT); |
| 356 | 360 | } |
| 357 | 361 | |
| 362 | +static inline u8 dfl_feature_revision(void __iomem *base) | |
| 363 | +{ | |
| 364 | + return (u8)FIELD_GET(DFH_REVISION, readq(base + DFH)); | |
| 365 | +} | |
| 366 | + | |
| 358 | 367 | /** |
| 359 | 368 | * struct dfl_fpga_enum_info - DFL FPGA enumeration information |
| 360 | 369 | * |
| @@ -262,6 +262,8 @@ enum probe_type { · 261 unmodified lines | ||
| 262 | 262 | * @resume: Called to bring a device from sleep mode. |
| 263 | 263 | * @groups: Default attributes that get created by the driver core |
| 264 | 264 | * automatically. |
| 265 | + * @dev_groups: Additional attributes attached to device instance once the | |
| 266 | + * it is bound to the driver. | |
| 265 | 267 | * @pm: Power management operations of the device which matched |
| 266 | 268 | * this driver. |
| 267 | 269 | * @coredump: Called when sysfs entry is written to. The device driver |
| @@ -296,6 +298,7 @@ struct device_driver { · 28 unmodified lines | ||
| 296 | 298 | int (*suspend) (struct device *dev, pm_message_t state); |
| 297 | 299 | int (*resume) (struct device *dev); |
| 298 | 300 | const struct attribute_group **groups; |
| 301 | + const struct attribute_group **dev_groups; | |
| 299 | 302 | |
| 300 | 303 | const struct dev_pm_ops *pm; |
| 301 | 304 | void (*coredump) (struct device *dev); |