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 int 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 return 0; 70 } 71 72 static struct platform_driver iris_driver = { 73 .driver = { 74 .name = "iris", 75 }, 76 .probe = iris_probe, 77 .remove = iris_remove, 78 }; 79 80 static struct resource iris_resources[] = { 81 { 82 .start = IRIS_GIO_BASE, 83 .end = IRIS_GIO_OUTPUT, 84 .flags = IORESOURCE_IO, 85 .name = "address" 86 } 87 }; 88 89 static struct platform_device *iris_device; 90 91 static int iris_init(void) 92 { 93 int ret; 94 if (force != 1) { 95 printk(KERN_ERR "The force parameter has not been set to 1." 96 " The Iris poweroff handler will not be installed.\n"); 97 return -ENODEV; 98 } 99 ret = platform_driver_register(&iris_driver); 100 if (ret < 0) { 101 printk(KERN_ERR "Failed to register iris platform driver: %d\n", 102 ret); 103 return ret; 104 } 105 iris_device = platform_device_register_simple("iris", (-1), 106 iris_resources, ARRAY_SIZE(iris_resources)); 107 if (IS_ERR(iris_device)) { 108 printk(KERN_ERR "Failed to register iris platform device\n"); 109 platform_driver_unregister(&iris_driver); 110 return PTR_ERR(iris_device); 111 } 112 return 0; 113 } 114 115 static void iris_exit(void) 116 { 117 platform_device_unregister(iris_device); 118 platform_driver_unregister(&iris_driver); 119 } 120 121 module_init(iris_init); 122 module_exit(iris_exit); 123