diff options
Diffstat (limited to 'drivers/mailbox/qcom-ipcc.c')
| -rw-r--r-- | drivers/mailbox/qcom-ipcc.c | 99 | 
1 files changed, 76 insertions, 23 deletions
| diff --git a/drivers/mailbox/qcom-ipcc.c b/drivers/mailbox/qcom-ipcc.c index f1d4f4679b17..c5d963222014 100644 --- a/drivers/mailbox/qcom-ipcc.c +++ b/drivers/mailbox/qcom-ipcc.c @@ -13,8 +13,6 @@  #include <dt-bindings/mailbox/qcom-ipcc.h> -#define IPCC_MBOX_MAX_CHAN		48 -  /* IPCC Register offsets */  #define IPCC_REG_SEND_ID		0x0c  #define IPCC_REG_RECV_ID		0x10 @@ -52,9 +50,10 @@ struct qcom_ipcc {  	struct device *dev;  	void __iomem *base;  	struct irq_domain *irq_domain; -	struct mbox_chan chan[IPCC_MBOX_MAX_CHAN]; -	struct qcom_ipcc_chan_info mchan[IPCC_MBOX_MAX_CHAN]; +	struct mbox_chan *chans; +	struct qcom_ipcc_chan_info *mchan;  	struct mbox_controller mbox; +	int num_chans;  	int irq;  }; @@ -166,25 +165,37 @@ static struct mbox_chan *qcom_ipcc_mbox_xlate(struct mbox_controller *mbox,  	struct qcom_ipcc *ipcc = to_qcom_ipcc(mbox);  	struct qcom_ipcc_chan_info *mchan;  	struct mbox_chan *chan; -	unsigned int i; +	struct device *dev; +	int chan_id; + +	dev = ipcc->dev;  	if (ph->args_count != 2)  		return ERR_PTR(-EINVAL); -	for (i = 0; i < IPCC_MBOX_MAX_CHAN; i++) { -		chan = &ipcc->chan[i]; -		if (!chan->con_priv) { -			mchan = &ipcc->mchan[i]; -			mchan->client_id = ph->args[0]; -			mchan->signal_id = ph->args[1]; -			chan->con_priv = mchan; -			break; -		} +	for (chan_id = 0; chan_id < mbox->num_chans; chan_id++) { +		chan = &ipcc->chans[chan_id]; +		mchan = chan->con_priv; -		chan = NULL; +		if (!mchan) +			break; +		else if (mchan->client_id == ph->args[0] && +				mchan->signal_id == ph->args[1]) +			return ERR_PTR(-EBUSY);  	} -	return chan ?: ERR_PTR(-EBUSY); +	if (chan_id >= mbox->num_chans) +		return ERR_PTR(-EBUSY); + +	mchan = devm_kzalloc(dev, sizeof(*mchan), GFP_KERNEL); +	if (!mchan) +		return ERR_PTR(-ENOMEM); + +	mchan->client_id = ph->args[0]; +	mchan->signal_id = ph->args[1]; +	chan->con_priv = mchan; + +	return chan;  }  static const struct mbox_chan_ops ipcc_mbox_chan_ops = { @@ -192,15 +203,49 @@ static const struct mbox_chan_ops ipcc_mbox_chan_ops = {  	.shutdown = qcom_ipcc_mbox_shutdown,  }; -static int qcom_ipcc_setup_mbox(struct qcom_ipcc *ipcc) +static int qcom_ipcc_setup_mbox(struct qcom_ipcc *ipcc, +				struct device_node *controller_dn)  { +	struct of_phandle_args curr_ph; +	struct device_node *client_dn;  	struct mbox_controller *mbox;  	struct device *dev = ipcc->dev; +	int i, j, ret; + +	/* +	 * Find out the number of clients interested in this mailbox +	 * and create channels accordingly. +	 */ +	ipcc->num_chans = 0; +	for_each_node_with_property(client_dn, "mboxes") { +		if (!of_device_is_available(client_dn)) +			continue; +		i = of_count_phandle_with_args(client_dn, +						"mboxes", "#mbox-cells"); +		for (j = 0; j < i; j++) { +			ret = of_parse_phandle_with_args(client_dn, "mboxes", +						"#mbox-cells", j, &curr_ph); +			of_node_put(curr_ph.np); +			if (!ret && curr_ph.np == controller_dn) { +				ipcc->num_chans++; +				break; +			} +		} +	} + +	/* If no clients are found, skip registering as a mbox controller */ +	if (!ipcc->num_chans) +		return 0; + +	ipcc->chans = devm_kcalloc(dev, ipcc->num_chans, +					sizeof(struct mbox_chan), GFP_KERNEL); +	if (!ipcc->chans) +		return -ENOMEM;  	mbox = &ipcc->mbox;  	mbox->dev = dev; -	mbox->num_chans = IPCC_MBOX_MAX_CHAN; -	mbox->chans = ipcc->chan; +	mbox->num_chans = ipcc->num_chans; +	mbox->chans = ipcc->chans;  	mbox->ops = &ipcc_mbox_chan_ops;  	mbox->of_xlate = qcom_ipcc_mbox_xlate;  	mbox->txdone_irq = false; @@ -212,6 +257,8 @@ static int qcom_ipcc_setup_mbox(struct qcom_ipcc *ipcc)  static int qcom_ipcc_probe(struct platform_device *pdev)  {  	struct qcom_ipcc *ipcc; +	static int id; +	char *name;  	int ret;  	ipcc = devm_kzalloc(&pdev->dev, sizeof(*ipcc), GFP_KERNEL); @@ -228,27 +275,33 @@ static int qcom_ipcc_probe(struct platform_device *pdev)  	if (ipcc->irq < 0)  		return ipcc->irq; +	name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ipcc_%d", id++); +	if (!name) +		return -ENOMEM; +  	ipcc->irq_domain = irq_domain_add_tree(pdev->dev.of_node,  					       &qcom_ipcc_irq_ops, ipcc);  	if (!ipcc->irq_domain)  		return -ENOMEM; -	ret = qcom_ipcc_setup_mbox(ipcc); +	ret = qcom_ipcc_setup_mbox(ipcc, pdev->dev.of_node);  	if (ret)  		goto err_mbox;  	ret = devm_request_irq(&pdev->dev, ipcc->irq, qcom_ipcc_irq_fn, -			       IRQF_TRIGGER_HIGH, "ipcc", ipcc); +			       IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, name, ipcc);  	if (ret < 0) {  		dev_err(&pdev->dev, "Failed to register the irq: %d\n", ret); -		goto err_mbox; +		goto err_req_irq;  	} -	enable_irq_wake(ipcc->irq);  	platform_set_drvdata(pdev, ipcc);  	return 0; +err_req_irq: +	if (ipcc->num_chans) +		mbox_controller_unregister(&ipcc->mbox);  err_mbox:  	irq_domain_remove(ipcc->irq_domain); |