1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Generic serial console support 4 * 5 * Author: Mark A. Greer <mgreer@mvista.com> 6 * 7 * Code in serial_edit_cmdline() copied from <file:arch/ppc/boot/simple/misc.c> 8 * and was written by Matt Porter <mporter@kernel.crashing.org>. 9 * 10 * 2001,2006 (c) MontaVista Software, Inc. 11 */ 12 #include <stdarg.h> 13 #include <stddef.h> 14 #include "types.h" 15 #include "string.h" 16 #include "stdio.h" 17 #include "io.h" 18 #include "ops.h" 19 20 static int serial_open(void) 21 { 22 struct serial_console_data *scdp = console_ops.data; 23 return scdp->open(); 24 } 25 26 static void serial_write(const char *buf, int len) 27 { 28 struct serial_console_data *scdp = console_ops.data; 29 30 while (*buf != '\0') 31 scdp->putc(*buf++); 32 } 33 34 static void serial_edit_cmdline(char *buf, int len, unsigned int timeout) 35 { 36 int timer = 0, count; 37 char ch, *cp; 38 struct serial_console_data *scdp = console_ops.data; 39 40 count = strlen(buf); 41 cp = &buf[count]; 42 count++; 43 44 do { 45 if (scdp->tstc()) { 46 while (((ch = scdp->getc()) != '\n') && (ch != '\r')) { 47 /* Test for backspace/delete */ 48 if ((ch == '\b') || (ch == '\177')) { 49 if (cp != buf) { 50 cp--; 51 count--; 52 printf("\b \b"); 53 } 54 /* Test for ^x/^u (and wipe the line) */ 55 } else if ((ch == '\030') || (ch == '\025')) { 56 while (cp != buf) { 57 cp--; 58 count--; 59 printf("\b \b"); 60 } 61 } else if (count < len) { 62 *cp++ = ch; 63 count++; 64 scdp->putc(ch); 65 } 66 } 67 break; /* Exit 'timer' loop */ 68 } 69 udelay(1000); /* 1 msec */ 70 } while (timer++ < timeout); 71 *cp = 0; 72 } 73 74 static void serial_close(void) 75 { 76 struct serial_console_data *scdp = console_ops.data; 77 78 if (scdp->close) 79 scdp->close(); 80 } 81 82 static void *serial_get_stdout_devp(void) 83 { 84 void *devp; 85 char devtype[MAX_PROP_LEN]; 86 char path[MAX_PATH_LEN]; 87 88 devp = finddevice("/chosen"); 89 if (devp == NULL) 90 goto err_out; 91 92 if (getprop(devp, "linux,stdout-path", path, MAX_PATH_LEN) > 0 || 93 getprop(devp, "stdout-path", path, MAX_PATH_LEN) > 0) { 94 devp = finddevice(path); 95 if (devp == NULL) 96 goto err_out; 97 98 if ((getprop(devp, "device_type", devtype, sizeof(devtype)) > 0) 99 && !strcmp(devtype, "serial")) 100 return devp; 101 } 102 err_out: 103 return NULL; 104 } 105 106 static struct serial_console_data serial_cd; 107 108 /* Node's "compatible" property determines which serial driver to use */ 109 int serial_console_init(void) 110 { 111 void *devp; 112 int rc = -1; 113 114 devp = serial_get_stdout_devp(); 115 if (devp == NULL) 116 goto err_out; 117 118 if (dt_is_compatible(devp, "ns16550") || 119 dt_is_compatible(devp, "pnpPNP,501")) 120 rc = ns16550_console_init(devp, &serial_cd); 121 #ifdef CONFIG_CPM 122 else if (dt_is_compatible(devp, "fsl,cpm1-scc-uart") || 123 dt_is_compatible(devp, "fsl,cpm1-smc-uart") || 124 dt_is_compatible(devp, "fsl,cpm2-scc-uart") || 125 dt_is_compatible(devp, "fsl,cpm2-smc-uart")) 126 rc = cpm_console_init(devp, &serial_cd); 127 #endif 128 #ifdef CONFIG_PPC_MPC52xx 129 else if (dt_is_compatible(devp, "fsl,mpc5200-psc-uart")) 130 rc = mpc5200_psc_console_init(devp, &serial_cd); 131 #endif 132 #ifdef CONFIG_PPC_POWERNV 133 else if (dt_is_compatible(devp, "ibm,opal-console-raw")) 134 rc = opal_console_init(devp, &serial_cd); 135 #endif 136 137 /* Add other serial console driver calls here */ 138 139 if (!rc) { 140 console_ops.open = serial_open; 141 console_ops.write = serial_write; 142 console_ops.close = serial_close; 143 console_ops.data = &serial_cd; 144 145 if (serial_cd.getc) 146 console_ops.edit_cmdline = serial_edit_cmdline; 147 148 return 0; 149 } 150 err_out: 151 return -1; 152 } 153