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 <common.h>
10#include <dm.h>
11#include <log.h>
12#include <malloc.h>
13#include <reset-uclass.h>
14#include <regmap.h>
15#include <linux/bitops.h>
16#include <linux/delay.h>
17
18#define BITS_PER_REG	32
19
20struct meson_reset_drvdata {
21	unsigned int reg_count;
22	unsigned int level_offset;
23};
24
25struct meson_reset_priv {
26	struct regmap *regmap;
27	struct meson_reset_drvdata *drvdata;
28};
29
30static int meson_reset_request(struct reset_ctl *reset_ctl)
31{
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))
36		return -EINVAL;
37
38	return 0;
39}
40
41static int meson_reset_level(struct reset_ctl *reset_ctl, bool assert)
42{
43	struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev);
44	struct meson_reset_drvdata *data = priv->drvdata;
45	uint bank = reset_ctl->id / BITS_PER_REG;
46	uint offset = reset_ctl->id % BITS_PER_REG;
47	uint reg_offset = data->level_offset + (bank << 2);
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
60static int meson_reset_assert(struct reset_ctl *reset_ctl)
61{
62	return meson_reset_level(reset_ctl, true);
63}
64
65static int meson_reset_deassert(struct reset_ctl *reset_ctl)
66{
67	return meson_reset_level(reset_ctl, false);
68}
69
70struct reset_ops meson_reset_ops = {
71	.request = meson_reset_request,
72	.rst_assert = meson_reset_assert,
73	.rst_deassert = meson_reset_deassert,
74};
75
76static const struct meson_reset_drvdata meson_gxbb_data = {
77	.reg_count = 8,
78	.level_offset = 0x7c,
79};
80
81static const struct meson_reset_drvdata meson_a1_data = {
82	.reg_count = 3,
83	.level_offset = 0x40,
84};
85
86static const struct udevice_id meson_reset_ids[] = {
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	},
99	{ }
100};
101
102static int meson_reset_probe(struct udevice *dev)
103{
104	struct meson_reset_priv *priv = dev_get_priv(dev);
105	priv->drvdata = (struct meson_reset_drvdata *)dev_get_driver_data(dev);
106
107	return regmap_init_mem(dev_ofnode(dev), &priv->regmap);
108}
109
110U_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,
116	.priv_auto	= sizeof(struct meson_reset_priv),
117};
118