1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Amlogic Meson Reset Controller driver
4  *
5  * Copyright (c) 2018 BayLibre, SAS.
6  * Author: Neil Armstrong <narmstrong@baylibre.com>
7  */
8 
9 #include <dm.h>
10 #include <log.h>
11 #include <malloc.h>
12 #include <reset-uclass.h>
13 #include <regmap.h>
14 #include <linux/bitops.h>
15 #include <linux/delay.h>
16 
17 #define BITS_PER_REG	32
18 
19 struct meson_reset_drvdata {
20 	unsigned int reg_count;
21 	unsigned int level_offset;
22 };
23 
24 struct meson_reset_priv {
25 	struct regmap *regmap;
26 	struct meson_reset_drvdata *drvdata;
27 };
28 
meson_reset_request(struct reset_ctl * reset_ctl)29 static int meson_reset_request(struct reset_ctl *reset_ctl)
30 {
31 	struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev);
32 	struct meson_reset_drvdata *data = priv->drvdata;
33 
34 	if (reset_ctl->id > (data->reg_count * BITS_PER_REG))
35 		return -EINVAL;
36 
37 	return 0;
38 }
39 
meson_reset_level(struct reset_ctl * reset_ctl,bool assert)40 static int meson_reset_level(struct reset_ctl *reset_ctl, bool assert)
41 {
42 	struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev);
43 	struct meson_reset_drvdata *data = priv->drvdata;
44 	uint bank = reset_ctl->id / BITS_PER_REG;
45 	uint offset = reset_ctl->id % BITS_PER_REG;
46 	uint reg_offset = data->level_offset + (bank << 2);
47 	uint val;
48 
49 	regmap_read(priv->regmap, reg_offset, &val);
50 	if (assert)
51 		val &= ~BIT(offset);
52 	else
53 		val |= BIT(offset);
54 	regmap_write(priv->regmap, reg_offset, val);
55 
56 	return 0;
57 }
58 
meson_reset_assert(struct reset_ctl * reset_ctl)59 static int meson_reset_assert(struct reset_ctl *reset_ctl)
60 {
61 	return meson_reset_level(reset_ctl, true);
62 }
63 
meson_reset_deassert(struct reset_ctl * reset_ctl)64 static int meson_reset_deassert(struct reset_ctl *reset_ctl)
65 {
66 	return meson_reset_level(reset_ctl, false);
67 }
68 
69 struct reset_ops meson_reset_ops = {
70 	.request = meson_reset_request,
71 	.rst_assert = meson_reset_assert,
72 	.rst_deassert = meson_reset_deassert,
73 };
74 
75 static const struct meson_reset_drvdata meson_gxbb_data = {
76 	.reg_count = 8,
77 	.level_offset = 0x7c,
78 };
79 
80 static const struct meson_reset_drvdata meson_a1_data = {
81 	.reg_count = 3,
82 	.level_offset = 0x40,
83 };
84 
85 static const struct udevice_id meson_reset_ids[] = {
86 	{
87 		.compatible = "amlogic,meson-gxbb-reset",
88 		.data = (ulong)&meson_gxbb_data,
89 	},
90 	{
91 		.compatible = "amlogic,meson-axg-reset",
92 		.data = (ulong)&meson_gxbb_data,
93 	},
94 	{
95 		.compatible = "amlogic,meson-a1-reset",
96 		.data = (ulong)&meson_a1_data,
97 	},
98 	{ }
99 };
100 
meson_reset_probe(struct udevice * dev)101 static int meson_reset_probe(struct udevice *dev)
102 {
103 	struct meson_reset_priv *priv = dev_get_priv(dev);
104 	priv->drvdata = (struct meson_reset_drvdata *)dev_get_driver_data(dev);
105 
106 	return regmap_init_mem(dev_ofnode(dev), &priv->regmap);
107 }
108 
109 U_BOOT_DRIVER(meson_reset) = {
110 	.name = "meson_reset",
111 	.id = UCLASS_RESET,
112 	.of_match = meson_reset_ids,
113 	.probe = meson_reset_probe,
114 	.ops = &meson_reset_ops,
115 	.priv_auto	= sizeof(struct meson_reset_priv),
116 };
117