// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2017 Intel Corporation */ #include #include #include #include #include #include #include #include #include /* Registers */ struct pmu_regs { u32 sts; u32 cmd; u32 ics; u32 reserved; u32 wkc[4]; u32 wks[4]; u32 ssc[4]; u32 sss[4]; }; /* Bits in PMU_REGS_STS */ #define PMU_REGS_STS_BUSY (1 << 8) struct pmu_mid { struct pmu_regs *regs; }; static int pmu_read_status(struct pmu_regs *regs) { int retry = 500000; u32 val; do { val = readl(®s->sts); if (!(val & PMU_REGS_STS_BUSY)) return 0; udelay(1); } while (--retry); printf("WARNING: PMU still busy\n"); return -EBUSY; } static int pmu_power_lss(struct pmu_regs *regs, unsigned int lss, bool on) { unsigned int offset = (lss * 2) / 32; unsigned int shift = (lss * 2) % 32; u32 ssc; int ret; /* Check PMU status */ ret = pmu_read_status(regs); if (ret) return ret; /* Read PMU values */ ssc = readl(®s->sss[offset]); /* Modify PMU values */ if (on) ssc &= ~(0x3 << shift); /* D0 */ else ssc |= 0x3 << shift; /* D3hot */ /* Write modified PMU values */ writel(ssc, ®s->ssc[offset]); /* Update modified PMU values */ writel(0x00002201, ®s->cmd); /* Check PMU status */ return pmu_read_status(regs); } int pmu_turn_power(unsigned int lss, bool on) { struct pmu_mid *pmu; struct udevice *dev; int ret; ret = syscon_get_by_driver_data(X86_SYSCON_PMU, &dev); if (ret) return ret; pmu = dev_get_priv(dev); return pmu_power_lss(pmu->regs, lss, on); } static int pmu_mid_probe(struct udevice *dev) { struct pmu_mid *pmu = dev_get_priv(dev); pmu->regs = syscon_get_first_range(X86_SYSCON_PMU); return 0; } static const struct udevice_id pmu_mid_match[] = { { .compatible = "intel,pmu-mid", .data = X86_SYSCON_PMU }, { /* sentinel */ } }; U_BOOT_DRIVER(intel_mid_pmu) = { .name = "pmu_mid", .id = UCLASS_SYSCON, .of_match = pmu_mid_match, .probe = pmu_mid_probe, .priv_auto = sizeof(struct pmu_mid), };