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