summaryrefslogtreecommitdiff
path: root/drivers/reset/reset-meson.c
blob: 6337cdaaffa2ba5b1d8ee72693ff752ff372fba5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// SPDX-License-Identifier: GPL-2.0
/*
 * Amlogic Meson Reset Controller driver
 *
 * Copyright (c) 2018 BayLibre, SAS.
 * Author: Neil Armstrong <narmstrong@baylibre.com>
 */

#include <dm.h>
#include <log.h>
#include <malloc.h>
#include <reset-uclass.h>
#include <regmap.h>
#include <linux/bitops.h>
#include <linux/delay.h>

#define BITS_PER_REG	32

struct meson_reset_drvdata {
	unsigned int reg_count;
	unsigned int level_offset;
};

struct meson_reset_priv {
	struct regmap *regmap;
	struct meson_reset_drvdata *drvdata;
};

static int meson_reset_request(struct reset_ctl *reset_ctl)
{
	struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev);
	struct meson_reset_drvdata *data = priv->drvdata;

	if (reset_ctl->id > (data->reg_count * BITS_PER_REG))
		return -EINVAL;

	return 0;
}

static int meson_reset_level(struct reset_ctl *reset_ctl, bool assert)
{
	struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev);
	struct meson_reset_drvdata *data = priv->drvdata;
	uint bank = reset_ctl->id / BITS_PER_REG;
	uint offset = reset_ctl->id % BITS_PER_REG;
	uint reg_offset = data->level_offset + (bank << 2);
	uint val;

	regmap_read(priv->regmap, reg_offset, &val);
	if (assert)
		val &= ~BIT(offset);
	else
		val |= BIT(offset);
	regmap_write(priv->regmap, reg_offset, val);

	return 0;
}

static int meson_reset_assert(struct reset_ctl *reset_ctl)
{
	return meson_reset_level(reset_ctl, true);
}

static int meson_reset_deassert(struct reset_ctl *reset_ctl)
{
	return meson_reset_level(reset_ctl, false);
}

struct reset_ops meson_reset_ops = {
	.request = meson_reset_request,
	.rst_assert = meson_reset_assert,
	.rst_deassert = meson_reset_deassert,
};

static const struct meson_reset_drvdata meson_gxbb_data = {
	.reg_count = 8,
	.level_offset = 0x7c,
};

static const struct meson_reset_drvdata meson_a1_data = {
	.reg_count = 3,
	.level_offset = 0x40,
};

static const struct udevice_id meson_reset_ids[] = {
	{
		.compatible = "amlogic,meson-gxbb-reset",
		.data = (ulong)&meson_gxbb_data,
	},
	{
		.compatible = "amlogic,meson-axg-reset",
		.data = (ulong)&meson_gxbb_data,
	},
	{
		.compatible = "amlogic,meson-a1-reset",
		.data = (ulong)&meson_a1_data,
	},
	{ }
};

static int meson_reset_probe(struct udevice *dev)
{
	struct meson_reset_priv *priv = dev_get_priv(dev);
	priv->drvdata = (struct meson_reset_drvdata *)dev_get_driver_data(dev);

	return regmap_init_mem(dev_ofnode(dev), &priv->regmap);
}

U_BOOT_DRIVER(meson_reset) = {
	.name = "meson_reset",
	.id = UCLASS_RESET,
	.of_match = meson_reset_ids,
	.probe = meson_reset_probe,
	.ops = &meson_reset_ops,
	.priv_auto	= sizeof(struct meson_reset_priv),
};