2010-10-28 02:33:15 +04:00
/*
* Driver for basic memory - mapped GPIO controllers .
*
* Copyright 2008 MontaVista Software , Inc .
* Copyright 2008 , 2010 Anton Vorontsov < cbouatmailru @ gmail . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
* . . . . ` ` . ` ` ` ~ ~ ~ ~ ` ` ` ` . ` . ` . ` . ` . ` ` ` ` ` ` ` ' ' , , , . . . . . . . . . ` ` ` ` ` . . . . . . ` . . . . . . .
* . . . ` ` ` ` ` ` ` ` ` . .
* . . The simplest form of a GPIO controller that the driver supports is ` `
* ` . just a single " data " register , where GPIO state can be read and / or `
* ` , . . written . , , . . ` ` ~ ~ ~ ~ . . . . . ` ` . ` . ` . ~ ~ . ` ` ` . ` . . . . . . . . . ` ` ` ` ` ` . ` ` ` ` ` ` `
* ` ` ` ` ` ` ` ` `
___
_ / ~ ~ | ___ / ~ | . ` ` ` ~ ~ ~ ~ ~ ~ ___ / ___ \ ___ , ~ . ` . ` . ` . ` ` ` ` ` . ~ ~ . . . , , , , . . .
__________ | ~ $ @ ~ ~ ~ % ~ / o * o * o * o * o * o \ . . Implementing such a GPIO .
o ` ~ ~ ~ ~ \ ___ / ~ ~ ~ ~ ` controller in FPGA is , . `
` . . . . trivial . . ' ~ ` . ` ` ` . ` ` `
* ` ` ` ` ` ` `
* . ` ` ` ` ` ` ` ~ ~ ~ ~ ` . . ` . ` ` . ` ` .
* . The driver supports ` . . . , . . ` ` ` . ` ~ ~ ~ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` . . . . ` ` ` ` . ` ` , ,
* . big - endian notation , just ` . . . A bit more sophisticated controllers ,
* . register the device with - be ` . . with a pair of set / clear - bit registers ,
* ` . . suffix . ` ` ` ~ ~ ` ` ` ` ` . . . . ` . ` . affecting the data register and the . `
* ` ` . ` . ` ` . . . ` ` ` ` ` ` . . output pins are also supported . `
* ^ ^ ` ` ` ` ` . ` ` ` ` ` ` ` ` ` . , ` ` ~ ` ` ~ ` ` ~ ~ ` ` ` ` ` `
* . ^ ^
* , . . ` . ` . ` . . . ` ` ` ` ` ` ` ` ` ` ` ` . . . . . . ` . ` . ` . ` . ` . ` . . ` . ` . ` . .
* . . The expectation is that in at least some cases . , - ~ ~ ~ - ,
* . this will be used with roll - your - own ASIC / FPGA . ` \ /
* . logic in Verilog or VHDL . ~ ~ ~ ` ` ` ` ` ` ` ` ` . . ` ` ` ` ` ~ ~ ` \ /
* . . ` ` ` ` ` ` ` ` . . . . . . ` ` ` ` ` ` ` ` ` ` ` \ o_
* |
* ^ ^ / \
*
* . . . ` ` ` ` ` ~ ~ ` . . . . . ` ` . ` . . . . . . . . . . ` ` ` ` ` ` . ` . ` ` . ` ` ` . . . . . . . . ` ` .
* ` 8 , 16 , 32 and 64 bits registers are supported , and ` ` .
* . the number of GPIOs is determined by the width of ~
* . . the registers . , . . . . . . . . . . . . ` ` ` . ` . ` . . ` . ` . ~ ~ ~ . ` . ` . ` ~
* ` . . . . . . . ` ` ` ` . ` ` `
*/
# include <linux/init.h>
# include <linux/bug.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/spinlock.h>
# include <linux/compiler.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/log2.h>
# include <linux/ioport.h>
# include <linux/io.h>
# include <linux/gpio.h>
# include <linux/slab.h>
# include <linux/platform_device.h>
# include <linux/mod_devicetable.h>
# include <linux/basic_mmio_gpio.h>
struct bgpio_chip {
struct gpio_chip gc ;
2011-05-20 10:40:14 +04:00
unsigned long ( * read_reg ) ( void __iomem * reg ) ;
void ( * write_reg ) ( void __iomem * reg , unsigned long data ) ;
2010-10-28 02:33:15 +04:00
void __iomem * reg_dat ;
void __iomem * reg_set ;
void __iomem * reg_clr ;
2011-05-20 10:40:17 +04:00
void __iomem * reg_dir ;
2010-10-28 02:33:15 +04:00
/* Number of bits (GPIOs): <register width> * 8. */
int bits ;
/*
* Some GPIO controllers work with the big - endian bits notation ,
* e . g . in a 8 - bits register , GPIO7 is the least significant bit .
*/
2011-05-20 10:40:14 +04:00
unsigned long ( * pin2mask ) ( struct bgpio_chip * bgc , unsigned int pin ) ;
2010-10-28 02:33:15 +04:00
/*
* Used to lock bgpio_chip - > data . Also , this is needed to keep
* shadowed and real data registers writes together .
*/
spinlock_t lock ;
/* Shadowed data register to clear/set bits safely. */
unsigned long data ;
2011-05-20 10:40:17 +04:00
/* Shadowed direction registers to clear/set direction safely. */
unsigned long dir ;
2010-10-28 02:33:15 +04:00
} ;
static struct bgpio_chip * to_bgpio_chip ( struct gpio_chip * gc )
{
return container_of ( gc , struct bgpio_chip , gc ) ;
}
2011-05-20 10:40:14 +04:00
static void bgpio_write8 ( void __iomem * reg , unsigned long data )
2010-10-28 02:33:15 +04:00
{
2011-05-20 10:40:14 +04:00
__raw_writeb ( data , reg ) ;
2010-10-28 02:33:15 +04:00
}
2011-05-20 10:40:14 +04:00
static unsigned long bgpio_read8 ( void __iomem * reg )
2010-10-28 02:33:15 +04:00
{
2011-05-20 10:40:14 +04:00
return __raw_readb ( reg ) ;
}
static void bgpio_write16 ( void __iomem * reg , unsigned long data )
{
__raw_writew ( data , reg ) ;
}
static unsigned long bgpio_read16 ( void __iomem * reg )
{
return __raw_readw ( reg ) ;
}
static void bgpio_write32 ( void __iomem * reg , unsigned long data )
{
__raw_writel ( data , reg ) ;
}
static unsigned long bgpio_read32 ( void __iomem * reg )
{
return __raw_readl ( reg ) ;
}
2010-10-28 02:33:15 +04:00
# if BITS_PER_LONG >= 64
2011-05-20 10:40:14 +04:00
static void bgpio_write64 ( void __iomem * reg , unsigned long data )
{
__raw_writeq ( data , reg ) ;
}
static unsigned long bgpio_read64 ( void __iomem * reg )
{
return __raw_readq ( reg ) ;
2010-10-28 02:33:15 +04:00
}
2011-05-20 10:40:14 +04:00
# endif /* BITS_PER_LONG >= 64 */
2010-10-28 02:33:15 +04:00
static unsigned long bgpio_pin2mask ( struct bgpio_chip * bgc , unsigned int pin )
{
2011-05-20 10:40:14 +04:00
return 1 < < pin ;
}
static unsigned long bgpio_pin2mask_be ( struct bgpio_chip * bgc ,
unsigned int pin )
{
return 1 < < ( bgc - > bits - 1 - pin ) ;
2010-10-28 02:33:15 +04:00
}
static int bgpio_get ( struct gpio_chip * gc , unsigned int gpio )
{
struct bgpio_chip * bgc = to_bgpio_chip ( gc ) ;
2011-05-20 10:40:14 +04:00
return bgc - > read_reg ( bgc - > reg_dat ) & bgc - > pin2mask ( bgc , gpio ) ;
2010-10-28 02:33:15 +04:00
}
static void bgpio_set ( struct gpio_chip * gc , unsigned int gpio , int val )
{
struct bgpio_chip * bgc = to_bgpio_chip ( gc ) ;
2011-05-20 10:40:14 +04:00
unsigned long mask = bgc - > pin2mask ( bgc , gpio ) ;
2010-10-28 02:33:15 +04:00
unsigned long flags ;
spin_lock_irqsave ( & bgc - > lock , flags ) ;
if ( val )
bgc - > data | = mask ;
else
bgc - > data & = ~ mask ;
2011-05-20 10:40:14 +04:00
bgc - > write_reg ( bgc - > reg_dat , bgc - > data ) ;
2010-10-28 02:33:15 +04:00
spin_unlock_irqrestore ( & bgc - > lock , flags ) ;
}
2011-05-20 10:40:16 +04:00
static void bgpio_set_with_clear ( struct gpio_chip * gc , unsigned int gpio ,
int val )
{
struct bgpio_chip * bgc = to_bgpio_chip ( gc ) ;
unsigned long mask = bgc - > pin2mask ( bgc , gpio ) ;
if ( val )
bgc - > write_reg ( bgc - > reg_set , mask ) ;
else
bgc - > write_reg ( bgc - > reg_clr , mask ) ;
}
2011-05-20 10:40:16 +04:00
static void bgpio_set_set ( struct gpio_chip * gc , unsigned int gpio , int val )
{
struct bgpio_chip * bgc = to_bgpio_chip ( gc ) ;
unsigned long mask = bgc - > pin2mask ( bgc , gpio ) ;
unsigned long flags ;
spin_lock_irqsave ( & bgc - > lock , flags ) ;
if ( val )
bgc - > data | = mask ;
else
bgc - > data & = ~ mask ;
bgc - > write_reg ( bgc - > reg_set , bgc - > data ) ;
spin_unlock_irqrestore ( & bgc - > lock , flags ) ;
}
2011-05-20 10:40:17 +04:00
static int bgpio_simple_dir_in ( struct gpio_chip * gc , unsigned int gpio )
{
return 0 ;
}
static int bgpio_simple_dir_out ( struct gpio_chip * gc , unsigned int gpio ,
int val )
{
gc - > set ( gc , gpio , val ) ;
return 0 ;
}
2010-10-28 02:33:15 +04:00
static int bgpio_dir_in ( struct gpio_chip * gc , unsigned int gpio )
{
2011-05-20 10:40:17 +04:00
struct bgpio_chip * bgc = to_bgpio_chip ( gc ) ;
unsigned long flags ;
spin_lock_irqsave ( & bgc - > lock , flags ) ;
bgc - > dir & = ~ bgc - > pin2mask ( bgc , gpio ) ;
bgc - > write_reg ( bgc - > reg_dir , bgc - > dir ) ;
spin_unlock_irqrestore ( & bgc - > lock , flags ) ;
2010-10-28 02:33:15 +04:00
return 0 ;
}
static int bgpio_dir_out ( struct gpio_chip * gc , unsigned int gpio , int val )
{
2011-05-20 10:40:17 +04:00
struct bgpio_chip * bgc = to_bgpio_chip ( gc ) ;
unsigned long flags ;
gc - > set ( gc , gpio , val ) ;
spin_lock_irqsave ( & bgc - > lock , flags ) ;
bgc - > dir | = bgc - > pin2mask ( bgc , gpio ) ;
bgc - > write_reg ( bgc - > reg_dir , bgc - > dir ) ;
spin_unlock_irqrestore ( & bgc - > lock , flags ) ;
return 0 ;
}
static int bgpio_dir_in_inv ( struct gpio_chip * gc , unsigned int gpio )
{
struct bgpio_chip * bgc = to_bgpio_chip ( gc ) ;
unsigned long flags ;
spin_lock_irqsave ( & bgc - > lock , flags ) ;
bgc - > dir | = bgc - > pin2mask ( bgc , gpio ) ;
bgc - > write_reg ( bgc - > reg_dir , bgc - > dir ) ;
spin_unlock_irqrestore ( & bgc - > lock , flags ) ;
return 0 ;
}
static int bgpio_dir_out_inv ( struct gpio_chip * gc , unsigned int gpio , int val )
{
struct bgpio_chip * bgc = to_bgpio_chip ( gc ) ;
unsigned long flags ;
2011-05-20 10:40:16 +04:00
gc - > set ( gc , gpio , val ) ;
2011-05-20 10:40:17 +04:00
spin_lock_irqsave ( & bgc - > lock , flags ) ;
bgc - > dir & = ~ bgc - > pin2mask ( bgc , gpio ) ;
bgc - > write_reg ( bgc - > reg_dir , bgc - > dir ) ;
spin_unlock_irqrestore ( & bgc - > lock , flags ) ;
2010-10-28 02:33:15 +04:00
return 0 ;
}
2011-05-20 10:40:16 +04:00
static void __iomem * bgpio_request_and_map ( struct device * dev ,
struct resource * res )
{
if ( ! devm_request_mem_region ( dev , res - > start , resource_size ( res ) ,
res - > name ? : " mmio_gpio " ) )
return NULL ;
return devm_ioremap ( dev , res - > start , resource_size ( res ) ) ;
}
2011-05-20 10:40:14 +04:00
static int bgpio_setup_accessors ( struct platform_device * pdev ,
struct bgpio_chip * bgc )
2010-10-28 02:33:15 +04:00
{
const struct platform_device_id * platid = platform_get_device_id ( pdev ) ;
2011-05-20 10:40:14 +04:00
switch ( bgc - > bits ) {
case 8 :
bgc - > read_reg = bgpio_read8 ;
bgc - > write_reg = bgpio_write8 ;
break ;
case 16 :
bgc - > read_reg = bgpio_read16 ;
bgc - > write_reg = bgpio_write16 ;
break ;
case 32 :
bgc - > read_reg = bgpio_read32 ;
bgc - > write_reg = bgpio_write32 ;
break ;
# if BITS_PER_LONG >= 64
case 64 :
bgc - > read_reg = bgpio_read64 ;
bgc - > write_reg = bgpio_write64 ;
break ;
# endif /* BITS_PER_LONG >= 64 */
default :
dev_err ( & pdev - > dev , " unsupported data width %u bits \n " ,
bgc - > bits ) ;
return - EINVAL ;
}
bgc - > pin2mask = strcmp ( platid - > name , " basic-mmio-gpio-be " ) ?
bgpio_pin2mask : bgpio_pin2mask_be ;
return 0 ;
}
2011-05-20 10:40:16 +04:00
/*
* Create the device and allocate the resources . For setting GPIO ' s there are
2011-05-20 10:40:16 +04:00
* three supported configurations :
2011-05-20 10:40:16 +04:00
*
2011-05-20 10:40:16 +04:00
* - single input / output register resource ( named " dat " ) .
2011-05-20 10:40:16 +04:00
* - set / clear pair ( named " set " and " clr " ) .
2011-05-20 10:40:16 +04:00
* - single output register resource and single input resource ( " set " and
* dat " ).
2011-05-20 10:40:16 +04:00
*
* For the single output register , this drives a 1 by setting a bit and a zero
* by clearing a bit . For the set clr pair , this drives a 1 by setting a bit
* in the set register and clears it by setting a bit in the clear register .
* The configuration is detected by which resources are present .
2011-05-20 10:40:17 +04:00
*
* For setting the GPIO direction , there are three supported configurations :
*
* - simple bidirection GPIO that requires no configuration .
* - an output direction register ( named " dirout " ) where a 1 bit
* indicates the GPIO is an output .
* - an input direction register ( named " dirin " ) where a 1 bit indicates
* the GPIO is an input .
2011-05-20 10:40:16 +04:00
*/
static int bgpio_setup_io ( struct platform_device * pdev ,
struct bgpio_chip * bgc )
2011-05-20 10:40:14 +04:00
{
2010-10-28 02:33:15 +04:00
struct resource * res_set ;
struct resource * res_clr ;
2011-05-20 10:40:16 +04:00
struct resource * res_dat ;
2010-10-28 02:33:15 +04:00
resource_size_t dat_sz ;
res_dat = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " dat " ) ;
if ( ! res_dat )
return - EINVAL ;
dat_sz = resource_size ( res_dat ) ;
if ( ! is_power_of_2 ( dat_sz ) )
return - EINVAL ;
2011-05-20 10:40:16 +04:00
bgc - > bits = dat_sz * 8 ;
if ( bgc - > bits > BITS_PER_LONG )
2010-10-28 02:33:15 +04:00
return - EINVAL ;
2011-05-20 10:40:16 +04:00
bgc - > reg_dat = bgpio_request_and_map ( & pdev - > dev , res_dat ) ;
2010-10-28 02:33:15 +04:00
if ( ! bgc - > reg_dat )
return - ENOMEM ;
res_set = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " set " ) ;
res_clr = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " clr " ) ;
if ( res_set & & res_clr ) {
if ( resource_size ( res_set ) ! = resource_size ( res_clr ) | |
2011-05-20 10:40:16 +04:00
resource_size ( res_set ) ! = resource_size ( res_dat ) )
2010-10-28 02:33:15 +04:00
return - EINVAL ;
2011-05-20 10:40:16 +04:00
bgc - > reg_set = bgpio_request_and_map ( & pdev - > dev , res_set ) ;
bgc - > reg_clr = bgpio_request_and_map ( & pdev - > dev , res_clr ) ;
2010-10-28 02:33:15 +04:00
if ( ! bgc - > reg_set | | ! bgc - > reg_clr )
return - ENOMEM ;
2011-05-20 10:40:16 +04:00
bgc - > gc . set = bgpio_set_with_clear ;
2011-05-20 10:40:16 +04:00
} else if ( res_set & & ! res_clr ) {
if ( resource_size ( res_set ) ! = resource_size ( res_dat ) )
return - EINVAL ;
bgc - > reg_set = bgpio_request_and_map ( & pdev - > dev , res_set ) ;
if ( ! bgc - > reg_set )
return - ENOMEM ;
bgc - > gc . set = bgpio_set_set ;
2011-05-20 10:40:16 +04:00
} else {
bgc - > gc . set = bgpio_set ;
2010-10-28 02:33:15 +04:00
}
2011-05-20 10:40:16 +04:00
bgc - > gc . get = bgpio_get ;
2011-05-20 10:40:16 +04:00
return 0 ;
}
2011-05-20 10:40:17 +04:00
static int bgpio_setup_direction ( struct platform_device * pdev ,
struct bgpio_chip * bgc )
{
struct resource * res_dirout ;
struct resource * res_dirin ;
res_dirout = platform_get_resource_byname ( pdev , IORESOURCE_MEM ,
" dirout " ) ;
res_dirin = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " dirin " ) ;
if ( res_dirout & & res_dirin ) {
return - EINVAL ;
} else if ( res_dirout ) {
bgc - > reg_dir = bgpio_request_and_map ( & pdev - > dev , res_dirout ) ;
if ( ! bgc - > reg_dir )
return - ENOMEM ;
bgc - > gc . direction_output = bgpio_dir_out ;
bgc - > gc . direction_input = bgpio_dir_in ;
} else if ( res_dirin ) {
bgc - > reg_dir = bgpio_request_and_map ( & pdev - > dev , res_dirin ) ;
if ( ! bgc - > reg_dir )
return - ENOMEM ;
bgc - > gc . direction_output = bgpio_dir_out_inv ;
bgc - > gc . direction_input = bgpio_dir_in_inv ;
} else {
bgc - > gc . direction_output = bgpio_simple_dir_out ;
bgc - > gc . direction_input = bgpio_simple_dir_in ;
}
return 0 ;
}
2011-05-20 10:40:16 +04:00
static int __devinit bgpio_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct bgpio_pdata * pdata = dev_get_platdata ( dev ) ;
struct bgpio_chip * bgc ;
int ret ;
int ngpio ;
bgc = devm_kzalloc ( dev , sizeof ( * bgc ) , GFP_KERNEL ) ;
if ( ! bgc )
return - ENOMEM ;
ret = bgpio_setup_io ( pdev , bgc ) ;
if ( ret )
return ret ;
2010-10-28 02:33:15 +04:00
2011-05-20 10:40:16 +04:00
ngpio = bgc - > bits ;
2011-05-20 10:40:15 +04:00
if ( pdata ) {
bgc - > gc . base = pdata - > base ;
if ( pdata - > ngpio > 0 )
ngpio = pdata - > ngpio ;
} else {
bgc - > gc . base = - 1 ;
}
2011-05-20 10:40:14 +04:00
ret = bgpio_setup_accessors ( pdev , bgc ) ;
if ( ret )
return ret ;
2010-10-28 02:33:15 +04:00
2011-05-20 10:40:16 +04:00
spin_lock_init ( & bgc - > lock ) ;
2011-05-20 10:40:17 +04:00
ret = bgpio_setup_direction ( pdev , bgc ) ;
if ( ret )
return ret ;
2011-05-20 10:40:14 +04:00
bgc - > data = bgc - > read_reg ( bgc - > reg_dat ) ;
2011-05-20 10:40:15 +04:00
bgc - > gc . ngpio = ngpio ;
2010-10-28 02:33:15 +04:00
bgc - > gc . dev = dev ;
bgc - > gc . label = dev_name ( dev ) ;
2011-05-20 10:40:14 +04:00
platform_set_drvdata ( pdev , bgc ) ;
2010-10-28 02:33:15 +04:00
ret = gpiochip_add ( & bgc - > gc ) ;
if ( ret )
dev_err ( dev , " gpiochip_add() failed: %d \n " , ret ) ;
return ret ;
}
static int __devexit bgpio_remove ( struct platform_device * pdev )
{
2011-05-20 10:40:14 +04:00
struct bgpio_chip * bgc = platform_get_drvdata ( pdev ) ;
2010-10-28 02:33:15 +04:00
return gpiochip_remove ( & bgc - > gc ) ;
}
static const struct platform_device_id bgpio_id_table [ ] = {
{ " basic-mmio-gpio " , } ,
{ " basic-mmio-gpio-be " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( platform , bgpio_id_table ) ;
static struct platform_driver bgpio_driver = {
. driver = {
. name = " basic-mmio-gpio " ,
} ,
. id_table = bgpio_id_table ,
. probe = bgpio_probe ,
. remove = __devexit_p ( bgpio_remove ) ,
} ;
static int __init bgpio_init ( void )
{
return platform_driver_register ( & bgpio_driver ) ;
}
module_init ( bgpio_init ) ;
static void __exit bgpio_exit ( void )
{
platform_driver_unregister ( & bgpio_driver ) ;
}
module_exit ( bgpio_exit ) ;
MODULE_DESCRIPTION ( " Driver for basic memory-mapped GPIO controllers " ) ;
MODULE_AUTHOR ( " Anton Vorontsov <cbouatmailru@gmail.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;