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/delay.h> 12#include <linux/errno.h> 13#include <linux/io.h> 14 15/* Registers */ 16struct 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 30struct pmu_mid { 31 struct pmu_regs *regs; 32}; 33 34static 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 51static 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 82int 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 97static 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 106static const struct udevice_id pmu_mid_match[] = { 107 { .compatible = "intel,pmu-mid", .data = X86_SYSCON_PMU }, 108 { /* sentinel */ } 109}; 110 111U_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 = sizeof(struct pmu_mid), 117}; 118