xref: /openbmc/libmctp/astlpc.c (revision 672c88524b5bea5481b4cf4d067a80f20f85862d)
1*672c8852SJeremy Kerr /* SPDX-License-Identifier: Apache-2.0 */
2*672c8852SJeremy Kerr 
3*672c8852SJeremy Kerr #include <assert.h>
4*672c8852SJeremy Kerr #include <fcntl.h>
5*672c8852SJeremy Kerr #include <stdbool.h>
6*672c8852SJeremy Kerr #include <stdlib.h>
7*672c8852SJeremy Kerr #include <string.h>
8*672c8852SJeremy Kerr #include <unistd.h>
9*672c8852SJeremy Kerr 
10*672c8852SJeremy Kerr #include <sys/ioctl.h>
11*672c8852SJeremy Kerr #include <sys/mman.h>
12*672c8852SJeremy Kerr 
13*672c8852SJeremy Kerr #include <linux/aspeed-lpc-ctrl.h>
14*672c8852SJeremy Kerr 
15*672c8852SJeremy Kerr #define pr_fmt(x) "astlpc: " x
16*672c8852SJeremy Kerr 
17*672c8852SJeremy Kerr #include "libmctp.h"
18*672c8852SJeremy Kerr #include "libmctp-alloc.h"
19*672c8852SJeremy Kerr #include "libmctp-log.h"
20*672c8852SJeremy Kerr #include "libmctp-astlpc.h"
21*672c8852SJeremy Kerr 
22*672c8852SJeremy Kerr struct mctp_binding_astlpc {
23*672c8852SJeremy Kerr 	struct mctp_binding	binding;
24*672c8852SJeremy Kerr 	void			*lpc_map_base;
25*672c8852SJeremy Kerr 	union {
26*672c8852SJeremy Kerr 		void			*lpc_map;
27*672c8852SJeremy Kerr 		struct mctp_lpcmap_hdr	*lpc_hdr;
28*672c8852SJeremy Kerr 	};
29*672c8852SJeremy Kerr 	int			kcs_fd;
30*672c8852SJeremy Kerr 	uint8_t			kcs_status;
31*672c8852SJeremy Kerr 
32*672c8852SJeremy Kerr 	bool			running;
33*672c8852SJeremy Kerr 
34*672c8852SJeremy Kerr 	/* temporary transmit buffer */
35*672c8852SJeremy Kerr 	uint8_t			txbuf[256];
36*672c8852SJeremy Kerr };
37*672c8852SJeremy Kerr 
38*672c8852SJeremy Kerr #ifndef container_of
39*672c8852SJeremy Kerr #define container_of(ptr, type, member) \
40*672c8852SJeremy Kerr     (type *)((char *)(ptr) - (char *)&((type *)0)->member)
41*672c8852SJeremy Kerr #endif
42*672c8852SJeremy Kerr 
43*672c8852SJeremy Kerr #define binding_to_astlpc(b) \
44*672c8852SJeremy Kerr 	container_of(b, struct mctp_binding_astlpc, binding)
45*672c8852SJeremy Kerr 
46*672c8852SJeremy Kerr #define MCTP_MAGIC	0x4d435450
47*672c8852SJeremy Kerr #define BMC_VER_MIN	1
48*672c8852SJeremy Kerr #define BMC_VER_CUR	1
49*672c8852SJeremy Kerr 
50*672c8852SJeremy Kerr struct mctp_lpcmap_hdr {
51*672c8852SJeremy Kerr 	uint32_t	magic;
52*672c8852SJeremy Kerr 
53*672c8852SJeremy Kerr 	uint16_t	bmc_ver_min;
54*672c8852SJeremy Kerr 	uint16_t	bmc_ver_cur;
55*672c8852SJeremy Kerr 	uint16_t	host_ver_min;
56*672c8852SJeremy Kerr 	uint16_t	host_ver_cur;
57*672c8852SJeremy Kerr 	uint16_t	negotiated_ver;
58*672c8852SJeremy Kerr 	uint16_t	pad0;
59*672c8852SJeremy Kerr 
60*672c8852SJeremy Kerr 	uint32_t	rx_offset;
61*672c8852SJeremy Kerr 	uint32_t	rx_size;
62*672c8852SJeremy Kerr 	uint32_t	tx_offset;
63*672c8852SJeremy Kerr 	uint32_t	tx_size;
64*672c8852SJeremy Kerr } __attribute__((packed));
65*672c8852SJeremy Kerr 
66*672c8852SJeremy Kerr /* layout of TX/RX areas */
67*672c8852SJeremy Kerr static const uint32_t	rx_offset = 0x100;
68*672c8852SJeremy Kerr static const uint32_t	rx_size   = 0x100;
69*672c8852SJeremy Kerr static const uint32_t	tx_offset = 0x200;
70*672c8852SJeremy Kerr static const uint32_t	tx_size   = 0x100;
71*672c8852SJeremy Kerr 
72*672c8852SJeremy Kerr /* kernel interface */
73*672c8852SJeremy Kerr static const char *kcs_path = "/dev/mctp0";
74*672c8852SJeremy Kerr static const char *lpc_path = "/dev/aspeed-lpc-ctrl";
75*672c8852SJeremy Kerr 
76*672c8852SJeremy Kerr #define LPC_WIN_SIZE                (1 * 1024 * 1024)
77*672c8852SJeremy Kerr 
78*672c8852SJeremy Kerr enum {
79*672c8852SJeremy Kerr 	KCS_REG_DATA = 0,
80*672c8852SJeremy Kerr 	KCS_REG_STATUS = 1,
81*672c8852SJeremy Kerr };
82*672c8852SJeremy Kerr 
83*672c8852SJeremy Kerr #define KCS_STATUS_BMC_READY		0x80
84*672c8852SJeremy Kerr #define KCS_STATUS_CHANNEL_ACTIVE	0x40
85*672c8852SJeremy Kerr #define KCS_STATUS_IBF			0x02
86*672c8852SJeremy Kerr #define KCS_STATUS_OBF			0x01
87*672c8852SJeremy Kerr 
88*672c8852SJeremy Kerr static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc,
89*672c8852SJeremy Kerr 		uint8_t status)
90*672c8852SJeremy Kerr {
91*672c8852SJeremy Kerr 	int rc;
92*672c8852SJeremy Kerr 
93*672c8852SJeremy Kerr 	rc = pwrite(astlpc->kcs_fd, &status, 1, KCS_REG_STATUS);
94*672c8852SJeremy Kerr 	if (rc != 1) {
95*672c8852SJeremy Kerr 		mctp_prwarn("KCS status write failed");
96*672c8852SJeremy Kerr 		return -1;
97*672c8852SJeremy Kerr 	}
98*672c8852SJeremy Kerr 	return 0;
99*672c8852SJeremy Kerr }
100*672c8852SJeremy Kerr 
101*672c8852SJeremy Kerr static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc,
102*672c8852SJeremy Kerr 		uint8_t data)
103*672c8852SJeremy Kerr {
104*672c8852SJeremy Kerr 	uint8_t status;
105*672c8852SJeremy Kerr 	int rc;
106*672c8852SJeremy Kerr 
107*672c8852SJeremy Kerr 	for (;;) {
108*672c8852SJeremy Kerr 		rc = pread(astlpc->kcs_fd, &status, 1, KCS_REG_STATUS);
109*672c8852SJeremy Kerr 		if (rc != 1) {
110*672c8852SJeremy Kerr 			mctp_prwarn("KCE status read failed");
111*672c8852SJeremy Kerr 			return -1;
112*672c8852SJeremy Kerr 		}
113*672c8852SJeremy Kerr 		if (!(status & KCS_STATUS_OBF))
114*672c8852SJeremy Kerr 			break;
115*672c8852SJeremy Kerr 		/* todo: timeout */
116*672c8852SJeremy Kerr 	}
117*672c8852SJeremy Kerr 
118*672c8852SJeremy Kerr 	rc = pwrite(astlpc->kcs_fd, &data, 1, KCS_REG_DATA);
119*672c8852SJeremy Kerr 	if (rc != 1) {
120*672c8852SJeremy Kerr 		mctp_prwarn("KCS data write failed");
121*672c8852SJeremy Kerr 		return -1;
122*672c8852SJeremy Kerr 	}
123*672c8852SJeremy Kerr 
124*672c8852SJeremy Kerr 	return 0;
125*672c8852SJeremy Kerr }
126*672c8852SJeremy Kerr 
127*672c8852SJeremy Kerr static int mctp_binding_astlpc_tx(struct mctp_binding *b,
128*672c8852SJeremy Kerr 		struct mctp_pktbuf *pkt)
129*672c8852SJeremy Kerr {
130*672c8852SJeremy Kerr 	struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b);
131*672c8852SJeremy Kerr 	uint32_t len;
132*672c8852SJeremy Kerr 
133*672c8852SJeremy Kerr 	len = mctp_pktbuf_size(pkt);
134*672c8852SJeremy Kerr 	if (len > rx_size - 4) {
135*672c8852SJeremy Kerr 		mctp_prwarn("invalid TX len 0x%x", len);
136*672c8852SJeremy Kerr 		return -1;
137*672c8852SJeremy Kerr 	}
138*672c8852SJeremy Kerr 
139*672c8852SJeremy Kerr 	*(uint32_t *)(astlpc->lpc_map + rx_offset) = htobe32(len);
140*672c8852SJeremy Kerr 
141*672c8852SJeremy Kerr 	memcpy(astlpc->lpc_map + rx_offset + 4, mctp_pktbuf_hdr(pkt), len);
142*672c8852SJeremy Kerr 
143*672c8852SJeremy Kerr 	mctp_binding_set_tx_enabled(b, false);
144*672c8852SJeremy Kerr 
145*672c8852SJeremy Kerr 	mctp_astlpc_kcs_send(astlpc, 0x1);
146*672c8852SJeremy Kerr 	return 0;
147*672c8852SJeremy Kerr }
148*672c8852SJeremy Kerr 
149*672c8852SJeremy Kerr static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc)
150*672c8852SJeremy Kerr {
151*672c8852SJeremy Kerr 	/* todo: actual version negotiation */
152*672c8852SJeremy Kerr 	astlpc->lpc_hdr->negotiated_ver = htobe16(1);
153*672c8852SJeremy Kerr 	mctp_astlpc_kcs_set_status(astlpc,
154*672c8852SJeremy Kerr 			KCS_STATUS_BMC_READY | KCS_STATUS_CHANNEL_ACTIVE |
155*672c8852SJeremy Kerr 			KCS_STATUS_OBF);
156*672c8852SJeremy Kerr 
157*672c8852SJeremy Kerr 	mctp_binding_set_tx_enabled(&astlpc->binding, true);
158*672c8852SJeremy Kerr }
159*672c8852SJeremy Kerr 
160*672c8852SJeremy Kerr static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc)
161*672c8852SJeremy Kerr {
162*672c8852SJeremy Kerr 	struct mctp_pktbuf *pkt;
163*672c8852SJeremy Kerr 	uint32_t len;
164*672c8852SJeremy Kerr 
165*672c8852SJeremy Kerr 	len = htobe32(*(uint32_t *)(astlpc->lpc_map + tx_offset));
166*672c8852SJeremy Kerr 	if (len > tx_size - 4) {
167*672c8852SJeremy Kerr 		mctp_prwarn("invalid RX len 0x%x", len);
168*672c8852SJeremy Kerr 		return;
169*672c8852SJeremy Kerr 	}
170*672c8852SJeremy Kerr 
171*672c8852SJeremy Kerr 	if (len > MCTP_MTU + sizeof(struct mctp_hdr)) {
172*672c8852SJeremy Kerr 		mctp_prwarn("invalid RX len 0x%x", len);
173*672c8852SJeremy Kerr 		return;
174*672c8852SJeremy Kerr 	}
175*672c8852SJeremy Kerr 
176*672c8852SJeremy Kerr 	pkt = mctp_pktbuf_alloc(len);
177*672c8852SJeremy Kerr 	if (!pkt)
178*672c8852SJeremy Kerr 		goto out_complete;
179*672c8852SJeremy Kerr 
180*672c8852SJeremy Kerr 	memcpy(mctp_pktbuf_hdr(pkt), astlpc->lpc_map + tx_offset + 4, len);
181*672c8852SJeremy Kerr 
182*672c8852SJeremy Kerr 	mctp_bus_rx(&astlpc->binding, pkt);
183*672c8852SJeremy Kerr 
184*672c8852SJeremy Kerr out_complete:
185*672c8852SJeremy Kerr 	mctp_astlpc_kcs_send(astlpc, 0x2);
186*672c8852SJeremy Kerr }
187*672c8852SJeremy Kerr 
188*672c8852SJeremy Kerr static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc)
189*672c8852SJeremy Kerr {
190*672c8852SJeremy Kerr 	mctp_binding_set_tx_enabled(&astlpc->binding, true);
191*672c8852SJeremy Kerr }
192*672c8852SJeremy Kerr 
193*672c8852SJeremy Kerr int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc)
194*672c8852SJeremy Kerr {
195*672c8852SJeremy Kerr 	uint8_t kcs_regs[2], data;
196*672c8852SJeremy Kerr 	int rc;
197*672c8852SJeremy Kerr 
198*672c8852SJeremy Kerr 	rc = pread(astlpc->kcs_fd, kcs_regs, 2, 0);
199*672c8852SJeremy Kerr 	if (rc < 0) {
200*672c8852SJeremy Kerr 		mctp_prwarn("KCS read error");
201*672c8852SJeremy Kerr 		return -1;
202*672c8852SJeremy Kerr 	} else if (rc != 2) {
203*672c8852SJeremy Kerr 		mctp_prwarn("KCS short read (%d)", rc);
204*672c8852SJeremy Kerr 		return -1;
205*672c8852SJeremy Kerr 	}
206*672c8852SJeremy Kerr 
207*672c8852SJeremy Kerr 	if (!(kcs_regs[KCS_REG_STATUS] & KCS_STATUS_IBF))
208*672c8852SJeremy Kerr 		return 0;
209*672c8852SJeremy Kerr 
210*672c8852SJeremy Kerr 	data = kcs_regs[KCS_REG_DATA];
211*672c8852SJeremy Kerr 	switch (data) {
212*672c8852SJeremy Kerr 	case 0x0:
213*672c8852SJeremy Kerr 		mctp_astlpc_init_channel(astlpc);
214*672c8852SJeremy Kerr 		break;
215*672c8852SJeremy Kerr 	case 0x1:
216*672c8852SJeremy Kerr 		mctp_astlpc_rx_start(astlpc);
217*672c8852SJeremy Kerr 		break;
218*672c8852SJeremy Kerr 	case 0x2:
219*672c8852SJeremy Kerr 		mctp_astlpc_tx_complete(astlpc);
220*672c8852SJeremy Kerr 		break;
221*672c8852SJeremy Kerr 	default:
222*672c8852SJeremy Kerr 		mctp_prwarn("unknown message 0x%x", data);
223*672c8852SJeremy Kerr 	}
224*672c8852SJeremy Kerr 	return 0;
225*672c8852SJeremy Kerr }
226*672c8852SJeremy Kerr 
227*672c8852SJeremy Kerr int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc)
228*672c8852SJeremy Kerr {
229*672c8852SJeremy Kerr 	return astlpc->kcs_fd;
230*672c8852SJeremy Kerr }
231*672c8852SJeremy Kerr 
232*672c8852SJeremy Kerr 
233*672c8852SJeremy Kerr void mctp_astlpc_register_bus(struct mctp_binding_astlpc *astlpc,
234*672c8852SJeremy Kerr 		struct mctp *mctp, mctp_eid_t eid)
235*672c8852SJeremy Kerr {
236*672c8852SJeremy Kerr 	mctp_register_bus(mctp, &astlpc->binding, eid);
237*672c8852SJeremy Kerr }
238*672c8852SJeremy Kerr 
239*672c8852SJeremy Kerr static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc)
240*672c8852SJeremy Kerr {
241*672c8852SJeremy Kerr 	uint8_t status;
242*672c8852SJeremy Kerr 	int rc;
243*672c8852SJeremy Kerr 
244*672c8852SJeremy Kerr 	astlpc->lpc_hdr->magic = htobe32(MCTP_MAGIC);
245*672c8852SJeremy Kerr 	astlpc->lpc_hdr->bmc_ver_min = htobe16(BMC_VER_MIN);
246*672c8852SJeremy Kerr 	astlpc->lpc_hdr->bmc_ver_cur = htobe16(BMC_VER_CUR);
247*672c8852SJeremy Kerr 
248*672c8852SJeremy Kerr 	astlpc->lpc_hdr->rx_offset = htobe32(rx_offset);
249*672c8852SJeremy Kerr 	astlpc->lpc_hdr->rx_size = htobe32(rx_size);
250*672c8852SJeremy Kerr 	astlpc->lpc_hdr->tx_offset = htobe32(tx_offset);
251*672c8852SJeremy Kerr 	astlpc->lpc_hdr->tx_size = htobe32(tx_size);
252*672c8852SJeremy Kerr 
253*672c8852SJeremy Kerr 	/* set status indicating that the BMC is now active */
254*672c8852SJeremy Kerr 	status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF;
255*672c8852SJeremy Kerr 	rc = pwrite(astlpc->kcs_fd, &status, 1, KCS_REG_STATUS);
256*672c8852SJeremy Kerr 	if (rc != 1) {
257*672c8852SJeremy Kerr 		mctp_prwarn("KCS write failed");
258*672c8852SJeremy Kerr 		rc = -1;
259*672c8852SJeremy Kerr 	} else {
260*672c8852SJeremy Kerr 		rc = 0;
261*672c8852SJeremy Kerr 	}
262*672c8852SJeremy Kerr 
263*672c8852SJeremy Kerr 	return rc;
264*672c8852SJeremy Kerr }
265*672c8852SJeremy Kerr 
266*672c8852SJeremy Kerr static int mctp_astlpc_init_lpc(struct mctp_binding_astlpc *astlpc)
267*672c8852SJeremy Kerr {
268*672c8852SJeremy Kerr 	struct aspeed_lpc_ctrl_mapping map = {
269*672c8852SJeremy Kerr 		.window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY,
270*672c8852SJeremy Kerr 		.window_id = 0, /* There's only one */
271*672c8852SJeremy Kerr 		.flags = 0,
272*672c8852SJeremy Kerr 		.addr = 0,
273*672c8852SJeremy Kerr 		.offset = 0,
274*672c8852SJeremy Kerr 		.size = 0
275*672c8852SJeremy Kerr 	};
276*672c8852SJeremy Kerr 	int fd, rc;
277*672c8852SJeremy Kerr 
278*672c8852SJeremy Kerr 	fd = open(lpc_path, O_RDWR | O_SYNC);
279*672c8852SJeremy Kerr 	if (fd < 0) {
280*672c8852SJeremy Kerr 		mctp_prwarn("LPC open (%s) failed", lpc_path);
281*672c8852SJeremy Kerr 		return -1;
282*672c8852SJeremy Kerr 	}
283*672c8852SJeremy Kerr 
284*672c8852SJeremy Kerr 	rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map);
285*672c8852SJeremy Kerr 	if (rc) {
286*672c8852SJeremy Kerr 		mctp_prwarn("LPC GET_SIZE failed");
287*672c8852SJeremy Kerr 		close(fd);
288*672c8852SJeremy Kerr 		return -1;
289*672c8852SJeremy Kerr 	}
290*672c8852SJeremy Kerr 
291*672c8852SJeremy Kerr 	astlpc->lpc_map_base = mmap(NULL, map.size, PROT_READ | PROT_WRITE,
292*672c8852SJeremy Kerr 			MAP_SHARED, fd, 0);
293*672c8852SJeremy Kerr 	if (astlpc->lpc_map_base == MAP_FAILED) {
294*672c8852SJeremy Kerr 		mctp_prwarn("LPC mmap failed");
295*672c8852SJeremy Kerr 		rc = -1;
296*672c8852SJeremy Kerr 	} else {
297*672c8852SJeremy Kerr 		astlpc->lpc_map = astlpc->lpc_map_base +
298*672c8852SJeremy Kerr 			map.size - LPC_WIN_SIZE;
299*672c8852SJeremy Kerr 	}
300*672c8852SJeremy Kerr 
301*672c8852SJeremy Kerr 	close(fd);
302*672c8852SJeremy Kerr 
303*672c8852SJeremy Kerr 	return rc;
304*672c8852SJeremy Kerr }
305*672c8852SJeremy Kerr 
306*672c8852SJeremy Kerr static int mctp_astlpc_init_kcs(struct mctp_binding_astlpc *astlpc)
307*672c8852SJeremy Kerr {
308*672c8852SJeremy Kerr 	astlpc->kcs_fd = open(kcs_path, O_RDWR);
309*672c8852SJeremy Kerr 	if (astlpc->kcs_fd < 0)
310*672c8852SJeremy Kerr 		return -1;
311*672c8852SJeremy Kerr 
312*672c8852SJeremy Kerr 	return 0;
313*672c8852SJeremy Kerr }
314*672c8852SJeremy Kerr 
315*672c8852SJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init(void)
316*672c8852SJeremy Kerr {
317*672c8852SJeremy Kerr 	struct mctp_binding_astlpc *astlpc;
318*672c8852SJeremy Kerr 	int rc;
319*672c8852SJeremy Kerr 
320*672c8852SJeremy Kerr 	astlpc = __mctp_alloc(sizeof(*astlpc));
321*672c8852SJeremy Kerr 	memset(astlpc, 0, sizeof(*astlpc));
322*672c8852SJeremy Kerr 	astlpc->binding.name = "astlpc";
323*672c8852SJeremy Kerr 	astlpc->binding.version = 1;
324*672c8852SJeremy Kerr 	astlpc->binding.tx = mctp_binding_astlpc_tx;
325*672c8852SJeremy Kerr 
326*672c8852SJeremy Kerr 	rc = mctp_astlpc_init_lpc(astlpc);
327*672c8852SJeremy Kerr 	if (rc) {
328*672c8852SJeremy Kerr 		free(astlpc);
329*672c8852SJeremy Kerr 		return NULL;
330*672c8852SJeremy Kerr 	}
331*672c8852SJeremy Kerr 
332*672c8852SJeremy Kerr 	rc = mctp_astlpc_init_kcs(astlpc);
333*672c8852SJeremy Kerr 	if (rc) {
334*672c8852SJeremy Kerr 		free(astlpc);
335*672c8852SJeremy Kerr 		return NULL;
336*672c8852SJeremy Kerr 	}
337*672c8852SJeremy Kerr 
338*672c8852SJeremy Kerr 	rc = mctp_astlpc_init_bmc(astlpc);
339*672c8852SJeremy Kerr 	if (rc) {
340*672c8852SJeremy Kerr 		free(astlpc);
341*672c8852SJeremy Kerr 		return NULL;
342*672c8852SJeremy Kerr 	}
343*672c8852SJeremy Kerr 
344*672c8852SJeremy Kerr 	return astlpc;
345*672c8852SJeremy Kerr }
346*672c8852SJeremy Kerr 
347