xref: /openbmc/linux/drivers/fpga/dfl-afu-main.c (revision 9a8d3cda)
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  */
__afu_port_enable(struct platform_device * pdev)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  */
__afu_port_disable(struct platform_device * pdev)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  */
__port_reset(struct platform_device * pdev)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 
port_reset(struct platform_device * pdev)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 
port_get_id(struct platform_device * pdev)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
id_show(struct device * dev,struct device_attribute * attr,char * buf)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
ltr_show(struct device * dev,struct device_attribute * attr,char * buf)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
ltr_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)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, &ltr))
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
ap1_event_show(struct device * dev,struct device_attribute * attr,char * buf)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
ap1_event_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)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
ap2_event_show(struct device * dev,struct device_attribute * attr,char * buf)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
ap2_event_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)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
power_state_show(struct device * dev,struct device_attribute * attr,char * buf)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
userclk_freqcmd_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)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
userclk_freqcntrcmd_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)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
userclk_freqsts_show(struct device * dev,struct device_attribute * attr,char * buf)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
userclk_freqcntrsts_show(struct device * dev,struct device_attribute * attr,char * buf)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 
port_hdr_attrs_visible(struct kobject * kobj,struct attribute * attr,int n)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 
port_hdr_init(struct platform_device * pdev,struct dfl_feature * feature)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
port_hdr_ioctl(struct platform_device * pdev,struct dfl_feature * feature,unsigned int cmd,unsigned long arg)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
afu_id_show(struct device * dev,struct device_attribute * attr,char * buf)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 
port_afu_attrs_visible(struct kobject * kobj,struct attribute * attr,int n)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 
port_afu_init(struct platform_device * pdev,struct dfl_feature * feature)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 
port_stp_init(struct platform_device * pdev,struct dfl_feature * feature)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
port_uint_ioctl(struct platform_device * pdev,struct dfl_feature * feature,unsigned int cmd,unsigned long arg)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 
afu_open(struct inode * inode,struct file * filp)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 
afu_release(struct inode * inode,struct file * filp)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 
afu_ioctl_check_extension(struct dfl_feature_platform_data * pdata,unsigned long arg)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
afu_ioctl_get_info(struct dfl_feature_platform_data * pdata,void __user * arg)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 
afu_ioctl_get_region_info(struct dfl_feature_platform_data * pdata,void __user * arg)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, &region);
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
afu_ioctl_dma_map(struct dfl_feature_platform_data * pdata,void __user * arg)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
afu_ioctl_dma_unmap(struct dfl_feature_platform_data * pdata,void __user * arg)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 
afu_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)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 
afu_mmap(struct file * filp,struct vm_area_struct * vma)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, &region);
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 
afu_dev_init(struct platform_device * pdev)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 
afu_dev_destroy(struct platform_device * pdev)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 
port_enable_set(struct platform_device * pdev,bool enable)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 
afu_probe(struct platform_device * pdev)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 
afu_remove(struct platform_device * pdev)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 
afu_init(void)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 
afu_exit(void)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