2018-11-06 15:11:42 +03:00
// SPDX-License-Identifier: GPL-2.0
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 >
*/
2018-09-04 14:26:25 +03:00
# include <linux/acpi.h>
2021-03-17 18:19:28 +03:00
# include <linux/bitops.h>
2018-09-04 14:26:25 +03:00
# include <linux/errno.h>
# include <linux/gpio/driver.h>
# include <linux/io.h>
2021-03-17 18:19:27 +03:00
# include <linux/irq.h>
2010-03-02 12:48:55 +03:00
# include <linux/kernel.h>
# include <linux/module.h>
2011-03-14 13:53:05 +03:00
# include <linux/pci_ids.h>
2018-09-04 14:26:25 +03:00
# include <linux/platform_device.h>
2021-03-17 18:19:27 +03:00
# include <linux/types.h>
2010-03-02 12:48:55 +03:00
2014-10-21 15:33:56 +04:00
# define GEN 0x00
# define GIO 0x04
# define GLV 0x08
2021-03-17 18:19:27 +03:00
# define GTPE 0x0c
# define GTNE 0x10
# define GGPE 0x14
# define GSMI 0x18
# define GTS 0x1c
# define CORE_BANK_OFFSET 0x00
# define RESUME_BANK_OFFSET 0x20
2014-10-21 15:33:56 +04:00
2021-03-17 18:19:28 +03:00
/*
* iLB datasheet describes GPE0BLK registers , in particular GPE0E . GPIO bit .
* Document Number : 328195 - 001
*/
# define GPE0E_GPIO 14
2014-10-21 15:33:56 +04:00
struct sch_gpio {
struct gpio_chip chip ;
2021-03-17 18:19:27 +03:00
struct irq_chip irqchip ;
2014-10-21 15:33:56 +04:00
spinlock_t lock ;
unsigned short iobase ;
unsigned short resume_base ;
2021-03-17 18:19:28 +03:00
/* GPE handling */
u32 gpe ;
acpi_gpe_handler gpe_handler ;
2014-10-21 15:33:56 +04:00
} ;
2010-03-02 12:48:55 +03:00
2020-07-21 17:51:03 +03:00
static unsigned int sch_gpio_offset ( struct sch_gpio * sch , unsigned int gpio ,
unsigned int reg )
2010-03-02 12:48:55 +03:00
{
2021-03-17 18:19:27 +03:00
unsigned int base = CORE_BANK_OFFSET ;
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 ;
2021-03-17 18:19:27 +03:00
base = RESUME_BANK_OFFSET ;
2014-10-21 15:33:56 +04:00
}
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
}
2020-07-21 17:51:03 +03:00
static unsigned int sch_gpio_bit ( struct sch_gpio * sch , unsigned int 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
}
2020-07-21 17:51:03 +03:00
static int sch_gpio_reg_get ( struct sch_gpio * sch , unsigned int gpio , unsigned int 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
}
2020-07-21 17:51:03 +03:00
static void sch_gpio_reg_set ( struct sch_gpio * sch , unsigned int gpio , unsigned int 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
2020-07-21 17:51:03 +03:00
static int sch_gpio_direction_in ( struct gpio_chip * gc , unsigned int gpio_num )
2015-01-21 13:32:21 +03:00
{
2015-12-07 16:21:49 +03:00
struct sch_gpio * sch = gpiochip_get_data ( gc ) ;
2021-03-17 18:19:27 +03:00
unsigned long flags ;
2010-03-02 12:48:55 +03:00
2021-03-17 18:19:27 +03:00
spin_lock_irqsave ( & sch - > lock , flags ) ;
2016-06-18 21:05:04 +03:00
sch_gpio_reg_set ( sch , gpio_num , GIO , 1 ) ;
2021-03-17 18:19:27 +03:00
spin_unlock_irqrestore ( & sch - > lock , flags ) ;
2010-03-02 12:48:55 +03:00
return 0 ;
}
2020-07-21 17:51:03 +03:00
static int sch_gpio_get ( struct gpio_chip * gc , unsigned int 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 ) ;
2020-07-21 17:51:04 +03:00
2016-06-18 21:05:04 +03:00
return sch_gpio_reg_get ( sch , gpio_num , GLV ) ;
2010-03-02 12:48:55 +03:00
}
2020-07-21 17:51:03 +03:00
static void sch_gpio_set ( struct gpio_chip * gc , unsigned int 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 ) ;
2021-03-17 18:19:27 +03:00
unsigned long flags ;
2010-03-02 12:48:55 +03:00
2021-03-17 18:19:27 +03:00
spin_lock_irqsave ( & sch - > lock , flags ) ;
2016-06-18 21:05:04 +03:00
sch_gpio_reg_set ( sch , gpio_num , GLV , val ) ;
2021-03-17 18:19:27 +03:00
spin_unlock_irqrestore ( & sch - > lock , flags ) ;
2010-03-02 12:48:55 +03:00
}
2020-07-21 17:51:03 +03:00
static int sch_gpio_direction_out ( struct gpio_chip * gc , unsigned int gpio_num ,
2014-10-21 15:33:56 +04:00
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 ) ;
2021-03-17 18:19:27 +03:00
unsigned long flags ;
2010-03-02 12:48:55 +03:00
2021-03-17 18:19:27 +03:00
spin_lock_irqsave ( & sch - > lock , flags ) ;
2016-06-18 21:05:04 +03:00
sch_gpio_reg_set ( sch , gpio_num , GIO , 0 ) ;
2021-03-17 18:19:27 +03:00
spin_unlock_irqrestore ( & sch - > lock , flags ) ;
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 ;
}
2020-07-21 17:51:03 +03:00
static int sch_gpio_get_direction ( struct gpio_chip * gc , unsigned int gpio_num )
2018-06-27 11:39:31 +03:00
{
struct sch_gpio * sch = gpiochip_get_data ( gc ) ;
2019-11-06 11:54:12 +03:00
if ( sch_gpio_reg_get ( sch , gpio_num , GIO ) )
return GPIO_LINE_DIRECTION_IN ;
return GPIO_LINE_DIRECTION_OUT ;
2018-06-27 11:39:31 +03:00
}
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 ,
2018-06-27 11:39:31 +03:00
. get_direction = sch_gpio_get_direction ,
2010-03-02 12:48:55 +03:00
} ;
2021-03-17 18:19:27 +03:00
static int sch_irq_type ( struct irq_data * d , unsigned int type )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct sch_gpio * sch = gpiochip_get_data ( gc ) ;
irq_hw_number_t gpio_num = irqd_to_hwirq ( d ) ;
unsigned long flags ;
int rising , falling ;
switch ( type & IRQ_TYPE_SENSE_MASK ) {
case IRQ_TYPE_EDGE_RISING :
rising = 1 ;
falling = 0 ;
break ;
case IRQ_TYPE_EDGE_FALLING :
rising = 0 ;
falling = 1 ;
break ;
case IRQ_TYPE_EDGE_BOTH :
rising = 1 ;
falling = 1 ;
break ;
default :
return - EINVAL ;
}
spin_lock_irqsave ( & sch - > lock , flags ) ;
sch_gpio_reg_set ( sch , gpio_num , GTPE , rising ) ;
sch_gpio_reg_set ( sch , gpio_num , GTNE , falling ) ;
irq_set_handler_locked ( d , handle_edge_irq ) ;
spin_unlock_irqrestore ( & sch - > lock , flags ) ;
return 0 ;
}
static void sch_irq_ack ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct sch_gpio * sch = gpiochip_get_data ( gc ) ;
irq_hw_number_t gpio_num = irqd_to_hwirq ( d ) ;
unsigned long flags ;
spin_lock_irqsave ( & sch - > lock , flags ) ;
sch_gpio_reg_set ( sch , gpio_num , GTS , 1 ) ;
spin_unlock_irqrestore ( & sch - > lock , flags ) ;
}
static void sch_irq_mask_unmask ( struct irq_data * d , int val )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct sch_gpio * sch = gpiochip_get_data ( gc ) ;
irq_hw_number_t gpio_num = irqd_to_hwirq ( d ) ;
unsigned long flags ;
spin_lock_irqsave ( & sch - > lock , flags ) ;
sch_gpio_reg_set ( sch , gpio_num , GGPE , val ) ;
spin_unlock_irqrestore ( & sch - > lock , flags ) ;
}
static void sch_irq_mask ( struct irq_data * d )
{
sch_irq_mask_unmask ( d , 0 ) ;
}
static void sch_irq_unmask ( struct irq_data * d )
{
sch_irq_mask_unmask ( d , 1 ) ;
}
2021-03-17 18:19:28 +03:00
static u32 sch_gpio_gpe_handler ( acpi_handle gpe_device , u32 gpe , void * context )
{
struct sch_gpio * sch = context ;
struct gpio_chip * gc = & sch - > chip ;
unsigned long core_status , resume_status ;
unsigned long pending ;
unsigned long flags ;
int offset ;
u32 ret ;
spin_lock_irqsave ( & sch - > lock , flags ) ;
core_status = inl ( sch - > iobase + CORE_BANK_OFFSET + GTS ) ;
resume_status = inl ( sch - > iobase + RESUME_BANK_OFFSET + GTS ) ;
spin_unlock_irqrestore ( & sch - > lock , flags ) ;
pending = ( resume_status < < sch - > resume_base ) | core_status ;
for_each_set_bit ( offset , & pending , sch - > chip . ngpio )
generic_handle_irq ( irq_find_mapping ( gc - > irq . domain , offset ) ) ;
/* Set returning value depending on whether we handled an interrupt */
ret = pending ? ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED ;
/* Acknowledge GPE to ACPICA */
ret | = ACPI_REENABLE_GPE ;
return ret ;
}
static void sch_gpio_remove_gpe_handler ( void * data )
{
struct sch_gpio * sch = data ;
acpi_disable_gpe ( NULL , sch - > gpe ) ;
acpi_remove_gpe_handler ( NULL , sch - > gpe , sch - > gpe_handler ) ;
}
static int sch_gpio_install_gpe_handler ( struct sch_gpio * sch )
{
struct device * dev = sch - > chip . parent ;
acpi_status status ;
status = acpi_install_gpe_handler ( NULL , sch - > gpe , ACPI_GPE_LEVEL_TRIGGERED ,
sch - > gpe_handler , sch ) ;
if ( ACPI_FAILURE ( status ) ) {
dev_err ( dev , " Failed to install GPE handler for %u: %s \n " ,
sch - > gpe , acpi_format_exception ( status ) ) ;
return - ENODEV ;
}
status = acpi_enable_gpe ( NULL , sch - > gpe ) ;
if ( ACPI_FAILURE ( status ) ) {
dev_err ( dev , " Failed to enable GPE handler for %u: %s \n " ,
sch - > gpe , acpi_format_exception ( status ) ) ;
acpi_remove_gpe_handler ( NULL , sch - > gpe , sch - > gpe_handler ) ;
return - ENODEV ;
}
return devm_add_action_or_reset ( dev , sch_gpio_remove_gpe_handler , sch ) ;
}
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
{
2021-03-17 18:19:27 +03:00
struct gpio_irq_chip * girq ;
2014-10-21 15:33:56 +04:00
struct sch_gpio * sch ;
2010-03-02 12:48:55 +03:00
struct resource * res ;
2021-03-17 18:19:28 +03:00
int ret ;
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 - > 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 - > 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 - > 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 - > 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
2021-03-17 18:19:27 +03:00
sch - > irqchip . name = " sch_gpio " ;
sch - > irqchip . irq_ack = sch_irq_ack ;
sch - > irqchip . irq_mask = sch_irq_mask ;
sch - > irqchip . irq_unmask = sch_irq_unmask ;
sch - > irqchip . irq_set_type = sch_irq_type ;
girq = & sch - > chip . irq ;
girq - > chip = & sch - > irqchip ;
girq - > num_parents = 0 ;
girq - > parents = NULL ;
girq - > parent_handler = NULL ;
girq - > default_type = IRQ_TYPE_NONE ;
girq - > handler = handle_bad_irq ;
2021-03-17 18:19:28 +03:00
/* GPE setup is optional */
sch - > gpe = GPE0E_GPIO ;
sch - > gpe_handler = sch_gpio_gpe_handler ;
ret = sch_gpio_install_gpe_handler ( sch ) ;
if ( ret )
dev_warn ( & pdev - > dev , " Can't setup GPE, no IRQ support \n " ) ;
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 " ) ;
2018-11-06 15:11:42 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;
2010-03-02 12:48:55 +03:00
MODULE_ALIAS ( " platform:sch_gpio " ) ;