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 | this FPGA belongs to, only valid for integrated solution. | 44 | this FPGA belongs to, only valid for integrated solution. |
| 45 | User only needs this information, in case standard numa node | 45 | User only needs this information, in case standard numa node |
| 46 | can't provide correct information. | 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 | Description: Read-write. Read or set AFU latency tolerance reporting value. | 46 | Description: Read-write. Read or set AFU latency tolerance reporting value. |
| 47 | Set ltr to 1 if the AFU can tolerate latency >= 40us or set it | 47 | Set ltr to 1 if the AFU can tolerate latency >= 40us or set it |
| 48 | to 0 if it is latency sensitive. | 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 | - Get driver API version (DFL_FPGA_GET_API_VERSION) | 87 | - Get driver API version (DFL_FPGA_GET_API_VERSION) |
| 88 | - Check for extensions (DFL_FPGA_CHECK_EXTENSION) | 88 | - Check for extensions (DFL_FPGA_CHECK_EXTENSION) |
| 89 | - Program bitstream (DFL_FPGA_FME_PORT_PR) | 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 | More functions are exposed through sysfs | 93 | More functions are exposed through sysfs |
| 92 | (/sys/class/fpga_region/regionX/dfl-fme.n/): | 94 | (/sys/class/fpga_region/regionX/dfl-fme.n/): |
| @@ -102,6 +104,10 @@ More functions are exposed through sysfs · 9 unmodified lines | |||
| 102 | one FPGA device may have more than one port, this sysfs interface indicates | 104 | one FPGA device may have more than one port, this sysfs interface indicates |
| 103 | how many ports the FPGA device has. | 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 | FIU - PORT | 112 | FIU - PORT |
| 107 | ========== | 113 | ========== |
| @@ -143,6 +149,10 @@ More functions are exposed through sysfs: · 35 unmodified lines | |||
| 143 | Read Accelerator GUID (afu_id) | 149 | Read Accelerator GUID (afu_id) |
| 144 | afu_id indicates which PR bitstream is programmed to this AFU. | 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 | DFL Framework Overview | 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 | userspace before calling the reconfiguration IOCTL. | 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 | Device enumeration | 326 | Device enumeration |
| 222 | ================== | 327 | ================== |
| 223 | This section introduces how applications enumerate the fpga device from | 328 | This section introduces how applications enumerate the fpga device from |
| @@ -554,9 +554,16 @@ re_probe: · 553 unmodified lines | |||
| 554 | goto probe_failed; | 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 | if (test_remove) { | 562 | if (test_remove) { |
| 558 | test_remove = false; | 563 | test_remove = false; |
| 559 | 564 | ||
| 565 | device_remove_groups(dev, drv->dev_groups); | ||
| 566 | |||
| 560 | if (dev->bus->remove) | 567 | if (dev->bus->remove) |
| 561 | dev->bus->remove(dev); | 568 | dev->bus->remove(dev); |
| 562 | else if (drv->remove) | 569 | else if (drv->remove) |
| @@ -584,6 +591,11 @@ re_probe: · 21 unmodified lines | |||
| 584 | drv->bus->name, __func__, dev_name(dev), drv->name); | 591 | drv->bus->name, __func__, dev_name(dev), drv->name); |
| 585 | goto done; | 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 | probe_failed: | 599 | probe_failed: |
| 588 | if (dev->bus) | 600 | if (dev->bus) |
| 589 | blocking_notifier_call_chain(&dev->bus->p->bus_notifier, | 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 | pm_runtime_put_sync(dev); | 1127 | pm_runtime_put_sync(dev); |
| 1116 | 1128 | ||
| 1129 | device_remove_groups(dev, drv->dev_groups); | ||
| 1130 | |||
| 1117 | if (dev->bus && dev->bus->remove) | 1131 | if (dev->bus && dev->bus->remove) |
| 1118 | dev->bus->remove(dev); | 1132 | dev->bus->remove(dev); |
| 1119 | else if (drv->remove) | 1133 | else if (drv->remove) |
| @@ -39,8 +39,9 @@ obj-$(CONFIG_FPGA_DFL_FME_BRIDGE) += dfl-fme-br.o · 38 unmodified lines | |||
| 39 | obj-$(CONFIG_FPGA_DFL_FME_REGION) += dfl-fme-region.o | 39 | obj-$(CONFIG_FPGA_DFL_FME_REGION) += dfl-fme-region.o |
| 40 | obj-$(CONFIG_FPGA_DFL_AFU) += dfl-afu.o | 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 | dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o dfl-afu-dma-region.o | 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 | # Drivers for FPGAs which implement DFL | 46 | # Drivers for FPGAs which implement DFL |
| 46 | obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o | 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 | #include "dfl-afu.h" | 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 | * @pdev: port platform device. | 26 | * @pdev: port platform device. |
| 27 | * | 27 | * |
| 28 | * Enable Port by clear the port soft reset bit, which is set by default. | 28 | * Enable Port by clear the port soft reset bit, which is set by default. |
| 29 | * The AFU is unable to respond to any MMIO access while in reset. | 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 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); | 37 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); |
| 35 | void __iomem *base; | 38 | void __iomem *base; |
| @@ -52,13 +55,14 @@ static void port_enable(struct platform_device *pdev) · 16 unmodified lines | |||
| 52 | #define RST_POLL_TIMEOUT 1000 /* us */ | 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 | * @pdev: port platform device. | 59 | * @pdev: port platform device. |
| 57 | * | 60 | * |
| 58 | * Disable Port by setting the port soft reset bit, it puts the port into | 61 | * Disable Port by setting the port soft reset bit, it puts the port into reset. |
| 59 | * 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 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); | 67 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); |
| 64 | void __iomem *base; | 68 | void __iomem *base; |
| @@ -104,9 +108,9 @@ static int __port_reset(struct platform_device *pdev) · 39 unmodified lines | |||
| 104 | { | 108 | { |
| 105 | int ret; | 109 | int ret; |
| 106 | 110 | ||
| 107 | ret = port_disable(pdev); | 111 | ret = __afu_port_disable(pdev); |
| 108 | if (!ret) | 112 | if (!ret) |
| 109 | port_enable(pdev); | 113 | __afu_port_enable(pdev); |
| 110 | 114 | ||
| 111 | return ret; | 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 | static DEVICE_ATTR_RO(power_state); | 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 | static struct attribute *port_hdr_attrs[] = { | 360 | static struct attribute *port_hdr_attrs[] = { |
| 278 | &dev_attr_id.attr, | 361 | &dev_attr_id.attr, |
| 279 | &dev_attr_ltr.attr, | 362 | &dev_attr_ltr.attr, |
| 280 | &dev_attr_ap1_event.attr, | 363 | &dev_attr_ap1_event.attr, |
| 281 | &dev_attr_ap2_event.attr, | 364 | &dev_attr_ap2_event.attr, |
| 282 | &dev_attr_power_state.attr, | 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 | NULL, | 370 | NULL, |
| 284 | }; | 371 | }; |
| 285 | ATTRIBUTE_GROUPS(port_hdr); | ||
| 286 | 372 | ||
| 287 | static int port_hdr_init(struct platform_device *pdev, | 373 | static umode_t port_hdr_attrs_visible(struct kobject *kobj, |
| 288 | struct dfl_feature *feature) | 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, | 398 | static const struct attribute_group port_hdr_group = { |
| 298 | struct dfl_feature *feature) | 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 | static long | 411 | static long |
| @@ -330,7 +436,6 @@ static const struct dfl_feature_id port_hdr_id_table[] = { · 24 unmodified lines | |||
| 330 | 436 | ||
| 331 | static const struct dfl_feature_ops port_hdr_ops = { | 437 | static const struct dfl_feature_ops port_hdr_ops = { |
| 332 | .init = port_hdr_init, | 438 | .init = port_hdr_init, |
| 333 | .uinit = port_hdr_uinit, | ||
| 334 | .ioctl = port_hdr_ioctl, | 439 | .ioctl = port_hdr_ioctl, |
| 335 | }; | 440 | }; |
| 336 | 441 | ||
| @@ -361,32 +466,37 @@ static struct attribute *port_afu_attrs[] = { · 24 unmodified lines | |||
| 361 | &dev_attr_afu_id.attr, | 466 | &dev_attr_afu_id.attr, |
| 362 | NULL | 467 | NULL |
| 363 | }; | 468 | }; |
| 364 | ATTRIBUTE_GROUPS(port_afu); | ||
| 365 | 469 | ||
| 366 | static int port_afu_init(struct platform_device *pdev, | 470 | static umode_t port_afu_attrs_visible(struct kobject *kobj, |
| 367 | struct dfl_feature *feature) | 471 | struct attribute *attr, int n) |
| 368 | { | 472 | { |
| 369 | struct resource *res = &pdev->resource[feature->resource_index]; | 473 | struct device *dev = kobj_to_dev(kobj); |
| 370 | int ret; | ||
| 371 | 474 | ||
| 372 | dev_dbg(&pdev->dev, "PORT AFU Init.\n"); | 475 | /* |
| 373 | 476 | * sysfs entries are visible only if related private feature is | |
| 374 | ret = afu_mmio_region_add(dev_get_platdata(&pdev->dev), | 477 | * enumerated. |
| 375 | DFL_PORT_REGION_INDEX_AFU, resource_size(res), | 478 | */ |
| 376 | res->start, DFL_PORT_REGION_READ | | 479 | if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_AFU)) |
| 377 | DFL_PORT_REGION_WRITE | DFL_PORT_REGION_MMAP); | 480 | return 0; |
| 378 | if (ret) | ||
| 379 | return ret; | ||
| 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, | 485 | static const struct attribute_group port_afu_group = { |
| 385 | struct dfl_feature *feature) | 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 | static const struct dfl_feature_id port_afu_id_table[] = { | 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 | static const struct dfl_feature_ops port_afu_ops = { | 507 | static const struct dfl_feature_ops port_afu_ops = { |
| 398 | .init = port_afu_init, | 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 | static struct dfl_feature_driver port_feature_drvs[] = { | 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 | .id_table = port_afu_id_table, | 538 | .id_table = port_afu_id_table, |
| 409 | .ops = &port_afu_ops, | 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 | .ops = NULL, | 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 | mutex_lock(&pdata->lock); | 833 | mutex_lock(&pdata->lock); |
| 696 | if (enable) | 834 | if (enable) |
| 697 | port_enable(pdev); | 835 | __afu_port_enable(pdev); |
| 698 | else | 836 | else |
| 699 | ret = port_disable(pdev); | 837 | ret = __afu_port_disable(pdev); |
| 700 | mutex_unlock(&pdata->lock); | 838 | mutex_unlock(&pdata->lock); |
| 701 | 839 | ||
| 702 | return ret; | 840 | return ret; |
| @@ -748,9 +886,17 @@ static int afu_remove(struct platform_device *pdev) · 45 unmodified lines | |||
| 748 | return 0; | 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 | static struct platform_driver afu_driver = { | 896 | static struct platform_driver afu_driver = { |
| 752 | .driver = { | 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 | .probe = afu_probe, | 901 | .probe = afu_probe, |
| 756 | .remove = afu_remove, | 902 | .remove = afu_remove, |
| @@ -79,6 +79,10 @@ struct dfl_afu { · 78 unmodified lines | |||
| 79 | struct dfl_feature_platform_data *pdata; | 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 | void afu_mmio_region_init(struct dfl_feature_platform_data *pdata); | 86 | void afu_mmio_region_init(struct dfl_feature_platform_data *pdata); |
| 83 | int afu_mmio_region_add(struct dfl_feature_platform_data *pdata, | 87 | int afu_mmio_region_add(struct dfl_feature_platform_data *pdata, |
| 84 | u32 region_index, u64 region_size, u64 phys, u32 flags); | 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 | struct dfl_afu_dma_region * | 101 | struct dfl_afu_dma_region * |
| 98 | afu_dma_region_find(struct dfl_feature_platform_data *pdata, | 102 | afu_dma_region_find(struct dfl_feature_platform_data *pdata, |
| 99 | u64 iova, u64 size); | 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 | #endif /* __DFL_AFU_H */ | 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 | &dev_attr_socket_id.attr, | 127 | &dev_attr_socket_id.attr, |
| 128 | NULL, | 128 | NULL, |
| 129 | }; | 129 | }; |
| 130 | ATTRIBUTE_GROUPS(fme_hdr); | ||
| 131 | 130 | ||
| 132 | static int fme_hdr_init(struct platform_device *pdev, | 131 | static const struct attribute_group fme_hdr_group = { |
| 133 | struct dfl_feature *feature) | 132 | .attrs = fme_hdr_attrs, |
| 134 | { | 133 | }; |
| 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 | } | ||
| 155 | 134 | ||
| 156 | static long fme_hdr_ioctl_release_port(struct dfl_feature_platform_data *pdata, | 135 | static long fme_hdr_ioctl_release_port(struct dfl_feature_platform_data *pdata, |
| 157 | unsigned long arg) | 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 | static const struct dfl_feature_ops fme_hdr_ops = { | 180 | static const struct dfl_feature_ops fme_hdr_ops = { |
| 202 | .init = fme_hdr_init, | ||
| 203 | .uinit = fme_hdr_uinit, | ||
| 204 | .ioctl = fme_hdr_ioctl, | 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 | .id_table = fme_pr_mgmt_id_table, | 190 | .id_table = fme_pr_mgmt_id_table, |
| 214 | .ops = &fme_pr_mgmt_ops, | 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 | .ops = NULL, | 198 | .ops = NULL, |
| 218 | }, | 199 | }, |
| @@ -359,9 +340,16 @@ static int fme_remove(struct platform_device *pdev) · 140 unmodified lines | |||
| 359 | return 0; | 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 | static struct platform_driver fme_driver = { | 349 | static struct platform_driver fme_driver = { |
| 363 | .driver = { | 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 | .probe = fme_probe, | 354 | .probe = fme_probe, |
| 367 | .remove = fme_remove, | 355 | .remove = fme_remove, |
| @@ -35,5 +35,8 @@ struct dfl_fme { · 34 unmodified lines | |||
| 35 | 35 | ||
| 36 | extern const struct dfl_feature_ops fme_pr_mgmt_ops; | 36 | extern const struct dfl_feature_ops fme_pr_mgmt_ops; |
| 37 | extern const struct dfl_feature_id fme_pr_mgmt_id_table[]; | 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 | #endif /* __DFL_FME_H */ | 42 | #endif /* __DFL_FME_H */ |
| @@ -271,11 +271,13 @@ static int dfl_feature_instance_init(struct platform_device *pdev, · 270 unmodified lines | |||
| 271 | struct dfl_feature *feature, | 271 | struct dfl_feature *feature, |
| 272 | struct dfl_feature_driver *drv) | 272 | struct dfl_feature_driver *drv) |
| 273 | { | 273 | { |
| 274 | int ret; | 274 | int ret = 0; |
| 275 | 275 | ||
| 276 | ret = drv->ops->init(pdev, feature); | 276 | if (drv->ops->init) { |
| 277 | if (ret) | 277 | ret = drv->ops->init(pdev, feature); |
| 278 | return ret; | 278 | if (ret) |
| 279 | return ret; | ||
| 280 | } | ||
| 279 | 281 | ||
| 280 | feature->ops = drv->ops; | 282 | feature->ops = drv->ops; |
| 281 | 283 | ||
| @@ -120,6 +120,10 @@ · 119 unmodified lines | |||
| 120 | #define PORT_HDR_CAP 0x30 | 120 | #define PORT_HDR_CAP 0x30 |
| 121 | #define PORT_HDR_CTRL 0x38 | 121 | #define PORT_HDR_CTRL 0x38 |
| 122 | #define PORT_HDR_STS 0x40 | 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 | /* Port Capability Register Bitfield */ | 128 | /* Port Capability Register Bitfield */ |
| 125 | #define PORT_CAP_PORT_NUM GENMASK_ULL(1, 0) /* ID of this port */ | 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 | (FIELD_GET(DFH_ID, v) == DFH_ID_FIU_PORT); | 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 | * struct dfl_fpga_enum_info - DFL FPGA enumeration information | 368 | * struct dfl_fpga_enum_info - DFL FPGA enumeration information |
| 360 | * | 369 | * |
| @@ -262,6 +262,8 @@ enum probe_type { · 261 unmodified lines | |||
| 262 | * @resume: Called to bring a device from sleep mode. | 262 | * @resume: Called to bring a device from sleep mode. |
| 263 | * @groups: Default attributes that get created by the driver core | 263 | * @groups: Default attributes that get created by the driver core |
| 264 | * automatically. | 264 | * automatically. |
| 265 | * @dev_groups: Additional attributes attached to device instance once the | ||
| 266 | * it is bound to the driver. | ||
| 265 | * @pm: Power management operations of the device which matched | 267 | * @pm: Power management operations of the device which matched |
| 266 | * this driver. | 268 | * this driver. |
| 267 | * @coredump: Called when sysfs entry is written to. The device driver | 269 | * @coredump: Called when sysfs entry is written to. The device driver |
| @@ -296,6 +298,7 @@ struct device_driver { · 28 unmodified lines | |||
| 296 | int (*suspend) (struct device *dev, pm_message_t state); | 298 | int (*suspend) (struct device *dev, pm_message_t state); |
| 297 | int (*resume) (struct device *dev); | 299 | int (*resume) (struct device *dev); |
| 298 | const struct attribute_group **groups; | 300 | const struct attribute_group **groups; |
| 301 | const struct attribute_group **dev_groups; | ||
| 299 | 302 | ||
| 300 | const struct dev_pm_ops *pm; | 303 | const struct dev_pm_ops *pm; |
| 301 | void (*coredump) (struct device *dev); | 304 | void (*coredump) (struct device *dev); |