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