2005-08-18 13:08:15 +04:00
/*
* linux / drivers / mfd / mcp - sa11x0 . c
*
* Copyright ( C ) 2001 - 2005 Russell King
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License .
*
* SA11x0 MCP ( Multimedia Communications Port ) driver .
*
* MCP read / write timeouts from Jordi Colomer , rehacked by rmk .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/spinlock.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2009-02-10 16:54:57 +03:00
# include <linux/mfd/mcp.h>
2005-08-18 13:08:15 +04:00
2008-11-29 14:40:28 +03:00
# include <mach/dma.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2005-08-18 13:08:15 +04:00
# include <asm/mach-types.h>
# include <asm/system.h>
2008-08-05 19:14:15 +04:00
# include <mach/mcp.h>
2005-08-18 13:08:15 +04:00
2008-08-05 19:14:15 +04:00
# include <mach/assabet.h>
2005-08-18 13:08:15 +04:00
struct mcp_sa11x0 {
u32 mccr0 ;
u32 mccr1 ;
} ;
# define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp))
static void
mcp_sa11x0_set_telecom_divisor ( struct mcp * mcp , unsigned int divisor )
{
unsigned int mccr0 ;
divisor / = 32 ;
mccr0 = Ser4MCCR0 & ~ 0x00007f00 ;
mccr0 | = divisor < < 8 ;
Ser4MCCR0 = mccr0 ;
}
static void
mcp_sa11x0_set_audio_divisor ( struct mcp * mcp , unsigned int divisor )
{
unsigned int mccr0 ;
divisor / = 32 ;
mccr0 = Ser4MCCR0 & ~ 0x0000007f ;
mccr0 | = divisor ;
Ser4MCCR0 = mccr0 ;
}
/*
* Write data to the device . The bit should be set after 3 subframe
* times ( each frame is 64 clocks ) . We wait a maximum of 6 subframes .
* We really should try doing something more productive while we
* wait .
*/
static void
mcp_sa11x0_write ( struct mcp * mcp , unsigned int reg , unsigned int val )
{
int ret = - ETIME ;
int i ;
Ser4MCDR2 = reg < < 17 | MCDR2_Wr | ( val & 0xffff ) ;
for ( i = 0 ; i < 2 ; i + + ) {
udelay ( mcp - > rw_timeout ) ;
if ( Ser4MCSR & MCSR_CWC ) {
ret = 0 ;
break ;
}
}
if ( ret < 0 )
printk ( KERN_WARNING " mcp: write timed out \n " ) ;
}
/*
* Read data from the device . The bit should be set after 3 subframe
* times ( each frame is 64 clocks ) . We wait a maximum of 6 subframes .
* We really should try doing something more productive while we
* wait .
*/
static unsigned int
mcp_sa11x0_read ( struct mcp * mcp , unsigned int reg )
{
int ret = - ETIME ;
int i ;
Ser4MCDR2 = reg < < 17 | MCDR2_Rd ;
for ( i = 0 ; i < 2 ; i + + ) {
udelay ( mcp - > rw_timeout ) ;
if ( Ser4MCSR & MCSR_CRC ) {
ret = Ser4MCDR2 & 0xffff ;
break ;
}
}
if ( ret < 0 )
printk ( KERN_WARNING " mcp: read timed out \n " ) ;
return ret ;
}
static void mcp_sa11x0_enable ( struct mcp * mcp )
{
Ser4MCSR = - 1 ;
Ser4MCCR0 | = MCCR0_MCE ;
}
static void mcp_sa11x0_disable ( struct mcp * mcp )
{
Ser4MCCR0 & = ~ MCCR0_MCE ;
}
/*
* Our methods .
*/
static struct mcp_ops mcp_sa11x0 = {
. set_telecom_divisor = mcp_sa11x0_set_telecom_divisor ,
. set_audio_divisor = mcp_sa11x0_set_audio_divisor ,
. reg_write = mcp_sa11x0_write ,
. reg_read = mcp_sa11x0_read ,
. enable = mcp_sa11x0_enable ,
. disable = mcp_sa11x0_disable ,
} ;
2005-11-10 01:32:44 +03:00
static int mcp_sa11x0_probe ( struct platform_device * pdev )
2005-08-18 13:08:15 +04:00
{
2005-08-18 13:10:46 +04:00
struct mcp_plat_data * data = pdev - > dev . platform_data ;
2005-08-18 13:08:15 +04:00
struct mcp * mcp ;
int ret ;
2005-08-18 13:10:46 +04:00
if ( ! data )
2005-08-18 13:08:15 +04:00
return - ENODEV ;
if ( ! request_mem_region ( 0x80060000 , 0x60 , " sa11x0-mcp " ) )
return - EBUSY ;
mcp = mcp_host_alloc ( & pdev - > dev , sizeof ( struct mcp_sa11x0 ) ) ;
if ( ! mcp ) {
ret = - ENOMEM ;
goto release ;
}
mcp - > owner = THIS_MODULE ;
mcp - > ops = & mcp_sa11x0 ;
2005-08-18 13:10:46 +04:00
mcp - > sclk_rate = data - > sclk_rate ;
2005-08-18 13:08:15 +04:00
mcp - > dma_audio_rd = DMA_Ser4MCP0Rd ;
mcp - > dma_audio_wr = DMA_Ser4MCP0Wr ;
mcp - > dma_telco_rd = DMA_Ser4MCP1Rd ;
mcp - > dma_telco_wr = DMA_Ser4MCP1Wr ;
2009-02-10 16:50:56 +03:00
mcp - > gpio_base = data - > gpio_base ;
2005-08-18 13:08:15 +04:00
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( pdev , mcp ) ;
2005-08-18 13:08:15 +04:00
if ( machine_is_assabet ( ) ) {
ASSABET_BCR_set ( ASSABET_BCR_CODEC_RST ) ;
}
/*
* Setup the PPC unit correctly .
*/
PPDR & = ~ PPC_RXD4 ;
PPDR | = PPC_TXD4 | PPC_SCLK | PPC_SFRM ;
PSDR | = PPC_RXD4 ;
PSDR & = ~ ( PPC_TXD4 | PPC_SCLK | PPC_SFRM ) ;
PPSR & = ~ ( PPC_TXD4 | PPC_SCLK | PPC_SFRM ) ;
2005-08-18 13:10:46 +04:00
/*
* Initialise device . Note that we initially
* set the sampling rate to minimum .
*/
2005-08-18 13:08:15 +04:00
Ser4MCSR = - 1 ;
2005-08-18 13:10:46 +04:00
Ser4MCCR1 = data - > mccr1 ;
Ser4MCCR0 = data - > mccr0 | 0x7f7f ;
2005-08-18 13:08:15 +04:00
/*
* Calculate the read / write timeout ( us ) from the bit clock
* rate . This is the period for 3 64 - bit frames . Always
* round this time up .
*/
mcp - > rw_timeout = ( 64 * 3 * 1000000 + mcp - > sclk_rate - 1 ) /
mcp - > sclk_rate ;
ret = mcp_host_register ( mcp ) ;
if ( ret = = 0 )
goto out ;
release :
release_mem_region ( 0x80060000 , 0x60 ) ;
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( pdev , NULL ) ;
2005-08-18 13:08:15 +04:00
out :
return ret ;
}
2005-11-10 01:32:44 +03:00
static int mcp_sa11x0_remove ( struct platform_device * dev )
2005-08-18 13:08:15 +04:00
{
2005-11-10 01:32:44 +03:00
struct mcp * mcp = platform_get_drvdata ( dev ) ;
2005-08-18 13:08:15 +04:00
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( dev , NULL ) ;
2005-08-18 13:08:15 +04:00
mcp_host_unregister ( mcp ) ;
release_mem_region ( 0x80060000 , 0x60 ) ;
return 0 ;
}
2005-11-10 01:32:44 +03:00
static int mcp_sa11x0_suspend ( struct platform_device * dev , pm_message_t state )
2005-08-18 13:08:15 +04:00
{
2005-11-10 01:32:44 +03:00
struct mcp * mcp = platform_get_drvdata ( dev ) ;
2005-08-18 13:08:15 +04:00
2005-10-28 20:52:56 +04:00
priv ( mcp ) - > mccr0 = Ser4MCCR0 ;
priv ( mcp ) - > mccr1 = Ser4MCCR1 ;
Ser4MCCR0 & = ~ MCCR0_MCE ;
2005-08-18 13:08:15 +04:00
return 0 ;
}
2005-11-10 01:32:44 +03:00
static int mcp_sa11x0_resume ( struct platform_device * dev )
2005-08-18 13:08:15 +04:00
{
2005-11-10 01:32:44 +03:00
struct mcp * mcp = platform_get_drvdata ( dev ) ;
2005-08-18 13:08:15 +04:00
2005-10-28 20:52:56 +04:00
Ser4MCCR1 = priv ( mcp ) - > mccr1 ;
Ser4MCCR0 = priv ( mcp ) - > mccr0 ;
2005-08-18 13:08:15 +04:00
return 0 ;
}
/*
* The driver for the SA11x0 MCP port .
*/
2008-07-25 12:45:47 +04:00
MODULE_ALIAS ( " platform:sa11x0-mcp " ) ;
2005-11-10 01:32:44 +03:00
static struct platform_driver mcp_sa11x0_driver = {
2005-08-18 13:08:15 +04:00
. probe = mcp_sa11x0_probe ,
. remove = mcp_sa11x0_remove ,
. suspend = mcp_sa11x0_suspend ,
. resume = mcp_sa11x0_resume ,
2005-11-10 01:32:44 +03:00
. driver = {
. name = " sa11x0-mcp " ,
} ,
2005-08-18 13:08:15 +04:00
} ;
/*
* This needs re - working
*/
static int __init mcp_sa11x0_init ( void )
{
2005-11-10 01:32:44 +03:00
return platform_driver_register ( & mcp_sa11x0_driver ) ;
2005-08-18 13:08:15 +04:00
}
static void __exit mcp_sa11x0_exit ( void )
{
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & mcp_sa11x0_driver ) ;
2005-08-18 13:08:15 +04:00
}
module_init ( mcp_sa11x0_init ) ;
module_exit ( mcp_sa11x0_exit ) ;
MODULE_AUTHOR ( " Russell King <rmk@arm.linux.org.uk> " ) ;
MODULE_DESCRIPTION ( " SA11x0 multimedia communications port driver " ) ;
MODULE_LICENSE ( " GPL " ) ;