xref: /openbmc/linux/drivers/fpga/dfl-afu-main.c (revision f09991adfb3454530598586424ece3082e95fb0b)
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 
2447c1b19cSWu Hao /**
2547c1b19cSWu Hao  * port_enable - enable a port
2647c1b19cSWu Hao  * @pdev: port platform device.
2747c1b19cSWu Hao  *
2847c1b19cSWu Hao  * Enable Port by clear the port soft reset bit, which is set by default.
29857a2622SXiao Guangrong  * The AFU is unable to respond to any MMIO access while in reset.
30857a2622SXiao Guangrong  * port_enable function should only be used after port_disable function.
3147c1b19cSWu Hao  */
3247c1b19cSWu Hao static void port_enable(struct platform_device *pdev)
3347c1b19cSWu Hao {
3447c1b19cSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
3547c1b19cSWu Hao 	void __iomem *base;
3647c1b19cSWu Hao 	u64 v;
3747c1b19cSWu Hao 
3847c1b19cSWu Hao 	WARN_ON(!pdata->disable_count);
3947c1b19cSWu Hao 
4047c1b19cSWu Hao 	if (--pdata->disable_count != 0)
4147c1b19cSWu Hao 		return;
4247c1b19cSWu Hao 
4347c1b19cSWu Hao 	base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
4447c1b19cSWu Hao 
4547c1b19cSWu Hao 	/* Clear port soft reset */
4647c1b19cSWu Hao 	v = readq(base + PORT_HDR_CTRL);
4747c1b19cSWu Hao 	v &= ~PORT_CTRL_SFTRST;
4847c1b19cSWu Hao 	writeq(v, base + PORT_HDR_CTRL);
4947c1b19cSWu Hao }
5047c1b19cSWu Hao 
5147c1b19cSWu Hao #define RST_POLL_INVL 10 /* us */
5247c1b19cSWu Hao #define RST_POLL_TIMEOUT 1000 /* us */
5347c1b19cSWu Hao 
5447c1b19cSWu Hao /**
5547c1b19cSWu Hao  * port_disable - disable a port
5647c1b19cSWu Hao  * @pdev: port platform device.
5747c1b19cSWu Hao  *
5847c1b19cSWu Hao  * Disable Port by setting the port soft reset bit, it puts the port into
5947c1b19cSWu Hao  * reset.
6047c1b19cSWu Hao  */
6147c1b19cSWu Hao static int port_disable(struct platform_device *pdev)
6247c1b19cSWu Hao {
6347c1b19cSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
6447c1b19cSWu Hao 	void __iomem *base;
6547c1b19cSWu Hao 	u64 v;
6647c1b19cSWu Hao 
6747c1b19cSWu Hao 	if (pdata->disable_count++ != 0)
6847c1b19cSWu Hao 		return 0;
6947c1b19cSWu Hao 
7047c1b19cSWu Hao 	base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
7147c1b19cSWu Hao 
7247c1b19cSWu Hao 	/* Set port soft reset */
7347c1b19cSWu Hao 	v = readq(base + PORT_HDR_CTRL);
7447c1b19cSWu Hao 	v |= PORT_CTRL_SFTRST;
7547c1b19cSWu Hao 	writeq(v, base + PORT_HDR_CTRL);
7647c1b19cSWu Hao 
7747c1b19cSWu Hao 	/*
7847c1b19cSWu Hao 	 * HW sets ack bit to 1 when all outstanding requests have been drained
7947c1b19cSWu Hao 	 * on this port and minimum soft reset pulse width has elapsed.
8047c1b19cSWu Hao 	 * Driver polls port_soft_reset_ack to determine if reset done by HW.
8147c1b19cSWu Hao 	 */
8247c1b19cSWu Hao 	if (readq_poll_timeout(base + PORT_HDR_CTRL, v, v & PORT_CTRL_SFTRST,
8347c1b19cSWu Hao 			       RST_POLL_INVL, RST_POLL_TIMEOUT)) {
8447c1b19cSWu Hao 		dev_err(&pdev->dev, "timeout, fail to reset device\n");
8547c1b19cSWu Hao 		return -ETIMEDOUT;
8647c1b19cSWu Hao 	}
8747c1b19cSWu Hao 
8847c1b19cSWu Hao 	return 0;
8947c1b19cSWu Hao }
9047c1b19cSWu Hao 
91e4664c0eSWu Hao /*
92e4664c0eSWu Hao  * This function resets the FPGA Port and its accelerator (AFU) by function
93e4664c0eSWu Hao  * __port_disable and __port_enable (set port soft reset bit and then clear
94e4664c0eSWu Hao  * it). Userspace can do Port reset at any time, e.g. during DMA or Partial
95e4664c0eSWu Hao  * Reconfiguration. But it should never cause any system level issue, only
96e4664c0eSWu Hao  * functional failure (e.g. DMA or PR operation failure) and be recoverable
97e4664c0eSWu Hao  * from the failure.
98e4664c0eSWu Hao  *
99e4664c0eSWu Hao  * Note: the accelerator (AFU) is not accessible when its port is in reset
100e4664c0eSWu Hao  * (disabled). Any attempts on MMIO access to AFU while in reset, will
101e4664c0eSWu Hao  * result errors reported via port error reporting sub feature (if present).
102e4664c0eSWu Hao  */
103e4664c0eSWu Hao static int __port_reset(struct platform_device *pdev)
104e4664c0eSWu Hao {
105e4664c0eSWu Hao 	int ret;
106e4664c0eSWu Hao 
107e4664c0eSWu Hao 	ret = port_disable(pdev);
108e4664c0eSWu Hao 	if (!ret)
109e4664c0eSWu Hao 		port_enable(pdev);
110e4664c0eSWu Hao 
111e4664c0eSWu Hao 	return ret;
112e4664c0eSWu Hao }
113e4664c0eSWu Hao 
114e4664c0eSWu Hao static int port_reset(struct platform_device *pdev)
115e4664c0eSWu Hao {
116e4664c0eSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
117e4664c0eSWu Hao 	int ret;
118e4664c0eSWu Hao 
119e4664c0eSWu Hao 	mutex_lock(&pdata->lock);
120e4664c0eSWu Hao 	ret = __port_reset(pdev);
121e4664c0eSWu Hao 	mutex_unlock(&pdata->lock);
122e4664c0eSWu Hao 
123e4664c0eSWu Hao 	return ret;
124e4664c0eSWu Hao }
125e4664c0eSWu Hao 
12647c1b19cSWu Hao static int port_get_id(struct platform_device *pdev)
12747c1b19cSWu Hao {
12847c1b19cSWu Hao 	void __iomem *base;
12947c1b19cSWu Hao 
13047c1b19cSWu Hao 	base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
13147c1b19cSWu Hao 
13247c1b19cSWu Hao 	return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + PORT_HDR_CAP));
13347c1b19cSWu Hao }
13447c1b19cSWu Hao 
135e4664c0eSWu Hao static ssize_t
136e4664c0eSWu Hao id_show(struct device *dev, struct device_attribute *attr, char *buf)
137e4664c0eSWu Hao {
138e4664c0eSWu Hao 	int id = port_get_id(to_platform_device(dev));
139e4664c0eSWu Hao 
140e4664c0eSWu Hao 	return scnprintf(buf, PAGE_SIZE, "%d\n", id);
141e4664c0eSWu Hao }
142e4664c0eSWu Hao static DEVICE_ATTR_RO(id);
143e4664c0eSWu Hao 
144d2ad5ac1SWu Hao static ssize_t
145d2ad5ac1SWu Hao ltr_show(struct device *dev, struct device_attribute *attr, char *buf)
146d2ad5ac1SWu Hao {
147d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
148d2ad5ac1SWu Hao 	void __iomem *base;
149d2ad5ac1SWu Hao 	u64 v;
150d2ad5ac1SWu Hao 
151d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
152d2ad5ac1SWu Hao 
153d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
154d2ad5ac1SWu Hao 	v = readq(base + PORT_HDR_CTRL);
155d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
156d2ad5ac1SWu Hao 
157d2ad5ac1SWu Hao 	return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_CTRL_LATENCY, v));
158d2ad5ac1SWu Hao }
159d2ad5ac1SWu Hao 
160d2ad5ac1SWu Hao static ssize_t
161d2ad5ac1SWu Hao ltr_store(struct device *dev, struct device_attribute *attr,
162d2ad5ac1SWu Hao 	  const char *buf, size_t count)
163d2ad5ac1SWu Hao {
164d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
165d2ad5ac1SWu Hao 	void __iomem *base;
166d2ad5ac1SWu Hao 	bool ltr;
167d2ad5ac1SWu Hao 	u64 v;
168d2ad5ac1SWu Hao 
169d2ad5ac1SWu Hao 	if (kstrtobool(buf, &ltr))
170d2ad5ac1SWu Hao 		return -EINVAL;
171d2ad5ac1SWu Hao 
172d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
173d2ad5ac1SWu Hao 
174d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
175d2ad5ac1SWu Hao 	v = readq(base + PORT_HDR_CTRL);
176d2ad5ac1SWu Hao 	v &= ~PORT_CTRL_LATENCY;
177d2ad5ac1SWu Hao 	v |= FIELD_PREP(PORT_CTRL_LATENCY, ltr ? 1 : 0);
178d2ad5ac1SWu Hao 	writeq(v, base + PORT_HDR_CTRL);
179d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
180d2ad5ac1SWu Hao 
181d2ad5ac1SWu Hao 	return count;
182d2ad5ac1SWu Hao }
183d2ad5ac1SWu Hao static DEVICE_ATTR_RW(ltr);
184d2ad5ac1SWu Hao 
185d2ad5ac1SWu Hao static ssize_t
186d2ad5ac1SWu Hao ap1_event_show(struct device *dev, struct device_attribute *attr, char *buf)
187d2ad5ac1SWu Hao {
188d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
189d2ad5ac1SWu Hao 	void __iomem *base;
190d2ad5ac1SWu Hao 	u64 v;
191d2ad5ac1SWu Hao 
192d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
193d2ad5ac1SWu Hao 
194d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
195d2ad5ac1SWu Hao 	v = readq(base + PORT_HDR_STS);
196d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
197d2ad5ac1SWu Hao 
198d2ad5ac1SWu Hao 	return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP1_EVT, v));
199d2ad5ac1SWu Hao }
200d2ad5ac1SWu Hao 
201d2ad5ac1SWu Hao static ssize_t
202d2ad5ac1SWu Hao ap1_event_store(struct device *dev, struct device_attribute *attr,
203d2ad5ac1SWu Hao 		const char *buf, size_t count)
204d2ad5ac1SWu Hao {
205d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
206d2ad5ac1SWu Hao 	void __iomem *base;
207d2ad5ac1SWu Hao 	bool clear;
208d2ad5ac1SWu Hao 
209d2ad5ac1SWu Hao 	if (kstrtobool(buf, &clear) || !clear)
210d2ad5ac1SWu Hao 		return -EINVAL;
211d2ad5ac1SWu Hao 
212d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
213d2ad5ac1SWu Hao 
214d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
215d2ad5ac1SWu Hao 	writeq(PORT_STS_AP1_EVT, base + PORT_HDR_STS);
216d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
217d2ad5ac1SWu Hao 
218d2ad5ac1SWu Hao 	return count;
219d2ad5ac1SWu Hao }
220d2ad5ac1SWu Hao static DEVICE_ATTR_RW(ap1_event);
221d2ad5ac1SWu Hao 
222d2ad5ac1SWu Hao static ssize_t
223d2ad5ac1SWu Hao ap2_event_show(struct device *dev, struct device_attribute *attr,
224d2ad5ac1SWu Hao 	       char *buf)
225d2ad5ac1SWu Hao {
226d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
227d2ad5ac1SWu Hao 	void __iomem *base;
228d2ad5ac1SWu Hao 	u64 v;
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 	v = readq(base + PORT_HDR_STS);
234d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
235d2ad5ac1SWu Hao 
236d2ad5ac1SWu Hao 	return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP2_EVT, v));
237d2ad5ac1SWu Hao }
238d2ad5ac1SWu Hao 
239d2ad5ac1SWu Hao static ssize_t
240d2ad5ac1SWu Hao ap2_event_store(struct device *dev, struct device_attribute *attr,
241d2ad5ac1SWu Hao 		const char *buf, size_t count)
242d2ad5ac1SWu Hao {
243d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
244d2ad5ac1SWu Hao 	void __iomem *base;
245d2ad5ac1SWu Hao 	bool clear;
246d2ad5ac1SWu Hao 
247d2ad5ac1SWu Hao 	if (kstrtobool(buf, &clear) || !clear)
248d2ad5ac1SWu Hao 		return -EINVAL;
249d2ad5ac1SWu Hao 
250d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
251d2ad5ac1SWu Hao 
252d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
253d2ad5ac1SWu Hao 	writeq(PORT_STS_AP2_EVT, base + PORT_HDR_STS);
254d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
255d2ad5ac1SWu Hao 
256d2ad5ac1SWu Hao 	return count;
257d2ad5ac1SWu Hao }
258d2ad5ac1SWu Hao static DEVICE_ATTR_RW(ap2_event);
259d2ad5ac1SWu Hao 
260d2ad5ac1SWu Hao static ssize_t
261d2ad5ac1SWu Hao power_state_show(struct device *dev, struct device_attribute *attr, char *buf)
262d2ad5ac1SWu Hao {
263d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
264d2ad5ac1SWu Hao 	void __iomem *base;
265d2ad5ac1SWu Hao 	u64 v;
266d2ad5ac1SWu Hao 
267d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
268d2ad5ac1SWu Hao 
269d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
270d2ad5ac1SWu Hao 	v = readq(base + PORT_HDR_STS);
271d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
272d2ad5ac1SWu Hao 
273d2ad5ac1SWu Hao 	return sprintf(buf, "0x%x\n", (u8)FIELD_GET(PORT_STS_PWR_STATE, v));
274d2ad5ac1SWu Hao }
275d2ad5ac1SWu Hao static DEVICE_ATTR_RO(power_state);
276d2ad5ac1SWu Hao 
277*f09991adSWu Hao static ssize_t
278*f09991adSWu Hao userclk_freqcmd_store(struct device *dev, struct device_attribute *attr,
279*f09991adSWu Hao 		      const char *buf, size_t count)
280*f09991adSWu Hao {
281*f09991adSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
282*f09991adSWu Hao 	u64 userclk_freq_cmd;
283*f09991adSWu Hao 	void __iomem *base;
284*f09991adSWu Hao 
285*f09991adSWu Hao 	if (kstrtou64(buf, 0, &userclk_freq_cmd))
286*f09991adSWu Hao 		return -EINVAL;
287*f09991adSWu Hao 
288*f09991adSWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
289*f09991adSWu Hao 
290*f09991adSWu Hao 	mutex_lock(&pdata->lock);
291*f09991adSWu Hao 	writeq(userclk_freq_cmd, base + PORT_HDR_USRCLK_CMD0);
292*f09991adSWu Hao 	mutex_unlock(&pdata->lock);
293*f09991adSWu Hao 
294*f09991adSWu Hao 	return count;
295*f09991adSWu Hao }
296*f09991adSWu Hao static DEVICE_ATTR_WO(userclk_freqcmd);
297*f09991adSWu Hao 
298*f09991adSWu Hao static ssize_t
299*f09991adSWu Hao userclk_freqcntrcmd_store(struct device *dev, struct device_attribute *attr,
300*f09991adSWu Hao 			  const char *buf, size_t count)
301*f09991adSWu Hao {
302*f09991adSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
303*f09991adSWu Hao 	u64 userclk_freqcntr_cmd;
304*f09991adSWu Hao 	void __iomem *base;
305*f09991adSWu Hao 
306*f09991adSWu Hao 	if (kstrtou64(buf, 0, &userclk_freqcntr_cmd))
307*f09991adSWu Hao 		return -EINVAL;
308*f09991adSWu Hao 
309*f09991adSWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
310*f09991adSWu Hao 
311*f09991adSWu Hao 	mutex_lock(&pdata->lock);
312*f09991adSWu Hao 	writeq(userclk_freqcntr_cmd, base + PORT_HDR_USRCLK_CMD1);
313*f09991adSWu Hao 	mutex_unlock(&pdata->lock);
314*f09991adSWu Hao 
315*f09991adSWu Hao 	return count;
316*f09991adSWu Hao }
317*f09991adSWu Hao static DEVICE_ATTR_WO(userclk_freqcntrcmd);
318*f09991adSWu Hao 
319*f09991adSWu Hao static ssize_t
320*f09991adSWu Hao userclk_freqsts_show(struct device *dev, struct device_attribute *attr,
321*f09991adSWu Hao 		     char *buf)
322*f09991adSWu Hao {
323*f09991adSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
324*f09991adSWu Hao 	u64 userclk_freqsts;
325*f09991adSWu Hao 	void __iomem *base;
326*f09991adSWu Hao 
327*f09991adSWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
328*f09991adSWu Hao 
329*f09991adSWu Hao 	mutex_lock(&pdata->lock);
330*f09991adSWu Hao 	userclk_freqsts = readq(base + PORT_HDR_USRCLK_STS0);
331*f09991adSWu Hao 	mutex_unlock(&pdata->lock);
332*f09991adSWu Hao 
333*f09991adSWu Hao 	return sprintf(buf, "0x%llx\n", (unsigned long long)userclk_freqsts);
334*f09991adSWu Hao }
335*f09991adSWu Hao static DEVICE_ATTR_RO(userclk_freqsts);
336*f09991adSWu Hao 
337*f09991adSWu Hao static ssize_t
338*f09991adSWu Hao userclk_freqcntrsts_show(struct device *dev, struct device_attribute *attr,
339*f09991adSWu Hao 			 char *buf)
340*f09991adSWu Hao {
341*f09991adSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
342*f09991adSWu Hao 	u64 userclk_freqcntrsts;
343*f09991adSWu Hao 	void __iomem *base;
344*f09991adSWu Hao 
345*f09991adSWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
346*f09991adSWu Hao 
347*f09991adSWu Hao 	mutex_lock(&pdata->lock);
348*f09991adSWu Hao 	userclk_freqcntrsts = readq(base + PORT_HDR_USRCLK_STS1);
349*f09991adSWu Hao 	mutex_unlock(&pdata->lock);
350*f09991adSWu Hao 
351*f09991adSWu Hao 	return sprintf(buf, "0x%llx\n",
352*f09991adSWu Hao 		       (unsigned long long)userclk_freqcntrsts);
353*f09991adSWu Hao }
354*f09991adSWu Hao static DEVICE_ATTR_RO(userclk_freqcntrsts);
355*f09991adSWu Hao 
356dcfecd4dSGreg Kroah-Hartman static struct attribute *port_hdr_attrs[] = {
357e4664c0eSWu Hao 	&dev_attr_id.attr,
358d2ad5ac1SWu Hao 	&dev_attr_ltr.attr,
359d2ad5ac1SWu Hao 	&dev_attr_ap1_event.attr,
360d2ad5ac1SWu Hao 	&dev_attr_ap2_event.attr,
361d2ad5ac1SWu Hao 	&dev_attr_power_state.attr,
362*f09991adSWu Hao 	&dev_attr_userclk_freqcmd.attr,
363*f09991adSWu Hao 	&dev_attr_userclk_freqcntrcmd.attr,
364*f09991adSWu Hao 	&dev_attr_userclk_freqsts.attr,
365*f09991adSWu Hao 	&dev_attr_userclk_freqcntrsts.attr,
366e4664c0eSWu Hao 	NULL,
367e4664c0eSWu Hao };
368a80a4b82SWu Hao 
369*f09991adSWu Hao static umode_t port_hdr_attrs_visible(struct kobject *kobj,
370*f09991adSWu Hao 				      struct attribute *attr, int n)
371*f09991adSWu Hao {
372*f09991adSWu Hao 	struct device *dev = kobj_to_dev(kobj);
373*f09991adSWu Hao 	umode_t mode = attr->mode;
374*f09991adSWu Hao 	void __iomem *base;
375*f09991adSWu Hao 
376*f09991adSWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
377*f09991adSWu Hao 
378*f09991adSWu Hao 	if (dfl_feature_revision(base) > 0) {
379*f09991adSWu Hao 		/*
380*f09991adSWu Hao 		 * userclk sysfs interfaces are only visible in case port
381*f09991adSWu Hao 		 * revision is 0, as hardware with revision >0 doesn't
382*f09991adSWu Hao 		 * support this.
383*f09991adSWu Hao 		 */
384*f09991adSWu Hao 		if (attr == &dev_attr_userclk_freqcmd.attr ||
385*f09991adSWu Hao 		    attr == &dev_attr_userclk_freqcntrcmd.attr ||
386*f09991adSWu Hao 		    attr == &dev_attr_userclk_freqsts.attr ||
387*f09991adSWu Hao 		    attr == &dev_attr_userclk_freqcntrsts.attr)
388*f09991adSWu Hao 			mode = 0;
389*f09991adSWu Hao 	}
390*f09991adSWu Hao 
391*f09991adSWu Hao 	return mode;
392*f09991adSWu Hao }
393*f09991adSWu Hao 
394a80a4b82SWu Hao static const struct attribute_group port_hdr_group = {
395a80a4b82SWu Hao 	.attrs      = port_hdr_attrs,
396*f09991adSWu Hao 	.is_visible = port_hdr_attrs_visible,
397a80a4b82SWu Hao };
398e4664c0eSWu Hao 
3991a1527cfSWu Hao static int port_hdr_init(struct platform_device *pdev,
4001a1527cfSWu Hao 			 struct dfl_feature *feature)
4011a1527cfSWu Hao {
402e4664c0eSWu Hao 	port_reset(pdev);
403e4664c0eSWu Hao 
404a80a4b82SWu Hao 	return 0;
405e4664c0eSWu Hao }
406e4664c0eSWu Hao 
407e4664c0eSWu Hao static long
408e4664c0eSWu Hao port_hdr_ioctl(struct platform_device *pdev, struct dfl_feature *feature,
409e4664c0eSWu Hao 	       unsigned int cmd, unsigned long arg)
410e4664c0eSWu Hao {
411e4664c0eSWu Hao 	long ret;
412e4664c0eSWu Hao 
413e4664c0eSWu Hao 	switch (cmd) {
414e4664c0eSWu Hao 	case DFL_FPGA_PORT_RESET:
415e4664c0eSWu Hao 		if (!arg)
416e4664c0eSWu Hao 			ret = port_reset(pdev);
417e4664c0eSWu Hao 		else
418e4664c0eSWu Hao 			ret = -EINVAL;
419e4664c0eSWu Hao 		break;
420e4664c0eSWu Hao 	default:
421e4664c0eSWu Hao 		dev_dbg(&pdev->dev, "%x cmd not handled", cmd);
422e4664c0eSWu Hao 		ret = -ENODEV;
423e4664c0eSWu Hao 	}
424e4664c0eSWu Hao 
425e4664c0eSWu Hao 	return ret;
4261a1527cfSWu Hao }
4271a1527cfSWu Hao 
42815bbb300SWu Hao static const struct dfl_feature_id port_hdr_id_table[] = {
42915bbb300SWu Hao 	{.id = PORT_FEATURE_ID_HEADER,},
43015bbb300SWu Hao 	{0,}
43115bbb300SWu Hao };
43215bbb300SWu Hao 
4331a1527cfSWu Hao static const struct dfl_feature_ops port_hdr_ops = {
4341a1527cfSWu Hao 	.init = port_hdr_init,
435e4664c0eSWu Hao 	.ioctl = port_hdr_ioctl,
4361a1527cfSWu Hao };
4371a1527cfSWu Hao 
438857a2622SXiao Guangrong static ssize_t
439857a2622SXiao Guangrong afu_id_show(struct device *dev, struct device_attribute *attr, char *buf)
440857a2622SXiao Guangrong {
441857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
442857a2622SXiao Guangrong 	void __iomem *base;
443857a2622SXiao Guangrong 	u64 guidl, guidh;
444857a2622SXiao Guangrong 
445857a2622SXiao Guangrong 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_AFU);
446857a2622SXiao Guangrong 
447857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
448857a2622SXiao Guangrong 	if (pdata->disable_count) {
449857a2622SXiao Guangrong 		mutex_unlock(&pdata->lock);
450857a2622SXiao Guangrong 		return -EBUSY;
451857a2622SXiao Guangrong 	}
452857a2622SXiao Guangrong 
453857a2622SXiao Guangrong 	guidl = readq(base + GUID_L);
454857a2622SXiao Guangrong 	guidh = readq(base + GUID_H);
455857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
456857a2622SXiao Guangrong 
457857a2622SXiao Guangrong 	return scnprintf(buf, PAGE_SIZE, "%016llx%016llx\n", guidh, guidl);
458857a2622SXiao Guangrong }
459857a2622SXiao Guangrong static DEVICE_ATTR_RO(afu_id);
460857a2622SXiao Guangrong 
461dcfecd4dSGreg Kroah-Hartman static struct attribute *port_afu_attrs[] = {
462857a2622SXiao Guangrong 	&dev_attr_afu_id.attr,
463857a2622SXiao Guangrong 	NULL
464857a2622SXiao Guangrong };
465a80a4b82SWu Hao 
466a80a4b82SWu Hao static umode_t port_afu_attrs_visible(struct kobject *kobj,
467a80a4b82SWu Hao 				      struct attribute *attr, int n)
468a80a4b82SWu Hao {
469a80a4b82SWu Hao 	struct device *dev = kobj_to_dev(kobj);
470a80a4b82SWu Hao 
471a80a4b82SWu Hao 	/*
472a80a4b82SWu Hao 	 * sysfs entries are visible only if related private feature is
473a80a4b82SWu Hao 	 * enumerated.
474a80a4b82SWu Hao 	 */
475a80a4b82SWu Hao 	if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_AFU))
476a80a4b82SWu Hao 		return 0;
477a80a4b82SWu Hao 
478a80a4b82SWu Hao 	return attr->mode;
479a80a4b82SWu Hao }
480a80a4b82SWu Hao 
481a80a4b82SWu Hao static const struct attribute_group port_afu_group = {
482a80a4b82SWu Hao 	.attrs      = port_afu_attrs,
483a80a4b82SWu Hao 	.is_visible = port_afu_attrs_visible,
484a80a4b82SWu Hao };
485857a2622SXiao Guangrong 
486857a2622SXiao Guangrong static int port_afu_init(struct platform_device *pdev,
487857a2622SXiao Guangrong 			 struct dfl_feature *feature)
488857a2622SXiao Guangrong {
489857a2622SXiao Guangrong 	struct resource *res = &pdev->resource[feature->resource_index];
490857a2622SXiao Guangrong 
491a80a4b82SWu Hao 	return afu_mmio_region_add(dev_get_platdata(&pdev->dev),
492a80a4b82SWu Hao 				   DFL_PORT_REGION_INDEX_AFU,
493a80a4b82SWu Hao 				   resource_size(res), res->start,
494a80a4b82SWu Hao 				   DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ |
495a80a4b82SWu Hao 				   DFL_PORT_REGION_WRITE);
496857a2622SXiao Guangrong }
497857a2622SXiao Guangrong 
49815bbb300SWu Hao static const struct dfl_feature_id port_afu_id_table[] = {
49915bbb300SWu Hao 	{.id = PORT_FEATURE_ID_AFU,},
50015bbb300SWu Hao 	{0,}
50115bbb300SWu Hao };
50215bbb300SWu Hao 
503857a2622SXiao Guangrong static const struct dfl_feature_ops port_afu_ops = {
504857a2622SXiao Guangrong 	.init = port_afu_init,
505857a2622SXiao Guangrong };
506857a2622SXiao Guangrong 
5071a1527cfSWu Hao static struct dfl_feature_driver port_feature_drvs[] = {
5081a1527cfSWu Hao 	{
50915bbb300SWu Hao 		.id_table = port_hdr_id_table,
5101a1527cfSWu Hao 		.ops = &port_hdr_ops,
5111a1527cfSWu Hao 	},
5121a1527cfSWu Hao 	{
51315bbb300SWu Hao 		.id_table = port_afu_id_table,
514857a2622SXiao Guangrong 		.ops = &port_afu_ops,
515857a2622SXiao Guangrong 	},
516857a2622SXiao Guangrong 	{
5171a1527cfSWu Hao 		.ops = NULL,
5181a1527cfSWu Hao 	}
5191a1527cfSWu Hao };
5201a1527cfSWu Hao 
5211a1527cfSWu Hao static int afu_open(struct inode *inode, struct file *filp)
5221a1527cfSWu Hao {
5231a1527cfSWu Hao 	struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
5241a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
5251a1527cfSWu Hao 	int ret;
5261a1527cfSWu Hao 
5271a1527cfSWu Hao 	pdata = dev_get_platdata(&fdev->dev);
5281a1527cfSWu Hao 	if (WARN_ON(!pdata))
5291a1527cfSWu Hao 		return -ENODEV;
5301a1527cfSWu Hao 
5311a1527cfSWu Hao 	ret = dfl_feature_dev_use_begin(pdata);
5321a1527cfSWu Hao 	if (ret)
5331a1527cfSWu Hao 		return ret;
5341a1527cfSWu Hao 
5351a1527cfSWu Hao 	dev_dbg(&fdev->dev, "Device File Open\n");
5361a1527cfSWu Hao 	filp->private_data = fdev;
5371a1527cfSWu Hao 
5381a1527cfSWu Hao 	return 0;
5391a1527cfSWu Hao }
5401a1527cfSWu Hao 
5411a1527cfSWu Hao static int afu_release(struct inode *inode, struct file *filp)
5421a1527cfSWu Hao {
5431a1527cfSWu Hao 	struct platform_device *pdev = filp->private_data;
5441a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
5451a1527cfSWu Hao 
5461a1527cfSWu Hao 	dev_dbg(&pdev->dev, "Device File Release\n");
5471a1527cfSWu Hao 
5481a1527cfSWu Hao 	pdata = dev_get_platdata(&pdev->dev);
5491a1527cfSWu Hao 
550fa8dda1eSWu Hao 	mutex_lock(&pdata->lock);
551fa8dda1eSWu Hao 	__port_reset(pdev);
552fa8dda1eSWu Hao 	afu_dma_region_destroy(pdata);
553fa8dda1eSWu Hao 	mutex_unlock(&pdata->lock);
554fa8dda1eSWu Hao 
5551a1527cfSWu Hao 	dfl_feature_dev_use_end(pdata);
5561a1527cfSWu Hao 
5571a1527cfSWu Hao 	return 0;
5581a1527cfSWu Hao }
5591a1527cfSWu Hao 
5606fd893c4SWu Hao static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
5616fd893c4SWu Hao 				      unsigned long arg)
5626fd893c4SWu Hao {
5636fd893c4SWu Hao 	/* No extension support for now */
5646fd893c4SWu Hao 	return 0;
5656fd893c4SWu Hao }
5666fd893c4SWu Hao 
567857a2622SXiao Guangrong static long
568857a2622SXiao Guangrong afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg)
569857a2622SXiao Guangrong {
570857a2622SXiao Guangrong 	struct dfl_fpga_port_info info;
571857a2622SXiao Guangrong 	struct dfl_afu *afu;
572857a2622SXiao Guangrong 	unsigned long minsz;
573857a2622SXiao Guangrong 
574857a2622SXiao Guangrong 	minsz = offsetofend(struct dfl_fpga_port_info, num_umsgs);
575857a2622SXiao Guangrong 
576857a2622SXiao Guangrong 	if (copy_from_user(&info, arg, minsz))
577857a2622SXiao Guangrong 		return -EFAULT;
578857a2622SXiao Guangrong 
579857a2622SXiao Guangrong 	if (info.argsz < minsz)
580857a2622SXiao Guangrong 		return -EINVAL;
581857a2622SXiao Guangrong 
582857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
583857a2622SXiao Guangrong 	afu = dfl_fpga_pdata_get_private(pdata);
584857a2622SXiao Guangrong 	info.flags = 0;
585857a2622SXiao Guangrong 	info.num_regions = afu->num_regions;
586857a2622SXiao Guangrong 	info.num_umsgs = afu->num_umsgs;
587857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
588857a2622SXiao Guangrong 
589857a2622SXiao Guangrong 	if (copy_to_user(arg, &info, sizeof(info)))
590857a2622SXiao Guangrong 		return -EFAULT;
591857a2622SXiao Guangrong 
592857a2622SXiao Guangrong 	return 0;
593857a2622SXiao Guangrong }
594857a2622SXiao Guangrong 
595857a2622SXiao Guangrong static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata,
596857a2622SXiao Guangrong 				      void __user *arg)
597857a2622SXiao Guangrong {
598857a2622SXiao Guangrong 	struct dfl_fpga_port_region_info rinfo;
599857a2622SXiao Guangrong 	struct dfl_afu_mmio_region region;
600857a2622SXiao Guangrong 	unsigned long minsz;
601857a2622SXiao Guangrong 	long ret;
602857a2622SXiao Guangrong 
603857a2622SXiao Guangrong 	minsz = offsetofend(struct dfl_fpga_port_region_info, offset);
604857a2622SXiao Guangrong 
605857a2622SXiao Guangrong 	if (copy_from_user(&rinfo, arg, minsz))
606857a2622SXiao Guangrong 		return -EFAULT;
607857a2622SXiao Guangrong 
608857a2622SXiao Guangrong 	if (rinfo.argsz < minsz || rinfo.padding)
609857a2622SXiao Guangrong 		return -EINVAL;
610857a2622SXiao Guangrong 
611857a2622SXiao Guangrong 	ret = afu_mmio_region_get_by_index(pdata, rinfo.index, &region);
612857a2622SXiao Guangrong 	if (ret)
613857a2622SXiao Guangrong 		return ret;
614857a2622SXiao Guangrong 
615857a2622SXiao Guangrong 	rinfo.flags = region.flags;
616857a2622SXiao Guangrong 	rinfo.size = region.size;
617857a2622SXiao Guangrong 	rinfo.offset = region.offset;
618857a2622SXiao Guangrong 
619857a2622SXiao Guangrong 	if (copy_to_user(arg, &rinfo, sizeof(rinfo)))
620857a2622SXiao Guangrong 		return -EFAULT;
621857a2622SXiao Guangrong 
622857a2622SXiao Guangrong 	return 0;
623857a2622SXiao Guangrong }
624857a2622SXiao Guangrong 
625fa8dda1eSWu Hao static long
626fa8dda1eSWu Hao afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg)
627fa8dda1eSWu Hao {
628fa8dda1eSWu Hao 	struct dfl_fpga_port_dma_map map;
629fa8dda1eSWu Hao 	unsigned long minsz;
630fa8dda1eSWu Hao 	long ret;
631fa8dda1eSWu Hao 
632fa8dda1eSWu Hao 	minsz = offsetofend(struct dfl_fpga_port_dma_map, iova);
633fa8dda1eSWu Hao 
634fa8dda1eSWu Hao 	if (copy_from_user(&map, arg, minsz))
635fa8dda1eSWu Hao 		return -EFAULT;
636fa8dda1eSWu Hao 
637fa8dda1eSWu Hao 	if (map.argsz < minsz || map.flags)
638fa8dda1eSWu Hao 		return -EINVAL;
639fa8dda1eSWu Hao 
640fa8dda1eSWu Hao 	ret = afu_dma_map_region(pdata, map.user_addr, map.length, &map.iova);
641fa8dda1eSWu Hao 	if (ret)
642fa8dda1eSWu Hao 		return ret;
643fa8dda1eSWu Hao 
644fa8dda1eSWu Hao 	if (copy_to_user(arg, &map, sizeof(map))) {
645fa8dda1eSWu Hao 		afu_dma_unmap_region(pdata, map.iova);
646fa8dda1eSWu Hao 		return -EFAULT;
647fa8dda1eSWu Hao 	}
648fa8dda1eSWu Hao 
649fa8dda1eSWu Hao 	dev_dbg(&pdata->dev->dev, "dma map: ua=%llx, len=%llx, iova=%llx\n",
650fa8dda1eSWu Hao 		(unsigned long long)map.user_addr,
651fa8dda1eSWu Hao 		(unsigned long long)map.length,
652fa8dda1eSWu Hao 		(unsigned long long)map.iova);
653fa8dda1eSWu Hao 
654fa8dda1eSWu Hao 	return 0;
655fa8dda1eSWu Hao }
656fa8dda1eSWu Hao 
657fa8dda1eSWu Hao static long
658fa8dda1eSWu Hao afu_ioctl_dma_unmap(struct dfl_feature_platform_data *pdata, void __user *arg)
659fa8dda1eSWu Hao {
660fa8dda1eSWu Hao 	struct dfl_fpga_port_dma_unmap unmap;
661fa8dda1eSWu Hao 	unsigned long minsz;
662fa8dda1eSWu Hao 
663fa8dda1eSWu Hao 	minsz = offsetofend(struct dfl_fpga_port_dma_unmap, iova);
664fa8dda1eSWu Hao 
665fa8dda1eSWu Hao 	if (copy_from_user(&unmap, arg, minsz))
666fa8dda1eSWu Hao 		return -EFAULT;
667fa8dda1eSWu Hao 
668fa8dda1eSWu Hao 	if (unmap.argsz < minsz || unmap.flags)
669fa8dda1eSWu Hao 		return -EINVAL;
670fa8dda1eSWu Hao 
671fa8dda1eSWu Hao 	return afu_dma_unmap_region(pdata, unmap.iova);
672fa8dda1eSWu Hao }
673fa8dda1eSWu Hao 
6741a1527cfSWu Hao static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
6751a1527cfSWu Hao {
6761a1527cfSWu Hao 	struct platform_device *pdev = filp->private_data;
6771a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
6781a1527cfSWu Hao 	struct dfl_feature *f;
6791a1527cfSWu Hao 	long ret;
6801a1527cfSWu Hao 
6811a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd);
6821a1527cfSWu Hao 
6831a1527cfSWu Hao 	pdata = dev_get_platdata(&pdev->dev);
6841a1527cfSWu Hao 
6851a1527cfSWu Hao 	switch (cmd) {
6866fd893c4SWu Hao 	case DFL_FPGA_GET_API_VERSION:
6876fd893c4SWu Hao 		return DFL_FPGA_API_VERSION;
6886fd893c4SWu Hao 	case DFL_FPGA_CHECK_EXTENSION:
6896fd893c4SWu Hao 		return afu_ioctl_check_extension(pdata, arg);
690857a2622SXiao Guangrong 	case DFL_FPGA_PORT_GET_INFO:
691857a2622SXiao Guangrong 		return afu_ioctl_get_info(pdata, (void __user *)arg);
692857a2622SXiao Guangrong 	case DFL_FPGA_PORT_GET_REGION_INFO:
693857a2622SXiao Guangrong 		return afu_ioctl_get_region_info(pdata, (void __user *)arg);
694fa8dda1eSWu Hao 	case DFL_FPGA_PORT_DMA_MAP:
695fa8dda1eSWu Hao 		return afu_ioctl_dma_map(pdata, (void __user *)arg);
696fa8dda1eSWu Hao 	case DFL_FPGA_PORT_DMA_UNMAP:
697fa8dda1eSWu Hao 		return afu_ioctl_dma_unmap(pdata, (void __user *)arg);
6981a1527cfSWu Hao 	default:
6991a1527cfSWu Hao 		/*
7001a1527cfSWu Hao 		 * Let sub-feature's ioctl function to handle the cmd
7011a1527cfSWu Hao 		 * Sub-feature's ioctl returns -ENODEV when cmd is not
7021a1527cfSWu Hao 		 * handled in this sub feature, and returns 0 and other
7031a1527cfSWu Hao 		 * error code if cmd is handled.
7041a1527cfSWu Hao 		 */
7051a1527cfSWu Hao 		dfl_fpga_dev_for_each_feature(pdata, f)
7061a1527cfSWu Hao 			if (f->ops && f->ops->ioctl) {
7071a1527cfSWu Hao 				ret = f->ops->ioctl(pdev, f, cmd, arg);
7081a1527cfSWu Hao 				if (ret != -ENODEV)
7091a1527cfSWu Hao 					return ret;
7101a1527cfSWu Hao 			}
7111a1527cfSWu Hao 	}
7121a1527cfSWu Hao 
7131a1527cfSWu Hao 	return -EINVAL;
7141a1527cfSWu Hao }
7151a1527cfSWu Hao 
716857a2622SXiao Guangrong static int afu_mmap(struct file *filp, struct vm_area_struct *vma)
717857a2622SXiao Guangrong {
718857a2622SXiao Guangrong 	struct platform_device *pdev = filp->private_data;
719857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata;
720857a2622SXiao Guangrong 	u64 size = vma->vm_end - vma->vm_start;
721857a2622SXiao Guangrong 	struct dfl_afu_mmio_region region;
722857a2622SXiao Guangrong 	u64 offset;
723857a2622SXiao Guangrong 	int ret;
724857a2622SXiao Guangrong 
725857a2622SXiao Guangrong 	if (!(vma->vm_flags & VM_SHARED))
726857a2622SXiao Guangrong 		return -EINVAL;
727857a2622SXiao Guangrong 
728857a2622SXiao Guangrong 	pdata = dev_get_platdata(&pdev->dev);
729857a2622SXiao Guangrong 
730857a2622SXiao Guangrong 	offset = vma->vm_pgoff << PAGE_SHIFT;
731857a2622SXiao Guangrong 	ret = afu_mmio_region_get_by_offset(pdata, offset, size, &region);
732857a2622SXiao Guangrong 	if (ret)
733857a2622SXiao Guangrong 		return ret;
734857a2622SXiao Guangrong 
735857a2622SXiao Guangrong 	if (!(region.flags & DFL_PORT_REGION_MMAP))
736857a2622SXiao Guangrong 		return -EINVAL;
737857a2622SXiao Guangrong 
738857a2622SXiao Guangrong 	if ((vma->vm_flags & VM_READ) && !(region.flags & DFL_PORT_REGION_READ))
739857a2622SXiao Guangrong 		return -EPERM;
740857a2622SXiao Guangrong 
741857a2622SXiao Guangrong 	if ((vma->vm_flags & VM_WRITE) &&
742857a2622SXiao Guangrong 	    !(region.flags & DFL_PORT_REGION_WRITE))
743857a2622SXiao Guangrong 		return -EPERM;
744857a2622SXiao Guangrong 
745857a2622SXiao Guangrong 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
746857a2622SXiao Guangrong 
747857a2622SXiao Guangrong 	return remap_pfn_range(vma, vma->vm_start,
748857a2622SXiao Guangrong 			(region.phys + (offset - region.offset)) >> PAGE_SHIFT,
749857a2622SXiao Guangrong 			size, vma->vm_page_prot);
750857a2622SXiao Guangrong }
751857a2622SXiao Guangrong 
7521a1527cfSWu Hao static const struct file_operations afu_fops = {
7531a1527cfSWu Hao 	.owner = THIS_MODULE,
7541a1527cfSWu Hao 	.open = afu_open,
7551a1527cfSWu Hao 	.release = afu_release,
7561a1527cfSWu Hao 	.unlocked_ioctl = afu_ioctl,
757857a2622SXiao Guangrong 	.mmap = afu_mmap,
7581a1527cfSWu Hao };
7591a1527cfSWu Hao 
760857a2622SXiao Guangrong static int afu_dev_init(struct platform_device *pdev)
761857a2622SXiao Guangrong {
762857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
763857a2622SXiao Guangrong 	struct dfl_afu *afu;
764857a2622SXiao Guangrong 
765857a2622SXiao Guangrong 	afu = devm_kzalloc(&pdev->dev, sizeof(*afu), GFP_KERNEL);
766857a2622SXiao Guangrong 	if (!afu)
767857a2622SXiao Guangrong 		return -ENOMEM;
768857a2622SXiao Guangrong 
769857a2622SXiao Guangrong 	afu->pdata = pdata;
770857a2622SXiao Guangrong 
771857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
772857a2622SXiao Guangrong 	dfl_fpga_pdata_set_private(pdata, afu);
773857a2622SXiao Guangrong 	afu_mmio_region_init(pdata);
774fa8dda1eSWu Hao 	afu_dma_region_init(pdata);
775857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
776857a2622SXiao Guangrong 
777857a2622SXiao Guangrong 	return 0;
778857a2622SXiao Guangrong }
779857a2622SXiao Guangrong 
780857a2622SXiao Guangrong static int afu_dev_destroy(struct platform_device *pdev)
781857a2622SXiao Guangrong {
782857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
783857a2622SXiao Guangrong 	struct dfl_afu *afu;
784857a2622SXiao Guangrong 
785857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
786857a2622SXiao Guangrong 	afu = dfl_fpga_pdata_get_private(pdata);
787857a2622SXiao Guangrong 	afu_mmio_region_destroy(pdata);
788fa8dda1eSWu Hao 	afu_dma_region_destroy(pdata);
789857a2622SXiao Guangrong 	dfl_fpga_pdata_set_private(pdata, NULL);
790857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
791857a2622SXiao Guangrong 
792857a2622SXiao Guangrong 	return 0;
793857a2622SXiao Guangrong }
794857a2622SXiao Guangrong 
79547c1b19cSWu Hao static int port_enable_set(struct platform_device *pdev, bool enable)
79647c1b19cSWu Hao {
79747c1b19cSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
79847c1b19cSWu Hao 	int ret = 0;
79947c1b19cSWu Hao 
80047c1b19cSWu Hao 	mutex_lock(&pdata->lock);
80147c1b19cSWu Hao 	if (enable)
80247c1b19cSWu Hao 		port_enable(pdev);
80347c1b19cSWu Hao 	else
80447c1b19cSWu Hao 		ret = port_disable(pdev);
80547c1b19cSWu Hao 	mutex_unlock(&pdata->lock);
80647c1b19cSWu Hao 
80747c1b19cSWu Hao 	return ret;
80847c1b19cSWu Hao }
80947c1b19cSWu Hao 
81047c1b19cSWu Hao static struct dfl_fpga_port_ops afu_port_ops = {
81147c1b19cSWu Hao 	.name = DFL_FPGA_FEATURE_DEV_PORT,
81247c1b19cSWu Hao 	.owner = THIS_MODULE,
81347c1b19cSWu Hao 	.get_id = port_get_id,
81447c1b19cSWu Hao 	.enable_set = port_enable_set,
81547c1b19cSWu Hao };
81647c1b19cSWu Hao 
8171a1527cfSWu Hao static int afu_probe(struct platform_device *pdev)
8181a1527cfSWu Hao {
8191a1527cfSWu Hao 	int ret;
8201a1527cfSWu Hao 
8211a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s\n", __func__);
8221a1527cfSWu Hao 
823857a2622SXiao Guangrong 	ret = afu_dev_init(pdev);
824857a2622SXiao Guangrong 	if (ret)
825857a2622SXiao Guangrong 		goto exit;
826857a2622SXiao Guangrong 
8271a1527cfSWu Hao 	ret = dfl_fpga_dev_feature_init(pdev, port_feature_drvs);
8281a1527cfSWu Hao 	if (ret)
829857a2622SXiao Guangrong 		goto dev_destroy;
8301a1527cfSWu Hao 
8311a1527cfSWu Hao 	ret = dfl_fpga_dev_ops_register(pdev, &afu_fops, THIS_MODULE);
832857a2622SXiao Guangrong 	if (ret) {
8331a1527cfSWu Hao 		dfl_fpga_dev_feature_uinit(pdev);
834857a2622SXiao Guangrong 		goto dev_destroy;
835857a2622SXiao Guangrong 	}
8361a1527cfSWu Hao 
837857a2622SXiao Guangrong 	return 0;
838857a2622SXiao Guangrong 
839857a2622SXiao Guangrong dev_destroy:
840857a2622SXiao Guangrong 	afu_dev_destroy(pdev);
841857a2622SXiao Guangrong exit:
8421a1527cfSWu Hao 	return ret;
8431a1527cfSWu Hao }
8441a1527cfSWu Hao 
8451a1527cfSWu Hao static int afu_remove(struct platform_device *pdev)
8461a1527cfSWu Hao {
8471a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s\n", __func__);
8481a1527cfSWu Hao 
8491a1527cfSWu Hao 	dfl_fpga_dev_ops_unregister(pdev);
8501a1527cfSWu Hao 	dfl_fpga_dev_feature_uinit(pdev);
851857a2622SXiao Guangrong 	afu_dev_destroy(pdev);
8521a1527cfSWu Hao 
8531a1527cfSWu Hao 	return 0;
8541a1527cfSWu Hao }
8551a1527cfSWu Hao 
856a80a4b82SWu Hao static const struct attribute_group *afu_dev_groups[] = {
857a80a4b82SWu Hao 	&port_hdr_group,
858a80a4b82SWu Hao 	&port_afu_group,
859a80a4b82SWu Hao 	NULL
860a80a4b82SWu Hao };
861a80a4b82SWu Hao 
8621a1527cfSWu Hao static struct platform_driver afu_driver = {
8631a1527cfSWu Hao 	.driver	= {
8641a1527cfSWu Hao 		.name	    = DFL_FPGA_FEATURE_DEV_PORT,
865a80a4b82SWu Hao 		.dev_groups = afu_dev_groups,
8661a1527cfSWu Hao 	},
8671a1527cfSWu Hao 	.probe   = afu_probe,
8681a1527cfSWu Hao 	.remove  = afu_remove,
8691a1527cfSWu Hao };
8701a1527cfSWu Hao 
87147c1b19cSWu Hao static int __init afu_init(void)
87247c1b19cSWu Hao {
87347c1b19cSWu Hao 	int ret;
87447c1b19cSWu Hao 
87547c1b19cSWu Hao 	dfl_fpga_port_ops_add(&afu_port_ops);
87647c1b19cSWu Hao 
87747c1b19cSWu Hao 	ret = platform_driver_register(&afu_driver);
87847c1b19cSWu Hao 	if (ret)
87947c1b19cSWu Hao 		dfl_fpga_port_ops_del(&afu_port_ops);
88047c1b19cSWu Hao 
88147c1b19cSWu Hao 	return ret;
88247c1b19cSWu Hao }
88347c1b19cSWu Hao 
88447c1b19cSWu Hao static void __exit afu_exit(void)
88547c1b19cSWu Hao {
88647c1b19cSWu Hao 	platform_driver_unregister(&afu_driver);
88747c1b19cSWu Hao 
88847c1b19cSWu Hao 	dfl_fpga_port_ops_del(&afu_port_ops);
88947c1b19cSWu Hao }
89047c1b19cSWu Hao 
89147c1b19cSWu Hao module_init(afu_init);
89247c1b19cSWu Hao module_exit(afu_exit);
8931a1527cfSWu Hao 
8941a1527cfSWu Hao MODULE_DESCRIPTION("FPGA Accelerated Function Unit driver");
8951a1527cfSWu Hao MODULE_AUTHOR("Intel Corporation");
8961a1527cfSWu Hao MODULE_LICENSE("GPL v2");
8971a1527cfSWu Hao MODULE_ALIAS("platform:dfl-port");
898