Module Name:    src
Committed By:   jruoho
Date:           Fri Mar  4 04:48:41 UTC 2011

Modified Files:
        src/sys/arch/amd64/conf: GENERIC
        src/sys/arch/i386/conf: ALL GENERIC
        src/sys/arch/x86/conf: files.x86
        src/sys/arch/x86/include: cpuvar.h
        src/sys/arch/x86/x86: identcpu.c
Added Files:
        src/sys/arch/x86/x86: odcm.c
Removed Files:
        src/sys/arch/x86/x86: iclockmod.c

Log Message:
Move INTEL_ONDEMAND_CLOCKMOD -- or odcm(4) -- to the cpufeaturebus.


To generate a diff of this commit:
cvs rdiff -u -r1.315 -r1.316 src/sys/arch/amd64/conf/GENERIC
cvs rdiff -u -r1.299 -r1.300 src/sys/arch/i386/conf/ALL
cvs rdiff -u -r1.1024 -r1.1025 src/sys/arch/i386/conf/GENERIC
cvs rdiff -u -r1.63 -r1.64 src/sys/arch/x86/conf/files.x86
cvs rdiff -u -r1.42 -r1.43 src/sys/arch/x86/include/cpuvar.h
cvs rdiff -u -r1.13 -r0 src/sys/arch/x86/x86/iclockmod.c
cvs rdiff -u -r1.27 -r1.28 src/sys/arch/x86/x86/identcpu.c
cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/x86/odcm.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/amd64/conf/GENERIC
diff -u src/sys/arch/amd64/conf/GENERIC:1.315 src/sys/arch/amd64/conf/GENERIC:1.316
--- src/sys/arch/amd64/conf/GENERIC:1.315	Sun Feb 27 17:10:33 2011
+++ src/sys/arch/amd64/conf/GENERIC	Fri Mar  4 04:48:40 2011
@@ -1,4 +1,4 @@
-# $NetBSD: GENERIC,v 1.315 2011/02/27 17:10:33 jruoho Exp $
+# $NetBSD: GENERIC,v 1.316 2011/03/04 04:48:40 jruoho Exp $
 #
 # GENERIC machine description file
 #
@@ -22,7 +22,7 @@
 
 options 	INCLUDE_CONFIG_FILE	# embed config file in kernel binary
 
-#ident 		"GENERIC-$Revision: 1.315 $"
+#ident 		"GENERIC-$Revision: 1.316 $"
 
 maxusers	64		# estimated number of users
 
@@ -82,11 +82,9 @@
 #acpicpu*	at cpu?		# ACPI CPU (including frequency scaling)
 coretemp*	at cpu?		# Intel on-die thermal sensor
 est0		at cpu0		# Intel Enhanced SpeedStep (non-ACPI)
+#odcm0		at cpu0		# On-demand clock modulation
 powernow0	at cpu0		# AMD PowerNow! and Cool'n'Quiet (non-ACPI)
 
-# Intel(R) On Demand Clock Modulation (aka ODCM)
-# options       INTEL_ONDEMAND_CLOCKMOD
-
 # Alternate buffer queue strategies for better responsiveness under high
 # disk I/O load.
 #options 	BUFQ_READPRIO

Index: src/sys/arch/i386/conf/ALL
diff -u src/sys/arch/i386/conf/ALL:1.299 src/sys/arch/i386/conf/ALL:1.300
--- src/sys/arch/i386/conf/ALL:1.299	Sun Feb 27 17:10:33 2011
+++ src/sys/arch/i386/conf/ALL	Fri Mar  4 04:48:40 2011
@@ -1,4 +1,4 @@
-# $NetBSD: ALL,v 1.299 2011/02/27 17:10:33 jruoho Exp $
+# $NetBSD: ALL,v 1.300 2011/03/04 04:48:40 jruoho Exp $
 # From NetBSD: GENERIC,v 1.787 2006/10/01 18:37:54 bouyer Exp
 #
 # ALL machine description file
