File Index
Files and directories
Sign in
FILE

power.c

File preview with syntax highlighting.

linux-6.19.14/drivers/parisc/power.c C
  1/* SPDX-License-Identifier: GPL-2.0-or-later */
  2/*
  3 * HP PARISC soft power switch driver
  4 *
  5 * Copyright (c) 2001-2023 Helge Deller <deller@gmx.de>
  6 *
  7 *  HINT:
  8 *  Support of the soft power switch button may be enabled or disabled at
  9 *  runtime through the "/proc/sys/kernel/power" procfs entry.
 10 */
 11
 12#include <linux/module.h>
 13#include <linux/init.h>
 14#include <linux/kernel.h>
 15#include <linux/panic_notifier.h>
 16#include <linux/reboot.h>
 17#include <linux/sched/signal.h>
 18#include <linux/kthread.h>
 19#include <linux/pm.h>
 20
 21#include <asm/pdc.h>
 22#include <asm/io.h>
 23#include <asm/led.h>
 24
 25#define DRIVER_NAME  "powersw"
 26#define KTHREAD_NAME "kpowerswd"
 27
 28/* how often should the power button be polled ? */
 29#define POWERSWITCH_POLL_PER_SEC 2
 30
 31/* how long does the power button needs to be down until we react ? */
 32#define POWERSWITCH_DOWN_SEC 2
 33
 34/* assembly code to access special registers */
 35/* taken from PCXL ERS page 82 */
 36#define DIAG_CODE(code)		(0x14000000 + ((code)<<5))
 37
 38#define MFCPU_X(rDiagReg, t_ch, t_th, code) \
 39	(DIAG_CODE(code) + ((rDiagReg)<<21) + ((t_ch)<<16) + ((t_th)<<0) )
 40
 41#define MTCPU(dr, gr)		MFCPU_X(dr, gr,  0, 0x12)       /* move value of gr to dr[dr] */
 42#define MFCPU_C(dr, gr)		MFCPU_X(dr, gr,  0, 0x30)	/* for dr0 and dr8 only ! */
 43#define MFCPU_T(dr, gr)		MFCPU_X(dr,  0, gr, 0xa0)	/* all dr except dr0 and dr8 */
 44
 45#define __getDIAG(dr) ( {			\
 46        register unsigned long __res asm("r28");\
 47	 __asm__ __volatile__ (			\
 48		".word %1" : "=&r" (__res) : "i" (MFCPU_T(dr,28) ) \
 49	);					\
 50	__res;					\
 51} )
 52
 53/* local shutdown counter */
 54static int shutdown_timer __read_mostly;
 55
 56/* check, give feedback and start shutdown after one second */
 57static void process_shutdown(void)
 58{
 59	if (shutdown_timer == 0)
 60		printk(KERN_ALERT KTHREAD_NAME ": Shutdown requested...\n");
 61
 62	shutdown_timer++;
 63
 64	/* wait until the button was pressed for 1 second */
 65	if (shutdown_timer == (POWERSWITCH_DOWN_SEC*POWERSWITCH_POLL_PER_SEC)) {
 66		static const char msg[] = "Shutting down...";
 67		printk(KERN_INFO KTHREAD_NAME ": %s\n", msg);
 68		lcd_print(msg);
 69
 70		/* send kill signal */
 71		if (kill_cad_pid(SIGINT, 1)) {
 72			/* just in case killing init process failed */
 73			machine_power_off();
 74		}
 75	}
 76}
 77
 78
 79/* main power switch task struct */
 80static struct task_struct *power_task;
 81
 82/* filename in /proc which can be used to enable/disable the power switch */
 83#define SYSCTL_FILENAME	"sys/kernel/power"
 84
 85/* soft power switch enabled/disabled */
 86static int pwrsw_enabled __read_mostly = 1;
 87
 88static const struct ctl_table power_sysctl_table[] = {
 89	{
 90		.procname	= "soft-power",
 91		.data		= &pwrsw_enabled,
 92		.maxlen		= sizeof(int),
 93		.mode		= 0644,
 94		.proc_handler	= proc_dointvec,
 95	},
 96};
 97
 98static int __init init_power_sysctl(void)
 99{
100	register_sysctl_init("kernel", power_sysctl_table);
101	return 0;
102}
103
104arch_initcall(init_power_sysctl);
105
106/* main kernel thread worker. It polls the button state */
107static int kpowerswd(void *param)
108{
109	__set_current_state(TASK_RUNNING);
110
111	do {
112		int button_not_pressed;
113		unsigned long soft_power_reg = (unsigned long) param;
114
115		schedule_timeout_interruptible(pwrsw_enabled ? HZ : HZ/POWERSWITCH_POLL_PER_SEC);
116
117		if (unlikely(!pwrsw_enabled))
118			continue;
119
120		if (soft_power_reg) {
121			/*
122			 * Non-Gecko-style machines:
123			 * Check the power switch status which is read from the
124			 * real I/O location at soft_power_reg.
125			 * Bit 31 ("the lowest bit) is the status of the power switch.
126			 * This bit is "1" if the button is NOT pressed.
127			 */
128			button_not_pressed = (gsc_readl(soft_power_reg) & 0x1);
129		} else {
130			/*
131			 * On gecko style machines (e.g. 712/xx and 715/xx)
132			 * the power switch status is stored in Bit 0 ("the highest bit")
133			 * of CPU diagnose register 25.
134			 * Warning: Some machines never reset the DIAG flag, even if
135			 * the button has been released again.
136			 */
137			button_not_pressed = (__getDIAG(25) & 0x80000000);
138		}
139
140		if (likely(button_not_pressed)) {
141			if (unlikely(shutdown_timer && /* avoid writing if not necessary */
142				shutdown_timer < (POWERSWITCH_DOWN_SEC*POWERSWITCH_POLL_PER_SEC))) {
143				shutdown_timer = 0;
144				printk(KERN_INFO KTHREAD_NAME ": Shutdown request aborted.\n");
145			}
146		} else
147			process_shutdown();
148
149
150	} while (!kthread_should_stop());
151
152	return 0;
153}
154
155
156/*
157 * powerfail interruption handler (irq IRQ_FROM_REGION(CPU_IRQ_REGION)+2)
158 */
159#if 0
160static void powerfail_interrupt(int code, void *x)
161{
162	printk(KERN_CRIT "POWERFAIL INTERRUPTION !\n");
163	poweroff();
164}
165#endif
166
167
168
169
170/*
171 * parisc_panic_event() is called by the panic handler.
172 *
173 * As soon as a panic occurs, our tasklets above will not
174 * be executed any longer. This function then re-enables
175 * the soft-power switch and allows the user to switch off
176 * the system. We rely in pdc_soft_power_button_panic()
177 * since this version spin_trylocks (instead of regular
178 * spinlock), preventing deadlocks on panic path.
179 */
180static int parisc_panic_event(struct notifier_block *this,
181		unsigned long event, void *ptr)
182{
183	/* re-enable the soft-power switch */
184	pdc_soft_power_button_panic(0);
185	return NOTIFY_DONE;
186}
187
188static struct notifier_block parisc_panic_block = {
189	.notifier_call	= parisc_panic_event,
190	.priority	= INT_MAX,
191};
192
193/* qemu soft power-off function */
194static int qemu_power_off(struct sys_off_data *data)
195{
196	/* this turns the system off via SeaBIOS */
197	gsc_writel(0, (unsigned long) data->cb_data);
198	pdc_soft_power_button(1);
199	return NOTIFY_DONE;
200}
201
202static int __init power_init(void)
203{
204	unsigned long ret;
205	unsigned long soft_power_reg;
206
207#if 0
208	request_irq( IRQ_FROM_REGION(CPU_IRQ_REGION)+2, &powerfail_interrupt,
209		0, "powerfail", NULL);
210#endif
211
212	/* enable the soft power switch if possible */
213	ret = pdc_soft_power_info(&soft_power_reg);
214	if (ret == PDC_OK)
215		ret = pdc_soft_power_button(1);
216	if (ret != PDC_OK)
217		soft_power_reg = -1UL;
218
219	switch (soft_power_reg) {
220	case 0:		printk(KERN_INFO DRIVER_NAME ": Gecko-style soft power switch enabled.\n");
221			break;
222
223	case -1UL:	printk(KERN_INFO DRIVER_NAME ": Soft power switch support not available.\n");
224			return -ENODEV;
225
226	default:	printk(KERN_INFO DRIVER_NAME ": Soft power switch at 0x%08lx enabled.\n",
227				soft_power_reg);
228	}
229
230	power_task = NULL;
231	if (running_on_qemu && soft_power_reg)
232		register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_DEFAULT,
233					qemu_power_off, (void *)soft_power_reg);
234	if (!running_on_qemu || soft_power_reg)
235		power_task = kthread_run(kpowerswd, (void*)soft_power_reg,
236					KTHREAD_NAME);
237	if (IS_ERR(power_task)) {
238		printk(KERN_ERR DRIVER_NAME ": thread creation failed.  Driver not loaded.\n");
239		pdc_soft_power_button(0);
240		return -EIO;
241	}
242
243	/* Register a call for panic conditions. */
244	atomic_notifier_chain_register(&panic_notifier_list,
245			&parisc_panic_block);
246
247	return 0;
248}
249
250static void __exit power_exit(void)
251{
252	kthread_stop(power_task);
253
254	atomic_notifier_chain_unregister(&panic_notifier_list,
255			&parisc_panic_block);
256
257	pdc_soft_power_button(0);
258}
259
260arch_initcall(power_init);
261module_exit(power_exit);
262
263
264MODULE_AUTHOR("Helge Deller <deller@gmx.de>");
265MODULE_DESCRIPTION("Soft power switch driver");
266MODULE_LICENSE("Dual BSD/GPL");
~/ Read-only file preview.