diff -urN linux-2.4.36-wt2.geode-id/arch/i386/kernel/Makefile linux-2.4.36-wt2.geode-mfgpt/arch/i386/kernel/Makefile --- linux-2.4.36-wt2.geode-id/arch/i386/kernel/Makefile 2006-02-26 22:49:54 +0100 +++ linux-2.4.36-wt2.geode-mfgpt/arch/i386/kernel/Makefile 2008-08-18 18:41:23 +0200 @@ -61,5 +61,10 @@ obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o obj-$(CONFIG_X86_LONGRUN) += longrun.o obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.o +obj-$(CONFIG_MGEODE) += geode-mfgpt.o + +ifdef CONFIG_MGEODE +export-objs += geode-mfgpt.o +endif include $(TOPDIR)/Rules.make diff -urN linux-2.4.36-wt2.geode-id/arch/i386/kernel/geode-mfgpt.c linux-2.4.36-wt2.geode-mfgpt/arch/i386/kernel/geode-mfgpt.c --- linux-2.4.36-wt2.geode-id/arch/i386/kernel/geode-mfgpt.c 1970-01-01 01:00:00 +0100 +++ linux-2.4.36-wt2.geode-mfgpt/arch/i386/kernel/geode-mfgpt.c 2008-08-18 18:45:17 +0200 @@ -0,0 +1,269 @@ +/* Driver/API for AMD Geode Multi-Function General Purpose Timers (MFGPT) + * + * Copyright (C) 2006, Advanced Micro Devices, Inc. + * Backported to 2.4 by Willy Tarreau + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* for pci_iomap/pci_iounmap */ +#include "mfgpt-compat.h" + +#define MFGPT_IRQ_MSR 0x51400028 +#define MFGPT_NR_MSR 0x51400029 + +#define MFGPT_MAX_TIMERS 8 +#define MFGPT_PCI_BAR 2 + +#define F_AVAIL 0x01 +#define F_RESERVED 0x02 + +static void *mfgpt_iobase; +static int reserved_mask = 0; + +static struct mfgpt_timer_t { + int index; + int flags; + struct module *owner; +} mfgpt_timers[MFGPT_MAX_TIMERS]; + +void +geode_mfgpt_write(int i, u16 r, u16 v) +{ + outw(v, (unsigned long)(mfgpt_iobase + (r + (i * 8)))); +} + +EXPORT_SYMBOL(geode_mfgpt_write); + +u16 +geode_mfgpt_read(int i, u16 r) +{ + return inw((unsigned long)(mfgpt_iobase + (r + (i * 8)))); +} + +EXPORT_SYMBOL(geode_mfgpt_read); + +void +geode_mfgpt_toggle_event(int timer, int cmp, int event, int setup) +{ + u32 msr, mask, value, dummy; + int shift = (cmp == MFGPT_CMP1) ? 0 : 8; + + switch(event) { + case MFGPT_EVENT_RESET: + msr = MFGPT_NR_MSR; + mask = 1 << (timer + 24); + break; + + case MFGPT_EVENT_NMI: + msr = MFGPT_NR_MSR; + mask = 1 << (timer + shift); + break; + + case MFGPT_EVENT_IRQ: + msr = MFGPT_IRQ_MSR; + mask = 1 << (timer + shift); + break; + + default: + return; + } + + rdmsr(msr, value, dummy); + + if (setup) + value |= mask; + else + value &= ~mask; + + wrmsr(msr, value, dummy); +} + +EXPORT_SYMBOL(geode_mfgpt_toggle_event); + +void +geode_mfgpt_set_irq(int timer, int cmp, int irq, int setup) +{ + u32 val, dummy; + int offset; + + geode_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, setup); + + rdmsr(0x51400022, val, dummy); + + offset = (timer % 4) * 4; + + val &= ~((0xF << offset) | (0xF << (offset + 16))); + + if (setup) { + val |= (irq & 0x0F) << (offset); + val |= (irq & 0x0F) << (offset + 16); + } + + wrmsr(0x51400022, val, dummy); +} + +/* Allow for disabling of MFGPTs */ +static int disable; +static int __init mfgpt_disable(char *s) +{ + disable = 1; + return 1; +} +__setup("nomfgpt", mfgpt_disable); + +/* Reset the MFGPT timers. This is required by some broken BIOSes which already + * do the same and leave the system in an unstable state. TinyBIOS 0.98 is + * affected at least (0.99 is OK with MFGPT workaround left to off). + */ +static int __init mfgpt_fix(char *s) +{ + u32 val, dummy; + + /* The following udocumented bit resets the MFGPT timers */ + val = 0xFF; dummy = 0; + wrmsr(0x5140002B, val, dummy); + return 1; +} +__setup("mfgptfix", mfgpt_fix); + +int +geode_mfgpt_alloc_timer(int timer, int domain, struct module *owner) +{ + int i; + + /* If they requested a specific timer, try to honor that */ + if (mfgpt_iobase == NULL) + return -ENODEV; + + if (timer != MFGPT_TIMER_ANY) { + if (mfgpt_timers[timer].flags & F_AVAIL) { + mfgpt_timers[timer].flags &= ~F_AVAIL; + mfgpt_timers[timer].owner = owner; + + printk("geode-mfgpt: Registered timer %d\n", timer); + return timer; + } + else if (mfgpt_timers[timer].owner == owner) + return timer; + + /* Request failed - somebody else owns it */ + return -1; + } + + /* Try to find an available timer */ + + for(i = 0; i < MFGPT_MAX_TIMERS; i++) { + + if ((mfgpt_timers[i].flags & F_AVAIL) && + !(mfgpt_timers[i].flags & F_RESERVED)) { + mfgpt_timers[i].flags &= ~F_AVAIL; + mfgpt_timers[i].owner = owner; + + printk("geode-mfgpt: Registered timer %d\n", i); + return i; + } + + if (i == 5 && domain == MFGPT_DOMAIN_WORKING) + break; + } + + /* No timers available - too bad */ + return -1; +} + +EXPORT_SYMBOL(geode_mfgpt_alloc_timer); + +static int +mfgpt_setup_timer(struct pci_dev *pdev, int timer) +{ + u16 val = geode_mfgpt_read(timer, MFGPT_REG_SETUP); + mfgpt_timers[timer].index = timer; + + if (reserved_mask & (1 << timer)) + mfgpt_timers[timer].flags |= F_RESERVED; + + if (!(val & MFGPT_SETUP_SETUP)) { + mfgpt_timers[timer].flags = F_AVAIL; + return 1; + } + + return 0; +} + +static struct pci_device_id geode_sbdevs[] = { + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) } +}; + +static int __init +geode_mfgpt_init(void) +{ + struct pci_dev *pdev = NULL; + int i, ret, dev, count = 0; + + if (disable) { + printk("geode-mfgpt: Skipping MFGPT setup\n"); + return -ENODEV; + } + + if (!is_geode()) + { + goto not_geode; + } + + for (dev = 0; dev < ARRAY_SIZE(geode_sbdevs); dev++) { + pdev = pci_find_device(geode_sbdevs[dev].vendor, + geode_sbdevs[dev].device, NULL); + + if (pdev != NULL) + break; + } + + if (pdev == NULL) { + printk(KERN_ERR "geode-mfgpt: No PCI devices found\n"); + goto err; + } + + if ((ret = pci_enable_device_bars(pdev, 1 << MFGPT_PCI_BAR))) + goto err; + + if ((ret = pci_request_region(pdev, MFGPT_PCI_BAR, "geode-mfgpt"))) + goto err; + + mfgpt_iobase = pci_iomap(pdev, MFGPT_PCI_BAR, 64); + + if (mfgpt_iobase == NULL) + goto ereq; + + for(i = 0; i < MFGPT_MAX_TIMERS; i++) + count += mfgpt_setup_timer(pdev, i); + + printk("geode-mfgpt: %d timers available.\n", count); + return 0; + + ereq: + pci_release_region(pdev, MFGPT_PCI_BAR); + + err: + printk("geode-mfgpt: Error initalizing the timers\n"); + return -1; + + not_geode: + printk("geode-mfgpt: Not a Geode GX/LX processor\n"); + return -ENODEV; +} + +module_init(geode_mfgpt_init); diff -urN linux-2.4.36-wt2.geode-id/arch/i386/kernel/mfgpt-compat.h linux-2.4.36-wt2.geode-mfgpt/arch/i386/kernel/mfgpt-compat.h --- linux-2.4.36-wt2.geode-id/arch/i386/kernel/mfgpt-compat.h 1970-01-01 01:00:00 +0100 +++ linux-2.4.36-wt2.geode-mfgpt/arch/i386/kernel/mfgpt-compat.h 2008-08-18 18:42:17 +0200 @@ -0,0 +1,251 @@ +/* compatibility layer for iomap functions used by mfgpt. + * + * This file has been extracted and inlined from linux-2.6.22/lib/iomap.c with + * the following original header and copyright : + * + * Implement the default iomap interfaces + * + * (C) Copyright 2004 Linus Torvalds + */ +#include + +/* + * Read/write from/to an (offsettable) iomem cookie. It might be a PIO + * access or a MMIO access, these functions don't care. The info is + * encoded in the hardware mapping set up by the mapping functions + * (or the cookie itself, depending on implementation and hw). + * + * The generic routines don't assume any hardware mappings, and just + * encode the PIO/MMIO as part of the cookie. They coldly assume that + * the MMIO IO mappings are not in the low address range. + * + * Architectures for which this is not true can't use this generic + * implementation and should do their own copy. + */ + +#ifndef HAVE_ARCH_PIO_SIZE +/* + * We encode the physical PIO addresses (0-0xffff) into the + * pointer by offsetting them with a constant (0x10000) and + * assuming that all the low addresses are always PIO. That means + * we can do some sanity checks on the low bits, and don't + * need to just take things for granted. + */ +#define PIO_OFFSET 0x10000UL +#define PIO_MASK 0x0ffffUL +#define PIO_RESERVED 0x40000UL +#endif + +static inline void bad_io_access(unsigned long port, const char *access) +{ + static int count = 10; + if (count) { + count--; + printk(KERN_ERR "Bad IO access at port %lx (%s)\n", port, access); + WARN_ON(1); + } +} + +/* + * Ugly macros are a way of life. + */ +#define IO_COND(addr, is_pio, is_mmio) do { \ + unsigned long port = (unsigned long)addr; \ + if (port >= PIO_RESERVED) { \ + is_mmio; \ + } else if (port > PIO_OFFSET) { \ + port &= PIO_MASK; \ + is_pio; \ + } else \ + bad_io_access(port, #is_pio ); \ +} while (0) + +#ifndef pio_read16be +#define pio_read16be(port) swab16(inw(port)) +#define pio_read32be(port) swab32(inl(port)) +#endif + +#ifndef mmio_read16be +#define mmio_read16be(addr) be16_to_cpu(__raw_readw(addr)) +#define mmio_read32be(addr) be32_to_cpu(__raw_readl(addr)) +#endif + +static inline unsigned int ioread8(void __iomem *addr) +{ + IO_COND(addr, return inb(port), return readb(addr)); + return 0xff; +} +static inline unsigned int ioread16(void __iomem *addr) +{ + IO_COND(addr, return inw(port), return readw(addr)); + return 0xffff; +} +static inline unsigned int ioread16be(void __iomem *addr) +{ + IO_COND(addr, return pio_read16be(port), return mmio_read16be(addr)); + return 0xffff; +} +static inline unsigned int ioread32(void __iomem *addr) +{ + IO_COND(addr, return inl(port), return readl(addr)); + return 0xffffffff; +} +static inline unsigned int ioread32be(void __iomem *addr) +{ + IO_COND(addr, return pio_read32be(port), return mmio_read32be(addr)); + return 0xffffffff; +} + +#ifndef pio_write16be +#define pio_write16be(val,port) outw(swab16(val),port) +#define pio_write32be(val,port) outl(swab32(val),port) +#endif + +#ifndef mmio_write16be +#define mmio_write16be(val,port) __raw_writew(be16_to_cpu(val),port) +#define mmio_write32be(val,port) __raw_writel(be32_to_cpu(val),port) +#endif + +static inline void iowrite8(u8 val, void __iomem *addr) +{ + IO_COND(addr, outb(val,port), writeb(val, addr)); +} +static inline void iowrite16(u16 val, void __iomem *addr) +{ + IO_COND(addr, outw(val,port), writew(val, addr)); +} +static inline void iowrite16be(u16 val, void __iomem *addr) +{ + IO_COND(addr, pio_write16be(val,port), mmio_write16be(val, addr)); +} +static inline void iowrite32(u32 val, void __iomem *addr) +{ + IO_COND(addr, outl(val,port), writel(val, addr)); +} +static inline void iowrite32be(u32 val, void __iomem *addr) +{ + IO_COND(addr, pio_write32be(val,port), mmio_write32be(val, addr)); +} + +/* + * These are the "repeat MMIO read/write" functions. + * Note the "__raw" accesses, since we don't want to + * convert to CPU byte order. We write in "IO byte + * order" (we also don't have IO barriers). + */ +#ifndef mmio_insb +static inline void mmio_insb(void __iomem *addr, u8 *dst, int count) +{ + while (--count >= 0) { + u8 data = __raw_readb(addr); + *dst = data; + dst++; + } +} +static inline void mmio_insw(void __iomem *addr, u16 *dst, int count) +{ + while (--count >= 0) { + u16 data = __raw_readw(addr); + *dst = data; + dst++; + } +} +static inline void mmio_insl(void __iomem *addr, u32 *dst, int count) +{ + while (--count >= 0) { + u32 data = __raw_readl(addr); + *dst = data; + dst++; + } +} +#endif + +#ifndef mmio_outsb +static inline void mmio_outsb(void __iomem *addr, const u8 *src, int count) +{ + while (--count >= 0) { + __raw_writeb(*src, addr); + src++; + } +} +static inline void mmio_outsw(void __iomem *addr, const u16 *src, int count) +{ + while (--count >= 0) { + __raw_writew(*src, addr); + src++; + } +} +static inline void mmio_outsl(void __iomem *addr, const u32 *src, int count) +{ + while (--count >= 0) { + __raw_writel(*src, addr); + src++; + } +} +#endif + +static inline void ioread8_rep(void __iomem *addr, void *dst, unsigned long count) +{ + IO_COND(addr, insb(port,dst,count), mmio_insb(addr, dst, count)); +} +static inline void ioread16_rep(void __iomem *addr, void *dst, unsigned long count) +{ + IO_COND(addr, insw(port,dst,count), mmio_insw(addr, dst, count)); +} +static inline void ioread32_rep(void __iomem *addr, void *dst, unsigned long count) +{ + IO_COND(addr, insl(port,dst,count), mmio_insl(addr, dst, count)); +} + +static inline void iowrite8_rep(void __iomem *addr, const void *src, unsigned long count) +{ + IO_COND(addr, outsb(port, src, count), mmio_outsb(addr, src, count)); +} +static inline void iowrite16_rep(void __iomem *addr, const void *src, unsigned long count) +{ + IO_COND(addr, outsw(port, src, count), mmio_outsw(addr, src, count)); +} +static inline void iowrite32_rep(void __iomem *addr, const void *src, unsigned long count) +{ + IO_COND(addr, outsl(port, src,count), mmio_outsl(addr, src, count)); +} + +/* Create a virtual mapping cookie for an IO port range */ +static inline void __iomem *ioport_map(unsigned long port, unsigned int nr) +{ + if (port > PIO_MASK) + return NULL; + return (void __iomem *) (unsigned long) (port + PIO_OFFSET); +} + +static inline void ioport_unmap(void __iomem *addr) +{ + /* Nothing to do */ +} + +/* Create a virtual mapping cookie for a PCI BAR (memory or IO) */ +static inline void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) +{ + unsigned long start = pci_resource_start(dev, bar); + unsigned long len = pci_resource_len(dev, bar); + unsigned long flags = pci_resource_flags(dev, bar); + + if (!len || !start) + return NULL; + if (maxlen && len > maxlen) + len = maxlen; + if (flags & IORESOURCE_IO) + return ioport_map(start, len); + if (flags & IORESOURCE_MEM) { + if (flags & IORESOURCE_CACHEABLE) + return ioremap(start, len); + return ioremap_nocache(start, len); + } + /* What? */ + return NULL; +} + +static inline void pci_iounmap(struct pci_dev *dev, void __iomem * addr) +{ + IO_COND(addr, /* nothing */, iounmap(addr)); +} diff -urN linux-2.4.36-wt2.geode-id/include/asm-i386/geode-mfgpt.h linux-2.4.36-wt2.geode-mfgpt/include/asm-i386/geode-mfgpt.h --- linux-2.4.36-wt2.geode-id/include/asm-i386/geode-mfgpt.h 1970-01-01 01:00:00 +0100 +++ linux-2.4.36-wt2.geode-mfgpt/include/asm-i386/geode-mfgpt.h 2008-08-18 18:41:23 +0200 @@ -0,0 +1,78 @@ +/* Driver/API for AMD Geode Multi-Function General Purpose Timers (MFGPT) + * + * Copyright (C) 2006, Advanced Micro Devices, Inc. + * + * 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. + * + */ + +#ifndef MFGPT_GEODE_H_ +#define MFGPT_GEODE_H_ + +#define MFGPT_TIMER_ANY -1 + +#define MFGPT_DOMAIN_WORKING 1 +#define MFGPT_DOMAIN_STANDBY 2 +#define MFGPT_DOMAIN_ANY (MFGPT_DOMAIN_WORKING | MFGPT_DOMAIN_STANDBY) + +#define MFGPT_CMP1 0 +#define MFGPT_CMP2 1 + +#define MFGPT_EVENT_IRQ 0 +#define MFGPT_EVENT_NMI 1 +#define MFGPT_EVENT_RESET 3 + +#define MFGPT_REG_CMP1 0 +#define MFGPT_REG_CMP2 2 +#define MFGPT_REG_COUNTER 4 +#define MFGPT_REG_SETUP 6 + +#define MFGPT_SETUP_CNTEN (1 << 15) +#define MFGPT_SETUP_CMP2 (1 << 14) +#define MFGPT_SETUP_CMP1 (1 << 13) +#define MFGPT_SETUP_SETUP (1 << 12) +#define MFGPT_SETUP_STOPEN (1 << 11) +#define MFGPT_SETUP_EXTEN (1 << 10) +#define MFGPT_SETUP_REVEN (1 << 5) +#define MFGPT_SETUP_CLKSEL (1 << 4) + +extern void geode_mfgpt_toggle_event(int, int, int, int); + +#define geode_mfgpt_set_event(t,c,e) geode_mfgpt_toggle_event(t,c,e,1) +#define geode_mfgpt_clear_event(t,c,e) geode_mfgpt_toggle_event(t,c,e,0) + +extern void geode_mfgpt_set_irq(int, int, int, int); + +#define geode_mfgpt_setup_irq(t, c, i) geode_mfgpt_set_irq(t,c,i,1) +#define geode_mfgpt_release_irq(t, c, i) geode_mfgpt_set_irq(t,c,i,0) + +extern void geode_mfgpt_write(int, u16, u16); +extern u16 geode_mfgpt_read(int, u16); + +extern int geode_mfgpt_alloc_timer(int, int, struct module *); + +/* Specific geode tests */ + +static inline int is_geode_gx(void) +{ + return ((boot_cpu_data.x86_vendor == X86_VENDOR_NSC) && + (boot_cpu_data.x86 == 5) && + (boot_cpu_data.x86_model == 5)); +} + +static inline int is_geode_lx(void) +{ + return ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && + (boot_cpu_data.x86 == 5) && + (boot_cpu_data.x86_model == 10)); +} + +static inline int is_geode(void) +{ + return (is_geode_gx() || is_geode_lx()); +} + +#endif