diff options
| -rw-r--r-- | arch/sparc/Config.help | 5 | ||||
| -rw-r--r-- | arch/sparc/config.in | 1 | ||||
| -rw-r--r-- | arch/sparc/defconfig | 1 | ||||
| -rw-r--r-- | arch/sparc/kernel/Makefile | 1 | ||||
| -rw-r--r-- | arch/sparc/kernel/apc.c | 173 | ||||
| -rw-r--r-- | arch/sparc/kernel/pmc.c | 101 | ||||
| -rw-r--r-- | arch/sparc/kernel/process.c | 21 | ||||
| -rw-r--r-- | arch/sparc/kernel/sparc_ksyms.c | 4 | ||||
| -rw-r--r-- | include/asm-sparc/apc.h | 66 |
9 files changed, 372 insertions, 1 deletions
diff --git a/arch/sparc/Config.help b/arch/sparc/Config.help index f64c93438e45..61663ab1e452 100644 --- a/arch/sparc/Config.help +++ b/arch/sparc/Config.help @@ -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 diff --git a/arch/sparc/config.in b/arch/sparc/config.in index 7b2e49484672..875a7003d4e8 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -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 diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig index 1c6f7db5f9a0..c3beb7acd062 100644 --- a/arch/sparc/defconfig +++ b/arch/sparc/defconfig @@ -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 diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 8143d2acc05f..3be4f138f948 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -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 diff --git a/arch/sparc/kernel/apc.c b/arch/sparc/kernel/apc.c new file mode 100644 index 000000000000..b243df526852 --- /dev/null +++ b/arch/sparc/kernel/apc.c @@ -0,0 +1,173 @@ +/* $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); diff --git a/arch/sparc/kernel/pmc.c b/arch/sparc/kernel/pmc.c new file mode 100644 index 000000000000..c2a6b09b2cdb --- /dev/null +++ b/arch/sparc/kernel/pmc.c @@ -0,0 +1,101 @@ +/* $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); diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c index 0afa24726678..7eff72df7cf1 100644 --- a/arch/sparc/kernel/process.c +++ b/arch/sparc/kernel/process.c @@ -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: diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index 316fc9696c78..e9bab9ae374e 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -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); diff --git a/include/asm-sparc/apc.h b/include/asm-sparc/apc.h new file mode 100644 index 000000000000..0e93067d256d --- /dev/null +++ b/include/asm-sparc/apc.h @@ -0,0 +1,66 @@ +/* $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) */ |
