1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2012 Gary Mills
24 *
25 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
26 * Use is subject to license terms.
27 */
28
29/*
30 * isa-specific console configuration routines
31 */
32
33#include <sys/types.h>
34#include <sys/param.h>
35#include <sys/cmn_err.h>
36#include <sys/systm.h>
37#include <sys/conf.h>
38#include <sys/debug.h>
39#include <sys/ddi.h>
40#include <sys/sunddi.h>
41#include <sys/sunndi.h>
42#include <sys/esunddi.h>
43#include <sys/ddi_impldefs.h>
44#include <sys/promif.h>
45#include <sys/modctl.h>
46#include <sys/termios.h>
47#include <sys/pci.h>
48#include <sys/framebuffer.h>
49#include <sys/boot_console.h>
50#if defined(__xpv)
51#include <sys/hypervisor.h>
52#endif
53
54extern int pseudo_isa;
55
56int
57plat_use_polled_debug()
58{
59	return (0);
60}
61
62int
63plat_support_serial_kbd_and_ms()
64{
65	return (0);
66}
67
68#define	A_CNT(arr)	(sizeof (arr) / sizeof (arr[0]))
69
70#ifndef	CONS_INVALID
71#define	CONS_INVALID		-1
72#define	CONS_SCREEN_TEXT	0
73#define	CONS_TTY		1
74#define	CONS_XXX		2	/* Unused */
75#define	CONS_USBSER		3
76#define	CONS_HYPERVISOR		4
77#define	CONS_SCREEN_GRAPHICS	5
78#endif	/* CONS_INVALID */
79
80char *plat_fbpath(void);
81
82static int
83console_type(int *tnum)
84{
85	static int boot_console = CONS_INVALID;
86	static int tty_num = 0;
87
88	char *cons;
89	dev_info_t *root;
90
91	/* If we already have determined the console, just return it. */
92	if (boot_console != CONS_INVALID) {
93		if (tnum != NULL)
94			*tnum = tty_num;
95		return (boot_console);
96	}
97
98#if defined(__xpv)
99	if (!DOMAIN_IS_INITDOMAIN(xen_info) || bcons_hypervisor_redirect()) {
100		boot_console = CONS_HYPERVISOR;
101		if (tnum != NULL)
102			*tnum = tty_num;
103		return (boot_console);
104	}
105#endif /* __xpv */
106
107	/*
108	 * console is defined by "console" property, with
109	 * fallback on the old "input-device" property.
110	 * If "input-device" is not defined either, also check "output-device".
111	 */
112	boot_console = CONS_SCREEN_TEXT;	/* default is screen/kb */
113	root = ddi_root_node();
114	if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, root,
115	    DDI_PROP_DONTPASS, "console", &cons) == DDI_SUCCESS) ||
116	    (ddi_prop_lookup_string(DDI_DEV_T_ANY, root,
117	    DDI_PROP_DONTPASS, "input-device", &cons) == DDI_SUCCESS) ||
118	    (ddi_prop_lookup_string(DDI_DEV_T_ANY, root,
119	    DDI_PROP_DONTPASS, "output-device", &cons) == DDI_SUCCESS)) {
120		if (strlen(cons) == 4 && strncmp(cons, "tty", 3) == 0 &&
121		    cons[3] >= 'a' && cons[3] <= 'd') {
122			boot_console = CONS_TTY;
123			tty_num = cons[3] - 'a';
124		} else if (strcmp(cons, "usb-serial") == 0) {
125			(void) i_ddi_attach_hw_nodes("xhci");
126			(void) i_ddi_attach_hw_nodes("ehci");
127			(void) i_ddi_attach_hw_nodes("uhci");
128			(void) i_ddi_attach_hw_nodes("ohci");
129			/*
130			 * USB device enumerate asynchronously.
131			 * Wait 2 seconds for USB serial devices to attach.
132			 */
133			delay(drv_usectohz(2000000));
134			boot_console = CONS_USBSER;
135#if defined(__xpv)
136		} else if (strcmp(cons, "hypervisor") == 0) {
137			boot_console = CONS_HYPERVISOR;
138#endif /* __xpv */
139		}
140		ddi_prop_free(cons);
141	}
142
143	/*
144	 * If the console is configured to use a framebuffer but none
145	 * could be found, fallback to "ttya" since it's likely to exist
146	 * and it matches longstanding behavior on SPARC.
147	 */
148	if (boot_console == CONS_SCREEN_TEXT && plat_fbpath() == NULL) {
149		boot_console = CONS_TTY;
150		tty_num = 0;
151	}
152
153	if (tnum != NULL)
154		*tnum = tty_num;
155	return (boot_console);
156}
157
158int
159plat_stdin_is_keyboard(void)
160{
161	return (console_type(NULL) == CONS_SCREEN_TEXT);
162}
163
164int
165plat_stdout_is_framebuffer(void)
166{
167	return (console_type(NULL) == CONS_SCREEN_TEXT);
168}
169
170static char *
171plat_devpath(char *name, char *path)
172{
173	major_t major;
174	dev_info_t *dip, *pdip;
175
176	if ((major = ddi_name_to_major(name)) == (major_t)-1)
177		return (NULL);
178
179	if ((dip = devnamesp[major].dn_head) == NULL)
180		return (NULL);
181
182	pdip = ddi_get_parent(dip);
183	if (i_ddi_attach_node_hierarchy(pdip) != DDI_SUCCESS)
184		return (NULL);
185	if (ddi_initchild(pdip, dip) != DDI_SUCCESS)
186		return (NULL);
187
188	(void) ddi_pathname(dip, path);
189
190	return (path);
191}
192
193/*
194 * Return generic path to keyboard device from the alias.
195 */
196char *
197plat_kbdpath(void)
198{
199	static char kbpath[MAXPATHLEN];
200
201	/*
202	 * Hardcode to isa keyboard path
203	 * XXX make it settable via bootprop?
204	 */
205	if (pseudo_isa)
206		return ("/isa/i8042@1,60/keyboard@0");
207
208	if (plat_devpath("kb8042", kbpath) == NULL)
209		return (NULL);
210
211	return (kbpath);
212}
213
214/*
215 * NOTE: this function is duplicated here and in gfx_private/vgatext while
216 *       we work on a set of commitable interfaces to sunpci.c.
217 *
218 * Use the class code to determine if the device is a PCI-to-PCI bridge.
219 * Returns:  B_TRUE  if the device is a bridge.
220 *           B_FALSE if the device is not a bridge or the property cannot be
221 *		     retrieved.
222 */
223static boolean_t
224is_pci_bridge(dev_info_t *dip)
225{
226	uint32_t class_code;
227
228	class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
229	    DDI_PROP_DONTPASS, "class-code", 0xffffffff);
230
231	if (class_code == 0xffffffff || class_code == DDI_PROP_NOT_FOUND)
232		return (B_FALSE);
233
234	class_code &= 0x00ffff00;
235	if (class_code == ((PCI_CLASS_BRIDGE << 16) | (PCI_BRIDGE_PCI << 8)))
236		return (B_TRUE);
237
238	return (B_FALSE);
239}
240
241/*
242 * Type for the parameter of find_fb_dev()
243 */
244struct find_fb_dev_param
245{
246	dev_info_t *found_dip;	/* dip found for VGA console */
247	int vga_enable;		/* check PCI_BCNF_BCNTRL_VGA_ENABLE or not */
248};
249
250/*
251 * The VGA device could be under a subtractive PCI bridge on some systems.
252 * Though the PCI_BCNF_BCNTRL_VGA_ENABLE bit is not set on such subtractive
253 * PCI bridge, the subtractive PCI bridge can forward VGA access if no other
254 * agent claims the access.
255 * The vga_enable element in param acts as a flag, if not set, ignore the
256 * checking for the PCI_BCNF_BCNTRL_VGA_ENABLE bit of the PCI bridge during
257 * the search.
258 */
259static int
260find_fb_dev(dev_info_t *dip, void *param)
261{
262	struct find_fb_dev_param *p = param;
263	char *dev_type;
264	dev_info_t *pdip;
265	char *parent_type;
266
267	if (dip == ddi_root_node())
268		return (DDI_WALK_CONTINUE);
269
270	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
271	    "device_type", &dev_type) != DDI_SUCCESS)
272		return (DDI_WALK_PRUNECHILD);
273
274	if ((strcmp(dev_type, "isa") == 0) || (strcmp(dev_type, "eisa") == 0)) {
275		ddi_prop_free(dev_type);
276		return (DDI_WALK_CONTINUE);
277	}
278
279	if ((strcmp(dev_type, "pci") == 0) ||
280	    (strcmp(dev_type, "pciex") == 0)) {
281		ddi_acc_handle_t pci_conf;
282		uint16_t data16;
283		char *nodename;
284
285		ddi_prop_free(dev_type);
286
287		if (!p->vga_enable)
288			return (DDI_WALK_CONTINUE);
289
290		nodename = ddi_node_name(dip);
291
292		/*
293		 * If the node is not a PCI-to-PCI bridge, continue traversing
294		 * (it could be the root node), otherwise, check for the
295		 * VGAEnable bit to be set in the Bridge Control Register.
296		 */
297		if (strcmp(nodename, "pci") == 0) {
298			if (is_pci_bridge(dip) == B_FALSE)
299				return (DDI_WALK_CONTINUE);
300		}
301
302		if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
303			return (DDI_WALK_PRUNECHILD);
304
305		if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS)
306			return (DDI_WALK_PRUNECHILD);
307
308		data16 = pci_config_get16(pci_conf, PCI_BCNF_BCNTRL);
309		pci_config_teardown(&pci_conf);
310
311		if (data16 & PCI_BCNF_BCNTRL_VGA_ENABLE)
312			return (DDI_WALK_CONTINUE);
313
314		return (DDI_WALK_PRUNECHILD);
315	}
316
317	if (strcmp(dev_type, "display") != 0) {
318		ddi_prop_free(dev_type);
319		return (DDI_WALK_CONTINUE);
320	}
321
322	ddi_prop_free(dev_type);
323
324	if ((pdip = ddi_get_parent(dip)) == NULL)
325		return (DDI_WALK_PRUNECHILD);
326
327	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
328	    "device_type", &parent_type) != DDI_SUCCESS)
329		return (DDI_WALK_PRUNECHILD);
330
331	if ((strcmp(parent_type, "isa") == 0) ||
332	    (strcmp(parent_type, "eisa") == 0)) {
333		p->found_dip = dip;
334		ddi_prop_free(parent_type);
335		return (DDI_WALK_TERMINATE);
336	}
337
338	if ((strcmp(parent_type, "pci") == 0) ||
339	    (strcmp(parent_type, "pciex") == 0)) {
340		ddi_acc_handle_t pci_conf;
341		uint16_t data16;
342
343		ddi_prop_free(parent_type);
344
345		if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
346			return (DDI_WALK_PRUNECHILD);
347
348		if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS)
349			return (DDI_WALK_PRUNECHILD);
350
351		data16 = pci_config_get16(pci_conf, PCI_CONF_COMM);
352		pci_config_teardown(&pci_conf);
353
354		if (!(data16 & PCI_COMM_IO))
355			return (DDI_WALK_PRUNECHILD);
356
357		p->found_dip = dip;
358		return (DDI_WALK_TERMINATE);
359	}
360
361	ddi_prop_free(parent_type);
362	return (DDI_WALK_PRUNECHILD);
363}
364
365/*
366 * The first round search is to find:
367 * 1) a VGA device.
368 * 2) a PCI VGA compatible device whose IO space is enabled
369 *    and the VGA Enable bit of any PCI-PCI bridge above it is set.
370 * If the first round search succeeds, prune the second round search.
371 *
372 * The second round seach does not check the VGA Enable bit.
373 *
374 * Return the device path as the console fb path.
375 */
376char *
377plat_fbpath(void)
378{
379	struct find_fb_dev_param param;
380	static char *fbpath = NULL;
381	static char fbpath_buf[MAXPATHLEN];
382
383	/* first round search */
384	param.found_dip = NULL;
385	param.vga_enable = 1;
386	ddi_walk_devs(ddi_root_node(), find_fb_dev, &param);
387
388	if (param.found_dip != NULL) {
389		(void) ddi_pathname(param.found_dip, fbpath_buf);
390		fbpath = fbpath_buf;
391		return (fbpath);
392	}
393
394	/*
395	 * second round search, do not check the
396	 * PCI_BCNF_BCNTRL_VGA_ENABLE bit
397	 */
398	param.found_dip = NULL;
399	param.vga_enable = 0;
400	ddi_walk_devs(ddi_root_node(), find_fb_dev, &param);
401
402	if (param.found_dip == NULL)
403		return (NULL);
404
405	(void) ddi_pathname(param.found_dip, fbpath_buf);
406	fbpath = fbpath_buf;
407	return (fbpath);
408}
409
410char *
411plat_mousepath(void)
412{
413	static char mpath[MAXPATHLEN];
414
415	/*
416	 * Hardcode to isa mouse path
417	 * XXX make it settable via bootprop?
418	 */
419	if (pseudo_isa)
420		return ("/isa/i8042@1,60/mouse@1");
421
422	if (plat_devpath("mouse8042", mpath) == NULL)
423		return (NULL);
424
425	return (mpath);
426}
427
428/* return path of first usb serial device */
429static char *
430plat_usbser_path(void)
431{
432	extern dev_info_t *usbser_first_device(void);
433
434	dev_info_t *us_dip;
435	static char *us_path = NULL;
436
437	if (us_path)
438		return (us_path);
439
440	us_dip = usbser_first_device();
441	if (us_dip == NULL)
442		return (NULL);
443
444	us_path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
445	(void) ddi_pathname(us_dip, us_path);
446	ndi_rele_devi(us_dip);	/* held from usbser_first_device */
447	return (us_path);
448}
449
450static char *
451plat_ttypath(int inum)
452{
453	static char *defaultpath[] = {
454	    "/isa/asy@1,3f8:a",
455	    "/isa/asy@1,2f8:b",
456	    "/isa/asy@1,3e8:c",
457	    "/isa/asy@1,2e8:d"
458	};
459	static char path[MAXPATHLEN];
460	char *bp;
461	major_t major;
462	dev_info_t *dip;
463
464	if (pseudo_isa)
465		return (defaultpath[inum]);
466
467	if ((major = ddi_name_to_major("asy")) == (major_t)-1)
468		return (NULL);
469
470	if ((dip = devnamesp[major].dn_head) == NULL)
471		return (NULL);
472
473	for (; dip != NULL; dip = ddi_get_next(dip)) {
474		if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
475			return (NULL);
476
477		if (DEVI(dip)->devi_minor->ddm_name[0] == ('a' + (char)inum))
478			break;
479	}
480	if (dip == NULL)
481		return (NULL);
482
483	(void) ddi_pathname(dip, path);
484	bp = path + strlen(path);
485	(void) snprintf(bp, 3, ":%s", DEVI(dip)->devi_minor->ddm_name);
486
487	return (path);
488}
489
490/*
491 * Another possible enhancement could be to use properties
492 * for the port mapping rather than simply hard-code them.
493 */
494char *
495plat_stdinpath(void)
496{
497	int tty_num = 0;
498
499	switch (console_type(&tty_num)) {
500#if defined(__xpv)
501	case CONS_HYPERVISOR:
502		return ("/xpvd/xencons@0");
503#endif /* __xpv */
504	case CONS_TTY:
505		return (plat_ttypath(tty_num));
506	case CONS_USBSER:
507		return (plat_usbser_path());
508	case CONS_SCREEN_TEXT:
509	default:
510		break;
511	};
512	return (plat_kbdpath());
513}
514
515char *
516plat_stdoutpath(void)
517{
518	int tty_num = 0;
519
520	switch (console_type(&tty_num)) {
521#if defined(__xpv)
522	case CONS_HYPERVISOR:
523		return ("/xpvd/xencons@0");
524#endif /* __xpv */
525	case CONS_TTY:
526		return (plat_ttypath(tty_num));
527	case CONS_USBSER:
528		return (plat_usbser_path());
529	case CONS_SCREEN_TEXT:
530	default:
531		break;
532	};
533	return (plat_fbpath());
534}
535
536char *
537plat_diagpath(void)
538{
539	dev_info_t *root;
540	char *diag;
541	int tty_num = -1;
542
543	root = ddi_root_node();
544
545	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, root, DDI_PROP_DONTPASS,
546	    "diag-device", &diag) == DDI_SUCCESS) {
547		if (strlen(diag) == 4 && strncmp(diag, "tty", 3) == 0 &&
548		    diag[3] >= 'a' && diag[3] <= 'd') {
549			tty_num = diag[3] - 'a';
550		}
551		ddi_prop_free(diag);
552	}
553
554	if (tty_num != -1)
555		return (plat_ttypath(tty_num));
556	return (NULL);
557}
558
559/*
560 * If VIS_PIXEL mode will be implemented on x86, these following
561 * functions should be re-considered. Now these functions are
562 * unused on x86.
563 */
564void
565plat_tem_get_colors(uint8_t *fg, uint8_t *bg)
566{
567	*fg = fb_info.fg_color;
568	*bg = fb_info.bg_color;
569}
570
571void
572plat_tem_get_inverses(int *inverse, int *inverse_screen)
573{
574	*inverse = fb_info.inverse == B_TRUE? 1 : 0;
575	*inverse_screen = fb_info.inverse_screen == B_TRUE? 1 : 0;
576}
577
578void
579plat_tem_get_prom_font_size(int *charheight, int *windowtop)
580{
581	*charheight = fb_info.font_height;
582	*windowtop = fb_info.terminal_origin.y;
583}
584
585/*ARGSUSED*/
586void
587plat_tem_get_prom_size(size_t *height, size_t *width)
588{
589	*height = fb_info.terminal.y;
590	*width = fb_info.terminal.x;
591}
592
593/* this gets called once at boot time and only in case of VIS_PIXEL */
594void
595plat_tem_hide_prom_cursor(void)
596{
597	if (boot_console_type(NULL) == CONS_FRAMEBUFFER)
598		boot_fb_cursor(B_FALSE);
599}
600
601/*ARGSUSED*/
602void
603plat_tem_get_prom_pos(uint32_t *row, uint32_t *col)
604{
605	*row = fb_info.cursor.pos.y;
606	*col = fb_info.cursor.pos.x;
607}
608