aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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