1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | // Copyright (c) 2018 BayLibre, SAS. |
3 | // Author: Jerome Brunet <jbrunet@baylibre.com> |
4 | |
5 | #include <linux/clk.h> |
6 | #include <linux/io.h> |
7 | #include <linux/module.h> |
8 | #include <linux/of.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/reset-controller.h> |
11 | #include <linux/spinlock.h> |
12 | |
13 | #include <dt-bindings/reset/amlogic,meson-axg-audio-arb.h> |
14 | |
15 | struct meson_audio_arb_data { |
16 | struct reset_controller_dev rstc; |
17 | void __iomem *regs; |
18 | struct clk *clk; |
19 | const unsigned int *reset_bits; |
20 | spinlock_t lock; |
21 | }; |
22 | |
23 | struct meson_audio_arb_match_data { |
24 | const unsigned int *reset_bits; |
25 | unsigned int reset_num; |
26 | }; |
27 | |
28 | #define ARB_GENERAL_BIT 31 |
29 | |
30 | static const unsigned int axg_audio_arb_reset_bits[] = { |
31 | [AXG_ARB_TODDR_A] = 0, |
32 | [AXG_ARB_TODDR_B] = 1, |
33 | [AXG_ARB_TODDR_C] = 2, |
34 | [AXG_ARB_FRDDR_A] = 4, |
35 | [AXG_ARB_FRDDR_B] = 5, |
36 | [AXG_ARB_FRDDR_C] = 6, |
37 | }; |
38 | |
39 | static const struct meson_audio_arb_match_data axg_audio_arb_match = { |
40 | .reset_bits = axg_audio_arb_reset_bits, |
41 | .reset_num = ARRAY_SIZE(axg_audio_arb_reset_bits), |
42 | }; |
43 | |
44 | static const unsigned int sm1_audio_arb_reset_bits[] = { |
45 | [AXG_ARB_TODDR_A] = 0, |
46 | [AXG_ARB_TODDR_B] = 1, |
47 | [AXG_ARB_TODDR_C] = 2, |
48 | [AXG_ARB_FRDDR_A] = 4, |
49 | [AXG_ARB_FRDDR_B] = 5, |
50 | [AXG_ARB_FRDDR_C] = 6, |
51 | [AXG_ARB_TODDR_D] = 3, |
52 | [AXG_ARB_FRDDR_D] = 7, |
53 | }; |
54 | |
55 | static const struct meson_audio_arb_match_data sm1_audio_arb_match = { |
56 | .reset_bits = sm1_audio_arb_reset_bits, |
57 | .reset_num = ARRAY_SIZE(sm1_audio_arb_reset_bits), |
58 | }; |
59 | |
60 | static int meson_audio_arb_update(struct reset_controller_dev *rcdev, |
61 | unsigned long id, bool assert) |
62 | { |
63 | u32 val; |
64 | struct meson_audio_arb_data *arb = |
65 | container_of(rcdev, struct meson_audio_arb_data, rstc); |
66 | |
67 | spin_lock(lock: &arb->lock); |
68 | val = readl(addr: arb->regs); |
69 | |
70 | if (assert) |
71 | val &= ~BIT(arb->reset_bits[id]); |
72 | else |
73 | val |= BIT(arb->reset_bits[id]); |
74 | |
75 | writel(val, addr: arb->regs); |
76 | spin_unlock(lock: &arb->lock); |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | static int meson_audio_arb_status(struct reset_controller_dev *rcdev, |
82 | unsigned long id) |
83 | { |
84 | u32 val; |
85 | struct meson_audio_arb_data *arb = |
86 | container_of(rcdev, struct meson_audio_arb_data, rstc); |
87 | |
88 | val = readl(addr: arb->regs); |
89 | |
90 | return !(val & BIT(arb->reset_bits[id])); |
91 | } |
92 | |
93 | static int meson_audio_arb_assert(struct reset_controller_dev *rcdev, |
94 | unsigned long id) |
95 | { |
96 | return meson_audio_arb_update(rcdev, id, assert: true); |
97 | } |
98 | |
99 | static int meson_audio_arb_deassert(struct reset_controller_dev *rcdev, |
100 | unsigned long id) |
101 | { |
102 | return meson_audio_arb_update(rcdev, id, assert: false); |
103 | } |
104 | |
105 | static const struct reset_control_ops meson_audio_arb_rstc_ops = { |
106 | .assert = meson_audio_arb_assert, |
107 | .deassert = meson_audio_arb_deassert, |
108 | .status = meson_audio_arb_status, |
109 | }; |
110 | |
111 | static const struct of_device_id meson_audio_arb_of_match[] = { |
112 | { |
113 | .compatible = "amlogic,meson-axg-audio-arb" , |
114 | .data = &axg_audio_arb_match, |
115 | }, { |
116 | .compatible = "amlogic,meson-sm1-audio-arb" , |
117 | .data = &sm1_audio_arb_match, |
118 | }, |
119 | {} |
120 | }; |
121 | MODULE_DEVICE_TABLE(of, meson_audio_arb_of_match); |
122 | |
123 | static int meson_audio_arb_remove(struct platform_device *pdev) |
124 | { |
125 | struct meson_audio_arb_data *arb = platform_get_drvdata(pdev); |
126 | |
127 | /* Disable all access */ |
128 | spin_lock(lock: &arb->lock); |
129 | writel(val: 0, addr: arb->regs); |
130 | spin_unlock(lock: &arb->lock); |
131 | |
132 | clk_disable_unprepare(clk: arb->clk); |
133 | |
134 | return 0; |
135 | } |
136 | |
137 | static int meson_audio_arb_probe(struct platform_device *pdev) |
138 | { |
139 | struct device *dev = &pdev->dev; |
140 | const struct meson_audio_arb_match_data *data; |
141 | struct meson_audio_arb_data *arb; |
142 | struct resource *res; |
143 | int ret; |
144 | |
145 | data = of_device_get_match_data(dev); |
146 | if (!data) |
147 | return -EINVAL; |
148 | |
149 | arb = devm_kzalloc(dev, size: sizeof(*arb), GFP_KERNEL); |
150 | if (!arb) |
151 | return -ENOMEM; |
152 | platform_set_drvdata(pdev, data: arb); |
153 | |
154 | arb->clk = devm_clk_get(dev, NULL); |
155 | if (IS_ERR(ptr: arb->clk)) |
156 | return dev_err_probe(dev, err: PTR_ERR(ptr: arb->clk), fmt: "failed to get clock\n" ); |
157 | |
158 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
159 | arb->regs = devm_ioremap_resource(dev, res); |
160 | if (IS_ERR(ptr: arb->regs)) |
161 | return PTR_ERR(ptr: arb->regs); |
162 | |
163 | spin_lock_init(&arb->lock); |
164 | arb->reset_bits = data->reset_bits; |
165 | arb->rstc.nr_resets = data->reset_num; |
166 | arb->rstc.ops = &meson_audio_arb_rstc_ops; |
167 | arb->rstc.of_node = dev->of_node; |
168 | arb->rstc.owner = THIS_MODULE; |
169 | |
170 | /* |
171 | * Enable general : |
172 | * In the initial state, all memory interfaces are disabled |
173 | * and the general bit is on |
174 | */ |
175 | ret = clk_prepare_enable(clk: arb->clk); |
176 | if (ret) { |
177 | dev_err(dev, "failed to enable arb clock\n" ); |
178 | return ret; |
179 | } |
180 | writel(BIT(ARB_GENERAL_BIT), addr: arb->regs); |
181 | |
182 | /* Register reset controller */ |
183 | ret = devm_reset_controller_register(dev, rcdev: &arb->rstc); |
184 | if (ret) { |
185 | dev_err(dev, "failed to register arb reset controller\n" ); |
186 | meson_audio_arb_remove(pdev); |
187 | } |
188 | |
189 | return ret; |
190 | } |
191 | |
192 | static struct platform_driver meson_audio_arb_pdrv = { |
193 | .probe = meson_audio_arb_probe, |
194 | .remove = meson_audio_arb_remove, |
195 | .driver = { |
196 | .name = "meson-audio-arb-reset" , |
197 | .of_match_table = meson_audio_arb_of_match, |
198 | }, |
199 | }; |
200 | module_platform_driver(meson_audio_arb_pdrv); |
201 | |
202 | MODULE_DESCRIPTION("Amlogic A113 Audio Memory Arbiter" ); |
203 | MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>" ); |
204 | MODULE_LICENSE("GPL v2" ); |
205 | |