1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25 /*
26 * This file contains SM-HBA support for MPT SAS driver
27 */
28
29 #if defined(lint) || defined(DEBUG)
30 #define MPTSAS_DEBUG
31 #endif
32
33 /*
34 * standard header files
35 */
36 #include <sys/note.h>
37 #include <sys/scsi/scsi.h>
38 #include <sys/pci.h>
39 #include <sys/scsi/generic/sas.h>
40 #include <sys/scsi/impl/scsi_sas.h>
41
42 #pragma pack(1)
43 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_type.h>
44 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2.h>
45 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_cnfg.h>
46 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_init.h>
47 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_ioc.h>
48 #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_sas.h>
49 #pragma pack()
50
51 /*
52 * private header files.
53 */
54 #include <sys/scsi/adapters/mpt_sas/mptsas_var.h>
55 #include <sys/scsi/adapters/mpt_sas/mptsas_smhba.h>
56
57 /*
58 * SM - HBA statics
59 */
60 extern char *mptsas_driver_rev;
61
62 static void
63 mptsas_smhba_add_hba_prop(mptsas_t *mpt, data_type_t dt,
64 char *prop_name, void *prop_val);
65
66 void
67 mptsas_smhba_show_phy_info(mptsas_t *mpt);
68
69 static void
mptsas_smhba_add_hba_prop(mptsas_t * mpt,data_type_t dt,char * prop_name,void * prop_val)70 mptsas_smhba_add_hba_prop(mptsas_t *mpt, data_type_t dt,
71 char *prop_name, void *prop_val)
72 {
73 ASSERT(mpt != NULL);
74
75 switch (dt) {
76 case DATA_TYPE_INT32:
77 if (ddi_prop_update_int(DDI_DEV_T_NONE, mpt->m_dip,
78 prop_name, *(int *)prop_val)) {
79 mptsas_log(mpt, CE_WARN,
80 "%s: %s prop update failed", __func__, prop_name);
81 }
82 break;
83 case DATA_TYPE_STRING:
84 if (ddi_prop_update_string(DDI_DEV_T_NONE, mpt->m_dip,
85 prop_name, (char *)prop_val)) {
86 mptsas_log(mpt, CE_WARN,
87 "%s: %s prop update failed", __func__, prop_name);
88 }
89 break;
90 default:
91 mptsas_log(mpt, CE_WARN, "%s: "
92 "Unhandled datatype(%d) for (%s). Skipping prop update.",
93 __func__, dt, prop_name);
94 }
95 }
96
97 void
mptsas_smhba_show_phy_info(mptsas_t * mpt)98 mptsas_smhba_show_phy_info(mptsas_t *mpt)
99 {
100 int i;
101
102 ASSERT(mpt != NULL);
103
104 for (i = 0; i < MPTSAS_MAX_PHYS; i++) {
105 mptsas_log(mpt, CE_WARN,
106 "phy %d, Owner hdl:0x%x, attached hdl: 0x%x,"
107 "attached phy identifier %d,Program link rate 0x%x,"
108 "hw link rate 0x%x, negotiator link rate 0x%x, path %s",
109 i, mpt->m_phy_info[i].smhba_info.owner_devhdl,
110 mpt->m_phy_info[i].smhba_info.attached_devhdl,
111 mpt->m_phy_info[i].smhba_info.attached_phy_identify,
112 mpt->m_phy_info[i].smhba_info.programmed_link_rate,
113 mpt->m_phy_info[i].smhba_info.hw_link_rate,
114 mpt->m_phy_info[i].smhba_info.negotiated_link_rate,
115 mpt->m_phy_info[i].smhba_info.path);
116 }
117 }
118
119 void
mptsas_smhba_set_phy_props(mptsas_t * mpt,char * iport,dev_info_t * dip,uint8_t phy_nums,uint16_t * attached_devhdl)120 mptsas_smhba_set_phy_props(mptsas_t *mpt, char *iport, dev_info_t *dip,
121 uint8_t phy_nums, uint16_t *attached_devhdl)
122 {
123 int i;
124 int j = 0;
125 int rval;
126 size_t packed_size;
127 char *packed_data = NULL;
128 char phymask[MPTSAS_MAX_PHYS];
129 nvlist_t **phy_props;
130 nvlist_t *nvl;
131 smhba_info_t *pSmhba = NULL;
132
133 if (phy_nums == 0) {
134 return;
135 }
136 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
137 mptsas_log(mpt, CE_WARN, "%s: nvlist_alloc() failed", __func__);
138 }
139
140 phy_props = kmem_zalloc(sizeof (nvlist_t *) * phy_nums,
141 KM_SLEEP);
142
143 for (i = 0; i < mpt->m_num_phys; i++) {
144
145 bzero(phymask, sizeof (phymask));
146 (void) sprintf(phymask, "%x", mpt->m_phy_info[i].phy_mask);
147 if (strcmp(phymask, iport) == 0) {
148 pSmhba = &mpt->m_phy_info[i].smhba_info;
149 (void) nvlist_alloc(&phy_props[j], NV_UNIQUE_NAME, 0);
150 (void) nvlist_add_uint8(phy_props[j], SAS_PHY_ID, i);
151 (void) nvlist_add_uint8(phy_props[j],
152 "phyState",
153 (pSmhba->negotiated_link_rate
154 & 0x0f));
155 (void) nvlist_add_int8(phy_props[j],
156 SAS_NEG_LINK_RATE,
157 (pSmhba->negotiated_link_rate
158 & 0x0f));
159 (void) nvlist_add_int8(phy_props[j],
160 SAS_PROG_MIN_LINK_RATE,
161 (pSmhba->programmed_link_rate
162 & 0x0f));
163 (void) nvlist_add_int8(phy_props[j],
164 SAS_HW_MIN_LINK_RATE,
165 (pSmhba->hw_link_rate
166 & 0x0f));
167 (void) nvlist_add_int8(phy_props[j],
168 SAS_PROG_MAX_LINK_RATE,
169 ((pSmhba->programmed_link_rate
170 & 0xf0) >> 4));
171 (void) nvlist_add_int8(phy_props[j],
172 SAS_HW_MAX_LINK_RATE,
173 ((pSmhba->hw_link_rate
174 & 0xf0) >> 4));
175
176 j++;
177
178 if (pSmhba->attached_devhdl &&
179 (attached_devhdl != NULL)) {
180 *attached_devhdl =
181 pSmhba->attached_devhdl;
182 }
183 }
184 }
185
186 rval = nvlist_add_nvlist_array(nvl, SAS_PHY_INFO_NVL, phy_props,
187 phy_nums);
188 if (rval) {
189 mptsas_log(mpt, CE_WARN,
190 " nv list array add failed, return value %d.",
191 rval);
192 goto exit;
193 }
194 (void) nvlist_size(nvl, &packed_size, NV_ENCODE_NATIVE);
195 packed_data = kmem_zalloc(packed_size, KM_SLEEP);
196 (void) nvlist_pack(nvl, &packed_data, &packed_size,
197 NV_ENCODE_NATIVE, 0);
198
199 (void) ddi_prop_update_byte_array(DDI_DEV_T_NONE, dip,
200 SAS_PHY_INFO, (uchar_t *)packed_data, packed_size);
201
202 exit:
203 for (i = 0; i < phy_nums && phy_props[i] != NULL; i++) {
204 nvlist_free(phy_props[i]);
205 }
206 nvlist_free(nvl);
207 kmem_free(phy_props, sizeof (nvlist_t *) * phy_nums);
208
209 if (packed_data != NULL) {
210 kmem_free(packed_data, packed_size);
211 }
212 }
213
214 /*
215 * Called with PHY lock held on phyp
216 */
217 void
mptsas_smhba_log_sysevent(mptsas_t * mpt,char * subclass,char * etype,smhba_info_t * phyp)218 mptsas_smhba_log_sysevent(mptsas_t *mpt, char *subclass, char *etype,
219 smhba_info_t *phyp)
220 {
221 nvlist_t *attr_list;
222 char *pname;
223 char sas_addr[MPTSAS_WWN_STRLEN];
224 uint8_t phynum = 0;
225 uint8_t lrate = 0;
226
227 if (mpt->m_dip == NULL)
228 return;
229 if (phyp == NULL)
230 return;
231
232 pname = kmem_zalloc(MAXPATHLEN, KM_NOSLEEP);
233 if (pname == NULL)
234 return;
235
236 if ((strcmp(subclass, ESC_SAS_PHY_EVENT) == 0) ||
237 (strcmp(subclass, ESC_SAS_HBA_PORT_BROADCAST) == 0)) {
238 ASSERT(phyp != NULL);
239 (void) strncpy(pname, phyp->path, strlen(phyp->path));
240 phynum = phyp->phy_id;
241 bzero(sas_addr, sizeof (sas_addr));
242 (void) sprintf(sas_addr, "w%016"PRIx64, phyp->sas_addr);
243 if (strcmp(etype, SAS_PHY_ONLINE) == 0) {
244 lrate = phyp->negotiated_link_rate;
245 }
246 }
247 if (strcmp(subclass, ESC_SAS_HBA_PORT_BROADCAST) == 0) {
248 (void) ddi_pathname(mpt->m_dip, pname);
249 }
250
251 if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, 0) != 0) {
252 mptsas_log(mpt, CE_WARN,
253 "%s: Failed to post sysevent", __func__);
254 kmem_free(pname, MAXPATHLEN);
255 return;
256 }
257
258 if (nvlist_add_int32(attr_list, SAS_DRV_INST,
259 ddi_get_instance(mpt->m_dip)) != 0)
260 goto fail;
261
262 if (nvlist_add_string(attr_list, SAS_PORT_ADDR, sas_addr) != 0)
263 goto fail;
264
265 if (nvlist_add_string(attr_list, SAS_DEVFS_PATH, pname) != 0)
266 goto fail;
267
268 if (nvlist_add_uint8(attr_list, SAS_PHY_ID, phynum) != 0)
269 goto fail;
270
271 if (strcmp(etype, SAS_PHY_ONLINE) == 0) {
272 if (nvlist_add_uint8(attr_list, SAS_LINK_RATE, lrate) != 0)
273 goto fail;
274 }
275
276 if (nvlist_add_string(attr_list, SAS_EVENT_TYPE, etype) != 0)
277 goto fail;
278
279 (void) ddi_log_sysevent(mpt->m_dip, DDI_VENDOR_SUNW, EC_HBA, subclass,
280 attr_list, NULL, DDI_NOSLEEP);
281
282 fail:
283 kmem_free(pname, MAXPATHLEN);
284 nvlist_free(attr_list);
285 }
286
287 void
mptsas_create_phy_stats(mptsas_t * mpt,char * iport,dev_info_t * dip)288 mptsas_create_phy_stats(mptsas_t *mpt, char *iport, dev_info_t *dip)
289 {
290 sas_phy_stats_t *ps;
291 smhba_info_t *phyp;
292 int ndata;
293 char ks_name[KSTAT_STRLEN];
294 char phymask[MPTSAS_MAX_PHYS];
295 int i;
296
297 ASSERT(iport != NULL);
298 ASSERT(mpt != NULL);
299
300 for (i = 0; i < mpt->m_num_phys; i++) {
301
302 bzero(phymask, sizeof (phymask));
303 (void) sprintf(phymask, "%x", mpt->m_phy_info[i].phy_mask);
304 if (strcmp(phymask, iport) == 0) {
305
306 phyp = &mpt->m_phy_info[i].smhba_info;
307 mutex_enter(&phyp->phy_mutex);
308
309 if (phyp->phy_stats != NULL) {
310 mutex_exit(&phyp->phy_mutex);
311 /* We've already created this kstat instance */
312 continue;
313 }
314
315 ndata = (sizeof (sas_phy_stats_t)/
316 sizeof (kstat_named_t));
317 (void) snprintf(ks_name, sizeof (ks_name),
318 "%s.%llx.%d.%d", ddi_driver_name(dip),
319 (longlong_t)mpt->un.m_base_wwid,
320 ddi_get_instance(dip), i);
321
322 phyp->phy_stats = kstat_create("mptsas",
323 ddi_get_instance(dip), ks_name, KSTAT_SAS_PHY_CLASS,
324 KSTAT_TYPE_NAMED, ndata, 0);
325
326 if (phyp->phy_stats == NULL) {
327 mutex_exit(&phyp->phy_mutex);
328 mptsas_log(mpt, CE_WARN,
329 "%s: Failed to create %s kstats", __func__,
330 ks_name);
331 continue;
332 }
333
334 ps = (sas_phy_stats_t *)phyp->phy_stats->ks_data;
335
336 kstat_named_init(&ps->seconds_since_last_reset,
337 "SecondsSinceLastReset", KSTAT_DATA_ULONGLONG);
338 kstat_named_init(&ps->tx_frames,
339 "TxFrames", KSTAT_DATA_ULONGLONG);
340 kstat_named_init(&ps->rx_frames,
341 "RxFrames", KSTAT_DATA_ULONGLONG);
342 kstat_named_init(&ps->tx_words,
343 "TxWords", KSTAT_DATA_ULONGLONG);
344 kstat_named_init(&ps->rx_words,
345 "RxWords", KSTAT_DATA_ULONGLONG);
346 kstat_named_init(&ps->invalid_dword_count,
347 "InvalidDwordCount", KSTAT_DATA_ULONGLONG);
348 kstat_named_init(&ps->running_disparity_error_count,
349 "RunningDisparityErrorCount", KSTAT_DATA_ULONGLONG);
350 kstat_named_init(&ps->loss_of_dword_sync_count,
351 "LossofDwordSyncCount", KSTAT_DATA_ULONGLONG);
352 kstat_named_init(&ps->phy_reset_problem_count,
353 "PhyResetProblemCount", KSTAT_DATA_ULONGLONG);
354
355 phyp->phy_stats->ks_private = phyp;
356 phyp->phy_stats->ks_update = mptsas_update_phy_stats;
357 kstat_install(phyp->phy_stats);
358 mutex_exit(&phyp->phy_mutex);
359 }
360 }
361 }
362
363 int
mptsas_update_phy_stats(kstat_t * ks,int rw)364 mptsas_update_phy_stats(kstat_t *ks, int rw)
365 {
366 int ret = DDI_FAILURE;
367 smhba_info_t *pptr = NULL;
368 sas_phy_stats_t *ps = ks->ks_data;
369 uint32_t page_address;
370 mptsas_t *mpt;
371
372 _NOTE(ARGUNUSED(rw));
373
374 pptr = (smhba_info_t *)ks->ks_private;
375 ASSERT((pptr != NULL));
376 mpt = (mptsas_t *)pptr->mpt;
377 ASSERT((mpt != NULL));
378 page_address = (MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | pptr->phy_id);
379
380 /*
381 * We just want to lock against other invocations of kstat;
382 * we don't need to pmcs_lock_phy() for this.
383 */
384 mutex_enter(&mpt->m_mutex);
385
386 ret = mptsas_get_sas_phy_page1(pptr->mpt, page_address, pptr);
387
388 if (ret == DDI_FAILURE)
389 goto fail;
390
391 ps->invalid_dword_count.value.ull =
392 (unsigned long long)pptr->invalid_dword_count;
393
394 ps->running_disparity_error_count.value.ull =
395 (unsigned long long)pptr->running_disparity_error_count;
396
397 ps->loss_of_dword_sync_count.value.ull =
398 (unsigned long long)pptr->loss_of_dword_sync_count;
399
400 ps->phy_reset_problem_count.value.ull =
401 (unsigned long long)pptr->phy_reset_problem_count;
402
403 ret = DDI_SUCCESS;
404 fail:
405 mutex_exit(&mpt->m_mutex);
406
407 return (ret);
408 }
409
410 void
mptsas_destroy_phy_stats(mptsas_t * mpt)411 mptsas_destroy_phy_stats(mptsas_t *mpt)
412 {
413 smhba_info_t *phyp;
414 int i = 0;
415
416 ASSERT(mpt != NULL);
417
418 for (i = 0; i < mpt->m_num_phys; i++) {
419 phyp = &mpt->m_phy_info[i].smhba_info;
420 if (phyp == NULL) {
421 continue;
422 }
423
424 mutex_enter(&phyp->phy_mutex);
425 if (phyp->phy_stats != NULL) {
426 kstat_delete(phyp->phy_stats);
427 phyp->phy_stats = NULL;
428 }
429 mutex_exit(&phyp->phy_mutex);
430 }
431 }
432
433 int
mptsas_smhba_phy_init(mptsas_t * mpt)434 mptsas_smhba_phy_init(mptsas_t *mpt)
435 {
436 int i = 0;
437 int rval = DDI_SUCCESS;
438 uint32_t page_address;
439
440 ASSERT(mutex_owned(&mpt->m_mutex));
441
442 for (i = 0; i < mpt->m_num_phys; i++) {
443 page_address =
444 (MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER |
445 (MPI2_SAS_PHY_PGAD_PHY_NUMBER_MASK & i));
446 rval = mptsas_get_sas_phy_page0(mpt,
447 page_address, &mpt->m_phy_info[i].smhba_info);
448 if (rval != DDI_SUCCESS) {
449 mptsas_log(mpt, CE_WARN,
450 "Failed to get sas phy page 0"
451 " for each phy");
452 return (DDI_FAILURE);
453 }
454 mpt->m_phy_info[i].smhba_info.phy_id = (uint8_t)i;
455 mpt->m_phy_info[i].smhba_info.sas_addr =
456 mpt->un.m_base_wwid + i;
457 mpt->m_phy_info[i].smhba_info.mpt = mpt;
458 }
459 return (DDI_SUCCESS);
460 }
461
462 int
mptsas_smhba_setup(mptsas_t * mpt)463 mptsas_smhba_setup(mptsas_t *mpt)
464 {
465 int sm_hba = 1;
466 char chiprev, hw_rev[24];
467 char serial_number[72];
468 int protocol = 0;
469
470 mutex_enter(&mpt->m_mutex);
471 if (mptsas_smhba_phy_init(mpt)) {
472 mutex_exit(&mpt->m_mutex);
473 return (DDI_FAILURE);
474 }
475 mutex_exit(&mpt->m_mutex);
476
477 /* SM-HBA support */
478 mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_INT32, MPTSAS_SMHBA_SUPPORTED,
479 &sm_hba);
480
481 /* SM-HBA driver version */
482 mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_DRV_VERSION,
483 mptsas_driver_rev);
484
485 /* SM-HBA hardware version */
486 chiprev = 'A' + mpt->m_revid;
487 (void) snprintf(hw_rev, 2, "%s", &chiprev);
488 mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_HWARE_VERSION,
489 hw_rev);
490
491 /* SM-HBA phy number per HBA */
492 mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_INT32, MPTSAS_NUM_PHYS_HBA,
493 &(mpt->m_num_phys));
494
495 /* SM-HBA protocal support */
496 protocol = SAS_SSP_SUPPORT | SAS_SATA_SUPPORT | SAS_SMP_SUPPORT;
497 mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_INT32,
498 MPTSAS_SUPPORTED_PROTOCOL, &protocol);
499
500 mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_MANUFACTURER,
501 mpt->m_MANU_page0.ChipName);
502
503 mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_MODEL_NAME,
504 mpt->m_MANU_page0.BoardName);
505
506 /*
507 * VPD data is not available, we make a serial number for this.
508 */
509
510 (void) sprintf(serial_number, "%s%s%s%s%s",
511 mpt->m_MANU_page0.ChipName,
512 mpt->m_MANU_page0.ChipRevision,
513 mpt->m_MANU_page0.BoardName,
514 mpt->m_MANU_page0.BoardAssembly,
515 mpt->m_MANU_page0.BoardTracerNumber);
516
517 mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_SERIAL_NUMBER,
518 &serial_number[0]);
519
520 return (DDI_SUCCESS);
521 }
522