2005-04-17 02:20:36 +04:00
/*
* Driver for TANBAC TB0219 base board .
*
* Copyright ( C ) 2005 Yoichi Yuasa < yuasa @ hh . iij4u . or . jp >
*
* 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 .
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
# include <linux/fs.h>
# include <linux/init.h>
# include <linux/module.h>
# include <asm/io.h>
# include <asm/reboot.h>
2005-07-13 00:58:32 +04:00
# include <asm/vr41xx/giu.h>
# include <asm/vr41xx/tb0219.h>
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Yoichi Yuasa <yuasa@hh.iij4u.or.jp> " ) ;
MODULE_DESCRIPTION ( " TANBAC TB0219 base board driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int major ; /* default is dynamic major device number */
module_param ( major , int , 0 ) ;
MODULE_PARM_DESC ( major , " Major device number " ) ;
static void ( * old_machine_restart ) ( char * command ) ;
static void __iomem * tb0219_base ;
static spinlock_t tb0219_lock ;
# define tb0219_read(offset) readw(tb0219_base + (offset))
# define tb0219_write(offset, value) writew((value), tb0219_base + (offset))
# define TB0219_START 0x0a000000UL
# define TB0219_SIZE 0x20UL
# define TB0219_LED 0x00
# define TB0219_GPIO_INPUT 0x02
# define TB0219_GPIO_OUTPUT 0x04
# define TB0219_DIP_SWITCH 0x06
# define TB0219_MISC 0x08
# define TB0219_RESET 0x0e
# define TB0219_PCI_SLOT1_IRQ_STATUS 0x10
# define TB0219_PCI_SLOT2_IRQ_STATUS 0x12
# define TB0219_PCI_SLOT3_IRQ_STATUS 0x14
typedef enum {
TYPE_LED ,
TYPE_GPIO_OUTPUT ,
} tb0219_type_t ;
/*
* Minor device number
* 0 = 7 segment LED
*
* 16 = GPIO IN 0
* 17 = GPIO IN 1
* 18 = GPIO IN 2
* 19 = GPIO IN 3
* 20 = GPIO IN 4
* 21 = GPIO IN 5
* 22 = GPIO IN 6
* 23 = GPIO IN 7
*
* 32 = GPIO OUT 0
* 33 = GPIO OUT 1
* 34 = GPIO OUT 2
* 35 = GPIO OUT 3
* 36 = GPIO OUT 4
* 37 = GPIO OUT 5
* 38 = GPIO OUT 6
* 39 = GPIO OUT 7
*
* 48 = DIP switch 1
* 49 = DIP switch 2
* 50 = DIP switch 3
* 51 = DIP switch 4
* 52 = DIP switch 5
* 53 = DIP switch 6
* 54 = DIP switch 7
* 55 = DIP switch 8
*/
static inline char get_led ( void )
{
return ( char ) tb0219_read ( TB0219_LED ) ;
}
static inline char get_gpio_input_pin ( unsigned int pin )
{
uint16_t values ;
values = tb0219_read ( TB0219_GPIO_INPUT ) ;
if ( values & ( 1 < < pin ) )
return ' 1 ' ;
return ' 0 ' ;
}
static inline char get_gpio_output_pin ( unsigned int pin )
{
uint16_t values ;
values = tb0219_read ( TB0219_GPIO_OUTPUT ) ;
if ( values & ( 1 < < pin ) )
return ' 1 ' ;
return ' 0 ' ;
}
static inline char get_dip_switch ( unsigned int pin )
{
uint16_t values ;
values = tb0219_read ( TB0219_DIP_SWITCH ) ;
if ( values & ( 1 < < pin ) )
return ' 1 ' ;
return ' 0 ' ;
}
static inline int set_led ( char command )
{
tb0219_write ( TB0219_LED , command ) ;
return 0 ;
}
static inline int set_gpio_output_pin ( unsigned int pin , char command )
{
unsigned long flags ;
uint16_t value ;
if ( command ! = ' 0 ' & & command ! = ' 1 ' )
return - EINVAL ;
spin_lock_irqsave ( & tb0219_lock , flags ) ;
value = tb0219_read ( TB0219_GPIO_OUTPUT ) ;
if ( command = = ' 0 ' )
value & = ~ ( 1 < < pin ) ;
else
value | = 1 < < pin ;
tb0219_write ( TB0219_GPIO_OUTPUT , value ) ;
spin_unlock_irqrestore ( & tb0219_lock , flags ) ;
return 0 ;
}
static ssize_t tanbac_tb0219_read ( struct file * file , char __user * buf , size_t len ,
loff_t * ppos )
{
unsigned int minor ;
char value ;
minor = iminor ( file - > f_dentry - > d_inode ) ;
switch ( minor ) {
case 0 :
value = get_led ( ) ;
break ;
case 16 . . . 23 :
value = get_gpio_input_pin ( minor - 16 ) ;
break ;
case 32 . . . 39 :
value = get_gpio_output_pin ( minor - 32 ) ;
break ;
case 48 . . . 55 :
value = get_dip_switch ( minor - 48 ) ;
break ;
default :
return - EBADF ;
}
if ( len < = 0 )
return - EFAULT ;
if ( put_user ( value , buf ) )
return - EFAULT ;
return 1 ;
}
static ssize_t tanbac_tb0219_write ( struct file * file , const char __user * data ,
size_t len , loff_t * ppos )
{
unsigned int minor ;
tb0219_type_t type ;
size_t i ;
int retval = 0 ;
char c ;
minor = iminor ( file - > f_dentry - > d_inode ) ;
switch ( minor ) {
case 0 :
type = TYPE_LED ;
break ;
case 32 . . . 39 :
type = TYPE_GPIO_OUTPUT ;
break ;
default :
return - EBADF ;
}
for ( i = 0 ; i < len ; i + + ) {
if ( get_user ( c , data + i ) )
return - EFAULT ;
switch ( type ) {
case TYPE_LED :
retval = set_led ( c ) ;
break ;
case TYPE_GPIO_OUTPUT :
retval = set_gpio_output_pin ( minor - 32 , c ) ;
break ;
}
if ( retval < 0 )
break ;
}
return i ;
}
static int tanbac_tb0219_open ( struct inode * inode , struct file * file )
{
unsigned int minor ;
minor = iminor ( inode ) ;
switch ( minor ) {
case 0 :
case 16 . . . 23 :
case 32 . . . 39 :
case 48 . . . 55 :
return nonseekable_open ( inode , file ) ;
default :
break ;
}
return - EBADF ;
}
static int tanbac_tb0219_release ( struct inode * inode , struct file * file )
{
return 0 ;
}
static struct file_operations tb0219_fops = {
. owner = THIS_MODULE ,
. read = tanbac_tb0219_read ,
. write = tanbac_tb0219_write ,
. open = tanbac_tb0219_open ,
. release = tanbac_tb0219_release ,
} ;
static void tb0219_restart ( char * command )
{
tb0219_write ( TB0219_RESET , 0 ) ;
}
2005-07-13 00:58:32 +04:00
static void tb0219_pci_irq_init ( void )
{
/* PCI Slot 1 */
vr41xx_set_irq_trigger ( TB0219_PCI_SLOT1_PIN , IRQ_TRIGGER_LEVEL , IRQ_SIGNAL_THROUGH ) ;
vr41xx_set_irq_level ( TB0219_PCI_SLOT1_PIN , IRQ_LEVEL_LOW ) ;
/* PCI Slot 2 */
vr41xx_set_irq_trigger ( TB0219_PCI_SLOT2_PIN , IRQ_TRIGGER_LEVEL , IRQ_SIGNAL_THROUGH ) ;
vr41xx_set_irq_level ( TB0219_PCI_SLOT2_PIN , IRQ_LEVEL_LOW ) ;
/* PCI Slot 3 */
vr41xx_set_irq_trigger ( TB0219_PCI_SLOT3_PIN , IRQ_TRIGGER_LEVEL , IRQ_SIGNAL_THROUGH ) ;
vr41xx_set_irq_level ( TB0219_PCI_SLOT3_PIN , IRQ_LEVEL_LOW ) ;
}
2005-11-10 01:32:44 +03:00
static int tb0219_probe ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
int retval ;
if ( request_mem_region ( TB0219_START , TB0219_SIZE , " TB0219 " ) = = NULL )
return - EBUSY ;
tb0219_base = ioremap ( TB0219_START , TB0219_SIZE ) ;
if ( tb0219_base = = NULL ) {
release_mem_region ( TB0219_START , TB0219_SIZE ) ;
return - ENOMEM ;
}
retval = register_chrdev ( major , " TB0219 " , & tb0219_fops ) ;
if ( retval < 0 ) {
iounmap ( tb0219_base ) ;
tb0219_base = NULL ;
release_mem_region ( TB0219_START , TB0219_SIZE ) ;
return retval ;
}
spin_lock_init ( & tb0219_lock ) ;
old_machine_restart = _machine_restart ;
_machine_restart = tb0219_restart ;
2005-07-13 00:58:32 +04:00
tb0219_pci_irq_init ( ) ;
2005-04-17 02:20:36 +04:00
if ( major = = 0 ) {
major = retval ;
printk ( KERN_INFO " TB0219: major number %d \n " , major ) ;
}
return 0 ;
}
2005-11-10 01:32:44 +03:00
static int tb0219_remove ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
_machine_restart = old_machine_restart ;
iounmap ( tb0219_base ) ;
tb0219_base = NULL ;
release_mem_region ( TB0219_START , TB0219_SIZE ) ;
return 0 ;
}
static struct platform_device * tb0219_platform_device ;
2005-11-10 01:32:44 +03:00
static struct platform_driver tb0219_device_driver = {
2005-04-17 02:20:36 +04:00
. probe = tb0219_probe ,
. remove = tb0219_remove ,
2005-11-10 01:32:44 +03:00
. driver = {
. name = " TB0219 " ,
} ,
2005-04-17 02:20:36 +04:00
} ;
static int __devinit tanbac_tb0219_init ( void )
{
int retval ;
tb0219_platform_device = platform_device_register_simple ( " TB0219 " , - 1 , NULL , 0 ) ;
if ( IS_ERR ( tb0219_platform_device ) )
return PTR_ERR ( tb0219_platform_device ) ;
2005-11-10 01:32:44 +03:00
retval = platform_driver_register ( & tb0219_device_driver ) ;
2005-04-17 02:20:36 +04:00
if ( retval < 0 )
platform_device_unregister ( tb0219_platform_device ) ;
return retval ;
}
static void __devexit tanbac_tb0219_exit ( void )
{
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & tb0219_device_driver ) ;
2005-04-17 02:20:36 +04:00
platform_device_unregister ( tb0219_platform_device ) ;
}
module_init ( tanbac_tb0219_init ) ;
module_exit ( tanbac_tb0219_exit ) ;