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 27 MODULE_LICENSE("GPL"); 28 MODULE_AUTHOR("Sébastien Hinderer <Sebastien.Hinderer@ens-lyon.org>"); 29 MODULE_DESCRIPTION("A power_off handler for Iris devices from EuroBraille"); 30 MODULE_SUPPORTED_DEVICE("Eurobraille/Iris"); 31 32 static bool force; 33 34 module_param(force, bool, 0); 35 MODULE_PARM_DESC(force, "Set to one to force poweroff handler installation."); 36 37 static void (*old_pm_power_off)(void); 38 39 static 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 */ 52 static 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 66 static 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 73 static struct platform_driver iris_driver = { 74 .driver = { 75 .name = "iris", 76 }, 77 .probe = iris_probe, 78 .remove = iris_remove, 79 }; 80 81 static 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 90 static struct platform_device *iris_device; 91 92 static 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 116 static void iris_exit(void) 117 { 118 platform_device_unregister(iris_device); 119 platform_driver_unregister(&iris_driver); 120 } 121 122 module_init(iris_init); 123 module_exit(iris_exit); 124