in bcm-vk/bcm_vk_dev.c [1279:1532]
static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int err;
int i;
int id;
int irq;
char name[20];
struct bcm_vk *vk;
struct device *dev = &pdev->dev;
struct miscdevice *misc_device;
u32 boot_status;
/* allocate vk structure which is tied to kref for freeing */
vk = kzalloc(sizeof(*vk), GFP_KERNEL);
if (!vk)
return -ENOMEM;
kref_init(&vk->kref);
if (nr_ib_sgl_blk > BCM_VK_IB_SGL_BLK_MAX) {
dev_warn(dev, "Inband SGL blk %d limited to max %d\n",
nr_ib_sgl_blk, BCM_VK_IB_SGL_BLK_MAX);
nr_ib_sgl_blk = BCM_VK_IB_SGL_BLK_MAX;
}
vk->ib_sgl_size = nr_ib_sgl_blk * VK_MSGQ_BLK_SIZE;
mutex_init(&vk->mutex);
err = pci_enable_device(pdev);
if (err) {
dev_err(dev, "Cannot enable PCI device\n");
goto err_free_exit;
}
vk->pdev = pci_dev_get(pdev);
err = pci_request_regions(pdev, DRV_MODULE_NAME);
if (err) {
dev_err(dev, "Cannot obtain PCI resources\n");
goto err_disable_pdev;
}
/* make sure DMA is good */
err = dma_set_mask_and_coherent(&pdev->dev,
DMA_BIT_MASK(BCM_VK_DMA_BITS));
if (err) {
dev_err(dev, "failed to set DMA mask\n");
goto err_disable_pdev;
}
/* The tdma is a scratch area for some DMA testings. */
if (nr_scratch_pages) {
vk->tdma_vaddr = dma_alloc_coherent
(dev,
nr_scratch_pages * PAGE_SIZE,
&vk->tdma_addr, GFP_KERNEL);
if (!vk->tdma_vaddr) {
err = -ENOMEM;
goto err_disable_pdev;
}
}
pci_set_master(pdev);
pci_set_drvdata(pdev, vk);
irq = pci_alloc_irq_vectors(pdev,
1,
VK_MSIX_IRQ_MAX,
PCI_IRQ_MSI | PCI_IRQ_MSIX);
if (irq < VK_MSIX_IRQ_MIN_REQ) {
dev_err(dev, "failed to get min %d MSIX interrupts, irq(%d)\n",
VK_MSIX_IRQ_MIN_REQ, irq);
err = (irq >= 0) ? -EINVAL : irq;
goto err_disable_pdev;
}
if (irq != VK_MSIX_IRQ_MAX)
dev_warn(dev, "Number of IRQs %d allocated - requested(%d).\n",
irq, VK_MSIX_IRQ_MAX);
for (i = 0; i < MAX_BAR; i++) {
/* multiple by 2 for 64 bit BAR mapping */
vk->bar[i] = pci_ioremap_bar(pdev, i * 2);
if (!vk->bar[i]) {
dev_err(dev, "failed to remap BAR%d\n", i);
err = -ENOMEM;
goto err_iounmap;
}
}
for (vk->num_irqs = 0;
vk->num_irqs < VK_MSIX_MSGQ_MAX;
vk->num_irqs++) {
err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs),
bcm_vk_msgq_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, vk);
if (err) {
dev_err(dev, "failed to request msgq IRQ %d for MSIX %d\n",
pdev->irq + vk->num_irqs, vk->num_irqs + 1);
goto err_irq;
}
}
/* one irq for notification from VK */
err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs),
bcm_vk_notf_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, vk);
if (err) {
dev_err(dev, "failed to request notf IRQ %d for MSIX %d\n",
pdev->irq + vk->num_irqs, vk->num_irqs + 1);
goto err_irq;
}
vk->num_irqs++;
for (i = 0;
(i < VK_MSIX_TTY_MAX) && (vk->num_irqs < irq);
i++, vk->num_irqs++) {
err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs),
bcm_vk_tty_irqhandler,
IRQF_SHARED, DRV_MODULE_NAME, vk);
if (err) {
dev_err(dev, "failed request tty IRQ %d for MSIX %d\n",
pdev->irq + vk->num_irqs, vk->num_irqs + 1);
goto err_irq;
}
bcm_vk_tty_set_irq_enabled(vk, i);
}
id = ida_simple_get(&bcm_vk_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
err = id;
dev_err(dev, "unable to get id\n");
goto err_irq;
}
vk->devid = id;
snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id);
misc_device = &vk->miscdev;
misc_device->minor = MISC_DYNAMIC_MINOR;
misc_device->name = kstrdup(name, GFP_KERNEL);
if (!misc_device->name) {
err = -ENOMEM;
goto err_ida_remove;
}
misc_device->fops = &bcm_vk_fops,
err = misc_register(misc_device);
if (err) {
dev_err(dev, "failed to register device\n");
goto err_kfree_name;
}
INIT_WORK(&vk->wq_work, bcm_vk_wq_handler);
/* create dedicated workqueue */
vk->wq_thread = create_singlethread_workqueue(name);
if (!vk->wq_thread) {
dev_err(dev, "Fail to create workqueue thread\n");
err = -ENOMEM;
goto err_misc_deregister;
}
err = bcm_vk_msg_init(vk);
if (err) {
dev_err(dev, "failed to init msg queue info\n");
goto err_destroy_workqueue;
}
/* sync other info */
bcm_vk_sync_card_info(vk);
/* register for panic notifier */
vk->panic_nb.notifier_call = bcm_vk_on_panic;
err = atomic_notifier_chain_register(&panic_notifier_list,
&vk->panic_nb);
if (err) {
dev_err(dev, "Fail to register panic notifier\n");
goto err_destroy_workqueue;
}
snprintf(name, sizeof(name), KBUILD_MODNAME ".%d_ttyVK", id);
err = bcm_vk_tty_init(vk, name);
if (err)
goto err_unregister_panic_notifier;
/*
* lets trigger an auto download. We don't want to do it serially here
* because at probing time, it is not supposed to block for a long time.
*/
boot_status = vkread32(vk, BAR_0, BAR_BOOT_STATUS);
if (auto_load) {
if ((boot_status & BOOT_STATE_MASK) == BROM_RUNNING) {
err = bcm_vk_trigger_autoload(vk);
if (err)
goto err_bcm_vk_tty_exit;
} else {
dev_err(dev,
"Auto-load skipped - BROM not in proper state (0x%x)\n",
boot_status);
}
}
/* enable hb */
bcm_vk_hb_init(vk);
dev_dbg(dev, "BCM-VK:%u created\n", id);
return 0;
err_bcm_vk_tty_exit:
bcm_vk_tty_exit(vk);
err_unregister_panic_notifier:
atomic_notifier_chain_unregister(&panic_notifier_list,
&vk->panic_nb);
err_destroy_workqueue:
destroy_workqueue(vk->wq_thread);
err_misc_deregister:
misc_deregister(misc_device);
err_kfree_name:
kfree(misc_device->name);
misc_device->name = NULL;
err_ida_remove:
ida_simple_remove(&bcm_vk_ida, id);
err_irq:
for (i = 0; i < vk->num_irqs; i++)
devm_free_irq(dev, pci_irq_vector(pdev, i), vk);
pci_disable_msix(pdev);
pci_disable_msi(pdev);
err_iounmap:
for (i = 0; i < MAX_BAR; i++) {
if (vk->bar[i])
pci_iounmap(pdev, vk->bar[i]);
}
pci_release_regions(pdev);
err_disable_pdev:
if (vk->tdma_vaddr)
dma_free_coherent(&pdev->dev, nr_scratch_pages * PAGE_SIZE,
vk->tdma_vaddr, vk->tdma_addr);
pci_free_irq_vectors(pdev);
pci_disable_device(pdev);
pci_dev_put(pdev);
err_free_exit:
kfree(vk);
return err;
}