lkdtm: Add Control Flow Integrity test
[linux/fpc-iii.git] / drivers / misc / xilinx_sdfec.c
blobf257d3812110f613d6b88824440813336bc23f97
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Xilinx SDFEC
5 * Copyright (C) 2019 Xilinx, Inc.
7 * Description:
8 * This driver is developed for SDFEC16 (Soft Decision FEC 16nm)
9 * IP. It exposes a char device which supports file operations
10 * like open(), close() and ioctl().
13 #include <linux/miscdevice.h>
14 #include <linux/io.h>
15 #include <linux/interrupt.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/of_platform.h>
19 #include <linux/poll.h>
20 #include <linux/slab.h>
21 #include <linux/clk.h>
23 #define DEV_NAME_LEN 12
25 static struct idr dev_idr;
26 static struct mutex dev_idr_lock;
28 /**
29 * struct xsdfec_clks - For managing SD-FEC clocks
30 * @core_clk: Main processing clock for core
31 * @axi_clk: AXI4-Lite memory-mapped clock
32 * @din_words_clk: DIN Words AXI4-Stream Slave clock
33 * @din_clk: DIN AXI4-Stream Slave clock
34 * @dout_clk: DOUT Words AXI4-Stream Slave clock
35 * @dout_words_clk: DOUT AXI4-Stream Slave clock
36 * @ctrl_clk: Control AXI4-Stream Slave clock
37 * @status_clk: Status AXI4-Stream Slave clock
39 struct xsdfec_clks {
40 struct clk *core_clk;
41 struct clk *axi_clk;
42 struct clk *din_words_clk;
43 struct clk *din_clk;
44 struct clk *dout_clk;
45 struct clk *dout_words_clk;
46 struct clk *ctrl_clk;
47 struct clk *status_clk;
50 /**
51 * struct xsdfec_dev - Driver data for SDFEC
52 * @regs: device physical base address
53 * @dev: pointer to device struct
54 * @miscdev: Misc device handle
55 * @error_data_lock: Error counter and states spinlock
56 * @clks: Clocks managed by the SDFEC driver
57 * @dev_name: Device name
58 * @dev_id: Device ID
60 * This structure contains necessary state for SDFEC driver to operate
62 struct xsdfec_dev {
63 void __iomem *regs;
64 struct device *dev;
65 struct miscdevice miscdev;
66 /* Spinlock to protect state_updated and stats_updated */
67 spinlock_t error_data_lock;
68 struct xsdfec_clks clks;
69 char dev_name[DEV_NAME_LEN];
70 int dev_id;
73 static const struct file_operations xsdfec_fops = {
74 .owner = THIS_MODULE,
77 static int xsdfec_clk_init(struct platform_device *pdev,
78 struct xsdfec_clks *clks)
80 int err;
82 clks->core_clk = devm_clk_get(&pdev->dev, "core_clk");
83 if (IS_ERR(clks->core_clk)) {
84 dev_err(&pdev->dev, "failed to get core_clk");
85 return PTR_ERR(clks->core_clk);
88 clks->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
89 if (IS_ERR(clks->axi_clk)) {
90 dev_err(&pdev->dev, "failed to get axi_clk");
91 return PTR_ERR(clks->axi_clk);
94 clks->din_words_clk = devm_clk_get(&pdev->dev, "s_axis_din_words_aclk");
95 if (IS_ERR(clks->din_words_clk)) {
96 if (PTR_ERR(clks->din_words_clk) != -ENOENT) {
97 err = PTR_ERR(clks->din_words_clk);
98 return err;
100 clks->din_words_clk = NULL;
103 clks->din_clk = devm_clk_get(&pdev->dev, "s_axis_din_aclk");
104 if (IS_ERR(clks->din_clk)) {
105 if (PTR_ERR(clks->din_clk) != -ENOENT) {
106 err = PTR_ERR(clks->din_clk);
107 return err;
109 clks->din_clk = NULL;
112 clks->dout_clk = devm_clk_get(&pdev->dev, "m_axis_dout_aclk");
113 if (IS_ERR(clks->dout_clk)) {
114 if (PTR_ERR(clks->dout_clk) != -ENOENT) {
115 err = PTR_ERR(clks->dout_clk);
116 return err;
118 clks->dout_clk = NULL;
121 clks->dout_words_clk =
122 devm_clk_get(&pdev->dev, "s_axis_dout_words_aclk");
123 if (IS_ERR(clks->dout_words_clk)) {
124 if (PTR_ERR(clks->dout_words_clk) != -ENOENT) {
125 err = PTR_ERR(clks->dout_words_clk);
126 return err;
128 clks->dout_words_clk = NULL;
131 clks->ctrl_clk = devm_clk_get(&pdev->dev, "s_axis_ctrl_aclk");
132 if (IS_ERR(clks->ctrl_clk)) {
133 if (PTR_ERR(clks->ctrl_clk) != -ENOENT) {
134 err = PTR_ERR(clks->ctrl_clk);
135 return err;
137 clks->ctrl_clk = NULL;
140 clks->status_clk = devm_clk_get(&pdev->dev, "m_axis_status_aclk");
141 if (IS_ERR(clks->status_clk)) {
142 if (PTR_ERR(clks->status_clk) != -ENOENT) {
143 err = PTR_ERR(clks->status_clk);
144 return err;
146 clks->status_clk = NULL;
149 err = clk_prepare_enable(clks->core_clk);
150 if (err) {
151 dev_err(&pdev->dev, "failed to enable core_clk (%d)", err);
152 return err;
155 err = clk_prepare_enable(clks->axi_clk);
156 if (err) {
157 dev_err(&pdev->dev, "failed to enable axi_clk (%d)", err);
158 goto err_disable_core_clk;
161 err = clk_prepare_enable(clks->din_clk);
162 if (err) {
163 dev_err(&pdev->dev, "failed to enable din_clk (%d)", err);
164 goto err_disable_axi_clk;
167 err = clk_prepare_enable(clks->din_words_clk);
168 if (err) {
169 dev_err(&pdev->dev, "failed to enable din_words_clk (%d)", err);
170 goto err_disable_din_clk;
173 err = clk_prepare_enable(clks->dout_clk);
174 if (err) {
175 dev_err(&pdev->dev, "failed to enable dout_clk (%d)", err);
176 goto err_disable_din_words_clk;
179 err = clk_prepare_enable(clks->dout_words_clk);
180 if (err) {
181 dev_err(&pdev->dev, "failed to enable dout_words_clk (%d)",
182 err);
183 goto err_disable_dout_clk;
186 err = clk_prepare_enable(clks->ctrl_clk);
187 if (err) {
188 dev_err(&pdev->dev, "failed to enable ctrl_clk (%d)", err);
189 goto err_disable_dout_words_clk;
192 err = clk_prepare_enable(clks->status_clk);
193 if (err) {
194 dev_err(&pdev->dev, "failed to enable status_clk (%d)\n", err);
195 goto err_disable_ctrl_clk;
198 return err;
200 err_disable_ctrl_clk:
201 clk_disable_unprepare(clks->ctrl_clk);
202 err_disable_dout_words_clk:
203 clk_disable_unprepare(clks->dout_words_clk);
204 err_disable_dout_clk:
205 clk_disable_unprepare(clks->dout_clk);
206 err_disable_din_words_clk:
207 clk_disable_unprepare(clks->din_words_clk);
208 err_disable_din_clk:
209 clk_disable_unprepare(clks->din_clk);
210 err_disable_axi_clk:
211 clk_disable_unprepare(clks->axi_clk);
212 err_disable_core_clk:
213 clk_disable_unprepare(clks->core_clk);
215 return err;
218 static void xsdfec_disable_all_clks(struct xsdfec_clks *clks)
220 clk_disable_unprepare(clks->status_clk);
221 clk_disable_unprepare(clks->ctrl_clk);
222 clk_disable_unprepare(clks->dout_words_clk);
223 clk_disable_unprepare(clks->dout_clk);
224 clk_disable_unprepare(clks->din_words_clk);
225 clk_disable_unprepare(clks->din_clk);
226 clk_disable_unprepare(clks->core_clk);
227 clk_disable_unprepare(clks->axi_clk);
230 static void xsdfec_idr_remove(struct xsdfec_dev *xsdfec)
232 mutex_lock(&dev_idr_lock);
233 idr_remove(&dev_idr, xsdfec->dev_id);
234 mutex_unlock(&dev_idr_lock);
237 static int xsdfec_probe(struct platform_device *pdev)
239 struct xsdfec_dev *xsdfec;
240 struct device *dev;
241 struct resource *res;
242 int err;
244 xsdfec = devm_kzalloc(&pdev->dev, sizeof(*xsdfec), GFP_KERNEL);
245 if (!xsdfec)
246 return -ENOMEM;
248 xsdfec->dev = &pdev->dev;
249 spin_lock_init(&xsdfec->error_data_lock);
251 err = xsdfec_clk_init(pdev, &xsdfec->clks);
252 if (err)
253 return err;
255 dev = xsdfec->dev;
256 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
257 xsdfec->regs = devm_ioremap_resource(dev, res);
258 if (IS_ERR(xsdfec->regs)) {
259 err = PTR_ERR(xsdfec->regs);
260 goto err_xsdfec_dev;
263 /* Save driver private data */
264 platform_set_drvdata(pdev, xsdfec);
266 mutex_lock(&dev_idr_lock);
267 err = idr_alloc(&dev_idr, xsdfec->dev_name, 0, 0, GFP_KERNEL);
268 mutex_unlock(&dev_idr_lock);
269 if (err < 0)
270 goto err_xsdfec_dev;
271 xsdfec->dev_id = err;
273 snprintf(xsdfec->dev_name, DEV_NAME_LEN, "xsdfec%d", xsdfec->dev_id);
274 xsdfec->miscdev.minor = MISC_DYNAMIC_MINOR;
275 xsdfec->miscdev.name = xsdfec->dev_name;
276 xsdfec->miscdev.fops = &xsdfec_fops;
277 xsdfec->miscdev.parent = dev;
278 err = misc_register(&xsdfec->miscdev);
279 if (err) {
280 dev_err(dev, "error:%d. Unable to register device", err);
281 goto err_xsdfec_idr;
283 return 0;
285 err_xsdfec_idr:
286 xsdfec_idr_remove(xsdfec);
287 err_xsdfec_dev:
288 xsdfec_disable_all_clks(&xsdfec->clks);
289 return err;
292 static int xsdfec_remove(struct platform_device *pdev)
294 struct xsdfec_dev *xsdfec;
296 xsdfec = platform_get_drvdata(pdev);
297 misc_deregister(&xsdfec->miscdev);
298 xsdfec_idr_remove(xsdfec);
299 xsdfec_disable_all_clks(&xsdfec->clks);
300 return 0;
303 static const struct of_device_id xsdfec_of_match[] = {
305 .compatible = "xlnx,sd-fec-1.1",
307 { /* end of table */ }
309 MODULE_DEVICE_TABLE(of, xsdfec_of_match);
311 static struct platform_driver xsdfec_driver = {
312 .driver = {
313 .name = "xilinx-sdfec",
314 .of_match_table = xsdfec_of_match,
316 .probe = xsdfec_probe,
317 .remove = xsdfec_remove,
320 static int __init xsdfec_init(void)
322 int err;
324 mutex_init(&dev_idr_lock);
325 idr_init(&dev_idr);
326 err = platform_driver_register(&xsdfec_driver);
327 if (err < 0) {
328 pr_err("%s Unabled to register SDFEC driver", __func__);
329 return err;
331 return 0;
334 static void __exit xsdfec_exit(void)
336 platform_driver_unregister(&xsdfec_driver);
337 idr_destroy(&dev_idr);
340 module_init(xsdfec_init);
341 module_exit(xsdfec_exit);
343 MODULE_AUTHOR("Xilinx, Inc");
344 MODULE_DESCRIPTION("Xilinx SD-FEC16 Driver");
345 MODULE_LICENSE("GPL");