2009-12-15 05:00:32 +03:00
/*
* AMD CS5535 / CS5536 GPIO driver
* Copyright ( C ) 2006 Advanced Micro Devices , Inc .
* Copyright ( C ) 2007 - 2009 Andres Salomon < dilinger @ collabora . co . uk >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/spinlock.h>
# include <linux/module.h>
2010-10-23 11:41:09 +04:00
# include <linux/platform_device.h>
2018-01-14 03:56:24 +03:00
# include <linux/gpio/driver.h>
2009-12-15 05:00:32 +03:00
# include <linux/io.h>
# include <linux/cs5535.h>
2011-01-13 04:00:10 +03:00
# include <asm/msr.h>
2009-12-15 05:00:32 +03:00
# define DRV_NAME "cs5535-gpio"
2009-12-15 05:00:35 +03:00
/*
* Some GPIO pins
* 31 - 29 , 23 : reserved ( always mask out )
* 28 : Power Button
* 26 : PME #
* 22 - 16 : LPC
* 14 , 15 : SMBus
* 9 , 8 : UART1
* 7 : PCI INTB
* 3 , 4 : UART2 / DDC
* 2 : IDE_IRQ0
* 1 : AC_BEEP
* 0 : PCI INTA
*
* If a mask was not specified , allow all except
* reserved and Power Button
*/
# define GPIO_DEFAULT_MASK 0x0F7FFFFF
static ulong mask = GPIO_DEFAULT_MASK ;
module_param_named ( mask , mask , ulong , 0444 ) ;
MODULE_PARM_DESC ( mask , " GPIO channel mask. " ) ;
2015-12-04 17:53:04 +03:00
/*
* FIXME : convert this singleton driver to use the state container
* design pattern , see Documentation / driver - model / design - patterns . txt
*/
2009-12-15 05:00:32 +03:00
static struct cs5535_gpio_chip {
struct gpio_chip chip ;
resource_size_t base ;
2010-10-23 11:41:09 +04:00
struct platform_device * pdev ;
2009-12-15 05:00:32 +03:00
spinlock_t lock ;
} cs5535_gpio_chip ;
/*
* The CS5535 / CS5536 GPIOs support a number of extra features not defined
* by the gpio_chip API , so these are exported . For a full list of the
* registers , see include / linux / cs5535 . h .
*/
2010-12-22 00:04:42 +03:00
static void errata_outl ( struct cs5535_gpio_chip * chip , u32 val ,
unsigned int reg )
2010-12-03 01:31:17 +03:00
{
2010-12-22 00:04:42 +03:00
unsigned long addr = chip - > base + 0x80 + reg ;
2010-12-03 01:31:17 +03:00
/*
* According to the CS5536 errata ( # 36 ) , after suspend
* a write to the high bank GPIO register will clear all
* non - selected bits ; the recommended workaround is a
* read - modify - write operation .
2010-12-22 00:04:42 +03:00
*
* Don ' t apply this errata to the edge status GPIOs , as writing
* to their lower bits will clear them .
2010-12-03 01:31:17 +03:00
*/
2010-12-22 00:04:52 +03:00
if ( reg ! = GPIO_POSITIVE_EDGE_STS & & reg ! = GPIO_NEGATIVE_EDGE_STS ) {
if ( val & 0xffff )
val | = ( inl ( addr ) & 0xffff ) ; /* ignore the high bits */
else
val | = ( inl ( addr ) ^ ( val > > 16 ) ) ;
}
2010-12-03 01:31:17 +03:00
outl ( val , addr ) ;
}
2009-12-15 05:00:32 +03:00
static void __cs5535_gpio_set ( struct cs5535_gpio_chip * chip , unsigned offset ,
unsigned int reg )
{
if ( offset < 16 )
/* low bank register */
outl ( 1 < < offset , chip - > base + reg ) ;
else
/* high bank register */
2010-12-22 00:04:42 +03:00
errata_outl ( chip , 1 < < ( offset - 16 ) , reg ) ;
2009-12-15 05:00:32 +03:00
}
void cs5535_gpio_set ( unsigned offset , unsigned int reg )
{
struct cs5535_gpio_chip * chip = & cs5535_gpio_chip ;
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
__cs5535_gpio_set ( chip , offset , reg ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
}
EXPORT_SYMBOL_GPL ( cs5535_gpio_set ) ;
static void __cs5535_gpio_clear ( struct cs5535_gpio_chip * chip , unsigned offset ,
unsigned int reg )
{
if ( offset < 16 )
/* low bank register */
outl ( 1 < < ( offset + 16 ) , chip - > base + reg ) ;
else
/* high bank register */
2010-12-22 00:04:42 +03:00
errata_outl ( chip , 1 < < offset , reg ) ;
2009-12-15 05:00:32 +03:00
}
void cs5535_gpio_clear ( unsigned offset , unsigned int reg )
{
struct cs5535_gpio_chip * chip = & cs5535_gpio_chip ;
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
__cs5535_gpio_clear ( chip , offset , reg ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
}
EXPORT_SYMBOL_GPL ( cs5535_gpio_clear ) ;
int cs5535_gpio_isset ( unsigned offset , unsigned int reg )
{
struct cs5535_gpio_chip * chip = & cs5535_gpio_chip ;
unsigned long flags ;
long val ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
if ( offset < 16 )
/* low bank register */
val = inl ( chip - > base + reg ) ;
else {
/* high bank register */
val = inl ( chip - > base + 0x80 + reg ) ;
offset - = 16 ;
}
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
return ( val & ( 1 < < offset ) ) ? 1 : 0 ;
}
EXPORT_SYMBOL_GPL ( cs5535_gpio_isset ) ;
2011-01-13 04:00:10 +03:00
int cs5535_gpio_set_irq ( unsigned group , unsigned irq )
{
uint32_t lo , hi ;
if ( group > 7 | | irq > 15 )
return - EINVAL ;
rdmsr ( MSR_PIC_ZSEL_HIGH , lo , hi ) ;
lo & = ~ ( 0xF < < ( group * 4 ) ) ;
lo | = ( irq & 0xF ) < < ( group * 4 ) ;
wrmsr ( MSR_PIC_ZSEL_HIGH , lo , hi ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( cs5535_gpio_set_irq ) ;
void cs5535_gpio_setup_event ( unsigned offset , int pair , int pme )
{
struct cs5535_gpio_chip * chip = & cs5535_gpio_chip ;
uint32_t shift = ( offset % 8 ) * 4 ;
unsigned long flags ;
uint32_t val ;
if ( offset > = 24 )
offset = GPIO_MAP_W ;
else if ( offset > = 16 )
offset = GPIO_MAP_Z ;
else if ( offset > = 8 )
offset = GPIO_MAP_Y ;
else
offset = GPIO_MAP_X ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
val = inl ( chip - > base + offset ) ;
/* Clear whatever was there before */
val & = ~ ( 0xF < < shift ) ;
/* Set the new value */
val | = ( ( pair & 7 ) < < shift ) ;
/* Set the PME bit if this is a PME event */
if ( pme )
val | = ( 1 < < ( shift + 3 ) ) ;
outl ( val , chip - > base + offset ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
}
EXPORT_SYMBOL_GPL ( cs5535_gpio_setup_event ) ;
2009-12-15 05:00:32 +03:00
/*
* Generic gpio_chip API support .
*/
2009-12-15 05:00:35 +03:00
static int chip_gpio_request ( struct gpio_chip * c , unsigned offset )
{
2015-12-04 17:53:04 +03:00
struct cs5535_gpio_chip * chip = gpiochip_get_data ( c ) ;
2009-12-15 05:00:35 +03:00
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
/* check if this pin is available */
if ( ( mask & ( 1 < < offset ) ) = = 0 ) {
dev_info ( & chip - > pdev - > dev ,
" pin %u is not available (check mask) \n " , offset ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
return - EINVAL ;
}
/* disable output aux 1 & 2 on this pin */
__cs5535_gpio_clear ( chip , offset , GPIO_OUTPUT_AUX1 ) ;
__cs5535_gpio_clear ( chip , offset , GPIO_OUTPUT_AUX2 ) ;
/* disable input aux 1 on this pin */
__cs5535_gpio_clear ( chip , offset , GPIO_INPUT_AUX1 ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
return 0 ;
}
2009-12-15 05:00:32 +03:00
static int chip_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
2010-03-06 00:44:38 +03:00
return cs5535_gpio_isset ( offset , GPIO_READ_BACK ) ;
2009-12-15 05:00:32 +03:00
}
static void chip_gpio_set ( struct gpio_chip * chip , unsigned offset , int val )
{
if ( val )
cs5535_gpio_set ( offset , GPIO_OUTPUT_VAL ) ;
else
cs5535_gpio_clear ( offset , GPIO_OUTPUT_VAL ) ;
}
static int chip_direction_input ( struct gpio_chip * c , unsigned offset )
{
2015-12-04 17:53:04 +03:00
struct cs5535_gpio_chip * chip = gpiochip_get_data ( c ) ;
2009-12-15 05:00:32 +03:00
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
__cs5535_gpio_set ( chip , offset , GPIO_INPUT_ENABLE ) ;
2010-03-06 00:44:38 +03:00
__cs5535_gpio_clear ( chip , offset , GPIO_OUTPUT_ENABLE ) ;
2009-12-15 05:00:32 +03:00
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
return 0 ;
}
static int chip_direction_output ( struct gpio_chip * c , unsigned offset , int val )
{
2015-12-04 17:53:04 +03:00
struct cs5535_gpio_chip * chip = gpiochip_get_data ( c ) ;
2009-12-15 05:00:32 +03:00
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
2010-03-06 00:44:38 +03:00
__cs5535_gpio_set ( chip , offset , GPIO_INPUT_ENABLE ) ;
2009-12-15 05:00:32 +03:00
__cs5535_gpio_set ( chip , offset , GPIO_OUTPUT_ENABLE ) ;
if ( val )
__cs5535_gpio_set ( chip , offset , GPIO_OUTPUT_VAL ) ;
else
__cs5535_gpio_clear ( chip , offset , GPIO_OUTPUT_VAL ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
return 0 ;
}
2010-05-27 01:42:17 +04:00
static const char * const cs5535_gpio_names [ ] = {
2009-12-15 05:00:35 +03:00
" GPIO0 " , " GPIO1 " , " GPIO2 " , " GPIO3 " ,
" GPIO4 " , " GPIO5 " , " GPIO6 " , " GPIO7 " ,
" GPIO8 " , " GPIO9 " , " GPIO10 " , " GPIO11 " ,
" GPIO12 " , " GPIO13 " , " GPIO14 " , " GPIO15 " ,
" GPIO16 " , " GPIO17 " , " GPIO18 " , " GPIO19 " ,
" GPIO20 " , " GPIO21 " , " GPIO22 " , NULL ,
" GPIO24 " , " GPIO25 " , " GPIO26 " , " GPIO27 " ,
" GPIO28 " , NULL , NULL , NULL ,
} ;
2009-12-15 05:00:32 +03:00
static struct cs5535_gpio_chip cs5535_gpio_chip = {
. chip = {
. owner = THIS_MODULE ,
. label = DRV_NAME ,
. base = 0 ,
2009-12-15 05:00:35 +03:00
. ngpio = 32 ,
. names = cs5535_gpio_names ,
. request = chip_gpio_request ,
2009-12-15 05:00:32 +03:00
. get = chip_gpio_get ,
. set = chip_gpio_set ,
. direction_input = chip_direction_input ,
. direction_output = chip_direction_output ,
} ,
} ;
2012-11-19 22:22:34 +04:00
static int cs5535_gpio_probe ( struct platform_device * pdev )
2009-12-15 05:00:32 +03:00
{
2010-10-23 11:41:09 +04:00
struct resource * res ;
int err = - EIO ;
2009-12-15 05:00:35 +03:00
ulong mask_orig = mask ;
2009-12-15 05:00:32 +03:00
/* There are two ways to get the GPIO base address; one is by
* fetching it from MSR_LBAR_GPIO , the other is by reading the
* PCI BAR info . The latter method is easier ( especially across
* different architectures ) , so we ' ll stick with that for now . If
* it turns out to be unreliable in the face of crappy BIOSes , we
* can always go back to using MSRs . . */
2010-10-23 11:41:09 +04:00
res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " can't fetch device resource info \n " ) ;
2016-02-22 15:13:28 +03:00
return err ;
2009-12-15 05:00:32 +03:00
}
2014-10-01 14:46:33 +04:00
if ( ! devm_request_region ( & pdev - > dev , res - > start , resource_size ( res ) ,
pdev - > name ) ) {
2010-10-23 11:41:09 +04:00
dev_err ( & pdev - > dev , " can't request region \n " ) ;
2016-02-22 15:13:28 +03:00
return err ;
2009-12-15 05:00:32 +03:00
}
/* set up the driver-specific struct */
2010-10-23 11:41:09 +04:00
cs5535_gpio_chip . base = res - > start ;
2009-12-15 05:00:32 +03:00
cs5535_gpio_chip . pdev = pdev ;
spin_lock_init ( & cs5535_gpio_chip . lock ) ;
2010-12-20 13:28:40 +03:00
dev_info ( & pdev - > dev , " reserved resource region %pR \n " , res ) ;
2009-12-15 05:00:32 +03:00
2009-12-15 05:00:35 +03:00
/* mask out reserved pins */
mask & = 0x1F7FFFFF ;
/* do not allow pin 28, Power Button, as there's special handling
* in the PMC needed . ( note 12 , p . 48 ) */
mask & = ~ ( 1 < < 28 ) ;
if ( mask_orig ! = mask )
dev_info ( & pdev - > dev , " mask changed from 0x%08lX to 0x%08lX \n " ,
mask_orig , mask ) ;
2009-12-15 05:00:32 +03:00
/* finally, register with the generic GPIO API */
2016-02-22 15:13:28 +03:00
err = devm_gpiochip_add_data ( & pdev - > dev , & cs5535_gpio_chip . chip ,
& cs5535_gpio_chip ) ;
2009-12-15 05:00:35 +03:00
if ( err )
2016-02-22 15:13:28 +03:00
return err ;
2009-12-15 05:00:32 +03:00
2010-10-23 11:41:09 +04:00
return 0 ;
2009-12-15 05:00:32 +03:00
}
2011-03-15 08:29:23 +03:00
static struct platform_driver cs5535_gpio_driver = {
2010-10-23 11:41:09 +04:00
. driver = {
. name = DRV_NAME ,
} ,
. probe = cs5535_gpio_probe ,
} ;
2009-12-15 05:00:32 +03:00
2011-12-07 20:24:00 +04:00
module_platform_driver ( cs5535_gpio_driver ) ;
2009-12-15 05:00:32 +03:00
2010-07-21 00:24:32 +04:00
MODULE_AUTHOR ( " Andres Salomon <dilinger@queued.net> " ) ;
2009-12-15 05:00:32 +03:00
MODULE_DESCRIPTION ( " AMD CS5535/CS5536 GPIO driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2010-12-02 06:55:10 +03:00
MODULE_ALIAS ( " platform: " DRV_NAME ) ;