2018-09-08 19:07:17 +08:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2018 MediaTek Inc .
*
* Author : Sean Wang < sean . wang @ mediatek . com >
*
*/
# include <linux/device.h>
# include <linux/err.h>
# include <linux/gpio.h>
# include <linux/io.h>
# include "pinctrl-mtk-common-v2.h"
2018-09-08 19:07:22 +08:00
/**
* struct mtk_drive_desc - the structure that holds the information
* of the driving current
* @ min : the minimum current of this group
* @ max : the maximum current of this group
* @ step : the step current of this group
* @ scal : the weight factor
*
* formula : output = ( ( input ) / step - 1 ) * scal
*/
struct mtk_drive_desc {
u8 min ;
u8 max ;
u8 step ;
u8 scal ;
} ;
/* The groups of drive strength */
const struct mtk_drive_desc mtk_drive [ ] = {
[ DRV_GRP0 ] = { 4 , 16 , 4 , 1 } ,
[ DRV_GRP1 ] = { 4 , 16 , 4 , 2 } ,
[ DRV_GRP2 ] = { 2 , 8 , 2 , 1 } ,
[ DRV_GRP3 ] = { 2 , 8 , 2 , 2 } ,
[ DRV_GRP4 ] = { 2 , 16 , 2 , 1 } ,
} ;
2018-09-08 19:07:30 +08:00
static void mtk_w32 ( struct mtk_pinctrl * pctl , u8 i , u32 reg , u32 val )
2018-09-08 19:07:17 +08:00
{
2018-09-08 19:07:30 +08:00
writel_relaxed ( val , pctl - > base [ i ] + reg ) ;
2018-09-08 19:07:17 +08:00
}
2018-09-08 19:07:30 +08:00
static u32 mtk_r32 ( struct mtk_pinctrl * pctl , u8 i , u32 reg )
2018-09-08 19:07:17 +08:00
{
2018-09-08 19:07:30 +08:00
return readl_relaxed ( pctl - > base [ i ] + reg ) ;
2018-09-08 19:07:17 +08:00
}
2018-09-08 19:07:30 +08:00
void mtk_rmw ( struct mtk_pinctrl * pctl , u8 i , u32 reg , u32 mask , u32 set )
2018-09-08 19:07:17 +08:00
{
u32 val ;
2018-09-08 19:07:30 +08:00
val = mtk_r32 ( pctl , i , reg ) ;
2018-09-08 19:07:17 +08:00
val & = ~ mask ;
val | = set ;
2018-09-08 19:07:30 +08:00
mtk_w32 ( pctl , i , reg , val ) ;
2018-09-08 19:07:17 +08:00
}
2018-09-08 19:07:29 +08:00
static int mtk_hw_pin_field_lookup ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc ,
2018-09-08 19:07:31 +08:00
int field , struct mtk_pin_field * pfd )
2018-09-08 19:07:17 +08:00
{
const struct mtk_pin_field_calc * c , * e ;
2018-09-08 19:07:31 +08:00
const struct mtk_pin_reg_calc * rc ;
2018-09-08 19:07:17 +08:00
u32 bits ;
2018-09-08 19:07:31 +08:00
if ( hw - > soc - > reg_cal & & hw - > soc - > reg_cal [ field ] . range ) {
rc = & hw - > soc - > reg_cal [ field ] ;
} else {
dev_dbg ( hw - > dev ,
" Not support field %d for pin %d (%s) \n " ,
field , desc - > number , desc - > name ) ;
return - ENOTSUPP ;
}
2018-09-08 19:07:17 +08:00
c = rc - > range ;
e = c + rc - > nranges ;
while ( c < e ) {
2018-09-08 19:07:29 +08:00
if ( desc - > number > = c - > s_pin & & desc - > number < = c - > e_pin )
2018-09-08 19:07:17 +08:00
break ;
c + + ;
}
if ( c > = e ) {
2018-09-08 19:07:31 +08:00
dev_dbg ( hw - > dev , " Not support field %d for pin = %d (%s) \n " ,
field , desc - > number , desc - > name ) ;
return - ENOTSUPP ;
2018-09-08 19:07:17 +08:00
}
2018-09-08 19:07:30 +08:00
if ( c - > i_base > hw - > nbase - 1 ) {
2018-09-08 19:07:31 +08:00
dev_err ( hw - > dev ,
" Invalid base for field %d for pin = %d (%s) \n " ,
field , desc - > number , desc - > name ) ;
2018-09-08 19:07:30 +08:00
return - EINVAL ;
}
2018-09-08 19:07:19 +08:00
/* Calculated bits as the overall offset the pin is located at,
* if c - > fixed is held , that determines the all the pins in the
* range use the same field with the s_pin .
*/
2018-09-08 19:07:29 +08:00
bits = c - > fixed ? c - > s_bit : c - > s_bit +
( desc - > number - c - > s_pin ) * ( c - > x_bits ) ;
2018-09-08 19:07:17 +08:00
2018-09-08 19:07:19 +08:00
/* Fill pfd from bits. For example 32-bit register applied is assumed
* when c - > sz_reg is equal to 32.
*/
2018-09-08 19:07:30 +08:00
pfd - > index = c - > i_base ;
2018-09-08 19:07:19 +08:00
pfd - > offset = c - > s_addr + c - > x_addrs * ( bits / c - > sz_reg ) ;
pfd - > bitpos = bits % c - > sz_reg ;
2018-09-08 19:07:17 +08:00
pfd - > mask = ( 1 < < c - > x_bits ) - 1 ;
/* pfd->next is used for indicating that bit wrapping-around happens
* which requires the manipulation for bit 0 starting in the next
* register to form the complete field read / write .
*/
2018-09-08 19:07:19 +08:00
pfd - > next = pfd - > bitpos + c - > x_bits > c - > sz_reg ? c - > x_addrs : 0 ;
2018-09-08 19:07:17 +08:00
return 0 ;
}
2018-09-08 19:07:29 +08:00
static int mtk_hw_pin_field_get ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc ,
2018-09-08 19:07:17 +08:00
int field , struct mtk_pin_field * pfd )
{
if ( field < 0 | | field > = PINCTRL_PIN_REG_MAX ) {
dev_err ( hw - > dev , " Invalid Field %d \n " , field ) ;
return - EINVAL ;
}
2018-09-08 19:07:31 +08:00
return mtk_hw_pin_field_lookup ( hw , desc , field , pfd ) ;
2018-09-08 19:07:17 +08:00
}
static void mtk_hw_bits_part ( struct mtk_pin_field * pf , int * h , int * l )
{
* l = 32 - pf - > bitpos ;
* h = get_count_order ( pf - > mask ) - * l ;
}
static void mtk_hw_write_cross_field ( struct mtk_pinctrl * hw ,
struct mtk_pin_field * pf , int value )
{
int nbits_l , nbits_h ;
mtk_hw_bits_part ( pf , & nbits_h , & nbits_l ) ;
2018-09-08 19:07:30 +08:00
mtk_rmw ( hw , pf - > index , pf - > offset , pf - > mask < < pf - > bitpos ,
2018-09-08 19:07:17 +08:00
( value & pf - > mask ) < < pf - > bitpos ) ;
2018-09-08 19:07:30 +08:00
mtk_rmw ( hw , pf - > index , pf - > offset + pf - > next , BIT ( nbits_h ) - 1 ,
2018-09-08 19:07:17 +08:00
( value & pf - > mask ) > > nbits_l ) ;
}
static void mtk_hw_read_cross_field ( struct mtk_pinctrl * hw ,
struct mtk_pin_field * pf , int * value )
{
int nbits_l , nbits_h , h , l ;
mtk_hw_bits_part ( pf , & nbits_h , & nbits_l ) ;
2018-09-08 19:07:30 +08:00
l = ( mtk_r32 ( hw , pf - > index , pf - > offset )
> > pf - > bitpos ) & ( BIT ( nbits_l ) - 1 ) ;
h = ( mtk_r32 ( hw , pf - > index , pf - > offset + pf - > next ) )
& ( BIT ( nbits_h ) - 1 ) ;
2018-09-08 19:07:17 +08:00
* value = ( h < < nbits_l ) | l ;
}
2018-09-08 19:07:29 +08:00
int mtk_hw_set_value ( struct mtk_pinctrl * hw , const struct mtk_pin_desc * desc ,
int field , int value )
2018-09-08 19:07:17 +08:00
{
struct mtk_pin_field pf ;
int err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_pin_field_get ( hw , desc , field , & pf ) ;
2018-09-08 19:07:17 +08:00
if ( err )
return err ;
if ( ! pf . next )
2018-09-08 19:07:30 +08:00
mtk_rmw ( hw , pf . index , pf . offset , pf . mask < < pf . bitpos ,
2018-09-08 19:07:17 +08:00
( value & pf . mask ) < < pf . bitpos ) ;
else
mtk_hw_write_cross_field ( hw , & pf , value ) ;
return 0 ;
}
2018-09-08 19:07:29 +08:00
int mtk_hw_get_value ( struct mtk_pinctrl * hw , const struct mtk_pin_desc * desc ,
int field , int * value )
2018-09-08 19:07:17 +08:00
{
struct mtk_pin_field pf ;
int err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_pin_field_get ( hw , desc , field , & pf ) ;
2018-09-08 19:07:17 +08:00
if ( err )
return err ;
if ( ! pf . next )
2018-09-08 19:07:30 +08:00
* value = ( mtk_r32 ( hw , pf . index , pf . offset )
> > pf . bitpos ) & pf . mask ;
2018-09-08 19:07:17 +08:00
else
mtk_hw_read_cross_field ( hw , & pf , value ) ;
return 0 ;
}
2018-09-08 19:07:22 +08:00
2018-09-08 19:07:27 +08:00
/* Revision 0 */
2018-09-08 19:07:24 +08:00
int mtk_pinconf_bias_disable_set ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc )
{
int err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_PU ,
2018-09-08 19:07:24 +08:00
MTK_DISABLE ) ;
if ( err )
return err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_PD ,
2018-09-08 19:07:24 +08:00
MTK_DISABLE ) ;
if ( err )
return err ;
return 0 ;
}
int mtk_pinconf_bias_disable_get ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc , int * res )
{
int v , v2 ;
int err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_get_value ( hw , desc , PINCTRL_PIN_REG_PU , & v ) ;
2018-09-08 19:07:24 +08:00
if ( err )
return err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_get_value ( hw , desc , PINCTRL_PIN_REG_PD , & v2 ) ;
2018-09-08 19:07:24 +08:00
if ( err )
return err ;
if ( v = = MTK_ENABLE | | v2 = = MTK_ENABLE )
return - EINVAL ;
* res = 1 ;
return 0 ;
}
int mtk_pinconf_bias_set ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc , bool pullup )
{
int err , arg ;
arg = pullup ? 1 : 2 ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_PU , arg & 1 ) ;
2018-09-08 19:07:24 +08:00
if ( err )
return err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_PD ,
2018-09-08 19:07:24 +08:00
! ! ( arg & 2 ) ) ;
if ( err )
return err ;
return 0 ;
}
int mtk_pinconf_bias_get ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc , bool pullup , int * res )
{
int reg , err , v ;
reg = pullup ? PINCTRL_PIN_REG_PU : PINCTRL_PIN_REG_PD ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_get_value ( hw , desc , reg , & v ) ;
2018-09-08 19:07:24 +08:00
if ( err )
return err ;
if ( ! v )
return - EINVAL ;
* res = 1 ;
return 0 ;
}
2018-09-08 19:07:27 +08:00
/* Revision 1 */
int mtk_pinconf_bias_disable_set_rev1 ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc )
{
int err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_PULLEN ,
2018-09-08 19:07:27 +08:00
MTK_DISABLE ) ;
if ( err )
return err ;
return 0 ;
}
int mtk_pinconf_bias_disable_get_rev1 ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc , int * res )
{
int v , err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_get_value ( hw , desc , PINCTRL_PIN_REG_PULLEN , & v ) ;
2018-09-08 19:07:27 +08:00
if ( err )
return err ;
if ( v = = MTK_ENABLE )
return - EINVAL ;
* res = 1 ;
return 0 ;
}
int mtk_pinconf_bias_set_rev1 ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc , bool pullup )
{
int err , arg ;
arg = pullup ? MTK_PULLUP : MTK_PULLDOWN ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_PULLEN ,
2018-09-08 19:07:27 +08:00
MTK_ENABLE ) ;
if ( err )
return err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_PULLSEL , arg ) ;
2018-09-08 19:07:27 +08:00
if ( err )
return err ;
return 0 ;
}
int mtk_pinconf_bias_get_rev1 ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc , bool pullup ,
int * res )
{
int err , v ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_get_value ( hw , desc , PINCTRL_PIN_REG_PULLEN , & v ) ;
2018-09-08 19:07:27 +08:00
if ( err )
return err ;
if ( v = = MTK_DISABLE )
return - EINVAL ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_get_value ( hw , desc , PINCTRL_PIN_REG_PULLSEL , & v ) ;
2018-09-08 19:07:27 +08:00
if ( err )
return err ;
if ( pullup ^ ( v = = MTK_PULLUP ) )
return - EINVAL ;
* res = 1 ;
return 0 ;
}
2018-09-08 19:07:22 +08:00
/* Revision 0 */
int mtk_pinconf_drive_set ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc , u32 arg )
{
const struct mtk_drive_desc * tb ;
int err = - ENOTSUPP ;
tb = & mtk_drive [ desc - > drv_n ] ;
/* 4mA when (e8, e4) = (0, 0)
* 8 mA when ( e8 , e4 ) = ( 0 , 1 )
* 12 mA when ( e8 , e4 ) = ( 1 , 0 )
* 16 mA when ( e8 , e4 ) = ( 1 , 1 )
*/
if ( ( arg > = tb - > min & & arg < = tb - > max ) & & ! ( arg % tb - > step ) ) {
arg = ( arg / tb - > step - 1 ) * tb - > scal ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_E4 ,
2018-09-08 19:07:22 +08:00
arg & 0x1 ) ;
if ( err )
return err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_E8 ,
2018-09-08 19:07:22 +08:00
( arg & 0x2 ) > > 1 ) ;
if ( err )
return err ;
}
return err ;
}
int mtk_pinconf_drive_get ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc , int * val )
{
const struct mtk_drive_desc * tb ;
int err , val1 , val2 ;
tb = & mtk_drive [ desc - > drv_n ] ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_get_value ( hw , desc , PINCTRL_PIN_REG_E4 , & val1 ) ;
2018-09-08 19:07:22 +08:00
if ( err )
return err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_get_value ( hw , desc , PINCTRL_PIN_REG_E8 , & val2 ) ;
2018-09-08 19:07:22 +08:00
if ( err )
return err ;
/* 4mA when (e8, e4) = (0, 0); 8mA when (e8, e4) = (0, 1)
* 12 mA when ( e8 , e4 ) = ( 1 , 0 ) ; 16 mA when ( e8 , e4 ) = ( 1 , 1 )
*/
* val = ( ( ( val2 < < 1 ) + val1 ) / tb - > scal + 1 ) * tb - > step ;
return 0 ;
}
2018-09-08 19:07:23 +08:00
/* Revision 1 */
int mtk_pinconf_drive_set_rev1 ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc , u32 arg )
{
const struct mtk_drive_desc * tb ;
int err = - ENOTSUPP ;
tb = & mtk_drive [ desc - > drv_n ] ;
if ( ( arg > = tb - > min & & arg < = tb - > max ) & & ! ( arg % tb - > step ) ) {
arg = ( arg / tb - > step - 1 ) * tb - > scal ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_DRV ,
2018-09-08 19:07:23 +08:00
arg ) ;
if ( err )
return err ;
}
return err ;
}
int mtk_pinconf_drive_get_rev1 ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc , int * val )
{
const struct mtk_drive_desc * tb ;
int err , val1 ;
tb = & mtk_drive [ desc - > drv_n ] ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_get_value ( hw , desc , PINCTRL_PIN_REG_DRV , & val1 ) ;
2018-09-08 19:07:23 +08:00
if ( err )
return err ;
* val = ( ( val1 & 0x7 ) / tb - > scal + 1 ) * tb - > step ;
return 0 ;
}
2018-09-08 19:07:25 +08:00
int mtk_pinconf_adv_pull_set ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc , bool pullup ,
u32 arg )
{
int err ;
/* 10K off & 50K (75K) off, when (R0, R1) = (0, 0);
* 10 K off & 50 K ( 75 K ) on , when ( R0 , R1 ) = ( 0 , 1 ) ;
* 10 K on & 50 K ( 75 K ) off , when ( R0 , R1 ) = ( 1 , 0 ) ;
* 10 K on & 50 K ( 75 K ) on , when ( R0 , R1 ) = ( 1 , 1 )
*/
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_R0 , arg & 1 ) ;
2018-09-08 19:07:25 +08:00
if ( err )
return 0 ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_R1 ,
2018-09-08 19:07:25 +08:00
! ! ( arg & 2 ) ) ;
if ( err )
return 0 ;
arg = pullup ? 0 : 1 ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_set_value ( hw , desc , PINCTRL_PIN_REG_PUPD , arg ) ;
2018-09-08 19:07:25 +08:00
return err ;
}
int mtk_pinconf_adv_pull_get ( struct mtk_pinctrl * hw ,
const struct mtk_pin_desc * desc , bool pullup ,
u32 * val )
{
u32 t , t2 ;
int err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_get_value ( hw , desc , PINCTRL_PIN_REG_PUPD , & t ) ;
2018-09-08 19:07:25 +08:00
if ( err )
return err ;
/* t == 0 supposes PULLUP for the customized PULL setup */
if ( pullup ^ ! t )
return - EINVAL ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_get_value ( hw , desc , PINCTRL_PIN_REG_R0 , & t ) ;
2018-09-08 19:07:25 +08:00
if ( err )
return err ;
2018-09-08 19:07:29 +08:00
err = mtk_hw_get_value ( hw , desc , PINCTRL_PIN_REG_R1 , & t2 ) ;
2018-09-08 19:07:25 +08:00
if ( err )
return err ;
* val = ( t | t2 < < 1 ) & 0x7 ;
return 0 ;
}