Commit | Line | Data |
---|---|---|
be944d42 SW |
1 | /* |
2 | * tegra30_ahub.c - Tegra30 AHUB driver | |
3 | * | |
4 | * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include <linux/clk.h> | |
20 | #include <linux/device.h> | |
21 | #include <linux/io.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/of_platform.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/pm_runtime.h> | |
26 | #include <linux/regmap.h> | |
27 | #include <linux/slab.h> | |
28 | #include <mach/clk.h> | |
29 | #include <mach/dma.h> | |
30 | #include <sound/soc.h> | |
31 | #include "tegra30_ahub.h" | |
32 | ||
33 | #define DRV_NAME "tegra30-ahub" | |
34 | ||
35 | static struct tegra30_ahub *ahub; | |
36 | ||
37 | static inline void tegra30_apbif_write(u32 reg, u32 val) | |
38 | { | |
39 | regmap_write(ahub->regmap_apbif, reg, val); | |
40 | } | |
41 | ||
42 | static inline u32 tegra30_apbif_read(u32 reg) | |
43 | { | |
44 | u32 val; | |
45 | regmap_read(ahub->regmap_apbif, reg, &val); | |
46 | return val; | |
47 | } | |
48 | ||
49 | static inline void tegra30_audio_write(u32 reg, u32 val) | |
50 | { | |
51 | regmap_write(ahub->regmap_ahub, reg, val); | |
52 | } | |
53 | ||
54 | static int tegra30_ahub_runtime_suspend(struct device *dev) | |
55 | { | |
56 | regcache_cache_only(ahub->regmap_apbif, true); | |
57 | regcache_cache_only(ahub->regmap_ahub, true); | |
58 | ||
59 | clk_disable(ahub->clk_apbif); | |
60 | clk_disable(ahub->clk_d_audio); | |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
65 | /* | |
66 | * clk_apbif isn't required for an I2S<->I2S configuration where no PCM data | |
67 | * is read from or sent to memory. However, that's not something the rest of | |
68 | * the driver supports right now, so we'll just treat the two clocks as one | |
69 | * for now. | |
70 | * | |
71 | * These functions should not be a plain ref-count. Instead, each active stream | |
72 | * contributes some requirement to the minimum clock rate, so starting or | |
73 | * stopping streams should dynamically adjust the clock as required. However, | |
74 | * this is not yet implemented. | |
75 | */ | |
76 | static int tegra30_ahub_runtime_resume(struct device *dev) | |
77 | { | |
78 | int ret; | |
79 | ||
80 | ret = clk_enable(ahub->clk_d_audio); | |
81 | if (ret) { | |
82 | dev_err(dev, "clk_enable d_audio failed: %d\n", ret); | |
83 | return ret; | |
84 | } | |
85 | ret = clk_enable(ahub->clk_apbif); | |
86 | if (ret) { | |
87 | dev_err(dev, "clk_enable apbif failed: %d\n", ret); | |
88 | clk_disable(ahub->clk_d_audio); | |
89 | return ret; | |
90 | } | |
91 | ||
92 | regcache_cache_only(ahub->regmap_apbif, false); | |
93 | regcache_cache_only(ahub->regmap_ahub, false); | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
98 | int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, | |
99 | unsigned long *fiforeg, | |
100 | unsigned long *reqsel) | |
101 | { | |
102 | int channel; | |
103 | u32 reg, val; | |
104 | ||
105 | channel = find_first_zero_bit(ahub->rx_usage, | |
106 | TEGRA30_AHUB_CHANNEL_CTRL_COUNT); | |
107 | if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) | |
108 | return -EBUSY; | |
109 | ||
110 | __set_bit(channel, ahub->rx_usage); | |
111 | ||
112 | *rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel; | |
113 | *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO + | |
114 | (channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE); | |
115 | *reqsel = ahub->dma_sel + channel; | |
116 | ||
117 | reg = TEGRA30_AHUB_CHANNEL_CTRL + | |
118 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); | |
119 | val = tegra30_apbif_read(reg); | |
120 | val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK | | |
121 | TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK); | |
122 | val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) | | |
123 | TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN | | |
124 | TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16; | |
125 | tegra30_apbif_write(reg, val); | |
126 | ||
127 | reg = TEGRA30_AHUB_CIF_RX_CTRL + | |
128 | (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE); | |
129 | val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | | |
130 | (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | | |
131 | (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | | |
132 | TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 | | |
133 | TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 | | |
134 | TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX; | |
135 | tegra30_apbif_write(reg, val); | |
136 | ||
137 | return 0; | |
138 | } | |
139 | EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_rx_fifo); | |
140 | ||
141 | int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif) | |
142 | { | |
143 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; | |
144 | int reg, val; | |
145 | ||
146 | reg = TEGRA30_AHUB_CHANNEL_CTRL + | |
147 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); | |
148 | val = tegra30_apbif_read(reg); | |
149 | val |= TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; | |
150 | tegra30_apbif_write(reg, val); | |
151 | ||
152 | return 0; | |
153 | } | |
154 | EXPORT_SYMBOL_GPL(tegra30_ahub_enable_rx_fifo); | |
155 | ||
156 | int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif) | |
157 | { | |
158 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; | |
159 | int reg, val; | |
160 | ||
161 | reg = TEGRA30_AHUB_CHANNEL_CTRL + | |
162 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); | |
163 | val = tegra30_apbif_read(reg); | |
164 | val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; | |
165 | tegra30_apbif_write(reg, val); | |
166 | ||
167 | return 0; | |
168 | } | |
169 | EXPORT_SYMBOL_GPL(tegra30_ahub_disable_rx_fifo); | |
170 | ||
171 | int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif) | |
172 | { | |
173 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; | |
174 | ||
175 | __clear_bit(channel, ahub->rx_usage); | |
176 | ||
177 | return 0; | |
178 | } | |
179 | EXPORT_SYMBOL_GPL(tegra30_ahub_free_rx_fifo); | |
180 | ||
181 | int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, | |
182 | unsigned long *fiforeg, | |
183 | unsigned long *reqsel) | |
184 | { | |
185 | int channel; | |
186 | u32 reg, val; | |
187 | ||
188 | channel = find_first_zero_bit(ahub->tx_usage, | |
189 | TEGRA30_AHUB_CHANNEL_CTRL_COUNT); | |
190 | if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT) | |
191 | return -EBUSY; | |
192 | ||
193 | __set_bit(channel, ahub->tx_usage); | |
194 | ||
195 | *txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel; | |
196 | *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO + | |
197 | (channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE); | |
198 | *reqsel = ahub->dma_sel + channel; | |
199 | ||
200 | reg = TEGRA30_AHUB_CHANNEL_CTRL + | |
201 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); | |
202 | val = tegra30_apbif_read(reg); | |
203 | val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK | | |
204 | TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK); | |
205 | val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) | | |
206 | TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN | | |
207 | TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16; | |
208 | tegra30_apbif_write(reg, val); | |
209 | ||
210 | reg = TEGRA30_AHUB_CIF_TX_CTRL + | |
211 | (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE); | |
212 | val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | | |
213 | (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | | |
214 | (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | | |
215 | TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 | | |
216 | TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 | | |
217 | TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX; | |
218 | tegra30_apbif_write(reg, val); | |
219 | ||
220 | return 0; | |
221 | } | |
222 | EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_tx_fifo); | |
223 | ||
224 | int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif) | |
225 | { | |
226 | int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; | |
227 | int reg, val; | |
228 | ||
229 | reg = TEGRA30_AHUB_CHANNEL_CTRL + | |
230 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); | |
231 | val = tegra30_apbif_read(reg); | |
232 | val |= TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; | |
233 | tegra30_apbif_write(reg, val); | |
234 | ||
235 | return 0; | |
236 | } | |
237 | EXPORT_SYMBOL_GPL(tegra30_ahub_enable_tx_fifo); | |
238 | ||
239 | int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif) | |
240 | { | |
241 | int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; | |
242 | int reg, val; | |
243 | ||
244 | reg = TEGRA30_AHUB_CHANNEL_CTRL + | |
245 | (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); | |
246 | val = tegra30_apbif_read(reg); | |
247 | val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; | |
248 | tegra30_apbif_write(reg, val); | |
249 | ||
250 | return 0; | |
251 | } | |
252 | EXPORT_SYMBOL_GPL(tegra30_ahub_disable_tx_fifo); | |
253 | ||
254 | int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif) | |
255 | { | |
256 | int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; | |
257 | ||
258 | __clear_bit(channel, ahub->tx_usage); | |
259 | ||
260 | return 0; | |
261 | } | |
262 | EXPORT_SYMBOL_GPL(tegra30_ahub_free_tx_fifo); | |
263 | ||
264 | int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif, | |
265 | enum tegra30_ahub_txcif txcif) | |
266 | { | |
267 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; | |
268 | int reg; | |
269 | ||
270 | reg = TEGRA30_AHUB_AUDIO_RX + | |
271 | (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); | |
272 | tegra30_audio_write(reg, 1 << txcif); | |
273 | ||
274 | return 0; | |
275 | } | |
276 | EXPORT_SYMBOL_GPL(tegra30_ahub_set_rx_cif_source); | |
277 | ||
278 | int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif) | |
279 | { | |
280 | int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; | |
281 | int reg; | |
282 | ||
283 | reg = TEGRA30_AHUB_AUDIO_RX + | |
284 | (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); | |
285 | tegra30_audio_write(reg, 0); | |
286 | ||
287 | return 0; | |
288 | } | |
289 | EXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source); | |
290 | ||
291 | static const char * const configlink_clocks[] __devinitconst = { | |
292 | "i2s0", | |
293 | "i2s1", | |
294 | "i2s2", | |
295 | "i2s3", | |
296 | "i2s4", | |
297 | "dam0", | |
298 | "dam1", | |
299 | "dam2", | |
300 | "spdif_in", | |
301 | }; | |
302 | ||
303 | struct of_dev_auxdata ahub_auxdata[] __devinitdata = { | |
304 | OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080300, "tegra30-i2s.0", NULL), | |
305 | OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080400, "tegra30-i2s.1", NULL), | |
306 | OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080500, "tegra30-i2s.2", NULL), | |
307 | OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080600, "tegra30-i2s.3", NULL), | |
308 | OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080700, "tegra30-i2s.4", NULL), | |
309 | {} | |
310 | }; | |
311 | ||
312 | #define LAST_REG(name) \ | |
313 | (TEGRA30_AHUB_##name + \ | |
314 | (TEGRA30_AHUB_##name##_STRIDE * TEGRA30_AHUB_##name##_COUNT) - 4) | |
315 | ||
316 | #define REG_IN_ARRAY(reg, name) \ | |
317 | ((reg >= TEGRA30_AHUB_##name) && \ | |
318 | (reg <= LAST_REG(name) && \ | |
319 | (!((reg - TEGRA30_AHUB_##name) % TEGRA30_AHUB_##name##_STRIDE)))) | |
320 | ||
321 | static bool tegra30_ahub_apbif_wr_rd_reg(struct device *dev, unsigned int reg) | |
322 | { | |
323 | switch (reg) { | |
324 | case TEGRA30_AHUB_CONFIG_LINK_CTRL: | |
325 | case TEGRA30_AHUB_MISC_CTRL: | |
326 | case TEGRA30_AHUB_APBDMA_LIVE_STATUS: | |
327 | case TEGRA30_AHUB_I2S_LIVE_STATUS: | |
328 | case TEGRA30_AHUB_SPDIF_LIVE_STATUS: | |
329 | case TEGRA30_AHUB_I2S_INT_MASK: | |
330 | case TEGRA30_AHUB_DAM_INT_MASK: | |
331 | case TEGRA30_AHUB_SPDIF_INT_MASK: | |
332 | case TEGRA30_AHUB_APBIF_INT_MASK: | |
333 | case TEGRA30_AHUB_I2S_INT_STATUS: | |
334 | case TEGRA30_AHUB_DAM_INT_STATUS: | |
335 | case TEGRA30_AHUB_SPDIF_INT_STATUS: | |
336 | case TEGRA30_AHUB_APBIF_INT_STATUS: | |
337 | case TEGRA30_AHUB_I2S_INT_SOURCE: | |
338 | case TEGRA30_AHUB_DAM_INT_SOURCE: | |
339 | case TEGRA30_AHUB_SPDIF_INT_SOURCE: | |
340 | case TEGRA30_AHUB_APBIF_INT_SOURCE: | |
341 | case TEGRA30_AHUB_I2S_INT_SET: | |
342 | case TEGRA30_AHUB_DAM_INT_SET: | |
343 | case TEGRA30_AHUB_SPDIF_INT_SET: | |
344 | case TEGRA30_AHUB_APBIF_INT_SET: | |
345 | return true; | |
346 | default: | |
347 | break; | |
348 | }; | |
349 | ||
350 | if (REG_IN_ARRAY(reg, CHANNEL_CTRL) || | |
351 | REG_IN_ARRAY(reg, CHANNEL_CLEAR) || | |
352 | REG_IN_ARRAY(reg, CHANNEL_STATUS) || | |
353 | REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || | |
354 | REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || | |
355 | REG_IN_ARRAY(reg, CIF_TX_CTRL) || | |
356 | REG_IN_ARRAY(reg, CIF_RX_CTRL) || | |
357 | REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) | |
358 | return true; | |
359 | ||
360 | return false; | |
361 | } | |
362 | ||
363 | static bool tegra30_ahub_apbif_volatile_reg(struct device *dev, | |
364 | unsigned int reg) | |
365 | { | |
366 | switch (reg) { | |
367 | case TEGRA30_AHUB_CONFIG_LINK_CTRL: | |
368 | case TEGRA30_AHUB_MISC_CTRL: | |
369 | case TEGRA30_AHUB_APBDMA_LIVE_STATUS: | |
370 | case TEGRA30_AHUB_I2S_LIVE_STATUS: | |
371 | case TEGRA30_AHUB_SPDIF_LIVE_STATUS: | |
372 | case TEGRA30_AHUB_I2S_INT_STATUS: | |
373 | case TEGRA30_AHUB_DAM_INT_STATUS: | |
374 | case TEGRA30_AHUB_SPDIF_INT_STATUS: | |
375 | case TEGRA30_AHUB_APBIF_INT_STATUS: | |
376 | case TEGRA30_AHUB_I2S_INT_SET: | |
377 | case TEGRA30_AHUB_DAM_INT_SET: | |
378 | case TEGRA30_AHUB_SPDIF_INT_SET: | |
379 | case TEGRA30_AHUB_APBIF_INT_SET: | |
380 | return true; | |
381 | default: | |
382 | break; | |
383 | }; | |
384 | ||
385 | if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) || | |
386 | REG_IN_ARRAY(reg, CHANNEL_STATUS) || | |
387 | REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || | |
388 | REG_IN_ARRAY(reg, CHANNEL_RXFIFO) || | |
389 | REG_IN_ARRAY(reg, DAM_LIVE_STATUS)) | |
390 | return true; | |
391 | ||
392 | return false; | |
393 | } | |
394 | ||
395 | static bool tegra30_ahub_apbif_precious_reg(struct device *dev, | |
396 | unsigned int reg) | |
397 | { | |
398 | if (REG_IN_ARRAY(reg, CHANNEL_TXFIFO) || | |
399 | REG_IN_ARRAY(reg, CHANNEL_RXFIFO)) | |
400 | return true; | |
401 | ||
402 | return false; | |
403 | } | |
404 | ||
405 | static const struct regmap_config tegra30_ahub_apbif_regmap_config = { | |
406 | .name = "apbif", | |
407 | .reg_bits = 32, | |
408 | .val_bits = 32, | |
409 | .reg_stride = 4, | |
410 | .max_register = TEGRA30_AHUB_APBIF_INT_SET, | |
411 | .writeable_reg = tegra30_ahub_apbif_wr_rd_reg, | |
412 | .readable_reg = tegra30_ahub_apbif_wr_rd_reg, | |
413 | .volatile_reg = tegra30_ahub_apbif_volatile_reg, | |
414 | .precious_reg = tegra30_ahub_apbif_precious_reg, | |
415 | .cache_type = REGCACHE_RBTREE, | |
416 | }; | |
417 | ||
418 | static bool tegra30_ahub_ahub_wr_rd_reg(struct device *dev, unsigned int reg) | |
419 | { | |
420 | if (REG_IN_ARRAY(reg, AUDIO_RX)) | |
421 | return true; | |
422 | ||
423 | return false; | |
424 | } | |
425 | ||
426 | static const struct regmap_config tegra30_ahub_ahub_regmap_config = { | |
427 | .name = "ahub", | |
428 | .reg_bits = 32, | |
429 | .val_bits = 32, | |
430 | .reg_stride = 4, | |
431 | .max_register = LAST_REG(AUDIO_RX), | |
432 | .writeable_reg = tegra30_ahub_ahub_wr_rd_reg, | |
433 | .readable_reg = tegra30_ahub_ahub_wr_rd_reg, | |
434 | .cache_type = REGCACHE_RBTREE, | |
435 | }; | |
436 | ||
437 | static int __devinit tegra30_ahub_probe(struct platform_device *pdev) | |
438 | { | |
439 | struct clk *clk; | |
440 | int i; | |
441 | struct resource *res0, *res1, *region; | |
442 | u32 of_dma[2]; | |
443 | void __iomem *regs_apbif, *regs_ahub; | |
444 | int ret = 0; | |
445 | ||
446 | if (ahub) | |
447 | return -ENODEV; | |
448 | ||
449 | /* | |
450 | * The AHUB hosts a register bus: the "configlink". For this to | |
451 | * operate correctly, all devices on this bus must be out of reset. | |
452 | * Ensure that here. | |
453 | */ | |
454 | for (i = 0; i < ARRAY_SIZE(configlink_clocks); i++) { | |
455 | clk = clk_get_sys(NULL, configlink_clocks[i]); | |
456 | if (IS_ERR(clk)) { | |
457 | dev_err(&pdev->dev, "Can't get clock %s\n", | |
458 | configlink_clocks[i]); | |
459 | ret = PTR_ERR(clk); | |
460 | goto err; | |
461 | } | |
462 | tegra_periph_reset_deassert(clk); | |
463 | clk_put(clk); | |
464 | } | |
465 | ||
466 | ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub), | |
467 | GFP_KERNEL); | |
468 | if (!ahub) { | |
469 | dev_err(&pdev->dev, "Can't allocate tegra30_ahub\n"); | |
470 | ret = -ENOMEM; | |
471 | goto err; | |
472 | } | |
473 | dev_set_drvdata(&pdev->dev, ahub); | |
474 | ||
475 | ahub->dev = &pdev->dev; | |
476 | ||
477 | ahub->clk_d_audio = clk_get(&pdev->dev, "d_audio"); | |
478 | if (IS_ERR(ahub->clk_d_audio)) { | |
479 | dev_err(&pdev->dev, "Can't retrieve ahub d_audio clock\n"); | |
480 | ret = PTR_ERR(ahub->clk_d_audio); | |
481 | goto err; | |
482 | } | |
483 | ||
484 | ahub->clk_apbif = clk_get(&pdev->dev, "apbif"); | |
485 | if (IS_ERR(ahub->clk_apbif)) { | |
486 | dev_err(&pdev->dev, "Can't retrieve ahub apbif clock\n"); | |
487 | ret = PTR_ERR(ahub->clk_apbif); | |
488 | goto err_clk_put_d_audio; | |
489 | } | |
490 | ||
491 | if (of_property_read_u32_array(pdev->dev.of_node, | |
492 | "nvidia,dma-request-selector", | |
493 | of_dma, 2) < 0) { | |
494 | dev_err(&pdev->dev, | |
495 | "Missing property nvidia,dma-request-selector\n"); | |
496 | ret = -ENODEV; | |
497 | goto err_clk_put_d_audio; | |
498 | } | |
499 | ahub->dma_sel = of_dma[1]; | |
500 | ||
501 | res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
502 | if (!res0) { | |
503 | dev_err(&pdev->dev, "No apbif memory resource\n"); | |
504 | ret = -ENODEV; | |
505 | goto err_clk_put_apbif; | |
506 | } | |
507 | ||
508 | region = devm_request_mem_region(&pdev->dev, res0->start, | |
509 | resource_size(res0), DRV_NAME); | |
510 | if (!region) { | |
511 | dev_err(&pdev->dev, "request region apbif failed\n"); | |
512 | ret = -EBUSY; | |
513 | goto err_clk_put_apbif; | |
514 | } | |
515 | ahub->apbif_addr = res0->start; | |
516 | ||
517 | regs_apbif = devm_ioremap(&pdev->dev, res0->start, | |
518 | resource_size(res0)); | |
519 | if (!regs_apbif) { | |
520 | dev_err(&pdev->dev, "ioremap apbif failed\n"); | |
521 | ret = -ENOMEM; | |
522 | goto err_clk_put_apbif; | |
523 | } | |
524 | ||
525 | ahub->regmap_apbif = devm_regmap_init_mmio(&pdev->dev, regs_apbif, | |
526 | &tegra30_ahub_apbif_regmap_config); | |
527 | if (IS_ERR(ahub->regmap_apbif)) { | |
528 | dev_err(&pdev->dev, "apbif regmap init failed\n"); | |
529 | ret = PTR_ERR(ahub->regmap_apbif); | |
530 | goto err_clk_put_apbif; | |
531 | } | |
532 | regcache_cache_only(ahub->regmap_apbif, true); | |
533 | ||
534 | res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); | |
535 | if (!res1) { | |
536 | dev_err(&pdev->dev, "No ahub memory resource\n"); | |
537 | ret = -ENODEV; | |
538 | goto err_clk_put_apbif; | |
539 | } | |
540 | ||
541 | region = devm_request_mem_region(&pdev->dev, res1->start, | |
542 | resource_size(res1), DRV_NAME); | |
543 | if (!region) { | |
544 | dev_err(&pdev->dev, "request region ahub failed\n"); | |
545 | ret = -EBUSY; | |
546 | goto err_clk_put_apbif; | |
547 | } | |
548 | ||
549 | regs_ahub = devm_ioremap(&pdev->dev, res1->start, | |
550 | resource_size(res1)); | |
551 | if (!regs_ahub) { | |
552 | dev_err(&pdev->dev, "ioremap ahub failed\n"); | |
553 | ret = -ENOMEM; | |
554 | goto err_clk_put_apbif; | |
555 | } | |
556 | ||
557 | ahub->regmap_ahub = devm_regmap_init_mmio(&pdev->dev, regs_ahub, | |
558 | &tegra30_ahub_ahub_regmap_config); | |
559 | if (IS_ERR(ahub->regmap_ahub)) { | |
560 | dev_err(&pdev->dev, "ahub regmap init failed\n"); | |
561 | ret = PTR_ERR(ahub->regmap_ahub); | |
562 | goto err_clk_put_apbif; | |
563 | } | |
564 | regcache_cache_only(ahub->regmap_ahub, true); | |
565 | ||
566 | pm_runtime_enable(&pdev->dev); | |
567 | if (!pm_runtime_enabled(&pdev->dev)) { | |
568 | ret = tegra30_ahub_runtime_resume(&pdev->dev); | |
569 | if (ret) | |
570 | goto err_pm_disable; | |
571 | } | |
572 | ||
573 | of_platform_populate(pdev->dev.of_node, NULL, ahub_auxdata, | |
574 | &pdev->dev); | |
575 | ||
576 | return 0; | |
577 | ||
578 | err_pm_disable: | |
579 | pm_runtime_disable(&pdev->dev); | |
580 | err_clk_put_apbif: | |
581 | clk_put(ahub->clk_apbif); | |
582 | err_clk_put_d_audio: | |
583 | clk_put(ahub->clk_d_audio); | |
584 | ahub = 0; | |
585 | err: | |
586 | return ret; | |
587 | } | |
588 | ||
589 | static int __devexit tegra30_ahub_remove(struct platform_device *pdev) | |
590 | { | |
591 | if (!ahub) | |
592 | return -ENODEV; | |
593 | ||
594 | pm_runtime_disable(&pdev->dev); | |
595 | if (!pm_runtime_status_suspended(&pdev->dev)) | |
596 | tegra30_ahub_runtime_suspend(&pdev->dev); | |
597 | ||
598 | clk_put(ahub->clk_apbif); | |
599 | clk_put(ahub->clk_d_audio); | |
600 | ||
601 | ahub = 0; | |
602 | ||
603 | return 0; | |
604 | } | |
605 | ||
606 | static const struct of_device_id tegra30_ahub_of_match[] __devinitconst = { | |
607 | { .compatible = "nvidia,tegra30-ahub", }, | |
608 | {}, | |
609 | }; | |
610 | ||
611 | static const struct dev_pm_ops tegra30_ahub_pm_ops __devinitconst = { | |
612 | SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend, | |
613 | tegra30_ahub_runtime_resume, NULL) | |
614 | }; | |
615 | ||
616 | static struct platform_driver tegra30_ahub_driver = { | |
617 | .probe = tegra30_ahub_probe, | |
618 | .remove = __devexit_p(tegra30_ahub_remove), | |
619 | .driver = { | |
620 | .name = DRV_NAME, | |
621 | .owner = THIS_MODULE, | |
622 | .of_match_table = tegra30_ahub_of_match, | |
623 | .pm = &tegra30_ahub_pm_ops, | |
624 | }, | |
625 | }; | |
626 | module_platform_driver(tegra30_ahub_driver); | |
627 | ||
628 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); | |
629 | MODULE_DESCRIPTION("Tegra30 AHUB driver"); | |
630 | MODULE_LICENSE("GPL v2"); | |
631 | MODULE_ALIAS("platform:" DRV_NAME); | |
69c5b753 | 632 | MODULE_DEVICE_TABLE(of, tegra30_ahub_of_match); |