162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (c) 2017-18 Linaro Limited 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/delay.h> 562306a36Sopenharmony_ci#include <linux/errno.h> 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/of.h> 962306a36Sopenharmony_ci#include <linux/of_platform.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/reboot.h> 1262306a36Sopenharmony_ci#include <linux/reboot-mode.h> 1362306a36Sopenharmony_ci#include <linux/regmap.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define PON_SOFT_RB_SPARE 0x8f 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define GEN1_REASON_SHIFT 2 1862306a36Sopenharmony_ci#define GEN2_REASON_SHIFT 1 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define NO_REASON_SHIFT 0 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct pm8916_pon { 2362306a36Sopenharmony_ci struct device *dev; 2462306a36Sopenharmony_ci struct regmap *regmap; 2562306a36Sopenharmony_ci u32 baseaddr; 2662306a36Sopenharmony_ci struct reboot_mode_driver reboot_mode; 2762306a36Sopenharmony_ci long reason_shift; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int pm8916_reboot_mode_write(struct reboot_mode_driver *reboot, 3162306a36Sopenharmony_ci unsigned int magic) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct pm8916_pon *pon = container_of 3462306a36Sopenharmony_ci (reboot, struct pm8916_pon, reboot_mode); 3562306a36Sopenharmony_ci int ret; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci ret = regmap_update_bits(pon->regmap, 3862306a36Sopenharmony_ci pon->baseaddr + PON_SOFT_RB_SPARE, 3962306a36Sopenharmony_ci GENMASK(7, pon->reason_shift), 4062306a36Sopenharmony_ci magic << pon->reason_shift); 4162306a36Sopenharmony_ci if (ret < 0) 4262306a36Sopenharmony_ci dev_err(pon->dev, "update reboot mode bits failed\n"); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return ret; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int pm8916_pon_probe(struct platform_device *pdev) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct pm8916_pon *pon; 5062306a36Sopenharmony_ci long reason_shift; 5162306a36Sopenharmony_ci int error; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci pon = devm_kzalloc(&pdev->dev, sizeof(*pon), GFP_KERNEL); 5462306a36Sopenharmony_ci if (!pon) 5562306a36Sopenharmony_ci return -ENOMEM; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci pon->dev = &pdev->dev; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci pon->regmap = dev_get_regmap(pdev->dev.parent, NULL); 6062306a36Sopenharmony_ci if (!pon->regmap) { 6162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to locate regmap\n"); 6262306a36Sopenharmony_ci return -ENODEV; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci error = of_property_read_u32(pdev->dev.of_node, "reg", 6662306a36Sopenharmony_ci &pon->baseaddr); 6762306a36Sopenharmony_ci if (error) 6862306a36Sopenharmony_ci return error; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci reason_shift = (long)of_device_get_match_data(&pdev->dev); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (reason_shift != NO_REASON_SHIFT) { 7362306a36Sopenharmony_ci pon->reboot_mode.dev = &pdev->dev; 7462306a36Sopenharmony_ci pon->reason_shift = reason_shift; 7562306a36Sopenharmony_ci pon->reboot_mode.write = pm8916_reboot_mode_write; 7662306a36Sopenharmony_ci error = devm_reboot_mode_register(&pdev->dev, &pon->reboot_mode); 7762306a36Sopenharmony_ci if (error) { 7862306a36Sopenharmony_ci dev_err(&pdev->dev, "can't register reboot mode\n"); 7962306a36Sopenharmony_ci return error; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci platform_set_drvdata(pdev, pon); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return devm_of_platform_populate(&pdev->dev); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic const struct of_device_id pm8916_pon_id_table[] = { 8962306a36Sopenharmony_ci { .compatible = "qcom,pm8916-pon", .data = (void *)GEN1_REASON_SHIFT }, 9062306a36Sopenharmony_ci { .compatible = "qcom,pm8941-pon", .data = (void *)NO_REASON_SHIFT }, 9162306a36Sopenharmony_ci { .compatible = "qcom,pms405-pon", .data = (void *)GEN1_REASON_SHIFT }, 9262306a36Sopenharmony_ci { .compatible = "qcom,pm8998-pon", .data = (void *)GEN2_REASON_SHIFT }, 9362306a36Sopenharmony_ci { .compatible = "qcom,pmk8350-pon", .data = (void *)GEN2_REASON_SHIFT }, 9462306a36Sopenharmony_ci { } 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pm8916_pon_id_table); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic struct platform_driver pm8916_pon_driver = { 9962306a36Sopenharmony_ci .probe = pm8916_pon_probe, 10062306a36Sopenharmony_ci .driver = { 10162306a36Sopenharmony_ci .name = "pm8916-pon", 10262306a36Sopenharmony_ci .of_match_table = pm8916_pon_id_table, 10362306a36Sopenharmony_ci }, 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_cimodule_platform_driver(pm8916_pon_driver); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ciMODULE_DESCRIPTION("pm8916 Power On driver"); 10862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 109