xref: /onnv-gate/usr/src/uts/common/io/igb/igb_manage.c (revision 5779:e875a8701bfc)
1*5779Sxy150489 /*
2*5779Sxy150489  * CDDL HEADER START
3*5779Sxy150489  *
4*5779Sxy150489  * Copyright(c) 2007-2008 Intel Corporation. All rights reserved.
5*5779Sxy150489  * The contents of this file are subject to the terms of the
6*5779Sxy150489  * Common Development and Distribution License (the "License").
7*5779Sxy150489  * You may not use this file except in compliance with the License.
8*5779Sxy150489  *
9*5779Sxy150489  * You can obtain a copy of the license at:
10*5779Sxy150489  *	http://www.opensolaris.org/os/licensing.
11*5779Sxy150489  * See the License for the specific language governing permissions
12*5779Sxy150489  * and limitations under the License.
13*5779Sxy150489  *
14*5779Sxy150489  * When using or redistributing this file, you may do so under the
15*5779Sxy150489  * License only. No other modification of this header is permitted.
16*5779Sxy150489  *
17*5779Sxy150489  * If applicable, add the following below this CDDL HEADER, with the
18*5779Sxy150489  * fields enclosed by brackets "[]" replaced with your own identifying
19*5779Sxy150489  * information: Portions Copyright [yyyy] [name of copyright owner]
20*5779Sxy150489  *
21*5779Sxy150489  * CDDL HEADER END
22*5779Sxy150489  */
23*5779Sxy150489 
24*5779Sxy150489 /*
25*5779Sxy150489  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
26*5779Sxy150489  * Use is subject to license terms of the CDDL.
27*5779Sxy150489  */
28*5779Sxy150489 
29*5779Sxy150489 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30*5779Sxy150489 
31*5779Sxy150489 #include "igb_api.h"
32*5779Sxy150489 #include "igb_manage.h"
33*5779Sxy150489 
34*5779Sxy150489 static u8 e1000_calculate_checksum(u8 *buffer, u32 length);
35*5779Sxy150489 
36*5779Sxy150489 /*
37*5779Sxy150489  * e1000_calculate_checksum - Calculate checksum for buffer
38*5779Sxy150489  * @buffer: pointer to EEPROM
39*5779Sxy150489  * @length: size of EEPROM to calculate a checksum for
40*5779Sxy150489  *
41*5779Sxy150489  * Calculates the checksum for some buffer on a specified length.  The
42*5779Sxy150489  * checksum calculated is returned.
43*5779Sxy150489  */
44*5779Sxy150489 static u8 e1000_calculate_checksum(u8 *buffer, u32 length)
45*5779Sxy150489 {
46*5779Sxy150489 	u32 i;
47*5779Sxy150489 	u8  sum = 0;
48*5779Sxy150489 
49*5779Sxy150489 	DEBUGFUNC("e1000_calculate_checksum");
50*5779Sxy150489 
51*5779Sxy150489 	if (!buffer)
52*5779Sxy150489 		return (0);
53*5779Sxy150489 
54*5779Sxy150489 	for (i = 0; i < length; i++)
55*5779Sxy150489 		sum += buffer[i];
56*5779Sxy150489 
57*5779Sxy150489 	return (u8) (0 - sum);
58*5779Sxy150489 }
59*5779Sxy150489 
60*5779Sxy150489 /*
61*5779Sxy150489  * e1000_mng_enable_host_if_generic - Checks host interface is enabled
62*5779Sxy150489  * @hw: pointer to the HW structure
63*5779Sxy150489  *
64*5779Sxy150489  * Returns E1000_success upon success, else E1000_ERR_HOST_INTERFACE_COMMAND
65*5779Sxy150489  *
66*5779Sxy150489  * This function checks whether the HOST IF is enabled for command operaton
67*5779Sxy150489  * and also checks whether the previous command is completed.  It busy waits
68*5779Sxy150489  * in case of previous command is not completed.
69*5779Sxy150489  */
70*5779Sxy150489 s32
71*5779Sxy150489 e1000_mng_enable_host_if_generic(struct e1000_hw *hw)
72*5779Sxy150489 {
73*5779Sxy150489 	u32 hicr;
74*5779Sxy150489 	s32 ret_val = E1000_SUCCESS;
75*5779Sxy150489 	u8  i;
76*5779Sxy150489 
77*5779Sxy150489 	DEBUGFUNC("e1000_mng_enable_host_if_generic");
78*5779Sxy150489 
79*5779Sxy150489 	/* Check that the host interface is enabled. */
80*5779Sxy150489 	hicr = E1000_READ_REG(hw, E1000_HICR);
81*5779Sxy150489 	if ((hicr & E1000_HICR_EN) == 0) {
82*5779Sxy150489 		DEBUGOUT("E1000_HOST_EN bit disabled.\n");
83*5779Sxy150489 		ret_val = -E1000_ERR_HOST_INTERFACE_COMMAND;
84*5779Sxy150489 		goto out;
85*5779Sxy150489 	}
86*5779Sxy150489 	/* check the previous command is completed */
87*5779Sxy150489 	for (i = 0; i < E1000_MNG_DHCP_COMMAND_TIMEOUT; i++) {
88*5779Sxy150489 		hicr = E1000_READ_REG(hw, E1000_HICR);
89*5779Sxy150489 		if (!(hicr & E1000_HICR_C))
90*5779Sxy150489 			break;
91*5779Sxy150489 		msec_delay_irq(1);
92*5779Sxy150489 	}
93*5779Sxy150489 
94*5779Sxy150489 	if (i == E1000_MNG_DHCP_COMMAND_TIMEOUT) {
95*5779Sxy150489 		DEBUGOUT("Previous command timeout failed .\n");
96*5779Sxy150489 		ret_val = -E1000_ERR_HOST_INTERFACE_COMMAND;
97*5779Sxy150489 		goto out;
98*5779Sxy150489 	}
99*5779Sxy150489 
100*5779Sxy150489 out:
101*5779Sxy150489 	return (ret_val);
102*5779Sxy150489 }
103*5779Sxy150489 
104*5779Sxy150489 /*
105*5779Sxy150489  * e1000_check_mng_mode_generic - Generic check managament mode
106*5779Sxy150489  * @hw: pointer to the HW structure
107*5779Sxy150489  *
108*5779Sxy150489  * Reads the firmware semaphore register and returns true (>0) if
109*5779Sxy150489  * manageability is enabled, else false (0).
110*5779Sxy150489  */
111*5779Sxy150489 bool
112*5779Sxy150489 e1000_check_mng_mode_generic(struct e1000_hw *hw)
113*5779Sxy150489 {
114*5779Sxy150489 	u32 fwsm;
115*5779Sxy150489 
116*5779Sxy150489 	DEBUGFUNC("e1000_check_mng_mode_generic");
117*5779Sxy150489 
118*5779Sxy150489 	fwsm = E1000_READ_REG(hw, E1000_FWSM);
119*5779Sxy150489 
120*5779Sxy150489 	return ((fwsm & E1000_FWSM_MODE_MASK) ==
121*5779Sxy150489 	    (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT));
122*5779Sxy150489 }
123*5779Sxy150489 
124*5779Sxy150489 /*
125*5779Sxy150489  * e1000_enable_tx_pkt_filtering_generic - Enable packet filtering on TX
126*5779Sxy150489  * @hw: pointer to the HW structure
127*5779Sxy150489  *
128*5779Sxy150489  * Enables packet filtering on transmit packets if manageability is enabled
129*5779Sxy150489  * and host interface is enabled.
130*5779Sxy150489  */
131*5779Sxy150489 bool
132*5779Sxy150489 e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw)
133*5779Sxy150489 {
134*5779Sxy150489 	struct e1000_host_mng_dhcp_cookie *hdr = &hw->mng_cookie;
135*5779Sxy150489 	u32 *buffer = (u32 *)&hw->mng_cookie;
136*5779Sxy150489 	u32 offset;
137*5779Sxy150489 	s32 ret_val, hdr_csum, csum;
138*5779Sxy150489 	u8 i, len;
139*5779Sxy150489 	bool tx_filter = TRUE;
140*5779Sxy150489 
141*5779Sxy150489 	DEBUGFUNC("e1000_enable_tx_pkt_filtering_generic");
142*5779Sxy150489 
143*5779Sxy150489 	/* No manageability, no filtering */
144*5779Sxy150489 	if (!e1000_check_mng_mode(hw)) {
145*5779Sxy150489 		tx_filter = FALSE;
146*5779Sxy150489 		goto out;
147*5779Sxy150489 	}
148*5779Sxy150489 
149*5779Sxy150489 	/*
150*5779Sxy150489 	 * If we can't read from the host interface for whatever
151*5779Sxy150489 	 * reason, disable filtering.
152*5779Sxy150489 	 */
153*5779Sxy150489 	ret_val = e1000_mng_enable_host_if(hw);
154*5779Sxy150489 	if (ret_val != E1000_SUCCESS) {
155*5779Sxy150489 		tx_filter = FALSE;
156*5779Sxy150489 		goto out;
157*5779Sxy150489 	}
158*5779Sxy150489 
159*5779Sxy150489 	/* Read in the header.  Length and offset are in dwords. */
160*5779Sxy150489 	len    = E1000_MNG_DHCP_COOKIE_LENGTH >> 2;
161*5779Sxy150489 	offset = E1000_MNG_DHCP_COOKIE_OFFSET >> 2;
162*5779Sxy150489 	for (i = 0; i < len; i++) {
163*5779Sxy150489 		*(buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw,
164*5779Sxy150489 		    E1000_HOST_IF, offset + i);
165*5779Sxy150489 	}
166*5779Sxy150489 	hdr_csum = hdr->checksum;
167*5779Sxy150489 	hdr->checksum = 0;
168*5779Sxy150489 	csum = e1000_calculate_checksum((u8 *)hdr,
169*5779Sxy150489 	    E1000_MNG_DHCP_COOKIE_LENGTH);
170*5779Sxy150489 	/*
171*5779Sxy150489 	 * If either the checksums or signature don't match, then
172*5779Sxy150489 	 * the cookie area isn't considered valid, in which case we
173*5779Sxy150489 	 * take the safe route of assuming Tx filtering is enabled.
174*5779Sxy150489 	 */
175*5779Sxy150489 	if (hdr_csum != csum)
176*5779Sxy150489 		goto out;
177*5779Sxy150489 	if (hdr->signature != E1000_IAMT_SIGNATURE)
178*5779Sxy150489 		goto out;
179*5779Sxy150489 
180*5779Sxy150489 	/* Cookie area is valid, make the final check for filtering. */
181*5779Sxy150489 	if (!(hdr->status & E1000_MNG_DHCP_COOKIE_STATUS_PARSING))
182*5779Sxy150489 		tx_filter = FALSE;
183*5779Sxy150489 
184*5779Sxy150489 out:
185*5779Sxy150489 	hw->mac.tx_pkt_filtering = tx_filter;
186*5779Sxy150489 	return (tx_filter);
187*5779Sxy150489 }
188*5779Sxy150489 
189*5779Sxy150489 /*
190*5779Sxy150489  * e1000_mng_write_dhcp_info_generic - Writes DHCP info to host interface
191*5779Sxy150489  * @hw: pointer to the HW structure
192*5779Sxy150489  * @buffer: pointer to the host interface
193*5779Sxy150489  * @length: size of the buffer
194*5779Sxy150489  *
195*5779Sxy150489  * Writes the DHCP information to the host interface.
196*5779Sxy150489  */
197*5779Sxy150489 s32
198*5779Sxy150489 e1000_mng_write_dhcp_info_generic(struct e1000_hw *hw, u8 *buffer,
199*5779Sxy150489     u16 length)
200*5779Sxy150489 {
201*5779Sxy150489 	struct e1000_host_mng_command_header hdr;
202*5779Sxy150489 	s32 ret_val;
203*5779Sxy150489 	u32 hicr;
204*5779Sxy150489 
205*5779Sxy150489 	DEBUGFUNC("e1000_mng_write_dhcp_info_generic");
206*5779Sxy150489 
207*5779Sxy150489 	hdr.command_id = E1000_MNG_DHCP_TX_PAYLOAD_CMD;
208*5779Sxy150489 	hdr.command_length = length;
209*5779Sxy150489 	hdr.reserved1 = 0;
210*5779Sxy150489 	hdr.reserved2 = 0;
211*5779Sxy150489 	hdr.checksum = 0;
212*5779Sxy150489 
213*5779Sxy150489 	/* Enable the host interface */
214*5779Sxy150489 	ret_val = e1000_mng_enable_host_if(hw);
215*5779Sxy150489 	if (ret_val)
216*5779Sxy150489 		goto out;
217*5779Sxy150489 
218*5779Sxy150489 	/* Populate the host interface with the contents of "buffer". */
219*5779Sxy150489 	ret_val = e1000_mng_host_if_write(hw, buffer, length,
220*5779Sxy150489 	    sizeof (hdr), &(hdr.checksum));
221*5779Sxy150489 	if (ret_val)
222*5779Sxy150489 		goto out;
223*5779Sxy150489 
224*5779Sxy150489 	/* Write the manageability command header */
225*5779Sxy150489 	ret_val = e1000_mng_write_cmd_header(hw, &hdr);
226*5779Sxy150489 	if (ret_val)
227*5779Sxy150489 		goto out;
228*5779Sxy150489 
229*5779Sxy150489 	/* Tell the ARC a new command is pending. */
230*5779Sxy150489 	hicr = E1000_READ_REG(hw, E1000_HICR);
231*5779Sxy150489 	E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C);
232*5779Sxy150489 
233*5779Sxy150489 out:
234*5779Sxy150489 	return (ret_val);
235*5779Sxy150489 }
236*5779Sxy150489 
237*5779Sxy150489 /*
238*5779Sxy150489  * e1000_mng_write_cmd_header_generic - Writes manageability command header
239*5779Sxy150489  * @hw: pointer to the HW structure
240*5779Sxy150489  * @hdr: pointer to the host interface command header
241*5779Sxy150489  *
242*5779Sxy150489  * Writes the command header after does the checksum calculation.
243*5779Sxy150489  */
244*5779Sxy150489 s32
245*5779Sxy150489 e1000_mng_write_cmd_header_generic(struct e1000_hw *hw,
246*5779Sxy150489     struct e1000_host_mng_command_header *hdr)
247*5779Sxy150489 {
248*5779Sxy150489 	u16 i, length = sizeof (struct e1000_host_mng_command_header);
249*5779Sxy150489 
250*5779Sxy150489 	DEBUGFUNC("e1000_mng_write_cmd_header_generic");
251*5779Sxy150489 
252*5779Sxy150489 	/* Write the whole command header structure with new checksum. */
253*5779Sxy150489 
254*5779Sxy150489 	hdr->checksum = e1000_calculate_checksum((u8 *)hdr, length);
255*5779Sxy150489 
256*5779Sxy150489 	length >>= 2;
257*5779Sxy150489 	/* Write the relevant command block into the ram area. */
258*5779Sxy150489 	for (i = 0; i < length; i++) {
259*5779Sxy150489 		E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, i,
260*5779Sxy150489 		    *((u32 *)(uintptr_t)hdr + i));
261*5779Sxy150489 		E1000_WRITE_FLUSH(hw);
262*5779Sxy150489 	}
263*5779Sxy150489 
264*5779Sxy150489 	return (E1000_SUCCESS);
265*5779Sxy150489 }
266*5779Sxy150489 
267*5779Sxy150489 /*
268*5779Sxy150489  * e1000_mng_host_if_write_generic - Write to the manageability host interface
269*5779Sxy150489  * @hw: pointer to the HW structure
270*5779Sxy150489  * @buffer: pointer to the host interface buffer
271*5779Sxy150489  * @length: size of the buffer
272*5779Sxy150489  * @offset: location in the buffer to write to
273*5779Sxy150489  * @sum: sum of the data (not checksum)
274*5779Sxy150489  *
275*5779Sxy150489  * This function writes the buffer content at the offset given on the host if.
276*5779Sxy150489  * It also does alignment considerations to do the writes in most efficient
277*5779Sxy150489  * way.  Also fills up the sum of the buffer in *buffer parameter.
278*5779Sxy150489  */
279*5779Sxy150489 s32
280*5779Sxy150489 e1000_mng_host_if_write_generic(struct e1000_hw *hw, u8 *buffer,
281*5779Sxy150489     u16 length, u16 offset, u8 *sum)
282*5779Sxy150489 {
283*5779Sxy150489 	u8 *tmp;
284*5779Sxy150489 	u8 *bufptr = buffer;
285*5779Sxy150489 	u32 data = 0;
286*5779Sxy150489 	s32 ret_val = E1000_SUCCESS;
287*5779Sxy150489 	u16 remaining, i, j, prev_bytes;
288*5779Sxy150489 
289*5779Sxy150489 	DEBUGFUNC("e1000_mng_host_if_write_generic");
290*5779Sxy150489 
291*5779Sxy150489 	/* sum = only sum of the data and it is not checksum */
292*5779Sxy150489 
293*5779Sxy150489 	if (length == 0 || offset + length > E1000_HI_MAX_MNG_DATA_LENGTH) {
294*5779Sxy150489 		ret_val = -E1000_ERR_PARAM;
295*5779Sxy150489 		goto out;
296*5779Sxy150489 	}
297*5779Sxy150489 
298*5779Sxy150489 	tmp = (u8 *)&data;
299*5779Sxy150489 	prev_bytes = offset & 0x3;
300*5779Sxy150489 	offset >>= 2;
301*5779Sxy150489 
302*5779Sxy150489 	if (prev_bytes) {
303*5779Sxy150489 		data = E1000_READ_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset);
304*5779Sxy150489 		for (j = prev_bytes; j < sizeof (u32); j++) {
305*5779Sxy150489 			*(tmp + j) = *bufptr++;
306*5779Sxy150489 			*sum += *(tmp + j);
307*5779Sxy150489 		}
308*5779Sxy150489 		E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset, data);
309*5779Sxy150489 		length -= j - prev_bytes;
310*5779Sxy150489 		offset++;
311*5779Sxy150489 	}
312*5779Sxy150489 
313*5779Sxy150489 	remaining = length & 0x3;
314*5779Sxy150489 	length -= remaining;
315*5779Sxy150489 
316*5779Sxy150489 	/* Calculate length in DWORDs */
317*5779Sxy150489 	length >>= 2;
318*5779Sxy150489 
319*5779Sxy150489 	/*
320*5779Sxy150489 	 * The device driver writes the relevant command block into the
321*5779Sxy150489 	 * ram area.
322*5779Sxy150489 	 */
323*5779Sxy150489 	for (i = 0; i < length; i++) {
324*5779Sxy150489 		for (j = 0; j < sizeof (u32); j++) {
325*5779Sxy150489 			*(tmp + j) = *bufptr++;
326*5779Sxy150489 			*sum += *(tmp + j);
327*5779Sxy150489 		}
328*5779Sxy150489 
329*5779Sxy150489 		E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF,
330*5779Sxy150489 		    offset + i, data);
331*5779Sxy150489 	}
332*5779Sxy150489 	if (remaining) {
333*5779Sxy150489 		for (j = 0; j < sizeof (u32); j++) {
334*5779Sxy150489 			if (j < remaining)
335*5779Sxy150489 				*(tmp + j) = *bufptr++;
336*5779Sxy150489 			else
337*5779Sxy150489 				*(tmp + j) = 0;
338*5779Sxy150489 
339*5779Sxy150489 			*sum += *(tmp + j);
340*5779Sxy150489 		}
341*5779Sxy150489 		E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF,
342*5779Sxy150489 		    offset + i, data);
343*5779Sxy150489 	}
344*5779Sxy150489 
345*5779Sxy150489 out:
346*5779Sxy150489 	return (ret_val);
347*5779Sxy150489 }
348*5779Sxy150489 
349*5779Sxy150489 /*
350*5779Sxy150489  * e1000_enable_mng_pass_thru - Enable processing of ARP's
351*5779Sxy150489  * @hw: pointer to the HW structure
352*5779Sxy150489  *
353*5779Sxy150489  * Verifies the hardware needs to allow ARPs to be processed by the host.
354*5779Sxy150489  */
355*5779Sxy150489 bool
356*5779Sxy150489 e1000_enable_mng_pass_thru(struct e1000_hw *hw)
357*5779Sxy150489 {
358*5779Sxy150489 	u32 manc;
359*5779Sxy150489 	u32 fwsm, factps;
360*5779Sxy150489 	bool ret_val = FALSE;
361*5779Sxy150489 
362*5779Sxy150489 	DEBUGFUNC("e1000_enable_mng_pass_thru");
363*5779Sxy150489 
364*5779Sxy150489 	if (!hw->mac.asf_firmware_present)
365*5779Sxy150489 		goto out;
366*5779Sxy150489 
367*5779Sxy150489 	manc = E1000_READ_REG(hw, E1000_MANC);
368*5779Sxy150489 
369*5779Sxy150489 	if (!(manc & E1000_MANC_RCV_TCO_EN) ||
370*5779Sxy150489 	    !(manc & E1000_MANC_EN_MAC_ADDR_FILTER))
371*5779Sxy150489 		goto out;
372*5779Sxy150489 
373*5779Sxy150489 	if (hw->mac.arc_subsystem_valid) {
374*5779Sxy150489 		fwsm = E1000_READ_REG(hw, E1000_FWSM);
375*5779Sxy150489 		factps = E1000_READ_REG(hw, E1000_FACTPS);
376*5779Sxy150489 
377*5779Sxy150489 		if (!(factps & E1000_FACTPS_MNGCG) &&
378*5779Sxy150489 		    ((fwsm & E1000_FWSM_MODE_MASK) ==
379*5779Sxy150489 		    (e1000_mng_mode_pt << E1000_FWSM_MODE_SHIFT))) {
380*5779Sxy150489 			ret_val = TRUE;
381*5779Sxy150489 			goto out;
382*5779Sxy150489 		}
383*5779Sxy150489 	} else {
384*5779Sxy150489 		if ((manc & E1000_MANC_SMBUS_EN) &&
385*5779Sxy150489 		    !(manc & E1000_MANC_ASF_EN)) {
386*5779Sxy150489 			ret_val = TRUE;
387*5779Sxy150489 			goto out;
388*5779Sxy150489 		}
389*5779Sxy150489 	}
390*5779Sxy150489 
391*5779Sxy150489 out:
392*5779Sxy150489 	return (ret_val);
393*5779Sxy150489 }
394