Commit 3ada9612 authored by Eric Brower's avatar Eric Brower Committed by David S. Miller

Add Sparc Voyager power management support.

parent 2e1e23d5
......@@ -34,6 +34,11 @@ CONFIG_SPARC32
maintains both the SPARC32 and SPARC64 ports; its web page is
available at <http://www.ultralinux.org/>.
SPARC power management support
CONFIG_SUN_PM
Enable power management and CPU standby features on supported
SPARC platforms.
CONFIG_BLK_DEV_FD
If you want to use the floppy disk drive(s) of your PC under Linux,
say Y. Information about this driver, especially important for IBM
......
......@@ -38,6 +38,7 @@ define_bool CONFIG_SUN_AUXIO y
define_bool CONFIG_SUN_IO y
define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y
define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n
define_bool CONFIG_SUN_PM y
bool 'Support for SUN4 machines (disables SUN4[CDM] support)' CONFIG_SUN4
if [ "$CONFIG_SUN4" != "y" ]; then
......
......@@ -48,6 +48,7 @@ CONFIG_SUN_AUXIO=y
CONFIG_SUN_IO=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
CONFIG_SUN_PM=y
# CONFIG_SUN4 is not set
# CONFIG_PCI is not set
CONFIG_SUN_OPENPROMFS=m
......
......@@ -32,6 +32,7 @@ obj-$(CONFIG_SUN4) += sun4setup.o
obj-$(CONFIG_SMP) += trampoline.o smp.o sun4m_smp.o sun4d_smp.o
obj-$(CONFIG_SUN_AUXIO) += auxio.o
obj-$(CONFIG_PCI) += ebus.o
obj-$(CONFIG_SUN_PM) += apc.o pmc.o
ifdef CONFIG_SUNOS_EMUL
obj-y += sys_sunos.o sunos_ioctl.o
......
/* $Id$
*
* apc - Driver implementation for power management functions
* of Aurora Personality Chip (APC) on SPARCstation-4/5 and
* derivatives.
*
* Copyright (c) 2002 Eric Brower (ebrower@usa.net)
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/pm.h>
#include <asm/io.h>
#include <asm/sbus.h>
#include <asm/oplib.h>
#include <asm/uaccess.h>
#include <asm/auxio.h>
#include <asm/apc.h>
/* Debugging
*
* #define APC_DEBUG_LED
* #define APC_NO_IDLE
*/
#define APC_MINOR MISC_DYNAMIC_MINOR
#define APC_OBPNAME "power-management"
#define APC_DEVNAME "apc"
volatile static u8 *regs;
static int apc_regsize;
#define apc_readb(offs) (sbus_readb(regs+offs))
#define apc_writeb(val, offs) (sbus_writeb(val, regs+offs))
/*
* CPU idle callback function
* See .../arch/sparc/kernel/process.c
*/
void apc_swift_idle(void)
{
#ifdef APC_DEBUG_LED
set_auxio(0x00, AUXIO_LED);
#endif
apc_writeb(apc_readb(APC_IDLE_REG) | APC_IDLE_ON, APC_IDLE_REG);
#ifdef APC_DEBUG_LED
set_auxio(AUXIO_LED, 0x00);
#endif
}
static inline void apc_free(void)
{
sbus_iounmap((unsigned long)regs, apc_regsize);
}
static int apc_open(struct inode *inode, struct file *f)
{
return 0;
}
static int apc_release(struct inode *inode, struct file *f)
{
return 0;
}
static int apc_ioctl(struct inode *inode, struct file *f,
unsigned int cmd, unsigned long arg)
{
__u8 inarg;
switch (cmd) {
case APCIOCGFANCTL:
if(put_user(apc_readb(APC_FANCTL_REG) & APC_REGMASK, (__u8*) arg)) {
return -EFAULT;
}
break;
case APCIOCGCPWR:
if(put_user(apc_readb(APC_CPOWER_REG) & APC_REGMASK, (__u8*) arg)) {
return -EFAULT;
}
break;
case APCIOCGBPORT:
if(put_user(apc_readb(APC_BPORT_REG) & APC_BPMASK, (__u8*) arg)) {
return -EFAULT;
}
break;
case APCIOCSFANCTL:
if(get_user(inarg, (__u8*) arg)) {
return -EFAULT;
}
apc_writeb(inarg & APC_REGMASK, APC_FANCTL_REG);
break;
case APCIOCSCPWR:
if(get_user(inarg, (__u8*) arg)) {
return -EFAULT;
}
apc_writeb(inarg & APC_REGMASK, APC_CPOWER_REG);
break;
case APCIOCSBPORT:
if(get_user(inarg, (__u8*) arg)) {
return -EFAULT;
}
apc_writeb(inarg & APC_BPMASK, APC_BPORT_REG);
break;
default:
return -EINVAL;
};
return 0;
}
static struct file_operations apc_fops = {
ioctl: apc_ioctl,
open: apc_open,
release: apc_release,
};
static struct miscdevice apc_miscdev = { APC_MINOR, APC_DEVNAME, &apc_fops };
static int __init apc_probe(void)
{
struct sbus_bus *sbus = NULL;
struct sbus_dev *sdev = NULL;
int iTmp = 0;
for_each_sbus(sbus) {
for_each_sbusdev(sdev, sbus) {
if (!strcmp(sdev->prom_name, APC_OBPNAME)) {
goto sbus_done;
}
}
}
sbus_done:
if (!sdev) {
return -ENODEV;
}
apc_regsize = sdev->reg_addrs[0].reg_size;
regs = (u8*) sbus_ioremap(&sdev->resource[0], 0,
apc_regsize, APC_OBPNAME);
if(NULL == regs) {
printk(KERN_ERR "%s: unable to map registers\n", APC_DEVNAME);
return -ENODEV;
}
iTmp = misc_register(&apc_miscdev);
if (iTmp != 0) {
printk(KERN_ERR "%s: unable to register device\n", APC_DEVNAME);
apc_free();
return -ENODEV;
}
#ifndef APC_NO_IDLE
/* Assign power management IDLE handler */
pm_idle = apc_swift_idle;
#endif
printk(KERN_INFO "%s: power management initialized\n", APC_DEVNAME);
return 0;
}
/* This driver is not critical to the boot process
* and is easiest to ioremap when SBus is already
* initialized, so we install ourselves thusly:
*/
__initcall(apc_probe);
/* $Id$
*
* pmc - Driver implementation for power management functions
* of Power Management Controller (PMC) on SPARCstation-Voyager.
*
* Copyright (c) 2002 Eric Brower (ebrower@usa.net)
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/pm.h>
#include <asm/io.h>
#include <asm/sbus.h>
#include <asm/oplib.h>
#include <asm/uaccess.h>
#include <asm/auxio.h>
/* Debug
*
* #define PMC_DEBUG_LED
* #define PMC_NO_IDLE
*/
#define PMC_MINOR MISC_DYNAMIC_MINOR
#define PMC_OBPNAME "SUNW,pmc"
#define PMC_DEVNAME "pmc"
#define PMC_IDLE_REG 0x00
#define PMC_IDLE_ON 0x01
volatile static u8 *regs;
static int pmc_regsize;
#define pmc_readb(offs) (sbus_readb(regs+offs))
#define pmc_writeb(val, offs) (sbus_writeb(val, regs+offs))
/*
* CPU idle callback function
* See .../arch/sparc/kernel/process.c
*/
void pmc_swift_idle(void)
{
#ifdef PMC_DEBUG_LED
set_auxio(0x00, AUXIO_LED);
#endif
pmc_writeb(pmc_readb(PMC_IDLE_REG) | PMC_IDLE_ON, PMC_IDLE_REG);
#ifdef PMC_DEBUG_LED
set_auxio(AUXIO_LED, 0x00);
#endif
}
static inline void pmc_free(void)
{
sbus_iounmap((unsigned long)regs, pmc_regsize);
}
static int __init pmc_probe(void)
{
struct sbus_bus *sbus = NULL;
struct sbus_dev *sdev = NULL;
for_each_sbus(sbus) {
for_each_sbusdev(sdev, sbus) {
if (!strcmp(sdev->prom_name, PMC_OBPNAME)) {
goto sbus_done;
}
}
}
sbus_done:
if (!sdev) {
return -ENODEV;
}
pmc_regsize = sdev->reg_addrs[0].reg_size;
regs = (u8*) sbus_ioremap(&sdev->resource[0], 0,
pmc_regsize, PMC_OBPNAME);
if(NULL == regs) {
printk(KERN_ERR "%s: unable to map registers\n", PMC_DEVNAME);
return -ENODEV;
}
#ifndef PMC_NO_IDLE
/* Assign power management IDLE handler */
pm_idle = pmc_swift_idle;
#endif
printk(KERN_INFO "%s: power management initialized\n", PMC_DEVNAME);
return 0;
}
/* This driver is not critical to the boot process
* and is easiest to ioremap when SBus is already
* initialized, so we install ourselves thusly:
*/
__initcall(pmc_probe);
......@@ -27,6 +27,7 @@
#include <linux/smp_lock.h>
#include <linux/reboot.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <asm/auxio.h>
#include <asm/oplib.h>
......@@ -40,6 +41,19 @@
#include <asm/psr.h>
#include <asm/elf.h>
/*
* Power management idle function
* Set in pm platform drivers
*/
void (*pm_idle)(void);
/*
* Power-off handler instantiation for pm.h compliance
* This is done via auxio, but could be used as a fallback
* handler when auxio is not present-- unused for now...
*/
void (*pm_power_off)(void);
extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *);
struct task_struct *last_task_used_math = NULL;
......@@ -91,8 +105,13 @@ int cpu_idle(void)
}
restore_flags(flags);
}
check_pgt_cache();
while((!current->need_resched) && pm_idle) {
(*pm_idle)();
}
schedule();
check_pgt_cache();
}
ret = 0;
out:
......
......@@ -22,6 +22,7 @@
#ifdef CONFIG_PCI
#include <linux/pci.h>
#endif
#include <linux/pm.h>
#include <asm/oplib.h>
#include <asm/delay.h>
......@@ -295,3 +296,6 @@ EXPORT_SYMBOL_DOT(mul);
EXPORT_SYMBOL_DOT(umul);
EXPORT_SYMBOL_DOT(div);
EXPORT_SYMBOL_DOT(udiv);
/* Sun Power Management Idle Handler */
EXPORT_SYMBOL(pm_idle);
/* $Id$
*
* apc - Driver definitions for power management functions
* of Aurora Personality Chip (APC) on SPARCstation-4/5 and
* derivatives
*
* Copyright (c) 2001 Eric Brower (ebrower@usa.net)
*
*/
#ifndef _SPARC_APC_H
#define _SPARC_APC_H
#include <linux/ioctl.h>
#define APC_IOC 'A'
#define APCIOCGFANCTL _IOR(APC_IOC, 0x00, int) /* Get fan speed */
#define APCIOCSFANCTL _IOW(APC_IOC, 0x01, int) /* Set fan speed */
#define APCIOCGCPWR _IOR(APC_IOC, 0x02, int) /* Get CPOWER state */
#define APCIOCSCPWR _IOW(APC_IOC, 0x03, int) /* Set CPOWER state */
#define APCIOCGBPORT _IOR(APC_IOC, 0x04, int) /* Get BPORT state */
#define APCIOCSBPORT _IOW(APC_IOC, 0x05, int) /* Set BPORT state */
/*
* Register offsets
*/
#define APC_IDLE_REG 0x00
#define APC_FANCTL_REG 0x20
#define APC_CPOWER_REG 0x24
#define APC_BPORT_REG 0x30
#define APC_REGMASK 0x01
#define APC_BPMASK 0x03
/*
* IDLE - CPU standby values (set to initiate standby)
*/
#define APC_IDLE_ON 0x01
/*
* FANCTL - Fan speed control state values
*/
#define APC_FANCTL_HI 0x00 /* Fan speed high */
#define APC_FANCTL_LO 0x01 /* Fan speed low */
/*
* CPWR - Convenience power outlet state values
*/
#define APC_CPOWER_ON 0x00 /* Conv power on */
#define APC_CPOWER_OFF 0x01 /* Conv power off */
/*
* BPA/BPB - Read-Write "Bit Ports" state values (reset to 0 at power-on)
*
* WARNING: Internal usage of bit ports is platform dependent--
* don't modify BPORT settings unless you know what you are doing.
*
* On SS5 BPA seems to toggle onboard ethernet loopback... -E
*/
#define APC_BPORT_A 0x01 /* Bit Port A */
#define APC_BPORT_B 0x02 /* Bit Port B */
#endif /* !(_SPARC_APC_H) */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment