2013-01-19 01:30:34 +04:00
/*
* Allwinner A1X SoCs pinctrl driver .
*
* Copyright ( C ) 2012 Maxime Ripard
*
* Maxime Ripard < maxime . ripard @ free - electrons . com >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/io.h>
2013-03-22 18:20:40 +04:00
# include <linux/clk.h>
2013-01-29 00:33:12 +04:00
# include <linux/gpio.h>
2013-06-08 14:05:44 +04:00
# include <linux/irqdomain.h>
2013-01-19 01:30:34 +04:00
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_device.h>
2013-06-08 14:05:44 +04:00
# include <linux/of_irq.h>
2013-01-19 01:30:34 +04:00
# include <linux/pinctrl/consumer.h>
# include <linux/pinctrl/machine.h>
# include <linux/pinctrl/pinctrl.h>
# include <linux/pinctrl/pinconf-generic.h>
# include <linux/pinctrl/pinmux.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include "core.h"
# include "pinctrl-sunxi.h"
2013-06-09 20:36:03 +04:00
# include "pinctrl-sunxi-pins.h"
2013-01-19 01:30:35 +04:00
2013-01-19 01:30:34 +04:00
static struct sunxi_pinctrl_group *
sunxi_pinctrl_find_group_by_name ( struct sunxi_pinctrl * pctl , const char * group )
{
int i ;
for ( i = 0 ; i < pctl - > ngroups ; i + + ) {
struct sunxi_pinctrl_group * grp = pctl - > groups + i ;
if ( ! strcmp ( grp - > name , group ) )
return grp ;
}
return NULL ;
}
static struct sunxi_pinctrl_function *
sunxi_pinctrl_find_function_by_name ( struct sunxi_pinctrl * pctl ,
const char * name )
{
struct sunxi_pinctrl_function * func = pctl - > functions ;
int i ;
for ( i = 0 ; i < pctl - > nfunctions ; i + + ) {
if ( ! func [ i ] . name )
break ;
if ( ! strcmp ( func [ i ] . name , name ) )
return func + i ;
}
return NULL ;
}
static struct sunxi_desc_function *
sunxi_pinctrl_desc_find_function_by_name ( struct sunxi_pinctrl * pctl ,
const char * pin_name ,
const char * func_name )
{
int i ;
for ( i = 0 ; i < pctl - > desc - > npins ; i + + ) {
const struct sunxi_desc_pin * pin = pctl - > desc - > pins + i ;
if ( ! strcmp ( pin - > pin . name , pin_name ) ) {
struct sunxi_desc_function * func = pin - > functions ;
while ( func - > name ) {
if ( ! strcmp ( func - > name , func_name ) )
return func ;
func + + ;
}
}
}
return NULL ;
}
2013-06-08 14:05:43 +04:00
static struct sunxi_desc_function *
sunxi_pinctrl_desc_find_function_by_pin ( struct sunxi_pinctrl * pctl ,
const u16 pin_num ,
const char * func_name )
{
int i ;
for ( i = 0 ; i < pctl - > desc - > npins ; i + + ) {
const struct sunxi_desc_pin * pin = pctl - > desc - > pins + i ;
if ( pin - > pin . number = = pin_num ) {
struct sunxi_desc_function * func = pin - > functions ;
while ( func - > name ) {
if ( ! strcmp ( func - > name , func_name ) )
return func ;
func + + ;
}
}
}
return NULL ;
}
2013-01-19 01:30:34 +04:00
static int sunxi_pctrl_get_groups_count ( struct pinctrl_dev * pctldev )
{
struct sunxi_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctl - > ngroups ;
}
static const char * sunxi_pctrl_get_group_name ( struct pinctrl_dev * pctldev ,
unsigned group )
{
struct sunxi_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctl - > groups [ group ] . name ;
}
static int sunxi_pctrl_get_group_pins ( struct pinctrl_dev * pctldev ,
unsigned group ,
const unsigned * * pins ,
unsigned * num_pins )
{
struct sunxi_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
* pins = ( unsigned * ) & pctl - > groups [ group ] . pin ;
* num_pins = 1 ;
return 0 ;
}
static int sunxi_pctrl_dt_node_to_map ( struct pinctrl_dev * pctldev ,
struct device_node * node ,
struct pinctrl_map * * map ,
unsigned * num_maps )
{
struct sunxi_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
unsigned long * pinconfig ;
struct property * prop ;
const char * function ;
const char * group ;
int ret , nmaps , i = 0 ;
u32 val ;
* map = NULL ;
* num_maps = 0 ;
ret = of_property_read_string ( node , " allwinner,function " , & function ) ;
if ( ret ) {
dev_err ( pctl - > dev ,
" missing allwinner,function property in node %s \n " ,
node - > name ) ;
return - EINVAL ;
}
nmaps = of_property_count_strings ( node , " allwinner,pins " ) * 2 ;
if ( nmaps < 0 ) {
dev_err ( pctl - > dev ,
" missing allwinner,pins property in node %s \n " ,
node - > name ) ;
return - EINVAL ;
}
* map = kmalloc ( nmaps * sizeof ( struct pinctrl_map ) , GFP_KERNEL ) ;
2013-07-29 12:19:32 +04:00
if ( ! * map )
2013-01-19 01:30:34 +04:00
return - ENOMEM ;
of_property_for_each_string ( node , " allwinner,pins " , prop , group ) {
struct sunxi_pinctrl_group * grp =
sunxi_pinctrl_find_group_by_name ( pctl , group ) ;
int j = 0 , configlen = 0 ;
if ( ! grp ) {
dev_err ( pctl - > dev , " unknown pin %s " , group ) ;
continue ;
}
if ( ! sunxi_pinctrl_desc_find_function_by_name ( pctl ,
grp - > name ,
function ) ) {
dev_err ( pctl - > dev , " unsupported function %s on pin %s " ,
function , group ) ;
continue ;
}
( * map ) [ i ] . type = PIN_MAP_TYPE_MUX_GROUP ;
( * map ) [ i ] . data . mux . group = group ;
( * map ) [ i ] . data . mux . function = function ;
i + + ;
( * map ) [ i ] . type = PIN_MAP_TYPE_CONFIGS_GROUP ;
( * map ) [ i ] . data . configs . group_or_pin = group ;
if ( of_find_property ( node , " allwinner,drive " , NULL ) )
configlen + + ;
if ( of_find_property ( node , " allwinner,pull " , NULL ) )
configlen + + ;
pinconfig = kzalloc ( configlen * sizeof ( * pinconfig ) , GFP_KERNEL ) ;
if ( ! of_property_read_u32 ( node , " allwinner,drive " , & val ) ) {
u16 strength = ( val + 1 ) * 10 ;
pinconfig [ j + + ] =
pinconf_to_config_packed ( PIN_CONFIG_DRIVE_STRENGTH ,
strength ) ;
}
if ( ! of_property_read_u32 ( node , " allwinner,pull " , & val ) ) {
enum pin_config_param pull = PIN_CONFIG_END ;
if ( val = = 1 )
pull = PIN_CONFIG_BIAS_PULL_UP ;
else if ( val = = 2 )
pull = PIN_CONFIG_BIAS_PULL_DOWN ;
pinconfig [ j + + ] = pinconf_to_config_packed ( pull , 0 ) ;
}
( * map ) [ i ] . data . configs . configs = pinconfig ;
( * map ) [ i ] . data . configs . num_configs = configlen ;
i + + ;
}
* num_maps = nmaps ;
return 0 ;
}
static void sunxi_pctrl_dt_free_map ( struct pinctrl_dev * pctldev ,
struct pinctrl_map * map ,
unsigned num_maps )
{
int i ;
for ( i = 0 ; i < num_maps ; i + + ) {
if ( map [ i ] . type = = PIN_MAP_TYPE_CONFIGS_GROUP )
kfree ( map [ i ] . data . configs . configs ) ;
}
kfree ( map ) ;
}
2013-02-16 13:25:07 +04:00
static const struct pinctrl_ops sunxi_pctrl_ops = {
2013-01-19 01:30:34 +04:00
. dt_node_to_map = sunxi_pctrl_dt_node_to_map ,
. dt_free_map = sunxi_pctrl_dt_free_map ,
. get_groups_count = sunxi_pctrl_get_groups_count ,
. get_group_name = sunxi_pctrl_get_group_name ,
. get_group_pins = sunxi_pctrl_get_group_pins ,
} ;
static int sunxi_pconf_group_get ( struct pinctrl_dev * pctldev ,
unsigned group ,
unsigned long * config )
{
struct sunxi_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
* config = pctl - > groups [ group ] . config ;
return 0 ;
}
static int sunxi_pconf_group_set ( struct pinctrl_dev * pctldev ,
unsigned group ,
2013-08-27 22:32:12 +04:00
unsigned long * configs ,
unsigned num_configs )
2013-01-19 01:30:34 +04:00
{
struct sunxi_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sunxi_pinctrl_group * g = & pctl - > groups [ group ] ;
2013-08-04 14:38:48 +04:00
unsigned long flags ;
2013-01-19 01:30:34 +04:00
u32 val , mask ;
u16 strength ;
u8 dlevel ;
2013-08-27 22:32:12 +04:00
int i ;
2013-01-19 01:30:34 +04:00
2013-08-29 11:46:30 +04:00
spin_lock_irqsave ( & pctl - > lock , flags ) ;
2013-08-04 14:38:48 +04:00
2013-08-27 22:32:12 +04:00
for ( i = 0 ; i < num_configs ; i + + ) {
switch ( pinconf_to_config_param ( configs [ i ] ) ) {
case PIN_CONFIG_DRIVE_STRENGTH :
strength = pinconf_to_config_argument ( configs [ i ] ) ;
2013-08-29 21:17:13 +04:00
if ( strength > 40 ) {
spin_unlock_irqrestore ( & pctl - > lock , flags ) ;
2013-08-27 22:32:12 +04:00
return - EINVAL ;
2013-08-29 21:17:13 +04:00
}
2013-08-27 22:32:12 +04:00
/*
* We convert from mA to what the register expects :
* 0 : 10 mA
* 1 : 20 mA
* 2 : 30 mA
* 3 : 40 mA
*/
dlevel = strength / 10 - 1 ;
val = readl ( pctl - > membase + sunxi_dlevel_reg ( g - > pin ) ) ;
mask = DLEVEL_PINS_MASK < < sunxi_dlevel_offset ( g - > pin ) ;
writel ( ( val & ~ mask )
| dlevel < < sunxi_dlevel_offset ( g - > pin ) ,
pctl - > membase + sunxi_dlevel_reg ( g - > pin ) ) ;
break ;
case PIN_CONFIG_BIAS_PULL_UP :
val = readl ( pctl - > membase + sunxi_pull_reg ( g - > pin ) ) ;
mask = PULL_PINS_MASK < < sunxi_pull_offset ( g - > pin ) ;
writel ( ( val & ~ mask ) | 1 < < sunxi_pull_offset ( g - > pin ) ,
pctl - > membase + sunxi_pull_reg ( g - > pin ) ) ;
break ;
case PIN_CONFIG_BIAS_PULL_DOWN :
val = readl ( pctl - > membase + sunxi_pull_reg ( g - > pin ) ) ;
mask = PULL_PINS_MASK < < sunxi_pull_offset ( g - > pin ) ;
writel ( ( val & ~ mask ) | 2 < < sunxi_pull_offset ( g - > pin ) ,
pctl - > membase + sunxi_pull_reg ( g - > pin ) ) ;
break ;
default :
break ;
}
/* cache the config value */
g - > config = configs [ i ] ;
} /* for each config */
2013-01-19 01:30:34 +04:00
2013-08-29 11:46:30 +04:00
spin_unlock_irqrestore ( & pctl - > lock , flags ) ;
2013-01-19 01:30:34 +04:00
return 0 ;
}
2013-02-16 13:25:07 +04:00
static const struct pinconf_ops sunxi_pconf_ops = {
2013-01-19 01:30:34 +04:00
. pin_config_group_get = sunxi_pconf_group_get ,
. pin_config_group_set = sunxi_pconf_group_set ,
} ;
static int sunxi_pmx_get_funcs_cnt ( struct pinctrl_dev * pctldev )
{
struct sunxi_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctl - > nfunctions ;
}
static const char * sunxi_pmx_get_func_name ( struct pinctrl_dev * pctldev ,
unsigned function )
{
struct sunxi_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctl - > functions [ function ] . name ;
}
static int sunxi_pmx_get_func_groups ( struct pinctrl_dev * pctldev ,
unsigned function ,
const char * const * * groups ,
unsigned * const num_groups )
{
struct sunxi_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
* groups = pctl - > functions [ function ] . groups ;
* num_groups = pctl - > functions [ function ] . ngroups ;
return 0 ;
}
static void sunxi_pmx_set ( struct pinctrl_dev * pctldev ,
unsigned pin ,
u8 config )
{
struct sunxi_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
2013-08-04 14:38:48 +04:00
unsigned long flags ;
u32 val , mask ;
spin_lock_irqsave ( & pctl - > lock , flags ) ;
2013-01-19 01:30:34 +04:00
2013-08-04 14:38:48 +04:00
val = readl ( pctl - > membase + sunxi_mux_reg ( pin ) ) ;
mask = MUX_PINS_MASK < < sunxi_mux_offset ( pin ) ;
2013-01-19 01:30:34 +04:00
writel ( ( val & ~ mask ) | config < < sunxi_mux_offset ( pin ) ,
pctl - > membase + sunxi_mux_reg ( pin ) ) ;
2013-08-04 14:38:48 +04:00
spin_unlock_irqrestore ( & pctl - > lock , flags ) ;
2013-01-19 01:30:34 +04:00
}
static int sunxi_pmx_enable ( struct pinctrl_dev * pctldev ,
unsigned function ,
unsigned group )
{
struct sunxi_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sunxi_pinctrl_group * g = pctl - > groups + group ;
struct sunxi_pinctrl_function * func = pctl - > functions + function ;
struct sunxi_desc_function * desc =
sunxi_pinctrl_desc_find_function_by_name ( pctl ,
g - > name ,
func - > name ) ;
if ( ! desc )
return - EINVAL ;
sunxi_pmx_set ( pctldev , g - > pin , desc - > muxval ) ;
return 0 ;
}
2013-01-29 00:33:12 +04:00
static int
sunxi_pmx_gpio_set_direction ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range ,
unsigned offset ,
bool input )
{
struct sunxi_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sunxi_desc_function * desc ;
const char * func ;
if ( input )
func = " gpio_in " ;
else
func = " gpio_out " ;
2013-06-08 14:05:43 +04:00
desc = sunxi_pinctrl_desc_find_function_by_pin ( pctl , offset , func ) ;
if ( ! desc )
return - EINVAL ;
2013-01-29 00:33:12 +04:00
sunxi_pmx_set ( pctldev , offset , desc - > muxval ) ;
2013-06-08 14:05:43 +04:00
return 0 ;
2013-01-29 00:33:12 +04:00
}
2013-02-16 13:25:07 +04:00
static const struct pinmux_ops sunxi_pmx_ops = {
2013-01-19 01:30:34 +04:00
. get_functions_count = sunxi_pmx_get_funcs_cnt ,
. get_function_name = sunxi_pmx_get_func_name ,
. get_function_groups = sunxi_pmx_get_func_groups ,
. enable = sunxi_pmx_enable ,
2013-01-29 00:33:12 +04:00
. gpio_set_direction = sunxi_pmx_gpio_set_direction ,
2013-01-19 01:30:34 +04:00
} ;
static struct pinctrl_desc sunxi_pctrl_desc = {
. confops = & sunxi_pconf_ops ,
. pctlops = & sunxi_pctrl_ops ,
. pmxops = & sunxi_pmx_ops ,
} ;
2013-01-29 00:33:12 +04:00
static int sunxi_pinctrl_gpio_request ( struct gpio_chip * chip , unsigned offset )
{
return pinctrl_request_gpio ( chip - > base + offset ) ;
}
static void sunxi_pinctrl_gpio_free ( struct gpio_chip * chip , unsigned offset )
{
pinctrl_free_gpio ( chip - > base + offset ) ;
}
static int sunxi_pinctrl_gpio_direction_input ( struct gpio_chip * chip ,
unsigned offset )
{
return pinctrl_gpio_direction_input ( chip - > base + offset ) ;
}
static int sunxi_pinctrl_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
struct sunxi_pinctrl * pctl = dev_get_drvdata ( chip - > dev ) ;
u32 reg = sunxi_data_reg ( offset ) ;
u8 index = sunxi_data_offset ( offset ) ;
u32 val = ( readl ( pctl - > membase + reg ) > > index ) & DATA_PINS_MASK ;
return val ;
}
static int sunxi_pinctrl_gpio_direction_output ( struct gpio_chip * chip ,
unsigned offset , int value )
{
return pinctrl_gpio_direction_output ( chip - > base + offset ) ;
}
static void sunxi_pinctrl_gpio_set ( struct gpio_chip * chip ,
unsigned offset , int value )
{
struct sunxi_pinctrl * pctl = dev_get_drvdata ( chip - > dev ) ;
u32 reg = sunxi_data_reg ( offset ) ;
u8 index = sunxi_data_offset ( offset ) ;
2013-08-04 14:38:48 +04:00
unsigned long flags ;
u32 regval ;
spin_lock_irqsave ( & pctl - > lock , flags ) ;
regval = readl ( pctl - > membase + reg ) ;
2013-01-29 00:33:12 +04:00
2013-07-25 14:41:16 +04:00
if ( value )
regval | = BIT ( index ) ;
else
regval & = ~ ( BIT ( index ) ) ;
2013-01-29 00:33:12 +04:00
2013-07-25 14:41:16 +04:00
writel ( regval , pctl - > membase + reg ) ;
2013-08-04 14:38:48 +04:00
spin_unlock_irqrestore ( & pctl - > lock , flags ) ;
2013-01-29 00:33:12 +04:00
}
2013-02-03 15:10:11 +04:00
static int sunxi_pinctrl_gpio_of_xlate ( struct gpio_chip * gc ,
const struct of_phandle_args * gpiospec ,
u32 * flags )
{
int pin , base ;
base = PINS_PER_BANK * gpiospec - > args [ 0 ] ;
pin = base + gpiospec - > args [ 1 ] ;
if ( pin > ( gc - > base + gc - > ngpio ) )
return - EINVAL ;
if ( flags )
* flags = gpiospec - > args [ 2 ] ;
return pin ;
}
2013-06-08 14:05:44 +04:00
static int sunxi_pinctrl_gpio_to_irq ( struct gpio_chip * chip , unsigned offset )
{
struct sunxi_pinctrl * pctl = dev_get_drvdata ( chip - > dev ) ;
struct sunxi_desc_function * desc ;
2013-08-30 12:31:25 +04:00
if ( offset > = chip - > ngpio )
2013-06-08 14:05:44 +04:00
return - ENXIO ;
desc = sunxi_pinctrl_desc_find_function_by_pin ( pctl , offset , " irq " ) ;
if ( ! desc )
return - EINVAL ;
pctl - > irq_array [ desc - > irqnum ] = offset ;
dev_dbg ( chip - > dev , " %s: request IRQ for GPIO %d, return %d \n " ,
chip - > label , offset + chip - > base , desc - > irqnum ) ;
return irq_find_mapping ( pctl - > domain , desc - > irqnum ) ;
}
2013-01-29 00:33:12 +04:00
static struct gpio_chip sunxi_pinctrl_gpio_chip = {
. owner = THIS_MODULE ,
. request = sunxi_pinctrl_gpio_request ,
. free = sunxi_pinctrl_gpio_free ,
. direction_input = sunxi_pinctrl_gpio_direction_input ,
. direction_output = sunxi_pinctrl_gpio_direction_output ,
. get = sunxi_pinctrl_gpio_get ,
. set = sunxi_pinctrl_gpio_set ,
2013-02-03 15:10:11 +04:00
. of_xlate = sunxi_pinctrl_gpio_of_xlate ,
2013-06-08 14:05:44 +04:00
. to_irq = sunxi_pinctrl_gpio_to_irq ,
2013-02-03 15:10:11 +04:00
. of_gpio_n_cells = 3 ,
2013-01-29 00:33:12 +04:00
. can_sleep = 0 ,
} ;
2013-06-08 14:05:44 +04:00
static int sunxi_pinctrl_irq_set_type ( struct irq_data * d ,
unsigned int type )
{
struct sunxi_pinctrl * pctl = irq_data_get_irq_chip_data ( d ) ;
u32 reg = sunxi_irq_cfg_reg ( d - > hwirq ) ;
u8 index = sunxi_irq_cfg_offset ( d - > hwirq ) ;
2013-08-04 14:38:48 +04:00
unsigned long flags ;
2013-08-04 14:38:47 +04:00
u32 regval ;
2013-06-08 14:05:44 +04:00
u8 mode ;
switch ( type ) {
case IRQ_TYPE_EDGE_RISING :
mode = IRQ_EDGE_RISING ;
break ;
case IRQ_TYPE_EDGE_FALLING :
mode = IRQ_EDGE_FALLING ;
break ;
case IRQ_TYPE_EDGE_BOTH :
mode = IRQ_EDGE_BOTH ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
mode = IRQ_LEVEL_HIGH ;
break ;
case IRQ_TYPE_LEVEL_LOW :
mode = IRQ_LEVEL_LOW ;
break ;
default :
return - EINVAL ;
}
2013-08-04 14:38:48 +04:00
spin_lock_irqsave ( & pctl - > lock , flags ) ;
2013-08-04 14:38:47 +04:00
regval = readl ( pctl - > membase + reg ) ;
regval & = ~ IRQ_CFG_IRQ_MASK ;
writel ( regval | ( mode < < index ) , pctl - > membase + reg ) ;
2013-06-08 14:05:44 +04:00
2013-08-04 14:38:48 +04:00
spin_unlock_irqrestore ( & pctl - > lock , flags ) ;
2013-06-08 14:05:44 +04:00
return 0 ;
}
static void sunxi_pinctrl_irq_mask_ack ( struct irq_data * d )
{
struct sunxi_pinctrl * pctl = irq_data_get_irq_chip_data ( d ) ;
u32 ctrl_reg = sunxi_irq_ctrl_reg ( d - > hwirq ) ;
u8 ctrl_idx = sunxi_irq_ctrl_offset ( d - > hwirq ) ;
u32 status_reg = sunxi_irq_status_reg ( d - > hwirq ) ;
u8 status_idx = sunxi_irq_status_offset ( d - > hwirq ) ;
2013-08-04 14:38:48 +04:00
unsigned long flags ;
2013-06-08 14:05:44 +04:00
u32 val ;
2013-08-04 14:38:48 +04:00
spin_lock_irqsave ( & pctl - > lock , flags ) ;
2013-06-08 14:05:44 +04:00
/* Mask the IRQ */
val = readl ( pctl - > membase + ctrl_reg ) ;
writel ( val & ~ ( 1 < < ctrl_idx ) , pctl - > membase + ctrl_reg ) ;
/* Clear the IRQ */
writel ( 1 < < status_idx , pctl - > membase + status_reg ) ;
2013-08-04 14:38:48 +04:00
spin_unlock_irqrestore ( & pctl - > lock , flags ) ;
2013-06-08 14:05:44 +04:00
}
static void sunxi_pinctrl_irq_mask ( struct irq_data * d )
{
struct sunxi_pinctrl * pctl = irq_data_get_irq_chip_data ( d ) ;
u32 reg = sunxi_irq_ctrl_reg ( d - > hwirq ) ;
u8 idx = sunxi_irq_ctrl_offset ( d - > hwirq ) ;
2013-08-04 14:38:48 +04:00
unsigned long flags ;
2013-06-08 14:05:44 +04:00
u32 val ;
2013-08-04 14:38:48 +04:00
spin_lock_irqsave ( & pctl - > lock , flags ) ;
2013-06-08 14:05:44 +04:00
/* Mask the IRQ */
val = readl ( pctl - > membase + reg ) ;
writel ( val & ~ ( 1 < < idx ) , pctl - > membase + reg ) ;
2013-08-04 14:38:48 +04:00
spin_unlock_irqrestore ( & pctl - > lock , flags ) ;
2013-06-08 14:05:44 +04:00
}
static void sunxi_pinctrl_irq_unmask ( struct irq_data * d )
{
struct sunxi_pinctrl * pctl = irq_data_get_irq_chip_data ( d ) ;
struct sunxi_desc_function * func ;
u32 reg = sunxi_irq_ctrl_reg ( d - > hwirq ) ;
u8 idx = sunxi_irq_ctrl_offset ( d - > hwirq ) ;
2013-08-04 14:38:48 +04:00
unsigned long flags ;
2013-06-08 14:05:44 +04:00
u32 val ;
func = sunxi_pinctrl_desc_find_function_by_pin ( pctl ,
pctl - > irq_array [ d - > hwirq ] ,
" irq " ) ;
/* Change muxing to INT mode */
sunxi_pmx_set ( pctl - > pctl_dev , pctl - > irq_array [ d - > hwirq ] , func - > muxval ) ;
2013-08-04 14:38:48 +04:00
spin_lock_irqsave ( & pctl - > lock , flags ) ;
2013-06-08 14:05:44 +04:00
/* Unmask the IRQ */
val = readl ( pctl - > membase + reg ) ;
writel ( val | ( 1 < < idx ) , pctl - > membase + reg ) ;
2013-08-04 14:38:48 +04:00
spin_unlock_irqrestore ( & pctl - > lock , flags ) ;
2013-06-08 14:05:44 +04:00
}
static struct irq_chip sunxi_pinctrl_irq_chip = {
. irq_mask = sunxi_pinctrl_irq_mask ,
. irq_mask_ack = sunxi_pinctrl_irq_mask_ack ,
. irq_unmask = sunxi_pinctrl_irq_unmask ,
. irq_set_type = sunxi_pinctrl_irq_set_type ,
} ;
static void sunxi_pinctrl_irq_handler ( unsigned irq , struct irq_desc * desc )
{
struct sunxi_pinctrl * pctl = irq_get_handler_data ( irq ) ;
const unsigned long reg = readl ( pctl - > membase + IRQ_STATUS_REG ) ;
/* Clear all interrupts */
writel ( reg , pctl - > membase + IRQ_STATUS_REG ) ;
if ( reg ) {
int irqoffset ;
for_each_set_bit ( irqoffset , & reg , SUNXI_IRQ_NUMBER ) {
int pin_irq = irq_find_mapping ( pctl - > domain , irqoffset ) ;
generic_handle_irq ( pin_irq ) ;
}
}
}
2013-01-19 01:30:34 +04:00
static struct of_device_id sunxi_pinctrl_match [ ] = {
2013-01-26 18:36:53 +04:00
{ . compatible = " allwinner,sun4i-a10-pinctrl " , . data = ( void * ) & sun4i_a10_pinctrl_data } ,
2013-06-09 20:36:04 +04:00
{ . compatible = " allwinner,sun5i-a10s-pinctrl " , . data = ( void * ) & sun5i_a10s_pinctrl_data } ,
2013-01-19 01:30:35 +04:00
{ . compatible = " allwinner,sun5i-a13-pinctrl " , . data = ( void * ) & sun5i_a13_pinctrl_data } ,
2013-08-04 13:47:34 +04:00
{ . compatible = " allwinner,sun6i-a31-pinctrl " , . data = ( void * ) & sun6i_a31_pinctrl_data } ,
2013-08-04 13:58:45 +04:00
{ . compatible = " allwinner,sun7i-a20-pinctrl " , . data = ( void * ) & sun7i_a20_pinctrl_data } ,
2013-01-19 01:30:34 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , sunxi_pinctrl_match ) ;
static int sunxi_pinctrl_add_function ( struct sunxi_pinctrl * pctl ,
const char * name )
{
struct sunxi_pinctrl_function * func = pctl - > functions ;
while ( func - > name ) {
/* function already there */
if ( strcmp ( func - > name , name ) = = 0 ) {
func - > ngroups + + ;
return - EEXIST ;
}
func + + ;
}
func - > name = name ;
func - > ngroups = 1 ;
pctl - > nfunctions + + ;
return 0 ;
}
static int sunxi_pinctrl_build_state ( struct platform_device * pdev )
{
struct sunxi_pinctrl * pctl = platform_get_drvdata ( pdev ) ;
int i ;
pctl - > ngroups = pctl - > desc - > npins ;
/* Allocate groups */
pctl - > groups = devm_kzalloc ( & pdev - > dev ,
pctl - > ngroups * sizeof ( * pctl - > groups ) ,
GFP_KERNEL ) ;
if ( ! pctl - > groups )
return - ENOMEM ;
for ( i = 0 ; i < pctl - > desc - > npins ; i + + ) {
const struct sunxi_desc_pin * pin = pctl - > desc - > pins + i ;
struct sunxi_pinctrl_group * group = pctl - > groups + i ;
group - > name = pin - > pin . name ;
group - > pin = pin - > pin . number ;
}
/*
* We suppose that we won ' t have any more functions than pins ,
* we ' ll reallocate that later anyway
*/
pctl - > functions = devm_kzalloc ( & pdev - > dev ,
pctl - > desc - > npins * sizeof ( * pctl - > functions ) ,
GFP_KERNEL ) ;
if ( ! pctl - > functions )
return - ENOMEM ;
/* Count functions and their associated groups */
for ( i = 0 ; i < pctl - > desc - > npins ; i + + ) {
const struct sunxi_desc_pin * pin = pctl - > desc - > pins + i ;
struct sunxi_desc_function * func = pin - > functions ;
while ( func - > name ) {
sunxi_pinctrl_add_function ( pctl , func - > name ) ;
func + + ;
}
}
pctl - > functions = krealloc ( pctl - > functions ,
pctl - > nfunctions * sizeof ( * pctl - > functions ) ,
GFP_KERNEL ) ;
for ( i = 0 ; i < pctl - > desc - > npins ; i + + ) {
const struct sunxi_desc_pin * pin = pctl - > desc - > pins + i ;
struct sunxi_desc_function * func = pin - > functions ;
while ( func - > name ) {
struct sunxi_pinctrl_function * func_item ;
const char * * func_grp ;
func_item = sunxi_pinctrl_find_function_by_name ( pctl ,
func - > name ) ;
if ( ! func_item )
return - EINVAL ;
if ( ! func_item - > groups ) {
func_item - > groups =
devm_kzalloc ( & pdev - > dev ,
func_item - > ngroups * sizeof ( * func_item - > groups ) ,
GFP_KERNEL ) ;
if ( ! func_item - > groups )
return - ENOMEM ;
}
func_grp = func_item - > groups ;
while ( * func_grp )
func_grp + + ;
* func_grp = pin - > pin . name ;
func + + ;
}
}
return 0 ;
}
static int sunxi_pinctrl_probe ( struct platform_device * pdev )
{
struct device_node * node = pdev - > dev . of_node ;
const struct of_device_id * device ;
struct pinctrl_pin_desc * pins ;
struct sunxi_pinctrl * pctl ;
2013-01-29 00:33:12 +04:00
int i , ret , last_pin ;
2013-03-22 18:20:40 +04:00
struct clk * clk ;
2013-01-19 01:30:34 +04:00
pctl = devm_kzalloc ( & pdev - > dev , sizeof ( * pctl ) , GFP_KERNEL ) ;
if ( ! pctl )
return - ENOMEM ;
platform_set_drvdata ( pdev , pctl ) ;
2013-08-04 14:38:48 +04:00
spin_lock_init ( & pctl - > lock ) ;
2013-01-19 01:30:34 +04:00
pctl - > membase = of_iomap ( node , 0 ) ;
if ( ! pctl - > membase )
return - ENOMEM ;
device = of_match_device ( sunxi_pinctrl_match , & pdev - > dev ) ;
if ( ! device )
return - ENODEV ;
pctl - > desc = ( struct sunxi_pinctrl_desc * ) device - > data ;
ret = sunxi_pinctrl_build_state ( pdev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " dt probe failed: %d \n " , ret ) ;
return ret ;
}
pins = devm_kzalloc ( & pdev - > dev ,
pctl - > desc - > npins * sizeof ( * pins ) ,
GFP_KERNEL ) ;
if ( ! pins )
return - ENOMEM ;
for ( i = 0 ; i < pctl - > desc - > npins ; i + + )
pins [ i ] = pctl - > desc - > pins [ i ] . pin ;
sunxi_pctrl_desc . name = dev_name ( & pdev - > dev ) ;
sunxi_pctrl_desc . owner = THIS_MODULE ;
sunxi_pctrl_desc . pins = pins ;
sunxi_pctrl_desc . npins = pctl - > desc - > npins ;
pctl - > dev = & pdev - > dev ;
pctl - > pctl_dev = pinctrl_register ( & sunxi_pctrl_desc ,
& pdev - > dev , pctl ) ;
if ( ! pctl - > pctl_dev ) {
dev_err ( & pdev - > dev , " couldn't register pinctrl driver \n " ) ;
return - EINVAL ;
}
2013-01-29 00:33:12 +04:00
pctl - > chip = devm_kzalloc ( & pdev - > dev , sizeof ( * pctl - > chip ) , GFP_KERNEL ) ;
if ( ! pctl - > chip ) {
ret = - ENOMEM ;
goto pinctrl_error ;
}
last_pin = pctl - > desc - > pins [ pctl - > desc - > npins - 1 ] . pin . number ;
pctl - > chip = & sunxi_pinctrl_gpio_chip ;
pctl - > chip - > ngpio = round_up ( last_pin , PINS_PER_BANK ) ;
pctl - > chip - > label = dev_name ( & pdev - > dev ) ;
pctl - > chip - > dev = & pdev - > dev ;
pctl - > chip - > base = 0 ;
ret = gpiochip_add ( pctl - > chip ) ;
if ( ret )
goto pinctrl_error ;
for ( i = 0 ; i < pctl - > desc - > npins ; i + + ) {
const struct sunxi_desc_pin * pin = pctl - > desc - > pins + i ;
ret = gpiochip_add_pin_range ( pctl - > chip , dev_name ( & pdev - > dev ) ,
pin - > pin . number ,
pin - > pin . number , 1 ) ;
if ( ret )
goto gpiochip_error ;
}
2013-03-22 18:20:40 +04:00
clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2013-05-23 13:32:14 +04:00
if ( IS_ERR ( clk ) ) {
ret = PTR_ERR ( clk ) ;
2013-03-22 18:20:40 +04:00
goto gpiochip_error ;
2013-05-23 13:32:14 +04:00
}
2013-03-22 18:20:40 +04:00
clk_prepare_enable ( clk ) ;
2013-06-08 14:05:44 +04:00
pctl - > irq = irq_of_parse_and_map ( node , 0 ) ;
if ( ! pctl - > irq ) {
ret = - EINVAL ;
goto gpiochip_error ;
}
pctl - > domain = irq_domain_add_linear ( node , SUNXI_IRQ_NUMBER ,
& irq_domain_simple_ops , NULL ) ;
if ( ! pctl - > domain ) {
dev_err ( & pdev - > dev , " Couldn't register IRQ domain \n " ) ;
ret = - ENOMEM ;
goto gpiochip_error ;
}
for ( i = 0 ; i < SUNXI_IRQ_NUMBER ; i + + ) {
int irqno = irq_create_mapping ( pctl - > domain , i ) ;
irq_set_chip_and_handler ( irqno , & sunxi_pinctrl_irq_chip ,
handle_simple_irq ) ;
irq_set_chip_data ( irqno , pctl ) ;
} ;
irq_set_chained_handler ( pctl - > irq , sunxi_pinctrl_irq_handler ) ;
irq_set_handler_data ( pctl - > irq , pctl ) ;
2013-01-29 00:33:12 +04:00
dev_info ( & pdev - > dev , " initialized sunXi PIO driver \n " ) ;
2013-01-19 01:30:34 +04:00
return 0 ;
2013-01-29 00:33:12 +04:00
gpiochip_error :
2013-05-19 09:58:37 +04:00
if ( gpiochip_remove ( pctl - > chip ) )
dev_err ( & pdev - > dev , " failed to remove gpio chip \n " ) ;
2013-01-29 00:33:12 +04:00
pinctrl_error :
pinctrl_unregister ( pctl - > pctl_dev ) ;
return ret ;
2013-01-19 01:30:34 +04:00
}
static struct platform_driver sunxi_pinctrl_driver = {
. probe = sunxi_pinctrl_probe ,
. driver = {
. name = " sunxi-pinctrl " ,
. owner = THIS_MODULE ,
. of_match_table = sunxi_pinctrl_match ,
} ,
} ;
module_platform_driver ( sunxi_pinctrl_driver ) ;
MODULE_AUTHOR ( " Maxime Ripard <maxime.ripard@free-electrons.com " ) ;
MODULE_DESCRIPTION ( " Allwinner A1X pinctrl driver " ) ;
MODULE_LICENSE ( " GPL " ) ;