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>
2014-10-21 15:33:56 +04:00
# define GEN 0x00
# define GIO 0x04
# define GLV 0x08
struct sch_gpio {
struct gpio_chip chip ;
spinlock_t lock ;
unsigned short iobase ;
unsigned short core_base ;
unsigned short resume_base ;
} ;
2010-03-02 12:48:55 +03:00
2014-10-21 15:33:56 +04:00
static unsigned sch_gpio_offset ( struct sch_gpio * sch , unsigned gpio ,
unsigned reg )
2010-03-02 12:48:55 +03:00
{
2014-10-21 15:33:56 +04:00
unsigned base = 0 ;
2010-03-02 12:48:55 +03:00
2014-10-21 15:33:56 +04:00
if ( gpio > = sch - > resume_base ) {
gpio - = sch - > resume_base ;
base + = 0x20 ;
}
2010-03-02 12:48:55 +03:00
2014-10-21 15:33:56 +04:00
return base + reg + gpio / 8 ;
2010-03-02 12:48:55 +03:00
}
2014-10-21 15:33:56 +04:00
static unsigned sch_gpio_bit ( struct sch_gpio * sch , unsigned gpio )
2010-03-02 12:48:55 +03:00
{
2014-10-21 15:33:56 +04:00
if ( gpio > = sch - > resume_base )
gpio - = sch - > resume_base ;
return gpio % 8 ;
2010-03-02 12:48:55 +03:00
}
2016-06-18 21:05:04 +03:00
static int sch_gpio_reg_get ( struct sch_gpio * sch , unsigned gpio , unsigned reg )
2010-03-02 12:48:55 +03:00
{
unsigned short offset , bit ;
2015-01-21 13:32:21 +03:00
u8 reg_val ;
2010-03-02 12:48:55 +03:00
2015-01-21 13:32:21 +03:00
offset = sch_gpio_offset ( sch , gpio , reg ) ;
2014-10-21 15:33:56 +04:00
bit = sch_gpio_bit ( sch , gpio ) ;
2010-03-02 12:48:55 +03:00
2015-01-21 13:32:21 +03:00
reg_val = ! ! ( inb ( sch - > iobase + offset ) & BIT ( bit ) ) ;
2014-04-07 16:20:32 +04:00
2015-01-21 13:32:21 +03:00
return reg_val ;
2010-03-02 12:48:55 +03:00
}
2016-06-18 21:05:04 +03:00
static void sch_gpio_reg_set ( struct sch_gpio * sch , unsigned gpio , unsigned reg ,
2015-01-21 13:32:21 +03:00
int val )
2010-03-02 12:48:55 +03:00
{
2013-03-07 01:49:36 +04:00
unsigned short offset , bit ;
2015-01-21 13:32:21 +03:00
u8 reg_val ;
2010-03-02 12:48:55 +03:00
2015-01-21 13:32:21 +03:00
offset = sch_gpio_offset ( sch , gpio , reg ) ;
bit = sch_gpio_bit ( sch , gpio ) ;
2010-03-02 12:48:55 +03:00
2015-01-21 13:32:21 +03:00
reg_val = inb ( sch - > iobase + offset ) ;
2013-03-07 01:49:36 +04:00
2015-01-21 13:32:21 +03:00
if ( val )
outb ( reg_val | BIT ( bit ) , sch - > iobase + offset ) ;
else
outb ( ( reg_val & ~ BIT ( bit ) ) , sch - > iobase + offset ) ;
}
2010-03-02 12:48:55 +03:00
2015-01-21 13:32:21 +03:00
static int sch_gpio_direction_in ( struct gpio_chip * gc , unsigned gpio_num )
{
2015-12-07 16:21:49 +03:00
struct sch_gpio * sch = gpiochip_get_data ( gc ) ;
2010-03-02 12:48:55 +03:00
2015-01-21 13:32:21 +03:00
spin_lock ( & sch - > lock ) ;
2016-06-18 21:05:04 +03:00
sch_gpio_reg_set ( sch , gpio_num , GIO , 1 ) ;
2014-10-21 15:33:56 +04:00
spin_unlock ( & sch - > lock ) ;
2010-03-02 12:48:55 +03:00
return 0 ;
}
2014-10-21 15:33:56 +04:00
static int sch_gpio_get ( struct gpio_chip * gc , unsigned gpio_num )
2010-03-02 12:48:55 +03:00
{
2016-06-18 21:05:04 +03:00
struct sch_gpio * sch = gpiochip_get_data ( gc ) ;
return sch_gpio_reg_get ( sch , gpio_num , GLV ) ;
2010-03-02 12:48:55 +03:00
}
2014-10-21 15:33:56 +04:00
static void sch_gpio_set ( struct gpio_chip * gc , unsigned gpio_num , int val )
2010-03-02 12:48:55 +03:00
{
2015-12-07 16:21:49 +03:00
struct sch_gpio * sch = gpiochip_get_data ( gc ) ;
2010-03-02 12:48:55 +03:00
2014-10-21 15:33:56 +04:00
spin_lock ( & sch - > lock ) ;
2016-06-18 21:05:04 +03:00
sch_gpio_reg_set ( sch , gpio_num , GLV , val ) ;
2014-10-21 15:33:56 +04:00
spin_unlock ( & sch - > lock ) ;
2010-03-02 12:48:55 +03:00
}
2014-10-21 15:33:56 +04:00
static int sch_gpio_direction_out ( struct gpio_chip * gc , unsigned gpio_num ,
int val )
2010-03-02 12:48:55 +03:00
{
2015-12-07 16:21:49 +03:00
struct sch_gpio * sch = gpiochip_get_data ( gc ) ;
2010-03-02 12:48:55 +03:00
2014-10-21 15:33:56 +04:00
spin_lock ( & sch - > lock ) ;
2016-06-18 21:05:04 +03:00
sch_gpio_reg_set ( sch , gpio_num , GIO , 0 ) ;
2014-10-21 15:33:56 +04:00
spin_unlock ( & sch - > lock ) ;
2014-04-07 16:20:32 +04:00
/*
2014-10-21 15:33:56 +04:00
* according to the datasheet , writing to the level register has no
* effect when GPIO is programmed as input .
* Actually the the level register is read - only when configured as input .
* Thus presetting the output level before switching to output is _NOT_ possible .
* Hence we set the level after configuring the GPIO as output .
* But we cannot prevent a short low pulse if direction is set to high
* and an external pull - up is connected .
*/
sch_gpio_set ( gc , gpio_num , val ) ;
2010-03-02 12:48:55 +03:00
return 0 ;
}
2016-09-11 15:14:37 +03:00
static const struct gpio_chip sch_gpio_chip = {
2014-10-21 15:33:56 +04:00
. label = " sch_gpio " ,
2010-03-02 12:48:55 +03:00
. owner = THIS_MODULE ,
2014-10-21 15:33:56 +04:00
. direction_input = sch_gpio_direction_in ,
. get = sch_gpio_get ,
. direction_output = sch_gpio_direction_out ,
. set = sch_gpio_set ,
2010-03-02 12:48:55 +03:00
} ;
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
{
2014-10-21 15:33:56 +04:00
struct sch_gpio * sch ;
2010-03-02 12:48:55 +03:00
struct resource * res ;
2011-03-14 13:53:05 +03:00
2014-10-21 15:33:56 +04:00
sch = devm_kzalloc ( & pdev - > dev , sizeof ( * sch ) , GFP_KERNEL ) ;
if ( ! sch )
return - ENOMEM ;
2010-03-02 12:48:55 +03:00
res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
if ( ! res )
return - EBUSY ;
2014-10-21 15:33:56 +04:00
if ( ! devm_request_region ( & pdev - > dev , res - > start , resource_size ( res ) ,
pdev - > name ) )
2010-03-02 12:48:55 +03:00
return - EBUSY ;
2014-10-21 15:33:56 +04:00
spin_lock_init ( & sch - > lock ) ;
sch - > iobase = res - > start ;
sch - > chip = sch_gpio_chip ;
sch - > chip . label = dev_name ( & pdev - > dev ) ;
2015-11-04 11:56:26 +03:00
sch - > chip . parent = & pdev - > dev ;
2010-03-02 12:48:55 +03:00
2014-10-21 15:33:56 +04:00
switch ( pdev - > id ) {
2013-03-20 16:16:00 +04:00
case PCI_DEVICE_ID_INTEL_SCH_LPC :
2014-10-21 15:33:56 +04:00
sch - > core_base = 0 ;
sch - > resume_base = 10 ;
sch - > chip . ngpio = 14 ;
2013-03-20 16:16:00 +04:00
/*
* GPIO [ 6 : 0 ] enabled by default
* GPIO7 is configured by the CMC as SLPIOVR
* Enable GPIO [ 9 : 8 ] core powered gpios explicitly
*/
2016-06-18 21:05:04 +03:00
sch_gpio_reg_set ( sch , 8 , GEN , 1 ) ;
sch_gpio_reg_set ( sch , 9 , GEN , 1 ) ;
2013-03-20 16:16:00 +04:00
/*
* SUS_GPIO [ 2 : 0 ] enabled by default
* Enable SUS_GPIO3 resume powered gpio explicitly
*/
2016-06-18 21:05:04 +03:00
sch_gpio_reg_set ( sch , 13 , GEN , 1 ) ;
2013-03-20 16:16:00 +04:00
break ;
case PCI_DEVICE_ID_INTEL_ITC_LPC :
2014-10-21 15:33:56 +04:00
sch - > core_base = 0 ;
sch - > resume_base = 5 ;
sch - > chip . ngpio = 14 ;
2013-03-20 16:16:00 +04:00
break ;
case PCI_DEVICE_ID_INTEL_CENTERTON_ILB :
2014-10-21 15:33:56 +04:00
sch - > core_base = 0 ;
sch - > resume_base = 21 ;
sch - > chip . ngpio = 30 ;
2013-03-20 16:16:00 +04:00
break ;
2014-12-08 12:38:10 +03:00
case PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB :
sch - > core_base = 0 ;
sch - > resume_base = 2 ;
sch - > chip . ngpio = 8 ;
break ;
2013-03-20 16:16:00 +04:00
default :
2014-10-21 15:33:56 +04:00
return - ENODEV ;
2011-03-14 13:53:05 +03:00
}
2010-03-02 12:48:55 +03:00
2014-10-21 15:33:56 +04:00
platform_set_drvdata ( pdev , sch ) ;
2010-03-02 12:48:55 +03:00
2016-02-22 15:13:28 +03:00
return devm_gpiochip_add_data ( & pdev - > dev , & sch - > chip , sch ) ;
2010-03-02 12:48:55 +03:00
}
static struct platform_driver sch_gpio_driver = {
. driver = {
. name = " sch_gpio " ,
} ,
. probe = sch_gpio_probe ,
} ;
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 " ) ;