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