2010-07-27 19:42:46 +04:00
/*
* arch / arm / mach - lpc32xx / common . c
*
* Author : Kevin Wells < kevin . wells @ nxp . com >
*
* Copyright ( C ) 2010 NXP Semiconductors
*
* 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 .
*/
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/i2c-pnx.h>
# include <linux/io.h>
# include <asm/mach/map.h>
2012-07-20 16:01:51 +04:00
# include <asm/system_info.h>
2010-07-27 19:42:46 +04:00
# include <mach/hardware.h>
# include <mach/platform.h>
# include "common.h"
/*
* Returns the unique ID for the device
*/
void lpc32xx_get_uid ( u32 devid [ 4 ] )
{
int i ;
for ( i = 0 ; i < 4 ; i + + )
devid [ i ] = __raw_readl ( LPC32XX_CLKPWR_DEVID ( i < < 2 ) ) ;
}
/*
* Returns SYSCLK source
* 0 = PLL397 , 1 = main oscillator
*/
int clk_is_sysclk_mainosc ( void )
{
if ( ( __raw_readl ( LPC32XX_CLKPWR_SYSCLK_CTRL ) &
LPC32XX_CLKPWR_SYSCTRL_SYSCLKMUX ) = = 0 )
return 1 ;
return 0 ;
}
/*
* System reset via the watchdog timer
*/
2011-11-05 16:17:40 +04:00
static void lpc32xx_watchdog_reset ( void )
2010-07-27 19:42:46 +04:00
{
/* Make sure WDT clocks are enabled */
__raw_writel ( LPC32XX_CLKPWR_PWMCLK_WDOG_EN ,
LPC32XX_CLKPWR_TIMER_CLK_CTRL ) ;
/* Instant assert of RESETOUT_N with pulse length 1mS */
__raw_writel ( 13000 , io_p2v ( LPC32XX_WDTIM_BASE + 0x18 ) ) ;
__raw_writel ( 0x70 , io_p2v ( LPC32XX_WDTIM_BASE + 0xC ) ) ;
}
/*
* Detects and returns IRAM size for the device variation
*/
# define LPC32XX_IRAM_BANK_SIZE SZ_128K
static u32 iram_size ;
u32 lpc32xx_return_iram_size ( void )
{
if ( iram_size = = 0 ) {
u32 savedval1 , savedval2 ;
void __iomem * iramptr1 , * iramptr2 ;
iramptr1 = io_p2v ( LPC32XX_IRAM_BASE ) ;
iramptr2 = io_p2v ( LPC32XX_IRAM_BASE + LPC32XX_IRAM_BANK_SIZE ) ;
savedval1 = __raw_readl ( iramptr1 ) ;
savedval2 = __raw_readl ( iramptr2 ) ;
if ( savedval1 = = savedval2 ) {
__raw_writel ( savedval2 + 1 , iramptr2 ) ;
if ( __raw_readl ( iramptr1 ) = = savedval2 + 1 )
iram_size = LPC32XX_IRAM_BANK_SIZE ;
else
iram_size = LPC32XX_IRAM_BANK_SIZE * 2 ;
__raw_writel ( savedval2 , iramptr2 ) ;
} else
iram_size = LPC32XX_IRAM_BANK_SIZE * 2 ;
}
return iram_size ;
}
/*
* Computes PLL rate from PLL register and input clock
*/
u32 clk_check_pll_setup ( u32 ifreq , struct clk_pll_setup * pllsetup )
{
u32 ilfreq , p , m , n , fcco , fref , cfreq ;
int mode ;
/*
* PLL requirements
* ifreq must be > = 1 MHz and < = 20 MHz
* FCCO must be > = 156 MHz and < = 320 MHz
* FREF must be > = 1 MHz and < = 27 MHz
* Assume the passed input data is not valid
*/
ilfreq = ifreq ;
m = pllsetup - > pll_m ;
n = pllsetup - > pll_n ;
p = pllsetup - > pll_p ;
mode = ( pllsetup - > cco_bypass_b15 < < 2 ) |
( pllsetup - > direct_output_b14 < < 1 ) |
pllsetup - > fdbk_div_ctrl_b13 ;
switch ( mode ) {
case 0x0 : /* Non-integer mode */
cfreq = ( m * ilfreq ) / ( 2 * p * n ) ;
fcco = ( m * ilfreq ) / n ;
fref = ilfreq / n ;
break ;
case 0x1 : /* integer mode */
cfreq = ( m * ilfreq ) / n ;
fcco = ( m * ilfreq ) / ( n * 2 * p ) ;
fref = ilfreq / n ;
break ;
case 0x2 :
case 0x3 : /* Direct mode */
cfreq = ( m * ilfreq ) / n ;
fcco = cfreq ;
fref = ilfreq / n ;
break ;
case 0x4 :
case 0x5 : /* Bypass mode */
cfreq = ilfreq / ( 2 * p ) ;
fcco = 156000000 ;
fref = 1000000 ;
break ;
case 0x6 :
case 0x7 : /* Direct bypass mode */
default :
cfreq = ilfreq ;
fcco = 156000000 ;
fref = 1000000 ;
break ;
}
if ( fcco < 156000000 | | fcco > 320000000 )
cfreq = 0 ;
if ( fref < 1000000 | | fref > 27000000 )
cfreq = 0 ;
return ( u32 ) cfreq ;
}
u32 clk_get_pclk_div ( void )
{
return 1 + ( ( __raw_readl ( LPC32XX_CLKPWR_HCLK_DIV ) > > 2 ) & 0x1F ) ;
}
static struct map_desc lpc32xx_io_desc [ ] __initdata = {
{
2012-09-15 00:20:24 +04:00
. virtual = ( unsigned long ) IO_ADDRESS ( LPC32XX_AHB0_START ) ,
2010-07-27 19:42:46 +04:00
. pfn = __phys_to_pfn ( LPC32XX_AHB0_START ) ,
. length = LPC32XX_AHB0_SIZE ,
. type = MT_DEVICE
} ,
{
2012-09-15 00:20:24 +04:00
. virtual = ( unsigned long ) IO_ADDRESS ( LPC32XX_AHB1_START ) ,
2010-07-27 19:42:46 +04:00
. pfn = __phys_to_pfn ( LPC32XX_AHB1_START ) ,
. length = LPC32XX_AHB1_SIZE ,
. type = MT_DEVICE
} ,
{
2012-09-15 00:20:24 +04:00
. virtual = ( unsigned long ) IO_ADDRESS ( LPC32XX_FABAPB_START ) ,
2010-07-27 19:42:46 +04:00
. pfn = __phys_to_pfn ( LPC32XX_FABAPB_START ) ,
. length = LPC32XX_FABAPB_SIZE ,
. type = MT_DEVICE
} ,
{
2012-09-15 00:20:24 +04:00
. virtual = ( unsigned long ) IO_ADDRESS ( LPC32XX_IRAM_BASE ) ,
2010-07-27 19:42:46 +04:00
. pfn = __phys_to_pfn ( LPC32XX_IRAM_BASE ) ,
. length = ( LPC32XX_IRAM_BANK_SIZE * 2 ) ,
. type = MT_DEVICE
} ,
} ;
void __init lpc32xx_map_io ( void )
{
iotable_init ( lpc32xx_io_desc , ARRAY_SIZE ( lpc32xx_io_desc ) ) ;
}
2011-11-05 16:17:40 +04:00
void lpc23xx_restart ( char mode , const char * cmd )
{
switch ( mode ) {
case ' s ' :
case ' h ' :
lpc32xx_watchdog_reset ( ) ;
break ;
default :
/* Do nothing */
break ;
}
/* Wait for watchdog to reset system */
while ( 1 )
;
}
2012-04-22 14:01:19 +04:00
2012-07-20 16:01:51 +04:00
static int __init lpc32xx_check_uid ( void )
2012-04-22 14:01:19 +04:00
{
u32 uid [ 4 ] ;
lpc32xx_get_uid ( uid ) ;
printk ( KERN_INFO " LPC32XX unique ID: %08x%08x%08x%08x \n " ,
uid [ 3 ] , uid [ 2 ] , uid [ 1 ] , uid [ 0 ] ) ;
2012-07-20 16:01:51 +04:00
if ( ! system_serial_low & & ! system_serial_high ) {
system_serial_low = uid [ 0 ] ;
system_serial_high = uid [ 1 ] ;
}
2012-04-22 14:01:19 +04:00
return 1 ;
}
2012-07-20 16:01:51 +04:00
arch_initcall ( lpc32xx_check_uid ) ;