static int stm32_rtc_probe()

in rtc-stm32.c [692:847]


static int stm32_rtc_probe(struct platform_device *pdev)
{
	struct stm32_rtc *rtc;
	const struct stm32_rtc_registers *regs;
	int ret;

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

	rtc->base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(rtc->base))
		return PTR_ERR(rtc->base);

	rtc->data = (struct stm32_rtc_data *)
		    of_device_get_match_data(&pdev->dev);
	regs = &rtc->data->regs;

	if (rtc->data->need_dbp) {
		rtc->dbp = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
							   "st,syscfg");
		if (IS_ERR(rtc->dbp)) {
			dev_err(&pdev->dev, "no st,syscfg\n");
			return PTR_ERR(rtc->dbp);
		}

		ret = of_property_read_u32_index(pdev->dev.of_node, "st,syscfg",
						 1, &rtc->dbp_reg);
		if (ret) {
			dev_err(&pdev->dev, "can't read DBP register offset\n");
			return ret;
		}

		ret = of_property_read_u32_index(pdev->dev.of_node, "st,syscfg",
						 2, &rtc->dbp_mask);
		if (ret) {
			dev_err(&pdev->dev, "can't read DBP register mask\n");
			return ret;
		}
	}

	if (!rtc->data->has_pclk) {
		rtc->pclk = NULL;
		rtc->rtc_ck = devm_clk_get(&pdev->dev, NULL);
	} else {
		rtc->pclk = devm_clk_get(&pdev->dev, "pclk");
		if (IS_ERR(rtc->pclk)) {
			dev_err(&pdev->dev, "no pclk clock");
			return PTR_ERR(rtc->pclk);
		}
		rtc->rtc_ck = devm_clk_get(&pdev->dev, "rtc_ck");
	}
	if (IS_ERR(rtc->rtc_ck)) {
		dev_err(&pdev->dev, "no rtc_ck clock");
		return PTR_ERR(rtc->rtc_ck);
	}

	if (rtc->data->has_pclk) {
		ret = clk_prepare_enable(rtc->pclk);
		if (ret)
			return ret;
	}

	ret = clk_prepare_enable(rtc->rtc_ck);
	if (ret)
		goto err_no_rtc_ck;

	if (rtc->data->need_dbp)
		regmap_update_bits(rtc->dbp, rtc->dbp_reg,
				   rtc->dbp_mask, rtc->dbp_mask);

	/*
	 * After a system reset, RTC_ISR.INITS flag can be read to check if
	 * the calendar has been initialized or not. INITS flag is reset by a
	 * power-on reset (no vbat, no power-supply). It is not reset if
	 * rtc_ck parent clock has changed (so RTC prescalers need to be
	 * changed). That's why we cannot rely on this flag to know if RTC
	 * init has to be done.
	 */
	ret = stm32_rtc_init(pdev, rtc);
	if (ret)
		goto err;

	rtc->irq_alarm = platform_get_irq(pdev, 0);
	if (rtc->irq_alarm <= 0) {
		ret = rtc->irq_alarm;
		goto err;
	}

	ret = device_init_wakeup(&pdev->dev, true);
	if (rtc->data->has_wakeirq) {
		rtc->wakeirq_alarm = platform_get_irq(pdev, 1);
		if (rtc->wakeirq_alarm > 0) {
			ret = dev_pm_set_dedicated_wake_irq(&pdev->dev,
							    rtc->wakeirq_alarm);
		} else {
			ret = rtc->wakeirq_alarm;
			if (rtc->wakeirq_alarm == -EPROBE_DEFER)
				goto err;
		}
	}
	if (ret)
		dev_warn(&pdev->dev, "alarm can't wake up the system: %d", ret);

	platform_set_drvdata(pdev, rtc);

	rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
						&stm32_rtc_ops, THIS_MODULE);
	if (IS_ERR(rtc->rtc_dev)) {
		ret = PTR_ERR(rtc->rtc_dev);
		dev_err(&pdev->dev, "rtc device registration failed, err=%d\n",
			ret);
		goto err;
	}

	/* Handle RTC alarm interrupts */
	ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_alarm, NULL,
					stm32_rtc_alarm_irq, IRQF_ONESHOT,
					pdev->name, rtc);
	if (ret) {
		dev_err(&pdev->dev, "IRQ%d (alarm interrupt) already claimed\n",
			rtc->irq_alarm);
		goto err;
	}

	/*
	 * If INITS flag is reset (calendar year field set to 0x00), calendar
	 * must be initialized
	 */
	if (!(readl_relaxed(rtc->base + regs->isr) & STM32_RTC_ISR_INITS))
		dev_warn(&pdev->dev, "Date/Time must be initialized\n");

	if (regs->verr != UNDEF_REG) {
		u32 ver = readl_relaxed(rtc->base + regs->verr);

		dev_info(&pdev->dev, "registered rev:%d.%d\n",
			 (ver >> STM32_RTC_VERR_MAJREV_SHIFT) & 0xF,
			 (ver >> STM32_RTC_VERR_MINREV_SHIFT) & 0xF);
	}

	return 0;

err:
	clk_disable_unprepare(rtc->rtc_ck);
err_no_rtc_ck:
	if (rtc->data->has_pclk)
		clk_disable_unprepare(rtc->pclk);

	if (rtc->data->need_dbp)
		regmap_update_bits(rtc->dbp, rtc->dbp_reg, rtc->dbp_mask, 0);

	dev_pm_clear_wake_irq(&pdev->dev);
	device_init_wakeup(&pdev->dev, false);

	return ret;
}