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