summaryrefslogtreecommitdiff
path: root/baseboard/dedede/variant_ec_npcx796fc.c
blob: 366fca878ede8109e03cd46e0e2a2527f2a4a384 (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
/* Copyright 2020 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* Common code for VARIANT_[DEDEDE|KEEBY]_NPCX79[6/7]FC configuration */

#include "adc.h"
#include "atomic.h"
#include "chipset.h"
#include "common.h"
#include "compile_time_macros.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "i2c.h"
#include "lid_switch.h"
#include "power.h"
#include "registers.h"
#include "task.h"
#include "timer.h"

/* Console output macros */
#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ##args)
#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ##args)

void pp3300_a_pgood_high(void)
{
	atomic_or(&pp3300_a_pgood, 1);

	/* Disable this interrupt while it's asserted. */
	npcx_adc_thresh_int_enable(NPCX_ADC_THRESH1, 0);
	/* Enable the voltage low interrupt. */
	npcx_adc_thresh_int_enable(NPCX_ADC_THRESH2, 1);

	/*
	 * Call power_signal_interrupt() with a fake GPIO in order for the
	 * chipset task to pick up the change in power sequencing signals.
	 */
	power_signal_interrupt(GPIO_PG_EC_DSW_PWROK);
}

void pp3300_a_pgood_low(void)
{
	atomic_clear_bits(&pp3300_a_pgood, 1);

	/* Disable this interrupt while it's asserted. */
	npcx_adc_thresh_int_enable(NPCX_ADC_THRESH2, 0);
	/* Enable the voltage high interrupt. */
	npcx_adc_thresh_int_enable(NPCX_ADC_THRESH1, 1);

	/*
	 * Call power_signal_interrupt() with a fake GPIO in order for the
	 * chipset task to pick up the change in power sequencing signals.
	 */
	power_signal_interrupt(GPIO_PG_EC_DSW_PWROK);
}

const struct npcx_adc_thresh_t adc_pp3300_a_pgood_high = {
	.adc_ch = ADC_VSNS_PP3300_A,
	.adc_thresh_cb = pp3300_a_pgood_high,
	.thresh_assert = 2700,
};

const struct npcx_adc_thresh_t adc_pp3300_a_pgood_low = {
	.adc_ch = ADC_VSNS_PP3300_A,
	.adc_thresh_cb = pp3300_a_pgood_low,
	.lower_or_higher = 1,
	.thresh_assert = 600,
};

static void set_up_adc_irqs(void)
{
	/* Set interrupt thresholds for the ADC. */
	npcx_adc_register_thresh_irq(NPCX_ADC_THRESH1,
				     &adc_pp3300_a_pgood_high);
	npcx_adc_register_thresh_irq(NPCX_ADC_THRESH2, &adc_pp3300_a_pgood_low);
	npcx_set_adc_repetitive(adc_channels[ADC_VSNS_PP3300_A].input_ch, 1);
	npcx_adc_thresh_int_enable(NPCX_ADC_THRESH1, 1);
	npcx_adc_thresh_int_enable(NPCX_ADC_THRESH2, 1);
}
DECLARE_HOOK(HOOK_INIT, set_up_adc_irqs, HOOK_PRIO_INIT_ADC + 1);

static void disable_adc_irqs_deferred(void)
{
	CPRINTS("%s", __func__);
	npcx_adc_thresh_int_enable(NPCX_ADC_THRESH1, 0);
	npcx_adc_thresh_int_enable(NPCX_ADC_THRESH2, 0);
	npcx_set_adc_repetitive(adc_channels[ADC_VSNS_PP3300_A].input_ch, 0);

	/*
	 * If we're already in G3, PP3300_A is already off.  Since the ADC
	 * interrupts were already disabled, this data is stale.  Therefore,
	 * force the PGOOD value to 0 and have the chipset task re-evaluate.
	 * This should help prevent leakage.
	 */
	if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
		pp3300_a_pgood = 0;
	power_signal_interrupt(GPIO_PG_EC_DSW_PWROK);
}
DECLARE_DEFERRED(disable_adc_irqs_deferred);

/*
 * The assumption is that the PP3300_A rail will not go down during runtime.
 * Therefore, we'll disable the ADC interrupts shortly after booting up
 * and also after shutting down.
 */
static void enable_adc_irqs(void);
static void disable_adc_irqs(void)
{
	int delay = 200 * MSEC;

	/*
	 * The EC stays in S5 for about 10s after shutting before heading down
	 * to G3.  Therefore, we'll postpone disabling the ADC IRQs until after
	 * this occurs.
	 */
	if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) {
		delay = 15 * SECOND;
		enable_adc_irqs();
	}
	hook_call_deferred(&disable_adc_irqs_deferred_data, delay);
}
DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, disable_adc_irqs, HOOK_PRIO_DEFAULT);
DECLARE_HOOK(HOOK_CHIPSET_RESUME, disable_adc_irqs, HOOK_PRIO_DEFAULT);

