2022-06-07 20:29:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-09-20 00:52:58 +04:00
/*
* GPIO driver for Marvell SoCs
*
* Copyright ( C ) 2012 Marvell
*
* Thomas Petazzoni < thomas . petazzoni @ free - electrons . com >
* Andrew Lunn < andrew @ lunn . ch >
* Sebastian Hesselbarth < sebastian . hesselbarth @ gmail . com >
*
* This driver is a fairly straightforward GPIO driver for the
* complete family of Marvell EBU SoC platforms ( Orion , Dove ,
* Kirkwood , Discovery , Armada 370 / XP ) . The only complexity of this
* driver is the different register layout that exists between the
* non - SMP platforms ( Orion , Dove , Kirkwood , Armada 370 ) and the SMP
* platforms ( MV78200 from the Discovery family and the Armada
* XP ) . Therefore , this driver handles three variants of the GPIO
* block :
* - the basic variant , called " orion-gpio " , with the simplest
* register set . Used on Orion , Dove , Kirkwoord , Armada 370 and
* non - SMP Discovery systems
* - the mv78200 variant for MV78200 Discovery systems . This variant
* turns the edge mask and level mask registers into CPU0 edge
* mask / level mask registers , and adds CPU1 edge mask / level mask
* registers .
* - the armadaxp variant for Armada XP systems . This variant keeps
* the normal cause / edge mask / level mask registers when the global
* interrupts are used , but adds per - CPU cause / edge mask / level mask
* registers n a separate memory area for the per - CPU GPIO
* interrupts .
*/
2017-05-19 19:09:20 +03:00
# include <linux/bitops.h>
# include <linux/clk.h>
2013-01-21 14:09:01 +04:00
# include <linux/err.h>
2018-04-13 16:40:45 +03:00
# include <linux/gpio/driver.h>
# include <linux/gpio/consumer.h>
2019-04-26 15:40:18 +03:00
# include <linux/gpio/machine.h>
2017-05-19 19:09:20 +03:00
# include <linux/init.h>
# include <linux/io.h>
2012-09-20 00:52:58 +04:00
# include <linux/irq.h>
2017-05-19 19:09:20 +03:00
# include <linux/irqchip/chained_irq.h>
2012-09-20 00:52:58 +04:00
# include <linux/irqdomain.h>
2017-06-12 18:34:59 +03:00
# include <linux/mfd/syscon.h>
2012-09-20 00:52:58 +04:00
# include <linux/of_device.h>
# include <linux/pinctrl/consumer.h>
2017-04-14 18:40:52 +03:00
# include <linux/platform_device.h>
2017-05-19 19:09:20 +03:00
# include <linux/pwm.h>
2017-05-19 19:09:21 +03:00
# include <linux/regmap.h>
2017-05-19 19:09:20 +03:00
# include <linux/slab.h>
2012-09-20 00:52:58 +04:00
/*
* GPIO unit register offsets .
*/
2017-04-14 18:40:52 +03:00
# define GPIO_OUT_OFF 0x0000
# define GPIO_IO_CONF_OFF 0x0004
# define GPIO_BLINK_EN_OFF 0x0008
# define GPIO_IN_POL_OFF 0x000c
# define GPIO_DATA_IN_OFF 0x0010
# define GPIO_EDGE_CAUSE_OFF 0x0014
# define GPIO_EDGE_MASK_OFF 0x0018
# define GPIO_LEVEL_MASK_OFF 0x001c
# define GPIO_BLINK_CNT_SELECT_OFF 0x0020
/*
* PWM register offsets .
*/
# define PWM_BLINK_ON_DURATION_OFF 0x0
# define PWM_BLINK_OFF_DURATION_OFF 0x4
2021-01-11 14:46:27 +03:00
# define PWM_BLINK_COUNTER_B_OFF 0x8
2017-04-14 18:40:52 +03:00
2021-01-11 14:46:27 +03:00
/* Armada 8k variant gpios register offsets */
# define AP80X_GPIO0_OFF_A8K 0x1040
# define CP11X_GPIO0_OFF_A8K 0x100
# define CP11X_GPIO1_OFF_A8K 0x140
2012-09-20 00:52:58 +04:00
/* The MV78200 has per-CPU registers for edge mask and level mask */
2015-01-10 02:34:47 +03:00
# define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18)
2012-09-20 00:52:58 +04:00
# define GPIO_LEVEL_MASK_MV78200_OFF(cpu) ((cpu) ? 0x34 : 0x1C)
2017-03-16 09:33:56 +03:00
/*
* The Armada XP has per - CPU registers for interrupt cause , interrupt
2020-12-02 10:15:33 +03:00
* mask and interrupt level mask . Those are in percpu_regs range .
2017-03-16 09:33:56 +03:00
*/
2012-09-20 00:52:58 +04:00
# define GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu) ((cpu) * 0x4)
# define GPIO_EDGE_MASK_ARMADAXP_OFF(cpu) (0x10 + (cpu) * 0x4)
# define GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu) (0x20 + (cpu) * 0x4)
2015-01-10 02:34:47 +03:00
# define MVEBU_GPIO_SOC_VARIANT_ORION 0x1
# define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2
2012-09-20 00:52:58 +04:00
# define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3
2017-06-12 18:34:59 +03:00
# define MVEBU_GPIO_SOC_VARIANT_A8K 0x4
2012-09-20 00:52:58 +04:00
2015-01-10 02:34:47 +03:00
# define MVEBU_MAX_GPIO_PER_BANK 32
2012-09-20 00:52:58 +04:00
2017-04-14 18:40:52 +03:00
struct mvebu_pwm {
2020-12-02 10:15:34 +03:00
struct regmap * regs ;
2021-01-11 14:46:27 +03:00
u32 offset ;
2017-04-14 18:40:52 +03:00
unsigned long clk_rate ;
struct gpio_desc * gpiod ;
struct pwm_chip chip ;
spinlock_t lock ;
struct mvebu_gpio_chip * mvchip ;
/* Used to preserve GPIO/PWM registers across suspend/resume */
u32 blink_select ;
u32 blink_on_duration ;
u32 blink_off_duration ;
} ;
2012-09-20 00:52:58 +04:00
struct mvebu_gpio_chip {
struct gpio_chip chip ;
2017-05-19 19:09:21 +03:00
struct regmap * regs ;
2017-06-12 18:34:59 +03:00
u32 offset ;
2017-05-19 19:09:21 +03:00
struct regmap * percpu_regs ;
2013-11-07 11:50:19 +04:00
int irqbase ;
2012-09-20 00:52:58 +04:00
struct irq_domain * domain ;
2015-01-10 02:34:47 +03:00
int soc_variant ;
2014-10-24 15:59:19 +04:00
2017-04-14 18:40:52 +03:00
/* Used for PWM support */
struct clk * clk ;
struct mvebu_pwm * mvpwm ;
2015-01-10 02:34:47 +03:00
/* Used to preserve GPIO registers across suspend/resume */
2017-03-16 09:34:00 +03:00
u32 out_reg ;
u32 io_conf_reg ;
u32 blink_en_reg ;
u32 in_pol_reg ;
u32 edge_mask_regs [ 4 ] ;
u32 level_mask_regs [ 4 ] ;
2012-09-20 00:52:58 +04:00
} ;
/*
* Functions returning addresses of individual registers for a given
* GPIO controller .
*/
2017-05-19 19:09:21 +03:00
static void mvebu_gpioreg_edge_cause ( struct mvebu_gpio_chip * mvchip ,
struct regmap * * map , unsigned int * offset )
2012-10-28 16:23:24 +04:00
{
2017-05-19 19:09:21 +03:00
int cpu ;
2012-10-28 16:23:24 +04:00
2017-05-19 19:09:21 +03:00
switch ( mvchip - > soc_variant ) {
case MVEBU_GPIO_SOC_VARIANT_ORION :
case MVEBU_GPIO_SOC_VARIANT_MV78200 :
2017-06-12 18:34:59 +03:00
case MVEBU_GPIO_SOC_VARIANT_A8K :
2017-05-19 19:09:21 +03:00
* map = mvchip - > regs ;
2017-06-12 18:34:59 +03:00
* offset = GPIO_EDGE_CAUSE_OFF + mvchip - > offset ;
2017-05-19 19:09:21 +03:00
break ;
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP :
cpu = smp_processor_id ( ) ;
* map = mvchip - > percpu_regs ;
* offset = GPIO_EDGE_CAUSE_ARMADAXP_OFF ( cpu ) ;
break ;
default :
BUG ( ) ;
}
2017-04-14 18:40:52 +03:00
}
2017-05-19 19:09:21 +03:00
static u32
mvebu_gpio_read_edge_cause ( struct mvebu_gpio_chip * mvchip )
2012-09-20 00:52:58 +04:00
{
2017-05-19 19:09:21 +03:00
struct regmap * map ;
unsigned int offset ;
u32 val ;
2012-09-20 00:52:58 +04:00
2017-05-19 19:09:21 +03:00
mvebu_gpioreg_edge_cause ( mvchip , & map , & offset ) ;
regmap_read ( map , offset , & val ) ;
return val ;
2012-09-20 00:52:58 +04:00
}
2017-05-19 19:09:21 +03:00
static void
mvebu_gpio_write_edge_cause ( struct mvebu_gpio_chip * mvchip , u32 val )
2012-09-20 00:52:58 +04:00
{
2017-05-19 19:09:21 +03:00
struct regmap * map ;
unsigned int offset ;
mvebu_gpioreg_edge_cause ( mvchip , & map , & offset ) ;
regmap_write ( map , offset , val ) ;
2012-09-20 00:52:58 +04:00
}
2017-05-19 19:09:21 +03:00
static inline void
mvebu_gpioreg_edge_mask ( struct mvebu_gpio_chip * mvchip ,
struct regmap * * map , unsigned int * offset )
2012-09-20 00:52:58 +04:00
{
int cpu ;
2013-03-20 16:15:56 +04:00
switch ( mvchip - > soc_variant ) {
2012-09-20 00:52:58 +04:00
case MVEBU_GPIO_SOC_VARIANT_ORION :
2017-06-12 18:34:59 +03:00
case MVEBU_GPIO_SOC_VARIANT_A8K :
2017-05-19 19:09:21 +03:00
* map = mvchip - > regs ;
2017-06-12 18:34:59 +03:00
* offset = GPIO_EDGE_MASK_OFF + mvchip - > offset ;
2017-05-19 19:09:21 +03:00
break ;
2012-09-20 00:52:58 +04:00
case MVEBU_GPIO_SOC_VARIANT_MV78200 :
2017-05-19 19:09:21 +03:00
cpu = smp_processor_id ( ) ;
* map = mvchip - > regs ;
* offset = GPIO_EDGE_MASK_MV78200_OFF ( cpu ) ;
break ;
2012-09-20 00:52:58 +04:00
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP :
cpu = smp_processor_id ( ) ;
2017-05-19 19:09:21 +03:00
* map = mvchip - > percpu_regs ;
* offset = GPIO_EDGE_MASK_ARMADAXP_OFF ( cpu ) ;
break ;
2012-09-20 00:52:58 +04:00
default :
BUG ( ) ;
}
}
2017-05-19 19:09:21 +03:00
static u32
mvebu_gpio_read_edge_mask ( struct mvebu_gpio_chip * mvchip )
2012-09-20 00:52:58 +04:00
{
2017-05-19 19:09:21 +03:00
struct regmap * map ;
unsigned int offset ;
u32 val ;
2012-09-20 00:52:58 +04:00
2017-05-19 19:09:21 +03:00
mvebu_gpioreg_edge_mask ( mvchip , & map , & offset ) ;
regmap_read ( map , offset , & val ) ;
return val ;
2012-09-20 00:52:58 +04:00
}
2017-05-19 19:09:21 +03:00
static void
mvebu_gpio_write_edge_mask ( struct mvebu_gpio_chip * mvchip , u32 val )
{
struct regmap * map ;
unsigned int offset ;
mvebu_gpioreg_edge_mask ( mvchip , & map , & offset ) ;
regmap_write ( map , offset , val ) ;
}
static void
mvebu_gpioreg_level_mask ( struct mvebu_gpio_chip * mvchip ,
struct regmap * * map , unsigned int * offset )
2012-09-20 00:52:58 +04:00
{
int cpu ;
2013-03-20 16:15:56 +04:00
switch ( mvchip - > soc_variant ) {
2012-09-20 00:52:58 +04:00
case MVEBU_GPIO_SOC_VARIANT_ORION :
2017-06-12 18:34:59 +03:00
case MVEBU_GPIO_SOC_VARIANT_A8K :
2017-05-19 19:09:21 +03:00
* map = mvchip - > regs ;
2017-06-12 18:34:59 +03:00
* offset = GPIO_LEVEL_MASK_OFF + mvchip - > offset ;
2017-05-19 19:09:21 +03:00
break ;
2012-09-20 00:52:58 +04:00
case MVEBU_GPIO_SOC_VARIANT_MV78200 :
cpu = smp_processor_id ( ) ;
2017-05-19 19:09:21 +03:00
* map = mvchip - > regs ;
* offset = GPIO_LEVEL_MASK_MV78200_OFF ( cpu ) ;
break ;
2012-09-20 00:52:58 +04:00
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP :
cpu = smp_processor_id ( ) ;
2017-05-19 19:09:21 +03:00
* map = mvchip - > percpu_regs ;
* offset = GPIO_LEVEL_MASK_ARMADAXP_OFF ( cpu ) ;
break ;
2012-09-20 00:52:58 +04:00
default :
BUG ( ) ;
}
}
2017-05-19 19:09:21 +03:00
static u32
mvebu_gpio_read_level_mask ( struct mvebu_gpio_chip * mvchip )
{
struct regmap * map ;
unsigned int offset ;
u32 val ;
mvebu_gpioreg_level_mask ( mvchip , & map , & offset ) ;
regmap_read ( map , offset , & val ) ;
return val ;
}
static void
mvebu_gpio_write_level_mask ( struct mvebu_gpio_chip * mvchip , u32 val )
{
struct regmap * map ;
unsigned int offset ;
mvebu_gpioreg_level_mask ( mvchip , & map , & offset ) ;
regmap_write ( map , offset , val ) ;
}
2017-04-14 18:40:52 +03:00
/*
2020-12-02 10:15:34 +03:00
* Functions returning offsets of individual registers for a given
2017-04-14 18:40:52 +03:00
* PWM controller .
*/
2020-12-02 10:15:34 +03:00
static unsigned int mvebu_pwmreg_blink_on_duration ( struct mvebu_pwm * mvpwm )
2017-04-14 18:40:52 +03:00
{
2021-01-11 14:46:27 +03:00
return mvpwm - > offset + PWM_BLINK_ON_DURATION_OFF ;
2017-04-14 18:40:52 +03:00
}
2020-12-02 10:15:34 +03:00
static unsigned int mvebu_pwmreg_blink_off_duration ( struct mvebu_pwm * mvpwm )
2017-04-14 18:40:52 +03:00
{
2021-01-11 14:46:27 +03:00
return mvpwm - > offset + PWM_BLINK_OFF_DURATION_OFF ;
2017-04-14 18:40:52 +03:00
}
2012-09-20 00:52:58 +04:00
/*
* Functions implementing the gpio_chip methods
*/
2017-03-16 09:33:58 +03:00
static void mvebu_gpio_set ( struct gpio_chip * chip , unsigned int pin , int value )
2012-09-20 00:52:58 +04:00
{
2015-12-07 13:09:24 +03:00
struct mvebu_gpio_chip * mvchip = gpiochip_get_data ( chip ) ;
2012-09-20 00:52:58 +04:00
2017-06-12 18:34:59 +03:00
regmap_update_bits ( mvchip - > regs , GPIO_OUT_OFF + mvchip - > offset ,
2017-05-19 19:09:21 +03:00
BIT ( pin ) , value ? BIT ( pin ) : 0 ) ;
2012-09-20 00:52:58 +04:00
}
2017-03-16 09:33:58 +03:00
static int mvebu_gpio_get ( struct gpio_chip * chip , unsigned int pin )
2012-09-20 00:52:58 +04:00
{
2015-12-07 13:09:24 +03:00
struct mvebu_gpio_chip * mvchip = gpiochip_get_data ( chip ) ;
2012-09-20 00:52:58 +04:00
u32 u ;
2017-06-12 18:34:59 +03:00
regmap_read ( mvchip - > regs , GPIO_IO_CONF_OFF + mvchip - > offset , & u ) ;
2017-05-19 19:09:21 +03:00
if ( u & BIT ( pin ) ) {
u32 data_in , in_pol ;
2017-06-12 18:34:59 +03:00
regmap_read ( mvchip - > regs , GPIO_DATA_IN_OFF + mvchip - > offset ,
& data_in ) ;
regmap_read ( mvchip - > regs , GPIO_IN_POL_OFF + mvchip - > offset ,
& in_pol ) ;
2017-05-19 19:09:21 +03:00
u = data_in ^ in_pol ;
2012-09-20 00:52:58 +04:00
} else {
2017-06-12 18:34:59 +03:00
regmap_read ( mvchip - > regs , GPIO_OUT_OFF + mvchip - > offset , & u ) ;
2012-09-20 00:52:58 +04:00
}
return ( u > > pin ) & 1 ;
}
2017-03-16 09:33:58 +03:00
static void mvebu_gpio_blink ( struct gpio_chip * chip , unsigned int pin ,
int value )
2012-10-28 16:23:24 +04:00
{
2015-12-07 13:09:24 +03:00
struct mvebu_gpio_chip * mvchip = gpiochip_get_data ( chip ) ;
2012-10-28 16:23:24 +04:00
2017-06-12 18:34:59 +03:00
regmap_update_bits ( mvchip - > regs , GPIO_BLINK_EN_OFF + mvchip - > offset ,
2017-05-19 19:09:21 +03:00
BIT ( pin ) , value ? BIT ( pin ) : 0 ) ;
2012-10-28 16:23:24 +04:00
}
2017-03-16 09:33:58 +03:00
static int mvebu_gpio_direction_input ( struct gpio_chip * chip , unsigned int pin )
2012-09-20 00:52:58 +04:00
{
2015-12-07 13:09:24 +03:00
struct mvebu_gpio_chip * mvchip = gpiochip_get_data ( chip ) ;
2012-09-20 00:52:58 +04:00
int ret ;
2017-03-16 09:33:56 +03:00
/*
* Check with the pinctrl driver whether this pin is usable as
* an input GPIO
*/
2012-09-20 00:52:58 +04:00
ret = pinctrl_gpio_direction_input ( chip - > base + pin ) ;
if ( ret )
return ret ;
2017-06-12 18:34:59 +03:00
regmap_update_bits ( mvchip - > regs , GPIO_IO_CONF_OFF + mvchip - > offset ,
2017-06-09 13:09:17 +03:00
BIT ( pin ) , BIT ( pin ) ) ;
2012-09-20 00:52:58 +04:00
return 0 ;
}
2017-03-16 09:33:58 +03:00
static int mvebu_gpio_direction_output ( struct gpio_chip * chip , unsigned int pin ,
2012-09-20 00:52:58 +04:00
int value )
{
2015-12-07 13:09:24 +03:00
struct mvebu_gpio_chip * mvchip = gpiochip_get_data ( chip ) ;
2012-09-20 00:52:58 +04:00
int ret ;
2017-03-16 09:33:56 +03:00
/*
* Check with the pinctrl driver whether this pin is usable as
* an output GPIO
*/
2012-09-20 00:52:58 +04:00
ret = pinctrl_gpio_direction_output ( chip - > base + pin ) ;
if ( ret )
return ret ;
2012-10-28 16:23:24 +04:00
mvebu_gpio_blink ( chip , pin , 0 ) ;
2012-10-23 12:17:05 +04:00
mvebu_gpio_set ( chip , pin , value ) ;
2017-06-12 18:34:59 +03:00
regmap_update_bits ( mvchip - > regs , GPIO_IO_CONF_OFF + mvchip - > offset ,
2017-05-19 19:09:21 +03:00
BIT ( pin ) , 0 ) ;
2012-09-20 00:52:58 +04:00
return 0 ;
}
2019-01-10 15:26:21 +03:00
static int mvebu_gpio_get_direction ( struct gpio_chip * chip , unsigned int pin )
{
struct mvebu_gpio_chip * mvchip = gpiochip_get_data ( chip ) ;
u32 u ;
regmap_read ( mvchip - > regs , GPIO_IO_CONF_OFF + mvchip - > offset , & u ) ;
2019-11-06 11:54:12 +03:00
if ( u & BIT ( pin ) )
return GPIO_LINE_DIRECTION_IN ;
return GPIO_LINE_DIRECTION_OUT ;
2019-01-10 15:26:21 +03:00
}
2017-03-16 09:33:58 +03:00
static int mvebu_gpio_to_irq ( struct gpio_chip * chip , unsigned int pin )
2012-09-20 00:52:58 +04:00
{
2015-12-07 13:09:24 +03:00
struct mvebu_gpio_chip * mvchip = gpiochip_get_data ( chip ) ;
2017-03-16 09:33:59 +03:00
2012-09-20 00:52:58 +04:00
return irq_create_mapping ( mvchip - > domain , pin ) ;
}
/*
* Functions implementing the irq_chip methods
*/
static void mvebu_gpio_irq_ack ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
struct mvebu_gpio_chip * mvchip = gc - > private ;
2016-10-20 00:03:41 +03:00
u32 mask = d - > mask ;
2012-09-20 00:52:58 +04:00
irq_gc_lock ( gc ) ;
2017-05-19 19:09:21 +03:00
mvebu_gpio_write_edge_cause ( mvchip , ~ mask ) ;
2012-09-20 00:52:58 +04:00
irq_gc_unlock ( gc ) ;
}
static void mvebu_gpio_edge_irq_mask ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
struct mvebu_gpio_chip * mvchip = gc - > private ;
2015-04-02 18:11:11 +03:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2016-10-20 00:03:41 +03:00
u32 mask = d - > mask ;
2012-09-20 00:52:58 +04:00
irq_gc_lock ( gc ) ;
2015-04-02 18:11:11 +03:00
ct - > mask_cache_priv & = ~ mask ;
2017-05-19 19:09:21 +03:00
mvebu_gpio_write_edge_mask ( mvchip , ct - > mask_cache_priv ) ;
2012-09-20 00:52:58 +04:00
irq_gc_unlock ( gc ) ;
}
static void mvebu_gpio_edge_irq_unmask ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
struct mvebu_gpio_chip * mvchip = gc - > private ;
2015-04-02 18:11:11 +03:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2016-10-20 00:03:41 +03:00
u32 mask = d - > mask ;
2012-09-20 00:52:58 +04:00
irq_gc_lock ( gc ) ;
2020-01-15 10:38:11 +03:00
mvebu_gpio_write_edge_cause ( mvchip , ~ mask ) ;
2015-04-02 18:11:11 +03:00
ct - > mask_cache_priv | = mask ;
2017-05-19 19:09:21 +03:00
mvebu_gpio_write_edge_mask ( mvchip , ct - > mask_cache_priv ) ;
2012-09-20 00:52:58 +04:00
irq_gc_unlock ( gc ) ;
}
static void mvebu_gpio_level_irq_mask ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
struct mvebu_gpio_chip * mvchip = gc - > private ;
2015-04-02 18:11:11 +03:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2016-10-20 00:03:41 +03:00
u32 mask = d - > mask ;
2012-09-20 00:52:58 +04:00
irq_gc_lock ( gc ) ;
2015-04-02 18:11:11 +03:00
ct - > mask_cache_priv & = ~ mask ;
2017-05-19 19:09:21 +03:00
mvebu_gpio_write_level_mask ( mvchip , ct - > mask_cache_priv ) ;
2012-09-20 00:52:58 +04:00
irq_gc_unlock ( gc ) ;
}
static void mvebu_gpio_level_irq_unmask ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
struct mvebu_gpio_chip * mvchip = gc - > private ;
2015-04-02 18:11:11 +03:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2016-10-20 00:03:41 +03:00
u32 mask = d - > mask ;
2012-09-20 00:52:58 +04:00
irq_gc_lock ( gc ) ;
2015-04-02 18:11:11 +03:00
ct - > mask_cache_priv | = mask ;
2017-05-19 19:09:21 +03:00
mvebu_gpio_write_level_mask ( mvchip , ct - > mask_cache_priv ) ;
2012-09-20 00:52:58 +04:00
irq_gc_unlock ( gc ) ;
}
/*****************************************************************************
* MVEBU GPIO IRQ
*
* GPIO_IN_POL register controls whether GPIO_DATA_IN will hold the same
* value of the line or the opposite value .
*
* Level IRQ handlers : DATA_IN is used directly as cause register .
2015-01-10 02:34:47 +03:00
* Interrupt are masked by LEVEL_MASK registers .
2012-09-20 00:52:58 +04:00
* Edge IRQ handlers : Change in DATA_IN are latched in EDGE_CAUSE .
2015-01-10 02:34:47 +03:00
* Interrupt are masked by EDGE_MASK registers .
2012-09-20 00:52:58 +04:00
* Both - edge handlers : Similar to regular Edge handlers , but also swaps
2015-01-10 02:34:47 +03:00
* the polarity to catch the next line transaction .
* This is a race condition that might not perfectly
* work on some use cases .
2012-09-20 00:52:58 +04:00
*
* Every eight GPIO lines are grouped ( OR ' ed ) before going up to main
* cause register .
*
2015-01-10 02:34:47 +03:00
* EDGE cause mask
* data - in / - - - - - - - - | | - - - - - | | - - - - \
* - - - - - | | - - - - - - - - - to main cause reg
* X \ - - - - - - - - - - - - - - - - | | - - - - /
* polarity LEVEL mask
2012-09-20 00:52:58 +04:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int mvebu_gpio_irq_set_type ( struct irq_data * d , unsigned int type )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
struct mvebu_gpio_chip * mvchip = gc - > private ;
int pin ;
u32 u ;
pin = d - > hwirq ;
2017-06-12 18:34:59 +03:00
regmap_read ( mvchip - > regs , GPIO_IO_CONF_OFF + mvchip - > offset , & u ) ;
2017-05-19 19:09:21 +03:00
if ( ( u & BIT ( pin ) ) = = 0 )
2012-09-20 00:52:58 +04:00
return - EINVAL ;
type & = IRQ_TYPE_SENSE_MASK ;
if ( type = = IRQ_TYPE_NONE )
return - EINVAL ;
/* Check if we need to change chip and handler */
if ( ! ( ct - > type & type ) )
if ( irq_setup_alt_chip ( d , type ) )
return - EINVAL ;
/*
* Configure interrupt polarity .
*/
2013-03-20 16:15:56 +04:00
switch ( type ) {
2012-09-20 00:52:58 +04:00
case IRQ_TYPE_EDGE_RISING :
case IRQ_TYPE_LEVEL_HIGH :
2017-06-12 18:34:59 +03:00
regmap_update_bits ( mvchip - > regs ,
GPIO_IN_POL_OFF + mvchip - > offset ,
2017-05-19 19:09:21 +03:00
BIT ( pin ) , 0 ) ;
2012-09-30 12:23:27 +04:00
break ;
2012-09-20 00:52:58 +04:00
case IRQ_TYPE_EDGE_FALLING :
case IRQ_TYPE_LEVEL_LOW :
2017-06-12 18:34:59 +03:00
regmap_update_bits ( mvchip - > regs ,
GPIO_IN_POL_OFF + mvchip - > offset ,
2017-06-09 13:09:17 +03:00
BIT ( pin ) , BIT ( pin ) ) ;
2012-09-30 12:23:27 +04:00
break ;
2012-09-20 00:52:58 +04:00
case IRQ_TYPE_EDGE_BOTH : {
2017-05-19 19:09:21 +03:00
u32 data_in , in_pol , val ;
2012-09-20 00:52:58 +04:00
2017-06-12 18:34:59 +03:00
regmap_read ( mvchip - > regs ,
GPIO_IN_POL_OFF + mvchip - > offset , & in_pol ) ;
regmap_read ( mvchip - > regs ,
GPIO_DATA_IN_OFF + mvchip - > offset , & data_in ) ;
2012-09-20 00:52:58 +04:00
/*
* set initial polarity based on current input level
*/
2017-05-19 19:09:21 +03:00
if ( ( data_in ^ in_pol ) & BIT ( pin ) )
val = BIT ( pin ) ; /* falling */
2012-09-20 00:52:58 +04:00
else
2017-05-19 19:09:21 +03:00
val = 0 ; /* raising */
2017-06-12 18:34:59 +03:00
regmap_update_bits ( mvchip - > regs ,
GPIO_IN_POL_OFF + mvchip - > offset ,
2017-05-19 19:09:21 +03:00
BIT ( pin ) , val ) ;
2012-09-30 12:23:27 +04:00
break ;
2012-09-20 00:52:58 +04:00
}
}
return 0 ;
}
2015-09-14 11:42:37 +03:00
static void mvebu_gpio_irq_handler ( struct irq_desc * desc )
2012-09-20 00:52:58 +04:00
{
2015-06-04 07:13:15 +03:00
struct mvebu_gpio_chip * mvchip = irq_desc_get_handler_data ( desc ) ;
2014-02-07 15:29:19 +04:00
struct irq_chip * chip = irq_desc_get_chip ( desc ) ;
2017-05-19 19:09:21 +03:00
u32 cause , type , data_in , level_mask , edge_cause , edge_mask ;
2012-09-20 00:52:58 +04:00
int i ;
if ( mvchip = = NULL )
return ;
2014-02-07 15:29:19 +04:00
chained_irq_enter ( chip , desc ) ;
2017-06-12 18:34:59 +03:00
regmap_read ( mvchip - > regs , GPIO_DATA_IN_OFF + mvchip - > offset , & data_in ) ;
2017-05-19 19:09:21 +03:00
level_mask = mvebu_gpio_read_level_mask ( mvchip ) ;
edge_cause = mvebu_gpio_read_edge_cause ( mvchip ) ;
edge_mask = mvebu_gpio_read_edge_mask ( mvchip ) ;
2017-07-12 14:22:29 +03:00
cause = ( data_in & level_mask ) | ( edge_cause & edge_mask ) ;
2012-09-20 00:52:58 +04:00
for ( i = 0 ; i < mvchip - > chip . ngpio ; i + + ) {
int irq ;
2016-10-20 00:03:41 +03:00
irq = irq_find_mapping ( mvchip - > domain , i ) ;
2012-09-20 00:52:58 +04:00
2017-03-17 20:44:06 +03:00
if ( ! ( cause & BIT ( i ) ) )
2012-09-20 00:52:58 +04:00
continue ;
2013-06-14 20:40:44 +04:00
type = irq_get_trigger_type ( irq ) ;
2012-09-20 00:52:58 +04:00
if ( ( type & IRQ_TYPE_SENSE_MASK ) = = IRQ_TYPE_EDGE_BOTH ) {
/* Swap polarity (race with GPIO line) */
u32 polarity ;
2017-06-12 18:34:59 +03:00
regmap_read ( mvchip - > regs ,
GPIO_IN_POL_OFF + mvchip - > offset ,
& polarity ) ;
2017-03-17 20:44:06 +03:00
polarity ^ = BIT ( i ) ;
2017-06-12 18:34:59 +03:00
regmap_write ( mvchip - > regs ,
GPIO_IN_POL_OFF + mvchip - > offset ,
polarity ) ;
2012-09-20 00:52:58 +04:00
}
2014-02-07 15:29:19 +04:00
2012-09-20 00:52:58 +04:00
generic_handle_irq ( irq ) ;
}
2014-02-07 15:29:19 +04:00
chained_irq_exit ( chip , desc ) ;
2012-09-20 00:52:58 +04:00
}
2020-12-02 10:15:34 +03:00
static const struct regmap_config mvebu_gpio_regmap_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. fast_io = true ,
} ;
2017-04-14 18:40:52 +03:00
/*
* Functions implementing the pwm_chip methods
*/
static struct mvebu_pwm * to_mvebu_pwm ( struct pwm_chip * chip )
{
return container_of ( chip , struct mvebu_pwm , chip ) ;
}
static int mvebu_pwm_request ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct mvebu_pwm * mvpwm = to_mvebu_pwm ( chip ) ;
struct mvebu_gpio_chip * mvchip = mvpwm - > mvchip ;
struct gpio_desc * desc ;
unsigned long flags ;
int ret = 0 ;
spin_lock_irqsave ( & mvpwm - > lock , flags ) ;
if ( mvpwm - > gpiod ) {
ret = - EBUSY ;
} else {
2018-04-13 16:40:45 +03:00
desc = gpiochip_request_own_desc ( & mvchip - > chip ,
2019-04-26 15:40:18 +03:00
pwm - > hwpwm , " mvebu-pwm " ,
GPIO_ACTIVE_HIGH ,
GPIOD_OUT_LOW ) ;
2018-04-13 16:40:45 +03:00
if ( IS_ERR ( desc ) ) {
ret = PTR_ERR ( desc ) ;
2017-04-14 18:40:52 +03:00
goto out ;
}
mvpwm - > gpiod = desc ;
}
out :
spin_unlock_irqrestore ( & mvpwm - > lock , flags ) ;
return ret ;
}
static void mvebu_pwm_free ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct mvebu_pwm * mvpwm = to_mvebu_pwm ( chip ) ;
unsigned long flags ;
spin_lock_irqsave ( & mvpwm - > lock , flags ) ;
2018-04-13 16:40:45 +03:00
gpiochip_free_own_desc ( mvpwm - > gpiod ) ;
2017-04-14 18:40:52 +03:00
mvpwm - > gpiod = NULL ;
spin_unlock_irqrestore ( & mvpwm - > lock , flags ) ;
}
2022-12-02 21:35:26 +03:00
static int mvebu_pwm_get_state ( struct pwm_chip * chip ,
struct pwm_device * pwm ,
struct pwm_state * state )
{
2017-04-14 18:40:52 +03:00
struct mvebu_pwm * mvpwm = to_mvebu_pwm ( chip ) ;
struct mvebu_gpio_chip * mvchip = mvpwm - > mvchip ;
unsigned long long val ;
unsigned long flags ;
u32 u ;
spin_lock_irqsave ( & mvpwm - > lock , flags ) ;
2020-12-02 10:15:34 +03:00
regmap_read ( mvpwm - > regs , mvebu_pwmreg_blink_on_duration ( mvpwm ) , & u ) ;
2021-01-20 19:16:28 +03:00
/* Hardware treats zero as 2^32. See mvebu_pwm_apply(). */
if ( u > 0 )
val = u ;
2017-04-14 18:40:52 +03:00
else
2021-01-20 19:16:28 +03:00
val = UINT_MAX + 1ULL ;
state - > duty_cycle = DIV_ROUND_UP_ULL ( val * NSEC_PER_SEC ,
mvpwm - > clk_rate ) ;
2017-04-14 18:40:52 +03:00
2020-12-02 10:15:34 +03:00
regmap_read ( mvpwm - > regs , mvebu_pwmreg_blink_off_duration ( mvpwm ) , & u ) ;
2021-01-20 19:16:28 +03:00
/* period = on + off duration */
if ( u > 0 )
val + = u ;
2021-01-17 16:17:02 +03:00
else
2021-01-20 19:16:28 +03:00
val + = UINT_MAX + 1ULL ;
state - > period = DIV_ROUND_UP_ULL ( val * NSEC_PER_SEC , mvpwm - > clk_rate ) ;
2017-04-14 18:40:52 +03:00
2017-06-12 18:34:59 +03:00
regmap_read ( mvchip - > regs , GPIO_BLINK_EN_OFF + mvchip - > offset , & u ) ;
2017-04-14 18:40:52 +03:00
if ( u )
state - > enabled = true ;
else
state - > enabled = false ;
spin_unlock_irqrestore ( & mvpwm - > lock , flags ) ;
2022-12-02 21:35:26 +03:00
return 0 ;
2017-04-14 18:40:52 +03:00
}
static int mvebu_pwm_apply ( struct pwm_chip * chip , struct pwm_device * pwm ,
2019-08-24 18:37:07 +03:00
const struct pwm_state * state )
2017-04-14 18:40:52 +03:00
{
struct mvebu_pwm * mvpwm = to_mvebu_pwm ( chip ) ;
struct mvebu_gpio_chip * mvchip = mvpwm - > mvchip ;
unsigned long long val ;
unsigned long flags ;
unsigned int on , off ;
2022-05-11 10:58:56 +03:00
if ( state - > polarity ! = PWM_POLARITY_NORMAL )
return - EINVAL ;
2017-04-14 18:40:52 +03:00
val = ( unsigned long long ) mvpwm - > clk_rate * state - > duty_cycle ;
do_div ( val , NSEC_PER_SEC ) ;
2021-01-20 19:16:28 +03:00
if ( val > UINT_MAX + 1ULL )
2017-04-14 18:40:52 +03:00
return - EINVAL ;
2021-01-20 19:16:28 +03:00
/*
* Zero on / off values don ' t work as expected . Experimentation shows
* that zero value is treated as 2 ^ 32. This behavior is not documented .
*/
if ( val = = UINT_MAX + 1ULL )
on = 0 ;
else if ( val )
2017-04-14 18:40:52 +03:00
on = val ;
else
on = 1 ;
2021-01-20 19:16:25 +03:00
val = ( unsigned long long ) mvpwm - > clk_rate * state - > period ;
2017-04-14 18:40:52 +03:00
do_div ( val , NSEC_PER_SEC ) ;
2021-01-20 19:16:25 +03:00
val - = on ;
2021-01-20 19:16:28 +03:00
if ( val > UINT_MAX + 1ULL )
2017-04-14 18:40:52 +03:00
return - EINVAL ;
2021-01-20 19:16:28 +03:00
if ( val = = UINT_MAX + 1ULL )
off = 0 ;
else if ( val )
2017-04-14 18:40:52 +03:00
off = val ;
else
off = 1 ;
spin_lock_irqsave ( & mvpwm - > lock , flags ) ;
2020-12-02 10:15:34 +03:00
regmap_write ( mvpwm - > regs , mvebu_pwmreg_blink_on_duration ( mvpwm ) , on ) ;
regmap_write ( mvpwm - > regs , mvebu_pwmreg_blink_off_duration ( mvpwm ) , off ) ;
2017-04-14 18:40:52 +03:00
if ( state - > enabled )
mvebu_gpio_blink ( & mvchip - > chip , pwm - > hwpwm , 1 ) ;
else
mvebu_gpio_blink ( & mvchip - > chip , pwm - > hwpwm , 0 ) ;
spin_unlock_irqrestore ( & mvpwm - > lock , flags ) ;
return 0 ;
}
static const struct pwm_ops mvebu_pwm_ops = {
. request = mvebu_pwm_request ,
. free = mvebu_pwm_free ,
. get_state = mvebu_pwm_get_state ,
. apply = mvebu_pwm_apply ,
. owner = THIS_MODULE ,
} ;
static void __maybe_unused mvebu_pwm_suspend ( struct mvebu_gpio_chip * mvchip )
{
struct mvebu_pwm * mvpwm = mvchip - > mvpwm ;
2017-06-12 18:34:59 +03:00
regmap_read ( mvchip - > regs , GPIO_BLINK_CNT_SELECT_OFF + mvchip - > offset ,
2017-05-19 19:09:21 +03:00
& mvpwm - > blink_select ) ;
2020-12-02 10:15:34 +03:00
regmap_read ( mvpwm - > regs , mvebu_pwmreg_blink_on_duration ( mvpwm ) ,
& mvpwm - > blink_on_duration ) ;
regmap_read ( mvpwm - > regs , mvebu_pwmreg_blink_off_duration ( mvpwm ) ,
& mvpwm - > blink_off_duration ) ;
2017-04-14 18:40:52 +03:00
}
static void __maybe_unused mvebu_pwm_resume ( struct mvebu_gpio_chip * mvchip )
{
struct mvebu_pwm * mvpwm = mvchip - > mvpwm ;
2017-06-12 18:34:59 +03:00
regmap_write ( mvchip - > regs , GPIO_BLINK_CNT_SELECT_OFF + mvchip - > offset ,
2017-05-19 19:09:21 +03:00
mvpwm - > blink_select ) ;
2020-12-02 10:15:34 +03:00
regmap_write ( mvpwm - > regs , mvebu_pwmreg_blink_on_duration ( mvpwm ) ,
mvpwm - > blink_on_duration ) ;
regmap_write ( mvpwm - > regs , mvebu_pwmreg_blink_off_duration ( mvpwm ) ,
mvpwm - > blink_off_duration ) ;
2017-04-14 18:40:52 +03:00
}
static int mvebu_pwm_probe ( struct platform_device * pdev ,
struct mvebu_gpio_chip * mvchip ,
int id )
{
struct device * dev = & pdev - > dev ;
struct mvebu_pwm * mvpwm ;
2020-12-02 10:15:34 +03:00
void __iomem * base ;
2021-01-11 14:46:27 +03:00
u32 offset ;
2017-04-14 18:40:52 +03:00
u32 set ;
2022-07-14 21:33:25 +03:00
if ( mvchip - > soc_variant = = MVEBU_GPIO_SOC_VARIANT_A8K ) {
int ret = of_property_read_u32 ( dev - > of_node ,
" marvell,pwm-offset " , & offset ) ;
if ( ret < 0 )
return 0 ;
} else {
2021-01-11 14:46:27 +03:00
/*
* There are only two sets of PWM configuration registers for
* all the GPIO lines on those SoCs which this driver reserves
* for the first two GPIO chips . So if the resource is missing
* we can ' t treat it as an error .
*/
if ( ! platform_get_resource_byname ( pdev , IORESOURCE_MEM , " pwm " ) )
return 0 ;
offset = 0 ;
}
2020-04-17 12:21:57 +03:00
2018-12-17 11:43:13 +03:00
if ( IS_ERR ( mvchip - > clk ) )
return PTR_ERR ( mvchip - > clk ) ;
2017-04-14 18:40:52 +03:00
mvpwm = devm_kzalloc ( dev , sizeof ( struct mvebu_pwm ) , GFP_KERNEL ) ;
if ( ! mvpwm )
return - ENOMEM ;
mvchip - > mvpwm = mvpwm ;
mvpwm - > mvchip = mvchip ;
2021-01-11 14:46:27 +03:00
mvpwm - > offset = offset ;
if ( mvchip - > soc_variant = = MVEBU_GPIO_SOC_VARIANT_A8K ) {
mvpwm - > regs = mvchip - > regs ;
switch ( mvchip - > offset ) {
case AP80X_GPIO0_OFF_A8K :
case CP11X_GPIO0_OFF_A8K :
/* Blink counter A */
set = 0 ;
break ;
case CP11X_GPIO1_OFF_A8K :
/* Blink counter B */
set = U32_MAX ;
mvpwm - > offset + = PWM_BLINK_COUNTER_B_OFF ;
break ;
default :
return - EINVAL ;
}
} else {
base = devm_platform_ioremap_resource_byname ( pdev , " pwm " ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
2017-04-14 18:40:52 +03:00
2021-01-11 14:46:27 +03:00
mvpwm - > regs = devm_regmap_init_mmio ( & pdev - > dev , base ,
& mvebu_gpio_regmap_config ) ;
if ( IS_ERR ( mvpwm - > regs ) )
return PTR_ERR ( mvpwm - > regs ) ;
2020-12-02 10:15:34 +03:00
2021-01-11 14:46:27 +03:00
/*
* Use set A for lines of GPIO chip with id 0 , B for GPIO chip
* with id 1. Don ' t allow further GPIO chips to be used for PWM .
*/
if ( id = = 0 )
set = 0 ;
else if ( id = = 1 )
set = U32_MAX ;
else
return - EINVAL ;
}
regmap_write ( mvchip - > regs ,
GPIO_BLINK_CNT_SELECT_OFF + mvchip - > offset , set ) ;
2017-04-14 18:40:52 +03:00
mvpwm - > clk_rate = clk_get_rate ( mvchip - > clk ) ;
if ( ! mvpwm - > clk_rate ) {
dev_err ( dev , " failed to get clock rate \n " ) ;
return - EINVAL ;
}
mvpwm - > chip . dev = dev ;
mvpwm - > chip . ops = & mvebu_pwm_ops ;
mvpwm - > chip . npwm = mvchip - > chip . ngpio ;
spin_lock_init ( & mvpwm - > lock ) ;
2023-07-17 17:27:43 +03:00
return devm_pwmchip_add ( dev , & mvpwm - > chip ) ;
2017-04-14 18:40:52 +03:00
}
2013-03-24 18:45:29 +04:00
# ifdef CONFIG_DEBUG_FS
# include <linux/seq_file.h>
static void mvebu_gpio_dbg_show ( struct seq_file * s , struct gpio_chip * chip )
{
2015-12-07 13:09:24 +03:00
struct mvebu_gpio_chip * mvchip = gpiochip_get_data ( chip ) ;
2013-03-24 18:45:29 +04:00
u32 out , io_conf , blink , in_pol , data_in , cause , edg_msk , lvl_msk ;
2020-06-15 18:05:43 +03:00
const char * label ;
2013-03-24 18:45:29 +04:00
int i ;
2017-06-12 18:34:59 +03:00
regmap_read ( mvchip - > regs , GPIO_OUT_OFF + mvchip - > offset , & out ) ;
regmap_read ( mvchip - > regs , GPIO_IO_CONF_OFF + mvchip - > offset , & io_conf ) ;
regmap_read ( mvchip - > regs , GPIO_BLINK_EN_OFF + mvchip - > offset , & blink ) ;
regmap_read ( mvchip - > regs , GPIO_IN_POL_OFF + mvchip - > offset , & in_pol ) ;
regmap_read ( mvchip - > regs , GPIO_DATA_IN_OFF + mvchip - > offset , & data_in ) ;
2017-05-19 19:09:21 +03:00
cause = mvebu_gpio_read_edge_cause ( mvchip ) ;
edg_msk = mvebu_gpio_read_edge_mask ( mvchip ) ;
lvl_msk = mvebu_gpio_read_level_mask ( mvchip ) ;
2013-03-24 18:45:29 +04:00
2020-06-15 18:05:43 +03:00
for_each_requested_gpio ( chip , i , label ) {
2013-03-24 18:45:29 +04:00
u32 msk ;
bool is_out ;
2017-03-17 20:44:06 +03:00
msk = BIT ( i ) ;
2013-03-24 18:45:29 +04:00
is_out = ! ( io_conf & msk ) ;
seq_printf ( s , " gpio-%-3d (%-20.20s) " , chip - > base + i , label ) ;
if ( is_out ) {
seq_printf ( s , " out %s %s \n " ,
out & msk ? " hi " : " lo " ,
blink & msk ? " (blink ) " : " " ) ;
continue ;
}
seq_printf ( s , " in %s (act %s) - IRQ " ,
( data_in ^ in_pol ) & msk ? " hi " : " lo " ,
in_pol & msk ? " lo " : " hi " ) ;
if ( ! ( ( edg_msk | lvl_msk ) & msk ) ) {
2015-01-10 02:34:47 +03:00
seq_puts ( s , " disabled \n " ) ;
2013-03-24 18:45:29 +04:00
continue ;
}
if ( edg_msk & msk )
2015-01-10 02:34:47 +03:00
seq_puts ( s , " edge " ) ;
2013-03-24 18:45:29 +04:00
if ( lvl_msk & msk )
2015-01-10 02:34:47 +03:00
seq_puts ( s , " level " ) ;
2013-03-24 18:45:29 +04:00
seq_printf ( s , " (%s) \n " , cause & msk ? " pending " : " clear " ) ;
}
}
# else
# define mvebu_gpio_dbg_show NULL
# endif
2014-05-07 13:06:08 +04:00
static const struct of_device_id mvebu_gpio_of_match [ ] = {
2012-09-20 00:52:58 +04:00
{
. compatible = " marvell,orion-gpio " ,
2015-01-10 02:34:47 +03:00
. data = ( void * ) MVEBU_GPIO_SOC_VARIANT_ORION ,
2012-09-20 00:52:58 +04:00
} ,
{
. compatible = " marvell,mv78200-gpio " ,
2015-01-10 02:34:47 +03:00
. data = ( void * ) MVEBU_GPIO_SOC_VARIANT_MV78200 ,
2012-09-20 00:52:58 +04:00
} ,
{
. compatible = " marvell,armadaxp-gpio " ,
2015-01-10 02:34:47 +03:00
. data = ( void * ) MVEBU_GPIO_SOC_VARIANT_ARMADAXP ,
2012-09-20 00:52:58 +04:00
} ,
2017-04-14 18:40:52 +03:00
{
2017-06-01 23:08:20 +03:00
. compatible = " marvell,armada-370-gpio " ,
2017-04-14 18:40:52 +03:00
. data = ( void * ) MVEBU_GPIO_SOC_VARIANT_ORION ,
} ,
2017-06-12 18:34:59 +03:00
{
. compatible = " marvell,armada-8k-gpio " ,
. data = ( void * ) MVEBU_GPIO_SOC_VARIANT_A8K ,
} ,
2012-09-20 00:52:58 +04:00
{
/* sentinel */
} ,
} ;
2014-10-24 15:59:19 +04:00
static int mvebu_gpio_suspend ( struct platform_device * pdev , pm_message_t state )
{
struct mvebu_gpio_chip * mvchip = platform_get_drvdata ( pdev ) ;
int i ;
2017-06-12 18:34:59 +03:00
regmap_read ( mvchip - > regs , GPIO_OUT_OFF + mvchip - > offset ,
& mvchip - > out_reg ) ;
regmap_read ( mvchip - > regs , GPIO_IO_CONF_OFF + mvchip - > offset ,
& mvchip - > io_conf_reg ) ;
regmap_read ( mvchip - > regs , GPIO_BLINK_EN_OFF + mvchip - > offset ,
& mvchip - > blink_en_reg ) ;
regmap_read ( mvchip - > regs , GPIO_IN_POL_OFF + mvchip - > offset ,
& mvchip - > in_pol_reg ) ;
2014-10-24 15:59:19 +04:00
switch ( mvchip - > soc_variant ) {
case MVEBU_GPIO_SOC_VARIANT_ORION :
2017-06-12 18:34:59 +03:00
case MVEBU_GPIO_SOC_VARIANT_A8K :
regmap_read ( mvchip - > regs , GPIO_EDGE_MASK_OFF + mvchip - > offset ,
2017-05-19 19:09:21 +03:00
& mvchip - > edge_mask_regs [ 0 ] ) ;
2017-06-12 18:34:59 +03:00
regmap_read ( mvchip - > regs , GPIO_LEVEL_MASK_OFF + mvchip - > offset ,
2017-05-19 19:09:21 +03:00
& mvchip - > level_mask_regs [ 0 ] ) ;
2014-10-24 15:59:19 +04:00
break ;
case MVEBU_GPIO_SOC_VARIANT_MV78200 :
for ( i = 0 ; i < 2 ; i + + ) {
2017-05-19 19:09:21 +03:00
regmap_read ( mvchip - > regs ,
GPIO_EDGE_MASK_MV78200_OFF ( i ) ,
& mvchip - > edge_mask_regs [ i ] ) ;
regmap_read ( mvchip - > regs ,
GPIO_LEVEL_MASK_MV78200_OFF ( i ) ,
& mvchip - > level_mask_regs [ i ] ) ;
2014-10-24 15:59:19 +04:00
}
break ;
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP :
for ( i = 0 ; i < 4 ; i + + ) {
2017-05-19 19:09:21 +03:00
regmap_read ( mvchip - > regs ,
GPIO_EDGE_MASK_ARMADAXP_OFF ( i ) ,
& mvchip - > edge_mask_regs [ i ] ) ;
regmap_read ( mvchip - > regs ,
GPIO_LEVEL_MASK_ARMADAXP_OFF ( i ) ,
& mvchip - > level_mask_regs [ i ] ) ;
2014-10-24 15:59:19 +04:00
}
break ;
default :
BUG ( ) ;
}
2023-01-20 10:53:33 +03:00
if ( IS_REACHABLE ( CONFIG_PWM ) )
2017-04-14 18:40:52 +03:00
mvebu_pwm_suspend ( mvchip ) ;
2014-10-24 15:59:19 +04:00
return 0 ;
}
static int mvebu_gpio_resume ( struct platform_device * pdev )
{
struct mvebu_gpio_chip * mvchip = platform_get_drvdata ( pdev ) ;
int i ;
2017-06-12 18:34:59 +03:00
regmap_write ( mvchip - > regs , GPIO_OUT_OFF + mvchip - > offset ,
mvchip - > out_reg ) ;
regmap_write ( mvchip - > regs , GPIO_IO_CONF_OFF + mvchip - > offset ,
mvchip - > io_conf_reg ) ;
regmap_write ( mvchip - > regs , GPIO_BLINK_EN_OFF + mvchip - > offset ,
mvchip - > blink_en_reg ) ;
regmap_write ( mvchip - > regs , GPIO_IN_POL_OFF + mvchip - > offset ,
mvchip - > in_pol_reg ) ;
2014-10-24 15:59:19 +04:00
switch ( mvchip - > soc_variant ) {
case MVEBU_GPIO_SOC_VARIANT_ORION :
2017-06-12 18:34:59 +03:00
case MVEBU_GPIO_SOC_VARIANT_A8K :
regmap_write ( mvchip - > regs , GPIO_EDGE_MASK_OFF + mvchip - > offset ,
2017-05-19 19:09:21 +03:00
mvchip - > edge_mask_regs [ 0 ] ) ;
2017-06-12 18:34:59 +03:00
regmap_write ( mvchip - > regs , GPIO_LEVEL_MASK_OFF + mvchip - > offset ,
2017-05-19 19:09:21 +03:00
mvchip - > level_mask_regs [ 0 ] ) ;
2014-10-24 15:59:19 +04:00
break ;
case MVEBU_GPIO_SOC_VARIANT_MV78200 :
for ( i = 0 ; i < 2 ; i + + ) {
2017-05-19 19:09:21 +03:00
regmap_write ( mvchip - > regs ,
GPIO_EDGE_MASK_MV78200_OFF ( i ) ,
mvchip - > edge_mask_regs [ i ] ) ;
regmap_write ( mvchip - > regs ,
GPIO_LEVEL_MASK_MV78200_OFF ( i ) ,
mvchip - > level_mask_regs [ i ] ) ;
2014-10-24 15:59:19 +04:00
}
break ;
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP :
for ( i = 0 ; i < 4 ; i + + ) {
2017-05-19 19:09:21 +03:00
regmap_write ( mvchip - > regs ,
GPIO_EDGE_MASK_ARMADAXP_OFF ( i ) ,
mvchip - > edge_mask_regs [ i ] ) ;
regmap_write ( mvchip - > regs ,
GPIO_LEVEL_MASK_ARMADAXP_OFF ( i ) ,
mvchip - > level_mask_regs [ i ] ) ;
2014-10-24 15:59:19 +04:00
}
break ;
default :
BUG ( ) ;
}
2023-01-20 10:53:33 +03:00
if ( IS_REACHABLE ( CONFIG_PWM ) )
2017-04-14 18:40:52 +03:00
mvebu_pwm_resume ( mvchip ) ;
2014-10-24 15:59:19 +04:00
return 0 ;
}
2017-06-12 18:34:59 +03:00
static int mvebu_gpio_probe_raw ( struct platform_device * pdev ,
struct mvebu_gpio_chip * mvchip )
{
void __iomem * base ;
2019-03-11 21:55:00 +03:00
base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2017-06-12 18:34:59 +03:00
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
mvchip - > regs = devm_regmap_init_mmio ( & pdev - > dev , base ,
& mvebu_gpio_regmap_config ) ;
if ( IS_ERR ( mvchip - > regs ) )
return PTR_ERR ( mvchip - > regs ) ;
/*
* For the legacy SoCs , the regmap directly maps to the GPIO
* registers , so no offset is needed .
*/
mvchip - > offset = 0 ;
/*
* The Armada XP has a second range of registers for the
* per - CPU registers
*/
if ( mvchip - > soc_variant = = MVEBU_GPIO_SOC_VARIANT_ARMADAXP ) {
2019-03-11 21:55:00 +03:00
base = devm_platform_ioremap_resource ( pdev , 1 ) ;
2017-06-12 18:34:59 +03:00
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
mvchip - > percpu_regs =
devm_regmap_init_mmio ( & pdev - > dev , base ,
& mvebu_gpio_regmap_config ) ;
if ( IS_ERR ( mvchip - > percpu_regs ) )
return PTR_ERR ( mvchip - > percpu_regs ) ;
}
return 0 ;
}
static int mvebu_gpio_probe_syscon ( struct platform_device * pdev ,
struct mvebu_gpio_chip * mvchip )
{
mvchip - > regs = syscon_node_to_regmap ( pdev - > dev . parent - > of_node ) ;
if ( IS_ERR ( mvchip - > regs ) )
return PTR_ERR ( mvchip - > regs ) ;
if ( of_property_read_u32 ( pdev - > dev . of_node , " offset " , & mvchip - > offset ) )
return - EINVAL ;
return 0 ;
}
2023-07-19 14:41:01 +03:00
static void mvebu_gpio_remove_irq_domain ( void * data )
{
struct irq_domain * domain = data ;
irq_domain_remove ( domain ) ;
}
2012-11-19 22:22:34 +04:00
static int mvebu_gpio_probe ( struct platform_device * pdev )
2012-09-20 00:52:58 +04:00
{
struct mvebu_gpio_chip * mvchip ;
const struct of_device_id * match ;
struct device_node * np = pdev - > dev . of_node ;
struct irq_chip_generic * gc ;
struct irq_chip_type * ct ;
unsigned int ngpios ;
2016-10-20 00:03:41 +03:00
bool have_irqs ;
2012-09-20 00:52:58 +04:00
int soc_variant ;
int i , cpu , id ;
2015-01-10 02:34:48 +03:00
int err ;
2012-09-20 00:52:58 +04:00
match = of_match_device ( mvebu_gpio_of_match , & pdev - > dev ) ;
if ( match )
2017-01-11 01:53:28 +03:00
soc_variant = ( unsigned long ) match - > data ;
2012-09-20 00:52:58 +04:00
else
soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION ;
2016-10-20 00:03:41 +03:00
/* Some gpio controllers do not provide irq support */
2019-12-04 12:24:35 +03:00
err = platform_irq_count ( pdev ) ;
if ( err < 0 )
return err ;
have_irqs = err ! = 0 ;
2016-10-20 00:03:41 +03:00
2015-01-10 02:34:47 +03:00
mvchip = devm_kzalloc ( & pdev - > dev , sizeof ( struct mvebu_gpio_chip ) ,
GFP_KERNEL ) ;
2014-04-29 12:38:21 +04:00
if ( ! mvchip )
2012-09-20 00:52:58 +04:00
return - ENOMEM ;
2014-10-24 15:59:19 +04:00
platform_set_drvdata ( pdev , mvchip ) ;
2012-09-20 00:52:58 +04:00
if ( of_property_read_u32 ( pdev - > dev . of_node , " ngpios " , & ngpios ) ) {
dev_err ( & pdev - > dev , " Missing ngpios OF property \n " ) ;
return - ENODEV ;
}
id = of_alias_get_id ( pdev - > dev . of_node , " gpio " ) ;
if ( id < 0 ) {
dev_err ( & pdev - > dev , " Couldn't get OF id \n " ) ;
return id ;
}
2017-04-14 18:40:52 +03:00
mvchip - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2013-02-03 14:34:26 +04:00
/* Not all SoCs require a clock.*/
2017-04-14 18:40:52 +03:00
if ( ! IS_ERR ( mvchip - > clk ) )
clk_prepare_enable ( mvchip - > clk ) ;
2013-02-03 14:34:26 +04:00
2012-09-20 00:52:58 +04:00
mvchip - > soc_variant = soc_variant ;
mvchip - > chip . label = dev_name ( & pdev - > dev ) ;
2015-11-04 11:56:26 +03:00
mvchip - > chip . parent = & pdev - > dev ;
2015-10-11 18:34:16 +03:00
mvchip - > chip . request = gpiochip_generic_request ;
mvchip - > chip . free = gpiochip_generic_free ;
2019-01-10 15:26:21 +03:00
mvchip - > chip . get_direction = mvebu_gpio_get_direction ;
2012-09-20 00:52:58 +04:00
mvchip - > chip . direction_input = mvebu_gpio_direction_input ;
mvchip - > chip . get = mvebu_gpio_get ;
mvchip - > chip . direction_output = mvebu_gpio_direction_output ;
mvchip - > chip . set = mvebu_gpio_set ;
2016-10-20 00:03:41 +03:00
if ( have_irqs )
mvchip - > chip . to_irq = mvebu_gpio_to_irq ;
2012-09-20 00:52:58 +04:00
mvchip - > chip . base = id * MVEBU_MAX_GPIO_PER_BANK ;
mvchip - > chip . ngpio = ngpios ;
2013-12-04 17:42:46 +04:00
mvchip - > chip . can_sleep = false ;
2013-03-24 18:45:29 +04:00
mvchip - > chip . dbg_show = mvebu_gpio_dbg_show ;
2012-09-20 00:52:58 +04:00
2017-06-12 18:34:59 +03:00
if ( soc_variant = = MVEBU_GPIO_SOC_VARIANT_A8K )
err = mvebu_gpio_probe_syscon ( pdev , mvchip ) ;
else
err = mvebu_gpio_probe_raw ( pdev , mvchip ) ;
2012-09-20 00:52:58 +04:00
2017-06-12 18:34:59 +03:00
if ( err )
return err ;
2012-09-20 00:52:58 +04:00
/*
* Mask and clear GPIO interrupts .
*/
2013-03-20 16:15:56 +04:00
switch ( soc_variant ) {
2012-09-20 00:52:58 +04:00
case MVEBU_GPIO_SOC_VARIANT_ORION :
2017-06-12 18:34:59 +03:00
case MVEBU_GPIO_SOC_VARIANT_A8K :
regmap_write ( mvchip - > regs ,
GPIO_EDGE_CAUSE_OFF + mvchip - > offset , 0 ) ;
regmap_write ( mvchip - > regs ,
GPIO_EDGE_MASK_OFF + mvchip - > offset , 0 ) ;
regmap_write ( mvchip - > regs ,
GPIO_LEVEL_MASK_OFF + mvchip - > offset , 0 ) ;
2012-09-20 00:52:58 +04:00
break ;
case MVEBU_GPIO_SOC_VARIANT_MV78200 :
2017-05-19 19:09:21 +03:00
regmap_write ( mvchip - > regs , GPIO_EDGE_CAUSE_OFF , 0 ) ;
2012-09-20 00:52:58 +04:00
for ( cpu = 0 ; cpu < 2 ; cpu + + ) {
2017-05-19 19:09:21 +03:00
regmap_write ( mvchip - > regs ,
GPIO_EDGE_MASK_MV78200_OFF ( cpu ) , 0 ) ;
regmap_write ( mvchip - > regs ,
GPIO_LEVEL_MASK_MV78200_OFF ( cpu ) , 0 ) ;
2012-09-20 00:52:58 +04:00
}
break ;
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP :
2017-05-19 19:09:21 +03:00
regmap_write ( mvchip - > regs , GPIO_EDGE_CAUSE_OFF , 0 ) ;
regmap_write ( mvchip - > regs , GPIO_EDGE_MASK_OFF , 0 ) ;
regmap_write ( mvchip - > regs , GPIO_LEVEL_MASK_OFF , 0 ) ;
2012-09-20 00:52:58 +04:00
for ( cpu = 0 ; cpu < 4 ; cpu + + ) {
2017-05-19 19:09:21 +03:00
regmap_write ( mvchip - > percpu_regs ,
GPIO_EDGE_CAUSE_ARMADAXP_OFF ( cpu ) , 0 ) ;
regmap_write ( mvchip - > percpu_regs ,
GPIO_EDGE_MASK_ARMADAXP_OFF ( cpu ) , 0 ) ;
regmap_write ( mvchip - > percpu_regs ,
GPIO_LEVEL_MASK_ARMADAXP_OFF ( cpu ) , 0 ) ;
2012-09-20 00:52:58 +04:00
}
break ;
default :
BUG ( ) ;
}
2016-02-22 15:13:28 +03:00
devm_gpiochip_add_data ( & pdev - > dev , & mvchip - > chip , mvchip ) ;
2012-09-20 00:52:58 +04:00
2020-12-02 10:15:32 +03:00
/* Some MVEBU SoCs have simple PWM support for GPIO lines */
2023-01-20 10:53:33 +03:00
if ( IS_REACHABLE ( CONFIG_PWM ) ) {
2020-12-02 10:15:32 +03:00
err = mvebu_pwm_probe ( pdev , mvchip , id ) ;
if ( err )
return err ;
}
2012-09-20 00:52:58 +04:00
/* Some gpio controllers do not provide irq support */
2016-10-20 00:03:41 +03:00
if ( ! have_irqs )
2012-09-20 00:52:58 +04:00
return 0 ;
2016-10-20 00:03:41 +03:00
mvchip - > domain =
irq_domain_add_linear ( np , ngpios , & irq_generic_chip_ops , NULL ) ;
if ( ! mvchip - > domain ) {
dev_err ( & pdev - > dev , " couldn't allocate irq domain %s (DT). \n " ,
mvchip - > chip . label ) ;
2023-07-17 17:27:43 +03:00
return - ENODEV ;
2012-09-20 00:52:58 +04:00
}
2023-07-19 14:41:01 +03:00
err = devm_add_action_or_reset ( & pdev - > dev , mvebu_gpio_remove_irq_domain ,
mvchip - > domain ) ;
if ( err )
return err ;
2016-10-20 00:03:41 +03:00
err = irq_alloc_domain_generic_chips (
mvchip - > domain , ngpios , 2 , np - > name , handle_level_irq ,
IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_LEVEL , 0 , 0 ) ;
if ( err ) {
dev_err ( & pdev - > dev , " couldn't allocate irq chips %s (DT). \n " ,
mvchip - > chip . label ) ;
2023-07-19 14:41:01 +03:00
return err ;
2012-09-20 00:52:58 +04:00
}
2017-03-16 09:33:57 +03:00
/*
* NOTE : The common accessors cannot be used because of the percpu
2016-10-20 00:03:41 +03:00
* access to the mask registers
*/
gc = irq_get_domain_generic_chip ( mvchip - > domain , 0 ) ;
2012-09-20 00:52:58 +04:00
gc - > private = mvchip ;
ct = & gc - > chip_types [ 0 ] ;
ct - > type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW ;
ct - > chip . irq_mask = mvebu_gpio_level_irq_mask ;
ct - > chip . irq_unmask = mvebu_gpio_level_irq_unmask ;
ct - > chip . irq_set_type = mvebu_gpio_irq_set_type ;
ct - > chip . name = mvchip - > chip . label ;
ct = & gc - > chip_types [ 1 ] ;
ct - > type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING ;
ct - > chip . irq_ack = mvebu_gpio_irq_ack ;
ct - > chip . irq_mask = mvebu_gpio_edge_irq_mask ;
ct - > chip . irq_unmask = mvebu_gpio_edge_irq_unmask ;
ct - > chip . irq_set_type = mvebu_gpio_irq_set_type ;
ct - > handler = handle_edge_irq ;
ct - > chip . name = mvchip - > chip . label ;
2017-03-16 09:33:57 +03:00
/*
* Setup the interrupt handlers . Each chip can have up to 4
2016-10-20 00:03:41 +03:00
* interrupt handlers , with each handler dealing with 8 GPIO
* pins .
*/
for ( i = 0 ; i < 4 ; i + + ) {
2020-03-13 06:42:44 +03:00
int irq = platform_get_irq_optional ( pdev , i ) ;
2012-09-20 00:52:58 +04:00
2016-10-20 00:03:41 +03:00
if ( irq < 0 )
continue ;
irq_set_chained_handler_and_data ( irq , mvebu_gpio_irq_handler ,
mvchip ) ;
2012-09-20 00:52:58 +04:00
}
return 0 ;
}
static struct platform_driver mvebu_gpio_driver = {
. driver = {
2015-01-10 02:34:47 +03:00
. name = " mvebu-gpio " ,
2012-09-20 00:52:58 +04:00
. of_match_table = mvebu_gpio_of_match ,
} ,
. probe = mvebu_gpio_probe ,
2014-10-24 15:59:19 +04:00
. suspend = mvebu_gpio_suspend ,
. resume = mvebu_gpio_resume ,
2012-09-20 00:52:58 +04:00
} ;
2016-03-27 18:44:45 +03:00
builtin_platform_driver ( mvebu_gpio_driver ) ;