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