1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Eurobraille/Iris power off support. 4 * 5 * Eurobraille's Iris machine is a PC with no APM or ACPI support. 6 * It is shutdown by a special I/O sequence which this module provides. 7 * 8 * Copyright (C) Shérab <Sebastien.Hinderer@ens-lyon.org> 9 */ 10 11#include <linux/moduleparam.h> 12#include <linux/module.h> 13#include <linux/platform_device.h> 14#include <linux/kernel.h> 15#include <linux/errno.h> 16#include <linux/delay.h> 17#include <linux/pm.h> 18#include <asm/io.h> 19 20#define IRIS_GIO_BASE 0x340 21#define IRIS_GIO_INPUT IRIS_GIO_BASE 22#define IRIS_GIO_OUTPUT (IRIS_GIO_BASE + 1) 23#define IRIS_GIO_PULSE 0x80 /* First byte to send */ 24#define IRIS_GIO_REST 0x00 /* Second byte to send */ 25#define IRIS_GIO_NODEV 0xff /* Likely not an Iris */ 26 27MODULE_LICENSE("GPL"); 28MODULE_AUTHOR("Sébastien Hinderer <Sebastien.Hinderer@ens-lyon.org>"); 29MODULE_DESCRIPTION("A power_off handler for Iris devices from EuroBraille"); 30MODULE_SUPPORTED_DEVICE("Eurobraille/Iris"); 31 32static bool force; 33 34module_param(force, bool, 0); 35MODULE_PARM_DESC(force, "Set to one to force poweroff handler installation."); 36 37static void (*old_pm_power_off)(void); 38 39static void iris_power_off(void) 40{ 41 outb(IRIS_GIO_PULSE, IRIS_GIO_OUTPUT); 42 msleep(850); 43 outb(IRIS_GIO_REST, IRIS_GIO_OUTPUT); 44} 45 46/* 47 * Before installing the power_off handler, try to make sure the OS is 48 * running on an Iris. Since Iris does not support DMI, this is done 49 * by reading its input port and seeing whether the read value is 50 * meaningful. 51 */ 52static int iris_probe(struct platform_device *pdev) 53{ 54 unsigned char status = inb(IRIS_GIO_INPUT); 55 if (status == IRIS_GIO_NODEV) { 56 printk(KERN_ERR "This machine does not seem to be an Iris. " 57 "Power off handler not installed.\n"); 58 return -ENODEV; 59 } 60 old_pm_power_off = pm_power_off; 61 pm_power_off = &iris_power_off; 62 printk(KERN_INFO "Iris power_off handler installed.\n"); 63 return 0; 64} 65 66static int iris_remove(struct platform_device *pdev) 67{ 68 pm_power_off = old_pm_power_off; 69 printk(KERN_INFO "Iris power_off handler uninstalled.\n"); 70 return 0; 71} 72 73static struct platform_driver iris_driver = { 74 .driver = { 75 .name = "iris", 76 }, 77 .probe = iris_probe, 78 .remove = iris_remove, 79}; 80 81static struct resource iris_resources[] = { 82 { 83 .start = IRIS_GIO_BASE, 84 .end = IRIS_GIO_OUTPUT, 85 .flags = IORESOURCE_IO, 86 .name = "address" 87 } 88}; 89 90static struct platform_device *iris_device; 91 92static int iris_init(void) 93{ 94 int ret; 95 if (force != 1) { 96 printk(KERN_ERR "The force parameter has not been set to 1." 97 " The Iris poweroff handler will not be installed.\n"); 98 return -ENODEV; 99 } 100 ret = platform_driver_register(&iris_driver); 101 if (ret < 0) { 102 printk(KERN_ERR "Failed to register iris platform driver: %d\n", 103 ret); 104 return ret; 105 } 106 iris_device = platform_device_register_simple("iris", (-1), 107 iris_resources, ARRAY_SIZE(iris_resources)); 108 if (IS_ERR(iris_device)) { 109 printk(KERN_ERR "Failed to register iris platform device\n"); 110 platform_driver_unregister(&iris_driver); 111 return PTR_ERR(iris_device); 112 } 113 return 0; 114} 115 116static void iris_exit(void) 117{ 118 platform_device_unregister(iris_device); 119 platform_driver_unregister(&iris_driver); 120} 121 122module_init(iris_init); 123module_exit(iris_exit); 124