blob: 03c8e0cf55b55279f3210e25311c42fa25416d82 [file] [log] [blame]
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.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, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <arch/io.h>
#include <config.h>
#include <console/console.h>
#include <device/pci.h>
#include <device/pci_ids.h>
#include <pc80/vga_io.h>
#include <stdlib.h>
#include "vx900.h"
#define CHROME_9_HD_MIN_FB_SIZE 8
#define CHROME_9_HD_MAX_FB_SIZE 512
/**
* @file chrome9hd.c
*
* \brief Initialization for Chrome9HD integrated graphics adapters
*
* This takes care of the initialization we need to do before calling the VGA
* BIOS. The device is not documented in the VX900 datasheet.
*
* The device is documented in:
* Open Graphics Programming Manual
* Chrome9GraphicsHD Processor
* VX900 Series System Processor
* Part I: Graphics Core / 2D
*
* This document was released by VIA to the Xorg project, and is available at:
* <http://www.x.org/docs/via/OGPM_Chrome9%20HD%20DX9%20_R100_PartI_Core_2D.pdf>
*
* STATUS:
* We do the minimal initialization described in VIA documents. Running the VGA
* option ROM does not get us a usable display. We configure the framebuffer and
* the IGP is able to use it. GRUB2 and linux are capable of getting a usable
* text console, which uses the monitor's native resolution (even 1920x1080).
* The graphical console (linux) does not work properly.
* @todo
* 1. Figure out what sequence we need to do to get the VGA BIOS running
* properly. Use the code provided by VIA and compare their sequence to ours,
* fill in any missing steps, etc.
* 2. Make BAR2 and the framebuffer use the same memory space. This is a feature
* called "Direct framebuffer access" which allows us to save memory space by
* setting BAR2 of the VGA to the location in memory of the framebuffer. This
* reduces the amount of PCI MMIO space we need below 4G, and is especially
* useful considering we only have 8GB (33 bits) of memory-mapped space.
*/
/* Helper to determine the framebuffer size */
u32 chrome9hd_fb_size(void)
{
static u32 fb_size = 0;
u8 reg8, ranksize;
u32 size_mb, tom_mb, max_size_mb;
int i;
/* We do some PCI and CMOS IO to find our value, so if we've already
* found it, save some time */
if (fb_size != 0)
return fb_size;
/* FIXME: read fb_size from CMOS, but until that is implemented, start
* from 512MB */
size_mb = 512;
/* The minimum framebuffer size is 8MB. */
size_mb = MAX(size_mb, CHROME_9_HD_MIN_FB_SIZE);
const device_t mcu = dev_find_device(PCI_VENDOR_ID_VIA,
PCI_DEVICE_ID_VIA_VX900_MEMCTRL,
0);
/*
* We have two limitations on the maximum framebuffer size:
* 1) (Sanity) No more that 1/4 of system RAM
* 2) (Hardware limitation) No larger than DRAM in last rank
* Check both of these limitations and apply them to our framebuffer */
tom_mb = (pci_read_config16(mcu, 0x88) & 0x07ff) << (24 - 20);
max_size_mb = tom_mb >> 2;
if (size_mb > max_size_mb) {
printk(BIOS_ALERT, "The framebuffer size of of %dMB is larger"
" than 1/4 of available memory.\n"
" Limiting framebuffer to %dMB\n", size_mb, max_size_mb);
size_mb = max_size_mb;
}
/* Now handle limitation #2
* Look at the ending address of the memory ranks, from last to first,
* until we find one that is not zero. That is our last rank, and its
* size is the limit of our framebuffer. */
/* FIXME: This has a bug. If we remap memory above 4G, we consider the
* memory hole as part of our RAM. Thus if we install 3G, with a TOLM of
* 2.5G, our TOM will be at 5G and we'll assume we have 5G RAM instead
* of the actual 3.5G */
for (i = VX900_MAX_MEM_RANKS - 1; i > -1; i--) {
reg8 = pci_read_config8(mcu, 0x40 + i);
if (reg8 == 0)
continue;
/* We've reached the last populated rank */
ranksize = reg8 - pci_read_config8(mcu, 0x48 + i);
max_size_mb = ranksize << 6;
/* That's it. We got what we needed. */
break;
};
if (size_mb > max_size_mb) {
printk(BIOS_ALERT, "The framebuffer size of %dMB is larger"
" than size of the last DRAM rank.\n"
" Limiting framebuffer to %dMB\n", size_mb, max_size_mb);
size_mb = max_size_mb;
}
/* Now round the framebuffer size to the closest power of 2 */
u8 fb_pow = 0;
while (size_mb >> fb_pow)
fb_pow++;
fb_pow--;
size_mb = (1 << fb_pow);
/* We store the framebuffer size in bytes, for simplicity */
fb_size = size_mb << 20;
return fb_size;
}
/**
* vx900_int15
*
* \brief INT15 helpers for Chrome9HD IGP
*
* The following are helpers for INT15 handlers for the VGA BIOS. The full set
* of INT15 callbacks is described in
*
* VIA/S3Graphics
* Video BIOS External Interface Specification for Chrome9 Series IGP
* VX900 Series
*
* This document is only available under NDA, however, the callbacks are very
* similar to other VIA/Intel IGP callbacks.
*
* Callback 0x5f18 is the most important one. It informs the VGA BIOS of the
* RAM speed and framebuffer size. The other callbacks seem to be optional.
* @{
*/
/**
* \brief Get X86_BL value for VGA INT15 function 5f18
*
* Int15 5f18 lets the VGA BIOS know the framebuffer size and the memory speed.
* This handler is very important. If it is not implemented, the VGA BIOS will
* not work correctly.
*
* To use, just call this from the 15f18 handler, and place the return value in
* X86_BL
*
* @code{.c}
* case 0x5f18:
* X86_BX = vx900_int15_get_5f18_bl();
* res = 0;
* break;
* @endcode
*
*/
u8 vx900_int15_get_5f18_bl(void)
{
u8 reg8, ret;
device_t dev;
/*
* BL Bit[7:4]
* Memory Data Rate (not to be confused with fCLK)
* 0000: 66MHz
* 0001: 100MHz
* 0010: 133MHz
* 0011: 200MHz ( DDR200 )
* 0100: 266MHz ( DDR266 )
* 0101: 333MHz ( DDR333 )
* 0110: 400MHz ( DDR400 )
* 0111: 533MHz ( DDR I/II 533)
* 1000: 667MHz ( DDR I/II 667)
* 1001: 800MHz ( DDR3 800)
* 1010: 1066MHz ( DDR3 1066)
* 1011: 1333MHz ( DDR3 1333)
* Bit[3:0]
* N: Frame Buffer Size 2^N MB
*/
dev = dev_find_slot(0, PCI_DEVFN(0, 3));
reg8 = pci_read_config8(dev, 0xa1);
ret = (u32) ((reg8 & 0x70) >> 4) + 2;
reg8 = pci_read_config8(dev, 0x90);
reg8 = ((reg8 & 0x07) + 3) << 4;
ret |= (u32) reg8;
return ret;
}
/** @} */
static void chrome9hd_set_sid_vid(u16 vendor, u16 device)
{
vga_sr_write(0x36, vendor >> 8); /* SVID high byte */
vga_sr_write(0x35, vendor & 0xff); /* SVID low byte */
vga_sr_write(0x38, device >> 8); /* SID high byte */
vga_sr_write(0x37, device & 0xff); /* SID low byte */
}
static void chrome9hd_handle_uma(device_t dev)
{
/* Mirror mirror, shiny glass, tell me that is not my ass */
u32 fb_size = chrome9hd_fb_size() >> 20;
u8 fb_pow = 0;
while (fb_size >> fb_pow)
fb_pow++;
fb_pow--;
/* Step 6 - Let MCU know the framebuffer size */
device_t mcu = dev_find_device(PCI_VENDOR_ID_VIA,
PCI_DEVICE_ID_VIA_VX900_MEMCTRL, 0);
pci_mod_config8(mcu, 0xa1, 7 << 4, (fb_pow - 2) << 4);
/* Step 7 - Let GFX know the framebuffer size (through PCI and IOCTL)
* The size we set here affects the behavior of BAR2, and the amount of
* MMIO space it requests. The default is 512MB, so if we don't set this
* before reading the resources, we could waste space below 4G */
pci_write_config8(dev, 0xb2, ((0xff << (fb_pow - 2)) & ~(1 << 7)));
vga_sr_write(0x68, (0xff << (fb_pow - 1)));
/* And also that the framebuffer is in the system, RAM */
pci_mod_config8(dev, 0xb0, 0, 1 << 0);
}
/**
* \brief Initialization sequence before running the VGA BIOS
*
* This is the initialization sequence described in:
*
* BIOS Porting Guide
* VX900 Series
* All-in-One System Processor
*
* This document is only available under NDA.
*/
static void chrome9hd_biosguide_init_seq(device_t dev)
{
device_t mcu = dev_find_device(PCI_VENDOR_ID_VIA,
PCI_DEVICE_ID_VIA_VX900_MEMCTRL, 0);
device_t host = dev_find_device(PCI_VENDOR_ID_VIA,
PCI_DEVICE_ID_VIA_VX900_HOST_BR, 0);
/* Step 1 - Enable VGA controller */
/* FIXME: This is the VGA hole @ 640k-768k, and the vga port io
* We need the port IO, but can we disable the memory hole? */
pci_mod_config8(mcu, 0xa4, 0, (1 << 7)); /* VGA memory hole */
/* Step 2 - Forward MDA cycles to GFX */
pci_mod_config8(host, 0x4e, 0, (1 << 1));
/* Step 3 - Enable GFX I/O space */
pci_mod_config8(dev, PCI_COMMAND, 0, PCI_COMMAND_IO);
/* Step 4 - Enable video subsystem */
vga_enable_mask((1 << 0), (1 << 0));
/* FIXME: VGA IO Address Select. 3B5 or 3D5? */
vga_misc_mask((1 << 0), (1 << 0));
/* Step 5 - Unlock accessing of IO space */
vga_sr_write(0x10, 0x01);
chrome9hd_handle_uma(dev);
uint64_t gfx_base = get_uma_memory_base();
if (gfx_base == 0)
die("uma_memory_base not set. Abandon ship!\n");
/* Step 8 - Enable memory base register on the GFX */
vga_sr_write(0x6d, (gfx_base >> 21) & 0xff); /* base 28:21 */
vga_sr_write(0x6e, (gfx_base >> 29) & 0xff); /* base 36:29 */
vga_sr_write(0x6f, 0x00); /* base 43:37 */
/* Step 9 - Set SID/VID */
chrome9hd_set_sid_vid(0x1106, 0x7122);
}
static void chrome9hd_init(device_t dev)
{
printk(BIOS_DEBUG, "======================================================\n");
printk(BIOS_DEBUG, "== Chrome9 HD INIT\n");
printk(BIOS_DEBUG, "======================================================\n");
chrome9hd_biosguide_init_seq(dev);
/* Prime PLL FIXME: bad comment */
vga_sr_mask(0x3c, 1 << 2, 1 << 2);
/* FIXME: recheck; VGA IO Address Select. 3B5 or 3D5? */
vga_misc_mask(1 << 0, 1 << 0);
/* FIXME: recheck; Enable Base VGA 16 Bits Decode */
////pci_mod_config8(host, 0x4e, 0, 1<<4);
u32 fb_address = pci_read_config32(dev, PCI_BASE_ADDRESS_2);
fb_address &= ~0x0F;
if (!fb_address) {
printk(BIOS_WARNING, "Chrome9HD: No FB BAR assigned!\n");
return;
}
printk(BIOS_INFO, "Chrome: Using %dMB Framebuffer at 0x%08X.\n",
256, fb_address);
printk(BIOS_DEBUG, "Initializing VGA...\n");
pci_dev_init(dev);
printk(BIOS_DEBUG, "Enable VGA console\n");
dump_pci_device(dev);
}
static void chrome9hd_enable(device_t dev)
{
device_t mcu = dev_find_device(PCI_VENDOR_ID_VIA,
PCI_DEVICE_ID_VIA_VX900_MEMCTRL, 0);
/* FIXME: here? -=- ACLK 250MHz */
pci_mod_config8(mcu, 0xbb, 0, 0x01);
}
static void chrome9hd_disable(device_t dev)
{
device_t mcu = dev_find_device(PCI_VENDOR_ID_VIA,
PCI_DEVICE_ID_VIA_VX900_MEMCTRL, 0);
/* Disable GFX - This step effectively renders the GFX inert
* It won't even show up as a PCI device during enumeration */
pci_mod_config8(mcu, 0xa1, 1 << 7, 0);
}
static struct device_operations chrome9hd_operations = {
.read_resources = pci_dev_read_resources,
.set_resources = pci_dev_set_resources,
.enable_resources = pci_dev_enable_resources,
.init = chrome9hd_init,
.disable = chrome9hd_disable,
.enable = chrome9hd_enable,
.ops_pci = 0,
};
static const struct pci_driver chrome9hd_driver __pci_driver = {
.ops = &chrome9hd_operations,
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_VX900_VGA,
};