2018-11-15 19:15:27 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Combined GPIO and pin controller support for Renesas RZ / A2 ( R7S9210 ) SoC
*
* Copyright ( C ) 2018 Chris Brandt
*/
/*
* This pin controller / gpio combined driver supports Renesas devices of RZ / A2
* family .
*/
# include <linux/bitops.h>
2019-08-20 16:59:55 +03:00
# include <linux/gpio/driver.h>
# include <linux/io.h>
2018-11-15 19:15:27 +03:00
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/pinctrl/pinmux.h>
# include "core.h"
# include "pinmux.h"
# define DRIVER_NAME "pinctrl-rza2"
# define RZA2_PINS_PER_PORT 8
# define RZA2_PIN_ID_TO_PORT(id) ((id) / RZA2_PINS_PER_PORT)
# define RZA2_PIN_ID_TO_PIN(id) ((id) % RZA2_PINS_PER_PORT)
/*
* Use 16 lower bits [ 15 : 0 ] for pin identifier
* Use 16 higher bits [ 31 : 16 ] for pin mux function
*/
# define MUX_PIN_ID_MASK GENMASK(15, 0)
# define MUX_FUNC_MASK GENMASK(31, 16)
# define MUX_FUNC_OFFS 16
# define MUX_FUNC(pinconf) ((pinconf & MUX_FUNC_MASK) >> MUX_FUNC_OFFS)
static const char port_names [ ] = " 0123456789ABCDEFGHJKLM " ;
struct rza2_pinctrl_priv {
struct device * dev ;
void __iomem * base ;
struct pinctrl_pin_desc * pins ;
struct pinctrl_desc desc ;
struct pinctrl_dev * pctl ;
struct pinctrl_gpio_range gpio_range ;
int npins ;
} ;
# define RZA2_PDR(port) (0x0000 + (port) * 2) /* Direction 16-bit */
# define RZA2_PODR(port) (0x0040 + (port)) /* Output Data 8-bit */
# define RZA2_PIDR(port) (0x0060 + (port)) /* Input Data 8-bit */
# define RZA2_PMR(port) (0x0080 + (port)) /* Mode 8-bit */
# define RZA2_DSCR(port) (0x0140 + (port) * 2) /* Drive 16-bit */
# define RZA2_PFS(port, pin) (0x0200 + ((port) * 8) + (pin)) /* Fnct 8-bit */
# define RZA2_PWPR 0x02ff /* Write Protect 8-bit */
# define RZA2_PFENET 0x0820 /* Ethernet Pins 8-bit */
# define RZA2_PPOC 0x0900 /* Dedicated Pins 32-bit */
# define RZA2_PHMOMO 0x0980 /* Peripheral Pins 32-bit */
# define RZA2_PCKIO 0x09d0 /* CKIO Drive 8-bit */
# define RZA2_PDR_INPUT 0x02
# define RZA2_PDR_OUTPUT 0x03
# define RZA2_PDR_MASK 0x03
# define PWPR_B0WI BIT(7) /* Bit Write Disable */
# define PWPR_PFSWE BIT(6) /* PFS Register Write Enable */
# define PFS_ISEL BIT(6) /* Interrupt Select */
static void rza2_set_pin_function ( void __iomem * pfc_base , u8 port , u8 pin ,
u8 func )
{
u16 mask16 ;
u16 reg16 ;
u8 reg8 ;
/* Set pin to 'Non-use (Hi-z input protection)' */
reg16 = readw ( pfc_base + RZA2_PDR ( port ) ) ;
mask16 = RZA2_PDR_MASK < < ( pin * 2 ) ;
reg16 & = ~ mask16 ;
writew ( reg16 , pfc_base + RZA2_PDR ( port ) ) ;
/* Temporarily switch to GPIO */
reg8 = readb ( pfc_base + RZA2_PMR ( port ) ) ;
reg8 & = ~ BIT ( pin ) ;
writeb ( reg8 , pfc_base + RZA2_PMR ( port ) ) ;
/* PFS Register Write Protect : OFF */
writeb ( 0x00 , pfc_base + RZA2_PWPR ) ; /* B0WI=0, PFSWE=0 */
writeb ( PWPR_PFSWE , pfc_base + RZA2_PWPR ) ; /* B0WI=0, PFSWE=1 */
/* Set Pin function (interrupt disabled, ISEL=0) */
writeb ( func , pfc_base + RZA2_PFS ( port , pin ) ) ;
/* PFS Register Write Protect : ON */
writeb ( 0x00 , pfc_base + RZA2_PWPR ) ; /* B0WI=0, PFSWE=0 */
writeb ( 0x80 , pfc_base + RZA2_PWPR ) ; /* B0WI=1, PFSWE=0 */
/* Port Mode : Peripheral module pin functions */
reg8 = readb ( pfc_base + RZA2_PMR ( port ) ) ;
reg8 | = BIT ( pin ) ;
writeb ( reg8 , pfc_base + RZA2_PMR ( port ) ) ;
}
static void rza2_pin_to_gpio ( void __iomem * pfc_base , unsigned int offset ,
u8 dir )
{
u8 port = RZA2_PIN_ID_TO_PORT ( offset ) ;
u8 pin = RZA2_PIN_ID_TO_PIN ( offset ) ;
u16 mask16 ;
u16 reg16 ;
reg16 = readw ( pfc_base + RZA2_PDR ( port ) ) ;
mask16 = RZA2_PDR_MASK < < ( pin * 2 ) ;
reg16 & = ~ mask16 ;
2019-08-20 16:59:54 +03:00
if ( dir )
2018-11-15 19:15:27 +03:00
reg16 | = RZA2_PDR_INPUT < < ( pin * 2 ) ; /* pin as input */
else
reg16 | = RZA2_PDR_OUTPUT < < ( pin * 2 ) ; /* pin as output */
writew ( reg16 , pfc_base + RZA2_PDR ( port ) ) ;
}
static int rza2_chip_get_direction ( struct gpio_chip * chip , unsigned int offset )
{
struct rza2_pinctrl_priv * priv = gpiochip_get_data ( chip ) ;
u8 port = RZA2_PIN_ID_TO_PORT ( offset ) ;
u8 pin = RZA2_PIN_ID_TO_PIN ( offset ) ;
u16 reg16 ;
reg16 = readw ( priv - > base + RZA2_PDR ( port ) ) ;
reg16 = ( reg16 > > ( pin * 2 ) ) & RZA2_PDR_MASK ;
if ( reg16 = = RZA2_PDR_OUTPUT )
2020-02-14 16:57:12 +03:00
return GPIO_LINE_DIRECTION_OUT ;
2018-11-15 19:15:27 +03:00
if ( reg16 = = RZA2_PDR_INPUT )
2020-02-14 16:57:12 +03:00
return GPIO_LINE_DIRECTION_IN ;
2018-11-15 19:15:27 +03:00
/*
* This GPIO controller has a default Hi - Z state that is not input or
* output , so force the pin to input now .
*/
2019-08-20 16:59:54 +03:00
rza2_pin_to_gpio ( priv - > base , offset , 1 ) ;
2018-11-15 19:15:27 +03:00
2020-02-14 16:57:12 +03:00
return GPIO_LINE_DIRECTION_IN ;
2018-11-15 19:15:27 +03:00
}
static int rza2_chip_direction_input ( struct gpio_chip * chip ,
unsigned int offset )
{
struct rza2_pinctrl_priv * priv = gpiochip_get_data ( chip ) ;
2019-08-20 16:59:54 +03:00
rza2_pin_to_gpio ( priv - > base , offset , 1 ) ;
2018-11-15 19:15:27 +03:00
return 0 ;
}
static int rza2_chip_get ( struct gpio_chip * chip , unsigned int offset )
{
struct rza2_pinctrl_priv * priv = gpiochip_get_data ( chip ) ;
u8 port = RZA2_PIN_ID_TO_PORT ( offset ) ;
u8 pin = RZA2_PIN_ID_TO_PIN ( offset ) ;
return ! ! ( readb ( priv - > base + RZA2_PIDR ( port ) ) & BIT ( pin ) ) ;
}
static void rza2_chip_set ( struct gpio_chip * chip , unsigned int offset ,
int value )
{
struct rza2_pinctrl_priv * priv = gpiochip_get_data ( chip ) ;
u8 port = RZA2_PIN_ID_TO_PORT ( offset ) ;
u8 pin = RZA2_PIN_ID_TO_PIN ( offset ) ;
u8 new_value ;
new_value = readb ( priv - > base + RZA2_PODR ( port ) ) ;
if ( value )
new_value | = BIT ( pin ) ;
else
new_value & = ~ BIT ( pin ) ;
writeb ( new_value , priv - > base + RZA2_PODR ( port ) ) ;
}
static int rza2_chip_direction_output ( struct gpio_chip * chip ,
unsigned int offset , int val )
{
struct rza2_pinctrl_priv * priv = gpiochip_get_data ( chip ) ;
rza2_chip_set ( chip , offset , val ) ;
2019-08-20 16:59:54 +03:00
rza2_pin_to_gpio ( priv - > base , offset , 0 ) ;
2018-11-15 19:15:27 +03:00
return 0 ;
}
static const char * const rza2_gpio_names [ ] = {
" P0_0 " , " P0_1 " , " P0_2 " , " P0_3 " , " P0_4 " , " P0_5 " , " P0_6 " , " P0_7 " ,
" P1_0 " , " P1_1 " , " P1_2 " , " P1_3 " , " P1_4 " , " P1_5 " , " P1_6 " , " P1_7 " ,
" P2_0 " , " P2_1 " , " P2_2 " , " P2_3 " , " P2_4 " , " P2_5 " , " P2_6 " , " P2_7 " ,
" P3_0 " , " P3_1 " , " P3_2 " , " P3_3 " , " P3_4 " , " P3_5 " , " P3_6 " , " P3_7 " ,
" P4_0 " , " P4_1 " , " P4_2 " , " P4_3 " , " P4_4 " , " P4_5 " , " P4_6 " , " P4_7 " ,
" P5_0 " , " P5_1 " , " P5_2 " , " P5_3 " , " P5_4 " , " P5_5 " , " P5_6 " , " P5_7 " ,
" P6_0 " , " P6_1 " , " P6_2 " , " P6_3 " , " P6_4 " , " P6_5 " , " P6_6 " , " P6_7 " ,
" P7_0 " , " P7_1 " , " P7_2 " , " P7_3 " , " P7_4 " , " P7_5 " , " P7_6 " , " P7_7 " ,
" P8_0 " , " P8_1 " , " P8_2 " , " P8_3 " , " P8_4 " , " P8_5 " , " P8_6 " , " P8_7 " ,
" P9_0 " , " P9_1 " , " P9_2 " , " P9_3 " , " P9_4 " , " P9_5 " , " P9_6 " , " P9_7 " ,
" PA_0 " , " PA_1 " , " PA_2 " , " PA_3 " , " PA_4 " , " PA_5 " , " PA_6 " , " PA_7 " ,
" PB_0 " , " PB_1 " , " PB_2 " , " PB_3 " , " PB_4 " , " PB_5 " , " PB_6 " , " PB_7 " ,
" PC_0 " , " PC_1 " , " PC_2 " , " PC_3 " , " PC_4 " , " PC_5 " , " PC_6 " , " PC_7 " ,
" PD_0 " , " PD_1 " , " PD_2 " , " PD_3 " , " PD_4 " , " PD_5 " , " PD_6 " , " PD_7 " ,
" PE_0 " , " PE_1 " , " PE_2 " , " PE_3 " , " PE_4 " , " PE_5 " , " PE_6 " , " PE_7 " ,
2019-09-30 17:58:04 +03:00
" PF_0 " , " PF_1 " , " PF_2 " , " PF_3 " , " PF_4 " , " PF_5 " , " PF_6 " , " PF_7 " ,
" PG_0 " , " PG_1 " , " PG_2 " , " PG_3 " , " PG_4 " , " PG_5 " , " PG_6 " , " PG_7 " ,
2018-11-15 19:15:27 +03:00
" PH_0 " , " PH_1 " , " PH_2 " , " PH_3 " , " PH_4 " , " PH_5 " , " PH_6 " , " PH_7 " ,
/* port I does not exist */
" PJ_0 " , " PJ_1 " , " PJ_2 " , " PJ_3 " , " PJ_4 " , " PJ_5 " , " PJ_6 " , " PJ_7 " ,
" PK_0 " , " PK_1 " , " PK_2 " , " PK_3 " , " PK_4 " , " PK_5 " , " PK_6 " , " PK_7 " ,
" PL_0 " , " PL_1 " , " PL_2 " , " PL_3 " , " PL_4 " , " PL_5 " , " PL_6 " , " PL_7 " ,
" PM_0 " , " PM_1 " , " PM_2 " , " PM_3 " , " PM_4 " , " PM_5 " , " PM_6 " , " PM_7 " ,
} ;
static struct gpio_chip chip = {
. names = rza2_gpio_names ,
. base = - 1 ,
. get_direction = rza2_chip_get_direction ,
. direction_input = rza2_chip_direction_input ,
. direction_output = rza2_chip_direction_output ,
. get = rza2_chip_get ,
. set = rza2_chip_set ,
} ;
static int rza2_gpio_register ( struct rza2_pinctrl_priv * priv )
{
struct device_node * np = priv - > dev - > of_node ;
struct of_phandle_args of_args ;
int ret ;
chip . label = devm_kasprintf ( priv - > dev , GFP_KERNEL , " %pOFn " , np ) ;
chip . of_node = np ;
chip . parent = priv - > dev ;
chip . ngpio = priv - > npins ;
ret = of_parse_phandle_with_fixed_args ( np , " gpio-ranges " , 3 , 0 ,
& of_args ) ;
if ( ret ) {
dev_err ( priv - > dev , " Unable to parse gpio-ranges \n " ) ;
return ret ;
}
if ( ( of_args . args [ 0 ] ! = 0 ) | |
( of_args . args [ 1 ] ! = 0 ) | |
( of_args . args [ 2 ] ! = priv - > npins ) ) {
dev_err ( priv - > dev , " gpio-ranges does not match selected SOC \n " ) ;
return - EINVAL ;
}
priv - > gpio_range . id = 0 ;
priv - > gpio_range . pin_base = priv - > gpio_range . base = 0 ;
priv - > gpio_range . npins = priv - > npins ;
priv - > gpio_range . name = chip . label ;
priv - > gpio_range . gc = & chip ;
/* Register our gpio chip with gpiolib */
ret = devm_gpiochip_add_data ( priv - > dev , & chip , priv ) ;
if ( ret )
return ret ;
/* Register pin range with pinctrl core */
pinctrl_add_gpio_range ( priv - > pctl , & priv - > gpio_range ) ;
dev_dbg ( priv - > dev , " Registered gpio controller \n " ) ;
return 0 ;
}
static int rza2_pinctrl_register ( struct rza2_pinctrl_priv * priv )
{
struct pinctrl_pin_desc * pins ;
unsigned int i ;
int ret ;
pins = devm_kcalloc ( priv - > dev , priv - > npins , sizeof ( * pins ) , GFP_KERNEL ) ;
if ( ! pins )
return - ENOMEM ;
priv - > pins = pins ;
priv - > desc . pins = pins ;
priv - > desc . npins = priv - > npins ;
for ( i = 0 ; i < priv - > npins ; i + + ) {
pins [ i ] . number = i ;
pins [ i ] . name = rza2_gpio_names [ i ] ;
}
ret = devm_pinctrl_register_and_init ( priv - > dev , & priv - > desc , priv ,
& priv - > pctl ) ;
if ( ret ) {
dev_err ( priv - > dev , " pinctrl registration failed \n " ) ;
return ret ;
}
ret = pinctrl_enable ( priv - > pctl ) ;
if ( ret ) {
dev_err ( priv - > dev , " pinctrl enable failed \n " ) ;
return ret ;
}
ret = rza2_gpio_register ( priv ) ;
if ( ret ) {
dev_err ( priv - > dev , " GPIO registration failed \n " ) ;
return ret ;
}
return 0 ;
}
/*
* For each DT node , create a single pin mapping . That pin mapping will only
* contain a single group of pins , and that group of pins will only have a
* single function that can be selected .
*/
static int rza2_dt_node_to_map ( struct pinctrl_dev * pctldev ,
struct device_node * np ,
struct pinctrl_map * * map ,
unsigned int * num_maps )
{
struct rza2_pinctrl_priv * priv = pinctrl_dev_get_drvdata ( pctldev ) ;
unsigned int * pins , * psel_val ;
int i , ret , npins , gsel , fsel ;
struct property * of_pins ;
const char * * pin_fn ;
/* Find out how many pins to map */
of_pins = of_find_property ( np , " pinmux " , NULL ) ;
if ( ! of_pins ) {
dev_info ( priv - > dev , " Missing pinmux property \n " ) ;
return - ENOENT ;
}
npins = of_pins - > length / sizeof ( u32 ) ;
pins = devm_kcalloc ( priv - > dev , npins , sizeof ( * pins ) , GFP_KERNEL ) ;
psel_val = devm_kcalloc ( priv - > dev , npins , sizeof ( * psel_val ) ,
GFP_KERNEL ) ;
pin_fn = devm_kzalloc ( priv - > dev , sizeof ( * pin_fn ) , GFP_KERNEL ) ;
if ( ! pins | | ! psel_val | | ! pin_fn )
return - ENOMEM ;
/* Collect pin locations and mux settings from DT properties */
for ( i = 0 ; i < npins ; + + i ) {
u32 value ;
ret = of_property_read_u32_index ( np , " pinmux " , i , & value ) ;
if ( ret )
return ret ;
pins [ i ] = value & MUX_PIN_ID_MASK ;
psel_val [ i ] = MUX_FUNC ( value ) ;
}
/* Register a single pin group listing all the pins we read from DT */
gsel = pinctrl_generic_add_group ( pctldev , np - > name , pins , npins , NULL ) ;
if ( gsel < 0 )
return gsel ;
/*
* Register a single group function where the ' data ' is an array PSEL
* register values read from DT .
*/
pin_fn [ 0 ] = np - > name ;
fsel = pinmux_generic_add_function ( pctldev , np - > name , pin_fn , 1 ,
psel_val ) ;
if ( fsel < 0 ) {
ret = fsel ;
goto remove_group ;
}
dev_dbg ( priv - > dev , " Parsed %pOF with %d pins \n " , np , npins ) ;
/* Create map where to retrieve function and mux settings from */
* num_maps = 0 ;
* map = kzalloc ( sizeof ( * * map ) , GFP_KERNEL ) ;
if ( ! * map ) {
ret = - ENOMEM ;
goto remove_function ;
}
( * map ) - > type = PIN_MAP_TYPE_MUX_GROUP ;
( * map ) - > data . mux . group = np - > name ;
( * map ) - > data . mux . function = np - > name ;
* num_maps = 1 ;
return 0 ;
remove_function :
pinmux_generic_remove_function ( pctldev , fsel ) ;
remove_group :
pinctrl_generic_remove_group ( pctldev , gsel ) ;
dev_err ( priv - > dev , " Unable to parse DT node %s \n " , np - > name ) ;
return ret ;
}
static void rza2_dt_free_map ( struct pinctrl_dev * pctldev ,
struct pinctrl_map * map , unsigned int num_maps )
{
kfree ( map ) ;
}
static const struct pinctrl_ops rza2_pinctrl_ops = {
. get_groups_count = pinctrl_generic_get_group_count ,
. get_group_name = pinctrl_generic_get_group_name ,
. get_group_pins = pinctrl_generic_get_group_pins ,
. dt_node_to_map = rza2_dt_node_to_map ,
. dt_free_map = rza2_dt_free_map ,
} ;
static int rza2_set_mux ( struct pinctrl_dev * pctldev , unsigned int selector ,
unsigned int group )
{
struct rza2_pinctrl_priv * priv = pinctrl_dev_get_drvdata ( pctldev ) ;
struct function_desc * func ;
unsigned int i , * psel_val ;
struct group_desc * grp ;
grp = pinctrl_generic_get_group ( pctldev , group ) ;
if ( ! grp )
return - EINVAL ;
func = pinmux_generic_get_function ( pctldev , selector ) ;
if ( ! func )
return - EINVAL ;
psel_val = func - > data ;
for ( i = 0 ; i < grp - > num_pins ; + + i ) {
dev_dbg ( priv - > dev , " Setting P%c_%d to PSEL=%d \n " ,
port_names [ RZA2_PIN_ID_TO_PORT ( grp - > pins [ i ] ) ] ,
RZA2_PIN_ID_TO_PIN ( grp - > pins [ i ] ) ,
psel_val [ i ] ) ;
rza2_set_pin_function (
priv - > base ,
RZA2_PIN_ID_TO_PORT ( grp - > pins [ i ] ) ,
RZA2_PIN_ID_TO_PIN ( grp - > pins [ i ] ) ,
psel_val [ i ] ) ;
}
return 0 ;
}
static const struct pinmux_ops rza2_pinmux_ops = {
. get_functions_count = pinmux_generic_get_function_count ,
. get_function_name = pinmux_generic_get_function_name ,
. get_function_groups = pinmux_generic_get_function_groups ,
. set_mux = rza2_set_mux ,
. strict = true ,
} ;
static int rza2_pinctrl_probe ( struct platform_device * pdev )
{
struct rza2_pinctrl_priv * priv ;
int ret ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > dev = & pdev - > dev ;
2019-11-04 17:26:54 +03:00
priv - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2018-11-15 19:15:27 +03:00
if ( IS_ERR ( priv - > base ) )
return PTR_ERR ( priv - > base ) ;
platform_set_drvdata ( pdev , priv ) ;
priv - > npins = ( int ) ( uintptr_t ) of_device_get_match_data ( & pdev - > dev ) *
RZA2_PINS_PER_PORT ;
priv - > desc . name = DRIVER_NAME ;
priv - > desc . pctlops = & rza2_pinctrl_ops ;
priv - > desc . pmxops = & rza2_pinmux_ops ;
priv - > desc . owner = THIS_MODULE ;
ret = rza2_pinctrl_register ( priv ) ;
if ( ret )
return ret ;
dev_info ( & pdev - > dev , " Registered ports P0 - P%c \n " ,
port_names [ priv - > desc . npins / RZA2_PINS_PER_PORT - 1 ] ) ;
return 0 ;
}
static const struct of_device_id rza2_pinctrl_of_match [ ] = {
{ . compatible = " renesas,r7s9210-pinctrl " , . data = ( void * ) 22 , } ,
{ /* sentinel */ }
} ;
static struct platform_driver rza2_pinctrl_driver = {
. driver = {
. name = DRIVER_NAME ,
. of_match_table = rza2_pinctrl_of_match ,
} ,
. probe = rza2_pinctrl_probe ,
} ;
static int __init rza2_pinctrl_init ( void )
{
return platform_driver_register ( & rza2_pinctrl_driver ) ;
}
core_initcall ( rza2_pinctrl_init ) ;
MODULE_AUTHOR ( " Chris Brandt <chris.brandt@renesas.com> " ) ;
MODULE_DESCRIPTION ( " Pin and gpio controller driver for RZ/A2 SoC " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;