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"); 346