#include <sys/types.h>
#include <sys/ddi.h>
#include <sys/kmem.h>
#include <sys/sysmacros.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/pcie.h>
#include <sys/pci_cap.h>
#include <sys/pcie_impl.h>
#include <io/pciex/pcieb.h>
#include "pcieb_plx.h"
#ifdef PX_PLX
static void plx_ro_disable(pcieb_devstate_t *pcieb);
#ifdef PRINT_PLX_SEEPROM_CRC
static void pcieb_print_plx_seeprom_crc_data(pcieb_devstate_t *pcieb_p);
#endif
#endif
int
pcieb_plat_peekpoke(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
void *arg, void *result)
{
return (ddi_ctlops(dip, rdip, ctlop, arg, result));
}
void
pcieb_set_prot_scan(dev_info_t *dip, ddi_acc_impl_t *hdlp)
{
}
void
pcieb_plat_attach_workaround(dev_info_t *dip)
{
}
int
pcieb_plat_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
ddi_intr_handle_impl_t *hdlp, void *result)
{
dev_info_t *cdip = rdip;
pci_regspec_t *pci_rp;
int reglen, len;
uint32_t d, intr;
if ((intr_op == DDI_INTROP_SUPPORTED_TYPES) ||
(hdlp->ih_type != DDI_INTR_TYPE_FIXED))
goto done;
if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"interrupt-map", &len) == DDI_PROP_SUCCESS)
goto done;
cdip = pcie_get_my_childs_dip(dip, rdip);
if (ddi_getlongprop(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
"reg", (caddr_t)&pci_rp, ®len) != DDI_SUCCESS)
return (DDI_FAILURE);
intr = hdlp->ih_vector;
d = (pcie_ari_is_enabled(dip) == PCIE_ARI_FORW_ENABLED) ? 0 :
PCI_REG_DEV_G(pci_rp[0].pci_phys_hi);
if ((intr >= PCI_INTA) && (intr <= PCI_INTD))
hdlp->ih_vector = ((intr - 1 + (d % 4)) % 4 + 1);
else
cmn_err(CE_WARN, "%s%d: %s: PCI intr=%x out of range",
ddi_driver_name(rdip), ddi_get_instance(rdip),
ddi_driver_name(dip), intr);
kmem_free(pci_rp, reglen);
done:
return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result));
}
int
pcieb_plat_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle)
{
uint16_t cap_ptr;
if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_HOTPLUG, &cap_ptr)) !=
DDI_FAILURE) {
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
boolean_t
pcieb_plat_pwr_disable(dev_info_t *dip)
{
uint16_t vendor_id = (PCIE_DIP2UPBUS(dip)->bus_dev_ven_id) & 0xFFFF;
return (IS_PLX_VENDORID(vendor_id) ? B_TRUE : B_FALSE);
}
boolean_t
pcieb_plat_msi_supported(dev_info_t *dip)
{
return (B_TRUE);
}
void
pcieb_plat_intr_attach(pcieb_devstate_t *pcieb)
{
}
int
pcieb_plat_ctlops(dev_info_t *rdip, ddi_ctl_enum_t ctlop, void *arg)
{
return (DDI_SUCCESS);
}
void
pcieb_plat_initchild(dev_info_t *child)
{
intptr_t ppd = (intptr_t)NULL;
if (ddi_prop_exists(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
"dvma-share"))
ppd = 1;
ddi_set_parent_data(child, (void *)ppd);
}
void
pcieb_plat_uninitchild(dev_info_t *child)
{
if ((intptr_t)ddi_get_parent_data(child) == 1)
ddi_set_parent_data(child, NULL);
}
#ifdef PX_PLX
void
pcieb_attach_plx_workarounds(pcieb_devstate_t *pcieb)
{
dev_info_t *dip = pcieb->pcieb_dip;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl;
uint_t bus_num, primary, secondary;
uint8_t dev_type = bus_p->bus_dev_type;
uint16_t vendor_id = bus_p->bus_dev_ven_id & 0xFFFF;
uint16_t device_id = bus_p->bus_dev_ven_id >> 16;
int ce_mask = 0;
if (!IS_PLX_VENDORID(vendor_id))
return;
if ((device_id == PXB_DEVICE_PLX_8532) &&
(bus_p->bus_rev_id <= PXB_DEVICE_PLX_AA_REV))
bus_p->bus_hp_sup_modes = PCIE_NONE_HP_MODE;
ce_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"pcie_ce_mask", 0);
(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip,
"pcie_ce_mask", (PCIE_AER_CE_RECEIVER_ERR|ce_mask));
primary = pci_config_get8(config_handle, PCI_BCNF_PRIBUS);
secondary = pci_config_get8(config_handle, PCI_BCNF_SECBUS);
bus_num = (secondary << 8) | primary;
pci_config_put16(config_handle, PCI_BCNF_PRIBUS, bus_num);
if ((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) ||
(dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT) ||
(dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) ||
(dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) {
pci_config_put16(config_handle, PCI_CONF_COMM,
pci_config_get16(config_handle, PCI_CONF_COMM) |
PCI_COMM_INTX_DISABLE);
}
plx_ro_disable(pcieb);
#ifdef PRINT_PLX_SEEPROM_CRC
(void) pcieb_print_plx_seeprom_crc_data(pcieb);
#endif
}
int
pcieb_init_plx_workarounds(pcieb_devstate_t *pcieb, dev_info_t *child)
{
int i;
int result = DDI_FAILURE;
uint16_t reg = 0;
ddi_acc_handle_t config_handle;
uint16_t vendor_id =
(PCIE_DIP2UPBUS(pcieb->pcieb_dip))->bus_dev_ven_id & 0xFFFF;
if (!IS_PLX_VENDORID(vendor_id))
return (DDI_SUCCESS);
if (!pxb_tlp_count) {
result = DDI_SUCCESS;
goto done;
}
if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) {
result = DDI_FAILURE;
goto done;
}
for (i = 0; i < pxb_tlp_count; i += 1)
reg |= pci_config_get16(config_handle, PCI_CONF_VENID);
if (PCIE_IS_PCIE_BDG(PCIE_DIP2BUS(pcieb->pcieb_dip)))
pcieb_set_pci_perf_parameters(child, config_handle);
pci_config_teardown(&config_handle);
result = DDI_SUCCESS;
done:
return (result);
}
static void
plx_ro_disable(pcieb_devstate_t *pcieb)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip);
dev_info_t *dip = pcieb->pcieb_dip;
uint16_t device_id = bus_p->bus_dev_ven_id >> 16;
pci_regspec_t *reg_spec, *addr_spec;
int rlen, alen;
int orig_rsize, new_rsize;
uint_t rnum, anum;
ddi_device_acc_attr_t attr;
ddi_acc_handle_t hdl;
caddr_t regsp;
uint32_t val, port_enable;
char *offset;
char *port_offset;
if (!((device_id == PXB_DEVICE_PLX_8533) ||
(device_id == PXB_DEVICE_PLX_8548)))
return;
val = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP);
val = (val >> PCIE_LINKCAP_PORT_NUMBER_SHIFT) &
PCIE_LINKCAP_PORT_NUMBER_MASK;
PCIEB_DEBUG(DBG_ATTACH, dip, "PLX RO Disable : bdf=0x%x port=%d\n",
bus_p->bus_bdf, val);
if (val != 0)
return;
if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
&orig_rsize) != DDI_SUCCESS)
return;
new_rsize = orig_rsize + sizeof (pci_regspec_t);
reg_spec = kmem_alloc(new_rsize, KM_SLEEP);
if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
(caddr_t)reg_spec, &orig_rsize) != DDI_SUCCESS)
goto fail;
rlen = orig_rsize / sizeof (pci_regspec_t);
for (rnum = 0; rnum < rlen; rnum++) {
if ((reg_spec[rnum].pci_phys_hi & PCI_ADDR_MASK) ==
PCI_ADDR_MEM32)
goto fix;
}
addr_spec = bus_p->bus_assigned_addr;
alen = bus_p->bus_assigned_entries;
for (anum = 0; anum < alen; anum++) {
if ((addr_spec[anum].pci_phys_hi & PCI_ADDR_MASK) ==
PCI_ADDR_MEM32)
goto update;
}
goto fail;
update:
reg_spec[rnum].pci_phys_hi = (addr_spec[anum].pci_phys_hi &
~PCI_REG_REL_M);
reg_spec[rnum].pci_phys_mid = 0;
reg_spec[rnum].pci_phys_low = 0;
reg_spec[rnum].pci_size_hi = addr_spec[anum].pci_size_hi;
reg_spec[rnum].pci_size_low = addr_spec[anum].pci_size_low;
if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg",
(int *)reg_spec, (new_rsize / sizeof (int))) != DDI_SUCCESS)
goto fail;
fix:
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
if (ddi_regs_map_setup(dip, rnum, ®sp, 0, 0, &attr,
&hdl) != DDI_SUCCESS)
goto fail;
offset = (char *)regsp + PLX_INGRESS_PORT_ENABLE;
port_enable = ddi_get32(hdl, (uint32_t *)offset);
if ((port_enable == 0xFFFFFFFF) || (port_enable == 0))
goto done;
offset = (char *)regsp + PLX_INGRESS_CONTROL_SHADOW;
port_offset = 0x0 + offset;
val = ddi_get32(hdl, (uint32_t *)port_offset);
if (val & PLX_RO_MODE_BIT)
val ^= PLX_RO_MODE_BIT;
ddi_put32(hdl, (uint32_t *)port_offset, val);
if (!(port_enable & (1 << 8)))
goto port12;
port_offset = (8 * 0x1000) + offset;
val = ddi_get32(hdl, (uint32_t *)port_offset);
if (val & PLX_RO_MODE_BIT)
val ^= PLX_RO_MODE_BIT;
ddi_put32(hdl, (uint32_t *)port_offset, val);
port12:
if (!(port_enable & (1 << 12)))
goto done;
port_offset = (12 * 0x1000) + offset;
val = ddi_get32(hdl, (uint32_t *)port_offset);
if (val & PLX_RO_MODE_BIT)
val ^= PLX_RO_MODE_BIT;
ddi_put32(hdl, (uint32_t *)port_offset, val);
goto done;
done:
ddi_regs_map_free(&hdl);
fail:
kmem_free(reg_spec, new_rsize);
}
#ifdef PRINT_PLX_SEEPROM_CRC
static void
pcieb_print_plx_seeprom_crc_data(pcieb_devstate_t *pcieb_p)
{
ddi_acc_handle_t h;
dev_info_t *dip = pcieb_p->pcieb_dip;
uint16_t vendorid = (PCIE_DIP2BUS(dip)->bus_dev_ven_id) & 0xFFFF;
int nregs;
caddr_t mp;
off_t bar_size;
ddi_device_acc_attr_t mattr = {
DDI_DEVICE_ATTR_V0,
DDI_STRUCTURE_LE_ACC,
DDI_STRICTORDER_ACC
};
uint32_t addr_reg_off = 0x260, data_reg_off = 0x264, data = 0x6BE4;
if (vendorid != PXB_VENDOR_PLX)
return;
if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS)
return;
if (nregs < 2)
return;
if (ddi_dev_regsize(dip, 1, &bar_size) != DDI_SUCCESS)
return;
if (ddi_regs_map_setup(dip, 1, (caddr_t *)&mp, 0, bar_size,
&mattr, &h) != DDI_SUCCESS)
return;
ddi_put32(h, (uint32_t *)((uchar_t *)mp + addr_reg_off), data);
delay(drv_usectohz(1000000));
printf("%s#%d: EEPROM StatusReg = %x, CRC = %x\n",
ddi_driver_name(dip), ddi_get_instance(dip),
ddi_get32(h, (uint32_t *)((uchar_t *)mp + addr_reg_off)),
ddi_get32(h, (uint32_t *)((uchar_t *)mp + data_reg_off)));
#ifdef PLX_HOT_RESET_DISABLE
data = ddi_get32(h, (uint32_t *)((uchar_t *)mp + 0x1DC));
ddi_put32(h, (uint32_t *)((uchar_t *)mp + 0x1DC), data | 0x80000);
delay(drv_usectohz(1000000));
printf("%s#%d: EEPROM 0x1DC prewrite=%x postwrite=%x\n",
ddi_driver_name(dip), ddi_get_instance(dip), data,
ddi_get32(h, (uint32_t *)((uchar_t *)mp + 0x1DC)));
#endif
ddi_regs_map_free(&h);
}
#endif
#endif