Merge branch 'net-aquantia-fixes-on-vlan-filters-and-other-conditions'
[linux-2.6-block.git] / drivers / misc / xilinx_sdfec.c
CommitLineData
76d83e1c
DC
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
25static struct idr dev_idr;
26static struct mutex dev_idr_lock;
27
21f010b1
DC
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 */
39struct 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
76d83e1c
DC
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
21f010b1 56 * @clks: Clocks managed by the SDFEC driver
76d83e1c
DC
57 * @dev_name: Device name
58 * @dev_id: Device ID
59 *
60 * This structure contains necessary state for SDFEC driver to operate
61 */
62struct 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;
21f010b1 68 struct xsdfec_clks clks;
76d83e1c
DC
69 char dev_name[DEV_NAME_LEN];
70 int dev_id;
71};
72
73static const struct file_operations xsdfec_fops = {
74 .owner = THIS_MODULE,
75};
76
21f010b1
DC
77static 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
200err_disable_ctrl_clk:
201 clk_disable_unprepare(clks->ctrl_clk);
202err_disable_dout_words_clk:
203 clk_disable_unprepare(clks->dout_words_clk);
204err_disable_dout_clk:
205 clk_disable_unprepare(clks->dout_clk);
206err_disable_din_words_clk:
207 clk_disable_unprepare(clks->din_words_clk);
208err_disable_din_clk:
209 clk_disable_unprepare(clks->din_clk);
210err_disable_axi_clk:
211 clk_disable_unprepare(clks->axi_clk);
212err_disable_core_clk:
213 clk_disable_unprepare(clks->core_clk);
214
215 return err;
216}
217
218static 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
76d83e1c
DC
230static 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
237static 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
21f010b1
DC
251 err = xsdfec_clk_init(pdev, &xsdfec->clks);
252 if (err)
253 return err;
254
76d83e1c
DC
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);
21f010b1 260 goto err_xsdfec_dev;
76d83e1c
DC
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)
21f010b1 270 goto err_xsdfec_dev;
76d83e1c
DC
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);
21f010b1 281 goto err_xsdfec_idr;
76d83e1c
DC
282 }
283 return 0;
284
21f010b1 285err_xsdfec_idr:
76d83e1c 286 xsdfec_idr_remove(xsdfec);
21f010b1
DC
287err_xsdfec_dev:
288 xsdfec_disable_all_clks(&xsdfec->clks);
76d83e1c
DC
289 return err;
290}
291
292static 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);
21f010b1 299 xsdfec_disable_all_clks(&xsdfec->clks);
76d83e1c
DC
300 return 0;
301}
302
303static const struct of_device_id xsdfec_of_match[] = {
304 {
305 .compatible = "xlnx,sd-fec-1.1",
306 },
307 { /* end of table */ }
308};
309MODULE_DEVICE_TABLE(of, xsdfec_of_match);
310
311static 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
320static 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
334static void __exit xsdfec_exit(void)
335{
336 platform_driver_unregister(&xsdfec_driver);
337 idr_destroy(&dev_idr);
338}
339
340module_init(xsdfec_init);
341module_exit(xsdfec_exit);
342
343MODULE_AUTHOR("Xilinx, Inc");
344MODULE_DESCRIPTION("Xilinx SD-FEC16 Driver");
345MODULE_LICENSE("GPL");