pmu.c 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (c) 2017 Intel Corporation
  4. */
  5. #include <common.h>
  6. #include <dm.h>
  7. #include <regmap.h>
  8. #include <syscon.h>
  9. #include <asm/cpu.h>
  10. #include <asm/pmu.h>
  11. #include <linux/errno.h>
  12. #include <linux/io.h>
  13. /* Registers */
  14. struct pmu_regs {
  15. u32 sts;
  16. u32 cmd;
  17. u32 ics;
  18. u32 reserved;
  19. u32 wkc[4];
  20. u32 wks[4];
  21. u32 ssc[4];
  22. u32 sss[4];
  23. };
  24. /* Bits in PMU_REGS_STS */
  25. #define PMU_REGS_STS_BUSY (1 << 8)
  26. struct pmu_mid {
  27. struct pmu_regs *regs;
  28. };
  29. static int pmu_read_status(struct pmu_regs *regs)
  30. {
  31. int retry = 500000;
  32. u32 val;
  33. do {
  34. val = readl(&regs->sts);
  35. if (!(val & PMU_REGS_STS_BUSY))
  36. return 0;
  37. udelay(1);
  38. } while (--retry);
  39. printf("WARNING: PMU still busy\n");
  40. return -EBUSY;
  41. }
  42. static int pmu_power_lss(struct pmu_regs *regs, unsigned int lss, bool on)
  43. {
  44. unsigned int offset = (lss * 2) / 32;
  45. unsigned int shift = (lss * 2) % 32;
  46. u32 ssc;
  47. int ret;
  48. /* Check PMU status */
  49. ret = pmu_read_status(regs);
  50. if (ret)
  51. return ret;
  52. /* Read PMU values */
  53. ssc = readl(&regs->sss[offset]);
  54. /* Modify PMU values */
  55. if (on)
  56. ssc &= ~(0x3 << shift); /* D0 */
  57. else
  58. ssc |= 0x3 << shift; /* D3hot */
  59. /* Write modified PMU values */
  60. writel(ssc, &regs->ssc[offset]);
  61. /* Update modified PMU values */
  62. writel(0x00002201, &regs->cmd);
  63. /* Check PMU status */
  64. return pmu_read_status(regs);
  65. }
  66. int pmu_turn_power(unsigned int lss, bool on)
  67. {
  68. struct pmu_mid *pmu;
  69. struct udevice *dev;
  70. int ret;
  71. ret = syscon_get_by_driver_data(X86_SYSCON_PMU, &dev);
  72. if (ret)
  73. return ret;
  74. pmu = dev_get_priv(dev);
  75. return pmu_power_lss(pmu->regs, lss, on);
  76. }
  77. static int pmu_mid_probe(struct udevice *dev)
  78. {
  79. struct pmu_mid *pmu = dev_get_priv(dev);
  80. pmu->regs = syscon_get_first_range(X86_SYSCON_PMU);
  81. return 0;
  82. }
  83. static const struct udevice_id pmu_mid_match[] = {
  84. { .compatible = "intel,pmu-mid", .data = X86_SYSCON_PMU },
  85. { /* sentinel */ }
  86. };
  87. U_BOOT_DRIVER(intel_mid_pmu) = {
  88. .name = "pmu_mid",
  89. .id = UCLASS_SYSCON,
  90. .of_match = pmu_mid_match,
  91. .probe = pmu_mid_probe,
  92. .priv_auto_alloc_size = sizeof(struct pmu_mid),
  93. };