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 #include <sys/file.h>
  28 #include <sys/sunndi.h>
  29 #include <sys/sunddi.h>
  30 #include <sys/sunldi.h>
  31 #include <io/px/px_regs.h>
  32 #include <sys/pci_tools.h>
  33 #include <fpc.h>
  34 #include <fpc-impl.h>
  35 
  36 #define CHIP_COMPATIBLE_NAME    "pciex108e,80f0"
  37 #define BANK_ADDR_MASK          0x7FFFFF
  38 
  39 #define OPEN_FLAGS (FREAD | FWRITE)
  40 
  41 #define PCIE_BANK       0
  42 #define JBUS_BANK       1
  43 
  44 typedef struct px_regs {
  45         uint32_t addr_hi;
  46         uint32_t addr_lo;
  47         uint32_t size_hi;
  48         uint32_t size_lo;
  49 } px_regs_t;
  50 
  51 /* There is one of these for every root nexus device found */
  52 typedef struct fire4u_specific {
  53         char *nodename;
  54         uintptr_t jbus_bank_base;
  55 } fire4u_specific_t;
  56 
  57 typedef struct fire_counter_handle_impl {
  58         ldi_handle_t devhandle;
  59         fire4u_specific_t *devspec; /* Points to proper one for specific dev. */
  60 } fire_counter_handle_impl_t;
  61 
  62 static uint64_t counter_select_offsets[] = {
  63         JBC_PERFORMANCE_COUNTER_SELECT,
  64         IMU_PERFORMANCE_COUNTER_SELECT,
  65         MMU_PERFORMANCE_COUNTER_SELECT,
  66         TLU_PERFORMANCE_COUNTER_SELECT,
  67         LPU_LINK_PERFORMANCE_COUNTER_SELECT
  68 };
  69 
  70 /*
  71  * The following event and offset arrays is organized by grouping in major
  72  * order the fire_perfcnt_t register types, and in minor order the register
  73  * numbers within that type.
  74  */
  75 
  76 static uint64_t counter_reg_offsets[] = {
  77         JBC_PERFORMANCE_COUNTER_ZERO,
  78         JBC_PERFORMANCE_COUNTER_ONE,
  79         IMU_PERFORMANCE_COUNTER_ZERO,
  80         IMU_PERFORMANCE_COUNTER_ONE,
  81         MMU_PERFORMANCE_COUNTER_ZERO,
  82         MMU_PERFORMANCE_COUNTER_ONE,
  83         TLU_PERFORMANCE_COUNTER_ZERO,
  84         TLU_PERFORMANCE_COUNTER_ONE,
  85         TLU_PERFORMANCE_COUNTER_TWO,
  86         LPU_LINK_PERFORMANCE_COUNTER1,
  87         LPU_LINK_PERFORMANCE_COUNTER2
  88 };
  89 
  90 /*
  91  * Add the following to one of the LPU_LINK_PERFORMANCE_COUNTERx offsets to
  92  * write a value to that counter.
  93  */
  94 #define LPU_LINK_PERFCTR_WRITE_OFFSET   0x8
  95 
  96 /*
  97  * Note that LPU_LINK_PERFORMANCE_COUNTER_CONTROL register is hard-reset to
  98  * zeros and this is the value we want.  This register isn't touched by this
  99  * module, and as long as it remains untouched by other modules we're OK.
 100  */
 101 
 102 static ldi_ident_t ldi_identifier;
 103 static boolean_t ldi_identifier_valid = B_FALSE;
 104 static cred_t *credentials = NULL;
 105 
 106 /* Called by _init to determine if it is OK to install driver. */
 107 int
 108 fpc_platform_check()
 109 {
 110         return (SUCCESS);
 111 }
 112 
 113 /* Called during attach to do module-wide initialization. */
 114 int
 115 fpc_platform_module_init(dev_info_t *dip)
 116 {
 117         int status;
 118 
 119         credentials = crget();
 120         status = ldi_ident_from_dip(dip, &ldi_identifier);
 121         if (status == 0)
 122                 ldi_identifier_valid = B_TRUE;
 123         return ((status == 0) ? DDI_SUCCESS : DDI_FAILURE);
 124 }
 125 
 126 int
 127 fpc_platform_node_init(dev_info_t *dip, int *avail)
 128 {
 129         int index;
 130         char *name;
 131         int nodename_size;
 132         char *nodename = NULL;
 133         fire4u_specific_t *platform_specific_data = NULL;
 134         char *compatible = NULL;
 135         px_regs_t *regs_p = NULL;
 136         int regs_length = 0;
 137 
 138         if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 139             "compatible", &compatible) != DDI_PROP_SUCCESS)
 140                 return (DDI_SUCCESS);
 141 
 142         if (strcmp(compatible, CHIP_COMPATIBLE_NAME) != 0) {
 143                 ddi_prop_free(compatible);
 144                 return (DDI_SUCCESS);
 145         }
 146         ddi_prop_free(compatible);
 147 
 148         fpc_common_node_setup(dip, &index);
 149 
 150         name = fpc_get_dev_name_by_number(index);
 151         nodename_size = strlen(name) + strlen(PCI_MINOR_REG) + 2;
 152         nodename = kmem_zalloc(nodename_size, KM_SLEEP);
 153 
 154         platform_specific_data =
 155             kmem_zalloc(sizeof (fire4u_specific_t), KM_SLEEP);
 156 
 157         (void) strcpy(nodename, name);
 158         (void) strcat(nodename, ":");
 159         (void) strcat(nodename, PCI_MINOR_REG);
 160         platform_specific_data->nodename = nodename;
 161 
 162         /* Get register banks. */
 163         if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 164             "reg", (caddr_t)&regs_p, &regs_length) != DDI_SUCCESS) {
 165                 goto bad_regs_p;
 166         }
 167 
 168         if ((regs_length / sizeof (px_regs_t)) < 2) {
 169                 goto bad_regs_length;
 170         }
 171 
 172         platform_specific_data->jbus_bank_base =
 173             regs_p[JBUS_BANK].addr_lo & BANK_ADDR_MASK;
 174 
 175         kmem_free(regs_p, regs_length);
 176 
 177         if (index == 0)
 178                 *avail |= (PCIE_A_REGS_AVAIL | JBUS_REGS_AVAIL);
 179         else
 180                 *avail |= PCIE_B_REGS_AVAIL;
 181 
 182         (void) fpc_set_platform_data_by_number(index, platform_specific_data);
 183 
 184         return (DDI_SUCCESS);
 185 
 186 bad_regs_length:
 187         if (regs_p)
 188                 kmem_free(regs_p, regs_length);
 189 bad_regs_p:
 190         kmem_free(platform_specific_data, sizeof (fire4u_specific_t));
 191         if (nodename)
 192                 kmem_free(nodename, nodename_size);
 193 
 194         return (DDI_FAILURE);
 195 }
 196 
 197 void
 198 fpc_platform_node_fini(void *arg)
 199 {
 200         fire4u_specific_t *plat_arg = (fire4u_specific_t *)arg;
 201         if (plat_arg == NULL)
 202                 return;
 203         if (plat_arg->nodename)
 204                 kmem_free(plat_arg->nodename, strlen(plat_arg->nodename)+1);
 205         kmem_free(plat_arg, sizeof (fire4u_specific_t));
 206 }
 207 
 208 /*ARGSUSED*/
 209 void
 210 fpc_platform_module_fini(dev_info_t *dip)
 211 {
 212         if (ldi_identifier_valid)
 213                 ldi_ident_release(ldi_identifier);
 214         if (credentials)
 215                 crfree(credentials);
 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, credentials, &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, credentials);
 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, credentials, &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, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) {
 326                 *value = prg.data;
 327         }
 328 
 329         return (rval);
 330 }