1 /*
2  * Copyright (c) 2020 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/device.h>
8 #include <soc.h>
9 #include <zephyr/drivers/espi.h>
10 #include <zephyr/logging/log_ctrl.h>
11 #include <zephyr/logging/log.h>
12 #include "espi_oob_handler.h"
13 
14 LOG_MODULE_DECLARE(espi, CONFIG_ESPI_LOG_LEVEL);
15 
16 struct oob_header {
17 	uint8_t dest_slave_addr;
18 	uint8_t oob_cmd_code;
19 	uint8_t byte_cnt;
20 	uint8_t src_slave_addr;
21 };
22 
23 #ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
24 
25 #define OOB_THREAD_STACK_SIZE  512ul
26 #define OOB_THREAD_PRIORITY    K_PRIO_COOP(5)
27 #define OOB_THREAD_WAIT        -1
28 
29 /* Thread to process asynchronous callbacks */
30 void espihub_thread(void *p1, void *p2, void *p3);
31 
32 void temperature_timer(struct k_timer *timer_id);
33 
34 K_TIMER_DEFINE(temp_timer, temperature_timer, NULL);
35 K_THREAD_DEFINE(espihub_thrd_id, OOB_THREAD_STACK_SIZE, espihub_thread,
36 		NULL, NULL, NULL,
37 		OOB_THREAD_PRIORITY, K_INHERIT_PERMS, OOB_THREAD_WAIT);
38 
39 K_MSGQ_DEFINE(from_host, sizeof(uint8_t), 8, 4);
40 
41 struct thread_context {
42 	const struct device  *espi_dev;
43 	int                  cycles;
44 };
45 
46 static struct thread_context context;
47 static bool need_temp;
48 #endif
49 
50 static struct espi_oob_packet resp_pckt;
51 static uint8_t buf[MAX_ESPI_BUF_LEN];
52 
request_temp(const struct device * dev)53 static int request_temp(const struct device *dev)
54 {
55 	int ret;
56 	struct oob_header oob_hdr;
57 	struct espi_oob_packet req_pckt;
58 
59 	LOG_WRN("%s", __func__);
60 
61 	oob_hdr.dest_slave_addr = PCH_DEST_SLV_ADDR;
62 	oob_hdr.oob_cmd_code = OOB_CMDCODE;
63 	oob_hdr.byte_cnt = 1;
64 	oob_hdr.src_slave_addr = SRC_SLV_ADDR;
65 
66 	/* Packetize OOB request */
67 	req_pckt.buf = (uint8_t *)&oob_hdr;
68 	req_pckt.len = sizeof(struct oob_header);
69 
70 	ret = espi_send_oob(dev, &req_pckt);
71 	if (ret) {
72 		LOG_ERR("OOB Tx failed %d", ret);
73 		return ret;
74 	}
75 
76 	return 0;
77 }
78 
retrieve_packet(const struct device * dev,uint8_t * sender)79 static int retrieve_packet(const struct device *dev, uint8_t *sender)
80 {
81 	int ret;
82 
83 #ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
84 	/* Note that no data is in the item */
85 	uint8_t response_len;
86 
87 	if (k_msgq_num_used_get(&from_host) == 0U) {
88 		return -EINVAL;
89 	}
90 
91 	k_msgq_get(&from_host, &response_len, K_FOREVER);
92 #endif
93 
94 	resp_pckt.buf = (uint8_t *)&buf;
95 	resp_pckt.len = MAX_ESPI_BUF_LEN;
96 
97 	ret = espi_receive_oob(dev, &resp_pckt);
98 	if (ret) {
99 		LOG_ERR("OOB Rx failed %d", ret);
100 		return ret;
101 	}
102 
103 	LOG_INF("OOB transaction completed rcvd: %d bytes", resp_pckt.len);
104 	for (int i = 0; i < resp_pckt.len; i++) {
105 		LOG_INF("%x ", buf[i]);
106 	}
107 
108 	if (sender) {
109 		*sender = buf[OOB_RESPONSE_SENDER_INDEX];
110 	}
111 
112 	return 0;
113 }
114 
get_pch_temp_sync(const struct device * dev)115 int get_pch_temp_sync(const struct device *dev)
116 {
117 	int ret;
118 
119 	for (int i = 0; i < MIN_GET_TEMP_CYCLES; i++) {
120 		ret = request_temp(dev);
121 		if (ret) {
122 			LOG_ERR("OOB req failed %d", ret);
123 			return ret;
124 		}
125 
126 		ret = retrieve_packet(dev, NULL);
127 		if (ret) {
128 			LOG_ERR("OOB retrieve failed %d", ret);
129 			return ret;
130 		}
131 	}
132 
133 	return 0;
134 }
135 
get_pch_temp_async(const struct device * dev)136 int get_pch_temp_async(const struct device *dev)
137 {
138 #if !defined(CONFIG_ESPI_OOB_CHANNEL) || \
139 	!defined(CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC)
140 	return -ENOTSUP;
141 #else
142 	context.espi_dev = dev;
143 	context.cycles = MIN_GET_TEMP_CYCLES;
144 
145 	k_thread_start(espihub_thrd_id);
146 	k_thread_join(espihub_thrd_id, K_FOREVER);
147 
148 	return 0;
149 #endif
150 }
151 
152 #ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC
153 
oob_rx_handler(const struct device * dev,struct espi_callback * cb,struct espi_event event)154 void oob_rx_handler(const struct device *dev, struct espi_callback *cb,
155 		    struct espi_event event)
156 {
157 	uint8_t last_resp_len = event.evt_details;
158 
159 	LOG_WRN("%s", __func__);
160 	/* Post for post-processing in a thread
161 	 * Should not attempt to retrieve in callback context
162 	 */
163 	k_msgq_put(&from_host, &last_resp_len, K_NO_WAIT);
164 }
165 
166 
temperature_timer(struct k_timer * timer_id)167 void temperature_timer(struct k_timer *timer_id)
168 {
169 	LOG_WRN("%s", __func__);
170 	need_temp = true;
171 }
172 
espihub_thread(void * p1,void * p2,void * p3)173 void espihub_thread(void *p1, void *p2, void *p3)
174 {
175 	int ret;
176 	uint8_t temp;
177 	uint8_t sender;
178 
179 	LOG_DBG("%s", __func__);
180 	k_timer_start(&temp_timer, K_MSEC(100), K_MSEC(100));
181 	while (context.cycles > 0) {
182 		k_msleep(50);
183 
184 		ret = retrieve_packet(context.espi_dev, &sender);
185 		if (!ret) {
186 			switch (sender) {
187 			case PCH_DEST_SLV_ADDR:
188 				LOG_INF("PCH response");
189 				/* Any other checks */
190 				if (resp_pckt.len == OOB_RESPONSE_LEN) {
191 					temp = buf[OOB_RESPONSE_DATA_INDEX];
192 					LOG_INF("Temp %d", temp);
193 				} else {
194 					LOG_ERR("Incorrect size response");
195 				}
196 
197 				break;
198 			default:
199 				LOG_INF("Other host sender %x", sender);
200 			}
201 		} else {
202 			LOG_ERR("Failure to retrieve temp %d", ret);
203 		}
204 
205 		/* Decrease cycles in both cases failure/success */
206 		context.cycles--;
207 
208 		if (need_temp) {
209 			request_temp(context.espi_dev);
210 			need_temp = false;
211 		}
212 	}
213 
214 	k_timer_stop(&temp_timer);
215 }
216 #endif /* CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC */
217