2015-03-09 23:45:00 +03:00
/*
* Copyright ( C ) 2014 - 2015 Broadcom Corporation
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed " as is " WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* This file contains the Broadcom Cygnus GPIO driver that supports 3
* GPIO controllers on Cygnus including the ASIU GPIO controller , the
* chipCommonG GPIO controller , and the always - on GPIO controller . Basic
* PINCONF such as bias pull up / down , and drive strength are also supported
* in this driver .
*
* Pins from the ASIU GPIO can be individually muxed to GPIO function ,
* through the interaction with the Cygnus IOMUX controller
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/gpio.h>
# include <linux/ioport.h>
# include <linux/of_device.h>
# include <linux/of_irq.h>
# include <linux/pinctrl/pinctrl.h>
# include <linux/pinctrl/pinconf.h>
# include <linux/pinctrl/pinconf-generic.h>
# include "../pinctrl-utils.h"
# define CYGNUS_GPIO_DATA_IN_OFFSET 0x00
# define CYGNUS_GPIO_DATA_OUT_OFFSET 0x04
# define CYGNUS_GPIO_OUT_EN_OFFSET 0x08
2015-05-14 03:06:22 +03:00
# define CYGNUS_GPIO_INT_TYPE_OFFSET 0x0c
2015-03-09 23:45:00 +03:00
# define CYGNUS_GPIO_INT_DE_OFFSET 0x10
# define CYGNUS_GPIO_INT_EDGE_OFFSET 0x14
# define CYGNUS_GPIO_INT_MSK_OFFSET 0x18
# define CYGNUS_GPIO_INT_STAT_OFFSET 0x1c
# define CYGNUS_GPIO_INT_MSTAT_OFFSET 0x20
# define CYGNUS_GPIO_INT_CLR_OFFSET 0x24
# define CYGNUS_GPIO_PAD_RES_OFFSET 0x34
# define CYGNUS_GPIO_RES_EN_OFFSET 0x38
/* drive strength control for ASIU GPIO */
# define CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58
/* drive strength control for CCM/CRMU (AON) GPIO */
# define CYGNUS_GPIO_DRV0_CTRL_OFFSET 0x00
# define GPIO_BANK_SIZE 0x200
# define NGPIOS_PER_BANK 32
# define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
# define CYGNUS_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg))
# define CYGNUS_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK)
# define GPIO_DRV_STRENGTH_BIT_SHIFT 20
# define GPIO_DRV_STRENGTH_BITS 3
# define GPIO_DRV_STRENGTH_BIT_MASK ((1 << GPIO_DRV_STRENGTH_BITS) - 1)
/*
* Cygnus GPIO core
*
* @ dev : pointer to device
* @ base : I / O register base for Cygnus GPIO controller
* @ io_ctrl : I / O register base for certain type of Cygnus GPIO controller that
* has the PINCONF support implemented outside of the GPIO block
* @ lock : lock to protect access to I / O registers
* @ gc : GPIO chip
* @ num_banks : number of GPIO banks , each bank supports up to 32 GPIOs
* @ pinmux_is_supported : flag to indicate this GPIO controller contains pins
* that can be individually muxed to GPIO
* @ pctl : pointer to pinctrl_dev
* @ pctldesc : pinctrl descriptor
*/
struct cygnus_gpio {
struct device * dev ;
void __iomem * base ;
void __iomem * io_ctrl ;
spinlock_t lock ;
struct gpio_chip gc ;
unsigned num_banks ;
bool pinmux_is_supported ;
struct pinctrl_dev * pctl ;
struct pinctrl_desc pctldesc ;
} ;
static inline struct cygnus_gpio * to_cygnus_gpio ( struct gpio_chip * gc )
{
return container_of ( gc , struct cygnus_gpio , gc ) ;
}
/*
* Mapping from PINCONF pins to GPIO pins is 1 - to - 1
*/
static inline unsigned cygnus_pin_to_gpio ( unsigned pin )
{
return pin ;
}
/**
* cygnus_set_bit - set or clear one bit ( corresponding to the GPIO pin ) in a
* Cygnus GPIO register
*
* @ cygnus_gpio : Cygnus GPIO device
* @ reg : register offset
* @ gpio : GPIO pin
* @ set : set or clear
*/
static inline void cygnus_set_bit ( struct cygnus_gpio * chip , unsigned int reg ,
unsigned gpio , bool set )
{
unsigned int offset = CYGNUS_GPIO_REG ( gpio , reg ) ;
unsigned int shift = CYGNUS_GPIO_SHIFT ( gpio ) ;
u32 val ;
val = readl ( chip - > base + offset ) ;
if ( set )
val | = BIT ( shift ) ;
else
val & = ~ BIT ( shift ) ;
writel ( val , chip - > base + offset ) ;
}
static inline bool cygnus_get_bit ( struct cygnus_gpio * chip , unsigned int reg ,
unsigned gpio )
{
unsigned int offset = CYGNUS_GPIO_REG ( gpio , reg ) ;
unsigned int shift = CYGNUS_GPIO_SHIFT ( gpio ) ;
return ! ! ( readl ( chip - > base + offset ) & BIT ( shift ) ) ;
}
2015-09-14 11:42:37 +03:00
static void cygnus_gpio_irq_handler ( struct irq_desc * desc )
2015-03-09 23:45:00 +03:00
{
struct gpio_chip * gc = irq_desc_get_handler_data ( desc ) ;
struct cygnus_gpio * chip = to_cygnus_gpio ( gc ) ;
struct irq_chip * irq_chip = irq_desc_get_chip ( desc ) ;
int i , bit ;
chained_irq_enter ( irq_chip , desc ) ;
/* go through the entire GPIO banks and handle all interrupts */
for ( i = 0 ; i < chip - > num_banks ; i + + ) {
unsigned long val = readl ( chip - > base + ( i * GPIO_BANK_SIZE ) +
CYGNUS_GPIO_INT_MSTAT_OFFSET ) ;
for_each_set_bit ( bit , & val , NGPIOS_PER_BANK ) {
unsigned pin = NGPIOS_PER_BANK * i + bit ;
int child_irq = irq_find_mapping ( gc - > irqdomain , pin ) ;
/*
* Clear the interrupt before invoking the
* handler , so we do not leave any window
*/
writel ( BIT ( bit ) , chip - > base + ( i * GPIO_BANK_SIZE ) +
CYGNUS_GPIO_INT_CLR_OFFSET ) ;
generic_handle_irq ( child_irq ) ;
}
}
chained_irq_exit ( irq_chip , desc ) ;
}
static void cygnus_gpio_irq_ack ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct cygnus_gpio * chip = to_cygnus_gpio ( gc ) ;
unsigned gpio = d - > hwirq ;
unsigned int offset = CYGNUS_GPIO_REG ( gpio ,
CYGNUS_GPIO_INT_CLR_OFFSET ) ;
unsigned int shift = CYGNUS_GPIO_SHIFT ( gpio ) ;
u32 val = BIT ( shift ) ;
writel ( val , chip - > base + offset ) ;
}
/**
* cygnus_gpio_irq_set_mask - mask / unmask a GPIO interrupt
*
* @ d : IRQ chip data
* @ unmask : mask / unmask GPIO interrupt
*/
static void cygnus_gpio_irq_set_mask ( struct irq_data * d , bool unmask )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct cygnus_gpio * chip = to_cygnus_gpio ( gc ) ;
unsigned gpio = d - > hwirq ;
cygnus_set_bit ( chip , CYGNUS_GPIO_INT_MSK_OFFSET , gpio , unmask ) ;
}
static void cygnus_gpio_irq_mask ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct cygnus_gpio * chip = to_cygnus_gpio ( gc ) ;
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
cygnus_gpio_irq_set_mask ( d , false ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
}
static void cygnus_gpio_irq_unmask ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct cygnus_gpio * chip = to_cygnus_gpio ( gc ) ;
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
cygnus_gpio_irq_set_mask ( d , true ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
}
static int cygnus_gpio_irq_set_type ( struct irq_data * d , unsigned int type )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct cygnus_gpio * chip = to_cygnus_gpio ( gc ) ;
unsigned gpio = d - > hwirq ;
bool level_triggered = false ;
bool dual_edge = false ;
bool rising_or_high = false ;
unsigned long flags ;
switch ( type & IRQ_TYPE_SENSE_MASK ) {
case IRQ_TYPE_EDGE_RISING :
rising_or_high = true ;
break ;
case IRQ_TYPE_EDGE_FALLING :
break ;
case IRQ_TYPE_EDGE_BOTH :
dual_edge = true ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
level_triggered = true ;
rising_or_high = true ;
break ;
case IRQ_TYPE_LEVEL_LOW :
level_triggered = true ;
break ;
default :
dev_err ( chip - > dev , " invalid GPIO IRQ type 0x%x \n " ,
type ) ;
return - EINVAL ;
}
spin_lock_irqsave ( & chip - > lock , flags ) ;
2015-05-14 03:06:22 +03:00
cygnus_set_bit ( chip , CYGNUS_GPIO_INT_TYPE_OFFSET , gpio ,
2015-03-09 23:45:00 +03:00
level_triggered ) ;
cygnus_set_bit ( chip , CYGNUS_GPIO_INT_DE_OFFSET , gpio , dual_edge ) ;
cygnus_set_bit ( chip , CYGNUS_GPIO_INT_EDGE_OFFSET , gpio ,
rising_or_high ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
dev_dbg ( chip - > dev ,
" gpio:%u level_triggered:%d dual_edge:%d rising_or_high:%d \n " ,
gpio , level_triggered , dual_edge , rising_or_high ) ;
return 0 ;
}
static struct irq_chip cygnus_gpio_irq_chip = {
. name = " bcm-cygnus-gpio " ,
. irq_ack = cygnus_gpio_irq_ack ,
. irq_mask = cygnus_gpio_irq_mask ,
. irq_unmask = cygnus_gpio_irq_unmask ,
. irq_set_type = cygnus_gpio_irq_set_type ,
} ;
/*
* Request the Cygnus IOMUX pinmux controller to mux individual pins to GPIO
*/
static int cygnus_gpio_request ( struct gpio_chip * gc , unsigned offset )
{
struct cygnus_gpio * chip = to_cygnus_gpio ( gc ) ;
unsigned gpio = gc - > base + offset ;
/* not all Cygnus GPIO pins can be muxed individually */
if ( ! chip - > pinmux_is_supported )
return 0 ;
return pinctrl_request_gpio ( gpio ) ;
}
static void cygnus_gpio_free ( struct gpio_chip * gc , unsigned offset )
{
struct cygnus_gpio * chip = to_cygnus_gpio ( gc ) ;
unsigned gpio = gc - > base + offset ;
if ( ! chip - > pinmux_is_supported )
return ;
pinctrl_free_gpio ( gpio ) ;
}
static int cygnus_gpio_direction_input ( struct gpio_chip * gc , unsigned gpio )
{
struct cygnus_gpio * chip = to_cygnus_gpio ( gc ) ;
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
cygnus_set_bit ( chip , CYGNUS_GPIO_OUT_EN_OFFSET , gpio , false ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
dev_dbg ( chip - > dev , " gpio:%u set input \n " , gpio ) ;
return 0 ;
}
static int cygnus_gpio_direction_output ( struct gpio_chip * gc , unsigned gpio ,
int val )
{
struct cygnus_gpio * chip = to_cygnus_gpio ( gc ) ;
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
cygnus_set_bit ( chip , CYGNUS_GPIO_OUT_EN_OFFSET , gpio , true ) ;
cygnus_set_bit ( chip , CYGNUS_GPIO_DATA_OUT_OFFSET , gpio , ! ! ( val ) ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
dev_dbg ( chip - > dev , " gpio:%u set output, value:%d \n " , gpio , val ) ;
return 0 ;
}
static void cygnus_gpio_set ( struct gpio_chip * gc , unsigned gpio , int val )
{
struct cygnus_gpio * chip = to_cygnus_gpio ( gc ) ;
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
cygnus_set_bit ( chip , CYGNUS_GPIO_DATA_OUT_OFFSET , gpio , ! ! ( val ) ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
dev_dbg ( chip - > dev , " gpio:%u set, value:%d \n " , gpio , val ) ;
}
static int cygnus_gpio_get ( struct gpio_chip * gc , unsigned gpio )
{
struct cygnus_gpio * chip = to_cygnus_gpio ( gc ) ;
unsigned int offset = CYGNUS_GPIO_REG ( gpio ,
CYGNUS_GPIO_DATA_IN_OFFSET ) ;
unsigned int shift = CYGNUS_GPIO_SHIFT ( gpio ) ;
return ! ! ( readl ( chip - > base + offset ) & BIT ( shift ) ) ;
}
static int cygnus_get_groups_count ( struct pinctrl_dev * pctldev )
{
return 1 ;
}
/*
* Only one group : " gpio_grp " , since this local pinctrl device only performs
* GPIO specific PINCONF configurations
*/
static const char * cygnus_get_group_name ( struct pinctrl_dev * pctldev ,
unsigned selector )
{
return " gpio_grp " ;
}
static const struct pinctrl_ops cygnus_pctrl_ops = {
. get_groups_count = cygnus_get_groups_count ,
. get_group_name = cygnus_get_group_name ,
. dt_node_to_map = pinconf_generic_dt_node_to_map_pin ,
. dt_free_map = pinctrl_utils_dt_free_map ,
} ;
static int cygnus_gpio_set_pull ( struct cygnus_gpio * chip , unsigned gpio ,
bool disable , bool pull_up )
{
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
if ( disable ) {
cygnus_set_bit ( chip , CYGNUS_GPIO_RES_EN_OFFSET , gpio , false ) ;
} else {
cygnus_set_bit ( chip , CYGNUS_GPIO_PAD_RES_OFFSET , gpio ,
pull_up ) ;
cygnus_set_bit ( chip , CYGNUS_GPIO_RES_EN_OFFSET , gpio , true ) ;
}
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
dev_dbg ( chip - > dev , " gpio:%u set pullup:%d \n " , gpio , pull_up ) ;
return 0 ;
}
static void cygnus_gpio_get_pull ( struct cygnus_gpio * chip , unsigned gpio ,
bool * disable , bool * pull_up )
{
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
* disable = ! cygnus_get_bit ( chip , CYGNUS_GPIO_RES_EN_OFFSET , gpio ) ;
* pull_up = cygnus_get_bit ( chip , CYGNUS_GPIO_PAD_RES_OFFSET , gpio ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
}
static int cygnus_gpio_set_strength ( struct cygnus_gpio * chip , unsigned gpio ,
unsigned strength )
{
void __iomem * base ;
unsigned int i , offset , shift ;
u32 val ;
unsigned long flags ;
/* make sure drive strength is supported */
if ( strength < 2 | | strength > 16 | | ( strength % 2 ) )
return - ENOTSUPP ;
if ( chip - > io_ctrl ) {
base = chip - > io_ctrl ;
offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET ;
} else {
base = chip - > base ;
offset = CYGNUS_GPIO_REG ( gpio ,
CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET ) ;
}
shift = CYGNUS_GPIO_SHIFT ( gpio ) ;
dev_dbg ( chip - > dev , " gpio:%u set drive strength:%d mA \n " , gpio ,
strength ) ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
strength = ( strength / 2 ) - 1 ;
for ( i = 0 ; i < GPIO_DRV_STRENGTH_BITS ; i + + ) {
val = readl ( base + offset ) ;
val & = ~ BIT ( shift ) ;
val | = ( ( strength > > i ) & 0x1 ) < < shift ;
writel ( val , base + offset ) ;
offset + = 4 ;
}
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
return 0 ;
}
static int cygnus_gpio_get_strength ( struct cygnus_gpio * chip , unsigned gpio ,
u16 * strength )
{
void __iomem * base ;
unsigned int i , offset , shift ;
u32 val ;
unsigned long flags ;
if ( chip - > io_ctrl ) {
base = chip - > io_ctrl ;
offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET ;
} else {
base = chip - > base ;
offset = CYGNUS_GPIO_REG ( gpio ,
CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET ) ;
}
shift = CYGNUS_GPIO_SHIFT ( gpio ) ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
* strength = 0 ;
for ( i = 0 ; i < GPIO_DRV_STRENGTH_BITS ; i + + ) {
val = readl ( base + offset ) & BIT ( shift ) ;
val > > = shift ;
* strength + = ( val < < i ) ;
offset + = 4 ;
}
/* convert to mA */
* strength = ( * strength + 1 ) * 2 ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
return 0 ;
}
static int cygnus_pin_config_get ( struct pinctrl_dev * pctldev , unsigned pin ,
unsigned long * config )
{
struct cygnus_gpio * chip = pinctrl_dev_get_drvdata ( pctldev ) ;
enum pin_config_param param = pinconf_to_config_param ( * config ) ;
unsigned gpio = cygnus_pin_to_gpio ( pin ) ;
u16 arg ;
bool disable , pull_up ;
int ret ;
switch ( param ) {
case PIN_CONFIG_BIAS_DISABLE :
cygnus_gpio_get_pull ( chip , gpio , & disable , & pull_up ) ;
if ( disable )
return 0 ;
else
return - EINVAL ;
case PIN_CONFIG_BIAS_PULL_UP :
cygnus_gpio_get_pull ( chip , gpio , & disable , & pull_up ) ;
if ( ! disable & & pull_up )
return 0 ;
else
return - EINVAL ;
case PIN_CONFIG_BIAS_PULL_DOWN :
cygnus_gpio_get_pull ( chip , gpio , & disable , & pull_up ) ;
if ( ! disable & & ! pull_up )
return 0 ;
else
return - EINVAL ;
case PIN_CONFIG_DRIVE_STRENGTH :
ret = cygnus_gpio_get_strength ( chip , gpio , & arg ) ;
if ( ret )
return ret ;
else
* config = pinconf_to_config_packed ( param , arg ) ;
return 0 ;
default :
return - ENOTSUPP ;
}
return - ENOTSUPP ;
}
static int cygnus_pin_config_set ( struct pinctrl_dev * pctldev , unsigned pin ,
unsigned long * configs , unsigned num_configs )
{
struct cygnus_gpio * chip = pinctrl_dev_get_drvdata ( pctldev ) ;
enum pin_config_param param ;
u16 arg ;
unsigned i , gpio = cygnus_pin_to_gpio ( pin ) ;
int ret = - ENOTSUPP ;
for ( i = 0 ; i < num_configs ; i + + ) {
param = pinconf_to_config_param ( configs [ i ] ) ;
arg = pinconf_to_config_argument ( configs [ i ] ) ;
switch ( param ) {
case PIN_CONFIG_BIAS_DISABLE :
ret = cygnus_gpio_set_pull ( chip , gpio , true , false ) ;
if ( ret < 0 )
goto out ;
break ;
case PIN_CONFIG_BIAS_PULL_UP :
ret = cygnus_gpio_set_pull ( chip , gpio , false , true ) ;
if ( ret < 0 )
goto out ;
break ;
case PIN_CONFIG_BIAS_PULL_DOWN :
ret = cygnus_gpio_set_pull ( chip , gpio , false , false ) ;
if ( ret < 0 )
goto out ;
break ;
case PIN_CONFIG_DRIVE_STRENGTH :
ret = cygnus_gpio_set_strength ( chip , gpio , arg ) ;
if ( ret < 0 )
goto out ;
break ;
default :
dev_err ( chip - > dev , " invalid configuration \n " ) ;
return - ENOTSUPP ;
}
} /* for each config */
out :
return ret ;
}
static const struct pinconf_ops cygnus_pconf_ops = {
. is_generic = true ,
. pin_config_get = cygnus_pin_config_get ,
. pin_config_set = cygnus_pin_config_set ,
} ;
/*
* Cygnus GPIO controller supports some PINCONF related configurations such as
* pull up , pull down , and drive strength , when the pin is configured to GPIO
*
* Here a local pinctrl device is created with simple 1 - to - 1 pin mapping to the
* local GPIO pins
*/
static int cygnus_gpio_register_pinconf ( struct cygnus_gpio * chip )
{
struct pinctrl_desc * pctldesc = & chip - > pctldesc ;
struct pinctrl_pin_desc * pins ;
struct gpio_chip * gc = & chip - > gc ;
int i ;
pins = devm_kcalloc ( chip - > dev , gc - > ngpio , sizeof ( * pins ) , GFP_KERNEL ) ;
if ( ! pins )
return - ENOMEM ;
for ( i = 0 ; i < gc - > ngpio ; i + + ) {
pins [ i ] . number = i ;
pins [ i ] . name = devm_kasprintf ( chip - > dev , GFP_KERNEL ,
" gpio-%d " , i ) ;
if ( ! pins [ i ] . name )
return - ENOMEM ;
}
pctldesc - > name = dev_name ( chip - > dev ) ;
pctldesc - > pctlops = & cygnus_pctrl_ops ;
pctldesc - > pins = pins ;
pctldesc - > npins = gc - > ngpio ;
pctldesc - > confops = & cygnus_pconf_ops ;
chip - > pctl = pinctrl_register ( pctldesc , chip - > dev , chip ) ;
2015-06-09 07:01:16 +03:00
if ( IS_ERR ( chip - > pctl ) ) {
2015-03-09 23:45:00 +03:00
dev_err ( chip - > dev , " unable to register pinctrl device \n " ) ;
2015-06-09 07:01:16 +03:00
return PTR_ERR ( chip - > pctl ) ;
2015-03-09 23:45:00 +03:00
}
return 0 ;
}
static void cygnus_gpio_unregister_pinconf ( struct cygnus_gpio * chip )
{
if ( chip - > pctl )
pinctrl_unregister ( chip - > pctl ) ;
}
struct cygnus_gpio_data {
unsigned num_gpios ;
} ;
static const struct cygnus_gpio_data cygnus_cmm_gpio_data = {
. num_gpios = 24 ,
} ;
static const struct cygnus_gpio_data cygnus_asiu_gpio_data = {
. num_gpios = 146 ,
} ;
static const struct cygnus_gpio_data cygnus_crmu_gpio_data = {
. num_gpios = 6 ,
} ;
static const struct of_device_id cygnus_gpio_of_match [ ] = {
{
. compatible = " brcm,cygnus-ccm-gpio " ,
. data = & cygnus_cmm_gpio_data ,
} ,
{
. compatible = " brcm,cygnus-asiu-gpio " ,
. data = & cygnus_asiu_gpio_data ,
} ,
{
. compatible = " brcm,cygnus-crmu-gpio " ,
. data = & cygnus_crmu_gpio_data ,
}
} ;
static int cygnus_gpio_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct resource * res ;
struct cygnus_gpio * chip ;
struct gpio_chip * gc ;
u32 ngpios ;
int irq , ret ;
const struct of_device_id * match ;
const struct cygnus_gpio_data * gpio_data ;
match = of_match_device ( cygnus_gpio_of_match , dev ) ;
if ( ! match )
return - ENODEV ;
gpio_data = match - > data ;
ngpios = gpio_data - > num_gpios ;
chip = devm_kzalloc ( dev , sizeof ( * chip ) , GFP_KERNEL ) ;
if ( ! chip )
return - ENOMEM ;
chip - > dev = dev ;
platform_set_drvdata ( pdev , chip ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
chip - > base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( chip - > base ) ) {
dev_err ( dev , " unable to map I/O memory \n " ) ;
return PTR_ERR ( chip - > base ) ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( res ) {
chip - > io_ctrl = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( chip - > io_ctrl ) ) {
dev_err ( dev , " unable to map I/O memory \n " ) ;
return PTR_ERR ( chip - > io_ctrl ) ;
}
}
spin_lock_init ( & chip - > lock ) ;
gc = & chip - > gc ;
gc - > base = - 1 ;
gc - > ngpio = ngpios ;
chip - > num_banks = ( ngpios + NGPIOS_PER_BANK - 1 ) / NGPIOS_PER_BANK ;
gc - > label = dev_name ( dev ) ;
gc - > dev = dev ;
gc - > of_node = dev - > of_node ;
gc - > request = cygnus_gpio_request ;
gc - > free = cygnus_gpio_free ;
gc - > direction_input = cygnus_gpio_direction_input ;
gc - > direction_output = cygnus_gpio_direction_output ;
gc - > set = cygnus_gpio_set ;
gc - > get = cygnus_gpio_get ;
2015-10-19 08:43:09 +03:00
chip - > pinmux_is_supported = of_property_read_bool ( dev - > of_node ,
" gpio-ranges " ) ;
2015-03-09 23:45:00 +03:00
ret = gpiochip_add ( gc ) ;
if ( ret < 0 ) {
dev_err ( dev , " unable to add GPIO chip \n " ) ;
return ret ;
}
ret = cygnus_gpio_register_pinconf ( chip ) ;
if ( ret ) {
dev_err ( dev , " unable to register pinconf \n " ) ;
goto err_rm_gpiochip ;
}
/* optional GPIO interrupt support */
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq ) {
ret = gpiochip_irqchip_add ( gc , & cygnus_gpio_irq_chip , 0 ,
handle_simple_irq , IRQ_TYPE_NONE ) ;
if ( ret ) {
dev_err ( dev , " no GPIO irqchip \n " ) ;
goto err_unregister_pinconf ;
}
gpiochip_set_chained_irqchip ( gc , & cygnus_gpio_irq_chip , irq ,
cygnus_gpio_irq_handler ) ;
}
return 0 ;
err_unregister_pinconf :
cygnus_gpio_unregister_pinconf ( chip ) ;
err_rm_gpiochip :
gpiochip_remove ( gc ) ;
return ret ;
}
static struct platform_driver cygnus_gpio_driver = {
. driver = {
. name = " cygnus-gpio " ,
. of_match_table = cygnus_gpio_of_match ,
} ,
. probe = cygnus_gpio_probe ,
} ;
static int __init cygnus_gpio_init ( void )
{
return platform_driver_probe ( & cygnus_gpio_driver , cygnus_gpio_probe ) ;
}
arch_initcall_sync ( cygnus_gpio_init ) ;