Skip to content

Commit b5e6d12

Browse files
committed
Merge tag 'hwlock-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc
Pull hwspinlock updates from Bjorn Andersson: "This adds a driver for the hardware spinlock in Allwinner sun6i" * tag 'hwlock-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc: dt-bindings: hwlock: sun6i: Fix various warnings in binding hwspinlock: add sun6i hardware spinlock support dt-bindings: hwlock: add sun6i_hwspinlock
2 parents d0fe3f4 + 234462b commit b5e6d12

File tree

5 files changed

+274
-0
lines changed

5 files changed

+274
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
2+
%YAML 1.2
3+
---
4+
$id: http://devicetree.org/schemas/hwlock/allwinner,sun6i-a31-hwspinlock.yaml#
5+
$schema: http://devicetree.org/meta-schemas/core.yaml#
6+
7+
title: SUN6I hardware spinlock driver for Allwinner sun6i compatible SoCs
8+
9+
maintainers:
10+
- Wilken Gottwalt <[email protected]>
11+
12+
description:
13+
The hardware unit provides semaphores between the ARM cores and the embedded
14+
companion core on the SoC.
15+
16+
properties:
17+
compatible:
18+
const: allwinner,sun6i-a31-hwspinlock
19+
20+
reg:
21+
maxItems: 1
22+
23+
clocks:
24+
maxItems: 1
25+
26+
resets:
27+
maxItems: 1
28+
29+
required:
30+
- compatible
31+
- reg
32+
- clocks
33+
- resets
34+
35+
additionalProperties: false
36+
37+
examples:
38+
- |
39+
#include <dt-bindings/clock/sun8i-a23-a33-ccu.h>
40+
#include <dt-bindings/reset/sun8i-a23-a33-ccu.h>
41+
42+
hwlock@1c18000 {
43+
compatible = "allwinner,sun6i-a31-hwspinlock";
44+
reg = <0x01c18000 0x1000>;
45+
clocks = <&ccu CLK_BUS_SPINLOCK>;
46+
resets = <&ccu RST_BUS_SPINLOCK>;
47+
};
48+
...

MAINTAINERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,12 @@ L: [email protected]
752752
S: Maintained
753753
F: drivers/crypto/allwinner/
754754

755+
ALLWINNER HARDWARE SPINLOCK SUPPORT
756+
M: Wilken Gottwalt <[email protected]>
757+
S: Maintained
758+
F: Documentation/devicetree/bindings/hwlock/allwinner,sun6i-hwspinlock.yaml
759+
F: drivers/hwspinlock/sun6i_hwspinlock.c
760+
755761
ALLWINNER THERMAL DRIVER
756762
M: Vasily Khoruzhick <[email protected]>
757763
M: Yangtao Li <[email protected]>

drivers/hwspinlock/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ config HWSPINLOCK_STM32
4444

4545
If unsure, say N.
4646

47+
config HWSPINLOCK_SUN6I
48+
tristate "SUN6I Hardware Spinlock device"
49+
depends on ARCH_SUNXI || COMPILE_TEST
50+
help
51+
Say y here to support the SUN6I Hardware Spinlock device which can be
52+
found in most of the sun6i compatible Allwinner SoCs.
53+
54+
If unsure, say N.
55+
4756
config HSEM_U8500
4857
tristate "STE Hardware Semaphore functionality"
4958
depends on ARCH_U8500 || COMPILE_TEST

drivers/hwspinlock/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o
88
obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o
99
obj-$(CONFIG_HWSPINLOCK_SPRD) += sprd_hwspinlock.o
1010
obj-$(CONFIG_HWSPINLOCK_STM32) += stm32_hwspinlock.o
11+
obj-$(CONFIG_HWSPINLOCK_SUN6I) += sun6i_hwspinlock.o
1112
obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o

