2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2008-09-10 05:01:17 +04:00
/*
* Based on sound / arm / pxa2xx - ac97 . c and sound / soc / pxa / pxa2xx - ac97 . c
* which contain :
*
* Author : Nicolas Pitre
* Created : Dec 02 , 2004
* Copyright : MontaVista Software Inc .
*/
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/clk.h>
# include <linux/delay.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2012-03-20 23:33:19 +04:00
# include <linux/io.h>
2013-01-08 01:55:13 +04:00
# include <linux/gpio.h>
2018-06-17 13:50:01 +03:00
# include <linux/of_gpio.h>
2019-09-01 23:26:10 +03:00
# include <linux/soc/pxa/cpu.h>
2008-09-10 05:01:17 +04:00
# include <sound/pxa2xx-lib.h>
2019-09-02 01:02:08 +03:00
# include <linux/platform_data/asoc-pxa.h>
2008-09-10 05:01:17 +04:00
2019-09-18 11:52:31 +03:00
# include "pxa2xx-ac97-regs.h"
2008-09-10 05:01:17 +04:00
static DEFINE_MUTEX ( car_mutex ) ;
static DECLARE_WAIT_QUEUE_HEAD ( gsr_wq ) ;
static volatile long gsr_bits ;
static struct clk * ac97_clk ;
static struct clk * ac97conf_clk ;
2009-03-15 16:10:54 +03:00
static int reset_gpio ;
2019-09-18 11:52:31 +03:00
static void __iomem * ac97_reg_base ;
2008-09-10 05:01:17 +04:00
/*
* Beware PXA27x bugs :
*
* o Slot 12 read from modem space will hang controller .
* o CDONE , SDONE interrupt fails after any slot 12 IO .
*
* We therefore have an hybrid approach for waiting on SDONE ( interrupt or
* 1 jiffy timeout if interrupt never comes ) .
*/
2017-09-02 22:54:06 +03:00
int pxa2xx_ac97_read ( int slot , unsigned short reg )
2008-09-10 05:01:17 +04:00
{
2017-09-02 22:54:06 +03:00
int val = - ENODEV ;
2019-09-18 11:52:31 +03:00
u32 __iomem * reg_addr ;
2008-09-10 05:01:17 +04:00
2017-09-02 22:54:06 +03:00
if ( slot > 0 )
return - ENODEV ;
2008-09-10 05:01:17 +04:00
mutex_lock ( & car_mutex ) ;
/* set up primary or secondary codec space */
2008-10-14 12:57:05 +04:00
if ( cpu_is_pxa25x ( ) & & reg = = AC97_GPIO_STATUS )
2019-09-18 11:52:31 +03:00
reg_addr = ac97_reg_base +
( slot ? SMC_REG_BASE : PMC_REG_BASE ) ;
2008-09-10 05:01:17 +04:00
else
2019-09-18 11:52:31 +03:00
reg_addr = ac97_reg_base +
( slot ? SAC_REG_BASE : PAC_REG_BASE ) ;
2008-09-10 05:01:17 +04:00
reg_addr + = ( reg > > 1 ) ;
/* start read access across the ac97 link */
2019-09-18 11:52:31 +03:00
writel ( GSR_CDONE | GSR_SDONE , ac97_reg_base + GSR ) ;
2008-09-10 05:01:17 +04:00
gsr_bits = 0 ;
2019-09-18 11:52:31 +03:00
val = ( readl ( reg_addr ) & 0xffff ) ;
2008-09-10 05:01:17 +04:00
if ( reg = = AC97_GPIO_STATUS )
goto out ;
2019-09-18 11:52:31 +03:00
if ( wait_event_timeout ( gsr_wq , ( readl ( ac97_reg_base + GSR ) | gsr_bits ) & GSR_SDONE , 1 ) < = 0 & &
! ( ( readl ( ac97_reg_base + GSR ) | gsr_bits ) & GSR_SDONE ) ) {
2008-09-10 05:01:17 +04:00
printk ( KERN_ERR " %s: read error (ac97_reg=%d GSR=%#lx) \n " ,
2019-09-18 11:52:31 +03:00
__func__ , reg , readl ( ac97_reg_base + GSR ) | gsr_bits ) ;
2017-09-02 22:54:06 +03:00
val = - ETIMEDOUT ;
2008-09-10 05:01:17 +04:00
goto out ;
}
/* valid data now */
2019-09-18 11:52:31 +03:00
writel ( GSR_CDONE | GSR_SDONE , ac97_reg_base + GSR ) ;
2008-09-10 05:01:17 +04:00
gsr_bits = 0 ;
2019-09-18 11:52:31 +03:00
val = ( readl ( reg_addr ) & 0xffff ) ;
2008-09-10 05:01:17 +04:00
/* but we've just started another cycle... */
2019-09-18 11:52:31 +03:00
wait_event_timeout ( gsr_wq , ( readl ( ac97_reg_base + GSR ) | gsr_bits ) & GSR_SDONE , 1 ) ;
2008-09-10 05:01:17 +04:00
out : mutex_unlock ( & car_mutex ) ;
return val ;
}
EXPORT_SYMBOL_GPL ( pxa2xx_ac97_read ) ;
2017-09-02 22:54:06 +03:00
int pxa2xx_ac97_write ( int slot , unsigned short reg , unsigned short val )
2008-09-10 05:01:17 +04:00
{
2019-09-18 11:52:31 +03:00
u32 __iomem * reg_addr ;
2017-09-02 22:54:06 +03:00
int ret = 0 ;
2008-09-10 05:01:17 +04:00
mutex_lock ( & car_mutex ) ;
/* set up primary or secondary codec space */
2008-10-14 12:57:05 +04:00
if ( cpu_is_pxa25x ( ) & & reg = = AC97_GPIO_STATUS )
2019-09-18 11:52:31 +03:00
reg_addr = ac97_reg_base +
( slot ? SMC_REG_BASE : PMC_REG_BASE ) ;
2008-09-10 05:01:17 +04:00
else
2019-09-18 11:52:31 +03:00
reg_addr = ac97_reg_base +
( slot ? SAC_REG_BASE : PAC_REG_BASE ) ;
2008-09-10 05:01:17 +04:00
reg_addr + = ( reg > > 1 ) ;
2019-09-18 11:52:31 +03:00
writel ( GSR_CDONE | GSR_SDONE , ac97_reg_base + GSR ) ;
2008-09-10 05:01:17 +04:00
gsr_bits = 0 ;
2019-09-18 11:52:31 +03:00
writel ( val , reg_addr ) ;
if ( wait_event_timeout ( gsr_wq , ( readl ( ac97_reg_base + GSR ) | gsr_bits ) & GSR_CDONE , 1 ) < = 0 & &
! ( ( readl ( ac97_reg_base + GSR ) | gsr_bits ) & GSR_CDONE ) ) {
2008-09-10 05:01:17 +04:00
printk ( KERN_ERR " %s: write error (ac97_reg=%d GSR=%#lx) \n " ,
2019-09-18 11:52:31 +03:00
__func__ , reg , readl ( ac97_reg_base + GSR ) | gsr_bits ) ;
2017-09-02 22:54:06 +03:00
ret = - EIO ;
}
2008-09-10 05:01:17 +04:00
mutex_unlock ( & car_mutex ) ;
2017-09-02 22:54:06 +03:00
return ret ;
2008-09-10 05:01:17 +04:00
}
EXPORT_SYMBOL_GPL ( pxa2xx_ac97_write ) ;
2008-09-10 05:01:18 +04:00
# ifdef CONFIG_PXA25x
static inline void pxa_ac97_warm_pxa25x ( void )
2008-09-10 05:01:17 +04:00
{
gsr_bits = 0 ;
2019-09-18 11:52:31 +03:00
writel ( readl ( ac97_reg_base + GCR ) | ( GCR_WARM_RST ) , ac97_reg_base + GCR ) ;
2008-09-10 05:01:18 +04:00
}
static inline void pxa_ac97_cold_pxa25x ( void )
{
2019-09-18 11:52:31 +03:00
writel ( readl ( ac97_reg_base + GCR ) & ( GCR_COLD_RST ) , ac97_reg_base + GCR ) ; /* clear everything but nCRST */
writel ( readl ( ac97_reg_base + GCR ) & ( ~ GCR_COLD_RST ) , ac97_reg_base + GCR ) ; /* then assert nCRST */
2008-09-10 05:01:18 +04:00
gsr_bits = 0 ;
2019-09-18 11:52:31 +03:00
writel ( GCR_COLD_RST , ac97_reg_base + GCR ) ;
2008-09-10 05:01:18 +04:00
}
# endif
2008-09-10 05:01:17 +04:00
# ifdef CONFIG_PXA27x
2008-09-10 05:01:18 +04:00
static inline void pxa_ac97_warm_pxa27x ( void )
{
gsr_bits = 0 ;
2010-01-04 11:30:58 +03:00
/* warm reset broken on Bulverde, so manually keep AC97 reset high */
2013-01-08 01:55:14 +04:00
pxa27x_configure_ac97reset ( reset_gpio , true ) ;
2008-09-10 05:01:17 +04:00
udelay ( 10 ) ;
2019-09-18 11:52:31 +03:00
writel ( readl ( ac97_reg_base + GCR ) | ( GCR_WARM_RST ) , ac97_reg_base + GCR ) ;
2013-01-08 01:55:14 +04:00
pxa27x_configure_ac97reset ( reset_gpio , false ) ;
2008-09-10 05:01:17 +04:00
udelay ( 500 ) ;
2008-09-10 05:01:18 +04:00
}
static inline void pxa_ac97_cold_pxa27x ( void )
{
2019-09-18 11:52:31 +03:00
writel ( readl ( ac97_reg_base + GCR ) & ( GCR_COLD_RST ) , ac97_reg_base + GCR ) ; /* clear everything but nCRST */
writel ( readl ( ac97_reg_base + GCR ) & ( ~ GCR_COLD_RST ) , ac97_reg_base + GCR ) ; /* then assert nCRST */
2008-09-10 05:01:18 +04:00
gsr_bits = 0 ;
/* PXA27x Developers Manual section 13.5.2.2.1 */
2014-06-09 23:59:12 +04:00
clk_prepare_enable ( ac97conf_clk ) ;
2008-09-10 05:01:18 +04:00
udelay ( 5 ) ;
2014-06-09 23:59:12 +04:00
clk_disable_unprepare ( ac97conf_clk ) ;
2019-09-18 11:52:31 +03:00
writel ( GCR_COLD_RST | GCR_WARM_RST , ac97_reg_base + GCR ) ;
2008-09-10 05:01:18 +04:00
}
2008-09-10 05:01:17 +04:00
# endif
2008-09-10 05:01:18 +04:00
# ifdef CONFIG_PXA3xx
static inline void pxa_ac97_warm_pxa3xx ( void )
{
gsr_bits = 0 ;
2008-09-10 05:01:17 +04:00
2008-09-10 05:01:18 +04:00
/* Can't use interrupts */
2019-09-18 11:52:31 +03:00
writel ( readl ( ac97_reg_base + GCR ) | ( GCR_WARM_RST ) , ac97_reg_base + GCR ) ;
2008-09-10 05:01:17 +04:00
}
2008-09-10 05:01:18 +04:00
static inline void pxa_ac97_cold_pxa3xx ( void )
2008-09-10 05:01:17 +04:00
{
/* Hold CLKBPB for 100us */
2019-09-18 11:52:31 +03:00
writel ( 0 , ac97_reg_base + GCR ) ;
writel ( GCR_CLKBPB , ac97_reg_base + GCR ) ;
2008-09-10 05:01:17 +04:00
udelay ( 100 ) ;
2019-09-18 11:52:31 +03:00
writel ( 0 , ac97_reg_base + GCR ) ;
2008-09-10 05:01:17 +04:00
2019-09-18 11:52:31 +03:00
writel ( readl ( ac97_reg_base + GCR ) & ( GCR_COLD_RST ) , ac97_reg_base + GCR ) ; /* clear everything but nCRST */
writel ( readl ( ac97_reg_base + GCR ) & ( ~ GCR_COLD_RST ) , ac97_reg_base + GCR ) ; /* then assert nCRST */
2008-09-10 05:01:17 +04:00
gsr_bits = 0 ;
2008-09-10 05:01:18 +04:00
2008-09-10 05:01:17 +04:00
/* Can't use interrupts on PXA3xx */
2019-09-18 11:52:31 +03:00
writel ( readl ( ac97_reg_base + GCR ) & ( ~ ( GCR_PRIRDY_IEN | GCR_SECRDY_IEN ) ) , ac97_reg_base + GCR ) ;
2008-09-10 05:01:17 +04:00
2019-09-18 11:52:31 +03:00
writel ( GCR_WARM_RST | GCR_COLD_RST , ac97_reg_base + GCR ) ;
2008-09-10 05:01:18 +04:00
}
# endif
2017-09-02 22:54:06 +03:00
bool pxa2xx_ac97_try_warm_reset ( void )
2008-09-10 05:01:18 +04:00
{
2009-03-26 15:18:03 +03:00
unsigned long gsr ;
2013-10-17 14:01:35 +04:00
unsigned int timeout = 100 ;
2009-03-26 15:18:03 +03:00
2008-09-10 05:01:18 +04:00
# ifdef CONFIG_PXA25x
2008-10-14 12:57:05 +04:00
if ( cpu_is_pxa25x ( ) )
2008-09-10 05:01:18 +04:00
pxa_ac97_warm_pxa25x ( ) ;
else
2008-09-10 05:01:17 +04:00
# endif
2008-09-10 05:01:18 +04:00
# ifdef CONFIG_PXA27x
if ( cpu_is_pxa27x ( ) )
pxa_ac97_warm_pxa27x ( ) ;
else
# endif
# ifdef CONFIG_PXA3xx
if ( cpu_is_pxa3xx ( ) )
pxa_ac97_warm_pxa3xx ( ) ;
else
# endif
2013-11-05 18:33:40 +04:00
snd_BUG ( ) ;
2013-10-17 14:01:35 +04:00
2019-09-18 11:52:31 +03:00
while ( ! ( ( readl ( ac97_reg_base + GSR ) | gsr_bits ) & ( GSR_PCR | GSR_SCR ) ) & & timeout - - )
2013-10-17 14:01:35 +04:00
mdelay ( 1 ) ;
2019-09-18 11:52:31 +03:00
gsr = readl ( ac97_reg_base + GSR ) | gsr_bits ;
2009-03-26 15:18:03 +03:00
if ( ! ( gsr & ( GSR_PCR | GSR_SCR ) ) ) {
2008-09-10 05:01:18 +04:00
printk ( KERN_INFO " %s: warm reset timeout (GSR=%#lx) \n " ,
2009-03-26 15:18:03 +03:00
__func__ , gsr ) ;
2008-09-10 05:01:18 +04:00
return false ;
}
return true ;
}
EXPORT_SYMBOL_GPL ( pxa2xx_ac97_try_warm_reset ) ;
2017-09-02 22:54:06 +03:00
bool pxa2xx_ac97_try_cold_reset ( void )
2008-09-10 05:01:18 +04:00
{
2009-03-26 15:18:03 +03:00
unsigned long gsr ;
2013-10-17 14:01:35 +04:00
unsigned int timeout = 1000 ;
2009-03-26 15:18:03 +03:00
2008-09-10 05:01:18 +04:00
# ifdef CONFIG_PXA25x
2008-10-14 12:57:05 +04:00
if ( cpu_is_pxa25x ( ) )
2008-09-10 05:01:18 +04:00
pxa_ac97_cold_pxa25x ( ) ;
else
# endif
# ifdef CONFIG_PXA27x
if ( cpu_is_pxa27x ( ) )
pxa_ac97_cold_pxa27x ( ) ;
else
# endif
# ifdef CONFIG_PXA3xx
if ( cpu_is_pxa3xx ( ) )
pxa_ac97_cold_pxa3xx ( ) ;
else
# endif
2013-11-05 18:33:40 +04:00
snd_BUG ( ) ;
2008-09-10 05:01:17 +04:00
2019-09-18 11:52:31 +03:00
while ( ! ( ( readl ( ac97_reg_base + GSR ) | gsr_bits ) & ( GSR_PCR | GSR_SCR ) ) & & timeout - - )
2013-10-17 14:01:35 +04:00
mdelay ( 1 ) ;
2019-09-18 11:52:31 +03:00
gsr = readl ( ac97_reg_base + GSR ) | gsr_bits ;
2009-03-26 15:18:03 +03:00
if ( ! ( gsr & ( GSR_PCR | GSR_SCR ) ) ) {
2008-09-10 05:01:17 +04:00
printk ( KERN_INFO " %s: cold reset timeout (GSR=%#lx) \n " ,
2009-03-26 15:18:03 +03:00
__func__ , gsr ) ;
2008-09-10 05:01:17 +04:00
return false ;
}
return true ;
}
EXPORT_SYMBOL_GPL ( pxa2xx_ac97_try_cold_reset ) ;
2017-09-02 22:54:06 +03:00
void pxa2xx_ac97_finish_reset ( void )
2008-09-10 05:01:17 +04:00
{
2019-09-18 11:52:31 +03:00
u32 gcr = readl ( ac97_reg_base + GCR ) ;
gcr & = ~ ( GCR_PRIRDY_IEN | GCR_SECRDY_IEN ) ;
gcr | = GCR_SDONE_IE | GCR_CDONE_IE ;
writel ( gcr , ac97_reg_base + GCR ) ;
2008-09-10 05:01:17 +04:00
}
EXPORT_SYMBOL_GPL ( pxa2xx_ac97_finish_reset ) ;
static irqreturn_t pxa2xx_ac97_irq ( int irq , void * dev_id )
{
long status ;
2019-09-18 11:52:31 +03:00
status = readl ( ac97_reg_base + GSR ) ;
2008-09-10 05:01:17 +04:00
if ( status ) {
2019-09-18 11:52:31 +03:00
writel ( status , ac97_reg_base + GSR ) ;
2008-09-10 05:01:17 +04:00
gsr_bits | = status ;
wake_up ( & gsr_wq ) ;
/* Although we don't use those we still need to clear them
since they tend to spuriously trigger when MMC is used
( hardware bug ? go figure ) . . . */
2008-09-10 05:01:18 +04:00
if ( cpu_is_pxa27x ( ) ) {
2019-09-18 11:52:31 +03:00
writel ( MISR_EOC , ac97_reg_base + MISR ) ;
writel ( PISR_EOC , ac97_reg_base + PISR ) ;
writel ( MCSR_EOC , ac97_reg_base + MCSR ) ;
2008-09-10 05:01:18 +04:00
}
2008-09-10 05:01:17 +04:00
return IRQ_HANDLED ;
}
return IRQ_NONE ;
}
# ifdef CONFIG_PM
int pxa2xx_ac97_hw_suspend ( void )
{
2019-09-18 11:52:31 +03:00
writel ( readl ( ac97_reg_base + GCR ) | ( GCR_ACLINK_OFF ) , ac97_reg_base + GCR ) ;
2014-06-09 23:59:12 +04:00
clk_disable_unprepare ( ac97_clk ) ;
2008-09-10 05:01:17 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( pxa2xx_ac97_hw_suspend ) ;
int pxa2xx_ac97_hw_resume ( void )
{
2014-06-09 23:59:12 +04:00
clk_prepare_enable ( ac97_clk ) ;
2008-09-10 05:01:17 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( pxa2xx_ac97_hw_resume ) ;
# endif
2012-12-06 21:35:12 +04:00
int pxa2xx_ac97_hw_probe ( struct platform_device * dev )
2008-09-10 05:01:17 +04:00
{
int ret ;
2019-09-10 16:23:52 +03:00
int irq ;
2009-04-13 14:48:03 +04:00
pxa2xx_audio_ops_t * pdata = dev - > dev . platform_data ;
2009-03-15 16:10:54 +03:00
2019-09-18 11:52:31 +03:00
ac97_reg_base = devm_platform_ioremap_resource ( dev , 0 ) ;
if ( IS_ERR ( ac97_reg_base ) ) {
dev_err ( & dev - > dev , " Missing MMIO resource \n " ) ;
return PTR_ERR ( ac97_reg_base ) ;
}
2009-03-15 16:10:54 +03:00
if ( pdata ) {
switch ( pdata - > reset_gpio ) {
case 95 :
case 113 :
reset_gpio = pdata - > reset_gpio ;
break ;
case 0 :
reset_gpio = 113 ;
break ;
case - 1 :
break ;
default :
2009-03-19 16:08:58 +03:00
dev_err ( & dev - > dev , " Invalid reset GPIO %d \n " ,
2009-03-15 16:10:54 +03:00
pdata - > reset_gpio ) ;
}
2018-06-17 13:50:01 +03:00
} else if ( ! pdata & & dev - > dev . of_node ) {
pdata = devm_kzalloc ( & dev - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
pdata - > reset_gpio = of_get_named_gpio ( dev - > dev . of_node ,
" reset-gpios " , 0 ) ;
if ( pdata - > reset_gpio = = - ENOENT )
pdata - > reset_gpio = - 1 ;
else if ( pdata - > reset_gpio < 0 )
return pdata - > reset_gpio ;
reset_gpio = pdata - > reset_gpio ;
2009-03-15 16:10:54 +03:00
} else {
if ( cpu_is_pxa27x ( ) )
reset_gpio = 113 ;
}
2008-09-10 05:01:17 +04:00
2008-09-10 05:01:18 +04:00
if ( cpu_is_pxa27x ( ) ) {
2013-01-08 01:55:13 +04:00
/*
* This gpio is needed for a work - around to a bug in the ac97
* controller during warm reset . The direction and level is set
* here so that it is an output driven high when switching from
* AC97_nRESET alt function to generic gpio .
*/
ret = gpio_request_one ( reset_gpio , GPIOF_OUT_INIT_HIGH ,
" pxa27x ac97 reset " ) ;
if ( ret < 0 ) {
pr_err ( " %s: gpio_request_one() failed: %d \n " ,
__func__ , ret ) ;
goto err_conf ;
}
2013-01-08 01:55:14 +04:00
pxa27x_configure_ac97reset ( reset_gpio , false ) ;
2013-01-08 01:55:13 +04:00
2008-09-10 05:01:18 +04:00
ac97conf_clk = clk_get ( & dev - > dev , " AC97CONFCLK " ) ;
if ( IS_ERR ( ac97conf_clk ) ) {
ret = PTR_ERR ( ac97conf_clk ) ;
ac97conf_clk = NULL ;
2009-01-05 12:58:06 +03:00
goto err_conf ;
2008-09-10 05:01:18 +04:00
}
2008-09-10 05:01:17 +04:00
}
ac97_clk = clk_get ( & dev - > dev , " AC97CLK " ) ;
if ( IS_ERR ( ac97_clk ) ) {
ret = PTR_ERR ( ac97_clk ) ;
ac97_clk = NULL ;
2009-01-05 12:58:06 +03:00
goto err_clk ;
2008-09-10 05:01:17 +04:00
}
2014-06-09 23:59:12 +04:00
ret = clk_prepare_enable ( ac97_clk ) ;
2009-01-05 12:58:06 +03:00
if ( ret )
goto err_clk2 ;
2019-09-10 16:23:52 +03:00
irq = platform_get_irq ( dev , 0 ) ;
2022-10-29 11:20:01 +03:00
if ( irq < 0 ) {
ret = irq ;
2019-09-10 16:23:52 +03:00
goto err_irq ;
2022-10-29 11:20:01 +03:00
}
2019-09-10 16:23:52 +03:00
ret = request_irq ( irq , pxa2xx_ac97_irq , 0 , " AC97 " , NULL ) ;
2009-01-05 12:58:06 +03:00
if ( ret < 0 )
goto err_irq ;
return 0 ;
2008-09-10 05:01:17 +04:00
err_irq :
2019-09-18 11:52:31 +03:00
writel ( readl ( ac97_reg_base + GCR ) | ( GCR_ACLINK_OFF ) , ac97_reg_base + GCR ) ;
2009-01-05 12:58:06 +03:00
err_clk2 :
clk_put ( ac97_clk ) ;
ac97_clk = NULL ;
err_clk :
2008-09-10 05:01:17 +04:00
if ( ac97conf_clk ) {
clk_put ( ac97conf_clk ) ;
ac97conf_clk = NULL ;
}
2009-01-05 12:58:06 +03:00
err_conf :
2008-09-10 05:01:17 +04:00
return ret ;
}
EXPORT_SYMBOL_GPL ( pxa2xx_ac97_hw_probe ) ;
void pxa2xx_ac97_hw_remove ( struct platform_device * dev )
{
2013-01-08 01:55:13 +04:00
if ( cpu_is_pxa27x ( ) )
gpio_free ( reset_gpio ) ;
2019-09-18 11:52:31 +03:00
writel ( readl ( ac97_reg_base + GCR ) | ( GCR_ACLINK_OFF ) , ac97_reg_base + GCR ) ;
2019-09-10 16:23:52 +03:00
free_irq ( platform_get_irq ( dev , 0 ) , NULL ) ;
2008-09-10 05:01:18 +04:00
if ( ac97conf_clk ) {
clk_put ( ac97conf_clk ) ;
ac97conf_clk = NULL ;
}
2014-06-09 23:59:12 +04:00
clk_disable_unprepare ( ac97_clk ) ;
2008-09-10 05:01:17 +04:00
clk_put ( ac97_clk ) ;
ac97_clk = NULL ;
}
EXPORT_SYMBOL_GPL ( pxa2xx_ac97_hw_remove ) ;
2019-09-18 10:55:23 +03:00
u32 pxa2xx_ac97_read_modr ( void )
{
2019-09-18 11:52:31 +03:00
if ( ! ac97_reg_base )
return 0 ;
return readl ( ac97_reg_base + MODR ) ;
2019-09-18 10:55:23 +03:00
}
EXPORT_SYMBOL_GPL ( pxa2xx_ac97_read_modr ) ;
u32 pxa2xx_ac97_read_misr ( void )
{
2019-09-18 11:52:31 +03:00
if ( ! ac97_reg_base )
return 0 ;
return readl ( ac97_reg_base + MISR ) ;
2019-09-18 10:55:23 +03:00
}
EXPORT_SYMBOL_GPL ( pxa2xx_ac97_read_misr ) ;
2008-09-10 05:01:17 +04:00
MODULE_AUTHOR ( " Nicolas Pitre " ) ;
MODULE_DESCRIPTION ( " Intel/Marvell PXA sound library " ) ;
MODULE_LICENSE ( " GPL " ) ;