]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/misc/xilinx_sdfec.c
Merge branch 'for-5.4/hidraw-hiddev-epoll' into for-linus
[linux.git] / drivers / misc / xilinx_sdfec.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Xilinx SDFEC
4  *
5  * Copyright (C) 2019 Xilinx, Inc.
6  *
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().
11  */
12
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>
22
23 #define DEV_NAME_LEN 12
24
25 static struct idr dev_idr;
26 static struct mutex dev_idr_lock;
27
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
38  */
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;
48 };
49
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
59  *
60  * This structure contains necessary state for SDFEC driver to operate
61  */
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;
71 };
72
73 static const struct file_operations xsdfec_fops = {
74         .owner = THIS_MODULE,
75 };
76
77 static int xsdfec_clk_init(struct platform_device *pdev,
78                            struct xsdfec_clks *clks)
79 {
80         int err;
81
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);
86         }
87
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);
92         }
93
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;
99                 }
100                 clks->din_words_clk = NULL;
101         }
102
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;
108                 }
109                 clks->din_clk = NULL;
110         }
111
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;
117                 }
118                 clks->dout_clk = NULL;
119         }
120
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;
127                 }
128                 clks->dout_words_clk = NULL;
129         }
130
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;
136                 }
137                 clks->ctrl_clk = NULL;
138         }
139
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;
145                 }
146                 clks->status_clk = NULL;
147         }
148
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;
153         }
154
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;
159         }
160
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;
165         }
166
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;
171         }
172
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;
177         }
178
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;
184         }
185
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;
190         }
191
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;
196         }
197
198         return err;
199
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);
214
215         return err;
216 }
217
218 static void xsdfec_disable_all_clks(struct xsdfec_clks *clks)
219 {
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);
228 }
229
230 static void xsdfec_idr_remove(struct xsdfec_dev *xsdfec)
231 {
232         mutex_lock(&dev_idr_lock);
233         idr_remove(&dev_idr, xsdfec->dev_id);
234         mutex_unlock(&dev_idr_lock);
235 }
236
237 static int xsdfec_probe(struct platform_device *pdev)
238 {
239         struct xsdfec_dev *xsdfec;
240         struct device *dev;
241         struct resource *res;
242         int err;
243
244         xsdfec = devm_kzalloc(&pdev->dev, sizeof(*xsdfec), GFP_KERNEL);
245         if (!xsdfec)
246                 return -ENOMEM;
247
248         xsdfec->dev = &pdev->dev;
249         spin_lock_init(&xsdfec->error_data_lock);
250
251         err = xsdfec_clk_init(pdev, &xsdfec->clks);
252         if (err)
253                 return err;
254
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;
261         }
262
263         /* Save driver private data */
264         platform_set_drvdata(pdev, xsdfec);
265
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;
272
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;
282         }
283         return 0;
284
285 err_xsdfec_idr:
286         xsdfec_idr_remove(xsdfec);
287 err_xsdfec_dev:
288         xsdfec_disable_all_clks(&xsdfec->clks);
289         return err;
290 }
291
292 static int xsdfec_remove(struct platform_device *pdev)
293 {
294         struct xsdfec_dev *xsdfec;
295
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;
301 }
302
303 static const struct of_device_id xsdfec_of_match[] = {
304         {
305                 .compatible = "xlnx,sd-fec-1.1",
306         },
307         { /* end of table */ }
308 };
309 MODULE_DEVICE_TABLE(of, xsdfec_of_match);
310
311 static struct platform_driver xsdfec_driver = {
312         .driver = {
313                 .name = "xilinx-sdfec",
314                 .of_match_table = xsdfec_of_match,
315         },
316         .probe = xsdfec_probe,
317         .remove =  xsdfec_remove,
318 };
319
320 static int __init xsdfec_init(void)
321 {
322         int err;
323
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;
330         }
331         return 0;
332 }
333
334 static void __exit xsdfec_exit(void)
335 {
336         platform_driver_unregister(&xsdfec_driver);
337         idr_destroy(&dev_idr);
338 }
339
340 module_init(xsdfec_init);
341 module_exit(xsdfec_exit);
342
343 MODULE_AUTHOR("Xilinx, Inc");
344 MODULE_DESCRIPTION("Xilinx SD-FEC16 Driver");
345 MODULE_LICENSE("GPL");