blob: 667827224f5ae63e3bd3c75b0da3966c2a4cb30d [file] [log] [blame]
Angel Pons8a3453f2020-04-02 23:48:19 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Sven Schnelle7435baa2012-07-08 18:32:23 +02002
3#include <console/console.h>
4#include <device/device.h>
5#include <arch/io.h>
Johnny Lind04c06b42020-09-04 17:05:58 +08006#include <timer.h>
Sergii Dmytrukef7dd5d2021-10-22 01:02:32 +03007#include "ipmi_if.h"
Sven Schnelle7435baa2012-07-08 18:32:23 +02008
9#define IPMI_KCS_STATE(_x) ((_x) >> 6)
10
Patrick Rudolphdb43afa2019-06-14 18:48:13 +020011#define IPMI_KCS_GET_STATUS_ABORT 0x60
Sven Schnelle7435baa2012-07-08 18:32:23 +020012#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
Johnny Lin1bc6bf12019-08-10 20:16:17 +080025#define IPMI_CMD(_x) ((_x) + CONFIG_IPMI_KCS_REGISTER_SPACING)
Sven Schnelle7435baa2012-07-08 18:32:23 +020026#define IPMI_DATA(_x) ((_x))
Johnny Lin1bc6bf12019-08-10 20:16:17 +080027#define IPMI_STAT(_x) ((_x) + CONFIG_IPMI_KCS_REGISTER_SPACING)
Sven Schnelle7435baa2012-07-08 18:32:23 +020028
29static unsigned char ipmi_kcs_status(int port)
30{
31 unsigned char status = inb(IPMI_STAT(port));
Marc Jonesdc12daf2021-04-16 14:26:08 -060032 if (CONFIG(DEBUG_IPMI))
33 printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, status);
Sven Schnelle7435baa2012-07-08 18:32:23 +020034 return status;
35}
36
37static int wait_ibf_timeout(int port)
38{
Johnny Lind04c06b42020-09-04 17:05:58 +080039 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 }
Sven Schnelle7435baa2012-07-08 18:32:23 +020045}
46
47static int wait_obf_timeout(int port)
48{
Johnny Lind04c06b42020-09-04 17:05:58 +080049 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 }
Sven Schnelle7435baa2012-07-08 18:32:23 +020055}
56
Sven Schnelle7435baa2012-07-08 18:32:23 +020057static int ipmi_kcs_send_data_byte(int port, const unsigned char byte)
58{
59 unsigned char status;
60
Marc Jonesdc12daf2021-04-16 14:26:08 -060061 if (CONFIG(DEBUG_IPMI))
62 printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, byte);
Sven Schnelle7435baa2012-07-08 18:32:23 +020063
64 outb(byte, IPMI_DATA(port));
65
66 if (wait_ibf_timeout(port))
67 return 1;
68
Martin Rothb9810a42017-07-23 20:00:04 -060069 status = ipmi_kcs_status(port);
Sven Schnelle7435baa2012-07-08 18:32:23 +020070 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
81static int ipmi_kcs_send_last_data_byte(int port, const unsigned char byte)
82{
83 unsigned char status;
84
Marc Jonesdc12daf2021-04-16 14:26:08 -060085 if (CONFIG(DEBUG_IPMI))
86 printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, byte);
Sven Schnelle7435baa2012-07-08 18:32:23 +020087
88 if (wait_ibf_timeout(port))
89 return 1;
90
Martin Rothb9810a42017-07-23 20:00:04 -060091 status = ipmi_kcs_status(port);
Sven Schnelle7435baa2012-07-08 18:32:23 +020092 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
105static int ipmi_kcs_send_cmd_byte(int port, const unsigned char byte)
106{
Marc Jonesdc12daf2021-04-16 14:26:08 -0600107 if (CONFIG(DEBUG_IPMI))
108 printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, byte);
Sven Schnelle7435baa2012-07-08 18:32:23 +0200109
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
126static 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
Patrick Rudolph14774762019-06-15 12:25:53 +0200131 ret = ipmi_kcs_send_cmd_byte(port, IPMI_KCS_START_WRITE);
132 if (ret) {
Sven Schnelle7435baa2012-07-08 18:32:23 +0200133 printk(BIOS_ERR, "IPMI START WRITE failed\n");
134 return ret;
135 }
136
Patrick Rudolph14774762019-06-15 12:25:53 +0200137 ret = ipmi_kcs_send_data_byte(port, (netfn << 2) | (lun & 3));
138 if (ret) {
Sven Schnelle7435baa2012-07-08 18:32:23 +0200139 printk(BIOS_ERR, "IPMI NETFN failed\n");
140 return ret;
141 }
142
Patrick Rudolphdb43afa2019-06-14 18:48:13 +0200143 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 }
Sven Schnelle7435baa2012-07-08 18:32:23 +0200149
Patrick Rudolphdb43afa2019-06-14 18:48:13 +0200150 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);
Patrick Rudolph14774762019-06-15 12:25:53 +0200178 if (ret) {
Sven Schnelle7435baa2012-07-08 18:32:23 +0200179 printk(BIOS_ERR, "IPMI BYTE WRITE failed\n");
180 return ret;
181 }
182 }
183
Sven Schnelle7435baa2012-07-08 18:32:23 +0200184 return 0;
185}
186
187static int ipmi_kcs_read_message(int port, unsigned char *msg, int len)
188{
189 int status, ret = 0;
190
Sven Schnelle7435baa2012-07-08 18:32:23 +0200191 if (wait_ibf_timeout(port))
192 return 1;
193
Elyes HAOUAS2e4d8062016-08-25 20:50:50 +0200194 for (;;) {
Sven Schnelle7435baa2012-07-08 18:32:23 +0200195 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) {
Patrick Rudolphdb43afa2019-06-14 18:48:13 +0200201 printk(BIOS_ERR, "%s: wrong state: 0x%02x\n", __func__,
202 status);
Sven Schnelle7435baa2012-07-08 18:32:23 +0200203 return -1;
204 }
205
206 if (wait_obf_timeout(port))
207 return -1;
208
Patrick Rudolphdb43afa2019-06-14 18:48:13 +0200209 if (msg && (ret < len)) {
210 *msg++ = inb(IPMI_DATA(port));
211 ret++;
212 }
Sven Schnelle7435baa2012-07-08 18:32:23 +0200213
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
Sergii Dmytrukef7dd5d2021-10-22 01:02:32 +0300222int ipmi_message(int port, int netfn, int lun, int cmd,
223 const unsigned char *inmsg, int inlen,
224 unsigned char *outmsg, int outlen)
Sven Schnelle7435baa2012-07-08 18:32:23 +0200225{
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}