diff options
Diffstat (limited to 'arch/arm/mach-tegra/arb_sema.c')
-rw-r--r-- | arch/arm/mach-tegra/arb_sema.c | 243 |
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 | |||
41 | struct 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 | |||
52 | static struct tegra_arb_dev *arb; | ||
53 | |||
54 | static inline u32 arb_sema_read(u32 offset) | ||
55 | { | ||
56 | return readl(arb->sema_base + offset); | ||
57 | } | ||
58 | |||
59 | static inline void arb_sema_write(u32 value, u32 offset) | ||
60 | { | ||
61 | writel(value, arb->sema_base + offset); | ||
62 | } | ||
63 | |||
64 | static inline u32 arb_gnt_read(u32 offset) | ||
65 | { | ||
66 | return readl(arb->gnt_base + offset); | ||
67 | } | ||
68 | |||
69 | static inline void arb_gnt_write(u32 value, u32 offset) | ||
70 | { | ||
71 | writel(value, arb->gnt_base + offset); | ||
72 | } | ||
73 | |||
74 | static 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 | |||
89 | static 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 | |||
104 | int 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 | } | ||
129 | EXPORT_SYMBOL(tegra_arb_mutex_lock_timeout); | ||
130 | |||
131 | int 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 | } | ||
145 | EXPORT_SYMBOL(tegra_arb_mutex_unlock); | ||
146 | |||
147 | static 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 | |||
173 | int 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 | |||
186 | int tegra_arb_resume(void) | ||
187 | { | ||
188 | arb->suspended = false; | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static 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 | |||
236 | out: | ||
237 | kfree(dev); | ||
238 | pr_err("%s: initialization failed.\n", __func__); | ||
239 | return err; | ||
240 | } | ||
241 | subsys_initcall(tegra_arb_init); | ||
242 | |||
243 | MODULE_LICENSE("GPLv2"); | ||