summaryrefslogtreecommitdiff
path: root/board/chromebook-x86/chromeos/power_management.c
blob: 2910f3a0a16a657e261e09c1462af9642167270c (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
/*
 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2 as published by the Free
 * Software Foundation.
 */

/* Implementation of per-board power management function */

#include <chromeos/power_management.h>
#include <common.h>
#include <asm/global_data.h>
#include <asm/io.h>
#include <pci.h>

#define PM1_STS         0x00
#define   PWRBTN_STS    (1 << 8)
#define PM1_EN          0x02
#define PM1_CNT         0x04
#define   SLP_EN        (1 << 13)
#define   SLP_TYP       (7 << 10)
#define   SLP_TYP_S0    (0 << 10)
#define   SLP_TYP_S1    (1 << 10)
#define   SLP_TYP_S3    (5 << 10)
#define   SLP_TYP_S4    (6 << 10)
#define   SLP_TYP_S5    (7 << 10)
#define GPE0_EN         0x2c

#define RST_CNT         0xcf9
#define   SYS_RST       (1 << 1)
#define   RST_CPU       (1 << 2)

DECLARE_GLOBAL_DATA_PTR;

int is_processor_reset(void)
{
	/*
	 * This isn't actually whether or not this boot is the result of a
	 * cold boot, it's really whether u-boot was started from the ELF
	 * entry point or from the start of the image. It also isn't really
	 * being used to check if the processor was reset either, it's
	 * checking if this copy of u-boot is the RW or RO firmware. This is a
	 * good enough approximation, though, and causes the right behavior.
	 */
	return gd->flags & GD_FLG_COLD_BOOT;
}

/* Do a hard reset through the chipset's reset control register. This
 * register is available on all x86 systems (at least those built in
 * the last 10ys)
 *
 * This function never returns.
 */
void cold_reboot(void)
{
	printf("Rebooting...\n");
	outb(SYS_RST, RST_CNT);
	outb(SYS_RST | RST_CPU, RST_CNT);

	for (;;)
		asm("hlt");
}

/* Power down the machine by using the power management sleep control
 * of the chipset. This will currently only work on Intel chipsets.
 * However, adapting it to new chipsets is fairly simple. You will
 * have to find the IO address of the power management register block
 * in your southbridge, and look up the appropriate SLP_TYP_S5 value
 * from your southbridge's data sheet.
 *
 * This function never returns.
 */
void power_off(void)
{
	u16 id, pmbase;
	u32 reg32;

	/* Make sure this is an Intel chipset with the
	 * LPC device hard coded at 0:1f.0
	 */
	pci_read_config_word(PCI_BDF(0, 0x1f, 0), 0x00, &id);
	if(id != 0x8086) {
		printf("Power off is not implemented for this chipset. "
		       "Halting the CPU.\n");
		for (;;)
			asm("hlt");
	}

	/* Find the base address of the powermanagement registers */
	pci_read_config_word(PCI_BDF(0, 0x1f, 0), 0x40, &pmbase);
	pmbase &= 0xfffe;

	/* Mask interrupts or system might stay in a coma
	 * (not executing code anymore, but not powered off either)
	 */
	asm("cli");

	/* Avoid any GPI waking the system from S5
	 * or the system might stay in a coma
	 */
	outl(0x00000000, pmbase + GPE0_EN);

	/* Clear Power Button Status */
	outw(PWRBTN_STS, pmbase + PM1_STS);

	/* PMBASE + 4, Bit 10-12, Sleeping Type,
	 * set to 111 -> S5, soft_off */

	reg32 = inl(pmbase + PM1_CNT);

	/* Set Sleeping Type to S5 (poweroff) */
	reg32 &= ~(SLP_EN | SLP_TYP);
	reg32 |= SLP_TYP_S5;
	outl(reg32, pmbase + PM1_CNT);

	/* Now set the Sleep Enable bit */
	reg32 |= SLP_EN;
	outl(reg32, pmbase + PM1_CNT);

	for (;;)
		asm("hlt");
}