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;
+ }
+}