2005-04-16 15:20:36 -07:00
/*
* linux / arch / arm / mach - pxa / ssp . c
*
* based on linux / arch / arm / mach - sa1100 / ssp . c by Russell King
*
* Copyright ( C ) 2003 Russell King .
* Copyright ( C ) 2003 Wolfson Microelectronics PLC
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* PXA2xx SSP driver . This provides the generic core for simple
* IO - based SSP applications and allows easy port setup for DMA access .
*
* Author : Liam Girdwood < liam . girdwood @ wolfsonmicro . com >
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include <linux/init.h>
2006-01-12 18:42:23 +00:00
# include <linux/mutex.h>
2007-12-06 17:56:42 +08:00
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/platform_device.h>
2010-11-22 17:12:15 -08:00
# include <linux/spi/pxa2xx_spi.h>
2008-09-06 12:10:45 +01:00
# include <linux/io.h>
2007-12-06 17:56:42 +08:00
2005-04-16 15:20:36 -07:00
# include <asm/irq.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2005-04-16 15:20:36 -07:00
2007-12-06 17:56:42 +08:00
static DEFINE_MUTEX ( ssp_lock ) ;
static LIST_HEAD ( ssp_list ) ;
2010-05-05 10:11:15 -04:00
struct ssp_device * pxa_ssp_request ( int port , const char * label )
2007-12-06 17:56:42 +08:00
{
struct ssp_device * ssp = NULL ;
mutex_lock ( & ssp_lock ) ;
2005-04-16 15:20:36 -07:00
2007-12-06 17:56:42 +08:00
list_for_each_entry ( ssp , & ssp_list , node ) {
if ( ssp - > port_id = = port & & ssp - > use_count = = 0 ) {
ssp - > use_count + + ;
ssp - > label = label ;
break ;
}
2005-04-16 15:20:36 -07:00
}
2007-12-06 17:56:42 +08:00
mutex_unlock ( & ssp_lock ) ;
2008-06-05 10:43:14 +01:00
if ( & ssp - > node = = & ssp_list )
2007-12-06 17:56:42 +08:00
return NULL ;
return ssp ;
}
2010-05-05 10:11:15 -04:00
EXPORT_SYMBOL ( pxa_ssp_request ) ;
2007-12-06 17:56:42 +08:00
2010-05-05 10:11:15 -04:00
void pxa_ssp_free ( struct ssp_device * ssp )
2007-12-06 17:56:42 +08:00
{
mutex_lock ( & ssp_lock ) ;
if ( ssp - > use_count ) {
ssp - > use_count - - ;
ssp - > label = NULL ;
} else
dev_err ( & ssp - > pdev - > dev , " device already free \n " ) ;
mutex_unlock ( & ssp_lock ) ;
2005-04-16 15:20:36 -07:00
}
2010-05-05 10:11:15 -04:00
EXPORT_SYMBOL ( pxa_ssp_free ) ;
2007-12-06 17:56:42 +08:00
2010-05-05 10:11:15 -04:00
static int __devinit pxa_ssp_probe ( struct platform_device * pdev )
2007-12-06 17:56:42 +08:00
{
2009-10-23 00:09:47 +08:00
const struct platform_device_id * id = platform_get_device_id ( pdev ) ;
2007-12-06 17:56:42 +08:00
struct resource * res ;
struct ssp_device * ssp ;
int ret = 0 ;
ssp = kzalloc ( sizeof ( struct ssp_device ) , GFP_KERNEL ) ;
if ( ssp = = NULL ) {
dev_err ( & pdev - > dev , " failed to allocate memory " ) ;
return - ENOMEM ;
}
2008-06-19 02:55:52 +01:00
ssp - > pdev = pdev ;
2007-12-06 17:56:42 +08:00
2008-11-11 17:52:32 +00:00
ssp - > clk = clk_get ( & pdev - > dev , NULL ) ;
2007-12-06 17:56:42 +08:00
if ( IS_ERR ( ssp - > clk ) ) {
ret = PTR_ERR ( ssp - > clk ) ;
goto err_free ;
}
2010-03-22 16:11:55 +08:00
res = platform_get_resource ( pdev , IORESOURCE_DMA , 0 ) ;
if ( res = = NULL ) {
dev_err ( & pdev - > dev , " no SSP RX DRCMR defined \n " ) ;
ret = - ENODEV ;
goto err_free_clk ;
}
ssp - > drcmr_rx = res - > start ;
res = platform_get_resource ( pdev , IORESOURCE_DMA , 1 ) ;
if ( res = = NULL ) {
dev_err ( & pdev - > dev , " no SSP TX DRCMR defined \n " ) ;
ret = - ENODEV ;
goto err_free_clk ;
}
ssp - > drcmr_tx = res - > start ;
2007-12-06 17:56:42 +08:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( res = = NULL ) {
dev_err ( & pdev - > dev , " no memory resource defined \n " ) ;
ret = - ENODEV ;
goto err_free_clk ;
}
2010-03-22 16:16:24 +08:00
res = request_mem_region ( res - > start , resource_size ( res ) ,
2007-12-06 17:56:42 +08:00
pdev - > name ) ;
if ( res = = NULL ) {
dev_err ( & pdev - > dev , " failed to request memory resource \n " ) ;
ret = - EBUSY ;
goto err_free_clk ;
}
ssp - > phys_base = res - > start ;
2010-03-22 16:16:24 +08:00
ssp - > mmio_base = ioremap ( res - > start , resource_size ( res ) ) ;
2007-12-06 17:56:42 +08:00
if ( ssp - > mmio_base = = NULL ) {
dev_err ( & pdev - > dev , " failed to ioremap() registers \n " ) ;
ret = - ENODEV ;
goto err_free_mem ;
}
ssp - > irq = platform_get_irq ( pdev , 0 ) ;
if ( ssp - > irq < 0 ) {
dev_err ( & pdev - > dev , " no IRQ resource defined \n " ) ;
ret = - ENODEV ;
goto err_free_io ;
}
/* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id
* starts from 0 , do a translation here
*/
ssp - > port_id = pdev - > id + 1 ;
ssp - > use_count = 0 ;
2009-10-23 00:09:47 +08:00
ssp - > type = ( int ) id - > driver_data ;
2007-12-06 17:56:42 +08:00
mutex_lock ( & ssp_lock ) ;
list_add ( & ssp - > node , & ssp_list ) ;
mutex_unlock ( & ssp_lock ) ;
platform_set_drvdata ( pdev , ssp ) ;
return 0 ;
err_free_io :
iounmap ( ssp - > mmio_base ) ;
err_free_mem :
2010-03-22 16:16:24 +08:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
2007-12-06 17:56:42 +08:00
err_free_clk :
clk_put ( ssp - > clk ) ;
err_free :
kfree ( ssp ) ;
return ret ;
}
2010-05-05 10:11:15 -04:00
static int __devexit pxa_ssp_remove ( struct platform_device * pdev )
2007-12-06 17:56:42 +08:00
{
struct resource * res ;
struct ssp_device * ssp ;
ssp = platform_get_drvdata ( pdev ) ;
if ( ssp = = NULL )
return - ENODEV ;
iounmap ( ssp - > mmio_base ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2010-03-22 16:16:24 +08:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
2007-12-06 17:56:42 +08:00
clk_put ( ssp - > clk ) ;
2005-04-16 15:20:36 -07:00
2007-12-06 17:56:42 +08:00
mutex_lock ( & ssp_lock ) ;
list_del ( & ssp - > node ) ;
mutex_unlock ( & ssp_lock ) ;
kfree ( ssp ) ;
return 0 ;
}
2009-10-23 00:09:47 +08:00
static const struct platform_device_id ssp_id_table [ ] = {
{ " pxa25x-ssp " , PXA25x_SSP } ,
{ " pxa25x-nssp " , PXA25x_NSSP } ,
{ " pxa27x-ssp " , PXA27x_SSP } ,
2010-03-19 11:53:17 -04:00
{ " pxa168-ssp " , PXA168_SSP } ,
2009-10-23 00:09:47 +08:00
{ } ,
2007-12-06 17:56:42 +08:00
} ;
2010-05-05 10:11:15 -04:00
static struct platform_driver pxa_ssp_driver = {
. probe = pxa_ssp_probe ,
. remove = __devexit_p ( pxa_ssp_remove ) ,
2007-12-06 17:56:42 +08:00
. driver = {
2009-10-23 00:09:47 +08:00
. owner = THIS_MODULE ,
. name = " pxa2xx-ssp " ,
2007-12-06 17:56:42 +08:00
} ,
2009-10-23 00:09:47 +08:00
. id_table = ssp_id_table ,
2007-12-06 17:56:42 +08:00
} ;
static int __init pxa_ssp_init ( void )
{
2010-05-05 10:11:15 -04:00
return platform_driver_register ( & pxa_ssp_driver ) ;
2007-12-06 17:56:42 +08:00
}
static void __exit pxa_ssp_exit ( void )
{
2010-05-05 10:11:15 -04:00
platform_driver_unregister ( & pxa_ssp_driver ) ;
2005-04-16 15:20:36 -07:00
}
2007-12-10 15:35:54 +00:00
arch_initcall ( pxa_ssp_init ) ;
2007-12-06 17:56:42 +08:00
module_exit ( pxa_ssp_exit ) ;
2005-04-16 15:20:36 -07:00
MODULE_DESCRIPTION ( " PXA SSP driver " ) ;
MODULE_AUTHOR ( " Liam Girdwood " ) ;
MODULE_LICENSE ( " GPL " ) ;