diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-15 10:48:44 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-15 10:48:44 -0800 |
commit | 4ba63072b998cc31515cc6305c25f3b808b50c01 (patch) | |
tree | 779863511765c70bfd232f676b885f940ba88722 /drivers | |
parent | e29876723f7cb7728f0d6a674d23f92673e9f112 (diff) | |
parent | 5fb31cd839c21130c0b2524ceb9244e98dfe10e3 (diff) | |
download | linux-4ba63072b998cc31515cc6305c25f3b808b50c01.tar.bz2 |
Merge tag 'char-misc-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char / misc patches from Greg KH:
"Here's the big char/misc driver update for 3.20-rc1.
Lots of little things in here, all described in the changelog.
Nothing major or unusual, except maybe the binder selinux stuff, which
was all acked by the proper selinux people and they thought it best to
come through this tree.
All of this has been in linux-next with no reported issues for a while"
* tag 'char-misc-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (90 commits)
coresight: fix function etm_writel_cp14() parameter order
coresight-etm: remove check for unknown Kconfig macro
coresight: fixing CPU hwid lookup in device tree
coresight: remove the unnecessary function coresight_is_bit_set()
coresight: fix the debug AMBA bus name
coresight: remove the extra spaces
coresight: fix the link between orphan connection and newly added device
coresight: remove the unnecessary replicator property
coresight: fix the replicator subtype value
pdfdocs: Fix 'make pdfdocs' failure for 'uio-howto.tmpl'
mcb: Fix error path of mcb_pci_probe
virtio/console: verify device has config space
ti-st: clean up data types (fix harmless memory corruption)
mei: me: release hw from reset only during the reset flow
mei: mask interrupt set bit on clean reset bit
extcon: max77693: Constify struct regmap_config
extcon: adc-jack: Release IIO channel on driver remove
extcon: Remove duplicated include from extcon-class.c
Drivers: hv: vmbus: hv_process_timer_expiration() can be static
Drivers: hv: vmbus: serialize Offer and Rescind offer
...
Diffstat (limited to 'drivers')
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(®s) ? : regs.eax & 0xff; +} + +/* + * Read the fan nominal rpm for specific fan speed. + */ +static int i8k_get_fan_nominal_speed(int fan, int speed) +{ + struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, }; + + regs.ebx = (fan & 0xff) | (speed << 8); + return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult; +} + +/* * Set the fan speed (off, low, high). Returns the new fan status. */ 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(®s) ? : i8k_get_fan_status(fan); } +static int i8k_get_temp_type(int sensor) +{ + struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP_TYPE, }; + + regs.ebx = sensor & 0xff; + return i8k_smm(®s) ? : regs.eax & 0xff; +} + /* * Read the cpu temperature. */ -static int i8k_get_temp(int sensor) +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(®s); - if (rc < 0) - return rc; + return i8k_smm(®s) ? : 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, ®_br_new, ®_or_new, + &irq, &uio_name); + if (ret) + goto out_err0; + + /* allocate UIO structure */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + goto out_err0; + } + + /* get current BR/OR values */ + reg_br_cur = in_be32(&priv->lbc->bank[priv->bank].br); + reg_or_cur = in_be32(&priv->lbc->bank[priv->bank].or); + + /* if bank already configured, make sure it matches */ + if ((reg_br_cur & BR_V)) { + if ((reg_br_cur & BR_MSEL) != BR_MS_GPCM || + (reg_br_cur & reg_or_cur & BR_BA) + != fsl_lbc_addr(res.start)) { + dev_err(priv->dev, + "bank in use by another peripheral\n"); + ret = -ENODEV; + goto out_err1; + } + + /* warn if behavior settings changing */ + if ((reg_br_cur & ~(BR_BA | BR_V)) != + (reg_br_new & ~(BR_BA | BR_V))) { + dev_warn(priv->dev, + "modifying BR settings: 0x%08x -> 0x%08x", + reg_br_cur, reg_br_new); + } + if ((reg_or_cur & ~OR_GPCM_AM) != (reg_or_new & ~OR_GPCM_AM)) { + dev_warn(priv->dev, + "modifying OR settings: 0x%08x -> 0x%08x", + reg_or_cur, reg_or_new); + } + } + + /* configure the bank (force base address and GPCM) */ + reg_br_new &= ~(BR_BA | BR_MSEL); + reg_br_new |= fsl_lbc_addr(res.start) | BR_MS_GPCM | BR_V; + out_be32(&priv->lbc->bank[priv->bank].or, reg_or_new); + out_be32(&priv->lbc->bank[priv->bank].br, reg_br_new); + + /* map the memory resource */ + info->mem[0].internal_addr = ioremap(res.start, resource_size(&res)); + if (!info->mem[0].internal_addr) { + dev_err(priv->dev, "failed to map chip region\n"); + ret = -ENODEV; + goto out_err1; + } + + /* set all UIO data */ + if (node->name) + info->mem[0].name = kstrdup(node->name, GFP_KERNEL); + info->mem[0].addr = res.start; + info->mem[0].size = resource_size(&res); + info->mem[0].memtype = UIO_MEM_PHYS; + info->priv = priv; + info->name = uio_name; + info->version = "0.0.1"; + if (irq != NO_IRQ) { + if (priv->irq_handler) { + info->irq = irq; + info->irq_flags = IRQF_SHARED; + info->handler = priv->irq_handler; + } else { + irq = NO_IRQ; + dev_warn(priv->dev, "ignoring irq, no handler\n"); + } + } + + if (priv->init) + priv->init(info); + + /* register UIO device */ + if (uio_register_device(priv->dev, info) != 0) { + dev_err(priv->dev, "UIO registration failed\n"); + ret = -ENODEV; + goto out_err2; + } + + /* store private data */ + platform_set_drvdata(pdev, info); + + /* create sysfs files */ + ret = device_create_file(priv->dev, &dev_attr_reg_br); + if (ret) + goto out_err3; + ret = device_create_file(priv->dev, &dev_attr_reg_or); + if (ret) + goto out_err4; + + dev_info(priv->dev, + "eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n", + priv->name, (unsigned long long)res.start, priv->bank, + irq != NO_IRQ ? irq : -1); + + return 0; +out_err4: + device_remove_file(priv->dev, &dev_attr_reg_br); +out_err3: + platform_set_drvdata(pdev, NULL); + uio_unregister_device(info); +out_err2: + if (priv->shutdown) + priv->shutdown(info, true); + iounmap(info->mem[0].internal_addr); +out_err1: + kfree(info->mem[0].name); + kfree(info); +out_err0: + kfree(uio_name); + kfree(priv); + return ret; +} + +static int uio_fsl_elbc_gpcm_remove(struct platform_device *pdev) +{ + struct uio_info *info = platform_get_drvdata(pdev); + struct fsl_elbc_gpcm *priv = info->priv; + + device_remove_file(priv->dev, &dev_attr_reg_or); + device_remove_file(priv->dev, &dev_attr_reg_br); + platform_set_drvdata(pdev, NULL); + uio_unregister_device(info); + if (priv->shutdown) + priv->shutdown(info, false); + iounmap(info->mem[0].internal_addr); + kfree(info->mem[0].name); + kfree(info->name); + kfree(info); + kfree(priv); + + return 0; + +} + +static const struct of_device_id uio_fsl_elbc_gpcm_match[] = { + { .compatible = "fsl,elbc-gpcm-uio", }, + {} +}; + +static struct platform_driver uio_fsl_elbc_gpcm_driver = { + .driver = { + .name = "fsl,elbc-gpcm-uio", + .owner = THIS_MODULE, + .of_match_table = uio_fsl_elbc_gpcm_match, + }, + .probe = uio_fsl_elbc_gpcm_probe, + .remove = uio_fsl_elbc_gpcm_remove, +}; + +static int __init uio_fsl_elbc_gpcm_init(void) +{ + return platform_driver_register(&uio_fsl_elbc_gpcm_driver); +} + +static void __exit uio_fsl_elbc_gpcm_exit(void) +{ + platform_driver_unregister(&uio_fsl_elbc_gpcm_driver); +} + +module_init(uio_fsl_elbc_gpcm_init); +module_exit(uio_fsl_elbc_gpcm_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>"); +MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller GPCM driver"); 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; } |