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 31 static bool force; 32 33 module_param(force, bool, 0); 34 MODULE_PARM_DESC(force, "Set to one to force poweroff handler installation."); 35 36 static void (*old_pm_power_off)(void); 37 38 static void iris_power_off(void) 39 { 40 outb(IRIS_GIO_PULSE, IRIS_GIO_OUTPUT); 41 msleep(850); 42 outb(IRIS_GIO_REST, IRIS_GIO_OUTPUT); 43 } 44 45 /* 46 * Before installing the power_off handler, try to make sure the OS is 47 * running on an Iris. Since Iris does not support DMI, this is done 48 * by reading its input port and seeing whether the read value is 49 * meaningful. 50 */ 51 static int iris_probe(struct platform_device *pdev) 52 { 53 unsigned char status = inb(IRIS_GIO_INPUT); 54 if (status == IRIS_GIO_NODEV) { 55 printk(KERN_ERR "This machine does not seem to be an Iris. " 56 "Power off handler not installed.\n"); 57 return -ENODEV; 58 } 59 old_pm_power_off = pm_power_off; 60 pm_power_off = &iris_power_off; 61 printk(KERN_INFO "Iris power_off handler installed.\n"); 62 return 0; 63 } 64 65 static void iris_remove(struct platform_device *pdev) 66 { 67 pm_power_off = old_pm_power_off; 68 printk(KERN_INFO "Iris power_off handler uninstalled.\n"); 69 } 70 71 static struct platform_driver iris_driver = { 72 .driver = { 73 .name = "iris", 74 }, 75 .probe = iris_probe, 76 .remove = iris_remove, 77 }; 78 79 static struct resource iris_resources[] = { 80 { 81 .start = IRIS_GIO_BASE, 82 .end = IRIS_GIO_OUTPUT, 83 .flags = IORESOURCE_IO, 84 .name = "address" 85 } 86 }; 87 88 static struct platform_device *iris_device; 89 90 static int iris_init(void) 91 { 92 int ret; 93 if (force != 1) { 94 printk(KERN_ERR "The force parameter has not been set to 1." 95 " The Iris poweroff handler will not be installed.\n"); 96 return -ENODEV; 97 } 98 ret = platform_driver_register(&iris_driver); 99 if (ret < 0) { 100 printk(KERN_ERR "Failed to register iris platform driver: %d\n", 101 ret); 102 return ret; 103 } 104 iris_device = platform_device_register_simple("iris", (-1), 105 iris_resources, ARRAY_SIZE(iris_resources)); 106 if (IS_ERR(iris_device)) { 107 printk(KERN_ERR "Failed to register iris platform device\n"); 108 platform_driver_unregister(&iris_driver); 109 return PTR_ERR(iris_device); 110 } 111 return 0; 112 } 113 114 static void iris_exit(void) 115 { 116 platform_device_unregister(iris_device); 117 platform_driver_unregister(&iris_driver); 118 } 119 120 module_init(iris_init); 121 module_exit(iris_exit); 122