Commit 945480b1 authored by Richard Weinberger's avatar Richard Weinberger Committed by Greg Kroah-Hartman

cs5535-mfgpt: Add another reset method

The CS5535/CS5536 MFGPT has no support to reset the device.
The current method uses an undocumented bit but does not work on all
devices. At least on my ALIX board it completely freezes the board.

This new method tries to soft reset all timers by unconfiguring them.
But this does not clear the RO setup register and therefore it has to
be ignored while probing.

Resetting the timers is not only needed on broken BIOSes also when
kexec is used. Otherwise the new kernel will find preconfigured timers
and odd things will happen.
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 97485726
...@@ -24,8 +24,11 @@ ...@@ -24,8 +24,11 @@
static int mfgpt_reset_timers; static int mfgpt_reset_timers;
module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644); module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644);
MODULE_PARM_DESC(mfgptfix, "Reset the MFGPT timers during init; " MODULE_PARM_DESC(mfgptfix, "Try to reset the MFGPT timers during init; "
"required by some broken BIOSes (ie, TinyBIOS < 0.99)."); "required by some broken BIOSes (ie, TinyBIOS < 0.99) or kexec "
"(1 = reset the MFGPT using an undocumented bit, "
"2 = perform a soft reset by unconfiguring all timers);
use what works best for you.");
struct cs5535_mfgpt_timer { struct cs5535_mfgpt_timer {
struct cs5535_mfgpt_chip *chip; struct cs5535_mfgpt_chip *chip;
...@@ -255,6 +258,28 @@ static void reset_all_timers(void) ...@@ -255,6 +258,28 @@ static void reset_all_timers(void)
wrmsr(MSR_MFGPT_SETUP, val, dummy); wrmsr(MSR_MFGPT_SETUP, val, dummy);
} }
/*
* This is another sledgehammer to reset all MFGPT timers.
* Instead of using the undocumented bit method it clears
* IRQ, NMI and RESET settings.
*/
static void soft_reset(void)
{
int i;
struct cs5535_mfgpt_timer t;
for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
t.nr = i;
cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_RESET, 0);
cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_RESET, 0);
cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_NMI, 0);
cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_NMI, 0);
cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_IRQ, 0);
cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_IRQ, 0);
}
}
/* /*
* Check whether any MFGPTs are available for the kernel to use. In most * 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 * cases, firmware that uses AMD's VSA code will claim all timers during
...@@ -271,15 +296,17 @@ static int scan_timers(struct cs5535_mfgpt_chip *mfgpt) ...@@ -271,15 +296,17 @@ static int scan_timers(struct cs5535_mfgpt_chip *mfgpt)
int i; int i;
/* bios workaround */ /* bios workaround */
if (mfgpt_reset_timers) if (mfgpt_reset_timers == 1)
reset_all_timers(); reset_all_timers();
else if (mfgpt_reset_timers == 2)
soft_reset();
/* just to be safe, protect this section w/ lock */ /* just to be safe, protect this section w/ lock */
spin_lock_irqsave(&mfgpt->lock, flags); spin_lock_irqsave(&mfgpt->lock, flags);
for (i = 0; i < MFGPT_MAX_TIMERS; i++) { for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
timer.nr = i; timer.nr = i;
val = cs5535_mfgpt_read(&timer, MFGPT_REG_SETUP); val = cs5535_mfgpt_read(&timer, MFGPT_REG_SETUP);
if (!(val & MFGPT_SETUP_SETUP)) { if (!(val & MFGPT_SETUP_SETUP) || mfgpt_reset_timers == 2) {
__set_bit(i, mfgpt->avail); __set_bit(i, mfgpt->avail);
timers++; timers++;
} }
...@@ -294,6 +321,12 @@ static int cs5535_mfgpt_probe(struct platform_device *pdev) ...@@ -294,6 +321,12 @@ static int cs5535_mfgpt_probe(struct platform_device *pdev)
struct resource *res; struct resource *res;
int err = -EIO, t; int err = -EIO, t;
if (mfgpt_reset_timers < 0 || mfgpt_reset_timers > 2) {
dev_err(&pdev->dev, "Bad mfgpt_reset_timers value: %i\n",
mfgpt_reset_timers);
goto done;
}
/* There are two ways to get the MFGPT base address; one is by /* There are two ways to get the MFGPT base address; one is by
* fetching it from MSR_LBAR_MFGPT, the other is by reading the * fetching it from MSR_LBAR_MFGPT, the other is by reading the
* PCI BAR info. The latter method is easier (especially across * PCI BAR info. The latter method is easier (especially across
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment