18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Generic serial console support
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Author: Mark A. Greer <mgreer@mvista.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Code in serial_edit_cmdline() copied from <file:arch/ppc/boot/simple/misc.c>
78c2ecf20Sopenharmony_ci * and was written by Matt Porter <mporter@kernel.crashing.org>.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * 2001,2006 (c) MontaVista Software, Inc.  This file is licensed under
108c2ecf20Sopenharmony_ci * the terms of the GNU General Public License version 2.  This program
118c2ecf20Sopenharmony_ci * is licensed "as is" without any warranty of any kind, whether express
128c2ecf20Sopenharmony_ci * or implied.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci#include <stdarg.h>
158c2ecf20Sopenharmony_ci#include <stddef.h>
168c2ecf20Sopenharmony_ci#include "types.h"
178c2ecf20Sopenharmony_ci#include "string.h"
188c2ecf20Sopenharmony_ci#include "stdio.h"
198c2ecf20Sopenharmony_ci#include "io.h"
208c2ecf20Sopenharmony_ci#include "ops.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int serial_open(void)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	struct serial_console_data *scdp = console_ops.data;
258c2ecf20Sopenharmony_ci	return scdp->open();
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic void serial_write(const char *buf, int len)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct serial_console_data *scdp = console_ops.data;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	while (*buf != '\0')
338c2ecf20Sopenharmony_ci		scdp->putc(*buf++);
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic void serial_edit_cmdline(char *buf, int len, unsigned int timeout)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	int timer = 0, count;
398c2ecf20Sopenharmony_ci	char ch, *cp;
408c2ecf20Sopenharmony_ci	struct serial_console_data *scdp = console_ops.data;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	cp = buf;
438c2ecf20Sopenharmony_ci	count = strlen(buf);
448c2ecf20Sopenharmony_ci	cp = &buf[count];
458c2ecf20Sopenharmony_ci	count++;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	do {
488c2ecf20Sopenharmony_ci		if (scdp->tstc()) {
498c2ecf20Sopenharmony_ci			while (((ch = scdp->getc()) != '\n') && (ch != '\r')) {
508c2ecf20Sopenharmony_ci				/* Test for backspace/delete */
518c2ecf20Sopenharmony_ci				if ((ch == '\b') || (ch == '\177')) {
528c2ecf20Sopenharmony_ci					if (cp != buf) {
538c2ecf20Sopenharmony_ci						cp--;
548c2ecf20Sopenharmony_ci						count--;
558c2ecf20Sopenharmony_ci						printf("\b \b");
568c2ecf20Sopenharmony_ci					}
578c2ecf20Sopenharmony_ci				/* Test for ^x/^u (and wipe the line) */
588c2ecf20Sopenharmony_ci				} else if ((ch == '\030') || (ch == '\025')) {
598c2ecf20Sopenharmony_ci					while (cp != buf) {
608c2ecf20Sopenharmony_ci						cp--;
618c2ecf20Sopenharmony_ci						count--;
628c2ecf20Sopenharmony_ci						printf("\b \b");
638c2ecf20Sopenharmony_ci					}
648c2ecf20Sopenharmony_ci				} else if (count < len) {
658c2ecf20Sopenharmony_ci						*cp++ = ch;
668c2ecf20Sopenharmony_ci						count++;
678c2ecf20Sopenharmony_ci						scdp->putc(ch);
688c2ecf20Sopenharmony_ci				}
698c2ecf20Sopenharmony_ci			}
708c2ecf20Sopenharmony_ci			break;  /* Exit 'timer' loop */
718c2ecf20Sopenharmony_ci		}
728c2ecf20Sopenharmony_ci		udelay(1000);  /* 1 msec */
738c2ecf20Sopenharmony_ci	} while (timer++ < timeout);
748c2ecf20Sopenharmony_ci	*cp = 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic void serial_close(void)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct serial_console_data *scdp = console_ops.data;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (scdp->close)
828c2ecf20Sopenharmony_ci		scdp->close();
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic void *serial_get_stdout_devp(void)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	void *devp;
888c2ecf20Sopenharmony_ci	char devtype[MAX_PROP_LEN];
898c2ecf20Sopenharmony_ci	char path[MAX_PATH_LEN];
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	devp = finddevice("/chosen");
928c2ecf20Sopenharmony_ci	if (devp == NULL)
938c2ecf20Sopenharmony_ci		goto err_out;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (getprop(devp, "linux,stdout-path", path, MAX_PATH_LEN) > 0 ||
968c2ecf20Sopenharmony_ci		getprop(devp, "stdout-path", path, MAX_PATH_LEN) > 0) {
978c2ecf20Sopenharmony_ci		devp = finddevice(path);
988c2ecf20Sopenharmony_ci		if (devp == NULL)
998c2ecf20Sopenharmony_ci			goto err_out;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		if ((getprop(devp, "device_type", devtype, sizeof(devtype)) > 0)
1028c2ecf20Sopenharmony_ci				&& !strcmp(devtype, "serial"))
1038c2ecf20Sopenharmony_ci			return devp;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_cierr_out:
1068c2ecf20Sopenharmony_ci	return NULL;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic struct serial_console_data serial_cd;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* Node's "compatible" property determines which serial driver to use */
1128c2ecf20Sopenharmony_ciint serial_console_init(void)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	void *devp;
1158c2ecf20Sopenharmony_ci	int rc = -1;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	devp = serial_get_stdout_devp();
1188c2ecf20Sopenharmony_ci	if (devp == NULL)
1198c2ecf20Sopenharmony_ci		goto err_out;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (dt_is_compatible(devp, "ns16550") ||
1228c2ecf20Sopenharmony_ci	    dt_is_compatible(devp, "pnpPNP,501"))
1238c2ecf20Sopenharmony_ci		rc = ns16550_console_init(devp, &serial_cd);
1248c2ecf20Sopenharmony_ci#ifdef CONFIG_CPM
1258c2ecf20Sopenharmony_ci	else if (dt_is_compatible(devp, "fsl,cpm1-scc-uart") ||
1268c2ecf20Sopenharmony_ci	         dt_is_compatible(devp, "fsl,cpm1-smc-uart") ||
1278c2ecf20Sopenharmony_ci	         dt_is_compatible(devp, "fsl,cpm2-scc-uart") ||
1288c2ecf20Sopenharmony_ci	         dt_is_compatible(devp, "fsl,cpm2-smc-uart"))
1298c2ecf20Sopenharmony_ci		rc = cpm_console_init(devp, &serial_cd);
1308c2ecf20Sopenharmony_ci#endif
1318c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_MPC52xx
1328c2ecf20Sopenharmony_ci	else if (dt_is_compatible(devp, "fsl,mpc5200-psc-uart"))
1338c2ecf20Sopenharmony_ci		rc = mpc5200_psc_console_init(devp, &serial_cd);
1348c2ecf20Sopenharmony_ci#endif
1358c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64_BOOT_WRAPPER
1368c2ecf20Sopenharmony_ci	else if (dt_is_compatible(devp, "ibm,opal-console-raw"))
1378c2ecf20Sopenharmony_ci		rc = opal_console_init(devp, &serial_cd);
1388c2ecf20Sopenharmony_ci#endif
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* Add other serial console driver calls here */
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (!rc) {
1438c2ecf20Sopenharmony_ci		console_ops.open = serial_open;
1448c2ecf20Sopenharmony_ci		console_ops.write = serial_write;
1458c2ecf20Sopenharmony_ci		console_ops.close = serial_close;
1468c2ecf20Sopenharmony_ci		console_ops.data = &serial_cd;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		if (serial_cd.getc)
1498c2ecf20Sopenharmony_ci			console_ops.edit_cmdline = serial_edit_cmdline;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		return 0;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_cierr_out:
1548c2ecf20Sopenharmony_ci	return -1;
1558c2ecf20Sopenharmony_ci}
156