xref: /openbmc/linux/arch/sparc/kernel/apc.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1*1da177e4SLinus Torvalds /* apc - Driver implementation for power management functions
2*1da177e4SLinus Torvalds  * of Aurora Personality Chip (APC) on SPARCstation-4/5 and
3*1da177e4SLinus Torvalds  * derivatives.
4*1da177e4SLinus Torvalds  *
5*1da177e4SLinus Torvalds  * Copyright (c) 2002 Eric Brower (ebrower@usa.net)
6*1da177e4SLinus Torvalds  */
7*1da177e4SLinus Torvalds 
8*1da177e4SLinus Torvalds #include <linux/kernel.h>
9*1da177e4SLinus Torvalds #include <linux/fs.h>
10*1da177e4SLinus Torvalds #include <linux/errno.h>
11*1da177e4SLinus Torvalds #include <linux/init.h>
12*1da177e4SLinus Torvalds #include <linux/miscdevice.h>
13*1da177e4SLinus Torvalds #include <linux/pm.h>
14*1da177e4SLinus Torvalds 
15*1da177e4SLinus Torvalds #include <asm/io.h>
16*1da177e4SLinus Torvalds #include <asm/sbus.h>
17*1da177e4SLinus Torvalds #include <asm/oplib.h>
18*1da177e4SLinus Torvalds #include <asm/uaccess.h>
19*1da177e4SLinus Torvalds #include <asm/auxio.h>
20*1da177e4SLinus Torvalds #include <asm/apc.h>
21*1da177e4SLinus Torvalds 
22*1da177e4SLinus Torvalds /* Debugging
23*1da177e4SLinus Torvalds  *
24*1da177e4SLinus Torvalds  * #define APC_DEBUG_LED
25*1da177e4SLinus Torvalds  */
26*1da177e4SLinus Torvalds 
27*1da177e4SLinus Torvalds #define APC_MINOR	MISC_DYNAMIC_MINOR
28*1da177e4SLinus Torvalds #define APC_OBPNAME	"power-management"
29*1da177e4SLinus Torvalds #define APC_DEVNAME "apc"
30*1da177e4SLinus Torvalds 
31*1da177e4SLinus Torvalds volatile static u8 __iomem *regs;
32*1da177e4SLinus Torvalds static int apc_regsize;
33*1da177e4SLinus Torvalds static int apc_no_idle __initdata = 0;
34*1da177e4SLinus Torvalds 
35*1da177e4SLinus Torvalds #define apc_readb(offs)			(sbus_readb(regs+offs))
36*1da177e4SLinus Torvalds #define apc_writeb(val, offs) 	(sbus_writeb(val, regs+offs))
37*1da177e4SLinus Torvalds 
38*1da177e4SLinus Torvalds /* Specify "apc=noidle" on the kernel command line to
39*1da177e4SLinus Torvalds  * disable APC CPU standby support.  Certain prototype
40*1da177e4SLinus Torvalds  * systems (SPARCstation-Fox) do not play well with APC
41*1da177e4SLinus Torvalds  * CPU idle, so disable this if your system has APC and
42*1da177e4SLinus Torvalds  * crashes randomly.
43*1da177e4SLinus Torvalds  */
44*1da177e4SLinus Torvalds static int __init apc_setup(char *str)
45*1da177e4SLinus Torvalds {
46*1da177e4SLinus Torvalds 	if(!strncmp(str, "noidle", strlen("noidle"))) {
47*1da177e4SLinus Torvalds 		apc_no_idle = 1;
48*1da177e4SLinus Torvalds 		return 1;
49*1da177e4SLinus Torvalds 	}
50*1da177e4SLinus Torvalds 	return 0;
51*1da177e4SLinus Torvalds }
52*1da177e4SLinus Torvalds __setup("apc=", apc_setup);
53*1da177e4SLinus Torvalds 
54*1da177e4SLinus Torvalds /*
55*1da177e4SLinus Torvalds  * CPU idle callback function
56*1da177e4SLinus Torvalds  * See .../arch/sparc/kernel/process.c
57*1da177e4SLinus Torvalds  */
58*1da177e4SLinus Torvalds void apc_swift_idle(void)
59*1da177e4SLinus Torvalds {
60*1da177e4SLinus Torvalds #ifdef APC_DEBUG_LED
61*1da177e4SLinus Torvalds 	set_auxio(0x00, AUXIO_LED);
62*1da177e4SLinus Torvalds #endif
63*1da177e4SLinus Torvalds 
64*1da177e4SLinus Torvalds 	apc_writeb(apc_readb(APC_IDLE_REG) | APC_IDLE_ON, APC_IDLE_REG);
65*1da177e4SLinus Torvalds 
66*1da177e4SLinus Torvalds #ifdef APC_DEBUG_LED
67*1da177e4SLinus Torvalds 	set_auxio(AUXIO_LED, 0x00);
68*1da177e4SLinus Torvalds #endif
69*1da177e4SLinus Torvalds }
70*1da177e4SLinus Torvalds 
71*1da177e4SLinus Torvalds static inline void apc_free(void)
72*1da177e4SLinus Torvalds {
73*1da177e4SLinus Torvalds 	sbus_iounmap(regs, apc_regsize);
74*1da177e4SLinus Torvalds }
75*1da177e4SLinus Torvalds 
76*1da177e4SLinus Torvalds static int apc_open(struct inode *inode, struct file *f)
77*1da177e4SLinus Torvalds {
78*1da177e4SLinus Torvalds 	return 0;
79*1da177e4SLinus Torvalds }
80*1da177e4SLinus Torvalds 
81*1da177e4SLinus Torvalds static int apc_release(struct inode *inode, struct file *f)
82*1da177e4SLinus Torvalds {
83*1da177e4SLinus Torvalds 	return 0;
84*1da177e4SLinus Torvalds }
85*1da177e4SLinus Torvalds 
86*1da177e4SLinus Torvalds static int apc_ioctl(struct inode *inode, struct file *f,
87*1da177e4SLinus Torvalds 		     unsigned int cmd, unsigned long __arg)
88*1da177e4SLinus Torvalds {
89*1da177e4SLinus Torvalds 	__u8 inarg, __user *arg;
90*1da177e4SLinus Torvalds 
91*1da177e4SLinus Torvalds 	arg = (__u8 __user *) __arg;
92*1da177e4SLinus Torvalds 	switch (cmd) {
93*1da177e4SLinus Torvalds 	case APCIOCGFANCTL:
94*1da177e4SLinus Torvalds 		if (put_user(apc_readb(APC_FANCTL_REG) & APC_REGMASK, arg))
95*1da177e4SLinus Torvalds 				return -EFAULT;
96*1da177e4SLinus Torvalds 		break;
97*1da177e4SLinus Torvalds 
98*1da177e4SLinus Torvalds 	case APCIOCGCPWR:
99*1da177e4SLinus Torvalds 		if (put_user(apc_readb(APC_CPOWER_REG) & APC_REGMASK, arg))
100*1da177e4SLinus Torvalds 			return -EFAULT;
101*1da177e4SLinus Torvalds 		break;
102*1da177e4SLinus Torvalds 
103*1da177e4SLinus Torvalds 	case APCIOCGBPORT:
104*1da177e4SLinus Torvalds 		if (put_user(apc_readb(APC_BPORT_REG) & APC_BPMASK, arg))
105*1da177e4SLinus Torvalds 			return -EFAULT;
106*1da177e4SLinus Torvalds 		break;
107*1da177e4SLinus Torvalds 
108*1da177e4SLinus Torvalds 	case APCIOCSFANCTL:
109*1da177e4SLinus Torvalds 		if (get_user(inarg, arg))
110*1da177e4SLinus Torvalds 			return -EFAULT;
111*1da177e4SLinus Torvalds 		apc_writeb(inarg & APC_REGMASK, APC_FANCTL_REG);
112*1da177e4SLinus Torvalds 		break;
113*1da177e4SLinus Torvalds 	case APCIOCSCPWR:
114*1da177e4SLinus Torvalds 		if (get_user(inarg, arg))
115*1da177e4SLinus Torvalds 			return -EFAULT;
116*1da177e4SLinus Torvalds 		apc_writeb(inarg & APC_REGMASK, APC_CPOWER_REG);
117*1da177e4SLinus Torvalds 		break;
118*1da177e4SLinus Torvalds 	case APCIOCSBPORT:
119*1da177e4SLinus Torvalds 		if (get_user(inarg, arg))
120*1da177e4SLinus Torvalds 			return -EFAULT;
121*1da177e4SLinus Torvalds 		apc_writeb(inarg & APC_BPMASK, APC_BPORT_REG);
122*1da177e4SLinus Torvalds 		break;
123*1da177e4SLinus Torvalds 	default:
124*1da177e4SLinus Torvalds 		return -EINVAL;
125*1da177e4SLinus Torvalds 	};
126*1da177e4SLinus Torvalds 
127*1da177e4SLinus Torvalds 	return 0;
128*1da177e4SLinus Torvalds }
129*1da177e4SLinus Torvalds 
130*1da177e4SLinus Torvalds static struct file_operations apc_fops = {
131*1da177e4SLinus Torvalds 	.ioctl =	apc_ioctl,
132*1da177e4SLinus Torvalds 	.open =		apc_open,
133*1da177e4SLinus Torvalds 	.release =	apc_release,
134*1da177e4SLinus Torvalds };
135*1da177e4SLinus Torvalds 
136*1da177e4SLinus Torvalds static struct miscdevice apc_miscdev = { APC_MINOR, APC_DEVNAME, &apc_fops };
137*1da177e4SLinus Torvalds 
138*1da177e4SLinus Torvalds static int __init apc_probe(void)
139*1da177e4SLinus Torvalds {
140*1da177e4SLinus Torvalds 	struct sbus_bus *sbus = NULL;
141*1da177e4SLinus Torvalds 	struct sbus_dev *sdev = NULL;
142*1da177e4SLinus Torvalds 	int iTmp = 0;
143*1da177e4SLinus Torvalds 
144*1da177e4SLinus Torvalds 	for_each_sbus(sbus) {
145*1da177e4SLinus Torvalds 		for_each_sbusdev(sdev, sbus) {
146*1da177e4SLinus Torvalds 			if (!strcmp(sdev->prom_name, APC_OBPNAME)) {
147*1da177e4SLinus Torvalds 				goto sbus_done;
148*1da177e4SLinus Torvalds 			}
149*1da177e4SLinus Torvalds 		}
150*1da177e4SLinus Torvalds 	}
151*1da177e4SLinus Torvalds 
152*1da177e4SLinus Torvalds sbus_done:
153*1da177e4SLinus Torvalds 	if (!sdev) {
154*1da177e4SLinus Torvalds 		return -ENODEV;
155*1da177e4SLinus Torvalds 	}
156*1da177e4SLinus Torvalds 
157*1da177e4SLinus Torvalds 	apc_regsize = sdev->reg_addrs[0].reg_size;
158*1da177e4SLinus Torvalds 	regs = sbus_ioremap(&sdev->resource[0], 0,
159*1da177e4SLinus Torvalds 				   apc_regsize, APC_OBPNAME);
160*1da177e4SLinus Torvalds 	if(!regs) {
161*1da177e4SLinus Torvalds 		printk(KERN_ERR "%s: unable to map registers\n", APC_DEVNAME);
162*1da177e4SLinus Torvalds 		return -ENODEV;
163*1da177e4SLinus Torvalds 	}
164*1da177e4SLinus Torvalds 
165*1da177e4SLinus Torvalds 	iTmp = misc_register(&apc_miscdev);
166*1da177e4SLinus Torvalds 	if (iTmp != 0) {
167*1da177e4SLinus Torvalds 		printk(KERN_ERR "%s: unable to register device\n", APC_DEVNAME);
168*1da177e4SLinus Torvalds 		apc_free();
169*1da177e4SLinus Torvalds 		return -ENODEV;
170*1da177e4SLinus Torvalds 	}
171*1da177e4SLinus Torvalds 
172*1da177e4SLinus Torvalds 	/* Assign power management IDLE handler */
173*1da177e4SLinus Torvalds 	if(!apc_no_idle)
174*1da177e4SLinus Torvalds 		pm_idle = apc_swift_idle;
175*1da177e4SLinus Torvalds 
176*1da177e4SLinus Torvalds 	printk(KERN_INFO "%s: power management initialized%s\n",
177*1da177e4SLinus Torvalds 		APC_DEVNAME, apc_no_idle ? " (CPU idle disabled)" : "");
178*1da177e4SLinus Torvalds 	return 0;
179*1da177e4SLinus Torvalds }
180*1da177e4SLinus Torvalds 
181*1da177e4SLinus Torvalds /* This driver is not critical to the boot process
182*1da177e4SLinus Torvalds  * and is easiest to ioremap when SBus is already
183*1da177e4SLinus Torvalds  * initialized, so we install ourselves thusly:
184*1da177e4SLinus Torvalds  */
185*1da177e4SLinus Torvalds __initcall(apc_probe);
186*1da177e4SLinus Torvalds 
187