@@ -17,7 +17,7 @@
 
 options 	INCLUDE_CONFIG_FILE	# embed config file in kernel binary
 
-#ident 		"ALL-$Revision: 1.299 $"
+#ident 		"ALL-$Revision: 1.300 $"
 
 maxusers	64		# estimated number of users
 
@@ -33,13 +33,11 @@
 acpicpu*	at cpu?		# ACPI CPU (including frequency scaling)
 coretemp*	at cpu?		# Intel on-die thermal sensor
 est0		at cpu0		# Intel Enhanced SpeedStep (non-ACPI)
+odcm0		at cpu0		# On-demand clock modulation
 padlock0	at cpu0		# VIA PadLock
 powernow0	at cpu0		# AMD PowerNow! and Cool'n'Quiet (non-ACPI)
 viac7temp*	at cpu?		# VIA C7 temperature sensor
 
-# Intel(R) On Demand Clock Modulation (aka ODCM)
-options 	INTEL_ONDEMAND_CLOCKMOD
-
 # XBOX support
 options 	XBOX
 

Index: src/sys/arch/i386/conf/GENERIC
diff -u src/sys/arch/i386/conf/GENERIC:1.1024 src/sys/arch/i386/conf/GENERIC:1.1025
--- src/sys/arch/i386/conf/GENERIC:1.1024	Sun Feb 27 17:10:34 2011
+++ src/sys/arch/i386/conf/GENERIC	Fri Mar  4 04:48:40 2011
@@ -1,4 +1,4 @@
-# $NetBSD: GENERIC,v 1.1024 2011/02/27 17:10:34 jruoho Exp $
+# $NetBSD: GENERIC,v 1.1025 2011/03/04 04:48:40 jruoho Exp $
 #
 # GENERIC machine description file
 #
@@ -22,7 +22,7 @@
 
 options 	INCLUDE_CONFIG_FILE	# embed config file in kernel binary
 
-#ident 		"GENERIC-$Revision: 1.1024 $"
+#ident 		"GENERIC-$Revision: 1.1025 $"
 
 maxusers	64		# estimated number of users
 
@@ -41,13 +41,11 @@
 #acpicpu*	at cpu?		# ACPI CPU (including frequency scaling)
 coretemp*	at cpu?		# Intel on-die thermal sensor
 est0		at cpu0		# Intel Enhanced SpeedStep (non-ACPI)
+#odcm0		at cpu0		# On-demand clock modulation
 #padlock0	at cpu0		# VIA PadLock
 powernow0	at cpu0		# AMD PowerNow! and Cool'n'Quiet (non-ACPI)
 viac7temp*	at cpu?		# VIA C7 temperature sensor
 
-# Intel(R) On Demand Clock Modulation (aka ODCM)
-#options 	INTEL_ONDEMAND_CLOCKMOD
-
 options 	MTRR		# memory-type range register syscall support
 # doesn't work with MP just yet..
 #options 	PERFCTRS	# performance-monitoring counters support

Index: src/sys/arch/x86/conf/files.x86
diff -u src/sys/arch/x86/conf/files.x86:1.63 src/sys/arch/x86/conf/files.x86:1.64
--- src/sys/arch/x86/conf/files.x86:1.63	Sun Feb 27 17:10:33 2011
+++ src/sys/arch/x86/conf/files.x86	Fri Mar  4 04:48:40 2011
@@ -1,4 +1,4 @@
-#	$NetBSD: files.x86,v 1.63 2011/02/27 17:10:33 jruoho Exp $
+#	$NetBSD: files.x86,v 1.64 2011/03/04 04:48:40 jruoho Exp $
 
 # options for MP configuration through the MP spec
 defflag opt_mpbios.h MPBIOS MPVERBOSE MPDEBUG MPBIOS_SCANPCI
@@ -13,9 +13,6 @@
 defflag opt_pcifixup.h	PCI_ADDR_FIXUP PCI_BUS_FIXUP
 			PCI_INTR_FIXUP PCI_INTR_FIXUP_FORCE
 
