2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / mach - pxa / pxa27x . c
*
* Author : Nicolas Pitre
* Created : Nov 05 , 2002
* Copyright : MontaVista Software Inc .
*
* Code specific to PXA27x aka Bulverde .
*
* 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/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
2007-10-18 14:04:39 +04:00
# include <linux/suspend.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2008-01-29 02:00:02 +03:00
# include <linux/sysdev.h>
2005-04-17 02:20:36 +04:00
# include <asm/hardware.h>
# include <asm/irq.h>
2007-06-22 07:14:09 +04:00
# include <asm/arch/irqs.h>
2005-04-17 02:20:36 +04:00
# include <asm/arch/pxa-regs.h>
2008-01-14 20:02:33 +03:00
# include <asm/arch/pxa2xx-regs.h>
2005-11-12 17:22:11 +03:00
# include <asm/arch/ohci.h>
2007-05-15 14:16:10 +04:00
# include <asm/arch/pm.h>
2007-06-22 08:40:17 +04:00
# include <asm/arch/dma.h>
2008-01-27 20:14:50 +03:00
# include <asm/arch/i2c.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
/* Crystal clock: 13MHz */
# define BASE_CLK 13000000
/*
* Get the clock frequency as reflected by CCSR 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 pxa27x_get_clk_frequency_khz ( int info )
2005-04-17 02:20:36 +04:00
{
unsigned long ccsr , clkcfg ;
unsigned int l , L , m , M , n2 , N , S ;
int cccr_a , t , ht , b ;
ccsr = CCSR ;
cccr_a = CCCR & ( 1 < < 25 ) ;
/* Read clkcfg register: it has turbo, b, half-turbo (and f) */
asm ( " mrc \t p14, 0, %0, c6, c0, 0 " : " =r " ( clkcfg ) ) ;
2006-02-01 22:25:59 +03:00
t = clkcfg & ( 1 < < 0 ) ;
2005-04-17 02:20:36 +04:00
ht = clkcfg & ( 1 < < 2 ) ;
b = clkcfg & ( 1 < < 3 ) ;
l = ccsr & 0x1f ;
n2 = ( ccsr > > 7 ) & 0xf ;
m = ( l < = 10 ) ? 1 : ( l < = 20 ) ? 2 : 4 ;
L = l * BASE_CLK ;
N = ( L * n2 ) / 2 ;
M = ( ! cccr_a ) ? ( L / m ) : ( ( b ) ? L : ( L / 2 ) ) ;
S = ( b ) ? L : ( L / 2 ) ;
if ( info ) {
printk ( KERN_INFO " Run Mode clock: %d.%02dMHz (*%d) \n " ,
L / 1000000 , ( L % 1000000 ) / 10000 , l ) ;
printk ( KERN_INFO " Turbo Mode clock: %d.%02dMHz (*%d.%d, %sactive) \n " ,
N / 1000000 , ( N % 1000000 ) / 10000 , n2 / 2 , ( n2 % 2 ) * 5 ,
( t ) ? " " : " in " ) ;
printk ( KERN_INFO " Memory clock: %d.%02dMHz (/%d) \n " ,
M / 1000000 , ( M % 1000000 ) / 10000 , m ) ;
printk ( KERN_INFO " System bus clock: %d.%02dMHz \n " ,
S / 1000000 , ( S % 1000000 ) / 10000 ) ;
}
return ( t ) ? ( N / 1000 ) : ( L / 1000 ) ;
}
/*
* Return the current mem clock frequency in units of 10 kHz as
* reflected by CCCR [ A ] , B , and L
*/
2007-08-20 13:07:44 +04:00
unsigned int pxa27x_get_memclk_frequency_10khz ( void )
2005-04-17 02:20:36 +04:00
{
unsigned long ccsr , clkcfg ;
unsigned int l , L , m , M ;
int cccr_a , b ;
ccsr = CCSR ;
cccr_a = CCCR & ( 1 < < 25 ) ;
/* Read clkcfg register: it has turbo, b, half-turbo (and f) */
asm ( " mrc \t p14, 0, %0, c6, c0, 0 " : " =r " ( clkcfg ) ) ;
b = clkcfg & ( 1 < < 3 ) ;
l = ccsr & 0x1f ;
m = ( l < = 10 ) ? 1 : ( l < = 20 ) ? 2 : 4 ;
L = l * BASE_CLK ;
M = ( ! cccr_a ) ? ( L / m ) : ( ( b ) ? L : ( L / 2 ) ) ;
return ( M / 10000 ) ;
}
/*
* Return the current LCD clock frequency in units of 10 kHz as
*/
2007-08-20 13:34:37 +04:00
static unsigned int pxa27x_get_lcdclk_frequency_10khz ( void )
2005-04-17 02:20:36 +04:00
{
unsigned long ccsr ;
unsigned int l , L , k , K ;
ccsr = CCSR ;
l = ccsr & 0x1f ;
k = ( l < = 7 ) ? 1 : ( l < = 16 ) ? 2 : 4 ;
L = l * BASE_CLK ;
K = L / k ;
return ( K / 10000 ) ;
}
2007-08-20 13:18:02 +04:00
static unsigned long clk_pxa27x_lcd_getrate ( struct clk * clk )
{
return pxa27x_get_lcdclk_frequency_10khz ( ) * 10000 ;
}
static const struct clkops clk_pxa27x_lcd_ops = {
. enable = clk_cken_enable ,
. disable = clk_cken_disable ,
. getrate = clk_pxa27x_lcd_getrate ,
} ;
static struct clk pxa27x_clks [ ] = {
INIT_CK ( " LCDCLK " , LCD , & clk_pxa27x_lcd_ops , & pxa_device_fb . dev ) ,
INIT_CK ( " CAMCLK " , CAMERA , & clk_pxa27x_lcd_ops , NULL ) ,
INIT_CKEN ( " UARTCLK " , FFUART , 14857000 , 1 , & pxa_device_ffuart . dev ) ,
INIT_CKEN ( " UARTCLK " , BTUART , 14857000 , 1 , & pxa_device_btuart . dev ) ,
2007-09-02 20:08:42 +04:00
INIT_CKEN ( " UARTCLK " , STUART , 14857000 , 1 , NULL ) ,
2007-08-20 13:18:02 +04:00
INIT_CKEN ( " I2SCLK " , I2S , 14682000 , 0 , & pxa_device_i2s . dev ) ,
INIT_CKEN ( " I2CCLK " , I2C , 32842000 , 0 , & pxa_device_i2c . dev ) ,
INIT_CKEN ( " UDCCLK " , USB , 48000000 , 5 , & pxa_device_udc . dev ) ,
INIT_CKEN ( " MMCCLK " , MMC , 19500000 , 0 , & pxa_device_mci . dev ) ,
INIT_CKEN ( " FICPCLK " , FICP , 48000000 , 0 , & pxa_device_ficp . dev ) ,
2007-11-20 03:35:08 +03:00
INIT_CKEN ( " USBCLK " , USBHOST , 48000000 , 0 , & pxa27x_device_ohci . dev ) ,
2007-08-20 13:18:02 +04:00
INIT_CKEN ( " I2CCLK " , PWRI2C , 13000000 , 0 , & pxa27x_device_i2c_power . dev ) ,
INIT_CKEN ( " KBDCLK " , KEYPAD , 32768 , 0 , NULL ) ,
2007-12-10 12:54:36 +03:00
INIT_CKEN ( " SSPCLK " , SSP1 , 13000000 , 0 , & pxa27x_device_ssp1 . dev ) ,
INIT_CKEN ( " SSPCLK " , SSP2 , 13000000 , 0 , & pxa27x_device_ssp2 . dev ) ,
INIT_CKEN ( " SSPCLK " , SSP3 , 13000000 , 0 , & pxa27x_device_ssp3 . dev ) ,
2008-03-04 13:14:22 +03:00
INIT_CKEN ( " AC97CLK " , AC97 , 24576000 , 0 , NULL ) ,
INIT_CKEN ( " AC97CONFCLK " , AC97CONF , 24576000 , 0 , NULL ) ,
2007-08-20 13:18:02 +04:00
/*
INIT_CKEN ( " PWMCLK " , PWM0 , 13000000 , 0 , NULL ) ,
INIT_CKEN ( " MSLCLK " , MSL , 48000000 , 0 , NULL ) ,
INIT_CKEN ( " USIMCLK " , USIM , 48000000 , 0 , NULL ) ,
INIT_CKEN ( " MSTKCLK " , MEMSTK , 19500000 , 0 , NULL ) ,
INIT_CKEN ( " IMCLK " , IM , 0 , 0 , NULL ) ,
INIT_CKEN ( " MEMCLK " , MEMC , 0 , 0 , NULL ) ,
*/
} ;
2005-06-14 01:35:41 +04:00
# ifdef CONFIG_PM
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 .
*/
enum { SLEEP_SAVE_START = 0 ,
SLEEP_SAVE_PGSR0 , SLEEP_SAVE_PGSR1 , SLEEP_SAVE_PGSR2 , SLEEP_SAVE_PGSR3 ,
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_GAFR3_L , SLEEP_SAVE_GAFR3_U ,
SLEEP_SAVE_PSTR ,
SLEEP_SAVE_CKEN ,
SLEEP_SAVE_MDREFR ,
SLEEP_SAVE_PWER , SLEEP_SAVE_PCFR , SLEEP_SAVE_PRER ,
SLEEP_SAVE_PFER , SLEEP_SAVE_PKWR ,
SLEEP_SAVE_SIZE
} ;
void pxa27x_cpu_pm_save ( unsigned long * sleep_save )
{
SAVE ( PGSR0 ) ; SAVE ( PGSR1 ) ; SAVE ( PGSR2 ) ; SAVE ( PGSR3 ) ;
SAVE ( GAFR0_L ) ; SAVE ( GAFR0_U ) ;
SAVE ( GAFR1_L ) ; SAVE ( GAFR1_U ) ;
SAVE ( GAFR2_L ) ; SAVE ( GAFR2_U ) ;
SAVE ( GAFR3_L ) ; SAVE ( GAFR3_U ) ;
SAVE ( MDREFR ) ;
SAVE ( PWER ) ; SAVE ( PCFR ) ; SAVE ( PRER ) ;
SAVE ( PFER ) ; SAVE ( PKWR ) ;
SAVE ( CKEN ) ;
SAVE ( PSTR ) ;
}
void pxa27x_cpu_pm_restore ( unsigned long * sleep_save )
{
/* ensure not to come back here if it wasn't intended */
PSPR = 0 ;
/* restore registers */
RESTORE ( GAFR0_L ) ; RESTORE ( GAFR0_U ) ;
RESTORE ( GAFR1_L ) ; RESTORE ( GAFR1_U ) ;
RESTORE ( GAFR2_L ) ; RESTORE ( GAFR2_U ) ;
RESTORE ( GAFR3_L ) ; RESTORE ( GAFR3_U ) ;
RESTORE ( PGSR0 ) ; RESTORE ( PGSR1 ) ; RESTORE ( PGSR2 ) ; RESTORE ( PGSR3 ) ;
RESTORE ( MDREFR ) ;
RESTORE ( PWER ) ; RESTORE ( PCFR ) ; RESTORE ( PRER ) ;
RESTORE ( PFER ) ; RESTORE ( PKWR ) ;
PSSR = PSSR_RDH | PSSR_PH ;
RESTORE ( CKEN ) ;
RESTORE ( PSTR ) ;
}
void pxa27x_cpu_pm_enter ( suspend_state_t state )
2005-06-03 23:52:27 +04:00
{
extern void pxa_cpu_standby ( void ) ;
/* ensure voltage-change sequencer not initiated, which hangs */
PCFR & = ~ PCFR_FVC ;
/* Clear edge-detect status register. */
PEDR = 0xDF12FE1B ;
switch ( state ) {
2005-07-01 14:27:05 +04:00
case PM_SUSPEND_STANDBY :
pxa_cpu_standby ( ) ;
break ;
2005-06-03 23:52:27 +04:00
case PM_SUSPEND_MEM :
/* set resume return address */
PSPR = virt_to_phys ( pxa_cpu_resume ) ;
2007-07-18 14:40:13 +04:00
pxa27x_cpu_suspend ( PWRMODE_SLEEP ) ;
2005-06-03 23:52:27 +04:00
break ;
}
}
2005-04-17 02:20:36 +04:00
2007-07-18 14:38:45 +04:00
static int pxa27x_cpu_pm_valid ( suspend_state_t state )
2007-05-15 14:22:48 +04:00
{
return state = = PM_SUSPEND_MEM | | state = = PM_SUSPEND_STANDBY ;
}
2007-07-18 14:38:45 +04:00
static struct pxa_cpu_pm_fns pxa27x_cpu_pm_fns = {
. save_size = SLEEP_SAVE_SIZE ,
. save = pxa27x_cpu_pm_save ,
. restore = pxa27x_cpu_pm_restore ,
. valid = pxa27x_cpu_pm_valid ,
. enter = pxa27x_cpu_pm_enter ,
2007-05-15 14:16:10 +04:00
} ;
2007-07-18 14:38:45 +04:00
static void __init pxa27x_init_pm ( void )
{
pxa_cpu_pm_fns = & pxa27x_cpu_pm_fns ;
}
2008-01-02 03:24:49 +03:00
# else
static inline void pxa27x_init_pm ( void ) { }
2005-06-14 01:35:41 +04:00
# endif
2007-08-29 13:22:17 +04:00
/* PXA27x: Various gpios can issue wakeup events. This logic only
* handles the simple cases , not the WEMUX2 and WEMUX3 options
*/
# define PXA27x_GPIO_NOWAKE_MASK \
( ( 1 < < 8 ) | ( 1 < < 7 ) | ( 1 < < 6 ) | ( 1 < < 5 ) | ( 1 < < 2 ) )
# define WAKEMASK(gpio) \
( ( ( gpio ) < = 15 ) \
? ( ( 1 < < ( gpio ) ) & ~ PXA27x_GPIO_NOWAKE_MASK ) \
: ( ( gpio = = 35 ) ? ( 1 < < 24 ) : 0 ) )
static int pxa27x_set_wake ( unsigned int irq , unsigned int on )
{
int gpio = IRQ_TO_GPIO ( irq ) ;
uint32_t mask ;
if ( ( gpio > = 0 & & gpio < = 15 ) | | ( gpio = = 35 ) ) {
if ( WAKEMASK ( gpio ) = = 0 )
return - EINVAL ;
mask = WAKEMASK ( gpio ) ;
if ( on ) {
if ( GRER ( gpio ) | GPIO_bit ( gpio ) )
PRER | = mask ;
else
PRER & = ~ mask ;
if ( GFER ( gpio ) | GPIO_bit ( gpio ) )
PFER | = mask ;
else
PFER & = ~ mask ;
}
goto set_pwer ;
}
switch ( irq ) {
case IRQ_RTCAlrm :
mask = PWER_RTC ;
break ;
case IRQ_USB :
mask = 1u < < 26 ;
break ;
default :
return - EINVAL ;
}
set_pwer :
if ( on )
PWER | = mask ;
else
PWER & = ~ mask ;
return 0 ;
}
void __init pxa27x_init_irq ( void )
{
2008-03-04 09:19:58 +03:00
pxa_init_irq ( 34 , pxa27x_set_wake ) ;
pxa_init_gpio ( 128 , pxa27x_set_wake ) ;
2007-08-29 13:22:17 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* device registration specific to PXA27x .
*/
2007-05-15 13:39:49 +04:00
static struct resource i2c_power_resources [ ] = {
{
. start = 0x40f00180 ,
. end = 0x40f001a3 ,
. flags = IORESOURCE_MEM ,
} , {
. start = IRQ_PWRI2C ,
. end = IRQ_PWRI2C ,
. flags = IORESOURCE_IRQ ,
} ,
} ;
2007-08-20 13:09:18 +04:00
struct platform_device pxa27x_device_i2c_power = {
2007-05-15 13:39:49 +04:00
. name = " pxa2xx-i2c " ,
. id = 1 ,
. resource = i2c_power_resources ,
. num_resources = ARRAY_SIZE ( i2c_power_resources ) ,
} ;
2008-01-27 20:14:50 +03:00
void __init pxa_set_i2c_power_info ( struct i2c_pxa_platform_data * info )
{
pxa27x_device_i2c_power . dev . platform_data = info ;
}
2005-04-17 02:20:36 +04:00
static struct platform_device * devices [ ] __initdata = {
2007-07-17 13:45:58 +04:00
& pxa_device_udc ,
& pxa_device_ffuart ,
& pxa_device_btuart ,
& pxa_device_stuart ,
& pxa_device_i2s ,
& pxa_device_rtc ,
& pxa27x_device_i2c_power ,
2007-12-10 12:54:36 +03:00
& pxa27x_device_ssp1 ,
& pxa27x_device_ssp2 ,
& pxa27x_device_ssp3 ,
2005-04-17 02:20:36 +04:00
} ;
2008-01-29 02:00:02 +03:00
static struct sys_device pxa27x_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
} ,
} ;
2005-04-17 02:20:36 +04:00
static int __init pxa27x_init ( void )
{
2008-01-29 02:00:02 +03:00
int i , ret = 0 ;
2007-05-15 14:16:10 +04:00
if ( cpu_is_pxa27x ( ) ) {
2007-08-20 13:18:02 +04:00
clks_register ( pxa27x_clks , ARRAY_SIZE ( pxa27x_clks ) ) ;
2007-06-22 08:40:17 +04:00
if ( ( ret = pxa_init_dma ( 32 ) ) )
return ret ;
2008-01-02 03:24:49 +03:00
2007-07-18 14:38:45 +04:00
pxa27x_init_pm ( ) ;
2008-01-02 03:24:49 +03:00
2008-01-29 02:00:02 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( pxa27x_sysdev ) ; i + + ) {
ret = sysdev_register ( & pxa27x_sysdev [ i ] ) ;
if ( ret )
pr_err ( " failed to register sysdev[%d] \n " , i ) ;
}
2007-05-15 14:16:10 +04:00
ret = platform_add_devices ( devices , ARRAY_SIZE ( devices ) ) ;
}
2008-01-29 02:00:02 +03:00
2007-05-15 14:16:10 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
subsys_initcall ( pxa27x_init ) ;