Commit 76d83e1c authored by Dragan Cvetic's avatar Dragan Cvetic Committed by Greg Kroah-Hartman

misc: xilinx-sdfec: add core driver

Implement a platform driver that matches with xlnx,
sd-fec-1.1 device tree node and registers as a character
device, including:
- SD-FEC driver binds to sdfec DT node.
- creates and initialise an initial driver dev structure.
- add the driver in Linux build and Kconfig.
Tested-by: default avatarDragan Cvetic <dragan.cvetic@xilinx.com>
Signed-off-by: default avatarDerek Kiernan <derek.kiernan@xilinx.com>
Signed-off-by: default avatarDragan Cvetic <dragan.cvetic@xilinx.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a00b0042
...@@ -472,6 +472,18 @@ config PCI_ENDPOINT_TEST ...@@ -472,6 +472,18 @@ config PCI_ENDPOINT_TEST
Enable this configuration option to enable the host side test driver Enable this configuration option to enable the host side test driver
for PCI Endpoint. for PCI Endpoint.
config XILINX_SDFEC
tristate "Xilinx SDFEC 16"
help
This option enables support for the Xilinx SDFEC (Soft Decision
Forward Error Correction) driver. This enables a char driver
for the SDFEC.
You may select this driver if your design instantiates the
SDFEC(16nm) hardened block. To compile this as a module choose M.
If unsure, say N.
config MISC_RTSX config MISC_RTSX
tristate tristate
default MISC_RTSX_PCI || MISC_RTSX_USB default MISC_RTSX_PCI || MISC_RTSX_USB
......
...@@ -59,3 +59,4 @@ obj-$(CONFIG_OCXL) += ocxl/ ...@@ -59,3 +59,4 @@ obj-$(CONFIG_OCXL) += ocxl/
obj-y += cardreader/ obj-y += cardreader/
obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_HABANA_AI) += habanalabs/ obj-$(CONFIG_HABANA_AI) += habanalabs/
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
// SPDX-License-Identifier: GPL-2.0
/*
* Xilinx SDFEC
*
* Copyright (C) 2019 Xilinx, Inc.
*
* Description:
* This driver is developed for SDFEC16 (Soft Decision FEC 16nm)
* IP. It exposes a char device which supports file operations
* like open(), close() and ioctl().
*/
#include <linux/miscdevice.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/clk.h>
#define DEV_NAME_LEN 12
static struct idr dev_idr;
static struct mutex dev_idr_lock;
/**
* struct xsdfec_dev - Driver data for SDFEC
* @regs: device physical base address
* @dev: pointer to device struct
* @miscdev: Misc device handle
* @error_data_lock: Error counter and states spinlock
* @dev_name: Device name
* @dev_id: Device ID
*
* This structure contains necessary state for SDFEC driver to operate
*/
struct xsdfec_dev {
void __iomem *regs;
struct device *dev;
struct miscdevice miscdev;
/* Spinlock to protect state_updated and stats_updated */
spinlock_t error_data_lock;
char dev_name[DEV_NAME_LEN];
int dev_id;
};
static const struct file_operations xsdfec_fops = {
.owner = THIS_MODULE,
};
static void xsdfec_idr_remove(struct xsdfec_dev *xsdfec)
{
mutex_lock(&dev_idr_lock);
idr_remove(&dev_idr, xsdfec->dev_id);
mutex_unlock(&dev_idr_lock);
}
static int xsdfec_probe(struct platform_device *pdev)
{
struct xsdfec_dev *xsdfec;
struct device *dev;
struct resource *res;
int err;
xsdfec = devm_kzalloc(&pdev->dev, sizeof(*xsdfec), GFP_KERNEL);
if (!xsdfec)
return -ENOMEM;
xsdfec->dev = &pdev->dev;
spin_lock_init(&xsdfec->error_data_lock);
dev = xsdfec->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
xsdfec->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(xsdfec->regs)) {
err = PTR_ERR(xsdfec->regs);
return err;
}
/* Save driver private data */
platform_set_drvdata(pdev, xsdfec);
mutex_lock(&dev_idr_lock);
err = idr_alloc(&dev_idr, xsdfec->dev_name, 0, 0, GFP_KERNEL);
mutex_unlock(&dev_idr_lock);
if (err < 0)
goto err_xsddev_idr;
xsdfec->dev_id = err;
snprintf(xsdfec->dev_name, DEV_NAME_LEN, "xsdfec%d", xsdfec->dev_id);
xsdfec->miscdev.minor = MISC_DYNAMIC_MINOR;
xsdfec->miscdev.name = xsdfec->dev_name;
xsdfec->miscdev.fops = &xsdfec_fops;
xsdfec->miscdev.parent = dev;
err = misc_register(&xsdfec->miscdev);
if (err) {
dev_err(dev, "error:%d. Unable to register device", err);
return err;
}
return 0;
err_xsddev_idr:
xsdfec_idr_remove(xsdfec);
return err;
}
static int xsdfec_remove(struct platform_device *pdev)
{
struct xsdfec_dev *xsdfec;
xsdfec = platform_get_drvdata(pdev);
misc_deregister(&xsdfec->miscdev);
xsdfec_idr_remove(xsdfec);
return 0;
}
static const struct of_device_id xsdfec_of_match[] = {
{
.compatible = "xlnx,sd-fec-1.1",
},
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, xsdfec_of_match);
static struct platform_driver xsdfec_driver = {
.driver = {
.name = "xilinx-sdfec",
.of_match_table = xsdfec_of_match,
},
.probe = xsdfec_probe,
.remove = xsdfec_remove,
};
static int __init xsdfec_init(void)
{
int err;
mutex_init(&dev_idr_lock);
idr_init(&dev_idr);
err = platform_driver_register(&xsdfec_driver);
if (err < 0) {
pr_err("%s Unabled to register SDFEC driver", __func__);
return err;
}
return 0;
}
static void __exit xsdfec_exit(void)
{
platform_driver_unregister(&xsdfec_driver);
idr_destroy(&dev_idr);
}
module_init(xsdfec_init);
module_exit(xsdfec_exit);
MODULE_AUTHOR("Xilinx, Inc");
MODULE_DESCRIPTION("Xilinx SD-FEC16 Driver");
MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment