1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2024 Oxide Computer Company
14 */
15
16 #include "ena_hw.h"
17 #include "ena.h"
18
19 uint32_t
ena_hw_bar_read32(const ena_t * ena,const uint16_t offset)20 ena_hw_bar_read32(const ena_t *ena, const uint16_t offset)
21 {
22 caddr_t addr = ena->ena_reg_base + offset;
23 return (ena_hw_abs_read32(ena, (uint32_t *)addr));
24 }
25
26 uint32_t
ena_hw_abs_read32(const ena_t * ena,uint32_t * addr)27 ena_hw_abs_read32(const ena_t *ena, uint32_t *addr)
28 {
29 VERIFY3U(addr, >=, ena->ena_reg_base);
30 VERIFY3U(addr, <, ena->ena_reg_base + (ena->ena_reg_size - 4));
31
32 return (ddi_get32(ena->ena_reg_hdl, addr));
33 }
34
35 void
ena_hw_bar_write32(const ena_t * ena,const uint16_t offset,const uint32_t val)36 ena_hw_bar_write32(const ena_t *ena, const uint16_t offset, const uint32_t val)
37 {
38 caddr_t addr = ena->ena_reg_base + offset;
39 ena_hw_abs_write32(ena, (uint32_t *)addr, val);
40 }
41
42 void
ena_hw_abs_write32(const ena_t * ena,uint32_t * addr,const uint32_t val)43 ena_hw_abs_write32(const ena_t *ena, uint32_t *addr, const uint32_t val)
44 {
45 VERIFY3P(ena, !=, NULL);
46 VERIFY3P(addr, !=, NULL);
47 VERIFY3U(addr, >=, ena->ena_reg_base);
48 VERIFY3U(addr, <, ena->ena_reg_base + (ena->ena_reg_size - 4));
49
50 ddi_put32(ena->ena_reg_hdl, addr, val);
51 }
52
53 int
enahw_resp_status_to_errno(ena_t * ena,enahw_resp_status_t status)54 enahw_resp_status_to_errno(ena_t *ena, enahw_resp_status_t status)
55 {
56 int ret = 0;
57
58 switch (status) {
59 case ENAHW_RESP_SUCCESS:
60 break;
61
62 case ENAHW_RESP_RESOURCE_ALLOCATION_FAILURE:
63 ret = ENOMEM;
64 break;
65
66 case ENAHW_RESP_UNSUPPORTED_OPCODE:
67 ret = ENOTSUP;
68 break;
69
70 case ENAHW_RESP_BAD_OPCODE:
71 case ENAHW_RESP_MALFORMED_REQUEST:
72 case ENAHW_RESP_ILLEGAL_PARAMETER:
73 ret = EINVAL;
74 break;
75
76 case ENAHW_RESP_RESOURCE_BUSY:
77 ret = EAGAIN;
78 break;
79
80 case ENAHW_RESP_UNKNOWN_ERROR:
81 default:
82 /*
83 * If the device presents us with an "unknown error"
84 * code, or the status code is undefined, then we log
85 * an error and convert it to EIO.
86 */
87 ena_err(ena, "unexpected status code: %d", status);
88 ret = EIO;
89 break;
90 }
91
92 return (ret);
93 }
94
95 const char *
enahw_reset_reason(enahw_reset_reason_t reason)96 enahw_reset_reason(enahw_reset_reason_t reason)
97 {
98 switch (reason) {
99 case ENAHW_RESET_NORMAL:
100 return ("normal");
101 case ENAHW_RESET_KEEP_ALIVE_TO:
102 return ("keep-alive timeout");
103 case ENAHW_RESET_ADMIN_TO:
104 return ("admin timeout");
105 case ENAHW_RESET_MISS_TX_CMPL:
106 return ("missed TX completion");
107 case ENAHW_RESET_INV_RX_REQ_ID:
108 return ("invalid RX request ID");
109 case ENAHW_RESET_INV_TX_REQ_ID:
110 return ("invalid TX request ID");
111 case ENAHW_RESET_TOO_MANY_RX_DESCS:
112 return ("too many RX descs");
113 case ENAHW_RESET_INIT_ERR:
114 return ("initialization error");
115 case ENAHW_RESET_DRIVER_INVALID_STATE:
116 return ("invalid driver state");
117 case ENAHW_RESET_OS_TRIGGER:
118 return ("OS trigger");
119 case ENAHW_RESET_OS_NETDEV_WD:
120 return ("netdev watchdog");
121 case ENAHW_RESET_SHUTDOWN:
122 return ("shutdown");
123 case ENAHW_RESET_USER_TRIGGER:
124 return ("user trigger");
125 case ENAHW_RESET_GENERIC:
126 return ("generic");
127 case ENAHW_RESET_MISS_INTERRUPT:
128 return ("missed interrupt");
129 case ENAHW_RESET_SUSPECTED_POLL_STARVATION:
130 return ("suspected poll starvation");
131 case ENAHW_RESET_RX_DESCRIPTOR_MALFORMED:
132 return ("malformed RX descriptor");
133 case ENAHW_RESET_TX_DESCRIPTOR_MALFORMED:
134 return ("malformed TX descriptor");
135 case ENAHW_RESET_MISSING_ADMIN_INTERRUPT:
136 return ("missing admin interrupt");
137 case ENAHW_RESET_DEVICE_REQUEST:
138 return ("device request");
139 default:
140 return ("unknown");
141 }
142 }
143
144 #ifdef DEBUG
145 static const ena_reg_t reg_cache_template[ENAHW_NUM_REGS] = {
146 {
147 .er_name = "Version",
148 .er_offset = ENAHW_REG_VERSION
149 },
150 {
151 .er_name = "Controller Version",
152 .er_offset = ENAHW_REG_CONTROLLER_VERSION
153 },
154 {
155 .er_name = "Caps",
156 .er_offset = ENAHW_REG_CAPS
157 },
158 {
159 .er_name = "Extended Caps",
160 .er_offset = ENAHW_REG_CAPS_EXT
161 },
162 {
163 .er_name = "Admin SQ Base Low",
164 .er_offset = ENAHW_REG_ASQ_BASE_LO
165 },
166 {
167 .er_name = "Admin SQ Base High",
168 .er_offset = ENAHW_REG_ASQ_BASE_HI
169 },
170 {
171 .er_name = "Admin SQ Caps",
172 .er_offset = ENAHW_REG_ASQ_CAPS
173 },
174 {
175 .er_name = "Gap 0x1C",
176 .er_offset = ENAHW_REG_GAP_1C
177 },
178 {
179 .er_name = "Admin CQ Base Low",
180 .er_offset = ENAHW_REG_ACQ_BASE_LO
181 },
182 {
183 .er_name = "Admin CQ Base High",
184 .er_offset = ENAHW_REG_ACQ_BASE_HI
185 },
186 {
187 .er_name = "Admin CQ Caps",
188 .er_offset = ENAHW_REG_ACQ_CAPS
189 },
190 {
191 .er_name = "Admin SQ Doorbell",
192 .er_offset = ENAHW_REG_ASQ_DB
193 },
194 {
195 .er_name = "Admin CQ Tail",
196 .er_offset = ENAHW_REG_ACQ_TAIL
197 },
198 {
199 .er_name = "Admin Event Notification Queue Caps",
200 .er_offset = ENAHW_REG_AENQ_CAPS
201 },
202 {
203 .er_name = "Admin Event Notification Queue Base Low",
204 .er_offset = ENAHW_REG_AENQ_BASE_LO
205 },
206 {
207 .er_name = "Admin Event Notification Queue Base High",
208 .er_offset = ENAHW_REG_AENQ_BASE_HI
209 },
210 {
211 .er_name = "Admin Event Notification Queue Head Doorbell",
212 .er_offset = ENAHW_REG_AENQ_HEAD_DB
213 },
214 {
215 .er_name = "Admin Event Notification Queue Tail",
216 .er_offset = ENAHW_REG_AENQ_TAIL
217 },
218 {
219 .er_name = "Gap 0x48",
220 .er_offset = ENAHW_REG_GAP_48
221 },
222 {
223 .er_name = "Interrupt Mask (disable interrupts)",
224 .er_offset = ENAHW_REG_INTERRUPT_MASK
225 },
226 {
227 .er_name = "Gap 0x50",
228 .er_offset = ENAHW_REG_GAP_50
229 },
230 {
231 .er_name = "Device Control",
232 .er_offset = ENAHW_REG_DEV_CTL
233 },
234 {
235 .er_name = "Device Status",
236 .er_offset = ENAHW_REG_DEV_STS
237 },
238 {
239 .er_name = "MMIO Register Read",
240 .er_offset = ENAHW_REG_MMIO_REG_READ
241 },
242 {
243 .er_name = "MMIO Response Address Low",
244 .er_offset = ENAHW_REG_MMIO_RESP_LO
245 },
246 {
247 .er_name = "MMIO Response Address High",
248 .er_offset = ENAHW_REG_MMIO_RESP_HI
249 },
250 {
251 .er_name = "RSS Indirection Entry Update",
252 .er_offset = ENAHW_REG_RSS_IND_ENTRY_UPDATE
253 },
254 };
255
256 void
ena_update_regcache(ena_t * ena)257 ena_update_regcache(ena_t *ena)
258 {
259 for (uint_t i = 0; i < ENAHW_NUM_REGS; i++) {
260 ena_reg_t *r = &ena->ena_reg[i];
261
262 r->er_value = ena_hw_bar_read32(ena, r->er_offset);
263 }
264 }
265
266 void
ena_init_regcache(ena_t * ena)267 ena_init_regcache(ena_t *ena)
268 {
269 bcopy(reg_cache_template, ena->ena_reg, sizeof (ena->ena_reg));
270 ena_update_regcache(ena);
271 }
272 #endif /* DEBUG */
273