summaryrefslogtreecommitdiffstats
path: root/drivers/reset/reset-meson-audio-arb.c
blob: c53a2185a0393c689c631d7df7f1b760cb2b72e0 (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
// Copyright (c) 2018 BayLibre, SAS.
// Author: Jerome Brunet <jbrunet@baylibre.com>

#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/reset-controller.h>
#include <linux/spinlock.h>

#include <dt-bindings/reset/amlogic,meson-axg-audio-arb.h>

struct meson_audio_arb_data {
	struct reset_controller_dev rstc;
	void __iomem *regs;
	struct clk *clk;
	const unsigned int *reset_bits;
	spinlock_t lock;
};

#define ARB_GENERAL_BIT	31

static const unsigned int axg_audio_arb_reset_bits[] = {
	[AXG_ARB_TODDR_A]	= 0,
	[AXG_ARB_TODDR_B]	= 1,
	[AXG_ARB_TODDR_C]	= 2,
	[AXG_ARB_FRDDR_A]	= 4,
	[AXG_ARB_FRDDR_B]	= 5,
	[AXG_ARB_FRDDR_C]	= 6,
};

static int meson_audio_arb_update(struct reset_controller_dev *rcdev,
				  unsigned long id, bool assert)
{
	u32 val;
	struct meson_audio_arb_data *arb =
		container_of(rcdev, struct meson_audio_arb_data, rstc);

	spin_lock(&arb->lock);
	val = readl(arb->regs);

	if (assert)
		val &= ~BIT(arb->reset_bits[id]);
	else
		val |= BIT(arb->reset_bits[id]);

	writel(val, arb->regs);
	spin_unlock(&arb->lock);

	return 0;
}

static int meson_audio_arb_status(struct reset_controller_dev *rcdev,
				  unsigned long id)
{
	u32 val;
	struct meson_audio_arb_data *arb =
		container_of(rcdev, struct meson_audio_arb_data, rstc);

	val = readl(arb->regs);

	return !(val & BIT(arb->reset_bits[id]));
}

static int meson_audio_arb_assert(struct reset_controller_dev *rcdev,
				  unsigned long id)
{
	return meson_audio_arb_update(rcdev, id, true);
}

static int meson_audio_arb_deassert(struct reset_controller_dev *rcdev,
				    unsigned long id)
{
	return meson_audio_arb_update(rcdev, id, false);
}

static const struct reset_control_ops meson_audio_arb_rstc_ops = {
	.assert = meson_audio_arb_assert,
	.deassert = meson_audio_arb_deassert,
	.status = meson_audio_arb_status,
};

static const struct of_device_id meson_audio_arb_of_match[] = {
	{ .compatible = "amlogic,meson-axg-audio-arb", },
	{}
};
MODULE_DEVICE_TABLE(of, meson_audio_arb_of_match);

static int meson_audio_arb_remove(struct platform_device *pdev)
{
	struct meson_audio_arb_data *arb = platform_get_drvdata(pdev);

	/* Disable all access */
	spin_lock(&arb->lock);
	writel(0, arb->regs);
	spin_unlock(&arb->lock);

	clk_disable_unprepare(arb->clk);

	return 0;
}

static int meson_audio_arb_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct meson_audio_arb_data *arb;
	struct resource *res;
	int ret;

	arb = devm_kzalloc(dev, sizeof(*arb), GFP_KERNEL);
	if (!arb)
		return -ENOMEM;
	platform_set_drvdata(pdev, arb);

	arb->clk = devm_clk_get(dev, NULL);
	if (IS_ERR(arb->clk)) {
		if (PTR_ERR(arb->clk) != -EPROBE_DEFER)
			dev_err(dev, "failed to get clock\n");
		return PTR_ERR(arb->clk);
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	arb->regs = devm_ioremap_resource(dev, res);
	if (IS_ERR(arb->regs))
		return PTR_ERR(arb->regs);

	spin_lock_init(&arb->lock);
	arb->reset_bits = axg_audio_arb_reset_bits;
	arb->rstc.nr_resets = ARRAY_SIZE(axg_audio_arb_reset_bits);
	arb->rstc.ops = &meson_audio_arb_rstc_ops;
	arb->rstc.of_node = dev->of_node;
	arb->rstc.owner = THIS_MODULE;

	/*
	 * Enable general :
	 * In the initial state, all memory interfaces are disabled
	 * and the general bit is on
	 */
	ret = clk_prepare_enable(arb->clk);
	if (ret) {
		dev_err(dev, "failed to enable arb clock\n");
		return ret;
	}
	writel(BIT(ARB_GENERAL_BIT), arb->regs);

	/* Register reset controller */
	ret = devm_reset_controller_register(dev, &arb->rstc);
	if (ret) {
		dev_err(dev, "failed to register arb reset controller\n");
		meson_audio_arb_remove(pdev);
	}

	return ret;
}

static struct platform_driver meson_audio_arb_pdrv = {
	.probe = meson_audio_arb_probe,
	.remove = meson_audio_arb_remove,
	.driver = {
		.name = "meson-audio-arb-reset",
		.of_match_table = meson_audio_arb_of_match,
	},
};
module_platform_driver(meson_audio_arb_pdrv);

MODULE_DESCRIPTION("Amlogic A113 Audio Memory Arbiter");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
MODULE_LICENSE("GPL v2");
OpenPOWER on IntegriCloud