General Software writes their own VSA2 module for their version of the Geode BIOS, which returns a different ID then the standard VSA2. This was causing the framebuffer driver to break for most GSW boards. Signed-off-by: Jordan Crouse <jordan.crouse@amd.com> Cc: tglx@linutronix.de Cc: linux-geode@lists.infradead.org Signed-off-by: Ingo Molnar <mingo@elte.hu>
		
			
				
	
	
		
			196 lines
		
	
	
	
		
			3.8 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			196 lines
		
	
	
	
		
			3.8 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * AMD Geode southbridge support code
 | 
						|
 * Copyright (C) 2006, Advanced Micro Devices, Inc.
 | 
						|
 * Copyright (C) 2007, Andres Salomon <dilinger@debian.org>
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of version 2 of the GNU General Public License
 | 
						|
 * as published by the Free Software Foundation.
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/ioport.h>
 | 
						|
#include <linux/io.h>
 | 
						|
#include <asm/msr.h>
 | 
						|
#include <asm/geode.h>
 | 
						|
 | 
						|
static struct {
 | 
						|
	char *name;
 | 
						|
	u32 msr;
 | 
						|
	int size;
 | 
						|
	u32 base;
 | 
						|
} lbars[] = {
 | 
						|
	{ "geode-pms",   MSR_LBAR_PMS, LBAR_PMS_SIZE, 0 },
 | 
						|
	{ "geode-acpi",  MSR_LBAR_ACPI, LBAR_ACPI_SIZE, 0 },
 | 
						|
	{ "geode-gpio",  MSR_LBAR_GPIO, LBAR_GPIO_SIZE, 0 },
 | 
						|
	{ "geode-mfgpt", MSR_LBAR_MFGPT, LBAR_MFGPT_SIZE, 0 }
 | 
						|
};
 | 
						|
 | 
						|
