diff options
author | Mark A. Greer <mgreer@mvista.com> | 2006-10-16 16:52:09 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-10-22 22:49:19 -0400 |
commit | 0c176fa80fdfa9b4e0753e37223b056994c818d2 (patch) | |
tree | aa45e2bba6f57ac1e85e81f2482667c0873fefa4 /arch | |
parent | 6fb4efc68f5c0e095153510dcfa8b54a42e914ba (diff) |
[POWERPC] Add non-OF serial console support
Add serial console support for non-OF systems. There is a generic serial
console layer which calls a serial console driver. Included is the serial
console driver for the ns16550 class of uarts. Necessary support routines
are added as well.
Signed-off-by: Mark A. Greer <mgreer@mvista.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/boot/Makefile | 4 | ||||
-rw-r--r-- | arch/powerpc/boot/io.h | 53 | ||||
-rw-r--r-- | arch/powerpc/boot/ns16550.c | 74 | ||||
-rw-r--r-- | arch/powerpc/boot/serial.c | 142 | ||||
-rw-r--r-- | arch/powerpc/boot/util.S | 88 |
5 files changed, 359 insertions, 2 deletions
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 8660cc50cb18..62435d951dbd 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile | |||
@@ -40,8 +40,8 @@ zliblinuxheader := zlib.h zconf.h zutil.h | |||
40 | $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \ | 40 | $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \ |
41 | $(addprefix $(obj)/,$(zlibheader)) | 41 | $(addprefix $(obj)/,$(zlibheader)) |
42 | 42 | ||
43 | src-wlib := string.S stdio.c main.c flatdevtree.c flatdevtree_misc.c div64.S \ | 43 | src-wlib := string.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \ |
44 | $(zlib) | 44 | ns16550.c serial.c div64.S util.S $(zlib) |
45 | src-plat := of.c | 45 | src-plat := of.c |
46 | src-boot := crt0.S $(src-wlib) $(src-plat) empty.c | 46 | src-boot := crt0.S $(src-wlib) $(src-plat) empty.c |
47 | 47 | ||
diff --git a/arch/powerpc/boot/io.h b/arch/powerpc/boot/io.h new file mode 100644 index 000000000000..32974ed49e02 --- /dev/null +++ b/arch/powerpc/boot/io.h | |||
@@ -0,0 +1,53 @@ | |||
1 | #ifndef _IO_H | ||
2 | #define __IO_H | ||
3 | /* | ||
4 | * Low-level I/O routines. | ||
5 | * | ||
6 | * Copied from <file:include/asm-powerpc/io.h> (which has no copyright) | ||
7 | */ | ||
8 | static inline int in_8(const volatile unsigned char *addr) | ||
9 | { | ||
10 | int ret; | ||
11 | |||
12 | __asm__ __volatile__("lbz%U1%X1 %0,%1; twi 0,%0,0; isync" | ||
13 | : "=r" (ret) : "m" (*addr)); | ||
14 | return ret; | ||
15 | } | ||
16 | |||
17 | static inline void out_8(volatile unsigned char *addr, int val) | ||
18 | { | ||
19 | __asm__ __volatile__("stb%U0%X0 %1,%0; sync" | ||
20 | : "=m" (*addr) : "r" (val)); | ||
21 | } | ||
22 | |||
23 | static inline unsigned in_le32(const volatile unsigned *addr) | ||
24 | { | ||
25 | unsigned ret; | ||
26 | |||
27 | __asm__ __volatile__("lwbrx %0,0,%1; twi 0,%0,0; isync" | ||
28 | : "=r" (ret) : "r" (addr), "m" (*addr)); | ||
29 | return ret; | ||
30 | } | ||
31 | |||
32 | static inline unsigned in_be32(const volatile unsigned *addr) | ||
33 | { | ||
34 | unsigned ret; | ||
35 | |||
36 | __asm__ __volatile__("lwz%U1%X1 %0,%1; twi 0,%0,0; isync" | ||
37 | : "=r" (ret) : "m" (*addr)); | ||
38 | return ret; | ||
39 | } | ||
40 | |||
41 | static inline void out_le32(volatile unsigned *addr, int val) | ||
42 | { | ||
43 | __asm__ __volatile__("stwbrx %1,0,%2; sync" : "=m" (*addr) | ||
44 | : "r" (val), "r" (addr)); | ||
45 | } | ||
46 | |||
47 | static inline void out_be32(volatile unsigned *addr, int val) | ||
48 | { | ||
49 | __asm__ __volatile__("stw%U0%X0 %1,%0; sync" | ||
50 | : "=m" (*addr) : "r" (val)); | ||
51 | } | ||
52 | |||
53 | #endif /* _IO_H */ | ||
diff --git a/arch/powerpc/boot/ns16550.c b/arch/powerpc/boot/ns16550.c new file mode 100644 index 000000000000..1ffe72e35cdc --- /dev/null +++ b/arch/powerpc/boot/ns16550.c | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * 16550 serial console support. | ||
3 | * | ||
4 | * Original copied from <file:arch/ppc/boot/common/ns16550.c> | ||
5 | * (which had no copyright) | ||
6 | * Modifications: 2006 (c) MontaVista Software, Inc. | ||
7 | * | ||
8 | * Modified by: Mark A. Greer <mgreer@mvista.com> | ||
9 | */ | ||
10 | #include <stdarg.h> | ||
11 | #include <stddef.h> | ||
12 | #include "types.h" | ||
13 | #include "string.h" | ||
14 | #include "stdio.h" | ||
15 | #include "io.h" | ||
16 | #include "ops.h" | ||
17 | |||
18 | #define UART_DLL 0 /* Out: Divisor Latch Low */ | ||
19 | #define UART_DLM 1 /* Out: Divisor Latch High */ | ||
20 | #define UART_FCR 2 /* Out: FIFO Control Register */ | ||
21 | #define UART_LCR 3 /* Out: Line Control Register */ | ||
22 | #define UART_MCR 4 /* Out: Modem Control Register */ | ||
23 | #define UART_LSR 5 /* In: Line Status Register */ | ||
24 | #define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ | ||
25 | #define UART_LSR_DR 0x01 /* Receiver data ready */ | ||
26 | #define UART_MSR 6 /* In: Modem Status Register */ | ||
27 | #define UART_SCR 7 /* I/O: Scratch Register */ | ||
28 | |||
29 | static unsigned char *reg_base; | ||
30 | static u32 reg_shift; | ||
31 | |||
32 | static int ns16550_open(void) | ||
33 | { | ||
34 | out_8(reg_base + (UART_FCR << reg_shift), 0x06); | ||
35 | return 0; | ||
36 | } | ||
37 | |||
38 | static void ns16550_putc(unsigned char c) | ||
39 | { | ||
40 | while ((in_8(reg_base + (UART_LSR << reg_shift)) & UART_LSR_THRE) == 0); | ||
41 | out_8(reg_base, c); | ||
42 | } | ||
43 | |||
44 | static unsigned char ns16550_getc(void) | ||
45 | { | ||
46 | while ((in_8(reg_base + (UART_LSR << reg_shift)) & UART_LSR_DR) == 0); | ||
47 | return in_8(reg_base); | ||
48 | } | ||
49 | |||
50 | static u8 ns16550_tstc(void) | ||
51 | { | ||
52 | return ((in_8(reg_base + (UART_LSR << reg_shift)) & UART_LSR_DR) != 0); | ||
53 | } | ||
54 | |||
55 | int ns16550_console_init(void *devp, struct serial_console_data *scdp) | ||
56 | { | ||
57 | int n; | ||
58 | |||
59 | n = getprop(devp, "virtual-reg", ®_base, sizeof(reg_base)); | ||
60 | if (n != sizeof(reg_base)) | ||
61 | return -1; | ||
62 | |||
63 | n = getprop(devp, "reg-shift", ®_shift, sizeof(reg_shift)); | ||
64 | if (n != sizeof(reg_shift)) | ||
65 | reg_shift = 0; | ||
66 | |||
67 | scdp->open = ns16550_open; | ||
68 | scdp->putc = ns16550_putc; | ||
69 | scdp->getc = ns16550_getc; | ||
70 | scdp->tstc = ns16550_tstc; | ||
71 | scdp->close = NULL; | ||
72 | |||
73 | return 0; | ||
74 | } | ||
diff --git a/arch/powerpc/boot/serial.c b/arch/powerpc/boot/serial.c new file mode 100644 index 000000000000..e8de4cf59be7 --- /dev/null +++ b/arch/powerpc/boot/serial.c | |||
@@ -0,0 +1,142 @@ | |||
1 | /* | ||
2 | * Generic serial console support | ||
3 | * | ||
4 | * Author: Mark A. Greer <mgreer@mvista.com> | ||
5 | * | ||
6 | * Code in serial_edit_cmdline() copied from <file:arch/ppc/boot/simple/misc.c> | ||
7 | * and was written by Matt Porter <mporter@kernel.crashing.org>. | ||
8 | * | ||
9 | * 2001,2006 (c) MontaVista Software, Inc. This file is licensed under | ||
10 | * the terms of the GNU General Public License version 2. This program | ||
11 | * is licensed "as is" without any warranty of any kind, whether express | ||
12 | * or implied. | ||
13 | */ | ||
14 | #include <stdarg.h> | ||
15 | #include <stddef.h> | ||
16 | #include "types.h" | ||
17 | #include "string.h" | ||
18 | #include "stdio.h" | ||
19 | #include "io.h" | ||
20 | #include "ops.h" | ||
21 | |||
22 | extern void udelay(long delay); | ||
23 | |||
24 | static int serial_open(void) | ||
25 | { | ||
26 | struct serial_console_data *scdp = console_ops.data; | ||
27 | return scdp->open(); | ||
28 | } | ||
29 | |||
30 | static void serial_write(char *buf, int len) | ||
31 | { | ||
32 | struct serial_console_data *scdp = console_ops.data; | ||
33 | |||
34 | while (*buf != '\0') | ||
35 | scdp->putc(*buf++); | ||
36 | } | ||
37 | |||
38 | static void serial_edit_cmdline(char *buf, int len) | ||
39 | { | ||
40 | int timer = 0, count; | ||
41 | char ch, *cp; | ||
42 | struct serial_console_data *scdp = console_ops.data; | ||
43 | |||
44 | cp = buf; | ||
45 | count = strlen(buf); | ||
46 | cp = &buf[count]; | ||
47 | count++; | ||
48 | |||
49 | while (timer++ < 5*1000) { | ||
50 | if (scdp->tstc()) { | ||
51 | while (((ch = scdp->getc()) != '\n') && (ch != '\r')) { | ||
52 | /* Test for backspace/delete */ | ||
53 | if ((ch == '\b') || (ch == '\177')) { | ||
54 | if (cp != buf) { | ||
55 | cp--; | ||
56 | count--; | ||
57 | printf("\b \b"); | ||
58 | } | ||
59 | /* Test for ^x/^u (and wipe the line) */ | ||
60 | } else if ((ch == '\030') || (ch == '\025')) { | ||
61 | while (cp != buf) { | ||
62 | cp--; | ||
63 | count--; | ||
64 | printf("\b \b"); | ||
65 | } | ||
66 | } else if (count < len) { | ||
67 | *cp++ = ch; | ||
68 | count++; | ||
69 | scdp->putc(ch); | ||
70 | } | ||
71 | } | ||
72 | break; /* Exit 'timer' loop */ | ||
73 | } | ||
74 | udelay(1000); /* 1 msec */ | ||
75 | } | ||
76 | *cp = 0; | ||
77 | } | ||
78 | |||
79 | static void serial_close(void) | ||
80 | { | ||
81 | struct serial_console_data *scdp = console_ops.data; | ||
82 | |||
83 | if (scdp->close) | ||
84 | scdp->close(); | ||
85 | } | ||
86 | |||
87 | static void *serial_get_stdout_devp(void) | ||
88 | { | ||
89 | void *devp; | ||
90 | char devtype[MAX_PROP_LEN]; | ||
91 | char path[MAX_PATH_LEN]; | ||
92 | |||
93 | devp = finddevice("/chosen"); | ||
94 | if (devp == NULL) | ||
95 | goto err_out; | ||
96 | |||
97 | if (getprop(devp, "linux,stdout-path", path, MAX_PATH_LEN) > 0) { | ||
98 | devp = finddevice(path); | ||
99 | if (devp == NULL) | ||
100 | goto err_out; | ||
101 | |||
102 | if ((getprop(devp, "device_type", devtype, sizeof(devtype)) > 0) | ||
103 | && !strcmp(devtype, "serial")) | ||
104 | return devp; | ||
105 | } | ||
106 | err_out: | ||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | static struct serial_console_data serial_cd; | ||
111 | |||
112 | /* Node's "compatible" property determines which serial driver to use */ | ||
113 | int serial_console_init(void) | ||
114 | { | ||
115 | void *devp; | ||
116 | int rc = -1; | ||
117 | char compat[MAX_PROP_LEN]; | ||
118 | |||
119 | devp = serial_get_stdout_devp(); | ||
120 | if (devp == NULL) | ||
121 | goto err_out; | ||
122 | |||
123 | if (getprop(devp, "compatible", compat, sizeof(compat)) < 0) | ||
124 | goto err_out; | ||
125 | |||
126 | if (!strcmp(compat, "ns16550")) | ||
127 | rc = ns16550_console_init(devp, &serial_cd); | ||
128 | |||
129 | /* Add other serial console driver calls here */ | ||
130 | |||
131 | if (!rc) { | ||
132 | console_ops.open = serial_open; | ||
133 | console_ops.write = serial_write; | ||
134 | console_ops.edit_cmdline = serial_edit_cmdline; | ||
135 | console_ops.close = serial_close; | ||
136 | console_ops.data = &serial_cd; | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | err_out: | ||
141 | return -1; | ||
142 | } | ||
diff --git a/arch/powerpc/boot/util.S b/arch/powerpc/boot/util.S new file mode 100644 index 000000000000..427ddfc11991 --- /dev/null +++ b/arch/powerpc/boot/util.S | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * Copied from <file:arch/powerpc/kernel/misc_32.S> | ||
3 | * | ||
4 | * This file contains miscellaneous low-level functions. | ||
5 | * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) | ||
6 | * | ||
7 | * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) | ||
8 | * and Paul Mackerras. | ||
9 | * | ||
10 | * kexec bits: | ||
11 | * Copyright (C) 2002-2003 Eric Biederman <ebiederm@xmission.com> | ||
12 | * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License | ||
16 | * as published by the Free Software Foundation; either version | ||
17 | * 2 of the License, or (at your option) any later version. | ||
18 | * | ||
19 | */ | ||
20 | #include "ppc_asm.h" | ||
21 | |||
22 | #define SPRN_PVR 0x11F /* Processor Version Register */ | ||
23 | |||
24 | .text | ||
25 | |||
26 | /* udelay (on non-601 processors) needs to know the period of the | ||
27 | * timebase in nanoseconds. This used to be hardcoded to be 60ns | ||
28 | * (period of 66MHz/4). Now a variable is used that is initialized to | ||
29 | * 60 for backward compatibility, but it can be overridden as necessary | ||
30 | * with code something like this: | ||
31 | * extern unsigned long timebase_period_ns; | ||
32 | * timebase_period_ns = 1000000000 / bd->bi_tbfreq; | ||
33 | */ | ||
34 | .data | ||
35 | .globl timebase_period_ns | ||
36 | timebase_period_ns: | ||
37 | .long 60 | ||
38 | |||
39 | .text | ||
40 | /* | ||
41 | * Delay for a number of microseconds | ||
42 | */ | ||
43 | .globl udelay | ||
44 | udelay: | ||
45 | mfspr r4,SPRN_PVR | ||
46 | srwi r4,r4,16 | ||
47 | cmpwi 0,r4,1 /* 601 ? */ | ||
48 | bne .udelay_not_601 | ||
49 | 00: li r0,86 /* Instructions / microsecond? */ | ||
50 | mtctr r0 | ||
51 | 10: addi r0,r0,0 /* NOP */ | ||
52 | bdnz 10b | ||
53 | subic. r3,r3,1 | ||
54 | bne 00b | ||
55 | blr | ||
56 | |||
57 | .udelay_not_601: | ||
58 | mulli r4,r3,1000 /* nanoseconds */ | ||
59 | /* Change r4 to be the number of ticks using: | ||
60 | * (nanoseconds + (timebase_period_ns - 1 )) / timebase_period_ns | ||
61 | * timebase_period_ns defaults to 60 (16.6MHz) */ | ||
62 | mflr r5 | ||
63 | bl 0f | ||
64 | 0: mflr r6 | ||
65 | mtlr r5 | ||
66 | lis r5,0b@ha | ||
67 | addi r5,r5,0b@l | ||
68 | subf r5,r5,r6 /* In case we're relocated */ | ||
69 | addis r5,r5,timebase_period_ns@ha | ||
70 | lwz r5,timebase_period_ns@l(r5) | ||
71 | add r4,r4,r5 | ||
72 | addi r4,r4,-1 | ||
73 | divw r4,r4,r5 /* BUS ticks */ | ||
74 | 1: mftbu r5 | ||
75 | mftb r6 | ||
76 | mftbu r7 | ||
77 | cmpw 0,r5,r7 | ||
78 | bne 1b /* Get [synced] base time */ | ||
79 | addc r9,r6,r4 /* Compute end time */ | ||
80 | addze r8,r5 | ||
81 | 2: mftbu r5 | ||
82 | cmpw 0,r5,r8 | ||
83 | blt 2b | ||
84 | bgt 3f | ||
85 | mftb r6 | ||
86 | cmpw 0,r6,r9 | ||
87 | blt 2b | ||
88 | 3: blr | ||