Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
9c636342 DB |
2 | /* |
3 | * Based on sound/arm/pxa2xx-ac97.c and sound/soc/pxa/pxa2xx-ac97.c | |
4 | * which contain: | |
5 | * | |
6 | * Author: Nicolas Pitre | |
7 | * Created: Dec 02, 2004 | |
8 | * Copyright: MontaVista Software Inc. | |
9c636342 DB |
9 | */ |
10 | ||
11 | #include <linux/kernel.h> | |
12 | #include <linux/platform_device.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/clk.h> | |
15 | #include <linux/delay.h> | |
da155d5b | 16 | #include <linux/module.h> |
23019a73 | 17 | #include <linux/io.h> |
3b4bc7bc | 18 | #include <linux/gpio.h> |
a4519526 | 19 | #include <linux/of_gpio.h> |
08d3df8c | 20 | #include <linux/soc/pxa/cpu.h> |
9c636342 | 21 | |
9c636342 DB |
22 | #include <sound/pxa2xx-lib.h> |
23 | ||
22f08665 | 24 | #include <linux/platform_data/asoc-pxa.h> |
9c636342 | 25 | |
8ff06452 AB |
26 | #include "pxa2xx-ac97-regs.h" |
27 | ||
9c636342 DB |
28 | static DEFINE_MUTEX(car_mutex); |
29 | static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); | |
30 | static volatile long gsr_bits; | |
31 | static struct clk *ac97_clk; | |
9c636342 | 32 | static struct clk *ac97conf_clk; |
26ade896 | 33 | static int reset_gpio; |
8ff06452 | 34 | static void __iomem *ac97_reg_base; |
9c636342 | 35 | |
053fe0f1 | 36 | extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio); |
fb1bf8cd | 37 | |
9c636342 DB |
38 | /* |
39 | * Beware PXA27x bugs: | |
40 | * | |
41 | * o Slot 12 read from modem space will hang controller. | |
42 | * o CDONE, SDONE interrupt fails after any slot 12 IO. | |
43 | * | |
44 | * We therefore have an hybrid approach for waiting on SDONE (interrupt or | |
45 | * 1 jiffy timeout if interrupt never comes). | |
46 | */ | |
47 | ||
6f8acad6 | 48 | int pxa2xx_ac97_read(int slot, unsigned short reg) |
9c636342 | 49 | { |
6f8acad6 | 50 | int val = -ENODEV; |
8ff06452 | 51 | u32 __iomem *reg_addr; |
9c636342 | 52 | |
6f8acad6 RJ |
53 | if (slot > 0) |
54 | return -ENODEV; | |
55 | ||
9c636342 DB |
56 | mutex_lock(&car_mutex); |
57 | ||
58 | /* set up primary or secondary codec space */ | |
8825e8e8 | 59 | if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS) |
8ff06452 AB |
60 | reg_addr = ac97_reg_base + |
61 | (slot ? SMC_REG_BASE : PMC_REG_BASE); | |
9c636342 | 62 | else |
8ff06452 AB |
63 | reg_addr = ac97_reg_base + |
64 | (slot ? SAC_REG_BASE : PAC_REG_BASE); | |
9c636342 DB |
65 | reg_addr += (reg >> 1); |
66 | ||
67 | /* start read access across the ac97 link */ | |
8ff06452 | 68 | writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR); |
9c636342 | 69 | gsr_bits = 0; |
8ff06452 | 70 | val = (readl(reg_addr) & 0xffff); |
9c636342 DB |
71 | if (reg == AC97_GPIO_STATUS) |
72 | goto out; | |
8ff06452 AB |
73 | if (wait_event_timeout(gsr_wq, (readl(ac97_reg_base + GSR) | gsr_bits) & GSR_SDONE, 1) <= 0 && |
74 | !((readl(ac97_reg_base + GSR) | gsr_bits) & GSR_SDONE)) { | |
9c636342 | 75 | printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n", |
8ff06452 | 76 | __func__, reg, readl(ac97_reg_base + GSR) | gsr_bits); |
6f8acad6 | 77 | val = -ETIMEDOUT; |
9c636342 DB |
78 | goto out; |
79 | } | |
80 | ||
81 | /* valid data now */ | |
8ff06452 | 82 | writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR); |
9c636342 | 83 | gsr_bits = 0; |
8ff06452 | 84 | val = (readl(reg_addr) & 0xffff); |
9c636342 | 85 | /* but we've just started another cycle... */ |
8ff06452 | 86 | wait_event_timeout(gsr_wq, (readl(ac97_reg_base + GSR) | gsr_bits) & GSR_SDONE, 1); |
9c636342 DB |
87 | |
88 | out: mutex_unlock(&car_mutex); | |
89 | return val; | |
90 | } | |
91 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_read); | |
92 | ||
6f8acad6 | 93 | int pxa2xx_ac97_write(int slot, unsigned short reg, unsigned short val) |
9c636342 | 94 | { |
8ff06452 | 95 | u32 __iomem *reg_addr; |
6f8acad6 | 96 | int ret = 0; |
9c636342 DB |
97 | |
98 | mutex_lock(&car_mutex); | |
99 | ||
100 | /* set up primary or secondary codec space */ | |
8825e8e8 | 101 | if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS) |
8ff06452 AB |
102 | reg_addr = ac97_reg_base + |
103 | (slot ? SMC_REG_BASE : PMC_REG_BASE); | |
9c636342 | 104 | else |
8ff06452 AB |
105 | reg_addr = ac97_reg_base + |
106 | (slot ? SAC_REG_BASE : PAC_REG_BASE); | |
9c636342 DB |
107 | reg_addr += (reg >> 1); |
108 | ||
8ff06452 | 109 | writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR); |
9c636342 | 110 | gsr_bits = 0; |
8ff06452 AB |
111 | writel(val, reg_addr); |
112 | if (wait_event_timeout(gsr_wq, (readl(ac97_reg_base + GSR) | gsr_bits) & GSR_CDONE, 1) <= 0 && | |
113 | !((readl(ac97_reg_base + GSR) | gsr_bits) & GSR_CDONE)) { | |
9c636342 | 114 | printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n", |
8ff06452 | 115 | __func__, reg, readl(ac97_reg_base + GSR) | gsr_bits); |
6f8acad6 RJ |
116 | ret = -EIO; |
117 | } | |
9c636342 DB |
118 | |
119 | mutex_unlock(&car_mutex); | |
6f8acad6 | 120 | return ret; |
9c636342 DB |
121 | } |
122 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_write); | |
123 | ||
9d1cf39b DB |
124 | #ifdef CONFIG_PXA25x |
125 | static inline void pxa_ac97_warm_pxa25x(void) | |
9c636342 | 126 | { |
9c636342 DB |
127 | gsr_bits = 0; |
128 | ||
8ff06452 | 129 | writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR); |
9d1cf39b DB |
130 | } |
131 | ||
132 | static inline void pxa_ac97_cold_pxa25x(void) | |
133 | { | |
8ff06452 AB |
134 | writel(readl(ac97_reg_base + GCR) & ( GCR_COLD_RST), ac97_reg_base + GCR); /* clear everything but nCRST */ |
135 | writel(readl(ac97_reg_base + GCR) & (~GCR_COLD_RST), ac97_reg_base + GCR); /* then assert nCRST */ | |
9d1cf39b DB |
136 | |
137 | gsr_bits = 0; | |
138 | ||
8ff06452 | 139 | writel(GCR_COLD_RST, ac97_reg_base + GCR); |
9d1cf39b DB |
140 | } |
141 | #endif | |
142 | ||
9c636342 | 143 | #ifdef CONFIG_PXA27x |
9d1cf39b DB |
144 | static inline void pxa_ac97_warm_pxa27x(void) |
145 | { | |
146 | gsr_bits = 0; | |
147 | ||
fb1bf8cd | 148 | /* warm reset broken on Bulverde, so manually keep AC97 reset high */ |
053fe0f1 | 149 | pxa27x_configure_ac97reset(reset_gpio, true); |
9c636342 | 150 | udelay(10); |
8ff06452 | 151 | writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR); |
053fe0f1 | 152 | pxa27x_configure_ac97reset(reset_gpio, false); |
9c636342 | 153 | udelay(500); |
9d1cf39b DB |
154 | } |
155 | ||
156 | static inline void pxa_ac97_cold_pxa27x(void) | |
157 | { | |
8ff06452 AB |
158 | writel(readl(ac97_reg_base + GCR) & ( GCR_COLD_RST), ac97_reg_base + GCR); /* clear everything but nCRST */ |
159 | writel(readl(ac97_reg_base + GCR) & (~GCR_COLD_RST), ac97_reg_base + GCR); /* then assert nCRST */ | |
9d1cf39b DB |
160 | |
161 | gsr_bits = 0; | |
162 | ||
163 | /* PXA27x Developers Manual section 13.5.2.2.1 */ | |
4091d342 | 164 | clk_prepare_enable(ac97conf_clk); |
9d1cf39b | 165 | udelay(5); |
4091d342 | 166 | clk_disable_unprepare(ac97conf_clk); |
8ff06452 | 167 | writel(GCR_COLD_RST | GCR_WARM_RST, ac97_reg_base + GCR); |
9d1cf39b | 168 | } |
9c636342 DB |
169 | #endif |
170 | ||
9d1cf39b DB |
171 | #ifdef CONFIG_PXA3xx |
172 | static inline void pxa_ac97_warm_pxa3xx(void) | |
173 | { | |
9d1cf39b | 174 | gsr_bits = 0; |
9c636342 | 175 | |
9d1cf39b | 176 | /* Can't use interrupts */ |
8ff06452 | 177 | writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR); |
9c636342 | 178 | } |
9c636342 | 179 | |
9d1cf39b | 180 | static inline void pxa_ac97_cold_pxa3xx(void) |
9c636342 | 181 | { |
9c636342 | 182 | /* Hold CLKBPB for 100us */ |
8ff06452 AB |
183 | writel(0, ac97_reg_base + GCR); |
184 | writel(GCR_CLKBPB, ac97_reg_base + GCR); | |
9c636342 | 185 | udelay(100); |
8ff06452 | 186 | writel(0, ac97_reg_base + GCR); |
9c636342 | 187 | |
8ff06452 AB |
188 | writel(readl(ac97_reg_base + GCR) & ( GCR_COLD_RST), ac97_reg_base + GCR); /* clear everything but nCRST */ |
189 | writel(readl(ac97_reg_base + GCR) & (~GCR_COLD_RST), ac97_reg_base + GCR); /* then assert nCRST */ | |
9c636342 DB |
190 | |
191 | gsr_bits = 0; | |
9d1cf39b | 192 | |
9c636342 | 193 | /* Can't use interrupts on PXA3xx */ |
8ff06452 | 194 | writel(readl(ac97_reg_base + GCR) & (~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN)), ac97_reg_base + GCR); |
9c636342 | 195 | |
8ff06452 | 196 | writel(GCR_WARM_RST | GCR_COLD_RST, ac97_reg_base + GCR); |
9d1cf39b DB |
197 | } |
198 | #endif | |
199 | ||
6f8acad6 | 200 | bool pxa2xx_ac97_try_warm_reset(void) |
9d1cf39b | 201 | { |
057de50c | 202 | unsigned long gsr; |
beb02cdd | 203 | unsigned int timeout = 100; |
057de50c | 204 | |
9d1cf39b | 205 | #ifdef CONFIG_PXA25x |
8825e8e8 | 206 | if (cpu_is_pxa25x()) |
9d1cf39b DB |
207 | pxa_ac97_warm_pxa25x(); |
208 | else | |
9c636342 | 209 | #endif |
9d1cf39b DB |
210 | #ifdef CONFIG_PXA27x |
211 | if (cpu_is_pxa27x()) | |
212 | pxa_ac97_warm_pxa27x(); | |
213 | else | |
214 | #endif | |
215 | #ifdef CONFIG_PXA3xx | |
216 | if (cpu_is_pxa3xx()) | |
217 | pxa_ac97_warm_pxa3xx(); | |
218 | else | |
219 | #endif | |
88ec7ae8 | 220 | snd_BUG(); |
beb02cdd | 221 | |
8ff06452 | 222 | while (!((readl(ac97_reg_base + GSR) | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) |
beb02cdd DES |
223 | mdelay(1); |
224 | ||
8ff06452 | 225 | gsr = readl(ac97_reg_base + GSR) | gsr_bits; |
057de50c | 226 | if (!(gsr & (GSR_PCR | GSR_SCR))) { |
9d1cf39b | 227 | printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", |
057de50c | 228 | __func__, gsr); |
9d1cf39b DB |
229 | |
230 | return false; | |
231 | } | |
232 | ||
233 | return true; | |
234 | } | |
235 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset); | |
236 | ||
6f8acad6 | 237 | bool pxa2xx_ac97_try_cold_reset(void) |
9d1cf39b | 238 | { |
057de50c | 239 | unsigned long gsr; |
beb02cdd | 240 | unsigned int timeout = 1000; |
057de50c | 241 | |
9d1cf39b | 242 | #ifdef CONFIG_PXA25x |
8825e8e8 | 243 | if (cpu_is_pxa25x()) |
9d1cf39b DB |
244 | pxa_ac97_cold_pxa25x(); |
245 | else | |
246 | #endif | |
247 | #ifdef CONFIG_PXA27x | |
248 | if (cpu_is_pxa27x()) | |
249 | pxa_ac97_cold_pxa27x(); | |
250 | else | |
251 | #endif | |
252 | #ifdef CONFIG_PXA3xx | |
253 | if (cpu_is_pxa3xx()) | |
254 | pxa_ac97_cold_pxa3xx(); | |
255 | else | |
256 | #endif | |
88ec7ae8 | 257 | snd_BUG(); |
9c636342 | 258 | |
8ff06452 | 259 | while (!((readl(ac97_reg_base + GSR) | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) |
beb02cdd DES |
260 | mdelay(1); |
261 | ||
8ff06452 | 262 | gsr = readl(ac97_reg_base + GSR) | gsr_bits; |
057de50c | 263 | if (!(gsr & (GSR_PCR | GSR_SCR))) { |
9c636342 | 264 | printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n", |
057de50c | 265 | __func__, gsr); |
9c636342 DB |
266 | |
267 | return false; | |
268 | } | |
269 | ||
270 | return true; | |
271 | } | |
272 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset); | |
273 | ||
274 | ||
6f8acad6 | 275 | void pxa2xx_ac97_finish_reset(void) |
9c636342 | 276 | { |
8ff06452 AB |
277 | u32 gcr = readl(ac97_reg_base + GCR); |
278 | gcr &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); | |
279 | gcr |= GCR_SDONE_IE|GCR_CDONE_IE; | |
280 | writel(gcr, ac97_reg_base + GCR); | |
9c636342 DB |
281 | } |
282 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_finish_reset); | |
283 | ||
284 | static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id) | |
285 | { | |
286 | long status; | |
287 | ||
8ff06452 | 288 | status = readl(ac97_reg_base + GSR); |
9c636342 | 289 | if (status) { |
8ff06452 | 290 | writel(status, ac97_reg_base + GSR); |
9c636342 DB |
291 | gsr_bits |= status; |
292 | wake_up(&gsr_wq); | |
293 | ||
9c636342 DB |
294 | /* Although we don't use those we still need to clear them |
295 | since they tend to spuriously trigger when MMC is used | |
296 | (hardware bug? go figure)... */ | |
9d1cf39b | 297 | if (cpu_is_pxa27x()) { |
8ff06452 AB |
298 | writel(MISR_EOC, ac97_reg_base + MISR); |
299 | writel(PISR_EOC, ac97_reg_base + PISR); | |
300 | writel(MCSR_EOC, ac97_reg_base + MCSR); | |
9d1cf39b | 301 | } |
9c636342 DB |
302 | |
303 | return IRQ_HANDLED; | |
304 | } | |
305 | ||
306 | return IRQ_NONE; | |
307 | } | |
308 | ||
309 | #ifdef CONFIG_PM | |
310 | int pxa2xx_ac97_hw_suspend(void) | |
311 | { | |
8ff06452 | 312 | writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR); |
4091d342 | 313 | clk_disable_unprepare(ac97_clk); |
9c636342 DB |
314 | return 0; |
315 | } | |
316 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_suspend); | |
317 | ||
318 | int pxa2xx_ac97_hw_resume(void) | |
319 | { | |
4091d342 | 320 | clk_prepare_enable(ac97_clk); |
9c636342 DB |
321 | return 0; |
322 | } | |
323 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_resume); | |
324 | #endif | |
325 | ||
e21596bb | 326 | int pxa2xx_ac97_hw_probe(struct platform_device *dev) |
9c636342 DB |
327 | { |
328 | int ret; | |
2548e6c7 | 329 | int irq; |
eae17754 | 330 | pxa2xx_audio_ops_t *pdata = dev->dev.platform_data; |
26ade896 | 331 | |
8ff06452 AB |
332 | ac97_reg_base = devm_platform_ioremap_resource(dev, 0); |
333 | if (IS_ERR(ac97_reg_base)) { | |
334 | dev_err(&dev->dev, "Missing MMIO resource\n"); | |
335 | return PTR_ERR(ac97_reg_base); | |
336 | } | |
337 | ||
26ade896 RJ |
338 | if (pdata) { |
339 | switch (pdata->reset_gpio) { | |
340 | case 95: | |
341 | case 113: | |
342 | reset_gpio = pdata->reset_gpio; | |
343 | break; | |
344 | case 0: | |
345 | reset_gpio = 113; | |
346 | break; | |
347 | case -1: | |
348 | break; | |
349 | default: | |
1f218695 | 350 | dev_err(&dev->dev, "Invalid reset GPIO %d\n", |
26ade896 RJ |
351 | pdata->reset_gpio); |
352 | } | |
a4519526 RJ |
353 | } else if (!pdata && dev->dev.of_node) { |
354 | pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL); | |
355 | if (!pdata) | |
356 | return -ENOMEM; | |
357 | pdata->reset_gpio = of_get_named_gpio(dev->dev.of_node, | |
358 | "reset-gpios", 0); | |
359 | if (pdata->reset_gpio == -ENOENT) | |
360 | pdata->reset_gpio = -1; | |
361 | else if (pdata->reset_gpio < 0) | |
362 | return pdata->reset_gpio; | |
363 | reset_gpio = pdata->reset_gpio; | |
26ade896 RJ |
364 | } else { |
365 | if (cpu_is_pxa27x()) | |
366 | reset_gpio = 113; | |
367 | } | |
9c636342 | 368 | |
9d1cf39b | 369 | if (cpu_is_pxa27x()) { |
3b4bc7bc MD |
370 | /* |
371 | * This gpio is needed for a work-around to a bug in the ac97 | |
372 | * controller during warm reset. The direction and level is set | |
373 | * here so that it is an output driven high when switching from | |
374 | * AC97_nRESET alt function to generic gpio. | |
375 | */ | |
376 | ret = gpio_request_one(reset_gpio, GPIOF_OUT_INIT_HIGH, | |
377 | "pxa27x ac97 reset"); | |
378 | if (ret < 0) { | |
379 | pr_err("%s: gpio_request_one() failed: %d\n", | |
380 | __func__, ret); | |
381 | goto err_conf; | |
382 | } | |
053fe0f1 | 383 | pxa27x_configure_ac97reset(reset_gpio, false); |
3b4bc7bc | 384 | |
9d1cf39b DB |
385 | ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK"); |
386 | if (IS_ERR(ac97conf_clk)) { | |
387 | ret = PTR_ERR(ac97conf_clk); | |
388 | ac97conf_clk = NULL; | |
79612336 | 389 | goto err_conf; |
9d1cf39b | 390 | } |
9c636342 | 391 | } |
9c636342 DB |
392 | |
393 | ac97_clk = clk_get(&dev->dev, "AC97CLK"); | |
394 | if (IS_ERR(ac97_clk)) { | |
395 | ret = PTR_ERR(ac97_clk); | |
396 | ac97_clk = NULL; | |
79612336 | 397 | goto err_clk; |
9c636342 DB |
398 | } |
399 | ||
4091d342 | 400 | ret = clk_prepare_enable(ac97_clk); |
79612336 DB |
401 | if (ret) |
402 | goto err_clk2; | |
403 | ||
2548e6c7 | 404 | irq = platform_get_irq(dev, 0); |
46cf1954 YY |
405 | if (irq < 0) { |
406 | ret = irq; | |
2548e6c7 | 407 | goto err_irq; |
46cf1954 | 408 | } |
2548e6c7 AB |
409 | |
410 | ret = request_irq(irq, pxa2xx_ac97_irq, 0, "AC97", NULL); | |
79612336 DB |
411 | if (ret < 0) |
412 | goto err_irq; | |
413 | ||
414 | return 0; | |
9c636342 DB |
415 | |
416 | err_irq: | |
8ff06452 | 417 | writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR); |
79612336 DB |
418 | err_clk2: |
419 | clk_put(ac97_clk); | |
420 | ac97_clk = NULL; | |
421 | err_clk: | |
9c636342 DB |
422 | if (ac97conf_clk) { |
423 | clk_put(ac97conf_clk); | |
424 | ac97conf_clk = NULL; | |
425 | } | |
79612336 | 426 | err_conf: |
9c636342 DB |
427 | return ret; |
428 | } | |
429 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe); | |
430 | ||
431 | void pxa2xx_ac97_hw_remove(struct platform_device *dev) | |
432 | { | |
3b4bc7bc MD |
433 | if (cpu_is_pxa27x()) |
434 | gpio_free(reset_gpio); | |
8ff06452 | 435 | writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR); |
2548e6c7 | 436 | free_irq(platform_get_irq(dev, 0), NULL); |
9d1cf39b DB |
437 | if (ac97conf_clk) { |
438 | clk_put(ac97conf_clk); | |
439 | ac97conf_clk = NULL; | |
440 | } | |
4091d342 | 441 | clk_disable_unprepare(ac97_clk); |
9c636342 DB |
442 | clk_put(ac97_clk); |
443 | ac97_clk = NULL; | |
444 | } | |
445 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_remove); | |
446 | ||
e217b085 AB |
447 | u32 pxa2xx_ac97_read_modr(void) |
448 | { | |
8ff06452 AB |
449 | if (!ac97_reg_base) |
450 | return 0; | |
451 | ||
452 | return readl(ac97_reg_base + MODR); | |
e217b085 AB |
453 | } |
454 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_read_modr); | |
455 | ||
456 | u32 pxa2xx_ac97_read_misr(void) | |
457 | { | |
8ff06452 AB |
458 | if (!ac97_reg_base) |
459 | return 0; | |
460 | ||
461 | return readl(ac97_reg_base + MISR); | |
e217b085 AB |
462 | } |
463 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_read_misr); | |
464 | ||
9c636342 DB |
465 | MODULE_AUTHOR("Nicolas Pitre"); |
466 | MODULE_DESCRIPTION("Intel/Marvell PXA sound library"); | |
467 | MODULE_LICENSE("GPL"); | |
468 |