2011-10-29 13:57:23 +04: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 .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/i2c.h>
# include <linux/clk.h>
2014-09-30 14:04:55 +04:00
# include <linux/clk-provider.h>
2011-10-29 13:57:23 +04:00
# include <linux/errno.h>
# include <linux/sched.h>
# include <linux/err.h>
# include <linux/interrupt.h>
2013-06-26 12:55:06 +04:00
# include <linux/of.h>
2011-10-29 13:57:23 +04:00
# include <linux/platform_device.h>
2012-02-24 15:31:15 +04:00
# include <linux/pm.h>
2013-01-17 14:31:06 +04:00
# include <linux/pm_runtime.h>
2011-10-29 13:57:23 +04:00
# include <linux/io.h>
# include <linux/slab.h>
2013-01-17 14:31:07 +04:00
# include <linux/acpi.h>
2014-09-03 06:41:38 +04:00
# include <linux/platform_data/i2c-designware.h>
2011-10-29 13:57:23 +04:00
# include "i2c-designware-core.h"
static struct i2c_algorithm i2c_dw_algo = {
. master_xfer = i2c_dw_xfer ,
. functionality = i2c_dw_func ,
} ;
2011-10-06 22:26:30 +04:00
static u32 i2c_dw_get_clk_rate_khz ( struct dw_i2c_dev * dev )
{
return clk_get_rate ( dev - > clk ) / 1000 ;
}
2011-10-29 13:57:23 +04:00
2013-01-17 14:31:07 +04:00
# ifdef CONFIG_ACPI
2013-08-19 16:07:54 +04:00
static void dw_i2c_acpi_params ( struct platform_device * pdev , char method [ ] ,
u16 * hcnt , u16 * lcnt , u32 * sda_hold )
{
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER } ;
acpi_handle handle = ACPI_HANDLE ( & pdev - > dev ) ;
union acpi_object * obj ;
if ( ACPI_FAILURE ( acpi_evaluate_object ( handle , method , NULL , & buf ) ) )
return ;
obj = ( union acpi_object * ) buf . pointer ;
if ( obj - > type = = ACPI_TYPE_PACKAGE & & obj - > package . count = = 3 ) {
const union acpi_object * objs = obj - > package . elements ;
* hcnt = ( u16 ) objs [ 0 ] . integer . value ;
* lcnt = ( u16 ) objs [ 1 ] . integer . value ;
if ( sda_hold )
* sda_hold = ( u32 ) objs [ 2 ] . integer . value ;
}
kfree ( buf . pointer ) ;
}
2013-01-17 14:31:07 +04:00
static int dw_i2c_acpi_configure ( struct platform_device * pdev )
{
struct dw_i2c_dev * dev = platform_get_drvdata ( pdev ) ;
2014-09-30 14:04:55 +04:00
const struct acpi_device_id * id ;
2013-01-17 14:31:07 +04:00
dev - > adapter . nr = - 1 ;
dev - > tx_fifo_depth = 32 ;
dev - > rx_fifo_depth = 32 ;
2013-08-19 16:07:54 +04:00
/*
* Try to get SDA hold time and * CNT values from an ACPI method if
* it exists for both supported speed modes .
*/
2014-09-30 14:04:53 +04:00
dw_i2c_acpi_params ( pdev , " SSCN " , & dev - > ss_hcnt , & dev - > ss_lcnt , NULL ) ;
2013-08-19 16:07:54 +04:00
dw_i2c_acpi_params ( pdev , " FMCN " , & dev - > fs_hcnt , & dev - > fs_lcnt ,
2014-09-30 14:04:53 +04:00
& dev - > sda_hold_time ) ;
2013-08-19 16:07:54 +04:00
2014-09-30 14:04:55 +04:00
/*
* Provide a way for Designware I2C host controllers that are not
* based on Intel LPSS to specify their input clock frequency via
* id - > driver_data .
*/
id = acpi_match_device ( pdev - > dev . driver - > acpi_match_table , & pdev - > dev ) ;
if ( id & & id - > driver_data )
clk_register_fixed_rate ( & pdev - > dev , dev_name ( & pdev - > dev ) , NULL ,
CLK_IS_ROOT , id - > driver_data ) ;
2013-01-17 14:31:07 +04:00
return 0 ;
}
2014-09-30 14:04:55 +04:00
static void dw_i2c_acpi_unconfigure ( struct platform_device * pdev )
{
struct dw_i2c_dev * dev = platform_get_drvdata ( pdev ) ;
const struct acpi_device_id * id ;
id = acpi_match_device ( pdev - > dev . driver - > acpi_match_table , & pdev - > dev ) ;
if ( id & & id - > driver_data )
clk_unregister ( dev - > clk ) ;
}
2013-01-17 14:31:07 +04:00
static const struct acpi_device_id dw_i2c_acpi_match [ ] = {
{ " INT33C2 " , 0 } ,
{ " INT33C3 " , 0 } ,
2013-11-12 13:57:30 +04:00
{ " INT3432 " , 0 } ,
{ " INT3433 " , 0 } ,
2013-05-13 04:54:31 +04:00
{ " 80860F41 " , 0 } ,
2014-07-23 16:06:57 +04:00
{ " 808622C1 " , 0 } ,
2014-09-30 14:04:55 +04:00
{ " AMD0010 " , 133 * 1000 * 1000 } ,
2013-01-17 14:31:07 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( acpi , dw_i2c_acpi_match ) ;
# else
static inline int dw_i2c_acpi_configure ( struct platform_device * pdev )
{
return - ENODEV ;
}
2014-09-30 14:04:55 +04:00
static inline void dw_i2c_acpi_unconfigure ( struct platform_device * pdev ) { }
2013-01-17 14:31:07 +04:00
# endif
2012-11-28 00:59:38 +04:00
static int dw_i2c_probe ( struct platform_device * pdev )
2011-10-29 13:57:23 +04:00
{
struct dw_i2c_dev * dev ;
struct i2c_adapter * adap ;
2013-04-10 04:36:36 +04:00
struct resource * mem ;
2014-09-03 06:41:38 +04:00
struct dw_i2c_platform_data * pdata ;
2011-10-29 13:57:23 +04:00
int irq , r ;
2014-09-30 14:04:54 +04:00
u32 clk_freq , ht = 0 ;
2011-10-29 13:57:23 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
dev_err ( & pdev - > dev , " no irq resource? \n " ) ;
return irq ; /* -ENXIO */
}
2013-04-10 04:36:36 +04:00
dev = devm_kzalloc ( & pdev - > dev , sizeof ( struct dw_i2c_dev ) , GFP_KERNEL ) ;
if ( ! dev )
return - ENOMEM ;
2011-10-29 13:57:23 +04:00
2013-05-10 12:16:54 +04:00
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-04-10 04:36:36 +04:00
dev - > base = devm_ioremap_resource ( & pdev - > dev , mem ) ;
if ( IS_ERR ( dev - > base ) )
return PTR_ERR ( dev - > base ) ;
2011-10-29 13:57:23 +04:00
init_completion ( & dev - > cmd_complete ) ;
mutex_init ( & dev - > lock ) ;
2013-04-10 04:36:36 +04:00
dev - > dev = & pdev - > dev ;
2011-10-29 13:57:23 +04:00
dev - > irq = irq ;
platform_set_drvdata ( pdev , dev ) ;
2014-08-20 18:29:08 +04:00
/* fast mode by default because of legacy reasons */
clk_freq = 400000 ;
2014-09-30 14:04:54 +04:00
if ( ACPI_COMPANION ( & pdev - > dev ) ) {
dw_i2c_acpi_configure ( pdev ) ;
} else if ( pdev - > dev . of_node ) {
2013-06-26 12:55:06 +04:00
of_property_read_u32 ( pdev - > dev . of_node ,
" i2c-sda-hold-time-ns " , & ht ) ;
2014-01-20 20:43:43 +04:00
of_property_read_u32 ( pdev - > dev . of_node ,
" i2c-sda-falling-time-ns " ,
& dev - > sda_falling_time ) ;
of_property_read_u32 ( pdev - > dev . of_node ,
" i2c-scl-falling-time-ns " ,
& dev - > scl_falling_time ) ;
2014-08-20 18:29:08 +04:00
of_property_read_u32 ( pdev - > dev . of_node , " clock-frequency " ,
& clk_freq ) ;
/* Only standard mode at 100kHz and fast mode at 400kHz
* are supported .
*/
if ( clk_freq ! = 100000 & & clk_freq ! = 400000 ) {
dev_err ( & pdev - > dev , " Only 100kHz and 400kHz supported " ) ;
return - EINVAL ;
}
2014-09-03 06:41:38 +04:00
} else {
pdata = dev_get_platdata ( & pdev - > dev ) ;
if ( pdata )
clk_freq = pdata - > i2c_scl_freq ;
2013-06-26 12:55:06 +04:00
}
2011-10-06 22:26:31 +04: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 ;
2014-08-20 18:29:08 +04:00
if ( clk_freq = = 100000 )
dev - > master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_STD ;
else
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 22:26:31 +04:00
2014-09-30 14:04:54 +04:00
dev - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
dev - > get_clk_rate_khz = i2c_dw_get_clk_rate_khz ;
if ( IS_ERR ( dev - > clk ) )
return PTR_ERR ( dev - > clk ) ;
clk_prepare_enable ( dev - > clk ) ;
if ( ! dev - > sda_hold_time & & ht ) {
u32 ic_clk = dev - > get_clk_rate_khz ( dev ) ;
dev - > sda_hold_time = div_u64 ( ( u64 ) ic_clk * ht + 500000 ,
1000000 ) ;
}
if ( ! dev - > tx_fifo_depth ) {
2011-10-06 22:26:34 +04:00
u32 param1 = i2c_dw_read_comp_param ( dev ) ;
2011-10-29 13:57:23 +04:00
dev - > tx_fifo_depth = ( ( param1 > > 16 ) & 0xff ) + 1 ;
dev - > rx_fifo_depth = ( ( param1 > > 8 ) & 0xff ) + 1 ;
2013-01-17 14:31:07 +04:00
dev - > adapter . nr = pdev - > id ;
2011-10-29 13:57:23 +04:00
}
r = i2c_dw_init ( dev ) ;
if ( r )
2013-04-10 04:36:36 +04:00
return r ;
2011-10-29 13:57:23 +04:00
2011-10-06 22:26:34 +04:00
i2c_dw_disable_int ( dev ) ;
2013-04-10 04:36:36 +04:00
r = devm_request_irq ( & pdev - > dev , dev - > irq , i2c_dw_isr , IRQF_SHARED ,
pdev - > name , dev ) ;
2011-10-29 13:57:23 +04:00
if ( r ) {
dev_err ( & pdev - > dev , " failure requesting irq %i \n " , dev - > irq ) ;
2013-04-10 04:36:36 +04:00
return r ;
2011-10-29 13:57:23 +04:00
}
adap = & dev - > adapter ;
i2c_set_adapdata ( adap , dev ) ;
adap - > owner = THIS_MODULE ;
2014-07-10 15:46:26 +04:00
adap - > class = I2C_CLASS_DEPRECATED ;
2011-10-29 13:57:23 +04:00
strlcpy ( adap - > name , " Synopsys DesignWare I2C adapter " ,
sizeof ( adap - > name ) ) ;
adap - > algo = & i2c_dw_algo ;
adap - > dev . parent = & pdev - > dev ;
2011-11-09 00:43:47 +04:00
adap - > dev . of_node = pdev - > dev . of_node ;
2011-10-29 13:57:23 +04:00
r = i2c_add_numbered_adapter ( adap ) ;
if ( r ) {
dev_err ( & pdev - > dev , " failure adding adapter \n " ) ;
2013-04-10 04:36:36 +04:00
return r ;
2011-10-29 13:57:23 +04:00
}
2013-04-10 04:36:42 +04:00
pm_runtime_set_autosuspend_delay ( & pdev - > dev , 1000 ) ;
pm_runtime_use_autosuspend ( & pdev - > dev ) ;
2013-01-17 14:31:06 +04:00
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2011-10-29 13:57:23 +04:00
return 0 ;
}
2012-11-28 00:59:38 +04:00
static int dw_i2c_remove ( struct platform_device * pdev )
2011-10-29 13:57:23 +04:00
{
struct dw_i2c_dev * dev = platform_get_drvdata ( pdev ) ;
2013-01-17 14:31:06 +04:00
pm_runtime_get_sync ( & pdev - > dev ) ;
2011-10-29 13:57:23 +04:00
i2c_del_adapter ( & dev - > adapter ) ;
2011-10-06 22:26:34 +04:00
i2c_dw_disable ( dev ) ;
2011-10-29 13:57:23 +04:00
2013-01-17 14:31:06 +04:00
pm_runtime_put ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
2014-09-30 14:04:55 +04:00
if ( ACPI_COMPANION ( & pdev - > dev ) )
dw_i2c_acpi_unconfigure ( pdev ) ;
2011-10-29 13:57:23 +04:00
return 0 ;
}
2011-11-09 00:43:47 +04: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
2014-05-15 18:37:23 +04:00
# ifdef CONFIG_PM
2012-02-24 15:31:15 +04:00
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 ) ;
2014-05-15 18:37:22 +04:00
i2c_dw_disable ( i_dev ) ;
2012-04-17 15:34:31 +04:00
clk_disable_unprepare ( i_dev - > clk ) ;
2012-02-24 15:31:15 +04:00
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 15:34:31 +04:00
clk_prepare_enable ( i_dev - > clk ) ;
2012-02-24 15:31:15 +04:00
i2c_dw_init ( i_dev ) ;
return 0 ;
}
2013-07-15 06:28:59 +04:00
# endif
2012-02-24 15:31:15 +04:00
2014-05-15 18:37:23 +04:00
static UNIVERSAL_DEV_PM_OPS ( dw_i2c_dev_pm_ops , dw_i2c_suspend ,
dw_i2c_resume , NULL ) ;
2011-10-29 13:57:23 +04:00
/* work with hotplug and coldplug */
MODULE_ALIAS ( " platform:i2c_designware " ) ;
static struct platform_driver dw_i2c_driver = {
2013-10-09 00:35:33 +04:00
. probe = dw_i2c_probe ,
. remove = dw_i2c_remove ,
2011-10-29 13:57:23 +04:00
. driver = {
. name = " i2c_designware " ,
2011-11-09 00:43:47 +04:00
. of_match_table = of_match_ptr ( dw_i2c_of_match ) ,
2013-01-17 14:31:07 +04:00
. acpi_match_table = ACPI_PTR ( dw_i2c_acpi_match ) ,
2014-05-15 18:37:23 +04:00
. pm = & dw_i2c_dev_pm_ops ,
2011-10-29 13:57:23 +04:00
} ,
} ;
static int __init dw_i2c_init_driver ( void )
{
2013-10-09 00:35:33 +04:00
return platform_driver_register ( & dw_i2c_driver ) ;
2011-10-29 13:57:23 +04:00
}
2012-02-29 10:57:46 +04:00
subsys_initcall ( dw_i2c_init_driver ) ;
2011-10-29 13:57:23 +04: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 " ) ;