xref: /openbmc/u-boot/arch/x86/lib/pmu.c (revision ae485b54)
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(&regs->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(&regs->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, &regs->ssc[offset]);
73 
74 	/* Update modified PMU values */
75 	writel(0x00002201, &regs->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