summaryrefslogtreecommitdiff
path: root/zephyr/shim/src/tcpc.c
blob: 98a3cd880bf36af346056bb1252118d9d0197f45 (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
/* Copyright 2022 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "hooks.h"
#include "usb_pd.h"
#include "usb_pd_tcpm.h"
#include "usbc/tcpc_anx7447.h"
#include "usbc/tcpc_anx7447_emul.h"
#include "usbc/tcpc_ccgxxf.h"
#include "usbc/tcpc_fusb302.h"
#include "usbc/tcpc_generic_emul.h"
#include "usbc/tcpc_it8xxx2.h"
#include "usbc/tcpc_nct38xx.h"
#include "usbc/tcpc_ps8xxx.h"
#include "usbc/tcpc_ps8xxx_emul.h"
#include "usbc/tcpc_raa489000.h"
#include "usbc/tcpc_rt1718s.h"
#include "usbc/tcpci.h"
#include "usbc/utils.h"

#include <zephyr/devicetree.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>

LOG_MODULE_REGISTER(tcpc, CONFIG_GPIO_LOG_LEVEL);

#define HAS_TCPC_PROP(usbc_id) \
	COND_CODE_1(DT_NODE_HAS_PROP(usbc_id, tcpc), (|| 1), ())

#define DT_HAS_TCPC (0 DT_FOREACH_STATUS_OKAY(named_usbc_port, HAS_TCPC_PROP))

#if DT_HAS_TCPC

#define TCPC_CHIP_ENTRY(usbc_id, tcpc_id, config_fn) \
	[USBC_PORT_NEW(usbc_id)] = config_fn(tcpc_id)

#define CHECK_COMPAT(compat, usbc_id, tcpc_id, config_fn) \
	COND_CODE_1(DT_NODE_HAS_COMPAT(tcpc_id, compat),  \
		    (TCPC_CHIP_ENTRY(usbc_id, tcpc_id, config_fn)), ())

#ifdef TEST_BUILD
#define TCPC_CHIP_FIND_EMUL(usbc_id, tcpc_id)               \
	CHECK_COMPAT(TCPCI_EMUL_COMPAT, usbc_id, tcpc_id,   \
		     TCPC_CONFIG_TCPCI_EMUL)                \
	CHECK_COMPAT(PS8XXX_EMUL_COMPAT, usbc_id, tcpc_id,  \
		     TCPC_CONFIG_PS8XXX_EMUL)               \
	CHECK_COMPAT(ANX7447_EMUL_COMPAT, usbc_id, tcpc_id, \
		     TCPC_CONFIG_ANX7447_EMUL)
#else
#define TCPC_CHIP_FIND_EMUL(...)
#endif /* TEST_BUILD */

#define TCPC_CHIP_FIND(usbc_id, tcpc_id)                                       \
	CHECK_COMPAT(ANX7447_TCPC_COMPAT, usbc_id, tcpc_id,                    \
		     TCPC_CONFIG_ANX7447)                                      \
	CHECK_COMPAT(CCGXXF_TCPC_COMPAT, usbc_id, tcpc_id, TCPC_CONFIG_CCGXXF) \
	CHECK_COMPAT(FUSB302_TCPC_COMPAT, usbc_id, tcpc_id,                    \
		     TCPC_CONFIG_FUSB302)                                      \
	CHECK_COMPAT(IT8XXX2_TCPC_COMPAT, usbc_id, tcpc_id,                    \
		     TCPC_CONFIG_IT8XXX2)                                      \
	CHECK_COMPAT(PS8XXX_COMPAT, usbc_id, tcpc_id, TCPC_CONFIG_PS8XXX)      \
	CHECK_COMPAT(NCT38XX_TCPC_COMPAT, usbc_id, tcpc_id,                    \
		     TCPC_CONFIG_NCT38XX)                                      \
	CHECK_COMPAT(RAA489000_TCPC_COMPAT, usbc_id, tcpc_id,                  \
		     TCPC_CONFIG_RAA489000)                                    \
	CHECK_COMPAT(RT1718S_TCPC_COMPAT, usbc_id, tcpc_id,                    \
		     TCPC_CONFIG_RT1718S)                                      \
	CHECK_COMPAT(TCPCI_COMPAT, usbc_id, tcpc_id, TCPC_CONFIG_TCPCI)        \
	TCPC_CHIP_FIND_EMUL(usbc_id, tcpc_id)

#define TCPC_CHIP(usbc_id)                           \
	COND_CODE_1(DT_NODE_HAS_PROP(usbc_id, tcpc), \
		    (TCPC_CHIP_FIND(usbc_id, DT_PHANDLE(usbc_id, tcpc))), ())

#define MAYBE_CONST \
	COND_CODE_1(CONFIG_PLATFORM_EC_USB_PD_TCPC_RUNTIME_CONFIG, (), (const))

/* Type C Port Controllers */
MAYBE_CONST struct tcpc_config_t tcpc_config[] = { DT_FOREACH_STATUS_OKAY(
	named_usbc_port, TCPC_CHIP) };

#ifdef CONFIG_PLATFORM_EC_TCPC_INTERRUPT

BUILD_ASSERT(ARRAY_SIZE(tcpc_config) == CONFIG_USB_PD_PORT_MAX_COUNT);

struct gpio_callback int_gpio_cb[CONFIG_USB_PD_PORT_MAX_COUNT];

static void tcpc_int_gpio_callback(const struct device *dev,
				   struct gpio_callback *cb, uint32_t pins)
{
	/*
	 * Retrieve the array index from the callback pointer, and
	 * use that to get the port number.
	 */
	int port = cb - &int_gpio_cb[0];

	schedule_deferred_pd_interrupt(port);
}

/*
 * Enable all tcpc interrupts from devicetree bindings.
 * Check whether the callback is already installed, and if
 * not, init and add the callback before enabling the
 * interrupt.
 */
void tcpc_enable_interrupt(void)
{
	gpio_flags_t flags;

	for (int i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
		/*
		 * Check whether the interrupt pin has been configured
		 * by the devicetree.
		 */
		if (!tcpc_config[i].irq_gpio.port)
			continue;
		/*
		 * Check whether the gpio pin is ready
		 */
		if (!gpio_is_ready_dt(&tcpc_config[i].irq_gpio)) {
			LOG_ERR("tcpc port #%i interrupt not ready.", i);
			return;
		}
		/*
		 * TODO(b/267537103): Once named-gpios support is dropped,
		 * evaluate if this code should call gpio_pin_configure_dt()
		 *
		 * Check whether callback has been initialised
		 */
		if (!int_gpio_cb[i].handler) {
			/*
			 * Initialise and add the callback.
			 */
			gpio_init_callback(&int_gpio_cb[i],
					   tcpc_int_gpio_callback,
					   BIT(tcpc_config[i].irq_gpio.pin));
			gpio_add_callback(tcpc_config[i].irq_gpio.port,
					  &int_gpio_cb[i]);
		}
		flags = tcpc_config[i].flags & TCPC_FLAGS_ALERT_ACTIVE_HIGH ?
				GPIO_INT_EDGE_RISING :
				GPIO_INT_EDGE_FALLING;
		flags = (flags | GPIO_INT_ENABLE) & ~GPIO_INT_DISABLE;
		gpio_pin_interrupt_configure_dt(&tcpc_config[i].irq_gpio,
						flags);
	}
}
/*
 * priority set to POST_I2C + 1 so projects can make local edits to
 * tcpc_config as needed at POST_I2C before the interrupts are enabled.
 */
DECLARE_HOOK(HOOK_INIT, tcpc_enable_interrupt, HOOK_PRIO_POST_I2C + 1);

#else /* CONFIG_PLATFORM_EC_TCPC_INTERRUPT */

/* TCPC GPIO Interrupt Handlers */
void tcpc_alert_event(enum gpio_signal signal)
{
	for (int i = 0; i < ARRAY_SIZE(tcpc_config); i++) {
		/* No alerts if the alert pin is not set in the devicetree */
		if (tcpc_config[i].alert_signal == GPIO_LIMIT) {
			continue;
		}

		if (signal == tcpc_config[i].alert_signal) {
			schedule_deferred_pd_interrupt(i);
			break;
		}
	}
}

#endif /* CONFIG_PLATFORM_EC_TCPC_INTERRUPT */
#endif /* DT_HAS_COMPAT_STATUS_OKAY */