blob: 3cd7b1f536e548e662e441d77a90d3d9bcc3703e [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Andy Shevchenko7d2c2012017-04-01 16:21:34 +03002/*
3 * Copyright (c) 2017 Intel Corporation
Andy Shevchenko7d2c2012017-04-01 16:21:34 +03004 */
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>
Simon Glassdbd79542020-05-10 11:40:11 -060011#include <linux/delay.h>
Andy Shevchenko7d2c2012017-04-01 16:21:34 +030012#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(&regs->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(&regs->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, &regs->ssc[offset]);
74
75 /* Update modified PMU values */
76 writel(0x00002201, &regs->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_alloc_size = sizeof(struct pmu_mid),
117};