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