1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2017-18 Linaro Limited |
3 | |
4 | #include <linux/delay.h> |
5 | #include <linux/errno.h> |
6 | #include <linux/kernel.h> |
7 | #include <linux/module.h> |
8 | #include <linux/of.h> |
9 | #include <linux/of_platform.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/reboot.h> |
12 | #include <linux/reboot-mode.h> |
13 | #include <linux/regmap.h> |
14 | |
15 | #define PON_SOFT_RB_SPARE 0x8f |
16 | |
17 | #define GEN1_REASON_SHIFT 2 |
18 | #define GEN2_REASON_SHIFT 1 |
19 | |
20 | #define NO_REASON_SHIFT 0 |
21 | |
22 | struct pm8916_pon { |
23 | struct device *dev; |
24 | struct regmap *regmap; |
25 | u32 baseaddr; |
26 | struct reboot_mode_driver reboot_mode; |
27 | long reason_shift; |
28 | }; |
29 | |
30 | static int pm8916_reboot_mode_write(struct reboot_mode_driver *reboot, |
31 | unsigned int magic) |
32 | { |
33 | struct pm8916_pon *pon = container_of |
34 | (reboot, struct pm8916_pon, reboot_mode); |
35 | int ret; |
36 | |
37 | ret = regmap_update_bits(map: pon->regmap, |
38 | reg: pon->baseaddr + PON_SOFT_RB_SPARE, |
39 | GENMASK(7, pon->reason_shift), |
40 | val: magic << pon->reason_shift); |
41 | if (ret < 0) |
42 | dev_err(pon->dev, "update reboot mode bits failed\n" ); |
43 | |
44 | return ret; |
45 | } |
46 | |
47 | static int pm8916_pon_probe(struct platform_device *pdev) |
48 | { |
49 | struct pm8916_pon *pon; |
50 | long reason_shift; |
51 | int error; |
52 | |
53 | pon = devm_kzalloc(dev: &pdev->dev, size: sizeof(*pon), GFP_KERNEL); |
54 | if (!pon) |
55 | return -ENOMEM; |
56 | |
57 | pon->dev = &pdev->dev; |
58 | |
59 | pon->regmap = dev_get_regmap(dev: pdev->dev.parent, NULL); |
60 | if (!pon->regmap) { |
61 | dev_err(&pdev->dev, "failed to locate regmap\n" ); |
62 | return -ENODEV; |
63 | } |
64 | |
65 | error = of_property_read_u32(np: pdev->dev.of_node, propname: "reg" , |
66 | out_value: &pon->baseaddr); |
67 | if (error) |
68 | return error; |
69 | |
70 | reason_shift = (long)of_device_get_match_data(dev: &pdev->dev); |
71 | |
72 | if (reason_shift != NO_REASON_SHIFT) { |
73 | pon->reboot_mode.dev = &pdev->dev; |
74 | pon->reason_shift = reason_shift; |
75 | pon->reboot_mode.write = pm8916_reboot_mode_write; |
76 | error = devm_reboot_mode_register(dev: &pdev->dev, reboot: &pon->reboot_mode); |
77 | if (error) { |
78 | dev_err(&pdev->dev, "can't register reboot mode\n" ); |
79 | return error; |
80 | } |
81 | } |
82 | |
83 | platform_set_drvdata(pdev, data: pon); |
84 | |
85 | return devm_of_platform_populate(dev: &pdev->dev); |
86 | } |
87 | |
88 | static const struct of_device_id pm8916_pon_id_table[] = { |
89 | { .compatible = "qcom,pm8916-pon" , .data = (void *)GEN1_REASON_SHIFT }, |
90 | { .compatible = "qcom,pm8941-pon" , .data = (void *)NO_REASON_SHIFT }, |
91 | { .compatible = "qcom,pms405-pon" , .data = (void *)GEN1_REASON_SHIFT }, |
92 | { .compatible = "qcom,pm8998-pon" , .data = (void *)GEN2_REASON_SHIFT }, |
93 | { .compatible = "qcom,pmk8350-pon" , .data = (void *)GEN2_REASON_SHIFT }, |
94 | { } |
95 | }; |
96 | MODULE_DEVICE_TABLE(of, pm8916_pon_id_table); |
97 | |
98 | static struct platform_driver pm8916_pon_driver = { |
99 | .probe = pm8916_pon_probe, |
100 | .driver = { |
101 | .name = "pm8916-pon" , |
102 | .of_match_table = pm8916_pon_id_table, |
103 | }, |
104 | }; |
105 | module_platform_driver(pm8916_pon_driver); |
106 | |
107 | MODULE_DESCRIPTION("pm8916 Power On driver" ); |
108 | MODULE_LICENSE("GPL v2" ); |
109 | |