• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <console/console.h>
4 #include <device/device.h>
5 #include <arch/io.h>
6 #include <timer.h>
7 #include "ipmi_if.h"
8 
9 #define IPMI_KCS_STATE(_x)	((_x) >> 6)
10 
11 #define IPMI_KCS_GET_STATUS_ABORT 0x60
12 #define IPMI_KCS_START_WRITE 0x61
13 #define IPMI_KCS_END_WRITE 0x62
14 #define IPMI_KCS_READ_BYTE 0x68
15 
16 #define IPMI_KCS_OBF 0x01
17 #define IPMI_KCS_IBF 0x02
18 #define IPMI_KCS_ATN 0x04
19 
20 #define IPMI_KCS_STATE_IDLE 0x00
21 #define IPMI_KCS_STATE_READ 0x01
22 #define IPMI_KCS_STATE_WRITE 0x02
23 #define IPMI_KCS_STATE_ERROR 0x03
24 
25 #define IPMI_CMD(_x) ((_x) + CONFIG_IPMI_KCS_REGISTER_SPACING)
26 #define IPMI_DATA(_x) ((_x))
27 #define IPMI_STAT(_x) ((_x) + CONFIG_IPMI_KCS_REGISTER_SPACING)
28 
ipmi_kcs_status(int port)29 static unsigned char ipmi_kcs_status(int port)
30 {
31 	unsigned char status = inb(IPMI_STAT(port));
32 	if (CONFIG(DEBUG_IPMI))
33 		printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, status);
34 	return status;
35 }
36 
wait_ibf_timeout(int port)37 static int wait_ibf_timeout(int port)
38 {
39 	if (!wait_ms(CONFIG_IPMI_KCS_TIMEOUT_MS, !(ipmi_kcs_status(port) & IPMI_KCS_IBF))) {
40 		printk(BIOS_ERR, "wait_ibf timeout!\n");
41 		return 1;
42 	} else {
43 		return 0;
44 	}
45 }
46 
wait_obf_timeout(int port)47 static int wait_obf_timeout(int port)
48 {
49 	if (!wait_ms(CONFIG_IPMI_KCS_TIMEOUT_MS, (ipmi_kcs_status(port) & IPMI_KCS_OBF))) {
50 		printk(BIOS_ERR, "wait_obf timeout!\n");
51 		return 1;
52 	} else {
53 		return 0;
54 	}
55 }
56 
ipmi_kcs_send_data_byte(int port,const unsigned char byte)57 static int ipmi_kcs_send_data_byte(int port, const unsigned char byte)
58 {
59 	unsigned char status;
60 
61 	if (CONFIG(DEBUG_IPMI))
62 		printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, byte);
63 
64 	outb(byte, IPMI_DATA(port));
65 
66 	if (wait_ibf_timeout(port))
67 		return 1;
68 
69 	status = ipmi_kcs_status(port);
70 	if ((status & IPMI_KCS_OBF) &&
71 	    IPMI_KCS_STATE(status) != IPMI_KCS_STATE_WRITE) {
72 		printk(BIOS_ERR, "%s: status %02x\n", __func__, status);
73 		return 1;
74 	}
75 
76 	if (ipmi_kcs_status(port) & IPMI_KCS_OBF)
77 		inb(IPMI_DATA(port));
78 	return 0;
79 }
80 
ipmi_kcs_send_last_data_byte(int port,const unsigned char byte)81 static int ipmi_kcs_send_last_data_byte(int port, const unsigned char byte)
82 {
83 	unsigned char status;
84 
85 	if (CONFIG(DEBUG_IPMI))
86 		printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, byte);
87 
88 	if (wait_ibf_timeout(port))
89 		return 1;
90 
91 	status = ipmi_kcs_status(port);
92 	if ((status & IPMI_KCS_OBF) &&
93 	    IPMI_KCS_STATE(status) != IPMI_KCS_STATE_WRITE) {
94 		printk(BIOS_ERR, "%s: status %02x\n", __func__, status);
95 		return 1;
96 	}
97 
98 	if (ipmi_kcs_status(port) & IPMI_KCS_OBF)
99 		inb(IPMI_DATA(port));
100 
101 	outb(byte, IPMI_DATA(port));
102 	return 0;
103 }
104 
ipmi_kcs_send_cmd_byte(int port,const unsigned char byte)105 static int ipmi_kcs_send_cmd_byte(int port, const unsigned char byte)
106 {
107 	if (CONFIG(DEBUG_IPMI))
108 		printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, byte);
109 
110 	if (wait_ibf_timeout(port))
111 		return 1;
112 
113 	if (ipmi_kcs_status(port) & IPMI_KCS_OBF)
114 		inb(IPMI_DATA(port));
115 	outb(byte, IPMI_CMD(port));
116 
117 	if (wait_ibf_timeout(port))
118 		return 1;
119 
120 	if (ipmi_kcs_status(port) & IPMI_KCS_OBF)
121 		inb(IPMI_DATA(port));
122 
123 	return 0;
124 }
125 
ipmi_kcs_send_message(int port,int netfn,int lun,int cmd,const unsigned char * msg,int len)126 static int ipmi_kcs_send_message(int port, int netfn, int lun, int cmd,
127 				const unsigned char *msg, int len)
128 {
129 	int ret;
130 
131 	ret = ipmi_kcs_send_cmd_byte(port, IPMI_KCS_START_WRITE);
132 	if (ret) {
133 		printk(BIOS_ERR, "IPMI START WRITE failed\n");
134 		return ret;
135 	}
136 
137 	ret = ipmi_kcs_send_data_byte(port, (netfn << 2) | (lun & 3));
138 	if (ret) {
139 		printk(BIOS_ERR, "IPMI NETFN failed\n");
140 		return ret;
141 	}
142 
143 	if (!len) {
144 		ret = ipmi_kcs_send_cmd_byte(port, IPMI_KCS_END_WRITE);
145 		if (ret) {
146 			printk(BIOS_ERR, "IPMI END WRITE failed\n");
147 			return ret;
148 		}
149 
150 		ret = ipmi_kcs_send_last_data_byte(port, cmd);
151 		if (ret) {
152 			printk(BIOS_ERR, "IPMI BYTE WRITE failed\n");
153 			return ret;
154 		}
155 	} else {
156 		ret = ipmi_kcs_send_data_byte(port, cmd);
157 		if (ret) {
158 			printk(BIOS_ERR, "IPMI CMD failed\n");
159 			return ret;
160 		}
161 
162 		while (len > 1) {
163 			ret = ipmi_kcs_send_data_byte(port, *msg++);
164 			if (ret) {
165 				printk(BIOS_ERR, "IPMI BYTE WRITE failed\n");
166 				return ret;
167 			}
168 			len--;
169 		}
170 
171 		ret = ipmi_kcs_send_cmd_byte(port, IPMI_KCS_END_WRITE);
172 		if (ret) {
173 			printk(BIOS_ERR, "IPMI END WRITE failed\n");
174 			return ret;
175 		}
176 
177 		ret = ipmi_kcs_send_last_data_byte(port, *msg);
178 		if (ret) {
179 			printk(BIOS_ERR, "IPMI BYTE WRITE failed\n");
180 			return ret;
181 		}
182 	}
183 
184 	return 0;
185 }
186 
ipmi_kcs_read_message(int port,unsigned char * msg,int len)187 static int ipmi_kcs_read_message(int port, unsigned char *msg, int len)
188 {
189 	int status, ret = 0;
190 
191 	if (wait_ibf_timeout(port))
192 		return 1;
193 
194 	for (;;) {
195 		status = ipmi_kcs_status(port);
196 
197 		if (IPMI_KCS_STATE(status) == IPMI_KCS_STATE_IDLE)
198 			return ret;
199 
200 		if (IPMI_KCS_STATE(status) != IPMI_KCS_STATE_READ) {
201 			printk(BIOS_ERR, "%s: wrong state: 0x%02x\n", __func__,
202 			       status);
203 			return -1;
204 		}
205 
206 		if (wait_obf_timeout(port))
207 			return -1;
208 
209 		if (msg && (ret < len)) {
210 			*msg++ = inb(IPMI_DATA(port));
211 			ret++;
212 		}
213 
214 		if (wait_ibf_timeout(port))
215 			return -1;
216 
217 		outb(IPMI_KCS_READ_BYTE, IPMI_DATA(port));
218 	}
219 	return ret;
220 }
221 
ipmi_message(int port,int netfn,int lun,int cmd,const unsigned char * inmsg,int inlen,unsigned char * outmsg,int outlen)222 int ipmi_message(int port, int netfn, int lun, int cmd,
223 		 const unsigned char *inmsg, int inlen,
224 		 unsigned char *outmsg, int outlen)
225 {
226 	if (ipmi_kcs_send_message(port, netfn, lun, cmd, inmsg, inlen)) {
227 		printk(BIOS_ERR, "ipmi_kcs_send_message failed\n");
228 		return -1;
229 	}
230 
231 	return ipmi_kcs_read_message(port, outmsg, outlen);
232 }
233