162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic serial console support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Mark A. Greer <mgreer@mvista.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Code in serial_edit_cmdline() copied from <file:arch/ppc/boot/simple/misc.c> 862306a36Sopenharmony_ci * and was written by Matt Porter <mporter@kernel.crashing.org>. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * 2001,2006 (c) MontaVista Software, Inc. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#include <stdarg.h> 1362306a36Sopenharmony_ci#include <stddef.h> 1462306a36Sopenharmony_ci#include "types.h" 1562306a36Sopenharmony_ci#include "string.h" 1662306a36Sopenharmony_ci#include "stdio.h" 1762306a36Sopenharmony_ci#include "io.h" 1862306a36Sopenharmony_ci#include "ops.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic int serial_open(void) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct serial_console_data *scdp = console_ops.data; 2362306a36Sopenharmony_ci return scdp->open(); 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic void serial_write(const char *buf, int len) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct serial_console_data *scdp = console_ops.data; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci while (*buf != '\0') 3162306a36Sopenharmony_ci scdp->putc(*buf++); 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic void serial_edit_cmdline(char *buf, int len, unsigned int timeout) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci int timer = 0, count; 3762306a36Sopenharmony_ci char ch, *cp; 3862306a36Sopenharmony_ci struct serial_console_data *scdp = console_ops.data; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci cp = buf; 4162306a36Sopenharmony_ci count = strlen(buf); 4262306a36Sopenharmony_ci cp = &buf[count]; 4362306a36Sopenharmony_ci count++; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci do { 4662306a36Sopenharmony_ci if (scdp->tstc()) { 4762306a36Sopenharmony_ci while (((ch = scdp->getc()) != '\n') && (ch != '\r')) { 4862306a36Sopenharmony_ci /* Test for backspace/delete */ 4962306a36Sopenharmony_ci if ((ch == '\b') || (ch == '\177')) { 5062306a36Sopenharmony_ci if (cp != buf) { 5162306a36Sopenharmony_ci cp--; 5262306a36Sopenharmony_ci count--; 5362306a36Sopenharmony_ci printf("\b \b"); 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci /* Test for ^x/^u (and wipe the line) */ 5662306a36Sopenharmony_ci } else if ((ch == '\030') || (ch == '\025')) { 5762306a36Sopenharmony_ci while (cp != buf) { 5862306a36Sopenharmony_ci cp--; 5962306a36Sopenharmony_ci count--; 6062306a36Sopenharmony_ci printf("\b \b"); 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci } else if (count < len) { 6362306a36Sopenharmony_ci *cp++ = ch; 6462306a36Sopenharmony_ci count++; 6562306a36Sopenharmony_ci scdp->putc(ch); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci break; /* Exit 'timer' loop */ 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci udelay(1000); /* 1 msec */ 7162306a36Sopenharmony_ci } while (timer++ < timeout); 7262306a36Sopenharmony_ci *cp = 0; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void serial_close(void) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct serial_console_data *scdp = console_ops.data; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (scdp->close) 8062306a36Sopenharmony_ci scdp->close(); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void *serial_get_stdout_devp(void) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci void *devp; 8662306a36Sopenharmony_ci char devtype[MAX_PROP_LEN]; 8762306a36Sopenharmony_ci char path[MAX_PATH_LEN]; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci devp = finddevice("/chosen"); 9062306a36Sopenharmony_ci if (devp == NULL) 9162306a36Sopenharmony_ci goto err_out; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (getprop(devp, "linux,stdout-path", path, MAX_PATH_LEN) > 0 || 9462306a36Sopenharmony_ci getprop(devp, "stdout-path", path, MAX_PATH_LEN) > 0) { 9562306a36Sopenharmony_ci devp = finddevice(path); 9662306a36Sopenharmony_ci if (devp == NULL) 9762306a36Sopenharmony_ci goto err_out; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if ((getprop(devp, "device_type", devtype, sizeof(devtype)) > 0) 10062306a36Sopenharmony_ci && !strcmp(devtype, "serial")) 10162306a36Sopenharmony_ci return devp; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_cierr_out: 10462306a36Sopenharmony_ci return NULL; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic struct serial_console_data serial_cd; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* Node's "compatible" property determines which serial driver to use */ 11062306a36Sopenharmony_ciint serial_console_init(void) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci void *devp; 11362306a36Sopenharmony_ci int rc = -1; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci devp = serial_get_stdout_devp(); 11662306a36Sopenharmony_ci if (devp == NULL) 11762306a36Sopenharmony_ci goto err_out; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (dt_is_compatible(devp, "ns16550") || 12062306a36Sopenharmony_ci dt_is_compatible(devp, "pnpPNP,501")) 12162306a36Sopenharmony_ci rc = ns16550_console_init(devp, &serial_cd); 12262306a36Sopenharmony_ci#ifdef CONFIG_CPM 12362306a36Sopenharmony_ci else if (dt_is_compatible(devp, "fsl,cpm1-scc-uart") || 12462306a36Sopenharmony_ci dt_is_compatible(devp, "fsl,cpm1-smc-uart") || 12562306a36Sopenharmony_ci dt_is_compatible(devp, "fsl,cpm2-scc-uart") || 12662306a36Sopenharmony_ci dt_is_compatible(devp, "fsl,cpm2-smc-uart")) 12762306a36Sopenharmony_ci rc = cpm_console_init(devp, &serial_cd); 12862306a36Sopenharmony_ci#endif 12962306a36Sopenharmony_ci#ifdef CONFIG_PPC_MPC52xx 13062306a36Sopenharmony_ci else if (dt_is_compatible(devp, "fsl,mpc5200-psc-uart")) 13162306a36Sopenharmony_ci rc = mpc5200_psc_console_init(devp, &serial_cd); 13262306a36Sopenharmony_ci#endif 13362306a36Sopenharmony_ci#ifdef CONFIG_PPC_POWERNV 13462306a36Sopenharmony_ci else if (dt_is_compatible(devp, "ibm,opal-console-raw")) 13562306a36Sopenharmony_ci rc = opal_console_init(devp, &serial_cd); 13662306a36Sopenharmony_ci#endif 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* Add other serial console driver calls here */ 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!rc) { 14162306a36Sopenharmony_ci console_ops.open = serial_open; 14262306a36Sopenharmony_ci console_ops.write = serial_write; 14362306a36Sopenharmony_ci console_ops.close = serial_close; 14462306a36Sopenharmony_ci console_ops.data = &serial_cd; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (serial_cd.getc) 14762306a36Sopenharmony_ci console_ops.edit_cmdline = serial_edit_cmdline; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_cierr_out: 15262306a36Sopenharmony_ci return -1; 15362306a36Sopenharmony_ci} 154