xref: /openbmc/libmctp/astlpc.c (revision df15f7e92d46e0a67dc8bb9706b576b7807e3499)
13d36ee2eSJeremy Kerr /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2672c8852SJeremy Kerr 
3672c8852SJeremy Kerr #include <assert.h>
4672c8852SJeremy Kerr #include <fcntl.h>
5672c8852SJeremy Kerr #include <stdbool.h>
6672c8852SJeremy Kerr #include <stdlib.h>
7672c8852SJeremy Kerr #include <string.h>
8672c8852SJeremy Kerr #include <unistd.h>
9672c8852SJeremy Kerr 
10672c8852SJeremy Kerr #include <sys/ioctl.h>
11672c8852SJeremy Kerr #include <sys/mman.h>
12672c8852SJeremy Kerr 
13672c8852SJeremy Kerr #include <linux/aspeed-lpc-ctrl.h>
14672c8852SJeremy Kerr 
15672c8852SJeremy Kerr #define pr_fmt(x) "astlpc: " x
16672c8852SJeremy Kerr 
17672c8852SJeremy Kerr #include "libmctp.h"
18672c8852SJeremy Kerr #include "libmctp-alloc.h"
19672c8852SJeremy Kerr #include "libmctp-log.h"
20672c8852SJeremy Kerr #include "libmctp-astlpc.h"
21672c8852SJeremy Kerr 
22672c8852SJeremy Kerr struct mctp_binding_astlpc {
23672c8852SJeremy Kerr 	struct mctp_binding	binding;
24672c8852SJeremy Kerr 	void			*lpc_map_base;
25672c8852SJeremy Kerr 	union {
26672c8852SJeremy Kerr 		void			*lpc_map;
27672c8852SJeremy Kerr 		struct mctp_lpcmap_hdr	*lpc_hdr;
28672c8852SJeremy Kerr 	};
29672c8852SJeremy Kerr 	int			kcs_fd;
30672c8852SJeremy Kerr 	uint8_t			kcs_status;
31672c8852SJeremy Kerr 
32672c8852SJeremy Kerr 	bool			running;
33672c8852SJeremy Kerr 
34672c8852SJeremy Kerr 	/* temporary transmit buffer */
35672c8852SJeremy Kerr 	uint8_t			txbuf[256];
36672c8852SJeremy Kerr };
37672c8852SJeremy Kerr 
38672c8852SJeremy Kerr #ifndef container_of
39672c8852SJeremy Kerr #define container_of(ptr, type, member) \
40672c8852SJeremy Kerr     (type *)((char *)(ptr) - (char *)&((type *)0)->member)
41672c8852SJeremy Kerr #endif
42672c8852SJeremy Kerr 
43672c8852SJeremy Kerr #define binding_to_astlpc(b) \
44672c8852SJeremy Kerr 	container_of(b, struct mctp_binding_astlpc, binding)
45672c8852SJeremy Kerr 
46672c8852SJeremy Kerr #define MCTP_MAGIC	0x4d435450
47672c8852SJeremy Kerr #define BMC_VER_MIN	1
48672c8852SJeremy Kerr #define BMC_VER_CUR	1
49672c8852SJeremy Kerr 
50672c8852SJeremy Kerr struct mctp_lpcmap_hdr {
51672c8852SJeremy Kerr 	uint32_t	magic;
52672c8852SJeremy Kerr 
53672c8852SJeremy Kerr 	uint16_t	bmc_ver_min;
54672c8852SJeremy Kerr 	uint16_t	bmc_ver_cur;
55672c8852SJeremy Kerr 	uint16_t	host_ver_min;
56672c8852SJeremy Kerr 	uint16_t	host_ver_cur;
57672c8852SJeremy Kerr 	uint16_t	negotiated_ver;
58672c8852SJeremy Kerr 	uint16_t	pad0;
59672c8852SJeremy Kerr 
60672c8852SJeremy Kerr 	uint32_t	rx_offset;
61672c8852SJeremy Kerr 	uint32_t	rx_size;
62672c8852SJeremy Kerr 	uint32_t	tx_offset;
63672c8852SJeremy Kerr 	uint32_t	tx_size;
64672c8852SJeremy Kerr } __attribute__((packed));
65672c8852SJeremy Kerr 
66672c8852SJeremy Kerr /* layout of TX/RX areas */
67672c8852SJeremy Kerr static const uint32_t	rx_offset = 0x100;
68672c8852SJeremy Kerr static const uint32_t	rx_size   = 0x100;
69672c8852SJeremy Kerr static const uint32_t	tx_offset = 0x200;
70672c8852SJeremy Kerr static const uint32_t	tx_size   = 0x100;
71672c8852SJeremy Kerr 
72672c8852SJeremy Kerr /* kernel interface */
73672c8852SJeremy Kerr static const char *kcs_path = "/dev/mctp0";
74672c8852SJeremy Kerr static const char *lpc_path = "/dev/aspeed-lpc-ctrl";
75672c8852SJeremy Kerr 
76672c8852SJeremy Kerr #define LPC_WIN_SIZE                (1 * 1024 * 1024)
77672c8852SJeremy Kerr 
78672c8852SJeremy Kerr enum {
79672c8852SJeremy Kerr 	KCS_REG_DATA = 0,
80672c8852SJeremy Kerr 	KCS_REG_STATUS = 1,
81672c8852SJeremy Kerr };
82672c8852SJeremy Kerr 
83672c8852SJeremy Kerr #define KCS_STATUS_BMC_READY		0x80
84672c8852SJeremy Kerr #define KCS_STATUS_CHANNEL_ACTIVE	0x40
85672c8852SJeremy Kerr #define KCS_STATUS_IBF			0x02
86672c8852SJeremy Kerr #define KCS_STATUS_OBF			0x01
87672c8852SJeremy Kerr 
88672c8852SJeremy Kerr static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc,
89672c8852SJeremy Kerr 		uint8_t status)
90672c8852SJeremy Kerr {
911a4b55acSJeremy Kerr 	uint8_t buf[2];
92672c8852SJeremy Kerr 	int rc;
93672c8852SJeremy Kerr 
941a4b55acSJeremy Kerr 	/* Since we're setting the status register, we want the other endpoint
951a4b55acSJeremy Kerr 	 * to be interrupted. However, some hardware may only raise a host-side
961a4b55acSJeremy Kerr 	 * interrupt on an ODR event.
971a4b55acSJeremy Kerr 	 * So, write a dummy value of 0xff to ODR, which will ensure that an
981a4b55acSJeremy Kerr 	 * interrupt is triggered, and can be ignored by the host.
991a4b55acSJeremy Kerr 	 */
1001a4b55acSJeremy Kerr 	buf[KCS_REG_DATA] = 0xff;
1011a4b55acSJeremy Kerr 	buf[KCS_REG_STATUS] = status;
1021a4b55acSJeremy Kerr 
1031a4b55acSJeremy Kerr 	rc = pwrite(astlpc->kcs_fd, buf, 2, 0);
104672c8852SJeremy Kerr 	if (rc != 1) {
105672c8852SJeremy Kerr 		mctp_prwarn("KCS status write failed");
106672c8852SJeremy Kerr 		return -1;
107672c8852SJeremy Kerr 	}
108672c8852SJeremy Kerr 	return 0;
109672c8852SJeremy Kerr }
110672c8852SJeremy Kerr 
111672c8852SJeremy Kerr static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc,
112672c8852SJeremy Kerr 		uint8_t data)
113672c8852SJeremy Kerr {
114672c8852SJeremy Kerr 	uint8_t status;
115672c8852SJeremy Kerr 	int rc;
116672c8852SJeremy Kerr 
117672c8852SJeremy Kerr 	for (;;) {
118672c8852SJeremy Kerr 		rc = pread(astlpc->kcs_fd, &status, 1, KCS_REG_STATUS);
119672c8852SJeremy Kerr 		if (rc != 1) {
120672c8852SJeremy Kerr 			mctp_prwarn("KCE status read failed");
121672c8852SJeremy Kerr 			return -1;
122672c8852SJeremy Kerr 		}
123672c8852SJeremy Kerr 		if (!(status & KCS_STATUS_OBF))
124672c8852SJeremy Kerr 			break;
125672c8852SJeremy Kerr 		/* todo: timeout */
126672c8852SJeremy Kerr 	}
127672c8852SJeremy Kerr 
128672c8852SJeremy Kerr 	rc = pwrite(astlpc->kcs_fd, &data, 1, KCS_REG_DATA);
129672c8852SJeremy Kerr 	if (rc != 1) {
130672c8852SJeremy Kerr 		mctp_prwarn("KCS data write failed");
131672c8852SJeremy Kerr 		return -1;
132672c8852SJeremy Kerr 	}
133672c8852SJeremy Kerr 
134672c8852SJeremy Kerr 	return 0;
135672c8852SJeremy Kerr }
136672c8852SJeremy Kerr 
137672c8852SJeremy Kerr static int mctp_binding_astlpc_tx(struct mctp_binding *b,
138672c8852SJeremy Kerr 		struct mctp_pktbuf *pkt)
139672c8852SJeremy Kerr {
140672c8852SJeremy Kerr 	struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b);
141672c8852SJeremy Kerr 	uint32_t len;
142672c8852SJeremy Kerr 
143672c8852SJeremy Kerr 	len = mctp_pktbuf_size(pkt);
144672c8852SJeremy Kerr 	if (len > rx_size - 4) {
145672c8852SJeremy Kerr 		mctp_prwarn("invalid TX len 0x%x", len);
146672c8852SJeremy Kerr 		return -1;
147672c8852SJeremy Kerr 	}
148672c8852SJeremy Kerr 
149672c8852SJeremy Kerr 	*(uint32_t *)(astlpc->lpc_map + rx_offset) = htobe32(len);
150672c8852SJeremy Kerr 
151672c8852SJeremy Kerr 	memcpy(astlpc->lpc_map + rx_offset + 4, mctp_pktbuf_hdr(pkt), len);
152672c8852SJeremy Kerr 
153672c8852SJeremy Kerr 	mctp_binding_set_tx_enabled(b, false);
154672c8852SJeremy Kerr 
155672c8852SJeremy Kerr 	mctp_astlpc_kcs_send(astlpc, 0x1);
156672c8852SJeremy Kerr 	return 0;
157672c8852SJeremy Kerr }
158672c8852SJeremy Kerr 
159672c8852SJeremy Kerr static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc)
160672c8852SJeremy Kerr {
161672c8852SJeremy Kerr 	/* todo: actual version negotiation */
162672c8852SJeremy Kerr 	astlpc->lpc_hdr->negotiated_ver = htobe16(1);
163672c8852SJeremy Kerr 	mctp_astlpc_kcs_set_status(astlpc,
164672c8852SJeremy Kerr 			KCS_STATUS_BMC_READY | KCS_STATUS_CHANNEL_ACTIVE |
165672c8852SJeremy Kerr 			KCS_STATUS_OBF);
166672c8852SJeremy Kerr 
167672c8852SJeremy Kerr 	mctp_binding_set_tx_enabled(&astlpc->binding, true);
168672c8852SJeremy Kerr }
169672c8852SJeremy Kerr 
170672c8852SJeremy Kerr static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc)
171672c8852SJeremy Kerr {
172672c8852SJeremy Kerr 	struct mctp_pktbuf *pkt;
173672c8852SJeremy Kerr 	uint32_t len;
174672c8852SJeremy Kerr 
175672c8852SJeremy Kerr 	len = htobe32(*(uint32_t *)(astlpc->lpc_map + tx_offset));
176672c8852SJeremy Kerr 	if (len > tx_size - 4) {
177672c8852SJeremy Kerr 		mctp_prwarn("invalid RX len 0x%x", len);
178672c8852SJeremy Kerr 		return;
179672c8852SJeremy Kerr 	}
180672c8852SJeremy Kerr 
181*df15f7e9SJeremy Kerr 	if (len > astlpc->binding.pkt_size) {
182672c8852SJeremy Kerr 		mctp_prwarn("invalid RX len 0x%x", len);
183672c8852SJeremy Kerr 		return;
184672c8852SJeremy Kerr 	}
185672c8852SJeremy Kerr 
186*df15f7e9SJeremy Kerr 	pkt = mctp_pktbuf_alloc(&astlpc->binding, len);
187672c8852SJeremy Kerr 	if (!pkt)
188672c8852SJeremy Kerr 		goto out_complete;
189672c8852SJeremy Kerr 
190672c8852SJeremy Kerr 	memcpy(mctp_pktbuf_hdr(pkt), astlpc->lpc_map + tx_offset + 4, len);
191672c8852SJeremy Kerr 
192672c8852SJeremy Kerr 	mctp_bus_rx(&astlpc->binding, pkt);
193672c8852SJeremy Kerr 
194672c8852SJeremy Kerr out_complete:
195672c8852SJeremy Kerr 	mctp_astlpc_kcs_send(astlpc, 0x2);
196672c8852SJeremy Kerr }
197672c8852SJeremy Kerr 
198672c8852SJeremy Kerr static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc)
199672c8852SJeremy Kerr {
200672c8852SJeremy Kerr 	mctp_binding_set_tx_enabled(&astlpc->binding, true);
201672c8852SJeremy Kerr }
202672c8852SJeremy Kerr 
203672c8852SJeremy Kerr int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc)
204672c8852SJeremy Kerr {
205672c8852SJeremy Kerr 	uint8_t kcs_regs[2], data;
206672c8852SJeremy Kerr 	int rc;
207672c8852SJeremy Kerr 
208672c8852SJeremy Kerr 	rc = pread(astlpc->kcs_fd, kcs_regs, 2, 0);
209672c8852SJeremy Kerr 	if (rc < 0) {
210672c8852SJeremy Kerr 		mctp_prwarn("KCS read error");
211672c8852SJeremy Kerr 		return -1;
212672c8852SJeremy Kerr 	} else if (rc != 2) {
213672c8852SJeremy Kerr 		mctp_prwarn("KCS short read (%d)", rc);
214672c8852SJeremy Kerr 		return -1;
215672c8852SJeremy Kerr 	}
216672c8852SJeremy Kerr 
217672c8852SJeremy Kerr 	if (!(kcs_regs[KCS_REG_STATUS] & KCS_STATUS_IBF))
218672c8852SJeremy Kerr 		return 0;
219672c8852SJeremy Kerr 
220672c8852SJeremy Kerr 	data = kcs_regs[KCS_REG_DATA];
221672c8852SJeremy Kerr 	switch (data) {
222672c8852SJeremy Kerr 	case 0x0:
223672c8852SJeremy Kerr 		mctp_astlpc_init_channel(astlpc);
224672c8852SJeremy Kerr 		break;
225672c8852SJeremy Kerr 	case 0x1:
226672c8852SJeremy Kerr 		mctp_astlpc_rx_start(astlpc);
227672c8852SJeremy Kerr 		break;
228672c8852SJeremy Kerr 	case 0x2:
229672c8852SJeremy Kerr 		mctp_astlpc_tx_complete(astlpc);
230672c8852SJeremy Kerr 		break;
2311a4b55acSJeremy Kerr 	case 0xff:
2321a4b55acSJeremy Kerr 		/* reserved value for dummy data writes; do nothing */
2331a4b55acSJeremy Kerr 		break;
234672c8852SJeremy Kerr 	default:
235672c8852SJeremy Kerr 		mctp_prwarn("unknown message 0x%x", data);
236672c8852SJeremy Kerr 	}
237672c8852SJeremy Kerr 	return 0;
238672c8852SJeremy Kerr }
239672c8852SJeremy Kerr 
240672c8852SJeremy Kerr int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc)
241672c8852SJeremy Kerr {
242672c8852SJeremy Kerr 	return astlpc->kcs_fd;
243672c8852SJeremy Kerr }
244672c8852SJeremy Kerr 
245672c8852SJeremy Kerr 
246672c8852SJeremy Kerr void mctp_astlpc_register_bus(struct mctp_binding_astlpc *astlpc,
247672c8852SJeremy Kerr 		struct mctp *mctp, mctp_eid_t eid)
248672c8852SJeremy Kerr {
249672c8852SJeremy Kerr 	mctp_register_bus(mctp, &astlpc->binding, eid);
250672c8852SJeremy Kerr }
251672c8852SJeremy Kerr 
252672c8852SJeremy Kerr static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc)
253672c8852SJeremy Kerr {
254672c8852SJeremy Kerr 	uint8_t status;
255672c8852SJeremy Kerr 	int rc;
256672c8852SJeremy Kerr 
257672c8852SJeremy Kerr 	astlpc->lpc_hdr->magic = htobe32(MCTP_MAGIC);
258672c8852SJeremy Kerr 	astlpc->lpc_hdr->bmc_ver_min = htobe16(BMC_VER_MIN);
259672c8852SJeremy Kerr 	astlpc->lpc_hdr->bmc_ver_cur = htobe16(BMC_VER_CUR);
260672c8852SJeremy Kerr 
261672c8852SJeremy Kerr 	astlpc->lpc_hdr->rx_offset = htobe32(rx_offset);
262672c8852SJeremy Kerr 	astlpc->lpc_hdr->rx_size = htobe32(rx_size);
263672c8852SJeremy Kerr 	astlpc->lpc_hdr->tx_offset = htobe32(tx_offset);
264672c8852SJeremy Kerr 	astlpc->lpc_hdr->tx_size = htobe32(tx_size);
265672c8852SJeremy Kerr 
266672c8852SJeremy Kerr 	/* set status indicating that the BMC is now active */
267672c8852SJeremy Kerr 	status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF;
268672c8852SJeremy Kerr 	rc = pwrite(astlpc->kcs_fd, &status, 1, KCS_REG_STATUS);
269672c8852SJeremy Kerr 	if (rc != 1) {
270672c8852SJeremy Kerr 		mctp_prwarn("KCS write failed");
271672c8852SJeremy Kerr 		rc = -1;
272672c8852SJeremy Kerr 	} else {
273672c8852SJeremy Kerr 		rc = 0;
274672c8852SJeremy Kerr 	}
275672c8852SJeremy Kerr 
276672c8852SJeremy Kerr 	return rc;
277672c8852SJeremy Kerr }
278672c8852SJeremy Kerr 
279672c8852SJeremy Kerr static int mctp_astlpc_init_lpc(struct mctp_binding_astlpc *astlpc)
280672c8852SJeremy Kerr {
281672c8852SJeremy Kerr 	struct aspeed_lpc_ctrl_mapping map = {
282672c8852SJeremy Kerr 		.window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY,
283672c8852SJeremy Kerr 		.window_id = 0, /* There's only one */
284672c8852SJeremy Kerr 		.flags = 0,
285672c8852SJeremy Kerr 		.addr = 0,
286672c8852SJeremy Kerr 		.offset = 0,
287672c8852SJeremy Kerr 		.size = 0
288672c8852SJeremy Kerr 	};
289672c8852SJeremy Kerr 	int fd, rc;
290672c8852SJeremy Kerr 
291672c8852SJeremy Kerr 	fd = open(lpc_path, O_RDWR | O_SYNC);
292672c8852SJeremy Kerr 	if (fd < 0) {
293672c8852SJeremy Kerr 		mctp_prwarn("LPC open (%s) failed", lpc_path);
294672c8852SJeremy Kerr 		return -1;
295672c8852SJeremy Kerr 	}
296672c8852SJeremy Kerr 
297672c8852SJeremy Kerr 	rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map);
298672c8852SJeremy Kerr 	if (rc) {
299672c8852SJeremy Kerr 		mctp_prwarn("LPC GET_SIZE failed");
300672c8852SJeremy Kerr 		close(fd);
301672c8852SJeremy Kerr 		return -1;
302672c8852SJeremy Kerr 	}
303672c8852SJeremy Kerr 
304672c8852SJeremy Kerr 	astlpc->lpc_map_base = mmap(NULL, map.size, PROT_READ | PROT_WRITE,
305672c8852SJeremy Kerr 			MAP_SHARED, fd, 0);
306672c8852SJeremy Kerr 	if (astlpc->lpc_map_base == MAP_FAILED) {
307672c8852SJeremy Kerr 		mctp_prwarn("LPC mmap failed");
308672c8852SJeremy Kerr 		rc = -1;
309672c8852SJeremy Kerr 	} else {
310672c8852SJeremy Kerr 		astlpc->lpc_map = astlpc->lpc_map_base +
311672c8852SJeremy Kerr 			map.size - LPC_WIN_SIZE;
312672c8852SJeremy Kerr 	}
313672c8852SJeremy Kerr 
314672c8852SJeremy Kerr 	close(fd);
315672c8852SJeremy Kerr 
316672c8852SJeremy Kerr 	return rc;
317672c8852SJeremy Kerr }
318672c8852SJeremy Kerr 
319672c8852SJeremy Kerr static int mctp_astlpc_init_kcs(struct mctp_binding_astlpc *astlpc)
320672c8852SJeremy Kerr {
321672c8852SJeremy Kerr 	astlpc->kcs_fd = open(kcs_path, O_RDWR);
322672c8852SJeremy Kerr 	if (astlpc->kcs_fd < 0)
323672c8852SJeremy Kerr 		return -1;
324672c8852SJeremy Kerr 
325672c8852SJeremy Kerr 	return 0;
326672c8852SJeremy Kerr }
327672c8852SJeremy Kerr 
328672c8852SJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init(void)
329672c8852SJeremy Kerr {
330672c8852SJeremy Kerr 	struct mctp_binding_astlpc *astlpc;
331672c8852SJeremy Kerr 	int rc;
332672c8852SJeremy Kerr 
333672c8852SJeremy Kerr 	astlpc = __mctp_alloc(sizeof(*astlpc));
334672c8852SJeremy Kerr 	memset(astlpc, 0, sizeof(*astlpc));
335672c8852SJeremy Kerr 	astlpc->binding.name = "astlpc";
336672c8852SJeremy Kerr 	astlpc->binding.version = 1;
337672c8852SJeremy Kerr 	astlpc->binding.tx = mctp_binding_astlpc_tx;
338*df15f7e9SJeremy Kerr 	astlpc->binding.pkt_size = MCTP_BMTU;
339*df15f7e9SJeremy Kerr 	astlpc->binding.pkt_pad = 0;
340672c8852SJeremy Kerr 
341672c8852SJeremy Kerr 	rc = mctp_astlpc_init_lpc(astlpc);
342672c8852SJeremy Kerr 	if (rc) {
343672c8852SJeremy Kerr 		free(astlpc);
344672c8852SJeremy Kerr 		return NULL;
345672c8852SJeremy Kerr 	}
346672c8852SJeremy Kerr 
347672c8852SJeremy Kerr 	rc = mctp_astlpc_init_kcs(astlpc);
348672c8852SJeremy Kerr 	if (rc) {
349672c8852SJeremy Kerr 		free(astlpc);
350672c8852SJeremy Kerr 		return NULL;
351672c8852SJeremy Kerr 	}
352672c8852SJeremy Kerr 
353672c8852SJeremy Kerr 	rc = mctp_astlpc_init_bmc(astlpc);
354672c8852SJeremy Kerr 	if (rc) {
355672c8852SJeremy Kerr 		free(astlpc);
356672c8852SJeremy Kerr 		return NULL;
357672c8852SJeremy Kerr 	}
358672c8852SJeremy Kerr 
359672c8852SJeremy Kerr 	return astlpc;
360672c8852SJeremy Kerr }
361672c8852SJeremy Kerr 
362