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 }