diff options
Diffstat (limited to 'drivers/uio')
-rw-r--r-- | drivers/uio/Kconfig | 26 | ||||
-rw-r--r-- | drivers/uio/Makefile | 4 | ||||
-rw-r--r-- | drivers/uio/fsl_dma_uio.c | 283 | ||||
-rw-r--r-- | drivers/uio/fsl_rmu_uio.c | 253 | ||||
-rw-r--r-- | drivers/uio/fsl_sec_uio.c | 383 | ||||
-rw-r--r-- | drivers/uio/fsl_srio_uio.c | 424 | ||||
-rw-r--r-- | drivers/uio/uio.c | 6 |
7 files changed, 1378 insertions, 1 deletions
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index 5295be0..26b86cc 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -128,4 +128,30 @@ config UIO_PRUSS To compile this driver as a module, choose M here: the module will be called uio_pruss. +config UIO_FSL_SRIO + tristate "Freescale Serial RapidIO support" + depends on RAPIDIO=n + default n + +config UIO_FSL_RMU + tristate "Freescale RapidIO Message Unit support" + depends on RAPIDIO=n + default n + help + UIO driver for the Freescale RapidIO Message Unit and Doorbell. + This driver will obtain the RMU nodes from DTB structure, and + expose the RMU regiter space to user space. + + If you don't know what to do here, say N. + +config UIO_FSL_DMA + tristate "Freescale DMA support" + depends on FSL_DMA=n + default n + +config UIO_FSL_SEC + tristate "Freescale SEC support" + depends on CRYPTO_DEV_FSL_CAAM=n + default n + endif diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile index b354c53..bc221ec 100644 --- a/drivers/uio/Makefile +++ b/drivers/uio/Makefile @@ -8,3 +8,7 @@ obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o obj-$(CONFIG_UIO_NETX) += uio_netx.o obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o +obj-$(CONFIG_UIO_FSL_SRIO) += fsl_srio_uio.o +obj-$(CONFIG_UIO_FSL_RMU) += fsl_rmu_uio.o +obj-$(CONFIG_UIO_FSL_DMA) += fsl_dma_uio.o +obj-$(CONFIG_UIO_FSL_SEC) += fsl_sec_uio.o diff --git a/drivers/uio/fsl_dma_uio.c b/drivers/uio/fsl_dma_uio.c new file mode 100644 index 0000000..6eeffd2 --- /dev/null +++ b/drivers/uio/fsl_dma_uio.c @@ -0,0 +1,283 @@ +/* + * Copyright 2011-2013 Freescale Semiconductor, Inc. + * + * Author: Kai Jiang <Kai.Jiang@freescale.com> + * + * 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 <linux/of_platform.h> +#include <linux/module.h> +#include <linux/uio_driver.h> +#include <linux/list.h> +#include <linux/io.h> + +static const char dma_uio_version[] = "DMA UIO driver v1.0"; + +#define DMA_SR 0x4 /* DMA Status Regsiter */ + +struct dma_uio_info { + atomic_t ref; /* exclusive, only one open() at a time */ + struct uio_info uio; + char name[20]; +}; + +struct dma_chan { + struct device *dev; + struct resource *res; + struct dma_uio_info *info; + struct list_head list; + void __iomem *regs_win; + int irq; + u32 dma_id; + u32 ch_id; +}; + +struct fsldma_device { + struct platform_device *dev; + struct list_head ch_list; + u32 dma_id; +}; + +static int dma_uio_open(struct uio_info *info, struct inode *inode) +{ + struct dma_uio_info *i = container_of(info, struct dma_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 dma_uio_release(struct uio_info *info, struct inode *inode) +{ + struct dma_uio_info *i = container_of(info, struct dma_uio_info, uio); + atomic_inc(&i->ref); + + return 0; +} + +static irqreturn_t dma_uio_irq_handler(int irq, struct uio_info *dev_info) +{ + struct dma_chan *dma_ch = dev_info->priv; + + out_be32((u32 *)((u8 *)dma_ch->regs_win + DMA_SR), ~0); + + return IRQ_HANDLED; +} + +static int __init dma_chan_uio_setup(struct dma_chan *dma_ch) +{ + int ret; + struct dma_uio_info *info; + + info = devm_kzalloc(dma_ch->dev, sizeof(struct dma_uio_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + dma_ch->info = info; + + atomic_set(&info->ref, 1); + snprintf(info->name, sizeof(info->name)-1, "dma-uio%d-%d", + dma_ch->dma_id, dma_ch->ch_id); + info->uio.name = info->name; + info->uio.version = dma_uio_version; + info->uio.mem[0].name = "dma regs"; + info->uio.mem[0].addr = dma_ch->res->start; + info->uio.mem[0].size = (dma_ch->res->end - dma_ch->res->start + 1 > + PAGE_SIZE) ? + dma_ch->res->end - dma_ch->res->start + 1 : PAGE_SIZE; + info->uio.mem[0].internal_addr = dma_ch->regs_win; + info->uio.mem[0].memtype = UIO_MEM_PHYS; + + info->uio.irq = dma_ch->irq; + info->uio.irq_flags = IRQF_SHARED; + info->uio.handler = dma_uio_irq_handler; + info->uio.open = dma_uio_open; + info->uio.release = dma_uio_release; + info->uio.priv = dma_ch; + + ret = uio_register_device(dma_ch->dev, &info->uio); + if (ret) { + dev_err(dma_ch->dev, "dma_uio: UIO registration failed\n"); + return ret; + } + + return 0; +} + +static int fsl_dma_chan_probe(struct fsldma_device *fdev, + struct device_node *node) +{ + struct resource regs; + struct dma_chan *dma_ch; + struct device_node *dma_node; + int err; + u32 *cell; + struct platform_device *dev = fdev->dev; + + dma_node = node; + dev_dbg(&dev->dev, "Of-device full name %s\n", dma_node->full_name); + + dma_ch = devm_kzalloc(&dev->dev, sizeof(struct dma_chan), GFP_KERNEL); + if (!dma_ch) { + dev_err(&dev->dev, "Can't alloc memory for 'dma_ch'\n"); + return -ENOMEM; + } + + cell = (u32 *)of_get_property(dma_node, "cell-index", NULL); + if (!cell) { + dev_err(&dev->dev, "Can't get property 'cell-index'\n"); + return -EFAULT; + } + + dma_ch->dma_id = fdev->dma_id; + dma_ch->ch_id = *cell; + dma_ch->dev = &dev->dev; + + err = of_address_to_resource(dma_node, 0, ®s); + if (err < 0) { + dev_err(&dev->dev, "Can't get property 'reg'\n"); + return -EFAULT; + } + + dma_ch->res = devm_request_mem_region(&dev->dev, regs.start, + regs.end + 1 - regs.start, "dma"); + if (dma_ch->res == NULL) { + dev_err(&dev->dev, "devm_request_mem_region failed\n"); + return -ENOMEM; + } + + dev_dbg(&dev->dev, "reg start 0x%016llx, size 0x%016llx.\n", + dma_ch->res->start, dma_ch->res->end + 1 - + dma_ch->res->start); + + dma_ch->regs_win = devm_ioremap(&dev->dev, dma_ch->res->start, + dma_ch->res->end - dma_ch->res->start + 1); + if (dma_ch->regs_win == NULL) { + dev_err(&dev->dev, "devm_ioremap failed\n"); + return -EIO; + } + + dma_ch->irq = irq_of_parse_and_map(dma_node, 0); + dev_dbg(dma_ch->dev, "dma channel irq: %d\n", dma_ch->irq); + + err = dma_chan_uio_setup(dma_ch); + if (err < 0) { + dev_err(dma_ch->dev, "dma_chan_uio_setup failed\n"); + return err; + } + + list_add_tail(&dma_ch->list, &fdev->ch_list); + + dev_info(&dev->dev, "dma channel %s initialized\n", dma_ch->info->name); + + return 0; +} + +static void fsl_dma_chan_remove(struct dma_chan *dma_ch) +{ + uio_unregister_device(&dma_ch->info->uio); +} + +static int fsl_dma_uio_probe(struct platform_device *dev) +{ + struct device_node *child; + struct fsldma_device *fdev; + u32 *cell; + + cell = (u32 *)of_get_property(dev->dev.of_node, "cell-index", NULL); + if (!cell) { + dev_err(&dev->dev, "Can't get property 'cell-index'\n"); + return -ENODEV; + } + + fdev = devm_kzalloc(&dev->dev, sizeof(struct fsldma_device), + GFP_KERNEL); + if (!fdev) { + dev_err(&dev->dev, "Can't alloc memory for 'fdev''\n"); + return -ENOMEM; + } + + fdev->dma_id = *cell; + fdev->dev = dev; + INIT_LIST_HEAD(&fdev->ch_list); + dev_set_drvdata(&dev->dev, fdev); + + for_each_child_of_node(dev->dev.of_node, child) + if (of_device_is_compatible(child, "fsl,eloplus-dma-channel")) + fsl_dma_chan_probe(fdev, child); + + return 0; +} + +static int fsl_dma_uio_remove(struct platform_device *dev) +{ + struct fsldma_device *fdev; + struct dma_chan *dma_ch, *ch_tmp; + + fdev = dev_get_drvdata(&dev->dev); + list_for_each_entry_safe(dma_ch, ch_tmp, &fdev->ch_list, list) { + list_del(&dma_ch->list); + fsl_dma_chan_remove(dma_ch); + } + + return 0; +} + + +static const struct of_device_id fsl_of_dma_match[] = { + { + .compatible = "fsl,eloplus-dma", + }, + {} +}; + +static struct platform_driver fsl_dma_uio_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "fsl-of-dma", + .of_match_table = fsl_of_dma_match, + }, + .probe = fsl_dma_uio_probe, + .remove = fsl_dma_uio_remove, +}; + +static __init int fsl_dma_uio_init(void) +{ + int err; + + err = platform_driver_register(&fsl_dma_uio_driver); + if (err < 0) + pr_warn( + ": %s:%hu:%s(): platform_driver_register() = %d\n", + __FILE__, __LINE__, __func__, err); + + return err; +} + +static void __exit fsl_dma_uio_exit(void) +{ + platform_driver_unregister(&fsl_dma_uio_driver); + + pr_warn("fsl dma uio driver removed\n"); +} + +module_init(fsl_dma_uio_init); +module_exit(fsl_dma_uio_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/uio/fsl_rmu_uio.c b/drivers/uio/fsl_rmu_uio.c new file mode 100644 index 0000000..22ee854 --- /dev/null +++ b/drivers/uio/fsl_rmu_uio.c @@ -0,0 +1,253 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * 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 <linux/of_platform.h> +#include <linux/module.h> +#include <linux/uio_driver.h> +#include <linux/list.h> +#include <linux/io.h> + +/* rmu unit ID, based on the unit register off-set */ +#define RMU_UNIT_MSG0 0 +#define RMU_UNIT_MSG1 1 +#define RMU_UNIT_DBELL 4 + +static const char rmu_uio_version[] = "RMU UIO driver v1.0"; + +struct rmu_uio_info { + atomic_t ref; /* exclusive, only one open() at a time */ + struct uio_info uio; + char name[20]; +}; + +struct rmu_unit { + struct device *dev; + struct resource *res; + struct rmu_uio_info *info; + struct list_head list; + void __iomem *regs_win; + int irq; + u32 unit_id; +}; + +struct rmu_device { + struct platform_device *pdev; + struct list_head unit_list; +}; + +static int rmu_uio_open(struct uio_info *info, struct inode *inode) +{ + struct rmu_uio_info *i = container_of(info, struct rmu_uio_info, uio); + struct rmu_unit *unit = info->priv; + + if (!atomic_dec_and_test(&i->ref)) { + dev_err(unit->dev, + "%s: failing non-exclusive open()\n", i->name); + atomic_inc(&i->ref); + return -EBUSY; + } + + return 0; +} + +static int rmu_uio_release(struct uio_info *info, struct inode *inode) +{ + struct rmu_uio_info *i = container_of(info, struct rmu_uio_info, uio); + + atomic_inc(&i->ref); + + return 0; +} + +static int __init rmu_unit_uio_setup(struct rmu_unit *unit) +{ + int ret; + struct rmu_uio_info *info; + + info = devm_kzalloc(unit->dev, sizeof(struct rmu_uio_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + unit->info = info; + + atomic_set(&info->ref, 1); + if (unit->unit_id == RMU_UNIT_DBELL) + snprintf(info->name, sizeof(info->name), "rmu-uio-doorbell"); + else + snprintf(info->name, sizeof(info->name), "rmu-uio-msg%d", + unit->unit_id); + + info->uio.name = info->name; + info->uio.version = rmu_uio_version; + info->uio.mem[0].name = "rmu regs"; + info->uio.mem[0].addr = unit->res->start; + info->uio.mem[0].size = resource_size(unit->res); + info->uio.mem[0].internal_addr = unit->regs_win; + info->uio.mem[0].memtype = UIO_MEM_PHYS; + info->uio.open = rmu_uio_open; + info->uio.release = rmu_uio_release; + info->uio.priv = unit; + + ret = uio_register_device(unit->dev, &info->uio); + if (ret) { + dev_err(unit->dev, "rmu_uio: UIO registration failed\n"); + return ret; + } + + return 0; +} + +static int fsl_rmu_unit_probe(struct rmu_device *rmu_dev, + struct device_node *node) +{ + struct resource regs; + struct rmu_unit *unit; + struct device_node *unit_node; + int err; + struct platform_device *pdev = rmu_dev->pdev; + + unit_node = node; + dev_dbg(&pdev->dev, "of-device full name %s\n", unit_node->full_name); + + unit = devm_kzalloc(&pdev->dev, sizeof(struct rmu_unit), GFP_KERNEL); + if (!unit) { + dev_err(&pdev->dev, "Can't alloc memory for 'rmu_unit'\n"); + return -ENOMEM; + } + + unit->dev = &pdev->dev; + + err = of_address_to_resource(unit_node, 0, ®s); + if (err < 0) { + dev_err(&pdev->dev, "Can't get property 'reg'\n"); + return -EFAULT; + } + + unit->unit_id = (regs.start >> 8) & 0xf; + unit->res = devm_request_mem_region(&pdev->dev, regs.start, + resource_size(®s), "rmu"); + if (!unit->res) { + dev_err(&pdev->dev, "devm_request_mem_region failed\n"); + return -ENOMEM; + } + + dev_dbg(&pdev->dev, "reg start 0x%016llx, size 0x%016llx.\n", + unit->res->start, resource_size(unit->res)); + + unit->regs_win = devm_ioremap(&pdev->dev, unit->res->start, + resource_size(unit->res)); + if (!unit->regs_win) { + dev_err(&pdev->dev, "devm_ioremap failed\n"); + return -EIO; + } + + err = rmu_unit_uio_setup(unit); + if (err < 0) { + dev_err(unit->dev, "rmu_unit_uio_setup failed\n"); + return err; + } + + list_add_tail(&unit->list, &rmu_dev->unit_list); + + dev_info(&pdev->dev, "rmu unit %s initialized\n", unit->info->name); + + return 0; +} + +static void fsl_rmu_unit_remove(struct rmu_unit *unit) +{ + uio_unregister_device(&unit->info->uio); +} + +static int fsl_rmu_uio_probe(struct platform_device *pdev) +{ + struct device_node *child; + struct rmu_device *rmu_dev; + + rmu_dev = devm_kzalloc(&pdev->dev, sizeof(struct rmu_device), + GFP_KERNEL); + if (!rmu_dev) { + dev_err(&pdev->dev, "Can't alloc memory for 'rmu_dev''\n"); + return -ENOMEM; + } + + rmu_dev->pdev = pdev; + INIT_LIST_HEAD(&rmu_dev->unit_list); + dev_set_drvdata(&pdev->dev, rmu_dev); + + for_each_child_of_node(pdev->dev.of_node, child) + if ((of_device_is_compatible(child, "fsl,srio-msg-unit")) + || (of_device_is_compatible(child, "fsl,srio-dbell-unit"))) + fsl_rmu_unit_probe(rmu_dev, child); + + return 0; +} + +static int fsl_rmu_uio_remove(struct platform_device *pdev) +{ + struct rmu_device *rmu_dev; + struct rmu_unit *unit, *unit_tmp; + + rmu_dev = dev_get_drvdata(&pdev->dev); + list_for_each_entry_safe(unit, unit_tmp, + &rmu_dev->unit_list, list) { + list_del(&unit->list); + fsl_rmu_unit_remove(unit); + } + + return 0; +} + +static const struct of_device_id fsl_of_rmu_match[] = { + { + .compatible = "fsl,srio-rmu", + }, + {} +}; + +static struct platform_driver fsl_rmu_uio_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "fsl-of-rmu", + .of_match_table = fsl_of_rmu_match, + }, + .probe = fsl_rmu_uio_probe, + .remove = fsl_rmu_uio_remove, +}; + +static __init int fsl_rmu_uio_init(void) +{ + int err; + + err = platform_driver_register(&fsl_rmu_uio_driver); + if (err < 0) + pr_err("fsl-rmu-uio: failed to register platform driver\n"); + + return err; +} + +static void __exit fsl_rmu_uio_exit(void) +{ + platform_driver_unregister(&fsl_rmu_uio_driver); +} + +module_init(fsl_rmu_uio_init); +module_exit(fsl_rmu_uio_exit); +MODULE_AUTHOR("Liu Gang <Gang.Liu@freescale.com>"); +MODULE_DESCRIPTION("UIO driver for Freescale RMU"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/uio/fsl_sec_uio.c b/drivers/uio/fsl_sec_uio.c new file mode 100644 index 0000000..295ae06 --- /dev/null +++ b/drivers/uio/fsl_sec_uio.c @@ -0,0 +1,383 @@ +/* + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * Author: Po Liu <po.liu@freescale.com> + * + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/io.h> +#include <linux/uio_driver.h> +#include <linux/slab.h> +#include <linux/list.h> + +static const char sec_uio_version[] = "fsl SEC UIO driver v1.0"; + +#define NAME_LENGTH 30 +#define JR_INDEX_OFFSET 12 + +static const char uio_device_name[NAME_LENGTH] = "fsl-sec"; +static LIST_HEAD(sec_list); + +struct sec_uio_info { + atomic_t ref; /* exclusive, only one open() at a time */ + struct uio_info uio; + char name[NAME_LENGTH]; +}; + +struct sec_dev { + u32 revision; + u32 index; + u32 irq; + void __iomem *global_regs; + struct device *dev; + struct resource res; + struct sec_uio_info info; + struct list_head node; + struct list_head jr_list; +}; + +struct sec_job_ring { + struct list_head list_node; + u32 index; + u32 irq; + struct device *dev; + struct sec_uio_info info; + struct resource *res; +}; + +static int sec_uio_open(struct uio_info *info, struct inode *inode) +{ + struct sec_uio_info *uio_info = container_of(info, + struct sec_uio_info, uio); + + if (!atomic_dec_and_test(&uio_info->ref)) { + pr_err("%s: failing non-exclusive open()\n", uio_info->name); + atomic_inc(&uio_info->ref); + return -EBUSY; + } + + return 0; +} + +static int sec_uio_release(struct uio_info *info, struct inode *inode) +{ + struct sec_uio_info *uio_info = container_of(info, + struct sec_uio_info, uio); + atomic_inc(&uio_info->ref); + + return 0; +} + +static irqreturn_t sec_uio_irq_handler(int irq, struct uio_info *dev_info) +{ + return IRQ_NONE; +} + +static int sec_uio_irqcontrol(struct uio_info *dev_info, int irqon) +{ + return 0; +} + +static irqreturn_t sec_jr_irq_handler(int irq, struct uio_info *dev_info) +{ + return IRQ_NONE; +} + +static int sec_jr_irqcontrol(struct uio_info *dev_info, int irqon) +{ + return 0; +} + +static int __init sec_uio_init(struct sec_dev *uio_dev) +{ + int ret; + struct sec_uio_info *info; + + info = &uio_dev->info; + atomic_set(&info->ref, 1); + info->uio.version = sec_uio_version; + info->uio.name = uio_dev->info.name; + info->uio.mem[0].name = "sec config space"; + info->uio.mem[0].addr = uio_dev->res.start; + info->uio.mem[0].size = uio_dev->res.end - uio_dev->res.start + 1; + info->uio.mem[0].internal_addr = uio_dev->global_regs; + info->uio.mem[0].memtype = UIO_MEM_PHYS; + info->uio.irq = uio_dev->irq; + info->uio.irq_flags = IRQF_SHARED; + info->uio.handler = sec_uio_irq_handler; + info->uio.irqcontrol = sec_uio_irqcontrol; + info->uio.open = sec_uio_open; + info->uio.release = sec_uio_release; + info->uio.priv = uio_dev; + + ret = uio_register_device(uio_dev->dev, &info->uio); + if (ret) { + pr_err("sec_uio: UIO registration failed\n"); + return ret; + } + + return 0; +} + +static int __init sec_jr_uio_init(struct sec_job_ring *jr) +{ + int ret; + struct sec_uio_info *info; + + info = &jr->info; + atomic_set(&info->ref, 1); + info->uio.version = sec_uio_version; + info->uio.name = jr->info.name; + info->uio.mem[0].name = "sec job ring"; + info->uio.mem[0].addr = jr->res->start; + info->uio.mem[0].size = jr->res->end - jr->res->start + 1; + info->uio.mem[0].memtype = UIO_MEM_PHYS; + info->uio.irq = jr->irq; + info->uio.irq_flags = IRQF_SHARED; + info->uio.handler = sec_jr_irq_handler; + info->uio.irqcontrol = sec_jr_irqcontrol; + info->uio.open = sec_uio_open; + info->uio.release = sec_uio_release; + info->uio.priv = jr; + + ret = uio_register_device(jr->dev, &info->uio); + if (ret) { + pr_err("sec_jr_uio: UIO registration failed\n"); + return ret; + } + + return 0; +} + +static int fsl_sec_jr_probe(struct device_node *jr_node, + struct sec_dev *scdev) +{ + struct sec_job_ring *jr; + struct resource regs; + int ret; + + if (!jr_node || !scdev) + return -EINVAL; + + jr = kzalloc(sizeof(*jr), GFP_KERNEL); + if (!jr) { + dev_err(scdev->dev, "Can't alloc memory for job ring\n"); + return -ENOMEM; + } + + jr->dev = scdev->dev; + + ret = of_address_to_resource(jr_node, 0, ®s); + if (unlikely(ret < 0)) { + dev_err(jr->dev, "Can't get property 'reg'\n"); + ret = -EFAULT; + goto abort_jr; + } + + jr->index = (regs.start >> JR_INDEX_OFFSET) & 0xf; + snprintf(jr->info.name, sizeof(jr->info.name)-1, + "sec_job_ring%d-%d", scdev->index, jr->index); + + jr->res = devm_request_mem_region(scdev->dev, regs.start, + regs.end + 1 - regs.start, + jr->info.name); + if (unlikely(!jr->res)) { + dev_err(jr->dev, "devm_request_mem_region failed\n"); + ret = -ENOMEM; + goto abort_jr; + } + + dev_dbg(jr->dev, + "sec_job_ring%d-%d reg start 0x%016llx, size 0x%016llx.\n", + scdev->index, jr->index, jr->res->start, + jr->res->end + 1 - jr->res->start); + + jr->irq = irq_of_parse_and_map(jr_node, 0); + dev_dbg(jr->dev, "errirq: %d\n", jr->irq); + + ret = sec_jr_uio_init(jr); + if (ret) + goto abort_jr; + + list_add(&jr->list_node, &scdev->jr_list); + dev_info(jr->dev, "sec_job_ring%d-%d initialized.\n", + scdev->index, jr->index); + + return 0; +abort_jr: + kfree(jr); + return ret; +} + +static int fsl_sec_jr_remove(struct sec_job_ring *jr) +{ + if (!jr) + return 0; + uio_unregister_device(&jr->info.uio); + kfree(jr); + + return 0; +} + +static const struct of_device_id jr_ids[] = { + { .compatible = "fsl,sec-v4.0-job-ring", }, + { .compatible = "fsl,sec-v4.4-job-ring", }, + { .compatible = "fsl,sec-v5.0-job-ring", }, + { .compatible = "fsl,sec-v6.0-job-ring", }, + {}, +}; + +static int fsl_sec_probe(struct platform_device *dev) +{ + struct resource regs; + struct sec_dev *sec_dev; + struct device_node *sec_node, *child; + struct sec_job_ring *jr, *tmp; + int ret, count = 0; + struct list_head *p; + + sec_node = dev->dev.of_node; + if (!sec_node) { + dev_err(&dev->dev, "Device OF-Node is NULL"); + return -EFAULT; + } + + sec_dev = kzalloc(sizeof(struct sec_dev), GFP_KERNEL); + if (!sec_dev) + return -ENOMEM; + + /* Creat name and index */ + list_for_each(p, &sec_list) { + count++; + } + sec_dev->index = count; + + snprintf(sec_dev->info.name, sizeof(sec_dev->info.name) - 1, + "%s%d", uio_device_name, sec_dev->index); + + sec_dev->dev = &dev->dev; + platform_set_drvdata(dev, sec_dev); + + dev_info(sec_dev->dev, "UIO device full name %s initialized\n", + sec_dev->info.name); + + /* Create each jr under this sec node */ + INIT_LIST_HEAD(&sec_dev->jr_list); + for_each_child_of_node(sec_node, child) { + if (of_match_node(jr_ids, child)) + fsl_sec_jr_probe(child, sec_dev); + } + + /* Get the resource from dtb node */ + ret = of_address_to_resource(sec_node, 0, ®s); + if (unlikely(ret < 0)) { + ret = -EFAULT; + goto abort; + } + + sec_dev->res = regs; + + sec_dev->global_regs = of_iomap(sec_node, 0); + + sec_dev->irq = irq_of_parse_and_map(sec_node, 0); + dev_dbg(sec_dev->dev, "errirq: %d\n", sec_dev->irq); + + /* Register UIO */ + ret = sec_uio_init(sec_dev); + if (ret) + goto abort_iounmap; + + list_add_tail(&sec_dev->node, &sec_list); + + return 0; + +abort_iounmap: + iounmap(sec_dev->global_regs); +abort: + list_for_each_entry_safe(jr, tmp, &sec_dev->jr_list, list_node) { + list_del(&jr->list_node); + fsl_sec_jr_remove(jr); + } + kfree(sec_dev); + return ret; +} + +static int fsl_sec_remove(struct platform_device *dev) +{ + struct sec_dev *sec_dev = platform_get_drvdata(dev); + struct sec_job_ring *jr, *tmp; + + if (!sec_dev) + return 0; + + list_for_each_entry_safe(jr, tmp, &sec_dev->jr_list, list_node) { + list_del(&jr->list_node); + fsl_sec_jr_remove(jr); + } + + list_del(&sec_dev->node); + uio_unregister_device(&sec_dev->info.uio); + platform_set_drvdata(dev, NULL); + iounmap(sec_dev->global_regs); + kfree(sec_dev); + + return 0; +} + +static const struct of_device_id sec_ids[] = { + { .compatible = "fsl,sec-v4.0", }, + { .compatible = "fsl,sec-v4.4", }, + { .compatible = "fsl,sec-v5.0", }, + { .compatible = "fsl,sec-v6.0", }, + {}, +}; + +static struct platform_driver fsl_sec_driver = { + .driver = { + .name = "fsl-sec-uio", + .owner = THIS_MODULE, + .of_match_table = sec_ids, + }, + .probe = fsl_sec_probe, + .remove = fsl_sec_remove, +}; + +static __init int fsl_sec_init(void) +{ + int ret; + + ret = platform_driver_register(&fsl_sec_driver); + if (unlikely(ret < 0)) + pr_warn(": %s:%hu:%s(): platform_driver_register() = %d\n", + __FILE__, __LINE__, __func__, ret); + + return ret; +} + +static void __exit fsl_sec_exit(void) +{ + platform_driver_unregister(&fsl_sec_driver); +} + +module_init(fsl_sec_init); +module_exit(fsl_sec_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Liu Po <po.liu@freescale.com>"); +MODULE_DESCRIPTION("FSL SEC UIO Driver"); diff --git a/drivers/uio/fsl_srio_uio.c b/drivers/uio/fsl_srio_uio.c new file mode 100644 index 0000000..23e799d --- /dev/null +++ b/drivers/uio/fsl_srio_uio.c @@ -0,0 +1,424 @@ +/* + * Copyright 2011-2013 Freescale Semiconductor, Inc. + * + * Author: Kai Jiang <Kai.Jiang@freescale.com> + * + * 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 <linux/kernel.h> +#include <linux/of_platform.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/uio_driver.h> +#include <linux/list.h> + +#define EPWISR 0x10010 /* Error/Port-Write Interrupt Status */ +#define IECSR 0x10130 /* Port Implementation Err Cmd & Status */ +#define ESCSR 0x00158 /* Port Error and Status Cmd & Status */ +#define EDCSR 0x00640 /* Port Error Detect Cmd & Status */ +#define LTLEDCSR 0x00608 /* Logical/Transport Layer Err Detect Cmd & Status */ +#define LTLEECSR 0x0060c /* Logical/Transport Layer Err Enable Cmd & Status */ +#define SRIO_ESCSR_CLEAR 0x07120204 +#define SRIO_IECSR_CLEAR 0x80000000 + +struct srio_uio_info { + atomic_t ref; + struct uio_info uio; + char name[20]; +}; + +struct srio_port_info { + struct device *dev; + struct srio_uio_info *info; + struct resource *res; + struct list_head list; + u32 port_id; +}; + +struct srio_regs_info { + struct device *dev; + struct srio_uio_info *info; + struct resource *res; + void __iomem *regs_win; +}; + +struct srio_dev { + struct device *dev; + struct srio_regs_info regs; + struct list_head port_list; + int irq; + u32 port_num; +}; + +enum srio_uio_init_type { + SRIO_REGS, + SRIO_PORT +}; + +static const char srio_uio_version[] = "SRIO UIO driver v1.0"; + +static int srio_uio_open(struct uio_info *info, struct inode *inode) +{ + struct srio_uio_info *i = container_of(info, struct srio_uio_info, uio); + + if (atomic_dec_return(&i->ref) < 0) { + pr_err("%s: failing open()\n", i->name); + atomic_inc(&i->ref); + return -EBUSY; + } + + return 0; +} + +static int srio_uio_release(struct uio_info *info, struct inode *inode) +{ + struct srio_uio_info *i = container_of(info, struct srio_uio_info, uio); + + atomic_inc(&i->ref); + + return 0; +} + +static irqreturn_t srio_uio_irq_handler(int irq, struct uio_info *dev_info) +{ + struct srio_dev *sriodev = dev_info->priv; + int i; + unsigned int port_bits, ltledcsr; + + ltledcsr = in_be32(sriodev->regs.regs_win + LTLEDCSR); + port_bits = in_be32(sriodev->regs.regs_win + EPWISR); + + if (!port_bits && !ltledcsr) + return IRQ_NONE; + + if (ltledcsr) + /* Disable logical/transport layer error interrupt */ + out_be32(sriodev->regs.regs_win + LTLEECSR, 0); + + for (i = 0; i < sriodev->port_num; i++) { + if (port_bits & (1 << (31 - i))) { + /* Clear retry error threshold exceeded */ + out_be32(sriodev->regs.regs_win + IECSR + 0x80 * i, + SRIO_IECSR_CLEAR); + /* Clear ESCSR */ + out_be32(sriodev->regs.regs_win + ESCSR + 0x20 * i, + SRIO_ESCSR_CLEAR); + /* Clear EDCSR */ + out_be32(sriodev->regs.regs_win + EDCSR + 0x40 * i, + 0); + } + } + + return IRQ_HANDLED; +} + +static int __init srio_uio_setup(struct srio_dev *sriodev, u8 type, u32 port_id) +{ + int err; + struct srio_uio_info *info; + struct srio_port_info *srio_port, *port_tmp; + + info = devm_kzalloc(sriodev->dev, sizeof(struct srio_uio_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + if (type == SRIO_REGS) { + atomic_set(&info->ref, sriodev->port_num); + sriodev->regs.info = info; + snprintf(info->name, sizeof(info->name)-1, "srio-uio-regs"); + info->uio.name = info->name; + info->uio.version = srio_uio_version; + info->uio.mem[0].name = "srio regs"; + info->uio.mem[0].addr = sriodev->regs.res->start; + info->uio.mem[0].size = sriodev->regs.res->end - + sriodev->regs.res->start + 1; + info->uio.mem[0].memtype = UIO_MEM_PHYS; + info->uio.irq = sriodev->irq; + + } else if (type == SRIO_PORT) { + err = -ENODEV; + atomic_set(&info->ref, 1); + list_for_each_entry_safe(srio_port, port_tmp, + &sriodev->port_list, list) { + + if (srio_port->port_id == port_id) { + srio_port->info = info; + snprintf(info->name, sizeof(info->name)-1, + "srio-uio-port%d", port_id); + info->uio.name = info->name; + info->uio.version = srio_uio_version; + info->uio.mem[0].name = "srio window"; + info->uio.mem[0].addr = srio_port->res->start; + info->uio.mem[0].size = srio_port->res->end - + srio_port->res->start + 1; + info->uio.mem[0].memtype = UIO_MEM_PHYS; + err = 0; + break; + } + + } + + if (err < 0) + return err; + } else + return -ENODEV; + + info->uio.irq_flags = IRQF_SHARED; + info->uio.handler = srio_uio_irq_handler; + info->uio.open = srio_uio_open; + info->uio.release = srio_uio_release; + info->uio.priv = sriodev; + + err = uio_register_device(sriodev->dev, &info->uio); + if (err) { + dev_err(sriodev->dev, "srio_uio: UIO registration failed\n"); + return err; + } + + return 0; +} + +static int srio_uio_init(struct srio_dev *sriodev) +{ + struct srio_port_info *srio_port, *port_tmp; + int err; + + srio_uio_setup(sriodev, SRIO_REGS, 0); + list_for_each_entry_safe(srio_port, port_tmp, + &sriodev->port_list, list) { + err = srio_uio_setup(sriodev, SRIO_PORT, srio_port->port_id); + if (err < 0) + return err; + } + + return 0; +} + +static int srio_uio_cleanup(struct srio_dev *sriodev) +{ + struct srio_port_info *srio_port, *port_tmp; + + list_for_each_entry_safe(srio_port, port_tmp, + &sriodev->port_list, list) { + uio_unregister_device(&srio_port->info->uio); + list_del(&srio_port->list); + } + + uio_unregister_device(&sriodev->regs.info->uio); + + return 0; +} + +static int fsl_srio_port_probe(struct srio_dev *srio_dev, + struct device_node *node) +{ + struct device_node *srio_node; + struct srio_port_info *srio_port; + const u32 *dt_range, *cell, *cell_index; + u64 law_start, law_size; + int paw, aw, sw; + + srio_node = node; + + cell_index = of_get_property(srio_node, "cell-index", NULL); + if (!cell_index) { + dev_err(srio_dev->dev, "Can't get %s property 'cell-index'\n", + srio_node->full_name); + return -ENODEV; + } + + dt_range = of_get_property(srio_node, "ranges", NULL); + if (!dt_range) { + dev_err(srio_dev->dev, "Can't get %s property 'ranges'\n", + srio_node->full_name); + return -ENODEV; + } + + /* Get node address wide */ + cell = of_get_property(srio_node, "#address-cells", NULL); + if (!cell) { + dev_err(srio_dev->dev, + "Can't get %s property '#address-cells'\n", + srio_node->full_name); + return -ENODEV; + } + aw = *cell; + + /* Get node size wide */ + cell = of_get_property(srio_node, "#size-cells", NULL); + if (!cell) { + dev_err(srio_dev->dev, "Can't get %s property '#size-cells'\n", + srio_node->full_name); + return -ENODEV; + } + sw = *cell; + + /* Get parent address wide wide */ + paw = of_n_addr_cells(srio_node); + law_start = of_read_number(dt_range + aw, paw); + law_size = of_read_number(dt_range + aw + paw, sw); + + srio_port = devm_kzalloc(srio_dev->dev, sizeof(struct srio_port_info), + GFP_KERNEL); + srio_port->res = devm_request_mem_region(srio_dev->dev, law_start, + law_size, "srio win"); + + if (srio_port->res == NULL) { + dev_err(srio_dev->dev, "devm_request_mem_region failed\n"); + return -ENOMEM; + } + + dev_dbg(srio_dev->dev, "window start 0x%016llx, size 0x%016llx.\n", + srio_port->res->start, srio_port->res->end + 1 - + srio_port->res->start); + + srio_port->port_id = *cell_index; + srio_port->dev = srio_dev->dev; + srio_dev->port_num++; + list_add_tail(&srio_port->list, &srio_dev->port_list); + + return 0; +} + +static void fsl_srio_port_remove(struct srio_dev *srio_dev) +{ + struct srio_port_info *srio_port, *port_tmp; + + list_for_each_entry_safe(srio_port, port_tmp, + &srio_dev->port_list, list) + list_del(&srio_port->list); +} + +static int fsl_srio_uio_probe(struct platform_device *dev) +{ + struct resource regs; + struct srio_dev *srio_dev; + struct device_node *srio_node; + struct device_node *child; + int err; + + srio_node = dev->dev.of_node; + dev_dbg(&dev->dev, "Of-device full name %s\n", srio_node->full_name); + + srio_dev = devm_kzalloc(&dev->dev, sizeof(struct srio_dev), GFP_KERNEL); + if (!srio_dev) { + dev_err(&dev->dev, "Can't alloc memory for 'srio_dev'\n"); + return -ENOMEM; + } + srio_dev->dev = &dev->dev; + dev_set_drvdata(&dev->dev, srio_dev); + + err = of_address_to_resource(srio_node, 0, ®s); + if (err < 0) { + dev_err(&dev->dev, "Can't get property 'reg'\n"); + return -EFAULT; + } + + srio_dev->regs.res = devm_request_mem_region(&dev->dev, regs.start, + regs.end + 1 - regs.start, "srio regs"); + if (srio_dev->regs.res == NULL) { + dev_err(&dev->dev, "devm_request_mem_region failed\n"); + return -ENOMEM; + } + + dev_dbg(&dev->dev, "reg start 0x%016llx, size 0x%016llx.\n", + srio_dev->regs.res->start, + srio_dev->regs.res->end + 1 - srio_dev->regs.res->start); + + srio_dev->regs.regs_win = devm_ioremap(&dev->dev, + srio_dev->regs.res->start, + srio_dev->regs.res->end - + srio_dev->regs.res->start + 1); + if (srio_dev->regs.regs_win == NULL) { + dev_err(&dev->dev, "devm_ioremap failed\n"); + return -EIO; + } + + srio_dev->irq = irq_of_parse_and_map(srio_node, 0); + dev_dbg(srio_dev->dev, "err irq: %d\n", srio_dev->irq); + + INIT_LIST_HEAD(&srio_dev->port_list); + + for_each_child_of_node(dev->dev.of_node, child) { + err = fsl_srio_port_probe(srio_dev, child); + if (err < 0) { + fsl_srio_port_remove(srio_dev); + return err; + } + } + + err = srio_uio_init(srio_dev); + if (err < 0) { + dev_err(srio_dev->dev, "srio_uio_init failed\n"); + srio_uio_cleanup(srio_dev); + return err; + } + + dev_info(srio_dev->dev, "Rapidio UIO driver initialized\n"); + + return 0; +} + +static int fsl_srio_uio_remove(struct platform_device *dev) +{ + struct srio_dev *srio_dev = platform_get_drvdata(dev); + + srio_uio_cleanup(srio_dev); + + return 0; +} + +static const struct of_device_id fsl_srio_uio_match[] = { + { + .compatible = "fsl,srio", + }, + {} +}; + +static struct platform_driver fsl_of_srio_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "fsl-of-srio", + .of_match_table = fsl_srio_uio_match, + }, + .probe = fsl_srio_uio_probe, + .remove = fsl_srio_uio_remove, +}; + +static __init int fsl_srio_uio_init(void) +{ + int err; + + err = platform_driver_register(&fsl_of_srio_driver); + if (err < 0) + pr_warn( + ": %s:%hu:%s(): platform_driver_register() = %d\n", + __FILE__, __LINE__, __func__, err); + + return err; +} + +static void __exit fsl_srio_uio_exit(void) +{ + platform_driver_unregister(&fsl_of_srio_driver); + + pr_warn("fsl srio uio driver removed\n"); +} + +module_init(fsl_srio_uio_init); +module_exit(fsl_srio_uio_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index b645c47..450c4e6 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -643,7 +643,11 @@ static int uio_mmap_physical(struct vm_area_struct *vma) if (mi < 0) return -EINVAL; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + if (idev->info->set_pgprot) + vma->vm_page_prot = idev->info->set_pgprot(idev->info, mi, + vma->vm_page_prot); + else + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); return remap_pfn_range(vma, vma->vm_start, |