static int sun6i_msgbox_probe()

in sun6i-msgbox.c [195:288]


static int sun6i_msgbox_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct mbox_chan *chans;
	struct reset_control *reset;
	struct sun6i_msgbox *mbox;
	int i, ret;

	mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
	if (!mbox)
		return -ENOMEM;

	chans = devm_kcalloc(dev, NUM_CHANS, sizeof(*chans), GFP_KERNEL);
	if (!chans)
		return -ENOMEM;

	for (i = 0; i < NUM_CHANS; ++i)
		chans[i].con_priv = mbox;

	mbox->clk = devm_clk_get(dev, NULL);
	if (IS_ERR(mbox->clk)) {
		ret = PTR_ERR(mbox->clk);
		dev_err(dev, "Failed to get clock: %d\n", ret);
		return ret;
	}

	ret = clk_prepare_enable(mbox->clk);
	if (ret) {
		dev_err(dev, "Failed to enable clock: %d\n", ret);
		return ret;
	}

	reset = devm_reset_control_get_exclusive(dev, NULL);
	if (IS_ERR(reset)) {
		ret = PTR_ERR(reset);
		dev_err(dev, "Failed to get reset control: %d\n", ret);
		goto err_disable_unprepare;
	}

	/*
	 * NOTE: We rely on platform firmware to preconfigure the channel
	 * directions, and we share this hardware block with other firmware
	 * that runs concurrently with Linux (e.g. a trusted monitor).
	 *
	 * Therefore, we do *not* assert the reset line if probing fails or
	 * when removing the device.
	 */
	ret = reset_control_deassert(reset);
	if (ret) {
		dev_err(dev, "Failed to deassert reset: %d\n", ret);
		goto err_disable_unprepare;
	}

	mbox->regs = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(mbox->regs)) {
		ret = PTR_ERR(mbox->regs);
		dev_err(dev, "Failed to map MMIO resource: %d\n", ret);
		goto err_disable_unprepare;
	}

	/* Disable all IRQs for this end of the msgbox. */
	writel(0, mbox->regs + LOCAL_IRQ_EN_REG);

	ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),
			       sun6i_msgbox_irq, 0, dev_name(dev), mbox);
	if (ret) {
		dev_err(dev, "Failed to register IRQ handler: %d\n", ret);
		goto err_disable_unprepare;
	}

	mbox->controller.dev           = dev;
	mbox->controller.ops           = &sun6i_msgbox_chan_ops;
	mbox->controller.chans         = chans;
	mbox->controller.num_chans     = NUM_CHANS;
	mbox->controller.txdone_irq    = false;
	mbox->controller.txdone_poll   = true;
	mbox->controller.txpoll_period = 5;

	spin_lock_init(&mbox->lock);
	platform_set_drvdata(pdev, mbox);

	ret = mbox_controller_register(&mbox->controller);
	if (ret) {
		dev_err(dev, "Failed to register controller: %d\n", ret);
		goto err_disable_unprepare;
	}

	return 0;

err_disable_unprepare:
	clk_disable_unprepare(mbox->clk);

	return ret;
}