xref: /OK3568_Linux_fs/kernel/drivers/hwspinlock/rockchip_hwspinlock.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
3 
4 #include <linux/kernel.h>
5 #include <linux/module.h>
6 #include <linux/device.h>
7 #include <linux/io.h>
8 #include <linux/slab.h>
9 #include <linux/hwspinlock.h>
10 #include <linux/platform_device.h>
11 #include <linux/of.h>
12 
13 #include "hwspinlock_internal.h"
14 
15 struct rockchip_hwspinlock {
16 	void __iomem *io_base;
17 	struct hwspinlock_device bank;
18 };
19 
20 /* Number of Hardware Spinlocks*/
21 #define	HWSPINLOCK_NUMBER	64
22 
23 /* Hardware spinlock register offsets */
24 #define HWSPINLOCK_OFFSET(x)	(0x4 * (x))
25 
26 #define HWSPINLOCK_OWNER_ID	0x01
27 
rockchip_hwspinlock_trylock(struct hwspinlock * lock)28 static int rockchip_hwspinlock_trylock(struct hwspinlock *lock)
29 {
30 	void __iomem *lock_addr = lock->priv;
31 
32 	writel(HWSPINLOCK_OWNER_ID, lock_addr);
33 
34 	/*
35 	 * Get only first 4 bits and compare to HWSPINLOCK_OWNER_ID,
36 	 * if equal, we attempt to acquire the lock, otherwise,
37 	 * someone else has it.
38 	 */
39 	return (HWSPINLOCK_OWNER_ID == (0x0F & readl(lock_addr)));
40 }
41 
rockchip_hwspinlock_unlock(struct hwspinlock * lock)42 static void rockchip_hwspinlock_unlock(struct hwspinlock *lock)
43 {
44 	void __iomem *lock_addr = lock->priv;
45 
46 	/* Release the lock by writing 0 to it */
47 	writel(0, lock_addr);
48 }
49 
50 static const struct hwspinlock_ops rockchip_hwspinlock_ops = {
51 	.trylock = rockchip_hwspinlock_trylock,
52 	.unlock = rockchip_hwspinlock_unlock,
53 };
54 
rockchip_hwspinlock_probe(struct platform_device * pdev)55 static int rockchip_hwspinlock_probe(struct platform_device *pdev)
56 {
57 	struct rockchip_hwspinlock *hwspin;
58 	struct hwspinlock *hwlock;
59 	int idx;
60 
61 	hwspin = devm_kzalloc(&pdev->dev,
62 			      struct_size(hwspin, bank.lock, HWSPINLOCK_NUMBER),
63 			      GFP_KERNEL);
64 	if (!hwspin)
65 		return -ENOMEM;
66 
67 	hwspin->io_base = devm_platform_ioremap_resource(pdev, 0);
68 	if (IS_ERR(hwspin->io_base))
69 		return PTR_ERR(hwspin->io_base);
70 
71 	for (idx = 0; idx < HWSPINLOCK_NUMBER; idx++) {
72 		hwlock = &hwspin->bank.lock[idx];
73 		hwlock->priv = hwspin->io_base + HWSPINLOCK_OFFSET(idx);
74 	}
75 
76 	platform_set_drvdata(pdev, hwspin);
77 
78 	return devm_hwspin_lock_register(&pdev->dev, &hwspin->bank,
79 					 &rockchip_hwspinlock_ops, 0,
80 					 HWSPINLOCK_NUMBER);
81 }
82 
83 static const struct of_device_id rockchip_hwpinlock_ids[] = {
84 	{ .compatible = "rockchip,hwspinlock", },
85 	{},
86 };
87 MODULE_DEVICE_TABLE(of, rockchip_hwpinlock_ids);
88 
89 static struct platform_driver rockchip_hwspinlock_driver = {
90 	.probe = rockchip_hwspinlock_probe,
91 	.driver = {
92 		.name = "rockchip_hwspinlock",
93 		.of_match_table = of_match_ptr(rockchip_hwpinlock_ids),
94 	},
95 };
96 
rockchip_hwspinlock_init(void)97 static int __init rockchip_hwspinlock_init(void)
98 {
99 	return platform_driver_register(&rockchip_hwspinlock_driver);
100 }
101 postcore_initcall(rockchip_hwspinlock_init);
102 
rockchip_hwspinlock_exit(void)103 static void __exit rockchip_hwspinlock_exit(void)
104 {
105 	platform_driver_unregister(&rockchip_hwspinlock_driver);
106 }
107 module_exit(rockchip_hwspinlock_exit);
108 
109 MODULE_LICENSE("GPL v2");
110 MODULE_DESCRIPTION("Rockchip Hardware spinlock driver");
111 MODULE_AUTHOR("Frank Wang <frank.wang@rock-chips.com>");
112