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>
2011-05-20 10:40:19 +04:00
# include <linux/err.h>
2010-10-28 02:33:15 +04:00
# 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>
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:17 +04:00
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:17 +04:00
return readb ( reg ) ;
2011-05-20 10:40:14 +04:00
}
static void bgpio_write16 ( void __iomem * reg , unsigned long data )
{
2011-05-20 10:40:17 +04:00
writew ( data , reg ) ;
2011-05-20 10:40:14 +04:00
}
static unsigned long bgpio_read16 ( void __iomem * reg )
{
2011-05-20 10:40:17 +04:00
return readw ( reg ) ;
2011-05-20 10:40:14 +04:00
}
static void bgpio_write32 ( void __iomem * reg , unsigned long data )
{
2011-05-20 10:40:17 +04:00
writel ( data , reg ) ;
2011-05-20 10:40:14 +04:00
}
static unsigned long bgpio_read32 ( void __iomem * reg )
{
2011-05-20 10:40:17 +04:00
return readl ( reg ) ;
2011-05-20 10:40:14 +04:00
}
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 )
{
2011-05-20 10:40:17 +04:00
writeq ( data , reg ) ;
2011-05-20 10:40:14 +04:00
}
static unsigned long bgpio_read64 ( void __iomem * reg )
{
2011-05-20 10:40:17 +04:00
return 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:19 +04:00
static int bgpio_setup_accessors ( struct device * dev ,
struct bgpio_chip * bgc ,
bool be )
2010-10-28 02:33:15 +04:00
{
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 :
2011-05-20 10:40:19 +04:00
dev_err ( dev , " unsupported data width %u bits \n " , bgc - > bits ) ;
2011-05-20 10:40:14 +04:00
return - EINVAL ;
}
2011-05-20 10:40:19 +04:00
bgc - > pin2mask = be ? bgpio_pin2mask_be : bgpio_pin2mask ;
2011-05-20 10:40:14 +04:00
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
*/
2011-05-20 10:40:19 +04:00
static int bgpio_setup_io ( struct bgpio_chip * bgc ,
void __iomem * dat ,
void __iomem * set ,
void __iomem * clr )
2011-05-20 10:40:14 +04:00
{
2010-10-28 02:33:15 +04:00
2011-05-20 10:40:19 +04:00
bgc - > reg_dat = dat ;
2010-10-28 02:33:15 +04:00
if ( ! bgc - > reg_dat )
2011-05-20 10:40:19 +04:00
return - EINVAL ;
2011-05-20 10:40:16 +04:00
2011-05-20 10:40:19 +04:00
if ( set & & clr ) {
bgc - > reg_set = set ;
bgc - > reg_clr = clr ;
2011-05-20 10:40:16 +04:00
bgc - > gc . set = bgpio_set_with_clear ;
2011-05-20 10:40:19 +04:00
} else if ( set & & ! clr ) {
bgc - > reg_set = set ;
2011-05-20 10:40:16 +04:00
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:19 +04:00
static int bgpio_setup_direction ( struct bgpio_chip * bgc ,
void __iomem * dirout ,
void __iomem * dirin )
2011-05-20 10:40:17 +04:00
{
2011-05-20 10:40:19 +04:00
if ( dirout & & dirin ) {
2011-05-20 10:40:17 +04:00
return - EINVAL ;
2011-05-20 10:40:19 +04:00
} else if ( dirout ) {
bgc - > reg_dir = dirout ;
2011-05-20 10:40:17 +04:00
bgc - > gc . direction_output = bgpio_dir_out ;
bgc - > gc . direction_input = bgpio_dir_in ;
2011-05-20 10:40:19 +04:00
} else if ( dirin ) {
bgc - > reg_dir = dirin ;
2011-05-20 10:40:17 +04:00
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:19 +04:00
int __devexit bgpio_remove ( struct bgpio_chip * bgc )
{
int err = gpiochip_remove ( & bgc - > gc ) ;
kfree ( bgc ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( bgpio_remove ) ;
int __devinit bgpio_init ( struct bgpio_chip * bgc ,
struct device * dev ,
unsigned long sz ,
void __iomem * dat ,
void __iomem * set ,
void __iomem * clr ,
void __iomem * dirout ,
void __iomem * dirin ,
bool big_endian )
2011-05-20 10:40:16 +04:00
{
int ret ;
2011-05-20 10:40:19 +04:00
if ( ! is_power_of_2 ( sz ) )
return - EINVAL ;
2011-05-20 10:40:16 +04:00
2011-05-20 10:40:19 +04:00
bgc - > bits = sz * 8 ;
if ( bgc - > bits > BITS_PER_LONG )
return - EINVAL ;
spin_lock_init ( & bgc - > lock ) ;
bgc - > gc . dev = dev ;
bgc - > gc . label = dev_name ( dev ) ;
bgc - > gc . base = - 1 ;
bgc - > gc . ngpio = bgc - > bits ;
ret = bgpio_setup_io ( bgc , dat , set , clr ) ;
2011-05-20 10:40:16 +04:00
if ( ret )
return ret ;
2010-10-28 02:33:15 +04:00
2011-05-20 10:40:19 +04:00
ret = bgpio_setup_accessors ( dev , bgc , big_endian ) ;
2011-05-20 10:40:14 +04:00
if ( ret )
return ret ;
2010-10-28 02:33:15 +04:00
2011-05-20 10:40:19 +04:00
ret = bgpio_setup_direction ( bgc , dirout , dirin ) ;
2011-05-20 10:40:17 +04:00
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
2011-05-20 10:40:19 +04:00
return ret ;
}
EXPORT_SYMBOL_GPL ( bgpio_init ) ;
2010-10-28 02:33:15 +04:00
2011-05-20 10:40:19 +04:00
# ifdef CONFIG_GPIO_BASIC_MMIO
2010-10-28 02:33:15 +04:00
2011-05-20 10:40:19 +04:00
static void __iomem * bgpio_map ( struct platform_device * pdev ,
const char * name ,
resource_size_t sane_sz ,
int * err )
{
struct device * dev = & pdev - > dev ;
struct resource * r ;
resource_size_t start ;
resource_size_t sz ;
void __iomem * ret ;
* err = 0 ;
r = platform_get_resource_byname ( pdev , IORESOURCE_MEM , name ) ;
if ( ! r )
return NULL ;
sz = resource_size ( r ) ;
if ( sz ! = sane_sz ) {
* err = - EINVAL ;
return NULL ;
}
start = r - > start ;
if ( ! devm_request_mem_region ( dev , start , sz , r - > name ) ) {
* err = - EBUSY ;
return NULL ;
}
ret = devm_ioremap ( dev , start , sz ) ;
if ( ! ret ) {
* err = - ENOMEM ;
return NULL ;
}
2010-10-28 02:33:15 +04:00
return ret ;
}
2011-05-20 10:40:19 +04:00
static int __devinit bgpio_pdev_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct resource * r ;
void __iomem * dat ;
void __iomem * set ;
void __iomem * clr ;
void __iomem * dirout ;
void __iomem * dirin ;
unsigned long sz ;
bool be ;
int err ;
struct bgpio_chip * bgc ;
struct bgpio_pdata * pdata = dev_get_platdata ( dev ) ;
r = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " dat " ) ;
if ( ! r )
return - EINVAL ;
sz = resource_size ( r ) ;
dat = bgpio_map ( pdev , " dat " , sz , & err ) ;
if ( ! dat )
return err ? err : - EINVAL ;
set = bgpio_map ( pdev , " set " , sz , & err ) ;
if ( err )
return err ;
clr = bgpio_map ( pdev , " clr " , sz , & err ) ;
if ( err )
return err ;
dirout = bgpio_map ( pdev , " dirout " , sz , & err ) ;
if ( err )
return err ;
dirin = bgpio_map ( pdev , " dirin " , sz , & err ) ;
if ( err )
return err ;
be = ! strcmp ( platform_get_device_id ( pdev ) - > name , " basic-mmio-gpio-be " ) ;
bgc = devm_kzalloc ( & pdev - > dev , sizeof ( * bgc ) , GFP_KERNEL ) ;
if ( ! bgc )
return - ENOMEM ;
err = bgpio_init ( bgc , dev , sz , dat , set , clr , dirout , dirin , be ) ;
if ( err )
return err ;
if ( pdata ) {
bgc - > gc . base = pdata - > base ;
if ( pdata - > ngpio > 0 )
bgc - > gc . ngpio = pdata - > ngpio ;
}
platform_set_drvdata ( pdev , bgc ) ;
return gpiochip_add ( & bgc - > gc ) ;
}
static int __devexit bgpio_pdev_remove ( struct platform_device * pdev )
2010-10-28 02:33:15 +04:00
{
2011-05-20 10:40:14 +04:00
struct bgpio_chip * bgc = platform_get_drvdata ( pdev ) ;
2010-10-28 02:33:15 +04:00
2011-05-20 10:40:19 +04:00
return bgpio_remove ( bgc ) ;
2010-10-28 02:33:15 +04:00
}
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 ,
2011-05-20 10:40:19 +04:00
. probe = bgpio_pdev_probe ,
. remove = __devexit_p ( bgpio_pdev_remove ) ,
2010-10-28 02:33:15 +04:00
} ;
2011-05-20 10:40:19 +04:00
static int __init bgpio_platform_init ( void )
2010-10-28 02:33:15 +04:00
{
return platform_driver_register ( & bgpio_driver ) ;
}
2011-05-20 10:40:19 +04:00
module_init ( bgpio_platform_init ) ;
2010-10-28 02:33:15 +04:00
2011-05-20 10:40:19 +04:00
static void __exit bgpio_platform_exit ( void )
2010-10-28 02:33:15 +04:00
{
platform_driver_unregister ( & bgpio_driver ) ;
}
2011-05-20 10:40:19 +04:00
module_exit ( bgpio_platform_exit ) ;
# endif /* CONFIG_GPIO_BASIC_MMIO */
2010-10-28 02:33:15 +04:00
MODULE_DESCRIPTION ( " Driver for basic memory-mapped GPIO controllers " ) ;
MODULE_AUTHOR ( " Anton Vorontsov <cbouatmailru@gmail.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;