static int bf_pci_probe()

in platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_main.c [866:1125]


static int bf_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) {
  struct bf_pci_dev *bfdev;
  int err, pci_use_highmem;
  int i, num_irq;
  u32 build_date, build_ver;

  memset(bf_global, 0, sizeof(bf_global));

  bfdev = kzalloc(sizeof(struct bf_pci_dev), GFP_KERNEL);
  if (!bfdev) {
    return -ENOMEM;
  }

  /* init the cookies to be passed to ISRs */
  for (i = 0; i < BF_MSIX_ENTRY_CNT; i++) {
    bfdev->bf_int_vec[i].int_vec_offset = i;
    bfdev->bf_int_vec[i].bf_dev = bfdev;
  }

  /* initialize intr_mode to none */
  bfdev->mode = BF_INTR_MODE_NONE;

  /* clear pci_error_state */
  bfdev->info.pci_error_state = 0;

  /*
   * enable device
   */
  err = pci_enable_device(pdev);
  if (err != 0) {
    printk(KERN_ERR "bf_fpga cannot enable PCI device\n");
    goto fail_free;
  }

  /*
   * reserve device's PCI memory regions for use by this
   * module
   */
  err = pci_request_regions(pdev, "bf_fpga_umem");
  if (err != 0) {
    printk(KERN_ERR "bf_fpga Cannot request regions\n");
    goto fail_pci_disable;
  }
  /* remap IO memory */
  err = bf_setup_bars(pdev, &bfdev->info);
  if (err != 0) {
    printk(KERN_ERR "bf_fpga Cannot setup BARs\n");
    goto fail_release_iomem;
  }

  if (!dma_set_mask(pci_dev_to_dev(pdev), DMA_BIT_MASK(64)) &&
      !dma_set_coherent_mask(pci_dev_to_dev(pdev), DMA_BIT_MASK(64))) {
    pci_use_highmem = 1;
  } else {
    err = dma_set_mask(pci_dev_to_dev(pdev), DMA_BIT_MASK(32));
    if (err) {
      err = dma_set_coherent_mask(pci_dev_to_dev(pdev), DMA_BIT_MASK(32));
      if (err) {
        printk(KERN_ERR "bf_fpga no usable DMA configuration, aborting\n");
        goto fail_release_iomem;
      }
    }
    pci_use_highmem = 0;
  }

  /* enable pci error reporting */
  /* for the current kernel version, kernel config must have set the followings:
   * CONFIG_PCIEPORTBUS=y and CONFIG_PCIEAER = y
   * we have pci_error_handlers defined that gets invoked by kernel AER module
   * upon detecting the pcie error on this device's addresses.
   * However, there seems no way that AER would pass the offending addresses
   * to the callback functions. AER logs the error messages on the console.
   * This driver's calback function send the SIGIO signal to the user space
   * to indicate the error condition.
   */
  pci_enable_pcie_error_reporting(pdev);

  bf_fpga_disable_int_dma(bfdev);

  /* enable bus mastering on the device */
  pci_set_master(pdev);

  /* fill in bfdev info */
  bfdev->info.version = "0.1";
  bfdev->info.owner = THIS_MODULE;
  bfdev->pdev = pdev;

  switch (bf_intr_mode_default) {
#ifdef CONFIG_PCI_MSI
    case BF_INTR_MODE_MSIX:
      /* Only 1 msi-x vector needed */
      bfdev->info.msix_entries =
          kcalloc(BF_MSIX_ENTRY_CNT, sizeof(struct msix_entry), GFP_KERNEL);
      if (!bfdev->info.msix_entries) {
        err = -ENOMEM;
        goto fail_clear_pci_master;
      }
      for (i = 0; i < BF_MSIX_ENTRY_CNT; i++) {
        bfdev->info.msix_entries[i].entry = i;
      }
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
      num_irq =
          pci_enable_msix(pdev, bfdev->info.msix_entries, BF_MSIX_ENTRY_CNT);
      if (num_irq == 0) {
        bfdev->info.num_irq = BF_MSIX_ENTRY_CNT;
        bfdev->info.irq = bfdev->info.msix_entries[0].vector;
        bfdev->mode = BF_INTR_MODE_MSIX;
        printk(KERN_DEBUG "bf_fpga using %d MSIX irq from %ld\n",
               num_irq,
               bfdev->info.irq);
        break;
      }
#else
      num_irq = pci_enable_msix_range(
          pdev, bfdev->info.msix_entries, BF_MSIX_ENTRY_CNT, BF_MSIX_ENTRY_CNT);
      if (num_irq == BF_MSIX_ENTRY_CNT) {
        bfdev->info.num_irq = num_irq;
        bfdev->info.irq = bfdev->info.msix_entries[0].vector;
        bfdev->mode = BF_INTR_MODE_MSIX;
        printk(KERN_DEBUG "bf_fpga using %d MSIX irq from %ld\n",
               num_irq,
               bfdev->info.irq);
        break;
      } else {
        if (num_irq) pci_disable_msix(pdev);
        kfree(bfdev->info.msix_entries);
        bfdev->info.msix_entries = NULL;
        printk(KERN_ERR
               "bf_fpga error allocating MSIX vectors. Trying MSI...\n");
        /* and, fall back to MSI */
      }
#endif /* LINUX_VERSION_CODE */
    /* ** intentional no-break */
    /* FALLTHRU */
    case BF_INTR_MODE_MSI:
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
      num_irq = pci_enable_msi_block(pdev, BF_MSI_ENTRY_CNT);
      /* we must get requested number of MSI vectors enabled */
      if (num_irq == 0) {
        bfdev->info.num_irq = BF_MSI_ENTRY_CNT;
        bfdev->info.irq = pdev->irq;
        bfdev->mode = BF_INTR_MODE_MSI;
        printk(KERN_DEBUG "bf_fpga using %d MSI irq from %ld\n",
               bfdev->info.num_irq,
               bfdev->info.irq);
        break;
      }
#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
      num_irq = pci_enable_msi_range(pdev, BF_MSI_ENTRY_CNT, BF_MSI_ENTRY_CNT);
      if (num_irq > 0) {
        bfdev->info.num_irq = num_irq;
        bfdev->info.irq = pdev->irq;
        bfdev->mode = BF_INTR_MODE_MSI;
        printk(KERN_DEBUG "bf_fpga using %d MSI irq from %ld\n",
               bfdev->info.num_irq,
               bfdev->info.irq);
        break;
      }
#else
      num_irq = pci_alloc_irq_vectors_affinity(pdev,
                                               BF_MSI_ENTRY_CNT,
                                               BF_MSI_ENTRY_CNT,
                                               PCI_IRQ_MSI | PCI_IRQ_AFFINITY,
                                               NULL);
      if (num_irq > 0) {
        bfdev->info.num_irq = num_irq;
        bfdev->info.irq = pci_irq_vector(pdev, 0);
        bfdev->mode = BF_INTR_MODE_MSI;
        printk(KERN_DEBUG "bf_fpga using %d MSI irq from %ld\n",
               bfdev->info.num_irq,
               bfdev->info.irq);
        break;
      }
#endif /* LINUX_VERSION_CODE */
#endif /* CONFIG_PCI_MSI */
      /* fall back to Legacy Interrupt, intentional no-break */
      /* FALLTHRU */

    case BF_INTR_MODE_LEGACY:
      if (pci_intx_mask_supported(pdev)) {
        bfdev->info.irq_flags = IRQF_SHARED;
        bfdev->info.irq = pdev->irq;
        bfdev->mode = BF_INTR_MODE_LEGACY;
        printk(KERN_DEBUG "bf_fpga using LEGACY irq %ld\n", bfdev->info.irq);
        break;
      }
      printk(KERN_NOTICE "bf_fpga PCI INTx mask not supported\n");
    /* fall back to no Interrupt, intentional no-break */
    /* FALLTHRU */
    case BF_INTR_MODE_NONE:
      bfdev->info.irq = 0;
      bfdev->info.num_irq = 0;
      bfdev->mode = BF_INTR_MODE_NONE;
      break;

    default:
      printk(KERN_DEBUG "bf_fpga invalid IRQ mode %u", bf_intr_mode_default);
      err = -EINVAL;
      goto fail_clear_pci_master;
  }

  pci_set_drvdata(pdev, bfdev);
  sprintf(bfdev->name, "bf_fpga%d", bfdev->info.minor);
  /* register bf driver */
  err = bf_register_device(&pdev->dev, bfdev);
  if (err != 0) {
    goto fail_release_irq;
  }

  bf_global[bfdev->info.minor].async_queue = NULL;
  bf_global[bfdev->info.minor].bfdev = bfdev;

  dev_info(&pdev->dev,
           "bf_fpga device %d registered with irq %ld\n",
           bfdev->instance,
           bfdev->info.irq);
  if (fpga_i2c_init(bfdev->info.mem[0].internal_addr)) {
    printk(KERN_ERR "bf_fpga i2c initialization failed\n");
    goto fail_register_device;
  }
  if (bf_fpga_sysfs_add(bfdev)) {
    printk(KERN_ERR "bf_fpga stsfs initialization failed\n");
    goto fail_i2c_init;
  }
  build_ver =
      *((u32 *)(bfdev->info.mem[0].internal_addr) + (BF_FPGA_VER_REG / 4));
  build_date =
      *((u32 *)(bfdev->info.mem[0].internal_addr) + (BF_FPGA_BUILD_DATE / 4));
  fpga_print_build_date(build_date);
  printk(KERN_ALERT "bf_fpga version %hu.%hu probe ok\n",
         (u16)(build_ver >> 16),
         (u16)(build_ver));
  return 0;

fail_i2c_init:
  fpga_i2c_deinit();
fail_register_device:
  bf_unregister_device(bfdev);
fail_release_irq:
  pci_set_drvdata(pdev, NULL);
  if (bfdev->mode == BF_INTR_MODE_MSIX) {
    pci_disable_msix(bfdev->pdev);
    kfree(bfdev->info.msix_entries);
    bfdev->info.msix_entries = NULL;
  } else if (bfdev->mode == BF_INTR_MODE_MSI) {
    pci_disable_msi(bfdev->pdev);
  }
fail_clear_pci_master:
  pci_clear_master(pdev);
fail_release_iomem:
  bf_pci_release_iomem(&bfdev->info);
  pci_release_regions(pdev);
fail_pci_disable:
  pci_disable_device(pdev);
fail_free:
  kfree(bfdev);

  printk(KERN_ERR "bf_fpga probe failed\n");
  return err;
}