2011-10-29 10:57:23 +01:00
/*
* Synopsys DesignWare I2C adapter driver ( master only ) .
*
* Based on the TI DAVINCI I2C adapter driver .
*
* Copyright ( C ) 2006 Texas Instruments .
* Copyright ( C ) 2007 MontaVista Software Inc .
* Copyright ( C ) 2009 Provigent Ltd .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* 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 , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/i2c.h>
# include <linux/clk.h>
# include <linux/errno.h>
# include <linux/sched.h>
# include <linux/err.h>
# include <linux/interrupt.h>
2011-11-08 14:43:47 -06:00
# include <linux/of_i2c.h>
2011-10-29 10:57:23 +01:00
# include <linux/platform_device.h>
2012-02-24 17:01:15 +05:30
# include <linux/pm.h>
2011-10-29 10:57:23 +01:00
# include <linux/io.h>
# include <linux/slab.h>
# include "i2c-designware-core.h"
static struct i2c_algorithm i2c_dw_algo = {
. master_xfer = i2c_dw_xfer ,
. functionality = i2c_dw_func ,
} ;
2011-10-06 11:26:30 -07:00
static u32 i2c_dw_get_clk_rate_khz ( struct dw_i2c_dev * dev )
{
return clk_get_rate ( dev - > clk ) / 1000 ;
}
2011-10-29 10:57:23 +01:00
static int __devinit dw_i2c_probe ( struct platform_device * pdev )
{
struct dw_i2c_dev * dev ;
struct i2c_adapter * adap ;
struct resource * mem , * ioarea ;
int irq , r ;
/* NOTE: driver uses the static register mapping */
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! mem ) {
dev_err ( & pdev - > dev , " no mem resource? \n " ) ;
return - EINVAL ;
}
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
dev_err ( & pdev - > dev , " no irq resource? \n " ) ;
return irq ; /* -ENXIO */
}
ioarea = request_mem_region ( mem - > start , resource_size ( mem ) ,
pdev - > name ) ;
if ( ! ioarea ) {
dev_err ( & pdev - > dev , " I2C region already claimed \n " ) ;
return - EBUSY ;
}
dev = kzalloc ( sizeof ( struct dw_i2c_dev ) , GFP_KERNEL ) ;
if ( ! dev ) {
r = - ENOMEM ;
goto err_release_region ;
}
init_completion ( & dev - > cmd_complete ) ;
mutex_init ( & dev - > lock ) ;
dev - > dev = get_device ( & pdev - > dev ) ;
dev - > irq = irq ;
platform_set_drvdata ( pdev , dev ) ;
dev - > clk = clk_get ( & pdev - > dev , NULL ) ;
2011-10-06 11:26:30 -07:00
dev - > get_clk_rate_khz = i2c_dw_get_clk_rate_khz ;
2011-10-29 10:57:23 +01:00
if ( IS_ERR ( dev - > clk ) ) {
r = - ENODEV ;
goto err_free_mem ;
}
2012-04-17 17:04:31 +05:30
clk_prepare_enable ( dev - > clk ) ;
2011-10-29 10:57:23 +01:00
2011-10-06 11:26:31 -07:00
dev - > functionality =
I2C_FUNC_I2C |
I2C_FUNC_10BIT_ADDR |
I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK ;
2011-10-06 11:26:32 -07:00
dev - > master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST ;
2011-10-06 11:26:31 -07:00
2011-10-29 10:57:23 +01:00
dev - > base = ioremap ( mem - > start , resource_size ( mem ) ) ;
if ( dev - > base = = NULL ) {
dev_err ( & pdev - > dev , " failure mapping io resources \n " ) ;
r = - EBUSY ;
goto err_unuse_clocks ;
}
{
2011-10-06 11:26:34 -07:00
u32 param1 = i2c_dw_read_comp_param ( dev ) ;
2011-10-29 10:57:23 +01:00
dev - > tx_fifo_depth = ( ( param1 > > 16 ) & 0xff ) + 1 ;
dev - > rx_fifo_depth = ( ( param1 > > 8 ) & 0xff ) + 1 ;
}
r = i2c_dw_init ( dev ) ;
if ( r )
goto err_iounmap ;
2011-10-06 11:26:34 -07:00
i2c_dw_disable_int ( dev ) ;
2011-10-29 10:57:23 +01:00
r = request_irq ( dev - > irq , i2c_dw_isr , IRQF_DISABLED , pdev - > name , dev ) ;
if ( r ) {
dev_err ( & pdev - > dev , " failure requesting irq %i \n " , dev - > irq ) ;
goto err_iounmap ;
}
adap = & dev - > adapter ;
i2c_set_adapdata ( adap , dev ) ;
adap - > owner = THIS_MODULE ;
adap - > class = I2C_CLASS_HWMON ;
strlcpy ( adap - > name , " Synopsys DesignWare I2C adapter " ,
sizeof ( adap - > name ) ) ;
adap - > algo = & i2c_dw_algo ;
adap - > dev . parent = & pdev - > dev ;
2011-11-08 14:43:47 -06:00
adap - > dev . of_node = pdev - > dev . of_node ;
2011-10-29 10:57:23 +01:00
adap - > nr = pdev - > id ;
r = i2c_add_numbered_adapter ( adap ) ;
if ( r ) {
dev_err ( & pdev - > dev , " failure adding adapter \n " ) ;
goto err_free_irq ;
}
2011-11-08 14:43:47 -06:00
of_i2c_register_devices ( adap ) ;
2011-10-29 10:57:23 +01:00
return 0 ;
err_free_irq :
free_irq ( dev - > irq , dev ) ;
err_iounmap :
iounmap ( dev - > base ) ;
err_unuse_clocks :
2012-04-17 17:04:31 +05:30
clk_disable_unprepare ( dev - > clk ) ;
2011-10-29 10:57:23 +01:00
clk_put ( dev - > clk ) ;
dev - > clk = NULL ;
err_free_mem :
platform_set_drvdata ( pdev , NULL ) ;
put_device ( & pdev - > dev ) ;
kfree ( dev ) ;
err_release_region :
release_mem_region ( mem - > start , resource_size ( mem ) ) ;
return r ;
}
static int __devexit dw_i2c_remove ( struct platform_device * pdev )
{
struct dw_i2c_dev * dev = platform_get_drvdata ( pdev ) ;
struct resource * mem ;
platform_set_drvdata ( pdev , NULL ) ;
i2c_del_adapter ( & dev - > adapter ) ;
put_device ( & pdev - > dev ) ;
2012-04-17 17:04:31 +05:30
clk_disable_unprepare ( dev - > clk ) ;
2011-10-29 10:57:23 +01:00
clk_put ( dev - > clk ) ;
dev - > clk = NULL ;
2011-10-06 11:26:34 -07:00
i2c_dw_disable ( dev ) ;
2011-10-29 10:57:23 +01:00
free_irq ( dev - > irq , dev ) ;
kfree ( dev ) ;
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
release_mem_region ( mem - > start , resource_size ( mem ) ) ;
return 0 ;
}
2011-11-08 14:43:47 -06:00
# ifdef CONFIG_OF
static const struct of_device_id dw_i2c_of_match [ ] = {
{ . compatible = " snps,designware-i2c " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , dw_i2c_of_match ) ;
# endif
2012-02-24 17:01:15 +05:30
# ifdef CONFIG_PM
static int dw_i2c_suspend ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct dw_i2c_dev * i_dev = platform_get_drvdata ( pdev ) ;
2012-04-17 17:04:31 +05:30
clk_disable_unprepare ( i_dev - > clk ) ;
2012-02-24 17:01:15 +05:30
return 0 ;
}
static int dw_i2c_resume ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct dw_i2c_dev * i_dev = platform_get_drvdata ( pdev ) ;
2012-04-17 17:04:31 +05:30
clk_prepare_enable ( i_dev - > clk ) ;
2012-02-24 17:01:15 +05:30
i2c_dw_init ( i_dev ) ;
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( dw_i2c_dev_pm_ops , dw_i2c_suspend , dw_i2c_resume ) ;
2011-10-29 10:57:23 +01:00
/* work with hotplug and coldplug */
MODULE_ALIAS ( " platform:i2c_designware " ) ;
static struct platform_driver dw_i2c_driver = {
. remove = __devexit_p ( dw_i2c_remove ) ,
. driver = {
. name = " i2c_designware " ,
. owner = THIS_MODULE ,
2011-11-08 14:43:47 -06:00
. of_match_table = of_match_ptr ( dw_i2c_of_match ) ,
2012-02-24 17:01:15 +05:30
. pm = & dw_i2c_dev_pm_ops ,
2011-10-29 10:57:23 +01:00
} ,
} ;
static int __init dw_i2c_init_driver ( void )
{
return platform_driver_probe ( & dw_i2c_driver , dw_i2c_probe ) ;
}
2012-02-29 12:27:46 +05:30
subsys_initcall ( dw_i2c_init_driver ) ;
2011-10-29 10:57:23 +01:00
static void __exit dw_i2c_exit_driver ( void )
{
platform_driver_unregister ( & dw_i2c_driver ) ;
}
module_exit ( dw_i2c_exit_driver ) ;
MODULE_AUTHOR ( " Baruch Siach <baruch@tkos.co.il> " ) ;
MODULE_DESCRIPTION ( " Synopsys DesignWare I2C bus adapter " ) ;
MODULE_LICENSE ( " GPL " ) ;