1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include <linux/module.h>
25#include <linux/delay.h>
26#include <linux/io.h>
27#include <linux/pm_runtime.h>
28#include <linux/slab.h>
29#include <linux/spinlock.h>
30#include <linux/hwspinlock.h>
31#include <linux/platform_device.h>
32
33#include "hwspinlock_internal.h"
34
35
36
37
38
39
40
41
42#define U8500_MAX_SEMAPHORE 32
43#define RESET_SEMAPHORE (0)
44
45
46
47
48
49
50
51#define HSEM_MASTER_ID 0x01
52
53#define HSEM_REGISTER_OFFSET 0x08
54
55#define HSEM_CTRL_REG 0x00
56#define HSEM_ICRALL 0x90
57#define HSEM_PROTOCOL_1 0x01
58
59static int u8500_hsem_trylock(struct hwspinlock *lock)
60{
61 void __iomem *lock_addr = lock->priv;
62
63 writel(HSEM_MASTER_ID, lock_addr);
64
65
66
67
68
69 return (HSEM_MASTER_ID == (0x0F & readl(lock_addr)));
70}
71
72static void u8500_hsem_unlock(struct hwspinlock *lock)
73{
74 void __iomem *lock_addr = lock->priv;
75
76
77 writel(RESET_SEMAPHORE, lock_addr);
78}
79
80
81
82
83static void u8500_hsem_relax(struct hwspinlock *lock)
84{
85 ndelay(50);
86}
87
88static const struct hwspinlock_ops u8500_hwspinlock_ops = {
89 .trylock = u8500_hsem_trylock,
90 .unlock = u8500_hsem_unlock,
91 .relax = u8500_hsem_relax,
92};
93
94static int u8500_hsem_probe(struct platform_device *pdev)
95{
96 struct hwspinlock_pdata *pdata = pdev->dev.platform_data;
97 struct hwspinlock_device *bank;
98 struct hwspinlock *hwlock;
99 struct resource *res;
100 void __iomem *io_base;
101 int i, ret, num_locks = U8500_MAX_SEMAPHORE;
102 ulong val;
103
104 if (!pdata)
105 return -ENODEV;
106
107 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
108 if (!res)
109 return -ENODEV;
110
111 io_base = ioremap(res->start, resource_size(res));
112 if (!io_base)
113 return -ENOMEM;
114
115
116 val = readl(io_base + HSEM_CTRL_REG);
117 writel((val & ~HSEM_PROTOCOL_1), io_base + HSEM_CTRL_REG);
118
119
120 writel(0xFFFF, io_base + HSEM_ICRALL);
121
122 bank = kzalloc(sizeof(*bank) + num_locks * sizeof(*hwlock), GFP_KERNEL);
123 if (!bank) {
124 ret = -ENOMEM;
125 goto iounmap_base;
126 }
127
128 platform_set_drvdata(pdev, bank);
129
130 for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++)
131 hwlock->priv = io_base + HSEM_REGISTER_OFFSET + sizeof(u32) * i;
132
133
134 pm_runtime_enable(&pdev->dev);
135
136 ret = hwspin_lock_register(bank, &pdev->dev, &u8500_hwspinlock_ops,
137 pdata->base_id, num_locks);
138 if (ret)
139 goto reg_fail;
140
141 return 0;
142
143reg_fail:
144 pm_runtime_disable(&pdev->dev);
145 kfree(bank);
146iounmap_base:
147 iounmap(io_base);
148 return ret;
149}
150
151static int u8500_hsem_remove(struct platform_device *pdev)
152{
153 struct hwspinlock_device *bank = platform_get_drvdata(pdev);
154 void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET;
155 int ret;
156
157
158 writel(0xFFFF, io_base + HSEM_ICRALL);
159
160 ret = hwspin_lock_unregister(bank);
161 if (ret) {
162 dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
163 return ret;
164 }
165
166 pm_runtime_disable(&pdev->dev);
167 iounmap(io_base);
168 kfree(bank);
169
170 return 0;
171}
172
173static struct platform_driver u8500_hsem_driver = {
174 .probe = u8500_hsem_probe,
175 .remove = u8500_hsem_remove,
176 .driver = {
177 .name = "u8500_hsem",
178 .owner = THIS_MODULE,
179 },
180};
181
182static int __init u8500_hsem_init(void)
183{
184 return platform_driver_register(&u8500_hsem_driver);
185}
186
187postcore_initcall(u8500_hsem_init);
188
189static void __exit u8500_hsem_exit(void)
190{
191 platform_driver_unregister(&u8500_hsem_driver);
192}
193module_exit(u8500_hsem_exit);
194
195MODULE_LICENSE("GPL v2");
196MODULE_DESCRIPTION("Hardware Spinlock driver for u8500");
197MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
198