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 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 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 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 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 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 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 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 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 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 }