18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for FPGA Management Engine (FME) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017-2018 Intel Corporation, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: 88c2ecf20Sopenharmony_ci * Kang Luwei <luwei.kang@intel.com> 98c2ecf20Sopenharmony_ci * Xiao Guangrong <guangrong.xiao@linux.intel.com> 108c2ecf20Sopenharmony_ci * Joseph Grecco <joe.grecco@intel.com> 118c2ecf20Sopenharmony_ci * Enno Luebbers <enno.luebbers@intel.com> 128c2ecf20Sopenharmony_ci * Tim Whisonant <tim.whisonant@intel.com> 138c2ecf20Sopenharmony_ci * Ananda Ravuri <ananda.ravuri@intel.com> 148c2ecf20Sopenharmony_ci * Henry Mitchel <henry.mitchel@intel.com> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 188c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 228c2ecf20Sopenharmony_ci#include <linux/fpga-dfl.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "dfl.h" 258c2ecf20Sopenharmony_ci#include "dfl-fme.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic ssize_t ports_num_show(struct device *dev, 288c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci void __iomem *base; 318c2ecf20Sopenharmony_ci u64 v; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci v = readq(base + FME_HDR_CAP); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%u\n", 388c2ecf20Sopenharmony_ci (unsigned int)FIELD_GET(FME_CAP_NUM_PORTS, v)); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(ports_num); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * Bitstream (static FPGA region) identifier number. It contains the 448c2ecf20Sopenharmony_ci * detailed version and other information of this static FPGA region. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_cistatic ssize_t bitstream_id_show(struct device *dev, 478c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci void __iomem *base; 508c2ecf20Sopenharmony_ci u64 v; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci v = readq(base + FME_HDR_BITSTREAM_ID); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "0x%llx\n", (unsigned long long)v); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(bitstream_id); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Bitstream (static FPGA region) meta data. It contains the synthesis 628c2ecf20Sopenharmony_ci * date, seed and other information of this static FPGA region. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cistatic ssize_t bitstream_metadata_show(struct device *dev, 658c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci void __iomem *base; 688c2ecf20Sopenharmony_ci u64 v; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci v = readq(base + FME_HDR_BITSTREAM_MD); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "0x%llx\n", (unsigned long long)v); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(bitstream_metadata); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic ssize_t cache_size_show(struct device *dev, 798c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci void __iomem *base; 828c2ecf20Sopenharmony_ci u64 v; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci v = readq(base + FME_HDR_CAP); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", 898c2ecf20Sopenharmony_ci (unsigned int)FIELD_GET(FME_CAP_CACHE_SIZE, v)); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(cache_size); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic ssize_t fabric_version_show(struct device *dev, 948c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci void __iomem *base; 978c2ecf20Sopenharmony_ci u64 v; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci v = readq(base + FME_HDR_CAP); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", 1048c2ecf20Sopenharmony_ci (unsigned int)FIELD_GET(FME_CAP_FABRIC_VERID, v)); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(fabric_version); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic ssize_t socket_id_show(struct device *dev, 1098c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci void __iomem *base; 1128c2ecf20Sopenharmony_ci u64 v; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci v = readq(base + FME_HDR_CAP); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", 1198c2ecf20Sopenharmony_ci (unsigned int)FIELD_GET(FME_CAP_SOCKET_ID, v)); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(socket_id); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic struct attribute *fme_hdr_attrs[] = { 1248c2ecf20Sopenharmony_ci &dev_attr_ports_num.attr, 1258c2ecf20Sopenharmony_ci &dev_attr_bitstream_id.attr, 1268c2ecf20Sopenharmony_ci &dev_attr_bitstream_metadata.attr, 1278c2ecf20Sopenharmony_ci &dev_attr_cache_size.attr, 1288c2ecf20Sopenharmony_ci &dev_attr_fabric_version.attr, 1298c2ecf20Sopenharmony_ci &dev_attr_socket_id.attr, 1308c2ecf20Sopenharmony_ci NULL, 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic const struct attribute_group fme_hdr_group = { 1348c2ecf20Sopenharmony_ci .attrs = fme_hdr_attrs, 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic long fme_hdr_ioctl_release_port(struct dfl_feature_platform_data *pdata, 1388c2ecf20Sopenharmony_ci unsigned long arg) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct dfl_fpga_cdev *cdev = pdata->dfl_cdev; 1418c2ecf20Sopenharmony_ci int port_id; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (get_user(port_id, (int __user *)arg)) 1448c2ecf20Sopenharmony_ci return -EFAULT; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return dfl_fpga_cdev_release_port(cdev, port_id); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic long fme_hdr_ioctl_assign_port(struct dfl_feature_platform_data *pdata, 1508c2ecf20Sopenharmony_ci unsigned long arg) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct dfl_fpga_cdev *cdev = pdata->dfl_cdev; 1538c2ecf20Sopenharmony_ci int port_id; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (get_user(port_id, (int __user *)arg)) 1568c2ecf20Sopenharmony_ci return -EFAULT; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return dfl_fpga_cdev_assign_port(cdev, port_id); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic long fme_hdr_ioctl(struct platform_device *pdev, 1628c2ecf20Sopenharmony_ci struct dfl_feature *feature, 1638c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci switch (cmd) { 1688c2ecf20Sopenharmony_ci case DFL_FPGA_FME_PORT_RELEASE: 1698c2ecf20Sopenharmony_ci return fme_hdr_ioctl_release_port(pdata, arg); 1708c2ecf20Sopenharmony_ci case DFL_FPGA_FME_PORT_ASSIGN: 1718c2ecf20Sopenharmony_ci return fme_hdr_ioctl_assign_port(pdata, arg); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return -ENODEV; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic const struct dfl_feature_id fme_hdr_id_table[] = { 1788c2ecf20Sopenharmony_ci {.id = FME_FEATURE_ID_HEADER,}, 1798c2ecf20Sopenharmony_ci {0,} 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic const struct dfl_feature_ops fme_hdr_ops = { 1838c2ecf20Sopenharmony_ci .ioctl = fme_hdr_ioctl, 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci#define FME_THERM_THRESHOLD 0x8 1878c2ecf20Sopenharmony_ci#define TEMP_THRESHOLD1 GENMASK_ULL(6, 0) 1888c2ecf20Sopenharmony_ci#define TEMP_THRESHOLD1_EN BIT_ULL(7) 1898c2ecf20Sopenharmony_ci#define TEMP_THRESHOLD2 GENMASK_ULL(14, 8) 1908c2ecf20Sopenharmony_ci#define TEMP_THRESHOLD2_EN BIT_ULL(15) 1918c2ecf20Sopenharmony_ci#define TRIP_THRESHOLD GENMASK_ULL(30, 24) 1928c2ecf20Sopenharmony_ci#define TEMP_THRESHOLD1_STATUS BIT_ULL(32) /* threshold1 reached */ 1938c2ecf20Sopenharmony_ci#define TEMP_THRESHOLD2_STATUS BIT_ULL(33) /* threshold2 reached */ 1948c2ecf20Sopenharmony_ci/* threshold1 policy: 0 - AP2 (90% throttle) / 1 - AP1 (50% throttle) */ 1958c2ecf20Sopenharmony_ci#define TEMP_THRESHOLD1_POLICY BIT_ULL(44) 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci#define FME_THERM_RDSENSOR_FMT1 0x10 1988c2ecf20Sopenharmony_ci#define FPGA_TEMPERATURE GENMASK_ULL(6, 0) 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci#define FME_THERM_CAP 0x20 2018c2ecf20Sopenharmony_ci#define THERM_NO_THROTTLE BIT_ULL(0) 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci#define MD_PRE_DEG 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic bool fme_thermal_throttle_support(void __iomem *base) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci u64 v = readq(base + FME_THERM_CAP); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return FIELD_GET(THERM_NO_THROTTLE, v) ? false : true; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic umode_t thermal_hwmon_attrs_visible(const void *drvdata, 2138c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 2148c2ecf20Sopenharmony_ci u32 attr, int channel) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci const struct dfl_feature *feature = drvdata; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* temperature is always supported, and check hardware cap for others */ 2198c2ecf20Sopenharmony_ci if (attr == hwmon_temp_input) 2208c2ecf20Sopenharmony_ci return 0444; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return fme_thermal_throttle_support(feature->ioaddr) ? 0444 : 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int thermal_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 2268c2ecf20Sopenharmony_ci u32 attr, int channel, long *val) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct dfl_feature *feature = dev_get_drvdata(dev); 2298c2ecf20Sopenharmony_ci u64 v; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci switch (attr) { 2328c2ecf20Sopenharmony_ci case hwmon_temp_input: 2338c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_THERM_RDSENSOR_FMT1); 2348c2ecf20Sopenharmony_ci *val = (long)(FIELD_GET(FPGA_TEMPERATURE, v) * 1000); 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci case hwmon_temp_max: 2378c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_THERM_THRESHOLD); 2388c2ecf20Sopenharmony_ci *val = (long)(FIELD_GET(TEMP_THRESHOLD1, v) * 1000); 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci case hwmon_temp_crit: 2418c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_THERM_THRESHOLD); 2428c2ecf20Sopenharmony_ci *val = (long)(FIELD_GET(TEMP_THRESHOLD2, v) * 1000); 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci case hwmon_temp_emergency: 2458c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_THERM_THRESHOLD); 2468c2ecf20Sopenharmony_ci *val = (long)(FIELD_GET(TRIP_THRESHOLD, v) * 1000); 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci case hwmon_temp_max_alarm: 2498c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_THERM_THRESHOLD); 2508c2ecf20Sopenharmony_ci *val = (long)FIELD_GET(TEMP_THRESHOLD1_STATUS, v); 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci case hwmon_temp_crit_alarm: 2538c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_THERM_THRESHOLD); 2548c2ecf20Sopenharmony_ci *val = (long)FIELD_GET(TEMP_THRESHOLD2_STATUS, v); 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci default: 2578c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic const struct hwmon_ops thermal_hwmon_ops = { 2648c2ecf20Sopenharmony_ci .is_visible = thermal_hwmon_attrs_visible, 2658c2ecf20Sopenharmony_ci .read = thermal_hwmon_read, 2668c2ecf20Sopenharmony_ci}; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *thermal_hwmon_info[] = { 2698c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_EMERGENCY | 2708c2ecf20Sopenharmony_ci HWMON_T_MAX | HWMON_T_MAX_ALARM | 2718c2ecf20Sopenharmony_ci HWMON_T_CRIT | HWMON_T_CRIT_ALARM), 2728c2ecf20Sopenharmony_ci NULL 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic const struct hwmon_chip_info thermal_hwmon_chip_info = { 2768c2ecf20Sopenharmony_ci .ops = &thermal_hwmon_ops, 2778c2ecf20Sopenharmony_ci .info = thermal_hwmon_info, 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic ssize_t temp1_max_policy_show(struct device *dev, 2818c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct dfl_feature *feature = dev_get_drvdata(dev); 2848c2ecf20Sopenharmony_ci u64 v; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_THERM_THRESHOLD); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", 2898c2ecf20Sopenharmony_ci (unsigned int)FIELD_GET(TEMP_THRESHOLD1_POLICY, v)); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(temp1_max_policy); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic struct attribute *thermal_extra_attrs[] = { 2958c2ecf20Sopenharmony_ci &dev_attr_temp1_max_policy.attr, 2968c2ecf20Sopenharmony_ci NULL, 2978c2ecf20Sopenharmony_ci}; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic umode_t thermal_extra_attrs_visible(struct kobject *kobj, 3008c2ecf20Sopenharmony_ci struct attribute *attr, int index) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 3038c2ecf20Sopenharmony_ci struct dfl_feature *feature = dev_get_drvdata(dev); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return fme_thermal_throttle_support(feature->ioaddr) ? attr->mode : 0; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic const struct attribute_group thermal_extra_group = { 3098c2ecf20Sopenharmony_ci .attrs = thermal_extra_attrs, 3108c2ecf20Sopenharmony_ci .is_visible = thermal_extra_attrs_visible, 3118c2ecf20Sopenharmony_ci}; 3128c2ecf20Sopenharmony_ci__ATTRIBUTE_GROUPS(thermal_extra); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int fme_thermal_mgmt_init(struct platform_device *pdev, 3158c2ecf20Sopenharmony_ci struct dfl_feature *feature) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct device *hwmon; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* 3208c2ecf20Sopenharmony_ci * create hwmon to allow userspace monitoring temperature and other 3218c2ecf20Sopenharmony_ci * threshold information. 3228c2ecf20Sopenharmony_ci * 3238c2ecf20Sopenharmony_ci * temp1_input -> FPGA device temperature 3248c2ecf20Sopenharmony_ci * temp1_max -> hardware threshold 1 -> 50% or 90% throttling 3258c2ecf20Sopenharmony_ci * temp1_crit -> hardware threshold 2 -> 100% throttling 3268c2ecf20Sopenharmony_ci * temp1_emergency -> hardware trip_threshold to shutdown FPGA 3278c2ecf20Sopenharmony_ci * temp1_max_alarm -> hardware threshold 1 alarm 3288c2ecf20Sopenharmony_ci * temp1_crit_alarm -> hardware threshold 2 alarm 3298c2ecf20Sopenharmony_ci * 3308c2ecf20Sopenharmony_ci * create device specific sysfs interfaces, e.g. read temp1_max_policy 3318c2ecf20Sopenharmony_ci * to understand the actual hardware throttling action (50% vs 90%). 3328c2ecf20Sopenharmony_ci * 3338c2ecf20Sopenharmony_ci * If hardware doesn't support automatic throttling per thresholds, 3348c2ecf20Sopenharmony_ci * then all above sysfs interfaces are not visible except temp1_input 3358c2ecf20Sopenharmony_ci * for temperature. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci hwmon = devm_hwmon_device_register_with_info(&pdev->dev, 3388c2ecf20Sopenharmony_ci "dfl_fme_thermal", feature, 3398c2ecf20Sopenharmony_ci &thermal_hwmon_chip_info, 3408c2ecf20Sopenharmony_ci thermal_extra_groups); 3418c2ecf20Sopenharmony_ci if (IS_ERR(hwmon)) { 3428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Fail to register thermal hwmon\n"); 3438c2ecf20Sopenharmony_ci return PTR_ERR(hwmon); 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic const struct dfl_feature_id fme_thermal_mgmt_id_table[] = { 3508c2ecf20Sopenharmony_ci {.id = FME_FEATURE_ID_THERMAL_MGMT,}, 3518c2ecf20Sopenharmony_ci {0,} 3528c2ecf20Sopenharmony_ci}; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic const struct dfl_feature_ops fme_thermal_mgmt_ops = { 3558c2ecf20Sopenharmony_ci .init = fme_thermal_mgmt_init, 3568c2ecf20Sopenharmony_ci}; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci#define FME_PWR_STATUS 0x8 3598c2ecf20Sopenharmony_ci#define FME_LATENCY_TOLERANCE BIT_ULL(18) 3608c2ecf20Sopenharmony_ci#define PWR_CONSUMED GENMASK_ULL(17, 0) 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci#define FME_PWR_THRESHOLD 0x10 3638c2ecf20Sopenharmony_ci#define PWR_THRESHOLD1 GENMASK_ULL(6, 0) /* in Watts */ 3648c2ecf20Sopenharmony_ci#define PWR_THRESHOLD2 GENMASK_ULL(14, 8) /* in Watts */ 3658c2ecf20Sopenharmony_ci#define PWR_THRESHOLD_MAX 0x7f /* in Watts */ 3668c2ecf20Sopenharmony_ci#define PWR_THRESHOLD1_STATUS BIT_ULL(16) 3678c2ecf20Sopenharmony_ci#define PWR_THRESHOLD2_STATUS BIT_ULL(17) 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci#define FME_PWR_XEON_LIMIT 0x18 3708c2ecf20Sopenharmony_ci#define XEON_PWR_LIMIT GENMASK_ULL(14, 0) /* in 0.1 Watts */ 3718c2ecf20Sopenharmony_ci#define XEON_PWR_EN BIT_ULL(15) 3728c2ecf20Sopenharmony_ci#define FME_PWR_FPGA_LIMIT 0x20 3738c2ecf20Sopenharmony_ci#define FPGA_PWR_LIMIT GENMASK_ULL(14, 0) /* in 0.1 Watts */ 3748c2ecf20Sopenharmony_ci#define FPGA_PWR_EN BIT_ULL(15) 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int power_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 3778c2ecf20Sopenharmony_ci u32 attr, int channel, long *val) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct dfl_feature *feature = dev_get_drvdata(dev); 3808c2ecf20Sopenharmony_ci u64 v; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci switch (attr) { 3838c2ecf20Sopenharmony_ci case hwmon_power_input: 3848c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_PWR_STATUS); 3858c2ecf20Sopenharmony_ci *val = (long)(FIELD_GET(PWR_CONSUMED, v) * 1000000); 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci case hwmon_power_max: 3888c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_PWR_THRESHOLD); 3898c2ecf20Sopenharmony_ci *val = (long)(FIELD_GET(PWR_THRESHOLD1, v) * 1000000); 3908c2ecf20Sopenharmony_ci break; 3918c2ecf20Sopenharmony_ci case hwmon_power_crit: 3928c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_PWR_THRESHOLD); 3938c2ecf20Sopenharmony_ci *val = (long)(FIELD_GET(PWR_THRESHOLD2, v) * 1000000); 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci case hwmon_power_max_alarm: 3968c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_PWR_THRESHOLD); 3978c2ecf20Sopenharmony_ci *val = (long)FIELD_GET(PWR_THRESHOLD1_STATUS, v); 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci case hwmon_power_crit_alarm: 4008c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_PWR_THRESHOLD); 4018c2ecf20Sopenharmony_ci *val = (long)FIELD_GET(PWR_THRESHOLD2_STATUS, v); 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci default: 4048c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int power_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 4118c2ecf20Sopenharmony_ci u32 attr, int channel, long val) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct dfl_feature_platform_data *pdata = dev_get_platdata(dev->parent); 4148c2ecf20Sopenharmony_ci struct dfl_feature *feature = dev_get_drvdata(dev); 4158c2ecf20Sopenharmony_ci int ret = 0; 4168c2ecf20Sopenharmony_ci u64 v; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci val = clamp_val(val / 1000000, 0, PWR_THRESHOLD_MAX); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci mutex_lock(&pdata->lock); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci switch (attr) { 4238c2ecf20Sopenharmony_ci case hwmon_power_max: 4248c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_PWR_THRESHOLD); 4258c2ecf20Sopenharmony_ci v &= ~PWR_THRESHOLD1; 4268c2ecf20Sopenharmony_ci v |= FIELD_PREP(PWR_THRESHOLD1, val); 4278c2ecf20Sopenharmony_ci writeq(v, feature->ioaddr + FME_PWR_THRESHOLD); 4288c2ecf20Sopenharmony_ci break; 4298c2ecf20Sopenharmony_ci case hwmon_power_crit: 4308c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_PWR_THRESHOLD); 4318c2ecf20Sopenharmony_ci v &= ~PWR_THRESHOLD2; 4328c2ecf20Sopenharmony_ci v |= FIELD_PREP(PWR_THRESHOLD2, val); 4338c2ecf20Sopenharmony_ci writeq(v, feature->ioaddr + FME_PWR_THRESHOLD); 4348c2ecf20Sopenharmony_ci break; 4358c2ecf20Sopenharmony_ci default: 4368c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci mutex_unlock(&pdata->lock); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return ret; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic umode_t power_hwmon_attrs_visible(const void *drvdata, 4468c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 4478c2ecf20Sopenharmony_ci u32 attr, int channel) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci switch (attr) { 4508c2ecf20Sopenharmony_ci case hwmon_power_input: 4518c2ecf20Sopenharmony_ci case hwmon_power_max_alarm: 4528c2ecf20Sopenharmony_ci case hwmon_power_crit_alarm: 4538c2ecf20Sopenharmony_ci return 0444; 4548c2ecf20Sopenharmony_ci case hwmon_power_max: 4558c2ecf20Sopenharmony_ci case hwmon_power_crit: 4568c2ecf20Sopenharmony_ci return 0644; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci return 0; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic const struct hwmon_ops power_hwmon_ops = { 4638c2ecf20Sopenharmony_ci .is_visible = power_hwmon_attrs_visible, 4648c2ecf20Sopenharmony_ci .read = power_hwmon_read, 4658c2ecf20Sopenharmony_ci .write = power_hwmon_write, 4668c2ecf20Sopenharmony_ci}; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *power_hwmon_info[] = { 4698c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | 4708c2ecf20Sopenharmony_ci HWMON_P_MAX | HWMON_P_MAX_ALARM | 4718c2ecf20Sopenharmony_ci HWMON_P_CRIT | HWMON_P_CRIT_ALARM), 4728c2ecf20Sopenharmony_ci NULL 4738c2ecf20Sopenharmony_ci}; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic const struct hwmon_chip_info power_hwmon_chip_info = { 4768c2ecf20Sopenharmony_ci .ops = &power_hwmon_ops, 4778c2ecf20Sopenharmony_ci .info = power_hwmon_info, 4788c2ecf20Sopenharmony_ci}; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic ssize_t power1_xeon_limit_show(struct device *dev, 4818c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct dfl_feature *feature = dev_get_drvdata(dev); 4848c2ecf20Sopenharmony_ci u16 xeon_limit = 0; 4858c2ecf20Sopenharmony_ci u64 v; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_PWR_XEON_LIMIT); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (FIELD_GET(XEON_PWR_EN, v)) 4908c2ecf20Sopenharmony_ci xeon_limit = FIELD_GET(XEON_PWR_LIMIT, v); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", xeon_limit * 100000); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic ssize_t power1_fpga_limit_show(struct device *dev, 4968c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct dfl_feature *feature = dev_get_drvdata(dev); 4998c2ecf20Sopenharmony_ci u16 fpga_limit = 0; 5008c2ecf20Sopenharmony_ci u64 v; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_PWR_FPGA_LIMIT); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (FIELD_GET(FPGA_PWR_EN, v)) 5058c2ecf20Sopenharmony_ci fpga_limit = FIELD_GET(FPGA_PWR_LIMIT, v); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", fpga_limit * 100000); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic ssize_t power1_ltr_show(struct device *dev, 5118c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct dfl_feature *feature = dev_get_drvdata(dev); 5148c2ecf20Sopenharmony_ci u64 v; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci v = readq(feature->ioaddr + FME_PWR_STATUS); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", 5198c2ecf20Sopenharmony_ci (unsigned int)FIELD_GET(FME_LATENCY_TOLERANCE, v)); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(power1_xeon_limit); 5238c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(power1_fpga_limit); 5248c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(power1_ltr); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic struct attribute *power_extra_attrs[] = { 5278c2ecf20Sopenharmony_ci &dev_attr_power1_xeon_limit.attr, 5288c2ecf20Sopenharmony_ci &dev_attr_power1_fpga_limit.attr, 5298c2ecf20Sopenharmony_ci &dev_attr_power1_ltr.attr, 5308c2ecf20Sopenharmony_ci NULL 5318c2ecf20Sopenharmony_ci}; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(power_extra); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int fme_power_mgmt_init(struct platform_device *pdev, 5368c2ecf20Sopenharmony_ci struct dfl_feature *feature) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct device *hwmon; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci hwmon = devm_hwmon_device_register_with_info(&pdev->dev, 5418c2ecf20Sopenharmony_ci "dfl_fme_power", feature, 5428c2ecf20Sopenharmony_ci &power_hwmon_chip_info, 5438c2ecf20Sopenharmony_ci power_extra_groups); 5448c2ecf20Sopenharmony_ci if (IS_ERR(hwmon)) { 5458c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Fail to register power hwmon\n"); 5468c2ecf20Sopenharmony_ci return PTR_ERR(hwmon); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic const struct dfl_feature_id fme_power_mgmt_id_table[] = { 5538c2ecf20Sopenharmony_ci {.id = FME_FEATURE_ID_POWER_MGMT,}, 5548c2ecf20Sopenharmony_ci {0,} 5558c2ecf20Sopenharmony_ci}; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic const struct dfl_feature_ops fme_power_mgmt_ops = { 5588c2ecf20Sopenharmony_ci .init = fme_power_mgmt_init, 5598c2ecf20Sopenharmony_ci}; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic struct dfl_feature_driver fme_feature_drvs[] = { 5628c2ecf20Sopenharmony_ci { 5638c2ecf20Sopenharmony_ci .id_table = fme_hdr_id_table, 5648c2ecf20Sopenharmony_ci .ops = &fme_hdr_ops, 5658c2ecf20Sopenharmony_ci }, 5668c2ecf20Sopenharmony_ci { 5678c2ecf20Sopenharmony_ci .id_table = fme_pr_mgmt_id_table, 5688c2ecf20Sopenharmony_ci .ops = &fme_pr_mgmt_ops, 5698c2ecf20Sopenharmony_ci }, 5708c2ecf20Sopenharmony_ci { 5718c2ecf20Sopenharmony_ci .id_table = fme_global_err_id_table, 5728c2ecf20Sopenharmony_ci .ops = &fme_global_err_ops, 5738c2ecf20Sopenharmony_ci }, 5748c2ecf20Sopenharmony_ci { 5758c2ecf20Sopenharmony_ci .id_table = fme_thermal_mgmt_id_table, 5768c2ecf20Sopenharmony_ci .ops = &fme_thermal_mgmt_ops, 5778c2ecf20Sopenharmony_ci }, 5788c2ecf20Sopenharmony_ci { 5798c2ecf20Sopenharmony_ci .id_table = fme_power_mgmt_id_table, 5808c2ecf20Sopenharmony_ci .ops = &fme_power_mgmt_ops, 5818c2ecf20Sopenharmony_ci }, 5828c2ecf20Sopenharmony_ci { 5838c2ecf20Sopenharmony_ci .id_table = fme_perf_id_table, 5848c2ecf20Sopenharmony_ci .ops = &fme_perf_ops, 5858c2ecf20Sopenharmony_ci }, 5868c2ecf20Sopenharmony_ci { 5878c2ecf20Sopenharmony_ci .ops = NULL, 5888c2ecf20Sopenharmony_ci }, 5898c2ecf20Sopenharmony_ci}; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic long fme_ioctl_check_extension(struct dfl_feature_platform_data *pdata, 5928c2ecf20Sopenharmony_ci unsigned long arg) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci /* No extension support for now */ 5958c2ecf20Sopenharmony_ci return 0; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int fme_open(struct inode *inode, struct file *filp) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode); 6018c2ecf20Sopenharmony_ci struct dfl_feature_platform_data *pdata = dev_get_platdata(&fdev->dev); 6028c2ecf20Sopenharmony_ci int ret; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (WARN_ON(!pdata)) 6058c2ecf20Sopenharmony_ci return -ENODEV; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci mutex_lock(&pdata->lock); 6088c2ecf20Sopenharmony_ci ret = dfl_feature_dev_use_begin(pdata, filp->f_flags & O_EXCL); 6098c2ecf20Sopenharmony_ci if (!ret) { 6108c2ecf20Sopenharmony_ci dev_dbg(&fdev->dev, "Device File Opened %d Times\n", 6118c2ecf20Sopenharmony_ci dfl_feature_dev_use_count(pdata)); 6128c2ecf20Sopenharmony_ci filp->private_data = pdata; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci mutex_unlock(&pdata->lock); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci return ret; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int fme_release(struct inode *inode, struct file *filp) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct dfl_feature_platform_data *pdata = filp->private_data; 6228c2ecf20Sopenharmony_ci struct platform_device *pdev = pdata->dev; 6238c2ecf20Sopenharmony_ci struct dfl_feature *feature; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Device File Release\n"); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci mutex_lock(&pdata->lock); 6288c2ecf20Sopenharmony_ci dfl_feature_dev_use_end(pdata); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (!dfl_feature_dev_use_count(pdata)) 6318c2ecf20Sopenharmony_ci dfl_fpga_dev_for_each_feature(pdata, feature) 6328c2ecf20Sopenharmony_ci dfl_fpga_set_irq_triggers(feature, 0, 6338c2ecf20Sopenharmony_ci feature->nr_irqs, NULL); 6348c2ecf20Sopenharmony_ci mutex_unlock(&pdata->lock); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci return 0; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct dfl_feature_platform_data *pdata = filp->private_data; 6428c2ecf20Sopenharmony_ci struct platform_device *pdev = pdata->dev; 6438c2ecf20Sopenharmony_ci struct dfl_feature *f; 6448c2ecf20Sopenharmony_ci long ret; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci switch (cmd) { 6498c2ecf20Sopenharmony_ci case DFL_FPGA_GET_API_VERSION: 6508c2ecf20Sopenharmony_ci return DFL_FPGA_API_VERSION; 6518c2ecf20Sopenharmony_ci case DFL_FPGA_CHECK_EXTENSION: 6528c2ecf20Sopenharmony_ci return fme_ioctl_check_extension(pdata, arg); 6538c2ecf20Sopenharmony_ci default: 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * Let sub-feature's ioctl function to handle the cmd. 6568c2ecf20Sopenharmony_ci * Sub-feature's ioctl returns -ENODEV when cmd is not 6578c2ecf20Sopenharmony_ci * handled in this sub feature, and returns 0 or other 6588c2ecf20Sopenharmony_ci * error code if cmd is handled. 6598c2ecf20Sopenharmony_ci */ 6608c2ecf20Sopenharmony_ci dfl_fpga_dev_for_each_feature(pdata, f) { 6618c2ecf20Sopenharmony_ci if (f->ops && f->ops->ioctl) { 6628c2ecf20Sopenharmony_ci ret = f->ops->ioctl(pdev, f, cmd, arg); 6638c2ecf20Sopenharmony_ci if (ret != -ENODEV) 6648c2ecf20Sopenharmony_ci return ret; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return -EINVAL; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic int fme_dev_init(struct platform_device *pdev) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); 6758c2ecf20Sopenharmony_ci struct dfl_fme *fme; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci fme = devm_kzalloc(&pdev->dev, sizeof(*fme), GFP_KERNEL); 6788c2ecf20Sopenharmony_ci if (!fme) 6798c2ecf20Sopenharmony_ci return -ENOMEM; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci fme->pdata = pdata; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci mutex_lock(&pdata->lock); 6848c2ecf20Sopenharmony_ci dfl_fpga_pdata_set_private(pdata, fme); 6858c2ecf20Sopenharmony_ci mutex_unlock(&pdata->lock); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci return 0; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic void fme_dev_destroy(struct platform_device *pdev) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci mutex_lock(&pdata->lock); 6958c2ecf20Sopenharmony_ci dfl_fpga_pdata_set_private(pdata, NULL); 6968c2ecf20Sopenharmony_ci mutex_unlock(&pdata->lock); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic const struct file_operations fme_fops = { 7008c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7018c2ecf20Sopenharmony_ci .open = fme_open, 7028c2ecf20Sopenharmony_ci .release = fme_release, 7038c2ecf20Sopenharmony_ci .unlocked_ioctl = fme_ioctl, 7048c2ecf20Sopenharmony_ci}; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic int fme_probe(struct platform_device *pdev) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci int ret; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci ret = fme_dev_init(pdev); 7118c2ecf20Sopenharmony_ci if (ret) 7128c2ecf20Sopenharmony_ci goto exit; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs); 7158c2ecf20Sopenharmony_ci if (ret) 7168c2ecf20Sopenharmony_ci goto dev_destroy; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci ret = dfl_fpga_dev_ops_register(pdev, &fme_fops, THIS_MODULE); 7198c2ecf20Sopenharmony_ci if (ret) 7208c2ecf20Sopenharmony_ci goto feature_uinit; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci return 0; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cifeature_uinit: 7258c2ecf20Sopenharmony_ci dfl_fpga_dev_feature_uinit(pdev); 7268c2ecf20Sopenharmony_cidev_destroy: 7278c2ecf20Sopenharmony_ci fme_dev_destroy(pdev); 7288c2ecf20Sopenharmony_ciexit: 7298c2ecf20Sopenharmony_ci return ret; 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistatic int fme_remove(struct platform_device *pdev) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci dfl_fpga_dev_ops_unregister(pdev); 7358c2ecf20Sopenharmony_ci dfl_fpga_dev_feature_uinit(pdev); 7368c2ecf20Sopenharmony_ci fme_dev_destroy(pdev); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci return 0; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic const struct attribute_group *fme_dev_groups[] = { 7428c2ecf20Sopenharmony_ci &fme_hdr_group, 7438c2ecf20Sopenharmony_ci &fme_global_err_group, 7448c2ecf20Sopenharmony_ci NULL 7458c2ecf20Sopenharmony_ci}; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic struct platform_driver fme_driver = { 7488c2ecf20Sopenharmony_ci .driver = { 7498c2ecf20Sopenharmony_ci .name = DFL_FPGA_FEATURE_DEV_FME, 7508c2ecf20Sopenharmony_ci .dev_groups = fme_dev_groups, 7518c2ecf20Sopenharmony_ci }, 7528c2ecf20Sopenharmony_ci .probe = fme_probe, 7538c2ecf20Sopenharmony_ci .remove = fme_remove, 7548c2ecf20Sopenharmony_ci}; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cimodule_platform_driver(fme_driver); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FPGA Management Engine driver"); 7598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 7608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7618c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:dfl-fme"); 762