#include <sys/scsi/adapters/smrt/smrt.h>
static int
smrt_locate_bar(pci_regspec_t *regs, unsigned nregs,
unsigned *i2o_bar)
{
for (unsigned i = 0; i < nregs; i++) {
unsigned type = regs[i].pci_phys_hi & PCI_ADDR_MASK;
if (type == PCI_ADDR_MEM32 || type == PCI_ADDR_MEM64) {
*i2o_bar = i;
return (DDI_SUCCESS);
}
}
return (DDI_FAILURE);
}
static int
smrt_locate_cfgtbl(smrt_t *smrt, pci_regspec_t *regs, unsigned nregs,
unsigned *ct_bar, uint32_t *baseaddr)
{
uint32_t cfg_offset, mem_offset;
unsigned want_type;
uint32_t want_bar;
cfg_offset = smrt_get32(smrt, CISS_I2O_CFGTBL_CFG_OFFSET);
mem_offset = smrt_get32(smrt, CISS_I2O_CFGTBL_MEM_OFFSET);
VERIFY3U(cfg_offset, !=, 0xffffffff);
VERIFY3U(mem_offset, !=, 0xffffffff);
want_bar = (cfg_offset & 0xffff);
want_type = (cfg_offset & (1UL << 16)) ? PCI_ADDR_MEM64 :
PCI_ADDR_MEM32;
DTRACE_PROBE4(locate_cfgtbl, uint32_t, want_bar, unsigned,
want_type, uint32_t, cfg_offset, uint32_t, mem_offset);
for (unsigned i = 0; i < nregs; i++) {
unsigned type = regs[i].pci_phys_hi & PCI_ADDR_MASK;
unsigned bar = PCI_REG_REG_G(regs[i].pci_phys_hi);
if (type != PCI_ADDR_MEM32 && type != PCI_ADDR_MEM64) {
continue;
}
if (bar == want_bar) {
*ct_bar = i;
*baseaddr = mem_offset;
return (DDI_SUCCESS);
}
}
return (DDI_FAILURE);
}
static int
smrt_identify_device(smrt_t *smrt)
{
ddi_acc_handle_t pci_hdl;
if (pci_config_setup(smrt->smrt_dip, &pci_hdl) != DDI_SUCCESS)
return (DDI_FAILURE);
smrt->smrt_pci_vendor = pci_config_get16(pci_hdl, PCI_CONF_VENID);
smrt->smrt_pci_device = pci_config_get16(pci_hdl, PCI_CONF_DEVID);
pci_config_teardown(&pci_hdl);
return (DDI_SUCCESS);
}
static int
smrt_map_device(smrt_t *smrt)
{
pci_regspec_t *regs;
uint_t regslen, nregs;
dev_info_t *dip = smrt->smrt_dip;
int r = DDI_FAILURE;
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"reg", (int **)®s, ®slen) != DDI_PROP_SUCCESS) {
dev_err(dip, CE_WARN, "could not load \"reg\" DDI prop");
return (DDI_FAILURE);
}
nregs = regslen * sizeof (int) / sizeof (pci_regspec_t);
if (smrt_locate_bar(regs, nregs, &smrt->smrt_i2o_bar) !=
DDI_SUCCESS) {
dev_err(dip, CE_WARN, "did not find any memory BARs");
goto out;
}
if (ddi_regs_map_setup(dip, smrt->smrt_i2o_bar, &smrt->smrt_i2o_space,
CISS_I2O_MAP_BASE, CISS_I2O_MAP_LIMIT - CISS_I2O_MAP_BASE,
&smrt_dev_attributes, &smrt->smrt_i2o_handle) != DDI_SUCCESS) {
dev_err(dip, CE_WARN, "failed to map I2O registers");
goto out;
}
smrt->smrt_init_level |= SMRT_INITLEVEL_I2O_MAPPED;
if (smrt_locate_cfgtbl(smrt, regs, nregs, &smrt->smrt_ct_bar,
&smrt->smrt_ct_baseaddr) != DDI_SUCCESS) {
dev_err(dip, CE_WARN, "could not find config table");
goto out;
}
if (ddi_regs_map_setup(dip, smrt->smrt_ct_bar,
(caddr_t *)&smrt->smrt_ct, smrt->smrt_ct_baseaddr,
sizeof (CfgTable_t), &smrt_dev_attributes,
&smrt->smrt_ct_handle) != DDI_SUCCESS) {
dev_err(dip, CE_WARN, "could not map config table");
goto out;
}
smrt->smrt_init_level |= SMRT_INITLEVEL_CFGTBL_MAPPED;
r = DDI_SUCCESS;
out:
ddi_prop_free(regs);
return (r);
}
int
smrt_device_setup(smrt_t *smrt)
{
if (ddi_slaveonly(smrt->smrt_dip) == DDI_SUCCESS) {
dev_err(smrt->smrt_dip, CE_WARN, "device cannot become DMA "
"master");
return (DDI_FAILURE);
}
if (smrt_identify_device(smrt) != DDI_SUCCESS)
goto fail;
if (smrt_map_device(smrt) != DDI_SUCCESS) {
goto fail;
}
return (DDI_SUCCESS);
fail:
smrt_device_teardown(smrt);
return (DDI_FAILURE);
}
void
smrt_device_teardown(smrt_t *smrt)
{
if (smrt->smrt_init_level & SMRT_INITLEVEL_CFGTBL_MAPPED) {
ddi_regs_map_free(&smrt->smrt_ct_handle);
smrt->smrt_init_level &= ~SMRT_INITLEVEL_CFGTBL_MAPPED;
}
if (smrt->smrt_init_level & SMRT_INITLEVEL_I2O_MAPPED) {
ddi_regs_map_free(&smrt->smrt_i2o_handle);
smrt->smrt_init_level &= ~SMRT_INITLEVEL_I2O_MAPPED;
}
}
uint32_t
smrt_get32(smrt_t *smrt, offset_t off)
{
VERIFY3S(off, >=, CISS_I2O_MAP_BASE);
VERIFY3S(off, <, CISS_I2O_MAP_BASE + CISS_I2O_MAP_LIMIT);
uint32_t *addr = (uint32_t *)(smrt->smrt_i2o_space +
(off - CISS_I2O_MAP_BASE));
return (ddi_get32(smrt->smrt_i2o_handle, addr));
}
void
smrt_put32(smrt_t *smrt, offset_t off, uint32_t val)
{
VERIFY3S(off, >=, CISS_I2O_MAP_BASE);
VERIFY3S(off, <, CISS_I2O_MAP_BASE + CISS_I2O_MAP_LIMIT);
uint32_t *addr = (uint32_t *)(smrt->smrt_i2o_space +
(off - CISS_I2O_MAP_BASE));
ddi_put32(smrt->smrt_i2o_handle, addr, val);
}