Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) STMicroelectronics SA 2018
  4 * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
  5 */
  6
  7#include <linux/clk.h>
  8#include <linux/delay.h>
  9#include <linux/hwspinlock.h>
 10#include <linux/io.h>
 11#include <linux/kernel.h>
 12#include <linux/module.h>
 13#include <linux/of.h>
 14#include <linux/platform_device.h>
 15#include <linux/pm_runtime.h>
 16
 17#include "hwspinlock_internal.h"
 18
 19#define STM32_MUTEX_COREID	BIT(8)
 20#define STM32_MUTEX_LOCK_BIT	BIT(31)
 21#define STM32_MUTEX_NUM_LOCKS	32
 22
 23struct stm32_hwspinlock {
 24	struct clk *clk;
 25	struct hwspinlock_device bank;
 26};
 27
 28static int stm32_hwspinlock_trylock(struct hwspinlock *lock)
 29{
 30	void __iomem *lock_addr = lock->priv;
 31	u32 status;
 32
 33	writel(STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID, lock_addr);
 34	status = readl(lock_addr);
 35
 36	return status == (STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID);
 37}
 38
 39static void stm32_hwspinlock_unlock(struct hwspinlock *lock)
 40{
 41	void __iomem *lock_addr = lock->priv;
 42
 43	writel(STM32_MUTEX_COREID, lock_addr);
 44}
 45
 46static void stm32_hwspinlock_relax(struct hwspinlock *lock)
 47{
 48	ndelay(50);
 49}
 50
 51static const struct hwspinlock_ops stm32_hwspinlock_ops = {
 52	.trylock	= stm32_hwspinlock_trylock,
 53	.unlock		= stm32_hwspinlock_unlock,
 54	.relax		= stm32_hwspinlock_relax,
 55};
 56
 57static void stm32_hwspinlock_disable_clk(void *data)
 58{
 59	struct platform_device *pdev = data;
 60	struct stm32_hwspinlock *hw = platform_get_drvdata(pdev);
 61	struct device *dev = &pdev->dev;
 62
 63	pm_runtime_get_sync(dev);
 64	pm_runtime_disable(dev);
 65	pm_runtime_set_suspended(dev);
 66	pm_runtime_put_noidle(dev);
 67
 68	clk_disable_unprepare(hw->clk);
 69}
 70
 71static int stm32_hwspinlock_probe(struct platform_device *pdev)
 72{
 73	struct device *dev = &pdev->dev;
 74	struct stm32_hwspinlock *hw;
 75	void __iomem *io_base;
 
 
 76	int i, ret;
 77
 78	io_base = devm_platform_ioremap_resource(pdev, 0);
 
 79	if (IS_ERR(io_base))
 80		return PTR_ERR(io_base);
 81
 82	hw = devm_kzalloc(dev, struct_size(hw, bank.lock, STM32_MUTEX_NUM_LOCKS), GFP_KERNEL);
 
 83	if (!hw)
 84		return -ENOMEM;
 85
 86	hw->clk = devm_clk_get(dev, "hsem");
 87	if (IS_ERR(hw->clk))
 88		return PTR_ERR(hw->clk);
 89
 90	ret = clk_prepare_enable(hw->clk);
 91	if (ret) {
 92		dev_err(dev, "Failed to prepare_enable clock\n");
 93		return ret;
 94	}
 95
 96	platform_set_drvdata(pdev, hw);
 
 97
 98	pm_runtime_get_noresume(dev);
 99	pm_runtime_set_active(dev);
100	pm_runtime_enable(dev);
101	pm_runtime_put(dev);
102
103	ret = devm_add_action_or_reset(dev, stm32_hwspinlock_disable_clk, pdev);
104	if (ret) {
105		dev_err(dev, "Failed to register action\n");
106		return ret;
107	}
108
109	for (i = 0; i < STM32_MUTEX_NUM_LOCKS; i++)
110		hw->bank.lock[i].priv = io_base + i * sizeof(u32);
 
 
 
111
112	ret = devm_hwspin_lock_register(dev, &hw->bank, &stm32_hwspinlock_ops,
113					0, STM32_MUTEX_NUM_LOCKS);
 
 
114
 
115	if (ret)
116		dev_err(dev, "Failed to register hwspinlock\n");
117
118	return ret;
 
 
119}
120
121static int __maybe_unused stm32_hwspinlock_runtime_suspend(struct device *dev)
122{
123	struct stm32_hwspinlock *hw = dev_get_drvdata(dev);
124
125	clk_disable_unprepare(hw->clk);
126
127	return 0;
128}
129
130static int __maybe_unused stm32_hwspinlock_runtime_resume(struct device *dev)
131{
132	struct stm32_hwspinlock *hw = dev_get_drvdata(dev);
133
134	clk_prepare_enable(hw->clk);
135
136	return 0;
137}
138
139static const struct dev_pm_ops stm32_hwspinlock_pm_ops = {
140	SET_RUNTIME_PM_OPS(stm32_hwspinlock_runtime_suspend,
141			   stm32_hwspinlock_runtime_resume,
142			   NULL)
143};
144
145static const struct of_device_id stm32_hwpinlock_ids[] = {
146	{ .compatible = "st,stm32-hwspinlock", },
147	{},
148};
149MODULE_DEVICE_TABLE(of, stm32_hwpinlock_ids);
150
151static struct platform_driver stm32_hwspinlock_driver = {
152	.probe		= stm32_hwspinlock_probe,
 
153	.driver		= {
154		.name	= "stm32_hwspinlock",
155		.of_match_table = stm32_hwpinlock_ids,
156		.pm	= &stm32_hwspinlock_pm_ops,
157	},
158};
159
160static int __init stm32_hwspinlock_init(void)
161{
162	return platform_driver_register(&stm32_hwspinlock_driver);
163}
164/* board init code might need to reserve hwspinlocks for predefined purposes */
165postcore_initcall(stm32_hwspinlock_init);
166
167static void __exit stm32_hwspinlock_exit(void)
168{
169	platform_driver_unregister(&stm32_hwspinlock_driver);
170}
171module_exit(stm32_hwspinlock_exit);
172
173MODULE_LICENSE("GPL v2");
174MODULE_DESCRIPTION("Hardware spinlock driver for STM32 SoCs");
175MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
v5.4
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) STMicroelectronics SA 2018
  4 * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
  5 */
  6
  7#include <linux/clk.h>
  8#include <linux/delay.h>
  9#include <linux/hwspinlock.h>
 10#include <linux/io.h>
 11#include <linux/kernel.h>
 12#include <linux/module.h>
 13#include <linux/of.h>
 14#include <linux/platform_device.h>
 15#include <linux/pm_runtime.h>
 16
 17#include "hwspinlock_internal.h"
 18
 19#define STM32_MUTEX_COREID	BIT(8)
 20#define STM32_MUTEX_LOCK_BIT	BIT(31)
 21#define STM32_MUTEX_NUM_LOCKS	32
 22
 23struct stm32_hwspinlock {
 24	struct clk *clk;
 25	struct hwspinlock_device bank;
 26};
 27
 28static int stm32_hwspinlock_trylock(struct hwspinlock *lock)
 29{
 30	void __iomem *lock_addr = lock->priv;
 31	u32 status;
 32
 33	writel(STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID, lock_addr);
 34	status = readl(lock_addr);
 35
 36	return status == (STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID);
 37}
 38
 39static void stm32_hwspinlock_unlock(struct hwspinlock *lock)
 40{
 41	void __iomem *lock_addr = lock->priv;
 42
 43	writel(STM32_MUTEX_COREID, lock_addr);
 44}
 45
 46static void stm32_hwspinlock_relax(struct hwspinlock *lock)
 47{
 48	ndelay(50);
 49}
 50
 51static const struct hwspinlock_ops stm32_hwspinlock_ops = {
 52	.trylock	= stm32_hwspinlock_trylock,
 53	.unlock		= stm32_hwspinlock_unlock,
 54	.relax		= stm32_hwspinlock_relax,
 55};
 56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 57static int stm32_hwspinlock_probe(struct platform_device *pdev)
 58{
 
 59	struct stm32_hwspinlock *hw;
 60	void __iomem *io_base;
 61	struct resource *res;
 62	size_t array_size;
 63	int i, ret;
 64
 65	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 66	io_base = devm_ioremap_resource(&pdev->dev, res);
 67	if (IS_ERR(io_base))
 68		return PTR_ERR(io_base);
 69
 70	array_size = STM32_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);
 71	hw = devm_kzalloc(&pdev->dev, sizeof(*hw) + array_size, GFP_KERNEL);
 72	if (!hw)
 73		return -ENOMEM;
 74
 75	hw->clk = devm_clk_get(&pdev->dev, "hsem");
 76	if (IS_ERR(hw->clk))
 77		return PTR_ERR(hw->clk);
 78
 79	for (i = 0; i < STM32_MUTEX_NUM_LOCKS; i++)
 80		hw->bank.lock[i].priv = io_base + i * sizeof(u32);
 
 
 
 81
 82	platform_set_drvdata(pdev, hw);
 83	pm_runtime_enable(&pdev->dev);
 84
 85	ret = hwspin_lock_register(&hw->bank, &pdev->dev, &stm32_hwspinlock_ops,
 86				   0, STM32_MUTEX_NUM_LOCKS);
 
 
 
 
 
 
 
 
 87
 88	if (ret)
 89		pm_runtime_disable(&pdev->dev);
 90
 91	return ret;
 92}
 93
 94static int stm32_hwspinlock_remove(struct platform_device *pdev)
 95{
 96	struct stm32_hwspinlock *hw = platform_get_drvdata(pdev);
 97	int ret;
 98
 99	ret = hwspin_lock_unregister(&hw->bank);
100	if (ret)
101		dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
102
103	pm_runtime_disable(&pdev->dev);
104
105	return 0;
106}
107
108static int __maybe_unused stm32_hwspinlock_runtime_suspend(struct device *dev)
109{
110	struct stm32_hwspinlock *hw = dev_get_drvdata(dev);
111
112	clk_disable_unprepare(hw->clk);
113
114	return 0;
115}
116
117static int __maybe_unused stm32_hwspinlock_runtime_resume(struct device *dev)
118{
119	struct stm32_hwspinlock *hw = dev_get_drvdata(dev);
120
121	clk_prepare_enable(hw->clk);
122
123	return 0;
124}
125
126static const struct dev_pm_ops stm32_hwspinlock_pm_ops = {
127	SET_RUNTIME_PM_OPS(stm32_hwspinlock_runtime_suspend,
128			   stm32_hwspinlock_runtime_resume,
129			   NULL)
130};
131
132static const struct of_device_id stm32_hwpinlock_ids[] = {
133	{ .compatible = "st,stm32-hwspinlock", },
134	{},
135};
136MODULE_DEVICE_TABLE(of, stm32_hwpinlock_ids);
137
138static struct platform_driver stm32_hwspinlock_driver = {
139	.probe		= stm32_hwspinlock_probe,
140	.remove		= stm32_hwspinlock_remove,
141	.driver		= {
142		.name	= "stm32_hwspinlock",
143		.of_match_table = stm32_hwpinlock_ids,
144		.pm	= &stm32_hwspinlock_pm_ops,
145	},
146};
147
148static int __init stm32_hwspinlock_init(void)
149{
150	return platform_driver_register(&stm32_hwspinlock_driver);
151}
152/* board init code might need to reserve hwspinlocks for predefined purposes */
153postcore_initcall(stm32_hwspinlock_init);
154
155static void __exit stm32_hwspinlock_exit(void)
156{
157	platform_driver_unregister(&stm32_hwspinlock_driver);
158}
159module_exit(stm32_hwspinlock_exit);
160
161MODULE_LICENSE("GPL v2");
162MODULE_DESCRIPTION("Hardware spinlock driver for STM32 SoCs");
163MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");