summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Brower <ebrower@usa.net>2002-04-23 20:23:28 -0700
committerDavid S. Miller <davem@nuts.ninka.net>2002-04-23 20:23:28 -0700
commit3ada9612d53b2db2e4d0e41895f1d38874dd60f9 (patch)
treeb8395e0172e126d7350734831f62fa3702c4d17c
parent2e1e23d5c3a86d551181857c675bc427b58364c5 (diff)
Add Sparc Voyager power management support.
-rw-r--r--arch/sparc/Config.help5
-rw-r--r--arch/sparc/config.in1
-rw-r--r--arch/sparc/defconfig1
-rw-r--r--arch/sparc/kernel/Makefile1
-rw-r--r--arch/sparc/kernel/apc.c173
-rw-r--r--arch/sparc/kernel/pmc.c101
-rw-r--r--arch/sparc/kernel/process.c21
-rw-r--r--arch/sparc/kernel/sparc_ksyms.c4
-rw-r--r--include/asm-sparc/apc.h66
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) */