|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Power off through MediaTek PMIC | 
|  | * | 
|  | * Copyright (C) 2018 MediaTek Inc. | 
|  | * | 
|  | * Author: Sean Wang <sean.wang@mediatek.com> | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include <linux/err.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/mfd/mt6397/core.h> | 
|  | #include <linux/mfd/mt6397/rtc.h> | 
|  | #include <linux/reboot.h> | 
|  |  | 
|  | struct mt6323_pwrc { | 
|  | struct device *dev; | 
|  | struct regmap *regmap; | 
|  | u32 base; | 
|  | }; | 
|  |  | 
|  | static int mt6323_do_pwroff(struct sys_off_data *data) | 
|  | { | 
|  | struct mt6323_pwrc *pwrc = data->cb_data; | 
|  | unsigned int val; | 
|  | int ret; | 
|  |  | 
|  | regmap_write(pwrc->regmap, pwrc->base + RTC_BBPU, RTC_BBPU_KEY); | 
|  | regmap_write(pwrc->regmap, pwrc->base + RTC_WRTGR_MT6323, 1); | 
|  |  | 
|  | ret = regmap_read_poll_timeout(pwrc->regmap, | 
|  | pwrc->base + RTC_BBPU, val, | 
|  | !(val & RTC_BBPU_CBUSY), | 
|  | MTK_RTC_POLL_DELAY_US, | 
|  | MTK_RTC_POLL_TIMEOUT); | 
|  | if (ret) | 
|  | dev_err(pwrc->dev, "failed to write BBPU: %d\n", ret); | 
|  |  | 
|  | /* Wait some time until system down, otherwise, notice with a warn */ | 
|  | mdelay(1000); | 
|  |  | 
|  | WARN_ONCE(1, "Unable to power off system\n"); | 
|  |  | 
|  | return NOTIFY_DONE; | 
|  | } | 
|  |  | 
|  | static int mt6323_pwrc_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent); | 
|  | struct mt6323_pwrc *pwrc; | 
|  | struct resource *res; | 
|  | int ret; | 
|  |  | 
|  | pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); | 
|  | if (!pwrc) | 
|  | return -ENOMEM; | 
|  |  | 
|  | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | if (!res) | 
|  | return -EINVAL; | 
|  |  | 
|  | pwrc->base = res->start; | 
|  | pwrc->regmap = mt6397_chip->regmap; | 
|  | pwrc->dev = &pdev->dev; | 
|  |  | 
|  | ret = devm_register_sys_off_handler(pwrc->dev, | 
|  | SYS_OFF_MODE_POWER_OFF, | 
|  | SYS_OFF_PRIO_DEFAULT, | 
|  | mt6323_do_pwroff, | 
|  | pwrc); | 
|  | if (ret) | 
|  | return dev_err_probe(pwrc->dev, ret, "failed to register power-off handler\n"); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct of_device_id mt6323_pwrc_dt_match[] = { | 
|  | { .compatible = "mediatek,mt6323-pwrc" }, | 
|  | {}, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, mt6323_pwrc_dt_match); | 
|  |  | 
|  | static struct platform_driver mt6323_pwrc_driver = { | 
|  | .probe          = mt6323_pwrc_probe, | 
|  | .driver         = { | 
|  | .name   = "mt6323-pwrc", | 
|  | .of_match_table = mt6323_pwrc_dt_match, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | module_platform_driver(mt6323_pwrc_driver); | 
|  |  | 
|  | MODULE_DESCRIPTION("Poweroff driver for MT6323 PMIC"); | 
|  | MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); |