From: Niravkumar L Rabara <niravkumar.l.rabara@xxxxxxxxx> Altera hardware mutex soft IP provides hardware assistance for synchronization and mutual exclusion between processors in asymmetric/symmetric multiprocessing (AMP/SMP) system or multi processes/threads in uniprocessor system. Signed-off-by: Niravkumar L Rabara <niravkumar.l.rabara@xxxxxxxxx> --- .../bindings/misc/altera-hwmutex.yaml | 47 +++ drivers/misc/Kconfig | 6 + drivers/misc/Makefile | 1 + drivers/misc/altera_hwmutex.c | 321 ++++++++++++++++++ include/linux/altera_hwmutex.h | 42 +++ 5 files changed, 417 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/altera-hwmutex.yaml create mode 100644 drivers/misc/altera_hwmutex.c create mode 100644 include/linux/altera_hwmutex.h diff --git a/Documentation/devicetree/bindings/misc/altera-hwmutex.yaml b/Documentation/devicetree/bindings/misc/altera-hwmutex.yaml new file mode 100644 index 000000000000..57a9ea19c563 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/altera-hwmutex.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/misc/altera-hwmutex.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Altera hardware mutex + +maintainers: + - Niravkumar L Rabara <niravkumar.l.rabara@xxxxxxxxx> + +description: + Altera hardware mutex can provide hardware assistance for synchronization + and mutual exclusion between processors in asymmetric/symmetric multiprocessing + (AMP/SMP) system or multi processes/threads in uniprocessor system. + +properties: + compatible: + enum: + - altr,hwmutex-1.0 + - client-1.0 + + reg: + items: + - description: physical address of hw mutex and length of memory mapped + region + +additionalProperties: false + +required: + - compatible + - reg + +examples: + - | + mutex0: mutex0@100 { + compatible = "altr,hwmutex-1.0"; + reg = <0x100 0x8>; + }; + + + #Example of mutex's client node that includes mutex phandle + #mclient0: mclient0@200 { + # compatible = "client-1.0"; + # reg = <0x200 0x10>; + # mutex = <&mutex0>; + # }; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0f5a49fc7c9e..707acf740c6f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -435,6 +435,12 @@ config DW_XDATA_PCIE If unsure, say N. +config ALTERA_HWMUTEX + tristate "Altera Hardware Mutex" + help + This option enables device driver support for Altera Hardware Mutex. + Say Y here if you want to use the Altera hardware mutex support. + config PCI_ENDPOINT_TEST depends on PCI select CRC32 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a086197af544..6fcbbd36b3cf 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_PCH_PHUB) += pch_phub.o obj-y += ti-st/ obj-y += lis3lv02d/ obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ +obj-$(CONFIG_ALTERA_HWMUTEX) += altera_hwmutex.o obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o diff --git a/drivers/misc/altera_hwmutex.c b/drivers/misc/altera_hwmutex.c new file mode 100644 index 000000000000..45f98e4b13d0 --- /dev/null +++ b/drivers/misc/altera_hwmutex.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright Intel Corporation (C) 2022. 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("Niravkumar L Rabara <niravkumar.l.rabara@xxxxxxxxx>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Altera Hardware Mutex driver"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/altera_hwmutex.h b/include/linux/altera_hwmutex.h new file mode 100644 index 000000000000..18b73e9120bc --- /dev/null +++ b/include/linux/altera_hwmutex.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright Intel Corporation (C) 2022. 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/>. + */ +#ifndef _ALTERA_MUTEX_H +#define _ALTERA_MUTEX_H + +#include <linux/device.h> +#include <linux/platform_device.h> + +struct altera_mutex { + struct list_head list; + struct platform_device *pdev; + struct mutex lock; + void __iomem *regs; + bool requested; +}; + +extern struct altera_mutex *altera_mutex_request(struct device_node *mutex_np); +extern int altera_mutex_free(struct altera_mutex *mutex); + +extern int altera_mutex_lock(struct altera_mutex *mutex, u16 owner, u16 value); + +extern int altera_mutex_trylock(struct altera_mutex *mutex, u16 owner, + u16 value); +extern int altera_mutex_unlock(struct altera_mutex *mutex, u16 owner); +extern int altera_mutex_owned(struct altera_mutex *mutex, u16 owner); +extern int altera_mutex_is_locked(struct altera_mutex *mutex); + +#endif /* _ALTERA_MUTEX_H */ -- 2.25.1