summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/android/binder.c26
-rw-r--r--drivers/char/Kconfig9
-rw-r--r--drivers/char/i8k.c316
-rw-r--r--drivers/char/mem.c22
-rw-r--r--drivers/char/virtio_console.c6
-rw-r--r--drivers/coresight/coresight-etb10.c14
-rw-r--r--drivers/coresight/coresight-etm3x.c24
-rw-r--r--drivers/coresight/coresight-funnel.c12
-rw-r--r--drivers/coresight/coresight-priv.h2
-rw-r--r--drivers/coresight/coresight-replicator.c2
-rw-r--r--drivers/coresight/coresight-tmc.c12
-rw-r--r--drivers/coresight/coresight-tpiu.c12
-rw-r--r--drivers/coresight/coresight.c11
-rw-r--r--drivers/coresight/of_coresight.c22
-rw-r--r--drivers/extcon/extcon-adc-jack.c1
-rw-r--r--drivers/extcon/extcon-class.c1
-rw-r--r--drivers/extcon/extcon-max77693.c2
-rw-r--r--drivers/hv/channel.c54
-rw-r--r--drivers/hv/channel_mgmt.c54
-rw-r--r--drivers/hv/connection.c6
-rw-r--r--drivers/hv/hv.c78
-rw-r--r--drivers/hv/hv_balloon.c88
-rw-r--r--drivers/hv/hv_fcopy.c27
-rw-r--r--drivers/hv/hyperv_vmbus.h21
-rw-r--r--drivers/hv/vmbus_drv.c37
-rw-r--r--drivers/mcb/mcb-pci.c18
-rw-r--r--drivers/misc/ad525x_dpot-spi.c4
-rw-r--r--drivers/misc/ad525x_dpot.c7
-rw-r--r--drivers/misc/genwqe/card_base.h1
-rw-r--r--drivers/misc/genwqe/card_sysfs.c1
-rw-r--r--drivers/misc/ioc4.c31
-rw-r--r--drivers/misc/mei/amthif.c14
-rw-r--r--drivers/misc/mei/bus.c69
-rw-r--r--drivers/misc/mei/client.c156
-rw-r--r--drivers/misc/mei/client.h17
-rw-r--r--drivers/misc/mei/debugfs.c32
-rw-r--r--drivers/misc/mei/hbm.c34
-rw-r--r--drivers/misc/mei/hw-me.c5
-rw-r--r--drivers/misc/mei/main.c22
-rw-r--r--drivers/misc/mei/mei_dev.h8
-rw-r--r--drivers/misc/mei/nfc.c2
-rw-r--r--drivers/misc/mei/wd.c1
-rw-r--r--drivers/misc/ti-st/st_core.c24
-rw-r--r--drivers/misc/ti-st/st_kim.c144
-rw-r--r--drivers/misc/ti-st/st_ll.c17
-rw-r--r--drivers/misc/vmw_vmci/vmci_driver.c2
-rw-r--r--drivers/misc/vmw_vmci/vmci_host.c13
-rw-r--r--drivers/pcmcia/Kconfig12
-rw-r--r--drivers/pcmcia/Makefile1
-rw-r--r--drivers/pcmcia/cistpl.c31
-rw-r--r--drivers/pcmcia/cs_internal.h6
-rw-r--r--drivers/pcmcia/ds.c3
-rw-r--r--drivers/pcmcia/rsrc_mgr.c5
-rw-r--r--drivers/pcmcia/rsrc_pci.c173
-rw-r--r--drivers/pnp/pnpbios/core.c3
-rw-r--r--drivers/uio/Kconfig20
-rw-r--r--drivers/uio/Makefile1
-rw-r--r--drivers/uio/uio_fsl_elbc_gpcm.c499
-rw-r--r--drivers/uio/uio_pci_generic.c3
-rw-r--r--drivers/vme/vme.c2
60 files changed, 1799 insertions, 441 deletions
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 8c43521d3f11..33b09b6568a4 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -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",
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index efefd12a0f7b..a4af8221751e 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -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
diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c
index e34a019eb930..24cc4ed9a780 100644
--- a/drivers/char/i8k.c
+++ b/drivers/char/i8k.c
@@ -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);
@@ -276,6 +280,28 @@ static int i8k_get_fan_speed(int fan)
}
/*
+ * 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(&regs) ? : 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(&regs) ? : (regs.eax & 0xffff) * i8k_fan_mult;
+}
+
+/*
* Set the fan speed (off, low, high). Returns the new fan status.
*/
static int i8k_set_fan(int fan, int speed)
@@ -288,42 +314,52 @@ static int i8k_set_fan(int fan, int speed)
return i8k_smm(&regs) ? : 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(&regs) ? : regs.eax & 0xff;
+}
+
/*
* Read the cpu temperature.
*/
-static int i8k_get_temp(int sensor)
+static int _i8k_get_temp(int sensor)
{
- struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP, };
- int rc;
- int temp;
+ struct smm_regs regs = {
+ .eax = I8K_SMM_GET_TEMP,
+ .ebx = sensor & 0xff,
+ };
-#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(&regs);
- if (rc < 0)
- return rc;
+ return i8k_smm(&regs) ? : regs.eax & 0xff;
+}
- temp = regs.eax & 0xff;
+static int i8k_get_temp(int sensor)
+{
+ int temp = _i8k_get_temp(sensor);
-#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))
+ if (index >= 2 && index <= 3 &&
+ !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
return 0;
- if (index == 2 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
+ if (index >= 4 && index <= 5 &&
+ !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
return 0;
- if (index == 3 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
+ if (index >= 6 && index <= 7 &&
+ !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
return 0;
- if (index >= 4 && index <= 5 &&
+ if (index >= 8 && index <= 10 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1))
return 0;
- if (index >= 6 && index <= 7 &&
+ 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,
},
@@ -715,22 +819,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 = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
@@ -790,12 +878,20 @@ 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;
}
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 9a6b63783a94..297110c12635 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -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
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index de03df9dd7c9..26afb56a8073 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -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();
diff --git a/drivers/coresight/coresight-etb10.c b/drivers/coresight/coresight-etb10.c
index c922d4aded8a..c9acd406f0d0 100644
--- a/drivers/coresight/coresight-etb10.c
+++ b/drivers/coresight/coresight-etb10.c
@@ -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");
diff --git a/drivers/coresight/coresight-etm3x.c b/drivers/coresight/coresight-etm3x.c
index d9e3ed6aa857..c965f5724abd 100644
--- a/drivers/coresight/coresight-etm3x.c
+++ b/drivers/coresight/coresight-etm3x.c
@@ -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 */
diff --git a/drivers/coresight/coresight-funnel.c b/drivers/coresight/coresight-funnel.c
index 2108edffe1f4..3db36f70b666 100644
--- a/drivers/coresight/coresight-funnel.c
+++ b/drivers/coresight/coresight-funnel.c
@@ -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");
diff --git a/drivers/coresight/coresight-priv.h b/drivers/coresight/coresight-priv.h
index 7b3372fca4f6..62fcd98cc7cf 100644
--- a/drivers/coresight/coresight-priv.h
+++ b/drivers/coresight/coresight-priv.h
@@ -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
diff --git a/drivers/coresight/coresight-replicator.c b/drivers/coresight/coresight-replicator.c
index a2dfcf903551..cdf05537d574 100644
--- a/drivers/coresight/coresight-replicator.c
+++ b/drivers/coresight/coresight-replicator.c
@@ -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;
diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c
index ce2c293f1707..3ff232f9ddf7 100644
--- a/drivers/coresight/coresight-tmc.c
+++ b/drivers/coresight/coresight-tmc.c
@@ -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");
diff --git a/drivers/coresight/coresight-tpiu.c b/drivers/coresight/coresight-tpiu.c
index ae101082791a..3b33af2416bb 100644
--- a/drivers/coresight/coresight-tpiu.c
+++ b/drivers/coresight/coresight-tpiu.c
@@ -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");
diff --git a/drivers/coresight/coresight.c b/drivers/coresight/coresight.c
index 6e0181f84425..c5def9382357 100644
--- a/drivers/coresight/coresight.c
+++ b/drivers/coresight/coresight.c
@@ -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;
+ }
}
}
diff --git a/drivers/coresight/of_coresight.c b/drivers/coresight/of_coresight.c
index 5030c0734508..c3efa418a86d 100644
--- a/drivers/coresight/of_coresight.c
+++ b/drivers/coresight/of_coresight.c
@@ -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;
}
diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c
index 5d7ab577fba9..2bb82e55065a 100644
--- a/drivers/extcon/extcon-adc-jack.c
+++ b/drivers/extcon/extcon-adc-jack.c
@@ -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;
}
diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c
index 043dcd9946c9..8319f25b7145 100644
--- a/drivers/extcon/extcon-class.c
+++ b/drivers/extcon/extcon-class.c
@@ -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
diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c
index 740a14d35072..af165fd0c6f5 100644
--- a/drivers/extcon/extcon-max77693.c
+++ b/drivers/extcon/extcon-max77693.c
@@ -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,
};
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
index 433f72a1c006..2978f5ee8d2a 100644
--- a/drivers/hv/channel.c
+++ b/drivers/hv/channel.c
@@ -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)
@@ -686,6 +686,50 @@ 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.
*/
int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
struct hv_multipage_buffer *multi_pagebuffer,
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 2c59f030546b..3736f71bdec5 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -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)
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index e206619b946e..a63a795300b9 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -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
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index 3e4235c7a47f..50e51a51ff8b 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -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;
}
diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
index b958ded8ac7e..ff169386b2c7 100644
--- a/drivers/hv/hv_balloon.c
+++ b/drivers/hv/hv_balloon.c
@@ -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);
diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c
index 23b2ce294c4c..cd453e4b2a07 100644
--- a/drivers/hv/hv_fcopy.c
+++ b/drivers/hv/hv_fcopy.c
@@ -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;
}
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index c386d8dc7223..44b1c9424712 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -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;
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index bb3725b672cf..f518b8d7a5b5 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -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);
+ }
}
/*
diff --git a/drivers/mcb/mcb-pci.c b/drivers/mcb/mcb-pci.c
index 5e1bd5db02c8..0af7361e377f 100644
--- a/drivers/mcb/mcb-pci.c
+++ b/drivers/mcb/mcb-pci.c
@@ -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;
}
diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c
index 9da04ede04f3..f4c82eafa8e5 100644
--- a/drivers/misc/ad525x_dpot-spi.c
+++ b/drivers/misc/ad525x_dpot-spi.c
@@ -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;
diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c
index a43053daad0e..15e88078ba1e 100644
--- a/drivers/misc/ad525x_dpot.c
+++ b/drivers/misc/ad525x_dpot.c
@@ -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);
diff --git a/drivers/misc/genwqe/card_base.h b/drivers/misc/genwqe/card_base.h
index c64d7cad1085..e7353449874b 100644
--- a/drivers/misc/genwqe/card_base.h
+++ b/drivers/misc/genwqe/card_base.h
@@ -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>
diff --git a/drivers/misc/genwqe/card_sysfs.c b/drivers/misc/genwqe/card_sysfs.c
index 2c33fbca9225..6ab31eff0536 100644
--- a/drivers/misc/genwqe/card_sysfs.c
+++ b/drivers/misc/genwqe/card_sysfs.c
@@ -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>
diff --git a/drivers/misc/ioc4.c b/drivers/misc/ioc4.c
index 3336ddca45ac..8758d033db23 100644
--- a/drivers/misc/ioc4.c
+++ b/drivers/misc/ioc4.c
@@ -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:
diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c
index 79f53941779d..c4cb9a984a5f 100644
--- a/drivers/misc/mei/amthif.c
+++ b/drivers/misc/mei/amthif.c
@@ -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;
}
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index b3a72bca5242..be767f4db26a 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -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;
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 1382d551d7ed..dfbddfe1c7a0 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -27,7 +27,63 @@
#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;
diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h
index d9d0c1525259..cfcde8e97fc4 100644
--- a/drivers/misc/mei/client.h
+++ b/drivers/misc/mei/client.h
@@ -24,15 +24,22 @@
#include "mei_dev.h"
+/*
+ * 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 *cuuid);
+ 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
diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c
index b60b4263cf0f..b125380ee871 100644
--- a/drivers/misc/mei/debugfs.c
+++ b/drivers/misc/mei/debugfs.c
@@ -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) {
-
- 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);
+ list_for_each_entry_safe(me_cl, n, &dev->me_clients, list) {
+
+ 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);
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index 239d7f5d6a92..c8412d41e4f1 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -105,21 +105,6 @@ void mei_hbm_idle(struct mei_device *dev)
}
/**
- * 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
*
* @dev: the device structure
@@ -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;
}
/**
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 06ff0a2ec960..f8fd503dfbd6 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -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 {
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index ae56ba6ca0e3..3c019c0e60eb 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -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;
}
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index 3dad74a8d496..6c6ce9381535 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -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);
diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c
index 60ca9240368e..bb61a119b8bb 100644
--- a/drivers/misc/mei/nfc.c
+++ b/drivers/misc/mei/nfc.c
@@ -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)
diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c
index b1d892cea94d..475f1dea45bf 100644
--- a/drivers/misc/mei/wd.c
+++ b/drivers/misc/mei/wd.c
@@ -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);
diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c
index 54be83d3efdd..c8c6a363069c 100644
--- a/drivers/misc/ti-st/st_core.c
+++ b/drivers/misc/ti-st/st_core.c
@@ -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);
}
}
-
-
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
index e4b7ee4f57b8..18e7a03985d4 100644
--- a/drivers/misc/ti-st/st_kim.c
+++ b/drivers/misc/ti-st/st_kim.c
@@ -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),
},
};
diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c
index 93b4d67cc4a3..518e1b7f2f95 100644
--- a/drivers/misc/ti-st/st_ll.c
+++ b/drivers/misc/ti-st/st_ll.c
@@ -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);
}
diff --git a/drivers/misc/vmw_vmci/vmci_driver.c b/drivers/misc/vmw_vmci/vmci_driver.c
index 3dee7ae123e7..032d35cf93ca 100644
--- a/drivers/misc/vmw_vmci/vmci_driver.c
+++ b/drivers/misc/vmw_vmci/vmci_driver.c
@@ -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");
diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c
index 1723a6e4f2e8..66fc9921fc85 100644
--- a/drivers/misc/vmw_vmci/vmci_host.c
+++ b/drivers/misc/vmw_vmci/vmci_host.c
@@ -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;
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 910e90bf16c6..8843a678f200 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -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
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index 27e94b30cf96..f1a7ca04d89e 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -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
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
index 884a984216fe..64d0515b76bd 100644
--- a/drivers/pcmcia/cistpl.c
+++ b/drivers/pcmcia/cistpl.c
@@ -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)
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
index 7f1953f78b12..e86cd6b31773 100644
--- a/drivers/pcmcia/cs_internal.h
+++ b/drivers/pcmcia/cs_internal.h
@@ -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":
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index 757119b87146..d3baf0bfca9f 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -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;
diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c
index aa628ed0e9f4..df2cb70aef5b 100644
--- a/drivers/pcmcia/rsrc_mgr.c
+++ b/drivers/pcmcia/rsrc_mgr.c
@@ -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);
diff --git a/drivers/pcmcia/rsrc_pci.c b/drivers/pcmcia/rsrc_pci.c
new file mode 100644
index 000000000000..1f67b3ba70fb
--- /dev/null
+++ b/drivers/pcmcia/rsrc_pci.c
@@ -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);
diff --git a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c
index 074569e77d22..facd43b8516c 100644
--- a/drivers/pnp/pnpbios/core.c
+++ b/drivers/pnp/pnpbios/core.c
@@ -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) {
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
index 5a90914d856d..8a15c323c030 100644
--- a/drivers/uio/Kconfig
+++ b/drivers/uio/Kconfig
@@ -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
diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
index d3218bde3aeb..8560dad52d0f 100644
--- a/drivers/uio/Makefile
+++ b/drivers/uio/Makefile
@@ -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
diff --git a/drivers/uio/uio_fsl_elbc_gpcm.c b/drivers/uio/uio_fsl_elbc_gpcm.c
new file mode 100644
index 000000000000..b6cac91c2ced
--- /dev/null
+++ b/drivers/uio/uio_fsl_elbc_gpcm.c
@@ -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, &reg_br_new, &reg_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");
diff --git a/drivers/uio/uio_pci_generic.c b/drivers/uio/uio_pci_generic.c
index 077ae12269ce..d0b508b68f3c 100644
--- a/drivers/uio/uio_pci_generic.c
+++ b/drivers/uio/uio_pci_generic.c
@@ -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);
diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c
index 7516030037a1..d95fb848dd03 100644
--- a/drivers/vme/vme.c
+++ b/drivers/vme/vme.c
@@ -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;
}