/* * Copyright (C) 2014 Freescale Semiconductor, Inc. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ #include #include #include #include #include #include #include #include #include #include static struct imx_sema4_mutex_device *imx6_sema4; /*! * \brief mutex create function. * * This function allocates imx_sema4_mutex structure and returns a handle * to it. The mutex to be created is identified by SEMA4 device number and mutex * (gate) number. The handle is used to reference the created mutex in calls to * other imx_sema4_mutex API functions. This function is to be called only * once for each mutex. * * \param[in] dev_num SEMA4 device (module) number. * \param[in] mutex_num Mutex (gate) number. * * \return NULL (Failure.) * \return imx_sema4_mutex (Success.) */ struct imx_sema4_mutex * imx_sema4_mutex_create(u32 dev_num, u32 mutex_num) { struct imx_sema4_mutex *mutex_ptr = NULL; if (mutex_num >= SEMA4_NUM_GATES || dev_num >= SEMA4_NUM_DEVICES) goto out; if (imx6_sema4->cpine_val & (1 < mutex_num)) { pr_err("Error: requiring a allocated sema4.\n"); pr_err("mutex_num %d cpine_val 0x%08x.\n", mutex_num, imx6_sema4->cpine_val); } mutex_ptr = kzalloc(sizeof(*mutex_ptr), GFP_KERNEL); if (!mutex_ptr) goto out; imx6_sema4->mutex_ptr[mutex_num] = mutex_ptr; imx6_sema4->alloced |= 1 < mutex_num; imx6_sema4->cpine_val |= idx_sema4[mutex_num]; writew(imx6_sema4->cpine_val, imx6_sema4->ioaddr + SEMA4_CP0INE); mutex_ptr->valid = CORE_MUTEX_VALID; mutex_ptr->gate_num = mutex_num; init_waitqueue_head(&mutex_ptr->wait_q); out: return mutex_ptr; } EXPORT_SYMBOL(imx_sema4_mutex_create); /*! * \brief mutex destroy function. * * This function destroys a mutex. * * \param[in] mutex_ptr Pointer to mutex structure. * * \return MQX_COMPONENT_DOES_NOT_EXIST (mutex component not installed.) * \return MQX_INVALID_PARAMETER (Wrong input parameter.) * \return COREMUTEX_OK (Success.) * */ int imx_sema4_mutex_destroy(struct imx_sema4_mutex *mutex_ptr) { u32 mutex_num; if ((mutex_ptr == NULL) || (mutex_ptr->valid != CORE_MUTEX_VALID)) return -EINVAL; mutex_num = mutex_ptr->gate_num; if ((imx6_sema4->cpine_val & idx_sema4[mutex_num]) == 0) { pr_err("Error: trying to destroy a un-allocated sema4.\n"); pr_err("mutex_num %d cpine_val 0x%08x.\n", mutex_num, imx6_sema4->cpine_val); } imx6_sema4->mutex_ptr[mutex_num] = NULL; imx6_sema4->alloced &= ~(1 << mutex_num); imx6_sema4->cpine_val &= ~(idx_sema4[mutex_num]); writew(imx6_sema4->cpine_val, imx6_sema4->ioaddr + SEMA4_CP0INE); kfree(mutex_ptr); return 0; } EXPORT_SYMBOL(imx_sema4_mutex_destroy); /*! * \brief Lock the mutex, shouldn't be interruted by INT. * * This function attempts to lock a mutex. If the mutex is already locked * by another task the function return -EBUSY, and tell invoker wait until * it is possible to lock the mutex. * * \param[in] mutex_ptr Pointer to mutex structure. * * \return MQX_INVALID_POINTER (Wrong pointer to the mutex structure provided.) * \return COREMUTEX_OK (mutex successfully locked.) * * \see imx_sema4_mutex_unlock */ int _imx_sema4_mutex_lock(struct imx_sema4_mutex *mutex_ptr) { int ret = 0, i = 0; if ((mutex_ptr == NULL) || (mutex_ptr->valid != CORE_MUTEX_VALID)) return -EINVAL; i = mutex_ptr->gate_num; mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i); mutex_ptr->gate_val &= SEMA4_GATE_MASK; /* Check to see if this core already own it */ if (mutex_ptr->gate_val == SEMA4_A9_LOCK) { /* return -EBUSY, invoker should be in sleep, and re-lock ag */ pr_err("%s -> %s %d already locked, wait! num %d val %d.\n", __FILE__, __func__, __LINE__, i, mutex_ptr->gate_val); ret = -EBUSY; goto out; } else { /* try to lock the mutex */ mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i); mutex_ptr->gate_val &= (~SEMA4_GATE_MASK); mutex_ptr->gate_val |= SEMA4_A9_LOCK; writeb(mutex_ptr->gate_val, imx6_sema4->ioaddr + i); mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i); mutex_ptr->gate_val &= SEMA4_GATE_MASK; /* double check the mutex is locked, otherwise, return -EBUSY */ if (mutex_ptr->gate_val != SEMA4_A9_LOCK) { pr_debug("wait-locked num %d val %d.\n", i, mutex_ptr->gate_val); ret = -EBUSY; } } out: return ret; } /* ! * \brief Try to lock the core mutex. * * This function attempts to lock a mutex. If the mutex is successfully locked * for the calling task, SEMA4_A9_LOCK is returned. If the mutex is already * locked by another task, the function does not block but rather returns * negative immediately. * * \param[in] mutex_ptr Pointer to core_mutex structure. * * \return SEMA4_A9_LOCK (mutex successfully locked.) * \return negative (mutex not locked.) * */ int imx_sema4_mutex_trylock(struct imx_sema4_mutex *mutex_ptr) { int ret = 0; ret = _imx_sema4_mutex_lock(mutex_ptr); if (ret == 0) return SEMA4_A9_LOCK; else return ret; } EXPORT_SYMBOL(imx_sema4_mutex_trylock); /*! * \brief Invoke _imx_sema4_mutex_lock to lock the mutex. * * This function attempts to lock a mutex. If the mutex is already locked * by another task the function, sleep itself and schedule out. * Wait until it is possible to lock the mutex. * * Invoker should add its own wait queue into the wait queue header of the * required semaphore, set TASK_INTERRUPTIBLE and sleep on itself by * schedule() when the lock is failed. Re-try to lock the semaphore when * it is woke up by the sema4 isr. * * \param[in] mutex_ptr Pointer to mutex structure. * * \return SEMA4_A9_LOCK (mutex successfully locked.) * * \see imx_sema4_mutex_unlock */ int imx_sema4_mutex_lock(struct imx_sema4_mutex *mutex_ptr) { int ret = 0; unsigned long flags; spin_lock_irqsave(&imx6_sema4->lock, flags); ret = _imx_sema4_mutex_lock(mutex_ptr); spin_unlock_irqrestore(&imx6_sema4->lock, flags); while (-EBUSY == ret) { spin_lock_irqsave(&imx6_sema4->lock, flags); ret = _imx_sema4_mutex_lock(mutex_ptr); spin_unlock_irqrestore(&imx6_sema4->lock, flags); if (ret == 0) break; } return ret; } EXPORT_SYMBOL(imx_sema4_mutex_lock); /*! * \brief Unlock the mutex. * * This function unlocks the specified mutex. * * \param[in] mutex_ptr Pointer to mutex structure. * * \return -EINVAL (Wrong pointer to the mutex structure provided.) * \return -EINVAL (This mutex has not been locked by this core.) * \return 0 (mutex successfully unlocked.) * * \see imx_sema4_mutex_lock */ int imx_sema4_mutex_unlock(struct imx_sema4_mutex *mutex_ptr) { int ret = 0, i = 0; if ((mutex_ptr == NULL) || (mutex_ptr->valid != CORE_MUTEX_VALID)) return -EINVAL; i = mutex_ptr->gate_num; mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i); mutex_ptr->gate_val &= SEMA4_GATE_MASK; /* make sure it is locked by this core */ if (mutex_ptr->gate_val != SEMA4_A9_LOCK) { pr_err("%d Trying to unlock an unlock mutex.\n", __LINE__); ret = -EINVAL; goto out; } /* unlock it */ mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i); mutex_ptr->gate_val &= (~SEMA4_GATE_MASK); writeb(mutex_ptr->gate_val | SEMA4_UNLOCK, imx6_sema4->ioaddr + i); mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i); mutex_ptr->gate_val &= SEMA4_GATE_MASK; /* make sure it is locked by this core */ if (mutex_ptr->gate_val == SEMA4_A9_LOCK) pr_err("%d ERROR, failed to unlock the mutex.\n", __LINE__); out: return ret; } EXPORT_SYMBOL(imx_sema4_mutex_unlock); /* * isr used by SEMA4, wake up the sleep tasks if there are the tasks waiting * for locking semaphore. * FIXME the bits order of the gatn, cpnie, cpnntf are not exact identified yet! */ static irqreturn_t imx_sema4_isr(int irq, void *dev_id) { int i; struct imx_sema4_mutex *mutex_ptr; unsigned int mask; struct imx_sema4_mutex_device *imx6_sema4 = dev_id; imx6_sema4->cpntf_val = readw(imx6_sema4->ioaddr + SEMA4_CP0NTF); for (i = 0; i < SEMA4_NUM_GATES; i++) { mask = idx_sema4[i]; if ((imx6_sema4->cpntf_val) & mask) { mutex_ptr = imx6_sema4->mutex_ptr[i]; /* * An interrupt is pending on this mutex, the only way * to clear it is to lock it (either by this core or * another). */ mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i); mutex_ptr->gate_val &= (~SEMA4_GATE_MASK); mutex_ptr->gate_val |= SEMA4_A9_LOCK; writeb(mutex_ptr->gate_val, imx6_sema4->ioaddr + i); mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i); mutex_ptr->gate_val &= SEMA4_GATE_MASK; if (mutex_ptr->gate_val == SEMA4_A9_LOCK) { /* * wake up the wait queue, whatever there * are wait task or not. * NOTE: check gate is locted or not in * sema4_lock func by wait task. */ mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i); mutex_ptr->gate_val &= (~SEMA4_GATE_MASK); mutex_ptr->gate_val |= SEMA4_UNLOCK; writeb(mutex_ptr->gate_val, imx6_sema4->ioaddr + i); wake_up(&mutex_ptr->wait_q); } else { pr_debug("can't lock gate%d %s retry!\n", i, mutex_ptr->gate_val ? "locked by m4" : ""); } } } return IRQ_HANDLED; } static const struct of_device_id imx_sema4_dt_ids[] = { { .compatible = "fsl,imx6sx-sema4", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_sema4_dt_ids); static int imx_sema4_probe(struct platform_device *pdev) { struct resource *res; int ret; imx6_sema4 = devm_kzalloc(&pdev->dev, sizeof(*imx6_sema4), GFP_KERNEL); if (!imx6_sema4) return -ENOMEM; imx6_sema4->dev = &pdev->dev; imx6_sema4->cpine_val = 0; spin_lock_init(&imx6_sema4->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (IS_ERR(res)) { dev_err(&pdev->dev, "unable to get imx sema4 resource 0\n"); ret = -ENODEV; goto err; } imx6_sema4->ioaddr = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(imx6_sema4->ioaddr)) { ret = PTR_ERR(imx6_sema4->ioaddr); goto err; } imx6_sema4->irq = platform_get_irq(pdev, 0); if (!imx6_sema4->irq) { dev_err(&pdev->dev, "failed to get irq\n"); ret = -ENODEV; goto err; } ret = devm_request_irq(&pdev->dev, imx6_sema4->irq, imx_sema4_isr, IRQF_SHARED, "imx6sx-sema4", imx6_sema4); if (ret) { dev_err(&pdev->dev, "failed to request imx sema4 irq\n"); ret = -ENODEV; goto err; } platform_set_drvdata(pdev, imx6_sema4); err: return ret; } static int imx_sema4_remove(struct platform_device *pdev) { return 0; } static struct platform_driver imx_sema4_driver = { .driver = { .owner = THIS_MODULE, .name = "imx-sema4", .of_match_table = imx_sema4_dt_ids, }, .probe = imx_sema4_probe, .remove = imx_sema4_remove, }; static int __init imx_sema4_init(void) { int ret; ret = platform_driver_register(&imx_sema4_driver); if (ret) pr_err("Unable to initialize sema4 driver\n"); else pr_info("imx sema4 driver is registered.\n"); return ret; } static void __exit imx_sema4_exit(void) { pr_info("imx sema4 driver is unregistered.\n"); platform_driver_unregister(&imx_sema4_driver); } module_exit(imx_sema4_exit); module_init(imx_sema4_init); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("IMX SEMA4 driver"); MODULE_LICENSE("GPL");