aboutsummaryrefslogtreecommitdiff
path: root/src/drivers/pc80/pc
diff options
context:
space:
mode:
authorStefan Reinauer <stefan.reinauer@coreboot.org>2016-03-11 20:22:28 -0800
committerStefan Reinauer <stefan.reinauer@coreboot.org>2016-04-19 18:34:18 +0200
commit86ddd732bd82b84724883b1f1579e18790611fe5 (patch)
treee52c4c88ceb7e14fd415031c56997d8c4054116e /src/drivers/pc80/pc
parent3812597c668b795d4fc85578021e501f2a56f6b9 (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/Kconfig18
-rw-r--r--src/drivers/pc80/pc/Makefile.inc12
-rw-r--r--src/drivers/pc80/pc/i8254.c51
-rw-r--r--src/drivers/pc80/pc/i8259.c139
-rw-r--r--src/drivers/pc80/pc/isa-dma.c44
-rw-r--r--src/drivers/pc80/pc/keyboard.c387
-rw-r--r--src/drivers/pc80/pc/ps2_controller.asl46
-rw-r--r--src/drivers/pc80/pc/spkmodem.c126
-rw-r--r--src/drivers/pc80/pc/udelay_io.c14
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);
+}