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