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 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright 2020 Nexenta by DDN, Inc. All rights reserved.
  29  */
  30 
  31 #include <sys/file.h>
  32 #include <sys/sunndi.h>
  33 #include <sys/sunddi.h>
  34 #include <sys/sunldi.h>
  35 #include <io/px/px_regs.h>
  36 #include <sys/pci_tools.h>
  37 #include <fpc.h>
  38 #include <fpc-impl.h>
  39 
  40 #define CHIP_COMPATIBLE_NAME    "pciex108e,80f0"
  41 #define BANK_ADDR_MASK          0x7FFFFF
  42 
  43 #define OPEN_FLAGS (FREAD | FWRITE)
  44 
  45 #define PCIE_BANK       0
  46 #define JBUS_BANK       1
  47 
  48 typedef struct px_regs {
  49         uint32_t addr_hi;
  50         uint32_t addr_lo;
  51         uint32_t size_hi;
  52         uint32_t size_lo;
  53 } px_regs_t;
  54 
  55 /* There is one of these for every root nexus device found */
  56 typedef struct fire4u_specific {
  57         char *nodename;
  58         uintptr_t jbus_bank_base;
  59 } fire4u_specific_t;
  60 
  61 typedef struct fire_counter_handle_impl {
  62         ldi_handle_t devhandle;
  63         fire4u_specific_t *devspec; /* Points to proper one for specific dev. */
  64 } fire_counter_handle_impl_t;
  65 
  66 static uint64_t counter_select_offsets[] = {
  67         JBC_PERFORMANCE_COUNTER_SELECT,
  68         IMU_PERFORMANCE_COUNTER_SELECT,
  69         MMU_PERFORMANCE_COUNTER_SELECT,
  70         TLU_PERFORMANCE_COUNTER_SELECT,
  71         LPU_LINK_PERFORMANCE_COUNTER_SELECT
  72 };
  73 
  74 /*
  75  * The following event and offset arrays is organized by grouping in major
  76  * order the fire_perfcnt_t register types, and in minor order the register
  77  * numbers within that type.
  78  */
  79 
  80 static uint64_t counter_reg_offsets[] = {
  81         JBC_PERFORMANCE_COUNTER_ZERO,
  82         JBC_PERFORMANCE_COUNTER_ONE,
  83         IMU_PERFORMANCE_COUNTER_ZERO,
  84         IMU_PERFORMANCE_COUNTER_ONE,
  85         MMU_PERFORMANCE_COUNTER_ZERO,
  86         MMU_PERFORMANCE_COUNTER_ONE,
  87         TLU_PERFORMANCE_COUNTER_ZERO,
  88         TLU_PERFORMANCE_COUNTER_ONE,
  89         TLU_PERFORMANCE_COUNTER_TWO,
  90         LPU_LINK_PERFORMANCE_COUNTER1,
  91         LPU_LINK_PERFORMANCE_COUNTER2
  92 };
  93 
  94 /*
  95  * Add the following to one of the LPU_LINK_PERFORMANCE_COUNTERx offsets to
  96  * write a value to that counter.
  97  */
  98 #define LPU_LINK_PERFCTR_WRITE_OFFSET   0x8
  99 
 100 /*
 101  * Note that LPU_LINK_PERFORMANCE_COUNTER_CONTROL register is hard-reset to
 102  * zeros and this is the value we want.  This register isn't touched by this
 103  * module, and as long as it remains untouched by other modules we're OK.
 104  */
 105 
 106 static ldi_ident_t ldi_identifier;
 107 static boolean_t ldi_identifier_valid = B_FALSE;
 108 
 109 /* Called by _init to determine if it is OK to install driver. */
 110 int
 111 fpc_platform_check()
 112 {
 113         return (SUCCESS);
 114 }
 115 
 116 /* Called during attach to do module-wide initialization. */
 117 int
 118 fpc_platform_module_init(dev_info_t *dip)
 119 {
 120         int status;
 121 
 122         status = ldi_ident_from_dip(dip, &ldi_identifier);
 123         if (status == 0)
 124                 ldi_identifier_valid = B_TRUE;
 125         return ((status == 0) ? DDI_SUCCESS : DDI_FAILURE);
 126 }
 127 
 128 int
 129 fpc_platform_node_init(dev_info_t *dip, int *avail)
 130 {
 131         int index;
 132         char *name;
 133         int nodename_size;
 134         char *nodename = NULL;
 135         fire4u_specific_t *platform_specific_data = NULL;
 136         char *compatible = NULL;
 137         px_regs_t *regs_p = NULL;
 138         int regs_length = 0;
 139 
 140         if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 141             "compatible", &compatible) != DDI_PROP_SUCCESS)
 142                 return (DDI_SUCCESS);
 143 
 144         if (strcmp(compatible, CHIP_COMPATIBLE_NAME) != 0) {
 145                 ddi_prop_free(compatible);
 146                 return (DDI_SUCCESS);
 147         }
 148         ddi_prop_free(compatible);
 149 
 150         fpc_common_node_setup(dip, &index);
 151 
 152         name = fpc_get_dev_name_by_number(index);
 153         nodename_size = strlen(name) + strlen(PCI_MINOR_REG) + 2;
 154         nodename = kmem_zalloc(nodename_size, KM_SLEEP);
 155 
 156         platform_specific_data =
 157             kmem_zalloc(sizeof (fire4u_specific_t), KM_SLEEP);
 158 
 159         (void) strcpy(nodename, name);
 160         (void) strcat(nodename, ":");
 161         (void) strcat(nodename, PCI_MINOR_REG);
 162         platform_specific_data->nodename = nodename;
 163 
 164         /* Get register banks. */
 165         if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 166             "reg", (caddr_t)®s_p, ®s_length) != DDI_SUCCESS) {
 167                 goto bad_regs_p;
 168         }
 169 
 170         if ((regs_length / sizeof (px_regs_t)) < 2) {
 171                 goto bad_regs_length;
 172         }
 173 
 174         platform_specific_data->jbus_bank_base =
 175             regs_p[JBUS_BANK].addr_lo & BANK_ADDR_MASK;
 176 
 177         kmem_free(regs_p, regs_length);
 178 
 179         if (index == 0)
 180                 *avail |= (PCIE_A_REGS_AVAIL | JBUS_REGS_AVAIL);
 181         else
 182                 *avail |= PCIE_B_REGS_AVAIL;
 183 
 184         (void) fpc_set_platform_data_by_number(index, platform_specific_data);
 185 
 186         return (DDI_SUCCESS);
 187 
 188 bad_regs_length:
 189         if (regs_p)
 190                 kmem_free(regs_p, regs_length);
 191 bad_regs_p:
 192         kmem_free(platform_specific_data, sizeof (fire4u_specific_t));
 193         if (nodename)
 194                 kmem_free(nodename, nodename_size);
 195 
 196         return (DDI_FAILURE);
 197 }
 198 
 199 void
 200 fpc_platform_node_fini(void *arg)
 201 {
 202         fire4u_specific_t *plat_arg = (fire4u_specific_t *)arg;
 203         if (plat_arg == NULL)
 204                 return;
 205         if (plat_arg->nodename)
 206                 kmem_free(plat_arg->nodename, strlen(plat_arg->nodename)+1);
 207         kmem_free(plat_arg, sizeof (fire4u_specific_t));
 208 }
 209 
 210 /*ARGSUSED*/
 211 void
 212 fpc_platform_module_fini(dev_info_t *dip)
 213 {
 214         if (ldi_identifier_valid)
 215                 ldi_ident_release(ldi_identifier);
 216 }
 217 
 218 fire_perfreg_handle_t
 219 fpc_get_perfreg_handle(int devnum)
 220 {
 221         int rval = EINVAL;
 222 
 223         fire_counter_handle_impl_t *handle_impl =
 224             kmem_zalloc(sizeof (fire_counter_handle_impl_t), KM_SLEEP);
 225 
 226         if ((handle_impl->devspec =
 227             fpc_get_platform_data_by_number(devnum)) != NULL) {
 228                 rval = ldi_open_by_name(handle_impl->devspec->nodename,
 229                     OPEN_FLAGS, kcred, &handle_impl->devhandle,
 230                     ldi_identifier);
 231         }
 232 
 233         if (rval != SUCCESS) {
 234                 kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
 235                 return ((fire_perfreg_handle_t)-1);
 236         } else {
 237                 return ((fire_perfreg_handle_t)handle_impl);
 238         }
 239 }
 240 
 241 int
 242 fpc_free_counter_handle(fire_perfreg_handle_t handle)
 243 {
 244         fire_counter_handle_impl_t *handle_impl =
 245             (fire_counter_handle_impl_t *)handle;
 246         (void) ldi_close(handle_impl->devhandle, OPEN_FLAGS, kcred);
 247         kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
 248         return (SUCCESS);
 249 }
 250 
 251 int
 252 fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
 253     uint64_t *reg_data, boolean_t is_write)
 254 {
 255         int rval;
 256         int ioctl_rval;
 257         pcitool_reg_t prg;
 258         fire_counter_handle_impl_t *handle_impl =
 259             (fire_counter_handle_impl_t *)handle;
 260         int cmd = is_write ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
 261 
 262         prg.user_version = PCITOOL_VERSION;
 263 
 264         if (group == jbc) {
 265                 prg.barnum = JBUS_BANK;
 266                 prg.offset = counter_select_offsets[group] -
 267                     handle_impl->devspec->jbus_bank_base;
 268         } else {
 269                 prg.barnum = PCIE_BANK;
 270 
 271                 /*
 272                  * Note that a pcie_bank_base isn't needed.  Pcie register
 273                  * offsets are already relative to the start of their bank.  No
 274                  * base needs to be subtracted to get the relative offset that
 275                  * pcitool ioctls want.
 276                  */
 277                 prg.offset = counter_select_offsets[group];
 278         }
 279         prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
 280         prg.data = *reg_data;
 281 
 282         /* Read original value. */
 283         if (((rval = ldi_ioctl(handle_impl->devhandle, cmd, (intptr_t)&prg,
 284             FKIOCTL, kcred, &ioctl_rval)) == SUCCESS) && (!is_write)) {
 285                 *reg_data = prg.data;
 286         }
 287 
 288         return (rval);
 289 }
 290 
 291 int
 292 fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
 293     int counter_index, uint64_t *value, boolean_t is_write)
 294 {
 295         int rval;
 296         int ioctl_rval;
 297         pcitool_reg_t prg;
 298         fire_counter_handle_impl_t *handle_impl =
 299             (fire_counter_handle_impl_t *)handle;
 300         int command =
 301             (is_write) ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
 302 
 303         prg.user_version = PCITOOL_VERSION;
 304         /*
 305          * Note that stated PCIE offsets are relative to the beginning of their
 306          * register bank, while JBUS offsets are absolute.
 307          */
 308         if (group == jbc) {
 309                 prg.barnum = JBUS_BANK;
 310                 prg.offset = counter_reg_offsets[counter_index] -
 311                     handle_impl->devspec->jbus_bank_base;
 312         } else {
 313                 prg.barnum = PCIE_BANK;
 314                 prg.offset = counter_reg_offsets[counter_index];
 315         }
 316 
 317         if ((group == lpu) && (is_write)) {
 318                 prg.offset += LPU_LINK_PERFCTR_WRITE_OFFSET;
 319         }
 320 
 321         prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
 322         prg.data = *value;
 323 
 324         if (((rval = ldi_ioctl(handle_impl->devhandle, command, (intptr_t)&prg,
 325             FKIOCTL, kcred, &ioctl_rval)) == SUCCESS) && (!is_write)) {
 326                 *value = prg.data;
 327         }
 328 
 329         return (rval);
 330 }