/*
 * We only need the ADC interrupts functional when powering up.  Therefore, only
 * enable them from our wake sources.  These will be the power button, or lid
 * open.  Below is a summary of the ADC interrupt action per power state and
 * wake source.
 *
 * Powering up to S0: ADC interrupts will be disabled after ~200ms.
 * S0ix/S3: No action as ADC interrupts are already disabled if suspending.
 * Powering down to S5/G3: ADC interrupts will be disabled after ~15s.
 * Powering up from S5/G3: ADC interrupts will be enabled.  They will be
 *                         disabled ~200ms after passing thru S3.
 * Power button press: If the system is in S5/G3, ADC interrupts will be
 *                     enabled.
 * Lid open: ADC interrupts will be enabled.
 */
static void enable_adc_irqs(void)
{
	if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) {
		CPRINTS("%s", __func__);
		hook_call_deferred(&disable_adc_irqs_deferred_data, -1);
		npcx_set_adc_repetitive(
			adc_channels[ADC_VSNS_PP3300_A].input_ch, 1);
		npcx_adc_thresh_int_enable(NPCX_ADC_THRESH1, 1);
		npcx_adc_thresh_int_enable(NPCX_ADC_THRESH2, 1);
	}
}
DECLARE_HOOK(HOOK_CHIPSET_STARTUP, enable_adc_irqs, HOOK_PRIO_DEFAULT);
DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, enable_adc_irqs, HOOK_PRIO_DEFAULT);

static void enable_adc_irqs_via_lid(void)
{
	if (lid_is_open())
		enable_adc_irqs();
}
DECLARE_HOOK(HOOK_LID_CHANGE, enable_adc_irqs_via_lid, HOOK_PRIO_DEFAULT);

/* I2C Ports */
__attribute__((weak)) const struct i2c_port_t i2c_ports[] = {
	{ .name = "eeprom",
	  .port = I2C_PORT_EEPROM,
	  .kbps = 1000,
	  .scl = GPIO_EC_I2C_EEPROM_SCL,
	  .sda = GPIO_EC_I2C_EEPROM_SDA },

	{ .name = "battery",
	  .port = I2C_PORT_BATTERY,
	  .kbps = 100,
	  .scl = GPIO_EC_I2C_BATTERY_SCL,
	  .sda = GPIO_EC_I2C_BATTERY_SDA },

#ifdef HAS_TASK_MOTIONSENSE
	{ .name = "sensor",
	  .port = I2C_PORT_SENSOR,
	  .kbps = 400,
	  .scl = GPIO_EC_I2C_SENSOR_SCL,
	  .sda = GPIO_EC_I2C_SENSOR_SDA },
#endif

	{ .name = "usbc0",
	  .port = I2C_PORT_USB_C0,
	  .kbps = 1000,
	  .scl = GPIO_EC_I2C_USB_C0_SCL,
	  .sda = GPIO_EC_I2C_USB_C0_SDA },
#if CONFIG_USB_PD_PORT_MAX_COUNT > 1
	{ .name = "sub_usbc1",
	  .port = I2C_PORT_SUB_USB_C1,
	  .kbps = 1000,
	  .scl = GPIO_EC_I2C_SUB_USB_C1_SCL,
	  .sda = GPIO_EC_I2C_SUB_USB_C1_SDA },
#endif
#ifdef BOARD_BUGZZY
	{ .name = "lcd",
	  .port = I2C_PORT_LCD,
	  .kbps = 400,
	  .scl = GPIO_EC_I2C_LCD_SCL,
	  .sda = GPIO_EC_I2C_LCD_SDA },
#endif
};
const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);