xref: /openbmc/linux/drivers/fpga/dfl-afu-main.c (revision e4664c0ee4ac44993c62d10b048ab0a960691da5)
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>
19*e4664c0eSWu Hao #include <linux/fpga-dfl.h>
201a1527cfSWu Hao 
211a1527cfSWu Hao #include "dfl.h"
221a1527cfSWu Hao 
2347c1b19cSWu Hao /**
2447c1b19cSWu Hao  * port_enable - enable a port
2547c1b19cSWu Hao  * @pdev: port platform device.
2647c1b19cSWu Hao  *
2747c1b19cSWu Hao  * Enable Port by clear the port soft reset bit, which is set by default.
2847c1b19cSWu Hao  * The User AFU is unable to respond to any MMIO access while in reset.
2947c1b19cSWu Hao  * port_enable function should only be used after port_disable
3047c1b19cSWu Hao  * function.
3147c1b19cSWu Hao  */
3247c1b19cSWu Hao static void port_enable(struct platform_device *pdev)
3347c1b19cSWu Hao {
3447c1b19cSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
3547c1b19cSWu Hao 	void __iomem *base;
3647c1b19cSWu Hao 	u64 v;
3747c1b19cSWu Hao 
3847c1b19cSWu Hao 	WARN_ON(!pdata->disable_count);
3947c1b19cSWu Hao 
4047c1b19cSWu Hao 	if (--pdata->disable_count != 0)
4147c1b19cSWu Hao 		return;
4247c1b19cSWu Hao 
4347c1b19cSWu Hao 	base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
4447c1b19cSWu Hao 
4547c1b19cSWu Hao 	/* Clear port soft reset */
4647c1b19cSWu Hao 	v = readq(base + PORT_HDR_CTRL);
4747c1b19cSWu Hao 	v &= ~PORT_CTRL_SFTRST;
4847c1b19cSWu Hao 	writeq(v, base + PORT_HDR_CTRL);
4947c1b19cSWu Hao }
5047c1b19cSWu Hao 
5147c1b19cSWu Hao #define RST_POLL_INVL 10 /* us */
5247c1b19cSWu Hao #define RST_POLL_TIMEOUT 1000 /* us */
5347c1b19cSWu Hao 
5447c1b19cSWu Hao /**
5547c1b19cSWu Hao  * port_disable - disable a port
5647c1b19cSWu Hao  * @pdev: port platform device.
5747c1b19cSWu Hao  *
5847c1b19cSWu Hao  * Disable Port by setting the port soft reset bit, it puts the port into
5947c1b19cSWu Hao  * reset.
6047c1b19cSWu Hao  */
6147c1b19cSWu Hao static int port_disable(struct platform_device *pdev)
6247c1b19cSWu Hao {
6347c1b19cSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
6447c1b19cSWu Hao 	void __iomem *base;
6547c1b19cSWu Hao 	u64 v;
6647c1b19cSWu Hao 
6747c1b19cSWu Hao 	if (pdata->disable_count++ != 0)
6847c1b19cSWu Hao 		return 0;
6947c1b19cSWu Hao 
7047c1b19cSWu Hao 	base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
7147c1b19cSWu Hao 
7247c1b19cSWu Hao 	/* Set port soft reset */
7347c1b19cSWu Hao 	v = readq(base + PORT_HDR_CTRL);
7447c1b19cSWu Hao 	v |= PORT_CTRL_SFTRST;
7547c1b19cSWu Hao 	writeq(v, base + PORT_HDR_CTRL);
7647c1b19cSWu Hao 
7747c1b19cSWu Hao 	/*
7847c1b19cSWu Hao 	 * HW sets ack bit to 1 when all outstanding requests have been drained
7947c1b19cSWu Hao 	 * on this port and minimum soft reset pulse width has elapsed.
8047c1b19cSWu Hao 	 * Driver polls port_soft_reset_ack to determine if reset done by HW.
8147c1b19cSWu Hao 	 */
8247c1b19cSWu Hao 	if (readq_poll_timeout(base + PORT_HDR_CTRL, v, v & PORT_CTRL_SFTRST,
8347c1b19cSWu Hao 			       RST_POLL_INVL, RST_POLL_TIMEOUT)) {
8447c1b19cSWu Hao 		dev_err(&pdev->dev, "timeout, fail to reset device\n");
8547c1b19cSWu Hao 		return -ETIMEDOUT;
8647c1b19cSWu Hao 	}
8747c1b19cSWu Hao 
8847c1b19cSWu Hao 	return 0;
8947c1b19cSWu Hao }
9047c1b19cSWu Hao 
91*e4664c0eSWu Hao /*
92*e4664c0eSWu Hao  * This function resets the FPGA Port and its accelerator (AFU) by function
93*e4664c0eSWu Hao  * __port_disable and __port_enable (set port soft reset bit and then clear
94*e4664c0eSWu Hao  * it). Userspace can do Port reset at any time, e.g. during DMA or Partial
95*e4664c0eSWu Hao  * Reconfiguration. But it should never cause any system level issue, only
96*e4664c0eSWu Hao  * functional failure (e.g. DMA or PR operation failure) and be recoverable
97*e4664c0eSWu Hao  * from the failure.
98*e4664c0eSWu Hao  *
99*e4664c0eSWu Hao  * Note: the accelerator (AFU) is not accessible when its port is in reset
100*e4664c0eSWu Hao  * (disabled). Any attempts on MMIO access to AFU while in reset, will
101*e4664c0eSWu Hao  * result errors reported via port error reporting sub feature (if present).
102*e4664c0eSWu Hao  */
103*e4664c0eSWu Hao static int __port_reset(struct platform_device *pdev)
104*e4664c0eSWu Hao {
105*e4664c0eSWu Hao 	int ret;
106*e4664c0eSWu Hao 
107*e4664c0eSWu Hao 	ret = port_disable(pdev);
108*e4664c0eSWu Hao 	if (!ret)
109*e4664c0eSWu Hao 		port_enable(pdev);
110*e4664c0eSWu Hao 
111*e4664c0eSWu Hao 	return ret;
112*e4664c0eSWu Hao }
113*e4664c0eSWu Hao 
114*e4664c0eSWu Hao static int port_reset(struct platform_device *pdev)
115*e4664c0eSWu Hao {
116*e4664c0eSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
117*e4664c0eSWu Hao 	int ret;
118*e4664c0eSWu Hao 
119*e4664c0eSWu Hao 	mutex_lock(&pdata->lock);
120*e4664c0eSWu Hao 	ret = __port_reset(pdev);
121*e4664c0eSWu Hao 	mutex_unlock(&pdata->lock);
122*e4664c0eSWu Hao 
123*e4664c0eSWu Hao 	return ret;
124*e4664c0eSWu Hao }
125*e4664c0eSWu Hao 
12647c1b19cSWu Hao static int port_get_id(struct platform_device *pdev)
12747c1b19cSWu Hao {
12847c1b19cSWu Hao 	void __iomem *base;
12947c1b19cSWu Hao 
13047c1b19cSWu Hao 	base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
13147c1b19cSWu Hao 
13247c1b19cSWu Hao 	return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + PORT_HDR_CAP));
13347c1b19cSWu Hao }
13447c1b19cSWu Hao 
135*e4664c0eSWu Hao static ssize_t
136*e4664c0eSWu Hao id_show(struct device *dev, struct device_attribute *attr, char *buf)
137*e4664c0eSWu Hao {
138*e4664c0eSWu Hao 	int id = port_get_id(to_platform_device(dev));
139*e4664c0eSWu Hao 
140*e4664c0eSWu Hao 	return scnprintf(buf, PAGE_SIZE, "%d\n", id);
141*e4664c0eSWu Hao }
142*e4664c0eSWu Hao static DEVICE_ATTR_RO(id);
143*e4664c0eSWu Hao 
144*e4664c0eSWu Hao static const struct attribute *port_hdr_attrs[] = {
145*e4664c0eSWu Hao 	&dev_attr_id.attr,
146*e4664c0eSWu Hao 	NULL,
147*e4664c0eSWu Hao };
148*e4664c0eSWu Hao 
1491a1527cfSWu Hao static int port_hdr_init(struct platform_device *pdev,
1501a1527cfSWu Hao 			 struct dfl_feature *feature)
1511a1527cfSWu Hao {
1521a1527cfSWu Hao 	dev_dbg(&pdev->dev, "PORT HDR Init.\n");
1531a1527cfSWu Hao 
154*e4664c0eSWu Hao 	port_reset(pdev);
155*e4664c0eSWu Hao 
156*e4664c0eSWu Hao 	return sysfs_create_files(&pdev->dev.kobj, port_hdr_attrs);
1571a1527cfSWu Hao }
1581a1527cfSWu Hao 
1591a1527cfSWu Hao static void port_hdr_uinit(struct platform_device *pdev,
1601a1527cfSWu Hao 			   struct dfl_feature *feature)
1611a1527cfSWu Hao {
1621a1527cfSWu Hao 	dev_dbg(&pdev->dev, "PORT HDR UInit.\n");
163*e4664c0eSWu Hao 
164*e4664c0eSWu Hao 	sysfs_remove_files(&pdev->dev.kobj, port_hdr_attrs);
165*e4664c0eSWu Hao }
166*e4664c0eSWu Hao 
167*e4664c0eSWu Hao static long
168*e4664c0eSWu Hao port_hdr_ioctl(struct platform_device *pdev, struct dfl_feature *feature,
169*e4664c0eSWu Hao 	       unsigned int cmd, unsigned long arg)
170*e4664c0eSWu Hao {
171*e4664c0eSWu Hao 	long ret;
172*e4664c0eSWu Hao 
173*e4664c0eSWu Hao 	switch (cmd) {
174*e4664c0eSWu Hao 	case DFL_FPGA_PORT_RESET:
175*e4664c0eSWu Hao 		if (!arg)
176*e4664c0eSWu Hao 			ret = port_reset(pdev);
177*e4664c0eSWu Hao 		else
178*e4664c0eSWu Hao 			ret = -EINVAL;
179*e4664c0eSWu Hao 		break;
180*e4664c0eSWu Hao 	default:
181*e4664c0eSWu Hao 		dev_dbg(&pdev->dev, "%x cmd not handled", cmd);
182*e4664c0eSWu Hao 		ret = -ENODEV;
183*e4664c0eSWu Hao 	}
184*e4664c0eSWu Hao 
185*e4664c0eSWu Hao 	return ret;
1861a1527cfSWu Hao }
1871a1527cfSWu Hao 
1881a1527cfSWu Hao static const struct dfl_feature_ops port_hdr_ops = {
1891a1527cfSWu Hao 	.init = port_hdr_init,
1901a1527cfSWu Hao 	.uinit = port_hdr_uinit,
191*e4664c0eSWu Hao 	.ioctl = port_hdr_ioctl,
1921a1527cfSWu Hao };
1931a1527cfSWu Hao 
1941a1527cfSWu Hao static struct dfl_feature_driver port_feature_drvs[] = {
1951a1527cfSWu Hao 	{
1961a1527cfSWu Hao 		.id = PORT_FEATURE_ID_HEADER,
1971a1527cfSWu Hao 		.ops = &port_hdr_ops,
1981a1527cfSWu Hao 	},
1991a1527cfSWu Hao 	{
2001a1527cfSWu Hao 		.ops = NULL,
2011a1527cfSWu Hao 	}
2021a1527cfSWu Hao };
2031a1527cfSWu Hao 
2041a1527cfSWu Hao static int afu_open(struct inode *inode, struct file *filp)
2051a1527cfSWu Hao {
2061a1527cfSWu Hao 	struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
2071a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
2081a1527cfSWu Hao 	int ret;
2091a1527cfSWu Hao 
2101a1527cfSWu Hao 	pdata = dev_get_platdata(&fdev->dev);
2111a1527cfSWu Hao 	if (WARN_ON(!pdata))
2121a1527cfSWu Hao 		return -ENODEV;
2131a1527cfSWu Hao 
2141a1527cfSWu Hao 	ret = dfl_feature_dev_use_begin(pdata);
2151a1527cfSWu Hao 	if (ret)
2161a1527cfSWu Hao 		return ret;
2171a1527cfSWu Hao 
2181a1527cfSWu Hao 	dev_dbg(&fdev->dev, "Device File Open\n");
2191a1527cfSWu Hao 	filp->private_data = fdev;
2201a1527cfSWu Hao 
2211a1527cfSWu Hao 	return 0;
2221a1527cfSWu Hao }
2231a1527cfSWu Hao 
2241a1527cfSWu Hao static int afu_release(struct inode *inode, struct file *filp)
2251a1527cfSWu Hao {
2261a1527cfSWu Hao 	struct platform_device *pdev = filp->private_data;
2271a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
2281a1527cfSWu Hao 
2291a1527cfSWu Hao 	dev_dbg(&pdev->dev, "Device File Release\n");
2301a1527cfSWu Hao 
2311a1527cfSWu Hao 	pdata = dev_get_platdata(&pdev->dev);
2321a1527cfSWu Hao 
233*e4664c0eSWu Hao 	port_reset(pdev);
2341a1527cfSWu Hao 	dfl_feature_dev_use_end(pdata);
2351a1527cfSWu Hao 
2361a1527cfSWu Hao 	return 0;
2371a1527cfSWu Hao }
2381a1527cfSWu Hao 
2391a1527cfSWu Hao static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
2401a1527cfSWu Hao {
2411a1527cfSWu Hao 	struct platform_device *pdev = filp->private_data;
2421a1527cfSWu Hao 	struct dfl_feature_platform_data *pdata;
2431a1527cfSWu Hao 	struct dfl_feature *f;
2441a1527cfSWu Hao 	long ret;
2451a1527cfSWu Hao 
2461a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd);
2471a1527cfSWu Hao 
2481a1527cfSWu Hao 	pdata = dev_get_platdata(&pdev->dev);
2491a1527cfSWu Hao 
2501a1527cfSWu Hao 	switch (cmd) {
2511a1527cfSWu Hao 	default:
2521a1527cfSWu Hao 		/*
2531a1527cfSWu Hao 		 * Let sub-feature's ioctl function to handle the cmd
2541a1527cfSWu Hao 		 * Sub-feature's ioctl returns -ENODEV when cmd is not
2551a1527cfSWu Hao 		 * handled in this sub feature, and returns 0 and other
2561a1527cfSWu Hao 		 * error code if cmd is handled.
2571a1527cfSWu Hao 		 */
2581a1527cfSWu Hao 		dfl_fpga_dev_for_each_feature(pdata, f)
2591a1527cfSWu Hao 			if (f->ops && f->ops->ioctl) {
2601a1527cfSWu Hao 				ret = f->ops->ioctl(pdev, f, cmd, arg);
2611a1527cfSWu Hao 				if (ret != -ENODEV)
2621a1527cfSWu Hao 					return ret;
2631a1527cfSWu Hao 			}
2641a1527cfSWu Hao 	}
2651a1527cfSWu Hao 
2661a1527cfSWu Hao 	return -EINVAL;
2671a1527cfSWu Hao }
2681a1527cfSWu Hao 
2691a1527cfSWu Hao static const struct file_operations afu_fops = {
2701a1527cfSWu Hao 	.owner = THIS_MODULE,
2711a1527cfSWu Hao 	.open = afu_open,
2721a1527cfSWu Hao 	.release = afu_release,
2731a1527cfSWu Hao 	.unlocked_ioctl = afu_ioctl,
2741a1527cfSWu Hao };
2751a1527cfSWu Hao 
27647c1b19cSWu Hao static int port_enable_set(struct platform_device *pdev, bool enable)
27747c1b19cSWu Hao {
27847c1b19cSWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
27947c1b19cSWu Hao 	int ret = 0;
28047c1b19cSWu Hao 
28147c1b19cSWu Hao 	mutex_lock(&pdata->lock);
28247c1b19cSWu Hao 	if (enable)
28347c1b19cSWu Hao 		port_enable(pdev);
28447c1b19cSWu Hao 	else
28547c1b19cSWu Hao 		ret = port_disable(pdev);
28647c1b19cSWu Hao 	mutex_unlock(&pdata->lock);
28747c1b19cSWu Hao 
28847c1b19cSWu Hao 	return ret;
28947c1b19cSWu Hao }
29047c1b19cSWu Hao 
29147c1b19cSWu Hao static struct dfl_fpga_port_ops afu_port_ops = {
29247c1b19cSWu Hao 	.name = DFL_FPGA_FEATURE_DEV_PORT,
29347c1b19cSWu Hao 	.owner = THIS_MODULE,
29447c1b19cSWu Hao 	.get_id = port_get_id,
29547c1b19cSWu Hao 	.enable_set = port_enable_set,
29647c1b19cSWu Hao };
29747c1b19cSWu Hao 
2981a1527cfSWu Hao static int afu_probe(struct platform_device *pdev)
2991a1527cfSWu Hao {
3001a1527cfSWu Hao 	int ret;
3011a1527cfSWu Hao 
3021a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s\n", __func__);
3031a1527cfSWu Hao 
3041a1527cfSWu Hao 	ret = dfl_fpga_dev_feature_init(pdev, port_feature_drvs);
3051a1527cfSWu Hao 	if (ret)
3061a1527cfSWu Hao 		return ret;
3071a1527cfSWu Hao 
3081a1527cfSWu Hao 	ret = dfl_fpga_dev_ops_register(pdev, &afu_fops, THIS_MODULE);
3091a1527cfSWu Hao 	if (ret)
3101a1527cfSWu Hao 		dfl_fpga_dev_feature_uinit(pdev);
3111a1527cfSWu Hao 
3121a1527cfSWu Hao 	return ret;
3131a1527cfSWu Hao }
3141a1527cfSWu Hao 
3151a1527cfSWu Hao static int afu_remove(struct platform_device *pdev)
3161a1527cfSWu Hao {
3171a1527cfSWu Hao 	dev_dbg(&pdev->dev, "%s\n", __func__);
3181a1527cfSWu Hao 
3191a1527cfSWu Hao 	dfl_fpga_dev_ops_unregister(pdev);
3201a1527cfSWu Hao 	dfl_fpga_dev_feature_uinit(pdev);
3211a1527cfSWu Hao 
3221a1527cfSWu Hao 	return 0;
3231a1527cfSWu Hao }
3241a1527cfSWu Hao 
3251a1527cfSWu Hao static struct platform_driver afu_driver = {
3261a1527cfSWu Hao 	.driver	= {
3271a1527cfSWu Hao 		.name    = DFL_FPGA_FEATURE_DEV_PORT,
3281a1527cfSWu Hao 	},
3291a1527cfSWu Hao 	.probe   = afu_probe,
3301a1527cfSWu Hao 	.remove  = afu_remove,
3311a1527cfSWu Hao };
3321a1527cfSWu Hao 
33347c1b19cSWu Hao static int __init afu_init(void)
33447c1b19cSWu Hao {
33547c1b19cSWu Hao 	int ret;
33647c1b19cSWu Hao 
33747c1b19cSWu Hao 	dfl_fpga_port_ops_add(&afu_port_ops);
33847c1b19cSWu Hao 
33947c1b19cSWu Hao 	ret = platform_driver_register(&afu_driver);
34047c1b19cSWu Hao 	if (ret)
34147c1b19cSWu Hao 		dfl_fpga_port_ops_del(&afu_port_ops);
34247c1b19cSWu Hao 
34347c1b19cSWu Hao 	return ret;
34447c1b19cSWu Hao }
34547c1b19cSWu Hao 
34647c1b19cSWu Hao static void __exit afu_exit(void)
34747c1b19cSWu Hao {
34847c1b19cSWu Hao 	platform_driver_unregister(&afu_driver);
34947c1b19cSWu Hao 
35047c1b19cSWu Hao 	dfl_fpga_port_ops_del(&afu_port_ops);
35147c1b19cSWu Hao }
35247c1b19cSWu Hao 
35347c1b19cSWu Hao module_init(afu_init);
35447c1b19cSWu Hao module_exit(afu_exit);
3551a1527cfSWu Hao 
3561a1527cfSWu Hao MODULE_DESCRIPTION("FPGA Accelerated Function Unit driver");
3571a1527cfSWu Hao MODULE_AUTHOR("Intel Corporation");
3581a1527cfSWu Hao MODULE_LICENSE("GPL v2");
3591a1527cfSWu Hao MODULE_ALIAS("platform:dfl-port");
360