xref: /openbmc/linux/drivers/fpga/dfl-afu-main.c (revision 44d247534ff266404ccb44c2f52131a850348919)
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 /**
2595844372SWu Hao  * __afu_port_enable - enable a port by clear reset
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.
3095844372SWu Hao  * __afu_port_enable function should only be used after __afu_port_disable
3195844372SWu Hao  * function.
3295844372SWu Hao  *
3395844372SWu Hao  * The caller needs to hold lock for protection.
3447c1b19cSWu Hao  */
3595844372SWu Hao void __afu_port_enable(struct platform_device *pdev)
3647c1b19cSWu Hao {
3747c1b19cSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
3847c1b19cSWu Hao 	void __iomem *base;
3947c1b19cSWu Hao 	u64 v;
4047c1b19cSWu Hao 
4147c1b19cSWu Hao 	WARN_ON(!pdata->disable_count);
4247c1b19cSWu Hao 
4347c1b19cSWu Hao 	if (--pdata->disable_count != 0)
4447c1b19cSWu Hao 		return;
4547c1b19cSWu Hao 
4647c1b19cSWu Hao 	base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
4747c1b19cSWu Hao 
4847c1b19cSWu Hao 	/* Clear port soft reset */
4947c1b19cSWu Hao 	v = readq(base + PORT_HDR_CTRL);
5047c1b19cSWu Hao 	v &= ~PORT_CTRL_SFTRST;
5147c1b19cSWu Hao 	writeq(v, base + PORT_HDR_CTRL);
5247c1b19cSWu Hao }
5347c1b19cSWu Hao 
5447c1b19cSWu Hao #define RST_POLL_INVL 10 /* us */
5547c1b19cSWu Hao #define RST_POLL_TIMEOUT 1000 /* us */
5647c1b19cSWu Hao 
5747c1b19cSWu Hao /**
5895844372SWu Hao  * __afu_port_disable - disable a port by hold reset
5947c1b19cSWu Hao  * @pdev: port platform device.
6047c1b19cSWu Hao  *
6195844372SWu Hao  * Disable Port by setting the port soft reset bit, it puts the port into reset.
6295844372SWu Hao  *
6395844372SWu Hao  * The caller needs to hold lock for protection.
6447c1b19cSWu Hao  */
6595844372SWu Hao int __afu_port_disable(struct platform_device *pdev)
6647c1b19cSWu Hao {
6747c1b19cSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
6847c1b19cSWu Hao 	void __iomem *base;
6947c1b19cSWu Hao 	u64 v;
7047c1b19cSWu Hao 
7147c1b19cSWu Hao 	if (pdata->disable_count++ != 0)
7247c1b19cSWu Hao 		return 0;
7347c1b19cSWu Hao 
7447c1b19cSWu Hao 	base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
7547c1b19cSWu Hao 
7647c1b19cSWu Hao 	/* Set port soft reset */
7747c1b19cSWu Hao 	v = readq(base + PORT_HDR_CTRL);
7847c1b19cSWu Hao 	v |= PORT_CTRL_SFTRST;
7947c1b19cSWu Hao 	writeq(v, base + PORT_HDR_CTRL);
8047c1b19cSWu Hao 
8147c1b19cSWu Hao 	/*
8247c1b19cSWu Hao 	 * HW sets ack bit to 1 when all outstanding requests have been drained
8347c1b19cSWu Hao 	 * on this port and minimum soft reset pulse width has elapsed.
8447c1b19cSWu Hao 	 * Driver polls port_soft_reset_ack to determine if reset done by HW.
8547c1b19cSWu Hao 	 */
8647c1b19cSWu Hao 	if (readq_poll_timeout(base + PORT_HDR_CTRL, v, v & PORT_CTRL_SFTRST,
8747c1b19cSWu Hao 			       RST_POLL_INVL, RST_POLL_TIMEOUT)) {
8847c1b19cSWu Hao 		dev_err(&pdev->dev, "timeout, fail to reset device\n");
8947c1b19cSWu Hao 		return -ETIMEDOUT;
9047c1b19cSWu Hao 	}
9147c1b19cSWu Hao 
9247c1b19cSWu Hao 	return 0;
9347c1b19cSWu Hao }
9447c1b19cSWu Hao 
95e4664c0eSWu Hao /*
96e4664c0eSWu Hao  * This function resets the FPGA Port and its accelerator (AFU) by function
97e4664c0eSWu Hao  * __port_disable and __port_enable (set port soft reset bit and then clear
98e4664c0eSWu Hao  * it). Userspace can do Port reset at any time, e.g. during DMA or Partial
99e4664c0eSWu Hao  * Reconfiguration. But it should never cause any system level issue, only
100e4664c0eSWu Hao  * functional failure (e.g. DMA or PR operation failure) and be recoverable
101e4664c0eSWu Hao  * from the failure.
102e4664c0eSWu Hao  *
103e4664c0eSWu Hao  * Note: the accelerator (AFU) is not accessible when its port is in reset
104e4664c0eSWu Hao  * (disabled). Any attempts on MMIO access to AFU while in reset, will
105e4664c0eSWu Hao  * result errors reported via port error reporting sub feature (if present).
106e4664c0eSWu Hao  */
107e4664c0eSWu Hao static int __port_reset(struct platform_device *pdev)
108e4664c0eSWu Hao {
109e4664c0eSWu Hao 	int ret;
110e4664c0eSWu Hao 
11195844372SWu Hao 	ret = __afu_port_disable(pdev);
112e4664c0eSWu Hao 	if (!ret)
11395844372SWu Hao 		__afu_port_enable(pdev);
114e4664c0eSWu Hao 
115e4664c0eSWu Hao 	return ret;
116e4664c0eSWu Hao }
117e4664c0eSWu Hao 
118e4664c0eSWu Hao static int port_reset(struct platform_device *pdev)
119e4664c0eSWu Hao {
120e4664c0eSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
121e4664c0eSWu Hao 	int ret;
122e4664c0eSWu Hao 
123e4664c0eSWu Hao 	mutex_lock(&pdata->lock);
124e4664c0eSWu Hao 	ret = __port_reset(pdev);
125e4664c0eSWu Hao 	mutex_unlock(&pdata->lock);
126e4664c0eSWu Hao 
127e4664c0eSWu Hao 	return ret;
128e4664c0eSWu Hao }
129e4664c0eSWu Hao 
13047c1b19cSWu Hao static int port_get_id(struct platform_device *pdev)
13147c1b19cSWu Hao {
13247c1b19cSWu Hao 	void __iomem *base;
13347c1b19cSWu Hao 
13447c1b19cSWu Hao 	base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
13547c1b19cSWu Hao 
13647c1b19cSWu Hao 	return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + PORT_HDR_CAP));
13747c1b19cSWu Hao }
13847c1b19cSWu Hao 
139e4664c0eSWu Hao static ssize_t
140e4664c0eSWu Hao id_show(struct device *dev, struct device_attribute *attr, char *buf)
141e4664c0eSWu Hao {
142e4664c0eSWu Hao 	int id = port_get_id(to_platform_device(dev));
143e4664c0eSWu Hao 
144e4664c0eSWu Hao 	return scnprintf(buf, PAGE_SIZE, "%d\n", id);
145e4664c0eSWu Hao }
146e4664c0eSWu Hao static DEVICE_ATTR_RO(id);
147e4664c0eSWu Hao 
148d2ad5ac1SWu Hao static ssize_t
149d2ad5ac1SWu Hao ltr_show(struct device *dev, struct device_attribute *attr, char *buf)
150d2ad5ac1SWu Hao {
151d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
152d2ad5ac1SWu Hao 	void __iomem *base;
153d2ad5ac1SWu Hao 	u64 v;
154d2ad5ac1SWu Hao 
155d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
156d2ad5ac1SWu Hao 
157d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
158d2ad5ac1SWu Hao 	v = readq(base + PORT_HDR_CTRL);
159d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
160d2ad5ac1SWu Hao 
161d2ad5ac1SWu Hao 	return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_CTRL_LATENCY, v));
162d2ad5ac1SWu Hao }
163d2ad5ac1SWu Hao 
164d2ad5ac1SWu Hao static ssize_t
165d2ad5ac1SWu Hao ltr_store(struct device *dev, struct device_attribute *attr,
166d2ad5ac1SWu Hao 	  const char *buf, size_t count)
167d2ad5ac1SWu Hao {
168d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
169d2ad5ac1SWu Hao 	void __iomem *base;
170d2ad5ac1SWu Hao 	bool ltr;
171d2ad5ac1SWu Hao 	u64 v;
172d2ad5ac1SWu Hao 
173d2ad5ac1SWu Hao 	if (kstrtobool(buf, &ltr))
174d2ad5ac1SWu Hao 		return -EINVAL;
175d2ad5ac1SWu Hao 
176d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
177d2ad5ac1SWu Hao 
178d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
179d2ad5ac1SWu Hao 	v = readq(base + PORT_HDR_CTRL);
180d2ad5ac1SWu Hao 	v &= ~PORT_CTRL_LATENCY;
181d2ad5ac1SWu Hao 	v |= FIELD_PREP(PORT_CTRL_LATENCY, ltr ? 1 : 0);
182d2ad5ac1SWu Hao 	writeq(v, base + PORT_HDR_CTRL);
183d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
184d2ad5ac1SWu Hao 
185d2ad5ac1SWu Hao 	return count;
186d2ad5ac1SWu Hao }
187d2ad5ac1SWu Hao static DEVICE_ATTR_RW(ltr);
188d2ad5ac1SWu Hao 
189d2ad5ac1SWu Hao static ssize_t
190d2ad5ac1SWu Hao ap1_event_show(struct device *dev, struct device_attribute *attr, char *buf)
191d2ad5ac1SWu Hao {
192d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
193d2ad5ac1SWu Hao 	void __iomem *base;
194d2ad5ac1SWu Hao 	u64 v;
195d2ad5ac1SWu Hao 
196d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
197d2ad5ac1SWu Hao 
198d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
199d2ad5ac1SWu Hao 	v = readq(base + PORT_HDR_STS);
200d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
201d2ad5ac1SWu Hao 
202d2ad5ac1SWu Hao 	return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP1_EVT, v));
203d2ad5ac1SWu Hao }
204d2ad5ac1SWu Hao 
205d2ad5ac1SWu Hao static ssize_t
206d2ad5ac1SWu Hao ap1_event_store(struct device *dev, struct device_attribute *attr,
207d2ad5ac1SWu Hao 		const char *buf, size_t count)
208d2ad5ac1SWu Hao {
209d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
210d2ad5ac1SWu Hao 	void __iomem *base;
211d2ad5ac1SWu Hao 	bool clear;
212d2ad5ac1SWu Hao 
213d2ad5ac1SWu Hao 	if (kstrtobool(buf, &clear) || !clear)
214d2ad5ac1SWu Hao 		return -EINVAL;
215d2ad5ac1SWu Hao 
216d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
217d2ad5ac1SWu Hao 
218d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
219d2ad5ac1SWu Hao 	writeq(PORT_STS_AP1_EVT, base + PORT_HDR_STS);
220d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
221d2ad5ac1SWu Hao 
222d2ad5ac1SWu Hao 	return count;
223d2ad5ac1SWu Hao }
224d2ad5ac1SWu Hao static DEVICE_ATTR_RW(ap1_event);
225d2ad5ac1SWu Hao 
226d2ad5ac1SWu Hao static ssize_t
227d2ad5ac1SWu Hao ap2_event_show(struct device *dev, struct device_attribute *attr,
228d2ad5ac1SWu Hao 	       char *buf)
229d2ad5ac1SWu Hao {
230d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
231d2ad5ac1SWu Hao 	void __iomem *base;
232d2ad5ac1SWu Hao 	u64 v;
233d2ad5ac1SWu Hao 
234d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
235d2ad5ac1SWu Hao 
236d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
237d2ad5ac1SWu Hao 	v = readq(base + PORT_HDR_STS);
238d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
239d2ad5ac1SWu Hao 
240d2ad5ac1SWu Hao 	return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP2_EVT, v));
241d2ad5ac1SWu Hao }
242d2ad5ac1SWu Hao 
243d2ad5ac1SWu Hao static ssize_t
244d2ad5ac1SWu Hao ap2_event_store(struct device *dev, struct device_attribute *attr,
245d2ad5ac1SWu Hao 		const char *buf, size_t count)
246d2ad5ac1SWu Hao {
247d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
248d2ad5ac1SWu Hao 	void __iomem *base;
249d2ad5ac1SWu Hao 	bool clear;
250d2ad5ac1SWu Hao 
251d2ad5ac1SWu Hao 	if (kstrtobool(buf, &clear) || !clear)
252d2ad5ac1SWu Hao 		return -EINVAL;
253d2ad5ac1SWu Hao 
254d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
255d2ad5ac1SWu Hao 
256d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
257d2ad5ac1SWu Hao 	writeq(PORT_STS_AP2_EVT, base + PORT_HDR_STS);
258d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
259d2ad5ac1SWu Hao 
260d2ad5ac1SWu Hao 	return count;
261d2ad5ac1SWu Hao }
262d2ad5ac1SWu Hao static DEVICE_ATTR_RW(ap2_event);
263d2ad5ac1SWu Hao 
264d2ad5ac1SWu Hao static ssize_t
265d2ad5ac1SWu Hao power_state_show(struct device *dev, struct device_attribute *attr, char *buf)
266d2ad5ac1SWu Hao {
267d2ad5ac1SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
268d2ad5ac1SWu Hao 	void __iomem *base;
269d2ad5ac1SWu Hao 	u64 v;
270d2ad5ac1SWu Hao 
271d2ad5ac1SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
272d2ad5ac1SWu Hao 
273d2ad5ac1SWu Hao 	mutex_lock(&pdata->lock);
274d2ad5ac1SWu Hao 	v = readq(base + PORT_HDR_STS);
275d2ad5ac1SWu Hao 	mutex_unlock(&pdata->lock);
276d2ad5ac1SWu Hao 
277d2ad5ac1SWu Hao 	return sprintf(buf, "0x%x\n", (u8)FIELD_GET(PORT_STS_PWR_STATE, v));
278d2ad5ac1SWu Hao }
279d2ad5ac1SWu Hao static DEVICE_ATTR_RO(power_state);
280d2ad5ac1SWu Hao 
281f09991adSWu Hao static ssize_t
282f09991adSWu Hao userclk_freqcmd_store(struct device *dev, struct device_attribute *attr,
283f09991adSWu Hao 		      const char *buf, size_t count)
284f09991adSWu Hao {
285f09991adSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
286f09991adSWu Hao 	u64 userclk_freq_cmd;
287f09991adSWu Hao 	void __iomem *base;
288f09991adSWu Hao 
289f09991adSWu Hao 	if (kstrtou64(buf, 0, &userclk_freq_cmd))
290f09991adSWu Hao 		return -EINVAL;
291f09991adSWu Hao 
292f09991adSWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
293f09991adSWu Hao 
294f09991adSWu Hao 	mutex_lock(&pdata->lock);
295f09991adSWu Hao 	writeq(userclk_freq_cmd, base + PORT_HDR_USRCLK_CMD0);
296f09991adSWu Hao 	mutex_unlock(&pdata->lock);
297f09991adSWu Hao 
298f09991adSWu Hao 	return count;
299f09991adSWu Hao }
300f09991adSWu Hao static DEVICE_ATTR_WO(userclk_freqcmd);
301f09991adSWu Hao 
302f09991adSWu Hao static ssize_t
303f09991adSWu Hao userclk_freqcntrcmd_store(struct device *dev, struct device_attribute *attr,
304f09991adSWu Hao 			  const char *buf, size_t count)
305f09991adSWu Hao {
306f09991adSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
307f09991adSWu Hao 	u64 userclk_freqcntr_cmd;
308f09991adSWu Hao 	void __iomem *base;
309f09991adSWu Hao 
310f09991adSWu Hao 	if (kstrtou64(buf, 0, &userclk_freqcntr_cmd))
311f09991adSWu Hao 		return -EINVAL;
312f09991adSWu Hao 
313f09991adSWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
314f09991adSWu Hao 
315f09991adSWu Hao 	mutex_lock(&pdata->lock);
316f09991adSWu Hao 	writeq(userclk_freqcntr_cmd, base + PORT_HDR_USRCLK_CMD1);
317f09991adSWu Hao 	mutex_unlock(&pdata->lock);
318f09991adSWu Hao 
319f09991adSWu Hao 	return count;
320f09991adSWu Hao }
321f09991adSWu Hao static DEVICE_ATTR_WO(userclk_freqcntrcmd);
322f09991adSWu Hao 
323f09991adSWu Hao static ssize_t
324f09991adSWu Hao userclk_freqsts_show(struct device *dev, struct device_attribute *attr,
325f09991adSWu Hao 		     char *buf)
326f09991adSWu Hao {
327f09991adSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
328f09991adSWu Hao 	u64 userclk_freqsts;
329f09991adSWu Hao 	void __iomem *base;
330f09991adSWu Hao 
331f09991adSWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
332f09991adSWu Hao 
333f09991adSWu Hao 	mutex_lock(&pdata->lock);
334f09991adSWu Hao 	userclk_freqsts = readq(base + PORT_HDR_USRCLK_STS0);
335f09991adSWu Hao 	mutex_unlock(&pdata->lock);
336f09991adSWu Hao 
337f09991adSWu Hao 	return sprintf(buf, "0x%llx\n", (unsigned long long)userclk_freqsts);
338f09991adSWu Hao }
339f09991adSWu Hao static DEVICE_ATTR_RO(userclk_freqsts);
340f09991adSWu Hao 
341f09991adSWu Hao static ssize_t
342f09991adSWu Hao userclk_freqcntrsts_show(struct device *dev, struct device_attribute *attr,
343f09991adSWu Hao 			 char *buf)
344f09991adSWu Hao {
345f09991adSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
346f09991adSWu Hao 	u64 userclk_freqcntrsts;
347f09991adSWu Hao 	void __iomem *base;
348f09991adSWu Hao 
349f09991adSWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
350f09991adSWu Hao 
351f09991adSWu Hao 	mutex_lock(&pdata->lock);
352f09991adSWu Hao 	userclk_freqcntrsts = readq(base + PORT_HDR_USRCLK_STS1);
353f09991adSWu Hao 	mutex_unlock(&pdata->lock);
354f09991adSWu Hao 
355f09991adSWu Hao 	return sprintf(buf, "0x%llx\n",
356f09991adSWu Hao 		       (unsigned long long)userclk_freqcntrsts);
357f09991adSWu Hao }
358f09991adSWu Hao static DEVICE_ATTR_RO(userclk_freqcntrsts);
359f09991adSWu Hao 
360dcfecd4dSGreg Kroah-Hartman static struct attribute *port_hdr_attrs[] = {
361e4664c0eSWu Hao 	&dev_attr_id.attr,
362d2ad5ac1SWu Hao 	&dev_attr_ltr.attr,
363d2ad5ac1SWu Hao 	&dev_attr_ap1_event.attr,
364d2ad5ac1SWu Hao 	&dev_attr_ap2_event.attr,
365d2ad5ac1SWu Hao 	&dev_attr_power_state.attr,
366f09991adSWu Hao 	&dev_attr_userclk_freqcmd.attr,
367f09991adSWu Hao 	&dev_attr_userclk_freqcntrcmd.attr,
368f09991adSWu Hao 	&dev_attr_userclk_freqsts.attr,
369f09991adSWu Hao 	&dev_attr_userclk_freqcntrsts.attr,
370e4664c0eSWu Hao 	NULL,
371e4664c0eSWu Hao };
372a80a4b82SWu Hao 
373f09991adSWu Hao static umode_t port_hdr_attrs_visible(struct kobject *kobj,
374f09991adSWu Hao 				      struct attribute *attr, int n)
375f09991adSWu Hao {
376f09991adSWu Hao 	struct device *dev = kobj_to_dev(kobj);
377f09991adSWu Hao 	umode_t mode = attr->mode;
378f09991adSWu Hao 	void __iomem *base;
379f09991adSWu Hao 
380f09991adSWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
381f09991adSWu Hao 
382f09991adSWu Hao 	if (dfl_feature_revision(base) > 0) {
383f09991adSWu Hao 		/*
384f09991adSWu Hao 		 * userclk sysfs interfaces are only visible in case port
385f09991adSWu Hao 		 * revision is 0, as hardware with revision >0 doesn't
386f09991adSWu Hao 		 * support this.
387f09991adSWu Hao 		 */
388f09991adSWu Hao 		if (attr == &dev_attr_userclk_freqcmd.attr ||
389f09991adSWu Hao 		    attr == &dev_attr_userclk_freqcntrcmd.attr ||
390f09991adSWu Hao 		    attr == &dev_attr_userclk_freqsts.attr ||
391f09991adSWu Hao 		    attr == &dev_attr_userclk_freqcntrsts.attr)
392f09991adSWu Hao 			mode = 0;
393f09991adSWu Hao 	}
394f09991adSWu Hao 
395f09991adSWu Hao 	return mode;
396f09991adSWu Hao }
397f09991adSWu Hao 
398a80a4b82SWu Hao static const struct attribute_group port_hdr_group = {
399a80a4b82SWu Hao 	.attrs      = port_hdr_attrs,
400f09991adSWu Hao 	.is_visible = port_hdr_attrs_visible,
401a80a4b82SWu Hao };
402e4664c0eSWu Hao 
4031a1527cfSWu Hao static int port_hdr_init(struct platform_device *pdev,
4041a1527cfSWu Hao 			 struct dfl_feature *feature)
4051a1527cfSWu Hao {
406e4664c0eSWu Hao 	port_reset(pdev);
407e4664c0eSWu Hao 
408a80a4b82SWu Hao 	return 0;
409e4664c0eSWu Hao }
410e4664c0eSWu Hao 
411e4664c0eSWu Hao static long
412e4664c0eSWu Hao port_hdr_ioctl(struct platform_device *pdev, struct dfl_feature *feature,
413e4664c0eSWu Hao 	       unsigned int cmd, unsigned long arg)
414e4664c0eSWu Hao {
415e4664c0eSWu Hao 	long ret;
416e4664c0eSWu Hao 
417e4664c0eSWu Hao 	switch (cmd) {
418e4664c0eSWu Hao 	case DFL_FPGA_PORT_RESET:
419e4664c0eSWu Hao 		if (!arg)
420e4664c0eSWu Hao 			ret = port_reset(pdev);
421e4664c0eSWu Hao 		else
422e4664c0eSWu Hao 			ret = -EINVAL;
423e4664c0eSWu Hao 		break;
424e4664c0eSWu Hao 	default:
425e4664c0eSWu Hao 		dev_dbg(&pdev->dev, "%x cmd not handled", cmd);
426e4664c0eSWu Hao 		ret = -ENODEV;
427e4664c0eSWu Hao 	}
428e4664c0eSWu Hao 
429e4664c0eSWu Hao 	return ret;
4301a1527cfSWu Hao }
4311a1527cfSWu Hao 
43215bbb300SWu Hao static const struct dfl_feature_id port_hdr_id_table[] = {
43315bbb300SWu Hao 	{.id = PORT_FEATURE_ID_HEADER,},
43415bbb300SWu Hao 	{0,}
43515bbb300SWu Hao };
43615bbb300SWu Hao 
4371a1527cfSWu Hao static const struct dfl_feature_ops port_hdr_ops = {
4381a1527cfSWu Hao 	.init = port_hdr_init,
439e4664c0eSWu Hao 	.ioctl = port_hdr_ioctl,
4401a1527cfSWu Hao };
4411a1527cfSWu Hao 
442857a2622SXiao Guangrong static ssize_t
443857a2622SXiao Guangrong afu_id_show(struct device *dev, struct device_attribute *attr, char *buf)
444857a2622SXiao Guangrong {
445857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
446857a2622SXiao Guangrong 	void __iomem *base;
447857a2622SXiao Guangrong 	u64 guidl, guidh;
448857a2622SXiao Guangrong 
449857a2622SXiao Guangrong 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_AFU);
450857a2622SXiao Guangrong 
451857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
452857a2622SXiao Guangrong 	if (pdata->disable_count) {
453857a2622SXiao Guangrong 		mutex_unlock(&pdata->lock);
454857a2622SXiao Guangrong 		return -EBUSY;
455857a2622SXiao Guangrong 	}
456857a2622SXiao Guangrong 
457857a2622SXiao Guangrong 	guidl = readq(base + GUID_L);
458857a2622SXiao Guangrong 	guidh = readq(base + GUID_H);
459857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
460857a2622SXiao Guangrong 
461857a2622SXiao Guangrong 	return scnprintf(buf, PAGE_SIZE, "%016llx%016llx\n", guidh, guidl);
462857a2622SXiao Guangrong }
463857a2622SXiao Guangrong static DEVICE_ATTR_RO(afu_id);
464857a2622SXiao Guangrong 
465dcfecd4dSGreg Kroah-Hartman static struct attribute *port_afu_attrs[] = {
466857a2622SXiao Guangrong 	&dev_attr_afu_id.attr,
467857a2622SXiao Guangrong 	NULL
468857a2622SXiao Guangrong };
469a80a4b82SWu Hao 
470a80a4b82SWu Hao static umode_t port_afu_attrs_visible(struct kobject *kobj,
471a80a4b82SWu Hao 				      struct attribute *attr, int n)
472a80a4b82SWu Hao {
473a80a4b82SWu Hao 	struct device *dev = kobj_to_dev(kobj);
474a80a4b82SWu Hao 
475a80a4b82SWu Hao 	/*
476a80a4b82SWu Hao 	 * sysfs entries are visible only if related private feature is
477a80a4b82SWu Hao 	 * enumerated.
478a80a4b82SWu Hao 	 */
479a80a4b82SWu Hao 	if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_AFU))
480a80a4b82SWu Hao 		return 0;
481a80a4b82SWu Hao 
482a80a4b82SWu Hao 	return attr->mode;
483a80a4b82SWu Hao }
484a80a4b82SWu Hao 
485a80a4b82SWu Hao static const struct attribute_group port_afu_group = {
486a80a4b82SWu Hao 	.attrs      = port_afu_attrs,
487a80a4b82SWu Hao 	.is_visible = port_afu_attrs_visible,
488a80a4b82SWu Hao };
489857a2622SXiao Guangrong 
490857a2622SXiao Guangrong static int port_afu_init(struct platform_device *pdev,
491857a2622SXiao Guangrong 			 struct dfl_feature *feature)
492857a2622SXiao Guangrong {
493857a2622SXiao Guangrong 	struct resource *res = &pdev->resource[feature->resource_index];
494857a2622SXiao Guangrong 
495a80a4b82SWu Hao 	return afu_mmio_region_add(dev_get_platdata(&pdev->dev),
496a80a4b82SWu Hao 				   DFL_PORT_REGION_INDEX_AFU,
497a80a4b82SWu Hao 				   resource_size(res), res->start,
498a80a4b82SWu Hao 				   DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ |
499a80a4b82SWu Hao 				   DFL_PORT_REGION_WRITE);
500857a2622SXiao Guangrong }
501857a2622SXiao Guangrong 
50215bbb300SWu Hao static const struct dfl_feature_id port_afu_id_table[] = {
50315bbb300SWu Hao 	{.id = PORT_FEATURE_ID_AFU,},
50415bbb300SWu Hao 	{0,}
50515bbb300SWu Hao };
50615bbb300SWu Hao 
507857a2622SXiao Guangrong static const struct dfl_feature_ops port_afu_ops = {
508857a2622SXiao Guangrong 	.init = port_afu_init,
509857a2622SXiao Guangrong };
510857a2622SXiao Guangrong 
5111a1527cfSWu Hao static struct dfl_feature_driver port_feature_drvs[] = {
5121a1527cfSWu Hao 	{
51315bbb300SWu Hao 		.id_table = port_hdr_id_table,
5141a1527cfSWu Hao 		.ops = &port_hdr_ops,
5151a1527cfSWu Hao 	},
5161a1527cfSWu Hao 	{
51715bbb300SWu Hao 		.id_table = port_afu_id_table,
518857a2622SXiao Guangrong 		.ops = &port_afu_ops,
519857a2622SXiao Guangrong 	},
520857a2622SXiao Guangrong 	{
521*44d24753SWu Hao 		.id_table = port_err_id_table,
522*44d24753SWu Hao 		.ops = &port_err_ops,
523*44d24753SWu Hao 	},
524*44d24753SWu Hao 	{
5251a1527cfSWu Hao 		.ops = NULL,
5261a1527cfSWu Hao 	}
5271a1527cfSWu Hao };
5281a1527cfSWu Hao 
5291a1527cfSWu Hao static int afu_open(struct inode *inode, struct file *filp)
5301a1527cfSWu Hao {
5311a1527cfSWu Hao 	struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
5321a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
5331a1527cfSWu Hao 	int ret;
5341a1527cfSWu Hao 
5351a1527cfSWu Hao 	pdata = dev_get_platdata(&fdev->dev);
5361a1527cfSWu Hao 	if (WARN_ON(!pdata))
5371a1527cfSWu Hao 		return -ENODEV;
5381a1527cfSWu Hao 
5391a1527cfSWu Hao 	ret = dfl_feature_dev_use_begin(pdata);
5401a1527cfSWu Hao 	if (ret)
5411a1527cfSWu Hao 		return ret;
5421a1527cfSWu Hao 
5431a1527cfSWu Hao 	dev_dbg(&fdev->dev, "Device File Open\n");
5441a1527cfSWu Hao 	filp->private_data = fdev;
5451a1527cfSWu Hao 
5461a1527cfSWu Hao 	return 0;
5471a1527cfSWu Hao }
5481a1527cfSWu Hao 
5491a1527cfSWu Hao static int afu_release(struct inode *inode, struct file *filp)
5501a1527cfSWu Hao {
5511a1527cfSWu Hao 	struct platform_device *pdev = filp->private_data;
5521a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
5531a1527cfSWu Hao 
5541a1527cfSWu Hao 	dev_dbg(&pdev->dev, "Device File Release\n");
5551a1527cfSWu Hao 
5561a1527cfSWu Hao 	pdata = dev_get_platdata(&pdev->dev);
5571a1527cfSWu Hao 
558fa8dda1eSWu Hao 	mutex_lock(&pdata->lock);
559fa8dda1eSWu Hao 	__port_reset(pdev);
560fa8dda1eSWu Hao 	afu_dma_region_destroy(pdata);
561fa8dda1eSWu Hao 	mutex_unlock(&pdata->lock);
562fa8dda1eSWu Hao 
5631a1527cfSWu Hao 	dfl_feature_dev_use_end(pdata);
5641a1527cfSWu Hao 
5651a1527cfSWu Hao 	return 0;
5661a1527cfSWu Hao }
5671a1527cfSWu Hao 
5686fd893c4SWu Hao static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
5696fd893c4SWu Hao 				      unsigned long arg)
5706fd893c4SWu Hao {
5716fd893c4SWu Hao 	/* No extension support for now */
5726fd893c4SWu Hao 	return 0;
5736fd893c4SWu Hao }
5746fd893c4SWu Hao 
575857a2622SXiao Guangrong static long
576857a2622SXiao Guangrong afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg)
577857a2622SXiao Guangrong {
578857a2622SXiao Guangrong 	struct dfl_fpga_port_info info;
579857a2622SXiao Guangrong 	struct dfl_afu *afu;
580857a2622SXiao Guangrong 	unsigned long minsz;
581857a2622SXiao Guangrong 
582857a2622SXiao Guangrong 	minsz = offsetofend(struct dfl_fpga_port_info, num_umsgs);
583857a2622SXiao Guangrong 
584857a2622SXiao Guangrong 	if (copy_from_user(&info, arg, minsz))
585857a2622SXiao Guangrong 		return -EFAULT;
586857a2622SXiao Guangrong 
587857a2622SXiao Guangrong 	if (info.argsz < minsz)
588857a2622SXiao Guangrong 		return -EINVAL;
589857a2622SXiao Guangrong 
590857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
591857a2622SXiao Guangrong 	afu = dfl_fpga_pdata_get_private(pdata);
592857a2622SXiao Guangrong 	info.flags = 0;
593857a2622SXiao Guangrong 	info.num_regions = afu->num_regions;
594857a2622SXiao Guangrong 	info.num_umsgs = afu->num_umsgs;
595857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
596857a2622SXiao Guangrong 
597857a2622SXiao Guangrong 	if (copy_to_user(arg, &info, sizeof(info)))
598857a2622SXiao Guangrong 		return -EFAULT;
599857a2622SXiao Guangrong 
600857a2622SXiao Guangrong 	return 0;
601857a2622SXiao Guangrong }
602857a2622SXiao Guangrong 
603857a2622SXiao Guangrong static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata,
604857a2622SXiao Guangrong 				      void __user *arg)
605857a2622SXiao Guangrong {
606857a2622SXiao Guangrong 	struct dfl_fpga_port_region_info rinfo;
607857a2622SXiao Guangrong 	struct dfl_afu_mmio_region region;
608857a2622SXiao Guangrong 	unsigned long minsz;
609857a2622SXiao Guangrong 	long ret;
610857a2622SXiao Guangrong 
611857a2622SXiao Guangrong 	minsz = offsetofend(struct dfl_fpga_port_region_info, offset);
612857a2622SXiao Guangrong 
613857a2622SXiao Guangrong 	if (copy_from_user(&rinfo, arg, minsz))
614857a2622SXiao Guangrong 		return -EFAULT;
615857a2622SXiao Guangrong 
616857a2622SXiao Guangrong 	if (rinfo.argsz < minsz || rinfo.padding)
617857a2622SXiao Guangrong 		return -EINVAL;
618857a2622SXiao Guangrong 
619857a2622SXiao Guangrong 	ret = afu_mmio_region_get_by_index(pdata, rinfo.index, &region);
620857a2622SXiao Guangrong 	if (ret)
621857a2622SXiao Guangrong 		return ret;
622857a2622SXiao Guangrong 
623857a2622SXiao Guangrong 	rinfo.flags = region.flags;
624857a2622SXiao Guangrong 	rinfo.size = region.size;
625857a2622SXiao Guangrong 	rinfo.offset = region.offset;
626857a2622SXiao Guangrong 
627857a2622SXiao Guangrong 	if (copy_to_user(arg, &rinfo, sizeof(rinfo)))
628857a2622SXiao Guangrong 		return -EFAULT;
629857a2622SXiao Guangrong 
630857a2622SXiao Guangrong 	return 0;
631857a2622SXiao Guangrong }
632857a2622SXiao Guangrong 
633fa8dda1eSWu Hao static long
634fa8dda1eSWu Hao afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg)
635fa8dda1eSWu Hao {
636fa8dda1eSWu Hao 	struct dfl_fpga_port_dma_map map;
637fa8dda1eSWu Hao 	unsigned long minsz;
638fa8dda1eSWu Hao 	long ret;
639fa8dda1eSWu Hao 
640fa8dda1eSWu Hao 	minsz = offsetofend(struct dfl_fpga_port_dma_map, iova);
641fa8dda1eSWu Hao 
642fa8dda1eSWu Hao 	if (copy_from_user(&map, arg, minsz))
643fa8dda1eSWu Hao 		return -EFAULT;
644fa8dda1eSWu Hao 
645fa8dda1eSWu Hao 	if (map.argsz < minsz || map.flags)
646fa8dda1eSWu Hao 		return -EINVAL;
647fa8dda1eSWu Hao 
648fa8dda1eSWu Hao 	ret = afu_dma_map_region(pdata, map.user_addr, map.length, &map.iova);
649fa8dda1eSWu Hao 	if (ret)
650fa8dda1eSWu Hao 		return ret;
651fa8dda1eSWu Hao 
652fa8dda1eSWu Hao 	if (copy_to_user(arg, &map, sizeof(map))) {
653fa8dda1eSWu Hao 		afu_dma_unmap_region(pdata, map.iova);
654fa8dda1eSWu Hao 		return -EFAULT;
655fa8dda1eSWu Hao 	}
656fa8dda1eSWu Hao 
657fa8dda1eSWu Hao 	dev_dbg(&pdata->dev->dev, "dma map: ua=%llx, len=%llx, iova=%llx\n",
658fa8dda1eSWu Hao 		(unsigned long long)map.user_addr,
659fa8dda1eSWu Hao 		(unsigned long long)map.length,
660fa8dda1eSWu Hao 		(unsigned long long)map.iova);
661fa8dda1eSWu Hao 
662fa8dda1eSWu Hao 	return 0;
663fa8dda1eSWu Hao }
664fa8dda1eSWu Hao 
665fa8dda1eSWu Hao static long
666fa8dda1eSWu Hao afu_ioctl_dma_unmap(struct dfl_feature_platform_data *pdata, void __user *arg)
667fa8dda1eSWu Hao {
668fa8dda1eSWu Hao 	struct dfl_fpga_port_dma_unmap unmap;
669fa8dda1eSWu Hao 	unsigned long minsz;
670fa8dda1eSWu Hao 
671fa8dda1eSWu Hao 	minsz = offsetofend(struct dfl_fpga_port_dma_unmap, iova);
672fa8dda1eSWu Hao 
673fa8dda1eSWu Hao 	if (copy_from_user(&unmap, arg, minsz))
674fa8dda1eSWu Hao 		return -EFAULT;
675fa8dda1eSWu Hao 
676fa8dda1eSWu Hao 	if (unmap.argsz < minsz || unmap.flags)
677fa8dda1eSWu Hao 		return -EINVAL;
678fa8dda1eSWu Hao 
679fa8dda1eSWu Hao 	return afu_dma_unmap_region(pdata, unmap.iova);
680fa8dda1eSWu Hao }
681fa8dda1eSWu Hao 
6821a1527cfSWu Hao static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
6831a1527cfSWu Hao {
6841a1527cfSWu Hao 	struct platform_device *pdev = filp->private_data;
6851a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
6861a1527cfSWu Hao 	struct dfl_feature *f;
6871a1527cfSWu Hao 	long ret;
6881a1527cfSWu Hao 
6891a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd);
6901a1527cfSWu Hao 
6911a1527cfSWu Hao 	pdata = dev_get_platdata(&pdev->dev);
6921a1527cfSWu Hao 
6931a1527cfSWu Hao 	switch (cmd) {
6946fd893c4SWu Hao 	case DFL_FPGA_GET_API_VERSION:
6956fd893c4SWu Hao 		return DFL_FPGA_API_VERSION;
6966fd893c4SWu Hao 	case DFL_FPGA_CHECK_EXTENSION:
6976fd893c4SWu Hao 		return afu_ioctl_check_extension(pdata, arg);
698857a2622SXiao Guangrong 	case DFL_FPGA_PORT_GET_INFO:
699857a2622SXiao Guangrong 		return afu_ioctl_get_info(pdata, (void __user *)arg);
700857a2622SXiao Guangrong 	case DFL_FPGA_PORT_GET_REGION_INFO:
701857a2622SXiao Guangrong 		return afu_ioctl_get_region_info(pdata, (void __user *)arg);
702fa8dda1eSWu Hao 	case DFL_FPGA_PORT_DMA_MAP:
703fa8dda1eSWu Hao 		return afu_ioctl_dma_map(pdata, (void __user *)arg);
704fa8dda1eSWu Hao 	case DFL_FPGA_PORT_DMA_UNMAP:
705fa8dda1eSWu Hao 		return afu_ioctl_dma_unmap(pdata, (void __user *)arg);
7061a1527cfSWu Hao 	default:
7071a1527cfSWu Hao 		/*
7081a1527cfSWu Hao 		 * Let sub-feature's ioctl function to handle the cmd
7091a1527cfSWu Hao 		 * Sub-feature's ioctl returns -ENODEV when cmd is not
7101a1527cfSWu Hao 		 * handled in this sub feature, and returns 0 and other
7111a1527cfSWu Hao 		 * error code if cmd is handled.
7121a1527cfSWu Hao 		 */
7131a1527cfSWu Hao 		dfl_fpga_dev_for_each_feature(pdata, f)
7141a1527cfSWu Hao 			if (f->ops && f->ops->ioctl) {
7151a1527cfSWu Hao 				ret = f->ops->ioctl(pdev, f, cmd, arg);
7161a1527cfSWu Hao 				if (ret != -ENODEV)
7171a1527cfSWu Hao 					return ret;
7181a1527cfSWu Hao 			}
7191a1527cfSWu Hao 	}
7201a1527cfSWu Hao 
7211a1527cfSWu Hao 	return -EINVAL;
7221a1527cfSWu Hao }
7231a1527cfSWu Hao 
724857a2622SXiao Guangrong static int afu_mmap(struct file *filp, struct vm_area_struct *vma)
725857a2622SXiao Guangrong {
726857a2622SXiao Guangrong 	struct platform_device *pdev = filp->private_data;
727857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata;
728857a2622SXiao Guangrong 	u64 size = vma->vm_end - vma->vm_start;
729857a2622SXiao Guangrong 	struct dfl_afu_mmio_region region;
730857a2622SXiao Guangrong 	u64 offset;
731857a2622SXiao Guangrong 	int ret;
732857a2622SXiao Guangrong 
733857a2622SXiao Guangrong 	if (!(vma->vm_flags & VM_SHARED))
734857a2622SXiao Guangrong 		return -EINVAL;
735857a2622SXiao Guangrong 
736857a2622SXiao Guangrong 	pdata = dev_get_platdata(&pdev->dev);
737857a2622SXiao Guangrong 
738857a2622SXiao Guangrong 	offset = vma->vm_pgoff << PAGE_SHIFT;
739857a2622SXiao Guangrong 	ret = afu_mmio_region_get_by_offset(pdata, offset, size, &region);
740857a2622SXiao Guangrong 	if (ret)
741857a2622SXiao Guangrong 		return ret;
742857a2622SXiao Guangrong 
743857a2622SXiao Guangrong 	if (!(region.flags & DFL_PORT_REGION_MMAP))
744857a2622SXiao Guangrong 		return -EINVAL;
745857a2622SXiao Guangrong 
746857a2622SXiao Guangrong 	if ((vma->vm_flags & VM_READ) && !(region.flags & DFL_PORT_REGION_READ))
747857a2622SXiao Guangrong 		return -EPERM;
748857a2622SXiao Guangrong 
749857a2622SXiao Guangrong 	if ((vma->vm_flags & VM_WRITE) &&
750857a2622SXiao Guangrong 	    !(region.flags & DFL_PORT_REGION_WRITE))
751857a2622SXiao Guangrong 		return -EPERM;
752857a2622SXiao Guangrong 
753857a2622SXiao Guangrong 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
754857a2622SXiao Guangrong 
755857a2622SXiao Guangrong 	return remap_pfn_range(vma, vma->vm_start,
756857a2622SXiao Guangrong 			(region.phys + (offset - region.offset)) >> PAGE_SHIFT,
757857a2622SXiao Guangrong 			size, vma->vm_page_prot);
758857a2622SXiao Guangrong }
759857a2622SXiao Guangrong 
7601a1527cfSWu Hao static const struct file_operations afu_fops = {
7611a1527cfSWu Hao 	.owner = THIS_MODULE,
7621a1527cfSWu Hao 	.open = afu_open,
7631a1527cfSWu Hao 	.release = afu_release,
7641a1527cfSWu Hao 	.unlocked_ioctl = afu_ioctl,
765857a2622SXiao Guangrong 	.mmap = afu_mmap,
7661a1527cfSWu Hao };
7671a1527cfSWu Hao 
768857a2622SXiao Guangrong static int afu_dev_init(struct platform_device *pdev)
769857a2622SXiao Guangrong {
770857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
771857a2622SXiao Guangrong 	struct dfl_afu *afu;
772857a2622SXiao Guangrong 
773857a2622SXiao Guangrong 	afu = devm_kzalloc(&pdev->dev, sizeof(*afu), GFP_KERNEL);
774857a2622SXiao Guangrong 	if (!afu)
775857a2622SXiao Guangrong 		return -ENOMEM;
776857a2622SXiao Guangrong 
777857a2622SXiao Guangrong 	afu->pdata = pdata;
778857a2622SXiao Guangrong 
779857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
780857a2622SXiao Guangrong 	dfl_fpga_pdata_set_private(pdata, afu);
781857a2622SXiao Guangrong 	afu_mmio_region_init(pdata);
782fa8dda1eSWu Hao 	afu_dma_region_init(pdata);
783857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
784857a2622SXiao Guangrong 
785857a2622SXiao Guangrong 	return 0;
786857a2622SXiao Guangrong }
787857a2622SXiao Guangrong 
788857a2622SXiao Guangrong static int afu_dev_destroy(struct platform_device *pdev)
789857a2622SXiao Guangrong {
790857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
791857a2622SXiao Guangrong 	struct dfl_afu *afu;
792857a2622SXiao Guangrong 
793857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
794857a2622SXiao Guangrong 	afu = dfl_fpga_pdata_get_private(pdata);
795857a2622SXiao Guangrong 	afu_mmio_region_destroy(pdata);
796fa8dda1eSWu Hao 	afu_dma_region_destroy(pdata);
797857a2622SXiao Guangrong 	dfl_fpga_pdata_set_private(pdata, NULL);
798857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
799857a2622SXiao Guangrong 
800857a2622SXiao Guangrong 	return 0;
801857a2622SXiao Guangrong }
802857a2622SXiao Guangrong 
80347c1b19cSWu Hao static int port_enable_set(struct platform_device *pdev, bool enable)
80447c1b19cSWu Hao {
80547c1b19cSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
80647c1b19cSWu Hao 	int ret = 0;
80747c1b19cSWu Hao 
80847c1b19cSWu Hao 	mutex_lock(&pdata->lock);
80947c1b19cSWu Hao 	if (enable)
81095844372SWu Hao 		__afu_port_enable(pdev);
81147c1b19cSWu Hao 	else
81295844372SWu Hao 		ret = __afu_port_disable(pdev);
81347c1b19cSWu Hao 	mutex_unlock(&pdata->lock);
81447c1b19cSWu Hao 
81547c1b19cSWu Hao 	return ret;
81647c1b19cSWu Hao }
81747c1b19cSWu Hao 
81847c1b19cSWu Hao static struct dfl_fpga_port_ops afu_port_ops = {
81947c1b19cSWu Hao 	.name = DFL_FPGA_FEATURE_DEV_PORT,
82047c1b19cSWu Hao 	.owner = THIS_MODULE,
82147c1b19cSWu Hao 	.get_id = port_get_id,
82247c1b19cSWu Hao 	.enable_set = port_enable_set,
82347c1b19cSWu Hao };
82447c1b19cSWu Hao 
8251a1527cfSWu Hao static int afu_probe(struct platform_device *pdev)
8261a1527cfSWu Hao {
8271a1527cfSWu Hao 	int ret;
8281a1527cfSWu Hao 
8291a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s\n", __func__);
8301a1527cfSWu Hao 
831857a2622SXiao Guangrong 	ret = afu_dev_init(pdev);
832857a2622SXiao Guangrong 	if (ret)
833857a2622SXiao Guangrong 		goto exit;
834857a2622SXiao Guangrong 
8351a1527cfSWu Hao 	ret = dfl_fpga_dev_feature_init(pdev, port_feature_drvs);
8361a1527cfSWu Hao 	if (ret)
837857a2622SXiao Guangrong 		goto dev_destroy;
8381a1527cfSWu Hao 
8391a1527cfSWu Hao 	ret = dfl_fpga_dev_ops_register(pdev, &afu_fops, THIS_MODULE);
840857a2622SXiao Guangrong 	if (ret) {
8411a1527cfSWu Hao 		dfl_fpga_dev_feature_uinit(pdev);
842857a2622SXiao Guangrong 		goto dev_destroy;
843857a2622SXiao Guangrong 	}
8441a1527cfSWu Hao 
845857a2622SXiao Guangrong 	return 0;
846857a2622SXiao Guangrong 
847857a2622SXiao Guangrong dev_destroy:
848857a2622SXiao Guangrong 	afu_dev_destroy(pdev);
849857a2622SXiao Guangrong exit:
8501a1527cfSWu Hao 	return ret;
8511a1527cfSWu Hao }
8521a1527cfSWu Hao 
8531a1527cfSWu Hao static int afu_remove(struct platform_device *pdev)
8541a1527cfSWu Hao {
8551a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s\n", __func__);
8561a1527cfSWu Hao 
8571a1527cfSWu Hao 	dfl_fpga_dev_ops_unregister(pdev);
8581a1527cfSWu Hao 	dfl_fpga_dev_feature_uinit(pdev);
859857a2622SXiao Guangrong 	afu_dev_destroy(pdev);
8601a1527cfSWu Hao 
8611a1527cfSWu Hao 	return 0;
8621a1527cfSWu Hao }
8631a1527cfSWu Hao 
864a80a4b82SWu Hao static const struct attribute_group *afu_dev_groups[] = {
865a80a4b82SWu Hao 	&port_hdr_group,
866a80a4b82SWu Hao 	&port_afu_group,
867*44d24753SWu Hao 	&port_err_group,
868a80a4b82SWu Hao 	NULL
869a80a4b82SWu Hao };
870a80a4b82SWu Hao 
8711a1527cfSWu Hao static struct platform_driver afu_driver = {
8721a1527cfSWu Hao 	.driver	= {
8731a1527cfSWu Hao 		.name	    = DFL_FPGA_FEATURE_DEV_PORT,
874a80a4b82SWu Hao 		.dev_groups = afu_dev_groups,
8751a1527cfSWu Hao 	},
8761a1527cfSWu Hao 	.probe   = afu_probe,
8771a1527cfSWu Hao 	.remove  = afu_remove,
8781a1527cfSWu Hao };
8791a1527cfSWu Hao 
88047c1b19cSWu Hao static int __init afu_init(void)
88147c1b19cSWu Hao {
88247c1b19cSWu Hao 	int ret;
88347c1b19cSWu Hao 
88447c1b19cSWu Hao 	dfl_fpga_port_ops_add(&afu_port_ops);
88547c1b19cSWu Hao 
88647c1b19cSWu Hao 	ret = platform_driver_register(&afu_driver);
88747c1b19cSWu Hao 	if (ret)
88847c1b19cSWu Hao 		dfl_fpga_port_ops_del(&afu_port_ops);
88947c1b19cSWu Hao 
89047c1b19cSWu Hao 	return ret;
89147c1b19cSWu Hao }
89247c1b19cSWu Hao 
89347c1b19cSWu Hao static void __exit afu_exit(void)
89447c1b19cSWu Hao {
89547c1b19cSWu Hao 	platform_driver_unregister(&afu_driver);
89647c1b19cSWu Hao 
89747c1b19cSWu Hao 	dfl_fpga_port_ops_del(&afu_port_ops);
89847c1b19cSWu Hao }
89947c1b19cSWu Hao 
90047c1b19cSWu Hao module_init(afu_init);
90147c1b19cSWu Hao module_exit(afu_exit);
9021a1527cfSWu Hao 
9031a1527cfSWu Hao MODULE_DESCRIPTION("FPGA Accelerated Function Unit driver");
9041a1527cfSWu Hao MODULE_AUTHOR("Intel Corporation");
9051a1527cfSWu Hao MODULE_LICENSE("GPL v2");
9061a1527cfSWu Hao MODULE_ALIAS("platform:dfl-port");
907