static int s3c24xx_dma_probe()

in s3c24xx-dma.c [1185:1363]


static int s3c24xx_dma_probe(struct platform_device *pdev)
{
	const struct s3c24xx_dma_platdata *pdata = dev_get_platdata(&pdev->dev);
	struct s3c24xx_dma_engine *s3cdma;
	struct soc_data *sdata;
	struct resource *res;
	int ret;
	int i;

	if (!pdata) {
		dev_err(&pdev->dev, "platform data missing\n");
		return -ENODEV;
	}

	/* Basic sanity check */
	if (pdata->num_phy_channels > MAX_DMA_CHANNELS) {
		dev_err(&pdev->dev, "too many dma channels %d, max %d\n",
			pdata->num_phy_channels, MAX_DMA_CHANNELS);
		return -EINVAL;
	}

	sdata = s3c24xx_dma_get_soc_data(pdev);
	if (!sdata)
		return -EINVAL;

	s3cdma = devm_kzalloc(&pdev->dev, sizeof(*s3cdma), GFP_KERNEL);
	if (!s3cdma)
		return -ENOMEM;

	s3cdma->pdev = pdev;
	s3cdma->pdata = pdata;
	s3cdma->sdata = sdata;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	s3cdma->base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(s3cdma->base))
		return PTR_ERR(s3cdma->base);

	s3cdma->phy_chans = devm_kcalloc(&pdev->dev,
					      pdata->num_phy_channels,
					      sizeof(struct s3c24xx_dma_phy),
					      GFP_KERNEL);
	if (!s3cdma->phy_chans)
		return -ENOMEM;

	/* acquire irqs and clocks for all physical channels */
	for (i = 0; i < pdata->num_phy_channels; i++) {
		struct s3c24xx_dma_phy *phy = &s3cdma->phy_chans[i];
		char clk_name[6];

		phy->id = i;
		phy->base = s3cdma->base + (i * sdata->stride);
		phy->host = s3cdma;

		phy->irq = platform_get_irq(pdev, i);
		if (phy->irq < 0)
			continue;

		ret = devm_request_irq(&pdev->dev, phy->irq, s3c24xx_dma_irq,
				       0, pdev->name, phy);
		if (ret) {
			dev_err(&pdev->dev, "Unable to request irq for channel %d, error %d\n",
				i, ret);
			continue;
		}

		if (sdata->has_clocks) {
			sprintf(clk_name, "dma.%d", i);
			phy->clk = devm_clk_get(&pdev->dev, clk_name);
			if (IS_ERR(phy->clk) && sdata->has_clocks) {
				dev_err(&pdev->dev, "unable to acquire clock for channel %d, error %lu\n",
					i, PTR_ERR(phy->clk));
				continue;
			}

			ret = clk_prepare(phy->clk);
			if (ret) {
				dev_err(&pdev->dev, "clock for phy %d failed, error %d\n",
					i, ret);
				continue;
			}
		}

		spin_lock_init(&phy->lock);
		phy->valid = true;

		dev_dbg(&pdev->dev, "physical channel %d is %s\n",
			i, s3c24xx_dma_phy_busy(phy) ? "BUSY" : "FREE");
	}

	/* Initialize memcpy engine */
	dma_cap_set(DMA_MEMCPY, s3cdma->memcpy.cap_mask);
	dma_cap_set(DMA_PRIVATE, s3cdma->memcpy.cap_mask);
	s3cdma->memcpy.dev = &pdev->dev;
	s3cdma->memcpy.device_free_chan_resources =
					s3c24xx_dma_free_chan_resources;
	s3cdma->memcpy.device_prep_dma_memcpy = s3c24xx_dma_prep_memcpy;
	s3cdma->memcpy.device_tx_status = s3c24xx_dma_tx_status;
	s3cdma->memcpy.device_issue_pending = s3c24xx_dma_issue_pending;
	s3cdma->memcpy.device_config = s3c24xx_dma_set_runtime_config;
	s3cdma->memcpy.device_terminate_all = s3c24xx_dma_terminate_all;
	s3cdma->memcpy.device_synchronize = s3c24xx_dma_synchronize;

	/* Initialize slave engine for SoC internal dedicated peripherals */
	dma_cap_set(DMA_SLAVE, s3cdma->slave.cap_mask);
	dma_cap_set(DMA_CYCLIC, s3cdma->slave.cap_mask);
	dma_cap_set(DMA_PRIVATE, s3cdma->slave.cap_mask);
	s3cdma->slave.dev = &pdev->dev;
	s3cdma->slave.device_free_chan_resources =
					s3c24xx_dma_free_chan_resources;
	s3cdma->slave.device_tx_status = s3c24xx_dma_tx_status;
	s3cdma->slave.device_issue_pending = s3c24xx_dma_issue_pending;
	s3cdma->slave.device_prep_slave_sg = s3c24xx_dma_prep_slave_sg;
	s3cdma->slave.device_prep_dma_cyclic = s3c24xx_dma_prep_dma_cyclic;
	s3cdma->slave.device_config = s3c24xx_dma_set_runtime_config;
	s3cdma->slave.device_terminate_all = s3c24xx_dma_terminate_all;
	s3cdma->slave.device_synchronize = s3c24xx_dma_synchronize;
	s3cdma->slave.filter.map = pdata->slave_map;
	s3cdma->slave.filter.mapcnt = pdata->slavecnt;
	s3cdma->slave.filter.fn = s3c24xx_dma_filter;

	/* Register as many memcpy channels as there are physical channels */
	ret = s3c24xx_dma_init_virtual_channels(s3cdma, &s3cdma->memcpy,
						pdata->num_phy_channels, false);
	if (ret <= 0) {
		dev_warn(&pdev->dev,
			 "%s failed to enumerate memcpy channels - %d\n",
			 __func__, ret);
		goto err_memcpy;
	}

	/* Register slave channels */
	ret = s3c24xx_dma_init_virtual_channels(s3cdma, &s3cdma->slave,
				pdata->num_channels, true);
	if (ret <= 0) {
		dev_warn(&pdev->dev,
			"%s failed to enumerate slave channels - %d\n",
				__func__, ret);
		goto err_slave;
	}

	ret = dma_async_device_register(&s3cdma->memcpy);
	if (ret) {
		dev_warn(&pdev->dev,
			"%s failed to register memcpy as an async device - %d\n",
			__func__, ret);
		goto err_memcpy_reg;
	}

	ret = dma_async_device_register(&s3cdma->slave);
	if (ret) {
		dev_warn(&pdev->dev,
			"%s failed to register slave as an async device - %d\n",
			__func__, ret);
		goto err_slave_reg;
	}

	platform_set_drvdata(pdev, s3cdma);
	dev_info(&pdev->dev, "Loaded dma driver with %d physical channels\n",
		 pdata->num_phy_channels);

	return 0;

err_slave_reg:
	dma_async_device_unregister(&s3cdma->memcpy);
err_memcpy_reg:
	s3c24xx_dma_free_virtual_channels(&s3cdma->slave);
err_slave:
	s3c24xx_dma_free_virtual_channels(&s3cdma->memcpy);
err_memcpy:
	if (sdata->has_clocks)
		for (i = 0; i < pdata->num_phy_channels; i++) {
			struct s3c24xx_dma_phy *phy = &s3cdma->phy_chans[i];
			if (phy->valid)
				clk_unprepare(phy->clk);
		}

	return ret;
}