File Index
Files and directories
Sign in
FILE

dcdbas.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/dell/dcdbas.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 *  dcdbas.c: Dell Systems Management Base Driver
  4 *
  5 *  The Dell Systems Management Base Driver provides a sysfs interface for
  6 *  systems management software to perform System Management Interrupts (SMIs)
  7 *  and Host Control Actions (power cycle or power off after OS shutdown) on
  8 *  Dell systems.
  9 *
 10 *  See Documentation/userspace-api/dcdbas.rst for more information.
 11 *
 12 *  Copyright (C) 1995-2006 Dell Inc.
 13 */
 14
 15#include <linux/platform_device.h>
 16#include <linux/acpi.h>
 17#include <linux/dma-mapping.h>
 18#include <linux/dmi.h>
 19#include <linux/errno.h>
 20#include <linux/cpu.h>
 21#include <linux/gfp.h>
 22#include <linux/init.h>
 23#include <linux/io.h>
 24#include <linux/kernel.h>
 25#include <linux/mc146818rtc.h>
 26#include <linux/module.h>
 27#include <linux/reboot.h>
 28#include <linux/sched.h>
 29#include <linux/smp.h>
 30#include <linux/spinlock.h>
 31#include <linux/string.h>
 32#include <linux/sysfs.h>
 33#include <linux/types.h>
 34#include <linux/mutex.h>
 35
 36#include "dcdbas.h"
 37
 38#define DRIVER_NAME		"dcdbas"
 39#define DRIVER_VERSION		"5.6.0-3.4"
 40#define DRIVER_DESCRIPTION	"Dell Systems Management Base Driver"
 41
 42static struct platform_device *dcdbas_pdev;
 43
 44static unsigned long max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE;
 45static DEFINE_MUTEX(smi_data_lock);
 46static u8 *bios_buffer;
 47static struct smi_buffer smi_buf;
 48
 49static unsigned int host_control_action;
 50static unsigned int host_control_smi_type;
 51static unsigned int host_control_on_shutdown;
 52
 53static bool wsmt_enabled;
 54
 55int dcdbas_smi_alloc(struct smi_buffer *smi_buffer, unsigned long size)
 56{
 57	smi_buffer->virt = dma_alloc_coherent(&dcdbas_pdev->dev, size,
 58					      &smi_buffer->dma, GFP_KERNEL);
 59	if (!smi_buffer->virt) {
 60		dev_dbg(&dcdbas_pdev->dev,
 61			"%s: failed to allocate memory size %lu\n",
 62			__func__, size);
 63		return -ENOMEM;
 64	}
 65	smi_buffer->size = size;
 66
 67	dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n",
 68		__func__, (u32)smi_buffer->dma, smi_buffer->size);
 69
 70	return 0;
 71}
 72EXPORT_SYMBOL_GPL(dcdbas_smi_alloc);
 73
 74void dcdbas_smi_free(struct smi_buffer *smi_buffer)
 75{
 76	if (!smi_buffer->virt)
 77		return;
 78
 79	dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n",
 80		__func__, (u32)smi_buffer->dma, smi_buffer->size);
 81	dma_free_coherent(&dcdbas_pdev->dev, smi_buffer->size,
 82			  smi_buffer->virt, smi_buffer->dma);
 83	smi_buffer->virt = NULL;
 84	smi_buffer->dma = 0;
 85	smi_buffer->size = 0;
 86}
 87EXPORT_SYMBOL_GPL(dcdbas_smi_free);
 88
 89/**
 90 * smi_data_buf_free: free SMI data buffer
 91 */
 92static void smi_data_buf_free(void)
 93{
 94	if (!smi_buf.virt || wsmt_enabled)
 95		return;
 96
 97	dcdbas_smi_free(&smi_buf);
 98}
 99
