2018-11-06 15:11:42 +03:00
// SPDX-License-Identifier: GPL-2.0
2011-06-27 11:26:23 +04:00
/*
* GPIO interface for Intel Sodaville SoCs .
*
* Copyright ( c ) 2010 , 2011 Intel Corporation
*
2016-05-10 02:59:55 +03:00
* Author : Hans J . Koch < hjk @ linutronix . de >
2011-06-27 11:26:23 +04:00
*/
# include <linux/errno.h>
2018-09-04 14:26:25 +03:00
# include <linux/gpio/driver.h>
2011-06-27 11:26:23 +04:00
# include <linux/init.h>
2018-09-04 14:26:25 +03:00
# include <linux/interrupt.h>
2011-06-27 11:26:23 +04:00
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/kernel.h>
2018-09-04 14:26:25 +03:00
# include <linux/of_irq.h>
2011-06-27 11:26:23 +04:00
# include <linux/pci.h>
# include <linux/platform_device.h>
# define DRV_NAME "sdv_gpio"
# define SDV_NUM_PUB_GPIOS 12
# define PCI_DEVICE_ID_SDV_GPIO 0x2e67
# define GPIO_BAR 0
# define GPOUTR 0x00
# define GPOER 0x04
# define GPINR 0x08
# define GPSTR 0x0c
# define GPIT1R0 0x10
# define GPIO_INT 0x14
# define GPIT1R1 0x18
# define GPMUXCTL 0x1c
struct sdv_gpio_chip_data {
int irq_base ;
void __iomem * gpio_pub_base ;
2012-03-29 00:55:04 +04:00
struct irq_domain * id ;
2011-06-27 11:26:23 +04:00
struct irq_chip_generic * gc ;
2015-12-04 16:02:58 +03:00
struct gpio_chip chip ;
2011-06-27 11:26:23 +04:00
} ;
static int sdv_gpio_pub_set_type ( struct irq_data * d , unsigned int type )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
struct sdv_gpio_chip_data * sd = gc - > private ;
void __iomem * type_reg ;
u32 reg ;
2012-03-29 00:55:04 +04:00
if ( d - > hwirq < 8 )
2011-06-27 11:26:23 +04:00
type_reg = sd - > gpio_pub_base + GPIT1R0 ;
else
type_reg = sd - > gpio_pub_base + GPIT1R1 ;
reg = readl ( type_reg ) ;
switch ( type ) {
case IRQ_TYPE_LEVEL_HIGH :
2012-03-29 00:55:04 +04:00
reg & = ~ BIT ( 4 * ( d - > hwirq % 8 ) ) ;
2011-06-27 11:26:23 +04:00
break ;
case IRQ_TYPE_LEVEL_LOW :
2012-03-29 00:55:04 +04:00
reg | = BIT ( 4 * ( d - > hwirq % 8 ) ) ;
2011-06-27 11:26:23 +04:00
break ;
default :
return - EINVAL ;
}
writel ( reg , type_reg ) ;
return 0 ;
}
static irqreturn_t sdv_gpio_pub_irq_handler ( int irq , void * data )
{
struct sdv_gpio_chip_data * sd = data ;
2018-11-06 15:38:55 +03:00
unsigned long irq_stat = readl ( sd - > gpio_pub_base + GPSTR ) ;
int irq_bit ;
2011-06-27 11:26:23 +04:00
irq_stat & = readl ( sd - > gpio_pub_base + GPIO_INT ) ;
if ( ! irq_stat )
return IRQ_NONE ;
2018-11-06 15:38:55 +03:00
for_each_set_bit ( irq_bit , & irq_stat , 32 )
2021-05-04 19:42:18 +03:00
generic_handle_domain_irq ( sd - > id , irq_bit ) ;
2011-06-27 11:26:23 +04:00
return IRQ_HANDLED ;
}
static int sdv_xlate ( struct irq_domain * h , struct device_node * node ,
const u32 * intspec , u32 intsize , irq_hw_number_t * out_hwirq ,
u32 * out_type )
{
u32 line , type ;
2015-10-13 14:51:29 +03:00
if ( node ! = irq_domain_get_of_node ( h ) )
2011-06-27 11:26:23 +04:00
return - EINVAL ;
if ( intsize < 2 )
return - EINVAL ;
line = * intspec ;
* out_hwirq = line ;
intspec + + ;
type = * intspec ;
switch ( type ) {
case IRQ_TYPE_LEVEL_LOW :
case IRQ_TYPE_LEVEL_HIGH :
* out_type = type ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2015-04-27 15:54:07 +03:00
static const struct irq_domain_ops irq_domain_sdv_ops = {
2012-03-29 00:55:04 +04:00
. xlate = sdv_xlate ,
2011-06-27 11:26:23 +04:00
} ;
2012-11-19 22:22:34 +04:00
static int sdv_register_irqsupport ( struct sdv_gpio_chip_data * sd ,
2011-06-27 11:26:23 +04:00
struct pci_dev * pdev )
{
struct irq_chip_type * ct ;
int ret ;
2017-03-04 19:23:37 +03:00
sd - > irq_base = devm_irq_alloc_descs ( & pdev - > dev , - 1 , 0 ,
SDV_NUM_PUB_GPIOS , - 1 ) ;
2011-06-27 11:26:23 +04:00
if ( sd - > irq_base < 0 )
return sd - > irq_base ;
/* mask + ACK all interrupt sources */
writel ( 0 , sd - > gpio_pub_base + GPIO_INT ) ;
writel ( ( 1 < < 11 ) - 1 , sd - > gpio_pub_base + GPSTR ) ;
2017-03-04 19:23:37 +03:00
ret = devm_request_irq ( & pdev - > dev , pdev - > irq ,
sdv_gpio_pub_irq_handler , IRQF_SHARED ,
" sdv_gpio " , sd ) ;
2011-06-27 11:26:23 +04:00
if ( ret )
2017-03-04 19:23:37 +03:00
return ret ;
2011-06-27 11:26:23 +04:00
/*
* This gpio irq controller latches level irqs . Testing shows that if
* we unmask & ACK the IRQ before the source of the interrupt is gone
* then the interrupt is active again .
*/
2018-11-07 22:18:04 +03:00
sd - > gc = devm_irq_alloc_generic_chip ( & pdev - > dev , " sdv-gpio " , 1 ,
sd - > irq_base ,
sd - > gpio_pub_base ,
handle_fasteoi_irq ) ;
2017-03-04 19:23:37 +03:00
if ( ! sd - > gc )
return - ENOMEM ;
2011-06-27 11:26:23 +04:00
sd - > gc - > private = sd ;
ct = sd - > gc - > chip_types ;
ct - > type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW ;
ct - > regs . eoi = GPSTR ;
ct - > regs . mask = GPIO_INT ;
ct - > chip . irq_mask = irq_gc_mask_clr_bit ;
ct - > chip . irq_unmask = irq_gc_mask_set_bit ;
ct - > chip . irq_eoi = irq_gc_eoi ;
ct - > chip . irq_set_type = sdv_gpio_pub_set_type ;
irq_setup_generic_chip ( sd - > gc , IRQ_MSK ( SDV_NUM_PUB_GPIOS ) ,
IRQ_GC_INIT_MASK_CACHE , IRQ_NOREQUEST ,
IRQ_LEVEL | IRQ_NOPROBE ) ;
2012-03-29 00:55:04 +04:00
sd - > id = irq_domain_add_legacy ( pdev - > dev . of_node , SDV_NUM_PUB_GPIOS ,
sd - > irq_base , 0 , & irq_domain_sdv_ops , sd ) ;
2017-03-04 19:23:37 +03:00
if ( ! sd - > id )
return - ENODEV ;
2011-06-27 11:26:23 +04:00
return 0 ;
}
2012-11-19 22:22:34 +04:00
static int sdv_gpio_probe ( struct pci_dev * pdev ,
2011-06-27 11:26:23 +04:00
const struct pci_device_id * pci_id )
{
struct sdv_gpio_chip_data * sd ;
int ret ;
u32 mux_val ;
2018-11-07 22:18:04 +03:00
sd = devm_kzalloc ( & pdev - > dev , sizeof ( * sd ) , GFP_KERNEL ) ;
2011-06-27 11:26:23 +04:00
if ( ! sd )
return - ENOMEM ;
2018-11-07 22:18:04 +03:00
ret = pcim_enable_device ( pdev ) ;
2011-06-27 11:26:23 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " can't enable device. \n " ) ;
2018-11-07 22:18:04 +03:00
return ret ;
2011-06-27 11:26:23 +04:00
}
2018-11-07 22:18:04 +03:00
ret = pcim_iomap_regions ( pdev , 1 < < GPIO_BAR , DRV_NAME ) ;
2011-06-27 11:26:23 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " can't alloc PCI BAR #%d \n " , GPIO_BAR ) ;
2018-11-07 22:18:04 +03:00
return ret ;
2011-06-27 11:26:23 +04:00
}
2018-11-07 22:18:04 +03:00
sd - > gpio_pub_base = pcim_iomap_table ( pdev ) [ GPIO_BAR ] ;
2011-06-27 11:26:23 +04:00
2018-11-07 22:18:04 +03:00
ret = of_property_read_u32 ( pdev - > dev . of_node , " intel,muxctl " , & mux_val ) ;
if ( ! ret )
2011-06-27 11:26:23 +04:00
writel ( mux_val , sd - > gpio_pub_base + GPMUXCTL ) ;
2015-12-04 16:02:58 +03:00
ret = bgpio_init ( & sd - > chip , & pdev - > dev , 4 ,
2011-06-27 11:26:23 +04:00
sd - > gpio_pub_base + GPINR , sd - > gpio_pub_base + GPOUTR ,
2012-05-19 17:34:58 +04:00
NULL , sd - > gpio_pub_base + GPOER , NULL , 0 ) ;
2011-06-27 11:26:23 +04:00
if ( ret )
2018-11-07 22:18:04 +03:00
return ret ;
2015-12-04 16:02:58 +03:00
sd - > chip . ngpio = SDV_NUM_PUB_GPIOS ;
2011-06-27 11:26:23 +04:00
2018-11-07 22:18:04 +03:00
ret = devm_gpiochip_add_data ( & pdev - > dev , & sd - > chip , sd ) ;
2011-06-27 11:26:23 +04:00
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " gpiochip_add() failed. \n " ) ;
2018-11-07 22:18:04 +03:00
return ret ;
2011-06-27 11:26:23 +04:00
}
ret = sdv_register_irqsupport ( sd , pdev ) ;
if ( ret )
2018-11-07 22:18:04 +03:00
return ret ;
2011-06-27 11:26:23 +04:00
pci_set_drvdata ( pdev , sd ) ;
dev_info ( & pdev - > dev , " Sodaville GPIO driver registered. \n " ) ;
return 0 ;
}
2013-12-03 03:08:45 +04:00
static const struct pci_device_id sdv_gpio_pci_ids [ ] = {
2011-06-27 11:26:23 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_SDV_GPIO ) } ,
{ 0 , } ,
} ;
static struct pci_driver sdv_gpio_driver = {
2016-05-10 02:59:55 +03:00
. driver = {
. suppress_bind_attrs = true ,
} ,
2011-06-27 11:26:23 +04:00
. name = DRV_NAME ,
. id_table = sdv_gpio_pci_ids ,
. probe = sdv_gpio_probe ,
} ;
2016-05-10 02:59:55 +03:00
builtin_pci_driver ( sdv_gpio_driver ) ;