aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/arb_sema.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/arb_sema.c')
-rw-r--r--arch/arm/mach-tegra/arb_sema.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/arb_sema.c b/arch/arm/mach-tegra/arb_sema.c
new file mode 100644
index 00000000000..eecdee5967c
--- /dev/null
+++ b/arch/arm/mach-tegra/arb_sema.c
@@ -0,0 +1,243 @@
1/*
2 * Copyright (C) 2010, NVIDIA Corporation
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#include <linux/module.h>
16#include <linux/kernel.h>
17#include <linux/slab.h>
18#include <linux/interrupt.h>
19#include <linux/irq.h>
20#include <linux/io.h>
21#include <linux/mutex.h>
22#include <linux/err.h>
23#include <linux/delay.h>
24#include <linux/completion.h>
25
26#include <mach/arb_sema.h>
27#include <mach/irqs.h>
28#include <mach/iomap.h>
29
30#define TEGRA_RPC_MAX_SEM 32
31
32/* arb_gnt ictrl */
33#define ARB_CPU_INT_EN 0x4
34
35/* arb_sema */
36#define ARB_GRANT_STATUS 0x0
37#define ARB_GRANT_REQUEST 0x4
38#define ARB_GRANT_RELEASE 0x8
39#define ARB_GRANT_PENDING 0xC
40
41struct tegra_arb_dev {
42 void __iomem *sema_base;
43 void __iomem *gnt_base;
44 spinlock_t lock;
45 struct completion arb_gnt_complete[TEGRA_RPC_MAX_SEM];
46 struct mutex mutexes[TEGRA_RPC_MAX_SEM];
47 int irq;
48 int status;
49 bool suspended;
50};
51
52static struct tegra_arb_dev *arb;
53
54static inline u32 arb_sema_read(u32 offset)
55{
56 return readl(arb->sema_base + offset);
57}
58
59static inline void arb_sema_write(u32 value, u32 offset)
60{
61 writel(value, arb->sema_base + offset);
62}
63
64static inline u32 arb_gnt_read(u32 offset)
65{
66 return readl(arb->gnt_base + offset);
67}
68
69static inline void arb_gnt_write(u32 value, u32 offset)
70{
71 writel(value, arb->gnt_base + offset);
72}
73
74static void request_arb_sem(enum tegra_arb_module lock)
75{
76 unsigned long flags;
77 u32 value;
78
79 spin_lock_irqsave(&arb->lock, flags);
80
81 arb_sema_write(1 << lock, ARB_GRANT_REQUEST);
82 value = arb_gnt_read(ARB_CPU_INT_EN);
83 value |= (1 << lock);
84 arb_gnt_write(value, ARB_CPU_INT_EN);
85
86 spin_unlock_irqrestore(&arb->lock, flags);
87}
88
89static void cancel_arb_sem(enum tegra_arb_module lock)
90{
91 unsigned long flags;
92 u32 value;
93
94 spin_lock_irqsave(&arb->lock, flags);
95
96 arb_sema_write(1 << lock, ARB_GRANT_RELEASE);
97 value = arb_gnt_read(ARB_CPU_INT_EN);
98 value &= ~(1 << lock);
99 arb_gnt_write(value, ARB_CPU_INT_EN);
100
101 spin_unlock_irqrestore(&arb->lock, flags);
102}
103
104int tegra_arb_mutex_lock_timeout(enum tegra_arb_module lock, int msecs)
105{
106 int ret;
107
108 if (!arb)
109 return -ENODEV;
110
111 if (arb->suspended) {
112 pr_err("device in suspend\n");
113 return -ETIMEDOUT;
114 }
115
116 mutex_lock(&arb->mutexes[lock]);
117 INIT_COMPLETION(arb->arb_gnt_complete[lock]);
118 request_arb_sem(lock);
119 ret = wait_for_completion_timeout(&arb->arb_gnt_complete[lock], msecs_to_jiffies(msecs));
120 if (ret == 0) {
121 pr_err("timed out. pending:0x%x\n", arb_sema_read(ARB_GRANT_PENDING));
122 cancel_arb_sem(lock);
123 mutex_unlock(&arb->mutexes[lock]);
124 return -ETIMEDOUT;
125 }
126
127 return 0;
128}
129EXPORT_SYMBOL(tegra_arb_mutex_lock_timeout);
130
131int tegra_arb_mutex_unlock(enum tegra_arb_module lock)
132{
133 if (!arb)
134 return -ENODEV;
135
136 if (arb->suspended) {
137 pr_err("device in suspend\n");
138 return -ETIMEDOUT;
139 }
140
141 cancel_arb_sem(lock);
142 mutex_unlock(&arb->mutexes[lock]);
143 return 0;
144}
145EXPORT_SYMBOL(tegra_arb_mutex_unlock);
146
147static irqreturn_t arb_gnt_isr(int irq, void *dev_id)
148{
149 struct tegra_arb_dev *dev = dev_id;
150 unsigned long status;
151 u32 cpu_int_en;
152 unsigned int bit;
153 unsigned long flags;
154
155 spin_lock_irqsave(&arb->lock, flags);
156
157 status = arb_sema_read(ARB_GRANT_STATUS);
158 pr_debug("%s: 0x%lx\n", __func__, status);
159
160 /* disable the arb semaphores which were signalled */
161 cpu_int_en = arb_gnt_read(ARB_CPU_INT_EN);
162 arb_gnt_write((cpu_int_en & ~(status & cpu_int_en)),
163 ARB_CPU_INT_EN);
164
165 status &= cpu_int_en;
166 for_each_set_bit(bit, &status, BITS_PER_LONG)
167 complete(&dev->arb_gnt_complete[bit]);
168
169 spin_unlock_irqrestore(&arb->lock, flags);
170 return IRQ_HANDLED;
171}
172
173int tegra_arb_suspend(void)
174{
175 unsigned long status = arb_sema_read(ARB_GRANT_STATUS);
176
177 if (WARN_ON(status != 0)) {
178 pr_err("%s: suspending while holding arbitration "
179 "semaphore: %08lx\n", __func__, status);
180 }
181 arb->suspended = true;
182
183 return status ? -EBUSY : 0;
184}
185
186int tegra_arb_resume(void)
187{
188 arb->suspended = false;
189 return 0;
190}
191
192static int __init tegra_arb_init(void)
193{
194 struct tegra_arb_dev *dev = NULL;
195 int err, i;
196
197 dev = kzalloc(sizeof(struct tegra_arb_dev), GFP_KERNEL);
198 if (dev == NULL) {
199 pr_err("%s: unable to alloc data struct.\n", __func__);
200 return -ENOMEM;
201 }
202
203 for (i = 0; i < TEGRA_RPC_MAX_SEM; i++) {
204 mutex_init(&dev->mutexes[i]);
205 init_completion(&dev->arb_gnt_complete[i]);
206 }
207
208 dev->sema_base = IO_ADDRESS(TEGRA_ARB_SEMA_BASE);
209 if (!dev->sema_base) {
210 pr_err("%s: can't get arb sema_base\n", __func__);
211 err = -ENOMEM;
212 goto out;
213 }
214
215 dev->gnt_base = IO_ADDRESS(TEGRA_ARBGNT_ICTLR_BASE);
216 if (!dev->gnt_base) {
217 pr_err("%s: can't ioremap gnt_base\n", __func__);
218 err = -ENOMEM;
219 goto out;
220 }
221
222 dev->irq = INT_GNT_1;
223 err = request_irq(dev->irq, arb_gnt_isr, 0, "rpc-arbsema", dev);
224 if (err) {
225 pr_err("%s: request_irq(%d) failed(%d)\n", __func__,
226 dev->irq, err);
227 goto out;
228 }
229
230 spin_lock_init(&dev->lock);
231 arb = dev;
232
233 pr_info("%s: initialized\n", __func__);
234 return 0;
235
236out:
237 kfree(dev);
238 pr_err("%s: initialization failed.\n", __func__);
239 return err;
240}
241subsys_initcall(tegra_arb_init);
242
243MODULE_LICENSE("GPLv2");