pci_axq.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* or http://www.opensolaris.org/os/licensing.
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* PCI nexus driver interface
*/
#include <sys/types.h>
#include <sys/conf.h> /* nulldev */
#include <sys/stat.h> /* devctl */
#include <sys/kmem.h>
#include <sys/async.h> /* ecc_flt for pci_ecc.h */
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/ontrap.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_subrdefs.h>
#include <sys/epm.h>
#include <sys/membar.h>
#include <sys/modctl.h>
#include <sys/hotplug/pci/pcihp.h>
#include <sys/pci/pci_obj.h>
/*LINTLIBRARY*/
static uint8_t pci_axq_hack_get8(ddi_acc_impl_t *handle, uint8_t *addr);
static uint16_t pci_axq_hack_get16(ddi_acc_impl_t *handle, uint16_t *addr);
static uint32_t pci_axq_hack_get32(ddi_acc_impl_t *handle, uint32_t *addr);
static uint64_t pci_axq_hack_get64(ddi_acc_impl_t *handle, uint64_t *addr);
static void pci_axq_hack_put8(ddi_acc_impl_t *handle, uint8_t *addr,
uint8_t data);
static void pci_axq_hack_put16(ddi_acc_impl_t *handle, uint16_t *addr,
uint16_t data);
static void pci_axq_hack_put32(ddi_acc_impl_t *handle, uint32_t *addr,
uint32_t data);
static void pci_axq_hack_put64(ddi_acc_impl_t *handle, uint64_t *addr,
uint64_t data);
static void pci_axq_hack_rep_get8(ddi_acc_impl_t *handle,
uint8_t *host_addr, uint8_t *dev_addr,
size_t repcount, uint_t flags);
static void pci_axq_hack_rep_get16(ddi_acc_impl_t *handle,
uint16_t *host_addr, uint16_t *dev_addr,
size_t repcount, uint_t flags);
static void pci_axq_hack_rep_get32(ddi_acc_impl_t *handle,
uint32_t *host_addr, uint32_t *dev_addr,
size_t repcount, uint_t flags);
static void pci_axq_hack_rep_get64(ddi_acc_impl_t *handle,
uint64_t *host_addr, uint64_t *dev_addr,
size_t repcount, uint_t flags);
static void pci_axq_hack_rep_put8(ddi_acc_impl_t *handle,
uint8_t *host_addr, uint8_t *dev_addr,
size_t repcount, uint_t flags);
static void pci_axq_hack_rep_put16(ddi_acc_impl_t *handle,
uint16_t *host_addr, uint16_t *dev_addr,
size_t repcount, uint_t flags);
static void pci_axq_hack_rep_put32(ddi_acc_impl_t *handle,
uint32_t *host_addr, uint32_t *dev_addr,
size_t repcount, uint_t flags);
static void pci_axq_hack_rep_put64(ddi_acc_impl_t *handle,
uint64_t *host_addr, uint64_t *dev_addr,
size_t repcount, uint_t flags);
/*
* On Sunfire 15k systems with AXQs less than 6.1
* we have to use special PIO routines that limit
* the number of outstanding PIOs. We setup the
* handle with pointers to our special functions
* after it has been succesfully mapped by our
* parent.
*/
void
pci_axq_pio_limit(pbm_t *pbm_p)
{
pci_t *pci_p = pbm_p->pbm_pci_p;
dev_info_t *dip = pci_p->pci_dip;
int (*axq_pio_workaround)(dev_info_t *) = NULL;
axq_pio_workaround =
(int (*)(dev_info_t *)) modgetsymvalue(
"starcat_axq_pio_workaround", 0);
if (axq_pio_workaround) {
pbm_p->pbm_pio_limit = (axq_pio_workaround)(dip);
pbm_p->pbm_pio_counter = pbm_p->pbm_pio_limit;
} else
pbm_p->pbm_pio_limit = 0;
}
void
pci_axq_setup(ddi_map_req_t *mp, pbm_t *pbm_p)
{
ddi_acc_hdl_t *hp;
ddi_acc_impl_t *ap;
if (mp->map_op != DDI_MO_MAP_LOCKED)
return;
if (!pbm_p->pbm_pio_limit)
return;
hp = (ddi_acc_hdl_t *)mp->map_handlep;
ap = (ddi_acc_impl_t *)hp->ah_platform_private;
ap->ahi_get8 = pci_axq_hack_get8;
ap->ahi_get16 = pci_axq_hack_get16;
ap->ahi_get32 = pci_axq_hack_get32;
ap->ahi_get64 = pci_axq_hack_get64;
ap->ahi_put8 = pci_axq_hack_put8;
ap->ahi_put16 = pci_axq_hack_put16;
ap->ahi_put32 = pci_axq_hack_put32;
ap->ahi_put64 = pci_axq_hack_put64;
ap->ahi_rep_get8 = pci_axq_hack_rep_get8;
ap->ahi_rep_get16 = pci_axq_hack_rep_get16;
ap->ahi_rep_get32 = pci_axq_hack_rep_get32;
ap->ahi_rep_get64 = pci_axq_hack_rep_get64;
ap->ahi_rep_put8 = pci_axq_hack_rep_put8;
ap->ahi_rep_put16 = pci_axq_hack_rep_put16;
ap->ahi_rep_put32 = pci_axq_hack_rep_put32;
ap->ahi_rep_put64 = pci_axq_hack_rep_put64;
hp->ah_bus_private = (void *)pbm_p;
}
/*
* get/put routines for SunFire 15K systems that have AXQ versions
* less than 6.1. These routines limit the number of outsanding
* PIOs issues across a PCI Bus.
*/
static uint8_t
pci_axq_hack_get8(ddi_acc_impl_t *handle, uint8_t *addr)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint8_t data;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
data = i_ddi_get8(handle, addr);
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
return (data);
}
static uint16_t
pci_axq_hack_get16(ddi_acc_impl_t *handle, uint16_t *addr)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint16_t data;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
data = i_ddi_swap_get16(handle, addr);
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
return (data);
}
static uint32_t
pci_axq_hack_get32(ddi_acc_impl_t *handle, uint32_t *addr)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t data;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
data = i_ddi_swap_get32(handle, addr);
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
return (data);
}
static uint64_t
pci_axq_hack_get64(ddi_acc_impl_t *handle, uint64_t *addr)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint64_t data;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
data = i_ddi_swap_get64(handle, addr);
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
return (data);
}
static void
pci_axq_hack_put8(ddi_acc_impl_t *handle, uint8_t *addr, uint8_t data)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
i_ddi_put8(handle, addr, data);
membar_sync();
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
}
static void
pci_axq_hack_put16(ddi_acc_impl_t *handle, uint16_t *addr, uint16_t data)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
i_ddi_swap_put16(handle, addr, data);
membar_sync();
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
}
static void
pci_axq_hack_put32(ddi_acc_impl_t *handle, uint32_t *addr, uint32_t data)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
i_ddi_swap_put32(handle, addr, data);
membar_sync();
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
}
static void
pci_axq_hack_put64(ddi_acc_impl_t *handle, uint64_t *addr, uint64_t data)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
i_ddi_swap_put64(handle, addr, data);
membar_sync();
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
}
static void
pci_axq_hack_rep_get8(ddi_acc_impl_t *handle, uint8_t *host_addr,
uint8_t *dev_addr, size_t repcount, uint_t flags)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
i_ddi_rep_get8(handle, host_addr, dev_addr, repcount, flags);
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
}
static void
pci_axq_hack_rep_get16(ddi_acc_impl_t *handle, uint16_t *host_addr,
uint16_t *dev_addr, size_t repcount, uint_t flags)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
i_ddi_swap_rep_get16(handle, host_addr, dev_addr, repcount, flags);
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
}
static void
pci_axq_hack_rep_get32(ddi_acc_impl_t *handle, uint32_t *host_addr,
uint32_t *dev_addr, size_t repcount, uint_t flags)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
i_ddi_swap_rep_get32(handle, host_addr, dev_addr, repcount, flags);
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
}
static void
pci_axq_hack_rep_get64(ddi_acc_impl_t *handle, uint64_t *host_addr,
uint64_t *dev_addr, size_t repcount, uint_t flags)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
i_ddi_swap_rep_get64(handle, host_addr, dev_addr, repcount, flags);
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
}
static void
pci_axq_hack_rep_put8(ddi_acc_impl_t *handle, uint8_t *host_addr,
uint8_t *dev_addr, size_t repcount, uint_t flags)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
while (repcount--) {
i_ddi_put8(handle, dev_addr, *host_addr);
membar_sync();
if (flags == DDI_DEV_AUTOINCR)
dev_addr++;
host_addr++;
}
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
}
static void
pci_axq_hack_rep_put16(ddi_acc_impl_t *handle, uint16_t *host_addr,
uint16_t *dev_addr, size_t repcount, uint_t flags)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
while (repcount--) {
i_ddi_put16(handle, dev_addr, *host_addr);
membar_sync();
if (flags == DDI_DEV_AUTOINCR)
dev_addr++;
host_addr++;
}
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
}
static void
pci_axq_hack_rep_put32(ddi_acc_impl_t *handle, uint32_t *host_addr,
uint32_t *dev_addr, size_t repcount, uint_t flags)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
while (repcount--) {
i_ddi_put32(handle, dev_addr, *host_addr);
membar_sync();
if (flags == DDI_DEV_AUTOINCR)
dev_addr++;
host_addr++;
}
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
}
static void
pci_axq_hack_rep_put64(ddi_acc_impl_t *handle, uint64_t *host_addr,
uint64_t *dev_addr, size_t repcount, uint_t flags)
{
pbm_t *pbm_p = (pbm_t *)handle->ahi_common.ah_bus_private;
uint32_t spl;
spl = ddi_enter_critical();
PIO_LIMIT_ENTER(pbm_p);
while (repcount--) {
i_ddi_put64(handle, dev_addr, *host_addr);
membar_sync();
if (flags == DDI_DEV_AUTOINCR)
dev_addr++;
host_addr++;
}
PIO_LIMIT_EXIT(pbm_p);
ddi_exit_critical(spl);
}