162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/string.h>
362306a36Sopenharmony_ci#include <linux/kernel.h>
462306a36Sopenharmony_ci#include <linux/errno.h>
562306a36Sopenharmony_ci#include <linux/bitops.h>
662306a36Sopenharmony_ci#include <linux/ptrace.h>
762306a36Sopenharmony_ci#include <linux/adb.h>
862306a36Sopenharmony_ci#include <linux/pmu.h>
962306a36Sopenharmony_ci#include <linux/cuda.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <asm/machdep.h>
1262306a36Sopenharmony_ci#include <asm/io.h>
1362306a36Sopenharmony_ci#include <asm/page.h>
1462306a36Sopenharmony_ci#include <asm/xmon.h>
1562306a36Sopenharmony_ci#include <asm/bootx.h>
1662306a36Sopenharmony_ci#include <asm/errno.h>
1762306a36Sopenharmony_ci#include <asm/pmac_feature.h>
1862306a36Sopenharmony_ci#include <asm/processor.h>
1962306a36Sopenharmony_ci#include <asm/delay.h>
2062306a36Sopenharmony_ci#include <asm/btext.h>
2162306a36Sopenharmony_ci#include <asm/time.h>
2262306a36Sopenharmony_ci#include <asm/udbg.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci * This implementation is "special", it can "patch" the current
2662306a36Sopenharmony_ci * udbg implementation and work on top of it. It must thus be
2762306a36Sopenharmony_ci * initialized last
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic void (*udbg_adb_old_putc)(char c);
3162306a36Sopenharmony_cistatic int (*udbg_adb_old_getc)(void);
3262306a36Sopenharmony_cistatic int (*udbg_adb_old_getc_poll)(void);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic enum {
3562306a36Sopenharmony_ci	input_adb_none,
3662306a36Sopenharmony_ci	input_adb_pmu,
3762306a36Sopenharmony_ci	input_adb_cuda,
3862306a36Sopenharmony_ci} input_type = input_adb_none;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ciint xmon_wants_key, xmon_adb_keycode;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic inline void udbg_adb_poll(void)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU
4562306a36Sopenharmony_ci	if (input_type == input_adb_pmu)
4662306a36Sopenharmony_ci		pmu_poll_adb();
4762306a36Sopenharmony_ci#endif /* CONFIG_ADB_PMU */
4862306a36Sopenharmony_ci#ifdef CONFIG_ADB_CUDA
4962306a36Sopenharmony_ci	if (input_type == input_adb_cuda)
5062306a36Sopenharmony_ci		cuda_poll();
5162306a36Sopenharmony_ci#endif /* CONFIG_ADB_CUDA */
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int udbg_adb_use_btext;
5762306a36Sopenharmony_cistatic int xmon_adb_shiftstate;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic unsigned char xmon_keytab[128] =
6062306a36Sopenharmony_ci	"asdfhgzxcv\000bqwer"				/* 0x00 - 0x0f */
6162306a36Sopenharmony_ci	"yt123465=97-80]o"				/* 0x10 - 0x1f */
6262306a36Sopenharmony_ci	"u[ip\rlj'k;\\,/nm."				/* 0x20 - 0x2f */
6362306a36Sopenharmony_ci	"\t `\177\0\033\0\0\0\0\0\0\0\0\0\0"		/* 0x30 - 0x3f */
6462306a36Sopenharmony_ci	"\0.\0*\0+\0\0\0\0\0/\r\0-\0"			/* 0x40 - 0x4f */
6562306a36Sopenharmony_ci	"\0\0000123456789\0\0\0";			/* 0x50 - 0x5f */
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic unsigned char xmon_shift_keytab[128] =
6862306a36Sopenharmony_ci	"ASDFHGZXCV\000BQWER"				/* 0x00 - 0x0f */
6962306a36Sopenharmony_ci	"YT!@#$^%+(&_*)}O"				/* 0x10 - 0x1f */
7062306a36Sopenharmony_ci	"U{IP\rLJ\"K:|<?NM>"				/* 0x20 - 0x2f */
7162306a36Sopenharmony_ci	"\t ~\177\0\033\0\0\0\0\0\0\0\0\0\0"		/* 0x30 - 0x3f */
7262306a36Sopenharmony_ci	"\0.\0*\0+\0\0\0\0\0/\r\0-\0"			/* 0x40 - 0x4f */
7362306a36Sopenharmony_ci	"\0\0000123456789\0\0\0";			/* 0x50 - 0x5f */
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int udbg_adb_local_getc(void)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	int k, t, on;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	xmon_wants_key = 1;
8062306a36Sopenharmony_ci	for (;;) {
8162306a36Sopenharmony_ci		xmon_adb_keycode = -1;
8262306a36Sopenharmony_ci		t = 0;
8362306a36Sopenharmony_ci		on = 0;
8462306a36Sopenharmony_ci		k = -1;
8562306a36Sopenharmony_ci		do {
8662306a36Sopenharmony_ci			if (--t < 0) {
8762306a36Sopenharmony_ci				on = 1 - on;
8862306a36Sopenharmony_ci				btext_drawchar(on? 0xdb: 0x20);
8962306a36Sopenharmony_ci				btext_drawchar('\b');
9062306a36Sopenharmony_ci				t = 200000;
9162306a36Sopenharmony_ci			}
9262306a36Sopenharmony_ci			udbg_adb_poll();
9362306a36Sopenharmony_ci			if (udbg_adb_old_getc_poll)
9462306a36Sopenharmony_ci				k = udbg_adb_old_getc_poll();
9562306a36Sopenharmony_ci		} while (k == -1 && xmon_adb_keycode == -1);
9662306a36Sopenharmony_ci		if (on)
9762306a36Sopenharmony_ci			btext_drawstring(" \b");
9862306a36Sopenharmony_ci		if (k != -1)
9962306a36Sopenharmony_ci			return k;
10062306a36Sopenharmony_ci		k = xmon_adb_keycode;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		/* test for shift keys */
10362306a36Sopenharmony_ci		if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) {
10462306a36Sopenharmony_ci			xmon_adb_shiftstate = (k & 0x80) == 0;
10562306a36Sopenharmony_ci			continue;
10662306a36Sopenharmony_ci		}
10762306a36Sopenharmony_ci		if (k >= 0x80)
10862306a36Sopenharmony_ci			continue;	/* ignore up transitions */
10962306a36Sopenharmony_ci		k = (xmon_adb_shiftstate? xmon_shift_keytab: xmon_keytab)[k];
11062306a36Sopenharmony_ci		if (k != 0)
11162306a36Sopenharmony_ci			break;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci	xmon_wants_key = 0;
11462306a36Sopenharmony_ci	return k;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci#endif /* CONFIG_BOOTX_TEXT */
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int udbg_adb_getc(void)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT
12162306a36Sopenharmony_ci	if (udbg_adb_use_btext && input_type != input_adb_none)
12262306a36Sopenharmony_ci		return udbg_adb_local_getc();
12362306a36Sopenharmony_ci#endif
12462306a36Sopenharmony_ci	if (udbg_adb_old_getc)
12562306a36Sopenharmony_ci		return udbg_adb_old_getc();
12662306a36Sopenharmony_ci	return -1;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/* getc_poll() is not really used, unless you have the xmon-over modem
13062306a36Sopenharmony_ci * hack that doesn't quite concern us here, thus we just poll the low level
13162306a36Sopenharmony_ci * ADB driver to prevent it from timing out and call back the original poll
13262306a36Sopenharmony_ci * routine.
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_cistatic int udbg_adb_getc_poll(void)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	udbg_adb_poll();
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (udbg_adb_old_getc_poll)
13962306a36Sopenharmony_ci		return udbg_adb_old_getc_poll();
14062306a36Sopenharmony_ci	return -1;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void udbg_adb_putc(char c)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT
14662306a36Sopenharmony_ci	if (udbg_adb_use_btext)
14762306a36Sopenharmony_ci		btext_drawchar(c);
14862306a36Sopenharmony_ci#endif
14962306a36Sopenharmony_ci	if (udbg_adb_old_putc)
15062306a36Sopenharmony_ci		return udbg_adb_old_putc(c);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_civoid __init udbg_adb_init_early(void)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT
15662306a36Sopenharmony_ci	if (btext_find_display(1) == 0) {
15762306a36Sopenharmony_ci		udbg_adb_use_btext = 1;
15862306a36Sopenharmony_ci		udbg_putc = udbg_adb_putc;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci#endif
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ciint __init udbg_adb_init(int force_btext)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct device_node *np;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* Capture existing callbacks */
16862306a36Sopenharmony_ci	udbg_adb_old_putc = udbg_putc;
16962306a36Sopenharmony_ci	udbg_adb_old_getc = udbg_getc;
17062306a36Sopenharmony_ci	udbg_adb_old_getc_poll = udbg_getc_poll;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Check if our early init was already called */
17362306a36Sopenharmony_ci	if (udbg_adb_old_putc == udbg_adb_putc)
17462306a36Sopenharmony_ci		udbg_adb_old_putc = NULL;
17562306a36Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT
17662306a36Sopenharmony_ci	if (udbg_adb_old_putc == btext_drawchar)
17762306a36Sopenharmony_ci		udbg_adb_old_putc = NULL;
17862306a36Sopenharmony_ci#endif
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* Set ours as output */
18162306a36Sopenharmony_ci	udbg_putc = udbg_adb_putc;
18262306a36Sopenharmony_ci	udbg_getc = udbg_adb_getc;
18362306a36Sopenharmony_ci	udbg_getc_poll = udbg_adb_getc_poll;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT
18662306a36Sopenharmony_ci	/* Check if we should use btext output */
18762306a36Sopenharmony_ci	if (btext_find_display(force_btext) == 0)
18862306a36Sopenharmony_ci		udbg_adb_use_btext = 1;
18962306a36Sopenharmony_ci#endif
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* See if there is a keyboard in the device tree with a parent
19262306a36Sopenharmony_ci	 * of type "adb". If not, we return a failure, but we keep the
19362306a36Sopenharmony_ci	 * bext output set for now
19462306a36Sopenharmony_ci	 */
19562306a36Sopenharmony_ci	for_each_node_by_name(np, "keyboard") {
19662306a36Sopenharmony_ci		struct device_node *parent = of_get_parent(np);
19762306a36Sopenharmony_ci		int found = of_node_is_type(parent, "adb");
19862306a36Sopenharmony_ci		of_node_put(parent);
19962306a36Sopenharmony_ci		if (found)
20062306a36Sopenharmony_ci			break;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	if (np == NULL)
20362306a36Sopenharmony_ci		return -ENODEV;
20462306a36Sopenharmony_ci	of_node_put(np);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU
20762306a36Sopenharmony_ci	if (find_via_pmu())
20862306a36Sopenharmony_ci		input_type = input_adb_pmu;
20962306a36Sopenharmony_ci#endif
21062306a36Sopenharmony_ci#ifdef CONFIG_ADB_CUDA
21162306a36Sopenharmony_ci	if (find_via_cuda())
21262306a36Sopenharmony_ci		input_type = input_adb_cuda;
21362306a36Sopenharmony_ci#endif
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Same as above: nothing found, keep btext set for output */
21662306a36Sopenharmony_ci	if (input_type == input_adb_none)
21762306a36Sopenharmony_ci		return -ENODEV;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return 0;
22062306a36Sopenharmony_ci}
221