From 25d3edd26a2977b14a202597aae1e417a9b76961 Mon Sep 17 00:00:00 2001 From: Minghuan Lian Date: Thu, 13 Oct 2011 13:06:24 +0800 Subject: uio: Add FSL RMan UIO driver support Add support to expose FSL RMan controller to user space via UIO system. The RMan is composed of a global configuration and multiple inbound classification units. We map the global configuration as one UIO device and each classification unit as a unique UIO device. Change-Id: Ia491dae0d82c5fee9eb7e2b535305b557ba7329a Signed-off-by: Minghuan Lian Signed-off-by: Andy Fleming Reviewed-on: http://git.am.freescale.net:8181/922 Reviewed-by: Fleming Andrew-AFLEMING Tested-by: Fleming Andrew-AFLEMING diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 4c2475a..ceb295f 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -146,4 +146,6 @@ source "drivers/staging/fsl_qbman/Kconfig" source "drivers/staging/fsl_pme2/Kconfig" +source "drivers/staging/fsl_rman/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 5b24529..fb0c4bd 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -65,4 +65,4 @@ obj-$(CONFIG_SB105X) += sb105x/ obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/ obj-$(CONFIG_FSL_DPA) += fsl_qbman/ obj-$(CONFIG_FSL_PME2) += fsl_pme2/ - +obj-$(CONFIG_FSL_RMAN_UIO) += fsl_rman/ diff --git a/drivers/staging/fsl_rman/Kconfig b/drivers/staging/fsl_rman/Kconfig new file mode 100644 index 0000000..9ba0504 --- /dev/null +++ b/drivers/staging/fsl_rman/Kconfig @@ -0,0 +1,4 @@ +config FSL_RMAN_UIO + bool "Freescale RapidIO Message Manager support" + depends on UIO + default y diff --git a/drivers/staging/fsl_rman/Makefile b/drivers/staging/fsl_rman/Makefile new file mode 100644 index 0000000..6329e96 --- /dev/null +++ b/drivers/staging/fsl_rman/Makefile @@ -0,0 +1,2 @@ +# FSL RMan +obj-$(CONFIG_FSL_RMAN_UIO) += rman_uio_driver.o diff --git a/drivers/staging/fsl_rman/rman_uio_driver.c b/drivers/staging/fsl_rman/rman_uio_driver.c new file mode 100644 index 0000000..80c2e38 --- /dev/null +++ b/drivers/staging/fsl_rman/rman_uio_driver.c @@ -0,0 +1,347 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * Author: Minghuan Lian + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +static const char rman_uio_version[] = "RMan UIO driver v1.0"; + +#define IB_INDEX_OFFSET 12 +#define MMIER 0x0420 /* Message manage Interrupt Enable Register*/ +#define MMEDR 0x0424 /* Message manager error detect register */ +#define MMEDR_CLEAR 0x800000FF + +struct rman_uio_info { + atomic_t ref; /* exclusive, only one open() at a time */ + struct uio_info uio; + char name[30]; +}; + +struct rman_dev { + u32 revision; + int irq; + void __iomem *global_regs; + struct device *dev; + struct rman_uio_info info; + struct resource *res; + struct list_head ib_list; +}; + +struct rman_inbound_block { + struct list_head node; + u32 index; + struct device *dev; + struct rman_uio_info info; + struct resource *res; +}; + +static int rman_uio_open(struct uio_info *info, struct inode *inode) +{ + struct rman_uio_info *i = container_of(info, struct rman_uio_info, uio); + if (!atomic_dec_and_test(&i->ref)) { + pr_err("%s: failing non-exclusive open()\n", i->name); + atomic_inc(&i->ref); + return -EBUSY; + } + return 0; +} + +static int rman_uio_release(struct uio_info *info, struct inode *inode) +{ + struct rman_uio_info *i = container_of(info, struct rman_uio_info, uio); + atomic_inc(&i->ref); + return 0; +} + +static irqreturn_t rman_uio_irq_handler(int irq, struct uio_info *dev_info) +{ + struct rman_dev *rmdev = dev_info->priv; + u32 status; + + status = in_be32(rmdev->global_regs + MMEDR); + + if (status) { + /* disable interrupt */ + out_be32(rmdev->global_regs + MMIER, 0); + return IRQ_HANDLED; + } else + return IRQ_NONE; +} + +static int rman_uio_init(struct rman_dev *rmdev) +{ + int ret; + struct rman_uio_info *info; + + info = &rmdev->info; + atomic_set(&info->ref, 1); + info->uio.name = info->name; + info->uio.version = rman_uio_version; + info->uio.mem[0].name = "rman regs"; + info->uio.mem[0].addr = rmdev->res->start; + info->uio.mem[0].size = rmdev->res->end - rmdev->res->start + 1; + info->uio.mem[0].internal_addr = rmdev->global_regs; + info->uio.mem[0].memtype = UIO_MEM_PHYS; + info->uio.irq = rmdev->irq; + info->uio.irq_flags = IRQF_SHARED; + info->uio.handler = rman_uio_irq_handler; + info->uio.open = rman_uio_open; + info->uio.release = rman_uio_release; + info->uio.priv = rmdev; + ret = uio_register_device(rmdev->dev, &info->uio); + if (ret) { + pr_err("rman_uio: UIO registration failed\n"); + return ret; + } + return 0; +} + +static int rman_ib_uio_init(struct rman_inbound_block *ib) +{ + int ret; + struct rman_uio_info *info; + + info = &ib->info; + atomic_set(&info->ref, 1); + info->uio.name = info->name; + info->uio.version = rman_uio_version; + info->uio.mem[0].name = "rman inbound block regs"; + info->uio.mem[0].addr = ib->res->start; + info->uio.mem[0].size = ib->res->end - ib->res->start + 1; + info->uio.mem[0].memtype = UIO_MEM_PHYS; + info->uio.open = rman_uio_open; + info->uio.release = rman_uio_release; + info->uio.priv = ib; + ret = uio_register_device(ib->dev, &info->uio); + if (ret) { + pr_err("rman_ib_uio: UIO registration failed\n"); + return ret; + } + return 0; +} + +static int fsl_rman_ib_probe(struct device_node *ib_node, + struct rman_dev *rmdev) +{ + struct rman_inbound_block *ib; + struct resource regs; + int err; + + if (!ib_node || !rmdev) + return -EINVAL; + + ib = kzalloc(sizeof(*ib), GFP_KERNEL); + if (!ib) { + dev_err(rmdev->dev, "Can't alloc memory for inbound_block\n"); + return -ENOMEM; + } + + ib->dev = rmdev->dev; + + err = of_address_to_resource(ib_node, 0, ®s); + if (unlikely(err < 0)) { + dev_err(ib->dev, "Can't get property 'reg'\n"); + err = -EFAULT; + goto _err; + } + + ib->index = (regs.start >> IB_INDEX_OFFSET) & 0xf; + snprintf(ib->info.name, sizeof(ib->info.name), + "rman-inbound-block%d", ib->index); + + ib->res = devm_request_mem_region(rmdev->dev, regs.start, + regs.end + 1 - regs.start, + ib->info.name); + if (unlikely(!ib->res)) { + dev_err(ib->dev, "devm_request_mem_region failed\n"); + err = -ENOMEM; + goto _err; + } + dev_dbg(ib->dev, + "inbound block%d reg start 0x%016llx, size 0x%016llx.\n", + ib->index, ib->res->start, + ib->res->end + 1 - ib->res->start); + + err = rman_ib_uio_init(ib); + if (err) + goto _err; + + list_add(&ib->node, &rmdev->ib_list); + dev_info(ib->dev, "RMan inbound block%d initialized.\n", ib->index); + return 0; +_err: + kfree(ib); + return err; +} + +static int fsl_rman_ib_remove(struct rman_inbound_block *ib) +{ + if (!ib) + return 0; + uio_unregister_device(&ib->info.uio); + kfree(ib); + return 0; +} + +static int fsl_rman_probe(struct platform_device *dev) +{ + struct resource regs; + struct rman_dev *rman_dev; + struct device_node *rman_node, *child; + struct rman_inbound_block *ib, *tmp; + int err; + + rman_node = dev->dev.of_node; + if (!rman_node) { + dev_err(&dev->dev, "Device OF-Node is NULL"); + return -EFAULT; + } + dev_info(&dev->dev, "Of-device %s initialized\n", + rman_node->full_name); + + rman_dev = kzalloc(sizeof(struct rman_dev), GFP_KERNEL); + if (!rman_dev) { + dev_err(&dev->dev, "Can't allocate memory for 'rman_dev'\n"); + return -ENOMEM; + } + + rman_dev->dev = &dev->dev; + INIT_LIST_HEAD(&rman_dev->ib_list); + platform_set_drvdata(dev, rman_dev); + + for_each_child_of_node(rman_node, child) { + if (of_device_is_compatible(child, "fsl,rman-inbound-block")) + fsl_rman_ib_probe(child, rman_dev); + + if (of_device_is_compatible(child, "fsl,rman-global-cfg")) { + err = of_address_to_resource(child, 0, ®s); + if (unlikely(err < 0)) { + dev_err(&dev->dev, + "Can't get property 'reg'\n"); + err = -EFAULT; + goto _err; + } + } + } + + snprintf(rman_dev->info.name, sizeof(rman_dev->info.name), + "rman-uio"); + rman_dev->res = devm_request_mem_region(&dev->dev, regs.start, + regs.end - regs.start + 1, + rman_dev->info.name); + dev_dbg(&dev->dev, "global regs start 0x%016llx, size 0x%016llx.\n", + rman_dev->res->start, + rman_dev->res->end + 1 - rman_dev->res->start); + if (unlikely(rman_dev->res == NULL)) { + dev_err(&dev->dev, "devm_request_mem_region failed\n"); + err = -ENOMEM; + goto _err; + } + + rman_dev->global_regs = devm_ioremap(&dev->dev, rman_dev->res->start, + rman_dev->res->end - rman_dev->res->start + 1); + if (unlikely(rman_dev->global_regs == 0)) { + dev_err(&dev->dev, "devm_ioremap failed\n"); + err = -EIO; + goto _err; + } + + rman_dev->irq = irq_of_parse_and_map(rman_node, 0); + dev_dbg(rman_dev->dev, "errirq: %d\n", rman_dev->irq); + + err = rman_uio_init(rman_dev); + if (err) + goto _err; + return 0; + +_err: + platform_set_drvdata(dev, NULL); + list_for_each_entry_safe(ib, tmp, &rman_dev->ib_list, node) { + list_del(&ib->node); + fsl_rman_ib_remove(ib); + } + kfree(rman_dev); + return err; +} + +static int fsl_rman_remove(struct platform_device *dev) +{ + struct rman_dev *rman_dev = platform_get_drvdata(dev); + struct rman_inbound_block *ib, *tmp; + + if (!rman_dev) + return -EINVAL; + + list_for_each_entry_safe(ib, tmp, &rman_dev->ib_list, node) { + list_del(&ib->node); + fsl_rman_ib_remove(ib); + } + + uio_unregister_device(&rman_dev->info.uio); + platform_set_drvdata(dev, NULL); + kfree(rman_dev); + return 0; +} + +static const struct of_device_id fsl_of_rman_match[] = { + { + .compatible = "fsl,rman", + }, + {} +}; + +static struct platform_driver fsl_rman_driver = { + .driver = { + .name = "fsl-of-rman", + .owner = THIS_MODULE, + .of_match_table = fsl_of_rman_match, + }, + .probe = fsl_rman_probe, + .remove = fsl_rman_remove, +}; + +static int __init fsl_rman_init(void) +{ + int err; + + err = platform_driver_register(&fsl_rman_driver); + if (unlikely(err < 0)) + pr_warn(": %s:%hu:%s(): platform_driver_register() = %d\n", + __FILE__, __LINE__, __func__, err); + + return err; +} + +static void __exit fsl_rman_exit(void) +{ + platform_driver_unregister(&fsl_rman_driver); +} + +module_init(fsl_rman_init); +module_exit(fsl_rman_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Minghuan Lian "); +MODULE_DESCRIPTION("Freescale RMan UIO driver"); -- cgit v0.10.2