aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorVitaly Wool <vwool@ru.mvista.com>2006-05-16 06:54:37 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2006-06-18 11:16:55 -0400
commit78818e477bf785391b02672d053fdbb2e111fb50 (patch)
treed1a43ddfb1b966fe981c10c874bae4faad070f29 /arch
parenta5a503038e71a6b7d4bd9e596ac13087274e60c7 (diff)
[ARM] 3466/1: [2/3] Support for Philips PNX4008 platform: chip support
Patch from Vitaly Wool This patch adds basic chip support for PNX4008 ARM platform. It's basically the same as the previous one, but with the rmk's comments taken into account. Signed-off-by: Vitaly Wool <vwool@ru.mvista.com> Signed-off-by: Dmitry Pervushin <dpervushin@gmail.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/Kconfig5
-rw-r--r--arch/arm/Makefile1
-rw-r--r--arch/arm/mach-pnx4008/Makefile12
-rw-r--r--arch/arm/mach-pnx4008/Makefile.boot4
-rw-r--r--arch/arm/mach-pnx4008/clock.c1010
-rw-r--r--arch/arm/mach-pnx4008/clock.h43
-rw-r--r--arch/arm/mach-pnx4008/core.c207
-rw-r--r--arch/arm/mach-pnx4008/dma.c1109
-rw-r--r--arch/arm/mach-pnx4008/gpio.c330
-rw-r--r--arch/arm/mach-pnx4008/irq.c121
-rw-r--r--arch/arm/mach-pnx4008/pm.c184
-rw-r--r--arch/arm/mach-pnx4008/serial.c69
-rw-r--r--arch/arm/mach-pnx4008/sleep.S196
-rw-r--r--arch/arm/mach-pnx4008/time.c141
-rw-r--r--arch/arm/mm/Kconfig4
15 files changed, 3434 insertions, 2 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 08b7cc900cae..f47cf9af3bc8 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -270,6 +270,11 @@ config ARCH_AT91RM9200
270 Say Y here if you intend to run this kernel on an Atmel 270 Say Y here if you intend to run this kernel on an Atmel
271 AT91RM9200-based board. 271 AT91RM9200-based board.
272 272
273config ARCH_PNX4008
274 bool "Philips Nexperia PNX4008 Mobile"
275 help
276 This enables support for Philips PNX4008 mobile platform.
277
273endchoice 278endchoice
274 279
275source "arch/arm/mach-clps711x/Kconfig" 280source "arch/arm/mach-clps711x/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 6f8e84c1c1f2..6c97aa70d3bc 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -116,6 +116,7 @@ endif
116 machine-$(CONFIG_ARCH_REALVIEW) := realview 116 machine-$(CONFIG_ARCH_REALVIEW) := realview
117 machine-$(CONFIG_ARCH_AT91RM9200) := at91rm9200 117 machine-$(CONFIG_ARCH_AT91RM9200) := at91rm9200
118 machine-$(CONFIG_ARCH_EP93XX) := ep93xx 118 machine-$(CONFIG_ARCH_EP93XX) := ep93xx
119 machine-$(CONFIG_ARCH_PNX4008) := pnx4008
119 120
120ifeq ($(CONFIG_ARCH_EBSA110),y) 121ifeq ($(CONFIG_ARCH_EBSA110),y)
121# This is what happens if you forget the IOCS16 line. 122# This is what happens if you forget the IOCS16 line.
diff --git a/arch/arm/mach-pnx4008/Makefile b/arch/arm/mach-pnx4008/Makefile
new file mode 100644
index 000000000000..b457ca0a431a
--- /dev/null
+++ b/arch/arm/mach-pnx4008/Makefile
@@ -0,0 +1,12 @@
1#
2# Makefile for the linux kernel.
3#
4
5obj-y := core.o irq.o time.o clock.o gpio.o serial.o dma.o
6obj-m :=
7obj-n :=
8obj- :=
9
10# Power Management
11obj-$(CONFIG_PM) += pm.o sleep.o
12
diff --git a/arch/arm/mach-pnx4008/Makefile.boot b/arch/arm/mach-pnx4008/Makefile.boot
new file mode 100644
index 000000000000..44c7117e20dd
--- /dev/null
+++ b/arch/arm/mach-pnx4008/Makefile.boot
@@ -0,0 +1,4 @@
1 zreladdr-y := 0x80008000
2params_phys-y := 0x80000100
3initrd_phys-y := 0x80800000
4
diff --git a/arch/arm/mach-pnx4008/clock.c b/arch/arm/mach-pnx4008/clock.c
new file mode 100644
index 000000000000..285b22f631e9
--- /dev/null
+++ b/arch/arm/mach-pnx4008/clock.c
@@ -0,0 +1,1010 @@
1/*
2 * arch/arm/mach-pnx4008/clock.c
3 *
4 * Clock control driver for PNX4008
5 *
6 * Authors: Vitaly Wool, Dmitry Chigirev <source@mvista.com>
7 * Generic clock management functions are partially based on:
8 * linux/arch/arm/mach-omap/clock.c
9 *
10 * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under
11 * the terms of the GNU General Public License version 2. This program
12 * is licensed "as is" without any warranty of any kind, whether express
13 * or implied.
14 */
15
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/list.h>
19#include <linux/errno.h>
20#include <linux/device.h>
21#include <linux/err.h>
22#include <linux/delay.h>
23
24#include <asm/semaphore.h>
25#include <asm/hardware.h>
26#include <asm/io.h>
27
28#include <asm/arch/clock.h>
29#include "clock.h"
30
31/*forward declaration*/
32static struct clk per_ck;
33static struct clk hclk_ck;
34static struct clk ck_1MHz;
35static struct clk ck_13MHz;
36static struct clk ck_pll1;
37static int local_set_rate(struct clk *clk, u32 rate);
38
39static inline void clock_lock(void)
40{
41 local_irq_disable();
42}
43
44static inline void clock_unlock(void)
45{
46 local_irq_enable();
47}
48
49static void propagate_rate(struct clk *clk)
50{
51 struct clk *tmp_clk;
52
53 tmp_clk = clk;
54 while (tmp_clk->propagate_next) {
55 tmp_clk = tmp_clk->propagate_next;
56 local_set_rate(tmp_clk, tmp_clk->user_rate);
57 }
58}
59
60static inline void clk_reg_disable(struct clk *clk)
61{
62 if (clk->enable_reg)
63 __raw_writel(__raw_readl(clk->enable_reg) &
64 ~(1 << clk->enable_shift), clk->enable_reg);
65}
66
67static inline void clk_reg_enable(struct clk *clk)
68{
69 if (clk->enable_reg)
70 __raw_writel(__raw_readl(clk->enable_reg) |
71 (1 << clk->enable_shift), clk->enable_reg);
72}
73
74static inline void clk_reg_disable1(struct clk *clk)
75{
76 if (clk->enable_reg1)
77 __raw_writel(__raw_readl(clk->enable_reg1) &
78 ~(1 << clk->enable_shift1), clk->enable_reg1);
79}
80
81static inline void clk_reg_enable1(struct clk *clk)
82{
83 if (clk->enable_reg1)
84 __raw_writel(__raw_readl(clk->enable_reg1) |
85 (1 << clk->enable_shift1), clk->enable_reg1);
86}
87
88static int clk_wait_for_pll_lock(struct clk *clk)
89{
90 int i;
91 i = 0;
92 while (i++ < 0xFFF && !(__raw_readl(clk->scale_reg) & 1)) ; /*wait for PLL to lock */
93
94 if (!(__raw_readl(clk->scale_reg) & 1)) {
95 printk(KERN_ERR
96 "%s ERROR: failed to lock, scale reg data: %x\n",
97 clk->name, __raw_readl(clk->scale_reg));
98 return -1;
99 }
100 return 0;
101}
102
103static int switch_to_dirty_13mhz(struct clk *clk)
104{
105 int i;
106 int ret;
107 u32 tmp_reg;
108
109 ret = 0;
110
111 if (!clk->rate)
112 clk_reg_enable1(clk);
113
114 tmp_reg = __raw_readl(clk->parent_switch_reg);
115 /*if 13Mhz clock selected, select 13'MHz (dirty) source from OSC */
116 if (!(tmp_reg & 1)) {
117 tmp_reg |= (1 << 1); /* Trigger switch to 13'MHz (dirty) clock */
118 __raw_writel(tmp_reg, clk->parent_switch_reg);
119 i = 0;
120 while (i++ < 0xFFF && !(__raw_readl(clk->parent_switch_reg) & 1)) ; /*wait for 13'MHz selection status */
121
122 if (!(__raw_readl(clk->parent_switch_reg) & 1)) {
123 printk(KERN_ERR
124 "%s ERROR: failed to select 13'MHz, parent sw reg data: %x\n",
125 clk->name, __raw_readl(clk->parent_switch_reg));
126 ret = -1;
127 }
128 }
129
130 if (!clk->rate)
131 clk_reg_disable1(clk);
132
133 return ret;
134}
135
136static int switch_to_clean_13mhz(struct clk *clk)
137{
138 int i;
139 int ret;
140 u32 tmp_reg;
141
142 ret = 0;
143
144 if (!clk->rate)
145 clk_reg_enable1(clk);
146
147 tmp_reg = __raw_readl(clk->parent_switch_reg);
148 /*if 13'Mhz clock selected, select 13MHz (clean) source from OSC */
149 if (tmp_reg & 1) {
150 tmp_reg &= ~(1 << 1); /* Trigger switch to 13MHz (clean) clock */
151 __raw_writel(tmp_reg, clk->parent_switch_reg);
152 i = 0;
153 while (i++ < 0xFFF && (__raw_readl(clk->parent_switch_reg) & 1)) ; /*wait for 13MHz selection status */
154
155 if (__raw_readl(clk->parent_switch_reg) & 1) {
156 printk(KERN_ERR
157 "%s ERROR: failed to select 13MHz, parent sw reg data: %x\n",
158 clk->name, __raw_readl(clk->parent_switch_reg));
159 ret = -1;
160 }
161 }
162
163 if (!clk->rate)
164 clk_reg_disable1(clk);
165
166 return ret;
167}
168
169static int set_13MHz_parent(struct clk *clk, struct clk *parent)
170{
171 int ret = -EINVAL;
172
173 if (parent == &ck_13MHz)
174 ret = switch_to_clean_13mhz(clk);
175 else if (parent == &ck_pll1)
176 ret = switch_to_dirty_13mhz(clk);
177
178 return ret;
179}
180
181#define PLL160_MIN_FCCO 156000
182#define PLL160_MAX_FCCO 320000
183
184/*
185 * Calculate pll160 settings.
186 * Possible input: up to 320MHz with step of clk->parent->rate.
187 * In PNX4008 parent rate for pll160s may be either 1 or 13MHz.
188 * Ignored paths: "feedback" (bit 13 set), "div-by-N".
189 * Setting ARM PLL4 rate to 0 will put CPU into direct run mode.
190 * Setting PLL5 and PLL3 rate to 0 will disable USB and DSP clock input.
191 * Please refer to PNX4008 IC manual for details.
192 */
193
194static int pll160_set_rate(struct clk *clk, u32 rate)
195{
196 u32 tmp_reg, tmp_m, tmp_2p, i;
197 u32 parent_rate;
198 int ret = -EINVAL;
199
200 parent_rate = clk->parent->rate;
201
202 if (!parent_rate)
203 goto out;
204
205 /* set direct run for ARM or disable output for others */
206 clk_reg_disable(clk);
207
208 /* disable source input as well (ignored for ARM) */
209 clk_reg_disable1(clk);
210
211 tmp_reg = __raw_readl(clk->scale_reg);
212 tmp_reg &= ~0x1ffff; /*clear all settings, power down */
213 __raw_writel(tmp_reg, clk->scale_reg);
214
215 rate -= rate % parent_rate; /*round down the input */
216
217 if (rate > PLL160_MAX_FCCO)
218 rate = PLL160_MAX_FCCO;
219
220 if (!rate) {
221 clk->rate = 0;
222 ret = 0;
223 goto out;
224 }
225
226 clk_reg_enable1(clk);
227 tmp_reg = __raw_readl(clk->scale_reg);
228
229 if (rate == parent_rate) {
230 /*enter direct bypass mode */
231 tmp_reg |= ((1 << 14) | (1 << 15));
232 __raw_writel(tmp_reg, clk->scale_reg);
233 clk->rate = parent_rate;
234 clk_reg_enable(clk);
235 ret = 0;
236 goto out;
237 }
238
239 i = 0;
240 for (tmp_2p = 1; tmp_2p < 16; tmp_2p <<= 1) {
241 if (rate * tmp_2p >= PLL160_MIN_FCCO)
242 break;
243 i++;
244 }
245
246 if (tmp_2p > 1)
247 tmp_reg |= ((i - 1) << 11);
248 else
249 tmp_reg |= (1 << 14); /*direct mode, no divide */
250
251 tmp_m = rate * tmp_2p;
252 tmp_m /= parent_rate;
253
254 tmp_reg |= (tmp_m - 1) << 1; /*calculate M */
255 tmp_reg |= (1 << 16); /*power up PLL */
256 __raw_writel(tmp_reg, clk->scale_reg);
257
258 if (clk_wait_for_pll_lock(clk) < 0) {
259 clk_reg_disable(clk);
260 clk_reg_disable1(clk);
261
262 tmp_reg = __raw_readl(clk->scale_reg);
263 tmp_reg &= ~0x1ffff; /*clear all settings, power down */
264 __raw_writel(tmp_reg, clk->scale_reg);
265 clk->rate = 0;
266 ret = -EFAULT;
267 goto out;
268 }
269
270 clk->rate = (tmp_m * parent_rate) / tmp_2p;
271
272 if (clk->flags & RATE_PROPAGATES)
273 propagate_rate(clk);
274
275 clk_reg_enable(clk);
276 ret = 0;
277
278out:
279 return ret;
280}
281
282/*configure PER_CLK*/
283static int per_clk_set_rate(struct clk *clk, u32 rate)
284{
285 u32 tmp;
286
287 tmp = __raw_readl(clk->scale_reg);
288 tmp &= ~(0x1f << 2);
289 tmp |= ((clk->parent->rate / clk->rate) - 1) << 2;
290 __raw_writel(tmp, clk->scale_reg);
291 clk->rate = rate;
292 return 0;
293}
294
295/*configure HCLK*/
296static int hclk_set_rate(struct clk *clk, u32 rate)
297{
298 u32 tmp;
299 tmp = __raw_readl(clk->scale_reg);
300 tmp = tmp & ~0x3;
301 switch (rate) {
302 case 1:
303 break;
304 case 2:
305 tmp |= 1;
306 break;
307 case 4:
308 tmp |= 2;
309 break;
310 }
311
312 __raw_writel(tmp, clk->scale_reg);
313 clk->rate = rate;
314 return 0;
315}
316
317static u32 hclk_round_rate(struct clk *clk, u32 rate)
318{
319 switch (rate) {
320 case 1:
321 case 4:
322 return rate;
323 }
324 return 2;
325}
326
327static u32 per_clk_round_rate(struct clk *clk, u32 rate)
328{
329 return CLK_RATE_13MHZ;
330}
331
332static int on_off_set_rate(struct clk *clk, u32 rate)
333{
334 if (rate) {
335 clk_reg_enable(clk);
336 clk->rate = 1;
337 } else {
338 clk_reg_disable(clk);
339 clk->rate = 0;
340 }
341 return 0;
342}
343
344static int on_off_inv_set_rate(struct clk *clk, u32 rate)
345{
346 if (rate) {
347 clk_reg_disable(clk); /*enable bit is inverted */
348 clk->rate = 1;
349 } else {
350 clk_reg_enable(clk);
351 clk->rate = 0;
352 }
353 return 0;
354}
355
356static u32 on_off_round_rate(struct clk *clk, u32 rate)
357{
358 return (rate ? 1 : 0);
359}
360
361static u32 pll4_round_rate(struct clk *clk, u32 rate)
362{
363 if (rate > CLK_RATE_208MHZ)
364 rate = CLK_RATE_208MHZ;
365 if (rate == CLK_RATE_208MHZ && hclk_ck.user_rate == 1)
366 rate = CLK_RATE_208MHZ - CLK_RATE_13MHZ;
367 return (rate - (rate % (hclk_ck.user_rate * CLK_RATE_13MHZ)));
368}
369
370static u32 pll3_round_rate(struct clk *clk, u32 rate)
371{
372 if (rate > CLK_RATE_208MHZ)
373 rate = CLK_RATE_208MHZ;
374 return (rate - rate % CLK_RATE_13MHZ);
375}
376
377static u32 pll5_round_rate(struct clk *clk, u32 rate)
378{
379 return (rate ? CLK_RATE_48MHZ : 0);
380}
381
382static u32 ck_13MHz_round_rate(struct clk *clk, u32 rate)
383{
384 return (rate ? CLK_RATE_13MHZ : 0);
385}
386
387static int ck_13MHz_set_rate(struct clk *clk, u32 rate)
388{
389 if (rate) {
390 clk_reg_disable(clk); /*enable bit is inverted */
391 udelay(500);
392 clk->rate = CLK_RATE_13MHZ;
393 ck_1MHz.rate = CLK_RATE_1MHZ;
394 } else {
395 clk_reg_enable(clk);
396 clk->rate = 0;
397 ck_1MHz.rate = 0;
398 }
399 return 0;
400}
401
402static int pll1_set_rate(struct clk *clk, u32 rate)
403{
404#if 0 /* doesn't work on some boards, probably a HW BUG */
405 if (rate) {
406 clk_reg_disable(clk); /*enable bit is inverted */
407 if (!clk_wait_for_pll_lock(clk)) {
408 clk->rate = CLK_RATE_13MHZ;
409 } else {
410 clk_reg_enable(clk);
411 clk->rate = 0;
412 }
413
414 } else {
415 clk_reg_enable(clk);
416 clk->rate = 0;
417 }
418#endif
419 return 0;
420}
421
422/* Clock sources */
423
424static struct clk osc_13MHz = {
425 .name = "osc_13MHz",
426 .flags = FIXED_RATE,
427 .rate = CLK_RATE_13MHZ,
428};
429
430static struct clk ck_13MHz = {
431 .name = "ck_13MHz",
432 .parent = &osc_13MHz,
433 .flags = NEEDS_INITIALIZATION,
434 .round_rate = &ck_13MHz_round_rate,
435 .set_rate = &ck_13MHz_set_rate,
436 .enable_reg = OSC13CTRL_REG,
437 .enable_shift = 0,
438 .rate = CLK_RATE_13MHZ,
439};
440
441static struct clk osc_32KHz = {
442 .name = "osc_32KHz",
443 .flags = FIXED_RATE,
444 .rate = CLK_RATE_32KHZ,
445};
446
447/*attached to PLL5*/
448static struct clk ck_1MHz = {
449 .name = "ck_1MHz",
450 .flags = FIXED_RATE | PARENT_SET_RATE,
451 .parent = &ck_13MHz,
452};
453
454/* PLL1 (397) - provides 13' MHz clock */
455static struct clk ck_pll1 = {
456 .name = "ck_pll1",
457 .parent = &osc_32KHz,
458 .flags = NEEDS_INITIALIZATION,
459 .round_rate = &ck_13MHz_round_rate,
460 .set_rate = &pll1_set_rate,
461 .enable_reg = PLLCTRL_REG,
462 .enable_shift = 1,
463 .scale_reg = PLLCTRL_REG,
464 .rate = CLK_RATE_13MHZ,
465};
466
467/* CPU/Bus PLL */
468static struct clk ck_pll4 = {
469 .name = "ck_pll4",
470 .parent = &ck_pll1,
471 .flags = RATE_PROPAGATES | NEEDS_INITIALIZATION,
472 .propagate_next = &per_ck,
473 .round_rate = &pll4_round_rate,
474 .set_rate = &pll160_set_rate,
475 .rate = CLK_RATE_208MHZ,
476 .scale_reg = HCLKPLLCTRL_REG,
477 .enable_reg = PWRCTRL_REG,
478 .enable_shift = 2,
479 .parent_switch_reg = SYSCLKCTRL_REG,
480 .set_parent = &set_13MHz_parent,
481};
482
483/* USB PLL */
484static struct clk ck_pll5 = {
485 .name = "ck_pll5",
486 .parent = &ck_1MHz,
487 .flags = NEEDS_INITIALIZATION,
488 .round_rate = &pll5_round_rate,
489 .set_rate = &pll160_set_rate,
490 .scale_reg = USBCTRL_REG,
491 .enable_reg = USBCTRL_REG,
492 .enable_shift = 18,
493 .enable_reg1 = USBCTRL_REG,
494 .enable_shift1 = 17,
495};
496
497/* XPERTTeak DSP PLL */
498static struct clk ck_pll3 = {
499 .name = "ck_pll3",
500 .parent = &ck_pll1,
501 .flags = NEEDS_INITIALIZATION,
502 .round_rate = &pll3_round_rate,
503 .set_rate = &pll160_set_rate,
504 .scale_reg = DSPPLLCTRL_REG,
505 .enable_reg = DSPCLKCTRL_REG,
506 .enable_shift = 3,
507 .enable_reg1 = DSPCLKCTRL_REG,
508 .enable_shift1 = 2,
509 .parent_switch_reg = DSPCLKCTRL_REG,
510 .set_parent = &set_13MHz_parent,
511};
512
513static struct clk hclk_ck = {
514 .name = "hclk_ck",
515 .parent = &ck_pll4,
516 .flags = PARENT_SET_RATE,
517 .set_rate = &hclk_set_rate,
518 .round_rate = &hclk_round_rate,
519 .scale_reg = HCLKDIVCTRL_REG,
520 .rate = 2,
521 .user_rate = 2,
522};
523
524static struct clk per_ck = {
525 .name = "per_ck",
526 .parent = &ck_pll4,
527 .flags = FIXED_RATE,
528 .propagate_next = &hclk_ck,
529 .set_rate = &per_clk_set_rate,
530 .round_rate = &per_clk_round_rate,
531 .scale_reg = HCLKDIVCTRL_REG,
532 .rate = CLK_RATE_13MHZ,
533 .user_rate = CLK_RATE_13MHZ,
534};
535
536static struct clk m2hclk_ck = {
537 .name = "m2hclk_ck",
538 .parent = &hclk_ck,
539 .flags = NEEDS_INITIALIZATION,
540 .round_rate = &on_off_round_rate,
541 .set_rate = &on_off_inv_set_rate,
542 .rate = 1,
543 .enable_shift = 6,
544 .enable_reg = PWRCTRL_REG,
545};
546
547static struct clk vfp9_ck = {
548 .name = "vfp9_ck",
549 .parent = &ck_pll4,
550 .flags = NEEDS_INITIALIZATION,
551 .round_rate = &on_off_round_rate,
552 .set_rate = &on_off_set_rate,
553 .rate = 1,
554 .enable_shift = 4,
555 .enable_reg = VFP9CLKCTRL_REG,
556};
557
558static struct clk keyscan_ck = {
559 .name = "keyscan_ck",
560 .parent = &osc_32KHz,
561 .flags = NEEDS_INITIALIZATION,
562 .round_rate = &on_off_round_rate,
563 .set_rate = &on_off_set_rate,
564 .enable_shift = 0,
565 .enable_reg = KEYCLKCTRL_REG,
566};
567
568static struct clk touch_ck = {
569 .name = "touch_ck",
570 .parent = &osc_32KHz,
571 .flags = NEEDS_INITIALIZATION,
572 .round_rate = &on_off_round_rate,
573 .set_rate = &on_off_set_rate,
574 .enable_shift = 0,
575 .enable_reg = TSCLKCTRL_REG,
576};
577
578static struct clk pwm1_ck = {
579 .name = "pwm1_ck",
580 .parent = &osc_32KHz,
581 .flags = NEEDS_INITIALIZATION,
582 .round_rate = &on_off_round_rate,
583 .set_rate = &on_off_set_rate,
584 .enable_shift = 0,
585 .enable_reg = PWMCLKCTRL_REG,
586};
587
588static struct clk pwm2_ck = {
589 .name = "pwm2_ck",
590 .parent = &osc_32KHz,
591 .flags = NEEDS_INITIALIZATION,
592 .round_rate = &on_off_round_rate,
593 .set_rate = &on_off_set_rate,
594 .enable_shift = 2,
595 .enable_reg = PWMCLKCTRL_REG,
596};
597
598static struct clk jpeg_ck = {
599 .name = "jpeg_ck",
600 .parent = &hclk_ck,
601 .flags = NEEDS_INITIALIZATION,
602 .round_rate = &on_off_round_rate,
603 .set_rate = &on_off_set_rate,
604 .enable_shift = 0,
605 .enable_reg = JPEGCLKCTRL_REG,
606};
607
608static struct clk ms_ck = {
609 .name = "ms_ck",
610 .parent = &ck_pll4,
611 .flags = NEEDS_INITIALIZATION,
612 .round_rate = &on_off_round_rate,
613 .set_rate = &on_off_set_rate,
614 .enable_shift = 5,
615 .enable_reg = MSCTRL_REG,
616};
617
618static struct clk dum_ck = {
619 .name = "dum_ck",
620 .parent = &hclk_ck,
621 .flags = NEEDS_INITIALIZATION,
622 .round_rate = &on_off_round_rate,
623 .set_rate = &on_off_set_rate,
624 .enable_shift = 0,
625 .enable_reg = DUMCLKCTRL_REG,
626};
627
628static struct clk flash_ck = {
629 .name = "flash_ck",
630 .parent = &hclk_ck,
631 .round_rate = &on_off_round_rate,
632 .set_rate = &on_off_set_rate,
633 .enable_shift = 1, /* Only MLC clock supported */
634 .enable_reg = FLASHCLKCTRL_REG,
635};
636
637static struct clk i2c0_ck = {
638 .name = "i2c0_ck",
639 .parent = &per_ck,
640 .flags = NEEDS_INITIALIZATION,
641 .round_rate = &on_off_round_rate,
642 .set_rate = &on_off_set_rate,
643 .enable_shift = 0,
644 .enable_reg = I2CCLKCTRL_REG,
645};
646
647static struct clk i2c1_ck = {
648 .name = "i2c1_ck",
649 .parent = &per_ck,
650 .flags = NEEDS_INITIALIZATION,
651 .round_rate = &on_off_round_rate,
652 .set_rate = &on_off_set_rate,
653 .enable_shift = 1,
654 .enable_reg = I2CCLKCTRL_REG,
655};
656
657static struct clk i2c2_ck = {
658 .name = "i2c2_ck",
659 .parent = &per_ck,
660 .flags = NEEDS_INITIALIZATION,
661 .round_rate = &on_off_round_rate,
662 .set_rate = &on_off_set_rate,
663 .enable_shift = 2,
664 .enable_reg = USB_OTG_CLKCTRL_REG,
665};
666
667static struct clk spi0_ck = {
668 .name = "spi0_ck",
669 .parent = &hclk_ck,
670 .flags = NEEDS_INITIALIZATION,
671 .round_rate = &on_off_round_rate,
672 .set_rate = &on_off_set_rate,
673 .enable_shift = 0,
674 .enable_reg = SPICTRL_REG,
675};
676
677static struct clk spi1_ck = {
678 .name = "spi1_ck",
679 .parent = &hclk_ck,
680 .flags = NEEDS_INITIALIZATION,
681 .round_rate = &on_off_round_rate,
682 .set_rate = &on_off_set_rate,
683 .enable_shift = 4,
684 .enable_reg = SPICTRL_REG,
685};
686
687static struct clk dma_ck = {
688 .name = "dma_ck",
689 .parent = &hclk_ck,
690 .round_rate = &on_off_round_rate,
691 .set_rate = &on_off_set_rate,
692 .enable_shift = 0,
693 .enable_reg = DMACLKCTRL_REG,
694};
695
696static struct clk uart3_ck = {
697 .name = "uart3_ck",
698 .parent = &per_ck,
699 .flags = NEEDS_INITIALIZATION,
700 .round_rate = &on_off_round_rate,
701 .set_rate = &on_off_set_rate,
702 .rate = 1,
703 .enable_shift = 0,
704 .enable_reg = UARTCLKCTRL_REG,
705};
706
707static struct clk uart4_ck = {
708 .name = "uart4_ck",
709 .parent = &per_ck,
710 .flags = NEEDS_INITIALIZATION,
711 .round_rate = &on_off_round_rate,
712 .set_rate = &on_off_set_rate,
713 .enable_shift = 1,
714 .enable_reg = UARTCLKCTRL_REG,
715};
716
717static struct clk uart5_ck = {
718 .name = "uart5_ck",
719 .parent = &per_ck,
720 .flags = NEEDS_INITIALIZATION,
721 .round_rate = &on_off_round_rate,
722 .set_rate = &on_off_set_rate,
723 .rate = 1,
724 .enable_shift = 2,
725 .enable_reg = UARTCLKCTRL_REG,
726};
727
728static struct clk uart6_ck = {
729 .name = "uart6_ck",
730 .parent = &per_ck,
731 .flags = NEEDS_INITIALIZATION,
732 .round_rate = &on_off_round_rate,
733 .set_rate = &on_off_set_rate,
734 .enable_shift = 3,
735 .enable_reg = UARTCLKCTRL_REG,
736};
737
738/* These clocks are visible outside this module
739 * and can be initialized
740 */
741static struct clk *onchip_clks[] = {
742 &ck_13MHz,
743 &ck_pll1,
744 &ck_pll4,
745 &ck_pll5,
746 &ck_pll3,
747 &vfp9_ck,
748 &m2hclk_ck,
749 &hclk_ck,
750 &dma_ck,
751 &flash_ck,
752 &dum_ck,
753 &keyscan_ck,
754 &pwm1_ck,
755 &pwm2_ck,
756 &jpeg_ck,
757 &ms_ck,
758 &touch_ck,
759 &i2c0_ck,
760 &i2c1_ck,
761 &i2c2_ck,
762 &spi0_ck,
763 &spi1_ck,
764 &uart3_ck,
765 &uart4_ck,
766 &uart5_ck,
767 &uart6_ck,
768};
769
770static int local_set_rate(struct clk *clk, u32 rate)
771{
772 int ret = -EINVAL;
773 if (clk->set_rate) {
774
775 if (clk->user_rate == clk->rate && clk->parent->rate) {
776 /* if clock enabled or rate not set */
777 clk->user_rate = clk->round_rate(clk, rate);
778 ret = clk->set_rate(clk, clk->user_rate);
779 } else
780 clk->user_rate = clk->round_rate(clk, rate);
781 ret = 0;
782 }
783 return ret;
784}
785
786int clk_set_rate(struct clk *clk, unsigned long rate)
787{
788 int ret = -EINVAL;
789
790 if (clk->flags & FIXED_RATE)
791 goto out;
792
793 clock_lock();
794 if ((clk->flags & PARENT_SET_RATE) && clk->parent) {
795
796 clk->user_rate = clk->round_rate(clk, rate);
797 /* parent clock needs to be refreshed
798 for the setting to take effect */
799 } else {
800 ret = local_set_rate(clk, rate);
801 }
802 ret = 0;
803 clock_unlock();
804
805out:
806 return ret;
807}
808
809EXPORT_SYMBOL(clk_set_rate);
810
811struct clk *clk_get(struct device *dev, const char *id)
812{
813 struct clk *clk = ERR_PTR(-ENOENT);
814 struct clk **clkp;
815
816 clock_lock();
817 for (clkp = onchip_clks; clkp < onchip_clks + ARRAY_SIZE(onchip_clks);
818 clkp++) {
819 if (strcmp(id, (*clkp)->name) == 0
820 && try_module_get((*clkp)->owner)) {
821 clk = (*clkp);
822 break;
823 }
824 }
825 clock_unlock();
826
827 return clk;
828}
829EXPORT_SYMBOL(clk_get);
830
831void clk_put(struct clk *clk)
832{
833 clock_lock();
834 if (clk && !IS_ERR(clk))
835 module_put(clk->owner);
836 clock_unlock();
837}
838EXPORT_SYMBOL(clk_put);
839
840unsigned long clk_get_rate(struct clk *clk)
841{
842 unsigned long ret;
843 clock_lock();
844 ret = clk->rate;
845 clock_unlock();
846 return ret;
847}
848EXPORT_SYMBOL(clk_get_rate);
849
850static int local_clk_enable(struct clk *clk)
851{
852 int ret = 0;
853
854 if (!(clk->flags & FIXED_RATE) && !clk->rate && clk->set_rate
855 && clk->user_rate)
856 ret = clk->set_rate(clk, clk->user_rate);
857 return ret;
858}
859
860static void local_clk_disable(struct clk *clk)
861{
862 if (!(clk->flags & FIXED_RATE) && clk->rate && clk->set_rate)
863 clk->set_rate(clk, 0);
864}
865
866int clk_enable(struct clk *clk)
867{
868 int ret = 0;
869
870 clock_lock();
871 ret = local_clk_enable(clk);
872 clock_unlock();
873 return ret;
874}
875
876EXPORT_SYMBOL(clk_enable);
877
878void clk_disable(struct clk *clk)
879{
880 clock_lock();
881 local_clk_disable(clk);
882 clock_unlock();
883}
884
885EXPORT_SYMBOL(clk_disable);
886
887static void local_clk_unuse(struct clk *clk)
888{
889 if (clk->usecount > 0 && !(--clk->usecount)) {
890 local_clk_disable(clk);
891 if (clk->parent)
892 local_clk_unuse(clk->parent);
893 }
894}
895
896static int local_clk_use(struct clk *clk)
897{
898 int ret = 0;
899 if (clk->usecount++ == 0) {
900 if (clk->parent)
901 ret = local_clk_use(clk->parent);
902
903 if (ret != 0) {
904 clk->usecount--;
905 goto out;
906 }
907
908 ret = local_clk_enable(clk);
909
910 if (ret != 0 && clk->parent) {
911 local_clk_unuse(clk->parent);
912 clk->usecount--;
913 }
914 }
915out:
916 return ret;
917}
918
919/* The main purpose of clk_use ans clk_unuse functions
920 * is to control switching 13MHz oscillator and PLL1 (13'MHz),
921 * so that they are disabled whenever none of PLL2-5 is using them.
922 * Although in theory these functions should work with any clock,
923 * please use them only on PLL2 - PLL5 to avoid confusion.
924 */
925int clk_use(struct clk *clk)
926{
927 int ret = 0;
928
929 clock_lock();
930 ret = local_clk_use(clk);
931 clock_unlock();
932 return ret;
933}
934EXPORT_SYMBOL(clk_use);
935
936void clk_unuse(struct clk *clk)
937{
938
939 clock_lock();
940 local_clk_unuse(clk);
941 clock_unlock();
942}
943
944EXPORT_SYMBOL(clk_unuse);
945
946long clk_round_rate(struct clk *clk, unsigned long rate)
947{
948 long ret;
949 clock_lock();
950 if (clk->round_rate)
951 ret = clk->round_rate(clk, rate);
952 else
953 ret = clk->rate;
954 clock_unlock();
955 return ret;
956}
957
958EXPORT_SYMBOL(clk_round_rate);
959
960int clk_set_parent(struct clk *clk, struct clk *parent)
961{
962 int ret = -ENODEV;
963 if (!clk->set_parent)
964 goto out;
965
966 clock_lock();
967 ret = clk->set_parent(clk, parent);
968 if (!ret)
969 clk->parent = parent;
970 clock_unlock();
971
972out:
973 return ret;
974}
975
976EXPORT_SYMBOL(clk_set_parent);
977
978static int __init clk_init(void)
979{
980 struct clk **clkp;
981
982 /* Disable autoclocking, as it doesn't seem to work */
983 __raw_writel(0xff, AUTOCLK_CTRL);
984
985 for (clkp = onchip_clks; clkp < onchip_clks + ARRAY_SIZE(onchip_clks);
986 clkp++) {
987 if (((*clkp)->flags & NEEDS_INITIALIZATION)
988 && ((*clkp)->set_rate)) {
989 (*clkp)->user_rate = (*clkp)->rate;
990 local_set_rate((*clkp), (*clkp)->user_rate);
991 if ((*clkp)->set_parent)
992 (*clkp)->set_parent((*clkp), (*clkp)->parent);
993 }
994 pr_debug("%s: clock %s, rate %ld\n",
995 __FUNCTION__, (*clkp)->name, (*clkp)->rate);
996 }
997
998 clk_use(&ck_pll4);
999
1000 /* if ck_13MHz is not used, disable it. */
1001 if (ck_13MHz.usecount == 0)
1002 local_clk_disable(&ck_13MHz);
1003
1004 /* Disable autoclocking */
1005 __raw_writeb(0xff, AUTOCLK_CTRL);
1006
1007 return 0;
1008}
1009
1010arch_initcall(clk_init);
diff --git a/arch/arm/mach-pnx4008/clock.h b/arch/arm/mach-pnx4008/clock.h
new file mode 100644
index 000000000000..cd58f372cfd0
--- /dev/null
+++ b/arch/arm/mach-pnx4008/clock.h
@@ -0,0 +1,43 @@
1/*
2 * arch/arm/mach-pnx4008/clock.h
3 *
4 * Clock control driver for PNX4008 - internal header file
5 *
6 * Author: Vitaly Wool <source@mvista.com>
7 *
8 * 2006 (c) MontaVista Software, Inc. This file is licensed under
9 * the terms of the GNU General Public License version 2. This program
10 * is licensed "as is" without any warranty of any kind, whether express
11 * or implied.
12 */
13#ifndef __ARCH_ARM_PNX4008_CLOCK_H__
14#define __ARCH_ARM_PNX4008_CLOCK_H__
15
16struct clk {
17 struct list_head node;
18 struct module *owner;
19 const char *name;
20 struct clk *parent;
21 struct clk *propagate_next;
22 u32 rate;
23 u32 user_rate;
24 s8 usecount;
25 u32 flags;
26 u32 scale_reg;
27 u8 enable_shift;
28 u32 enable_reg;
29 u8 enable_shift1;
30 u32 enable_reg1;
31 u32 parent_switch_reg;
32 u32(*round_rate) (struct clk *, u32);
33 int (*set_rate) (struct clk *, u32);
34 int (*set_parent) (struct clk * clk, struct clk * parent);
35};
36
37/* Flags */
38#define RATE_PROPAGATES (1<<0)
39#define NEEDS_INITIALIZATION (1<<1)
40#define PARENT_SET_RATE (1<<2)
41#define FIXED_RATE (1<<3)
42
43#endif
diff --git a/arch/arm/mach-pnx4008/core.c b/arch/arm/mach-pnx4008/core.c
new file mode 100644
index 000000000000..ba91daad64fb
--- /dev/null
+++ b/arch/arm/mach-pnx4008/core.c
@@ -0,0 +1,207 @@
1/*
2 * arch/arm/mach-pnx4008/core.c
3 *
4 * PNX4008 core startup code
5 *
6 * Authors: Vitaly Wool, Dmitry Chigirev,
7 * Grigory Tolstolytkin, Dmitry Pervushin <source@mvista.com>
8 *
9 * Based on reference code received from Philips:
10 * Copyright (C) 2003 Philips Semiconductors
11 *
12 * 2005 (c) MontaVista Software, Inc. This file is licensed under
13 * the terms of the GNU General Public License version 2. This program
14 * is licensed "as is" without any warranty of any kind, whether express
15 * or implied.
16 */
17
18#include <linux/kernel.h>
19#include <linux/types.h>
20#include <linux/mm.h>
21#include <linux/interrupt.h>
22#include <linux/list.h>
23#include <linux/init.h>
24#include <linux/ioport.h>
25#include <linux/serial_8250.h>
26#include <linux/device.h>
27#include <linux/spi/spi.h>
28
29#include <asm/hardware.h>
30#include <asm/irq.h>
31#include <asm/io.h>
32#include <asm/setup.h>
33#include <asm/mach-types.h>
34#include <asm/pgtable.h>
35#include <asm/page.h>
36#include <asm/system.h>
37
38#include <asm/mach/arch.h>
39#include <asm/mach/irq.h>
40#include <asm/mach/map.h>
41#include <asm/mach/time.h>
42
43#include <asm/arch/irq.h>
44#include <asm/arch/clock.h>
45#include <asm/arch/dma.h>
46
47struct resource spipnx_0_resources[] = {
48 {
49 .start = PNX4008_SPI1_BASE,
50 .end = PNX4008_SPI1_BASE + SZ_4K,
51 .flags = IORESOURCE_MEM,
52 }, {
53 .start = PER_SPI1_REC_XMIT,
54 .flags = IORESOURCE_DMA,
55 }, {
56 .start = SPI1_INT,
57 .flags = IORESOURCE_IRQ,
58 }, {
59 .flags = 0,
60 },
61};
62
63struct resource spipnx_1_resources[] = {
64 {
65 .start = PNX4008_SPI2_BASE,
66 .end = PNX4008_SPI2_BASE + SZ_4K,
67 .flags = IORESOURCE_MEM,
68 }, {
69 .start = PER_SPI2_REC_XMIT,
70 .flags = IORESOURCE_DMA,
71 }, {
72 .start = SPI2_INT,
73 .flags = IORESOURCE_IRQ,
74 }, {
75 .flags = 0,
76 }
77};
78
79static struct spi_board_info spi_board_info[] __initdata = {
80 {
81 .modalias = "m25p80",
82 .max_speed_hz = 1000000,
83 .bus_num = 1,
84 .chip_select = 0,
85 },
86};
87
88static struct platform_device spipnx_1 = {
89 .name = "spipnx",
90 .id = 1,
91 .num_resources = ARRAY_SIZE(spipnx_0_resources),
92 .resource = spipnx_0_resources,
93 .dev = {
94 .coherent_dma_mask = 0xFFFFFFFF,
95 },
96};
97
98static struct platform_device spipnx_2 = {
99 .name = "spipnx",
100 .id = 2,
101 .num_resources = ARRAY_SIZE(spipnx_1_resources),
102 .resource = spipnx_1_resources,
103 .dev = {
104 .coherent_dma_mask = 0xFFFFFFFF,
105 },
106};
107
108static struct plat_serial8250_port platform_serial_ports[] = {
109 {
110 .membase = (void *)__iomem(IO_ADDRESS(PNX4008_UART5_BASE)),
111 .mapbase = (unsigned long)PNX4008_UART5_BASE,
112 .irq = IIR5_INT,
113 .uartclk = PNX4008_UART_CLK,
114 .regshift = 2,
115 .iotype = UPIO_MEM,
116 .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | UPF_SKIP_TEST,
117 },
118 {
119 .membase = (void *)__iomem(IO_ADDRESS(PNX4008_UART3_BASE)),
120 .mapbase = (unsigned long)PNX4008_UART3_BASE,
121 .irq = IIR3_INT,
122 .uartclk = PNX4008_UART_CLK,
123 .regshift = 2,
124 .iotype = UPIO_MEM,
125 .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | UPF_SKIP_TEST,
126 },
127 {}
128};
129
130static struct platform_device serial_device = {
131 .name = "serial8250",
132 .id = PLAT8250_DEV_PLATFORM,
133 .dev = {
134 .platform_data = &platform_serial_ports,
135 },
136};
137
138static struct platform_device *devices[] __initdata = {
139 &spipnx_1,
140 &spipnx_2,
141 &serial_device,
142};
143
144
145extern void pnx4008_uart_init(void);
146
147static void __init pnx4008_init(void)
148{
149 /*disable all START interrupt sources,
150 and clear all START interrupt flags */
151 __raw_writel(0, START_INT_ER_REG(SE_PIN_BASE_INT));
152 __raw_writel(0, START_INT_ER_REG(SE_INT_BASE_INT));
153 __raw_writel(0xffffffff, START_INT_RSR_REG(SE_PIN_BASE_INT));
154 __raw_writel(0xffffffff, START_INT_RSR_REG(SE_INT_BASE_INT));
155
156 platform_add_devices(devices, ARRAY_SIZE(devices));
157 spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
158 /* Switch on the UART clocks */
159 pnx4008_uart_init();
160}
161
162static struct map_desc pnx4008_io_desc[] __initdata = {
163 {
164 .virtual = IO_ADDRESS(PNX4008_IRAM_BASE),
165 .pfn = __phys_to_pfn(PNX4008_IRAM_BASE),
166 .length = SZ_64K,
167 .type = MT_DEVICE,
168 }, {
169 .virtual = IO_ADDRESS(PNX4008_NDF_FLASH_BASE),
170 .pfn = __phys_to_pfn(PNX4008_NDF_FLASH_BASE),
171 .length = SZ_1M - SZ_128K,
172 .type = MT_DEVICE,
173 }, {
174 .virtual = IO_ADDRESS(PNX4008_JPEG_CONFIG_BASE),
175 .pfn = __phys_to_pfn(PNX4008_JPEG_CONFIG_BASE),
176 .length = SZ_128K * 3,
177 .type = MT_DEVICE,
178 }, {
179 .virtual = IO_ADDRESS(PNX4008_DMA_CONFIG_BASE),
180 .pfn = __phys_to_pfn(PNX4008_DMA_CONFIG_BASE),
181 .length = SZ_1M,
182 .type = MT_DEVICE,
183 }, {
184 .virtual = IO_ADDRESS(PNX4008_AHB2FAB_BASE),
185 .pfn = __phys_to_pfn(PNX4008_AHB2FAB_BASE),
186 .length = SZ_1M,
187 .type = MT_DEVICE,
188 },
189};
190
191void __init pnx4008_map_io(void)
192{
193 iotable_init(pnx4008_io_desc, ARRAY_SIZE(pnx4008_io_desc));
194}
195
196extern struct sys_timer pnx4008_timer;
197
198MACHINE_START(PNX4008, "Philips PNX4008")
199 /* Maintainer: MontaVista Software Inc. */
200 .phys_io = 0x40090000,
201 .io_pg_offst = (0xf4090000 >> 18) & 0xfffc,
202 .boot_params = 0x80000100,
203 .map_io = pnx4008_map_io,
204 .init_irq = pnx4008_init_irq,
205 .init_machine = pnx4008_init,
206 .timer = &pnx4008_timer,
207MACHINE_END
diff --git a/arch/arm/mach-pnx4008/dma.c b/arch/arm/mach-pnx4008/dma.c
new file mode 100644
index 000000000000..981aa9dcdede
--- /dev/null
+++ b/arch/arm/mach-pnx4008/dma.c
@@ -0,0 +1,1109 @@
1/*
2 * linux/arch/arm/mach-pnx4008/dma.c
3 *
4 * PNX4008 DMA registration and IRQ dispatching
5 *
6 * Author: Vitaly Wool
7 * Copyright: MontaVista Software Inc. (c) 2005
8 *
9 * Based on the code from Nicolas Pitre
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
15
16#include <linux/module.h>
17#include <linux/init.h>
18#include <linux/kernel.h>
19#include <linux/interrupt.h>
20#include <linux/errno.h>
21#include <linux/err.h>
22#include <linux/dma-mapping.h>
23#include <linux/clk.h>
24
25#include <asm/system.h>
26#include <asm/irq.h>
27#include <asm/hardware.h>
28#include <asm/dma.h>
29#include <asm/dma-mapping.h>
30#include <asm/io.h>
31#include <asm/mach/dma.h>
32#include <asm/arch/clock.h>
33
34static struct dma_channel {
35 char *name;
36 void (*irq_handler) (int, int, void *, struct pt_regs *);
37 void *data;
38 struct pnx4008_dma_ll *ll;
39 u32 ll_dma;
40 void *target_addr;
41 int target_id;
42} dma_channels[MAX_DMA_CHANNELS];
43
44static struct ll_pool {
45 void *vaddr;
46 void *cur;
47 dma_addr_t dma_addr;
48 int count;
49} ll_pool;
50
51static spinlock_t ll_lock = SPIN_LOCK_UNLOCKED;
52
53struct pnx4008_dma_ll *pnx4008_alloc_ll_entry(dma_addr_t * ll_dma)
54{
55 struct pnx4008_dma_ll *ll = NULL;
56 unsigned long flags;
57
58 spin_lock_irqsave(&ll_lock, flags);
59 if (ll_pool.count > 4) { /* can give one more */
60 ll = *(struct pnx4008_dma_ll **) ll_pool.cur;
61 *ll_dma = ll_pool.dma_addr + ((void *)ll - ll_pool.vaddr);
62 *(void **)ll_pool.cur = **(void ***)ll_pool.cur;
63 memset(ll, 0, sizeof(*ll));
64 ll_pool.count--;
65 }
66 spin_unlock_irqrestore(&ll_lock, flags);
67
68 return ll;
69}
70
71EXPORT_SYMBOL_GPL(pnx4008_alloc_ll_entry);
72
73void pnx4008_free_ll_entry(struct pnx4008_dma_ll * ll, dma_addr_t ll_dma)
74{
75 unsigned long flags;
76
77 if (ll) {
78 if ((unsigned long)((long)ll - (long)ll_pool.vaddr) > 0x4000) {
79 printk(KERN_ERR "Trying to free entry not allocated by DMA\n");
80 BUG();
81 }
82
83 if (ll->flags & DMA_BUFFER_ALLOCATED)
84 ll->free(ll->alloc_data);
85
86 spin_lock_irqsave(&ll_lock, flags);
87 *(long *)ll = *(long *)ll_pool.cur;
88 *(long *)ll_pool.cur = (long)ll;
89 ll_pool.count++;
90 spin_unlock_irqrestore(&ll_lock, flags);
91 }
92}
93
94EXPORT_SYMBOL_GPL(pnx4008_free_ll_entry);
95
96void pnx4008_free_ll(u32 ll_dma, struct pnx4008_dma_ll * ll)
97{
98 struct pnx4008_dma_ll *ptr;
99 u32 dma;
100
101 while (ll) {
102 dma = ll->next_dma;
103 ptr = ll->next;
104 pnx4008_free_ll_entry(ll, ll_dma);
105
106 ll_dma = dma;
107 ll = ptr;
108 }
109}
110
111EXPORT_SYMBOL_GPL(pnx4008_free_ll);
112
113static int dma_channels_requested = 0;
114
115static inline void dma_increment_usage(void)
116{
117 if (!dma_channels_requested++) {
118 struct clk *clk = clk_get(0, "dma_ck");
119 if (!IS_ERR(clk)) {
120 clk_set_rate(clk, 1);
121 clk_put(clk);
122 }
123 pnx4008_config_dma(-1, -1, 1);
124 }
125}
126static inline void dma_decrement_usage(void)
127{
128 if (!--dma_channels_requested) {
129 struct clk *clk = clk_get(0, "dma_ck");
130 if (!IS_ERR(clk)) {
131 clk_set_rate(clk, 0);
132 clk_put(clk);
133 }
134 pnx4008_config_dma(-1, -1, 0);
135
136 }
137}
138
139static spinlock_t dma_lock = SPIN_LOCK_UNLOCKED;
140
141static inline void pnx4008_dma_lock(void)
142{
143 spin_lock_irq(&dma_lock);
144}
145
146static inline void pnx4008_dma_unlock(void)
147{
148 spin_unlock_irq(&dma_lock);
149}
150
151#define VALID_CHANNEL(c) (((c) >= 0) && ((c) < MAX_DMA_CHANNELS))
152
153int pnx4008_request_channel(char *name, int ch,
154 void (*irq_handler) (int, int, void *,
155 struct pt_regs *), void *data)
156{
157 int i, found = 0;
158
159 /* basic sanity checks */
160 if (!name || (ch != -1 && !VALID_CHANNEL(ch)))
161 return -EINVAL;
162
163 pnx4008_dma_lock();
164
165 /* try grabbing a DMA channel with the requested priority */
166 for (i = MAX_DMA_CHANNELS - 1; i >= 0; i--) {
167 if (!dma_channels[i].name && (ch == -1 || ch == i)) {
168 found = 1;
169 break;
170 }
171 }
172
173 if (found) {
174 dma_increment_usage();
175 dma_channels[i].name = name;
176 dma_channels[i].irq_handler = irq_handler;
177 dma_channels[i].data = data;
178 dma_channels[i].ll = NULL;
179 dma_channels[i].ll_dma = 0;
180 } else {
181 printk(KERN_WARNING "No more available DMA channels for %s\n",
182 name);
183 i = -ENODEV;
184 }
185
186 pnx4008_dma_unlock();
187 return i;
188}
189
190EXPORT_SYMBOL_GPL(pnx4008_request_channel);
191
192void pnx4008_free_channel(int ch)
193{
194 if (!dma_channels[ch].name) {
195 printk(KERN_CRIT
196 "%s: trying to free channel %d which is already freed\n",
197 __FUNCTION__, ch);
198 return;
199 }
200
201 pnx4008_dma_lock();
202 pnx4008_free_ll(dma_channels[ch].ll_dma, dma_channels[ch].ll);
203 dma_channels[ch].ll = NULL;
204 dma_decrement_usage();
205
206 dma_channels[ch].name = NULL;
207 pnx4008_dma_unlock();
208}
209
210EXPORT_SYMBOL_GPL(pnx4008_free_channel);
211
212int pnx4008_config_dma(int ahb_m1_be, int ahb_m2_be, int enable)
213{
214 unsigned long dma_cfg = __raw_readl(DMAC_CONFIG);
215
216 switch (ahb_m1_be) {
217 case 0:
218 dma_cfg &= ~(1 << 1);
219 break;
220 case 1:
221 dma_cfg |= (1 << 1);
222 break;
223 default:
224 break;
225 }
226
227 switch (ahb_m2_be) {
228 case 0:
229 dma_cfg &= ~(1 << 2);
230 break;
231 case 1:
232 dma_cfg |= (1 << 2);
233 break;
234 default:
235 break;
236 }
237
238 switch (enable) {
239 case 0:
240 dma_cfg &= ~(1 << 0);
241 break;
242 case 1:
243 dma_cfg |= (1 << 0);
244 break;
245 default:
246 break;
247 }
248
249 pnx4008_dma_lock();
250 __raw_writel(dma_cfg, DMAC_CONFIG);
251 pnx4008_dma_unlock();
252
253 return 0;
254}
255
256EXPORT_SYMBOL_GPL(pnx4008_config_dma);
257
258int pnx4008_dma_pack_control(const struct pnx4008_dma_ch_ctrl * ch_ctrl,
259 unsigned long *ctrl)
260{
261 int i = 0, dbsize, sbsize, err = 0;
262
263 if (!ctrl || !ch_ctrl) {
264 err = -EINVAL;
265 goto out;
266 }
267
268 *ctrl = 0;
269
270 switch (ch_ctrl->tc_mask) {
271 case 0:
272 break;
273 case 1:
274 *ctrl |= (1 << 31);
275 break;
276
277 default:
278 err = -EINVAL;
279 goto out;
280 }
281
282 switch (ch_ctrl->cacheable) {
283 case 0:
284 break;
285 case 1:
286 *ctrl |= (1 << 30);
287 break;
288
289 default:
290 err = -EINVAL;
291 goto out;
292 }
293 switch (ch_ctrl->bufferable) {
294 case 0:
295 break;
296 case 1:
297 *ctrl |= (1 << 29);
298 break;
299
300 default:
301 err = -EINVAL;
302 goto out;
303 }
304 switch (ch_ctrl->priv_mode) {
305 case 0:
306 break;
307 case 1:
308 *ctrl |= (1 << 28);
309 break;
310
311 default:
312 err = -EINVAL;
313 goto out;
314 }
315 switch (ch_ctrl->di) {
316 case 0:
317 break;
318 case 1:
319 *ctrl |= (1 << 27);
320 break;
321
322 default:
323 err = -EINVAL;
324 goto out;
325 }
326 switch (ch_ctrl->si) {
327 case 0:
328 break;
329 case 1:
330 *ctrl |= (1 << 26);
331 break;
332
333 default:
334 err = -EINVAL;
335 goto out;
336 }
337 switch (ch_ctrl->dest_ahb1) {
338 case 0:
339 break;
340 case 1:
341 *ctrl |= (1 << 25);
342 break;
343
344 default:
345 err = -EINVAL;
346 goto out;
347 }
348 switch (ch_ctrl->src_ahb1) {
349 case 0:
350 break;
351 case 1:
352 *ctrl |= (1 << 24);
353 break;
354
355 default:
356 err = -EINVAL;
357 goto out;
358 }
359 switch (ch_ctrl->dwidth) {
360 case WIDTH_BYTE:
361 *ctrl &= ~(7 << 21);
362 break;
363 case WIDTH_HWORD:
364 *ctrl &= ~(7 << 21);
365 *ctrl |= (1 << 21);
366 break;
367 case WIDTH_WORD:
368 *ctrl &= ~(7 << 21);
369 *ctrl |= (2 << 21);
370 break;
371
372 default:
373 err = -EINVAL;
374 goto out;
375 }
376 switch (ch_ctrl->swidth) {
377 case WIDTH_BYTE:
378 *ctrl &= ~(7 << 18);
379 break;
380 case WIDTH_HWORD:
381 *ctrl &= ~(7 << 18);
382 *ctrl |= (1 << 18);
383 break;
384 case WIDTH_WORD:
385 *ctrl &= ~(7 << 18);
386 *ctrl |= (2 << 18);
387 break;
388
389 default:
390 err = -EINVAL;
391 goto out;
392 }
393 dbsize = ch_ctrl->dbsize;
394 while (!(dbsize & 1)) {
395 i++;
396 dbsize >>= 1;
397 }
398 if (ch_ctrl->dbsize != 1 || i > 8 || i == 1) {
399 err = -EINVAL;
400 goto out;
401 } else if (i > 1)
402 i--;
403 *ctrl &= ~(7 << 15);
404 *ctrl |= (i << 15);
405
406 sbsize = ch_ctrl->sbsize;
407 while (!(sbsize & 1)) {
408 i++;
409 sbsize >>= 1;
410 }
411 if (ch_ctrl->sbsize != 1 || i > 8 || i == 1) {
412 err = -EINVAL;
413 goto out;
414 } else if (i > 1)
415 i--;
416 *ctrl &= ~(7 << 12);
417 *ctrl |= (i << 12);
418
419 if (ch_ctrl->tr_size > 0x7ff) {
420 err = -E2BIG;
421 goto out;
422 }
423 *ctrl &= ~0x7ff;
424 *ctrl |= ch_ctrl->tr_size & 0x7ff;
425
426out:
427 return err;
428}
429
430EXPORT_SYMBOL_GPL(pnx4008_dma_pack_control);
431
432int pnx4008_dma_parse_control(unsigned long ctrl,
433 struct pnx4008_dma_ch_ctrl * ch_ctrl)
434{
435 int err = 0;
436
437 if (!ch_ctrl) {
438 err = -EINVAL;
439 goto out;
440 }
441
442 ch_ctrl->tr_size = ctrl & 0x7ff;
443 ctrl >>= 12;
444
445 ch_ctrl->sbsize = 1 << (ctrl & 7);
446 if (ch_ctrl->sbsize > 1)
447 ch_ctrl->sbsize <<= 1;
448 ctrl >>= 3;
449
450 ch_ctrl->dbsize = 1 << (ctrl & 7);
451 if (ch_ctrl->dbsize > 1)
452 ch_ctrl->dbsize <<= 1;
453 ctrl >>= 3;
454
455 switch (ctrl & 7) {
456 case 0:
457 ch_ctrl->swidth = WIDTH_BYTE;
458 break;
459 case 1:
460 ch_ctrl->swidth = WIDTH_HWORD;
461 break;
462 case 2:
463 ch_ctrl->swidth = WIDTH_WORD;
464 break;
465 default:
466 err = -EINVAL;
467 goto out;
468 }
469 ctrl >>= 3;
470
471 switch (ctrl & 7) {
472 case 0:
473 ch_ctrl->dwidth = WIDTH_BYTE;
474 break;
475 case 1:
476 ch_ctrl->dwidth = WIDTH_HWORD;
477 break;
478 case 2:
479 ch_ctrl->dwidth = WIDTH_WORD;
480 break;
481 default:
482 err = -EINVAL;
483 goto out;
484 }
485 ctrl >>= 3;
486
487 ch_ctrl->src_ahb1 = ctrl & 1;
488 ctrl >>= 1;
489
490 ch_ctrl->dest_ahb1 = ctrl & 1;
491 ctrl >>= 1;
492
493 ch_ctrl->si = ctrl & 1;
494 ctrl >>= 1;
495
496 ch_ctrl->di = ctrl & 1;
497 ctrl >>= 1;
498
499 ch_ctrl->priv_mode = ctrl & 1;
500 ctrl >>= 1;
501
502 ch_ctrl->bufferable = ctrl & 1;
503 ctrl >>= 1;
504
505 ch_ctrl->cacheable = ctrl & 1;
506 ctrl >>= 1;
507
508 ch_ctrl->tc_mask = ctrl & 1;
509
510out:
511 return err;
512}
513
514EXPORT_SYMBOL_GPL(pnx4008_dma_parse_control);
515
516int pnx4008_dma_pack_config(const struct pnx4008_dma_ch_config * ch_cfg,
517 unsigned long *cfg)
518{
519 int err = 0;
520
521 if (!cfg || !ch_cfg) {
522 err = -EINVAL;
523 goto out;
524 }
525
526 *cfg = 0;
527
528 switch (ch_cfg->halt) {
529 case 0:
530 break;
531 case 1:
532 *cfg |= (1 << 18);
533 break;
534
535 default:
536 err = -EINVAL;
537 goto out;
538 }
539 switch (ch_cfg->active) {
540 case 0:
541 break;
542 case 1:
543 *cfg |= (1 << 17);
544 break;
545
546 default:
547 err = -EINVAL;
548 goto out;
549 }
550 switch (ch_cfg->lock) {
551 case 0:
552 break;
553 case 1:
554 *cfg |= (1 << 16);
555 break;
556
557 default:
558 err = -EINVAL;
559 goto out;
560 }
561 switch (ch_cfg->itc) {
562 case 0:
563 break;
564 case 1:
565 *cfg |= (1 << 15);
566 break;
567
568 default:
569 err = -EINVAL;
570 goto out;
571 }
572 switch (ch_cfg->ie) {
573 case 0:
574 break;
575 case 1:
576 *cfg |= (1 << 14);
577 break;
578
579 default:
580 err = -EINVAL;
581 goto out;
582 }
583 switch (ch_cfg->flow_cntrl) {
584 case FC_MEM2MEM_DMA:
585 *cfg &= ~(7 << 11);
586 break;
587 case FC_MEM2PER_DMA:
588 *cfg &= ~(7 << 11);
589 *cfg |= (1 << 11);
590 break;
591 case FC_PER2MEM_DMA:
592 *cfg &= ~(7 << 11);
593 *cfg |= (2 << 11);
594 break;
595 case FC_PER2PER_DMA:
596 *cfg &= ~(7 << 11);
597 *cfg |= (3 << 11);
598 break;
599 case FC_PER2PER_DPER:
600 *cfg &= ~(7 << 11);
601 *cfg |= (4 << 11);
602 break;
603 case FC_MEM2PER_PER:
604 *cfg &= ~(7 << 11);
605 *cfg |= (5 << 11);
606 break;
607 case FC_PER2MEM_PER:
608 *cfg &= ~(7 << 11);
609 *cfg |= (6 << 11);
610 break;
611 case FC_PER2PER_SPER:
612 *cfg |= (7 << 11);
613 break;
614
615 default:
616 err = -EINVAL;
617 goto out;
618 }
619 *cfg &= ~(0x1f << 6);
620 *cfg |= ((ch_cfg->dest_per & 0x1f) << 6);
621
622 *cfg &= ~(0x1f << 1);
623 *cfg |= ((ch_cfg->src_per & 0x1f) << 1);
624
625out:
626 return err;
627}
628
629EXPORT_SYMBOL_GPL(pnx4008_dma_pack_config);
630
631int pnx4008_dma_parse_config(unsigned long cfg,
632 struct pnx4008_dma_ch_config * ch_cfg)
633{
634 int err = 0;
635
636 if (!ch_cfg) {
637 err = -EINVAL;
638 goto out;
639 }
640
641 cfg >>= 1;
642
643 ch_cfg->src_per = cfg & 0x1f;
644 cfg >>= 5;
645
646 ch_cfg->dest_per = cfg & 0x1f;
647 cfg >>= 5;
648
649 switch (cfg & 7) {
650 case 0:
651 ch_cfg->flow_cntrl = FC_MEM2MEM_DMA;
652 break;
653 case 1:
654 ch_cfg->flow_cntrl = FC_MEM2PER_DMA;
655 break;
656 case 2:
657 ch_cfg->flow_cntrl = FC_PER2MEM_DMA;
658 break;
659 case 3:
660 ch_cfg->flow_cntrl = FC_PER2PER_DMA;
661 break;
662 case 4:
663 ch_cfg->flow_cntrl = FC_PER2PER_DPER;
664 break;
665 case 5:
666 ch_cfg->flow_cntrl = FC_MEM2PER_PER;
667 break;
668 case 6:
669 ch_cfg->flow_cntrl = FC_PER2MEM_PER;
670 break;
671 case 7:
672 ch_cfg->flow_cntrl = FC_PER2PER_SPER;
673 }
674 cfg >>= 3;
675
676 ch_cfg->ie = cfg & 1;
677 cfg >>= 1;
678
679 ch_cfg->itc = cfg & 1;
680 cfg >>= 1;
681
682 ch_cfg->lock = cfg & 1;
683 cfg >>= 1;
684
685 ch_cfg->active = cfg & 1;
686 cfg >>= 1;
687
688 ch_cfg->halt = cfg & 1;
689
690out:
691 return err;
692}
693
694EXPORT_SYMBOL_GPL(pnx4008_dma_parse_config);
695
696void pnx4008_dma_split_head_entry(struct pnx4008_dma_config * config,
697 struct pnx4008_dma_ch_ctrl * ctrl)
698{
699 int new_len = ctrl->tr_size, num_entries = 0;
700 int old_len = new_len;
701 int src_width, dest_width, count = 1;
702
703 switch (ctrl->swidth) {
704 case WIDTH_BYTE:
705 src_width = 1;
706 break;
707 case WIDTH_HWORD:
708 src_width = 2;
709 break;
710 case WIDTH_WORD:
711 src_width = 4;
712 break;
713 default:
714 return;
715 }
716
717 switch (ctrl->dwidth) {
718 case WIDTH_BYTE:
719 dest_width = 1;
720 break;
721 case WIDTH_HWORD:
722 dest_width = 2;
723 break;
724 case WIDTH_WORD:
725 dest_width = 4;
726 break;
727 default:
728 return;
729 }
730
731 while (new_len > 0x7FF) {
732 num_entries++;
733 new_len = (ctrl->tr_size + num_entries) / (num_entries + 1);
734 }
735 if (num_entries != 0) {
736 struct pnx4008_dma_ll *ll = NULL;
737 config->ch_ctrl &= ~0x7ff;
738 config->ch_ctrl |= new_len;
739 if (!config->is_ll) {
740 config->is_ll = 1;
741 while (num_entries) {
742 if (!ll) {
743 config->ll =
744 pnx4008_alloc_ll_entry(&config->
745 ll_dma);
746 ll = config->ll;
747 } else {
748 ll->next =
749 pnx4008_alloc_ll_entry(&ll->
750 next_dma);
751 ll = ll->next;
752 }
753
754 if (ctrl->si)
755 ll->src_addr =
756 config->src_addr +
757 src_width * new_len * count;
758 else
759 ll->src_addr = config->src_addr;
760 if (ctrl->di)
761 ll->dest_addr =
762 config->dest_addr +
763 dest_width * new_len * count;
764 else
765 ll->dest_addr = config->dest_addr;
766 ll->ch_ctrl = config->ch_ctrl & 0x7fffffff;
767 ll->next_dma = 0;
768 ll->next = NULL;
769 num_entries--;
770 count++;
771 }
772 } else {
773 struct pnx4008_dma_ll *ll_old = config->ll;
774 unsigned long ll_dma_old = config->ll_dma;
775 while (num_entries) {
776 if (!ll) {
777 config->ll =
778 pnx4008_alloc_ll_entry(&config->
779 ll_dma);
780 ll = config->ll;
781 } else {
782 ll->next =
783 pnx4008_alloc_ll_entry(&ll->
784 next_dma);
785 ll = ll->next;
786 }
787
788 if (ctrl->si)
789 ll->src_addr =
790 config->src_addr +
791 src_width * new_len * count;
792 else
793 ll->src_addr = config->src_addr;
794 if (ctrl->di)
795 ll->dest_addr =
796 config->dest_addr +
797 dest_width * new_len * count;
798 else
799 ll->dest_addr = config->dest_addr;
800 ll->ch_ctrl = config->ch_ctrl & 0x7fffffff;
801 ll->next_dma = 0;
802 ll->next = NULL;
803 num_entries--;
804 count++;
805 }
806 ll->next_dma = ll_dma_old;
807 ll->next = ll_old;
808 }
809 /* adjust last length/tc */
810 ll->ch_ctrl = config->ch_ctrl & (~0x7ff);
811 ll->ch_ctrl |= old_len - new_len * (count - 1);
812 config->ch_ctrl &= 0x7fffffff;
813 }
814}
815
816EXPORT_SYMBOL_GPL(pnx4008_dma_split_head_entry);
817
818void pnx4008_dma_split_ll_entry(struct pnx4008_dma_ll * cur_ll,
819 struct pnx4008_dma_ch_ctrl * ctrl)
820{
821 int new_len = ctrl->tr_size, num_entries = 0;
822 int old_len = new_len;
823 int src_width, dest_width, count = 1;
824
825 switch (ctrl->swidth) {
826 case WIDTH_BYTE:
827 src_width = 1;
828 break;
829 case WIDTH_HWORD:
830 src_width = 2;
831 break;
832 case WIDTH_WORD:
833 src_width = 4;
834 break;
835 default:
836 return;
837 }
838
839 switch (ctrl->dwidth) {
840 case WIDTH_BYTE:
841 dest_width = 1;
842 break;
843 case WIDTH_HWORD:
844 dest_width = 2;
845 break;
846 case WIDTH_WORD:
847 dest_width = 4;
848 break;
849 default:
850 return;
851 }
852
853 while (new_len > 0x7FF) {
854 num_entries++;
855 new_len = (ctrl->tr_size + num_entries) / (num_entries + 1);
856 }
857 if (num_entries != 0) {
858 struct pnx4008_dma_ll *ll = NULL;
859 cur_ll->ch_ctrl &= ~0x7ff;
860 cur_ll->ch_ctrl |= new_len;
861 if (!cur_ll->next) {
862 while (num_entries) {
863 if (!ll) {
864 cur_ll->next =
865 pnx4008_alloc_ll_entry(&cur_ll->
866 next_dma);
867 ll = cur_ll->next;
868 } else {
869 ll->next =
870 pnx4008_alloc_ll_entry(&ll->
871 next_dma);
872 ll = ll->next;
873 }
874
875 if (ctrl->si)
876 ll->src_addr =
877 cur_ll->src_addr +
878 src_width * new_len * count;
879 else
880 ll->src_addr = cur_ll->src_addr;
881 if (ctrl->di)
882 ll->dest_addr =
883 cur_ll->dest_addr +
884 dest_width * new_len * count;
885 else
886 ll->dest_addr = cur_ll->dest_addr;
887 ll->ch_ctrl = cur_ll->ch_ctrl & 0x7fffffff;
888 ll->next_dma = 0;
889 ll->next = NULL;
890 num_entries--;
891 count++;
892 }
893 } else {
894 struct pnx4008_dma_ll *ll_old = cur_ll->next;
895 unsigned long ll_dma_old = cur_ll->next_dma;
896 while (num_entries) {
897 if (!ll) {
898 cur_ll->next =
899 pnx4008_alloc_ll_entry(&cur_ll->
900 next_dma);
901 ll = cur_ll->next;
902 } else {
903 ll->next =
904 pnx4008_alloc_ll_entry(&ll->
905 next_dma);
906 ll = ll->next;
907 }
908
909 if (ctrl->si)
910 ll->src_addr =
911 cur_ll->src_addr +
912 src_width * new_len * count;
913 else
914 ll->src_addr = cur_ll->src_addr;
915 if (ctrl->di)
916 ll->dest_addr =
917 cur_ll->dest_addr +
918 dest_width * new_len * count;
919 else
920 ll->dest_addr = cur_ll->dest_addr;
921 ll->ch_ctrl = cur_ll->ch_ctrl & 0x7fffffff;
922 ll->next_dma = 0;
923 ll->next = NULL;
924 num_entries--;
925 count++;
926 }
927
928 ll->next_dma = ll_dma_old;
929 ll->next = ll_old;
930 }
931 /* adjust last length/tc */
932 ll->ch_ctrl = cur_ll->ch_ctrl & (~0x7ff);
933 ll->ch_ctrl |= old_len - new_len * (count - 1);
934 cur_ll->ch_ctrl &= 0x7fffffff;
935 }
936}
937
938EXPORT_SYMBOL_GPL(pnx4008_dma_split_ll_entry);
939
940int pnx4008_config_channel(int ch, struct pnx4008_dma_config * config)
941{
942 if (!VALID_CHANNEL(ch) || !dma_channels[ch].name)
943 return -EINVAL;
944
945 pnx4008_dma_lock();
946 __raw_writel(config->src_addr, DMAC_Cx_SRC_ADDR(ch));
947 __raw_writel(config->dest_addr, DMAC_Cx_DEST_ADDR(ch));
948
949 if (config->is_ll)
950 __raw_writel(config->ll_dma, DMAC_Cx_LLI(ch));
951 else
952 __raw_writel(0, DMAC_Cx_LLI(ch));
953
954 __raw_writel(config->ch_ctrl, DMAC_Cx_CONTROL(ch));
955 __raw_writel(config->ch_cfg, DMAC_Cx_CONFIG(ch));
956 pnx4008_dma_unlock();
957
958 return 0;
959
960}
961
962EXPORT_SYMBOL_GPL(pnx4008_config_channel);
963
964int pnx4008_channel_get_config(int ch, struct pnx4008_dma_config * config)
965{
966 if (!VALID_CHANNEL(ch) || !dma_channels[ch].name || !config)
967 return -EINVAL;
968
969 pnx4008_dma_lock();
970 config->ch_cfg = __raw_readl(DMAC_Cx_CONFIG(ch));
971 config->ch_ctrl = __raw_readl(DMAC_Cx_CONTROL(ch));
972
973 config->ll_dma = __raw_readl(DMAC_Cx_LLI(ch));
974 config->is_ll = config->ll_dma ? 1 : 0;
975
976 config->src_addr = __raw_readl(DMAC_Cx_SRC_ADDR(ch));
977 config->dest_addr = __raw_readl(DMAC_Cx_DEST_ADDR(ch));
978 pnx4008_dma_unlock();
979
980 return 0;
981}
982
983EXPORT_SYMBOL_GPL(pnx4008_channel_get_config);
984
985int pnx4008_dma_ch_enable(int ch)
986{
987 unsigned long ch_cfg;
988
989 if (!VALID_CHANNEL(ch) || !dma_channels[ch].name)
990 return -EINVAL;
991
992 pnx4008_dma_lock();
993 ch_cfg = __raw_readl(DMAC_Cx_CONFIG(ch));
994 ch_cfg |= 1;
995 __raw_writel(ch_cfg, DMAC_Cx_CONFIG(ch));
996 pnx4008_dma_unlock();
997
998 return 0;
999}
1000
1001EXPORT_SYMBOL_GPL(pnx4008_dma_ch_enable);
1002
1003int pnx4008_dma_ch_disable(int ch)
1004{
1005 unsigned long ch_cfg;
1006
1007 if (!VALID_CHANNEL(ch) || !dma_channels[ch].name)
1008 return -EINVAL;
1009
1010 pnx4008_dma_lock();
1011 ch_cfg = __raw_readl(DMAC_Cx_CONFIG(ch));
1012 ch_cfg &= ~1;
1013 __raw_writel(ch_cfg, DMAC_Cx_CONFIG(ch));
1014 pnx4008_dma_unlock();
1015
1016 return 0;
1017}
1018
1019EXPORT_SYMBOL_GPL(pnx4008_dma_ch_disable);
1020
1021int pnx4008_dma_ch_enabled(int ch)
1022{
1023 unsigned long ch_cfg;
1024
1025 if (!VALID_CHANNEL(ch) || !dma_channels[ch].name)
1026 return -EINVAL;
1027
1028 pnx4008_dma_lock();
1029 ch_cfg = __raw_readl(DMAC_Cx_CONFIG(ch));
1030 pnx4008_dma_unlock();
1031
1032 return ch_cfg & 1;
1033}
1034
1035EXPORT_SYMBOL_GPL(pnx4008_dma_ch_enabled);
1036
1037static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
1038{
1039 int i;
1040 unsigned long dint = __raw_readl(DMAC_INT_STAT);
1041 unsigned long tcint = __raw_readl(DMAC_INT_TC_STAT);
1042 unsigned long eint = __raw_readl(DMAC_INT_ERR_STAT);
1043 unsigned long i_bit;
1044
1045 for (i = MAX_DMA_CHANNELS - 1; i >= 0; i--) {
1046 i_bit = 1 << i;
1047 if (dint & i_bit) {
1048 struct dma_channel *channel = &dma_channels[i];
1049
1050 if (channel->name && channel->irq_handler) {
1051 int cause = 0;
1052
1053 if (eint & i_bit)
1054 cause |= DMA_ERR_INT;
1055 if (tcint & i_bit)
1056 cause |= DMA_TC_INT;
1057 channel->irq_handler(i, cause, channel->data,
1058 regs);
1059 } else {
1060 /*
1061 * IRQ for an unregistered DMA channel
1062 */
1063 printk(KERN_WARNING
1064 "spurious IRQ for DMA channel %d\n", i);
1065 }
1066 if (tcint & i_bit)
1067 __raw_writel(i_bit, DMAC_INT_TC_CLEAR);
1068 if (eint & i_bit)
1069 __raw_writel(i_bit, DMAC_INT_ERR_CLEAR);
1070 }
1071 }
1072 return IRQ_HANDLED;
1073}
1074
1075static int __init pnx4008_dma_init(void)
1076{
1077 int ret, i;
1078
1079 ret = request_irq(DMA_INT, dma_irq_handler, 0, "DMA", NULL);
1080 if (ret) {
1081 printk(KERN_CRIT "Wow! Can't register IRQ for DMA\n");
1082 goto out;
1083 }
1084
1085 ll_pool.count = 0x4000 / sizeof(struct pnx4008_dma_ll);
1086 ll_pool.cur = ll_pool.vaddr =
1087 dma_alloc_coherent(NULL, ll_pool.count * sizeof(struct pnx4008_dma_ll),
1088 &ll_pool.dma_addr, GFP_KERNEL);
1089
1090 if (!ll_pool.vaddr) {
1091 ret = -ENOMEM;
1092 free_irq(DMA_INT, NULL);
1093 goto out;
1094 }
1095
1096 for (i = 0; i < ll_pool.count - 1; i++) {
1097 void **addr = ll_pool.vaddr + i * sizeof(struct pnx4008_dma_ll);
1098 *addr = (void *)addr + sizeof(struct pnx4008_dma_ll);
1099 }
1100 *(long *)(ll_pool.vaddr +
1101 (ll_pool.count - 1) * sizeof(struct pnx4008_dma_ll)) =
1102 (long)ll_pool.vaddr;
1103
1104 __raw_writel(1, DMAC_CONFIG);
1105
1106out:
1107 return ret;
1108}
1109arch_initcall(pnx4008_dma_init);
diff --git a/arch/arm/mach-pnx4008/gpio.c b/arch/arm/mach-pnx4008/gpio.c
new file mode 100644
index 000000000000..e1ce050d8fe0
--- /dev/null
+++ b/arch/arm/mach-pnx4008/gpio.c
@@ -0,0 +1,330 @@
1/*
2 * arch/arm/mach-pnx4008/gpio.c
3 *
4 * PNX4008 GPIO driver
5 *
6 * Author: Dmitry Chigirev <source@mvista.com>
7 *
8 * Based on reference code by Iwo Mergler and Z.Tabaaloute from Philips:
9 * Copyright (c) 2005 Koninklijke Philips Electronics N.V.
10 *
11 * 2005 (c) MontaVista Software, Inc. This file is licensed under
12 * the terms of the GNU General Public License version 2. This program
13 * is licensed "as is" without any warranty of any kind, whether express
14 * or implied.
15 */
16
17#include <linux/config.h>
18#include <linux/types.h>
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <asm/semaphore.h>
22#include <asm/io.h>
23#include <asm/arch/platform.h>
24#include <asm/arch/gpio.h>
25
26/* register definitions */
27#define PIO_VA_BASE IO_ADDRESS(PNX4008_PIO_BASE)
28
29#define PIO_INP_STATE (0x00U)
30#define PIO_OUTP_SET (0x04U)
31#define PIO_OUTP_CLR (0x08U)
32#define PIO_OUTP_STATE (0x0CU)
33#define PIO_DRV_SET (0x10U)
34#define PIO_DRV_CLR (0x14U)
35#define PIO_DRV_STATE (0x18U)
36#define PIO_SDINP_STATE (0x1CU)
37#define PIO_SDOUTP_SET (0x20U)
38#define PIO_SDOUTP_CLR (0x24U)
39#define PIO_MUX_SET (0x28U)
40#define PIO_MUX_CLR (0x2CU)
41#define PIO_MUX_STATE (0x30U)
42
43static inline void gpio_lock(void)
44{
45 local_irq_disable();
46}
47
48static inline void gpio_unlock(void)
49{
50 local_irq_enable();
51}
52
53/* Inline functions */
54static inline int gpio_read_bit(u32 reg, int gpio)
55{
56 u32 bit, val;
57 int ret = -EFAULT;
58
59 if (gpio < 0)
60 goto out;
61
62 bit = GPIO_BIT(gpio);
63 if (bit) {
64 val = __raw_readl(PIO_VA_BASE + reg);
65 ret = (val & bit) ? 1 : 0;
66 }
67out:
68 return ret;
69}
70
71static inline int gpio_set_bit(u32 reg, int gpio)
72{
73 u32 bit, val;
74 int ret = -EFAULT;
75
76 if (gpio < 0)
77 goto out;
78
79 bit = GPIO_BIT(gpio);
80 if (bit) {
81 val = __raw_readl(PIO_VA_BASE + reg);
82 val |= bit;
83 __raw_writel(val, PIO_VA_BASE + reg);
84 ret = 0;
85 }
86out:
87 return ret;
88}
89
90/* Very simple access control, bitmap for allocated/free */
91static unsigned long access_map[4];
92#define INP_INDEX 0
93#define OUTP_INDEX 1
94#define GPIO_INDEX 2
95#define MUX_INDEX 3
96
97/*GPIO to Input Mapping */
98static short gpio_to_inp_map[32] = {
99 -1, -1, -1, -1, -1, -1, -1, -1,
100 -1, -1, -1, -1, -1, -1, -1, -1,
101 -1, -1, -1, -1, -1, -1, -1, -1,
102 -1, 10, 11, 12, 13, 14, 24, -1
103};
104
105/*GPIO to Mux Mapping */
106static short gpio_to_mux_map[32] = {
107 -1, -1, -1, -1, -1, -1, -1, -1,
108 -1, -1, -1, -1, -1, -1, -1, -1,
109 -1, -1, -1, -1, -1, -1, -1, -1,
110 -1, -1, -1, 0, 1, 4, 5, -1
111};
112
113/*Output to Mux Mapping */
114static short outp_to_mux_map[32] = {
115 -1, -1, -1, 6, -1, -1, -1, -1,
116 -1, -1, -1, -1, -1, -1, -1, -1,
117 -1, -1, -1, -1, -1, 2, -1, -1,
118 -1, -1, -1, -1, -1, -1, -1, -1
119};
120
121int pnx4008_gpio_register_pin(unsigned short pin)
122{
123 unsigned long bit = GPIO_BIT(pin);
124 int ret = -EBUSY; /* Already in use */
125
126 gpio_lock();
127
128 if (GPIO_ISBID(pin)) {
129 if (access_map[GPIO_INDEX] & bit)
130 goto out;
131 access_map[GPIO_INDEX] |= bit;
132
133 } else if (GPIO_ISRAM(pin)) {
134 if (access_map[GPIO_INDEX] & bit)
135 goto out;
136 access_map[GPIO_INDEX] |= bit;
137
138 } else if (GPIO_ISMUX(pin)) {
139 if (access_map[MUX_INDEX] & bit)
140 goto out;
141 access_map[MUX_INDEX] |= bit;
142
143 } else if (GPIO_ISOUT(pin)) {
144 if (access_map[OUTP_INDEX] & bit)
145 goto out;
146 access_map[OUTP_INDEX] |= bit;
147
148 } else if (GPIO_ISIN(pin)) {
149 if (access_map[INP_INDEX] & bit)
150 goto out;
151 access_map[INP_INDEX] |= bit;
152 } else
153 goto out;
154 ret = 0;
155
156out:
157 gpio_unlock();
158 return ret;
159}
160
161EXPORT_SYMBOL(pnx4008_gpio_register_pin);
162
163int pnx4008_gpio_unregister_pin(unsigned short pin)
164{
165 unsigned long bit = GPIO_BIT(pin);
166 int ret = -EFAULT; /* Not registered */
167
168 gpio_lock();
169
170 if (GPIO_ISBID(pin)) {
171 if (~access_map[GPIO_INDEX] & bit)
172 goto out;
173 access_map[GPIO_INDEX] &= ~bit;
174 } else if (GPIO_ISRAM(pin)) {
175 if (~access_map[GPIO_INDEX] & bit)
176 goto out;
177 access_map[GPIO_INDEX] &= ~bit;
178 } else if (GPIO_ISMUX(pin)) {
179 if (~access_map[MUX_INDEX] & bit)
180 goto out;
181 access_map[MUX_INDEX] &= ~bit;
182 } else if (GPIO_ISOUT(pin)) {
183 if (~access_map[OUTP_INDEX] & bit)
184 goto out;
185 access_map[OUTP_INDEX] &= ~bit;
186 } else if (GPIO_ISIN(pin)) {
187 if (~access_map[INP_INDEX] & bit)
188 goto out;
189 access_map[INP_INDEX] &= ~bit;
190 } else
191 goto out;
192 ret = 0;
193
194out:
195 gpio_unlock();
196 return ret;
197}
198
199EXPORT_SYMBOL(pnx4008_gpio_unregister_pin);
200
201unsigned long pnx4008_gpio_read_pin(unsigned short pin)
202{
203 unsigned long ret = -EFAULT;
204 int gpio = GPIO_BIT_MASK(pin);
205 gpio_lock();
206 if (GPIO_ISOUT(pin)) {
207 ret = gpio_read_bit(PIO_OUTP_STATE, gpio);
208 } else if (GPIO_ISRAM(pin)) {
209 if (gpio_read_bit(PIO_DRV_STATE, gpio) == 0) {
210 ret = gpio_read_bit(PIO_SDINP_STATE, gpio);
211 }
212 } else if (GPIO_ISBID(pin)) {
213 ret = gpio_read_bit(PIO_DRV_STATE, gpio);
214 if (ret > 0)
215 ret = gpio_read_bit(PIO_OUTP_STATE, gpio);
216 else if (ret == 0)
217 ret =
218 gpio_read_bit(PIO_INP_STATE, gpio_to_inp_map[gpio]);
219 } else if (GPIO_ISIN(pin)) {
220 ret = gpio_read_bit(PIO_INP_STATE, gpio);
221 }
222 gpio_unlock();
223 return ret;
224}
225
226EXPORT_SYMBOL(pnx4008_gpio_read_pin);
227
228/* Write Value to output */
229int pnx4008_gpio_write_pin(unsigned short pin, int output)
230{
231 int gpio = GPIO_BIT_MASK(pin);
232 int ret = -EFAULT;
233
234 gpio_lock();
235 if (GPIO_ISOUT(pin)) {
236 printk( "writing '%x' to '%x'\n",
237 gpio, output ? PIO_OUTP_SET : PIO_OUTP_CLR );
238 ret = gpio_set_bit(output ? PIO_OUTP_SET : PIO_OUTP_CLR, gpio);
239 } else if (GPIO_ISRAM(pin)) {
240 if (gpio_read_bit(PIO_DRV_STATE, gpio) > 0)
241 ret = gpio_set_bit(output ? PIO_SDOUTP_SET :
242 PIO_SDOUTP_CLR, gpio);
243 } else if (GPIO_ISBID(pin)) {
244 if (gpio_read_bit(PIO_DRV_STATE, gpio) > 0)
245 ret = gpio_set_bit(output ? PIO_OUTP_SET :
246 PIO_OUTP_CLR, gpio);
247 }
248 gpio_unlock();
249 return ret;
250}
251
252EXPORT_SYMBOL(pnx4008_gpio_write_pin);
253
254/* Value = 1 : Set GPIO pin as output */
255/* Value = 0 : Set GPIO pin as input */
256int pnx4008_gpio_set_pin_direction(unsigned short pin, int output)
257{
258 int gpio = GPIO_BIT_MASK(pin);
259 int ret = -EFAULT;
260
261 gpio_lock();
262 if (GPIO_ISBID(pin) || GPIO_ISRAM(pin)) {
263 ret = gpio_set_bit(output ? PIO_DRV_SET : PIO_DRV_CLR, gpio);
264 }
265 gpio_unlock();
266 return ret;
267}
268
269EXPORT_SYMBOL(pnx4008_gpio_set_pin_direction);
270
271/* Read GPIO pin direction: 0= pin used as input, 1= pin used as output*/
272int pnx4008_gpio_read_pin_direction(unsigned short pin)
273{
274 int gpio = GPIO_BIT_MASK(pin);
275 int ret = -EFAULT;
276
277 gpio_lock();
278 if (GPIO_ISBID(pin) || GPIO_ISRAM(pin)) {
279 ret = gpio_read_bit(PIO_DRV_STATE, gpio);
280 }
281 gpio_unlock();
282 return ret;
283}
284
285EXPORT_SYMBOL(pnx4008_gpio_read_pin_direction);
286
287/* Value = 1 : Set pin to muxed function */
288/* Value = 0 : Set pin as GPIO */
289int pnx4008_gpio_set_pin_mux(unsigned short pin, int output)
290{
291 int gpio = GPIO_BIT_MASK(pin);
292 int ret = -EFAULT;
293
294 gpio_lock();
295 if (GPIO_ISBID(pin)) {
296 ret =
297 gpio_set_bit(output ? PIO_MUX_SET : PIO_MUX_CLR,
298 gpio_to_mux_map[gpio]);
299 } else if (GPIO_ISOUT(pin)) {
300 ret =
301 gpio_set_bit(output ? PIO_MUX_SET : PIO_MUX_CLR,
302 outp_to_mux_map[gpio]);
303 } else if (GPIO_ISMUX(pin)) {
304 ret = gpio_set_bit(output ? PIO_MUX_SET : PIO_MUX_CLR, gpio);
305 }
306 gpio_unlock();
307 return ret;
308}
309
310EXPORT_SYMBOL(pnx4008_gpio_set_pin_mux);
311
312/* Read pin mux function: 0= pin used as GPIO, 1= pin used for muxed function*/
313int pnx4008_gpio_read_pin_mux(unsigned short pin)
314{
315 int gpio = GPIO_BIT_MASK(pin);
316 int ret = -EFAULT;
317
318 gpio_lock();
319 if (GPIO_ISBID(pin)) {
320 ret = gpio_read_bit(PIO_MUX_STATE, gpio_to_mux_map[gpio]);
321 } else if (GPIO_ISOUT(pin)) {
322 ret = gpio_read_bit(PIO_MUX_STATE, outp_to_mux_map[gpio]);
323 } else if (GPIO_ISMUX(pin)) {
324 ret = gpio_read_bit(PIO_MUX_STATE, gpio);
325 }
326 gpio_unlock();
327 return ret;
328}
329
330EXPORT_SYMBOL(pnx4008_gpio_read_pin_mux);
diff --git a/arch/arm/mach-pnx4008/irq.c b/arch/arm/mach-pnx4008/irq.c
new file mode 100644
index 000000000000..9b0a8e084e99
--- /dev/null
+++ b/arch/arm/mach-pnx4008/irq.c
@@ -0,0 +1,121 @@
1/*
2 * arch/arm/mach-pnx4008/irq.c
3 *
4 * PNX4008 IRQ controller driver
5 *
6 * Author: Dmitry Chigirev <source@mvista.com>
7 *
8 * Based on reference code received from Philips:
9 * Copyright (C) 2003 Philips Semiconductors
10 *
11 * 2005 (c) MontaVista Software, Inc. This file is licensed under
12 * the terms of the GNU General Public License version 2. This program
13 * is licensed "as is" without any warranty of any kind, whether express
14 * or implied.
15 */
16
17#include <linux/kernel.h>
18#include <linux/types.h>
19#include <linux/mm.h>
20#include <linux/interrupt.h>
21#include <linux/list.h>
22#include <linux/init.h>
23#include <linux/ioport.h>
24#include <linux/device.h>
25#include <asm/hardware.h>
26#include <asm/irq.h>
27#include <asm/io.h>
28#include <asm/setup.h>
29#include <asm/mach-types.h>
30#include <asm/pgtable.h>
31#include <asm/page.h>
32#include <asm/system.h>
33#include <asm/mach/arch.h>
34#include <asm/mach/irq.h>
35#include <asm/mach/map.h>
36#include <asm/arch/irq.h>
37
38static u8 pnx4008_irq_type[NR_IRQS] = PNX4008_IRQ_TYPES;
39
40static void pnx4008_mask_irq(unsigned int irq)
41{
42 __raw_writel(__raw_readl(INTC_ER(irq)) & ~INTC_BIT(irq), INTC_ER(irq)); /* mask interrupt */
43}
44
45static void pnx4008_unmask_irq(unsigned int irq)
46{
47 __raw_writel(__raw_readl(INTC_ER(irq)) | INTC_BIT(irq), INTC_ER(irq)); /* unmask interrupt */
48}
49
50static void pnx4008_mask_ack_irq(unsigned int irq)
51{
52 __raw_writel(__raw_readl(INTC_ER(irq)) & ~INTC_BIT(irq), INTC_ER(irq)); /* mask interrupt */
53 __raw_writel(INTC_BIT(irq), INTC_SR(irq)); /* clear interrupt status */
54}
55
56static int pnx4008_set_irq_type(unsigned int irq, unsigned int type)
57{
58 switch (type) {
59 case IRQT_RISING:
60 __raw_writel(__raw_readl(INTC_ATR(irq)) | INTC_BIT(irq), INTC_ATR(irq)); /*edge sensitive */
61 __raw_writel(__raw_readl(INTC_APR(irq)) | INTC_BIT(irq), INTC_APR(irq)); /*rising edge */
62 set_irq_handler(irq, do_edge_IRQ);
63 break;
64 case IRQT_FALLING:
65 __raw_writel(__raw_readl(INTC_ATR(irq)) | INTC_BIT(irq), INTC_ATR(irq)); /*edge sensitive */
66 __raw_writel(__raw_readl(INTC_APR(irq)) & ~INTC_BIT(irq), INTC_APR(irq)); /*falling edge */
67 set_irq_handler(irq, do_edge_IRQ);
68 break;
69 case IRQT_LOW:
70 __raw_writel(__raw_readl(INTC_ATR(irq)) & ~INTC_BIT(irq), INTC_ATR(irq)); /*level sensitive */
71 __raw_writel(__raw_readl(INTC_APR(irq)) & ~INTC_BIT(irq), INTC_APR(irq)); /*low level */
72 set_irq_handler(irq, do_level_IRQ);
73 break;
74 case IRQT_HIGH:
75 __raw_writel(__raw_readl(INTC_ATR(irq)) & ~INTC_BIT(irq), INTC_ATR(irq)); /*level sensitive */
76 __raw_writel(__raw_readl(INTC_APR(irq)) | INTC_BIT(irq), INTC_APR(irq)); /* high level */
77 set_irq_handler(irq, do_level_IRQ);
78 break;
79
80 /* IRQT_BOTHEDGE is not supported */
81 default:
82 printk(KERN_ERR "PNX4008 IRQ: Unsupported irq type %d\n", type);
83 return -1;
84 }
85 return 0;
86}
87
88static struct irqchip pnx4008_irq_chip = {
89 .ack = pnx4008_mask_ack_irq,
90 .mask = pnx4008_mask_irq,
91 .unmask = pnx4008_unmask_irq,
92 .set_type = pnx4008_set_irq_type,
93};
94
95void __init pnx4008_init_irq(void)
96{
97 unsigned int i;
98
99 /* configure and enable IRQ 0,1,30,31 (cascade interrupts) mask all others */
100 pnx4008_set_irq_type(SUB1_IRQ_N, pnx4008_irq_type[SUB1_IRQ_N]);
101 pnx4008_set_irq_type(SUB2_IRQ_N, pnx4008_irq_type[SUB2_IRQ_N]);
102 pnx4008_set_irq_type(SUB1_FIQ_N, pnx4008_irq_type[SUB1_FIQ_N]);
103 pnx4008_set_irq_type(SUB2_FIQ_N, pnx4008_irq_type[SUB2_FIQ_N]);
104
105 __raw_writel((1 << SUB2_FIQ_N) | (1 << SUB1_FIQ_N) |
106 (1 << SUB2_IRQ_N) | (1 << SUB1_IRQ_N),
107 INTC_ER(MAIN_BASE_INT));
108 __raw_writel(0, INTC_ER(SIC1_BASE_INT));
109 __raw_writel(0, INTC_ER(SIC2_BASE_INT));
110
111 /* configure all other IRQ's */
112 for (i = 0; i < NR_IRQS; i++) {
113 if (i == SUB2_FIQ_N || i == SUB1_FIQ_N ||
114 i == SUB2_IRQ_N || i == SUB1_IRQ_N)
115 continue;
116 set_irq_flags(i, IRQF_VALID);
117 set_irq_chip(i, &pnx4008_irq_chip);
118 pnx4008_set_irq_type(i, pnx4008_irq_type[i]);
119 }
120}
121
diff --git a/arch/arm/mach-pnx4008/pm.c b/arch/arm/mach-pnx4008/pm.c
new file mode 100644
index 000000000000..3649cd3dfc9a
--- /dev/null
+++ b/arch/arm/mach-pnx4008/pm.c
@@ -0,0 +1,184 @@
1/*
2 * arch/arm/mach-pnx4008/pm.c
3 *
4 * Power Management driver for PNX4008
5 *
6 * Authors: Vitaly Wool, Dmitry Chigirev <source@mvista.com>
7 *
8 * 2005 (c) MontaVista Software, Inc. This file is licensed under
9 * the terms of the GNU General Public License version 2. This program
10 * is licensed "as is" without any warranty of any kind, whether express
11 * or implied.
12 */
13
14#include <linux/pm.h>
15#include <linux/rtc.h>
16#include <linux/sched.h>
17#include <linux/proc_fs.h>
18#include <linux/pm.h>
19#include <linux/delay.h>
20#include <linux/clk.h>
21
22#include <asm/io.h>
23#include <asm/mach-types.h>
24#include <asm/cacheflush.h>
25#include <asm/arch/pm.h>
26#include <asm/arch/clock.h>
27
28#define SRAM_VA IO_ADDRESS(PNX4008_IRAM_BASE)
29
30static void *saved_sram;
31
32static struct clk *pll4_clk;
33
34static inline void pnx4008_standby(void)
35{
36 void (*pnx4008_cpu_standby_ptr) (void);
37
38 local_irq_disable();
39 local_fiq_disable();
40
41 clk_disable(pll4_clk);
42
43 /*saving portion of SRAM to be used by suspend function. */
44 memcpy(saved_sram, (void *)SRAM_VA, pnx4008_cpu_standby_sz);
45
46 /*make sure SRAM copy gets physically written into SDRAM.
47 SDRAM will be placed into self-refresh during power down */
48 flush_cache_all();
49
50 /*copy suspend function into SRAM */
51 memcpy((void *)SRAM_VA, pnx4008_cpu_standby, pnx4008_cpu_standby_sz);
52
53 /*do suspend */
54 pnx4008_cpu_standby_ptr = (void *)SRAM_VA;
55 pnx4008_cpu_standby_ptr();
56
57 /*restoring portion of SRAM that was used by suspend function */
58 memcpy((void *)SRAM_VA, saved_sram, pnx4008_cpu_standby_sz);
59
60 clk_enable(pll4_clk);
61
62 local_fiq_enable();
63 local_irq_enable();
64}
65
66static inline void pnx4008_suspend(void)
67{
68 void (*pnx4008_cpu_suspend_ptr) (void);
69
70 local_irq_disable();
71 local_fiq_disable();
72
73 clk_disable(pll4_clk);
74
75 __raw_writel(0xffffffff, START_INT_RSR_REG(SE_PIN_BASE_INT));
76 __raw_writel(0xffffffff, START_INT_RSR_REG(SE_INT_BASE_INT));
77
78 /*saving portion of SRAM to be used by suspend function. */
79 memcpy(saved_sram, (void *)SRAM_VA, pnx4008_cpu_suspend_sz);
80
81 /*make sure SRAM copy gets physically written into SDRAM.
82 SDRAM will be placed into self-refresh during power down */
83 flush_cache_all();
84
85 /*copy suspend function into SRAM */
86 memcpy((void *)SRAM_VA, pnx4008_cpu_suspend, pnx4008_cpu_suspend_sz);
87
88 /*do suspend */
89 pnx4008_cpu_suspend_ptr = (void *)SRAM_VA;
90 pnx4008_cpu_suspend_ptr();
91
92 /*restoring portion of SRAM that was used by suspend function */
93 memcpy((void *)SRAM_VA, saved_sram, pnx4008_cpu_suspend_sz);
94
95 clk_enable(pll4_clk);
96
97 local_fiq_enable();
98 local_irq_enable();
99}
100
101static int pnx4008_pm_enter(suspend_state_t state)
102{
103 switch (state) {
104 case PM_SUSPEND_STANDBY:
105 pnx4008_standby();
106 break;
107 case PM_SUSPEND_MEM:
108 pnx4008_suspend();
109 break;
110 case PM_SUSPEND_DISK:
111 return -ENOTSUPP;
112 default:
113 return -EINVAL;
114 }
115 return 0;
116}
117
118/*
119 * Called after processes are frozen, but before we shut down devices.
120 */
121static int pnx4008_pm_prepare(suspend_state_t state)
122{
123 switch (state) {
124 case PM_SUSPEND_STANDBY:
125 case PM_SUSPEND_MEM:
126 break;
127
128 case PM_SUSPEND_DISK:
129 return -ENOTSUPP;
130 break;
131
132 default:
133 return -EINVAL;
134 break;
135 }
136 return 0;
137}
138
139/*
140 * Called after devices are re-setup, but before processes are thawed.
141 */
142static int pnx4008_pm_finish(suspend_state_t state)
143{
144 return 0;
145}
146
147/*
148 * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
149 */
150static struct pm_ops pnx4008_pm_ops = {
151 .prepare = pnx4008_pm_prepare,
152 .enter = pnx4008_pm_enter,
153 .finish = pnx4008_pm_finish,
154};
155
156static int __init pnx4008_pm_init(void)
157{
158 u32 sram_size_to_allocate;
159
160 pll4_clk = clk_get(0, "ck_pll4");
161 if (IS_ERR(pll4_clk)) {
162 printk(KERN_ERR
163 "PM Suspend cannot acquire ARM(PLL4) clock control\n");
164 return PTR_ERR(pll4_clk);
165 }
166
167 if (pnx4008_cpu_standby_sz > pnx4008_cpu_suspend_sz)
168 sram_size_to_allocate = pnx4008_cpu_standby_sz;
169 else
170 sram_size_to_allocate = pnx4008_cpu_suspend_sz;
171
172 saved_sram = kmalloc(sram_size_to_allocate, GFP_ATOMIC);
173 if (!saved_sram) {
174 printk(KERN_ERR
175 "PM Suspend: cannot allocate memory to save portion of SRAM\n");
176 clk_put(pll4_clk);
177 return -ENOMEM;
178 }
179
180 pm_set_ops(&pnx4008_pm_ops);
181 return 0;
182}
183
184late_initcall(pnx4008_pm_init);
diff --git a/arch/arm/mach-pnx4008/serial.c b/arch/arm/mach-pnx4008/serial.c
new file mode 100644
index 000000000000..2e1e04cc048c
--- /dev/null
+++ b/arch/arm/mach-pnx4008/serial.c
@@ -0,0 +1,69 @@
1/*
2 * linux/arch/arm/mach-pnx4008/serial.c
3 *
4 * PNX4008 UART initialization
5 *
6 * Copyright: MontaVista Software Inc. (c) 2005
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/kernel.h>
14#include <linux/types.h>
15
16#include <asm/io.h>
17
18#include <asm/arch/platform.h>
19#include <asm/arch/hardware.h>
20
21#include <linux/serial_core.h>
22#include <linux/serial_reg.h>
23#include <asm/arch/pm.h>
24
25#include <asm/arch/clock.h>
26
27#define UART_3 0
28#define UART_4 1
29#define UART_5 2
30#define UART_6 3
31#define UART_UNKNOWN (-1)
32
33#define UART3_BASE_VA IO_ADDRESS(PNX4008_UART3_BASE)
34#define UART4_BASE_VA IO_ADDRESS(PNX4008_UART4_BASE)
35#define UART5_BASE_VA IO_ADDRESS(PNX4008_UART5_BASE)
36#define UART6_BASE_VA IO_ADDRESS(PNX4008_UART6_BASE)
37
38#define UART_FCR_OFFSET 8
39#define UART_FIFO_SIZE 64
40
41void pnx4008_uart_init(void)
42{
43 u32 tmp;
44 int i = UART_FIFO_SIZE;
45
46 __raw_writel(0xC1, UART5_BASE_VA + UART_FCR_OFFSET);
47 __raw_writel(0xC1, UART3_BASE_VA + UART_FCR_OFFSET);
48
49 /* Send a NULL to fix the UART HW bug */
50 __raw_writel(0x00, UART5_BASE_VA);
51 __raw_writel(0x00, UART3_BASE_VA);
52
53 while (i--) {
54 tmp = __raw_readl(UART5_BASE_VA);
55 tmp = __raw_readl(UART3_BASE_VA);
56 }
57 __raw_writel(0, UART5_BASE_VA + UART_FCR_OFFSET);
58 __raw_writel(0, UART3_BASE_VA + UART_FCR_OFFSET);
59
60 /* setup wakeup interrupt */
61 start_int_set_rising_edge(SE_U3_RX_INT);
62 start_int_ack(SE_U3_RX_INT);
63 start_int_umask(SE_U3_RX_INT);
64
65 start_int_set_rising_edge(SE_U5_RX_INT);
66 start_int_ack(SE_U5_RX_INT);
67 start_int_umask(SE_U5_RX_INT);
68}
69
diff --git a/arch/arm/mach-pnx4008/sleep.S b/arch/arm/mach-pnx4008/sleep.S
new file mode 100644
index 000000000000..93c802bac269
--- /dev/null
+++ b/arch/arm/mach-pnx4008/sleep.S
@@ -0,0 +1,196 @@
1/*
2 * linux/arch/arm/mach-pnx4008/sleep.S
3 *
4 * PNX4008 support for STOP mode and SDRAM self-refresh
5 *
6 * Authors: Dmitry Chigirev, Vitaly Wool <source@mvista.com>
7 *
8 * 2005 (c) MontaVista Software, Inc. This file is licensed under
9 * the terms of the GNU General Public License version 2. This program
10 * is licensed "as is" without any warranty of any kind, whether express
11 * or implied.
12 */
13
14#include <linux/config.h>
15#include <linux/linkage.h>
16#include <asm/assembler.h>
17#include <asm/hardware.h>
18
19#define PWRMAN_VA_BASE IO_ADDRESS(PNX4008_PWRMAN_BASE)
20#define PWR_CTRL_REG_OFFS 0x44
21
22#define SDRAM_CFG_VA_BASE IO_ADDRESS(PNX4008_SDRAM_CFG_BASE)
23#define MPMC_STATUS_REG_OFFS 0x4
24
25 .text
26
27ENTRY(pnx4008_cpu_suspend)
28 @this function should be entered in Direct run mode.
29
30 @ save registers on stack
31 stmfd sp!, {r0 - r6, lr}
32
33 @ setup Power Manager base address in r4
34 @ and put it's value in r5
35 mov r4, #(PWRMAN_VA_BASE & 0xff000000)
36 orr r4, r4, #(PWRMAN_VA_BASE & 0x00ff0000)
37 orr r4, r4, #(PWRMAN_VA_BASE & 0x0000ff00)
38 orr r4, r4, #(PWRMAN_VA_BASE & 0x000000ff)
39 ldr r5, [r4, #PWR_CTRL_REG_OFFS]
40
41 @ setup SDRAM controller base address in r2
42 @ and put it's value in r3
43 mov r2, #(SDRAM_CFG_VA_BASE & 0xff000000)
44 orr r2, r2, #(SDRAM_CFG_VA_BASE & 0x00ff0000)
45 orr r2, r2, #(SDRAM_CFG_VA_BASE & 0x0000ff00)
46 orr r2, r2, #(SDRAM_CFG_VA_BASE & 0x000000ff)
47 ldr r3, [r2, #MPMC_STATUS_REG_OFFS] @extra read - HW bug workaround
48
49 @ clear SDRAM self-refresh bit latch
50 and r5, r5, #(~(1 << 8))
51 @ clear SDRAM self-refresh bit
52 and r5, r5, #(~(1 << 9))
53 str r5, [r4, #PWR_CTRL_REG_OFFS]
54
55 @ do save current bit settings in r1
56 mov r1, r5
57
58 @ set SDRAM self-refresh bit
59 orr r5, r5, #(1 << 9)
60 str r5, [r4, #PWR_CTRL_REG_OFFS]
61
62 @ set SDRAM self-refresh bit latch
63 orr r5, r5, #(1 << 8)
64 str r5, [r4, #PWR_CTRL_REG_OFFS]
65
66 @ clear SDRAM self-refresh bit latch
67 and r5, r5, #(~(1 << 8))
68 str r5, [r4, #PWR_CTRL_REG_OFFS]
69
70 @ clear SDRAM self-refresh bit
71 and r5, r5, #(~(1 << 9))
72 str r5, [r4, #PWR_CTRL_REG_OFFS]
73
74 @ wait for SDRAM to get into self-refresh mode
752: ldr r3, [r2, #MPMC_STATUS_REG_OFFS]
76 tst r3, #(1 << 2)
77 beq 2b
78
79 @ to prepare SDRAM to get out of self-refresh mode after wakeup
80 orr r5, r5, #(1 << 7)
81 str r5, [r4, #PWR_CTRL_REG_OFFS]
82
83 @ do enter stop mode
84 orr r5, r5, #(1 << 0)
85 str r5, [r4, #PWR_CTRL_REG_OFFS]
86 nop
87 nop
88 nop
89 nop
90 nop
91 nop
92 nop
93 nop
94 nop
95
96 @ sleeping now...
97
98 @ coming out of STOP mode into Direct Run mode
99 @ clear STOP mode and SDRAM self-refresh bits
100 str r1, [r4, #PWR_CTRL_REG_OFFS]
101
102 @ wait for SDRAM to get out self-refresh mode
1033: ldr r3, [r2, #MPMC_STATUS_REG_OFFS]
104 tst r3, #5
105 bne 3b
106
107 @ restore regs and return
108 ldmfd sp!, {r0 - r6, pc}
109
110ENTRY(pnx4008_cpu_suspend_sz)
111 .word . - pnx4008_cpu_suspend
112
113ENTRY(pnx4008_cpu_standby)
114 @ save registers on stack
115 stmfd sp!, {r0 - r6, lr}
116
117 @ setup Power Manager base address in r4
118 @ and put it's value in r5
119 mov r4, #(PWRMAN_VA_BASE & 0xff000000)
120 orr r4, r4, #(PWRMAN_VA_BASE & 0x00ff0000)
121 orr r4, r4, #(PWRMAN_VA_BASE & 0x0000ff00)
122 orr r4, r4, #(PWRMAN_VA_BASE & 0x000000ff)
123 ldr r5, [r4, #PWR_CTRL_REG_OFFS]
124
125 @ setup SDRAM controller base address in r2
126 @ and put it's value in r3
127 mov r2, #(SDRAM_CFG_VA_BASE & 0xff000000)
128 orr r2, r2, #(SDRAM_CFG_VA_BASE & 0x00ff0000)
129 orr r2, r2, #(SDRAM_CFG_VA_BASE & 0x0000ff00)
130 orr r2, r2, #(SDRAM_CFG_VA_BASE & 0x000000ff)
131 ldr r3, [r2, #MPMC_STATUS_REG_OFFS] @extra read - HW bug workaround
132
133 @ clear SDRAM self-refresh bit latch
134 and r5, r5, #(~(1 << 8))
135 @ clear SDRAM self-refresh bit
136 and r5, r5, #(~(1 << 9))
137 str r5, [r4, #PWR_CTRL_REG_OFFS]
138
139 @ do save current bit settings in r1
140 mov r1, r5
141
142 @ set SDRAM self-refresh bit
143 orr r5, r5, #(1 << 9)
144 str r5, [r4, #PWR_CTRL_REG_OFFS]
145
146 @ set SDRAM self-refresh bit latch
147 orr r5, r5, #(1 << 8)
148 str r5, [r4, #PWR_CTRL_REG_OFFS]
149
150 @ clear SDRAM self-refresh bit latch
151 and r5, r5, #(~(1 << 8))
152 str r5, [r4, #PWR_CTRL_REG_OFFS]
153
154 @ clear SDRAM self-refresh bit
155 and r5, r5, #(~(1 << 9))
156 str r5, [r4, #PWR_CTRL_REG_OFFS]
157
158 @ wait for SDRAM to get into self-refresh mode
1592: ldr r3, [r2, #MPMC_STATUS_REG_OFFS]
160 tst r3, #(1 << 2)
161 beq 2b
162
163 @ set 'get out of self-refresh mode after wakeup' bit
164 orr r5, r5, #(1 << 7)
165 str r5, [r4, #PWR_CTRL_REG_OFFS]
166
167 mcr p15, 0, r0, c7, c0, 4 @ kinda sleeping now...
168
169 @ set SDRAM self-refresh bit latch
170 orr r5, r5, #(1 << 8)
171 str r5, [r4, #PWR_CTRL_REG_OFFS]
172
173 @ clear SDRAM self-refresh bit latch
174 and r5, r5, #(~(1 << 8))
175 str r5, [r4, #PWR_CTRL_REG_OFFS]
176
177 @ wait for SDRAM to get out self-refresh mode
1783: ldr r3, [r2, #MPMC_STATUS_REG_OFFS]
179 tst r3, #5
180 bne 3b
181
182 @ restore regs and return
183 ldmfd sp!, {r0 - r6, pc}
184
185ENTRY(pnx4008_cpu_standby_sz)
186 .word . - pnx4008_cpu_standby
187
188ENTRY(pnx4008_cache_clean_invalidate)
189 stmfd sp!, {r0 - r6, lr}
190#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
191 mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache
192#else
1931: mrc p15, 0, r15, c7, c14, 3 @ test,clean,invalidate
194 bne 1b
195#endif
196 ldmfd sp!, {r0 - r6, pc}
diff --git a/arch/arm/mach-pnx4008/time.c b/arch/arm/mach-pnx4008/time.c
new file mode 100644
index 000000000000..4ce680698529
--- /dev/null
+++ b/arch/arm/mach-pnx4008/time.c
@@ -0,0 +1,141 @@
1/*
2 * arch/arm/mach-pnx4008/time.c
3 *
4 * PNX4008 Timers
5 *
6 * Authors: Vitaly Wool, Dmitry Chigirev, Grigory Tolstolytkin <source@mvista.com>
7 *
8 * 2005 (c) MontaVista Software, Inc. This file is licensed under
9 * the terms of the GNU General Public License version 2. This program
10 * is licensed "as is" without any warranty of any kind, whether express
11 * or implied.
12 */
13
14#include <linux/config.h>
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/delay.h>
18#include <linux/interrupt.h>
19#include <linux/sched.h>
20#include <linux/spinlock.h>
21#include <linux/module.h>
22#include <linux/kallsyms.h>
23
24#include <asm/system.h>
25#include <asm/hardware.h>
26#include <asm/io.h>
27#include <asm/leds.h>
28#include <asm/irq.h>
29#include <asm/mach/irq.h>
30#include <asm/mach/time.h>
31
32#include <linux/time.h>
33#include <linux/timex.h>
34#include <asm/errno.h>
35
36/*! Note: all timers are UPCOUNTING */
37
38/*!
39 * Returns number of us since last clock interrupt. Note that interrupts
40 * will have been disabled by do_gettimeoffset()
41 */
42static unsigned long pnx4008_gettimeoffset(void)
43{
44 u32 ticks_to_match =
45 __raw_readl(HSTIM_MATCH0) - __raw_readl(HSTIM_COUNTER);
46 u32 elapsed = LATCH - ticks_to_match;
47 return (elapsed * (tick_nsec / 1000)) / LATCH;
48}
49
50/*!
51 * IRQ handler for the timer
52 */
53static irqreturn_t pnx4008_timer_interrupt(int irq, void *dev_id,
54 struct pt_regs *regs)
55{
56 if (__raw_readl(HSTIM_INT) & MATCH0_INT) {
57
58 write_seqlock(&xtime_lock);
59
60 do {
61 timer_tick(regs);
62
63 /*
64 * this algorithm takes care of possible delay
65 * for this interrupt handling longer than a normal
66 * timer period
67 */
68 __raw_writel(__raw_readl(HSTIM_MATCH0) + LATCH,
69 HSTIM_MATCH0);
70 __raw_writel(MATCH0_INT, HSTIM_INT); /* clear interrupt */
71
72 /*
73 * The goal is to keep incrementing HSTIM_MATCH0
74 * register until HSTIM_MATCH0 indicates time after
75 * what HSTIM_COUNTER indicates.
76 */
77 } while ((signed)
78 (__raw_readl(HSTIM_MATCH0) -
79 __raw_readl(HSTIM_COUNTER)) < 0);
80
81 write_sequnlock(&xtime_lock);
82 }
83
84 return IRQ_HANDLED;
85}
86
87static struct irqaction pnx4008_timer_irq = {
88 .name = "PNX4008 Tick Timer",
89 .flags = SA_INTERRUPT | SA_TIMER,
90 .handler = pnx4008_timer_interrupt
91};
92
93/*!
94 * Set up timer and timer interrupt.
95 */
96static __init void pnx4008_setup_timer(void)
97{
98 __raw_writel(RESET_COUNT, MSTIM_CTRL);
99 while (__raw_readl(MSTIM_COUNTER)) ; /* wait for reset to complete. 100% guarantee event */
100 __raw_writel(0, MSTIM_CTRL); /* stop the timer */
101 __raw_writel(0, MSTIM_MCTRL);
102
103 __raw_writel(RESET_COUNT, HSTIM_CTRL);
104 while (__raw_readl(HSTIM_COUNTER)) ; /* wait for reset to complete. 100% guarantee event */
105 __raw_writel(0, HSTIM_CTRL);
106 __raw_writel(0, HSTIM_MCTRL);
107 __raw_writel(0, HSTIM_CCR);
108 __raw_writel(12, HSTIM_PMATCH); /* scale down to 1 MHZ */
109 __raw_writel(LATCH, HSTIM_MATCH0);
110 __raw_writel(MR0_INT, HSTIM_MCTRL);
111
112 setup_irq(HSTIMER_INT, &pnx4008_timer_irq);
113
114 __raw_writel(COUNT_ENAB | DEBUG_EN, HSTIM_CTRL); /*start timer, stop when JTAG active */
115}
116
117/* Timer Clock Control in PM register */
118#define TIMCLK_CTRL_REG IO_ADDRESS((PNX4008_PWRMAN_BASE + 0xBC))
119#define WATCHDOG_CLK_EN 1
120#define TIMER_CLK_EN 2 /* HS and MS timers? */
121
122static u32 timclk_ctrl_reg_save;
123
124void pnx4008_timer_suspend(void)
125{
126 timclk_ctrl_reg_save = __raw_readl(TIMCLK_CTRL_REG);
127 __raw_writel(0, TIMCLK_CTRL_REG); /* disable timers */
128}
129
130void pnx4008_timer_resume(void)
131{
132 __raw_writel(timclk_ctrl_reg_save, TIMCLK_CTRL_REG); /* enable timers */
133}
134
135struct sys_timer pnx4008_timer = {
136 .init = pnx4008_setup_timer,
137 .offset = pnx4008_gettimeoffset,
138 .suspend = pnx4008_timer_suspend,
139 .resume = pnx4008_timer_resume,
140};
141
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index c55b739e10ba..1ff2f073a55d 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -121,8 +121,8 @@ config CPU_ARM925T
121# ARM926T 121# ARM926T
122config CPU_ARM926T 122config CPU_ARM926T
123 bool "Support ARM926T processor" 123 bool "Support ARM926T processor"
124 depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || MACH_REALVIEW_EB 124 depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || MACH_REALVIEW_EB || ARCH_PNX4008
125 default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX 125 default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || ARCH_PNX4008
126 select CPU_32v5 126 select CPU_32v5
127 select CPU_ABRT_EV5TJ 127 select CPU_ABRT_EV5TJ
128 select CPU_CACHE_VIVT 128 select CPU_CACHE_VIVT