-# Pentium 4+ Thermal Monitor ODCM (aka On Demand Clock Modulation)
-defflag opt_intel_odcm.h 	INTEL_ONDEMAND_CLOCKMOD
-
 # To be able to test for NetBSD/xen in shared files
 defflag	opt_xen.h		DO_NOT_DEFINE
 
@@ -48,6 +45,10 @@
 file	arch/x86/x86/est.c		est
 file	arch/x86/x86/intel_busclock.c	est
 
+device	odcm
+attach	odcm at cpufeaturebus
+file	arch/x86/x86/odcm.c		odcm
+
 device	padlock: opencrypto
 attach	padlock at cpufeaturebus
 file	arch/x86/x86/via_padlock.c	padlock

Index: src/sys/arch/x86/include/cpuvar.h
diff -u src/sys/arch/x86/include/cpuvar.h:1.42 src/sys/arch/x86/include/cpuvar.h:1.43
--- src/sys/arch/x86/include/cpuvar.h:1.42	Thu Feb 24 15:42:17 2011
+++ src/sys/arch/x86/include/cpuvar.h	Fri Mar  4 04:48:40 2011
@@ -1,4 +1,4 @@
-/* 	$NetBSD: cpuvar.h,v 1.42 2011/02/24 15:42:17 jruoho Exp $ */
+/* 	$NetBSD: cpuvar.h,v 1.43 2011/03/04 04:48:40 jruoho Exp $ */
 
 /*-
  * Copyright (c) 2000, 2007 The NetBSD Foundation, Inc.
@@ -94,9 +94,6 @@
 
 #if defined(_KERNEL_OPT)
 #include "opt_multiprocessor.h"
-#ifndef XEN
-#include "opt_intel_odcm.h"
-#endif
 #endif /* defined(_KERNEL_OPT) */
 
 #ifdef MULTIPROCESSOR
@@ -122,14 +119,6 @@
 void x86_cpu_idle_xen(void);
 #endif
 
-#ifdef VIA_C7TEMP
-void viac7temp_register(struct cpu_info *);
-#endif
-
-#ifdef INTEL_ONDEMAND_CLOCKMOD
-void clockmod_init(void);
-#endif
-
 void	cpu_get_tsc_freq(struct cpu_info *);
 void	pat_init(struct cpu_info *);
 

Index: src/sys/arch/x86/x86/identcpu.c
diff -u src/sys/arch/x86/x86/identcpu.c:1.27 src/sys/arch/x86/x86/identcpu.c:1.28
--- src/sys/arch/x86/x86/identcpu.c:1.27	Thu Feb 24 13:58:39 2011
+++ src/sys/arch/x86/x86/identcpu.c	Fri Mar  4 04:48:39 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: identcpu.c,v 1.27 2011/02/24 13:58:39 jruoho Exp $	*/
+/*	$NetBSD: identcpu.c,v 1.28 2011/03/04 04:48:39 jruoho Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -30,9 +30,8 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: identcpu.c,v 1.27 2011/02/24 13:58:39 jruoho Exp $");
+__KERNEL_RCSID(0, "$NetBSD: identcpu.c,v 1.28 2011/03/04 04:48:39 jruoho Exp $");
 
-#include "opt_intel_odcm.h"
 #include "opt_xen.h"
 
 #include <sys/param.h>
@@ -794,10 +793,4 @@
 	} else
 		i386_use_fxsave = 0;
 #endif	/* i386 */
-
-#ifdef INTEL_ONDEMAND_CLOCKMOD
-	if (cpuid_level >= 1) {
-		clockmod_init();
-	}
-#endif
 }

Added files:

