/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright © 2003-2011 Emulex. All rights reserved. */ /* * Source file containing the implementation of the Hardware specific * functions */ #include #include #include static ddi_device_acc_attr_t reg_accattr = { DDI_DEVICE_ATTR_V1, DDI_STRUCTURE_LE_ACC, DDI_STRICTORDER_ACC, DDI_FLAGERR_ACC }; extern int oce_destroy_q(struct oce_dev *dev, struct oce_mbx *mbx, size_t req_size, enum qtype qtype); static int oce_map_regs(struct oce_dev *dev) { int ret = 0; off_t bar_size = 0; ASSERT(NULL != dev); ASSERT(NULL != dev->dip); /* get number of supported bars */ ret = ddi_dev_nregs(dev->dip, &dev->num_bars); if (ret != DDI_SUCCESS) { oce_log(dev, CE_WARN, MOD_CONFIG, "%d: could not retrieve num_bars", MOD_CONFIG); return (DDI_FAILURE); } /* verify each bar and map it accordingly */ /* PCI CFG */ ret = ddi_dev_regsize(dev->dip, OCE_DEV_CFG_BAR, &bar_size); if (ret != DDI_SUCCESS) { oce_log(dev, CE_WARN, MOD_CONFIG, "Could not get sizeof BAR %d", OCE_DEV_CFG_BAR); return (DDI_FAILURE); } ret = ddi_regs_map_setup(dev->dip, OCE_DEV_CFG_BAR, &dev->dev_cfg_addr, 0, bar_size, ®_accattr, &dev->dev_cfg_handle); if (ret != DDI_SUCCESS) { oce_log(dev, CE_WARN, MOD_CONFIG, "Could not map bar %d", OCE_DEV_CFG_BAR); return (DDI_FAILURE); } /* CSR */ ret = ddi_dev_regsize(dev->dip, OCE_PCI_CSR_BAR, &bar_size); if (ret != DDI_SUCCESS) { oce_log(dev, CE_WARN, MOD_CONFIG, "Could not get sizeof BAR %d", OCE_PCI_CSR_BAR); return (DDI_FAILURE); } ret = ddi_regs_map_setup(dev->dip, OCE_PCI_CSR_BAR, &dev->csr_addr, 0, bar_size, ®_accattr, &dev->csr_handle); if (ret != DDI_SUCCESS) { oce_log(dev, CE_WARN, MOD_CONFIG, "Could not map bar %d", OCE_PCI_CSR_BAR); ddi_regs_map_free(&dev->dev_cfg_handle); return (DDI_FAILURE); } /* Doorbells */ ret = ddi_dev_regsize(dev->dip, OCE_PCI_DB_BAR, &bar_size); if (ret != DDI_SUCCESS) { oce_log(dev, CE_WARN, MOD_CONFIG, "%d Could not get sizeof BAR %d", ret, OCE_PCI_DB_BAR); ddi_regs_map_free(&dev->csr_handle); ddi_regs_map_free(&dev->dev_cfg_handle); return (DDI_FAILURE); } ret = ddi_regs_map_setup(dev->dip, OCE_PCI_DB_BAR, &dev->db_addr, 0, 0, ®_accattr, &dev->db_handle); if (ret != DDI_SUCCESS) { oce_log(dev, CE_WARN, MOD_CONFIG, "Could not map bar %d", OCE_PCI_DB_BAR); ddi_regs_map_free(&dev->csr_handle); ddi_regs_map_free(&dev->dev_cfg_handle); return (DDI_FAILURE); } return (DDI_SUCCESS); } static void oce_unmap_regs(struct oce_dev *dev) { ASSERT(NULL != dev); ASSERT(NULL != dev->dip); ddi_regs_map_free(&dev->db_handle); ddi_regs_map_free(&dev->csr_handle); ddi_regs_map_free(&dev->dev_cfg_handle); } /* * function to map the device memory * * dev - handle to device private data structure * */ int oce_pci_init(struct oce_dev *dev) { int ret = 0; ret = oce_map_regs(dev); if (ret != DDI_SUCCESS) { return (DDI_FAILURE); } dev->fn = OCE_PCI_FUNC(dev); if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) { ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); } if (ret != DDI_FM_OK) { oce_pci_fini(dev); return (DDI_FAILURE); } return (DDI_SUCCESS); } /* oce_pci_init */ /* * function to free device memory mapping mapped using * oce_pci_init * * dev - handle to device private data */ void oce_pci_fini(struct oce_dev *dev) { oce_unmap_regs(dev); } /* oce_pci_fini */ /* * function to read the PCI Bus, Device, and function numbers for the * device instance. * * dev - handle to device private data */ int oce_get_bdf(struct oce_dev *dev) { pci_regspec_t *pci_rp; uint32_t length; int rc; /* Get "reg" property */ rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dev->dip, 0, "reg", (int **)&pci_rp, (uint_t *)&length); if ((rc != DDI_SUCCESS) || (length < (sizeof (pci_regspec_t) / sizeof (int)))) { oce_log(dev, CE_WARN, MOD_CONFIG, "Failed to read \"reg\" property, Status = 0x%x", rc); return (rc); } dev->pci_bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi); dev->pci_device = PCI_REG_DEV_G(pci_rp->pci_phys_hi); dev->pci_function = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); oce_log(dev, CE_NOTE, MOD_CONFIG, "\"reg\" property num=%d, Bus=%d, Device=%d, Function=%d", length, dev->pci_bus, dev->pci_device, dev->pci_function); /* Free the memory allocated by ddi_prop_lookup_int_array() */ ddi_prop_free(pci_rp); return (rc); } int oce_identify_hw(struct oce_dev *dev) { int ret = DDI_SUCCESS; dev->vendor_id = pci_config_get16(dev->pci_cfg_handle, PCI_CONF_VENID); dev->device_id = pci_config_get16(dev->pci_cfg_handle, PCI_CONF_DEVID); dev->subsys_id = pci_config_get16(dev->pci_cfg_handle, PCI_CONF_SUBSYSID); dev->subvendor_id = pci_config_get16(dev->pci_cfg_handle, PCI_CONF_SUBVENID); switch (dev->device_id) { case DEVID_TIGERSHARK: dev->chip_rev = OC_CNA_GEN2; break; case DEVID_TOMCAT: dev->chip_rev = OC_CNA_GEN3; break; default: dev->chip_rev = 0; ret = DDI_FAILURE; break; } return (ret); } /* * function to check if a reset is required * * dev - software handle to the device * */ boolean_t oce_is_reset_pci(struct oce_dev *dev) { mpu_ep_semaphore_t post_status; ASSERT(dev != NULL); ASSERT(dev->dip != NULL); post_status.dw0 = 0; post_status.dw0 = OCE_CSR_READ32(dev, MPU_EP_SEMAPHORE); if (post_status.bits.stage == POST_STAGE_ARMFW_READY) { return (B_FALSE); } return (B_TRUE); } /* oce_is_reset_pci */ /* * function to do a soft reset on the device * * dev - software handle to the device * */ int oce_pci_soft_reset(struct oce_dev *dev) { pcicfg_soft_reset_t soft_rst; /* struct mpu_ep_control ep_control; */ /* struct pcicfg_online1 online1; */ clock_t tmo; clock_t earlier = ddi_get_lbolt(); ASSERT(dev != NULL); /* issue soft reset */ soft_rst.dw0 = OCE_CFG_READ32(dev, PCICFG_SOFT_RESET); soft_rst.bits.soft_reset = 0x01; OCE_CFG_WRITE32(dev, PCICFG_SOFT_RESET, soft_rst.dw0); /* wait till soft reset bit deasserts */ tmo = drv_usectohz(60000000); /* 1.0min */ do { if ((ddi_get_lbolt() - earlier) > tmo) { tmo = 0; break; } soft_rst.dw0 = OCE_CFG_READ32(dev, PCICFG_SOFT_RESET); if (soft_rst.bits.soft_reset) drv_usecwait(100); } while (soft_rst.bits.soft_reset); if (soft_rst.bits.soft_reset) { oce_log(dev, CE_WARN, MOD_CONFIG, "0x%x soft_reset" "bit asserted[1]. Reset failed", soft_rst.dw0); return (DDI_FAILURE); } return (oce_POST(dev)); } /* oce_pci_soft_reset */ /* * function to trigger a POST on the device * * dev - software handle to the device * */ int oce_POST(struct oce_dev *dev) { mpu_ep_semaphore_t post_status; clock_t tmo; clock_t earlier = ddi_get_lbolt(); /* read semaphore CSR */ post_status.dw0 = OCE_CSR_READ32(dev, MPU_EP_SEMAPHORE); if (oce_fm_check_acc_handle(dev, dev->csr_handle) != DDI_FM_OK) { ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); return (DDI_FAILURE); } /* if host is ready then wait for fw ready else send POST */ if (post_status.bits.stage <= POST_STAGE_AWAITING_HOST_RDY) { post_status.bits.stage = POST_STAGE_CHIP_RESET; OCE_CSR_WRITE32(dev, MPU_EP_SEMAPHORE, post_status.dw0); if (oce_fm_check_acc_handle(dev, dev->csr_handle) != DDI_FM_OK) { ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); return (DDI_FAILURE); } } /* wait for FW ready */ tmo = drv_usectohz(60000000); /* 1.0min */ for (;;) { if ((ddi_get_lbolt() - earlier) > tmo) { tmo = 0; break; } post_status.dw0 = OCE_CSR_READ32(dev, MPU_EP_SEMAPHORE); if (oce_fm_check_acc_handle(dev, dev->csr_handle) != DDI_FM_OK) { ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); return (DDI_FAILURE); } if (post_status.bits.error) { oce_log(dev, CE_WARN, MOD_CONFIG, "0x%x POST ERROR!!", post_status.dw0); return (DDI_FAILURE); } if (post_status.bits.stage == POST_STAGE_ARMFW_READY) return (DDI_SUCCESS); drv_usecwait(100); } return (DDI_FAILURE); } /* oce_POST */ /* * function to modify register access attributes corresponding to the * FM capabilities configured by the user * * fm_caps - fm capability configured by the user and accepted by the driver */ void oce_set_reg_fma_flags(int fm_caps) { if (fm_caps == DDI_FM_NOT_CAPABLE) { return; } if (DDI_FM_ACC_ERR_CAP(fm_caps)) { reg_accattr.devacc_attr_access = DDI_FLAGERR_ACC; } else { reg_accattr.devacc_attr_access = DDI_DEFAULT_ACC; } } /* oce_set_fma_flags */ int oce_create_nw_interface(struct oce_dev *dev) { int ret; uint32_t capab_flags = OCE_CAPAB_FLAGS; uint32_t capab_en_flags = OCE_CAPAB_ENABLE; if (dev->rss_enable) { capab_flags |= MBX_RX_IFACE_FLAGS_RSS; capab_en_flags |= MBX_RX_IFACE_FLAGS_RSS; } /* create an interface for the device with out mac */ ret = oce_if_create(dev, capab_flags, capab_en_flags, 0, &dev->mac_addr[0], (uint32_t *)&dev->if_id); if (ret != 0) { oce_log(dev, CE_WARN, MOD_CONFIG, "Interface creation failed: 0x%x", ret); return (ret); } atomic_inc_32(&dev->nifs); dev->if_cap_flags = capab_en_flags; /* Enable VLAN Promisc on HW */ ret = oce_config_vlan(dev, (uint8_t)dev->if_id, NULL, 0, B_TRUE, B_TRUE); if (ret != 0) { oce_log(dev, CE_WARN, MOD_CONFIG, "Config vlan failed: %d", ret); oce_delete_nw_interface(dev); return (ret); } /* set default flow control */ ret = oce_set_flow_control(dev, dev->flow_control); if (ret != 0) { oce_log(dev, CE_NOTE, MOD_CONFIG, "Set flow control failed: %d", ret); } ret = oce_set_promiscuous(dev, dev->promisc); if (ret != 0) { oce_log(dev, CE_NOTE, MOD_CONFIG, "Set Promisc failed: %d", ret); } return (0); } void oce_delete_nw_interface(struct oce_dev *dev) { /* currently only single interface is implmeneted */ if (dev->nifs > 0) { (void) oce_if_del(dev, dev->if_id); atomic_dec_32(&dev->nifs); } } static void oce_create_itbl(struct oce_dev *dev, char *itbl) { int i; struct oce_rq **rss_queuep = &dev->rq[1]; int nrss = dev->nrqs - 1; /* fill the indirection table rq 0 is default queue */ for (i = 0; i < OCE_ITBL_SIZE; i++) { itbl[i] = rss_queuep[i % nrss]->rss_cpuid; } } int oce_setup_adapter(struct oce_dev *dev) { int ret; char itbl[OCE_ITBL_SIZE]; char hkey[OCE_HKEY_SIZE]; /* disable the interrupts here and enable in start */ oce_chip_di(dev); ret = oce_create_nw_interface(dev); if (ret != DDI_SUCCESS) { return (DDI_FAILURE); } ret = oce_create_queues(dev); if (ret != DDI_SUCCESS) { oce_delete_nw_interface(dev); return (DDI_FAILURE); } if (dev->rss_enable) { (void) oce_create_itbl(dev, itbl); (void) oce_gen_hkey(hkey, OCE_HKEY_SIZE); ret = oce_config_rss(dev, dev->if_id, hkey, itbl, OCE_ITBL_SIZE, OCE_DEFAULT_RSS_TYPE, B_TRUE); if (ret != DDI_SUCCESS) { oce_log(dev, CE_NOTE, MOD_CONFIG, "%s", "Failed to Configure RSS"); oce_delete_queues(dev); oce_delete_nw_interface(dev); return (ret); } } ret = oce_setup_handlers(dev); if (ret != DDI_SUCCESS) { oce_log(dev, CE_NOTE, MOD_CONFIG, "%s", "Failed to Setup handlers"); oce_delete_queues(dev); oce_delete_nw_interface(dev); return (ret); } return (DDI_SUCCESS); } void oce_unsetup_adapter(struct oce_dev *dev) { oce_remove_handler(dev); if (dev->rss_enable) { char itbl[OCE_ITBL_SIZE] = {0}; char hkey[OCE_HKEY_SIZE] = {0}; int ret = 0; ret = oce_config_rss(dev, dev->if_id, hkey, itbl, OCE_ITBL_SIZE, RSS_ENABLE_NONE, B_TRUE); if (ret != DDI_SUCCESS) { oce_log(dev, CE_NOTE, MOD_CONFIG, "%s", "Failed to Disable RSS"); } } oce_delete_queues(dev); oce_delete_nw_interface(dev); } int oce_hw_init(struct oce_dev *dev) { int ret; struct mac_address_format mac_addr; ret = oce_POST(dev); if (ret != DDI_SUCCESS) { oce_log(dev, CE_WARN, MOD_CONFIG, "%s", "!!!HW POST1 FAILED"); /* ADD FM FAULT */ return (DDI_FAILURE); } /* create bootstrap mailbox */ dev->bmbx = oce_alloc_dma_buffer(dev, sizeof (struct oce_bmbx), NULL, DDI_DMA_CONSISTENT); if (dev->bmbx == NULL) { oce_log(dev, CE_WARN, MOD_CONFIG, "Failed to allocate bmbx: size = %u", (uint32_t)sizeof (struct oce_bmbx)); return (DDI_FAILURE); } ret = oce_reset_fun(dev); if (ret != 0) { oce_log(dev, CE_WARN, MOD_CONFIG, "%s", "!!!FUNCTION RESET FAILED"); goto init_fail; } /* reset the Endianess of BMBX */ ret = oce_mbox_init(dev); if (ret != 0) { oce_log(dev, CE_WARN, MOD_CONFIG, "Mailbox initialization2 Failed with %d", ret); goto init_fail; } /* read the firmware version */ ret = oce_get_fw_version(dev); if (ret != 0) { oce_log(dev, CE_WARN, MOD_CONFIG, "Firmaware version read failed with %d", ret); goto init_fail; } /* read the fw config */ ret = oce_get_fw_config(dev); if (ret != 0) { oce_log(dev, CE_WARN, MOD_CONFIG, "Firmware configuration read failed with %d", ret); goto init_fail; } /* read the Factory MAC address */ ret = oce_read_mac_addr(dev, 0, 1, MAC_ADDRESS_TYPE_NETWORK, &mac_addr); if (ret != 0) { oce_log(dev, CE_WARN, MOD_CONFIG, "MAC address read failed with %d", ret); goto init_fail; } bcopy(&mac_addr.mac_addr[0], &dev->mac_addr[0], ETHERADDRL); return (DDI_SUCCESS); init_fail: oce_hw_fini(dev); return (DDI_FAILURE); } void oce_hw_fini(struct oce_dev *dev) { if (dev->bmbx != NULL) { oce_free_dma_buffer(dev, dev->bmbx); dev->bmbx = NULL; } }