Merge tag 'u-boot-atmel-fixes-2020.07-a' of https://gitlab.denx.de/u-boot/custodians...
[oweals/u-boot.git] / arch / x86 / lib / pmu.c
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 */
16 struct 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
30 struct pmu_mid {
31         struct pmu_regs *regs;
32 };
33
34 static 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
51 static 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
82 int 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
97 static 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
106 static const struct udevice_id pmu_mid_match[] = {
107         { .compatible = "intel,pmu-mid", .data = X86_SYSCON_PMU },
108         { /* sentinel */ }
109 };
110
111 U_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 };