2012-09-07 01:07:19 +04:00
/*
* pin - controller / pin - mux / pin - config / gpio - driver for Samsung ' s SoC ' s .
*
* Copyright ( c ) 2012 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com
* Copyright ( c ) 2012 Linaro Ltd
* http : //www.linaro.org
*
* Author : Thomas Abraham < thomas . ab @ samsung . com >
*
* 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 driver implements the Samsung pinctrl driver . It supports setting up of
* pinmux and pinconf configurations . The gpiolib interface is also included .
* External interrupt ( gpio and wakeup ) support are not included in this driver
* but provides extensions to which platform specific implementation of the gpio
* and wakeup interrupts can be hooked to .
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/err.h>
# include <linux/gpio.h>
2012-10-11 12:11:20 +04:00
# include <linux/irqdomain.h>
2013-03-19 01:31:50 +04:00
# include <linux/spinlock.h>
2013-05-17 08:33:18 +04:00
# include <linux/syscore_ops.h>
2012-09-07 01:07:19 +04:00
# include "core.h"
# include "pinctrl-samsung.h"
# define GROUP_SUFFIX "-grp"
# define GSUFFIX_LEN sizeof(GROUP_SUFFIX)
# define FUNCTION_SUFFIX "-mux"
# define FSUFFIX_LEN sizeof(FUNCTION_SUFFIX)
/* list of all possible config options supported */
2012-12-10 04:54:03 +04:00
static struct pin_config {
2012-09-07 01:07:19 +04:00
char * prop_cfg ;
unsigned int cfg_type ;
} pcfgs [ ] = {
{ " samsung,pin-pud " , PINCFG_TYPE_PUD } ,
{ " samsung,pin-drv " , PINCFG_TYPE_DRV } ,
{ " samsung,pin-con-pdn " , PINCFG_TYPE_CON_PDN } ,
{ " samsung,pin-pud-pdn " , PINCFG_TYPE_PUD_PDN } ,
} ;
2013-05-17 08:33:18 +04:00
/* Global list of devices (struct samsung_pinctrl_drv_data) */
LIST_HEAD ( drvdata_list ) ;
2012-11-24 06:20:51 +04:00
static unsigned int pin_base ;
2012-10-11 12:11:09 +04:00
2012-10-11 12:11:17 +04:00
static inline struct samsung_pin_bank * gc_to_pin_bank ( struct gpio_chip * gc )
{
return container_of ( gc , struct samsung_pin_bank , gpio_chip ) ;
}
2012-09-07 01:07:19 +04:00
/* check if the selector is a valid pin group selector */
static int samsung_get_group_count ( struct pinctrl_dev * pctldev )
{
struct samsung_pinctrl_drv_data * drvdata ;
drvdata = pinctrl_dev_get_drvdata ( pctldev ) ;
return drvdata - > nr_groups ;
}
/* return the name of the group selected by the group selector */
static const char * samsung_get_group_name ( struct pinctrl_dev * pctldev ,
unsigned selector )
{
struct samsung_pinctrl_drv_data * drvdata ;
drvdata = pinctrl_dev_get_drvdata ( pctldev ) ;
return drvdata - > pin_groups [ selector ] . name ;
}
/* return the pin numbers associated with the specified group */
static int samsung_get_group_pins ( struct pinctrl_dev * pctldev ,
unsigned selector , const unsigned * * pins , unsigned * num_pins )
{
struct samsung_pinctrl_drv_data * drvdata ;
drvdata = pinctrl_dev_get_drvdata ( pctldev ) ;
* pins = drvdata - > pin_groups [ selector ] . pins ;
* num_pins = drvdata - > pin_groups [ selector ] . num_pins ;
return 0 ;
}
/* create pinctrl_map entries by parsing device tree nodes */
static int samsung_dt_node_to_map ( struct pinctrl_dev * pctldev ,
struct device_node * np , struct pinctrl_map * * maps ,
unsigned * nmaps )
{
struct device * dev = pctldev - > dev ;
struct pinctrl_map * map ;
unsigned long * cfg = NULL ;
char * gname , * fname ;
int cfg_cnt = 0 , map_cnt = 0 , idx = 0 ;
/* count the number of config options specfied in the node */
for ( idx = 0 ; idx < ARRAY_SIZE ( pcfgs ) ; idx + + ) {
if ( of_find_property ( np , pcfgs [ idx ] . prop_cfg , NULL ) )
cfg_cnt + + ;
}
/*
* Find out the number of map entries to create . All the config options
* can be accomadated into a single config map entry .
*/
if ( cfg_cnt )
map_cnt = 1 ;
if ( of_find_property ( np , " samsung,pin-function " , NULL ) )
map_cnt + + ;
if ( ! map_cnt ) {
dev_err ( dev , " node %s does not have either config or function "
" configurations \n " , np - > name ) ;
return - EINVAL ;
}
/* Allocate memory for pin-map entries */
map = kzalloc ( sizeof ( * map ) * map_cnt , GFP_KERNEL ) ;
if ( ! map ) {
dev_err ( dev , " could not alloc memory for pin-maps \n " ) ;
return - ENOMEM ;
}
* nmaps = 0 ;
/*
* Allocate memory for pin group name . The pin group name is derived
* from the node name from which these map entries are be created .
*/
gname = kzalloc ( strlen ( np - > name ) + GSUFFIX_LEN , GFP_KERNEL ) ;
if ( ! gname ) {
dev_err ( dev , " failed to alloc memory for group name \n " ) ;
goto free_map ;
}
sprintf ( gname , " %s%s " , np - > name , GROUP_SUFFIX ) ;
/*
* don ' t have config options ? then skip over to creating function
* map entries .
*/
if ( ! cfg_cnt )
goto skip_cfgs ;
/* Allocate memory for config entries */
cfg = kzalloc ( sizeof ( * cfg ) * cfg_cnt , GFP_KERNEL ) ;
if ( ! cfg ) {
dev_err ( dev , " failed to alloc memory for configs \n " ) ;
goto free_gname ;
}
/* Prepare a list of config settings */
for ( idx = 0 , cfg_cnt = 0 ; idx < ARRAY_SIZE ( pcfgs ) ; idx + + ) {
u32 value ;
if ( ! of_property_read_u32 ( np , pcfgs [ idx ] . prop_cfg , & value ) )
cfg [ cfg_cnt + + ] =
PINCFG_PACK ( pcfgs [ idx ] . cfg_type , value ) ;
}
/* create the config map entry */
map [ * nmaps ] . data . configs . group_or_pin = gname ;
map [ * nmaps ] . data . configs . configs = cfg ;
map [ * nmaps ] . data . configs . num_configs = cfg_cnt ;
map [ * nmaps ] . type = PIN_MAP_TYPE_CONFIGS_GROUP ;
* nmaps + = 1 ;
skip_cfgs :
/* create the function map entry */
if ( of_find_property ( np , " samsung,pin-function " , NULL ) ) {
fname = kzalloc ( strlen ( np - > name ) + FSUFFIX_LEN , GFP_KERNEL ) ;
if ( ! fname ) {
dev_err ( dev , " failed to alloc memory for func name \n " ) ;
goto free_cfg ;
}
sprintf ( fname , " %s%s " , np - > name , FUNCTION_SUFFIX ) ;
map [ * nmaps ] . data . mux . group = gname ;
map [ * nmaps ] . data . mux . function = fname ;
map [ * nmaps ] . type = PIN_MAP_TYPE_MUX_GROUP ;
* nmaps + = 1 ;
}
* maps = map ;
return 0 ;
free_cfg :
kfree ( cfg ) ;
free_gname :
kfree ( gname ) ;
free_map :
kfree ( map ) ;
return - ENOMEM ;
}
/* free the memory allocated to hold the pin-map table */
static void samsung_dt_free_map ( struct pinctrl_dev * pctldev ,
struct pinctrl_map * map , unsigned num_maps )
{
int idx ;
for ( idx = 0 ; idx < num_maps ; idx + + ) {
if ( map [ idx ] . type = = PIN_MAP_TYPE_MUX_GROUP ) {
kfree ( map [ idx ] . data . mux . function ) ;
if ( ! idx )
kfree ( map [ idx ] . data . mux . group ) ;
} else if ( map - > type = = PIN_MAP_TYPE_CONFIGS_GROUP ) {
kfree ( map [ idx ] . data . configs . configs ) ;
if ( ! idx )
kfree ( map [ idx ] . data . configs . group_or_pin ) ;
}
} ;
kfree ( map ) ;
}
/* list of pinctrl callbacks for the pinctrl core */
2013-02-16 13:25:07 +04:00
static const struct pinctrl_ops samsung_pctrl_ops = {
2012-09-07 01:07:19 +04:00
. get_groups_count = samsung_get_group_count ,
. get_group_name = samsung_get_group_name ,
. get_group_pins = samsung_get_group_pins ,
. dt_node_to_map = samsung_dt_node_to_map ,
. dt_free_map = samsung_dt_free_map ,
} ;
/* check if the selector is a valid pin function selector */
static int samsung_get_functions_count ( struct pinctrl_dev * pctldev )
{
struct samsung_pinctrl_drv_data * drvdata ;
drvdata = pinctrl_dev_get_drvdata ( pctldev ) ;
return drvdata - > nr_functions ;
}
/* return the name of the pin function specified */
static const char * samsung_pinmux_get_fname ( struct pinctrl_dev * pctldev ,
unsigned selector )
{
struct samsung_pinctrl_drv_data * drvdata ;
drvdata = pinctrl_dev_get_drvdata ( pctldev ) ;
return drvdata - > pmx_functions [ selector ] . name ;
}
/* return the groups associated for the specified function selector */
static int samsung_pinmux_get_groups ( struct pinctrl_dev * pctldev ,
unsigned selector , const char * const * * groups ,
unsigned * const num_groups )
{
struct samsung_pinctrl_drv_data * drvdata ;
drvdata = pinctrl_dev_get_drvdata ( pctldev ) ;
* groups = drvdata - > pmx_functions [ selector ] . groups ;
* num_groups = drvdata - > pmx_functions [ selector ] . num_groups ;
return 0 ;
}
/*
* given a pin number that is local to a pin controller , find out the pin bank
* and the register base of the pin bank .
*/
2012-10-11 12:11:08 +04:00
static void pin_to_reg_bank ( struct samsung_pinctrl_drv_data * drvdata ,
unsigned pin , void __iomem * * reg , u32 * offset ,
2012-09-07 01:07:19 +04:00
struct samsung_pin_bank * * bank )
{
struct samsung_pin_bank * b ;
b = drvdata - > ctrl - > pin_banks ;
while ( ( pin > = b - > pin_base ) & &
( ( b - > pin_base + b - > nr_pins - 1 ) < pin ) )
b + + ;
* reg = drvdata - > virt_base + b - > pctl_offset ;
* offset = pin - b - > pin_base ;
if ( bank )
* bank = b ;
}
/* enable or disable a pinmux function */
static void samsung_pinmux_setup ( struct pinctrl_dev * pctldev , unsigned selector ,
unsigned group , bool enable )
{
struct samsung_pinctrl_drv_data * drvdata ;
const unsigned int * pins ;
struct samsung_pin_bank * bank ;
void __iomem * reg ;
u32 mask , shift , data , pin_offset , cnt ;
2013-03-19 01:31:50 +04:00
unsigned long flags ;
2012-09-07 01:07:19 +04:00
drvdata = pinctrl_dev_get_drvdata ( pctldev ) ;
pins = drvdata - > pin_groups [ group ] . pins ;
/*
* for each pin in the pin group selected , program the correspoding pin
* pin function number in the config register .
*/
for ( cnt = 0 ; cnt < drvdata - > pin_groups [ group ] . num_pins ; cnt + + ) {
2013-03-19 01:31:52 +04:00
struct samsung_pin_bank_type * type ;
2012-10-11 12:11:08 +04:00
pin_to_reg_bank ( drvdata , pins [ cnt ] - drvdata - > ctrl - > base ,
2012-09-07 01:07:19 +04:00
& reg , & pin_offset , & bank ) ;
2013-03-19 01:31:52 +04:00
type = bank - > type ;
mask = ( 1 < < type - > fld_width [ PINCFG_TYPE_FUNC ] ) - 1 ;
shift = pin_offset * type - > fld_width [ PINCFG_TYPE_FUNC ] ;
2013-03-19 01:31:54 +04:00
if ( shift > = 32 ) {
/* Some banks have two config registers */
shift - = 32 ;
reg + = 4 ;
}
2012-09-07 01:07:19 +04:00
2013-03-19 01:31:50 +04:00
spin_lock_irqsave ( & bank - > slock , flags ) ;
2013-03-19 01:31:53 +04:00
data = readl ( reg + type - > reg_offset [ PINCFG_TYPE_FUNC ] ) ;
2012-09-07 01:07:19 +04:00
data & = ~ ( mask < < shift ) ;
if ( enable )
data | = drvdata - > pin_groups [ group ] . func < < shift ;
2013-03-19 01:31:53 +04:00
writel ( data , reg + type - > reg_offset [ PINCFG_TYPE_FUNC ] ) ;
2013-03-19 01:31:50 +04:00
spin_unlock_irqrestore ( & bank - > slock , flags ) ;
2012-09-07 01:07:19 +04:00
}
}
/* enable a specified pinmux by writing to registers */
static int samsung_pinmux_enable ( struct pinctrl_dev * pctldev , unsigned selector ,
unsigned group )
{
samsung_pinmux_setup ( pctldev , selector , group , true ) ;
return 0 ;
}
/* disable a specified pinmux by writing to registers */
static void samsung_pinmux_disable ( struct pinctrl_dev * pctldev ,
unsigned selector , unsigned group )
{
samsung_pinmux_setup ( pctldev , selector , group , false ) ;
}
/*
* The calls to gpio_direction_output ( ) and gpio_direction_input ( )
* leads to this function call ( via the pinctrl_gpio_direction_ { input | output } ( )
* function called from the gpiolib interface ) .
*/
static int samsung_pinmux_gpio_set_direction ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range , unsigned offset , bool input )
{
2013-03-19 01:31:52 +04:00
struct samsung_pin_bank_type * type ;
2012-09-07 01:07:19 +04:00
struct samsung_pin_bank * bank ;
2012-10-11 12:11:08 +04:00
struct samsung_pinctrl_drv_data * drvdata ;
2012-09-07 01:07:19 +04:00
void __iomem * reg ;
u32 data , pin_offset , mask , shift ;
2013-03-19 01:31:50 +04:00
unsigned long flags ;
2012-09-07 01:07:19 +04:00
2012-10-11 12:11:17 +04:00
bank = gc_to_pin_bank ( range - > gc ) ;
2013-03-19 01:31:52 +04:00
type = bank - > type ;
2012-10-11 12:11:08 +04:00
drvdata = pinctrl_dev_get_drvdata ( pctldev ) ;
2012-10-11 12:11:17 +04:00
pin_offset = offset - bank - > pin_base ;
2013-03-19 01:31:53 +04:00
reg = drvdata - > virt_base + bank - > pctl_offset +
type - > reg_offset [ PINCFG_TYPE_FUNC ] ;
2012-10-11 12:11:17 +04:00
2013-03-19 01:31:52 +04:00
mask = ( 1 < < type - > fld_width [ PINCFG_TYPE_FUNC ] ) - 1 ;
shift = pin_offset * type - > fld_width [ PINCFG_TYPE_FUNC ] ;
2013-03-19 01:31:54 +04:00
if ( shift > = 32 ) {
/* Some banks have two config registers */
shift - = 32 ;
reg + = 4 ;
}
2012-09-07 01:07:19 +04:00
2013-03-19 01:31:50 +04:00
spin_lock_irqsave ( & bank - > slock , flags ) ;
2012-09-07 01:07:19 +04:00
data = readl ( reg ) ;
data & = ~ ( mask < < shift ) ;
if ( ! input )
data | = FUNC_OUTPUT < < shift ;
writel ( data , reg ) ;
2013-03-19 01:31:50 +04:00
spin_unlock_irqrestore ( & bank - > slock , flags ) ;
2012-09-07 01:07:19 +04:00
return 0 ;
}
/* list of pinmux callbacks for the pinmux vertical in pinctrl core */
2013-02-16 13:25:07 +04:00
static const struct pinmux_ops samsung_pinmux_ops = {
2012-09-07 01:07:19 +04:00
. get_functions_count = samsung_get_functions_count ,
. get_function_name = samsung_pinmux_get_fname ,
. get_function_groups = samsung_pinmux_get_groups ,
. enable = samsung_pinmux_enable ,
. disable = samsung_pinmux_disable ,
. gpio_set_direction = samsung_pinmux_gpio_set_direction ,
} ;
/* set or get the pin config settings for a specified pin */
static int samsung_pinconf_rw ( struct pinctrl_dev * pctldev , unsigned int pin ,
unsigned long * config , bool set )
{
struct samsung_pinctrl_drv_data * drvdata ;
2013-03-19 01:31:52 +04:00
struct samsung_pin_bank_type * type ;
2012-09-07 01:07:19 +04:00
struct samsung_pin_bank * bank ;
void __iomem * reg_base ;
enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE ( * config ) ;
u32 data , width , pin_offset , mask , shift ;
u32 cfg_value , cfg_reg ;
2013-03-19 01:31:50 +04:00
unsigned long flags ;
2012-09-07 01:07:19 +04:00
drvdata = pinctrl_dev_get_drvdata ( pctldev ) ;
2012-10-11 12:11:08 +04:00
pin_to_reg_bank ( drvdata , pin - drvdata - > ctrl - > base , & reg_base ,
2012-09-07 01:07:19 +04:00
& pin_offset , & bank ) ;
2013-03-19 01:31:52 +04:00
type = bank - > type ;
2012-09-07 01:07:19 +04:00
2013-03-19 01:31:52 +04:00
if ( cfg_type > = PINCFG_TYPE_NUM | | ! type - > fld_width [ cfg_type ] )
2012-10-11 12:11:07 +04:00
return - EINVAL ;
2013-03-19 01:31:52 +04:00
width = type - > fld_width [ cfg_type ] ;
2013-03-19 01:31:53 +04:00
cfg_reg = type - > reg_offset [ cfg_type ] ;
2013-03-19 01:31:52 +04:00
2013-03-19 01:31:50 +04:00
spin_lock_irqsave ( & bank - > slock , flags ) ;
2012-09-07 01:07:19 +04:00
mask = ( 1 < < width ) - 1 ;
shift = pin_offset * width ;
data = readl ( reg_base + cfg_reg ) ;
if ( set ) {
cfg_value = PINCFG_UNPACK_VALUE ( * config ) ;
data & = ~ ( mask < < shift ) ;
data | = ( cfg_value < < shift ) ;
writel ( data , reg_base + cfg_reg ) ;
} else {
data > > = shift ;
data & = mask ;
* config = PINCFG_PACK ( cfg_type , data ) ;
}
2013-03-19 01:31:50 +04:00
spin_unlock_irqrestore ( & bank - > slock , flags ) ;
2012-09-07 01:07:19 +04:00
return 0 ;
}
/* set the pin config settings for a specified pin */
static int samsung_pinconf_set ( struct pinctrl_dev * pctldev , unsigned int pin ,
unsigned long config )
{
return samsung_pinconf_rw ( pctldev , pin , & config , true ) ;
}
/* get the pin config settings for a specified pin */
static int samsung_pinconf_get ( struct pinctrl_dev * pctldev , unsigned int pin ,
unsigned long * config )
{
return samsung_pinconf_rw ( pctldev , pin , config , false ) ;
}
/* set the pin config settings for a specified pin group */
static int samsung_pinconf_group_set ( struct pinctrl_dev * pctldev ,
unsigned group , unsigned long config )
{
struct samsung_pinctrl_drv_data * drvdata ;
const unsigned int * pins ;
unsigned int cnt ;
drvdata = pinctrl_dev_get_drvdata ( pctldev ) ;
pins = drvdata - > pin_groups [ group ] . pins ;
for ( cnt = 0 ; cnt < drvdata - > pin_groups [ group ] . num_pins ; cnt + + )
samsung_pinconf_set ( pctldev , pins [ cnt ] , config ) ;
return 0 ;
}
/* get the pin config settings for a specified pin group */
static int samsung_pinconf_group_get ( struct pinctrl_dev * pctldev ,
unsigned int group , unsigned long * config )
{
struct samsung_pinctrl_drv_data * drvdata ;
const unsigned int * pins ;
drvdata = pinctrl_dev_get_drvdata ( pctldev ) ;
pins = drvdata - > pin_groups [ group ] . pins ;
samsung_pinconf_get ( pctldev , pins [ 0 ] , config ) ;
return 0 ;
}
/* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */
2013-02-16 13:25:07 +04:00
static const struct pinconf_ops samsung_pinconf_ops = {
2012-09-07 01:07:19 +04:00
. pin_config_get = samsung_pinconf_get ,
. pin_config_set = samsung_pinconf_set ,
. pin_config_group_get = samsung_pinconf_group_get ,
. pin_config_group_set = samsung_pinconf_group_set ,
} ;
/* gpiolib gpio_set callback function */
static void samsung_gpio_set ( struct gpio_chip * gc , unsigned offset , int value )
{
2012-10-11 12:11:17 +04:00
struct samsung_pin_bank * bank = gc_to_pin_bank ( gc ) ;
2013-03-19 01:31:52 +04:00
struct samsung_pin_bank_type * type = bank - > type ;
2013-03-19 01:31:50 +04:00
unsigned long flags ;
2012-09-07 01:07:19 +04:00
void __iomem * reg ;
2012-10-11 12:11:17 +04:00
u32 data ;
2012-09-07 01:07:19 +04:00
2012-10-11 12:11:17 +04:00
reg = bank - > drvdata - > virt_base + bank - > pctl_offset ;
2012-09-07 01:07:19 +04:00
2013-03-19 01:31:50 +04:00
spin_lock_irqsave ( & bank - > slock , flags ) ;
2013-03-19 01:31:53 +04:00
data = readl ( reg + type - > reg_offset [ PINCFG_TYPE_DAT ] ) ;
2012-10-11 12:11:17 +04:00
data & = ~ ( 1 < < offset ) ;
2012-09-07 01:07:19 +04:00
if ( value )
2012-10-11 12:11:17 +04:00
data | = 1 < < offset ;
2013-03-19 01:31:53 +04:00
writel ( data , reg + type - > reg_offset [ PINCFG_TYPE_DAT ] ) ;
2013-03-19 01:31:50 +04:00
spin_unlock_irqrestore ( & bank - > slock , flags ) ;
2012-09-07 01:07:19 +04:00
}
/* gpiolib gpio_get callback function */
static int samsung_gpio_get ( struct gpio_chip * gc , unsigned offset )
{
void __iomem * reg ;
2012-10-11 12:11:17 +04:00
u32 data ;
struct samsung_pin_bank * bank = gc_to_pin_bank ( gc ) ;
2013-03-19 01:31:53 +04:00
struct samsung_pin_bank_type * type = bank - > type ;
2012-10-11 12:11:08 +04:00
2012-10-11 12:11:17 +04:00
reg = bank - > drvdata - > virt_base + bank - > pctl_offset ;
2012-09-07 01:07:19 +04:00
2013-03-19 01:31:53 +04:00
data = readl ( reg + type - > reg_offset [ PINCFG_TYPE_DAT ] ) ;
2012-10-11 12:11:17 +04:00
data > > = offset ;
2012-09-07 01:07:19 +04:00
data & = 1 ;
return data ;
}
/*
* gpiolib gpio_direction_input callback function . The setting of the pin
* mux function as ' gpio input ' will be handled by the pinctrl susbsystem
* interface .
*/
static int samsung_gpio_direction_input ( struct gpio_chip * gc , unsigned offset )
{
return pinctrl_gpio_direction_input ( gc - > base + offset ) ;
}
/*
* gpiolib gpio_direction_output callback function . The setting of the pin
* mux function as ' gpio output ' will be handled by the pinctrl susbsystem
* interface .
*/
static int samsung_gpio_direction_output ( struct gpio_chip * gc , unsigned offset ,
int value )
{
samsung_gpio_set ( gc , offset , value ) ;
return pinctrl_gpio_direction_output ( gc - > base + offset ) ;
}
2012-10-11 12:11:20 +04:00
/*
* gpiolib gpio_to_irq callback function . Creates a mapping between a GPIO pin
* and a virtual IRQ , if not already present .
*/
static int samsung_gpio_to_irq ( struct gpio_chip * gc , unsigned offset )
{
struct samsung_pin_bank * bank = gc_to_pin_bank ( gc ) ;
unsigned int virq ;
if ( ! bank - > irq_domain )
return - ENXIO ;
virq = irq_create_mapping ( bank - > irq_domain , offset ) ;
return ( virq ) ? : - ENXIO ;
}
2012-09-07 01:07:19 +04:00
/*
* Parse the pin names listed in the ' samsung , pins ' property and convert it
* into a list of gpio numbers are create a pin group from it .
*/
2012-12-22 01:10:23 +04:00
static int samsung_pinctrl_parse_dt_pins ( struct platform_device * pdev ,
struct device_node * cfg_np ,
struct pinctrl_desc * pctl ,
unsigned int * * pin_list ,
unsigned int * npins )
2012-09-07 01:07:19 +04:00
{
struct device * dev = & pdev - > dev ;
struct property * prop ;
struct pinctrl_pin_desc const * pdesc = pctl - > pins ;
unsigned int idx = 0 , cnt ;
const char * pin_name ;
* npins = of_property_count_strings ( cfg_np , " samsung,pins " ) ;
2012-11-19 04:45:19 +04:00
if ( IS_ERR_VALUE ( * npins ) ) {
2012-09-07 01:07:19 +04:00
dev_err ( dev , " invalid pin list in %s node " , cfg_np - > name ) ;
return - EINVAL ;
}
* pin_list = devm_kzalloc ( dev , * npins * sizeof ( * * pin_list ) , GFP_KERNEL ) ;
if ( ! * pin_list ) {
dev_err ( dev , " failed to allocate memory for pin list \n " ) ;
return - ENOMEM ;
}
of_property_for_each_string ( cfg_np , " samsung,pins " , prop , pin_name ) {
for ( cnt = 0 ; cnt < pctl - > npins ; cnt + + ) {
if ( pdesc [ cnt ] . name ) {
if ( ! strcmp ( pin_name , pdesc [ cnt ] . name ) ) {
( * pin_list ) [ idx + + ] = pdesc [ cnt ] . number ;
break ;
}
}
}
if ( cnt = = pctl - > npins ) {
dev_err ( dev , " pin %s not valid in %s node \n " ,
pin_name , cfg_np - > name ) ;
devm_kfree ( dev , * pin_list ) ;
return - EINVAL ;
}
}
return 0 ;
}
/*
* Parse the information about all the available pin groups and pin functions
* from device node of the pin - controller . A pin group is formed with all
* the pins listed in the " samsung,pins " property .
*/
2012-12-22 01:10:23 +04:00
static int samsung_pinctrl_parse_dt ( struct platform_device * pdev ,
struct samsung_pinctrl_drv_data * drvdata )
2012-09-07 01:07:19 +04:00
{
struct device * dev = & pdev - > dev ;
struct device_node * dev_np = dev - > of_node ;
struct device_node * cfg_np ;
struct samsung_pin_group * groups , * grp ;
struct samsung_pmx_func * functions , * func ;
unsigned * pin_list ;
unsigned int npins , grp_cnt , func_idx = 0 ;
char * gname , * fname ;
int ret ;
grp_cnt = of_get_child_count ( dev_np ) ;
if ( ! grp_cnt )
return - EINVAL ;
groups = devm_kzalloc ( dev , grp_cnt * sizeof ( * groups ) , GFP_KERNEL ) ;
if ( ! groups ) {
dev_err ( dev , " failed allocate memory for ping group list \n " ) ;
return - EINVAL ;
}
grp = groups ;
functions = devm_kzalloc ( dev , grp_cnt * sizeof ( * functions ) , GFP_KERNEL ) ;
if ( ! functions ) {
dev_err ( dev , " failed to allocate memory for function list \n " ) ;
return - EINVAL ;
}
func = functions ;
/*
* Iterate over all the child nodes of the pin controller node
* and create pin groups and pin function lists .
*/
for_each_child_of_node ( dev_np , cfg_np ) {
u32 function ;
2012-10-11 12:11:11 +04:00
if ( ! of_find_property ( cfg_np , " samsung,pins " , NULL ) )
2012-09-07 01:07:19 +04:00
continue ;
ret = samsung_pinctrl_parse_dt_pins ( pdev , cfg_np ,
& drvdata - > pctl , & pin_list , & npins ) ;
if ( ret )
return ret ;
/* derive pin group name from the node name */
gname = devm_kzalloc ( dev , strlen ( cfg_np - > name ) + GSUFFIX_LEN ,
GFP_KERNEL ) ;
if ( ! gname ) {
dev_err ( dev , " failed to alloc memory for group name \n " ) ;
return - ENOMEM ;
}
sprintf ( gname , " %s%s " , cfg_np - > name , GROUP_SUFFIX ) ;
grp - > name = gname ;
grp - > pins = pin_list ;
grp - > num_pins = npins ;
of_property_read_u32 ( cfg_np , " samsung,pin-function " , & function ) ;
grp - > func = function ;
grp + + ;
if ( ! of_find_property ( cfg_np , " samsung,pin-function " , NULL ) )
continue ;
/* derive function name from the node name */
fname = devm_kzalloc ( dev , strlen ( cfg_np - > name ) + FSUFFIX_LEN ,
GFP_KERNEL ) ;
if ( ! fname ) {
dev_err ( dev , " failed to alloc memory for func name \n " ) ;
return - ENOMEM ;
}
sprintf ( fname , " %s%s " , cfg_np - > name , FUNCTION_SUFFIX ) ;
func - > name = fname ;
func - > groups = devm_kzalloc ( dev , sizeof ( char * ) , GFP_KERNEL ) ;
if ( ! func - > groups ) {
dev_err ( dev , " failed to alloc memory for group list "
" in pin function " ) ;
return - ENOMEM ;
}
func - > groups [ 0 ] = gname ;
func - > num_groups = 1 ;
func + + ;
func_idx + + ;
}
drvdata - > pin_groups = groups ;
drvdata - > nr_groups = grp_cnt ;
drvdata - > pmx_functions = functions ;
drvdata - > nr_functions = func_idx ;
return 0 ;
}
/* register the pinctrl interface with the pinctrl subsystem */
2012-12-22 01:10:23 +04:00
static int samsung_pinctrl_register ( struct platform_device * pdev ,
struct samsung_pinctrl_drv_data * drvdata )
2012-09-07 01:07:19 +04:00
{
struct pinctrl_desc * ctrldesc = & drvdata - > pctl ;
struct pinctrl_pin_desc * pindesc , * pdesc ;
struct samsung_pin_bank * pin_bank ;
char * pin_names ;
int pin , bank , ret ;
ctrldesc - > name = " samsung-pinctrl " ;
ctrldesc - > owner = THIS_MODULE ;
ctrldesc - > pctlops = & samsung_pctrl_ops ;
ctrldesc - > pmxops = & samsung_pinmux_ops ;
ctrldesc - > confops = & samsung_pinconf_ops ;
pindesc = devm_kzalloc ( & pdev - > dev , sizeof ( * pindesc ) *
drvdata - > ctrl - > nr_pins , GFP_KERNEL ) ;
if ( ! pindesc ) {
dev_err ( & pdev - > dev , " mem alloc for pin descriptors failed \n " ) ;
return - ENOMEM ;
}
ctrldesc - > pins = pindesc ;
ctrldesc - > npins = drvdata - > ctrl - > nr_pins ;
/* dynamically populate the pin number and pin name for pindesc */
for ( pin = 0 , pdesc = pindesc ; pin < ctrldesc - > npins ; pin + + , pdesc + + )
pdesc - > number = pin + drvdata - > ctrl - > base ;
/*
* allocate space for storing the dynamically generated names for all
* the pins which belong to this pin - controller .
*/
pin_names = devm_kzalloc ( & pdev - > dev , sizeof ( char ) * PIN_NAME_LENGTH *
drvdata - > ctrl - > nr_pins , GFP_KERNEL ) ;
if ( ! pin_names ) {
dev_err ( & pdev - > dev , " mem alloc for pin names failed \n " ) ;
return - ENOMEM ;
}
/* for each pin, the name of the pin is pin-bank name + pin number */
for ( bank = 0 ; bank < drvdata - > ctrl - > nr_banks ; bank + + ) {
pin_bank = & drvdata - > ctrl - > pin_banks [ bank ] ;
for ( pin = 0 ; pin < pin_bank - > nr_pins ; pin + + ) {
sprintf ( pin_names , " %s-%d " , pin_bank - > name , pin ) ;
pdesc = pindesc + pin_bank - > pin_base + pin ;
pdesc - > name = pin_names ;
pin_names + = PIN_NAME_LENGTH ;
}
}
drvdata - > pctl_dev = pinctrl_register ( ctrldesc , & pdev - > dev , drvdata ) ;
if ( ! drvdata - > pctl_dev ) {
dev_err ( & pdev - > dev , " could not register pinctrl driver \n " ) ;
return - EINVAL ;
}
2012-10-11 12:11:17 +04:00
for ( bank = 0 ; bank < drvdata - > ctrl - > nr_banks ; + + bank ) {
pin_bank = & drvdata - > ctrl - > pin_banks [ bank ] ;
pin_bank - > grange . name = pin_bank - > name ;
pin_bank - > grange . id = bank ;
pin_bank - > grange . pin_base = pin_bank - > pin_base ;
pin_bank - > grange . base = pin_bank - > gpio_chip . base ;
pin_bank - > grange . npins = pin_bank - > gpio_chip . ngpio ;
pin_bank - > grange . gc = & pin_bank - > gpio_chip ;
pinctrl_add_gpio_range ( drvdata - > pctl_dev , & pin_bank - > grange ) ;
}
2012-09-07 01:07:19 +04:00
ret = samsung_pinctrl_parse_dt ( pdev , drvdata ) ;
if ( ret ) {
pinctrl_unregister ( drvdata - > pctl_dev ) ;
return ret ;
}
return 0 ;
}
2012-10-11 12:11:17 +04:00
static const struct gpio_chip samsung_gpiolib_chip = {
. set = samsung_gpio_set ,
. get = samsung_gpio_get ,
. direction_input = samsung_gpio_direction_input ,
. direction_output = samsung_gpio_direction_output ,
2012-10-11 12:11:20 +04:00
. to_irq = samsung_gpio_to_irq ,
2012-10-11 12:11:17 +04:00
. owner = THIS_MODULE ,
} ;
2012-09-07 01:07:19 +04:00
/* register the gpiolib interface with the gpiolib subsystem */
2012-12-22 01:10:23 +04:00
static int samsung_gpiolib_register ( struct platform_device * pdev ,
struct samsung_pinctrl_drv_data * drvdata )
2012-09-07 01:07:19 +04:00
{
2012-10-11 12:11:17 +04:00
struct samsung_pin_ctrl * ctrl = drvdata - > ctrl ;
struct samsung_pin_bank * bank = ctrl - > pin_banks ;
2012-09-07 01:07:19 +04:00
struct gpio_chip * gc ;
int ret ;
2012-10-11 12:11:17 +04:00
int i ;
for ( i = 0 ; i < ctrl - > nr_banks ; + + i , + + bank ) {
bank - > gpio_chip = samsung_gpiolib_chip ;
gc = & bank - > gpio_chip ;
gc - > base = ctrl - > base + bank - > pin_base ;
gc - > ngpio = bank - > nr_pins ;
gc - > dev = & pdev - > dev ;
gc - > of_node = bank - > of_node ;
gc - > label = bank - > name ;
ret = gpiochip_add ( gc ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to register gpio_chip %s, error code: %d \n " ,
gc - > label , ret ) ;
goto fail ;
}
2012-09-07 01:07:19 +04:00
}
return 0 ;
2012-10-11 12:11:17 +04:00
fail :
for ( - - i , - - bank ; i > = 0 ; - - i , - - bank )
if ( gpiochip_remove ( & bank - > gpio_chip ) )
dev_err ( & pdev - > dev , " gpio chip %s remove failed \n " ,
bank - > gpio_chip . label ) ;
return ret ;
2012-09-07 01:07:19 +04:00
}
/* unregister the gpiolib interface with the gpiolib subsystem */
2012-12-22 01:10:23 +04:00
static int samsung_gpiolib_unregister ( struct platform_device * pdev ,
struct samsung_pinctrl_drv_data * drvdata )
2012-09-07 01:07:19 +04:00
{
2012-10-11 12:11:17 +04:00
struct samsung_pin_ctrl * ctrl = drvdata - > ctrl ;
struct samsung_pin_bank * bank = ctrl - > pin_banks ;
int ret = 0 ;
int i ;
for ( i = 0 ; ! ret & & i < ctrl - > nr_banks ; + + i , + + bank )
ret = gpiochip_remove ( & bank - > gpio_chip ) ;
if ( ret )
2012-09-07 01:07:19 +04:00
dev_err ( & pdev - > dev , " gpio chip remove failed \n " ) ;
2012-10-11 12:11:17 +04:00
return ret ;
2012-09-07 01:07:19 +04:00
}
static const struct of_device_id samsung_pinctrl_dt_match [ ] ;
/* retrieve the soc specific data */
2012-09-21 02:34:04 +04:00
static struct samsung_pin_ctrl * samsung_pinctrl_get_soc_data (
2012-10-11 12:11:14 +04:00
struct samsung_pinctrl_drv_data * d ,
2012-09-07 01:07:19 +04:00
struct platform_device * pdev )
{
int id ;
const struct of_device_id * match ;
2012-10-11 12:11:14 +04:00
struct device_node * node = pdev - > dev . of_node ;
2012-10-11 12:11:13 +04:00
struct device_node * np ;
2012-10-11 12:11:09 +04:00
struct samsung_pin_ctrl * ctrl ;
struct samsung_pin_bank * bank ;
int i ;
2012-09-07 01:07:19 +04:00
2012-10-11 12:11:14 +04:00
id = of_alias_get_id ( node , " pinctrl " ) ;
2012-09-07 01:07:19 +04:00
if ( id < 0 ) {
dev_err ( & pdev - > dev , " failed to get alias id \n " ) ;
return NULL ;
}
match = of_match_node ( samsung_pinctrl_dt_match , node ) ;
2012-10-11 12:11:09 +04:00
ctrl = ( struct samsung_pin_ctrl * ) match - > data + id ;
bank = ctrl - > pin_banks ;
for ( i = 0 ; i < ctrl - > nr_banks ; + + i , + + bank ) {
2013-03-19 01:31:50 +04:00
spin_lock_init ( & bank - > slock ) ;
2012-10-11 12:11:14 +04:00
bank - > drvdata = d ;
2012-10-11 12:11:09 +04:00
bank - > pin_base = ctrl - > nr_pins ;
ctrl - > nr_pins + = bank - > nr_pins ;
}
2012-10-11 12:11:13 +04:00
for_each_child_of_node ( node , np ) {
if ( ! of_find_property ( np , " gpio-controller " , NULL ) )
continue ;
bank = ctrl - > pin_banks ;
for ( i = 0 ; i < ctrl - > nr_banks ; + + i , + + bank ) {
if ( ! strcmp ( bank - > name , np - > name ) ) {
bank - > of_node = np ;
break ;
}
}
}
2012-10-11 12:11:09 +04:00
ctrl - > base = pin_base ;
pin_base + = ctrl - > nr_pins ;
return ctrl ;
2012-09-07 01:07:19 +04:00
}
2012-12-22 01:10:23 +04:00
static int samsung_pinctrl_probe ( struct platform_device * pdev )
2012-09-07 01:07:19 +04:00
{
struct samsung_pinctrl_drv_data * drvdata ;
struct device * dev = & pdev - > dev ;
struct samsung_pin_ctrl * ctrl ;
struct resource * res ;
int ret ;
if ( ! dev - > of_node ) {
dev_err ( dev , " device tree node not found \n " ) ;
return - ENODEV ;
}
drvdata = devm_kzalloc ( dev , sizeof ( * drvdata ) , GFP_KERNEL ) ;
if ( ! drvdata ) {
dev_err ( dev , " failed to allocate memory for driver's "
" private data \n " ) ;
return - ENOMEM ;
}
2012-10-11 12:11:14 +04:00
ctrl = samsung_pinctrl_get_soc_data ( drvdata , pdev ) ;
if ( ! ctrl ) {
dev_err ( & pdev - > dev , " driver data not available \n " ) ;
return - EINVAL ;
}
2012-09-07 01:07:19 +04:00
drvdata - > ctrl = ctrl ;
drvdata - > dev = dev ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-21 14:09:14 +04:00
drvdata - > virt_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( drvdata - > virt_base ) )
return PTR_ERR ( drvdata - > virt_base ) ;
2012-09-07 01:07:19 +04:00
res = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
if ( res )
drvdata - > irq = res - > start ;
ret = samsung_gpiolib_register ( pdev , drvdata ) ;
if ( ret )
return ret ;
ret = samsung_pinctrl_register ( pdev , drvdata ) ;
if ( ret ) {
samsung_gpiolib_unregister ( pdev , drvdata ) ;
return ret ;
}
if ( ctrl - > eint_gpio_init )
ctrl - > eint_gpio_init ( drvdata ) ;
if ( ctrl - > eint_wkup_init )
ctrl - > eint_wkup_init ( drvdata ) ;
platform_set_drvdata ( pdev , drvdata ) ;
2013-05-17 08:33:18 +04:00
/* Add to the global list */
list_add_tail ( & drvdata - > node , & drvdata_list ) ;
2012-09-07 01:07:19 +04:00
return 0 ;
}
2013-05-17 08:33:18 +04:00
# ifdef CONFIG_PM
/**
* samsung_pinctrl_suspend_dev - save pinctrl state for suspend for a device
*
* Save data for all banks handled by this device .
*/
static void samsung_pinctrl_suspend_dev (
struct samsung_pinctrl_drv_data * drvdata )
{
struct samsung_pin_ctrl * ctrl = drvdata - > ctrl ;
void __iomem * virt_base = drvdata - > virt_base ;
int i ;
for ( i = 0 ; i < ctrl - > nr_banks ; i + + ) {
struct samsung_pin_bank * bank = & ctrl - > pin_banks [ i ] ;
void __iomem * reg = virt_base + bank - > pctl_offset ;
u8 * offs = bank - > type - > reg_offset ;
u8 * widths = bank - > type - > fld_width ;
enum pincfg_type type ;
/* Registers without a powerdown config aren't lost */
if ( ! widths [ PINCFG_TYPE_CON_PDN ] )
continue ;
for ( type = 0 ; type < PINCFG_TYPE_NUM ; type + + )
if ( widths [ type ] )
bank - > pm_save [ type ] = readl ( reg + offs [ type ] ) ;
if ( widths [ PINCFG_TYPE_FUNC ] * bank - > nr_pins > 32 ) {
/* Some banks have two config registers */
bank - > pm_save [ PINCFG_TYPE_NUM ] =
readl ( reg + offs [ PINCFG_TYPE_FUNC ] + 4 ) ;
pr_debug ( " Save %s @ %p (con %#010x %08x) \n " ,
bank - > name , reg ,
bank - > pm_save [ PINCFG_TYPE_FUNC ] ,
bank - > pm_save [ PINCFG_TYPE_NUM ] ) ;
} else {
pr_debug ( " Save %s @ %p (con %#010x) \n " , bank - > name ,
reg , bank - > pm_save [ PINCFG_TYPE_FUNC ] ) ;
}
}
2013-05-17 20:24:30 +04:00
if ( ctrl - > suspend )
ctrl - > suspend ( drvdata ) ;
2013-05-17 08:33:18 +04:00
}
/**
* samsung_pinctrl_resume_dev - restore pinctrl state from suspend for a device
*
* Restore one of the banks that was saved during suspend .
*
* We don ' t bother doing anything complicated to avoid glitching lines since
* we ' re called before pad retention is turned off .
*/
static void samsung_pinctrl_resume_dev ( struct samsung_pinctrl_drv_data * drvdata )
{
struct samsung_pin_ctrl * ctrl = drvdata - > ctrl ;
void __iomem * virt_base = drvdata - > virt_base ;
int i ;
2013-05-17 20:24:30 +04:00
if ( ctrl - > resume )
ctrl - > resume ( drvdata ) ;
2013-05-17 08:33:18 +04:00
for ( i = 0 ; i < ctrl - > nr_banks ; i + + ) {
struct samsung_pin_bank * bank = & ctrl - > pin_banks [ i ] ;
void __iomem * reg = virt_base + bank - > pctl_offset ;
u8 * offs = bank - > type - > reg_offset ;
u8 * widths = bank - > type - > fld_width ;
enum pincfg_type type ;
/* Registers without a powerdown config aren't lost */
if ( ! widths [ PINCFG_TYPE_CON_PDN ] )
continue ;
if ( widths [ PINCFG_TYPE_FUNC ] * bank - > nr_pins > 32 ) {
/* Some banks have two config registers */
pr_debug ( " %s @ %p (con %#010x %08x => %#010x %08x) \n " ,
bank - > name , reg ,
readl ( reg + offs [ PINCFG_TYPE_FUNC ] ) ,
readl ( reg + offs [ PINCFG_TYPE_FUNC ] + 4 ) ,
bank - > pm_save [ PINCFG_TYPE_FUNC ] ,
bank - > pm_save [ PINCFG_TYPE_NUM ] ) ;
writel ( bank - > pm_save [ PINCFG_TYPE_NUM ] ,
reg + offs [ PINCFG_TYPE_FUNC ] + 4 ) ;
} else {
pr_debug ( " %s @ %p (con %#010x => %#010x) \n " , bank - > name ,
reg , readl ( reg + offs [ PINCFG_TYPE_FUNC ] ) ,
bank - > pm_save [ PINCFG_TYPE_FUNC ] ) ;
}
for ( type = 0 ; type < PINCFG_TYPE_NUM ; type + + )
if ( widths [ type ] )
writel ( bank - > pm_save [ type ] , reg + offs [ type ] ) ;
}
}
/**
* samsung_pinctrl_suspend - save pinctrl state for suspend
*
* Save data for all banks across all devices .
*/
static int samsung_pinctrl_suspend ( void )
{
struct samsung_pinctrl_drv_data * drvdata ;
list_for_each_entry ( drvdata , & drvdata_list , node ) {
samsung_pinctrl_suspend_dev ( drvdata ) ;
}
return 0 ;
}
/**
* samsung_pinctrl_resume - restore pinctrl state for suspend
*
* Restore data for all banks across all devices .
*/
static void samsung_pinctrl_resume ( void )
{
struct samsung_pinctrl_drv_data * drvdata ;
list_for_each_entry_reverse ( drvdata , & drvdata_list , node ) {
samsung_pinctrl_resume_dev ( drvdata ) ;
}
}
# else
# define samsung_pinctrl_suspend NULL
# define samsung_pinctrl_resume NULL
# endif
static struct syscore_ops samsung_pinctrl_syscore_ops = {
. suspend = samsung_pinctrl_suspend ,
. resume = samsung_pinctrl_resume ,
} ;
2012-09-07 01:07:19 +04:00
static const struct of_device_id samsung_pinctrl_dt_match [ ] = {
2013-03-19 01:31:51 +04:00
# ifdef CONFIG_PINCTRL_EXYNOS
2013-01-03 04:05:42 +04:00
{ . compatible = " samsung,exynos4210-pinctrl " ,
2012-09-07 01:07:19 +04:00
. data = ( void * ) exynos4210_pin_ctrl } ,
2013-01-03 04:05:42 +04:00
{ . compatible = " samsung,exynos4x12-pinctrl " ,
2012-11-07 03:44:59 +04:00
. data = ( void * ) exynos4x12_pin_ctrl } ,
2012-12-28 22:37:27 +04:00
{ . compatible = " samsung,exynos5250-pinctrl " ,
. data = ( void * ) exynos5250_pin_ctrl } ,
2013-03-19 01:31:55 +04:00
# endif
# ifdef CONFIG_PINCTRL_S3C64XX
{ . compatible = " samsung,s3c64xx-pinctrl " ,
. data = s3c64xx_pin_ctrl } ,
2013-05-20 19:56:13 +04:00
# endif
# ifdef CONFIG_PINCTRL_S3C24XX
{ . compatible = " samsung,s3c2412-pinctrl " ,
. data = s3c2412_pin_ctrl } ,
{ . compatible = " samsung,s3c2416-pinctrl " ,
. data = s3c2416_pin_ctrl } ,
{ . compatible = " samsung,s3c2440-pinctrl " ,
. data = s3c2440_pin_ctrl } ,
{ . compatible = " samsung,s3c2450-pinctrl " ,
. data = s3c2450_pin_ctrl } ,
2013-03-19 01:31:51 +04:00
# endif
2012-09-07 01:07:19 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , samsung_pinctrl_dt_match ) ;
static struct platform_driver samsung_pinctrl_driver = {
. probe = samsung_pinctrl_probe ,
. driver = {
. name = " samsung-pinctrl " ,
. owner = THIS_MODULE ,
. of_match_table = of_match_ptr ( samsung_pinctrl_dt_match ) ,
} ,
} ;
static int __init samsung_pinctrl_drv_register ( void )
{
2013-05-17 08:33:18 +04:00
/*
* Register syscore ops for save / restore of registers across suspend .
* It ' s important to ensure that this driver is running at an earlier
* initcall level than any arch - specific init calls that install syscore
* ops that turn off pad retention ( like exynos_pm_resume ) .
*/
register_syscore_ops ( & samsung_pinctrl_syscore_ops ) ;
2012-09-07 01:07:19 +04:00
return platform_driver_register ( & samsung_pinctrl_driver ) ;
}
postcore_initcall ( samsung_pinctrl_drv_register ) ;
static void __exit samsung_pinctrl_drv_unregister ( void )
{
platform_driver_unregister ( & samsung_pinctrl_driver ) ;
}
module_exit ( samsung_pinctrl_drv_unregister ) ;
MODULE_AUTHOR ( " Thomas Abraham <thomas.ab@samsung.com> " ) ;
MODULE_DESCRIPTION ( " Samsung pinctrl driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;