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