diff options
author | Manuel Lauss <manuel.lauss@googlemail.com> | 2011-11-16 09:42:28 -0500 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2011-12-07 17:01:45 -0500 |
commit | 864c6c22e9a5742b0f43c983b6c405d52817bacd (patch) | |
tree | 694bf23de85eb7f93d45017b6f155e211368f749 /arch/mips/pci/pci-alchemy.c | |
parent | 5611cc4572e889b62a7b4c72a413536bf6a9c416 (diff) |
MIPS: Alchemy: Fix PCI PM
Move PCI Controller PM to syscore_ops since the platform_driver PM methods
are called way too late on resume and far too early on suspend (after and
before PCI device resume/suspend).
This also allows to simplify wired entry management a bit.
Signed-off-by: Manuel Lauss <manuel.lauss@googlemail.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/3007/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/pci/pci-alchemy.c')
-rw-r--r-- | arch/mips/pci/pci-alchemy.c | 137 |
1 files changed, 67 insertions, 70 deletions
diff --git a/arch/mips/pci/pci-alchemy.c b/arch/mips/pci/pci-alchemy.c index b5ce041cdafb..b5eddf533c0b 100644 --- a/arch/mips/pci/pci-alchemy.c +++ b/arch/mips/pci/pci-alchemy.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/syscore_ops.h> | ||
16 | #include <linux/vmalloc.h> | 17 | #include <linux/vmalloc.h> |
17 | 18 | ||
18 | #include <asm/mach-au1x00/au1000.h> | 19 | #include <asm/mach-au1x00/au1000.h> |
@@ -41,6 +42,12 @@ struct alchemy_pci_context { | |||
41 | int (*board_pci_idsel)(unsigned int devsel, int assert); | 42 | int (*board_pci_idsel)(unsigned int devsel, int assert); |
42 | }; | 43 | }; |
43 | 44 | ||
45 | /* for syscore_ops. There's only one PCI controller on Alchemy chips, so this | ||
46 | * should suffice for now. | ||
47 | */ | ||
48 | static struct alchemy_pci_context *__alchemy_pci_ctx; | ||
49 | |||
50 | |||
44 | /* IO/MEM resources for PCI. Keep the memres in sync with __fixup_bigphys_addr | 51 | /* IO/MEM resources for PCI. Keep the memres in sync with __fixup_bigphys_addr |
45 | * in arch/mips/alchemy/common/setup.c | 52 | * in arch/mips/alchemy/common/setup.c |
46 | */ | 53 | */ |
@@ -99,18 +106,6 @@ static int config_access(unsigned char access_type, struct pci_bus *bus, | |||
99 | return -1; | 106 | return -1; |
100 | } | 107 | } |
101 | 108 | ||
102 | /* YAMON on all db1xxx boards wipes the TLB and writes zero to C0_wired | ||
103 | * on resume, clearing our wired entry. Unfortunately the ->resume() | ||
104 | * callback is called way way way too late (and ->suspend() too early) | ||
105 | * to have them destroy and recreate it. Instead just test if c0_wired | ||
106 | * is now lower than the index we retrieved before suspending and then | ||
107 | * recreate the entry if necessary. Of course this is totally bonkers | ||
108 | * and breaks as soon as someone else adds another wired entry somewhere | ||
109 | * else. Anyone have any ideas how to handle this better? | ||
110 | */ | ||
111 | if (unlikely(read_c0_wired() < ctx->wired_entry)) | ||
112 | alchemy_pci_wired_entry(ctx); | ||
113 | |||
114 | local_irq_save(flags); | 109 | local_irq_save(flags); |
115 | r = __raw_readl(ctx->regs + PCI_REG_STATCMD) & 0x0000ffff; | 110 | r = __raw_readl(ctx->regs + PCI_REG_STATCMD) & 0x0000ffff; |
116 | r |= PCI_STATCMD_STATUS(0x2000); | 111 | r |= PCI_STATCMD_STATUS(0x2000); |
@@ -304,6 +299,62 @@ static int alchemy_pci_def_idsel(unsigned int devsel, int assert) | |||
304 | return 1; /* success */ | 299 | return 1; /* success */ |
305 | } | 300 | } |
306 | 301 | ||
302 | /* save PCI controller register contents. */ | ||
303 | static int alchemy_pci_suspend(void) | ||
304 | { | ||
305 | struct alchemy_pci_context *ctx = __alchemy_pci_ctx; | ||
306 | if (!ctx) | ||
307 | return 0; | ||
308 | |||
309 | ctx->pm[0] = __raw_readl(ctx->regs + PCI_REG_CMEM); | ||
310 | ctx->pm[1] = __raw_readl(ctx->regs + PCI_REG_CONFIG) & 0x0009ffff; | ||
311 | ctx->pm[2] = __raw_readl(ctx->regs + PCI_REG_B2BMASK_CCH); | ||
312 | ctx->pm[3] = __raw_readl(ctx->regs + PCI_REG_B2BBASE0_VID); | ||
313 | ctx->pm[4] = __raw_readl(ctx->regs + PCI_REG_B2BBASE1_SID); | ||
314 | ctx->pm[5] = __raw_readl(ctx->regs + PCI_REG_MWMASK_DEV); | ||
315 | ctx->pm[6] = __raw_readl(ctx->regs + PCI_REG_MWBASE_REV_CCL); | ||
316 | ctx->pm[7] = __raw_readl(ctx->regs + PCI_REG_ID); | ||
317 | ctx->pm[8] = __raw_readl(ctx->regs + PCI_REG_CLASSREV); | ||
318 | ctx->pm[9] = __raw_readl(ctx->regs + PCI_REG_PARAM); | ||
319 | ctx->pm[10] = __raw_readl(ctx->regs + PCI_REG_MBAR); | ||
320 | ctx->pm[11] = __raw_readl(ctx->regs + PCI_REG_TIMEOUT); | ||
321 | |||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | static void alchemy_pci_resume(void) | ||
326 | { | ||
327 | struct alchemy_pci_context *ctx = __alchemy_pci_ctx; | ||
328 | if (!ctx) | ||
329 | return; | ||
330 | |||
331 | __raw_writel(ctx->pm[0], ctx->regs + PCI_REG_CMEM); | ||
332 | __raw_writel(ctx->pm[2], ctx->regs + PCI_REG_B2BMASK_CCH); | ||
333 | __raw_writel(ctx->pm[3], ctx->regs + PCI_REG_B2BBASE0_VID); | ||
334 | __raw_writel(ctx->pm[4], ctx->regs + PCI_REG_B2BBASE1_SID); | ||
335 | __raw_writel(ctx->pm[5], ctx->regs + PCI_REG_MWMASK_DEV); | ||
336 | __raw_writel(ctx->pm[6], ctx->regs + PCI_REG_MWBASE_REV_CCL); | ||
337 | __raw_writel(ctx->pm[7], ctx->regs + PCI_REG_ID); | ||
338 | __raw_writel(ctx->pm[8], ctx->regs + PCI_REG_CLASSREV); | ||
339 | __raw_writel(ctx->pm[9], ctx->regs + PCI_REG_PARAM); | ||
340 | __raw_writel(ctx->pm[10], ctx->regs + PCI_REG_MBAR); | ||
341 | __raw_writel(ctx->pm[11], ctx->regs + PCI_REG_TIMEOUT); | ||
342 | wmb(); | ||
343 | __raw_writel(ctx->pm[1], ctx->regs + PCI_REG_CONFIG); | ||
344 | wmb(); | ||
345 | |||
346 | /* YAMON on all db1xxx boards wipes the TLB and writes zero to C0_wired | ||
347 | * on resume, making it necessary to recreate it as soon as possible. | ||
348 | */ | ||
349 | ctx->wired_entry = 8191; /* impossibly high value */ | ||
350 | alchemy_pci_wired_entry(ctx); /* install it */ | ||
351 | } | ||
352 | |||
353 | static struct syscore_ops alchemy_pci_pmops = { | ||
354 | .suspend = alchemy_pci_suspend, | ||
355 | .resume = alchemy_pci_resume, | ||
356 | }; | ||
357 | |||
307 | static int __devinit alchemy_pci_probe(struct platform_device *pdev) | 358 | static int __devinit alchemy_pci_probe(struct platform_device *pdev) |
308 | { | 359 | { |
309 | struct alchemy_pci_platdata *pd = pdev->dev.platform_data; | 360 | struct alchemy_pci_platdata *pd = pdev->dev.platform_data; |
@@ -396,7 +447,8 @@ static int __devinit alchemy_pci_probe(struct platform_device *pdev) | |||
396 | ret = -ENOMEM; | 447 | ret = -ENOMEM; |
397 | goto out4; | 448 | goto out4; |
398 | } | 449 | } |
399 | ctx->wired_entry = 8192; /* impossibly high value */ | 450 | ctx->wired_entry = 8191; /* impossibly high value */ |
451 | alchemy_pci_wired_entry(ctx); /* install it */ | ||
400 | 452 | ||
401 | set_io_port_base((unsigned long)ctx->alchemy_pci_ctrl.io_map_base); | 453 | set_io_port_base((unsigned long)ctx->alchemy_pci_ctrl.io_map_base); |
402 | 454 | ||
@@ -408,7 +460,9 @@ static int __devinit alchemy_pci_probe(struct platform_device *pdev) | |||
408 | __raw_writel(val, ctx->regs + PCI_REG_CONFIG); | 460 | __raw_writel(val, ctx->regs + PCI_REG_CONFIG); |
409 | wmb(); | 461 | wmb(); |
410 | 462 | ||
463 | __alchemy_pci_ctx = ctx; | ||
411 | platform_set_drvdata(pdev, ctx); | 464 | platform_set_drvdata(pdev, ctx); |
465 | register_syscore_ops(&alchemy_pci_pmops); | ||
412 | register_pci_controller(&ctx->alchemy_pci_ctrl); | 466 | register_pci_controller(&ctx->alchemy_pci_ctrl); |
413 | 467 | ||
414 | return 0; | 468 | return 0; |
@@ -425,68 +479,11 @@ out: | |||
425 | return ret; | 479 | return ret; |
426 | } | 480 | } |
427 | 481 | ||
428 | |||
429 | #ifdef CONFIG_PM | ||
430 | /* save PCI controller register contents. */ | ||
431 | static int alchemy_pci_suspend(struct device *dev) | ||
432 | { | ||
433 | struct alchemy_pci_context *ctx = dev_get_drvdata(dev); | ||
434 | |||
435 | ctx->pm[0] = __raw_readl(ctx->regs + PCI_REG_CMEM); | ||
436 | ctx->pm[1] = __raw_readl(ctx->regs + PCI_REG_CONFIG) & 0x0009ffff; | ||
437 | ctx->pm[2] = __raw_readl(ctx->regs + PCI_REG_B2BMASK_CCH); | ||
438 | ctx->pm[3] = __raw_readl(ctx->regs + PCI_REG_B2BBASE0_VID); | ||
439 | ctx->pm[4] = __raw_readl(ctx->regs + PCI_REG_B2BBASE1_SID); | ||
440 | ctx->pm[5] = __raw_readl(ctx->regs + PCI_REG_MWMASK_DEV); | ||
441 | ctx->pm[6] = __raw_readl(ctx->regs + PCI_REG_MWBASE_REV_CCL); | ||
442 | ctx->pm[7] = __raw_readl(ctx->regs + PCI_REG_ID); | ||
443 | ctx->pm[8] = __raw_readl(ctx->regs + PCI_REG_CLASSREV); | ||
444 | ctx->pm[9] = __raw_readl(ctx->regs + PCI_REG_PARAM); | ||
445 | ctx->pm[10] = __raw_readl(ctx->regs + PCI_REG_MBAR); | ||
446 | ctx->pm[11] = __raw_readl(ctx->regs + PCI_REG_TIMEOUT); | ||
447 | |||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | static int alchemy_pci_resume(struct device *dev) | ||
452 | { | ||
453 | struct alchemy_pci_context *ctx = dev_get_drvdata(dev); | ||
454 | |||
455 | __raw_writel(ctx->pm[0], ctx->regs + PCI_REG_CMEM); | ||
456 | __raw_writel(ctx->pm[2], ctx->regs + PCI_REG_B2BMASK_CCH); | ||
457 | __raw_writel(ctx->pm[3], ctx->regs + PCI_REG_B2BBASE0_VID); | ||
458 | __raw_writel(ctx->pm[4], ctx->regs + PCI_REG_B2BBASE1_SID); | ||
459 | __raw_writel(ctx->pm[5], ctx->regs + PCI_REG_MWMASK_DEV); | ||
460 | __raw_writel(ctx->pm[6], ctx->regs + PCI_REG_MWBASE_REV_CCL); | ||
461 | __raw_writel(ctx->pm[7], ctx->regs + PCI_REG_ID); | ||
462 | __raw_writel(ctx->pm[8], ctx->regs + PCI_REG_CLASSREV); | ||
463 | __raw_writel(ctx->pm[9], ctx->regs + PCI_REG_PARAM); | ||
464 | __raw_writel(ctx->pm[10], ctx->regs + PCI_REG_MBAR); | ||
465 | __raw_writel(ctx->pm[11], ctx->regs + PCI_REG_TIMEOUT); | ||
466 | wmb(); | ||
467 | __raw_writel(ctx->pm[1], ctx->regs + PCI_REG_CONFIG); | ||
468 | wmb(); | ||
469 | |||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | static const struct dev_pm_ops alchemy_pci_pmops = { | ||
474 | .suspend = alchemy_pci_suspend, | ||
475 | .resume = alchemy_pci_resume, | ||
476 | }; | ||
477 | |||
478 | #define ALCHEMY_PCICTL_PM (&alchemy_pci_pmops) | ||
479 | |||
480 | #else | ||
481 | #define ALCHEMY_PCICTL_PM NULL | ||
482 | #endif | ||
483 | |||
484 | static struct platform_driver alchemy_pcictl_driver = { | 482 | static struct platform_driver alchemy_pcictl_driver = { |
485 | .probe = alchemy_pci_probe, | 483 | .probe = alchemy_pci_probe, |
486 | .driver = { | 484 | .driver = { |
487 | .name = "alchemy-pci", | 485 | .name = "alchemy-pci", |
488 | .owner = THIS_MODULE, | 486 | .owner = THIS_MODULE, |
489 | .pm = ALCHEMY_PCICTL_PM, | ||
490 | }, | 487 | }, |
491 | }; | 488 | }; |
492 | 489 | ||