summaryrefslogtreecommitdiff
path: root/src/cpu/via/nano
diff options
context:
space:
mode:
Diffstat (limited to 'src/cpu/via/nano')
-rw-r--r--src/cpu/via/nano/Kconfig41
-rw-r--r--src/cpu/via/nano/Makefile.inc33
-rw-r--r--src/cpu/via/nano/nano_init.c204
-rw-r--r--src/cpu/via/nano/update_ucode.c150
-rw-r--r--src/cpu/via/nano/update_ucode.h73
5 files changed, 501 insertions, 0 deletions
diff --git a/src/cpu/via/nano/Kconfig b/src/cpu/via/nano/Kconfig
new file mode 100644
index 0000000000..3b1c2137e8
--- /dev/null
+++ b/src/cpu/via/nano/Kconfig
@@ -0,0 +1,41 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+config CPU_VIA_NANO
+ bool
+
+if CPU_VIA_NANO
+
+config CPU_SPECIFIC_OPTIONS
+ def_bool y
+ select UDELAY_TSC
+ select MMX
+ select SSE2
+ select CACHE_AS_RAM
+ select CPU_MICROCODE_IN_CBFS
+
+config DCACHE_RAM_BASE
+ hex
+ default 0xffe00000
+
+config DCACHE_RAM_SIZE
+ hex
+ default 0x8000
+
+endif # CPU_VIA_NANO
diff --git a/src/cpu/via/nano/Makefile.inc b/src/cpu/via/nano/Makefile.inc
new file mode 100644
index 0000000000..6b0c1e934a
--- /dev/null
+++ b/src/cpu/via/nano/Makefile.inc
@@ -0,0 +1,33 @@
+##
+## 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.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+subdirs-y += ../../x86/tsc
+subdirs-y += ../../x86/mtrr
+subdirs-y += ../../x86/lapic
+subdirs-y += ../../x86/cache
+subdirs-y += ../../x86/smm
+
+driver-y += nano_init.c
+ramstage-y += update_ucode.c
+
+# We need to hear from VIA to get permission to include this file in the
+# official coreboot repository. Until then, we leave this commented out
+# cpu-microcode-y += nano_ucode_blob.c
+
+cpu_incs += $(src)/cpu/via/car/cache_as_ram.inc
diff --git a/src/cpu/via/nano/nano_init.c b/src/cpu/via/nano/nano_init.c
new file mode 100644
index 0000000000..417119f356
--- /dev/null
+++ b/src/cpu/via/nano/nano_init.c
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "update_ucode.h"
+#include <console/console.h>
+#include <device/device.h>
+#include <cpu/cpu.h>
+#include <cpu/x86/mtrr.h>
+#include <cpu/x86/msr.h>
+#include <cpu/x86/lapic.h>
+#include <cpu/x86/cache.h>
+#include <delay.h>
+
+#define MODEL_NANO 0x2
+#define MODEL_NANO_3000_B0 0x8
+#define MODEL_NANO_3000_B2 0xa
+
+#define MSR_IA32_PERF_STATUS 0x00000198
+#define MSR_IA32_PERF_CTL 0x00000199
+#define MSR_IA32_MISC_ENABLE 0x000001a0
+#define NANO_MYSTERIOUS_MSR 0x120e
+
+static void nano_finish_fid_vid_transition(void)
+{
+
+ msr_t msr;
+ /* Wait until the power transition ends */
+ int cnt = 0;
+ do {
+ udelay(16);
+ msr = rdmsr(MSR_IA32_PERF_STATUS);
+ cnt++;
+ if (cnt > 128) {
+ printk(BIOS_WARNING,
+ "Error while updating multiplier and voltage\n");
+ break;
+ }
+ } while (msr.lo & ((1 << 16) | (1 << 17)));
+
+ /* Print the new FID and Voltage */
+ u8 cur_vid = (msr.lo >> 0) & 0xff;
+ u8 cur_fid = (msr.lo >> 8) & 0xff;
+ printk(BIOS_INFO, "New CPU multiplier: %dx\n", cur_fid);
+ printk(BIOS_INFO, "New Voltage ID : %dx\n", cur_vid);
+}
+
+static void nano_set_max_fid_vid(void)
+{
+ msr_t msr;
+ /* Get voltage and frequency info */
+ msr = rdmsr(MSR_IA32_PERF_STATUS);
+ u8 min_fid = (msr.hi >> 24);
+ u8 max_fid = (msr.hi >> 8) & 0xff;
+ u8 min_vid = (msr.hi >> 16) & 0xff;
+ u8 max_vid = (msr.hi >> 0) & 0xff;
+ u8 cur_vid = (msr.lo >> 0) & 0xff;
+ u8 cur_fid = (msr.lo >> 8) & 0xff;
+
+ printk(BIOS_INFO, "CPU multiplier: %dx (min %dx; max %dx)\n",
+ cur_fid, min_fid, max_fid);
+ printk(BIOS_INFO, "Voltage ID : %dx (min %dx; max %dx)\n",
+ cur_vid, min_vid, max_vid);
+
+ if( (cur_fid != max_fid) || (cur_vid != max_vid) ) {
+ /* Set highest frequency and VID */
+ msr.lo = msr.hi;
+ msr.hi = 0;
+ wrmsr(MSR_IA32_PERF_CTL, msr);
+ /* Wait for the transition to complete, otherwise, the CPU
+ * might reset itself repeatedly */
+ nano_finish_fid_vid_transition();
+ }
+ /* As a side note, if we didn't update the microcode by this point, the
+ * second PLL will not lock correctly. The clock will still be provided
+ * by the first PLL, and execution will continue normally, ___until___
+ * the CPU switches PLL. Once that happens we will no longer have a
+ * working clock source, and the CPU will hang
+ * Moral of the story: update the microcode, or don't change FID
+ * This check is handled before calling nano_power() */
+}
+
+static void nano_power(void)
+{
+ msr_t msr;
+ /* Enable Powersaver */
+ msr = rdmsr(MSR_IA32_MISC_ENABLE);
+ msr.lo |= (1 << 16);
+ wrmsr(MSR_IA32_MISC_ENABLE, msr);
+
+ /* Enable 6 bit or 7-bit VRM support
+ * This MSR is not documented by VIA docs, other than setting these
+ * bits */
+ msr = rdmsr(NANO_MYSTERIOUS_MSR);
+ msr.lo |= ( (1<<7) | (1<<4) );
+ /* FIXME: Do we have a 6-bit or 7-bit VRM?
+ * set bit [5] for 7-bit, or don't set it for 6 bit VRM
+ * This will probably require a Kconfig option
+ * My board has a 7-bit VRM, so I can't test the 6-bit VRM stuff */
+ msr.lo |= (1<<5);
+ wrmsr(NANO_MYSTERIOUS_MSR, msr);
+
+ /* Set the maximum frequency and voltage */
+ nano_set_max_fid_vid();
+
+ /* Enable TM3 */
+ msr = rdmsr(MSR_IA32_MISC_ENABLE);
+ msr.lo |= ( (1<<3) | (1<<13) );
+ wrmsr(MSR_IA32_MISC_ENABLE, msr);
+
+ u8 stepping = ( cpuid_eax(0x1) ) &0xf;
+ if(stepping >= MODEL_NANO_3000_B0) {
+ /* Hello Nano 3000. The Terminator needs a CPU upgrade */
+ /* Enable C1e, C2e, C3e, and C4e states */
+ msr = rdmsr(MSR_IA32_MISC_ENABLE);
+ msr.lo |= ( (1<<25) | (1<<26) | (1<<31)); /* C1e, C2e, C3e */
+ msr.hi |= (1<<0); /* C4e */
+ wrmsr(MSR_IA32_MISC_ENABLE, msr);
+ }
+
+ /* Lock on Powersaver */
+ msr = rdmsr(MSR_IA32_MISC_ENABLE);
+ msr.lo |= (1<<20);
+ wrmsr(MSR_IA32_MISC_ENABLE, msr);
+}
+
+static void nano_init(device_t dev)
+{
+ struct cpuinfo_x86 c;
+
+ get_fms(&c, dev->device);
+
+ /* We didn't test this on the Nano 1000/2000 series, so warn the user */
+ if(c.x86_mask < MODEL_NANO_3000_B0) {
+ printk(BIOS_EMERG, "WARNING: This CPU has not been tested. "
+ "Please report any issues encountered. \n");
+ }
+ switch (c.x86_mask) {
+ case MODEL_NANO:
+ printk(BIOS_INFO, "VIA Nano");
+ break;
+ case MODEL_NANO_3000_B0:
+ printk(BIOS_INFO, "VIA Nano 3000 rev B0");
+ break;
+ case MODEL_NANO_3000_B2:
+ printk(BIOS_INFO, "VIA Nano 3000 rev B2");
+ break;
+ default:
+ printk(BIOS_EMERG, "Stepping not recognized: %x\n", c.x86_mask);
+ }
+ printk(BIOS_INFO, "\n");
+
+ /* We only read microcode from CBFS. If we don't have any microcode in
+ * CBFS, we'll just get back with 0 updates. User choice FTW. */
+ unsigned int n_updates = nano_update_ucode();
+
+ if(n_updates != 0){
+ nano_power();
+ } else {
+ /* Changing the frequency or voltage without first updating the
+ * microcode will hang the CPU, so just don't do it */
+ printk(BIOS_EMERG, "WARNING: CPU Microcode not updated.\n"
+ " Will not change frequency, as this may hang the CPU.\n");
+ }
+
+ /* Turn on cache */
+ x86_enable_cache();
+ /* Set up Memory Type Range Registers */
+ x86_setup_mtrrs();
+ x86_mtrr_check();
+ /* Enable the local cpu apics */
+ setup_lapic();
+}
+
+static struct device_operations cpu_dev_ops = {
+ .init = nano_init,
+};
+
+static struct cpu_device_id cpu_table[] = {
+ {X86_VENDOR_CENTAUR, 0x06f2}, // VIA NANO 1000/2000 Series
+ {X86_VENDOR_CENTAUR, 0x06f8}, // VIA NANO 3000 rev B0
+ {X86_VENDOR_CENTAUR, 0x06fa}, // VIA NANO 3000 rev B2
+ {0, 0},
+};
+
+static const struct cpu_driver driver __cpu_driver = {
+ .ops = &cpu_dev_ops,
+ .id_table = cpu_table,
+};
diff --git a/src/cpu/via/nano/update_ucode.c b/src/cpu/via/nano/update_ucode.c
new file mode 100644
index 0000000000..8f7ee224a4
--- /dev/null
+++ b/src/cpu/via/nano/update_ucode.c
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "update_ucode.h"
+#include <cpu/x86/msr.h>
+#include <console/console.h>
+#include <stddef.h>
+#include <cpu/cpu.h>
+#include <arch/cpu.h>
+#include <cbfs.h>
+
+static ucode_update_status nano_apply_ucode(const nano_ucode_header *ucode)
+{
+ printk(BIOS_SPEW, "Attempting to apply microcode update\n");
+
+ msr_t msr;
+ /* Address of ucode block goes in msr.lo for 32-bit mode
+ * Now remember, we need to pass the address of the actual microcode,
+ * not the header. The header is just there to help us. */
+ msr.lo = (unsigned int)(&(ucode->ucode_start));
+ msr.hi = 0;
+ wrmsr(MSR_IA32_BIOS_UPDT_TRIG, msr);
+
+ /* Let's see if we updated succesfully */
+ msr = rdmsr(MSR_UCODE_UPDATE_STATUS);
+
+ return msr.lo & 0x07;
+}
+
+static void nano_print_ucode_info(const nano_ucode_header *ucode)
+{
+ printk(BIOS_SPEW, "Microcode update information:\n");
+ printk(BIOS_SPEW, "Name: %8s\n", ucode->name );
+ printk(BIOS_SPEW, "Date: %u/%u/%u\n", ucode->month,
+ ucode->day, ucode->year );
+}
+
+static ucode_validity nano_ucode_is_valid(const nano_ucode_header *ucode)
+{
+ /* We must have a valid signature */
+ if(ucode->signature != NANO_UCODE_SIGNATURE)
+ return NANO_UCODE_SIGNATURE_ERROR;
+ /* The size of the head must be exactly 12 double words */
+ if( (ucode->total_size - ucode->payload_size) != NANO_UCODE_HEADER_SIZE)
+ return NANO_UCODE_WRONG_SIZE;
+
+ /* How about a checksum ? Checksum must be 0
+ * Two's complement done over the entire file, including the header */
+ int i;
+ u32 check = 0;
+ u32 *raw = (void*) ucode;
+ for(i = 0 ; i < ((ucode->total_size) >> 2); i++) {
+ check += raw[i];
+ }
+ if(check != 0)
+ return NANO_UCODE_CHECKSUM_FAIL;
+ /* Made it here huh? Then it looks valid to us.
+ * If there's anything else wrong, the CPU will reject the update */
+ return NANO_UCODE_VALID;
+}
+
+static void nano_print_ucode_status(ucode_update_status stat)
+{
+ switch(stat)
+ {
+ case UCODE_UPDATE_SUCCESS:
+ printk(BIOS_INFO, "Microcode update succesful.\n");
+ break;
+ case UCODE_UPDATE_FAIL:
+ printk(BIOS_ALERT, "Microcode update failed, bad environment."
+ "Update was not applied.\n");
+ break;
+ case UCODE_UPDATE_WRONG_CPU:
+ printk(BIOS_ALERT, "Update not applicable to this CPU.\n");
+ break;
+ case UCODE_INVALID_UPDATE_BLOCK:
+ printk(BIOS_ALERT, "Microcode block invalid."
+ "Update was not applied.\n");
+ break;
+ default:
+ printk(BIOS_ALERT, "Unknown status. No update applied.\n");
+ }
+}
+
+unsigned int nano_update_ucode(void)
+{
+ size_t i;
+ unsigned int n_updates = 0;
+ const struct cbfs_file *cbfs_ucode;
+ u32 fms = cpuid_eax(0x1);
+
+ cbfs_ucode = cbfs_find("cpu_microcode_blob.bin");
+ /* Oops, did you forget to include the microcode ? */
+ if(cbfs_ucode == NULL) {
+ printk(BIOS_ALERT, "WARNING: No microcode file found in CBFS. "
+ "Aborting microcode updates\n");
+ return 0;
+ }
+
+ /* Considering we are running with eXecute-In-Place (XIP), there's no
+ * need to worry that accessing data from ROM will slow us down.
+ * Microcode data should be aligned to a 4-byte boundary, but CBFS
+ * already does that for us (Do you, CBFS?) */
+ const u32 *ucode_data = CBFS_SUBHEADER(cbfs_ucode);
+ const u32 ucode_len = ntohl(cbfs_ucode->len);
+
+ /* We might do a lot of loops searching for the microcode updates, but
+ * keep in mind, nano_ucode_is_valid searches for the signature before
+ * doing anything else. */
+ for( i = 0; i < (ucode_len >> 2); /* don't increment i here */ )
+ {
+ ucode_update_status stat;
+ const nano_ucode_header * ucode = (void *)(&ucode_data[i]);
+ if(nano_ucode_is_valid(ucode) != NANO_UCODE_VALID) {
+ i++;
+ continue;
+ }
+ /* Since we have a valid microcode, there's no need to search
+ * in this region, so we restart our search at the end of this
+ * microcode */
+ i += (ucode->total_size >> 2);
+ /* Is the microcode compatible with our CPU? */
+ if(ucode->applicable_fms != fms) continue;
+ /* For our most curious users */
+ nano_print_ucode_info(ucode);
+ /* The meat of the pie */
+ stat = nano_apply_ucode(ucode);
+ /* The user might want to know how the update went */
+ nano_print_ucode_status(stat);
+ if(stat == UCODE_UPDATE_SUCCESS) n_updates++;
+ }
+
+ return n_updates;
+}
diff --git a/src/cpu/via/nano/update_ucode.h b/src/cpu/via/nano/update_ucode.h
new file mode 100644
index 0000000000..6a22d183a7
--- /dev/null
+++ b/src/cpu/via/nano/update_ucode.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __UPDATE_UCODE_H
+#define __UPDATE_UCODE_H
+
+#include <console/console.h>
+#include <cpu/cpu.h>
+
+#define MSR_IA32_BIOS_UPDT_TRIG 0x00000079
+#define MSR_IA32_BIOS_SIGN_ID 0x0000008b
+#define MSR_UCODE_UPDATE_STATUS 0x00001205
+
+#define NANO_UCODE_SIGNATURE 0x53415252
+#define NANO_UCODE_HEADER_SIZE 0x30
+
+/* These are values returned by the CPU after we attempt microcode updates.
+ * We care what these values are exactly, so we define them to be sure */
+typedef enum {
+ UCODE_UPDATE_NOT_ATTEMPTED = 0x0,
+ UCODE_UPDATE_SUCCESS = 0x1,
+ UCODE_UPDATE_FAIL = 0x2,
+ UCODE_UPDATE_WRONG_CPU = 0x3,
+ UCODE_INVALID_UPDATE_BLOCK = 0x4,
+} ucode_update_status;
+
+
+typedef enum {
+ NANO_UCODE_VALID = 0, /* We only care that valid == 0 */
+ NANO_UCODE_SIGNATURE_ERROR,
+ NANO_UCODE_WRONG_SIZE,
+ NANO_UCODE_CHECKSUM_FAIL,
+} ucode_validity;
+
+typedef struct {
+ u32 signature; /* NANO_UCODE_SIGNATURE */
+ u32 update_revision; /* Revision of the update header */
+ u16 year; /* Year of patch release */
+ u8 day; /* Day of patch release */
+ u8 month; /* Month of patch release */
+ u32 applicable_fms; /* Fam/model/stepping to which ucode applies */
+ u32 checksum; /* Two's complement checksum of ucode+header */
+ u32 loader_revision; /* Revision of hardware ucode update loader*/
+ u32 rfu_1; /* Reservod for future use */
+ u32 payload_size; /* Size of the ucode payload only */
+ u32 total_size; /* Size of the ucode, including header */
+ char name[8]; /* ASCII string of ucode filename */
+ u32 rfu_2; /* Reservod for future use */
+ /* First double-word of the ucode payload
+ * Its address represents the beginning of the ucode update we need to
+ * send to the CPU */
+ u32 ucode_start;
+
+} nano_ucode_header;
+
+unsigned int nano_update_ucode(void);
+
+#endif /* __UPDATE_UCODE_H */