diff options
author | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2016-03-11 20:22:28 -0800 |
---|---|---|
committer | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2016-04-19 18:34:18 +0200 |
commit | 86ddd732bd82b84724883b1f1579e18790611fe5 (patch) | |
tree | e52c4c88ceb7e14fd415031c56997d8c4054116e /src/drivers/pc80/pc | |
parent | 3812597c668b795d4fc85578021e501f2a56f6b9 (diff) |
kbuild: Allow drivers to fit src/drivers/[X]/[Y]/ scheme
Reorder drivers to fit src/drivers/[X]/[Y]/ scheme to make
them pluggable.
Also, fix up the following driver subdirectories by switching
to the src/drivers/[X]/[Y]/ scheme as these are hard requirements
for the main change:
* drivers/intel
* drivers/pc80
* drivers/dec
Change-Id: I455d3089a317181d5b99bf658df759ec728a5f6b
Signed-off-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Reviewed-on: https://review.coreboot.org/14047
Tested-by: build bot (Jenkins)
Reviewed-by: Martin Roth <martinroth@google.com>
Diffstat (limited to 'src/drivers/pc80/pc')
-rw-r--r-- | src/drivers/pc80/pc/Kconfig | 18 | ||||
-rw-r--r-- | src/drivers/pc80/pc/Makefile.inc | 12 | ||||
-rw-r--r-- | src/drivers/pc80/pc/i8254.c | 51 | ||||
-rw-r--r-- | src/drivers/pc80/pc/i8259.c | 139 | ||||
-rw-r--r-- | src/drivers/pc80/pc/isa-dma.c | 44 | ||||
-rw-r--r-- | src/drivers/pc80/pc/keyboard.c | 387 | ||||
-rw-r--r-- | src/drivers/pc80/pc/ps2_controller.asl | 46 | ||||
-rw-r--r-- | src/drivers/pc80/pc/spkmodem.c | 126 | ||||
-rw-r--r-- | src/drivers/pc80/pc/udelay_io.c | 14 |
9 files changed, 837 insertions, 0 deletions
diff --git a/src/drivers/pc80/pc/Kconfig b/src/drivers/pc80/pc/Kconfig new file mode 100644 index 0000000000..c44cf9144d --- /dev/null +++ b/src/drivers/pc80/pc/Kconfig @@ -0,0 +1,18 @@ +# Might be removed (alongside with the PS/2 init code) once payloads +# reliably support PS/2 init themselves. + +config DRIVERS_PS2_KEYBOARD + bool "PS/2 keyboard init" + default n + depends on PC80_SYSTEM + help + Enable this option to initialize PS/2 keyboards found connected + to the PS/2 port. + + Some payloads (eg, filo) require this option. Other payloads + (eg, GRUB 2, SeaBIOS, Linux) do not require it. + Initializing a PS/2 keyboard can take several hundred milliseconds. + + If you know you will only use a payload which does not require + this option, then you can say N here to speed up boot time. + Otherwise say Y. diff --git a/src/drivers/pc80/pc/Makefile.inc b/src/drivers/pc80/pc/Makefile.inc new file mode 100644 index 0000000000..8c348e323e --- /dev/null +++ b/src/drivers/pc80/pc/Makefile.inc @@ -0,0 +1,12 @@ +ifeq ($(CONFIG_ARCH_X86),y) + +ramstage-y += isa-dma.c +ramstage-y += i8254.c +ramstage-y += i8259.c +ramstage-$(CONFIG_UDELAY_IO) += udelay_io.c +romstage-$(CONFIG_UDELAY_IO) += udelay_io.c +ramstage-y += keyboard.c +ramstage-$(CONFIG_SPKMODEM) += spkmodem.c +romstage-$(CONFIG_SPKMODEM) += spkmodem.c + +endif diff --git a/src/drivers/pc80/pc/i8254.c b/src/drivers/pc80/pc/i8254.c new file mode 100644 index 0000000000..5851ec08a0 --- /dev/null +++ b/src/drivers/pc80/pc/i8254.c @@ -0,0 +1,51 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 coresystems GmbH + * + * 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; version 2 of the License. + * + * 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 + * GNU General Public License for more details. + */ + +#include <arch/io.h> +#include <pc80/i8254.h> +#include <console/console.h> + +/* Initialize i8254 timers */ + +void setup_i8254(void) +{ + /* Timer 0 (taken from biosemu) */ + outb(TIMER0_SEL | WORD_ACCESS | MODE3 | BINARY_COUNT, TIMER_MODE_PORT); + outb(0x00, TIMER0_PORT); + outb(0x00, TIMER0_PORT); + + /* Timer 1 */ + outb(TIMER1_SEL | LOBYTE_ACCESS | MODE3 | BINARY_COUNT, + TIMER_MODE_PORT); + outb(0x12, TIMER1_PORT); +} + +#if CONFIG_UDELAY_TIMER2 +static void load_timer2(unsigned int ticks) +{ + /* Set up the timer gate, turn off the speaker */ + outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB); + outb(TIMER2_SEL | WORD_ACCESS | MODE0 | BINARY_COUNT, TIMER_MODE_PORT); + outb(ticks & 0xFF, TIMER2_PORT); + outb(ticks >> 8, TIMER2_PORT); +} + +void udelay(int usecs) +{ + load_timer2((usecs * TICKS_PER_MS) / 1000); + while ((inb(PPC_PORTB) & PPCB_T2OUT) == 0) + ; +} +#endif diff --git a/src/drivers/pc80/pc/i8259.c b/src/drivers/pc80/pc/i8259.c new file mode 100644 index 0000000000..4261d5bec4 --- /dev/null +++ b/src/drivers/pc80/pc/i8259.c @@ -0,0 +1,139 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 coresystems GmbH + * Copyright (C) 2013 Sage Electronic Engineering, LLC. + * + * 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; version 2 of the License. + * + * 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 + * GNU General Public License for more details. + */ + +#include <arch/io.h> +#include <pc80/i8259.h> +#include <console/console.h> + +/* Read the current PIC IRQ mask */ +u16 pic_read_irq_mask(void) +{ + u16 mask; + int i; + + mask = inb(MASTER_PIC_OCW1) | (inb(SLAVE_PIC_OCW1) << 8); + + printk(BIOS_DEBUG, "8259 PIC: OCW1 IRQ Mask: 0x%x\n", mask); + printk(BIOS_SPEW, "\tEnabled IRQs (0 = Unmasked, 1 = Masked off):\n" + "\t\tMaster\t\tSlave\n"); + for (i = 0; i <= 7; i++) { + printk(BIOS_SPEW, "\t\tIRQ%X: %x\t\tIRQ%X: %x\n", + i, (mask >> i) & 1, i + 8, (mask >> (i + 8)) & 1); + } + return mask; +} + +/* + * Write an IRQ mask to the PIC: + * IRQA is bit 0xA in the 16 bit bitmask (OCW1) + */ +void pic_write_irq_mask(u16 mask) +{ + outb(mask, MASTER_PIC_OCW1); + outb(mask >> 8, SLAVE_PIC_OCW1); +} + +/* + * The PIC IRQs default to masked off + * Allow specific IRQs to be enabled (1) + * or disabled by (0) the user + */ +void pic_irq_enable(u8 int_num, u8 mask) +{ + pic_write_irq_mask(pic_read_irq_mask() & ~(mask << int_num)); + pic_read_irq_mask(); +} + +void setup_i8259(void) +{ + /* A write to ICW1 starts the Interrupt Controller Initialization + * Sequence. This implicitly causes the following to happen: + * - Interrupt Mask register is cleared + * - Priority 7 is assigned to IRQ7 input + * - Slave mode address is set to 7 + * - Special mask mode is cleared + * + * We send the initialization sequence to both the master and + * slave i8259 controller. + */ + outb(ICW_SELECT|IC4, MASTER_PIC_ICW1); + outb(ICW_SELECT|IC4, SLAVE_PIC_ICW1); + + /* Now the interrupt controller expects us to write to ICW2. */ + outb(INT_VECTOR_MASTER | IRQ0, MASTER_PIC_ICW2); + outb(INT_VECTOR_SLAVE | IRQ8, SLAVE_PIC_ICW2); + + /* Now the interrupt controller expects us to write to ICW3. + * + * The normal scenario is to set up cascading on IRQ2 on the master + * i8259 and assign the slave ID 2 to the slave i8259. + */ + outb(CASCADED_PIC, MASTER_PIC_ICW3); + outb(SLAVE_ID, SLAVE_PIC_ICW3); + + /* Now the interrupt controller expects us to write to ICW4. + * + * We switch both i8259 to microprocessor mode because they're + * operating as part of an x86 architecture based chipset + */ + outb(MICROPROCESSOR_MODE, MASTER_PIC_ICW2); + outb(MICROPROCESSOR_MODE, SLAVE_PIC_ICW2); + + /* Now clear the interrupts through OCW1. + * First we mask off all interrupts on the slave interrupt controller + * then we mask off all interrupts but interrupt 2 on the master + * controller. This way the cascading stays alive. + */ + outb(ALL_IRQS, SLAVE_PIC_OCW1); + outb(ALL_IRQS & ~IRQ2, MASTER_PIC_OCW1); +} + +/** + * @brief Configure IRQ triggering in the i8259 compatible Interrupt Controller. + * + * Switch a certain interrupt to be level / edge triggered. + * + * @param int_num legacy interrupt number (3-7, 9-15) + * @param is_level_triggered 1 for level triggered interrupt, 0 for edge + * triggered interrupt + */ +void i8259_configure_irq_trigger(int int_num, int is_level_triggered) +{ + u16 int_bits = inb(ELCR1) | (((u16)inb(ELCR2)) << 8); + + if (is_level_triggered) + int_bits |= (1 << int_num); + else + int_bits &= ~(1 << int_num); + + /* Write new values */ + outb((u8)(int_bits & 0xff), ELCR1); + outb((u8)(int_bits >> 8), ELCR2); + +#ifdef PARANOID_IRQ_TRIGGERS + /* Try reading back the new values. This seems like an error + * but it is not. */ + if (inb(ELCR1) != (int_bits & 0xff)) { + printk(BIOS_ERR, "%s: lower order bits are wrong: want 0x%x, got 0x%x\n", + __func__, (int_bits & 0xff), inb(ELCR1)); + } + + if (inb(ELCR2) != (int_bits >> 8)) { + printk(BIOS_ERR, "%s: higher order bits are wrong: want 0x%x, got 0x%x\n", + __func__, (int_bits>>8), inb(ELCR2)); + } +#endif +} diff --git a/src/drivers/pc80/pc/isa-dma.c b/src/drivers/pc80/pc/isa-dma.c new file mode 100644 index 0000000000..b64f125f3a --- /dev/null +++ b/src/drivers/pc80/pc/isa-dma.c @@ -0,0 +1,44 @@ +#include <arch/io.h> +#include <pc80/isa-dma.h> + +/* DMA controller registers */ +#define DMA1_CMD_REG 0x08 /* command register (w) */ +#define DMA1_STAT_REG 0x08 /* status register (r) */ +#define DMA1_REQ_REG 0x09 /* request register (w) */ +#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */ +#define DMA1_MODE_REG 0x0B /* mode register (w) */ +#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */ +#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */ +#define DMA1_RESET_REG 0x0D /* Master Clear (w) */ +#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */ +#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */ + +#define DMA2_CMD_REG 0xD0 /* command register (w) */ +#define DMA2_STAT_REG 0xD0 /* status register (r) */ +#define DMA2_REQ_REG 0xD2 /* request register (w) */ +#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */ +#define DMA2_MODE_REG 0xD6 /* mode register (w) */ +#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */ +#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */ +#define DMA2_RESET_REG 0xDA /* Master Clear (w) */ +#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */ +#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */ + +#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ +#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ +#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */ + +#define DMA_AUTOINIT 0x10 + + +void isa_dma_init(void) +{ + /* slave at 0x00 - 0x0f */ + /* master at 0xc0 - 0xdf */ + /* 0x80 - 0x8f DMA page registers */ + /* DMA: 0x00, 0x02, 0x4, 0x06 base address for DMA channel */ + outb(0, DMA1_RESET_REG); + outb(0, DMA2_RESET_REG); + outb(DMA_MODE_CASCADE, DMA2_MODE_REG); + outb(0, DMA2_MASK_REG); +} diff --git a/src/drivers/pc80/pc/keyboard.c b/src/drivers/pc80/pc/keyboard.c new file mode 100644 index 0000000000..415dbd1d13 --- /dev/null +++ b/src/drivers/pc80/pc/keyboard.c @@ -0,0 +1,387 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2015 Raptor Engineering + * Copyright (C) 2009 coresystems GmbH + * Copyright (C) 2008 Advanced Micro Devices, Inc. + * Copyright (C) 2003 Ollie Lo <ollielo@hotmail.com> + * + * 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; version 2 of the License. + * + * 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 + * GNU General Public License for more details. + */ + +#include <console/console.h> +#include <pc80/keyboard.h> +#include <device/device.h> +#include <arch/io.h> +#include <delay.h> +#include <types.h> +#include <arch/acpi.h> + +#define KBD_DATA 0x60 +#define KBD_COMMAND 0x64 +#define KBD_STATUS 0x64 +#define KBD_IBF (1 << 1) // 1: input buffer full (data ready for ec) +#define KBD_OBF (1 << 0) // 1: output buffer full (data ready for host) + +// Keyboard Controller Commands +#define KBC_CMD_READ_COMMAND 0x20 // Read command byte +#define KBC_CMD_WRITE_COMMAND 0x60 // Write command byte +#define KBC_CMD_AUX_ENABLE 0xA8 // Auxiliary Interface enable +#define KBC_CMD_AUX_TEST 0xA9 // Auxiliary Interface test +#define KBC_CMD_SELF_TEST 0xAA // Controller self-test +#define KBC_CMD_KBD_TEST 0xAB // Keyboard Interface test + +/* The Keyboard controller command byte + * BIT | Description + * ----+------------------------------------------------------- + * 7 | reserved, must be zero + * 6 | XT Translation, (1 = on, 0 = off) + * 5 | Disable Mouse Port (1 = disable, 0 = enable) + * 4 | Disable Keyboard Port (1 = disable, 0 = enable) + * 3 | reserved, must be zero + * 2 | System Flag (1 = self-test passed. DO NOT SET TO ZERO) + * 1 | Mouse Port Interrupts (1 = enable, 0 = disable) + * 0 | Keyboard Port Interrupts (1 = enable, 0 = disable) + */ + +// Keyboard Controller Replies +#define KBC_REPLY_SELFTEST_OK 0x55 // controller self-test succeeded + +// +// Keyboard Replies +// +#define KBD_REPLY_POR 0xAA // Power on reset +#define KBD_REPLY_ACK 0xFA // Command ACK +#define KBD_REPLY_RESEND 0xFE // Command NACK, send command again + +/* Wait 400ms for keyboard controller answers */ +#define KBC_TIMEOUT_IN_MS 400 + +static int kbc_input_buffer_empty(void) +{ + u32 timeout; + + for (timeout = KBC_TIMEOUT_IN_MS; + timeout && (inb(KBD_STATUS) & KBD_IBF); timeout--) + mdelay(1); + + if (!timeout) + printk(BIOS_WARNING, + "Unexpected Keyboard controller input buffer full\n"); + return !!timeout; +} + +static int kbc_output_buffer_full(void) +{ + u32 timeout; + + for (timeout = KBC_TIMEOUT_IN_MS; + timeout && ((inb(KBD_STATUS) & KBD_OBF) == 0); timeout--) + mdelay(1); + + if (!timeout) + printk(BIOS_INFO, + "Keyboard controller output buffer result timeout\n"); + return !!timeout; +} + +static int kbc_cleanup_buffers(void) +{ + u32 timeout; + + for (timeout = KBC_TIMEOUT_IN_MS; + timeout && (inb(KBD_STATUS) & (KBD_OBF | KBD_IBF)); timeout--) { + mdelay(1); + inb(KBD_DATA); + } + + if (!timeout) { + printk(BIOS_ERR, + "Couldn't cleanup the keyboard controller buffers\n"); + printk(BIOS_ERR, "Status (0x%x): 0x%x, Buffer (0x%x): 0x%x\n", + KBD_STATUS, inb(KBD_STATUS), KBD_DATA, inb(KBD_DATA)); + } + + return !!timeout; +} + +static enum cb_err kbc_self_test(uint8_t probe_aux, uint8_t *aux_probe_result) +{ + uint8_t self_test; + uint8_t byte; + + /* Set initial aux probe output value */ + if (aux_probe_result) + *aux_probe_result = 0; + + /* Clean up any junk that might have been in the KBC. + * Both input and output buffers must be empty. + */ + if (!kbc_cleanup_buffers()) + return CB_KBD_CONTROLLER_FAILURE; + + /* reset/self test 8042 - send cmd 0xAA */ + outb(KBC_CMD_SELF_TEST, KBD_COMMAND); + + if (!kbc_output_buffer_full()) { + /* There probably is no keyboard controller. */ + printk(BIOS_ERR, "Could not reset keyboard controller.\n"); + return CB_KBD_CONTROLLER_FAILURE; + } + + /* read self-test result, 0x55 is returned in the output buffer */ + self_test = inb(KBD_DATA); + + if (self_test != 0x55) { + printk(BIOS_ERR, "Keyboard Controller self-test failed: 0x%x\n", + self_test); + return CB_KBD_CONTROLLER_FAILURE; + } + + /* ensure the buffers are empty */ + kbc_cleanup_buffers(); + + /* keyboard interface test */ + outb(KBC_CMD_KBD_TEST, KBD_COMMAND); + + if (!kbc_output_buffer_full()) { + printk(BIOS_ERR, "Keyboard Interface test timed out.\n"); + return CB_KBD_CONTROLLER_FAILURE; + } + + /* read test result, 0x00 should be returned in case of no failures */ + self_test = inb(KBD_DATA); + + if (self_test != 0x00) { + printk(BIOS_ERR, "Keyboard Interface test failed: 0x%x\n", + self_test); + return CB_KBD_INTERFACE_FAILURE; + } + + if (probe_aux) { + /* aux interface detect */ + outb(KBC_CMD_AUX_ENABLE, KBD_COMMAND); + if (!kbc_input_buffer_empty()) { + printk(BIOS_ERR, "Timeout waiting for controller during aux enable.\n"); + return CB_KBD_CONTROLLER_FAILURE; + } + outb(KBC_CMD_READ_COMMAND, KBD_COMMAND); + if (!kbc_output_buffer_full()) { + printk(BIOS_ERR, "Timeout waiting for controller during aux probe.\n"); + return CB_KBD_CONTROLLER_FAILURE; + } + + byte = inb(KBD_DATA); + if (!(byte & (0x1 << 5))) { + printk(BIOS_DEBUG, "PS/2 auxiliary channel detected...\n"); + + /* auxiliary interface test */ + outb(KBC_CMD_AUX_TEST, KBD_COMMAND); + + if (!kbc_output_buffer_full()) { + printk(BIOS_ERR, "Auxiliary channel probe timed out.\n"); + goto aux_failure; + } + + /* read test result, 0x00 should be returned in case of no failures */ + self_test = inb(KBD_DATA); + + if (self_test != 0x00) { + printk(BIOS_ERR, "No device detected on auxiliary channel: 0x%x\n", + self_test); + goto aux_failure; + } + + printk(BIOS_DEBUG, "PS/2 device detected on auxiliary channel\n"); + if (aux_probe_result) + *aux_probe_result = 1; + } + } + +aux_failure: + + return CB_SUCCESS; +} + +static u8 send_keyboard(u8 command) +{ + u8 regval = 0; + u8 resend = 10; + + do { + if (!kbc_input_buffer_empty()) + return 0; + outb(command, KBD_DATA); + /* the reset command takes much longer then normal commands and + * even worse, some keyboards do send the ACK _after_ doing the + * reset */ + if (command == 0xFF) { + u8 retries; + + for (retries = 9; retries && !kbc_output_buffer_full(); + retries--) + ; + } + if (!kbc_output_buffer_full()) { + printk(BIOS_ERR, + "Could not send keyboard command %02x\n", + command); + return 0; + } + regval = inb(KBD_DATA); + --resend; + } while (regval == KBD_REPLY_RESEND && resend > 0); + + return regval; +} + +uint8_t pc_keyboard_init(uint8_t probe_aux) +{ + u8 retries; + u8 regval; + enum cb_err err; + uint8_t aux_dev_detected; + + if (!CONFIG_DRIVERS_PS2_KEYBOARD) + return 0; + + if (acpi_is_wakeup_s3()) + return 0; + + printk(BIOS_DEBUG, "Keyboard init...\n"); + + /* Run a keyboard controller self-test */ + err = kbc_self_test(probe_aux, &aux_dev_detected); + /* Ignore iterface failure as it's non-fatal. */ + if (err != CB_SUCCESS && err != CB_KBD_INTERFACE_FAILURE) + return 0; + + /* Enable keyboard interface - No IRQ */ + if (!kbc_input_buffer_empty()) + return 0; + outb(0x60, KBD_COMMAND); + if (!kbc_input_buffer_empty()) + return 0; + outb(0x20, KBD_DATA); /* send cmd: enable keyboard */ + if (!kbc_input_buffer_empty()) { + printk(BIOS_INFO, "Timeout while enabling keyboard\n"); + return 0; + } + + /* clean up any junk that might have been in the keyboard */ + if (!kbc_cleanup_buffers()) + return 0; + + /* reset keyboard and self test (keyboard side) */ + regval = send_keyboard(0xFF); + if (regval == KBD_REPLY_RESEND) { + /* keeps sending RESENDs, probably no keyboard. */ + printk(BIOS_INFO, "No PS/2 keyboard detected.\n"); + return 0; + } + + if (regval != KBD_REPLY_ACK) { + printk(BIOS_ERR, "Keyboard reset failed ACK: 0x%x\n", regval); + return 0; + } + + /* the reset command takes some time, so wait a little longer */ + for (retries = 9; retries && !kbc_output_buffer_full(); retries--) + ; + + if (!kbc_output_buffer_full()) { + printk(BIOS_ERR, "Timeout waiting for keyboard after reset.\n"); + return 0; + } + + regval = inb(KBD_DATA); + if (regval != 0xAA) { + printk(BIOS_ERR, "Keyboard reset selftest failed: 0x%x\n", + regval); + return 0; + } + + /* + * The following set scancode stuff is what normal BIOS do. It could be + * argued that coreboot shouldn't set the scan code..... + */ + + /* disable the keyboard */ + regval = send_keyboard(0xF5); + if (regval != KBD_REPLY_ACK) { + printk(BIOS_ERR, "Keyboard disable failed ACK: 0x%x\n", regval); + return 0; + } + + /* Set scancode command */ + regval = send_keyboard(0xF0); + if (regval != KBD_REPLY_ACK) { + printk(BIOS_ERR, "Keyboard set scancode cmd failed ACK: 0x%x\n", + regval); + return 0; + } + /* Set scancode mode 2 */ + regval = send_keyboard(0x02); + if (regval != KBD_REPLY_ACK) { + printk(BIOS_ERR, + "Keyboard set scancode mode failed ACK: 0x%x\n", regval); + return 0; + } + + /* All is well - enable keyboard interface */ + if (!kbc_input_buffer_empty()) + return 0; + outb(0x60, KBD_COMMAND); + if (!kbc_input_buffer_empty()) + return 0; + outb(0x65, KBD_DATA); /* send cmd: enable keyboard and IRQ 1 */ + if (!kbc_input_buffer_empty()) { + printk(BIOS_ERR, "Timeout during keyboard enable\n"); + return 0; + } + + /* enable the keyboard */ + regval = send_keyboard(0xF4); + if (regval != KBD_REPLY_ACK) { + printk(BIOS_ERR, "Keyboard enable failed ACK: 0x%x\n", regval); + return 0; + } + + printk(BIOS_DEBUG, "PS/2 keyboard initialized on primary channel\n"); + + return aux_dev_detected; +} + +/* + * Support PS/2 mode - oddball SIOs(KBC) need this setup + * Not well documented. Google - 0xcb keyboard controller + * This is called before pc_keyboard_init(). + */ +void set_kbc_ps2_mode(void) +{ + enum cb_err err; + + /* Run a keyboard controller self-test */ + err = kbc_self_test(0, NULL); + /* Ignore iterface failure as it's non-fatal. */ + if (err != CB_SUCCESS && err != CB_KBD_INTERFACE_FAILURE) + return; + + /* Support PS/2 mode */ + if (!kbc_input_buffer_empty()) + return; + outb(0xcb, KBD_COMMAND); + + if (!kbc_input_buffer_empty()) + return; + outb(0x01, KBD_DATA); + + kbc_cleanup_buffers(); +} diff --git a/src/drivers/pc80/pc/ps2_controller.asl b/src/drivers/pc80/pc/ps2_controller.asl new file mode 100644 index 0000000000..a4984cfdb9 --- /dev/null +++ b/src/drivers/pc80/pc/ps2_controller.asl @@ -0,0 +1,46 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2013 Vladimir Serbinenko + * + * 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; version 2 of + * the License. + * + * 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 + * GNU General Public License for more details. + */ + Device (PS2K) // Keyboard + { + Name(_HID, EISAID("PNP0303")) + Name(_CID, EISAID("PNP030B")) + + Name(_CRS, ResourceTemplate() + { + IO (Decode16, 0x60, 0x60, 0x01, 0x01) + IO (Decode16, 0x64, 0x64, 0x01, 0x01) + IRQ (Edge, ActiveHigh, Exclusive) { 0x01 } // IRQ 1 + }) + + Method (_STA, 0) + { + Return (0xf) + } + } + + Device (PS2M) // Mouse + { + Name(_HID, EISAID("PNP0F13")) + Name(_CRS, ResourceTemplate() + { + IRQ (Edge, ActiveHigh, Exclusive) { 0x0c } // IRQ 12 + }) + + Method(_STA, 0) + { + Return (0xf) + } + } diff --git a/src/drivers/pc80/pc/spkmodem.c b/src/drivers/pc80/pc/spkmodem.c new file mode 100644 index 0000000000..df66f6fa62 --- /dev/null +++ b/src/drivers/pc80/pc/spkmodem.c @@ -0,0 +1,126 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 Vladimir Serbinenko + * + * 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 + * GNU General Public License for more details. + */ + +#include <console/console.h> +#include <arch/io.h> +#include <console/spkmodem.h> +#include <cpu/x86/tsc.h> + +#define SPEAKER_PIT_FREQUENCY 0x1234dd + + +enum { + PIT_COUNTER_0 = 0x40, + PIT_COUNTER_1 = 0x41, + PIT_COUNTER_2 = 0x42, + PIT_CTRL = 0x43, + PIT_SPEAKER_PORT = 0x61, +}; + + +enum { + PIT_SPK_TMR2 = 0x01, + PIT_SPK_DATA = 0x02, + PIT_SPK_TMR2_LATCH = 0x20 +}; + +enum { + PIT_CTRL_SELECT_MASK = 0xc0, + PIT_CTRL_SELECT_0 = 0x00, + PIT_CTRL_SELECT_1 = 0x40, + PIT_CTRL_SELECT_2 = 0x80, + + PIT_CTRL_READLOAD_MASK = 0x30, + PIT_CTRL_COUNTER_LATCH = 0x00, + PIT_CTRL_READLOAD_LSB = 0x10, + PIT_CTRL_READLOAD_MSB = 0x20, + PIT_CTRL_READLOAD_WORD = 0x30, + + PIT_CTRL_MODE_MASK = 0x0e, + PIT_CTRL_INTR_ON_TERM = 0x00, + PIT_CTRL_PROGR_ONE_SHOT = 0x02, + + PIT_CTRL_RATE_GEN = 0x04, + + PIT_CTRL_SQUAREWAVE_GEN = 0x06, + PIT_CTRL_SOFTSTROBE = 0x08, + + PIT_CTRL_HARDSTROBE = 0x0a, + + + PIT_CTRL_COUNT_MASK = 0x01, + PIT_CTRL_COUNT_BINARY = 0x00, + PIT_CTRL_COUNT_BCD = 0x01 +}; + + +static void +make_tone(uint16_t freq_count, unsigned int duration) +{ + outb(PIT_CTRL_SELECT_2 + | PIT_CTRL_READLOAD_WORD + | PIT_CTRL_SQUAREWAVE_GEN + | PIT_CTRL_COUNT_BINARY, PIT_CTRL); + + outb(freq_count & 0xff, PIT_COUNTER_2); + + outb((freq_count >> 8) & 0xff, PIT_COUNTER_2); + + outb(inb(PIT_SPEAKER_PORT) + | PIT_SPK_TMR2 | PIT_SPK_DATA, + PIT_SPEAKER_PORT); + + for (; duration; duration--) { + unsigned short counter, previous_counter = 0xffff; + + while (1) { + counter = inb(PIT_COUNTER_2); + counter |= ((uint16_t)inb(PIT_COUNTER_2)) << 8; + if (counter > previous_counter) { + previous_counter = counter; + break; + } + previous_counter = counter; + } + } +} + +void spkmodem_tx_byte(unsigned char c) +{ + int i; + + make_tone(SPEAKER_PIT_FREQUENCY / 200, 4); + for (i = 7; i >= 0; i--) { + if ((c >> i) & 1) + make_tone(SPEAKER_PIT_FREQUENCY / 2000, 20); + else + make_tone(SPEAKER_PIT_FREQUENCY / 4000, 40); + make_tone(SPEAKER_PIT_FREQUENCY / 1000, 10); + } + make_tone(SPEAKER_PIT_FREQUENCY / 200, 0); +} + +void spkmodem_init(void) +{ + /* Some cards need time to come online. + * Output some message to get it started. + */ + spkmodem_tx_byte('S'); + spkmodem_tx_byte('P'); + spkmodem_tx_byte('K'); + spkmodem_tx_byte('\r'); + spkmodem_tx_byte('\n'); +} diff --git a/src/drivers/pc80/pc/udelay_io.c b/src/drivers/pc80/pc/udelay_io.c new file mode 100644 index 0000000000..3305e4c3a3 --- /dev/null +++ b/src/drivers/pc80/pc/udelay_io.c @@ -0,0 +1,14 @@ +#include <arch/io.h> +#include <delay.h> + +void init_timer(void) +{ +} + +void udelay(unsigned usecs) +{ + int i; + + for (i = 0; i < usecs; i++) + inb(0x80); +} |