[oe-commits] org.oe.dev linux: added forgotten alix patches - close #2878

hrw commit openembedded-commits at lists.openembedded.org
Mon Aug 27 20:32:04 UTC 2007


linux: added forgotten alix patches - close #2878

Author: hrw at openembedded.org
Branch: org.openembedded.dev
Revision: c386b75d3046ae9a229198121bffd56b36b626c6
ViewMTN: http://monotone.openembedded.org/revision.psp?id=c386b75d3046ae9a229198121bffd56b36b626c6
Files:
1
packages/linux/linux/alix/geode-mfgpt-clock-event-device-support.patch
packages/linux/linux/alix/geode-mfgpt-support-for-geode-class-machines.patch
Diffs:

#
# mt diff -r913e5ff8efdd2d62c384775744e3e776ee4d9255 -rc386b75d3046ae9a229198121bffd56b36b626c6
#
# 
# 
# add_file "packages/linux/linux/alix/geode-mfgpt-clock-event-device-support.patch"
#  content [d0e230915059acdf6b19ea60e86d9936b545c098]
# 
# add_file "packages/linux/linux/alix/geode-mfgpt-support-for-geode-class-machines.patch"
#  content [c55a38ac6a13fc66730cc600ffa2effc475670a6]
# 
============================================================
--- packages/linux/linux/alix/geode-mfgpt-clock-event-device-support.patch	d0e230915059acdf6b19ea60e86d9936b545c098
+++ packages/linux/linux/alix/geode-mfgpt-clock-event-device-support.patch	d0e230915059acdf6b19ea60e86d9936b545c098
@@ -0,0 +1,237 @@
+From: Andres Salomon <dilinger at 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 at amd.com>
+Signed-off-by: Andres Salomon <dilinger at debian.org>
+Cc: Andi Kleen <ak at suse.de>
+Cc: Alan Cox <alan at lxorguk.ukuu.org.uk>
+Cc: john stultz <johnstul at us.ibm.com>
+Cc: Thomas Gleixner <tglx at linutronix.de>
+Cc: Ingo Molnar <mingo at elte.hu>
+Signed-off-by: Andrew Morton <akpm at 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
============================================================
--- packages/linux/linux/alix/geode-mfgpt-support-for-geode-class-machines.patch	c55a38ac6a13fc66730cc600ffa2effc475670a6
+++ packages/linux/linux/alix/geode-mfgpt-support-for-geode-class-machines.patch	c55a38ac6a13fc66730cc600ffa2effc475670a6
@@ -0,0 +1,311 @@
+From: Andres Salomon <dilinger at 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 at amd.com>
+Signed-off-by: Andres Salomon <dilinger at debian.org>
+Cc: Andi Kleen <ak at suse.de>
+Cc: Alan Cox <alan at lxorguk.ukuu.org.uk>
+Signed-off-by: Andrew Morton <akpm at 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 at 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%s
>>> DIFF TRUNCATED @ 16K






More information about the Openembedded-commits mailing list