2009-06-17 02:33:53 +04:00
/*
* Copyright ( C ) 2006 , 2007 Eugene Konev < ejka @ openwrt . org >
*
* 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 . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*
* Parts of the VLYNQ specification can be found here :
* http : //www.ti.com/litv/pdf/sprue36a
*/
# include <linux/init.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/device.h>
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/device.h>
# include <linux/delay.h>
# include <linux/io.h>
# include <linux/vlynq.h>
# define VLYNQ_CTRL_PM_ENABLE 0x80000000
# define VLYNQ_CTRL_CLOCK_INT 0x00008000
# define VLYNQ_CTRL_CLOCK_DIV(x) (((x) & 7) << 16)
# define VLYNQ_CTRL_INT_LOCAL 0x00004000
# define VLYNQ_CTRL_INT_ENABLE 0x00002000
# define VLYNQ_CTRL_INT_VECTOR(x) (((x) & 0x1f) << 8)
# define VLYNQ_CTRL_INT2CFG 0x00000080
# define VLYNQ_CTRL_RESET 0x00000001
# define VLYNQ_CTRL_CLOCK_MASK (0x7 << 16)
# define VLYNQ_INT_OFFSET 0x00000014
# define VLYNQ_REMOTE_OFFSET 0x00000080
# define VLYNQ_STATUS_LINK 0x00000001
# define VLYNQ_STATUS_LERROR 0x00000080
# define VLYNQ_STATUS_RERROR 0x00000100
# define VINT_ENABLE 0x00000100
# define VINT_TYPE_EDGE 0x00000080
# define VINT_LEVEL_LOW 0x00000040
# define VINT_VECTOR(x) ((x) & 0x1f)
# define VINT_OFFSET(irq) (8 * ((irq) % 4))
# define VLYNQ_AUTONEGO_V2 0x00010000
struct vlynq_regs {
u32 revision ;
u32 control ;
u32 status ;
u32 int_prio ;
u32 int_status ;
u32 int_pending ;
u32 int_ptr ;
u32 tx_offset ;
struct vlynq_mapping rx_mapping [ 4 ] ;
u32 chip ;
u32 autonego ;
u32 unused [ 6 ] ;
u32 int_device [ 8 ] ;
} ;
2009-07-05 23:08:00 +04:00
# ifdef CONFIG_VLYNQ_DEBUG
2009-06-17 02:33:53 +04:00
static void vlynq_dump_regs ( struct vlynq_device * dev )
{
int i ;
printk ( KERN_DEBUG " VLYNQ local=%p remote=%p \n " ,
dev - > local , dev - > remote ) ;
for ( i = 0 ; i < 32 ; i + + ) {
printk ( KERN_DEBUG " VLYNQ: local %d: %08x \n " ,
i + 1 , ( ( u32 * ) dev - > local ) [ i ] ) ;
printk ( KERN_DEBUG " VLYNQ: remote %d: %08x \n " ,
i + 1 , ( ( u32 * ) dev - > remote ) [ i ] ) ;
}
}
static void vlynq_dump_mem ( u32 * base , int count )
{
int i ;
for ( i = 0 ; i < ( count + 3 ) / 4 ; i + + ) {
if ( i % 4 = = 0 )
printk ( KERN_DEBUG " \n MEM[0x%04x]: " , i * 4 ) ;
printk ( KERN_DEBUG " 0x%08x " , * ( base + i ) ) ;
}
printk ( KERN_DEBUG " \n " ) ;
}
# endif
/* Check the VLYNQ link status with a given device */
static int vlynq_linked ( struct vlynq_device * dev )
{
int i ;
for ( i = 0 ; i < 100 ; i + + )
if ( readl ( & dev - > local - > status ) & VLYNQ_STATUS_LINK )
return 1 ;
else
cpu_relax ( ) ;
return 0 ;
}
static void vlynq_reset ( struct vlynq_device * dev )
{
writel ( readl ( & dev - > local - > control ) | VLYNQ_CTRL_RESET ,
& dev - > local - > control ) ;
/* Wait for the devices to finish resetting */
msleep ( 5 ) ;
/* Remove reset bit */
writel ( readl ( & dev - > local - > control ) & ~ VLYNQ_CTRL_RESET ,
& dev - > local - > control ) ;
/* Give some time for the devices to settle */
msleep ( 5 ) ;
}
static void vlynq_irq_unmask ( unsigned int irq )
{
u32 val ;
struct vlynq_device * dev = get_irq_chip_data ( irq ) ;
int virq ;
BUG_ON ( ! dev ) ;
virq = irq - dev - > irq_start ;
val = readl ( & dev - > remote - > int_device [ virq > > 2 ] ) ;
val | = ( VINT_ENABLE | virq ) < < VINT_OFFSET ( virq ) ;
writel ( val , & dev - > remote - > int_device [ virq > > 2 ] ) ;
}
static void vlynq_irq_mask ( unsigned int irq )
{
u32 val ;
struct vlynq_device * dev = get_irq_chip_data ( irq ) ;
int virq ;
BUG_ON ( ! dev ) ;
virq = irq - dev - > irq_start ;
val = readl ( & dev - > remote - > int_device [ virq > > 2 ] ) ;
val & = ~ ( VINT_ENABLE < < VINT_OFFSET ( virq ) ) ;
writel ( val , & dev - > remote - > int_device [ virq > > 2 ] ) ;
}
static int vlynq_irq_type ( unsigned int irq , unsigned int flow_type )
{
u32 val ;
struct vlynq_device * dev = get_irq_chip_data ( irq ) ;
int virq ;
BUG_ON ( ! dev ) ;
virq = irq - dev - > irq_start ;
val = readl ( & dev - > remote - > int_device [ virq > > 2 ] ) ;
switch ( flow_type & IRQ_TYPE_SENSE_MASK ) {
case IRQ_TYPE_EDGE_RISING :
case IRQ_TYPE_EDGE_FALLING :
case IRQ_TYPE_EDGE_BOTH :
val | = VINT_TYPE_EDGE < < VINT_OFFSET ( virq ) ;
val & = ~ ( VINT_LEVEL_LOW < < VINT_OFFSET ( virq ) ) ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
val & = ~ ( VINT_TYPE_EDGE < < VINT_OFFSET ( virq ) ) ;
val & = ~ ( VINT_LEVEL_LOW < < VINT_OFFSET ( virq ) ) ;
break ;
case IRQ_TYPE_LEVEL_LOW :
val & = ~ ( VINT_TYPE_EDGE < < VINT_OFFSET ( virq ) ) ;
val | = VINT_LEVEL_LOW < < VINT_OFFSET ( virq ) ;
break ;
default :
return - EINVAL ;
}
writel ( val , & dev - > remote - > int_device [ virq > > 2 ] ) ;
return 0 ;
}
static void vlynq_local_ack ( unsigned int irq )
{
struct vlynq_device * dev = get_irq_chip_data ( irq ) ;
u32 status = readl ( & dev - > local - > status ) ;
pr_debug ( " %s: local status: 0x%08x \n " ,
dev_name ( & dev - > dev ) , status ) ;
writel ( status , & dev - > local - > status ) ;
}
static void vlynq_remote_ack ( unsigned int irq )
{
struct vlynq_device * dev = get_irq_chip_data ( irq ) ;
u32 status = readl ( & dev - > remote - > status ) ;
pr_debug ( " %s: remote status: 0x%08x \n " ,
dev_name ( & dev - > dev ) , status ) ;
writel ( status , & dev - > remote - > status ) ;
}
static irqreturn_t vlynq_irq ( int irq , void * dev_id )
{
struct vlynq_device * dev = dev_id ;
u32 status ;
int virq = 0 ;
status = readl ( & dev - > local - > int_status ) ;
writel ( status , & dev - > local - > int_status ) ;
if ( unlikely ( ! status ) )
spurious_interrupt ( ) ;
while ( status ) {
if ( status & 1 )
do_IRQ ( dev - > irq_start + virq ) ;
status > > = 1 ;
virq + + ;
}
return IRQ_HANDLED ;
}
static struct irq_chip vlynq_irq_chip = {
. name = " vlynq " ,
. unmask = vlynq_irq_unmask ,
. mask = vlynq_irq_mask ,
. set_type = vlynq_irq_type ,
} ;
static struct irq_chip vlynq_local_chip = {
. name = " vlynq local error " ,
. unmask = vlynq_irq_unmask ,
. mask = vlynq_irq_mask ,
. ack = vlynq_local_ack ,
} ;
static struct irq_chip vlynq_remote_chip = {
. name = " vlynq local error " ,
. unmask = vlynq_irq_unmask ,
. mask = vlynq_irq_mask ,
. ack = vlynq_remote_ack ,
} ;
static int vlynq_setup_irq ( struct vlynq_device * dev )
{
u32 val ;
int i , virq ;
if ( dev - > local_irq = = dev - > remote_irq ) {
printk ( KERN_ERR
" %s: local vlynq irq should be different from remote \n " ,
dev_name ( & dev - > dev ) ) ;
return - EINVAL ;
}
/* Clear local and remote error bits */
writel ( readl ( & dev - > local - > status ) , & dev - > local - > status ) ;
writel ( readl ( & dev - > remote - > status ) , & dev - > remote - > status ) ;
/* Now setup interrupts */
val = VLYNQ_CTRL_INT_VECTOR ( dev - > local_irq ) ;
val | = VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL |
VLYNQ_CTRL_INT2CFG ;
val | = readl ( & dev - > local - > control ) ;
writel ( VLYNQ_INT_OFFSET , & dev - > local - > int_ptr ) ;
writel ( val , & dev - > local - > control ) ;
val = VLYNQ_CTRL_INT_VECTOR ( dev - > remote_irq ) ;
val | = VLYNQ_CTRL_INT_ENABLE ;
val | = readl ( & dev - > remote - > control ) ;
writel ( VLYNQ_INT_OFFSET , & dev - > remote - > int_ptr ) ;
writel ( val , & dev - > remote - > int_ptr ) ;
writel ( val , & dev - > remote - > control ) ;
for ( i = dev - > irq_start ; i < = dev - > irq_end ; i + + ) {
virq = i - dev - > irq_start ;
if ( virq = = dev - > local_irq ) {
set_irq_chip_and_handler ( i , & vlynq_local_chip ,
handle_level_irq ) ;
set_irq_chip_data ( i , dev ) ;
} else if ( virq = = dev - > remote_irq ) {
set_irq_chip_and_handler ( i , & vlynq_remote_chip ,
handle_level_irq ) ;
set_irq_chip_data ( i , dev ) ;
} else {
set_irq_chip_and_handler ( i , & vlynq_irq_chip ,
handle_simple_irq ) ;
set_irq_chip_data ( i , dev ) ;
writel ( 0 , & dev - > remote - > int_device [ virq > > 2 ] ) ;
}
}
if ( request_irq ( dev - > irq , vlynq_irq , IRQF_SHARED , " vlynq " , dev ) ) {
printk ( KERN_ERR " %s: request_irq failed \n " ,
dev_name ( & dev - > dev ) ) ;
return - EAGAIN ;
}
return 0 ;
}
static void vlynq_device_release ( struct device * dev )
{
struct vlynq_device * vdev = to_vlynq_device ( dev ) ;
kfree ( vdev ) ;
}
static int vlynq_device_match ( struct device * dev ,
struct device_driver * drv )
{
struct vlynq_device * vdev = to_vlynq_device ( dev ) ;
struct vlynq_driver * vdrv = to_vlynq_driver ( drv ) ;
struct vlynq_device_id * ids = vdrv - > id_table ;
while ( ids - > id ) {
if ( ids - > id = = vdev - > dev_id ) {
vdev - > divisor = ids - > divisor ;
vlynq_set_drvdata ( vdev , ids ) ;
printk ( KERN_INFO " Driver found for VLYNQ "
" device: %08x \n " , vdev - > dev_id ) ;
return 1 ;
}
printk ( KERN_DEBUG " Not using the %08x VLYNQ device's driver "
" for VLYNQ device: %08x \n " , ids - > id , vdev - > dev_id ) ;
ids + + ;
}
return 0 ;
}
static int vlynq_device_probe ( struct device * dev )
{
struct vlynq_device * vdev = to_vlynq_device ( dev ) ;
struct vlynq_driver * drv = to_vlynq_driver ( dev - > driver ) ;
struct vlynq_device_id * id = vlynq_get_drvdata ( vdev ) ;
int result = - ENODEV ;
if ( drv - > probe )
result = drv - > probe ( vdev , id ) ;
if ( result )
put_device ( dev ) ;
return result ;
}
static int vlynq_device_remove ( struct device * dev )
{
struct vlynq_driver * drv = to_vlynq_driver ( dev - > driver ) ;
if ( drv - > remove )
drv - > remove ( to_vlynq_device ( dev ) ) ;
return 0 ;
}
int __vlynq_register_driver ( struct vlynq_driver * driver , struct module * owner )
{
driver - > driver . name = driver - > name ;
driver - > driver . bus = & vlynq_bus_type ;
return driver_register ( & driver - > driver ) ;
}
EXPORT_SYMBOL ( __vlynq_register_driver ) ;
void vlynq_unregister_driver ( struct vlynq_driver * driver )
{
driver_unregister ( & driver - > driver ) ;
}
EXPORT_SYMBOL ( vlynq_unregister_driver ) ;
/*
* A VLYNQ remote device can clock the VLYNQ bus master
* using a dedicated clock line . In that case , both the
* remove device and the bus master should have the same
* serial clock dividers configured . Iterate through the
* 8 possible dividers until we actually link with the
* device .
*/
static int __vlynq_try_remote ( struct vlynq_device * dev )
{
int i ;
vlynq_reset ( dev ) ;
for ( i = dev - > dev_id ? vlynq_rdiv2 : vlynq_rdiv8 ; dev - > dev_id ?
i < = vlynq_rdiv8 : i > = vlynq_rdiv2 ;
dev - > dev_id ? i + + : i - - ) {
if ( ! vlynq_linked ( dev ) )
break ;
writel ( ( readl ( & dev - > remote - > control ) &
~ VLYNQ_CTRL_CLOCK_MASK ) |
VLYNQ_CTRL_CLOCK_INT |
VLYNQ_CTRL_CLOCK_DIV ( i - vlynq_rdiv1 ) ,
& dev - > remote - > control ) ;
writel ( ( readl ( & dev - > local - > control )
& ~ ( VLYNQ_CTRL_CLOCK_INT |
VLYNQ_CTRL_CLOCK_MASK ) ) |
VLYNQ_CTRL_CLOCK_DIV ( i - vlynq_rdiv1 ) ,
& dev - > local - > control ) ;
if ( vlynq_linked ( dev ) ) {
printk ( KERN_DEBUG
" %s: using remote clock divisor %d \n " ,
dev_name ( & dev - > dev ) , i - vlynq_rdiv1 + 1 ) ;
dev - > divisor = i ;
return 0 ;
} else {
vlynq_reset ( dev ) ;
}
}
return - ENODEV ;
}
/*
* A VLYNQ remote device can be clocked by the VLYNQ bus
* master using a dedicated clock line . In that case , only
* the bus master configures the serial clock divider .
* Iterate through the 8 possible dividers until we
* actually get a link with the device .
*/
static int __vlynq_try_local ( struct vlynq_device * dev )
{
int i ;
vlynq_reset ( dev ) ;
for ( i = dev - > dev_id ? vlynq_ldiv2 : vlynq_ldiv8 ; dev - > dev_id ?
i < = vlynq_ldiv8 : i > = vlynq_ldiv2 ;
dev - > dev_id ? i + + : i - - ) {
writel ( ( readl ( & dev - > local - > control ) &
~ VLYNQ_CTRL_CLOCK_MASK ) |
VLYNQ_CTRL_CLOCK_INT |
VLYNQ_CTRL_CLOCK_DIV ( i - vlynq_ldiv1 ) ,
& dev - > local - > control ) ;
if ( vlynq_linked ( dev ) ) {
printk ( KERN_DEBUG
" %s: using local clock divisor %d \n " ,
dev_name ( & dev - > dev ) , i - vlynq_ldiv1 + 1 ) ;
dev - > divisor = i ;
return 0 ;
} else {
vlynq_reset ( dev ) ;
}
}
return - ENODEV ;
}
/*
* When using external clocking method , serial clock
* is supplied by an external oscillator , therefore we
* should mask the local clock bit in the clock control
* register for both the bus master and the remote device .
*/
static int __vlynq_try_external ( struct vlynq_device * dev )
{
vlynq_reset ( dev ) ;
if ( ! vlynq_linked ( dev ) )
return - ENODEV ;
writel ( ( readl ( & dev - > remote - > control ) &
~ VLYNQ_CTRL_CLOCK_INT ) ,
& dev - > remote - > control ) ;
writel ( ( readl ( & dev - > local - > control ) &
~ VLYNQ_CTRL_CLOCK_INT ) ,
& dev - > local - > control ) ;
if ( vlynq_linked ( dev ) ) {
printk ( KERN_DEBUG " %s: using external clock \n " ,
dev_name ( & dev - > dev ) ) ;
dev - > divisor = vlynq_div_external ;
return 0 ;
}
return - ENODEV ;
}
static int __vlynq_enable_device ( struct vlynq_device * dev )
{
int result ;
struct plat_vlynq_ops * ops = dev - > dev . platform_data ;
result = ops - > on ( dev ) ;
if ( result )
return result ;
switch ( dev - > divisor ) {
case vlynq_div_external :
case vlynq_div_auto :
/* When the device is brought from reset it should have clock
* generation negotiated by hardware .
* Check which device is generating clocks and perform setup
* accordingly */
if ( vlynq_linked ( dev ) & & readl ( & dev - > remote - > control ) &
VLYNQ_CTRL_CLOCK_INT ) {
if ( ! __vlynq_try_remote ( dev ) | |
! __vlynq_try_local ( dev ) | |
! __vlynq_try_external ( dev ) )
return 0 ;
} else {
if ( ! __vlynq_try_external ( dev ) | |
! __vlynq_try_local ( dev ) | |
! __vlynq_try_remote ( dev ) )
return 0 ;
}
break ;
case vlynq_ldiv1 :
case vlynq_ldiv2 :
case vlynq_ldiv3 :
case vlynq_ldiv4 :
case vlynq_ldiv5 :
case vlynq_ldiv6 :
case vlynq_ldiv7 :
case vlynq_ldiv8 :
writel ( VLYNQ_CTRL_CLOCK_INT |
VLYNQ_CTRL_CLOCK_DIV ( dev - > divisor -
vlynq_ldiv1 ) , & dev - > local - > control ) ;
writel ( 0 , & dev - > remote - > control ) ;
if ( vlynq_linked ( dev ) ) {
printk ( KERN_DEBUG
" %s: using local clock divisor %d \n " ,
dev_name ( & dev - > dev ) ,
dev - > divisor - vlynq_ldiv1 + 1 ) ;
return 0 ;
}
break ;
case vlynq_rdiv1 :
case vlynq_rdiv2 :
case vlynq_rdiv3 :
case vlynq_rdiv4 :
case vlynq_rdiv5 :
case vlynq_rdiv6 :
case vlynq_rdiv7 :
case vlynq_rdiv8 :
writel ( 0 , & dev - > local - > control ) ;
writel ( VLYNQ_CTRL_CLOCK_INT |
VLYNQ_CTRL_CLOCK_DIV ( dev - > divisor -
vlynq_rdiv1 ) , & dev - > remote - > control ) ;
if ( vlynq_linked ( dev ) ) {
printk ( KERN_DEBUG
" %s: using remote clock divisor %d \n " ,
dev_name ( & dev - > dev ) ,
dev - > divisor - vlynq_rdiv1 + 1 ) ;
return 0 ;
}
break ;
}
ops - > off ( dev ) ;
return - ENODEV ;
}
int vlynq_enable_device ( struct vlynq_device * dev )
{
struct plat_vlynq_ops * ops = dev - > dev . platform_data ;
int result = - ENODEV ;
result = __vlynq_enable_device ( dev ) ;
if ( result )
return result ;
result = vlynq_setup_irq ( dev ) ;
if ( result )
ops - > off ( dev ) ;
dev - > enabled = ! result ;
return result ;
}
EXPORT_SYMBOL ( vlynq_enable_device ) ;
void vlynq_disable_device ( struct vlynq_device * dev )
{
struct plat_vlynq_ops * ops = dev - > dev . platform_data ;
dev - > enabled = 0 ;
free_irq ( dev - > irq , dev ) ;
ops - > off ( dev ) ;
}
EXPORT_SYMBOL ( vlynq_disable_device ) ;
int vlynq_set_local_mapping ( struct vlynq_device * dev , u32 tx_offset ,
struct vlynq_mapping * mapping )
{
int i ;
if ( ! dev - > enabled )
return - ENXIO ;
writel ( tx_offset , & dev - > local - > tx_offset ) ;
for ( i = 0 ; i < 4 ; i + + ) {
writel ( mapping [ i ] . offset , & dev - > local - > rx_mapping [ i ] . offset ) ;
writel ( mapping [ i ] . size , & dev - > local - > rx_mapping [ i ] . size ) ;
}
return 0 ;
}
EXPORT_SYMBOL ( vlynq_set_local_mapping ) ;
int vlynq_set_remote_mapping ( struct vlynq_device * dev , u32 tx_offset ,
struct vlynq_mapping * mapping )
{
int i ;
if ( ! dev - > enabled )
return - ENXIO ;
writel ( tx_offset , & dev - > remote - > tx_offset ) ;
for ( i = 0 ; i < 4 ; i + + ) {
writel ( mapping [ i ] . offset , & dev - > remote - > rx_mapping [ i ] . offset ) ;
writel ( mapping [ i ] . size , & dev - > remote - > rx_mapping [ i ] . size ) ;
}
return 0 ;
}
EXPORT_SYMBOL ( vlynq_set_remote_mapping ) ;
int vlynq_set_local_irq ( struct vlynq_device * dev , int virq )
{
int irq = dev - > irq_start + virq ;
if ( dev - > enabled )
return - EBUSY ;
if ( ( irq < dev - > irq_start ) | | ( irq > dev - > irq_end ) )
return - EINVAL ;
if ( virq = = dev - > remote_irq )
return - EINVAL ;
dev - > local_irq = virq ;
return 0 ;
}
EXPORT_SYMBOL ( vlynq_set_local_irq ) ;
int vlynq_set_remote_irq ( struct vlynq_device * dev , int virq )
{
int irq = dev - > irq_start + virq ;
if ( dev - > enabled )
return - EBUSY ;
if ( ( irq < dev - > irq_start ) | | ( irq > dev - > irq_end ) )
return - EINVAL ;
if ( virq = = dev - > local_irq )
return - EINVAL ;
dev - > remote_irq = virq ;
return 0 ;
}
EXPORT_SYMBOL ( vlynq_set_remote_irq ) ;
static int vlynq_probe ( struct platform_device * pdev )
{
struct vlynq_device * dev ;
struct resource * regs_res , * mem_res , * irq_res ;
int len , result ;
regs_res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " regs " ) ;
if ( ! regs_res )
return - ENODEV ;
mem_res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " mem " ) ;
if ( ! mem_res )
return - ENODEV ;
irq_res = platform_get_resource_byname ( pdev , IORESOURCE_IRQ , " devirq " ) ;
if ( ! irq_res )
return - ENODEV ;
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev ) {
printk ( KERN_ERR
" vlynq: failed to allocate device structure \n " ) ;
return - ENOMEM ;
}
dev - > id = pdev - > id ;
dev - > dev . bus = & vlynq_bus_type ;
dev - > dev . parent = & pdev - > dev ;
dev_set_name ( & dev - > dev , " vlynq%d " , dev - > id ) ;
dev - > dev . platform_data = pdev - > dev . platform_data ;
dev - > dev . release = vlynq_device_release ;
dev - > regs_start = regs_res - > start ;
dev - > regs_end = regs_res - > end ;
dev - > mem_start = mem_res - > start ;
dev - > mem_end = mem_res - > end ;
len = regs_res - > end - regs_res - > start ;
if ( ! request_mem_region ( regs_res - > start , len , dev_name ( & dev - > dev ) ) ) {
printk ( KERN_ERR " %s: Can't request vlynq registers \n " ,
dev_name ( & dev - > dev ) ) ;
result = - ENXIO ;
goto fail_request ;
}
dev - > local = ioremap ( regs_res - > start , len ) ;
if ( ! dev - > local ) {
printk ( KERN_ERR " %s: Can't remap vlynq registers \n " ,
dev_name ( & dev - > dev ) ) ;
result = - ENXIO ;
goto fail_remap ;
}
dev - > remote = ( struct vlynq_regs * ) ( ( void * ) dev - > local +
VLYNQ_REMOTE_OFFSET ) ;
dev - > irq = platform_get_irq_byname ( pdev , " irq " ) ;
dev - > irq_start = irq_res - > start ;
dev - > irq_end = irq_res - > end ;
dev - > local_irq = dev - > irq_end - dev - > irq_start ;
dev - > remote_irq = dev - > local_irq - 1 ;
if ( device_register ( & dev - > dev ) )
goto fail_register ;
platform_set_drvdata ( pdev , dev ) ;
printk ( KERN_INFO " %s: regs 0x%p, irq %d, mem 0x%p \n " ,
dev_name ( & dev - > dev ) , ( void * ) dev - > regs_start , dev - > irq ,
( void * ) dev - > mem_start ) ;
dev - > dev_id = 0 ;
dev - > divisor = vlynq_div_auto ;
result = __vlynq_enable_device ( dev ) ;
if ( result = = 0 ) {
dev - > dev_id = readl ( & dev - > remote - > chip ) ;
( ( struct plat_vlynq_ops * ) ( dev - > dev . platform_data ) ) - > off ( dev ) ;
}
if ( dev - > dev_id )
printk ( KERN_INFO " Found a VLYNQ device: %08x \n " , dev - > dev_id ) ;
return 0 ;
fail_register :
iounmap ( dev - > local ) ;
fail_remap :
fail_request :
release_mem_region ( regs_res - > start , len ) ;
kfree ( dev ) ;
return result ;
}
static int vlynq_remove ( struct platform_device * pdev )
{
struct vlynq_device * dev = platform_get_drvdata ( pdev ) ;
device_unregister ( & dev - > dev ) ;
iounmap ( dev - > local ) ;
release_mem_region ( dev - > regs_start , dev - > regs_end - dev - > regs_start ) ;
kfree ( dev ) ;
return 0 ;
}
static struct platform_driver vlynq_platform_driver = {
. driver . name = " vlynq " ,
. probe = vlynq_probe ,
. remove = __devexit_p ( vlynq_remove ) ,
} ;
struct bus_type vlynq_bus_type = {
. name = " vlynq " ,
. match = vlynq_device_match ,
. probe = vlynq_device_probe ,
. remove = vlynq_device_remove ,
} ;
EXPORT_SYMBOL ( vlynq_bus_type ) ;
static int __devinit vlynq_init ( void )
{
int res = 0 ;
res = bus_register ( & vlynq_bus_type ) ;
if ( res )
goto fail_bus ;
res = platform_driver_register ( & vlynq_platform_driver ) ;
if ( res )
goto fail_platform ;
return 0 ;
fail_platform :
bus_unregister ( & vlynq_bus_type ) ;
fail_bus :
return res ;
}
static void __devexit vlynq_exit ( void )
{
platform_driver_unregister ( & vlynq_platform_driver ) ;
bus_unregister ( & vlynq_bus_type ) ;
}
module_init ( vlynq_init ) ;
module_exit ( vlynq_exit ) ;