^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * elanfreq: cpufreq driver for the AMD ELAN family
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * (c) Copyright 2002 Robert Schwebel <r.schwebel@pengutronix.de>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Parts of this code are (c) Sven Geggus <sven@geggus.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * All Rights Reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * 2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/cpufreq.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <asm/cpu_device_id.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <asm/msr.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/timex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define REG_CSCIR 0x22 /* Chip Setup and Control Index Register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define REG_CSCDR 0x23 /* Chip Setup and Control Data Register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) /* Module parameter */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) static int max_freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) struct s_elan_multiplier {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) int clock; /* frequency in kHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) int val40h; /* PMU Force Mode register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) int val80h; /* CPU Clock Speed Register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) * It is important that the frequencies
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) * are listed in ascending order here!
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) static struct s_elan_multiplier elan_multiplier[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) {1000, 0x02, 0x18},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) {2000, 0x02, 0x10},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) {4000, 0x02, 0x08},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) {8000, 0x00, 0x00},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) {16000, 0x00, 0x02},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) {33000, 0x00, 0x04},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) {66000, 0x01, 0x04},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) {99000, 0x01, 0x05}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) static struct cpufreq_frequency_table elanfreq_table[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) {0, 0, 1000},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) {0, 1, 2000},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) {0, 2, 4000},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) {0, 3, 8000},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) {0, 4, 16000},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) {0, 5, 33000},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) {0, 6, 66000},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) {0, 7, 99000},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) {0, 0, CPUFREQ_TABLE_END},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) * elanfreq_get_cpu_frequency: determine current cpu speed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) * Finds out at which frequency the CPU of the Elan SOC runs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) * at the moment. Frequencies from 1 to 33 MHz are generated
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) * the normal way, 66 and 99 MHz are called "Hyperspeed Mode"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) * and have the rest of the chip running with 33 MHz.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) static unsigned int elanfreq_get_cpu_frequency(unsigned int cpu)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) u8 clockspeed_reg; /* Clock Speed Register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) local_irq_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) outb_p(0x80, REG_CSCIR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) clockspeed_reg = inb_p(REG_CSCDR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) local_irq_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) if ((clockspeed_reg & 0xE0) == 0xE0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) /* Are we in CPU clock multiplied mode (66/99 MHz)? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) if ((clockspeed_reg & 0xE0) == 0xC0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) if ((clockspeed_reg & 0x01) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) return 66000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) return 99000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) /* 33 MHz is not 32 MHz... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) if ((clockspeed_reg & 0xE0) == 0xA0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) return 33000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) return (1<<((clockspeed_reg & 0xE0) >> 5)) * 1000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) static int elanfreq_target(struct cpufreq_policy *policy,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) unsigned int state)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) * Access to the Elan's internal registers is indexed via
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) * 0x22: Chip Setup & Control Register Index Register (CSCI)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) * 0x23: Chip Setup & Control Register Data Register (CSCD)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) * 0x40 is the Power Management Unit's Force Mode Register.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) * Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) local_irq_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) outb_p(0x40, REG_CSCIR); /* Disable hyperspeed mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) outb_p(0x00, REG_CSCDR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) local_irq_enable(); /* wait till internal pipelines and */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) udelay(1000); /* buffers have cleaned up */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) local_irq_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) /* now, set the CPU clock speed register (0x80) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) outb_p(0x80, REG_CSCIR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) outb_p(elan_multiplier[state].val80h, REG_CSCDR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) /* now, the hyperspeed bit in PMU Force Mode Register (0x40) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) outb_p(0x40, REG_CSCIR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) outb_p(elan_multiplier[state].val40h, REG_CSCDR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) udelay(10000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) local_irq_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) * Module init and exit code
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) static int elanfreq_cpu_init(struct cpufreq_policy *policy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) struct cpuinfo_x86 *c = &cpu_data(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) struct cpufreq_frequency_table *pos;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) /* capability check */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) if ((c->x86_vendor != X86_VENDOR_AMD) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) (c->x86 != 4) || (c->x86_model != 10))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) /* max freq */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) if (!max_freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) max_freq = elanfreq_get_cpu_frequency(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) /* table init */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) cpufreq_for_each_entry(pos, elanfreq_table)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) if (pos->frequency > max_freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) pos->frequency = CPUFREQ_ENTRY_INVALID;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) policy->freq_table = elanfreq_table;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) #ifndef MODULE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) * elanfreq_setup - elanfreq command line parameter parsing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) * elanfreq command line parameter. Use:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) * elanfreq=66000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) * to set the maximum CPU frequency to 66 MHz. Note that in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) * case you do not give this boot parameter, the maximum
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) * frequency will fall back to _current_ CPU frequency which
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) * might be lower. If you build this as a module, use the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) * max_freq module parameter instead.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) static int __init elanfreq_setup(char *str)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) max_freq = simple_strtoul(str, &str, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) pr_warn("You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) __setup("elanfreq=", elanfreq_setup);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) static struct cpufreq_driver elanfreq_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) .get = elanfreq_get_cpu_frequency,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) .verify = cpufreq_generic_frequency_table_verify,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) .target_index = elanfreq_target,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) .init = elanfreq_cpu_init,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) .name = "elanfreq",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) .attr = cpufreq_generic_attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) static const struct x86_cpu_id elan_id[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) X86_MATCH_VENDOR_FAM_MODEL(AMD, 4, 10, NULL),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) {}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) MODULE_DEVICE_TABLE(x86cpu, elan_id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) static int __init elanfreq_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) if (!x86_match_cpu(elan_id))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) return cpufreq_register_driver(&elanfreq_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) static void __exit elanfreq_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) cpufreq_unregister_driver(&elanfreq_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) module_param(max_freq, int, 0444);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) "Sven Geggus <sven@geggus.net>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) module_init(elanfreq_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) module_exit(elanfreq_exit);