drivers/hwspinlock/sun6i_hwspinlock.c

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* sun6i_hwspinlock.c - hardware spinlock driver for sun6i compatible Allwinner SoCs
4+
* Copyright (C) 2020 Wilken Gottwalt <[email protected]>
5+
*/
6+
7+
#include <linux/clk.h>
8+
#include <linux/debugfs.h>
9+
#include <linux/errno.h>
10+
#include <linux/hwspinlock.h>
11+
#include <linux/io.h>
12+
#include <linux/module.h>
13+
#include <linux/of.h>
14+
#include <linux/platform_device.h>
15+
#include <linux/reset.h>
16+
#include <linux/slab.h>
17+
#include <linux/spinlock.h>
18+
#include <linux/types.h>
19+
20+
#include "hwspinlock_internal.h"
21+
22+
#define DRIVER_NAME "sun6i_hwspinlock"
23+
24+
#define SPINLOCK_BASE_ID 0 /* there is only one hwspinlock device per SoC */
25+
#define SPINLOCK_SYSSTATUS_REG 0x0000
26+
#define SPINLOCK_LOCK_REGN 0x0100
27+
#define SPINLOCK_NOTTAKEN 0
28+
29+
struct sun6i_hwspinlock_data {
30+
struct hwspinlock_device *bank;
31+
struct reset_control *reset;
32+
struct clk *ahb_clk;
33+
struct dentry *debugfs;
34+
int nlocks;
35+
};
36+
37+
#ifdef CONFIG_DEBUG_FS
38+
39+
static int hwlocks_supported_show(struct seq_file *seqf, void *unused)
40+
{
41+
struct sun6i_hwspinlock_data *priv = seqf->private;
42+
43+
seq_printf(seqf, "%d\n", priv->nlocks);
44+
45+
return 0;
46+
}
47+
DEFINE_SHOW_ATTRIBUTE(hwlocks_supported);
48+
49+
static void sun6i_hwspinlock_debugfs_init(struct sun6i_hwspinlock_data *priv)
50+
{
51+
priv->debugfs = debugfs_create_dir(DRIVER_NAME, NULL);
52+
debugfs_create_file("supported", 0444, priv->debugfs, priv, &hwlocks_supported_fops);
53+
}
54+
55+
#else
56+
57+
static void sun6i_hwspinlock_debugfs_init(struct sun6i_hwspinlock_data *priv)
58+
{
59+
}
60+
61+
#endif
62+
63+
static int sun6i_hwspinlock_trylock(struct hwspinlock *lock)
64+
{
65+
void __iomem *lock_addr = lock->priv;
66+
67+
return (readl(lock_addr) == SPINLOCK_NOTTAKEN);
68+
}
69+
70+
static void sun6i_hwspinlock_unlock(struct hwspinlock *lock)
71+
{
72+
void __iomem *lock_addr = lock->priv;
73+
74+
writel(SPINLOCK_NOTTAKEN, lock_addr);
75+
}
76+
77+
static const struct hwspinlock_ops sun6i_hwspinlock_ops = {
78+
.trylock = sun6i_hwspinlock_trylock,
79+
.unlock = sun6i_hwspinlock_unlock,
80+
};
81+
82+
static void sun6i_hwspinlock_disable(void *data)
83+
{
84+
struct sun6i_hwspinlock_data *priv = data;
85+
86+
debugfs_remove_recursive(priv->debugfs);
87+
clk_disable_unprepare(priv->ahb_clk);
88+
reset_control_assert(priv->reset);
89+
}
90+
91+
static int sun6i_hwspinlock_probe(struct platform_device *pdev)
92+
{
93+
struct sun6i_hwspinlock_data *priv;
94+
struct hwspinlock *hwlock;
95+
void __iomem *io_base;
96+
u32 num_banks;
97+
int err, i;
98+
99+
io_base = devm_platform_ioremap_resource(pdev, SPINLOCK_BASE_ID);
100+
if (IS_ERR(io_base))
101+
return PTR_ERR(io_base);
102+
103+
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
104+
if (!priv)
105+
return -ENOMEM;
106+
107+
priv->ahb_clk = devm_clk_get(&pdev->dev, "ahb");
108+
if (IS_ERR(priv->ahb_clk)) {
109+
err = PTR_ERR(priv->ahb_clk);
110+
dev_err(&pdev->dev, "unable to get AHB clock (%d)\n", err);
111+
return err;
112+
}
113+
114+
priv->reset = devm_reset_control_get(&pdev->dev, "ahb");
115+
if (IS_ERR(priv->reset))
116+
return dev_err_probe(&pdev->dev, PTR_ERR(priv->reset),
117+
"unable to get reset control\n");
118+
119+
err = reset_control_deassert(priv->reset);
120+
if (err) {
121+
dev_err(&pdev->dev, "deassert reset control failure (%d)\n", err);
122+
return err;
123+
}
124+
125+
err = clk_prepare_enable(priv->ahb_clk);
126+
if (err) {
127+
dev_err(&pdev->dev, "unable to prepare AHB clk (%d)\n", err);
128+
goto clk_fail;
129+
}
130+
131+
/*
132+
* bit 28 and 29 represents the hwspinlock setup
133+
*
134+
* every datasheet (A64, A80, A83T, H3, H5, H6 ...) says the default value is 0x1 and 0x1
135+
* to 0x4 represent 32, 64, 128 and 256 locks
136+
* but later datasheets (H5, H6) say 00, 01, 10, 11 represent 32, 64, 128 and 256 locks,
137+
* but that would mean H5 and H6 have 64 locks, while their datasheets talk about 32 locks
138+
* all the time, not a single mentioning of 64 locks
139+
* the 0x4 value is also not representable by 2 bits alone, so some datasheets are not
140+
* correct
141+
* one thing have all in common, default value of the sysstatus register is 0x10000000,
142+
* which results in bit 28 being set
143+
* this is the reason 0x1 is considered being 32 locks and bit 30 is taken into account
144+
* verified on H2+ (datasheet 0x1 = 32 locks) and H5 (datasheet 01 = 64 locks)
145+
*/
146+
num_banks = readl(io_base + SPINLOCK_SYSSTATUS_REG) >> 28;
147+
switch (num_banks) {
148+
case 1 ... 4:
149+
priv->nlocks = 1 << (4 + num_banks);
150+
break;
151+
default:
152+
err = -EINVAL;
153+
dev_err(&pdev->dev, "unsupported hwspinlock setup (%d)\n", num_banks);
154+
goto bank_fail;
155+
}
156+
157+
priv->bank = devm_kzalloc(&pdev->dev, struct_size(priv->bank, lock, priv->nlocks),
158+
GFP_KERNEL);
159+
if (!priv->bank) {
160+
err = -ENOMEM;
161+
goto bank_fail;
162+
}
163+
164+
for (i = 0; i < priv->nlocks; ++i) {
165+
hwlock = &priv->bank->lock[i];
166+
hwlock->priv = io_base + SPINLOCK_LOCK_REGN + sizeof(u32) * i;
167+
}
168+
169+
/* failure of debugfs is considered non-fatal */
170+
sun6i_hwspinlock_debugfs_init(priv);
171+
if (IS_ERR(priv->debugfs))
172+
priv->debugfs = NULL;
173+
174+
err = devm_add_action_or_reset(&pdev->dev, sun6i_hwspinlock_disable, priv);
175+
if (err) {
176+
dev_err(&pdev->dev, "failed to add hwspinlock disable action\n");
177+
goto bank_fail;
178+
}
179+
180+
platform_set_drvdata(pdev, priv);
181+
182+
return devm_hwspin_lock_register(&pdev->dev, priv->bank, &sun6i_hwspinlock_ops,
183+
SPINLOCK_BASE_ID, priv->nlocks);
184+
185+
bank_fail:
186+
clk_disable_unprepare(priv->ahb_clk);
187+
clk_fail:
188+
reset_control_assert(priv->reset);
189+
190+
return err;
191+
}
192+
193+
static const struct of_device_id sun6i_hwspinlock_ids[] = {
194+
{ .compatible = "allwinner,sun6i-a31-hwspinlock", },
195+
{},
196+
};
197+
MODULE_DEVICE_TABLE(of, sun6i_hwspinlock_ids);
198+
199+
static struct platform_driver sun6i_hwspinlock_driver = {
200+
.probe = sun6i_hwspinlock_probe,
201+
.driver = {
202+
.name = DRIVER_NAME,
203+
.of_match_table = sun6i_hwspinlock_ids,
204+
},
205+
};
206+
module_platform_driver(sun6i_hwspinlock_driver);
207+
208+
MODULE_LICENSE("GPL");
209+
MODULE_DESCRIPTION("SUN6I hardware spinlock driver");
210+
MODULE_AUTHOR("Wilken Gottwalt <[email protected]>");

0 commit comments

Comments
 (0)