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:
Stefan Reinauer
2012-04-27 02:31:28 +02:00
parent aee1869fcf
commit ae5e11d7cd
70 changed files with 59 additions and 60 deletions

View File

@@ -26,3 +26,4 @@ subdirs-y += oxford
subdirs-y += sil
subdirs-y += trident
subdirs-y += ics
subdirs-$(CONFIG_ARCH_X86) += pc80

View 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
View 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
View 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
}

View 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
View 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();
}

View 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 */

View 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
View 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);
}

View 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);
}

View 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
View 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! */
}

View 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 */

File diff suppressed because it is too large Load Diff

View 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);
}

View 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 */
};