Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Mark A. Greer and committed by
Paul Mackerras
0c176fa8 6fb4efc6

+359 -2
+2 -2
arch/powerpc/boot/Makefile
··· 40 40 $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \ 41 41 $(addprefix $(obj)/,$(zlibheader)) 42 42 43 - src-wlib := string.S stdio.c main.c flatdevtree.c flatdevtree_misc.c div64.S \ 44 - $(zlib) 43 + src-wlib := string.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \ 44 + ns16550.c serial.c div64.S util.S $(zlib) 45 45 src-plat := of.c 46 46 src-boot := crt0.S $(src-wlib) $(src-plat) empty.c 47 47
+53
arch/powerpc/boot/io.h
··· 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 */
+74
arch/powerpc/boot/ns16550.c
··· 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", &reg_base, sizeof(reg_base)); 60 + if (n != sizeof(reg_base)) 61 + return -1; 62 + 63 + n = getprop(devp, "reg-shift", &reg_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 + }
+142
arch/powerpc/boot/serial.c
··· 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 + }
+88
arch/powerpc/boot/util.S
··· 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