2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / mach - pxa / pxa25x . c
*
* Author : Nicolas Pitre
* Created : Jun 15 , 2001
* Copyright : MontaVista Software Inc .
*
* Code specific to PXA21x / 25 x / 26 x variants .
*
* 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 .
*
* Since this file should be linked before any other machine specific file ,
* the __initcall ( ) here will be executed first . This serves as default
* initialization stuff for PXA machines which can be overridden later if
* need be .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
2007-05-15 13:39:49 +04:00
# include <linux/platform_device.h>
2007-10-18 14:04:39 +04:00
# include <linux/suspend.h>
2008-01-29 02:00:02 +03:00
# include <linux/sysdev.h>
2005-04-17 02:20:36 +04:00
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
# include <mach/irqs.h>
# include <mach/pxa-regs.h>
# include <mach/pxa2xx-regs.h>
# include <mach/mfp-pxa25x.h>
2008-08-07 14:05:25 +04:00
# include <mach/reset.h>
2008-08-05 19:14:15 +04:00
# include <mach/pm.h>
# include <mach/dma.h>
2005-04-17 02:20:36 +04:00
# include "generic.h"
2007-05-15 18:39:36 +04:00
# include "devices.h"
2007-08-20 13:18:02 +04:00
# include "clock.h"
2005-04-17 02:20:36 +04:00
/*
* Various clock factors driven by the CCCR register .
*/
/* Crystal Frequency to Memory Frequency Multiplier (L) */
static unsigned char L_clk_mult [ 32 ] = { 0 , 27 , 32 , 36 , 40 , 45 , 0 , } ;
/* Memory Frequency to Run Mode Frequency Multiplier (M) */
static unsigned char M_clk_mult [ 4 ] = { 0 , 1 , 2 , 4 } ;
/* Run Mode Frequency to Turbo Mode Frequency Multiplier (N) */
/* Note: we store the value N * 2 here. */
static unsigned char N2_clk_mult [ 8 ] = { 0 , 0 , 2 , 3 , 4 , 0 , 6 , 0 } ;
/* Crystal clock */
# define BASE_CLK 3686400
/*
* Get the clock frequency as reflected by CCCR and the turbo flag .
* We assume these values have been applied via a fcs .
* If info is not 0 we also display the current settings .
*/
2007-08-20 13:07:44 +04:00
unsigned int pxa25x_get_clk_frequency_khz ( int info )
2005-04-17 02:20:36 +04:00
{
unsigned long cccr , turbo ;
unsigned int l , L , m , M , n2 , N ;
cccr = CCCR ;
asm ( " mrc \t p14, 0, %0, c6, c0, 0 " : " =r " ( turbo ) ) ;
l = L_clk_mult [ ( cccr > > 0 ) & 0x1f ] ;
m = M_clk_mult [ ( cccr > > 5 ) & 0x03 ] ;
n2 = N2_clk_mult [ ( cccr > > 7 ) & 0x07 ] ;
L = l * BASE_CLK ;
M = m * L ;
N = n2 * M / 2 ;
if ( info )
{
L + = 5000 ;
printk ( KERN_INFO " Memory clock: %d.%02dMHz (*%d) \n " ,
L / 1000000 , ( L % 1000000 ) / 10000 , l ) ;
M + = 5000 ;
printk ( KERN_INFO " Run Mode clock: %d.%02dMHz (*%d) \n " ,
M / 1000000 , ( M % 1000000 ) / 10000 , m ) ;
N + = 5000 ;
printk ( KERN_INFO " Turbo Mode clock: %d.%02dMHz (*%d.%d, %sactive) \n " ,
N / 1000000 , ( N % 1000000 ) / 10000 , n2 / 2 , ( n2 % 2 ) * 5 ,
( turbo & 1 ) ? " " : " in " ) ;
}
return ( turbo & 1 ) ? ( N / 1000 ) : ( M / 1000 ) ;
}
/*
* Return the current memory clock frequency in units of 10 kHz
*/
2007-08-20 13:07:44 +04:00
unsigned int pxa25x_get_memclk_frequency_10khz ( void )
2005-04-17 02:20:36 +04:00
{
return L_clk_mult [ ( CCCR > > 0 ) & 0x1f ] * BASE_CLK / 10000 ;
}
2007-08-20 13:18:02 +04:00
static unsigned long clk_pxa25x_lcd_getrate ( struct clk * clk )
{
return pxa25x_get_memclk_frequency_10khz ( ) * 10000 ;
}
static const struct clkops clk_pxa25x_lcd_ops = {
. enable = clk_cken_enable ,
. disable = clk_cken_disable ,
. getrate = clk_pxa25x_lcd_getrate ,
} ;
2008-07-08 13:32:08 +04:00
static unsigned long gpio12_config_32k [ ] = {
GPIO12_32KHz ,
} ;
static unsigned long gpio12_config_gpio [ ] = {
GPIO12_GPIO ,
} ;
static void clk_gpio12_enable ( struct clk * clk )
{
pxa2xx_mfp_config ( gpio12_config_32k , 1 ) ;
}
static void clk_gpio12_disable ( struct clk * clk )
{
pxa2xx_mfp_config ( gpio12_config_gpio , 1 ) ;
}
static const struct clkops clk_pxa25x_gpio12_ops = {
. enable = clk_gpio12_enable ,
. disable = clk_gpio12_disable ,
} ;
2008-07-08 13:32:50 +04:00
static unsigned long gpio11_config_3m6 [ ] = {
GPIO11_3_6MHz ,
} ;
static unsigned long gpio11_config_gpio [ ] = {
GPIO11_GPIO ,
} ;
static void clk_gpio11_enable ( struct clk * clk )
{
pxa2xx_mfp_config ( gpio11_config_3m6 , 1 ) ;
}
static void clk_gpio11_disable ( struct clk * clk )
{
pxa2xx_mfp_config ( gpio11_config_gpio , 1 ) ;
}
static const struct clkops clk_pxa25x_gpio11_ops = {
. enable = clk_gpio11_enable ,
. disable = clk_gpio11_disable ,
} ;
2007-08-20 13:18:02 +04:00
/*
* 3.6864 MHz - > OST , GPIO , SSP , PWM , PLLs ( 95.842 MHz , 147.456 MHz )
* 95.842 MHz - > MMC 19.169 MHz , I2C 31.949 MHz , FICP 47.923 MHz , USB 47.923 MHz
* 147.456 MHz - > UART 14.7456 MHz , AC97 12.288 MHz , I2S 5.672 MHz ( allegedly )
*/
2008-01-28 01:11:48 +03:00
static struct clk pxa25x_hwuart_clk =
INIT_CKEN ( " UARTCLK " , HWUART , 14745600 , 1 , & pxa_device_hwuart . dev )
;
2008-06-30 22:47:59 +04:00
/*
2008-07-26 03:52:36 +04:00
* PXA 2 xx clock declarations .
2008-06-30 22:47:59 +04:00
*/
2007-08-20 13:18:02 +04:00
static struct clk pxa25x_clks [ ] = {
INIT_CK ( " LCDCLK " , LCD , & clk_pxa25x_lcd_ops , & pxa_device_fb . dev ) ,
INIT_CKEN ( " UARTCLK " , FFUART , 14745600 , 1 , & pxa_device_ffuart . dev ) ,
INIT_CKEN ( " UARTCLK " , BTUART , 14745600 , 1 , & pxa_device_btuart . dev ) ,
2007-09-02 20:08:42 +04:00
INIT_CKEN ( " UARTCLK " , STUART , 14745600 , 1 , NULL ) ,
2008-06-23 02:36:39 +04:00
INIT_CKEN ( " UDCCLK " , USB , 47923000 , 5 , & pxa25x_device_udc . dev ) ,
2008-07-08 13:32:50 +04:00
INIT_CLK ( " GPIO11_CLK " , & clk_pxa25x_gpio11_ops , 3686400 , 0 , NULL ) ,
2008-07-08 13:32:08 +04:00
INIT_CLK ( " GPIO12_CLK " , & clk_pxa25x_gpio12_ops , 32768 , 0 , NULL ) ,
2007-08-20 13:18:02 +04:00
INIT_CKEN ( " MMCCLK " , MMC , 19169000 , 0 , & pxa_device_mci . dev ) ,
INIT_CKEN ( " I2CCLK " , I2C , 31949000 , 0 , & pxa_device_i2c . dev ) ,
2007-12-10 12:54:36 +03:00
INIT_CKEN ( " SSPCLK " , SSP , 3686400 , 0 , & pxa25x_device_ssp . dev ) ,
INIT_CKEN ( " SSPCLK " , NSSP , 3686400 , 0 , & pxa25x_device_nssp . dev ) ,
INIT_CKEN ( " SSPCLK " , ASSP , 3686400 , 0 , & pxa25x_device_assp . dev ) ,
2008-04-14 00:44:04 +04:00
INIT_CKEN ( " PWMCLK " , PWM0 , 3686400 , 0 , & pxa25x_device_pwm0 . dev ) ,
INIT_CKEN ( " PWMCLK " , PWM1 , 3686400 , 0 , & pxa25x_device_pwm1 . dev ) ,
2007-12-10 12:54:36 +03:00
2008-03-04 13:14:22 +03:00
INIT_CKEN ( " AC97CLK " , AC97 , 24576000 , 0 , NULL ) ,
2007-08-20 13:18:02 +04:00
/*
INIT_CKEN ( " I2SCLK " , I2S , 14745600 , 0 , NULL ) ,
*/
2007-09-02 20:08:42 +04:00
INIT_CKEN ( " FICPCLK " , FICP , 47923000 , 0 , NULL ) ,
2007-08-20 13:18:02 +04:00
} ;
2005-06-14 01:35:41 +04:00
# ifdef CONFIG_PM
2005-06-03 23:52:27 +04:00
2007-07-18 14:38:45 +04:00
# define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x
# define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x]
/*
* List of global PXA peripheral registers to preserve .
* More ones like CP and general purpose register values are preserved
* with the stack pointer in sleep . S .
*/
2008-05-03 00:17:06 +04:00
enum { SLEEP_SAVE_PGSR0 , SLEEP_SAVE_PGSR1 , SLEEP_SAVE_PGSR2 ,
2007-07-18 14:38:45 +04:00
SLEEP_SAVE_GAFR0_L , SLEEP_SAVE_GAFR0_U ,
SLEEP_SAVE_GAFR1_L , SLEEP_SAVE_GAFR1_U ,
SLEEP_SAVE_GAFR2_L , SLEEP_SAVE_GAFR2_U ,
SLEEP_SAVE_PSTR ,
SLEEP_SAVE_CKEN ,
2008-05-03 00:17:06 +04:00
SLEEP_SAVE_COUNT
2007-07-18 14:38:45 +04:00
} ;
static void pxa25x_cpu_pm_save ( unsigned long * sleep_save )
{
SAVE ( PGSR0 ) ; SAVE ( PGSR1 ) ; SAVE ( PGSR2 ) ;
SAVE ( GAFR0_L ) ; SAVE ( GAFR0_U ) ;
SAVE ( GAFR1_L ) ; SAVE ( GAFR1_U ) ;
SAVE ( GAFR2_L ) ; SAVE ( GAFR2_U ) ;
SAVE ( CKEN ) ;
SAVE ( PSTR ) ;
2008-01-02 02:54:49 +03:00
/* Clear GPIO transition detect bits */
GEDR0 = GEDR0 ; GEDR1 = GEDR1 ; GEDR2 = GEDR2 ;
2007-07-18 14:38:45 +04:00
}
static void pxa25x_cpu_pm_restore ( unsigned long * sleep_save )
{
2008-01-02 02:54:49 +03:00
/* ensure not to come back here if it wasn't intended */
PSPR = 0 ;
2007-07-18 14:38:45 +04:00
/* restore registers */
RESTORE ( GAFR0_L ) ; RESTORE ( GAFR0_U ) ;
RESTORE ( GAFR1_L ) ; RESTORE ( GAFR1_U ) ;
RESTORE ( GAFR2_L ) ; RESTORE ( GAFR2_U ) ;
RESTORE ( PGSR0 ) ; RESTORE ( PGSR1 ) ; RESTORE ( PGSR2 ) ;
2008-01-02 02:54:49 +03:00
PSSR = PSSR_RDH | PSSR_PH ;
2007-07-18 14:38:45 +04:00
RESTORE ( CKEN ) ;
RESTORE ( PSTR ) ;
}
static void pxa25x_cpu_pm_enter ( suspend_state_t state )
2005-06-03 23:52:27 +04:00
{
2008-05-08 19:50:39 +04:00
/* Clear reset status */
RCSR = RCSR_HWR | RCSR_WDR | RCSR_SMR | RCSR_GPR ;
2005-06-03 23:52:27 +04:00
switch ( state ) {
case PM_SUSPEND_MEM :
/* set resume return address */
PSPR = virt_to_phys ( pxa_cpu_resume ) ;
2007-07-18 14:40:13 +04:00
pxa25x_cpu_suspend ( PWRMODE_SLEEP ) ;
2005-06-03 23:52:27 +04:00
break ;
}
}
2005-06-14 01:35:41 +04:00
2007-07-18 14:38:45 +04:00
static struct pxa_cpu_pm_fns pxa25x_cpu_pm_fns = {
2008-05-03 00:17:06 +04:00
. save_count = SLEEP_SAVE_COUNT ,
2007-10-18 14:04:40 +04:00
. valid = suspend_valid_only_mem ,
2007-07-18 14:38:45 +04:00
. save = pxa25x_cpu_pm_save ,
. restore = pxa25x_cpu_pm_restore ,
. enter = pxa25x_cpu_pm_enter ,
2007-05-15 14:16:10 +04:00
} ;
2007-07-18 14:38:45 +04:00
static void __init pxa25x_init_pm ( void )
{
pxa_cpu_pm_fns = & pxa25x_cpu_pm_fns ;
}
2008-01-02 03:24:49 +03:00
# else
static inline void pxa25x_init_pm ( void ) { }
2005-06-14 01:35:41 +04:00
# endif
2007-05-15 14:16:10 +04:00
2007-08-29 13:22:17 +04:00
/* PXA25x: supports wakeup from GPIO0..GPIO15 and RTC alarm
*/
static int pxa25x_set_wake ( unsigned int irq , unsigned int on )
{
int gpio = IRQ_TO_GPIO ( irq ) ;
2008-03-11 04:46:28 +03:00
uint32_t mask = 0 ;
if ( gpio > = 0 & & gpio < 85 )
return gpio_set_wake ( gpio , on ) ;
2007-08-29 13:22:17 +04:00
if ( irq = = IRQ_RTCAlrm ) {
mask = PWER_RTC ;
goto set_pwer ;
}
return - EINVAL ;
set_pwer :
if ( on )
PWER | = mask ;
else
PWER & = ~ mask ;
return 0 ;
}
2007-06-22 07:14:09 +04:00
void __init pxa25x_init_irq ( void )
{
2008-03-04 09:19:58 +03:00
pxa_init_irq ( 32 , pxa25x_set_wake ) ;
pxa_init_gpio ( 85 , pxa25x_set_wake ) ;
2007-06-22 07:14:09 +04:00
}
2007-05-15 13:39:49 +04:00
static struct platform_device * pxa25x_devices [ ] __initdata = {
2008-06-23 02:36:39 +04:00
& pxa25x_device_udc ,
2007-07-17 13:45:58 +04:00
& pxa_device_ffuart ,
& pxa_device_btuart ,
& pxa_device_stuart ,
& pxa_device_i2s ,
& pxa_device_rtc ,
2007-12-10 12:54:36 +03:00
& pxa25x_device_ssp ,
& pxa25x_device_nssp ,
& pxa25x_device_assp ,
2008-04-14 00:44:04 +04:00
& pxa25x_device_pwm0 ,
& pxa25x_device_pwm1 ,
2007-05-15 13:39:49 +04:00
} ;
2008-01-29 02:00:02 +03:00
static struct sys_device pxa25x_sysdev [ ] = {
{
. cls = & pxa_irq_sysclass ,
2008-01-29 02:00:02 +03:00
} , {
. cls = & pxa_gpio_sysclass ,
2008-01-29 02:00:02 +03:00
} ,
} ;
2007-05-15 14:16:10 +04:00
static int __init pxa25x_init ( void )
{
2008-01-29 02:00:02 +03:00
int i , ret = 0 ;
2007-06-22 08:40:17 +04:00
2008-01-28 01:11:48 +03:00
/* Only add HWUART for PXA255/26x; PXA210/250/27x do not have it. */
2008-06-26 01:17:16 +04:00
if ( cpu_is_pxa255 ( ) )
2008-01-28 01:11:48 +03:00
clks_register ( & pxa25x_hwuart_clk , 1 ) ;
2007-05-15 14:16:10 +04:00
if ( cpu_is_pxa21x ( ) | | cpu_is_pxa25x ( ) ) {
2008-07-29 10:26:00 +04:00
reset_status = RCSR ;
2007-08-20 13:18:02 +04:00
clks_register ( pxa25x_clks , ARRAY_SIZE ( pxa25x_clks ) ) ;
2007-06-22 08:40:17 +04:00
if ( ( ret = pxa_init_dma ( 16 ) ) )
return ret ;
2008-01-02 03:24:49 +03:00
2007-07-18 14:38:45 +04:00
pxa25x_init_pm ( ) ;
2008-01-02 03:24:49 +03:00
2008-01-29 02:00:02 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( pxa25x_sysdev ) ; i + + ) {
ret = sysdev_register ( & pxa25x_sysdev [ i ] ) ;
if ( ret )
pr_err ( " failed to register sysdev[%d] \n " , i ) ;
}
2007-05-15 13:39:49 +04:00
ret = platform_add_devices ( pxa25x_devices ,
ARRAY_SIZE ( pxa25x_devices ) ) ;
2008-01-29 02:00:02 +03:00
if ( ret )
return ret ;
2007-05-15 14:16:10 +04:00
}
2008-01-29 02:00:02 +03:00
2007-05-15 13:39:49 +04:00
/* Only add HWUART for PXA255/26x; PXA210/250/27x do not have it. */
2008-06-26 01:17:16 +04:00
if ( cpu_is_pxa255 ( ) )
2007-07-17 13:45:58 +04:00
ret = platform_device_register ( & pxa_device_hwuart ) ;
2007-05-15 13:39:49 +04:00
return ret ;
2007-05-15 14:16:10 +04:00
}
2008-04-19 13:59:24 +04:00
postcore_initcall ( pxa25x_init ) ;