1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for FPGA Management Engine (FME)
5 * Copyright (C) 2017-2018 Intel Corporation, Inc.
8 * Kang Luwei <luwei.kang@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>
17 #include <linux/hwmon.h>
18 #include <linux/hwmon-sysfs.h>
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/uaccess.h>
22 #include <linux/fpga-dfl.h>
27 static ssize_t
ports_num_show(struct device
*dev
,
28 struct device_attribute
*attr
, char *buf
)
33 base
= dfl_get_feature_ioaddr_by_id(dev
, FME_FEATURE_ID_HEADER
);
35 v
= readq(base
+ FME_HDR_CAP
);
37 return scnprintf(buf
, PAGE_SIZE
, "%u\n",
38 (unsigned int)FIELD_GET(FME_CAP_NUM_PORTS
, v
));
40 static DEVICE_ATTR_RO(ports_num
);
43 * Bitstream (static FPGA region) identifier number. It contains the
44 * detailed version and other information of this static FPGA region.
46 static ssize_t
bitstream_id_show(struct device
*dev
,
47 struct device_attribute
*attr
, char *buf
)
52 base
= dfl_get_feature_ioaddr_by_id(dev
, FME_FEATURE_ID_HEADER
);
54 v
= readq(base
+ FME_HDR_BITSTREAM_ID
);
56 return scnprintf(buf
, PAGE_SIZE
, "0x%llx\n", (unsigned long long)v
);
58 static DEVICE_ATTR_RO(bitstream_id
);
61 * Bitstream (static FPGA region) meta data. It contains the synthesis
62 * date, seed and other information of this static FPGA region.
64 static ssize_t
bitstream_metadata_show(struct device
*dev
,
65 struct device_attribute
*attr
, char *buf
)
70 base
= dfl_get_feature_ioaddr_by_id(dev
, FME_FEATURE_ID_HEADER
);
72 v
= readq(base
+ FME_HDR_BITSTREAM_MD
);
74 return scnprintf(buf
, PAGE_SIZE
, "0x%llx\n", (unsigned long long)v
);
76 static DEVICE_ATTR_RO(bitstream_metadata
);
78 static ssize_t
cache_size_show(struct device
*dev
,
79 struct device_attribute
*attr
, char *buf
)
84 base
= dfl_get_feature_ioaddr_by_id(dev
, FME_FEATURE_ID_HEADER
);
86 v
= readq(base
+ FME_HDR_CAP
);
88 return sprintf(buf
, "%u\n",
89 (unsigned int)FIELD_GET(FME_CAP_CACHE_SIZE
, v
));
91 static DEVICE_ATTR_RO(cache_size
);
93 static ssize_t
fabric_version_show(struct device
*dev
,
94 struct device_attribute
*attr
, char *buf
)
99 base
= dfl_get_feature_ioaddr_by_id(dev
, FME_FEATURE_ID_HEADER
);
101 v
= readq(base
+ FME_HDR_CAP
);
103 return sprintf(buf
, "%u\n",
104 (unsigned int)FIELD_GET(FME_CAP_FABRIC_VERID
, v
));
106 static DEVICE_ATTR_RO(fabric_version
);
108 static ssize_t
socket_id_show(struct device
*dev
,
109 struct device_attribute
*attr
, char *buf
)
114 base
= dfl_get_feature_ioaddr_by_id(dev
, FME_FEATURE_ID_HEADER
);
116 v
= readq(base
+ FME_HDR_CAP
);
118 return sprintf(buf
, "%u\n",
119 (unsigned int)FIELD_GET(FME_CAP_SOCKET_ID
, v
));
121 static DEVICE_ATTR_RO(socket_id
);
123 static struct attribute
*fme_hdr_attrs
[] = {
124 &dev_attr_ports_num
.attr
,
125 &dev_attr_bitstream_id
.attr
,
126 &dev_attr_bitstream_metadata
.attr
,
127 &dev_attr_cache_size
.attr
,
128 &dev_attr_fabric_version
.attr
,
129 &dev_attr_socket_id
.attr
,
133 static const struct attribute_group fme_hdr_group
= {
134 .attrs
= fme_hdr_attrs
,
137 static long fme_hdr_ioctl_release_port(struct dfl_feature_platform_data
*pdata
,
140 struct dfl_fpga_cdev
*cdev
= pdata
->dfl_cdev
;
143 if (get_user(port_id
, (int __user
*)arg
))
146 return dfl_fpga_cdev_release_port(cdev
, port_id
);
149 static long fme_hdr_ioctl_assign_port(struct dfl_feature_platform_data
*pdata
,
152 struct dfl_fpga_cdev
*cdev
= pdata
->dfl_cdev
;
155 if (get_user(port_id
, (int __user
*)arg
))
158 return dfl_fpga_cdev_assign_port(cdev
, port_id
);
161 static long fme_hdr_ioctl(struct platform_device
*pdev
,
162 struct dfl_feature
*feature
,
163 unsigned int cmd
, unsigned long arg
)
165 struct dfl_feature_platform_data
*pdata
= dev_get_platdata(&pdev
->dev
);
168 case DFL_FPGA_FME_PORT_RELEASE
:
169 return fme_hdr_ioctl_release_port(pdata
, arg
);
170 case DFL_FPGA_FME_PORT_ASSIGN
:
171 return fme_hdr_ioctl_assign_port(pdata
, arg
);
177 static const struct dfl_feature_id fme_hdr_id_table
[] = {
178 {.id
= FME_FEATURE_ID_HEADER
,},
182 static const struct dfl_feature_ops fme_hdr_ops
= {
183 .ioctl
= fme_hdr_ioctl
,
186 #define FME_THERM_THRESHOLD 0x8
187 #define TEMP_THRESHOLD1 GENMASK_ULL(6, 0)
188 #define TEMP_THRESHOLD1_EN BIT_ULL(7)
189 #define TEMP_THRESHOLD2 GENMASK_ULL(14, 8)
190 #define TEMP_THRESHOLD2_EN BIT_ULL(15)
191 #define TRIP_THRESHOLD GENMASK_ULL(30, 24)
192 #define TEMP_THRESHOLD1_STATUS BIT_ULL(32) /* threshold1 reached */
193 #define TEMP_THRESHOLD2_STATUS BIT_ULL(33) /* threshold2 reached */
194 /* threshold1 policy: 0 - AP2 (90% throttle) / 1 - AP1 (50% throttle) */
195 #define TEMP_THRESHOLD1_POLICY BIT_ULL(44)
197 #define FME_THERM_RDSENSOR_FMT1 0x10
198 #define FPGA_TEMPERATURE GENMASK_ULL(6, 0)
200 #define FME_THERM_CAP 0x20
201 #define THERM_NO_THROTTLE BIT_ULL(0)
205 static bool fme_thermal_throttle_support(void __iomem
*base
)
207 u64 v
= readq(base
+ FME_THERM_CAP
);
209 return FIELD_GET(THERM_NO_THROTTLE
, v
) ? false : true;
212 static umode_t
thermal_hwmon_attrs_visible(const void *drvdata
,
213 enum hwmon_sensor_types type
,
214 u32 attr
, int channel
)
216 const struct dfl_feature
*feature
= drvdata
;
218 /* temperature is always supported, and check hardware cap for others */
219 if (attr
== hwmon_temp_input
)
222 return fme_thermal_throttle_support(feature
->ioaddr
) ? 0444 : 0;
225 static int thermal_hwmon_read(struct device
*dev
, enum hwmon_sensor_types type
,
226 u32 attr
, int channel
, long *val
)
228 struct dfl_feature
*feature
= dev_get_drvdata(dev
);
232 case hwmon_temp_input
:
233 v
= readq(feature
->ioaddr
+ FME_THERM_RDSENSOR_FMT1
);
234 *val
= (long)(FIELD_GET(FPGA_TEMPERATURE
, v
) * 1000);
237 v
= readq(feature
->ioaddr
+ FME_THERM_THRESHOLD
);
238 *val
= (long)(FIELD_GET(TEMP_THRESHOLD1
, v
) * 1000);
240 case hwmon_temp_crit
:
241 v
= readq(feature
->ioaddr
+ FME_THERM_THRESHOLD
);
242 *val
= (long)(FIELD_GET(TEMP_THRESHOLD2
, v
) * 1000);
244 case hwmon_temp_emergency
:
245 v
= readq(feature
->ioaddr
+ FME_THERM_THRESHOLD
);
246 *val
= (long)(FIELD_GET(TRIP_THRESHOLD
, v
) * 1000);
248 case hwmon_temp_max_alarm
:
249 v
= readq(feature
->ioaddr
+ FME_THERM_THRESHOLD
);
250 *val
= (long)FIELD_GET(TEMP_THRESHOLD1_STATUS
, v
);
252 case hwmon_temp_crit_alarm
:
253 v
= readq(feature
->ioaddr
+ FME_THERM_THRESHOLD
);
254 *val
= (long)FIELD_GET(TEMP_THRESHOLD2_STATUS
, v
);
263 static const struct hwmon_ops thermal_hwmon_ops
= {
264 .is_visible
= thermal_hwmon_attrs_visible
,
265 .read
= thermal_hwmon_read
,
268 static const struct hwmon_channel_info
*thermal_hwmon_info
[] = {
269 HWMON_CHANNEL_INFO(temp
, HWMON_T_INPUT
| HWMON_T_EMERGENCY
|
270 HWMON_T_MAX
| HWMON_T_MAX_ALARM
|
271 HWMON_T_CRIT
| HWMON_T_CRIT_ALARM
),
275 static const struct hwmon_chip_info thermal_hwmon_chip_info
= {
276 .ops
= &thermal_hwmon_ops
,
277 .info
= thermal_hwmon_info
,
280 static ssize_t
temp1_max_policy_show(struct device
*dev
,
281 struct device_attribute
*attr
, char *buf
)
283 struct dfl_feature
*feature
= dev_get_drvdata(dev
);
286 v
= readq(feature
->ioaddr
+ FME_THERM_THRESHOLD
);
288 return sprintf(buf
, "%u\n",
289 (unsigned int)FIELD_GET(TEMP_THRESHOLD1_POLICY
, v
));
292 static DEVICE_ATTR_RO(temp1_max_policy
);
294 static struct attribute
*thermal_extra_attrs
[] = {
295 &dev_attr_temp1_max_policy
.attr
,
299 static umode_t
thermal_extra_attrs_visible(struct kobject
*kobj
,
300 struct attribute
*attr
, int index
)
302 struct device
*dev
= kobj_to_dev(kobj
);
303 struct dfl_feature
*feature
= dev_get_drvdata(dev
);
305 return fme_thermal_throttle_support(feature
->ioaddr
) ? attr
->mode
: 0;
308 static const struct attribute_group thermal_extra_group
= {
309 .attrs
= thermal_extra_attrs
,
310 .is_visible
= thermal_extra_attrs_visible
,
312 __ATTRIBUTE_GROUPS(thermal_extra
);
314 static int fme_thermal_mgmt_init(struct platform_device
*pdev
,
315 struct dfl_feature
*feature
)
317 struct device
*hwmon
;
320 * create hwmon to allow userspace monitoring temperature and other
321 * threshold information.
323 * temp1_input -> FPGA device temperature
324 * temp1_max -> hardware threshold 1 -> 50% or 90% throttling
325 * temp1_crit -> hardware threshold 2 -> 100% throttling
326 * temp1_emergency -> hardware trip_threshold to shutdown FPGA
327 * temp1_max_alarm -> hardware threshold 1 alarm
328 * temp1_crit_alarm -> hardware threshold 2 alarm
330 * create device specific sysfs interfaces, e.g. read temp1_max_policy
331 * to understand the actual hardware throttling action (50% vs 90%).
333 * If hardware doesn't support automatic throttling per thresholds,
334 * then all above sysfs interfaces are not visible except temp1_input
337 hwmon
= devm_hwmon_device_register_with_info(&pdev
->dev
,
338 "dfl_fme_thermal", feature
,
339 &thermal_hwmon_chip_info
,
340 thermal_extra_groups
);
342 dev_err(&pdev
->dev
, "Fail to register thermal hwmon\n");
343 return PTR_ERR(hwmon
);
349 static const struct dfl_feature_id fme_thermal_mgmt_id_table
[] = {
350 {.id
= FME_FEATURE_ID_THERMAL_MGMT
,},
354 static const struct dfl_feature_ops fme_thermal_mgmt_ops
= {
355 .init
= fme_thermal_mgmt_init
,
358 #define FME_PWR_STATUS 0x8
359 #define FME_LATENCY_TOLERANCE BIT_ULL(18)
360 #define PWR_CONSUMED GENMASK_ULL(17, 0)
362 #define FME_PWR_THRESHOLD 0x10
363 #define PWR_THRESHOLD1 GENMASK_ULL(6, 0) /* in Watts */
364 #define PWR_THRESHOLD2 GENMASK_ULL(14, 8) /* in Watts */
365 #define PWR_THRESHOLD_MAX 0x7f /* in Watts */
366 #define PWR_THRESHOLD1_STATUS BIT_ULL(16)
367 #define PWR_THRESHOLD2_STATUS BIT_ULL(17)
369 #define FME_PWR_XEON_LIMIT 0x18
370 #define XEON_PWR_LIMIT GENMASK_ULL(14, 0) /* in 0.1 Watts */
371 #define XEON_PWR_EN BIT_ULL(15)
372 #define FME_PWR_FPGA_LIMIT 0x20
373 #define FPGA_PWR_LIMIT GENMASK_ULL(14, 0) /* in 0.1 Watts */
374 #define FPGA_PWR_EN BIT_ULL(15)
376 static int power_hwmon_read(struct device
*dev
, enum hwmon_sensor_types type
,
377 u32 attr
, int channel
, long *val
)
379 struct dfl_feature
*feature
= dev_get_drvdata(dev
);
383 case hwmon_power_input
:
384 v
= readq(feature
->ioaddr
+ FME_PWR_STATUS
);
385 *val
= (long)(FIELD_GET(PWR_CONSUMED
, v
) * 1000000);
387 case hwmon_power_max
:
388 v
= readq(feature
->ioaddr
+ FME_PWR_THRESHOLD
);
389 *val
= (long)(FIELD_GET(PWR_THRESHOLD1
, v
) * 1000000);
391 case hwmon_power_crit
:
392 v
= readq(feature
->ioaddr
+ FME_PWR_THRESHOLD
);
393 *val
= (long)(FIELD_GET(PWR_THRESHOLD2
, v
) * 1000000);
395 case hwmon_power_max_alarm
:
396 v
= readq(feature
->ioaddr
+ FME_PWR_THRESHOLD
);
397 *val
= (long)FIELD_GET(PWR_THRESHOLD1_STATUS
, v
);
399 case hwmon_power_crit_alarm
:
400 v
= readq(feature
->ioaddr
+ FME_PWR_THRESHOLD
);
401 *val
= (long)FIELD_GET(PWR_THRESHOLD2_STATUS
, v
);
410 static int power_hwmon_write(struct device
*dev
, enum hwmon_sensor_types type
,
411 u32 attr
, int channel
, long val
)
413 struct dfl_feature_platform_data
*pdata
= dev_get_platdata(dev
->parent
);
414 struct dfl_feature
*feature
= dev_get_drvdata(dev
);
418 val
= clamp_val(val
/ 1000000, 0, PWR_THRESHOLD_MAX
);
420 mutex_lock(&pdata
->lock
);
423 case hwmon_power_max
:
424 v
= readq(feature
->ioaddr
+ FME_PWR_THRESHOLD
);
425 v
&= ~PWR_THRESHOLD1
;
426 v
|= FIELD_PREP(PWR_THRESHOLD1
, val
);
427 writeq(v
, feature
->ioaddr
+ FME_PWR_THRESHOLD
);
429 case hwmon_power_crit
:
430 v
= readq(feature
->ioaddr
+ FME_PWR_THRESHOLD
);
431 v
&= ~PWR_THRESHOLD2
;
432 v
|= FIELD_PREP(PWR_THRESHOLD2
, val
);
433 writeq(v
, feature
->ioaddr
+ FME_PWR_THRESHOLD
);
440 mutex_unlock(&pdata
->lock
);
445 static umode_t
power_hwmon_attrs_visible(const void *drvdata
,
446 enum hwmon_sensor_types type
,
447 u32 attr
, int channel
)
450 case hwmon_power_input
:
451 case hwmon_power_max_alarm
:
452 case hwmon_power_crit_alarm
:
454 case hwmon_power_max
:
455 case hwmon_power_crit
:
462 static const struct hwmon_ops power_hwmon_ops
= {
463 .is_visible
= power_hwmon_attrs_visible
,
464 .read
= power_hwmon_read
,
465 .write
= power_hwmon_write
,
468 static const struct hwmon_channel_info
*power_hwmon_info
[] = {
469 HWMON_CHANNEL_INFO(power
, HWMON_P_INPUT
|
470 HWMON_P_MAX
| HWMON_P_MAX_ALARM
|
471 HWMON_P_CRIT
| HWMON_P_CRIT_ALARM
),
475 static const struct hwmon_chip_info power_hwmon_chip_info
= {
476 .ops
= &power_hwmon_ops
,
477 .info
= power_hwmon_info
,
480 static ssize_t
power1_xeon_limit_show(struct device
*dev
,
481 struct device_attribute
*attr
, char *buf
)
483 struct dfl_feature
*feature
= dev_get_drvdata(dev
);
487 v
= readq(feature
->ioaddr
+ FME_PWR_XEON_LIMIT
);
489 if (FIELD_GET(XEON_PWR_EN
, v
))
490 xeon_limit
= FIELD_GET(XEON_PWR_LIMIT
, v
);
492 return sprintf(buf
, "%u\n", xeon_limit
* 100000);
495 static ssize_t
power1_fpga_limit_show(struct device
*dev
,
496 struct device_attribute
*attr
, char *buf
)
498 struct dfl_feature
*feature
= dev_get_drvdata(dev
);
502 v
= readq(feature
->ioaddr
+ FME_PWR_FPGA_LIMIT
);
504 if (FIELD_GET(FPGA_PWR_EN
, v
))
505 fpga_limit
= FIELD_GET(FPGA_PWR_LIMIT
, v
);
507 return sprintf(buf
, "%u\n", fpga_limit
* 100000);
510 static ssize_t
power1_ltr_show(struct device
*dev
,
511 struct device_attribute
*attr
, char *buf
)
513 struct dfl_feature
*feature
= dev_get_drvdata(dev
);
516 v
= readq(feature
->ioaddr
+ FME_PWR_STATUS
);
518 return sprintf(buf
, "%u\n",
519 (unsigned int)FIELD_GET(FME_LATENCY_TOLERANCE
, v
));
522 static DEVICE_ATTR_RO(power1_xeon_limit
);
523 static DEVICE_ATTR_RO(power1_fpga_limit
);
524 static DEVICE_ATTR_RO(power1_ltr
);
526 static struct attribute
*power_extra_attrs
[] = {
527 &dev_attr_power1_xeon_limit
.attr
,
528 &dev_attr_power1_fpga_limit
.attr
,
529 &dev_attr_power1_ltr
.attr
,
533 ATTRIBUTE_GROUPS(power_extra
);
535 static int fme_power_mgmt_init(struct platform_device
*pdev
,
536 struct dfl_feature
*feature
)
538 struct device
*hwmon
;
540 hwmon
= devm_hwmon_device_register_with_info(&pdev
->dev
,
541 "dfl_fme_power", feature
,
542 &power_hwmon_chip_info
,
545 dev_err(&pdev
->dev
, "Fail to register power hwmon\n");
546 return PTR_ERR(hwmon
);
552 static const struct dfl_feature_id fme_power_mgmt_id_table
[] = {
553 {.id
= FME_FEATURE_ID_POWER_MGMT
,},
557 static const struct dfl_feature_ops fme_power_mgmt_ops
= {
558 .init
= fme_power_mgmt_init
,
561 static struct dfl_feature_driver fme_feature_drvs
[] = {
563 .id_table
= fme_hdr_id_table
,
567 .id_table
= fme_pr_mgmt_id_table
,
568 .ops
= &fme_pr_mgmt_ops
,
571 .id_table
= fme_global_err_id_table
,
572 .ops
= &fme_global_err_ops
,
575 .id_table
= fme_thermal_mgmt_id_table
,
576 .ops
= &fme_thermal_mgmt_ops
,
579 .id_table
= fme_power_mgmt_id_table
,
580 .ops
= &fme_power_mgmt_ops
,
583 .id_table
= fme_perf_id_table
,
584 .ops
= &fme_perf_ops
,
591 static long fme_ioctl_check_extension(struct dfl_feature_platform_data
*pdata
,
594 /* No extension support for now */
598 static int fme_open(struct inode
*inode
, struct file
*filp
)
600 struct platform_device
*fdev
= dfl_fpga_inode_to_feature_dev(inode
);
601 struct dfl_feature_platform_data
*pdata
= dev_get_platdata(&fdev
->dev
);
607 mutex_lock(&pdata
->lock
);
608 ret
= dfl_feature_dev_use_begin(pdata
, filp
->f_flags
& O_EXCL
);
610 dev_dbg(&fdev
->dev
, "Device File Opened %d Times\n",
611 dfl_feature_dev_use_count(pdata
));
612 filp
->private_data
= pdata
;
614 mutex_unlock(&pdata
->lock
);
619 static int fme_release(struct inode
*inode
, struct file
*filp
)
621 struct dfl_feature_platform_data
*pdata
= filp
->private_data
;
622 struct platform_device
*pdev
= pdata
->dev
;
623 struct dfl_feature
*feature
;
625 dev_dbg(&pdev
->dev
, "Device File Release\n");
627 mutex_lock(&pdata
->lock
);
628 dfl_feature_dev_use_end(pdata
);
630 if (!dfl_feature_dev_use_count(pdata
))
631 dfl_fpga_dev_for_each_feature(pdata
, feature
)
632 dfl_fpga_set_irq_triggers(feature
, 0,
633 feature
->nr_irqs
, NULL
);
634 mutex_unlock(&pdata
->lock
);
639 static long fme_ioctl(struct file
*filp
, unsigned int cmd
, unsigned long arg
)
641 struct dfl_feature_platform_data
*pdata
= filp
->private_data
;
642 struct platform_device
*pdev
= pdata
->dev
;
643 struct dfl_feature
*f
;
646 dev_dbg(&pdev
->dev
, "%s cmd 0x%x\n", __func__
, cmd
);
649 case DFL_FPGA_GET_API_VERSION
:
650 return DFL_FPGA_API_VERSION
;
651 case DFL_FPGA_CHECK_EXTENSION
:
652 return fme_ioctl_check_extension(pdata
, arg
);
655 * Let sub-feature's ioctl function to handle the cmd.
656 * Sub-feature's ioctl returns -ENODEV when cmd is not
657 * handled in this sub feature, and returns 0 or other
658 * error code if cmd is handled.
660 dfl_fpga_dev_for_each_feature(pdata
, f
) {
661 if (f
->ops
&& f
->ops
->ioctl
) {
662 ret
= f
->ops
->ioctl(pdev
, f
, cmd
, arg
);
672 static int fme_dev_init(struct platform_device
*pdev
)
674 struct dfl_feature_platform_data
*pdata
= dev_get_platdata(&pdev
->dev
);
677 fme
= devm_kzalloc(&pdev
->dev
, sizeof(*fme
), GFP_KERNEL
);
683 mutex_lock(&pdata
->lock
);
684 dfl_fpga_pdata_set_private(pdata
, fme
);
685 mutex_unlock(&pdata
->lock
);
690 static void fme_dev_destroy(struct platform_device
*pdev
)
692 struct dfl_feature_platform_data
*pdata
= dev_get_platdata(&pdev
->dev
);
694 mutex_lock(&pdata
->lock
);
695 dfl_fpga_pdata_set_private(pdata
, NULL
);
696 mutex_unlock(&pdata
->lock
);
699 static const struct file_operations fme_fops
= {
700 .owner
= THIS_MODULE
,
702 .release
= fme_release
,
703 .unlocked_ioctl
= fme_ioctl
,
706 static int fme_probe(struct platform_device
*pdev
)
710 ret
= fme_dev_init(pdev
);
714 ret
= dfl_fpga_dev_feature_init(pdev
, fme_feature_drvs
);
718 ret
= dfl_fpga_dev_ops_register(pdev
, &fme_fops
, THIS_MODULE
);
725 dfl_fpga_dev_feature_uinit(pdev
);
727 fme_dev_destroy(pdev
);
732 static int fme_remove(struct platform_device
*pdev
)
734 dfl_fpga_dev_ops_unregister(pdev
);
735 dfl_fpga_dev_feature_uinit(pdev
);
736 fme_dev_destroy(pdev
);
741 static const struct attribute_group
*fme_dev_groups
[] = {
743 &fme_global_err_group
,
747 static struct platform_driver fme_driver
= {
749 .name
= DFL_FPGA_FEATURE_DEV_FME
,
750 .dev_groups
= fme_dev_groups
,
753 .remove
= fme_remove
,
756 module_platform_driver(fme_driver
);
758 MODULE_DESCRIPTION("FPGA Management Engine driver");
759 MODULE_AUTHOR("Intel Corporation");
760 MODULE_LICENSE("GPL v2");
761 MODULE_ALIAS("platform:dfl-fme");