2021-04-22 11:30:02 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* ZynqMP pin controller
*
2021-06-21 14:00:14 +03:00
* Copyright ( C ) 2020 , 2021 Xilinx , Inc .
2021-04-22 11:30:02 +03:00
*
* Sai Krishna Potthuri < lakshmi . sai . krishna . potthuri @ xilinx . com >
* Rajan Vaja < rajan . vaja @ xilinx . com >
*/
# include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/of_address.h>
# include <linux/platform_device.h>
2022-10-07 16:44:44 +03:00
2021-04-22 11:30:02 +03:00
# include <linux/firmware/xlnx-zynqmp.h>
# include <linux/pinctrl/pinconf-generic.h>
2022-10-07 16:44:44 +03:00
# include <linux/pinctrl/pinconf.h>
# include <linux/pinctrl/pinctrl.h>
# include <linux/pinctrl/pinmux.h>
2021-04-22 11:30:02 +03:00
# include "core.h"
# include "pinctrl-utils.h"
# define ZYNQMP_PIN_PREFIX "MIO"
# define PINCTRL_GET_FUNC_NAME_RESP_LEN 16
# define MAX_FUNC_NAME_LEN 16
# define MAX_GROUP_PIN 50
# define MAX_PIN_GROUPS 50
# define END_OF_FUNCTIONS "END_OF_FUNCTIONS"
# define NUM_GROUPS_PER_RESP 6
# define PINCTRL_GET_FUNC_GROUPS_RESP_LEN 12
# define PINCTRL_GET_PIN_GROUPS_RESP_LEN 12
# define NA_GROUP 0xFFFF
# define RESERVED_GROUP 0xFFFE
# define DRIVE_STRENGTH_2MA 2
# define DRIVE_STRENGTH_4MA 4
# define DRIVE_STRENGTH_8MA 8
# define DRIVE_STRENGTH_12MA 12
/**
* struct zynqmp_pmux_function - a pinmux function
* @ name : Name of the pin mux function
* @ groups : List of pin groups for this function
* @ ngroups : Number of entries in @ groups
* @ node : Firmware node matching with the function
*
* This structure holds information about pin control function
* and function group names supporting that function .
*/
struct zynqmp_pmux_function {
char name [ MAX_FUNC_NAME_LEN ] ;
const char * const * groups ;
unsigned int ngroups ;
} ;
/**
* struct zynqmp_pinctrl - driver data
* @ pctrl : Pin control device
* @ groups : Pin groups
* @ ngroups : Number of @ groups
* @ funcs : Pin mux functions
* @ nfuncs : Number of @ funcs
*
* This struct is stored as driver data and used to retrieve
* information regarding pin control functions , groups and
* group pins .
*/
struct zynqmp_pinctrl {
struct pinctrl_dev * pctrl ;
const struct zynqmp_pctrl_group * groups ;
unsigned int ngroups ;
const struct zynqmp_pmux_function * funcs ;
unsigned int nfuncs ;
} ;
/**
* struct zynqmp_pctrl_group - Pin control group info
* @ name : Group name
* @ pins : Group pin numbers
* @ npins : Number of pins in the group
*/
struct zynqmp_pctrl_group {
const char * name ;
unsigned int pins [ MAX_GROUP_PIN ] ;
unsigned int npins ;
} ;
static struct pinctrl_desc zynqmp_desc ;
static int zynqmp_pctrl_get_groups_count ( struct pinctrl_dev * pctldev )
{
struct zynqmp_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctrl - > ngroups ;
}
static const char * zynqmp_pctrl_get_group_name ( struct pinctrl_dev * pctldev ,
unsigned int selector )
{
struct zynqmp_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctrl - > groups [ selector ] . name ;
}
static int zynqmp_pctrl_get_group_pins ( struct pinctrl_dev * pctldev ,
unsigned int selector ,
const unsigned int * * pins ,
unsigned int * npins )
{
struct zynqmp_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
* pins = pctrl - > groups [ selector ] . pins ;
* npins = pctrl - > groups [ selector ] . npins ;
return 0 ;
}
static const struct pinctrl_ops zynqmp_pctrl_ops = {
. get_groups_count = zynqmp_pctrl_get_groups_count ,
. get_group_name = zynqmp_pctrl_get_group_name ,
. get_group_pins = zynqmp_pctrl_get_group_pins ,
. dt_node_to_map = pinconf_generic_dt_node_to_map_all ,
. dt_free_map = pinctrl_utils_free_map ,
} ;
static int zynqmp_pinmux_request_pin ( struct pinctrl_dev * pctldev ,
unsigned int pin )
{
int ret ;
ret = zynqmp_pm_pinctrl_request ( pin ) ;
if ( ret ) {
dev_err ( pctldev - > dev , " request failed for pin %u \n " , pin ) ;
return ret ;
}
return 0 ;
}
static int zynqmp_pmux_get_functions_count ( struct pinctrl_dev * pctldev )
{
struct zynqmp_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctrl - > nfuncs ;
}
static const char * zynqmp_pmux_get_function_name ( struct pinctrl_dev * pctldev ,
unsigned int selector )
{
struct zynqmp_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctrl - > funcs [ selector ] . name ;
}
/**
* zynqmp_pmux_get_function_groups ( ) - Get groups for the function
* @ pctldev : Pincontrol device pointer .
* @ selector : Function ID
* @ groups : Group names .
* @ num_groups : Number of function groups .
*
* Get function ' s group count and group names .
2022-06-17 13:46:59 +03:00
*
* Return : 0
2021-04-22 11:30:02 +03:00
*/
static int zynqmp_pmux_get_function_groups ( struct pinctrl_dev * pctldev ,
unsigned int selector ,
const char * const * * groups ,
unsigned * const num_groups )
{
struct zynqmp_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
* groups = pctrl - > funcs [ selector ] . groups ;
* num_groups = pctrl - > funcs [ selector ] . ngroups ;
return 0 ;
}
/**
* zynqmp_pinmux_set_mux ( ) - Set requested function for the group
* @ pctldev : Pincontrol device pointer .
* @ function : Function ID .
* @ group : Group ID .
*
* Loop through all pins of the group and call firmware API
* to set requested function for all pins in the group .
*
* Return : 0 on success else error code .
*/
static int zynqmp_pinmux_set_mux ( struct pinctrl_dev * pctldev ,
unsigned int function ,
unsigned int group )
{
struct zynqmp_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
const struct zynqmp_pctrl_group * pgrp = & pctrl - > groups [ group ] ;
int ret , i ;
for ( i = 0 ; i < pgrp - > npins ; i + + ) {
unsigned int pin = pgrp - > pins [ i ] ;
ret = zynqmp_pm_pinctrl_set_function ( pin , function ) ;
if ( ret ) {
dev_err ( pctldev - > dev , " set mux failed for pin %u \n " ,
pin ) ;
return ret ;
}
}
return 0 ;
}
static int zynqmp_pinmux_release_pin ( struct pinctrl_dev * pctldev ,
unsigned int pin )
{
int ret ;
ret = zynqmp_pm_pinctrl_release ( pin ) ;
if ( ret ) {
dev_err ( pctldev - > dev , " free pin failed for pin %u \n " ,
pin ) ;
return ret ;
}
return 0 ;
}
static const struct pinmux_ops zynqmp_pinmux_ops = {
. request = zynqmp_pinmux_request_pin ,
. get_functions_count = zynqmp_pmux_get_functions_count ,
. get_function_name = zynqmp_pmux_get_function_name ,
. get_function_groups = zynqmp_pmux_get_function_groups ,
. set_mux = zynqmp_pinmux_set_mux ,
. free = zynqmp_pinmux_release_pin ,
} ;
/**
* zynqmp_pinconf_cfg_get ( ) - get config value for the pin
* @ pctldev : Pin control device pointer .
* @ pin : Pin number .
* @ config : Value of config param .
*
* Get value of the requested configuration parameter for the
* given pin .
*
* Return : 0 on success else error code .
*/
static int zynqmp_pinconf_cfg_get ( struct pinctrl_dev * pctldev ,
unsigned int pin ,
unsigned long * config )
{
unsigned int arg , param = pinconf_to_config_param ( * config ) ;
int ret ;
switch ( param ) {
case PIN_CONFIG_SLEW_RATE :
param = PM_PINCTRL_CONFIG_SLEW_RATE ;
ret = zynqmp_pm_pinctrl_get_config ( pin , param , & arg ) ;
break ;
case PIN_CONFIG_BIAS_PULL_UP :
param = PM_PINCTRL_CONFIG_PULL_CTRL ;
ret = zynqmp_pm_pinctrl_get_config ( pin , param , & arg ) ;
if ( arg ! = PM_PINCTRL_BIAS_PULL_UP )
return - EINVAL ;
arg = 1 ;
break ;
case PIN_CONFIG_BIAS_PULL_DOWN :
param = PM_PINCTRL_CONFIG_PULL_CTRL ;
ret = zynqmp_pm_pinctrl_get_config ( pin , param , & arg ) ;
if ( arg ! = PM_PINCTRL_BIAS_PULL_DOWN )
return - EINVAL ;
arg = 1 ;
break ;
case PIN_CONFIG_BIAS_DISABLE :
param = PM_PINCTRL_CONFIG_BIAS_STATUS ;
ret = zynqmp_pm_pinctrl_get_config ( pin , param , & arg ) ;
if ( arg ! = PM_PINCTRL_BIAS_DISABLE )
return - EINVAL ;
arg = 1 ;
break ;
case PIN_CONFIG_POWER_SOURCE :
param = PM_PINCTRL_CONFIG_VOLTAGE_STATUS ;
ret = zynqmp_pm_pinctrl_get_config ( pin , param , & arg ) ;
break ;
case PIN_CONFIG_INPUT_SCHMITT_ENABLE :
param = PM_PINCTRL_CONFIG_SCHMITT_CMOS ;
ret = zynqmp_pm_pinctrl_get_config ( pin , param , & arg ) ;
break ;
case PIN_CONFIG_DRIVE_STRENGTH :
param = PM_PINCTRL_CONFIG_DRIVE_STRENGTH ;
ret = zynqmp_pm_pinctrl_get_config ( pin , param , & arg ) ;
switch ( arg ) {
case PM_PINCTRL_DRIVE_STRENGTH_2MA :
arg = DRIVE_STRENGTH_2MA ;
break ;
case PM_PINCTRL_DRIVE_STRENGTH_4MA :
arg = DRIVE_STRENGTH_4MA ;
break ;
case PM_PINCTRL_DRIVE_STRENGTH_8MA :
arg = DRIVE_STRENGTH_8MA ;
break ;
case PM_PINCTRL_DRIVE_STRENGTH_12MA :
arg = DRIVE_STRENGTH_12MA ;
break ;
default :
/* Invalid drive strength */
dev_warn ( pctldev - > dev ,
" Invalid drive strength for pin %d \n " ,
pin ) ;
return - EINVAL ;
}
break ;
default :
2021-06-21 14:00:14 +03:00
ret = - ENOTSUPP ;
2021-04-22 11:30:02 +03:00
break ;
}
if ( ret )
return ret ;
param = pinconf_to_config_param ( * config ) ;
* config = pinconf_to_config_packed ( param , arg ) ;
return 0 ;
}
/**
* zynqmp_pinconf_cfg_set ( ) - Set requested config for the pin
* @ pctldev : Pincontrol device pointer .
* @ pin : Pin number .
* @ configs : Configuration to set .
* @ num_configs : Number of configurations .
*
* Loop through all configurations and call firmware API
* to set requested configurations for the pin .
*
* Return : 0 on success else error code .
*/
static int zynqmp_pinconf_cfg_set ( struct pinctrl_dev * pctldev ,
unsigned int pin , unsigned long * configs ,
unsigned int num_configs )
{
int i , ret ;
for ( i = 0 ; i < num_configs ; i + + ) {
unsigned int param = pinconf_to_config_param ( configs [ i ] ) ;
unsigned int arg = pinconf_to_config_argument ( configs [ i ] ) ;
unsigned int value ;
switch ( param ) {
case PIN_CONFIG_SLEW_RATE :
param = PM_PINCTRL_CONFIG_SLEW_RATE ;
ret = zynqmp_pm_pinctrl_set_config ( pin , param , arg ) ;
break ;
case PIN_CONFIG_BIAS_PULL_UP :
param = PM_PINCTRL_CONFIG_PULL_CTRL ;
arg = PM_PINCTRL_BIAS_PULL_UP ;
ret = zynqmp_pm_pinctrl_set_config ( pin , param , arg ) ;
break ;
case PIN_CONFIG_BIAS_PULL_DOWN :
param = PM_PINCTRL_CONFIG_PULL_CTRL ;
arg = PM_PINCTRL_BIAS_PULL_DOWN ;
ret = zynqmp_pm_pinctrl_set_config ( pin , param , arg ) ;
break ;
case PIN_CONFIG_BIAS_DISABLE :
param = PM_PINCTRL_CONFIG_BIAS_STATUS ;
arg = PM_PINCTRL_BIAS_DISABLE ;
ret = zynqmp_pm_pinctrl_set_config ( pin , param , arg ) ;
break ;
case PIN_CONFIG_INPUT_SCHMITT_ENABLE :
param = PM_PINCTRL_CONFIG_SCHMITT_CMOS ;
ret = zynqmp_pm_pinctrl_set_config ( pin , param , arg ) ;
break ;
case PIN_CONFIG_DRIVE_STRENGTH :
switch ( arg ) {
case DRIVE_STRENGTH_2MA :
value = PM_PINCTRL_DRIVE_STRENGTH_2MA ;
break ;
case DRIVE_STRENGTH_4MA :
value = PM_PINCTRL_DRIVE_STRENGTH_4MA ;
break ;
case DRIVE_STRENGTH_8MA :
value = PM_PINCTRL_DRIVE_STRENGTH_8MA ;
break ;
case DRIVE_STRENGTH_12MA :
value = PM_PINCTRL_DRIVE_STRENGTH_12MA ;
break ;
default :
/* Invalid drive strength */
dev_warn ( pctldev - > dev ,
" Invalid drive strength for pin %d \n " ,
pin ) ;
return - EINVAL ;
}
param = PM_PINCTRL_CONFIG_DRIVE_STRENGTH ;
ret = zynqmp_pm_pinctrl_set_config ( pin , param , value ) ;
break ;
case PIN_CONFIG_POWER_SOURCE :
param = PM_PINCTRL_CONFIG_VOLTAGE_STATUS ;
ret = zynqmp_pm_pinctrl_get_config ( pin , param , & value ) ;
if ( arg ! = value )
dev_warn ( pctldev - > dev ,
" Invalid IO Standard requested for pin %d \n " ,
pin ) ;
break ;
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE :
2022-06-17 13:46:58 +03:00
param = PM_PINCTRL_CONFIG_TRI_STATE ;
arg = PM_PINCTRL_TRI_STATE_ENABLE ;
ret = zynqmp_pm_pinctrl_set_config ( pin , param , arg ) ;
break ;
2021-04-22 11:30:02 +03:00
case PIN_CONFIG_MODE_LOW_POWER :
/*
* These cases are mentioned in dts but configurable
* registers are unknown . So falling through to ignore
* boot time warnings as of now .
*/
ret = 0 ;
break ;
2022-06-17 13:46:58 +03:00
case PIN_CONFIG_OUTPUT_ENABLE :
param = PM_PINCTRL_CONFIG_TRI_STATE ;
arg = PM_PINCTRL_TRI_STATE_DISABLE ;
ret = zynqmp_pm_pinctrl_set_config ( pin , param , arg ) ;
break ;
2021-04-22 11:30:02 +03:00
default :
dev_warn ( pctldev - > dev ,
" unsupported configuration parameter '%u' \n " ,
param ) ;
2021-06-21 14:00:14 +03:00
ret = - ENOTSUPP ;
2021-04-22 11:30:02 +03:00
break ;
}
param = pinconf_to_config_param ( configs [ i ] ) ;
arg = pinconf_to_config_argument ( configs [ i ] ) ;
if ( ret )
dev_warn ( pctldev - > dev ,
" failed to set: pin %u param %u value %u \n " ,
pin , param , arg ) ;
}
return 0 ;
}
/**
* zynqmp_pinconf_group_set ( ) - Set requested config for the group
* @ pctldev : Pincontrol device pointer .
* @ selector : Group ID .
* @ configs : Configuration to set .
* @ num_configs : Number of configurations .
*
* Call function to set configs for each pin in the group .
*
* Return : 0 on success else error code .
*/
static int zynqmp_pinconf_group_set ( struct pinctrl_dev * pctldev ,
unsigned int selector ,
unsigned long * configs ,
unsigned int num_configs )
{
int i , ret ;
struct zynqmp_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
const struct zynqmp_pctrl_group * pgrp = & pctrl - > groups [ selector ] ;
for ( i = 0 ; i < pgrp - > npins ; i + + ) {
ret = zynqmp_pinconf_cfg_set ( pctldev , pgrp - > pins [ i ] , configs ,
num_configs ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static const struct pinconf_ops zynqmp_pinconf_ops = {
. is_generic = true ,
. pin_config_get = zynqmp_pinconf_cfg_get ,
. pin_config_set = zynqmp_pinconf_cfg_set ,
. pin_config_group_set = zynqmp_pinconf_group_set ,
} ;
static struct pinctrl_desc zynqmp_desc = {
. name = " zynqmp_pinctrl " ,
. owner = THIS_MODULE ,
. pctlops = & zynqmp_pctrl_ops ,
. pmxops = & zynqmp_pinmux_ops ,
. confops = & zynqmp_pinconf_ops ,
} ;
static int zynqmp_pinctrl_get_function_groups ( u32 fid , u32 index , u16 * groups )
{
struct zynqmp_pm_query_data qdata = { 0 } ;
u32 payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
qdata . qid = PM_QID_PINCTRL_GET_FUNCTION_GROUPS ;
qdata . arg1 = fid ;
qdata . arg2 = index ;
ret = zynqmp_pm_query_data ( qdata , payload ) ;
if ( ret )
return ret ;
memcpy ( groups , & payload [ 1 ] , PINCTRL_GET_FUNC_GROUPS_RESP_LEN ) ;
2021-06-21 14:00:14 +03:00
return 0 ;
2021-04-22 11:30:02 +03:00
}
static int zynqmp_pinctrl_get_func_num_groups ( u32 fid , unsigned int * ngroups )
{
struct zynqmp_pm_query_data qdata = { 0 } ;
u32 payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
qdata . qid = PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS ;
qdata . arg1 = fid ;
ret = zynqmp_pm_query_data ( qdata , payload ) ;
if ( ret )
return ret ;
* ngroups = payload [ 1 ] ;
2021-06-21 14:00:14 +03:00
return 0 ;
2021-04-22 11:30:02 +03:00
}
/**
* zynqmp_pinctrl_prepare_func_groups ( ) - prepare function and groups data
* @ dev : Device pointer .
* @ fid : Function ID .
* @ func : Function data .
* @ groups : Groups data .
*
* Query firmware to get group IDs for each function . Firmware returns
2021-06-21 14:00:14 +03:00
* group IDs . Based on the group index for the function , group names in
2021-04-22 11:30:02 +03:00
* the function are stored . For example , the first group in " eth0 " function
2021-06-21 14:00:14 +03:00
* is named as " eth0_0 " and the second group as " eth0_1 " and so on .
2021-04-22 11:30:02 +03:00
*
* Based on the group ID received from the firmware , function stores name of
* the group for that group ID . For example , if " eth0 " first group ID
* is x , groups [ x ] name will be stored as " eth0_0 " .
*
* Once done for each function , each function would have its group names
2021-06-21 14:00:14 +03:00
* and each group would also have their names .
2021-04-22 11:30:02 +03:00
*
* Return : 0 on success else error code .
*/
static int zynqmp_pinctrl_prepare_func_groups ( struct device * dev , u32 fid ,
struct zynqmp_pmux_function * func ,
struct zynqmp_pctrl_group * groups )
{
u16 resp [ NUM_GROUPS_PER_RESP ] = { 0 } ;
const char * * fgroups ;
2021-06-21 14:00:14 +03:00
int ret , index , i ;
2021-04-22 11:30:02 +03:00
fgroups = devm_kzalloc ( dev , sizeof ( * fgroups ) * func - > ngroups , GFP_KERNEL ) ;
if ( ! fgroups )
return - ENOMEM ;
for ( index = 0 ; index < func - > ngroups ; index + = NUM_GROUPS_PER_RESP ) {
ret = zynqmp_pinctrl_get_function_groups ( fid , index , resp ) ;
if ( ret )
return ret ;
for ( i = 0 ; i < NUM_GROUPS_PER_RESP ; i + + ) {
if ( resp [ i ] = = NA_GROUP )
goto done ;
if ( resp [ i ] = = RESERVED_GROUP )
continue ;
fgroups [ index + i ] = devm_kasprintf ( dev , GFP_KERNEL ,
" %s_%d_grp " ,
func - > name ,
index + i ) ;
if ( ! fgroups [ index + i ] )
return - ENOMEM ;
groups [ resp [ i ] ] . name = devm_kasprintf ( dev , GFP_KERNEL ,
" %s_%d_grp " ,
func - > name ,
index + i ) ;
if ( ! groups [ resp [ i ] ] . name )
return - ENOMEM ;
}
}
done :
func - > groups = fgroups ;
2021-06-21 14:00:14 +03:00
return 0 ;
2021-04-22 11:30:02 +03:00
}
static void zynqmp_pinctrl_get_function_name ( u32 fid , char * name )
{
struct zynqmp_pm_query_data qdata = { 0 } ;
u32 payload [ PAYLOAD_ARG_CNT ] ;
qdata . qid = PM_QID_PINCTRL_GET_FUNCTION_NAME ;
qdata . arg1 = fid ;
/*
* Name of the function is maximum 16 bytes and cannot
* accommodate the return value in SMC buffers , hence ignoring
* the return value for this specific qid .
*/
zynqmp_pm_query_data ( qdata , payload ) ;
memcpy ( name , payload , PINCTRL_GET_FUNC_NAME_RESP_LEN ) ;
}
static int zynqmp_pinctrl_get_num_functions ( unsigned int * nfuncs )
{
struct zynqmp_pm_query_data qdata = { 0 } ;
u32 payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
qdata . qid = PM_QID_PINCTRL_GET_NUM_FUNCTIONS ;
ret = zynqmp_pm_query_data ( qdata , payload ) ;
if ( ret )
return ret ;
* nfuncs = payload [ 1 ] ;
2021-06-21 14:00:14 +03:00
return 0 ;
2021-04-22 11:30:02 +03:00
}
static int zynqmp_pinctrl_get_pin_groups ( u32 pin , u32 index , u16 * groups )
{
struct zynqmp_pm_query_data qdata = { 0 } ;
u32 payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
qdata . qid = PM_QID_PINCTRL_GET_PIN_GROUPS ;
qdata . arg1 = pin ;
qdata . arg2 = index ;
ret = zynqmp_pm_query_data ( qdata , payload ) ;
if ( ret )
return ret ;
memcpy ( groups , & payload [ 1 ] , PINCTRL_GET_PIN_GROUPS_RESP_LEN ) ;
2021-06-21 14:00:14 +03:00
return 0 ;
2021-04-22 11:30:02 +03:00
}
static void zynqmp_pinctrl_group_add_pin ( struct zynqmp_pctrl_group * group ,
unsigned int pin )
{
group - > pins [ group - > npins + + ] = pin ;
}
/**
* zynqmp_pinctrl_create_pin_groups ( ) - assign pins to respective groups
* @ dev : Device pointer .
* @ groups : Groups data .
* @ pin : Pin number .
*
* Query firmware to get groups available for the given pin .
* Based on the firmware response ( group IDs for the pin ) , add
* pin number to the respective group ' s pin array .
*
2021-06-21 14:00:14 +03:00
* Once all pins are queries , each group would have its number
2021-04-22 11:30:02 +03:00
* of pins and pin numbers data .
*
* Return : 0 on success else error code .
*/
static int zynqmp_pinctrl_create_pin_groups ( struct device * dev ,
struct zynqmp_pctrl_group * groups ,
unsigned int pin )
{
u16 resp [ NUM_GROUPS_PER_RESP ] = { 0 } ;
int ret , i , index = 0 ;
do {
ret = zynqmp_pinctrl_get_pin_groups ( pin , index , resp ) ;
if ( ret )
return ret ;
for ( i = 0 ; i < NUM_GROUPS_PER_RESP ; i + + ) {
if ( resp [ i ] = = NA_GROUP )
return ret ;
if ( resp [ i ] = = RESERVED_GROUP )
continue ;
zynqmp_pinctrl_group_add_pin ( & groups [ resp [ i ] ] , pin ) ;
}
index + = NUM_GROUPS_PER_RESP ;
} while ( index < = MAX_PIN_GROUPS ) ;
2021-06-21 14:00:14 +03:00
return 0 ;
2021-04-22 11:30:02 +03:00
}
/**
* zynqmp_pinctrl_prepare_group_pins ( ) - prepare each group ' s pin data
* @ dev : Device pointer .
* @ groups : Groups data .
* @ ngroups : Number of groups .
*
* Prepare pin number and number of pins data for each pins .
*
* Return : 0 on success else error code .
*/
static int zynqmp_pinctrl_prepare_group_pins ( struct device * dev ,
struct zynqmp_pctrl_group * groups ,
unsigned int ngroups )
{
unsigned int pin ;
int ret ;
for ( pin = 0 ; pin < zynqmp_desc . npins ; pin + + ) {
ret = zynqmp_pinctrl_create_pin_groups ( dev , groups , pin ) ;
if ( ret )
return ret ;
}
return 0 ;
}
/**
* zynqmp_pinctrl_prepare_function_info ( ) - prepare function info
* @ dev : Device pointer .
* @ pctrl : Pin control driver data .
*
* Query firmware for functions , groups and pin information and
* prepare pin control driver data .
*
* Query number of functions and number of function groups ( number
2021-06-21 14:00:14 +03:00
* of groups in the given function ) to allocate required memory buffers
2021-04-22 11:30:02 +03:00
* for functions and groups . Once buffers are allocated to store
* functions and groups data , query and store required information
* ( number of groups and group names for each function , number of
* pins and pin numbers for each group ) .
*
* Return : 0 on success else error code .
*/
static int zynqmp_pinctrl_prepare_function_info ( struct device * dev ,
struct zynqmp_pinctrl * pctrl )
{
struct zynqmp_pmux_function * funcs ;
struct zynqmp_pctrl_group * groups ;
int ret , i ;
ret = zynqmp_pinctrl_get_num_functions ( & pctrl - > nfuncs ) ;
if ( ret )
return ret ;
funcs = devm_kzalloc ( dev , sizeof ( * funcs ) * pctrl - > nfuncs , GFP_KERNEL ) ;
if ( ! funcs )
return - ENOMEM ;
for ( i = 0 ; i < pctrl - > nfuncs ; i + + ) {
zynqmp_pinctrl_get_function_name ( i , funcs [ i ] . name ) ;
ret = zynqmp_pinctrl_get_func_num_groups ( i , & funcs [ i ] . ngroups ) ;
if ( ret )
return ret ;
pctrl - > ngroups + = funcs [ i ] . ngroups ;
}
groups = devm_kzalloc ( dev , sizeof ( * groups ) * pctrl - > ngroups , GFP_KERNEL ) ;
if ( ! groups )
return - ENOMEM ;
for ( i = 0 ; i < pctrl - > nfuncs ; i + + ) {
ret = zynqmp_pinctrl_prepare_func_groups ( dev , i , & funcs [ i ] ,
groups ) ;
if ( ret )
return ret ;
}
ret = zynqmp_pinctrl_prepare_group_pins ( dev , groups , pctrl - > ngroups ) ;
if ( ret )
return ret ;
pctrl - > funcs = funcs ;
pctrl - > groups = groups ;
2021-06-21 14:00:14 +03:00
return 0 ;
2021-04-22 11:30:02 +03:00
}
static int zynqmp_pinctrl_get_num_pins ( unsigned int * npins )
{
struct zynqmp_pm_query_data qdata = { 0 } ;
u32 payload [ PAYLOAD_ARG_CNT ] ;
int ret ;
qdata . qid = PM_QID_PINCTRL_GET_NUM_PINS ;
ret = zynqmp_pm_query_data ( qdata , payload ) ;
if ( ret )
return ret ;
* npins = payload [ 1 ] ;
2021-06-21 14:00:14 +03:00
return 0 ;
2021-04-22 11:30:02 +03:00
}
/**
* zynqmp_pinctrl_prepare_pin_desc ( ) - prepare pin description info
* @ dev : Device pointer .
* @ zynqmp_pins : Pin information .
* @ npins : Number of pins .
*
* Query number of pins information from firmware and prepare pin
* description containing pin number and pin name .
*
* Return : 0 on success else error code .
*/
static int zynqmp_pinctrl_prepare_pin_desc ( struct device * dev ,
const struct pinctrl_pin_desc
* * zynqmp_pins ,
unsigned int * npins )
{
struct pinctrl_pin_desc * pins , * pin ;
int ret ;
int i ;
ret = zynqmp_pinctrl_get_num_pins ( npins ) ;
if ( ret )
return ret ;
pins = devm_kzalloc ( dev , sizeof ( * pins ) * * npins , GFP_KERNEL ) ;
if ( ! pins )
return - ENOMEM ;
for ( i = 0 ; i < * npins ; i + + ) {
pin = & pins [ i ] ;
pin - > number = i ;
2022-01-27 22:46:02 +03:00
pin - > name = devm_kasprintf ( dev , GFP_KERNEL , " %s%d " ,
ZYNQMP_PIN_PREFIX , i ) ;
if ( ! pin - > name )
return - ENOMEM ;
2021-04-22 11:30:02 +03:00
}
* zynqmp_pins = pins ;
return 0 ;
}
static int zynqmp_pinctrl_probe ( struct platform_device * pdev )
{
struct zynqmp_pinctrl * pctrl ;
int ret ;
pctrl = devm_kzalloc ( & pdev - > dev , sizeof ( * pctrl ) , GFP_KERNEL ) ;
if ( ! pctrl )
return - ENOMEM ;
ret = zynqmp_pinctrl_prepare_pin_desc ( & pdev - > dev ,
& zynqmp_desc . pins ,
& zynqmp_desc . npins ) ;
if ( ret ) {
2021-06-21 14:00:14 +03:00
dev_err ( & pdev - > dev , " pin desc prepare fail with %d \n " , ret ) ;
2021-04-22 11:30:02 +03:00
return ret ;
}
ret = zynqmp_pinctrl_prepare_function_info ( & pdev - > dev , pctrl ) ;
if ( ret ) {
2021-06-21 14:00:14 +03:00
dev_err ( & pdev - > dev , " function info prepare fail with %d \n " , ret ) ;
2021-04-22 11:30:02 +03:00
return ret ;
}
2021-06-21 14:00:14 +03:00
pctrl - > pctrl = devm_pinctrl_register ( & pdev - > dev , & zynqmp_desc , pctrl ) ;
2021-04-22 11:30:02 +03:00
if ( IS_ERR ( pctrl - > pctrl ) )
return PTR_ERR ( pctrl - > pctrl ) ;
platform_set_drvdata ( pdev , pctrl ) ;
return ret ;
}
static const struct of_device_id zynqmp_pinctrl_of_match [ ] = {
{ . compatible = " xlnx,zynqmp-pinctrl " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , zynqmp_pinctrl_of_match ) ;
static struct platform_driver zynqmp_pinctrl_driver = {
. driver = {
. name = " zynqmp-pinctrl " ,
. of_match_table = zynqmp_pinctrl_of_match ,
} ,
. probe = zynqmp_pinctrl_probe ,
} ;
module_platform_driver ( zynqmp_pinctrl_driver ) ;
MODULE_AUTHOR ( " Sai Krishna Potthuri <lakshmi.sai.krishna.potthuri@xilinx.com> " ) ;
MODULE_DESCRIPTION ( " ZynqMP Pin Controller Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;