aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorVernon Mauery <vernux@us.ibm.com>2010-10-05 18:47:18 -0400
committerMatthew Garrett <mjg@redhat.com>2010-10-21 10:10:46 -0400
commit35f0ce032b0f2d6974da516b5a113f49b7b70b09 (patch)
treec988974a4e0cb06e0bff53f29e6541d21631e16a /drivers/platform
parent260586d2b444909380137de6c6423e5b44edf4db (diff)
IBM Real-Time "SMI Free" mode driver -v7
After a period of RFC for this driver, I think it is ready for inclusion in the platform-driver-x86 tree, hopefully to be staged in the next merge window into Linus's tree. --Vernon ------------------------------------------------------------ IBM Real-Time "SMI Free" mode driver This driver supports the Real-Time Linux (RTL) BIOS feature. The RTL feature allows non-fatal System Management Interrupts (SMIs) to be disabled on supported IBM platforms and is intended to be coupled with a user-space daemon to monitor the hardware in a way that can be prioritized and scheduled to better suit the requirements for the system. The Device is presented as a special "_RTL_" table to the OS in the Extended BIOS Data Area. There is a simple protocol for entering and exiting the mode at runtime. This driver creates a simple sysfs interface to allow a simple entry and exit from RTL mode in the UFI/BIOS. Since the driver is specific to IBM SystemX hardware (x86- based servers) it only builds on x86 builds. To reduce the risk of loading on the wrong hardware, the module uses DMI information and checks a list of servers that are known to work. Signed-off-by: Vernon Mauery <vernux@us.ibm.com> Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/Kconfig16
-rw-r--r--drivers/platform/x86/Makefile2
-rw-r--r--drivers/platform/x86/ibm_rtl.c341
3 files changed, 358 insertions, 1 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 91e431b6f20f..faec777b1ed4 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -615,6 +615,22 @@ config INTEL_IPS
615 functionality. If in doubt, say Y here; it will only load on 615 functionality. If in doubt, say Y here; it will only load on
616 supported platforms. 616 supported platforms.
617 617
618config IBM_RTL
619 tristate "Device driver to enable PRTL support"
620 depends on X86 && PCI
621 ---help---
622 Enable support for IBM Premium Real Time Mode (PRTM).
623 This module will allow you the enter and exit PRTM in the BIOS via
624 sysfs on platforms that support this feature. System in PRTM will
625 not receive CPU-generated SMIs for recoverable errors. Use of this
626 feature without proper support may void your hardware warranty.
627
628 If the proper BIOS support is found the driver will load and create
629 /sys/devices/system/ibm_rtl/. The "state" variable will indicate
630 whether or not the BIOS is in PRTM.
631 state = 0 (BIOS SMIs on)
632 state = 1 (BIOS SMIs off)
633
618config XO1_RFKILL 634config XO1_RFKILL
619 tristate "OLPC XO-1 software RF kill switch" 635 tristate "OLPC XO-1 software RF kill switch"
620 depends on OLPC 636 depends on OLPC
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index c15e96336e65..9950ccc940b5 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -32,4 +32,4 @@ obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
32obj-$(CONFIG_INTEL_IPS) += intel_ips.o 32obj-$(CONFIG_INTEL_IPS) += intel_ips.o
33obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o 33obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
34obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o 34obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
35 35obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c
new file mode 100644
index 000000000000..3c2c6b91ecb3
--- /dev/null
+++ b/drivers/platform/x86/ibm_rtl.c
@@ -0,0 +1,341 @@
1/*
2 * IBM Real-Time Linux driver
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * Copyright (C) IBM Corporation, 2010
19 *
20 * Author: Keith Mannthey <kmannth@us.ibm.com>
21 * Vernon Mauery <vernux@us.ibm.com>
22 *
23 */
24
25#include <linux/kernel.h>
26#include <linux/delay.h>
27#include <linux/module.h>
28#include <linux/io.h>
29#include <linux/sysdev.h>
30#include <linux/dmi.h>
31#include <linux/mutex.h>
32#include <asm/bios_ebda.h>
33
34static bool force;
35module_param(force, bool, 0);
36MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
37
38static bool debug;
39module_param(debug, bool, 0644);
40MODULE_PARM_DESC(debug, "Show debug output");
41
42MODULE_LICENSE("GPL");
43MODULE_AUTHOR("Keith Mannthey <kmmanth@us.ibm.com>");
44MODULE_AUTHOR("Vernon Mauery <vernux@us.ibm.com>");
45
46#define RTL_ADDR_TYPE_IO 1
47#define RTL_ADDR_TYPE_MMIO 2
48
49#define RTL_CMD_ENTER_PRTM 1
50#define RTL_CMD_EXIT_PRTM 2
51
52/* The RTL table as presented by the EBDA: */
53struct ibm_rtl_table {
54 char signature[5]; /* signature should be "_RTL_" */
55 u8 version;
56 u8 rt_status;
57 u8 command;
58 u8 command_status;
59 u8 cmd_address_type;
60 u8 cmd_granularity;
61 u8 cmd_offset;
62 u16 reserve1;
63 u32 cmd_port_address; /* platform dependent address */
64 u32 cmd_port_value; /* platform dependent value */
65} __attribute__((packed));
66
67/* to locate "_RTL_" signature do a masked 5-byte integer compare */
68#define RTL_SIGNATURE 0x0000005f4c54525fULL
69#define RTL_MASK 0x000000ffffffffffULL
70
71#define RTL_DEBUG(A, ...) do { \
72 if (debug) \
73 pr_info("ibm-rtl: " A, ##__VA_ARGS__ ); \
74} while (0)
75
76static DEFINE_MUTEX(rtl_lock);
77static struct ibm_rtl_table __iomem *rtl_table;
78static void __iomem *ebda_map;
79static void __iomem *rtl_cmd_addr;
80static u8 rtl_cmd_type;
81static u8 rtl_cmd_width;
82
83static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len)
84{
85 if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
86 return ioremap(addr, len);
87 return ioport_map(addr, len);
88}
89
90static void rtl_port_unmap(void __iomem *addr)
91{
92 if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
93 iounmap(addr);
94 else
95 ioport_unmap(addr);
96}
97
98static int ibm_rtl_write(u8 value)
99{
100 int ret = 0, count = 0;
101 static u32 cmd_port_val;
102
103 RTL_DEBUG("%s(%d)\n", __FUNCTION__, value);
104
105 value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM;
106
107 mutex_lock(&rtl_lock);
108
109 if (ioread8(&rtl_table->rt_status) != value) {
110 iowrite8(value, &rtl_table->command);
111
112 switch (rtl_cmd_width) {
113 case 8:
114 cmd_port_val = ioread8(&rtl_table->cmd_port_value);
115 RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
116 iowrite8((u8)cmd_port_val, rtl_cmd_addr);
117 break;
118 case 16:
119 cmd_port_val = ioread16(&rtl_table->cmd_port_value);
120 RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
121 iowrite16((u16)cmd_port_val, rtl_cmd_addr);
122 break;
123 case 32:
124 cmd_port_val = ioread32(&rtl_table->cmd_port_value);
125 RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
126 iowrite32(cmd_port_val, rtl_cmd_addr);
127 break;
128 }
129
130 while (ioread8(&rtl_table->command)) {
131 msleep(10);
132 if (count++ > 500) {
133 pr_err("ibm-rtl: Hardware not responding to "
134 "mode switch request\n");
135 ret = -EIO;
136 break;
137 }
138
139 }
140
141 if (ioread8(&rtl_table->command_status)) {
142 RTL_DEBUG("command_status reports failed command\n");
143 ret = -EIO;
144 }
145 }
146
147 mutex_unlock(&rtl_lock);
148 return ret;
149}
150
151static ssize_t rtl_show_version(struct sysdev_class * dev,
152 struct sysdev_class_attribute *attr,
153 char *buf)
154{
155 return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version));
156}
157
158static ssize_t rtl_show_state(struct sysdev_class *dev,
159 struct sysdev_class_attribute *attr,
160 char *buf)
161{
162 return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status));
163}
164
165static ssize_t rtl_set_state(struct sysdev_class *dev,
166 struct sysdev_class_attribute *attr,
167 const char *buf,
168 size_t count)
169{
170 ssize_t ret;
171
172 if (count < 1 || count > 2)
173 return -EINVAL;
174
175 switch (buf[0]) {
176 case '0':
177 ret = ibm_rtl_write(0);
178 break;
179 case '1':
180 ret = ibm_rtl_write(1);
181 break;
182 default:
183 ret = -EINVAL;
184 }
185 if (ret >= 0)
186 ret = count;
187
188 return ret;
189}
190
191static struct sysdev_class class_rtl = {
192 .name = "ibm_rtl",
193};
194
195static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL);
196static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state);
197
198static struct sysdev_class_attribute *rtl_attributes[] = {
199 &attr_version,
200 &attr_state,
201 NULL
202};
203
204
205static int rtl_setup_sysfs(void) {
206 int ret, i;
207 ret = sysdev_class_register(&class_rtl);
208
209 if (!ret) {
210 for (i = 0; rtl_attributes[i]; i ++)
211 sysdev_class_create_file(&class_rtl, rtl_attributes[i]);
212 }
213 return ret;
214}
215
216static void rtl_teardown_sysfs(void) {
217 int i;
218 for (i = 0; rtl_attributes[i]; i ++)
219 sysdev_class_remove_file(&class_rtl, rtl_attributes[i]);
220 sysdev_class_unregister(&class_rtl);
221}
222
223static int dmi_check_cb(const struct dmi_system_id *id)
224{
225 RTL_DEBUG("found IBM server '%s'\n", id->ident);
226 return 0;
227}
228
229#define ibm_dmi_entry(NAME, TYPE) \
230{ \
231 .ident = NAME, \
232 .matches = { \
233 DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \
234 DMI_MATCH(DMI_PRODUCT_NAME, TYPE), \
235 }, \
236 .callback = dmi_check_cb \
237}
238
239static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = {
240 ibm_dmi_entry("BladeCenter LS21", "7971"),
241 ibm_dmi_entry("BladeCenter LS22", "7901"),
242 ibm_dmi_entry("BladeCenter HS21 XM", "7995"),
243 ibm_dmi_entry("BladeCenter HS22", "7870"),
244 ibm_dmi_entry("BladeCenter HS22V", "7871"),
245 ibm_dmi_entry("System x3550 M2", "7946"),
246 ibm_dmi_entry("System x3650 M2", "7947"),
247 ibm_dmi_entry("System x3550 M3", "7944"),
248 ibm_dmi_entry("System x3650 M3", "7945"),
249 { }
250};
251
252static int __init ibm_rtl_init(void) {
253 unsigned long ebda_addr, ebda_size;
254 unsigned int ebda_kb;
255 int ret = -ENODEV, i;
256
257 if (force)
258 pr_warning("ibm-rtl: module loaded by force\n");
259 /* first ensure that we are running on IBM HW */
260 else if (!dmi_check_system(ibm_rtl_dmi_table))
261 return -ENODEV;
262
263 /* Get the address for the Extended BIOS Data Area */
264 ebda_addr = get_bios_ebda();
265 if (!ebda_addr) {
266 RTL_DEBUG("no BIOS EBDA found\n");
267 return -ENODEV;
268 }
269
270 ebda_map = ioremap(ebda_addr, 4);
271 if (!ebda_map)
272 return -ENOMEM;
273
274 /* First word in the EDBA is the Size in KB */
275 ebda_kb = ioread16(ebda_map);
276 RTL_DEBUG("EBDA is %d kB\n", ebda_kb);
277
278 if (ebda_kb == 0)
279 goto out;
280
281 iounmap(ebda_map);
282 ebda_size = ebda_kb*1024;
283
284 /* Remap the whole table */
285 ebda_map = ioremap(ebda_addr, ebda_size);
286 if (!ebda_map)
287 return -ENOMEM;
288
289 /* search for the _RTL_ signature at the start of the table */
290 for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) {
291 struct ibm_rtl_table __iomem * tmp;
292 tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i);
293 if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) {
294 phys_addr_t addr;
295 unsigned int plen;
296 RTL_DEBUG("found RTL_SIGNATURE at %#llx\n", (u64)tmp);
297 rtl_table = tmp;
298 /* The address, value, width and offset are platform
299 * dependent and found in the ibm_rtl_table */
300 rtl_cmd_width = ioread8(&rtl_table->cmd_granularity);
301 rtl_cmd_type = ioread8(&rtl_table->cmd_address_type);
302 RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n",
303 rtl_cmd_width, rtl_cmd_type);
304 addr = ioread32(&rtl_table->cmd_port_address);
305 RTL_DEBUG("addr = %#llx\n", addr);
306 plen = rtl_cmd_width/sizeof(char);
307 rtl_cmd_addr = rtl_port_map(addr, plen);
308 RTL_DEBUG("rtl_cmd_addr = %#llx\n", (u64)rtl_cmd_addr);
309 if (!rtl_cmd_addr) {
310 ret = -ENOMEM;
311 break;
312 }
313 ret = rtl_setup_sysfs();
314 break;
315 }
316 }
317
318out:
319 if (ret) {
320 iounmap(ebda_map);
321 rtl_port_unmap(rtl_cmd_addr);
322 }
323
324 return ret;
325}
326
327static void __exit ibm_rtl_exit(void)
328{
329 if (rtl_table) {
330 RTL_DEBUG("cleaning up");
331 /* do not leave the machine in SMI-free mode */
332 ibm_rtl_write(0);
333 /* unmap, unlink and remove all traces */
334 rtl_teardown_sysfs();
335 iounmap(ebda_map);
336 rtl_port_unmap(rtl_cmd_addr);
337 }
338}
339
340module_init(ibm_rtl_init);
341module_exit(ibm_rtl_exit);