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: Dragan Cvetic <dragan.cvetic@xilinx.com>
Signed-off-by: Derek Kiernan <derek.kiernan@xilinx.com>
Signed-off-by: Dragan Cvetic <dragan.cvetic@xilinx.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Dragan Cvetic 2019-06-11 18:29:36 +01:00 committed by Greg Kroah-Hartman
parent a00b004218
commit 76d83e1c32
3 changed files with 175 additions and 0 deletions

View File

@ -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

View File

@ -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

162
drivers/misc/xilinx_sdfec.c Normal file
View File

@ -0,0 +1,162 @@
// 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");