1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2019 Xilinx, Inc.
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>
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
;
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
42 struct clk
*din_words_clk
;
45 struct clk
*dout_words_clk
;
47 struct clk
*status_clk
;
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
60 * This structure contains necessary state for SDFEC driver to operate
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
];
73 static const struct file_operations xsdfec_fops
= {
77 static int xsdfec_clk_init(struct platform_device
*pdev
,
78 struct xsdfec_clks
*clks
)
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
);
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
);
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
);
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
);
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
);
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
);
146 clks
->status_clk
= NULL
;
149 err
= clk_prepare_enable(clks
->core_clk
);
151 dev_err(&pdev
->dev
, "failed to enable core_clk (%d)", err
);
155 err
= clk_prepare_enable(clks
->axi_clk
);
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
);
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
);
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
);
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
);
181 dev_err(&pdev
->dev
, "failed to enable dout_words_clk (%d)",
183 goto err_disable_dout_clk
;
186 err
= clk_prepare_enable(clks
->ctrl_clk
);
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
);
194 dev_err(&pdev
->dev
, "failed to enable status_clk (%d)\n", err
);
195 goto err_disable_ctrl_clk
;
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
);
209 clk_disable_unprepare(clks
->din_clk
);
211 clk_disable_unprepare(clks
->axi_clk
);
212 err_disable_core_clk
:
213 clk_disable_unprepare(clks
->core_clk
);
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
;
241 struct resource
*res
;
244 xsdfec
= devm_kzalloc(&pdev
->dev
, sizeof(*xsdfec
), GFP_KERNEL
);
248 xsdfec
->dev
= &pdev
->dev
;
249 spin_lock_init(&xsdfec
->error_data_lock
);
251 err
= xsdfec_clk_init(pdev
, &xsdfec
->clks
);
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
);
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
);
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
);
280 dev_err(dev
, "error:%d. Unable to register device", err
);
286 xsdfec_idr_remove(xsdfec
);
288 xsdfec_disable_all_clks(&xsdfec
->clks
);
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
);
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
= {
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)
324 mutex_init(&dev_idr_lock
);
326 err
= platform_driver_register(&xsdfec_driver
);
328 pr_err("%s Unabled to register SDFEC driver", __func__
);
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");