2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-04-07 12:08:01 +04:00
/*
* Janz MODULbus VMOD - TTL GPIO Driver
*
* Copyright ( c ) 2010 Ira W . Snyder < iws @ ovro . caltech . edu >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/platform_device.h>
# include <linux/io.h>
2018-03-12 14:08:02 +03:00
# include <linux/gpio/driver.h>
2010-04-07 12:08:01 +04:00
# include <linux/slab.h>
2018-03-12 14:11:45 +03:00
# include <linux/bitops.h>
2010-04-07 12:08:01 +04:00
# include <linux/mfd/janz.h>
# define DRV_NAME "janz-ttl"
# define PORTA_DIRECTION 0x23
# define PORTB_DIRECTION 0x2B
# define PORTC_DIRECTION 0x06
# define PORTA_IOCTL 0x24
# define PORTB_IOCTL 0x2C
# define PORTC_IOCTL 0x07
# define MASTER_INT_CTL 0x00
# define MASTER_CONF_CTL 0x01
2018-03-12 14:11:45 +03:00
# define CONF_PAE BIT(2)
# define CONF_PBE BIT(7)
# define CONF_PCE BIT(4)
2010-04-07 12:08:01 +04:00
struct ttl_control_regs {
__be16 portc ;
__be16 portb ;
__be16 porta ;
__be16 control ;
} ;
struct ttl_module {
struct gpio_chip gpio ;
/* base address of registers */
struct ttl_control_regs __iomem * regs ;
u8 portc_shadow ;
u8 portb_shadow ;
u8 porta_shadow ;
spinlock_t lock ;
} ;
static int ttl_get_value ( struct gpio_chip * gpio , unsigned offset )
{
2015-11-04 11:56:26 +03:00
struct ttl_module * mod = dev_get_drvdata ( gpio - > parent ) ;
2010-04-07 12:08:01 +04:00
u8 * shadow ;
int ret ;
if ( offset < 8 ) {
shadow = & mod - > porta_shadow ;
} else if ( offset < 16 ) {
shadow = & mod - > portb_shadow ;
offset - = 8 ;
} else {
shadow = & mod - > portc_shadow ;
offset - = 16 ;
}
spin_lock ( & mod - > lock ) ;
2018-03-12 14:11:45 +03:00
ret = * shadow & BIT ( offset ) ;
2010-04-07 12:08:01 +04:00
spin_unlock ( & mod - > lock ) ;
2015-12-21 13:03:33 +03:00
return ! ! ret ;
2010-04-07 12:08:01 +04:00
}
static void ttl_set_value ( struct gpio_chip * gpio , unsigned offset , int value )
{
2015-11-04 11:56:26 +03:00
struct ttl_module * mod = dev_get_drvdata ( gpio - > parent ) ;
2010-04-07 12:08:01 +04:00
void __iomem * port ;
u8 * shadow ;
if ( offset < 8 ) {
port = & mod - > regs - > porta ;
shadow = & mod - > porta_shadow ;
} else if ( offset < 16 ) {
port = & mod - > regs - > portb ;
shadow = & mod - > portb_shadow ;
offset - = 8 ;
} else {
port = & mod - > regs - > portc ;
shadow = & mod - > portc_shadow ;
offset - = 16 ;
}
spin_lock ( & mod - > lock ) ;
if ( value )
2018-03-12 14:11:45 +03:00
* shadow | = BIT ( offset ) ;
2010-04-07 12:08:01 +04:00
else
2018-03-12 14:11:45 +03:00
* shadow & = ~ BIT ( offset ) ;
2010-04-07 12:08:01 +04:00
iowrite16be ( * shadow , port ) ;
spin_unlock ( & mod - > lock ) ;
}
2012-11-19 22:22:34 +04:00
static void ttl_write_reg ( struct ttl_module * mod , u8 reg , u16 val )
2010-04-07 12:08:01 +04:00
{
iowrite16be ( reg , & mod - > regs - > control ) ;
iowrite16be ( val , & mod - > regs - > control ) ;
}
2012-11-19 22:22:34 +04:00
static void ttl_setup_device ( struct ttl_module * mod )
2010-04-07 12:08:01 +04:00
{
/* reset the device to a known state */
iowrite16be ( 0x0000 , & mod - > regs - > control ) ;
iowrite16be ( 0x0001 , & mod - > regs - > control ) ;
iowrite16be ( 0x0000 , & mod - > regs - > control ) ;
/* put all ports in open-drain mode */
ttl_write_reg ( mod , PORTA_IOCTL , 0x00ff ) ;
ttl_write_reg ( mod , PORTB_IOCTL , 0x00ff ) ;
ttl_write_reg ( mod , PORTC_IOCTL , 0x000f ) ;
/* set all ports as outputs */
ttl_write_reg ( mod , PORTA_DIRECTION , 0x0000 ) ;
ttl_write_reg ( mod , PORTB_DIRECTION , 0x0000 ) ;
ttl_write_reg ( mod , PORTC_DIRECTION , 0x0000 ) ;
/* set all ports to drive zeroes */
iowrite16be ( 0x0000 , & mod - > regs - > porta ) ;
iowrite16be ( 0x0000 , & mod - > regs - > portb ) ;
iowrite16be ( 0x0000 , & mod - > regs - > portc ) ;
/* enable all ports */
ttl_write_reg ( mod , MASTER_CONF_CTL , CONF_PAE | CONF_PBE | CONF_PCE ) ;
}
2012-11-19 22:22:34 +04:00
static int ttl_probe ( struct platform_device * pdev )
2010-04-07 12:08:01 +04:00
{
struct janz_platform_data * pdata ;
struct ttl_module * mod ;
struct gpio_chip * gpio ;
int ret ;
2013-07-30 12:08:05 +04:00
pdata = dev_get_platdata ( & pdev - > dev ) ;
2010-04-07 12:08:01 +04:00
if ( ! pdata ) {
2019-06-17 19:49:19 +03:00
dev_err ( & pdev - > dev , " no platform data \n " ) ;
2014-05-13 20:42:12 +04:00
return - ENXIO ;
2010-04-07 12:08:01 +04:00
}
2019-06-17 19:49:19 +03:00
mod = devm_kzalloc ( & pdev - > dev , sizeof ( * mod ) , GFP_KERNEL ) ;
2014-05-13 20:42:12 +04:00
if ( ! mod )
return - ENOMEM ;
2010-04-07 12:08:01 +04:00
platform_set_drvdata ( pdev , mod ) ;
spin_lock_init ( & mod - > lock ) ;
/* get access to the MODULbus registers for this module */
2019-03-11 21:54:54 +03:00
mod - > regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
2014-05-13 20:42:12 +04:00
if ( IS_ERR ( mod - > regs ) )
return PTR_ERR ( mod - > regs ) ;
2010-04-07 12:08:01 +04:00
ttl_setup_device ( mod ) ;
/* Initialize the GPIO data structures */
gpio = & mod - > gpio ;
2015-11-04 11:56:26 +03:00
gpio - > parent = & pdev - > dev ;
2010-04-07 12:08:01 +04:00
gpio - > label = pdev - > name ;
gpio - > get = ttl_get_value ;
gpio - > set = ttl_set_value ;
gpio - > owner = THIS_MODULE ;
/* request dynamic allocation */
gpio - > base = - 1 ;
gpio - > ngpio = 20 ;
2019-06-17 19:49:19 +03:00
ret = devm_gpiochip_add_data ( & pdev - > dev , gpio , NULL ) ;
2010-04-07 12:08:01 +04:00
if ( ret ) {
2019-06-17 19:49:19 +03:00
dev_err ( & pdev - > dev , " unable to add GPIO chip \n " ) ;
2014-05-13 20:42:12 +04:00
return ret ;
2010-04-07 12:08:01 +04:00
}
return 0 ;
}
static struct platform_driver ttl_driver = {
. driver = {
. name = DRV_NAME ,
} ,
. probe = ttl_probe ,
} ;
2011-12-07 20:24:00 +04:00
module_platform_driver ( ttl_driver ) ;
2010-04-07 12:08:01 +04:00
MODULE_AUTHOR ( " Ira W. Snyder <iws@ovro.caltech.edu> " ) ;
MODULE_DESCRIPTION ( " Janz MODULbus VMOD-TTL Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:janz-ttl " ) ;