Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
[linux-2.6] / arch / powerpc / sysdev / mv64x60_udbg.c
1 /*
2  * udbg serial input/output routines for the Marvell MV64x60 (Discovery).
3  *
4  * Author: Dale Farnsworth <dale@farnsworth.org>
5  *
6  * 2007 (c) MontaVista Software, Inc.  This file is licensed under
7  * the terms of the GNU General Public License version 2.  This program
8  * is licensed "as is" without any warranty of any kind, whether express
9  * or implied.
10  */
11
12 #include <asm/io.h>
13 #include <asm/prom.h>
14 #include <asm/udbg.h>
15
16 #include <sysdev/mv64x60.h>
17
18 #define MPSC_0_CR1_OFFSET       0x000c
19
20 #define MPSC_0_CR2_OFFSET       0x0010
21 #define MPSC_CHR_2_TCS          (1 << 9)
22
23 #define MPSC_0_CHR_10_OFFSET    0x0030
24
25 #define MPSC_INTR_CAUSE_OFF_0   0x0004
26 #define MPSC_INTR_CAUSE_OFF_1   0x000c
27 #define MPSC_INTR_CAUSE_RCC     (1<<6)
28
29 static void __iomem *mpsc_base;
30 static void __iomem *mpsc_intr_cause;
31
32 static void mv64x60_udbg_putc(char c)
33 {
34         if (c == '\n')
35                 mv64x60_udbg_putc('\r');
36
37         while(in_le32(mpsc_base + MPSC_0_CR2_OFFSET) & MPSC_CHR_2_TCS)
38                 ;
39         out_le32(mpsc_base + MPSC_0_CR1_OFFSET, c);
40         out_le32(mpsc_base + MPSC_0_CR2_OFFSET, MPSC_CHR_2_TCS);
41 }
42
43 static int mv64x60_udbg_testc(void)
44 {
45         return (in_le32(mpsc_intr_cause) & MPSC_INTR_CAUSE_RCC) != 0;
46 }
47
48 static int mv64x60_udbg_getc(void)
49 {
50         int cause = 0;
51         int c;
52
53         while (!mv64x60_udbg_testc())
54                 ;
55
56         c = in_8(mpsc_base + MPSC_0_CHR_10_OFFSET + 2);
57         out_8(mpsc_base + MPSC_0_CHR_10_OFFSET + 2, c);
58         out_le32(mpsc_intr_cause, cause & ~MPSC_INTR_CAUSE_RCC);
59         return c;
60 }
61
62 static int mv64x60_udbg_getc_poll(void)
63 {
64         if (!mv64x60_udbg_testc())
65                 return -1;
66
67         return mv64x60_udbg_getc();
68 }
69
70 static void mv64x60_udbg_init(void)
71 {
72         struct device_node *np, *mpscintr, *stdout = NULL;
73         const char *path;
74         const phandle *ph;
75         struct resource r[2];
76         const int *block_index;
77         int intr_cause_offset;
78         int err;
79
80         path = of_get_property(of_chosen, "linux,stdout-path", NULL);
81         if (!path)
82                 return;
83
84         stdout = of_find_node_by_path(path);
85         if (!stdout)
86                 return;
87
88         for_each_compatible_node(np, "serial", "marvell,mv64360-mpsc") {
89                 if (np == stdout)
90                         break;
91         }
92
93         of_node_put(stdout);
94         if (!np)
95                 return;
96
97         block_index = of_get_property(np, "cell-index", NULL);
98         if (!block_index)
99                 goto error;
100
101         switch (*block_index) {
102         case 0:
103                 intr_cause_offset = MPSC_INTR_CAUSE_OFF_0;
104                 break;
105         case 1:
106                 intr_cause_offset = MPSC_INTR_CAUSE_OFF_1;
107                 break;
108         default:
109                 goto error;
110         }
111
112         err = of_address_to_resource(np, 0, &r[0]);
113         if (err)
114                 goto error;
115
116         ph = of_get_property(np, "mpscintr", NULL);
117         mpscintr = of_find_node_by_phandle(*ph);
118         if (!mpscintr)
119                 goto error;
120
121         err = of_address_to_resource(mpscintr, 0, &r[1]);
122         of_node_put(mpscintr);
123         if (err)
124                 goto error;
125
126         of_node_put(np);
127
128         mpsc_base = ioremap(r[0].start, r[0].end - r[0].start + 1);
129         if (!mpsc_base)
130                 return;
131
132         mpsc_intr_cause = ioremap(r[1].start, r[1].end - r[1].start + 1);
133         if (!mpsc_intr_cause) {
134                 iounmap(mpsc_base);
135                 return;
136         }
137         mpsc_intr_cause += intr_cause_offset;
138
139         udbg_putc = mv64x60_udbg_putc;
140         udbg_getc = mv64x60_udbg_getc;
141         udbg_getc_poll = mv64x60_udbg_getc_poll;
142
143         return;
144
145 error:
146         of_node_put(np);
147 }
148
149 void mv64x60_init_early(void)
150 {
151         mv64x60_udbg_init();
152 }