aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/dcdbas.txt91
-rw-r--r--MAINTAINERS5
-rw-r--r--drivers/firmware/Kconfig18
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/dcdbas.c596
-rw-r--r--drivers/firmware/dcdbas.h107
6 files changed, 818 insertions, 0 deletions
diff --git a/Documentation/dcdbas.txt b/Documentation/dcdbas.txt
new file mode 100644
index 000000000000..e1c52e2dc361
--- /dev/null
+++ b/Documentation/dcdbas.txt
@@ -0,0 +1,91 @@
1Overview
2
3The Dell Systems Management Base Driver provides a sysfs interface for
4systems management software such as Dell OpenManage to perform system
5management interrupts and host control actions (system power cycle or
6power off after OS shutdown) on certain Dell systems.
7
8Dell OpenManage requires this driver on the following Dell PowerEdge systems:
9300, 1300, 1400, 400SC, 500SC, 1500SC, 1550, 600SC, 1600SC, 650, 1655MC,
10700, and 750. Other Dell software such as the open source libsmbios project
11is expected to make use of this driver, and it may include the use of this
12driver on other Dell systems.
13
14The Dell libsmbios project aims towards providing access to as much BIOS
15information as possible. See http://linux.dell.com/libsmbios/main/ for
16more information about the libsmbios project.
17
18
19System Management Interrupt
20
21On some Dell systems, systems management software must access certain
22management information via a system management interrupt (SMI). The SMI data
23buffer must reside in 32-bit address space, and the physical address of the
24buffer is required for the SMI. The driver maintains the memory required for
25the SMI and provides a way for the application to generate the SMI.
26The driver creates the following sysfs entries for systems management
27software to perform these system management interrupts:
28
29/sys/devices/platform/dcdbas/smi_data
30/sys/devices/platform/dcdbas/smi_data_buf_phys_addr
31/sys/devices/platform/dcdbas/smi_data_buf_size
32/sys/devices/platform/dcdbas/smi_request
33
34Systems management software must perform the following steps to execute
35a SMI using this driver:
36
371) Lock smi_data.
382) Write system management command to smi_data.
393) Write "1" to smi_request to generate a calling interface SMI or
40 "2" to generate a raw SMI.
414) Read system management command response from smi_data.
425) Unlock smi_data.
43
44
45Host Control Action
46
47Dell OpenManage supports a host control feature that allows the administrator
48to perform a power cycle or power off of the system after the OS has finished
49shutting down. On some Dell systems, this host control feature requires that
50a driver perform a SMI after the OS has finished shutting down.
51
52The driver creates the following sysfs entries for systems management software
53to schedule the driver to perform a power cycle or power off host control
54action after the system has finished shutting down:
55
56/sys/devices/platform/dcdbas/host_control_action
57/sys/devices/platform/dcdbas/host_control_smi_type
58/sys/devices/platform/dcdbas/host_control_on_shutdown
59
60Dell OpenManage performs the following steps to execute a power cycle or
61power off host control action using this driver:
62
631) Write host control action to be performed to host_control_action.
642) Write type of SMI that driver needs to perform to host_control_smi_type.
653) Write "1" to host_control_on_shutdown to enable host control action.
664) Initiate OS shutdown.
67 (Driver will perform host control SMI when it is notified that the OS
68 has finished shutting down.)
69
70
71Host Control SMI Type
72
73The following table shows the value to write to host_control_smi_type to
74perform a power cycle or power off host control action:
75
76PowerEdge System Host Control SMI Type
77---------------- ---------------------
78 300 HC_SMITYPE_TYPE1
79 1300 HC_SMITYPE_TYPE1
80 1400 HC_SMITYPE_TYPE2
81 500SC HC_SMITYPE_TYPE2
82 1500SC HC_SMITYPE_TYPE2
83 1550 HC_SMITYPE_TYPE2
84 600SC HC_SMITYPE_TYPE2
85 1600SC HC_SMITYPE_TYPE2
86 650 HC_SMITYPE_TYPE2
87 1655MC HC_SMITYPE_TYPE2
88 700 HC_SMITYPE_TYPE3
89 750 HC_SMITYPE_TYPE3
90
91
diff --git a/MAINTAINERS b/MAINTAINERS
index 7e1f67130a16..1a35d335364d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -696,6 +696,11 @@ M: dz@debian.org
696W: http://www.debian.org/~dz/i8k/ 696W: http://www.debian.org/~dz/i8k/
697S: Maintained 697S: Maintained
698 698
699DELL SYSTEMS MANAGEMENT BASE DRIVER (dcdbas)
700P: Doug Warzecha
701M: Douglas_Warzecha@dell.com
702S: Maintained
703
699DEVICE-MAPPER 704DEVICE-MAPPER
700P: Alasdair Kergon 705P: Alasdair Kergon
701L: dm-devel@redhat.com 706L: dm-devel@redhat.com
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index b9b6e4273f01..327b58e64875 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -67,4 +67,22 @@ config DELL_RBU
67 supporting application to comunicate with the BIOS regarding the new 67 supporting application to comunicate with the BIOS regarding the new
68 image for the image update to take effect. 68 image for the image update to take effect.
69 See <file:Documentation/dell_rbu.txt> for more details on the driver. 69 See <file:Documentation/dell_rbu.txt> for more details on the driver.
70
71config DCDBAS
72 tristate "Dell Systems Management Base Driver"
73 depends on X86 || X86_64
74 default m
75 help
76 The Dell Systems Management Base Driver provides a sysfs interface
77 for systems management software to perform System Management
78 Interrupts (SMIs) and Host Control Actions (system power cycle or
79 power off after OS shutdown) on certain Dell systems.
80
81 See <file:Documentation/dcdbas.txt> for more details on the driver
82 and the Dell systems on which Dell systems management software makes
83 use of this driver.
84
85 Say Y or M here to enable the driver for use by Dell systems
86 management software such as Dell OpenManage.
87
70endmenu 88endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 186138572980..85429979d0db 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_EDD) += edd.o
5obj-$(CONFIG_EFI_VARS) += efivars.o 5obj-$(CONFIG_EFI_VARS) += efivars.o
6obj-$(CONFIG_EFI_PCDP) += pcdp.o 6obj-$(CONFIG_EFI_PCDP) += pcdp.o
7obj-$(CONFIG_DELL_RBU) += dell_rbu.o 7obj-$(CONFIG_DELL_RBU) += dell_rbu.o
8obj-$(CONFIG_DCDBAS) += dcdbas.o
diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c
new file mode 100644
index 000000000000..955537fe9958
--- /dev/null
+++ b/drivers/firmware/dcdbas.c
@@ -0,0 +1,596 @@
1/*
2 * dcdbas.c: Dell Systems Management Base Driver
3 *
4 * The Dell Systems Management Base Driver provides a sysfs interface for
5 * systems management software to perform System Management Interrupts (SMIs)
6 * and Host Control Actions (power cycle or power off after OS shutdown) on
7 * Dell systems.
8 *
9 * See Documentation/dcdbas.txt for more information.
10 *
11 * Copyright (C) 1995-2005 Dell Inc.
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License v2.0 as published by
15 * the Free Software Foundation.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 */
22
23#include <linux/device.h>
24#include <linux/dma-mapping.h>
25#include <linux/errno.h>
26#include <linux/init.h>
27#include <linux/kernel.h>
28#include <linux/mc146818rtc.h>
29#include <linux/module.h>
30#include <linux/reboot.h>
31#include <linux/sched.h>
32#include <linux/smp.h>
33#include <linux/spinlock.h>
34#include <linux/string.h>
35#include <linux/types.h>
36#include <asm/io.h>
37#include <asm/semaphore.h>
38
39#include "dcdbas.h"
40
41#define DRIVER_NAME "dcdbas"
42#define DRIVER_VERSION "5.6.0-1"
43#define DRIVER_DESCRIPTION "Dell Systems Management Base Driver"
44
45static struct platform_device *dcdbas_pdev;
46
47static u8 *smi_data_buf;
48static dma_addr_t smi_data_buf_handle;
49static unsigned long smi_data_buf_size;
50static u32 smi_data_buf_phys_addr;
51static DECLARE_MUTEX(smi_data_lock);
52
53static unsigned int host_control_action;
54static unsigned int host_control_smi_type;
55static unsigned int host_control_on_shutdown;
56
57/**
58 * smi_data_buf_free: free SMI data buffer
59 */
60static void smi_data_buf_free(void)
61{
62 if (!smi_data_buf)
63 return;
64
65 dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n",
66 __FUNCTION__, smi_data_buf_phys_addr, smi_data_buf_size);
67
68 dma_free_coherent(&dcdbas_pdev->dev, smi_data_buf_size, smi_data_buf,
69 smi_data_buf_handle);
70 smi_data_buf = NULL;
71 smi_data_buf_handle = 0;
72 smi_data_buf_phys_addr = 0;
73 smi_data_buf_size = 0;
74}
75
76/**
77 * smi_data_buf_realloc: grow SMI data buffer if needed
78 */
79static int smi_data_buf_realloc(unsigned long size)
80{
81 void *buf;
82 dma_addr_t handle;
83
84 if (smi_data_buf_size >= size)
85 return 0;
86
87 if (size > MAX_SMI_DATA_BUF_SIZE)
88 return -EINVAL;
89
90 /* new buffer is needed */
91 buf = dma_alloc_coherent(&dcdbas_pdev->dev, size, &handle, GFP_KERNEL);
92 if (!buf) {
93 dev_dbg(&dcdbas_pdev->dev,
94 "%s: failed to allocate memory size %lu\n",
95 __FUNCTION__, size);
96 return -ENOMEM;
97 }
98 /* memory zeroed by dma_alloc_coherent */
99
100 if (smi_data_buf)
101 memcpy(buf, smi_data_buf, smi_data_buf_size);
102
103 /* free any existing buffer */
104 smi_data_buf_free();
105
106 /* set up new buffer for use */
107 smi_data_buf = buf;
108 smi_data_buf_handle = handle;
109 smi_data_buf_phys_addr = (u32) virt_to_phys(buf);
110 smi_data_buf_size = size;
111
112 dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n",
113 __FUNCTION__, smi_data_buf_phys_addr, smi_data_buf_size);
114
115 return 0;
116}
117
118static ssize_t smi_data_buf_phys_addr_show(struct device *dev,
119 struct device_attribute *attr,
120 char *buf)
121{
122 return sprintf(buf, "%x\n", smi_data_buf_phys_addr);
123}
124
125static ssize_t smi_data_buf_size_show(struct device *dev,
126 struct device_attribute *attr,
127 char *buf)
128{
129 return sprintf(buf, "%lu\n", smi_data_buf_size);
130}
131
132static ssize_t smi_data_buf_size_store(struct device *dev,
133 struct device_attribute *attr,
134 const char *buf, size_t count)
135{
136 unsigned long buf_size;
137 ssize_t ret;
138
139 buf_size = simple_strtoul(buf, NULL, 10);
140
141 /* make sure SMI data buffer is at least buf_size */
142 down(&smi_data_lock);
143 ret = smi_data_buf_realloc(buf_size);
144 up(&smi_data_lock);
145 if (ret)
146 return ret;
147
148 return count;
149}
150
151static ssize_t smi_data_read(struct kobject *kobj, char *buf, loff_t pos,
152 size_t count)
153{
154 size_t max_read;
155 ssize_t ret;
156
157 down(&smi_data_lock);
158
159 if (pos >= smi_data_buf_size) {
160 ret = 0;
161 goto out;
162 }
163
164 max_read = smi_data_buf_size - pos;
165 ret = min(max_read, count);
166 memcpy(buf, smi_data_buf + pos, ret);
167out:
168 up(&smi_data_lock);
169 return ret;
170}
171
172static ssize_t smi_data_write(struct kobject *kobj, char *buf, loff_t pos,
173 size_t count)
174{
175 ssize_t ret;
176
177 down(&smi_data_lock);
178
179 ret = smi_data_buf_realloc(pos + count);
180 if (ret)
181 goto out;
182
183 memcpy(smi_data_buf + pos, buf, count);
184 ret = count;
185out:
186 up(&smi_data_lock);
187 return ret;
188}
189
190static ssize_t host_control_action_show(struct device *dev,
191 struct device_attribute *attr,
192 char *buf)
193{
194 return sprintf(buf, "%u\n", host_control_action);
195}
196
197static ssize_t host_control_action_store(struct device *dev,
198 struct device_attribute *attr,
199 const char *buf, size_t count)
200{
201 ssize_t ret;
202
203 /* make sure buffer is available for host control command */
204 down(&smi_data_lock);
205 ret = smi_data_buf_realloc(sizeof(struct apm_cmd));
206 up(&smi_data_lock);
207 if (ret)
208 return ret;
209
210 host_control_action = simple_strtoul(buf, NULL, 10);
211 return count;
212}
213
214static ssize_t host_control_smi_type_show(struct device *dev,
215 struct device_attribute *attr,
216 char *buf)
217{
218 return sprintf(buf, "%u\n", host_control_smi_type);
219}
220
221static ssize_t host_control_smi_type_store(struct device *dev,
222 struct device_attribute *attr,
223 const char *buf, size_t count)
224{
225 host_control_smi_type = simple_strtoul(buf, NULL, 10);
226 return count;
227}
228
229static ssize_t host_control_on_shutdown_show(struct device *dev,
230 struct device_attribute *attr,
231 char *buf)
232{
233 return sprintf(buf, "%u\n", host_control_on_shutdown);
234}
235
236static ssize_t host_control_on_shutdown_store(struct device *dev,
237 struct device_attribute *attr,
238 const char *buf, size_t count)
239{
240 host_control_on_shutdown = simple_strtoul(buf, NULL, 10);
241 return count;
242}
243
244/**
245 * smi_request: generate SMI request
246 *
247 * Called with smi_data_lock.
248 */
249static int smi_request(struct smi_cmd *smi_cmd)
250{
251 cpumask_t old_mask;
252 int ret = 0;
253
254 if (smi_cmd->magic != SMI_CMD_MAGIC) {
255 dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n",
256 __FUNCTION__);
257 return -EBADR;
258 }
259
260 /* SMI requires CPU 0 */
261 old_mask = current->cpus_allowed;
262 set_cpus_allowed(current, cpumask_of_cpu(0));
263 if (smp_processor_id() != 0) {
264 dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n",
265 __FUNCTION__);
266 ret = -EBUSY;
267 goto out;
268 }
269
270 /* generate SMI */
271 asm volatile (
272 "outb %b0,%w1"
273 : /* no output args */
274 : "a" (smi_cmd->command_code),
275 "d" (smi_cmd->command_address),
276 "b" (smi_cmd->ebx),
277 "c" (smi_cmd->ecx)
278 : "memory"
279 );
280
281out:
282 set_cpus_allowed(current, old_mask);
283 return ret;
284}
285
286/**
287 * smi_request_store:
288 *
289 * The valid values are:
290 * 0: zero SMI data buffer
291 * 1: generate calling interface SMI
292 * 2: generate raw SMI
293 *
294 * User application writes smi_cmd to smi_data before telling driver
295 * to generate SMI.
296 */
297static ssize_t smi_request_store(struct device *dev,
298 struct device_attribute *attr,
299 const char *buf, size_t count)
300{
301 struct smi_cmd *smi_cmd;
302 unsigned long val = simple_strtoul(buf, NULL, 10);
303 ssize_t ret;
304
305 down(&smi_data_lock);
306
307 if (smi_data_buf_size < sizeof(struct smi_cmd)) {
308 ret = -ENODEV;
309 goto out;
310 }
311 smi_cmd = (struct smi_cmd *)smi_data_buf;
312
313 switch (val) {
314 case 2:
315 /* Raw SMI */
316 ret = smi_request(smi_cmd);
317 if (!ret)
318 ret = count;
319 break;
320 case 1:
321 /* Calling Interface SMI */
322 smi_cmd->ebx = (u32) virt_to_phys(smi_cmd->command_buffer);
323 ret = smi_request(smi_cmd);
324 if (!ret)
325 ret = count;
326 break;
327 case 0:
328 memset(smi_data_buf, 0, smi_data_buf_size);
329 ret = count;
330 break;
331 default:
332 ret = -EINVAL;
333 break;
334 }
335
336out:
337 up(&smi_data_lock);
338 return ret;
339}
340
341/**
342 * host_control_smi: generate host control SMI
343 *
344 * Caller must set up the host control command in smi_data_buf.
345 */
346static int host_control_smi(void)
347{
348 struct apm_cmd *apm_cmd;
349 u8 *data;
350 unsigned long flags;
351 u32 num_ticks;
352 s8 cmd_status;
353 u8 index;
354
355 apm_cmd = (struct apm_cmd *)smi_data_buf;
356 apm_cmd->status = ESM_STATUS_CMD_UNSUCCESSFUL;
357
358 switch (host_control_smi_type) {
359 case HC_SMITYPE_TYPE1:
360 spin_lock_irqsave(&rtc_lock, flags);
361 /* write SMI data buffer physical address */
362 data = (u8 *)&smi_data_buf_phys_addr;
363 for (index = PE1300_CMOS_CMD_STRUCT_PTR;
364 index < (PE1300_CMOS_CMD_STRUCT_PTR + 4);
365 index++, data++) {
366 outb(index,
367 (CMOS_BASE_PORT + CMOS_PAGE2_INDEX_PORT_PIIX4));
368 outb(*data,
369 (CMOS_BASE_PORT + CMOS_PAGE2_DATA_PORT_PIIX4));
370 }
371
372 /* first set status to -1 as called by spec */
373 cmd_status = ESM_STATUS_CMD_UNSUCCESSFUL;
374 outb((u8) cmd_status, PCAT_APM_STATUS_PORT);
375
376 /* generate SMM call */
377 outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT);
378 spin_unlock_irqrestore(&rtc_lock, flags);
379
380 /* wait a few to see if it executed */
381 num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING;
382 while ((cmd_status = inb(PCAT_APM_STATUS_PORT))
383 == ESM_STATUS_CMD_UNSUCCESSFUL) {
384 num_ticks--;
385 if (num_ticks == EXPIRED_TIMER)
386 return -ETIME;
387 }
388 break;
389
390 case HC_SMITYPE_TYPE2:
391 case HC_SMITYPE_TYPE3:
392 spin_lock_irqsave(&rtc_lock, flags);
393 /* write SMI data buffer physical address */
394 data = (u8 *)&smi_data_buf_phys_addr;
395 for (index = PE1400_CMOS_CMD_STRUCT_PTR;
396 index < (PE1400_CMOS_CMD_STRUCT_PTR + 4);
397 index++, data++) {
398 outb(index, (CMOS_BASE_PORT + CMOS_PAGE1_INDEX_PORT));
399 outb(*data, (CMOS_BASE_PORT + CMOS_PAGE1_DATA_PORT));
400 }
401
402 /* generate SMM call */
403 if (host_control_smi_type == HC_SMITYPE_TYPE3)
404 outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT);
405 else
406 outb(ESM_APM_CMD, PE1400_APM_CONTROL_PORT);
407
408 /* restore RTC index pointer since it was written to above */
409 CMOS_READ(RTC_REG_C);
410 spin_unlock_irqrestore(&rtc_lock, flags);
411
412 /* read control port back to serialize write */
413 cmd_status = inb(PE1400_APM_CONTROL_PORT);
414
415 /* wait a few to see if it executed */
416 num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING;
417 while (apm_cmd->status == ESM_STATUS_CMD_UNSUCCESSFUL) {
418 num_ticks--;
419 if (num_ticks == EXPIRED_TIMER)
420 return -ETIME;
421 }
422 break;
423
424 default:
425 dev_dbg(&dcdbas_pdev->dev, "%s: invalid SMI type %u\n",
426 __FUNCTION__, host_control_smi_type);
427 return -ENOSYS;
428 }
429
430 return 0;
431}
432
433/**
434 * dcdbas_host_control: initiate host control
435 *
436 * This function is called by the driver after the system has
437 * finished shutting down if the user application specified a
438 * host control action to perform on shutdown. It is safe to
439 * use smi_data_buf at this point because the system has finished
440 * shutting down and no userspace apps are running.
441 */
442static void dcdbas_host_control(void)
443{
444 struct apm_cmd *apm_cmd;
445 u8 action;
446
447 if (host_control_action == HC_ACTION_NONE)
448 return;
449
450 action = host_control_action;
451 host_control_action = HC_ACTION_NONE;
452
453 if (!smi_data_buf) {
454 dev_dbg(&dcdbas_pdev->dev, "%s: no SMI buffer\n", __FUNCTION__);
455 return;
456 }
457
458 if (smi_data_buf_size < sizeof(struct apm_cmd)) {
459 dev_dbg(&dcdbas_pdev->dev, "%s: SMI buffer too small\n",
460 __FUNCTION__);
461 return;
462 }
463
464 apm_cmd = (struct apm_cmd *)smi_data_buf;
465
466 /* power off takes precedence */
467 if (action & HC_ACTION_HOST_CONTROL_POWEROFF) {
468 apm_cmd->command = ESM_APM_POWER_CYCLE;
469 apm_cmd->reserved = 0;
470 *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 0;
471 host_control_smi();
472 } else if (action & HC_ACTION_HOST_CONTROL_POWERCYCLE) {
473 apm_cmd->command = ESM_APM_POWER_CYCLE;
474 apm_cmd->reserved = 0;
475 *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 20;
476 host_control_smi();
477 }
478}
479
480/**
481 * dcdbas_reboot_notify: handle reboot notification for host control
482 */
483static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code,
484 void *unused)
485{
486 static unsigned int notify_cnt = 0;
487
488 switch (code) {
489 case SYS_DOWN:
490 case SYS_HALT:
491 case SYS_POWER_OFF:
492 if (host_control_on_shutdown) {
493 /* firmware is going to perform host control action */
494 if (++notify_cnt == 2) {
495 printk(KERN_WARNING
496 "Please wait for shutdown "
497 "action to complete...\n");
498 dcdbas_host_control();
499 }
500 /*
501 * register again and initiate the host control
502 * action on the second notification to allow
503 * everyone that registered to be notified
504 */
505 register_reboot_notifier(nb);
506 }
507 break;
508 }
509
510 return NOTIFY_DONE;
511}
512
513static struct notifier_block dcdbas_reboot_nb = {
514 .notifier_call = dcdbas_reboot_notify,
515 .next = NULL,
516 .priority = 0
517};
518
519static DCDBAS_BIN_ATTR_RW(smi_data);
520
521static struct bin_attribute *dcdbas_bin_attrs[] = {
522 &bin_attr_smi_data,
523 NULL
524};
525
526static DCDBAS_DEV_ATTR_RW(smi_data_buf_size);
527static DCDBAS_DEV_ATTR_RO(smi_data_buf_phys_addr);
528static DCDBAS_DEV_ATTR_WO(smi_request);
529static DCDBAS_DEV_ATTR_RW(host_control_action);
530static DCDBAS_DEV_ATTR_RW(host_control_smi_type);
531static DCDBAS_DEV_ATTR_RW(host_control_on_shutdown);
532
533static struct device_attribute *dcdbas_dev_attrs[] = {
534 &dev_attr_smi_data_buf_size,
535 &dev_attr_smi_data_buf_phys_addr,
536 &dev_attr_smi_request,
537 &dev_attr_host_control_action,
538 &dev_attr_host_control_smi_type,
539 &dev_attr_host_control_on_shutdown,
540 NULL
541};
542
543/**
544 * dcdbas_init: initialize driver
545 */
546static int __init dcdbas_init(void)
547{
548 int i;
549
550 host_control_action = HC_ACTION_NONE;
551 host_control_smi_type = HC_SMITYPE_NONE;
552
553 dcdbas_pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
554 if (IS_ERR(dcdbas_pdev))
555 return PTR_ERR(dcdbas_pdev);
556
557 /*
558 * BIOS SMI calls require buffer addresses be in 32-bit address space.
559 * This is done by setting the DMA mask below.
560 */
561 dcdbas_pdev->dev.coherent_dma_mask = DMA_32BIT_MASK;
562 dcdbas_pdev->dev.dma_mask = &dcdbas_pdev->dev.coherent_dma_mask;
563
564 register_reboot_notifier(&dcdbas_reboot_nb);
565
566 for (i = 0; dcdbas_bin_attrs[i]; i++)
567 sysfs_create_bin_file(&dcdbas_pdev->dev.kobj,
568 dcdbas_bin_attrs[i]);
569
570 for (i = 0; dcdbas_dev_attrs[i]; i++)
571 device_create_file(&dcdbas_pdev->dev, dcdbas_dev_attrs[i]);
572
573 dev_info(&dcdbas_pdev->dev, "%s (version %s)\n",
574 DRIVER_DESCRIPTION, DRIVER_VERSION);
575
576 return 0;
577}
578
579/**
580 * dcdbas_exit: perform driver cleanup
581 */
582static void __exit dcdbas_exit(void)
583{
584 platform_device_unregister(dcdbas_pdev);
585 unregister_reboot_notifier(&dcdbas_reboot_nb);
586 smi_data_buf_free();
587}
588
589module_init(dcdbas_init);
590module_exit(dcdbas_exit);
591
592MODULE_DESCRIPTION(DRIVER_DESCRIPTION " (version " DRIVER_VERSION ")");
593MODULE_VERSION(DRIVER_VERSION);
594MODULE_AUTHOR("Dell Inc.");
595MODULE_LICENSE("GPL");
596
diff --git a/drivers/firmware/dcdbas.h b/drivers/firmware/dcdbas.h
new file mode 100644
index 000000000000..58a85182b3e8
--- /dev/null
+++ b/drivers/firmware/dcdbas.h
@@ -0,0 +1,107 @@
1/*
2 * dcdbas.h: Definitions for Dell Systems Management Base driver
3 *
4 * Copyright (C) 1995-2005 Dell Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License v2.0 as published by
8 * the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#ifndef _DCDBAS_H_
17#define _DCDBAS_H_
18
19#include <linux/device.h>
20#include <linux/input.h>
21#include <linux/sysfs.h>
22#include <linux/types.h>
23
24#define MAX_SMI_DATA_BUF_SIZE (256 * 1024)
25
26#define HC_ACTION_NONE (0)
27#define HC_ACTION_HOST_CONTROL_POWEROFF BIT(1)
28#define HC_ACTION_HOST_CONTROL_POWERCYCLE BIT(2)
29
30#define HC_SMITYPE_NONE (0)
31#define HC_SMITYPE_TYPE1 (1)
32#define HC_SMITYPE_TYPE2 (2)
33#define HC_SMITYPE_TYPE3 (3)
34
35#define ESM_APM_CMD (0x0A0)
36#define ESM_APM_POWER_CYCLE (0x10)
37#define ESM_STATUS_CMD_UNSUCCESSFUL (-1)
38
39#define CMOS_BASE_PORT (0x070)
40#define CMOS_PAGE1_INDEX_PORT (0)
41#define CMOS_PAGE1_DATA_PORT (1)
42#define CMOS_PAGE2_INDEX_PORT_PIIX4 (2)
43#define CMOS_PAGE2_DATA_PORT_PIIX4 (3)
44#define PE1400_APM_CONTROL_PORT (0x0B0)
45#define PCAT_APM_CONTROL_PORT (0x0B2)
46#define PCAT_APM_STATUS_PORT (0x0B3)
47#define PE1300_CMOS_CMD_STRUCT_PTR (0x38)
48#define PE1400_CMOS_CMD_STRUCT_PTR (0x70)
49
50#define MAX_SYSMGMT_SHORTCMD_PARMBUF_LEN (14)
51#define MAX_SYSMGMT_LONGCMD_SGENTRY_NUM (16)
52
53#define TIMEOUT_USEC_SHORT_SEMA_BLOCKING (10000)
54#define EXPIRED_TIMER (0)
55
56#define SMI_CMD_MAGIC (0x534D4931)
57
58#define DCDBAS_DEV_ATTR_RW(_name) \
59 DEVICE_ATTR(_name,0600,_name##_show,_name##_store);
60
61#define DCDBAS_DEV_ATTR_RO(_name) \
62 DEVICE_ATTR(_name,0400,_name##_show,NULL);
63
64#define DCDBAS_DEV_ATTR_WO(_name) \
65 DEVICE_ATTR(_name,0200,NULL,_name##_store);
66
67#define DCDBAS_BIN_ATTR_RW(_name) \
68struct bin_attribute bin_attr_##_name = { \
69 .attr = { .name = __stringify(_name), \
70 .mode = 0600, \
71 .owner = THIS_MODULE }, \
72 .read = _name##_read, \
73 .write = _name##_write, \
74}
75
76struct smi_cmd {
77 __u32 magic;
78 __u32 ebx;
79 __u32 ecx;
80 __u16 command_address;
81 __u8 command_code;
82 __u8 reserved;
83 __u8 command_buffer[1];
84} __attribute__ ((packed));
85
86struct apm_cmd {
87 __u8 command;
88 __s8 status;
89 __u16 reserved;
90 union {
91 struct {
92 __u8 parm[MAX_SYSMGMT_SHORTCMD_PARMBUF_LEN];
93 } __attribute__ ((packed)) shortreq;
94
95 struct {
96 __u16 num_sg_entries;
97 struct {
98 __u32 size;
99 __u64 addr;
100 } __attribute__ ((packed))
101 sglist[MAX_SYSMGMT_LONGCMD_SGENTRY_NUM];
102 } __attribute__ ((packed)) longreq;
103 } __attribute__ ((packed)) parameters;
104} __attribute__ ((packed));
105
106#endif /* _DCDBAS_H_ */
107