xref: /openbmc/linux/drivers/fpga/dfl-afu-main.c (revision b6862193ca12e4bce6a18f31bb36eaa6d801b377)
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 
5321a1527cfSWu Hao static struct dfl_feature_driver port_feature_drvs[] = {
5331a1527cfSWu Hao 	{
53415bbb300SWu Hao 		.id_table = port_hdr_id_table,
5351a1527cfSWu Hao 		.ops = &port_hdr_ops,
5361a1527cfSWu Hao 	},
5371a1527cfSWu Hao 	{
53815bbb300SWu Hao 		.id_table = port_afu_id_table,
539857a2622SXiao Guangrong 		.ops = &port_afu_ops,
540857a2622SXiao Guangrong 	},
541857a2622SXiao Guangrong 	{
54244d24753SWu Hao 		.id_table = port_err_id_table,
54344d24753SWu Hao 		.ops = &port_err_ops,
54444d24753SWu Hao 	},
54544d24753SWu Hao 	{
546bd127b81SWu Hao 		.id_table = port_stp_id_table,
547bd127b81SWu Hao 		.ops = &port_stp_ops,
548bd127b81SWu Hao 	},
549bd127b81SWu Hao 	{
5501a1527cfSWu Hao 		.ops = NULL,
5511a1527cfSWu Hao 	}
5521a1527cfSWu Hao };
5531a1527cfSWu Hao 
5541a1527cfSWu Hao static int afu_open(struct inode *inode, struct file *filp)
5551a1527cfSWu Hao {
5561a1527cfSWu Hao 	struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
5571a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
5581a1527cfSWu Hao 	int ret;
5591a1527cfSWu Hao 
5601a1527cfSWu Hao 	pdata = dev_get_platdata(&fdev->dev);
5611a1527cfSWu Hao 	if (WARN_ON(!pdata))
5621a1527cfSWu Hao 		return -ENODEV;
5631a1527cfSWu Hao 
564*b6862193SXu Yilun 	mutex_lock(&pdata->lock);
565*b6862193SXu Yilun 	ret = dfl_feature_dev_use_begin(pdata, filp->f_flags & O_EXCL);
566*b6862193SXu Yilun 	if (!ret) {
567*b6862193SXu Yilun 		dev_dbg(&fdev->dev, "Device File Opened %d Times\n",
568*b6862193SXu Yilun 			dfl_feature_dev_use_count(pdata));
5691a1527cfSWu Hao 		filp->private_data = fdev;
570*b6862193SXu Yilun 	}
571*b6862193SXu Yilun 	mutex_unlock(&pdata->lock);
5721a1527cfSWu Hao 
573*b6862193SXu Yilun 	return ret;
5741a1527cfSWu Hao }
5751a1527cfSWu Hao 
5761a1527cfSWu Hao static int afu_release(struct inode *inode, struct file *filp)
5771a1527cfSWu Hao {
5781a1527cfSWu Hao 	struct platform_device *pdev = filp->private_data;
5791a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
5801a1527cfSWu Hao 
5811a1527cfSWu Hao 	dev_dbg(&pdev->dev, "Device File Release\n");
5821a1527cfSWu Hao 
5831a1527cfSWu Hao 	pdata = dev_get_platdata(&pdev->dev);
5841a1527cfSWu Hao 
585fa8dda1eSWu Hao 	mutex_lock(&pdata->lock);
586*b6862193SXu Yilun 	dfl_feature_dev_use_end(pdata);
587*b6862193SXu Yilun 
588*b6862193SXu Yilun 	if (!dfl_feature_dev_use_count(pdata)) {
589fa8dda1eSWu Hao 		__port_reset(pdev);
590fa8dda1eSWu Hao 		afu_dma_region_destroy(pdata);
591*b6862193SXu Yilun 	}
592fa8dda1eSWu Hao 	mutex_unlock(&pdata->lock);
593fa8dda1eSWu Hao 
5941a1527cfSWu Hao 	return 0;
5951a1527cfSWu Hao }
5961a1527cfSWu Hao 
5976fd893c4SWu Hao static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
5986fd893c4SWu Hao 				      unsigned long arg)
5996fd893c4SWu Hao {
6006fd893c4SWu Hao 	/* No extension support for now */
6016fd893c4SWu Hao 	return 0;
6026fd893c4SWu Hao }
6036fd893c4SWu Hao 
604857a2622SXiao Guangrong static long
605857a2622SXiao Guangrong afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg)
606857a2622SXiao Guangrong {
607857a2622SXiao Guangrong 	struct dfl_fpga_port_info info;
608857a2622SXiao Guangrong 	struct dfl_afu *afu;
609857a2622SXiao Guangrong 	unsigned long minsz;
610857a2622SXiao Guangrong 
611857a2622SXiao Guangrong 	minsz = offsetofend(struct dfl_fpga_port_info, num_umsgs);
612857a2622SXiao Guangrong 
613857a2622SXiao Guangrong 	if (copy_from_user(&info, arg, minsz))
614857a2622SXiao Guangrong 		return -EFAULT;
615857a2622SXiao Guangrong 
616857a2622SXiao Guangrong 	if (info.argsz < minsz)
617857a2622SXiao Guangrong 		return -EINVAL;
618857a2622SXiao Guangrong 
619857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
620857a2622SXiao Guangrong 	afu = dfl_fpga_pdata_get_private(pdata);
621857a2622SXiao Guangrong 	info.flags = 0;
622857a2622SXiao Guangrong 	info.num_regions = afu->num_regions;
623857a2622SXiao Guangrong 	info.num_umsgs = afu->num_umsgs;
624857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
625857a2622SXiao Guangrong 
626857a2622SXiao Guangrong 	if (copy_to_user(arg, &info, sizeof(info)))
627857a2622SXiao Guangrong 		return -EFAULT;
628857a2622SXiao Guangrong 
629857a2622SXiao Guangrong 	return 0;
630857a2622SXiao Guangrong }
631857a2622SXiao Guangrong 
632857a2622SXiao Guangrong static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata,
633857a2622SXiao Guangrong 				      void __user *arg)
634857a2622SXiao Guangrong {
635857a2622SXiao Guangrong 	struct dfl_fpga_port_region_info rinfo;
636857a2622SXiao Guangrong 	struct dfl_afu_mmio_region region;
637857a2622SXiao Guangrong 	unsigned long minsz;
638857a2622SXiao Guangrong 	long ret;
639857a2622SXiao Guangrong 
640857a2622SXiao Guangrong 	minsz = offsetofend(struct dfl_fpga_port_region_info, offset);
641857a2622SXiao Guangrong 
642857a2622SXiao Guangrong 	if (copy_from_user(&rinfo, arg, minsz))
643857a2622SXiao Guangrong 		return -EFAULT;
644857a2622SXiao Guangrong 
645857a2622SXiao Guangrong 	if (rinfo.argsz < minsz || rinfo.padding)
646857a2622SXiao Guangrong 		return -EINVAL;
647857a2622SXiao Guangrong 
648857a2622SXiao Guangrong 	ret = afu_mmio_region_get_by_index(pdata, rinfo.index, &region);
649857a2622SXiao Guangrong 	if (ret)
650857a2622SXiao Guangrong 		return ret;
651857a2622SXiao Guangrong 
652857a2622SXiao Guangrong 	rinfo.flags = region.flags;
653857a2622SXiao Guangrong 	rinfo.size = region.size;
654857a2622SXiao Guangrong 	rinfo.offset = region.offset;
655857a2622SXiao Guangrong 
656857a2622SXiao Guangrong 	if (copy_to_user(arg, &rinfo, sizeof(rinfo)))
657857a2622SXiao Guangrong 		return -EFAULT;
658857a2622SXiao Guangrong 
659857a2622SXiao Guangrong 	return 0;
660857a2622SXiao Guangrong }
661857a2622SXiao Guangrong 
662fa8dda1eSWu Hao static long
663fa8dda1eSWu Hao afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg)
664fa8dda1eSWu Hao {
665fa8dda1eSWu Hao 	struct dfl_fpga_port_dma_map map;
666fa8dda1eSWu Hao 	unsigned long minsz;
667fa8dda1eSWu Hao 	long ret;
668fa8dda1eSWu Hao 
669fa8dda1eSWu Hao 	minsz = offsetofend(struct dfl_fpga_port_dma_map, iova);
670fa8dda1eSWu Hao 
671fa8dda1eSWu Hao 	if (copy_from_user(&map, arg, minsz))
672fa8dda1eSWu Hao 		return -EFAULT;
673fa8dda1eSWu Hao 
674fa8dda1eSWu Hao 	if (map.argsz < minsz || map.flags)
675fa8dda1eSWu Hao 		return -EINVAL;
676fa8dda1eSWu Hao 
677fa8dda1eSWu Hao 	ret = afu_dma_map_region(pdata, map.user_addr, map.length, &map.iova);
678fa8dda1eSWu Hao 	if (ret)
679fa8dda1eSWu Hao 		return ret;
680fa8dda1eSWu Hao 
681fa8dda1eSWu Hao 	if (copy_to_user(arg, &map, sizeof(map))) {
682fa8dda1eSWu Hao 		afu_dma_unmap_region(pdata, map.iova);
683fa8dda1eSWu Hao 		return -EFAULT;
684fa8dda1eSWu Hao 	}
685fa8dda1eSWu Hao 
686fa8dda1eSWu Hao 	dev_dbg(&pdata->dev->dev, "dma map: ua=%llx, len=%llx, iova=%llx\n",
687fa8dda1eSWu Hao 		(unsigned long long)map.user_addr,
688fa8dda1eSWu Hao 		(unsigned long long)map.length,
689fa8dda1eSWu Hao 		(unsigned long long)map.iova);
690fa8dda1eSWu Hao 
691fa8dda1eSWu Hao 	return 0;
692fa8dda1eSWu Hao }
693fa8dda1eSWu Hao 
694fa8dda1eSWu Hao static long
695fa8dda1eSWu Hao afu_ioctl_dma_unmap(struct dfl_feature_platform_data *pdata, void __user *arg)
696fa8dda1eSWu Hao {
697fa8dda1eSWu Hao 	struct dfl_fpga_port_dma_unmap unmap;
698fa8dda1eSWu Hao 	unsigned long minsz;
699fa8dda1eSWu Hao 
700fa8dda1eSWu Hao 	minsz = offsetofend(struct dfl_fpga_port_dma_unmap, iova);
701fa8dda1eSWu Hao 
702fa8dda1eSWu Hao 	if (copy_from_user(&unmap, arg, minsz))
703fa8dda1eSWu Hao 		return -EFAULT;
704fa8dda1eSWu Hao 
705fa8dda1eSWu Hao 	if (unmap.argsz < minsz || unmap.flags)
706fa8dda1eSWu Hao 		return -EINVAL;
707fa8dda1eSWu Hao 
708fa8dda1eSWu Hao 	return afu_dma_unmap_region(pdata, unmap.iova);
709fa8dda1eSWu Hao }
710fa8dda1eSWu Hao 
7111a1527cfSWu Hao static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
7121a1527cfSWu Hao {
7131a1527cfSWu Hao 	struct platform_device *pdev = filp->private_data;
7141a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
7151a1527cfSWu Hao 	struct dfl_feature *f;
7161a1527cfSWu Hao 	long ret;
7171a1527cfSWu Hao 
7181a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd);
7191a1527cfSWu Hao 
7201a1527cfSWu Hao 	pdata = dev_get_platdata(&pdev->dev);
7211a1527cfSWu Hao 
7221a1527cfSWu Hao 	switch (cmd) {
7236fd893c4SWu Hao 	case DFL_FPGA_GET_API_VERSION:
7246fd893c4SWu Hao 		return DFL_FPGA_API_VERSION;
7256fd893c4SWu Hao 	case DFL_FPGA_CHECK_EXTENSION:
7266fd893c4SWu Hao 		return afu_ioctl_check_extension(pdata, arg);
727857a2622SXiao Guangrong 	case DFL_FPGA_PORT_GET_INFO:
728857a2622SXiao Guangrong 		return afu_ioctl_get_info(pdata, (void __user *)arg);
729857a2622SXiao Guangrong 	case DFL_FPGA_PORT_GET_REGION_INFO:
730857a2622SXiao Guangrong 		return afu_ioctl_get_region_info(pdata, (void __user *)arg);
731fa8dda1eSWu Hao 	case DFL_FPGA_PORT_DMA_MAP:
732fa8dda1eSWu Hao 		return afu_ioctl_dma_map(pdata, (void __user *)arg);
733fa8dda1eSWu Hao 	case DFL_FPGA_PORT_DMA_UNMAP:
734fa8dda1eSWu Hao 		return afu_ioctl_dma_unmap(pdata, (void __user *)arg);
7351a1527cfSWu Hao 	default:
7361a1527cfSWu Hao 		/*
7371a1527cfSWu Hao 		 * Let sub-feature's ioctl function to handle the cmd
7381a1527cfSWu Hao 		 * Sub-feature's ioctl returns -ENODEV when cmd is not
7391a1527cfSWu Hao 		 * handled in this sub feature, and returns 0 and other
7401a1527cfSWu Hao 		 * error code if cmd is handled.
7411a1527cfSWu Hao 		 */
7421a1527cfSWu Hao 		dfl_fpga_dev_for_each_feature(pdata, f)
7431a1527cfSWu Hao 			if (f->ops && f->ops->ioctl) {
7441a1527cfSWu Hao 				ret = f->ops->ioctl(pdev, f, cmd, arg);
7451a1527cfSWu Hao 				if (ret != -ENODEV)
7461a1527cfSWu Hao 					return ret;
7471a1527cfSWu Hao 			}
7481a1527cfSWu Hao 	}
7491a1527cfSWu Hao 
7501a1527cfSWu Hao 	return -EINVAL;
7511a1527cfSWu Hao }
7521a1527cfSWu Hao 
753857a2622SXiao Guangrong static int afu_mmap(struct file *filp, struct vm_area_struct *vma)
754857a2622SXiao Guangrong {
755857a2622SXiao Guangrong 	struct platform_device *pdev = filp->private_data;
756857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata;
757857a2622SXiao Guangrong 	u64 size = vma->vm_end - vma->vm_start;
758857a2622SXiao Guangrong 	struct dfl_afu_mmio_region region;
759857a2622SXiao Guangrong 	u64 offset;
760857a2622SXiao Guangrong 	int ret;
761857a2622SXiao Guangrong 
762857a2622SXiao Guangrong 	if (!(vma->vm_flags & VM_SHARED))
763857a2622SXiao Guangrong 		return -EINVAL;
764857a2622SXiao Guangrong 
765857a2622SXiao Guangrong 	pdata = dev_get_platdata(&pdev->dev);
766857a2622SXiao Guangrong 
767857a2622SXiao Guangrong 	offset = vma->vm_pgoff << PAGE_SHIFT;
768857a2622SXiao Guangrong 	ret = afu_mmio_region_get_by_offset(pdata, offset, size, &region);
769857a2622SXiao Guangrong 	if (ret)
770857a2622SXiao Guangrong 		return ret;
771857a2622SXiao Guangrong 
772857a2622SXiao Guangrong 	if (!(region.flags & DFL_PORT_REGION_MMAP))
773857a2622SXiao Guangrong 		return -EINVAL;
774857a2622SXiao Guangrong 
775857a2622SXiao Guangrong 	if ((vma->vm_flags & VM_READ) && !(region.flags & DFL_PORT_REGION_READ))
776857a2622SXiao Guangrong 		return -EPERM;
777857a2622SXiao Guangrong 
778857a2622SXiao Guangrong 	if ((vma->vm_flags & VM_WRITE) &&
779857a2622SXiao Guangrong 	    !(region.flags & DFL_PORT_REGION_WRITE))
780857a2622SXiao Guangrong 		return -EPERM;
781857a2622SXiao Guangrong 
782857a2622SXiao Guangrong 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
783857a2622SXiao Guangrong 
784857a2622SXiao Guangrong 	return remap_pfn_range(vma, vma->vm_start,
785857a2622SXiao Guangrong 			(region.phys + (offset - region.offset)) >> PAGE_SHIFT,
786857a2622SXiao Guangrong 			size, vma->vm_page_prot);
787857a2622SXiao Guangrong }
788857a2622SXiao Guangrong 
7891a1527cfSWu Hao static const struct file_operations afu_fops = {
7901a1527cfSWu Hao 	.owner = THIS_MODULE,
7911a1527cfSWu Hao 	.open = afu_open,
7921a1527cfSWu Hao 	.release = afu_release,
7931a1527cfSWu Hao 	.unlocked_ioctl = afu_ioctl,
794857a2622SXiao Guangrong 	.mmap = afu_mmap,
7951a1527cfSWu Hao };
7961a1527cfSWu Hao 
797857a2622SXiao Guangrong static int afu_dev_init(struct platform_device *pdev)
798857a2622SXiao Guangrong {
799857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
800857a2622SXiao Guangrong 	struct dfl_afu *afu;
801857a2622SXiao Guangrong 
802857a2622SXiao Guangrong 	afu = devm_kzalloc(&pdev->dev, sizeof(*afu), GFP_KERNEL);
803857a2622SXiao Guangrong 	if (!afu)
804857a2622SXiao Guangrong 		return -ENOMEM;
805857a2622SXiao Guangrong 
806857a2622SXiao Guangrong 	afu->pdata = pdata;
807857a2622SXiao Guangrong 
808857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
809857a2622SXiao Guangrong 	dfl_fpga_pdata_set_private(pdata, afu);
810857a2622SXiao Guangrong 	afu_mmio_region_init(pdata);
811fa8dda1eSWu Hao 	afu_dma_region_init(pdata);
812857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
813857a2622SXiao Guangrong 
814857a2622SXiao Guangrong 	return 0;
815857a2622SXiao Guangrong }
816857a2622SXiao Guangrong 
817857a2622SXiao Guangrong static int afu_dev_destroy(struct platform_device *pdev)
818857a2622SXiao Guangrong {
819857a2622SXiao Guangrong 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
820857a2622SXiao Guangrong 
821857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
822857a2622SXiao Guangrong 	afu_mmio_region_destroy(pdata);
823fa8dda1eSWu Hao 	afu_dma_region_destroy(pdata);
824857a2622SXiao Guangrong 	dfl_fpga_pdata_set_private(pdata, NULL);
825857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
826857a2622SXiao Guangrong 
827857a2622SXiao Guangrong 	return 0;
828857a2622SXiao Guangrong }
829857a2622SXiao Guangrong 
83047c1b19cSWu Hao static int port_enable_set(struct platform_device *pdev, bool enable)
83147c1b19cSWu Hao {
83247c1b19cSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
83347c1b19cSWu Hao 	int ret = 0;
83447c1b19cSWu Hao 
83547c1b19cSWu Hao 	mutex_lock(&pdata->lock);
83647c1b19cSWu Hao 	if (enable)
83795844372SWu Hao 		__afu_port_enable(pdev);
83847c1b19cSWu Hao 	else
83995844372SWu Hao 		ret = __afu_port_disable(pdev);
84047c1b19cSWu Hao 	mutex_unlock(&pdata->lock);
84147c1b19cSWu Hao 
84247c1b19cSWu Hao 	return ret;
84347c1b19cSWu Hao }
84447c1b19cSWu Hao 
84547c1b19cSWu Hao static struct dfl_fpga_port_ops afu_port_ops = {
84647c1b19cSWu Hao 	.name = DFL_FPGA_FEATURE_DEV_PORT,
84747c1b19cSWu Hao 	.owner = THIS_MODULE,
84847c1b19cSWu Hao 	.get_id = port_get_id,
84947c1b19cSWu Hao 	.enable_set = port_enable_set,
85047c1b19cSWu Hao };
85147c1b19cSWu Hao 
8521a1527cfSWu Hao static int afu_probe(struct platform_device *pdev)
8531a1527cfSWu Hao {
8541a1527cfSWu Hao 	int ret;
8551a1527cfSWu Hao 
8561a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s\n", __func__);
8571a1527cfSWu Hao 
858857a2622SXiao Guangrong 	ret = afu_dev_init(pdev);
859857a2622SXiao Guangrong 	if (ret)
860857a2622SXiao Guangrong 		goto exit;
861857a2622SXiao Guangrong 
8621a1527cfSWu Hao 	ret = dfl_fpga_dev_feature_init(pdev, port_feature_drvs);
8631a1527cfSWu Hao 	if (ret)
864857a2622SXiao Guangrong 		goto dev_destroy;
8651a1527cfSWu Hao 
8661a1527cfSWu Hao 	ret = dfl_fpga_dev_ops_register(pdev, &afu_fops, THIS_MODULE);
867857a2622SXiao Guangrong 	if (ret) {
8681a1527cfSWu Hao 		dfl_fpga_dev_feature_uinit(pdev);
869857a2622SXiao Guangrong 		goto dev_destroy;
870857a2622SXiao Guangrong 	}
8711a1527cfSWu Hao 
872857a2622SXiao Guangrong 	return 0;
873857a2622SXiao Guangrong 
874857a2622SXiao Guangrong dev_destroy:
875857a2622SXiao Guangrong 	afu_dev_destroy(pdev);
876857a2622SXiao Guangrong exit:
8771a1527cfSWu Hao 	return ret;
8781a1527cfSWu Hao }
8791a1527cfSWu Hao 
8801a1527cfSWu Hao static int afu_remove(struct platform_device *pdev)
8811a1527cfSWu Hao {
8821a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s\n", __func__);
8831a1527cfSWu Hao 
8841a1527cfSWu Hao 	dfl_fpga_dev_ops_unregister(pdev);
8851a1527cfSWu Hao 	dfl_fpga_dev_feature_uinit(pdev);
886857a2622SXiao Guangrong 	afu_dev_destroy(pdev);
8871a1527cfSWu Hao 
8881a1527cfSWu Hao 	return 0;
8891a1527cfSWu Hao }
8901a1527cfSWu Hao 
891a80a4b82SWu Hao static const struct attribute_group *afu_dev_groups[] = {
892a80a4b82SWu Hao 	&port_hdr_group,
893a80a4b82SWu Hao 	&port_afu_group,
89444d24753SWu Hao 	&port_err_group,
895a80a4b82SWu Hao 	NULL
896a80a4b82SWu Hao };
897a80a4b82SWu Hao 
8981a1527cfSWu Hao static struct platform_driver afu_driver = {
8991a1527cfSWu Hao 	.driver	= {
9001a1527cfSWu Hao 		.name	    = DFL_FPGA_FEATURE_DEV_PORT,
901a80a4b82SWu Hao 		.dev_groups = afu_dev_groups,
9021a1527cfSWu Hao 	},
9031a1527cfSWu Hao 	.probe   = afu_probe,
9041a1527cfSWu Hao 	.remove  = afu_remove,
9051a1527cfSWu Hao };
9061a1527cfSWu Hao 
90747c1b19cSWu Hao static int __init afu_init(void)
90847c1b19cSWu Hao {
90947c1b19cSWu Hao 	int ret;
91047c1b19cSWu Hao 
91147c1b19cSWu Hao 	dfl_fpga_port_ops_add(&afu_port_ops);
91247c1b19cSWu Hao 
91347c1b19cSWu Hao 	ret = platform_driver_register(&afu_driver);
91447c1b19cSWu Hao 	if (ret)
91547c1b19cSWu Hao 		dfl_fpga_port_ops_del(&afu_port_ops);
91647c1b19cSWu Hao 
91747c1b19cSWu Hao 	return ret;
91847c1b19cSWu Hao }
91947c1b19cSWu Hao 
92047c1b19cSWu Hao static void __exit afu_exit(void)
92147c1b19cSWu Hao {
92247c1b19cSWu Hao 	platform_driver_unregister(&afu_driver);
92347c1b19cSWu Hao 
92447c1b19cSWu Hao 	dfl_fpga_port_ops_del(&afu_port_ops);
92547c1b19cSWu Hao }
92647c1b19cSWu Hao 
92747c1b19cSWu Hao module_init(afu_init);
92847c1b19cSWu Hao module_exit(afu_exit);
9291a1527cfSWu Hao 
9301a1527cfSWu Hao MODULE_DESCRIPTION("FPGA Accelerated Function Unit driver");
9311a1527cfSWu Hao MODULE_AUTHOR("Intel Corporation");
9321a1527cfSWu Hao MODULE_LICENSE("GPL v2");
9331a1527cfSWu Hao MODULE_ALIAS("platform:dfl-port");
934