#include <sys/cpuvar.h>
#include <sys/ddi_subrdefs.h>
#include <sys/machsystm.h>
#include <sys/sunndi.h>
#include <sys/sysmacros.h>
#include <sys/ontrap.h>
#include <vm/seg_kmem.h>
#include <sys/membar.h>
#include <sys/dditypes.h>
#include <sys/ndifm.h>
#include <sys/fm/io/ddi.h>
#include <sys/ivintr.h>
#include <sys/bootconf.h>
#include <sys/conf.h>
#include <sys/ethernet.h>
#include <sys/idprom.h>
#include <sys/promif.h>
#include <sys/prom_plat.h>
#include <sys/systeminfo.h>
#include <sys/fpu/fpusystm.h>
#include <sys/vm.h>
#include <sys/ddi_isa.h>
#include <sys/modctl.h>
dev_info_t *get_intr_parent(dev_info_t *, dev_info_t *,
ddi_intr_handle_impl_t *);
#pragma weak get_intr_parent
int process_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
ddi_intr_handle_impl_t *, void *);
#pragma weak process_intr_ops
void cells_1275_copy(prop_1275_cell_t *, prop_1275_cell_t *, int32_t);
prop_1275_cell_t *cells_1275_cmp(prop_1275_cell_t *, prop_1275_cell_t *,
int32_t len);
#pragma weak cells_1275_copy
static int
get_prop_int_array(dev_info_t *di, char *pname, int **pval, uint_t *plen)
{
int ret;
if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, di,
DDI_PROP_DONTPASS, pname, pval, plen)) == DDI_PROP_SUCCESS) {
*plen = (*plen) * (uint_t)sizeof (int);
}
return (ret);
}
struct ddi_parent_private_data *
init_regspec_64(dev_info_t *dip)
{
struct ddi_parent_private_data *pd;
dev_info_t *parent;
int size_cells;
pd = ddi_get_parent_data(dip);
if ((pd == NULL) || (pd->par_nreg == 0)) {
return (pd);
}
parent = ddi_get_parent(dip);
size_cells = ddi_prop_get_int(DDI_DEV_T_ANY, parent,
DDI_PROP_DONTPASS, "#size-cells", 1);
if (size_cells != 1) {
int n, j;
struct regspec *irp;
struct reg_64 {
uint_t addr_hi, addr_lo, size_hi, size_lo;
};
struct reg_64 *r64_rp;
struct regspec *rp;
uint_t len = 0;
int *reg_prop;
ASSERT(size_cells == 2);
(void) ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, OBP_REG, ®_prop, &len);
ASSERT(len != 0);
n = sizeof (struct reg_64) / sizeof (int);
n = len / n;
irp = rp = (struct regspec *)reg_prop;
r64_rp = (struct reg_64 *)pd->par_reg;
for (j = 0; j < n; ++j, ++rp, ++r64_rp) {
ASSERT(r64_rp->size_hi == 0);
rp->regspec_bustype = r64_rp->addr_hi;
rp->regspec_addr = r64_rp->addr_lo;
rp->regspec_size = r64_rp->size_lo;
}
ddi_prop_free((void *)pd->par_reg);
pd->par_nreg = n;
pd->par_reg = irp;
}
return (pd);
}
void
make_ddi_ppd(dev_info_t *child, struct ddi_parent_private_data **ppd)
{
struct ddi_parent_private_data *pdptr;
int *reg_prop, *rng_prop;
uint_t reg_len = 0, rng_len = 0;
dev_info_t *parent;
int parent_addr_cells, parent_size_cells;
int child_addr_cells, child_size_cells;
*ppd = pdptr = kmem_zalloc(sizeof (*pdptr), KM_SLEEP);
if ((parent = ddi_get_parent(child)) == NULL)
return;
if ((get_prop_int_array(child, OBP_REG, ®_prop, ®_len)
== DDI_PROP_SUCCESS) && (reg_len != 0)) {
pdptr->par_nreg = (int)(reg_len / sizeof (struct regspec));
pdptr->par_reg = (struct regspec *)reg_prop;
}
if ((parent = ddi_get_parent(child)) == NULL)
return;
child_addr_cells = ddi_prop_get_int(DDI_DEV_T_ANY, child,
DDI_PROP_DONTPASS, "#address-cells", 2);
child_size_cells = ddi_prop_get_int(DDI_DEV_T_ANY, child,
DDI_PROP_DONTPASS, "#size-cells", 1);
parent_addr_cells = ddi_prop_get_int(DDI_DEV_T_ANY, parent,
DDI_PROP_DONTPASS, "#address-cells", 2);
parent_size_cells = ddi_prop_get_int(DDI_DEV_T_ANY, parent,
DDI_PROP_DONTPASS, "#size-cells", 1);
if (child_addr_cells != 2 || parent_addr_cells != 2 ||
(child_size_cells != 1 && parent_size_cells != 1)) {
NDI_CONFIG_DEBUG((CE_NOTE, "!ranges not made in parent data; "
"#address-cells or #size-cells have non-default value"));
return;
}
if (get_prop_int_array(child, OBP_RANGES, &rng_prop, &rng_len)
== DDI_PROP_SUCCESS) {
pdptr->par_nrng = rng_len / (int)(sizeof (struct rangespec));
pdptr->par_rng = (struct rangespec *)rng_prop;
}
}
void
impl_free_ddi_ppd(dev_info_t *dip)
{
struct ddi_parent_private_data *pdptr = ddi_get_parent_data(dip);
if (pdptr == NULL)
return;
if (pdptr->par_nrng != 0)
ddi_prop_free((void *)pdptr->par_rng);
if (pdptr->par_nreg != 0)
ddi_prop_free((void *)pdptr->par_reg);
kmem_free(pdptr, sizeof (*pdptr));
ddi_set_parent_data(dip, NULL);
}
static int
impl_sunbus_name_child(dev_info_t *child, char *name, int namelen)
{
struct ddi_parent_private_data *pdptr;
struct regspec *rp;
if (ddi_get_parent_data(child) == NULL) {
make_ddi_ppd(child, &pdptr);
ddi_set_parent_data(child, pdptr);
}
name[0] = '\0';
if (sparc_pd_getnreg(child) == 0) {
return (DDI_SUCCESS);
}
rp = sparc_pd_getreg(child, 0);
(void) snprintf(name, namelen, "%x,%x",
rp->regspec_bustype, rp->regspec_addr);
return (DDI_SUCCESS);
}
int
impl_ddi_sunbus_initchild(dev_info_t *child)
{
char name[MAXNAMELEN];
(void) impl_sunbus_name_child(child, name, MAXNAMELEN);
ddi_set_name_addr(child, name);
if ((ndi_dev_is_persistent_node(child) == 0) &&
(ndi_merge_node(child, impl_sunbus_name_child) == DDI_SUCCESS)) {
impl_ddi_sunbus_removechild(child);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
impl_ddi_sunbus_removechild(dev_info_t *dip)
{
impl_free_ddi_ppd(dip);
ddi_set_name_addr(dip, NULL);
impl_rem_dev_props(dip);
}
void
cells_1275_copy(prop_1275_cell_t *from, prop_1275_cell_t *to, int32_t len)
{
int i;
for (i = 0; i < len; i++)
*to = *from;
}
prop_1275_cell_t *
cells_1275_cmp(prop_1275_cell_t *cell1, prop_1275_cell_t *cell2, int32_t len)
{
prop_1275_cell_t *match_cell = 0;
int32_t i;
for (i = 0; i < len; i++)
if (cell1[i] != cell2[i]) {
match_cell = &cell1[i];
break;
}
return (match_cell);
}
dev_info_t *
get_intr_parent(dev_info_t *pdip, dev_info_t *dip, ddi_intr_handle_impl_t *hdlp)
{
prop_1275_cell_t *imap, *imap_mask, *scan, *reg_p, *match_req;
int32_t imap_sz, imap_cells, imap_scan_cells, imap_mask_sz,
addr_cells, intr_cells, reg_len, i, j;
int32_t match_found = 0;
dev_info_t *intr_parent_dip = NULL;
uint32_t *intr = &hdlp->ih_vector;
uint32_t nodeid;
#ifdef DEBUG
static int debug = 0;
#endif
if ((nodeid = ddi_getprop(DDI_DEV_T_ANY, dip, 0,
"interrupt-parent", -1)) != -1) {
intr_parent_dip = e_ddi_nodeid_to_dip(nodeid);
ASSERT(intr_parent_dip);
if (i_ddi_attach_node_hierarchy(intr_parent_dip)
!= DDI_SUCCESS) {
ndi_rele_devi(intr_parent_dip);
return (NULL);
}
return (intr_parent_dip);
}
if (ddi_getlongprop(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
"interrupt-map", (caddr_t)&imap, &imap_sz)
!= DDI_PROP_SUCCESS) {
ndi_hold_devi(pdip);
return (pdip);
}
if (ddi_getlongprop(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
"interrupt-map-mask", (caddr_t)&imap_mask, &imap_mask_sz)
!= DDI_PROP_SUCCESS) {
ASSERT(intr_parent_dip == NULL);
goto exit2;
}
addr_cells = ddi_getprop(DDI_DEV_T_ANY, pdip, 0,
"#address-cells", 2);
intr_cells = ddi_getprop(DDI_DEV_T_ANY, pdip, 0,
"#interrupt-cells", 1);
if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
(caddr_t)®_p, ®_len) != DDI_SUCCESS) {
ASSERT(intr_parent_dip == NULL);
goto exit3;
}
match_req = kmem_alloc(CELLS_1275_TO_BYTES(addr_cells) +
CELLS_1275_TO_BYTES(intr_cells), KM_SLEEP);
for (i = 0; i < addr_cells; i++)
match_req[i] = (reg_p[i] & imap_mask[i]);
for (j = 0; j < intr_cells; i++, j++)
match_req[i] = (intr[j] & imap_mask[i]);
imap_cells = BYTES_TO_1275_CELLS(imap_sz);
#ifdef DEBUG
if (debug)
prom_printf("reg cell size 0x%x, intr cell size 0x%x, "
"match_request 0x%p, imap 0x%p\n", addr_cells, intr_cells,
(void *)match_req, (void *)imap);
#endif
for (scan = imap, imap_scan_cells = i = 0;
imap_scan_cells < imap_cells; scan += i, imap_scan_cells += i) {
int new_intr_cells;
i = addr_cells + intr_cells;
ASSERT(intr_parent_dip == NULL);
intr_parent_dip = e_ddi_nodeid_to_dip((uint_t)scan[i++]);
ASSERT(intr_parent_dip != 0);
#ifdef DEBUG
if (debug)
prom_printf("scan 0x%p\n", (void *)scan);
#endif
new_intr_cells = ddi_getprop(DDI_DEV_T_ANY, intr_parent_dip, 0,
"#interrupts-cells", 1);
if (cells_1275_cmp(match_req, scan, addr_cells + intr_cells)
== 0) {
uint32_t *intr;
match_found = 1;
if (i_ddi_attach_node_hierarchy(intr_parent_dip)
!= DDI_SUCCESS) {
ndi_rele_devi(intr_parent_dip);
intr_parent_dip = (dev_info_t *)NULL;
goto exit4;
}
intr = kmem_alloc(
CELLS_1275_TO_BYTES(new_intr_cells), KM_SLEEP);
for (j = 0; j < new_intr_cells; j++, i++)
intr[j] = scan[i];
cells_1275_copy(intr, &hdlp->ih_vector, new_intr_cells);
kmem_free(intr, CELLS_1275_TO_BYTES(new_intr_cells));
#ifdef DEBUG
if (debug)
prom_printf("dip 0x%p\n",
(void *)intr_parent_dip);
#endif
break;
} else {
#ifdef DEBUG
if (debug)
prom_printf("dip 0x%p\n",
(void *)intr_parent_dip);
#endif
ndi_rele_devi(intr_parent_dip);
intr_parent_dip = NULL;
i += new_intr_cells;
}
}
if (!match_found) {
ndi_hold_devi(pdip);
ASSERT(intr_parent_dip == NULL);
intr_parent_dip = pdip;
}
ASSERT(intr_parent_dip != NULL);
exit4:
kmem_free(reg_p, reg_len);
kmem_free(match_req, CELLS_1275_TO_BYTES(addr_cells) +
CELLS_1275_TO_BYTES(intr_cells));
exit3:
kmem_free(imap_mask, imap_mask_sz);
exit2:
kmem_free(imap, imap_sz);
return (intr_parent_dip);
}
int
process_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t op,
ddi_intr_handle_impl_t *hdlp, void *result)
{
int ret = DDI_FAILURE;
if (NEXUS_HAS_INTR_OP(pdip)) {
ret = (*(DEVI(pdip)->devi_ops->devo_bus_ops->
bus_intr_op)) (pdip, rdip, op, hdlp, result);
} else {
cmn_err(CE_WARN, "Failed to process interrupt "
"for %s%d due to down-rev nexus driver %s%d",
ddi_get_name(rdip), ddi_get_instance(rdip),
ddi_get_name(pdip), ddi_get_instance(pdip));
}
return (ret);
}
uint_t
softlevel1(caddr_t arg)
{
softint();
return (1);
}
struct autovec *const vectorlist[] = { 0 };
const uint_t maxautovec = (sizeof (vectorlist) / sizeof (vectorlist[0]));
int
exclude_settrap(int lvl)
{
return (1);
}
int
exclude_level(int lvl)
{
return ((lvl < 1) || (lvl > 15));
}
int
i_ddi_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op,
ddi_intr_handle_impl_t *hdlp, void *result)
{
dev_info_t *pdip = ddi_get_parent(dip);
int ret = DDI_FAILURE;
if (pdip == NULL)
return (DDI_FAILURE);
if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
return (process_intr_ops(pdip, rdip, op, hdlp, result));
if (hdlp->ih_vector == 0)
hdlp->ih_vector = i_ddi_get_inum(rdip, hdlp->ih_inum);
if (hdlp->ih_pri == 0)
hdlp->ih_pri = i_ddi_get_intr_pri(rdip, hdlp->ih_inum);
switch (op) {
case DDI_INTROP_ADDISR:
case DDI_INTROP_REMISR:
case DDI_INTROP_GETTARGET:
case DDI_INTROP_SETTARGET:
case DDI_INTROP_ENABLE:
case DDI_INTROP_DISABLE:
case DDI_INTROP_BLOCKENABLE:
case DDI_INTROP_BLOCKDISABLE:
if ((pdip = get_intr_parent(pdip, dip, hdlp)) == NULL)
goto done;
}
ret = process_intr_ops(pdip, rdip, op, hdlp, result);
done:
switch (op) {
case DDI_INTROP_ADDISR:
case DDI_INTROP_REMISR:
case DDI_INTROP_ENABLE:
case DDI_INTROP_DISABLE:
case DDI_INTROP_BLOCKENABLE:
case DDI_INTROP_BLOCKDISABLE:
if (pdip)
ndi_rele_devi(pdip);
}
hdlp->ih_vector = 0;
return (ret);
}
int
i_ddi_add_ivintr(ddi_intr_handle_impl_t *hdlp)
{
if ((hdlp->ih_pri < 1) || (hdlp->ih_pri > PIL_MAX))
hdlp->ih_pri = 1;
VERIFY(add_ivintr(hdlp->ih_vector, hdlp->ih_pri,
(intrfunc)hdlp->ih_cb_func, hdlp->ih_cb_arg1,
hdlp->ih_cb_arg2, NULL) == 0);
return (DDI_SUCCESS);
}
void
i_ddi_rem_ivintr(ddi_intr_handle_impl_t *hdlp)
{
VERIFY(rem_ivintr(hdlp->ih_vector, hdlp->ih_pri) == 0);
}
uint32_t
i_ddi_get_inum(dev_info_t *dip, uint_t inumber)
{
int32_t intrlen, intr_cells, max_intrs;
prop_1275_cell_t *ip, intr_sz;
uint32_t intr = 0;
if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS |
DDI_PROP_CANSLEEP,
"interrupts", (caddr_t)&ip, &intrlen) == DDI_SUCCESS) {
intr_cells = ddi_getprop(DDI_DEV_T_ANY, dip, 0,
"#interrupt-cells", 1);
intr_sz = CELLS_1275_TO_BYTES(intr_cells);
max_intrs = intrlen / intr_sz;
if (inumber < max_intrs) {
prop_1275_cell_t *intrp = ip;
intrp += (inumber * intr_cells);
cells_1275_copy(intrp, &intr, intr_cells);
}
kmem_free(ip, intrlen);
}
return (intr);
}
uint32_t
i_ddi_get_intr_pri(dev_info_t *dip, uint_t inumber)
{
uint32_t *intr_prio_p;
uint32_t pri = 0;
int32_t i;
if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"interrupt-priorities", (caddr_t)&intr_prio_p,
&i) == DDI_SUCCESS) {
if (inumber < (i / sizeof (int32_t)))
pri = intr_prio_p[inumber];
kmem_free(intr_prio_p, i);
}
return (pri);
}
int
i_ddi_get_intx_nintrs(dev_info_t *dip)
{
int32_t intrlen;
prop_1275_cell_t intr_sz;
prop_1275_cell_t *ip;
int32_t ret = 0;
if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS |
DDI_PROP_CANSLEEP,
"interrupts", (caddr_t)&ip, &intrlen) == DDI_SUCCESS) {
intr_sz = ddi_getprop(DDI_DEV_T_ANY, dip, 0,
"#interrupt-cells", 1);
intr_sz = CELLS_1275_TO_BYTES(intr_sz);
ret = intrlen / intr_sz;
kmem_free(ip, intrlen);
}
return (ret);
}
int
i_ddi_add_softint(ddi_softint_hdl_impl_t *hdlp)
{
if ((hdlp->ih_private = (void *)add_softintr(hdlp->ih_pri,
hdlp->ih_cb_func, hdlp->ih_cb_arg1, SOFTINT_ST)) == NULL)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
void
i_ddi_remove_softint(ddi_softint_hdl_impl_t *hdlp)
{
ASSERT(hdlp->ih_private != NULL);
if (rem_softintr((uint64_t)hdlp->ih_private) == 0)
hdlp->ih_private = NULL;
}
int
i_ddi_trigger_softint(ddi_softint_hdl_impl_t *hdlp, void *arg2)
{
int ret;
ASSERT(hdlp->ih_private != NULL);
if ((ret = update_softint_arg2((uint64_t)hdlp->ih_private, arg2)) == 0)
setsoftint((uint64_t)hdlp->ih_private);
return (ret ? DDI_EPENDING : DDI_SUCCESS);
}
int
i_ddi_set_softint_pri(ddi_softint_hdl_impl_t *hdlp, uint_t old_pri)
{
int ret;
ASSERT(hdlp->ih_private != NULL);
ret = update_softint_pri((uint64_t)hdlp->ih_private, hdlp->ih_pri);
return (ret ? DDI_FAILURE : DDI_SUCCESS);
}
void
i_ddi_alloc_intr_phdl(ddi_intr_handle_impl_t *hdlp)
{
}
void
i_ddi_free_intr_phdl(ddi_intr_handle_impl_t *hdlp)
{
}
void
i_ddi_devacc_to_hatacc(ddi_device_acc_attr_t *devaccp, uint_t *hataccp)
{
if (devaccp != NULL) {
if (devaccp->devacc_attr_endian_flags == DDI_STRUCTURE_LE_ACC) {
*hataccp &= ~HAT_ENDIAN_MASK;
*hataccp |= HAT_STRUCTURE_LE;
}
}
}
boolean_t
i_ddi_check_cache_attr(uint_t flags)
{
uint_t cache_attr = IOMEM_CACHE_ATTR(flags);
if ((cache_attr != 0) && !ISP2(cache_attr))
return (B_FALSE);
if (cache_attr & IOMEM_DATA_CACHED)
return (B_TRUE);
else
return (B_FALSE);
}
void
i_ddi_cacheattr_to_hatacc(uint_t flags, uint_t *hataccp)
{
uint_t cache_attr = IOMEM_CACHE_ATTR(flags);
static char *fname = "i_ddi_cacheattr_to_hatacc";
#if defined(lint)
*hataccp = *hataccp;
#endif
switch (cache_attr) {
case IOMEM_DATA_CACHED:
break;
case IOMEM_DATA_UNCACHED:
case IOMEM_DATA_UC_WR_COMBINE:
default:
cmn_err(CE_WARN, "%s: cache_attr=0x%x is ignored.",
fname, cache_attr);
}
}
static vmem_t *little_endian_arena;
static vmem_t *big_endian_arena;
static void *
segkmem_alloc_le(vmem_t *vmp, size_t size, int flag)
{
return (segkmem_xalloc(vmp, NULL, size, flag, HAT_STRUCTURE_LE,
segkmem_page_create, NULL));
}
static void *
segkmem_alloc_be(vmem_t *vmp, size_t size, int flag)
{
return (segkmem_xalloc(vmp, NULL, size, flag, HAT_STRUCTURE_BE,
segkmem_page_create, NULL));
}
void
ka_init(void)
{
little_endian_arena = vmem_create("little_endian", NULL, 0, 1,
segkmem_alloc_le, segkmem_free, heap_arena, 0, VM_SLEEP);
big_endian_arena = vmem_create("big_endian", NULL, 0, 1,
segkmem_alloc_be, segkmem_free, heap_arena, 0, VM_SLEEP);
}
static void *
kalloca(size_t size, size_t align, int cansleep, uint_t endian_flags)
{
size_t *addr, *raddr, rsize;
size_t hdrsize = 4 * sizeof (size_t);
align = MAX(align, hdrsize);
ASSERT((align & (align - 1)) == 0);
rsize = size + hdrsize + align;
if (endian_flags == DDI_STRUCTURE_LE_ACC) {
raddr = vmem_alloc(little_endian_arena, rsize,
cansleep ? VM_SLEEP : VM_NOSLEEP);
} else {
raddr = vmem_alloc(big_endian_arena, rsize,
cansleep ? VM_SLEEP : VM_NOSLEEP);
}
if (raddr == NULL)
return (NULL);
addr = (size_t *)P2ROUNDUP((uintptr_t)raddr + hdrsize, align);
ASSERT((uintptr_t)addr + size - (uintptr_t)raddr <= rsize);
addr[-3] = (size_t)endian_flags;
addr[-2] = (size_t)raddr;
addr[-1] = rsize;
return (addr);
}
static void
kfreea(void *addr)
{
size_t *saddr = addr;
if (saddr[-3] == DDI_STRUCTURE_LE_ACC)
vmem_free(little_endian_arena, (void *)saddr[-2], saddr[-1]);
else
vmem_free(big_endian_arena, (void *)saddr[-2], saddr[-1]);
}
static int
i_ddi_iomin(dev_info_t *a, int i, int stream)
{
int r;
if (!ISP2(i))
return (0);
if (i == 0)
i = (stream) ? 4 : 1;
r = ddi_ctlops(a, a,
DDI_CTLOPS_IOMIN, (void *)(uintptr_t)stream, (void *)&i);
if (r != DDI_SUCCESS || !ISP2(i))
return (0);
return (i);
}
int
i_ddi_mem_alloc(dev_info_t *dip, ddi_dma_attr_t *attr,
size_t length, int cansleep, int flags,
ddi_device_acc_attr_t *accattrp,
caddr_t *kaddrp, size_t *real_length, ddi_acc_hdl_t *handlep)
{
caddr_t a;
int iomin, align, streaming;
uint_t endian_flags = DDI_NEVERSWAP_ACC;
#if defined(lint)
*handlep = *handlep;
#endif
if (length == 0 || kaddrp == NULL || attr == NULL) {
return (DDI_FAILURE);
}
if (attr->dma_attr_minxfer == 0 || attr->dma_attr_align == 0 ||
!ISP2(attr->dma_attr_align) || !ISP2(attr->dma_attr_minxfer)) {
return (DDI_FAILURE);
}
streaming = (flags & DDI_DMA_STREAMING) ? 1 : 0;
iomin = (attr->dma_attr_burstsizes & 0xffff) |
((attr->dma_attr_burstsizes >> 16) & 0xffff);
if (iomin == 0)
iomin = 1;
else
iomin = 1 << (ddi_fls(iomin) - 1);
iomin = maxbit(iomin, attr->dma_attr_minxfer);
iomin = maxbit(iomin, attr->dma_attr_align);
iomin = i_ddi_iomin(dip, iomin, streaming);
if (iomin == 0)
return (DDI_FAILURE);
ASSERT((iomin & (iomin - 1)) == 0);
ASSERT(iomin >= attr->dma_attr_minxfer);
ASSERT(iomin >= attr->dma_attr_align);
length = P2ROUNDUP(length, iomin);
align = iomin;
if (accattrp != NULL)
endian_flags = accattrp->devacc_attr_endian_flags;
a = kalloca(length, align, cansleep, endian_flags);
if ((*kaddrp = a) == 0) {
return (DDI_FAILURE);
} else {
if (real_length) {
*real_length = length;
}
if (handlep) {
impl_acc_hdl_init(handlep);
}
return (DDI_SUCCESS);
}
}
void
i_ddi_mem_free(caddr_t kaddr, ddi_acc_hdl_t *ap)
{
kfreea(kaddr);
}
static uintptr_t impl_acc_hdl_id = 0;
ddi_acc_hdl_t *
impl_acc_hdl_get(ddi_acc_handle_t hdl)
{
return (&((ddi_acc_impl_t *)hdl)->ahi_common);
}
ddi_acc_handle_t
impl_acc_hdl_alloc(int (*waitfp)(caddr_t), caddr_t arg)
{
ddi_acc_impl_t *hp;
on_trap_data_t *otp;
int sleepflag;
sleepflag = ((waitfp == (int (*)())KM_SLEEP) ? KM_SLEEP : KM_NOSLEEP);
if ((hp = kmem_zalloc(sizeof (ddi_acc_impl_t), sleepflag)) == NULL)
goto fail;
if ((hp->ahi_err = (ndi_err_t *)kmem_zalloc(
sizeof (ndi_err_t), sleepflag)) == NULL) {
kmem_free(hp, sizeof (ddi_acc_impl_t));
goto fail;
}
if ((otp = (on_trap_data_t *)kmem_zalloc(
sizeof (on_trap_data_t), sleepflag)) == NULL) {
kmem_free(hp->ahi_err, sizeof (ndi_err_t));
kmem_free(hp, sizeof (ddi_acc_impl_t));
goto fail;
}
hp->ahi_err->err_ontrap = otp;
hp->ahi_common.ah_platform_private = (void *)hp;
return ((ddi_acc_handle_t)hp);
fail:
if ((waitfp != (int (*)())KM_SLEEP) &&
(waitfp != (int (*)())KM_NOSLEEP))
ddi_set_callback(waitfp, arg, &impl_acc_hdl_id);
return (NULL);
}
void
impl_acc_hdl_free(ddi_acc_handle_t handle)
{
ddi_acc_impl_t *hp;
hp = (ddi_acc_impl_t *)handle;
if (hp) {
kmem_free(hp->ahi_err->err_ontrap, sizeof (on_trap_data_t));
kmem_free(hp->ahi_err, sizeof (ndi_err_t));
kmem_free(hp, sizeof (ddi_acc_impl_t));
if (impl_acc_hdl_id)
ddi_run_callback(&impl_acc_hdl_id);
}
}
#define PCI_GET_MP_PFN(mp, page_no) ((mp)->dmai_ndvmapages == 1 ? \
(pfn_t)(mp)->dmai_iopte:(((pfn_t *)(mp)->dmai_iopte)[page_no]))
int
impl_dma_check(dev_info_t *dip, const void *handle, const void *addr,
const void *not_used)
{
ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle;
pfn_t fault_pfn = mmu_btop(*(uint64_t *)addr);
pfn_t comp_pfn;
int page;
ASSERT(mp);
for (page = 0; page < mp->dmai_ndvmapages; page++) {
comp_pfn = PCI_GET_MP_PFN(mp, page);
if (fault_pfn == comp_pfn)
return (DDI_FM_NONFATAL);
}
return (DDI_FM_UNKNOWN);
}
static int
impl_acc_check(dev_info_t *dip, const void *handle, const void *addr,
const void *not_used)
{
pfn_t pfn, fault_pfn;
ddi_acc_hdl_t *hp;
hp = impl_acc_hdl_get((ddi_acc_handle_t)handle);
ASSERT(hp);
if (addr != NULL) {
pfn = hp->ah_pfn;
fault_pfn = mmu_btop(*(uint64_t *)addr);
if (fault_pfn >= pfn && fault_pfn < (pfn + hp->ah_pnum))
return (DDI_FM_NONFATAL);
}
return (DDI_FM_UNKNOWN);
}
void
impl_acc_err_init(ddi_acc_hdl_t *handlep)
{
int fmcap;
ndi_err_t *errp;
on_trap_data_t *otp;
ddi_acc_impl_t *hp = (ddi_acc_impl_t *)handlep;
fmcap = ddi_fm_capable(handlep->ah_dip);
if (handlep->ah_acc.devacc_attr_version < DDI_DEVICE_ATTR_V1 ||
!DDI_FM_ACC_ERR_CAP(fmcap)) {
handlep->ah_acc.devacc_attr_access = DDI_DEFAULT_ACC;
} else if (DDI_FM_ACC_ERR_CAP(fmcap)) {
if (handlep->ah_acc.devacc_attr_access == DDI_DEFAULT_ACC) {
if (handlep->ah_xfermodes)
return;
i_ddi_drv_ereport_post(handlep->ah_dip, DVR_EFMCAP,
NULL, DDI_NOSLEEP);
} else {
errp = hp->ahi_err;
otp = (on_trap_data_t *)errp->err_ontrap;
otp->ot_handle = (void *)(hp);
otp->ot_prot = OT_DATA_ACCESS;
if (handlep->ah_acc.devacc_attr_access ==
DDI_CAUTIOUS_ACC)
otp->ot_trampoline =
(uintptr_t)&i_ddi_caut_trampoline;
else
otp->ot_trampoline =
(uintptr_t)&i_ddi_prot_trampoline;
errp->err_status = DDI_FM_OK;
errp->err_expected = DDI_FM_ERR_UNEXPECTED;
errp->err_cf = impl_acc_check;
}
}
}
void
impl_acc_hdl_init(ddi_acc_hdl_t *handlep)
{
ddi_acc_impl_t *hp;
ASSERT(handlep);
hp = (ddi_acc_impl_t *)handlep;
hp->ahi_get8 = i_ddi_get8;
hp->ahi_put8 = i_ddi_put8;
hp->ahi_rep_get8 = i_ddi_rep_get8;
hp->ahi_rep_put8 = i_ddi_rep_put8;
if (handlep->ah_acc.devacc_attr_endian_flags & DDI_STRUCTURE_LE_ACC) {
hp->ahi_get16 = i_ddi_swap_get16;
hp->ahi_get32 = i_ddi_swap_get32;
hp->ahi_get64 = i_ddi_swap_get64;
hp->ahi_put16 = i_ddi_swap_put16;
hp->ahi_put32 = i_ddi_swap_put32;
hp->ahi_put64 = i_ddi_swap_put64;
hp->ahi_rep_get16 = i_ddi_swap_rep_get16;
hp->ahi_rep_get32 = i_ddi_swap_rep_get32;
hp->ahi_rep_get64 = i_ddi_swap_rep_get64;
hp->ahi_rep_put16 = i_ddi_swap_rep_put16;
hp->ahi_rep_put32 = i_ddi_swap_rep_put32;
hp->ahi_rep_put64 = i_ddi_swap_rep_put64;
} else {
hp->ahi_get16 = i_ddi_get16;
hp->ahi_get32 = i_ddi_get32;
hp->ahi_get64 = i_ddi_get64;
hp->ahi_put16 = i_ddi_put16;
hp->ahi_put32 = i_ddi_put32;
hp->ahi_put64 = i_ddi_put64;
hp->ahi_rep_get16 = i_ddi_rep_get16;
hp->ahi_rep_get32 = i_ddi_rep_get32;
hp->ahi_rep_get64 = i_ddi_rep_get64;
hp->ahi_rep_put16 = i_ddi_rep_put16;
hp->ahi_rep_put32 = i_ddi_rep_put32;
hp->ahi_rep_put64 = i_ddi_rep_put64;
}
hp->ahi_fault_check = i_ddi_acc_fault_check;
hp->ahi_fault_notify = i_ddi_acc_fault_notify;
hp->ahi_fault = 0;
impl_acc_err_init(handlep);
}
void
i_ddi_acc_set_fault(ddi_acc_handle_t handle)
{
ddi_acc_impl_t *hp = (ddi_acc_impl_t *)handle;
if (!hp->ahi_fault) {
hp->ahi_fault = 1;
(*hp->ahi_fault_notify)(hp);
}
}
void
i_ddi_acc_clr_fault(ddi_acc_handle_t handle)
{
ddi_acc_impl_t *hp = (ddi_acc_impl_t *)handle;
if (hp->ahi_fault) {
hp->ahi_fault = 0;
(*hp->ahi_fault_notify)(hp);
}
}
void
i_ddi_acc_fault_notify(ddi_acc_impl_t *hp)
{
}
uint_t
impl_assign_instance(dev_info_t *dip)
{
return ((uint_t)-1);
}
int
impl_keep_instance(dev_info_t *dip)
{
return (DDI_FAILURE);
}
int
impl_free_instance(dev_info_t *dip)
{
return (DDI_FAILURE);
}
int
impl_check_cpu(dev_info_t *devi)
{
return (DDI_SUCCESS);
}
static const char *nocopydevs[] = {
"SUNW,ffb",
"SUNW,afb",
NULL
};
int
e_ddi_copyfromdev(dev_info_t *devi,
off_t off, const void *devaddr, void *kaddr, size_t len)
{
const char **argv;
for (argv = nocopydevs; *argv; argv++)
if (strcmp(ddi_binding_name(devi), *argv) == 0) {
bzero(kaddr, len);
return (0);
}
bcopy(devaddr, kaddr, len);
return (0);
}
int
e_ddi_copytodev(dev_info_t *devi,
off_t off, const void *kaddr, void *devaddr, size_t len)
{
const char **argv;
for (argv = nocopydevs; *argv; argv++)
if (strcmp(ddi_binding_name(devi), *argv) == 0)
return (1);
bcopy(kaddr, devaddr, len);
return (0);
}
idprom_t idprom;
void
configure(void)
{
extern void i_ddi_init_root();
ASSERT(!bootops);
if (fpu_exists)
fpu_probe();
else
cmn_err(CE_CONT, "FPU not in use\n");
#if 0
(void) prom_nextnode((pnode_t)0);
#endif
i_ddi_init_root();
#ifdef DDI_PROP_DEBUG
(void) ddi_prop_debug(1);
#endif
}
int
status_okay(int id, char *buf, int buflen)
{
char status_buf[OBP_MAXPROPNAME];
char *bufp = buf;
int len = buflen;
int proplen;
static const char *status = "status";
static const char *fail = "fail";
size_t fail_len = strlen(fail);
proplen = prom_getproplen((pnode_t)id, (caddr_t)status);
if (proplen <= fail_len)
return (1);
if ((buf == (char *)NULL) || (buflen <= 0)) {
bufp = status_buf;
len = sizeof (status_buf);
}
*bufp = (char)0;
(void) prom_bounded_getprop((pnode_t)id, (caddr_t)status,
(caddr_t)bufp, len);
*(bufp + len - 1) = (char)0;
if (strncmp(bufp, fail, fail_len) == 0)
return (0);
return (1);
}
void
setcputype(void)
{
(void) prom_getidprom((caddr_t)&idprom, sizeof (idprom));
}
void
parse_idprom(void)
{
if (idprom.id_format == IDFORM_1) {
(void) localetheraddr((struct ether_addr *)idprom.id_ether,
(struct ether_addr *)NULL);
(void) snprintf(hw_serial, HW_HOSTID_LEN, "%u",
(idprom.id_machine << 24) + idprom.id_serial);
} else
prom_printf("Invalid format code in IDprom.\n");
}
void
impl_fix_props(dev_info_t *dip, dev_info_t *ch_dip, char *name, int len,
caddr_t buffer)
{
}
static void
i_ddi_caut_getput_ctlops(
ddi_acc_impl_t *hp, uint64_t host_addr, uint64_t dev_addr, size_t size,
size_t repcount, uint_t flags, ddi_ctl_enum_t cmd)
{
peekpoke_ctlops_t cautacc_ctlops_arg;
cautacc_ctlops_arg.size = size;
cautacc_ctlops_arg.dev_addr = dev_addr;
cautacc_ctlops_arg.host_addr = host_addr;
cautacc_ctlops_arg.handle = (ddi_acc_handle_t)hp;
cautacc_ctlops_arg.repcount = repcount;
cautacc_ctlops_arg.flags = flags;
(void) ddi_ctlops(hp->ahi_common.ah_dip, hp->ahi_common.ah_dip, cmd,
&cautacc_ctlops_arg, NULL);
}
uint8_t
i_ddi_caut_get8(ddi_acc_impl_t *hp, uint8_t *addr)
{
uint8_t value;
i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr,
sizeof (uint8_t), 1, 0, DDI_CTLOPS_PEEK);
return (value);
}
uint16_t
i_ddi_caut_get16(ddi_acc_impl_t *hp, uint16_t *addr)
{
uint16_t value;
i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr,
sizeof (uint16_t), 1, 0, DDI_CTLOPS_PEEK);
return (value);
}
uint32_t
i_ddi_caut_get32(ddi_acc_impl_t *hp, uint32_t *addr)
{
uint32_t value;
i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr,
sizeof (uint32_t), 1, 0, DDI_CTLOPS_PEEK);
return (value);
}
uint64_t
i_ddi_caut_get64(ddi_acc_impl_t *hp, uint64_t *addr)
{
uint64_t value;
i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr,
sizeof (uint64_t), 1, 0, DDI_CTLOPS_PEEK);
return (value);
}
void
i_ddi_caut_put8(ddi_acc_impl_t *hp, uint8_t *addr, uint8_t value)
{
i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr,
sizeof (uint8_t), 1, 0, DDI_CTLOPS_POKE);
}
void
i_ddi_caut_put16(ddi_acc_impl_t *hp, uint16_t *addr, uint16_t value)
{
i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr,
sizeof (uint16_t), 1, 0, DDI_CTLOPS_POKE);
}
void
i_ddi_caut_put32(ddi_acc_impl_t *hp, uint32_t *addr, uint32_t value)
{
i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr,
sizeof (uint32_t), 1, 0, DDI_CTLOPS_POKE);
}
void
i_ddi_caut_put64(ddi_acc_impl_t *hp, uint64_t *addr, uint64_t value)
{
i_ddi_caut_getput_ctlops(hp, (uint64_t)&value, (uint64_t)addr,
sizeof (uint64_t), 1, 0, DDI_CTLOPS_POKE);
}
void
i_ddi_caut_rep_get8(ddi_acc_impl_t *hp, uint8_t *host_addr, uint8_t *dev_addr,
size_t repcount, uint_t flags)
{
i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr,
sizeof (uint8_t), repcount, flags, DDI_CTLOPS_PEEK);
}
void
i_ddi_caut_rep_get16(ddi_acc_impl_t *hp, uint16_t *host_addr,
uint16_t *dev_addr, size_t repcount, uint_t flags)
{
i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr,
sizeof (uint16_t), repcount, flags, DDI_CTLOPS_PEEK);
}
void
i_ddi_caut_rep_get32(ddi_acc_impl_t *hp, uint32_t *host_addr,
uint32_t *dev_addr, size_t repcount, uint_t flags)
{
i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr,
sizeof (uint32_t), repcount, flags, DDI_CTLOPS_PEEK);
}
void
i_ddi_caut_rep_get64(ddi_acc_impl_t *hp, uint64_t *host_addr,
uint64_t *dev_addr, size_t repcount, uint_t flags)
{
i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr,
sizeof (uint64_t), repcount, flags, DDI_CTLOPS_PEEK);
}
void
i_ddi_caut_rep_put8(ddi_acc_impl_t *hp, uint8_t *host_addr, uint8_t *dev_addr,
size_t repcount, uint_t flags)
{
i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr,
sizeof (uint8_t), repcount, flags, DDI_CTLOPS_POKE);
}
void
i_ddi_caut_rep_put16(ddi_acc_impl_t *hp, uint16_t *host_addr,
uint16_t *dev_addr, size_t repcount, uint_t flags)
{
i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr,
sizeof (uint16_t), repcount, flags, DDI_CTLOPS_POKE);
}
void
i_ddi_caut_rep_put32(ddi_acc_impl_t *hp, uint32_t *host_addr,
uint32_t *dev_addr, size_t repcount, uint_t flags)
{
i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr,
sizeof (uint32_t), repcount, flags, DDI_CTLOPS_POKE);
}
void
i_ddi_caut_rep_put64(ddi_acc_impl_t *hp, uint64_t *host_addr,
uint64_t *dev_addr, size_t repcount, uint_t flags)
{
i_ddi_caut_getput_ctlops(hp, (uint64_t)host_addr, (uint64_t)dev_addr,
sizeof (uint64_t), repcount, flags, DDI_CTLOPS_POKE);
}
int
peekpoke_mem(ddi_ctl_enum_t cmd, peekpoke_ctlops_t *in_args)
{
int err = DDI_SUCCESS;
on_trap_data_t otd;
if (!on_trap(&otd, OT_DATA_ACCESS)) {
uintptr_t tramp = otd.ot_trampoline;
if (cmd == DDI_CTLOPS_POKE) {
otd.ot_trampoline = (uintptr_t)&poke_fault;
err = do_poke(in_args->size, (void *)in_args->dev_addr,
(void *)in_args->host_addr);
} else {
otd.ot_trampoline = (uintptr_t)&peek_fault;
err = do_peek(in_args->size, (void *)in_args->dev_addr,
(void *)in_args->host_addr);
}
otd.ot_trampoline = tramp;
} else
err = DDI_FAILURE;
no_trap();
return (err);
}