/* * Copyright 2013 Freescale Semiconductor, Inc. * */ #include #include #include #include #include #include #include #include #include #include "regs.h" #include "fsl_jr_uio.h" static const char jr_uio_version[] = "fsl JR UIO driver v1.0"; #define NAME_LENGTH 30 #define JR_INDEX_OFFSET 12 static const char uio_device_name[] = "fsl-jr"; static LIST_HEAD(jr_list); struct jr_uio_info { atomic_t ref; /* exclusive, only one open() at a time */ struct uio_info uio; char name[NAME_LENGTH]; }; struct jr_dev { u32 revision; u32 index; u32 irq; struct caam_job_ring __iomem *global_regs; struct device *dev; struct resource *res; struct jr_uio_info info; struct list_head node; struct list_head jr_list; }; static int jr_uio_open(struct uio_info *info, struct inode *inode) { struct jr_uio_info *uio_info = container_of(info, struct jr_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 jr_uio_release(struct uio_info *info, struct inode *inode) { struct jr_uio_info *uio_info = container_of(info, struct jr_uio_info, uio); atomic_inc(&uio_info->ref); return 0; } static irqreturn_t jr_uio_irq_handler(int irq, struct uio_info *dev_info) { struct jr_dev *jrdev = dev_info->priv; u32 irqstate; irqstate = rd_reg32(&jrdev->global_regs->jrintstatus); if (!irqstate) { return IRQ_NONE; } if (irqstate & JRINT_JR_ERROR) dev_info(jrdev->dev, "uio job ring error - irqstate: %08x\n", irqstate); /*mask valid interrupts */ setbits32(&jrdev->global_regs->rconfig_lo, JRCFG_IMSK); /* Have valid interrupt at this point, just ACK and trigger */ wr_reg32(&jrdev->global_regs->jrintstatus, irqstate); return IRQ_HANDLED; } static int jr_uio_irqcontrol(struct uio_info *dev_info, int irqon) { struct jr_dev *jrdev = dev_info->priv; switch (irqon) { case SEC_UIO_SIMULATE_IRQ_CMD: uio_event_notify(dev_info); break; case SEC_UIO_ENABLE_IRQ_CMD: /* Enable Job Ring interrupt */ clrbits32(&jrdev->global_regs->rconfig_lo, JRCFG_IMSK); break; case SEC_UIO_DISABLE_IRQ_CMD: /* Disable Job Ring interrupt */ setbits32(&jrdev->global_regs->rconfig_lo, JRCFG_IMSK); break; default: break; } return 0; } static int __init jr_uio_init(struct jr_dev *uio_dev) { int ret; struct jr_uio_info *info; info = &uio_dev->info; atomic_set(&info->ref, 1); info->uio.version = jr_uio_version; info->uio.name = uio_dev->info.name; info->uio.mem[0].name = "JR 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 = jr_uio_irq_handler; info->uio.irqcontrol = jr_uio_irqcontrol; info->uio.open = jr_uio_open; info->uio.release = jr_uio_release; info->uio.priv = uio_dev; ret = uio_register_device(uio_dev->dev, &info->uio); if (ret) { pr_err("jr_uio: UIO registration failed\n"); return ret; } 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_jr_probe(struct platform_device *dev) { struct resource regs; struct jr_dev *jr_dev; struct device_node *jr_node; int ret, count = 0; struct list_head *p; jr_node = dev->dev.of_node; if (!jr_node) { dev_err(&dev->dev, "Device OF-Node is NULL\n"); return -EFAULT; } jr_dev = devm_kzalloc(&dev->dev, sizeof(struct jr_dev), GFP_KERNEL); if (!jr_dev) { dev_err(&dev->dev, "kzalloc failed\n"); return -ENOMEM; } /* Creat name and index */ list_for_each(p, &jr_list) { count++; } jr_dev->index = count; snprintf(jr_dev->info.name, sizeof(jr_dev->info.name) - 1, "%s%d", uio_device_name, jr_dev->index); jr_dev->dev = &dev->dev; platform_set_drvdata(dev, jr_dev); /* Get the resource from dtb node */ ret = of_address_to_resource(jr_node, 0, ®s); if (unlikely(ret < 0)) { dev_err(&dev->dev, "OF-Address-to-resource Failed\n"); ret = -EFAULT; goto abort; } jr_dev->res = devm_request_mem_region(&dev->dev, regs.start, regs.end-regs.start + 1, jr_dev->info.name); if (unlikely(!jr_dev->res)) { dev_err(jr_dev->dev, "devm_request_mem_region failed\n"); ret = -ENOMEM; goto abort; } jr_dev->global_regs = devm_ioremap(&dev->dev, jr_dev->res->start, jr_dev->res->end-jr_dev->res->start+1); if (unlikely(jr_dev->global_regs == 0)) { dev_err(jr_dev->dev, "devm_ioremap failed\n"); ret = -EIO; goto abort; } jr_dev->irq = irq_of_parse_and_map(jr_node, 0); dev_dbg(jr_dev->dev, "errirq: %d\n", jr_dev->irq); /* Register UIO */ ret = jr_uio_init(jr_dev); if (ret) { dev_err(&dev->dev, "UIO init Failed\n"); goto abort; } list_add_tail(&jr_dev->node, &jr_list); dev_info(jr_dev->dev, "UIO device full name %s initialized\n", jr_dev->info.name); return 0; abort: return ret; } static int fsl_jr_remove(struct platform_device *dev) { struct jr_dev *jr_dev = platform_get_drvdata(dev); if (!jr_dev) return 0; list_del(&jr_dev->node); uio_unregister_device(&jr_dev->info.uio); platform_set_drvdata(dev, NULL); return 0; } static struct platform_driver fsl_jr_driver = { .driver = { .name = "fsl-jr-uio", .owner = THIS_MODULE, .of_match_table = jr_ids, }, .probe = fsl_jr_probe, .remove = fsl_jr_remove, }; static __init int fsl_jr_init(void) { int ret; ret = platform_driver_register(&fsl_jr_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_jr_exit(void) { platform_driver_unregister(&fsl_jr_driver); } module_init(fsl_jr_init); module_exit(fsl_jr_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ruchika Gupta "); MODULE_DESCRIPTION("FSL SEC UIO Driver");