static int ppc440spe_adma_probe()

in ppc4xx/adma.c [4004:4228]


static int ppc440spe_adma_probe(struct platform_device *ofdev)
{
	struct device_node *np = ofdev->dev.of_node;
	struct resource res;
	struct ppc440spe_adma_device *adev;
	struct ppc440spe_adma_chan *chan;
	struct ppc_dma_chan_ref *ref, *_ref;
	int ret = 0, initcode = PPC_ADMA_INIT_OK;
	const u32 *idx;
	int len;
	void *regs;
	u32 id, pool_size;

	if (of_device_is_compatible(np, "amcc,xor-accelerator")) {
		id = PPC440SPE_XOR_ID;
		/* As far as the XOR engine is concerned, it does not
		 * use FIFOs but uses linked list. So there is no dependency
		 * between pool size to allocate and the engine configuration.
		 */
		pool_size = PAGE_SIZE << 1;
	} else {
		/* it is DMA0 or DMA1 */
		idx = of_get_property(np, "cell-index", &len);
		if (!idx || (len != sizeof(u32))) {
			dev_err(&ofdev->dev, "Device node %pOF has missing "
				"or invalid cell-index property\n",
				np);
			return -EINVAL;
		}
		id = *idx;
		/* DMA0,1 engines use FIFO to maintain CDBs, so we
		 * should allocate the pool accordingly to size of this
		 * FIFO. Thus, the pool size depends on the FIFO depth:
		 * how much CDBs pointers the FIFO may contain then so
		 * much CDBs we should provide in the pool.
		 * That is
		 *   CDB size = 32B;
		 *   CDBs number = (DMA0_FIFO_SIZE >> 3);
		 *   Pool size = CDBs number * CDB size =
		 *      = (DMA0_FIFO_SIZE >> 3) << 5 = DMA0_FIFO_SIZE << 2.
		 */
		pool_size = (id == PPC440SPE_DMA0_ID) ?
			    DMA0_FIFO_SIZE : DMA1_FIFO_SIZE;
		pool_size <<= 2;
	}

	if (of_address_to_resource(np, 0, &res)) {
		dev_err(&ofdev->dev, "failed to get memory resource\n");
		initcode = PPC_ADMA_INIT_MEMRES;
		ret = -ENODEV;
		goto out;
	}

	if (!request_mem_region(res.start, resource_size(&res),
				dev_driver_string(&ofdev->dev))) {
		dev_err(&ofdev->dev, "failed to request memory region %pR\n",
			&res);
		initcode = PPC_ADMA_INIT_MEMREG;
		ret = -EBUSY;
		goto out;
	}

	/* create a device */
	adev = kzalloc(sizeof(*adev), GFP_KERNEL);
	if (!adev) {
		initcode = PPC_ADMA_INIT_ALLOC;
		ret = -ENOMEM;
		goto err_adev_alloc;
	}

	adev->id = id;
	adev->pool_size = pool_size;
	/* allocate coherent memory for hardware descriptors */
	adev->dma_desc_pool_virt = dma_alloc_coherent(&ofdev->dev,
					adev->pool_size, &adev->dma_desc_pool,
					GFP_KERNEL);
	if (adev->dma_desc_pool_virt == NULL) {
		dev_err(&ofdev->dev, "failed to allocate %d bytes of coherent "
			"memory for hardware descriptors\n",
			adev->pool_size);
		initcode = PPC_ADMA_INIT_COHERENT;
		ret = -ENOMEM;
		goto err_dma_alloc;
	}
	dev_dbg(&ofdev->dev, "allocated descriptor pool virt 0x%p phys 0x%llx\n",
		adev->dma_desc_pool_virt, (u64)adev->dma_desc_pool);

	regs = ioremap(res.start, resource_size(&res));
	if (!regs) {
		dev_err(&ofdev->dev, "failed to ioremap regs!\n");
		ret = -ENOMEM;
		goto err_regs_alloc;
	}

	if (adev->id == PPC440SPE_XOR_ID) {
		adev->xor_reg = regs;
		/* Reset XOR */
		iowrite32be(XOR_CRSR_XASR_BIT, &adev->xor_reg->crsr);
		iowrite32be(XOR_CRSR_64BA_BIT, &adev->xor_reg->crrr);
	} else {
		size_t fifo_size = (adev->id == PPC440SPE_DMA0_ID) ?
				   DMA0_FIFO_SIZE : DMA1_FIFO_SIZE;
		adev->dma_reg = regs;
		/* DMAx_FIFO_SIZE is defined in bytes,
		 * <fsiz> - is defined in number of CDB pointers (8byte).
		 * DMA FIFO Length = CSlength + CPlength, where
		 * CSlength = CPlength = (fsiz + 1) * 8.
		 */
		iowrite32(DMA_FIFO_ENABLE | ((fifo_size >> 3) - 2),
			  &adev->dma_reg->fsiz);
		/* Configure DMA engine */
		iowrite32(DMA_CFG_DXEPR_HP | DMA_CFG_DFMPP_HP | DMA_CFG_FALGN,
			  &adev->dma_reg->cfg);
		/* Clear Status */
		iowrite32(~0, &adev->dma_reg->dsts);
	}

	adev->dev = &ofdev->dev;
	adev->common.dev = &ofdev->dev;
	INIT_LIST_HEAD(&adev->common.channels);
	platform_set_drvdata(ofdev, adev);

	/* create a channel */
	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
	if (!chan) {
		initcode = PPC_ADMA_INIT_CHANNEL;
		ret = -ENOMEM;
		goto err_chan_alloc;
	}

	spin_lock_init(&chan->lock);
	INIT_LIST_HEAD(&chan->chain);
	INIT_LIST_HEAD(&chan->all_slots);
	chan->device = adev;
	chan->common.device = &adev->common;
	dma_cookie_init(&chan->common);
	list_add_tail(&chan->common.device_node, &adev->common.channels);
	tasklet_setup(&chan->irq_tasklet, ppc440spe_adma_tasklet);

	/* allocate and map helper pages for async validation or
	 * async_mult/async_sum_product operations on DMA0/1.
	 */
	if (adev->id != PPC440SPE_XOR_ID) {
		chan->pdest_page = alloc_page(GFP_KERNEL);
		chan->qdest_page = alloc_page(GFP_KERNEL);
		if (!chan->pdest_page ||
		    !chan->qdest_page) {
			if (chan->pdest_page)
				__free_page(chan->pdest_page);
			if (chan->qdest_page)
				__free_page(chan->qdest_page);
			ret = -ENOMEM;
			goto err_page_alloc;
		}
		chan->pdest = dma_map_page(&ofdev->dev, chan->pdest_page, 0,
					   PAGE_SIZE, DMA_BIDIRECTIONAL);
		chan->qdest = dma_map_page(&ofdev->dev, chan->qdest_page, 0,
					   PAGE_SIZE, DMA_BIDIRECTIONAL);
	}

	ref = kmalloc(sizeof(*ref), GFP_KERNEL);
	if (ref) {
		ref->chan = &chan->common;
		INIT_LIST_HEAD(&ref->node);
		list_add_tail(&ref->node, &ppc440spe_adma_chan_list);
	} else {
		dev_err(&ofdev->dev, "failed to allocate channel reference!\n");
		ret = -ENOMEM;
		goto err_ref_alloc;
	}

	ret = ppc440spe_adma_setup_irqs(adev, chan, &initcode);
	if (ret)
		goto err_irq;

	ppc440spe_adma_init_capabilities(adev);

	ret = dma_async_device_register(&adev->common);
	if (ret) {
		initcode = PPC_ADMA_INIT_REGISTER;
		dev_err(&ofdev->dev, "failed to register dma device\n");
		goto err_dev_reg;
	}

	goto out;

err_dev_reg:
	ppc440spe_adma_release_irqs(adev, chan);
err_irq:
	list_for_each_entry_safe(ref, _ref, &ppc440spe_adma_chan_list, node) {
		if (chan == to_ppc440spe_adma_chan(ref->chan)) {
			list_del(&ref->node);
			kfree(ref);
		}
	}
err_ref_alloc:
	if (adev->id != PPC440SPE_XOR_ID) {
		dma_unmap_page(&ofdev->dev, chan->pdest,
			       PAGE_SIZE, DMA_BIDIRECTIONAL);
		dma_unmap_page(&ofdev->dev, chan->qdest,
			       PAGE_SIZE, DMA_BIDIRECTIONAL);
		__free_page(chan->pdest_page);
		__free_page(chan->qdest_page);
	}
err_page_alloc:
	kfree(chan);
err_chan_alloc:
	if (adev->id == PPC440SPE_XOR_ID)
		iounmap(adev->xor_reg);
	else
		iounmap(adev->dma_reg);
err_regs_alloc:
	dma_free_coherent(adev->dev, adev->pool_size,
			  adev->dma_desc_pool_virt,
			  adev->dma_desc_pool);
err_dma_alloc:
	kfree(adev);
err_adev_alloc:
	release_mem_region(res.start, resource_size(&res));
out:
	if (id < PPC440SPE_ADMA_ENGINES_NUM)
		ppc440spe_adma_devices[id] = initcode;

	return ret;
}