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