1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
4 *
5 * Derived from linux/drivers/power/reset/syscon-reboot.c:
6 * Copyright (C) 2013, Applied Micro Circuits Corporation
7 * Author: Feng Kan <fkan@apm.com>
8 */
9
10 #include <common.h>
11 #include <dm.h>
12 #include <errno.h>
13 #include <regmap.h>
14 #include <sysreset.h>
15 #include <syscon.h>
16 #include <linux/err.h>
17
18 struct syscon_reboot_priv {
19 struct regmap *regmap;
20 unsigned int offset;
21 unsigned int mask;
22 unsigned int value;
23 };
24
syscon_reboot_request(struct udevice * dev,enum sysreset_t type)25 static int syscon_reboot_request(struct udevice *dev, enum sysreset_t type)
26 {
27 struct syscon_reboot_priv *priv = dev_get_priv(dev);
28 ulong driver_data = dev_get_driver_data(dev);
29
30 if (type != driver_data)
31 return -EPROTONOSUPPORT;
32
33 regmap_update_bits(priv->regmap, priv->offset, priv->mask, priv->value);
34
35 return -EINPROGRESS;
36 }
37
38 static struct sysreset_ops syscon_reboot_ops = {
39 .request = syscon_reboot_request,
40 };
41
syscon_reboot_probe(struct udevice * dev)42 int syscon_reboot_probe(struct udevice *dev)
43 {
44 struct syscon_reboot_priv *priv = dev_get_priv(dev);
45 int err;
46 int mask_err, value_err;
47
48 priv->regmap = syscon_regmap_lookup_by_phandle(dev, "regmap");
49 if (IS_ERR(priv->regmap)) {
50 pr_err("unable to find regmap\n");
51 return -ENODEV;
52 }
53
54 err = dev_read_u32(dev, "offset", &priv->offset);
55 if (err) {
56 pr_err("unable to find offset\n");
57 return -ENOENT;
58 }
59
60 mask_err = dev_read_u32(dev, "mask", &priv->mask);
61 value_err = dev_read_u32(dev, "value", &priv->value);
62 if (mask_err && value_err) {
63 pr_err("unable to find mask and value\n");
64 return -EINVAL;
65 }
66
67 if (value_err) {
68 /* support old binding */
69 priv->value = priv->mask;
70 priv->mask = 0xffffffff;
71 } else if (mask_err) {
72 /* support value without mask*/
73 priv->mask = 0xffffffff;
74 }
75
76 return 0;
77 }
78
79 static const struct udevice_id syscon_reboot_ids[] = {
80 { .compatible = "syscon-reboot", .data = SYSRESET_COLD },
81 { .compatible = "syscon-poweroff", .data = SYSRESET_POWER_OFF },
82 { /* sentinel */ }
83 };
84
85 U_BOOT_DRIVER(syscon_reboot) = {
86 .name = "syscon_reboot",
87 .id = UCLASS_SYSRESET,
88 .of_match = syscon_reboot_ids,
89 .probe = syscon_reboot_probe,
90 .priv_auto = sizeof(struct syscon_reboot_priv),
91 .ops = &syscon_reboot_ops,
92 };
93