ARM: mali400: r5p2_rel0: fix Makefile & Kconfig
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / drivers / hwspinlock / sci_hwspinlock.c
1 /*
2  * SCI hardware spinlock driver
3  *
4  * Copyright (C) 2012 Spreadtrum  - http://www.spreadtrum.com
5  * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com
6  *
7  * Contact: steve.zhan <steve.zhan@spreadtrum.com>
8  *        
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * version 2 as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  */
18
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/device.h>
22 #include <linux/delay.h>
23 #include <linux/io.h>
24 #include <linux/bitops.h>
25 #include <linux/pm_runtime.h>
26 #include <linux/slab.h>
27 #include <linux/spinlock.h>
28 #include <linux/hwspinlock.h>
29 #include <linux/platform_device.h>
30 #include <linux/of.h>
31 #include <linux/of_device.h>
32
33 #include <soc/sprd/sci.h>
34 #include <soc/sprd/sci_glb_regs.h>
35 #include <soc/sprd/arch_lock.h>
36
37 #include "hwspinlock_internal.h"
38
39
40 #define HWSPINLOCK_ENABLE_CLEAR         (0x454e434c)
41
42 #define HWSPINLOCK_CLEAR                (0x0)
43 #define HWSPINLOCK_TTLSTS               (0x4)
44 #define HWSPINLOCK_DTLSTL               (0x8)
45 #define HWSPINLOCK_CLEAREN              (0xc)
46
47 #define HWSPINLOCK_TOKEN_V0(_X_)        (0x80 + 0x4*(_X_))
48 #define HWSPINLOCK_TOKEN_V1(_X_)        (0x800 + 0x4*(_X_))
49
50 #define THIS_PROCESSOR_KEY      (HWSPINLOCK_WRITE_KEY)  /*first processor */
51 static void __iomem *hwspinlock_base;
52 static int hwspinlock_exist_cnt = 0;
53
54 #define SPINLOCKS_BUSY()        (readl(hwspinlock_base + HWSPINLOCK_TTLSTS))
55 #define SPINLOCKS_DETAIL_STATUS(_X_)    (_X_ = readl(hwspinlock_base + HWSPINLOCK_DTLSTL))
56 #define SPINLOCKS_ENABLE_CLEAR()        writel(HWSPINLOCK_ENABLE_CLEAR,hwspinlock_base + HWSPINLOCK_CLEAREN)
57 #define SPINLOCKS_DISABLE_CLEAR()       writel(~HWSPINLOCK_ENABLE_CLEAR, hwspinlock_base + HWSPINLOCK_CLEAREN)
58
59 __used static int hwspinlock_isbusy(unsigned int lockid)
60 {
61         unsigned int status = 0;
62         SPINLOCKS_DETAIL_STATUS(status);
63         return ((status & (1 << lockid)) ? 1 : 0);
64 }
65
66 __used static int hwspinlocks_isbusy(void)
67 {
68         return ((SPINLOCKS_BUSY())? 1 : 0);
69 }
70
71 __used static void hwspinlock_clear(unsigned int lockid)
72 {
73         /*setting the abnormal clear bit to 1 makes the corresponding
74          *lock to Not Taken state
75          */
76         SPINLOCKS_ENABLE_CLEAR();
77         writel(1 << lockid, hwspinlock_base + HWSPINLOCK_CLEAR);
78         SPINLOCKS_DISABLE_CLEAR();
79 }
80
81 __used static void hwspinlock_clear_all(void)
82 {
83         /*clear all the locks
84          */
85         unsigned int lockid = 0;
86         SPINLOCKS_ENABLE_CLEAR();
87         do {
88                 writel(1 << lockid, hwspinlock_base + HWSPINLOCK_CLEAR);
89         } while (lockid++ < HWSPINLOCK_ID_TOTAL_NUMS);
90         SPINLOCKS_DISABLE_CLEAR();
91 }
92
93 static unsigned int do_lock_key(struct hwspinlock *lock)
94 {
95         unsigned int key = THIS_PROCESSOR_KEY;
96         return key;
97 }
98
99 static int __hwspinlock_trylock(struct hwspinlock *lock)
100 {
101         void __iomem *addr = lock->priv;
102
103         if (hwspinlock_vid == 0x100 || hwspinlock_vid == 0x200) {
104                 if (!readl(addr))
105                         goto __locked;
106         } else {
107                 unsigned int key = do_lock_key(lock);
108                 if (!(key ^ HWSPINLOCK_NOTTAKEN_V0))
109                         BUG_ON(1);
110                 if (HWSPINLOCK_NOTTAKEN_V0 == readl(addr)) {
111                         writel(key, addr);
112                         if (key == readl(addr))
113                                 goto __locked;
114                 }
115         }
116         return 0;
117
118 __locked:
119         RECORD_HWLOCKS_STATUS_LOCK(hwlock_to_id(lock));
120         return 1;
121 }
122
123 static void __hwspinlock_unlock(struct hwspinlock *lock)
124 {
125         void __iomem *lock_addr = lock->priv;
126         int unlock_key = 0;
127
128         if (hwspinlock_vid == 0x100 || hwspinlock_vid == 0x200) {
129                 unlock_key = HWSPINLOCK_NOTTAKEN_V1;
130         } else {
131                 unlock_key = HWSPINLOCK_NOTTAKEN_V0;
132                 if (!(readl(lock_addr) ^ unlock_key))
133                         BUG_ON(1);
134         }
135         RECORD_HWLOCKS_STATUS_UNLOCK(hwlock_to_id(lock));
136         writel(unlock_key, lock_addr);
137 }
138
139 /*
140  * relax the  interconnect while spinning on it.
141  *
142  * The specs recommended that the retry delay time will be
143  * just over half of the time that a requester would be
144  * expected to hold the lock.
145  *
146  * The number below is taken from an hardware specs example,
147  * obviously it is somewhat arbitrary.
148  */
149 static void __hwspinlock_relax(struct hwspinlock *lock)
150 {
151         ndelay(10);
152 }
153
154 static const struct hwspinlock_ops sci_hwspinlock_ops = {
155         .trylock = __hwspinlock_trylock,
156         .unlock = __hwspinlock_unlock,
157         .relax = __hwspinlock_relax,
158 };
159
160 static int sci_hwspinlock_probe(struct platform_device *pdev)
161 {
162         struct hwspinlock_device *bank;
163         struct hwspinlock *hwlock;
164         int i, ret, num_locks;
165
166         pdev->id = of_alias_get_id(pdev->dev.of_node, "hwspinlock");
167         if(pdev->id == 0)
168                 return 0;
169         __hwspinlock_init();
170
171         /*each group have 32 locks*/
172         num_locks = 32;
173         hwspinlock_base = (void __iomem *)(pdev->id == 0 ? SPRD_HWSPINLOCK0_BASE : SPRD_HWSPINLOCK1_BASE);
174         hwspinlock_vid = __raw_readl(hwspinlock_base + 0xffc);
175
176         bank = kzalloc(sizeof(*bank) + num_locks * sizeof(*hwlock), GFP_KERNEL);
177         if (!bank) {
178                 ret = -ENOMEM;
179                 goto exit;
180         }
181
182         platform_set_drvdata(pdev, bank);
183
184         for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++) {
185                 if (hwspinlock_vid == 0x100 || hwspinlock_vid == 0x200)
186                         hwlock->priv = (void __iomem *)(hwspinlock_base + HWSPINLOCK_TOKEN_V1(i));
187                 else
188                         hwlock->priv = (void __iomem *)(hwspinlock_base + HWSPINLOCK_TOKEN_V0(i));
189         }
190         /*
191          * runtime PM will make sure the clock of this module is
192          * enabled if at least one lock is requested
193          */
194         pm_runtime_enable(&pdev->dev);
195
196         ret = hwspin_lock_register(bank, &pdev->dev, &sci_hwspinlock_ops,
197                                    hwspinlock_exist_cnt, num_locks);
198
199         if (ret)
200                 goto reg_fail;
201
202         hwspinlock_exist_cnt += num_locks;
203
204         printk("sci_hwspinlock_probe ok: hwspinlock name = %s, hwspinlock_vid = 0x%x, add num_locks = %d\n",
205                pdev->name,hwspinlock_vid,num_locks);
206
207         return 0;
208
209 reg_fail:
210         pm_runtime_disable(&pdev->dev);
211         kfree(bank);
212 exit:
213         return ret;
214 }
215
216 static int sci_hwspinlock_remove(struct platform_device *pdev)
217 {
218         struct hwspinlock_device *bank = platform_get_drvdata(pdev);
219         int ret;
220
221         ret = hwspin_lock_unregister(bank);
222         if (ret) {
223                 dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
224                 return ret;
225         }
226
227         pm_runtime_disable(&pdev->dev);
228         kfree(bank);
229
230         return 0;
231 }
232
233 static const struct of_device_id sprd_hwspinlock_of_match[] = {
234         { .compatible = "sprd,sprd-hwspinlock", },
235         { /* sentinel */ }
236 };
237
238 static struct platform_driver sci_hwspinlock_driver = {
239         .probe = sci_hwspinlock_probe,
240         .remove = sci_hwspinlock_remove,
241         .driver = {
242                 .name = "sci_hwspinlock",
243                 .owner = THIS_MODULE,
244                 .of_match_table = of_match_ptr(sprd_hwspinlock_of_match),
245         },
246 };
247
248 static int __init sci_hwspinlock_init(void)
249 {
250         return platform_driver_register(&sci_hwspinlock_driver);
251 }
252
253 /* board init code might need to reserve hwspinlocks for predefined purposes */
254 postcore_initcall(sci_hwspinlock_init);
255
256 static void __exit sci_hwspinlock_exit(void)
257 {
258         platform_driver_unregister(&sci_hwspinlock_driver);
259 }
260
261 module_exit(sci_hwspinlock_exit);
262
263 MODULE_LICENSE("GPL v2");
264 MODULE_DESCRIPTION("Hardware spinlock driver for Spreadtrum");
265 MODULE_AUTHOR("steve.zhan <steve.zhan@spreadtrum.com>");