xref: /illumos-gate/usr/src/uts/common/io/bnx/bnxhwi.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * Copyright 2014-2017 Cavium, Inc.
3  * The contents of this file are subject to the terms of the Common Development
4  * and Distribution License, v.1,  (the "License").
5  *
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the License at available
9  * at http://opensource.org/licenses/CDDL-1.0
10  *
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14 
15 /*
16  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
17  * Copyright (c) 2019, Joyent, Inc.
18  */
19 
20 #include "bnx.h"
21 #include "bnx_mm.h"
22 #include "bnxsnd.h"
23 #include "bnxrcv.h"
24 #include "bnxint.h"
25 #include "bnxtmr.h"
26 #include "bnxcfg.h"
27 
28 void
29 bnx_update_phy(um_device_t * const umdevice)
30 {
31 	lm_status_t lmstatus;
32 	lm_device_t *lmdevice;
33 
34 	lmdevice = &(umdevice->lm_dev);
35 
36 	/* Map 'ndd' parameters to LM struct. */
37 	bnx_cfg_map_phy(umdevice);
38 
39 	mutex_enter(&umdevice->os_param.phy_mutex);
40 
41 	/* Reset, re-program and bring-up phy. */
42 	lmstatus = lm_init_phy(lmdevice, lmdevice->params.req_medium,
43 	    lmdevice->params.flow_ctrl_cap, lmdevice->params.selective_autoneg,
44 	    lmdevice->params.wire_speed, 0);
45 	if (lmstatus != LM_STATUS_SUCCESS) {
46 		cmn_err(CE_WARN, "%s: Failed to configure the PHY.",
47 		    umdevice->dev_name);
48 	}
49 
50 	lm_service_phy_int(lmdevice, TRUE);
51 
52 	mutex_exit(&umdevice->os_param.phy_mutex);
53 }
54 
55 ddi_dma_handle_t *
56 bnx_find_dma_hdl(um_device_t *const umdevice, const void *const virtaddr)
57 {
58 	int i;
59 	ddi_dma_handle_t *dmahdl;
60 
61 	dmahdl = NULL;
62 	for (i = 0; i < umdevice->os_param.dma_handles_used; i++) {
63 		if (umdevice->os_param.dma_virt[i] == virtaddr) {
64 			dmahdl = &(umdevice->os_param.dma_handle[i]);
65 		}
66 	}
67 
68 	return (dmahdl);
69 }
70 
71 static void
72 bnx_free_lmmem(um_device_t * const umdevice)
73 {
74 	int i;
75 	bnx_memreq_t *memreq;
76 	ddi_dma_handle_t *dma_handle;
77 	ddi_acc_handle_t *acc_handle;
78 
79 	if (umdevice->os_param.dma_handles_used != 0) {
80 		i = umdevice->os_param.dma_handles_used - 1;
81 
82 		dma_handle = &(umdevice->os_param.dma_handle[i]);
83 		acc_handle = &(umdevice->os_param.dma_acc_handle[i]);
84 
85 		/* Free all shared memory. */
86 		for (; i >= 0; i--) {
87 			(void) ddi_dma_unbind_handle(*dma_handle);
88 
89 			ddi_dma_mem_free(acc_handle);
90 
91 			ddi_dma_free_handle(dma_handle);
92 
93 			dma_handle--;
94 			acc_handle--;
95 		}
96 
97 		umdevice->os_param.dma_handles_used = 0;
98 	}
99 
100 	if (umdevice->memcnt != 0) {
101 		/* Free all local memory. */
102 		for (i = umdevice->memcnt - 1; i >= 0; i--) {
103 			memreq = &umdevice->memreq[i];
104 
105 			kmem_free(memreq->addr, memreq->size);
106 
107 			memreq->addr = NULL;
108 			memreq->size = 0;
109 		}
110 
111 		umdevice->memcnt = 0;
112 	}
113 }
114 
115 int
116 bnx_hdwr_init(um_device_t *const umdevice)
117 {
118 	lm_status_t lmstatus;
119 	lm_device_t *lmdevice;
120 
121 	lmdevice = &(umdevice->lm_dev);
122 
123 	lmstatus = lm_get_dev_info(lmdevice);
124 	if (lmstatus != LM_STATUS_SUCCESS) {
125 		cmn_err(CE_WARN, "%s: Failed to get device information.\n",
126 		    umdevice->dev_name);
127 		return (-1);
128 	}
129 
130 	/*
131 	 * Initialize the adapter resource.  Mainly allocating memory needed
132 	 * by the driver, such as packet descriptors, shared memory, etc.
133 	 */
134 	lmstatus = lm_init_resc(lmdevice);
135 	if (lmstatus != LM_STATUS_SUCCESS) {
136 		cmn_err(CE_WARN, "%s: Failed to allocate device resources.\n",
137 		    umdevice->dev_name);
138 		goto error1;
139 	}
140 
141 	if (bnx_txpkts_init(umdevice)) {
142 		goto error1;
143 	}
144 
145 	if (bnx_rxpkts_init(umdevice)) {
146 		goto error2;
147 	}
148 
149 	/* Find	the DMA handle associated with the status block memory. */
150 	umdevice->os_param.status_block_dma_hdl = bnx_find_dma_hdl(umdevice,
151 	    (void *)(umdevice->lm_dev.vars.status_virt));
152 
153 	/* Reset the local interrupt event index. */
154 	umdevice->dev_var.processed_status_idx = 0;
155 
156 	/* Initialize the receive mask to a sane default. */
157 	umdevice->dev_var.rx_filter_mask = LM_RX_MASK_ACCEPT_UNICAST |
158 	    LM_RX_MASK_ACCEPT_BROADCAST;
159 
160 	return (0);
161 
162 error2:
163 	bnx_txpkts_fini(umdevice);
164 
165 error1:
166 	bnx_free_lmmem(umdevice);
167 
168 	return (-1);
169 }
170 
171 int
172 bnx_hdwr_acquire(um_device_t *const umdevice)
173 {
174 	lm_status_t lmstatus;
175 	lm_device_t *lmdevice;
176 
177 	lmdevice = &(umdevice->lm_dev);
178 
179 	/* Reset the configuration to the hardware default. */
180 	bnx_cfg_reset(umdevice);
181 
182 	/*
183 	 * A call to lm_reset() implicitly means we are relieving the firmware
184 	 * of it's responsibility to maintain the device.  The driver assumes
185 	 * control.  The LM vars.medium field normally gets set with a call to
186 	 * lm_init_phy(), but this function cannot be called before we assume
187 	 * control of the device.  If we did, we run the risk of contending
188 	 * with the firmware for PHY accesses.  Do the next best thing.
189 	 */
190 	lmdevice->vars.medium = lm_get_medium(lmdevice);
191 
192 	/* Map 'ndd' parameters to LM struct. */
193 	bnx_cfg_map_phy(umdevice);
194 
195 	/* Bring the chip under driver control. */
196 	lmstatus = lm_reset(lmdevice, LM_REASON_DRIVER_RESET);
197 	if (lmstatus != LM_STATUS_SUCCESS) {
198 		cmn_err(CE_WARN, "%s: Failed to reset chip.\n",
199 		    umdevice->dev_name);
200 		return (-1);
201 	}
202 
203 	/* Configure the PHY to the requested settings. */
204 	lmstatus = lm_init_phy(lmdevice, lmdevice->params.req_medium,
205 	    lmdevice->params.flow_ctrl_cap, lmdevice->params.selective_autoneg,
206 	    lmdevice->params.wire_speed, 0);
207 	if (lmstatus != LM_STATUS_SUCCESS) {
208 		cmn_err(CE_WARN, "%s: Failed to initialize the PHY.",
209 		    umdevice->dev_name);
210 	}
211 
212 	lm_service_phy_int(lmdevice, FALSE); /* force a phy status update */
213 
214 	umdevice->dev_var.indLink = lmdevice->vars.link_status;
215 	umdevice->dev_var.indMedium = lmdevice->vars.medium;
216 
217 	/*
218 	 * Need to clear TX PATCH scratch register at offset 0x420
219 	 * to instruct chip to do full TCP checksum calculations.
220 	 */
221 	REG_WR_IND(lmdevice, (OFFSETOF(reg_space_t, tpat.tpat_scratch[0]) +
222 	    0x420), 0);
223 
224 	FLUSHPOSTEDWRITES(lmdevice);
225 
226 	umdevice->recv_discards = 0;
227 
228 	/* Make sure the rx statistics counters are reset. */
229 	bzero(&(lmdevice->rx_info.stats), sizeof (lm_rx_stats_t));
230 
231 	/* Post rx buffers to the chip. */
232 	(void) lm_post_buffers(lmdevice, 0, NULL);
233 
234 	/* Allow the hardware to accept rx traffic. */
235 	(void) lm_set_rx_mask(lmdevice, RX_FILTER_USER_IDX0,
236 	    umdevice->dev_var.rx_filter_mask);
237 
238 	FLUSHPOSTEDWRITES(lmdevice);
239 
240 	/* Enable interrupts. */
241 	bnx_intr_enable(umdevice);
242 
243 	/* Start the periodic timer. */
244 	bnx_timer_start(umdevice);
245 
246 	return (0);
247 }
248 
249 void
250 bnx_hdwr_release(um_device_t *const umdevice)
251 {
252 	int reason;
253 	lm_device_t *lmdevice;
254 
255 	lmdevice = &(umdevice->lm_dev);
256 
257 	/* Stop the periodic timer. */
258 	bnx_timer_stop(umdevice);
259 
260 	/* Disable interrupts. */
261 	bnx_intr_disable(umdevice);
262 
263 	/*
264 	 * In Solaris when RX traffic is accepted, the system might generate
265 	 * and attempt to send some TX packets (from within gld_recv() !).
266 	 * Claiming any TX locks before this point would create a deadlock.
267 	 * The ISR would be waiting for a lock acquired here that would never
268 	 * be freed, since we in-turn would be waiting for the ISR to finish
269 	 * here.  Consequently, we acquire the TX lock as soon as we know that
270 	 * no TX traffic is a result of RX traffic.
271 	 */
272 	rw_enter(&umdevice->os_param.gld_snd_mutex, RW_WRITER);
273 
274 	/* Set RX mask to stop receiving any further packets */
275 	(void) lm_set_rx_mask(lmdevice, RX_FILTER_USER_IDX0,
276 	    LM_RX_MASK_ACCEPT_NONE);
277 
278 	FLUSHPOSTEDWRITES(lmdevice);
279 
280 	if (umdevice->dev_var.fw_ver < FW_VER_WITH_UNLOAD_POWER_DOWN) {
281 		reason = LM_REASON_DRIVER_SHUTDOWN;
282 	} else {
283 		reason = LM_REASON_DRIVER_UNLOAD_POWER_DOWN;
284 	}
285 
286 	lm_chip_reset(lmdevice, reason);
287 
288 	FLUSHPOSTEDWRITES(lmdevice);
289 
290 	/* Reclaim all tx buffers submitted to the hardware. */
291 	bnx_txpkts_flush(umdevice);
292 
293 	/* Reclaim all rx buffers submitted to the hardware. */
294 	bnx_rxpkts_recycle(umdevice);
295 
296 	rw_exit(&umdevice->os_param.gld_snd_mutex);
297 }
298 
299 void
300 bnx_hdwr_fini(um_device_t *const umdevice)
301 {
302 	bnx_rxpkts_fini(umdevice);
303 
304 	bnx_txpkts_fini(umdevice);
305 
306 	bnx_free_lmmem(umdevice);
307 }
308 
309 static u32_t
310 compute_crc32(const u8_t *const buf, u32_t buf_size)
311 {
312 	u32_t reg;
313 	u32_t tmp;
314 	u32_t j;
315 	u32_t k;
316 
317 	reg = 0xffffffff;
318 
319 	for (j = 0; j < buf_size; j++) {
320 		reg ^= buf[j];
321 
322 		for (k = 0; k < 8; k++) {
323 			tmp = reg & 0x01;
324 
325 			reg >>= 1;
326 
327 			if (tmp) {
328 				reg ^= 0xedb88320;
329 			}
330 		}
331 	}
332 
333 	return (~reg);
334 }
335 
336 int
337 bnx_find_mchash_collision(lm_mc_table_t *mc_table, const uint8_t *const mc_addr)
338 {
339 	u32_t cur_bit_pos;
340 	u32_t tgt_bit_pos;
341 	u32_t idx;
342 	u32_t crc32;
343 
344 	crc32 = compute_crc32(mc_addr, ETHERNET_ADDRESS_SIZE);
345 
346 	tgt_bit_pos = ~crc32 & 0xff;
347 
348 	for (idx = 0; idx < mc_table->entry_cnt; idx++) {
349 		crc32 = compute_crc32(mc_table->addr_arr[idx].mc_addr,
350 		    ETHERNET_ADDRESS_SIZE);
351 
352 		/*
353 		 * The most significant 7 bits of the CRC32 (no inversion),
354 		 * are used to index into one of the possible 128 bit positions.
355 		 */
356 		cur_bit_pos = ~crc32 & 0xff;
357 
358 		if (tgt_bit_pos == cur_bit_pos) {
359 			return (idx);
360 		}
361 	}
362 
363 	return (-1);
364 }
365 
366 
367 
368 /*
369  * Name:	um_send_driver_pulse
370  *
371  * Input:       ptr to driver structure
372  *
373  * Return:      none
374  *
375  * Description: um_send_driver_pulse routine sends heartbeat pulse to firmware.
376  */
377 void
378 um_send_driver_pulse(um_device_t *const umdevice)
379 {
380 	u32_t msg_code;
381 	u32_t offset;
382 	lm_device_t *lmdevice;
383 
384 	lmdevice = &(umdevice->lm_dev);
385 
386 	offset = lmdevice->hw_info.shmem_base;
387 	offset += OFFSETOF(shmem_region_t, drv_fw_mb.drv_pulse_mb);
388 
389 	mutex_enter(&umdevice->os_param.ind_mutex);
390 
391 	lmdevice->vars.drv_pulse_wr_seq++;
392 
393 	msg_code = lmdevice->vars.drv_pulse_wr_seq & DRV_PULSE_SEQ_MASK;
394 
395 	mutex_exit(&umdevice->os_param.ind_mutex);
396 
397 	if (lmdevice->params.test_mode & TEST_MODE_DRIVER_PULSE_ALWAYS_ALIVE) {
398 		msg_code |= DRV_PULSE_ALWAYS_ALIVE;
399 	}
400 
401 	REG_WR_IND(lmdevice, offset, msg_code);
402 }
403