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