summaryrefslogtreecommitdiff
path: root/board/primus/ps2.c
blob: e5ac1f0aa7b34192eeccc3f873990870050a0e97 (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
/* Copyright 2021 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.
 */

#include <stddef.h>

#include "cbi_ssfc.h"
#include "gpio.h"
#include "hooks.h"
#include "keyboard_8042.h"
#include "ps2.h"
#include "ps2_chip.h"
#include "time.h"
#include "registers.h"

#define	PS2_TRANSMIT_DELAY_MS	10

void send_aux_data_to_device(uint8_t data)
{
	ps2_transmit_byte(PRIMUS_PS2_CH, data);
}

static void board_init(void)
{
	ps2_enable_channel(PRIMUS_PS2_CH, 1, send_aux_data_to_host_interrupt);
}
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);

/*
 * Goodix touchpad AVDD need to pull low to 0V when poweroff.
 * Setting PS2 module in GPIO.inc will let AVDD have 0.9V offset.
 * So we need to enable PS2 module later than PLTRST# to avoid the 0.9V
 * offset.
 */
static void enable_ps2(void)
{
	gpio_set_alternate_function(GPIO_PORT_6,
		BIT(2) | BIT(3), GPIO_ALT_FUNC_DEFAULT);
}
DECLARE_DEFERRED(enable_ps2);

static void disable_ps2(void)
{
	gpio_set_flags(GPIO_EC_PS2_SCL_TPAD, GPIO_ODR_LOW);
	gpio_set_flags(GPIO_EC_PS2_SDA_TPAD, GPIO_ODR_LOW);
	gpio_set_alternate_function(GPIO_PORT_6,
		BIT(2) | BIT(3), GPIO_ALT_FUNC_NONE);
	/* make sure PLTRST# goes high and re-enable ps2.*/
	hook_call_deferred(&enable_ps2_data, 2 * SECOND);
}
DECLARE_HOOK(HOOK_CHIPSET_RESET, disable_ps2, HOOK_PRIO_DEFAULT);

static void ps2_transmit(uint8_t cmd)
{
	ps2_transmit_byte(PRIMUS_PS2_CH, cmd);
	msleep(PS2_TRANSMIT_DELAY_MS);
}

static void send_command_to_trackpoint(uint8_t command1, uint8_t command2)
{
	/*
	 * Send command to trackpoint and wait,
	 * this will make sure EC get ACK from PS2 device
	 * and send the next command.
	 */

	ps2_transmit(TP_COMMAND);
	ps2_transmit(TP_TOGGLE);
	ps2_transmit(command1);
	ps2_transmit(command2);
}

int get_trackpoint_id(void)
{
	if (get_cbi_ssfc_trackpoint() == SSFC_SENSOR_TRACKPOINT_ELAN)
		return TP_VARIANT_ELAN;
	else
		return TP_VARIANT_SYNAPTICS;
}

/* Called on AP S0 -> S3 transition */
static void ps2_suspend(void)
{
	int trackpoint_id;
	/*
	 * When EC send PS2 command to PS2 device,
	 * PS2 device will return ACK(0xFA).
	 * EC will send it to host and cause host wake from suspend.
	 * So disable EC send data to host to avoid it.
	 */
	ps2_enable_channel(PRIMUS_PS2_CH, 1, NULL);
	trackpoint_id = get_trackpoint_id();
	/*
	 * Send suspend mode to trackpoint
	 * Those commands was provide by Elan and Synaptics
	 */
	if (trackpoint_id == TP_VARIANT_ELAN)
		send_command_to_trackpoint(TP_TOGGLE_BURST,
			TP_TOGGLE_ELAN_SLEEP);
	else if (trackpoint_id == TP_VARIANT_SYNAPTICS)
		send_command_to_trackpoint(TP_TOGGLE_SOURCE_TAG,
			TP_TOGGLE_SNAPTICS_SLEEP);
}
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, ps2_suspend, HOOK_PRIO_DEFAULT);

/* Called on AP S3 -> S0 transition */
static void ps2_resume(void)
{
	int trackpoint_id;

	trackpoint_id = get_trackpoint_id();
	ps2_enable_channel(PRIMUS_PS2_CH, 1, send_aux_data_to_host_interrupt);
	/*
	 * For Synaptics trackpoint, EC need to send command to it again.
	 * For Elan trackpoint, we just need to touch trackpoint and it wake.
	 */
	if (trackpoint_id == TP_VARIANT_SYNAPTICS)
		send_command_to_trackpoint(TP_TOGGLE_SOURCE_TAG,
			TP_TOGGLE_SNAPTICS_SLEEP);
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, ps2_resume, HOOK_PRIO_DEFAULT);