2013-05-28 14:20:07 +08:00
/*
* EIM driver for Freescale ' s i . MX chips
*
* Copyright ( C ) 2013 Freescale Semiconductor , Inc .
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/module.h>
# include <linux/clk.h>
# include <linux/io.h>
2023-02-01 15:58:54 -06:00
# include <linux/of_address.h>
2013-05-28 14:20:07 +08:00
# include <linux/of_device.h>
2014-02-11 09:52:09 +08:00
# include <linux/mfd/syscon.h>
# include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
# include <linux/regmap.h>
2013-05-28 14:20:07 +08:00
2013-06-29 08:27:54 +04:00
struct imx_weim_devtype {
unsigned int cs_count ;
unsigned int cs_regs_count ;
unsigned int cs_stride ;
2019-07-12 16:43:15 -04:00
unsigned int wcr_offset ;
unsigned int wcr_bcm ;
2021-12-02 08:57:23 +03:00
unsigned int wcr_cont_bclk ;
2013-06-29 08:27:54 +04:00
} ;
static const struct imx_weim_devtype imx1_weim_devtype = {
. cs_count = 6 ,
. cs_regs_count = 2 ,
. cs_stride = 0x08 ,
} ;
static const struct imx_weim_devtype imx27_weim_devtype = {
. cs_count = 6 ,
. cs_regs_count = 3 ,
. cs_stride = 0x10 ,
} ;
static const struct imx_weim_devtype imx50_weim_devtype = {
. cs_count = 4 ,
. cs_regs_count = 6 ,
. cs_stride = 0x18 ,
2019-07-12 16:43:15 -04:00
. wcr_offset = 0x90 ,
. wcr_bcm = BIT ( 0 ) ,
2021-12-02 08:57:23 +03:00
. wcr_cont_bclk = BIT ( 3 ) ,
2013-06-29 08:27:54 +04:00
} ;
static const struct imx_weim_devtype imx51_weim_devtype = {
. cs_count = 6 ,
. cs_regs_count = 6 ,
. cs_stride = 0x18 ,
} ;
2018-06-28 17:04:21 -07:00
# define MAX_CS_REGS_COUNT 6
bus: imx-weim: guard against timing configuration conflicts
When specifying weim child devices, there is a risk that more than
one timing setting is specified for the same chip select.
The driver cannot support such a configuration.
In case of conflict, this patch will print a warning to the log,
and will ignore the child node in question.
In this example, node acme@1 will be ignored, as it tries to modify
timing settings for CS0:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something else>;
};
};
However in this example, the driver will be happy:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something>;
};
};
Signed-off-by: Sven Van Asbroeck <TheSven73@googlemail.com>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
2018-12-17 10:48:00 -05:00
# define MAX_CS_COUNT 6
2018-12-17 10:47:59 -05:00
# define OF_REG_SIZE 3
2018-06-28 17:04:21 -07:00
bus: imx-weim: guard against timing configuration conflicts
When specifying weim child devices, there is a risk that more than
one timing setting is specified for the same chip select.
The driver cannot support such a configuration.
In case of conflict, this patch will print a warning to the log,
and will ignore the child node in question.
In this example, node acme@1 will be ignored, as it tries to modify
timing settings for CS0:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something else>;
};
};
However in this example, the driver will be happy:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something>;
};
};
Signed-off-by: Sven Van Asbroeck <TheSven73@googlemail.com>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
2018-12-17 10:48:00 -05:00
struct cs_timing {
bool is_applied ;
u32 regs [ MAX_CS_REGS_COUNT ] ;
} ;
struct cs_timing_state {
struct cs_timing cs [ MAX_CS_COUNT ] ;
} ;
2022-02-22 08:20:59 +03:00
struct weim_priv {
void __iomem * base ;
struct cs_timing_state timing_state ;
} ;
2013-05-28 14:20:07 +08:00
static const struct of_device_id weim_id_table [ ] = {
2013-06-29 08:27:54 +04:00
/* i.MX1/21 */
{ . compatible = " fsl,imx1-weim " , . data = & imx1_weim_devtype , } ,
/* i.MX25/27/31/35 */
{ . compatible = " fsl,imx27-weim " , . data = & imx27_weim_devtype , } ,
/* i.MX50/53/6Q */
{ . compatible = " fsl,imx50-weim " , . data = & imx50_weim_devtype , } ,
{ . compatible = " fsl,imx6q-weim " , . data = & imx50_weim_devtype , } ,
/* i.MX51 */
{ . compatible = " fsl,imx51-weim " , . data = & imx51_weim_devtype , } ,
{ }
2013-05-28 14:20:07 +08:00
} ;
MODULE_DEVICE_TABLE ( of , weim_id_table ) ;
2019-09-04 15:19:56 +02:00
static int imx_weim_gpr_setup ( struct platform_device * pdev )
2014-02-11 09:52:09 +08:00
{
struct device_node * np = pdev - > dev . of_node ;
2023-02-01 15:58:54 -06:00
struct of_range_parser parser ;
struct of_range range ;
2014-02-11 09:52:09 +08:00
struct regmap * gpr ;
u32 gprvals [ 4 ] = {
05 , /* CS0(128M) CS1(0M) CS2(0M) CS3(0M) */
033 , /* CS0(64M) CS1(64M) CS2(0M) CS3(0M) */
0113 , /* CS0(64M) CS1(32M) CS2(32M) CS3(0M) */
01111 , /* CS0(32M) CS1(32M) CS2(32M) CS3(32M) */
} ;
u32 gprval = 0 ;
u32 val ;
int cs = 0 ;
int i = 0 ;
gpr = syscon_regmap_lookup_by_phandle ( np , " fsl,weim-cs-gpr " ) ;
if ( IS_ERR ( gpr ) ) {
dev_dbg ( & pdev - > dev , " failed to find weim-cs-gpr \n " ) ;
return 0 ;
}
2023-02-01 15:58:54 -06:00
if ( of_range_parser_init ( & parser , np ) )
goto err ;
for_each_of_range ( & parser , & range ) {
cs = range . bus_addr > > 32 ;
val = ( range . size / SZ_32M ) | 1 ;
gprval | = val < < cs * 3 ;
2014-02-11 09:52:09 +08:00
i + + ;
}
if ( i = = 0 | | i % 4 )
goto err ;
for ( i = 0 ; i < ARRAY_SIZE ( gprvals ) ; i + + ) {
if ( gprval = = gprvals [ i ] ) {
/* Found it. Set up IOMUXC_GPR1[11:0] with it. */
regmap_update_bits ( gpr , IOMUXC_GPR1 , 0xfff , gprval ) ;
return 0 ;
}
}
err :
dev_err ( & pdev - > dev , " Invalid 'ranges' configuration \n " ) ;
return - EINVAL ;
}
2013-05-28 14:20:07 +08:00
/* Parse and set the timing for this device. */
2022-02-22 08:20:59 +03:00
static int weim_timing_setup ( struct device * dev , struct device_node * np ,
const struct imx_weim_devtype * devtype )
2013-05-28 14:20:07 +08:00
{
2018-06-28 17:04:21 -07:00
u32 cs_idx , value [ MAX_CS_REGS_COUNT ] ;
2013-06-29 08:27:54 +04:00
int i , ret ;
2018-12-17 10:47:59 -05:00
int reg_idx , num_regs ;
bus: imx-weim: guard against timing configuration conflicts
When specifying weim child devices, there is a risk that more than
one timing setting is specified for the same chip select.
The driver cannot support such a configuration.
In case of conflict, this patch will print a warning to the log,
and will ignore the child node in question.
In this example, node acme@1 will be ignored, as it tries to modify
timing settings for CS0:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something else>;
};
};
However in this example, the driver will be happy:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something>;
};
};
Signed-off-by: Sven Van Asbroeck <TheSven73@googlemail.com>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
2018-12-17 10:48:00 -05:00
struct cs_timing * cst ;
2022-02-22 08:20:59 +03:00
struct weim_priv * priv ;
struct cs_timing_state * ts ;
void __iomem * base ;
2013-05-28 14:20:07 +08:00
2018-06-28 17:04:21 -07:00
if ( WARN_ON ( devtype - > cs_regs_count > MAX_CS_REGS_COUNT ) )
return - EINVAL ;
bus: imx-weim: guard against timing configuration conflicts
When specifying weim child devices, there is a risk that more than
one timing setting is specified for the same chip select.
The driver cannot support such a configuration.
In case of conflict, this patch will print a warning to the log,
and will ignore the child node in question.
In this example, node acme@1 will be ignored, as it tries to modify
timing settings for CS0:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something else>;
};
};
However in this example, the driver will be happy:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something>;
};
};
Signed-off-by: Sven Van Asbroeck <TheSven73@googlemail.com>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
2018-12-17 10:48:00 -05:00
if ( WARN_ON ( devtype - > cs_count > MAX_CS_COUNT ) )
return - EINVAL ;
2018-06-28 17:04:21 -07:00
2022-02-22 08:20:59 +03:00
priv = dev_get_drvdata ( dev ) ;
base = priv - > base ;
ts = & priv - > timing_state ;
2018-12-17 10:47:59 -05:00
ret = of_property_read_u32_array ( np , " fsl,weim-cs-timing " ,
value , devtype - > cs_regs_count ) ;
2013-05-28 14:20:07 +08:00
if ( ret )
return ret ;
2018-12-17 10:47:59 -05:00
/*
* the child node ' s " reg " property may contain multiple address ranges ,
* extract the chip select for each .
*/
num_regs = of_property_count_elems_of_size ( np , " reg " , OF_REG_SIZE ) ;
if ( num_regs < 0 )
return num_regs ;
if ( ! num_regs )
2013-05-28 14:20:07 +08:00
return - EINVAL ;
2018-12-17 10:47:59 -05:00
for ( reg_idx = 0 ; reg_idx < num_regs ; reg_idx + + ) {
/* get the CS index from this child node's "reg" property. */
ret = of_property_read_u32_index ( np , " reg " ,
reg_idx * OF_REG_SIZE , & cs_idx ) ;
if ( ret )
break ;
2013-05-28 14:20:07 +08:00
2018-12-17 10:47:59 -05:00
if ( cs_idx > = devtype - > cs_count )
return - EINVAL ;
2013-05-28 14:20:07 +08:00
bus: imx-weim: guard against timing configuration conflicts
When specifying weim child devices, there is a risk that more than
one timing setting is specified for the same chip select.
The driver cannot support such a configuration.
In case of conflict, this patch will print a warning to the log,
and will ignore the child node in question.
In this example, node acme@1 will be ignored, as it tries to modify
timing settings for CS0:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something else>;
};
};
However in this example, the driver will be happy:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something>;
};
};
Signed-off-by: Sven Van Asbroeck <TheSven73@googlemail.com>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
2018-12-17 10:48:00 -05:00
/* prevent re-configuring a CS that's already been configured */
cst = & ts - > cs [ cs_idx ] ;
if ( cst - > is_applied & & memcmp ( value , cst - > regs ,
devtype - > cs_regs_count * sizeof ( u32 ) ) ) {
dev_err ( dev , " fsl,weim-cs-timing conflict on %pOF " , np ) ;
return - EINVAL ;
}
2018-12-17 10:47:59 -05:00
/* set the timing for WEIM */
for ( i = 0 ; i < devtype - > cs_regs_count ; i + + )
writel ( value [ i ] ,
base + cs_idx * devtype - > cs_stride + i * 4 ) ;
bus: imx-weim: guard against timing configuration conflicts
When specifying weim child devices, there is a risk that more than
one timing setting is specified for the same chip select.
The driver cannot support such a configuration.
In case of conflict, this patch will print a warning to the log,
and will ignore the child node in question.
In this example, node acme@1 will be ignored, as it tries to modify
timing settings for CS0:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something else>;
};
};
However in this example, the driver will be happy:
&weim {
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>;
fsl,weim-cs-timing = <something>;
};
acme@1 {
compatible = "acme,whatnot";
reg = <0 0x500 0x100>;
fsl,weim-cs-timing = <something>;
};
};
Signed-off-by: Sven Van Asbroeck <TheSven73@googlemail.com>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
2018-12-17 10:48:00 -05:00
if ( ! cst - > is_applied ) {
cst - > is_applied = true ;
memcpy ( cst - > regs , value ,
devtype - > cs_regs_count * sizeof ( u32 ) ) ;
}
2018-12-17 10:47:59 -05:00
}
2013-06-29 08:27:54 +04:00
2013-05-28 14:20:07 +08:00
return 0 ;
}
2022-02-22 08:20:59 +03:00
static int weim_parse_dt ( struct platform_device * pdev )
2013-05-28 14:20:07 +08:00
{
2013-06-29 08:27:54 +04:00
const struct of_device_id * of_id = of_match_device ( weim_id_table ,
& pdev - > dev ) ;
const struct imx_weim_devtype * devtype = of_id - > data ;
2023-03-06 16:25:26 +03:00
int ret = 0 , have_child = 0 ;
2013-05-28 14:20:07 +08:00
struct device_node * child ;
2022-02-22 08:20:59 +03:00
struct weim_priv * priv ;
void __iomem * base ;
2019-07-12 16:43:15 -04:00
u32 reg ;
2013-05-28 14:20:07 +08:00
2014-02-11 09:52:09 +08:00
if ( devtype = = & imx50_weim_devtype ) {
ret = imx_weim_gpr_setup ( pdev ) ;
if ( ret )
return ret ;
}
2022-02-22 08:20:59 +03:00
priv = dev_get_drvdata ( & pdev - > dev ) ;
base = priv - > base ;
2019-07-12 16:43:15 -04:00
if ( of_property_read_bool ( pdev - > dev . of_node , " fsl,burst-clk-enable " ) ) {
if ( devtype - > wcr_bcm ) {
reg = readl ( base + devtype - > wcr_offset ) ;
2021-12-02 08:57:23 +03:00
reg | = devtype - > wcr_bcm ;
if ( of_property_read_bool ( pdev - > dev . of_node ,
" fsl,continuous-burst-clk " ) ) {
if ( devtype - > wcr_cont_bclk ) {
reg | = devtype - > wcr_cont_bclk ;
} else {
dev_err ( & pdev - > dev ,
" continuous burst clk not supported. \n " ) ;
return - EINVAL ;
}
}
writel ( reg , base + devtype - > wcr_offset ) ;
2019-07-12 16:43:15 -04:00
} else {
dev_err ( & pdev - > dev , " burst clk mode not supported. \n " ) ;
return - EINVAL ;
}
}
2016-02-22 09:01:53 -03:00
for_each_available_child_of_node ( pdev - > dev . of_node , child ) {
2022-02-22 08:20:59 +03:00
ret = weim_timing_setup ( & pdev - > dev , child , devtype ) ;
2015-02-18 23:24:10 -08:00
if ( ret )
2017-07-18 16:42:51 -05:00
dev_warn ( & pdev - > dev , " %pOF set timing failed. \n " ,
child ) ;
2015-02-18 23:24:10 -08:00
else
have_child = 1 ;
2013-05-28 14:20:07 +08:00
}
2015-02-18 23:24:10 -08:00
if ( have_child )
2016-06-01 14:53:07 +08:00
ret = of_platform_default_populate ( pdev - > dev . of_node ,
NULL , & pdev - > dev ) ;
2013-05-28 14:20:07 +08:00
if ( ret )
2017-07-18 16:42:51 -05:00
dev_err ( & pdev - > dev , " %pOF fail to create devices. \n " ,
pdev - > dev . of_node ) ;
2013-05-28 14:20:07 +08:00
return ret ;
}
2019-08-14 10:23:16 +02:00
static int weim_probe ( struct platform_device * pdev )
2013-05-28 14:20:07 +08:00
{
2022-02-22 08:20:59 +03:00
struct weim_priv * priv ;
2013-06-29 08:27:50 +04:00
struct clk * clk ;
void __iomem * base ;
2013-06-29 08:27:51 +04:00
int ret ;
2013-05-28 14:20:07 +08:00
2022-02-22 08:20:59 +03:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2013-05-28 14:20:07 +08:00
/* get the resource */
2023-07-09 15:39:44 +02:00
base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2013-06-29 08:27:51 +04:00
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
2013-05-28 14:20:07 +08:00
2022-02-22 08:20:59 +03:00
priv - > base = base ;
dev_set_drvdata ( & pdev - > dev , priv ) ;
2013-05-28 14:20:07 +08:00
/* get the clock */
2013-06-29 08:27:50 +04:00
clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( clk ) )
2013-06-29 08:27:51 +04:00
return PTR_ERR ( clk ) ;
2013-05-28 14:20:07 +08:00
2013-06-29 08:27:50 +04:00
ret = clk_prepare_enable ( clk ) ;
2013-05-28 14:20:07 +08:00
if ( ret )
2013-06-29 08:27:51 +04:00
return ret ;
2013-05-28 14:20:07 +08:00
/* parse the device node */
2022-02-22 08:20:59 +03:00
ret = weim_parse_dt ( pdev ) ;
2013-06-29 08:27:51 +04:00
if ( ret )
2013-06-29 08:27:50 +04:00
clk_disable_unprepare ( clk ) ;
2013-06-29 08:27:51 +04:00
else
dev_info ( & pdev - > dev , " Driver registered. \n " ) ;
2013-05-28 14:20:07 +08:00
return ret ;
}
2022-02-22 08:20:59 +03:00
# if IS_ENABLED(CONFIG_OF_DYNAMIC)
static int of_weim_notify ( struct notifier_block * nb , unsigned long action ,
void * arg )
{
const struct imx_weim_devtype * devtype ;
struct of_reconfig_data * rd = arg ;
const struct of_device_id * of_id ;
struct platform_device * pdev ;
int ret = NOTIFY_OK ;
switch ( of_reconfig_get_state_change ( action , rd ) ) {
case OF_RECONFIG_CHANGE_ADD :
of_id = of_match_node ( weim_id_table , rd - > dn - > parent ) ;
if ( ! of_id )
return NOTIFY_OK ; /* not for us */
devtype = of_id - > data ;
pdev = of_find_device_by_node ( rd - > dn - > parent ) ;
if ( ! pdev ) {
pr_err ( " %s: could not find platform device for '%pOF' \n " ,
__func__ , rd - > dn - > parent ) ;
return notifier_from_errno ( - EINVAL ) ;
}
if ( weim_timing_setup ( & pdev - > dev , rd - > dn , devtype ) )
dev_warn ( & pdev - > dev ,
" Failed to setup timing for '%pOF' \n " , rd - > dn ) ;
if ( ! of_node_check_flag ( rd - > dn , OF_POPULATED ) ) {
2023-03-30 15:26:13 +02:00
/*
* Clear the flag before adding the device so that
* fw_devlink doesn ' t skip adding consumers to this
* device .
*/
rd - > dn - > fwnode . flags & = ~ FWNODE_FLAG_NOT_DEVICE ;
2022-02-22 08:20:59 +03:00
if ( ! of_platform_device_create ( rd - > dn , NULL , & pdev - > dev ) ) {
dev_err ( & pdev - > dev ,
" Failed to create child device '%pOF' \n " ,
rd - > dn ) ;
ret = notifier_from_errno ( - EINVAL ) ;
}
}
platform_device_put ( pdev ) ;
break ;
case OF_RECONFIG_CHANGE_REMOVE :
if ( ! of_node_check_flag ( rd - > dn , OF_POPULATED ) )
return NOTIFY_OK ; /* device already destroyed */
of_id = of_match_node ( weim_id_table , rd - > dn - > parent ) ;
if ( ! of_id )
return NOTIFY_OK ; /* not for us */
pdev = of_find_device_by_node ( rd - > dn ) ;
if ( ! pdev ) {
2022-02-23 11:51:45 +08:00
pr_err ( " Could not find platform device for '%pOF' \n " ,
2022-02-22 08:20:59 +03:00
rd - > dn ) ;
ret = notifier_from_errno ( - EINVAL ) ;
} else {
of_platform_device_destroy ( & pdev - > dev , NULL ) ;
platform_device_put ( pdev ) ;
}
break ;
default :
break ;
}
return ret ;
}
2022-02-26 09:43:33 +00:00
static struct notifier_block weim_of_notifier = {
2022-02-22 08:20:59 +03:00
. notifier_call = of_weim_notify ,
} ;
# endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
2013-05-28 14:20:07 +08:00
static struct platform_driver weim_driver = {
. driver = {
2013-06-29 08:27:53 +04:00
. name = " imx-weim " ,
. of_match_table = weim_id_table ,
2013-05-28 14:20:07 +08:00
} ,
2019-08-14 10:23:16 +02:00
. probe = weim_probe ,
2013-05-28 14:20:07 +08:00
} ;
2022-02-22 08:20:59 +03:00
static int __init weim_init ( void )
{
# if IS_ENABLED(CONFIG_OF_DYNAMIC)
WARN_ON ( of_reconfig_notifier_register ( & weim_of_notifier ) ) ;
# endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
return platform_driver_register ( & weim_driver ) ;
}
module_init ( weim_init ) ;
static void __exit weim_exit ( void )
{
# if IS_ENABLED(CONFIG_OF_DYNAMIC)
of_reconfig_notifier_unregister ( & weim_of_notifier ) ;
# endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
return platform_driver_unregister ( & weim_driver ) ;
}
module_exit ( weim_exit ) ;
2013-05-28 14:20:07 +08:00
MODULE_AUTHOR ( " Freescale Semiconductor Inc. " ) ;
MODULE_DESCRIPTION ( " i.MX EIM Controller Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;