2014-05-19 21:36:29 +04:00
/*
* Marvell Berlin SoC pinctrl core driver
*
* Copyright ( C ) 2014 Marvell Technology Group Ltd .
*
* Antoine TÃ © nart < antoine . tenart @ free - electrons . com >
*
* 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/io.h>
2015-05-16 02:16:09 +03:00
# include <linux/mfd/syscon.h>
2014-05-19 21:36:29 +04:00
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_device.h>
# include <linux/pinctrl/pinctrl.h>
# include <linux/pinctrl/pinmux.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# include "../core.h"
# include "../pinctrl-utils.h"
# include "berlin.h"
struct berlin_pinctrl {
struct regmap * regmap ;
struct device * dev ;
const struct berlin_pinctrl_desc * desc ;
struct berlin_pinctrl_function * functions ;
unsigned nfunctions ;
struct pinctrl_dev * pctrl_dev ;
} ;
static int berlin_pinctrl_get_group_count ( struct pinctrl_dev * pctrl_dev )
{
struct berlin_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctrl_dev ) ;
return pctrl - > desc - > ngroups ;
}
static const char * berlin_pinctrl_get_group_name ( struct pinctrl_dev * pctrl_dev ,
unsigned group )
{
struct berlin_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctrl_dev ) ;
return pctrl - > desc - > groups [ group ] . name ;
}
static int berlin_pinctrl_dt_node_to_map ( struct pinctrl_dev * pctrl_dev ,
struct device_node * node ,
struct pinctrl_map * * map ,
unsigned * num_maps )
{
struct berlin_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctrl_dev ) ;
struct property * prop ;
const char * function_name , * group_name ;
unsigned reserved_maps = 0 ;
int ret , ngroups ;
* map = NULL ;
* num_maps = 0 ;
ret = of_property_read_string ( node , " function " , & function_name ) ;
if ( ret ) {
dev_err ( pctrl - > dev ,
" missing function property in node %s \n " ,
node - > name ) ;
return - EINVAL ;
}
ngroups = of_property_count_strings ( node , " groups " ) ;
if ( ngroups < 0 ) {
dev_err ( pctrl - > dev ,
" missing groups property in node %s \n " ,
node - > name ) ;
return - EINVAL ;
}
ret = pinctrl_utils_reserve_map ( pctrl_dev , map , & reserved_maps ,
num_maps , ngroups ) ;
if ( ret ) {
dev_err ( pctrl - > dev , " can't reserve map: %d \n " , ret ) ;
return ret ;
}
of_property_for_each_string ( node , " groups " , prop , group_name ) {
ret = pinctrl_utils_add_map_mux ( pctrl_dev , map , & reserved_maps ,
num_maps , group_name ,
function_name ) ;
if ( ret ) {
dev_err ( pctrl - > dev , " can't add map: %d \n " , ret ) ;
return ret ;
}
}
return 0 ;
}
static const struct pinctrl_ops berlin_pinctrl_ops = {
. get_groups_count = & berlin_pinctrl_get_group_count ,
. get_group_name = & berlin_pinctrl_get_group_name ,
. dt_node_to_map = & berlin_pinctrl_dt_node_to_map ,
2014-09-10 13:15:51 +04:00
. dt_free_map = & pinctrl_utils_dt_free_map ,
2014-05-19 21:36:29 +04:00
} ;
static int berlin_pinmux_get_functions_count ( struct pinctrl_dev * pctrl_dev )
{
struct berlin_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctrl_dev ) ;
return pctrl - > nfunctions ;
}
static const char * berlin_pinmux_get_function_name ( struct pinctrl_dev * pctrl_dev ,
unsigned function )
{
struct berlin_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctrl_dev ) ;
return pctrl - > functions [ function ] . name ;
}
static int berlin_pinmux_get_function_groups ( struct pinctrl_dev * pctrl_dev ,
unsigned function ,
const char * const * * groups ,
unsigned * const num_groups )
{
struct berlin_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctrl_dev ) ;
* groups = pctrl - > functions [ function ] . groups ;
* num_groups = pctrl - > functions [ function ] . ngroups ;
return 0 ;
}
static struct berlin_desc_function *
berlin_pinctrl_find_function_by_name ( struct berlin_pinctrl * pctrl ,
const struct berlin_desc_group * group ,
const char * fname )
{
struct berlin_desc_function * function = group - > functions ;
while ( function - > name ) {
if ( ! strcmp ( function - > name , fname ) )
return function ;
function + + ;
}
return NULL ;
}
2014-09-03 15:02:56 +04:00
static int berlin_pinmux_set ( struct pinctrl_dev * pctrl_dev ,
unsigned function ,
unsigned group )
2014-05-19 21:36:29 +04:00
{
struct berlin_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctrl_dev ) ;
const struct berlin_desc_group * group_desc = pctrl - > desc - > groups + group ;
struct berlin_pinctrl_function * func = pctrl - > functions + function ;
struct berlin_desc_function * function_desc =
berlin_pinctrl_find_function_by_name ( pctrl , group_desc ,
func - > name ) ;
u32 mask , val ;
if ( ! function_desc )
return - EINVAL ;
mask = GENMASK ( group_desc - > lsb + group_desc - > bit_width - 1 ,
group_desc - > lsb ) ;
val = function_desc - > muxval < < group_desc - > lsb ;
regmap_update_bits ( pctrl - > regmap , group_desc - > offset , mask , val ) ;
return 0 ;
}
static const struct pinmux_ops berlin_pinmux_ops = {
. get_functions_count = & berlin_pinmux_get_functions_count ,
. get_function_name = & berlin_pinmux_get_function_name ,
. get_function_groups = & berlin_pinmux_get_function_groups ,
2014-09-03 15:02:56 +04:00
. set_mux = & berlin_pinmux_set ,
2014-05-19 21:36:29 +04:00
} ;
static int berlin_pinctrl_add_function ( struct berlin_pinctrl * pctrl ,
const char * name )
{
struct berlin_pinctrl_function * function = pctrl - > functions ;
while ( function - > name ) {
if ( ! strcmp ( function - > name , name ) ) {
function - > ngroups + + ;
return - EEXIST ;
}
function + + ;
}
function - > name = name ;
function - > ngroups = 1 ;
pctrl - > nfunctions + + ;
return 0 ;
}
static int berlin_pinctrl_build_state ( struct platform_device * pdev )
{
struct berlin_pinctrl * pctrl = platform_get_drvdata ( pdev ) ;
struct berlin_desc_group const * desc_group ;
struct berlin_desc_function const * desc_function ;
int i , max_functions = 0 ;
pctrl - > nfunctions = 0 ;
for ( i = 0 ; i < pctrl - > desc - > ngroups ; i + + ) {
desc_group = pctrl - > desc - > groups + i ;
/* compute the maxiumum number of functions a group can have */
max_functions + = 1 < < ( desc_group - > bit_width + 1 ) ;
}
/* we will reallocate later */
pctrl - > functions = devm_kzalloc ( & pdev - > dev ,
max_functions * sizeof ( * pctrl - > functions ) ,
GFP_KERNEL ) ;
if ( ! pctrl - > functions )
return - ENOMEM ;
/* register all functions */
for ( i = 0 ; i < pctrl - > desc - > ngroups ; i + + ) {
desc_group = pctrl - > desc - > groups + i ;
desc_function = desc_group - > functions ;
while ( desc_function - > name ) {
berlin_pinctrl_add_function ( pctrl , desc_function - > name ) ;
desc_function + + ;
}
}
pctrl - > functions = krealloc ( pctrl - > functions ,
pctrl - > nfunctions * sizeof ( * pctrl - > functions ) ,
GFP_KERNEL ) ;
/* map functions to theirs groups */
for ( i = 0 ; i < pctrl - > desc - > ngroups ; i + + ) {
desc_group = pctrl - > desc - > groups + i ;
desc_function = desc_group - > functions ;
while ( desc_function - > name ) {
struct berlin_pinctrl_function
* function = pctrl - > functions ;
const char * * groups ;
bool found = false ;
while ( function - > name ) {
if ( ! strcmp ( desc_function - > name , function - > name ) ) {
found = true ;
break ;
}
function + + ;
}
if ( ! found )
return - EINVAL ;
if ( ! function - > groups ) {
function - > groups =
devm_kzalloc ( & pdev - > dev ,
function - > ngroups * sizeof ( char * ) ,
GFP_KERNEL ) ;
if ( ! function - > groups )
return - ENOMEM ;
}
groups = function - > groups ;
while ( * groups )
groups + + ;
* groups = desc_group - > name ;
desc_function + + ;
}
}
return 0 ;
}
static struct pinctrl_desc berlin_pctrl_desc = {
. name = " berlin-pinctrl " ,
. pctlops = & berlin_pinctrl_ops ,
. pmxops = & berlin_pinmux_ops ,
. owner = THIS_MODULE ,
} ;
int berlin_pinctrl_probe ( struct platform_device * pdev ,
const struct berlin_pinctrl_desc * desc )
{
struct device * dev = & pdev - > dev ;
2015-05-16 02:16:09 +03:00
struct device_node * parent_np = of_get_parent ( dev - > of_node ) ;
2014-05-19 21:36:29 +04:00
struct berlin_pinctrl * pctrl ;
struct regmap * regmap ;
int ret ;
2015-05-16 02:21:39 +03:00
regmap = syscon_node_to_regmap ( parent_np ) ;
2015-05-16 02:16:09 +03:00
of_node_put ( parent_np ) ;
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
2014-05-19 21:36:29 +04:00
pctrl = devm_kzalloc ( dev , sizeof ( * pctrl ) , GFP_KERNEL ) ;
if ( ! pctrl )
return - ENOMEM ;
platform_set_drvdata ( pdev , pctrl ) ;
pctrl - > regmap = regmap ;
pctrl - > dev = & pdev - > dev ;
pctrl - > desc = desc ;
ret = berlin_pinctrl_build_state ( pdev ) ;
if ( ret ) {
dev_err ( dev , " cannot build driver state: %d \n " , ret ) ;
return ret ;
}
pctrl - > pctrl_dev = pinctrl_register ( & berlin_pctrl_desc , dev , pctrl ) ;
2015-06-09 07:01:16 +03:00
if ( IS_ERR ( pctrl - > pctrl_dev ) ) {
2014-05-19 21:36:29 +04:00
dev_err ( dev , " failed to register pinctrl driver \n " ) ;
2015-06-09 07:01:16 +03:00
return PTR_ERR ( pctrl - > pctrl_dev ) ;
2014-05-19 21:36:29 +04:00
}
return 0 ;
}