static int qcom_rpm_probe()

in qcom_rpm.c [529:677]


static int qcom_rpm_probe(struct platform_device *pdev)
{
	const struct of_device_id *match;
	struct device_node *syscon_np;
	struct resource *res;
	struct qcom_rpm *rpm;
	u32 fw_version[3];
	int irq_wakeup;
	int irq_ack;
	int irq_err;
	int ret;

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

	rpm->dev = &pdev->dev;
	mutex_init(&rpm->lock);
	init_completion(&rpm->ack);

	/* Enable message RAM clock */
	rpm->ramclk = devm_clk_get(&pdev->dev, "ram");
	if (IS_ERR(rpm->ramclk)) {
		ret = PTR_ERR(rpm->ramclk);
		if (ret == -EPROBE_DEFER)
			return ret;
		/*
		 * Fall through in all other cases, as the clock is
		 * optional. (Does not exist on all platforms.)
		 */
		rpm->ramclk = NULL;
	}
	clk_prepare_enable(rpm->ramclk); /* Accepts NULL */

	irq_ack = platform_get_irq_byname(pdev, "ack");
	if (irq_ack < 0)
		return irq_ack;

	irq_err = platform_get_irq_byname(pdev, "err");
	if (irq_err < 0)
		return irq_err;

	irq_wakeup = platform_get_irq_byname(pdev, "wakeup");
	if (irq_wakeup < 0)
		return irq_wakeup;

	match = of_match_device(qcom_rpm_of_match, &pdev->dev);
	if (!match)
		return -ENODEV;
	rpm->data = match->data;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	rpm->status_regs = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(rpm->status_regs))
		return PTR_ERR(rpm->status_regs);
	rpm->ctrl_regs = rpm->status_regs + 0x400;
	rpm->req_regs = rpm->status_regs + 0x600;

	syscon_np = of_parse_phandle(pdev->dev.of_node, "qcom,ipc", 0);
	if (!syscon_np) {
		dev_err(&pdev->dev, "no qcom,ipc node\n");
		return -ENODEV;
	}

	rpm->ipc_regmap = syscon_node_to_regmap(syscon_np);
	of_node_put(syscon_np);
	if (IS_ERR(rpm->ipc_regmap))
		return PTR_ERR(rpm->ipc_regmap);

	ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 1,
					 &rpm->ipc_offset);
	if (ret < 0) {
		dev_err(&pdev->dev, "no offset in qcom,ipc\n");
		return -EINVAL;
	}

	ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 2,
					 &rpm->ipc_bit);
	if (ret < 0) {
		dev_err(&pdev->dev, "no bit in qcom,ipc\n");
		return -EINVAL;
	}

	dev_set_drvdata(&pdev->dev, rpm);

	fw_version[0] = readl(RPM_STATUS_REG(rpm, 0));
	fw_version[1] = readl(RPM_STATUS_REG(rpm, 1));
	fw_version[2] = readl(RPM_STATUS_REG(rpm, 2));
	if (fw_version[0] != rpm->data->version) {
		dev_err(&pdev->dev,
			"RPM version %u.%u.%u incompatible with driver version %u",
			fw_version[0],
			fw_version[1],
			fw_version[2],
			rpm->data->version);
		return -EFAULT;
	}

	writel(fw_version[0], RPM_CTRL_REG(rpm, 0));
	writel(fw_version[1], RPM_CTRL_REG(rpm, 1));
	writel(fw_version[2], RPM_CTRL_REG(rpm, 2));

	dev_info(&pdev->dev, "RPM firmware %u.%u.%u\n", fw_version[0],
							fw_version[1],
							fw_version[2]);

	ret = devm_request_irq(&pdev->dev,
			       irq_ack,
			       qcom_rpm_ack_interrupt,
			       IRQF_TRIGGER_RISING,
			       "qcom_rpm_ack",
			       rpm);
	if (ret) {
		dev_err(&pdev->dev, "failed to request ack interrupt\n");
		return ret;
	}

	ret = irq_set_irq_wake(irq_ack, 1);
	if (ret)
		dev_warn(&pdev->dev, "failed to mark ack irq as wakeup\n");

	ret = devm_request_irq(&pdev->dev,
			       irq_err,
			       qcom_rpm_err_interrupt,
			       IRQF_TRIGGER_RISING,
			       "qcom_rpm_err",
			       rpm);
	if (ret) {
		dev_err(&pdev->dev, "failed to request err interrupt\n");
		return ret;
	}

	ret = devm_request_irq(&pdev->dev,
			       irq_wakeup,
			       qcom_rpm_wakeup_interrupt,
			       IRQF_TRIGGER_RISING,
			       "qcom_rpm_wakeup",
			       rpm);
	if (ret) {
		dev_err(&pdev->dev, "failed to request wakeup interrupt\n");
		return ret;
	}

	ret = irq_set_irq_wake(irq_wakeup, 1);
	if (ret)
		dev_warn(&pdev->dev, "failed to mark wakeup irq as wakeup\n");

	return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
}