18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2018 48c2ecf20Sopenharmony_ci * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/hwspinlock.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "hwspinlock_internal.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define STM32_MUTEX_COREID BIT(8) 208c2ecf20Sopenharmony_ci#define STM32_MUTEX_LOCK_BIT BIT(31) 218c2ecf20Sopenharmony_ci#define STM32_MUTEX_NUM_LOCKS 32 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct stm32_hwspinlock { 248c2ecf20Sopenharmony_ci struct clk *clk; 258c2ecf20Sopenharmony_ci struct hwspinlock_device bank; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int stm32_hwspinlock_trylock(struct hwspinlock *lock) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci void __iomem *lock_addr = lock->priv; 318c2ecf20Sopenharmony_ci u32 status; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci writel(STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID, lock_addr); 348c2ecf20Sopenharmony_ci status = readl(lock_addr); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci return status == (STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void stm32_hwspinlock_unlock(struct hwspinlock *lock) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci void __iomem *lock_addr = lock->priv; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci writel(STM32_MUTEX_COREID, lock_addr); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void stm32_hwspinlock_relax(struct hwspinlock *lock) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci ndelay(50); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic const struct hwspinlock_ops stm32_hwspinlock_ops = { 528c2ecf20Sopenharmony_ci .trylock = stm32_hwspinlock_trylock, 538c2ecf20Sopenharmony_ci .unlock = stm32_hwspinlock_unlock, 548c2ecf20Sopenharmony_ci .relax = stm32_hwspinlock_relax, 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int stm32_hwspinlock_probe(struct platform_device *pdev) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct stm32_hwspinlock *hw; 608c2ecf20Sopenharmony_ci void __iomem *io_base; 618c2ecf20Sopenharmony_ci size_t array_size; 628c2ecf20Sopenharmony_ci int i, ret; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci io_base = devm_platform_ioremap_resource(pdev, 0); 658c2ecf20Sopenharmony_ci if (IS_ERR(io_base)) 668c2ecf20Sopenharmony_ci return PTR_ERR(io_base); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci array_size = STM32_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock); 698c2ecf20Sopenharmony_ci hw = devm_kzalloc(&pdev->dev, sizeof(*hw) + array_size, GFP_KERNEL); 708c2ecf20Sopenharmony_ci if (!hw) 718c2ecf20Sopenharmony_ci return -ENOMEM; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci hw->clk = devm_clk_get(&pdev->dev, "hsem"); 748c2ecf20Sopenharmony_ci if (IS_ERR(hw->clk)) 758c2ecf20Sopenharmony_ci return PTR_ERR(hw->clk); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci for (i = 0; i < STM32_MUTEX_NUM_LOCKS; i++) 788c2ecf20Sopenharmony_ci hw->bank.lock[i].priv = io_base + i * sizeof(u32); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, hw); 818c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci ret = hwspin_lock_register(&hw->bank, &pdev->dev, &stm32_hwspinlock_ops, 848c2ecf20Sopenharmony_ci 0, STM32_MUTEX_NUM_LOCKS); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (ret) 878c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return ret; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int stm32_hwspinlock_remove(struct platform_device *pdev) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct stm32_hwspinlock *hw = platform_get_drvdata(pdev); 958c2ecf20Sopenharmony_ci int ret; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci ret = hwspin_lock_unregister(&hw->bank); 988c2ecf20Sopenharmony_ci if (ret) 998c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int __maybe_unused stm32_hwspinlock_runtime_suspend(struct device *dev) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct stm32_hwspinlock *hw = dev_get_drvdata(dev); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci clk_disable_unprepare(hw->clk); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int __maybe_unused stm32_hwspinlock_runtime_resume(struct device *dev) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct stm32_hwspinlock *hw = dev_get_drvdata(dev); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci clk_prepare_enable(hw->clk); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic const struct dev_pm_ops stm32_hwspinlock_pm_ops = { 1258c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(stm32_hwspinlock_runtime_suspend, 1268c2ecf20Sopenharmony_ci stm32_hwspinlock_runtime_resume, 1278c2ecf20Sopenharmony_ci NULL) 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic const struct of_device_id stm32_hwpinlock_ids[] = { 1318c2ecf20Sopenharmony_ci { .compatible = "st,stm32-hwspinlock", }, 1328c2ecf20Sopenharmony_ci {}, 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, stm32_hwpinlock_ids); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic struct platform_driver stm32_hwspinlock_driver = { 1378c2ecf20Sopenharmony_ci .probe = stm32_hwspinlock_probe, 1388c2ecf20Sopenharmony_ci .remove = stm32_hwspinlock_remove, 1398c2ecf20Sopenharmony_ci .driver = { 1408c2ecf20Sopenharmony_ci .name = "stm32_hwspinlock", 1418c2ecf20Sopenharmony_ci .of_match_table = stm32_hwpinlock_ids, 1428c2ecf20Sopenharmony_ci .pm = &stm32_hwspinlock_pm_ops, 1438c2ecf20Sopenharmony_ci }, 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int __init stm32_hwspinlock_init(void) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci return platform_driver_register(&stm32_hwspinlock_driver); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci/* board init code might need to reserve hwspinlocks for predefined purposes */ 1518c2ecf20Sopenharmony_cipostcore_initcall(stm32_hwspinlock_init); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void __exit stm32_hwspinlock_exit(void) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci platform_driver_unregister(&stm32_hwspinlock_driver); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_cimodule_exit(stm32_hwspinlock_exit); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Hardware spinlock driver for STM32 SoCs"); 1618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); 162