static int tsi148_probe()

in bridges/vme_tsi148.c [2269:2556]


static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
	int retval, i, master_num;
	u32 data;
	struct list_head *pos = NULL, *n;
	struct vme_bridge *tsi148_bridge;
	struct tsi148_driver *tsi148_device;
	struct vme_master_resource *master_image;
	struct vme_slave_resource *slave_image;
	struct vme_dma_resource *dma_ctrlr;
	struct vme_lm_resource *lm;

	/* If we want to support more than one of each bridge, we need to
	 * dynamically generate this so we get one per device
	 */
	tsi148_bridge = kzalloc(sizeof(*tsi148_bridge), GFP_KERNEL);
	if (!tsi148_bridge) {
		retval = -ENOMEM;
		goto err_struct;
	}
	vme_init_bridge(tsi148_bridge);

	tsi148_device = kzalloc(sizeof(*tsi148_device), GFP_KERNEL);
	if (!tsi148_device) {
		retval = -ENOMEM;
		goto err_driver;
	}

	tsi148_bridge->driver_priv = tsi148_device;

	/* Enable the device */
	retval = pci_enable_device(pdev);
	if (retval) {
		dev_err(&pdev->dev, "Unable to enable device\n");
		goto err_enable;
	}

	/* Map Registers */
	retval = pci_request_regions(pdev, driver_name);
	if (retval) {
		dev_err(&pdev->dev, "Unable to reserve resources\n");
		goto err_resource;
	}

	/* map registers in BAR 0 */
	tsi148_device->base = ioremap(pci_resource_start(pdev, 0),
		4096);
	if (!tsi148_device->base) {
		dev_err(&pdev->dev, "Unable to remap CRG region\n");
		retval = -EIO;
		goto err_remap;
	}

	/* Check to see if the mapping worked out */
	data = ioread32(tsi148_device->base + TSI148_PCFS_ID) & 0x0000FFFF;
	if (data != PCI_VENDOR_ID_TUNDRA) {
		dev_err(&pdev->dev, "CRG region check failed\n");
		retval = -EIO;
		goto err_test;
	}

	/* Initialize wait queues & mutual exclusion flags */
	init_waitqueue_head(&tsi148_device->dma_queue[0]);
	init_waitqueue_head(&tsi148_device->dma_queue[1]);
	init_waitqueue_head(&tsi148_device->iack_queue);
	mutex_init(&tsi148_device->vme_int);
	mutex_init(&tsi148_device->vme_rmw);

	tsi148_bridge->parent = &pdev->dev;
	strcpy(tsi148_bridge->name, driver_name);

	/* Setup IRQ */
	retval = tsi148_irq_init(tsi148_bridge);
	if (retval != 0) {
		dev_err(&pdev->dev, "Chip Initialization failed.\n");
		goto err_irq;
	}

	/* If we are going to flush writes, we need to read from the VME bus.
	 * We need to do this safely, thus we read the devices own CR/CSR
	 * register. To do this we must set up a window in CR/CSR space and
	 * hence have one less master window resource available.
	 */
	master_num = TSI148_MAX_MASTER;
	if (err_chk) {
		master_num--;

		tsi148_device->flush_image =
			kmalloc(sizeof(*tsi148_device->flush_image),
				GFP_KERNEL);
		if (!tsi148_device->flush_image) {
			retval = -ENOMEM;
			goto err_master;
		}
		tsi148_device->flush_image->parent = tsi148_bridge;
		spin_lock_init(&tsi148_device->flush_image->lock);
		tsi148_device->flush_image->locked = 1;
		tsi148_device->flush_image->number = master_num;
		memset(&tsi148_device->flush_image->bus_resource, 0,
		       sizeof(tsi148_device->flush_image->bus_resource));
		tsi148_device->flush_image->kern_base  = NULL;
	}

	/* Add master windows to list */
	for (i = 0; i < master_num; i++) {
		master_image = kmalloc(sizeof(*master_image), GFP_KERNEL);
		if (!master_image) {
			retval = -ENOMEM;
			goto err_master;
		}
		master_image->parent = tsi148_bridge;
		spin_lock_init(&master_image->lock);
		master_image->locked = 0;
		master_image->number = i;
		master_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
			VME_A64 | VME_CRCSR | VME_USER1 | VME_USER2 |
			VME_USER3 | VME_USER4;
		master_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
			VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
			VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
			VME_PROG | VME_DATA;
		master_image->width_attr = VME_D16 | VME_D32;
		memset(&master_image->bus_resource, 0,
		       sizeof(master_image->bus_resource));
		master_image->kern_base  = NULL;
		list_add_tail(&master_image->list,
			&tsi148_bridge->master_resources);
	}

	/* Add slave windows to list */
	for (i = 0; i < TSI148_MAX_SLAVE; i++) {
		slave_image = kmalloc(sizeof(*slave_image), GFP_KERNEL);
		if (!slave_image) {
			retval = -ENOMEM;
			goto err_slave;
		}
		slave_image->parent = tsi148_bridge;
		mutex_init(&slave_image->mtx);
		slave_image->locked = 0;
		slave_image->number = i;
		slave_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
			VME_A64;
		slave_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
			VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
			VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
			VME_PROG | VME_DATA;
		list_add_tail(&slave_image->list,
			&tsi148_bridge->slave_resources);
	}

	/* Add dma engines to list */
	for (i = 0; i < TSI148_MAX_DMA; i++) {
		dma_ctrlr = kmalloc(sizeof(*dma_ctrlr), GFP_KERNEL);
		if (!dma_ctrlr) {
			retval = -ENOMEM;
			goto err_dma;
		}
		dma_ctrlr->parent = tsi148_bridge;
		mutex_init(&dma_ctrlr->mtx);
		dma_ctrlr->locked = 0;
		dma_ctrlr->number = i;
		dma_ctrlr->route_attr = VME_DMA_VME_TO_MEM |
			VME_DMA_MEM_TO_VME | VME_DMA_VME_TO_VME |
			VME_DMA_MEM_TO_MEM | VME_DMA_PATTERN_TO_VME |
			VME_DMA_PATTERN_TO_MEM;
		INIT_LIST_HEAD(&dma_ctrlr->pending);
		INIT_LIST_HEAD(&dma_ctrlr->running);
		list_add_tail(&dma_ctrlr->list,
			&tsi148_bridge->dma_resources);
	}

	/* Add location monitor to list */
	lm = kmalloc(sizeof(*lm), GFP_KERNEL);
	if (!lm) {
		retval = -ENOMEM;
		goto err_lm;
	}
	lm->parent = tsi148_bridge;
	mutex_init(&lm->mtx);
	lm->locked = 0;
	lm->number = 1;
	lm->monitors = 4;
	list_add_tail(&lm->list, &tsi148_bridge->lm_resources);

	tsi148_bridge->slave_get = tsi148_slave_get;
	tsi148_bridge->slave_set = tsi148_slave_set;
	tsi148_bridge->master_get = tsi148_master_get;
	tsi148_bridge->master_set = tsi148_master_set;
	tsi148_bridge->master_read = tsi148_master_read;
	tsi148_bridge->master_write = tsi148_master_write;
	tsi148_bridge->master_rmw = tsi148_master_rmw;
	tsi148_bridge->dma_list_add = tsi148_dma_list_add;
	tsi148_bridge->dma_list_exec = tsi148_dma_list_exec;
	tsi148_bridge->dma_list_empty = tsi148_dma_list_empty;
	tsi148_bridge->irq_set = tsi148_irq_set;
	tsi148_bridge->irq_generate = tsi148_irq_generate;
	tsi148_bridge->lm_set = tsi148_lm_set;
	tsi148_bridge->lm_get = tsi148_lm_get;
	tsi148_bridge->lm_attach = tsi148_lm_attach;
	tsi148_bridge->lm_detach = tsi148_lm_detach;
	tsi148_bridge->slot_get = tsi148_slot_get;
	tsi148_bridge->alloc_consistent = tsi148_alloc_consistent;
	tsi148_bridge->free_consistent = tsi148_free_consistent;

	data = ioread32be(tsi148_device->base + TSI148_LCSR_VSTAT);
	dev_info(&pdev->dev, "Board is%s the VME system controller\n",
		(data & TSI148_LCSR_VSTAT_SCONS) ? "" : " not");
	if (!geoid)
		dev_info(&pdev->dev, "VME geographical address is %d\n",
			data & TSI148_LCSR_VSTAT_GA_M);
	else
		dev_info(&pdev->dev, "VME geographical address is set to %d\n",
			geoid);

	dev_info(&pdev->dev, "VME Write and flush and error check is %s\n",
		err_chk ? "enabled" : "disabled");

	retval = tsi148_crcsr_init(tsi148_bridge, pdev);
	if (retval) {
		dev_err(&pdev->dev, "CR/CSR configuration failed.\n");
		goto err_crcsr;
	}

	retval = vme_register_bridge(tsi148_bridge);
	if (retval != 0) {
		dev_err(&pdev->dev, "Chip Registration failed.\n");
		goto err_reg;
	}

	pci_set_drvdata(pdev, tsi148_bridge);

	/* Clear VME bus "board fail", and "power-up reset" lines */
	data = ioread32be(tsi148_device->base + TSI148_LCSR_VSTAT);
	data &= ~TSI148_LCSR_VSTAT_BRDFL;
	data |= TSI148_LCSR_VSTAT_CPURST;
	iowrite32be(data, tsi148_device->base + TSI148_LCSR_VSTAT);

	return 0;

