18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* suncore.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Common SUN serial routines.  Based entirely
58c2ecf20Sopenharmony_ci * upon drivers/sbus/char/sunserial.c which is:
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Adaptation to new UART layer is:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Copyright (C) 2002 David S. Miller (davem@redhat.com)
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/console.h>
168c2ecf20Sopenharmony_ci#include <linux/tty.h>
178c2ecf20Sopenharmony_ci#include <linux/errno.h>
188c2ecf20Sopenharmony_ci#include <linux/string.h>
198c2ecf20Sopenharmony_ci#include <linux/serial_core.h>
208c2ecf20Sopenharmony_ci#include <linux/sunserialcore.h>
218c2ecf20Sopenharmony_ci#include <linux/init.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <asm/prom.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int sunserial_current_minor = 64;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ciint sunserial_register_minors(struct uart_driver *drv, int count)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	int err = 0;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	drv->minor = sunserial_current_minor;
338c2ecf20Sopenharmony_ci	drv->nr += count;
348c2ecf20Sopenharmony_ci	/* Register the driver on the first call */
358c2ecf20Sopenharmony_ci	if (drv->nr == count)
368c2ecf20Sopenharmony_ci		err = uart_register_driver(drv);
378c2ecf20Sopenharmony_ci	if (err == 0) {
388c2ecf20Sopenharmony_ci		sunserial_current_minor += count;
398c2ecf20Sopenharmony_ci		drv->tty_driver->name_base = drv->minor - 64;
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci	return err;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sunserial_register_minors);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_civoid sunserial_unregister_minors(struct uart_driver *drv, int count)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	drv->nr -= count;
488c2ecf20Sopenharmony_ci	sunserial_current_minor -= count;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (drv->nr == 0)
518c2ecf20Sopenharmony_ci		uart_unregister_driver(drv);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sunserial_unregister_minors);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ciint sunserial_console_match(struct console *con, struct device_node *dp,
568c2ecf20Sopenharmony_ci			    struct uart_driver *drv, int line, bool ignore_line)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	if (!con)
598c2ecf20Sopenharmony_ci		return 0;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	drv->cons = con;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (of_console_device != dp)
648c2ecf20Sopenharmony_ci		return 0;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (!ignore_line) {
678c2ecf20Sopenharmony_ci		int off = 0;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci		if (of_console_options &&
708c2ecf20Sopenharmony_ci		    *of_console_options == 'b')
718c2ecf20Sopenharmony_ci			off = 1;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		if ((line & 1) != off)
748c2ecf20Sopenharmony_ci			return 0;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (!console_set_on_cmdline) {
788c2ecf20Sopenharmony_ci		con->index = line;
798c2ecf20Sopenharmony_ci		add_preferred_console(con->name, line, NULL);
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci	return 1;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sunserial_console_match);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_civoid sunserial_console_termios(struct console *con, struct device_node *uart_dp)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	const char *mode, *s;
888c2ecf20Sopenharmony_ci	char mode_prop[] = "ttyX-mode";
898c2ecf20Sopenharmony_ci	int baud, bits, stop, cflag;
908c2ecf20Sopenharmony_ci	char parity;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (of_node_name_eq(uart_dp, "rsc") ||
938c2ecf20Sopenharmony_ci	    of_node_name_eq(uart_dp, "rsc-console") ||
948c2ecf20Sopenharmony_ci	    of_node_name_eq(uart_dp, "rsc-control")) {
958c2ecf20Sopenharmony_ci		mode = of_get_property(uart_dp,
968c2ecf20Sopenharmony_ci				       "ssp-console-modes", NULL);
978c2ecf20Sopenharmony_ci		if (!mode)
988c2ecf20Sopenharmony_ci			mode = "115200,8,n,1,-";
998c2ecf20Sopenharmony_ci	} else if (of_node_name_eq(uart_dp, "lom-console")) {
1008c2ecf20Sopenharmony_ci		mode = "9600,8,n,1,-";
1018c2ecf20Sopenharmony_ci	} else {
1028c2ecf20Sopenharmony_ci		struct device_node *dp;
1038c2ecf20Sopenharmony_ci		char c;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		c = 'a';
1068c2ecf20Sopenharmony_ci		if (of_console_options)
1078c2ecf20Sopenharmony_ci			c = *of_console_options;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		mode_prop[3] = c;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		dp = of_find_node_by_path("/options");
1128c2ecf20Sopenharmony_ci		mode = of_get_property(dp, mode_prop, NULL);
1138c2ecf20Sopenharmony_ci		if (!mode)
1148c2ecf20Sopenharmony_ci			mode = "9600,8,n,1,-";
1158c2ecf20Sopenharmony_ci		of_node_put(dp);
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	cflag = CREAD | HUPCL | CLOCAL;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	s = mode;
1218c2ecf20Sopenharmony_ci	baud = simple_strtoul(s, NULL, 0);
1228c2ecf20Sopenharmony_ci	s = strchr(s, ',');
1238c2ecf20Sopenharmony_ci	bits = simple_strtoul(++s, NULL, 0);
1248c2ecf20Sopenharmony_ci	s = strchr(s, ',');
1258c2ecf20Sopenharmony_ci	parity = *(++s);
1268c2ecf20Sopenharmony_ci	s = strchr(s, ',');
1278c2ecf20Sopenharmony_ci	stop = simple_strtoul(++s, NULL, 0);
1288c2ecf20Sopenharmony_ci	s = strchr(s, ',');
1298c2ecf20Sopenharmony_ci	/* XXX handshake is not handled here. */
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	switch (baud) {
1328c2ecf20Sopenharmony_ci		case 150: cflag |= B150; break;
1338c2ecf20Sopenharmony_ci		case 300: cflag |= B300; break;
1348c2ecf20Sopenharmony_ci		case 600: cflag |= B600; break;
1358c2ecf20Sopenharmony_ci		case 1200: cflag |= B1200; break;
1368c2ecf20Sopenharmony_ci		case 2400: cflag |= B2400; break;
1378c2ecf20Sopenharmony_ci		case 4800: cflag |= B4800; break;
1388c2ecf20Sopenharmony_ci		case 9600: cflag |= B9600; break;
1398c2ecf20Sopenharmony_ci		case 19200: cflag |= B19200; break;
1408c2ecf20Sopenharmony_ci		case 38400: cflag |= B38400; break;
1418c2ecf20Sopenharmony_ci		case 57600: cflag |= B57600; break;
1428c2ecf20Sopenharmony_ci		case 115200: cflag |= B115200; break;
1438c2ecf20Sopenharmony_ci		case 230400: cflag |= B230400; break;
1448c2ecf20Sopenharmony_ci		case 460800: cflag |= B460800; break;
1458c2ecf20Sopenharmony_ci		default: baud = 9600; cflag |= B9600; break;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	switch (bits) {
1498c2ecf20Sopenharmony_ci		case 5: cflag |= CS5; break;
1508c2ecf20Sopenharmony_ci		case 6: cflag |= CS6; break;
1518c2ecf20Sopenharmony_ci		case 7: cflag |= CS7; break;
1528c2ecf20Sopenharmony_ci		case 8: cflag |= CS8; break;
1538c2ecf20Sopenharmony_ci		default: cflag |= CS8; break;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	switch (parity) {
1578c2ecf20Sopenharmony_ci		case 'o': cflag |= (PARENB | PARODD); break;
1588c2ecf20Sopenharmony_ci		case 'e': cflag |= PARENB; break;
1598c2ecf20Sopenharmony_ci		case 'n': default: break;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	switch (stop) {
1638c2ecf20Sopenharmony_ci		case 2: cflag |= CSTOPB; break;
1648c2ecf20Sopenharmony_ci		case 1: default: break;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	con->cflag = cflag;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/* Sun serial MOUSE auto baud rate detection.  */
1718c2ecf20Sopenharmony_cistatic struct mouse_baud_cflag {
1728c2ecf20Sopenharmony_ci	int baud;
1738c2ecf20Sopenharmony_ci	unsigned int cflag;
1748c2ecf20Sopenharmony_ci} mouse_baud_table[] = {
1758c2ecf20Sopenharmony_ci	{ 1200, B1200 },
1768c2ecf20Sopenharmony_ci	{ 2400, B2400 },
1778c2ecf20Sopenharmony_ci	{ 4800, B4800 },
1788c2ecf20Sopenharmony_ci	{ 9600, B9600 },
1798c2ecf20Sopenharmony_ci	{ -1, ~0 },
1808c2ecf20Sopenharmony_ci	{ -1, ~0 },
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ciunsigned int suncore_mouse_baud_cflag_next(unsigned int cflag, int *new_baud)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	int i;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	for (i = 0; mouse_baud_table[i].baud != -1; i++)
1888c2ecf20Sopenharmony_ci		if (mouse_baud_table[i].cflag == (cflag & CBAUD))
1898c2ecf20Sopenharmony_ci			break;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	i += 1;
1928c2ecf20Sopenharmony_ci	if (mouse_baud_table[i].baud == -1)
1938c2ecf20Sopenharmony_ci		i = 0;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	*new_baud = mouse_baud_table[i].baud;
1968c2ecf20Sopenharmony_ci	return mouse_baud_table[i].cflag;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(suncore_mouse_baud_cflag_next);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/* Basically, when the baud rate is wrong the mouse spits out
2028c2ecf20Sopenharmony_ci * breaks to us.
2038c2ecf20Sopenharmony_ci */
2048c2ecf20Sopenharmony_ciint suncore_mouse_baud_detection(unsigned char ch, int is_break)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	static int mouse_got_break = 0;
2078c2ecf20Sopenharmony_ci	static int ctr = 0;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (is_break) {
2108c2ecf20Sopenharmony_ci		/* Let a few normal bytes go by before we jump the gun
2118c2ecf20Sopenharmony_ci		 * and say we need to try another baud rate.
2128c2ecf20Sopenharmony_ci		 */
2138c2ecf20Sopenharmony_ci		if (mouse_got_break && ctr < 8)
2148c2ecf20Sopenharmony_ci			return 1;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		/* Ok, we need to try another baud. */
2178c2ecf20Sopenharmony_ci		ctr = 0;
2188c2ecf20Sopenharmony_ci		mouse_got_break = 1;
2198c2ecf20Sopenharmony_ci		return 2;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci	if (mouse_got_break) {
2228c2ecf20Sopenharmony_ci		ctr++;
2238c2ecf20Sopenharmony_ci		if (ch == 0x87) {
2248c2ecf20Sopenharmony_ci			/* Correct baud rate determined. */
2258c2ecf20Sopenharmony_ci			mouse_got_break = 0;
2268c2ecf20Sopenharmony_ci		}
2278c2ecf20Sopenharmony_ci		return 1;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci	return 0;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(suncore_mouse_baud_detection);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic int __init suncore_init(void)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	return 0;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_cidevice_initcall(suncore_init);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci#if 0 /* ..def MODULE ; never supported as such */
2418c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eddie C. Dost, David S. Miller");
2428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sun serial common layer");
2438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2448c2ecf20Sopenharmony_ci#endif
245