2013-06-24 08:00:05 +04:00
/*
* Kontron PLD GPIO driver
*
* Copyright ( c ) 2010 - 2013 Kontron Europe GmbH
* Author : Michael Brunner < michael . brunner @ kontron . com >
*
* 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 .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/bitops.h>
# include <linux/errno.h>
# include <linux/platform_device.h>
# include <linux/gpio.h>
# include <linux/mfd/kempld.h>
# define KEMPLD_GPIO_MAX_NUM 16
2014-04-27 04:00:48 +04:00
# define KEMPLD_GPIO_MASK(x) (BIT((x) % 8))
2013-06-24 08:00:05 +04:00
# define KEMPLD_GPIO_DIR_NUM(x) (0x40 + (x) / 8)
# define KEMPLD_GPIO_LVL_NUM(x) (0x42 + (x) / 8)
# define KEMPLD_GPIO_EVT_LVL_EDGE 0x46
# define KEMPLD_GPIO_IEN 0x4A
struct kempld_gpio_data {
struct gpio_chip chip ;
struct kempld_device_data * pld ;
} ;
/*
* Set or clear GPIO bit
* kempld_get_mutex must be called prior to calling this function .
*/
static void kempld_gpio_bitop ( struct kempld_device_data * pld ,
u8 reg , u8 bit , u8 val )
{
u8 status ;
status = kempld_read8 ( pld , reg ) ;
if ( val )
2013-07-31 22:55:39 +04:00
status | = KEMPLD_GPIO_MASK ( bit ) ;
2013-06-24 08:00:05 +04:00
else
2013-07-31 22:55:39 +04:00
status & = ~ KEMPLD_GPIO_MASK ( bit ) ;
2013-06-24 08:00:05 +04:00
kempld_write8 ( pld , reg , status ) ;
}
static int kempld_gpio_get_bit ( struct kempld_device_data * pld , u8 reg , u8 bit )
{
u8 status ;
kempld_get_mutex ( pld ) ;
status = kempld_read8 ( pld , reg ) ;
kempld_release_mutex ( pld ) ;
2013-07-31 22:55:39 +04:00
return ! ! ( status & KEMPLD_GPIO_MASK ( bit ) ) ;
2013-06-24 08:00:05 +04:00
}
static int kempld_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
struct kempld_gpio_data * gpio
= container_of ( chip , struct kempld_gpio_data , chip ) ;
struct kempld_device_data * pld = gpio - > pld ;
2013-07-31 22:55:39 +04:00
return kempld_gpio_get_bit ( pld , KEMPLD_GPIO_LVL_NUM ( offset ) , offset ) ;
2013-06-24 08:00:05 +04:00
}
static void kempld_gpio_set ( struct gpio_chip * chip , unsigned offset , int value )
{
struct kempld_gpio_data * gpio
= container_of ( chip , struct kempld_gpio_data , chip ) ;
struct kempld_device_data * pld = gpio - > pld ;
kempld_get_mutex ( pld ) ;
2013-07-31 22:55:39 +04:00
kempld_gpio_bitop ( pld , KEMPLD_GPIO_LVL_NUM ( offset ) , offset , value ) ;
2013-06-24 08:00:05 +04:00
kempld_release_mutex ( pld ) ;
}
static int kempld_gpio_direction_input ( struct gpio_chip * chip , unsigned offset )
{
struct kempld_gpio_data * gpio
= container_of ( chip , struct kempld_gpio_data , chip ) ;
struct kempld_device_data * pld = gpio - > pld ;
kempld_get_mutex ( pld ) ;
2013-07-31 22:55:39 +04:00
kempld_gpio_bitop ( pld , KEMPLD_GPIO_DIR_NUM ( offset ) , offset , 0 ) ;
2013-06-24 08:00:05 +04:00
kempld_release_mutex ( pld ) ;
return 0 ;
}
static int kempld_gpio_direction_output ( struct gpio_chip * chip , unsigned offset ,
int value )
{
struct kempld_gpio_data * gpio
= container_of ( chip , struct kempld_gpio_data , chip ) ;
struct kempld_device_data * pld = gpio - > pld ;
kempld_get_mutex ( pld ) ;
2013-07-31 22:55:39 +04:00
kempld_gpio_bitop ( pld , KEMPLD_GPIO_LVL_NUM ( offset ) , offset , value ) ;
kempld_gpio_bitop ( pld , KEMPLD_GPIO_DIR_NUM ( offset ) , offset , 1 ) ;
2013-06-24 08:00:05 +04:00
kempld_release_mutex ( pld ) ;
return 0 ;
}
static int kempld_gpio_get_direction ( struct gpio_chip * chip , unsigned offset )
{
struct kempld_gpio_data * gpio
= container_of ( chip , struct kempld_gpio_data , chip ) ;
struct kempld_device_data * pld = gpio - > pld ;
2013-07-31 22:55:39 +04:00
return kempld_gpio_get_bit ( pld , KEMPLD_GPIO_DIR_NUM ( offset ) , offset ) ;
2013-06-24 08:00:05 +04:00
}
static int kempld_gpio_pincount ( struct kempld_device_data * pld )
{
u16 evt , evt_back ;
kempld_get_mutex ( pld ) ;
/* Backup event register as it might be already initialized */
evt_back = kempld_read16 ( pld , KEMPLD_GPIO_EVT_LVL_EDGE ) ;
/* Clear event register */
kempld_write16 ( pld , KEMPLD_GPIO_EVT_LVL_EDGE , 0x0000 ) ;
/* Read back event register */
evt = kempld_read16 ( pld , KEMPLD_GPIO_EVT_LVL_EDGE ) ;
/* Restore event register */
kempld_write16 ( pld , KEMPLD_GPIO_EVT_LVL_EDGE , evt_back ) ;
kempld_release_mutex ( pld ) ;
return evt ? __ffs ( evt ) : 16 ;
}
static int kempld_gpio_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct kempld_device_data * pld = dev_get_drvdata ( dev - > parent ) ;
2013-07-30 12:08:05 +04:00
struct kempld_platform_data * pdata = dev_get_platdata ( pld - > dev ) ;
2013-06-24 08:00:05 +04:00
struct kempld_gpio_data * gpio ;
struct gpio_chip * chip ;
int ret ;
if ( pld - > info . spec_major < 2 ) {
dev_err ( dev ,
" Driver only supports GPIO devices compatible to PLD spec. rev. 2.0 or higher \n " ) ;
return - ENODEV ;
}
gpio = devm_kzalloc ( dev , sizeof ( * gpio ) , GFP_KERNEL ) ;
if ( gpio = = NULL )
return - ENOMEM ;
gpio - > pld = pld ;
platform_set_drvdata ( pdev , gpio ) ;
chip = & gpio - > chip ;
chip - > label = " gpio-kempld " ;
chip - > owner = THIS_MODULE ;
chip - > dev = dev ;
2013-12-04 17:42:46 +04:00
chip - > can_sleep = true ;
2013-06-24 08:00:05 +04:00
if ( pdata & & pdata - > gpio_base )
chip - > base = pdata - > gpio_base ;
else
chip - > base = - 1 ;
chip - > direction_input = kempld_gpio_direction_input ;
chip - > direction_output = kempld_gpio_direction_output ;
chip - > get_direction = kempld_gpio_get_direction ;
chip - > get = kempld_gpio_get ;
chip - > set = kempld_gpio_set ;
chip - > ngpio = kempld_gpio_pincount ( pld ) ;
if ( chip - > ngpio = = 0 ) {
dev_err ( dev , " No GPIO pins detected \n " ) ;
return - ENODEV ;
}
ret = gpiochip_add ( chip ) ;
if ( ret ) {
dev_err ( dev , " Could not register GPIO chip \n " ) ;
return ret ;
}
dev_info ( dev , " GPIO functionality initialized with %d pins \n " ,
chip - > ngpio ) ;
return 0 ;
}
static int kempld_gpio_remove ( struct platform_device * pdev )
{
struct kempld_gpio_data * gpio = platform_get_drvdata ( pdev ) ;
2014-07-13 00:30:12 +04:00
gpiochip_remove ( & gpio - > chip ) ;
return 0 ;
2013-06-24 08:00:05 +04:00
}
static struct platform_driver kempld_gpio_driver = {
. driver = {
2013-08-09 19:33:06 +04:00
. name = " kempld-gpio " ,
2013-06-24 08:00:05 +04:00
. owner = THIS_MODULE ,
} ,
. probe = kempld_gpio_probe ,
. remove = kempld_gpio_remove ,
} ;
module_platform_driver ( kempld_gpio_driver ) ;
MODULE_DESCRIPTION ( " KEM PLD GPIO Driver " ) ;
MODULE_AUTHOR ( " Michael Brunner <michael.brunner@kontron.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2014-04-12 08:45:18 +04:00
MODULE_ALIAS ( " platform:kempld-gpio " ) ;