Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 67744
b: refs/heads/master
c: 83d7384
h: refs/heads/master
v: v3
  • Loading branch information
Andres Salomon authored and Thomas Gleixner committed Oct 12, 2007
1 parent 9e377e2 commit 6e1e1ac
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 5fa3a246ea2e1be2ffaa01343bc183c8c0bfa472
refs/heads/master: 83d7384f8d4aa216b49cf9cb286ea743054b119f
3 changes: 3 additions & 0 deletions trunk/Documentation/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,9 @@ and is between 256 and 4096 characters. It is defined in the file

nomce [X86-32] Machine Check Exception

nomfgpt [X86-32] Disable Multi-Function General Purpose
Timer usage (for AMD Geode machines).

noreplace-paravirt [X86-32,PV_OPS] Don't patch paravirt_ops

noreplace-smp [X86-32,SMP] Don't replace SMP instructions
Expand Down
2 changes: 1 addition & 1 deletion trunk/arch/x86/kernel/Makefile_32
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ obj-$(CONFIG_VM86) += vm86_32.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_HPET_TIMER) += hpet_32.o
obj-$(CONFIG_K8_NB) += k8.o
obj-$(CONFIG_MGEODE_LX) += geode_32.o
obj-$(CONFIG_MGEODE_LX) += geode_32.o mfgpt_32.o

obj-$(CONFIG_VMI) += vmi_32.o vmiclock_32.o
obj-$(CONFIG_PARAVIRT) += paravirt_32.o
Expand Down
4 changes: 4 additions & 0 deletions trunk/arch/x86/kernel/geode_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,14 @@ EXPORT_SYMBOL_GPL(geode_gpio_setup_event);

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;
}

Expand Down
197 changes: 197 additions & 0 deletions trunk/arch/x86/kernel/mfgpt_32.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/*
* 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;
}

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(MSR_PIC_ZSEL_LOW, val, dummy);

offset = (timer % 4) * 4;

val &= ~((0xF << offset) | (0xF << (offset + 16)));

if (enable) {
val |= (irq & 0x0F) << (offset);
val |= (irq & 0x0F) << (offset + 16);
}

wrmsr(MSR_PIC_ZSEL_LOW, val, dummy);
return 0;
}

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;
}

50 changes: 50 additions & 0 deletions trunk/include/asm-x86/geode.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,54 @@ static inline int is_geode(void)
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

0 comments on commit 6e1e1ac

Please sign in to comment.