static int tegra_dma_probe()

in tegra20-apb-dma.c [1439:1588]


static int tegra_dma_probe(struct platform_device *pdev)
{
	const struct tegra_dma_chip_data *cdata;
	struct tegra_dma *tdma;
	unsigned int i;
	size_t size;
	int ret;

	cdata = of_device_get_match_data(&pdev->dev);
	size = struct_size(tdma, channels, cdata->nr_channels);

	tdma = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
	if (!tdma)
		return -ENOMEM;

	tdma->dev = &pdev->dev;
	tdma->chip_data = cdata;
	platform_set_drvdata(pdev, tdma);

	tdma->base_addr = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(tdma->base_addr))
		return PTR_ERR(tdma->base_addr);

	tdma->dma_clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(tdma->dma_clk)) {
		dev_err(&pdev->dev, "Error: Missing controller clock\n");
		return PTR_ERR(tdma->dma_clk);
	}

	tdma->rst = devm_reset_control_get(&pdev->dev, "dma");
	if (IS_ERR(tdma->rst)) {
		dev_err(&pdev->dev, "Error: Missing reset\n");
		return PTR_ERR(tdma->rst);
	}

	spin_lock_init(&tdma->global_lock);

	ret = clk_prepare(tdma->dma_clk);
	if (ret)
		return ret;

	ret = tegra_dma_init_hw(tdma);
	if (ret)
		goto err_clk_unprepare;

	pm_runtime_irq_safe(&pdev->dev);
	pm_runtime_enable(&pdev->dev);

	INIT_LIST_HEAD(&tdma->dma_dev.channels);
	for (i = 0; i < cdata->nr_channels; i++) {
		struct tegra_dma_channel *tdc = &tdma->channels[i];
		int irq;

		tdc->chan_addr = tdma->base_addr +
				 TEGRA_APBDMA_CHANNEL_BASE_ADD_OFFSET +
				 (i * cdata->channel_reg_size);

		irq = platform_get_irq(pdev, i);
		if (irq < 0) {
			ret = irq;
			goto err_pm_disable;
		}

		snprintf(tdc->name, sizeof(tdc->name), "apbdma.%d", i);
		ret = devm_request_irq(&pdev->dev, irq, tegra_dma_isr, 0,
				       tdc->name, tdc);
		if (ret) {
			dev_err(&pdev->dev,
				"request_irq failed with err %d channel %d\n",
				ret, i);
			goto err_pm_disable;
		}

		tdc->dma_chan.device = &tdma->dma_dev;
		dma_cookie_init(&tdc->dma_chan);
		list_add_tail(&tdc->dma_chan.device_node,
			      &tdma->dma_dev.channels);
		tdc->tdma = tdma;
		tdc->id = i;
		tdc->slave_id = TEGRA_APBDMA_SLAVE_ID_INVALID;

		tasklet_setup(&tdc->tasklet, tegra_dma_tasklet);
		spin_lock_init(&tdc->lock);
		init_waitqueue_head(&tdc->wq);

		INIT_LIST_HEAD(&tdc->pending_sg_req);
		INIT_LIST_HEAD(&tdc->free_sg_req);
		INIT_LIST_HEAD(&tdc->free_dma_desc);
		INIT_LIST_HEAD(&tdc->cb_desc);
	}

	dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask);
	dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask);
	dma_cap_set(DMA_CYCLIC, tdma->dma_dev.cap_mask);

	tdma->global_pause_count = 0;
	tdma->dma_dev.dev = &pdev->dev;
	tdma->dma_dev.device_alloc_chan_resources =
					tegra_dma_alloc_chan_resources;
	tdma->dma_dev.device_free_chan_resources =
					tegra_dma_free_chan_resources;
	tdma->dma_dev.device_prep_slave_sg = tegra_dma_prep_slave_sg;
	tdma->dma_dev.device_prep_dma_cyclic = tegra_dma_prep_dma_cyclic;
	tdma->dma_dev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
		BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
		BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
		BIT(DMA_SLAVE_BUSWIDTH_8_BYTES);
	tdma->dma_dev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
		BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
		BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
		BIT(DMA_SLAVE_BUSWIDTH_8_BYTES);
	tdma->dma_dev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
	tdma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
	tdma->dma_dev.device_config = tegra_dma_slave_config;
	tdma->dma_dev.device_terminate_all = tegra_dma_terminate_all;
	tdma->dma_dev.device_synchronize = tegra_dma_synchronize;
	tdma->dma_dev.device_tx_status = tegra_dma_tx_status;
	tdma->dma_dev.device_issue_pending = tegra_dma_issue_pending;

	ret = dma_async_device_register(&tdma->dma_dev);
	if (ret < 0) {
		dev_err(&pdev->dev,
			"Tegra20 APB DMA driver registration failed %d\n", ret);
		goto err_pm_disable;
	}

	ret = of_dma_controller_register(pdev->dev.of_node,
					 tegra_dma_of_xlate, tdma);
	if (ret < 0) {
		dev_err(&pdev->dev,
			"Tegra20 APB DMA OF registration failed %d\n", ret);
		goto err_unregister_dma_dev;
	}

	dev_info(&pdev->dev, "Tegra20 APB DMA driver registered %u channels\n",
		 cdata->nr_channels);

	return 0;

err_unregister_dma_dev:
	dma_async_device_unregister(&tdma->dma_dev);

err_pm_disable:
	pm_runtime_disable(&pdev->dev);

err_clk_unprepare:
	clk_unprepare(tdma->dma_clk);

	return ret;
}