+int wait_for_msix_trans(nic_t *nic, int i)
+{
+ XENA_dev_config_t *bar0 = (XENA_dev_config_t *) nic->bar0;
+ u64 val64;
+ int ret = 0, cnt = 0;
+
+ do {
+ val64 = readq(&bar0->xmsi_access);
+ if (!(val64 & BIT(15)))
+ break;
+ mdelay(1);
+ cnt++;
+ } while(cnt < 5);
+ if (cnt == 5) {
+ DBG_PRINT(ERR_DBG, "XMSI # %d Access failed\n", i);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+void restore_xmsi_data(nic_t *nic)
+{
+ XENA_dev_config_t *bar0 = (XENA_dev_config_t *) nic->bar0;
+ u64 val64;
+ int i;
+
+ for (i=0; i< MAX_REQUESTED_MSI_X; i++) {
+ writeq(nic->msix_info[i].addr, &bar0->xmsi_address);
+ writeq(nic->msix_info[i].data, &bar0->xmsi_data);
+ val64 = (BIT(7) | BIT(15) | vBIT(i, 26, 6));
+ writeq(val64, &bar0->xmsi_access);
+ if (wait_for_msix_trans(nic, i)) {
+ DBG_PRINT(ERR_DBG, "failed in %s\n", __FUNCTION__);
+ continue;
+ }
+ }
+}
+
+void store_xmsi_data(nic_t *nic)
+{
+ XENA_dev_config_t *bar0 = (XENA_dev_config_t *) nic->bar0;
+ u64 val64, addr, data;
+ int i;
+
+ /* Store and display */
+ for (i=0; i< MAX_REQUESTED_MSI_X; i++) {
+ val64 = (BIT(15) | vBIT(i, 26, 6));
+ writeq(val64, &bar0->xmsi_access);
+ if (wait_for_msix_trans(nic, i)) {
+ DBG_PRINT(ERR_DBG, "failed in %s\n", __FUNCTION__);
+ continue;
+ }
+ addr = readq(&bar0->xmsi_address);
+ data = readq(&bar0->xmsi_data);
+ if (addr && data) {
+ nic->msix_info[i].addr = addr;
+ nic->msix_info[i].data = data;
+ }
+ }
+}
+
+int s2io_enable_msi(nic_t *nic)
+{
+ XENA_dev_config_t *bar0 = (XENA_dev_config_t *) nic->bar0;
+ u16 msi_ctrl, msg_val;
+ struct config_param *config = &nic->config;
+ struct net_device *dev = nic->dev;
+ u64 val64, tx_mat, rx_mat;
+ int i, err;
+
+ val64 = readq(&bar0->pic_control);
+ val64 &= ~BIT(1);
+ writeq(val64, &bar0->pic_control);
+
+ err = pci_enable_msi(nic->pdev);
+ if (err) {
+ DBG_PRINT(ERR_DBG, "%s: enabling MSI failed\n",
+ nic->dev->name);
+ return err;
+ }
+
+ /*
+ * Enable MSI and use MSI-1 in stead of the standard MSI-0
+ * for interrupt handling.
+ */
+ pci_read_config_word(nic->pdev, 0x4c, &msg_val);
+ msg_val ^= 0x1;
+ pci_write_config_word(nic->pdev, 0x4c, msg_val);
+ pci_read_config_word(nic->pdev, 0x4c, &msg_val);
+
+ pci_read_config_word(nic->pdev, 0x42, &msi_ctrl);
+ msi_ctrl |= 0x10;
+ pci_write_config_word(nic->pdev, 0x42, msi_ctrl);
+
+ /* program MSI-1 into all usable Tx_Mat and Rx_Mat fields */
+ tx_mat = readq(&bar0->tx_mat0_n[0]);
+ for (i=0; i<config->tx_fifo_num; i++) {
+ tx_mat |= TX_MAT_SET(i, 1);
+ }
+ writeq(tx_mat, &bar0->tx_mat0_n[0]);
+
+ rx_mat = readq(&bar0->rx_mat);
+ for (i=0; i<config->rx_ring_num; i++) {
+ rx_mat |= RX_MAT_SET(i, 1);
+ }
+ writeq(rx_mat, &bar0->rx_mat);
+
+ dev->irq = nic->pdev->irq;
+ return 0;
+}
+
+int s2io_enable_msi_x(nic_t *nic)
+{
+ XENA_dev_config_t *bar0 = (XENA_dev_config_t *) nic->bar0;
+ u64 tx_mat, rx_mat;
+ u16 msi_control; /* Temp variable */
+ int ret, i, j, msix_indx = 1;
+
+ nic->entries = kmalloc(MAX_REQUESTED_MSI_X * sizeof(struct msix_entry),
+ GFP_KERNEL);
+ if (nic->entries == NULL) {
+ DBG_PRINT(ERR_DBG, "%s: Memory allocation failed\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+ memset(nic->entries, 0, MAX_REQUESTED_MSI_X * sizeof(struct msix_entry));
+
+ nic->s2io_entries =
+ kmalloc(MAX_REQUESTED_MSI_X * sizeof(struct s2io_msix_entry),
+ GFP_KERNEL);
+ if (nic->s2io_entries == NULL) {
+ DBG_PRINT(ERR_DBG, "%s: Memory allocation failed\n", __FUNCTION__);
+ kfree(nic->entries);
+ return -ENOMEM;
+ }
+ memset(nic->s2io_entries, 0,
+ MAX_REQUESTED_MSI_X * sizeof(struct s2io_msix_entry));
+
+ for (i=0; i< MAX_REQUESTED_MSI_X; i++) {
+ nic->entries[i].entry = i;
+ nic->s2io_entries[i].entry = i;
+ nic->s2io_entries[i].arg = NULL;
+ nic->s2io_entries[i].in_use = 0;
+ }
+
+ tx_mat = readq(&bar0->tx_mat0_n[0]);
+ for (i=0; i<nic->config.tx_fifo_num; i++, msix_indx++) {
+ tx_mat |= TX_MAT_SET(i, msix_indx);
+ nic->s2io_entries[msix_indx].arg = &nic->mac_control.fifos[i];
+ nic->s2io_entries[msix_indx].type = MSIX_FIFO_TYPE;
+ nic->s2io_entries[msix_indx].in_use = MSIX_FLG;
+ }
+ writeq(tx_mat, &bar0->tx_mat0_n[0]);
+
+ if (!nic->config.bimodal) {
+ rx_mat = readq(&bar0->rx_mat);
+ for (j=0; j<nic->config.rx_ring_num; j++, msix_indx++) {
+ rx_mat |= RX_MAT_SET(j, msix_indx);
+ nic->s2io_entries[msix_indx].arg = &nic->mac_control.rings[j];
+ nic->s2io_entries[msix_indx].type = MSIX_RING_TYPE;
+ nic->s2io_entries[msix_indx].in_use = MSIX_FLG;
+ }
+ writeq(rx_mat, &bar0->rx_mat);
+ } else {
+ tx_mat = readq(&bar0->tx_mat0_n[7]);
+ for (j=0; j<nic->config.rx_ring_num; j++, msix_indx++) {
+ tx_mat |= TX_MAT_SET(i, msix_indx);
+ nic->s2io_entries[msix_indx].arg = &nic->mac_control.rings[j];
+ nic->s2io_entries[msix_indx].type = MSIX_RING_TYPE;
+ nic->s2io_entries[msix_indx].in_use = MSIX_FLG;
+ }
+ writeq(tx_mat, &bar0->tx_mat0_n[7]);
+ }
+
+ ret = pci_enable_msix(nic->pdev, nic->entries, MAX_REQUESTED_MSI_X);
+ if (ret) {
+ DBG_PRINT(ERR_DBG, "%s: Enabling MSIX failed\n", nic->dev->name);
+ kfree(nic->entries);
+ kfree(nic->s2io_entries);
+ nic->entries = NULL;
+ nic->s2io_entries = NULL;
+ return -ENOMEM;
+ }
+
+ /*
+ * To enable MSI-X, MSI also needs to be enabled, due to a bug
+ * in the herc NIC. (Temp change, needs to be removed later)
+ */
+ pci_read_config_word(nic->pdev, 0x42, &msi_control);
+ msi_control |= 0x1; /* Enable MSI */
+ pci_write_config_word(nic->pdev, 0x42, msi_control);
+
+ return 0;
+}
+