// SPDX-License-Identifier: GPL-2.0 /* * Driver for FPGA Management Engine (FME) * * Copyright (C) 2017-2018 Intel Corporation, Inc. * * Authors: * Kang Luwei * Xiao Guangrong * Joseph Grecco * Enno Luebbers * Tim Whisonant * Ananda Ravuri * Henry Mitchel */ #include #include #include #include "dfl.h" #include "dfl-fme.h" static ssize_t ports_num_show(struct device *dev, struct device_attribute *attr, char *buf) { void __iomem *base; u64 v; base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); v = readq(base + FME_HDR_CAP); return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned int)FIELD_GET(FME_CAP_NUM_PORTS, v)); } static DEVICE_ATTR_RO(ports_num); /* * Bitstream (static FPGA region) identifier number. It contains the * detailed version and other information of this static FPGA region. */ static ssize_t bitstream_id_show(struct device *dev, struct device_attribute *attr, char *buf) { void __iomem *base; u64 v; base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); v = readq(base + FME_HDR_BITSTREAM_ID); return scnprintf(buf, PAGE_SIZE, "0x%llx\n", (unsigned long long)v); } static DEVICE_ATTR_RO(bitstream_id); /* * Bitstream (static FPGA region) meta data. It contains the synthesis * date, seed and other information of this static FPGA region. */ static ssize_t bitstream_metadata_show(struct device *dev, struct device_attribute *attr, char *buf) { void __iomem *base; u64 v; base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); v = readq(base + FME_HDR_BITSTREAM_MD); return scnprintf(buf, PAGE_SIZE, "0x%llx\n", (unsigned long long)v); } static DEVICE_ATTR_RO(bitstream_metadata); static const struct attribute *fme_hdr_attrs[] = { &dev_attr_ports_num.attr, &dev_attr_bitstream_id.attr, &dev_attr_bitstream_metadata.attr, NULL, }; static int fme_hdr_init(struct platform_device *pdev, struct dfl_feature *feature) { void __iomem *base = feature->ioaddr; int ret; dev_dbg(&pdev->dev, "FME HDR Init.\n"); dev_dbg(&pdev->dev, "FME cap %llx.\n", (unsigned long long)readq(base + FME_HDR_CAP)); ret = sysfs_create_files(&pdev->dev.kobj, fme_hdr_attrs); if (ret) return ret; return 0; } static void fme_hdr_uinit(struct platform_device *pdev, struct dfl_feature *feature) { dev_dbg(&pdev->dev, "FME HDR UInit.\n"); sysfs_remove_files(&pdev->dev.kobj, fme_hdr_attrs); } static const struct dfl_feature_ops fme_hdr_ops = { .init = fme_hdr_init, .uinit = fme_hdr_uinit, }; static struct dfl_feature_driver fme_feature_drvs[] = { { .id = FME_FEATURE_ID_HEADER, .ops = &fme_hdr_ops, }, { .id = FME_FEATURE_ID_PR_MGMT, .ops = &pr_mgmt_ops, }, { .ops = NULL, }, }; static long fme_ioctl_check_extension(struct dfl_feature_platform_data *pdata, unsigned long arg) { /* No extension support for now */ return 0; } static int fme_open(struct inode *inode, struct file *filp) { struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode); struct dfl_feature_platform_data *pdata = dev_get_platdata(&fdev->dev); int ret; if (WARN_ON(!pdata)) return -ENODEV; ret = dfl_feature_dev_use_begin(pdata); if (ret) return ret; dev_dbg(&fdev->dev, "Device File Open\n"); filp->private_data = pdata; return 0; } static int fme_release(struct inode *inode, struct file *filp) { struct dfl_feature_platform_data *pdata = filp->private_data; struct platform_device *pdev = pdata->dev; dev_dbg(&pdev->dev, "Device File Release\n"); dfl_feature_dev_use_end(pdata); return 0; } static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct dfl_feature_platform_data *pdata = filp->private_data; struct platform_device *pdev = pdata->dev; struct dfl_feature *f; long ret; dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd); switch (cmd) { case DFL_FPGA_GET_API_VERSION: return DFL_FPGA_API_VERSION; case DFL_FPGA_CHECK_EXTENSION: return fme_ioctl_check_extension(pdata, arg); default: /* * Let sub-feature's ioctl function to handle the cmd. * Sub-feature's ioctl returns -ENODEV when cmd is not * handled in this sub feature, and returns 0 or other * error code if cmd is handled. */ dfl_fpga_dev_for_each_feature(pdata, f) { if (f->ops && f->ops->ioctl) { ret = f->ops->ioctl(pdev, f, cmd, arg); if (ret != -ENODEV) return ret; } } } return -EINVAL; } static int fme_dev_init(struct platform_device *pdev) { struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); struct dfl_fme *fme; fme = devm_kzalloc(&pdev->dev, sizeof(*fme), GFP_KERNEL); if (!fme) return -ENOMEM; fme->pdata = pdata; mutex_lock(&pdata->lock); dfl_fpga_pdata_set_private(pdata, fme); mutex_unlock(&pdata->lock); return 0; } static void fme_dev_destroy(struct platform_device *pdev) { struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); struct dfl_fme *fme; mutex_lock(&pdata->lock); fme = dfl_fpga_pdata_get_private(pdata); dfl_fpga_pdata_set_private(pdata, NULL); mutex_unlock(&pdata->lock); } static const struct file_operations fme_fops = { .owner = THIS_MODULE, .open = fme_open, .release = fme_release, .unlocked_ioctl = fme_ioctl, }; static int fme_probe(struct platform_device *pdev) { int ret; ret = fme_dev_init(pdev); if (ret) goto exit; ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs); if (ret) goto dev_destroy; ret = dfl_fpga_dev_ops_register(pdev, &fme_fops, THIS_MODULE); if (ret) goto feature_uinit; return 0; feature_uinit: dfl_fpga_dev_feature_uinit(pdev); dev_destroy: fme_dev_destroy(pdev); exit: return ret; } static int fme_remove(struct platform_device *pdev) { dfl_fpga_dev_ops_unregister(pdev); dfl_fpga_dev_feature_uinit(pdev); fme_dev_destroy(pdev); return 0; } static struct platform_driver fme_driver = { .driver = { .name = DFL_FPGA_FEATURE_DEV_FME, }, .probe = fme_probe, .remove = fme_remove, }; module_platform_driver(fme_driver); MODULE_DESCRIPTION("FPGA Management Engine driver"); MODULE_AUTHOR("Intel Corporation"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:dfl-fme");