2012-10-23 09:48:08 +04:00
/*
* Copyright ( C ) 2004 , 2007 - 2010 , 2011 - 2012 Synopsys , Inc . ( www . synopsys . 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 .
*
* Driver is originally developed by Pavel Sokolov < psokolov @ synopsys . com >
*/
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/input.h>
# include <linux/serio.h>
# include <linux/platform_device.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# define ARC_PS2_PORTS 2
# define ARC_ARC_PS2_ID 0x0001f609
# define STAT_TIMEOUT 128
# define PS2_STAT_RX_FRM_ERR (1)
# define PS2_STAT_RX_BUF_OVER (1 << 1)
# define PS2_STAT_RX_INT_EN (1 << 2)
# define PS2_STAT_RX_VAL (1 << 3)
# define PS2_STAT_TX_ISNOT_FUL (1 << 4)
# define PS2_STAT_TX_INT_EN (1 << 5)
struct arc_ps2_port {
void __iomem * data_addr ;
void __iomem * status_addr ;
struct serio * io ;
} ;
struct arc_ps2_data {
struct arc_ps2_port port [ ARC_PS2_PORTS ] ;
void __iomem * addr ;
unsigned int frame_error ;
unsigned int buf_overflow ;
unsigned int total_int ;
} ;
static void arc_ps2_check_rx ( struct arc_ps2_data * arc_ps2 ,
struct arc_ps2_port * port )
{
unsigned int timeout = 1000 ;
unsigned int flag , status ;
unsigned char data ;
do {
status = ioread32 ( port - > status_addr ) ;
if ( ! ( status & PS2_STAT_RX_VAL ) )
return ;
data = ioread32 ( port - > data_addr ) & 0xff ;
flag = 0 ;
arc_ps2 - > total_int + + ;
if ( status & PS2_STAT_RX_FRM_ERR ) {
arc_ps2 - > frame_error + + ;
flag | = SERIO_PARITY ;
} else if ( status & PS2_STAT_RX_BUF_OVER ) {
arc_ps2 - > buf_overflow + + ;
flag | = SERIO_FRAME ;
}
serio_interrupt ( port - > io , data , flag ) ;
} while ( - - timeout ) ;
dev_err ( & port - > io - > dev , " PS/2 hardware stuck \n " ) ;
}
static irqreturn_t arc_ps2_interrupt ( int irq , void * dev )
{
struct arc_ps2_data * arc_ps2 = dev ;
int i ;
for ( i = 0 ; i < ARC_PS2_PORTS ; i + + )
arc_ps2_check_rx ( arc_ps2 , & arc_ps2 - > port [ i ] ) ;
return IRQ_HANDLED ;
}
static int arc_ps2_write ( struct serio * io , unsigned char val )
{
unsigned status ;
struct arc_ps2_port * port = io - > port_data ;
int timeout = STAT_TIMEOUT ;
do {
status = ioread32 ( port - > status_addr ) ;
cpu_relax ( ) ;
if ( status & PS2_STAT_TX_ISNOT_FUL ) {
iowrite32 ( val & 0xff , port - > data_addr ) ;
return 0 ;
}
} while ( - - timeout ) ;
dev_err ( & io - > dev , " write timeout \n " ) ;
return - ETIMEDOUT ;
}
static int arc_ps2_open ( struct serio * io )
{
struct arc_ps2_port * port = io - > port_data ;
iowrite32 ( PS2_STAT_RX_INT_EN , port - > status_addr ) ;
return 0 ;
}
static void arc_ps2_close ( struct serio * io )
{
struct arc_ps2_port * port = io - > port_data ;
iowrite32 ( ioread32 ( port - > status_addr ) & ~ PS2_STAT_RX_INT_EN ,
port - > status_addr ) ;
}
2012-10-25 10:53:01 +04:00
static void __iomem * __devinit arc_ps2_calc_addr ( struct arc_ps2_data * arc_ps2 ,
int index , bool status )
{
void __iomem * addr ;
addr = arc_ps2 - > addr + 4 + 4 * index ;
if ( status )
addr + = ARC_PS2_PORTS * 4 ;
return addr ;
}
static void __devinit arc_ps2_inhibit_ports ( struct arc_ps2_data * arc_ps2 )
{
void __iomem * addr ;
u32 val ;
int i ;
for ( i = 0 ; i < ARC_PS2_PORTS ; i + + ) {
addr = arc_ps2_calc_addr ( arc_ps2 , i , true ) ;
val = ioread32 ( addr ) ;
val & = ~ ( PS2_STAT_RX_INT_EN | PS2_STAT_TX_INT_EN ) ;
iowrite32 ( val , addr ) ;
}
}
2012-10-23 09:48:08 +04:00
static int __devinit arc_ps2_create_port ( struct platform_device * pdev ,
struct arc_ps2_data * arc_ps2 ,
int index )
{
struct arc_ps2_port * port = & arc_ps2 - > port [ index ] ;
struct serio * io ;
io = kzalloc ( sizeof ( struct serio ) , GFP_KERNEL ) ;
if ( ! io )
return - ENOMEM ;
io - > id . type = SERIO_8042 ;
io - > write = arc_ps2_write ;
io - > open = arc_ps2_open ;
io - > close = arc_ps2_close ;
snprintf ( io - > name , sizeof ( io - > name ) , " ARC PS/2 port%d " , index ) ;
snprintf ( io - > phys , sizeof ( io - > phys ) , " arc/serio%d " , index ) ;
io - > port_data = port ;
port - > io = io ;
2012-10-25 10:53:01 +04:00
port - > data_addr = arc_ps2_calc_addr ( arc_ps2 , index , false ) ;
port - > status_addr = arc_ps2_calc_addr ( arc_ps2 , index , true ) ;
2012-10-23 09:48:08 +04:00
dev_dbg ( & pdev - > dev , " port%d is allocated (data = 0x%p, status = 0x%p) \n " ,
index , port - > data_addr , port - > status_addr ) ;
serio_register_port ( port - > io ) ;
return 0 ;
}
static int __devinit arc_ps2_probe ( struct platform_device * pdev )
{
struct arc_ps2_data * arc_ps2 ;
2012-10-25 10:53:01 +04:00
struct resource * res ;
int irq ;
2012-10-23 09:48:08 +04:00
int error , id , i ;
2012-10-25 10:53:01 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
2012-10-23 09:48:08 +04:00
dev_err ( & pdev - > dev , " no IO memory defined \n " ) ;
2012-10-25 10:53:01 +04:00
return - EINVAL ;
2012-10-23 09:48:08 +04:00
}
2012-10-25 10:53:01 +04:00
irq = platform_get_irq_byname ( pdev , " arc_ps2_irq " ) ;
if ( irq < 0 ) {
2012-10-23 09:48:08 +04:00
dev_err ( & pdev - > dev , " no IRQ defined \n " ) ;
2012-10-25 10:53:01 +04:00
return - EINVAL ;
2012-10-23 09:48:08 +04:00
}
2012-10-25 10:53:01 +04:00
arc_ps2 = devm_kzalloc ( & pdev - > dev , sizeof ( struct arc_ps2_data ) ,
GFP_KERNEL ) ;
if ( ! arc_ps2 ) {
dev_err ( & pdev - > dev , " out of memory \n " ) ;
return - ENOMEM ;
2012-10-23 09:48:08 +04:00
}
2012-10-25 10:53:01 +04:00
arc_ps2 - > addr = devm_request_and_ioremap ( & pdev - > dev , res ) ;
if ( ! arc_ps2 - > addr )
return - EBUSY ;
2012-10-23 09:48:08 +04:00
dev_info ( & pdev - > dev , " irq = %d, address = 0x%p, ports = %i \n " ,
2012-10-25 10:53:01 +04:00
irq , arc_ps2 - > addr , ARC_PS2_PORTS ) ;
2012-10-23 09:48:08 +04:00
id = ioread32 ( arc_ps2 - > addr ) ;
if ( id ! = ARC_ARC_PS2_ID ) {
dev_err ( & pdev - > dev , " device id does not match \n " ) ;
2012-10-25 10:53:01 +04:00
return - ENXIO ;
2012-10-23 09:48:08 +04:00
}
2012-10-25 10:53:01 +04:00
arc_ps2_inhibit_ports ( arc_ps2 ) ;
2012-10-23 09:48:08 +04:00
2012-10-25 10:53:01 +04:00
error = devm_request_irq ( & pdev - > dev , irq , arc_ps2_interrupt ,
0 , " arc_ps2 " , arc_ps2 ) ;
2012-10-23 09:48:08 +04:00
if ( error ) {
dev_err ( & pdev - > dev , " Could not allocate IRQ \n " ) ;
2012-10-25 10:53:01 +04:00
return error ;
}
for ( i = 0 ; i < ARC_PS2_PORTS ; i + + ) {
error = arc_ps2_create_port ( pdev , arc_ps2 , i ) ;
if ( error ) {
while ( - - i > = 0 )
serio_unregister_port ( arc_ps2 - > port [ i ] . io ) ;
return error ;
}
2012-10-23 09:48:08 +04:00
}
platform_set_drvdata ( pdev , arc_ps2 ) ;
return 0 ;
}
static int __devexit arc_ps2_remove ( struct platform_device * pdev )
{
struct arc_ps2_data * arc_ps2 = platform_get_drvdata ( pdev ) ;
int i ;
for ( i = 0 ; i < ARC_PS2_PORTS ; i + + )
serio_unregister_port ( arc_ps2 - > port [ i ] . io ) ;
dev_dbg ( & pdev - > dev , " interrupt count = %i \n " , arc_ps2 - > total_int ) ;
dev_dbg ( & pdev - > dev , " frame error count = %i \n " , arc_ps2 - > frame_error ) ;
dev_dbg ( & pdev - > dev , " buffer overflow count = %i \n " ,
arc_ps2 - > buf_overflow ) ;
return 0 ;
}
static struct platform_driver arc_ps2_driver = {
. driver = {
. name = " arc_ps2 " ,
. owner = THIS_MODULE ,
} ,
. probe = arc_ps2_probe ,
2012-11-24 09:27:39 +04:00
. remove = arc_ps2_remove ,
2012-10-23 09:48:08 +04:00
} ;
module_platform_driver ( arc_ps2_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Pavel Sokolov <psokolov@synopsys.com> " ) ;
MODULE_DESCRIPTION ( " ARC PS/2 Driver " ) ;