18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Power off through MediaTek PMIC
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2018 MediaTek Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Sean Wang <sean.wang@mediatek.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <linux/mfd/mt6397/core.h>
168c2ecf20Sopenharmony_ci#include <linux/mfd/mt6397/rtc.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistruct mt6323_pwrc {
198c2ecf20Sopenharmony_ci	struct device *dev;
208c2ecf20Sopenharmony_ci	struct regmap *regmap;
218c2ecf20Sopenharmony_ci	u32 base;
228c2ecf20Sopenharmony_ci};
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic struct mt6323_pwrc *mt_pwrc;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic void mt6323_do_pwroff(void)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct mt6323_pwrc *pwrc = mt_pwrc;
298c2ecf20Sopenharmony_ci	unsigned int val;
308c2ecf20Sopenharmony_ci	int ret;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	regmap_write(pwrc->regmap, pwrc->base + RTC_BBPU, RTC_BBPU_KEY);
338c2ecf20Sopenharmony_ci	regmap_write(pwrc->regmap, pwrc->base + RTC_WRTGR_MT6323, 1);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	ret = regmap_read_poll_timeout(pwrc->regmap,
368c2ecf20Sopenharmony_ci					pwrc->base + RTC_BBPU, val,
378c2ecf20Sopenharmony_ci					!(val & RTC_BBPU_CBUSY),
388c2ecf20Sopenharmony_ci					MTK_RTC_POLL_DELAY_US,
398c2ecf20Sopenharmony_ci					MTK_RTC_POLL_TIMEOUT);
408c2ecf20Sopenharmony_ci	if (ret)
418c2ecf20Sopenharmony_ci		dev_err(pwrc->dev, "failed to write BBPU: %d\n", ret);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* Wait some time until system down, otherwise, notice with a warn */
448c2ecf20Sopenharmony_ci	mdelay(1000);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	WARN_ONCE(1, "Unable to power off system\n");
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic int mt6323_pwrc_probe(struct platform_device *pdev)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent);
528c2ecf20Sopenharmony_ci	struct mt6323_pwrc *pwrc;
538c2ecf20Sopenharmony_ci	struct resource *res;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL);
568c2ecf20Sopenharmony_ci	if (!pwrc)
578c2ecf20Sopenharmony_ci		return -ENOMEM;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
608c2ecf20Sopenharmony_ci	if (!res)
618c2ecf20Sopenharmony_ci		return -EINVAL;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	pwrc->base = res->start;
648c2ecf20Sopenharmony_ci	pwrc->regmap = mt6397_chip->regmap;
658c2ecf20Sopenharmony_ci	pwrc->dev = &pdev->dev;
668c2ecf20Sopenharmony_ci	mt_pwrc = pwrc;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	pm_power_off = &mt6323_do_pwroff;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return 0;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic int mt6323_pwrc_remove(struct platform_device *pdev)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	if (pm_power_off == &mt6323_do_pwroff)
768c2ecf20Sopenharmony_ci		pm_power_off = NULL;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return 0;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic const struct of_device_id mt6323_pwrc_dt_match[] = {
828c2ecf20Sopenharmony_ci	{ .compatible = "mediatek,mt6323-pwrc" },
838c2ecf20Sopenharmony_ci	{},
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mt6323_pwrc_dt_match);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic struct platform_driver mt6323_pwrc_driver = {
888c2ecf20Sopenharmony_ci	.probe          = mt6323_pwrc_probe,
898c2ecf20Sopenharmony_ci	.remove         = mt6323_pwrc_remove,
908c2ecf20Sopenharmony_ci	.driver         = {
918c2ecf20Sopenharmony_ci		.name   = "mt6323-pwrc",
928c2ecf20Sopenharmony_ci		.of_match_table = mt6323_pwrc_dt_match,
938c2ecf20Sopenharmony_ci	},
948c2ecf20Sopenharmony_ci};
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cimodule_platform_driver(mt6323_pwrc_driver);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Poweroff driver for MT6323 PMIC");
998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
1008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
101