1 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2 /*
3  * Copyright (C) 2018 Amarula Solutions.
4  * Author: Jagan Teki <jagan@amarulasolutions.com>
5  */
6 
7 #include <common.h>
8 #include <dm.h>
9 #include <errno.h>
10 #include <log.h>
11 #include <malloc.h>
12 #include <reset-uclass.h>
13 #include <asm/io.h>
14 #include <dm/device-internal.h>
15 #include <dm/lists.h>
16 #include <linux/bitops.h>
17 #include <linux/log2.h>
18 #include <asm/arch/ccu.h>
19 
20 struct sunxi_reset_priv {
21 	void *base;
22 	ulong count;
23 	const struct ccu_desc *desc;
24 };
25 
priv_to_reset(struct sunxi_reset_priv * priv,unsigned long id)26 static const struct ccu_reset *priv_to_reset(struct sunxi_reset_priv *priv,
27 					     unsigned long id)
28 {
29 	return	&priv->desc->resets[id];
30 }
31 
sunxi_reset_request(struct reset_ctl * reset_ctl)32 static int sunxi_reset_request(struct reset_ctl *reset_ctl)
33 {
34 	struct sunxi_reset_priv *priv = dev_get_priv(reset_ctl->dev);
35 
36 	debug("%s: (RST#%ld)\n", __func__, reset_ctl->id);
37 
38 	if (reset_ctl->id >= priv->count)
39 		return -EINVAL;
40 
41 	return 0;
42 }
43 
sunxi_reset_free(struct reset_ctl * reset_ctl)44 static int sunxi_reset_free(struct reset_ctl *reset_ctl)
45 {
46 	debug("%s: (RST#%ld)\n", __func__, reset_ctl->id);
47 
48 	return 0;
49 }
50 
sunxi_set_reset(struct reset_ctl * reset_ctl,bool on)51 static int sunxi_set_reset(struct reset_ctl *reset_ctl, bool on)
52 {
53 	struct sunxi_reset_priv *priv = dev_get_priv(reset_ctl->dev);
54 	const struct ccu_reset *reset = priv_to_reset(priv, reset_ctl->id);
55 	u32 reg;
56 
57 	if (!(reset->flags & CCU_RST_F_IS_VALID)) {
58 		printf("%s: (RST#%ld) unhandled\n", __func__, reset_ctl->id);
59 		return 0;
60 	}
61 
62 	debug("%s: (RST#%ld) off#0x%x, BIT(%d)\n", __func__,
63 	      reset_ctl->id, reset->off, ilog2(reset->bit));
64 
65 	reg = readl(priv->base + reset->off);
66 	if (on)
67 		reg |= reset->bit;
68 	else
69 		reg &= ~reset->bit;
70 
71 	writel(reg, priv->base + reset->off);
72 
73 	return 0;
74 }
75 
sunxi_reset_assert(struct reset_ctl * reset_ctl)76 static int sunxi_reset_assert(struct reset_ctl *reset_ctl)
77 {
78 	return sunxi_set_reset(reset_ctl, false);
79 }
80 
sunxi_reset_deassert(struct reset_ctl * reset_ctl)81 static int sunxi_reset_deassert(struct reset_ctl *reset_ctl)
82 {
83 	return sunxi_set_reset(reset_ctl, true);
84 }
85 
86 struct reset_ops sunxi_reset_ops = {
87 	.request = sunxi_reset_request,
88 	.rfree = sunxi_reset_free,
89 	.rst_assert = sunxi_reset_assert,
90 	.rst_deassert = sunxi_reset_deassert,
91 };
92 
sunxi_reset_probe(struct udevice * dev)93 static int sunxi_reset_probe(struct udevice *dev)
94 {
95 	struct sunxi_reset_priv *priv = dev_get_priv(dev);
96 
97 	priv->base = dev_read_addr_ptr(dev);
98 
99 	return 0;
100 }
101 
sunxi_reset_bind(struct udevice * dev,ulong count)102 int sunxi_reset_bind(struct udevice *dev, ulong count)
103 {
104 	struct udevice *rst_dev;
105 	struct sunxi_reset_priv *priv;
106 	int ret;
107 
108 	ret = device_bind_driver_to_node(dev, "sunxi_reset", "reset",
109 					 dev_ofnode(dev), &rst_dev);
110 	if (ret) {
111 		debug("failed to bind sunxi_reset driver (ret=%d)\n", ret);
112 		return ret;
113 	}
114 	priv = malloc(sizeof(struct sunxi_reset_priv));
115 	priv->count = count;
116 	priv->desc = (const struct ccu_desc *)dev_get_driver_data(dev);
117 	dev_set_priv(rst_dev, priv);
118 
119 	return 0;
120 }
121 
122 U_BOOT_DRIVER(sunxi_reset) = {
123 	.name		= "sunxi_reset",
124 	.id		= UCLASS_RESET,
125 	.ops		= &sunxi_reset_ops,
126 	.probe		= sunxi_reset_probe,
127 	.priv_auto	= sizeof(struct sunxi_reset_priv),
128 };
129