Home | History | Annotate | Download | only in fpc
      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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <sys/file.h>
     30 #include <sys/sunndi.h>
     31 #include <sys/sunddi.h>
     32 #include <sys/sunldi.h>
     33 #include <io/px/px_regs.h>
     34 #include <sys/pci_tools.h>
     35 #include <fpc.h>
     36 #include <fpc-impl.h>
     37 
     38 #define	CHIP_COMPATIBLE_NAME	"pciex108e,80f0"
     39 #define	BANK_ADDR_MASK		0x7FFFFF
     40 
     41 #define	OPEN_FLAGS (FREAD | FWRITE)
     42 
     43 #define	PCIE_BANK	0
     44 #define	JBUS_BANK	1
     45 
     46 typedef struct px_regs {
     47 	uint32_t addr_hi;
     48 	uint32_t addr_lo;
     49 	uint32_t size_hi;
     50 	uint32_t size_lo;
     51 } px_regs_t;
     52 
     53 /* There is one of these for every root nexus device found */
     54 typedef struct fire4u_specific {
     55 	char *nodename;
     56 	uintptr_t jbus_bank_base;
     57 } fire4u_specific_t;
     58 
     59 typedef struct fire_counter_handle_impl {
     60 	ldi_handle_t devhandle;
     61 	fire4u_specific_t *devspec; /* Points to proper one for specific dev. */
     62 } fire_counter_handle_impl_t;
     63 
     64 static uint64_t counter_select_offsets[] = {
     65 	JBC_PERFORMANCE_COUNTER_SELECT,
     66 	IMU_PERFORMANCE_COUNTER_SELECT,
     67 	MMU_PERFORMANCE_COUNTER_SELECT,
     68 	TLU_PERFORMANCE_COUNTER_SELECT,
     69 	LPU_LINK_PERFORMANCE_COUNTER_SELECT
     70 };
     71 
     72 /*
     73  * The following event and offset arrays is organized by grouping in major
     74  * order the fire_perfcnt_t register types, and in minor order the register
     75  * numbers within that type.
     76  */
     77 
     78 static uint64_t counter_reg_offsets[] = {
     79 	JBC_PERFORMANCE_COUNTER_ZERO,
     80 	JBC_PERFORMANCE_COUNTER_ONE,
     81 	IMU_PERFORMANCE_COUNTER_ZERO,
     82 	IMU_PERFORMANCE_COUNTER_ONE,
     83 	MMU_PERFORMANCE_COUNTER_ZERO,
     84 	MMU_PERFORMANCE_COUNTER_ONE,
     85 	TLU_PERFORMANCE_COUNTER_ZERO,
     86 	TLU_PERFORMANCE_COUNTER_ONE,
     87 	TLU_PERFORMANCE_COUNTER_TWO,
     88 	LPU_LINK_PERFORMANCE_COUNTER1,
     89 	LPU_LINK_PERFORMANCE_COUNTER2
     90 };
     91 
     92 /*
     93  * Add the following to one of the LPU_LINK_PERFORMANCE_COUNTERx offsets to
     94  * write a value to that counter.
     95  */
     96 #define	LPU_LINK_PERFCTR_WRITE_OFFSET	0x8
     97 
     98 /*
     99  * Note that LPU_LINK_PERFORMANCE_COUNTER_CONTROL register is hard-reset to
    100  * zeros and this is the value we want.  This register isn't touched by this
    101  * module, and as long as it remains untouched by other modules we're OK.
    102  */
    103 
    104 static ldi_ident_t ldi_identifier;
    105 static boolean_t ldi_identifier_valid = B_FALSE;
    106 static cred_t *credentials = NULL;
    107 
    108 /* Called by _init to determine if it is OK to install driver. */
    109 int
    110 fpc_platform_check()
    111 {
    112 	return (SUCCESS);
    113 }
    114 
    115 /* Called during attach to do module-wide initialization. */
    116 int
    117 fpc_platform_module_init(dev_info_t *dip)
    118 {
    119 	int status;
    120 
    121 	credentials = crget();
    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)&regs_p, &regs_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 	if (platform_specific_data)
    193 		kmem_free(platform_specific_data, sizeof (fire4u_specific_t));
    194 	if (nodename)
    195 		kmem_free(nodename, nodename_size);
    196 
    197 	return (DDI_FAILURE);
    198 }
    199 
    200 void
    201 fpc_platform_node_fini(void *arg)
    202 {
    203 	fire4u_specific_t *plat_arg = (fire4u_specific_t *)arg;
    204 	if (plat_arg == NULL)
    205 		return;
    206 	if (plat_arg->nodename)
    207 		kmem_free(plat_arg->nodename, strlen(plat_arg->nodename)+1);
    208 	kmem_free(plat_arg, sizeof (fire4u_specific_t));
    209 }
    210 
    211 /*ARGSUSED*/
    212 void
    213 fpc_platform_module_fini(dev_info_t *dip)
    214 {
    215 	if (ldi_identifier_valid)
    216 		ldi_ident_release(ldi_identifier);
    217 	if (credentials)
    218 		crfree(credentials);
    219 }
    220 
    221 fire_perfreg_handle_t
    222 fpc_get_perfreg_handle(int devnum)
    223 {
    224 	int rval = EINVAL;
    225 
    226 	fire_counter_handle_impl_t *handle_impl =
    227 	    kmem_zalloc(sizeof (fire_counter_handle_impl_t), KM_SLEEP);
    228 
    229 	if ((handle_impl->devspec =
    230 	    fpc_get_platform_data_by_number(devnum)) != NULL) {
    231 		rval = ldi_open_by_name(handle_impl->devspec->nodename,
    232 		    OPEN_FLAGS, credentials, &handle_impl->devhandle,
    233 		    ldi_identifier);
    234 	}
    235 
    236 	if (rval != SUCCESS) {
    237 		kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
    238 		return ((fire_perfreg_handle_t)-1);
    239 	} else {
    240 		return ((fire_perfreg_handle_t)handle_impl);
    241 	}
    242 }
    243 
    244 int
    245 fpc_free_counter_handle(fire_perfreg_handle_t handle)
    246 {
    247 	fire_counter_handle_impl_t *handle_impl =
    248 	    (fire_counter_handle_impl_t *)handle;
    249 	(void) ldi_close(handle_impl->devhandle, OPEN_FLAGS, credentials);
    250 	kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
    251 	return (SUCCESS);
    252 }
    253 
    254 int
    255 fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
    256     uint64_t *reg_data, boolean_t is_write)
    257 {
    258 	int rval;
    259 	int ioctl_rval;
    260 	pcitool_reg_t prg;
    261 	fire_counter_handle_impl_t *handle_impl =
    262 	    (fire_counter_handle_impl_t *)handle;
    263 	int cmd = is_write ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
    264 
    265 	prg.user_version = PCITOOL_VERSION;
    266 
    267 	if (group == jbc) {
    268 		prg.barnum = JBUS_BANK;
    269 		prg.offset = counter_select_offsets[group] -
    270 		    handle_impl->devspec->jbus_bank_base;
    271 	} else {
    272 		prg.barnum = PCIE_BANK;
    273 
    274 		/*
    275 		 * Note that a pcie_bank_base isn't needed.  Pcie register
    276 		 * offsets are already relative to the start of their bank.  No
    277 		 * base needs to be subtracted to get the relative offset that
    278 		 * pcitool ioctls want.
    279 		 */
    280 		prg.offset = counter_select_offsets[group];
    281 	}
    282 	prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
    283 	prg.data = *reg_data;
    284 
    285 	/* Read original value. */
    286 	if (((rval = ldi_ioctl(handle_impl->devhandle, cmd, (intptr_t)&prg,
    287 	    FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) {
    288 		*reg_data = prg.data;
    289 	}
    290 
    291 	return (rval);
    292 }
    293 
    294 int
    295 fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
    296     int counter_index, uint64_t *value, boolean_t is_write)
    297 {
    298 	int rval;
    299 	int ioctl_rval;
    300 	pcitool_reg_t prg;
    301 	fire_counter_handle_impl_t *handle_impl =
    302 	    (fire_counter_handle_impl_t *)handle;
    303 	int command =
    304 	    (is_write) ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
    305 
    306 	prg.user_version = PCITOOL_VERSION;
    307 	/*
    308 	 * Note that stated PCIE offsets are relative to the beginning of their
    309 	 * register bank, while JBUS offsets are absolute.
    310 	 */
    311 	if (group == jbc) {
    312 		prg.barnum = JBUS_BANK;
    313 		prg.offset = counter_reg_offsets[counter_index] -
    314 		    handle_impl->devspec->jbus_bank_base;
    315 	} else {
    316 		prg.barnum = PCIE_BANK;
    317 		prg.offset = counter_reg_offsets[counter_index];
    318 	}
    319 
    320 	if ((group == lpu) && (is_write)) {
    321 		prg.offset += LPU_LINK_PERFCTR_WRITE_OFFSET;
    322 	}
    323 
    324 	prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
    325 	prg.data = *value;
    326 
    327 	if (((rval = ldi_ioctl(handle_impl->devhandle, command, (intptr_t)&prg,
    328 	    FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) {
    329 		*value = prg.data;
    330 	}
    331 
    332 	return (rval);
    333 }
    334