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