2011-02-13 05:22:49 +03:00
/*
* Copyright ( C ) 2011 Google , Inc .
*
* Author :
* Colin Cross < ccross @ android . com >
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* 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 .
*
*/
# include <linux/kernel.h>
2011-10-17 03:54:51 +04:00
# include <linux/device.h>
2011-02-13 05:22:49 +03:00
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/module.h>
2011-10-18 03:05:22 +04:00
# include <linux/of.h>
2011-10-17 03:54:51 +04:00
# include <linux/platform_device.h>
# include <linux/platform_data/tegra_emc.h>
2011-02-13 05:22:49 +03:00
# include "tegra2_emc.h"
2011-10-18 03:05:22 +04:00
# include "fuse.h"
2011-02-13 05:22:49 +03:00
# ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE
static bool emc_enable = true ;
# else
static bool emc_enable ;
# endif
module_param ( emc_enable , bool , 0644 ) ;
2011-10-17 03:54:51 +04:00
static struct platform_device * emc_pdev ;
static void __iomem * emc_regbase ;
2011-02-13 05:22:49 +03:00
static inline void emc_writel ( u32 val , unsigned long addr )
{
2011-10-17 03:54:51 +04:00
writel ( val , emc_regbase + addr ) ;
2011-02-13 05:22:49 +03:00
}
static inline u32 emc_readl ( unsigned long addr )
{
2011-10-17 03:54:51 +04:00
return readl ( emc_regbase + addr ) ;
2011-02-13 05:22:49 +03:00
}
static const unsigned long emc_reg_addr [ TEGRA_EMC_NUM_REGS ] = {
0x2c , /* RC */
0x30 , /* RFC */
0x34 , /* RAS */
0x38 , /* RP */
0x3c , /* R2W */
0x40 , /* W2R */
0x44 , /* R2P */
0x48 , /* W2P */
0x4c , /* RD_RCD */
0x50 , /* WR_RCD */
0x54 , /* RRD */
0x58 , /* REXT */
0x5c , /* WDV */
0x60 , /* QUSE */
0x64 , /* QRST */
0x68 , /* QSAFE */
0x6c , /* RDV */
0x70 , /* REFRESH */
0x74 , /* BURST_REFRESH_NUM */
0x78 , /* PDEX2WR */
0x7c , /* PDEX2RD */
0x80 , /* PCHG2PDEN */
0x84 , /* ACT2PDEN */
0x88 , /* AR2PDEN */
0x8c , /* RW2PDEN */
0x90 , /* TXSR */
0x94 , /* TCKE */
0x98 , /* TFAW */
0x9c , /* TRPAB */
0xa0 , /* TCLKSTABLE */
0xa4 , /* TCLKSTOP */
0xa8 , /* TREFBW */
0xac , /* QUSE_EXTRA */
0x114 , /* FBIO_CFG6 */
0xb0 , /* ODT_WRITE */
0xb4 , /* ODT_READ */
0x104 , /* FBIO_CFG5 */
0x2bc , /* CFG_DIG_DLL */
0x2c0 , /* DLL_XFORM_DQS */
0x2c4 , /* DLL_XFORM_QUSE */
0x2e0 , /* ZCAL_REF_CNT */
0x2e4 , /* ZCAL_WAIT_CNT */
0x2a8 , /* AUTO_CAL_INTERVAL */
0x2d0 , /* CFG_CLKTRIM_0 */
0x2d4 , /* CFG_CLKTRIM_1 */
0x2d8 , /* CFG_CLKTRIM_2 */
} ;
/* Select the closest EMC rate that is higher than the requested rate */
long tegra_emc_round_rate ( unsigned long rate )
{
2011-10-17 03:54:51 +04:00
struct tegra_emc_pdata * pdata ;
2011-02-13 05:22:49 +03:00
int i ;
int best = - 1 ;
unsigned long distance = ULONG_MAX ;
2011-10-17 03:54:51 +04:00
if ( ! emc_pdev )
2011-02-13 05:22:49 +03:00
return - EINVAL ;
2011-10-17 03:54:51 +04:00
pdata = emc_pdev - > dev . platform_data ;
2011-02-13 05:22:49 +03:00
pr_debug ( " %s: %lu \n " , __func__ , rate ) ;
/*
* The EMC clock rate is twice the bus rate , and the bus rate is
* measured in kHz
*/
rate = rate / 2 / 1000 ;
2011-10-17 03:54:51 +04:00
for ( i = 0 ; i < pdata - > num_tables ; i + + ) {
if ( pdata - > tables [ i ] . rate > = rate & &
( pdata - > tables [ i ] . rate - rate ) < distance ) {
distance = pdata - > tables [ i ] . rate - rate ;
2011-02-13 05:22:49 +03:00
best = i ;
}
}
if ( best < 0 )
return - EINVAL ;
2011-10-17 03:54:51 +04:00
pr_debug ( " %s: using %lu \n " , __func__ , pdata - > tables [ best ] . rate ) ;
2011-02-13 05:22:49 +03:00
2011-10-17 03:54:51 +04:00
return pdata - > tables [ best ] . rate * 2 * 1000 ;
2011-02-13 05:22:49 +03:00
}
/*
* The EMC registers have shadow registers . When the EMC clock is updated
* in the clock controller , the shadow registers are copied to the active
* registers , allowing glitchless memory bus frequency changes .
* This function updates the shadow registers for a new clock frequency ,
* and relies on the clock lock on the emc clock to avoid races between
* multiple frequency changes
*/
int tegra_emc_set_rate ( unsigned long rate )
{
2011-10-17 03:54:51 +04:00
struct tegra_emc_pdata * pdata ;
2011-02-13 05:22:49 +03:00
int i ;
int j ;
2011-10-17 03:54:51 +04:00
if ( ! emc_pdev )
2011-02-13 05:22:49 +03:00
return - EINVAL ;
2011-10-17 03:54:51 +04:00
pdata = emc_pdev - > dev . platform_data ;
2011-02-13 05:22:49 +03:00
/*
* The EMC clock rate is twice the bus rate , and the bus rate is
* measured in kHz
*/
rate = rate / 2 / 1000 ;
2011-10-17 03:54:51 +04:00
for ( i = 0 ; i < pdata - > num_tables ; i + + )
if ( pdata - > tables [ i ] . rate = = rate )
2011-02-13 05:22:49 +03:00
break ;
2011-10-17 03:54:51 +04:00
if ( i > = pdata - > num_tables )
2011-02-13 05:22:49 +03:00
return - EINVAL ;
pr_debug ( " %s: setting to %lu \n " , __func__ , rate ) ;
for ( j = 0 ; j < TEGRA_EMC_NUM_REGS ; j + + )
2011-10-17 03:54:51 +04:00
emc_writel ( pdata - > tables [ i ] . regs [ j ] , emc_reg_addr [ j ] ) ;
emc_readl ( pdata - > tables [ i ] . regs [ TEGRA_EMC_NUM_REGS - 1 ] ) ;
return 0 ;
}
2011-10-18 03:05:22 +04:00
# ifdef CONFIG_OF
static struct device_node * tegra_emc_ramcode_devnode ( struct device_node * np )
{
struct device_node * iter ;
u32 reg ;
for_each_child_of_node ( np , iter ) {
if ( of_property_read_u32 ( np , " nvidia,ram-code " , & reg ) )
continue ;
if ( reg = = tegra_bct_strapping )
return of_node_get ( iter ) ;
}
return NULL ;
}
static struct tegra_emc_pdata * tegra_emc_dt_parse_pdata (
struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct device_node * tnp , * iter ;
struct tegra_emc_pdata * pdata ;
int ret , i , num_tables ;
if ( ! np )
return NULL ;
if ( of_find_property ( np , " nvidia,use-ram-code " , NULL ) ) {
tnp = tegra_emc_ramcode_devnode ( np ) ;
if ( ! tnp )
dev_warn ( & pdev - > dev ,
" can't find emc table for ram-code 0x%02x \n " ,
tegra_bct_strapping ) ;
} else
tnp = of_node_get ( np ) ;
if ( ! tnp )
return NULL ;
num_tables = 0 ;
for_each_child_of_node ( tnp , iter )
if ( of_device_is_compatible ( iter , " nvidia,tegra20-emc-table " ) )
num_tables + + ;
if ( ! num_tables ) {
pdata = NULL ;
goto out ;
}
pdata = devm_kzalloc ( & pdev - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
pdata - > tables = devm_kzalloc ( & pdev - > dev ,
sizeof ( * pdata - > tables ) * num_tables ,
GFP_KERNEL ) ;
i = 0 ;
for_each_child_of_node ( tnp , iter ) {
u32 prop ;
ret = of_property_read_u32 ( iter , " clock-frequency " , & prop ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " no clock-frequency in %s \n " ,
iter - > full_name ) ;
continue ;
}
pdata - > tables [ i ] . rate = prop ;
ret = of_property_read_u32_array ( iter , " nvidia,emc-registers " ,
pdata - > tables [ i ] . regs ,
TEGRA_EMC_NUM_REGS ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" malformed emc-registers property in %s \n " ,
iter - > full_name ) ;
continue ;
}
i + + ;
}
pdata - > num_tables = i ;
out :
of_node_put ( tnp ) ;
return pdata ;
}
# else
static struct tegra_emc_pdata * tegra_emc_dt_parse_pdata (
struct platform_device * pdev )
{
return NULL ;
}
# endif
2012-12-22 02:02:24 +04:00
static struct tegra_emc_pdata * tegra_emc_fill_pdata ( struct platform_device * pdev )
2011-10-18 03:05:22 +04:00
{
struct clk * c = clk_get_sys ( NULL , " emc " ) ;
struct tegra_emc_pdata * pdata ;
unsigned long khz ;
int i ;
WARN_ON ( pdev - > dev . platform_data ) ;
BUG_ON ( IS_ERR_OR_NULL ( c ) ) ;
pdata = devm_kzalloc ( & pdev - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
pdata - > tables = devm_kzalloc ( & pdev - > dev , sizeof ( * pdata - > tables ) ,
GFP_KERNEL ) ;
2012-01-26 19:12:23 +04:00
pdata - > tables [ 0 ] . rate = clk_get_rate ( c ) / 2 / 1000 ;
2011-10-18 03:05:22 +04:00
for ( i = 0 ; i < TEGRA_EMC_NUM_REGS ; i + + )
pdata - > tables [ 0 ] . regs [ i ] = emc_readl ( emc_reg_addr [ i ] ) ;
pdata - > num_tables = 1 ;
2012-01-26 19:12:23 +04:00
khz = pdata - > tables [ 0 ] . rate ;
2011-10-18 03:05:22 +04:00
dev_info ( & pdev - > dev , " no tables provided, using %ld kHz emc, "
2012-01-26 19:12:23 +04:00
" %ld kHz mem \n " , khz * 2 , khz ) ;
2011-10-18 03:05:22 +04:00
return pdata ;
}
2012-12-22 02:02:24 +04:00
static int tegra_emc_probe ( struct platform_device * pdev )
2011-10-17 03:54:51 +04:00
{
struct tegra_emc_pdata * pdata ;
struct resource * res ;
if ( ! emc_enable ) {
dev_err ( & pdev - > dev , " disabled per module parameter \n " ) ;
return - ENODEV ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " missing register base \n " ) ;
return - ENOMEM ;
}
2013-01-21 14:08:55 +04:00
emc_regbase = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( emc_regbase ) )
return PTR_ERR ( emc_regbase ) ;
2011-10-18 03:05:22 +04:00
pdata = pdev - > dev . platform_data ;
if ( ! pdata )
pdata = tegra_emc_dt_parse_pdata ( pdev ) ;
if ( ! pdata )
pdata = tegra_emc_fill_pdata ( pdev ) ;
pdev - > dev . platform_data = pdata ;
2011-10-17 03:54:51 +04:00
emc_pdev = pdev ;
2011-02-13 05:22:49 +03:00
return 0 ;
}
2012-12-22 02:02:24 +04:00
static struct of_device_id tegra_emc_of_match [ ] = {
2011-10-18 03:05:22 +04:00
{ . compatible = " nvidia,tegra20-emc " , } ,
{ } ,
} ;
2011-10-17 03:54:51 +04:00
static struct platform_driver tegra_emc_driver = {
. driver = {
. name = " tegra-emc " ,
. owner = THIS_MODULE ,
2011-10-18 03:05:22 +04:00
. of_match_table = tegra_emc_of_match ,
2011-10-17 03:54:51 +04:00
} ,
. probe = tegra_emc_probe ,
} ;
static int __init tegra_emc_init ( void )
2011-02-13 05:22:49 +03:00
{
2011-10-17 03:54:51 +04:00
return platform_driver_register ( & tegra_emc_driver ) ;
2011-02-13 05:22:49 +03:00
}
2011-10-17 03:54:51 +04:00
device_initcall ( tegra_emc_init ) ;