2012-12-12 19:42:38 +04:00
/*
* GPIO controller driver for Intel Lynxpoint PCH chipset >
* Copyright ( c ) 2012 , 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/gpio.h>
# include <linux/slab.h>
# include <linux/acpi.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
2013-03-08 16:38:12 +04:00
# include <linux/io.h>
2012-12-12 19:42:38 +04:00
/* LynxPoint chipset has support for 94 gpio pins */
# define LP_NUM_GPIO 94
/* Bitmapped register offsets */
# define LP_ACPI_OWNED 0x00 /* Bitmap, set by bios, 0: pin reserved for ACPI */
# define LP_GC 0x7C /* set APIC IRQ to IRQ14 or IRQ15 for all pins */
# define LP_INT_STAT 0x80
# define LP_INT_ENABLE 0x90
/* Each pin has two 32 bit config registers, starting at 0x100 */
# define LP_CONFIG1 0x100
# define LP_CONFIG2 0x104
/* LP_CONFIG1 reg bits */
# define OUT_LVL_BIT BIT(31)
# define IN_LVL_BIT BIT(30)
# define TRIG_SEL_BIT BIT(4) /* 0: Edge, 1: Level */
# define INT_INV_BIT BIT(3) /* Invert interrupt triggering */
# define DIR_BIT BIT(2) /* 0: Output, 1: Input */
# define USE_SEL_BIT BIT(0) /* 0: Native, 1: GPIO */
/* LP_CONFIG2 reg bits */
# define GPINDIS_BIT BIT(2) /* disable input sensing */
# define GPIWP_BIT (BIT(0) | BIT(1)) /* weak pull options */
struct lp_gpio {
struct gpio_chip chip ;
struct platform_device * pdev ;
spinlock_t lock ;
unsigned long reg_base ;
} ;
/*
* Lynxpoint gpios are controlled through both bitmapped registers and
* per gpio specific registers . The bitmapped registers are in chunks of
* 3 x 32 bit registers to cover all 94 gpios
*
* per gpio specific registers consist of two 32 bit registers per gpio
* ( LP_CONFIG1 and LP_CONFIG2 ) , with 94 gpios there ' s a total of
2015-05-18 21:41:43 +03:00
* 188 config registers .
2012-12-12 19:42:38 +04:00
*
* A simplified view of the register layout look like this :
*
* LP_ACPI_OWNED [ 31 : 0 ] gpio ownerships for gpios 0 - 31 ( bitmapped registers )
* LP_ACPI_OWNED [ 63 : 32 ] gpio ownerships for gpios 32 - 63
* LP_ACPI_OWNED [ 94 : 64 ] gpio ownerships for gpios 63 - 94
* . . .
* LP_INT_ENABLE [ 31 : 0 ] . . .
* LP_INT_ENABLE [ 63 : 31 ] . . .
* LP_INT_ENABLE [ 94 : 64 ] . . .
* LP0_CONFIG1 ( gpio 0 ) config1 reg for gpio 0 ( per gpio registers )
* LP0_CONFIG2 ( gpio 0 ) config2 reg for gpio 0
* LP1_CONFIG1 ( gpio 1 ) config1 reg for gpio 1
* LP1_CONFIG2 ( gpio 1 ) config2 reg for gpio 1
* LP2_CONFIG1 ( gpio 2 ) . . .
* LP2_CONFIG2 ( gpio 2 ) . . .
* . . .
* LP94_CONFIG1 ( gpio 94 ) . . .
* LP94_CONFIG2 ( gpio 94 ) . . .
*/
static unsigned long lp_gpio_reg ( struct gpio_chip * chip , unsigned offset ,
int reg )
{
struct lp_gpio * lg = container_of ( chip , struct lp_gpio , chip ) ;
int reg_offset ;
if ( reg = = LP_CONFIG1 | | reg = = LP_CONFIG2 )
/* per gpio specific config registers */
reg_offset = offset * 8 ;
else
/* bitmapped registers */
reg_offset = ( offset / 32 ) * 4 ;
return lg - > reg_base + reg + reg_offset ;
}
static int lp_gpio_request ( struct gpio_chip * chip , unsigned offset )
{
struct lp_gpio * lg = container_of ( chip , struct lp_gpio , chip ) ;
unsigned long reg = lp_gpio_reg ( chip , offset , LP_CONFIG1 ) ;
unsigned long conf2 = lp_gpio_reg ( chip , offset , LP_CONFIG2 ) ;
unsigned long acpi_use = lp_gpio_reg ( chip , offset , LP_ACPI_OWNED ) ;
pm_runtime_get ( & lg - > pdev - > dev ) ; /* should we put if failed */
/* Fail if BIOS reserved pin for ACPI use */
if ( ! ( inl ( acpi_use ) & BIT ( offset % 32 ) ) ) {
dev_err ( & lg - > pdev - > dev , " gpio %d reserved for ACPI \n " , offset ) ;
return - EBUSY ;
}
/* Fail if pin is in alternate function mode (not GPIO mode) */
if ( ! ( inl ( reg ) & USE_SEL_BIT ) )
return - ENODEV ;
/* enable input sensing */
outl ( inl ( conf2 ) & ~ GPINDIS_BIT , conf2 ) ;
return 0 ;
}
static void lp_gpio_free ( struct gpio_chip * chip , unsigned offset )
{
struct lp_gpio * lg = container_of ( chip , struct lp_gpio , chip ) ;
unsigned long conf2 = lp_gpio_reg ( chip , offset , LP_CONFIG2 ) ;
/* disable input sensing */
outl ( inl ( conf2 ) | GPINDIS_BIT , conf2 ) ;
pm_runtime_put ( & lg - > pdev - > dev ) ;
}
static int lp_irq_type ( struct irq_data * d , unsigned type )
{
2014-07-25 10:54:46 +04:00
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct lp_gpio * lg = container_of ( gc , struct lp_gpio , chip ) ;
2012-12-12 19:42:38 +04:00
u32 hwirq = irqd_to_hwirq ( d ) ;
unsigned long flags ;
u32 value ;
unsigned long reg = lp_gpio_reg ( & lg - > chip , hwirq , LP_CONFIG1 ) ;
if ( hwirq > = lg - > chip . ngpio )
return - EINVAL ;
spin_lock_irqsave ( & lg - > lock , flags ) ;
value = inl ( reg ) ;
/* set both TRIG_SEL and INV bits to 0 for rising edge */
if ( type & IRQ_TYPE_EDGE_RISING )
value & = ~ ( TRIG_SEL_BIT | INT_INV_BIT ) ;
/* TRIG_SEL bit 0, INV bit 1 for falling edge */
if ( type & IRQ_TYPE_EDGE_FALLING )
value = ( value | INT_INV_BIT ) & ~ TRIG_SEL_BIT ;
/* TRIG_SEL bit 1, INV bit 0 for level low */
if ( type & IRQ_TYPE_LEVEL_LOW )
value = ( value | TRIG_SEL_BIT ) & ~ INT_INV_BIT ;
/* TRIG_SEL bit 1, INV bit 1 for level high */
if ( type & IRQ_TYPE_LEVEL_HIGH )
value | = TRIG_SEL_BIT | INT_INV_BIT ;
outl ( value , reg ) ;
spin_unlock_irqrestore ( & lg - > lock , flags ) ;
return 0 ;
}
static int lp_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
unsigned long reg = lp_gpio_reg ( chip , offset , LP_CONFIG1 ) ;
2014-03-27 17:02:08 +04:00
return ! ! ( inl ( reg ) & IN_LVL_BIT ) ;
2012-12-12 19:42:38 +04:00
}
static void lp_gpio_set ( struct gpio_chip * chip , unsigned offset , int value )
{
struct lp_gpio * lg = container_of ( chip , struct lp_gpio , chip ) ;
unsigned long reg = lp_gpio_reg ( chip , offset , LP_CONFIG1 ) ;
unsigned long flags ;
spin_lock_irqsave ( & lg - > lock , flags ) ;
if ( value )
outl ( inl ( reg ) | OUT_LVL_BIT , reg ) ;
else
outl ( inl ( reg ) & ~ OUT_LVL_BIT , reg ) ;
spin_unlock_irqrestore ( & lg - > lock , flags ) ;
}
static int lp_gpio_direction_input ( struct gpio_chip * chip , unsigned offset )
{
struct lp_gpio * lg = container_of ( chip , struct lp_gpio , chip ) ;
unsigned long reg = lp_gpio_reg ( chip , offset , LP_CONFIG1 ) ;
unsigned long flags ;
spin_lock_irqsave ( & lg - > lock , flags ) ;
outl ( inl ( reg ) | DIR_BIT , reg ) ;
spin_unlock_irqrestore ( & lg - > lock , flags ) ;
return 0 ;
}
static int lp_gpio_direction_output ( struct gpio_chip * chip ,
unsigned offset , int value )
{
struct lp_gpio * lg = container_of ( chip , struct lp_gpio , chip ) ;
unsigned long reg = lp_gpio_reg ( chip , offset , LP_CONFIG1 ) ;
unsigned long flags ;
lp_gpio_set ( chip , offset , value ) ;
spin_lock_irqsave ( & lg - > lock , flags ) ;
outl ( inl ( reg ) & ~ DIR_BIT , reg ) ;
spin_unlock_irqrestore ( & lg - > lock , flags ) ;
return 0 ;
}
2015-09-14 11:42:37 +03:00
static void lp_gpio_irq_handler ( struct irq_desc * desc )
2012-12-12 19:42:38 +04:00
{
struct irq_data * data = irq_desc_get_irq_data ( desc ) ;
2014-07-25 10:54:46 +04:00
struct gpio_chip * gc = irq_desc_get_handler_data ( desc ) ;
struct lp_gpio * lg = container_of ( gc , struct lp_gpio , chip ) ;
2012-12-12 19:42:38 +04:00
struct irq_chip * chip = irq_data_get_irq_chip ( data ) ;
u32 base , pin , mask ;
2013-10-01 18:35:43 +04:00
unsigned long reg , ena , pending ;
2012-12-12 19:42:38 +04:00
/* check from GPIO controller which pin triggered the interrupt */
for ( base = 0 ; base < lg - > chip . ngpio ; base + = 32 ) {
reg = lp_gpio_reg ( & lg - > chip , base , LP_INT_STAT ) ;
2013-10-01 18:35:43 +04:00
ena = lp_gpio_reg ( & lg - > chip , base , LP_INT_ENABLE ) ;
2012-12-12 19:42:38 +04:00
2013-10-01 18:35:43 +04:00
while ( ( pending = ( inl ( reg ) & inl ( ena ) ) ) ) {
2013-10-11 21:32:16 +04:00
unsigned irq ;
2012-12-12 19:42:38 +04:00
pin = __ffs ( pending ) ;
mask = BIT ( pin ) ;
/* Clear before handling so we don't lose an edge */
outl ( mask , reg ) ;
2014-07-25 10:54:46 +04:00
irq = irq_find_mapping ( lg - > chip . irqdomain , base + pin ) ;
2013-10-11 21:32:16 +04:00
generic_handle_irq ( irq ) ;
2012-12-12 19:42:38 +04:00
}
}
chip - > irq_eoi ( data ) ;
}
static void lp_irq_unmask ( struct irq_data * d )
{
}
static void lp_irq_mask ( struct irq_data * d )
{
}
static void lp_irq_enable ( struct irq_data * d )
{
2014-07-25 10:54:46 +04:00
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct lp_gpio * lg = container_of ( gc , struct lp_gpio , chip ) ;
2012-12-12 19:42:38 +04:00
u32 hwirq = irqd_to_hwirq ( d ) ;
unsigned long reg = lp_gpio_reg ( & lg - > chip , hwirq , LP_INT_ENABLE ) ;
unsigned long flags ;
spin_lock_irqsave ( & lg - > lock , flags ) ;
outl ( inl ( reg ) | BIT ( hwirq % 32 ) , reg ) ;
spin_unlock_irqrestore ( & lg - > lock , flags ) ;
}
static void lp_irq_disable ( struct irq_data * d )
{
2014-07-25 10:54:46 +04:00
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct lp_gpio * lg = container_of ( gc , struct lp_gpio , chip ) ;
2012-12-12 19:42:38 +04:00
u32 hwirq = irqd_to_hwirq ( d ) ;
unsigned long reg = lp_gpio_reg ( & lg - > chip , hwirq , LP_INT_ENABLE ) ;
unsigned long flags ;
spin_lock_irqsave ( & lg - > lock , flags ) ;
outl ( inl ( reg ) & ~ BIT ( hwirq % 32 ) , reg ) ;
spin_unlock_irqrestore ( & lg - > lock , flags ) ;
}
static struct irq_chip lp_irqchip = {
. name = " LP-GPIO " ,
. irq_mask = lp_irq_mask ,
. irq_unmask = lp_irq_unmask ,
. irq_enable = lp_irq_enable ,
. irq_disable = lp_irq_disable ,
. irq_set_type = lp_irq_type ,
. flags = IRQCHIP_SKIP_SET_WAKE ,
} ;
static void lp_gpio_irq_init_hw ( struct lp_gpio * lg )
{
unsigned long reg ;
unsigned base ;
for ( base = 0 ; base < lg - > chip . ngpio ; base + = 32 ) {
/* disable gpio pin interrupts */
reg = lp_gpio_reg ( & lg - > chip , base , LP_INT_ENABLE ) ;
outl ( 0 , reg ) ;
/* Clear interrupt status register */
reg = lp_gpio_reg ( & lg - > chip , base , LP_INT_STAT ) ;
outl ( 0xffffffff , reg ) ;
}
}
static int lp_gpio_probe ( struct platform_device * pdev )
{
struct lp_gpio * lg ;
struct gpio_chip * gc ;
struct resource * io_rc , * irq_rc ;
struct device * dev = & pdev - > dev ;
unsigned long reg_len ;
int ret = - ENODEV ;
lg = devm_kzalloc ( dev , sizeof ( struct lp_gpio ) , GFP_KERNEL ) ;
2014-04-29 12:36:26 +04:00
if ( ! lg )
2012-12-12 19:42:38 +04:00
return - ENOMEM ;
lg - > pdev = pdev ;
platform_set_drvdata ( pdev , lg ) ;
io_rc = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
irq_rc = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
if ( ! io_rc ) {
dev_err ( dev , " missing IO resources \n " ) ;
return - EINVAL ;
}
lg - > reg_base = io_rc - > start ;
reg_len = resource_size ( io_rc ) ;
if ( ! devm_request_region ( dev , lg - > reg_base , reg_len , " lp-gpio " ) ) {
dev_err ( dev , " failed requesting IO region 0x%x \n " ,
( unsigned int ) lg - > reg_base ) ;
return - EBUSY ;
}
spin_lock_init ( & lg - > lock ) ;
gc = & lg - > chip ;
gc - > label = dev_name ( dev ) ;
gc - > owner = THIS_MODULE ;
gc - > request = lp_gpio_request ;
gc - > free = lp_gpio_free ;
gc - > direction_input = lp_gpio_direction_input ;
gc - > direction_output = lp_gpio_direction_output ;
gc - > get = lp_gpio_get ;
gc - > set = lp_gpio_set ;
gc - > base = - 1 ;
gc - > ngpio = LP_NUM_GPIO ;
2013-12-04 17:42:46 +04:00
gc - > can_sleep = false ;
2015-11-04 11:56:26 +03:00
gc - > parent = dev ;
2012-12-12 19:42:38 +04:00
2014-07-25 10:54:46 +04:00
ret = gpiochip_add ( gc ) ;
if ( ret ) {
dev_err ( dev , " failed adding lp-gpio chip \n " ) ;
return ret ;
}
2012-12-12 19:42:38 +04:00
/* set up interrupts */
if ( irq_rc & & irq_rc - > start ) {
lp_gpio_irq_init_hw ( lg ) ;
2014-07-25 10:54:46 +04:00
ret = gpiochip_irqchip_add ( gc , & lp_irqchip , 0 ,
handle_simple_irq , IRQ_TYPE_NONE ) ;
if ( ret ) {
dev_err ( dev , " failed to add irqchip \n " ) ;
gpiochip_remove ( gc ) ;
return ret ;
}
2012-12-12 19:42:38 +04:00
2014-07-25 10:54:46 +04:00
gpiochip_set_chained_irqchip ( gc , & lp_irqchip ,
( unsigned ) irq_rc - > start ,
lp_gpio_irq_handler ) ;
2012-12-12 19:42:38 +04:00
}
pm_runtime_enable ( dev ) ;
return 0 ;
}
static int lp_gpio_runtime_suspend ( struct device * dev )
{
return 0 ;
}
static int lp_gpio_runtime_resume ( struct device * dev )
{
return 0 ;
}
2014-08-19 15:00:01 +04:00
static int lp_gpio_resume ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct lp_gpio * lg = platform_get_drvdata ( pdev ) ;
unsigned long reg ;
int i ;
/* on some hardware suspend clears input sensing, re-enable it here */
for ( i = 0 ; i < lg - > chip . ngpio ; i + + ) {
if ( gpiochip_is_requested ( & lg - > chip , i ) ! = NULL ) {
reg = lp_gpio_reg ( & lg - > chip , i , LP_CONFIG2 ) ;
outl ( inl ( reg ) & ~ GPINDIS_BIT , reg ) ;
}
}
return 0 ;
}
2012-12-12 19:42:38 +04:00
static const struct dev_pm_ops lp_gpio_pm_ops = {
. runtime_suspend = lp_gpio_runtime_suspend ,
. runtime_resume = lp_gpio_runtime_resume ,
2014-08-19 15:00:01 +04:00
. resume = lp_gpio_resume ,
2012-12-12 19:42:38 +04:00
} ;
static const struct acpi_device_id lynxpoint_gpio_acpi_match [ ] = {
{ " INT33C7 " , 0 } ,
2013-11-12 13:52:32 +04:00
{ " INT3437 " , 0 } ,
2012-12-12 19:42:38 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( acpi , lynxpoint_gpio_acpi_match ) ;
static int lp_gpio_remove ( struct platform_device * pdev )
{
struct lp_gpio * lg = platform_get_drvdata ( pdev ) ;
2013-08-12 15:05:41 +04:00
pm_runtime_disable ( & pdev - > dev ) ;
2014-07-13 00:30:12 +04:00
gpiochip_remove ( & lg - > chip ) ;
2012-12-12 19:42:38 +04:00
return 0 ;
}
static struct platform_driver lp_gpio_driver = {
. probe = lp_gpio_probe ,
. remove = lp_gpio_remove ,
. driver = {
. name = " lp_gpio " ,
. pm = & lp_gpio_pm_ops ,
. acpi_match_table = ACPI_PTR ( lynxpoint_gpio_acpi_match ) ,
} ,
} ;
static int __init lp_gpio_init ( void )
{
return platform_driver_register ( & lp_gpio_driver ) ;
}
2013-11-27 18:46:06 +04:00
static void __exit lp_gpio_exit ( void )
{
platform_driver_unregister ( & lp_gpio_driver ) ;
}
2012-12-12 19:42:38 +04:00
subsys_initcall ( lp_gpio_init ) ;
2013-11-27 18:46:06 +04:00
module_exit ( lp_gpio_exit ) ;
MODULE_AUTHOR ( " Mathias Nyman (Intel) " ) ;
MODULE_DESCRIPTION ( " GPIO interface for Intel Lynxpoint " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:lp_gpio " ) ;