2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04: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
*
* 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 21:42:23 +03:00
# include <linux/mutex.h>
2007-12-06 12:56:42 +03:00
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/platform_device.h>
2010-11-23 04:12:15 +03:00
# include <linux/spi/pxa2xx_spi.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2013-08-12 12:37:16 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
2007-12-06 12:56:42 +03:00
2005-04-17 02:20:36 +04:00
# include <asm/irq.h>
2007-12-06 12:56:42 +03:00
static DEFINE_MUTEX ( ssp_lock ) ;
static LIST_HEAD ( ssp_list ) ;
2010-05-05 18:11:15 +04:00
struct ssp_device * pxa_ssp_request ( int port , const char * label )
2007-12-06 12:56:42 +03:00
{
struct ssp_device * ssp = NULL ;
mutex_lock ( & ssp_lock ) ;
2005-04-17 02:20:36 +04:00
2007-12-06 12:56:42 +03: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-17 02:20:36 +04:00
}
2007-12-06 12:56:42 +03:00
mutex_unlock ( & ssp_lock ) ;
2008-06-05 13:43:14 +04:00
if ( & ssp - > node = = & ssp_list )
2007-12-06 12:56:42 +03:00
return NULL ;
return ssp ;
}
2010-05-05 18:11:15 +04:00
EXPORT_SYMBOL ( pxa_ssp_request ) ;
2007-12-06 12:56:42 +03:00
2013-08-12 12:37:18 +04:00
struct ssp_device * pxa_ssp_request_of ( const struct device_node * of_node ,
const char * label )
{
struct ssp_device * ssp = NULL ;
mutex_lock ( & ssp_lock ) ;
list_for_each_entry ( ssp , & ssp_list , node ) {
if ( ssp - > of_node = = of_node & & ssp - > use_count = = 0 ) {
ssp - > use_count + + ;
ssp - > label = label ;
break ;
}
}
mutex_unlock ( & ssp_lock ) ;
if ( & ssp - > node = = & ssp_list )
return NULL ;
return ssp ;
}
EXPORT_SYMBOL ( pxa_ssp_request_of ) ;
2010-05-05 18:11:15 +04:00
void pxa_ssp_free ( struct ssp_device * ssp )
2007-12-06 12:56:42 +03:00
{
mutex_lock ( & ssp_lock ) ;
if ( ssp - > use_count ) {
ssp - > use_count - - ;
ssp - > label = NULL ;
} else
2019-10-18 13:54:25 +03:00
dev_err ( ssp - > dev , " device already free \n " ) ;
2007-12-06 12:56:42 +03:00
mutex_unlock ( & ssp_lock ) ;
2005-04-17 02:20:36 +04:00
}
2010-05-05 18:11:15 +04:00
EXPORT_SYMBOL ( pxa_ssp_free ) ;
2007-12-06 12:56:42 +03:00
2013-08-12 12:37:16 +04:00
# ifdef CONFIG_OF
static const struct of_device_id pxa_ssp_of_ids [ ] = {
{ . compatible = " mrvl,pxa25x-ssp " , . data = ( void * ) PXA25x_SSP } ,
{ . compatible = " mvrl,pxa25x-nssp " , . data = ( void * ) PXA25x_NSSP } ,
{ . compatible = " mrvl,pxa27x-ssp " , . data = ( void * ) PXA27x_SSP } ,
{ . compatible = " mrvl,pxa3xx-ssp " , . data = ( void * ) PXA3xx_SSP } ,
{ . compatible = " mvrl,pxa168-ssp " , . data = ( void * ) PXA168_SSP } ,
{ . compatible = " mrvl,pxa910-ssp " , . data = ( void * ) PXA910_SSP } ,
{ . compatible = " mrvl,ce4100-ssp " , . data = ( void * ) CE4100_SSP } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , pxa_ssp_of_ids ) ;
# endif
2012-12-22 02:02:24 +04:00
static int pxa_ssp_probe ( struct platform_device * pdev )
2007-12-06 12:56:42 +03:00
{
struct resource * res ;
struct ssp_device * ssp ;
2013-08-12 12:37:15 +04:00
struct device * dev = & pdev - > dev ;
2007-12-06 12:56:42 +03:00
2013-08-12 12:37:17 +04:00
ssp = devm_kzalloc ( dev , sizeof ( struct ssp_device ) , GFP_KERNEL ) ;
2013-08-12 12:37:14 +04:00
if ( ssp = = NULL )
2007-12-06 12:56:42 +03:00
return - ENOMEM ;
2013-08-12 12:37:14 +04:00
2019-10-18 13:54:25 +03:00
ssp - > dev = dev ;
2007-12-06 12:56:42 +03:00
2013-08-12 12:37:17 +04:00
ssp - > clk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( ssp - > clk ) )
return PTR_ERR ( ssp - > clk ) ;
2007-12-06 12:56:42 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( res = = NULL ) {
2013-08-12 12:37:15 +04:00
dev_err ( dev , " no memory resource defined \n " ) ;
2013-08-12 12:37:17 +04:00
return - ENODEV ;
2007-12-06 12:56:42 +03:00
}
2013-08-12 12:37:17 +04:00
res = devm_request_mem_region ( dev , res - > start , resource_size ( res ) ,
pdev - > name ) ;
2007-12-06 12:56:42 +03:00
if ( res = = NULL ) {
2013-08-12 12:37:15 +04:00
dev_err ( dev , " failed to request memory resource \n " ) ;
2013-08-12 12:37:17 +04:00
return - EBUSY ;
2007-12-06 12:56:42 +03:00
}
ssp - > phys_base = res - > start ;
2013-08-12 12:37:17 +04:00
ssp - > mmio_base = devm_ioremap ( dev , res - > start , resource_size ( res ) ) ;
2007-12-06 12:56:42 +03:00
if ( ssp - > mmio_base = = NULL ) {
2013-08-12 12:37:15 +04:00
dev_err ( dev , " failed to ioremap() registers \n " ) ;
2013-08-12 12:37:17 +04:00
return - ENODEV ;
2007-12-06 12:56:42 +03:00
}
ssp - > irq = platform_get_irq ( pdev , 0 ) ;
2022-12-02 05:42:04 +03:00
if ( ssp - > irq < 0 )
2013-08-12 12:37:17 +04:00
return - ENODEV ;
2007-12-06 12:56:42 +03:00
2013-08-12 12:37:16 +04:00
if ( dev - > of_node ) {
const struct of_device_id * id =
of_match_device ( of_match_ptr ( pxa_ssp_of_ids ) , dev ) ;
ssp - > type = ( int ) id - > data ;
} else {
const struct platform_device_id * id =
platform_get_device_id ( pdev ) ;
ssp - > type = ( int ) id - > driver_data ;
/* 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 ;
}
2007-12-06 12:56:42 +03:00
ssp - > use_count = 0 ;
2013-08-12 12:37:18 +04:00
ssp - > of_node = dev - > of_node ;
2007-12-06 12:56:42 +03:00
mutex_lock ( & ssp_lock ) ;
list_add ( & ssp - > node , & ssp_list ) ;
mutex_unlock ( & ssp_lock ) ;
platform_set_drvdata ( pdev , ssp ) ;
2013-08-12 12:37:17 +04:00
return 0 ;
2007-12-06 12:56:42 +03:00
}
2012-12-22 02:02:24 +04:00
static int pxa_ssp_remove ( struct platform_device * pdev )
2007-12-06 12:56:42 +03:00
{
2022-09-26 13:40:11 +03:00
struct ssp_device * ssp = platform_get_drvdata ( pdev ) ;
2007-12-06 12:56:42 +03:00
mutex_lock ( & ssp_lock ) ;
list_del ( & ssp - > node ) ;
mutex_unlock ( & ssp_lock ) ;
return 0 ;
}
2009-10-22 20:09:47 +04:00
static const struct platform_device_id ssp_id_table [ ] = {
{ " pxa25x-ssp " , PXA25x_SSP } ,
{ " pxa25x-nssp " , PXA25x_NSSP } ,
{ " pxa27x-ssp " , PXA27x_SSP } ,
2014-08-13 23:59:18 +04:00
{ " pxa3xx-ssp " , PXA3xx_SSP } ,
2010-03-19 18:53:17 +03:00
{ " pxa168-ssp " , PXA168_SSP } ,
2012-06-04 06:41:03 +04:00
{ " pxa910-ssp " , PXA910_SSP } ,
2009-10-22 20:09:47 +04:00
{ } ,
2007-12-06 12:56:42 +03:00
} ;
2010-05-05 18:11:15 +04:00
static struct platform_driver pxa_ssp_driver = {
. probe = pxa_ssp_probe ,
2012-12-22 02:02:24 +04:00
. remove = pxa_ssp_remove ,
2007-12-06 12:56:42 +03:00
. driver = {
2013-08-12 12:37:16 +04:00
. name = " pxa2xx-ssp " ,
. of_match_table = of_match_ptr ( pxa_ssp_of_ids ) ,
2007-12-06 12:56:42 +03:00
} ,
2009-10-22 20:09:47 +04:00
. id_table = ssp_id_table ,
2007-12-06 12:56:42 +03:00
} ;
static int __init pxa_ssp_init ( void )
{
2010-05-05 18:11:15 +04:00
return platform_driver_register ( & pxa_ssp_driver ) ;
2007-12-06 12:56:42 +03:00
}
static void __exit pxa_ssp_exit ( void )
{
2010-05-05 18:11:15 +04:00
platform_driver_unregister ( & pxa_ssp_driver ) ;
2005-04-17 02:20:36 +04:00
}
2007-12-10 18:35:54 +03:00
arch_initcall ( pxa_ssp_init ) ;
2007-12-06 12:56:42 +03:00
module_exit ( pxa_ssp_exit ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " PXA SSP driver " ) ;
MODULE_AUTHOR ( " Liam Girdwood " ) ;
MODULE_LICENSE ( " GPL " ) ;