diff options
Diffstat (limited to 'drivers/fpga')
-rw-r--r-- | drivers/fpga/dfl-afu-main.c | 79 |
1 files changed, 78 insertions, 1 deletions
diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c index a38d6a825e7e..d36b3e9f3984 100644 --- a/drivers/fpga/dfl-afu-main.c +++ b/drivers/fpga/dfl-afu-main.c | |||
@@ -16,6 +16,7 @@ | |||
16 | 16 | ||
17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/fpga-dfl.h> | ||
19 | 20 | ||
20 | #include "dfl.h" | 21 | #include "dfl.h" |
21 | 22 | ||
@@ -87,6 +88,41 @@ static int port_disable(struct platform_device *pdev) | |||
87 | return 0; | 88 | return 0; |
88 | } | 89 | } |
89 | 90 | ||
91 | /* | ||
92 | * This function resets the FPGA Port and its accelerator (AFU) by function | ||
93 | * __port_disable and __port_enable (set port soft reset bit and then clear | ||
94 | * it). Userspace can do Port reset at any time, e.g. during DMA or Partial | ||
95 | * Reconfiguration. But it should never cause any system level issue, only | ||
96 | * functional failure (e.g. DMA or PR operation failure) and be recoverable | ||
97 | * from the failure. | ||
98 | * | ||
99 | * Note: the accelerator (AFU) is not accessible when its port is in reset | ||
100 | * (disabled). Any attempts on MMIO access to AFU while in reset, will | ||
101 | * result errors reported via port error reporting sub feature (if present). | ||
102 | */ | ||
103 | static int __port_reset(struct platform_device *pdev) | ||
104 | { | ||
105 | int ret; | ||
106 | |||
107 | ret = port_disable(pdev); | ||
108 | if (!ret) | ||
109 | port_enable(pdev); | ||
110 | |||
111 | return ret; | ||
112 | } | ||
113 | |||
114 | static int port_reset(struct platform_device *pdev) | ||
115 | { | ||
116 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); | ||
117 | int ret; | ||
118 | |||
119 | mutex_lock(&pdata->lock); | ||
120 | ret = __port_reset(pdev); | ||
121 | mutex_unlock(&pdata->lock); | ||
122 | |||
123 | return ret; | ||
124 | } | ||
125 | |||
90 | static int port_get_id(struct platform_device *pdev) | 126 | static int port_get_id(struct platform_device *pdev) |
91 | { | 127 | { |
92 | void __iomem *base; | 128 | void __iomem *base; |
@@ -96,23 +132,63 @@ static int port_get_id(struct platform_device *pdev) | |||
96 | return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + PORT_HDR_CAP)); | 132 | return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + PORT_HDR_CAP)); |
97 | } | 133 | } |
98 | 134 | ||
135 | static ssize_t | ||
136 | id_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
137 | { | ||
138 | int id = port_get_id(to_platform_device(dev)); | ||
139 | |||
140 | return scnprintf(buf, PAGE_SIZE, "%d\n", id); | ||
141 | } | ||
142 | static DEVICE_ATTR_RO(id); | ||
143 | |||
144 | static const struct attribute *port_hdr_attrs[] = { | ||
145 | &dev_attr_id.attr, | ||
146 | NULL, | ||
147 | }; | ||
148 | |||
99 | static int port_hdr_init(struct platform_device *pdev, | 149 | static int port_hdr_init(struct platform_device *pdev, |
100 | struct dfl_feature *feature) | 150 | struct dfl_feature *feature) |
101 | { | 151 | { |
102 | dev_dbg(&pdev->dev, "PORT HDR Init.\n"); | 152 | dev_dbg(&pdev->dev, "PORT HDR Init.\n"); |
103 | 153 | ||
104 | return 0; | 154 | port_reset(pdev); |
155 | |||
156 | return sysfs_create_files(&pdev->dev.kobj, port_hdr_attrs); | ||
105 | } | 157 | } |
106 | 158 | ||
107 | static void port_hdr_uinit(struct platform_device *pdev, | 159 | static void port_hdr_uinit(struct platform_device *pdev, |
108 | struct dfl_feature *feature) | 160 | struct dfl_feature *feature) |
109 | { | 161 | { |
110 | dev_dbg(&pdev->dev, "PORT HDR UInit.\n"); | 162 | dev_dbg(&pdev->dev, "PORT HDR UInit.\n"); |
163 | |||
164 | sysfs_remove_files(&pdev->dev.kobj, port_hdr_attrs); | ||
165 | } | ||
166 | |||
167 | static long | ||
168 | port_hdr_ioctl(struct platform_device *pdev, struct dfl_feature *feature, | ||
169 | unsigned int cmd, unsigned long arg) | ||
170 | { | ||
171 | long ret; | ||
172 | |||
173 | switch (cmd) { | ||
174 | case DFL_FPGA_PORT_RESET: | ||
175 | if (!arg) | ||
176 | ret = port_reset(pdev); | ||
177 | else | ||
178 | ret = -EINVAL; | ||
179 | break; | ||
180 | default: | ||
181 | dev_dbg(&pdev->dev, "%x cmd not handled", cmd); | ||
182 | ret = -ENODEV; | ||
183 | } | ||
184 | |||
185 | return ret; | ||
111 | } | 186 | } |
112 | 187 | ||
113 | static const struct dfl_feature_ops port_hdr_ops = { | 188 | static const struct dfl_feature_ops port_hdr_ops = { |
114 | .init = port_hdr_init, | 189 | .init = port_hdr_init, |
115 | .uinit = port_hdr_uinit, | 190 | .uinit = port_hdr_uinit, |
191 | .ioctl = port_hdr_ioctl, | ||
116 | }; | 192 | }; |
117 | 193 | ||
118 | static struct dfl_feature_driver port_feature_drvs[] = { | 194 | static struct dfl_feature_driver port_feature_drvs[] = { |
@@ -154,6 +230,7 @@ static int afu_release(struct inode *inode, struct file *filp) | |||
154 | 230 | ||
155 | pdata = dev_get_platdata(&pdev->dev); | 231 | pdata = dev_get_platdata(&pdev->dev); |
156 | 232 | ||
233 | port_reset(pdev); | ||
157 | dfl_feature_dev_use_end(pdata); | 234 | dfl_feature_dev_use_end(pdata); |
158 | 235 | ||
159 | return 0; | 236 | return 0; |