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