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>
2015-09-24 12:06:54 +03:00
# include <linux/dmi.h>
2011-10-29 13:57:23 +04:00
# 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>
2015-11-30 18:11:44 +03:00
# include <linux/property.h>
2011-10-29 13:57:23 +04:00
# include <linux/io.h>
2016-12-27 17:22:40 +03:00
# include <linux/reset.h>
2011-10-29 13:57:23 +04:00
# 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"
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
2015-09-24 12:06:54 +03:00
/*
* The HCNT / LCNT information coming from ACPI should be the most accurate
* for given platform . However , some systems get it wrong . On such systems
* we get better results by calculating those based on the input clock .
*/
static const struct dmi_system_id dw_i2c_no_acpi_params [ ] = {
{
. ident = " Dell Inspiron 7348 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Inspiron 7348 " ) ,
} ,
} ,
{ }
} ;
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 ;
2015-09-24 12:06:54 +03:00
if ( dmi_check_system ( dw_i2c_no_acpi_params ) )
return ;
2013-08-19 16:07:54 +04:00
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 ;
2017-03-28 11:48:02 +03:00
* sda_hold = ( u32 ) objs [ 2 ] . integer . value ;
2013-08-19 16:07:54 +04:00
}
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 ) ;
2017-05-22 08:46:55 +03:00
u32 ss_ht = 0 , fp_ht = 0 , hs_ht = 0 , fs_ht = 0 ;
2017-03-14 01:25:10 +03:00
acpi_handle handle = ACPI_HANDLE ( & pdev - > dev ) ;
2015-12-11 15:02:53 +03:00
const struct acpi_device_id * id ;
2017-03-14 01:25:10 +03:00
struct acpi_device * adev ;
const char * uid ;
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
/*
2017-03-28 11:48:02 +03:00
* Try to get SDA hold time and * CNT values from an ACPI method for
* selected speed modes .
2013-08-19 16:07:54 +04:00
*/
2017-05-19 11:56:40 +03:00
dw_i2c_acpi_params ( pdev , " SSCN " , & dev - > ss_hcnt , & dev - > ss_lcnt , & ss_ht ) ;
dw_i2c_acpi_params ( pdev , " FPCN " , & dev - > fp_hcnt , & dev - > fp_lcnt , & fp_ht ) ;
dw_i2c_acpi_params ( pdev , " HSCN " , & dev - > hs_hcnt , & dev - > hs_lcnt , & hs_ht ) ;
dw_i2c_acpi_params ( pdev , " FMCN " , & dev - > fs_hcnt , & dev - > fs_lcnt , & fs_ht ) ;
2017-03-28 11:48:02 +03:00
switch ( dev - > clk_freq ) {
case 100000 :
2017-05-19 11:56:40 +03:00
dev - > sda_hold_time = ss_ht ;
2017-03-28 11:48:02 +03:00
break ;
case 1000000 :
2017-05-19 11:56:40 +03:00
dev - > sda_hold_time = fp_ht ;
2017-03-28 11:48:02 +03:00
break ;
case 3400000 :
2017-05-19 11:56:40 +03:00
dev - > sda_hold_time = hs_ht ;
2017-03-28 11:48:02 +03:00
break ;
case 400000 :
default :
2017-05-19 11:56:40 +03:00
dev - > sda_hold_time = fs_ht ;
2017-03-28 11:48:02 +03:00
break ;
}
2013-08-19 16:07:54 +04:00
2015-12-11 15:02:53 +03:00
id = acpi_match_device ( pdev - > dev . driver - > acpi_match_table , & pdev - > dev ) ;
if ( id & & id - > driver_data )
2017-02-10 13:27:53 +03:00
dev - > flags | = ( u32 ) id - > driver_data ;
2015-12-11 15:02:53 +03:00
2017-03-14 01:25:10 +03:00
if ( acpi_bus_get_device ( handle , & adev ) )
return - ENODEV ;
/*
* Cherrytrail I2C7 gets used for the PMIC which gets accessed
* through ACPI opregions during late suspend / early resume
* disable pm for it .
*/
uid = adev - > pnp . unique_id ;
if ( ( dev - > flags & MODEL_CHERRYTRAIL ) & & ! strcmp ( uid , " 7 " ) )
dev - > pm_disabled = true ;
2013-01-17 14:31:07 +04:00
return 0 ;
}
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 } ,
2017-02-10 13:27:58 +03:00
{ " 808622C1 " , MODEL_CHERRYTRAIL } ,
2015-12-11 15:02:53 +03:00
{ " AMD0010 " , ACCESS_INTR_MASK } ,
2016-03-10 14:34:52 +03:00
{ " AMDI0010 " , ACCESS_INTR_MASK } ,
2015-12-16 00:55:53 +03:00
{ " AMDI0510 " , 0 } ,
2015-12-11 00:19:17 +03:00
{ " APMC0D0F " , 0 } ,
2017-04-22 06:23:44 +03:00
{ " HISI02A1 " , 0 } ,
{ " HISI02A2 " , 0 } ,
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 ;
}
# endif
2016-01-04 18:17:35 +03:00
static int i2c_dw_plat_prepare_clk ( struct dw_i2c_dev * i_dev , bool prepare )
{
if ( IS_ERR ( i_dev - > clk ) )
return PTR_ERR ( i_dev - > clk ) ;
if ( prepare )
return clk_prepare_enable ( i_dev - > clk ) ;
clk_disable_unprepare ( i_dev - > clk ) ;
return 0 ;
}
2016-12-14 12:23:58 +03:00
static void dw_i2c_set_fifo_size ( struct dw_i2c_dev * dev , int id )
{
u32 param , tx_fifo_depth , rx_fifo_depth ;
/*
* Try to detect the FIFO depth if not set by interface driver ,
* the depth could be from 2 to 256 from HW spec .
*/
param = i2c_dw_read_comp_param ( dev ) ;
tx_fifo_depth = ( ( param > > 16 ) & 0xff ) + 1 ;
rx_fifo_depth = ( ( param > > 8 ) & 0xff ) + 1 ;
if ( ! dev - > tx_fifo_depth ) {
dev - > tx_fifo_depth = tx_fifo_depth ;
dev - > rx_fifo_depth = rx_fifo_depth ;
dev - > adapter . nr = id ;
} else if ( tx_fifo_depth > = 2 ) {
dev - > tx_fifo_depth = min_t ( u32 , dev - > tx_fifo_depth ,
tx_fifo_depth ) ;
dev - > rx_fifo_depth = min_t ( u32 , dev - > rx_fifo_depth ,
rx_fifo_depth ) ;
}
}
2015-08-31 17:31:32 +03:00
static int dw_i2c_plat_probe ( struct platform_device * pdev )
2011-10-29 13:57:23 +04:00
{
2015-11-30 18:11:44 +03:00
struct dw_i2c_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
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 ;
2011-10-29 13:57:23 +04:00
int irq , r ;
2016-08-12 17:02:54 +03:00
u32 acpi_speed , ht = 0 ;
2011-10-29 13:57:23 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
2015-03-09 12:03:12 +03:00
if ( irq < 0 )
return irq ;
2011-10-29 13:57:23 +04:00
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
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 ) ;
2016-12-27 17:22:40 +03:00
dev - > rst = devm_reset_control_get_optional_exclusive ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( dev - > rst ) ) {
if ( PTR_ERR ( dev - > rst ) = = - EPROBE_DEFER )
return - EPROBE_DEFER ;
} else {
reset_control_deassert ( dev - > rst ) ;
}
2015-11-30 18:11:44 +03:00
if ( pdata ) {
2016-08-12 17:02:47 +03:00
dev - > clk_freq = pdata - > i2c_scl_freq ;
2014-09-03 06:41:38 +04:00
} else {
2015-11-30 18:11:44 +03:00
device_property_read_u32 ( & pdev - > dev , " i2c-sda-hold-time-ns " ,
& ht ) ;
device_property_read_u32 ( & pdev - > dev , " i2c-sda-falling-time-ns " ,
& dev - > sda_falling_time ) ;
device_property_read_u32 ( & pdev - > dev , " i2c-scl-falling-time-ns " ,
& dev - > scl_falling_time ) ;
device_property_read_u32 ( & pdev - > dev , " clock-frequency " ,
2016-08-12 17:02:47 +03:00
& dev - > clk_freq ) ;
2015-11-30 18:11:44 +03:00
}
2016-08-12 17:02:54 +03:00
acpi_speed = i2c_acpi_find_bus_speed ( & pdev - > dev ) ;
2016-11-10 14:37:20 +03:00
/*
* Find bus speed from the " clock-frequency " device property , ACPI
* or by using fast mode if neither is set .
*/
if ( acpi_speed & & dev - > clk_freq )
dev - > clk_freq = min ( dev - > clk_freq , acpi_speed ) ;
else if ( acpi_speed | | dev - > clk_freq )
dev - > clk_freq = max ( dev - > clk_freq , acpi_speed ) ;
else
dev - > clk_freq = 400000 ;
2016-08-12 17:02:54 +03:00
2015-11-30 18:11:44 +03:00
if ( has_acpi_companion ( & pdev - > dev ) )
dw_i2c_acpi_configure ( pdev ) ;
/*
2016-08-12 17:02:49 +03:00
* Only standard mode at 100 kHz , fast mode at 400 kHz ,
2016-08-12 17:02:51 +03:00
* fast mode plus at 1 MHz and high speed mode at 3.4 MHz are supported .
2015-11-30 18:11:44 +03:00
*/
2016-08-12 17:02:49 +03:00
if ( dev - > clk_freq ! = 100000 & & dev - > clk_freq ! = 400000
2016-08-12 17:02:51 +03:00
& & dev - > clk_freq ! = 1000000 & & dev - > clk_freq ! = 3400000 ) {
2016-08-12 17:02:49 +03:00
dev_err ( & pdev - > dev ,
2016-08-12 17:02:51 +03:00
" Only 100kHz, 400kHz, 1MHz and 3.4MHz supported " ) ;
2016-12-27 17:22:40 +03:00
r = - EINVAL ;
goto exit_reset ;
2013-06-26 12:55:06 +04:00
}
2017-02-10 13:27:56 +03:00
r = i2c_dw_probe_lock_support ( dev ) ;
2015-01-15 12:12:17 +03:00
if ( r )
2016-12-27 17:22:40 +03:00
goto exit_reset ;
2015-01-15 12:12:17 +03:00
2016-11-21 13:43:20 +03:00
dev - > functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY ;
2016-08-12 17:02:50 +03:00
dev - > master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
DW_IC_CON_RESTART_EN ;
2016-08-12 17:02:51 +03:00
switch ( dev - > clk_freq ) {
case 100000 :
2016-08-12 17:02:50 +03:00
dev - > master_cfg | = DW_IC_CON_SPEED_STD ;
2016-08-12 17:02:51 +03:00
break ;
case 3400000 :
dev - > master_cfg | = DW_IC_CON_SPEED_HIGH ;
break ;
default :
2016-08-12 17:02:50 +03:00
dev - > master_cfg | = DW_IC_CON_SPEED_FAST ;
2016-08-12 17:02:51 +03:00
}
2011-10-06 22:26:31 +04:00
2014-09-30 14:04:54 +04:00
dev - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2016-01-04 18:17:35 +03:00
if ( ! i2c_dw_plat_prepare_clk ( dev , true ) ) {
dev - > get_clk_rate_khz = i2c_dw_get_clk_rate_khz ;
2014-09-30 14:04:54 +04:00
2016-01-04 18:17:35 +03:00
if ( ! dev - > sda_hold_time & & ht )
dev - > sda_hold_time = div_u64 (
( u64 ) dev - > get_clk_rate_khz ( dev ) * ht + 500000 ,
1000000 ) ;
2014-09-30 14:04:54 +04:00
}
2016-12-14 12:23:58 +03:00
dw_i2c_set_fifo_size ( dev , pdev - > id ) ;
2011-10-29 13:57:23 +04:00
adap = & dev - > adapter ;
adap - > owner = THIS_MODULE ;
2014-07-10 15:46:26 +04:00
adap - > class = I2C_CLASS_DEPRECATED ;
2015-10-23 22:27:07 +03:00
ACPI_COMPANION_SET ( & adap - > dev , ACPI_COMPANION ( & 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
2017-03-14 01:25:09 +03:00
if ( dev - > pm_disabled ) {
2015-01-15 12:12:17 +03:00
pm_runtime_forbid ( & pdev - > dev ) ;
} else {
pm_runtime_set_autosuspend_delay ( & pdev - > dev , 1000 ) ;
pm_runtime_use_autosuspend ( & pdev - > dev ) ;
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
}
2013-01-17 14:31:06 +04:00
2015-11-10 22:58:25 +03:00
r = i2c_dw_probe ( dev ) ;
2016-12-27 17:22:40 +03:00
if ( r )
goto exit_probe ;
2015-10-09 12:39:24 +03:00
2015-12-10 14:48:43 +03:00
return r ;
2016-12-27 17:22:40 +03:00
exit_probe :
2017-03-14 01:25:09 +03:00
if ( ! dev - > pm_disabled )
2016-12-27 17:22:40 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
exit_reset :
if ( ! IS_ERR_OR_NULL ( dev - > rst ) )
reset_control_assert ( dev - > rst ) ;
return r ;
2011-10-29 13:57:23 +04:00
}
2015-08-31 17:31:32 +03:00
static int dw_i2c_plat_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
2015-06-17 12:08:38 +03:00
pm_runtime_dont_use_autosuspend ( & pdev - > dev ) ;
pm_runtime_put_sync ( & pdev - > dev ) ;
2017-03-14 01:25:09 +03:00
if ( ! dev - > pm_disabled )
2015-12-10 14:48:43 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
2016-12-27 17:22:40 +03:00
if ( ! IS_ERR_OR_NULL ( dev - > rst ) )
reset_control_assert ( dev - > rst ) ;
2013-01-17 14:31:06 +04:00
2017-02-10 13:27:56 +03:00
i2c_dw_remove_lock_support ( dev ) ;
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
2015-05-20 17:33:13 +03:00
# ifdef CONFIG_PM_SLEEP
2015-08-31 17:31:32 +03:00
static int dw_i2c_plat_prepare ( struct device * dev )
2015-05-20 17:33:13 +03:00
{
return pm_runtime_suspended ( dev ) ;
}
2015-08-31 17:31:32 +03:00
static void dw_i2c_plat_complete ( struct device * dev )
2015-05-20 17:33:13 +03:00
{
if ( dev - > power . direct_complete )
pm_request_resume ( dev ) ;
}
# else
2015-10-21 10:09:17 +03:00
# define dw_i2c_plat_prepare NULL
# define dw_i2c_plat_complete NULL
2015-05-20 17:33:13 +03:00
# endif
2014-05-15 18:37:23 +04:00
# ifdef CONFIG_PM
2015-08-31 17:31:32 +03:00
static int dw_i2c_plat_suspend ( struct device * dev )
2012-02-24 15:31:15 +04:00
{
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 ) ;
2016-01-04 18:17:35 +03:00
i2c_dw_plat_prepare_clk ( i_dev , false ) ;
2012-02-24 15:31:15 +04:00
return 0 ;
}
2015-08-31 17:31:32 +03:00
static int dw_i2c_plat_resume ( struct device * dev )
2012-02-24 15:31:15 +04:00
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct dw_i2c_dev * i_dev = platform_get_drvdata ( pdev ) ;
2016-01-04 18:17:35 +03:00
i2c_dw_plat_prepare_clk ( i_dev , true ) ;
2017-03-14 01:25:09 +03:00
i2c_dw_init ( i_dev ) ;
2012-02-24 15:31:15 +04:00
return 0 ;
}
2015-05-20 17:33:13 +03:00
static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
2015-08-31 17:31:32 +03:00
. prepare = dw_i2c_plat_prepare ,
. complete = dw_i2c_plat_complete ,
SET_SYSTEM_SLEEP_PM_OPS ( dw_i2c_plat_suspend , dw_i2c_plat_resume )
SET_RUNTIME_PM_OPS ( dw_i2c_plat_suspend , dw_i2c_plat_resume , NULL )
2015-05-20 17:33:13 +03:00
} ;
# define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
# else
# define DW_I2C_DEV_PMOPS NULL
# endif
2014-05-15 18:37:23 +04:00
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 = {
2015-08-31 17:31:32 +03:00
. probe = dw_i2c_plat_probe ,
. remove = dw_i2c_plat_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 ) ,
2015-05-20 17:33:13 +03:00
. pm = DW_I2C_DEV_PMOPS ,
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 " ) ;