xref: /freebsd/sys/dev/smartpqi/smartpqi_features.c (revision 7ea28254ec5376b5deb86c136e1838d0134dbb22)
1*7ea28254SJohn Hall /*-
2*7ea28254SJohn Hall  * Copyright 2016-2023 Microchip Technology, Inc. and/or its subsidiaries.
3*7ea28254SJohn Hall  *
4*7ea28254SJohn Hall  * Redistribution and use in source and binary forms, with or without
5*7ea28254SJohn Hall  * modification, are permitted provided that the following conditions
6*7ea28254SJohn Hall  * are met:
7*7ea28254SJohn Hall  * 1. Redistributions of source code must retain the above copyright
8*7ea28254SJohn Hall  *    notice, this list of conditions and the following disclaimer.
9*7ea28254SJohn Hall  * 2. Redistributions in binary form must reproduce the above copyright
10*7ea28254SJohn Hall  *    notice, this list of conditions and the following disclaimer in the
11*7ea28254SJohn Hall  *    documentation and/or other materials provided with the distribution.
12*7ea28254SJohn Hall  *
13*7ea28254SJohn Hall  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14*7ea28254SJohn Hall  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15*7ea28254SJohn Hall  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16*7ea28254SJohn Hall  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17*7ea28254SJohn Hall  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18*7ea28254SJohn Hall  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19*7ea28254SJohn Hall  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20*7ea28254SJohn Hall  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21*7ea28254SJohn Hall  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22*7ea28254SJohn Hall  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23*7ea28254SJohn Hall  * SUCH DAMAGE.
24*7ea28254SJohn Hall  */
25*7ea28254SJohn Hall 
26*7ea28254SJohn Hall 
27*7ea28254SJohn Hall #include "smartpqi_includes.h"
28*7ea28254SJohn Hall 
29*7ea28254SJohn Hall /*
30*7ea28254SJohn Hall  * Checks a firmware feature status, given bit position.
31*7ea28254SJohn Hall  */
32*7ea28254SJohn Hall static inline boolean_t
pqi_is_firmware_feature_supported(struct pqi_config_table_firmware_features * firmware_features,unsigned int bit_position)33*7ea28254SJohn Hall pqi_is_firmware_feature_supported(
34*7ea28254SJohn Hall 	struct pqi_config_table_firmware_features *firmware_features,
35*7ea28254SJohn Hall 	unsigned int bit_position)
36*7ea28254SJohn Hall {
37*7ea28254SJohn Hall 	unsigned int byte_index;
38*7ea28254SJohn Hall 
39*7ea28254SJohn Hall 	byte_index = bit_position / BITS_PER_BYTE;
40*7ea28254SJohn Hall 
41*7ea28254SJohn Hall 	if (byte_index >= firmware_features->num_elements) {
42*7ea28254SJohn Hall 		DBG_ERR_NO_SOFTS("Invalid byte index for bit position %u\n",
43*7ea28254SJohn Hall 			bit_position);
44*7ea28254SJohn Hall 		return false;
45*7ea28254SJohn Hall 	}
46*7ea28254SJohn Hall 
47*7ea28254SJohn Hall 	return (firmware_features->features_supported[byte_index] &
48*7ea28254SJohn Hall 		(1 << (bit_position % BITS_PER_BYTE))) ? true : false;
49*7ea28254SJohn Hall }
50*7ea28254SJohn Hall 
51*7ea28254SJohn Hall /*
52*7ea28254SJohn Hall  * Counts down into the enabled section of firmware
53*7ea28254SJohn Hall  * features and reports current enabled status, given
54*7ea28254SJohn Hall  * bit position.
55*7ea28254SJohn Hall  */
56*7ea28254SJohn Hall static inline boolean_t
pqi_is_firmware_feature_enabled(struct pqi_config_table_firmware_features * firmware_features,uint8_t * firmware_features_iomem_addr,unsigned int bit_position)57*7ea28254SJohn Hall pqi_is_firmware_feature_enabled(
58*7ea28254SJohn Hall 	struct pqi_config_table_firmware_features *firmware_features,
59*7ea28254SJohn Hall 	uint8_t *firmware_features_iomem_addr,
60*7ea28254SJohn Hall 	unsigned int bit_position)
61*7ea28254SJohn Hall {
62*7ea28254SJohn Hall 	unsigned int byte_index;
63*7ea28254SJohn Hall 	uint8_t *features_enabled_iomem_addr;
64*7ea28254SJohn Hall 
65*7ea28254SJohn Hall 	byte_index = (bit_position / BITS_PER_BYTE) +
66*7ea28254SJohn Hall 		(firmware_features->num_elements * 2);
67*7ea28254SJohn Hall 
68*7ea28254SJohn Hall 	features_enabled_iomem_addr = firmware_features_iomem_addr +
69*7ea28254SJohn Hall 		offsetof(struct pqi_config_table_firmware_features,
70*7ea28254SJohn Hall 			features_supported) + byte_index;
71*7ea28254SJohn Hall 
72*7ea28254SJohn Hall 	return (*features_enabled_iomem_addr &
73*7ea28254SJohn Hall 		(1 << (bit_position % BITS_PER_BYTE))) ? true : false;
74*7ea28254SJohn Hall }
75*7ea28254SJohn Hall 
76*7ea28254SJohn Hall /*
77*7ea28254SJohn Hall  * Sets the given bit position for the driver to request the indicated
78*7ea28254SJohn Hall  * firmware feature be enabled.
79*7ea28254SJohn Hall  */
80*7ea28254SJohn Hall static inline void
pqi_request_firmware_feature(struct pqi_config_table_firmware_features * firmware_features,unsigned int bit_position)81*7ea28254SJohn Hall pqi_request_firmware_feature(
82*7ea28254SJohn Hall 	struct pqi_config_table_firmware_features *firmware_features,
83*7ea28254SJohn Hall 	unsigned int bit_position)
84*7ea28254SJohn Hall {
85*7ea28254SJohn Hall 	unsigned int byte_index;
86*7ea28254SJohn Hall 
87*7ea28254SJohn Hall 	/* byte_index adjusted to index into requested start bits */
88*7ea28254SJohn Hall 	byte_index = (bit_position / BITS_PER_BYTE) +
89*7ea28254SJohn Hall 		firmware_features->num_elements;
90*7ea28254SJohn Hall 
91*7ea28254SJohn Hall 	/* setting requested bits of local firmware_features */
92*7ea28254SJohn Hall 	firmware_features->features_supported[byte_index] |=
93*7ea28254SJohn Hall 		(1 << (bit_position % BITS_PER_BYTE));
94*7ea28254SJohn Hall }
95*7ea28254SJohn Hall 
96*7ea28254SJohn Hall /*
97*7ea28254SJohn Hall  * Creates and sends the request for firmware to update the config
98*7ea28254SJohn Hall  * table.
99*7ea28254SJohn Hall  */
100*7ea28254SJohn Hall static int
pqi_config_table_update(pqisrc_softstate_t * softs,uint16_t first_section,uint16_t last_section)101*7ea28254SJohn Hall pqi_config_table_update(pqisrc_softstate_t *softs,
102*7ea28254SJohn Hall 	uint16_t first_section, uint16_t last_section)
103*7ea28254SJohn Hall {
104*7ea28254SJohn Hall 	struct pqi_vendor_general_request request;
105*7ea28254SJohn Hall 	int ret;
106*7ea28254SJohn Hall 
107*7ea28254SJohn Hall 	memset(&request, 0, sizeof(request));
108*7ea28254SJohn Hall 
109*7ea28254SJohn Hall 	request.header.iu_type = PQI_REQUEST_IU_VENDOR_GENERAL;
110*7ea28254SJohn Hall 	request.header.iu_length = sizeof(request) - PQI_REQUEST_HEADER_LENGTH;
111*7ea28254SJohn Hall 	request.function_code = PQI_VENDOR_GENERAL_CONFIG_TABLE_UPDATE;
112*7ea28254SJohn Hall 	request.data.config_table_update.first_section = first_section;
113*7ea28254SJohn Hall 	request.data.config_table_update.last_section = last_section;
114*7ea28254SJohn Hall 
115*7ea28254SJohn Hall 	ret = pqisrc_build_send_vendor_request(softs, &request);
116*7ea28254SJohn Hall 
117*7ea28254SJohn Hall 	if (ret != PQI_STATUS_SUCCESS) {
118*7ea28254SJohn Hall 		DBG_ERR("Failed to submit vendor general request IU, Ret status: %d\n", ret);
119*7ea28254SJohn Hall 	}
120*7ea28254SJohn Hall 
121*7ea28254SJohn Hall 	return ret;
122*7ea28254SJohn Hall }
123*7ea28254SJohn Hall 
124*7ea28254SJohn Hall /*
125*7ea28254SJohn Hall  * Copies requested features bits into firmware config table,
126*7ea28254SJohn Hall  * checks for support, and returns status of updating the config table.
127*7ea28254SJohn Hall  */
128*7ea28254SJohn Hall static int
pqi_enable_firmware_features(pqisrc_softstate_t * softs,struct pqi_config_table_firmware_features * firmware_features,uint8_t * firmware_features_abs_addr)129*7ea28254SJohn Hall pqi_enable_firmware_features(pqisrc_softstate_t *softs,
130*7ea28254SJohn Hall 	struct pqi_config_table_firmware_features *firmware_features,
131*7ea28254SJohn Hall 	uint8_t *firmware_features_abs_addr)
132*7ea28254SJohn Hall {
133*7ea28254SJohn Hall 	uint8_t *features_requested;
134*7ea28254SJohn Hall 	uint8_t *features_requested_abs_addr;
135*7ea28254SJohn Hall 	uint16_t *host_max_known_feature_iomem_addr;
136*7ea28254SJohn Hall 	uint16_t pqi_max_feature = PQI_FIRMWARE_FEATURE_MAXIMUM;
137*7ea28254SJohn Hall 
138*7ea28254SJohn Hall 	features_requested = firmware_features->features_supported +
139*7ea28254SJohn Hall 		firmware_features->num_elements;
140*7ea28254SJohn Hall 
141*7ea28254SJohn Hall 	features_requested_abs_addr = firmware_features_abs_addr +
142*7ea28254SJohn Hall 		(features_requested - (uint8_t*)firmware_features);
143*7ea28254SJohn Hall 	/*
144*7ea28254SJohn Hall 	 * NOTE: This memcpy is writing to a BAR-mapped address
145*7ea28254SJohn Hall 	 * which may not be safe for all OSes without proper API
146*7ea28254SJohn Hall 	 */
147*7ea28254SJohn Hall 	memcpy(features_requested_abs_addr, features_requested,
148*7ea28254SJohn Hall 		firmware_features->num_elements);
149*7ea28254SJohn Hall 
150*7ea28254SJohn Hall 	if (pqi_is_firmware_feature_supported(firmware_features,
151*7ea28254SJohn Hall 		PQI_FIRMWARE_FEATURE_MAX_KNOWN_FEATURE)) {
152*7ea28254SJohn Hall 		host_max_known_feature_iomem_addr =
153*7ea28254SJohn Hall 			(uint16_t*)(features_requested_abs_addr +
154*7ea28254SJohn Hall 			(firmware_features->num_elements * 2) + sizeof(uint16_t));
155*7ea28254SJohn Hall 			/*
156*7ea28254SJohn Hall 			 * NOTE: This writes to a BAR-mapped address
157*7ea28254SJohn Hall 			 * which may not be safe for all OSes without proper API
158*7ea28254SJohn Hall 			 */
159*7ea28254SJohn Hall 			*host_max_known_feature_iomem_addr = pqi_max_feature;
160*7ea28254SJohn Hall 	}
161*7ea28254SJohn Hall 
162*7ea28254SJohn Hall 	return pqi_config_table_update(softs,
163*7ea28254SJohn Hall 		PQI_CONF_TABLE_SECTION_FIRMWARE_FEATURES,
164*7ea28254SJohn Hall 		PQI_CONF_TABLE_SECTION_FIRMWARE_FEATURES);
165*7ea28254SJohn Hall }
166*7ea28254SJohn Hall 
167*7ea28254SJohn Hall typedef struct pqi_firmware_feature pqi_firmware_feature_t;
168*7ea28254SJohn Hall typedef void (*feature_status_fn)(pqisrc_softstate_t *softs,
169*7ea28254SJohn Hall 	pqi_firmware_feature_t *firmware_feature);
170*7ea28254SJohn Hall 
171*7ea28254SJohn Hall struct pqi_firmware_feature {
172*7ea28254SJohn Hall 	char		*feature_name;
173*7ea28254SJohn Hall 	unsigned int	feature_bit;
174*7ea28254SJohn Hall 	boolean_t		supported;
175*7ea28254SJohn Hall 	boolean_t		enabled;
176*7ea28254SJohn Hall 	feature_status_fn	feature_status;
177*7ea28254SJohn Hall };
178*7ea28254SJohn Hall 
179*7ea28254SJohn Hall static void
pqi_firmware_feature_status(pqisrc_softstate_t * softs,struct pqi_firmware_feature * firmware_feature)180*7ea28254SJohn Hall pqi_firmware_feature_status(pqisrc_softstate_t *softs,
181*7ea28254SJohn Hall 	struct pqi_firmware_feature *firmware_feature)
182*7ea28254SJohn Hall {
183*7ea28254SJohn Hall 	if (!firmware_feature->supported) {
184*7ea28254SJohn Hall 		DBG_NOTE("%s not supported by controller\n",
185*7ea28254SJohn Hall 			firmware_feature->feature_name);
186*7ea28254SJohn Hall 		return;
187*7ea28254SJohn Hall 	}
188*7ea28254SJohn Hall 
189*7ea28254SJohn Hall 	if (firmware_feature->enabled) {
190*7ea28254SJohn Hall 		DBG_NOTE("%s enabled\n", firmware_feature->feature_name);
191*7ea28254SJohn Hall 		return;
192*7ea28254SJohn Hall 	}
193*7ea28254SJohn Hall 
194*7ea28254SJohn Hall 	DBG_NOTE("failed to enable %s\n", firmware_feature->feature_name);
195*7ea28254SJohn Hall }
196*7ea28254SJohn Hall 
197*7ea28254SJohn Hall static void
pqi_ctrl_update_feature_flags(pqisrc_softstate_t * softs,struct pqi_firmware_feature * firmware_feature)198*7ea28254SJohn Hall pqi_ctrl_update_feature_flags(pqisrc_softstate_t *softs,
199*7ea28254SJohn Hall 	struct pqi_firmware_feature *firmware_feature)
200*7ea28254SJohn Hall {
201*7ea28254SJohn Hall 	switch (firmware_feature->feature_bit) {
202*7ea28254SJohn Hall 	case PQI_FIRMWARE_FEATURE_RAID_1_WRITE_BYPASS:
203*7ea28254SJohn Hall 		softs->aio_raid1_write_bypass = firmware_feature->enabled;
204*7ea28254SJohn Hall 		break;
205*7ea28254SJohn Hall 	case PQI_FIRMWARE_FEATURE_RAID_5_WRITE_BYPASS:
206*7ea28254SJohn Hall 		softs->aio_raid5_write_bypass = firmware_feature->enabled;
207*7ea28254SJohn Hall 		break;
208*7ea28254SJohn Hall 	case PQI_FIRMWARE_FEATURE_RAID_6_WRITE_BYPASS:
209*7ea28254SJohn Hall 		softs->aio_raid6_write_bypass = firmware_feature->enabled;
210*7ea28254SJohn Hall 		break;
211*7ea28254SJohn Hall 	case PQI_FIRMWARE_FEATURE_RAID_IU_TIMEOUT:
212*7ea28254SJohn Hall 		softs->timeout_in_passthrough = true;
213*7ea28254SJohn Hall 		break;
214*7ea28254SJohn Hall 	case PQI_FIRMWARE_FEATURE_TMF_IU_TIMEOUT:
215*7ea28254SJohn Hall 		softs->timeout_in_tmf = true;
216*7ea28254SJohn Hall 		break;
217*7ea28254SJohn Hall 	case PQI_FIRMWARE_FEATURE_UNIQUE_SATA_WWN:
218*7ea28254SJohn Hall 		break;
219*7ea28254SJohn Hall 	case PQI_FIRMWARE_FEATURE_PAGE83_IDENTIFIER_FOR_RPL_WWID:
220*7ea28254SJohn Hall 		softs->page83id_in_rpl = true;
221*7ea28254SJohn Hall 		break;
222*7ea28254SJohn Hall 	default:
223*7ea28254SJohn Hall 		DBG_NOTE("Nothing to do\n");
224*7ea28254SJohn Hall 		return;
225*7ea28254SJohn Hall 		break;
226*7ea28254SJohn Hall 	}
227*7ea28254SJohn Hall 	/* for any valid feature, also go update the feature status. */
228*7ea28254SJohn Hall 	pqi_firmware_feature_status(softs, firmware_feature);
229*7ea28254SJohn Hall }
230*7ea28254SJohn Hall 
231*7ea28254SJohn Hall 
232*7ea28254SJohn Hall static inline void
pqi_firmware_feature_update(pqisrc_softstate_t * softs,struct pqi_firmware_feature * firmware_feature)233*7ea28254SJohn Hall pqi_firmware_feature_update(pqisrc_softstate_t *softs,
234*7ea28254SJohn Hall 	struct pqi_firmware_feature *firmware_feature)
235*7ea28254SJohn Hall {
236*7ea28254SJohn Hall 	if (firmware_feature->feature_status)
237*7ea28254SJohn Hall 		firmware_feature->feature_status(softs, firmware_feature);
238*7ea28254SJohn Hall }
239*7ea28254SJohn Hall 
240*7ea28254SJohn Hall /* Defines PQI features that driver wishes to support */
241*7ea28254SJohn Hall static struct pqi_firmware_feature pqi_firmware_features[] = {
242*7ea28254SJohn Hall #if 0
243*7ea28254SJohn Hall 	{
244*7ea28254SJohn Hall 		.feature_name = "Online Firmware Activation",
245*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_OFA,
246*7ea28254SJohn Hall 		.feature_status = pqi_firmware_feature_status,
247*7ea28254SJohn Hall 	},
248*7ea28254SJohn Hall 	{
249*7ea28254SJohn Hall 		.feature_name = "Serial Management Protocol",
250*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_SMP,
251*7ea28254SJohn Hall 		.feature_status = pqi_firmware_feature_status,
252*7ea28254SJohn Hall 	},
253*7ea28254SJohn Hall #endif
254*7ea28254SJohn Hall 	{
255*7ea28254SJohn Hall 		.feature_name = "SATA WWN Unique ID",
256*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_UNIQUE_SATA_WWN,
257*7ea28254SJohn Hall 		.feature_status = pqi_ctrl_update_feature_flags,
258*7ea28254SJohn Hall 	},
259*7ea28254SJohn Hall 	{
260*7ea28254SJohn Hall 		.feature_name = "RAID IU Timeout",
261*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_RAID_IU_TIMEOUT,
262*7ea28254SJohn Hall 		.feature_status = pqi_ctrl_update_feature_flags,
263*7ea28254SJohn Hall 	},
264*7ea28254SJohn Hall 	{
265*7ea28254SJohn Hall 		.feature_name = "TMF IU Timeout",
266*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_TMF_IU_TIMEOUT,
267*7ea28254SJohn Hall 		.feature_status = pqi_ctrl_update_feature_flags,
268*7ea28254SJohn Hall 	},
269*7ea28254SJohn Hall 	{
270*7ea28254SJohn Hall 		.feature_name = "Support for RPL WWID filled by Page83 identifier",
271*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_PAGE83_IDENTIFIER_FOR_RPL_WWID,
272*7ea28254SJohn Hall 		.feature_status = pqi_ctrl_update_feature_flags,
273*7ea28254SJohn Hall 	},
274*7ea28254SJohn Hall 	/* Features independent of Maximum Known Feature should be added
275*7ea28254SJohn Hall 	before Maximum Known Feature*/
276*7ea28254SJohn Hall 	{
277*7ea28254SJohn Hall 		.feature_name = "Maximum Known Feature",
278*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_MAX_KNOWN_FEATURE,
279*7ea28254SJohn Hall 		.feature_status = pqi_firmware_feature_status,
280*7ea28254SJohn Hall 	},
281*7ea28254SJohn Hall 	{
282*7ea28254SJohn Hall 		.feature_name = "RAID 0 Read Bypass",
283*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_RAID_0_READ_BYPASS,
284*7ea28254SJohn Hall 		.feature_status = pqi_firmware_feature_status,
285*7ea28254SJohn Hall 	},
286*7ea28254SJohn Hall 	{
287*7ea28254SJohn Hall 		.feature_name = "RAID 1 Read Bypass",
288*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_RAID_1_READ_BYPASS,
289*7ea28254SJohn Hall 		.feature_status = pqi_firmware_feature_status,
290*7ea28254SJohn Hall 	},
291*7ea28254SJohn Hall 	{
292*7ea28254SJohn Hall 		.feature_name = "RAID 5 Read Bypass",
293*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_RAID_5_READ_BYPASS,
294*7ea28254SJohn Hall 		.feature_status = pqi_firmware_feature_status,
295*7ea28254SJohn Hall 	},
296*7ea28254SJohn Hall 	{
297*7ea28254SJohn Hall 		.feature_name = "RAID 6 Read Bypass",
298*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_RAID_6_READ_BYPASS,
299*7ea28254SJohn Hall 		.feature_status = pqi_firmware_feature_status,
300*7ea28254SJohn Hall 	},
301*7ea28254SJohn Hall 	{
302*7ea28254SJohn Hall 		.feature_name = "RAID 0 Write Bypass",
303*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_RAID_0_WRITE_BYPASS,
304*7ea28254SJohn Hall 		.feature_status = pqi_firmware_feature_status,
305*7ea28254SJohn Hall 	},
306*7ea28254SJohn Hall 	{
307*7ea28254SJohn Hall 		.feature_name = "RAID 1 Write Bypass",
308*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_RAID_1_WRITE_BYPASS,
309*7ea28254SJohn Hall 		.feature_status = pqi_ctrl_update_feature_flags,
310*7ea28254SJohn Hall 	},
311*7ea28254SJohn Hall 	{
312*7ea28254SJohn Hall 		.feature_name = "RAID 5 Write Bypass",
313*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_RAID_5_WRITE_BYPASS,
314*7ea28254SJohn Hall 		.feature_status = pqi_ctrl_update_feature_flags,
315*7ea28254SJohn Hall 	},
316*7ea28254SJohn Hall 	{
317*7ea28254SJohn Hall 		.feature_name = "RAID 6 Write Bypass",
318*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_RAID_6_WRITE_BYPASS,
319*7ea28254SJohn Hall 		.feature_status = pqi_ctrl_update_feature_flags,
320*7ea28254SJohn Hall 	},
321*7ea28254SJohn Hall #if 0
322*7ea28254SJohn Hall 	{
323*7ea28254SJohn Hall 		.feature_name = "New Soft Reset Handshake",
324*7ea28254SJohn Hall 		.feature_bit = PQI_FIRMWARE_FEATURE_SOFT_RESET_HANDSHAKE,
325*7ea28254SJohn Hall 		.feature_status = pqi_ctrl_update_feature_flags,
326*7ea28254SJohn Hall 	},
327*7ea28254SJohn Hall #endif
328*7ea28254SJohn Hall 
329*7ea28254SJohn Hall };
330*7ea28254SJohn Hall 
331*7ea28254SJohn Hall static void
pqi_process_firmware_features(pqisrc_softstate_t * softs,void * features,void * firmware_features_abs_addr)332*7ea28254SJohn Hall pqi_process_firmware_features(pqisrc_softstate_t *softs,
333*7ea28254SJohn Hall 	void *features, void *firmware_features_abs_addr)
334*7ea28254SJohn Hall {
335*7ea28254SJohn Hall 	int rc;
336*7ea28254SJohn Hall 	struct pqi_config_table_firmware_features *firmware_features = features;
337*7ea28254SJohn Hall 	unsigned int i;
338*7ea28254SJohn Hall 	unsigned int num_features_supported;
339*7ea28254SJohn Hall 
340*7ea28254SJohn Hall 	/* Iterates through local PQI feature support list to
341*7ea28254SJohn Hall 	see if the controller also supports the feature */
342*7ea28254SJohn Hall 	for (i = 0, num_features_supported = 0;
343*7ea28254SJohn Hall 		i < ARRAY_SIZE(pqi_firmware_features); i++) {
344*7ea28254SJohn Hall 		/*Check if SATA_WWN_FOR_DEV_UNIQUE_ID feature enabled by setting module
345*7ea28254SJohn Hall 		parameter if not avoid checking for the feature*/
346*7ea28254SJohn Hall 		if ((pqi_firmware_features[i].feature_bit ==
347*7ea28254SJohn Hall 			PQI_FIRMWARE_FEATURE_UNIQUE_SATA_WWN) &&
348*7ea28254SJohn Hall 			(!softs->sata_unique_wwn)) {
349*7ea28254SJohn Hall 			continue;
350*7ea28254SJohn Hall 		}
351*7ea28254SJohn Hall 		if (pqi_is_firmware_feature_supported(firmware_features,
352*7ea28254SJohn Hall 			pqi_firmware_features[i].feature_bit)) {
353*7ea28254SJohn Hall 			pqi_firmware_features[i].supported = true;
354*7ea28254SJohn Hall 			num_features_supported++;
355*7ea28254SJohn Hall 		} else {
356*7ea28254SJohn Hall 			DBG_WARN("Feature %s is not supported by firmware\n",
357*7ea28254SJohn Hall 			pqi_firmware_features[i].feature_name);
358*7ea28254SJohn Hall 			pqi_firmware_feature_update(softs,
359*7ea28254SJohn Hall 				&pqi_firmware_features[i]);
360*7ea28254SJohn Hall 
361*7ea28254SJohn Hall 			/* if max known feature bit isn't supported,
362*7ea28254SJohn Hall  			 * then no other feature bits are supported.
363*7ea28254SJohn Hall  			 */
364*7ea28254SJohn Hall 			if (pqi_firmware_features[i].feature_bit ==
365*7ea28254SJohn Hall 				PQI_FIRMWARE_FEATURE_MAX_KNOWN_FEATURE)
366*7ea28254SJohn Hall 				break;
367*7ea28254SJohn Hall 		}
368*7ea28254SJohn Hall 	}
369*7ea28254SJohn Hall 
370*7ea28254SJohn Hall 	DBG_INFO("Num joint features supported : %u \n", num_features_supported);
371*7ea28254SJohn Hall 
372*7ea28254SJohn Hall 	if (num_features_supported == 0)
373*7ea28254SJohn Hall 		return;
374*7ea28254SJohn Hall 
375*7ea28254SJohn Hall 	/* request driver features that are also on firmware-supported list */
376*7ea28254SJohn Hall 	for (i = 0; i < ARRAY_SIZE(pqi_firmware_features); i++) {
377*7ea28254SJohn Hall 		if (!pqi_firmware_features[i].supported)
378*7ea28254SJohn Hall 			continue;
379*7ea28254SJohn Hall #ifdef DEVICE_HINT
380*7ea28254SJohn Hall 		if (check_device_hint_status(softs, pqi_firmware_features[i].feature_bit))
381*7ea28254SJohn Hall 			continue;
382*7ea28254SJohn Hall #endif
383*7ea28254SJohn Hall 		pqi_request_firmware_feature(firmware_features,
384*7ea28254SJohn Hall 			pqi_firmware_features[i].feature_bit);
385*7ea28254SJohn Hall 	}
386*7ea28254SJohn Hall 
387*7ea28254SJohn Hall 	/* enable the features that were successfully requested. */
388*7ea28254SJohn Hall 	rc = pqi_enable_firmware_features(softs, firmware_features,
389*7ea28254SJohn Hall 		firmware_features_abs_addr);
390*7ea28254SJohn Hall 	if (rc) {
391*7ea28254SJohn Hall 		DBG_ERR("failed to enable firmware features in PQI configuration table\n");
392*7ea28254SJohn Hall 		for (i = 0; i < ARRAY_SIZE(pqi_firmware_features); i++) {
393*7ea28254SJohn Hall 			if (!pqi_firmware_features[i].supported)
394*7ea28254SJohn Hall 				continue;
395*7ea28254SJohn Hall 			pqi_firmware_feature_update(softs,
396*7ea28254SJohn Hall 				&pqi_firmware_features[i]);
397*7ea28254SJohn Hall 		}
398*7ea28254SJohn Hall 		return;
399*7ea28254SJohn Hall 	}
400*7ea28254SJohn Hall 
401*7ea28254SJohn Hall 	/* report the features that were successfully enabled. */
402*7ea28254SJohn Hall 	for (i = 0; i < ARRAY_SIZE(pqi_firmware_features); i++) {
403*7ea28254SJohn Hall 		if (!pqi_firmware_features[i].supported)
404*7ea28254SJohn Hall 			continue;
405*7ea28254SJohn Hall 		if (pqi_is_firmware_feature_enabled(firmware_features,
406*7ea28254SJohn Hall 			firmware_features_abs_addr,
407*7ea28254SJohn Hall 			pqi_firmware_features[i].feature_bit)) {
408*7ea28254SJohn Hall 				pqi_firmware_features[i].enabled = true;
409*7ea28254SJohn Hall 		} else {
410*7ea28254SJohn Hall 			DBG_WARN("Feature %s could not be enabled.\n",
411*7ea28254SJohn Hall 				pqi_firmware_features[i].feature_name);
412*7ea28254SJohn Hall 		}
413*7ea28254SJohn Hall 		pqi_firmware_feature_update(softs,
414*7ea28254SJohn Hall 			&pqi_firmware_features[i]);
415*7ea28254SJohn Hall 	}
416*7ea28254SJohn Hall }
417*7ea28254SJohn Hall 
418*7ea28254SJohn Hall static void
pqi_init_firmware_features(void)419*7ea28254SJohn Hall pqi_init_firmware_features(void)
420*7ea28254SJohn Hall {
421*7ea28254SJohn Hall 	unsigned int i;
422*7ea28254SJohn Hall 
423*7ea28254SJohn Hall 	for (i = 0; i < ARRAY_SIZE(pqi_firmware_features); i++) {
424*7ea28254SJohn Hall 		pqi_firmware_features[i].supported = false;
425*7ea28254SJohn Hall 		pqi_firmware_features[i].enabled = false;
426*7ea28254SJohn Hall 	}
427*7ea28254SJohn Hall }
428*7ea28254SJohn Hall 
429*7ea28254SJohn Hall static void
pqi_process_firmware_features_section(pqisrc_softstate_t * softs,void * features,void * firmware_features_abs_addr)430*7ea28254SJohn Hall pqi_process_firmware_features_section(pqisrc_softstate_t *softs,
431*7ea28254SJohn Hall 	void *features, void *firmware_features_abs_addr)
432*7ea28254SJohn Hall {
433*7ea28254SJohn Hall 	pqi_init_firmware_features();
434*7ea28254SJohn Hall 	pqi_process_firmware_features(softs, features, firmware_features_abs_addr);
435*7ea28254SJohn Hall }
436*7ea28254SJohn Hall 
437*7ea28254SJohn Hall 
438*7ea28254SJohn Hall /*
439*7ea28254SJohn Hall  * Get the PQI configuration table parameters.
440*7ea28254SJohn Hall  * Currently using for heart-beat counter scratch-pad register.
441*7ea28254SJohn Hall  */
442*7ea28254SJohn Hall int
pqisrc_process_config_table(pqisrc_softstate_t * softs)443*7ea28254SJohn Hall pqisrc_process_config_table(pqisrc_softstate_t *softs)
444*7ea28254SJohn Hall {
445*7ea28254SJohn Hall 	int ret = PQI_STATUS_FAILURE;
446*7ea28254SJohn Hall 	uint32_t config_table_size;
447*7ea28254SJohn Hall 	uint32_t section_off;
448*7ea28254SJohn Hall 	uint8_t *config_table_abs_addr;
449*7ea28254SJohn Hall 	struct pqi_conf_table *conf_table;
450*7ea28254SJohn Hall 	struct pqi_conf_table_section_header *section_hdr;
451*7ea28254SJohn Hall 
452*7ea28254SJohn Hall 	config_table_size = softs->pqi_cap.conf_tab_sz;
453*7ea28254SJohn Hall 
454*7ea28254SJohn Hall 	if (config_table_size < sizeof(*conf_table) ||
455*7ea28254SJohn Hall 		config_table_size > PQI_CONF_TABLE_MAX_LEN) {
456*7ea28254SJohn Hall 		DBG_ERR("Invalid PQI conf table length of %u\n",
457*7ea28254SJohn Hall 			config_table_size);
458*7ea28254SJohn Hall 		return ret;
459*7ea28254SJohn Hall 	}
460*7ea28254SJohn Hall 
461*7ea28254SJohn Hall 	conf_table = os_mem_alloc(softs, config_table_size);
462*7ea28254SJohn Hall 	if (!conf_table) {
463*7ea28254SJohn Hall 		DBG_ERR("Failed to allocate memory for PQI conf table\n");
464*7ea28254SJohn Hall 		return ret;
465*7ea28254SJohn Hall 	}
466*7ea28254SJohn Hall 
467*7ea28254SJohn Hall 	config_table_abs_addr = (uint8_t *)(softs->pci_mem_base_vaddr +
468*7ea28254SJohn Hall 					softs->pqi_cap.conf_tab_off);
469*7ea28254SJohn Hall 
470*7ea28254SJohn Hall 	PCI_MEM_GET_BUF(softs, config_table_abs_addr,
471*7ea28254SJohn Hall 			softs->pqi_cap.conf_tab_off,
472*7ea28254SJohn Hall 			(uint8_t*)conf_table, config_table_size);
473*7ea28254SJohn Hall 
474*7ea28254SJohn Hall 	if (memcmp(conf_table->sign, PQI_CONF_TABLE_SIGNATURE,
475*7ea28254SJohn Hall 			sizeof(conf_table->sign)) != 0) {
476*7ea28254SJohn Hall 		DBG_ERR("Invalid PQI config signature\n");
477*7ea28254SJohn Hall 		goto out;
478*7ea28254SJohn Hall 	}
479*7ea28254SJohn Hall 
480*7ea28254SJohn Hall 	section_off = LE_32(conf_table->first_section_off);
481*7ea28254SJohn Hall 
482*7ea28254SJohn Hall 	while (section_off) {
483*7ea28254SJohn Hall 
484*7ea28254SJohn Hall 		if (section_off+ sizeof(*section_hdr) >= config_table_size) {
485*7ea28254SJohn Hall 			DBG_INFO("Reached end of PQI config table. Breaking off.\n");
486*7ea28254SJohn Hall 			break;
487*7ea28254SJohn Hall 		}
488*7ea28254SJohn Hall 
489*7ea28254SJohn Hall 		section_hdr = (struct pqi_conf_table_section_header *)((uint8_t *)conf_table + section_off);
490*7ea28254SJohn Hall 
491*7ea28254SJohn Hall 		switch (LE_16(section_hdr->section_id)) {
492*7ea28254SJohn Hall 		case PQI_CONF_TABLE_SECTION_GENERAL_INFO:
493*7ea28254SJohn Hall 			break;
494*7ea28254SJohn Hall 		case PQI_CONF_TABLE_SECTION_FIRMWARE_FEATURES:
495*7ea28254SJohn Hall 			pqi_process_firmware_features_section(softs, section_hdr, (config_table_abs_addr + section_off));
496*7ea28254SJohn Hall 			break;
497*7ea28254SJohn Hall 		case PQI_CONF_TABLE_SECTION_FIRMWARE_ERRATA:
498*7ea28254SJohn Hall 		case PQI_CONF_TABLE_SECTION_DEBUG:
499*7ea28254SJohn Hall 			break;
500*7ea28254SJohn Hall 		case PQI_CONF_TABLE_SECTION_HEARTBEAT:
501*7ea28254SJohn Hall 			softs->heartbeat_counter_off = softs->pqi_cap.conf_tab_off +
502*7ea28254SJohn Hall 				section_off +
503*7ea28254SJohn Hall 				offsetof(struct pqi_conf_table_heartbeat, heartbeat_counter);
504*7ea28254SJohn Hall 			softs->heartbeat_counter_abs_addr = (uint64_t *)(softs->pci_mem_base_vaddr +
505*7ea28254SJohn Hall 				softs->heartbeat_counter_off);
506*7ea28254SJohn Hall 			ret = PQI_STATUS_SUCCESS;
507*7ea28254SJohn Hall 			break;
508*7ea28254SJohn Hall 		case PQI_CONF_TABLE_SOFT_RESET:
509*7ea28254SJohn Hall 			break;
510*7ea28254SJohn Hall 		default:
511*7ea28254SJohn Hall 			DBG_NOTE("unrecognized PQI config table section ID: 0x%x\n",
512*7ea28254SJohn Hall 				LE_16(section_hdr->section_id));
513*7ea28254SJohn Hall 			break;
514*7ea28254SJohn Hall 		}
515*7ea28254SJohn Hall 		section_off = LE_16(section_hdr->next_section_off);
516*7ea28254SJohn Hall 	}
517*7ea28254SJohn Hall out:
518*7ea28254SJohn Hall 	os_mem_free(softs, (void *)conf_table,config_table_size);
519*7ea28254SJohn Hall 	return ret;
520*7ea28254SJohn Hall }
521