static int omap_mbox_probe()

in omap-mailbox.c [700:883]


static int omap_mbox_probe(struct platform_device *pdev)
{
	int ret;
	struct mbox_chan *chnls;
	struct omap_mbox **list, *mbox, *mboxblk;
	struct omap_mbox_fifo_info *finfo, *finfoblk;
	struct omap_mbox_device *mdev;
	struct omap_mbox_fifo *fifo;
	struct device_node *node = pdev->dev.of_node;
	struct device_node *child;
	const struct omap_mbox_match_data *match_data;
	u32 intr_type, info_count;
	u32 num_users, num_fifos;
	u32 tmp[3];
	u32 l;
	int i;

	if (!node) {
		pr_err("%s: only DT-based devices are supported\n", __func__);
		return -ENODEV;
	}

	match_data = of_device_get_match_data(&pdev->dev);
	if (!match_data)
		return -ENODEV;
	intr_type = match_data->intr_type;

	if (of_property_read_u32(node, "ti,mbox-num-users", &num_users))
		return -ENODEV;

	if (of_property_read_u32(node, "ti,mbox-num-fifos", &num_fifos))
		return -ENODEV;

	info_count = of_get_available_child_count(node);
	if (!info_count) {
		dev_err(&pdev->dev, "no available mbox devices found\n");
		return -ENODEV;
	}

	finfoblk = devm_kcalloc(&pdev->dev, info_count, sizeof(*finfoblk),
				GFP_KERNEL);
	if (!finfoblk)
		return -ENOMEM;

	finfo = finfoblk;
	child = NULL;
	for (i = 0; i < info_count; i++, finfo++) {
		child = of_get_next_available_child(node, child);
		ret = of_property_read_u32_array(child, "ti,mbox-tx", tmp,
						 ARRAY_SIZE(tmp));
		if (ret)
			return ret;
		finfo->tx_id = tmp[0];
		finfo->tx_irq = tmp[1];
		finfo->tx_usr = tmp[2];

		ret = of_property_read_u32_array(child, "ti,mbox-rx", tmp,
						 ARRAY_SIZE(tmp));
		if (ret)
			return ret;
		finfo->rx_id = tmp[0];
		finfo->rx_irq = tmp[1];
		finfo->rx_usr = tmp[2];

		finfo->name = child->name;

		if (of_find_property(child, "ti,mbox-send-noirq", NULL))
			finfo->send_no_irq = true;

		if (finfo->tx_id >= num_fifos || finfo->rx_id >= num_fifos ||
		    finfo->tx_usr >= num_users || finfo->rx_usr >= num_users)
			return -EINVAL;
	}

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

	mdev->mbox_base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(mdev->mbox_base))
		return PTR_ERR(mdev->mbox_base);

	mdev->irq_ctx = devm_kcalloc(&pdev->dev, num_users, sizeof(u32),
				     GFP_KERNEL);
	if (!mdev->irq_ctx)
		return -ENOMEM;

	/* allocate one extra for marking end of list */
	list = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*list),
			    GFP_KERNEL);
	if (!list)
		return -ENOMEM;

	chnls = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*chnls),
			     GFP_KERNEL);
	if (!chnls)
		return -ENOMEM;

	mboxblk = devm_kcalloc(&pdev->dev, info_count, sizeof(*mbox),
			       GFP_KERNEL);
	if (!mboxblk)
		return -ENOMEM;

	mbox = mboxblk;
	finfo = finfoblk;
	for (i = 0; i < info_count; i++, finfo++) {
		fifo = &mbox->tx_fifo;
		fifo->msg = MAILBOX_MESSAGE(finfo->tx_id);
		fifo->fifo_stat = MAILBOX_FIFOSTATUS(finfo->tx_id);
		fifo->intr_bit = MAILBOX_IRQ_NOTFULL(finfo->tx_id);
		fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->tx_usr);
		fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->tx_usr);
		fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->tx_usr);

		fifo = &mbox->rx_fifo;
		fifo->msg = MAILBOX_MESSAGE(finfo->rx_id);
		fifo->msg_stat =  MAILBOX_MSGSTATUS(finfo->rx_id);
		fifo->intr_bit = MAILBOX_IRQ_NEWMSG(finfo->rx_id);
		fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->rx_usr);
		fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->rx_usr);
		fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->rx_usr);

		mbox->send_no_irq = finfo->send_no_irq;
		mbox->intr_type = intr_type;

		mbox->parent = mdev;
		mbox->name = finfo->name;
		mbox->irq = platform_get_irq(pdev, finfo->tx_irq);
		if (mbox->irq < 0)
			return mbox->irq;
		mbox->chan = &chnls[i];
		chnls[i].con_priv = mbox;
		list[i] = mbox++;
	}

	mutex_init(&mdev->cfg_lock);
	mdev->dev = &pdev->dev;
	mdev->num_users = num_users;
	mdev->num_fifos = num_fifos;
	mdev->intr_type = intr_type;
	mdev->mboxes = list;

	/*
	 * OMAP/K3 Mailbox IP does not have a Tx-Done IRQ, but rather a Tx-Ready
	 * IRQ and is needed to run the Tx state machine
	 */
	mdev->controller.txdone_irq = true;
	mdev->controller.dev = mdev->dev;
	mdev->controller.ops = &omap_mbox_chan_ops;
	mdev->controller.chans = chnls;
	mdev->controller.num_chans = info_count;
	mdev->controller.of_xlate = omap_mbox_of_xlate;
	ret = omap_mbox_register(mdev);
	if (ret)
		return ret;

	platform_set_drvdata(pdev, mdev);
	pm_runtime_enable(mdev->dev);

	ret = pm_runtime_get_sync(mdev->dev);
	if (ret < 0) {
		pm_runtime_put_noidle(mdev->dev);
		goto unregister;
	}

	/*
	 * just print the raw revision register, the format is not
	 * uniform across all SoCs
	 */
	l = mbox_read_reg(mdev, MAILBOX_REVISION);
	dev_info(mdev->dev, "omap mailbox rev 0x%x\n", l);

	ret = pm_runtime_put_sync(mdev->dev);
	if (ret < 0 && ret != -ENOSYS)
		goto unregister;

	devm_kfree(&pdev->dev, finfoblk);
	return 0;

unregister:
	pm_runtime_disable(mdev->dev);
	omap_mbox_unregister(mdev);
	return ret;
}