driver core merge for 3.4-rc1
Here's the big driver core merge for 3.4-rc1. Lots of various things here, sysfs fixes/tweaks (with the nlink breakage reverted), dynamic debugging updates, w1 drivers, hyperv driver updates, and a variety of other bits and pieces, full information in the shortlog. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.18 (GNU/Linux) iEYEABECAAYFAk9neCsACgkQMUfUDdst+ylyQwCfY2eizvzw5HhjQs8gOiBRDADe yrgAnj1Zan2QkoCnQIFJNAoxqNX9yAhd =biH6 -----END PGP SIGNATURE----- Merge tag 'driver-core-3.3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core Pull driver core patches for 3.4-rc1 from Greg KH: "Here's the big driver core merge for 3.4-rc1. Lots of various things here, sysfs fixes/tweaks (with the nlink breakage reverted), dynamic debugging updates, w1 drivers, hyperv driver updates, and a variety of other bits and pieces, full information in the shortlog." * tag 'driver-core-3.3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (78 commits) Tools: hv: Support enumeration from all the pools Tools: hv: Fully support the new KVP verbs in the user level daemon Drivers: hv: Support the newly introduced KVP messages in the driver Drivers: hv: Add new message types to enhance KVP regulator: Support driver probe deferral Revert "sysfs: Kill nlink counting." uevent: send events in correct order according to seqnum (v3) driver core: minor comment formatting cleanups driver core: move the deferred probe pointer into the private area drivercore: Add driver probe deferral mechanism DS2781 Maxim Stand-Alone Fuel Gauge battery and w1 slave drivers w1_bq27000: Only one thread can access the bq27000 at a time. w1_bq27000 - remove w1_bq27000_write w1_bq27000: remove unnecessary NULL test. sysfs: Fix memory leak in sysfs_sd_setsecdata(). intel_idle: Revert change of auto_demotion_disable_flags for Nehalem w1: Fix w1_bq27000 driver-core: documentation: fix up Greg's email address powernow-k6: Really enable auto-loading powernow-k7: Fix CPU family number ...
This commit is contained in:
commit
4a52246302
|
@ -1,6 +1,6 @@
|
|||
What: devfs
|
||||
Date: July 2005 (scheduled), finally removed in kernel v2.6.18
|
||||
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Description:
|
||||
devfs has been unmaintained for a number of years, has unfixable
|
||||
races, contains a naming policy within the kernel that is
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
What: /sys/bus/usb/drivers/usbtmc/devices/*/interface_capabilities
|
||||
What: /sys/bus/usb/drivers/usbtmc/devices/*/device_capabilities
|
||||
Date: August 2008
|
||||
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Description:
|
||||
These files show the various USB TMC capabilities as described
|
||||
by the device itself. The full description of the bitfields
|
||||
|
@ -15,7 +15,7 @@ Description:
|
|||
What: /sys/bus/usb/drivers/usbtmc/devices/*/usb488_interface_capabilities
|
||||
What: /sys/bus/usb/drivers/usbtmc/devices/*/usb488_device_capabilities
|
||||
Date: August 2008
|
||||
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Description:
|
||||
These files show the various USB TMC capabilities as described
|
||||
by the device itself. The full description of the bitfields
|
||||
|
@ -29,7 +29,7 @@ Description:
|
|||
|
||||
What: /sys/bus/usb/drivers/usbtmc/devices/*/TermChar
|
||||
Date: August 2008
|
||||
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Description:
|
||||
This file is the TermChar value to be sent to the USB TMC
|
||||
device as described by the document, "Universal Serial Bus Test
|
||||
|
@ -42,7 +42,7 @@ Description:
|
|||
|
||||
What: /sys/bus/usb/drivers/usbtmc/devices/*/TermCharEnabled
|
||||
Date: August 2008
|
||||
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Description:
|
||||
This file determines if the TermChar is to be sent to the
|
||||
device on every transaction or not. For more details about
|
||||
|
@ -53,7 +53,7 @@ Description:
|
|||
|
||||
What: /sys/bus/usb/drivers/usbtmc/devices/*/auto_abort
|
||||
Date: August 2008
|
||||
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Description:
|
||||
This file determines if the the transaction of the USB TMC
|
||||
device is to be automatically aborted if there is any error.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
What: /sys/class/
|
||||
Date: Febuary 2006
|
||||
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Description:
|
||||
The /sys/class directory will consist of a group of
|
||||
subdirectories describing individual classes of devices
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
What: /sys/devices
|
||||
Date: February 2006
|
||||
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Description:
|
||||
The /sys/devices tree contains a snapshot of the
|
||||
internal state of the kernel device tree. Devices will
|
||||
|
|
58
Documentation/ABI/testing/sysfs-devices-soc
Normal file
58
Documentation/ABI/testing/sysfs-devices-soc
Normal file
|
@ -0,0 +1,58 @@
|
|||
What: /sys/devices/socX
|
||||
Date: January 2012
|
||||
contact: Lee Jones <lee.jones@linaro.org>
|
||||
Description:
|
||||
The /sys/devices/ directory contains a sub-directory for each
|
||||
System-on-Chip (SoC) device on a running platform. Information
|
||||
regarding each SoC can be obtained by reading sysfs files. This
|
||||
functionality is only available if implemented by the platform.
|
||||
|
||||
The directory created for each SoC will also house information
|
||||
about devices which are commonly contained in /sys/devices/platform.
|
||||
It has been agreed that if an SoC device exists, its supported
|
||||
devices would be better suited to appear as children of that SoC.
|
||||
|
||||
What: /sys/devices/socX/machine
|
||||
Date: January 2012
|
||||
contact: Lee Jones <lee.jones@linaro.org>
|
||||
Description:
|
||||
Read-only attribute common to all SoCs. Contains the SoC machine
|
||||
name (e.g. Ux500).
|
||||
|
||||
What: /sys/devices/socX/family
|
||||
Date: January 2012
|
||||
contact: Lee Jones <lee.jones@linaro.org>
|
||||
Description:
|
||||
Read-only attribute common to all SoCs. Contains SoC family name
|
||||
(e.g. DB8500).
|
||||
|
||||
What: /sys/devices/socX/soc_id
|
||||
Date: January 2012
|
||||
contact: Lee Jones <lee.jones@linaro.org>
|
||||
Description:
|
||||
Read-only attribute supported by most SoCs. In the case of
|
||||
ST-Ericsson's chips this contains the SoC serial number.
|
||||
|
||||
What: /sys/devices/socX/revision
|
||||
Date: January 2012
|
||||
contact: Lee Jones <lee.jones@linaro.org>
|
||||
Description:
|
||||
Read-only attribute supported by most SoCs. Contains the SoC's
|
||||
manufacturing revision number.
|
||||
|
||||
What: /sys/devices/socX/process
|
||||
Date: January 2012
|
||||
contact: Lee Jones <lee.jones@linaro.org>
|
||||
Description:
|
||||
Read-only attribute supported ST-Ericsson's silicon. Contains the
|
||||
the process by which the silicon chip was manufactured.
|
||||
|
||||
What: /sys/bus/soc
|
||||
Date: January 2012
|
||||
contact: Lee Jones <lee.jones@linaro.org>
|
||||
Description:
|
||||
The /sys/bus/soc/ directory contains the usual sub-folders
|
||||
expected under most buses. /sys/bus/soc/devices is of particular
|
||||
interest, as it contains a symlink for each SoC device found on
|
||||
the system. Each symlink points back into the aforementioned
|
||||
/sys/devices/socX devices.
|
|
@ -1,7 +1,7 @@
|
|||
What: /sys/devices/platform/samsung/performance_level
|
||||
Date: January 1, 2010
|
||||
KernelVersion: 2.6.33
|
||||
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Description: Some Samsung laptops have different "performance levels"
|
||||
that are can be modified by a function key, and by this
|
||||
sysfs file. These values don't always make a whole lot
|
||||
|
|
|
@ -12,7 +12,7 @@ dynamically enabled per-callsite.
|
|||
Dynamic debug has even more useful features:
|
||||
|
||||
* Simple query language allows turning on and off debugging statements by
|
||||
matching any combination of:
|
||||
matching any combination of 0 or 1 of:
|
||||
|
||||
- source filename
|
||||
- function name
|
||||
|
@ -79,31 +79,24 @@ Command Language Reference
|
|||
==========================
|
||||
|
||||
At the lexical level, a command comprises a sequence of words separated
|
||||
by whitespace characters. Note that newlines are treated as word
|
||||
separators and do *not* end a command or allow multiple commands to
|
||||
be done together. So these are all equivalent:
|
||||
by spaces or tabs. So these are all equivalent:
|
||||
|
||||
nullarbor:~ # echo -c 'file svcsock.c line 1603 +p' >
|
||||
<debugfs>/dynamic_debug/control
|
||||
nullarbor:~ # echo -c ' file svcsock.c line 1603 +p ' >
|
||||
<debugfs>/dynamic_debug/control
|
||||
nullarbor:~ # echo -c 'file svcsock.c\nline 1603 +p' >
|
||||
<debugfs>/dynamic_debug/control
|
||||
nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' >
|
||||
<debugfs>/dynamic_debug/control
|
||||
|
||||
Commands are bounded by a write() system call. If you want to do
|
||||
multiple commands you need to do a separate "echo" for each, like:
|
||||
Command submissions are bounded by a write() system call.
|
||||
Multiple commands can be written together, separated by ';' or '\n'.
|
||||
|
||||
nullarbor:~ # echo 'file svcsock.c line 1603 +p' > /proc/dprintk ;\
|
||||
> echo 'file svcsock.c line 1563 +p' > /proc/dprintk
|
||||
~# echo "func pnpacpi_get_resources +p; func pnp_assign_mem +p" \
|
||||
> <debugfs>/dynamic_debug/control
|
||||
|
||||
or even like:
|
||||
If your query set is big, you can batch them too:
|
||||
|
||||
nullarbor:~ # (
|
||||
> echo 'file svcsock.c line 1603 +p' ;\
|
||||
> echo 'file svcsock.c line 1563 +p' ;\
|
||||
> ) > /proc/dprintk
|
||||
~# cat query-batch-file > <debugfs>/dynamic_debug/control
|
||||
|
||||
At the syntactical level, a command comprises a sequence of match
|
||||
specifications, followed by a flags change specification.
|
||||
|
@ -144,11 +137,12 @@ func
|
|||
func svc_tcp_accept
|
||||
|
||||
file
|
||||
The given string is compared against either the full
|
||||
pathname or the basename of the source file of each
|
||||
callsite. Examples:
|
||||
The given string is compared against either the full pathname, the
|
||||
src-root relative pathname, or the basename of the source file of
|
||||
each callsite. Examples:
|
||||
|
||||
file svcsock.c
|
||||
file kernel/freezer.c
|
||||
file /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c
|
||||
|
||||
module
|
||||
|
|
|
@ -14,7 +14,10 @@ Debugfs is typically mounted with a command like:
|
|||
|
||||
mount -t debugfs none /sys/kernel/debug
|
||||
|
||||
(Or an equivalent /etc/fstab line).
|
||||
(Or an equivalent /etc/fstab line).
|
||||
The debugfs root directory is accessible by anyone by default. To
|
||||
restrict access to the tree the "uid", "gid" and "mode" mount
|
||||
options can be used.
|
||||
|
||||
Note that the debugfs API is exported GPL-only to modules.
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ Code Seq#(hex) Include File Comments
|
|||
'Y' all linux/cyclades.h
|
||||
'Z' 14-15 drivers/message/fusion/mptctl.h
|
||||
'[' 00-07 linux/usb/tmc.h USB Test and Measurement Devices
|
||||
<mailto:gregkh@suse.de>
|
||||
<mailto:gregkh@linuxfoundation.org>
|
||||
'a' all linux/atm*.h, linux/sonet.h ATM on linux
|
||||
<http://lrcwww.epfl.ch/>
|
||||
'b' 00-FF conflict! bit3 vme host bridge
|
||||
|
|
|
@ -354,7 +354,7 @@ Andrew Morton에 의해 배포된 실험적인 커널 패치들이다. Andrew는
|
|||
git.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git
|
||||
|
||||
quilt trees:
|
||||
- USB, PCI, Driver Core, and I2C, Greg Kroah-Hartman < gregkh@suse.de>
|
||||
- USB, PCI, Driver Core, and I2C, Greg Kroah-Hartman < gregkh@linuxfoundation.org>
|
||||
kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/
|
||||
- x86-64, partly i386, Andi Kleen < ak@suse.de>
|
||||
ftp.firstfloor.org:/pub/ak/x86_64/quilt/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Everything you never wanted to know about kobjects, ksets, and ktypes
|
||||
|
||||
Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
|
||||
Based on an original article by Jon Corbet for lwn.net written October 1,
|
||||
2003 and located at http://lwn.net/Articles/51437/
|
||||
|
|
|
@ -316,7 +316,7 @@ linux-kernel邮件列表中提供反馈,告诉大家你遇到了问题还是
|
|||
git.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git
|
||||
|
||||
使用quilt管理的补丁集:
|
||||
- USB, PCI, 驱动程序核心和I2C, Greg Kroah-Hartman <gregkh@suse.de>
|
||||
- USB, PCI, 驱动程序核心和I2C, Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/
|
||||
- x86-64, 部分i386, Andi Kleen <ak@suse.de>
|
||||
ftp.firstfloor.org:/pub/ak/x86_64/quilt/
|
||||
|
|
|
@ -180,6 +180,9 @@ config ARCH_HAS_DEFAULT_IDLE
|
|||
config ARCH_HAS_CACHE_LINE_SIZE
|
||||
def_bool y
|
||||
|
||||
config ARCH_HAS_CPU_AUTOPROBE
|
||||
def_bool y
|
||||
|
||||
config HAVE_SETUP_PER_CPU_AREA
|
||||
def_bool y
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <crypto/aes.h>
|
||||
#include <crypto/cryptd.h>
|
||||
#include <crypto/ctr.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/i387.h>
|
||||
#include <asm/aes.h>
|
||||
#include <crypto/scatterwalk.h>
|
||||
|
@ -1253,14 +1254,19 @@ static struct crypto_alg __rfc4106_alg = {
|
|||
};
|
||||
#endif
|
||||
|
||||
|
||||
static const struct x86_cpu_id aesni_cpu_id[] = {
|
||||
X86_FEATURE_MATCH(X86_FEATURE_AES),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, aesni_cpu_id);
|
||||
|
||||
static int __init aesni_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!cpu_has_aes) {
|
||||
printk(KERN_INFO "Intel AES-NI instructions are not detected.\n");
|
||||
if (!x86_match_cpu(aesni_cpu_id))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if ((err = crypto_fpu_init()))
|
||||
goto fpu_err;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <crypto/internal/hash.h>
|
||||
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#define CHKSUM_BLOCK_SIZE 1
|
||||
#define CHKSUM_DIGEST_SIZE 4
|
||||
|
@ -173,13 +174,17 @@ static struct shash_alg alg = {
|
|||
}
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id crc32c_cpu_id[] = {
|
||||
X86_FEATURE_MATCH(X86_FEATURE_XMM4_2),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, crc32c_cpu_id);
|
||||
|
||||
static int __init crc32c_intel_mod_init(void)
|
||||
{
|
||||
if (cpu_has_xmm4_2)
|
||||
return crypto_register_shash(&alg);
|
||||
else
|
||||
if (!x86_match_cpu(crc32c_cpu_id))
|
||||
return -ENODEV;
|
||||
return crypto_register_shash(&alg);
|
||||
}
|
||||
|
||||
static void __exit crc32c_intel_mod_fini(void)
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <crypto/gf128mul.h>
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <asm/i387.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#define GHASH_BLOCK_SIZE 16
|
||||
#define GHASH_DIGEST_SIZE 16
|
||||
|
@ -294,15 +295,18 @@ static struct ahash_alg ghash_async_alg = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id pcmul_cpu_id[] = {
|
||||
X86_FEATURE_MATCH(X86_FEATURE_PCLMULQDQ), /* Pickle-Mickle-Duck */
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, pcmul_cpu_id);
|
||||
|
||||
static int __init ghash_pclmulqdqni_mod_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!cpu_has_pclmulqdq) {
|
||||
printk(KERN_INFO "Intel PCLMULQDQ-NI instructions are not"
|
||||
" detected.\n");
|
||||
if (!x86_match_cpu(pcmul_cpu_id))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = crypto_register_shash(&ghash_alg);
|
||||
if (err)
|
||||
|
|
13
arch/x86/include/asm/cpu_device_id.h
Normal file
13
arch/x86/include/asm/cpu_device_id.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef _CPU_DEVICE_ID
|
||||
#define _CPU_DEVICE_ID 1
|
||||
|
||||
/*
|
||||
* Declare drivers belonging to specific x86 CPUs
|
||||
* Similar in spirit to pci_device_id and related PCI functions
|
||||
*/
|
||||
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
extern const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match);
|
||||
|
||||
#endif
|
|
@ -177,6 +177,7 @@
|
|||
#define X86_FEATURE_PLN (7*32+ 5) /* Intel Power Limit Notification */
|
||||
#define X86_FEATURE_PTS (7*32+ 6) /* Intel Package Thermal Status */
|
||||
#define X86_FEATURE_DTS (7*32+ 7) /* Digital Thermal Sensor */
|
||||
#define X86_FEATURE_HW_PSTATE (7*32+ 8) /* AMD HW-PState */
|
||||
|
||||
/* Virtualization flags: Linux defined, word 8 */
|
||||
#define X86_FEATURE_TPR_SHADOW (8*32+ 0) /* Intel TPR Shadow */
|
||||
|
|
|
@ -16,6 +16,7 @@ obj-y := intel_cacheinfo.o scattered.o topology.o
|
|||
obj-y += proc.o capflags.o powerflags.o common.o
|
||||
obj-y += vmware.o hypervisor.o sched.o mshyperv.o
|
||||
obj-y += rdrand.o
|
||||
obj-y += match.o
|
||||
|
||||
obj-$(CONFIG_X86_32) += bugs.o
|
||||
obj-$(CONFIG_X86_64) += bugs_64.o
|
||||
|
|
91
arch/x86/kernel/cpu/match.c
Normal file
91
arch/x86/kernel/cpu/match.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/processor.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/**
|
||||
* x86_match_cpu - match current CPU again an array of x86_cpu_ids
|
||||
* @match: Pointer to array of x86_cpu_ids. Last entry terminated with
|
||||
* {}.
|
||||
*
|
||||
* Return the entry if the current CPU matches the entries in the
|
||||
* passed x86_cpu_id match table. Otherwise NULL. The match table
|
||||
* contains vendor (X86_VENDOR_*), family, model and feature bits or
|
||||
* respective wildcard entries.
|
||||
*
|
||||
* A typical table entry would be to match a specific CPU
|
||||
* { X86_VENDOR_INTEL, 6, 0x12 }
|
||||
* or to match a specific CPU feature
|
||||
* { X86_FEATURE_MATCH(X86_FEATURE_FOOBAR) }
|
||||
*
|
||||
* Fields can be wildcarded with %X86_VENDOR_ANY, %X86_FAMILY_ANY,
|
||||
* %X86_MODEL_ANY, %X86_FEATURE_ANY or 0 (except for vendor)
|
||||
*
|
||||
* Arrays used to match for this should also be declared using
|
||||
* MODULE_DEVICE_TABLE(x86_cpu, ...)
|
||||
*
|
||||
* This always matches against the boot cpu, assuming models and features are
|
||||
* consistent over all CPUs.
|
||||
*/
|
||||
const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match)
|
||||
{
|
||||
const struct x86_cpu_id *m;
|
||||
struct cpuinfo_x86 *c = &boot_cpu_data;
|
||||
|
||||
for (m = match; m->vendor | m->family | m->model | m->feature; m++) {
|
||||
if (m->vendor != X86_VENDOR_ANY && c->x86_vendor != m->vendor)
|
||||
continue;
|
||||
if (m->family != X86_FAMILY_ANY && c->x86 != m->family)
|
||||
continue;
|
||||
if (m->model != X86_MODEL_ANY && c->x86_model != m->model)
|
||||
continue;
|
||||
if (m->feature != X86_FEATURE_ANY && !cpu_has(c, m->feature))
|
||||
continue;
|
||||
return m;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(x86_match_cpu);
|
||||
|
||||
ssize_t arch_print_cpu_modalias(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *bufptr)
|
||||
{
|
||||
int size = PAGE_SIZE;
|
||||
int i, n;
|
||||
char *buf = bufptr;
|
||||
|
||||
n = snprintf(buf, size, "x86cpu:vendor:%04X:family:%04X:"
|
||||
"model:%04X:feature:",
|
||||
boot_cpu_data.x86_vendor,
|
||||
boot_cpu_data.x86,
|
||||
boot_cpu_data.x86_model);
|
||||
size -= n;
|
||||
buf += n;
|
||||
size -= 1;
|
||||
for (i = 0; i < NCAPINTS*32; i++) {
|
||||
if (boot_cpu_has(i)) {
|
||||
n = snprintf(buf, size, ",%04X", i);
|
||||
if (n >= size) {
|
||||
WARN(1, "x86 features overflow page\n");
|
||||
break;
|
||||
}
|
||||
size -= n;
|
||||
buf += n;
|
||||
}
|
||||
}
|
||||
*buf++ = '\n';
|
||||
return buf - bufptr;
|
||||
}
|
||||
|
||||
int arch_cpu_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (buf) {
|
||||
arch_print_cpu_modalias(NULL, NULL, buf);
|
||||
add_uevent_var(env, "MODALIAS=%s", buf);
|
||||
kfree(buf);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -40,6 +40,7 @@ void __cpuinit init_scattered_cpuid_features(struct cpuinfo_x86 *c)
|
|||
{ X86_FEATURE_EPB, CR_ECX, 3, 0x00000006, 0 },
|
||||
{ X86_FEATURE_XSAVEOPT, CR_EAX, 0, 0x0000000d, 1 },
|
||||
{ X86_FEATURE_CPB, CR_EDX, 9, 0x80000007, 0 },
|
||||
{ X86_FEATURE_HW_PSTATE, CR_EDX, 7, 0x80000007, 0 },
|
||||
{ X86_FEATURE_NPT, CR_EDX, 0, 0x8000000a, 0 },
|
||||
{ X86_FEATURE_LBRV, CR_EDX, 1, 0x8000000a, 0 },
|
||||
{ X86_FEATURE_SVML, CR_EDX, 2, 0x8000000a, 0 },
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
|
||||
#include <asm/microcode.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
MODULE_DESCRIPTION("Microcode Update Driver");
|
||||
MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
|
||||
|
@ -504,6 +505,20 @@ static struct notifier_block __refdata mc_cpu_notifier = {
|
|||
.notifier_call = mc_cpu_callback,
|
||||
};
|
||||
|
||||
#ifdef MODULE
|
||||
/* Autoload on Intel and AMD systems */
|
||||
static const struct x86_cpu_id microcode_id[] = {
|
||||
#ifdef CONFIG_MICROCODE_INTEL
|
||||
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, },
|
||||
#endif
|
||||
#ifdef CONFIG_MICROCODE_AMD
|
||||
{ X86_VENDOR_AMD, X86_FAMILY_ANY, X86_MODEL_ANY, },
|
||||
#endif
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, microcode_id);
|
||||
#endif
|
||||
|
||||
static int __init microcode_init(void)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
|
|
|
@ -474,6 +474,7 @@ static __ref int acpi_processor_start(struct acpi_processor *pr)
|
|||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
acpi_processor_ppc_has_changed(pr, 0);
|
||||
acpi_processor_load_module(pr);
|
||||
#endif
|
||||
acpi_processor_get_throttling_info(pr);
|
||||
acpi_processor_get_limit_info(pr);
|
||||
|
|
|
@ -240,6 +240,28 @@ void acpi_processor_ppc_exit(void)
|
|||
acpi_processor_ppc_status &= ~PPC_REGISTERED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a quick check if the systems looks like it should use ACPI
|
||||
* cpufreq. We look at a _PCT method being available, but don't
|
||||
* do a whole lot of sanity checks.
|
||||
*/
|
||||
void acpi_processor_load_module(struct acpi_processor *pr)
|
||||
{
|
||||
static int requested;
|
||||
acpi_status status = 0;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
|
||||
if (!arch_has_acpi_pdc() || requested)
|
||||
return;
|
||||
status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
|
||||
if (!ACPI_FAILURE(status)) {
|
||||
printk(KERN_INFO PREFIX "Requesting acpi_cpufreq\n");
|
||||
request_module_nowait("acpi_cpufreq");
|
||||
requested = 1;
|
||||
}
|
||||
kfree(buffer.pointer);
|
||||
}
|
||||
|
||||
static int acpi_processor_get_performance_control(struct acpi_processor *pr)
|
||||
{
|
||||
int result = 0;
|
||||
|
|
|
@ -176,6 +176,9 @@ config GENERIC_CPU_DEVICES
|
|||
bool
|
||||
default n
|
||||
|
||||
config SOC_BUS
|
||||
bool
|
||||
|
||||
source "drivers/base/regmap/Kconfig"
|
||||
|
||||
config DMA_SHARED_BUFFER
|
||||
|
|
|
@ -19,6 +19,7 @@ obj-$(CONFIG_MODULES) += module.o
|
|||
endif
|
||||
obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
|
||||
obj-$(CONFIG_REGMAP) += regmap/
|
||||
obj-$(CONFIG_SOC_BUS) += soc.o
|
||||
|
||||
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
|
||||
|
||||
|
|
|
@ -59,6 +59,10 @@ struct driver_private {
|
|||
* @knode_parent - node in sibling list
|
||||
* @knode_driver - node in driver list
|
||||
* @knode_bus - node in bus list
|
||||
* @deferred_probe - entry in deferred_probe_list which is used to retry the
|
||||
* binding of drivers which were unable to get all the resources needed by
|
||||
* the device; typically because it depends on another driver getting
|
||||
* probed first.
|
||||
* @driver_data - private pointer for driver specific info. Will turn into a
|
||||
* list soon.
|
||||
* @device - pointer back to the struct class that this structure is
|
||||
|
@ -71,6 +75,7 @@ struct device_private {
|
|||
struct klist_node knode_parent;
|
||||
struct klist_node knode_driver;
|
||||
struct klist_node knode_bus;
|
||||
struct list_head deferred_probe;
|
||||
void *driver_data;
|
||||
struct device *device;
|
||||
};
|
||||
|
@ -105,6 +110,7 @@ extern void bus_remove_driver(struct device_driver *drv);
|
|||
|
||||
extern void driver_detach(struct device_driver *drv);
|
||||
extern int driver_probe_device(struct device_driver *drv, struct device *dev);
|
||||
extern void driver_deferred_probe_del(struct device *dev);
|
||||
static inline int driver_match_device(struct device_driver *drv,
|
||||
struct device *dev)
|
||||
{
|
||||
|
|
|
@ -1194,13 +1194,15 @@ EXPORT_SYMBOL_GPL(subsys_interface_register);
|
|||
|
||||
void subsys_interface_unregister(struct subsys_interface *sif)
|
||||
{
|
||||
struct bus_type *subsys = sif->subsys;
|
||||
struct bus_type *subsys;
|
||||
struct subsys_dev_iter iter;
|
||||
struct device *dev;
|
||||
|
||||
if (!sif)
|
||||
if (!sif || !sif->subsys)
|
||||
return;
|
||||
|
||||
subsys = sif->subsys;
|
||||
|
||||
mutex_lock(&subsys->p->mutex);
|
||||
list_del_init(&sif->node);
|
||||
if (sif->remove_dev) {
|
||||
|
|
|
@ -921,6 +921,7 @@ int device_private_init(struct device *dev)
|
|||
dev->p->device = dev;
|
||||
klist_init(&dev->p->klist_children, klist_children_get,
|
||||
klist_children_put);
|
||||
INIT_LIST_HEAD(&dev->p->deferred_probe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1188,6 +1189,7 @@ void device_del(struct device *dev)
|
|||
device_remove_file(dev, &uevent_attr);
|
||||
device_remove_attrs(dev);
|
||||
bus_remove_device(dev);
|
||||
driver_deferred_probe_del(dev);
|
||||
|
||||
/*
|
||||
* Some platform devices are driven without driver attached
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/node.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/percpu.h>
|
||||
|
||||
#include "base.h"
|
||||
|
@ -244,6 +245,9 @@ int __cpuinit register_cpu(struct cpu *cpu, int num)
|
|||
cpu->dev.id = num;
|
||||
cpu->dev.bus = &cpu_subsys;
|
||||
cpu->dev.release = cpu_device_release;
|
||||
#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
|
||||
cpu->dev.bus->uevent = arch_cpu_uevent;
|
||||
#endif
|
||||
error = device_register(&cpu->dev);
|
||||
if (!error && cpu->hotpluggable)
|
||||
register_cpu_control(cpu);
|
||||
|
@ -268,6 +272,10 @@ struct device *get_cpu_device(unsigned cpu)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(get_cpu_device);
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
|
||||
static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL);
|
||||
#endif
|
||||
|
||||
static struct attribute *cpu_root_attrs[] = {
|
||||
#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
|
||||
&dev_attr_probe.attr,
|
||||
|
@ -278,6 +286,9 @@ static struct attribute *cpu_root_attrs[] = {
|
|||
&cpu_attrs[2].attr.attr,
|
||||
&dev_attr_kernel_max.attr,
|
||||
&dev_attr_offline.attr,
|
||||
#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
|
||||
&dev_attr_modalias.attr,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
@ -28,6 +28,141 @@
|
|||
#include "base.h"
|
||||
#include "power/power.h"
|
||||
|
||||
/*
|
||||
* Deferred Probe infrastructure.
|
||||
*
|
||||
* Sometimes driver probe order matters, but the kernel doesn't always have
|
||||
* dependency information which means some drivers will get probed before a
|
||||
* resource it depends on is available. For example, an SDHCI driver may
|
||||
* first need a GPIO line from an i2c GPIO controller before it can be
|
||||
* initialized. If a required resource is not available yet, a driver can
|
||||
* request probing to be deferred by returning -EPROBE_DEFER from its probe hook
|
||||
*
|
||||
* Deferred probe maintains two lists of devices, a pending list and an active
|
||||
* list. A driver returning -EPROBE_DEFER causes the device to be added to the
|
||||
* pending list. A successful driver probe will trigger moving all devices
|
||||
* from the pending to the active list so that the workqueue will eventually
|
||||
* retry them.
|
||||
*
|
||||
* The deferred_probe_mutex must be held any time the deferred_probe_*_list
|
||||
* of the (struct device*)->p->deferred_probe pointers are manipulated
|
||||
*/
|
||||
static DEFINE_MUTEX(deferred_probe_mutex);
|
||||
static LIST_HEAD(deferred_probe_pending_list);
|
||||
static LIST_HEAD(deferred_probe_active_list);
|
||||
static struct workqueue_struct *deferred_wq;
|
||||
|
||||
/**
|
||||
* deferred_probe_work_func() - Retry probing devices in the active list.
|
||||
*/
|
||||
static void deferred_probe_work_func(struct work_struct *work)
|
||||
{
|
||||
struct device *dev;
|
||||
struct device_private *private;
|
||||
/*
|
||||
* This block processes every device in the deferred 'active' list.
|
||||
* Each device is removed from the active list and passed to
|
||||
* bus_probe_device() to re-attempt the probe. The loop continues
|
||||
* until every device in the active list is removed and retried.
|
||||
*
|
||||
* Note: Once the device is removed from the list and the mutex is
|
||||
* released, it is possible for the device get freed by another thread
|
||||
* and cause a illegal pointer dereference. This code uses
|
||||
* get/put_device() to ensure the device structure cannot disappear
|
||||
* from under our feet.
|
||||
*/
|
||||
mutex_lock(&deferred_probe_mutex);
|
||||
while (!list_empty(&deferred_probe_active_list)) {
|
||||
private = list_first_entry(&deferred_probe_active_list,
|
||||
typeof(*dev->p), deferred_probe);
|
||||
dev = private->device;
|
||||
list_del_init(&private->deferred_probe);
|
||||
|
||||
get_device(dev);
|
||||
|
||||
/*
|
||||
* Drop the mutex while probing each device; the probe path may
|
||||
* manipulate the deferred list
|
||||
*/
|
||||
mutex_unlock(&deferred_probe_mutex);
|
||||
dev_dbg(dev, "Retrying from deferred list\n");
|
||||
bus_probe_device(dev);
|
||||
mutex_lock(&deferred_probe_mutex);
|
||||
|
||||
put_device(dev);
|
||||
}
|
||||
mutex_unlock(&deferred_probe_mutex);
|
||||
}
|
||||
static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func);
|
||||
|
||||
static void driver_deferred_probe_add(struct device *dev)
|
||||
{
|
||||
mutex_lock(&deferred_probe_mutex);
|
||||
if (list_empty(&dev->p->deferred_probe)) {
|
||||
dev_dbg(dev, "Added to deferred list\n");
|
||||
list_add(&dev->p->deferred_probe, &deferred_probe_pending_list);
|
||||
}
|
||||
mutex_unlock(&deferred_probe_mutex);
|
||||
}
|
||||
|
||||
void driver_deferred_probe_del(struct device *dev)
|
||||
{
|
||||
mutex_lock(&deferred_probe_mutex);
|
||||
if (!list_empty(&dev->p->deferred_probe)) {
|
||||
dev_dbg(dev, "Removed from deferred list\n");
|
||||
list_del_init(&dev->p->deferred_probe);
|
||||
}
|
||||
mutex_unlock(&deferred_probe_mutex);
|
||||
}
|
||||
|
||||
static bool driver_deferred_probe_enable = false;
|
||||
/**
|
||||
* driver_deferred_probe_trigger() - Kick off re-probing deferred devices
|
||||
*
|
||||
* This functions moves all devices from the pending list to the active
|
||||
* list and schedules the deferred probe workqueue to process them. It
|
||||
* should be called anytime a driver is successfully bound to a device.
|
||||
*/
|
||||
static void driver_deferred_probe_trigger(void)
|
||||
{
|
||||
if (!driver_deferred_probe_enable)
|
||||
return;
|
||||
|
||||
/*
|
||||
* A successful probe means that all the devices in the pending list
|
||||
* should be triggered to be reprobed. Move all the deferred devices
|
||||
* into the active list so they can be retried by the workqueue
|
||||
*/
|
||||
mutex_lock(&deferred_probe_mutex);
|
||||
list_splice_tail_init(&deferred_probe_pending_list,
|
||||
&deferred_probe_active_list);
|
||||
mutex_unlock(&deferred_probe_mutex);
|
||||
|
||||
/*
|
||||
* Kick the re-probe thread. It may already be scheduled, but it is
|
||||
* safe to kick it again.
|
||||
*/
|
||||
queue_work(deferred_wq, &deferred_probe_work);
|
||||
}
|
||||
|
||||
/**
|
||||
* deferred_probe_initcall() - Enable probing of deferred devices
|
||||
*
|
||||
* We don't want to get in the way when the bulk of drivers are getting probed.
|
||||
* Instead, this initcall makes sure that deferred probing is delayed until
|
||||
* late_initcall time.
|
||||
*/
|
||||
static int deferred_probe_initcall(void)
|
||||
{
|
||||
deferred_wq = create_singlethread_workqueue("deferwq");
|
||||
if (WARN_ON(!deferred_wq))
|
||||
return -ENOMEM;
|
||||
|
||||
driver_deferred_probe_enable = true;
|
||||
driver_deferred_probe_trigger();
|
||||
return 0;
|
||||
}
|
||||
late_initcall(deferred_probe_initcall);
|
||||
|
||||
static void driver_bound(struct device *dev)
|
||||
{
|
||||
|
@ -42,6 +177,13 @@ static void driver_bound(struct device *dev)
|
|||
|
||||
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
|
||||
|
||||
/*
|
||||
* Make sure the device is no longer in one of the deferred lists and
|
||||
* kick off retrying all pending devices
|
||||
*/
|
||||
driver_deferred_probe_del(dev);
|
||||
driver_deferred_probe_trigger();
|
||||
|
||||
if (dev->bus)
|
||||
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
|
||||
BUS_NOTIFY_BOUND_DRIVER, dev);
|
||||
|
@ -142,7 +284,11 @@ probe_failed:
|
|||
driver_sysfs_remove(dev);
|
||||
dev->driver = NULL;
|
||||
|
||||
if (ret != -ENODEV && ret != -ENXIO) {
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
/* Driver requested deferred probing */
|
||||
dev_info(dev, "Driver %s requests probe deferral\n", drv->name);
|
||||
driver_deferred_probe_add(dev);
|
||||
} else if (ret != -ENODEV && ret != -ENXIO) {
|
||||
/* driver matched but the probe failed */
|
||||
printk(KERN_WARNING
|
||||
"%s: probe of %s failed with error %d\n",
|
||||
|
|
|
@ -153,34 +153,6 @@ int driver_add_kobj(struct device_driver *drv, struct kobject *kobj,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(driver_add_kobj);
|
||||
|
||||
/**
|
||||
* get_driver - increment driver reference count.
|
||||
* @drv: driver.
|
||||
*/
|
||||
struct device_driver *get_driver(struct device_driver *drv)
|
||||
{
|
||||
if (drv) {
|
||||
struct driver_private *priv;
|
||||
struct kobject *kobj;
|
||||
|
||||
kobj = kobject_get(&drv->p->kobj);
|
||||
priv = to_driver(kobj);
|
||||
return priv->driver;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_driver);
|
||||
|
||||
/**
|
||||
* put_driver - decrement driver's refcount.
|
||||
* @drv: driver.
|
||||
*/
|
||||
void put_driver(struct device_driver *drv)
|
||||
{
|
||||
kobject_put(&drv->p->kobj);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(put_driver);
|
||||
|
||||
static int driver_add_groups(struct device_driver *drv,
|
||||
const struct attribute_group **groups)
|
||||
{
|
||||
|
@ -234,7 +206,6 @@ int driver_register(struct device_driver *drv)
|
|||
|
||||
other = driver_find(drv->name, drv->bus);
|
||||
if (other) {
|
||||
put_driver(other);
|
||||
printk(KERN_ERR "Error: Driver '%s' is already registered, "
|
||||
"aborting...\n", drv->name);
|
||||
return -EBUSY;
|
||||
|
@ -275,7 +246,9 @@ EXPORT_SYMBOL_GPL(driver_unregister);
|
|||
* Call kset_find_obj() to iterate over list of drivers on
|
||||
* a bus to find driver by name. Return driver if found.
|
||||
*
|
||||
* Note that kset_find_obj increments driver's reference count.
|
||||
* This routine provides no locking to prevent the driver it returns
|
||||
* from being unregistered or unloaded while the caller is using it.
|
||||
* The caller is responsible for preventing this.
|
||||
*/
|
||||
struct device_driver *driver_find(const char *name, struct bus_type *bus)
|
||||
{
|
||||
|
@ -283,6 +256,8 @@ struct device_driver *driver_find(const char *name, struct bus_type *bus)
|
|||
struct driver_private *priv;
|
||||
|
||||
if (k) {
|
||||
/* Drop reference added by kset_find_obj() */
|
||||
kobject_put(k);
|
||||
priv = to_driver(k);
|
||||
return priv->driver;
|
||||
}
|
||||
|
|
183
drivers/base/soc.c
Normal file
183
drivers/base/soc.c
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2011
|
||||
*
|
||||
* Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
static DEFINE_IDR(soc_ida);
|
||||
static DEFINE_SPINLOCK(soc_lock);
|
||||
|
||||
static ssize_t soc_info_get(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
struct soc_device {
|
||||
struct device dev;
|
||||
struct soc_device_attribute *attr;
|
||||
int soc_dev_num;
|
||||
};
|
||||
|
||||
static struct bus_type soc_bus_type = {
|
||||
.name = "soc",
|
||||
};
|
||||
|
||||
static DEVICE_ATTR(machine, S_IRUGO, soc_info_get, NULL);
|
||||
static DEVICE_ATTR(family, S_IRUGO, soc_info_get, NULL);
|
||||
static DEVICE_ATTR(soc_id, S_IRUGO, soc_info_get, NULL);
|
||||
static DEVICE_ATTR(revision, S_IRUGO, soc_info_get, NULL);
|
||||
|
||||
struct device *soc_device_to_device(struct soc_device *soc_dev)
|
||||
{
|
||||
return &soc_dev->dev;
|
||||
}
|
||||
|
||||
static mode_t soc_attribute_mode(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
|
||||
|
||||
if ((attr == &dev_attr_machine.attr)
|
||||
&& (soc_dev->attr->machine != NULL))
|
||||
return attr->mode;
|
||||
if ((attr == &dev_attr_family.attr)
|
||||
&& (soc_dev->attr->family != NULL))
|
||||
return attr->mode;
|
||||
if ((attr == &dev_attr_revision.attr)
|
||||
&& (soc_dev->attr->revision != NULL))
|
||||
return attr->mode;
|
||||
if ((attr == &dev_attr_soc_id.attr)
|
||||
&& (soc_dev->attr->soc_id != NULL))
|
||||
return attr->mode;
|
||||
|
||||
/* Unknown or unfilled attribute. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t soc_info_get(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
|
||||
|
||||
if (attr == &dev_attr_machine)
|
||||
return sprintf(buf, "%s\n", soc_dev->attr->machine);
|
||||
if (attr == &dev_attr_family)
|
||||
return sprintf(buf, "%s\n", soc_dev->attr->family);
|
||||
if (attr == &dev_attr_revision)
|
||||
return sprintf(buf, "%s\n", soc_dev->attr->revision);
|
||||
if (attr == &dev_attr_soc_id)
|
||||
return sprintf(buf, "%s\n", soc_dev->attr->soc_id);
|
||||
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
|
||||
static struct attribute *soc_attr[] = {
|
||||
&dev_attr_machine.attr,
|
||||
&dev_attr_family.attr,
|
||||
&dev_attr_soc_id.attr,
|
||||
&dev_attr_revision.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group soc_attr_group = {
|
||||
.attrs = soc_attr,
|
||||
.is_visible = soc_attribute_mode,
|
||||
};
|
||||
|
||||
static const struct attribute_group *soc_attr_groups[] = {
|
||||
&soc_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void soc_release(struct device *dev)
|
||||
{
|
||||
struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
|
||||
|
||||
kfree(soc_dev);
|
||||
}
|
||||
|
||||
struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
|
||||
{
|
||||
struct soc_device *soc_dev;
|
||||
int ret;
|
||||
|
||||
soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
|
||||
if (!soc_dev) {
|
||||
ret = -ENOMEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
/* Fetch a unique (reclaimable) SOC ID. */
|
||||
do {
|
||||
if (!ida_pre_get(&soc_ida, GFP_KERNEL)) {
|
||||
ret = -ENOMEM;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
spin_lock(&soc_lock);
|
||||
ret = ida_get_new(&soc_ida, &soc_dev->soc_dev_num);
|
||||
spin_unlock(&soc_lock);
|
||||
|
||||
} while (ret == -EAGAIN);
|
||||
|
||||
if (ret)
|
||||
goto out2;
|
||||
|
||||
soc_dev->attr = soc_dev_attr;
|
||||
soc_dev->dev.bus = &soc_bus_type;
|
||||
soc_dev->dev.groups = soc_attr_groups;
|
||||
soc_dev->dev.release = soc_release;
|
||||
|
||||
dev_set_name(&soc_dev->dev, "soc%d", soc_dev->soc_dev_num);
|
||||
|
||||
ret = device_register(&soc_dev->dev);
|
||||
if (ret)
|
||||
goto out3;
|
||||
|
||||
return soc_dev;
|
||||
|
||||
out3:
|
||||
ida_remove(&soc_ida, soc_dev->soc_dev_num);
|
||||
out2:
|
||||
kfree(soc_dev);
|
||||
out1:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */
|
||||
void soc_device_unregister(struct soc_device *soc_dev)
|
||||
{
|
||||
ida_remove(&soc_ida, soc_dev->soc_dev_num);
|
||||
|
||||
device_unregister(&soc_dev->dev);
|
||||
}
|
||||
|
||||
static int __init soc_bus_register(void)
|
||||
{
|
||||
spin_lock_init(&soc_lock);
|
||||
|
||||
return bus_register(&soc_bus_type);
|
||||
}
|
||||
core_initcall(soc_bus_register);
|
||||
|
||||
static void __exit soc_bus_unregister(void)
|
||||
{
|
||||
ida_destroy(&soc_ida);
|
||||
|
||||
bus_unregister(&soc_bus_type);
|
||||
}
|
||||
module_exit(soc_bus_unregister);
|
|
@ -385,6 +385,14 @@ static struct cpufreq_driver nforce2_driver = {
|
|||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
#ifdef MODULE
|
||||
static DEFINE_PCI_DEVICE_TABLE(nforce2_ids) = {
|
||||
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, nforce2_ids);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic
|
||||
*
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/tsc.h>
|
||||
|
||||
|
@ -437,18 +438,19 @@ static struct cpufreq_driver eps_driver = {
|
|||
.attr = eps_attr,
|
||||
};
|
||||
|
||||
|
||||
/* This driver will work only on Centaur C7 processors with
|
||||
* Enhanced SpeedStep/PowerSaver registers */
|
||||
static const struct x86_cpu_id eps_cpu_id[] = {
|
||||
{ X86_VENDOR_CENTAUR, 6, X86_MODEL_ANY, X86_FEATURE_EST },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, eps_cpu_id);
|
||||
|
||||
static int __init eps_init(void)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
|
||||
/* This driver will work only on Centaur C7 processors with
|
||||
* Enhanced SpeedStep/PowerSaver registers */
|
||||
if (c->x86_vendor != X86_VENDOR_CENTAUR
|
||||
|| c->x86 != 6 || c->x86_model < 10)
|
||||
if (!x86_match_cpu(eps_cpu_id) || boot_cpu_data.x86_model < 10)
|
||||
return -ENODEV;
|
||||
if (!cpu_has(c, X86_FEATURE_EST))
|
||||
return -ENODEV;
|
||||
|
||||
if (cpufreq_register_driver(&eps_driver))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/cpufreq.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/msr.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/io.h>
|
||||
|
@ -277,17 +278,16 @@ static struct cpufreq_driver elanfreq_driver = {
|
|||
.attr = elanfreq_attr,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id elan_id[] = {
|
||||
{ X86_VENDOR_AMD, 4, 10, },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, elan_id);
|
||||
|
||||
static int __init elanfreq_init(void)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
|
||||
/* Test if we have the right hardware */
|
||||
if ((c->x86_vendor != X86_VENDOR_AMD) ||
|
||||
(c->x86 != 4) || (c->x86_model != 10)) {
|
||||
printk(KERN_INFO "elanfreq: error: no Elan processor found!\n");
|
||||
if (!x86_match_cpu(elan_id))
|
||||
return -ENODEV;
|
||||
}
|
||||
return cpufreq_register_driver(&elanfreq_driver);
|
||||
}
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/processor-cyrix.h>
|
||||
|
||||
/* PCI config registers, all at F0 */
|
||||
|
@ -171,6 +172,7 @@ static struct pci_device_id gx_chipset_tbl[] __initdata = {
|
|||
{ PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5510), },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, gx_chipset_tbl);
|
||||
|
||||
static void gx_write_byte(int reg, int value)
|
||||
{
|
||||
|
@ -185,13 +187,6 @@ static __init struct pci_dev *gx_detect_chipset(void)
|
|||
{
|
||||
struct pci_dev *gx_pci = NULL;
|
||||
|
||||
/* check if CPU is a MediaGX or a Geode. */
|
||||
if ((boot_cpu_data.x86_vendor != X86_VENDOR_NSC) &&
|
||||
(boot_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) {
|
||||
pr_debug("error: no MediaGX/Geode processor found!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* detect which companion chip is used */
|
||||
for_each_pci_dev(gx_pci) {
|
||||
if ((pci_match_id(gx_chipset_tbl, gx_pci)) != NULL)
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/acpi.h>
|
||||
|
||||
#include <asm/msr.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <acpi/processor.h>
|
||||
|
||||
#include "longhaul.h"
|
||||
|
@ -951,12 +952,17 @@ static struct cpufreq_driver longhaul_driver = {
|
|||
.attr = longhaul_attr,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id longhaul_id[] = {
|
||||
{ X86_VENDOR_CENTAUR, 6 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, longhaul_id);
|
||||
|
||||
static int __init longhaul_init(void)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
|
||||
if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6)
|
||||
if (!x86_match_cpu(longhaul_id))
|
||||
return -ENODEV;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <asm/msr.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
static struct cpufreq_driver longrun_driver;
|
||||
|
||||
|
@ -288,6 +289,12 @@ static struct cpufreq_driver longrun_driver = {
|
|||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id longrun_ids[] = {
|
||||
{ X86_VENDOR_TRANSMETA, X86_FAMILY_ANY, X86_MODEL_ANY,
|
||||
X86_FEATURE_LONGRUN },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, longrun_ids);
|
||||
|
||||
/**
|
||||
* longrun_init - initializes the Transmeta Crusoe LongRun CPUFreq driver
|
||||
|
@ -296,12 +303,8 @@ static struct cpufreq_driver longrun_driver = {
|
|||
*/
|
||||
static int __init longrun_init(void)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
|
||||
if (c->x86_vendor != X86_VENDOR_TRANSMETA ||
|
||||
!cpu_has(c, X86_FEATURE_LONGRUN))
|
||||
if (!x86_match_cpu(longrun_ids))
|
||||
return -ENODEV;
|
||||
|
||||
return cpufreq_register_driver(&longrun_driver);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <asm/processor.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/timer.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#include "speedstep-lib.h"
|
||||
|
||||
|
@ -289,21 +290,25 @@ static struct cpufreq_driver p4clockmod_driver = {
|
|||
.attr = p4clockmod_attr,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id cpufreq_p4_id[] = {
|
||||
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ACC },
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
* Intentionally no MODULE_DEVICE_TABLE here: this driver should not
|
||||
* be auto loaded. Please don't add one.
|
||||
*/
|
||||
|
||||
static int __init cpufreq_p4_init(void)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* THERM_CONTROL is architectural for IA32 now, so
|
||||
* we can rely on the capability checks
|
||||
*/
|
||||
if (c->x86_vendor != X86_VENDOR_INTEL)
|
||||
return -ENODEV;
|
||||
|
||||
if (!test_cpu_cap(c, X86_FEATURE_ACPI) ||
|
||||
!test_cpu_cap(c, X86_FEATURE_ACC))
|
||||
if (!x86_match_cpu(cpufreq_p4_id) || !boot_cpu_has(X86_FEATURE_ACPI))
|
||||
return -ENODEV;
|
||||
|
||||
ret = cpufreq_register_driver(&p4clockmod_driver);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/timex.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/msr.h>
|
||||
|
||||
#define POWERNOW_IOPORT 0xfff0 /* it doesn't matter where, as long
|
||||
|
@ -210,6 +211,12 @@ static struct cpufreq_driver powernow_k6_driver = {
|
|||
.attr = powernow_k6_attr,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id powernow_k6_ids[] = {
|
||||
{ X86_VENDOR_AMD, 5, 12 },
|
||||
{ X86_VENDOR_AMD, 5, 13 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, powernow_k6_ids);
|
||||
|
||||
/**
|
||||
* powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver
|
||||
|
@ -220,10 +227,7 @@ static struct cpufreq_driver powernow_k6_driver = {
|
|||
*/
|
||||
static int __init powernow_k6_init(void)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
|
||||
if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 5) ||
|
||||
((c->x86_model != 12) && (c->x86_model != 13)))
|
||||
if (!x86_match_cpu(powernow_k6_ids))
|
||||
return -ENODEV;
|
||||
|
||||
if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) {
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <asm/timer.h> /* Needed for recalibrate_cpu_khz() */
|
||||
#include <asm/msr.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
|
||||
#include <linux/acpi.h>
|
||||
|
@ -110,18 +111,19 @@ static int check_fsb(unsigned int fsbspeed)
|
|||
return delta < 5;
|
||||
}
|
||||
|
||||
static const struct x86_cpu_id powernow_k7_cpuids[] = {
|
||||
{ X86_VENDOR_AMD, 6, },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, powernow_k7_cpuids);
|
||||
|
||||
static int check_powernow(void)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
unsigned int maxei, eax, ebx, ecx, edx;
|
||||
|
||||
if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 6)) {
|
||||
#ifdef MODULE
|
||||
printk(KERN_INFO PFX "This module only works with "
|
||||
"AMD K7 CPUs\n");
|
||||
#endif
|
||||
if (!x86_match_cpu(powernow_k7_cpuids))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get maximum capabilities */
|
||||
maxei = cpuid_eax(0x80000000);
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/msr.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/mutex.h>
|
||||
|
@ -520,6 +521,15 @@ static int core_voltage_post_transition(struct powernow_k8_data *data,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct x86_cpu_id powernow_k8_ids[] = {
|
||||
/* IO based frequency switching */
|
||||
{ X86_VENDOR_AMD, 0xf },
|
||||
/* MSR based frequency switching supported */
|
||||
X86_FEATURE_MATCH(X86_FEATURE_HW_PSTATE),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, powernow_k8_ids);
|
||||
|
||||
static void check_supported_cpu(void *_rc)
|
||||
{
|
||||
u32 eax, ebx, ecx, edx;
|
||||
|
@ -527,13 +537,7 @@ static void check_supported_cpu(void *_rc)
|
|||
|
||||
*rc = -ENODEV;
|
||||
|
||||
if (__this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_AMD)
|
||||
return;
|
||||
|
||||
eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
|
||||
if (((eax & CPUID_XFAM) != CPUID_XFAM_K8) &&
|
||||
((eax & CPUID_XFAM) < CPUID_XFAM_10H))
|
||||
return;
|
||||
|
||||
if ((eax & CPUID_XFAM) == CPUID_XFAM_K8) {
|
||||
if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
|
||||
|
@ -1553,6 +1557,9 @@ static int __cpuinit powernowk8_init(void)
|
|||
unsigned int i, supported_cpus = 0, cpu;
|
||||
int rv;
|
||||
|
||||
if (!x86_match_cpu(powernow_k8_ids))
|
||||
return -ENODEV;
|
||||
|
||||
for_each_online_cpu(i) {
|
||||
int rc;
|
||||
smp_call_function_single(i, check_supported_cpu, &rc, 1);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/timex.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/msr.h>
|
||||
|
||||
#define MMCR_BASE 0xfffef000 /* The default base address */
|
||||
|
@ -150,18 +151,19 @@ static struct cpufreq_driver sc520_freq_driver = {
|
|||
.attr = sc520_freq_attr,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id sc520_ids[] = {
|
||||
{ X86_VENDOR_AMD, 4, 9 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, sc520_ids);
|
||||
|
||||
static int __init sc520_freq_init(void)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
int err;
|
||||
|
||||
/* Test if we have the right hardware */
|
||||
if (c->x86_vendor != X86_VENDOR_AMD ||
|
||||
c->x86 != 4 || c->x86_model != 9) {
|
||||
pr_debug("no Elan SC520 processor found!\n");
|
||||
if (!x86_match_cpu(sc520_ids))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);
|
||||
if (!cpuctl) {
|
||||
printk(KERN_ERR "sc520_freq: error: failed to remap memory\n");
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <asm/msr.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#define PFX "speedstep-centrino: "
|
||||
#define MAINTAINER "cpufreq@vger.kernel.org"
|
||||
|
@ -595,6 +596,24 @@ static struct cpufreq_driver centrino_driver = {
|
|||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/*
|
||||
* This doesn't replace the detailed checks above because
|
||||
* the generic CPU IDs don't have a way to match for steppings
|
||||
* or ASCII model IDs.
|
||||
*/
|
||||
static const struct x86_cpu_id centrino_ids[] = {
|
||||
{ X86_VENDOR_INTEL, 6, 9, X86_FEATURE_EST },
|
||||
{ X86_VENDOR_INTEL, 6, 13, X86_FEATURE_EST },
|
||||
{ X86_VENDOR_INTEL, 6, 13, X86_FEATURE_EST },
|
||||
{ X86_VENDOR_INTEL, 6, 13, X86_FEATURE_EST },
|
||||
{ X86_VENDOR_INTEL, 15, 3, X86_FEATURE_EST },
|
||||
{ X86_VENDOR_INTEL, 15, 4, X86_FEATURE_EST },
|
||||
{}
|
||||
};
|
||||
#if 0
|
||||
/* Autoload or not? Do not for now. */
|
||||
MODULE_DEVICE_TABLE(x86cpu, centrino_ids);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* centrino_init - initializes the Enhanced SpeedStep CPUFreq driver
|
||||
|
@ -612,11 +631,8 @@ static struct cpufreq_driver centrino_driver = {
|
|||
*/
|
||||
static int __init centrino_init(void)
|
||||
{
|
||||
struct cpuinfo_x86 *cpu = &cpu_data(0);
|
||||
|
||||
if (!cpu_has(cpu, X86_FEATURE_EST))
|
||||
if (!x86_match_cpu(centrino_ids))
|
||||
return -ENODEV;
|
||||
|
||||
return cpufreq_register_driver(¢rino_driver);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#include "speedstep-lib.h"
|
||||
|
||||
|
||||
|
@ -388,6 +390,16 @@ static struct cpufreq_driver speedstep_driver = {
|
|||
.attr = speedstep_attr,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id ss_smi_ids[] = {
|
||||
{ X86_VENDOR_INTEL, 6, 0xb, },
|
||||
{ X86_VENDOR_INTEL, 6, 0x8, },
|
||||
{ X86_VENDOR_INTEL, 15, 2 },
|
||||
{}
|
||||
};
|
||||
#if 0
|
||||
/* Autoload or not? Do not for now. */
|
||||
MODULE_DEVICE_TABLE(x86cpu, ss_smi_ids);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* speedstep_init - initializes the SpeedStep CPUFreq driver
|
||||
|
@ -398,6 +410,9 @@ static struct cpufreq_driver speedstep_driver = {
|
|||
*/
|
||||
static int __init speedstep_init(void)
|
||||
{
|
||||
if (!x86_match_cpu(ss_smi_ids))
|
||||
return -ENODEV;
|
||||
|
||||
/* detect processor */
|
||||
speedstep_processor = speedstep_detect_processor();
|
||||
if (!speedstep_processor) {
|
||||
|
|
|
@ -249,6 +249,7 @@ EXPORT_SYMBOL_GPL(speedstep_get_frequency);
|
|||
* DETECT SPEEDSTEP-CAPABLE PROCESSOR *
|
||||
*********************************************************************/
|
||||
|
||||
/* Keep in sync with the x86_cpu_id tables in the different modules */
|
||||
unsigned int speedstep_detect_processor(void)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/ist.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#include "speedstep-lib.h"
|
||||
|
||||
|
@ -379,6 +380,17 @@ static struct cpufreq_driver speedstep_driver = {
|
|||
.attr = speedstep_attr,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id ss_smi_ids[] = {
|
||||
{ X86_VENDOR_INTEL, 6, 0xb, },
|
||||
{ X86_VENDOR_INTEL, 6, 0x8, },
|
||||
{ X86_VENDOR_INTEL, 15, 2 },
|
||||
{}
|
||||
};
|
||||
#if 0
|
||||
/* Not auto loaded currently */
|
||||
MODULE_DEVICE_TABLE(x86cpu, ss_smi_ids);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* speedstep_init - initializes the SpeedStep CPUFreq driver
|
||||
*
|
||||
|
@ -388,6 +400,9 @@ static struct cpufreq_driver speedstep_driver = {
|
|||
*/
|
||||
static int __init speedstep_init(void)
|
||||
{
|
||||
if (!x86_match_cpu(ss_smi_ids))
|
||||
return -ENODEV;
|
||||
|
||||
speedstep_processor = speedstep_detect_processor();
|
||||
|
||||
switch (speedstep_processor) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/percpu.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/i387.h>
|
||||
|
@ -503,12 +504,18 @@ static struct crypto_alg cbc_aes_alg = {
|
|||
}
|
||||
};
|
||||
|
||||
static struct x86_cpu_id padlock_cpu_id[] = {
|
||||
X86_FEATURE_MATCH(X86_FEATURE_XCRYPT),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, padlock_cpu_id);
|
||||
|
||||
static int __init padlock_init(void)
|
||||
{
|
||||
int ret;
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
|
||||
if (!cpu_has_xcrypt)
|
||||
if (!x86_match_cpu(padlock_cpu_id))
|
||||
return -ENODEV;
|
||||
|
||||
if (!cpu_has_xcrypt_enabled) {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/i387.h>
|
||||
|
||||
struct padlock_sha_desc {
|
||||
|
@ -526,6 +527,12 @@ static struct shash_alg sha256_alg_nano = {
|
|||
}
|
||||
};
|
||||
|
||||
static struct x86_cpu_id padlock_sha_ids[] = {
|
||||
X86_FEATURE_MATCH(X86_FEATURE_PHE),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, padlock_sha_ids);
|
||||
|
||||
static int __init padlock_init(void)
|
||||
{
|
||||
int rc = -ENODEV;
|
||||
|
@ -533,15 +540,8 @@ static int __init padlock_init(void)
|
|||
struct shash_alg *sha1;
|
||||
struct shash_alg *sha256;
|
||||
|
||||
if (!cpu_has_phe) {
|
||||
printk(KERN_NOTICE PFX "VIA PadLock Hash Engine not detected.\n");
|
||||
if (!x86_match_cpu(padlock_sha_ids) || !cpu_has_phe_enabled)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!cpu_has_phe_enabled) {
|
||||
printk(KERN_NOTICE PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Register the newly added algorithm module if on *
|
||||
* VIA Nano processor, or else just do as before */
|
||||
|
|
|
@ -1619,11 +1619,7 @@ static ssize_t store_new_id(struct device_driver *drv, const char *buf,
|
|||
list_add_tail(&dynid->list, &hdrv->dyn_list);
|
||||
spin_unlock(&hdrv->dyn_lock);
|
||||
|
||||
ret = 0;
|
||||
if (get_driver(&hdrv->driver)) {
|
||||
ret = driver_attach(&hdrv->driver);
|
||||
put_driver(&hdrv->driver);
|
||||
}
|
||||
ret = driver_attach(&hdrv->driver);
|
||||
|
||||
return ret ? : count;
|
||||
}
|
||||
|
|
|
@ -37,81 +37,6 @@ struct vmbus_channel_message_table_entry {
|
|||
void (*message_handler)(struct vmbus_channel_message_header *msg);
|
||||
};
|
||||
|
||||
#define MAX_MSG_TYPES 4
|
||||
#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 8
|
||||
|
||||
static const uuid_le
|
||||
supported_device_classes[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = {
|
||||
/* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */
|
||||
/* Storage - SCSI */
|
||||
{
|
||||
.b = {
|
||||
0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d,
|
||||
0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f
|
||||
}
|
||||
},
|
||||
|
||||
/* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
|
||||
/* Network */
|
||||
{
|
||||
.b = {
|
||||
0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
|
||||
0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E
|
||||
}
|
||||
},
|
||||
|
||||
/* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */
|
||||
/* Input */
|
||||
{
|
||||
.b = {
|
||||
0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c,
|
||||
0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A
|
||||
}
|
||||
},
|
||||
|
||||
/* {32412632-86cb-44a2-9b5c-50d1417354f5} */
|
||||
/* IDE */
|
||||
{
|
||||
.b = {
|
||||
0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44,
|
||||
0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5
|
||||
}
|
||||
},
|
||||
/* 0E0B6031-5213-4934-818B-38D90CED39DB */
|
||||
/* Shutdown */
|
||||
{
|
||||
.b = {
|
||||
0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49,
|
||||
0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB
|
||||
}
|
||||
},
|
||||
/* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */
|
||||
/* TimeSync */
|
||||
{
|
||||
.b = {
|
||||
0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49,
|
||||
0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf
|
||||
}
|
||||
},
|
||||
/* {57164f39-9115-4e78-ab55-382f3bd5422d} */
|
||||
/* Heartbeat */
|
||||
{
|
||||
.b = {
|
||||
0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e,
|
||||
0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d
|
||||
}
|
||||
},
|
||||
/* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */
|
||||
/* KVP */
|
||||
{
|
||||
.b = {
|
||||
0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d,
|
||||
0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message
|
||||
|
@ -321,20 +246,8 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
|
|||
struct vmbus_channel *newchannel;
|
||||
uuid_le *guidtype;
|
||||
uuid_le *guidinstance;
|
||||
int i;
|
||||
int fsupported = 0;
|
||||
|
||||
offer = (struct vmbus_channel_offer_channel *)hdr;
|
||||
for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) {
|
||||
if (!uuid_le_cmp(offer->offer.if_type,
|
||||
supported_device_classes[i])) {
|
||||
fsupported = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fsupported)
|
||||
return;
|
||||
|
||||
guidtype = &offer->offer.if_type;
|
||||
guidinstance = &offer->offer.if_instance;
|
||||
|
|
|
@ -155,9 +155,9 @@ int hv_init(void)
|
|||
union hv_x64_msr_hypercall_contents hypercall_msr;
|
||||
void *virtaddr = NULL;
|
||||
|
||||
memset(hv_context.synic_event_page, 0, sizeof(void *) * MAX_NUM_CPUS);
|
||||
memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS);
|
||||
memset(hv_context.synic_message_page, 0,
|
||||
sizeof(void *) * MAX_NUM_CPUS);
|
||||
sizeof(void *) * NR_CPUS);
|
||||
|
||||
if (!query_hypervisor_presence())
|
||||
goto cleanup;
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
#include <linux/workqueue.h>
|
||||
#include <linux/hyperv.h>
|
||||
|
||||
#include "hv_kvp.h"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
@ -44,9 +42,10 @@
|
|||
static struct {
|
||||
bool active; /* transaction status - active or not */
|
||||
int recv_len; /* number of bytes received. */
|
||||
int index; /* current index */
|
||||
struct hv_kvp_msg *kvp_msg; /* current message */
|
||||
struct vmbus_channel *recv_channel; /* chn we got the request */
|
||||
u64 recv_req_id; /* request ID. */
|
||||
void *kvp_context; /* for the channel callback */
|
||||
} kvp_transaction;
|
||||
|
||||
static void kvp_send_key(struct work_struct *dummy);
|
||||
|
@ -73,15 +72,20 @@ kvp_register(void)
|
|||
{
|
||||
|
||||
struct cn_msg *msg;
|
||||
struct hv_kvp_msg *kvp_msg;
|
||||
char *version;
|
||||
|
||||
msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC);
|
||||
msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg), GFP_ATOMIC);
|
||||
|
||||
if (msg) {
|
||||
kvp_msg = (struct hv_kvp_msg *)msg->data;
|
||||
version = kvp_msg->body.kvp_register.version;
|
||||
msg->id.idx = CN_KVP_IDX;
|
||||
msg->id.val = CN_KVP_VAL;
|
||||
msg->seq = KVP_REGISTER;
|
||||
strcpy(msg->data, HV_DRV_VERSION);
|
||||
msg->len = strlen(HV_DRV_VERSION) + 1;
|
||||
|
||||
kvp_msg->kvp_hdr.operation = KVP_OP_REGISTER;
|
||||
strcpy(version, HV_DRV_VERSION);
|
||||
msg->len = sizeof(struct hv_kvp_msg);
|
||||
cn_netlink_send(msg, 0, GFP_ATOMIC);
|
||||
kfree(msg);
|
||||
}
|
||||
|
@ -103,23 +107,28 @@ kvp_work_func(struct work_struct *dummy)
|
|||
static void
|
||||
kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
|
||||
{
|
||||
struct hv_ku_msg *message;
|
||||
struct hv_kvp_msg *message;
|
||||
struct hv_kvp_msg_enumerate *data;
|
||||
|
||||
message = (struct hv_ku_msg *)msg->data;
|
||||
if (msg->seq == KVP_REGISTER) {
|
||||
message = (struct hv_kvp_msg *)msg->data;
|
||||
switch (message->kvp_hdr.operation) {
|
||||
case KVP_OP_REGISTER:
|
||||
pr_info("KVP: user-mode registering done.\n");
|
||||
kvp_register();
|
||||
}
|
||||
kvp_transaction.active = false;
|
||||
hv_kvp_onchannelcallback(kvp_transaction.kvp_context);
|
||||
break;
|
||||
|
||||
if (msg->seq == KVP_USER_SET) {
|
||||
default:
|
||||
data = &message->body.kvp_enum_data;
|
||||
/*
|
||||
* Complete the transaction by forwarding the key value
|
||||
* to the host. But first, cancel the timeout.
|
||||
*/
|
||||
if (cancel_delayed_work_sync(&kvp_work))
|
||||
kvp_respond_to_host(message->kvp_key,
|
||||
message->kvp_value,
|
||||
!strlen(message->kvp_key));
|
||||
kvp_respond_to_host(data->data.key,
|
||||
data->data.value,
|
||||
!strlen(data->data.key));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,19 +136,105 @@ static void
|
|||
kvp_send_key(struct work_struct *dummy)
|
||||
{
|
||||
struct cn_msg *msg;
|
||||
int index = kvp_transaction.index;
|
||||
struct hv_kvp_msg *message;
|
||||
struct hv_kvp_msg *in_msg;
|
||||
__u8 operation = kvp_transaction.kvp_msg->kvp_hdr.operation;
|
||||
__u8 pool = kvp_transaction.kvp_msg->kvp_hdr.pool;
|
||||
__u32 val32;
|
||||
__u64 val64;
|
||||
|
||||
msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
if (msg) {
|
||||
msg->id.idx = CN_KVP_IDX;
|
||||
msg->id.val = CN_KVP_VAL;
|
||||
msg->seq = KVP_KERNEL_GET;
|
||||
((struct hv_ku_msg *)msg->data)->kvp_index = index;
|
||||
msg->len = sizeof(struct hv_ku_msg);
|
||||
cn_netlink_send(msg, 0, GFP_ATOMIC);
|
||||
kfree(msg);
|
||||
msg->id.idx = CN_KVP_IDX;
|
||||
msg->id.val = CN_KVP_VAL;
|
||||
|
||||
message = (struct hv_kvp_msg *)msg->data;
|
||||
message->kvp_hdr.operation = operation;
|
||||
message->kvp_hdr.pool = pool;
|
||||
in_msg = kvp_transaction.kvp_msg;
|
||||
|
||||
/*
|
||||
* The key/value strings sent from the host are encoded in
|
||||
* in utf16; convert it to utf8 strings.
|
||||
* The host assures us that the utf16 strings will not exceed
|
||||
* the max lengths specified. We will however, reserve room
|
||||
* for the string terminating character - in the utf16s_utf8s()
|
||||
* function we limit the size of the buffer where the converted
|
||||
* string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to gaurantee
|
||||
* that the strings can be properly terminated!
|
||||
*/
|
||||
|
||||
switch (message->kvp_hdr.operation) {
|
||||
case KVP_OP_SET:
|
||||
switch (in_msg->body.kvp_set.data.value_type) {
|
||||
case REG_SZ:
|
||||
/*
|
||||
* The value is a string - utf16 encoding.
|
||||
*/
|
||||
message->body.kvp_set.data.value_size =
|
||||
utf16s_to_utf8s(
|
||||
(wchar_t *)in_msg->body.kvp_set.data.value,
|
||||
in_msg->body.kvp_set.data.value_size,
|
||||
UTF16_LITTLE_ENDIAN,
|
||||
message->body.kvp_set.data.value,
|
||||
HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1) + 1;
|
||||
break;
|
||||
|
||||
case REG_U32:
|
||||
/*
|
||||
* The value is a 32 bit scalar.
|
||||
* We save this as a utf8 string.
|
||||
*/
|
||||
val32 = in_msg->body.kvp_set.data.value_u32;
|
||||
message->body.kvp_set.data.value_size =
|
||||
sprintf(message->body.kvp_set.data.value,
|
||||
"%d", val32) + 1;
|
||||
break;
|
||||
|
||||
case REG_U64:
|
||||
/*
|
||||
* The value is a 64 bit scalar.
|
||||
* We save this as a utf8 string.
|
||||
*/
|
||||
val64 = in_msg->body.kvp_set.data.value_u64;
|
||||
message->body.kvp_set.data.value_size =
|
||||
sprintf(message->body.kvp_set.data.value,
|
||||
"%llu", val64) + 1;
|
||||
break;
|
||||
|
||||
}
|
||||
case KVP_OP_GET:
|
||||
message->body.kvp_set.data.key_size =
|
||||
utf16s_to_utf8s(
|
||||
(wchar_t *)in_msg->body.kvp_set.data.key,
|
||||
in_msg->body.kvp_set.data.key_size,
|
||||
UTF16_LITTLE_ENDIAN,
|
||||
message->body.kvp_set.data.key,
|
||||
HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
|
||||
break;
|
||||
|
||||
case KVP_OP_DELETE:
|
||||
message->body.kvp_delete.key_size =
|
||||
utf16s_to_utf8s(
|
||||
(wchar_t *)in_msg->body.kvp_delete.key,
|
||||
in_msg->body.kvp_delete.key_size,
|
||||
UTF16_LITTLE_ENDIAN,
|
||||
message->body.kvp_delete.key,
|
||||
HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
|
||||
break;
|
||||
|
||||
case KVP_OP_ENUMERATE:
|
||||
message->body.kvp_enum_data.index =
|
||||
in_msg->body.kvp_enum_data.index;
|
||||
break;
|
||||
}
|
||||
|
||||
msg->len = sizeof(struct hv_kvp_msg);
|
||||
cn_netlink_send(msg, 0, GFP_ATOMIC);
|
||||
kfree(msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -151,10 +246,11 @@ static void
|
|||
kvp_respond_to_host(char *key, char *value, int error)
|
||||
{
|
||||
struct hv_kvp_msg *kvp_msg;
|
||||
struct hv_kvp_msg_enumerate *kvp_data;
|
||||
struct hv_kvp_exchg_msg_value *kvp_data;
|
||||
char *key_name;
|
||||
struct icmsg_hdr *icmsghdrp;
|
||||
int keylen, valuelen;
|
||||
int keylen = 0;
|
||||
int valuelen = 0;
|
||||
u32 buf_len;
|
||||
struct vmbus_channel *channel;
|
||||
u64 req_id;
|
||||
|
@ -181,6 +277,9 @@ kvp_respond_to_host(char *key, char *value, int error)
|
|||
|
||||
kvp_transaction.active = false;
|
||||
|
||||
icmsghdrp = (struct icmsg_hdr *)
|
||||
&recv_buffer[sizeof(struct vmbuspipe_hdr)];
|
||||
|
||||
if (channel->onchannel_callback == NULL)
|
||||
/*
|
||||
* We have raced with util driver being unloaded;
|
||||
|
@ -188,41 +287,67 @@ kvp_respond_to_host(char *key, char *value, int error)
|
|||
*/
|
||||
return;
|
||||
|
||||
icmsghdrp = (struct icmsg_hdr *)
|
||||
&recv_buffer[sizeof(struct vmbuspipe_hdr)];
|
||||
kvp_msg = (struct hv_kvp_msg *)
|
||||
&recv_buffer[sizeof(struct vmbuspipe_hdr) +
|
||||
sizeof(struct icmsg_hdr)];
|
||||
kvp_data = &kvp_msg->kvp_data;
|
||||
key_name = key;
|
||||
|
||||
/*
|
||||
* If the error parameter is set, terminate the host's enumeration.
|
||||
* If the error parameter is set, terminate the host's enumeration
|
||||
* on this pool.
|
||||
*/
|
||||
if (error) {
|
||||
/*
|
||||
* We don't support this index or the we have timedout;
|
||||
* terminate the host-side iteration by returning an error.
|
||||
* Something failed or the we have timedout;
|
||||
* terminate the current host-side iteration.
|
||||
*/
|
||||
icmsghdrp->status = HV_E_FAIL;
|
||||
icmsghdrp->status = HV_S_CONT;
|
||||
goto response_done;
|
||||
}
|
||||
|
||||
icmsghdrp->status = HV_S_OK;
|
||||
|
||||
kvp_msg = (struct hv_kvp_msg *)
|
||||
&recv_buffer[sizeof(struct vmbuspipe_hdr) +
|
||||
sizeof(struct icmsg_hdr)];
|
||||
|
||||
switch (kvp_transaction.kvp_msg->kvp_hdr.operation) {
|
||||
case KVP_OP_GET:
|
||||
kvp_data = &kvp_msg->body.kvp_get.data;
|
||||
goto copy_value;
|
||||
|
||||
case KVP_OP_SET:
|
||||
case KVP_OP_DELETE:
|
||||
goto response_done;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
kvp_data = &kvp_msg->body.kvp_enum_data.data;
|
||||
key_name = key;
|
||||
|
||||
/*
|
||||
* The windows host expects the key/value pair to be encoded
|
||||
* in utf16.
|
||||
* in utf16. Ensure that the key/value size reported to the host
|
||||
* will be less than or equal to the MAX size (including the
|
||||
* terminating character).
|
||||
*/
|
||||
keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN,
|
||||
(wchar_t *) kvp_data->data.key,
|
||||
HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2);
|
||||
kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */
|
||||
valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN,
|
||||
(wchar_t *) kvp_data->data.value,
|
||||
HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2);
|
||||
kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */
|
||||
(wchar_t *) kvp_data->key,
|
||||
(HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2);
|
||||
kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */
|
||||
|
||||
kvp_data->data.value_type = REG_SZ; /* all our values are strings */
|
||||
icmsghdrp->status = HV_S_OK;
|
||||
copy_value:
|
||||
valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN,
|
||||
(wchar_t *) kvp_data->value,
|
||||
(HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2);
|
||||
kvp_data->value_size = 2*(valuelen + 1); /* utf16 encoding */
|
||||
|
||||
/*
|
||||
* If the utf8s to utf16s conversion failed; notify host
|
||||
* of the error.
|
||||
*/
|
||||
if ((keylen < 0) || (valuelen < 0))
|
||||
icmsghdrp->status = HV_E_FAIL;
|
||||
|
||||
kvp_data->value_type = REG_SZ; /* all our values are strings */
|
||||
|
||||
response_done:
|
||||
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
|
||||
|
@ -249,11 +374,18 @@ void hv_kvp_onchannelcallback(void *context)
|
|||
u64 requestid;
|
||||
|
||||
struct hv_kvp_msg *kvp_msg;
|
||||
struct hv_kvp_msg_enumerate *kvp_data;
|
||||
|
||||
struct icmsg_hdr *icmsghdrp;
|
||||
struct icmsg_negotiate *negop = NULL;
|
||||
|
||||
if (kvp_transaction.active) {
|
||||
/*
|
||||
* We will defer processing this callback once
|
||||
* the current transaction is complete.
|
||||
*/
|
||||
kvp_transaction.kvp_context = context;
|
||||
return;
|
||||
}
|
||||
|
||||
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid);
|
||||
|
||||
|
@ -268,29 +400,16 @@ void hv_kvp_onchannelcallback(void *context)
|
|||
sizeof(struct vmbuspipe_hdr) +
|
||||
sizeof(struct icmsg_hdr)];
|
||||
|
||||
kvp_data = &kvp_msg->kvp_data;
|
||||
|
||||
/*
|
||||
* We only support the "get" operation on
|
||||
* "KVP_POOL_AUTO" pool.
|
||||
*/
|
||||
|
||||
if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) ||
|
||||
(kvp_msg->kvp_hdr.operation !=
|
||||
KVP_OP_ENUMERATE)) {
|
||||
icmsghdrp->status = HV_E_FAIL;
|
||||
goto callback_done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stash away this global state for completing the
|
||||
* transaction; note transactions are serialized.
|
||||
*/
|
||||
|
||||
kvp_transaction.recv_len = recvlen;
|
||||
kvp_transaction.recv_channel = channel;
|
||||
kvp_transaction.recv_req_id = requestid;
|
||||
kvp_transaction.active = true;
|
||||
kvp_transaction.index = kvp_data->index;
|
||||
kvp_transaction.kvp_msg = kvp_msg;
|
||||
|
||||
/*
|
||||
* Get the information from the
|
||||
|
@ -308,8 +427,6 @@ void hv_kvp_onchannelcallback(void *context)
|
|||
|
||||
}
|
||||
|
||||
callback_done:
|
||||
|
||||
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
|
||||
| ICMSGHDRFLAG_RESPONSE;
|
||||
|
||||
|
@ -330,6 +447,14 @@ hv_kvp_init(struct hv_util_service *srv)
|
|||
return err;
|
||||
recv_buffer = srv->recv_buffer;
|
||||
|
||||
/*
|
||||
* When this driver loads, the user level daemon that
|
||||
* processes the host requests may not yet be running.
|
||||
* Defer processing channel callbacks until the daemon
|
||||
* has registered.
|
||||
*/
|
||||
kvp_transaction.active = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
* An implementation of HyperV key value pair (KVP) functionality for Linux.
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2010, Novell, Inc.
|
||||
* Author : K. Y. Srinivasan <ksrinivasan@novell.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
|
||||
* NON INFRINGEMENT. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
#ifndef _KVP_H
|
||||
#define _KVP_H_
|
||||
|
||||
/*
|
||||
* Maximum value size - used for both key names and value data, and includes
|
||||
* any applicable NULL terminators.
|
||||
*
|
||||
* Note: This limit is somewhat arbitrary, but falls easily within what is
|
||||
* supported for all native guests (back to Win 2000) and what is reasonable
|
||||
* for the IC KVP exchange functionality. Note that Windows Me/98/95 are
|
||||
* limited to 255 character key names.
|
||||
*
|
||||
* MSDN recommends not storing data values larger than 2048 bytes in the
|
||||
* registry.
|
||||
*
|
||||
* Note: This value is used in defining the KVP exchange message - this value
|
||||
* cannot be modified without affecting the message size and compatibility.
|
||||
*/
|
||||
|
||||
/*
|
||||
* bytes, including any null terminators
|
||||
*/
|
||||
#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048)
|
||||
|
||||
|
||||
/*
|
||||
* Maximum key size - the registry limit for the length of an entry name
|
||||
* is 256 characters, including the null terminator
|
||||
*/
|
||||
|
||||
#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512)
|
||||
|
||||
/*
|
||||
* In Linux, we implement the KVP functionality in two components:
|
||||
* 1) The kernel component which is packaged as part of the hv_utils driver
|
||||
* is responsible for communicating with the host and responsible for
|
||||
* implementing the host/guest protocol. 2) A user level daemon that is
|
||||
* responsible for data gathering.
|
||||
*
|
||||
* Host/Guest Protocol: The host iterates over an index and expects the guest
|
||||
* to assign a key name to the index and also return the value corresponding to
|
||||
* the key. The host will have atmost one KVP transaction outstanding at any
|
||||
* given point in time. The host side iteration stops when the guest returns
|
||||
* an error. Microsoft has specified the following mapping of key names to
|
||||
* host specified index:
|
||||
*
|
||||
* Index Key Name
|
||||
* 0 FullyQualifiedDomainName
|
||||
* 1 IntegrationServicesVersion
|
||||
* 2 NetworkAddressIPv4
|
||||
* 3 NetworkAddressIPv6
|
||||
* 4 OSBuildNumber
|
||||
* 5 OSName
|
||||
* 6 OSMajorVersion
|
||||
* 7 OSMinorVersion
|
||||
* 8 OSVersion
|
||||
* 9 ProcessorArchitecture
|
||||
*
|
||||
* The Windows host expects the Key Name and Key Value to be encoded in utf16.
|
||||
*
|
||||
* Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the
|
||||
* data gathering functionality in a user mode daemon. The user level daemon
|
||||
* is also responsible for binding the key name to the index as well. The
|
||||
* kernel and user-level daemon communicate using a connector channel.
|
||||
*
|
||||
* The user mode component first registers with the
|
||||
* the kernel component. Subsequently, the kernel component requests, data
|
||||
* for the specified keys. In response to this message the user mode component
|
||||
* fills in the value corresponding to the specified key. We overload the
|
||||
* sequence field in the cn_msg header to define our KVP message types.
|
||||
*
|
||||
*
|
||||
* The kernel component simply acts as a conduit for communication between the
|
||||
* Windows host and the user-level daemon. The kernel component passes up the
|
||||
* index received from the Host to the user-level daemon. If the index is
|
||||
* valid (supported), the corresponding key as well as its
|
||||
* value (both are strings) is returned. If the index is invalid
|
||||
* (not supported), a NULL key string is returned.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* The following definitions are shared with the user-mode component; do not
|
||||
* change any of this without making the corresponding changes in
|
||||
* the KVP user-mode component.
|
||||
*/
|
||||
|
||||
#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */
|
||||
#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */
|
||||
|
||||
enum hv_ku_op {
|
||||
KVP_REGISTER = 0, /* Register the user mode component */
|
||||
KVP_KERNEL_GET, /* Kernel is requesting the value */
|
||||
KVP_KERNEL_SET, /* Kernel is providing the value */
|
||||
KVP_USER_GET, /* User is requesting the value */
|
||||
KVP_USER_SET /* User is providing the value */
|
||||
};
|
||||
|
||||
struct hv_ku_msg {
|
||||
__u32 kvp_index; /* Key index */
|
||||
__u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */
|
||||
__u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/*
|
||||
* Registry value types.
|
||||
*/
|
||||
|
||||
#define REG_SZ 1
|
||||
|
||||
enum hv_kvp_exchg_op {
|
||||
KVP_OP_GET = 0,
|
||||
KVP_OP_SET,
|
||||
KVP_OP_DELETE,
|
||||
KVP_OP_ENUMERATE,
|
||||
KVP_OP_COUNT /* Number of operations, must be last. */
|
||||
};
|
||||
|
||||
enum hv_kvp_exchg_pool {
|
||||
KVP_POOL_EXTERNAL = 0,
|
||||
KVP_POOL_GUEST,
|
||||
KVP_POOL_AUTO,
|
||||
KVP_POOL_AUTO_EXTERNAL,
|
||||
KVP_POOL_AUTO_INTERNAL,
|
||||
KVP_POOL_COUNT /* Number of pools, must be last. */
|
||||
};
|
||||
|
||||
struct hv_kvp_hdr {
|
||||
u8 operation;
|
||||
u8 pool;
|
||||
};
|
||||
|
||||
struct hv_kvp_exchg_msg_value {
|
||||
u32 value_type;
|
||||
u32 key_size;
|
||||
u32 value_size;
|
||||
u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
|
||||
u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
|
||||
};
|
||||
|
||||
struct hv_kvp_msg_enumerate {
|
||||
u32 index;
|
||||
struct hv_kvp_exchg_msg_value data;
|
||||
};
|
||||
|
||||
struct hv_kvp_msg {
|
||||
struct hv_kvp_hdr kvp_hdr;
|
||||
struct hv_kvp_msg_enumerate kvp_data;
|
||||
};
|
||||
|
||||
int hv_kvp_init(struct hv_util_service *);
|
||||
void hv_kvp_deinit(void);
|
||||
void hv_kvp_onchannelcallback(void *);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* _KVP_H */
|
||||
|
|
@ -28,9 +28,6 @@
|
|||
#include <linux/reboot.h>
|
||||
#include <linux/hyperv.h>
|
||||
|
||||
#include "hv_kvp.h"
|
||||
|
||||
|
||||
static void shutdown_onchannelcallback(void *context);
|
||||
static struct hv_util_service util_shutdown = {
|
||||
.util_cb = shutdown_onchannelcallback,
|
||||
|
|
|
@ -457,7 +457,6 @@ static const uuid_le VMBUS_SERVICE_ID = {
|
|||
},
|
||||
};
|
||||
|
||||
#define MAX_NUM_CPUS 32
|
||||
|
||||
|
||||
struct hv_input_signal_event_buffer {
|
||||
|
@ -483,8 +482,8 @@ struct hv_context {
|
|||
/* 8-bytes aligned of the buffer above */
|
||||
struct hv_input_signal_event *signal_event_param;
|
||||
|
||||
void *synic_message_page[MAX_NUM_CPUS];
|
||||
void *synic_event_page[MAX_NUM_CPUS];
|
||||
void *synic_message_page[NR_CPUS];
|
||||
void *synic_event_page[NR_CPUS];
|
||||
};
|
||||
|
||||
extern struct hv_context hv_context;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <linux/moduleparam.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#define DRVNAME "coretemp"
|
||||
|
||||
|
@ -759,13 +760,23 @@ static struct notifier_block coretemp_cpu_notifier __refdata = {
|
|||
.notifier_call = coretemp_cpu_callback,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id coretemp_ids[] = {
|
||||
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTS },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, coretemp_ids);
|
||||
|
||||
static int __init coretemp_init(void)
|
||||
{
|
||||
int i, err = -ENODEV;
|
||||
|
||||
/* quick check if we run Intel */
|
||||
if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL)
|
||||
goto exit;
|
||||
/*
|
||||
* CPUID.06H.EAX[0] indicates whether the CPU has thermal
|
||||
* sensors. We check this bit only, all the early CPUs
|
||||
* without thermal sensors will be filtered out.
|
||||
*/
|
||||
if (!x86_match_cpu(coretemp_ids))
|
||||
return -ENODEV;
|
||||
|
||||
err = platform_driver_register(&coretemp_driver);
|
||||
if (err)
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <linux/cpu.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#define DRVNAME "via_cputemp"
|
||||
|
||||
|
@ -308,15 +309,20 @@ static struct notifier_block via_cputemp_cpu_notifier __refdata = {
|
|||
.notifier_call = via_cputemp_cpu_callback,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id cputemp_ids[] = {
|
||||
{ X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */
|
||||
{ X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */
|
||||
{ X86_VENDOR_CENTAUR, 6, 0xf, }, /* Nano */
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, cputemp_ids);
|
||||
|
||||
static int __init via_cputemp_init(void)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
if (cpu_data(0).x86_vendor != X86_VENDOR_CENTAUR) {
|
||||
printk(KERN_DEBUG DRVNAME ": Not a VIA CPU\n");
|
||||
err = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
if (!x86_match_cpu(cputemp_ids))
|
||||
return -ENODEV;
|
||||
|
||||
err = platform_driver_register(&via_cputemp_driver);
|
||||
if (err)
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
#include <linux/notifier.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/mwait.h>
|
||||
#include <asm/msr.h>
|
||||
|
||||
|
@ -81,18 +82,23 @@ static unsigned int mwait_substates;
|
|||
/* Reliable LAPIC Timer States, bit 1 for C1 etc. */
|
||||
static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */
|
||||
|
||||
struct idle_cpu {
|
||||
struct cpuidle_state *state_table;
|
||||
|
||||
/*
|
||||
* Hardware C-state auto-demotion may not always be optimal.
|
||||
* Indicate which enable bits to clear here.
|
||||
*/
|
||||
unsigned long auto_demotion_disable_flags;
|
||||
};
|
||||
|
||||
static const struct idle_cpu *icpu;
|
||||
static struct cpuidle_device __percpu *intel_idle_cpuidle_devices;
|
||||
static int intel_idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index);
|
||||
|
||||
static struct cpuidle_state *cpuidle_state_table;
|
||||
|
||||
/*
|
||||
* Hardware C-state auto-demotion may not always be optimal.
|
||||
* Indicate which enable bits to clear here.
|
||||
*/
|
||||
static unsigned long long auto_demotion_disable_flags;
|
||||
|
||||
/*
|
||||
* Set this flag for states where the HW flushes the TLB for us
|
||||
* and so we don't need cross-calls to keep it consistent.
|
||||
|
@ -319,27 +325,68 @@ static void auto_demotion_disable(void *dummy)
|
|||
unsigned long long msr_bits;
|
||||
|
||||
rdmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits);
|
||||
msr_bits &= ~auto_demotion_disable_flags;
|
||||
msr_bits &= ~(icpu->auto_demotion_disable_flags);
|
||||
wrmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits);
|
||||
}
|
||||
|
||||
static const struct idle_cpu idle_cpu_nehalem = {
|
||||
.state_table = nehalem_cstates,
|
||||
.auto_demotion_disable_flags = NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE,
|
||||
};
|
||||
|
||||
static const struct idle_cpu idle_cpu_atom = {
|
||||
.state_table = atom_cstates,
|
||||
};
|
||||
|
||||
static const struct idle_cpu idle_cpu_lincroft = {
|
||||
.state_table = atom_cstates,
|
||||
.auto_demotion_disable_flags = ATM_LNC_C6_AUTO_DEMOTE,
|
||||
};
|
||||
|
||||
static const struct idle_cpu idle_cpu_snb = {
|
||||
.state_table = snb_cstates,
|
||||
};
|
||||
|
||||
#define ICPU(model, cpu) \
|
||||
{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&cpu }
|
||||
|
||||
static const struct x86_cpu_id intel_idle_ids[] = {
|
||||
ICPU(0x1a, idle_cpu_nehalem),
|
||||
ICPU(0x1e, idle_cpu_nehalem),
|
||||
ICPU(0x1f, idle_cpu_nehalem),
|
||||
ICPU(0x25, idle_cpu_nehalem),
|
||||
ICPU(0x2c, idle_cpu_nehalem),
|
||||
ICPU(0x2e, idle_cpu_nehalem),
|
||||
ICPU(0x1c, idle_cpu_atom),
|
||||
ICPU(0x26, idle_cpu_lincroft),
|
||||
ICPU(0x2f, idle_cpu_nehalem),
|
||||
ICPU(0x2a, idle_cpu_snb),
|
||||
ICPU(0x2d, idle_cpu_snb),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, intel_idle_ids);
|
||||
|
||||
/*
|
||||
* intel_idle_probe()
|
||||
*/
|
||||
static int intel_idle_probe(void)
|
||||
{
|
||||
unsigned int eax, ebx, ecx;
|
||||
const struct x86_cpu_id *id;
|
||||
|
||||
if (max_cstate == 0) {
|
||||
pr_debug(PREFIX "disabled\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
|
||||
return -ENODEV;
|
||||
|
||||
if (!boot_cpu_has(X86_FEATURE_MWAIT))
|
||||
id = x86_match_cpu(intel_idle_ids);
|
||||
if (!id) {
|
||||
if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
|
||||
boot_cpu_data.x86 == 6)
|
||||
pr_debug(PREFIX "does not run on family %d model %d\n",
|
||||
boot_cpu_data.x86, boot_cpu_data.x86_model);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)
|
||||
return -ENODEV;
|
||||
|
@ -353,43 +400,8 @@ static int intel_idle_probe(void)
|
|||
|
||||
pr_debug(PREFIX "MWAIT substates: 0x%x\n", mwait_substates);
|
||||
|
||||
|
||||
if (boot_cpu_data.x86 != 6) /* family 6 */
|
||||
return -ENODEV;
|
||||
|
||||
switch (boot_cpu_data.x86_model) {
|
||||
|
||||
case 0x1A: /* Core i7, Xeon 5500 series */
|
||||
case 0x1E: /* Core i7 and i5 Processor - Lynnfield Jasper Forest */
|
||||
case 0x1F: /* Core i7 and i5 Processor - Nehalem */
|
||||
case 0x2E: /* Nehalem-EX Xeon */
|
||||
case 0x2F: /* Westmere-EX Xeon */
|
||||
case 0x25: /* Westmere */
|
||||
case 0x2C: /* Westmere */
|
||||
cpuidle_state_table = nehalem_cstates;
|
||||
auto_demotion_disable_flags =
|
||||
(NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE);
|
||||
break;
|
||||
|
||||
case 0x1C: /* 28 - Atom Processor */
|
||||
cpuidle_state_table = atom_cstates;
|
||||
break;
|
||||
|
||||
case 0x26: /* 38 - Lincroft Atom Processor */
|
||||
cpuidle_state_table = atom_cstates;
|
||||
auto_demotion_disable_flags = ATM_LNC_C6_AUTO_DEMOTE;
|
||||
break;
|
||||
|
||||
case 0x2A: /* SNB */
|
||||
case 0x2D: /* SNB Xeon */
|
||||
cpuidle_state_table = snb_cstates;
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_debug(PREFIX "does not run on family %d model %d\n",
|
||||
boot_cpu_data.x86, boot_cpu_data.x86_model);
|
||||
return -ENODEV;
|
||||
}
|
||||
icpu = (const struct idle_cpu *)id->driver_data;
|
||||
cpuidle_state_table = icpu->state_table;
|
||||
|
||||
if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */
|
||||
lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE;
|
||||
|
@ -470,7 +482,7 @@ static int intel_idle_cpuidle_driver_init(void)
|
|||
drv->state_count += 1;
|
||||
}
|
||||
|
||||
if (auto_demotion_disable_flags)
|
||||
if (icpu->auto_demotion_disable_flags)
|
||||
on_each_cpu(auto_demotion_disable, NULL, 1);
|
||||
|
||||
return 0;
|
||||
|
@ -522,7 +534,7 @@ int intel_idle_cpu_init(int cpu)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
if (auto_demotion_disable_flags)
|
||||
if (icpu->auto_demotion_disable_flags)
|
||||
smp_call_function_single(cpu, auto_demotion_disable, NULL, 1);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -449,7 +449,6 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut
|
|||
} else if ((drv = driver_find(buf, &gameport_bus)) != NULL) {
|
||||
gameport_disconnect_port(gameport);
|
||||
error = gameport_bind_driver(gameport, to_gameport_driver(drv));
|
||||
put_driver(drv);
|
||||
} else {
|
||||
error = -EINVAL;
|
||||
}
|
||||
|
|
|
@ -441,7 +441,6 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
|
|||
} else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
|
||||
serio_disconnect_port(serio);
|
||||
error = serio_bind_driver(serio, to_serio_driver(drv));
|
||||
put_driver(drv);
|
||||
serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT);
|
||||
} else {
|
||||
error = -EINVAL;
|
||||
|
|
|
@ -285,7 +285,6 @@ static void __exit cx18_alsa_exit(void)
|
|||
|
||||
drv = driver_find("cx18", &pci_bus_type);
|
||||
ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback);
|
||||
put_driver(drv);
|
||||
|
||||
cx18_ext_init = NULL;
|
||||
printk(KERN_INFO "cx18-alsa: module unload complete\n");
|
||||
|
|
|
@ -1293,7 +1293,6 @@ static int __init ivtvfb_init(void)
|
|||
|
||||
drv = driver_find("ivtv", &pci_bus_type);
|
||||
err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init);
|
||||
put_driver(drv);
|
||||
if (!registered) {
|
||||
printk(KERN_ERR "ivtvfb: no cards found\n");
|
||||
return -ENODEV;
|
||||
|
@ -1310,7 +1309,6 @@ static void ivtvfb_cleanup(void)
|
|||
|
||||
drv = driver_find("ivtv", &pci_bus_type);
|
||||
err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup);
|
||||
put_driver(drv);
|
||||
}
|
||||
|
||||
module_init(ivtvfb_init);
|
||||
|
|
|
@ -344,16 +344,13 @@ static int fimc_md_register_platform_entities(struct fimc_md *fmd)
|
|||
return -ENODEV;
|
||||
ret = driver_for_each_device(driver, NULL, fmd,
|
||||
fimc_register_callback);
|
||||
put_driver(driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
|
||||
if (driver) {
|
||||
if (driver)
|
||||
ret = driver_for_each_device(driver, NULL, fmd,
|
||||
csis_register_callback);
|
||||
put_driver(driver);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@ static struct v4l2_subdev *find_and_register_subdev(
|
|||
}
|
||||
|
||||
done:
|
||||
put_driver(drv);
|
||||
return sd;
|
||||
}
|
||||
|
||||
|
|
|
@ -915,9 +915,7 @@ static int phy_probe(struct device *dev)
|
|||
|
||||
phydev = to_phy_device(dev);
|
||||
|
||||
/* Make sure the driver is held.
|
||||
* XXX -- Is this correct? */
|
||||
drv = get_driver(phydev->dev.driver);
|
||||
drv = phydev->dev.driver;
|
||||
phydrv = to_phy_driver(drv);
|
||||
phydev->drv = phydrv;
|
||||
|
||||
|
@ -957,8 +955,6 @@ static int phy_remove(struct device *dev)
|
|||
|
||||
if (phydev->drv->remove)
|
||||
phydev->drv->remove(phydev);
|
||||
|
||||
put_driver(dev->driver);
|
||||
phydev->drv = NULL;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -72,9 +72,7 @@ int pci_add_dynid(struct pci_driver *drv,
|
|||
list_add_tail(&dynid->node, &drv->dynids.list);
|
||||
spin_unlock(&drv->dynids.lock);
|
||||
|
||||
get_driver(&drv->driver);
|
||||
retval = driver_attach(&drv->driver);
|
||||
put_driver(&drv->driver);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -190,43 +188,34 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
|
|||
static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
|
||||
|
||||
static int
|
||||
pci_create_newid_file(struct pci_driver *drv)
|
||||
pci_create_newid_files(struct pci_driver *drv)
|
||||
{
|
||||
int error = 0;
|
||||
if (drv->probe != NULL)
|
||||
|
||||
if (drv->probe != NULL) {
|
||||
error = driver_create_file(&drv->driver, &driver_attr_new_id);
|
||||
if (error == 0) {
|
||||
error = driver_create_file(&drv->driver,
|
||||
&driver_attr_remove_id);
|
||||
if (error)
|
||||
driver_remove_file(&drv->driver,
|
||||
&driver_attr_new_id);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static void pci_remove_newid_file(struct pci_driver *drv)
|
||||
{
|
||||
driver_remove_file(&drv->driver, &driver_attr_new_id);
|
||||
}
|
||||
|
||||
static int
|
||||
pci_create_removeid_file(struct pci_driver *drv)
|
||||
{
|
||||
int error = 0;
|
||||
if (drv->probe != NULL)
|
||||
error = driver_create_file(&drv->driver,&driver_attr_remove_id);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void pci_remove_removeid_file(struct pci_driver *drv)
|
||||
static void pci_remove_newid_files(struct pci_driver *drv)
|
||||
{
|
||||
driver_remove_file(&drv->driver, &driver_attr_remove_id);
|
||||
driver_remove_file(&drv->driver, &driver_attr_new_id);
|
||||
}
|
||||
#else /* !CONFIG_HOTPLUG */
|
||||
static inline int pci_create_newid_file(struct pci_driver *drv)
|
||||
static inline int pci_create_newid_files(struct pci_driver *drv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void pci_remove_newid_file(struct pci_driver *drv) {}
|
||||
static inline int pci_create_removeid_file(struct pci_driver *drv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void pci_remove_removeid_file(struct pci_driver *drv) {}
|
||||
static inline void pci_remove_newid_files(struct pci_driver *drv) {}
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -1138,18 +1127,12 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
|
|||
if (error)
|
||||
goto out;
|
||||
|
||||
error = pci_create_newid_file(drv);
|
||||
error = pci_create_newid_files(drv);
|
||||
if (error)
|
||||
goto out_newid;
|
||||
|
||||
error = pci_create_removeid_file(drv);
|
||||
if (error)
|
||||
goto out_removeid;
|
||||
out:
|
||||
return error;
|
||||
|
||||
out_removeid:
|
||||
pci_remove_newid_file(drv);
|
||||
out_newid:
|
||||
driver_unregister(&drv->driver);
|
||||
goto out;
|
||||
|
@ -1168,8 +1151,7 @@ out_newid:
|
|||
void
|
||||
pci_unregister_driver(struct pci_driver *drv)
|
||||
{
|
||||
pci_remove_removeid_file(drv);
|
||||
pci_remove_newid_file(drv);
|
||||
pci_remove_newid_files(drv);
|
||||
driver_unregister(&drv->driver);
|
||||
pci_free_dynids(drv);
|
||||
}
|
||||
|
|
|
@ -593,7 +593,7 @@ static pci_ers_result_t pcifront_common_process(int cmd,
|
|||
}
|
||||
pdrv = pcidev->driver;
|
||||
|
||||
if (get_driver(&pdrv->driver)) {
|
||||
if (pdrv) {
|
||||
if (pdrv->err_handler && pdrv->err_handler->error_detected) {
|
||||
dev_dbg(&pcidev->dev,
|
||||
"trying to call AER service\n");
|
||||
|
@ -623,7 +623,6 @@ static pci_ers_result_t pcifront_common_process(int cmd,
|
|||
}
|
||||
}
|
||||
}
|
||||
put_driver(&pdrv->driver);
|
||||
}
|
||||
if (!flag)
|
||||
result = PCI_ERS_RESULT_NONE;
|
||||
|
|
|
@ -127,10 +127,7 @@ pcmcia_store_new_id(struct device_driver *driver, const char *buf, size_t count)
|
|||
list_add_tail(&dynid->node, &pdrv->dynids.list);
|
||||
mutex_unlock(&pdrv->dynids.lock);
|
||||
|
||||
if (get_driver(&pdrv->drv)) {
|
||||
retval = driver_attach(&pdrv->drv);
|
||||
put_driver(&pdrv->drv);
|
||||
}
|
||||
retval = driver_attach(&pdrv->drv);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
@ -160,6 +157,11 @@ pcmcia_create_newid_file(struct pcmcia_driver *drv)
|
|||
return error;
|
||||
}
|
||||
|
||||
static void
|
||||
pcmcia_remove_newid_file(struct pcmcia_driver *drv)
|
||||
{
|
||||
driver_remove_file(&drv->drv, &driver_attr_new_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* pcmcia_register_driver - register a PCMCIA driver with the bus core
|
||||
|
@ -204,6 +206,7 @@ EXPORT_SYMBOL(pcmcia_register_driver);
|
|||
void pcmcia_unregister_driver(struct pcmcia_driver *driver)
|
||||
{
|
||||
pr_debug("unregistering driver %s\n", driver->name);
|
||||
pcmcia_remove_newid_file(driver);
|
||||
driver_unregister(&driver->drv);
|
||||
pcmcia_free_dynids(driver);
|
||||
}
|
||||
|
|
|
@ -76,6 +76,20 @@ config BATTERY_DS2780
|
|||
help
|
||||
Say Y here to enable support for batteries with ds2780 chip.
|
||||
|
||||
config BATTERY_DS2781
|
||||
tristate "2781 battery driver"
|
||||
depends on HAS_IOMEM
|
||||
select W1
|
||||
select W1_SLAVE_DS2781
|
||||
help
|
||||
If you enable this you will have the DS2781 battery driver support.
|
||||
|
||||
The battery monitor chip is used in many batteries/devices
|
||||
as the one who is responsible for charging/discharging/monitoring
|
||||
Li+ batteries.
|
||||
|
||||
If you are unsure, say N.
|
||||
|
||||
config BATTERY_DS2782
|
||||
tristate "DS2782/DS2786 standalone gas-gauge"
|
||||
depends on I2C
|
||||
|
|
|
@ -16,6 +16,7 @@ obj-$(CONFIG_TEST_POWER) += test_power.o
|
|||
|
||||
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o
|
||||
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
|
||||
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
|
||||
|
|
874
drivers/power/ds2781_battery.c
Normal file
874
drivers/power/ds2781_battery.c
Normal file
|
@ -0,0 +1,874 @@
|
|||
/*
|
||||
* 1-wire client/driver for the Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC
|
||||
*
|
||||
* Author: Renata Sayakhova <renata@oktetlabs.ru>
|
||||
*
|
||||
* Based on ds2780_battery drivers
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
#include "../w1/w1.h"
|
||||
#include "../w1/slaves/w1_ds2781.h"
|
||||
|
||||
/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
|
||||
#define DS2781_CURRENT_UNITS 1563
|
||||
/* Charge unit measurement in uAh for a 1 milli-ohm sense resistor */
|
||||
#define DS2781_CHARGE_UNITS 6250
|
||||
/* Number of bytes in user EEPROM space */
|
||||
#define DS2781_USER_EEPROM_SIZE (DS2781_EEPROM_BLOCK0_END - \
|
||||
DS2781_EEPROM_BLOCK0_START + 1)
|
||||
/* Number of bytes in parameter EEPROM space */
|
||||
#define DS2781_PARAM_EEPROM_SIZE (DS2781_EEPROM_BLOCK1_END - \
|
||||
DS2781_EEPROM_BLOCK1_START + 1)
|
||||
|
||||
struct ds2781_device_info {
|
||||
struct device *dev;
|
||||
struct power_supply bat;
|
||||
struct device *w1_dev;
|
||||
struct task_struct *mutex_holder;
|
||||
};
|
||||
|
||||
enum current_types {
|
||||
CURRENT_NOW,
|
||||
CURRENT_AVG,
|
||||
};
|
||||
|
||||
static const char model[] = "DS2781";
|
||||
static const char manufacturer[] = "Maxim/Dallas";
|
||||
|
||||
static inline struct ds2781_device_info *
|
||||
to_ds2781_device_info(struct power_supply *psy)
|
||||
{
|
||||
return container_of(psy, struct ds2781_device_info, bat);
|
||||
}
|
||||
|
||||
static inline struct power_supply *to_power_supply(struct device *dev)
|
||||
{
|
||||
return dev_get_drvdata(dev);
|
||||
}
|
||||
|
||||
static inline int ds2781_battery_io(struct ds2781_device_info *dev_info,
|
||||
char *buf, int addr, size_t count, int io)
|
||||
{
|
||||
if (dev_info->mutex_holder == current)
|
||||
return w1_ds2781_io_nolock(dev_info->w1_dev, buf, addr,
|
||||
count, io);
|
||||
else
|
||||
return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io);
|
||||
}
|
||||
|
||||
int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf,
|
||||
int addr, size_t count)
|
||||
{
|
||||
return ds2781_battery_io(dev_info, buf, addr, count, 0);
|
||||
}
|
||||
|
||||
static inline int ds2781_read8(struct ds2781_device_info *dev_info, u8 *val,
|
||||
int addr)
|
||||
{
|
||||
return ds2781_battery_io(dev_info, val, addr, sizeof(u8), 0);
|
||||
}
|
||||
|
||||
static int ds2781_read16(struct ds2781_device_info *dev_info, s16 *val,
|
||||
int addr)
|
||||
{
|
||||
int ret;
|
||||
u8 raw[2];
|
||||
|
||||
ret = ds2781_battery_io(dev_info, raw, addr, sizeof(raw), 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = (raw[0] << 8) | raw[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ds2781_read_block(struct ds2781_device_info *dev_info,
|
||||
u8 *val, int addr, size_t count)
|
||||
{
|
||||
return ds2781_battery_io(dev_info, val, addr, count, 0);
|
||||
}
|
||||
|
||||
static inline int ds2781_write(struct ds2781_device_info *dev_info, u8 *val,
|
||||
int addr, size_t count)
|
||||
{
|
||||
return ds2781_battery_io(dev_info, val, addr, count, 1);
|
||||
}
|
||||
|
||||
static inline int ds2781_store_eeprom(struct device *dev, int addr)
|
||||
{
|
||||
return w1_ds2781_eeprom_cmd(dev, addr, W1_DS2781_COPY_DATA);
|
||||
}
|
||||
|
||||
static inline int ds2781_recall_eeprom(struct device *dev, int addr)
|
||||
{
|
||||
return w1_ds2781_eeprom_cmd(dev, addr, W1_DS2781_RECALL_DATA);
|
||||
}
|
||||
|
||||
static int ds2781_save_eeprom(struct ds2781_device_info *dev_info, int reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ds2781_store_eeprom(dev_info->w1_dev, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ds2781_recall_eeprom(dev_info->w1_dev, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set sense resistor value in mhos */
|
||||
static int ds2781_set_sense_register(struct ds2781_device_info *dev_info,
|
||||
u8 conductance)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ds2781_write(dev_info, &conductance,
|
||||
DS2781_RSNSP, sizeof(u8));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ds2781_save_eeprom(dev_info, DS2781_RSNSP);
|
||||
}
|
||||
|
||||
/* Get RSGAIN value from 0 to 1.999 in steps of 0.001 */
|
||||
static int ds2781_get_rsgain_register(struct ds2781_device_info *dev_info,
|
||||
u16 *rsgain)
|
||||
{
|
||||
return ds2781_read16(dev_info, rsgain, DS2781_RSGAIN_MSB);
|
||||
}
|
||||
|
||||
/* Set RSGAIN value from 0 to 1.999 in steps of 0.001 */
|
||||
static int ds2781_set_rsgain_register(struct ds2781_device_info *dev_info,
|
||||
u16 rsgain)
|
||||
{
|
||||
int ret;
|
||||
u8 raw[] = {rsgain >> 8, rsgain & 0xFF};
|
||||
|
||||
ret = ds2781_write(dev_info, raw,
|
||||
DS2781_RSGAIN_MSB, sizeof(raw));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ds2781_save_eeprom(dev_info, DS2781_RSGAIN_MSB);
|
||||
}
|
||||
|
||||
static int ds2781_get_voltage(struct ds2781_device_info *dev_info,
|
||||
int *voltage_uV)
|
||||
{
|
||||
int ret;
|
||||
char val[2];
|
||||
int voltage_raw;
|
||||
|
||||
ret = w1_ds2781_read(dev_info, val, DS2781_VOLT_MSB, 2 * sizeof(u8));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* The voltage value is located in 10 bits across the voltage MSB
|
||||
* and LSB registers in two's compliment form
|
||||
* Sign bit of the voltage value is in bit 7 of the voltage MSB register
|
||||
* Bits 9 - 3 of the voltage value are in bits 6 - 0 of the
|
||||
* voltage MSB register
|
||||
* Bits 2 - 0 of the voltage value are in bits 7 - 5 of the
|
||||
* voltage LSB register
|
||||
*/
|
||||
voltage_raw = (val[0] << 3) |
|
||||
(val[1] >> 5);
|
||||
|
||||
/* DS2781 reports voltage in units of 9.76mV, but the battery class
|
||||
* reports in units of uV, so convert by multiplying by 9760. */
|
||||
*voltage_uV = voltage_raw * 9760;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds2781_get_temperature(struct ds2781_device_info *dev_info,
|
||||
int *temp)
|
||||
{
|
||||
int ret;
|
||||
char val[2];
|
||||
int temp_raw;
|
||||
|
||||
ret = w1_ds2781_read(dev_info, val, DS2781_TEMP_MSB, 2 * sizeof(u8));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* The temperature value is located in 10 bits across the temperature
|
||||
* MSB and LSB registers in two's compliment form
|
||||
* Sign bit of the temperature value is in bit 7 of the temperature
|
||||
* MSB register
|
||||
* Bits 9 - 3 of the temperature value are in bits 6 - 0 of the
|
||||
* temperature MSB register
|
||||
* Bits 2 - 0 of the temperature value are in bits 7 - 5 of the
|
||||
* temperature LSB register
|
||||
*/
|
||||
temp_raw = ((val[0]) << 3) |
|
||||
(val[1] >> 5);
|
||||
*temp = temp_raw + (temp_raw / 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds2781_get_current(struct ds2781_device_info *dev_info,
|
||||
enum current_types type, int *current_uA)
|
||||
{
|
||||
int ret, sense_res;
|
||||
s16 current_raw;
|
||||
u8 sense_res_raw, reg_msb;
|
||||
|
||||
/*
|
||||
* The units of measurement for current are dependent on the value of
|
||||
* the sense resistor.
|
||||
*/
|
||||
ret = ds2781_read8(dev_info, &sense_res_raw, DS2781_RSNSP);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (sense_res_raw == 0) {
|
||||
dev_err(dev_info->dev, "sense resistor value is 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
sense_res = 1000 / sense_res_raw;
|
||||
|
||||
if (type == CURRENT_NOW)
|
||||
reg_msb = DS2781_CURRENT_MSB;
|
||||
else if (type == CURRENT_AVG)
|
||||
reg_msb = DS2781_IAVG_MSB;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The current value is located in 16 bits across the current MSB
|
||||
* and LSB registers in two's compliment form
|
||||
* Sign bit of the current value is in bit 7 of the current MSB register
|
||||
* Bits 14 - 8 of the current value are in bits 6 - 0 of the current
|
||||
* MSB register
|
||||
* Bits 7 - 0 of the current value are in bits 7 - 0 of the current
|
||||
* LSB register
|
||||
*/
|
||||
ret = ds2781_read16(dev_info, ¤t_raw, reg_msb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*current_uA = current_raw * (DS2781_CURRENT_UNITS / sense_res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds2781_get_accumulated_current(struct ds2781_device_info *dev_info,
|
||||
int *accumulated_current)
|
||||
{
|
||||
int ret, sense_res;
|
||||
s16 current_raw;
|
||||
u8 sense_res_raw;
|
||||
|
||||
/*
|
||||
* The units of measurement for accumulated current are dependent on
|
||||
* the value of the sense resistor.
|
||||
*/
|
||||
ret = ds2781_read8(dev_info, &sense_res_raw, DS2781_RSNSP);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (sense_res_raw == 0) {
|
||||
dev_err(dev_info->dev, "sense resistor value is 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
sense_res = 1000 / sense_res_raw;
|
||||
|
||||
/*
|
||||
* The ACR value is located in 16 bits across the ACR MSB and
|
||||
* LSB registers
|
||||
* Bits 15 - 8 of the ACR value are in bits 7 - 0 of the ACR
|
||||
* MSB register
|
||||
* Bits 7 - 0 of the ACR value are in bits 7 - 0 of the ACR
|
||||
* LSB register
|
||||
*/
|
||||
ret = ds2781_read16(dev_info, ¤t_raw, DS2781_ACR_MSB);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*accumulated_current = current_raw * (DS2781_CHARGE_UNITS / sense_res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds2781_get_capacity(struct ds2781_device_info *dev_info,
|
||||
int *capacity)
|
||||
{
|
||||
int ret;
|
||||
u8 raw;
|
||||
|
||||
ret = ds2781_read8(dev_info, &raw, DS2781_RARC);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*capacity = raw;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds2781_get_status(struct ds2781_device_info *dev_info, int *status)
|
||||
{
|
||||
int ret, current_uA, capacity;
|
||||
|
||||
ret = ds2781_get_current(dev_info, CURRENT_NOW, ¤t_uA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ds2781_get_capacity(dev_info, &capacity);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (power_supply_am_i_supplied(&dev_info->bat)) {
|
||||
if (capacity == 100)
|
||||
*status = POWER_SUPPLY_STATUS_FULL;
|
||||
else if (current_uA > 50000)
|
||||
*status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
else
|
||||
*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
} else {
|
||||
*status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds2781_get_charge_now(struct ds2781_device_info *dev_info,
|
||||
int *charge_now)
|
||||
{
|
||||
int ret;
|
||||
u16 charge_raw;
|
||||
|
||||
/*
|
||||
* The RAAC value is located in 16 bits across the RAAC MSB and
|
||||
* LSB registers
|
||||
* Bits 15 - 8 of the RAAC value are in bits 7 - 0 of the RAAC
|
||||
* MSB register
|
||||
* Bits 7 - 0 of the RAAC value are in bits 7 - 0 of the RAAC
|
||||
* LSB register
|
||||
*/
|
||||
ret = ds2781_read16(dev_info, &charge_raw, DS2781_RAAC_MSB);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*charge_now = charge_raw * 1600;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds2781_get_control_register(struct ds2781_device_info *dev_info,
|
||||
u8 *control_reg)
|
||||
{
|
||||
return ds2781_read8(dev_info, control_reg, DS2781_CONTROL);
|
||||
}
|
||||
|
||||
static int ds2781_set_control_register(struct ds2781_device_info *dev_info,
|
||||
u8 control_reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ds2781_write(dev_info, &control_reg,
|
||||
DS2781_CONTROL, sizeof(u8));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ds2781_save_eeprom(dev_info, DS2781_CONTROL);
|
||||
}
|
||||
|
||||
static int ds2781_battery_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
ret = ds2781_get_voltage(dev_info, &val->intval);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
ret = ds2781_get_temperature(dev_info, &val->intval);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = model;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||
val->strval = manufacturer;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
ret = ds2781_get_current(dev_info, CURRENT_NOW, &val->intval);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CURRENT_AVG:
|
||||
ret = ds2781_get_current(dev_info, CURRENT_AVG, &val->intval);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
ret = ds2781_get_status(dev_info, &val->intval);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
ret = ds2781_get_capacity(dev_info, &val->intval);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
|
||||
ret = ds2781_get_accumulated_current(dev_info, &val->intval);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
ret = ds2781_get_charge_now(dev_info, &val->intval);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum power_supply_property ds2781_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_AVG,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CHARGE_COUNTER,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
};
|
||||
|
||||
static ssize_t ds2781_get_pmod_enabled(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 control_reg;
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
/* Get power mode */
|
||||
ret = ds2781_get_control_register(dev_info, &control_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
!!(control_reg & DS2781_CONTROL_PMOD));
|
||||
}
|
||||
|
||||
static ssize_t ds2781_set_pmod_enabled(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int ret;
|
||||
u8 control_reg, new_setting;
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
/* Set power mode */
|
||||
ret = ds2781_get_control_register(dev_info, &control_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = kstrtou8(buf, 0, &new_setting);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((new_setting != 0) && (new_setting != 1)) {
|
||||
dev_err(dev_info->dev, "Invalid pmod setting (0 or 1)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (new_setting)
|
||||
control_reg |= DS2781_CONTROL_PMOD;
|
||||
else
|
||||
control_reg &= ~DS2781_CONTROL_PMOD;
|
||||
|
||||
ret = ds2781_set_control_register(dev_info, control_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ds2781_get_sense_resistor_value(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 sense_resistor;
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
ret = ds2781_read8(dev_info, &sense_resistor, DS2781_RSNSP);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sprintf(buf, "%d\n", sense_resistor);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ds2781_set_sense_resistor_value(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int ret;
|
||||
u8 new_setting;
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
ret = kstrtou8(buf, 0, &new_setting);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ds2781_set_sense_register(dev_info, new_setting);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ds2781_get_rsgain_setting(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u16 rsgain;
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
ret = ds2781_get_rsgain_register(dev_info, &rsgain);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", rsgain);
|
||||
}
|
||||
|
||||
static ssize_t ds2781_set_rsgain_setting(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int ret;
|
||||
u16 new_setting;
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
ret = kstrtou16(buf, 0, &new_setting);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Gain can only be from 0 to 1.999 in steps of .001 */
|
||||
if (new_setting > 1999) {
|
||||
dev_err(dev_info->dev, "Invalid rsgain setting (0 - 1999)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = ds2781_set_rsgain_register(dev_info, new_setting);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ds2781_get_pio_pin(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 sfr;
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
ret = ds2781_read8(dev_info, &sfr, DS2781_SFR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sprintf(buf, "%d\n", sfr & DS2781_SFR_PIOSC);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ds2781_set_pio_pin(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int ret;
|
||||
u8 new_setting;
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
ret = kstrtou8(buf, 0, &new_setting);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((new_setting != 0) && (new_setting != 1)) {
|
||||
dev_err(dev_info->dev, "Invalid pio_pin setting (0 or 1)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = ds2781_write(dev_info, &new_setting,
|
||||
DS2781_SFR, sizeof(u8));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
|
||||
struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
count = min_t(loff_t, count,
|
||||
DS2781_EEPROM_BLOCK1_END -
|
||||
DS2781_EEPROM_BLOCK1_START + 1 - off);
|
||||
|
||||
return ds2781_read_block(dev_info, buf,
|
||||
DS2781_EEPROM_BLOCK1_START + off, count);
|
||||
}
|
||||
|
||||
static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
|
||||
struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
int ret;
|
||||
|
||||
count = min_t(loff_t, count,
|
||||
DS2781_EEPROM_BLOCK1_END -
|
||||
DS2781_EEPROM_BLOCK1_START + 1 - off);
|
||||
|
||||
ret = ds2781_write(dev_info, buf,
|
||||
DS2781_EEPROM_BLOCK1_START + off, count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ds2781_save_eeprom(dev_info, DS2781_EEPROM_BLOCK1_START);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct bin_attribute ds2781_param_eeprom_bin_attr = {
|
||||
.attr = {
|
||||
.name = "param_eeprom",
|
||||
.mode = S_IRUGO | S_IWUSR,
|
||||
},
|
||||
.size = DS2781_EEPROM_BLOCK1_END - DS2781_EEPROM_BLOCK1_START + 1,
|
||||
.read = ds2781_read_param_eeprom_bin,
|
||||
.write = ds2781_write_param_eeprom_bin,
|
||||
};
|
||||
|
||||
static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
|
||||
struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
|
||||
count = min_t(loff_t, count,
|
||||
DS2781_EEPROM_BLOCK0_END -
|
||||
DS2781_EEPROM_BLOCK0_START + 1 - off);
|
||||
|
||||
return ds2781_read_block(dev_info, buf,
|
||||
DS2781_EEPROM_BLOCK0_START + off, count);
|
||||
|
||||
}
|
||||
|
||||
static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
|
||||
struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
|
||||
int ret;
|
||||
|
||||
count = min_t(loff_t, count,
|
||||
DS2781_EEPROM_BLOCK0_END -
|
||||
DS2781_EEPROM_BLOCK0_START + 1 - off);
|
||||
|
||||
ret = ds2781_write(dev_info, buf,
|
||||
DS2781_EEPROM_BLOCK0_START + off, count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ds2781_save_eeprom(dev_info, DS2781_EEPROM_BLOCK0_START);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct bin_attribute ds2781_user_eeprom_bin_attr = {
|
||||
.attr = {
|
||||
.name = "user_eeprom",
|
||||
.mode = S_IRUGO | S_IWUSR,
|
||||
},
|
||||
.size = DS2781_EEPROM_BLOCK0_END - DS2781_EEPROM_BLOCK0_START + 1,
|
||||
.read = ds2781_read_user_eeprom_bin,
|
||||
.write = ds2781_write_user_eeprom_bin,
|
||||
};
|
||||
|
||||
static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2781_get_pmod_enabled,
|
||||
ds2781_set_pmod_enabled);
|
||||
static DEVICE_ATTR(sense_resistor_value, S_IRUGO | S_IWUSR,
|
||||
ds2781_get_sense_resistor_value, ds2781_set_sense_resistor_value);
|
||||
static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2781_get_rsgain_setting,
|
||||
ds2781_set_rsgain_setting);
|
||||
static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2781_get_pio_pin,
|
||||
ds2781_set_pio_pin);
|
||||
|
||||
|
||||
static struct attribute *ds2781_attributes[] = {
|
||||
&dev_attr_pmod_enabled.attr,
|
||||
&dev_attr_sense_resistor_value.attr,
|
||||
&dev_attr_rsgain_setting.attr,
|
||||
&dev_attr_pio_pin.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ds2781_attr_group = {
|
||||
.attrs = ds2781_attributes,
|
||||
};
|
||||
|
||||
static int __devinit ds2781_battery_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ds2781_device_info *dev_info;
|
||||
|
||||
dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
|
||||
if (!dev_info) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, dev_info);
|
||||
|
||||
dev_info->dev = &pdev->dev;
|
||||
dev_info->w1_dev = pdev->dev.parent;
|
||||
dev_info->bat.name = dev_name(&pdev->dev);
|
||||
dev_info->bat.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
dev_info->bat.properties = ds2781_battery_props;
|
||||
dev_info->bat.num_properties = ARRAY_SIZE(ds2781_battery_props);
|
||||
dev_info->bat.get_property = ds2781_battery_get_property;
|
||||
dev_info->mutex_holder = current;
|
||||
|
||||
ret = power_supply_register(&pdev->dev, &dev_info->bat);
|
||||
if (ret) {
|
||||
dev_err(dev_info->dev, "failed to register battery\n");
|
||||
goto fail_free_info;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2781_attr_group);
|
||||
if (ret) {
|
||||
dev_err(dev_info->dev, "failed to create sysfs group\n");
|
||||
goto fail_unregister;
|
||||
}
|
||||
|
||||
ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj,
|
||||
&ds2781_param_eeprom_bin_attr);
|
||||
if (ret) {
|
||||
dev_err(dev_info->dev,
|
||||
"failed to create param eeprom bin file");
|
||||
goto fail_remove_group;
|
||||
}
|
||||
|
||||
ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj,
|
||||
&ds2781_user_eeprom_bin_attr);
|
||||
if (ret) {
|
||||
dev_err(dev_info->dev,
|
||||
"failed to create user eeprom bin file");
|
||||
goto fail_remove_bin_file;
|
||||
}
|
||||
|
||||
dev_info->mutex_holder = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_remove_bin_file:
|
||||
sysfs_remove_bin_file(&dev_info->bat.dev->kobj,
|
||||
&ds2781_param_eeprom_bin_attr);
|
||||
fail_remove_group:
|
||||
sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group);
|
||||
fail_unregister:
|
||||
power_supply_unregister(&dev_info->bat);
|
||||
fail_free_info:
|
||||
kfree(dev_info);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ds2781_battery_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ds2781_device_info *dev_info = platform_get_drvdata(pdev);
|
||||
|
||||
dev_info->mutex_holder = current;
|
||||
|
||||
/* remove attributes */
|
||||
sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group);
|
||||
|
||||
power_supply_unregister(&dev_info->bat);
|
||||
|
||||
kfree(dev_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ds2781_battery_driver = {
|
||||
.driver = {
|
||||
.name = "ds2781-battery",
|
||||
},
|
||||
.probe = ds2781_battery_probe,
|
||||
.remove = __devexit_p(ds2781_battery_remove),
|
||||
};
|
||||
|
||||
static int __init ds2781_battery_init(void)
|
||||
{
|
||||
return platform_driver_register(&ds2781_battery_driver);
|
||||
}
|
||||
|
||||
static void __exit ds2781_battery_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ds2781_battery_driver);
|
||||
}
|
||||
|
||||
module_init(ds2781_battery_init);
|
||||
module_exit(ds2781_battery_exit);
|
||||
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>");
|
||||
MODULE_DESCRIPTION("Maxim/Dallas DS2781 Stand-Alone Fuel Gauage IC driver");
|
||||
MODULE_ALIAS("platform:ds2781-battery");
|
||||
|
|
@ -1210,7 +1210,7 @@ static struct regulator *_regulator_get(struct device *dev, const char *id,
|
|||
{
|
||||
struct regulator_dev *rdev;
|
||||
struct regulator_map *map;
|
||||
struct regulator *regulator = ERR_PTR(-ENODEV);
|
||||
struct regulator *regulator = ERR_PTR(-EPROBE_DEFER);
|
||||
const char *devname = NULL;
|
||||
int ret;
|
||||
|
||||
|
@ -2834,7 +2834,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
|
|||
|
||||
if (!r) {
|
||||
dev_err(dev, "Failed to find supply %s\n", supply);
|
||||
ret = -ENODEV;
|
||||
ret = -EPROBE_DEFER;
|
||||
goto scrub;
|
||||
}
|
||||
|
||||
|
|
|
@ -580,7 +580,6 @@ void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver)
|
|||
struct device *dev;
|
||||
|
||||
/* We don't want ccwgroup devices to live longer than their driver. */
|
||||
get_driver(&cdriver->driver);
|
||||
while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
|
||||
__ccwgroup_match_all))) {
|
||||
struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
|
||||
|
@ -592,7 +591,6 @@ void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver)
|
|||
mutex_unlock(&gdev->reg_mutex);
|
||||
put_device(dev);
|
||||
}
|
||||
put_driver(&cdriver->driver);
|
||||
driver_unregister(&cdriver->driver);
|
||||
}
|
||||
EXPORT_SYMBOL(ccwgroup_driver_unregister);
|
||||
|
|
|
@ -1676,15 +1676,9 @@ struct ccw_device *get_ccwdev_by_busid(struct ccw_driver *cdrv,
|
|||
const char *bus_id)
|
||||
{
|
||||
struct device *dev;
|
||||
struct device_driver *drv;
|
||||
|
||||
drv = get_driver(&cdrv->driver);
|
||||
if (!drv)
|
||||
return NULL;
|
||||
|
||||
dev = driver_find_device(drv, NULL, (void *)bus_id,
|
||||
dev = driver_find_device(&cdrv->driver, NULL, (void *)bus_id,
|
||||
__ccwdev_check_busid);
|
||||
put_driver(drv);
|
||||
|
||||
return dev ? to_ccwdev(dev) : NULL;
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ static int __init smsgiucv_app_init(void)
|
|||
rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT);
|
||||
if (rc) {
|
||||
kfree(smsg_app_dev);
|
||||
goto fail_put_driver;
|
||||
goto fail;
|
||||
}
|
||||
smsg_app_dev->bus = &iucv_bus;
|
||||
smsg_app_dev->parent = iucv_root;
|
||||
|
@ -177,7 +177,7 @@ static int __init smsgiucv_app_init(void)
|
|||
rc = device_register(smsg_app_dev);
|
||||
if (rc) {
|
||||
put_device(smsg_app_dev);
|
||||
goto fail_put_driver;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* convert sender to uppercase characters */
|
||||
|
@ -191,12 +191,11 @@ static int __init smsgiucv_app_init(void)
|
|||
rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
|
||||
if (rc) {
|
||||
device_unregister(smsg_app_dev);
|
||||
goto fail_put_driver;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
fail_put_driver:
|
||||
put_driver(smsgiucv_drv);
|
||||
fail:
|
||||
return rc;
|
||||
}
|
||||
module_init(smsgiucv_app_init);
|
||||
|
|
|
@ -140,19 +140,6 @@ static void ssb_device_put(struct ssb_device *dev)
|
|||
put_device(dev->dev);
|
||||
}
|
||||
|
||||
static inline struct ssb_driver *ssb_driver_get(struct ssb_driver *drv)
|
||||
{
|
||||
if (drv)
|
||||
get_driver(&drv->drv);
|
||||
return drv;
|
||||
}
|
||||
|
||||
static inline void ssb_driver_put(struct ssb_driver *drv)
|
||||
{
|
||||
if (drv)
|
||||
put_driver(&drv->drv);
|
||||
}
|
||||
|
||||
static int ssb_device_resume(struct device *dev)
|
||||
{
|
||||
struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
|
||||
|
@ -250,11 +237,9 @@ int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx)
|
|||
ssb_device_put(sdev);
|
||||
continue;
|
||||
}
|
||||
sdrv = ssb_driver_get(drv_to_ssb_drv(sdev->dev->driver));
|
||||
if (!sdrv || SSB_WARN_ON(!sdrv->remove)) {
|
||||
ssb_device_put(sdev);
|
||||
sdrv = drv_to_ssb_drv(sdev->dev->driver);
|
||||
if (SSB_WARN_ON(!sdrv->remove))
|
||||
continue;
|
||||
}
|
||||
sdrv->remove(sdev);
|
||||
ctx->device_frozen[i] = 1;
|
||||
}
|
||||
|
@ -293,7 +278,6 @@ int ssb_devices_thaw(struct ssb_freeze_context *ctx)
|
|||
dev_name(sdev->dev));
|
||||
result = err;
|
||||
}
|
||||
ssb_driver_put(sdrv);
|
||||
ssb_device_put(sdev);
|
||||
}
|
||||
|
||||
|
|
|
@ -71,10 +71,7 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
|
|||
list_add_tail(&dynid->node, &dynids->list);
|
||||
spin_unlock(&dynids->lock);
|
||||
|
||||
if (get_driver(driver)) {
|
||||
retval = driver_attach(driver);
|
||||
put_driver(driver);
|
||||
}
|
||||
retval = driver_attach(driver);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
@ -132,43 +129,39 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
|
|||
}
|
||||
static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
|
||||
|
||||
static int usb_create_newid_file(struct usb_driver *usb_drv)
|
||||
static int usb_create_newid_files(struct usb_driver *usb_drv)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (usb_drv->no_dynamic_id)
|
||||
goto exit;
|
||||
|
||||
if (usb_drv->probe != NULL)
|
||||
if (usb_drv->probe != NULL) {
|
||||
error = driver_create_file(&usb_drv->drvwrap.driver,
|
||||
&driver_attr_new_id);
|
||||
if (error == 0) {
|
||||
error = driver_create_file(&usb_drv->drvwrap.driver,
|
||||
&driver_attr_remove_id);
|
||||
if (error)
|
||||
driver_remove_file(&usb_drv->drvwrap.driver,
|
||||
&driver_attr_new_id);
|
||||
}
|
||||
}
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
static void usb_remove_newid_file(struct usb_driver *usb_drv)
|
||||
static void usb_remove_newid_files(struct usb_driver *usb_drv)
|
||||
{
|
||||
if (usb_drv->no_dynamic_id)
|
||||
return;
|
||||
|
||||
if (usb_drv->probe != NULL)
|
||||
if (usb_drv->probe != NULL) {
|
||||
driver_remove_file(&usb_drv->drvwrap.driver,
|
||||
&driver_attr_remove_id);
|
||||
driver_remove_file(&usb_drv->drvwrap.driver,
|
||||
&driver_attr_new_id);
|
||||
}
|
||||
|
||||
static int
|
||||
usb_create_removeid_file(struct usb_driver *drv)
|
||||
{
|
||||
int error = 0;
|
||||
if (drv->probe != NULL)
|
||||
error = driver_create_file(&drv->drvwrap.driver,
|
||||
&driver_attr_remove_id);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void usb_remove_removeid_file(struct usb_driver *drv)
|
||||
{
|
||||
driver_remove_file(&drv->drvwrap.driver, &driver_attr_remove_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_free_dynids(struct usb_driver *usb_drv)
|
||||
|
@ -183,22 +176,12 @@ static void usb_free_dynids(struct usb_driver *usb_drv)
|
|||
spin_unlock(&usb_drv->dynids.lock);
|
||||
}
|
||||
#else
|
||||
static inline int usb_create_newid_file(struct usb_driver *usb_drv)
|
||||
static inline int usb_create_newid_files(struct usb_driver *usb_drv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_remove_newid_file(struct usb_driver *usb_drv)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
usb_create_removeid_file(struct usb_driver *drv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_remove_removeid_file(struct usb_driver *drv)
|
||||
static void usb_remove_newid_files(struct usb_driver *usb_drv)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -875,22 +858,16 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
|
|||
|
||||
usbfs_update_special();
|
||||
|
||||
retval = usb_create_newid_file(new_driver);
|
||||
retval = usb_create_newid_files(new_driver);
|
||||
if (retval)
|
||||
goto out_newid;
|
||||
|
||||
retval = usb_create_removeid_file(new_driver);
|
||||
if (retval)
|
||||
goto out_removeid;
|
||||
|
||||
pr_info("%s: registered new interface driver %s\n",
|
||||
usbcore_name, new_driver->name);
|
||||
|
||||
out:
|
||||
return retval;
|
||||
|
||||
out_removeid:
|
||||
usb_remove_newid_file(new_driver);
|
||||
out_newid:
|
||||
driver_unregister(&new_driver->drvwrap.driver);
|
||||
|
||||
|
@ -917,10 +894,9 @@ void usb_deregister(struct usb_driver *driver)
|
|||
pr_info("%s: deregistering interface driver %s\n",
|
||||
usbcore_name, driver->name);
|
||||
|
||||
usb_remove_removeid_file(driver);
|
||||
usb_remove_newid_file(driver);
|
||||
usb_free_dynids(driver);
|
||||
usb_remove_newid_files(driver);
|
||||
driver_unregister(&driver->drvwrap.driver);
|
||||
usb_free_dynids(driver);
|
||||
|
||||
usbfs_update_special();
|
||||
}
|
||||
|
|
|
@ -171,14 +171,4 @@ MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
|||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer");
|
||||
|
||||
static int __devinit dwc3_pci_init(void)
|
||||
{
|
||||
return pci_register_driver(&dwc3_pci_driver);
|
||||
}
|
||||
module_init(dwc3_pci_init);
|
||||
|
||||
static void __exit dwc3_pci_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&dwc3_pci_driver);
|
||||
}
|
||||
module_exit(dwc3_pci_exit);
|
||||
module_pci_driver(dwc3_pci_driver);
|
||||
|
|
|
@ -13,12 +13,11 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/w1-gpio.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include "../w1.h"
|
||||
#include "../w1_int.h"
|
||||
|
||||
#include <asm/gpio.h>
|
||||
|
||||
static void w1_gpio_write_bit_dir(void *data, u8 bit)
|
||||
{
|
||||
struct w1_gpio_platform_data *pdata = data;
|
||||
|
|
|
@ -81,6 +81,19 @@ config W1_SLAVE_DS2780
|
|||
|
||||
If you are unsure, say N.
|
||||
|
||||
config W1_SLAVE_DS2781
|
||||
tristate "Dallas 2781 battery monitor chip"
|
||||
depends on W1
|
||||
help
|
||||
If you enable this you will have the DS2781 battery monitor
|
||||
chip support.
|
||||
|
||||
The battery monitor chip is used in many batteries/devices
|
||||
as the one who is responsible for charging/discharging/monitoring
|
||||
Li+ batteries.
|
||||
|
||||
If you are unsure, say N.
|
||||
|
||||
config W1_SLAVE_BQ27000
|
||||
tristate "BQ27000 slave support"
|
||||
depends on W1
|
||||
|
|
|
@ -10,4 +10,5 @@ obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o
|
|||
obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o
|
||||
obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o
|
||||
obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o
|
||||
obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o
|
||||
obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/power/bq27x00_battery.h>
|
||||
|
||||
#include "../w1.h"
|
||||
#include "../w1_int.h"
|
||||
|
@ -25,46 +26,37 @@
|
|||
|
||||
static int F_ID;
|
||||
|
||||
void w1_bq27000_write(struct device *dev, u8 buf, u8 reg)
|
||||
{
|
||||
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
|
||||
|
||||
if (!dev) {
|
||||
pr_info("Could not obtain slave dev ptr\n");
|
||||
return;
|
||||
}
|
||||
|
||||
w1_write_8(sl->master, HDQ_CMD_WRITE | reg);
|
||||
w1_write_8(sl->master, buf);
|
||||
}
|
||||
EXPORT_SYMBOL(w1_bq27000_write);
|
||||
|
||||
int w1_bq27000_read(struct device *dev, u8 reg)
|
||||
static int w1_bq27000_read(struct device *dev, unsigned int reg)
|
||||
{
|
||||
u8 val;
|
||||
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
|
||||
|
||||
if (!dev)
|
||||
return 0;
|
||||
struct w1_slave *sl = container_of(dev->parent, struct w1_slave, dev);
|
||||
|
||||
mutex_lock(&sl->master->mutex);
|
||||
w1_write_8(sl->master, HDQ_CMD_READ | reg);
|
||||
val = w1_read_8(sl->master);
|
||||
mutex_unlock(&sl->master->mutex);
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL(w1_bq27000_read);
|
||||
|
||||
static struct bq27000_platform_data bq27000_battery_info = {
|
||||
.read = w1_bq27000_read,
|
||||
.name = "bq27000-battery",
|
||||
};
|
||||
|
||||
static int w1_bq27000_add_slave(struct w1_slave *sl)
|
||||
{
|
||||
int ret;
|
||||
int id = 1;
|
||||
struct platform_device *pdev;
|
||||
|
||||
pdev = platform_device_alloc("bq27000-battery", id);
|
||||
pdev = platform_device_alloc("bq27000-battery", -1);
|
||||
if (!pdev) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
ret = platform_device_add_data(pdev,
|
||||
&bq27000_battery_info,
|
||||
sizeof(bq27000_battery_info));
|
||||
pdev->dev.parent = &sl->dev;
|
||||
|
||||
ret = platform_device_add(pdev);
|
||||
|
|
201
drivers/w1/slaves/w1_ds2781.c
Normal file
201
drivers/w1/slaves/w1_ds2781.c
Normal file
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* 1-Wire implementation for the ds2781 chip
|
||||
*
|
||||
* Author: Renata Sayakhova <renata@oktetlabs.ru>
|
||||
*
|
||||
* Based on w1-ds2780 driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
#include "../w1.h"
|
||||
#include "../w1_int.h"
|
||||
#include "../w1_family.h"
|
||||
#include "w1_ds2781.h"
|
||||
|
||||
static int w1_ds2781_do_io(struct device *dev, char *buf, int addr,
|
||||
size_t count, int io)
|
||||
{
|
||||
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
|
||||
|
||||
if (addr > DS2781_DATA_SIZE || addr < 0)
|
||||
return 0;
|
||||
|
||||
count = min_t(int, count, DS2781_DATA_SIZE - addr);
|
||||
|
||||
if (w1_reset_select_slave(sl) == 0) {
|
||||
if (io) {
|
||||
w1_write_8(sl->master, W1_DS2781_WRITE_DATA);
|
||||
w1_write_8(sl->master, addr);
|
||||
w1_write_block(sl->master, buf, count);
|
||||
} else {
|
||||
w1_write_8(sl->master, W1_DS2781_READ_DATA);
|
||||
w1_write_8(sl->master, addr);
|
||||
count = w1_read_block(sl->master, buf, count);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count,
|
||||
int io)
|
||||
{
|
||||
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&sl->master->mutex);
|
||||
|
||||
ret = w1_ds2781_do_io(dev, buf, addr, count, io);
|
||||
|
||||
mutex_unlock(&sl->master->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(w1_ds2781_io);
|
||||
|
||||
int w1_ds2781_io_nolock(struct device *dev, char *buf, int addr, size_t count,
|
||||
int io)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
ret = w1_ds2781_do_io(dev, buf, addr, count, io);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(w1_ds2781_io_nolock);
|
||||
|
||||
int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd)
|
||||
{
|
||||
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&sl->master->mutex);
|
||||
|
||||
if (w1_reset_select_slave(sl) == 0) {
|
||||
w1_write_8(sl->master, cmd);
|
||||
w1_write_8(sl->master, addr);
|
||||
}
|
||||
|
||||
mutex_unlock(&sl->master->mutex);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(w1_ds2781_eeprom_cmd);
|
||||
|
||||
static ssize_t w1_ds2781_read_bin(struct file *filp,
|
||||
struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
return w1_ds2781_io(dev, buf, off, count, 0);
|
||||
}
|
||||
|
||||
static struct bin_attribute w1_ds2781_bin_attr = {
|
||||
.attr = {
|
||||
.name = "w1_slave",
|
||||
.mode = S_IRUGO,
|
||||
},
|
||||
.size = DS2781_DATA_SIZE,
|
||||
.read = w1_ds2781_read_bin,
|
||||
};
|
||||
|
||||
static DEFINE_IDA(bat_ida);
|
||||
|
||||
static int w1_ds2781_add_slave(struct w1_slave *sl)
|
||||
{
|
||||
int ret;
|
||||
int id;
|
||||
struct platform_device *pdev;
|
||||
|
||||
id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL);
|
||||
if (id < 0) {
|
||||
ret = id;
|
||||
goto noid;
|
||||
}
|
||||
|
||||
pdev = platform_device_alloc("ds2781-battery", id);
|
||||
if (!pdev) {
|
||||
ret = -ENOMEM;
|
||||
goto pdev_alloc_failed;
|
||||
}
|
||||
pdev->dev.parent = &sl->dev;
|
||||
|
||||
ret = platform_device_add(pdev);
|
||||
if (ret)
|
||||
goto pdev_add_failed;
|
||||
|
||||
ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2781_bin_attr);
|
||||
if (ret)
|
||||
goto bin_attr_failed;
|
||||
|
||||
dev_set_drvdata(&sl->dev, pdev);
|
||||
|
||||
return 0;
|
||||
|
||||
bin_attr_failed:
|
||||
pdev_add_failed:
|
||||
platform_device_unregister(pdev);
|
||||
pdev_alloc_failed:
|
||||
ida_simple_remove(&bat_ida, id);
|
||||
noid:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void w1_ds2781_remove_slave(struct w1_slave *sl)
|
||||
{
|
||||
struct platform_device *pdev = dev_get_drvdata(&sl->dev);
|
||||
int id = pdev->id;
|
||||
|
||||
platform_device_unregister(pdev);
|
||||
ida_simple_remove(&bat_ida, id);
|
||||
sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2781_bin_attr);
|
||||
}
|
||||
|
||||
static struct w1_family_ops w1_ds2781_fops = {
|
||||
.add_slave = w1_ds2781_add_slave,
|
||||
.remove_slave = w1_ds2781_remove_slave,
|
||||
};
|
||||
|
||||
static struct w1_family w1_ds2781_family = {
|
||||
.fid = W1_FAMILY_DS2781,
|
||||
.fops = &w1_ds2781_fops,
|
||||
};
|
||||
|
||||
static int __init w1_ds2781_init(void)
|
||||
{
|
||||
ida_init(&bat_ida);
|
||||
return w1_register_family(&w1_ds2781_family);
|
||||
}
|
||||
|
||||
static void __exit w1_ds2781_exit(void)
|
||||
{
|
||||
w1_unregister_family(&w1_ds2781_family);
|
||||
ida_destroy(&bat_ida);
|
||||
}
|
||||
|
||||
module_init(w1_ds2781_init);
|
||||
module_exit(w1_ds2781_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>");
|
||||
MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC");
|
136
drivers/w1/slaves/w1_ds2781.h
Normal file
136
drivers/w1/slaves/w1_ds2781.h
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 1-Wire implementation for the ds2780 chip
|
||||
*
|
||||
* Author: Renata Sayakhova <renata@oktetlabs.ru>
|
||||
*
|
||||
* Based on w1-ds2760 driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _W1_DS2781_H
|
||||
#define _W1_DS2781_H
|
||||
|
||||
/* Function commands */
|
||||
#define W1_DS2781_READ_DATA 0x69
|
||||
#define W1_DS2781_WRITE_DATA 0x6C
|
||||
#define W1_DS2781_COPY_DATA 0x48
|
||||
#define W1_DS2781_RECALL_DATA 0xB8
|
||||
#define W1_DS2781_LOCK 0x6A
|
||||
|
||||
/* Register map */
|
||||
/* Register 0x00 Reserved */
|
||||
#define DS2781_STATUS 0x01
|
||||
#define DS2781_RAAC_MSB 0x02
|
||||
#define DS2781_RAAC_LSB 0x03
|
||||
#define DS2781_RSAC_MSB 0x04
|
||||
#define DS2781_RSAC_LSB 0x05
|
||||
#define DS2781_RARC 0x06
|
||||
#define DS2781_RSRC 0x07
|
||||
#define DS2781_IAVG_MSB 0x08
|
||||
#define DS2781_IAVG_LSB 0x09
|
||||
#define DS2781_TEMP_MSB 0x0A
|
||||
#define DS2781_TEMP_LSB 0x0B
|
||||
#define DS2781_VOLT_MSB 0x0C
|
||||
#define DS2781_VOLT_LSB 0x0D
|
||||
#define DS2781_CURRENT_MSB 0x0E
|
||||
#define DS2781_CURRENT_LSB 0x0F
|
||||
#define DS2781_ACR_MSB 0x10
|
||||
#define DS2781_ACR_LSB 0x11
|
||||
#define DS2781_ACRL_MSB 0x12
|
||||
#define DS2781_ACRL_LSB 0x13
|
||||
#define DS2781_AS 0x14
|
||||
#define DS2781_SFR 0x15
|
||||
#define DS2781_FULL_MSB 0x16
|
||||
#define DS2781_FULL_LSB 0x17
|
||||
#define DS2781_AE_MSB 0x18
|
||||
#define DS2781_AE_LSB 0x19
|
||||
#define DS2781_SE_MSB 0x1A
|
||||
#define DS2781_SE_LSB 0x1B
|
||||
/* Register 0x1C - 0x1E Reserved */
|
||||
#define DS2781_EEPROM 0x1F
|
||||
#define DS2781_EEPROM_BLOCK0_START 0x20
|
||||
/* Register 0x20 - 0x2F User EEPROM */
|
||||
#define DS2781_EEPROM_BLOCK0_END 0x2F
|
||||
/* Register 0x30 - 0x5F Reserved */
|
||||
#define DS2781_EEPROM_BLOCK1_START 0x60
|
||||
#define DS2781_CONTROL 0x60
|
||||
#define DS2781_AB 0x61
|
||||
#define DS2781_AC_MSB 0x62
|
||||
#define DS2781_AC_LSB 0x63
|
||||
#define DS2781_VCHG 0x64
|
||||
#define DS2781_IMIN 0x65
|
||||
#define DS2781_VAE 0x66
|
||||
#define DS2781_IAE 0x67
|
||||
#define DS2781_AE_40 0x68
|
||||
#define DS2781_RSNSP 0x69
|
||||
#define DS2781_FULL_40_MSB 0x6A
|
||||
#define DS2781_FULL_40_LSB 0x6B
|
||||
#define DS2781_FULL_4_SLOPE 0x6C
|
||||
#define DS2781_FULL_3_SLOPE 0x6D
|
||||
#define DS2781_FULL_2_SLOPE 0x6E
|
||||
#define DS2781_FULL_1_SLOPE 0x6F
|
||||
#define DS2781_AE_4_SLOPE 0x70
|
||||
#define DS2781_AE_3_SLOPE 0x71
|
||||
#define DS2781_AE_2_SLOPE 0x72
|
||||
#define DS2781_AE_1_SLOPE 0x73
|
||||
#define DS2781_SE_4_SLOPE 0x74
|
||||
#define DS2781_SE_3_SLOPE 0x75
|
||||
#define DS2781_SE_2_SLOPE 0x76
|
||||
#define DS2781_SE_1_SLOPE 0x77
|
||||
#define DS2781_RSGAIN_MSB 0x78
|
||||
#define DS2781_RSGAIN_LSB 0x79
|
||||
#define DS2781_RSTC 0x7A
|
||||
#define DS2781_COB 0x7B
|
||||
#define DS2781_TBP34 0x7C
|
||||
#define DS2781_TBP23 0x7D
|
||||
#define DS2781_TBP12 0x7E
|
||||
#define DS2781_EEPROM_BLOCK1_END 0x7F
|
||||
/* Register 0x7D - 0xFF Reserved */
|
||||
|
||||
#define DS2781_FSGAIN_MSB 0xB0
|
||||
#define DS2781_FSGAIN_LSB 0xB1
|
||||
|
||||
/* Number of valid register addresses */
|
||||
#define DS2781_DATA_SIZE 0xB2
|
||||
|
||||
/* Status register bits */
|
||||
#define DS2781_STATUS_CHGTF (1 << 7)
|
||||
#define DS2781_STATUS_AEF (1 << 6)
|
||||
#define DS2781_STATUS_SEF (1 << 5)
|
||||
#define DS2781_STATUS_LEARNF (1 << 4)
|
||||
/* Bit 3 Reserved */
|
||||
#define DS2781_STATUS_UVF (1 << 2)
|
||||
#define DS2781_STATUS_PORF (1 << 1)
|
||||
/* Bit 0 Reserved */
|
||||
|
||||
/* Control register bits */
|
||||
/* Bit 7 Reserved */
|
||||
#define DS2781_CONTROL_NBEN (1 << 7)
|
||||
#define DS2781_CONTROL_UVEN (1 << 6)
|
||||
#define DS2781_CONTROL_PMOD (1 << 5)
|
||||
#define DS2781_CONTROL_RNAOP (1 << 4)
|
||||
#define DS1781_CONTROL_UVTH (1 << 3)
|
||||
/* Bit 0 - 2 Reserved */
|
||||
|
||||
/* Special feature register bits */
|
||||
/* Bit 1 - 7 Reserved */
|
||||
#define DS2781_SFR_PIOSC (1 << 0)
|
||||
|
||||
/* EEPROM register bits */
|
||||
#define DS2781_EEPROM_EEC (1 << 7)
|
||||
#define DS2781_EEPROM_LOCK (1 << 6)
|
||||
/* Bit 2 - 6 Reserved */
|
||||
#define DS2781_EEPROM_BL1 (1 << 1)
|
||||
#define DS2781_EEPROM_BL0 (1 << 0)
|
||||
|
||||
extern int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count,
|
||||
int io);
|
||||
extern int w1_ds2781_io_nolock(struct device *dev, char *buf, int addr,
|
||||
size_t count, int io);
|
||||
extern int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd);
|
||||
|
||||
#endif /* !_W1_DS2781_H */
|
|
@ -38,6 +38,7 @@
|
|||
#define W1_EEPROM_DS2431 0x2D
|
||||
#define W1_FAMILY_DS2760 0x30
|
||||
#define W1_FAMILY_DS2780 0x32
|
||||
#define W1_FAMILY_DS2781 0x3D
|
||||
#define W1_THERM_DS28EA00 0x42
|
||||
|
||||
#define MAXNAMELEN 32
|
||||
|
|
|
@ -23,9 +23,13 @@
|
|||
#include <linux/debugfs.h>
|
||||
#include <linux/fsnotify.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define DEBUGFS_DEFAULT_MODE 0755
|
||||
|
||||
static struct vfsmount *debugfs_mount;
|
||||
static int debugfs_mount_count;
|
||||
static bool debugfs_registered;
|
||||
|
@ -125,11 +129,154 @@ static inline int debugfs_positive(struct dentry *dentry)
|
|||
return dentry->d_inode && !d_unhashed(dentry);
|
||||
}
|
||||
|
||||
struct debugfs_mount_opts {
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
umode_t mode;
|
||||
};
|
||||
|
||||
enum {
|
||||
Opt_uid,
|
||||
Opt_gid,
|
||||
Opt_mode,
|
||||
Opt_err
|
||||
};
|
||||
|
||||
static const match_table_t tokens = {
|
||||
{Opt_uid, "uid=%u"},
|
||||
{Opt_gid, "gid=%u"},
|
||||
{Opt_mode, "mode=%o"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
struct debugfs_fs_info {
|
||||
struct debugfs_mount_opts mount_opts;
|
||||
};
|
||||
|
||||
static int debugfs_parse_options(char *data, struct debugfs_mount_opts *opts)
|
||||
{
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int option;
|
||||
int token;
|
||||
char *p;
|
||||
|
||||
opts->mode = DEBUGFS_DEFAULT_MODE;
|
||||
|
||||
while ((p = strsep(&data, ",")) != NULL) {
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
token = match_token(p, tokens, args);
|
||||
switch (token) {
|
||||
case Opt_uid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
opts->uid = option;
|
||||
break;
|
||||
case Opt_gid:
|
||||
if (match_octal(&args[0], &option))
|
||||
return -EINVAL;
|
||||
opts->gid = option;
|
||||
break;
|
||||
case Opt_mode:
|
||||
if (match_octal(&args[0], &option))
|
||||
return -EINVAL;
|
||||
opts->mode = option & S_IALLUGO;
|
||||
break;
|
||||
/*
|
||||
* We might like to report bad mount options here;
|
||||
* but traditionally debugfs has ignored all mount options
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int debugfs_apply_options(struct super_block *sb)
|
||||
{
|
||||
struct debugfs_fs_info *fsi = sb->s_fs_info;
|
||||
struct inode *inode = sb->s_root->d_inode;
|
||||
struct debugfs_mount_opts *opts = &fsi->mount_opts;
|
||||
|
||||
inode->i_mode &= ~S_IALLUGO;
|
||||
inode->i_mode |= opts->mode;
|
||||
|
||||
inode->i_uid = opts->uid;
|
||||
inode->i_gid = opts->gid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int debugfs_remount(struct super_block *sb, int *flags, char *data)
|
||||
{
|
||||
int err;
|
||||
struct debugfs_fs_info *fsi = sb->s_fs_info;
|
||||
|
||||
err = debugfs_parse_options(data, &fsi->mount_opts);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
debugfs_apply_options(sb);
|
||||
|
||||
fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int debugfs_show_options(struct seq_file *m, struct dentry *root)
|
||||
{
|
||||
struct debugfs_fs_info *fsi = root->d_sb->s_fs_info;
|
||||
struct debugfs_mount_opts *opts = &fsi->mount_opts;
|
||||
|
||||
if (opts->uid != 0)
|
||||
seq_printf(m, ",uid=%u", opts->uid);
|
||||
if (opts->gid != 0)
|
||||
seq_printf(m, ",gid=%u", opts->gid);
|
||||
if (opts->mode != DEBUGFS_DEFAULT_MODE)
|
||||
seq_printf(m, ",mode=%o", opts->mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct super_operations debugfs_super_operations = {
|
||||
.statfs = simple_statfs,
|
||||
.remount_fs = debugfs_remount,
|
||||
.show_options = debugfs_show_options,
|
||||
};
|
||||
|
||||
static int debug_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
static struct tree_descr debug_files[] = {{""}};
|
||||
struct debugfs_fs_info *fsi;
|
||||
int err;
|
||||
|
||||
return simple_fill_super(sb, DEBUGFS_MAGIC, debug_files);
|
||||
save_mount_options(sb, data);
|
||||
|
||||
fsi = kzalloc(sizeof(struct debugfs_fs_info), GFP_KERNEL);
|
||||
sb->s_fs_info = fsi;
|
||||
if (!fsi) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = debugfs_parse_options(data, &fsi->mount_opts);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
err = simple_fill_super(sb, DEBUGFS_MAGIC, debug_files);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
sb->s_op = &debugfs_super_operations;
|
||||
|
||||
debugfs_apply_options(sb);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(fsi);
|
||||
sb->s_fs_info = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct dentry *debug_mount(struct file_system_type *fs_type,
|
||||
|
|
218
fs/sysfs/dir.c
218
fs/sysfs/dir.c
|
@ -22,76 +22,103 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/hash.h>
|
||||
#include "sysfs.h"
|
||||
|
||||
DEFINE_MUTEX(sysfs_mutex);
|
||||
DEFINE_SPINLOCK(sysfs_assoc_lock);
|
||||
|
||||
#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb);
|
||||
|
||||
static DEFINE_SPINLOCK(sysfs_ino_lock);
|
||||
static DEFINE_IDA(sysfs_ino_ida);
|
||||
|
||||
/**
|
||||
* sysfs_link_sibling - link sysfs_dirent into sibling list
|
||||
* sysfs_name_hash
|
||||
* @ns: Namespace tag to hash
|
||||
* @name: Null terminated string to hash
|
||||
*
|
||||
* Returns 31 bit hash of ns + name (so it fits in an off_t )
|
||||
*/
|
||||
static unsigned int sysfs_name_hash(const void *ns, const char *name)
|
||||
{
|
||||
unsigned long hash = init_name_hash();
|
||||
unsigned int len = strlen(name);
|
||||
while (len--)
|
||||
hash = partial_name_hash(*name++, hash);
|
||||
hash = ( end_name_hash(hash) ^ hash_ptr( (void *)ns, 31 ) );
|
||||
hash &= 0x7fffffffU;
|
||||
/* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */
|
||||
if (hash < 1)
|
||||
hash += 2;
|
||||
if (hash >= INT_MAX)
|
||||
hash = INT_MAX - 1;
|
||||
return hash;
|
||||
}
|
||||
|
||||
static int sysfs_name_compare(unsigned int hash, const void *ns,
|
||||
const char *name, const struct sysfs_dirent *sd)
|
||||
{
|
||||
if (hash != sd->s_hash)
|
||||
return hash - sd->s_hash;
|
||||
if (ns != sd->s_ns)
|
||||
return ns - sd->s_ns;
|
||||
return strcmp(name, sd->s_name);
|
||||
}
|
||||
|
||||
static int sysfs_sd_compare(const struct sysfs_dirent *left,
|
||||
const struct sysfs_dirent *right)
|
||||
{
|
||||
return sysfs_name_compare(left->s_hash, left->s_ns, left->s_name,
|
||||
right);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_link_subling - link sysfs_dirent into sibling rbtree
|
||||
* @sd: sysfs_dirent of interest
|
||||
*
|
||||
* Link @sd into its sibling list which starts from
|
||||
* Link @sd into its sibling rbtree which starts from
|
||||
* sd->s_parent->s_dir.children.
|
||||
*
|
||||
* Locking:
|
||||
* mutex_lock(sysfs_mutex)
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 on susccess -EEXIST on failure.
|
||||
*/
|
||||
static void sysfs_link_sibling(struct sysfs_dirent *sd)
|
||||
static int sysfs_link_sibling(struct sysfs_dirent *sd)
|
||||
{
|
||||
struct sysfs_dirent *parent_sd = sd->s_parent;
|
||||
|
||||
struct rb_node **p;
|
||||
struct rb_node *parent;
|
||||
struct rb_node **node = &sd->s_parent->s_dir.children.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
|
||||
if (sysfs_type(sd) == SYSFS_DIR)
|
||||
parent_sd->s_dir.subdirs++;
|
||||
sd->s_parent->s_dir.subdirs++;
|
||||
|
||||
p = &parent_sd->s_dir.inode_tree.rb_node;
|
||||
parent = NULL;
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
#define node rb_entry(parent, struct sysfs_dirent, inode_node)
|
||||
if (sd->s_ino < node->s_ino) {
|
||||
p = &node->inode_node.rb_left;
|
||||
} else if (sd->s_ino > node->s_ino) {
|
||||
p = &node->inode_node.rb_right;
|
||||
} else {
|
||||
printk(KERN_CRIT "sysfs: inserting duplicate inode '%lx'\n",
|
||||
(unsigned long) sd->s_ino);
|
||||
BUG();
|
||||
}
|
||||
#undef node
|
||||
}
|
||||
rb_link_node(&sd->inode_node, parent, p);
|
||||
rb_insert_color(&sd->inode_node, &parent_sd->s_dir.inode_tree);
|
||||
while (*node) {
|
||||
struct sysfs_dirent *pos;
|
||||
int result;
|
||||
|
||||
p = &parent_sd->s_dir.name_tree.rb_node;
|
||||
parent = NULL;
|
||||
while (*p) {
|
||||
int c;
|
||||
parent = *p;
|
||||
#define node rb_entry(parent, struct sysfs_dirent, name_node)
|
||||
c = strcmp(sd->s_name, node->s_name);
|
||||
if (c < 0) {
|
||||
p = &node->name_node.rb_left;
|
||||
} else {
|
||||
p = &node->name_node.rb_right;
|
||||
}
|
||||
#undef node
|
||||
pos = to_sysfs_dirent(*node);
|
||||
parent = *node;
|
||||
result = sysfs_sd_compare(sd, pos);
|
||||
if (result < 0)
|
||||
node = &pos->s_rb.rb_left;
|
||||
else if (result > 0)
|
||||
node = &pos->s_rb.rb_right;
|
||||
else
|
||||
return -EEXIST;
|
||||
}
|
||||
rb_link_node(&sd->name_node, parent, p);
|
||||
rb_insert_color(&sd->name_node, &parent_sd->s_dir.name_tree);
|
||||
/* add new node and rebalance the tree */
|
||||
rb_link_node(&sd->s_rb, parent, node);
|
||||
rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_unlink_sibling - unlink sysfs_dirent from sibling list
|
||||
* sysfs_unlink_sibling - unlink sysfs_dirent from sibling rbtree
|
||||
* @sd: sysfs_dirent of interest
|
||||
*
|
||||
* Unlink @sd from its sibling list which starts from
|
||||
* Unlink @sd from its sibling rbtree which starts from
|
||||
* sd->s_parent->s_dir.children.
|
||||
*
|
||||
* Locking:
|
||||
|
@ -102,8 +129,7 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
|
|||
if (sysfs_type(sd) == SYSFS_DIR)
|
||||
sd->s_parent->s_dir.subdirs--;
|
||||
|
||||
rb_erase(&sd->inode_node, &sd->s_parent->s_dir.inode_tree);
|
||||
rb_erase(&sd->name_node, &sd->s_parent->s_dir.name_tree);
|
||||
rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -198,7 +224,7 @@ static void sysfs_deactivate(struct sysfs_dirent *sd)
|
|||
rwsem_release(&sd->dep_map, 1, _RET_IP_);
|
||||
}
|
||||
|
||||
static int sysfs_alloc_ino(ino_t *pino)
|
||||
static int sysfs_alloc_ino(unsigned int *pino)
|
||||
{
|
||||
int ino, rc;
|
||||
|
||||
|
@ -217,7 +243,7 @@ static int sysfs_alloc_ino(ino_t *pino)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static void sysfs_free_ino(ino_t ino)
|
||||
static void sysfs_free_ino(unsigned int ino)
|
||||
{
|
||||
spin_lock(&sysfs_ino_lock);
|
||||
ida_remove(&sysfs_ino_ida, ino);
|
||||
|
@ -402,6 +428,7 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
|
|||
int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
|
||||
{
|
||||
struct sysfs_inode_attrs *ps_iattr;
|
||||
int ret;
|
||||
|
||||
if (!!sysfs_ns_type(acxt->parent_sd) != !!sd->s_ns) {
|
||||
WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
|
||||
|
@ -410,12 +437,12 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name))
|
||||
return -EEXIST;
|
||||
|
||||
sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name);
|
||||
sd->s_parent = sysfs_get(acxt->parent_sd);
|
||||
|
||||
sysfs_link_sibling(sd);
|
||||
ret = sysfs_link_sibling(sd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Update timestamps on the parent */
|
||||
ps_iattr = acxt->parent_sd->s_iattr;
|
||||
|
@ -565,8 +592,8 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
|
|||
const void *ns,
|
||||
const unsigned char *name)
|
||||
{
|
||||
struct rb_node *p = parent_sd->s_dir.name_tree.rb_node;
|
||||
struct sysfs_dirent *found = NULL;
|
||||
struct rb_node *node = parent_sd->s_dir.children.rb_node;
|
||||
unsigned int hash;
|
||||
|
||||
if (!!sysfs_ns_type(parent_sd) != !!ns) {
|
||||
WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
|
||||
|
@ -575,33 +602,21 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
while (p) {
|
||||
int c;
|
||||
#define node rb_entry(p, struct sysfs_dirent, name_node)
|
||||
c = strcmp(name, node->s_name);
|
||||
if (c < 0) {
|
||||
p = node->name_node.rb_left;
|
||||
} else if (c > 0) {
|
||||
p = node->name_node.rb_right;
|
||||
} else {
|
||||
found = node;
|
||||
p = node->name_node.rb_left;
|
||||
}
|
||||
#undef node
|
||||
}
|
||||
hash = sysfs_name_hash(ns, name);
|
||||
while (node) {
|
||||
struct sysfs_dirent *sd;
|
||||
int result;
|
||||
|
||||
if (found) {
|
||||
while (found->s_ns != ns) {
|
||||
p = rb_next(&found->name_node);
|
||||
if (!p)
|
||||
return NULL;
|
||||
found = rb_entry(p, struct sysfs_dirent, name_node);
|
||||
if (strcmp(name, found->s_name))
|
||||
return NULL;
|
||||
}
|
||||
sd = to_sysfs_dirent(node);
|
||||
result = sysfs_name_compare(hash, ns, name, sd);
|
||||
if (result < 0)
|
||||
node = node->rb_left;
|
||||
else if (result > 0)
|
||||
node = node->rb_right;
|
||||
else
|
||||
return sd;
|
||||
}
|
||||
|
||||
return found;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -804,9 +819,9 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
|
|||
|
||||
pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
|
||||
sysfs_addrm_start(&acxt, dir_sd);
|
||||
pos = rb_first(&dir_sd->s_dir.inode_tree);
|
||||
pos = rb_first(&dir_sd->s_dir.children);
|
||||
while (pos) {
|
||||
struct sysfs_dirent *sd = rb_entry(pos, struct sysfs_dirent, inode_node);
|
||||
struct sysfs_dirent *sd = to_sysfs_dirent(pos);
|
||||
pos = rb_next(pos);
|
||||
if (sysfs_type(sd) != SYSFS_DIR)
|
||||
sysfs_remove_one(&acxt, sd);
|
||||
|
@ -863,6 +878,7 @@ int sysfs_rename(struct sysfs_dirent *sd,
|
|||
|
||||
dup_name = sd->s_name;
|
||||
sd->s_name = new_name;
|
||||
sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name);
|
||||
}
|
||||
|
||||
/* Move to the appropriate place in the appropriate directories rbtree. */
|
||||
|
@ -919,38 +935,36 @@ static int sysfs_dir_release(struct inode *inode, struct file *filp)
|
|||
}
|
||||
|
||||
static struct sysfs_dirent *sysfs_dir_pos(const void *ns,
|
||||
struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos)
|
||||
struct sysfs_dirent *parent_sd, loff_t hash, struct sysfs_dirent *pos)
|
||||
{
|
||||
if (pos) {
|
||||
int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) &&
|
||||
pos->s_parent == parent_sd &&
|
||||
ino == pos->s_ino;
|
||||
hash == pos->s_hash;
|
||||
sysfs_put(pos);
|
||||
if (!valid)
|
||||
pos = NULL;
|
||||
}
|
||||
if (!pos && (ino > 1) && (ino < INT_MAX)) {
|
||||
struct rb_node *p = parent_sd->s_dir.inode_tree.rb_node;
|
||||
while (p) {
|
||||
#define node rb_entry(p, struct sysfs_dirent, inode_node)
|
||||
if (ino < node->s_ino) {
|
||||
pos = node;
|
||||
p = node->inode_node.rb_left;
|
||||
} else if (ino > node->s_ino) {
|
||||
p = node->inode_node.rb_right;
|
||||
} else {
|
||||
pos = node;
|
||||
if (!pos && (hash > 1) && (hash < INT_MAX)) {
|
||||
struct rb_node *node = parent_sd->s_dir.children.rb_node;
|
||||
while (node) {
|
||||
pos = to_sysfs_dirent(node);
|
||||
|
||||
if (hash < pos->s_hash)
|
||||
node = node->rb_left;
|
||||
else if (hash > pos->s_hash)
|
||||
node = node->rb_right;
|
||||
else
|
||||
break;
|
||||
}
|
||||
#undef node
|
||||
}
|
||||
}
|
||||
/* Skip over entries in the wrong namespace */
|
||||
while (pos && pos->s_ns != ns) {
|
||||
struct rb_node *p = rb_next(&pos->inode_node);
|
||||
if (!p)
|
||||
struct rb_node *node = rb_next(&pos->s_rb);
|
||||
if (!node)
|
||||
pos = NULL;
|
||||
else
|
||||
pos = rb_entry(p, struct sysfs_dirent, inode_node);
|
||||
pos = to_sysfs_dirent(node);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
@ -960,11 +974,11 @@ static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns,
|
|||
{
|
||||
pos = sysfs_dir_pos(ns, parent_sd, ino, pos);
|
||||
if (pos) do {
|
||||
struct rb_node *p = rb_next(&pos->inode_node);
|
||||
if (!p)
|
||||
struct rb_node *node = rb_next(&pos->s_rb);
|
||||
if (!node)
|
||||
pos = NULL;
|
||||
else
|
||||
pos = rb_entry(p, struct sysfs_dirent, inode_node);
|
||||
pos = to_sysfs_dirent(node);
|
||||
} while (pos && pos->s_ns != ns);
|
||||
return pos;
|
||||
}
|
||||
|
@ -1006,7 +1020,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
|
|||
len = strlen(name);
|
||||
ino = pos->s_ino;
|
||||
type = dt_type(pos);
|
||||
filp->f_pos = ino;
|
||||
filp->f_pos = pos->s_hash;
|
||||
filp->private_data = sysfs_get(pos);
|
||||
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
|
|
|
@ -136,12 +136,13 @@ static int sysfs_sd_setsecdata(struct sysfs_dirent *sd, void **secdata, u32 *sec
|
|||
void *old_secdata;
|
||||
size_t old_secdata_len;
|
||||
|
||||
iattrs = sd->s_iattr;
|
||||
if (!iattrs)
|
||||
iattrs = sysfs_init_inode_attrs(sd);
|
||||
if (!iattrs)
|
||||
return -ENOMEM;
|
||||
if (!sd->s_iattr) {
|
||||
sd->s_iattr = sysfs_init_inode_attrs(sd);
|
||||
if (!sd->s_iattr)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
iattrs = sd->s_iattr;
|
||||
old_secdata = iattrs->ia_secdata;
|
||||
old_secdata_len = iattrs->ia_secdata_len;
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ struct sysfs_dirent sysfs_root = {
|
|||
.s_name = "",
|
||||
.s_count = ATOMIC_INIT(1),
|
||||
.s_flags = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT),
|
||||
.s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
|
||||
.s_mode = S_IFDIR | S_IRUGO | S_IXUGO,
|
||||
.s_ino = 1,
|
||||
};
|
||||
|
||||
|
|
|
@ -20,9 +20,8 @@ struct sysfs_elem_dir {
|
|||
struct kobject *kobj;
|
||||
|
||||
unsigned long subdirs;
|
||||
|
||||
struct rb_root inode_tree;
|
||||
struct rb_root name_tree;
|
||||
/* children rbtree starts here and goes through sd->s_rb */
|
||||
struct rb_root children;
|
||||
};
|
||||
|
||||
struct sysfs_elem_symlink {
|
||||
|
@ -62,8 +61,7 @@ struct sysfs_dirent {
|
|||
struct sysfs_dirent *s_parent;
|
||||
const char *s_name;
|
||||
|
||||
struct rb_node inode_node;
|
||||
struct rb_node name_node;
|
||||
struct rb_node s_rb;
|
||||
|
||||
union {
|
||||
struct completion *completion;
|
||||
|
@ -71,6 +69,7 @@ struct sysfs_dirent {
|
|||
} u;
|
||||
|
||||
const void *s_ns; /* namespace tag */
|
||||
unsigned int s_hash; /* ns + name hash */
|
||||
union {
|
||||
struct sysfs_elem_dir s_dir;
|
||||
struct sysfs_elem_symlink s_symlink;
|
||||
|
@ -78,9 +77,9 @@ struct sysfs_dirent {
|
|||
struct sysfs_elem_bin_attr s_bin_attr;
|
||||
};
|
||||
|
||||
unsigned int s_flags;
|
||||
unsigned short s_flags;
|
||||
umode_t s_mode;
|
||||
ino_t s_ino;
|
||||
unsigned int s_ino;
|
||||
struct sysfs_inode_attrs *s_iattr;
|
||||
};
|
||||
|
||||
|
@ -95,11 +94,11 @@ struct sysfs_dirent {
|
|||
#define SYSFS_ACTIVE_REF (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR)
|
||||
|
||||
/* identify any namespace tag on sysfs_dirents */
|
||||
#define SYSFS_NS_TYPE_MASK 0xff00
|
||||
#define SYSFS_NS_TYPE_MASK 0xf00
|
||||
#define SYSFS_NS_TYPE_SHIFT 8
|
||||
|
||||
#define SYSFS_FLAG_MASK ~(SYSFS_NS_TYPE_MASK|SYSFS_TYPE_MASK)
|
||||
#define SYSFS_FLAG_REMOVED 0x020000
|
||||
#define SYSFS_FLAG_REMOVED 0x02000
|
||||
|
||||
static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
|
||||
{
|
||||
|
|
|
@ -225,6 +225,7 @@ struct acpi_processor_errata {
|
|||
} piix4;
|
||||
};
|
||||
|
||||
extern void acpi_processor_load_module(struct acpi_processor *pr);
|
||||
extern int acpi_processor_preregister_performance(struct
|
||||
acpi_processor_performance
|
||||
__percpu *performance);
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#define CN_IDX_DRBD 0x8
|
||||
#define CN_VAL_DRBD 0x1
|
||||
#define CN_KVP_IDX 0x9 /* HyperV KVP */
|
||||
#define CN_KVP_VAL 0x1 /* queries from the kernel */
|
||||
|
||||
#define CN_NETLINK_USERS 10 /* Highest index + 1 */
|
||||
|
||||
|
|
|
@ -44,6 +44,13 @@ extern ssize_t arch_cpu_release(const char *, size_t);
|
|||
#endif
|
||||
struct notifier_block;
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
|
||||
extern int arch_cpu_uevent(struct device *dev, struct kobj_uevent_env *env);
|
||||
extern ssize_t arch_print_cpu_modalias(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *bufptr);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* CPU notifier priorities.
|
||||
*/
|
||||
|
|
|
@ -238,8 +238,6 @@ struct device_driver {
|
|||
extern int __must_check driver_register(struct device_driver *drv);
|
||||
extern void driver_unregister(struct device_driver *drv);
|
||||
|
||||
extern struct device_driver *get_driver(struct device_driver *drv);
|
||||
extern void put_driver(struct device_driver *drv);
|
||||
extern struct device_driver *driver_find(const char *name,
|
||||
struct bus_type *bus);
|
||||
extern int driver_probe_done(void);
|
||||
|
@ -946,14 +944,14 @@ int _dev_info(const struct device *dev, const char *fmt, ...)
|
|||
|
||||
#define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg)
|
||||
|
||||
#if defined(DEBUG)
|
||||
#define dev_dbg(dev, format, arg...) \
|
||||
dev_printk(KERN_DEBUG, dev, format, ##arg)
|
||||
#elif defined(CONFIG_DYNAMIC_DEBUG)
|
||||
#if defined(CONFIG_DYNAMIC_DEBUG)
|
||||
#define dev_dbg(dev, format, ...) \
|
||||
do { \
|
||||
dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#elif defined(DEBUG)
|
||||
#define dev_dbg(dev, format, arg...) \
|
||||
dev_printk(KERN_DEBUG, dev, format, ##arg)
|
||||
#else
|
||||
#define dev_dbg(dev, format, arg...) \
|
||||
({ \
|
||||
|
|
|
@ -15,20 +15,24 @@ struct _ddebug {
|
|||
const char *function;
|
||||
const char *filename;
|
||||
const char *format;
|
||||
unsigned int lineno:24;
|
||||
unsigned int lineno:18;
|
||||
/*
|
||||
* The flags field controls the behaviour at the callsite.
|
||||
* The bits here are changed dynamically when the user
|
||||
* writes commands to <debugfs>/dynamic_debug/control
|
||||
*/
|
||||
#define _DPRINTK_FLAGS_PRINT (1<<0) /* printk() a message using the format */
|
||||
#define _DPRINTK_FLAGS_NONE 0
|
||||
#define _DPRINTK_FLAGS_PRINT (1<<0) /* printk() a message using the format */
|
||||
#define _DPRINTK_FLAGS_INCL_MODNAME (1<<1)
|
||||
#define _DPRINTK_FLAGS_INCL_FUNCNAME (1<<2)
|
||||
#define _DPRINTK_FLAGS_INCL_LINENO (1<<3)
|
||||
#define _DPRINTK_FLAGS_INCL_TID (1<<4)
|
||||
#if defined DEBUG
|
||||
#define _DPRINTK_FLAGS_DEFAULT _DPRINTK_FLAGS_PRINT
|
||||
#else
|
||||
#define _DPRINTK_FLAGS_DEFAULT 0
|
||||
#endif
|
||||
unsigned int flags:8;
|
||||
char enabled;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
|
||||
|
@ -62,21 +66,20 @@ int __dynamic_netdev_dbg(struct _ddebug *descriptor,
|
|||
.format = (fmt), \
|
||||
.lineno = __LINE__, \
|
||||
.flags = _DPRINTK_FLAGS_DEFAULT, \
|
||||
.enabled = false, \
|
||||
}
|
||||
|
||||
#define dynamic_pr_debug(fmt, ...) \
|
||||
do { \
|
||||
DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
|
||||
if (unlikely(descriptor.enabled)) \
|
||||
if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \
|
||||
__dynamic_pr_debug(&descriptor, pr_fmt(fmt), \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define dynamic_dev_dbg(dev, fmt, ...) \
|
||||
do { \
|
||||
DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
|
||||
if (unlikely(descriptor.enabled)) \
|
||||
DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
|
||||
if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \
|
||||
__dynamic_dev_dbg(&descriptor, dev, fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
@ -84,7 +87,7 @@ do { \
|
|||
#define dynamic_netdev_dbg(dev, fmt, ...) \
|
||||
do { \
|
||||
DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
|
||||
if (unlikely(descriptor.enabled)) \
|
||||
if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \
|
||||
__dynamic_netdev_dbg(&descriptor, dev, fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#define ERESTARTNOHAND 514 /* restart if no handler.. */
|
||||
#define ENOIOCTLCMD 515 /* No ioctl command */
|
||||
#define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */
|
||||
#define EPROBE_DEFER 517 /* Driver requests probe retry */
|
||||
|
||||
/* Defined for the NFSv3 protocol */
|
||||
#define EBADHANDLE 521 /* Illegal NFS file handle */
|
||||
|
|
|
@ -25,6 +25,173 @@
|
|||
#ifndef _HYPERV_H
|
||||
#define _HYPERV_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* An implementation of HyperV key value pair (KVP) functionality for Linux.
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2010, Novell, Inc.
|
||||
* Author : K. Y. Srinivasan <ksrinivasan@novell.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Maximum value size - used for both key names and value data, and includes
|
||||
* any applicable NULL terminators.
|
||||
*
|
||||
* Note: This limit is somewhat arbitrary, but falls easily within what is
|
||||
* supported for all native guests (back to Win 2000) and what is reasonable
|
||||
* for the IC KVP exchange functionality. Note that Windows Me/98/95 are
|
||||
* limited to 255 character key names.
|
||||
*
|
||||
* MSDN recommends not storing data values larger than 2048 bytes in the
|
||||
* registry.
|
||||
*
|
||||
* Note: This value is used in defining the KVP exchange message - this value
|
||||
* cannot be modified without affecting the message size and compatibility.
|
||||
*/
|
||||
|
||||
/*
|
||||
* bytes, including any null terminators
|
||||
*/
|
||||
#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048)
|
||||
|
||||
|
||||
/*
|
||||
* Maximum key size - the registry limit for the length of an entry name
|
||||
* is 256 characters, including the null terminator
|
||||
*/
|
||||
|
||||
#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512)
|
||||
|
||||
/*
|
||||
* In Linux, we implement the KVP functionality in two components:
|
||||
* 1) The kernel component which is packaged as part of the hv_utils driver
|
||||
* is responsible for communicating with the host and responsible for
|
||||
* implementing the host/guest protocol. 2) A user level daemon that is
|
||||
* responsible for data gathering.
|
||||
*
|
||||
* Host/Guest Protocol: The host iterates over an index and expects the guest
|
||||
* to assign a key name to the index and also return the value corresponding to
|
||||
* the key. The host will have atmost one KVP transaction outstanding at any
|
||||
* given point in time. The host side iteration stops when the guest returns
|
||||
* an error. Microsoft has specified the following mapping of key names to
|
||||
* host specified index:
|
||||
*
|
||||
* Index Key Name
|
||||
* 0 FullyQualifiedDomainName
|
||||
* 1 IntegrationServicesVersion
|
||||
* 2 NetworkAddressIPv4
|
||||
* 3 NetworkAddressIPv6
|
||||
* 4 OSBuildNumber
|
||||
* 5 OSName
|
||||
* 6 OSMajorVersion
|
||||
* 7 OSMinorVersion
|
||||
* 8 OSVersion
|
||||
* 9 ProcessorArchitecture
|
||||
*
|
||||
* The Windows host expects the Key Name and Key Value to be encoded in utf16.
|
||||
*
|
||||
* Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the
|
||||
* data gathering functionality in a user mode daemon. The user level daemon
|
||||
* is also responsible for binding the key name to the index as well. The
|
||||
* kernel and user-level daemon communicate using a connector channel.
|
||||
*
|
||||
* The user mode component first registers with the
|
||||
* the kernel component. Subsequently, the kernel component requests, data
|
||||
* for the specified keys. In response to this message the user mode component
|
||||
* fills in the value corresponding to the specified key. We overload the
|
||||
* sequence field in the cn_msg header to define our KVP message types.
|
||||
*
|
||||
*
|
||||
* The kernel component simply acts as a conduit for communication between the
|
||||
* Windows host and the user-level daemon. The kernel component passes up the
|
||||
* index received from the Host to the user-level daemon. If the index is
|
||||
* valid (supported), the corresponding key as well as its
|
||||
* value (both are strings) is returned. If the index is invalid
|
||||
* (not supported), a NULL key string is returned.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Registry value types.
|
||||
*/
|
||||
|
||||
#define REG_SZ 1
|
||||
#define REG_U32 4
|
||||
#define REG_U64 8
|
||||
|
||||
enum hv_kvp_exchg_op {
|
||||
KVP_OP_GET = 0,
|
||||
KVP_OP_SET,
|
||||
KVP_OP_DELETE,
|
||||
KVP_OP_ENUMERATE,
|
||||
KVP_OP_REGISTER,
|
||||
KVP_OP_COUNT /* Number of operations, must be last. */
|
||||
};
|
||||
|
||||
enum hv_kvp_exchg_pool {
|
||||
KVP_POOL_EXTERNAL = 0,
|
||||
KVP_POOL_GUEST,
|
||||
KVP_POOL_AUTO,
|
||||
KVP_POOL_AUTO_EXTERNAL,
|
||||
KVP_POOL_AUTO_INTERNAL,
|
||||
KVP_POOL_COUNT /* Number of pools, must be last. */
|
||||
};
|
||||
|
||||
struct hv_kvp_hdr {
|
||||
__u8 operation;
|
||||
__u8 pool;
|
||||
__u16 pad;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct hv_kvp_exchg_msg_value {
|
||||
__u32 value_type;
|
||||
__u32 key_size;
|
||||
__u32 value_size;
|
||||
__u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
|
||||
union {
|
||||
__u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
|
||||
__u32 value_u32;
|
||||
__u64 value_u64;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
struct hv_kvp_msg_enumerate {
|
||||
__u32 index;
|
||||
struct hv_kvp_exchg_msg_value data;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct hv_kvp_msg_get {
|
||||
struct hv_kvp_exchg_msg_value data;
|
||||
};
|
||||
|
||||
struct hv_kvp_msg_set {
|
||||
struct hv_kvp_exchg_msg_value data;
|
||||
};
|
||||
|
||||
struct hv_kvp_msg_delete {
|
||||
__u32 key_size;
|
||||
__u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
|
||||
};
|
||||
|
||||
struct hv_kvp_register {
|
||||
__u8 version[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
|
||||
};
|
||||
|
||||
struct hv_kvp_msg {
|
||||
struct hv_kvp_hdr kvp_hdr;
|
||||
union {
|
||||
struct hv_kvp_msg_get kvp_get;
|
||||
struct hv_kvp_msg_set kvp_set;
|
||||
struct hv_kvp_msg_delete kvp_delete;
|
||||
struct hv_kvp_msg_enumerate kvp_enum_data;
|
||||
struct hv_kvp_register kvp_register;
|
||||
} body;
|
||||
} __attribute__((packed));
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/uuid.h>
|
||||
|
@ -785,6 +952,7 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver);
|
|||
|
||||
#define HV_S_OK 0x00000000
|
||||
#define HV_E_FAIL 0x80004005
|
||||
#define HV_S_CONT 0x80070103
|
||||
#define HV_ERROR_NOT_SUPPORTED 0x80070032
|
||||
#define HV_ERROR_MACHINE_LOCKED 0x800704F7
|
||||
|
||||
|
@ -870,4 +1038,9 @@ struct hyperv_service_callback {
|
|||
extern void vmbus_prep_negotiate_resp(struct icmsg_hdr *,
|
||||
struct icmsg_negotiate *, u8 *);
|
||||
|
||||
int hv_kvp_init(struct hv_util_service *);
|
||||
void hv_kvp_deinit(void);
|
||||
void hv_kvp_onchannelcallback(void *);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* _HYPERV_H */
|
||||
|
|
|
@ -560,4 +560,25 @@ struct amba_id {
|
|||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Match x86 CPUs for CPU specific drivers.
|
||||
* See documentation of "x86_match_cpu" for details.
|
||||
*/
|
||||
|
||||
struct x86_cpu_id {
|
||||
__u16 vendor;
|
||||
__u16 family;
|
||||
__u16 model;
|
||||
__u16 feature; /* bit index */
|
||||
kernel_ulong_t driver_data;
|
||||
};
|
||||
|
||||
#define X86_FEATURE_MATCH(x) \
|
||||
{ X86_VENDOR_ANY, X86_FAMILY_ANY, X86_MODEL_ANY, x }
|
||||
|
||||
#define X86_VENDOR_ANY 0xffff
|
||||
#define X86_FAMILY_ANY 0
|
||||
#define X86_MODEL_ANY 0
|
||||
#define X86_FEATURE_ANY 0 /* Same as FPU, you can't test for that */
|
||||
|
||||
#endif /* LINUX_MOD_DEVICETABLE_H */
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue