asoc: tegra: init cache values for dam/ahub/apbif registers
[linux-2.6.git] / sound / soc / tegra / tegra30_ahub.c
1 /*
2  * tegra30_ahub.c - Tegra 30 AHUB driver
3  *
4  * Author: Stephen Warren <swarren@nvidia.com>
5  * Copyright (C) 2011 - NVIDIA, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19  * 02110-1301 USA
20  *
21  */
22
23 #include <linux/clk.h>
24 #include <linux/module.h>
25 #include <linux/debugfs.h>
26 #include <linux/device.h>
27 #include <linux/platform_device.h>
28 #include <linux/seq_file.h>
29 #include <linux/slab.h>
30 #include <linux/io.h>
31 #include <mach/dma.h>
32 #include <mach/iomap.h>
33 #include <sound/soc.h>
34 #include "tegra30_ahub.h"
35
36 #define DRV_NAME "tegra30-ahub"
37
38 static struct tegra30_ahub *ahub;
39
40 static inline void tegra30_apbif_write(u32 reg, u32 val)
41 {
42 #ifdef CONFIG_PM
43         ahub->apbif_reg_cache[reg >> 2] = val;
44 #endif
45         __raw_writel(val, ahub->apbif_regs + reg);
46 }
47
48 static inline u32 tegra30_apbif_read(u32 reg)
49 {
50         return __raw_readl(ahub->apbif_regs + reg);
51 }
52
53 static inline void tegra30_audio_write(u32 reg, u32 val)
54 {
55 #ifdef CONFIG_PM
56         ahub->ahub_reg_cache[reg >> 2] = val;
57 #endif
58         __raw_writel(val, ahub->audio_regs + reg);
59 }
60
61 static inline u32 tegra30_audio_read(u32 reg)
62 {
63         return __raw_readl(ahub->audio_regs + reg);
64 }
65
66 #ifdef CONFIG_PM
67 int tegra30_ahub_apbif_resume()
68 {
69         int i = 0;
70         int cache_idx_rsvd;
71
72         tegra30_ahub_enable_clocks();
73
74         /*restore ahub regs*/
75         for (i = 0; i < TEGRA30_AHUB_AUDIO_RX_COUNT; i++)
76                 tegra30_audio_write(i<<2, ahub->ahub_reg_cache[i]);
77
78         /*restore apbif regs*/
79         cache_idx_rsvd = TEGRA30_APBIF_CACHE_REG_INDEX_RSVD;
80         for (i = 0; i < TEGRA30_APBIF_CACHE_REG_COUNT; i++) {
81                 if (i == cache_idx_rsvd) {
82                         cache_idx_rsvd +=
83                                 TEGRA30_APBIF_CACHE_REG_INDEX_RSVD_STRIDE;
84                         continue;
85                 }
86
87                 tegra30_apbif_write(i<<2, ahub->apbif_reg_cache[i]);
88         }
89
90         tegra30_ahub_disable_clocks();
91
92         return 0;
93 }
94 #endif
95
96 /*
97  * clk_apbif isn't required for a theoretical I2S<->I2S configuration where
98  * no PCM data is read from or sent to memory. However, that's an unlikely
99  * use-case, and not something the rest of the driver supports right now, so
100  * we'll just treat the two clocks as one for now.
101  *
102  * This function should not be a plain ref-count. Instead, each active stream
103  * contributes some requirement to the minimum clock rate, so starting or
104  * stopping streams should dynamically adjust the clock as required.  However,
105  * this is not yet implemented.
106  */
107 void tegra30_ahub_enable_clocks(void)
108 {
109         clk_enable(ahub->clk_d_audio);
110         clk_enable(ahub->clk_apbif);
111 }
112
113 void tegra30_ahub_disable_clocks(void)
114 {
115         clk_disable(ahub->clk_apbif);
116         clk_disable(ahub->clk_d_audio);
117 }
118
119 #ifdef CONFIG_DEBUG_FS
120 static inline u32 tegra30_ahub_read(u32 space, u32 reg)
121 {
122         if (space == 0)
123                 return tegra30_apbif_read(reg);
124         else
125                 return tegra30_audio_read(reg);
126 }
127
128 static int tegra30_ahub_show(struct seq_file *s, void *unused)
129 {
130 #define REG(space, r) { space, r, 0,          1,         #r }
131 #define ARR(space, r) { space, r, r##_STRIDE, r##_COUNT, #r }
132         static const struct {
133                 int space;
134                 u32 offset;
135                 u32 stride;
136                 u32 count;
137                 const char *name;
138         } regs[] = {
139                 ARR(0, TEGRA30_AHUB_CHANNEL_CTRL),
140                 ARR(0, TEGRA30_AHUB_CHANNEL_CLEAR),
141                 ARR(0, TEGRA30_AHUB_CHANNEL_STATUS),
142                 ARR(0, TEGRA30_AHUB_CIF_TX_CTRL),
143                 ARR(0, TEGRA30_AHUB_CIF_RX_CTRL),
144                 REG(0, TEGRA30_AHUB_CONFIG_LINK_CTRL),
145                 REG(0, TEGRA30_AHUB_MISC_CTRL),
146                 REG(0, TEGRA30_AHUB_APBDMA_LIVE_STATUS),
147                 REG(0, TEGRA30_AHUB_I2S_LIVE_STATUS),
148                 ARR(0, TEGRA30_AHUB_DAM_LIVE_STATUS),
149                 REG(0, TEGRA30_AHUB_SPDIF_LIVE_STATUS),
150                 REG(0, TEGRA30_AHUB_I2S_INT_MASK),
151                 REG(0, TEGRA30_AHUB_DAM_INT_MASK),
152                 REG(0, TEGRA30_AHUB_SPDIF_INT_MASK),
153                 REG(0, TEGRA30_AHUB_APBIF_INT_MASK),
154                 REG(0, TEGRA30_AHUB_I2S_INT_STATUS),
155                 REG(0, TEGRA30_AHUB_DAM_INT_STATUS),
156                 REG(0, TEGRA30_AHUB_SPDIF_INT_STATUS),
157                 REG(0, TEGRA30_AHUB_APBIF_INT_STATUS),
158                 REG(0, TEGRA30_AHUB_I2S_INT_SOURCE),
159                 REG(0, TEGRA30_AHUB_DAM_INT_SOURCE),
160                 REG(0, TEGRA30_AHUB_SPDIF_INT_SOURCE),
161                 REG(0, TEGRA30_AHUB_APBIF_INT_SOURCE),
162                 REG(0, TEGRA30_AHUB_I2S_INT_SET),
163                 REG(0, TEGRA30_AHUB_DAM_INT_SET),
164                 REG(0, TEGRA30_AHUB_SPDIF_INT_SET),
165                 REG(0, TEGRA30_AHUB_APBIF_INT_SET),
166                 ARR(1, TEGRA30_AHUB_AUDIO_RX),
167         };
168 #undef ARRG
169 #undef REG
170
171         int i, j;
172
173         tegra30_ahub_enable_clocks();
174
175         for (i = 0; i < ARRAY_SIZE(regs); i++) {
176                 if (regs[i].count > 1) {
177                         for (j = 0; j < regs[i].count; j++) {
178                                 u32 reg = regs[i].offset + (j * regs[i].stride);
179                                 u32 val = tegra30_ahub_read(regs[i].space, reg);
180                                 seq_printf(s, "%s[%d] = %08x\n", regs[i].name,
181                                            j, val);
182                         }
183                 } else {
184                         u32 val = tegra30_ahub_read(regs[i].space,
185                                                   regs[i].offset);
186                         seq_printf(s, "%s = %08x\n", regs[i].name, val);
187                 }
188         }
189
190         tegra30_ahub_disable_clocks();
191
192         return 0;
193 }
194
195 static int tegra30_ahub_debug_open(struct inode *inode, struct file *file)
196 {
197         return single_open(file, tegra30_ahub_show, inode->i_private);
198 }
199
200 static const struct file_operations tegra30_ahub_debug_fops = {
201         .open    = tegra30_ahub_debug_open,
202         .read    = seq_read,
203         .llseek  = seq_lseek,
204         .release = single_release,
205 };
206
207 static void tegra30_ahub_debug_add(struct tegra30_ahub *ahub)
208 {
209         ahub->debug = debugfs_create_file(DRV_NAME, S_IRUGO,
210                                           snd_soc_debugfs_root, ahub,
211                                           &tegra30_ahub_debug_fops);
212 }
213
214 static void tegra30_ahub_debug_remove(struct tegra30_ahub *ahub)
215 {
216         if (ahub->debug)
217                 debugfs_remove(ahub->debug);
218 }
219 #else
220 static inline void tegra30_ahub_debug_add(struct tegra30_ahub *ahub)
221 {
222 }
223
224 static inline void tegra30_ahub_debug_remove(struct tegra30_ahub *ahub)
225 {
226 }
227 #endif
228
229 int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
230                                   unsigned long *fiforeg,
231                                   unsigned long *reqsel)
232 {
233         int channel;
234         u32 reg, val;
235
236         channel = find_first_zero_bit(ahub->rx_usage,
237                                       TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
238         if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT)
239                 return -EBUSY;
240
241         __set_bit(channel, ahub->rx_usage);
242
243         *rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel;
244         *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO +
245                    (channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE);
246         *reqsel = TEGRA_DMA_REQ_SEL_APBIF_CH0 + channel;
247
248         tegra30_ahub_enable_clocks();
249
250         reg = TEGRA30_AHUB_CHANNEL_CTRL +
251               (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
252         val = tegra30_apbif_read(reg);
253         val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK |
254                TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK);
255         val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) |
256                TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN |
257                TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16;
258         tegra30_apbif_write(reg, val);
259
260         reg = TEGRA30_AHUB_CIF_RX_CTRL +
261               (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE);
262         val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
263               (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
264               (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
265               TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 |
266               TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 |
267               TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX;
268         tegra30_apbif_write(reg, val);
269
270         tegra30_ahub_disable_clocks();
271
272         return 0;
273 }
274
275 int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif)
276 {
277         int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
278         int reg, val;
279
280         tegra30_ahub_enable_clocks();
281
282         reg = TEGRA30_AHUB_CHANNEL_CTRL +
283               (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
284         val = tegra30_apbif_read(reg);
285         val |= TEGRA30_AHUB_CHANNEL_CTRL_RX_EN;
286         tegra30_apbif_write(reg, val);
287
288         return 0;
289 }
290
291 int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif)
292 {
293         int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
294         int reg, val;
295
296         reg = TEGRA30_AHUB_CHANNEL_CTRL +
297               (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
298         val = tegra30_apbif_read(reg);
299         val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_EN;
300         tegra30_apbif_write(reg, val);
301
302         tegra30_ahub_disable_clocks();
303
304         return 0;
305 }
306
307 int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif)
308 {
309         int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
310
311         __clear_bit(channel, ahub->rx_usage);
312
313         return 0;
314 }
315
316 int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
317                                   unsigned long *fiforeg,
318                                   unsigned long *reqsel)
319 {
320         int channel;
321         u32 reg, val;
322
323         channel = find_first_zero_bit(ahub->tx_usage,
324                                       TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
325         if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT)
326                 return -EBUSY;
327
328         __set_bit(channel, ahub->tx_usage);
329
330         *txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel;
331         *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO +
332                    (channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE);
333         *reqsel = TEGRA_DMA_REQ_SEL_APBIF_CH0 + channel;
334
335         tegra30_ahub_enable_clocks();
336
337         reg = TEGRA30_AHUB_CHANNEL_CTRL +
338               (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
339         val = tegra30_apbif_read(reg);
340         val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK |
341                TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK);
342         val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) |
343                TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN |
344                TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16;
345         tegra30_apbif_write(reg, val);
346
347         reg = TEGRA30_AHUB_CIF_TX_CTRL +
348               (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE);
349         val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
350               (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
351               (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
352               TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 |
353               TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 |
354               TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX;
355         tegra30_apbif_write(reg, val);
356
357         tegra30_ahub_disable_clocks();
358
359         return 0;
360 }
361
362 int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif)
363 {
364         int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
365         int reg, val;
366
367         tegra30_ahub_enable_clocks();
368
369         reg = TEGRA30_AHUB_CHANNEL_CTRL +
370               (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
371         val = tegra30_apbif_read(reg);
372         val |= TEGRA30_AHUB_CHANNEL_CTRL_TX_EN;
373         tegra30_apbif_write(reg, val);
374
375         return 0;
376 }
377
378 int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif)
379 {
380         int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
381         int reg, val;
382
383         reg = TEGRA30_AHUB_CHANNEL_CTRL +
384               (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
385         val = tegra30_apbif_read(reg);
386         val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_EN;
387         tegra30_apbif_write(reg, val);
388
389         tegra30_ahub_disable_clocks();
390
391         return 0;
392 }
393
394 int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif)
395 {
396         int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
397
398         __clear_bit(channel, ahub->tx_usage);
399
400         return 0;
401 }
402
403 int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif,
404                                    enum tegra30_ahub_txcif txcif)
405 {
406         int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
407         int reg;
408
409         tegra30_ahub_enable_clocks();
410
411         reg = TEGRA30_AHUB_AUDIO_RX +
412               (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE);
413         tegra30_audio_write(reg, 1 << txcif);
414
415         tegra30_ahub_disable_clocks();
416
417         return 0;
418 }
419
420 int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif)
421 {
422         int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
423         int reg;
424
425         tegra30_ahub_enable_clocks();
426
427         reg = TEGRA30_AHUB_AUDIO_RX +
428               (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE);
429         tegra30_audio_write(reg, 0);
430
431         tegra30_ahub_disable_clocks();
432
433         return 0;
434 }
435
436 int tegra30_ahub_set_rx_cif_channels(enum tegra30_ahub_rxcif rxcif,
437                                      unsigned int audio_ch,
438                                      unsigned int client_ch)
439 {
440         int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
441         unsigned int reg, val;
442
443         tegra30_ahub_enable_clocks();
444
445         reg = TEGRA30_AHUB_CIF_RX_CTRL +
446               (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE);
447         val = tegra30_apbif_read(reg);
448         val &= ~(TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK |
449                 TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK);
450         val |= ((audio_ch - 1) << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
451               ((client_ch - 1) << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT);
452         tegra30_apbif_write(reg, val);
453
454         tegra30_ahub_disable_clocks();
455
456         return 0;
457 }
458
459 int tegra30_ahub_set_tx_cif_channels(enum tegra30_ahub_txcif txcif,
460                                      unsigned int audio_ch,
461                                      unsigned int client_ch)
462 {
463         int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
464         unsigned int reg, val;
465
466         tegra30_ahub_enable_clocks();
467
468         reg = TEGRA30_AHUB_CIF_TX_CTRL +
469               (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE);
470         val = tegra30_apbif_read(reg);
471         val &= ~(TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK |
472                 TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK);
473         val |= ((audio_ch - 1) << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
474               ((client_ch - 1) << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT);
475
476         tegra30_apbif_write(reg, val);
477
478         tegra30_ahub_disable_clocks();
479
480         return 0;
481 }
482
483 static int __devinit tegra30_ahub_probe(struct platform_device *pdev)
484 {
485         struct resource *res0, *res1, *region;
486         int ret = 0, i = 0, cache_idx_rsvd;
487
488         if (ahub)
489                 return -ENODEV;
490
491         ahub = kzalloc(sizeof(struct tegra30_ahub), GFP_KERNEL);
492         if (!ahub) {
493                 dev_err(&pdev->dev, "Can't allocate tegra30_ahub\n");
494                 ret = -ENOMEM;
495                 goto exit;
496         }
497         ahub->dev = &pdev->dev;
498
499         ahub->clk_d_audio = clk_get(&pdev->dev, "d_audio");
500         if (IS_ERR(ahub->clk_d_audio)) {
501                 dev_err(&pdev->dev, "Can't retrieve ahub d_audio clock\n");
502                 ret = PTR_ERR(ahub->clk_d_audio);
503                 goto err_free;
504         }
505
506         ahub->clk_apbif = clk_get(&pdev->dev, "apbif");
507         if (IS_ERR(ahub->clk_apbif)) {
508                 dev_err(&pdev->dev, "Can't retrieve ahub apbif clock\n");
509                 ret = PTR_ERR(ahub->clk_apbif);
510                 goto err_clk_put_d_audio;
511         }
512
513         res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
514         if (!res0) {
515                 dev_err(&pdev->dev, "No memory 0 resource\n");
516                 ret = -ENODEV;
517                 goto err_clk_put_apbif;
518         }
519
520         region = request_mem_region(res0->start, resource_size(res0),
521                                     pdev->name);
522         if (!region) {
523                 dev_err(&pdev->dev, "Memory region 0 already claimed\n");
524                 ret = -EBUSY;
525                 goto err_clk_put_apbif;
526         }
527
528         ahub->apbif_regs = ioremap(res0->start, resource_size(res0));
529         if (!ahub->apbif_regs) {
530                 dev_err(&pdev->dev, "ioremap 0 failed\n");
531                 ret = -ENOMEM;
532                 goto err_release0;
533         }
534
535         ahub->apbif_addr = res0->start;
536
537         res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
538         if (!res1) {
539                 dev_err(&pdev->dev, "No memory 1 resource\n");
540                 ret = -ENODEV;
541                 goto err_unmap0;
542         }
543
544         region = request_mem_region(res1->start, resource_size(res1),
545                                     pdev->name);
546         if (!region) {
547                 dev_err(&pdev->dev, "Memory region 1 already claimed\n");
548                 ret = -EBUSY;
549                 goto err_unmap0;
550         }
551
552         ahub->audio_regs = ioremap(res1->start, resource_size(res1));
553         if (!ahub->audio_regs) {
554                 dev_err(&pdev->dev, "ioremap 1 failed\n");
555                 ret = -ENOMEM;
556                 goto err_release1;
557         }
558
559         /* cache the POR values of ahub/apbif regs*/
560         tegra30_ahub_enable_clocks();
561
562         for (i = 0; i < TEGRA30_AHUB_AUDIO_RX_COUNT; i++)
563                 ahub->ahub_reg_cache[i] = tegra30_audio_read(i<<2);
564
565         cache_idx_rsvd = TEGRA30_APBIF_CACHE_REG_INDEX_RSVD;
566         for (i = 0; i < TEGRA30_APBIF_CACHE_REG_COUNT; i++) {
567                 if (i == cache_idx_rsvd) {
568                         cache_idx_rsvd +=
569                                 TEGRA30_APBIF_CACHE_REG_INDEX_RSVD_STRIDE;
570                         continue;
571                 }
572
573                 ahub->apbif_reg_cache[i] = tegra30_apbif_read(i<<2);
574         }
575
576         tegra30_ahub_disable_clocks();
577
578         tegra30_ahub_debug_add(ahub);
579
580         platform_set_drvdata(pdev, ahub);
581
582         return 0;
583
584 err_release1:
585         release_mem_region(res1->start, resource_size(res1));
586 err_unmap0:
587         iounmap(ahub->apbif_regs);
588 err_release0:
589         release_mem_region(res0->start, resource_size(res0));
590 err_clk_put_apbif:
591         clk_put(ahub->clk_apbif);
592 err_clk_put_d_audio:
593         clk_put(ahub->clk_d_audio);
594 err_free:
595         kfree(ahub);
596         ahub = 0;
597 exit:
598         return ret;
599 }
600
601 static int __devexit tegra30_ahub_remove(struct platform_device *pdev)
602 {
603         struct resource *res;
604
605         if (!ahub)
606                 return -ENODEV;
607
608         platform_set_drvdata(pdev, NULL);
609
610         tegra30_ahub_debug_remove(ahub);
611
612         iounmap(ahub->audio_regs);
613
614         res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
615         release_mem_region(res->start, resource_size(res));
616
617         iounmap(ahub->apbif_regs);
618
619         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
620         release_mem_region(res->start, resource_size(res));
621
622         clk_put(ahub->clk_apbif);
623         clk_put(ahub->clk_d_audio);
624
625         kfree(ahub);
626         ahub = 0;
627
628         return 0;
629 }
630
631 static struct platform_driver tegra30_ahub_driver = {
632         .probe = tegra30_ahub_probe,
633         .remove = __devexit_p(tegra30_ahub_remove),
634         .driver = {
635                 .name = DRV_NAME,
636         },
637 };
638
639 static int __init tegra30_ahub_modinit(void)
640 {
641         return platform_driver_register(&tegra30_ahub_driver);
642 }
643 module_init(tegra30_ahub_modinit);
644
645 static void __exit tegra30_ahub_modexit(void)
646 {
647         platform_driver_unregister(&tegra30_ahub_driver);
648 }
649 module_exit(tegra30_ahub_modexit);
650
651 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
652 MODULE_DESCRIPTION("Tegra 30 AHUB driver");
653 MODULE_LICENSE("GPL");
654 MODULE_ALIAS("platform:" DRV_NAME);