11a1527cfSWu Hao // SPDX-License-Identifier: GPL-2.0 21a1527cfSWu Hao /* 31a1527cfSWu Hao * Driver for FPGA Accelerated Function Unit (AFU) 41a1527cfSWu Hao * 51a1527cfSWu Hao * Copyright (C) 2017-2018 Intel Corporation, Inc. 61a1527cfSWu Hao * 71a1527cfSWu Hao * Authors: 81a1527cfSWu Hao * Wu Hao <hao.wu@intel.com> 91a1527cfSWu Hao * Xiao Guangrong <guangrong.xiao@linux.intel.com> 101a1527cfSWu Hao * Joseph Grecco <joe.grecco@intel.com> 111a1527cfSWu Hao * Enno Luebbers <enno.luebbers@intel.com> 121a1527cfSWu Hao * Tim Whisonant <tim.whisonant@intel.com> 131a1527cfSWu Hao * Ananda Ravuri <ananda.ravuri@intel.com> 141a1527cfSWu Hao * Henry Mitchel <henry.mitchel@intel.com> 151a1527cfSWu Hao */ 161a1527cfSWu Hao 171a1527cfSWu Hao #include <linux/kernel.h> 181a1527cfSWu Hao #include <linux/module.h> 19857a2622SXiao Guangrong #include <linux/uaccess.h> 20e4664c0eSWu Hao #include <linux/fpga-dfl.h> 211a1527cfSWu Hao 22857a2622SXiao Guangrong #include "dfl-afu.h" 231a1527cfSWu Hao 24*9a8d3cdaSRuss Weight #define RST_POLL_INVL 10 /* us */ 25*9a8d3cdaSRuss Weight #define RST_POLL_TIMEOUT 1000 /* us */ 26*9a8d3cdaSRuss Weight 2747c1b19cSWu Hao /** 2895844372SWu Hao * __afu_port_enable - enable a port by clear reset 2947c1b19cSWu Hao * @pdev: port platform device. 3047c1b19cSWu Hao * 3147c1b19cSWu Hao * Enable Port by clear the port soft reset bit, which is set by default. 32857a2622SXiao Guangrong * The AFU is unable to respond to any MMIO access while in reset. 3395844372SWu Hao * __afu_port_enable function should only be used after __afu_port_disable 3495844372SWu Hao * function. 3595844372SWu Hao * 3695844372SWu Hao * The caller needs to hold lock for protection. 3747c1b19cSWu Hao */ 38*9a8d3cdaSRuss Weight int __afu_port_enable(struct platform_device *pdev) 3947c1b19cSWu Hao { 4047c1b19cSWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); 4147c1b19cSWu Hao void __iomem *base; 4247c1b19cSWu Hao u64 v; 4347c1b19cSWu Hao 4447c1b19cSWu Hao WARN_ON(!pdata->disable_count); 4547c1b19cSWu Hao 4647c1b19cSWu Hao if (--pdata->disable_count != 0) 47*9a8d3cdaSRuss Weight return 0; 4847c1b19cSWu Hao 4947c1b19cSWu Hao base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER); 5047c1b19cSWu Hao 5147c1b19cSWu Hao /* Clear port soft reset */ 5247c1b19cSWu Hao v = readq(base + PORT_HDR_CTRL); 5347c1b19cSWu Hao v &= ~PORT_CTRL_SFTRST; 5447c1b19cSWu Hao writeq(v, base + PORT_HDR_CTRL); 55*9a8d3cdaSRuss Weight 56*9a8d3cdaSRuss Weight /* 57*9a8d3cdaSRuss Weight * HW clears the ack bit to indicate that the port is fully out 58*9a8d3cdaSRuss Weight * of reset. 59*9a8d3cdaSRuss Weight */ 60*9a8d3cdaSRuss Weight if (readq_poll_timeout(base + PORT_HDR_CTRL, v, 61*9a8d3cdaSRuss Weight !(v & PORT_CTRL_SFTRST_ACK), 62*9a8d3cdaSRuss Weight RST_POLL_INVL, RST_POLL_TIMEOUT)) { 63*9a8d3cdaSRuss Weight dev_err(&pdev->dev, "timeout, failure to enable device\n"); 64*9a8d3cdaSRuss Weight return -ETIMEDOUT; 6547c1b19cSWu Hao } 6647c1b19cSWu Hao 67*9a8d3cdaSRuss Weight return 0; 68*9a8d3cdaSRuss Weight } 6947c1b19cSWu Hao 7047c1b19cSWu Hao /** 7195844372SWu Hao * __afu_port_disable - disable a port by hold reset 7247c1b19cSWu Hao * @pdev: port platform device. 7347c1b19cSWu Hao * 7495844372SWu Hao * Disable Port by setting the port soft reset bit, it puts the port into reset. 7595844372SWu Hao * 7695844372SWu Hao * The caller needs to hold lock for protection. 7747c1b19cSWu Hao */ 7895844372SWu Hao int __afu_port_disable(struct platform_device *pdev) 7947c1b19cSWu Hao { 8047c1b19cSWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); 8147c1b19cSWu Hao void __iomem *base; 8247c1b19cSWu Hao u64 v; 8347c1b19cSWu Hao 8447c1b19cSWu Hao if (pdata->disable_count++ != 0) 8547c1b19cSWu Hao return 0; 8647c1b19cSWu Hao 8747c1b19cSWu Hao base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER); 8847c1b19cSWu Hao 8947c1b19cSWu Hao /* Set port soft reset */ 9047c1b19cSWu Hao v = readq(base + PORT_HDR_CTRL); 9147c1b19cSWu Hao v |= PORT_CTRL_SFTRST; 9247c1b19cSWu Hao writeq(v, base + PORT_HDR_CTRL); 9347c1b19cSWu Hao 9447c1b19cSWu Hao /* 9547c1b19cSWu Hao * HW sets ack bit to 1 when all outstanding requests have been drained 9647c1b19cSWu Hao * on this port and minimum soft reset pulse width has elapsed. 9747c1b19cSWu Hao * Driver polls port_soft_reset_ack to determine if reset done by HW. 9847c1b19cSWu Hao */ 998614afd6SMatthew Gerlach if (readq_poll_timeout(base + PORT_HDR_CTRL, v, 1008614afd6SMatthew Gerlach v & PORT_CTRL_SFTRST_ACK, 10147c1b19cSWu Hao RST_POLL_INVL, RST_POLL_TIMEOUT)) { 102*9a8d3cdaSRuss Weight dev_err(&pdev->dev, "timeout, failure to disable device\n"); 10347c1b19cSWu Hao return -ETIMEDOUT; 10447c1b19cSWu Hao } 10547c1b19cSWu Hao 10647c1b19cSWu Hao return 0; 10747c1b19cSWu Hao } 10847c1b19cSWu Hao 109e4664c0eSWu Hao /* 110e4664c0eSWu Hao * This function resets the FPGA Port and its accelerator (AFU) by function 111e4664c0eSWu Hao * __port_disable and __port_enable (set port soft reset bit and then clear 112e4664c0eSWu Hao * it). Userspace can do Port reset at any time, e.g. during DMA or Partial 113e4664c0eSWu Hao * Reconfiguration. But it should never cause any system level issue, only 114e4664c0eSWu Hao * functional failure (e.g. DMA or PR operation failure) and be recoverable 115e4664c0eSWu Hao * from the failure. 116e4664c0eSWu Hao * 117e4664c0eSWu Hao * Note: the accelerator (AFU) is not accessible when its port is in reset 118e4664c0eSWu Hao * (disabled). Any attempts on MMIO access to AFU while in reset, will 119e4664c0eSWu Hao * result errors reported via port error reporting sub feature (if present). 120e4664c0eSWu Hao */ 121e4664c0eSWu Hao static int __port_reset(struct platform_device *pdev) 122e4664c0eSWu Hao { 123e4664c0eSWu Hao int ret; 124e4664c0eSWu Hao 12595844372SWu Hao ret = __afu_port_disable(pdev); 126*9a8d3cdaSRuss Weight if (ret) 127e4664c0eSWu Hao return ret; 128*9a8d3cdaSRuss Weight 129*9a8d3cdaSRuss Weight return __afu_port_enable(pdev); 130e4664c0eSWu Hao } 131e4664c0eSWu Hao 132e4664c0eSWu Hao static int port_reset(struct platform_device *pdev) 133e4664c0eSWu Hao { 134e4664c0eSWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); 135e4664c0eSWu Hao int ret; 136e4664c0eSWu Hao 137e4664c0eSWu Hao mutex_lock(&pdata->lock); 138e4664c0eSWu Hao ret = __port_reset(pdev); 139e4664c0eSWu Hao mutex_unlock(&pdata->lock); 140e4664c0eSWu Hao 141e4664c0eSWu Hao return ret; 142e4664c0eSWu Hao } 143e4664c0eSWu Hao 14447c1b19cSWu Hao static int port_get_id(struct platform_device *pdev) 14547c1b19cSWu Hao { 14647c1b19cSWu Hao void __iomem *base; 14747c1b19cSWu Hao 14847c1b19cSWu Hao base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER); 14947c1b19cSWu Hao 15047c1b19cSWu Hao return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + PORT_HDR_CAP)); 15147c1b19cSWu Hao } 15247c1b19cSWu Hao 153e4664c0eSWu Hao static ssize_t 154e4664c0eSWu Hao id_show(struct device *dev, struct device_attribute *attr, char *buf) 155e4664c0eSWu Hao { 156e4664c0eSWu Hao int id = port_get_id(to_platform_device(dev)); 157e4664c0eSWu Hao 158e4664c0eSWu Hao return scnprintf(buf, PAGE_SIZE, "%d\n", id); 159e4664c0eSWu Hao } 160e4664c0eSWu Hao static DEVICE_ATTR_RO(id); 161e4664c0eSWu Hao 162d2ad5ac1SWu Hao static ssize_t 163d2ad5ac1SWu Hao ltr_show(struct device *dev, struct device_attribute *attr, char *buf) 164d2ad5ac1SWu Hao { 165d2ad5ac1SWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); 166d2ad5ac1SWu Hao void __iomem *base; 167d2ad5ac1SWu Hao u64 v; 168d2ad5ac1SWu Hao 169d2ad5ac1SWu Hao base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); 170d2ad5ac1SWu Hao 171d2ad5ac1SWu Hao mutex_lock(&pdata->lock); 172d2ad5ac1SWu Hao v = readq(base + PORT_HDR_CTRL); 173d2ad5ac1SWu Hao mutex_unlock(&pdata->lock); 174d2ad5ac1SWu Hao 175d2ad5ac1SWu Hao return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_CTRL_LATENCY, v)); 176d2ad5ac1SWu Hao } 177d2ad5ac1SWu Hao 178d2ad5ac1SWu Hao static ssize_t 179d2ad5ac1SWu Hao ltr_store(struct device *dev, struct device_attribute *attr, 180d2ad5ac1SWu Hao const char *buf, size_t count) 181d2ad5ac1SWu Hao { 182d2ad5ac1SWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); 183d2ad5ac1SWu Hao void __iomem *base; 184d2ad5ac1SWu Hao bool ltr; 185d2ad5ac1SWu Hao u64 v; 186d2ad5ac1SWu Hao 187d2ad5ac1SWu Hao if (kstrtobool(buf, <r)) 188d2ad5ac1SWu Hao return -EINVAL; 189d2ad5ac1SWu Hao 190d2ad5ac1SWu Hao base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); 191d2ad5ac1SWu Hao 192d2ad5ac1SWu Hao mutex_lock(&pdata->lock); 193d2ad5ac1SWu Hao v = readq(base + PORT_HDR_CTRL); 194d2ad5ac1SWu Hao v &= ~PORT_CTRL_LATENCY; 195d2ad5ac1SWu Hao v |= FIELD_PREP(PORT_CTRL_LATENCY, ltr ? 1 : 0); 196d2ad5ac1SWu Hao writeq(v, base + PORT_HDR_CTRL); 197d2ad5ac1SWu Hao mutex_unlock(&pdata->lock); 198d2ad5ac1SWu Hao 199d2ad5ac1SWu Hao return count; 200d2ad5ac1SWu Hao } 201d2ad5ac1SWu Hao static DEVICE_ATTR_RW(ltr); 202d2ad5ac1SWu Hao 203d2ad5ac1SWu Hao static ssize_t 204d2ad5ac1SWu Hao ap1_event_show(struct device *dev, struct device_attribute *attr, char *buf) 205d2ad5ac1SWu Hao { 206d2ad5ac1SWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); 207d2ad5ac1SWu Hao void __iomem *base; 208d2ad5ac1SWu Hao u64 v; 209d2ad5ac1SWu Hao 210d2ad5ac1SWu Hao base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); 211d2ad5ac1SWu Hao 212d2ad5ac1SWu Hao mutex_lock(&pdata->lock); 213d2ad5ac1SWu Hao v = readq(base + PORT_HDR_STS); 214d2ad5ac1SWu Hao mutex_unlock(&pdata->lock); 215d2ad5ac1SWu Hao 216d2ad5ac1SWu Hao return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP1_EVT, v)); 217d2ad5ac1SWu Hao } 218d2ad5ac1SWu Hao 219d2ad5ac1SWu Hao static ssize_t 220d2ad5ac1SWu Hao ap1_event_store(struct device *dev, struct device_attribute *attr, 221d2ad5ac1SWu Hao const char *buf, size_t count) 222d2ad5ac1SWu Hao { 223d2ad5ac1SWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); 224d2ad5ac1SWu Hao void __iomem *base; 225d2ad5ac1SWu Hao bool clear; 226d2ad5ac1SWu Hao 227d2ad5ac1SWu Hao if (kstrtobool(buf, &clear) || !clear) 228d2ad5ac1SWu Hao return -EINVAL; 229d2ad5ac1SWu Hao 230d2ad5ac1SWu Hao base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); 231d2ad5ac1SWu Hao 232d2ad5ac1SWu Hao mutex_lock(&pdata->lock); 233d2ad5ac1SWu Hao writeq(PORT_STS_AP1_EVT, base + PORT_HDR_STS); 234d2ad5ac1SWu Hao mutex_unlock(&pdata->lock); 235d2ad5ac1SWu Hao 236d2ad5ac1SWu Hao return count; 237d2ad5ac1SWu Hao } 238d2ad5ac1SWu Hao static DEVICE_ATTR_RW(ap1_event); 239d2ad5ac1SWu Hao 240d2ad5ac1SWu Hao static ssize_t 241d2ad5ac1SWu Hao ap2_event_show(struct device *dev, struct device_attribute *attr, 242d2ad5ac1SWu Hao char *buf) 243d2ad5ac1SWu Hao { 244d2ad5ac1SWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); 245d2ad5ac1SWu Hao void __iomem *base; 246d2ad5ac1SWu Hao u64 v; 247d2ad5ac1SWu Hao 248d2ad5ac1SWu Hao base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); 249d2ad5ac1SWu Hao 250d2ad5ac1SWu Hao mutex_lock(&pdata->lock); 251d2ad5ac1SWu Hao v = readq(base + PORT_HDR_STS); 252d2ad5ac1SWu Hao mutex_unlock(&pdata->lock); 253d2ad5ac1SWu Hao 254d2ad5ac1SWu Hao return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP2_EVT, v)); 255d2ad5ac1SWu Hao } 256d2ad5ac1SWu Hao 257d2ad5ac1SWu Hao static ssize_t 258d2ad5ac1SWu Hao ap2_event_store(struct device *dev, struct device_attribute *attr, 259d2ad5ac1SWu Hao const char *buf, size_t count) 260d2ad5ac1SWu Hao { 261d2ad5ac1SWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); 262d2ad5ac1SWu Hao void __iomem *base; 263d2ad5ac1SWu Hao bool clear; 264d2ad5ac1SWu Hao 265d2ad5ac1SWu Hao if (kstrtobool(buf, &clear) || !clear) 266d2ad5ac1SWu Hao return -EINVAL; 267d2ad5ac1SWu Hao 268d2ad5ac1SWu Hao base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); 269d2ad5ac1SWu Hao 270d2ad5ac1SWu Hao mutex_lock(&pdata->lock); 271d2ad5ac1SWu Hao writeq(PORT_STS_AP2_EVT, base + PORT_HDR_STS); 272d2ad5ac1SWu Hao mutex_unlock(&pdata->lock); 273d2ad5ac1SWu Hao 274d2ad5ac1SWu Hao return count; 275d2ad5ac1SWu Hao } 276d2ad5ac1SWu Hao static DEVICE_ATTR_RW(ap2_event); 277d2ad5ac1SWu Hao 278d2ad5ac1SWu Hao static ssize_t 279d2ad5ac1SWu Hao power_state_show(struct device *dev, struct device_attribute *attr, char *buf) 280d2ad5ac1SWu Hao { 281d2ad5ac1SWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); 282d2ad5ac1SWu Hao void __iomem *base; 283d2ad5ac1SWu Hao u64 v; 284d2ad5ac1SWu Hao 285d2ad5ac1SWu Hao base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); 286d2ad5ac1SWu Hao 287d2ad5ac1SWu Hao mutex_lock(&pdata->lock); 288d2ad5ac1SWu Hao v = readq(base + PORT_HDR_STS); 289d2ad5ac1SWu Hao mutex_unlock(&pdata->lock); 290d2ad5ac1SWu Hao 291d2ad5ac1SWu Hao return sprintf(buf, "0x%x\n", (u8)FIELD_GET(PORT_STS_PWR_STATE, v)); 292d2ad5ac1SWu Hao } 293d2ad5ac1SWu Hao static DEVICE_ATTR_RO(power_state); 294d2ad5ac1SWu Hao 295f09991adSWu Hao static ssize_t 296f09991adSWu Hao userclk_freqcmd_store(struct device *dev, struct device_attribute *attr, 297f09991adSWu Hao const char *buf, size_t count) 298f09991adSWu Hao { 299f09991adSWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); 300f09991adSWu Hao u64 userclk_freq_cmd; 301f09991adSWu Hao void __iomem *base; 302f09991adSWu Hao 303f09991adSWu Hao if (kstrtou64(buf, 0, &userclk_freq_cmd)) 304f09991adSWu Hao return -EINVAL; 305f09991adSWu Hao 306f09991adSWu Hao base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); 307f09991adSWu Hao 308f09991adSWu Hao mutex_lock(&pdata->lock); 309f09991adSWu Hao writeq(userclk_freq_cmd, base + PORT_HDR_USRCLK_CMD0); 310f09991adSWu Hao mutex_unlock(&pdata->lock); 311f09991adSWu Hao 312f09991adSWu Hao return count; 313f09991adSWu Hao } 314f09991adSWu Hao static DEVICE_ATTR_WO(userclk_freqcmd); 315f09991adSWu Hao 316f09991adSWu Hao static ssize_t 317f09991adSWu Hao userclk_freqcntrcmd_store(struct device *dev, struct device_attribute *attr, 318f09991adSWu Hao const char *buf, size_t count) 319f09991adSWu Hao { 320f09991adSWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); 321f09991adSWu Hao u64 userclk_freqcntr_cmd; 322f09991adSWu Hao void __iomem *base; 323f09991adSWu Hao 324f09991adSWu Hao if (kstrtou64(buf, 0, &userclk_freqcntr_cmd)) 325f09991adSWu Hao return -EINVAL; 326f09991adSWu Hao 327f09991adSWu Hao base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); 328f09991adSWu Hao 329f09991adSWu Hao mutex_lock(&pdata->lock); 330f09991adSWu Hao writeq(userclk_freqcntr_cmd, base + PORT_HDR_USRCLK_CMD1); 331f09991adSWu Hao mutex_unlock(&pdata->lock); 332f09991adSWu Hao 333f09991adSWu Hao return count; 334f09991adSWu Hao } 335f09991adSWu Hao static DEVICE_ATTR_WO(userclk_freqcntrcmd); 336f09991adSWu Hao 337f09991adSWu Hao static ssize_t 338f09991adSWu Hao userclk_freqsts_show(struct device *dev, struct device_attribute *attr, 339f09991adSWu Hao char *buf) 340f09991adSWu Hao { 341f09991adSWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); 342f09991adSWu Hao u64 userclk_freqsts; 343f09991adSWu Hao void __iomem *base; 344f09991adSWu Hao 345f09991adSWu Hao base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); 346f09991adSWu Hao 347f09991adSWu Hao mutex_lock(&pdata->lock); 348f09991adSWu Hao userclk_freqsts = readq(base + PORT_HDR_USRCLK_STS0); 349f09991adSWu Hao mutex_unlock(&pdata->lock); 350f09991adSWu Hao 351f09991adSWu Hao return sprintf(buf, "0x%llx\n", (unsigned long long)userclk_freqsts); 352f09991adSWu Hao } 353f09991adSWu Hao static DEVICE_ATTR_RO(userclk_freqsts); 354f09991adSWu Hao 355f09991adSWu Hao static ssize_t 356f09991adSWu Hao userclk_freqcntrsts_show(struct device *dev, struct device_attribute *attr, 357f09991adSWu Hao char *buf) 358f09991adSWu Hao { 359f09991adSWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); 360f09991adSWu Hao u64 userclk_freqcntrsts; 361f09991adSWu Hao void __iomem *base; 362f09991adSWu Hao 363f09991adSWu Hao base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); 364f09991adSWu Hao 365f09991adSWu Hao mutex_lock(&pdata->lock); 366f09991adSWu Hao userclk_freqcntrsts = readq(base + PORT_HDR_USRCLK_STS1); 367f09991adSWu Hao mutex_unlock(&pdata->lock); 368f09991adSWu Hao 369f09991adSWu Hao return sprintf(buf, "0x%llx\n", 370f09991adSWu Hao (unsigned long long)userclk_freqcntrsts); 371f09991adSWu Hao } 372f09991adSWu Hao static DEVICE_ATTR_RO(userclk_freqcntrsts); 373f09991adSWu Hao 374dcfecd4dSGreg Kroah-Hartman static struct attribute *port_hdr_attrs[] = { 375e4664c0eSWu Hao &dev_attr_id.attr, 376d2ad5ac1SWu Hao &dev_attr_ltr.attr, 377d2ad5ac1SWu Hao &dev_attr_ap1_event.attr, 378d2ad5ac1SWu Hao &dev_attr_ap2_event.attr, 379d2ad5ac1SWu Hao &dev_attr_power_state.attr, 380f09991adSWu Hao &dev_attr_userclk_freqcmd.attr, 381f09991adSWu Hao &dev_attr_userclk_freqcntrcmd.attr, 382f09991adSWu Hao &dev_attr_userclk_freqsts.attr, 383f09991adSWu Hao &dev_attr_userclk_freqcntrsts.attr, 384e4664c0eSWu Hao NULL, 385e4664c0eSWu Hao }; 386a80a4b82SWu Hao 387f09991adSWu Hao static umode_t port_hdr_attrs_visible(struct kobject *kobj, 388f09991adSWu Hao struct attribute *attr, int n) 389f09991adSWu Hao { 390f09991adSWu Hao struct device *dev = kobj_to_dev(kobj); 391f09991adSWu Hao umode_t mode = attr->mode; 392f09991adSWu Hao void __iomem *base; 393f09991adSWu Hao 394f09991adSWu Hao base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); 395f09991adSWu Hao 396f09991adSWu Hao if (dfl_feature_revision(base) > 0) { 397f09991adSWu Hao /* 398f09991adSWu Hao * userclk sysfs interfaces are only visible in case port 399f09991adSWu Hao * revision is 0, as hardware with revision >0 doesn't 400f09991adSWu Hao * support this. 401f09991adSWu Hao */ 402f09991adSWu Hao if (attr == &dev_attr_userclk_freqcmd.attr || 403f09991adSWu Hao attr == &dev_attr_userclk_freqcntrcmd.attr || 404f09991adSWu Hao attr == &dev_attr_userclk_freqsts.attr || 405f09991adSWu Hao attr == &dev_attr_userclk_freqcntrsts.attr) 406f09991adSWu Hao mode = 0; 407f09991adSWu Hao } 408f09991adSWu Hao 409f09991adSWu Hao return mode; 410f09991adSWu Hao } 411f09991adSWu Hao 412a80a4b82SWu Hao static const struct attribute_group port_hdr_group = { 413a80a4b82SWu Hao .attrs = port_hdr_attrs, 414f09991adSWu Hao .is_visible = port_hdr_attrs_visible, 415a80a4b82SWu Hao }; 416e4664c0eSWu Hao 4171a1527cfSWu Hao static int port_hdr_init(struct platform_device *pdev, 4181a1527cfSWu Hao struct dfl_feature *feature) 4191a1527cfSWu Hao { 420e4664c0eSWu Hao port_reset(pdev); 421e4664c0eSWu Hao 422a80a4b82SWu Hao return 0; 423e4664c0eSWu Hao } 424e4664c0eSWu Hao 425e4664c0eSWu Hao static long 426e4664c0eSWu Hao port_hdr_ioctl(struct platform_device *pdev, struct dfl_feature *feature, 427e4664c0eSWu Hao unsigned int cmd, unsigned long arg) 428e4664c0eSWu Hao { 429e4664c0eSWu Hao long ret; 430e4664c0eSWu Hao 431e4664c0eSWu Hao switch (cmd) { 432e4664c0eSWu Hao case DFL_FPGA_PORT_RESET: 433e4664c0eSWu Hao if (!arg) 434e4664c0eSWu Hao ret = port_reset(pdev); 435e4664c0eSWu Hao else 436e4664c0eSWu Hao ret = -EINVAL; 437e4664c0eSWu Hao break; 438e4664c0eSWu Hao default: 439e4664c0eSWu Hao dev_dbg(&pdev->dev, "%x cmd not handled", cmd); 440e4664c0eSWu Hao ret = -ENODEV; 441e4664c0eSWu Hao } 442e4664c0eSWu Hao 443e4664c0eSWu Hao return ret; 4441a1527cfSWu Hao } 4451a1527cfSWu Hao 44615bbb300SWu Hao static const struct dfl_feature_id port_hdr_id_table[] = { 44715bbb300SWu Hao {.id = PORT_FEATURE_ID_HEADER,}, 44815bbb300SWu Hao {0,} 44915bbb300SWu Hao }; 45015bbb300SWu Hao 4511a1527cfSWu Hao static const struct dfl_feature_ops port_hdr_ops = { 4521a1527cfSWu Hao .init = port_hdr_init, 453e4664c0eSWu Hao .ioctl = port_hdr_ioctl, 4541a1527cfSWu Hao }; 4551a1527cfSWu Hao 456857a2622SXiao Guangrong static ssize_t 457857a2622SXiao Guangrong afu_id_show(struct device *dev, struct device_attribute *attr, char *buf) 458857a2622SXiao Guangrong { 459857a2622SXiao Guangrong struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); 460857a2622SXiao Guangrong void __iomem *base; 461857a2622SXiao Guangrong u64 guidl, guidh; 462857a2622SXiao Guangrong 463857a2622SXiao Guangrong base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_AFU); 464857a2622SXiao Guangrong 465857a2622SXiao Guangrong mutex_lock(&pdata->lock); 466857a2622SXiao Guangrong if (pdata->disable_count) { 467857a2622SXiao Guangrong mutex_unlock(&pdata->lock); 468857a2622SXiao Guangrong return -EBUSY; 469857a2622SXiao Guangrong } 470857a2622SXiao Guangrong 471857a2622SXiao Guangrong guidl = readq(base + GUID_L); 472857a2622SXiao Guangrong guidh = readq(base + GUID_H); 473857a2622SXiao Guangrong mutex_unlock(&pdata->lock); 474857a2622SXiao Guangrong 475857a2622SXiao Guangrong return scnprintf(buf, PAGE_SIZE, "%016llx%016llx\n", guidh, guidl); 476857a2622SXiao Guangrong } 477857a2622SXiao Guangrong static DEVICE_ATTR_RO(afu_id); 478857a2622SXiao Guangrong 479dcfecd4dSGreg Kroah-Hartman static struct attribute *port_afu_attrs[] = { 480857a2622SXiao Guangrong &dev_attr_afu_id.attr, 481857a2622SXiao Guangrong NULL 482857a2622SXiao Guangrong }; 483a80a4b82SWu Hao 484a80a4b82SWu Hao static umode_t port_afu_attrs_visible(struct kobject *kobj, 485a80a4b82SWu Hao struct attribute *attr, int n) 486a80a4b82SWu Hao { 487a80a4b82SWu Hao struct device *dev = kobj_to_dev(kobj); 488a80a4b82SWu Hao 489a80a4b82SWu Hao /* 490a80a4b82SWu Hao * sysfs entries are visible only if related private feature is 491a80a4b82SWu Hao * enumerated. 492a80a4b82SWu Hao */ 493a80a4b82SWu Hao if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_AFU)) 494a80a4b82SWu Hao return 0; 495a80a4b82SWu Hao 496a80a4b82SWu Hao return attr->mode; 497a80a4b82SWu Hao } 498a80a4b82SWu Hao 499a80a4b82SWu Hao static const struct attribute_group port_afu_group = { 500a80a4b82SWu Hao .attrs = port_afu_attrs, 501a80a4b82SWu Hao .is_visible = port_afu_attrs_visible, 502a80a4b82SWu Hao }; 503857a2622SXiao Guangrong 504857a2622SXiao Guangrong static int port_afu_init(struct platform_device *pdev, 505857a2622SXiao Guangrong struct dfl_feature *feature) 506857a2622SXiao Guangrong { 507857a2622SXiao Guangrong struct resource *res = &pdev->resource[feature->resource_index]; 508857a2622SXiao Guangrong 509a80a4b82SWu Hao return afu_mmio_region_add(dev_get_platdata(&pdev->dev), 510a80a4b82SWu Hao DFL_PORT_REGION_INDEX_AFU, 511a80a4b82SWu Hao resource_size(res), res->start, 512a80a4b82SWu Hao DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ | 513a80a4b82SWu Hao DFL_PORT_REGION_WRITE); 514857a2622SXiao Guangrong } 515857a2622SXiao Guangrong 51615bbb300SWu Hao static const struct dfl_feature_id port_afu_id_table[] = { 51715bbb300SWu Hao {.id = PORT_FEATURE_ID_AFU,}, 51815bbb300SWu Hao {0,} 51915bbb300SWu Hao }; 52015bbb300SWu Hao 521857a2622SXiao Guangrong static const struct dfl_feature_ops port_afu_ops = { 522857a2622SXiao Guangrong .init = port_afu_init, 523857a2622SXiao Guangrong }; 524857a2622SXiao Guangrong 525bd127b81SWu Hao static int port_stp_init(struct platform_device *pdev, 526bd127b81SWu Hao struct dfl_feature *feature) 527bd127b81SWu Hao { 528bd127b81SWu Hao struct resource *res = &pdev->resource[feature->resource_index]; 529bd127b81SWu Hao 530bd127b81SWu Hao return afu_mmio_region_add(dev_get_platdata(&pdev->dev), 531bd127b81SWu Hao DFL_PORT_REGION_INDEX_STP, 532bd127b81SWu Hao resource_size(res), res->start, 533bd127b81SWu Hao DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ | 534bd127b81SWu Hao DFL_PORT_REGION_WRITE); 535bd127b81SWu Hao } 536bd127b81SWu Hao 537bd127b81SWu Hao static const struct dfl_feature_id port_stp_id_table[] = { 538bd127b81SWu Hao {.id = PORT_FEATURE_ID_STP,}, 539bd127b81SWu Hao {0,} 540bd127b81SWu Hao }; 541bd127b81SWu Hao 542bd127b81SWu Hao static const struct dfl_feature_ops port_stp_ops = { 543bd127b81SWu Hao .init = port_stp_init, 544bd127b81SWu Hao }; 545bd127b81SWu Hao 54609d86150SXu Yilun static long 54709d86150SXu Yilun port_uint_ioctl(struct platform_device *pdev, struct dfl_feature *feature, 54809d86150SXu Yilun unsigned int cmd, unsigned long arg) 54909d86150SXu Yilun { 55009d86150SXu Yilun switch (cmd) { 55109d86150SXu Yilun case DFL_FPGA_PORT_UINT_GET_IRQ_NUM: 55209d86150SXu Yilun return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg); 55309d86150SXu Yilun case DFL_FPGA_PORT_UINT_SET_IRQ: 55409d86150SXu Yilun return dfl_feature_ioctl_set_irq(pdev, feature, arg); 55509d86150SXu Yilun default: 55609d86150SXu Yilun dev_dbg(&pdev->dev, "%x cmd not handled", cmd); 55709d86150SXu Yilun return -ENODEV; 55809d86150SXu Yilun } 55909d86150SXu Yilun } 56009d86150SXu Yilun 56109d86150SXu Yilun static const struct dfl_feature_id port_uint_id_table[] = { 56209d86150SXu Yilun {.id = PORT_FEATURE_ID_UINT,}, 56309d86150SXu Yilun {0,} 56409d86150SXu Yilun }; 56509d86150SXu Yilun 56609d86150SXu Yilun static const struct dfl_feature_ops port_uint_ops = { 56709d86150SXu Yilun .ioctl = port_uint_ioctl, 56809d86150SXu Yilun }; 56909d86150SXu Yilun 5701a1527cfSWu Hao static struct dfl_feature_driver port_feature_drvs[] = { 5711a1527cfSWu Hao { 57215bbb300SWu Hao .id_table = port_hdr_id_table, 5731a1527cfSWu Hao .ops = &port_hdr_ops, 5741a1527cfSWu Hao }, 5751a1527cfSWu Hao { 57615bbb300SWu Hao .id_table = port_afu_id_table, 577857a2622SXiao Guangrong .ops = &port_afu_ops, 578857a2622SXiao Guangrong }, 579857a2622SXiao Guangrong { 58044d24753SWu Hao .id_table = port_err_id_table, 58144d24753SWu Hao .ops = &port_err_ops, 58244d24753SWu Hao }, 58344d24753SWu Hao { 584bd127b81SWu Hao .id_table = port_stp_id_table, 585bd127b81SWu Hao .ops = &port_stp_ops, 586bd127b81SWu Hao }, 587bd127b81SWu Hao { 58809d86150SXu Yilun .id_table = port_uint_id_table, 58909d86150SXu Yilun .ops = &port_uint_ops, 59009d86150SXu Yilun }, 59109d86150SXu Yilun { 5921a1527cfSWu Hao .ops = NULL, 5931a1527cfSWu Hao } 5941a1527cfSWu Hao }; 5951a1527cfSWu Hao 5961a1527cfSWu Hao static int afu_open(struct inode *inode, struct file *filp) 5971a1527cfSWu Hao { 5981a1527cfSWu Hao struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode); 5991a1527cfSWu Hao struct dfl_feature_platform_data *pdata; 6001a1527cfSWu Hao int ret; 6011a1527cfSWu Hao 6021a1527cfSWu Hao pdata = dev_get_platdata(&fdev->dev); 6031a1527cfSWu Hao if (WARN_ON(!pdata)) 6041a1527cfSWu Hao return -ENODEV; 6051a1527cfSWu Hao 606b6862193SXu Yilun mutex_lock(&pdata->lock); 607b6862193SXu Yilun ret = dfl_feature_dev_use_begin(pdata, filp->f_flags & O_EXCL); 608b6862193SXu Yilun if (!ret) { 609b6862193SXu Yilun dev_dbg(&fdev->dev, "Device File Opened %d Times\n", 610b6862193SXu Yilun dfl_feature_dev_use_count(pdata)); 6111a1527cfSWu Hao filp->private_data = fdev; 612b6862193SXu Yilun } 613b6862193SXu Yilun mutex_unlock(&pdata->lock); 6141a1527cfSWu Hao 615b6862193SXu Yilun return ret; 6161a1527cfSWu Hao } 6171a1527cfSWu Hao 6181a1527cfSWu Hao static int afu_release(struct inode *inode, struct file *filp) 6191a1527cfSWu Hao { 6201a1527cfSWu Hao struct platform_device *pdev = filp->private_data; 6211a1527cfSWu Hao struct dfl_feature_platform_data *pdata; 622fe6a3d65SXu Yilun struct dfl_feature *feature; 6231a1527cfSWu Hao 6241a1527cfSWu Hao dev_dbg(&pdev->dev, "Device File Release\n"); 6251a1527cfSWu Hao 6261a1527cfSWu Hao pdata = dev_get_platdata(&pdev->dev); 6271a1527cfSWu Hao 628fa8dda1eSWu Hao mutex_lock(&pdata->lock); 629b6862193SXu Yilun dfl_feature_dev_use_end(pdata); 630b6862193SXu Yilun 631b6862193SXu Yilun if (!dfl_feature_dev_use_count(pdata)) { 632fe6a3d65SXu Yilun dfl_fpga_dev_for_each_feature(pdata, feature) 633fe6a3d65SXu Yilun dfl_fpga_set_irq_triggers(feature, 0, 634fe6a3d65SXu Yilun feature->nr_irqs, NULL); 635fa8dda1eSWu Hao __port_reset(pdev); 636fa8dda1eSWu Hao afu_dma_region_destroy(pdata); 637b6862193SXu Yilun } 638fa8dda1eSWu Hao mutex_unlock(&pdata->lock); 639fa8dda1eSWu Hao 6401a1527cfSWu Hao return 0; 6411a1527cfSWu Hao } 6421a1527cfSWu Hao 6436fd893c4SWu Hao static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata, 6446fd893c4SWu Hao unsigned long arg) 6456fd893c4SWu Hao { 6466fd893c4SWu Hao /* No extension support for now */ 6476fd893c4SWu Hao return 0; 6486fd893c4SWu Hao } 6496fd893c4SWu Hao 650857a2622SXiao Guangrong static long 651857a2622SXiao Guangrong afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg) 652857a2622SXiao Guangrong { 653857a2622SXiao Guangrong struct dfl_fpga_port_info info; 654857a2622SXiao Guangrong struct dfl_afu *afu; 655857a2622SXiao Guangrong unsigned long minsz; 656857a2622SXiao Guangrong 657857a2622SXiao Guangrong minsz = offsetofend(struct dfl_fpga_port_info, num_umsgs); 658857a2622SXiao Guangrong 659857a2622SXiao Guangrong if (copy_from_user(&info, arg, minsz)) 660857a2622SXiao Guangrong return -EFAULT; 661857a2622SXiao Guangrong 662857a2622SXiao Guangrong if (info.argsz < minsz) 663857a2622SXiao Guangrong return -EINVAL; 664857a2622SXiao Guangrong 665857a2622SXiao Guangrong mutex_lock(&pdata->lock); 666857a2622SXiao Guangrong afu = dfl_fpga_pdata_get_private(pdata); 667857a2622SXiao Guangrong info.flags = 0; 668857a2622SXiao Guangrong info.num_regions = afu->num_regions; 669857a2622SXiao Guangrong info.num_umsgs = afu->num_umsgs; 670857a2622SXiao Guangrong mutex_unlock(&pdata->lock); 671857a2622SXiao Guangrong 672857a2622SXiao Guangrong if (copy_to_user(arg, &info, sizeof(info))) 673857a2622SXiao Guangrong return -EFAULT; 674857a2622SXiao Guangrong 675857a2622SXiao Guangrong return 0; 676857a2622SXiao Guangrong } 677857a2622SXiao Guangrong 678857a2622SXiao Guangrong static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata, 679857a2622SXiao Guangrong void __user *arg) 680857a2622SXiao Guangrong { 681857a2622SXiao Guangrong struct dfl_fpga_port_region_info rinfo; 682857a2622SXiao Guangrong struct dfl_afu_mmio_region region; 683857a2622SXiao Guangrong unsigned long minsz; 684857a2622SXiao Guangrong long ret; 685857a2622SXiao Guangrong 686857a2622SXiao Guangrong minsz = offsetofend(struct dfl_fpga_port_region_info, offset); 687857a2622SXiao Guangrong 688857a2622SXiao Guangrong if (copy_from_user(&rinfo, arg, minsz)) 689857a2622SXiao Guangrong return -EFAULT; 690857a2622SXiao Guangrong 691857a2622SXiao Guangrong if (rinfo.argsz < minsz || rinfo.padding) 692857a2622SXiao Guangrong return -EINVAL; 693857a2622SXiao Guangrong 694857a2622SXiao Guangrong ret = afu_mmio_region_get_by_index(pdata, rinfo.index, ®ion); 695857a2622SXiao Guangrong if (ret) 696857a2622SXiao Guangrong return ret; 697857a2622SXiao Guangrong 698857a2622SXiao Guangrong rinfo.flags = region.flags; 699857a2622SXiao Guangrong rinfo.size = region.size; 700857a2622SXiao Guangrong rinfo.offset = region.offset; 701857a2622SXiao Guangrong 702857a2622SXiao Guangrong if (copy_to_user(arg, &rinfo, sizeof(rinfo))) 703857a2622SXiao Guangrong return -EFAULT; 704857a2622SXiao Guangrong 705857a2622SXiao Guangrong return 0; 706857a2622SXiao Guangrong } 707857a2622SXiao Guangrong 708fa8dda1eSWu Hao static long 709fa8dda1eSWu Hao afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg) 710fa8dda1eSWu Hao { 711fa8dda1eSWu Hao struct dfl_fpga_port_dma_map map; 712fa8dda1eSWu Hao unsigned long minsz; 713fa8dda1eSWu Hao long ret; 714fa8dda1eSWu Hao 715fa8dda1eSWu Hao minsz = offsetofend(struct dfl_fpga_port_dma_map, iova); 716fa8dda1eSWu Hao 717fa8dda1eSWu Hao if (copy_from_user(&map, arg, minsz)) 718fa8dda1eSWu Hao return -EFAULT; 719fa8dda1eSWu Hao 720fa8dda1eSWu Hao if (map.argsz < minsz || map.flags) 721fa8dda1eSWu Hao return -EINVAL; 722fa8dda1eSWu Hao 723fa8dda1eSWu Hao ret = afu_dma_map_region(pdata, map.user_addr, map.length, &map.iova); 724fa8dda1eSWu Hao if (ret) 725fa8dda1eSWu Hao return ret; 726fa8dda1eSWu Hao 727fa8dda1eSWu Hao if (copy_to_user(arg, &map, sizeof(map))) { 728fa8dda1eSWu Hao afu_dma_unmap_region(pdata, map.iova); 729fa8dda1eSWu Hao return -EFAULT; 730fa8dda1eSWu Hao } 731fa8dda1eSWu Hao 732fa8dda1eSWu Hao dev_dbg(&pdata->dev->dev, "dma map: ua=%llx, len=%llx, iova=%llx\n", 733fa8dda1eSWu Hao (unsigned long long)map.user_addr, 734fa8dda1eSWu Hao (unsigned long long)map.length, 735fa8dda1eSWu Hao (unsigned long long)map.iova); 736fa8dda1eSWu Hao 737fa8dda1eSWu Hao return 0; 738fa8dda1eSWu Hao } 739fa8dda1eSWu Hao 740fa8dda1eSWu Hao static long 741fa8dda1eSWu Hao afu_ioctl_dma_unmap(struct dfl_feature_platform_data *pdata, void __user *arg) 742fa8dda1eSWu Hao { 743fa8dda1eSWu Hao struct dfl_fpga_port_dma_unmap unmap; 744fa8dda1eSWu Hao unsigned long minsz; 745fa8dda1eSWu Hao 746fa8dda1eSWu Hao minsz = offsetofend(struct dfl_fpga_port_dma_unmap, iova); 747fa8dda1eSWu Hao 748fa8dda1eSWu Hao if (copy_from_user(&unmap, arg, minsz)) 749fa8dda1eSWu Hao return -EFAULT; 750fa8dda1eSWu Hao 751fa8dda1eSWu Hao if (unmap.argsz < minsz || unmap.flags) 752fa8dda1eSWu Hao return -EINVAL; 753fa8dda1eSWu Hao 754fa8dda1eSWu Hao return afu_dma_unmap_region(pdata, unmap.iova); 755fa8dda1eSWu Hao } 756fa8dda1eSWu Hao 7571a1527cfSWu Hao static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 7581a1527cfSWu Hao { 7591a1527cfSWu Hao struct platform_device *pdev = filp->private_data; 7601a1527cfSWu Hao struct dfl_feature_platform_data *pdata; 7611a1527cfSWu Hao struct dfl_feature *f; 7621a1527cfSWu Hao long ret; 7631a1527cfSWu Hao 7641a1527cfSWu Hao dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd); 7651a1527cfSWu Hao 7661a1527cfSWu Hao pdata = dev_get_platdata(&pdev->dev); 7671a1527cfSWu Hao 7681a1527cfSWu Hao switch (cmd) { 7696fd893c4SWu Hao case DFL_FPGA_GET_API_VERSION: 7706fd893c4SWu Hao return DFL_FPGA_API_VERSION; 7716fd893c4SWu Hao case DFL_FPGA_CHECK_EXTENSION: 7726fd893c4SWu Hao return afu_ioctl_check_extension(pdata, arg); 773857a2622SXiao Guangrong case DFL_FPGA_PORT_GET_INFO: 774857a2622SXiao Guangrong return afu_ioctl_get_info(pdata, (void __user *)arg); 775857a2622SXiao Guangrong case DFL_FPGA_PORT_GET_REGION_INFO: 776857a2622SXiao Guangrong return afu_ioctl_get_region_info(pdata, (void __user *)arg); 777fa8dda1eSWu Hao case DFL_FPGA_PORT_DMA_MAP: 778fa8dda1eSWu Hao return afu_ioctl_dma_map(pdata, (void __user *)arg); 779fa8dda1eSWu Hao case DFL_FPGA_PORT_DMA_UNMAP: 780fa8dda1eSWu Hao return afu_ioctl_dma_unmap(pdata, (void __user *)arg); 7811a1527cfSWu Hao default: 7821a1527cfSWu Hao /* 7831a1527cfSWu Hao * Let sub-feature's ioctl function to handle the cmd 7841a1527cfSWu Hao * Sub-feature's ioctl returns -ENODEV when cmd is not 7851a1527cfSWu Hao * handled in this sub feature, and returns 0 and other 7861a1527cfSWu Hao * error code if cmd is handled. 7871a1527cfSWu Hao */ 7881a1527cfSWu Hao dfl_fpga_dev_for_each_feature(pdata, f) 7891a1527cfSWu Hao if (f->ops && f->ops->ioctl) { 7901a1527cfSWu Hao ret = f->ops->ioctl(pdev, f, cmd, arg); 7911a1527cfSWu Hao if (ret != -ENODEV) 7921a1527cfSWu Hao return ret; 7931a1527cfSWu Hao } 7941a1527cfSWu Hao } 7951a1527cfSWu Hao 7961a1527cfSWu Hao return -EINVAL; 7971a1527cfSWu Hao } 7981a1527cfSWu Hao 799a2b9d4eaSDominic Chen static const struct vm_operations_struct afu_vma_ops = { 800a2b9d4eaSDominic Chen #ifdef CONFIG_HAVE_IOREMAP_PROT 801a2b9d4eaSDominic Chen .access = generic_access_phys, 802a2b9d4eaSDominic Chen #endif 803a2b9d4eaSDominic Chen }; 804a2b9d4eaSDominic Chen 805857a2622SXiao Guangrong static int afu_mmap(struct file *filp, struct vm_area_struct *vma) 806857a2622SXiao Guangrong { 807857a2622SXiao Guangrong struct platform_device *pdev = filp->private_data; 808857a2622SXiao Guangrong struct dfl_feature_platform_data *pdata; 809857a2622SXiao Guangrong u64 size = vma->vm_end - vma->vm_start; 810857a2622SXiao Guangrong struct dfl_afu_mmio_region region; 811857a2622SXiao Guangrong u64 offset; 812857a2622SXiao Guangrong int ret; 813857a2622SXiao Guangrong 814857a2622SXiao Guangrong if (!(vma->vm_flags & VM_SHARED)) 815857a2622SXiao Guangrong return -EINVAL; 816857a2622SXiao Guangrong 817857a2622SXiao Guangrong pdata = dev_get_platdata(&pdev->dev); 818857a2622SXiao Guangrong 819857a2622SXiao Guangrong offset = vma->vm_pgoff << PAGE_SHIFT; 820857a2622SXiao Guangrong ret = afu_mmio_region_get_by_offset(pdata, offset, size, ®ion); 821857a2622SXiao Guangrong if (ret) 822857a2622SXiao Guangrong return ret; 823857a2622SXiao Guangrong 824857a2622SXiao Guangrong if (!(region.flags & DFL_PORT_REGION_MMAP)) 825857a2622SXiao Guangrong return -EINVAL; 826857a2622SXiao Guangrong 827857a2622SXiao Guangrong if ((vma->vm_flags & VM_READ) && !(region.flags & DFL_PORT_REGION_READ)) 828857a2622SXiao Guangrong return -EPERM; 829857a2622SXiao Guangrong 830857a2622SXiao Guangrong if ((vma->vm_flags & VM_WRITE) && 831857a2622SXiao Guangrong !(region.flags & DFL_PORT_REGION_WRITE)) 832857a2622SXiao Guangrong return -EPERM; 833857a2622SXiao Guangrong 834a2b9d4eaSDominic Chen /* Support debug access to the mapping */ 835a2b9d4eaSDominic Chen vma->vm_ops = &afu_vma_ops; 836a2b9d4eaSDominic Chen 837857a2622SXiao Guangrong vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 838857a2622SXiao Guangrong 839857a2622SXiao Guangrong return remap_pfn_range(vma, vma->vm_start, 840857a2622SXiao Guangrong (region.phys + (offset - region.offset)) >> PAGE_SHIFT, 841857a2622SXiao Guangrong size, vma->vm_page_prot); 842857a2622SXiao Guangrong } 843857a2622SXiao Guangrong 8441a1527cfSWu Hao static const struct file_operations afu_fops = { 8451a1527cfSWu Hao .owner = THIS_MODULE, 8461a1527cfSWu Hao .open = afu_open, 8471a1527cfSWu Hao .release = afu_release, 8481a1527cfSWu Hao .unlocked_ioctl = afu_ioctl, 849857a2622SXiao Guangrong .mmap = afu_mmap, 8501a1527cfSWu Hao }; 8511a1527cfSWu Hao 852857a2622SXiao Guangrong static int afu_dev_init(struct platform_device *pdev) 853857a2622SXiao Guangrong { 854857a2622SXiao Guangrong struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); 855857a2622SXiao Guangrong struct dfl_afu *afu; 856857a2622SXiao Guangrong 857857a2622SXiao Guangrong afu = devm_kzalloc(&pdev->dev, sizeof(*afu), GFP_KERNEL); 858857a2622SXiao Guangrong if (!afu) 859857a2622SXiao Guangrong return -ENOMEM; 860857a2622SXiao Guangrong 861857a2622SXiao Guangrong afu->pdata = pdata; 862857a2622SXiao Guangrong 863857a2622SXiao Guangrong mutex_lock(&pdata->lock); 864857a2622SXiao Guangrong dfl_fpga_pdata_set_private(pdata, afu); 865857a2622SXiao Guangrong afu_mmio_region_init(pdata); 866fa8dda1eSWu Hao afu_dma_region_init(pdata); 867857a2622SXiao Guangrong mutex_unlock(&pdata->lock); 868857a2622SXiao Guangrong 869857a2622SXiao Guangrong return 0; 870857a2622SXiao Guangrong } 871857a2622SXiao Guangrong 872857a2622SXiao Guangrong static int afu_dev_destroy(struct platform_device *pdev) 873857a2622SXiao Guangrong { 874857a2622SXiao Guangrong struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); 875857a2622SXiao Guangrong 876857a2622SXiao Guangrong mutex_lock(&pdata->lock); 877857a2622SXiao Guangrong afu_mmio_region_destroy(pdata); 878fa8dda1eSWu Hao afu_dma_region_destroy(pdata); 879857a2622SXiao Guangrong dfl_fpga_pdata_set_private(pdata, NULL); 880857a2622SXiao Guangrong mutex_unlock(&pdata->lock); 881857a2622SXiao Guangrong 882857a2622SXiao Guangrong return 0; 883857a2622SXiao Guangrong } 884857a2622SXiao Guangrong 88547c1b19cSWu Hao static int port_enable_set(struct platform_device *pdev, bool enable) 88647c1b19cSWu Hao { 88747c1b19cSWu Hao struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); 888*9a8d3cdaSRuss Weight int ret; 88947c1b19cSWu Hao 89047c1b19cSWu Hao mutex_lock(&pdata->lock); 89147c1b19cSWu Hao if (enable) 892*9a8d3cdaSRuss Weight ret = __afu_port_enable(pdev); 89347c1b19cSWu Hao else 89495844372SWu Hao ret = __afu_port_disable(pdev); 89547c1b19cSWu Hao mutex_unlock(&pdata->lock); 89647c1b19cSWu Hao 89747c1b19cSWu Hao return ret; 89847c1b19cSWu Hao } 89947c1b19cSWu Hao 90047c1b19cSWu Hao static struct dfl_fpga_port_ops afu_port_ops = { 90147c1b19cSWu Hao .name = DFL_FPGA_FEATURE_DEV_PORT, 90247c1b19cSWu Hao .owner = THIS_MODULE, 90347c1b19cSWu Hao .get_id = port_get_id, 90447c1b19cSWu Hao .enable_set = port_enable_set, 90547c1b19cSWu Hao }; 90647c1b19cSWu Hao 9071a1527cfSWu Hao static int afu_probe(struct platform_device *pdev) 9081a1527cfSWu Hao { 9091a1527cfSWu Hao int ret; 9101a1527cfSWu Hao 9111a1527cfSWu Hao dev_dbg(&pdev->dev, "%s\n", __func__); 9121a1527cfSWu Hao 913857a2622SXiao Guangrong ret = afu_dev_init(pdev); 914857a2622SXiao Guangrong if (ret) 915857a2622SXiao Guangrong goto exit; 916857a2622SXiao Guangrong 9171a1527cfSWu Hao ret = dfl_fpga_dev_feature_init(pdev, port_feature_drvs); 9181a1527cfSWu Hao if (ret) 919857a2622SXiao Guangrong goto dev_destroy; 9201a1527cfSWu Hao 9211a1527cfSWu Hao ret = dfl_fpga_dev_ops_register(pdev, &afu_fops, THIS_MODULE); 922857a2622SXiao Guangrong if (ret) { 9231a1527cfSWu Hao dfl_fpga_dev_feature_uinit(pdev); 924857a2622SXiao Guangrong goto dev_destroy; 925857a2622SXiao Guangrong } 9261a1527cfSWu Hao 927857a2622SXiao Guangrong return 0; 928857a2622SXiao Guangrong 929857a2622SXiao Guangrong dev_destroy: 930857a2622SXiao Guangrong afu_dev_destroy(pdev); 931857a2622SXiao Guangrong exit: 9321a1527cfSWu Hao return ret; 9331a1527cfSWu Hao } 9341a1527cfSWu Hao 9351a1527cfSWu Hao static int afu_remove(struct platform_device *pdev) 9361a1527cfSWu Hao { 9371a1527cfSWu Hao dev_dbg(&pdev->dev, "%s\n", __func__); 9381a1527cfSWu Hao 9391a1527cfSWu Hao dfl_fpga_dev_ops_unregister(pdev); 9401a1527cfSWu Hao dfl_fpga_dev_feature_uinit(pdev); 941857a2622SXiao Guangrong afu_dev_destroy(pdev); 9421a1527cfSWu Hao 9431a1527cfSWu Hao return 0; 9441a1527cfSWu Hao } 9451a1527cfSWu Hao 946a80a4b82SWu Hao static const struct attribute_group *afu_dev_groups[] = { 947a80a4b82SWu Hao &port_hdr_group, 948a80a4b82SWu Hao &port_afu_group, 94944d24753SWu Hao &port_err_group, 950a80a4b82SWu Hao NULL 951a80a4b82SWu Hao }; 952a80a4b82SWu Hao 9531a1527cfSWu Hao static struct platform_driver afu_driver = { 9541a1527cfSWu Hao .driver = { 9551a1527cfSWu Hao .name = DFL_FPGA_FEATURE_DEV_PORT, 956a80a4b82SWu Hao .dev_groups = afu_dev_groups, 9571a1527cfSWu Hao }, 9581a1527cfSWu Hao .probe = afu_probe, 9591a1527cfSWu Hao .remove = afu_remove, 9601a1527cfSWu Hao }; 9611a1527cfSWu Hao 96247c1b19cSWu Hao static int __init afu_init(void) 96347c1b19cSWu Hao { 96447c1b19cSWu Hao int ret; 96547c1b19cSWu Hao 96647c1b19cSWu Hao dfl_fpga_port_ops_add(&afu_port_ops); 96747c1b19cSWu Hao 96847c1b19cSWu Hao ret = platform_driver_register(&afu_driver); 96947c1b19cSWu Hao if (ret) 97047c1b19cSWu Hao dfl_fpga_port_ops_del(&afu_port_ops); 97147c1b19cSWu Hao 97247c1b19cSWu Hao return ret; 97347c1b19cSWu Hao } 97447c1b19cSWu Hao 97547c1b19cSWu Hao static void __exit afu_exit(void) 97647c1b19cSWu Hao { 97747c1b19cSWu Hao platform_driver_unregister(&afu_driver); 97847c1b19cSWu Hao 97947c1b19cSWu Hao dfl_fpga_port_ops_del(&afu_port_ops); 98047c1b19cSWu Hao } 98147c1b19cSWu Hao 98247c1b19cSWu Hao module_init(afu_init); 98347c1b19cSWu Hao module_exit(afu_exit); 9841a1527cfSWu Hao 9851a1527cfSWu Hao MODULE_DESCRIPTION("FPGA Accelerated Function Unit driver"); 9861a1527cfSWu Hao MODULE_AUTHOR("Intel Corporation"); 9871a1527cfSWu Hao MODULE_LICENSE("GPL v2"); 9881a1527cfSWu Hao MODULE_ALIAS("platform:dfl-port"); 989