2018-06-29 15:36:34 +03:00
// SPDX-License-Identifier: GPL-2.0
2015-03-30 17:31:49 +03:00
/*
* Intel pinctrl / GPIO core driver .
*
* Copyright ( C ) 2015 , Intel Corporation
* Authors : Mathias Nyman < mathias . nyman @ linux . intel . com >
* Mika Westerberg < mika . westerberg @ linux . intel . com >
*/
2018-08-30 19:27:36 +03:00
# include <linux/acpi.h>
2015-03-30 17:31:49 +03:00
# include <linux/gpio/driver.h>
2019-10-25 12:10:28 +03:00
# include <linux/interrupt.h>
2017-01-27 13:07:14 +03:00
# include <linux/log2.h>
2019-08-07 16:41:50 +03:00
# include <linux/module.h>
2015-03-30 17:31:49 +03:00
# include <linux/platform_device.h>
2018-08-30 19:27:36 +03:00
# include <linux/property.h>
2019-08-07 16:41:50 +03:00
# include <linux/time.h>
2018-08-30 19:27:36 +03:00
2015-03-30 17:31:49 +03:00
# include <linux/pinctrl/pinctrl.h>
# include <linux/pinctrl/pinmux.h>
# include <linux/pinctrl/pinconf.h>
# include <linux/pinctrl/pinconf-generic.h>
2016-10-10 16:39:31 +03:00
# include "../core.h"
2015-03-30 17:31:49 +03:00
# include "pinctrl-intel.h"
/* Offset from regs */
2017-01-27 13:07:14 +03:00
# define REVID 0x000
# define REVID_SHIFT 16
# define REVID_MASK GENMASK(31, 16)
2021-01-08 16:40:05 +03:00
# define CAPLIST 0x004
# define CAPLIST_ID_SHIFT 16
# define CAPLIST_ID_MASK GENMASK(23, 16)
# define CAPLIST_ID_GPIO_HW_INFO 1
# define CAPLIST_ID_PWM 2
# define CAPLIST_ID_BLINK 3
# define CAPLIST_ID_EXP 4
# define CAPLIST_NEXT_SHIFT 0
# define CAPLIST_NEXT_MASK GENMASK(15, 0)
2015-03-30 17:31:49 +03:00
# define PADBAR 0x00c
# define PADOWN_BITS 4
# define PADOWN_SHIFT(p) ((p) % 8 * PADOWN_BITS)
2019-04-01 15:06:44 +03:00
# define PADOWN_MASK(p) (GENMASK(3, 0) << PADOWN_SHIFT(p))
2015-11-30 14:20:16 +03:00
# define PADOWN_GPP(p) ((p) / 8)
2015-03-30 17:31:49 +03:00
/* Offset from pad_regs */
# define PADCFG0 0x000
# define PADCFG0_RXEVCFG_SHIFT 25
2019-04-01 15:06:44 +03:00
# define PADCFG0_RXEVCFG_MASK GENMASK(26, 25)
2015-03-30 17:31:49 +03:00
# define PADCFG0_RXEVCFG_LEVEL 0
# define PADCFG0_RXEVCFG_EDGE 1
# define PADCFG0_RXEVCFG_DISABLED 2
# define PADCFG0_RXEVCFG_EDGE_BOTH 3
2017-01-27 13:07:14 +03:00
# define PADCFG0_PREGFRXSEL BIT(24)
2015-03-30 17:31:49 +03:00
# define PADCFG0_RXINV BIT(23)
# define PADCFG0_GPIROUTIOXAPIC BIT(20)
# define PADCFG0_GPIROUTSCI BIT(19)
# define PADCFG0_GPIROUTSMI BIT(18)
# define PADCFG0_GPIROUTNMI BIT(17)
# define PADCFG0_PMODE_SHIFT 10
2019-04-01 15:06:44 +03:00
# define PADCFG0_PMODE_MASK GENMASK(13, 10)
2019-10-14 12:51:04 +03:00
# define PADCFG0_PMODE_GPIO 0
2015-03-30 17:31:49 +03:00
# define PADCFG0_GPIORXDIS BIT(9)
# define PADCFG0_GPIOTXDIS BIT(8)
# define PADCFG0_GPIORXSTATE BIT(1)
# define PADCFG0_GPIOTXSTATE BIT(0)
# define PADCFG1 0x004
# define PADCFG1_TERM_UP BIT(13)
# define PADCFG1_TERM_SHIFT 10
2019-04-01 15:06:44 +03:00
# define PADCFG1_TERM_MASK GENMASK(12, 10)
2020-10-14 13:46:37 +03:00
# define PADCFG1_TERM_20K BIT(2)
# define PADCFG1_TERM_5K BIT(1)
# define PADCFG1_TERM_1K BIT(0)
# define PADCFG1_TERM_833 (BIT(1) | BIT(0))
2015-03-30 17:31:49 +03:00
2017-01-27 13:07:14 +03:00
# define PADCFG2 0x008
# define PADCFG2_DEBEN BIT(0)
# define PADCFG2_DEBOUNCE_SHIFT 1
# define PADCFG2_DEBOUNCE_MASK GENMASK(4, 1)
2019-08-07 16:41:50 +03:00
# define DEBOUNCE_PERIOD_NSEC 31250
2017-01-27 13:07:14 +03:00
2015-03-30 17:31:49 +03:00
struct intel_pad_context {
u32 padcfg0 ;
u32 padcfg1 ;
2017-01-27 13:07:14 +03:00
u32 padcfg2 ;
2015-03-30 17:31:49 +03:00
} ;
struct intel_community_context {
u32 * intmask ;
2019-04-15 08:53:58 +03:00
u32 * hostown ;
2015-03-30 17:31:49 +03:00
} ;
# define pin_to_padno(c, p) ((p) - (c)->pin_base)
2017-06-06 16:18:17 +03:00
# define padgroup_offset(g, p) ((p) - (g)->base)
2015-03-30 17:31:49 +03:00
static struct intel_community * intel_get_community ( struct intel_pinctrl * pctrl ,
2018-09-26 17:50:26 +03:00
unsigned int pin )
2015-03-30 17:31:49 +03:00
{
struct intel_community * community ;
int i ;
for ( i = 0 ; i < pctrl - > ncommunities ; i + + ) {
community = & pctrl - > communities [ i ] ;
if ( pin > = community - > pin_base & &
pin < community - > pin_base + community - > npins )
return community ;
}
dev_warn ( pctrl - > dev , " failed to find community for pin %u \n " , pin ) ;
return NULL ;
}
2017-06-06 16:18:17 +03:00
static const struct intel_padgroup *
intel_community_get_padgroup ( const struct intel_community * community ,
2018-09-26 17:50:26 +03:00
unsigned int pin )
2017-06-06 16:18:17 +03:00
{
int i ;
for ( i = 0 ; i < community - > ngpps ; i + + ) {
const struct intel_padgroup * padgrp = & community - > gpps [ i ] ;
if ( pin > = padgrp - > base & & pin < padgrp - > base + padgrp - > size )
return padgrp ;
}
return NULL ;
}
2018-09-26 17:50:26 +03:00
static void __iomem * intel_get_padcfg ( struct intel_pinctrl * pctrl ,
unsigned int pin , unsigned int reg )
2015-03-30 17:31:49 +03:00
{
const struct intel_community * community ;
2018-09-26 17:50:26 +03:00
unsigned int padno ;
2017-01-27 13:07:14 +03:00
size_t nregs ;
2015-03-30 17:31:49 +03:00
community = intel_get_community ( pctrl , pin ) ;
if ( ! community )
return NULL ;
padno = pin_to_padno ( community , pin ) ;
2017-01-27 13:07:14 +03:00
nregs = ( community - > features & PINCTRL_FEATURE_DEBOUNCE ) ? 4 : 2 ;
2019-07-23 18:55:14 +03:00
if ( reg > = nregs * 4 )
2017-01-27 13:07:14 +03:00
return NULL ;
return community - > pad_regs + reg + padno * nregs * 4 ;
2015-03-30 17:31:49 +03:00
}
2018-09-26 17:50:26 +03:00
static bool intel_pad_owned_by_host ( struct intel_pinctrl * pctrl , unsigned int pin )
2015-03-30 17:31:49 +03:00
{
const struct intel_community * community ;
2017-06-06 16:18:17 +03:00
const struct intel_padgroup * padgrp ;
2018-09-26 17:50:26 +03:00
unsigned int gpp , offset , gpp_offset ;
2015-03-30 17:31:49 +03:00
void __iomem * padown ;
community = intel_get_community ( pctrl , pin ) ;
if ( ! community )
return false ;
if ( ! community - > padown_offset )
return true ;
2017-06-06 16:18:17 +03:00
padgrp = intel_community_get_padgroup ( community , pin ) ;
if ( ! padgrp )
return false ;
gpp_offset = padgroup_offset ( padgrp , pin ) ;
gpp = PADOWN_GPP ( gpp_offset ) ;
offset = community - > padown_offset + padgrp - > padown_num * 4 + gpp * 4 ;
2015-03-30 17:31:49 +03:00
padown = community - > regs + offset ;
2017-06-06 16:18:17 +03:00
return ! ( readl ( padown ) & PADOWN_MASK ( gpp_offset ) ) ;
2015-03-30 17:31:49 +03:00
}
2018-09-26 17:50:26 +03:00
static bool intel_pad_acpi_mode ( struct intel_pinctrl * pctrl , unsigned int pin )
2015-03-30 17:31:49 +03:00
{
const struct intel_community * community ;
2017-06-06 16:18:17 +03:00
const struct intel_padgroup * padgrp ;
2018-09-26 17:50:26 +03:00
unsigned int offset , gpp_offset ;
2015-03-30 17:31:49 +03:00
void __iomem * hostown ;
community = intel_get_community ( pctrl , pin ) ;
if ( ! community )
return true ;
if ( ! community - > hostown_offset )
return false ;
2017-06-06 16:18:17 +03:00
padgrp = intel_community_get_padgroup ( community , pin ) ;
if ( ! padgrp )
return true ;
gpp_offset = padgroup_offset ( padgrp , pin ) ;
offset = community - > hostown_offset + padgrp - > reg_num * 4 ;
2015-03-30 17:31:49 +03:00
hostown = community - > regs + offset ;
2017-06-06 16:18:17 +03:00
return ! ( readl ( hostown ) & BIT ( gpp_offset ) ) ;
2015-03-30 17:31:49 +03:00
}
2019-08-12 19:14:01 +03:00
/**
* enum - Locking variants of the pad configuration
*
* @ PAD_UNLOCKED : pad is fully controlled by the configuration registers
* @ PAD_LOCKED : pad configuration registers , except TX state , are locked
* @ PAD_LOCKED_TX : pad configuration TX state is locked
* @ PAD_LOCKED_FULL : pad configuration registers are locked completely
*
* Locking is considered as read - only mode for corresponding registers and
* their respective fields . That said , TX state bit is locked separately from
* the main locking scheme .
*/
enum {
PAD_UNLOCKED = 0 ,
PAD_LOCKED = 1 ,
PAD_LOCKED_TX = 2 ,
PAD_LOCKED_FULL = PAD_LOCKED | PAD_LOCKED_TX ,
} ;
static int intel_pad_locked ( struct intel_pinctrl * pctrl , unsigned int pin )
2015-03-30 17:31:49 +03:00
{
struct intel_community * community ;
2017-06-06 16:18:17 +03:00
const struct intel_padgroup * padgrp ;
2018-09-26 17:50:26 +03:00
unsigned int offset , gpp_offset ;
2015-03-30 17:31:49 +03:00
u32 value ;
2019-08-12 19:14:01 +03:00
int ret = PAD_UNLOCKED ;
2015-03-30 17:31:49 +03:00
community = intel_get_community ( pctrl , pin ) ;
if ( ! community )
2019-08-12 19:14:01 +03:00
return PAD_LOCKED_FULL ;
2015-03-30 17:31:49 +03:00
if ( ! community - > padcfglock_offset )
2019-08-12 19:14:01 +03:00
return PAD_UNLOCKED ;
2015-03-30 17:31:49 +03:00
2017-06-06 16:18:17 +03:00
padgrp = intel_community_get_padgroup ( community , pin ) ;
if ( ! padgrp )
2019-08-12 19:14:01 +03:00
return PAD_LOCKED_FULL ;
2017-06-06 16:18:17 +03:00
gpp_offset = padgroup_offset ( padgrp , pin ) ;
2015-03-30 17:31:49 +03:00
/*
* If PADCFGLOCK and PADCFGLOCKTX bits are both clear for this pad ,
* the pad is considered unlocked . Any other case means that it is
2019-08-12 19:14:01 +03:00
* either fully or partially locked .
2015-03-30 17:31:49 +03:00
*/
2019-08-12 19:14:01 +03:00
offset = community - > padcfglock_offset + 0 + padgrp - > reg_num * 8 ;
2015-03-30 17:31:49 +03:00
value = readl ( community - > regs + offset ) ;
2017-06-06 16:18:17 +03:00
if ( value & BIT ( gpp_offset ) )
2019-08-12 19:14:01 +03:00
ret | = PAD_LOCKED ;
2015-03-30 17:31:49 +03:00
2017-06-06 16:18:17 +03:00
offset = community - > padcfglock_offset + 4 + padgrp - > reg_num * 8 ;
2015-03-30 17:31:49 +03:00
value = readl ( community - > regs + offset ) ;
2017-06-06 16:18:17 +03:00
if ( value & BIT ( gpp_offset ) )
2019-08-12 19:14:01 +03:00
ret | = PAD_LOCKED_TX ;
2015-03-30 17:31:49 +03:00
2019-08-12 19:14:01 +03:00
return ret ;
}
static bool intel_pad_is_unlocked ( struct intel_pinctrl * pctrl , unsigned int pin )
{
return ( intel_pad_locked ( pctrl , pin ) & PAD_LOCKED ) = = PAD_UNLOCKED ;
2015-03-30 17:31:49 +03:00
}
2018-09-26 17:50:26 +03:00
static bool intel_pad_usable ( struct intel_pinctrl * pctrl , unsigned int pin )
2015-03-30 17:31:49 +03:00
{
2019-08-12 19:14:01 +03:00
return intel_pad_owned_by_host ( pctrl , pin ) & & intel_pad_is_unlocked ( pctrl , pin ) ;
2015-03-30 17:31:49 +03:00
}
static int intel_get_groups_count ( struct pinctrl_dev * pctldev )
{
struct intel_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctrl - > soc - > ngroups ;
}
static const char * intel_get_group_name ( struct pinctrl_dev * pctldev ,
2018-09-26 17:50:26 +03:00
unsigned int group )
2015-03-30 17:31:49 +03:00
{
struct intel_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctrl - > soc - > groups [ group ] . name ;
}
2018-09-26 17:50:26 +03:00
static int intel_get_group_pins ( struct pinctrl_dev * pctldev , unsigned int group ,
const unsigned int * * pins , unsigned int * npins )
2015-03-30 17:31:49 +03:00
{
struct intel_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
* pins = pctrl - > soc - > groups [ group ] . pins ;
* npins = pctrl - > soc - > groups [ group ] . npins ;
return 0 ;
}
static void intel_pin_dbg_show ( struct pinctrl_dev * pctldev , struct seq_file * s ,
2018-09-26 17:50:26 +03:00
unsigned int pin )
2015-03-30 17:31:49 +03:00
{
struct intel_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
2017-01-27 13:07:14 +03:00
void __iomem * padcfg ;
2015-03-30 17:31:49 +03:00
u32 cfg0 , cfg1 , mode ;
2019-08-12 19:14:01 +03:00
int locked ;
bool acpi ;
2015-03-30 17:31:49 +03:00
if ( ! intel_pad_owned_by_host ( pctrl , pin ) ) {
seq_puts ( s , " not available " ) ;
return ;
}
cfg0 = readl ( intel_get_padcfg ( pctrl , pin , PADCFG0 ) ) ;
cfg1 = readl ( intel_get_padcfg ( pctrl , pin , PADCFG1 ) ) ;
mode = ( cfg0 & PADCFG0_PMODE_MASK ) > > PADCFG0_PMODE_SHIFT ;
2019-10-14 12:51:04 +03:00
if ( mode = = PADCFG0_PMODE_GPIO )
2015-03-30 17:31:49 +03:00
seq_puts ( s , " GPIO " ) ;
else
seq_printf ( s , " mode %d " , mode ) ;
seq_printf ( s , " 0x%08x 0x%08x " , cfg0 , cfg1 ) ;
2017-01-27 13:07:14 +03:00
/* Dump the additional PADCFG registers if available */
padcfg = intel_get_padcfg ( pctrl , pin , PADCFG2 ) ;
if ( padcfg )
seq_printf ( s , " 0x%08x " , readl ( padcfg ) ) ;
2015-03-30 17:31:49 +03:00
locked = intel_pad_locked ( pctrl , pin ) ;
2015-10-21 13:08:44 +03:00
acpi = intel_pad_acpi_mode ( pctrl , pin ) ;
2015-03-30 17:31:49 +03:00
if ( locked | | acpi ) {
seq_puts ( s , " [ " ) ;
2019-08-12 19:14:01 +03:00
if ( locked )
2015-03-30 17:31:49 +03:00
seq_puts ( s , " LOCKED " ) ;
2019-08-12 19:14:01 +03:00
if ( ( locked & PAD_LOCKED_FULL ) = = PAD_LOCKED_TX )
seq_puts ( s , " tx " ) ;
else if ( ( locked & PAD_LOCKED_FULL ) = = PAD_LOCKED_FULL )
seq_puts ( s , " full " ) ;
if ( locked & & acpi )
seq_puts ( s , " , " ) ;
2015-03-30 17:31:49 +03:00
if ( acpi )
seq_puts ( s , " ACPI " ) ;
seq_puts ( s , " ] " ) ;
}
}
static const struct pinctrl_ops intel_pinctrl_ops = {
. get_groups_count = intel_get_groups_count ,
. get_group_name = intel_get_group_name ,
. get_group_pins = intel_get_group_pins ,
. pin_dbg_show = intel_pin_dbg_show ,
} ;
static int intel_get_functions_count ( struct pinctrl_dev * pctldev )
{
struct intel_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctrl - > soc - > nfunctions ;
}
static const char * intel_get_function_name ( struct pinctrl_dev * pctldev ,
2018-09-26 17:50:26 +03:00
unsigned int function )
2015-03-30 17:31:49 +03:00
{
struct intel_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctrl - > soc - > functions [ function ] . name ;
}
static int intel_get_function_groups ( struct pinctrl_dev * pctldev ,
2018-09-26 17:50:26 +03:00
unsigned int function ,
2015-03-30 17:31:49 +03:00
const char * const * * groups ,
2018-09-26 17:50:26 +03:00
unsigned int * const ngroups )
2015-03-30 17:31:49 +03:00
{
struct intel_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
* groups = pctrl - > soc - > functions [ function ] . groups ;
* ngroups = pctrl - > soc - > functions [ function ] . ngroups ;
return 0 ;
}
2018-09-26 17:50:26 +03:00
static int intel_pinmux_set_mux ( struct pinctrl_dev * pctldev ,
unsigned int function , unsigned int group )
2015-03-30 17:31:49 +03:00
{
struct intel_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
const struct intel_pingroup * grp = & pctrl - > soc - > groups [ group ] ;
unsigned long flags ;
int i ;
2016-06-16 11:25:36 +03:00
raw_spin_lock_irqsave ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
/*
* All pins in the groups needs to be accessible and writable
* before we can enable the mux for this group .
*/
for ( i = 0 ; i < grp - > npins ; i + + ) {
if ( ! intel_pad_usable ( pctrl , grp - > pins [ i ] ) ) {
2016-06-16 11:25:36 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
return - EBUSY ;
}
}
/* Now enable the mux setting for each pin in the group */
for ( i = 0 ; i < grp - > npins ; i + + ) {
void __iomem * padcfg0 ;
u32 value ;
padcfg0 = intel_get_padcfg ( pctrl , grp - > pins [ i ] , PADCFG0 ) ;
value = readl ( padcfg0 ) ;
value & = ~ PADCFG0_PMODE_MASK ;
2017-06-06 16:18:18 +03:00
if ( grp - > modes )
value | = grp - > modes [ i ] < < PADCFG0_PMODE_SHIFT ;
else
value | = grp - > mode < < PADCFG0_PMODE_SHIFT ;
2015-03-30 17:31:49 +03:00
writel ( value , padcfg0 ) ;
}
2016-06-16 11:25:36 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
return 0 ;
}
2017-01-02 15:07:22 +03:00
static void __intel_gpio_set_direction ( void __iomem * padcfg0 , bool input )
{
u32 value ;
value = readl ( padcfg0 ) ;
if ( input ) {
value & = ~ PADCFG0_GPIORXDIS ;
value | = PADCFG0_GPIOTXDIS ;
} else {
value & = ~ PADCFG0_GPIOTXDIS ;
value | = PADCFG0_GPIORXDIS ;
}
writel ( value , padcfg0 ) ;
}
2019-10-14 12:51:04 +03:00
static int intel_gpio_get_gpio_mode ( void __iomem * padcfg0 )
{
return ( readl ( padcfg0 ) & PADCFG0_PMODE_MASK ) > > PADCFG0_PMODE_SHIFT ;
}
2017-11-29 16:25:44 +03:00
static void intel_gpio_set_gpio_mode ( void __iomem * padcfg0 )
{
u32 value ;
2020-06-12 17:49:54 +03:00
value = readl ( padcfg0 ) ;
2017-11-29 16:25:44 +03:00
/* Put the pad into GPIO mode */
2020-06-12 17:49:54 +03:00
value & = ~ PADCFG0_PMODE_MASK ;
value | = PADCFG0_PMODE_GPIO ;
/* Disable input and output buffers */
2020-12-08 21:24:03 +03:00
value | = PADCFG0_GPIORXDIS ;
value | = PADCFG0_GPIOTXDIS ;
2020-06-12 17:49:54 +03:00
2017-11-29 16:25:44 +03:00
/* Disable SCI/SMI/NMI generation */
value & = ~ ( PADCFG0_GPIROUTIOXAPIC | PADCFG0_GPIROUTSCI ) ;
value & = ~ ( PADCFG0_GPIROUTSMI | PADCFG0_GPIROUTNMI ) ;
2020-06-12 17:49:54 +03:00
2017-11-29 16:25:44 +03:00
writel ( value , padcfg0 ) ;
}
2015-03-30 17:31:49 +03:00
static int intel_gpio_request_enable ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range ,
2018-09-26 17:50:26 +03:00
unsigned int pin )
2015-03-30 17:31:49 +03:00
{
struct intel_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
void __iomem * padcfg0 ;
unsigned long flags ;
2020-06-12 17:49:55 +03:00
padcfg0 = intel_get_padcfg ( pctrl , pin , PADCFG0 ) ;
2016-06-16 11:25:36 +03:00
raw_spin_lock_irqsave ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
2019-08-12 19:14:01 +03:00
if ( ! intel_pad_owned_by_host ( pctrl , pin ) ) {
2016-06-16 11:25:36 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
return - EBUSY ;
}
2019-08-12 19:14:01 +03:00
if ( ! intel_pad_is_unlocked ( pctrl , pin ) ) {
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
return 0 ;
}
2019-10-14 12:51:04 +03:00
/*
* If pin is already configured in GPIO mode , we assume that
* firmware provides correct settings . In such case we avoid
* potential glitches on the pin . Otherwise , for the pin in
* alternative mode , consumer has to supply respective flags .
*/
if ( intel_gpio_get_gpio_mode ( padcfg0 ) = = PADCFG0_PMODE_GPIO ) {
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
return 0 ;
}
2017-11-29 16:25:44 +03:00
intel_gpio_set_gpio_mode ( padcfg0 ) ;
2019-10-14 12:51:04 +03:00
2017-01-02 15:07:22 +03:00
/* Disable TX buffer and enable RX (this will be input) */
__intel_gpio_set_direction ( padcfg0 , true ) ;
2016-06-16 11:25:36 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
return 0 ;
}
static int intel_gpio_set_direction ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range ,
2018-09-26 17:50:26 +03:00
unsigned int pin , bool input )
2015-03-30 17:31:49 +03:00
{
struct intel_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
void __iomem * padcfg0 ;
unsigned long flags ;
padcfg0 = intel_get_padcfg ( pctrl , pin , PADCFG0 ) ;
2020-06-12 17:49:55 +03:00
raw_spin_lock_irqsave ( & pctrl - > lock , flags ) ;
__intel_gpio_set_direction ( padcfg0 , input ) ;
2016-06-16 11:25:36 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
return 0 ;
}
static const struct pinmux_ops intel_pinmux_ops = {
. get_functions_count = intel_get_functions_count ,
. get_function_name = intel_get_function_name ,
. get_function_groups = intel_get_function_groups ,
. set_mux = intel_pinmux_set_mux ,
. gpio_request_enable = intel_gpio_request_enable ,
. gpio_set_direction = intel_gpio_set_direction ,
} ;
2020-06-12 17:49:59 +03:00
static int intel_config_get_pull ( struct intel_pinctrl * pctrl , unsigned int pin ,
enum pin_config_param param , u32 * arg )
2015-03-30 17:31:49 +03:00
{
2017-01-27 13:07:15 +03:00
const struct intel_community * community ;
2020-06-12 17:49:59 +03:00
void __iomem * padcfg1 ;
2020-06-12 17:50:00 +03:00
unsigned long flags ;
2015-03-30 17:31:49 +03:00
u32 value , term ;
2017-01-27 13:07:15 +03:00
community = intel_get_community ( pctrl , pin ) ;
2020-06-12 17:49:59 +03:00
padcfg1 = intel_get_padcfg ( pctrl , pin , PADCFG1 ) ;
2020-06-12 17:50:00 +03:00
raw_spin_lock_irqsave ( & pctrl - > lock , flags ) ;
2020-06-12 17:49:59 +03:00
value = readl ( padcfg1 ) ;
2020-06-12 17:50:00 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2020-06-12 17:49:59 +03:00
2015-03-30 17:31:49 +03:00
term = ( value & PADCFG1_TERM_MASK ) > > PADCFG1_TERM_SHIFT ;
switch ( param ) {
case PIN_CONFIG_BIAS_DISABLE :
if ( term )
return - EINVAL ;
break ;
case PIN_CONFIG_BIAS_PULL_UP :
if ( ! term | | ! ( value & PADCFG1_TERM_UP ) )
return - EINVAL ;
switch ( term ) {
2020-10-14 13:46:37 +03:00
case PADCFG1_TERM_833 :
* arg = 833 ;
break ;
2015-03-30 17:31:49 +03:00
case PADCFG1_TERM_1K :
2020-06-12 17:49:59 +03:00
* arg = 1000 ;
2015-03-30 17:31:49 +03:00
break ;
case PADCFG1_TERM_5K :
2020-06-12 17:49:59 +03:00
* arg = 5000 ;
2015-03-30 17:31:49 +03:00
break ;
case PADCFG1_TERM_20K :
2020-06-12 17:49:59 +03:00
* arg = 20000 ;
2015-03-30 17:31:49 +03:00
break ;
}
break ;
case PIN_CONFIG_BIAS_PULL_DOWN :
if ( ! term | | value & PADCFG1_TERM_UP )
return - EINVAL ;
switch ( term ) {
2020-10-14 13:46:37 +03:00
case PADCFG1_TERM_833 :
if ( ! ( community - > features & PINCTRL_FEATURE_1K_PD ) )
return - EINVAL ;
* arg = 833 ;
break ;
2017-01-27 13:07:15 +03:00
case PADCFG1_TERM_1K :
if ( ! ( community - > features & PINCTRL_FEATURE_1K_PD ) )
return - EINVAL ;
2020-06-12 17:49:59 +03:00
* arg = 1000 ;
2017-01-27 13:07:15 +03:00
break ;
2015-03-30 17:31:49 +03:00
case PADCFG1_TERM_5K :
2020-06-12 17:49:59 +03:00
* arg = 5000 ;
2015-03-30 17:31:49 +03:00
break ;
case PADCFG1_TERM_20K :
2020-06-12 17:49:59 +03:00
* arg = 20000 ;
2015-03-30 17:31:49 +03:00
break ;
}
break ;
2020-06-12 17:49:59 +03:00
default :
return - EINVAL ;
}
return 0 ;
}
static int intel_config_get_debounce ( struct intel_pinctrl * pctrl , unsigned int pin ,
enum pin_config_param param , u32 * arg )
{
void __iomem * padcfg2 ;
2020-06-12 17:50:00 +03:00
unsigned long flags ;
2020-06-12 17:49:59 +03:00
unsigned long v ;
u32 value2 ;
2017-01-27 13:07:14 +03:00
2020-06-12 17:49:59 +03:00
padcfg2 = intel_get_padcfg ( pctrl , pin , PADCFG2 ) ;
if ( ! padcfg2 )
return - ENOTSUPP ;
2017-01-27 13:07:14 +03:00
2020-06-12 17:50:00 +03:00
raw_spin_lock_irqsave ( & pctrl - > lock , flags ) ;
2020-06-12 17:49:59 +03:00
value2 = readl ( padcfg2 ) ;
2020-06-12 17:50:00 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2020-06-12 17:49:59 +03:00
if ( ! ( value2 & PADCFG2_DEBEN ) )
return - EINVAL ;
2017-01-27 13:07:14 +03:00
2020-06-12 17:49:59 +03:00
v = ( value2 & PADCFG2_DEBOUNCE_MASK ) > > PADCFG2_DEBOUNCE_SHIFT ;
* arg = BIT ( v ) * DEBOUNCE_PERIOD_NSEC / NSEC_PER_USEC ;
2017-01-27 13:07:14 +03:00
2020-06-12 17:49:59 +03:00
return 0 ;
}
static int intel_config_get ( struct pinctrl_dev * pctldev , unsigned int pin ,
unsigned long * config )
{
struct intel_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
enum pin_config_param param = pinconf_to_config_param ( * config ) ;
u32 arg = 0 ;
int ret ;
if ( ! intel_pad_owned_by_host ( pctrl , pin ) )
return - ENOTSUPP ;
switch ( param ) {
case PIN_CONFIG_BIAS_DISABLE :
case PIN_CONFIG_BIAS_PULL_UP :
case PIN_CONFIG_BIAS_PULL_DOWN :
ret = intel_config_get_pull ( pctrl , pin , param , & arg ) ;
if ( ret )
return ret ;
break ;
case PIN_CONFIG_INPUT_DEBOUNCE :
ret = intel_config_get_debounce ( pctrl , pin , param , & arg ) ;
if ( ret )
return ret ;
2017-01-27 13:07:14 +03:00
break ;
2015-03-30 17:31:49 +03:00
default :
return - ENOTSUPP ;
}
* config = pinconf_to_config_packed ( param , arg ) ;
return 0 ;
}
2018-09-26 17:50:26 +03:00
static int intel_config_set_pull ( struct intel_pinctrl * pctrl , unsigned int pin ,
2015-03-30 17:31:49 +03:00
unsigned long config )
{
2018-09-26 17:50:26 +03:00
unsigned int param = pinconf_to_config_param ( config ) ;
unsigned int arg = pinconf_to_config_argument ( config ) ;
2017-01-27 13:07:15 +03:00
const struct intel_community * community ;
2015-03-30 17:31:49 +03:00
void __iomem * padcfg1 ;
unsigned long flags ;
int ret = 0 ;
u32 value ;
2017-01-27 13:07:15 +03:00
community = intel_get_community ( pctrl , pin ) ;
2015-03-30 17:31:49 +03:00
padcfg1 = intel_get_padcfg ( pctrl , pin , PADCFG1 ) ;
2020-06-12 17:49:55 +03:00
raw_spin_lock_irqsave ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
value = readl ( padcfg1 ) ;
switch ( param ) {
case PIN_CONFIG_BIAS_DISABLE :
value & = ~ ( PADCFG1_TERM_MASK | PADCFG1_TERM_UP ) ;
break ;
case PIN_CONFIG_BIAS_PULL_UP :
value & = ~ PADCFG1_TERM_MASK ;
value | = PADCFG1_TERM_UP ;
2020-10-14 13:46:38 +03:00
/* Set default strength value in case none is given */
if ( arg = = 1 )
arg = 5000 ;
2015-03-30 17:31:49 +03:00
switch ( arg ) {
case 20000 :
value | = PADCFG1_TERM_20K < < PADCFG1_TERM_SHIFT ;
break ;
case 5000 :
value | = PADCFG1_TERM_5K < < PADCFG1_TERM_SHIFT ;
break ;
case 1000 :
value | = PADCFG1_TERM_1K < < PADCFG1_TERM_SHIFT ;
break ;
2020-10-14 13:46:37 +03:00
case 833 :
value | = PADCFG1_TERM_833 < < PADCFG1_TERM_SHIFT ;
break ;
2015-03-30 17:31:49 +03:00
default :
ret = - EINVAL ;
}
break ;
case PIN_CONFIG_BIAS_PULL_DOWN :
value & = ~ ( PADCFG1_TERM_UP | PADCFG1_TERM_MASK ) ;
2020-10-14 13:46:38 +03:00
/* Set default strength value in case none is given */
if ( arg = = 1 )
arg = 5000 ;
2015-03-30 17:31:49 +03:00
switch ( arg ) {
case 20000 :
value | = PADCFG1_TERM_20K < < PADCFG1_TERM_SHIFT ;
break ;
case 5000 :
value | = PADCFG1_TERM_5K < < PADCFG1_TERM_SHIFT ;
break ;
2017-01-27 13:07:15 +03:00
case 1000 :
2017-02-07 16:20:08 +03:00
if ( ! ( community - > features & PINCTRL_FEATURE_1K_PD ) ) {
ret = - EINVAL ;
break ;
}
2017-01-27 13:07:15 +03:00
value | = PADCFG1_TERM_1K < < PADCFG1_TERM_SHIFT ;
break ;
2020-10-14 13:46:37 +03:00
case 833 :
if ( ! ( community - > features & PINCTRL_FEATURE_1K_PD ) ) {
ret = - EINVAL ;
break ;
}
value | = PADCFG1_TERM_833 < < PADCFG1_TERM_SHIFT ;
break ;
2015-03-30 17:31:49 +03:00
default :
ret = - EINVAL ;
}
break ;
}
if ( ! ret )
writel ( value , padcfg1 ) ;
2016-06-16 11:25:36 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
return ret ;
}
2018-09-26 17:50:26 +03:00
static int intel_config_set_debounce ( struct intel_pinctrl * pctrl ,
unsigned int pin , unsigned int debounce )
2017-01-27 13:07:14 +03:00
{
void __iomem * padcfg0 , * padcfg2 ;
unsigned long flags ;
u32 value0 , value2 ;
padcfg2 = intel_get_padcfg ( pctrl , pin , PADCFG2 ) ;
if ( ! padcfg2 )
return - ENOTSUPP ;
padcfg0 = intel_get_padcfg ( pctrl , pin , PADCFG0 ) ;
raw_spin_lock_irqsave ( & pctrl - > lock , flags ) ;
value0 = readl ( padcfg0 ) ;
value2 = readl ( padcfg2 ) ;
/* Disable glitch filter and debouncer */
value0 & = ~ PADCFG0_PREGFRXSEL ;
value2 & = ~ ( PADCFG2_DEBEN | PADCFG2_DEBOUNCE_MASK ) ;
if ( debounce ) {
unsigned long v ;
2019-08-07 16:41:50 +03:00
v = order_base_2 ( debounce * NSEC_PER_USEC / DEBOUNCE_PERIOD_NSEC ) ;
2017-01-27 13:07:14 +03:00
if ( v < 3 | | v > 15 ) {
2020-06-12 17:49:58 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
return - EINVAL ;
2017-01-27 13:07:14 +03:00
}
2020-06-12 17:49:57 +03:00
/* Enable glitch filter and debouncer */
value0 | = PADCFG0_PREGFRXSEL ;
value2 | = v < < PADCFG2_DEBOUNCE_SHIFT ;
value2 | = PADCFG2_DEBEN ;
2017-01-27 13:07:14 +03:00
}
writel ( value0 , padcfg0 ) ;
writel ( value2 , padcfg2 ) ;
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2020-06-12 17:49:58 +03:00
return 0 ;
2017-01-27 13:07:14 +03:00
}
2018-09-26 17:50:26 +03:00
static int intel_config_set ( struct pinctrl_dev * pctldev , unsigned int pin ,
unsigned long * configs , unsigned int nconfigs )
2015-03-30 17:31:49 +03:00
{
struct intel_pinctrl * pctrl = pinctrl_dev_get_drvdata ( pctldev ) ;
int i , ret ;
if ( ! intel_pad_usable ( pctrl , pin ) )
return - ENOTSUPP ;
for ( i = 0 ; i < nconfigs ; i + + ) {
switch ( pinconf_to_config_param ( configs [ i ] ) ) {
case PIN_CONFIG_BIAS_DISABLE :
case PIN_CONFIG_BIAS_PULL_UP :
case PIN_CONFIG_BIAS_PULL_DOWN :
ret = intel_config_set_pull ( pctrl , pin , configs [ i ] ) ;
if ( ret )
return ret ;
break ;
2017-01-27 13:07:14 +03:00
case PIN_CONFIG_INPUT_DEBOUNCE :
ret = intel_config_set_debounce ( pctrl , pin ,
pinconf_to_config_argument ( configs [ i ] ) ) ;
if ( ret )
return ret ;
break ;
2015-03-30 17:31:49 +03:00
default :
return - ENOTSUPP ;
}
}
return 0 ;
}
static const struct pinconf_ops intel_pinconf_ops = {
. is_generic = true ,
. pin_config_get = intel_config_get ,
. pin_config_set = intel_config_set ,
} ;
static const struct pinctrl_desc intel_pinctrl_desc = {
. pctlops = & intel_pinctrl_ops ,
. pmxops = & intel_pinmux_ops ,
. confops = & intel_pinconf_ops ,
. owner = THIS_MODULE ,
} ;
2018-09-18 18:36:21 +03:00
/**
* intel_gpio_to_pin ( ) - Translate from GPIO offset to pin number
* @ pctrl : Pinctrl structure
* @ offset : GPIO offset from gpiolib
2018-09-26 17:43:17 +03:00
* @ community : Community is filled here if not % NULL
2018-09-18 18:36:21 +03:00
* @ padgrp : Pad group is filled here if not % NULL
*
* When coming through gpiolib irqchip , the GPIO offset is not
* automatically translated to pinctrl pin number . This function can be
* used to find out the corresponding pinctrl pin .
*/
2018-09-26 17:50:26 +03:00
static int intel_gpio_to_pin ( struct intel_pinctrl * pctrl , unsigned int offset ,
2018-09-18 18:36:21 +03:00
const struct intel_community * * community ,
const struct intel_padgroup * * padgrp )
{
int i ;
for ( i = 0 ; i < pctrl - > ncommunities ; i + + ) {
const struct intel_community * comm = & pctrl - > communities [ i ] ;
int j ;
for ( j = 0 ; j < comm - > ngpps ; j + + ) {
const struct intel_padgroup * pgrp = & comm - > gpps [ j ] ;
2020-04-13 14:18:20 +03:00
if ( pgrp - > gpio_base = = INTEL_GPIO_BASE_NOMAP )
2018-09-18 18:36:21 +03:00
continue ;
if ( offset > = pgrp - > gpio_base & &
offset < pgrp - > gpio_base + pgrp - > size ) {
int pin ;
pin = pgrp - > base + offset - pgrp - > gpio_base ;
if ( community )
* community = comm ;
if ( padgrp )
* padgrp = pgrp ;
return pin ;
}
}
}
return - EINVAL ;
}
2019-08-16 12:38:38 +03:00
/**
* intel_pin_to_gpio ( ) - Translate from pin number to GPIO offset
* @ pctrl : Pinctrl structure
* @ pin : pin number
*
* Translate the pin number of pinctrl to GPIO offset
*/
2019-09-06 21:51:59 +03:00
static __maybe_unused int intel_pin_to_gpio ( struct intel_pinctrl * pctrl , int pin )
2019-08-16 12:38:38 +03:00
{
const struct intel_community * community ;
const struct intel_padgroup * padgrp ;
community = intel_get_community ( pctrl , pin ) ;
if ( ! community )
return - EINVAL ;
padgrp = intel_community_get_padgroup ( community , pin ) ;
if ( ! padgrp )
return - EINVAL ;
return pin - padgrp - > base + padgrp - > gpio_base ;
}
2018-09-26 17:50:26 +03:00
static int intel_gpio_get ( struct gpio_chip * chip , unsigned int offset )
2015-03-30 17:31:49 +03:00
{
2015-12-08 02:18:59 +03:00
struct intel_pinctrl * pctrl = gpiochip_get_data ( chip ) ;
2015-03-30 17:31:49 +03:00
void __iomem * reg ;
2017-08-24 11:19:33 +03:00
u32 padcfg0 ;
2018-09-18 18:36:21 +03:00
int pin ;
2015-03-30 17:31:49 +03:00
2018-09-18 18:36:21 +03:00
pin = intel_gpio_to_pin ( pctrl , offset , NULL , NULL ) ;
if ( pin < 0 )
return - EINVAL ;
2015-03-30 17:31:49 +03:00
2018-09-18 18:36:21 +03:00
reg = intel_get_padcfg ( pctrl , pin , PADCFG0 ) ;
2015-03-30 17:31:49 +03:00
if ( ! reg )
return - EINVAL ;
2017-08-24 11:19:33 +03:00
padcfg0 = readl ( reg ) ;
if ( ! ( padcfg0 & PADCFG0_GPIOTXDIS ) )
return ! ! ( padcfg0 & PADCFG0_GPIOTXSTATE ) ;
return ! ! ( padcfg0 & PADCFG0_GPIORXSTATE ) ;
2015-03-30 17:31:49 +03:00
}
2018-09-26 17:50:26 +03:00
static void intel_gpio_set ( struct gpio_chip * chip , unsigned int offset ,
int value )
2015-03-30 17:31:49 +03:00
{
2015-12-08 02:18:59 +03:00
struct intel_pinctrl * pctrl = gpiochip_get_data ( chip ) ;
2017-08-24 11:19:34 +03:00
unsigned long flags ;
2015-03-30 17:31:49 +03:00
void __iomem * reg ;
2017-08-24 11:19:34 +03:00
u32 padcfg0 ;
2018-09-18 18:36:21 +03:00
int pin ;
2015-03-30 17:31:49 +03:00
2018-09-18 18:36:21 +03:00
pin = intel_gpio_to_pin ( pctrl , offset , NULL , NULL ) ;
if ( pin < 0 )
return ;
reg = intel_get_padcfg ( pctrl , pin , PADCFG0 ) ;
2017-08-24 11:19:34 +03:00
if ( ! reg )
return ;
raw_spin_lock_irqsave ( & pctrl - > lock , flags ) ;
padcfg0 = readl ( reg ) ;
if ( value )
padcfg0 | = PADCFG0_GPIOTXSTATE ;
else
padcfg0 & = ~ PADCFG0_GPIOTXSTATE ;
writel ( padcfg0 , reg ) ;
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
}
2018-03-06 16:42:13 +03:00
static int intel_gpio_get_direction ( struct gpio_chip * chip , unsigned int offset )
{
struct intel_pinctrl * pctrl = gpiochip_get_data ( chip ) ;
2020-06-12 17:50:00 +03:00
unsigned long flags ;
2018-03-06 16:42:13 +03:00
void __iomem * reg ;
u32 padcfg0 ;
2018-09-18 18:36:21 +03:00
int pin ;
pin = intel_gpio_to_pin ( pctrl , offset , NULL , NULL ) ;
if ( pin < 0 )
return - EINVAL ;
2018-03-06 16:42:13 +03:00
2018-09-18 18:36:21 +03:00
reg = intel_get_padcfg ( pctrl , pin , PADCFG0 ) ;
2018-03-06 16:42:13 +03:00
if ( ! reg )
return - EINVAL ;
2020-06-12 17:50:00 +03:00
raw_spin_lock_irqsave ( & pctrl - > lock , flags ) ;
2018-03-06 16:42:13 +03:00
padcfg0 = readl ( reg ) ;
2020-06-12 17:50:00 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2018-03-06 16:42:13 +03:00
if ( padcfg0 & PADCFG0_PMODE_MASK )
return - EINVAL ;
2019-12-12 09:34:32 +03:00
if ( padcfg0 & PADCFG0_GPIOTXDIS )
return GPIO_LINE_DIRECTION_IN ;
return GPIO_LINE_DIRECTION_OUT ;
2018-03-06 16:42:13 +03:00
}
2018-09-26 17:50:26 +03:00
static int intel_gpio_direction_input ( struct gpio_chip * chip , unsigned int offset )
2015-03-30 17:31:49 +03:00
{
return pinctrl_gpio_direction_input ( chip - > base + offset ) ;
}
2018-09-26 17:50:26 +03:00
static int intel_gpio_direction_output ( struct gpio_chip * chip , unsigned int offset ,
2015-03-30 17:31:49 +03:00
int value )
{
intel_gpio_set ( chip , offset , value ) ;
return pinctrl_gpio_direction_output ( chip - > base + offset ) ;
}
static const struct gpio_chip intel_gpio_chip = {
. owner = THIS_MODULE ,
2015-10-11 18:34:19 +03:00
. request = gpiochip_generic_request ,
. free = gpiochip_generic_free ,
2018-03-06 16:42:13 +03:00
. get_direction = intel_gpio_get_direction ,
2015-03-30 17:31:49 +03:00
. direction_input = intel_gpio_direction_input ,
. direction_output = intel_gpio_direction_output ,
. get = intel_gpio_get ,
. set = intel_gpio_set ,
2017-01-27 13:07:14 +03:00
. set_config = gpiochip_generic_config ,
2015-03-30 17:31:49 +03:00
} ;
static void intel_gpio_irq_ack ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
2015-12-08 02:18:59 +03:00
struct intel_pinctrl * pctrl = gpiochip_get_data ( gc ) ;
2015-03-30 17:31:49 +03:00
const struct intel_community * community ;
2017-11-27 16:54:43 +03:00
const struct intel_padgroup * padgrp ;
int pin ;
2015-03-30 17:31:49 +03:00
2017-11-27 16:54:43 +03:00
pin = intel_gpio_to_pin ( pctrl , irqd_to_hwirq ( d ) , & community , & padgrp ) ;
if ( pin > = 0 ) {
2018-09-26 17:50:26 +03:00
unsigned int gpp , gpp_offset , is_offset ;
2017-06-06 16:18:17 +03:00
gpp = padgrp - > reg_num ;
gpp_offset = padgroup_offset ( padgrp , pin ) ;
2017-10-23 15:40:25 +03:00
is_offset = community - > is_offset + gpp * 4 ;
2015-03-30 17:31:49 +03:00
2017-06-06 16:18:17 +03:00
raw_spin_lock ( & pctrl - > lock ) ;
2017-10-23 15:40:25 +03:00
writel ( BIT ( gpp_offset ) , community - > regs + is_offset ) ;
2017-06-06 16:18:17 +03:00
raw_spin_unlock ( & pctrl - > lock ) ;
2015-03-30 17:31:49 +03:00
}
}
static void intel_gpio_irq_mask_unmask ( struct irq_data * d , bool mask )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
2015-12-08 02:18:59 +03:00
struct intel_pinctrl * pctrl = gpiochip_get_data ( gc ) ;
2015-03-30 17:31:49 +03:00
const struct intel_community * community ;
2017-11-27 16:54:43 +03:00
const struct intel_padgroup * padgrp ;
int pin ;
2015-03-30 17:31:49 +03:00
2017-11-27 16:54:43 +03:00
pin = intel_gpio_to_pin ( pctrl , irqd_to_hwirq ( d ) , & community , & padgrp ) ;
if ( pin > = 0 ) {
2018-09-26 17:50:26 +03:00
unsigned int gpp , gpp_offset ;
2017-06-06 16:18:17 +03:00
unsigned long flags ;
2019-04-30 11:37:53 +03:00
void __iomem * reg , * is ;
2015-03-30 17:31:49 +03:00
u32 value ;
2017-06-06 16:18:17 +03:00
gpp = padgrp - > reg_num ;
gpp_offset = padgroup_offset ( padgrp , pin ) ;
2015-03-30 17:31:49 +03:00
reg = community - > regs + community - > ie_offset + gpp * 4 ;
2019-04-30 11:37:53 +03:00
is = community - > regs + community - > is_offset + gpp * 4 ;
2017-06-06 16:18:17 +03:00
raw_spin_lock_irqsave ( & pctrl - > lock , flags ) ;
2019-04-30 11:37:53 +03:00
/* Clear interrupt status first to avoid unexpected interrupt */
writel ( BIT ( gpp_offset ) , is ) ;
2015-03-30 17:31:49 +03:00
value = readl ( reg ) ;
if ( mask )
value & = ~ BIT ( gpp_offset ) ;
else
value | = BIT ( gpp_offset ) ;
writel ( value , reg ) ;
2017-06-06 16:18:17 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
}
}
static void intel_gpio_irq_mask ( struct irq_data * d )
{
intel_gpio_irq_mask_unmask ( d , true ) ;
}
static void intel_gpio_irq_unmask ( struct irq_data * d )
{
intel_gpio_irq_mask_unmask ( d , false ) ;
}
2018-09-26 17:50:26 +03:00
static int intel_gpio_irq_type ( struct irq_data * d , unsigned int type )
2015-03-30 17:31:49 +03:00
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
2015-12-08 02:18:59 +03:00
struct intel_pinctrl * pctrl = gpiochip_get_data ( gc ) ;
2018-09-26 17:50:26 +03:00
unsigned int pin = intel_gpio_to_pin ( pctrl , irqd_to_hwirq ( d ) , NULL , NULL ) ;
2015-03-30 17:31:49 +03:00
unsigned long flags ;
void __iomem * reg ;
u32 value ;
reg = intel_get_padcfg ( pctrl , pin , PADCFG0 ) ;
if ( ! reg )
return - EINVAL ;
2015-10-21 13:08:44 +03:00
/*
* If the pin is in ACPI mode it is still usable as a GPIO but it
* cannot be used as IRQ because GPI_IS status bit will not be
* updated by the host controller hardware .
*/
if ( intel_pad_acpi_mode ( pctrl , pin ) ) {
dev_warn ( pctrl - > dev , " pin %u cannot be used as IRQ \n " , pin ) ;
return - EPERM ;
}
2016-06-16 11:25:36 +03:00
raw_spin_lock_irqsave ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
2017-11-29 16:25:44 +03:00
intel_gpio_set_gpio_mode ( reg ) ;
2020-06-12 17:49:54 +03:00
/* Disable TX buffer and enable RX (this will be input) */
__intel_gpio_set_direction ( reg , true ) ;
2015-03-30 17:31:49 +03:00
value = readl ( reg ) ;
value & = ~ ( PADCFG0_RXEVCFG_MASK | PADCFG0_RXINV ) ;
if ( ( type & IRQ_TYPE_EDGE_BOTH ) = = IRQ_TYPE_EDGE_BOTH ) {
value | = PADCFG0_RXEVCFG_EDGE_BOTH < < PADCFG0_RXEVCFG_SHIFT ;
} else if ( type & IRQ_TYPE_EDGE_FALLING ) {
value | = PADCFG0_RXEVCFG_EDGE < < PADCFG0_RXEVCFG_SHIFT ;
value | = PADCFG0_RXINV ;
} else if ( type & IRQ_TYPE_EDGE_RISING ) {
value | = PADCFG0_RXEVCFG_EDGE < < PADCFG0_RXEVCFG_SHIFT ;
2016-03-16 21:15:25 +03:00
} else if ( type & IRQ_TYPE_LEVEL_MASK ) {
if ( type & IRQ_TYPE_LEVEL_LOW )
value | = PADCFG0_RXINV ;
2015-03-30 17:31:49 +03:00
} else {
value | = PADCFG0_RXEVCFG_DISABLED < < PADCFG0_RXEVCFG_SHIFT ;
}
writel ( value , reg ) ;
if ( type & IRQ_TYPE_EDGE_BOTH )
2015-06-23 16:52:45 +03:00
irq_set_handler_locked ( d , handle_edge_irq ) ;
2015-03-30 17:31:49 +03:00
else if ( type & IRQ_TYPE_LEVEL_MASK )
2015-06-23 16:52:45 +03:00
irq_set_handler_locked ( d , handle_level_irq ) ;
2015-03-30 17:31:49 +03:00
2016-06-16 11:25:36 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
return 0 ;
}
static int intel_gpio_irq_wake ( struct irq_data * d , unsigned int on )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
2015-12-08 02:18:59 +03:00
struct intel_pinctrl * pctrl = gpiochip_get_data ( gc ) ;
2018-09-26 17:50:26 +03:00
unsigned int pin = intel_gpio_to_pin ( pctrl , irqd_to_hwirq ( d ) , NULL , NULL ) ;
2016-07-08 14:30:46 +03:00
2015-03-30 17:31:49 +03:00
if ( on )
2016-09-22 02:35:23 +03:00
enable_irq_wake ( pctrl - > irq ) ;
2015-03-30 17:31:49 +03:00
else
2016-09-22 02:35:23 +03:00
disable_irq_wake ( pctrl - > irq ) ;
2016-07-08 14:30:46 +03:00
2015-03-30 17:31:49 +03:00
dev_dbg ( pctrl - > dev , " %sable wake for pin %u \n " , on ? " en " : " dis " , pin ) ;
return 0 ;
}
2020-06-12 17:49:56 +03:00
static int intel_gpio_community_irq_handler ( struct intel_pinctrl * pctrl ,
const struct intel_community * community )
2015-03-30 17:31:49 +03:00
{
2015-10-21 13:08:43 +03:00
struct gpio_chip * gc = & pctrl - > chip ;
2020-06-12 17:49:56 +03:00
unsigned int gpp ;
int ret = 0 ;
2015-03-30 17:31:49 +03:00
for ( gpp = 0 ; gpp < community - > ngpps ; gpp + + ) {
2017-06-06 16:18:17 +03:00
const struct intel_padgroup * padgrp = & community - > gpps [ gpp ] ;
2015-03-30 17:31:49 +03:00
unsigned long pending , enabled , gpp_offset ;
2020-06-12 17:50:00 +03:00
unsigned long flags ;
raw_spin_lock_irqsave ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
2017-10-23 15:40:25 +03:00
pending = readl ( community - > regs + community - > is_offset +
padgrp - > reg_num * 4 ) ;
2015-03-30 17:31:49 +03:00
enabled = readl ( community - > regs + community - > ie_offset +
2017-06-06 16:18:17 +03:00
padgrp - > reg_num * 4 ) ;
2015-03-30 17:31:49 +03:00
2020-06-12 17:50:00 +03:00
raw_spin_unlock_irqrestore ( & pctrl - > lock , flags ) ;
2015-03-30 17:31:49 +03:00
/* Only interrupts that are enabled */
pending & = enabled ;
2017-06-06 16:18:17 +03:00
for_each_set_bit ( gpp_offset , & pending , padgrp - > size ) {
2019-11-06 17:39:48 +03:00
unsigned int irq ;
2015-03-30 17:31:49 +03:00
2017-11-07 21:15:47 +03:00
irq = irq_find_mapping ( gc - > irq . domain ,
2017-11-27 16:54:43 +03:00
padgrp - > gpio_base + gpp_offset ) ;
2015-03-30 17:31:49 +03:00
generic_handle_irq ( irq ) ;
}
2020-06-12 17:49:56 +03:00
ret + = pending ? 1 : 0 ;
2015-03-30 17:31:49 +03:00
}
2015-10-21 13:08:43 +03:00
return ret ;
2015-03-30 17:31:49 +03:00
}
2015-10-21 13:08:43 +03:00
static irqreturn_t intel_gpio_irq ( int irq , void * data )
2015-03-30 17:31:49 +03:00
{
2015-10-21 13:08:43 +03:00
const struct intel_community * community ;
struct intel_pinctrl * pctrl = data ;
2020-06-12 17:49:56 +03:00
unsigned int i ;
int ret = 0 ;
2015-03-30 17:31:49 +03:00
/* Need to check all communities for pending interrupts */
2015-10-21 13:08:43 +03:00
for ( i = 0 ; i < pctrl - > ncommunities ; i + + ) {
community = & pctrl - > communities [ i ] ;
2020-06-12 17:49:56 +03:00
ret + = intel_gpio_community_irq_handler ( pctrl , community ) ;
2015-10-21 13:08:43 +03:00
}
2015-03-30 17:31:49 +03:00
2020-06-12 17:49:56 +03:00
return IRQ_RETVAL ( ret ) ;
2015-03-30 17:31:49 +03:00
}
2020-01-09 10:53:28 +03:00
static int intel_gpio_add_community_ranges ( struct intel_pinctrl * pctrl ,
const struct intel_community * community )
2017-11-27 16:54:43 +03:00
{
2017-12-04 20:08:15 +03:00
int ret = 0 , i ;
2017-11-27 16:54:43 +03:00
for ( i = 0 ; i < community - > ngpps ; i + + ) {
const struct intel_padgroup * gpp = & community - > gpps [ i ] ;
2020-04-13 14:18:20 +03:00
if ( gpp - > gpio_base = = INTEL_GPIO_BASE_NOMAP )
2017-11-27 16:54:43 +03:00
continue ;
ret = gpiochip_add_pin_range ( & pctrl - > chip , dev_name ( pctrl - > dev ) ,
gpp - > gpio_base , gpp - > base ,
gpp - > size ) ;
if ( ret )
return ret ;
}
return ret ;
}
2020-01-09 10:53:28 +03:00
static int intel_gpio_add_pin_ranges ( struct gpio_chip * gc )
{
struct intel_pinctrl * pctrl = gpiochip_get_data ( gc ) ;
int ret , i ;
for ( i = 0 ; i < pctrl - > ncommunities ; i + + ) {
struct intel_community * community = & pctrl - > communities [ i ] ;
ret = intel_gpio_add_community_ranges ( pctrl , community ) ;
if ( ret ) {
dev_err ( pctrl - > dev , " failed to add GPIO pin range \n " ) ;
return ret ;
}
}
return 0 ;
}
2019-11-06 17:39:48 +03:00
static unsigned int intel_gpio_ngpio ( const struct intel_pinctrl * pctrl )
2017-11-27 16:54:43 +03:00
{
const struct intel_community * community ;
2018-09-26 17:50:26 +03:00
unsigned int ngpio = 0 ;
2017-11-27 16:54:43 +03:00
int i , j ;
for ( i = 0 ; i < pctrl - > ncommunities ; i + + ) {
community = & pctrl - > communities [ i ] ;
for ( j = 0 ; j < community - > ngpps ; j + + ) {
const struct intel_padgroup * gpp = & community - > gpps [ j ] ;
2020-04-13 14:18:20 +03:00
if ( gpp - > gpio_base = = INTEL_GPIO_BASE_NOMAP )
2017-11-27 16:54:43 +03:00
continue ;
if ( gpp - > gpio_base + gpp - > size > ngpio )
ngpio = gpp - > gpio_base + gpp - > size ;
}
}
return ngpio ;
}
2015-03-30 17:31:49 +03:00
static int intel_gpio_probe ( struct intel_pinctrl * pctrl , int irq )
{
2020-01-09 10:53:28 +03:00
int ret ;
2020-01-09 10:53:29 +03:00
struct gpio_irq_chip * girq ;
2015-03-30 17:31:49 +03:00
pctrl - > chip = intel_gpio_chip ;
2019-09-16 17:47:51 +03:00
/* Setup GPIO chip */
2017-11-27 16:54:43 +03:00
pctrl - > chip . ngpio = intel_gpio_ngpio ( pctrl ) ;
2015-03-30 17:31:49 +03:00
pctrl - > chip . label = dev_name ( pctrl - > dev ) ;
2015-11-04 11:56:26 +03:00
pctrl - > chip . parent = pctrl - > dev ;
2015-03-30 17:31:49 +03:00
pctrl - > chip . base = - 1 ;
2020-01-09 10:53:28 +03:00
pctrl - > chip . add_pin_ranges = intel_gpio_add_pin_ranges ;
2016-09-22 02:35:23 +03:00
pctrl - > irq = irq ;
2015-03-30 17:31:49 +03:00
2019-09-16 17:47:51 +03:00
/* Setup IRQ chip */
pctrl - > irqchip . name = dev_name ( pctrl - > dev ) ;
pctrl - > irqchip . irq_ack = intel_gpio_irq_ack ;
pctrl - > irqchip . irq_mask = intel_gpio_irq_mask ;
pctrl - > irqchip . irq_unmask = intel_gpio_irq_unmask ;
pctrl - > irqchip . irq_set_type = intel_gpio_irq_type ;
pctrl - > irqchip . irq_set_wake = intel_gpio_irq_wake ;
pctrl - > irqchip . flags = IRQCHIP_MASK_ON_SUSPEND ;
2015-10-21 13:08:43 +03:00
/*
2020-01-09 10:53:29 +03:00
* On some platforms several GPIO controllers share the same interrupt
* line .
2015-10-21 13:08:43 +03:00
*/
2016-06-16 11:25:37 +03:00
ret = devm_request_irq ( pctrl - > dev , irq , intel_gpio_irq ,
IRQF_SHARED | IRQF_NO_THREAD ,
2015-10-21 13:08:43 +03:00
dev_name ( pctrl - > dev ) , pctrl ) ;
if ( ret ) {
dev_err ( pctrl - > dev , " failed to request interrupt \n " ) ;
2017-01-10 17:31:57 +03:00
return ret ;
2015-03-30 17:31:49 +03:00
}
2020-01-09 10:53:29 +03:00
girq = & pctrl - > chip . irq ;
girq - > chip = & pctrl - > irqchip ;
/* This will let us handle the IRQ in the driver */
girq - > parent_handler = NULL ;
girq - > num_parents = 0 ;
girq - > default_type = IRQ_TYPE_NONE ;
girq - > handler = handle_bad_irq ;
ret = devm_gpiochip_add_data ( pctrl - > dev , & pctrl - > chip , pctrl ) ;
2015-03-30 17:31:49 +03:00
if ( ret ) {
2020-01-09 10:53:29 +03:00
dev_err ( pctrl - > dev , " failed to register gpiochip \n " ) ;
2017-01-10 17:31:57 +03:00
return ret ;
2015-03-30 17:31:49 +03:00
}
return 0 ;
}
2021-01-07 22:01:57 +03:00
static int intel_pinctrl_add_padgroups_by_gpps ( struct intel_pinctrl * pctrl ,
struct intel_community * community )
2017-06-06 16:18:17 +03:00
{
struct intel_padgroup * gpps ;
2018-09-26 17:50:26 +03:00
unsigned int padown_num = 0 ;
2021-01-07 22:01:57 +03:00
size_t i , ngpps = community - > ngpps ;
2017-06-06 16:18:17 +03:00
gpps = devm_kcalloc ( pctrl - > dev , ngpps , sizeof ( * gpps ) , GFP_KERNEL ) ;
if ( ! gpps )
return - ENOMEM ;
for ( i = 0 ; i < ngpps ; i + + ) {
2021-01-07 22:01:57 +03:00
gpps [ i ] = community - > gpps [ i ] ;
2017-06-06 16:18:17 +03:00
if ( gpps [ i ] . size > 32 )
return - EINVAL ;
2020-04-13 14:18:20 +03:00
/* Special treatment for GPIO base */
switch ( gpps [ i ] . gpio_base ) {
case INTEL_GPIO_BASE_MATCH :
gpps [ i ] . gpio_base = gpps [ i ] . base ;
break ;
2020-04-13 14:18:24 +03:00
case INTEL_GPIO_BASE_ZERO :
gpps [ i ] . gpio_base = 0 ;
break ;
2020-04-13 14:18:20 +03:00
case INTEL_GPIO_BASE_NOMAP :
2021-03-08 19:49:10 +03:00
break ;
2020-04-13 14:18:20 +03:00
default :
break ;
}
2017-11-27 16:54:43 +03:00
2021-01-07 22:01:57 +03:00
gpps [ i ] . padown_num = padown_num ;
padown_num + = DIV_ROUND_UP ( gpps [ i ] . size * 4 , 32 ) ;
}
community - > gpps = gpps ;
return 0 ;
}
static int intel_pinctrl_add_padgroups_by_size ( struct intel_pinctrl * pctrl ,
struct intel_community * community )
{
struct intel_padgroup * gpps ;
unsigned int npins = community - > npins ;
unsigned int padown_num = 0 ;
size_t i , ngpps = DIV_ROUND_UP ( npins , community - > gpp_size ) ;
if ( community - > gpp_size > 32 )
return - EINVAL ;
gpps = devm_kcalloc ( pctrl - > dev , ngpps , sizeof ( * gpps ) , GFP_KERNEL ) ;
if ( ! gpps )
return - ENOMEM ;
for ( i = 0 ; i < ngpps ; i + + ) {
unsigned int gpp_size = community - > gpp_size ;
gpps [ i ] . reg_num = i ;
gpps [ i ] . base = community - > pin_base + i * gpp_size ;
gpps [ i ] . size = min ( gpp_size , npins ) ;
npins - = gpps [ i ] . size ;
2021-03-08 19:49:10 +03:00
gpps [ i ] . gpio_base = gpps [ i ] . base ;
2017-06-06 16:18:17 +03:00
gpps [ i ] . padown_num = padown_num ;
/*
* In older hardware the number of padown registers per
* group is fixed regardless of the group size .
*/
if ( community - > gpp_num_padown_regs )
padown_num + = community - > gpp_num_padown_regs ;
else
padown_num + = DIV_ROUND_UP ( gpps [ i ] . size * 4 , 32 ) ;
}
community - > ngpps = ngpps ;
community - > gpps = gpps ;
return 0 ;
}
2015-03-30 17:31:49 +03:00
static int intel_pinctrl_pm_init ( struct intel_pinctrl * pctrl )
{
# ifdef CONFIG_PM_SLEEP
const struct intel_pinctrl_soc_data * soc = pctrl - > soc ;
struct intel_community_context * communities ;
struct intel_pad_context * pads ;
int i ;
pads = devm_kcalloc ( pctrl - > dev , soc - > npins , sizeof ( * pads ) , GFP_KERNEL ) ;
if ( ! pads )
return - ENOMEM ;
communities = devm_kcalloc ( pctrl - > dev , pctrl - > ncommunities ,
sizeof ( * communities ) , GFP_KERNEL ) ;
if ( ! communities )
return - ENOMEM ;
for ( i = 0 ; i < pctrl - > ncommunities ; i + + ) {
struct intel_community * community = & pctrl - > communities [ i ] ;
2019-04-15 08:53:58 +03:00
u32 * intmask , * hostown ;
2015-03-30 17:31:49 +03:00
intmask = devm_kcalloc ( pctrl - > dev , community - > ngpps ,
sizeof ( * intmask ) , GFP_KERNEL ) ;
if ( ! intmask )
return - ENOMEM ;
communities [ i ] . intmask = intmask ;
2019-04-15 08:53:58 +03:00
hostown = devm_kcalloc ( pctrl - > dev , community - > ngpps ,
sizeof ( * hostown ) , GFP_KERNEL ) ;
if ( ! hostown )
return - ENOMEM ;
communities [ i ] . hostown = hostown ;
2015-03-30 17:31:49 +03:00
}
pctrl - > context . pads = pads ;
pctrl - > context . communities = communities ;
# endif
return 0 ;
}
2018-10-17 19:10:27 +03:00
static int intel_pinctrl_probe ( struct platform_device * pdev ,
const struct intel_pinctrl_soc_data * soc_data )
2015-03-30 17:31:49 +03:00
{
struct intel_pinctrl * pctrl ;
int i , ret , irq ;
pctrl = devm_kzalloc ( & pdev - > dev , sizeof ( * pctrl ) , GFP_KERNEL ) ;
if ( ! pctrl )
return - ENOMEM ;
pctrl - > dev = & pdev - > dev ;
pctrl - > soc = soc_data ;
2016-06-16 11:25:36 +03:00
raw_spin_lock_init ( & pctrl - > lock ) ;
2015-03-30 17:31:49 +03:00
/*
* Make a copy of the communities which we can use to hold pointers
* to the registers .
*/
pctrl - > ncommunities = pctrl - > soc - > ncommunities ;
pctrl - > communities = devm_kcalloc ( & pdev - > dev , pctrl - > ncommunities ,
sizeof ( * pctrl - > communities ) , GFP_KERNEL ) ;
if ( ! pctrl - > communities )
return - ENOMEM ;
for ( i = 0 ; i < pctrl - > ncommunities ; i + + ) {
struct intel_community * community = & pctrl - > communities [ i ] ;
void __iomem * regs ;
2021-01-08 16:40:05 +03:00
u32 offset ;
2021-01-07 22:01:58 +03:00
u32 value ;
2015-03-30 17:31:49 +03:00
* community = pctrl - > soc - > communities [ i ] ;
2019-07-03 17:44:20 +03:00
regs = devm_platform_ioremap_resource ( pdev , community - > barno ) ;
2015-03-30 17:31:49 +03:00
if ( IS_ERR ( regs ) )
return PTR_ERR ( regs ) ;
2021-01-07 22:01:58 +03:00
/* Determine community features based on the revision */
value = readl ( regs + REVID ) ;
if ( ( ( value & REVID_MASK ) > > REVID_SHIFT ) > = 0x94 ) {
community - > features | = PINCTRL_FEATURE_DEBOUNCE ;
community - > features | = PINCTRL_FEATURE_1K_PD ;
2017-01-27 13:07:14 +03:00
}
2021-01-08 16:40:05 +03:00
/* Determine community features based on the capabilities */
offset = CAPLIST ;
do {
value = readl ( regs + offset ) ;
switch ( ( value & CAPLIST_ID_MASK ) > > CAPLIST_ID_SHIFT ) {
case CAPLIST_ID_GPIO_HW_INFO :
community - > features | = PINCTRL_FEATURE_GPIO_HW_INFO ;
break ;
case CAPLIST_ID_PWM :
community - > features | = PINCTRL_FEATURE_PWM ;
break ;
case CAPLIST_ID_BLINK :
community - > features | = PINCTRL_FEATURE_BLINK ;
break ;
case CAPLIST_ID_EXP :
community - > features | = PINCTRL_FEATURE_EXP ;
break ;
default :
break ;
}
offset = ( value & CAPLIST_NEXT_MASK ) > > CAPLIST_NEXT_SHIFT ;
} while ( offset ) ;
dev_dbg ( & pdev - > dev , " Community%d features: %#08x \n " , i , community - > features ) ;
2015-03-30 17:31:49 +03:00
/* Read offset of the pad configuration registers */
2021-01-08 16:40:05 +03:00
offset = readl ( regs + PADBAR ) ;
2015-03-30 17:31:49 +03:00
community - > regs = regs ;
2021-01-08 16:40:05 +03:00
community - > pad_regs = regs + offset ;
2017-06-06 16:18:17 +03:00
2021-01-07 22:01:57 +03:00
if ( community - > gpps )
ret = intel_pinctrl_add_padgroups_by_gpps ( pctrl , community ) ;
else
ret = intel_pinctrl_add_padgroups_by_size ( pctrl , community ) ;
2017-06-06 16:18:17 +03:00
if ( ret )
return ret ;
2015-03-30 17:31:49 +03:00
}
irq = platform_get_irq ( pdev , 0 ) ;
2019-07-30 21:15:34 +03:00
if ( irq < 0 )
2015-03-30 17:31:49 +03:00
return irq ;
ret = intel_pinctrl_pm_init ( pctrl ) ;
if ( ret )
return ret ;
pctrl - > pctldesc = intel_pinctrl_desc ;
pctrl - > pctldesc . name = dev_name ( & pdev - > dev ) ;
pctrl - > pctldesc . pins = pctrl - > soc - > pins ;
pctrl - > pctldesc . npins = pctrl - > soc - > npins ;
2016-02-28 12:12:47 +03:00
pctrl - > pctldev = devm_pinctrl_register ( & pdev - > dev , & pctrl - > pctldesc ,
pctrl ) ;
2015-06-09 07:01:16 +03:00
if ( IS_ERR ( pctrl - > pctldev ) ) {
2015-03-30 17:31:49 +03:00
dev_err ( & pdev - > dev , " failed to register pinctrl driver \n " ) ;
2015-06-09 07:01:16 +03:00
return PTR_ERR ( pctrl - > pctldev ) ;
2015-03-30 17:31:49 +03:00
}
ret = intel_gpio_probe ( pctrl , irq ) ;
2016-02-28 12:12:47 +03:00
if ( ret )
2015-03-30 17:31:49 +03:00
return ret ;
platform_set_drvdata ( pdev , pctrl ) ;
return 0 ;
}
2018-08-30 19:27:40 +03:00
int intel_pinctrl_probe_by_hid ( struct platform_device * pdev )
{
const struct intel_pinctrl_soc_data * data ;
data = device_get_match_data ( & pdev - > dev ) ;
2020-07-29 14:57:06 +03:00
if ( ! data )
return - ENODATA ;
2018-08-30 19:27:40 +03:00
return intel_pinctrl_probe ( pdev , data ) ;
}
EXPORT_SYMBOL_GPL ( intel_pinctrl_probe_by_hid ) ;
2018-08-30 19:27:36 +03:00
int intel_pinctrl_probe_by_uid ( struct platform_device * pdev )
2020-07-29 14:57:06 +03:00
{
const struct intel_pinctrl_soc_data * data ;
data = intel_pinctrl_get_soc_data ( pdev ) ;
if ( IS_ERR ( data ) )
return PTR_ERR ( data ) ;
return intel_pinctrl_probe ( pdev , data ) ;
}
EXPORT_SYMBOL_GPL ( intel_pinctrl_probe_by_uid ) ;
const struct intel_pinctrl_soc_data * intel_pinctrl_get_soc_data ( struct platform_device * pdev )
2018-08-30 19:27:36 +03:00
{
const struct intel_pinctrl_soc_data * data = NULL ;
const struct intel_pinctrl_soc_data * * table ;
struct acpi_device * adev ;
unsigned int i ;
adev = ACPI_COMPANION ( & pdev - > dev ) ;
if ( adev ) {
const void * match = device_get_match_data ( & pdev - > dev ) ;
table = ( const struct intel_pinctrl_soc_data * * ) match ;
for ( i = 0 ; table [ i ] ; i + + ) {
if ( ! strcmp ( adev - > pnp . unique_id , table [ i ] - > uid ) ) {
data = table [ i ] ;
break ;
}
}
} else {
const struct platform_device_id * id ;
id = platform_get_device_id ( pdev ) ;
if ( ! id )
2020-07-29 14:57:06 +03:00
return ERR_PTR ( - ENODEV ) ;
2018-08-30 19:27:36 +03:00
table = ( const struct intel_pinctrl_soc_data * * ) id - > driver_data ;
data = table [ pdev - > id ] ;
}
2020-07-29 14:57:06 +03:00
return data ? : ERR_PTR ( - ENODATA ) ;
2018-08-30 19:27:36 +03:00
}
2020-07-29 14:57:06 +03:00
EXPORT_SYMBOL_GPL ( intel_pinctrl_get_soc_data ) ;
2018-08-30 19:27:36 +03:00
2015-03-30 17:31:49 +03:00
# ifdef CONFIG_PM_SLEEP
2018-09-26 17:50:26 +03:00
static bool intel_pinctrl_should_save ( struct intel_pinctrl * pctrl , unsigned int pin )
2016-10-10 16:39:31 +03:00
{
const struct pin_desc * pd = pin_desc_get ( pctrl - > pctldev , pin ) ;
if ( ! pd | | ! intel_pad_usable ( pctrl , pin ) )
return false ;
/*
* Only restore the pin if it is actually in use by the kernel ( or
* by userspace ) . It is possible that some pins are used by the
* BIOS during resume and those are not always locked down so leave
* them alone .
*/
if ( pd - > mux_owner | | pd - > gpio_owner | |
2019-08-16 12:38:38 +03:00
gpiochip_line_is_irq ( & pctrl - > chip , intel_pin_to_gpio ( pctrl , pin ) ) )
2016-10-10 16:39:31 +03:00
return true ;
return false ;
}
pinctrl: pinctrl-intel: move gpio suspend/resume to noirq phase
In current driver, SET_LATE_SYSTEM_SLEEP_PM_OPS is used to install the
callbacks for suspend/resume.
GPIO pin may be used as the interrupt pin by some device. However, using
SET_LATE_SYSTEM_SLEEP_PM_OPS() to install the callbacks, the resume
callback is called after resume_device_irqs(). Unintended interrupts may
arrive due to resuming device irqs first, but the GPIO controller is not
properly restored.
Normally, for a SMP system, there are multiple cores, so even when there are
unintended interrupts, BSP gets the chance to initialize the GPIO chip soon.
But when there is only 1 core is active (other cores are offlined or
single core) during resume, it is more easily to observe the unintended
interrupts.
This patch renames the suspend/resume function by adding suffix "_noirq",
and installs the callbacks using SET_NOIRQ_SYSTEM_SLEEP_PM_OPS().
Signed-off-by: Binbin Wu <binbin.wu@intel.com>
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
2019-04-08 13:49:26 +03:00
int intel_pinctrl_suspend_noirq ( struct device * dev )
2015-03-30 17:31:49 +03:00
{
2018-10-21 23:00:29 +03:00
struct intel_pinctrl * pctrl = dev_get_drvdata ( dev ) ;
2015-03-30 17:31:49 +03:00
struct intel_community_context * communities ;
struct intel_pad_context * pads ;
int i ;
pads = pctrl - > context . pads ;
for ( i = 0 ; i < pctrl - > soc - > npins ; i + + ) {
const struct pinctrl_pin_desc * desc = & pctrl - > soc - > pins [ i ] ;
2017-01-27 13:07:14 +03:00
void __iomem * padcfg ;
2015-03-30 17:31:49 +03:00
u32 val ;
2016-10-10 16:39:31 +03:00
if ( ! intel_pinctrl_should_save ( pctrl , desc - > number ) )
2015-03-30 17:31:49 +03:00
continue ;
val = readl ( intel_get_padcfg ( pctrl , desc - > number , PADCFG0 ) ) ;
pads [ i ] . padcfg0 = val & ~ PADCFG0_GPIORXSTATE ;
val = readl ( intel_get_padcfg ( pctrl , desc - > number , PADCFG1 ) ) ;
pads [ i ] . padcfg1 = val ;
2017-01-27 13:07:14 +03:00
padcfg = intel_get_padcfg ( pctrl , desc - > number , PADCFG2 ) ;
if ( padcfg )
pads [ i ] . padcfg2 = readl ( padcfg ) ;
2015-03-30 17:31:49 +03:00
}
communities = pctrl - > context . communities ;
for ( i = 0 ; i < pctrl - > ncommunities ; i + + ) {
struct intel_community * community = & pctrl - > communities [ i ] ;
void __iomem * base ;
2018-09-26 17:50:26 +03:00
unsigned int gpp ;
2015-03-30 17:31:49 +03:00
base = community - > regs + community - > ie_offset ;
for ( gpp = 0 ; gpp < community - > ngpps ; gpp + + )
communities [ i ] . intmask [ gpp ] = readl ( base + gpp * 4 ) ;
2019-04-15 08:53:58 +03:00
base = community - > regs + community - > hostown_offset ;
for ( gpp = 0 ; gpp < community - > ngpps ; gpp + + )
communities [ i ] . hostown [ gpp ] = readl ( base + gpp * 4 ) ;
2015-03-30 17:31:49 +03:00
}
return 0 ;
}
pinctrl: pinctrl-intel: move gpio suspend/resume to noirq phase
In current driver, SET_LATE_SYSTEM_SLEEP_PM_OPS is used to install the
callbacks for suspend/resume.
GPIO pin may be used as the interrupt pin by some device. However, using
SET_LATE_SYSTEM_SLEEP_PM_OPS() to install the callbacks, the resume
callback is called after resume_device_irqs(). Unintended interrupts may
arrive due to resuming device irqs first, but the GPIO controller is not
properly restored.
Normally, for a SMP system, there are multiple cores, so even when there are
unintended interrupts, BSP gets the chance to initialize the GPIO chip soon.
But when there is only 1 core is active (other cores are offlined or
single core) during resume, it is more easily to observe the unintended
interrupts.
This patch renames the suspend/resume function by adding suffix "_noirq",
and installs the callbacks using SET_NOIRQ_SYSTEM_SLEEP_PM_OPS().
Signed-off-by: Binbin Wu <binbin.wu@intel.com>
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
2019-04-08 13:49:26 +03:00
EXPORT_SYMBOL_GPL ( intel_pinctrl_suspend_noirq ) ;
2015-03-30 17:31:49 +03:00
2015-10-13 17:51:25 +03:00
static void intel_gpio_irq_init ( struct intel_pinctrl * pctrl )
{
size_t i ;
for ( i = 0 ; i < pctrl - > ncommunities ; i + + ) {
const struct intel_community * community ;
void __iomem * base ;
2018-09-26 17:50:26 +03:00
unsigned int gpp ;
2015-10-13 17:51:25 +03:00
community = & pctrl - > communities [ i ] ;
base = community - > regs ;
for ( gpp = 0 ; gpp < community - > ngpps ; gpp + + ) {
/* Mask and clear all interrupts */
writel ( 0 , base + community - > ie_offset + gpp * 4 ) ;
2017-10-23 15:40:25 +03:00
writel ( 0xffff , base + community - > is_offset + gpp * 4 ) ;
2015-10-13 17:51:25 +03:00
}
}
}
2019-10-22 13:00:04 +03:00
static bool intel_gpio_update_reg ( void __iomem * reg , u32 mask , u32 value )
2019-04-15 08:53:58 +03:00
{
2019-04-28 20:19:06 +03:00
u32 curr , updated ;
2019-04-15 08:53:58 +03:00
2019-10-22 13:00:04 +03:00
curr = readl ( reg ) ;
2019-04-28 20:19:06 +03:00
updated = ( curr & ~ mask ) | ( value & mask ) ;
2019-10-22 13:00:04 +03:00
if ( curr = = updated )
return false ;
2019-04-28 20:19:06 +03:00
2019-10-22 13:00:04 +03:00
writel ( updated , reg ) ;
return true ;
2019-04-15 08:53:58 +03:00
}
2019-10-22 13:00:01 +03:00
static void intel_restore_hostown ( struct intel_pinctrl * pctrl , unsigned int c ,
void __iomem * base , unsigned int gpp , u32 saved )
{
const struct intel_community * community = & pctrl - > communities [ c ] ;
const struct intel_padgroup * padgrp = & community - > gpps [ gpp ] ;
struct device * dev = pctrl - > dev ;
2020-06-10 21:14:49 +03:00
const char * dummy ;
u32 requested = 0 ;
unsigned int i ;
2019-10-22 13:00:01 +03:00
2020-04-13 14:18:20 +03:00
if ( padgrp - > gpio_base = = INTEL_GPIO_BASE_NOMAP )
2019-10-22 13:00:01 +03:00
return ;
2020-06-10 21:14:49 +03:00
for_each_requested_gpio_in_range ( & pctrl - > chip , i , padgrp - > gpio_base , padgrp - > size , dummy )
requested | = BIT ( i ) ;
2019-10-22 13:00:04 +03:00
if ( ! intel_gpio_update_reg ( base + gpp * 4 , requested , saved ) )
2019-10-22 13:00:01 +03:00
return ;
2019-10-22 13:00:03 +03:00
dev_dbg ( dev , " restored hostown %u/%u %#08x \n " , c , gpp , readl ( base + gpp * 4 ) ) ;
2019-10-22 13:00:01 +03:00
}
2019-10-22 13:00:02 +03:00
static void intel_restore_intmask ( struct intel_pinctrl * pctrl , unsigned int c ,
void __iomem * base , unsigned int gpp , u32 saved )
{
struct device * dev = pctrl - > dev ;
2019-10-22 13:00:04 +03:00
if ( ! intel_gpio_update_reg ( base + gpp * 4 , ~ 0U , saved ) )
return ;
2019-10-22 13:00:02 +03:00
dev_dbg ( dev , " restored mask %u/%u %#08x \n " , c , gpp , readl ( base + gpp * 4 ) ) ;
}
2019-10-22 13:00:00 +03:00
static void intel_restore_padcfg ( struct intel_pinctrl * pctrl , unsigned int pin ,
unsigned int reg , u32 saved )
{
u32 mask = ( reg = = PADCFG0 ) ? PADCFG0_GPIORXSTATE : 0 ;
unsigned int n = reg / sizeof ( u32 ) ;
struct device * dev = pctrl - > dev ;
void __iomem * padcfg ;
padcfg = intel_get_padcfg ( pctrl , pin , reg ) ;
if ( ! padcfg )
return ;
2019-10-22 13:00:04 +03:00
if ( ! intel_gpio_update_reg ( padcfg , ~ mask , saved ) )
2019-10-22 13:00:00 +03:00
return ;
dev_dbg ( dev , " restored pin %u padcfg%u %#08x \n " , pin , n , readl ( padcfg ) ) ;
}
pinctrl: pinctrl-intel: move gpio suspend/resume to noirq phase
In current driver, SET_LATE_SYSTEM_SLEEP_PM_OPS is used to install the
callbacks for suspend/resume.
GPIO pin may be used as the interrupt pin by some device. However, using
SET_LATE_SYSTEM_SLEEP_PM_OPS() to install the callbacks, the resume
callback is called after resume_device_irqs(). Unintended interrupts may
arrive due to resuming device irqs first, but the GPIO controller is not
properly restored.
Normally, for a SMP system, there are multiple cores, so even when there are
unintended interrupts, BSP gets the chance to initialize the GPIO chip soon.
But when there is only 1 core is active (other cores are offlined or
single core) during resume, it is more easily to observe the unintended
interrupts.
This patch renames the suspend/resume function by adding suffix "_noirq",
and installs the callbacks using SET_NOIRQ_SYSTEM_SLEEP_PM_OPS().
Signed-off-by: Binbin Wu <binbin.wu@intel.com>
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
2019-04-08 13:49:26 +03:00
int intel_pinctrl_resume_noirq ( struct device * dev )
2015-03-30 17:31:49 +03:00
{
2018-10-21 23:00:29 +03:00
struct intel_pinctrl * pctrl = dev_get_drvdata ( dev ) ;
2015-03-30 17:31:49 +03:00
const struct intel_community_context * communities ;
const struct intel_pad_context * pads ;
int i ;
/* Mask all interrupts */
intel_gpio_irq_init ( pctrl ) ;
pads = pctrl - > context . pads ;
for ( i = 0 ; i < pctrl - > soc - > npins ; i + + ) {
const struct pinctrl_pin_desc * desc = & pctrl - > soc - > pins [ i ] ;
2016-10-10 16:39:31 +03:00
if ( ! intel_pinctrl_should_save ( pctrl , desc - > number ) )
2015-03-30 17:31:49 +03:00
continue ;
2019-10-22 13:00:00 +03:00
intel_restore_padcfg ( pctrl , desc - > number , PADCFG0 , pads [ i ] . padcfg0 ) ;
intel_restore_padcfg ( pctrl , desc - > number , PADCFG1 , pads [ i ] . padcfg1 ) ;
intel_restore_padcfg ( pctrl , desc - > number , PADCFG2 , pads [ i ] . padcfg2 ) ;
2015-03-30 17:31:49 +03:00
}
communities = pctrl - > context . communities ;
for ( i = 0 ; i < pctrl - > ncommunities ; i + + ) {
struct intel_community * community = & pctrl - > communities [ i ] ;
void __iomem * base ;
2018-09-26 17:50:26 +03:00
unsigned int gpp ;
2015-03-30 17:31:49 +03:00
base = community - > regs + community - > ie_offset ;
2019-10-22 13:00:02 +03:00
for ( gpp = 0 ; gpp < community - > ngpps ; gpp + + )
intel_restore_intmask ( pctrl , i , base , gpp , communities [ i ] . intmask [ gpp ] ) ;
2019-04-15 08:53:58 +03:00
base = community - > regs + community - > hostown_offset ;
2019-10-22 13:00:01 +03:00
for ( gpp = 0 ; gpp < community - > ngpps ; gpp + + )
intel_restore_hostown ( pctrl , i , base , gpp , communities [ i ] . hostown [ gpp ] ) ;
2015-03-30 17:31:49 +03:00
}
return 0 ;
}
pinctrl: pinctrl-intel: move gpio suspend/resume to noirq phase
In current driver, SET_LATE_SYSTEM_SLEEP_PM_OPS is used to install the
callbacks for suspend/resume.
GPIO pin may be used as the interrupt pin by some device. However, using
SET_LATE_SYSTEM_SLEEP_PM_OPS() to install the callbacks, the resume
callback is called after resume_device_irqs(). Unintended interrupts may
arrive due to resuming device irqs first, but the GPIO controller is not
properly restored.
Normally, for a SMP system, there are multiple cores, so even when there are
unintended interrupts, BSP gets the chance to initialize the GPIO chip soon.
But when there is only 1 core is active (other cores are offlined or
single core) during resume, it is more easily to observe the unintended
interrupts.
This patch renames the suspend/resume function by adding suffix "_noirq",
and installs the callbacks using SET_NOIRQ_SYSTEM_SLEEP_PM_OPS().
Signed-off-by: Binbin Wu <binbin.wu@intel.com>
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
2019-04-08 13:49:26 +03:00
EXPORT_SYMBOL_GPL ( intel_pinctrl_resume_noirq ) ;
2015-03-30 17:31:49 +03:00
# endif
MODULE_AUTHOR ( " Mathias Nyman <mathias.nyman@linux.intel.com> " ) ;
MODULE_AUTHOR ( " Mika Westerberg <mika.westerberg@linux.intel.com> " ) ;
MODULE_DESCRIPTION ( " Intel pinctrl/GPIO core driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;