summaryrefslogtreecommitdiff
path: root/arch/x86/cpu/apollolake/pmc.c
blob: e23d38ea0729ac8b7f36efbf187e3bdb51173e5a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2017 Intel Corporation.
 * Copyright 2019 Google LLC
 *
 * Modified from coreboot pmclib.c, pmc.c and pmutil.c
 */

#define LOG_CATEGORY UCLASS_ACPI_PMC

#include <common.h>
#include <dt-structs.h>
#include <dm.h>
#include <log.h>
#include <spl.h>
#include <acpi/acpi_s3.h>
#include <asm/io.h>
#include <asm/pci.h>
#include <asm/arch/pmc.h>
#include <linux/bitops.h>
#include <power/acpi_pmc.h>

#define GPIO_GPE_CFG		0x1050

/* Memory mapped IO registers behind PMC_BASE_ADDRESS */
#define PRSTS			0x1000
#define GEN_PMCON1		0x1020
#define  COLD_BOOT_STS		BIT(27)
#define  COLD_RESET_STS		BIT(26)
#define  WARM_RESET_STS		BIT(25)
#define  GLOBAL_RESET_STS	BIT(24)
#define  SRS			BIT(20)
#define  MS4V			BIT(18)
#define  RPS			BIT(2)
#define GEN_PMCON1_CLR1_BITS	(COLD_BOOT_STS | COLD_RESET_STS | \
				 WARM_RESET_STS | GLOBAL_RESET_STS | \
				 SRS | MS4V)
#define GEN_PMCON2		0x1024
#define GEN_PMCON3		0x1028

/* Offset of TCO registers from ACPI base I/O address */
#define TCO_REG_OFFSET		0x60
#define TCO1_STS	0x64
#define   DMISCI_STS	BIT(9)
#define   BOOT_STS	BIT(18)
#define TCO2_STS	0x66
#define TCO1_CNT	0x68
#define   TCO_LOCK	BIT(12)
#define TCO2_CNT	0x6a

enum {
	ETR		= 0x1048,
	CF9_LOCK        = 1UL << 31,
	CF9_GLB_RST	= 1 << 20,
};

static int apl_pmc_fill_power_state(struct udevice *dev)
{
	struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev);

	upriv->tco1_sts = inw(upriv->acpi_base + TCO1_STS);
	upriv->tco2_sts = inw(upriv->acpi_base + TCO2_STS);

	upriv->prsts = readl(upriv->pmc_bar0 + PRSTS);
	upriv->gen_pmcon1 = readl(upriv->pmc_bar0 + GEN_PMCON1);
	upriv->gen_pmcon2 = readl(upriv->pmc_bar0 + GEN_PMCON2);
	upriv->gen_pmcon3 = readl(upriv->pmc_bar0 + GEN_PMCON3);

	return 0;
}

static int apl_prev_sleep_state(struct udevice *dev, int prev_sleep_state)
{
	struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev);

	/* WAK_STS bit will not be set when waking from G3 state */
	if (!(upriv->pm1_sts & WAK_STS) &&
	    (upriv->gen_pmcon1 & COLD_BOOT_STS))
		prev_sleep_state = ACPI_S5;

	return prev_sleep_state;
}

static int apl_disable_tco(struct udevice *dev)
{
	struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev);

	pmc_disable_tco_base(upriv->acpi_base + TCO_REG_OFFSET);

	return 0;
}

static int apl_global_reset_set_enable(struct udevice *dev, bool enable)
{
	struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev);

	if (enable)
		setbits_le32(upriv->pmc_bar0 + ETR, CF9_GLB_RST);
	else
		clrbits_le32(upriv->pmc_bar0 + ETR, CF9_GLB_RST);

	return 0;
}

int apl_pmc_ofdata_to_uc_plat(struct udevice *dev)
{
	struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev);
	struct apl_pmc_plat *plat = dev_get_plat(dev);

