blob: d43bc163dd021b77786906701ac01a82fc802d39 [file] [log] [blame]
Antoine Ténartbd132512014-09-03 09:48:20 +02001/*
2 * Copyright (C) 2014 Marvell Technology Group Ltd.
3 *
4 * Antoine Tenart <antoine.tenart@free-electrons.com>
5 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
6 *
7 * This file is licensed under the terms of the GNU General Public
8 * License version 2. This program is licensed "as is" without any
9 * warranty of any kind, whether express or implied.
10 */
11
12#include <linux/delay.h>
13#include <linux/io.h>
Antoine Tenartaed6f3c2015-05-16 00:41:25 +020014#include <linux/mfd/syscon.h>
Antoine Ténartbd132512014-09-03 09:48:20 +020015#include <linux/module.h>
16#include <linux/of.h>
17#include <linux/of_address.h>
18#include <linux/platform_device.h>
Antoine Tenartaed6f3c2015-05-16 00:41:25 +020019#include <linux/regmap.h>
Antoine Ténartbd132512014-09-03 09:48:20 +020020#include <linux/reset-controller.h>
21#include <linux/slab.h>
22#include <linux/types.h>
23
24#define BERLIN_MAX_RESETS 32
25
26#define to_berlin_reset_priv(p) \
27 container_of((p), struct berlin_reset_priv, rcdev)
28
29struct berlin_reset_priv {
30 void __iomem *base;
31 unsigned int size;
Antoine Tenartaed6f3c2015-05-16 00:41:25 +020032 struct regmap *regmap;
Antoine Ténartbd132512014-09-03 09:48:20 +020033 struct reset_controller_dev rcdev;
34};
35
36static int berlin_reset_reset(struct reset_controller_dev *rcdev,
37 unsigned long id)
38{
39 struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev);
40 int offset = id >> 8;
41 int mask = BIT(id & 0x1f);
42
Antoine Tenartaed6f3c2015-05-16 00:41:25 +020043 if (priv->regmap)
44 regmap_write(priv->regmap, offset, mask);
45 else
46 writel(mask, priv->base + offset);
Antoine Ténartbd132512014-09-03 09:48:20 +020047
48 /* let the reset be effective */
49 udelay(10);
50
51 return 0;
52}
53
54static struct reset_control_ops berlin_reset_ops = {
55 .reset = berlin_reset_reset,
56};
57
58static int berlin_reset_xlate(struct reset_controller_dev *rcdev,
59 const struct of_phandle_args *reset_spec)
60{
61 struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev);
62 unsigned offset, bit;
63
64 if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells))
65 return -EINVAL;
66
67 offset = reset_spec->args[0];
68 bit = reset_spec->args[1];
69
70 if (offset >= priv->size)
71 return -EINVAL;
72
73 if (bit >= BERLIN_MAX_RESETS)
74 return -EINVAL;
75
76 return (offset << 8) | bit;
77}
78
Antoine Tenartaed6f3c2015-05-16 00:41:25 +020079static int berlin2_reset_probe(struct platform_device *pdev)
80{
81 struct device_node *parent_np = of_get_parent(pdev->dev.of_node);
82 struct berlin_reset_priv *priv;
83
84 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
85 if (!priv)
86 return -ENOMEM;
87
88 priv->regmap = syscon_node_to_regmap(parent_np);
89 of_node_put(parent_np);
90 if (IS_ERR(priv->regmap))
91 return PTR_ERR(priv->regmap);
92
93 priv->rcdev.owner = THIS_MODULE;
94 priv->rcdev.ops = &berlin_reset_ops;
95 priv->rcdev.of_node = pdev->dev.of_node;
96 priv->rcdev.of_reset_n_cells = 2;
97 priv->rcdev.of_xlate = berlin_reset_xlate;
98
99 reset_controller_register(&priv->rcdev);
100
101 return 0;
102}
103
104static const struct of_device_id berlin_reset_dt_match[] = {
105 { .compatible = "marvell,berlin2-reset" },
106 { },
107};
108MODULE_DEVICE_TABLE(of, berlin_reset_dt_match);
109
110static struct platform_driver berlin_reset_driver = {
111 .probe = berlin2_reset_probe,
112 .driver = {
113 .name = "berlin2-reset",
114 .of_match_table = berlin_reset_dt_match,
115 },
116};
117module_platform_driver(berlin_reset_driver);
118
119MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
120MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>");
121MODULE_DESCRIPTION("Marvell Berlin reset driver");
122MODULE_LICENSE("GPL");
123
Antoine Ténartbd132512014-09-03 09:48:20 +0200124static int __berlin_reset_init(struct device_node *np)
125{
126 struct berlin_reset_priv *priv;
127 struct resource res;
128 resource_size_t size;
129 int ret;
130
131 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
132 if (!priv)
133 return -ENOMEM;
134
135 ret = of_address_to_resource(np, 0, &res);
136 if (ret)
137 goto err;
138
139 size = resource_size(&res);
140 priv->base = ioremap(res.start, size);
141 if (!priv->base) {
142 ret = -ENOMEM;
143 goto err;
144 }
145 priv->size = size;
146
147 priv->rcdev.owner = THIS_MODULE;
148 priv->rcdev.ops = &berlin_reset_ops;
149 priv->rcdev.of_node = np;
150 priv->rcdev.of_reset_n_cells = 2;
151 priv->rcdev.of_xlate = berlin_reset_xlate;
152
153 reset_controller_register(&priv->rcdev);
154
155 return 0;
156
157err:
158 kfree(priv);
159 return ret;
160}
161
162static const struct of_device_id berlin_reset_of_match[] __initconst = {
163 { .compatible = "marvell,berlin2-chip-ctrl" },
164 { .compatible = "marvell,berlin2cd-chip-ctrl" },
165 { .compatible = "marvell,berlin2q-chip-ctrl" },
166 { },
167};
168
169static int __init berlin_reset_init(void)
170{
171 struct device_node *np;
172 int ret;
173
174 for_each_matching_node(np, berlin_reset_of_match) {
175 ret = __berlin_reset_init(np);
176 if (ret)
177 return ret;
178 }
179
180 return 0;
181}
182arch_initcall(berlin_reset_init);