iris.c (3218B)
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"); 30 31static bool force; 32 33module_param(force, bool, 0); 34MODULE_PARM_DESC(force, "Set to one to force poweroff handler installation."); 35 36static void (*old_pm_power_off)(void); 37 38static 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 */ 51static 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 65static 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 72static struct platform_driver iris_driver = { 73 .driver = { 74 .name = "iris", 75 }, 76 .probe = iris_probe, 77 .remove = iris_remove, 78}; 79 80static 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 89static struct platform_device *iris_device; 90 91static 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 115static void iris_exit(void) 116{ 117 platform_device_unregister(iris_device); 118 platform_driver_unregister(&iris_driver); 119} 120 121module_init(iris_init); 122module_exit(iris_exit);