Char / Misc patches for 3.20-rc1
Here's the big char/misc driver update for 3.20-rc1. Lots of little things in here, all described in the changelog. Nothing major or unusual, except maybe the binder selinux stuff, which was all acked by the proper selinux people and they thought it best to come through this tree. All of this has been in linux-next with no reported issues for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlTgs80ACgkQMUfUDdst+yn86gCeMLbxANGExVLd+PR46GNsAUQb SJ4AmgIqrkIz+5LCwZWM02ldbYhPeBVf =lfmM -----END PGP SIGNATURE----- Merge tag 'char-misc-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char / misc patches from Greg KH: "Here's the big char/misc driver update for 3.20-rc1. Lots of little things in here, all described in the changelog. Nothing major or unusual, except maybe the binder selinux stuff, which was all acked by the proper selinux people and they thought it best to come through this tree. All of this has been in linux-next with no reported issues for a while" * tag 'char-misc-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (90 commits) coresight: fix function etm_writel_cp14() parameter order coresight-etm: remove check for unknown Kconfig macro coresight: fixing CPU hwid lookup in device tree coresight: remove the unnecessary function coresight_is_bit_set() coresight: fix the debug AMBA bus name coresight: remove the extra spaces coresight: fix the link between orphan connection and newly added device coresight: remove the unnecessary replicator property coresight: fix the replicator subtype value pdfdocs: Fix 'make pdfdocs' failure for 'uio-howto.tmpl' mcb: Fix error path of mcb_pci_probe virtio/console: verify device has config space ti-st: clean up data types (fix harmless memory corruption) mei: me: release hw from reset only during the reset flow mei: mask interrupt set bit on clean reset bit extcon: max77693: Constify struct regmap_config extcon: adc-jack: Release IIO channel on driver remove extcon: Remove duplicated include from extcon-class.c Drivers: hv: vmbus: hv_process_timer_expiration() can be static Drivers: hv: vmbus: serialize Offer and Rescind offer ...
This commit is contained in:
commit
4ba63072b9
@ -719,7 +719,7 @@ framework to set up sysfs files for this region. Simply leave it alone.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="using uio_dmem_genirq">
|
||||
<sect1 id="using-uio_dmem_genirq">
|
||||
<title>Using uio_dmem_genirq for platform devices</title>
|
||||
<para>
|
||||
In addition to statically allocated memory ranges, they may also be
|
||||
@ -746,16 +746,16 @@ framework to set up sysfs files for this region. Simply leave it alone.
|
||||
following elements:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem><varname>struct uio_info uioinfo</varname>: The same
|
||||
<listitem><para><varname>struct uio_info uioinfo</varname>: The same
|
||||
structure used as the <varname>uio_pdrv_genirq</varname> platform
|
||||
data</listitem>
|
||||
<listitem><varname>unsigned int *dynamic_region_sizes</varname>:
|
||||
data</para></listitem>
|
||||
<listitem><para><varname>unsigned int *dynamic_region_sizes</varname>:
|
||||
Pointer to list of sizes of dynamic memory regions to be mapped into
|
||||
user space.
|
||||
</listitem>
|
||||
<listitem><varname>unsigned int num_dynamic_regions</varname>:
|
||||
</para></listitem>
|
||||
<listitem><para><varname>unsigned int num_dynamic_regions</varname>:
|
||||
Number of elements in <varname>dynamic_region_sizes</varname> array.
|
||||
</listitem>
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
The dynamic regions defined in the platform data will be appended to
|
||||
|
@ -38,8 +38,6 @@ its hardware characteristcs.
|
||||
AMBA markee):
|
||||
- "arm,coresight-replicator"
|
||||
|
||||
* id: a unique number that will identify this replicator.
|
||||
|
||||
* port or ports: same as above.
|
||||
|
||||
* Optional properties for ETM/PTMs:
|
||||
@ -94,8 +92,6 @@ Example:
|
||||
* AMBA bus. As such no need to add "arm,primecell".
|
||||
*/
|
||||
compatible = "arm,coresight-replicator";
|
||||
/* this will show up in debugfs as "0.replicator" */
|
||||
id = <0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
|
@ -16,20 +16,28 @@ Example:
|
||||
"fsl,pq2-localbus";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
reg = <f0010100 40>;
|
||||
reg = <0xf0010100 0x40>;
|
||||
|
||||
ranges = <0 0 fe000000 02000000
|
||||
1 0 f4500000 00008000>;
|
||||
ranges = <0x0 0x0 0xfe000000 0x02000000
|
||||
0x1 0x0 0xf4500000 0x00008000
|
||||
0x2 0x0 0xfd810000 0x00010000>;
|
||||
|
||||
flash@0,0 {
|
||||
compatible = "jedec-flash";
|
||||
reg = <0 0 2000000>;
|
||||
reg = <0x0 0x0 0x2000000>;
|
||||
bank-width = <4>;
|
||||
device-width = <1>;
|
||||
};
|
||||
|
||||
board-control@1,0 {
|
||||
reg = <1 0 20>;
|
||||
reg = <0x1 0x0 0x20>;
|
||||
compatible = "fsl,mpc8272ads-bcsr";
|
||||
};
|
||||
|
||||
simple-periph@2,0 {
|
||||
compatible = "fsl,elbc-gpcm-uio";
|
||||
reg = <0x2 0x0 0x10000>;
|
||||
elbc-gpcm-br = <0xfd810800>;
|
||||
elbc-gpcm-or = <0xffff09f7>;
|
||||
};
|
||||
};
|
||||
|
@ -32,18 +32,42 @@ Procedure for submitting patches to the -stable tree:
|
||||
- If the patch covers files in net/ or drivers/net please follow netdev stable
|
||||
submission guidelines as described in
|
||||
Documentation/networking/netdev-FAQ.txt
|
||||
- Send the patch, after verifying that it follows the above rules, to
|
||||
stable@vger.kernel.org. You must note the upstream commit ID in the
|
||||
changelog of your submission, as well as the kernel version you wish
|
||||
it to be applied to.
|
||||
- To have the patch automatically included in the stable tree, add the tag
|
||||
- Security patches should not be handled (solely) by the -stable review
|
||||
process but should follow the procedures in Documentation/SecurityBugs.
|
||||
|
||||
For all other submissions, choose one of the following procedures:
|
||||
|
||||
--- Option 1 ---
|
||||
|
||||
To have the patch automatically included in the stable tree, add the tag
|
||||
Cc: stable@vger.kernel.org
|
||||
in the sign-off area. Once the patch is merged it will be applied to
|
||||
the stable tree without anything else needing to be done by the author
|
||||
or subsystem maintainer.
|
||||
- If the patch requires other patches as prerequisites which can be
|
||||
cherry-picked, then this can be specified in the following format in
|
||||
the sign-off area:
|
||||
|
||||
--- Option 2 ---
|
||||
|
||||
After the patch has been merged to Linus' tree, send an email to
|
||||
stable@vger.kernel.org containing the subject of the patch, the commit ID,
|
||||
why you think it should be applied, and what kernel version you wish it to
|
||||
be applied to.
|
||||
|
||||
--- Option 3 ---
|
||||
|
||||
Send the patch, after verifying that it follows the above rules, to
|
||||
stable@vger.kernel.org. You must note the upstream commit ID in the
|
||||
changelog of your submission, as well as the kernel version you wish
|
||||
it to be applied to.
|
||||
|
||||
Option 1 is probably the easiest and most common. Options 2 and 3 are more
|
||||
useful if the patch isn't deemed worthy at the time it is applied to a public
|
||||
git tree (for instance, because it deserves more regression testing first).
|
||||
Option 3 is especially useful if the patch needs some special handling to apply
|
||||
to an older kernel (e.g., if API's have changed in the meantime).
|
||||
|
||||
Additionally, some patches submitted via Option 1 may have additional patch
|
||||
prerequisites which can be cherry-picked. This can be specified in the following
|
||||
format in the sign-off area:
|
||||
|
||||
Cc: <stable@vger.kernel.org> # 3.3.x: a1f84a3: sched: Check for idle
|
||||
Cc: <stable@vger.kernel.org> # 3.3.x: 1b9508f: sched: Rate-limit newidle
|
||||
@ -57,13 +81,13 @@ Procedure for submitting patches to the -stable tree:
|
||||
git cherry-pick fd21073
|
||||
git cherry-pick <this commit>
|
||||
|
||||
Following the submission:
|
||||
|
||||
- The sender will receive an ACK when the patch has been accepted into the
|
||||
queue, or a NAK if the patch is rejected. This response might take a few
|
||||
days, according to the developer's schedules.
|
||||
- If accepted, the patch will be added to the -stable queue, for review by
|
||||
other developers and by the relevant subsystem maintainer.
|
||||
- Security patches should not be sent to this alias, but instead to the
|
||||
documented security@kernel.org address.
|
||||
|
||||
|
||||
Review cycle:
|
||||
|
@ -46,7 +46,7 @@ At typical coresight system would look like this:
|
||||
| | . | ! | | . | ! | ! . | | SWD/
|
||||
| | . | ! | | . | ! | ! . | | JTAG
|
||||
*****************************************************************<-|
|
||||
*************************** AMBA Debug ABP ************************
|
||||
*************************** AMBA Debug APB ************************
|
||||
*****************************************************************
|
||||
| . ! . ! ! . |
|
||||
| . * . * * . |
|
||||
@ -79,7 +79,7 @@ At typical coresight system would look like this:
|
||||
To trace port TPIU= Trace Port Interface Unit
|
||||
SWD = Serial Wire Debug
|
||||
|
||||
While on target configuration of the components is done via the ABP bus,
|
||||
While on target configuration of the components is done via the APB bus,
|
||||
all trace data are carried out-of-band on the ATB bus. The CTM provides
|
||||
a way to aggregate and distribute signals between CoreSight components.
|
||||
|
||||
|
@ -3033,6 +3033,7 @@ F: drivers/platform/x86/dell-laptop.c
|
||||
|
||||
DELL LAPTOP SMM DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
S: Maintained
|
||||
F: drivers/char/i8k.c
|
||||
F: include/uapi/linux/i8k.h
|
||||
|
||||
|
@ -95,6 +95,9 @@ struct fsl_lbc_bank {
|
||||
#define OR_FCM_TRLX_SHIFT 2
|
||||
#define OR_FCM_EHTR 0x00000002
|
||||
#define OR_FCM_EHTR_SHIFT 1
|
||||
|
||||
#define OR_GPCM_AM 0xFFFF8000
|
||||
#define OR_GPCM_AM_SHIFT 15
|
||||
};
|
||||
|
||||
struct fsl_lbc_regs {
|
||||
|
@ -187,6 +187,17 @@
|
||||
#define HV_X64_MSR_SINT14 0x4000009E
|
||||
#define HV_X64_MSR_SINT15 0x4000009F
|
||||
|
||||
/*
|
||||
* Synthetic Timer MSRs. Four timers per vcpu.
|
||||
*/
|
||||
#define HV_X64_MSR_STIMER0_CONFIG 0x400000B0
|
||||
#define HV_X64_MSR_STIMER0_COUNT 0x400000B1
|
||||
#define HV_X64_MSR_STIMER1_CONFIG 0x400000B2
|
||||
#define HV_X64_MSR_STIMER1_COUNT 0x400000B3
|
||||
#define HV_X64_MSR_STIMER2_CONFIG 0x400000B4
|
||||
#define HV_X64_MSR_STIMER2_COUNT 0x400000B5
|
||||
#define HV_X64_MSR_STIMER3_CONFIG 0x400000B6
|
||||
#define HV_X64_MSR_STIMER3_COUNT 0x400000B7
|
||||
|
||||
#define HV_X64_MSR_HYPERCALL_ENABLE 0x00000001
|
||||
#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT 12
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pid_namespace.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
#ifdef CONFIG_ANDROID_BINDER_IPC_32BIT
|
||||
#define BINDER_IPC_32BIT 1
|
||||
@ -1400,6 +1401,11 @@ static void binder_transaction(struct binder_proc *proc,
|
||||
return_error = BR_DEAD_REPLY;
|
||||
goto err_dead_binder;
|
||||
}
|
||||
if (security_binder_transaction(proc->tsk,
|
||||
target_proc->tsk) < 0) {
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_invalid_target_handle;
|
||||
}
|
||||
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
|
||||
struct binder_transaction *tmp;
|
||||
|
||||
@ -1551,6 +1557,11 @@ static void binder_transaction(struct binder_proc *proc,
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_binder_get_ref_for_node_failed;
|
||||
}
|
||||
if (security_binder_transfer_binder(proc->tsk,
|
||||
target_proc->tsk)) {
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_binder_get_ref_for_node_failed;
|
||||
}
|
||||
ref = binder_get_ref_for_node(target_proc, node);
|
||||
if (ref == NULL) {
|
||||
return_error = BR_FAILED_REPLY;
|
||||
@ -1581,6 +1592,11 @@ static void binder_transaction(struct binder_proc *proc,
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_binder_get_ref_failed;
|
||||
}
|
||||
if (security_binder_transfer_binder(proc->tsk,
|
||||
target_proc->tsk)) {
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_binder_get_ref_failed;
|
||||
}
|
||||
if (ref->node->proc == target_proc) {
|
||||
if (fp->type == BINDER_TYPE_HANDLE)
|
||||
fp->type = BINDER_TYPE_BINDER;
|
||||
@ -1638,6 +1654,13 @@ static void binder_transaction(struct binder_proc *proc,
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_fget_failed;
|
||||
}
|
||||
if (security_binder_transfer_file(proc->tsk,
|
||||
target_proc->tsk,
|
||||
file) < 0) {
|
||||
fput(file);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_get_unused_fd_failed;
|
||||
}
|
||||
target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
|
||||
if (target_fd < 0) {
|
||||
fput(file);
|
||||
@ -2675,6 +2698,9 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp)
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
ret = security_binder_set_context_mgr(proc->tsk);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (uid_valid(binder_context_mgr_uid)) {
|
||||
if (!uid_eq(binder_context_mgr_uid, curr_euid)) {
|
||||
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
|
||||
|
@ -6,6 +6,15 @@ menu "Character devices"
|
||||
|
||||
source "drivers/tty/Kconfig"
|
||||
|
||||
config DEVMEM
|
||||
bool "/dev/mem virtual device support"
|
||||
default y
|
||||
help
|
||||
Say Y here if you want to support the /dev/mem device.
|
||||
The /dev/mem device is used to access areas of physical
|
||||
memory.
|
||||
When in doubt, say "Y".
|
||||
|
||||
config DEVKMEM
|
||||
bool "/dev/kmem virtual device support"
|
||||
default y
|
||||
|
@ -5,7 +5,8 @@
|
||||
*
|
||||
* Hwmon integration:
|
||||
* Copyright (C) 2011 Jean Delvare <jdelvare@suse.de>
|
||||
* Copyright (C) 2013 Guenter Roeck <linux@roeck-us.net>
|
||||
* Copyright (C) 2013, 2014 Guenter Roeck <linux@roeck-us.net>
|
||||
* Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
@ -20,6 +21,7 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
@ -41,11 +43,15 @@
|
||||
#define I8K_SMM_SET_FAN 0x01a3
|
||||
#define I8K_SMM_GET_FAN 0x00a3
|
||||
#define I8K_SMM_GET_SPEED 0x02a3
|
||||
#define I8K_SMM_GET_FAN_TYPE 0x03a3
|
||||
#define I8K_SMM_GET_NOM_SPEED 0x04a3
|
||||
#define I8K_SMM_GET_TEMP 0x10a3
|
||||
#define I8K_SMM_GET_TEMP_TYPE 0x11a3
|
||||
#define I8K_SMM_GET_DELL_SIG1 0xfea3
|
||||
#define I8K_SMM_GET_DELL_SIG2 0xffa3
|
||||
|
||||
#define I8K_FAN_MULT 30
|
||||
#define I8K_FAN_MAX_RPM 30000
|
||||
#define I8K_MAX_TEMP 127
|
||||
|
||||
#define I8K_FN_NONE 0x00
|
||||
@ -58,15 +64,13 @@
|
||||
#define I8K_POWER_AC 0x05
|
||||
#define I8K_POWER_BATTERY 0x01
|
||||
|
||||
#define I8K_TEMPERATURE_BUG 1
|
||||
|
||||
static DEFINE_MUTEX(i8k_mutex);
|
||||
static char bios_version[4];
|
||||
static struct device *i8k_hwmon_dev;
|
||||
static u32 i8k_hwmon_flags;
|
||||
static int i8k_fan_mult;
|
||||
static int i8k_pwm_mult;
|
||||
static int i8k_fan_max = I8K_FAN_HIGH;
|
||||
static uint i8k_fan_mult = I8K_FAN_MULT;
|
||||
static uint i8k_pwm_mult;
|
||||
static uint i8k_fan_max = I8K_FAN_HIGH;
|
||||
|
||||
#define I8K_HWMON_HAVE_TEMP1 (1 << 0)
|
||||
#define I8K_HWMON_HAVE_TEMP2 (1 << 1)
|
||||
@ -95,13 +99,13 @@ static bool power_status;
|
||||
module_param(power_status, bool, 0600);
|
||||
MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k");
|
||||
|
||||
static int fan_mult = I8K_FAN_MULT;
|
||||
module_param(fan_mult, int, 0);
|
||||
MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with");
|
||||
static uint fan_mult;
|
||||
module_param(fan_mult, uint, 0);
|
||||
MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)");
|
||||
|
||||
static int fan_max = I8K_FAN_HIGH;
|
||||
module_param(fan_max, int, 0);
|
||||
MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed");
|
||||
static uint fan_max;
|
||||
module_param(fan_max, uint, 0);
|
||||
MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)");
|
||||
|
||||
static int i8k_open_fs(struct inode *inode, struct file *file);
|
||||
static long i8k_ioctl(struct file *, unsigned int, unsigned long);
|
||||
@ -275,6 +279,28 @@ static int i8k_get_fan_speed(int fan)
|
||||
return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the fan type.
|
||||
*/
|
||||
static int i8k_get_fan_type(int fan)
|
||||
{
|
||||
struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, };
|
||||
|
||||
regs.ebx = fan & 0xff;
|
||||
return i8k_smm(®s) ? : regs.eax & 0xff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the fan nominal rpm for specific fan speed.
|
||||
*/
|
||||
static int i8k_get_fan_nominal_speed(int fan, int speed)
|
||||
{
|
||||
struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, };
|
||||
|
||||
regs.ebx = (fan & 0xff) | (speed << 8);
|
||||
return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the fan speed (off, low, high). Returns the new fan status.
|
||||
*/
|
||||
@ -288,42 +314,52 @@ static int i8k_set_fan(int fan, int speed)
|
||||
return i8k_smm(®s) ? : i8k_get_fan_status(fan);
|
||||
}
|
||||
|
||||
static int i8k_get_temp_type(int sensor)
|
||||
{
|
||||
struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP_TYPE, };
|
||||
|
||||
regs.ebx = sensor & 0xff;
|
||||
return i8k_smm(®s) ? : regs.eax & 0xff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the cpu temperature.
|
||||
*/
|
||||
static int _i8k_get_temp(int sensor)
|
||||
{
|
||||
struct smm_regs regs = {
|
||||
.eax = I8K_SMM_GET_TEMP,
|
||||
.ebx = sensor & 0xff,
|
||||
};
|
||||
|
||||
return i8k_smm(®s) ? : regs.eax & 0xff;
|
||||
}
|
||||
|
||||
static int i8k_get_temp(int sensor)
|
||||
{
|
||||
struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP, };
|
||||
int rc;
|
||||
int temp;
|
||||
int temp = _i8k_get_temp(sensor);
|
||||
|
||||
#ifdef I8K_TEMPERATURE_BUG
|
||||
static int prev[4] = { I8K_MAX_TEMP+1, I8K_MAX_TEMP+1, I8K_MAX_TEMP+1, I8K_MAX_TEMP+1 };
|
||||
#endif
|
||||
regs.ebx = sensor & 0xff;
|
||||
rc = i8k_smm(®s);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
temp = regs.eax & 0xff;
|
||||
|
||||
#ifdef I8K_TEMPERATURE_BUG
|
||||
/*
|
||||
* Sometimes the temperature sensor returns 0x99, which is out of range.
|
||||
* In this case we return (once) the previous cached value. For example:
|
||||
* In this case we retry (once) before returning an error.
|
||||
# 1003655137 00000058 00005a4b
|
||||
# 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees
|
||||
# 1003655139 00000054 00005c52
|
||||
*/
|
||||
if (temp > I8K_MAX_TEMP) {
|
||||
temp = prev[sensor];
|
||||
prev[sensor] = I8K_MAX_TEMP+1;
|
||||
} else {
|
||||
prev[sensor] = temp;
|
||||
if (temp == 0x99) {
|
||||
msleep(100);
|
||||
temp = _i8k_get_temp(sensor);
|
||||
}
|
||||
/*
|
||||
* Return -ENODATA for all invalid temperatures.
|
||||
*
|
||||
* Known instances are the 0x99 value as seen above as well as
|
||||
* 0xc1 (193), which may be returned when trying to read the GPU
|
||||
* temperature if the system supports a GPU and it is currently
|
||||
* turned off.
|
||||
*/
|
||||
if (temp > I8K_MAX_TEMP)
|
||||
return -ERANGE;
|
||||
#endif
|
||||
return -ENODATA;
|
||||
|
||||
return temp;
|
||||
}
|
||||
@ -493,6 +529,29 @@ static int i8k_open_fs(struct inode *inode, struct file *file)
|
||||
* Hwmon interface
|
||||
*/
|
||||
|
||||
static ssize_t i8k_hwmon_show_temp_label(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
static const char * const labels[] = {
|
||||
"CPU",
|
||||
"GPU",
|
||||
"SODIMM",
|
||||
"Other",
|
||||
"Ambient",
|
||||
"Other",
|
||||
};
|
||||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
int type;
|
||||
|
||||
type = i8k_get_temp_type(index);
|
||||
if (type < 0)
|
||||
return type;
|
||||
if (type >= ARRAY_SIZE(labels))
|
||||
type = ARRAY_SIZE(labels) - 1;
|
||||
return sprintf(buf, "%s\n", labels[type]);
|
||||
}
|
||||
|
||||
static ssize_t i8k_hwmon_show_temp(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
char *buf)
|
||||
@ -501,13 +560,42 @@ static ssize_t i8k_hwmon_show_temp(struct device *dev,
|
||||
int temp;
|
||||
|
||||
temp = i8k_get_temp(index);
|
||||
if (temp == -ERANGE)
|
||||
return -EINVAL;
|
||||
if (temp < 0)
|
||||
return temp;
|
||||
return sprintf(buf, "%d\n", temp * 1000);
|
||||
}
|
||||
|
||||
static ssize_t i8k_hwmon_show_fan_label(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
static const char * const labels[] = {
|
||||
"Processor Fan",
|
||||
"Motherboard Fan",
|
||||
"Video Fan",
|
||||
"Power Supply Fan",
|
||||
"Chipset Fan",
|
||||
"Other Fan",
|
||||
};
|
||||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
bool dock = false;
|
||||
int type;
|
||||
|
||||
type = i8k_get_fan_type(index);
|
||||
if (type < 0)
|
||||
return type;
|
||||
|
||||
if (type & 0x10) {
|
||||
dock = true;
|
||||
type &= 0x0F;
|
||||
}
|
||||
|
||||
if (type >= ARRAY_SIZE(labels))
|
||||
type = (ARRAY_SIZE(labels) - 1);
|
||||
|
||||
return sprintf(buf, "%s%s\n", (dock ? "Docking " : ""), labels[type]);
|
||||
}
|
||||
|
||||
static ssize_t i8k_hwmon_show_fan(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
char *buf)
|
||||
@ -555,45 +643,66 @@ static ssize_t i8k_hwmon_set_pwm(struct device *dev,
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL,
|
||||
0);
|
||||
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL,
|
||||
1);
|
||||
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL,
|
||||
2);
|
||||
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
|
||||
I8K_FAN_LEFT);
|
||||
static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL,
|
||||
3);
|
||||
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_fan_label, NULL,
|
||||
0);
|
||||
static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm,
|
||||
i8k_hwmon_set_pwm, I8K_FAN_LEFT);
|
||||
i8k_hwmon_set_pwm, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
|
||||
I8K_FAN_RIGHT);
|
||||
1);
|
||||
static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_fan_label, NULL,
|
||||
1);
|
||||
static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm,
|
||||
i8k_hwmon_set_pwm, I8K_FAN_RIGHT);
|
||||
i8k_hwmon_set_pwm, 1);
|
||||
|
||||
static struct attribute *i8k_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr, /* 1 */
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr, /* 2 */
|
||||
&sensor_dev_attr_temp4_input.dev_attr.attr, /* 3 */
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr, /* 4 */
|
||||
&sensor_dev_attr_pwm1.dev_attr.attr, /* 5 */
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr, /* 6 */
|
||||
&sensor_dev_attr_pwm2.dev_attr.attr, /* 7 */
|
||||
&sensor_dev_attr_temp1_label.dev_attr.attr, /* 1 */
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr, /* 2 */
|
||||
&sensor_dev_attr_temp2_label.dev_attr.attr, /* 3 */
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr, /* 4 */
|
||||
&sensor_dev_attr_temp3_label.dev_attr.attr, /* 5 */
|
||||
&sensor_dev_attr_temp4_input.dev_attr.attr, /* 6 */
|
||||
&sensor_dev_attr_temp4_label.dev_attr.attr, /* 7 */
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr, /* 8 */
|
||||
&sensor_dev_attr_fan1_label.dev_attr.attr, /* 9 */
|
||||
&sensor_dev_attr_pwm1.dev_attr.attr, /* 10 */
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr, /* 11 */
|
||||
&sensor_dev_attr_fan2_label.dev_attr.attr, /* 12 */
|
||||
&sensor_dev_attr_pwm2.dev_attr.attr, /* 13 */
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
|
||||
int index)
|
||||
{
|
||||
if (index == 0 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
|
||||
if (index >= 0 && index <= 1 &&
|
||||
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
|
||||
return 0;
|
||||
if (index == 1 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
|
||||
return 0;
|
||||
if (index == 2 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
|
||||
return 0;
|
||||
if (index == 3 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
|
||||
if (index >= 2 && index <= 3 &&
|
||||
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
|
||||
return 0;
|
||||
if (index >= 4 && index <= 5 &&
|
||||
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1))
|
||||
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
|
||||
return 0;
|
||||
if (index >= 6 && index <= 7 &&
|
||||
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
|
||||
return 0;
|
||||
if (index >= 8 && index <= 10 &&
|
||||
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1))
|
||||
return 0;
|
||||
if (index >= 11 && index <= 13 &&
|
||||
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2))
|
||||
return 0;
|
||||
|
||||
@ -612,28 +721,28 @@ static int __init i8k_init_hwmon(void)
|
||||
|
||||
i8k_hwmon_flags = 0;
|
||||
|
||||
/* CPU temperature attributes, if temperature reading is OK */
|
||||
err = i8k_get_temp(0);
|
||||
if (err >= 0 || err == -ERANGE)
|
||||
/* CPU temperature attributes, if temperature type is OK */
|
||||
err = i8k_get_temp_type(0);
|
||||
if (err >= 0)
|
||||
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP1;
|
||||
/* check for additional temperature sensors */
|
||||
err = i8k_get_temp(1);
|
||||
if (err >= 0 || err == -ERANGE)
|
||||
err = i8k_get_temp_type(1);
|
||||
if (err >= 0)
|
||||
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP2;
|
||||
err = i8k_get_temp(2);
|
||||
if (err >= 0 || err == -ERANGE)
|
||||
err = i8k_get_temp_type(2);
|
||||
if (err >= 0)
|
||||
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP3;
|
||||
err = i8k_get_temp(3);
|
||||
if (err >= 0 || err == -ERANGE)
|
||||
err = i8k_get_temp_type(3);
|
||||
if (err >= 0)
|
||||
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4;
|
||||
|
||||
/* Left fan attributes, if left fan is present */
|
||||
err = i8k_get_fan_status(I8K_FAN_LEFT);
|
||||
/* First fan attributes, if fan type is OK */
|
||||
err = i8k_get_fan_type(0);
|
||||
if (err >= 0)
|
||||
i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1;
|
||||
|
||||
/* Right fan attributes, if right fan is present */
|
||||
err = i8k_get_fan_status(I8K_FAN_RIGHT);
|
||||
/* Second fan attributes, if fan type is OK */
|
||||
err = i8k_get_fan_type(1);
|
||||
if (err >= 0)
|
||||
i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2;
|
||||
|
||||
@ -649,16 +758,15 @@ static int __init i8k_init_hwmon(void)
|
||||
}
|
||||
|
||||
struct i8k_config_data {
|
||||
int fan_mult;
|
||||
int fan_max;
|
||||
uint fan_mult;
|
||||
uint fan_max;
|
||||
};
|
||||
|
||||
enum i8k_configs {
|
||||
DELL_LATITUDE_D520,
|
||||
DELL_LATITUDE_E6540,
|
||||
DELL_PRECISION_490,
|
||||
DELL_STUDIO,
|
||||
DELL_XPS_M140,
|
||||
DELL_XPS,
|
||||
};
|
||||
|
||||
static const struct i8k_config_data i8k_config_data[] = {
|
||||
@ -666,10 +774,6 @@ static const struct i8k_config_data i8k_config_data[] = {
|
||||
.fan_mult = 1,
|
||||
.fan_max = I8K_FAN_TURBO,
|
||||
},
|
||||
[DELL_LATITUDE_E6540] = {
|
||||
.fan_mult = 1,
|
||||
.fan_max = I8K_FAN_HIGH,
|
||||
},
|
||||
[DELL_PRECISION_490] = {
|
||||
.fan_mult = 1,
|
||||
.fan_max = I8K_FAN_TURBO,
|
||||
@ -678,7 +782,7 @@ static const struct i8k_config_data i8k_config_data[] = {
|
||||
.fan_mult = 1,
|
||||
.fan_max = I8K_FAN_HIGH,
|
||||
},
|
||||
[DELL_XPS_M140] = {
|
||||
[DELL_XPS] = {
|
||||
.fan_mult = 1,
|
||||
.fan_max = I8K_FAN_HIGH,
|
||||
},
|
||||
@ -714,22 +818,6 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = {
|
||||
},
|
||||
.driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520],
|
||||
},
|
||||
{
|
||||
.ident = "Dell Latitude E6440",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6440"),
|
||||
},
|
||||
.driver_data = (void *)&i8k_config_data[DELL_LATITUDE_E6540],
|
||||
},
|
||||
{
|
||||
.ident = "Dell Latitude E6540",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6540"),
|
||||
},
|
||||
.driver_data = (void *)&i8k_config_data[DELL_LATITUDE_E6540],
|
||||
},
|
||||
{
|
||||
.ident = "Dell Latitude 2",
|
||||
.matches = {
|
||||
@ -789,13 +877,21 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = {
|
||||
},
|
||||
.driver_data = (void *)&i8k_config_data[DELL_STUDIO],
|
||||
},
|
||||
{
|
||||
.ident = "Dell XPS 13",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "XPS13"),
|
||||
},
|
||||
.driver_data = (void *)&i8k_config_data[DELL_XPS],
|
||||
},
|
||||
{
|
||||
.ident = "Dell XPS M140",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"),
|
||||
},
|
||||
.driver_data = (void *)&i8k_config_data[DELL_XPS_M140],
|
||||
.driver_data = (void *)&i8k_config_data[DELL_XPS],
|
||||
},
|
||||
{ }
|
||||
};
|
||||
@ -808,6 +904,7 @@ MODULE_DEVICE_TABLE(dmi, i8k_dmi_table);
|
||||
static int __init i8k_probe(void)
|
||||
{
|
||||
const struct dmi_system_id *id;
|
||||
int fan, ret;
|
||||
|
||||
/*
|
||||
* Get DMI information
|
||||
@ -836,19 +933,40 @@ static int __init i8k_probe(void)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
i8k_fan_mult = fan_mult;
|
||||
i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */
|
||||
/*
|
||||
* Set fan multiplier and maximal fan speed from dmi config
|
||||
* Values specified in module parameters override values from dmi
|
||||
*/
|
||||
id = dmi_first_match(i8k_dmi_table);
|
||||
if (id && id->driver_data) {
|
||||
const struct i8k_config_data *conf = id->driver_data;
|
||||
|
||||
if (fan_mult == I8K_FAN_MULT && conf->fan_mult)
|
||||
i8k_fan_mult = conf->fan_mult;
|
||||
if (fan_max == I8K_FAN_HIGH && conf->fan_max)
|
||||
i8k_fan_max = conf->fan_max;
|
||||
if (!fan_mult && conf->fan_mult)
|
||||
fan_mult = conf->fan_mult;
|
||||
if (!fan_max && conf->fan_max)
|
||||
fan_max = conf->fan_max;
|
||||
}
|
||||
|
||||
i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */
|
||||
i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max);
|
||||
|
||||
if (!fan_mult) {
|
||||
/*
|
||||
* Autodetect fan multiplier based on nominal rpm
|
||||
* If fan reports rpm value too high then set multiplier to 1
|
||||
*/
|
||||
for (fan = 0; fan < 2; ++fan) {
|
||||
ret = i8k_get_fan_nominal_speed(fan, i8k_fan_max);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
if (ret > I8K_FAN_MAX_RPM)
|
||||
i8k_fan_mult = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Fan multiplier was specified in module param or in dmi */
|
||||
i8k_fan_mult = fan_mult;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/aio.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#ifdef CONFIG_IA64
|
||||
# include <linux/efi.h>
|
||||
@ -352,7 +352,6 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEVKMEM
|
||||
static int mmap_kmem(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
unsigned long pfn;
|
||||
@ -373,9 +372,7 @@ static int mmap_kmem(struct file *file, struct vm_area_struct *vma)
|
||||
vma->vm_pgoff = pfn;
|
||||
return mmap_mem(file, vma);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEVKMEM
|
||||
/*
|
||||
* This function reads the *virtual* memory as seen by the kernel.
|
||||
*/
|
||||
@ -555,9 +552,7 @@ static ssize_t write_kmem(struct file *file, const char __user *buf,
|
||||
*ppos = p;
|
||||
return virtr + wrote ? : err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEVPORT
|
||||
static ssize_t read_port(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
@ -586,6 +581,7 @@ static ssize_t write_port(struct file *file, const char __user *buf,
|
||||
return -EFAULT;
|
||||
while (count-- > 0 && i < 65536) {
|
||||
char c;
|
||||
|
||||
if (__get_user(c, tmp)) {
|
||||
if (tmp > buf)
|
||||
break;
|
||||
@ -598,7 +594,6 @@ static ssize_t write_port(struct file *file, const char __user *buf,
|
||||
*ppos = i;
|
||||
return tmp-buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t read_null(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
@ -642,6 +637,7 @@ static ssize_t read_iter_zero(struct kiocb *iocb, struct iov_iter *iter)
|
||||
|
||||
while (iov_iter_count(iter)) {
|
||||
size_t chunk = iov_iter_count(iter), n;
|
||||
|
||||
if (chunk > PAGE_SIZE)
|
||||
chunk = PAGE_SIZE; /* Just for latency reasons */
|
||||
n = iov_iter_zero(chunk, iter);
|
||||
@ -726,7 +722,7 @@ static int open_port(struct inode *inode, struct file *filp)
|
||||
#define open_mem open_port
|
||||
#define open_kmem open_mem
|
||||
|
||||
static const struct file_operations mem_fops = {
|
||||
static const struct file_operations __maybe_unused mem_fops = {
|
||||
.llseek = memory_lseek,
|
||||
.read = read_mem,
|
||||
.write = write_mem,
|
||||
@ -738,8 +734,7 @@ static const struct file_operations mem_fops = {
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEVKMEM
|
||||
static const struct file_operations kmem_fops = {
|
||||
static const struct file_operations __maybe_unused kmem_fops = {
|
||||
.llseek = memory_lseek,
|
||||
.read = read_kmem,
|
||||
.write = write_kmem,
|
||||
@ -750,7 +745,6 @@ static const struct file_operations kmem_fops = {
|
||||
.mmap_capabilities = memory_mmap_capabilities,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct file_operations null_fops = {
|
||||
.llseek = null_lseek,
|
||||
@ -761,14 +755,12 @@ static const struct file_operations null_fops = {
|
||||
.splice_write = splice_write_null,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEVPORT
|
||||
static const struct file_operations port_fops = {
|
||||
static const struct file_operations __maybe_unused port_fops = {
|
||||
.llseek = memory_lseek,
|
||||
.read = read_port,
|
||||
.write = write_port,
|
||||
.open = open_port,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct file_operations zero_fops = {
|
||||
.llseek = zero_lseek,
|
||||
@ -795,7 +787,9 @@ static const struct memdev {
|
||||
const struct file_operations *fops;
|
||||
fmode_t fmode;
|
||||
} devlist[] = {
|
||||
#ifdef CONFIG_DEVMEM
|
||||
[1] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET },
|
||||
#endif
|
||||
#ifdef CONFIG_DEVKMEM
|
||||
[2] = { "kmem", 0, &kmem_fops, FMODE_UNSIGNED_OFFSET },
|
||||
#endif
|
||||
|
@ -1986,6 +1986,12 @@ static int virtcons_probe(struct virtio_device *vdev)
|
||||
bool multiport;
|
||||
bool early = early_put_chars != NULL;
|
||||
|
||||
if (!vdev->config->get) {
|
||||
dev_err(&vdev->dev, "%s failure: config access disabled\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Ensure to read early_put_chars now */
|
||||
barrier();
|
||||
|
||||
|
@ -454,7 +454,7 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drvdata->buffer_depth = etb_get_buffer_depth(drvdata);
|
||||
drvdata->buffer_depth = etb_get_buffer_depth(drvdata);
|
||||
clk_disable_unprepare(drvdata->clk);
|
||||
|
||||
if (drvdata->buffer_depth < 0)
|
||||
@ -521,17 +521,7 @@ static struct amba_driver etb_driver = {
|
||||
.id_table = etb_ids,
|
||||
};
|
||||
|
||||
static int __init etb_init(void)
|
||||
{
|
||||
return amba_driver_register(&etb_driver);
|
||||
}
|
||||
module_init(etb_init);
|
||||
|
||||
static void __exit etb_exit(void)
|
||||
{
|
||||
amba_driver_unregister(&etb_driver);
|
||||
}
|
||||
module_exit(etb_exit);
|
||||
module_amba_driver(etb_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("CoreSight Embedded Trace Buffer driver");
|
||||
|
@ -34,14 +34,8 @@
|
||||
|
||||
#include "coresight-etm.h"
|
||||
|
||||
#ifdef CONFIG_CORESIGHT_SOURCE_ETM_DEFAULT_ENABLE
|
||||
static int boot_enable = 1;
|
||||
#else
|
||||
static int boot_enable;
|
||||
#endif
|
||||
module_param_named(
|
||||
boot_enable, boot_enable, int, S_IRUGO
|
||||
);
|
||||
module_param_named(boot_enable, boot_enable, int, S_IRUGO);
|
||||
|
||||
/* The number of ETM/PTM currently registered */
|
||||
static int etm_count;
|
||||
@ -573,7 +567,8 @@ static ssize_t mode_store(struct device *dev,
|
||||
if (drvdata->mode & ETM_MODE_STALL) {
|
||||
if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) {
|
||||
dev_warn(drvdata->dev, "stall mode not supported\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err_unlock;
|
||||
}
|
||||
drvdata->ctrl |= ETMCR_STALL_MODE;
|
||||
} else
|
||||
@ -582,7 +577,8 @@ static ssize_t mode_store(struct device *dev,
|
||||
if (drvdata->mode & ETM_MODE_TIMESTAMP) {
|
||||
if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) {
|
||||
dev_warn(drvdata->dev, "timestamp not supported\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err_unlock;
|
||||
}
|
||||
drvdata->ctrl |= ETMCR_TIMESTAMP_EN;
|
||||
} else
|
||||
@ -595,6 +591,10 @@ static ssize_t mode_store(struct device *dev,
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
|
||||
return size;
|
||||
|
||||
err_unlock:
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR_RW(mode);
|
||||
|
||||
@ -1743,7 +1743,11 @@ static void etm_init_arch_data(void *info)
|
||||
|
||||
static void etm_init_default_data(struct etm_drvdata *drvdata)
|
||||
{
|
||||
static int etm3x_traceid;
|
||||
/*
|
||||
* A trace ID of value 0 is invalid, so let's start at some
|
||||
* random value that fits in 7 bits and will be just as good.
|
||||
*/
|
||||
static int etm3x_traceid = 0x10;
|
||||
|
||||
u32 flags = (1 << 0 | /* instruction execute*/
|
||||
3 << 3 | /* ARM instruction */
|
||||
|
@ -252,17 +252,7 @@ static struct amba_driver funnel_driver = {
|
||||
.id_table = funnel_ids,
|
||||
};
|
||||
|
||||
static int __init funnel_init(void)
|
||||
{
|
||||
return amba_driver_register(&funnel_driver);
|
||||
}
|
||||
module_init(funnel_init);
|
||||
|
||||
static void __exit funnel_exit(void)
|
||||
{
|
||||
amba_driver_unregister(&funnel_driver);
|
||||
}
|
||||
module_exit(funnel_exit);
|
||||
module_amba_driver(funnel_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("CoreSight Funnel driver");
|
||||
|
@ -57,7 +57,7 @@ extern int etm_readl_cp14(u32 off, unsigned int *val);
|
||||
extern int etm_writel_cp14(u32 off, u32 val);
|
||||
#else
|
||||
static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; }
|
||||
static inline int etm_writel_cp14(u32 val, u32 off) { return 0; }
|
||||
static inline int etm_writel_cp14(u32 off, u32 val) { return 0; }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -87,7 +87,7 @@ static int replicator_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_LINK;
|
||||
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
|
||||
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
|
||||
desc->ops = &replicator_cs_ops;
|
||||
desc->pdata = pdev->dev.platform_data;
|
||||
desc->dev = &pdev->dev;
|
||||
|
@ -760,17 +760,7 @@ static struct amba_driver tmc_driver = {
|
||||
.id_table = tmc_ids,
|
||||
};
|
||||
|
||||
static int __init tmc_init(void)
|
||||
{
|
||||
return amba_driver_register(&tmc_driver);
|
||||
}
|
||||
module_init(tmc_init);
|
||||
|
||||
static void __exit tmc_exit(void)
|
||||
{
|
||||
amba_driver_unregister(&tmc_driver);
|
||||
}
|
||||
module_exit(tmc_exit);
|
||||
module_amba_driver(tmc_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("CoreSight Trace Memory Controller driver");
|
||||
|
@ -201,17 +201,7 @@ static struct amba_driver tpiu_driver = {
|
||||
.id_table = tpiu_ids,
|
||||
};
|
||||
|
||||
static int __init tpiu_init(void)
|
||||
{
|
||||
return amba_driver_register(&tpiu_driver);
|
||||
}
|
||||
module_init(tpiu_init);
|
||||
|
||||
static void __exit tpiu_exit(void)
|
||||
{
|
||||
amba_driver_unregister(&tpiu_driver);
|
||||
}
|
||||
module_exit(tpiu_exit);
|
||||
module_amba_driver(tpiu_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("CoreSight Trace Port Interface Unit driver");
|
||||
|
@ -498,17 +498,18 @@ static int coresight_orphan_match(struct device *dev, void *data)
|
||||
* Circle throuch all the connection of that component. If we find
|
||||
* an orphan connection whose name matches @csdev, link it.
|
||||
*/
|
||||
for (i = 0; i < i_csdev->nr_outport; i++) {
|
||||
for (i = 0; i < i_csdev->nr_outport; i++) {
|
||||
conn = &i_csdev->conns[i];
|
||||
|
||||
/* We have found at least one orphan connection */
|
||||
if (conn->child_dev == NULL) {
|
||||
/* Does it match this newly added device? */
|
||||
if (!strcmp(dev_name(&csdev->dev), conn->child_name))
|
||||
if (!strcmp(dev_name(&csdev->dev), conn->child_name)) {
|
||||
conn->child_dev = csdev;
|
||||
} else {
|
||||
/* Too bad, this component still has an orphan */
|
||||
still_orphan = true;
|
||||
} else {
|
||||
/* This component still has an orphan */
|
||||
still_orphan = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ static int of_coresight_alloc_memory(struct device *dev,
|
||||
if (!pdata->outports)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Children connected to this component via @outport */
|
||||
/* Children connected to this component via @outports */
|
||||
pdata->child_names = devm_kzalloc(dev, pdata->nr_outport *
|
||||
sizeof(*pdata->child_names),
|
||||
GFP_KERNEL);
|
||||
@ -117,7 +117,7 @@ struct coresight_platform_data *of_get_coresight_platform_data(
|
||||
struct coresight_platform_data *pdata;
|
||||
struct of_endpoint endpoint, rendpoint;
|
||||
struct device *rdev;
|
||||
struct device_node *cpu;
|
||||
struct device_node *dn;
|
||||
struct device_node *ep = NULL;
|
||||
struct device_node *rparent = NULL;
|
||||
struct device_node *rport = NULL;
|
||||
@ -126,7 +126,7 @@ struct coresight_platform_data *of_get_coresight_platform_data(
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Use device name as debugfs handle */
|
||||
/* Use device name as sysfs handle */
|
||||
pdata->name = dev_name(dev);
|
||||
|
||||
/* Get the number of input and output port for this component */
|
||||
@ -174,7 +174,7 @@ struct coresight_platform_data *of_get_coresight_platform_data(
|
||||
continue;
|
||||
|
||||
rdev = of_coresight_get_endpoint_device(rparent);
|
||||
if (!dev)
|
||||
if (!rdev)
|
||||
continue;
|
||||
|
||||
pdata->child_names[i] = dev_name(rdev);
|
||||
@ -186,14 +186,16 @@ struct coresight_platform_data *of_get_coresight_platform_data(
|
||||
|
||||
/* Affinity defaults to CPU0 */
|
||||
pdata->cpu = 0;
|
||||
cpu = of_parse_phandle(node, "cpu", 0);
|
||||
if (cpu) {
|
||||
const u32 *mpidr;
|
||||
dn = of_parse_phandle(node, "cpu", 0);
|
||||
if (dn) {
|
||||
const u32 *cell;
|
||||
int len, index;
|
||||
u64 hwid;
|
||||
|
||||
mpidr = of_get_property(cpu, "reg", &len);
|
||||
if (mpidr && len == 4) {
|
||||
index = get_logical_index(be32_to_cpup(mpidr));
|
||||
cell = of_get_property(dn, "reg", &len);
|
||||
if (cell) {
|
||||
hwid = of_read_number(cell, of_n_addr_cells(dn));
|
||||
index = get_logical_index(hwid);
|
||||
if (index != -EINVAL)
|
||||
pdata->cpu = index;
|
||||
}
|
||||
|
@ -173,6 +173,7 @@ static int adc_jack_remove(struct platform_device *pdev)
|
||||
|
||||
free_irq(data->irq, data);
|
||||
cancel_work_sync(&data->handler.work);
|
||||
iio_channel_release(data->chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/*
|
||||
* extcon_cable_name suggests the standard cable names for commonly used
|
||||
|
@ -1033,7 +1033,7 @@ static irqreturn_t max77693_muic_irq_handler(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct regmap_config max77693_muic_regmap_config = {
|
||||
static const struct regmap_config max77693_muic_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
@ -73,14 +73,14 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
|
||||
unsigned long flags;
|
||||
int ret, t, err = 0;
|
||||
|
||||
spin_lock_irqsave(&newchannel->sc_lock, flags);
|
||||
spin_lock_irqsave(&newchannel->lock, flags);
|
||||
if (newchannel->state == CHANNEL_OPEN_STATE) {
|
||||
newchannel->state = CHANNEL_OPENING_STATE;
|
||||
} else {
|
||||
spin_unlock_irqrestore(&newchannel->sc_lock, flags);
|
||||
spin_unlock_irqrestore(&newchannel->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
spin_unlock_irqrestore(&newchannel->sc_lock, flags);
|
||||
spin_unlock_irqrestore(&newchannel->lock, flags);
|
||||
|
||||
newchannel->onchannel_callback = onchannelcallback;
|
||||
newchannel->channel_callback_context = context;
|
||||
@ -366,8 +366,8 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
next_gpadl_handle = atomic_read(&vmbus_connection.next_gpadl_handle);
|
||||
atomic_inc(&vmbus_connection.next_gpadl_handle);
|
||||
next_gpadl_handle =
|
||||
(atomic_inc_return(&vmbus_connection.next_gpadl_handle) - 1);
|
||||
|
||||
ret = create_gpadl_header(kbuffer, size, &msginfo, &msgcount);
|
||||
if (ret)
|
||||
@ -683,6 +683,50 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer);
|
||||
|
||||
/*
|
||||
* vmbus_sendpacket_multipagebuffer - Send a multi-page buffer packet
|
||||
* using a GPADL Direct packet type.
|
||||
* The buffer includes the vmbus descriptor.
|
||||
*/
|
||||
int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
|
||||
struct vmbus_packet_mpb_array *desc,
|
||||
u32 desc_size,
|
||||
void *buffer, u32 bufferlen, u64 requestid)
|
||||
{
|
||||
int ret;
|
||||
u32 packetlen;
|
||||
u32 packetlen_aligned;
|
||||
struct kvec bufferlist[3];
|
||||
u64 aligned_data = 0;
|
||||
bool signal = false;
|
||||
|
||||
packetlen = desc_size + bufferlen;
|
||||
packetlen_aligned = ALIGN(packetlen, sizeof(u64));
|
||||
|
||||
/* Setup the descriptor */
|
||||
desc->type = VM_PKT_DATA_USING_GPA_DIRECT;
|
||||
desc->flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
|
||||
desc->dataoffset8 = desc_size >> 3; /* in 8-bytes grandularity */
|
||||
desc->length8 = (u16)(packetlen_aligned >> 3);
|
||||
desc->transactionid = requestid;
|
||||
desc->rangecount = 1;
|
||||
|
||||
bufferlist[0].iov_base = desc;
|
||||
bufferlist[0].iov_len = desc_size;
|
||||
bufferlist[1].iov_base = buffer;
|
||||
bufferlist[1].iov_len = bufferlen;
|
||||
bufferlist[2].iov_base = &aligned_data;
|
||||
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
|
||||
|
||||
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
|
||||
|
||||
if (ret == 0 && signal)
|
||||
vmbus_setevent(channel);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vmbus_sendpacket_mpb_desc);
|
||||
|
||||
/*
|
||||
* vmbus_sendpacket_multipagebuffer - Send a multi-page buffer packet
|
||||
* using a GPADL Direct packet type.
|
||||
|
@ -146,7 +146,7 @@ static struct vmbus_channel *alloc_channel(void)
|
||||
return NULL;
|
||||
|
||||
spin_lock_init(&channel->inbound_lock);
|
||||
spin_lock_init(&channel->sc_lock);
|
||||
spin_lock_init(&channel->lock);
|
||||
|
||||
INIT_LIST_HEAD(&channel->sc_list);
|
||||
INIT_LIST_HEAD(&channel->percpu_list);
|
||||
@ -246,9 +246,9 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
|
||||
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
|
||||
} else {
|
||||
primary_channel = channel->primary_channel;
|
||||
spin_lock_irqsave(&primary_channel->sc_lock, flags);
|
||||
spin_lock_irqsave(&primary_channel->lock, flags);
|
||||
list_del(&channel->sc_list);
|
||||
spin_unlock_irqrestore(&primary_channel->sc_lock, flags);
|
||||
spin_unlock_irqrestore(&primary_channel->lock, flags);
|
||||
}
|
||||
free_channel(channel);
|
||||
}
|
||||
@ -279,9 +279,6 @@ static void vmbus_process_offer(struct work_struct *work)
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
/* The next possible work is rescind handling */
|
||||
INIT_WORK(&newchannel->work, vmbus_process_rescind_offer);
|
||||
|
||||
/* Make sure this is a new offer */
|
||||
spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
|
||||
|
||||
@ -323,9 +320,9 @@ static void vmbus_process_offer(struct work_struct *work)
|
||||
* Process the sub-channel.
|
||||
*/
|
||||
newchannel->primary_channel = channel;
|
||||
spin_lock_irqsave(&channel->sc_lock, flags);
|
||||
spin_lock_irqsave(&channel->lock, flags);
|
||||
list_add_tail(&newchannel->sc_list, &channel->sc_list);
|
||||
spin_unlock_irqrestore(&channel->sc_lock, flags);
|
||||
spin_unlock_irqrestore(&channel->lock, flags);
|
||||
|
||||
if (newchannel->target_cpu != get_cpu()) {
|
||||
put_cpu();
|
||||
@ -341,11 +338,10 @@ static void vmbus_process_offer(struct work_struct *work)
|
||||
if (channel->sc_creation_callback != NULL)
|
||||
channel->sc_creation_callback(newchannel);
|
||||
|
||||
return;
|
||||
goto done_init_rescind;
|
||||
}
|
||||
|
||||
free_channel(newchannel);
|
||||
return;
|
||||
goto err_free_chan;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -364,6 +360,8 @@ static void vmbus_process_offer(struct work_struct *work)
|
||||
&newchannel->offermsg.offer.if_type,
|
||||
&newchannel->offermsg.offer.if_instance,
|
||||
newchannel);
|
||||
if (!newchannel->device_obj)
|
||||
goto err_free_chan;
|
||||
|
||||
/*
|
||||
* Add the new device to the bus. This will kick off device-driver
|
||||
@ -379,9 +377,19 @@ static void vmbus_process_offer(struct work_struct *work)
|
||||
list_del(&newchannel->listentry);
|
||||
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
|
||||
kfree(newchannel->device_obj);
|
||||
|
||||
free_channel(newchannel);
|
||||
goto err_free_chan;
|
||||
}
|
||||
done_init_rescind:
|
||||
spin_lock_irqsave(&newchannel->lock, flags);
|
||||
/* The next possible work is rescind handling */
|
||||
INIT_WORK(&newchannel->work, vmbus_process_rescind_offer);
|
||||
/* Check if rescind offer was already received */
|
||||
if (newchannel->rescind)
|
||||
queue_work(newchannel->controlwq, &newchannel->work);
|
||||
spin_unlock_irqrestore(&newchannel->lock, flags);
|
||||
return;
|
||||
err_free_chan:
|
||||
free_channel(newchannel);
|
||||
}
|
||||
|
||||
enum {
|
||||
@ -516,6 +524,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
||||
{
|
||||
struct vmbus_channel_rescind_offer *rescind;
|
||||
struct vmbus_channel *channel;
|
||||
unsigned long flags;
|
||||
|
||||
rescind = (struct vmbus_channel_rescind_offer *)hdr;
|
||||
channel = relid2channel(rescind->child_relid);
|
||||
@ -524,11 +533,20 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
||||
/* Just return here, no channel found */
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&channel->lock, flags);
|
||||
channel->rescind = true;
|
||||
/*
|
||||
* channel->work.func != vmbus_process_rescind_offer means we are still
|
||||
* processing offer request and the rescind offer processing should be
|
||||
* postponed. It will be done at the very end of vmbus_process_offer()
|
||||
* as rescind flag is being checked there.
|
||||
*/
|
||||
if (channel->work.func == vmbus_process_rescind_offer)
|
||||
/* work is initialized for vmbus_process_rescind_offer() from
|
||||
* vmbus_process_offer() where the channel got created */
|
||||
queue_work(channel->controlwq, &channel->work);
|
||||
|
||||
/* work is initialized for vmbus_process_rescind_offer() from
|
||||
* vmbus_process_offer() where the channel got created */
|
||||
queue_work(channel->controlwq, &channel->work);
|
||||
spin_unlock_irqrestore(&channel->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -815,7 +833,7 @@ cleanup:
|
||||
struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary)
|
||||
{
|
||||
struct list_head *cur, *tmp;
|
||||
int cur_cpu = hv_context.vp_index[smp_processor_id()];
|
||||
int cur_cpu;
|
||||
struct vmbus_channel *cur_channel;
|
||||
struct vmbus_channel *outgoing_channel = primary;
|
||||
int cpu_distance, new_cpu_distance;
|
||||
@ -823,6 +841,8 @@ struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary)
|
||||
if (list_empty(&primary->sc_list))
|
||||
return outgoing_channel;
|
||||
|
||||
cur_cpu = hv_context.vp_index[get_cpu()];
|
||||
put_cpu();
|
||||
list_for_each_safe(cur, tmp, &primary->sc_list) {
|
||||
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
|
||||
if (cur_channel->state != CHANNEL_OPENED_STATE)
|
||||
|
@ -80,8 +80,10 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
|
||||
msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
|
||||
msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]);
|
||||
msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]);
|
||||
if (version == VERSION_WIN8_1)
|
||||
msg->target_vcpu = hv_context.vp_index[smp_processor_id()];
|
||||
if (version == VERSION_WIN8_1) {
|
||||
msg->target_vcpu = hv_context.vp_index[get_cpu()];
|
||||
put_cpu();
|
||||
}
|
||||
|
||||
/*
|
||||
* Add to list before we send the request since we may
|
||||
|
@ -28,7 +28,9 @@
|
||||
#include <linux/hyperv.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <asm/hyperv.h>
|
||||
#include <asm/mshyperv.h>
|
||||
#include "hyperv_vmbus.h"
|
||||
|
||||
/* The one and only */
|
||||
@ -37,6 +39,10 @@ struct hv_context hv_context = {
|
||||
.hypercall_page = NULL,
|
||||
};
|
||||
|
||||
#define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */
|
||||
#define HV_MAX_MAX_DELTA_TICKS 0xffffffff
|
||||
#define HV_MIN_DELTA_TICKS 1
|
||||
|
||||
/*
|
||||
* query_hypervisor_info - Get version info of the windows hypervisor
|
||||
*/
|
||||
@ -144,6 +150,8 @@ int hv_init(void)
|
||||
sizeof(int) * NR_CPUS);
|
||||
memset(hv_context.event_dpc, 0,
|
||||
sizeof(void *) * NR_CPUS);
|
||||
memset(hv_context.clk_evt, 0,
|
||||
sizeof(void *) * NR_CPUS);
|
||||
|
||||
max_leaf = query_hypervisor_info();
|
||||
|
||||
@ -258,10 +266,63 @@ u16 hv_signal_event(void *con_id)
|
||||
return status;
|
||||
}
|
||||
|
||||
static int hv_ce_set_next_event(unsigned long delta,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
cycle_t current_tick;
|
||||
|
||||
WARN_ON(evt->mode != CLOCK_EVT_MODE_ONESHOT);
|
||||
|
||||
rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
|
||||
current_tick += delta;
|
||||
wrmsrl(HV_X64_MSR_STIMER0_COUNT, current_tick);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hv_ce_setmode(enum clock_event_mode mode,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
union hv_timer_config timer_cfg;
|
||||
|
||||
switch (mode) {
|
||||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
/* unsupported */
|
||||
break;
|
||||
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
timer_cfg.enable = 1;
|
||||
timer_cfg.auto_enable = 1;
|
||||
timer_cfg.sintx = VMBUS_MESSAGE_SINT;
|
||||
wrmsrl(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64);
|
||||
break;
|
||||
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
wrmsrl(HV_X64_MSR_STIMER0_COUNT, 0);
|
||||
wrmsrl(HV_X64_MSR_STIMER0_CONFIG, 0);
|
||||
break;
|
||||
case CLOCK_EVT_MODE_RESUME:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu)
|
||||
{
|
||||
dev->name = "Hyper-V clockevent";
|
||||
dev->features = CLOCK_EVT_FEAT_ONESHOT;
|
||||
dev->cpumask = cpumask_of(cpu);
|
||||
dev->rating = 1000;
|
||||
dev->owner = THIS_MODULE;
|
||||
|
||||
dev->set_mode = hv_ce_setmode;
|
||||
dev->set_next_event = hv_ce_set_next_event;
|
||||
}
|
||||
|
||||
|
||||
int hv_synic_alloc(void)
|
||||
{
|
||||
size_t size = sizeof(struct tasklet_struct);
|
||||
size_t ced_size = sizeof(struct clock_event_device);
|
||||
int cpu;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
@ -272,6 +333,13 @@ int hv_synic_alloc(void)
|
||||
}
|
||||
tasklet_init(hv_context.event_dpc[cpu], vmbus_on_event, cpu);
|
||||
|
||||
hv_context.clk_evt[cpu] = kzalloc(ced_size, GFP_ATOMIC);
|
||||
if (hv_context.clk_evt[cpu] == NULL) {
|
||||
pr_err("Unable to allocate clock event device\n");
|
||||
goto err;
|
||||
}
|
||||
hv_init_clockevent_device(hv_context.clk_evt[cpu], cpu);
|
||||
|
||||
hv_context.synic_message_page[cpu] =
|
||||
(void *)get_zeroed_page(GFP_ATOMIC);
|
||||
|
||||
@ -305,6 +373,7 @@ err:
|
||||
static void hv_synic_free_cpu(int cpu)
|
||||
{
|
||||
kfree(hv_context.event_dpc[cpu]);
|
||||
kfree(hv_context.clk_evt[cpu]);
|
||||
if (hv_context.synic_event_page[cpu])
|
||||
free_page((unsigned long)hv_context.synic_event_page[cpu]);
|
||||
if (hv_context.synic_message_page[cpu])
|
||||
@ -388,6 +457,15 @@ void hv_synic_init(void *arg)
|
||||
hv_context.vp_index[cpu] = (u32)vp_index;
|
||||
|
||||
INIT_LIST_HEAD(&hv_context.percpu_list[cpu]);
|
||||
|
||||
/*
|
||||
* Register the per-cpu clockevent source.
|
||||
*/
|
||||
if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE)
|
||||
clockevents_config_and_register(hv_context.clk_evt[cpu],
|
||||
HV_TIMER_FREQUENCY,
|
||||
HV_MIN_DELTA_TICKS,
|
||||
HV_MAX_MAX_DELTA_TICKS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -533,6 +533,9 @@ struct hv_dynmem_device {
|
||||
*/
|
||||
struct task_struct *thread;
|
||||
|
||||
struct mutex ha_region_mutex;
|
||||
struct completion waiter_event;
|
||||
|
||||
/*
|
||||
* A list of hot-add regions.
|
||||
*/
|
||||
@ -549,7 +552,59 @@ struct hv_dynmem_device {
|
||||
static struct hv_dynmem_device dm_device;
|
||||
|
||||
static void post_status(struct hv_dynmem_device *dm);
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
static void acquire_region_mutex(bool trylock)
|
||||
{
|
||||
if (trylock) {
|
||||
reinit_completion(&dm_device.waiter_event);
|
||||
while (!mutex_trylock(&dm_device.ha_region_mutex))
|
||||
wait_for_completion(&dm_device.waiter_event);
|
||||
} else {
|
||||
mutex_lock(&dm_device.ha_region_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void release_region_mutex(bool trylock)
|
||||
{
|
||||
if (trylock) {
|
||||
mutex_unlock(&dm_device.ha_region_mutex);
|
||||
} else {
|
||||
mutex_unlock(&dm_device.ha_region_mutex);
|
||||
complete(&dm_device.waiter_event);
|
||||
}
|
||||
}
|
||||
|
||||
static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
|
||||
void *v)
|
||||
{
|
||||
switch (val) {
|
||||
case MEM_GOING_ONLINE:
|
||||
acquire_region_mutex(true);
|
||||
break;
|
||||
|
||||
case MEM_ONLINE:
|
||||
case MEM_CANCEL_ONLINE:
|
||||
release_region_mutex(true);
|
||||
if (dm_device.ha_waiting) {
|
||||
dm_device.ha_waiting = false;
|
||||
complete(&dm_device.ol_waitevent);
|
||||
}
|
||||
break;
|
||||
|
||||
case MEM_GOING_OFFLINE:
|
||||
case MEM_OFFLINE:
|
||||
case MEM_CANCEL_OFFLINE:
|
||||
break;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block hv_memory_nb = {
|
||||
.notifier_call = hv_memory_notifier,
|
||||
.priority = 0
|
||||
};
|
||||
|
||||
|
||||
static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size)
|
||||
{
|
||||
@ -591,6 +646,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
|
||||
init_completion(&dm_device.ol_waitevent);
|
||||
dm_device.ha_waiting = true;
|
||||
|
||||
release_region_mutex(false);
|
||||
nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn));
|
||||
ret = add_memory(nid, PFN_PHYS((start_pfn)),
|
||||
(HA_CHUNK << PAGE_SHIFT));
|
||||
@ -619,6 +675,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
|
||||
* have not been "onlined" within the allowed time.
|
||||
*/
|
||||
wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ);
|
||||
acquire_region_mutex(false);
|
||||
post_status(&dm_device);
|
||||
}
|
||||
|
||||
@ -632,11 +689,6 @@ static void hv_online_page(struct page *pg)
|
||||
unsigned long cur_start_pgp;
|
||||
unsigned long cur_end_pgp;
|
||||
|
||||
if (dm_device.ha_waiting) {
|
||||
dm_device.ha_waiting = false;
|
||||
complete(&dm_device.ol_waitevent);
|
||||
}
|
||||
|
||||
list_for_each(cur, &dm_device.ha_region_list) {
|
||||
has = list_entry(cur, struct hv_hotadd_state, list);
|
||||
cur_start_pgp = (unsigned long)
|
||||
@ -834,6 +886,7 @@ static void hot_add_req(struct work_struct *dummy)
|
||||
resp.hdr.size = sizeof(struct dm_hot_add_response);
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
acquire_region_mutex(false);
|
||||
pg_start = dm->ha_wrk.ha_page_range.finfo.start_page;
|
||||
pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt;
|
||||
|
||||
@ -865,6 +918,7 @@ static void hot_add_req(struct work_struct *dummy)
|
||||
if (do_hot_add)
|
||||
resp.page_count = process_hot_add(pg_start, pfn_cnt,
|
||||
rg_start, rg_sz);
|
||||
release_region_mutex(false);
|
||||
#endif
|
||||
/*
|
||||
* The result field of the response structure has the
|
||||
@ -928,9 +982,8 @@ static unsigned long compute_balloon_floor(void)
|
||||
* 128 72 (1/2)
|
||||
* 512 168 (1/4)
|
||||
* 2048 360 (1/8)
|
||||
* 8192 552 (1/32)
|
||||
* 32768 1320
|
||||
* 131072 4392
|
||||
* 8192 768 (1/16)
|
||||
* 32768 1536 (1/32)
|
||||
*/
|
||||
if (totalram_pages < MB2PAGES(128))
|
||||
min_pages = MB2PAGES(8) + (totalram_pages >> 1);
|
||||
@ -938,8 +991,10 @@ static unsigned long compute_balloon_floor(void)
|
||||
min_pages = MB2PAGES(40) + (totalram_pages >> 2);
|
||||
else if (totalram_pages < MB2PAGES(2048))
|
||||
min_pages = MB2PAGES(104) + (totalram_pages >> 3);
|
||||
else if (totalram_pages < MB2PAGES(8192))
|
||||
min_pages = MB2PAGES(256) + (totalram_pages >> 4);
|
||||
else
|
||||
min_pages = MB2PAGES(296) + (totalram_pages >> 5);
|
||||
min_pages = MB2PAGES(512) + (totalram_pages >> 5);
|
||||
#undef MB2PAGES
|
||||
return min_pages;
|
||||
}
|
||||
@ -1171,7 +1226,7 @@ static void balloon_down(struct hv_dynmem_device *dm,
|
||||
|
||||
for (i = 0; i < range_count; i++) {
|
||||
free_balloon_pages(dm, &range_array[i]);
|
||||
post_status(&dm_device);
|
||||
complete(&dm_device.config_event);
|
||||
}
|
||||
|
||||
if (req->more_pages == 1)
|
||||
@ -1195,19 +1250,16 @@ static void balloon_onchannelcallback(void *context);
|
||||
static int dm_thread_func(void *dm_dev)
|
||||
{
|
||||
struct hv_dynmem_device *dm = dm_dev;
|
||||
int t;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
t = wait_for_completion_interruptible_timeout(
|
||||
wait_for_completion_interruptible_timeout(
|
||||
&dm_device.config_event, 1*HZ);
|
||||
/*
|
||||
* The host expects us to post information on the memory
|
||||
* pressure every second.
|
||||
*/
|
||||
|
||||
if (t == 0)
|
||||
post_status(dm);
|
||||
|
||||
reinit_completion(&dm_device.config_event);
|
||||
post_status(dm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1387,7 +1439,9 @@ static int balloon_probe(struct hv_device *dev,
|
||||
dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN7;
|
||||
init_completion(&dm_device.host_event);
|
||||
init_completion(&dm_device.config_event);
|
||||
init_completion(&dm_device.waiter_event);
|
||||
INIT_LIST_HEAD(&dm_device.ha_region_list);
|
||||
mutex_init(&dm_device.ha_region_mutex);
|
||||
INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up);
|
||||
INIT_WORK(&dm_device.ha_wrk.wrk, hot_add_req);
|
||||
dm_device.host_specified_ha_region = false;
|
||||
@ -1401,6 +1455,7 @@ static int balloon_probe(struct hv_device *dev,
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
set_online_page_callback(&hv_online_page);
|
||||
register_memory_notifier(&hv_memory_nb);
|
||||
#endif
|
||||
|
||||
hv_set_drvdata(dev, &dm_device);
|
||||
@ -1519,6 +1574,7 @@ static int balloon_remove(struct hv_device *dev)
|
||||
kfree(send_buffer);
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
restore_online_page_callback(&hv_online_page);
|
||||
unregister_memory_notifier(&hv_memory_nb);
|
||||
#endif
|
||||
list_for_each_safe(cur, tmp, &dm->ha_region_list) {
|
||||
has = list_entry(cur, struct hv_hotadd_state, list);
|
||||
|
@ -86,6 +86,18 @@ static void fcopy_work_func(struct work_struct *dummy)
|
||||
* process the pending transaction.
|
||||
*/
|
||||
fcopy_respond_to_host(HV_E_FAIL);
|
||||
|
||||
/* In the case the user-space daemon crashes, hangs or is killed, we
|
||||
* need to down the semaphore, otherwise, after the daemon starts next
|
||||
* time, the obsolete data in fcopy_transaction.message or
|
||||
* fcopy_transaction.fcopy_msg will be used immediately.
|
||||
*
|
||||
* NOTE: fcopy_read() happens to get the semaphore (very rare)? We're
|
||||
* still OK, because we've reported the failure to the host.
|
||||
*/
|
||||
if (down_trylock(&fcopy_transaction.read_sema))
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
static int fcopy_handle_handshake(u32 version)
|
||||
@ -344,6 +356,14 @@ static int fcopy_open(struct inode *inode, struct file *f)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: there are still some tricky corner cases, e.g.,
|
||||
* 1) In a SMP guest, when fcopy_release() runs between
|
||||
* schedule_delayed_work() and fcopy_send_data(), there is
|
||||
* still a chance an obsolete message will be queued.
|
||||
*
|
||||
* 2) When the fcopy daemon is running, if we unload the driver,
|
||||
* we'll notice a kernel oops when we kill the daemon later.
|
||||
*/
|
||||
static int fcopy_release(struct inode *inode, struct file *f)
|
||||
{
|
||||
/*
|
||||
@ -351,6 +371,13 @@ static int fcopy_release(struct inode *inode, struct file *f)
|
||||
*/
|
||||
in_hand_shake = true;
|
||||
opened = false;
|
||||
|
||||
if (cancel_delayed_work_sync(&fcopy_work)) {
|
||||
/* We haven't up()-ed the semaphore(very rare)? */
|
||||
if (down_trylock(&fcopy_transaction.read_sema))
|
||||
;
|
||||
fcopy_respond_to_host(HV_E_FAIL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -178,6 +178,23 @@ struct hv_message_header {
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Timer configuration register.
|
||||
*/
|
||||
union hv_timer_config {
|
||||
u64 as_uint64;
|
||||
struct {
|
||||
u64 enable:1;
|
||||
u64 periodic:1;
|
||||
u64 lazy:1;
|
||||
u64 auto_enable:1;
|
||||
u64 reserved_z0:12;
|
||||
u64 sintx:4;
|
||||
u64 reserved_z1:44;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* Define timer message payload structure. */
|
||||
struct hv_timer_message_payload {
|
||||
u32 timer_index;
|
||||
@ -519,6 +536,10 @@ struct hv_context {
|
||||
* buffer to post messages to the host.
|
||||
*/
|
||||
void *post_msg_page[NR_CPUS];
|
||||
/*
|
||||
* Support PV clockevent device.
|
||||
*/
|
||||
struct clock_event_device *clk_evt[NR_CPUS];
|
||||
};
|
||||
|
||||
extern struct hv_context hv_context;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <linux/completion.h>
|
||||
#include <linux/hyperv.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <asm/hyperv.h>
|
||||
#include <asm/hypervisor.h>
|
||||
#include <asm/mshyperv.h>
|
||||
@ -578,6 +579,34 @@ static void vmbus_onmessage_work(struct work_struct *work)
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
static void hv_process_timer_expiration(struct hv_message *msg, int cpu)
|
||||
{
|
||||
struct clock_event_device *dev = hv_context.clk_evt[cpu];
|
||||
|
||||
if (dev->event_handler)
|
||||
dev->event_handler(dev);
|
||||
|
||||
msg->header.message_type = HVMSG_NONE;
|
||||
|
||||
/*
|
||||
* Make sure the write to MessageType (ie set to
|
||||
* HVMSG_NONE) happens before we read the
|
||||
* MessagePending and EOMing. Otherwise, the EOMing
|
||||
* will not deliver any more messages since there is
|
||||
* no empty slot
|
||||
*/
|
||||
mb();
|
||||
|
||||
if (msg->header.message_flags.msg_pending) {
|
||||
/*
|
||||
* This will cause message queue rescan to
|
||||
* possibly deliver another msg from the
|
||||
* hypervisor
|
||||
*/
|
||||
wrmsrl(HV_X64_MSR_EOM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void vmbus_on_msg_dpc(unsigned long data)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
@ -667,8 +696,12 @@ static void vmbus_isr(void)
|
||||
msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
|
||||
|
||||
/* Check if there are actual msgs to be processed */
|
||||
if (msg->header.message_type != HVMSG_NONE)
|
||||
tasklet_schedule(&msg_dpc);
|
||||
if (msg->header.message_type != HVMSG_NONE) {
|
||||
if (msg->header.message_type == HVMSG_TIMER_EXPIRED)
|
||||
hv_process_timer_expiration(msg, cpu);
|
||||
else
|
||||
tasklet_schedule(&msg_dpc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -51,7 +51,7 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
priv->mapbase = pci_resource_start(pdev, 0);
|
||||
if (!priv->mapbase) {
|
||||
dev_err(&pdev->dev, "No PCI resource\n");
|
||||
goto err_start;
|
||||
goto out_disable;
|
||||
}
|
||||
|
||||
res = request_mem_region(priv->mapbase, CHAM_HEADER_SIZE,
|
||||
@ -59,14 +59,14 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
if (IS_ERR(res)) {
|
||||
dev_err(&pdev->dev, "Failed to request PCI memory\n");
|
||||
ret = PTR_ERR(res);
|
||||
goto err_start;
|
||||
goto out_disable;
|
||||
}
|
||||
|
||||
priv->base = ioremap(priv->mapbase, CHAM_HEADER_SIZE);
|
||||
if (!priv->base) {
|
||||
dev_err(&pdev->dev, "Cannot ioremap\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_ioremap;
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
flags = pci_resource_flags(pdev, 0);
|
||||
@ -74,7 +74,7 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
ret = -ENOTSUPP;
|
||||
dev_err(&pdev->dev,
|
||||
"IO mapped PCI devices are not supported\n");
|
||||
goto err_ioremap;
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, priv);
|
||||
@ -82,14 +82,14 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
priv->bus = mcb_alloc_bus(&pdev->dev);
|
||||
if (IS_ERR(priv->bus)) {
|
||||
ret = PTR_ERR(priv->bus);
|
||||
goto err_drvdata;
|
||||
goto out_iounmap;
|
||||
}
|
||||
|
||||
priv->bus->get_irq = mcb_pci_get_irq;
|
||||
|
||||
ret = chameleon_parse_cells(priv->bus, priv->mapbase, priv->base);
|
||||
if (ret < 0)
|
||||
goto err_drvdata;
|
||||
goto out_iounmap;
|
||||
num_cells = ret;
|
||||
|
||||
dev_dbg(&pdev->dev, "Found %d cells\n", num_cells);
|
||||
@ -98,11 +98,11 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
|
||||
return 0;
|
||||
|
||||
err_drvdata:
|
||||
out_iounmap:
|
||||
iounmap(priv->base);
|
||||
err_ioremap:
|
||||
out_release:
|
||||
pci_release_region(pdev, 0);
|
||||
err_start:
|
||||
out_disable:
|
||||
pci_disable_device(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
@ -15,18 +15,21 @@
|
||||
static int write8(void *client, u8 val)
|
||||
{
|
||||
u8 data = val;
|
||||
|
||||
return spi_write(client, &data, 1);
|
||||
}
|
||||
|
||||
static int write16(void *client, u8 reg, u8 val)
|
||||
{
|
||||
u8 data[2] = {reg, val};
|
||||
|
||||
return spi_write(client, data, 2);
|
||||
}
|
||||
|
||||
static int write24(void *client, u8 reg, u16 val)
|
||||
{
|
||||
u8 data[3] = {reg, val >> 8, val};
|
||||
|
||||
return spi_write(client, data, 3);
|
||||
}
|
||||
|
||||
@ -34,6 +37,7 @@ static int read8(void *client)
|
||||
{
|
||||
int ret;
|
||||
u8 data;
|
||||
|
||||
ret = spi_read(client, &data, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -176,6 +176,7 @@ static s32 dpot_read_i2c(struct dpot_data *dpot, u8 reg)
|
||||
{
|
||||
int value;
|
||||
unsigned ctrl = 0;
|
||||
|
||||
switch (dpot->uid) {
|
||||
case DPOT_UID(AD5246_ID):
|
||||
case DPOT_UID(AD5247_ID):
|
||||
@ -333,7 +334,6 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
|
||||
case DPOT_UID(AD5246_ID):
|
||||
case DPOT_UID(AD5247_ID):
|
||||
return dpot_write_d8(dpot, value);
|
||||
break;
|
||||
|
||||
case DPOT_UID(AD5245_ID):
|
||||
case DPOT_UID(AD5241_ID):
|
||||
@ -345,7 +345,6 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
|
||||
ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
|
||||
0 : DPOT_AD5282_RDAC_AB;
|
||||
return dpot_write_r8d8(dpot, ctrl, value);
|
||||
break;
|
||||
case DPOT_UID(AD5171_ID):
|
||||
case DPOT_UID(AD5273_ID):
|
||||
if (reg & DPOT_ADDR_OTP) {
|
||||
@ -355,7 +354,6 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
|
||||
ctrl = DPOT_AD5273_FUSE;
|
||||
}
|
||||
return dpot_write_r8d8(dpot, ctrl, value);
|
||||
break;
|
||||
case DPOT_UID(AD5172_ID):
|
||||
case DPOT_UID(AD5173_ID):
|
||||
ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
|
||||
@ -367,7 +365,6 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
|
||||
ctrl |= DPOT_AD5170_2_3_FUSE;
|
||||
}
|
||||
return dpot_write_r8d8(dpot, ctrl, value);
|
||||
break;
|
||||
case DPOT_UID(AD5170_ID):
|
||||
if (reg & DPOT_ADDR_OTP) {
|
||||
tmp = dpot_read_r8d16(dpot, tmp);
|
||||
@ -376,7 +373,6 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
|
||||
ctrl = DPOT_AD5170_2_3_FUSE;
|
||||
}
|
||||
return dpot_write_r8d8(dpot, ctrl, value);
|
||||
break;
|
||||
case DPOT_UID(AD5272_ID):
|
||||
case DPOT_UID(AD5274_ID):
|
||||
dpot_write_r8d8(dpot, DPOT_AD5270_1_2_4_CTRLREG << 2,
|
||||
@ -391,7 +387,6 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
|
||||
|
||||
return dpot_write_r8d8(dpot, (DPOT_AD5270_1_2_4_RDAC << 2) |
|
||||
(value >> 8), value & 0xFF);
|
||||
break;
|
||||
default:
|
||||
if (reg & DPOT_ADDR_CMD)
|
||||
return dpot_write_d8(dpot, reg);
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -24,7 +24,6 @@
|
||||
* debugging, please also see the debugfs interfaces of this driver.
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -144,9 +144,9 @@ ioc4_clock_calibrate(struct ioc4_driver_data *idd)
|
||||
{
|
||||
union ioc4_int_out int_out;
|
||||
union ioc4_gpcr gpcr;
|
||||
unsigned int state, last_state = 1;
|
||||
unsigned int state, last_state;
|
||||
uint64_t start, end, period;
|
||||
unsigned int count = 0;
|
||||
unsigned int count;
|
||||
|
||||
/* Enable output */
|
||||
gpcr.raw = 0;
|
||||
@ -167,19 +167,20 @@ ioc4_clock_calibrate(struct ioc4_driver_data *idd)
|
||||
mmiowb();
|
||||
|
||||
/* Check square wave period averaged over some number of cycles */
|
||||
do {
|
||||
int_out.raw = readl(&idd->idd_misc_regs->int_out.raw);
|
||||
state = int_out.fields.int_out;
|
||||
if (!last_state && state) {
|
||||
count++;
|
||||
if (count == IOC4_CALIBRATE_END) {
|
||||
end = ktime_get_ns();
|
||||
break;
|
||||
} else if (count == IOC4_CALIBRATE_DISCARD)
|
||||
start = ktime_get_ns();
|
||||
}
|
||||
last_state = state;
|
||||
} while (1);
|
||||
start = ktime_get_ns();
|
||||
state = 1; /* make sure the first read isn't a rising edge */
|
||||
for (count = 0; count <= IOC4_CALIBRATE_END; count++) {
|
||||
do { /* wait for a rising edge */
|
||||
last_state = state;
|
||||
int_out.raw = readl(&idd->idd_misc_regs->int_out.raw);
|
||||
state = int_out.fields.int_out;
|
||||
} while (last_state || !state);
|
||||
|
||||
/* discard the first few cycles */
|
||||
if (count == IOC4_CALIBRATE_DISCARD)
|
||||
start = ktime_get_ns();
|
||||
}
|
||||
end = ktime_get_ns();
|
||||
|
||||
/* Calculation rearranged to preserve intermediate precision.
|
||||
* Logically:
|
||||
|
@ -97,23 +97,25 @@ int mei_amthif_host_init(struct mei_device *dev)
|
||||
/* allocate storage for ME message buffer */
|
||||
msg_buf = kcalloc(dev->iamthif_mtu,
|
||||
sizeof(unsigned char), GFP_KERNEL);
|
||||
if (!msg_buf)
|
||||
return -ENOMEM;
|
||||
if (!msg_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->iamthif_msg_buf = msg_buf;
|
||||
|
||||
ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev,
|
||||
"amthif: failed link client %d\n", ret);
|
||||
return ret;
|
||||
dev_err(dev->dev, "amthif: failed cl_link %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mei_cl_connect(cl, NULL);
|
||||
|
||||
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
||||
|
||||
out:
|
||||
mei_me_cl_put(me_cl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -224,46 +224,53 @@ void mei_cl_driver_unregister(struct mei_cl_driver *driver)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_driver_unregister);
|
||||
|
||||
static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
|
||||
static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
|
||||
bool blocking)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_me_client *me_cl;
|
||||
struct mei_cl_cb *cb;
|
||||
int rets;
|
||||
struct mei_me_client *me_cl = NULL;
|
||||
struct mei_cl_cb *cb = NULL;
|
||||
ssize_t rets;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
if (cl->state != MEI_FILE_CONNECTED)
|
||||
return -ENODEV;
|
||||
mutex_lock(&dev->device_lock);
|
||||
if (cl->state != MEI_FILE_CONNECTED) {
|
||||
rets = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check if we have an ME client device */
|
||||
me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
|
||||
if (!me_cl)
|
||||
return -ENOTTY;
|
||||
if (!me_cl) {
|
||||
rets = -ENOTTY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (length > me_cl->props.max_msg_length)
|
||||
return -EFBIG;
|
||||
if (length > me_cl->props.max_msg_length) {
|
||||
rets = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cb = mei_io_cb_init(cl, NULL);
|
||||
if (!cb)
|
||||
return -ENOMEM;
|
||||
if (!cb) {
|
||||
rets = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rets = mei_io_cb_alloc_req_buf(cb, length);
|
||||
if (rets < 0) {
|
||||
mei_io_cb_free(cb);
|
||||
return rets;
|
||||
}
|
||||
if (rets < 0)
|
||||
goto out;
|
||||
|
||||
memcpy(cb->request_buffer.data, buf, length);
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
rets = mei_cl_write(cl, cb, blocking);
|
||||
|
||||
out:
|
||||
mei_me_cl_put(me_cl);
|
||||
mutex_unlock(&dev->device_lock);
|
||||
if (rets < 0)
|
||||
mei_io_cb_free(cb);
|
||||
@ -271,12 +278,12 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
|
||||
return rets;
|
||||
}
|
||||
|
||||
int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl_cb *cb;
|
||||
size_t r_length;
|
||||
int err;
|
||||
ssize_t rets;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
@ -286,11 +293,9 @@ int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (!cl->read_cb) {
|
||||
err = mei_cl_read_start(cl, length);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
return err;
|
||||
}
|
||||
rets = mei_cl_read_start(cl, length);
|
||||
if (rets < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cl->reading_state != MEI_READ_COMPLETE &&
|
||||
@ -313,13 +318,13 @@ int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
cb = cl->read_cb;
|
||||
|
||||
if (cl->reading_state != MEI_READ_COMPLETE) {
|
||||
r_length = 0;
|
||||
rets = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r_length = min_t(size_t, length, cb->buf_idx);
|
||||
|
||||
memcpy(buf, cb->response_buffer.data, r_length);
|
||||
rets = r_length;
|
||||
|
||||
mei_io_cb_free(cb);
|
||||
cl->reading_state = MEI_IDLE;
|
||||
@ -328,20 +333,20 @@ int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
out:
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
return r_length;
|
||||
return rets;
|
||||
}
|
||||
|
||||
inline int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
inline ssize_t __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
{
|
||||
return ___mei_cl_send(cl, buf, length, 0);
|
||||
}
|
||||
|
||||
inline int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
inline ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
{
|
||||
return ___mei_cl_send(cl, buf, length, 1);
|
||||
}
|
||||
|
||||
int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
|
||||
ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
|
||||
{
|
||||
struct mei_cl *cl = device->cl;
|
||||
|
||||
@ -355,7 +360,7 @@ int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_send);
|
||||
|
||||
int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length)
|
||||
ssize_t mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length)
|
||||
{
|
||||
struct mei_cl *cl = device->cl;
|
||||
|
||||
|
@ -26,8 +26,64 @@
|
||||
#include "hbm.h"
|
||||
#include "client.h"
|
||||
|
||||
/**
|
||||
* mei_me_cl_init - initialize me client
|
||||
*
|
||||
* @me_cl: me client
|
||||
*/
|
||||
void mei_me_cl_init(struct mei_me_client *me_cl)
|
||||
{
|
||||
INIT_LIST_HEAD(&me_cl->list);
|
||||
kref_init(&me_cl->refcnt);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_cl_get - increases me client refcount
|
||||
*
|
||||
* @me_cl: me client
|
||||
*
|
||||
* Locking: called under "dev->device_lock" lock
|
||||
*
|
||||
* Return: me client or NULL
|
||||
*/
|
||||
struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl)
|
||||
{
|
||||
if (me_cl)
|
||||
kref_get(&me_cl->refcnt);
|
||||
|
||||
return me_cl;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_cl_release - unlink and free me client
|
||||
*
|
||||
* Locking: called under "dev->device_lock" lock
|
||||
*
|
||||
* @ref: me_client refcount
|
||||
*/
|
||||
static void mei_me_cl_release(struct kref *ref)
|
||||
{
|
||||
struct mei_me_client *me_cl =
|
||||
container_of(ref, struct mei_me_client, refcnt);
|
||||
list_del(&me_cl->list);
|
||||
kfree(me_cl);
|
||||
}
|
||||
/**
|
||||
* mei_me_cl_put - decrease me client refcount and free client if necessary
|
||||
*
|
||||
* Locking: called under "dev->device_lock" lock
|
||||
*
|
||||
* @me_cl: me client
|
||||
*/
|
||||
void mei_me_cl_put(struct mei_me_client *me_cl)
|
||||
{
|
||||
if (me_cl)
|
||||
kref_put(&me_cl->refcnt, mei_me_cl_release);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_cl_by_uuid - locate me client by uuid
|
||||
* increases ref count
|
||||
*
|
||||
* @dev: mei device
|
||||
* @uuid: me client uuid
|
||||
@ -43,13 +99,14 @@ struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev,
|
||||
|
||||
list_for_each_entry(me_cl, &dev->me_clients, list)
|
||||
if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0)
|
||||
return me_cl;
|
||||
return mei_me_cl_get(me_cl);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_cl_by_id - locate me client by client id
|
||||
* increases ref count
|
||||
*
|
||||
* @dev: the device structure
|
||||
* @client_id: me client id
|
||||
@ -65,12 +122,14 @@ struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
|
||||
|
||||
list_for_each_entry(me_cl, &dev->me_clients, list)
|
||||
if (me_cl->client_id == client_id)
|
||||
return me_cl;
|
||||
return mei_me_cl_get(me_cl);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_cl_by_uuid_id - locate me client by client id and uuid
|
||||
* increases ref count
|
||||
*
|
||||
* @dev: the device structure
|
||||
* @uuid: me client uuid
|
||||
@ -88,31 +147,67 @@ struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev,
|
||||
list_for_each_entry(me_cl, &dev->me_clients, list)
|
||||
if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 &&
|
||||
me_cl->client_id == client_id)
|
||||
return me_cl;
|
||||
return mei_me_cl_get(me_cl);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_cl_remove - remove me client matching uuid and client_id
|
||||
* mei_me_cl_rm_by_uuid - remove all me clients matching uuid
|
||||
*
|
||||
* @dev: the device structure
|
||||
* @uuid: me client uuid
|
||||
* @client_id: me client address
|
||||
*
|
||||
* Locking: called under "dev->device_lock" lock
|
||||
*/
|
||||
void mei_me_cl_remove(struct mei_device *dev, const uuid_le *uuid, u8 client_id)
|
||||
void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid)
|
||||
{
|
||||
struct mei_me_client *me_cl, *next;
|
||||
|
||||
dev_dbg(dev->dev, "remove %pUl\n", uuid);
|
||||
list_for_each_entry_safe(me_cl, next, &dev->me_clients, list)
|
||||
if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0)
|
||||
mei_me_cl_put(me_cl);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_cl_rm_by_uuid_id - remove all me clients matching client id
|
||||
*
|
||||
* @dev: the device structure
|
||||
* @uuid: me client uuid
|
||||
* @id: me client id
|
||||
*
|
||||
* Locking: called under "dev->device_lock" lock
|
||||
*/
|
||||
void mei_me_cl_rm_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 id)
|
||||
{
|
||||
struct mei_me_client *me_cl, *next;
|
||||
const uuid_le *pn;
|
||||
|
||||
dev_dbg(dev->dev, "remove %pUl %d\n", uuid, id);
|
||||
list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) {
|
||||
if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 &&
|
||||
me_cl->client_id == client_id) {
|
||||
list_del(&me_cl->list);
|
||||
kfree(me_cl);
|
||||
break;
|
||||
}
|
||||
pn = &me_cl->props.protocol_name;
|
||||
if (me_cl->client_id == id && uuid_le_cmp(*uuid, *pn) == 0)
|
||||
mei_me_cl_put(me_cl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_cl_rm_all - remove all me clients
|
||||
*
|
||||
* @dev: the device structure
|
||||
*
|
||||
* Locking: called under "dev->device_lock" lock
|
||||
*/
|
||||
void mei_me_cl_rm_all(struct mei_device *dev)
|
||||
{
|
||||
struct mei_me_client *me_cl, *next;
|
||||
|
||||
list_for_each_entry_safe(me_cl, next, &dev->me_clients, list)
|
||||
mei_me_cl_put(me_cl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* mei_cl_cmp_id - tells if the clients are the same
|
||||
@ -695,6 +790,7 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_me_client *me_cl;
|
||||
int rets = 0;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -EINVAL;
|
||||
@ -704,18 +800,19 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
|
||||
if (cl->mei_flow_ctrl_creds > 0)
|
||||
return 1;
|
||||
|
||||
me_cl = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
|
||||
if (!me_cl) {
|
||||
cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (me_cl->mei_flow_ctrl_creds) {
|
||||
if (me_cl->mei_flow_ctrl_creds > 0) {
|
||||
rets = 1;
|
||||
if (WARN_ON(me_cl->props.single_recv_buf == 0))
|
||||
return -EINVAL;
|
||||
return 1;
|
||||
rets = -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
mei_me_cl_put(me_cl);
|
||||
return rets;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -732,28 +829,36 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_me_client *me_cl;
|
||||
int rets;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -EINVAL;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
me_cl = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
|
||||
if (!me_cl) {
|
||||
cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (me_cl->props.single_recv_buf) {
|
||||
if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) {
|
||||
rets = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
me_cl->mei_flow_ctrl_creds--;
|
||||
} else {
|
||||
if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) {
|
||||
rets = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
cl->mei_flow_ctrl_creds--;
|
||||
}
|
||||
return 0;
|
||||
rets = 0;
|
||||
out:
|
||||
mei_me_cl_put(me_cl);
|
||||
return rets;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -788,6 +893,9 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
|
||||
cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
|
||||
return -ENOTTY;
|
||||
}
|
||||
/* always allocate at least client max message */
|
||||
length = max_t(size_t, length, me_cl->props.max_msg_length);
|
||||
mei_me_cl_put(me_cl);
|
||||
|
||||
rets = pm_runtime_get(dev->dev);
|
||||
if (rets < 0 && rets != -EINPROGRESS) {
|
||||
@ -802,8 +910,6 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* always allocate at least client max message */
|
||||
length = max_t(size_t, length, me_cl->props.max_msg_length);
|
||||
rets = mei_io_cb_alloc_resp_buf(cb, length);
|
||||
if (rets)
|
||||
goto out;
|
||||
|
@ -24,15 +24,22 @@
|
||||
|
||||
#include "mei_dev.h"
|
||||
|
||||
struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev,
|
||||
const uuid_le *cuuid);
|
||||
struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id);
|
||||
/*
|
||||
* reference counting base function
|
||||
*/
|
||||
void mei_me_cl_init(struct mei_me_client *me_cl);
|
||||
void mei_me_cl_put(struct mei_me_client *me_cl);
|
||||
struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl);
|
||||
|
||||
struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev,
|
||||
const uuid_le *uuid);
|
||||
struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id);
|
||||
struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev,
|
||||
const uuid_le *uuid, u8 client_id);
|
||||
|
||||
void mei_me_cl_remove(struct mei_device *dev,
|
||||
const uuid_le *uuid, u8 client_id);
|
||||
void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid);
|
||||
void mei_me_cl_rm_by_uuid_id(struct mei_device *dev,
|
||||
const uuid_le *uuid, u8 id);
|
||||
void mei_me_cl_rm_all(struct mei_device *dev);
|
||||
|
||||
/*
|
||||
* MEI IO Functions
|
||||
|
@ -21,20 +21,22 @@
|
||||
#include <linux/mei.h>
|
||||
|
||||
#include "mei_dev.h"
|
||||
#include "client.h"
|
||||
#include "hw.h"
|
||||
|
||||
static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct mei_device *dev = fp->private_data;
|
||||
struct mei_me_client *me_cl;
|
||||
struct mei_me_client *me_cl, *n;
|
||||
size_t bufsz = 1;
|
||||
char *buf;
|
||||
int i = 0;
|
||||
int pos = 0;
|
||||
int ret;
|
||||
|
||||
#define HDR " |id|fix| UUID |con|msg len|sb|\n"
|
||||
#define HDR \
|
||||
" |id|fix| UUID |con|msg len|sb|refc|\n"
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
@ -54,16 +56,22 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf,
|
||||
if (dev->dev_state != MEI_DEV_ENABLED)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(me_cl, &dev->me_clients, list) {
|
||||
list_for_each_entry_safe(me_cl, n, &dev->me_clients, list) {
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"%2d|%2d|%3d|%pUl|%3d|%7d|%2d|\n",
|
||||
i++, me_cl->client_id,
|
||||
me_cl->props.fixed_address,
|
||||
&me_cl->props.protocol_name,
|
||||
me_cl->props.max_number_of_connections,
|
||||
me_cl->props.max_msg_length,
|
||||
me_cl->props.single_recv_buf);
|
||||
me_cl = mei_me_cl_get(me_cl);
|
||||
if (me_cl) {
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"%2d|%2d|%3d|%pUl|%3d|%7d|%2d|%4d|\n",
|
||||
i++, me_cl->client_id,
|
||||
me_cl->props.fixed_address,
|
||||
&me_cl->props.protocol_name,
|
||||
me_cl->props.max_number_of_connections,
|
||||
me_cl->props.max_msg_length,
|
||||
me_cl->props.single_recv_buf,
|
||||
atomic_read(&me_cl->refcnt.refcount));
|
||||
}
|
||||
|
||||
mei_me_cl_put(me_cl);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
@ -104,21 +104,6 @@ void mei_hbm_idle(struct mei_device *dev)
|
||||
dev->hbm_state = MEI_HBM_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_me_cl_remove_all - remove all me clients
|
||||
*
|
||||
* @dev: the device structure
|
||||
*/
|
||||
static void mei_me_cl_remove_all(struct mei_device *dev)
|
||||
{
|
||||
struct mei_me_client *me_cl, *next;
|
||||
|
||||
list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) {
|
||||
list_del(&me_cl->list);
|
||||
kfree(me_cl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_hbm_reset - reset hbm counters and book keeping data structurs
|
||||
*
|
||||
@ -128,7 +113,7 @@ void mei_hbm_reset(struct mei_device *dev)
|
||||
{
|
||||
dev->me_client_index = 0;
|
||||
|
||||
mei_me_cl_remove_all(dev);
|
||||
mei_me_cl_rm_all(dev);
|
||||
|
||||
mei_hbm_idle(dev);
|
||||
}
|
||||
@ -339,11 +324,16 @@ static int mei_hbm_me_cl_add(struct mei_device *dev,
|
||||
struct hbm_props_response *res)
|
||||
{
|
||||
struct mei_me_client *me_cl;
|
||||
const uuid_le *uuid = &res->client_properties.protocol_name;
|
||||
|
||||
mei_me_cl_rm_by_uuid(dev, uuid);
|
||||
|
||||
me_cl = kzalloc(sizeof(struct mei_me_client), GFP_KERNEL);
|
||||
if (!me_cl)
|
||||
return -ENOMEM;
|
||||
|
||||
mei_me_cl_init(me_cl);
|
||||
|
||||
me_cl->props = res->client_properties;
|
||||
me_cl->client_id = res->me_addr;
|
||||
me_cl->mei_flow_ctrl_creds = 0;
|
||||
@ -484,6 +474,7 @@ static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
|
||||
struct hbm_flow_control *flow)
|
||||
{
|
||||
struct mei_me_client *me_cl;
|
||||
int rets;
|
||||
|
||||
me_cl = mei_me_cl_by_id(dev, flow->me_addr);
|
||||
if (!me_cl) {
|
||||
@ -492,14 +483,19 @@ static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (WARN_ON(me_cl->props.single_recv_buf == 0))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(me_cl->props.single_recv_buf == 0)) {
|
||||
rets = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
me_cl->mei_flow_ctrl_creds++;
|
||||
dev_dbg(dev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n",
|
||||
flow->me_addr, me_cl->mei_flow_ctrl_creds);
|
||||
|
||||
return 0;
|
||||
rets = 0;
|
||||
out:
|
||||
mei_me_cl_put(me_cl);
|
||||
return rets;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -242,7 +242,7 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
|
||||
if ((hcsr & H_RST) == H_RST) {
|
||||
dev_warn(dev->dev, "H_RST is set = 0x%08X", hcsr);
|
||||
hcsr &= ~H_RST;
|
||||
mei_me_reg_write(hw, H_CSR, hcsr);
|
||||
mei_hcsr_set(hw, hcsr);
|
||||
hcsr = mei_hcsr_read(hw);
|
||||
}
|
||||
|
||||
@ -335,6 +335,7 @@ static int mei_me_hw_ready_wait(struct mei_device *dev)
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
mei_me_hw_reset_release(dev);
|
||||
dev->recvd_hw_ready = false;
|
||||
return 0;
|
||||
}
|
||||
@ -731,9 +732,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
||||
/* check if we need to start the dev */
|
||||
if (!mei_host_is_ready(dev)) {
|
||||
if (mei_hw_is_ready(dev)) {
|
||||
mei_me_hw_reset_release(dev);
|
||||
dev_dbg(dev->dev, "we need to start the dev.\n");
|
||||
|
||||
dev->recvd_hw_ready = true;
|
||||
wake_up(&dev->wait_hw_ready);
|
||||
} else {
|
||||
|
@ -303,7 +303,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
|
||||
size_t length, loff_t *offset)
|
||||
{
|
||||
struct mei_cl *cl = file->private_data;
|
||||
struct mei_me_client *me_cl;
|
||||
struct mei_me_client *me_cl = NULL;
|
||||
struct mei_cl_cb *write_cb = NULL;
|
||||
struct mei_device *dev;
|
||||
unsigned long timeout = 0;
|
||||
@ -399,12 +399,14 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
|
||||
"amthif write failed with status = %d\n", rets);
|
||||
goto out;
|
||||
}
|
||||
mei_me_cl_put(me_cl);
|
||||
mutex_unlock(&dev->device_lock);
|
||||
return length;
|
||||
}
|
||||
|
||||
rets = mei_cl_write(cl, write_cb, false);
|
||||
out:
|
||||
mei_me_cl_put(me_cl);
|
||||
mutex_unlock(&dev->device_lock);
|
||||
if (rets < 0)
|
||||
mei_io_cb_free(write_cb);
|
||||
@ -433,24 +435,19 @@ static int mei_ioctl_connect_client(struct file *file,
|
||||
cl = file->private_data;
|
||||
dev = cl->dev;
|
||||
|
||||
if (dev->dev_state != MEI_DEV_ENABLED) {
|
||||
rets = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
if (dev->dev_state != MEI_DEV_ENABLED)
|
||||
return -ENODEV;
|
||||
|
||||
if (cl->state != MEI_FILE_INITIALIZING &&
|
||||
cl->state != MEI_FILE_DISCONNECTED) {
|
||||
rets = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
cl->state != MEI_FILE_DISCONNECTED)
|
||||
return -EBUSY;
|
||||
|
||||
/* find ME client we're trying to connect to */
|
||||
me_cl = mei_me_cl_by_uuid(dev, &data->in_client_uuid);
|
||||
if (!me_cl || me_cl->props.fixed_address) {
|
||||
dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n",
|
||||
&data->in_client_uuid);
|
||||
rets = -ENOTTY;
|
||||
goto end;
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
cl->me_client_id = me_cl->client_id;
|
||||
@ -487,17 +484,16 @@ static int mei_ioctl_connect_client(struct file *file,
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
||||
/* prepare the output buffer */
|
||||
client = &data->out_client_properties;
|
||||
client->max_msg_length = me_cl->props.max_msg_length;
|
||||
client->protocol_version = me_cl->props.protocol_version;
|
||||
dev_dbg(dev->dev, "Can connect?\n");
|
||||
|
||||
|
||||
rets = mei_cl_connect(cl, file);
|
||||
|
||||
end:
|
||||
mei_me_cl_put(me_cl);
|
||||
return rets;
|
||||
}
|
||||
|
||||
|
@ -172,12 +172,14 @@ struct mei_fw_status {
|
||||
* struct mei_me_client - representation of me (fw) client
|
||||
*
|
||||
* @list: link in me client list
|
||||
* @refcnt: struct reference count
|
||||
* @props: client properties
|
||||
* @client_id: me client id
|
||||
* @mei_flow_ctrl_creds: flow control credits
|
||||
*/
|
||||
struct mei_me_client {
|
||||
struct list_head list;
|
||||
struct kref refcnt;
|
||||
struct mei_client_properties props;
|
||||
u8 client_id;
|
||||
u8 mei_flow_ctrl_creds;
|
||||
@ -345,9 +347,9 @@ struct mei_cl_device *mei_cl_add_device(struct mei_device *dev,
|
||||
struct mei_cl_ops *ops);
|
||||
void mei_cl_remove_device(struct mei_cl_device *device);
|
||||
|
||||
int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
ssize_t __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
void mei_cl_bus_rx_event(struct mei_cl *cl);
|
||||
void mei_cl_bus_remove_devices(struct mei_device *dev);
|
||||
int mei_cl_bus_init(void);
|
||||
|
@ -521,6 +521,7 @@ int mei_nfc_host_init(struct mei_device *dev)
|
||||
|
||||
cl_info->me_client_id = me_cl->client_id;
|
||||
cl_info->cl_uuid = me_cl->props.protocol_name;
|
||||
mei_me_cl_put(me_cl);
|
||||
|
||||
ret = mei_cl_link(cl_info, MEI_HOST_CLIENT_ID_ANY);
|
||||
if (ret)
|
||||
@ -539,6 +540,7 @@ int mei_nfc_host_init(struct mei_device *dev)
|
||||
|
||||
cl->me_client_id = me_cl->client_id;
|
||||
cl->cl_uuid = me_cl->props.protocol_name;
|
||||
mei_me_cl_put(me_cl);
|
||||
|
||||
ret = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY);
|
||||
if (ret)
|
||||
|
@ -76,6 +76,7 @@ int mei_wd_host_init(struct mei_device *dev)
|
||||
|
||||
cl->me_client_id = me_cl->client_id;
|
||||
cl->cl_uuid = me_cl->props.protocol_name;
|
||||
mei_me_cl_put(me_cl);
|
||||
|
||||
ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID);
|
||||
|
||||
|
@ -343,12 +343,26 @@ void st_int_recv(void *disc_data,
|
||||
/* Unknow packet? */
|
||||
default:
|
||||
type = *ptr;
|
||||
if (st_gdata->list[type] == NULL) {
|
||||
pr_err("chip/interface misbehavior dropping"
|
||||
" frame starting with 0x%02x", type);
|
||||
goto done;
|
||||
|
||||
/* Default case means non-HCILL packets,
|
||||
* possibilities are packets for:
|
||||
* (a) valid protocol - Supported Protocols within
|
||||
* the ST_MAX_CHANNELS.
|
||||
* (b) registered protocol - Checked by
|
||||
* "st_gdata->list[type] == NULL)" are supported
|
||||
* protocols only.
|
||||
* Rules out any invalid protocol and
|
||||
* unregistered protocols with channel ID < 16.
|
||||
*/
|
||||
|
||||
if ((type >= ST_MAX_CHANNELS) ||
|
||||
(st_gdata->list[type] == NULL)) {
|
||||
pr_err("chip/interface misbehavior: "
|
||||
"dropping frame starting "
|
||||
"with 0x%02x\n", type);
|
||||
goto done;
|
||||
}
|
||||
|
||||
st_gdata->rx_skb = alloc_skb(
|
||||
st_gdata->list[type]->max_frame_size,
|
||||
GFP_ATOMIC);
|
||||
@ -893,5 +907,3 @@ void st_core_exit(struct st_data_s *st_gdata)
|
||||
kfree(st_gdata);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -36,7 +36,8 @@
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ti_wilink_st.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#define MAX_ST_DEVICES 3 /* Imagine 1 on each UART for now */
|
||||
static struct platform_device *st_kim_devices[MAX_ST_DEVICES];
|
||||
@ -44,6 +45,9 @@ static struct platform_device *st_kim_devices[MAX_ST_DEVICES];
|
||||
/**********************************************************************/
|
||||
/* internal functions */
|
||||
|
||||
struct ti_st_plat_data *dt_pdata;
|
||||
static struct ti_st_plat_data *get_platform_data(struct device *dev);
|
||||
|
||||
/**
|
||||
* st_get_plat_device -
|
||||
* function which returns the reference to the platform device
|
||||
@ -215,6 +219,7 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name)
|
||||
{
|
||||
unsigned short version = 0, chip = 0, min_ver = 0, maj_ver = 0;
|
||||
const char read_ver_cmd[] = { 0x01, 0x01, 0x10, 0x00 };
|
||||
long timeout;
|
||||
|
||||
pr_debug("%s", __func__);
|
||||
|
||||
@ -224,10 +229,11 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!wait_for_completion_interruptible_timeout(
|
||||
&kim_gdata->kim_rcvd, msecs_to_jiffies(CMD_RESP_TIME))) {
|
||||
pr_err(" waiting for ver info- timed out ");
|
||||
return -ETIMEDOUT;
|
||||
timeout = wait_for_completion_interruptible_timeout(
|
||||
&kim_gdata->kim_rcvd, msecs_to_jiffies(CMD_RESP_TIME));
|
||||
if (timeout <= 0) {
|
||||
pr_err(" waiting for ver info- timed out or received signal");
|
||||
return timeout ? -ERESTARTSYS : -ETIMEDOUT;
|
||||
}
|
||||
reinit_completion(&kim_gdata->kim_rcvd);
|
||||
/* the positions 12 & 13 in the response buffer provide with the
|
||||
@ -391,13 +397,14 @@ static long download_firmware(struct kim_data_s *kim_gdata)
|
||||
break;
|
||||
case ACTION_WAIT_EVENT: /* wait */
|
||||
pr_debug("W");
|
||||
if (!wait_for_completion_interruptible_timeout(
|
||||
err = wait_for_completion_interruptible_timeout(
|
||||
&kim_gdata->kim_rcvd,
|
||||
msecs_to_jiffies(CMD_RESP_TIME))) {
|
||||
pr_err("response timeout during fw download ");
|
||||
msecs_to_jiffies(CMD_RESP_TIME));
|
||||
if (err <= 0) {
|
||||
pr_err("response timeout/signaled during fw download ");
|
||||
/* timed out */
|
||||
release_firmware(kim_gdata->fw_entry);
|
||||
return -ETIMEDOUT;
|
||||
return err ? -ERESTARTSYS : -ETIMEDOUT;
|
||||
}
|
||||
reinit_completion(&kim_gdata->kim_rcvd);
|
||||
break;
|
||||
@ -462,7 +469,12 @@ long st_kim_start(void *kim_data)
|
||||
struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data;
|
||||
|
||||
pr_info(" %s", __func__);
|
||||
pdata = kim_gdata->kim_pdev->dev.platform_data;
|
||||
if (kim_gdata->kim_pdev->dev.of_node) {
|
||||
pr_debug("use device tree data");
|
||||
pdata = dt_pdata;
|
||||
} else {
|
||||
pdata = kim_gdata->kim_pdev->dev.platform_data;
|
||||
}
|
||||
|
||||
do {
|
||||
/* platform specific enabling code here */
|
||||
@ -522,12 +534,18 @@ long st_kim_stop(void *kim_data)
|
||||
{
|
||||
long err = 0;
|
||||
struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data;
|
||||
struct ti_st_plat_data *pdata =
|
||||
kim_gdata->kim_pdev->dev.platform_data;
|
||||
struct ti_st_plat_data *pdata;
|
||||
struct tty_struct *tty = kim_gdata->core_data->tty;
|
||||
|
||||
reinit_completion(&kim_gdata->ldisc_installed);
|
||||
|
||||
if (kim_gdata->kim_pdev->dev.of_node) {
|
||||
pr_debug("use device tree data");
|
||||
pdata = dt_pdata;
|
||||
} else
|
||||
pdata = kim_gdata->kim_pdev->dev.platform_data;
|
||||
|
||||
|
||||
if (tty) { /* can be called before ldisc is installed */
|
||||
/* Flush any pending characters in the driver and discipline. */
|
||||
tty_ldisc_flush(tty);
|
||||
@ -620,7 +638,7 @@ static ssize_t show_baud_rate(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct kim_data_s *kim_data = dev_get_drvdata(dev);
|
||||
return sprintf(buf, "%ld\n", kim_data->baud_rate);
|
||||
return sprintf(buf, "%d\n", kim_data->baud_rate);
|
||||
}
|
||||
|
||||
static ssize_t show_flow_cntrl(struct device *dev,
|
||||
@ -676,12 +694,16 @@ void st_kim_ref(struct st_data_s **core_data, int id)
|
||||
struct kim_data_s *kim_gdata;
|
||||
/* get kim_gdata reference from platform device */
|
||||
pdev = st_get_plat_device(id);
|
||||
if (!pdev) {
|
||||
*core_data = NULL;
|
||||
return;
|
||||
}
|
||||
if (!pdev)
|
||||
goto err;
|
||||
kim_gdata = platform_get_drvdata(pdev);
|
||||
if (!kim_gdata)
|
||||
goto err;
|
||||
|
||||
*core_data = kim_gdata->core_data;
|
||||
return;
|
||||
err:
|
||||
*core_data = NULL;
|
||||
}
|
||||
|
||||
static int kim_version_open(struct inode *i, struct file *f)
|
||||
@ -715,13 +737,53 @@ static const struct file_operations list_debugfs_fops = {
|
||||
* board-*.c file
|
||||
*/
|
||||
|
||||
static const struct of_device_id kim_of_match[] = {
|
||||
{
|
||||
.compatible = "kim",
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, kim_of_match);
|
||||
|
||||
static struct ti_st_plat_data *get_platform_data(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
const u32 *dt_property;
|
||||
int len;
|
||||
|
||||
dt_pdata = kzalloc(sizeof(*dt_pdata), GFP_KERNEL);
|
||||
|
||||
if (!dt_pdata)
|
||||
pr_err("Can't allocate device_tree platform data\n");
|
||||
|
||||
dt_property = of_get_property(np, "dev_name", &len);
|
||||
if (dt_property)
|
||||
memcpy(&dt_pdata->dev_name, dt_property, len);
|
||||
of_property_read_u32(np, "nshutdown_gpio",
|
||||
&dt_pdata->nshutdown_gpio);
|
||||
of_property_read_u32(np, "flow_cntrl", &dt_pdata->flow_cntrl);
|
||||
of_property_read_u32(np, "baud_rate", &dt_pdata->baud_rate);
|
||||
|
||||
return dt_pdata;
|
||||
}
|
||||
|
||||
static struct dentry *kim_debugfs_dir;
|
||||
static int kim_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct kim_data_s *kim_gdata;
|
||||
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
|
||||
struct ti_st_plat_data *pdata;
|
||||
int err;
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
pdata = get_platform_data(&pdev->dev);
|
||||
else
|
||||
pdata = pdev->dev.platform_data;
|
||||
|
||||
if (pdata == NULL) {
|
||||
dev_err(&pdev->dev, "Platform Data is missing\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) {
|
||||
/* multiple devices could exist */
|
||||
st_kim_devices[pdev->id] = pdev;
|
||||
@ -750,14 +812,14 @@ static int kim_probe(struct platform_device *pdev)
|
||||
kim_gdata->nshutdown = pdata->nshutdown_gpio;
|
||||
err = gpio_request(kim_gdata->nshutdown, "kim");
|
||||
if (unlikely(err)) {
|
||||
pr_err(" gpio %ld request failed ", kim_gdata->nshutdown);
|
||||
pr_err(" gpio %d request failed ", kim_gdata->nshutdown);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Configure nShutdown GPIO as output=0 */
|
||||
err = gpio_direction_output(kim_gdata->nshutdown, 0);
|
||||
if (unlikely(err)) {
|
||||
pr_err(" unable to configure gpio %ld", kim_gdata->nshutdown);
|
||||
pr_err(" unable to configure gpio %d", kim_gdata->nshutdown);
|
||||
return err;
|
||||
}
|
||||
/* get reference of pdev for request_firmware
|
||||
@ -781,8 +843,7 @@ static int kim_probe(struct platform_device *pdev)
|
||||
kim_debugfs_dir = debugfs_create_dir("ti-st", NULL);
|
||||
if (!kim_debugfs_dir) {
|
||||
pr_err(" debugfs entries creation failed ");
|
||||
err = -EIO;
|
||||
goto err_debugfs_dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
debugfs_create_file("version", S_IRUGO, kim_debugfs_dir,
|
||||
@ -791,9 +852,6 @@ static int kim_probe(struct platform_device *pdev)
|
||||
kim_gdata, &list_debugfs_fops);
|
||||
return 0;
|
||||
|
||||
err_debugfs_dir:
|
||||
sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp);
|
||||
|
||||
err_sysfs_group:
|
||||
st_core_exit(kim_gdata->core_data);
|
||||
|
||||
@ -806,9 +864,16 @@ err_core_init:
|
||||
static int kim_remove(struct platform_device *pdev)
|
||||
{
|
||||
/* free the GPIOs requested */
|
||||
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
|
||||
struct ti_st_plat_data *pdata;
|
||||
struct kim_data_s *kim_gdata;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
pr_debug("use device tree data");
|
||||
pdata = dt_pdata;
|
||||
} else {
|
||||
pdata = pdev->dev.platform_data;
|
||||
}
|
||||
|
||||
kim_gdata = platform_get_drvdata(pdev);
|
||||
|
||||
/* Free the Bluetooth/FM/GPIO
|
||||
@ -826,27 +891,44 @@ static int kim_remove(struct platform_device *pdev)
|
||||
|
||||
kfree(kim_gdata);
|
||||
kim_gdata = NULL;
|
||||
kfree(dt_pdata);
|
||||
dt_pdata = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kim_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
|
||||
struct ti_st_plat_data *pdata;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
pr_debug("use device tree data");
|
||||
pdata = dt_pdata;
|
||||
} else {
|
||||
pdata = pdev->dev.platform_data;
|
||||
}
|
||||
|
||||
if (pdata->suspend)
|
||||
return pdata->suspend(pdev, state);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kim_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
|
||||
struct ti_st_plat_data *pdata;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
pr_debug("use device tree data");
|
||||
pdata = dt_pdata;
|
||||
} else {
|
||||
pdata = pdev->dev.platform_data;
|
||||
}
|
||||
|
||||
if (pdata->resume)
|
||||
return pdata->resume(pdev);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
@ -858,6 +940,8 @@ static struct platform_driver kim_platform_driver = {
|
||||
.resume = kim_resume,
|
||||
.driver = {
|
||||
.name = "kim",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(kim_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/ti_wilink_st.h>
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
/* internal functions */
|
||||
static void send_ll_cmd(struct st_data_s *st_data,
|
||||
unsigned char cmd)
|
||||
@ -53,7 +54,13 @@ static void ll_device_want_to_sleep(struct st_data_s *st_data)
|
||||
|
||||
/* communicate to platform about chip asleep */
|
||||
kim_data = st_data->kim_data;
|
||||
pdata = kim_data->kim_pdev->dev.platform_data;
|
||||
if (kim_data->kim_pdev->dev.of_node) {
|
||||
pr_debug("use device tree data");
|
||||
pdata = dt_pdata;
|
||||
} else {
|
||||
pdata = kim_data->kim_pdev->dev.platform_data;
|
||||
}
|
||||
|
||||
if (pdata->chip_asleep)
|
||||
pdata->chip_asleep(NULL);
|
||||
}
|
||||
@ -86,7 +93,13 @@ static void ll_device_want_to_wakeup(struct st_data_s *st_data)
|
||||
|
||||
/* communicate to platform about chip wakeup */
|
||||
kim_data = st_data->kim_data;
|
||||
pdata = kim_data->kim_pdev->dev.platform_data;
|
||||
if (kim_data->kim_pdev->dev.of_node) {
|
||||
pr_debug("use device tree data");
|
||||
pdata = dt_pdata;
|
||||
} else {
|
||||
pdata = kim_data->kim_pdev->dev.platform_data;
|
||||
}
|
||||
|
||||
if (pdata->chip_awake)
|
||||
pdata->chip_awake(NULL);
|
||||
}
|
||||
|
@ -113,5 +113,5 @@ module_exit(vmci_drv_exit);
|
||||
|
||||
MODULE_AUTHOR("VMware, Inc.");
|
||||
MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface.");
|
||||
MODULE_VERSION("1.1.0.0-k");
|
||||
MODULE_VERSION("1.1.1.0-k");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -218,13 +218,12 @@ static int drv_cp_harray_to_user(void __user *user_buf_uva,
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets up a given context for notify to work. Calls drv_map_bool_ptr()
|
||||
* which maps the notify boolean in user VA in kernel space.
|
||||
* Sets up a given context for notify to work. Maps the notify
|
||||
* boolean in user VA into kernel space.
|
||||
*/
|
||||
static int vmci_host_setup_notify(struct vmci_ctx *context,
|
||||
unsigned long uva)
|
||||
{
|
||||
struct page *page;
|
||||
int retval;
|
||||
|
||||
if (context->notify_page) {
|
||||
@ -243,14 +242,16 @@ static int vmci_host_setup_notify(struct vmci_ctx *context,
|
||||
/*
|
||||
* Lock physical page backing a given user VA.
|
||||
*/
|
||||
retval = get_user_pages_fast(PAGE_ALIGN(uva), 1, 1, &page);
|
||||
if (retval != 1)
|
||||
retval = get_user_pages_fast(uva, 1, 1, &context->notify_page);
|
||||
if (retval != 1) {
|
||||
context->notify_page = NULL;
|
||||
return VMCI_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map the locked page and set up notify pointer.
|
||||
*/
|
||||
context->notify = kmap(page) + (uva & (PAGE_SIZE - 1));
|
||||
context->notify = kmap(context->notify_page) + (uva & (PAGE_SIZE - 1));
|
||||
vmci_ctx_check_signal_notify(context);
|
||||
|
||||
return VMCI_SUCCESS;
|
||||
|
@ -69,7 +69,8 @@ config YENTA
|
||||
tristate "CardBus yenta-compatible bridge support"
|
||||
depends on PCI
|
||||
select CARDBUS if !EXPERT
|
||||
select PCCARD_NONSTATIC if PCMCIA != n
|
||||
select PCCARD_NONSTATIC if PCMCIA != n && ISA
|
||||
select PCCARD_PCI if PCMCIA !=n && !ISA
|
||||
---help---
|
||||
This option enables support for CardBus host bridges. Virtually
|
||||
all modern PCMCIA bridges are CardBus compatible. A "bridge" is
|
||||
@ -109,7 +110,8 @@ config YENTA_TOSHIBA
|
||||
config PD6729
|
||||
tristate "Cirrus PD6729 compatible bridge support"
|
||||
depends on PCMCIA && PCI
|
||||
select PCCARD_NONSTATIC
|
||||
select PCCARD_NONSTATIC if PCMCIA != n && ISA
|
||||
select PCCARD_PCI if PCMCIA !=n && !ISA
|
||||
help
|
||||
This provides support for the Cirrus PD6729 PCI-to-PCMCIA bridge
|
||||
device, found in some older laptops and PCMCIA card readers.
|
||||
@ -117,7 +119,8 @@ config PD6729
|
||||
config I82092
|
||||
tristate "i82092 compatible bridge support"
|
||||
depends on PCMCIA && PCI
|
||||
select PCCARD_NONSTATIC
|
||||
select PCCARD_NONSTATIC if PCMCIA != n && ISA
|
||||
select PCCARD_PCI if PCMCIA !=n && !ISA
|
||||
help
|
||||
This provides support for the Intel I82092AA PCI-to-PCMCIA bridge device,
|
||||
found in some older laptops and more commonly in evaluation boards for the
|
||||
@ -287,6 +290,9 @@ config ELECTRA_CF
|
||||
Say Y here to support the CompactFlash controller on the
|
||||
PA Semi Electra eval board.
|
||||
|
||||
config PCCARD_PCI
|
||||
bool
|
||||
|
||||
config PCCARD_NONSTATIC
|
||||
bool
|
||||
|
||||
|
@ -12,6 +12,7 @@ obj-$(CONFIG_PCMCIA) += pcmcia.o
|
||||
pcmcia_rsrc-y += rsrc_mgr.o
|
||||
pcmcia_rsrc-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o
|
||||
pcmcia_rsrc-$(CONFIG_PCCARD_IODYN) += rsrc_iodyn.o
|
||||
pcmcia_rsrc-$(CONFIG_PCCARD_PCI) += rsrc_pci.o
|
||||
obj-$(CONFIG_PCCARD) += pcmcia_rsrc.o
|
||||
|
||||
|
||||
|
@ -168,9 +168,12 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
||||
} else {
|
||||
u_int inc = 1, card_offset, flags;
|
||||
|
||||
if (addr > CISTPL_MAX_CIS_SIZE)
|
||||
if (addr > CISTPL_MAX_CIS_SIZE) {
|
||||
dev_dbg(&s->dev,
|
||||
"attempt to read CIS mem at addr %#x", addr);
|
||||
memset(ptr, 0xff, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
|
||||
if (attr) {
|
||||
@ -1383,7 +1386,7 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
|
||||
if (!s)
|
||||
return -EINVAL;
|
||||
|
||||
if (s->functions) {
|
||||
if (s->functions || !(s->state & SOCKET_PRESENT)) {
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1448,10 +1451,26 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
|
||||
done:
|
||||
/* invalidate CIS cache on failure */
|
||||
if (!dev_ok || !ident_ok || !count) {
|
||||
mutex_lock(&s->ops_mutex);
|
||||
destroy_cis_cache(s);
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
ret = -EIO;
|
||||
#if defined(CONFIG_MTD_PCMCIA_ANONYMOUS)
|
||||
/* Set up as an anonymous card. If we don't have anonymous
|
||||
memory support then just error the card as there is no
|
||||
point trying to second guess.
|
||||
|
||||
Note: some cards have just a device entry, it may be
|
||||
worth extending support to cover these in future */
|
||||
if (!dev_ok || !ident_ok) {
|
||||
dev_info(&s->dev, "no CIS, assuming an anonymous memory card.\n");
|
||||
pcmcia_replace_cis(s, "\xFF", 1);
|
||||
count = 1;
|
||||
ret = 0;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
mutex_lock(&s->ops_mutex);
|
||||
destroy_cis_cache(s);
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
ret = -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (info)
|
||||
|
@ -80,9 +80,9 @@ struct pccard_resource_ops {
|
||||
* Stuff internal to module "pcmcia_rsrc":
|
||||
*/
|
||||
extern int static_init(struct pcmcia_socket *s);
|
||||
extern struct resource *pcmcia_make_resource(unsigned long start,
|
||||
unsigned long end,
|
||||
int flags, const char *name);
|
||||
extern struct resource *pcmcia_make_resource(resource_size_t start,
|
||||
resource_size_t end,
|
||||
unsigned long flags, const char *name);
|
||||
|
||||
/*
|
||||
* Stuff internal to module "pcmcia_core":
|
||||
|
@ -667,6 +667,9 @@ static void pcmcia_requery(struct pcmcia_socket *s)
|
||||
{
|
||||
int has_pfc;
|
||||
|
||||
if (!(s->state & SOCKET_PRESENT))
|
||||
return;
|
||||
|
||||
if (s->functions == 0) {
|
||||
pcmcia_card_add(s);
|
||||
return;
|
||||
|
@ -30,8 +30,9 @@ int static_init(struct pcmcia_socket *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct resource *pcmcia_make_resource(unsigned long start, unsigned long end,
|
||||
int flags, const char *name)
|
||||
struct resource *pcmcia_make_resource(resource_size_t start,
|
||||
resource_size_t end,
|
||||
unsigned long flags, const char *name)
|
||||
{
|
||||
struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
|
||||
|
||||
|
173
drivers/pcmcia/rsrc_pci.c
Normal file
173
drivers/pcmcia/rsrc_pci.c
Normal file
@ -0,0 +1,173 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <pcmcia/ss.h>
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include "cs_internal.h"
|
||||
|
||||
|
||||
struct pcmcia_align_data {
|
||||
unsigned long mask;
|
||||
unsigned long offset;
|
||||
};
|
||||
|
||||
static resource_size_t pcmcia_align(void *align_data,
|
||||
const struct resource *res,
|
||||
resource_size_t size, resource_size_t align)
|
||||
{
|
||||
struct pcmcia_align_data *data = align_data;
|
||||
resource_size_t start;
|
||||
|
||||
start = (res->start & ~data->mask) + data->offset;
|
||||
if (start < res->start)
|
||||
start += data->mask + 1;
|
||||
return start;
|
||||
}
|
||||
|
||||
static struct resource *find_io_region(struct pcmcia_socket *s,
|
||||
unsigned long base, int num,
|
||||
unsigned long align)
|
||||
{
|
||||
struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO,
|
||||
dev_name(&s->dev));
|
||||
struct pcmcia_align_data data;
|
||||
int ret;
|
||||
|
||||
data.mask = align - 1;
|
||||
data.offset = base & data.mask;
|
||||
|
||||
ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
|
||||
base, 0, pcmcia_align, &data);
|
||||
if (ret != 0) {
|
||||
kfree(res);
|
||||
res = NULL;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int res_pci_find_io(struct pcmcia_socket *s, unsigned int attr,
|
||||
unsigned int *base, unsigned int num,
|
||||
unsigned int align, struct resource **parent)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
/* Check for an already-allocated window that must conflict with
|
||||
* what was asked for. It is a hack because it does not catch all
|
||||
* potential conflicts, just the most obvious ones.
|
||||
*/
|
||||
for (i = 0; i < MAX_IO_WIN; i++) {
|
||||
if (!s->io[i].res)
|
||||
continue;
|
||||
|
||||
if (!*base)
|
||||
continue;
|
||||
|
||||
if ((s->io[i].res->start & (align-1)) == *base)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_IO_WIN; i++) {
|
||||
struct resource *res = s->io[i].res;
|
||||
unsigned int try;
|
||||
|
||||
if (res && (res->flags & IORESOURCE_BITS) !=
|
||||
(attr & IORESOURCE_BITS))
|
||||
continue;
|
||||
|
||||
if (!res) {
|
||||
if (align == 0)
|
||||
align = 0x10000;
|
||||
|
||||
res = s->io[i].res = find_io_region(s, *base, num,
|
||||
align);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
*base = res->start;
|
||||
s->io[i].res->flags =
|
||||
((res->flags & ~IORESOURCE_BITS) |
|
||||
(attr & IORESOURCE_BITS));
|
||||
s->io[i].InUse = num;
|
||||
*parent = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to extend top of window */
|
||||
try = res->end + 1;
|
||||
if ((*base == 0) || (*base == try)) {
|
||||
ret = adjust_resource(s->io[i].res, res->start,
|
||||
resource_size(res) + num);
|
||||
if (ret)
|
||||
continue;
|
||||
*base = try;
|
||||
s->io[i].InUse += num;
|
||||
*parent = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to extend bottom of window */
|
||||
try = res->start - num;
|
||||
if ((*base == 0) || (*base == try)) {
|
||||
ret = adjust_resource(s->io[i].res,
|
||||
res->start - num,
|
||||
resource_size(res) + num);
|
||||
if (ret)
|
||||
continue;
|
||||
*base = try;
|
||||
s->io[i].InUse += num;
|
||||
*parent = res;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct resource *res_pci_find_mem(u_long base, u_long num,
|
||||
u_long align, int low, struct pcmcia_socket *s)
|
||||
{
|
||||
struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM,
|
||||
dev_name(&s->dev));
|
||||
struct pcmcia_align_data data;
|
||||
unsigned long min;
|
||||
int ret;
|
||||
|
||||
if (align < 0x20000)
|
||||
align = 0x20000;
|
||||
data.mask = align - 1;
|
||||
data.offset = base & data.mask;
|
||||
|
||||
min = 0;
|
||||
if (!low)
|
||||
min = 0x100000UL;
|
||||
|
||||
ret = pci_bus_alloc_resource(s->cb_dev->bus,
|
||||
res, num, 1, min, 0,
|
||||
pcmcia_align, &data);
|
||||
|
||||
if (ret != 0) {
|
||||
kfree(res);
|
||||
res = NULL;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static int res_pci_init(struct pcmcia_socket *s)
|
||||
{
|
||||
if (!s->cb_dev || !(s->features & SS_CAP_PAGE_REGS)) {
|
||||
dev_err(&s->dev, "not supported by res_pci\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pccard_resource_ops pccard_nonstatic_ops = {
|
||||
.validate_mem = NULL,
|
||||
.find_io = res_pci_find_io,
|
||||
.find_mem = res_pci_find_mem,
|
||||
.init = res_pci_init,
|
||||
.exit = NULL,
|
||||
};
|
||||
EXPORT_SYMBOL(pccard_nonstatic_ops);
|
@ -181,7 +181,8 @@ static int pnp_dock_thread(void *unused)
|
||||
break;
|
||||
default:
|
||||
pnpbios_print_status("pnp_dock_thread", status);
|
||||
continue;
|
||||
printk(KERN_WARNING "PnPBIOS: disabling dock monitoring.\n");
|
||||
complete_and_exit(&unload_sem, 0);
|
||||
}
|
||||
if (d != docked) {
|
||||
if (pnp_dock_event(d, &now) == 0) {
|
||||
|
@ -104,6 +104,26 @@ config UIO_NETX
|
||||
To compile this driver as a module, choose M here; the module
|
||||
will be called uio_netx.
|
||||
|
||||
config UIO_FSL_ELBC_GPCM
|
||||
tristate "eLBC/GPCM driver"
|
||||
depends on FSL_LBC
|
||||
help
|
||||
Generic driver for accessing a peripheral connected to an eLBC port
|
||||
that is running in GPCM mode. GPCM is an interface for simple lower
|
||||
performance memories and memory-mapped devices. For devices using
|
||||
FCM or UPM eLBC modes, other device-specific drivers are available.
|
||||
|
||||
config UIO_FSL_ELBC_GPCM_NETX5152
|
||||
bool "eLBC/GPCM netX 51/52 support"
|
||||
depends on UIO_FSL_ELBC_GPCM
|
||||
help
|
||||
This will add support for netX 51/52 devices connected via eLBC/GPCM.
|
||||
In particular, it implements interrupt handling. This can be used
|
||||
together with the userspace netX stack from Hilscher.
|
||||
|
||||
Information about this hardware can be found at:
|
||||
http://www.hilscher.com/netx
|
||||
|
||||
config UIO_PRUSS
|
||||
tristate "Texas Instruments PRUSS driver"
|
||||
depends on ARCH_DAVINCI_DA850
|
||||
|
@ -8,3 +8,4 @@ obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o
|
||||
obj-$(CONFIG_UIO_NETX) += uio_netx.o
|
||||
obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o
|
||||
obj-$(CONFIG_UIO_MF624) += uio_mf624.o
|
||||
obj-$(CONFIG_UIO_FSL_ELBC_GPCM) += uio_fsl_elbc_gpcm.o
|
||||
|
499
drivers/uio/uio_fsl_elbc_gpcm.c
Normal file
499
drivers/uio/uio_fsl_elbc_gpcm.c
Normal file
@ -0,0 +1,499 @@
|
||||
/* uio_fsl_elbc_gpcm: UIO driver for eLBC/GPCM peripherals
|
||||
|
||||
Copyright (C) 2014 Linutronix GmbH
|
||||
Author: John Ogness <john.ogness@linutronix.de>
|
||||
|
||||
This driver provides UIO access to memory of a peripheral connected
|
||||
to the Freescale enhanced local bus controller (eLBC) interface
|
||||
using the general purpose chip-select mode (GPCM).
|
||||
|
||||
Here is an example of the device tree entries:
|
||||
|
||||
localbus@ffe05000 {
|
||||
ranges = <0x2 0x0 0x0 0xff810000 0x10000>;
|
||||
|
||||
dpm@2,0 {
|
||||
compatible = "fsl,elbc-gpcm-uio";
|
||||
reg = <0x2 0x0 0x10000>;
|
||||
elbc-gpcm-br = <0xff810800>;
|
||||
elbc-gpcm-or = <0xffff09f7>;
|
||||
interrupt-parent = <&mpic>;
|
||||
interrupts = <4 1>;
|
||||
device_type = "netx5152";
|
||||
uio_name = "netx_custom";
|
||||
netx5152,init-win0-offset = <0x0>;
|
||||
};
|
||||
};
|
||||
|
||||
Only the entries reg (to identify bank) and elbc-gpcm-* (initial BR/OR
|
||||
values) are required. The entries interrupt*, device_type, and uio_name
|
||||
are optional (as well as any type-specific options such as
|
||||
netx5152,init-win0-offset). As long as no interrupt handler is needed,
|
||||
this driver can be used without any type-specific implementation.
|
||||
|
||||
The netx5152 type has been tested to work with the netX 51/52 hardware
|
||||
from Hilscher using the Hilscher userspace netX stack.
|
||||
|
||||
The netx5152 type should serve as a model to add new type-specific
|
||||
devices as needed.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/uio_driver.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#include <asm/fsl_lbc.h>
|
||||
|
||||
#define MAX_BANKS 8
|
||||
|
||||
struct fsl_elbc_gpcm {
|
||||
struct device *dev;
|
||||
struct fsl_lbc_regs __iomem *lbc;
|
||||
u32 bank;
|
||||
const char *name;
|
||||
|
||||
void (*init)(struct uio_info *info);
|
||||
void (*shutdown)(struct uio_info *info, bool init_err);
|
||||
irqreturn_t (*irq_handler)(int irq, struct uio_info *info);
|
||||
};
|
||||
|
||||
static ssize_t reg_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf);
|
||||
static ssize_t reg_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
DEVICE_ATTR(reg_br, S_IRUGO|S_IWUSR|S_IWGRP, reg_show, reg_store);
|
||||
DEVICE_ATTR(reg_or, S_IRUGO|S_IWUSR|S_IWGRP, reg_show, reg_store);
|
||||
|
||||
static ssize_t reg_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct uio_info *info = platform_get_drvdata(pdev);
|
||||
struct fsl_elbc_gpcm *priv = info->priv;
|
||||
struct fsl_lbc_bank *bank = &priv->lbc->bank[priv->bank];
|
||||
|
||||
if (attr == &dev_attr_reg_br) {
|
||||
return scnprintf(buf, PAGE_SIZE, "0x%08x\n",
|
||||
in_be32(&bank->br));
|
||||
|
||||
} else if (attr == &dev_attr_reg_or) {
|
||||
return scnprintf(buf, PAGE_SIZE, "0x%08x\n",
|
||||
in_be32(&bank->or));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t reg_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct uio_info *info = platform_get_drvdata(pdev);
|
||||
struct fsl_elbc_gpcm *priv = info->priv;
|
||||
struct fsl_lbc_bank *bank = &priv->lbc->bank[priv->bank];
|
||||
unsigned long val;
|
||||
u32 reg_br_cur;
|
||||
u32 reg_or_cur;
|
||||
u32 reg_new;
|
||||
|
||||
/* parse use input */
|
||||
if (kstrtoul(buf, 0, &val) != 0)
|
||||
return -EINVAL;
|
||||
reg_new = (u32)val;
|
||||
|
||||
/* read current values */
|
||||
reg_br_cur = in_be32(&bank->br);
|
||||
reg_or_cur = in_be32(&bank->or);
|
||||
|
||||
if (attr == &dev_attr_reg_br) {
|
||||
/* not allowed to change effective base address */
|
||||
if ((reg_br_cur & reg_or_cur & BR_BA) !=
|
||||
(reg_new & reg_or_cur & BR_BA)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* not allowed to change mode */
|
||||
if ((reg_new & BR_MSEL) != BR_MS_GPCM)
|
||||
return -EINVAL;
|
||||
|
||||
/* write new value (force valid) */
|
||||
out_be32(&bank->br, reg_new | BR_V);
|
||||
|
||||
} else if (attr == &dev_attr_reg_or) {
|
||||
/* not allowed to change access mask */
|
||||
if ((reg_or_cur & OR_GPCM_AM) != (reg_new & OR_GPCM_AM))
|
||||
return -EINVAL;
|
||||
|
||||
/* write new value */
|
||||
out_be32(&bank->or, reg_new);
|
||||
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UIO_FSL_ELBC_GPCM_NETX5152
|
||||
#define DPM_HOST_WIN0_OFFSET 0xff00
|
||||
#define DPM_HOST_INT_STAT0 0xe0
|
||||
#define DPM_HOST_INT_EN0 0xf0
|
||||
#define DPM_HOST_INT_MASK 0xe600ffff
|
||||
#define DPM_HOST_INT_GLOBAL_EN 0x80000000
|
||||
|
||||
static irqreturn_t netx5152_irq_handler(int irq, struct uio_info *info)
|
||||
{
|
||||
void __iomem *reg_int_en = info->mem[0].internal_addr +
|
||||
DPM_HOST_WIN0_OFFSET +
|
||||
DPM_HOST_INT_EN0;
|
||||
void __iomem *reg_int_stat = info->mem[0].internal_addr +
|
||||
DPM_HOST_WIN0_OFFSET +
|
||||
DPM_HOST_INT_STAT0;
|
||||
|
||||
/* check if an interrupt is enabled and active */
|
||||
if ((ioread32(reg_int_en) & ioread32(reg_int_stat) &
|
||||
DPM_HOST_INT_MASK) == 0) {
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* disable interrupts */
|
||||
iowrite32(ioread32(reg_int_en) & ~DPM_HOST_INT_GLOBAL_EN, reg_int_en);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void netx5152_init(struct uio_info *info)
|
||||
{
|
||||
unsigned long win0_offset = DPM_HOST_WIN0_OFFSET;
|
||||
struct fsl_elbc_gpcm *priv = info->priv;
|
||||
const void *prop;
|
||||
|
||||
/* get an optional initial win0 offset */
|
||||
prop = of_get_property(priv->dev->of_node,
|
||||
"netx5152,init-win0-offset", NULL);
|
||||
if (prop)
|
||||
win0_offset = of_read_ulong(prop, 1);
|
||||
|
||||
/* disable interrupts */
|
||||
iowrite32(0, info->mem[0].internal_addr + win0_offset +
|
||||
DPM_HOST_INT_EN0);
|
||||
}
|
||||
|
||||
static void netx5152_shutdown(struct uio_info *info, bool init_err)
|
||||
{
|
||||
if (init_err)
|
||||
return;
|
||||
|
||||
/* disable interrupts */
|
||||
iowrite32(0, info->mem[0].internal_addr + DPM_HOST_WIN0_OFFSET +
|
||||
DPM_HOST_INT_EN0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void setup_periph(struct fsl_elbc_gpcm *priv,
|
||||
const char *type)
|
||||
{
|
||||
#ifdef CONFIG_UIO_FSL_ELBC_GPCM_NETX5152
|
||||
if (strcmp(type, "netx5152") == 0) {
|
||||
priv->irq_handler = netx5152_irq_handler;
|
||||
priv->init = netx5152_init;
|
||||
priv->shutdown = netx5152_shutdown;
|
||||
priv->name = "netX 51/52";
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int check_of_data(struct fsl_elbc_gpcm *priv,
|
||||
struct resource *res,
|
||||
u32 reg_br, u32 reg_or)
|
||||
{
|
||||
/* check specified bank */
|
||||
if (priv->bank >= MAX_BANKS) {
|
||||
dev_err(priv->dev, "invalid bank\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* check specified mode (BR_MS_GPCM is 0) */
|
||||
if ((reg_br & BR_MSEL) != BR_MS_GPCM) {
|
||||
dev_err(priv->dev, "unsupported mode\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* check specified mask vs. resource size */
|
||||
if ((~(reg_or & OR_GPCM_AM) + 1) != resource_size(res)) {
|
||||
dev_err(priv->dev, "address mask / size mismatch\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* check specified address */
|
||||
if ((reg_br & reg_or & BR_BA) != fsl_lbc_addr(res->start)) {
|
||||
dev_err(priv->dev, "base address mismatch\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_of_data(struct fsl_elbc_gpcm *priv, struct device_node *node,
|
||||
struct resource *res, u32 *reg_br,
|
||||
u32 *reg_or, unsigned int *irq, char **name)
|
||||
{
|
||||
const char *dt_name;
|
||||
const char *type;
|
||||
int ret;
|
||||
|
||||
/* get the memory resource */
|
||||
ret = of_address_to_resource(node, 0, res);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to get resource\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get the bank number */
|
||||
ret = of_property_read_u32(node, "reg", &priv->bank);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to get bank number\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get BR value to set */
|
||||
ret = of_property_read_u32(node, "elbc-gpcm-br", reg_br);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "missing elbc-gpcm-br value\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get OR value to set */
|
||||
ret = of_property_read_u32(node, "elbc-gpcm-or", reg_or);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "missing elbc-gpcm-or value\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get optional peripheral type */
|
||||
priv->name = "generic";
|
||||
if (of_property_read_string(node, "device_type", &type) == 0)
|
||||
setup_periph(priv, type);
|
||||
|
||||
/* get optional irq value */
|
||||
*irq = irq_of_parse_and_map(node, 0);
|
||||
|
||||
/* sanity check device tree data */
|
||||
ret = check_of_data(priv, res, *reg_br, *reg_or);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* get optional uio name */
|
||||
if (of_property_read_string(node, "uio_name", &dt_name) != 0)
|
||||
dt_name = "eLBC_GPCM";
|
||||
*name = kstrdup(dt_name, GFP_KERNEL);
|
||||
if (!*name)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct fsl_elbc_gpcm *priv;
|
||||
struct uio_info *info;
|
||||
char *uio_name = NULL;
|
||||
struct resource res;
|
||||
unsigned int irq;
|
||||
u32 reg_br_cur;
|
||||
u32 reg_or_cur;
|
||||
u32 reg_br_new;
|
||||
u32 reg_or_new;
|
||||
int ret;
|
||||
|
||||
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
|
||||
return -ENODEV;
|
||||
|
||||
/* allocate private data */
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
priv->dev = &pdev->dev;
|
||||
priv->lbc = fsl_lbc_ctrl_dev->regs;
|
||||
|
||||
/* get device tree data */
|
||||
ret = get_of_data(priv, node, &res, ®_br_new, ®_or_new,
|
||||
&irq, &uio_name);
|
||||
if (ret)
|
||||
goto out_err0;
|
||||
|
||||
/* allocate UIO structure */
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
ret = -ENOMEM;
|
||||
goto out_err0;
|
||||
}
|
||||
|
||||
/* get current BR/OR values */
|
||||
reg_br_cur = in_be32(&priv->lbc->bank[priv->bank].br);
|
||||
reg_or_cur = in_be32(&priv->lbc->bank[priv->bank].or);
|
||||
|
||||
/* if bank already configured, make sure it matches */
|
||||
if ((reg_br_cur & BR_V)) {
|
||||
if ((reg_br_cur & BR_MSEL) != BR_MS_GPCM ||
|
||||
(reg_br_cur & reg_or_cur & BR_BA)
|
||||
!= fsl_lbc_addr(res.start)) {
|
||||
dev_err(priv->dev,
|
||||
"bank in use by another peripheral\n");
|
||||
ret = -ENODEV;
|
||||
goto out_err1;
|
||||
}
|
||||
|
||||
/* warn if behavior settings changing */
|
||||
if ((reg_br_cur & ~(BR_BA | BR_V)) !=
|
||||
(reg_br_new & ~(BR_BA | BR_V))) {
|
||||
dev_warn(priv->dev,
|
||||
"modifying BR settings: 0x%08x -> 0x%08x",
|
||||
reg_br_cur, reg_br_new);
|
||||
}
|
||||
if ((reg_or_cur & ~OR_GPCM_AM) != (reg_or_new & ~OR_GPCM_AM)) {
|
||||
dev_warn(priv->dev,
|
||||
"modifying OR settings: 0x%08x -> 0x%08x",
|
||||
reg_or_cur, reg_or_new);
|
||||
}
|
||||
}
|
||||
|
||||
/* configure the bank (force base address and GPCM) */
|
||||
reg_br_new &= ~(BR_BA | BR_MSEL);
|
||||
reg_br_new |= fsl_lbc_addr(res.start) | BR_MS_GPCM | BR_V;
|
||||
out_be32(&priv->lbc->bank[priv->bank].or, reg_or_new);
|
||||
out_be32(&priv->lbc->bank[priv->bank].br, reg_br_new);
|
||||
|
||||
/* map the memory resource */
|
||||
info->mem[0].internal_addr = ioremap(res.start, resource_size(&res));
|
||||
if (!info->mem[0].internal_addr) {
|
||||
dev_err(priv->dev, "failed to map chip region\n");
|
||||
ret = -ENODEV;
|
||||
goto out_err1;
|
||||
}
|
||||
|
||||
/* set all UIO data */
|
||||
if (node->name)
|
||||
info->mem[0].name = kstrdup(node->name, GFP_KERNEL);
|
||||
info->mem[0].addr = res.start;
|
||||
info->mem[0].size = resource_size(&res);
|
||||
info->mem[0].memtype = UIO_MEM_PHYS;
|
||||
info->priv = priv;
|
||||
info->name = uio_name;
|
||||
info->version = "0.0.1";
|
||||
if (irq != NO_IRQ) {
|
||||
if (priv->irq_handler) {
|
||||
info->irq = irq;
|
||||
info->irq_flags = IRQF_SHARED;
|
||||
info->handler = priv->irq_handler;
|
||||
} else {
|
||||
irq = NO_IRQ;
|
||||
dev_warn(priv->dev, "ignoring irq, no handler\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->init)
|
||||
priv->init(info);
|
||||
|
||||
/* register UIO device */
|
||||
if (uio_register_device(priv->dev, info) != 0) {
|
||||
dev_err(priv->dev, "UIO registration failed\n");
|
||||
ret = -ENODEV;
|
||||
goto out_err2;
|
||||
}
|
||||
|
||||
/* store private data */
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
/* create sysfs files */
|
||||
ret = device_create_file(priv->dev, &dev_attr_reg_br);
|
||||
if (ret)
|
||||
goto out_err3;
|
||||
ret = device_create_file(priv->dev, &dev_attr_reg_or);
|
||||
if (ret)
|
||||
goto out_err4;
|
||||
|
||||
dev_info(priv->dev,
|
||||
"eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n",
|
||||
priv->name, (unsigned long long)res.start, priv->bank,
|
||||
irq != NO_IRQ ? irq : -1);
|
||||
|
||||
return 0;
|
||||
out_err4:
|
||||
device_remove_file(priv->dev, &dev_attr_reg_br);
|
||||
out_err3:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
uio_unregister_device(info);
|
||||
out_err2:
|
||||
if (priv->shutdown)
|
||||
priv->shutdown(info, true);
|
||||
iounmap(info->mem[0].internal_addr);
|
||||
out_err1:
|
||||
kfree(info->mem[0].name);
|
||||
kfree(info);
|
||||
out_err0:
|
||||
kfree(uio_name);
|
||||
kfree(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uio_fsl_elbc_gpcm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct uio_info *info = platform_get_drvdata(pdev);
|
||||
struct fsl_elbc_gpcm *priv = info->priv;
|
||||
|
||||
device_remove_file(priv->dev, &dev_attr_reg_or);
|
||||
device_remove_file(priv->dev, &dev_attr_reg_br);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
uio_unregister_device(info);
|
||||
if (priv->shutdown)
|
||||
priv->shutdown(info, false);
|
||||
iounmap(info->mem[0].internal_addr);
|
||||
kfree(info->mem[0].name);
|
||||
kfree(info->name);
|
||||
kfree(info);
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static const struct of_device_id uio_fsl_elbc_gpcm_match[] = {
|
||||
{ .compatible = "fsl,elbc-gpcm-uio", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver uio_fsl_elbc_gpcm_driver = {
|
||||
.driver = {
|
||||
.name = "fsl,elbc-gpcm-uio",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = uio_fsl_elbc_gpcm_match,
|
||||
},
|
||||
.probe = uio_fsl_elbc_gpcm_probe,
|
||||
.remove = uio_fsl_elbc_gpcm_remove,
|
||||
};
|
||||
|
||||
static int __init uio_fsl_elbc_gpcm_init(void)
|
||||
{
|
||||
return platform_driver_register(&uio_fsl_elbc_gpcm_driver);
|
||||
}
|
||||
|
||||
static void __exit uio_fsl_elbc_gpcm_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&uio_fsl_elbc_gpcm_driver);
|
||||
}
|
||||
|
||||
module_init(uio_fsl_elbc_gpcm_init);
|
||||
module_exit(uio_fsl_elbc_gpcm_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
|
||||
MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller GPCM driver");
|
@ -91,7 +91,8 @@ static int probe(struct pci_dev *pdev,
|
||||
gdev->info.handler = irqhandler;
|
||||
gdev->pdev = pdev;
|
||||
|
||||
if (uio_register_device(&pdev->dev, &gdev->info))
|
||||
err = uio_register_device(&pdev->dev, &gdev->info);
|
||||
if (err)
|
||||
goto err_register;
|
||||
pci_set_drvdata(pdev, gdev);
|
||||
|
||||
|
@ -502,7 +502,7 @@ int vme_master_get(struct vme_resource *resource, int *enabled,
|
||||
image = list_entry(resource->entry, struct vme_master_resource, list);
|
||||
|
||||
if (bridge->master_get == NULL) {
|
||||
printk(KERN_WARNING "vme_master_set not supported\n");
|
||||
printk(KERN_WARNING "%s not supported\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -179,15 +179,6 @@ struct coresight_device {
|
||||
#define sink_ops(csdev) csdev->ops->sink_ops
|
||||
#define link_ops(csdev) csdev->ops->link_ops
|
||||
|
||||
#define CORESIGHT_DEBUGFS_ENTRY(__name, __entry_name, \
|
||||
__mode, __get, __set, __fmt) \
|
||||
DEFINE_SIMPLE_ATTRIBUTE(__name ## _ops, __get, __set, __fmt); \
|
||||
static const struct coresight_ops_entry __name ## _entry = { \
|
||||
.name = __entry_name, \
|
||||
.mode = __mode, \
|
||||
.ops = &__name ## _ops \
|
||||
}
|
||||
|
||||
/**
|
||||
* struct coresight_ops_sink - basic operations for a sink
|
||||
* Operations available for sinks
|
||||
@ -236,13 +227,8 @@ coresight_register(struct coresight_desc *desc);
|
||||
extern void coresight_unregister(struct coresight_device *csdev);
|
||||
extern int coresight_enable(struct coresight_device *csdev);
|
||||
extern void coresight_disable(struct coresight_device *csdev);
|
||||
extern int coresight_is_bit_set(u32 val, int position, int value);
|
||||
extern int coresight_timeout(void __iomem *addr, u32 offset,
|
||||
int position, int value);
|
||||
#ifdef CONFIG_OF
|
||||
extern struct coresight_platform_data *of_get_coresight_platform_data(
|
||||
struct device *dev, struct device_node *node);
|
||||
#endif
|
||||
#else
|
||||
static inline struct coresight_device *
|
||||
coresight_register(struct coresight_desc *desc) { return NULL; }
|
||||
@ -250,14 +236,16 @@ static inline void coresight_unregister(struct coresight_device *csdev) {}
|
||||
static inline int
|
||||
coresight_enable(struct coresight_device *csdev) { return -ENOSYS; }
|
||||
static inline void coresight_disable(struct coresight_device *csdev) {}
|
||||
static inline int coresight_is_bit_set(u32 val, int position, int value)
|
||||
{ return 0; }
|
||||
static inline int coresight_timeout(void __iomem *addr, u32 offset,
|
||||
int position, int value) { return 1; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
extern struct coresight_platform_data *of_get_coresight_platform_data(
|
||||
struct device *dev, struct device_node *node);
|
||||
#else
|
||||
static inline struct coresight_platform_data *of_get_coresight_platform_data(
|
||||
struct device *dev, struct device_node *node) { return NULL; }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -57,6 +57,18 @@ struct hv_multipage_buffer {
|
||||
u64 pfn_array[MAX_MULTIPAGE_BUFFER_COUNT];
|
||||
};
|
||||
|
||||
/*
|
||||
* Multiple-page buffer array; the pfn array is variable size:
|
||||
* The number of entries in the PFN array is determined by
|
||||
* "len" and "offset".
|
||||
*/
|
||||
struct hv_mpb_array {
|
||||
/* Length and Offset determines the # of pfns in the array */
|
||||
u32 len;
|
||||
u32 offset;
|
||||
u64 pfn_array[];
|
||||
};
|
||||
|
||||
/* 0x18 includes the proprietary packet header */
|
||||
#define MAX_PAGE_BUFFER_PACKET (0x18 + \
|
||||
(sizeof(struct hv_page_buffer) * \
|
||||
@ -722,7 +734,12 @@ struct vmbus_channel {
|
||||
*/
|
||||
void (*sc_creation_callback)(struct vmbus_channel *new_sc);
|
||||
|
||||
spinlock_t sc_lock;
|
||||
/*
|
||||
* The spinlock to protect the structure. It is being used to protect
|
||||
* test-and-set access to various attributes of the structure as well
|
||||
* as all sc_list operations.
|
||||
*/
|
||||
spinlock_t lock;
|
||||
/*
|
||||
* All Sub-channels of a primary channel are linked here.
|
||||
*/
|
||||
@ -814,6 +831,18 @@ struct vmbus_channel_packet_multipage_buffer {
|
||||
struct hv_multipage_buffer range;
|
||||
} __packed;
|
||||
|
||||
/* The format must be the same as struct vmdata_gpa_direct */
|
||||
struct vmbus_packet_mpb_array {
|
||||
u16 type;
|
||||
u16 dataoffset8;
|
||||
u16 length8;
|
||||
u16 flags;
|
||||
u64 transactionid;
|
||||
u32 reserved;
|
||||
u32 rangecount; /* Always 1 in this case */
|
||||
struct hv_mpb_array range;
|
||||
} __packed;
|
||||
|
||||
|
||||
extern int vmbus_open(struct vmbus_channel *channel,
|
||||
u32 send_ringbuffersize,
|
||||
@ -845,6 +874,13 @@ extern int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
|
||||
u32 bufferlen,
|
||||
u64 requestid);
|
||||
|
||||
extern int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
|
||||
struct vmbus_packet_mpb_array *mpb,
|
||||
u32 desc_size,
|
||||
void *buffer,
|
||||
u32 bufferlen,
|
||||
u64 requestid);
|
||||
|
||||
extern int vmbus_establish_gpadl(struct vmbus_channel *channel,
|
||||
void *kbuffer,
|
||||
u32 size,
|
||||
|
@ -25,8 +25,8 @@ int __mei_cl_driver_register(struct mei_cl_driver *driver,
|
||||
|
||||
void mei_cl_driver_unregister(struct mei_cl_driver *driver);
|
||||
|
||||
int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length);
|
||||
int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length);
|
||||
ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length);
|
||||
ssize_t mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length);
|
||||
|
||||
typedef void (*mei_cl_event_cb_t)(struct mei_cl_device *device,
|
||||
u32 events, void *context);
|
||||
|
@ -1281,6 +1281,25 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
* @alter contains the flag indicating whether changes are to be made.
|
||||
* Return 0 if permission is granted.
|
||||
*
|
||||
* @binder_set_context_mgr
|
||||
* Check whether @mgr is allowed to be the binder context manager.
|
||||
* @mgr contains the task_struct for the task being registered.
|
||||
* Return 0 if permission is granted.
|
||||
* @binder_transaction
|
||||
* Check whether @from is allowed to invoke a binder transaction call
|
||||
* to @to.
|
||||
* @from contains the task_struct for the sending task.
|
||||
* @to contains the task_struct for the receiving task.
|
||||
* @binder_transfer_binder
|
||||
* Check whether @from is allowed to transfer a binder reference to @to.
|
||||
* @from contains the task_struct for the sending task.
|
||||
* @to contains the task_struct for the receiving task.
|
||||
* @binder_transfer_file
|
||||
* Check whether @from is allowed to transfer @file to @to.
|
||||
* @from contains the task_struct for the sending task.
|
||||
* @file contains the struct file being transferred.
|
||||
* @to contains the task_struct for the receiving task.
|
||||
*
|
||||
* @ptrace_access_check:
|
||||
* Check permission before allowing the current process to trace the
|
||||
* @child process.
|
||||
@ -1441,6 +1460,14 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
struct security_operations {
|
||||
char name[SECURITY_NAME_MAX + 1];
|
||||
|
||||
int (*binder_set_context_mgr) (struct task_struct *mgr);
|
||||
int (*binder_transaction) (struct task_struct *from,
|
||||
struct task_struct *to);
|
||||
int (*binder_transfer_binder) (struct task_struct *from,
|
||||
struct task_struct *to);
|
||||
int (*binder_transfer_file) (struct task_struct *from,
|
||||
struct task_struct *to, struct file *file);
|
||||
|
||||
int (*ptrace_access_check) (struct task_struct *child, unsigned int mode);
|
||||
int (*ptrace_traceme) (struct task_struct *parent);
|
||||
int (*capget) (struct task_struct *target,
|
||||
@ -1739,6 +1766,13 @@ extern void __init security_fixup_ops(struct security_operations *ops);
|
||||
|
||||
|
||||
/* Security operations */
|
||||
int security_binder_set_context_mgr(struct task_struct *mgr);
|
||||
int security_binder_transaction(struct task_struct *from,
|
||||
struct task_struct *to);
|
||||
int security_binder_transfer_binder(struct task_struct *from,
|
||||
struct task_struct *to);
|
||||
int security_binder_transfer_file(struct task_struct *from,
|
||||
struct task_struct *to, struct file *file);
|
||||
int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
|
||||
int security_ptrace_traceme(struct task_struct *parent);
|
||||
int security_capget(struct task_struct *target,
|
||||
@ -1927,6 +1961,30 @@ static inline int security_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_binder_set_context_mgr(struct task_struct *mgr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_binder_transaction(struct task_struct *from,
|
||||
struct task_struct *to)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_binder_transfer_binder(struct task_struct *from,
|
||||
struct task_struct *to)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_binder_transfer_file(struct task_struct *from,
|
||||
struct task_struct *to,
|
||||
struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_ptrace_access_check(struct task_struct *child,
|
||||
unsigned int mode)
|
||||
{
|
||||
|
@ -86,6 +86,7 @@ struct st_proto_s {
|
||||
extern long st_register(struct st_proto_s *);
|
||||
extern long st_unregister(struct st_proto_s *);
|
||||
|
||||
extern struct ti_st_plat_data *dt_pdata;
|
||||
|
||||
/*
|
||||
* header information used by st_core.c
|
||||
@ -261,7 +262,7 @@ struct kim_data_s {
|
||||
struct completion kim_rcvd, ldisc_installed;
|
||||
char resp_buffer[30];
|
||||
const struct firmware *fw_entry;
|
||||
long nshutdown;
|
||||
unsigned nshutdown;
|
||||
unsigned long rx_state;
|
||||
unsigned long rx_count;
|
||||
struct sk_buff *rx_skb;
|
||||
@ -269,8 +270,8 @@ struct kim_data_s {
|
||||
struct chip_version version;
|
||||
unsigned char ldisc_install;
|
||||
unsigned char dev_name[UART_DEV_NAME_LEN + 1];
|
||||
unsigned char flow_cntrl;
|
||||
unsigned long baud_rate;
|
||||
unsigned flow_cntrl;
|
||||
unsigned baud_rate;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -436,10 +437,10 @@ struct gps_event_hdr {
|
||||
*
|
||||
*/
|
||||
struct ti_st_plat_data {
|
||||
long nshutdown_gpio;
|
||||
u32 nshutdown_gpio;
|
||||
unsigned char dev_name[UART_DEV_NAME_LEN]; /* uart name */
|
||||
unsigned char flow_cntrl; /* flow control flag */
|
||||
unsigned long baud_rate;
|
||||
u32 flow_cntrl; /* flow control flag */
|
||||
u32 baud_rate;
|
||||
int (*suspend)(struct platform_device *, pm_message_t);
|
||||
int (*resume)(struct platform_device *);
|
||||
int (*chip_enable) (struct kim_data_s *);
|
||||
|
@ -12,6 +12,29 @@
|
||||
|
||||
#include <linux/security.h>
|
||||
|
||||
static int cap_binder_set_context_mgr(struct task_struct *mgr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_binder_transaction(struct task_struct *from,
|
||||
struct task_struct *to)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_binder_transfer_binder(struct task_struct *from,
|
||||
struct task_struct *to)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_binder_transfer_file(struct task_struct *from,
|
||||
struct task_struct *to, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_syslog(int type)
|
||||
{
|
||||
return 0;
|
||||
@ -930,6 +953,10 @@ static void cap_audit_rule_free(void *lsmrule)
|
||||
|
||||
void __init security_fixup_ops(struct security_operations *ops)
|
||||
{
|
||||
set_to_cap_if_null(ops, binder_set_context_mgr);
|
||||
set_to_cap_if_null(ops, binder_transaction);
|
||||
set_to_cap_if_null(ops, binder_transfer_binder);
|
||||
set_to_cap_if_null(ops, binder_transfer_file);
|
||||
set_to_cap_if_null(ops, ptrace_access_check);
|
||||
set_to_cap_if_null(ops, ptrace_traceme);
|
||||
set_to_cap_if_null(ops, capget);
|
||||
|
@ -135,6 +135,29 @@ int __init register_security(struct security_operations *ops)
|
||||
|
||||
/* Security operations */
|
||||
|
||||
int security_binder_set_context_mgr(struct task_struct *mgr)
|
||||
{
|
||||
return security_ops->binder_set_context_mgr(mgr);
|
||||
}
|
||||
|
||||
int security_binder_transaction(struct task_struct *from,
|
||||
struct task_struct *to)
|
||||
{
|
||||
return security_ops->binder_transaction(from, to);
|
||||
}
|
||||
|
||||
int security_binder_transfer_binder(struct task_struct *from,
|
||||
struct task_struct *to)
|
||||
{
|
||||
return security_ops->binder_transfer_binder(from, to);
|
||||
}
|
||||
|
||||
int security_binder_transfer_file(struct task_struct *from,
|
||||
struct task_struct *to, struct file *file)
|
||||
{
|
||||
return security_ops->binder_transfer_file(from, to, file);
|
||||
}
|
||||
|
||||
int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_YAMA_STACKED
|
||||
|
@ -1920,6 +1920,74 @@ static inline u32 open_file_to_av(struct file *file)
|
||||
|
||||
/* Hook functions begin here. */
|
||||
|
||||
static int selinux_binder_set_context_mgr(struct task_struct *mgr)
|
||||
{
|
||||
u32 mysid = current_sid();
|
||||
u32 mgrsid = task_sid(mgr);
|
||||
|
||||
return avc_has_perm(mysid, mgrsid, SECCLASS_BINDER,
|
||||
BINDER__SET_CONTEXT_MGR, NULL);
|
||||
}
|
||||
|
||||
static int selinux_binder_transaction(struct task_struct *from,
|
||||
struct task_struct *to)
|
||||
{
|
||||
u32 mysid = current_sid();
|
||||
u32 fromsid = task_sid(from);
|
||||
u32 tosid = task_sid(to);
|
||||
int rc;
|
||||
|
||||
if (mysid != fromsid) {
|
||||
rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER,
|
||||
BINDER__IMPERSONATE, NULL);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__CALL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static int selinux_binder_transfer_binder(struct task_struct *from,
|
||||
struct task_struct *to)
|
||||
{
|
||||
u32 fromsid = task_sid(from);
|
||||
u32 tosid = task_sid(to);
|
||||
|
||||
return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static int selinux_binder_transfer_file(struct task_struct *from,
|
||||
struct task_struct *to,
|
||||
struct file *file)
|
||||
{
|
||||
u32 sid = task_sid(to);
|
||||
struct file_security_struct *fsec = file->f_security;
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
struct common_audit_data ad;
|
||||
int rc;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_PATH;
|
||||
ad.u.path = file->f_path;
|
||||
|
||||
if (sid != fsec->sid) {
|
||||
rc = avc_has_perm(sid, fsec->sid,
|
||||
SECCLASS_FD,
|
||||
FD__USE,
|
||||
&ad);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (unlikely(IS_PRIVATE(inode)))
|
||||
return 0;
|
||||
|
||||
return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file),
|
||||
&ad);
|
||||
}
|
||||
|
||||
static int selinux_ptrace_access_check(struct task_struct *child,
|
||||
unsigned int mode)
|
||||
{
|
||||
@ -5797,6 +5865,11 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
|
||||
static struct security_operations selinux_ops = {
|
||||
.name = "selinux",
|
||||
|
||||
.binder_set_context_mgr = selinux_binder_set_context_mgr,
|
||||
.binder_transaction = selinux_binder_transaction,
|
||||
.binder_transfer_binder = selinux_binder_transfer_binder,
|
||||
.binder_transfer_file = selinux_binder_transfer_file,
|
||||
|
||||
.ptrace_access_check = selinux_ptrace_access_check,
|
||||
.ptrace_traceme = selinux_ptrace_traceme,
|
||||
.capget = selinux_capget,
|
||||
|
@ -151,5 +151,7 @@ struct security_class_mapping secclass_map[] = {
|
||||
{ "kernel_service", { "use_as_override", "create_files_as", NULL } },
|
||||
{ "tun_socket",
|
||||
{ COMMON_SOCK_PERMS, "attach_queue", NULL } },
|
||||
{ "binder", { "impersonate", "call", "set_context_mgr", "transfer",
|
||||
NULL } },
|
||||
{ NULL }
|
||||
};
|
||||
|
@ -5,9 +5,9 @@ PTHREAD_LIBS = -lpthread
|
||||
WARNINGS = -Wall -Wextra
|
||||
CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS)
|
||||
|
||||
all: hv_kvp_daemon hv_vss_daemon
|
||||
all: hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon
|
||||
%: %.c
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
$(RM) hv_kvp_daemon hv_vss_daemon
|
||||
$(RM) hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon
|
||||
|
@ -43,15 +43,9 @@ static int hv_start_fcopy(struct hv_start_fcopy *smsg)
|
||||
int error = HV_E_FAIL;
|
||||
char *q, *p;
|
||||
|
||||
/*
|
||||
* If possile append a path seperator to the path.
|
||||
*/
|
||||
if (strlen((char *)smsg->path_name) < (W_MAX_PATH - 2))
|
||||
strcat((char *)smsg->path_name, "/");
|
||||
|
||||
p = (char *)smsg->path_name;
|
||||
snprintf(target_fname, sizeof(target_fname), "%s/%s",
|
||||
(char *)smsg->path_name, smsg->file_name);
|
||||
(char *)smsg->path_name, (char *)smsg->file_name);
|
||||
|
||||
syslog(LOG_INFO, "Target file name: %s", target_fname);
|
||||
/*
|
||||
@ -137,7 +131,7 @@ void print_usage(char *argv[])
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int fd, fcopy_fd, len;
|
||||
int fcopy_fd, len;
|
||||
int error;
|
||||
int daemonize = 1, long_index = 0, opt;
|
||||
int version = FCOPY_CURRENT_VERSION;
|
||||
|
@ -147,7 +147,6 @@ static void kvp_release_lock(int pool)
|
||||
static void kvp_update_file(int pool)
|
||||
{
|
||||
FILE *filep;
|
||||
size_t bytes_written;
|
||||
|
||||
/*
|
||||
* We are going to write our in-memory registry out to
|
||||
@ -163,8 +162,7 @@ static void kvp_update_file(int pool)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
bytes_written = fwrite(kvp_file_info[pool].records,
|
||||
sizeof(struct kvp_record),
|
||||
fwrite(kvp_file_info[pool].records, sizeof(struct kvp_record),
|
||||
kvp_file_info[pool].num_records, filep);
|
||||
|
||||
if (ferror(filep) || fclose(filep)) {
|
||||
@ -310,7 +308,7 @@ static int kvp_file_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvp_key_delete(int pool, const char *key, int key_size)
|
||||
static int kvp_key_delete(int pool, const __u8 *key, int key_size)
|
||||
{
|
||||
int i;
|
||||
int j, k;
|
||||
@ -353,8 +351,8 @@ static int kvp_key_delete(int pool, const char *key, int key_size)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int kvp_key_add_or_modify(int pool, const char *key, int key_size, const char *value,
|
||||
int value_size)
|
||||
static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size,
|
||||
const __u8 *value, int value_size)
|
||||
{
|
||||
int i;
|
||||
int num_records;
|
||||
@ -407,7 +405,7 @@ static int kvp_key_add_or_modify(int pool, const char *key, int key_size, const
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvp_get_value(int pool, const char *key, int key_size, char *value,
|
||||
static int kvp_get_value(int pool, const __u8 *key, int key_size, __u8 *value,
|
||||
int value_size)
|
||||
{
|
||||
int i;
|
||||
@ -439,8 +437,8 @@ static int kvp_get_value(int pool, const char *key, int key_size, char *value,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int kvp_pool_enumerate(int pool, int index, char *key, int key_size,
|
||||
char *value, int value_size)
|
||||
static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
|
||||
__u8 *value, int value_size)
|
||||
{
|
||||
struct kvp_record *record;
|
||||
|
||||
@ -661,7 +659,7 @@ static char *kvp_if_name_to_mac(char *if_name)
|
||||
char *p, *x;
|
||||
char buf[256];
|
||||
char addr_file[256];
|
||||
int i;
|
||||
unsigned int i;
|
||||
char *mac_addr = NULL;
|
||||
|
||||
snprintf(addr_file, sizeof(addr_file), "%s%s%s", "/sys/class/net/",
|
||||
@ -700,7 +698,7 @@ static char *kvp_mac_to_if_name(char *mac)
|
||||
char buf[256];
|
||||
char *kvp_net_dir = "/sys/class/net/";
|
||||
char dev_id[256];
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
dir = opendir(kvp_net_dir);
|
||||
if (dir == NULL)
|
||||
@ -750,7 +748,7 @@ static char *kvp_mac_to_if_name(char *mac)
|
||||
|
||||
|
||||
static void kvp_process_ipconfig_file(char *cmd,
|
||||
char *config_buf, int len,
|
||||
char *config_buf, unsigned int len,
|
||||
int element_size, int offset)
|
||||
{
|
||||
char buf[256];
|
||||
@ -768,7 +766,7 @@ static void kvp_process_ipconfig_file(char *cmd,
|
||||
if (offset == 0)
|
||||
memset(config_buf, 0, len);
|
||||
while ((p = fgets(buf, sizeof(buf), file)) != NULL) {
|
||||
if ((len - strlen(config_buf)) < (element_size + 1))
|
||||
if (len < strlen(config_buf) + element_size + 1)
|
||||
break;
|
||||
|
||||
x = strchr(p, '\n');
|
||||
@ -916,7 +914,7 @@ static int kvp_process_ip_address(void *addrp,
|
||||
|
||||
static int
|
||||
kvp_get_ip_info(int family, char *if_name, int op,
|
||||
void *out_buffer, int length)
|
||||
void *out_buffer, unsigned int length)
|
||||
{
|
||||
struct ifaddrs *ifap;
|
||||
struct ifaddrs *curp;
|
||||
@ -1019,8 +1017,7 @@ kvp_get_ip_info(int family, char *if_name, int op,
|
||||
weight += hweight32(&w[i]);
|
||||
|
||||
sprintf(cidr_mask, "/%d", weight);
|
||||
if ((length - sn_offset) <
|
||||
(strlen(cidr_mask) + 1))
|
||||
if (length < sn_offset + strlen(cidr_mask) + 1)
|
||||
goto gather_ipaddr;
|
||||
|
||||
if (sn_offset == 0)
|
||||
@ -1308,16 +1305,17 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
|
||||
if (error)
|
||||
goto setval_error;
|
||||
|
||||
/*
|
||||
* The dhcp_enabled flag is only for IPv4. In the case the host only
|
||||
* injects an IPv6 address, the flag is true, but we still need to
|
||||
* proceed to parse and pass the IPv6 information to the
|
||||
* disto-specific script hv_set_ifconfig.
|
||||
*/
|
||||
if (new_val->dhcp_enabled) {
|
||||
error = kvp_write_file(file, "BOOTPROTO", "", "dhcp");
|
||||
if (error)
|
||||
goto setval_error;
|
||||
|
||||
/*
|
||||
* We are done!.
|
||||
*/
|
||||
goto setval_done;
|
||||
|
||||
} else {
|
||||
error = kvp_write_file(file, "BOOTPROTO", "", "none");
|
||||
if (error)
|
||||
@ -1345,7 +1343,6 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
|
||||
if (error)
|
||||
goto setval_error;
|
||||
|
||||
setval_done:
|
||||
fclose(file);
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user