brintos

brintos / linux-shallow public Read only

0
0
Text · 6.6 KiB · 7a6a3e7 Raw
249 lines · c
1/* SPDX-License-Identifier: GPL-2.0-or-later */2/*3 * HP PARISC soft power switch driver4 *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 at9 *  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 230 31/* how long does the power button needs to be down until we react ? */32#define POWERSWITCH_DOWN_SEC 233 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 */86int pwrsw_enabled __read_mostly = 1;87 88/* main kernel thread worker. It polls the button state */89static int kpowerswd(void *param)90{91	__set_current_state(TASK_RUNNING);92 93	do {94		int button_not_pressed;95		unsigned long soft_power_reg = (unsigned long) param;96 97		schedule_timeout_interruptible(pwrsw_enabled ? HZ : HZ/POWERSWITCH_POLL_PER_SEC);98 99		if (unlikely(!pwrsw_enabled))100			continue;101 102		if (soft_power_reg) {103			/*104			 * Non-Gecko-style machines:105			 * Check the power switch status which is read from the106			 * real I/O location at soft_power_reg.107			 * Bit 31 ("the lowest bit) is the status of the power switch.108			 * This bit is "1" if the button is NOT pressed.109			 */110			button_not_pressed = (gsc_readl(soft_power_reg) & 0x1);111		} else {112			/*113			 * On gecko style machines (e.g. 712/xx and 715/xx)114			 * the power switch status is stored in Bit 0 ("the highest bit")115			 * of CPU diagnose register 25.116			 * Warning: Some machines never reset the DIAG flag, even if117			 * the button has been released again.118			 */119			button_not_pressed = (__getDIAG(25) & 0x80000000);120		}121 122		if (likely(button_not_pressed)) {123			if (unlikely(shutdown_timer && /* avoid writing if not necessary */124				shutdown_timer < (POWERSWITCH_DOWN_SEC*POWERSWITCH_POLL_PER_SEC))) {125				shutdown_timer = 0;126				printk(KERN_INFO KTHREAD_NAME ": Shutdown request aborted.\n");127			}128		} else129			process_shutdown();130 131 132	} while (!kthread_should_stop());133 134	return 0;135}136 137 138/*139 * powerfail interruption handler (irq IRQ_FROM_REGION(CPU_IRQ_REGION)+2)140 */141#if 0142static void powerfail_interrupt(int code, void *x)143{144	printk(KERN_CRIT "POWERFAIL INTERRUPTION !\n");145	poweroff();146}147#endif148 149 150 151 152/*153 * parisc_panic_event() is called by the panic handler.154 *155 * As soon as a panic occurs, our tasklets above will not156 * be executed any longer. This function then re-enables157 * the soft-power switch and allows the user to switch off158 * the system. We rely in pdc_soft_power_button_panic()159 * since this version spin_trylocks (instead of regular160 * spinlock), preventing deadlocks on panic path.161 */162static int parisc_panic_event(struct notifier_block *this,163		unsigned long event, void *ptr)164{165	/* re-enable the soft-power switch */166	pdc_soft_power_button_panic(0);167	return NOTIFY_DONE;168}169 170static struct notifier_block parisc_panic_block = {171	.notifier_call	= parisc_panic_event,172	.priority	= INT_MAX,173};174 175/* qemu soft power-off function */176static int qemu_power_off(struct sys_off_data *data)177{178	/* this turns the system off via SeaBIOS */179	gsc_writel(0, (unsigned long) data->cb_data);180	pdc_soft_power_button(1);181	return NOTIFY_DONE;182}183 184static int __init power_init(void)185{186	unsigned long ret;187	unsigned long soft_power_reg;188 189#if 0190	request_irq( IRQ_FROM_REGION(CPU_IRQ_REGION)+2, &powerfail_interrupt,191		0, "powerfail", NULL);192#endif193 194	/* enable the soft power switch if possible */195	ret = pdc_soft_power_info(&soft_power_reg);196	if (ret == PDC_OK)197		ret = pdc_soft_power_button(1);198	if (ret != PDC_OK)199		soft_power_reg = -1UL;200 201	switch (soft_power_reg) {202	case 0:		printk(KERN_INFO DRIVER_NAME ": Gecko-style soft power switch enabled.\n");203			break;204 205	case -1UL:	printk(KERN_INFO DRIVER_NAME ": Soft power switch support not available.\n");206			return -ENODEV;207 208	default:	printk(KERN_INFO DRIVER_NAME ": Soft power switch at 0x%08lx enabled.\n",209				soft_power_reg);210	}211 212	power_task = NULL;213	if (running_on_qemu && soft_power_reg)214		register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_DEFAULT,215					qemu_power_off, (void *)soft_power_reg);216	if (!running_on_qemu || soft_power_reg)217		power_task = kthread_run(kpowerswd, (void*)soft_power_reg,218					KTHREAD_NAME);219	if (IS_ERR(power_task)) {220		printk(KERN_ERR DRIVER_NAME ": thread creation failed.  Driver not loaded.\n");221		pdc_soft_power_button(0);222		return -EIO;223	}224 225	/* Register a call for panic conditions. */226	atomic_notifier_chain_register(&panic_notifier_list,227			&parisc_panic_block);228 229	return 0;230}231 232static void __exit power_exit(void)233{234	kthread_stop(power_task);235 236	atomic_notifier_chain_unregister(&panic_notifier_list,237			&parisc_panic_block);238 239	pdc_soft_power_button(0);240}241 242arch_initcall(power_init);243module_exit(power_exit);244 245 246MODULE_AUTHOR("Helge Deller <deller@gmx.de>");247MODULE_DESCRIPTION("Soft power switch driver");248MODULE_LICENSE("Dual BSD/GPL");249