2009-12-01 20:39:57 -07:00
/*
* Support for viafb GPIO ports .
*
* Copyright 2009 Jonathan Corbet < corbet @ lwn . net >
* Distributable under version 2 of the GNU General Public License .
*/
# include <linux/spinlock.h>
# include <linux/gpio.h>
2010-04-22 17:39:34 -06:00
# include <linux/platform_device.h>
2010-05-05 14:44:55 -06:00
# include <linux/via-core.h>
# include <linux/via-gpio.h>
2011-07-10 13:20:26 -04:00
# include <linux/export.h>
2009-12-01 20:39:57 -07:00
/*
* The ports we know about . Note that the port - 25 gpios are not
* mentioned in the datasheet .
*/
struct viafb_gpio {
char * vg_name ; /* Data sheet name */
u16 vg_io_port ;
u8 vg_port_index ;
int vg_mask_shift ;
} ;
static struct viafb_gpio viafb_all_gpios [ ] = {
{
. vg_name = " VGPIO0 " , /* Guess - not in datasheet */
. vg_io_port = VIASR ,
. vg_port_index = 0x25 ,
. vg_mask_shift = 1
} ,
{
. vg_name = " VGPIO1 " ,
. vg_io_port = VIASR ,
. vg_port_index = 0x25 ,
. vg_mask_shift = 0
} ,
{
. vg_name = " VGPIO2 " , /* aka DISPCLKI0 */
. vg_io_port = VIASR ,
. vg_port_index = 0x2c ,
. vg_mask_shift = 1
} ,
{
. vg_name = " VGPIO3 " , /* aka DISPCLKO0 */
. vg_io_port = VIASR ,
. vg_port_index = 0x2c ,
. vg_mask_shift = 0
} ,
{
. vg_name = " VGPIO4 " , /* DISPCLKI1 */
. vg_io_port = VIASR ,
. vg_port_index = 0x3d ,
. vg_mask_shift = 1
} ,
{
. vg_name = " VGPIO5 " , /* DISPCLKO1 */
. vg_io_port = VIASR ,
. vg_port_index = 0x3d ,
. vg_mask_shift = 0
} ,
} ;
# define VIAFB_NUM_GPIOS ARRAY_SIZE(viafb_all_gpios)
/*
* This structure controls the active GPIOs , which may be a subset
* of those which are known .
*/
struct viafb_gpio_cfg {
struct gpio_chip gpio_chip ;
struct viafb_dev * vdev ;
struct viafb_gpio * active_gpios [ VIAFB_NUM_GPIOS ] ;
2010-06-29 14:09:43 -07:00
const char * gpio_names [ VIAFB_NUM_GPIOS ] ;
2009-12-01 20:39:57 -07:00
} ;
/*
* GPIO access functions
*/
static void via_gpio_set ( struct gpio_chip * chip , unsigned int nr ,
int value )
{
struct viafb_gpio_cfg * cfg = container_of ( chip ,
struct viafb_gpio_cfg ,
gpio_chip ) ;
u8 reg ;
struct viafb_gpio * gpio ;
unsigned long flags ;
spin_lock_irqsave ( & cfg - > vdev - > reg_lock , flags ) ;
gpio = cfg - > active_gpios [ nr ] ;
2010-04-22 14:36:04 -06:00
reg = via_read_reg ( VIASR , gpio - > vg_port_index ) ;
2009-12-01 20:39:57 -07:00
reg | = 0x40 < < gpio - > vg_mask_shift ; /* output enable */
if ( value )
reg | = 0x10 < < gpio - > vg_mask_shift ;
else
reg & = ~ ( 0x10 < < gpio - > vg_mask_shift ) ;
2010-04-22 14:36:04 -06:00
via_write_reg ( VIASR , gpio - > vg_port_index , reg ) ;
2009-12-01 20:39:57 -07:00
spin_unlock_irqrestore ( & cfg - > vdev - > reg_lock , flags ) ;
}
static int via_gpio_dir_out ( struct gpio_chip * chip , unsigned int nr ,
int value )
{
via_gpio_set ( chip , nr , value ) ;
return 0 ;
}
/*
* Set the input direction . I ' m not sure this is right ; we should
* be able to do input without disabling output .
*/
static int via_gpio_dir_input ( struct gpio_chip * chip , unsigned int nr )
{
struct viafb_gpio_cfg * cfg = container_of ( chip ,
struct viafb_gpio_cfg ,
gpio_chip ) ;
struct viafb_gpio * gpio ;
unsigned long flags ;
spin_lock_irqsave ( & cfg - > vdev - > reg_lock , flags ) ;
gpio = cfg - > active_gpios [ nr ] ;
2010-04-22 14:36:04 -06:00
via_write_reg_mask ( VIASR , gpio - > vg_port_index , 0 ,
0x40 < < gpio - > vg_mask_shift ) ;
2009-12-01 20:39:57 -07:00
spin_unlock_irqrestore ( & cfg - > vdev - > reg_lock , flags ) ;
return 0 ;
}
static int via_gpio_get ( struct gpio_chip * chip , unsigned int nr )
{
struct viafb_gpio_cfg * cfg = container_of ( chip ,
struct viafb_gpio_cfg ,
gpio_chip ) ;
u8 reg ;
struct viafb_gpio * gpio ;
unsigned long flags ;
spin_lock_irqsave ( & cfg - > vdev - > reg_lock , flags ) ;
gpio = cfg - > active_gpios [ nr ] ;
2010-04-22 14:36:04 -06:00
reg = via_read_reg ( VIASR , gpio - > vg_port_index ) ;
2009-12-01 20:39:57 -07:00
spin_unlock_irqrestore ( & cfg - > vdev - > reg_lock , flags ) ;
return reg & ( 0x04 < < gpio - > vg_mask_shift ) ;
}
2011-05-20 00:40:20 -06:00
static struct viafb_gpio_cfg viafb_gpio_config = {
2009-12-01 20:39:57 -07:00
. gpio_chip = {
. label = " VIAFB onboard GPIO " ,
. owner = THIS_MODULE ,
. direction_output = via_gpio_dir_out ,
. set = via_gpio_set ,
. direction_input = via_gpio_dir_input ,
. get = via_gpio_get ,
. base = - 1 ,
. ngpio = 0 ,
. can_sleep = 0
}
} ;
/*
* Manage the software enable bit .
*/
static void viafb_gpio_enable ( struct viafb_gpio * gpio )
{
2010-04-22 14:36:04 -06:00
via_write_reg_mask ( VIASR , gpio - > vg_port_index , 0x02 , 0x02 ) ;
2009-12-01 20:39:57 -07:00
}
static void viafb_gpio_disable ( struct viafb_gpio * gpio )
{
2010-04-22 14:36:04 -06:00
via_write_reg_mask ( VIASR , gpio - > vg_port_index , 0 , 0x02 ) ;
2009-12-01 20:39:57 -07:00
}
2010-10-28 18:23:21 +01:00
# ifdef CONFIG_PM
static int viafb_gpio_suspend ( void * private )
{
return 0 ;
}
static int viafb_gpio_resume ( void * private )
{
int i ;
2011-05-20 00:40:20 -06:00
for ( i = 0 ; i < viafb_gpio_config . gpio_chip . ngpio ; i + = 2 )
viafb_gpio_enable ( viafb_gpio_config . active_gpios [ i ] ) ;
2010-10-28 18:23:21 +01:00
return 0 ;
}
static struct viafb_pm_hooks viafb_gpio_pm_hooks = {
. suspend = viafb_gpio_suspend ,
. resume = viafb_gpio_resume
} ;
# endif /* CONFIG_PM */
2010-04-22 17:39:34 -06:00
/*
* Look up a specific gpio and return the number it was assigned .
*/
int viafb_gpio_lookup ( const char * name )
{
int i ;
2009-12-01 20:39:57 -07:00
2011-05-20 00:40:20 -06:00
for ( i = 0 ; i < viafb_gpio_config . gpio_chip . ngpio ; i + + )
if ( ! strcmp ( name , viafb_gpio_config . active_gpios [ i ] - > vg_name ) )
return viafb_gpio_config . gpio_chip . base + i ;
2010-04-22 17:39:34 -06:00
return - 1 ;
}
EXPORT_SYMBOL_GPL ( viafb_gpio_lookup ) ;
2009-12-01 20:39:57 -07:00
2010-04-22 17:39:34 -06:00
/*
* Platform device stuff .
*/
static __devinit int viafb_gpio_probe ( struct platform_device * platdev )
2009-12-01 20:39:57 -07:00
{
2010-04-22 17:39:34 -06:00
struct viafb_dev * vdev = platdev - > dev . platform_data ;
struct via_port_cfg * port_cfg = vdev - > port_cfg ;
2009-12-01 20:39:57 -07:00
int i , ngpio = 0 , ret ;
struct viafb_gpio * gpio ;
unsigned long flags ;
/*
* Set up entries for all GPIOs which have been configured to
* operate as such ( as opposed to as i2c ports ) .
*/
for ( i = 0 ; i < VIAFB_NUM_PORTS ; i + + ) {
if ( port_cfg [ i ] . mode ! = VIA_MODE_GPIO )
continue ;
for ( gpio = viafb_all_gpios ;
gpio < viafb_all_gpios + VIAFB_NUM_GPIOS ; gpio + + )
if ( gpio - > vg_port_index = = port_cfg [ i ] . ioport_index ) {
2011-05-20 00:40:20 -06:00
viafb_gpio_config . active_gpios [ ngpio ] = gpio ;
viafb_gpio_config . gpio_names [ ngpio ] =
gpio - > vg_name ;
2009-12-01 20:39:57 -07:00
ngpio + + ;
}
}
2011-05-20 00:40:20 -06:00
viafb_gpio_config . gpio_chip . ngpio = ngpio ;
viafb_gpio_config . gpio_chip . names = viafb_gpio_config . gpio_names ;
viafb_gpio_config . vdev = vdev ;
2009-12-01 20:39:57 -07:00
if ( ngpio = = 0 ) {
printk ( KERN_INFO " viafb: no GPIOs configured \n " ) ;
return 0 ;
}
/*
* Enable the ports . They come in pairs , with a single
* enable bit for both .
*/
2011-05-20 00:40:20 -06:00
spin_lock_irqsave ( & viafb_gpio_config . vdev - > reg_lock , flags ) ;
2009-12-01 20:39:57 -07:00
for ( i = 0 ; i < ngpio ; i + = 2 )
2011-05-20 00:40:20 -06:00
viafb_gpio_enable ( viafb_gpio_config . active_gpios [ i ] ) ;
spin_unlock_irqrestore ( & viafb_gpio_config . vdev - > reg_lock , flags ) ;
2009-12-01 20:39:57 -07:00
/*
* Get registered .
*/
2011-05-20 00:40:20 -06:00
viafb_gpio_config . gpio_chip . base = - 1 ; /* Dynamic */
ret = gpiochip_add ( & viafb_gpio_config . gpio_chip ) ;
2009-12-01 20:39:57 -07:00
if ( ret ) {
printk ( KERN_ERR " viafb: failed to add gpios (%d) \n " , ret ) ;
2011-05-20 00:40:20 -06:00
viafb_gpio_config . gpio_chip . ngpio = 0 ;
2009-12-01 20:39:57 -07:00
}
2010-10-28 18:23:21 +01:00
# ifdef CONFIG_PM
viafb_pm_register ( & viafb_gpio_pm_hooks ) ;
# endif
2009-12-01 20:39:57 -07:00
return ret ;
}
2010-04-22 17:39:34 -06:00
static int viafb_gpio_remove ( struct platform_device * platdev )
2009-12-01 20:39:57 -07:00
{
unsigned long flags ;
int ret = 0 , i ;
2010-10-28 18:23:21 +01:00
# ifdef CONFIG_PM
viafb_pm_unregister ( & viafb_gpio_pm_hooks ) ;
# endif
2009-12-01 20:39:57 -07:00
/*
* Get unregistered .
*/
2011-05-20 00:40:20 -06:00
if ( viafb_gpio_config . gpio_chip . ngpio > 0 ) {
ret = gpiochip_remove ( & viafb_gpio_config . gpio_chip ) ;
2009-12-01 20:39:57 -07:00
if ( ret ) { /* Somebody still using it? */
printk ( KERN_ERR " Viafb: GPIO remove failed \n " ) ;
2010-04-30 09:45:51 -06:00
return ret ;
2009-12-01 20:39:57 -07:00
}
}
/*
* Disable the ports .
*/
2011-05-20 00:40:20 -06:00
spin_lock_irqsave ( & viafb_gpio_config . vdev - > reg_lock , flags ) ;
for ( i = 0 ; i < viafb_gpio_config . gpio_chip . ngpio ; i + = 2 )
viafb_gpio_disable ( viafb_gpio_config . active_gpios [ i ] ) ;
viafb_gpio_config . gpio_chip . ngpio = 0 ;
spin_unlock_irqrestore ( & viafb_gpio_config . vdev - > reg_lock , flags ) ;
2009-12-01 20:39:57 -07:00
return ret ;
}
2010-04-22 17:39:34 -06:00
static struct platform_driver via_gpio_driver = {
. driver = {
. name = " viafb-gpio " ,
} ,
. probe = viafb_gpio_probe ,
. remove = viafb_gpio_remove ,
} ;
int viafb_gpio_init ( void )
2009-12-01 20:39:57 -07:00
{
2010-04-22 17:39:34 -06:00
return platform_driver_register ( & via_gpio_driver ) ;
}
2009-12-01 20:39:57 -07:00
2010-04-22 17:39:34 -06:00
void viafb_gpio_exit ( void )
{
platform_driver_unregister ( & via_gpio_driver ) ;
2009-12-01 20:39:57 -07:00
}