• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * SIRF hardware spinlock driver
3  *
4  * Copyright (c) 2015 Cambridge Silicon Radio Limited, a CSR plc group company.
5  *
6  * Licensed under GPLv2.
7  */
8 
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/device.h>
12 #include <linux/io.h>
13 #include <linux/pm_runtime.h>
14 #include <linux/slab.h>
15 #include <linux/spinlock.h>
16 #include <linux/hwspinlock.h>
17 #include <linux/platform_device.h>
18 #include <linux/of.h>
19 #include <linux/of_address.h>
20 
21 #include "hwspinlock_internal.h"
22 
23 struct sirf_hwspinlock {
24 	void __iomem *io_base;
25 	struct hwspinlock_device bank;
26 };
27 
28 /* Number of Hardware Spinlocks*/
29 #define	HW_SPINLOCK_NUMBER	30
30 
31 /* Hardware spinlock register offsets */
32 #define HW_SPINLOCK_BASE	0x404
33 #define HW_SPINLOCK_OFFSET(x)	(HW_SPINLOCK_BASE + 0x4 * (x))
34 
sirf_hwspinlock_trylock(struct hwspinlock * lock)35 static int sirf_hwspinlock_trylock(struct hwspinlock *lock)
36 {
37 	void __iomem *lock_addr = lock->priv;
38 
39 	/* attempt to acquire the lock by reading value == 1 from it */
40 	return !!readl(lock_addr);
41 }
42 
sirf_hwspinlock_unlock(struct hwspinlock * lock)43 static void sirf_hwspinlock_unlock(struct hwspinlock *lock)
44 {
45 	void __iomem *lock_addr = lock->priv;
46 
47 	/* release the lock by writing 0 to it */
48 	writel(0, lock_addr);
49 }
50 
51 static const struct hwspinlock_ops sirf_hwspinlock_ops = {
52 	.trylock = sirf_hwspinlock_trylock,
53 	.unlock = sirf_hwspinlock_unlock,
54 };
55 
sirf_hwspinlock_probe(struct platform_device * pdev)56 static int sirf_hwspinlock_probe(struct platform_device *pdev)
57 {
58 	struct sirf_hwspinlock *hwspin;
59 	struct hwspinlock *hwlock;
60 	int idx, ret;
61 
62 	if (!pdev->dev.of_node)
63 		return -ENODEV;
64 
65 	hwspin = devm_kzalloc(&pdev->dev, sizeof(*hwspin) +
66 			sizeof(*hwlock) * HW_SPINLOCK_NUMBER, GFP_KERNEL);
67 	if (!hwspin)
68 		return -ENOMEM;
69 
70 	/* retrieve io base */
71 	hwspin->io_base = of_iomap(pdev->dev.of_node, 0);
72 	if (!hwspin->io_base)
73 		return -ENOMEM;
74 
75 	for (idx = 0; idx < HW_SPINLOCK_NUMBER; idx++) {
76 		hwlock = &hwspin->bank.lock[idx];
77 		hwlock->priv = hwspin->io_base + HW_SPINLOCK_OFFSET(idx);
78 	}
79 
80 	platform_set_drvdata(pdev, hwspin);
81 
82 	pm_runtime_enable(&pdev->dev);
83 
84 	ret = hwspin_lock_register(&hwspin->bank, &pdev->dev,
85 				   &sirf_hwspinlock_ops, 0,
86 				   HW_SPINLOCK_NUMBER);
87 	if (ret)
88 		goto reg_failed;
89 
90 	return 0;
91 
92 reg_failed:
93 	pm_runtime_disable(&pdev->dev);
94 	iounmap(hwspin->io_base);
95 
96 	return ret;
97 }
98 
sirf_hwspinlock_remove(struct platform_device * pdev)99 static int sirf_hwspinlock_remove(struct platform_device *pdev)
100 {
101 	struct sirf_hwspinlock *hwspin = platform_get_drvdata(pdev);
102 	int ret;
103 
104 	ret = hwspin_lock_unregister(&hwspin->bank);
105 	if (ret) {
106 		dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
107 		return ret;
108 	}
109 
110 	pm_runtime_disable(&pdev->dev);
111 
112 	iounmap(hwspin->io_base);
113 
114 	return 0;
115 }
116 
117 static const struct of_device_id sirf_hwpinlock_ids[] = {
118 	{ .compatible = "sirf,hwspinlock", },
119 	{},
120 };
121 MODULE_DEVICE_TABLE(of, sirf_hwpinlock_ids);
122 
123 static struct platform_driver sirf_hwspinlock_driver = {
124 	.probe = sirf_hwspinlock_probe,
125 	.remove = sirf_hwspinlock_remove,
126 	.driver = {
127 		.name = "atlas7_hwspinlock",
128 		.of_match_table = of_match_ptr(sirf_hwpinlock_ids),
129 	},
130 };
131 
132 module_platform_driver(sirf_hwspinlock_driver);
133 
134 MODULE_LICENSE("GPL v2");
135 MODULE_DESCRIPTION("SIRF Hardware spinlock driver");
136 MODULE_AUTHOR("Wei Chen <wei.chen@csr.com>");
137