/*
* linux/arch/arm/mach-pxa/viper.c
*
* Support for the Arcom VIPER SBC.
*
* Author: Ian Campbell
* Created: Feb 03, 2003
* Copyright: Arcom Control Systems
*
* Maintained by Marc Zyngier <maz@misterjones.org>
* <marc.zyngier@altran.com>
*
* Based on lubbock.c:
* Author: Nicolas Pitre
* Created: Jun 15, 2001
* Copyright: MontaVista Software Inc.
*
* 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/types.h>
#include <linux/memory.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/major.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/sched.h>
#include <linux/gpio.h>
#include <linux/jiffies.h>
#include <linux/i2c-gpio.h>
#include <linux/i2c/pxa-i2c.h>
#include <linux/serial_8250.h>
#include <linux/smc91x.h>
#include <linux/pwm_backlight.h>
#include <linux/usb/isp116x.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
#include <linux/syscore_ops.h>
#include <mach/pxa25x.h>
#include <mach/audio.h>
#include <linux/platform_data/video-pxafb.h>
#include <mach/regs-uart.h>
#include <linux/platform_data/pcmcia-pxa2xx_viper.h>
#include <mach/viper.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/irq.h>
#include <asm/sizes.h>
#include <asm/system_info.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/irq.h>
#include "generic.h"
#include "devices.h"
static unsigned int icr;
static void viper_icr_set_bit(unsigned int bit)
{
icr |= bit;
VIPER_ICR = icr;
}
static void viper_icr_clear_bit(unsigned int bit)
{
icr &= ~bit;
VIPER_ICR = icr;
}
/* This function is used from the pcmcia module to reset the CF */
static void viper_cf_reset(int state)
{
if (state)
viper_icr_set_bit(VIPER_ICR_CF_RST);
else
viper_icr_clear_bit(VIPER_ICR_CF_RST);
}
static struct arcom_pcmcia_pdata viper_pcmcia_info = {
.cd_gpio = VIPER_CF_CD_GPIO,
.rdy_gpio = VIPER_CF_RDY_GPIO,
.pwr_gpio = VIPER_CF_POWER_GPIO,
.reset = viper_cf_reset,
};
static struct platform_device viper_pcmcia_device = {
.name = "viper-pcmcia",
.id = -1,
.dev = {
.platform_data = &viper_pcmcia_info,
},
};
/*
* The CPLD version register was not present on VIPER boards prior to
* v2i1. On v1 boards where the version register is not present we
* will just read back the previous value from the databus.
*
* Therefore we do two reads. The first time we write 0 to the
* (read-only) register before reading and the second time we write
* 0xff first. If the two reads do not match or they read back as 0xff
* or 0x00 then we have version 1 hardware.
*/
static u8 viper_hw_version(void)
{
u8 v1, v2;
unsigned long flags;
local_irq_save(flags);
VIPER_VERSION = 0;
v1 = VIPER_VERSION;
VIPER_VERSION = 0xff;
v2 = VIPER_VERSION;
v1 = (v1 != v2 || v1 == 0xff) ? 0 : v1;
local_irq_restore(flags);
return v1;
}
/* CPU system core operations. */
static int viper_cpu_suspend(void)
{
viper_icr_set_bit(VIPER_ICR_R_DIS);
return 0;
}
static void viper_cpu_resume(void)
{
viper_icr_clear_bit(VIPER_ICR_R_DIS);
}
static struct syscore_ops viper_cpu_syscore_ops = {
.suspend = viper_cpu_suspend,
.resume = viper_cpu_resume,
};
static unsigned int current_voltage_divisor;
/*
* If force is not true then step from existing to new divisor. If
* force is true then jump straight to the new divisor. Stepping is
* used because if the jump in voltage is too large, the VCC can dip
* too low and the regulator cuts out.
*
* force can be used to initialize the divisor to a know state by
* setting the value for the current clock speed, since we are already
* running at that speed we know the voltage should be pretty close so
* the jump won't be too large
*/
static void viper_set_core_cpu_voltage(unsigned long khz, int force)
{
int i = 0;
unsigned int divisor = 0;
const char *v;
if (khz < 200000) {
v = "1.0"; divisor = 0xfff;
} else if (khz < 300000) {
v = "1.1"; divisor = 0xde5;
} else {
v = "1.3"; divisor = 0x325;
}
pr_debug("viper: setting CPU core voltage to %sV at %d.%03dMHz\n",
v, (int)khz / 1000, (int)khz % 1000);
#define STEP 0x100
do {
int step;
if (force)
step = divisor;
else if (current_voltage_divisor < divisor - STEP)
step = current_voltage_divisor + STEP;
else if (current_voltage_divisor > divisor + STEP)
step = current_voltage_divisor - STEP;
else
step = divisor;
force = 0