xref: /linux/drivers/reset/amlogic/reset-meson.c (revision 2c138ee3354f8088769d05701a2e16d1cb4cc22d)
1*2c138ee3SJerome Brunet // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2*2c138ee3SJerome Brunet /*
3*2c138ee3SJerome Brunet  * Amlogic Meson Reset Controller driver
4*2c138ee3SJerome Brunet  *
5*2c138ee3SJerome Brunet  * Copyright (c) 2016 BayLibre, SAS.
6*2c138ee3SJerome Brunet  * Author: Neil Armstrong <narmstrong@baylibre.com>
7*2c138ee3SJerome Brunet  */
8*2c138ee3SJerome Brunet #include <linux/err.h>
9*2c138ee3SJerome Brunet #include <linux/init.h>
10*2c138ee3SJerome Brunet #include <linux/io.h>
11*2c138ee3SJerome Brunet #include <linux/of.h>
12*2c138ee3SJerome Brunet #include <linux/module.h>
13*2c138ee3SJerome Brunet #include <linux/platform_device.h>
14*2c138ee3SJerome Brunet #include <linux/regmap.h>
15*2c138ee3SJerome Brunet #include <linux/reset-controller.h>
16*2c138ee3SJerome Brunet #include <linux/slab.h>
17*2c138ee3SJerome Brunet #include <linux/types.h>
18*2c138ee3SJerome Brunet 
19*2c138ee3SJerome Brunet struct meson_reset_param {
20*2c138ee3SJerome Brunet 	unsigned int reset_num;
21*2c138ee3SJerome Brunet 	unsigned int reset_offset;
22*2c138ee3SJerome Brunet 	unsigned int level_offset;
23*2c138ee3SJerome Brunet 	bool level_low_reset;
24*2c138ee3SJerome Brunet };
25*2c138ee3SJerome Brunet 
26*2c138ee3SJerome Brunet struct meson_reset {
27*2c138ee3SJerome Brunet 	const struct meson_reset_param *param;
28*2c138ee3SJerome Brunet 	struct reset_controller_dev rcdev;
29*2c138ee3SJerome Brunet 	struct regmap *map;
30*2c138ee3SJerome Brunet };
31*2c138ee3SJerome Brunet 
32*2c138ee3SJerome Brunet static void meson_reset_offset_and_bit(struct meson_reset *data,
33*2c138ee3SJerome Brunet 				       unsigned long id,
34*2c138ee3SJerome Brunet 				       unsigned int *offset,
35*2c138ee3SJerome Brunet 				       unsigned int *bit)
36*2c138ee3SJerome Brunet {
37*2c138ee3SJerome Brunet 	unsigned int stride = regmap_get_reg_stride(data->map);
38*2c138ee3SJerome Brunet 
39*2c138ee3SJerome Brunet 	*offset = (id / (stride * BITS_PER_BYTE)) * stride;
40*2c138ee3SJerome Brunet 	*bit = id % (stride * BITS_PER_BYTE);
41*2c138ee3SJerome Brunet }
42*2c138ee3SJerome Brunet 
43*2c138ee3SJerome Brunet static int meson_reset_reset(struct reset_controller_dev *rcdev,
44*2c138ee3SJerome Brunet 			     unsigned long id)
45*2c138ee3SJerome Brunet {
46*2c138ee3SJerome Brunet 	struct meson_reset *data =
47*2c138ee3SJerome Brunet 		container_of(rcdev, struct meson_reset, rcdev);
48*2c138ee3SJerome Brunet 	unsigned int offset, bit;
49*2c138ee3SJerome Brunet 
50*2c138ee3SJerome Brunet 	meson_reset_offset_and_bit(data, id, &offset, &bit);
51*2c138ee3SJerome Brunet 	offset += data->param->reset_offset;
52*2c138ee3SJerome Brunet 
53*2c138ee3SJerome Brunet 	return regmap_write(data->map, offset, BIT(bit));
54*2c138ee3SJerome Brunet }
55*2c138ee3SJerome Brunet 
56*2c138ee3SJerome Brunet static int meson_reset_level(struct reset_controller_dev *rcdev,
57*2c138ee3SJerome Brunet 			    unsigned long id, bool assert)
58*2c138ee3SJerome Brunet {
59*2c138ee3SJerome Brunet 	struct meson_reset *data =
60*2c138ee3SJerome Brunet 		container_of(rcdev, struct meson_reset, rcdev);
61*2c138ee3SJerome Brunet 	unsigned int offset, bit;
62*2c138ee3SJerome Brunet 
63*2c138ee3SJerome Brunet 	meson_reset_offset_and_bit(data, id, &offset, &bit);
64*2c138ee3SJerome Brunet 	offset += data->param->level_offset;
65*2c138ee3SJerome Brunet 	assert ^= data->param->level_low_reset;
66*2c138ee3SJerome Brunet 
67*2c138ee3SJerome Brunet 	return regmap_update_bits(data->map, offset,
68*2c138ee3SJerome Brunet 				  BIT(bit), assert ? BIT(bit) : 0);
69*2c138ee3SJerome Brunet }
70*2c138ee3SJerome Brunet 
71*2c138ee3SJerome Brunet static int meson_reset_status(struct reset_controller_dev *rcdev,
72*2c138ee3SJerome Brunet 			      unsigned long id)
73*2c138ee3SJerome Brunet {
74*2c138ee3SJerome Brunet 	struct meson_reset *data =
75*2c138ee3SJerome Brunet 		container_of(rcdev, struct meson_reset, rcdev);
76*2c138ee3SJerome Brunet 	unsigned int val, offset, bit;
77*2c138ee3SJerome Brunet 
78*2c138ee3SJerome Brunet 	meson_reset_offset_and_bit(data, id, &offset, &bit);
79*2c138ee3SJerome Brunet 	offset += data->param->level_offset;
80*2c138ee3SJerome Brunet 
81*2c138ee3SJerome Brunet 	regmap_read(data->map, offset, &val);
82*2c138ee3SJerome Brunet 	val = !!(BIT(bit) & val);
83*2c138ee3SJerome Brunet 
84*2c138ee3SJerome Brunet 	return val ^ data->param->level_low_reset;
85*2c138ee3SJerome Brunet }
86*2c138ee3SJerome Brunet 
87*2c138ee3SJerome Brunet static int meson_reset_assert(struct reset_controller_dev *rcdev,
88*2c138ee3SJerome Brunet 			      unsigned long id)
89*2c138ee3SJerome Brunet {
90*2c138ee3SJerome Brunet 	return meson_reset_level(rcdev, id, true);
91*2c138ee3SJerome Brunet }
92*2c138ee3SJerome Brunet 
93*2c138ee3SJerome Brunet static int meson_reset_deassert(struct reset_controller_dev *rcdev,
94*2c138ee3SJerome Brunet 				unsigned long id)
95*2c138ee3SJerome Brunet {
96*2c138ee3SJerome Brunet 	return meson_reset_level(rcdev, id, false);
97*2c138ee3SJerome Brunet }
98*2c138ee3SJerome Brunet 
99*2c138ee3SJerome Brunet static const struct reset_control_ops meson_reset_ops = {
100*2c138ee3SJerome Brunet 	.reset		= meson_reset_reset,
101*2c138ee3SJerome Brunet 	.assert		= meson_reset_assert,
102*2c138ee3SJerome Brunet 	.deassert	= meson_reset_deassert,
103*2c138ee3SJerome Brunet 	.status		= meson_reset_status,
104*2c138ee3SJerome Brunet };
105*2c138ee3SJerome Brunet 
106*2c138ee3SJerome Brunet static const struct meson_reset_param meson8b_param = {
107*2c138ee3SJerome Brunet 	.reset_num	= 256,
108*2c138ee3SJerome Brunet 	.reset_offset	= 0x0,
109*2c138ee3SJerome Brunet 	.level_offset	= 0x7c,
110*2c138ee3SJerome Brunet 	.level_low_reset = true,
111*2c138ee3SJerome Brunet };
112*2c138ee3SJerome Brunet 
113*2c138ee3SJerome Brunet static const struct meson_reset_param meson_a1_param = {
114*2c138ee3SJerome Brunet 	.reset_num	= 96,
115*2c138ee3SJerome Brunet 	.reset_offset	= 0x0,
116*2c138ee3SJerome Brunet 	.level_offset	= 0x40,
117*2c138ee3SJerome Brunet 	.level_low_reset = true,
118*2c138ee3SJerome Brunet };
119*2c138ee3SJerome Brunet 
120*2c138ee3SJerome Brunet static const struct meson_reset_param meson_s4_param = {
121*2c138ee3SJerome Brunet 	.reset_num	= 192,
122*2c138ee3SJerome Brunet 	.reset_offset	= 0x0,
123*2c138ee3SJerome Brunet 	.level_offset	= 0x40,
124*2c138ee3SJerome Brunet 	.level_low_reset = true,
125*2c138ee3SJerome Brunet };
126*2c138ee3SJerome Brunet 
127*2c138ee3SJerome Brunet static const struct meson_reset_param t7_param = {
128*2c138ee3SJerome Brunet 	.reset_num      = 224,
129*2c138ee3SJerome Brunet 	.reset_offset	= 0x0,
130*2c138ee3SJerome Brunet 	.level_offset   = 0x40,
131*2c138ee3SJerome Brunet 	.level_low_reset = true,
132*2c138ee3SJerome Brunet };
133*2c138ee3SJerome Brunet 
134*2c138ee3SJerome Brunet static const struct of_device_id meson_reset_dt_ids[] = {
135*2c138ee3SJerome Brunet 	 { .compatible = "amlogic,meson8b-reset",    .data = &meson8b_param},
136*2c138ee3SJerome Brunet 	 { .compatible = "amlogic,meson-gxbb-reset", .data = &meson8b_param},
137*2c138ee3SJerome Brunet 	 { .compatible = "amlogic,meson-axg-reset",  .data = &meson8b_param},
138*2c138ee3SJerome Brunet 	 { .compatible = "amlogic,meson-a1-reset",   .data = &meson_a1_param},
139*2c138ee3SJerome Brunet 	 { .compatible = "amlogic,meson-s4-reset",   .data = &meson_s4_param},
140*2c138ee3SJerome Brunet 	 { .compatible = "amlogic,c3-reset",   .data = &meson_s4_param},
141*2c138ee3SJerome Brunet 	 { .compatible = "amlogic,t7-reset",   .data = &t7_param},
142*2c138ee3SJerome Brunet 	 { /* sentinel */ },
143*2c138ee3SJerome Brunet };
144*2c138ee3SJerome Brunet MODULE_DEVICE_TABLE(of, meson_reset_dt_ids);
145*2c138ee3SJerome Brunet 
146*2c138ee3SJerome Brunet static const struct regmap_config regmap_config = {
147*2c138ee3SJerome Brunet 	.reg_bits   = 32,
148*2c138ee3SJerome Brunet 	.val_bits   = 32,
149*2c138ee3SJerome Brunet 	.reg_stride = 4,
150*2c138ee3SJerome Brunet };
151*2c138ee3SJerome Brunet 
152*2c138ee3SJerome Brunet static int meson_reset_probe(struct platform_device *pdev)
153*2c138ee3SJerome Brunet {
154*2c138ee3SJerome Brunet 	struct device *dev = &pdev->dev;
155*2c138ee3SJerome Brunet 	struct meson_reset *data;
156*2c138ee3SJerome Brunet 	void __iomem *base;
157*2c138ee3SJerome Brunet 
158*2c138ee3SJerome Brunet 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
159*2c138ee3SJerome Brunet 	if (!data)
160*2c138ee3SJerome Brunet 		return -ENOMEM;
161*2c138ee3SJerome Brunet 
162*2c138ee3SJerome Brunet 	base = devm_platform_ioremap_resource(pdev, 0);
163*2c138ee3SJerome Brunet 	if (IS_ERR(base))
164*2c138ee3SJerome Brunet 		return PTR_ERR(base);
165*2c138ee3SJerome Brunet 
166*2c138ee3SJerome Brunet 	data->param = device_get_match_data(dev);
167*2c138ee3SJerome Brunet 	if (!data->param)
168*2c138ee3SJerome Brunet 		return -ENODEV;
169*2c138ee3SJerome Brunet 
170*2c138ee3SJerome Brunet 	data->map = devm_regmap_init_mmio(dev, base, &regmap_config);
171*2c138ee3SJerome Brunet 	if (IS_ERR(data->map))
172*2c138ee3SJerome Brunet 		return dev_err_probe(dev, PTR_ERR(data->map),
173*2c138ee3SJerome Brunet 				     "can't init regmap mmio region\n");
174*2c138ee3SJerome Brunet 
175*2c138ee3SJerome Brunet 	data->rcdev.owner = THIS_MODULE;
176*2c138ee3SJerome Brunet 	data->rcdev.nr_resets = data->param->reset_num;
177*2c138ee3SJerome Brunet 	data->rcdev.ops = &meson_reset_ops;
178*2c138ee3SJerome Brunet 	data->rcdev.of_node = dev->of_node;
179*2c138ee3SJerome Brunet 
180*2c138ee3SJerome Brunet 	return devm_reset_controller_register(dev, &data->rcdev);
181*2c138ee3SJerome Brunet }
182*2c138ee3SJerome Brunet 
183*2c138ee3SJerome Brunet static struct platform_driver meson_reset_driver = {
184*2c138ee3SJerome Brunet 	.probe	= meson_reset_probe,
185*2c138ee3SJerome Brunet 	.driver = {
186*2c138ee3SJerome Brunet 		.name		= "meson_reset",
187*2c138ee3SJerome Brunet 		.of_match_table	= meson_reset_dt_ids,
188*2c138ee3SJerome Brunet 	},
189*2c138ee3SJerome Brunet };
190*2c138ee3SJerome Brunet module_platform_driver(meson_reset_driver);
191*2c138ee3SJerome Brunet 
192*2c138ee3SJerome Brunet MODULE_DESCRIPTION("Amlogic Meson Reset Controller driver");
193*2c138ee3SJerome Brunet MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
194*2c138ee3SJerome Brunet MODULE_LICENSE("Dual BSD/GPL");
195