fpga: dfl: fme: add header sub feature support
[sfrench/cifs-2.6.git] / drivers / fpga / dfl-fme-main.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Driver for FPGA Management Engine (FME)
4  *
5  * Copyright (C) 2017-2018 Intel Corporation, Inc.
6  *
7  * Authors:
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>
15  */
16
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19
20 #include "dfl.h"
21
22 static ssize_t ports_num_show(struct device *dev,
23                               struct device_attribute *attr, char *buf)
24 {
25         void __iomem *base;
26         u64 v;
27
28         base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
29
30         v = readq(base + FME_HDR_CAP);
31
32         return scnprintf(buf, PAGE_SIZE, "%u\n",
33                          (unsigned int)FIELD_GET(FME_CAP_NUM_PORTS, v));
34 }
35 static DEVICE_ATTR_RO(ports_num);
36
37 /*
38  * Bitstream (static FPGA region) identifier number. It contains the
39  * detailed version and other information of this static FPGA region.
40  */
41 static ssize_t bitstream_id_show(struct device *dev,
42                                  struct device_attribute *attr, char *buf)
43 {
44         void __iomem *base;
45         u64 v;
46
47         base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
48
49         v = readq(base + FME_HDR_BITSTREAM_ID);
50
51         return scnprintf(buf, PAGE_SIZE, "0x%llx\n", (unsigned long long)v);
52 }
53 static DEVICE_ATTR_RO(bitstream_id);
54
55 /*
56  * Bitstream (static FPGA region) meta data. It contains the synthesis
57  * date, seed and other information of this static FPGA region.
58  */
59 static ssize_t bitstream_metadata_show(struct device *dev,
60                                        struct device_attribute *attr, char *buf)
61 {
62         void __iomem *base;
63         u64 v;
64
65         base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
66
67         v = readq(base + FME_HDR_BITSTREAM_MD);
68
69         return scnprintf(buf, PAGE_SIZE, "0x%llx\n", (unsigned long long)v);
70 }
71 static DEVICE_ATTR_RO(bitstream_metadata);
72
73 static const struct attribute *fme_hdr_attrs[] = {
74         &dev_attr_ports_num.attr,
75         &dev_attr_bitstream_id.attr,
76         &dev_attr_bitstream_metadata.attr,
77         NULL,
78 };
79
80 static int fme_hdr_init(struct platform_device *pdev,
81                         struct dfl_feature *feature)
82 {
83         void __iomem *base = feature->ioaddr;
84         int ret;
85
86         dev_dbg(&pdev->dev, "FME HDR Init.\n");
87         dev_dbg(&pdev->dev, "FME cap %llx.\n",
88                 (unsigned long long)readq(base + FME_HDR_CAP));
89
90         ret = sysfs_create_files(&pdev->dev.kobj, fme_hdr_attrs);
91         if (ret)
92                 return ret;
93
94         return 0;
95 }
96
97 static void fme_hdr_uinit(struct platform_device *pdev,
98                           struct dfl_feature *feature)
99 {
100         dev_dbg(&pdev->dev, "FME HDR UInit.\n");
101         sysfs_remove_files(&pdev->dev.kobj, fme_hdr_attrs);
102 }
103
104 static const struct dfl_feature_ops fme_hdr_ops = {
105         .init = fme_hdr_init,
106         .uinit = fme_hdr_uinit,
107 };
108
109 static struct dfl_feature_driver fme_feature_drvs[] = {
110         {
111                 .id = FME_FEATURE_ID_HEADER,
112                 .ops = &fme_hdr_ops,
113         },
114         {
115                 .ops = NULL,
116         },
117 };
118
119 static int fme_open(struct inode *inode, struct file *filp)
120 {
121         struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
122         struct dfl_feature_platform_data *pdata = dev_get_platdata(&fdev->dev);
123         int ret;
124
125         if (WARN_ON(!pdata))
126                 return -ENODEV;
127
128         ret = dfl_feature_dev_use_begin(pdata);
129         if (ret)
130                 return ret;
131
132         dev_dbg(&fdev->dev, "Device File Open\n");
133         filp->private_data = pdata;
134
135         return 0;
136 }
137
138 static int fme_release(struct inode *inode, struct file *filp)
139 {
140         struct dfl_feature_platform_data *pdata = filp->private_data;
141         struct platform_device *pdev = pdata->dev;
142
143         dev_dbg(&pdev->dev, "Device File Release\n");
144         dfl_feature_dev_use_end(pdata);
145
146         return 0;
147 }
148
149 static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
150 {
151         struct dfl_feature_platform_data *pdata = filp->private_data;
152         struct platform_device *pdev = pdata->dev;
153         struct dfl_feature *f;
154         long ret;
155
156         dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd);
157
158         switch (cmd) {
159         default:
160                 /*
161                  * Let sub-feature's ioctl function to handle the cmd.
162                  * Sub-feature's ioctl returns -ENODEV when cmd is not
163                  * handled in this sub feature, and returns 0 or other
164                  * error code if cmd is handled.
165                  */
166                 dfl_fpga_dev_for_each_feature(pdata, f) {
167                         if (f->ops && f->ops->ioctl) {
168                                 ret = f->ops->ioctl(pdev, f, cmd, arg);
169                                 if (ret != -ENODEV)
170                                         return ret;
171                         }
172                 }
173         }
174
175         return -EINVAL;
176 }
177
178 static const struct file_operations fme_fops = {
179         .owner          = THIS_MODULE,
180         .open           = fme_open,
181         .release        = fme_release,
182         .unlocked_ioctl = fme_ioctl,
183 };
184
185 static int fme_probe(struct platform_device *pdev)
186 {
187         int ret;
188
189         ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs);
190         if (ret)
191                 goto exit;
192
193         ret = dfl_fpga_dev_ops_register(pdev, &fme_fops, THIS_MODULE);
194         if (ret)
195                 goto feature_uinit;
196
197         return 0;
198
199 feature_uinit:
200         dfl_fpga_dev_feature_uinit(pdev);
201 exit:
202         return ret;
203 }
204
205 static int fme_remove(struct platform_device *pdev)
206 {
207         dfl_fpga_dev_ops_unregister(pdev);
208         dfl_fpga_dev_feature_uinit(pdev);
209
210         return 0;
211 }
212
213 static struct platform_driver fme_driver = {
214         .driver = {
215                 .name    = DFL_FPGA_FEATURE_DEV_FME,
216         },
217         .probe   = fme_probe,
218         .remove  = fme_remove,
219 };
220
221 module_platform_driver(fme_driver);
222
223 MODULE_DESCRIPTION("FPGA Management Engine driver");
224 MODULE_AUTHOR("Intel Corporation");
225 MODULE_LICENSE("GPL v2");
226 MODULE_ALIAS("platform:dfl-fme");