#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/autoconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_subrdefs.h>
#include <sys/cmn_err.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/spl.h>
#include <sys/async.h>
#include <sys/dvma.h>
#include <sys/upa64s.h>
#include <sys/machsystm.h>
static void *per_upa64s_state;
static int
upa64s_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
off_t offset, off_t len, caddr_t *addrp);
static int
upa64s_ctlops(dev_info_t *dip, dev_info_t *rdip,
ddi_ctl_enum_t op, void *arg, void *result);
static int
upa64_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
ddi_intr_handle_impl_t *hdlp, void *result);
static int
upa64s_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp);
static int
upa64s_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp);
static int upa64s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int upa64s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
static int upa64s_power(dev_info_t *dip, int component, int level);
static struct bus_ops upa64s_bus_ops = {
BUSO_REV,
upa64s_map,
0,
0,
0,
i_ddi_map_fault,
ddi_no_dma_map,
ddi_no_dma_allochdl,
ddi_no_dma_freehdl,
ddi_no_dma_bindhdl,
ddi_no_dma_unbindhdl,
ddi_no_dma_flush,
ddi_no_dma_win,
ddi_no_dma_mctl,
upa64s_ctlops,
ddi_bus_prop_op,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
upa64_intr_ops
};
static struct dev_ops upa64s_ops = {
DEVO_REV,
0,
ddi_no_info,
nulldev,
0,
upa64s_attach,
upa64s_detach,
nodev,
(struct cb_ops *)0,
&upa64s_bus_ops,
upa64s_power,
ddi_quiesce_not_supported,
};
#include <sys/modctl.h>
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops,
"UPA64S nexus driver",
&upa64s_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
int
_init(void)
{
int e;
if (e = ddi_soft_state_init(&per_upa64s_state,
sizeof (upa64s_devstate_t), 2))
return (e);
if (e = mod_install(&modlinkage))
ddi_soft_state_fini(&per_upa64s_state);
return (e);
}
int
_fini(void)
{
int e = mod_remove(&modlinkage);
if (e)
return (e);
ddi_soft_state_fini(&per_upa64s_state);
return (e);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static void upa64s_intrdist(void *arg);
static int init_child(dev_info_t *child);
static int report_dev(dev_info_t *dip);
static int get_properties(upa64s_devstate_t *upa64s_p, dev_info_t *dip);
static void save_state(upa64s_devstate_t *upa64s_p);
static void restore_state(upa64s_devstate_t *upa64s_p);
static int xlate_reg_prop(dev_info_t *dip, upa64s_regspec_t *upa64s_rp,
off_t off, off_t len, struct regspec *rp);
static int get_reg_set(dev_info_t *dip, dev_info_t *child, int rnumber,
off_t off, off_t len, struct regspec *rp);
static off_t get_reg_set_size(dev_info_t *child, int rnumber);
static uint_t get_nreg_set(dev_info_t *child);
static int
upa64s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
upa64s_devstate_t *upa64s_p;
ddi_device_acc_attr_t attr;
int instance;
char *pmc[] = { "NAME=Framebuffer Power", "0=Off", "1=On", NULL };
switch (cmd) {
case DDI_ATTACH:
instance = ddi_get_instance(dip);
if (alloc_upa64s_soft_state(instance) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: can't allocate upa64s state",
ddi_get_name(dip), instance);
return (DDI_FAILURE);
}
upa64s_p = get_upa64s_soft_state(instance);
upa64s_p->dip = dip;
if (get_properties(upa64s_p, dip) != DDI_SUCCESS)
goto fail;
if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
"pm-components", pmc, ((sizeof (pmc)/sizeof (char *)) - 1))
!= DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "%s%d: failed to create pm-components "
"property.", ddi_get_name(dip), instance);
goto fail;
}
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
if (ddi_regs_map_setup(dip, 0,
(caddr_t *)&upa64s_p->config_base, 0, 0, &attr,
&upa64s_p->config_base_ah) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: failed to map reg1.",
ddi_get_name(dip), instance);
goto fail;
}
upa64s_p->upa0_config = (uint64_t *)(upa64s_p->config_base +
UPA64S_UPA0_CONFIG_OFFSET);
upa64s_p->upa1_config = (uint64_t *)(upa64s_p->config_base +
UPA64S_UPA1_CONFIG_OFFSET);
upa64s_p->if_config = (uint64_t *)(upa64s_p->config_base +
UPA64S_IF_CONFIG_OFFSET);
upa64s_p->estar = (uint64_t *)(upa64s_p->config_base +
UPA64S_ESTAR_OFFSET);
if (ddi_regs_map_setup(dip, 1, (caddr_t *)&upa64s_p->imr[0],
0, 0, &attr, &upa64s_p->imr_ah[0]) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: failed to map reg2.",
ddi_get_name(dip), instance);
goto fail1;
}
if (ddi_regs_map_setup(dip, 2, (caddr_t *)&upa64s_p->imr[1],
0, 0, &attr, &upa64s_p->imr_ah[1]) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: failed to map reg3.",
ddi_get_name(dip), instance);
goto fail2;
}
upa64s_p->power_level = UPA64S_PM_UNKNOWN;
if (pm_raise_power(dip, UPA64S_PM_COMP, UPA64S_PM_NORMOP) !=
DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: failed to raise the power.",
ddi_get_name(dip), instance);
goto fail3;
}
intr_dist_add(upa64s_intrdist, dip);
ddi_report_dev(dip);
return (DDI_SUCCESS);
case DDI_RESUME:
upa64s_p = get_upa64s_soft_state(ddi_get_instance(dip));
DBG(D_ATTACH, dip, "DDI_RESUME\n");
restore_state(upa64s_p);
upa64s_p->power_level = UPA64S_PM_UNKNOWN;
if (pm_raise_power(dip, UPA64S_PM_COMP,
upa64s_p->saved_power_level) != DDI_SUCCESS)
cmn_err(CE_WARN, "%s%d: failed to change power level "
"during resume!", ddi_get_name(dip), instance);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
fail3:
ddi_regs_map_free(&upa64s_p->imr_ah[1]);
fail2:
ddi_regs_map_free(&upa64s_p->imr_ah[0]);
fail1:
ddi_regs_map_free(&upa64s_p->config_base_ah);
fail:
free_upa64s_soft_state(instance);
return (DDI_FAILURE);
}
static int
upa64s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int instance = ddi_get_instance(dip);
upa64s_devstate_t *upa64s_p = get_upa64s_soft_state(instance);
switch (cmd) {
case DDI_DETACH:
DBG(D_DETACH, dip, "DDI_DETACH\n");
if (pm_lower_power(dip, UPA64S_PM_COMP, UPA64S_PM_RESET) !=
DDI_SUCCESS)
DBG(D_DETACH, dip, "failed to power off!\n");
intr_dist_rem(upa64s_intrdist, dip);
ddi_regs_map_free(&upa64s_p->config_base_ah);
ddi_regs_map_free(&upa64s_p->imr_ah[0]);
ddi_regs_map_free(&upa64s_p->imr_ah[1]);
free_upa64s_soft_state(instance);
return (DDI_SUCCESS);
case DDI_SUSPEND:
DBG(D_DETACH, dip, "DDI_SUSPEND\n");
save_state(upa64s_p);
upa64s_p->saved_power_level = upa64s_p->power_level;
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
upa64s_power(dev_info_t *dip, int component, int level)
{
int instance = ddi_get_instance(dip);
upa64s_devstate_t *upa64s_p = get_upa64s_soft_state(instance);
volatile uint64_t uint64_data;
DBG2(D_POWER, dip, "component=%d, level=%d\n", component, level);
if (component != UPA64S_PM_COMP ||
level < UPA64S_PM_RESET || level > UPA64S_PM_NORMOP)
return (DDI_FAILURE);
if (upa64s_p->power_level == UPA64S_PM_UNKNOWN) {
uint64_data = ddi_get64(upa64s_p->config_base_ah,
upa64s_p->if_config);
if ((level == UPA64S_PM_RESET &&
uint64_data == UPA64S_NOT_POK_RST_L) ||
(level == UPA64S_PM_NORMOP &&
uint64_data == UPA64S_POK_NOT_RST_L)) {
upa64s_p->power_level = level;
return (DDI_SUCCESS);
}
}
if (level == upa64s_p->power_level) {
DBG1(D_POWER, dip, "device is already at power level %d\n",
level);
return (DDI_SUCCESS);
}
if (level == UPA64S_PM_RESET) {
ddi_put64(upa64s_p->config_base_ah, upa64s_p->if_config,
UPA64S_POK_RST_L);
ddi_put64(upa64s_p->config_base_ah, upa64s_p->if_config,
UPA64S_NOT_POK_RST_L);
uint64_data = ddi_get64(upa64s_p->config_base_ah,
upa64s_p->if_config);
ddi_put64(upa64s_p->config_base_ah, upa64s_p->estar,
UPA64S_1_2_SPEED);
ddi_put64(upa64s_p->config_base_ah, upa64s_p->estar,
UPA64S_1_64_SPEED);
uint64_data = ddi_get64(upa64s_p->config_base_ah,
upa64s_p->estar);
} else {
ddi_put64(upa64s_p->config_base_ah, upa64s_p->estar,
UPA64S_1_2_SPEED);
ddi_put64(upa64s_p->config_base_ah, upa64s_p->estar,
UPA64S_FULL_SPEED);
uint64_data = ddi_get64(upa64s_p->config_base_ah,
upa64s_p->estar);
ddi_put64(upa64s_p->config_base_ah, upa64s_p->if_config,
UPA64S_POK_RST_L);
uint64_data = ddi_get64(upa64s_p->config_base_ah,
upa64s_p->if_config);
delay(drv_usectohz(20*1000));
ddi_put64(upa64s_p->config_base_ah, upa64s_p->if_config,
UPA64S_POK_NOT_RST_L);
uint64_data = ddi_get64(upa64s_p->config_base_ah,
upa64s_p->if_config);
}
upa64s_p->power_level = level;
return (DDI_SUCCESS);
}
static int
upa64s_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
off_t off, off_t len, caddr_t *addrp)
{
struct regspec regspec;
ddi_map_req_t p_map_request;
int rnumber, rval;
DBG4(D_MAP, dip, "upa64s_map() mp=%x.%x addrp=%x.%08x\n",
HI32(mp), LO32(mp), HI32(addrp), LO32(addrp));
if (mp->map_flags & DDI_MF_USER_MAPPING) {
DBG2(D_MAP, dip, "rdip=%s%d: no user level mappings yet!\n",
ddi_get_name(rdip), ddi_get_instance(rdip));
return (DDI_ME_UNIMPLEMENTED);
}
switch (mp->map_type) {
case DDI_MT_REGSPEC:
DBG3(D_MAP, dip, "rdip=%s%d: REGSPEC - handlep=%x\n",
ddi_get_name(rdip), ddi_get_instance(rdip),
mp->map_handlep);
rval = xlate_reg_prop(dip, (upa64s_regspec_t *)mp->map_obj.rp,
off, len, ®spec);
break;
case DDI_MT_RNUMBER:
DBG4(D_MAP, dip, "rdip=%s%d: rnumber=%x handlep=%x\n",
ddi_get_name(rdip), ddi_get_instance(rdip),
mp->map_obj.rnumber, mp->map_handlep);
rnumber = mp->map_obj.rnumber;
if (rnumber < 0)
return (DDI_ME_RNUMBER_RANGE);
rval = get_reg_set(dip, rdip, rnumber, off, len, ®spec);
break;
default:
return (DDI_ME_INVAL);
}
if (rval != DDI_SUCCESS) {
DBG(D_MAP, dip, "failed on regspec\n\n");
return (rval);
}
p_map_request = *mp;
p_map_request.map_type = DDI_MT_REGSPEC;
p_map_request.map_obj.rp = ®spec;
rval = ddi_map(dip, &p_map_request, 0, 0, addrp);
DBG3(D_MAP, dip, "ddi_map returns: rval=%x addrp=%x.%08x\n\n",
rval, HI32(*addrp), LO32(*addrp));
return (rval);
}
#define UPA_BASE_INO 0x2a
static int
upa64s_xlate_intr(dev_info_t *rdip, int32_t safariport, uint32_t *intr)
{
uint32_t ino = UPA_BASE_INO;
int32_t portid;
*intr = 0;
if ((portid = ddi_getprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
"upa-portid", -1)) == -1)
return (-1);
ino += portid;
*intr = UPA64S_MAKE_MONDO(safariport, ino);
DBG5(D_A_ISPEC, rdip, "upa64s_xlate_intr: rdip=%s%d: upa portid %d "
"ino=%x mondo 0x%x\n", ddi_get_name(rdip), ddi_get_instance(rdip),
portid, ino, *intr);
return (portid);
}
static int
upa64s_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp)
{
int upaport, instance = ddi_get_instance(dip);
upa64s_devstate_t *upa64s_p = get_upa64s_soft_state(instance);
#ifdef DEBUG
uint_t (*int_handler)(caddr_t, caddr_t) = hdlp->ih_cb_func;
caddr_t int_handler_arg1 = hdlp->ih_cb_arg1;
#endif
uint_t cpu_id;
volatile uint64_t imr_data;
upaport = upa64s_xlate_intr(rdip, upa64s_p->safari_id,
(uint32_t *)&hdlp->ih_vector);
if (hdlp->ih_vector == 0)
return (DDI_FAILURE);
DBG3(D_A_ISPEC, dip,
"rdip=%s%d - IDDI_INTR_TYPE_NORMAL, mondo=%x\n",
ddi_driver_name(rdip), ddi_get_instance(rdip), hdlp->ih_vector);
if (upa64s_p->ino_state[upaport] != INO_FREE) {
return (DDI_FAILURE);
}
#ifdef DEBUG
DBG2(D_A_ISPEC, dip, "i_ddi_add_ivintr: hdlr=%p arg=%p\n",
int_handler, int_handler_arg1);
#endif
if (i_ddi_add_ivintr(hdlp) != DDI_SUCCESS)
return (DDI_FAILURE);
cpu_id = intr_dist_cpuid();
imr_data = UPA64S_CPUID_TO_IMR(cpu_id);
imr_data = UPA64S_GET_MAP_REG(hdlp->ih_vector, imr_data);
DBG4(D_A_ISPEC, dip, "IMR [upaport=%d mapping reg 0x%p] = %x.%x\n",
upaport, upa64s_p->imr[upaport], HI32(imr_data), LO32(imr_data));
ddi_put64(upa64s_p->imr_ah[upaport], upa64s_p->imr[upaport], imr_data);
imr_data = ddi_get64(upa64s_p->imr_ah[upaport], upa64s_p->imr[upaport]);
upa64s_p->ino_state[upaport] = INO_INUSE;
DBG(D_A_ISPEC, dip, "add_intr success!\n");
return (DDI_SUCCESS);
}
static int
upa64s_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp)
{
upa64s_devstate_t *upa64s_p =
get_upa64s_soft_state(ddi_get_instance(dip));
int upaport;
#ifndef lint
volatile uint64_t tmp;
#endif
upaport = upa64s_xlate_intr(rdip, upa64s_p->safari_id,
(uint32_t *)&hdlp->ih_vector);
if (hdlp->ih_vector == 0)
return (DDI_FAILURE);
DBG3(D_R_ISPEC, dip,
"rdip=%s%d - IDDI_INTR_TYPE_NORMAL, mondo=%x\n",
ddi_driver_name(rdip), ddi_get_instance(rdip), hdlp->ih_vector);
if (upa64s_p->ino_state[upaport] != INO_INUSE) {
return (DDI_FAILURE);
}
i_ddi_rem_ivintr(hdlp);
ddi_put64(upa64s_p->imr_ah[upaport], upa64s_p->imr[upaport], 0);
#ifndef lint
tmp = ddi_get64(upa64s_p->imr_ah[upaport], upa64s_p->imr[upaport]);
#endif
upa64s_p->ino_state[upaport] = INO_FREE;
return (DDI_SUCCESS);
}
static int
upa64_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
ddi_intr_handle_impl_t *hdlp, void *result)
{
int ret = DDI_SUCCESS;
switch (intr_op) {
case DDI_INTROP_GETCAP:
*(int *)result = DDI_INTR_FLAG_EDGE;
break;
case DDI_INTROP_ALLOC:
*(int *)result = hdlp->ih_scratch1;
break;
case DDI_INTROP_FREE:
break;
case DDI_INTROP_GETPRI:
*(int *)result = hdlp->ih_pri ? hdlp->ih_pri : 5;
break;
case DDI_INTROP_SETPRI:
break;
case DDI_INTROP_ADDISR:
ret = upa64s_add_intr_impl(dip, rdip, hdlp);
break;
case DDI_INTROP_REMISR:
ret = upa64s_remove_intr_impl(dip, rdip, hdlp);
break;
case DDI_INTROP_ENABLE:
case DDI_INTROP_DISABLE:
break;
case DDI_INTROP_NINTRS:
case DDI_INTROP_NAVAIL:
*(int *)result = i_ddi_get_intx_nintrs(rdip);
break;
case DDI_INTROP_SETCAP:
case DDI_INTROP_SETMASK:
case DDI_INTROP_CLRMASK:
case DDI_INTROP_GETPENDING:
ret = DDI_ENOTSUP;
break;
case DDI_INTROP_SUPPORTED_TYPES:
*(int *)result = i_ddi_get_intx_nintrs(rdip) ?
DDI_INTR_TYPE_FIXED : 0;
break;
default:
ret = i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result);
break;
}
return (ret);
}
#ifdef DEBUG
uint_t upa64s_debug_flags = (uint_t)0;
extern void prom_printf(const char *, ...);
#endif
static int
upa64s_ctlops(dev_info_t *dip, dev_info_t *rdip,
ddi_ctl_enum_t op, void *arg, void *result)
{
DBG5(D_CTLOPS, dip, "dip=%x.%x rdip=%x.%x op=%x",
HI32(dip), LO32(dip), HI32(rdip), LO32(rdip), op);
DBG4(D_CTLOPS|D_CONT, dip, " arg=%x.%x result=%x.%x\n",
HI32(arg), LO32(arg), HI32(result), LO32(result));
switch (op) {
case DDI_CTLOPS_INITCHILD:
DBG2(D_CTLOPS, dip, "DDI_CTLOPS_INITCHILD: rdip=%s%d\n",
ddi_get_name(rdip), ddi_get_instance(rdip));
return (init_child((dev_info_t *)arg));
case DDI_CTLOPS_UNINITCHILD:
DBG2(D_CTLOPS, dip, "DDI_CTLOPS_UNINITCHILD: rdip=%s%d\n",
ddi_get_name(rdip), ddi_get_instance(rdip));
ddi_set_name_addr((dev_info_t *)arg, NULL);
ddi_remove_minor_node((dev_info_t *)arg, NULL);
impl_rem_dev_props((dev_info_t *)arg);
return (DDI_SUCCESS);
case DDI_CTLOPS_REPORTDEV:
DBG2(D_CTLOPS, dip, "DDI_CTLOPS_REPORTDEV: rdip=%s%d\n",
ddi_get_name(rdip), ddi_get_instance(rdip));
return (report_dev(rdip));
case DDI_CTLOPS_REGSIZE:
DBG2(D_CTLOPS, dip, "DDI_CTLOPS_REGSIZE: rdip=%s%d\n",
ddi_get_name(rdip), ddi_get_instance(rdip));
*((off_t *)result) = get_reg_set_size(rdip, *((int *)arg));
return (*((off_t *)result) == -1 ? DDI_FAILURE : DDI_SUCCESS);
case DDI_CTLOPS_NREGS:
DBG2(D_CTLOPS, dip, "DDI_CTLOPS_NREGS: rdip=%s%d\n",
ddi_get_name(rdip), ddi_get_instance(rdip));
*((uint_t *)result) = get_nreg_set(rdip);
return (DDI_SUCCESS);
}
DBG3(D_CTLOPS, dip, "passing request to parent: rdip=%s%d op=%x\n\n",
ddi_get_name(rdip), ddi_get_instance(rdip), op);
return (ddi_ctlops(dip, rdip, op, arg, result));
}
static int
get_properties(upa64s_devstate_t *upa64s_p, dev_info_t *dip)
{
int safari_id;
safari_id = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"portid", -1);
if (safari_id == -1) {
int instance = ddi_get_instance(dip);
panic("%s%d: no portid property", ddi_get_name(dip), instance);
}
upa64s_p->safari_id = safari_id;
return (DDI_SUCCESS);
}
static void
save_state(upa64s_devstate_t *upa64s_p)
{
upa64s_p->imr_data[0] = ddi_get64(upa64s_p->imr_ah[0],
upa64s_p->imr[0]);
upa64s_p->imr_data[1] = ddi_get64(upa64s_p->imr_ah[1],
upa64s_p->imr[1]);
}
static void
restore_state(upa64s_devstate_t *upa64s_p)
{
#ifndef lint
volatile uint64_t tmp;
#endif
ddi_put64(upa64s_p->imr_ah[0], upa64s_p->imr[0],
upa64s_p->imr_data[0]);
ddi_put64(upa64s_p->imr_ah[1], upa64s_p->imr[1],
upa64s_p->imr_data[1]);
#ifndef lint
tmp = ddi_get64(upa64s_p->imr_ah[0], upa64s_p->imr[0]);
tmp = ddi_get64(upa64s_p->imr_ah[1], upa64s_p->imr[1]);
#endif
}
static int
get_reg_set(dev_info_t *dip, dev_info_t *child, int rnumber,
off_t off, off_t len, struct regspec *rp)
{
upa64s_regspec_t *upa64s_rp;
int i, n, rval;
if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
(caddr_t)&upa64s_rp, &i) != DDI_SUCCESS)
return (DDI_ME_RNUMBER_RANGE);
n = i / (int)sizeof (upa64s_regspec_t);
if (rnumber >= n) {
kmem_free(upa64s_rp, i);
return (DDI_ME_RNUMBER_RANGE);
}
rval = xlate_reg_prop(dip, &upa64s_rp[rnumber], off, len, rp);
kmem_free(upa64s_rp, i);
return (rval);
}
static int
xlate_reg_prop(dev_info_t *dip, upa64s_regspec_t *child_rp, off_t off,
off_t len, struct regspec *rp)
{
int n_ranges, ranges_len, i;
uint64_t child_beg, child_end;
upa64s_ranges_t *range_p, *rng_p;
DBG4(D_MAP, dip, "upa64s regspec - ((%x,%x) (%x,%x))\n",
HI32(child_rp->upa64s_phys), LO32(child_rp->upa64s_phys),
HI32(child_rp->upa64s_size), LO32(child_rp->upa64s_size));
DBG2(D_MAP, dip, "upa64s xlate_reg_prp - off=%lx len=%lx\n", off, len);
#if 0
if (off + len > child_rp->upa64s_size) {
DBG(D_MAP, dip, "upa64s xlate_reg_prp: bad off + len\n");
return (DDI_FAILURE);
}
#endif
if (child_rp->upa64s_size >= (1ull << 32))
panic("upa64s: reg size must be less than 4 Gb");
if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"ranges", (caddr_t)&range_p, &ranges_len) != DDI_SUCCESS) {
ranges_len = 0;
cmn_err(CE_WARN, "%s%d: no ranges property",
ddi_get_name(dip), ddi_get_instance(dip));
}
n_ranges = ranges_len / sizeof (upa64s_regspec_t);
child_beg = child_rp->upa64s_phys;
#if 0
child_end = child_beg + child_rp->upa64s_size;
#else
#define UPA64S_MAX_CHILD_LEN 0xe000000
child_end = child_beg + UPA64S_MAX_CHILD_LEN;
#endif
for (i = 0, rng_p = range_p; i < n_ranges; i++, rng_p++) {
uint64_t rng_beg = rng_p->upa64s_child;
uint64_t rng_end = rng_beg + rng_p->upa64s_size;
if ((rng_beg <= child_beg) && (rng_end >= child_end)) {
uint64_t addr = child_beg - rng_beg + off;
addr += rng_p->upa64s_parent;
rp->regspec_bustype = HI32(addr);
rp->regspec_addr = LO32(addr);
rp->regspec_size = len ? len : child_rp->upa64s_size;
break;
}
}
if (ranges_len)
kmem_free(range_p, ranges_len);
DBG4(D_MAP, dip, "regspec (%x,%x,%x) i=%x\n",
rp->regspec_bustype, rp->regspec_addr, rp->regspec_size, i);
return (i < n_ranges? DDI_SUCCESS : DDI_ME_INVAL);
}
static int
report_dev(dev_info_t *dip)
{
if (dip == (dev_info_t *)0)
return (DDI_FAILURE);
cmn_err(CE_CONT, "?UPA64S-device: %s@%s, %s #%d\n",
ddi_node_name(dip), ddi_get_name_addr(dip),
ddi_get_name(dip), ddi_get_instance(dip));
return (DDI_SUCCESS);
}
static int
init_child(dev_info_t *child)
{
upa64s_regspec_t *child_rp;
int i;
char addr[256];
int32_t portid;
if ((portid = ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
"upa-portid", -1)) == -1)
return (DDI_FAILURE);
if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
(caddr_t)&child_rp, &i) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
(void) sprintf(addr, "%x,%x", portid, LO32(child_rp->upa64s_phys));
ddi_set_name_addr(child, addr);
ddi_set_parent_data(child, NULL);
kmem_free(child_rp, i);
return (DDI_SUCCESS);
}
static off_t
get_reg_set_size(dev_info_t *child, int rnumber)
{
upa64s_regspec_t *upa64s_rp;
uint_t size;
int i;
if (rnumber < 0)
return (-1);
if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
(caddr_t)&upa64s_rp, &i) != DDI_SUCCESS)
return (-1);
if (rnumber >= (i / (int)sizeof (upa64s_regspec_t))) {
kmem_free(upa64s_rp, i);
return (-1);
}
size = (uint32_t)upa64s_rp[rnumber].upa64s_size;
kmem_free(upa64s_rp, i);
return (size);
}
static uint_t
get_nreg_set(dev_info_t *child)
{
upa64s_regspec_t *upa64s_rp;
int i, n;
if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
(caddr_t)&upa64s_rp, &i) != DDI_SUCCESS)
return (0);
n = i / (int)sizeof (upa64s_regspec_t);
kmem_free(upa64s_rp, i);
return (n);
}
static void
upa64s_intrdist(void *arg)
{
dev_info_t *dip = (dev_info_t *)arg;
int instance = ddi_get_instance(dip);
upa64s_devstate_t *upa64s_p = get_upa64s_soft_state(instance);
uint_t upaport;
for (upaport = 0; upaport < UPA64S_PORTS; upaport++) {
volatile uint64_t *imr;
volatile uint64_t imr_dat;
uint_t mondo;
uint32_t cpuid;
if (upa64s_p->ino_state[upaport] != INO_INUSE)
continue;
imr = upa64s_p->imr[upaport];
mondo = UPA64S_IMR_TO_MONDO(*imr);
cpuid = intr_dist_cpuid();
imr_dat = UPA64S_CPUID_TO_IMR(cpuid);
imr_dat = UPA64S_GET_MAP_REG(mondo, imr_dat);
DBG2(D_INTRDIST, dip, "mondo=%x cpuid=%x\n", mondo, cpuid);
if (UPA64S_IMR_TO_CPUID(*imr) == cpuid) {
DBG(D_INTRDIST, dip, "same cpuid\n");
continue;
}
ddi_put64(upa64s_p->imr_ah[upaport], (uint64_t *)imr, imr_dat);
imr_dat = ddi_get64(upa64s_p->imr_ah[upaport], (uint64_t *)imr);
}
}
#ifdef DEBUG
static void
upa64s_debug(uint_t flag, dev_info_t *dip, char *fmt,
uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
{
char *s = NULL;
uint_t cont = 0;
if (flag & D_CONT) {
flag &= ~D_CONT;
cont = 1;
}
if (!(upa64s_debug_flags & flag))
return;
switch (flag) {
case D_ATTACH: s = "attach"; break;
case D_DETACH: s = "detach"; break;
case D_POWER: s = "power"; break;
case D_MAP: s = "map"; break;
case D_CTLOPS: s = "ctlops"; break;
case D_G_ISPEC: s = "get_intrspec"; break;
case D_A_ISPEC: s = "add_intrspec"; break;
case D_R_ISPEC: s = "remove_intrspec"; break;
case D_INIT_CLD: s = "init_child"; break;
case D_INTRDIST: s = "intrdist"; break;
}
if (s && cont == 0) {
prom_printf("%s(%d): %s: ", ddi_get_name(dip),
ddi_get_instance(dip), s);
}
prom_printf(fmt, a1, a2, a3, a4, a5);
}
#endif