static void __init init_lbars(void)
 | 
						|
{
 | 
						|
	u32 lo, hi;
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < ARRAY_SIZE(lbars); i++) {
 | 
						|
		rdmsr(lbars[i].msr, lo, hi);
 | 
						|
		if (hi & 0x01)
 | 
						|
			lbars[i].base = lo & 0x0000ffff;
 | 
						|
 | 
						|
		if (lbars[i].base == 0)
 | 
						|
			printk(KERN_ERR "geode:  Couldn't initialize '%s'\n",
 | 
						|
					lbars[i].name);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int geode_get_dev_base(unsigned int dev)
 | 
						|
{
 | 
						|
	BUG_ON(dev >= ARRAY_SIZE(lbars));
 | 
						|
	return lbars[dev].base;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(geode_get_dev_base);
 | 
						|
 | 
						|
/* === GPIO API === */
 | 
						|
 | 
						|
void geode_gpio_set(u32 gpio, unsigned int reg)
 | 
						|
{
 | 
						|
	u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
 | 
						|
 | 
						|
	if (!base)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* low bank register */
 | 
						|
	if (gpio & 0xFFFF)
 | 
						|
		outl(gpio & 0xFFFF, base + reg);
 | 
						|
	/* high bank register */
 | 
						|
	gpio >>= 16;
 | 
						|
	if (gpio)
 | 
						|
		outl(gpio, base + 0x80 + reg);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(geode_gpio_set);
 | 
						|
 | 
						|
void geode_gpio_clear(u32 gpio, unsigned int reg)
 | 
						|
{
 | 
						|
	u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
 | 
						|
 | 
						|
	if (!base)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* low bank register */
 | 
						|
	if (gpio & 0xFFFF)
 | 
						|
		outl((gpio & 0xFFFF) << 16, base + reg);
 | 
						|
	/* high bank register */
 | 
						|
	gpio &= (0xFFFF << 16);
 | 
						|
	if (gpio)
 | 
						|
		outl(gpio, base + 0x80 + reg);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(geode_gpio_clear);
 | 
						|
 | 
						|
int geode_gpio_isset(u32 gpio, unsigned int reg)
 | 
						|
{
 | 
						|
	u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
 | 
						|
	u32 val;
 | 
						|
 | 
						|
	if (!base)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	/* low bank register */
 | 
						|
	if (gpio & 0xFFFF) {
 | 
						|
		val = inl(base + reg) & (gpio & 0xFFFF);
 | 
						|
		if ((gpio & 0xFFFF) == val)
 | 
						|
			return 1;
 | 
						|
	}
 | 
						|
	/* high bank register */
 | 
						|
	gpio >>= 16;
 | 
						|
	if (gpio) {
 | 
						|
		val = inl(base + 0x80 + reg) & gpio;
 | 
						|
		if (gpio == val)
 | 
						|
			return 1;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(geode_gpio_isset);
 | 
						|
 | 
						|
void geode_gpio_set_irq(unsigned int group, unsigned int irq)
 | 
						|
{
 | 
						|
	u32 lo, hi;
 | 
						|
 | 
						|
	if (group > 7 || irq > 15)
 | 
						|
		return;
 | 
						|
 | 
						|
	rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
 | 
						|
 | 
						|
	lo &= ~(0xF << (group * 4));
 | 
						|
	lo |= (irq & 0xF) << (group * 4);
 | 
						|
 | 
						|
	wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(geode_gpio_set_irq);
 | 
						|
 | 
						|
void geode_gpio_setup_event(unsigned int gpio, int pair, int pme)
 | 
						|
{
 | 
						|
	u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
 | 
						|
	u32 offset, shift, val;
 | 
						|
 | 
						|
	if (gpio >= 24)
 | 
						|
		offset = GPIO_MAP_W;
 | 
						|
	else if (gpio >= 16)
 | 
						|
		offset = GPIO_MAP_Z;
 | 
						|
	else if (gpio >= 8)
 | 
						|
		offset = GPIO_MAP_Y;
 | 
						|
	else
 | 
						|
		offset = GPIO_MAP_X;
 | 
						|
 | 
						|
	shift = (gpio % 8) * 4;
 | 
						|
 | 
						|
	val = inl(base + offset);
 | 
						|
 | 
						|
	/* Clear whatever was there before */
 | 
						|
	val &= ~(0xF << shift);
 | 
						|
 | 
						|
	/* And set the new value */
 | 
						|
 | 
						|
	val |= ((pair & 7) << shift);
 | 
						|
 | 
						|
	/* Set the PME bit if this is a PME event */
 | 
						|
 | 
						|
	if (pme)
 | 
						|
		val |= (1 << (shift + 3));
 | 
						|
 | 
						|
	outl(val, base + offset);
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(geode_gpio_setup_event);
 | 
						|
 | 
						|
int geode_has_vsa2(void)
 | 
						|
{
 | 
						|
	static int has_vsa2 = -1;
 | 
						|
 | 
						|
	if (has_vsa2 == -1) {
 | 
						|
		u16 val;
 | 
						|
 | 
						|
		/*
 | 
						|
		 * The VSA has virtual registers that we can query for a
 | 
						|
		 * signature.
 | 
						|
		 */
 | 
						|
		outw(VSA_VR_UNLOCK, VSA_VRC_INDEX);
 | 
						|
		outw(VSA_VR_SIGNATURE, VSA_VRC_INDEX);
 | 
						|
 | 
						|
		val = inw(VSA_VRC_DATA);
 | 
						|
		has_vsa2 = (val == AMD_VSA_SIG || val == GSW_VSA_SIG);
 | 
						|
	}
 | 
						|
 | 
						|
	return has_vsa2;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL_GPL(geode_has_vsa2);
 | 
						|
 | 
						|
static int __init geode_southbridge_init(void)
 | 
						|
{
 | 
						|
	if (!is_geode())
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	init_lbars();
 | 
						|
	(void) mfgpt_timer_setup();
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
postcore_initcall(geode_southbridge_init);
 |