diff options
Diffstat (limited to 'drivers/misc/cs5535-mfgpt.c')
-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..effd8c6b2b94 100644 --- a/drivers/misc/cs5535-mfgpt.c +++ b/drivers/misc/cs5535-mfgpt.c | |||
@@ -24,8 +24,11 @@ | |||
24 | 24 | ||
25 | static int mfgpt_reset_timers; | 25 | static int mfgpt_reset_timers; |
26 | module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644); | 26 | module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644); |
27 | MODULE_PARM_DESC(mfgptfix, "Reset the MFGPT timers during init; " | 27 | MODULE_PARM_DESC(mfgptfix, "Try to reset the MFGPT timers during init; " |
28 | "required by some broken BIOSes (ie, TinyBIOS < 0.99)."); | 28 | "required by some broken BIOSes (ie, TinyBIOS < 0.99) or kexec " |
29 | "(1 = reset the MFGPT using an undocumented bit, " | ||
30 | "2 = perform a soft reset by unconfiguring all timers); " | ||
31 | "use what works best for you."); | ||
29 | 32 | ||
30 | struct cs5535_mfgpt_timer { | 33 | struct cs5535_mfgpt_timer { |
31 | struct cs5535_mfgpt_chip *chip; | 34 | struct cs5535_mfgpt_chip *chip; |
@@ -256,6 +259,28 @@ static void reset_all_timers(void) | |||
256 | } | 259 | } |
257 | 260 | ||
258 | /* | 261 | /* |
262 | * This is another sledgehammer to reset all MFGPT timers. | ||
263 | * Instead of using the undocumented bit method it clears | ||
264 | * IRQ, NMI and RESET settings. | ||
265 | */ | ||
266 | static void soft_reset(void) | ||
267 | { | ||
268 | int i; | ||
269 | struct cs5535_mfgpt_timer t; | ||
270 | |||
271 | for (i = 0; i < MFGPT_MAX_TIMERS; i++) { | ||
272 | t.nr = i; | ||
273 | |||
274 | cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_RESET, 0); | ||
275 | cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_RESET, 0); | ||
276 | cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_NMI, 0); | ||
277 | cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_NMI, 0); | ||
278 | cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_IRQ, 0); | ||
279 | cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_IRQ, 0); | ||
280 | } | ||
281 | } | ||
282 | |||
283 | /* | ||
259 | * Check whether any MFGPTs are available for the kernel to use. In most | 284 | * Check whether any MFGPTs are available for the kernel to use. In most |
260 | * cases, firmware that uses AMD's VSA code will claim all timers during | 285 | * cases, firmware that uses AMD's VSA code will claim all timers during |
261 | * bootup; we certainly don't want to take them if they're already in use. | 286 | * 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) | |||
271 | int i; | 296 | int i; |
272 | 297 | ||
273 | /* bios workaround */ | 298 | /* bios workaround */ |
274 | if (mfgpt_reset_timers) | 299 | if (mfgpt_reset_timers == 1) |
275 | reset_all_timers(); | 300 | reset_all_timers(); |
301 | else if (mfgpt_reset_timers == 2) | ||
302 | soft_reset(); | ||
276 | 303 | ||
277 | /* just to be safe, protect this section w/ lock */ | 304 | /* just to be safe, protect this section w/ lock */ |
278 | spin_lock_irqsave(&mfgpt->lock, flags); | 305 | spin_lock_irqsave(&mfgpt->lock, flags); |
279 | for (i = 0; i < MFGPT_MAX_TIMERS; i++) { | 306 | for (i = 0; i < MFGPT_MAX_TIMERS; i++) { |
280 | timer.nr = i; | 307 | timer.nr = i; |
281 | val = cs5535_mfgpt_read(&timer, MFGPT_REG_SETUP); | 308 | val = cs5535_mfgpt_read(&timer, MFGPT_REG_SETUP); |
282 | if (!(val & MFGPT_SETUP_SETUP)) { | 309 | if (!(val & MFGPT_SETUP_SETUP) || mfgpt_reset_timers == 2) { |
283 | __set_bit(i, mfgpt->avail); | 310 | __set_bit(i, mfgpt->avail); |
284 | timers++; | 311 | timers++; |
285 | } | 312 | } |
@@ -294,6 +321,12 @@ static int cs5535_mfgpt_probe(struct platform_device *pdev) | |||
294 | struct resource *res; | 321 | struct resource *res; |
295 | int err = -EIO, t; | 322 | int err = -EIO, t; |
296 | 323 | ||
324 | if (mfgpt_reset_timers < 0 || mfgpt_reset_timers > 2) { | ||
325 | dev_err(&pdev->dev, "Bad mfgpt_reset_timers value: %i\n", | ||
326 | mfgpt_reset_timers); | ||
327 | goto done; | ||
328 | } | ||
329 | |||
297 | /* There are two ways to get the MFGPT base address; one is by | 330 | /* There are two ways to get the MFGPT base address; one is by |
298 | * fetching it from MSR_LBAR_MFGPT, the other is by reading the | 331 | * fetching it from MSR_LBAR_MFGPT, the other is by reading the |
299 | * PCI BAR info. The latter method is easier (especially across | 332 | * PCI BAR info. The latter method is easier (especially across |