/*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Copyright (C) 2002 Motorola Semiconductors HK Ltd
*
*/
/******************************************************************************
* Copyright (C) 2002, Motorola Semiconductors H.K Ltd. All Rights Reserved
*
* File Name: apm_Mx1.c
*
* Programers: AlexYu
*
* Date of Creations: 15 Mar,2002
*
* Synopsis:
*
* Descirption:
* Bios-less APM Power Management driver for DBMX1
* adapted from the APM BIOS driver for Linux by Stephen Rothwell (sfr@linuxcare.com)
*
* APM 1.2 Reference:
* Intel Corporation, Microsoft Corporation. Advanced Power Management
* (APM) BIOS Interface Specification, Revision 1.2, February 1996.
*
* [This document is available from Microsoft at:
* http://www.microsoft.com/hwdev/busbios/amp_12.htm]
*
* Modification History:
* 15 Mar, 2002, initialization version
*
* ****************************************************************************/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
//#include <linux/malloc.h> /* kmalloc() */
//To use the new slab.h header for new kernel v2.4.18.
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <asm/page.h>
#include <linux/smp_lock.h>
#include "sysdep.h"
#include "apm_bios.h"
#include "Mx1_def.h"
#include <linux/init.h>
#include <linux/string.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <linux/pm.h>
#include <linux/pci.h>
#include <asm/system.h>
#include <asm/irq.h>
#if 0
#define APM_TEST
#endif
#ifdef LINUX_20
# error "This module can't run with Linux-2.0"
#endif
#ifdef CONFIG_PM
#define PM_OPT " [pm]"
#else
#define PM_OPT ""
#endif
static int apm_major = 0;
MODULE_PARM(apm_major, "i");
MODULE_AUTHOR("Alex Yu");
#undef controlLCD
/*
* The apm_bios device is one of the misc char devices.
* This is its minor number.
*/
#define APM_MINOR_DEV 134
#define _reg_SIC_ID (*((volatile U32 *)(0xf021b804)))
int siliconID = 0;//default is MX1
//#define Corsica_APM
/**************************************************/
//Karen add for test
/**************************************************/
#ifdef KAREN_DEBUG
#define REG_TIMER1_CTL (*((volatile unsigned long*)(IO_ADDRESS(0x00202000))))
#define REG_INTENABLE (*((volatile unsigned long*)(IO_ADDRESS(0x00223010))))
unsigned long timer_regval;
void disabletimer()
{
timer_regval = REG_TIMER1_CTL;
timer_regval &= 0xfffffffe;
REG_TIMER1_CTL = timer_regval;
}
void enabletimer()
{
timer_regval |= 0x00000001;
REG_TIMER1_CTL = timer_regval;
}
#endif
/*
* See Documentation/Config.help for the configuration options.
*
* Various options can be changed at boot time as follows:
* (We allow underscores for compatibility with the modules code)
* apm=on/off enable/disable APM
* [no-]debug log some debugging messages
* [no-]power[-_]off power off on shutdown
*/
#define MAX_ID 200
static int checkDevice(struct inode *pInode)
{
int minor;
kdev_t dev = pInode->i_rdev;
if( MAJOR(dev) != apm_major)
{
printk("<1>checkDevice bad major = %d\n",MAJOR(dev) );
return -1;
}
minor = MINOR(dev);
if ( minor < MAX_ID )
return minor;
else
{
printk("<1>checkDevice bad minor = %d\n",minor );
return -1;
}
}
/*
* Define to always call the APM BIOS busy routine even if the clock was
* not slowed by the idle routine.
*/
#define ALWAYS_CALL_BUSY
/*
* Need to poll the APM BIOS every second
*/
#define APM_CHECK_TIMEOUT (HZ)
/*
* Ignore suspend events for this amount of time after a resume
*/
#define DEFAULT_BOUNCE_INTERVAL (3 * HZ)
/*
* Maximum number of events stored
*/
#define APM_MAX_EVENTS 20
/* Macro for LCD control */
#define VIRT(x) (0xF0000000 | (x))
/*
* The per-file APM data
*/
struct apm_user {
int magic;
struct apm_user * next;
int suser: 1;
int suspend_wait: 1;
int suspend_result;
int suspends_pending;
int standbys_pending;
int suspends_read;
int standbys_read;
int event_head;
int event_tail;
apm_event_t events[APM_MAX_EVENTS];
};
/*
* The magic number in apm_user
*/
#define APM_BIOS_MAGIC 0x4101
static int suspends_pending;
static int standbys_pending;
static int waiting_for_resume;
static int ignore_normal_resume;
static int bounce_interval = DEFAULT_BOUNCE_INTERVAL;
//static long clock_cmos_diff;
//static int got_clock_diff;
static int debug;
static int apm_disabled;
static int power_off = 1;
static int exit_kapmd;
static int kapmd_running;
static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
static struct apm_user * user_list = NULL;
static char driver_version[] = "1.13"; /* no spaces */
#if 0
static char * apm_event_name[] = {
"system standby",
"system suspend",
"normal resume",
"critical resume",
"low battery",
"power status change",
"update time",
"critical suspend",
"user standby",
"user suspend",
"system standby resume",
"capabilities change"
};
#endif
#define NR_APM_EVENT_NAME (sizeof(apm_event_name)/ sizeof(apm_event_name[0]))
//alexyu
/* Below are the global variable to replace the BIOS call.*/
/* APM BIOS info */
apm_bios_info_t apm_bios_info;
/* Register to store speed info. */
u32 CPUSpeed_reg;
/* apm EVENT status from BIOS */
apm_event_t apm_event;
apm_eventinfo_t apm_eventinfo;
/* apm devices states read from BIOS */
u_short apm_state;
typedef struct lookup_t {
int key;
char * msg;
} lookup_t;
static const lookup_t error_table[] = {
/* N/A { APM_SUCCESS, "Operation succeeded" }, */
{ APM_DISABLED, "Power management disabled" },
{ APM_CONNECTED, "Real mode interface already connected" },
{ APM_NOT_CONNECTED, "Interface not connected" },
{ APM_16_CONNECTED, "16 bit interface already connected" },
/* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */
{ APM_32_CONNECTED, "32 bit interface already connected" },
{ APM_32_UNSUPPORTED, "32 bit interface not supported" },
{ APM_BAD_DEVICE, "Unrecognized device ID" },
{ APM_BAD_PARAM, "Parameter out of range" },
{ APM_NOT_ENGAGED, "Interface not engaged" },
{ APM_BAD_FUNCTION, "Function not supported" },
{ APM_RESUME_DISABLED, "Resume timer disabled" },
{ APM_BAD_STATE, "Unable to enter requested state" },
/* N/A { APM_NO_EVENTS, "No events pending" }, */
{ APM_NO_ERROR, "BIOS did not set a return code" },
{ APM_NOT_PRESENT, "No APM present" }
};
#define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t))
// IRQ handling function for the ARM core.
// Enable IRQ
void EnableIRQ (void)
{
__asm__ ("
MRS r1, CPSR
BIC r1, r1, #0x80
MSR CPSR_c, r1
");
}
// Disable IRQ
void DisableIRQ (void)
{
__asm__ ("
MRS r1,