Index: src/sys/arch/x86/x86/odcm.c
diff -u /dev/null src/sys/arch/x86/x86/odcm.c:1.1
--- /dev/null	Fri Mar  4 04:48:41 2011
+++ src/sys/arch/x86/x86/odcm.c	Fri Mar  4 04:48:39 2011
@@ -0,0 +1,411 @@
+/*	$NetBSD: odcm.c,v 1.1 2011/03/04 04:48:39 jruoho Exp $ */
+/*      $OpenBSD: p4tcc.c,v 1.13 2006/12/20 17:50:40 gwk Exp $ */
+
+/*
+ * Copyright (c) 2007 Juan Romero Pardines
+ * Copyright (c) 2003 Ted Unangst
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * On-Demand Clock Modulation driver, to modulate the clock duty cycle
+ * by software. Available on Pentium M and later models (feature TM).
+ *
+ * References:
+ * Intel Developer's manual v.3 #245472-012
+ *
+ * On some models, the cpu can hang if it's running at a slow speed.
+ * Workarounds included below.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: odcm.c,v 1.1 2011/03/04 04:48:39 jruoho Exp $");
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/kmem.h>
+#include <sys/sysctl.h>
+#include <sys/xcall.h>
+
+#include <x86/cpuvar.h>
+#include <x86/cpu_msr.h>
+#include <x86/specialreg.h>
+
+#define ODCM_ENABLE		(1 << 4) /* Enable bit 4 */
+#define ODCM_REGOFFSET		1
+#define ODCM_MAXSTATES		8
+
+static struct {
+	int level;
+	int reg;
+	int errata;
+} state[] = {
+	{ .level = 7, .reg = 0, .errata = 0 },
+	{ .level = 6, .reg = 7, .errata = 0 },
+	{ .level = 5, .reg = 6, .errata = 0 },
+	{ .level = 4, .reg = 5, .errata = 0 },
+	{ .level = 3, .reg = 4, .errata = 0 },
+	{ .level = 2, .reg = 3, .errata = 0 },
+	{ .level = 1, .reg = 2, .errata = 0 },
+	{ .level = 0, .reg = 1, .errata = 0 }
+};
+
+struct odcm_softc {
+	device_t		 sc_dev;
+	struct sysctllog	*sc_log;
+	char			*sc_names;
+	size_t			 sc_names_len;
+	int			 sc_level;
+	int			 sc_target;
+	int			 sc_current;
+};
+
+static int	odcm_match(device_t, cfdata_t, void *);
+static void	odcm_attach(device_t, device_t, void *);
+static int	odcm_detach(device_t, int);
+static void	odcm_quirks(void);
+static bool	odcm_init(device_t);
+static bool	odcm_sysctl(device_t);
+static int	odcm_state_get(void);
+static void	odcm_state_set(int);
+static int	odcm_sysctl_helper(SYSCTLFN_ARGS);
+
+CFATTACH_DECL_NEW(odcm, sizeof(struct odcm_softc),
+    odcm_match, odcm_attach, odcm_detach, NULL);
+
+static int
+odcm_match(device_t parent, cfdata_t cf, void *aux)
+{
+	const uint32_t odcm = CPUID_ACPI | CPUID_TM;
+	struct cpufeature_attach_args *cfaa = aux;
+	uint32_t regs[4];
+
+	if (strcmp(cfaa->name, "frequency") != 0)
+		return 0;
+
+	if (cpuid_level < 1)
+		return 0;
+	else {
+		x86_cpuid(1, regs);
+
+		return ((regs[3] & odcm) != odcm) ? 0 : 1;
+	}
+}
+
+static void
+odcm_attach(device_t parent, device_t self, void *aux)
+{
+	struct odcm_softc *sc = device_private(self);
+
+	sc->sc_dev = self;
+	sc->sc_log = NULL;
+	sc->sc_names = NULL;
+
+	odcm_quirks();
+
+	if (odcm_init(self) != true) {
+		aprint_normal(": failed to initialize\n");
+		return;
+	}
+
+	aprint_naive("\n");
+	aprint_normal(": on-demand clock modulation (state %s)\n",
+	    sc->sc_level == (ODCM_MAXSTATES - 1) ? "disabled" : "enabled");
+
+	(void)pmf_device_register(self, NULL, NULL);
+}
+
+static int
+odcm_detach(device_t self, int flags)
+{
+	struct odcm_softc *sc = device_private(self);
+
+	/*
+	 * Make sure the CPU operates with
+	 * 100 % duty cycle after we are done.
+	 */
+	odcm_state_set(7);
+
+	if (sc->sc_log != NULL)
+		sysctl_teardown(&sc->sc_log);
+
+	if (sc->sc_names != NULL)
+		kmem_free(sc->sc_names, sc->sc_names_len);
+
+	pmf_device_deregister(self);
+
+	return 0;
+}
+
+static void
+odcm_quirks(void)
+{
+	uint32_t regs[4];
+
+	x86_cpuid(1, regs);
+
+	switch (CPUID2STEPPING(regs[0])) {
+
+	case 0x22:	/* errata O50 P44 and Z21 */
+	case 0x24:
+	case 0x25:
+	case 0x27:
+	case 0x29:
+		/* hang with 12.5 */
+		state[__arraycount(state) - 1].errata = 1;
+		break;
+
+	case 0x07:	/* errata N44 and P18 */
+	case 0x0a:
+	case 0x12:
+	case 0x13:
+		/* hang at 12.5 and 25 */
+		state[__arraycount(state) - 1].errata = 1;
+		state[__arraycount(state) - 2].errata = 1;
+		break;
+	}
+}
+
+static bool
+odcm_init(device_t self)
+{
+	struct odcm_softc *sc = device_private(self);
+	size_t i, len;
+
+	sc->sc_names_len = state[0].level  * (sizeof("9999 ") - 1) + 1;
+	sc->sc_names = kmem_zalloc(sc->sc_names_len, KM_SLEEP);
+
+	if (sc->sc_names == NULL)
+		return false;
+
+	for (i = len = 0; i < __arraycount(state); i++) {
+
+		if (state[i].errata)
+			continue;
+
+		len += snprintf(sc->sc_names + len,
+		    sc->sc_names_len - len, "%d%s", state[i].level,
+		    i < __arraycount(state) ? " " : "");
+	}
+
+	/*
+	 * Get the current value and create
+	 * sysctl machdep.clockmod subtree.
+	 */
+	sc->sc_level = odcm_state_get();
+
+	return odcm_sysctl(self);
+}
+
+static bool
+odcm_sysctl(device_t self)
+{
+	struct odcm_softc *sc = device_private(self);
+	const struct sysctlnode *node, *odcmnode;
+	int rv;
+
+	rv = sysctl_createv(&sc->sc_log, 0, NULL, &node,
+	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
+	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
+
+	if (rv != 0)
+		goto fail;
+
+	rv = sysctl_createv(&sc->sc_log, 0, &node, &odcmnode,
+	    0, CTLTYPE_NODE, "clockmod", NULL,
+	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
+
+	if (rv != 0)
+		goto fail;
+
+	rv = sysctl_createv(&sc->sc_log, 0, &odcmnode, &node,
+	    CTLFLAG_READWRITE, CTLTYPE_INT, "target",
+	    SYSCTL_DESCR("target duty cycle (0 = lowest, 7 highest)"),
+	    odcm_sysctl_helper, 0, sc, 0, CTL_CREATE, CTL_EOL);
+
+	if (rv != 0)
+		goto fail;
+
+	sc->sc_target = node->sysctl_num;
+
+	rv = sysctl_createv(&sc->sc_log, 0, &odcmnode, &node,
+	    0, CTLTYPE_INT, "current",
+	    SYSCTL_DESCR("current duty cycle"),
+	    odcm_sysctl_helper, 0, sc, 0, CTL_CREATE, CTL_EOL);
+
+	if (rv != 0)
+		goto fail;
+
+	sc->sc_current = node->sysctl_num;
+
+	rv = sysctl_createv(&sc->sc_log, 0, &odcmnode, &node,
+	    0, CTLTYPE_STRING, "available",
+	    SYSCTL_DESCR("list of duty cycles available"),
+	    NULL, 0, sc->sc_names, sc->sc_names_len,
+	    CTL_CREATE, CTL_EOL);
+
+	if (rv != 0)
+		goto fail;
+
+	return true;
+
+fail:
+	sysctl_teardown(&sc->sc_log);
+	sc->sc_log = NULL;
+
+	return false;
+}
+
+static int
+odcm_sysctl_helper(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node;
+	struct odcm_softc *sc;
+	int level, old, err;
+	size_t i;
+
+	node = *rnode;
+	sc = node.sysctl_data;
+
+	level = old = 0;
+	node.sysctl_data = &level;
+
+	if (rnode->sysctl_num == sc->sc_target)
+		level = old = sc->sc_level;
+	else if (rnode->sysctl_num == sc->sc_current)
+		level = odcm_state_get();
+	else
+		return EOPNOTSUPP;
+
+	err = sysctl_lookup(SYSCTLFN_CALL(&node));
+
+	if (err || newp == NULL)
+		return err;
+
+	/*
+	 * Check for an invalid level.
+	 */
+	for (i = 0; i < __arraycount(state); i++) {
+
+		if (level == state[i].level && !state[i].errata)
+			break;
+	}
+
+	if (i == __arraycount(state))
+		return EINVAL;
+
+	if (rnode->sysctl_num == sc->sc_target && level != old) {
+		odcm_state_set(level);
+		sc->sc_level = level;
+	}
+
+	return 0;
+}
+
+static int
+odcm_state_get(void)
+{
+	uint64_t msr;
+	size_t i;
+	int val;
+
+	msr = rdmsr(MSR_THERM_CONTROL);
+
+	if ((msr & ODCM_ENABLE) == 0)
+		return (ODCM_MAXSTATES - 1);
+
+	msr = (msr >> ODCM_REGOFFSET) & (ODCM_MAXSTATES - 1);
+
+	for (val = -1, i = 0; i < __arraycount(state); i++) {
+
+		KASSERT(msr < INT_MAX);
+
+		if ((int)msr == state[i].reg) {
+			val = state[i].level;
+			break;
+		}
+	}
+
+	KASSERT(val != -1);
+
+	return val;
+}
+
+static void
+odcm_state_set(int level)
+{
+	struct msr_rw_info msr;
+	uint64_t xc;
+	size_t i;
+
+	for (i = 0; i < __arraycount(state); i++) {
+
+		if (level == state[i].level && !state[i].errata)
+			break;
+	}
+
+	KASSERT(i != __arraycount(state));
+
+	msr.msr_read = true;
+	msr.msr_type = MSR_THERM_CONTROL;
+	msr.msr_mask = 0x1e;
+
+	if (state[i].reg != 0)	/* bit 0 reserved */
+		msr.msr_value = (state[i].reg << ODCM_REGOFFSET) | ODCM_ENABLE;
+	else
+		msr.msr_value = 0; /* max state */
+
+	xc = xc_broadcast(0, (xcfunc_t)x86_msr_xcall, &msr, NULL);
+	xc_wait(xc);
+}
+
+MODULE(MODULE_CLASS_DRIVER, odcm, NULL);
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+odcm_modcmd(modcmd_t cmd, void *aux)
+{
+	int error = 0;
+
+	switch (cmd) {
+	case MODULE_CMD_INIT:
+#ifdef _MODULE
+		error = config_init_component(cfdriver_ioconf_odcm,
+		    cfattach_ioconf_odcm, cfdata_ioconf_odcm);
+#endif
+		return error;
+	case MODULE_CMD_FINI:
+#ifdef _MODULE
+		error = config_fini_component(cfdriver_ioconf_odcm,
+		    cfattach_ioconf_odcm, cfdata_ioconf_odcm);
+#endif
+		return error;
+	default:
+		return ENOTTY;
+	}
+}

Reply via email to