blob: 5004fe92e94b445aee365595404769e2ea9dac6f [file] [log] [blame] [edit]
/*
* Copyright Altera Corporation (C) 2013. All rights reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/altera_hwmutex.h>
#define DRV_NAME "altera_hwmutex"
static DEFINE_SPINLOCK(list_lock); /* protect mutex_list */
static LIST_HEAD(mutex_list);
/* Mutex Registers */
#define MUTEX_REG 0x0
#define MUTEX_REG_VALUE_MASK 0xFFFF
#define MUTEX_REG_OWNER_OFFSET 16
#define MUTEX_REG_OWNER_MASK 0xFFFF
#define MUTEX_GET_OWNER(reg) \
((reg >> MUTEX_REG_OWNER_OFFSET) & MUTEX_REG_OWNER_MASK)
/**
* altera_mutex_request - Retrieves a pointer to an acquired mutex device
* structure
* @mutex_np: The pointer to mutex device node
*
* Returns a pointer to the mutex device structure associated with the
* supplied device node, or NULL if no corresponding mutex device was
* found.
*/
struct altera_mutex *altera_mutex_request(struct device_node *mutex_np)
{
struct altera_mutex *mutex;
spin_lock(&list_lock);
list_for_each_entry(mutex, &mutex_list, list) {
if (mutex_np == mutex->pdev->dev.of_node) {
if (!mutex->requested) {
mutex->requested = true;
spin_unlock(&list_lock);
return mutex;
} else {
pr_info("Mutex device is in use.\n");
spin_unlock(&list_lock);
return NULL;
}
}
}
spin_unlock(&list_lock);
pr_info("Mutex device not found!\n");
return NULL;
}
EXPORT_SYMBOL(altera_mutex_request);
/**
* altera_mutex_free - Free the mutex
* @mutex: the mutex
*
* Return 0 if success. Otherwise, returns non-zero.
*/
int altera_mutex_free(struct altera_mutex *mutex)
{
if (!mutex || !mutex->requested)
return -EINVAL;
spin_lock(&list_lock);
mutex->requested = false;
spin_unlock(&list_lock);
return 0;
}
EXPORT_SYMBOL(altera_mutex_free);
static int __mutex_trylock(struct altera_mutex *mutex, u16 owner, u16 value)
{
u32 read;
int ret = 0;
u32 data = (owner << MUTEX_REG_OWNER_OFFSET) | value;
mutex_lock(&mutex->lock);
__raw_writel(data, mutex->regs + MUTEX_REG);
read = __raw_readl(mutex->regs + MUTEX_REG);
if (read != data)
ret = -1;
mutex_unlock(&mutex->lock);
return ret;
}
/**
* altera_mutex_lock - Acquires a hardware mutex, wait until it can get it.
* @mutex: the mutex to be acquired
* @owner: owner ID
* @value: the new non-zero value to write to mutex
*
* Returns 0 if mutex was successfully locked. Otherwise, returns non-zero.
*
* The mutex must later on be released by the same owner that acquired it.
* This function is not ISR callable.
*/
int altera_mutex_lock(struct altera_mutex *mutex, u16 owner, u16 value)
{
if (!mutex || !mutex->requested)
return -EINVAL;
while (__mutex_trylock(mutex, owner, value) != 0)
;
return 0;
}
EXPORT_SYMBOL(altera_mutex_lock);
/**
* altera_mutex_trylock - Tries once to lock the hardware mutex and returns
* immediately
* @mutex: the mutex to be acquired
* @owner: owner ID
* @value: the new non-zero value to write to mutex
*
* Returns 0 if mutex was successfully locked. Otherwise, returns non-zero.
*
* The mutex must later on be released by the same owner that acquired it.
* This function is not ISR callable.
*/
int altera_mutex_trylock(struct altera_mutex *mutex, u16 owner, u16 value)
{
if (!mutex || !mutex->requested)
return -EINVAL;
return __mutex_trylock(mutex, owner, value);
}
EXPORT_SYMBOL(altera_mutex_trylock);
/**
* altera_mutex_unlock - Unlock a mutex that has been locked by this owner
* previously that was locked on the
* altera_mutex_lock. Upon release, the value stored
* in the mutex is set to zero.
* @mutex: the mutex to be released
* @owner: Owner ID
*
* Returns 0 if mutex was successfully unlocked. Otherwise, returns
* non-zero.
*
* This function is not ISR callable.
*/
int altera_mutex_unlock(struct altera_mutex *mutex, u16 owner)
{
u32 reg;
if (!mutex || !mutex->requested)
return -EINVAL;
mutex_lock(&mutex->lock);
__raw_writel(owner << MUTEX_REG_OWNER_OFFSET,
mutex->regs + MUTEX_REG);
reg = __raw_readl(mutex->regs + MUTEX_REG);
if (reg & MUTEX_REG_VALUE_MASK) {
/* Unlock failed */
dev_dbg(&mutex->pdev->dev,
"Unlock mutex failed, owner %d and expected owner %d\n",
owner, MUTEX_GET_OWNER(reg));
mutex_unlock(&mutex->lock);
return -EINVAL;
}
mutex_unlock(&mutex->lock);
return 0;
}
EXPORT_SYMBOL(altera_mutex_unlock);
/**
* altera_mutex_owned - Determines if this owner owns the mutex
* @mutex: the mutex to be queried
* @owner: Owner ID
*
* Returns 1 if the owner owns the mutex. Otherwise, returns zero.
*/
int altera_mutex_owned(struct altera_mutex *mutex, u16 owner)
{
u32 reg;
u16 actual_owner;
int ret = 0;
if (!mutex || !mutex->requested)
return ret;
mutex_lock(&mutex->lock);
reg = __raw_readl(mutex->regs + MUTEX_REG);
actual_owner = MUTEX_GET_OWNER(reg);
if (actual_owner == owner)
ret = 1;
mutex_unlock(&mutex->lock);
return ret;
}
EXPORT_SYMBOL(altera_mutex_owned);
/**
* altera_mutex_is_locked - Determines if the mutex is locked
* @mutex: the mutex to be queried
*
* Returns 1 if the mutex is locked, 0 if unlocked.
*/
int altera_mutex_is_locked(struct altera_mutex *mutex)
{
u32 reg;
int ret = 0;
if (!mutex || !mutex->requested)
return ret;
mutex_lock(&mutex->lock);
reg = __raw_readl(mutex->regs + MUTEX_REG);
reg &= MUTEX_REG_VALUE_MASK;
if (reg)
ret = 1;
mutex_unlock(&mutex->lock);
return ret;
}
EXPORT_SYMBOL(altera_mutex_is_locked);
static int altera_mutex_probe(struct platform_device *pdev)
{
struct altera_mutex *mutex;
struct resource *regs;
mutex = devm_kzalloc(&pdev->dev, sizeof(struct altera_mutex),
GFP_KERNEL);
if (!mutex)
return -ENOMEM;
mutex->pdev = pdev;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs)
return -ENXIO;
mutex->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(mutex->regs))
return PTR_ERR(mutex->regs);
mutex_init(&mutex->lock);
spin_lock(&list_lock);
list_add_tail(&mutex->list, &mutex_list);
spin_unlock(&list_lock);
platform_set_drvdata(pdev, mutex);
return 0;
}
static int altera_mutex_remove(struct platform_device *pdev)
{
struct altera_mutex *mutex = platform_get_drvdata(pdev);
spin_lock(&list_lock);
if (mutex)
list_del(&mutex->list);
spin_unlock(&list_lock);
platform_set_drvdata(pdev, NULL);
return 0;
}
static const struct of_device_id altera_mutex_match[] = {
{ .compatible = "altr,hwmutex-1.0" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, altera_mutex_match);
static struct platform_driver altera_mutex_platform_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = altera_mutex_match,
},
.remove = altera_mutex_remove,
};
static int __init altera_mutex_init(void)
{
return platform_driver_probe(&altera_mutex_platform_driver,
altera_mutex_probe);
}
static void __exit altera_mutex_exit(void)
{
platform_driver_unregister(&altera_mutex_platform_driver);
}
module_init(altera_mutex_init);
module_exit(altera_mutex_exit);
MODULE_AUTHOR("Ley Foon Tan <[email protected]>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Altera Hardware Mutex driver");
MODULE_ALIAS("platform:" DRV_NAME);