linux: added forgotten alix patches - close #2878
authorMarcin Juszkiewicz <hrw@openembedded.org>
Mon, 27 Aug 2007 20:02:18 +0000 (20:02 +0000)
committerMarcin Juszkiewicz <hrw@openembedded.org>
Mon, 27 Aug 2007 20:02:18 +0000 (20:02 +0000)
packages/linux/linux/alix/geode-mfgpt-clock-event-device-support.patch [new file with mode: 0644]
packages/linux/linux/alix/geode-mfgpt-support-for-geode-class-machines.patch [new file with mode: 0644]

diff --git a/packages/linux/linux/alix/geode-mfgpt-clock-event-device-support.patch b/packages/linux/linux/alix/geode-mfgpt-clock-event-device-support.patch
new file mode 100644 (file)
index 0000000..6f0b10a
--- /dev/null
@@ -0,0 +1,237 @@
+From: Andres Salomon <dilinger@queued.net>
+
+Add support for an MFGPT clock event device; this allows us to use MFGPTs as
+the basis for high-resolution timers.
+
+Signed-off-by: Jordan Crouse <jordan.crouse@amd.com>
+Signed-off-by: Andres Salomon <dilinger@debian.org>
+Cc: Andi Kleen <ak@suse.de>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Cc: john stultz <johnstul@us.ibm.com>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+Cc: Ingo Molnar <mingo@elte.hu>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+
+ Documentation/kernel-parameters.txt |    4 
+ arch/i386/Kconfig                   |   10 +
+ arch/i386/kernel/mfgpt.c            |  165 ++++++++++++++++++++++++++
+ 3 files changed, 179 insertions(+)
+
+--- linux-2.6.22.orig/Documentation/kernel-parameters.txt
++++ linux-2.6.22/Documentation/kernel-parameters.txt
+@@ -1012,6 +1012,10 @@
+       meye.*=         [HW] Set MotionEye Camera parameters
+                       See Documentation/video4linux/meye.txt.
++      mfgpt_irq=      [IA-32] Specify the IRQ to use for the
++                      Multi-Function General Purpose Timers on AMD Geode
++                      platforms.
++
+       mga=            [HW,DRM]
+       migration_cost=
+--- linux-2.6.22.orig/arch/i386/Kconfig
++++ linux-2.6.22/arch/i386/Kconfig
+@@ -1190,6 +1190,16 @@
+         processor goes idle (as is done by the scheduler).  The
+         other workaround is idle=poll boot option.
++config GEODE_MFGPT_TIMER
++      bool "Geode Multi-Function General Purpose Timer (MFGPT) events"
++      depends on MGEODE_LX && GENERIC_TIME && GENERIC_CLOCKEVENTS
++      default y
++      help
++        This driver provides a clock event source based on the MFGPT
++        timer(s) in the CS5535 and CS5536 companion chip for the geode.
++        MFGPTs have a better resolution and max interval than the
++        generic PIT, and are suitable for use as high-res timers.
++
+ config K8_NB
+       def_bool y
+       depends on AGP_AMD64
+--- linux-2.6.22.orig/arch/i386/kernel/mfgpt.c
++++ linux-2.6.22/arch/i386/kernel/mfgpt.c
+@@ -48,6 +48,12 @@
+ #define MFGPT_HZ  (32000 / MFGPT_DIVISOR)
+ #define MFGPT_PERIODIC (MFGPT_HZ / HZ)
++#ifdef CONFIG_GEODE_MFGPT_TIMER
++static int __init mfgpt_timer_setup(void);
++#else
++#define mfgpt_timer_setup() (0)
++#endif
++
+ /* Allow for disabling of MFGPTs */
+ static int disable;
+ static int __init mfgpt_disable(char *s)
+@@ -82,6 +88,9 @@
+               }
+       }
++      /* set up clock event device, if desired */
++      i = mfgpt_timer_setup();
++
+       return count;
+ }
+@@ -197,3 +206,159 @@
+       return -1;
+ }
+ EXPORT_SYMBOL(geode_mfgpt_alloc_timer);
++
++#ifdef CONFIG_GEODE_MFGPT_TIMER
++
++/*
++ * The MFPGT timers on the CS5536 provide us with suitable timers to use
++ * as clock event sources - not as good as a HPET or APIC, but certainly
++ * better then the PIT.  This isn't a general purpose MFGPT driver, but
++ * a simplified one designed specifically to act as a clock event source.
++ * For full details about the MFGPT, please consult the CS5536 data sheet.
++ */
++
++#include <linux/clocksource.h>
++#include <linux/clockchips.h>
++
++static unsigned int mfgpt_tick_mode = CLOCK_EVT_MODE_SHUTDOWN;
++static u16 mfgpt_event_clock;
++
++static int irq = 7;
++static int __init mfgpt_setup(char *str)
++{
++      get_option(&str, &irq);
++      return 1;
++}
++__setup("mfgpt_irq=", mfgpt_setup);
++
++static inline void mfgpt_disable_timer(u16 clock)
++{
++      u16 val = geode_mfgpt_read(clock, MFGPT_REG_SETUP);
++      geode_mfgpt_write(clock, MFGPT_REG_SETUP, val & ~MFGPT_SETUP_CNTEN);
++}
++
++static int mfgpt_next_event(unsigned long, struct clock_event_device *);
++static void mfgpt_set_mode(enum clock_event_mode, struct clock_event_device *);
++
++static struct clock_event_device mfgpt_clockevent = {
++      .name = "mfgpt-timer",
++      .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
++      .set_mode = mfgpt_set_mode,
++      .set_next_event = mfgpt_next_event,
++      .rating = 250,
++      .cpumask = CPU_MASK_ALL,
++      .shift = 32
++};
++
++static inline void mfgpt_start_timer(u16 clock, u16 delta)
++{
++      geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_CMP2, (u16) delta);
++      geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0);
++
++      geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP,
++                        MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
++}
++
++static void mfgpt_set_mode(enum clock_event_mode mode,
++                         struct clock_event_device *evt)
++{
++      mfgpt_disable_timer(mfgpt_event_clock);
++
++      if (mode == CLOCK_EVT_MODE_PERIODIC)
++              mfgpt_start_timer(mfgpt_event_clock, MFGPT_PERIODIC);
++
++      mfgpt_tick_mode = mode;
++}
++
++static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt)
++{
++      mfgpt_start_timer(mfgpt_event_clock, delta);
++      return 0;
++}
++
++/* Assume (foolishly?), that this interrupt was due to our tick */
++
++static irqreturn_t mfgpt_tick(int irq, void *dev_id)
++{
++      if (mfgpt_tick_mode == CLOCK_EVT_MODE_SHUTDOWN)
++              return IRQ_HANDLED;
++
++      /* Turn off the clock */
++      mfgpt_disable_timer(mfgpt_event_clock);
++
++      /* Clear the counter */
++      geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0);
++
++      /* Restart the clock in periodic mode */
++
++      if (mfgpt_tick_mode == CLOCK_EVT_MODE_PERIODIC) {
++              geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP,
++                                MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
++      }
++
++      mfgpt_clockevent.event_handler(&mfgpt_clockevent);
++      return IRQ_HANDLED;
++}
++
++static struct irqaction mfgptirq  = {
++      .handler = mfgpt_tick,
++      .flags = IRQF_DISABLED | IRQF_NOBALANCING,
++      .mask = CPU_MASK_NONE,
++      .name = "mfgpt-timer"
++};
++
++static int __init mfgpt_timer_setup(void)
++{
++      int timer, ret;
++      u16 val;
++
++      timer = geode_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING,
++                      THIS_MODULE);
++      if (timer < 0) {
++              printk(KERN_ERR "mfgpt-timer:  Could not allocate a MFPGT "
++                              "timer\n");
++              return -ENODEV;
++      }
++
++      mfgpt_event_clock = timer;
++      /* Set the clock scale and enable the event mode for CMP2 */
++      val = MFGPT_SCALE | (3 << 8);
++
++      geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP, val);
++
++      /* Set up the IRQ on the MFGPT side */
++      if (geode_mfgpt_setup_irq(mfgpt_event_clock, MFGPT_CMP2, irq)) {
++              printk(KERN_ERR "mfgpt-timer:  Could not set up IRQ %d\n", irq);
++              return -EIO;
++      }
++
++      /* And register it with the kernel */
++      ret = setup_irq(irq, &mfgptirq);
++
++      if (ret) {
++              printk(KERN_ERR "mfgpt-timer:  Unable to set up the "
++                              "interrupt.\n");
++              goto err;
++      }
++
++      /* Set up the clock event */
++      mfgpt_clockevent.mult = div_sc(MFGPT_HZ, NSEC_PER_SEC, 32);
++      mfgpt_clockevent.min_delta_ns = clockevent_delta2ns(0xF,
++                      &mfgpt_clockevent);
++      mfgpt_clockevent.max_delta_ns = clockevent_delta2ns(0xFFFE,
++                      &mfgpt_clockevent);
++
++      printk(KERN_INFO "mfgpt-timer:  registering the MFGT timer as a "
++                      "clock event.\n");
++      clockevents_register_device(&mfgpt_clockevent);
++
++      return 0;
++
++err:
++      geode_mfgpt_release_irq(mfgpt_event_clock, MFGPT_CMP2, irq);
++      printk(KERN_ERR "mfgpt-timer:  Unable to set up the MFGPT clock "
++                      "source\n");
++      return -EIO;
++}
++
++#endif
diff --git a/packages/linux/linux/alix/geode-mfgpt-support-for-geode-class-machines.patch b/packages/linux/linux/alix/geode-mfgpt-support-for-geode-class-machines.patch
new file mode 100644 (file)
index 0000000..533412c
--- /dev/null
@@ -0,0 +1,311 @@
+From: Andres Salomon <dilinger@queued.net>
+
+This adds support for Multi-Function General Purpose Timers.  It detects the
+available timers during southbridge init, and provides an API for allocating
+and setting the timers.  They're higher resolution than the standard PIT, so
+the MFGPTs come in handy for quite a few things.
+
+Note that we never clobber the timers that the BIOS might have opted to use;
+we just check for unused timers.
+
+Signed-off-by: Jordan Crouse <jordan.crouse@amd.com>
+Signed-off-by: Andres Salomon <dilinger@debian.org>
+Cc: Andi Kleen <ak@suse.de>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+
+ Documentation/kernel-parameters.txt |    3 
+ arch/i386/kernel/Makefile           |    2 
+ arch/i386/kernel/geode.c            |    4 
+ arch/i386/kernel/mfgpt.c            |  199 ++++++++++++++++++++++++++
+ include/asm-i386/geode.h            |   50 ++++++
+ 5 files changed, 257 insertions(+), 1 deletion(-)
+
+--- linux-2.6.22.orig/arch/i386/kernel/Makefile
++++ linux-2.6.22/arch/i386/kernel/Makefile
+@@ -40,7 +40,7 @@
+ obj-$(CONFIG_EARLY_PRINTK)    += early_printk.o
+ obj-$(CONFIG_HPET_TIMER)      += hpet.o
+ obj-$(CONFIG_K8_NB)           += k8.o
+-obj-$(CONFIG_MGEODE_LX)               += geode.o
++obj-$(CONFIG_MGEODE_LX)               += geode.o mfgpt.o
+ obj-$(CONFIG_VMI)             += vmi.o vmiclock.o
+ obj-$(CONFIG_PARAVIRT)                += paravirt.o
+--- linux-2.6.22.orig/arch/i386/kernel/geode.c
++++ linux-2.6.22/arch/i386/kernel/geode.c
+@@ -146,10 +146,14 @@
+ static int __init geode_southbridge_init(void)
+ {
++      int timers;
++
+       if (!is_geode())
+               return -ENODEV;
+       init_lbars();
++      timers = geode_mfgpt_detect();
++      printk(KERN_INFO "geode:  %d MFGPT timers available.\n", timers);
+       return 0;
+ }
+--- /dev/null
++++ linux-2.6.22/arch/i386/kernel/mfgpt.c
+@@ -0,0 +1,199 @@
++/*
++ * Driver/API for AMD Geode Multi-Function General Purpose Timers (MFGPT)
++ *
++ * 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.
++ *
++ * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
++ */
++
++/*
++ * We are using the 32Khz input clock - its the only one that has the
++ * ranges we find desirable.  The following table lists the suitable
++ * divisors and the associated hz, minimum interval
++ * and the maximum interval:
++ *
++ *  Divisor   Hz      Min Delta (S) Max Delta (S)
++ *   1        32000     .0005          2.048
++ *   2        16000      .001          4.096
++ *   4         8000      .002          8.192
++ *   8         4000      .004         16.384
++ *   16        2000      .008         32.768
++ *   32        1000      .016         65.536
++ *   64         500      .032        131.072
++ *  128         250      .064        262.144
++ *  256         125      .128        524.288
++ */
++
++#include <linux/kernel.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <asm/geode.h>
++
++#define F_AVAIL    0x01
++
++static struct mfgpt_timer_t {
++      int flags;
++      struct module *owner;
++} mfgpt_timers[MFGPT_MAX_TIMERS];
++
++/* Selected from the table above */
++
++#define MFGPT_DIVISOR 16
++#define MFGPT_SCALE  4     /* divisor = 2^(scale) */
++#define MFGPT_HZ  (32000 / MFGPT_DIVISOR)
++#define MFGPT_PERIODIC (MFGPT_HZ / HZ)
++
++/* Allow for disabling of MFGPTs */
++static int disable;
++static int __init mfgpt_disable(char *s)
++{
++      disable = 1;
++      return 1;
++}
++__setup("nomfgpt", mfgpt_disable);
++
++/*
++ * Check whether any MFGPTs are available for the kernel to use.  In most
++ * cases, firmware that uses AMD's VSA code will claim all timers during
++ * bootup; we certainly don't want to take them if they're already in use.
++ * In other cases (such as with VSAless OpenFirmware), the system firmware
++ * leaves timers available for us to use.
++ */
++int __init geode_mfgpt_detect(void)
++{
++      int count = 0, i;
++      u16 val;
++
++      if (disable) {
++              printk(KERN_INFO "geode-mfgpt:  Skipping MFGPT setup\n");
++              return 0;
++      }
++
++      for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
++              val = geode_mfgpt_read(i, MFGPT_REG_SETUP);
++              if (!(val & MFGPT_SETUP_SETUP)) {
++                      mfgpt_timers[i].flags = F_AVAIL;
++                      count++;
++              }
++      }
++
++      return count;
++}
++
++int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable)
++{
++      u32 msr, mask, value, dummy;
++      int shift = (cmp == MFGPT_CMP1) ? 0 : 8;
++
++      if (timer < 0 || timer >= MFGPT_MAX_TIMERS)
++              return -EIO;
++
++      /*
++       * The register maps for these are described in sections 6.17.1.x of
++       * the AMD Geode CS5536 Companion Device Data Book.
++       */
++      switch (event) {
++      case MFGPT_EVENT_RESET:
++              /*
++               * XXX: According to the docs, we cannot reset timers above
++               * 6; that is, resets for 7 and 8 will be ignored.  Is this
++               * a problem?   -dilinger
++               */
++              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 -EIO;
++      }
++
++      rdmsr(msr, value, dummy);
++
++      if (enable)
++              value |= mask;
++      else
++              value &= ~mask;
++
++      wrmsr(msr, value, dummy);
++      return 0;
++}
++EXPORT_SYMBOL(geode_mfgpt_toggle_event);
++
++int geode_mfgpt_set_irq(int timer, int cmp, int irq, int enable)
++{
++      u32 val, dummy;
++      int offset;
++
++      if (timer < 0 || timer >= MFGPT_MAX_TIMERS)
++              return -EIO;
++
++      if (geode_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable))
++              return -EIO;
++
++      rdmsr(0x51400022, val, dummy);
++
++      offset = (timer % 4) * 4;
++
++      val &= ~((0xF << offset) | (0xF << (offset + 16)));
++
++      if (enable) {
++              val |= (irq & 0x0F) << (offset);
++              val |= (irq & 0x0F) << (offset + 16);
++      }
++
++      wrmsr(0x51400022, val, dummy);
++      return 0;
++}
++EXPORT_SYMBOL(geode_mfgpt_set_irq);
++
++static int mfgpt_get(int timer, struct module *owner)
++{
++      mfgpt_timers[timer].flags &= ~F_AVAIL;
++      mfgpt_timers[timer].owner = owner;
++      printk(KERN_INFO "geode-mfgpt:  Registered timer %d\n", timer);
++      return timer;
++}
++
++int geode_mfgpt_alloc_timer(int timer, int domain, struct module *owner)
++{
++      int i;
++
++      if (!geode_get_dev_base(GEODE_DEV_MFGPT))
++              return -ENODEV;
++      if (timer >= MFGPT_MAX_TIMERS)
++              return -EIO;
++
++      if (timer < 0) {
++              /* Try to find an available timer */
++              for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
++                      if (mfgpt_timers[i].flags & F_AVAIL)
++                              return mfgpt_get(i, owner);
++
++                      if (i == 5 && domain == MFGPT_DOMAIN_WORKING)
++                              break;
++              }
++      } else {
++              /* If they requested a specific timer, try to honor that */
++              if (mfgpt_timers[timer].flags & F_AVAIL)
++                      return mfgpt_get(timer, owner);
++      }
++
++      /* No timers available - too bad */
++      return -1;
++}
++EXPORT_SYMBOL(geode_mfgpt_alloc_timer);
+--- linux-2.6.22.orig/include/asm-i386/geode.h
++++ linux-2.6.22/include/asm-i386/geode.h
+@@ -156,4 +156,54 @@
+       return (is_geode_gx() || is_geode_lx());
+ }
++/* MFGPTs */
++
++#define MFGPT_MAX_TIMERS      8
++#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)
++
++static inline void geode_mfgpt_write(int timer, u16 reg, u16 value)
++{
++      u32 base = geode_get_dev_base(GEODE_DEV_MFGPT);
++      outw(value, base + reg + (timer * 8));
++}
++
++static inline u16 geode_mfgpt_read(int timer, u16 reg)
++{
++      u32 base = geode_get_dev_base(GEODE_DEV_MFGPT);
++      return inw(base + reg + (timer * 8));
++}
++
++extern int __init geode_mfgpt_detect(void);
++extern int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable);
++extern int geode_mfgpt_set_irq(int timer, int cmp, int irq, int enable);
++extern int geode_mfgpt_alloc_timer(int timer, int domain, struct module *owner);
++
++#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)
++
+ #endif