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