18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/string.h> 38c2ecf20Sopenharmony_ci#include <linux/kernel.h> 48c2ecf20Sopenharmony_ci#include <linux/errno.h> 58c2ecf20Sopenharmony_ci#include <linux/bitops.h> 68c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 78c2ecf20Sopenharmony_ci#include <linux/adb.h> 88c2ecf20Sopenharmony_ci#include <linux/pmu.h> 98c2ecf20Sopenharmony_ci#include <linux/cuda.h> 108c2ecf20Sopenharmony_ci#include <asm/machdep.h> 118c2ecf20Sopenharmony_ci#include <asm/io.h> 128c2ecf20Sopenharmony_ci#include <asm/page.h> 138c2ecf20Sopenharmony_ci#include <asm/xmon.h> 148c2ecf20Sopenharmony_ci#include <asm/prom.h> 158c2ecf20Sopenharmony_ci#include <asm/bootx.h> 168c2ecf20Sopenharmony_ci#include <asm/errno.h> 178c2ecf20Sopenharmony_ci#include <asm/pmac_feature.h> 188c2ecf20Sopenharmony_ci#include <asm/processor.h> 198c2ecf20Sopenharmony_ci#include <asm/delay.h> 208c2ecf20Sopenharmony_ci#include <asm/btext.h> 218c2ecf20Sopenharmony_ci#include <asm/time.h> 228c2ecf20Sopenharmony_ci#include <asm/udbg.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * This implementation is "special", it can "patch" the current 268c2ecf20Sopenharmony_ci * udbg implementation and work on top of it. It must thus be 278c2ecf20Sopenharmony_ci * initialized last 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic void (*udbg_adb_old_putc)(char c); 318c2ecf20Sopenharmony_cistatic int (*udbg_adb_old_getc)(void); 328c2ecf20Sopenharmony_cistatic int (*udbg_adb_old_getc_poll)(void); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic enum { 358c2ecf20Sopenharmony_ci input_adb_none, 368c2ecf20Sopenharmony_ci input_adb_pmu, 378c2ecf20Sopenharmony_ci input_adb_cuda, 388c2ecf20Sopenharmony_ci} input_type = input_adb_none; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ciint xmon_wants_key, xmon_adb_keycode; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic inline void udbg_adb_poll(void) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB_PMU 458c2ecf20Sopenharmony_ci if (input_type == input_adb_pmu) 468c2ecf20Sopenharmony_ci pmu_poll_adb(); 478c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB_PMU */ 488c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB_CUDA 498c2ecf20Sopenharmony_ci if (input_type == input_adb_cuda) 508c2ecf20Sopenharmony_ci cuda_poll(); 518c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB_CUDA */ 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int udbg_adb_use_btext; 578c2ecf20Sopenharmony_cistatic int xmon_adb_shiftstate; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic unsigned char xmon_keytab[128] = 608c2ecf20Sopenharmony_ci "asdfhgzxcv\000bqwer" /* 0x00 - 0x0f */ 618c2ecf20Sopenharmony_ci "yt123465=97-80]o" /* 0x10 - 0x1f */ 628c2ecf20Sopenharmony_ci "u[ip\rlj'k;\\,/nm." /* 0x20 - 0x2f */ 638c2ecf20Sopenharmony_ci "\t `\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */ 648c2ecf20Sopenharmony_ci "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */ 658c2ecf20Sopenharmony_ci "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic unsigned char xmon_shift_keytab[128] = 688c2ecf20Sopenharmony_ci "ASDFHGZXCV\000BQWER" /* 0x00 - 0x0f */ 698c2ecf20Sopenharmony_ci "YT!@#$^%+(&_*)}O" /* 0x10 - 0x1f */ 708c2ecf20Sopenharmony_ci "U{IP\rLJ\"K:|<?NM>" /* 0x20 - 0x2f */ 718c2ecf20Sopenharmony_ci "\t ~\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */ 728c2ecf20Sopenharmony_ci "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */ 738c2ecf20Sopenharmony_ci "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int udbg_adb_local_getc(void) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci int k, t, on; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci xmon_wants_key = 1; 808c2ecf20Sopenharmony_ci for (;;) { 818c2ecf20Sopenharmony_ci xmon_adb_keycode = -1; 828c2ecf20Sopenharmony_ci t = 0; 838c2ecf20Sopenharmony_ci on = 0; 848c2ecf20Sopenharmony_ci k = -1; 858c2ecf20Sopenharmony_ci do { 868c2ecf20Sopenharmony_ci if (--t < 0) { 878c2ecf20Sopenharmony_ci on = 1 - on; 888c2ecf20Sopenharmony_ci btext_drawchar(on? 0xdb: 0x20); 898c2ecf20Sopenharmony_ci btext_drawchar('\b'); 908c2ecf20Sopenharmony_ci t = 200000; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci udbg_adb_poll(); 938c2ecf20Sopenharmony_ci if (udbg_adb_old_getc_poll) 948c2ecf20Sopenharmony_ci k = udbg_adb_old_getc_poll(); 958c2ecf20Sopenharmony_ci } while (k == -1 && xmon_adb_keycode == -1); 968c2ecf20Sopenharmony_ci if (on) 978c2ecf20Sopenharmony_ci btext_drawstring(" \b"); 988c2ecf20Sopenharmony_ci if (k != -1) 998c2ecf20Sopenharmony_ci return k; 1008c2ecf20Sopenharmony_ci k = xmon_adb_keycode; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* test for shift keys */ 1038c2ecf20Sopenharmony_ci if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) { 1048c2ecf20Sopenharmony_ci xmon_adb_shiftstate = (k & 0x80) == 0; 1058c2ecf20Sopenharmony_ci continue; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci if (k >= 0x80) 1088c2ecf20Sopenharmony_ci continue; /* ignore up transitions */ 1098c2ecf20Sopenharmony_ci k = (xmon_adb_shiftstate? xmon_shift_keytab: xmon_keytab)[k]; 1108c2ecf20Sopenharmony_ci if (k != 0) 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci xmon_wants_key = 0; 1148c2ecf20Sopenharmony_ci return k; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci#endif /* CONFIG_BOOTX_TEXT */ 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int udbg_adb_getc(void) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 1218c2ecf20Sopenharmony_ci if (udbg_adb_use_btext && input_type != input_adb_none) 1228c2ecf20Sopenharmony_ci return udbg_adb_local_getc(); 1238c2ecf20Sopenharmony_ci#endif 1248c2ecf20Sopenharmony_ci if (udbg_adb_old_getc) 1258c2ecf20Sopenharmony_ci return udbg_adb_old_getc(); 1268c2ecf20Sopenharmony_ci return -1; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* getc_poll() is not really used, unless you have the xmon-over modem 1308c2ecf20Sopenharmony_ci * hack that doesn't quite concern us here, thus we just poll the low level 1318c2ecf20Sopenharmony_ci * ADB driver to prevent it from timing out and call back the original poll 1328c2ecf20Sopenharmony_ci * routine. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_cistatic int udbg_adb_getc_poll(void) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci udbg_adb_poll(); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (udbg_adb_old_getc_poll) 1398c2ecf20Sopenharmony_ci return udbg_adb_old_getc_poll(); 1408c2ecf20Sopenharmony_ci return -1; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void udbg_adb_putc(char c) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 1468c2ecf20Sopenharmony_ci if (udbg_adb_use_btext) 1478c2ecf20Sopenharmony_ci btext_drawchar(c); 1488c2ecf20Sopenharmony_ci#endif 1498c2ecf20Sopenharmony_ci if (udbg_adb_old_putc) 1508c2ecf20Sopenharmony_ci return udbg_adb_old_putc(c); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_civoid __init udbg_adb_init_early(void) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 1568c2ecf20Sopenharmony_ci if (btext_find_display(1) == 0) { 1578c2ecf20Sopenharmony_ci udbg_adb_use_btext = 1; 1588c2ecf20Sopenharmony_ci udbg_putc = udbg_adb_putc; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci#endif 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciint __init udbg_adb_init(int force_btext) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct device_node *np; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* Capture existing callbacks */ 1688c2ecf20Sopenharmony_ci udbg_adb_old_putc = udbg_putc; 1698c2ecf20Sopenharmony_ci udbg_adb_old_getc = udbg_getc; 1708c2ecf20Sopenharmony_ci udbg_adb_old_getc_poll = udbg_getc_poll; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Check if our early init was already called */ 1738c2ecf20Sopenharmony_ci if (udbg_adb_old_putc == udbg_adb_putc) 1748c2ecf20Sopenharmony_ci udbg_adb_old_putc = NULL; 1758c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 1768c2ecf20Sopenharmony_ci if (udbg_adb_old_putc == btext_drawchar) 1778c2ecf20Sopenharmony_ci udbg_adb_old_putc = NULL; 1788c2ecf20Sopenharmony_ci#endif 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Set ours as output */ 1818c2ecf20Sopenharmony_ci udbg_putc = udbg_adb_putc; 1828c2ecf20Sopenharmony_ci udbg_getc = udbg_adb_getc; 1838c2ecf20Sopenharmony_ci udbg_getc_poll = udbg_adb_getc_poll; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 1868c2ecf20Sopenharmony_ci /* Check if we should use btext output */ 1878c2ecf20Sopenharmony_ci if (btext_find_display(force_btext) == 0) 1888c2ecf20Sopenharmony_ci udbg_adb_use_btext = 1; 1898c2ecf20Sopenharmony_ci#endif 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* See if there is a keyboard in the device tree with a parent 1928c2ecf20Sopenharmony_ci * of type "adb". If not, we return a failure, but we keep the 1938c2ecf20Sopenharmony_ci * bext output set for now 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci for_each_node_by_name(np, "keyboard") { 1968c2ecf20Sopenharmony_ci struct device_node *parent = of_get_parent(np); 1978c2ecf20Sopenharmony_ci int found = of_node_is_type(parent, "adb"); 1988c2ecf20Sopenharmony_ci of_node_put(parent); 1998c2ecf20Sopenharmony_ci if (found) 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci if (np == NULL) 2038c2ecf20Sopenharmony_ci return -ENODEV; 2048c2ecf20Sopenharmony_ci of_node_put(np); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB_PMU 2078c2ecf20Sopenharmony_ci if (find_via_pmu()) 2088c2ecf20Sopenharmony_ci input_type = input_adb_pmu; 2098c2ecf20Sopenharmony_ci#endif 2108c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB_CUDA 2118c2ecf20Sopenharmony_ci if (find_via_cuda()) 2128c2ecf20Sopenharmony_ci input_type = input_adb_cuda; 2138c2ecf20Sopenharmony_ci#endif 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* Same as above: nothing found, keep btext set for output */ 2168c2ecf20Sopenharmony_ci if (input_type == input_adb_none) 2178c2ecf20Sopenharmony_ci return -ENODEV; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 221