Configures the DIVIL component of the AMD CS5535 (Geode companion device). This patch does the following: - verifies the existence of the CS5535 by checking the DIVIL signature - configures UART1 as a NS16550A - (optionally) enables UART2 and configures it as a NS16550A - (optionally) enables the SMBus/I2C interface Signed-off-by: Ben Gardner <bgardner at wabtec.com> -------------- next part -------------- arch/i386/Kconfig | 27 ++++++ arch/i386/kernel/Makefile | 8 + arch/i386/kernel/cs5535.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 225 insertions(+) Index: linux-2.6.14/arch/i386/kernel/cs5535.c =================================================================== --- /dev/null +++ linux-2.6.14/arch/i386/kernel/cs5535.c @@ -0,0 +1,190 @@ +/** + * linux/arch/i386/kernel/cs5535.c + * + * Copyright (c) 2005 Ben Gardner <bgardner at wabtec.com> + * + * AMD CS5535 Companion Device support (AMD Geode processor). + * + * This does early configuration of the UARTs, SMB port, and GPIO. + * + * 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. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/msr.h> +#include <asm/io.h> + + +#define NAME "cs5535" + +MODULE_AUTHOR("Ben Gardner <bgardner at wabtec.com>"); +MODULE_DESCRIPTION("AMD CS5535 Driver"); +MODULE_LICENSE("GPL"); + +/* GPIO base address (from MSR_LBAR_GPIO; MSR 5140000Ch, bits 15:0) */ +u32 cs5535_gpio_base; +u32 cs5535_gpio_mask; +EXPORT_SYMBOL(cs5535_gpio_base); +EXPORT_SYMBOL(cs5535_gpio_mask); + +#define MSR_DIVIL_GLD_CAP 0x51400000 +#define DEVID_DIVIL 0x2DF0 + +#define MSR_LBAR_SMB 0x5140000B +#define MSR_LBAR_GPIO 0x5140000C + +#define MSR_UART1_CONF 0x5140003a +#define MSR_UART2_CONF 0x5140003e + +#define MSR_DIVIL_LEG_IO 0x51400014 +#define MSR_DIVIL_BALL_OPT 0x51400015 +#define MSR_IRQ_MAPY_H 0x51400021 + +struct gpio_reg_val { + u32 reg; + u32 val; +}; + +#ifdef CS5535_SMB +static const struct gpio_reg_val gpio_smb[] __initdata = +{ + { 0x04, 0x0000c000 }, /* enable OUTPUT */ + { 0x08, 0x0000c000 }, /* enable OpenDrain */ + { 0x10, 0x0000c000 }, /* enable OUT_AUX1 */ + { 0x14, 0xc0000000UL }, /* disable OUT_AUX2 */ + { 0x20, 0x0000c000 }, /* enable INPUT */ + { 0x34, 0x0000c000 }, /* enable IN_AUX1 */ +}; +#endif + +/* don't touch GPIO 3 & 4 unless UART2 is enabled */ +#ifdef CS5535_UART2 +#define RMSK 0x03180318 +#else +#define RMSK 0x03000300 +#endif + +static const struct gpio_reg_val gpio_uarts[] __initdata = +{ + { 0x00, 0x03180000 & RMSK }, /* output val */ + { 0x04, 0x02080110 & RMSK }, /* output enable */ + { 0x08, 0x02080110 & RMSK }, /* open-drain enable */ + { 0x0c, 0x03180000 & RMSK }, /* invert output */ + { 0x10, 0x02080110 & RMSK }, /* Out-Aux-1 */ + { 0x14, 0x03180000 & RMSK }, /* Out-Aux-2 */ + { 0x18, 0x03180000 & RMSK }, /* Pull-Up */ + { 0x1c, 0x03180000 & RMSK }, /* Pull-Down */ + { 0x20, 0x01100208 & RMSK }, /* Input enable */ + { 0x24, 0x03180000 & RMSK }, /* invert */ + { 0x28, 0x03180000 & RMSK }, /* filter */ + { 0x2c, 0x03180000 & RMSK }, /* event-count */ + { 0x34, 0x01100208 & RMSK }, /* in-aux1 */ + { 0x38, 0x03180000 & RMSK }, /* events */ + { 0x40, 0x03180000 & RMSK }, /* Input Pos Edge */ + { 0x44, 0x03180000 & RMSK }, /* Input Neg Edge */ +}; + +static int __init init_cs5535_divil(void) +{ + u32 low32; + u32 high32; + int idx; + + /* Check the DIVIL device ID for validation */ + rdmsr(MSR_DIVIL_GLD_CAP, low32, high32); + if ((high32 != 0) || ((low32 >> 8) != DEVID_DIVIL)) { + printk(KERN_WARNING NAME ": DIVIL device not found\n"); + return -ENODEV; + } + + /* Grab the GPIO I/O range */ + rdmsr(MSR_LBAR_GPIO, low32, high32); + cs5535_gpio_base = low32 & 0x0000ff00; + + /* Check the mask and whether GPIO is enabled */ + if (high32 != 0x0000f001) { + /* TODO: enable GPIO IO mappings via LBAR */ + printk(KERN_WARNING NAME ": GPIO not enabled\n"); + return -ENODEV; + } + + /* GPIO pins 31-29,23 are reserved, 22-16 are used for LPC, + * 9,8 are used for UART1 */ + cs5535_gpio_mask = ~0xe0ff0300UL; + +#ifdef CS5535_SMB + /* GPIO pins 14 & 15 are used for SMBus */ + cs5535_gpio_mask &= ~0x0000c000UL; + + /* Grab & reserve the SMB I/O range */ + rdmsr(MSR_LBAR_SMB, low32, high32); + + /* Check the mask and whether SMB is enabled */ + if (high32 != 0x0000F001) { + /* TODO: enable SMB IO mappings via LBAR */ + printk(KERN_WARNING NAME ": SMBus not enabled\n"); + return -ENODEV; + } + + /* Configure GPIO 14 & 15 to do SMB/ACB/I2C */ + for (idx = 0; idx < ARRAY_SIZE(gpio_smb); idx++) { + outl(gpio_smb[idx].val, + gpio_smb[idx].reg + cs5535_gpio_base); + } +#endif /* CS5535_SMB */ + + /* Configure GPIO to do UART1 and maybe UART2 */ + for (idx = 0; idx < ARRAY_SIZE(gpio_uarts); idx++) { + outl(gpio_uarts[idx].val, + gpio_uarts[idx].reg + cs5535_gpio_base); + } + + /* Set UART1 base address to 0x3f8 */ + rdmsr(MSR_DIVIL_LEG_IO, low32, high32); + low32 &= 0xfff8ffffUL; /* UART1 base IO */ + low32 |= 0x00070000; /* 0x3F8 */ + wrmsr(MSR_DIVIL_LEG_IO, low32, high32); + + /* Set UART1 interrupt to 4 */ + rdmsr(MSR_IRQ_MAPY_H, low32, high32); + low32 &= 0xf0ffffffUL; /* UART1 is on MAPY-14 */ + low32 |= 0x04000000; /* IRQ 4 */ + wrmsr(MSR_IRQ_MAPY_H, low32, high32); + + /* Set up UART1 as a NS15560A */ + wrmsr(MSR_UART1_CONF, 0x12, 0); + +#ifdef CS5535_UART2 + /* GPIO pins 3 & 4 are used for UART2 */ + cs5535_gpio_mask &= ~0x00000018UL; + + /* Set UART2 base address to 0x2f8 */ + rdmsr(MSR_DIVIL_LEG_IO, low32, high32); + low32 &= 0xff8fffffUL; /* UART2 base IO */ + low32 |= 0x00500000; /* 0x2F8 */ + wrmsr(MSR_DIVIL_LEG_IO, low32, high32); + + /* Set UART1 interrupt to 3 */ + rdmsr(MSR_IRQ_MAPY_H, low32, high32); + low32 &= 0x0fffffffUL; /* UART2 is on MAPY-15 */ + low32 |= 0x30000000; /* IRQ 3 */ + wrmsr(MSR_IRQ_MAPY_H, low32, high32); + + /* Set up UART2 as a NS15560A */ + wrmsr(MSR_UART2_CONF, 0x12, 0); +#endif /* CS5535_UART2 */ + + printk(KERN_INFO NAME ": GPIO=%#x Mask=%#x\n", + cs5535_gpio_base, cs5535_gpio_mask); + + return 0; +} + +subsys_initcall(init_cs5535_divil); + Index: linux-2.6.14/arch/i386/Kconfig =================================================================== --- linux-2.6.14.orig/arch/i386/Kconfig +++ linux-2.6.14/arch/i386/Kconfig @@ -336,6 +336,33 @@ config I8K Say Y if you intend to run this kernel on a Dell Inspiron 8000. Say N otherwise. +config CS5535 + tristate "AMD CS5535 (Geode Companion Device) support" + help + This provides basic support for the CS5535 Companion Chip for + the AMD Geode processor. + + If you don't know what to do here, say N. + +config CS5535_SMB + bool "Enable CS5535 SMBus/Access.Bus/I2C" + depends on CS5535 + default y + help + Choosing this will configure the CS5535 GPIO pins 14 & 15 for SMB. + Select I2C_CS5535 under i2c to build the SMB/I2C driver. + + Say Y if you intend to use the SMBus. + +config CS5535_UART2 + bool "Enable UART2" + depends on CS5535 + default y + help + By default, CS5535 GPIO pins 3 & 4 are configured for DDC. + + Say Y to configure them as UART2 instead. + config X86_REBOOTFIXUPS bool "Enable X86 board specific fixups for reboot" depends on X86 Index: linux-2.6.14/arch/i386/kernel/Makefile =================================================================== --- linux-2.6.14.orig/arch/i386/kernel/Makefile +++ linux-2.6.14/arch/i386/kernel/Makefile @@ -42,6 +42,14 @@ EXTRA_AFLAGS := -traditional obj-$(CONFIG_SCx200) += scx200.o +obj-$(CONFIG_CS5535) += cs5535.o +ifeq ($(CONFIG_CS5535_SMB), y) +EXTRA_CFLAGS += -DCS5535_SMB +endif +ifeq ($(CONFIG_CS5535_UART2), y) +EXTRA_CFLAGS += -DCS5535_UART2 +endif + # vsyscall.o contains the vsyscall DSO images as __initdata. # We must build both images before we can assemble it. # Note: kbuild does not track this dependency due to usage of .incbin