Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) STMicroelectronics SA 2018
0004  * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
0005  */
0006 
0007 #include <linux/clk.h>
0008 #include <linux/delay.h>
0009 #include <linux/hwspinlock.h>
0010 #include <linux/io.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/of.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/pm_runtime.h>
0016 
0017 #include "hwspinlock_internal.h"
0018 
0019 #define STM32_MUTEX_COREID  BIT(8)
0020 #define STM32_MUTEX_LOCK_BIT    BIT(31)
0021 #define STM32_MUTEX_NUM_LOCKS   32
0022 
0023 struct stm32_hwspinlock {
0024     struct clk *clk;
0025     struct hwspinlock_device bank;
0026 };
0027 
0028 static int stm32_hwspinlock_trylock(struct hwspinlock *lock)
0029 {
0030     void __iomem *lock_addr = lock->priv;
0031     u32 status;
0032 
0033     writel(STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID, lock_addr);
0034     status = readl(lock_addr);
0035 
0036     return status == (STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID);
0037 }
0038 
0039 static void stm32_hwspinlock_unlock(struct hwspinlock *lock)
0040 {
0041     void __iomem *lock_addr = lock->priv;
0042 
0043     writel(STM32_MUTEX_COREID, lock_addr);
0044 }
0045 
0046 static void stm32_hwspinlock_relax(struct hwspinlock *lock)
0047 {
0048     ndelay(50);
0049 }
0050 
0051 static const struct hwspinlock_ops stm32_hwspinlock_ops = {
0052     .trylock    = stm32_hwspinlock_trylock,
0053     .unlock     = stm32_hwspinlock_unlock,
0054     .relax      = stm32_hwspinlock_relax,
0055 };
0056 
0057 static void stm32_hwspinlock_disable_clk(void *data)
0058 {
0059     struct platform_device *pdev = data;
0060     struct stm32_hwspinlock *hw = platform_get_drvdata(pdev);
0061     struct device *dev = &pdev->dev;
0062 
0063     pm_runtime_get_sync(dev);
0064     pm_runtime_disable(dev);
0065     pm_runtime_set_suspended(dev);
0066     pm_runtime_put_noidle(dev);
0067 
0068     clk_disable_unprepare(hw->clk);
0069 }
0070 
0071 static int stm32_hwspinlock_probe(struct platform_device *pdev)
0072 {
0073     struct device *dev = &pdev->dev;
0074     struct stm32_hwspinlock *hw;
0075     void __iomem *io_base;
0076     int i, ret;
0077 
0078     io_base = devm_platform_ioremap_resource(pdev, 0);
0079     if (IS_ERR(io_base))
0080         return PTR_ERR(io_base);
0081 
0082     hw = devm_kzalloc(dev, struct_size(hw, bank.lock, STM32_MUTEX_NUM_LOCKS), GFP_KERNEL);
0083     if (!hw)
0084         return -ENOMEM;
0085 
0086     hw->clk = devm_clk_get(dev, "hsem");
0087     if (IS_ERR(hw->clk))
0088         return PTR_ERR(hw->clk);
0089 
0090     ret = clk_prepare_enable(hw->clk);
0091     if (ret) {
0092         dev_err(dev, "Failed to prepare_enable clock\n");
0093         return ret;
0094     }
0095 
0096     platform_set_drvdata(pdev, hw);
0097 
0098     pm_runtime_get_noresume(dev);
0099     pm_runtime_set_active(dev);
0100     pm_runtime_enable(dev);
0101     pm_runtime_put(dev);
0102 
0103     ret = devm_add_action_or_reset(dev, stm32_hwspinlock_disable_clk, pdev);
0104     if (ret) {
0105         dev_err(dev, "Failed to register action\n");
0106         return ret;
0107     }
0108 
0109     for (i = 0; i < STM32_MUTEX_NUM_LOCKS; i++)
0110         hw->bank.lock[i].priv = io_base + i * sizeof(u32);
0111 
0112     ret = devm_hwspin_lock_register(dev, &hw->bank, &stm32_hwspinlock_ops,
0113                     0, STM32_MUTEX_NUM_LOCKS);
0114 
0115     if (ret)
0116         dev_err(dev, "Failed to register hwspinlock\n");
0117 
0118     return ret;
0119 }
0120 
0121 static int __maybe_unused stm32_hwspinlock_runtime_suspend(struct device *dev)
0122 {
0123     struct stm32_hwspinlock *hw = dev_get_drvdata(dev);
0124 
0125     clk_disable_unprepare(hw->clk);
0126 
0127     return 0;
0128 }
0129 
0130 static int __maybe_unused stm32_hwspinlock_runtime_resume(struct device *dev)
0131 {
0132     struct stm32_hwspinlock *hw = dev_get_drvdata(dev);
0133 
0134     clk_prepare_enable(hw->clk);
0135 
0136     return 0;
0137 }
0138 
0139 static const struct dev_pm_ops stm32_hwspinlock_pm_ops = {
0140     SET_RUNTIME_PM_OPS(stm32_hwspinlock_runtime_suspend,
0141                stm32_hwspinlock_runtime_resume,
0142                NULL)
0143 };
0144 
0145 static const struct of_device_id stm32_hwpinlock_ids[] = {
0146     { .compatible = "st,stm32-hwspinlock", },
0147     {},
0148 };
0149 MODULE_DEVICE_TABLE(of, stm32_hwpinlock_ids);
0150 
0151 static struct platform_driver stm32_hwspinlock_driver = {
0152     .probe      = stm32_hwspinlock_probe,
0153     .driver     = {
0154         .name   = "stm32_hwspinlock",
0155         .of_match_table = stm32_hwpinlock_ids,
0156         .pm = &stm32_hwspinlock_pm_ops,
0157     },
0158 };
0159 
0160 static int __init stm32_hwspinlock_init(void)
0161 {
0162     return platform_driver_register(&stm32_hwspinlock_driver);
0163 }
0164 /* board init code might need to reserve hwspinlocks for predefined purposes */
0165 postcore_initcall(stm32_hwspinlock_init);
0166 
0167 static void __exit stm32_hwspinlock_exit(void)
0168 {
0169     platform_driver_unregister(&stm32_hwspinlock_driver);
0170 }
0171 module_exit(stm32_hwspinlock_exit);
0172 
0173 MODULE_LICENSE("GPL v2");
0174 MODULE_DESCRIPTION("Hardware spinlock driver for STM32 SoCs");
0175 MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");