2013-06-18 15:33:02 +04:00
/*
* Pinctrl GPIO driver for Intel Baytrail
* Copyright ( c ) 2012 - 2013 , Intel Corporation .
*
* Author : Mathias Nyman < mathias . nyman @ linux . intel . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA .
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/bitops.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/gpio.h>
# include <linux/irqdomain.h>
# include <linux/acpi.h>
# include <linux/acpi_gpio.h>
# include <linux/platform_device.h>
# include <linux/seq_file.h>
# include <linux/io.h>
# include <linux/pm_runtime.h>
# include <linux/pinctrl/pinctrl.h>
/* memory mapped register offsets */
# define BYT_CONF0_REG 0x000
# define BYT_CONF1_REG 0x004
# define BYT_VAL_REG 0x008
# define BYT_DFT_REG 0x00c
# define BYT_INT_STAT_REG 0x800
/* BYT_CONF0_REG register bits */
# define BYT_TRIG_NEG BIT(26)
# define BYT_TRIG_POS BIT(25)
# define BYT_TRIG_LVL BIT(24)
# define BYT_PIN_MUX 0x07
/* BYT_VAL_REG register bits */
# define BYT_INPUT_EN BIT(2) /* 0: input enabled (active low)*/
# define BYT_OUTPUT_EN BIT(1) /* 0: output enabled (active low)*/
# define BYT_LEVEL BIT(0)
# define BYT_DIR_MASK (BIT(1) | BIT(2))
# define BYT_TRIG_MASK (BIT(26) | BIT(25) | BIT(24))
# define BYT_NGPIO_SCORE 102
# define BYT_NGPIO_NCORE 28
# define BYT_NGPIO_SUS 44
/*
* Baytrail gpio controller consist of three separate sub - controllers called
* SCORE , NCORE and SUS . The sub - controllers are identified by their acpi UID .
*
* GPIO numbering is _not_ ordered meaning that gpio # 0 in ACPI namespace does
* _not_ correspond to the first gpio register at controller ' s gpio base .
* There is no logic or pattern in mapping gpio numbers to registers ( pads ) so
* each sub - controller needs to have its own mapping table
*/
/* score_pins[gpio_nr] = pad_nr */
static unsigned const score_pins [ BYT_NGPIO_SCORE ] = {
85 , 89 , 93 , 96 , 99 , 102 , 98 , 101 , 34 , 37 ,
36 , 38 , 39 , 35 , 40 , 84 , 62 , 61 , 64 , 59 ,
54 , 56 , 60 , 55 , 63 , 57 , 51 , 50 , 53 , 47 ,
52 , 49 , 48 , 43 , 46 , 41 , 45 , 42 , 58 , 44 ,
95 , 105 , 70 , 68 , 67 , 66 , 69 , 71 , 65 , 72 ,
86 , 90 , 88 , 92 , 103 , 77 , 79 , 83 , 78 , 81 ,
80 , 82 , 13 , 12 , 15 , 14 , 17 , 18 , 19 , 16 ,
2 , 1 , 0 , 4 , 6 , 7 , 9 , 8 , 33 , 32 ,
31 , 30 , 29 , 27 , 25 , 28 , 26 , 23 , 21 , 20 ,
24 , 22 , 5 , 3 , 10 , 11 , 106 , 87 , 91 , 104 ,
97 , 100 ,
} ;
static unsigned const ncore_pins [ BYT_NGPIO_NCORE ] = {
19 , 18 , 17 , 20 , 21 , 22 , 24 , 25 , 23 , 16 ,
14 , 15 , 12 , 26 , 27 , 1 , 4 , 8 , 11 , 0 ,
3 , 6 , 10 , 13 , 2 , 5 , 9 , 7 ,
} ;
static unsigned const sus_pins [ BYT_NGPIO_SUS ] = {
29 , 33 , 30 , 31 , 32 , 34 , 36 , 35 , 38 , 37 ,
18 , 7 , 11 , 20 , 17 , 1 , 8 , 10 , 19 , 12 ,
0 , 2 , 23 , 39 , 28 , 27 , 22 , 21 , 24 , 25 ,
26 , 51 , 56 , 54 , 49 , 55 , 48 , 57 , 50 , 58 ,
52 , 53 , 59 , 40 ,
} ;
static struct pinctrl_gpio_range byt_ranges [ ] = {
{
. name = " 1 " , /* match with acpi _UID in probe */
. npins = BYT_NGPIO_SCORE ,
. pins = score_pins ,
} ,
{
. name = " 2 " ,
. npins = BYT_NGPIO_NCORE ,
. pins = ncore_pins ,
} ,
{
. name = " 3 " ,
. npins = BYT_NGPIO_SUS ,
. pins = sus_pins ,
} ,
{
} ,
} ;
struct byt_gpio {
struct gpio_chip chip ;
struct irq_domain * domain ;
struct platform_device * pdev ;
spinlock_t lock ;
void __iomem * reg_base ;
struct pinctrl_gpio_range * range ;
} ;
static void __iomem * byt_gpio_reg ( struct gpio_chip * chip , unsigned offset ,
int reg )
{
struct byt_gpio * vg = container_of ( chip , struct byt_gpio , chip ) ;
u32 reg_offset ;
void __iomem * ptr ;
if ( reg = = BYT_INT_STAT_REG )
reg_offset = ( offset / 32 ) * 4 ;
else
reg_offset = vg - > range - > pins [ offset ] * 16 ;
ptr = ( void __iomem * ) ( vg - > reg_base + reg_offset + reg ) ;
return ptr ;
}
static int byt_gpio_request ( struct gpio_chip * chip , unsigned offset )
{
struct byt_gpio * vg = container_of ( chip , struct byt_gpio , chip ) ;
pm_runtime_get ( & vg - > pdev - > dev ) ;
return 0 ;
}
static void byt_gpio_free ( struct gpio_chip * chip , unsigned offset )
{
struct byt_gpio * vg = container_of ( chip , struct byt_gpio , chip ) ;
void __iomem * reg = byt_gpio_reg ( & vg - > chip , offset , BYT_CONF0_REG ) ;
u32 value ;
/* clear interrupt triggering */
value = readl ( reg ) ;
value & = ~ ( BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL ) ;
writel ( value , reg ) ;
pm_runtime_put ( & vg - > pdev - > dev ) ;
}
static int byt_irq_type ( struct irq_data * d , unsigned type )
{
struct byt_gpio * vg = irq_data_get_irq_chip_data ( d ) ;
u32 offset = irqd_to_hwirq ( d ) ;
u32 value ;
unsigned long flags ;
void __iomem * reg = byt_gpio_reg ( & vg - > chip , offset , BYT_CONF0_REG ) ;
if ( offset > = vg - > chip . ngpio )
return - EINVAL ;
spin_lock_irqsave ( & vg - > lock , flags ) ;
value = readl ( reg ) ;
/* For level trigges the BYT_TRIG_POS and BYT_TRIG_NEG bits
* are used to indicate high and low level triggering
*/
value & = ~ ( BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL ) ;
switch ( type ) {
case IRQ_TYPE_LEVEL_HIGH :
value | = BYT_TRIG_LVL ;
case IRQ_TYPE_EDGE_RISING :
value | = BYT_TRIG_POS ;
break ;
case IRQ_TYPE_LEVEL_LOW :
value | = BYT_TRIG_LVL ;
case IRQ_TYPE_EDGE_FALLING :
value | = BYT_TRIG_NEG ;
break ;
case IRQ_TYPE_EDGE_BOTH :
value | = ( BYT_TRIG_NEG | BYT_TRIG_POS ) ;
break ;
}
writel ( value , reg ) ;
spin_unlock_irqrestore ( & vg - > lock , flags ) ;
return 0 ;
}
static int byt_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
void __iomem * reg = byt_gpio_reg ( chip , offset , BYT_VAL_REG ) ;
return readl ( reg ) & BYT_LEVEL ;
}
static void byt_gpio_set ( struct gpio_chip * chip , unsigned offset , int value )
{
struct byt_gpio * vg = container_of ( chip , struct byt_gpio , chip ) ;
void __iomem * reg = byt_gpio_reg ( chip , offset , BYT_VAL_REG ) ;
unsigned long flags ;
u32 old_val ;
spin_lock_irqsave ( & vg - > lock , flags ) ;
old_val = readl ( reg ) ;
if ( value )
writel ( old_val | BYT_LEVEL , reg ) ;
else
writel ( old_val & ~ BYT_LEVEL , reg ) ;
spin_unlock_irqrestore ( & vg - > lock , flags ) ;
}
static int byt_gpio_direction_input ( struct gpio_chip * chip , unsigned offset )
{
struct byt_gpio * vg = container_of ( chip , struct byt_gpio , chip ) ;
void __iomem * reg = byt_gpio_reg ( chip , offset , BYT_VAL_REG ) ;
unsigned long flags ;
u32 value ;
spin_lock_irqsave ( & vg - > lock , flags ) ;
value = readl ( reg ) | BYT_DIR_MASK ;
value = value & ( ~ BYT_INPUT_EN ) ; /* active low */
writel ( value , reg ) ;
spin_unlock_irqrestore ( & vg - > lock , flags ) ;
return 0 ;
}
static int byt_gpio_direction_output ( struct gpio_chip * chip ,
unsigned gpio , int value )
{
struct byt_gpio * vg = container_of ( chip , struct byt_gpio , chip ) ;
void __iomem * reg = byt_gpio_reg ( chip , gpio , BYT_VAL_REG ) ;
unsigned long flags ;
u32 reg_val ;
spin_lock_irqsave ( & vg - > lock , flags ) ;
reg_val = readl ( reg ) | ( BYT_DIR_MASK | ! ! value ) ;
reg_val & = ~ ( BYT_OUTPUT_EN | ! value ) ;
writel ( reg_val , reg ) ;
spin_unlock_irqrestore ( & vg - > lock , flags ) ;
return 0 ;
}
static void byt_gpio_dbg_show ( struct seq_file * s , struct gpio_chip * chip )
{
struct byt_gpio * vg = container_of ( chip , struct byt_gpio , chip ) ;
int i ;
unsigned long flags ;
u32 conf0 , val , offs ;
spin_lock_irqsave ( & vg - > lock , flags ) ;
for ( i = 0 ; i < vg - > chip . ngpio ; i + + ) {
offs = vg - > range - > pins [ i ] * 16 ;
conf0 = readl ( vg - > reg_base + offs + BYT_CONF0_REG ) ;
val = readl ( vg - > reg_base + offs + BYT_VAL_REG ) ;
seq_printf ( s ,
" gpio-%-3d %s %s %s pad-%-3d offset:0x%03x mux:%d %s%s%s \n " ,
i ,
val & BYT_INPUT_EN ? " " : " in " ,
val & BYT_OUTPUT_EN ? " " : " out " ,
val & BYT_LEVEL ? " hi " : " lo " ,
vg - > range - > pins [ i ] , offs ,
conf0 & 0x7 ,
2013-07-10 17:42:14 +04:00
conf0 & BYT_TRIG_NEG ? " fall " : " " ,
conf0 & BYT_TRIG_POS ? " rise " : " " ,
conf0 & BYT_TRIG_LVL ? " level " : " " ) ;
2013-06-18 15:33:02 +04:00
}
spin_unlock_irqrestore ( & vg - > lock , flags ) ;
}
static int byt_gpio_to_irq ( struct gpio_chip * chip , unsigned offset )
{
struct byt_gpio * vg = container_of ( chip , struct byt_gpio , chip ) ;
return irq_create_mapping ( vg - > domain , offset ) ;
}
static void byt_gpio_irq_handler ( unsigned irq , struct irq_desc * desc )
{
struct irq_data * data = irq_desc_get_irq_data ( desc ) ;
struct byt_gpio * vg = irq_data_get_irq_handler_data ( data ) ;
struct irq_chip * chip = irq_data_get_irq_chip ( data ) ;
u32 base , pin , mask ;
void __iomem * reg ;
u32 pending ;
unsigned virq ;
int looplimit = 0 ;
/* check from GPIO controller which pin triggered the interrupt */
for ( base = 0 ; base < vg - > chip . ngpio ; base + = 32 ) {
reg = byt_gpio_reg ( & vg - > chip , base , BYT_INT_STAT_REG ) ;
while ( ( pending = readl ( reg ) ) ) {
pin = __ffs ( pending ) ;
mask = BIT ( pin ) ;
/* Clear before handling so we can't lose an edge */
writel ( mask , reg ) ;
virq = irq_find_mapping ( vg - > domain , base + pin ) ;
generic_handle_irq ( virq ) ;
/* In case bios or user sets triggering incorretly a pin
* might remain in " interrupt triggered " state .
*/
if ( looplimit + + > 32 ) {
dev_err ( & vg - > pdev - > dev ,
" Gpio %d interrupt flood, disabling \n " ,
base + pin ) ;
reg = byt_gpio_reg ( & vg - > chip , base + pin ,
BYT_CONF0_REG ) ;
mask = readl ( reg ) ;
mask & = ~ ( BYT_TRIG_NEG | BYT_TRIG_POS |
BYT_TRIG_LVL ) ;
writel ( mask , reg ) ;
mask = readl ( reg ) ; /* flush */
break ;
}
}
}
chip - > irq_eoi ( data ) ;
}
static void byt_irq_unmask ( struct irq_data * d )
{
}
static void byt_irq_mask ( struct irq_data * d )
{
}
static struct irq_chip byt_irqchip = {
. name = " BYT-GPIO " ,
. irq_mask = byt_irq_mask ,
. irq_unmask = byt_irq_unmask ,
. irq_set_type = byt_irq_type ,
} ;
static void byt_gpio_irq_init_hw ( struct byt_gpio * vg )
{
void __iomem * reg ;
u32 base , value ;
/* clear interrupt status trigger registers */
for ( base = 0 ; base < vg - > chip . ngpio ; base + = 32 ) {
reg = byt_gpio_reg ( & vg - > chip , base , BYT_INT_STAT_REG ) ;
writel ( 0xffffffff , reg ) ;
/* make sure trigger bits are cleared, if not then a pin
might be misconfigured in bios */
value = readl ( reg ) ;
if ( value )
dev_err ( & vg - > pdev - > dev ,
" GPIO interrupt error, pins misconfigured \n " ) ;
}
}
static int byt_gpio_irq_map ( struct irq_domain * d , unsigned int virq ,
irq_hw_number_t hw )
{
struct byt_gpio * vg = d - > host_data ;
irq_set_chip_and_handler_name ( virq , & byt_irqchip , handle_simple_irq ,
" demux " ) ;
irq_set_chip_data ( virq , vg ) ;
irq_set_irq_type ( virq , IRQ_TYPE_NONE ) ;
return 0 ;
}
static const struct irq_domain_ops byt_gpio_irq_ops = {
. map = byt_gpio_irq_map ,
} ;
static int byt_gpio_probe ( struct platform_device * pdev )
{
struct byt_gpio * vg ;
struct gpio_chip * gc ;
struct resource * mem_rc , * irq_rc ;
struct device * dev = & pdev - > dev ;
struct acpi_device * acpi_dev ;
struct pinctrl_gpio_range * range ;
acpi_handle handle = ACPI_HANDLE ( dev ) ;
unsigned hwirq ;
int ret ;
if ( acpi_bus_get_device ( handle , & acpi_dev ) )
return - ENODEV ;
vg = devm_kzalloc ( dev , sizeof ( struct byt_gpio ) , GFP_KERNEL ) ;
if ( ! vg ) {
dev_err ( & pdev - > dev , " can't allocate byt_gpio chip data \n " ) ;
return - ENOMEM ;
}
for ( range = byt_ranges ; range - > name ; range + + ) {
if ( ! strcmp ( acpi_dev - > pnp . unique_id , range - > name ) ) {
vg - > chip . ngpio = range - > npins ;
vg - > range = range ;
break ;
}
}
if ( ! vg - > chip . ngpio | | ! vg - > range )
return - ENODEV ;
vg - > pdev = pdev ;
platform_set_drvdata ( pdev , vg ) ;
mem_rc = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
vg - > reg_base = devm_ioremap_resource ( dev , mem_rc ) ;
if ( IS_ERR ( vg - > reg_base ) )
return PTR_ERR ( vg - > reg_base ) ;
spin_lock_init ( & vg - > lock ) ;
gc = & vg - > chip ;
gc - > label = dev_name ( & pdev - > dev ) ;
gc - > owner = THIS_MODULE ;
gc - > request = byt_gpio_request ;
gc - > free = byt_gpio_free ;
gc - > direction_input = byt_gpio_direction_input ;
gc - > direction_output = byt_gpio_direction_output ;
gc - > get = byt_gpio_get ;
gc - > set = byt_gpio_set ;
gc - > dbg_show = byt_gpio_dbg_show ;
gc - > base = - 1 ;
gc - > can_sleep = 0 ;
gc - > dev = dev ;
ret = gpiochip_add ( gc ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed adding byt-gpio chip \n " ) ;
return ret ;
}
/* set up interrupts */
irq_rc = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
if ( irq_rc & & irq_rc - > start ) {
hwirq = irq_rc - > start ;
gc - > to_irq = byt_gpio_to_irq ;
vg - > domain = irq_domain_add_linear ( NULL , gc - > ngpio ,
& byt_gpio_irq_ops , vg ) ;
if ( ! vg - > domain )
return - ENXIO ;
byt_gpio_irq_init_hw ( vg ) ;
irq_set_handler_data ( hwirq , vg ) ;
irq_set_chained_handler ( hwirq , byt_gpio_irq_handler ) ;
/* Register interrupt handlers for gpio signaled acpi events */
acpi_gpiochip_request_interrupts ( gc ) ;
}
pm_runtime_enable ( dev ) ;
return 0 ;
}
static int byt_gpio_runtime_suspend ( struct device * dev )
{
return 0 ;
}
static int byt_gpio_runtime_resume ( struct device * dev )
{
return 0 ;
}
static const struct dev_pm_ops byt_gpio_pm_ops = {
. runtime_suspend = byt_gpio_runtime_suspend ,
. runtime_resume = byt_gpio_runtime_resume ,
} ;
static const struct acpi_device_id byt_gpio_acpi_match [ ] = {
{ " INT33B2 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( acpi , byt_gpio_acpi_match ) ;
static int byt_gpio_remove ( struct platform_device * pdev )
{
struct byt_gpio * vg = platform_get_drvdata ( pdev ) ;
int err ;
2013-07-10 15:55:36 +04:00
2013-06-18 15:33:02 +04:00
pm_runtime_disable ( & pdev - > dev ) ;
err = gpiochip_remove ( & vg - > chip ) ;
if ( err )
dev_warn ( & pdev - > dev , " failed to remove gpio_chip. \n " ) ;
return 0 ;
}
static struct platform_driver byt_gpio_driver = {
. probe = byt_gpio_probe ,
. remove = byt_gpio_remove ,
. driver = {
. name = " byt_gpio " ,
. owner = THIS_MODULE ,
. pm = & byt_gpio_pm_ops ,
. acpi_match_table = ACPI_PTR ( byt_gpio_acpi_match ) ,
} ,
} ;
static int __init byt_gpio_init ( void )
{
return platform_driver_register ( & byt_gpio_driver ) ;
}
subsys_initcall ( byt_gpio_init ) ;