100/**
101 * smi_data_buf_realloc: grow SMI data buffer if needed
102 */
103static int smi_data_buf_realloc(unsigned long size)
104{
105	struct smi_buffer tmp;
106	int ret;
107
108	if (smi_buf.size >= size)
109		return 0;
110
111	if (size > max_smi_data_buf_size)
112		return -EINVAL;
113
114	/* new buffer is needed */
115	ret = dcdbas_smi_alloc(&tmp, size);
116	if (ret)
117		return ret;
118
119	/* memory zeroed by dma_alloc_coherent */
120	if (smi_buf.virt)
121		memcpy(tmp.virt, smi_buf.virt, smi_buf.size);
122
123	/* free any existing buffer */
124	smi_data_buf_free();
125
126	/* set up new buffer for use */
127	smi_buf = tmp;
128
129	return 0;
130}
131
132static ssize_t smi_data_buf_phys_addr_show(struct device *dev,
133					   struct device_attribute *attr,
134					   char *buf)
135{
136	return sysfs_emit(buf, "%x\n", (u32)smi_buf.dma);
137}
138
139static ssize_t smi_data_buf_size_show(struct device *dev,
140				      struct device_attribute *attr,
141				      char *buf)
142{
143	return sysfs_emit(buf, "%lu\n", smi_buf.size);
144}
145
146static ssize_t smi_data_buf_size_store(struct device *dev,
147				       struct device_attribute *attr,
148				       const char *buf, size_t count)
149{
150	unsigned long buf_size;
151	ssize_t ret;
152
153	buf_size = simple_strtoul(buf, NULL, 10);
154
155	/* make sure SMI data buffer is at least buf_size */
156	mutex_lock(&smi_data_lock);
157	ret = smi_data_buf_realloc(buf_size);
158	mutex_unlock(&smi_data_lock);
159	if (ret)
160		return ret;
161
162	return count;
163}
164
165static ssize_t smi_data_read(struct file *filp, struct kobject *kobj,
166			     const struct bin_attribute *bin_attr,
167			     char *buf, loff_t pos, size_t count)
168{
169	ssize_t ret;
170
171	mutex_lock(&smi_data_lock);
172	ret = memory_read_from_buffer(buf, count, &pos, smi_buf.virt,
173					smi_buf.size);
174	mutex_unlock(&smi_data_lock);
175	return ret;
176}
177
178static ssize_t smi_data_write(struct file *filp, struct kobject *kobj,
179			      const struct bin_attribute *bin_attr,
180			      char *buf, loff_t pos, size_t count)
181{
182	ssize_t ret;
183
184	if ((pos + count) > max_smi_data_buf_size)
185		return -EINVAL;
186
187	mutex_lock(&smi_data_lock);
188
189	ret = smi_data_buf_realloc(pos + count);
190	if (ret)
191		goto out;
192
193	memcpy(smi_buf.virt + pos, buf, count);
194	ret = count;
195out:
196	mutex_unlock(&smi_data_lock);
197	return ret;
198}
199
200static ssize_t host_control_action_show(struct device *dev,
201					struct device_attribute *attr,
202					char *buf)
203{
204	return sysfs_emit(buf, "%u\n", host_control_action);
205}
206
207static ssize_t host_control_action_store(struct device *dev,
208					 struct device_attribute *attr,
209					 const char *buf, size_t count)
210{
211	ssize_t ret;
212
213	/* make sure buffer is available for host control command */
214	mutex_lock(&smi_data_lock);
215	ret = smi_data_buf_realloc(sizeof(struct apm_cmd));
216	mutex_unlock(&smi_data_lock);
217	if (ret)
218		return ret;
219
220	host_control_action = simple_strtoul(buf, NULL, 10);
221	return count;
222}
223
224static ssize_t host_control_smi_type_show(struct device *dev,
225					  struct device_attribute *attr,
226					  char *buf)
227{
228	return sysfs_emit(buf, "%u\n", host_control_smi_type);
229}
230
231static ssize_t host_control_smi_type_store(struct device *dev,
232					   struct device_attribute *attr,
233					   const char *buf, size_t count)
234{
235	host_control_smi_type = simple_strtoul(buf, NULL, 10);
236	return count;
237}
238
239static ssize_t host_control_on_shutdown_show(struct device *dev,
240					     struct device_attribute *attr,
241					     char *buf)
242{
243	return sysfs_emit(buf, "%u\n", host_control_on_shutdown);
244}
245
246static ssize_t host_control_on_shutdown_store(struct device *dev,
247					      struct device_attribute *attr,
248					      const char *buf, size_t count)
249{
250	host_control_on_shutdown = simple_strtoul(buf, NULL, 10);
251	return count;
252}
253
254static int raise_smi(void *par)
255{
256	struct smi_cmd *smi_cmd = par;
257
258	if (smp_processor_id() != 0) {
259		dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n",
260			__func__);
261		return -EBUSY;
262	}
263
264	/* generate SMI */
265	/* inb to force posted write through and make SMI happen now */
266	asm volatile (
267		"outb %b0,%w1\n"
268		"inb %w1"
269		: /* no output args */
270		: "a" (smi_cmd->command_code),
271		  "d" (smi_cmd->command_address),
272		  "b" (smi_cmd->ebx),
273		  "c" (smi_cmd->ecx)
274		: "memory"
275	);
276
277	return 0;
278}
279/**
280 * dcdbas_smi_request: generate SMI request
281 *
282 * Called with smi_data_lock.
283 */
284int dcdbas_smi_request(struct smi_cmd *smi_cmd)
285{
286	int ret;
287
288	if (smi_cmd->magic != SMI_CMD_MAGIC) {
289		dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n",
290			 __func__);
291		return -EBADR;
292	}
293
294	/* SMI requires CPU 0 */
295	cpus_read_lock();
296	ret = smp_call_on_cpu(0, raise_smi, smi_cmd, true);
297	cpus_read_unlock();
298
299	return ret;
300}
301EXPORT_SYMBOL(dcdbas_smi_request);
302
303/**
304 * smi_request_store:
305 *
306 * The valid values are:
307 * 0: zero SMI data buffer
308 * 1: generate calling interface SMI
309 * 2: generate raw SMI
310 *
311 * User application writes smi_cmd to smi_data before telling driver
312 * to generate SMI.
313 */
314static ssize_t smi_request_store(struct device *dev,
315				 struct device_attribute *attr,
316				 const char *buf, size_t count)
317{
318	struct smi_cmd *smi_cmd;
319	unsigned long val = simple_strtoul(buf, NULL, 10);
320	ssize_t ret;
321
322	mutex_lock(&smi_data_lock);
323
324	if (smi_buf.size < sizeof(struct smi_cmd)) {
325		ret = -ENODEV;
326		goto out;
327	}
328	smi_cmd = (struct smi_cmd *)smi_buf.virt;
329
330	switch (val) {
331	case 2:
332		/* Raw SMI */
333		ret = dcdbas_smi_request(smi_cmd);
334		if (!ret)
335			ret = count;
336		break;
337	case 1:
338		/*
339		 * Calling Interface SMI
340		 *
341		 * Provide physical address of command buffer field within
342		 * the struct smi_cmd to BIOS.
343		 *
344		 * Because the address that smi_cmd (smi_buf.virt) points to
345		 * will be from memremap() of a non-memory address if WSMT
346		 * is present, we can't use virt_to_phys() on smi_cmd, so
347		 * we have to use the physical address that was saved when
348		 * the virtual address for smi_cmd was received.
349		 */
350		smi_cmd->ebx = (u32)smi_buf.dma +
351				offsetof(struct smi_cmd, command_buffer);
352		ret = dcdbas_smi_request(smi_cmd);
353		if (!ret)
354			ret = count;
355		break;
356	case 0:
357		memset(smi_buf.virt, 0, smi_buf.size);
358		ret = count;
359		break;
360	default:
361		ret = -EINVAL;
362		break;
363	}
364
365out:
366	mutex_unlock(&smi_data_lock);
367	return ret;
368}
369
370/**
371 * host_control_smi: generate host control SMI
372 *
373 * Caller must set up the host control command in smi_buf.virt.
374 */
375static int host_control_smi(void)
376{
377	struct apm_cmd *apm_cmd;
378	u8 *data;
379	unsigned long flags;
380	u32 num_ticks;
381	s8 cmd_status;
382	u8 index;
383
384	apm_cmd = (struct apm_cmd *)smi_buf.virt;
385	apm_cmd->status = ESM_STATUS_CMD_UNSUCCESSFUL;
386
387	switch (host_control_smi_type) {
388	case HC_SMITYPE_TYPE1:
389		spin_lock_irqsave(&rtc_lock, flags);
390		/* write SMI data buffer physical address */
391		data = (u8 *)&smi_buf.dma;
392		for (index = PE1300_CMOS_CMD_STRUCT_PTR;
393		     index < (PE1300_CMOS_CMD_STRUCT_PTR + 4);
394		     index++, data++) {
395			outb(index,
396			     (CMOS_BASE_PORT + CMOS_PAGE2_INDEX_PORT_PIIX4));
397			outb(*data,
398			     (CMOS_BASE_PORT + CMOS_PAGE2_DATA_PORT_PIIX4));
399		}
400
401		/* first set status to -1 as called by spec */
402		cmd_status = ESM_STATUS_CMD_UNSUCCESSFUL;
403		outb((u8) cmd_status, PCAT_APM_STATUS_PORT);
404
405		/* generate SMM call */
406		outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT);
407		spin_unlock_irqrestore(&rtc_lock, flags);
408
409		/* wait a few to see if it executed */
410		num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING;
411		while ((s8)inb(PCAT_APM_STATUS_PORT) == ESM_STATUS_CMD_UNSUCCESSFUL) {
412			num_ticks--;
413			if (num_ticks == EXPIRED_TIMER)
414				return -ETIME;
415		}
416		break;
417
418	case HC_SMITYPE_TYPE2:
419	case HC_SMITYPE_TYPE3:
420		spin_lock_irqsave(&rtc_lock, flags);
421		/* write SMI data buffer physical address */
422		data = (u8 *)&smi_buf.dma;
423		for (index = PE1400_CMOS_CMD_STRUCT_PTR;
424		     index < (PE1400_CMOS_CMD_STRUCT_PTR + 4);
425		     index++, data++) {
426			outb(index, (CMOS_BASE_PORT + CMOS_PAGE1_INDEX_PORT));
427			outb(*data, (CMOS_BASE_PORT + CMOS_PAGE1_DATA_PORT));
428		}
429
430		/* generate SMM call */
431		if (host_control_smi_type == HC_SMITYPE_TYPE3)
432			outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT);
433		else
434			outb(ESM_APM_CMD, PE1400_APM_CONTROL_PORT);
435
436		/* restore RTC index pointer since it was written to above */
437		CMOS_READ(RTC_REG_C);
438		spin_unlock_irqrestore(&rtc_lock, flags);
439
440		/* read control port back to serialize write */
441		cmd_status = inb(PE1400_APM_CONTROL_PORT);
442
443		/* wait a few to see if it executed */
444		num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING;
445		while (apm_cmd->status == ESM_STATUS_CMD_UNSUCCESSFUL) {
446			num_ticks--;
447			if (num_ticks == EXPIRED_TIMER)
448				return -ETIME;
449		}
450		break;
451
452	default:
453		dev_dbg(&dcdbas_pdev->dev, "%s: invalid SMI type %u\n",
454			__func__, host_control_smi_type);
455		return -ENOSYS;
456	}
457
458	return 0;
459}
460
461/**
462 * dcdbas_host_control: initiate host control
463 *
464 * This function is called by the driver after the system has
465 * finished shutting down if the user application specified a
466 * host control action to perform on shutdown.  It is safe to
467 * use smi_buf.virt at this point because the system has finished
468 * shutting down and no userspace apps are running.
469 */
470static void dcdbas_host_control(void)
471{
472	struct apm_cmd *apm_cmd;
473	u8 action;
474
475	if (host_control_action == HC_ACTION_NONE)
476		return;
477
478	action = host_control_action;
479	host_control_action = HC_ACTION_NONE;
480
481	if (!smi_buf.virt) {
482		dev_dbg(&dcdbas_pdev->dev, "%s: no SMI buffer\n", __func__);
483		return;
484	}
485
486	if (smi_buf.size < sizeof(struct apm_cmd)) {
487		dev_dbg(&dcdbas_pdev->dev, "%s: SMI buffer too small\n",
488			__func__);
489		return;
490	}
491
492	apm_cmd = (struct apm_cmd *)smi_buf.virt;
493
494	/* power off takes precedence */
495	if (action & HC_ACTION_HOST_CONTROL_POWEROFF) {
496		apm_cmd->command = ESM_APM_POWER_CYCLE;
497		apm_cmd->reserved = 0;
498		*((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 0;
499		host_control_smi();
500	} else if (action & HC_ACTION_HOST_CONTROL_POWERCYCLE) {
501		apm_cmd->command = ESM_APM_POWER_CYCLE;
502		apm_cmd->reserved = 0;
503		*((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 20;
504		host_control_smi();
505	}
506}
507
508/* WSMT */
509
510static u8 checksum(u8 *buffer, u8 length)
511{
512	u8 sum = 0;
513	u8 *end = buffer + length;
514
515	while (buffer < end)
516		sum += *buffer++;
517	return sum;
518}
519
520static inline struct smm_eps_table *check_eps_table(u8 *addr)
521{
522	struct smm_eps_table *eps = (struct smm_eps_table *)addr;
523
524	if (strncmp(eps->smm_comm_buff_anchor, SMM_EPS_SIG, 4) != 0)
525		return NULL;
526
527	if (checksum(addr, eps->length) != 0)
528		return NULL;
529
530	return eps;
531}
532
533static int dcdbas_check_wsmt(void)
534{
535	const struct dmi_device *dev = NULL;
536	struct acpi_table_wsmt *wsmt = NULL;
537	struct smm_eps_table *eps = NULL;
538	u64 bios_buf_paddr;
539	u64 remap_size;
540	u8 *addr;
541
542	acpi_get_table(ACPI_SIG_WSMT, 0, (struct acpi_table_header **)&wsmt);
543	if (!wsmt)
544		return 0;
545
546	/* Check if WSMT ACPI table shows that protection is enabled */
547	if (!(wsmt->protection_flags & ACPI_WSMT_FIXED_COMM_BUFFERS) ||
548	    !(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION))
549		return 0;
550
551	/*
552	 * BIOS could provide the address/size of the protected buffer
553	 * in an SMBIOS string or in an EPS structure in 0xFxxxx.
554	 */
555
556	/* Check SMBIOS for buffer address */
557	while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev)))
558		if (sscanf(dev->name, "30[%16llx;%8llx]", &bios_buf_paddr,
559		    &remap_size) == 2)
560			goto remap;
561
562	/* Scan for EPS (entry point structure) */
563	for (addr = (u8 *)__va(0xf0000);
564	     addr < (u8 *)__va(0x100000 - sizeof(struct smm_eps_table));
565	     addr += 16) {
566		eps = check_eps_table(addr);
567		if (eps)
568			break;
569	}
570
571	if (!eps) {
572		dev_dbg(&dcdbas_pdev->dev, "found WSMT, but no firmware buffer found\n");
573		return -ENODEV;
574	}
575	bios_buf_paddr = eps->smm_comm_buff_addr;
576	remap_size = eps->num_of_4k_pages * PAGE_SIZE;
577
578remap:
579	/*
580	 * Get physical address of buffer and map to virtual address.
581	 * Table gives size in 4K pages, regardless of actual system page size.
582	 */
583	if (upper_32_bits(bios_buf_paddr + 8)) {
584		dev_warn(&dcdbas_pdev->dev, "found WSMT, but buffer address is above 4GB\n");
585		return -EINVAL;
586	}
587	/*
588	 * Limit remap size to MAX_SMI_DATA_BUF_SIZE + 8 (since the first 8
589	 * bytes are used for a semaphore, not the data buffer itself).
590	 */
591	if (remap_size > MAX_SMI_DATA_BUF_SIZE + 8)
592		remap_size = MAX_SMI_DATA_BUF_SIZE + 8;
593
594	bios_buffer = memremap(bios_buf_paddr, remap_size, MEMREMAP_WB);
595	if (!bios_buffer) {
596		dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map buffer\n");
597		return -ENOMEM;
598	}
599
600	/* First 8 bytes is for a semaphore, not part of the smi_buf.virt */
601	smi_buf.dma = bios_buf_paddr + 8;
602	smi_buf.virt = bios_buffer + 8;
603	smi_buf.size = remap_size - 8;
604	max_smi_data_buf_size = smi_buf.size;
605	wsmt_enabled = true;
606	dev_info(&dcdbas_pdev->dev,
607		 "WSMT found, using firmware-provided SMI buffer.\n");
608	return 1;
609}
610
611/**
612 * dcdbas_reboot_notify: handle reboot notification for host control
613 */
614static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code,
615				void *unused)
616{
617	switch (code) {
618	case SYS_DOWN:
619	case SYS_HALT:
620	case SYS_POWER_OFF:
621		if (host_control_on_shutdown) {
622			/* firmware is going to perform host control action */
623			printk(KERN_WARNING "Please wait for shutdown "
624			       "action to complete...\n");
625			dcdbas_host_control();
626		}
627		break;
628	}
629
630	return NOTIFY_DONE;
631}
632
633static struct notifier_block dcdbas_reboot_nb = {
634	.notifier_call = dcdbas_reboot_notify,
635	.next = NULL,
636	.priority = INT_MIN
637};
638
639static const BIN_ATTR_ADMIN_RW(smi_data, 0);
640
641static const struct bin_attribute *const dcdbas_bin_attrs[] = {
642	&bin_attr_smi_data,
643	NULL
644};
645
646static DCDBAS_DEV_ATTR_RW(smi_data_buf_size);
647static DCDBAS_DEV_ATTR_RO(smi_data_buf_phys_addr);
648static DCDBAS_DEV_ATTR_WO(smi_request);
649static DCDBAS_DEV_ATTR_RW(host_control_action);
650static DCDBAS_DEV_ATTR_RW(host_control_smi_type);
651static DCDBAS_DEV_ATTR_RW(host_control_on_shutdown);
652
653static struct attribute *dcdbas_dev_attrs[] = {
654	&dev_attr_smi_data_buf_size.attr,
655	&dev_attr_smi_data_buf_phys_addr.attr,
656	&dev_attr_smi_request.attr,
657	&dev_attr_host_control_action.attr,
658	&dev_attr_host_control_smi_type.attr,
659	&dev_attr_host_control_on_shutdown.attr,
660	NULL
661};
662
663static const struct attribute_group dcdbas_attr_group = {
664	.attrs = dcdbas_dev_attrs,
665	.bin_attrs = dcdbas_bin_attrs,
666};
667
668static int dcdbas_probe(struct platform_device *dev)
669{
670	int error;
671
672	host_control_action = HC_ACTION_NONE;
673	host_control_smi_type = HC_SMITYPE_NONE;
674
675	dcdbas_pdev = dev;
676
677	/* Check if ACPI WSMT table specifies protected SMI buffer address */
678	error = dcdbas_check_wsmt();
679	if (error < 0)
680		return error;
681
682	/*
683	 * BIOS SMI calls require buffer addresses be in 32-bit address space.
684	 * This is done by setting the DMA mask below.
685	 */
686	error = dma_set_coherent_mask(&dcdbas_pdev->dev, DMA_BIT_MASK(32));
687	if (error)
688		return error;
689
690	error = sysfs_create_group(&dev->dev.kobj, &dcdbas_attr_group);
691	if (error)
692		return error;
693
694	register_reboot_notifier(&dcdbas_reboot_nb);
695
696	dev_info(&dev->dev, "%s (version %s)\n",
697		 DRIVER_DESCRIPTION, DRIVER_VERSION);
698
699	return 0;
700}
701
702static void dcdbas_remove(struct platform_device *dev)
703{
704	unregister_reboot_notifier(&dcdbas_reboot_nb);
705	sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group);
706}
707
708static struct platform_driver dcdbas_driver = {
709	.driver		= {
710		.name	= DRIVER_NAME,
711	},
712	.probe		= dcdbas_probe,
713	.remove		= dcdbas_remove,
714};
715
716static const struct platform_device_info dcdbas_dev_info __initconst = {
717	.name		= DRIVER_NAME,
718	.id		= PLATFORM_DEVID_NONE,
719	.dma_mask	= DMA_BIT_MASK(32),
720};
721
722static struct platform_device *dcdbas_pdev_reg;
723
724/**
725 * dcdbas_init: initialize driver
726 */
727static int __init dcdbas_init(void)
728{
729	int error;
730
731	error = platform_driver_register(&dcdbas_driver);
732	if (error)
733		return error;
734
735	dcdbas_pdev_reg = platform_device_register_full(&dcdbas_dev_info);
736	if (IS_ERR(dcdbas_pdev_reg)) {
737		error = PTR_ERR(dcdbas_pdev_reg);
738		goto err_unregister_driver;
739	}
740
741	return 0;
742
743 err_unregister_driver:
744	platform_driver_unregister(&dcdbas_driver);
745	return error;
746}
747
748/**
749 * dcdbas_exit: perform driver cleanup
750 */
751static void __exit dcdbas_exit(void)
752{
753	/*
754	 * make sure functions that use dcdbas_pdev are called
755	 * before platform_device_unregister
756	 */
757	unregister_reboot_notifier(&dcdbas_reboot_nb);
758
759	/*
760	 * We have to free the buffer here instead of dcdbas_remove
761	 * because only in module exit function we can be sure that
762	 * all sysfs attributes belonging to this module have been
763	 * released.
764	 */
765	if (dcdbas_pdev)
766		smi_data_buf_free();
767	if (bios_buffer)
768		memunmap(bios_buffer);
769	platform_device_unregister(dcdbas_pdev_reg);
770	platform_driver_unregister(&dcdbas_driver);
771}
772
773subsys_initcall_sync(dcdbas_init);
774module_exit(dcdbas_exit);
775
776MODULE_DESCRIPTION(DRIVER_DESCRIPTION " (version " DRIVER_VERSION ")");
777MODULE_VERSION(DRIVER_VERSION);
778MODULE_AUTHOR("Dell Inc.");
779MODULE_LICENSE("GPL");
780/* Any System or BIOS claiming to be by Dell */
781MODULE_ALIAS("dmi:*:[bs]vnD[Ee][Ll][Ll]*:*");
~/ Read-only file preview.