Move top level pc80 directory to drivers/
There is no reason for this to be a top level directory. Some stuff from lib/ should also be moved to drivers/ Change-Id: I3c2d2e127f7215eadead029cfc7442c22b26814a Signed-off-by: Stefan Reinauer <reinauer@google.com> Reviewed-on: http://review.coreboot.org/939 Tested-by: build bot (Jenkins) Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
This commit is contained in:
@@ -26,3 +26,4 @@ subdirs-y += oxford
|
||||
subdirs-y += sil
|
||||
subdirs-y += trident
|
||||
subdirs-y += ics
|
||||
subdirs-$(CONFIG_ARCH_X86) += pc80
|
||||
|
16
src/drivers/pc80/Makefile.inc
Normal file
16
src/drivers/pc80/Makefile.inc
Normal file
@@ -0,0 +1,16 @@
|
||||
ramstage-y += mc146818rtc.c
|
||||
ramstage-y += isa-dma.c
|
||||
ramstage-y += i8254.c
|
||||
ramstage-y += i8259.c
|
||||
ramstage-$(CONFIG_UDELAY_IO) += udelay_io.c
|
||||
ramstage-y += keyboard.c
|
||||
romstage-$(CONFIG_TPM) += tpm.c
|
||||
|
||||
romstage-$(CONFIG_USE_OPTION_TABLE) += mc146818rtc_early.c
|
||||
subdirs-y += vga
|
||||
|
||||
cbfs-files-$(CONFIG_HAVE_CMOS_DEFAULT) += cmos.default
|
||||
cmos.default-file = $(CONFIG_CMOS_DEFAULT_FILE)
|
||||
cmos.default-type = 0xaa
|
||||
|
||||
smm-y += mc146818rtc.c
|
55
src/drivers/pc80/i8254.c
Normal file
55
src/drivers/pc80/i8254.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#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 defined(CONFIG_UDELAY_TIMER2) && 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
|
141
src/drivers/pc80/i8259.c
Normal file
141
src/drivers/pc80/i8259.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <arch/io.h>
|
||||
#include <pc80/i8259.h>
|
||||
#include <console/console.h>
|
||||
|
||||
#define MASTER_PIC_ICW1 0x20
|
||||
#define SLAVE_PIC_ICW1 0xa0
|
||||
#define ICW_SELECT (1 << 4)
|
||||
#define OCW_SELECT (0 << 4)
|
||||
#define ADI (1 << 2)
|
||||
#define SNGL (1 << 1)
|
||||
#define IC4 (1 << 0)
|
||||
|
||||
#define MASTER_PIC_ICW2 0x21
|
||||
#define SLAVE_PIC_ICW2 0xa1
|
||||
#define INT_VECTOR_MASTER 0x20
|
||||
#define IRQ0 0x00
|
||||
#define IRQ1 0x01
|
||||
#define INT_VECTOR_SLAVE 0x28
|
||||
#define IRQ8 0x00
|
||||
#define IRQ9 0x01
|
||||
|
||||
#define MASTER_PIC_ICW3 0x21
|
||||
#define CASCADED_PIC (1 << 2)
|
||||
|
||||
#define MASTER_PIC_ICW4 0x21
|
||||
#define SLAVE_PIC_ICW4 0xa1
|
||||
#define MICROPROCESSOR_MODE (1 << 0)
|
||||
|
||||
#define SLAVE_PIC_ICW3 0xa1
|
||||
#define SLAVE_ID 0x02
|
||||
|
||||
#define MASTER_PIC_OCW1 0x21
|
||||
#define SLAVE_PIC_OCW1 0xa1
|
||||
#define IRQ2 (1 << 2)
|
||||
#define ALL_IRQS 0xff
|
||||
|
||||
#define ELCR1 0x4d0
|
||||
#define ELCR2 0x4d1
|
||||
|
||||
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 alife.
|
||||
*/
|
||||
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);
|
||||
|
||||
printk(BIOS_SPEW, "%s: current interrupts are 0x%x\n", __func__, int_bits);
|
||||
if (is_level_triggered)
|
||||
int_bits |= (1 << int_num);
|
||||
else
|
||||
int_bits &= ~(1 << int_num);
|
||||
|
||||
/* Write new values */
|
||||
printk(BIOS_SPEW, "%s: try to set interrupts 0x%x\n", __func__, int_bits);
|
||||
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 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
|
||||
}
|
||||
|
||||
|
44
src/drivers/pc80/isa-dma.c
Normal file
44
src/drivers/pc80/isa-dma.c
Normal file
@@ -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);
|
||||
}
|
306
src/drivers/pc80/keyboard.c
Normal file
306
src/drivers/pc80/keyboard.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
#include <console/console.h>
|
||||
#include <pc80/keyboard.h>
|
||||
#include <device/device.h>
|
||||
#include <arch/io.h>
|
||||
#include <delay.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_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 int kbc_self_test(void)
|
||||
{
|
||||
u8 self_test;
|
||||
|
||||
/* Clean up any junk that might have been in the KBC.
|
||||
* Both input and output buffers must be empty.
|
||||
*/
|
||||
if (!kbc_cleanup_buffers())
|
||||
return 0;
|
||||
|
||||
/* 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 0;
|
||||
}
|
||||
|
||||
/* 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 0;
|
||||
}
|
||||
|
||||
/* 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 0;
|
||||
}
|
||||
|
||||
/* 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 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void pc_keyboard_init(struct pc_keyboard *keyboard)
|
||||
{
|
||||
u8 retries;
|
||||
u8 regval;
|
||||
if (!CONFIG_DRIVERS_PS2_KEYBOARD)
|
||||
return;
|
||||
printk(BIOS_DEBUG, "Keyboard init...\n");
|
||||
|
||||
/* Run a keyboard controller self-test */
|
||||
if (!kbc_self_test())
|
||||
return;
|
||||
|
||||
/* Enable keyboard interface - No IRQ */
|
||||
if (!kbc_input_buffer_empty()) return;
|
||||
outb(0x60, KBD_COMMAND);
|
||||
if (!kbc_input_buffer_empty()) return;
|
||||
outb(0x20, KBD_DATA); /* send cmd: enable keyboard */
|
||||
if (!kbc_input_buffer_empty()) {
|
||||
printk(BIOS_INFO, "Timeout while enabling keyboard\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* clean up any junk that might have been in the keyboard */
|
||||
if (!kbc_cleanup_buffers()) return;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (regval != KBD_REPLY_ACK) {
|
||||
printk(BIOS_ERR, "Keyboard reset failed ACK: 0x%x\n", regval);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
regval = inb(KBD_DATA);
|
||||
if (regval != 0xAA) {
|
||||
printk(BIOS_ERR, "Keyboard reset selftest failed: 0x%x\n", regval);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* All is well - enable keyboard interface */
|
||||
if (!kbc_input_buffer_empty()) return;
|
||||
outb(0x60, KBD_COMMAND);
|
||||
if (!kbc_input_buffer_empty()) return;
|
||||
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;
|
||||
}
|
||||
|
||||
/* enable the keyboard */
|
||||
regval = send_keyboard(0xF4);
|
||||
if (regval != KBD_REPLY_ACK) {
|
||||
printk(BIOS_ERR, "Keyboard enable failed ACK: 0x%x\n", regval);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
/* Run a keyboard controller self-test */
|
||||
if (!kbc_self_test())
|
||||
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();
|
||||
}
|
340
src/drivers/pc80/mc146818rtc.c
Normal file
340
src/drivers/pc80/mc146818rtc.c
Normal file
@@ -0,0 +1,340 @@
|
||||
#include <stdint.h>
|
||||
#include <console/console.h>
|
||||
#include <pc80/mc146818rtc.h>
|
||||
#include <boot/coreboot_tables.h>
|
||||
#include <string.h>
|
||||
#if CONFIG_USE_OPTION_TABLE
|
||||
#include "option_table.h"
|
||||
#include <cbfs.h>
|
||||
#endif
|
||||
|
||||
/* control registers - Moto names
|
||||
*/
|
||||
#define RTC_REG_A 10
|
||||
#define RTC_REG_B 11
|
||||
#define RTC_REG_C 12
|
||||
#define RTC_REG_D 13
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* register details
|
||||
**********************************************************************/
|
||||
#define RTC_FREQ_SELECT RTC_REG_A
|
||||
|
||||
/* update-in-progress - set to "1" 244 microsecs before RTC goes off the bus,
|
||||
* reset after update (may take 1.984ms @ 32768Hz RefClock) is complete,
|
||||
* totalling to a max high interval of 2.228 ms.
|
||||
*/
|
||||
# define RTC_UIP 0x80
|
||||
# define RTC_DIV_CTL 0x70
|
||||
/* divider control: refclock values 4.194 / 1.049 MHz / 32.768 kHz */
|
||||
# define RTC_REF_CLCK_4MHZ 0x00
|
||||
# define RTC_REF_CLCK_1MHZ 0x10
|
||||
# define RTC_REF_CLCK_32KHZ 0x20
|
||||
/* 2 values for divider stage reset, others for "testing purposes only" */
|
||||
# define RTC_DIV_RESET1 0x60
|
||||
# define RTC_DIV_RESET2 0x70
|
||||
/* Periodic intr. / Square wave rate select. 0=none, 1=32.8kHz,... 15=2Hz */
|
||||
# define RTC_RATE_SELECT 0x0F
|
||||
# define RTC_RATE_NONE 0x00
|
||||
# define RTC_RATE_32786HZ 0x01
|
||||
# define RTC_RATE_16384HZ 0x02
|
||||
# define RTC_RATE_8192HZ 0x03
|
||||
# define RTC_RATE_4096HZ 0x04
|
||||
# define RTC_RATE_2048HZ 0x05
|
||||
# define RTC_RATE_1024HZ 0x06
|
||||
# define RTC_RATE_512HZ 0x07
|
||||
# define RTC_RATE_256HZ 0x08
|
||||
# define RTC_RATE_128HZ 0x09
|
||||
# define RTC_RATE_64HZ 0x0a
|
||||
# define RTC_RATE_32HZ 0x0b
|
||||
# define RTC_RATE_16HZ 0x0c
|
||||
# define RTC_RATE_8HZ 0x0d
|
||||
# define RTC_RATE_4HZ 0x0e
|
||||
# define RTC_RATE_2HZ 0x0f
|
||||
|
||||
/**********************************************************************/
|
||||
#define RTC_CONTROL RTC_REG_B
|
||||
# define RTC_SET 0x80 /* disable updates for clock setting */
|
||||
# define RTC_PIE 0x40 /* periodic interrupt enable */
|
||||
# define RTC_AIE 0x20 /* alarm interrupt enable */
|
||||
# define RTC_UIE 0x10 /* update-finished interrupt enable */
|
||||
# define RTC_SQWE 0x08 /* enable square-wave output */
|
||||
# define RTC_DM_BINARY 0x04 /* all time/date values are BCD if clear */
|
||||
# define RTC_24H 0x02 /* 24 hour mode - else hours bit 7 means pm */
|
||||
# define RTC_DST_EN 0x01 /* auto switch DST - works f. USA only */
|
||||
|
||||
/**********************************************************************/
|
||||
#define RTC_INTR_FLAGS RTC_REG_C
|
||||
/* caution - cleared by read */
|
||||
# define RTC_IRQF 0x80 /* any of the following 3 is active */
|
||||
# define RTC_PF 0x40
|
||||
# define RTC_AF 0x20
|
||||
# define RTC_UF 0x10
|
||||
|
||||
/**********************************************************************/
|
||||
#define RTC_VALID RTC_REG_D
|
||||
# define RTC_VRT 0x80 /* valid RAM and time */
|
||||
/**********************************************************************/
|
||||
|
||||
#if CONFIG_USE_OPTION_TABLE
|
||||
static int rtc_checksum_valid(int range_start, int range_end, int cks_loc)
|
||||
{
|
||||
int i;
|
||||
u16 sum, old_sum;
|
||||
sum = 0;
|
||||
for(i = range_start; i <= range_end; i++) {
|
||||
sum += cmos_read(i);
|
||||
}
|
||||
old_sum = ((cmos_read(cks_loc)<<8) | cmos_read(cks_loc+1))&0x0ffff;
|
||||
return sum == old_sum;
|
||||
}
|
||||
|
||||
static void rtc_set_checksum(int range_start, int range_end, int cks_loc)
|
||||
{
|
||||
int i;
|
||||
u16 sum;
|
||||
sum = 0;
|
||||
for(i = range_start; i <= range_end; i++) {
|
||||
sum += cmos_read(i);
|
||||
}
|
||||
cmos_write(((sum >> 8) & 0x0ff), cks_loc);
|
||||
cmos_write(((sum >> 0) & 0x0ff), cks_loc+1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_ARCH_X86
|
||||
#define RTC_CONTROL_DEFAULT (RTC_24H)
|
||||
#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ)
|
||||
#else
|
||||
#if CONFIG_ARCH_ALPHA
|
||||
#define RTC_CONTROL_DEFAULT (RTC_SQWE | RTC_24H)
|
||||
#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void rtc_init(int invalid)
|
||||
{
|
||||
#if CONFIG_USE_OPTION_TABLE
|
||||
unsigned char x;
|
||||
int cmos_invalid, checksum_invalid;
|
||||
#endif
|
||||
|
||||
printk(BIOS_DEBUG, "RTC Init\n");
|
||||
|
||||
#if CONFIG_USE_OPTION_TABLE
|
||||
/* See if there has been a CMOS power problem. */
|
||||
x = cmos_read(RTC_VALID);
|
||||
cmos_invalid = !(x & RTC_VRT);
|
||||
|
||||
/* See if there is a CMOS checksum error */
|
||||
checksum_invalid = !rtc_checksum_valid(PC_CKS_RANGE_START,
|
||||
PC_CKS_RANGE_END,PC_CKS_LOC);
|
||||
|
||||
#define CLEAR_CMOS 0
|
||||
if (invalid || cmos_invalid || checksum_invalid) {
|
||||
printk(BIOS_WARNING, "RTC:%s%s%s%s\n",
|
||||
invalid?" Clear requested":"",
|
||||
cmos_invalid?" Power Problem":"",
|
||||
checksum_invalid?" Checksum invalid":"",
|
||||
CLEAR_CMOS?" zeroing cmos":"");
|
||||
#if CLEAR_CMOS
|
||||
cmos_write(0, 0x01);
|
||||
cmos_write(0, 0x03);
|
||||
cmos_write(0, 0x05);
|
||||
for(i = 10; i < 48; i++) {
|
||||
cmos_write(0, i);
|
||||
}
|
||||
|
||||
if (cmos_invalid) {
|
||||
/* Now setup a default date of Sat 1 January 2000 */
|
||||
cmos_write(0, 0x00); /* seconds */
|
||||
cmos_write(0, 0x02); /* minutes */
|
||||
cmos_write(1, 0x04); /* hours */
|
||||
cmos_write(7, 0x06); /* day of week */
|
||||
cmos_write(1, 0x07); /* day of month */
|
||||
cmos_write(1, 0x08); /* month */
|
||||
cmos_write(0, 0x09); /* year */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Setup the real time clock */
|
||||
cmos_write(RTC_CONTROL_DEFAULT, RTC_CONTROL);
|
||||
/* Setup the frequency it operates at */
|
||||
cmos_write(RTC_FREQ_SELECT_DEFAULT, RTC_FREQ_SELECT);
|
||||
|
||||
#if CONFIG_USE_OPTION_TABLE
|
||||
/* See if there is a LB CMOS checksum error */
|
||||
checksum_invalid = !rtc_checksum_valid(LB_CKS_RANGE_START,
|
||||
LB_CKS_RANGE_END,LB_CKS_LOC);
|
||||
if(checksum_invalid)
|
||||
printk(BIOS_DEBUG, "RTC: coreboot checksum invalid\n");
|
||||
|
||||
/* Make certain we have a valid checksum */
|
||||
rtc_set_checksum(PC_CKS_RANGE_START,
|
||||
PC_CKS_RANGE_END,PC_CKS_LOC);
|
||||
#endif
|
||||
|
||||
/* Clear any pending interrupts */
|
||||
(void) cmos_read(RTC_INTR_FLAGS);
|
||||
}
|
||||
|
||||
|
||||
#if CONFIG_USE_OPTION_TABLE
|
||||
/* This routine returns the value of the requested bits
|
||||
input bit = bit count from the beginning of the cmos image
|
||||
length = number of bits to include in the value
|
||||
ret = a character pointer to where the value is to be returned
|
||||
output the value placed in ret
|
||||
returns 0 = successful, -1 = an error occurred
|
||||
*/
|
||||
static int get_cmos_value(unsigned long bit, unsigned long length, void *vret)
|
||||
{
|
||||
unsigned char *ret;
|
||||
unsigned long byte,byte_bit;
|
||||
unsigned long i;
|
||||
unsigned char uchar;
|
||||
|
||||
/* The table is checked when it is built to ensure all
|
||||
values are valid. */
|
||||
ret = vret;
|
||||
byte=bit/8; /* find the byte where the data starts */
|
||||
byte_bit=bit%8; /* find the bit in the byte where the data starts */
|
||||
if(length<9) { /* one byte or less */
|
||||
uchar = cmos_read(byte); /* load the byte */
|
||||
uchar >>= byte_bit; /* shift the bits to byte align */
|
||||
/* clear unspecified bits */
|
||||
ret[0] = uchar & ((1 << length) -1);
|
||||
}
|
||||
else { /* more that one byte so transfer the whole bytes */
|
||||
for(i=0;length;i++,length-=8,byte++) {
|
||||
/* load the byte */
|
||||
ret[i]=cmos_read(byte);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_option(void *dest, const char *name)
|
||||
{
|
||||
struct cmos_option_table *ct;
|
||||
struct cmos_entries *ce;
|
||||
size_t namelen;
|
||||
int found=0;
|
||||
|
||||
/* Figure out how long name is */
|
||||
namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
|
||||
|
||||
/* find the requested entry record */
|
||||
ct=cbfs_find_file("cmos_layout.bin", CBFS_COMPONENT_CMOS_LAYOUT);
|
||||
if (!ct) {
|
||||
printk(BIOS_ERR, "RTC: cmos_layout.bin could not be found. "
|
||||
"Options are disabled\n");
|
||||
return(-2);
|
||||
}
|
||||
ce=(struct cmos_entries*)((unsigned char *)ct + ct->header_length);
|
||||
for(;ce->tag==LB_TAG_OPTION;
|
||||
ce=(struct cmos_entries*)((unsigned char *)ce + ce->size)) {
|
||||
if (memcmp(ce->name, name, namelen) == 0) {
|
||||
found=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name);
|
||||
return(-2);
|
||||
}
|
||||
|
||||
if(get_cmos_value(ce->bit, ce->length, dest))
|
||||
return(-3);
|
||||
if(!rtc_checksum_valid(LB_CKS_RANGE_START,
|
||||
LB_CKS_RANGE_END,LB_CKS_LOC))
|
||||
return(-4);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int set_cmos_value(unsigned long bit, unsigned long length, void *vret)
|
||||
{
|
||||
unsigned char *ret;
|
||||
unsigned long byte,byte_bit;
|
||||
unsigned long i;
|
||||
unsigned char uchar, mask;
|
||||
unsigned int chksum_update_needed = 0;
|
||||
|
||||
ret = vret;
|
||||
byte = bit / 8; /* find the byte where the data starts */
|
||||
byte_bit = bit % 8; /* find the bit in the byte where the data starts */
|
||||
if(length <= 8) { /* one byte or less */
|
||||
mask = (1 << length) - 1;
|
||||
mask <<= byte_bit;
|
||||
|
||||
uchar = cmos_read(byte);
|
||||
uchar &= ~mask;
|
||||
uchar |= (ret[0] << byte_bit);
|
||||
cmos_write(uchar, byte);
|
||||
if (byte >= LB_CKS_RANGE_START && byte <= LB_CKS_RANGE_END)
|
||||
chksum_update_needed = 1;
|
||||
} else { /* more that one byte so transfer the whole bytes */
|
||||
if (byte_bit || length % 8)
|
||||
return -1;
|
||||
|
||||
for(i=0; length; i++, length-=8, byte++)
|
||||
cmos_write(ret[i], byte);
|
||||
if (byte >= LB_CKS_RANGE_START && byte <= LB_CKS_RANGE_END)
|
||||
chksum_update_needed = 1;
|
||||
}
|
||||
|
||||
if (chksum_update_needed) {
|
||||
rtc_set_checksum(LB_CKS_RANGE_START,
|
||||
LB_CKS_RANGE_END,LB_CKS_LOC);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int set_option(const char *name, void *value)
|
||||
{
|
||||
struct cmos_option_table *ct;
|
||||
struct cmos_entries *ce;
|
||||
unsigned long length;
|
||||
size_t namelen;
|
||||
int found=0;
|
||||
|
||||
/* Figure out how long name is */
|
||||
namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
|
||||
|
||||
/* find the requested entry record */
|
||||
ct=cbfs_find_file("cmos_layout.bin", CBFS_COMPONENT_CMOS_LAYOUT);
|
||||
if (!ct) {
|
||||
printk(BIOS_ERR, "cmos_layout.bin could not be found. Options are disabled\n");
|
||||
return(-2);
|
||||
}
|
||||
ce=(struct cmos_entries*)((unsigned char *)ct + ct->header_length);
|
||||
for(;ce->tag==LB_TAG_OPTION;
|
||||
ce=(struct cmos_entries*)((unsigned char *)ce + ce->size)) {
|
||||
if (memcmp(ce->name, name, namelen) == 0) {
|
||||
found=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name);
|
||||
return(-2);
|
||||
}
|
||||
|
||||
length = ce->length;
|
||||
if (ce->config == 's') {
|
||||
length = MAX(strlen((const char *)value) * 8, ce->length - 8);
|
||||
/* make sure the string is null terminated */
|
||||
if ((set_cmos_value(ce->bit + ce->length - 8, 8, &(u8[]){0})))
|
||||
return (-3);
|
||||
}
|
||||
|
||||
if ((set_cmos_value(ce->bit, length, value)))
|
||||
return (-3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_USE_OPTION_TABLE */
|
104
src/drivers/pc80/mc146818rtc_early.c
Normal file
104
src/drivers/pc80/mc146818rtc_early.c
Normal file
@@ -0,0 +1,104 @@
|
||||
#include <stdint.h>
|
||||
#include <pc80/mc146818rtc.h>
|
||||
#include <fallback.h>
|
||||
#if CONFIG_USE_OPTION_TABLE
|
||||
#include "option_table.h"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_MAX_REBOOT_CNT
|
||||
#error "CONFIG_MAX_REBOOT_CNT not defined"
|
||||
#endif
|
||||
#if CONFIG_MAX_REBOOT_CNT > 15
|
||||
#error "CONFIG_MAX_REBOOT_CNT too high"
|
||||
#endif
|
||||
|
||||
static int cmos_error(void)
|
||||
{
|
||||
unsigned char reg_d;
|
||||
/* See if the cmos error condition has been flagged */
|
||||
reg_d = cmos_read(RTC_REG_D);
|
||||
return (reg_d & RTC_VRT) == 0;
|
||||
}
|
||||
|
||||
static int cmos_chksum_valid(void)
|
||||
{
|
||||
#if CONFIG_USE_OPTION_TABLE
|
||||
unsigned char addr;
|
||||
u16 sum, old_sum;
|
||||
sum = 0;
|
||||
/* Compute the cmos checksum */
|
||||
for(addr = LB_CKS_RANGE_START; addr <= LB_CKS_RANGE_END; addr++) {
|
||||
sum += cmos_read(addr);
|
||||
}
|
||||
|
||||
/* Read the stored checksum */
|
||||
old_sum = cmos_read(LB_CKS_LOC) << 8;
|
||||
old_sum |= cmos_read(LB_CKS_LOC+1);
|
||||
|
||||
return sum == old_sum;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static inline int last_boot_normal(void)
|
||||
{
|
||||
unsigned char byte;
|
||||
byte = cmos_read(RTC_BOOT_BYTE);
|
||||
return (byte & (1 << 1));
|
||||
}
|
||||
|
||||
static inline int do_normal_boot(void)
|
||||
{
|
||||
unsigned char byte;
|
||||
|
||||
if (cmos_error() || !cmos_chksum_valid()) {
|
||||
/* There are no impossible values, no checksums so just
|
||||
* trust whatever value we have in the the cmos,
|
||||
* but clear the fallback bit.
|
||||
*/
|
||||
byte = cmos_read(RTC_BOOT_BYTE);
|
||||
byte &= 0x0c;
|
||||
byte |= CONFIG_MAX_REBOOT_CNT << 4;
|
||||
cmos_write(byte, RTC_BOOT_BYTE);
|
||||
}
|
||||
|
||||
/* The RTC_BOOT_BYTE is now o.k. see where to go. */
|
||||
byte = cmos_read(RTC_BOOT_BYTE);
|
||||
|
||||
/* Are we in normal mode? */
|
||||
if (byte & 1) {
|
||||
byte &= 0x0f; /* yes, clear the boot count */
|
||||
}
|
||||
|
||||
/* Properly set the last boot flag */
|
||||
byte &= 0xfc;
|
||||
if ((byte >> 4) < CONFIG_MAX_REBOOT_CNT) {
|
||||
byte |= (1<<1);
|
||||
}
|
||||
|
||||
/* Are we already at the max count? */
|
||||
if ((byte >> 4) < CONFIG_MAX_REBOOT_CNT) {
|
||||
byte += 1 << 4; /* No, add 1 to the count */
|
||||
}
|
||||
else {
|
||||
byte &= 0xfc; /* Yes, put in fallback mode */
|
||||
}
|
||||
|
||||
/* Save the boot byte */
|
||||
cmos_write(byte, RTC_BOOT_BYTE);
|
||||
|
||||
return (byte & (1<<1));
|
||||
}
|
||||
|
||||
unsigned read_option_lowlevel(unsigned start, unsigned size, unsigned def)
|
||||
{
|
||||
#if CONFIG_USE_OPTION_TABLE
|
||||
unsigned byte;
|
||||
byte = cmos_read(start/8);
|
||||
return (byte >> (start & 7U)) & ((1U << size) - 1U);
|
||||
#else
|
||||
return def;
|
||||
#endif
|
||||
}
|
548
src/drivers/pc80/tpm.c
Normal file
548
src/drivers/pc80/tpm.c
Normal file
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code in this file has been heavily based on the article "Writing a TPM
|
||||
* Device Driver" published on http://ptgmedia.pearsoncmg.com and the
|
||||
* submission by Stefan Berger on Qemu-devel mailing list.
|
||||
*
|
||||
* One principal difference is that in the simplest config the other than 0
|
||||
* TPM localities do not get mapped by some devices (for instance, by
|
||||
* Infineon slb9635), so this driver provides access to locality 0 only.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <delay.h>
|
||||
#include <arch/io.h>
|
||||
#include <arch/byteorder.h>
|
||||
#include <console/console.h>
|
||||
#include <pc80/tpm.h>
|
||||
#include <cpu/x86/car.h>
|
||||
|
||||
#define PREFIX "lpc_tpm: "
|
||||
|
||||
/* coreboot wrapper for TPM driver (start) */
|
||||
#define TPM_DEBUG(fmt, args...) \
|
||||
if (CONFIG_DEBUG_TPM) { \
|
||||
printk(BIOS_DEBUG, PREFIX); \
|
||||
printk(BIOS_DEBUG, fmt , ##args); \
|
||||
}
|
||||
#define printf(x...) printk(BIOS_ERR, x)
|
||||
|
||||
#define min(a,b) MIN(a,b)
|
||||
#define max(a,b) MAX(a,b)
|
||||
#define readb(_a) (*(volatile unsigned char *) (_a))
|
||||
#define writeb(_v, _a) (*(volatile unsigned char *) (_a) = (_v))
|
||||
#define readl(_a) (*(volatile unsigned long *) (_a))
|
||||
#define writel(_v, _a) (*(volatile unsigned long *) (_a) = (_v))
|
||||
/* coreboot wrapper for TPM driver (end) */
|
||||
|
||||
#ifndef CONFIG_TPM_TIS_BASE_ADDRESS
|
||||
/* Base TPM address standard for x86 systems */
|
||||
#define CONFIG_TPM_TIS_BASE_ADDRESS 0xfed40000
|
||||
#endif
|
||||
|
||||
/* the macro accepts the locality value, but only locality 0 is operational */
|
||||
#define TIS_REG(LOCALITY, REG) \
|
||||
(void *)(CONFIG_TPM_TIS_BASE_ADDRESS + (LOCALITY << 12) + REG)
|
||||
|
||||
/* hardware registers' offsets */
|
||||
#define TIS_REG_ACCESS 0x0
|
||||
#define TIS_REG_INT_ENABLE 0x8
|
||||
#define TIS_REG_INT_VECTOR 0xc
|
||||
#define TIS_REG_INT_STATUS 0x10
|
||||
#define TIS_REG_INTF_CAPABILITY 0x14
|
||||
#define TIS_REG_STS 0x18
|
||||
#define TIS_REG_DATA_FIFO 0x24
|
||||
#define TIS_REG_DID_VID 0xf00
|
||||
#define TIS_REG_RID 0xf04
|
||||
|
||||
/* Some registers' bit field definitions */
|
||||
#define TIS_STS_VALID (1 << 7) /* 0x80 */
|
||||
#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */
|
||||
#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */
|
||||
#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */
|
||||
#define TIS_STS_EXPECT (1 << 3) /* 0x08 */
|
||||
#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */
|
||||
|
||||
#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */
|
||||
#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */
|
||||
#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */
|
||||
#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */
|
||||
#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */
|
||||
#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */
|
||||
#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */
|
||||
|
||||
#define TIS_STS_BURST_COUNT_MASK (0xffff)
|
||||
#define TIS_STS_BURST_COUNT_SHIFT (8)
|
||||
|
||||
/*
|
||||
* Error value returned if a tpm register does not enter the expected state
|
||||
* after continuous polling. No actual TPM register reading ever returns ~0,
|
||||
* so this value is a safe error indication to be mixed with possible status
|
||||
* register values.
|
||||
*/
|
||||
#define TPM_TIMEOUT_ERR (~0)
|
||||
|
||||
/* Error value returned on various TPM driver errors */
|
||||
#define TPM_DRIVER_ERR (~0)
|
||||
|
||||
/* 1 second is plenty for anything TPM does.*/
|
||||
#define MAX_DELAY_US (1000 * 1000)
|
||||
|
||||
/* Retrieve burst count value out of the status register contents. */
|
||||
#define BURST_COUNT(status) ((u16)(((status) >> TIS_STS_BURST_COUNT_SHIFT) & \
|
||||
TIS_STS_BURST_COUNT_MASK))
|
||||
|
||||
/*
|
||||
* Structures defined below allow creating descriptions of TPM vendor/device
|
||||
* ID information for run time discovery. The only device the system knows
|
||||
* about at this time is Infineon slb9635
|
||||
*/
|
||||
struct device_name {
|
||||
u16 dev_id;
|
||||
const char * const dev_name;
|
||||
};
|
||||
|
||||
struct vendor_name {
|
||||
u16 vendor_id;
|
||||
const char * vendor_name;
|
||||
const struct device_name* dev_names;
|
||||
};
|
||||
|
||||
static const struct device_name infineon_devices[] = {
|
||||
{0xb, "SLB9635 TT 1.2"},
|
||||
{0}
|
||||
};
|
||||
|
||||
static const struct vendor_name vendor_names[] = {
|
||||
{0x15d1, "Infineon", infineon_devices},
|
||||
};
|
||||
|
||||
/*
|
||||
* Cached vendor/device ID pair to indicate that the device has been already
|
||||
* discovered
|
||||
*/
|
||||
static u32 vendor_dev_id CAR_GLOBAL;
|
||||
|
||||
static int is_byte_reg(u32 reg)
|
||||
{
|
||||
/*
|
||||
* These TPM registers are 8 bits wide and as such require byte access
|
||||
* on writes and truncated value on reads.
|
||||
*/
|
||||
return ((reg == TIS_REG_ACCESS) ||
|
||||
(reg == TIS_REG_INT_VECTOR) ||
|
||||
(reg == TIS_REG_DATA_FIFO));
|
||||
}
|
||||
|
||||
/* TPM access functions are carved out to make tracing easier. */
|
||||
static u32 tpm_read(int locality, u32 reg)
|
||||
{
|
||||
u32 value;
|
||||
/*
|
||||
* Data FIFO register must be read and written in byte access mode,
|
||||
* otherwise the FIFO values are returned 4 bytes at a time.
|
||||
*/
|
||||
if (is_byte_reg(reg))
|
||||
value = readb(TIS_REG(locality, reg));
|
||||
else
|
||||
value = readl(TIS_REG(locality, reg));
|
||||
|
||||
TPM_DEBUG("Read reg 0x%x returns 0x%x\n", reg, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
static void tpm_write(u32 value, int locality, u32 reg)
|
||||
{
|
||||
TPM_DEBUG("Write reg 0x%x with 0x%x\n", reg, value);
|
||||
|
||||
if (is_byte_reg(reg))
|
||||
writeb(value & 0xff, TIS_REG(locality, reg));
|
||||
else
|
||||
writel(value, TIS_REG(locality, reg));
|
||||
}
|
||||
|
||||
/*
|
||||
* tis_wait_reg()
|
||||
*
|
||||
* Wait for at least a second for a register to change its state to match the
|
||||
* expected state. Normally the transition happens within microseconds.
|
||||
*
|
||||
* @reg - the TPM register offset
|
||||
* @locality - locality
|
||||
* @mask - bitmask for the bitfield(s) to watch
|
||||
* @expected - value the field(s) are supposed to be set to
|
||||
*
|
||||
* Returns the register contents in case the expected value was found in the
|
||||
* appropriate register bits, or TPM_TIMEOUT_ERR on timeout.
|
||||
*/
|
||||
static u32 tis_wait_reg(u8 reg, u8 locality, u8 mask, u8 expected)
|
||||
{
|
||||
u32 time_us = MAX_DELAY_US;
|
||||
while (time_us > 0) {
|
||||
u32 value = tpm_read(locality, reg);
|
||||
if ((value & mask) == expected)
|
||||
return value;
|
||||
udelay(1); /* 1 us */
|
||||
time_us--;
|
||||
}
|
||||
return TPM_TIMEOUT_ERR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe the TPM device and try determining its manufacturer/device name.
|
||||
*
|
||||
* Returns 0 on success (the device is found or was found during an earlier
|
||||
* invocation) or TPM_DRIVER_ERR if the device is not found.
|
||||
*/
|
||||
static u32 tis_probe(void)
|
||||
{
|
||||
u32 didvid = tpm_read(0, TIS_REG_DID_VID);
|
||||
int i;
|
||||
const char *device_name = "unknown";
|
||||
const char *vendor_name = device_name;
|
||||
u16 vid, did;
|
||||
|
||||
if (vendor_dev_id)
|
||||
return 0; /* Already probed. */
|
||||
|
||||
if (!didvid || (didvid == 0xffffffff)) {
|
||||
printf("%s: No TPM device found\n", __FUNCTION__);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
|
||||
vendor_dev_id = didvid;
|
||||
|
||||
vid = didvid & 0xffff;
|
||||
did = (didvid >> 16) & 0xffff;
|
||||
for (i = 0; i < ARRAY_SIZE(vendor_names); i++) {
|
||||
int j = 0;
|
||||
u16 known_did;
|
||||
if (vid == vendor_names[i].vendor_id) {
|
||||
vendor_name = vendor_names[i].vendor_name;
|
||||
}
|
||||
while ((known_did = vendor_names[i].dev_names[j].dev_id) != 0) {
|
||||
if (known_did == did) {
|
||||
device_name =
|
||||
vendor_names[i].dev_names[j].dev_name;
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* this will have to be converted into debug printout */
|
||||
TPM_DEBUG("Found TPM %s by %s\n", device_name, vendor_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* tis_senddata()
|
||||
*
|
||||
* send the passed in data to the TPM device.
|
||||
*
|
||||
* @data - address of the data to send, byte by byte
|
||||
* @len - length of the data to send
|
||||
*
|
||||
* Returns 0 on success, TPM_DRIVER_ERR on error (in case the device does
|
||||
* not accept the entire command).
|
||||
*/
|
||||
static u32 tis_senddata(const u8 * const data, u32 len)
|
||||
{
|
||||
u32 offset = 0;
|
||||
u16 burst = 0;
|
||||
u32 max_cycles = 0;
|
||||
u8 locality = 0;
|
||||
u32 value;
|
||||
|
||||
value = tis_wait_reg(TIS_REG_STS, locality, TIS_STS_COMMAND_READY,
|
||||
TIS_STS_COMMAND_READY);
|
||||
if (value == TPM_TIMEOUT_ERR) {
|
||||
printf("%s:%d - failed to get 'command_ready' status\n",
|
||||
__FILE__, __LINE__);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
burst = BURST_COUNT(value);
|
||||
|
||||
while (1) {
|
||||
unsigned count;
|
||||
|
||||
/* Wait till the device is ready to accept more data. */
|
||||
while (!burst) {
|
||||
if (max_cycles++ == MAX_DELAY_US) {
|
||||
printf("%s:%d failed to feed %d bytes of %d\n",
|
||||
__FILE__, __LINE__, len - offset, len);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
udelay(1);
|
||||
burst = BURST_COUNT(tpm_read(locality, TIS_REG_STS));
|
||||
}
|
||||
|
||||
max_cycles = 0;
|
||||
|
||||
/*
|
||||
* Calculate number of bytes the TPM is ready to accept in one
|
||||
* shot.
|
||||
*
|
||||
* We want to send the last byte outside of the loop (hence
|
||||
* the -1 below) to make sure that the 'expected' status bit
|
||||
* changes to zero exactly after the last byte is fed into the
|
||||
* FIFO.
|
||||
*/
|
||||
count = min(burst, len - offset - 1);
|
||||
while (count--)
|
||||
tpm_write(data[offset++], locality, TIS_REG_DATA_FIFO);
|
||||
|
||||
value = tis_wait_reg(TIS_REG_STS, locality,
|
||||
TIS_STS_VALID, TIS_STS_VALID);
|
||||
|
||||
if ((value == TPM_TIMEOUT_ERR) || !(value & TIS_STS_EXPECT)) {
|
||||
printf("%s:%d TPM command feed overflow\n",
|
||||
__FILE__, __LINE__);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
|
||||
burst = BURST_COUNT(value);
|
||||
if ((offset == (len - 1)) && burst)
|
||||
/*
|
||||
* We need to be able to send the last byte to the
|
||||
* device, so burst size must be nonzero before we
|
||||
* break out.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
/* Send the last byte. */
|
||||
tpm_write(data[offset++], locality, TIS_REG_DATA_FIFO);
|
||||
|
||||
/*
|
||||
* Verify that TPM does not expect any more data as part of this
|
||||
* command.
|
||||
*/
|
||||
value = tis_wait_reg(TIS_REG_STS, locality,
|
||||
TIS_STS_VALID, TIS_STS_VALID);
|
||||
if ((value == TPM_TIMEOUT_ERR) || (value & TIS_STS_EXPECT)) {
|
||||
printf("%s:%d unexpected TPM status 0x%x\n",
|
||||
__FILE__, __LINE__, value);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
|
||||
/* OK, sitting pretty, let's start the command execution. */
|
||||
tpm_write(TIS_STS_TPM_GO, locality, TIS_REG_STS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* tis_readresponse()
|
||||
*
|
||||
* read the TPM device response after a command was issued.
|
||||
*
|
||||
* @buffer - address where to read the response, byte by byte.
|
||||
* @len - pointer to the size of buffer
|
||||
*
|
||||
* On success stores the number of received bytes to len and returns 0. On
|
||||
* errors (misformatted TPM data or synchronization problems) returns
|
||||
* TPM_DRIVER_ERR.
|
||||
*/
|
||||
static u32 tis_readresponse(u8 *buffer, size_t *len)
|
||||
{
|
||||
u16 burst_count;
|
||||
u32 status;
|
||||
u32 offset = 0;
|
||||
u8 locality = 0;
|
||||
const u32 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
|
||||
u32 expected_count = *len;
|
||||
int max_cycles = 0;
|
||||
|
||||
/* Wait for the TPM to process the command */
|
||||
status = tis_wait_reg(TIS_REG_STS, locality, has_data, has_data);
|
||||
if (status == TPM_TIMEOUT_ERR) {
|
||||
printf("%s:%d failed processing command\n",
|
||||
__FILE__, __LINE__);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
|
||||
do {
|
||||
while ((burst_count = BURST_COUNT(status)) == 0) {
|
||||
if (max_cycles++ == MAX_DELAY_US) {
|
||||
printf("%s:%d TPM stuck on read\n",
|
||||
__FILE__, __LINE__);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
udelay(1);
|
||||
status = tpm_read(locality, TIS_REG_STS);
|
||||
}
|
||||
|
||||
max_cycles = 0;
|
||||
|
||||
while (burst_count-- && (offset < expected_count)) {
|
||||
buffer[offset++] = (u8) tpm_read(locality,
|
||||
TIS_REG_DATA_FIFO);
|
||||
if (offset == 6) {
|
||||
/*
|
||||
* We got the first six bytes of the reply,
|
||||
* let's figure out how many bytes to expect
|
||||
* total - it is stored as a 4 byte number in
|
||||
* network order, starting with offset 2 into
|
||||
* the body of the reply.
|
||||
*/
|
||||
u32 real_length;
|
||||
memcpy(&real_length,
|
||||
buffer + 2,
|
||||
sizeof(real_length));
|
||||
expected_count = be32_to_cpu(real_length);
|
||||
|
||||
if ((expected_count < offset) ||
|
||||
(expected_count > *len)) {
|
||||
printf("%s:%d bad response size %d\n",
|
||||
__FILE__, __LINE__,
|
||||
expected_count);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for the next portion */
|
||||
status = tis_wait_reg(TIS_REG_STS, locality,
|
||||
TIS_STS_VALID, TIS_STS_VALID);
|
||||
if (status == TPM_TIMEOUT_ERR) {
|
||||
printf("%s:%d failed to read response\n",
|
||||
__FILE__, __LINE__);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
|
||||
if (offset == expected_count)
|
||||
break; /* We got all we need */
|
||||
|
||||
} while ((status & has_data) == has_data);
|
||||
|
||||
/*
|
||||
* Make sure we indeed read all there was. The TIS_STS_VALID bit is
|
||||
* known to be set.
|
||||
*/
|
||||
if (status & TIS_STS_DATA_AVAILABLE) {
|
||||
printf("%s:%d wrong receive status %x\n",
|
||||
__FILE__, __LINE__, status);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
|
||||
/* Tell the TPM that we are done. */
|
||||
tpm_write(TIS_STS_COMMAND_READY, locality, TIS_REG_STS);
|
||||
|
||||
*len = offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* tis_init()
|
||||
*
|
||||
* Initialize the TPM device. Returns 0 on success or TPM_DRIVER_ERR on
|
||||
* failure (in case device probing did not succeed).
|
||||
*/
|
||||
int tis_init(void)
|
||||
{
|
||||
if (tis_probe())
|
||||
return TPM_DRIVER_ERR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* tis_open()
|
||||
*
|
||||
* Requests access to locality 0 for the caller. After all commands have been
|
||||
* completed the caller is supposed to call tis_close().
|
||||
*
|
||||
* Returns 0 on success, TPM_DRIVER_ERR on failure.
|
||||
*/
|
||||
int tis_open(void)
|
||||
{
|
||||
u8 locality = 0; /* we use locality zero for everything */
|
||||
|
||||
if (tis_close())
|
||||
return TPM_DRIVER_ERR;
|
||||
|
||||
/* now request access to locality */
|
||||
tpm_write(TIS_ACCESS_REQUEST_USE, locality, TIS_REG_ACCESS);
|
||||
|
||||
/* did we get a lock? */
|
||||
if (tis_wait_reg(TIS_REG_ACCESS, locality,
|
||||
TIS_ACCESS_ACTIVE_LOCALITY,
|
||||
TIS_ACCESS_ACTIVE_LOCALITY) == TPM_TIMEOUT_ERR) {
|
||||
printf("%s:%d - failed to lock locality %d\n",
|
||||
__FILE__, __LINE__, locality);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
|
||||
tpm_write(TIS_STS_COMMAND_READY, locality, TIS_REG_STS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* tis_close()
|
||||
*
|
||||
* terminate the currect session with the TPM by releasing the locked
|
||||
* locality. Returns 0 on success of TPM_DRIVER_ERR on failure (in case lock
|
||||
* removal did not succeed).
|
||||
*/
|
||||
int tis_close(void)
|
||||
{
|
||||
u8 locality = 0;
|
||||
if (tpm_read(locality, TIS_REG_ACCESS) &
|
||||
TIS_ACCESS_ACTIVE_LOCALITY) {
|
||||
tpm_write(TIS_ACCESS_ACTIVE_LOCALITY, locality, TIS_REG_ACCESS);
|
||||
|
||||
if (tis_wait_reg(TIS_REG_ACCESS, locality,
|
||||
TIS_ACCESS_ACTIVE_LOCALITY, 0) ==
|
||||
TPM_TIMEOUT_ERR) {
|
||||
printf("%s:%d - failed to release locality %d\n",
|
||||
__FILE__, __LINE__, locality);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* tis_sendrecv()
|
||||
*
|
||||
* Send the requested data to the TPM and then try to get its response
|
||||
*
|
||||
* @sendbuf - buffer of the data to send
|
||||
* @send_size size of the data to send
|
||||
* @recvbuf - memory to save the response to
|
||||
* @recv_len - pointer to the size of the response buffer
|
||||
*
|
||||
* Returns 0 on success (and places the number of response bytes at recv_len)
|
||||
* or TPM_DRIVER_ERR on failure.
|
||||
*/
|
||||
int tis_sendrecv(const uint8_t *sendbuf, size_t send_size,
|
||||
uint8_t *recvbuf, size_t *recv_len)
|
||||
{
|
||||
if (tis_senddata(sendbuf, send_size)) {
|
||||
printf("%s:%d failed sending data to TPM\n",
|
||||
__FILE__, __LINE__);
|
||||
return TPM_DRIVER_ERR;
|
||||
}
|
||||
|
||||
return tis_readresponse(recvbuf, recv_len);
|
||||
}
|
10
src/drivers/pc80/udelay_io.c
Normal file
10
src/drivers/pc80/udelay_io.c
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <arch/io.h>
|
||||
#include <delay.h>
|
||||
|
||||
void udelay(unsigned usecs)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < usecs; i++)
|
||||
inb(0x80);
|
||||
}
|
||||
|
4
src/drivers/pc80/vga/Makefile.inc
Normal file
4
src/drivers/pc80/vga/Makefile.inc
Normal file
@@ -0,0 +1,4 @@
|
||||
ramstage-$(CONFIG_VGA) += vga_io.c
|
||||
ramstage-$(CONFIG_VGA) += vga_palette.c
|
||||
ramstage-$(CONFIG_VGA) += vga_font_8x16.c
|
||||
ramstage-$(CONFIG_VGA) += vga.c
|
325
src/drivers/pc80/vga/vga.c
Normal file
325
src/drivers/pc80/vga/vga.c
Normal file
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009 Luc Verhaegen <libv@skynet.be>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <pc80/vga.h>
|
||||
#include <pc80/vga_io.h>
|
||||
|
||||
#include <string.h>
|
||||
#include "vga.h"
|
||||
|
||||
/*
|
||||
* pci io enable should've happened before
|
||||
*/
|
||||
void
|
||||
vga_io_init(void)
|
||||
{
|
||||
vga_enable_mask(0x01, 0x01);
|
||||
|
||||
/* cr io is at 0x3D4/0x3D5 */
|
||||
vga_misc_mask(0x01, 0x01);
|
||||
|
||||
/* clear cr0-7 protection */
|
||||
vga_cr_mask(0x11, 0x00, 0x80);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static void
|
||||
vga_fb_init(void)
|
||||
{
|
||||
vga_sr_write(0x02, 0x03);
|
||||
vga_sr_write(0x03, 0x00);
|
||||
vga_sr_write(0x04, 0x02); /* access all 256kB */
|
||||
|
||||
vga_gr_write(0x00, 0x00);
|
||||
vga_gr_write(0x01, 0x00);
|
||||
vga_gr_write(0x02, 0x00);
|
||||
vga_gr_write(0x03, 0x00);
|
||||
vga_gr_write(0x04, 0x00);
|
||||
vga_gr_write(0x05, 0x10);
|
||||
vga_gr_write(0x06, 0x0E); /* map at 0xB8000 */
|
||||
vga_gr_write(0x07, 0x00);
|
||||
vga_gr_write(0x08, 0xFF);
|
||||
|
||||
/* o/e enable: ram enable */
|
||||
vga_misc_mask(0x22, 0x22);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static void
|
||||
vga_fb_clear(void)
|
||||
{
|
||||
memset((void *)VGA_FB, 0x00, 0x8000);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static void
|
||||
vga_palette_init(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* set up attribute registers */
|
||||
for (i = 0; i < 0x10; i++)
|
||||
vga_ar_write(i, i);
|
||||
|
||||
vga_ar_write(0x10, 0x0c);
|
||||
vga_ar_write(0x11, 0x00);
|
||||
vga_ar_write(0x12, 0x0F);
|
||||
vga_ar_write(0x13, 0x08);
|
||||
vga_ar_write(0x14, 0x00);
|
||||
|
||||
vga_palette_disable();
|
||||
|
||||
/* load actual palette */
|
||||
vga_dac_mask_write(0xFF);
|
||||
|
||||
for (i = 0; i < 0x100; i++) {
|
||||
vga_dac_write_address(i);
|
||||
vga_dac_data_write(default_vga_palette[i].red);
|
||||
vga_dac_data_write(default_vga_palette[i].green);
|
||||
vga_dac_data_write(default_vga_palette[i].blue);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static void
|
||||
vga_mode_set(int hdisplay, int hblankstart, int hsyncstart, int hsyncend,
|
||||
int hblankend, int htotal, int vdisplay, int vblankstart,
|
||||
int vsyncstart, int vsyncend, int vblankend, int vtotal,
|
||||
int stride)
|
||||
{
|
||||
/* htotal: 2080 */
|
||||
htotal /= 8;
|
||||
htotal -= 5;
|
||||
vga_cr_write(0x00, htotal);
|
||||
|
||||
/* hdisplay: 2048 */
|
||||
hdisplay /= 8;
|
||||
hdisplay -= 1;
|
||||
vga_cr_write(0x01, hdisplay);
|
||||
|
||||
/* hblankstart: 2048 */
|
||||
hblankstart /= 8;
|
||||
hblankstart -= 1;
|
||||
vga_cr_write(0x02, hblankstart);
|
||||
|
||||
/* hblankend: hblankstart + 512 */
|
||||
hblankend /= 8;
|
||||
hblankend -= 1;
|
||||
vga_cr_mask(0x03, hblankend, 0x1F);
|
||||
vga_cr_mask(0x05, hblankend << 2, 0x80);
|
||||
|
||||
/* hsyncstart: 255 * 8: 2040 */
|
||||
vga_cr_write(0x04, hsyncstart / 8);
|
||||
|
||||
/* hsyncend: hsyncstart + 255 */
|
||||
vga_cr_mask(0x05, hsyncend / 8, 0x1F);
|
||||
|
||||
/* vtotal: 1025 */
|
||||
vtotal -= 2;
|
||||
vga_cr_write(0x06, vtotal);
|
||||
vga_cr_mask(0x07, vtotal >> 8, 0x01);
|
||||
vga_cr_mask(0x07, vtotal >> 4, 0x20);
|
||||
|
||||
/* vdisplay: 1024 */
|
||||
vdisplay -= 1;
|
||||
vga_cr_write(0x12, vdisplay);
|
||||
vga_cr_mask(0x07, vdisplay >> 7, 0x02);
|
||||
vga_cr_mask(0x07, vdisplay >> 3, 0x40);
|
||||
|
||||
/* vblankstart: 1024 */
|
||||
vblankstart -= 1;
|
||||
vga_cr_write(0x15, vblankstart);
|
||||
vga_cr_mask(0x07, vblankstart >> 5, 0x08);
|
||||
vga_cr_mask(0x09, vblankstart >> 4, 0x20);
|
||||
|
||||
/* vblankend: vblankstart + 256 */
|
||||
vblankend -= 1;
|
||||
vga_cr_write(0x16, vblankend);
|
||||
|
||||
/* vsyncstart: 1023 */
|
||||
vga_cr_write(0x10, vsyncstart);
|
||||
vga_cr_mask(0x07, vsyncstart >> 6, 0x04);
|
||||
vga_cr_mask(0x07, vsyncstart >> 2, 0x80);
|
||||
|
||||
/* vsyncend: vsyncstart + 16 */
|
||||
vga_cr_mask(0x11, vsyncend, 0x0F);
|
||||
|
||||
/* stride */
|
||||
vga_cr_write(0x13, stride / 8);
|
||||
|
||||
/* line compare */
|
||||
vga_cr_write(0x18, 0xFF);
|
||||
vga_cr_mask(0x07, 0x10, 0x10);
|
||||
vga_cr_mask(0x09, 0x40, 0x40);
|
||||
|
||||
vga_misc_mask(0x44, 0xCC); /* set up clock: 27mhz and h/vsync */
|
||||
|
||||
vga_cr_mask(0x09, 0x00, 0x80); /* disable doublescan */
|
||||
}
|
||||
|
||||
static void
|
||||
vga_font_8x16_load(void)
|
||||
{
|
||||
unsigned char *p;
|
||||
int i, j;
|
||||
unsigned char sr2, sr4, gr5, gr6;
|
||||
|
||||
#define height 16
|
||||
#define count 256
|
||||
|
||||
sr2 = vga_sr_read(0x02);
|
||||
sr4 = vga_sr_read(0x04);
|
||||
gr5 = vga_gr_read(0x05);
|
||||
gr6 = vga_gr_read(0x06);
|
||||
|
||||
/* disable odd/even */
|
||||
vga_sr_mask(0x04, 0x04, 0x04);
|
||||
vga_gr_mask(0x05, 0x00, 0x10);
|
||||
vga_gr_mask(0x06, 0x00, 0x02);
|
||||
|
||||
/* plane 2 */
|
||||
vga_sr_write(0x02, 0x04);
|
||||
p = (unsigned char *) VGA_FB;
|
||||
for (i = 0; i < count; i++) {
|
||||
for (j = 0; j < 32; j++) {
|
||||
if (j < height)
|
||||
*p = vga_font_8x16[i][j];
|
||||
else
|
||||
*p = 0x00;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
vga_gr_write(0x06, gr6);
|
||||
vga_gr_write(0x05, gr5);
|
||||
vga_sr_write(0x04, sr4);
|
||||
vga_sr_write(0x02, sr2);
|
||||
|
||||
/* set up font size */
|
||||
vga_cr_mask(0x09, 16 - 1, 0x1F);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void
|
||||
vga_cursor_enable(int enable)
|
||||
{
|
||||
if (enable)
|
||||
vga_cr_mask(0x0A, 0x00, 0x20);
|
||||
else
|
||||
vga_cr_mask(0x0A, 0x20, 0x20);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void
|
||||
vga_cursor_reset(void)
|
||||
{
|
||||
vga_cr_write(0x0A, 0x2E);
|
||||
vga_cr_write(0x0B, 0x0E);
|
||||
vga_cr_write(0x0E, 0x00);
|
||||
vga_cr_write(0x0F, 0x00);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void
|
||||
vga_cursor_set(unsigned int line, unsigned int character)
|
||||
{
|
||||
unsigned int offset = (80 * line + character) & 0xFFFF;
|
||||
|
||||
vga_cr_write(0x0A, 0x0E);
|
||||
vga_cr_write(0x0B, 0x0E);
|
||||
vga_cr_write(0x0E, offset >> 8);
|
||||
vga_cr_write(0x0F, offset & 0xFF);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void
|
||||
vga_frame_set(unsigned int line, unsigned int character)
|
||||
{
|
||||
unsigned int offset = (80 * line + character) & 0xFFFF;
|
||||
|
||||
vga_cr_write(0x0C, offset >> 8);
|
||||
vga_cr_write(0x0D, offset & 0xFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* simply fills a line with the given string.
|
||||
*/
|
||||
void
|
||||
vga_line_write(unsigned int line, const char *string)
|
||||
{
|
||||
unsigned short *p = (unsigned short *) VGA_FB + (80 * line);
|
||||
int i, len = strlen(string);
|
||||
|
||||
for (i = 0; i < 80; i++) {
|
||||
if (i < len)
|
||||
p[i] = 0x0F00 | string[i];
|
||||
else
|
||||
p[i] = 0x0F00;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set up everything to get a basic 80x25 textmode.
|
||||
*/
|
||||
void
|
||||
vga_textmode_init(void)
|
||||
{
|
||||
vga_sr_write(0x00, 0x01); /* clear reset */
|
||||
vga_sr_write(0x01, 0x00);
|
||||
|
||||
/* set up cr */
|
||||
vga_cr_mask(0x03, 0x80, 0xE0);
|
||||
vga_cr_mask(0x05, 0x00, 0x60);
|
||||
|
||||
vga_cr_write(0x08, 0x00);
|
||||
|
||||
vga_cr_write(0x14, 0x00); /* */
|
||||
|
||||
vga_cr_write(0x17, 0x23);
|
||||
|
||||
vga_palette_init();
|
||||
|
||||
vga_mode_set(640, 648, 680, 776, 792, 800,
|
||||
400, 407, 412, 414, 442, 449, 320);
|
||||
|
||||
vga_cursor_reset();
|
||||
vga_frame_set(0, 0);
|
||||
|
||||
vga_fb_init();
|
||||
vga_fb_clear();
|
||||
vga_font_8x16_load();
|
||||
|
||||
vga_sr_mask(0x00, 0x02, 0x02); /* take us out of reset */
|
||||
vga_cr_mask(0x17, 0x80, 0x80); /* sync! */
|
||||
}
|
17
src/drivers/pc80/vga/vga.h
Normal file
17
src/drivers/pc80/vga/vga.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _VGA_H
|
||||
#define _VGA_H
|
||||
|
||||
/*
|
||||
* Basic palette.
|
||||
*/
|
||||
struct palette {
|
||||
unsigned char red;
|
||||
unsigned char green;
|
||||
unsigned char blue;
|
||||
};
|
||||
|
||||
extern const struct palette default_vga_palette[0x100];
|
||||
|
||||
extern const unsigned char vga_font_8x16[256][16];
|
||||
|
||||
#endif /* _VGA_H */
|
4382
src/drivers/pc80/vga/vga_font_8x16.c
Normal file
4382
src/drivers/pc80/vga/vga_font_8x16.c
Normal file
File diff suppressed because it is too large
Load Diff
275
src/drivers/pc80/vga/vga_io.c
Normal file
275
src/drivers/pc80/vga/vga_io.c
Normal file
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009 Luc Verhaegen <libv@skynet.be>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* All IO necessary to poke VGA registers.
|
||||
*/
|
||||
#include <pc80/vga_io.h>
|
||||
|
||||
#include <arch/io.h>
|
||||
|
||||
#define VGA_CR_INDEX 0x3D4
|
||||
#define VGA_CR_VALUE 0x3D5
|
||||
|
||||
#define VGA_SR_INDEX 0x3C4
|
||||
#define VGA_SR_VALUE 0x3C5
|
||||
|
||||
#define VGA_GR_INDEX 0x3CE
|
||||
#define VGA_GR_VALUE 0x3CF
|
||||
|
||||
#define VGA_AR_INDEX 0x3C0
|
||||
#define VGA_AR_VALUE_READ 0x3C1
|
||||
#define VGA_AR_VALUE_WRITE VGA_AR_INDEX
|
||||
|
||||
#define VGA_MISC_WRITE 0x3C2
|
||||
#define VGA_MISC_READ 0x3CC
|
||||
|
||||
#define VGA_ENABLE 0x3C3
|
||||
#define VGA_STAT1 0x3DA
|
||||
|
||||
#define VGA_DAC_MASK 0x3C6
|
||||
#define VGA_DAC_READ_ADDRESS 0x3C7
|
||||
#define VGA_DAC_WRITE_ADDRESS 0x3C8
|
||||
#define VGA_DAC_DATA 0x3C9
|
||||
|
||||
/*
|
||||
* VGA enable. Poke this to have the PCI IO enabled device accept VGA IO.
|
||||
*/
|
||||
unsigned char
|
||||
vga_enable_read(void)
|
||||
{
|
||||
return inb(VGA_ENABLE);
|
||||
}
|
||||
|
||||
void
|
||||
vga_enable_write(unsigned char value)
|
||||
{
|
||||
outb(value, VGA_ENABLE);
|
||||
}
|
||||
|
||||
void
|
||||
vga_enable_mask(unsigned char value, unsigned char mask)
|
||||
{
|
||||
unsigned char tmp;
|
||||
|
||||
tmp = vga_enable_read();
|
||||
tmp &= ~mask;
|
||||
tmp |= (value & mask);
|
||||
vga_enable_write(tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Miscellaneous register.
|
||||
*/
|
||||
unsigned char
|
||||
vga_misc_read(void)
|
||||
{
|
||||
return inb(VGA_MISC_READ);
|
||||
}
|
||||
|
||||
void
|
||||
vga_misc_write(unsigned char value)
|
||||
{
|
||||
outb(value, VGA_MISC_WRITE);
|
||||
}
|
||||
|
||||
void
|
||||
vga_misc_mask(unsigned char value, unsigned char mask)
|
||||
{
|
||||
unsigned char tmp;
|
||||
|
||||
tmp = vga_misc_read();
|
||||
tmp &= ~mask;
|
||||
tmp |= (value & mask);
|
||||
vga_misc_write(tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sequencer registers.
|
||||
*/
|
||||
unsigned char
|
||||
vga_sr_read(unsigned char index)
|
||||
{
|
||||
outb(index, VGA_SR_INDEX);
|
||||
return (inb(VGA_SR_VALUE));
|
||||
}
|
||||
|
||||
void
|
||||
vga_sr_write(unsigned char index, unsigned char value)
|
||||
{
|
||||
outb(index, VGA_SR_INDEX);
|
||||
outb(value, VGA_SR_VALUE);
|
||||
}
|
||||
|
||||
void
|
||||
vga_sr_mask(unsigned char index, unsigned char value, unsigned char mask)
|
||||
{
|
||||
unsigned char tmp;
|
||||
|
||||
tmp = vga_sr_read(index);
|
||||
tmp &= ~mask;
|
||||
tmp |= (value & mask);
|
||||
vga_sr_write(index, tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* CRTC registers.
|
||||
*/
|
||||
unsigned char
|
||||
vga_cr_read(unsigned char index)
|
||||
{
|
||||
outb(index, VGA_CR_INDEX);
|
||||
return (inb(VGA_CR_VALUE));
|
||||
}
|
||||
|
||||
void
|
||||
vga_cr_write(unsigned char index, unsigned char value)
|
||||
{
|
||||
outb(index, VGA_CR_INDEX);
|
||||
outb(value, VGA_CR_VALUE);
|
||||
}
|
||||
|
||||
void
|
||||
vga_cr_mask(unsigned char index, unsigned char value, unsigned char mask)
|
||||
{
|
||||
unsigned char tmp;
|
||||
|
||||
tmp = vga_cr_read(index);
|
||||
tmp &= ~mask;
|
||||
tmp |= (value & mask);
|
||||
vga_cr_write(index, tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attribute registers.
|
||||
*/
|
||||
unsigned char
|
||||
vga_ar_read(unsigned char index)
|
||||
{
|
||||
unsigned char ret;
|
||||
|
||||
(void) inb(VGA_STAT1);
|
||||
outb(index, VGA_AR_INDEX);
|
||||
ret = inb(VGA_AR_VALUE_READ);
|
||||
(void) inb(VGA_STAT1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
vga_ar_write(unsigned char index, unsigned char value)
|
||||
{
|
||||
(void) inb(VGA_STAT1);
|
||||
outb(index, VGA_AR_INDEX);
|
||||
outb(value, VGA_AR_VALUE_WRITE);
|
||||
(void) inb(VGA_STAT1);
|
||||
}
|
||||
|
||||
void
|
||||
vga_ar_mask(unsigned char index, unsigned char value, unsigned char mask)
|
||||
{
|
||||
unsigned char tmp;
|
||||
|
||||
tmp = vga_ar_read(index);
|
||||
tmp &= ~mask;
|
||||
tmp |= (value & mask);
|
||||
vga_ar_write(index, tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Graphics registers.
|
||||
*/
|
||||
unsigned char
|
||||
vga_gr_read(unsigned char index)
|
||||
{
|
||||
outb(index, VGA_GR_INDEX);
|
||||
return (inb(VGA_GR_VALUE));
|
||||
}
|
||||
|
||||
void
|
||||
vga_gr_write(unsigned char index, unsigned char value)
|
||||
{
|
||||
outb(index, VGA_GR_INDEX);
|
||||
outb(value, VGA_GR_VALUE);
|
||||
}
|
||||
|
||||
void
|
||||
vga_gr_mask(unsigned char index, unsigned char value, unsigned char mask)
|
||||
{
|
||||
unsigned char tmp;
|
||||
|
||||
tmp = vga_gr_read(index);
|
||||
tmp &= ~mask;
|
||||
tmp |= (value & mask);
|
||||
vga_gr_write(index, tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* DAC functions.
|
||||
*/
|
||||
void
|
||||
vga_palette_enable(void)
|
||||
{
|
||||
(void) inb(VGA_STAT1);
|
||||
outb(0x00, VGA_AR_INDEX);
|
||||
(void) inb(VGA_STAT1);
|
||||
}
|
||||
|
||||
void
|
||||
vga_palette_disable(void)
|
||||
{
|
||||
(void) inb(VGA_STAT1);
|
||||
outb(0x20, VGA_AR_INDEX);
|
||||
(void) inb(VGA_STAT1);
|
||||
}
|
||||
|
||||
unsigned char
|
||||
vga_dac_mask_read(void)
|
||||
{
|
||||
return inb(VGA_DAC_MASK);
|
||||
}
|
||||
|
||||
void
|
||||
vga_dac_mask_write(unsigned char mask)
|
||||
{
|
||||
outb(mask, VGA_DAC_MASK);
|
||||
}
|
||||
|
||||
void
|
||||
vga_dac_read_address(unsigned char address)
|
||||
{
|
||||
outb(address, VGA_DAC_READ_ADDRESS);
|
||||
}
|
||||
|
||||
void
|
||||
vga_dac_write_address(unsigned char address)
|
||||
{
|
||||
outb(address, VGA_DAC_WRITE_ADDRESS);
|
||||
}
|
||||
|
||||
unsigned char
|
||||
vga_dac_data_read(void)
|
||||
{
|
||||
return inb(VGA_DAC_DATA);
|
||||
}
|
||||
|
||||
void
|
||||
vga_dac_data_write(unsigned char data)
|
||||
{
|
||||
outb(data, VGA_DAC_DATA);
|
||||
}
|
276
src/drivers/pc80/vga/vga_palette.c
Normal file
276
src/drivers/pc80/vga/vga_palette.c
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009 Luc Verhaegen <libv@skynet.be>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "vga.h"
|
||||
|
||||
/*
|
||||
* Basic palette.
|
||||
*/
|
||||
const struct palette
|
||||
default_vga_palette[0x100] = {
|
||||
{ 0x00, 0x00, 0x00},
|
||||
{ 0x00, 0x00, 0x2A},
|
||||
{ 0x00, 0x2A, 0x00},
|
||||
{ 0x00, 0x2A, 0x2A},
|
||||
{ 0x2A, 0x00, 0x00},
|
||||
{ 0x2A, 0x00, 0x2A},
|
||||
{ 0x2A, 0x15, 0x00},
|
||||
{ 0x2A, 0x2A, 0x2A},
|
||||
{ 0x15, 0x15, 0x15},
|
||||
{ 0x15, 0x15, 0x3F},
|
||||
{ 0x15, 0x3F, 0x15},
|
||||
{ 0x15, 0x3F, 0x3F},
|
||||
{ 0x3F, 0x15, 0x15},
|
||||
{ 0x3F, 0x15, 0x3F},
|
||||
{ 0x3F, 0x3F, 0x15},
|
||||
{ 0x3F, 0x3F, 0x3F},
|
||||
{ 0x00, 0x00, 0x00},
|
||||
{ 0x05, 0x05, 0x05},
|
||||
{ 0x08, 0x08, 0x08},
|
||||
{ 0x0B, 0x0B, 0x0B},
|
||||
{ 0x0E, 0x0E, 0x0E},
|
||||
{ 0x11, 0x11, 0x11},
|
||||
{ 0x16, 0x16, 0x16},
|
||||
{ 0x18, 0x18, 0x18},
|
||||
{ 0x1C, 0x1C, 0x1C},
|
||||
{ 0x20, 0x20, 0x20},
|
||||
{ 0x24, 0x24, 0x24},
|
||||
{ 0x28, 0x28, 0x28},
|
||||
{ 0x2D, 0x2D, 0x2D},
|
||||
{ 0x32, 0x32, 0x32},
|
||||
{ 0x38, 0x38, 0x38},
|
||||
{ 0x3F, 0x3F, 0x3F},
|
||||
{ 0x00, 0x00, 0x3F},
|
||||
{ 0x20, 0x00, 0x3F},
|
||||
{ 0x1F, 0x00, 0x3F},
|
||||
{ 0x2F, 0x00, 0x3F},
|
||||
{ 0x3F, 0x00, 0x3F},
|
||||
{ 0x3F, 0x00, 0x2F},
|
||||
{ 0x3F, 0x00, 0x1F},
|
||||
{ 0x3F, 0x00, 0x20},
|
||||
{ 0x3F, 0x00, 0x00},
|
||||
{ 0x3F, 0x20, 0x00},
|
||||
{ 0x3F, 0x1F, 0x00},
|
||||
{ 0x3F, 0x2F, 0x00},
|
||||
{ 0x3F, 0x3F, 0x00},
|
||||
{ 0x2F, 0x3F, 0x00},
|
||||
{ 0x1F, 0x3F, 0x00},
|
||||
{ 0x20, 0x3F, 0x00},
|
||||
{ 0x00, 0x3F, 0x00},
|
||||
{ 0x00, 0x3F, 0x20},
|
||||
{ 0x00, 0x3F, 0x1F},
|
||||
{ 0x00, 0x3F, 0x2F},
|
||||
{ 0x00, 0x3F, 0x3F},
|
||||
{ 0x00, 0x2F, 0x3F},
|
||||
{ 0x00, 0x1F, 0x3F},
|
||||
{ 0x00, 0x20, 0x3F},
|
||||
{ 0x1F, 0x1F, 0x3F},
|
||||
{ 0x27, 0x1F, 0x3F},
|
||||
{ 0x2F, 0x1F, 0x3F},
|
||||
{ 0x37, 0x1F, 0x3F},
|
||||
{ 0x3F, 0x1F, 0x3F},
|
||||
{ 0x3F, 0x1F, 0x37},
|
||||
{ 0x3F, 0x1F, 0x2F},
|
||||
{ 0x3F, 0x1F, 0x27},
|
||||
{ 0x3F, 0x1F, 0x1F},
|
||||
{ 0x3F, 0x27, 0x1F},
|
||||
{ 0x3F, 0x2F, 0x1F},
|
||||
{ 0x3F, 0x37, 0x1F},
|
||||
{ 0x3F, 0x3F, 0x1F},
|
||||
{ 0x37, 0x3F, 0x1F},
|
||||
{ 0x2F, 0x3F, 0x1F},
|
||||
{ 0x27, 0x3F, 0x1F},
|
||||
{ 0x1F, 0x3F, 0x1F},
|
||||
{ 0x1F, 0x3F, 0x27},
|
||||
{ 0x1F, 0x3F, 0x2F},
|
||||
{ 0x1F, 0x3F, 0x37},
|
||||
{ 0x1F, 0x3F, 0x3F},
|
||||
{ 0x1F, 0x37, 0x3F},
|
||||
{ 0x1F, 0x2F, 0x3F},
|
||||
{ 0x1F, 0x27, 0x3F},
|
||||
{ 0x2D, 0x2D, 0x3F},
|
||||
{ 0x31, 0x2D, 0x3F},
|
||||
{ 0x36, 0x2D, 0x3F},
|
||||
{ 0x3A, 0x2D, 0x3F},
|
||||
{ 0x3F, 0x2D, 0x3F},
|
||||
{ 0x3F, 0x2D, 0x3A},
|
||||
{ 0x3F, 0x2D, 0x36},
|
||||
{ 0x3F, 0x2D, 0x31},
|
||||
{ 0x3F, 0x2D, 0x2D},
|
||||
{ 0x3F, 0x31, 0x2D},
|
||||
{ 0x3F, 0x36, 0x2D},
|
||||
{ 0x3F, 0x3A, 0x2D},
|
||||
{ 0x3F, 0x3F, 0x2D},
|
||||
{ 0x3A, 0x3F, 0x2D},
|
||||
{ 0x36, 0x3F, 0x2D},
|
||||
{ 0x31, 0x3F, 0x2D},
|
||||
{ 0x2D, 0x3F, 0x2D},
|
||||
{ 0x2D, 0x3F, 0x31},
|
||||
{ 0x2D, 0x3F, 0x36},
|
||||
{ 0x2D, 0x3F, 0x3A},
|
||||
{ 0x2D, 0x3F, 0x3F},
|
||||
{ 0x2D, 0x3A, 0x3F},
|
||||
{ 0x2D, 0x36, 0x3F},
|
||||
{ 0x2D, 0x31, 0x3F},
|
||||
{ 0x00, 0x00, 0x1C},
|
||||
{ 0x07, 0x00, 0x1C},
|
||||
{ 0x0E, 0x00, 0x1C},
|
||||
{ 0x15, 0x00, 0x1C},
|
||||
{ 0x1C, 0x00, 0x1C},
|
||||
{ 0x1C, 0x00, 0x15},
|
||||
{ 0x1C, 0x00, 0x0E},
|
||||
{ 0x1C, 0x00, 0x07},
|
||||
{ 0x1C, 0x00, 0x00},
|
||||
{ 0x1C, 0x07, 0x00},
|
||||
{ 0x1C, 0x0E, 0x00},
|
||||
{ 0x1C, 0x15, 0x00},
|
||||
{ 0x1C, 0x1C, 0x00},
|
||||
{ 0x15, 0x1C, 0x00},
|
||||
{ 0x0E, 0x1C, 0x00},
|
||||
{ 0x07, 0x1C, 0x00},
|
||||
{ 0x00, 0x1C, 0x00},
|
||||
{ 0x00, 0x1C, 0x07},
|
||||
{ 0x00, 0x1C, 0x0E},
|
||||
{ 0x00, 0x1C, 0x15},
|
||||
{ 0x00, 0x1C, 0x1C},
|
||||
{ 0x00, 0x15, 0x1C},
|
||||
{ 0x00, 0x0E, 0x1C},
|
||||
{ 0x00, 0x07, 0x1C},
|
||||
{ 0x0E, 0x0E, 0x1C},
|
||||
{ 0x11, 0x0E, 0x1C},
|
||||
{ 0x15, 0x0E, 0x1C},
|
||||
{ 0x18, 0x0E, 0x1C},
|
||||
{ 0x1C, 0x0E, 0x1C},
|
||||
{ 0x1C, 0x0E, 0x18},
|
||||
{ 0x1C, 0x0E, 0x15},
|
||||
{ 0x1C, 0x0E, 0x11},
|
||||
{ 0x1C, 0x0E, 0x0E},
|
||||
{ 0x1C, 0x11, 0x0E},
|
||||
{ 0x1C, 0x15, 0x0E},
|
||||
{ 0x1C, 0x18, 0x0E},
|
||||
{ 0x1C, 0x1C, 0x0E},
|
||||
{ 0x18, 0x1C, 0x0E},
|
||||
{ 0x15, 0x1C, 0x0E},
|
||||
{ 0x11, 0x1C, 0x0E},
|
||||
{ 0x0E, 0x1C, 0x0E},
|
||||
{ 0x0E, 0x1C, 0x11},
|
||||
{ 0x0E, 0x1C, 0x15},
|
||||
{ 0x0E, 0x1C, 0x18},
|
||||
{ 0x0E, 0x1C, 0x1C},
|
||||
{ 0x0E, 0x18, 0x1C},
|
||||
{ 0x0E, 0x15, 0x1C},
|
||||
{ 0x0E, 0x11, 0x1C},
|
||||
{ 0x16, 0x16, 0x1C},
|
||||
{ 0x16, 0x16, 0x1C},
|
||||
{ 0x18, 0x16, 0x1C},
|
||||
{ 0x1A, 0x16, 0x1C},
|
||||
{ 0x1C, 0x16, 0x1C},
|
||||
{ 0x1C, 0x16, 0x1A},
|
||||
{ 0x1C, 0x16, 0x18},
|
||||
{ 0x1C, 0x16, 0x16},
|
||||
{ 0x1C, 0x16, 0x16},
|
||||
{ 0x1C, 0x16, 0x16},
|
||||
{ 0x1C, 0x18, 0x16},
|
||||
{ 0x1C, 0x1A, 0x16},
|
||||
{ 0x1C, 0x1C, 0x16},
|
||||
{ 0x1A, 0x1C, 0x16},
|
||||
{ 0x18, 0x1C, 0x16},
|
||||
{ 0x16, 0x1C, 0x16},
|
||||
{ 0x16, 0x1C, 0x16},
|
||||
{ 0x16, 0x1C, 0x16},
|
||||
{ 0x16, 0x1C, 0x18},
|
||||
{ 0x16, 0x1C, 0x1A},
|
||||
{ 0x16, 0x1C, 0x1C},
|
||||
{ 0x16, 0x1A, 0x1C},
|
||||
{ 0x16, 0x18, 0x1C},
|
||||
{ 0x16, 0x16, 0x1C},
|
||||
{ 0x00, 0x00, 0x20},
|
||||
{ 0x04, 0x00, 0x20},
|
||||
{ 0x08, 0x00, 0x20},
|
||||
{ 0x0C, 0x00, 0x20},
|
||||
{ 0x20, 0x00, 0x20},
|
||||
{ 0x20, 0x00, 0x0C},
|
||||
{ 0x20, 0x00, 0x08},
|
||||
{ 0x20, 0x00, 0x04},
|
||||
{ 0x20, 0x00, 0x00},
|
||||
{ 0x20, 0x04, 0x00},
|
||||
{ 0x20, 0x08, 0x00},
|
||||
{ 0x20, 0x0C, 0x00},
|
||||
{ 0x20, 0x20, 0x00},
|
||||
{ 0x0C, 0x20, 0x00},
|
||||
{ 0x08, 0x20, 0x00},
|
||||
{ 0x04, 0x20, 0x00},
|
||||
{ 0x00, 0x20, 0x00},
|
||||
{ 0x00, 0x20, 0x04},
|
||||
{ 0x00, 0x20, 0x08},
|
||||
{ 0x00, 0x20, 0x0C},
|
||||
{ 0x00, 0x20, 0x20},
|
||||
{ 0x00, 0x0C, 0x20},
|
||||
{ 0x00, 0x08, 0x20},
|
||||
{ 0x00, 0x04, 0x20},
|
||||
{ 0x08, 0x08, 0x20},
|
||||
{ 0x0A, 0x08, 0x20},
|
||||
{ 0x0C, 0x08, 0x20},
|
||||
{ 0x0E, 0x08, 0x20},
|
||||
{ 0x20, 0x08, 0x20},
|
||||
{ 0x20, 0x08, 0x0E},
|
||||
{ 0x20, 0x08, 0x0C},
|
||||
{ 0x20, 0x08, 0x0A},
|
||||
{ 0x20, 0x08, 0x08},
|
||||
{ 0x20, 0x0A, 0x08},
|
||||
{ 0x20, 0x0C, 0x08},
|
||||
{ 0x20, 0x0E, 0x08},
|
||||
{ 0x20, 0x20, 0x08},
|
||||
{ 0x0E, 0x20, 0x08},
|
||||
{ 0x0C, 0x20, 0x08},
|
||||
{ 0x0A, 0x20, 0x08},
|
||||
{ 0x08, 0x20, 0x08},
|
||||
{ 0x08, 0x20, 0x0A},
|
||||
{ 0x08, 0x20, 0x0C},
|
||||
{ 0x08, 0x20, 0x0E},
|
||||
{ 0x08, 0x20, 0x20},
|
||||
{ 0x08, 0x0E, 0x20},
|
||||
{ 0x08, 0x0C, 0x20},
|
||||
{ 0x08, 0x0A, 0x20},
|
||||
{ 0x0B, 0x0B, 0x20},
|
||||
{ 0x0C, 0x0B, 0x20},
|
||||
{ 0x0D, 0x0B, 0x20},
|
||||
{ 0x0F, 0x0B, 0x20},
|
||||
{ 0x20, 0x0B, 0x20},
|
||||
{ 0x20, 0x0B, 0x0F},
|
||||
{ 0x20, 0x0B, 0x0D},
|
||||
{ 0x20, 0x0B, 0x0C},
|
||||
{ 0x20, 0x0B, 0x0B},
|
||||
{ 0x20, 0x0C, 0x0B},
|
||||
{ 0x20, 0x0D, 0x0B},
|
||||
{ 0x20, 0x0F, 0x0B},
|
||||
{ 0x20, 0x20, 0x0B},
|
||||
{ 0x0F, 0x20, 0x0B},
|
||||
{ 0x0D, 0x20, 0x0B},
|
||||
{ 0x0C, 0x20, 0x0B},
|
||||
{ 0x0B, 0x20, 0x0B},
|
||||
{ 0x0B, 0x20, 0x0C},
|
||||
{ 0x0B, 0x20, 0x0D},
|
||||
{ 0x0B, 0x20, 0x0F},
|
||||
{ 0x0B, 0x20, 0x20},
|
||||
{ 0x0B, 0x0F, 0x20},
|
||||
{ 0x0B, 0x0D, 0x20},
|
||||
{ 0x0B, 0x0C, 0x20},
|
||||
/* Pad with NULL */
|
||||
};
|
||||
|
Reference in New Issue
Block a user