2010-10-28 02:33:18 +04:00
/*
* 74 Hx164 - Generic serial - in / parallel - out 8 - bits shift register GPIO driver
*
* Copyright ( C ) 2010 Gabor Juhos < juhosg @ openwrt . org >
* Copyright ( C ) 2010 Miguel Gaio < miguel . gaio @ efixo . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/init.h>
# include <linux/mutex.h>
# include <linux/spi/spi.h>
# include <linux/gpio.h>
2012-09-11 00:35:39 +04:00
# include <linux/of_gpio.h>
2010-10-28 02:33:18 +04:00
# include <linux/slab.h>
2011-07-03 21:38:09 +04:00
# include <linux/module.h>
2010-10-28 02:33:18 +04:00
2012-09-11 00:35:39 +04:00
# define GEN_74X164_NUMBER_GPIOS 8
2010-10-28 02:33:18 +04:00
struct gen_74x164_chip {
struct spi_device * spi ;
2012-09-11 00:35:39 +04:00
u8 * buffer ;
2010-10-28 02:33:18 +04:00
struct gpio_chip gpio_chip ;
struct mutex lock ;
2012-09-11 00:35:39 +04:00
u32 registers ;
2010-10-28 02:33:18 +04:00
} ;
2011-06-13 12:47:15 +04:00
static struct gen_74x164_chip * gpio_to_74x164_chip ( struct gpio_chip * gc )
2010-10-28 02:33:18 +04:00
{
return container_of ( gc , struct gen_74x164_chip , gpio_chip ) ;
}
static int __gen_74x164_write_config ( struct gen_74x164_chip * chip )
{
2012-09-11 00:35:39 +04:00
struct spi_message message ;
struct spi_transfer * msg_buf ;
int i , ret = 0 ;
msg_buf = kzalloc ( chip - > registers * sizeof ( struct spi_transfer ) ,
GFP_KERNEL ) ;
if ( ! msg_buf )
return - ENOMEM ;
spi_message_init ( & message ) ;
/*
* Since the registers are chained , every byte sent will make
* the previous byte shift to the next register in the
* chain . Thus , the first byte send will end up in the last
* register at the end of the transfer . So , to have a logical
* numbering , send the bytes in reverse order so that the last
* byte of the buffer will end up in the last register .
*/
for ( i = chip - > registers - 1 ; i > = 0 ; i - - ) {
msg_buf [ i ] . tx_buf = chip - > buffer + i ;
msg_buf [ i ] . len = sizeof ( u8 ) ;
spi_message_add_tail ( msg_buf + i , & message ) ;
}
ret = spi_sync ( chip - > spi , & message ) ;
kfree ( msg_buf ) ;
return ret ;
2010-10-28 02:33:18 +04:00
}
static int gen_74x164_get_value ( struct gpio_chip * gc , unsigned offset )
{
2011-06-13 12:47:15 +04:00
struct gen_74x164_chip * chip = gpio_to_74x164_chip ( gc ) ;
2012-09-11 00:35:39 +04:00
u8 bank = offset / 8 ;
u8 pin = offset % 8 ;
2010-10-28 02:33:18 +04:00
int ret ;
mutex_lock ( & chip - > lock ) ;
2012-09-11 00:35:39 +04:00
ret = ( chip - > buffer [ bank ] > > pin ) & 0x1 ;
2010-10-28 02:33:18 +04:00
mutex_unlock ( & chip - > lock ) ;
return ret ;
}
static void gen_74x164_set_value ( struct gpio_chip * gc ,
unsigned offset , int val )
{
2011-06-13 12:47:15 +04:00
struct gen_74x164_chip * chip = gpio_to_74x164_chip ( gc ) ;
2012-09-11 00:35:39 +04:00
u8 bank = offset / 8 ;
u8 pin = offset % 8 ;
2010-10-28 02:33:18 +04:00
mutex_lock ( & chip - > lock ) ;
if ( val )
2012-09-11 00:35:39 +04:00
chip - > buffer [ bank ] | = ( 1 < < pin ) ;
2010-10-28 02:33:18 +04:00
else
2012-09-11 00:35:39 +04:00
chip - > buffer [ bank ] & = ~ ( 1 < < pin ) ;
2010-10-28 02:33:18 +04:00
__gen_74x164_write_config ( chip ) ;
mutex_unlock ( & chip - > lock ) ;
}
2011-05-28 03:35:59 +04:00
static int gen_74x164_direction_output ( struct gpio_chip * gc ,
unsigned offset , int val )
{
gen_74x164_set_value ( gc , offset , val ) ;
return 0 ;
}
2012-11-19 22:22:34 +04:00
static int gen_74x164_probe ( struct spi_device * spi )
2010-10-28 02:33:18 +04:00
{
struct gen_74x164_chip * chip ;
int ret ;
/*
* bits_per_word cannot be configured in platform data
*/
spi - > bits_per_word = 8 ;
ret = spi_setup ( spi ) ;
if ( ret < 0 )
return ret ;
2012-09-05 12:40:51 +04:00
chip = devm_kzalloc ( & spi - > dev , sizeof ( * chip ) , GFP_KERNEL ) ;
2010-10-28 02:33:18 +04:00
if ( ! chip )
return - ENOMEM ;
mutex_init ( & chip - > lock ) ;
2013-03-15 13:17:18 +04:00
spi_set_drvdata ( spi , chip ) ;
2010-10-28 02:33:18 +04:00
chip - > spi = spi ;
2011-05-28 03:35:59 +04:00
chip - > gpio_chip . label = spi - > modalias ;
chip - > gpio_chip . direction_output = gen_74x164_direction_output ;
2010-10-28 02:33:18 +04:00
chip - > gpio_chip . get = gen_74x164_get_value ;
chip - > gpio_chip . set = gen_74x164_set_value ;
2013-12-07 14:08:22 +04:00
chip - > gpio_chip . base = - 1 ;
2012-09-11 00:35:39 +04:00
if ( of_property_read_u32 ( spi - > dev . of_node , " registers-number " , & chip - > registers ) ) {
dev_err ( & spi - > dev , " Missing registers-number property in the DT. \n " ) ;
ret = - EINVAL ;
goto exit_destroy ;
}
chip - > gpio_chip . ngpio = GEN_74X164_NUMBER_GPIOS * chip - > registers ;
2012-10-16 17:24:01 +04:00
chip - > buffer = devm_kzalloc ( & spi - > dev , chip - > registers , GFP_KERNEL ) ;
2012-09-11 00:35:39 +04:00
if ( ! chip - > buffer ) {
ret = - ENOMEM ;
goto exit_destroy ;
}
2013-12-04 17:42:46 +04:00
chip - > gpio_chip . can_sleep = true ;
2010-10-28 02:33:18 +04:00
chip - > gpio_chip . dev = & spi - > dev ;
chip - > gpio_chip . owner = THIS_MODULE ;
ret = __gen_74x164_write_config ( chip ) ;
if ( ret ) {
dev_err ( & spi - > dev , " Failed writing: %d \n " , ret ) ;
goto exit_destroy ;
}
ret = gpiochip_add ( & chip - > gpio_chip ) ;
if ( ret )
goto exit_destroy ;
return ret ;
exit_destroy :
mutex_destroy ( & chip - > lock ) ;
return ret ;
}
2012-11-19 22:25:50 +04:00
static int gen_74x164_remove ( struct spi_device * spi )
2010-10-28 02:33:18 +04:00
{
struct gen_74x164_chip * chip ;
int ret ;
2013-03-15 13:17:18 +04:00
chip = spi_get_drvdata ( spi ) ;
2010-10-28 02:33:18 +04:00
if ( chip = = NULL )
return - ENODEV ;
ret = gpiochip_remove ( & chip - > gpio_chip ) ;
2012-09-05 12:40:51 +04:00
if ( ! ret )
2010-10-28 02:33:18 +04:00
mutex_destroy ( & chip - > lock ) ;
2012-09-05 12:40:51 +04:00
else
2010-10-28 02:33:18 +04:00
dev_err ( & spi - > dev , " Failed to remove the GPIO controller: %d \n " ,
ret ) ;
return ret ;
}
2012-09-07 16:18:13 +04:00
static const struct of_device_id gen_74x164_dt_ids [ ] = {
{ . compatible = " fairchild,74hc595 " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , gen_74x164_dt_ids ) ;
2010-10-28 02:33:18 +04:00
static struct spi_driver gen_74x164_driver = {
. driver = {
2011-05-28 03:35:59 +04:00
. name = " 74x164 " ,
2010-10-28 02:33:18 +04:00
. owner = THIS_MODULE ,
2013-09-19 15:58:08 +04:00
. of_match_table = gen_74x164_dt_ids ,
2010-10-28 02:33:18 +04:00
} ,
. probe = gen_74x164_probe ,
2012-11-19 22:20:08 +04:00
. remove = gen_74x164_remove ,
2010-10-28 02:33:18 +04:00
} ;
2012-09-05 12:40:50 +04:00
module_spi_driver ( gen_74x164_driver ) ;
2010-10-28 02:33:18 +04:00
MODULE_AUTHOR ( " Gabor Juhos <juhosg@openwrt.org> " ) ;
MODULE_AUTHOR ( " Miguel Gaio <miguel.gaio@efixo.com> " ) ;
MODULE_DESCRIPTION ( " GPIO expander driver for 74X164 8-bits shift register " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;