leon_pmc.c 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. /* leon_pmc.c: LEON Power-down cpu_idle() handler
  2. *
  3. * Copyright (C) 2011 Daniel Hellstrom (daniel@gaisler.com) Aeroflex Gaisler AB
  4. */
  5. #include <linux/init.h>
  6. #include <linux/pm.h>
  7. #include <asm/leon_amba.h>
  8. #include <asm/cpu_type.h>
  9. #include <asm/leon.h>
  10. /* List of Systems that need fixup instructions around power-down instruction */
  11. unsigned int pmc_leon_fixup_ids[] = {
  12. AEROFLEX_UT699,
  13. GAISLER_GR712RC,
  14. LEON4_NEXTREME1,
  15. 0
  16. };
  17. int pmc_leon_need_fixup(void)
  18. {
  19. unsigned int systemid = amba_system_id >> 16;
  20. unsigned int *id;
  21. id = &pmc_leon_fixup_ids[0];
  22. while (*id != 0) {
  23. if (*id == systemid)
  24. return 1;
  25. id++;
  26. }
  27. return 0;
  28. }
  29. /*
  30. * CPU idle callback function for systems that need some extra handling
  31. * See .../arch/sparc/kernel/process.c
  32. */
  33. void pmc_leon_idle_fixup(void)
  34. {
  35. /* Prepare an address to a non-cachable region. APB is always
  36. * none-cachable. One instruction is executed after the Sleep
  37. * instruction, we make sure to read the bus and throw away the
  38. * value by accessing a non-cachable area, also we make sure the
  39. * MMU does not get a TLB miss here by using the MMU BYPASS ASI.
  40. */
  41. register unsigned int address = (unsigned int)leon3_irqctrl_regs;
  42. __asm__ __volatile__ (
  43. "mov %%g0, %%asr19\n"
  44. "lda [%0] %1, %%g0\n"
  45. :
  46. : "r"(address), "i"(ASI_LEON_BYPASS));
  47. }
  48. /*
  49. * CPU idle callback function
  50. * See .../arch/sparc/kernel/process.c
  51. */
  52. void pmc_leon_idle(void)
  53. {
  54. /* For systems without power-down, this will be no-op */
  55. __asm__ __volatile__ ("mov %g0, %asr19\n\t");
  56. }
  57. /* Install LEON Power Down function */
  58. static int __init leon_pmc_install(void)
  59. {
  60. if (sparc_cpu_model == sparc_leon) {
  61. /* Assign power management IDLE handler */
  62. if (pmc_leon_need_fixup())
  63. pm_idle = pmc_leon_idle_fixup;
  64. else
  65. pm_idle = pmc_leon_idle;
  66. printk(KERN_INFO "leon: power management initialized\n");
  67. }
  68. return 0;
  69. }
  70. /* This driver is not critical to the boot process, don't care
  71. * if initialized late.
  72. */
  73. late_initcall(leon_pmc_install);