2012-03-28 20:57:07 +04:00
/*
* Driver for the ST Microelectronics SPEAr pinmux
*
* Copyright ( C ) 2012 ST Microelectronics
2015-07-18 02:23:50 +03:00
* Viresh Kumar < vireshk @ kernel . org >
2012-03-28 20:57:07 +04:00
*
* Inspired from :
* - U300 Pinctl drivers
* - Tegra Pinctl drivers
*
* 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/err.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_address.h>
2012-10-27 13:51:38 +04:00
# include <linux/of_gpio.h>
2012-03-28 20:57:07 +04:00
# include <linux/pinctrl/machine.h>
# include <linux/pinctrl/pinctrl.h>
# include <linux/pinctrl/pinmux.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include "pinctrl-spear.h"
# define DRIVER_NAME "spear-pinmux"
2012-10-27 13:51:38 +04:00
static void muxregs_endisable ( struct spear_pmx * pmx ,
struct spear_muxreg * muxregs , u8 count , bool enable )
{
struct spear_muxreg * muxreg ;
u32 val , temp , j ;
for ( j = 0 ; j < count ; j + + ) {
muxreg = & muxregs [ j ] ;
val = pmx_readl ( pmx , muxreg - > reg ) ;
val & = ~ muxreg - > mask ;
if ( enable )
temp = muxreg - > val ;
else
temp = ~ muxreg - > val ;
val | = muxreg - > mask & temp ;
pmx_writel ( pmx , val , muxreg - > reg ) ;
}
}
2012-03-28 20:57:07 +04:00
static int set_mode ( struct spear_pmx * pmx , int mode )
{
struct spear_pmx_mode * pmx_mode = NULL ;
int i ;
u32 val ;
if ( ! pmx - > machdata - > pmx_modes | | ! pmx - > machdata - > npmx_modes )
return - EINVAL ;
for ( i = 0 ; i < pmx - > machdata - > npmx_modes ; i + + ) {
if ( pmx - > machdata - > pmx_modes [ i ] - > mode = = ( 1 < < mode ) ) {
pmx_mode = pmx - > machdata - > pmx_modes [ i ] ;
break ;
}
}
if ( ! pmx_mode )
return - EINVAL ;
val = pmx_readl ( pmx , pmx_mode - > reg ) ;
val & = ~ pmx_mode - > mask ;
val | = pmx_mode - > val ;
pmx_writel ( pmx , val , pmx_mode - > reg ) ;
pmx - > machdata - > mode = pmx_mode - > mode ;
dev_info ( pmx - > dev , " Configured Mode: %s with id: %x \n \n " ,
pmx_mode - > name ? pmx_mode - > name : " no_name " ,
pmx_mode - > reg ) ;
return 0 ;
}
2012-12-22 01:10:23 +04:00
void pmx_init_gpio_pingroup_addr ( struct spear_gpio_pingroup * gpio_pingroup ,
unsigned count , u16 reg )
2012-10-27 13:51:38 +04:00
{
2012-11-13 11:31:41 +04:00
int i , j ;
2012-10-27 13:51:38 +04:00
2012-11-13 11:31:41 +04:00
for ( i = 0 ; i < count ; i + + )
for ( j = 0 ; j < gpio_pingroup [ i ] . nmuxregs ; j + + )
2012-10-27 13:51:38 +04:00
gpio_pingroup [ i ] . muxregs [ j ] . reg = reg ;
}
2012-12-22 01:10:23 +04:00
void pmx_init_addr ( struct spear_pinctrl_machdata * machdata , u16 reg )
2012-03-28 20:57:07 +04:00
{
struct spear_pingroup * pgroup ;
struct spear_modemux * modemux ;
int i , j , group ;
for ( group = 0 ; group < machdata - > ngroups ; group + + ) {
pgroup = machdata - > groups [ group ] ;
for ( i = 0 ; i < pgroup - > nmodemuxs ; i + + ) {
modemux = & pgroup - > modemuxs [ i ] ;
for ( j = 0 ; j < modemux - > nmuxregs ; j + + )
if ( modemux - > muxregs [ j ] . reg = = 0xFFFF )
modemux - > muxregs [ j ] . reg = reg ;
}
}
}
static int spear_pinctrl_get_groups_cnt ( struct pinctrl_dev * pctldev )
{
struct spear_pmx * pmx = pinctrl_dev_get_drvdata ( pctldev ) ;
return pmx - > machdata - > ngroups ;
}
static const char * spear_pinctrl_get_group_name ( struct pinctrl_dev * pctldev ,
unsigned group )
{
struct spear_pmx * pmx = pinctrl_dev_get_drvdata ( pctldev ) ;
return pmx - > machdata - > groups [ group ] - > name ;
}
static int spear_pinctrl_get_group_pins ( struct pinctrl_dev * pctldev ,
unsigned group , const unsigned * * pins , unsigned * num_pins )
{
struct spear_pmx * pmx = pinctrl_dev_get_drvdata ( pctldev ) ;
* pins = pmx - > machdata - > groups [ group ] - > pins ;
* num_pins = pmx - > machdata - > groups [ group ] - > npins ;
return 0 ;
}
static void spear_pinctrl_pin_dbg_show ( struct pinctrl_dev * pctldev ,
struct seq_file * s , unsigned offset )
{
seq_printf ( s , " " DRIVER_NAME ) ;
}
2012-11-11 06:29:40 +04:00
static int spear_pinctrl_dt_node_to_map ( struct pinctrl_dev * pctldev ,
struct device_node * np_config ,
struct pinctrl_map * * map ,
unsigned * num_maps )
2012-03-28 20:57:07 +04:00
{
struct spear_pmx * pmx = pinctrl_dev_get_drvdata ( pctldev ) ;
struct device_node * np ;
struct property * prop ;
const char * function , * group ;
int ret , index = 0 , count = 0 ;
/* calculate number of maps required */
for_each_child_of_node ( np_config , np ) {
ret = of_property_read_string ( np , " st,function " , & function ) ;
if ( ret < 0 )
return ret ;
ret = of_property_count_strings ( np , " st,pins " ) ;
if ( ret < 0 )
return ret ;
count + = ret ;
}
if ( ! count ) {
dev_err ( pmx - > dev , " No child nodes passed via DT \n " ) ;
return - ENODEV ;
}
* map = kzalloc ( sizeof ( * * map ) * count , GFP_KERNEL ) ;
if ( ! * map )
return - ENOMEM ;
for_each_child_of_node ( np_config , np ) {
of_property_read_string ( np , " st,function " , & function ) ;
of_property_for_each_string ( np , " st,pins " , prop , group ) {
( * map ) [ index ] . type = PIN_MAP_TYPE_MUX_GROUP ;
( * map ) [ index ] . data . mux . group = group ;
( * map ) [ index ] . data . mux . function = function ;
index + + ;
}
}
* num_maps = count ;
return 0 ;
}
2012-11-11 06:29:40 +04:00
static void spear_pinctrl_dt_free_map ( struct pinctrl_dev * pctldev ,
struct pinctrl_map * map ,
unsigned num_maps )
2012-03-28 20:57:07 +04:00
{
kfree ( map ) ;
}
2013-02-16 13:25:07 +04:00
static const struct pinctrl_ops spear_pinctrl_ops = {
2012-03-28 20:57:07 +04:00
. get_groups_count = spear_pinctrl_get_groups_cnt ,
. get_group_name = spear_pinctrl_get_group_name ,
. get_group_pins = spear_pinctrl_get_group_pins ,
. pin_dbg_show = spear_pinctrl_pin_dbg_show ,
. dt_node_to_map = spear_pinctrl_dt_node_to_map ,
. dt_free_map = spear_pinctrl_dt_free_map ,
} ;
static int spear_pinctrl_get_funcs_count ( struct pinctrl_dev * pctldev )
{
struct spear_pmx * pmx = pinctrl_dev_get_drvdata ( pctldev ) ;
return pmx - > machdata - > nfunctions ;
}
static const char * spear_pinctrl_get_func_name ( struct pinctrl_dev * pctldev ,
unsigned function )
{
struct spear_pmx * pmx = pinctrl_dev_get_drvdata ( pctldev ) ;
return pmx - > machdata - > functions [ function ] - > name ;
}
static int spear_pinctrl_get_func_groups ( struct pinctrl_dev * pctldev ,
unsigned function , const char * const * * groups ,
unsigned * const ngroups )
{
struct spear_pmx * pmx = pinctrl_dev_get_drvdata ( pctldev ) ;
* groups = pmx - > machdata - > functions [ function ] - > groups ;
* ngroups = pmx - > machdata - > functions [ function ] - > ngroups ;
return 0 ;
}
static int spear_pinctrl_endisable ( struct pinctrl_dev * pctldev ,
unsigned function , unsigned group , bool enable )
{
struct spear_pmx * pmx = pinctrl_dev_get_drvdata ( pctldev ) ;
const struct spear_pingroup * pgroup ;
const struct spear_modemux * modemux ;
2012-10-27 13:51:38 +04:00
int i ;
2012-03-28 20:57:07 +04:00
bool found = false ;
pgroup = pmx - > machdata - > groups [ group ] ;
for ( i = 0 ; i < pgroup - > nmodemuxs ; i + + ) {
modemux = & pgroup - > modemuxs [ i ] ;
/* SoC have any modes */
if ( pmx - > machdata - > modes_supported ) {
if ( ! ( pmx - > machdata - > mode & modemux - > modes ) )
continue ;
}
found = true ;
2012-10-27 13:51:38 +04:00
muxregs_endisable ( pmx , modemux - > muxregs , modemux - > nmuxregs ,
enable ) ;
2012-03-28 20:57:07 +04:00
}
if ( ! found ) {
dev_err ( pmx - > dev , " pinmux group: %s not supported \n " ,
pgroup - > name ) ;
return - ENODEV ;
}
return 0 ;
}
2014-09-03 15:02:56 +04:00
static int spear_pinctrl_set_mux ( struct pinctrl_dev * pctldev , unsigned function ,
2012-03-28 20:57:07 +04:00
unsigned group )
{
return spear_pinctrl_endisable ( pctldev , function , group , true ) ;
}
2012-10-27 13:51:38 +04:00
/* gpio with pinmux */
static struct spear_gpio_pingroup * get_gpio_pingroup ( struct spear_pmx * pmx ,
unsigned pin )
{
struct spear_gpio_pingroup * gpio_pingroup ;
2012-11-13 16:20:50 +04:00
int i , j ;
2012-10-27 13:51:38 +04:00
if ( ! pmx - > machdata - > gpio_pingroups )
return NULL ;
2012-11-13 16:20:50 +04:00
for ( i = 0 ; i < pmx - > machdata - > ngpio_pingroups ; i + + ) {
2012-10-27 13:51:38 +04:00
gpio_pingroup = & pmx - > machdata - > gpio_pingroups [ i ] ;
for ( j = 0 ; j < gpio_pingroup - > npins ; j + + ) {
if ( gpio_pingroup - > pins [ j ] = = pin )
return gpio_pingroup ;
}
}
2012-11-13 16:20:50 +04:00
return NULL ;
2012-10-27 13:51:38 +04:00
}
static int gpio_request_endisable ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range , unsigned offset , bool enable )
{
struct spear_pmx * pmx = pinctrl_dev_get_drvdata ( pctldev ) ;
2012-11-07 18:37:25 +04:00
struct spear_pinctrl_machdata * machdata = pmx - > machdata ;
2012-10-27 13:51:38 +04:00
struct spear_gpio_pingroup * gpio_pingroup ;
2012-11-07 18:37:25 +04:00
/*
* Some SoC have configuration options applicable to group of pins ,
* rather than a single pin .
*/
2012-10-27 13:51:38 +04:00
gpio_pingroup = get_gpio_pingroup ( pmx , offset ) ;
if ( gpio_pingroup )
muxregs_endisable ( pmx , gpio_pingroup - > muxregs ,
gpio_pingroup - > nmuxregs , enable ) ;
2012-11-07 18:37:25 +04:00
/*
* SoC may need some extra configurations , or configurations for single
* pin
*/
if ( machdata - > gpio_request_endisable )
machdata - > gpio_request_endisable ( pmx , offset , enable ) ;
2012-10-27 13:51:38 +04:00
return 0 ;
}
static int gpio_request_enable ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range , unsigned offset )
{
return gpio_request_endisable ( pctldev , range , offset , true ) ;
}
static void gpio_disable_free ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range , unsigned offset )
{
gpio_request_endisable ( pctldev , range , offset , false ) ;
}
2013-02-16 13:25:07 +04:00
static const struct pinmux_ops spear_pinmux_ops = {
2012-03-28 20:57:07 +04:00
. get_functions_count = spear_pinctrl_get_funcs_count ,
. get_function_name = spear_pinctrl_get_func_name ,
. get_function_groups = spear_pinctrl_get_func_groups ,
2014-09-03 15:02:56 +04:00
. set_mux = spear_pinctrl_set_mux ,
2012-10-27 13:51:38 +04:00
. gpio_request_enable = gpio_request_enable ,
. gpio_disable_free = gpio_disable_free ,
2012-03-28 20:57:07 +04:00
} ;
static struct pinctrl_desc spear_pinctrl_desc = {
. name = DRIVER_NAME ,
. pctlops = & spear_pinctrl_ops ,
. pmxops = & spear_pinmux_ops ,
. owner = THIS_MODULE ,
} ;
2012-12-22 01:10:23 +04:00
int spear_pinctrl_probe ( struct platform_device * pdev ,
struct spear_pinctrl_machdata * machdata )
2012-03-28 20:57:07 +04:00
{
struct device_node * np = pdev - > dev . of_node ;
struct resource * res ;
struct spear_pmx * pmx ;
if ( ! machdata )
return - ENODEV ;
pmx = devm_kzalloc ( & pdev - > dev , sizeof ( * pmx ) , GFP_KERNEL ) ;
if ( ! pmx ) {
dev_err ( & pdev - > dev , " Can't alloc spear_pmx \n " ) ;
return - ENOMEM ;
}
2013-08-26 04:20:45 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
pmx - > vbase = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( pmx - > vbase ) )
return PTR_ERR ( pmx - > vbase ) ;
2012-03-28 20:57:07 +04:00
pmx - > dev = & pdev - > dev ;
pmx - > machdata = machdata ;
/* configure mode, if supported by SoC */
if ( machdata - > modes_supported ) {
int mode = 0 ;
if ( of_property_read_u32 ( np , " st,pinmux-mode " , & mode ) ) {
dev_err ( & pdev - > dev , " OF: pinmux mode not passed \n " ) ;
return - EINVAL ;
}
if ( set_mode ( pmx , mode ) ) {
dev_err ( & pdev - > dev , " OF: Couldn't configure mode: %x \n " ,
mode ) ;
return - EINVAL ;
}
}
platform_set_drvdata ( pdev , pmx ) ;
spear_pinctrl_desc . pins = machdata - > pins ;
spear_pinctrl_desc . npins = machdata - > npins ;
pmx - > pctl = pinctrl_register ( & spear_pinctrl_desc , & pdev - > dev , pmx ) ;
2015-06-09 07:01:16 +03:00
if ( IS_ERR ( pmx - > pctl ) ) {
2012-03-28 20:57:07 +04:00
dev_err ( & pdev - > dev , " Couldn't register pinctrl driver \n " ) ;
2015-06-09 07:01:16 +03:00
return PTR_ERR ( pmx - > pctl ) ;
2012-03-28 20:57:07 +04:00
}
return 0 ;
}
2012-11-19 22:26:06 +04:00
int spear_pinctrl_remove ( struct platform_device * pdev )
2012-03-28 20:57:07 +04:00
{
struct spear_pmx * pmx = platform_get_drvdata ( pdev ) ;
pinctrl_unregister ( pmx - > pctl ) ;
return 0 ;
}