diff options
-rw-r--r-- | drivers/misc/cs5535-mfgpt.c | 41 |
1 files changed, 37 insertions, 4 deletions
diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c index 9858f36dad8b..6bdb3baf7bcf 100644 --- a/drivers/misc/cs5535-mfgpt.c +++ b/drivers/misc/cs5535-mfgpt.c @@ -24,8 +24,11 @@ static int mfgpt_reset_timers; module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644); -MODULE_PARM_DESC(mfgptfix, "Reset the MFGPT timers during init; " - "required by some broken BIOSes (ie, TinyBIOS < 0.99)."); +MODULE_PARM_DESC(mfgptfix, "Try to reset the MFGPT timers during init; " + "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_chip *chip; @@ -256,6 +259,28 @@ static void reset_all_timers(void) } /* + * 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 * 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. @@ -271,15 +296,17 @@ static int scan_timers(struct cs5535_mfgpt_chip *mfgpt) int i; /* bios workaround */ - if (mfgpt_reset_timers) + if (mfgpt_reset_timers == 1) reset_all_timers(); + else if (mfgpt_reset_timers == 2) + soft_reset(); /* just to be safe, protect this section w/ lock */ spin_lock_irqsave(&mfgpt->lock, flags); for (i = 0; i < MFGPT_MAX_TIMERS; i++) { timer.nr = i; 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); timers++; } @@ -294,6 +321,12 @@ static int cs5535_mfgpt_probe(struct platform_device *pdev) struct resource *res; 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 * fetching it from MSR_LBAR_MFGPT, the other is by reading the * PCI BAR info. The latter method is easier (especially across |