]> Git Repo - linux.git/blob - drivers/power/reset/syscon-poweroff.c
mm: abstract the vma_merge()/split_vma() pattern for mprotect() et al.
[linux.git] / drivers / power / reset / syscon-poweroff.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Generic Syscon Poweroff Driver
4  *
5  * Copyright (c) 2015, National Instruments Corp.
6  * Author: Moritz Fischer <[email protected]>
7  */
8
9 #include <linux/delay.h>
10 #include <linux/io.h>
11 #include <linux/notifier.h>
12 #include <linux/mfd/syscon.h>
13 #include <linux/of.h>
14 #include <linux/platform_device.h>
15 #include <linux/pm.h>
16 #include <linux/regmap.h>
17
18 static struct regmap *map;
19 static u32 offset;
20 static u32 value;
21 static u32 mask;
22
23 static void syscon_poweroff(void)
24 {
25         /* Issue the poweroff */
26         regmap_update_bits(map, offset, mask, value);
27
28         mdelay(1000);
29
30         pr_emerg("Unable to poweroff system\n");
31 }
32
33 static int syscon_poweroff_probe(struct platform_device *pdev)
34 {
35         int mask_err, value_err;
36
37         map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
38         if (IS_ERR(map)) {
39                 dev_err(&pdev->dev, "unable to get syscon");
40                 return PTR_ERR(map);
41         }
42
43         if (of_property_read_u32(pdev->dev.of_node, "offset", &offset)) {
44                 dev_err(&pdev->dev, "unable to read 'offset'");
45                 return -EINVAL;
46         }
47
48         value_err = of_property_read_u32(pdev->dev.of_node, "value", &value);
49         mask_err = of_property_read_u32(pdev->dev.of_node, "mask", &mask);
50         if (value_err && mask_err) {
51                 dev_err(&pdev->dev, "unable to read 'value' and 'mask'");
52                 return -EINVAL;
53         }
54
55         if (value_err) {
56                 /* support old binding */
57                 value = mask;
58                 mask = 0xFFFFFFFF;
59         } else if (mask_err) {
60                 /* support value without mask*/
61                 mask = 0xFFFFFFFF;
62         }
63
64         if (pm_power_off) {
65                 dev_err(&pdev->dev, "pm_power_off already claimed for %ps",
66                         pm_power_off);
67                 return -EBUSY;
68         }
69
70         pm_power_off = syscon_poweroff;
71
72         return 0;
73 }
74
75 static int syscon_poweroff_remove(struct platform_device *pdev)
76 {
77         if (pm_power_off == syscon_poweroff)
78                 pm_power_off = NULL;
79
80         return 0;
81 }
82
83 static const struct of_device_id syscon_poweroff_of_match[] = {
84         { .compatible = "syscon-poweroff" },
85         {}
86 };
87
88 static struct platform_driver syscon_poweroff_driver = {
89         .probe = syscon_poweroff_probe,
90         .remove = syscon_poweroff_remove,
91         .driver = {
92                 .name = "syscon-poweroff",
93                 .of_match_table = syscon_poweroff_of_match,
94         },
95 };
96
97 static int __init syscon_poweroff_register(void)
98 {
99         return platform_driver_register(&syscon_poweroff_driver);
100 }
101 device_initcall(syscon_poweroff_register);
This page took 0.034357 seconds and 4 git commands to generate.