@ -13,6 +13,7 @@
* GNU General Public License for more details .
*/
# include <linux/list.h>
# include <linux/mfd/syscon.h>
# include <linux/of.h>
# include <linux/pinctrl/pinconf.h>
@ -34,11 +35,19 @@
# define UNIPHIER_PINCTRL_PUPDCTRL_BASE 0x1a00
# define UNIPHIER_PINCTRL_IECTRL_BASE 0x1d00
struct uniphier_pinctrl_reg_region {
struct list_head node ;
unsigned int base ;
unsigned int nregs ;
u32 vals [ 0 ] ;
} ;
struct uniphier_pinctrl_priv {
struct pinctrl_desc pctldesc ;
struct pinctrl_dev * pctldev ;
struct regmap * regmap ;
struct uniphier_pinctrl_socdata * socdata ;
struct list_head reg_regions ;
} ;
static int uniphier_pctl_get_groups_count ( struct pinctrl_dev * pctldev )
@ -688,12 +697,177 @@ static const struct pinmux_ops uniphier_pmxops = {
. strict = true ,
} ;
# ifdef CONFIG_PM_SLEEP
static int uniphier_pinctrl_suspend ( struct device * dev )
{
struct uniphier_pinctrl_priv * priv = dev_get_drvdata ( dev ) ;
struct uniphier_pinctrl_reg_region * r ;
int ret ;
list_for_each_entry ( r , & priv - > reg_regions , node ) {
ret = regmap_bulk_read ( priv - > regmap , r - > base , r - > vals ,
r - > nregs ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static int uniphier_pinctrl_resume ( struct device * dev )
{
struct uniphier_pinctrl_priv * priv = dev_get_drvdata ( dev ) ;
struct uniphier_pinctrl_reg_region * r ;
int ret ;
list_for_each_entry ( r , & priv - > reg_regions , node ) {
ret = regmap_bulk_write ( priv - > regmap , r - > base , r - > vals ,
r - > nregs ) ;
if ( ret )
return ret ;
}
if ( priv - > socdata - > caps & UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE ) {
ret = regmap_write ( priv - > regmap ,
UNIPHIER_PINCTRL_LOAD_PINMUX , 1 ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static int uniphier_pinctrl_add_reg_region ( struct device * dev ,
struct uniphier_pinctrl_priv * priv ,
unsigned int base ,
unsigned int count ,
unsigned int width )
{
struct uniphier_pinctrl_reg_region * region ;
unsigned int nregs ;
if ( ! count )
return 0 ;
nregs = DIV_ROUND_UP ( count * width , 32 ) ;
region = devm_kzalloc ( dev ,
sizeof ( * region ) + sizeof ( region - > vals [ 0 ] ) * nregs ,
GFP_KERNEL ) ;
if ( ! region )
return - ENOMEM ;
region - > base = base ;
region - > nregs = nregs ;
list_add_tail ( & region - > node , & priv - > reg_regions ) ;
return 0 ;
}
# endif
static int uniphier_pinctrl_pm_init ( struct device * dev ,
struct uniphier_pinctrl_priv * priv )
{
# ifdef CONFIG_PM_SLEEP
const struct uniphier_pinctrl_socdata * socdata = priv - > socdata ;
unsigned int num_drvctrl = 0 ;
unsigned int num_drv2ctrl = 0 ;
unsigned int num_drv3ctrl = 0 ;
unsigned int num_pupdctrl = 0 ;
unsigned int num_iectrl = 0 ;
unsigned int iectrl , drvctrl , pupdctrl ;
enum uniphier_pin_drv_type drv_type ;
enum uniphier_pin_pull_dir pull_dir ;
int i , ret ;
for ( i = 0 ; i < socdata - > npins ; i + + ) {
void * drv_data = socdata - > pins [ i ] . drv_data ;
drvctrl = uniphier_pin_get_drvctrl ( drv_data ) ;
drv_type = uniphier_pin_get_drv_type ( drv_data ) ;
pupdctrl = uniphier_pin_get_pupdctrl ( drv_data ) ;
pull_dir = uniphier_pin_get_pull_dir ( drv_data ) ;
iectrl = uniphier_pin_get_iectrl ( drv_data ) ;
switch ( drv_type ) {
case UNIPHIER_PIN_DRV_1BIT :
num_drvctrl = max ( num_drvctrl , drvctrl + 1 ) ;
break ;
case UNIPHIER_PIN_DRV_2BIT :
num_drv2ctrl = max ( num_drv2ctrl , drvctrl + 1 ) ;
break ;
case UNIPHIER_PIN_DRV_3BIT :
num_drv3ctrl = max ( num_drv3ctrl , drvctrl + 1 ) ;
break ;
default :
break ;
}
if ( pull_dir = = UNIPHIER_PIN_PULL_UP | |
pull_dir = = UNIPHIER_PIN_PULL_DOWN )
num_pupdctrl = max ( num_pupdctrl , pupdctrl + 1 ) ;
if ( iectrl ! = UNIPHIER_PIN_IECTRL_NONE ) {
if ( socdata - > caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL )
iectrl = i ;
num_iectrl = max ( num_iectrl , iectrl + 1 ) ;
}
}
INIT_LIST_HEAD ( & priv - > reg_regions ) ;
ret = uniphier_pinctrl_add_reg_region ( dev , priv ,
UNIPHIER_PINCTRL_PINMUX_BASE ,
socdata - > npins , 8 ) ;
if ( ret )
return ret ;
ret = uniphier_pinctrl_add_reg_region ( dev , priv ,
UNIPHIER_PINCTRL_DRVCTRL_BASE ,
num_drvctrl , 1 ) ;
if ( ret )
return ret ;
ret = uniphier_pinctrl_add_reg_region ( dev , priv ,
UNIPHIER_PINCTRL_DRV2CTRL_BASE ,
num_drv2ctrl , 2 ) ;
if ( ret )
return ret ;
ret = uniphier_pinctrl_add_reg_region ( dev , priv ,
UNIPHIER_PINCTRL_DRV3CTRL_BASE ,
num_drv3ctrl , 3 ) ;
if ( ret )
return ret ;
ret = uniphier_pinctrl_add_reg_region ( dev , priv ,
UNIPHIER_PINCTRL_PUPDCTRL_BASE ,
num_pupdctrl , 1 ) ;
if ( ret )
return ret ;
ret = uniphier_pinctrl_add_reg_region ( dev , priv ,
UNIPHIER_PINCTRL_IECTRL_BASE ,
num_iectrl , 1 ) ;
if ( ret )
return ret ;
# endif
return 0 ;
}
const struct dev_pm_ops uniphier_pinctrl_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS ( uniphier_pinctrl_suspend ,
uniphier_pinctrl_resume )
} ;
int uniphier_pinctrl_probe ( struct platform_device * pdev ,
struct uniphier_pinctrl_socdata * socdata )
{
struct device * dev = & pdev - > dev ;
struct uniphier_pinctrl_priv * priv ;
struct device_node * parent ;
int ret ;
if ( ! socdata | |
! socdata - > pins | | ! socdata - > npins | |
@ -725,6 +899,10 @@ int uniphier_pinctrl_probe(struct platform_device *pdev,
priv - > pctldesc . confops = & uniphier_confops ;
priv - > pctldesc . owner = dev - > driver - > owner ;
ret = uniphier_pinctrl_pm_init ( dev , priv ) ;
if ( ret )
return ret ;
priv - > pctldev = devm_pinctrl_register ( dev , & priv - > pctldesc , priv ) ;
if ( IS_ERR ( priv - > pctldev ) ) {
dev_err ( dev , " failed to register UniPhier pinctrl driver \n " ) ;