#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_implfuncs.h>
#include <sys/obpdefs.h>
#include <sys/cmn_err.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/autoconf.h>
#include <sys/spl.h>
#include <sys/iommu.h>
#include <sys/sysiosbus.h>
#include <sys/sysioerr.h>
#include <sys/iocache.h>
#include <sys/async.h>
#include <sys/machsystm.h>
#include <sys/intreg.h>
#include <sys/ddi_subrdefs.h>
#include <sys/sdt.h>
#include <sys/nexusdebug.h>
#define SBUS_ATTACH_DEBUG 0x1
#define SBUS_SBUSMEM_DEBUG 0x2
#define SBUS_INTERRUPT_DEBUG 0x4
#define SBUS_REGISTERS_DEBUG 0x8
static struct sbus_slot_entry ino_1 = {SBUS_SLOT0_CONFIG, SBUS_SLOT0_MAPREG,
SBUS_SLOT0_L1_CLEAR, 0};
static struct sbus_slot_entry ino_2 = {SBUS_SLOT0_CONFIG, SBUS_SLOT0_MAPREG,
SBUS_SLOT0_L2_CLEAR, 0};
static struct sbus_slot_entry ino_3 = {SBUS_SLOT0_CONFIG, SBUS_SLOT0_MAPREG,
SBUS_SLOT0_L3_CLEAR, 0};
static struct sbus_slot_entry ino_4 = {SBUS_SLOT0_CONFIG, SBUS_SLOT0_MAPREG,
SBUS_SLOT0_L4_CLEAR, 0};
static struct sbus_slot_entry ino_5 = {SBUS_SLOT0_CONFIG, SBUS_SLOT0_MAPREG,
SBUS_SLOT0_L5_CLEAR, 0};
static struct sbus_slot_entry ino_6 = {SBUS_SLOT0_CONFIG, SBUS_SLOT0_MAPREG,
SBUS_SLOT0_L6_CLEAR, 0};
static struct sbus_slot_entry ino_7 = {SBUS_SLOT0_CONFIG, SBUS_SLOT0_MAPREG,
SBUS_SLOT0_L7_CLEAR, 0};
static struct sbus_slot_entry ino_9 = {SBUS_SLOT1_CONFIG, SBUS_SLOT1_MAPREG,
SBUS_SLOT1_L1_CLEAR, 0};
static struct sbus_slot_entry ino_10 = {SBUS_SLOT1_CONFIG, SBUS_SLOT1_MAPREG,
SBUS_SLOT1_L2_CLEAR, 0};
static struct sbus_slot_entry ino_11 = {SBUS_SLOT1_CONFIG, SBUS_SLOT1_MAPREG,
SBUS_SLOT1_L3_CLEAR, 0};
static struct sbus_slot_entry ino_12 = {SBUS_SLOT1_CONFIG, SBUS_SLOT1_MAPREG,
SBUS_SLOT1_L4_CLEAR, 0};
static struct sbus_slot_entry ino_13 = {SBUS_SLOT1_CONFIG, SBUS_SLOT1_MAPREG,
SBUS_SLOT1_L5_CLEAR, 0};
static struct sbus_slot_entry ino_14 = {SBUS_SLOT1_CONFIG, SBUS_SLOT1_MAPREG,
SBUS_SLOT1_L6_CLEAR, 0};
static struct sbus_slot_entry ino_15 = {SBUS_SLOT1_CONFIG, SBUS_SLOT1_MAPREG,
SBUS_SLOT1_L7_CLEAR, 0};
static struct sbus_slot_entry ino_17 = {SBUS_SLOT2_CONFIG, SBUS_SLOT2_MAPREG,
SBUS_SLOT2_L1_CLEAR, 0};
static struct sbus_slot_entry ino_18 = {SBUS_SLOT2_CONFIG, SBUS_SLOT2_MAPREG,
SBUS_SLOT2_L2_CLEAR, 0};
static struct sbus_slot_entry ino_19 = {SBUS_SLOT2_CONFIG, SBUS_SLOT2_MAPREG,
SBUS_SLOT2_L3_CLEAR, 0};
static struct sbus_slot_entry ino_20 = {SBUS_SLOT2_CONFIG, SBUS_SLOT2_MAPREG,
SBUS_SLOT2_L4_CLEAR, 0};
static struct sbus_slot_entry ino_21 = {SBUS_SLOT2_CONFIG, SBUS_SLOT2_MAPREG,
SBUS_SLOT2_L5_CLEAR, 0};
static struct sbus_slot_entry ino_22 = {SBUS_SLOT2_CONFIG, SBUS_SLOT2_MAPREG,
SBUS_SLOT2_L6_CLEAR, 0};
static struct sbus_slot_entry ino_23 = {SBUS_SLOT2_CONFIG, SBUS_SLOT2_MAPREG,
SBUS_SLOT2_L7_CLEAR, 0};
static struct sbus_slot_entry ino_25 = {SBUS_SLOT3_CONFIG, SBUS_SLOT3_MAPREG,
SBUS_SLOT3_L1_CLEAR, 0};
static struct sbus_slot_entry ino_26 = {SBUS_SLOT3_CONFIG, SBUS_SLOT3_MAPREG,
SBUS_SLOT3_L2_CLEAR, 0};
static struct sbus_slot_entry ino_27 = {SBUS_SLOT3_CONFIG, SBUS_SLOT3_MAPREG,
SBUS_SLOT3_L3_CLEAR, 0};
static struct sbus_slot_entry ino_28 = {SBUS_SLOT3_CONFIG, SBUS_SLOT3_MAPREG,
SBUS_SLOT3_L4_CLEAR, 0};
static struct sbus_slot_entry ino_29 = {SBUS_SLOT3_CONFIG, SBUS_SLOT3_MAPREG,
SBUS_SLOT3_L5_CLEAR, 0};
static struct sbus_slot_entry ino_30 = {SBUS_SLOT3_CONFIG, SBUS_SLOT3_MAPREG,
SBUS_SLOT3_L6_CLEAR, 0};
static struct sbus_slot_entry ino_31 = {SBUS_SLOT3_CONFIG, SBUS_SLOT3_MAPREG,
SBUS_SLOT3_L7_CLEAR, 0};
static struct sbus_slot_entry ino_32 = {SBUS_SLOT5_CONFIG, ESP_MAPREG,
ESP_CLEAR, ESP_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_33 = {SBUS_SLOT5_CONFIG, ETHER_MAPREG,
ETHER_CLEAR, ETHER_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_34 = {SBUS_SLOT5_CONFIG, PP_MAPREG,
PP_CLEAR, PP_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_36 = {SBUS_SLOT4_CONFIG, AUDIO_MAPREG,
AUDIO_CLEAR, AUDIO_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_40 = {SBUS_SLOT6_CONFIG, KBDMOUSE_MAPREG,
KBDMOUSE_CLEAR,
KBDMOUSE_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_41 = {SBUS_SLOT6_CONFIG, FLOPPY_MAPREG,
FLOPPY_CLEAR, FLOPPY_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_42 = {SBUS_SLOT6_CONFIG, THERMAL_MAPREG,
THERMAL_CLEAR,
THERMAL_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_48 = {SBUS_SLOT6_CONFIG, TIMER0_MAPREG,
TIMER0_CLEAR, TIMER0_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_49 = {SBUS_SLOT6_CONFIG, TIMER1_MAPREG,
TIMER1_CLEAR, TIMER1_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_52 = {SBUS_SLOT6_CONFIG, UE_ECC_MAPREG,
UE_ECC_CLEAR, UE_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_53 = {SBUS_SLOT6_CONFIG, CE_ECC_MAPREG,
CE_ECC_CLEAR, CE_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_54 = {SBUS_SLOT6_CONFIG, SBUS_ERR_MAPREG,
SBUS_ERR_CLEAR, SERR_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_55 = {SBUS_SLOT6_CONFIG, PM_WAKEUP_MAPREG,
PM_WAKEUP_CLEAR, PM_INTR_STATE_SHIFT};
static struct sbus_slot_entry ino_ffb = {0, FFB_MAPPING_REG, 0, 0};
static struct sbus_slot_entry ino_exp = {0, EXP_MAPPING_REG, 0, 0};
struct sbus_slot_entry *ino_table[] = {
NULL, &ino_1, &ino_2, &ino_3, &ino_4, &ino_5, &ino_6, &ino_7,
NULL, &ino_9, &ino_10, &ino_11, &ino_12, &ino_13, &ino_14, &ino_15,
NULL, &ino_17, &ino_18, &ino_19, &ino_20, &ino_21, &ino_22, &ino_23,
NULL, &ino_25, &ino_26, &ino_27, &ino_28, &ino_29, &ino_30, &ino_31,
&ino_32, &ino_33, &ino_34, NULL, &ino_36, NULL, NULL, NULL,
&ino_40, &ino_41, &ino_42, NULL, NULL, NULL, NULL, NULL, &ino_48,
&ino_49, NULL, NULL, &ino_52, &ino_53, &ino_54, &ino_55, &ino_ffb,
&ino_exp
};
int interrupt_priorities[] = {
-1, 2, 3, 5, 7, 9, 11, 13,
-1, 2, 3, 5, 7, 9, 11, 13,
-1, 2, 3, 5, 7, 9, 11, 13,
-1, 2, 3, 5, 7, 9, 11, 13,
4,
6,
3,
-1,
9,
-1, -1, -1,
12,
11,
9,
-1, -1, -1,
10,
14,
15,
10,
10,
10,
};
static int intr_cntr_on;
static int
sbus_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
static int
sbus_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp);
static void
sbus_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp);
static int
sbus_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
sbus_xlate_intrs(dev_info_t *dip, dev_info_t *rdip, uint32_t *intr,
uint32_t *pil, int32_t ign);
static int
sbus_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
static int
sbus_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
static int
sbus_do_detach(dev_info_t *devi);
static void
sbus_add_picN_kstats(dev_info_t *dip);
static void
sbus_add_kstats(struct sbus_soft_state *);
static int
sbus_counters_kstat_update(kstat_t *, int);
extern int
sysio_err_uninit(struct sbus_soft_state *softsp);
extern int
iommu_uninit(struct sbus_soft_state *softsp);
extern int
stream_buf_uninit(struct sbus_soft_state *softsp);
static int
find_sbus_slot(dev_info_t *dip, dev_info_t *rdip);
static void make_sbus_ppd(dev_info_t *child);
static int
sbusmem_initchild(dev_info_t *dip, dev_info_t *child);
static int
sbus_initchild(dev_info_t *dip, dev_info_t *child);
static int
sbus_uninitchild(dev_info_t *dip);
static int
sbus_ctlops_poke(struct sbus_soft_state *softsp, peekpoke_ctlops_t *in_args);
static int
sbus_ctlops_peek(struct sbus_soft_state *softsp, peekpoke_ctlops_t *in_args,
void *result);
static int
sbus_init(struct sbus_soft_state *softsp, caddr_t address);
static int
sbus_resume_init(struct sbus_soft_state *softsp, int resume);
static void
sbus_cpr_handle_intr_map_reg(uint64_t *cpr_softsp, volatile uint64_t *baddr,
int flag);
static void sbus_intrdist(void *);
static uint_t sbus_intr_reset(void *);
static int
sbus_update_intr_state(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp, uint_t new_intr_state);
static struct bus_ops sbus_bus_ops = {
BUSO_REV,
i_ddi_bus_map,
0,
0,
0,
i_ddi_map_fault,
0,
iommu_dma_allochdl,
iommu_dma_freehdl,
iommu_dma_bindhdl,
iommu_dma_unbindhdl,
iommu_dma_flush,
iommu_dma_win,
iommu_dma_mctl,
sbus_ctlops,
ddi_bus_prop_op,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
sbus_intr_ops
};
static struct cb_ops sbus_cb_ops = {
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
NULL,
D_NEW | D_MP | D_HOTPLUG,
CB_REV,
nodev,
nodev
};
static struct dev_ops sbus_ops = {
DEVO_REV,
0,
ddi_no_info,
nulldev,
nulldev,
sbus_attach,
sbus_detach,
nodev,
&sbus_cb_ops,
&sbus_bus_ops,
nulldev,
ddi_quiesce_not_supported,
};
void *sbusp;
void *sbus_cprp;
static kstat_t *sbus_picN_ksp[SBUS_NUM_PICS];
static int sbus_attachcnt = 0;
static kmutex_t sbus_attachcnt_mutex;
#include <sys/modctl.h>
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops,
"SBus (sysio) nexus driver",
&sbus_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
int
_init(void)
{
int error;
if ((error = ddi_soft_state_init(&sbusp,
sizeof (struct sbus_soft_state), 1)) != 0)
return (error);
if ((error = ddi_soft_state_init(&sbus_cprp,
sizeof (uint64_t) * MAX_INO_TABLE_SIZE, 0)) != 0)
return (error);
mutex_init(&sbus_attachcnt_mutex, NULL, MUTEX_DRIVER, NULL);
return (mod_install(&modlinkage));
}
int
_fini(void)
{
int error;
if ((error = mod_remove(&modlinkage)) != 0)
return (error);
mutex_destroy(&sbus_attachcnt_mutex);
ddi_soft_state_fini(&sbusp);
ddi_soft_state_fini(&sbus_cprp);
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
sbus_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
struct sbus_soft_state *softsp;
int instance, error;
uint64_t *cpr_softsp;
ddi_device_acc_attr_t attr;
#ifdef DEBUG
debug_info = 1;
debug_print_level = 0;
#endif
instance = ddi_get_instance(devi);
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
softsp = ddi_get_soft_state(sbusp, instance);
if ((error = iommu_resume_init(softsp)) != DDI_SUCCESS)
return (error);
if ((error = sbus_resume_init(softsp, 1)) != DDI_SUCCESS)
return (error);
if ((error = stream_buf_resume_init(softsp)) != DDI_SUCCESS)
return (error);
cpr_softsp = ddi_get_soft_state(sbus_cprp, instance);
if (cpr_softsp != NULL) {
sbus_cpr_handle_intr_map_reg(cpr_softsp,
softsp->intr_mapping_reg, 0);
ddi_soft_state_free(sbus_cprp, instance);
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (ddi_soft_state_zalloc(sbusp, instance) != DDI_SUCCESS)
return (DDI_FAILURE);
softsp = ddi_get_soft_state(sbusp, instance);
softsp->dip = devi;
if ((softsp->upa_id = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
DDI_PROP_DONTPASS, "upa-portid", -1)) == -1) {
cmn_err(CE_WARN, "Unable to retrieve sbus upa-portid"
"property.");
error = DDI_FAILURE;
goto bad;
}
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(softsp->dip, 0, &softsp->address, 0, 0,
&attr, &softsp->ac) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: unable to map reg set 0\n",
ddi_get_name(softsp->dip),
ddi_get_instance(softsp->dip));
return (0);
}
if (softsp->address == (caddr_t)-1) {
cmn_err(CE_CONT, "?sbus%d: No sysio <address> property\n",
ddi_get_instance(softsp->dip));
return (DDI_FAILURE);
}
DPRINTF(SBUS_ATTACH_DEBUG, ("sbus: devi=0x%p, softsp=0x%p\n",
(void *)devi, (void *)softsp));
#ifdef notdef
{
caddr_t addr;
int rv;
cmn_err(CE_CONT, "?sbus: address property = 0x%x\n", address);
if ((rv = ddi_map_regs(softsp->dip, 0, &addr,
(off_t)0, (off_t)0)) != DDI_SUCCESS) {
cmn_err(CE_CONT, "?sbus: ddi_map_regs failed: %d\n",
rv);
} else {
cmn_err(CE_CONT, "?sbus: ddi_map_regs returned "
" virtual address 0x%x\n", addr);
}
}
#endif
if ((error = iommu_init(softsp, softsp->address)) != DDI_SUCCESS)
goto bad;
if ((error = sbus_init(softsp, softsp->address)) != DDI_SUCCESS)
goto bad;
if ((error = sysio_err_init(softsp, softsp->address)) != DDI_SUCCESS)
goto bad;
if ((error = stream_buf_init(softsp, softsp->address)) != DDI_SUCCESS)
goto bad;
mutex_init(&softsp->pokefault_mutex, NULL, MUTEX_SPIN,
(void *)ipltospl(SBUS_ERR_PIL - 1));
sbus_add_kstats(softsp);
bus_func_register(BF_TYPE_RESINTR, sbus_intr_reset, devi);
intr_dist_add(sbus_intrdist, devi);
ddi_report_dev(devi);
return (DDI_SUCCESS);
bad:
ddi_soft_state_free(sbusp, instance);
return (error);
}
static int
sbus_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
int instance;
struct sbus_soft_state *softsp;
uint64_t *cpr_softsp;
switch (cmd) {
case DDI_SUSPEND:
instance = ddi_get_instance(devi);
if (ddi_soft_state_zalloc(sbus_cprp, instance)
!= DDI_SUCCESS)
return (DDI_FAILURE);
cpr_softsp = ddi_get_soft_state(sbus_cprp, instance);
softsp = ddi_get_soft_state(sbusp, instance);
sbus_cpr_handle_intr_map_reg(cpr_softsp,
softsp->intr_mapping_reg, 1);
return (DDI_SUCCESS);
case DDI_DETACH:
return (sbus_do_detach(devi));
default:
return (DDI_FAILURE);
}
}
static int
sbus_do_detach(dev_info_t *devi)
{
int instance, pic;
struct sbus_soft_state *softsp;
instance = ddi_get_instance(devi);
softsp = ddi_get_soft_state(sbusp, instance);
ASSERT(softsp != NULL);
bus_func_unregister(BF_TYPE_RESINTR, sbus_intr_reset, devi);
intr_dist_rem(sbus_intrdist, devi);
if (stream_buf_uninit(softsp) == DDI_FAILURE) {
goto err;
}
if (sysio_err_uninit(softsp) == DDI_FAILURE) {
goto err;
}
if (iommu_uninit(softsp)) {
goto err;
}
if (softsp->ac) {
ddi_regs_map_free(&softsp->ac);
softsp->address = NULL;
}
if (softsp->sbus_counters_ksp != (kstat_t *)NULL)
kstat_delete(softsp->sbus_counters_ksp);
mutex_enter(&sbus_attachcnt_mutex);
sbus_attachcnt --;
if (sbus_attachcnt == 0) {
for (pic = 0; pic < SBUS_NUM_PICS; pic++) {
if (sbus_picN_ksp[pic] != (kstat_t *)NULL) {
kstat_delete(sbus_picN_ksp[pic]);
sbus_picN_ksp[pic] = NULL;
}
}
}
mutex_exit(&sbus_attachcnt_mutex);
ddi_soft_state_free(sbusp, instance);
return (DDI_SUCCESS);
err:
return (DDI_FAILURE);
}
static int
sbus_init(struct sbus_soft_state *softsp, caddr_t address)
{
int i;
extern void set_intr_mapping_reg(int, uint64_t *, int);
int numproxy;
#define REG_ADDR(b, o) (uint64_t *)((caddr_t)(b) + (o))
softsp->sysio_ctrl_reg = REG_ADDR(address, OFF_SYSIO_CTRL_REG);
softsp->sbus_ctrl_reg = REG_ADDR(address, OFF_SBUS_CTRL_REG);
softsp->sbus_slot_config_reg = REG_ADDR(address, OFF_SBUS_SLOT_CONFIG);
softsp->intr_mapping_reg = REG_ADDR(address, OFF_INTR_MAPPING_REG);
softsp->clr_intr_reg = REG_ADDR(address, OFF_CLR_INTR_REG);
softsp->intr_retry_reg = REG_ADDR(address, OFF_INTR_RETRY_REG);
softsp->sbus_intr_state = REG_ADDR(address, OFF_SBUS_INTR_STATE_REG);
softsp->sbus_pcr = REG_ADDR(address, OFF_SBUS_PCR);
softsp->sbus_pic = REG_ADDR(address, OFF_SBUS_PIC);
#undef REG_ADDR
DPRINTF(SBUS_REGISTERS_DEBUG, ("SYSIO Control reg: 0x%p\n"
"SBUS Control reg: 0x%p", (void *)softsp->sysio_ctrl_reg,
(void *)softsp->sbus_ctrl_reg));
softsp->intr_mapping_ign =
UPAID_TO_IGN(softsp->upa_id) << IMR_IGN_SHIFT;
softsp->obio_intr_state = softsp->sbus_intr_state + 1;
(void) sbus_resume_init(softsp, 0);
for (i = 0; i < MAX_SBUS_SLOTS; i++)
softsp->sbus_slave_burstsizes[i] = 0xffffffffu;
numproxy = ddi_prop_get_int(DDI_DEV_T_ANY, softsp->dip,
DDI_PROP_DONTPASS, "#upa-interrupt-proxies", 1);
if (numproxy > 0)
set_intr_mapping_reg(softsp->upa_id,
(uint64_t *)(softsp->intr_mapping_reg +
FFB_MAPPING_REG), 1);
if (numproxy > 1)
set_intr_mapping_reg(softsp->upa_id,
(uint64_t *)(softsp->intr_mapping_reg +
EXP_MAPPING_REG), 2);
#ifndef DEBUG
intr_cntr_on = 1;
#else
intr_cntr_on = 0;
#endif
return (DDI_SUCCESS);
}
static int
sbus_resume_init(struct sbus_soft_state *softsp, int resume)
{
int i;
uint_t sbus_burst_sizes;
*softsp->sysio_ctrl_reg |=
(uint64_t)softsp->upa_id << 51;
*softsp->sysio_ctrl_reg |=
(uint64_t)softsp->upa_id << SYSIO_IGN;
*softsp->sbus_ctrl_reg |= SBUS_ARBIT_ALL;
sbus_burst_sizes = (SYSIO64_BURST_RANGE << SYSIO64_BURST_SHIFT)
| SYSIO_BURST_RANGE;
sbus_burst_sizes = ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
DDI_PROP_DONTPASS, "up-burst-sizes", sbus_burst_sizes);
softsp->sbus_burst_sizes = sbus_burst_sizes & SYSIO_BURST_MASK;
softsp->sbus64_burst_sizes = sbus_burst_sizes & SYSIO64_BURST_MASK;
if (!resume) {
for (i = 0; i < MAX_SBUS_SLOTS; i++) {
volatile uint64_t *config;
uint64_t tmpreg;
config = softsp->sbus_slot_config_reg + i;
tmpreg = (uint64_t)0;
*config = tmpreg;
tmpreg = *softsp->sbus_ctrl_reg;
DPRINTF(SBUS_REGISTERS_DEBUG, ("Sbus slot 0x%x slot "
"configuration reg: 0x%p", (i > 3) ? i + 9 : i,
(void *)config));
}
} else {
for (i = 0; i < MAX_SBUS_SLOTS; i++) {
volatile uint64_t *config;
#ifndef lint
uint64_t tmpreg;
#endif
uint_t slave_burstsizes;
slave_burstsizes = 0;
if (softsp->sbus_slave_burstsizes[i] != 0xffffffffu) {
config = softsp->sbus_slot_config_reg + i;
if (softsp->sbus_slave_burstsizes[i] &
SYSIO64_BURST_MASK) {
slave_burstsizes =
softsp->sbus_slave_burstsizes[i] >>
SYSIO64_BURST_SHIFT;
*config |= SBUS_ETM;
} else {
slave_burstsizes =
softsp->sbus_slave_burstsizes[i] &
SYSIO_BURST_MASK;
}
slave_burstsizes >>= SYSIO_SLAVEBURST_REGSHIFT;
*config |= (uint64_t)slave_burstsizes;
#ifndef lint
tmpreg = *softsp->sbus_ctrl_reg;
#endif
}
}
}
return (DDI_SUCCESS);
}
#define get_prop(di, pname, flag, pval, plen) \
(ddi_prop_op(DDI_DEV_T_NONE, di, PROP_LEN_AND_VAL_ALLOC, \
flag | DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, \
pname, (caddr_t)pval, plen))
struct prop_ispec {
uint_t pri, vec;
};
static void
make_sbus_ppd(dev_info_t *child)
{
struct sysio_parent_private_data *pdptr;
int n;
int *reg_prop, *rgstr_prop, *rng_prop;
int reg_len, rgstr_len, rng_len;
if (ddi_get_parent_data(child) != NULL)
return;
pdptr = kmem_zalloc(sizeof (*pdptr), KM_SLEEP);
ddi_set_parent_data(child, pdptr);
if (get_prop(child, OBP_REG, 0, ®_prop, ®_len) != DDI_SUCCESS)
reg_len = 0;
pdptr->slot = (uint_t)-1;
if (reg_len != 0) {
pdptr->slot = ((struct regspec *)reg_prop)->regspec_bustype;
pdptr->offset = ((struct regspec *)reg_prop)->regspec_addr;
}
rgstr_len = 0;
(void) get_prop(child, "registers", DDI_PROP_NOTPROM,
&rgstr_prop, &rgstr_len);
if (rgstr_len != 0) {
if (ndi_dev_is_persistent_node(child) && (reg_len != 0)) {
struct regspec *rp = (struct regspec *)reg_prop;
uint_t slot = rp->regspec_bustype;
int i;
rp = (struct regspec *)rgstr_prop;
n = rgstr_len / sizeof (struct regspec);
for (i = 0; i < n; ++i, ++rp)
rp->regspec_bustype = slot;
}
if (reg_len != 0)
kmem_free(reg_prop, reg_len);
reg_prop = rgstr_prop;
reg_len = rgstr_len;
}
if (reg_len != 0) {
pdptr->par_nreg = reg_len / (int)sizeof (struct regspec);
pdptr->par_reg = (struct regspec *)reg_prop;
}
if (get_prop(child, OBP_RANGES, 0, &rng_prop, &rng_len) ==
DDI_SUCCESS) {
pdptr->par_nrng = rng_len / (int)(sizeof (struct rangespec));
pdptr->par_rng = (struct rangespec *)rng_prop;
}
}
static int
sbusmem_initchild(dev_info_t *dip, dev_info_t *child)
{
int i, n;
int slot, size;
char ident[10];
slot = ddi_getprop(DDI_DEV_T_NONE, child,
DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "slot", -1);
if (slot == -1) {
DPRINTF(SBUS_SBUSMEM_DEBUG, ("can't get slot property\n"));
return (DDI_FAILURE);
}
for (i = 0, n = sparc_pd_getnrng(dip); i < n; i++) {
struct rangespec *rp = sparc_pd_getrng(dip, i);
if (rp->rng_cbustype == (uint_t)slot) {
struct regspec r;
r.regspec_bustype = (uint_t)slot;
r.regspec_addr = 0;
r.regspec_size = rp->rng_size;
(void) ddi_prop_update_int_array(DDI_DEV_T_NONE,
child, "reg", (int *)&r,
sizeof (struct regspec) / sizeof (int));
size = rp->rng_size;
(void) ddi_prop_update_int(DDI_DEV_T_NONE,
child, "size", size);
(void) sprintf(ident, "slot%x", slot);
(void) ddi_prop_update_string(DDI_DEV_T_NONE,
child, "ident", ident);
return (DDI_SUCCESS);
}
}
return (DDI_FAILURE);
}
static int
sysio_name_child(dev_info_t *child, char *name, int namelen)
{
make_sbus_ppd(child);
name[0] = '\0';
if (sysio_pd_getslot(child) != (uint_t)-1) {
(void) snprintf(name, namelen, "%x,%x",
sysio_pd_getslot(child), sysio_pd_getoffset(child));
}
return (DDI_SUCCESS);
}
static int
sbus_initchild(dev_info_t *dip, dev_info_t *child)
{
char name[MAXNAMELEN];
ulong_t slave_burstsizes;
int slot;
volatile uint64_t *slot_reg;
#ifndef lint
uint64_t tmp;
#endif
struct sbus_soft_state *softsp = (struct sbus_soft_state *)
ddi_get_soft_state(sbusp, ddi_get_instance(dip));
if (strcmp(ddi_get_name(child), "sbusmem") == 0) {
if (sbusmem_initchild(dip, child) != DDI_SUCCESS)
return (DDI_FAILURE);
}
if (ndi_dev_is_persistent_node(child) == 0) {
int len = 0;
if ((ddi_getproplen(DDI_DEV_T_ANY, child, DDI_PROP_NOTPROM,
"registers", &len) == DDI_SUCCESS) && (len != 0)) {
ndi_merge_wildcard_node(child);
return (DDI_FAILURE);
}
}
(void) sysio_name_child(child, name, MAXNAMELEN);
ddi_set_name_addr(child, name);
if ((ndi_dev_is_persistent_node(child) == 0) &&
(ndi_merge_node(child, sysio_name_child) == DDI_SUCCESS)) {
(void) sbus_uninitchild(child);
return (DDI_FAILURE);
}
slot = sysio_pd_getslot(child);
if (slot < 0 || slot >= MAX_SBUS_SLOT_ADDR) {
#ifdef DEBUG
cmn_err(CE_WARN, "?Invalid sbus slot address 0x%x for %s "
"device\n", slot, ddi_get_name(child));
#endif
goto done;
}
slot = (slot > 3) ? slot - 9 : slot;
slot_reg = softsp->sbus_slot_config_reg + slot;
if ((slave_burstsizes = (ulong_t)ddi_getprop(DDI_DEV_T_ANY, child,
DDI_PROP_DONTPASS, "slave-burst-sizes", 0)) != 0 ||
(slave_burstsizes = (ulong_t)ddi_getprop(DDI_DEV_T_ANY, child,
DDI_PROP_DONTPASS, "burst-sizes", 0)) != 0) {
uint_t burstsizes = 0;
if (((softsp->sbus_slave_burstsizes[slot] &
0xffff0000u) == 0) &&
((softsp->sbus_slave_burstsizes[slot] & 0xffff) != 0)) {
slave_burstsizes &= 0xffff;
}
if (slave_burstsizes == 0)
goto done;
softsp->sbus_slave_burstsizes[slot] &=
(slave_burstsizes &
((SYSIO64_SLAVEBURST_RANGE <<
SYSIO64_BURST_SHIFT) |
SYSIO_SLAVEBURST_RANGE));
if (softsp->sbus_slave_burstsizes[slot] &
SYSIO64_BURST_MASK) {
burstsizes = softsp->sbus_slave_burstsizes[slot] >>
SYSIO64_BURST_SHIFT;
*slot_reg |= SBUS_ETM;
} else {
*slot_reg &= ~SBUS_ETM;
if (softsp->sbus_slave_burstsizes[slot] &
SYSIO_BURST_MASK) {
burstsizes =
softsp->sbus_slave_burstsizes[slot] &
SYSIO_BURST_MASK;
}
}
burstsizes >>= SYSIO_SLAVEBURST_REGSHIFT;
*slot_reg &= (uint64_t)~SYSIO_SLAVEBURST_MASK;
*slot_reg |= (uint64_t)burstsizes;
#ifndef lint
tmp = *slot_reg;
#endif
}
done:
return (DDI_SUCCESS);
}
static int
sbus_uninitchild(dev_info_t *dip)
{
struct sysio_parent_private_data *pdptr;
size_t n;
if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
if ((n = (size_t)pdptr->par_nrng) != 0)
kmem_free(pdptr->par_rng, n *
sizeof (struct rangespec));
if ((n = pdptr->par_nreg) != 0)
kmem_free(pdptr->par_reg, n * sizeof (struct regspec));
kmem_free(pdptr, sizeof (*pdptr));
ddi_set_parent_data(dip, NULL);
}
ddi_set_name_addr(dip, NULL);
ddi_remove_minor_node(dip, NULL);
impl_rem_dev_props(dip);
return (DDI_SUCCESS);
}
#ifdef DEBUG
int sbus_peekfault_cnt = 0;
int sbus_pokefault_cnt = 0;
#endif
static int
sbus_ctlops_poke(struct sbus_soft_state *softsp, peekpoke_ctlops_t *in_args)
{
int err = DDI_SUCCESS;
on_trap_data_t otd;
volatile uint64_t tmpreg;
if (in_args->handle != NULL)
return (DDI_FAILURE);
mutex_enter(&softsp->pokefault_mutex);
softsp->ontrap_data = &otd;
if (!on_trap(&otd, OT_DATA_ACCESS)) {
uintptr_t tramp = otd.ot_trampoline;
otd.ot_trampoline = (uintptr_t)&poke_fault;
err = do_poke(in_args->size, (void *)in_args->dev_addr,
(void *)in_args->host_addr);
otd.ot_trampoline = tramp;
} else
err = DDI_FAILURE;
tmpreg = *softsp->sbus_ctrl_reg;
tmpreg = *softsp->sbus_err_reg;
while (tmpreg & SB_AFSR_P_TO || tmpreg & SB_AFSR_P_BERR)
tmpreg = *softsp->sbus_err_reg;
no_trap();
softsp->ontrap_data = NULL;
mutex_exit(&softsp->pokefault_mutex);
#ifdef DEBUG
if (err == DDI_FAILURE)
sbus_pokefault_cnt++;
#endif
return (err);
}
static int
sbus_ctlops_peek(struct sbus_soft_state *softsp, peekpoke_ctlops_t *in_args,
void *result)
{
int err = DDI_SUCCESS;
on_trap_data_t otd;
if (in_args->handle != NULL)
return (DDI_FAILURE);
if (!on_trap(&otd, OT_DATA_ACCESS)) {
uintptr_t tramp = otd.ot_trampoline;
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;
result = (void *)in_args->host_addr;
} else
err = DDI_FAILURE;
#ifdef DEBUG
if (err == DDI_FAILURE)
sbus_peekfault_cnt++;
#endif
no_trap();
return (err);
}
static int
sbus_ctlops(dev_info_t *dip, dev_info_t *rdip,
ddi_ctl_enum_t op, void *arg, void *result)
{
struct sbus_soft_state *softsp = (struct sbus_soft_state *)
ddi_get_soft_state(sbusp, ddi_get_instance(dip));
switch (op) {
case DDI_CTLOPS_INITCHILD:
return (sbus_initchild(dip, (dev_info_t *)arg));
case DDI_CTLOPS_UNINITCHILD:
return (sbus_uninitchild(arg));
case DDI_CTLOPS_IOMIN: {
int val = *((int *)result);
if ((int)(uintptr_t)arg)
val = maxbit(val,
(1 << (ddi_fls(softsp->sbus_burst_sizes) - 1)));
else
val = maxbit(val,
(1 << (ddi_ffs(softsp->sbus_burst_sizes) - 1)));
*((int *)result) = val;
return (ddi_ctlops(dip, rdip, op, arg, result));
}
case DDI_CTLOPS_REPORTDEV: {
dev_info_t *pdev;
int i, n, len, f_len;
char *msgbuf;
#define REPORTDEV_BUFSIZE 1024
int sbusid = ddi_get_instance(dip);
if (ddi_get_parent_data(rdip) == NULL)
return (DDI_FAILURE);
msgbuf = kmem_zalloc(REPORTDEV_BUFSIZE, KM_SLEEP);
pdev = ddi_get_parent(rdip);
f_len = snprintf(msgbuf, REPORTDEV_BUFSIZE,
"%s%d at %s%d: SBus%d ",
ddi_driver_name(rdip), ddi_get_instance(rdip),
ddi_driver_name(pdev), ddi_get_instance(pdev), sbusid);
len = strlen(msgbuf);
for (i = 0, n = sysio_pd_getnreg(rdip); i < n; i++) {
struct regspec *rp;
rp = sysio_pd_getreg(rdip, i);
if (i != 0) {
f_len += snprintf(msgbuf + len,
REPORTDEV_BUFSIZE - len, " and ");
len = strlen(msgbuf);
}
f_len += snprintf(msgbuf + len, REPORTDEV_BUFSIZE - len,
"slot 0x%x offset 0x%x",
rp->regspec_bustype, rp->regspec_addr);
len = strlen(msgbuf);
}
for (i = 0, n = i_ddi_get_intx_nintrs(rdip); i < n; i++) {
uint32_t sbuslevel, inum, pri;
if (i != 0) {
f_len += snprintf(msgbuf + len,
REPORTDEV_BUFSIZE - len, ",");
len = strlen(msgbuf);
}
sbuslevel = inum = i_ddi_get_inum(rdip, i);
pri = i_ddi_get_intr_pri(rdip, i);
(void) sbus_xlate_intrs(dip, rdip, &inum,
&pri, softsp->intr_mapping_ign);
if (sbuslevel > MAX_SBUS_LEVEL)
f_len += snprintf(msgbuf + len,
REPORTDEV_BUFSIZE - len,
" Onboard device ");
else
f_len += snprintf(msgbuf + len,
REPORTDEV_BUFSIZE - len, " SBus level %d ",
sbuslevel);
len = strlen(msgbuf);
f_len += snprintf(msgbuf + len, REPORTDEV_BUFSIZE - len,
"sparc9 ipl %d", pri);
len = strlen(msgbuf);
}
#ifdef DEBUG
if (f_len + 1 >= REPORTDEV_BUFSIZE) {
cmn_err(CE_NOTE, "next message is truncated: "
"printed length 1024, real length %d", f_len);
}
#endif
cmn_err(CE_CONT, "?%s\n", msgbuf);
kmem_free(msgbuf, REPORTDEV_BUFSIZE);
return (DDI_SUCCESS);
#undef REPORTDEV_BUFSIZE
}
case DDI_CTLOPS_SLAVEONLY:
return (DDI_FAILURE);
case DDI_CTLOPS_AFFINITY: {
dev_info_t *dipb = (dev_info_t *)arg;
int r_slot, b_slot;
if ((b_slot = find_sbus_slot(dip, dipb)) < 0)
return (DDI_FAILURE);
if ((r_slot = find_sbus_slot(dip, rdip)) < 0)
return (DDI_FAILURE);
return ((b_slot == r_slot)? DDI_SUCCESS : DDI_FAILURE);
}
case DDI_CTLOPS_DMAPMAPC:
cmn_err(CE_CONT, "?DDI_DMAPMAPC called!!\n");
return (DDI_FAILURE);
case DDI_CTLOPS_POKE:
return (sbus_ctlops_poke(softsp, (peekpoke_ctlops_t *)arg));
case DDI_CTLOPS_PEEK:
return (sbus_ctlops_peek(softsp, (peekpoke_ctlops_t *)arg,
result));
case DDI_CTLOPS_DVMAPAGESIZE:
*(ulong_t *)result = IOMMU_PAGESIZE;
return (DDI_SUCCESS);
default:
return (ddi_ctlops(dip, rdip, op, arg, result));
}
}
static int
find_sbus_slot(dev_info_t *dip, dev_info_t *rdip)
{
dev_info_t *child;
int slot = -1;
while (rdip && (child = ddi_get_parent(rdip)) != dip) {
rdip = child;
}
if (child == dip)
slot = sysio_pd_getslot(rdip);
return (slot);
}
#define MAX_INTR_CNT 10
static uint_t
sbus_intr_wrapper(caddr_t arg)
{
uint_t intr_return = DDI_INTR_UNCLAIMED;
volatile uint64_t tmpreg;
struct sbus_wrapper_arg *intr_info;
struct sbus_intr_handler *intr_handler;
uchar_t *spurious_cntr;
intr_info = (struct sbus_wrapper_arg *)arg;
spurious_cntr = &intr_info->softsp->spurious_cntrs[intr_info->pil];
intr_handler = intr_info->handler_list;
while (intr_handler) {
caddr_t arg1 = intr_handler->arg1;
caddr_t arg2 = intr_handler->arg2;
uint_t (*funcp)() = intr_handler->funcp;
dev_info_t *dip = intr_handler->dip;
int r;
if (intr_handler->intr_state == SBUS_INTR_STATE_DISABLE) {
intr_handler = intr_handler->next;
continue;
}
DTRACE_PROBE4(interrupt__start, dev_info_t, dip,
void *, funcp, caddr_t, arg1, caddr_t, arg2);
r = (*funcp)(arg1, arg2);
DTRACE_PROBE4(interrupt__complete, dev_info_t, dip,
void *, funcp, caddr_t, arg1, int, r);
intr_return |= r;
intr_handler = intr_handler->next;
}
tmpreg = *intr_info->softsp->sbus_ctrl_reg;
tmpreg = SBUS_INTR_IDLE;
*intr_info->clear_reg = tmpreg;
tmpreg = *intr_info->softsp->sbus_ctrl_reg;
if (intr_return == DDI_INTR_UNCLAIMED) {
(*spurious_cntr)++;
if (*spurious_cntr < MAX_INTR_CNT) {
if (intr_cntr_on)
return (DDI_INTR_CLAIMED);
}
#ifdef DEBUG
else if (intr_info->pil >= LOCK_LEVEL) {
cmn_err(CE_PANIC, "%d unclaimed interrupts at "
"interrupt level %d", MAX_INTR_CNT,
intr_info->pil);
}
#endif
*spurious_cntr = (uchar_t)0;
} else {
*spurious_cntr = (uchar_t)0;
}
return (intr_return);
}
static int
sbus_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp)
{
struct sbus_soft_state *softsp = (struct sbus_soft_state *)
ddi_get_soft_state(sbusp, ddi_get_instance(dip));
volatile uint64_t *mondo_vec_reg;
volatile uint64_t tmp_mondo_vec;
volatile uint64_t *intr_state_reg;
volatile uint64_t tmpreg;
uint_t start_bit;
int ino;
uint_t cpu_id;
struct sbus_wrapper_arg *sbus_arg;
struct sbus_intr_handler *intr_handler;
int slot;
int reset_ism_register = 1;
int ret = DDI_SUCCESS;
slot = find_sbus_slot(dip, rdip);
if (slot >= MAX_SBUS_SLOT_ADDR || slot < 0) {
cmn_err(CE_WARN, "Invalid sbus slot 0x%x during add intr\n",
slot);
return (DDI_FAILURE);
}
DPRINTF(SBUS_INTERRUPT_DEBUG, ("Add intr: sbus interrupt %d "
"for device %s%d\n", hdlp->ih_vector, ddi_driver_name(rdip),
ddi_get_instance(rdip)));
if (sbus_xlate_intrs(dip, rdip, (uint32_t *)&hdlp->ih_vector,
&hdlp->ih_pri, softsp->intr_mapping_ign) == DDI_FAILURE) {
cmn_err(CE_WARN, "Can't xlate SBUS devices %s interrupt.\n",
ddi_driver_name(rdip));
return (DDI_FAILURE);
}
ino = hdlp->ih_vector & SBUS_MAX_INO;
mondo_vec_reg = (softsp->intr_mapping_reg +
ino_table[ino]->mapping_reg);
if (ino > MAX_MONDO_EXTERNAL) {
start_bit = ino_table[ino]->diagreg_shift;
intr_state_reg = softsp->obio_intr_state;
} else {
start_bit = 16 * (ino >> 3) + 2 * (ino & 0x7);
intr_state_reg = softsp->sbus_intr_state;
}
intr_handler = kmem_zalloc(sizeof (struct sbus_intr_handler), KM_SLEEP);
intr_handler->dip = rdip;
intr_handler->funcp = hdlp->ih_cb_func;
intr_handler->arg1 = hdlp->ih_cb_arg1;
intr_handler->arg2 = hdlp->ih_cb_arg2;
intr_handler->inum = hdlp->ih_inum;
DPRINTF(SBUS_INTERRUPT_DEBUG, ("Add intr: xlated interrupt 0x%x "
"intr_handler 0x%p\n", hdlp->ih_vector, (void *)intr_handler));
mutex_enter(&softsp->intr_poll_list_lock);
sbus_arg = softsp->intr_list[ino];
if (sbus_arg) {
tmp_mondo_vec = *mondo_vec_reg;
tmp_mondo_vec &= ~INTERRUPT_VALID;
*mondo_vec_reg = tmp_mondo_vec;
tmpreg = *softsp->sbus_ctrl_reg;
#ifdef lint
tmpreg = tmpreg;
#endif
DPRINTF(SBUS_INTERRUPT_DEBUG, ("Add intr:sbus_arg exists "
"0x%p\n", (void *)sbus_arg));
while (((*intr_state_reg >>
start_bit) & 0x3) == INT_PENDING && !panicstr)
;
intr_handler->next = sbus_arg->handler_list;
sbus_arg->handler_list = intr_handler;
reset_ism_register = 0;
} else {
sbus_arg = kmem_zalloc(sizeof (struct sbus_wrapper_arg),
KM_SLEEP);
softsp->intr_list[ino] = sbus_arg;
sbus_arg->clear_reg = (softsp->clr_intr_reg +
ino_table[ino]->clear_reg);
DPRINTF(SBUS_INTERRUPT_DEBUG, ("Add intr:Ino 0x%x Interrupt "
"clear reg: 0x%p\n", ino, (void *)sbus_arg->clear_reg));
sbus_arg->softsp = softsp;
sbus_arg->handler_list = intr_handler;
DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
(ddi_intr_handler_t *)sbus_intr_wrapper,
(caddr_t)sbus_arg, NULL);
ret = i_ddi_add_ivintr(hdlp);
DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, intr_handler->funcp,
intr_handler->arg1, intr_handler->arg2);
if (ret != DDI_SUCCESS) {
mutex_exit(&softsp->intr_poll_list_lock);
goto done;
}
if ((slot >= EXT_SBUS_SLOTS) ||
(softsp->intr_hndlr_cnt[slot] == 0)) {
cpu_id = intr_dist_cpuid();
tmp_mondo_vec =
cpu_id << IMR_TID_SHIFT;
DPRINTF(SBUS_INTERRUPT_DEBUG, ("Add intr: initial "
"mapping reg 0x%lx\n", tmp_mondo_vec));
} else {
tmp_mondo_vec = *mondo_vec_reg;
tmp_mondo_vec &= ~INTERRUPT_VALID;
*mondo_vec_reg = tmp_mondo_vec;
DPRINTF(SBUS_INTERRUPT_DEBUG, ("Add intr: existing "
"mapping reg 0x%lx\n", tmp_mondo_vec));
}
sbus_arg->pil = hdlp->ih_pri;
DPRINTF(SBUS_INTERRUPT_DEBUG, ("Add intr:Alloc sbus_arg "
"0x%p\n", (void *)sbus_arg));
}
softsp->intr_hndlr_cnt[slot]++;
mutex_exit(&softsp->intr_poll_list_lock);
tmp_mondo_vec |= INTERRUPT_VALID;
DPRINTF(SBUS_INTERRUPT_DEBUG, ("Add intr: Ino 0x%x mapping reg: 0x%p "
"Intr cntr %d\n", ino, (void *)mondo_vec_reg,
softsp->intr_hndlr_cnt[slot]));
if (reset_ism_register) {
tmpreg = SBUS_INTR_IDLE;
*sbus_arg->clear_reg = tmpreg;
}
*mondo_vec_reg = tmp_mondo_vec;
tmpreg = *softsp->sbus_ctrl_reg;
done:
return (ret);
}
static void
sbus_free_handler(dev_info_t *dip, uint32_t inum,
struct sbus_wrapper_arg *sbus_arg)
{
struct sbus_intr_handler *listp, *prevp;
if (sbus_arg) {
prevp = NULL;
listp = sbus_arg->handler_list;
while (listp) {
if (listp->dip == dip && listp->inum == inum) {
if (prevp)
prevp->next = listp->next;
else {
prevp = listp->next;
sbus_arg->handler_list = prevp;
}
kmem_free(listp,
sizeof (struct sbus_intr_handler));
break;
}
prevp = listp;
listp = listp->next;
}
}
}
static void
sbus_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp)
{
volatile uint64_t *mondo_vec_reg;
volatile uint64_t *intr_state_reg;
#ifndef lint
volatile uint64_t tmpreg;
#endif
struct sbus_soft_state *softsp = (struct sbus_soft_state *)
ddi_get_soft_state(sbusp, ddi_get_instance(dip));
int start_bit, ino, slot;
struct sbus_wrapper_arg *sbus_arg;
mutex_enter(&softsp->intr_poll_list_lock);
if (sbus_xlate_intrs(dip, rdip, (uint32_t *)&hdlp->ih_vector,
&hdlp->ih_pri, softsp->intr_mapping_ign) == DDI_FAILURE) {
cmn_err(CE_WARN, "Can't xlate SBUS devices %s interrupt.\n",
ddi_driver_name(rdip));
goto done;
}
ino = ((int32_t)hdlp->ih_vector) & SBUS_MAX_INO;
mondo_vec_reg = (softsp->intr_mapping_reg +
ino_table[ino]->mapping_reg);
*mondo_vec_reg &= ~INTERRUPT_VALID;
#ifndef lint
tmpreg = *softsp->sbus_ctrl_reg;
#endif
if (ino > MAX_MONDO_EXTERNAL) {
start_bit = ino_table[ino]->diagreg_shift;
intr_state_reg = softsp->obio_intr_state;
} else {
start_bit = 16 * (ino >> 3) + 2 * (ino & 0x7);
intr_state_reg = softsp->sbus_intr_state;
}
while (((*intr_state_reg >> start_bit) & 0x3) == INT_PENDING &&
!panicstr)
;
slot = find_sbus_slot(dip, rdip);
if (slot >= MAX_SBUS_SLOT_ADDR || slot < 0) {
goto done;
}
sbus_arg = softsp->intr_list[ino];
softsp->intr_hndlr_cnt[slot]--;
DPRINTF(SBUS_INTERRUPT_DEBUG, ("Rem intr: Softsp 0x%p, Mondo 0x%x, "
"ino 0x%x, sbus_arg 0x%p intr cntr %d\n", (void *)softsp,
hdlp->ih_vector, ino, (void *)sbus_arg,
softsp->intr_hndlr_cnt[slot]));
ASSERT(sbus_arg != NULL);
ASSERT(sbus_arg->handler_list != NULL);
sbus_free_handler(rdip, hdlp->ih_inum, sbus_arg);
if (sbus_arg->handler_list == NULL)
i_ddi_rem_ivintr(hdlp);
if (softsp->intr_hndlr_cnt[slot] > 0) {
*mondo_vec_reg |= INTERRUPT_VALID;
#ifndef lint
tmpreg = *softsp->sbus_ctrl_reg;
#endif
}
if ((softsp->intr_hndlr_cnt[slot] == 0) || (slot >= EXT_SBUS_SLOTS)) {
ASSERT(sbus_arg->handler_list == NULL);
}
if (sbus_arg->handler_list == NULL) {
DPRINTF(SBUS_INTERRUPT_DEBUG, ("Rem intr: Freeing sbus arg "
"0x%p\n", (void *)sbus_arg));
kmem_free(sbus_arg, sizeof (struct sbus_wrapper_arg));
softsp->intr_list[ino] = NULL;
}
done:
mutex_exit(&softsp->intr_poll_list_lock);
}
static int
sbus_xlate_intrs(dev_info_t *dip, dev_info_t *rdip, uint32_t *intr,
uint32_t *pil, int32_t ign)
{
uint32_t ino, slot, level = *intr;
int ret = DDI_SUCCESS;
if (level > MAX_SBUS_LEVEL) {
ino = level;
} else {
if ((slot = find_sbus_slot(dip, rdip)) == -1) {
cmn_err(CE_WARN, "Can't determine sbus slot "
"of %s device\n", ddi_driver_name(rdip));
ret = DDI_FAILURE;
goto done;
}
if (slot >= MAX_SBUS_SLOT_ADDR) {
cmn_err(CE_WARN, "Invalid sbus slot 0x%x"
"in %s device\n", slot, ddi_driver_name(rdip));
ret = DDI_FAILURE;
goto done;
}
ino = slot << 3;
ino |= level;
}
if (ino >= MAX_INO_TABLE_SIZE) {
cmn_err(CE_WARN, "Ino vector 0x%x out of range", ino);
ret = DDI_FAILURE;
goto done;
}
if (!ino_table[ino]) {
cmn_err(CE_WARN, "Ino vector 0x%x is invalid", ino);
ret = DDI_FAILURE;
goto done;
}
if (*pil == 0) {
#define SOC_PRIORITY 5
if ((ino_table[ino]->clear_reg == PP_CLEAR) &&
((strcmp(ddi_get_name(rdip), "soc") == 0) ||
(strcmp(ddi_get_name(rdip), "SUNW,soc") == 0))) {
*pil = SOC_PRIORITY;
} else {
*pil = interrupt_priorities[ino];
}
}
*intr = (uint32_t)(ino | ign);
DPRINTF(SBUS_INTERRUPT_DEBUG, ("Xlate intr: Interrupt info for "
"device %s Mondo: 0x%x, ino: 0x%x, Pil: 0x%x, sbus level: 0x%x\n",
ddi_driver_name(rdip), *intr, ino, *pil, level));
done:
return (ret);
}
int
sbus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
ddi_intr_handle_impl_t *hdlp, void *result)
{
struct sbus_soft_state *softsp = (struct sbus_soft_state *)
ddi_get_soft_state(sbusp, ddi_get_instance(dip));
int ret = DDI_SUCCESS;
switch (intr_op) {
case DDI_INTROP_GETCAP:
*(int *)result = DDI_INTR_FLAG_LEVEL;
break;
case DDI_INTROP_ALLOC:
*(int *)result = hdlp->ih_scratch1;
break;
case DDI_INTROP_FREE:
break;
case DDI_INTROP_GETPRI:
if (hdlp->ih_pri == 0) {
(void) sbus_xlate_intrs(dip, rdip,
(uint32_t *)&hdlp->ih_vector, &hdlp->ih_pri,
softsp->intr_mapping_ign);
}
*(int *)result = hdlp->ih_pri;
break;
case DDI_INTROP_SETPRI:
break;
case DDI_INTROP_ADDISR:
ret = sbus_add_intr_impl(dip, rdip, hdlp);
break;
case DDI_INTROP_REMISR:
sbus_remove_intr_impl(dip, rdip, hdlp);
break;
case DDI_INTROP_ENABLE:
ret = sbus_update_intr_state(dip, rdip, hdlp,
SBUS_INTR_STATE_ENABLE);
break;
case DDI_INTROP_DISABLE:
ret = sbus_update_intr_state(dip, rdip, hdlp,
SBUS_INTR_STATE_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);
}
static void
sbus_cpr_handle_intr_map_reg(uint64_t *cpr_softsp, volatile uint64_t *baddr,
int save)
{
int i;
volatile uint64_t *mondo_vec_reg;
for (i = 0; i < MAX_INO_TABLE_SIZE; i++) {
if (ino_table[i] != NULL) {
mondo_vec_reg = baddr + ino_table[i]->mapping_reg;
if (save) {
if (*mondo_vec_reg & INTERRUPT_VALID) {
cpr_softsp[i] = *mondo_vec_reg;
}
} else {
if (cpr_softsp[i]) {
*mondo_vec_reg = cpr_softsp[i];
}
}
}
}
}
#define SZ_INO_TABLE (sizeof (ino_table) / sizeof (ino_table[0]))
static void
sbus_intrdist(void *arg)
{
struct sbus_soft_state *softsp;
dev_info_t *dip = (dev_info_t *)arg;
volatile uint64_t *mondo_vec_reg;
uint64_t *last_mondo_vec_reg;
uint64_t mondo_vec;
volatile uint64_t *intr_state_reg;
uint_t start_bit;
volatile uint64_t tmpreg;
uint_t mondo;
uint_t cpu_id;
softsp = ddi_get_soft_state(sbusp, ddi_get_instance(dip));
last_mondo_vec_reg = NULL;
for (mondo = 0; mondo < SZ_INO_TABLE; mondo++) {
if (ino_table[mondo] == NULL)
continue;
mondo_vec_reg = (softsp->intr_mapping_reg +
ino_table[mondo]->mapping_reg);
if (mondo_vec_reg == last_mondo_vec_reg)
continue;
if ((*mondo_vec_reg & INTERRUPT_VALID) == 0)
continue;
last_mondo_vec_reg = (uint64_t *)mondo_vec_reg;
cpu_id = intr_dist_cpuid();
if (((*mondo_vec_reg & IMR_TID) >> IMR_TID_SHIFT) == cpu_id) {
return;
}
*mondo_vec_reg &= ~INTERRUPT_VALID;
tmpreg = *softsp->sbus_ctrl_reg;
#ifdef lint
tmpreg = tmpreg;
#endif
if (mondo > MAX_MONDO_EXTERNAL) {
start_bit = ino_table[mondo]->diagreg_shift;
intr_state_reg = softsp->obio_intr_state;
while ((((*intr_state_reg >> start_bit) & 0x3) ==
INT_PENDING) && !panicstr)
;
} else {
int int_pending = 0;
start_bit = 16 * (mondo >> 3) + 2;
intr_state_reg = softsp->sbus_intr_state;
do {
int level;
int shift;
uint64_t state_reg = *intr_state_reg;
int_pending = 0;
for (shift = start_bit, level = 1; level < 8;
level++, shift += 2) {
if (((state_reg >> shift) &
0x3) == INT_PENDING) {
int_pending = 1;
break;
}
}
} while (int_pending && !panicstr);
}
mondo_vec = (cpu_id << INTERRUPT_CPU_FIELD) | INTERRUPT_VALID;
*mondo_vec_reg = mondo_vec;
tmpreg = *mondo_vec_reg;
#ifdef lint
tmpreg = tmpreg;
#endif
}
}
static uint_t
sbus_intr_reset(void *arg)
{
dev_info_t *dip = (dev_info_t *)arg;
struct sbus_soft_state *softsp;
uint_t mondo;
volatile uint64_t *mondo_clear_reg;
softsp = ddi_get_soft_state(sbusp, ddi_get_instance(dip));
for (mondo = 0; mondo < SZ_INO_TABLE; mondo++) {
if (ino_table[mondo] == NULL ||
ino_table[mondo]->clear_reg == 0) {
continue;
}
mondo_clear_reg = (softsp->clr_intr_reg +
ino_table[mondo]->clear_reg);
*mondo_clear_reg = SBUS_INTR_IDLE;
}
return (BF_NONE);
}
static void
sbus_add_picN_kstats(dev_info_t *dip)
{
sbus_event_mask_t sbus_events_arr[SBUS_NUM_EVENTS] = {
{"dvma_stream_rd", 0x0}, {"dvma_stream_wr", 0x1},
{"dvma_const_rd", 0x2}, {"dvma_const_wr", 0x3},
{"dvma_tlb_misses", 0x4}, {"dvma_stream_buf_mis", 0x5},
{"dvma_cycles", 0x6}, {"dvma_bytes_xfr", 0x7},
{"interrupts", 0x8}, {"upa_inter_nack", 0x9},
{"pio_reads", 0xA}, {"pio_writes", 0xB},
{"sbus_reruns", 0xC}, {"pio_cycles", 0xD}
};
sbus_event_mask_t sbus_clear_pic[SBUS_NUM_PICS] = {
{"clear_pic", (uint64_t)~(0xf)},
{"clear_pic", (uint64_t)~(0xf << 8)}
};
struct kstat_named *sbus_pic_named_data;
int event, pic;
char pic_name[30];
int instance = ddi_get_instance(dip);
int pic_shift = 0;
for (pic = 0; pic < SBUS_NUM_PICS; pic++) {
(void) sprintf(pic_name, "pic%d", pic);
if ((sbus_picN_ksp[pic] = kstat_create("sbus",
instance, pic_name, "bus", KSTAT_TYPE_NAMED,
SBUS_NUM_EVENTS + 1, 0)) == NULL) {
cmn_err(CE_WARN, "sbus %s: kstat_create failed",
pic_name);
if (pic == 1) {
kstat_delete(sbus_picN_ksp[0]);
sbus_picN_ksp[0] = NULL;
}
return;
}
sbus_pic_named_data =
(struct kstat_named *)(sbus_picN_ksp[pic]->ks_data);
if (pic == 1)
pic_shift = 8;
for (event = 0; event < SBUS_NUM_EVENTS; event ++) {
sbus_pic_named_data[event].value.ui64 =
sbus_events_arr[event].pcr_mask << pic_shift;
kstat_named_init(&sbus_pic_named_data[event],
sbus_events_arr[event].event_name,
KSTAT_DATA_UINT64);
}
sbus_pic_named_data[SBUS_NUM_EVENTS].value.ui64 =
sbus_clear_pic[pic].pcr_mask;
kstat_named_init(&sbus_pic_named_data[SBUS_NUM_EVENTS],
sbus_clear_pic[pic].event_name,
KSTAT_DATA_UINT64);
kstat_install(sbus_picN_ksp[pic]);
}
}
static void
sbus_add_kstats(struct sbus_soft_state *softsp)
{
struct kstat *sbus_counters_ksp;
struct kstat_named *sbus_counters_named_data;
mutex_enter(&sbus_attachcnt_mutex);
if (sbus_attachcnt == 0)
sbus_add_picN_kstats(softsp->dip);
sbus_attachcnt ++;
mutex_exit(&sbus_attachcnt_mutex);
if ((sbus_counters_ksp = kstat_create("sbus",
ddi_get_instance(softsp->dip), "counters",
"bus", KSTAT_TYPE_NAMED, SBUS_NUM_PICS + 1,
KSTAT_FLAG_WRITABLE)) == NULL) {
cmn_err(CE_WARN, "sbus%d counters: kstat_create"
" failed", ddi_get_instance(softsp->dip));
return;
}
sbus_counters_named_data =
(struct kstat_named *)(sbus_counters_ksp->ks_data);
kstat_named_init(&sbus_counters_named_data[0],
"pcr", KSTAT_DATA_UINT64);
kstat_named_init(&sbus_counters_named_data[1],
"pic0", KSTAT_DATA_UINT64);
kstat_named_init(&sbus_counters_named_data[2],
"pic1", KSTAT_DATA_UINT64);
sbus_counters_ksp->ks_update = sbus_counters_kstat_update;
sbus_counters_ksp->ks_private = (void *)softsp;
kstat_install(sbus_counters_ksp);
softsp->sbus_counters_ksp = sbus_counters_ksp;
}
static int
sbus_counters_kstat_update(kstat_t *ksp, int rw)
{
struct kstat_named *sbus_counters_data;
struct sbus_soft_state *softsp;
uint64_t pic_register;
sbus_counters_data = (struct kstat_named *)ksp->ks_data;
softsp = (struct sbus_soft_state *)ksp->ks_private;
if (rw == KSTAT_WRITE) {
*softsp->sbus_pcr =
(uint32_t)sbus_counters_data[0].value.ui64;
} else {
sbus_counters_data[0].value.ui64 = *softsp->sbus_pcr >> 4;
pic_register = *softsp->sbus_pic;
sbus_counters_data[1].value.ui64 = pic_register >> 32;
sbus_counters_data[2].value.ui64 =
pic_register & SBUS_PIC0_MASK;
}
return (0);
}
static int
sbus_update_intr_state(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp, uint_t new_intr_state)
{
struct sbus_soft_state *softsp = (struct sbus_soft_state *)
ddi_get_soft_state(sbusp, ddi_get_instance(dip));
int ino;
struct sbus_wrapper_arg *sbus_arg;
struct sbus_intr_handler *intr_handler;
if (sbus_xlate_intrs(dip, rdip, (uint32_t *)&hdlp->ih_vector,
&hdlp->ih_pri, softsp->intr_mapping_ign) == DDI_FAILURE) {
cmn_err(CE_WARN, "sbus_update_intr_state() can't xlate SBUS "
"devices %s interrupt.", ddi_driver_name(rdip));
return (DDI_FAILURE);
}
ino = ((int32_t)hdlp->ih_vector) & SBUS_MAX_INO;
sbus_arg = softsp->intr_list[ino];
ASSERT(sbus_arg != NULL);
ASSERT(sbus_arg->handler_list != NULL);
intr_handler = sbus_arg->handler_list;
while (intr_handler) {
if ((intr_handler->inum == hdlp->ih_inum) &&
(intr_handler->dip == rdip)) {
intr_handler->intr_state = new_intr_state;
return (DDI_SUCCESS);
}
intr_handler = intr_handler->next;
}
return (DDI_FAILURE);
}