2010-03-02 12:48:55 +03:00
/*
2011-06-05 04:38:28 +04:00
* GPIO interface for Intel Poulsbo SCH
2010-03-02 12:48:55 +03:00
*
* Copyright ( c ) 2010 CompuLab Ltd
* Author : Denis Turischev < denis @ compulab . co . il >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License 2 as published
* by the Free Software Foundation .
*
* This program is distributed in the hope that 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 ; see the file COPYING . If not , write to
* the Free Software Foundation , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/io.h>
# include <linux/errno.h>
# include <linux/acpi.h>
# include <linux/platform_device.h>
2011-03-14 13:53:05 +03:00
# include <linux/pci_ids.h>
2010-03-02 12:48:55 +03:00
# include <linux/gpio.h>
static DEFINE_SPINLOCK ( gpio_lock ) ;
# define CGEN (0x00)
# define CGIO (0x04)
# define CGLV (0x08)
# define RGEN (0x20)
# define RGIO (0x24)
# define RGLV (0x28)
static unsigned short gpio_ba ;
static int sch_gpio_core_direction_in ( struct gpio_chip * gc , unsigned gpio_num )
{
u8 curr_dirs ;
unsigned short offset , bit ;
spin_lock ( & gpio_lock ) ;
offset = CGIO + gpio_num / 8 ;
bit = gpio_num % 8 ;
curr_dirs = inb ( gpio_ba + offset ) ;
if ( ! ( curr_dirs & ( 1 < < bit ) ) )
outb ( curr_dirs | ( 1 < < bit ) , gpio_ba + offset ) ;
spin_unlock ( & gpio_lock ) ;
return 0 ;
}
static int sch_gpio_core_get ( struct gpio_chip * gc , unsigned gpio_num )
{
int res ;
unsigned short offset , bit ;
offset = CGLV + gpio_num / 8 ;
bit = gpio_num % 8 ;
res = ! ! ( inb ( gpio_ba + offset ) & ( 1 < < bit ) ) ;
return res ;
}
static void sch_gpio_core_set ( struct gpio_chip * gc , unsigned gpio_num , int val )
{
u8 curr_vals ;
unsigned short offset , bit ;
spin_lock ( & gpio_lock ) ;
offset = CGLV + gpio_num / 8 ;
bit = gpio_num % 8 ;
curr_vals = inb ( gpio_ba + offset ) ;
if ( val )
outb ( curr_vals | ( 1 < < bit ) , gpio_ba + offset ) ;
else
outb ( ( curr_vals & ~ ( 1 < < bit ) ) , gpio_ba + offset ) ;
spin_unlock ( & gpio_lock ) ;
}
static int sch_gpio_core_direction_out ( struct gpio_chip * gc ,
unsigned gpio_num , int val )
{
u8 curr_dirs ;
unsigned short offset , bit ;
sch_gpio_core_set ( gc , gpio_num , val ) ;
spin_lock ( & gpio_lock ) ;
offset = CGIO + gpio_num / 8 ;
bit = gpio_num % 8 ;
curr_dirs = inb ( gpio_ba + offset ) ;
if ( curr_dirs & ( 1 < < bit ) )
outb ( curr_dirs & ~ ( 1 < < bit ) , gpio_ba + offset ) ;
spin_unlock ( & gpio_lock ) ;
return 0 ;
}
static struct gpio_chip sch_gpio_core = {
. label = " sch_gpio_core " ,
. owner = THIS_MODULE ,
. direction_input = sch_gpio_core_direction_in ,
. get = sch_gpio_core_get ,
. direction_output = sch_gpio_core_direction_out ,
. set = sch_gpio_core_set ,
} ;
static int sch_gpio_resume_direction_in ( struct gpio_chip * gc ,
unsigned gpio_num )
{
u8 curr_dirs ;
spin_lock ( & gpio_lock ) ;
curr_dirs = inb ( gpio_ba + RGIO ) ;
if ( ! ( curr_dirs & ( 1 < < gpio_num ) ) )
outb ( curr_dirs | ( 1 < < gpio_num ) , gpio_ba + RGIO ) ;
spin_unlock ( & gpio_lock ) ;
return 0 ;
}
static int sch_gpio_resume_get ( struct gpio_chip * gc , unsigned gpio_num )
{
return ! ! ( inb ( gpio_ba + RGLV ) & ( 1 < < gpio_num ) ) ;
}
static void sch_gpio_resume_set ( struct gpio_chip * gc ,
unsigned gpio_num , int val )
{
u8 curr_vals ;
spin_lock ( & gpio_lock ) ;
curr_vals = inb ( gpio_ba + RGLV ) ;
if ( val )
outb ( curr_vals | ( 1 < < gpio_num ) , gpio_ba + RGLV ) ;
else
outb ( ( curr_vals & ~ ( 1 < < gpio_num ) ) , gpio_ba + RGLV ) ;
spin_unlock ( & gpio_lock ) ;
}
static int sch_gpio_resume_direction_out ( struct gpio_chip * gc ,
unsigned gpio_num , int val )
{
u8 curr_dirs ;
sch_gpio_resume_set ( gc , gpio_num , val ) ;
spin_lock ( & gpio_lock ) ;
curr_dirs = inb ( gpio_ba + RGIO ) ;
if ( curr_dirs & ( 1 < < gpio_num ) )
outb ( curr_dirs & ~ ( 1 < < gpio_num ) , gpio_ba + RGIO ) ;
spin_unlock ( & gpio_lock ) ;
return 0 ;
}
static struct gpio_chip sch_gpio_resume = {
. label = " sch_gpio_resume " ,
. owner = THIS_MODULE ,
. direction_input = sch_gpio_resume_direction_in ,
. get = sch_gpio_resume_get ,
. direction_output = sch_gpio_resume_direction_out ,
. set = sch_gpio_resume_set ,
} ;
2012-11-19 22:22:34 +04:00
static int sch_gpio_probe ( struct platform_device * pdev )
2010-03-02 12:48:55 +03:00
{
struct resource * res ;
2011-03-14 13:53:05 +03:00
int err , id ;
id = pdev - > id ;
if ( ! id )
return - ENODEV ;
2010-03-02 12:48:55 +03:00
res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
if ( ! res )
return - EBUSY ;
if ( ! request_region ( res - > start , resource_size ( res ) , pdev - > name ) )
return - EBUSY ;
gpio_ba = res - > start ;
2011-03-14 13:53:05 +03:00
switch ( id ) {
case PCI_DEVICE_ID_INTEL_SCH_LPC :
sch_gpio_core . base = 0 ;
sch_gpio_core . ngpio = 10 ;
sch_gpio_resume . base = 10 ;
sch_gpio_resume . ngpio = 4 ;
/*
* GPIO [ 6 : 0 ] enabled by default
* GPIO7 is configured by the CMC as SLPIOVR
* Enable GPIO [ 9 : 8 ] core powered gpios explicitly
*/
outb ( 0x3 , gpio_ba + CGEN + 1 ) ;
/*
* SUS_GPIO [ 2 : 0 ] enabled by default
* Enable SUS_GPIO3 resume powered gpio explicitly
*/
outb ( 0x8 , gpio_ba + RGEN ) ;
break ;
case PCI_DEVICE_ID_INTEL_ITC_LPC :
sch_gpio_core . base = 0 ;
sch_gpio_core . ngpio = 5 ;
sch_gpio_resume . base = 5 ;
sch_gpio_resume . ngpio = 9 ;
break ;
2012-05-23 00:54:20 +04:00
case PCI_DEVICE_ID_INTEL_CENTERTON_ILB :
sch_gpio_core . base = 0 ;
sch_gpio_core . ngpio = 21 ;
sch_gpio_resume . base = 21 ;
sch_gpio_resume . ngpio = 9 ;
break ;
2011-03-14 13:53:05 +03:00
default :
2012-07-24 16:55:10 +04:00
err = - ENODEV ;
goto err_sch_gpio_core ;
2011-03-14 13:53:05 +03:00
}
2010-03-02 12:48:55 +03:00
2011-03-14 13:53:05 +03:00
sch_gpio_core . dev = & pdev - > dev ;
2010-03-02 12:48:55 +03:00
sch_gpio_resume . dev = & pdev - > dev ;
err = gpiochip_add ( & sch_gpio_core ) ;
if ( err < 0 )
goto err_sch_gpio_core ;
err = gpiochip_add ( & sch_gpio_resume ) ;
if ( err < 0 )
goto err_sch_gpio_resume ;
return 0 ;
err_sch_gpio_resume :
2010-03-05 19:14:01 +03:00
err = gpiochip_remove ( & sch_gpio_core ) ;
if ( err )
dev_err ( & pdev - > dev , " %s failed, %d \n " ,
" gpiochip_remove() " , err ) ;
2010-03-02 12:48:55 +03:00
err_sch_gpio_core :
release_region ( res - > start , resource_size ( res ) ) ;
gpio_ba = 0 ;
return err ;
}
static int __devexit sch_gpio_remove ( struct platform_device * pdev )
{
struct resource * res ;
if ( gpio_ba ) {
2010-03-05 19:14:01 +03:00
int err ;
err = gpiochip_remove ( & sch_gpio_core ) ;
if ( err )
dev_err ( & pdev - > dev , " %s failed, %d \n " ,
" gpiochip_remove() " , err ) ;
err = gpiochip_remove ( & sch_gpio_resume ) ;
if ( err )
dev_err ( & pdev - > dev , " %s failed, %d \n " ,
" gpiochip_remove() " , err ) ;
2010-03-02 12:48:55 +03:00
res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
release_region ( res - > start , resource_size ( res ) ) ;
gpio_ba = 0 ;
2010-03-05 19:14:01 +03:00
return err ;
2010-03-02 12:48:55 +03:00
}
return 0 ;
}
static struct platform_driver sch_gpio_driver = {
. driver = {
. name = " sch_gpio " ,
. owner = THIS_MODULE ,
} ,
. probe = sch_gpio_probe ,
2012-11-19 22:20:08 +04:00
. remove = sch_gpio_remove ,
2010-03-02 12:48:55 +03:00
} ;
2011-12-07 20:24:00 +04:00
module_platform_driver ( sch_gpio_driver ) ;
2010-03-02 12:48:55 +03:00
MODULE_AUTHOR ( " Denis Turischev <denis@compulab.co.il> " ) ;
MODULE_DESCRIPTION ( " GPIO interface for Intel Poulsbo SCH " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:sch_gpio " ) ;