err_reg:
	tsi148_crcsr_exit(tsi148_bridge, pdev);
err_crcsr:
err_lm:
	/* resources are stored in link list */
	list_for_each_safe(pos, n, &tsi148_bridge->lm_resources) {
		lm = list_entry(pos, struct vme_lm_resource, list);
		list_del(pos);
		kfree(lm);
	}
err_dma:
	/* resources are stored in link list */
	list_for_each_safe(pos, n, &tsi148_bridge->dma_resources) {
		dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
		list_del(pos);
		kfree(dma_ctrlr);
	}
err_slave:
	/* resources are stored in link list */
	list_for_each_safe(pos, n, &tsi148_bridge->slave_resources) {
		slave_image = list_entry(pos, struct vme_slave_resource, list);
		list_del(pos);
		kfree(slave_image);
	}
err_master:
	/* resources are stored in link list */
	list_for_each_safe(pos, n, &tsi148_bridge->master_resources) {
		master_image = list_entry(pos, struct vme_master_resource,
			list);
		list_del(pos);
		kfree(master_image);
	}

	tsi148_irq_exit(tsi148_bridge, pdev);
err_irq:
err_test:
	iounmap(tsi148_device->base);
err_remap:
	pci_release_regions(pdev);
err_resource:
	pci_disable_device(pdev);
err_enable:
	kfree(tsi148_device);
err_driver:
	kfree(tsi148_bridge);
err_struct:
	return retval;

}