/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2011 Bayard G. Bell. All rights reserved.
*/
/*
* fcpci.c: Framework PCI fcode ops
*/
#include <sys/ddidmareq.h>
#include <sys/ndi_impldefs.h>
#include <sys/promimpl.h>
#include <sys/ddi_implfuncs.h>
int prom_get_fcode_size(char *);
int prom_get_fcode(char *, char *);
static int fcpci_indirect_map(dev_info_t *);
int fcpci_unloadable;
DMA_ATTR_V0, /* version number */
0x0, /* lowest usable address */
0xFFFFFFFFull, /* high DMA address range */
0xFFFFFFFFull, /* DMA counter register */
1, /* DMA address alignment */
1, /* DMA burstsizes */
1, /* min effective DMA size */
0xFFFFFFFFull, /* max DMA xfer size */
0xFFFFFFFFull, /* segment boundary */
1, /* s/g list length */
1, /* granularity of device */
0 /* DMA transfer flags */
};
/*
* Module linkage information for the kernel.
*/
&mod_miscops, "FCode pci bus functions"
};
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
if (fcpci_unloadable)
return (mod_remove(&modlinkage));
return (EBUSY);
}
int
{
}
struct pfc_ops_v {
char *svc_name;
fc_ops_t *f;
};
{ "map-in", pfc_map_in},
{ "map-out", pfc_map_out},
{ "dma-map-in", pfc_dma_map_in},
{ "dma-map-out", pfc_dma_map_out},
{ "dma-sync", pfc_dma_sync},
{ "rx@", pfc_register_fetch},
{ "rl@", pfc_register_fetch},
{ "rw@", pfc_register_fetch},
{ "rb@", pfc_register_fetch},
{ "rx!", pfc_register_store},
{ "rl!", pfc_register_store},
{ "rw!", pfc_register_store},
{ "rb!", pfc_register_store},
{ "config-l@", pfc_config_fetch},
{ "config-w@", pfc_config_fetch},
{ "config-b@", pfc_config_fetch},
{ "config-l!", pfc_config_store},
{ "config-w!", pfc_config_store},
{ "config-b!", pfc_config_store},
};
};
struct pci_ops_bus_args *up)
{
phandle_t h;
unit_address, NULL);
if (unit_address) {
char *buf;
}
/*
* Add the child's nodeid to our table...
*/
return (rp);
}
void
{
if (rp->next_handle)
if (rp->unit_address)
/*
* Release all the resources from the resource list
* XXX: We don't handle 'unknown' types, but we don't create them.
*/
case RT_MAP:
break;
case RT_DMA:
/* DMA has to be freed up at exit time */
break;
default:
break;
}
}
}
int
{
/*
* First try the generic fc_ops. If the ops is a shared op,
* also call our local function.
*/
return (0);
}
return (-1);
}
/*
* Create a dma mapping for a given user address.
*/
static int
{
int error;
/*
* XXX: It's not clear what we should do with a non-cacheable request
*/
#ifdef notdef
#endif
/*
* Set up the address space for physio from userland
*/
if (error) {
}
NULL, &h);
if (error != DDI_SUCCESS) {
}
&c, &ccnt);
}
if (error != DDI_SUCCESS) {
}
}
c.dmac_address);
/*
* Now we have to log this resource saving the handle and buf header
*/
ip->fc_dma_handle = h;
}
static int
{
void *virt;
int error;
/*
* Find if this virt is 'within' a request we know about
*/
continue;
continue;
break;
}
"known dma mapping"));
/*
* We know about this request, so we trust it enough to sync it.
* Unfortunately, we don't know which direction, so we'll do
* both directions.
*/
if (error)
}
static int
{
void *virt;
int e;
/*
* Find if this virt matches a request we know about
*/
continue;
continue;
continue;
break;
}
"known dma mapping"));
/*
* ddi_dma_unbind_handle does an implied sync ...
*/
if (e != DDI_SUCCESS) {
}
/*
* Tear down the physio mappings
*/
/*
* remove the resource from the list and release it.
*/
}
static struct fc_resource *
{
break;
return (ip);
}
static int
{
int e;
/*
* Free the dma handle
*/
if (e != DDI_SUCCESS) {
"ddi_dma_unbind failed!\n");
}
/*
* Tear down the userland mapping and free the buf header
*/
}
}
static int
{
int error;
p.pci_size_hi = 0;
/*
* Fcode is expecting the bytes are not swapped.
*/
/*
* First We need to allocate the PCI Resource.
*/
if (error) {
}
if (error) {
}
/*
* Log this resource ...
*/
ip->fc_map_handle = h;
*ph = p;
}
static int
{
/*
* Find if this request matches a mapping resource we set up.
*/
continue;
continue;
break;
}
"known mapping"));
/*
* remove the resource from the list and release it.
*/
}
static int
{
int error;
uint64_t x;
uint32_t l;
uint16_t w;
uint8_t b;
/*
* Determine the access width .. we can switch on the 2nd
* character of the name which is "rx@", "rl@", "rb@" or "rw@"
*/
switch (*(name + 1)) {
case 'x': len = sizeof (x); break;
case 'l': len = sizeof (l); break;
case 'w': len = sizeof (w); break;
case 'b': len = sizeof (b); break;
}
/*
* Check the alignment ...
*/
/*
* Find if this virt is 'within' a request we know about
*/
continue;
break;
}
"known mapping"));
/*
* beyond the prototype ... we assume that we have hardware
* byte swapping enabled for pci register access here which
* is a huge dependency on the current implementation.
*/
switch (len) {
case sizeof (x):
break;
case sizeof (l):
break;
case sizeof (w):
break;
case sizeof (b):
break;
}
if (error) {
}
switch (len) {
}
}
static int
{
int error;
uint64_t x;
uint32_t l;
uint16_t w;
uint8_t b;
/*
* Determine the access width .. we can switch on the 2nd
* character of the name which is "rl!", "rb!" or "rw!"
*/
switch (*(name + 1)) {
}
/*
* Check the alignment ...
*/
/*
* Find if this virt is 'within' a request we know about
*/
continue;
break;
}
"known mapping"));
/*
* beyond the prototype ... we assume that we have hardware
* byte swapping enabled for pci register access here which
* is a huge dependency on the current implementation.
*/
switch (len) {
case sizeof (x):
break;
case sizeof (l):
break;
case sizeof (w):
break;
case sizeof (b):
break;
}
if (error) {
}
}
static int
{
uint16_t w;
uint8_t b;
/*
* Construct a config address pci reg property from the args.
* arg[0] is the configuration address.
*/
p.pci_phys_mid = p.pci_phys_low = 0;
p.pci_size_hi = p.pci_size_low = 0;
/*
* Verify that the address is a configuration space address
* ss must be zero.
*/
"invalid config addr: %x\n", p.pci_phys_hi);
}
/*
* Extract the register number from the config address and
* remove the register number from the physical address.
*/
p.pci_phys_hi &= PCI_BDF_bits;
/*
* Determine the access width .. we can switch on the 9th
* character of the name which is "config-{l,w,b}@"
*/
switch (*(name + 7)) {
case 'l': len = sizeof (l); break;
case 'w': len = sizeof (w); break;
case 'b': len = sizeof (b); break;
}
/*
* Verify that the access is properly aligned
*/
/*
* Map in configuration space (temporarily)
*/
if (error) {
}
if (flags & PCICFG_CONF_INDIRECT_MAP) {
error = DDI_SUCCESS;
} else
if (error == DDI_SUCCESS)
error = DDI_FAILURE;
}
if (error != DDI_SUCCESS) {
}
/*
* beyond the prototype ... we assume that we have hardware
* byte swapping enabled for pci register access here which
* is a huge dependency on the current implementation.
*/
switch (len) {
case sizeof (l):
break;
case sizeof (w):
break;
case sizeof (b):
break;
}
/*
* Remove the temporary config space mapping
*/
pci_unmap_phys(&h, &p);
if (error) {
}
switch (len) {
}
}
static int
{
uint16_t w;
uint8_t b;
/*
* Construct a config address pci reg property from the args.
* arg[0] is the configuration address. arg[1] is the data.
*/
p.pci_phys_mid = p.pci_phys_low = 0;
p.pci_size_hi = p.pci_size_low = 0;
/*
* Verify that the address is a configuration space address
* ss must be zero.
*/
"invalid config addr: %x\n", p.pci_phys_hi);
}
/*
* Extract the register number from the config address and
* remove the register number from the physical address.
*/
p.pci_phys_hi &= PCI_BDF_bits;
/*
* Determine the access width .. we can switch on the 8th
* character of the name which is "config-{l,w,b}@"
*/
switch (*(name + 7)) {
}
/*
* Verify that the access is properly aligned
*/
/*
* Map in configuration space (temporarily)
*/
if (error) {
}
if (flags & PCICFG_CONF_INDIRECT_MAP) {
error = DDI_SUCCESS;
} else
if (error == DDI_SUCCESS)
error = DDI_FAILURE;
}
if (error != DDI_SUCCESS) {
}
/*
* beyond the prototype ... we assume that we have hardware
* byte swapping enabled for pci register access here which
* is a huge dependency on the current implementation.
*/
switch (len) {
case sizeof (l):
break;
case sizeof (w):
break;
case sizeof (b):
break;
}
/*
* Remove the temporary config space mapping
*/
pci_unmap_phys(&h, &p);
if (error) {
}
}
static int
{
status = 0;
} else {
fcode_len)) {
"to copy out fcode image\n");
status = 0;
}
}
}
}
static int
{
char *name;
int len;
len = 0;
} else {
}
}
/*
* Return the physical probe address: lo=0, mid=0, hi-config-addr
*/
static int
{
}
/*
* Return the phys.hi component of the probe address.
*/
static int
{
}
static int
{
fc_phandle_t h;
}
int
{
int rval;
req.ra_boundbase = 0;
return (rval);
}
int
{
int rval;
req.ra_boundbase = 0;
return (rval);
}
int
{
l = phys_spec.pci_size_low;
&assigned_len) == DDI_PROP_SUCCESS) {
/*
* Walk through the assigned-addresses entries. If there is
* a match, there is no need to allocate the resource.
*/
for (i = 0; i < entries; i++) {
if (assigned[i].pci_size_low >=
return (0);
}
/*
* Fcode wants to assign more than what
* probe found.
*/
/*
* Go on to allocate resources.
*/
break;
}
/*
* Check if Fcode wants to map using different
* NPT bits.
*/
/*
* It is an error to change SS bits
*/
"ss bits in reg %x -- %x",
assigned[i].pci_phys_hi,
}
/*
* Allocate enough
*/
phys_spec.pci_size_low = l;
/*
* Go on to allocate resources.
*/
break;
}
}
}
/*
* Map in configuration space (temporarily)
*/
return (1);
}
if (flags & PCICFG_CONF_INDIRECT_MAP) {
error = DDI_SUCCESS;
} else
if (error == DDI_SUCCESS)
error = DDI_FAILURE;
}
if (error != DDI_SUCCESS) {
return (1);
}
request.ra_boundbase = 0;
/* allocate memory space from the allocator */
NDI_RA_PASS) != NDI_SUCCESS) {
pci_unmap_phys(&h, &config);
return (1);
}
/* program the low word */
} else {
case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
/*
* If it is a non relocatable address,
* then specify the address we want.
*/
}
/* allocate memory space from the allocator */
NDI_RA_PASS) != NDI_SUCCESS) {
pci_unmap_phys(&h, &config);
"non relocatable address 0x%p\n",
return (1);
}
"64 addr = [0x%x.%x] len [0x%x]\n",
alen);
/* program the low word */
/* program the high word with value zero */
v += 4;
/*
* currently support 32b address space
* assignments only.
*/
break;
case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
/*
* If it is a non relocatable address,
* then specify the address we want.
*/
}
/* allocate memory space from the allocator */
NDI_RA_PASS) != NDI_SUCCESS) {
pci_unmap_phys(&h, &config);
"non relocatable address 0x%p\n",
return (1);
}
"32 addr = [0x%x.%x] len [0x%x]\n",
alen);
/* program the low word */
break;
case PCI_REG_ADDR_G(PCI_ADDR_IO):
/*
* If it is a non relocatable address,
* then specify the address we want.
*/
}
/* allocate I/O space from the allocator */
NDI_RA_PASS) != NDI_SUCCESS) {
pci_unmap_phys(&h, &config);
"non relocatable IO Space 0x%p\n",
return (1);
}
"I/O addr = [0x%x.%x] len [0x%x]\n",
alen);
break;
default:
pci_unmap_phys(&h, &config);
return (1);
} /* switch */
}
/*
* Now that memory locations are assigned,
* update the assigned address property.
*/
pci_unmap_phys(&h, &config);
return (1);
}
pci_unmap_phys(&h, &config);
return (0);
}
int
{
/*
* Map in configuration space (temporarily)
*/
return (1);
}
if (flags & PCICFG_CONF_INDIRECT_MAP) {
error = DDI_SUCCESS;
} else
if (error == DDI_SUCCESS)
error = DDI_FAILURE;
}
if (error != DDI_SUCCESS) {
return (1);
}
/*
* Pick up the size to be freed. It may be different from
* what probe finds.
*/
l = phys_spec.pci_size_low;
/* free memory back to the allocator */
l, NDI_RA_TYPE_MEM,
NDI_RA_PASS) != NDI_SUCCESS) {
pci_unmap_phys(&h, &config);
return (1);
}
/* Unmap the BAR by writing a zero */
} else {
/* free memory back to the allocator */
l, NDI_RA_TYPE_MEM,
NDI_RA_PASS) != NDI_SUCCESS) {
pci_unmap_phys(&h, &config);
return (1);
}
break;
/* free memory back to the allocator */
l, NDI_RA_TYPE_MEM,
NDI_RA_PASS) != NDI_SUCCESS) {
pci_unmap_phys(&h, &config);
return (1);
}
break;
/* free I/O space back to the allocator */
l, NDI_RA_TYPE_IO,
NDI_RA_PASS) != NDI_SUCCESS) {
pci_unmap_phys(&h, &config);
return (1);
}
break;
default:
pci_unmap_phys(&h, &config);
return (1);
} /* switch */
}
/*
* Now that memory locations are assigned,
* update the assigned address property.
*/
pci_unmap_phys(&h, &config);
return (1);
}
pci_unmap_phys(&h, &config);
return (0);
}
int
{
int result;
hp->ah_rnumber = 0;
if (result != DDI_SUCCESS) {
} else {
}
return (result);
}
void
{
}
int
{
int alen;
switch (status) {
case DDI_PROP_SUCCESS:
break;
case DDI_PROP_NO_MEMORY:
return (1);
default:
"assigned-addresses", (int *)newone,
sizeof (*newone)/sizeof (int));
return (0);
}
/*
* Allocate memory for the existing
* assigned-addresses(s) plus one and then
* build it.
*/
/*
* Write out the new "assigned-addresses" spec
*/
"assigned-addresses", (int *)newreg,
return (0);
}
int
{
switch (status) {
case DDI_PROP_SUCCESS:
break;
case DDI_PROP_NO_MEMORY:
return (1);
default:
return (0);
}
/*
* Search for the memory being removed.
*/
for (i = 0; i < num_entries; i++) {
if (new_len == 0) {
"assigned-addresses");
break;
}
if ((new_len - (i * sizeof (pci_regspec_t)))
== 0) {
"%x removed from property (last entry)\n",
} else {
(void *)(assigned + i),
(new_len - (i * sizeof (pci_regspec_t))));
"%x removed from property\n",
}
(new_len/sizeof (int)));
break;
}
}
return (0);
}
/*
* we recognize the non transparent bridge child nodes with the
* following property. This is specific to this implementation only.
* This property is specific to AP nodes only.
*/
/*
* the following property must be defined for the node either by
* the driver or the OBP.
*/
/*
* this function is called only for SPARC platforms, where we may have
* a mix n' match of direct vs indirectly mapped configuration space.
*/
/*ARGSUSED*/
static int
{
rc = DDI_SUCCESS;
else
rc = DDI_SUCCESS;
return (rc);
}