xref: /openbmc/libmctp/astlpc.c (revision 4663f67ce5646ce618b5b9f16d486bc67d170d74)
13d36ee2eSJeremy Kerr /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2672c8852SJeremy Kerr 
3672c8852SJeremy Kerr #include <assert.h>
492a10a6bSJeremy Kerr #include <endian.h>
559c6a5c9SAndrew Jeffery #include <err.h>
6edfe383fSAndrew Jeffery #include <inttypes.h>
7672c8852SJeremy Kerr #include <stdbool.h>
8672c8852SJeremy Kerr #include <stdlib.h>
9672c8852SJeremy Kerr #include <string.h>
10672c8852SJeremy Kerr 
11672c8852SJeremy Kerr #define pr_fmt(x) "astlpc: " x
12672c8852SJeremy Kerr 
1359c6a5c9SAndrew Jeffery #if HAVE_CONFIG_H
1459c6a5c9SAndrew Jeffery #include "config.h"
1559c6a5c9SAndrew Jeffery #endif
1659c6a5c9SAndrew Jeffery 
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 
22b214c643SJeremy Kerr #ifdef MCTP_HAVE_FILEIO
2392a10a6bSJeremy Kerr 
24c6f676d1SJeremy Kerr #include <unistd.h>
2592a10a6bSJeremy Kerr #include <fcntl.h>
2692a10a6bSJeremy Kerr #include <sys/ioctl.h>
2792a10a6bSJeremy Kerr #include <sys/mman.h>
2892a10a6bSJeremy Kerr #include <linux/aspeed-lpc-ctrl.h>
2992a10a6bSJeremy Kerr 
3092a10a6bSJeremy Kerr /* kernel interface */
3192a10a6bSJeremy Kerr static const char *kcs_path = "/dev/mctp0";
3292a10a6bSJeremy Kerr static const char *lpc_path = "/dev/aspeed-lpc-ctrl";
3392a10a6bSJeremy Kerr 
3492a10a6bSJeremy Kerr #endif
3592a10a6bSJeremy Kerr 
36672c8852SJeremy Kerr struct mctp_binding_astlpc {
37672c8852SJeremy Kerr 	struct mctp_binding	binding;
38bc53d35aSJeremy Kerr 
39672c8852SJeremy Kerr 	union {
40672c8852SJeremy Kerr 		void			*lpc_map;
41672c8852SJeremy Kerr 		struct mctp_lpcmap_hdr	*lpc_hdr;
42672c8852SJeremy Kerr 	};
43bc53d35aSJeremy Kerr 
44bc53d35aSJeremy Kerr 	/* direct ops data */
45bc53d35aSJeremy Kerr 	struct mctp_binding_astlpc_ops	ops;
46bc53d35aSJeremy Kerr 	void			*ops_data;
47bc53d35aSJeremy Kerr 	struct mctp_lpcmap_hdr	*priv_hdr;
48bc53d35aSJeremy Kerr 
49bc53d35aSJeremy Kerr 	/* fileio ops data */
50bc53d35aSJeremy Kerr 	void			*lpc_map_base;
51672c8852SJeremy Kerr 	int			kcs_fd;
52672c8852SJeremy Kerr 	uint8_t			kcs_status;
53672c8852SJeremy Kerr 
54672c8852SJeremy Kerr 	bool			running;
55672c8852SJeremy Kerr 
56672c8852SJeremy Kerr 	/* temporary transmit buffer */
57672c8852SJeremy Kerr 	uint8_t			txbuf[256];
58672c8852SJeremy Kerr };
59672c8852SJeremy Kerr 
60672c8852SJeremy Kerr #ifndef container_of
61672c8852SJeremy Kerr #define container_of(ptr, type, member) \
62672c8852SJeremy Kerr 	(type *)((char *)(ptr) - (char *)&((type *)0)->member)
63672c8852SJeremy Kerr #endif
64672c8852SJeremy Kerr 
65672c8852SJeremy Kerr #define binding_to_astlpc(b) \
66672c8852SJeremy Kerr 	container_of(b, struct mctp_binding_astlpc, binding)
67672c8852SJeremy Kerr 
68672c8852SJeremy Kerr #define MCTP_MAGIC	0x4d435450
69672c8852SJeremy Kerr #define BMC_VER_MIN	1
70672c8852SJeremy Kerr #define BMC_VER_CUR	1
71672c8852SJeremy Kerr 
72672c8852SJeremy Kerr struct mctp_lpcmap_hdr {
73672c8852SJeremy Kerr 	uint32_t	magic;
74672c8852SJeremy Kerr 
75672c8852SJeremy Kerr 	uint16_t	bmc_ver_min;
76672c8852SJeremy Kerr 	uint16_t	bmc_ver_cur;
77672c8852SJeremy Kerr 	uint16_t	host_ver_min;
78672c8852SJeremy Kerr 	uint16_t	host_ver_cur;
79672c8852SJeremy Kerr 	uint16_t	negotiated_ver;
80672c8852SJeremy Kerr 	uint16_t	pad0;
81672c8852SJeremy Kerr 
82672c8852SJeremy Kerr 	uint32_t	rx_offset;
83672c8852SJeremy Kerr 	uint32_t	rx_size;
84672c8852SJeremy Kerr 	uint32_t	tx_offset;
85672c8852SJeremy Kerr 	uint32_t	tx_size;
86672c8852SJeremy Kerr } __attribute__((packed));
87672c8852SJeremy Kerr 
88672c8852SJeremy Kerr /* layout of TX/RX areas */
89672c8852SJeremy Kerr static const uint32_t	rx_offset = 0x100;
90672c8852SJeremy Kerr static const uint32_t	rx_size   = 0x100;
91672c8852SJeremy Kerr static const uint32_t	tx_offset = 0x200;
92672c8852SJeremy Kerr static const uint32_t	tx_size   = 0x100;
93672c8852SJeremy Kerr 
94672c8852SJeremy Kerr #define LPC_WIN_SIZE                (1 * 1024 * 1024)
95672c8852SJeremy Kerr 
96672c8852SJeremy Kerr enum {
97672c8852SJeremy Kerr 	KCS_REG_DATA = 0,
98672c8852SJeremy Kerr 	KCS_REG_STATUS = 1,
99672c8852SJeremy Kerr };
100672c8852SJeremy Kerr 
101672c8852SJeremy Kerr #define KCS_STATUS_BMC_READY		0x80
102672c8852SJeremy Kerr #define KCS_STATUS_CHANNEL_ACTIVE	0x40
103672c8852SJeremy Kerr #define KCS_STATUS_IBF			0x02
104672c8852SJeremy Kerr #define KCS_STATUS_OBF			0x01
105672c8852SJeremy Kerr 
106bc53d35aSJeremy Kerr static bool lpc_direct(struct mctp_binding_astlpc *astlpc)
107bc53d35aSJeremy Kerr {
108bc53d35aSJeremy Kerr 	return astlpc->lpc_map != NULL;
109bc53d35aSJeremy Kerr }
110bc53d35aSJeremy Kerr 
111672c8852SJeremy Kerr static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc,
112672c8852SJeremy Kerr 		uint8_t status)
113672c8852SJeremy Kerr {
114bc53d35aSJeremy Kerr 	uint8_t data;
115672c8852SJeremy Kerr 	int rc;
116672c8852SJeremy Kerr 
1171a4b55acSJeremy Kerr 	/* Since we're setting the status register, we want the other endpoint
1181a4b55acSJeremy Kerr 	 * to be interrupted. However, some hardware may only raise a host-side
1191a4b55acSJeremy Kerr 	 * interrupt on an ODR event.
1201a4b55acSJeremy Kerr 	 * So, write a dummy value of 0xff to ODR, which will ensure that an
1211a4b55acSJeremy Kerr 	 * interrupt is triggered, and can be ignored by the host.
1221a4b55acSJeremy Kerr 	 */
123bc53d35aSJeremy Kerr 	data = 0xff;
124bc53d35aSJeremy Kerr 	status |= KCS_STATUS_OBF;
1251a4b55acSJeremy Kerr 
126c84fd56dSAndrew Jeffery 	rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS,
127c84fd56dSAndrew Jeffery 			status);
128c84fd56dSAndrew Jeffery 	if (rc) {
129c84fd56dSAndrew Jeffery 		mctp_prwarn("KCS status write failed");
130c84fd56dSAndrew Jeffery 		return -1;
131c84fd56dSAndrew Jeffery 	}
132c84fd56dSAndrew Jeffery 
133bc53d35aSJeremy Kerr 	rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA,
134bc53d35aSJeremy Kerr 			data);
135bc53d35aSJeremy Kerr 	if (rc) {
136bc53d35aSJeremy Kerr 		mctp_prwarn("KCS dummy data write failed");
137bc53d35aSJeremy Kerr 		return -1;
138bc53d35aSJeremy Kerr 	}
139bc53d35aSJeremy Kerr 
140672c8852SJeremy Kerr 	return 0;
141672c8852SJeremy Kerr }
142672c8852SJeremy Kerr 
143672c8852SJeremy Kerr static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc,
144672c8852SJeremy Kerr 		uint8_t data)
145672c8852SJeremy Kerr {
146672c8852SJeremy Kerr 	uint8_t status;
147672c8852SJeremy Kerr 	int rc;
148672c8852SJeremy Kerr 
149672c8852SJeremy Kerr 	for (;;) {
150bc53d35aSJeremy Kerr 		rc = astlpc->ops.kcs_read(astlpc->ops_data,
151bc53d35aSJeremy Kerr 				MCTP_ASTLPC_KCS_REG_STATUS, &status);
1521b27fe87SAndrew Jeffery 		if (rc) {
153182204e3SAndrew Jeffery 			mctp_prwarn("KCS status read failed");
154672c8852SJeremy Kerr 			return -1;
155672c8852SJeremy Kerr 		}
156672c8852SJeremy Kerr 		if (!(status & KCS_STATUS_OBF))
157672c8852SJeremy Kerr 			break;
158672c8852SJeremy Kerr 		/* todo: timeout */
159672c8852SJeremy Kerr 	}
160672c8852SJeremy Kerr 
161bc53d35aSJeremy Kerr 	rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA,
162bc53d35aSJeremy Kerr 			data);
163bc53d35aSJeremy Kerr 	if (rc) {
164672c8852SJeremy Kerr 		mctp_prwarn("KCS data write failed");
165672c8852SJeremy Kerr 		return -1;
166672c8852SJeremy Kerr 	}
167672c8852SJeremy Kerr 
168672c8852SJeremy Kerr 	return 0;
169672c8852SJeremy Kerr }
170672c8852SJeremy Kerr 
171672c8852SJeremy Kerr static int mctp_binding_astlpc_tx(struct mctp_binding *b,
172672c8852SJeremy Kerr 		struct mctp_pktbuf *pkt)
173672c8852SJeremy Kerr {
174672c8852SJeremy Kerr 	struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b);
175672c8852SJeremy Kerr 	uint32_t len;
176edfe383fSAndrew Jeffery 	struct mctp_hdr *hdr;
177672c8852SJeremy Kerr 
178edfe383fSAndrew Jeffery 	hdr = mctp_pktbuf_hdr(pkt);
179672c8852SJeremy Kerr 	len = mctp_pktbuf_size(pkt);
180edfe383fSAndrew Jeffery 
181edfe383fSAndrew Jeffery 	mctp_prdebug("%s: Transmitting %"PRIu32"-byte packet (%hhu, %hhu, 0x%hhx)",
182edfe383fSAndrew Jeffery 		     __func__, len, hdr->src, hdr->dest, hdr->flags_seq_tag);
183edfe383fSAndrew Jeffery 
184672c8852SJeremy Kerr 	if (len > rx_size - 4) {
185672c8852SJeremy Kerr 		mctp_prwarn("invalid TX len 0x%x", len);
186672c8852SJeremy Kerr 		return -1;
187672c8852SJeremy Kerr 	}
188672c8852SJeremy Kerr 
189bc53d35aSJeremy Kerr 	if (lpc_direct(astlpc)) {
190672c8852SJeremy Kerr 		*(uint32_t *)(astlpc->lpc_map + rx_offset) = htobe32(len);
191bc53d35aSJeremy Kerr 		memcpy(astlpc->lpc_map + rx_offset + 4, mctp_pktbuf_hdr(pkt),
192bc53d35aSJeremy Kerr 				len);
193bc53d35aSJeremy Kerr 	} else {
194bc53d35aSJeremy Kerr 		uint32_t tmp = htobe32(len);
195bc53d35aSJeremy Kerr 		astlpc->ops.lpc_write(astlpc->ops_data, &tmp, rx_offset,
196bc53d35aSJeremy Kerr 				sizeof(tmp));
197bc53d35aSJeremy Kerr 		astlpc->ops.lpc_write(astlpc->ops_data, mctp_pktbuf_hdr(pkt),
198bc53d35aSJeremy Kerr 				rx_offset + 4, len);
199bc53d35aSJeremy Kerr 	}
200672c8852SJeremy Kerr 
201672c8852SJeremy Kerr 	mctp_binding_set_tx_enabled(b, false);
202672c8852SJeremy Kerr 
203672c8852SJeremy Kerr 	mctp_astlpc_kcs_send(astlpc, 0x1);
204672c8852SJeremy Kerr 	return 0;
205672c8852SJeremy Kerr }
206672c8852SJeremy Kerr 
207672c8852SJeremy Kerr static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc)
208672c8852SJeremy Kerr {
209672c8852SJeremy Kerr 	/* todo: actual version negotiation */
210bc53d35aSJeremy Kerr 	if (lpc_direct(astlpc)) {
211672c8852SJeremy Kerr 		astlpc->lpc_hdr->negotiated_ver = htobe16(1);
212bc53d35aSJeremy Kerr 	} else {
213bc53d35aSJeremy Kerr 		uint16_t ver = htobe16(1);
214bc53d35aSJeremy Kerr 		astlpc->ops.lpc_write(astlpc->ops_data, &ver,
215bc53d35aSJeremy Kerr 				offsetof(struct mctp_lpcmap_hdr,
216bc53d35aSJeremy Kerr 					negotiated_ver),
217bc53d35aSJeremy Kerr 				sizeof(ver));
218bc53d35aSJeremy Kerr 	}
219672c8852SJeremy Kerr 	mctp_astlpc_kcs_set_status(astlpc,
220672c8852SJeremy Kerr 			KCS_STATUS_BMC_READY | KCS_STATUS_CHANNEL_ACTIVE |
221672c8852SJeremy Kerr 			KCS_STATUS_OBF);
222672c8852SJeremy Kerr 
223672c8852SJeremy Kerr 	mctp_binding_set_tx_enabled(&astlpc->binding, true);
224672c8852SJeremy Kerr }
225672c8852SJeremy Kerr 
226672c8852SJeremy Kerr static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc)
227672c8852SJeremy Kerr {
228672c8852SJeremy Kerr 	struct mctp_pktbuf *pkt;
229672c8852SJeremy Kerr 	uint32_t len;
230672c8852SJeremy Kerr 
231bc53d35aSJeremy Kerr 	if (lpc_direct(astlpc)) {
232bc53d35aSJeremy Kerr 		len = *(uint32_t *)(astlpc->lpc_map + tx_offset);
233bc53d35aSJeremy Kerr 	} else {
234bc53d35aSJeremy Kerr 		astlpc->ops.lpc_read(astlpc->ops_data, &len,
235bc53d35aSJeremy Kerr 				tx_offset, sizeof(len));
236bc53d35aSJeremy Kerr 	}
237bc53d35aSJeremy Kerr 	len = be32toh(len);
238bc53d35aSJeremy Kerr 
239672c8852SJeremy Kerr 	if (len > tx_size - 4) {
240672c8852SJeremy Kerr 		mctp_prwarn("invalid RX len 0x%x", len);
241672c8852SJeremy Kerr 		return;
242672c8852SJeremy Kerr 	}
243672c8852SJeremy Kerr 
244df15f7e9SJeremy Kerr 	if (len > astlpc->binding.pkt_size) {
245672c8852SJeremy Kerr 		mctp_prwarn("invalid RX len 0x%x", len);
246672c8852SJeremy Kerr 		return;
247672c8852SJeremy Kerr 	}
248672c8852SJeremy Kerr 
249df15f7e9SJeremy Kerr 	pkt = mctp_pktbuf_alloc(&astlpc->binding, len);
250672c8852SJeremy Kerr 	if (!pkt)
251672c8852SJeremy Kerr 		goto out_complete;
252672c8852SJeremy Kerr 
253bc53d35aSJeremy Kerr 	if (lpc_direct(astlpc)) {
254bc53d35aSJeremy Kerr 		memcpy(mctp_pktbuf_hdr(pkt),
255bc53d35aSJeremy Kerr 				astlpc->lpc_map + tx_offset + 4, len);
256bc53d35aSJeremy Kerr 	} else {
257bc53d35aSJeremy Kerr 		astlpc->ops.lpc_read(astlpc->ops_data, mctp_pktbuf_hdr(pkt),
258bc53d35aSJeremy Kerr 				tx_offset + 4, len);
259bc53d35aSJeremy Kerr 	}
260672c8852SJeremy Kerr 
261672c8852SJeremy Kerr 	mctp_bus_rx(&astlpc->binding, pkt);
262672c8852SJeremy Kerr 
263672c8852SJeremy Kerr out_complete:
264672c8852SJeremy Kerr 	mctp_astlpc_kcs_send(astlpc, 0x2);
265672c8852SJeremy Kerr }
266672c8852SJeremy Kerr 
267672c8852SJeremy Kerr static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc)
268672c8852SJeremy Kerr {
269672c8852SJeremy Kerr 	mctp_binding_set_tx_enabled(&astlpc->binding, true);
270672c8852SJeremy Kerr }
271672c8852SJeremy Kerr 
272672c8852SJeremy Kerr int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc)
273672c8852SJeremy Kerr {
274bc53d35aSJeremy Kerr 	uint8_t status, data;
275672c8852SJeremy Kerr 	int rc;
276672c8852SJeremy Kerr 
277bc53d35aSJeremy Kerr 	rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS,
278bc53d35aSJeremy Kerr 			&status);
279bc53d35aSJeremy Kerr 	if (rc) {
280672c8852SJeremy Kerr 		mctp_prwarn("KCS read error");
281672c8852SJeremy Kerr 		return -1;
282672c8852SJeremy Kerr 	}
283672c8852SJeremy Kerr 
284edfe383fSAndrew Jeffery 	mctp_prdebug("%s: status: 0x%hhx", __func__, status);
285edfe383fSAndrew Jeffery 
286bc53d35aSJeremy Kerr 	if (!(status & KCS_STATUS_IBF))
287672c8852SJeremy Kerr 		return 0;
288672c8852SJeremy Kerr 
289bc53d35aSJeremy Kerr 	rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA,
290bc53d35aSJeremy Kerr 			&data);
291bc53d35aSJeremy Kerr 	if (rc) {
292bc53d35aSJeremy Kerr 		mctp_prwarn("KCS data read error");
293bc53d35aSJeremy Kerr 		return -1;
294bc53d35aSJeremy Kerr 	}
295bc53d35aSJeremy Kerr 
296edfe383fSAndrew Jeffery 	mctp_prdebug("%s: data: 0x%hhx", __func__, data);
297edfe383fSAndrew Jeffery 
298672c8852SJeremy Kerr 	switch (data) {
299672c8852SJeremy Kerr 	case 0x0:
300672c8852SJeremy Kerr 		mctp_astlpc_init_channel(astlpc);
301672c8852SJeremy Kerr 		break;
302672c8852SJeremy Kerr 	case 0x1:
303672c8852SJeremy Kerr 		mctp_astlpc_rx_start(astlpc);
304672c8852SJeremy Kerr 		break;
305672c8852SJeremy Kerr 	case 0x2:
306672c8852SJeremy Kerr 		mctp_astlpc_tx_complete(astlpc);
307672c8852SJeremy Kerr 		break;
3081a4b55acSJeremy Kerr 	case 0xff:
3091a4b55acSJeremy Kerr 		/* reserved value for dummy data writes; do nothing */
3101a4b55acSJeremy Kerr 		break;
311672c8852SJeremy Kerr 	default:
312672c8852SJeremy Kerr 		mctp_prwarn("unknown message 0x%x", data);
313672c8852SJeremy Kerr 	}
314672c8852SJeremy Kerr 	return 0;
315672c8852SJeremy Kerr }
316672c8852SJeremy Kerr 
317672c8852SJeremy Kerr static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc)
318672c8852SJeremy Kerr {
319bc53d35aSJeremy Kerr 	struct mctp_lpcmap_hdr *hdr;
320672c8852SJeremy Kerr 	uint8_t status;
321672c8852SJeremy Kerr 	int rc;
322672c8852SJeremy Kerr 
323bc53d35aSJeremy Kerr 	if (lpc_direct(astlpc))
324bc53d35aSJeremy Kerr 		hdr = astlpc->lpc_hdr;
325bc53d35aSJeremy Kerr 	else
326bc53d35aSJeremy Kerr 		hdr = astlpc->priv_hdr;
327672c8852SJeremy Kerr 
328bc53d35aSJeremy Kerr 	hdr->magic = htobe32(MCTP_MAGIC);
329bc53d35aSJeremy Kerr 	hdr->bmc_ver_min = htobe16(BMC_VER_MIN);
330bc53d35aSJeremy Kerr 	hdr->bmc_ver_cur = htobe16(BMC_VER_CUR);
331bc53d35aSJeremy Kerr 
332bc53d35aSJeremy Kerr 	hdr->rx_offset = htobe32(rx_offset);
333bc53d35aSJeremy Kerr 	hdr->rx_size = htobe32(rx_size);
334bc53d35aSJeremy Kerr 	hdr->tx_offset = htobe32(tx_offset);
335bc53d35aSJeremy Kerr 	hdr->tx_size = htobe32(tx_size);
336bc53d35aSJeremy Kerr 
337bc53d35aSJeremy Kerr 	if (!lpc_direct(astlpc))
338bc53d35aSJeremy Kerr 		astlpc->ops.lpc_write(astlpc->ops_data, hdr, 0, sizeof(*hdr));
339672c8852SJeremy Kerr 
340672c8852SJeremy Kerr 	/* set status indicating that the BMC is now active */
341672c8852SJeremy Kerr 	status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF;
342bc53d35aSJeremy Kerr 	rc = astlpc->ops.kcs_write(astlpc->ops_data,
343bc53d35aSJeremy Kerr 			MCTP_ASTLPC_KCS_REG_STATUS, status);
344bc53d35aSJeremy Kerr 	if (rc) {
345672c8852SJeremy Kerr 		mctp_prwarn("KCS write failed");
346672c8852SJeremy Kerr 	}
347672c8852SJeremy Kerr 
348672c8852SJeremy Kerr 	return rc;
349672c8852SJeremy Kerr }
350672c8852SJeremy Kerr 
3518081bebaSJeremy Kerr static int mctp_binding_astlpc_start(struct mctp_binding *b)
3528081bebaSJeremy Kerr {
3538081bebaSJeremy Kerr 	struct mctp_binding_astlpc *astlpc = container_of(b,
3548081bebaSJeremy Kerr 			struct mctp_binding_astlpc, binding);
3558081bebaSJeremy Kerr 
3568081bebaSJeremy Kerr 	return mctp_astlpc_init_bmc(astlpc);
3578081bebaSJeremy Kerr }
3588081bebaSJeremy Kerr 
359bc53d35aSJeremy Kerr /* allocate and basic initialisation */
360bc53d35aSJeremy Kerr static struct mctp_binding_astlpc *__mctp_astlpc_init(void)
361bc53d35aSJeremy Kerr {
362bc53d35aSJeremy Kerr 	struct mctp_binding_astlpc *astlpc;
363bc53d35aSJeremy Kerr 
364bc53d35aSJeremy Kerr 	astlpc = __mctp_alloc(sizeof(*astlpc));
365bc53d35aSJeremy Kerr 	memset(astlpc, 0, sizeof(*astlpc));
366bc53d35aSJeremy Kerr 	astlpc->binding.name = "astlpc";
367bc53d35aSJeremy Kerr 	astlpc->binding.version = 1;
368bc53d35aSJeremy Kerr 	astlpc->binding.tx = mctp_binding_astlpc_tx;
3698081bebaSJeremy Kerr 	astlpc->binding.start = mctp_binding_astlpc_start;
37073c268e4SAndrew Jeffery 	astlpc->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU);
371bc53d35aSJeremy Kerr 	astlpc->binding.pkt_pad = 0;
372bc53d35aSJeremy Kerr 	astlpc->lpc_map = NULL;
373bc53d35aSJeremy Kerr 
374bc53d35aSJeremy Kerr 	return astlpc;
375bc53d35aSJeremy Kerr }
376bc53d35aSJeremy Kerr 
3773b36d17cSJeremy Kerr struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b)
3783b36d17cSJeremy Kerr {
3793b36d17cSJeremy Kerr 	return &b->binding;
3803b36d17cSJeremy Kerr }
3813b36d17cSJeremy Kerr 
382bc53d35aSJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init_ops(
383a0452495SAndrew Jeffery 		const struct mctp_binding_astlpc_ops *ops,
384bc53d35aSJeremy Kerr 		void *ops_data, void *lpc_map)
385bc53d35aSJeremy Kerr {
386bc53d35aSJeremy Kerr 	struct mctp_binding_astlpc *astlpc;
387bc53d35aSJeremy Kerr 
388bc53d35aSJeremy Kerr 	astlpc = __mctp_astlpc_init();
389bc53d35aSJeremy Kerr 	if (!astlpc)
390bc53d35aSJeremy Kerr 		return NULL;
391bc53d35aSJeremy Kerr 
392bc53d35aSJeremy Kerr 	memcpy(&astlpc->ops, ops, sizeof(astlpc->ops));
393bc53d35aSJeremy Kerr 	astlpc->ops_data = ops_data;
394bc53d35aSJeremy Kerr 	astlpc->lpc_map = lpc_map;
395bc53d35aSJeremy Kerr 
396bc53d35aSJeremy Kerr 	/* In indirect mode, we keep a separate buffer of header data.
397bc53d35aSJeremy Kerr 	 * We need to sync this through the lpc_read/lpc_write ops.
398bc53d35aSJeremy Kerr 	 */
399bc53d35aSJeremy Kerr 	if (!astlpc->lpc_map)
400bc53d35aSJeremy Kerr 		astlpc->priv_hdr = __mctp_alloc(sizeof(*astlpc->priv_hdr));
401bc53d35aSJeremy Kerr 
402bc53d35aSJeremy Kerr 	return astlpc;
403bc53d35aSJeremy Kerr }
404bc53d35aSJeremy Kerr 
405*4663f67cSAndrew Jeffery void mctp_astlpc_destroy(struct mctp_binding_astlpc *astlpc)
406*4663f67cSAndrew Jeffery {
407*4663f67cSAndrew Jeffery 	if (astlpc->priv_hdr)
408*4663f67cSAndrew Jeffery 		__mctp_free(astlpc->priv_hdr);
409*4663f67cSAndrew Jeffery 	__mctp_free(astlpc);
410*4663f67cSAndrew Jeffery }
411*4663f67cSAndrew Jeffery 
412b214c643SJeremy Kerr #ifdef MCTP_HAVE_FILEIO
413bc53d35aSJeremy Kerr static int mctp_astlpc_init_fileio_lpc(struct mctp_binding_astlpc *astlpc)
414672c8852SJeremy Kerr {
415672c8852SJeremy Kerr 	struct aspeed_lpc_ctrl_mapping map = {
416672c8852SJeremy Kerr 		.window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY,
417672c8852SJeremy Kerr 		.window_id = 0, /* There's only one */
418672c8852SJeremy Kerr 		.flags = 0,
419672c8852SJeremy Kerr 		.addr = 0,
420672c8852SJeremy Kerr 		.offset = 0,
421672c8852SJeremy Kerr 		.size = 0
422672c8852SJeremy Kerr 	};
423672c8852SJeremy Kerr 	int fd, rc;
424672c8852SJeremy Kerr 
425672c8852SJeremy Kerr 	fd = open(lpc_path, O_RDWR | O_SYNC);
426672c8852SJeremy Kerr 	if (fd < 0) {
427672c8852SJeremy Kerr 		mctp_prwarn("LPC open (%s) failed", lpc_path);
428672c8852SJeremy Kerr 		return -1;
429672c8852SJeremy Kerr 	}
430672c8852SJeremy Kerr 
431672c8852SJeremy Kerr 	rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map);
432672c8852SJeremy Kerr 	if (rc) {
433672c8852SJeremy Kerr 		mctp_prwarn("LPC GET_SIZE failed");
434672c8852SJeremy Kerr 		close(fd);
435672c8852SJeremy Kerr 		return -1;
436672c8852SJeremy Kerr 	}
437672c8852SJeremy Kerr 
438672c8852SJeremy Kerr 	astlpc->lpc_map_base = mmap(NULL, map.size, PROT_READ | PROT_WRITE,
439672c8852SJeremy Kerr 			MAP_SHARED, fd, 0);
440672c8852SJeremy Kerr 	if (astlpc->lpc_map_base == MAP_FAILED) {
441672c8852SJeremy Kerr 		mctp_prwarn("LPC mmap failed");
442672c8852SJeremy Kerr 		rc = -1;
443672c8852SJeremy Kerr 	} else {
444672c8852SJeremy Kerr 		astlpc->lpc_map = astlpc->lpc_map_base +
445672c8852SJeremy Kerr 			map.size - LPC_WIN_SIZE;
446672c8852SJeremy Kerr 	}
447672c8852SJeremy Kerr 
448672c8852SJeremy Kerr 	close(fd);
449672c8852SJeremy Kerr 
450672c8852SJeremy Kerr 	return rc;
451672c8852SJeremy Kerr }
452672c8852SJeremy Kerr 
453bc53d35aSJeremy Kerr static int mctp_astlpc_init_fileio_kcs(struct mctp_binding_astlpc *astlpc)
454672c8852SJeremy Kerr {
455672c8852SJeremy Kerr 	astlpc->kcs_fd = open(kcs_path, O_RDWR);
456672c8852SJeremy Kerr 	if (astlpc->kcs_fd < 0)
457672c8852SJeremy Kerr 		return -1;
458672c8852SJeremy Kerr 
459672c8852SJeremy Kerr 	return 0;
460672c8852SJeremy Kerr }
461672c8852SJeremy Kerr 
462bc53d35aSJeremy Kerr static int __mctp_astlpc_fileio_kcs_read(void *arg,
463bc53d35aSJeremy Kerr 		enum mctp_binding_astlpc_kcs_reg reg, uint8_t *val)
464bc53d35aSJeremy Kerr {
465bc53d35aSJeremy Kerr 	struct mctp_binding_astlpc *astlpc = arg;
466bc53d35aSJeremy Kerr 	off_t offset = reg;
467bc53d35aSJeremy Kerr 	int rc;
468bc53d35aSJeremy Kerr 
469bc53d35aSJeremy Kerr 	rc = pread(astlpc->kcs_fd, val, 1, offset);
470bc53d35aSJeremy Kerr 
471bc53d35aSJeremy Kerr 	return rc == 1 ? 0 : -1;
472bc53d35aSJeremy Kerr }
473bc53d35aSJeremy Kerr 
474bc53d35aSJeremy Kerr static int __mctp_astlpc_fileio_kcs_write(void *arg,
475bc53d35aSJeremy Kerr 		enum mctp_binding_astlpc_kcs_reg reg, uint8_t val)
476bc53d35aSJeremy Kerr {
477bc53d35aSJeremy Kerr 	struct mctp_binding_astlpc *astlpc = arg;
478bc53d35aSJeremy Kerr 	off_t offset = reg;
479bc53d35aSJeremy Kerr 	int rc;
480bc53d35aSJeremy Kerr 
481bc53d35aSJeremy Kerr 	rc = pwrite(astlpc->kcs_fd, &val, 1, offset);
482bc53d35aSJeremy Kerr 
483bc53d35aSJeremy Kerr 	return rc == 1 ? 0 : -1;
484bc53d35aSJeremy Kerr }
485bc53d35aSJeremy Kerr 
486bc53d35aSJeremy Kerr int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc)
487bc53d35aSJeremy Kerr {
488bc53d35aSJeremy Kerr 	return astlpc->kcs_fd;
489bc53d35aSJeremy Kerr }
490bc53d35aSJeremy Kerr 
491bc53d35aSJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void)
492672c8852SJeremy Kerr {
493672c8852SJeremy Kerr 	struct mctp_binding_astlpc *astlpc;
494672c8852SJeremy Kerr 	int rc;
495672c8852SJeremy Kerr 
496bc53d35aSJeremy Kerr 	astlpc = __mctp_astlpc_init();
497bc53d35aSJeremy Kerr 	if (!astlpc)
498bc53d35aSJeremy Kerr 		return NULL;
499672c8852SJeremy Kerr 
500bc53d35aSJeremy Kerr 	/* Set internal operations for kcs. We use direct accesses to the lpc
501bc53d35aSJeremy Kerr 	 * map area */
502bc53d35aSJeremy Kerr 	astlpc->ops.kcs_read = __mctp_astlpc_fileio_kcs_read;
503bc53d35aSJeremy Kerr 	astlpc->ops.kcs_write = __mctp_astlpc_fileio_kcs_write;
504bc53d35aSJeremy Kerr 	astlpc->ops_data = astlpc;
505bc53d35aSJeremy Kerr 
506bc53d35aSJeremy Kerr 	rc = mctp_astlpc_init_fileio_lpc(astlpc);
507672c8852SJeremy Kerr 	if (rc) {
508672c8852SJeremy Kerr 		free(astlpc);
509672c8852SJeremy Kerr 		return NULL;
510672c8852SJeremy Kerr 	}
511672c8852SJeremy Kerr 
512bc53d35aSJeremy Kerr 	rc = mctp_astlpc_init_fileio_kcs(astlpc);
513672c8852SJeremy Kerr 	if (rc) {
514672c8852SJeremy Kerr 		free(astlpc);
515672c8852SJeremy Kerr 		return NULL;
516672c8852SJeremy Kerr 	}
517672c8852SJeremy Kerr 
518672c8852SJeremy Kerr 	return astlpc;
519672c8852SJeremy Kerr }
52092a10a6bSJeremy Kerr #else
52192a10a6bSJeremy Kerr struct mctp_binding_astlpc * __attribute__((const))
52292a10a6bSJeremy Kerr 	mctp_astlpc_init_fileio(void)
52392a10a6bSJeremy Kerr {
52459c6a5c9SAndrew Jeffery 	warnx("Missing support for file IO");
52592a10a6bSJeremy Kerr 	return NULL;
52692a10a6bSJeremy Kerr }
527672c8852SJeremy Kerr 
52892a10a6bSJeremy Kerr int __attribute__((const)) mctp_astlpc_get_fd(
52992a10a6bSJeremy Kerr 		struct mctp_binding_astlpc *astlpc __attribute__((unused)))
53092a10a6bSJeremy Kerr {
53159c6a5c9SAndrew Jeffery 	warnx("Missing support for file IO");
53292a10a6bSJeremy Kerr 	return -1;
53392a10a6bSJeremy Kerr }
53492a10a6bSJeremy Kerr #endif
535