summaryrefslogtreecommitdiffstats
path: root/kernel/arch/x86/platform/iris/iris.c
blob: 735ba21efe9169de76d14ef062d08fe1574b1d46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * Eurobraille/Iris power off support.
 *
 * Eurobraille's Iris machine is a PC with no APM or ACPI support.
 * It is shutdown by a special I/O sequence which this module provides.
 *
 *  Copyright (C) Shérab <Sebastien.Hinderer@ens-lyon.org>
 *
 * This program is free software ; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation ; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY ; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the program ; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/moduleparam.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <asm/io.h>

#define IRIS_GIO_BASE		0x340
#define IRIS_GIO_INPUT		IRIS_GIO_BASE
#define IRIS_GIO_OUTPUT		(IRIS_GIO_BASE + 1)
#define IRIS_GIO_PULSE		0x80 /* First byte to send */
#define IRIS_GIO_REST		0x00 /* Second byte to send */
#define IRIS_GIO_NODEV		0xff /* Likely not an Iris */

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sébastien Hinderer <Sebastien.Hinderer@ens-lyon.org>");
MODULE_DESCRIPTION("A power_off handler for Iris devices from EuroBraille");
MODULE_SUPPORTED_DEVICE("Eurobraille/Iris");

static bool force;

module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Set to one to force poweroff handler installation.");

static void (*old_pm_power_off)(void);

static void iris_power_off(void)
{
	outb(IRIS_GIO_PULSE, IRIS_GIO_OUTPUT);
	msleep(850);
	outb(IRIS_GIO_REST, IRIS_GIO_OUTPUT);
}

/*
 * Before installing the power_off handler, try to make sure the OS is
 * running on an Iris.  Since Iris does not support DMI, this is done
 * by reading its input port and seeing whether the read value is
 * meaningful.
 */
static int iris_probe(struct platform_device *pdev)
{
	unsigned char status = inb(IRIS_GIO_INPUT);
	if (status == IRIS_GIO_NODEV) {
		printk(KERN_ERR "This machine does not seem to be an Iris. "
			"Power off handler not installed.\n");
		return -ENODEV;
	}
	old_pm_power_off = pm_power_off;
	pm_power_off = &iris_power_off;
	printk(KERN_INFO "Iris power_off handler installed.\n");
	return 0;
}

static int iris_remove(struct platform_device *pdev)
{
	pm_power_off = old_pm_power_off;
	printk(KERN_INFO "Iris power_off handler uninstalled.\n");
	return 0;
}

static struct platform_driver iris_driver = {
	.driver		= {
		.name   = "iris",
	},
	.probe          = iris_probe,
	.remove         = iris_remove,
};

static struct resource iris_resources[] = {
	{
		.start  = IRIS_GIO_BASE,
		.end    = IRIS_GIO_OUTPUT,
		.flags  = IORESOURCE_IO,
		.name   = "address"
	}
};

static struct platform_device *iris_device;

static int iris_init(void)
{
	int ret;
	if (force != 1) {
		printk(KERN_ERR "The force parameter has not been set to 1."
			" The Iris poweroff handler will not be installed.\n");
		return -ENODEV;
	}
	ret = platform_driver_register(&iris_driver);
	if (ret < 0) {
		printk(KERN_ERR "Failed to register iris platform driver: %d\n",
			ret);
		return ret;
	}
	iris_device = platform_device_register_simple("iris", (-1),
				iris_resources, ARRAY_SIZE(iris_resources));
	if (IS_ERR(iris_device)) {
		printk(KERN_ERR "Failed to register iris platform device\n");
		platform_driver_unregister(&iris_driver);
		return PTR_ERR(iris_device);
	}
	return 0;
}

static void iris_exit(void)
{
	platform_device_unregister(iris_device);
	platform_driver_unregister(&iris_driver);
}

module_init(iris_init);
module_exit(iris_exit);