/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/sysmacros.h>
#include <sys/machsystm.h>
#include <sys/pci_tools.h>
/*
* Number of interrupts supported per PCI bus.
*/
/*
* PCI Space definitions.
*/
/*
* Extract 64 bit parent or size values from 32 bit cells of
* pci_ranges_t property.
*
* Only bits 42-32 are relevant in parent_high.
*/
/* Big and little endian as boolean values. */
#define SUCCESS 0
/* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
typedef union {
/*
* Offsets of BARS in config space. First entry of 0 means config space.
* Entries here correlate to pcitool_bars_t enumerated type.
*/
0x0,
};
/*LINTLIBRARY*/
/*
* Safe C wrapper around assy language routine pci_do_phys_peek
*
* Type is TRUE for big endian, FALSE for little endian.
* Size is 1, 2, 4 or 8 bytes.
* paddr is the physical address in IO space to access read.
* value_p is where the value is returned.
*/
static int
{
/* Set up trap handling to make the access safe. */
/*
* on_trap works like setjmp.
* Set it up to not panic on data access error,
* but to call peek_fault instead.
* Call pci_do_phys_peek after trap handling is setup.
* When on_trap returns FALSE, it has been setup.
* When it returns TRUE, an it has caught an error.
*/
} else {
err = DDI_FAILURE;
}
no_trap();
if (err != DDI_FAILURE) {
switch (size) {
case 8:
break;
case 4:
break;
case 2:
break;
case 1:
break;
default:
err = DDI_FAILURE;
}
}
return (err);
}
/*
* Safe C wrapper around assy language routine pci_do_phys_poke
*
* Type is TRUE for big endian, FALSE for little endian.
* Size is 1,2,4 or 8 bytes.
* paddr is the physical address in IO space to access read.
* value contains the value to be written.
*/
static int
{
switch (size) {
case 8:
break;
case 4:
break;
case 2:
break;
case 1:
break;
default:
return (DDI_FAILURE);
}
/*
* on_trap works like setjmp.
* Set it up to not panic on data access error,
* but to call poke_fault instead.
* Call pci_do_phys_poke after trap handling is setup.
* When on_trap returns FALSE, it has been setup.
* When it returns TRUE, an it has caught an error.
*/
}
/* Let the dust settle and errors occur if they will. */
/* Check for an error. */
err = DDI_FAILURE;
}
no_trap();
return (err);
}
/*ARGSUSED*/
static int
{
/* If we need user_version, and to ret same user version as passed in */
DDI_SUCCESS) {
return (EFAULT);
}
return (ENOTSUP);
DDI_SUCCESS) {
}
return (rval);
}
/*
* Get interrupt information for a given ino.
* Returns info only for inos mapped to devices.
*
* Returned info is valid only when iget.num_devs is returned > 0.
* If ino is not enabled or is not mapped to a device, num_devs will be = 0.
*/
/*ARGSUSED*/
static int
{
/* Array part isn't used here, but oh well... */
int cpu_id;
int copyout_rval;
/* Read in just the header part, no array section. */
DDI_SUCCESS) {
return (EFAULT);
}
partial_iget.num_devs_ret = 0;
goto done_get_intr;
}
/* Validate argument. */
if (ino > PCI_MAX_INO) {
partial_iget.num_devs_ret = 0;
goto done_get_intr;
}
/* Caller wants device information returned. */
if (num_devs_ret > 0) {
/*
* Allocate room.
* Note if num_devs_ret == 0 iget remains pointing to
* partial_iget.
*/
/* Read in whole structure to verify there's room. */
SUCCESS) {
/* Be consistent and just return EFAULT here. */
return (EFAULT);
}
}
/*
* Read "valid" bit. If set, interrupts are enabled.
* This bit happens to be the same on Fire and Tomatillo.
*/
if (imregval & COMMON_INTR_MAP_REG_VALID) {
/*
* The following looks up the ib_ino_info and returns
* info of devices mapped to this ino.
*/
goto done_get_intr;
}
/*
* Consider only inos mapped to devices (as opposed to
* inos mapped to the bridge itself.
*/
/*
* These 2 items are platform specific,
* extracted from the bridge.
*/
}
}
if (iget_kmem_alloc_size > 0) {
}
if (copyout_rval != DDI_SUCCESS) {
}
return (rval);
}
/*
* Associate a new CPU with a given ino.
*
* Operate only on inos which are already mapped to devices.
*/
static int
{
int old_cpu_id;
/* Version 1 of pcitool_intr_set_t doesn't have flags. */
return (EFAULT);
switch (iset.user_version) {
case PCITOOL_V1:
break;
case PCITOOL_V2:
copyinout_size = sizeof (pcitool_intr_set_t);
return (EFAULT);
break;
default:
goto done_set_intr;
}
goto done_set_intr;
}
/* Validate input argument and that ino given belongs to a device. */
goto done_set_intr;
}
/* Operate only on inos which are already enabled. */
if (!(imregval & COMMON_INTR_MAP_REG_VALID)) {
goto done_set_intr;
}
goto done_set_intr;
}
goto done_set_intr;
}
switch (ret) {
case DDI_EPENDING:
break;
case DDI_EINVAL:
break;
default:
break;
}
return (rval);
}
/* Main function for handling interrupt CPU binding requests and queries. */
int
{
switch (cmd) {
/* Get system interrupt information. */
case PCITOOL_SYSTEM_INTR_INFO:
break;
/* Get interrupt information for a given ino. */
case PCITOOL_DEVICE_GET_INTR:
break;
/* Associate a new CPU with a given ino. */
case PCITOOL_DEVICE_SET_INTR:
break;
default:
}
return (rval);
}
/*
* Wrapper around pcitool_phys_peek/poke.
*
* Validates arguments and calls pcitool_phys_peek/poke appropriately.
*
* Dip is of the nexus,
* phys_addr is the address to write in physical space,
* max_addr is the upper bound on the physical space used for bounds checking,
* pcitool_status returns more detailed status in addition to a more generic
* errno-style function return value.
* other args are self-explanatory.
*/
static int
{
/* Upper bounds checking. */
"Phys addr 0x%llx out of range (max 0x%llx).\n",
/* Alignment checking. */
/* Made it through checks. Do the access. */
} else if (write) {
"%d byte %s pcitool_phys_poke at addr 0x%llx\n",
*data) != DDI_SUCCESS) {
"%d byte %s pcitool_phys_poke at addr "
"0x%llx failed\n",
}
} else { /* Read */
"%d byte %s pcitool_phys_peek at addr 0x%llx\n",
data) != DDI_SUCCESS) {
"%d byte %s pcitool_phys_peek at addr "
"0x%llx failed\n",
}
}
return (rval);
}
/*
* Perform register accesses on the nexus device itself.
*/
int
{
if (cmd == PCITOOL_NEXUS_SET_REG)
write_flag = B_TRUE;
/* Read data from userland. */
DDI_SUCCESS) {
return (EFAULT);
}
/* Read reg property which contains starting addr and size of banks. */
if (((reglen * sizeof (int)) %
sizeof (pci_nexus_regspec_t)) != 0) {
goto done;
}
}
/* Bounds check the bank number. */
(reglen * sizeof (int)) / sizeof (pci_nexus_regspec_t)) {
goto done;
}
"pcitool_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, "
"addr:0x%llx, max_addr:0x%llx\n",
/* Access device. prg.status is modified. */
done:
DDI_SUCCESS) {
return (EFAULT);
}
return (rval);
}
static int
{
/* Validate address arguments of bus / dev / func */
}
return (rval);
}
static int
{
int rval;
*bar = 0;
*is_io_space = B_FALSE;
/*
* Translate BAR number into offset of the BAR in
* the device's config space.
*/
/*
* Get Bus Address Register (BAR) from config space.
* bar_offset is the offset into config space of the BAR desired.
* prg->status is modified on error.
*/
4, /* 4 bytes. */
B_FALSE, /* Read */
B_FALSE, /* Little endian. */
return (rval);
if (!(*bar)) {
return (EINVAL);
}
/*
* BAR has bits saying this space is IO space, unless
* this is the ROM address register.
*/
(bar_offset != PCI_CONF_ROM)) {
*is_io_space = B_TRUE;
*bar &= PCI_BASE_IO_ADDR_M;
/*
* BAR has bits saying this space is 64 bit memory
* space, unless this is the ROM address register.
*
* The 64 bit address stored in two BAR cells is not necessarily
* aligned on an 8-byte boundary. Need to keep the first 4
* bytes read, and do a separate read of the high 4 bytes.
*/
/* Don't try to read past the end of BARs. */
if (bar_offset >= PCI_CONF_BASE5) {
return (EIO);
}
/* Access device. prg->status is modified on error. */
4, /* 4 bytes. */
B_FALSE, /* Read */
B_FALSE, /* Little endian. */
return (rval);
}
return (SUCCESS);
}
static int
{
int rval;
/* Access config space and we're done. */
"offset:0x%llx, phys_addr:0x%llx, end:%s\n",
/* Access device. pr.status is modified. */
return (rval);
}
/* Perform register accesses on PCI leaf devices. */
int
{
int rval = 0;
if (cmd == PCITOOL_DEVICE_SET_REG)
write_flag = B_TRUE;
DDI_SUCCESS) {
return (EFAULT);
}
goto done_reg;
/* Get config space first. */
/*
* Build device address based on base addr from range prop, and bus,
* dev and func values passed in. This address is where config space
* begins.
*/
base_addr = range_prop +
goto done_reg;
}
"func:0x%x, addr:0x%x\n", range_prop,
/* Proper config space desired. */
size, write_flag);
} else { /* IO / MEM / MEM64 space. */
&is_io_space) != SUCCESS)
goto done_reg;
/* IO space. */
if (is_io_space) {
/* Reposition to focus on IO space. */
/* 64 bit memory space. */
} else if ((bar >> 32) != 0) {
"64 bit mem space. 64-bit bar is 0x%llx\n", bar);
/* Reposition to MEM64 range space. */
} else { /* Mem32 space, including ROM */
"Additional ROM checking\n");
/* Can't write to ROM */
if (write_flag) {
goto done_reg;
/* ROM disabled for reading */
} else if (!(bar & 0x00000001)) {
goto done_reg;
}
}
}
"addr portion of bar is 0x%llx, base=0x%llx, "
/*
* Use offset provided by caller to index into
* desired space, then access.
* Note that prg.status is modified on error.
*/
}
DDI_SUCCESS) {
}
return (rval);
}
int
{
DDI_NT_REGACC, 0) != DDI_SUCCESS)
return (DDI_FAILURE);
DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
{
}