mirror of
				https://gitlab.com/qemu-project/qemu-palcode.git
				synced 2024-02-13 08:32:59 +08:00 
			
		
		
		
	Add ps2 keyboard initialization.
Which also requires that we properly initialize the i8259 (ISA) interrupt controller.
This commit is contained in:
		
							
								
								
									
										3
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Makefile
									
									
									
									
									
								
							| @ -12,7 +12,8 @@ CPPFLAGS = -DSYSTEM_H='"sys-$(SYSTEM).h"' | ||||
|  | ||||
| CFLAGS += -mcpu=ev67 | ||||
|  | ||||
| OBJS = pal.o sys-$(SYSTEM).o init.o crb.o uart.o console.o console-low.o memset.o printf.o | ||||
| OBJS = pal.o sys-$(SYSTEM).o init.o crb.o uart.o console.o console-low.o \ | ||||
| 	memset.o printf.o util.o ps2port.o | ||||
|  | ||||
| all: palcode-$(SYSTEM) | ||||
|  | ||||
|  | ||||
							
								
								
									
										27
									
								
								init.c
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								init.c
									
									
									
									
									
								
							| @ -22,6 +22,7 @@ | ||||
| #include <stddef.h> | ||||
| #include "hwrpb.h" | ||||
| #include "osf.h" | ||||
| #include "ioport.h" | ||||
| #include "uart.h" | ||||
| #include "protos.h" | ||||
| #include SYSTEM_H | ||||
| @ -230,6 +231,30 @@ init_pcb (void) | ||||
|   pcb.flags = 1; /* FEN */ | ||||
| } | ||||
|  | ||||
| static void | ||||
| init_i8259 (void) | ||||
| { | ||||
|   /* Initialize the slave PIC.  */ | ||||
|   outb(0x11, PORT_PIC2_CMD);	/* ICW1: edge trigger, cascade, ICW4 req */ | ||||
|   outb(0x08, PORT_PIC2_DATA);	/* ICW2: irq offset = 8 */ | ||||
|   outb(0x02, PORT_PIC2_DATA);	/* ICW3: slave ID 2 */ | ||||
|   outb(0x01, PORT_PIC2_DATA);	/* ICW4: not special nested, normal eoi */ | ||||
|  | ||||
|   /* Initialize the master PIC.  */ | ||||
|   outb(0x11, PORT_PIC1_CMD);	/* ICW1 */ | ||||
|   outb(0x00, PORT_PIC1_DATA);	/* ICW2: irq offset = 0 */ | ||||
|   outb(0x04, PORT_PIC1_DATA);	/* ICW3: slave control INTC2 */ | ||||
|   outb(0x01, PORT_PIC1_DATA);	/* ICW4 */ | ||||
|  | ||||
|   /* Disable all interrupts.  */ | ||||
|   outb(0xff, PORT_PIC2_DATA); | ||||
|   outb(0xff, PORT_PIC1_DATA); | ||||
|  | ||||
|   /* Non-specific EOI, clearing anything the might be pending.  */ | ||||
|   outb(0x20, PORT_PIC2_CMD); | ||||
|   outb(0x20, PORT_PIC1_CMD); | ||||
| } | ||||
|  | ||||
| void | ||||
| do_start(unsigned long memsize, void (*kernel_entry)(void), long cpus) | ||||
| { | ||||
| @ -238,7 +263,9 @@ do_start(unsigned long memsize, void (*kernel_entry)(void), long cpus) | ||||
|   init_page_table(); | ||||
|   init_hwrpb(memsize); | ||||
|   init_pcb(); | ||||
|   init_i8259(); | ||||
|   uart_init(); | ||||
|   ps2port_setup(); | ||||
|  | ||||
|   { | ||||
|     register int variant __asm__("$16") = 2;	/* OSF/1 PALcode */ | ||||
|  | ||||
							
								
								
									
										82
									
								
								ioport.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								ioport.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | ||||
| // Definitions for X86 IO port access. | ||||
| // | ||||
| // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net> | ||||
| // | ||||
| // This file may be distributed under the terms of the GNU LGPLv3 license. | ||||
| // | ||||
| // This file copied (somewhat) intact from SeaBIOS. | ||||
|  | ||||
| #ifndef IOPORT_H | ||||
| #define IOPORT_H | ||||
|  | ||||
| #define PORT_DMA_ADDR_2        0x0004 | ||||
| #define PORT_DMA_CNT_2         0x0005 | ||||
| #define PORT_DMA1_MASK_REG     0x000a | ||||
| #define PORT_DMA1_MODE_REG     0x000b | ||||
| #define PORT_DMA1_CLEAR_FF_REG 0x000c | ||||
| #define PORT_DMA1_MASTER_CLEAR 0x000d | ||||
| #define PORT_PIC1_CMD          0x0020 | ||||
| #define PORT_PIC1_DATA         0x0021 | ||||
| #define PORT_PIT_COUNTER0      0x0040 | ||||
| #define PORT_PIT_COUNTER1      0x0041 | ||||
| #define PORT_PIT_COUNTER2      0x0042 | ||||
| #define PORT_PIT_MODE          0x0043 | ||||
| #define PORT_PS2_DATA          0x0060 | ||||
| #define PORT_PS2_CTRLB         0x0061 | ||||
| #define PORT_PS2_STATUS        0x0064 | ||||
| #define PORT_CMOS_INDEX        0x0070 | ||||
| #define PORT_CMOS_DATA         0x0071 | ||||
| #define PORT_DIAG              0x0080 | ||||
| #define PORT_DMA_PAGE_2        0x0081 | ||||
| #define PORT_A20               0x0092 | ||||
| #define PORT_PIC2_CMD          0x00a0 | ||||
| #define PORT_PIC2_DATA         0x00a1 | ||||
| #define PORT_SMI_CMD           0x00b2 | ||||
| #define PORT_SMI_STATUS        0x00b3 | ||||
| #define PORT_DMA2_MASK_REG     0x00d4 | ||||
| #define PORT_DMA2_MODE_REG     0x00d6 | ||||
| #define PORT_DMA2_MASTER_CLEAR 0x00da | ||||
| #define PORT_MATH_CLEAR        0x00f0 | ||||
| #define PORT_ATA2_CMD_BASE     0x0170 | ||||
| #define PORT_ATA1_CMD_BASE     0x01f0 | ||||
| #define PORT_LPT2              0x0278 | ||||
| #define PORT_SERIAL4           0x02e8 | ||||
| #define PORT_SERIAL2           0x02f8 | ||||
| #define PORT_ATA2_CTRL_BASE    0x0374 | ||||
| #define PORT_LPT1              0x0378 | ||||
| #define PORT_SERIAL3           0x03e8 | ||||
| #define PORT_ATA1_CTRL_BASE    0x03f4 | ||||
| #define PORT_FD_BASE           0x03f0 | ||||
| #define PORT_FD_DOR            0x03f2 | ||||
| #define PORT_FD_STATUS         0x03f4 | ||||
| #define PORT_FD_DATA           0x03f5 | ||||
| #define PORT_HD_DATA           0x03f6 | ||||
| #define PORT_FD_DIR            0x03f7 | ||||
| #define PORT_SERIAL1           0x03f8 | ||||
| #define PORT_PCI_CMD           0x0cf8 | ||||
| #define PORT_PCI_REBOOT        0x0cf9 | ||||
| #define PORT_PCI_DATA          0x0cfc | ||||
| #define PORT_BIOS_DEBUG        0x0402 | ||||
| #define PORT_QEMU_CFG_CTL      0x0510 | ||||
| #define PORT_QEMU_CFG_DATA     0x0511 | ||||
| #define PORT_ACPI_PM_BASE      0xb000 | ||||
| #define PORT_SMB_BASE          0xb100 | ||||
| #define PORT_BIOS_APM          0x8900 | ||||
|  | ||||
| // Serial port offsets | ||||
| #define SEROFF_DATA    0 | ||||
| #define SEROFF_DLL     0 | ||||
| #define SEROFF_IER     1 | ||||
| #define SEROFF_DLH     1 | ||||
| #define SEROFF_IIR     2 | ||||
| #define SEROFF_LCR     3 | ||||
| #define SEROFF_LSR     5 | ||||
| #define SEROFF_MSR     6 | ||||
|  | ||||
| // PORT_A20 bitdefs | ||||
| #define A20_ENABLE_BIT 0x02 | ||||
|  | ||||
| // PORT_CMOS_INDEX nmi disable bit | ||||
| #define NMI_DISABLE_BIT 0x80 | ||||
|  | ||||
| #endif // ioport.h | ||||
							
								
								
									
										21
									
								
								protos.h
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								protos.h
									
									
									
									
									
								
							| @ -21,6 +21,11 @@ | ||||
| #ifndef PROTOS_H | ||||
| #define PROTOS_H 1 | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include <stddef.h> | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Call_Pal functions. | ||||
|  */ | ||||
| @ -167,4 +172,20 @@ extern unsigned long crb_fixup(unsigned long vptptr, unsigned long hwrpb); | ||||
| extern void do_console(void); | ||||
| extern void entInt(void); | ||||
|  | ||||
| /* | ||||
|  * Utils | ||||
|  */ | ||||
|  | ||||
| extern void ndelay(unsigned long nsec); | ||||
|  | ||||
| static inline void udelay(unsigned long msec) | ||||
| { | ||||
|   ndelay(msec * 1000); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Initialization | ||||
|  */ | ||||
| extern void ps2port_setup(void); | ||||
|  | ||||
| #endif /* PROTOS_H */ | ||||
|  | ||||
							
								
								
									
										507
									
								
								ps2port.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										507
									
								
								ps2port.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,507 @@ | ||||
| // Support for handling the PS/2 mouse/keyboard ports. | ||||
| // | ||||
| // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net> | ||||
| // Several ideas taken from code Copyright (c) 1999-2004 Vojtech Pavlik | ||||
| // | ||||
| // This file may be distributed under the terms of the GNU LGPLv3 license. | ||||
| // | ||||
| // This file is copied (mostly) intact from SeaBIOS. | ||||
|  | ||||
| #include "protos.h" | ||||
| #include "ioport.h" | ||||
| #include "ps2port.h" | ||||
|  | ||||
| typedef uint64_t u64; | ||||
|  | ||||
| #define dprintf(lvl, fmt, ...) | ||||
| #define warn_timeout() | ||||
|  | ||||
| static inline u64 | ||||
| calc_future_tsc(int timeout) | ||||
| { | ||||
|   return get_wall_time() + timeout; | ||||
| } | ||||
|  | ||||
| static inline bool | ||||
| check_tsc(u64 end) | ||||
| { | ||||
|   return get_wall_time() > end; | ||||
| } | ||||
|  | ||||
| static inline void | ||||
| yield(void) | ||||
| { | ||||
|   udelay(1); | ||||
| } | ||||
|  | ||||
| static struct { | ||||
|   u8 ps2ctr; | ||||
| } ebda; | ||||
|  | ||||
| #define GET_EBDA(VAR)		ebda.VAR | ||||
| #define SET_EBDA(VAR, VAL)	(ebda.VAR = (VAL)) | ||||
|  | ||||
| #define ASSERT32FLAT() | ||||
| #define CONFIG_PS2PORT 1 | ||||
|  | ||||
| #define enable_hwirq(level, func) | ||||
| #define run_thread(func, val)	func(val) | ||||
|  | ||||
| /**************************************************************** | ||||
|  * Low level i8042 commands. | ||||
|  ****************************************************************/ | ||||
|  | ||||
| // Timeout value. | ||||
| #define I8042_CTL_TIMEOUT       10000 | ||||
|  | ||||
| #define I8042_BUFFER_SIZE       16 | ||||
|  | ||||
| static int | ||||
| i8042_wait_read(void) | ||||
| { | ||||
|     dprintf(7, "i8042_wait_read\n"); | ||||
|     int i; | ||||
|     for (i=0; i<I8042_CTL_TIMEOUT; i++) { | ||||
|         u8 status = inb(PORT_PS2_STATUS); | ||||
|         if (status & I8042_STR_OBF) | ||||
|             return 0; | ||||
|         udelay(50); | ||||
|     } | ||||
|     warn_timeout(); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int | ||||
| i8042_wait_write(void) | ||||
| { | ||||
|     dprintf(7, "i8042_wait_write\n"); | ||||
|     int i; | ||||
|     for (i=0; i<I8042_CTL_TIMEOUT; i++) { | ||||
|         u8 status = inb(PORT_PS2_STATUS); | ||||
|         if (! (status & I8042_STR_IBF)) | ||||
|             return 0; | ||||
|         udelay(50); | ||||
|     } | ||||
|     warn_timeout(); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int | ||||
| i8042_flush(void) | ||||
| { | ||||
|     dprintf(7, "i8042_flush\n"); | ||||
|     int i; | ||||
|     for (i=0; i<I8042_BUFFER_SIZE; i++) { | ||||
|         u8 status = inb(PORT_PS2_STATUS); | ||||
|         if (! (status & I8042_STR_OBF)) | ||||
|             return 0; | ||||
|         udelay(50); | ||||
| 	inb(PORT_PS2_DATA); | ||||
|     } | ||||
|  | ||||
|     warn_timeout(); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int | ||||
| __i8042_command(int command, u8 *param) | ||||
| { | ||||
|     int receive = (command >> 8) & 0xf; | ||||
|     int send = (command >> 12) & 0xf; | ||||
|  | ||||
|     // Send the command. | ||||
|     int ret = i8042_wait_write(); | ||||
|     if (ret) | ||||
|         return ret; | ||||
|     outb(command, PORT_PS2_STATUS); | ||||
|  | ||||
|     // Send parameters (if any). | ||||
|     int i; | ||||
|     for (i = 0; i < send; i++) { | ||||
|         ret = i8042_wait_write(); | ||||
|         if (ret) | ||||
|             return ret; | ||||
|         outb(param[i], PORT_PS2_DATA); | ||||
|     } | ||||
|  | ||||
|     // Receive parameters (if any). | ||||
|     for (i = 0; i < receive; i++) { | ||||
|         ret = i8042_wait_read(); | ||||
|         if (ret) | ||||
|             return ret; | ||||
|         param[i] = inb(PORT_PS2_DATA); | ||||
|         dprintf(7, "i8042 param=%x\n", param[i]); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| i8042_command(int command, u8 *param) | ||||
| { | ||||
|     dprintf(7, "i8042_command cmd=%x\n", command); | ||||
|     int ret = __i8042_command(command, param); | ||||
|     if (ret) | ||||
|         dprintf(2, "i8042 command %x failed\n", command); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int | ||||
| i8042_kbd_write(u8 c) | ||||
| { | ||||
|     dprintf(7, "i8042_kbd_write c=%d\n", c); | ||||
|     int ret = i8042_wait_write(); | ||||
|     if (! ret) | ||||
|         outb(c, PORT_PS2_DATA); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int | ||||
| i8042_aux_write(u8 c) | ||||
| { | ||||
|     return i8042_command(I8042_CMD_AUX_SEND, &c); | ||||
| } | ||||
|  | ||||
| void | ||||
| i8042_reboot(void) | ||||
| { | ||||
|     int i; | ||||
|     for (i=0; i<10; i++) { | ||||
|         i8042_wait_write(); | ||||
|         udelay(50); | ||||
|         outb(0xfe, PORT_PS2_STATUS); /* pulse reset low */ | ||||
|         udelay(50); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| /**************************************************************** | ||||
|  * Device commands. | ||||
|  ****************************************************************/ | ||||
|  | ||||
| #define PS2_RET_ACK             0xfa | ||||
| #define PS2_RET_NAK             0xfe | ||||
|  | ||||
| static int | ||||
| ps2_recvbyte(int aux, int needack, int timeout) | ||||
| { | ||||
|     u64 end = calc_future_tsc(timeout); | ||||
|     for (;;) { | ||||
|         u8 status = inb(PORT_PS2_STATUS); | ||||
|         if (status & I8042_STR_OBF) { | ||||
|             u8 data = inb(PORT_PS2_DATA); | ||||
|             dprintf(7, "ps2 read %x\n", data); | ||||
|  | ||||
|             if (!!(status & I8042_STR_AUXDATA) == aux) { | ||||
|                 if (!needack) | ||||
|                     return data; | ||||
|                 if (data == PS2_RET_ACK) | ||||
|                     return data; | ||||
|                 if (data == PS2_RET_NAK) { | ||||
|                     dprintf(1, "Got ps2 nak (status=%x)\n", status); | ||||
|                     return data; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // This data not part of command - just discard it. | ||||
|             dprintf(1, "Discarding ps2 data %02x (status=%02x)\n", data, status); | ||||
|         } | ||||
|  | ||||
|         if (check_tsc(end)) { | ||||
|             // Don't warn on second byte of a reset | ||||
|             if (timeout > 100) | ||||
|                 warn_timeout(); | ||||
|             return -1; | ||||
|         } | ||||
|         yield(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int | ||||
| ps2_sendbyte(int aux, u8 command, int timeout) | ||||
| { | ||||
|     dprintf(7, "ps2_sendbyte aux=%d cmd=%x\n", aux, command); | ||||
|     int ret; | ||||
|     if (aux) | ||||
|         ret = i8042_aux_write(command); | ||||
|     else | ||||
|         ret = i8042_kbd_write(command); | ||||
|     if (ret) | ||||
|         return ret; | ||||
|  | ||||
|     // Read ack. | ||||
|     ret = ps2_recvbyte(aux, 1, timeout); | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
|     if (ret != PS2_RET_ACK) | ||||
|         return -1; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| __ps2_command(int aux, int command, u8 *param) | ||||
| { | ||||
|     int ret2; | ||||
|     int receive = (command >> 8) & 0xf; | ||||
|     int send = (command >> 12) & 0xf; | ||||
|  | ||||
|     // Disable interrupts and keyboard/mouse. | ||||
|     u8 ps2ctr = GET_EBDA(ps2ctr); | ||||
|     u8 newctr = ((ps2ctr | I8042_CTR_AUXDIS | I8042_CTR_KBDDIS) | ||||
|                  & ~(I8042_CTR_KBDINT|I8042_CTR_AUXINT)); | ||||
|     dprintf(6, "i8042 ctr old=%x new=%x\n", ps2ctr, newctr); | ||||
|     int ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr); | ||||
|     if (ret) | ||||
|         return ret; | ||||
|  | ||||
|     // Flush any interrupts already pending. | ||||
|     yield(); | ||||
|  | ||||
|     // Enable port command is being sent to. | ||||
|     if (aux) | ||||
|         newctr &= ~I8042_CTR_AUXDIS; | ||||
|     else | ||||
|         newctr &= ~I8042_CTR_KBDDIS; | ||||
|     ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr); | ||||
|     if (ret) | ||||
|         goto fail; | ||||
|  | ||||
|     if (command == ATKBD_CMD_RESET_BAT) { | ||||
|         // Reset is special wrt timeouts and bytes received. | ||||
|  | ||||
|         // Send command. | ||||
|         ret = ps2_sendbyte(aux, command, 1000); | ||||
|         if (ret) | ||||
|             goto fail; | ||||
|  | ||||
|         // Receive parameters. | ||||
|         ret = ps2_recvbyte(aux, 0, 4000); | ||||
|         if (ret < 0) | ||||
|             goto fail; | ||||
|         param[0] = ret; | ||||
|         ret = ps2_recvbyte(aux, 0, 100); | ||||
|         if (ret < 0) | ||||
|             // Some devices only respond with one byte on reset. | ||||
|             ret = 0; | ||||
|         param[1] = ret; | ||||
|     } else if (command == ATKBD_CMD_GETID) { | ||||
|         // Getid is special wrt bytes received. | ||||
|  | ||||
|         // Send command. | ||||
|         ret = ps2_sendbyte(aux, command, 200); | ||||
|         if (ret) | ||||
|             goto fail; | ||||
|  | ||||
|         // Receive parameters. | ||||
|         ret = ps2_recvbyte(aux, 0, 500); | ||||
|         if (ret < 0) | ||||
|             goto fail; | ||||
|         param[0] = ret; | ||||
|         if (ret == 0xab || ret == 0xac || ret == 0x2b || ret == 0x5d | ||||
|             || ret == 0x60 || ret == 0x47) { | ||||
|             // These ids (keyboards) return two bytes. | ||||
|             ret = ps2_recvbyte(aux, 0, 500); | ||||
|             if (ret < 0) | ||||
|                 goto fail; | ||||
|             param[1] = ret; | ||||
|         } else { | ||||
|             param[1] = 0; | ||||
|         } | ||||
|     } else { | ||||
|         // Send command. | ||||
|         ret = ps2_sendbyte(aux, command, 200); | ||||
|         if (ret) | ||||
|             goto fail; | ||||
|  | ||||
|         // Send parameters (if any). | ||||
|         int i; | ||||
|         for (i = 0; i < send; i++) { | ||||
|             ret = ps2_sendbyte(aux, param[i], 200); | ||||
|             if (ret) | ||||
|                 goto fail; | ||||
|         } | ||||
|  | ||||
|         // Receive parameters (if any). | ||||
|         for (i = 0; i < receive; i++) { | ||||
|             ret = ps2_recvbyte(aux, 0, 500); | ||||
|             if (ret < 0) | ||||
|                 goto fail; | ||||
|             param[i] = ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ret = 0; | ||||
|  | ||||
| fail: | ||||
|     // Restore interrupts and keyboard/mouse. | ||||
|     ret2 = i8042_command(I8042_CMD_CTL_WCTR, &ps2ctr); | ||||
|     if (ret2) | ||||
|         return ret2; | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int | ||||
| ps2_command(int aux, int command, u8 *param) | ||||
| { | ||||
|     dprintf(7, "ps2_command aux=%d cmd=%x\n", aux, command); | ||||
|     int ret = __ps2_command(aux, command, param); | ||||
|     if (ret) | ||||
|         dprintf(2, "ps2 command %x failed (aux=%d)\n", command, aux); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| int | ||||
| ps2_kbd_command(int command, u8 *param) | ||||
| { | ||||
|     return ps2_command(0, command, param); | ||||
| } | ||||
|  | ||||
| int | ||||
| ps2_mouse_command(int command, u8 *param) | ||||
| { | ||||
|     return ps2_command(1, command, param); | ||||
| } | ||||
|  | ||||
|  | ||||
| /**************************************************************** | ||||
|  * IRQ handlers | ||||
|  ****************************************************************/ | ||||
| #if 0 | ||||
|  | ||||
| // INT74h : PS/2 mouse hardware interrupt | ||||
| void VISIBLE16 | ||||
| handle_74(void) | ||||
| { | ||||
|     if (! CONFIG_PS2PORT) | ||||
|         return; | ||||
|  | ||||
|     debug_isr(DEBUG_ISR_74); | ||||
|  | ||||
|     u8 v = inb(PORT_PS2_STATUS); | ||||
|     if ((v & (I8042_STR_OBF|I8042_STR_AUXDATA)) | ||||
|         != (I8042_STR_OBF|I8042_STR_AUXDATA)) { | ||||
|         dprintf(1, "ps2 mouse irq but no mouse data.\n"); | ||||
|         goto done; | ||||
|     } | ||||
|     v = inb(PORT_PS2_DATA); | ||||
|  | ||||
|     if (!(GET_EBDA(ps2ctr) & I8042_CTR_AUXINT)) | ||||
|         // Interrupts not enabled. | ||||
|         goto done; | ||||
|  | ||||
|     process_mouse(v); | ||||
|  | ||||
| done: | ||||
|     eoi_pic2(); | ||||
| } | ||||
|  | ||||
| // INT09h : Keyboard Hardware Service Entry Point | ||||
| void VISIBLE16 | ||||
| handle_09(void) | ||||
| { | ||||
|     if (! CONFIG_PS2PORT) | ||||
|         return; | ||||
|  | ||||
|     debug_isr(DEBUG_ISR_09); | ||||
|  | ||||
|     // read key from keyboard controller | ||||
|     u8 v = inb(PORT_PS2_STATUS); | ||||
|     if (v & I8042_STR_AUXDATA) { | ||||
|         dprintf(1, "ps2 keyboard irq but found mouse data?!\n"); | ||||
|         goto done; | ||||
|     } | ||||
|     v = inb(PORT_PS2_DATA); | ||||
|  | ||||
|     if (!(GET_EBDA(ps2ctr) & I8042_CTR_KBDINT)) | ||||
|         // Interrupts not enabled. | ||||
|         goto done; | ||||
|  | ||||
|     process_key(v); | ||||
|  | ||||
| done: | ||||
|     eoi_pic1(); | ||||
| } | ||||
|  | ||||
| #endif | ||||
| /**************************************************************** | ||||
|  * Setup | ||||
|  ****************************************************************/ | ||||
|  | ||||
| static void | ||||
| keyboard_init(void *data) | ||||
| { | ||||
|     /* flush incoming keys */ | ||||
|     int ret = i8042_flush(); | ||||
|     if (ret) | ||||
|         return; | ||||
|  | ||||
|     // Controller self-test. | ||||
|     u8 param[2]; | ||||
|     ret = i8042_command(I8042_CMD_CTL_TEST, param); | ||||
|     if (ret) | ||||
|         return; | ||||
|     if (param[0] != 0x55) { | ||||
|         dprintf(1, "i8042 self test failed (got %x not 0x55)\n", param[0]); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Controller keyboard test. | ||||
|     ret = i8042_command(I8042_CMD_KBD_TEST, param); | ||||
|     if (ret) | ||||
|         return; | ||||
|     if (param[0] != 0x00) { | ||||
|         dprintf(1, "i8042 keyboard test failed (got %x not 0x00)\n", param[0]); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Disable keyboard and mouse events. | ||||
|     SET_EBDA(ps2ctr, I8042_CTR_KBDDIS | I8042_CTR_AUXDIS); | ||||
|  | ||||
|  | ||||
|     /* ------------------- keyboard side ------------------------*/ | ||||
|     /* reset keyboard and self test  (keyboard side) */ | ||||
|     ret = ps2_kbd_command(ATKBD_CMD_RESET_BAT, param); | ||||
|     if (ret) | ||||
|         return; | ||||
|     if (param[0] != 0xaa) { | ||||
|         dprintf(1, "keyboard self test failed (got %x not 0xaa)\n", param[0]); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Disable keyboard */ | ||||
|     ret = ps2_kbd_command(ATKBD_CMD_RESET_DIS, NULL); | ||||
|     if (ret) | ||||
|         return; | ||||
|  | ||||
|     // Set scancode command (mode 2) | ||||
|     param[0] = 0x02; | ||||
|     ret = ps2_kbd_command(ATKBD_CMD_SSCANSET, param); | ||||
|     if (ret) | ||||
|         return; | ||||
|  | ||||
|     // Keyboard Mode: disable mouse, scan code convert, enable kbd IRQ | ||||
|     SET_EBDA(ps2ctr, I8042_CTR_AUXDIS | I8042_CTR_XLATE | I8042_CTR_KBDINT); | ||||
|  | ||||
|     /* Enable keyboard */ | ||||
|     ret = ps2_kbd_command(ATKBD_CMD_ENABLE, NULL); | ||||
|     if (ret) | ||||
|         return; | ||||
|  | ||||
|     dprintf(1, "PS2 keyboard initialized\n"); | ||||
| } | ||||
|  | ||||
| void | ||||
| ps2port_setup(void) | ||||
| { | ||||
|     ASSERT32FLAT(); | ||||
|     if (! CONFIG_PS2PORT) | ||||
|         return; | ||||
|     dprintf(3, "init ps2port\n"); | ||||
|  | ||||
|     enable_hwirq(1, FUNC16(entry_09)); | ||||
|     enable_hwirq(12, FUNC16(entry_74)); | ||||
|  | ||||
|     run_thread(keyboard_init, NULL); | ||||
| } | ||||
							
								
								
									
										71
									
								
								ps2port.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								ps2port.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| /* Basic ps2 port (keyboard/mouse) command handling. | ||||
|  | ||||
|    This file is copied (mostly) intact from SeaBIOS. | ||||
|    It is covered by the GNU Lesser General Public License, v3. | ||||
|  | ||||
|    You should have received a copy of the GNU Lesser General Public License | ||||
|    along with this program; see the file COPYING.  If not see | ||||
|    <http://www.gnu.org/licenses/>.  */ | ||||
|  | ||||
| #ifndef PS2PORT_H | ||||
| #define PS2PORT_H | ||||
|  | ||||
| typedef uint8_t u8; | ||||
|  | ||||
| // Standard commands. | ||||
| #define I8042_CMD_CTL_RCTR      0x0120 | ||||
| #define I8042_CMD_CTL_WCTR      0x1060 | ||||
| #define I8042_CMD_CTL_TEST      0x01aa | ||||
|  | ||||
| #define I8042_CMD_KBD_TEST      0x01ab | ||||
| #define I8042_CMD_KBD_DISABLE   0x00ad | ||||
| #define I8042_CMD_KBD_ENABLE    0x00ae | ||||
|  | ||||
| #define I8042_CMD_AUX_DISABLE   0x00a7 | ||||
| #define I8042_CMD_AUX_ENABLE    0x00a8 | ||||
| #define I8042_CMD_AUX_SEND      0x10d4 | ||||
|  | ||||
| // Keyboard commands | ||||
| #define ATKBD_CMD_SETLEDS       0x10ed | ||||
| #define ATKBD_CMD_SSCANSET      0x10f0 | ||||
| #define ATKBD_CMD_GETID         0x02f2 | ||||
| #define ATKBD_CMD_ENABLE        0x00f4 | ||||
| #define ATKBD_CMD_RESET_DIS     0x00f5 | ||||
| #define ATKBD_CMD_RESET_BAT     0x02ff | ||||
|  | ||||
| // Mouse commands | ||||
| #define PSMOUSE_CMD_SETSCALE11  0x00e6 | ||||
| #define PSMOUSE_CMD_SETSCALE21  0x00e7 | ||||
| #define PSMOUSE_CMD_SETRES      0x10e8 | ||||
| #define PSMOUSE_CMD_GETINFO     0x03e9 | ||||
| #define PSMOUSE_CMD_GETID       0x02f2 | ||||
| #define PSMOUSE_CMD_SETRATE     0x10f3 | ||||
| #define PSMOUSE_CMD_ENABLE      0x00f4 | ||||
| #define PSMOUSE_CMD_DISABLE     0x00f5 | ||||
| #define PSMOUSE_CMD_RESET_BAT   0x02ff | ||||
|  | ||||
| // Status register bits. | ||||
| #define I8042_STR_PARITY        0x80 | ||||
| #define I8042_STR_TIMEOUT       0x40 | ||||
| #define I8042_STR_AUXDATA       0x20 | ||||
| #define I8042_STR_KEYLOCK       0x10 | ||||
| #define I8042_STR_CMDDAT        0x08 | ||||
| #define I8042_STR_MUXERR        0x04 | ||||
| #define I8042_STR_IBF           0x02 | ||||
| #define I8042_STR_OBF           0x01 | ||||
|  | ||||
| // Control register bits. | ||||
| #define I8042_CTR_KBDINT        0x01 | ||||
| #define I8042_CTR_AUXINT        0x02 | ||||
| #define I8042_CTR_IGNKEYLOCK    0x08 | ||||
| #define I8042_CTR_KBDDIS        0x10 | ||||
| #define I8042_CTR_AUXDIS        0x20 | ||||
| #define I8042_CTR_XLATE         0x40 | ||||
|  | ||||
| // functions | ||||
| void i8042_reboot(void); | ||||
| int ps2_kbd_command(int command, u8 *param); | ||||
| int ps2_mouse_command(int command, u8 *param); | ||||
| void ps2port_setup(void); | ||||
|  | ||||
| #endif // ps2port.h | ||||
							
								
								
									
										39
									
								
								util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								util.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| /* Utility functions for the QEMU PALcode. | ||||
|  | ||||
|    Copyright (C) 2011 Richard Henderson | ||||
|  | ||||
|    This file is part of QEMU PALcode. | ||||
|  | ||||
|    This program is free software; you can redistribute it and/or modify | ||||
|    it under the terms of the GNU General Public License as published by | ||||
|    the Free Software Foundation; either version 2 of the License or | ||||
|    (at your option) any later version. | ||||
|  | ||||
|    This program is distributed in the hope that it will be useful, | ||||
|    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the text | ||||
|    of the GNU General Public License for more details. | ||||
|  | ||||
|    You should have received a copy of the GNU General Public License | ||||
|    along with this program; see the file COPYING.  If not see | ||||
|    <http://www.gnu.org/licenses/>.  */ | ||||
|  | ||||
| #include "protos.h" | ||||
|  | ||||
|  | ||||
| void | ||||
| ndelay(unsigned long nsec) | ||||
| { | ||||
|   unsigned long target, now; | ||||
|  | ||||
|   now = get_wall_time(); | ||||
|   target = now + nsec; | ||||
|  | ||||
|   set_alarm_abs(nsec); | ||||
|   do | ||||
|     { | ||||
|       wtint(0); | ||||
|       now = get_wall_time(); | ||||
|     } | ||||
|   while (now < target); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Richard Henderson
					Richard Henderson