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->invalid_dword_count,
 349                             "InvalidDwordCount", KSTAT_DATA_ULONGLONG);
 350                         kstat_named_init(&ps->running_disparity_error_count,
 351                             "RunningDisparityErrorCount", KSTAT_DATA_ULONGLONG);
 352                         kstat_named_init(&ps->loss_of_dword_sync_count,
 353                             "LossofDwordSyncCount", KSTAT_DATA_ULONGLONG);
 354                         kstat_named_init(&ps->phy_reset_problem_count,
 355                             "PhyResetProblemCount", KSTAT_DATA_ULONGLONG);
 356 
 357                         phyp->phy_stats->ks_private = phyp;
 358                         phyp->phy_stats->ks_update = mptsas_update_phy_stats;
 359                         kstat_install(phyp->phy_stats);
 360                         mutex_exit(&phyp->phy_mutex);
 361                 }
 362         }
 363 }
 364 
 365 int
 366 mptsas_update_phy_stats(kstat_t *ks, int rw)
 367 {
 368         int                     ret = DDI_FAILURE;
 369         smhba_info_t            *pptr = NULL;
 370         sas_phy_stats_t         *ps = ks->ks_data;
 371         uint32_t                page_address;
 372         mptsas_t                *mpt;
 373 
 374         _NOTE(ARGUNUSED(rw));
 375 
 376         pptr = (smhba_info_t *)ks->ks_private;
 377         ASSERT((pptr != NULL));
 378         mpt = (mptsas_t *)pptr->mpt;
 379         ASSERT((mpt != NULL));
 380         page_address = (MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | pptr->phy_id);
 381 
 382         /*
 383          * We just want to lock against other invocations of kstat;
 384          * we don't need to pmcs_lock_phy() for this.
 385          */
 386         mutex_enter(&mpt->m_mutex);
 387 
 388         ret = mptsas_get_sas_phy_page1(pptr->mpt, page_address, pptr);
 389 
 390         if (ret == DDI_FAILURE)
 391                 goto fail;
 392 
 393         ps->invalid_dword_count.value.ull =
 394             (unsigned long long)pptr->invalid_dword_count;
 395 
 396         ps->running_disparity_error_count.value.ull =
 397             (unsigned long long)pptr->running_disparity_error_count;
 398 
 399         ps->loss_of_dword_sync_count.value.ull =
 400             (unsigned long long)pptr->loss_of_dword_sync_count;
 401 
 402         ps->phy_reset_problem_count.value.ull =
 403             (unsigned long long)pptr->phy_reset_problem_count;
 404 
 405         ret = DDI_SUCCESS;
 406 fail:
 407         mutex_exit(&mpt->m_mutex);
 408 
 409         return (ret);
 410 }
 411 
 412 void
 413 mptsas_destroy_phy_stats(mptsas_t *mpt)
 414 {
 415         smhba_info_t    *phyp;
 416         int                     i = 0;
 417 
 418         ASSERT(mpt != NULL);
 419 
 420         for (i = 0; i < mpt->m_num_phys; i++) {
 421                 phyp = &mpt->m_phy_info[i].smhba_info;
 422                 if (phyp == NULL) {
 423                         continue;
 424                 }
 425 
 426                 mutex_enter(&phyp->phy_mutex);
 427                 if (phyp->phy_stats != NULL) {
 428                         kstat_delete(phyp->phy_stats);
 429                         phyp->phy_stats = NULL;
 430                 }
 431                 mutex_exit(&phyp->phy_mutex);
 432         }
 433 }
 434 
 435 int
 436 mptsas_smhba_phy_init(mptsas_t *mpt)
 437 {
 438         int             i = 0;
 439         int             rval = DDI_SUCCESS;
 440         uint32_t        page_address;
 441 
 442         ASSERT(mutex_owned(&mpt->m_mutex));
 443 
 444         for (i = 0; i < mpt->m_num_phys; i++) {
 445                 page_address =
 446                     (MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER |
 447                     (MPI2_SAS_PHY_PGAD_PHY_NUMBER_MASK & i));
 448                 rval = mptsas_get_sas_phy_page0(mpt,
 449                     page_address, &mpt->m_phy_info[i].smhba_info);
 450                 if (rval != DDI_SUCCESS) {
 451                         mptsas_log(mpt, CE_WARN,
 452                             "Failed to get sas phy page 0"
 453                             " for each phy");
 454                         return (DDI_FAILURE);
 455                 }
 456                 mpt->m_phy_info[i].smhba_info.phy_id = (uint8_t)i;
 457                 mpt->m_phy_info[i].smhba_info.sas_addr =
 458                     mpt->un.m_base_wwid + i;
 459                 mpt->m_phy_info[i].smhba_info.mpt = mpt;
 460         }
 461         return (DDI_SUCCESS);
 462 }
 463 
 464 int
 465 mptsas_smhba_setup(mptsas_t *mpt)
 466 {
 467         int             sm_hba = 1;
 468         char            chiprev, hw_rev[24];
 469         char            serial_number[72];
 470         int             protocol = 0;
 471 
 472         mutex_enter(&mpt->m_mutex);
 473         if (mptsas_smhba_phy_init(mpt)) {
 474                 mutex_exit(&mpt->m_mutex);
 475                 return (DDI_FAILURE);
 476         }
 477         mutex_exit(&mpt->m_mutex);
 478 
 479         /* SM-HBA support */
 480         mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_INT32, MPTSAS_SMHBA_SUPPORTED,
 481             &sm_hba);
 482 
 483         /* SM-HBA driver version */
 484         mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_DRV_VERSION,
 485             mptsas_driver_rev);
 486 
 487         /* SM-HBA hardware version */
 488         chiprev = 'A' + mpt->m_revid;
 489         (void) snprintf(hw_rev, 2, "%s", &chiprev);
 490         mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_HWARE_VERSION,
 491             hw_rev);
 492 
 493         /* SM-HBA phy number per HBA */
 494         mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_INT32, MPTSAS_NUM_PHYS_HBA,
 495             &(mpt->m_num_phys));
 496 
 497         /* SM-HBA protocal support */
 498         protocol = SAS_SSP_SUPPORT | SAS_SATA_SUPPORT | SAS_SMP_SUPPORT;
 499         mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_INT32,
 500             MPTSAS_SUPPORTED_PROTOCOL, &protocol);
 501 
 502         mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_MANUFACTURER,
 503             mpt->m_MANU_page0.ChipName);
 504 
 505         mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_MODEL_NAME,
 506             mpt->m_MANU_page0.BoardName);
 507 
 508         /*
 509          * VPD data is not available, we make a serial number for this.
 510          */
 511 
 512         (void) sprintf(serial_number, "%s%s%s%s%s",
 513             mpt->m_MANU_page0.ChipName,
 514             mpt->m_MANU_page0.ChipRevision,
 515             mpt->m_MANU_page0.BoardName,
 516             mpt->m_MANU_page0.BoardAssembly,
 517             mpt->m_MANU_page0.BoardTracerNumber);
 518 
 519         mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_SERIAL_NUMBER,
 520             &serial_number[0]);
 521 
 522         return (DDI_SUCCESS);
 523 }