#if !CONFIG_IS_ENABLED(OF_PLATDATA)
	u32 base[6];
	int size;
	int ret;

	ret = dev_read_u32_array(dev, "early-regs", base,
				 ARRAY_SIZE(base));
	if (ret)
		return log_msg_ret("Missing/short early-regs", ret);
	if (spl_phase() == PHASE_TPL) {
		upriv->pmc_bar0 = (void *)base[0];
		upriv->pmc_bar2 = (void *)base[2];

		/* Since PCI is not enabled, we must get the BDF manually */
		plat->bdf = pci_get_devfn(dev);
		if (plat->bdf < 0)
			return log_msg_ret("Cannot get PMC PCI address",
					   plat->bdf);
	}
	upriv->acpi_base = base[4];

	/* Get the dwX values for pmc gpe settings */
	size = dev_read_size(dev, "gpe0-dw");
	if (size < 0)
		return log_msg_ret("Cannot read gpe0-dm", size);
	upriv->gpe0_count = size / sizeof(u32);
	ret = dev_read_u32_array(dev, "gpe0-dw", upriv->gpe0_dw,
				 upriv->gpe0_count);
	if (ret)
		return log_msg_ret("Bad gpe0-dw", ret);

	return pmc_ofdata_to_uc_plat(dev);
#else
	struct dtd_intel_apl_pmc *dtplat = &plat->dtplat;

	plat->bdf = pci_ofplat_get_devfn(dtplat->reg[0]);
	upriv->pmc_bar0 = (void *)dtplat->early_regs[0];
	upriv->pmc_bar2 = (void *)dtplat->early_regs[2];
	upriv->acpi_base = dtplat->early_regs[4];
	upriv->gpe0_dwx_mask = dtplat->gpe0_dwx_mask;
	upriv->gpe0_dwx_shift_base = dtplat->gpe0_dwx_shift_base;
	upriv->gpe0_sts_reg = dtplat->gpe0_sts;
	upriv->gpe0_sts_reg += upriv->acpi_base;
	upriv->gpe0_en_reg = dtplat->gpe0_en;
	upriv->gpe0_en_reg += upriv->acpi_base;
	upriv->gpe0_count = min((int)ARRAY_SIZE(dtplat->gpe0_dw), GPE0_REG_MAX);
	memcpy(upriv->gpe0_dw, dtplat->gpe0_dw, sizeof(dtplat->gpe0_dw));
#endif
	upriv->gpe_cfg = (u32 *)(upriv->pmc_bar0 + GPIO_GPE_CFG);

	return 0;
}

static int enable_pmcbar(struct udevice *dev)
{
	struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev);
	struct apl_pmc_plat *priv = dev_get_plat(dev);
	pci_dev_t pmc = priv->bdf;

	/*
	 * Set PMC base addresses and enable decoding. BARs 1 and 3 are 64-bit
	 * BARs.
	 */
	pci_x86_write_config(pmc, PCI_BASE_ADDRESS_0, (ulong)upriv->pmc_bar0,
			     PCI_SIZE_32);
	pci_x86_write_config(pmc, PCI_BASE_ADDRESS_1, 0, PCI_SIZE_32);
	pci_x86_write_config(pmc, PCI_BASE_ADDRESS_2, (ulong)upriv->pmc_bar2,
			     PCI_SIZE_32);
	pci_x86_write_config(pmc, PCI_BASE_ADDRESS_3, 0, PCI_SIZE_32);
	pci_x86_write_config(pmc, PCI_BASE_ADDRESS_4, upriv->acpi_base,
			     PCI_SIZE_16);
	pci_x86_write_config(pmc, PCI_COMMAND, PCI_COMMAND_IO |
			     PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER,
			     PCI_SIZE_16);

	return 0;
}

static int apl_pmc_probe(struct udevice *dev)
{
	if (spl_phase() == PHASE_TPL) {
		return enable_pmcbar(dev);
	} else {
		struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev);

		upriv->pmc_bar0 = (void *)dm_pci_read_bar32(dev, 0);
		upriv->pmc_bar2 = (void *)dm_pci_read_bar32(dev, 2);
	}

	return 0;
}

static const struct acpi_pmc_ops apl_pmc_ops = {
	.init			= apl_pmc_fill_power_state,
	.prev_sleep_state	= apl_prev_sleep_state,
	.disable_tco		= apl_disable_tco,
	.global_reset_set_enable = apl_global_reset_set_enable,
};

#if !CONFIG_IS_ENABLED(OF_PLATDATA)
static const struct udevice_id apl_pmc_ids[] = {
	{ .compatible = "intel,apl-pmc" },
	{ }
};
#endif

U_BOOT_DRIVER(intel_apl_pmc) = {
	.name		= "intel_apl_pmc",
	.id		= UCLASS_ACPI_PMC,
	.of_match	= of_match_ptr(apl_pmc_ids),
	.of_to_plat = apl_pmc_ofdata_to_uc_plat,
	.probe		= apl_pmc_probe,
	.ops		= &apl_pmc_ops,
	.plat_auto	= sizeof(struct apl_pmc_plat),
};