xref: /openbmc/linux/drivers/fpga/dfl-afu-main.c (revision 09d86150141955ba1b3d7cbef23785f4996e4d6f)
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 
511bd127b81SWu Hao static int port_stp_init(struct platform_device *pdev,
512bd127b81SWu Hao 			 struct dfl_feature *feature)
513bd127b81SWu Hao {
514bd127b81SWu Hao 	struct resource *res = &pdev->resource[feature->resource_index];
515bd127b81SWu Hao 
516bd127b81SWu Hao 	return afu_mmio_region_add(dev_get_platdata(&pdev->dev),
517bd127b81SWu Hao 				   DFL_PORT_REGION_INDEX_STP,
518bd127b81SWu Hao 				   resource_size(res), res->start,
519bd127b81SWu Hao 				   DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ |
520bd127b81SWu Hao 				   DFL_PORT_REGION_WRITE);
521bd127b81SWu Hao }
522bd127b81SWu Hao 
523bd127b81SWu Hao static const struct dfl_feature_id port_stp_id_table[] = {
524bd127b81SWu Hao 	{.id = PORT_FEATURE_ID_STP,},
525bd127b81SWu Hao 	{0,}
526bd127b81SWu Hao };
527bd127b81SWu Hao 
528bd127b81SWu Hao static const struct dfl_feature_ops port_stp_ops = {
529bd127b81SWu Hao 	.init = port_stp_init,
530bd127b81SWu Hao };
531bd127b81SWu Hao 
532*09d86150SXu Yilun static long
533*09d86150SXu Yilun port_uint_ioctl(struct platform_device *pdev, struct dfl_feature *feature,
534*09d86150SXu Yilun 		unsigned int cmd, unsigned long arg)
535*09d86150SXu Yilun {
536*09d86150SXu Yilun 	switch (cmd) {
537*09d86150SXu Yilun 	case DFL_FPGA_PORT_UINT_GET_IRQ_NUM:
538*09d86150SXu Yilun 		return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg);
539*09d86150SXu Yilun 	case DFL_FPGA_PORT_UINT_SET_IRQ:
540*09d86150SXu Yilun 		return dfl_feature_ioctl_set_irq(pdev, feature, arg);
541*09d86150SXu Yilun 	default:
542*09d86150SXu Yilun 		dev_dbg(&pdev->dev, "%x cmd not handled", cmd);
543*09d86150SXu Yilun 		return -ENODEV;
544*09d86150SXu Yilun 	}
545*09d86150SXu Yilun }
546*09d86150SXu Yilun 
547*09d86150SXu Yilun static const struct dfl_feature_id port_uint_id_table[] = {
548*09d86150SXu Yilun 	{.id = PORT_FEATURE_ID_UINT,},
549*09d86150SXu Yilun 	{0,}
550*09d86150SXu Yilun };
551*09d86150SXu Yilun 
552*09d86150SXu Yilun static const struct dfl_feature_ops port_uint_ops = {
553*09d86150SXu Yilun 	.ioctl = port_uint_ioctl,
554*09d86150SXu Yilun };
555*09d86150SXu Yilun 
5561a1527cfSWu Hao static struct dfl_feature_driver port_feature_drvs[] = {
5571a1527cfSWu Hao 	{
55815bbb300SWu Hao 		.id_table = port_hdr_id_table,
5591a1527cfSWu Hao 		.ops = &port_hdr_ops,
5601a1527cfSWu Hao 	},
5611a1527cfSWu Hao 	{
56215bbb300SWu Hao 		.id_table = port_afu_id_table,
563857a2622SXiao Guangrong 		.ops = &port_afu_ops,
564857a2622SXiao Guangrong 	},
565857a2622SXiao Guangrong 	{
56644d24753SWu Hao 		.id_table = port_err_id_table,
56744d24753SWu Hao 		.ops = &port_err_ops,
56844d24753SWu Hao 	},
56944d24753SWu Hao 	{
570bd127b81SWu Hao 		.id_table = port_stp_id_table,
571bd127b81SWu Hao 		.ops = &port_stp_ops,
572bd127b81SWu Hao 	},
573bd127b81SWu Hao 	{
574*09d86150SXu Yilun 		.id_table = port_uint_id_table,
575*09d86150SXu Yilun 		.ops = &port_uint_ops,
576*09d86150SXu Yilun 	},
577*09d86150SXu Yilun 	{
5781a1527cfSWu Hao 		.ops = NULL,
5791a1527cfSWu Hao 	}
5801a1527cfSWu Hao };
5811a1527cfSWu Hao 
5821a1527cfSWu Hao static int afu_open(struct inode *inode, struct file *filp)
5831a1527cfSWu Hao {
5841a1527cfSWu Hao 	struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
5851a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
5861a1527cfSWu Hao 	int ret;
5871a1527cfSWu Hao 
5881a1527cfSWu Hao 	pdata = dev_get_platdata(&fdev->dev);
5891a1527cfSWu Hao 	if (WARN_ON(!pdata))
5901a1527cfSWu Hao 		return -ENODEV;
5911a1527cfSWu Hao 
592b6862193SXu Yilun 	mutex_lock(&pdata->lock);
593b6862193SXu Yilun 	ret = dfl_feature_dev_use_begin(pdata, filp->f_flags & O_EXCL);
594b6862193SXu Yilun 	if (!ret) {
595b6862193SXu Yilun 		dev_dbg(&fdev->dev, "Device File Opened %d Times\n",
596b6862193SXu Yilun 			dfl_feature_dev_use_count(pdata));
5971a1527cfSWu Hao 		filp->private_data = fdev;
598b6862193SXu Yilun 	}
599b6862193SXu Yilun 	mutex_unlock(&pdata->lock);
6001a1527cfSWu Hao 
601b6862193SXu Yilun 	return ret;
6021a1527cfSWu Hao }
6031a1527cfSWu Hao 
6041a1527cfSWu Hao static int afu_release(struct inode *inode, struct file *filp)
6051a1527cfSWu Hao {
6061a1527cfSWu Hao 	struct platform_device *pdev = filp->private_data;
6071a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
608fe6a3d65SXu Yilun 	struct dfl_feature *feature;
6091a1527cfSWu Hao 
6101a1527cfSWu Hao 	dev_dbg(&pdev->dev, "Device File Release\n");
6111a1527cfSWu Hao 
6121a1527cfSWu Hao 	pdata = dev_get_platdata(&pdev->dev);
6131a1527cfSWu Hao 
614fa8dda1eSWu Hao 	mutex_lock(&pdata->lock);
615b6862193SXu Yilun 	dfl_feature_dev_use_end(pdata);
616b6862193SXu Yilun 
617b6862193SXu Yilun 	if (!dfl_feature_dev_use_count(pdata)) {
618fe6a3d65SXu Yilun 		dfl_fpga_dev_for_each_feature(pdata, feature)
619fe6a3d65SXu Yilun 			dfl_fpga_set_irq_triggers(feature, 0,
620fe6a3d65SXu Yilun 						  feature->nr_irqs, NULL);
621fa8dda1eSWu Hao 		__port_reset(pdev);
622fa8dda1eSWu Hao 		afu_dma_region_destroy(pdata);
623b6862193SXu Yilun 	}
624fa8dda1eSWu Hao 	mutex_unlock(&pdata->lock);
625fa8dda1eSWu Hao 
6261a1527cfSWu Hao 	return 0;
6271a1527cfSWu Hao }
6281a1527cfSWu Hao 
6296fd893c4SWu Hao static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
6306fd893c4SWu Hao 				      unsigned long arg)
6316fd893c4SWu Hao {
6326fd893c4SWu Hao 	/* No extension support for now */
6336fd893c4SWu Hao 	return 0;
6346fd893c4SWu Hao }
6356fd893c4SWu Hao 
636857a2622SXiao Guangrong static long
637857a2622SXiao Guangrong afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg)
638857a2622SXiao Guangrong {
639857a2622SXiao Guangrong 	struct dfl_fpga_port_info info;
640857a2622SXiao Guangrong 	struct dfl_afu *afu;
641857a2622SXiao Guangrong 	unsigned long minsz;
642857a2622SXiao Guangrong 
643857a2622SXiao Guangrong 	minsz = offsetofend(struct dfl_fpga_port_info, num_umsgs);
644857a2622SXiao Guangrong 
645857a2622SXiao Guangrong 	if (copy_from_user(&info, arg, minsz))
646857a2622SXiao Guangrong 		return -EFAULT;
647857a2622SXiao Guangrong 
648857a2622SXiao Guangrong 	if (info.argsz < minsz)
649857a2622SXiao Guangrong 		return -EINVAL;
650857a2622SXiao Guangrong 
651857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
652857a2622SXiao Guangrong 	afu = dfl_fpga_pdata_get_private(pdata);
653857a2622SXiao Guangrong 	info.flags = 0;
654857a2622SXiao Guangrong 	info.num_regions = afu->num_regions;
655857a2622SXiao Guangrong 	info.num_umsgs = afu->num_umsgs;
656857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
657857a2622SXiao Guangrong 
658857a2622SXiao Guangrong 	if (copy_to_user(arg, &info, sizeof(info)))
659857a2622SXiao Guangrong 		return -EFAULT;
660857a2622SXiao Guangrong 
661857a2622SXiao Guangrong 	return 0;
662857a2622SXiao Guangrong }
663857a2622SXiao Guangrong 
664857a2622SXiao Guangrong static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata,
665857a2622SXiao Guangrong 				      void __user *arg)
666857a2622SXiao Guangrong {
667857a2622SXiao Guangrong 	struct dfl_fpga_port_region_info rinfo;
668857a2622SXiao Guangrong 	struct dfl_afu_mmio_region region;
669857a2622SXiao Guangrong 	unsigned long minsz;
670857a2622SXiao Guangrong 	long ret;
671857a2622SXiao Guangrong 
672857a2622SXiao Guangrong 	minsz = offsetofend(struct dfl_fpga_port_region_info, offset);
673857a2622SXiao Guangrong 
674857a2622SXiao Guangrong 	if (copy_from_user(&rinfo, arg, minsz))
675857a2622SXiao Guangrong 		return -EFAULT;
676857a2622SXiao Guangrong 
677857a2622SXiao Guangrong 	if (rinfo.argsz < minsz || rinfo.padding)
678857a2622SXiao Guangrong 		return -EINVAL;
679857a2622SXiao Guangrong 
680857a2622SXiao Guangrong 	ret = afu_mmio_region_get_by_index(pdata, rinfo.index, &region);
681857a2622SXiao Guangrong 	if (ret)
682857a2622SXiao Guangrong 		return ret;
683857a2622SXiao Guangrong 
684857a2622SXiao Guangrong 	rinfo.flags = region.flags;
685857a2622SXiao Guangrong 	rinfo.size = region.size;
686857a2622SXiao Guangrong 	rinfo.offset = region.offset;
687857a2622SXiao Guangrong 
688857a2622SXiao Guangrong 	if (copy_to_user(arg, &rinfo, sizeof(rinfo)))
689857a2622SXiao Guangrong 		return -EFAULT;
690857a2622SXiao Guangrong 
691857a2622SXiao Guangrong 	return 0;
692857a2622SXiao Guangrong }
693857a2622SXiao Guangrong 
694fa8dda1eSWu Hao static long
695fa8dda1eSWu Hao afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg)
696fa8dda1eSWu Hao {
697fa8dda1eSWu Hao 	struct dfl_fpga_port_dma_map map;
698fa8dda1eSWu Hao 	unsigned long minsz;
699fa8dda1eSWu Hao 	long ret;
700fa8dda1eSWu Hao 
701fa8dda1eSWu Hao 	minsz = offsetofend(struct dfl_fpga_port_dma_map, iova);
702fa8dda1eSWu Hao 
703fa8dda1eSWu Hao 	if (copy_from_user(&map, arg, minsz))
704fa8dda1eSWu Hao 		return -EFAULT;
705fa8dda1eSWu Hao 
706fa8dda1eSWu Hao 	if (map.argsz < minsz || map.flags)
707fa8dda1eSWu Hao 		return -EINVAL;
708fa8dda1eSWu Hao 
709fa8dda1eSWu Hao 	ret = afu_dma_map_region(pdata, map.user_addr, map.length, &map.iova);
710fa8dda1eSWu Hao 	if (ret)
711fa8dda1eSWu Hao 		return ret;
712fa8dda1eSWu Hao 
713fa8dda1eSWu Hao 	if (copy_to_user(arg, &map, sizeof(map))) {
714fa8dda1eSWu Hao 		afu_dma_unmap_region(pdata, map.iova);
715fa8dda1eSWu Hao 		return -EFAULT;
716fa8dda1eSWu Hao 	}
717fa8dda1eSWu Hao 
718fa8dda1eSWu Hao 	dev_dbg(&pdata->dev->dev, "dma map: ua=%llx, len=%llx, iova=%llx\n",
719fa8dda1eSWu Hao 		(unsigned long long)map.user_addr,
720fa8dda1eSWu Hao 		(unsigned long long)map.length,
721fa8dda1eSWu Hao 		(unsigned long long)map.iova);
722fa8dda1eSWu Hao 
723fa8dda1eSWu Hao 	return 0;
724fa8dda1eSWu Hao }
725fa8dda1eSWu Hao 
726fa8dda1eSWu Hao static long
727fa8dda1eSWu Hao afu_ioctl_dma_unmap(struct dfl_feature_platform_data *pdata, void __user *arg)
728fa8dda1eSWu Hao {
729fa8dda1eSWu Hao 	struct dfl_fpga_port_dma_unmap unmap;
730fa8dda1eSWu Hao 	unsigned long minsz;
731fa8dda1eSWu Hao 
732fa8dda1eSWu Hao 	minsz = offsetofend(struct dfl_fpga_port_dma_unmap, iova);
733fa8dda1eSWu Hao 
734fa8dda1eSWu Hao 	if (copy_from_user(&unmap, arg, minsz))
735fa8dda1eSWu Hao 		return -EFAULT;
736fa8dda1eSWu Hao 
737fa8dda1eSWu Hao 	if (unmap.argsz < minsz || unmap.flags)
738fa8dda1eSWu Hao 		return -EINVAL;
739fa8dda1eSWu Hao 
740fa8dda1eSWu Hao 	return afu_dma_unmap_region(pdata, unmap.iova);
741fa8dda1eSWu Hao }
742fa8dda1eSWu Hao 
7431a1527cfSWu Hao static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
7441a1527cfSWu Hao {
7451a1527cfSWu Hao 	struct platform_device *pdev = filp->private_data;
7461a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
7471a1527cfSWu Hao 	struct dfl_feature *f;
7481a1527cfSWu Hao 	long ret;
7491a1527cfSWu Hao 
7501a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd);
7511a1527cfSWu Hao 
7521a1527cfSWu Hao 	pdata = dev_get_platdata(&pdev->dev);
7531a1527cfSWu Hao 
7541a1527cfSWu Hao 	switch (cmd) {
7556fd893c4SWu Hao 	case DFL_FPGA_GET_API_VERSION:
7566fd893c4SWu Hao 		return DFL_FPGA_API_VERSION;
7576fd893c4SWu Hao 	case DFL_FPGA_CHECK_EXTENSION:
7586fd893c4SWu Hao 		return afu_ioctl_check_extension(pdata, arg);
759857a2622SXiao Guangrong 	case DFL_FPGA_PORT_GET_INFO:
760857a2622SXiao Guangrong 		return afu_ioctl_get_info(pdata, (void __user *)arg);
761857a2622SXiao Guangrong 	case DFL_FPGA_PORT_GET_REGION_INFO:
762857a2622SXiao Guangrong 		return afu_ioctl_get_region_info(pdata, (void __user *)arg);
763fa8dda1eSWu Hao 	case DFL_FPGA_PORT_DMA_MAP:
764fa8dda1eSWu Hao 		return afu_ioctl_dma_map(pdata, (void __user *)arg);
765fa8dda1eSWu Hao 	case DFL_FPGA_PORT_DMA_UNMAP:
766fa8dda1eSWu Hao 		return afu_ioctl_dma_unmap(pdata, (void __user *)arg);
7671a1527cfSWu Hao 	default:
7681a1527cfSWu Hao 		/*
7691a1527cfSWu Hao 		 * Let sub-feature's ioctl function to handle the cmd
7701a1527cfSWu Hao 		 * Sub-feature's ioctl returns -ENODEV when cmd is not
7711a1527cfSWu Hao 		 * handled in this sub feature, and returns 0 and other
7721a1527cfSWu Hao 		 * error code if cmd is handled.
7731a1527cfSWu Hao 		 */
7741a1527cfSWu Hao 		dfl_fpga_dev_for_each_feature(pdata, f)
7751a1527cfSWu Hao 			if (f->ops && f->ops->ioctl) {
7761a1527cfSWu Hao 				ret = f->ops->ioctl(pdev, f, cmd, arg);
7771a1527cfSWu Hao 				if (ret != -ENODEV)
7781a1527cfSWu Hao 					return ret;
7791a1527cfSWu Hao 			}
7801a1527cfSWu Hao 	}
7811a1527cfSWu Hao 
7821a1527cfSWu Hao 	return -EINVAL;
7831a1527cfSWu Hao }
7841a1527cfSWu Hao 
785a2b9d4eaSDominic Chen static const struct vm_operations_struct afu_vma_ops = {
786a2b9d4eaSDominic Chen #ifdef CONFIG_HAVE_IOREMAP_PROT
787a2b9d4eaSDominic Chen 	.access = generic_access_phys,
788a2b9d4eaSDominic Chen #endif
789a2b9d4eaSDominic Chen };
790a2b9d4eaSDominic Chen 
791857a2622SXiao Guangrong static int afu_mmap(struct file *filp, struct vm_area_struct *vma)
792857a2622SXiao Guangrong {
793857a2622SXiao Guangrong 	struct platform_device *pdev = filp->private_data;
794857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata;
795857a2622SXiao Guangrong 	u64 size = vma->vm_end - vma->vm_start;
796857a2622SXiao Guangrong 	struct dfl_afu_mmio_region region;
797857a2622SXiao Guangrong 	u64 offset;
798857a2622SXiao Guangrong 	int ret;
799857a2622SXiao Guangrong 
800857a2622SXiao Guangrong 	if (!(vma->vm_flags & VM_SHARED))
801857a2622SXiao Guangrong 		return -EINVAL;
802857a2622SXiao Guangrong 
803857a2622SXiao Guangrong 	pdata = dev_get_platdata(&pdev->dev);
804857a2622SXiao Guangrong 
805857a2622SXiao Guangrong 	offset = vma->vm_pgoff << PAGE_SHIFT;
806857a2622SXiao Guangrong 	ret = afu_mmio_region_get_by_offset(pdata, offset, size, &region);
807857a2622SXiao Guangrong 	if (ret)
808857a2622SXiao Guangrong 		return ret;
809857a2622SXiao Guangrong 
810857a2622SXiao Guangrong 	if (!(region.flags & DFL_PORT_REGION_MMAP))
811857a2622SXiao Guangrong 		return -EINVAL;
812857a2622SXiao Guangrong 
813857a2622SXiao Guangrong 	if ((vma->vm_flags & VM_READ) && !(region.flags & DFL_PORT_REGION_READ))
814857a2622SXiao Guangrong 		return -EPERM;
815857a2622SXiao Guangrong 
816857a2622SXiao Guangrong 	if ((vma->vm_flags & VM_WRITE) &&
817857a2622SXiao Guangrong 	    !(region.flags & DFL_PORT_REGION_WRITE))
818857a2622SXiao Guangrong 		return -EPERM;
819857a2622SXiao Guangrong 
820a2b9d4eaSDominic Chen 	/* Support debug access to the mapping */
821a2b9d4eaSDominic Chen 	vma->vm_ops = &afu_vma_ops;
822a2b9d4eaSDominic Chen 
823857a2622SXiao Guangrong 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
824857a2622SXiao Guangrong 
825857a2622SXiao Guangrong 	return remap_pfn_range(vma, vma->vm_start,
826857a2622SXiao Guangrong 			(region.phys + (offset - region.offset)) >> PAGE_SHIFT,
827857a2622SXiao Guangrong 			size, vma->vm_page_prot);
828857a2622SXiao Guangrong }
829857a2622SXiao Guangrong 
8301a1527cfSWu Hao static const struct file_operations afu_fops = {
8311a1527cfSWu Hao 	.owner = THIS_MODULE,
8321a1527cfSWu Hao 	.open = afu_open,
8331a1527cfSWu Hao 	.release = afu_release,
8341a1527cfSWu Hao 	.unlocked_ioctl = afu_ioctl,
835857a2622SXiao Guangrong 	.mmap = afu_mmap,
8361a1527cfSWu Hao };
8371a1527cfSWu Hao 
838857a2622SXiao Guangrong static int afu_dev_init(struct platform_device *pdev)
839857a2622SXiao Guangrong {
840857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
841857a2622SXiao Guangrong 	struct dfl_afu *afu;
842857a2622SXiao Guangrong 
843857a2622SXiao Guangrong 	afu = devm_kzalloc(&pdev->dev, sizeof(*afu), GFP_KERNEL);
844857a2622SXiao Guangrong 	if (!afu)
845857a2622SXiao Guangrong 		return -ENOMEM;
846857a2622SXiao Guangrong 
847857a2622SXiao Guangrong 	afu->pdata = pdata;
848857a2622SXiao Guangrong 
849857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
850857a2622SXiao Guangrong 	dfl_fpga_pdata_set_private(pdata, afu);
851857a2622SXiao Guangrong 	afu_mmio_region_init(pdata);
852fa8dda1eSWu Hao 	afu_dma_region_init(pdata);
853857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
854857a2622SXiao Guangrong 
855857a2622SXiao Guangrong 	return 0;
856857a2622SXiao Guangrong }
857857a2622SXiao Guangrong 
858857a2622SXiao Guangrong static int afu_dev_destroy(struct platform_device *pdev)
859857a2622SXiao Guangrong {
860857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
861857a2622SXiao Guangrong 
862857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
863857a2622SXiao Guangrong 	afu_mmio_region_destroy(pdata);
864fa8dda1eSWu Hao 	afu_dma_region_destroy(pdata);
865857a2622SXiao Guangrong 	dfl_fpga_pdata_set_private(pdata, NULL);
866857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
867857a2622SXiao Guangrong 
868857a2622SXiao Guangrong 	return 0;
869857a2622SXiao Guangrong }
870857a2622SXiao Guangrong 
87147c1b19cSWu Hao static int port_enable_set(struct platform_device *pdev, bool enable)
87247c1b19cSWu Hao {
87347c1b19cSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
87447c1b19cSWu Hao 	int ret = 0;
87547c1b19cSWu Hao 
87647c1b19cSWu Hao 	mutex_lock(&pdata->lock);
87747c1b19cSWu Hao 	if (enable)
87895844372SWu Hao 		__afu_port_enable(pdev);
87947c1b19cSWu Hao 	else
88095844372SWu Hao 		ret = __afu_port_disable(pdev);
88147c1b19cSWu Hao 	mutex_unlock(&pdata->lock);
88247c1b19cSWu Hao 
88347c1b19cSWu Hao 	return ret;
88447c1b19cSWu Hao }
88547c1b19cSWu Hao 
88647c1b19cSWu Hao static struct dfl_fpga_port_ops afu_port_ops = {
88747c1b19cSWu Hao 	.name = DFL_FPGA_FEATURE_DEV_PORT,
88847c1b19cSWu Hao 	.owner = THIS_MODULE,
88947c1b19cSWu Hao 	.get_id = port_get_id,
89047c1b19cSWu Hao 	.enable_set = port_enable_set,
89147c1b19cSWu Hao };
89247c1b19cSWu Hao 
8931a1527cfSWu Hao static int afu_probe(struct platform_device *pdev)
8941a1527cfSWu Hao {
8951a1527cfSWu Hao 	int ret;
8961a1527cfSWu Hao 
8971a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s\n", __func__);
8981a1527cfSWu Hao 
899857a2622SXiao Guangrong 	ret = afu_dev_init(pdev);
900857a2622SXiao Guangrong 	if (ret)
901857a2622SXiao Guangrong 		goto exit;
902857a2622SXiao Guangrong 
9031a1527cfSWu Hao 	ret = dfl_fpga_dev_feature_init(pdev, port_feature_drvs);
9041a1527cfSWu Hao 	if (ret)
905857a2622SXiao Guangrong 		goto dev_destroy;
9061a1527cfSWu Hao 
9071a1527cfSWu Hao 	ret = dfl_fpga_dev_ops_register(pdev, &afu_fops, THIS_MODULE);
908857a2622SXiao Guangrong 	if (ret) {
9091a1527cfSWu Hao 		dfl_fpga_dev_feature_uinit(pdev);
910857a2622SXiao Guangrong 		goto dev_destroy;
911857a2622SXiao Guangrong 	}
9121a1527cfSWu Hao 
913857a2622SXiao Guangrong 	return 0;
914857a2622SXiao Guangrong 
915857a2622SXiao Guangrong dev_destroy:
916857a2622SXiao Guangrong 	afu_dev_destroy(pdev);
917857a2622SXiao Guangrong exit:
9181a1527cfSWu Hao 	return ret;
9191a1527cfSWu Hao }
9201a1527cfSWu Hao 
9211a1527cfSWu Hao static int afu_remove(struct platform_device *pdev)
9221a1527cfSWu Hao {
9231a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s\n", __func__);
9241a1527cfSWu Hao 
9251a1527cfSWu Hao 	dfl_fpga_dev_ops_unregister(pdev);
9261a1527cfSWu Hao 	dfl_fpga_dev_feature_uinit(pdev);
927857a2622SXiao Guangrong 	afu_dev_destroy(pdev);
9281a1527cfSWu Hao 
9291a1527cfSWu Hao 	return 0;
9301a1527cfSWu Hao }
9311a1527cfSWu Hao 
932a80a4b82SWu Hao static const struct attribute_group *afu_dev_groups[] = {
933a80a4b82SWu Hao 	&port_hdr_group,
934a80a4b82SWu Hao 	&port_afu_group,
93544d24753SWu Hao 	&port_err_group,
936a80a4b82SWu Hao 	NULL
937a80a4b82SWu Hao };
938a80a4b82SWu Hao 
9391a1527cfSWu Hao static struct platform_driver afu_driver = {
9401a1527cfSWu Hao 	.driver	= {
9411a1527cfSWu Hao 		.name	    = DFL_FPGA_FEATURE_DEV_PORT,
942a80a4b82SWu Hao 		.dev_groups = afu_dev_groups,
9431a1527cfSWu Hao 	},
9441a1527cfSWu Hao 	.probe   = afu_probe,
9451a1527cfSWu Hao 	.remove  = afu_remove,
9461a1527cfSWu Hao };
9471a1527cfSWu Hao 
94847c1b19cSWu Hao static int __init afu_init(void)
94947c1b19cSWu Hao {
95047c1b19cSWu Hao 	int ret;
95147c1b19cSWu Hao 
95247c1b19cSWu Hao 	dfl_fpga_port_ops_add(&afu_port_ops);
95347c1b19cSWu Hao 
95447c1b19cSWu Hao 	ret = platform_driver_register(&afu_driver);
95547c1b19cSWu Hao 	if (ret)
95647c1b19cSWu Hao 		dfl_fpga_port_ops_del(&afu_port_ops);
95747c1b19cSWu Hao 
95847c1b19cSWu Hao 	return ret;
95947c1b19cSWu Hao }
96047c1b19cSWu Hao 
96147c1b19cSWu Hao static void __exit afu_exit(void)
96247c1b19cSWu Hao {
96347c1b19cSWu Hao 	platform_driver_unregister(&afu_driver);
96447c1b19cSWu Hao 
96547c1b19cSWu Hao 	dfl_fpga_port_ops_del(&afu_port_ops);
96647c1b19cSWu Hao }
96747c1b19cSWu Hao 
96847c1b19cSWu Hao module_init(afu_init);
96947c1b19cSWu Hao module_exit(afu_exit);
9701a1527cfSWu Hao 
9711a1527cfSWu Hao MODULE_DESCRIPTION("FPGA Accelerated Function Unit driver");
9721a1527cfSWu Hao MODULE_AUTHOR("Intel Corporation");
9731a1527cfSWu Hao MODULE_LICENSE("GPL v2");
9741a1527cfSWu Hao MODULE_ALIAS("platform:dfl-port");
975