Commit | Line | Data |
---|---|---|
1a1527cf WH |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Driver for FPGA Accelerated Function Unit (AFU) | |
4 | * | |
5 | * Copyright (C) 2017-2018 Intel Corporation, Inc. | |
6 | * | |
7 | * Authors: | |
8 | * Wu Hao <hao.wu@intel.com> | |
9 | * Xiao Guangrong <guangrong.xiao@linux.intel.com> | |
10 | * Joseph Grecco <joe.grecco@intel.com> | |
11 | * Enno Luebbers <enno.luebbers@intel.com> | |
12 | * Tim Whisonant <tim.whisonant@intel.com> | |
13 | * Ananda Ravuri <ananda.ravuri@intel.com> | |
14 | * Henry Mitchel <henry.mitchel@intel.com> | |
15 | */ | |
16 | ||
17 | #include <linux/kernel.h> | |
18 | #include <linux/module.h> | |
857a2622 | 19 | #include <linux/uaccess.h> |
e4664c0e | 20 | #include <linux/fpga-dfl.h> |
1a1527cf | 21 | |
857a2622 | 22 | #include "dfl-afu.h" |
1a1527cf | 23 | |
9a8d3cda RW |
24 | #define RST_POLL_INVL 10 /* us */ |
25 | #define RST_POLL_TIMEOUT 1000 /* us */ | |
26 | ||
47c1b19c | 27 | /** |
95844372 | 28 | * __afu_port_enable - enable a port by clear reset |
47c1b19c WH |
29 | * @pdev: port platform device. |
30 | * | |
31 | * Enable Port by clear the port soft reset bit, which is set by default. | |
857a2622 | 32 | * The AFU is unable to respond to any MMIO access while in reset. |
95844372 WH |
33 | * __afu_port_enable function should only be used after __afu_port_disable |
34 | * function. | |
35 | * | |
36 | * The caller needs to hold lock for protection. | |
47c1b19c | 37 | */ |
9a8d3cda | 38 | int __afu_port_enable(struct platform_device *pdev) |
47c1b19c WH |
39 | { |
40 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); | |
41 | void __iomem *base; | |
42 | u64 v; | |
43 | ||
44 | WARN_ON(!pdata->disable_count); | |
45 | ||
46 | if (--pdata->disable_count != 0) | |
9a8d3cda | 47 | return 0; |
47c1b19c WH |
48 | |
49 | base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER); | |
50 | ||
51 | /* Clear port soft reset */ | |
52 | v = readq(base + PORT_HDR_CTRL); | |
53 | v &= ~PORT_CTRL_SFTRST; | |
54 | writeq(v, base + PORT_HDR_CTRL); | |
47c1b19c | 55 | |
9a8d3cda RW |
56 | /* |
57 | * HW clears the ack bit to indicate that the port is fully out | |
58 | * of reset. | |
59 | */ | |
60 | if (readq_poll_timeout(base + PORT_HDR_CTRL, v, | |
61 | !(v & PORT_CTRL_SFTRST_ACK), | |
62 | RST_POLL_INVL, RST_POLL_TIMEOUT)) { | |
63 | dev_err(&pdev->dev, "timeout, failure to enable device\n"); | |
64 | return -ETIMEDOUT; | |
65 | } | |
66 | ||
67 | return 0; | |
68 | } | |
47c1b19c WH |
69 | |
70 | /** | |
95844372 | 71 | * __afu_port_disable - disable a port by hold reset |
47c1b19c WH |
72 | * @pdev: port platform device. |
73 | * | |
95844372 WH |
74 | * Disable Port by setting the port soft reset bit, it puts the port into reset. |
75 | * | |
76 | * The caller needs to hold lock for protection. | |
47c1b19c | 77 | */ |
95844372 | 78 | int __afu_port_disable(struct platform_device *pdev) |
47c1b19c WH |
79 | { |
80 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); | |
81 | void __iomem *base; | |
82 | u64 v; | |
83 | ||
84 | if (pdata->disable_count++ != 0) | |
85 | return 0; | |
86 | ||
87 | base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER); | |
88 | ||
89 | /* Set port soft reset */ | |
90 | v = readq(base + PORT_HDR_CTRL); | |
91 | v |= PORT_CTRL_SFTRST; | |
92 | writeq(v, base + PORT_HDR_CTRL); | |
93 | ||
94 | /* | |
95 | * HW sets ack bit to 1 when all outstanding requests have been drained | |
96 | * on this port and minimum soft reset pulse width has elapsed. | |
97 | * Driver polls port_soft_reset_ack to determine if reset done by HW. | |
98 | */ | |
8614afd6 MG |
99 | if (readq_poll_timeout(base + PORT_HDR_CTRL, v, |
100 | v & PORT_CTRL_SFTRST_ACK, | |
47c1b19c | 101 | RST_POLL_INVL, RST_POLL_TIMEOUT)) { |
9a8d3cda | 102 | dev_err(&pdev->dev, "timeout, failure to disable device\n"); |
47c1b19c WH |
103 | return -ETIMEDOUT; |
104 | } | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
e4664c0e WH |
109 | /* |
110 | * This function resets the FPGA Port and its accelerator (AFU) by function | |
111 | * __port_disable and __port_enable (set port soft reset bit and then clear | |
112 | * it). Userspace can do Port reset at any time, e.g. during DMA or Partial | |
113 | * Reconfiguration. But it should never cause any system level issue, only | |
114 | * functional failure (e.g. DMA or PR operation failure) and be recoverable | |
115 | * from the failure. | |
116 | * | |
117 | * Note: the accelerator (AFU) is not accessible when its port is in reset | |
118 | * (disabled). Any attempts on MMIO access to AFU while in reset, will | |
119 | * result errors reported via port error reporting sub feature (if present). | |
120 | */ | |
121 | static int __port_reset(struct platform_device *pdev) | |
122 | { | |
123 | int ret; | |
124 | ||
95844372 | 125 | ret = __afu_port_disable(pdev); |
9a8d3cda RW |
126 | if (ret) |
127 | return ret; | |
e4664c0e | 128 | |
9a8d3cda | 129 | return __afu_port_enable(pdev); |
e4664c0e WH |
130 | } |
131 | ||
132 | static int port_reset(struct platform_device *pdev) | |
133 | { | |
134 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); | |
135 | int ret; | |
136 | ||
137 | mutex_lock(&pdata->lock); | |
138 | ret = __port_reset(pdev); | |
139 | mutex_unlock(&pdata->lock); | |
140 | ||
141 | return ret; | |
142 | } | |
143 | ||
47c1b19c WH |
144 | static int port_get_id(struct platform_device *pdev) |
145 | { | |
146 | void __iomem *base; | |
147 | ||
148 | base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER); | |
149 | ||
150 | return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + PORT_HDR_CAP)); | |
151 | } | |
152 | ||
e4664c0e WH |
153 | static ssize_t |
154 | id_show(struct device *dev, struct device_attribute *attr, char *buf) | |
155 | { | |
156 | int id = port_get_id(to_platform_device(dev)); | |
157 | ||
158 | return scnprintf(buf, PAGE_SIZE, "%d\n", id); | |
159 | } | |
160 | static DEVICE_ATTR_RO(id); | |
161 | ||
d2ad5ac1 WH |
162 | static ssize_t |
163 | ltr_show(struct device *dev, struct device_attribute *attr, char *buf) | |
164 | { | |
165 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
166 | void __iomem *base; | |
167 | u64 v; | |
168 | ||
169 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
170 | ||
171 | mutex_lock(&pdata->lock); | |
172 | v = readq(base + PORT_HDR_CTRL); | |
173 | mutex_unlock(&pdata->lock); | |
174 | ||
175 | return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_CTRL_LATENCY, v)); | |
176 | } | |
177 | ||
178 | static ssize_t | |
179 | ltr_store(struct device *dev, struct device_attribute *attr, | |
180 | const char *buf, size_t count) | |
181 | { | |
182 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
183 | void __iomem *base; | |
184 | bool ltr; | |
185 | u64 v; | |
186 | ||
187 | if (kstrtobool(buf, <r)) | |
188 | return -EINVAL; | |
189 | ||
190 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
191 | ||
192 | mutex_lock(&pdata->lock); | |
193 | v = readq(base + PORT_HDR_CTRL); | |
194 | v &= ~PORT_CTRL_LATENCY; | |
195 | v |= FIELD_PREP(PORT_CTRL_LATENCY, ltr ? 1 : 0); | |
196 | writeq(v, base + PORT_HDR_CTRL); | |
197 | mutex_unlock(&pdata->lock); | |
198 | ||
199 | return count; | |
200 | } | |
201 | static DEVICE_ATTR_RW(ltr); | |
202 | ||
203 | static ssize_t | |
204 | ap1_event_show(struct device *dev, struct device_attribute *attr, char *buf) | |
205 | { | |
206 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
207 | void __iomem *base; | |
208 | u64 v; | |
209 | ||
210 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
211 | ||
212 | mutex_lock(&pdata->lock); | |
213 | v = readq(base + PORT_HDR_STS); | |
214 | mutex_unlock(&pdata->lock); | |
215 | ||
216 | return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP1_EVT, v)); | |
217 | } | |
218 | ||
219 | static ssize_t | |
220 | ap1_event_store(struct device *dev, struct device_attribute *attr, | |
221 | const char *buf, size_t count) | |
222 | { | |
223 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
224 | void __iomem *base; | |
225 | bool clear; | |
226 | ||
227 | if (kstrtobool(buf, &clear) || !clear) | |
228 | return -EINVAL; | |
229 | ||
230 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
231 | ||
232 | mutex_lock(&pdata->lock); | |
233 | writeq(PORT_STS_AP1_EVT, base + PORT_HDR_STS); | |
234 | mutex_unlock(&pdata->lock); | |
235 | ||
236 | return count; | |
237 | } | |
238 | static DEVICE_ATTR_RW(ap1_event); | |
239 | ||
240 | static ssize_t | |
241 | ap2_event_show(struct device *dev, struct device_attribute *attr, | |
242 | char *buf) | |
243 | { | |
244 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
245 | void __iomem *base; | |
246 | u64 v; | |
247 | ||
248 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
249 | ||
250 | mutex_lock(&pdata->lock); | |
251 | v = readq(base + PORT_HDR_STS); | |
252 | mutex_unlock(&pdata->lock); | |
253 | ||
254 | return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP2_EVT, v)); | |
255 | } | |
256 | ||
257 | static ssize_t | |
258 | ap2_event_store(struct device *dev, struct device_attribute *attr, | |
259 | const char *buf, size_t count) | |
260 | { | |
261 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
262 | void __iomem *base; | |
263 | bool clear; | |
264 | ||
265 | if (kstrtobool(buf, &clear) || !clear) | |
266 | return -EINVAL; | |
267 | ||
268 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
269 | ||
270 | mutex_lock(&pdata->lock); | |
271 | writeq(PORT_STS_AP2_EVT, base + PORT_HDR_STS); | |
272 | mutex_unlock(&pdata->lock); | |
273 | ||
274 | return count; | |
275 | } | |
276 | static DEVICE_ATTR_RW(ap2_event); | |
277 | ||
278 | static ssize_t | |
279 | power_state_show(struct device *dev, struct device_attribute *attr, char *buf) | |
280 | { | |
281 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
282 | void __iomem *base; | |
283 | u64 v; | |
284 | ||
285 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
286 | ||
287 | mutex_lock(&pdata->lock); | |
288 | v = readq(base + PORT_HDR_STS); | |
289 | mutex_unlock(&pdata->lock); | |
290 | ||
291 | return sprintf(buf, "0x%x\n", (u8)FIELD_GET(PORT_STS_PWR_STATE, v)); | |
292 | } | |
293 | static DEVICE_ATTR_RO(power_state); | |
294 | ||
f09991ad WH |
295 | static ssize_t |
296 | userclk_freqcmd_store(struct device *dev, struct device_attribute *attr, | |
297 | const char *buf, size_t count) | |
298 | { | |
299 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
300 | u64 userclk_freq_cmd; | |
301 | void __iomem *base; | |
302 | ||
303 | if (kstrtou64(buf, 0, &userclk_freq_cmd)) | |
304 | return -EINVAL; | |
305 | ||
306 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
307 | ||
308 | mutex_lock(&pdata->lock); | |
309 | writeq(userclk_freq_cmd, base + PORT_HDR_USRCLK_CMD0); | |
310 | mutex_unlock(&pdata->lock); | |
311 | ||
312 | return count; | |
313 | } | |
314 | static DEVICE_ATTR_WO(userclk_freqcmd); | |
315 | ||
316 | static ssize_t | |
317 | userclk_freqcntrcmd_store(struct device *dev, struct device_attribute *attr, | |
318 | const char *buf, size_t count) | |
319 | { | |
320 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
321 | u64 userclk_freqcntr_cmd; | |
322 | void __iomem *base; | |
323 | ||
324 | if (kstrtou64(buf, 0, &userclk_freqcntr_cmd)) | |
325 | return -EINVAL; | |
326 | ||
327 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
328 | ||
329 | mutex_lock(&pdata->lock); | |
330 | writeq(userclk_freqcntr_cmd, base + PORT_HDR_USRCLK_CMD1); | |
331 | mutex_unlock(&pdata->lock); | |
332 | ||
333 | return count; | |
334 | } | |
335 | static DEVICE_ATTR_WO(userclk_freqcntrcmd); | |
336 | ||
337 | static ssize_t | |
338 | userclk_freqsts_show(struct device *dev, struct device_attribute *attr, | |
339 | char *buf) | |
340 | { | |
341 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
342 | u64 userclk_freqsts; | |
343 | void __iomem *base; | |
344 | ||
345 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
346 | ||
347 | mutex_lock(&pdata->lock); | |
348 | userclk_freqsts = readq(base + PORT_HDR_USRCLK_STS0); | |
349 | mutex_unlock(&pdata->lock); | |
350 | ||
351 | return sprintf(buf, "0x%llx\n", (unsigned long long)userclk_freqsts); | |
352 | } | |
353 | static DEVICE_ATTR_RO(userclk_freqsts); | |
354 | ||
355 | static ssize_t | |
356 | userclk_freqcntrsts_show(struct device *dev, struct device_attribute *attr, | |
357 | char *buf) | |
358 | { | |
359 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
360 | u64 userclk_freqcntrsts; | |
361 | void __iomem *base; | |
362 | ||
363 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
364 | ||
365 | mutex_lock(&pdata->lock); | |
366 | userclk_freqcntrsts = readq(base + PORT_HDR_USRCLK_STS1); | |
367 | mutex_unlock(&pdata->lock); | |
368 | ||
369 | return sprintf(buf, "0x%llx\n", | |
370 | (unsigned long long)userclk_freqcntrsts); | |
371 | } | |
372 | static DEVICE_ATTR_RO(userclk_freqcntrsts); | |
373 | ||
dcfecd4d | 374 | static struct attribute *port_hdr_attrs[] = { |
e4664c0e | 375 | &dev_attr_id.attr, |
d2ad5ac1 WH |
376 | &dev_attr_ltr.attr, |
377 | &dev_attr_ap1_event.attr, | |
378 | &dev_attr_ap2_event.attr, | |
379 | &dev_attr_power_state.attr, | |
f09991ad WH |
380 | &dev_attr_userclk_freqcmd.attr, |
381 | &dev_attr_userclk_freqcntrcmd.attr, | |
382 | &dev_attr_userclk_freqsts.attr, | |
383 | &dev_attr_userclk_freqcntrsts.attr, | |
e4664c0e WH |
384 | NULL, |
385 | }; | |
a80a4b82 | 386 | |
f09991ad WH |
387 | static umode_t port_hdr_attrs_visible(struct kobject *kobj, |
388 | struct attribute *attr, int n) | |
389 | { | |
390 | struct device *dev = kobj_to_dev(kobj); | |
391 | umode_t mode = attr->mode; | |
392 | void __iomem *base; | |
393 | ||
394 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
395 | ||
396 | if (dfl_feature_revision(base) > 0) { | |
397 | /* | |
398 | * userclk sysfs interfaces are only visible in case port | |
399 | * revision is 0, as hardware with revision >0 doesn't | |
400 | * support this. | |
401 | */ | |
402 | if (attr == &dev_attr_userclk_freqcmd.attr || | |
403 | attr == &dev_attr_userclk_freqcntrcmd.attr || | |
404 | attr == &dev_attr_userclk_freqsts.attr || | |
405 | attr == &dev_attr_userclk_freqcntrsts.attr) | |
406 | mode = 0; | |
407 | } | |
408 | ||
409 | return mode; | |
410 | } | |
411 | ||
a80a4b82 | 412 | static const struct attribute_group port_hdr_group = { |
f09991ad WH |
413 | .attrs = port_hdr_attrs, |
414 | .is_visible = port_hdr_attrs_visible, | |
a80a4b82 | 415 | }; |
e4664c0e | 416 | |
1a1527cf WH |
417 | static int port_hdr_init(struct platform_device *pdev, |
418 | struct dfl_feature *feature) | |
419 | { | |
e4664c0e WH |
420 | port_reset(pdev); |
421 | ||
a80a4b82 | 422 | return 0; |
e4664c0e WH |
423 | } |
424 | ||
425 | static long | |
426 | port_hdr_ioctl(struct platform_device *pdev, struct dfl_feature *feature, | |
427 | unsigned int cmd, unsigned long arg) | |
428 | { | |
429 | long ret; | |
430 | ||
431 | switch (cmd) { | |
432 | case DFL_FPGA_PORT_RESET: | |
433 | if (!arg) | |
434 | ret = port_reset(pdev); | |
435 | else | |
436 | ret = -EINVAL; | |
437 | break; | |
438 | default: | |
439 | dev_dbg(&pdev->dev, "%x cmd not handled", cmd); | |
440 | ret = -ENODEV; | |
441 | } | |
442 | ||
443 | return ret; | |
1a1527cf WH |
444 | } |
445 | ||
15bbb300 WH |
446 | static const struct dfl_feature_id port_hdr_id_table[] = { |
447 | {.id = PORT_FEATURE_ID_HEADER,}, | |
448 | {0,} | |
449 | }; | |
450 | ||
1a1527cf WH |
451 | static const struct dfl_feature_ops port_hdr_ops = { |
452 | .init = port_hdr_init, | |
e4664c0e | 453 | .ioctl = port_hdr_ioctl, |
1a1527cf WH |
454 | }; |
455 | ||
857a2622 XG |
456 | static ssize_t |
457 | afu_id_show(struct device *dev, struct device_attribute *attr, char *buf) | |
458 | { | |
459 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
460 | void __iomem *base; | |
461 | u64 guidl, guidh; | |
462 | ||
463 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_AFU); | |
464 | ||
465 | mutex_lock(&pdata->lock); | |
466 | if (pdata->disable_count) { | |
467 | mutex_unlock(&pdata->lock); | |
468 | return -EBUSY; | |
469 | } | |
470 | ||
471 | guidl = readq(base + GUID_L); | |
472 | guidh = readq(base + GUID_H); | |
473 | mutex_unlock(&pdata->lock); | |
474 | ||
475 | return scnprintf(buf, PAGE_SIZE, "%016llx%016llx\n", guidh, guidl); | |
476 | } | |
477 | static DEVICE_ATTR_RO(afu_id); | |
478 | ||
dcfecd4d | 479 | static struct attribute *port_afu_attrs[] = { |
857a2622 XG |
480 | &dev_attr_afu_id.attr, |
481 | NULL | |
482 | }; | |
483 | ||
a80a4b82 WH |
484 | static umode_t port_afu_attrs_visible(struct kobject *kobj, |
485 | struct attribute *attr, int n) | |
857a2622 | 486 | { |
a80a4b82 | 487 | struct device *dev = kobj_to_dev(kobj); |
857a2622 | 488 | |
a80a4b82 WH |
489 | /* |
490 | * sysfs entries are visible only if related private feature is | |
491 | * enumerated. | |
492 | */ | |
493 | if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_AFU)) | |
494 | return 0; | |
857a2622 | 495 | |
a80a4b82 | 496 | return attr->mode; |
857a2622 XG |
497 | } |
498 | ||
a80a4b82 WH |
499 | static const struct attribute_group port_afu_group = { |
500 | .attrs = port_afu_attrs, | |
501 | .is_visible = port_afu_attrs_visible, | |
502 | }; | |
503 | ||
504 | static int port_afu_init(struct platform_device *pdev, | |
505 | struct dfl_feature *feature) | |
857a2622 | 506 | { |
a80a4b82 | 507 | struct resource *res = &pdev->resource[feature->resource_index]; |
857a2622 | 508 | |
a80a4b82 WH |
509 | return afu_mmio_region_add(dev_get_platdata(&pdev->dev), |
510 | DFL_PORT_REGION_INDEX_AFU, | |
511 | resource_size(res), res->start, | |
512 | DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ | | |
513 | DFL_PORT_REGION_WRITE); | |
857a2622 XG |
514 | } |
515 | ||
15bbb300 WH |
516 | static const struct dfl_feature_id port_afu_id_table[] = { |
517 | {.id = PORT_FEATURE_ID_AFU,}, | |
518 | {0,} | |
519 | }; | |
520 | ||
857a2622 XG |
521 | static const struct dfl_feature_ops port_afu_ops = { |
522 | .init = port_afu_init, | |
857a2622 XG |
523 | }; |
524 | ||
bd127b81 WH |
525 | static int port_stp_init(struct platform_device *pdev, |
526 | struct dfl_feature *feature) | |
527 | { | |
528 | struct resource *res = &pdev->resource[feature->resource_index]; | |
529 | ||
530 | return afu_mmio_region_add(dev_get_platdata(&pdev->dev), | |
531 | DFL_PORT_REGION_INDEX_STP, | |
532 | resource_size(res), res->start, | |
533 | DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ | | |
534 | DFL_PORT_REGION_WRITE); | |
535 | } | |
536 | ||
537 | static const struct dfl_feature_id port_stp_id_table[] = { | |
538 | {.id = PORT_FEATURE_ID_STP,}, | |
539 | {0,} | |
540 | }; | |
541 | ||
542 | static const struct dfl_feature_ops port_stp_ops = { | |
543 | .init = port_stp_init, | |
544 | }; | |
545 | ||
09d86150 XY |
546 | static long |
547 | port_uint_ioctl(struct platform_device *pdev, struct dfl_feature *feature, | |
548 | unsigned int cmd, unsigned long arg) | |
549 | { | |
550 | switch (cmd) { | |
551 | case DFL_FPGA_PORT_UINT_GET_IRQ_NUM: | |
552 | return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg); | |
553 | case DFL_FPGA_PORT_UINT_SET_IRQ: | |
554 | return dfl_feature_ioctl_set_irq(pdev, feature, arg); | |
555 | default: | |
556 | dev_dbg(&pdev->dev, "%x cmd not handled", cmd); | |
557 | return -ENODEV; | |
558 | } | |
559 | } | |
560 | ||
561 | static const struct dfl_feature_id port_uint_id_table[] = { | |
562 | {.id = PORT_FEATURE_ID_UINT,}, | |
563 | {0,} | |
564 | }; | |
565 | ||
566 | static const struct dfl_feature_ops port_uint_ops = { | |
567 | .ioctl = port_uint_ioctl, | |
568 | }; | |
569 | ||
1a1527cf WH |
570 | static struct dfl_feature_driver port_feature_drvs[] = { |
571 | { | |
15bbb300 | 572 | .id_table = port_hdr_id_table, |
1a1527cf WH |
573 | .ops = &port_hdr_ops, |
574 | }, | |
857a2622 | 575 | { |
15bbb300 | 576 | .id_table = port_afu_id_table, |
857a2622 XG |
577 | .ops = &port_afu_ops, |
578 | }, | |
44d24753 WH |
579 | { |
580 | .id_table = port_err_id_table, | |
581 | .ops = &port_err_ops, | |
582 | }, | |
bd127b81 WH |
583 | { |
584 | .id_table = port_stp_id_table, | |
585 | .ops = &port_stp_ops, | |
586 | }, | |
09d86150 XY |
587 | { |
588 | .id_table = port_uint_id_table, | |
589 | .ops = &port_uint_ops, | |
590 | }, | |
1a1527cf WH |
591 | { |
592 | .ops = NULL, | |
593 | } | |
594 | }; | |
595 | ||
596 | static int afu_open(struct inode *inode, struct file *filp) | |
597 | { | |
598 | struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode); | |
599 | struct dfl_feature_platform_data *pdata; | |
600 | int ret; | |
601 | ||
602 | pdata = dev_get_platdata(&fdev->dev); | |
603 | if (WARN_ON(!pdata)) | |
604 | return -ENODEV; | |
605 | ||
b6862193 XY |
606 | mutex_lock(&pdata->lock); |
607 | ret = dfl_feature_dev_use_begin(pdata, filp->f_flags & O_EXCL); | |
608 | if (!ret) { | |
609 | dev_dbg(&fdev->dev, "Device File Opened %d Times\n", | |
610 | dfl_feature_dev_use_count(pdata)); | |
611 | filp->private_data = fdev; | |
612 | } | |
613 | mutex_unlock(&pdata->lock); | |
1a1527cf | 614 | |
b6862193 | 615 | return ret; |
1a1527cf WH |
616 | } |
617 | ||
618 | static int afu_release(struct inode *inode, struct file *filp) | |
619 | { | |
620 | struct platform_device *pdev = filp->private_data; | |
621 | struct dfl_feature_platform_data *pdata; | |
fe6a3d65 | 622 | struct dfl_feature *feature; |
1a1527cf WH |
623 | |
624 | dev_dbg(&pdev->dev, "Device File Release\n"); | |
625 | ||
626 | pdata = dev_get_platdata(&pdev->dev); | |
627 | ||
fa8dda1e | 628 | mutex_lock(&pdata->lock); |
1a1527cf WH |
629 | dfl_feature_dev_use_end(pdata); |
630 | ||
b6862193 | 631 | if (!dfl_feature_dev_use_count(pdata)) { |
fe6a3d65 XY |
632 | dfl_fpga_dev_for_each_feature(pdata, feature) |
633 | dfl_fpga_set_irq_triggers(feature, 0, | |
634 | feature->nr_irqs, NULL); | |
b6862193 XY |
635 | __port_reset(pdev); |
636 | afu_dma_region_destroy(pdata); | |
637 | } | |
638 | mutex_unlock(&pdata->lock); | |
639 | ||
1a1527cf WH |
640 | return 0; |
641 | } | |
642 | ||
6fd893c4 WH |
643 | static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata, |
644 | unsigned long arg) | |
645 | { | |
646 | /* No extension support for now */ | |
647 | return 0; | |
648 | } | |
649 | ||
857a2622 XG |
650 | static long |
651 | afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg) | |
652 | { | |
653 | struct dfl_fpga_port_info info; | |
654 | struct dfl_afu *afu; | |
655 | unsigned long minsz; | |
656 | ||
657 | minsz = offsetofend(struct dfl_fpga_port_info, num_umsgs); | |
658 | ||
659 | if (copy_from_user(&info, arg, minsz)) | |
660 | return -EFAULT; | |
661 | ||
662 | if (info.argsz < minsz) | |
663 | return -EINVAL; | |
664 | ||
665 | mutex_lock(&pdata->lock); | |
666 | afu = dfl_fpga_pdata_get_private(pdata); | |
667 | info.flags = 0; | |
668 | info.num_regions = afu->num_regions; | |
669 | info.num_umsgs = afu->num_umsgs; | |
670 | mutex_unlock(&pdata->lock); | |
671 | ||
672 | if (copy_to_user(arg, &info, sizeof(info))) | |
673 | return -EFAULT; | |
674 | ||
675 | return 0; | |
676 | } | |
677 | ||
678 | static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata, | |
679 | void __user *arg) | |
680 | { | |
681 | struct dfl_fpga_port_region_info rinfo; | |
682 | struct dfl_afu_mmio_region region; | |
683 | unsigned long minsz; | |
684 | long ret; | |
685 | ||
686 | minsz = offsetofend(struct dfl_fpga_port_region_info, offset); | |
687 | ||
688 | if (copy_from_user(&rinfo, arg, minsz)) | |
689 | return -EFAULT; | |
690 | ||
691 | if (rinfo.argsz < minsz || rinfo.padding) | |
692 | return -EINVAL; | |
693 | ||
694 | ret = afu_mmio_region_get_by_index(pdata, rinfo.index, ®ion); | |
695 | if (ret) | |
696 | return ret; | |
697 | ||
698 | rinfo.flags = region.flags; | |
699 | rinfo.size = region.size; | |
700 | rinfo.offset = region.offset; | |
701 | ||
702 | if (copy_to_user(arg, &rinfo, sizeof(rinfo))) | |
703 | return -EFAULT; | |
704 | ||
705 | return 0; | |
706 | } | |
707 | ||
fa8dda1e WH |
708 | static long |
709 | afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg) | |
710 | { | |
711 | struct dfl_fpga_port_dma_map map; | |
712 | unsigned long minsz; | |
713 | long ret; | |
714 | ||
715 | minsz = offsetofend(struct dfl_fpga_port_dma_map, iova); | |
716 | ||
717 | if (copy_from_user(&map, arg, minsz)) | |
718 | return -EFAULT; | |
719 | ||
720 | if (map.argsz < minsz || map.flags) | |
721 | return -EINVAL; | |
722 | ||
723 | ret = afu_dma_map_region(pdata, map.user_addr, map.length, &map.iova); | |
724 | if (ret) | |
725 | return ret; | |
726 | ||
727 | if (copy_to_user(arg, &map, sizeof(map))) { | |
728 | afu_dma_unmap_region(pdata, map.iova); | |
729 | return -EFAULT; | |
730 | } | |
731 | ||
732 | dev_dbg(&pdata->dev->dev, "dma map: ua=%llx, len=%llx, iova=%llx\n", | |
733 | (unsigned long long)map.user_addr, | |
734 | (unsigned long long)map.length, | |
735 | (unsigned long long)map.iova); | |
736 | ||
737 | return 0; | |
738 | } | |
739 | ||
740 | static long | |
741 | afu_ioctl_dma_unmap(struct dfl_feature_platform_data *pdata, void __user *arg) | |
742 | { | |
743 | struct dfl_fpga_port_dma_unmap unmap; | |
744 | unsigned long minsz; | |
745 | ||
746 | minsz = offsetofend(struct dfl_fpga_port_dma_unmap, iova); | |
747 | ||
748 | if (copy_from_user(&unmap, arg, minsz)) | |
749 | return -EFAULT; | |
750 | ||
751 | if (unmap.argsz < minsz || unmap.flags) | |
752 | return -EINVAL; | |
753 | ||
754 | return afu_dma_unmap_region(pdata, unmap.iova); | |
755 | } | |
756 | ||
1a1527cf WH |
757 | static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
758 | { | |
759 | struct platform_device *pdev = filp->private_data; | |
760 | struct dfl_feature_platform_data *pdata; | |
761 | struct dfl_feature *f; | |
762 | long ret; | |
763 | ||
764 | dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd); | |
765 | ||
766 | pdata = dev_get_platdata(&pdev->dev); | |
767 | ||
768 | switch (cmd) { | |
6fd893c4 WH |
769 | case DFL_FPGA_GET_API_VERSION: |
770 | return DFL_FPGA_API_VERSION; | |
771 | case DFL_FPGA_CHECK_EXTENSION: | |
772 | return afu_ioctl_check_extension(pdata, arg); | |
857a2622 XG |
773 | case DFL_FPGA_PORT_GET_INFO: |
774 | return afu_ioctl_get_info(pdata, (void __user *)arg); | |
775 | case DFL_FPGA_PORT_GET_REGION_INFO: | |
776 | return afu_ioctl_get_region_info(pdata, (void __user *)arg); | |
fa8dda1e WH |
777 | case DFL_FPGA_PORT_DMA_MAP: |
778 | return afu_ioctl_dma_map(pdata, (void __user *)arg); | |
779 | case DFL_FPGA_PORT_DMA_UNMAP: | |
780 | return afu_ioctl_dma_unmap(pdata, (void __user *)arg); | |
1a1527cf WH |
781 | default: |
782 | /* | |
783 | * Let sub-feature's ioctl function to handle the cmd | |
784 | * Sub-feature's ioctl returns -ENODEV when cmd is not | |
785 | * handled in this sub feature, and returns 0 and other | |
786 | * error code if cmd is handled. | |
787 | */ | |
788 | dfl_fpga_dev_for_each_feature(pdata, f) | |
789 | if (f->ops && f->ops->ioctl) { | |
790 | ret = f->ops->ioctl(pdev, f, cmd, arg); | |
791 | if (ret != -ENODEV) | |
792 | return ret; | |
793 | } | |
794 | } | |
795 | ||
796 | return -EINVAL; | |
797 | } | |
798 | ||
a2b9d4ea DC |
799 | static const struct vm_operations_struct afu_vma_ops = { |
800 | #ifdef CONFIG_HAVE_IOREMAP_PROT | |
801 | .access = generic_access_phys, | |
802 | #endif | |
803 | }; | |
804 | ||
857a2622 XG |
805 | static int afu_mmap(struct file *filp, struct vm_area_struct *vma) |
806 | { | |
807 | struct platform_device *pdev = filp->private_data; | |
808 | struct dfl_feature_platform_data *pdata; | |
809 | u64 size = vma->vm_end - vma->vm_start; | |
810 | struct dfl_afu_mmio_region region; | |
811 | u64 offset; | |
812 | int ret; | |
813 | ||
814 | if (!(vma->vm_flags & VM_SHARED)) | |
815 | return -EINVAL; | |
816 | ||
817 | pdata = dev_get_platdata(&pdev->dev); | |
818 | ||
819 | offset = vma->vm_pgoff << PAGE_SHIFT; | |
820 | ret = afu_mmio_region_get_by_offset(pdata, offset, size, ®ion); | |
821 | if (ret) | |
822 | return ret; | |
823 | ||
824 | if (!(region.flags & DFL_PORT_REGION_MMAP)) | |
825 | return -EINVAL; | |
826 | ||
827 | if ((vma->vm_flags & VM_READ) && !(region.flags & DFL_PORT_REGION_READ)) | |
828 | return -EPERM; | |
829 | ||
830 | if ((vma->vm_flags & VM_WRITE) && | |
831 | !(region.flags & DFL_PORT_REGION_WRITE)) | |
832 | return -EPERM; | |
833 | ||
a2b9d4ea DC |
834 | /* Support debug access to the mapping */ |
835 | vma->vm_ops = &afu_vma_ops; | |
836 | ||
857a2622 XG |
837 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); |
838 | ||
839 | return remap_pfn_range(vma, vma->vm_start, | |
840 | (region.phys + (offset - region.offset)) >> PAGE_SHIFT, | |
841 | size, vma->vm_page_prot); | |
842 | } | |
843 | ||
1a1527cf WH |
844 | static const struct file_operations afu_fops = { |
845 | .owner = THIS_MODULE, | |
846 | .open = afu_open, | |
847 | .release = afu_release, | |
848 | .unlocked_ioctl = afu_ioctl, | |
857a2622 | 849 | .mmap = afu_mmap, |
1a1527cf WH |
850 | }; |
851 | ||
857a2622 XG |
852 | static int afu_dev_init(struct platform_device *pdev) |
853 | { | |
854 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); | |
855 | struct dfl_afu *afu; | |
856 | ||
857 | afu = devm_kzalloc(&pdev->dev, sizeof(*afu), GFP_KERNEL); | |
858 | if (!afu) | |
859 | return -ENOMEM; | |
860 | ||
861 | afu->pdata = pdata; | |
862 | ||
863 | mutex_lock(&pdata->lock); | |
864 | dfl_fpga_pdata_set_private(pdata, afu); | |
865 | afu_mmio_region_init(pdata); | |
fa8dda1e | 866 | afu_dma_region_init(pdata); |
857a2622 XG |
867 | mutex_unlock(&pdata->lock); |
868 | ||
869 | return 0; | |
870 | } | |
871 | ||
872 | static int afu_dev_destroy(struct platform_device *pdev) | |
873 | { | |
874 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); | |
857a2622 XG |
875 | |
876 | mutex_lock(&pdata->lock); | |
857a2622 | 877 | afu_mmio_region_destroy(pdata); |
fa8dda1e | 878 | afu_dma_region_destroy(pdata); |
857a2622 XG |
879 | dfl_fpga_pdata_set_private(pdata, NULL); |
880 | mutex_unlock(&pdata->lock); | |
881 | ||
882 | return 0; | |
883 | } | |
884 | ||
47c1b19c WH |
885 | static int port_enable_set(struct platform_device *pdev, bool enable) |
886 | { | |
887 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); | |
9a8d3cda | 888 | int ret; |
47c1b19c WH |
889 | |
890 | mutex_lock(&pdata->lock); | |
891 | if (enable) | |
9a8d3cda | 892 | ret = __afu_port_enable(pdev); |
47c1b19c | 893 | else |
95844372 | 894 | ret = __afu_port_disable(pdev); |
47c1b19c WH |
895 | mutex_unlock(&pdata->lock); |
896 | ||
897 | return ret; | |
898 | } | |
899 | ||
900 | static struct dfl_fpga_port_ops afu_port_ops = { | |
901 | .name = DFL_FPGA_FEATURE_DEV_PORT, | |
902 | .owner = THIS_MODULE, | |
903 | .get_id = port_get_id, | |
904 | .enable_set = port_enable_set, | |
905 | }; | |
906 | ||
1a1527cf WH |
907 | static int afu_probe(struct platform_device *pdev) |
908 | { | |
909 | int ret; | |
910 | ||
911 | dev_dbg(&pdev->dev, "%s\n", __func__); | |
912 | ||
857a2622 XG |
913 | ret = afu_dev_init(pdev); |
914 | if (ret) | |
915 | goto exit; | |
916 | ||
1a1527cf WH |
917 | ret = dfl_fpga_dev_feature_init(pdev, port_feature_drvs); |
918 | if (ret) | |
857a2622 | 919 | goto dev_destroy; |
1a1527cf WH |
920 | |
921 | ret = dfl_fpga_dev_ops_register(pdev, &afu_fops, THIS_MODULE); | |
857a2622 | 922 | if (ret) { |
1a1527cf | 923 | dfl_fpga_dev_feature_uinit(pdev); |
857a2622 XG |
924 | goto dev_destroy; |
925 | } | |
926 | ||
927 | return 0; | |
1a1527cf | 928 | |
857a2622 XG |
929 | dev_destroy: |
930 | afu_dev_destroy(pdev); | |
931 | exit: | |
1a1527cf WH |
932 | return ret; |
933 | } | |
934 | ||
935 | static int afu_remove(struct platform_device *pdev) | |
936 | { | |
937 | dev_dbg(&pdev->dev, "%s\n", __func__); | |
938 | ||
939 | dfl_fpga_dev_ops_unregister(pdev); | |
940 | dfl_fpga_dev_feature_uinit(pdev); | |
857a2622 | 941 | afu_dev_destroy(pdev); |
1a1527cf WH |
942 | |
943 | return 0; | |
944 | } | |
945 | ||
a80a4b82 WH |
946 | static const struct attribute_group *afu_dev_groups[] = { |
947 | &port_hdr_group, | |
948 | &port_afu_group, | |
44d24753 | 949 | &port_err_group, |
a80a4b82 WH |
950 | NULL |
951 | }; | |
952 | ||
1a1527cf WH |
953 | static struct platform_driver afu_driver = { |
954 | .driver = { | |
a80a4b82 WH |
955 | .name = DFL_FPGA_FEATURE_DEV_PORT, |
956 | .dev_groups = afu_dev_groups, | |
1a1527cf WH |
957 | }, |
958 | .probe = afu_probe, | |
959 | .remove = afu_remove, | |
960 | }; | |
961 | ||
47c1b19c WH |
962 | static int __init afu_init(void) |
963 | { | |
964 | int ret; | |
965 | ||
966 | dfl_fpga_port_ops_add(&afu_port_ops); | |
967 | ||
968 | ret = platform_driver_register(&afu_driver); | |
969 | if (ret) | |
970 | dfl_fpga_port_ops_del(&afu_port_ops); | |
971 | ||
972 | return ret; | |
973 | } | |
974 | ||
975 | static void __exit afu_exit(void) | |
976 | { | |
977 | platform_driver_unregister(&afu_driver); | |
978 | ||
979 | dfl_fpga_port_ops_del(&afu_port_ops); | |
980 | } | |
981 | ||
982 | module_init(afu_init); | |
983 | module_exit(afu_exit); | |
1a1527cf WH |
984 | |
985 | MODULE_DESCRIPTION("FPGA Accelerated Function Unit driver"); | |
986 | MODULE_AUTHOR("Intel Corporation"); | |
987 | MODULE_LICENSE("GPL v2"); | |
988 | MODULE_ALIAS("platform:dfl-port"); |