2005-04-17 06:20:36 +08: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>
|
|
|
|
#include <linux/pm.h>
|
2005-10-30 02:07:23 +08:00
|
|
|
#include <linux/platform_device.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <asm/hardware.h>
|
|
|
|
#include <asm/irq.h>
|
|
|
|
#include <asm/arch/pxa-regs.h>
|
2005-11-12 22:22:11 +08:00
|
|
|
#include <asm/arch/ohci.h>
|
2007-05-15 18:16:10 +08:00
|
|
|
#include <asm/arch/pm.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include "generic.h"
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
unsigned int get_clk_frequency_khz( int info)
|
|
|
|
{
|
|
|
|
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\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg) );
|
2006-02-02 03:25:59 +08:00
|
|
|
t = clkcfg & (1 << 0);
|
2005-04-17 06:20:36 +08: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 10kHz as
|
|
|
|
* reflected by CCCR[A], B, and L
|
|
|
|
*/
|
|
|
|
unsigned int get_memclk_frequency_10khz(void)
|
|
|
|
{
|
|
|
|
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\tp14, 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 10kHz as
|
|
|
|
*/
|
|
|
|
unsigned int get_lcdclk_frequency_10khz(void)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL(get_clk_frequency_khz);
|
|
|
|
EXPORT_SYMBOL(get_memclk_frequency_10khz);
|
|
|
|
EXPORT_SYMBOL(get_lcdclk_frequency_10khz);
|
|
|
|
|
2005-06-14 05:35:41 +08:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
|
2005-06-04 03:52:27 +08:00
|
|
|
void pxa_cpu_pm_enter(suspend_state_t state)
|
|
|
|
{
|
|
|
|
extern void pxa_cpu_standby(void);
|
|
|
|
extern void pxa_cpu_suspend(unsigned int);
|
|
|
|
extern void pxa_cpu_resume(void);
|
|
|
|
|
2005-07-01 18:27:05 +08:00
|
|
|
if (state == PM_SUSPEND_STANDBY)
|
2007-07-02 17:19:07 +08:00
|
|
|
CKEN = (1 << CKEN_MEMC) | (1 << CKEN_OSTIMER) | (1 << CKEN_LCD) | (1 << CKEN_PWM0);
|
2005-07-01 18:27:05 +08:00
|
|
|
else
|
2007-07-02 17:19:07 +08:00
|
|
|
CKEN = (1 << CKEN_MEMC) | (1 << CKEN_OSTIMER);
|
2005-06-04 03:52:27 +08:00
|
|
|
|
|
|
|
/* ensure voltage-change sequencer not initiated, which hangs */
|
|
|
|
PCFR &= ~PCFR_FVC;
|
|
|
|
|
|
|
|
/* Clear edge-detect status register. */
|
|
|
|
PEDR = 0xDF12FE1B;
|
|
|
|
|
|
|
|
switch (state) {
|
2005-07-01 18:27:05 +08:00
|
|
|
case PM_SUSPEND_STANDBY:
|
|
|
|
pxa_cpu_standby();
|
|
|
|
break;
|
2005-06-04 03:52:27 +08:00
|
|
|
case PM_SUSPEND_MEM:
|
|
|
|
/* set resume return address */
|
|
|
|
PSPR = virt_to_phys(pxa_cpu_resume);
|
2005-10-28 23:25:01 +08:00
|
|
|
pxa_cpu_suspend(PWRMODE_SLEEP);
|
2005-06-04 03:52:27 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-05-15 18:22:48 +08:00
|
|
|
static int pxa27x_pm_valid(suspend_state_t state)
|
|
|
|
{
|
|
|
|
return state == PM_SUSPEND_MEM || state == PM_SUSPEND_STANDBY;
|
|
|
|
}
|
|
|
|
|
2007-05-15 18:16:10 +08:00
|
|
|
static struct pm_ops pxa27x_pm_ops = {
|
|
|
|
.enter = pxa_pm_enter,
|
2007-05-15 18:22:48 +08:00
|
|
|
.valid = pxa27x_pm_valid,
|
2007-05-15 18:16:10 +08:00
|
|
|
};
|
2005-06-14 05:35:41 +08:00
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* device registration specific to PXA27x.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static u64 pxa27x_dmamask = 0xffffffffUL;
|
|
|
|
|
|
|
|
static struct resource pxa27x_ohci_resources[] = {
|
|
|
|
[0] = {
|
|
|
|
.start = 0x4C000000,
|
|
|
|
.end = 0x4C00ff6f,
|
|
|
|
.flags = IORESOURCE_MEM,
|
|
|
|
},
|
|
|
|
[1] = {
|
|
|
|
.start = IRQ_USBH1,
|
|
|
|
.end = IRQ_USBH1,
|
|
|
|
.flags = IORESOURCE_IRQ,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct platform_device ohci_device = {
|
|
|
|
.name = "pxa27x-ohci",
|
|
|
|
.id = -1,
|
|
|
|
.dev = {
|
|
|
|
.dma_mask = &pxa27x_dmamask,
|
|
|
|
.coherent_dma_mask = 0xffffffff,
|
|
|
|
},
|
|
|
|
.num_resources = ARRAY_SIZE(pxa27x_ohci_resources),
|
|
|
|
.resource = pxa27x_ohci_resources,
|
|
|
|
};
|
|
|
|
|
2005-11-12 22:22:11 +08:00
|
|
|
void __init pxa_set_ohci_info(struct pxaohci_platform_data *info)
|
|
|
|
{
|
|
|
|
ohci_device.dev.platform_data = info;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct platform_device *devices[] __initdata = {
|
|
|
|
&ohci_device,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init pxa27x_init(void)
|
|
|
|
{
|
2007-05-15 18:16:10 +08:00
|
|
|
int ret = 0;
|
|
|
|
if (cpu_is_pxa27x()) {
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
pm_set_ops(&pxa27x_pm_ops);
|
|
|
|
#endif
|
|
|
|
ret = platform_add_devices(devices, ARRAY_SIZE(devices));
|
|
|
|
}
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
subsys_initcall(pxa27x_init);
|