2009-05-29 00:23:52 +04:00
/*
* linux / arch / arm / mach - omap2 / gpmc - smc91x . c
*
* Copyright ( C ) 2009 Nokia Corporation
* Contact : Tony Lindgren
*
* 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 .
*/
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/gpio.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/smc91x.h>
2009-10-20 20:40:47 +04:00
# include <plat/board.h>
# include <plat/gpmc.h>
# include <plat/gpmc-smc91x.h>
2009-05-29 00:23:52 +04:00
static struct omap_smc91x_platform_data * gpmc_cfg ;
static struct resource gpmc_smc91x_resources [ ] = {
[ 0 ] = {
. flags = IORESOURCE_MEM ,
} ,
[ 1 ] = {
. flags = IORESOURCE_IRQ ,
} ,
} ;
static struct smc91x_platdata gpmc_smc91x_info = {
2009-12-12 03:16:33 +03:00
. flags = SMC91X_USE_16BIT | SMC91X_NOWAIT | SMC91X_IO_SHIFT_0 ,
. leda = RPC_LED_100_10 ,
. ledb = RPC_LED_TX_RX ,
2009-05-29 00:23:52 +04:00
} ;
static struct platform_device gpmc_smc91x_device = {
. name = " smc91x " ,
. id = - 1 ,
. dev = {
. platform_data = & gpmc_smc91x_info ,
} ,
2009-12-12 03:16:33 +03:00
. num_resources = ARRAY_SIZE ( gpmc_smc91x_resources ) ,
. resource = gpmc_smc91x_resources ,
2009-05-29 00:23:52 +04:00
} ;
/*
* Set the gpmc timings for smc91c96 . The timings are taken
* from the data sheet available at :
* http : //www.smsc.com/main/catalog/lan91c96.html
* REVISIT : Level shifters can add at least to the access latency .
*/
static int smc91c96_gpmc_retime ( void )
{
struct gpmc_timings t ;
const int t3 = 10 ; /* Figure 12.2 read and 12.4 write */
const int t4_r = 20 ; /* Figure 12.2 read */
const int t4_w = 5 ; /* Figure 12.4 write */
const int t5 = 25 ; /* Figure 12.2 read */
const int t6 = 15 ; /* Figure 12.2 read */
const int t7 = 5 ; /* Figure 12.4 write */
const int t8 = 5 ; /* Figure 12.4 write */
const int t20 = 185 ; /* Figure 12.2 read and 12.4 write */
u32 l ;
memset ( & t , 0 , sizeof ( t ) ) ;
/* Read timings */
t . cs_on = 0 ;
t . adv_on = t . cs_on ;
t . oe_on = t . adv_on + t3 ;
t . access = t . oe_on + t5 ;
t . oe_off = t . access ;
t . adv_rd_off = t . oe_off + max ( t4_r , t6 ) ;
t . cs_rd_off = t . oe_off ;
t . rd_cycle = t20 - t . oe_on ;
/* Write timings */
t . we_on = t . adv_on + t3 ;
if ( cpu_is_omap34xx ( ) & & ( gpmc_cfg - > flags & GPMC_MUX_ADD_DATA ) ) {
t . wr_data_mux_bus = t . we_on ;
t . we_off = t . wr_data_mux_bus + t7 ;
} else
t . we_off = t . we_on + t7 ;
if ( cpu_is_omap34xx ( ) )
t . wr_access = t . we_off ;
t . adv_wr_off = t . we_off + max ( t4_w , t8 ) ;
t . cs_wr_off = t . we_off + t4_w ;
t . wr_cycle = t20 - t . we_on ;
l = GPMC_CONFIG1_DEVICESIZE_16 ;
if ( gpmc_cfg - > flags & GPMC_MUX_ADD_DATA )
l | = GPMC_CONFIG1_MUXADDDATA ;
if ( gpmc_cfg - > flags & GPMC_READ_MON )
l | = GPMC_CONFIG1_WAIT_READ_MON ;
if ( gpmc_cfg - > flags & GPMC_WRITE_MON )
l | = GPMC_CONFIG1_WAIT_WRITE_MON ;
if ( gpmc_cfg - > wait_pin )
l | = GPMC_CONFIG1_WAIT_PIN_SEL ( gpmc_cfg - > wait_pin ) ;
gpmc_cs_write_reg ( gpmc_cfg - > cs , GPMC_CS_CONFIG1 , l ) ;
/*
* FIXME : Calculate the address and data bus muxed timings .
* Note that at least adv_rd_off needs to be changed according
* to omap3430 TRM Figure 11 - 11. Are the sdp boards using the
* FPGA in between smc91x and omap as the timings are different
* from above ?
*/
if ( gpmc_cfg - > flags & GPMC_MUX_ADD_DATA )
return 0 ;
return gpmc_cs_set_timings ( gpmc_cfg - > cs , & t ) ;
}
/*
* Initialize smc91x device connected to the GPMC . Note that we
* assume that pin multiplexing is done in the board - * . c file ,
* or in the bootloader .
*/
void __init gpmc_smc91x_init ( struct omap_smc91x_platform_data * board_data )
{
unsigned long cs_mem_base ;
int ret ;
gpmc_cfg = board_data ;
if ( gpmc_cfg - > flags & GPMC_TIMINGS_SMC91C96 )
gpmc_cfg - > retime = smc91c96_gpmc_retime ;
if ( gpmc_cs_request ( gpmc_cfg - > cs , SZ_16M , & cs_mem_base ) < 0 ) {
printk ( KERN_ERR " Failed to request GPMC mem for smc91x \n " ) ;
return ;
}
gpmc_smc91x_resources [ 0 ] . start = cs_mem_base + 0x300 ;
gpmc_smc91x_resources [ 0 ] . end = cs_mem_base + 0x30f ;
gpmc_smc91x_resources [ 1 ] . flags | = ( gpmc_cfg - > flags & IRQF_TRIGGER_MASK ) ;
if ( gpmc_cfg - > retime ) {
ret = gpmc_cfg - > retime ( ) ;
if ( ret ! = 0 )
goto free1 ;
}
if ( gpio_request ( gpmc_cfg - > gpio_irq , " SMC91X irq " ) < 0 )
goto free1 ;
gpio_direction_input ( gpmc_cfg - > gpio_irq ) ;
gpmc_smc91x_resources [ 1 ] . start = gpio_to_irq ( gpmc_cfg - > gpio_irq ) ;
if ( gpmc_cfg - > gpio_pwrdwn ) {
ret = gpio_request ( gpmc_cfg - > gpio_pwrdwn , " SMC91X powerdown " ) ;
if ( ret )
goto free2 ;
gpio_direction_output ( gpmc_cfg - > gpio_pwrdwn , 0 ) ;
}
if ( gpmc_cfg - > gpio_reset ) {
ret = gpio_request ( gpmc_cfg - > gpio_reset , " SMC91X reset " ) ;
if ( ret )
goto free3 ;
gpio_direction_output ( gpmc_cfg - > gpio_reset , 0 ) ;
gpio_set_value ( gpmc_cfg - > gpio_reset , 1 ) ;
msleep ( 100 ) ;
gpio_set_value ( gpmc_cfg - > gpio_reset , 0 ) ;
}
if ( platform_device_register ( & gpmc_smc91x_device ) < 0 ) {
printk ( KERN_ERR " Unable to register smc91x device \n " ) ;
gpio_free ( gpmc_cfg - > gpio_reset ) ;
goto free3 ;
}
return ;
free3 :
if ( gpmc_cfg - > gpio_pwrdwn )
gpio_free ( gpmc_cfg - > gpio_pwrdwn ) ;
free2 :
gpio_free ( gpmc_cfg - > gpio_irq ) ;
free1 :
gpmc_cs_free ( gpmc_cfg - > cs ) ;
printk ( KERN_ERR " Could not initialize smc91x \n " ) ;
}