Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
mv64x60_udbg.c
Go to the documentation of this file.
1 /*
2  * udbg serial input/output routines for the Marvell MV64x60 (Discovery).
3  *
4  * Author: Dale Farnsworth <[email protected]>
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, resource_size(&r[0]));
129  if (!mpsc_base)
130  return;
131 
132  mpsc_intr_cause = ioremap(r[1].start, resource_size(&r[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 
150 {
151  mv64x60_udbg_init();
152 }