From 4f0be26a7939d8cae5d010b9de33550a366ceef4 Mon Sep 17 00:00:00 2001 From: Jaewon Kim Date: Thu, 29 Jan 2015 17:45:23 +0900 Subject: [PATCH 001/133] extcon: max77693: Fix cable name of MHL-TA This patch fixes extcon cable name of MHL-TA instead of MHL_TA to unify cable name style. Signed-off-by: Jaewon Kim Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-max77693.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index af165fd0c6f5..9aca3b7719b9 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -228,7 +228,7 @@ static const char *max77693_extcon_cable[] = { [EXTCON_CABLE_SLOW_CHARGER] = "Slow-charger", [EXTCON_CABLE_CHARGE_DOWNSTREAM] = "Charge-downstream", [EXTCON_CABLE_MHL] = "MHL", - [EXTCON_CABLE_MHL_TA] = "MHL_TA", + [EXTCON_CABLE_MHL_TA] = "MHL-TA", [EXTCON_CABLE_JIG_USB_ON] = "JIG-USB-ON", [EXTCON_CABLE_JIG_USB_OFF] = "JIG-USB-OFF", [EXTCON_CABLE_JIG_UART_OFF] = "JIG-UART-OFF", @@ -823,19 +823,19 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) case MAX77693_MUIC_GND_MHL: case MAX77693_MUIC_GND_MHL_VB: /* - * MHL cable with MHL_TA(USB/TA) cable + * MHL cable with MHL-TA(USB/TA) cable * - MHL cable include two port(HDMI line and separate * micro-usb port. When the target connect MHL cable, - * extcon driver check whether MHL_TA(USB/TA) cable is - * connected. If MHL_TA cable is connected, extcon + * extcon driver check whether MHL-TA(USB/TA) cable is + * connected. If MHL-TA cable is connected, extcon * driver notify state to notifiee for charging battery. * - * Features of 'MHL_TA(USB/TA) with MHL cable' + * Features of 'MHL-TA(USB/TA) with MHL cable' * - Support MHL * - Support charging through micro-usb port without * data connection */ - extcon_set_cable_state(info->edev, "MHL_TA", attached); + extcon_set_cable_state(info->edev, "MHL-TA", attached); if (!cable_attached) extcon_set_cable_state(info->edev, "MHL", cable_attached); From 4c883abee08b13d7fac5e672159575bb7a3365a6 Mon Sep 17 00:00:00 2001 From: Jaewon Kim Date: Thu, 29 Jan 2015 17:45:24 +0900 Subject: [PATCH 002/133] extcon: max77693: Use HOST term to express USB-HOST cable instead of OTG term This patch unifies the term called 'USB_OTG' and 'USB_HOST' into USB_HOST. OTG(On-The-Go) function supports USB host and this driver sents 'USB-Host event. So, unifies term to USB_HOST. Signed-off-by: Jaewon Kim [cw00.choi: Fix patch title to indicate the correct meaning of patch] Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-max77693.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 9aca3b7719b9..dfcc5cba25f0 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -190,8 +190,8 @@ enum max77693_muic_acc_type { /* The below accessories have same ADC value so ADCLow and ADC1K bit is used to separate specific accessory */ /* ADC|VBVolot|ADCLow|ADC1K| */ - MAX77693_MUIC_GND_USB_OTG = 0x100, /* 0x0| 0| 0| 0| */ - MAX77693_MUIC_GND_USB_OTG_VB = 0x104, /* 0x0| 1| 0| 0| */ + MAX77693_MUIC_GND_USB_HOST = 0x100, /* 0x0| 0| 0| 0| */ + MAX77693_MUIC_GND_USB_HOST_VB = 0x104, /* 0x0| 1| 0| 0| */ MAX77693_MUIC_GND_AV_CABLE_LOAD = 0x102,/* 0x0| 0| 1| 0| */ MAX77693_MUIC_GND_MHL = 0x103, /* 0x0| 0| 1| 1| */ MAX77693_MUIC_GND_MHL_VB = 0x107, /* 0x0| 1| 1| 1| */ @@ -403,8 +403,8 @@ static int max77693_muic_get_cable_type(struct max77693_muic_info *info, /** * [0x1|VBVolt|ADCLow|ADC1K] - * [0x1| 0| 0| 0] USB_OTG - * [0x1| 1| 0| 0] USB_OTG_VB + * [0x1| 0| 0| 0] USB_HOST + * [0x1| 1| 0| 0] USB_HSOT_VB * [0x1| 0| 1| 0] Audio Video cable with load * [0x1| 0| 1| 1] MHL without charging cable * [0x1| 1| 1| 1] MHL with charging cable @@ -523,7 +523,7 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info, * - Support charging and data connection through micro-usb port * if USB cable is connected between target and host * device. - * - Support OTG device (Mouse/Keyboard) + * - Support OTG(On-The-Go) device (Ex: Mouse/Keyboard) */ ret = max77693_muic_set_path(info, info->path_usb, attached); if (ret < 0) @@ -609,9 +609,9 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info) MAX77693_CABLE_GROUP_ADC_GND, &attached); switch (cable_type_gnd) { - case MAX77693_MUIC_GND_USB_OTG: - case MAX77693_MUIC_GND_USB_OTG_VB: - /* USB_OTG, PATH: AP_USB */ + case MAX77693_MUIC_GND_USB_HOST: + case MAX77693_MUIC_GND_USB_HOST_VB: + /* USB_HOST, PATH: AP_USB */ ret = max77693_muic_set_path(info, CONTROL1_SW_USB, attached); if (ret < 0) return ret; @@ -704,7 +704,7 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info) switch (cable_type) { case MAX77693_MUIC_ADC_GROUND: - /* USB_OTG/MHL/Audio */ + /* USB_HOST/MHL/Audio */ max77693_muic_adc_ground_handler(info); break; case MAX77693_MUIC_ADC_FACTORY_MODE_USB_OFF: @@ -886,7 +886,7 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) * - Support charging and data connection through micro- * usb port if USB cable is connected between target * and host device - * - Support OTG device (Mouse/Keyboard) + * - Support OTG(On-The-Go) device (Ex: Mouse/Keyboard) */ ret = max77693_muic_set_path(info, info->path_usb, attached); From e52817faae359ce95c93c2b6eb88b16d4b430181 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 2 Feb 2015 12:21:59 +0200 Subject: [PATCH 003/133] extcon: usb-gpio: Introduce gpio usb extcon driver This driver observes the USB ID pin connected over a GPIO and updates the USB cable extcon states accordingly. The existing GPIO extcon driver is not suitable for this purpose as it needs to be taught to understand USB cable states and it can't handle more than one cable per instance. For the USB case we need to handle 2 cable states. 1) USB (attach/detach) 2) USB-HOST (attach/detach) This driver can be easily updated in the future to handle VBUS events in case it happens to be available on GPIO for any platform. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi Acked-by: Felipe Balbi Signed-off-by: Chanwoo Choi --- .../bindings/extcon/extcon-usb-gpio.txt | 18 ++ drivers/extcon/Kconfig | 7 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-usb-gpio.c | 237 ++++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt create mode 100644 drivers/extcon/extcon-usb-gpio.c diff --git a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt new file mode 100644 index 000000000000..af0b903de293 --- /dev/null +++ b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt @@ -0,0 +1,18 @@ +USB GPIO Extcon device + +This is a virtual device used to generate USB cable states from the USB ID pin +connected to a GPIO pin. + +Required properties: +- compatible: Should be "linux,extcon-usb-gpio" +- id-gpio: gpio for USB ID pin. See gpio binding. + +Example: Examples of extcon-usb-gpio node in dra7-evm.dts as listed below: + extcon_usb1 { + compatible = "linux,extcon-usb-gpio"; + id-gpio = <&gpio6 1 GPIO_ACTIVE_HIGH>; + } + + &omap_dwc3_1 { + extcon = <&extcon_usb1>; + }; diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 6a1f7de6fa54..e4c01ab046f7 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -93,4 +93,11 @@ config EXTCON_SM5502 Silicon Mitus SM5502. The SM5502 is a USB port accessory detector and switch. +config EXTCON_USB_GPIO + tristate "USB GPIO extcon support" + depends on GPIOLIB + help + Say Y here to enable GPIO based USB cable detection extcon support. + Used typically if GPIO is used for USB ID pin detection. + endif # MULTISTATE_SWITCH diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 0370b42e5a27..6a08a98dc069 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o obj-$(CONFIG_EXTCON_RT8973A) += extcon-rt8973a.o obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o +obj-$(CONFIG_EXTCON_USB_GPIO) += extcon-usb-gpio.o diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c new file mode 100644 index 000000000000..3f0bad3ce8aa --- /dev/null +++ b/drivers/extcon/extcon-usb-gpio.c @@ -0,0 +1,237 @@ +/** + * drivers/extcon/extcon-usb-gpio.c - USB GPIO extcon driver + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Author: Roger Quadros + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_GPIO_DEBOUNCE_MS 20 /* ms */ + +struct usb_extcon_info { + struct device *dev; + struct extcon_dev *edev; + + struct gpio_desc *id_gpiod; + int id_irq; + + unsigned long debounce_jiffies; + struct delayed_work wq_detcable; +}; + +/* List of detectable cables */ +enum { + EXTCON_CABLE_USB = 0, + EXTCON_CABLE_USB_HOST, + + EXTCON_CABLE_END, +}; + +static const char *usb_extcon_cable[] = { + [EXTCON_CABLE_USB] = "USB", + [EXTCON_CABLE_USB_HOST] = "USB-HOST", + NULL, +}; + +static void usb_extcon_detect_cable(struct work_struct *work) +{ + int id; + struct usb_extcon_info *info = container_of(to_delayed_work(work), + struct usb_extcon_info, + wq_detcable); + + /* check ID and update cable state */ + id = gpiod_get_value_cansleep(info->id_gpiod); + if (id) { + /* + * ID = 1 means USB HOST cable detached. + * As we don't have event for USB peripheral cable attached, + * we simulate USB peripheral attach here. + */ + extcon_set_cable_state(info->edev, + usb_extcon_cable[EXTCON_CABLE_USB_HOST], + false); + extcon_set_cable_state(info->edev, + usb_extcon_cable[EXTCON_CABLE_USB], + true); + } else { + /* + * ID = 0 means USB HOST cable attached. + * As we don't have event for USB peripheral cable detached, + * we simulate USB peripheral detach here. + */ + extcon_set_cable_state(info->edev, + usb_extcon_cable[EXTCON_CABLE_USB], + false); + extcon_set_cable_state(info->edev, + usb_extcon_cable[EXTCON_CABLE_USB_HOST], + true); + } +} + +static irqreturn_t usb_irq_handler(int irq, void *dev_id) +{ + struct usb_extcon_info *info = dev_id; + + queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, + info->debounce_jiffies); + + return IRQ_HANDLED; +} + +static int usb_extcon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct usb_extcon_info *info; + int ret; + + if (!np) + return -EINVAL; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = dev; + info->id_gpiod = devm_gpiod_get(&pdev->dev, "id"); + if (IS_ERR(info->id_gpiod)) { + dev_err(dev, "failed to get ID GPIO\n"); + return PTR_ERR(info->id_gpiod); + } + + ret = gpiod_set_debounce(info->id_gpiod, + USB_GPIO_DEBOUNCE_MS * 1000); + if (ret < 0) + info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEBOUNCE_MS); + + INIT_DELAYED_WORK(&info->wq_detcable, usb_extcon_detect_cable); + + info->id_irq = gpiod_to_irq(info->id_gpiod); + if (info->id_irq < 0) { + dev_err(dev, "failed to get ID IRQ\n"); + return info->id_irq; + } + + ret = devm_request_threaded_irq(dev, info->id_irq, NULL, + usb_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + pdev->name, info); + if (ret < 0) { + dev_err(dev, "failed to request handler for ID IRQ\n"); + return ret; + } + + info->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable); + if (IS_ERR(info->edev)) { + dev_err(dev, "failed to allocate extcon device\n"); + return -ENOMEM; + } + + ret = devm_extcon_dev_register(dev, info->edev); + if (ret < 0) { + dev_err(dev, "failed to register extcon device\n"); + return ret; + } + + platform_set_drvdata(pdev, info); + device_init_wakeup(dev, 1); + + /* Perform initial detection */ + usb_extcon_detect_cable(&info->wq_detcable.work); + + return 0; +} + +static int usb_extcon_remove(struct platform_device *pdev) +{ + struct usb_extcon_info *info = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&info->wq_detcable); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int usb_extcon_suspend(struct device *dev) +{ + struct usb_extcon_info *info = dev_get_drvdata(dev); + int ret = 0; + + if (device_may_wakeup(dev)) { + ret = enable_irq_wake(info->id_irq); + if (ret) + return ret; + } + + /* + * We don't want to process any IRQs after this point + * as GPIOs used behind I2C subsystem might not be + * accessible until resume completes. So disable IRQ. + */ + disable_irq(info->id_irq); + + return ret; +} + +static int usb_extcon_resume(struct device *dev) +{ + struct usb_extcon_info *info = dev_get_drvdata(dev); + int ret = 0; + + if (device_may_wakeup(dev)) { + ret = disable_irq_wake(info->id_irq); + if (ret) + return ret; + } + + enable_irq(info->id_irq); + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(usb_extcon_pm_ops, + usb_extcon_suspend, usb_extcon_resume); + +static struct of_device_id usb_extcon_dt_match[] = { + { .compatible = "linux,extcon-usb-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, usb_extcon_dt_match); + +static struct platform_driver usb_extcon_driver = { + .probe = usb_extcon_probe, + .remove = usb_extcon_remove, + .driver = { + .name = "extcon-usb-gpio", + .pm = &usb_extcon_pm_ops, + .of_match_table = usb_extcon_dt_match, + }, +}; + +module_platform_driver(usb_extcon_driver); + +MODULE_AUTHOR("Roger Quadros "); +MODULE_DESCRIPTION("USB GPIO extcon driver"); +MODULE_LICENSE("GPL v2"); From 27a28d32b4f22a4ae687837aeda6afb42116cca4 Mon Sep 17 00:00:00 2001 From: Jaewon Kim Date: Wed, 4 Feb 2015 13:56:07 +0900 Subject: [PATCH 004/133] extcon: max77843: Add max77843 MUIC driver This patch adds MAX77843 extcon driver to support for MUIC(Micro USB Interface Controller) device by using EXTCON subsystem to handle various external connectors. Signed-off-by: Jaewon Kim Signed-off-by: Chanwoo Choi --- drivers/extcon/Kconfig | 10 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-max77843.c | 881 +++++++++++++++++++++++++++++++ 3 files changed, 892 insertions(+) create mode 100644 drivers/extcon/extcon-max77843.c diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index e4c01ab046f7..fdc0bf0543ce 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -55,6 +55,16 @@ config EXTCON_MAX77693 Maxim MAX77693 PMIC. The MAX77693 MUIC is a USB port accessory detector and switch. +config EXTCON_MAX77843 + tristate "MAX77843 EXTCON Support" + depends on MFD_MAX77843 + select IRQ_DOMAIN + select REGMAP_I2C + help + If you say yes here you get support for the MUIC device of + Maxim MAX77843. The MAX77843 MUIC is a USB port accessory + detector add switch. + config EXTCON_MAX8997 tristate "MAX8997 EXTCON Support" depends on MFD_MAX8997 && IRQ_DOMAIN diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 6a08a98dc069..e1eb2d50e72b 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o +obj-$(CONFIG_EXTCON_MAX77843) += extcon-max77843.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o obj-$(CONFIG_EXTCON_RT8973A) += extcon-rt8973a.o diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c new file mode 100644 index 000000000000..598a017cc91b --- /dev/null +++ b/drivers/extcon/extcon-max77843.c @@ -0,0 +1,881 @@ +/* + * extcon-max77843.c - Maxim MAX77843 extcon driver to support + * MUIC(Micro USB Interface Controller) + * + * Copyright (C) 2015 Samsung Electronics + * Author: Jaewon Kim + * + * 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 Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DELAY_MS_DEFAULT 15000 /* unit: millisecond */ + +enum max77843_muic_status { + MAX77843_MUIC_STATUS1 = 0, + MAX77843_MUIC_STATUS2, + MAX77843_MUIC_STATUS3, + + MAX77843_MUIC_STATUS_NUM, +}; + +struct max77843_muic_info { + struct device *dev; + struct max77843 *max77843; + struct extcon_dev *edev; + + struct mutex mutex; + struct work_struct irq_work; + struct delayed_work wq_detcable; + + u8 status[MAX77843_MUIC_STATUS_NUM]; + int prev_cable_type; + int prev_chg_type; + int prev_gnd_type; + + bool irq_adc; + bool irq_chg; +}; + +enum max77843_muic_cable_group { + MAX77843_CABLE_GROUP_ADC = 0, + MAX77843_CABLE_GROUP_ADC_GND, + MAX77843_CABLE_GROUP_CHG, +}; + +enum max77843_muic_adc_debounce_time { + MAX77843_DEBOUNCE_TIME_5MS = 0, + MAX77843_DEBOUNCE_TIME_10MS, + MAX77843_DEBOUNCE_TIME_25MS, + MAX77843_DEBOUNCE_TIME_38_62MS, +}; + +/* Define accessory cable type */ +enum max77843_muic_accessory_type { + MAX77843_MUIC_ADC_GROUND = 0, + MAX77843_MUIC_ADC_SEND_END_BUTTON, + MAX77843_MUIC_ADC_REMOTE_S1_BUTTON, + MAX77843_MUIC_ADC_REMOTE_S2_BUTTON, + MAX77843_MUIC_ADC_REMOTE_S3_BUTTON, + MAX77843_MUIC_ADC_REMOTE_S4_BUTTON, + MAX77843_MUIC_ADC_REMOTE_S5_BUTTON, + MAX77843_MUIC_ADC_REMOTE_S6_BUTTON, + MAX77843_MUIC_ADC_REMOTE_S7_BUTTON, + MAX77843_MUIC_ADC_REMOTE_S8_BUTTON, + MAX77843_MUIC_ADC_REMOTE_S9_BUTTON, + MAX77843_MUIC_ADC_REMOTE_S10_BUTTON, + MAX77843_MUIC_ADC_REMOTE_S11_BUTTON, + MAX77843_MUIC_ADC_REMOTE_S12_BUTTON, + MAX77843_MUIC_ADC_RESERVED_ACC_1, + MAX77843_MUIC_ADC_RESERVED_ACC_2, + MAX77843_MUIC_ADC_RESERVED_ACC_3, + MAX77843_MUIC_ADC_RESERVED_ACC_4, + MAX77843_MUIC_ADC_RESERVED_ACC_5, + MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE2, + MAX77843_MUIC_ADC_PHONE_POWERED_DEV, + MAX77843_MUIC_ADC_TTY_CONVERTER, + MAX77843_MUIC_ADC_UART_CABLE, + MAX77843_MUIC_ADC_CEA936A_TYPE1_CHG, + MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF, + MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON, + MAX77843_MUIC_ADC_AV_CABLE_NOLOAD, + MAX77843_MUIC_ADC_CEA936A_TYPE2_CHG, + MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF, + MAX77843_MUIC_ADC_FACTORY_MODE_UART_ON, + MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1, + MAX77843_MUIC_ADC_OPEN, + + /* The blow accessories should check + not only ADC value but also ADC1K and VBVolt value. */ + /* Offset|ADC1K|VBVolt| */ + MAX77843_MUIC_GND_USB_HOST = 0x100, /* 0x1| 0| 0| */ + MAX77843_MUIC_GND_USB_HOST_VB = 0x101, /* 0x1| 0| 1| */ + MAX77843_MUIC_GND_MHL = 0x102, /* 0x1| 1| 0| */ + MAX77843_MUIC_GND_MHL_VB = 0x103, /* 0x1| 1| 1| */ +}; + +/* Define charger cable type */ +enum max77843_muic_charger_type { + MAX77843_MUIC_CHG_NONE = 0, + MAX77843_MUIC_CHG_USB, + MAX77843_MUIC_CHG_DOWNSTREAM, + MAX77843_MUIC_CHG_DEDICATED, + MAX77843_MUIC_CHG_SPECIAL_500MA, + MAX77843_MUIC_CHG_SPECIAL_1A, + MAX77843_MUIC_CHG_SPECIAL_BIAS, + MAX77843_MUIC_CHG_RESERVED, + MAX77843_MUIC_CHG_GND, +}; + +enum { + MAX77843_CABLE_USB = 0, + MAX77843_CABLE_USB_HOST, + MAX77843_CABLE_TA, + MAX77843_CABLE_CHARGE_DOWNSTREAM, + MAX77843_CABLE_FAST_CHARGER, + MAX77843_CABLE_SLOW_CHARGER, + MAX77843_CABLE_MHL, + MAX77843_CABLE_MHL_TA, + MAX77843_CABLE_JIG_USB_ON, + MAX77843_CABLE_JIG_USB_OFF, + MAX77843_CABLE_JIG_UART_ON, + MAX77843_CABLE_JIG_UART_OFF, + + MAX77843_CABLE_NUM, +}; + +static const char *max77843_extcon_cable[] = { + [MAX77843_CABLE_USB] = "USB", + [MAX77843_CABLE_USB_HOST] = "USB-HOST", + [MAX77843_CABLE_TA] = "TA", + [MAX77843_CABLE_CHARGE_DOWNSTREAM] = "CHARGER-DOWNSTREAM", + [MAX77843_CABLE_FAST_CHARGER] = "FAST-CHARGER", + [MAX77843_CABLE_SLOW_CHARGER] = "SLOW-CHARGER", + [MAX77843_CABLE_MHL] = "MHL", + [MAX77843_CABLE_MHL_TA] = "MHL-TA", + [MAX77843_CABLE_JIG_USB_ON] = "JIG-USB-ON", + [MAX77843_CABLE_JIG_USB_OFF] = "JIG-USB-OFF", + [MAX77843_CABLE_JIG_UART_ON] = "JIG-UART-ON", + [MAX77843_CABLE_JIG_UART_OFF] = "JIG-UART-OFF", +}; + +struct max77843_muic_irq { + unsigned int irq; + const char *name; + unsigned int virq; +}; + +static struct max77843_muic_irq max77843_muic_irqs[] = { + { MAX77843_MUIC_IRQ_INT1_ADC, "MUIC-ADC" }, + { MAX77843_MUIC_IRQ_INT1_ADCERROR, "MUIC-ADC_ERROR" }, + { MAX77843_MUIC_IRQ_INT1_ADC1K, "MUIC-ADC1K" }, + { MAX77843_MUIC_IRQ_INT2_CHGTYP, "MUIC-CHGTYP" }, + { MAX77843_MUIC_IRQ_INT2_CHGDETRUN, "MUIC-CHGDETRUN" }, + { MAX77843_MUIC_IRQ_INT2_DCDTMR, "MUIC-DCDTMR" }, + { MAX77843_MUIC_IRQ_INT2_DXOVP, "MUIC-DXOVP" }, + { MAX77843_MUIC_IRQ_INT2_VBVOLT, "MUIC-VBVOLT" }, + { MAX77843_MUIC_IRQ_INT3_VBADC, "MUIC-VBADC" }, + { MAX77843_MUIC_IRQ_INT3_VDNMON, "MUIC-VDNMON" }, + { MAX77843_MUIC_IRQ_INT3_DNRES, "MUIC-DNRES" }, + { MAX77843_MUIC_IRQ_INT3_MPNACK, "MUIC-MPNACK"}, + { MAX77843_MUIC_IRQ_INT3_MRXBUFOW, "MUIC-MRXBUFOW"}, + { MAX77843_MUIC_IRQ_INT3_MRXTRF, "MUIC-MRXTRF"}, + { MAX77843_MUIC_IRQ_INT3_MRXPERR, "MUIC-MRXPERR"}, + { MAX77843_MUIC_IRQ_INT3_MRXRDY, "MUIC-MRXRDY"}, +}; + +static const struct regmap_config max77843_muic_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77843_MUIC_REG_END, +}; + +static const struct regmap_irq max77843_muic_irq[] = { + /* INT1 interrupt */ + { .reg_offset = 0, .mask = MAX77843_MUIC_ADC, }, + { .reg_offset = 0, .mask = MAX77843_MUIC_ADCERROR, }, + { .reg_offset = 0, .mask = MAX77843_MUIC_ADC1K, }, + + /* INT2 interrupt */ + { .reg_offset = 1, .mask = MAX77843_MUIC_CHGTYP, }, + { .reg_offset = 1, .mask = MAX77843_MUIC_CHGDETRUN, }, + { .reg_offset = 1, .mask = MAX77843_MUIC_DCDTMR, }, + { .reg_offset = 1, .mask = MAX77843_MUIC_DXOVP, }, + { .reg_offset = 1, .mask = MAX77843_MUIC_VBVOLT, }, + + /* INT3 interrupt */ + { .reg_offset = 2, .mask = MAX77843_MUIC_VBADC, }, + { .reg_offset = 2, .mask = MAX77843_MUIC_VDNMON, }, + { .reg_offset = 2, .mask = MAX77843_MUIC_DNRES, }, + { .reg_offset = 2, .mask = MAX77843_MUIC_MPNACK, }, + { .reg_offset = 2, .mask = MAX77843_MUIC_MRXBUFOW, }, + { .reg_offset = 2, .mask = MAX77843_MUIC_MRXTRF, }, + { .reg_offset = 2, .mask = MAX77843_MUIC_MRXPERR, }, + { .reg_offset = 2, .mask = MAX77843_MUIC_MRXRDY, }, +}; + +static const struct regmap_irq_chip max77843_muic_irq_chip = { + .name = "max77843-muic", + .status_base = MAX77843_MUIC_REG_INT1, + .mask_base = MAX77843_MUIC_REG_INTMASK1, + .mask_invert = true, + .num_regs = 3, + .irqs = max77843_muic_irq, + .num_irqs = ARRAY_SIZE(max77843_muic_irq), +}; + +static int max77843_muic_set_path(struct max77843_muic_info *info, + u8 val, bool attached) +{ + struct max77843 *max77843 = info->max77843; + int ret = 0; + unsigned int ctrl1, ctrl2; + + if (attached) + ctrl1 = val; + else + ctrl1 = CONTROL1_SW_OPEN; + + ret = regmap_update_bits(max77843->regmap_muic, + MAX77843_MUIC_REG_CONTROL1, + CONTROL1_COM_SW, ctrl1); + if (ret < 0) { + dev_err(info->dev, "Cannot switch MUIC port\n"); + return ret; + } + + if (attached) + ctrl2 = MAX77843_MUIC_CONTROL2_CPEN_MASK; + else + ctrl2 = MAX77843_MUIC_CONTROL2_LOWPWR_MASK; + + ret = regmap_update_bits(max77843->regmap_muic, + MAX77843_MUIC_REG_CONTROL2, + MAX77843_MUIC_CONTROL2_LOWPWR_MASK | + MAX77843_MUIC_CONTROL2_CPEN_MASK, ctrl2); + if (ret < 0) { + dev_err(info->dev, "Cannot update lowpower mode\n"); + return ret; + } + + dev_dbg(info->dev, + "CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n", + ctrl1, ctrl2, attached ? "attached" : "detached"); + + return 0; +} + +static int max77843_muic_get_cable_type(struct max77843_muic_info *info, + enum max77843_muic_cable_group group, bool *attached) +{ + int adc, chg_type, cable_type, gnd_type; + + adc = info->status[MAX77843_MUIC_STATUS1] & + MAX77843_MUIC_STATUS1_ADC_MASK; + adc >>= STATUS1_ADC_SHIFT; + + switch (group) { + case MAX77843_CABLE_GROUP_ADC: + if (adc == MAX77843_MUIC_ADC_OPEN) { + *attached = false; + cable_type = info->prev_cable_type; + info->prev_cable_type = MAX77843_MUIC_ADC_OPEN; + } else { + *attached = true; + cable_type = info->prev_cable_type = adc; + } + break; + case MAX77843_CABLE_GROUP_CHG: + chg_type = info->status[MAX77843_MUIC_STATUS2] & + MAX77843_MUIC_STATUS2_CHGTYP_MASK; + + /* Check GROUND accessory with charger cable */ + if (adc == MAX77843_MUIC_ADC_GROUND) { + if (chg_type == MAX77843_MUIC_CHG_NONE) { + /* The following state when charger cable is + * disconnected but the GROUND accessory still + * connected */ + *attached = false; + cable_type = info->prev_chg_type; + info->prev_chg_type = MAX77843_MUIC_CHG_NONE; + } else { + + /* The following state when charger cable is + * connected on the GROUND accessory */ + *attached = true; + cable_type = MAX77843_MUIC_CHG_GND; + info->prev_chg_type = MAX77843_MUIC_CHG_GND; + } + break; + } + + if (chg_type == MAX77843_MUIC_CHG_NONE) { + *attached = false; + cable_type = info->prev_chg_type; + info->prev_chg_type = MAX77843_MUIC_CHG_NONE; + } else { + *attached = true; + cable_type = info->prev_chg_type = chg_type; + } + break; + case MAX77843_CABLE_GROUP_ADC_GND: + if (adc == MAX77843_MUIC_ADC_OPEN) { + *attached = false; + cable_type = info->prev_gnd_type; + info->prev_gnd_type = MAX77843_MUIC_ADC_OPEN; + } else { + *attached = true; + + /* Offset|ADC1K|VBVolt| + * 0x1| 0| 0| USB-HOST + * 0x1| 0| 1| USB-HOST with VB + * 0x1| 1| 0| MHL + * 0x1| 1| 1| MHL with VB */ + /* Get ADC1K register bit */ + gnd_type = (info->status[MAX77843_MUIC_STATUS1] & + MAX77843_MUIC_STATUS1_ADC1K_MASK); + + /* Get VBVolt register bit */ + gnd_type |= (info->status[MAX77843_MUIC_STATUS2] & + MAX77843_MUIC_STATUS2_VBVOLT_MASK); + gnd_type >>= STATUS2_VBVOLT_SHIFT; + + /* Offset of GND cable */ + gnd_type |= MAX77843_MUIC_GND_USB_HOST; + cable_type = info->prev_gnd_type = gnd_type; + } + break; + default: + dev_err(info->dev, "Unknown cable group (%d)\n", group); + cable_type = -EINVAL; + break; + } + + return cable_type; +} + +static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info) +{ + int ret, gnd_cable_type; + bool attached; + + gnd_cable_type = max77843_muic_get_cable_type(info, + MAX77843_CABLE_GROUP_ADC_GND, &attached); + dev_dbg(info->dev, "external connector is %s (gnd:0x%02x)\n", + attached ? "attached" : "detached", gnd_cable_type); + + switch (gnd_cable_type) { + case MAX77843_MUIC_GND_USB_HOST: + case MAX77843_MUIC_GND_USB_HOST_VB: + ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached); + if (ret < 0) + return ret; + + extcon_set_cable_state(info->edev, "USB-HOST", attached); + break; + case MAX77843_MUIC_GND_MHL_VB: + case MAX77843_MUIC_GND_MHL: + ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); + if (ret < 0) + return ret; + + extcon_set_cable_state(info->edev, "MHL", attached); + break; + default: + dev_err(info->dev, "failed to detect %s accessory(gnd:0x%x)\n", + attached ? "attached" : "detached", gnd_cable_type); + return -EINVAL; + } + + return 0; +} + +static int max77843_muic_jig_handler(struct max77843_muic_info *info, + int cable_type, bool attached) +{ + int ret; + + dev_dbg(info->dev, "external connector is %s (adc:0x%02x)\n", + attached ? "attached" : "detached", cable_type); + + switch (cable_type) { + case MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF: + ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached); + if (ret < 0) + return ret; + extcon_set_cable_state(info->edev, "JIG-USB-OFF", attached); + break; + case MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON: + ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached); + if (ret < 0) + return ret; + extcon_set_cable_state(info->edev, "JIG-USB-ON", attached); + break; + case MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF: + ret = max77843_muic_set_path(info, CONTROL1_SW_UART, attached); + if (ret < 0) + return ret; + extcon_set_cable_state(info->edev, "JIG-UART-OFF", attached); + break; + default: + ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); + if (ret < 0) + return ret; + break; + } + + return 0; +} + +static int max77843_muic_adc_handler(struct max77843_muic_info *info) +{ + int ret, cable_type; + bool attached; + + cable_type = max77843_muic_get_cable_type(info, + MAX77843_CABLE_GROUP_ADC, &attached); + + dev_dbg(info->dev, + "external connector is %s (adc:0x%02x, prev_adc:0x%x)\n", + attached ? "attached" : "detached", cable_type, + info->prev_cable_type); + + switch (cable_type) { + case MAX77843_MUIC_ADC_GROUND: + ret = max77843_muic_adc_gnd_handler(info); + if (ret < 0) + return ret; + break; + case MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF: + case MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON: + case MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF: + ret = max77843_muic_jig_handler(info, cable_type, attached); + if (ret < 0) + return ret; + break; + case MAX77843_MUIC_ADC_SEND_END_BUTTON: + case MAX77843_MUIC_ADC_REMOTE_S1_BUTTON: + case MAX77843_MUIC_ADC_REMOTE_S2_BUTTON: + case MAX77843_MUIC_ADC_REMOTE_S3_BUTTON: + case MAX77843_MUIC_ADC_REMOTE_S4_BUTTON: + case MAX77843_MUIC_ADC_REMOTE_S5_BUTTON: + case MAX77843_MUIC_ADC_REMOTE_S6_BUTTON: + case MAX77843_MUIC_ADC_REMOTE_S7_BUTTON: + case MAX77843_MUIC_ADC_REMOTE_S8_BUTTON: + case MAX77843_MUIC_ADC_REMOTE_S9_BUTTON: + case MAX77843_MUIC_ADC_REMOTE_S10_BUTTON: + case MAX77843_MUIC_ADC_REMOTE_S11_BUTTON: + case MAX77843_MUIC_ADC_REMOTE_S12_BUTTON: + case MAX77843_MUIC_ADC_RESERVED_ACC_1: + case MAX77843_MUIC_ADC_RESERVED_ACC_2: + case MAX77843_MUIC_ADC_RESERVED_ACC_3: + case MAX77843_MUIC_ADC_RESERVED_ACC_4: + case MAX77843_MUIC_ADC_RESERVED_ACC_5: + case MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE2: + case MAX77843_MUIC_ADC_PHONE_POWERED_DEV: + case MAX77843_MUIC_ADC_TTY_CONVERTER: + case MAX77843_MUIC_ADC_UART_CABLE: + case MAX77843_MUIC_ADC_CEA936A_TYPE1_CHG: + case MAX77843_MUIC_ADC_AV_CABLE_NOLOAD: + case MAX77843_MUIC_ADC_CEA936A_TYPE2_CHG: + case MAX77843_MUIC_ADC_FACTORY_MODE_UART_ON: + case MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1: + case MAX77843_MUIC_ADC_OPEN: + dev_err(info->dev, + "accessory is %s but it isn't used (adc:0x%x)\n", + attached ? "attached" : "detached", cable_type); + return -EAGAIN; + default: + dev_err(info->dev, + "failed to detect %s accessory (adc:0x%x)\n", + attached ? "attached" : "detached", cable_type); + return -EINVAL; + } + + return 0; +} + +static int max77843_muic_chg_handler(struct max77843_muic_info *info) +{ + int ret, chg_type, gnd_type; + bool attached; + + chg_type = max77843_muic_get_cable_type(info, + MAX77843_CABLE_GROUP_CHG, &attached); + + dev_dbg(info->dev, + "external connector is %s(chg_type:0x%x, prev_chg_type:0x%x)\n", + attached ? "attached" : "detached", + chg_type, info->prev_chg_type); + + switch (chg_type) { + case MAX77843_MUIC_CHG_USB: + ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached); + if (ret < 0) + return ret; + + extcon_set_cable_state(info->edev, "USB", attached); + break; + case MAX77843_MUIC_CHG_DOWNSTREAM: + ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); + if (ret < 0) + return ret; + + extcon_set_cable_state(info->edev, + "CHARGER-DOWNSTREAM", attached); + break; + case MAX77843_MUIC_CHG_DEDICATED: + ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); + if (ret < 0) + return ret; + + extcon_set_cable_state(info->edev, "TA", attached); + break; + case MAX77843_MUIC_CHG_SPECIAL_500MA: + ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); + if (ret < 0) + return ret; + + extcon_set_cable_state(info->edev, "SLOW-CHAREGER", attached); + break; + case MAX77843_MUIC_CHG_SPECIAL_1A: + ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); + if (ret < 0) + return ret; + + extcon_set_cable_state(info->edev, "FAST-CHARGER", attached); + break; + case MAX77843_MUIC_CHG_GND: + gnd_type = max77843_muic_get_cable_type(info, + MAX77843_CABLE_GROUP_ADC_GND, &attached); + + /* Charger cable on MHL accessory is attach or detach */ + if (gnd_type == MAX77843_MUIC_GND_MHL_VB) + extcon_set_cable_state(info->edev, "MHL-TA", true); + else if (gnd_type == MAX77843_MUIC_GND_MHL) + extcon_set_cable_state(info->edev, "MHL-TA", false); + break; + case MAX77843_MUIC_CHG_NONE: + break; + default: + dev_err(info->dev, + "failed to detect %s accessory (chg_type:0x%x)\n", + attached ? "attached" : "detached", chg_type); + + max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); + return -EINVAL; + } + + return 0; +} + +static void max77843_muic_irq_work(struct work_struct *work) +{ + struct max77843_muic_info *info = container_of(work, + struct max77843_muic_info, irq_work); + struct max77843 *max77843 = info->max77843; + int ret = 0; + + mutex_lock(&info->mutex); + + ret = regmap_bulk_read(max77843->regmap_muic, + MAX77843_MUIC_REG_STATUS1, info->status, + MAX77843_MUIC_STATUS_NUM); + if (ret) { + dev_err(info->dev, "Cannot read STATUS registers\n"); + mutex_unlock(&info->mutex); + return; + } + + if (info->irq_adc) { + ret = max77843_muic_adc_handler(info); + if (ret) + dev_err(info->dev, "Unknown cable type\n"); + info->irq_adc = false; + } + + if (info->irq_chg) { + ret = max77843_muic_chg_handler(info); + if (ret) + dev_err(info->dev, "Unknown charger type\n"); + info->irq_chg = false; + } + + mutex_unlock(&info->mutex); +} + +static irqreturn_t max77843_muic_irq_handler(int irq, void *data) +{ + struct max77843_muic_info *info = data; + int i, irq_type = -1; + + for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++) + if (irq == max77843_muic_irqs[i].virq) + irq_type = max77843_muic_irqs[i].irq; + + switch (irq_type) { + case MAX77843_MUIC_IRQ_INT1_ADC: + case MAX77843_MUIC_IRQ_INT1_ADCERROR: + case MAX77843_MUIC_IRQ_INT1_ADC1K: + info->irq_adc = true; + break; + case MAX77843_MUIC_IRQ_INT2_CHGTYP: + case MAX77843_MUIC_IRQ_INT2_CHGDETRUN: + case MAX77843_MUIC_IRQ_INT2_DCDTMR: + case MAX77843_MUIC_IRQ_INT2_DXOVP: + case MAX77843_MUIC_IRQ_INT2_VBVOLT: + info->irq_chg = true; + break; + case MAX77843_MUIC_IRQ_INT3_VBADC: + case MAX77843_MUIC_IRQ_INT3_VDNMON: + case MAX77843_MUIC_IRQ_INT3_DNRES: + case MAX77843_MUIC_IRQ_INT3_MPNACK: + case MAX77843_MUIC_IRQ_INT3_MRXBUFOW: + case MAX77843_MUIC_IRQ_INT3_MRXTRF: + case MAX77843_MUIC_IRQ_INT3_MRXPERR: + case MAX77843_MUIC_IRQ_INT3_MRXRDY: + break; + default: + dev_err(info->dev, "Cannot recognize IRQ(%d)\n", irq_type); + break; + } + + schedule_work(&info->irq_work); + + return IRQ_HANDLED; +} + +static void max77843_muic_detect_cable_wq(struct work_struct *work) +{ + struct max77843_muic_info *info = container_of(to_delayed_work(work), + struct max77843_muic_info, wq_detcable); + struct max77843 *max77843 = info->max77843; + int chg_type, adc, ret; + bool attached; + + mutex_lock(&info->mutex); + + ret = regmap_bulk_read(max77843->regmap_muic, + MAX77843_MUIC_REG_STATUS1, info->status, + MAX77843_MUIC_STATUS_NUM); + if (ret) { + dev_err(info->dev, "Cannot read STATUS registers\n"); + goto err_cable_wq; + } + + adc = max77843_muic_get_cable_type(info, + MAX77843_CABLE_GROUP_ADC, &attached); + if (attached && adc != MAX77843_MUIC_ADC_OPEN) { + ret = max77843_muic_adc_handler(info); + if (ret < 0) { + dev_err(info->dev, "Cannot detect accessory\n"); + goto err_cable_wq; + } + } + + chg_type = max77843_muic_get_cable_type(info, + MAX77843_CABLE_GROUP_CHG, &attached); + if (attached && chg_type != MAX77843_MUIC_CHG_NONE) { + ret = max77843_muic_chg_handler(info); + if (ret < 0) { + dev_err(info->dev, "Cannot detect charger accessory\n"); + goto err_cable_wq; + } + } + +err_cable_wq: + mutex_unlock(&info->mutex); +} + +static int max77843_muic_set_debounce_time(struct max77843_muic_info *info, + enum max77843_muic_adc_debounce_time time) +{ + struct max77843 *max77843 = info->max77843; + unsigned int ret; + + switch (time) { + case MAX77843_DEBOUNCE_TIME_5MS: + case MAX77843_DEBOUNCE_TIME_10MS: + case MAX77843_DEBOUNCE_TIME_25MS: + case MAX77843_DEBOUNCE_TIME_38_62MS: + ret = regmap_update_bits(max77843->regmap_muic, + MAX77843_MUIC_REG_CONTROL4, + MAX77843_MUIC_CONTROL4_ADCDBSET_MASK, + time << CONTROL4_ADCDBSET_SHIFT); + if (ret < 0) { + dev_err(info->dev, "Cannot write MUIC regmap\n"); + return ret; + } + break; + default: + dev_err(info->dev, "Invalid ADC debounce time\n"); + return -EINVAL; + } + + return 0; +} + +static int max77843_init_muic_regmap(struct max77843 *max77843) +{ + int ret; + + max77843->i2c_muic = i2c_new_dummy(max77843->i2c->adapter, + I2C_ADDR_MUIC); + if (!max77843->i2c_muic) { + dev_err(&max77843->i2c->dev, + "Cannot allocate I2C device for MUIC\n"); + return PTR_ERR(max77843->i2c_muic); + } + + i2c_set_clientdata(max77843->i2c_muic, max77843); + + max77843->regmap_muic = devm_regmap_init_i2c(max77843->i2c_muic, + &max77843_muic_regmap_config); + if (IS_ERR(max77843->regmap_muic)) { + ret = PTR_ERR(max77843->regmap_muic); + goto err_muic_i2c; + } + + ret = regmap_add_irq_chip(max77843->regmap_muic, max77843->irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED, + 0, &max77843_muic_irq_chip, &max77843->irq_data_muic); + if (ret < 0) { + dev_err(&max77843->i2c->dev, "Cannot add MUIC IRQ chip\n"); + goto err_muic_i2c; + } + + return 0; + +err_muic_i2c: + i2c_unregister_device(max77843->i2c_muic); + + return ret; +} + +static int max77843_muic_probe(struct platform_device *pdev) +{ + struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent); + struct max77843_muic_info *info; + unsigned int id; + int i, ret; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = &pdev->dev; + info->max77843 = max77843; + + platform_set_drvdata(pdev, info); + mutex_init(&info->mutex); + + /* Initialize i2c and regmap */ + ret = max77843_init_muic_regmap(max77843); + if (ret) { + dev_err(&pdev->dev, "Failed to init MUIC regmap\n"); + return ret; + } + + /* Turn off auto detection configuration */ + ret = regmap_update_bits(max77843->regmap_muic, + MAX77843_MUIC_REG_CONTROL4, + MAX77843_MUIC_CONTROL4_USBAUTO_MASK | + MAX77843_MUIC_CONTROL4_FCTAUTO_MASK, + CONTROL4_AUTO_DISABLE); + + /* Initialize extcon device */ + info->edev = devm_extcon_dev_allocate(&pdev->dev, + max77843_extcon_cable); + if (IS_ERR(info->edev)) { + dev_err(&pdev->dev, "Failed to allocate memory for extcon\n"); + ret = -ENODEV; + goto err_muic_irq; + } + + ret = devm_extcon_dev_register(&pdev->dev, info->edev); + if (ret) { + dev_err(&pdev->dev, "Failed to register extcon device\n"); + goto err_muic_irq; + } + + /* Set ADC debounce time */ + max77843_muic_set_debounce_time(info, MAX77843_DEBOUNCE_TIME_25MS); + + /* Set initial path for UART */ + max77843_muic_set_path(info, CONTROL1_SW_UART, true); + + /* Check revision number of MUIC device */ + ret = regmap_read(max77843->regmap_muic, MAX77843_MUIC_REG_ID, &id); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read revision number\n"); + goto err_muic_irq; + } + dev_info(info->dev, "MUIC device ID : 0x%x\n", id); + + /* Support virtual irq domain for max77843 MUIC device */ + INIT_WORK(&info->irq_work, max77843_muic_irq_work); + + for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++) { + struct max77843_muic_irq *muic_irq = &max77843_muic_irqs[i]; + unsigned int virq = 0; + + virq = regmap_irq_get_virq(max77843->irq_data_muic, + muic_irq->irq); + if (virq <= 0) { + ret = -EINVAL; + goto err_muic_irq; + } + muic_irq->virq = virq; + + ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, + max77843_muic_irq_handler, IRQF_NO_SUSPEND, + muic_irq->name, info); + if (ret) { + dev_err(&pdev->dev, + "Failed to request irq (IRQ: %d, error: %d)\n", + muic_irq->irq, ret); + goto err_muic_irq; + } + } + + /* Detect accessory after completing the initialization of platform */ + INIT_DELAYED_WORK(&info->wq_detcable, max77843_muic_detect_cable_wq); + queue_delayed_work(system_power_efficient_wq, + &info->wq_detcable, msecs_to_jiffies(DELAY_MS_DEFAULT)); + + return 0; + +err_muic_irq: + regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic); + i2c_unregister_device(max77843->i2c_muic); + + return ret; +} + +static int max77843_muic_remove(struct platform_device *pdev) +{ + struct max77843_muic_info *info = platform_get_drvdata(pdev); + struct max77843 *max77843 = info->max77843; + + cancel_work_sync(&info->irq_work); + regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic); + i2c_unregister_device(max77843->i2c_muic); + + return 0; +} + +static const struct platform_device_id max77843_muic_id[] = { + { "max77843-muic", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, max77843_muic_id); + +static struct platform_driver max77843_muic_driver = { + .driver = { + .name = "max77843-muic", + }, + .probe = max77843_muic_probe, + .remove = max77843_muic_remove, + .id_table = max77843_muic_id, +}; + +static int __init max77843_muic_init(void) +{ + return platform_driver_register(&max77843_muic_driver); +} +subsys_initcall(max77843_muic_init); + +MODULE_DESCRIPTION("Maxim MAX77843 Extcon driver"); +MODULE_AUTHOR("Jaewon Kim "); +MODULE_LICENSE("GPL"); From e513229b4c386e6c9f66298c13fde92f73e6e1ac Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 27 Feb 2015 11:25:51 -0800 Subject: [PATCH 005/133] Drivers: hv: vmbus: prevent cpu offlining on newer hypervisors When an SMP Hyper-V guest is running on top of 2012R2 Server and secondary cpus are sent offline (with echo 0 > /sys/devices/system/cpu/cpu$cpu/online) the system freeze is observed. This happens due to the fact that on newer hypervisors (Win8, WS2012R2, ...) vmbus channel handlers are distributed across all cpus (see init_vp_index() function in drivers/hv/channel_mgmt.c) and on cpu offlining nobody reassigns them to CPU0. Prevent cpu offlining when vmbus is loaded until the issue is fixed host-side. This patch also disables hibernation but it is OK as it is also broken (MCE error is hit on resume). Suspend still works. Tested with WS2008R2 and WS2012R2. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index f518b8d7a5b5..3b18a665ef4c 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -704,6 +705,39 @@ static void vmbus_isr(void) } } +#ifdef CONFIG_HOTPLUG_CPU +static int hyperv_cpu_disable(void) +{ + return -ENOSYS; +} + +static void hv_cpu_hotplug_quirk(bool vmbus_loaded) +{ + static void *previous_cpu_disable; + + /* + * Offlining a CPU when running on newer hypervisors (WS2012R2, Win8, + * ...) is not supported at this moment as channel interrupts are + * distributed across all of them. + */ + + if ((vmbus_proto_version == VERSION_WS2008) || + (vmbus_proto_version == VERSION_WIN7)) + return; + + if (vmbus_loaded) { + previous_cpu_disable = smp_ops.cpu_disable; + smp_ops.cpu_disable = hyperv_cpu_disable; + pr_notice("CPU offlining is not supported by hypervisor\n"); + } else if (previous_cpu_disable) + smp_ops.cpu_disable = previous_cpu_disable; +} +#else +static void hv_cpu_hotplug_quirk(bool vmbus_loaded) +{ +} +#endif + /* * vmbus_bus_init -Main vmbus driver initialization routine. * @@ -744,6 +778,7 @@ static int vmbus_bus_init(int irq) if (ret) goto err_alloc; + hv_cpu_hotplug_quirk(true); vmbus_request_offers(); return 0; @@ -997,6 +1032,7 @@ static void __exit vmbus_exit(void) bus_unregister(&hv_bus); hv_cleanup(); acpi_bus_unregister_driver(&vmbus_acpi_driver); + hv_cpu_hotplug_quirk(false); } From bc63b6f634d91a0b2a7f3ba4f266e55fec369de3 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 27 Feb 2015 11:25:52 -0800 Subject: [PATCH 006/133] Drivers: hv: vmbus: rename channel work queues All channel work queues are named 'hv_vmbus_ctl', this makes them indistinguishable in ps output and makes it hard to link to the corresponding vmbus device. Rename them to hv_vmbus_ctl/N and make vmbus device names match, e.g. now vmbus_1 device is served by hv_vmbus_ctl/1 work queue. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 5 ++++- drivers/hv/vmbus_drv.c | 6 ++---- include/linux/hyperv.h | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 3736f71bdec5..ba4b25f0b213 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -139,19 +139,22 @@ EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); */ static struct vmbus_channel *alloc_channel(void) { + static atomic_t chan_num = ATOMIC_INIT(0); struct vmbus_channel *channel; channel = kzalloc(sizeof(*channel), GFP_ATOMIC); if (!channel) return NULL; + channel->id = atomic_inc_return(&chan_num); spin_lock_init(&channel->inbound_lock); spin_lock_init(&channel->lock); INIT_LIST_HEAD(&channel->sc_list); INIT_LIST_HEAD(&channel->percpu_list); - channel->controlwq = create_workqueue("hv_vmbus_ctl"); + channel->controlwq = alloc_workqueue("hv_vmbus_ctl/%d", WQ_MEM_RECLAIM, + 1, channel->id); if (!channel->controlwq) { kfree(channel); return NULL; diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 3b18a665ef4c..e334ccc70582 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -875,10 +875,8 @@ int vmbus_device_register(struct hv_device *child_device_obj) { int ret = 0; - static atomic_t device_num = ATOMIC_INIT(0); - - dev_set_name(&child_device_obj->device, "vmbus_0_%d", - atomic_inc_return(&device_num)); + dev_set_name(&child_device_obj->device, "vmbus_%d", + child_device_obj->channel->id); child_device_obj->device.bus = &hv_bus; child_device_obj->device.parent = &hv_acpi_dev->dev; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 5a2ba674795e..26a32b771ee5 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -646,6 +646,9 @@ struct hv_input_signal_event_buffer { }; struct vmbus_channel { + /* Unique channel id */ + int id; + struct list_head listentry; struct hv_device *device_obj; From adcde069a85cb6674bdcf5f49f434d18b2db6e58 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 27 Feb 2015 11:25:53 -0800 Subject: [PATCH 007/133] Drivers: hv: vmbus: avoid double kfree for device_obj On driver shutdown device_obj is being freed twice: 1) In vmbus_free_channels() 2) vmbus_device_release() (which is being triggered by device_unregister() in vmbus_device_unregister(). This double kfree leads to the following sporadic crash on driver unload: [ 23.469876] general protection fault: 0000 [#1] SMP [ 23.470036] Modules linked in: hv_vmbus(-) [ 23.470036] CPU: 2 PID: 213 Comm: rmmod Not tainted 3.19.0-rc5_bug923184+ #488 [ 23.470036] Hardware name: Microsoft Corporation Virtual Machine/Virtual Machine, BIOS 090006 05/23/2012 [ 23.470036] task: ffff880036ef1cb0 ti: ffff880036ce8000 task.ti: ffff880036ce8000 [ 23.470036] RIP: 0010:[] [] __kmalloc_node_track_caller+0xdb/0x1e0 [ 23.470036] RSP: 0018:ffff880036cebcc8 EFLAGS: 00010246 ... When this crash does not happen on driver unload the similar one is expected if we try to load hv_vmbus again. Remove kfree from vmbus_free_channels() as freeing it from vmbus_device_release() seems right. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index ba4b25f0b213..36bacc75a32a 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -262,7 +262,6 @@ void vmbus_free_channels(void) list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { vmbus_device_unregister(channel->device_obj); - kfree(channel->device_obj); free_channel(channel); } } From 09a196288ec4617a920e051af6651ce03968c8b9 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 27 Feb 2015 11:25:54 -0800 Subject: [PATCH 008/133] Drivers: hv: vmbus: teardown hv_vmbus_con workqueue and vmbus_connection pages on shutdown We need to destroy hv_vmbus_con on module shutdown, otherwise the following crash is sometimes observed: [ 76.569845] hv_vmbus: Hyper-V Host Build:9600-6.3-17-0.17039; Vmbus version:3.0 [ 82.598859] BUG: unable to handle kernel paging request at ffffffffa0003480 [ 82.599287] IP: [] 0xffffffffa0003480 [ 82.599287] PGD 1f34067 PUD 1f35063 PMD 3f72d067 PTE 0 [ 82.599287] Oops: 0010 [#1] SMP [ 82.599287] Modules linked in: [last unloaded: hv_vmbus] [ 82.599287] CPU: 0 PID: 26 Comm: kworker/0:1 Not tainted 3.19.0-rc5_bug923184+ #488 [ 82.599287] Hardware name: Microsoft Corporation Virtual Machine/Virtual Machine, BIOS Hyper-V UEFI Release v1.0 11/26/2012 [ 82.599287] Workqueue: hv_vmbus_con 0xffffffffa0003480 [ 82.599287] task: ffff88007b6ddfa0 ti: ffff88007f8f8000 task.ti: ffff88007f8f8000 [ 82.599287] RIP: 0010:[] [] 0xffffffffa0003480 [ 82.599287] RSP: 0018:ffff88007f8fbe00 EFLAGS: 00010202 ... To avoid memory leaks we need to free monitor_pages and int_page for vmbus_connection. Implement vmbus_disconnect() function by separating cleanup path from vmbus_connect(). As we use hv_vmbus_con to release channels (see free_channel() in channel_mgmt.c) we need to make sure the work was done before we remove the queue, do that with drain_workqueue(). We also need to avoid handling messages which can (potentially) create new channels, so set vmbus_connection.conn_state = DISCONNECTED at the very beginning of vmbus_exit() and check for that in vmbus_onmessage_work(). Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 19 +++++++++++++------ drivers/hv/hyperv_vmbus.h | 1 + drivers/hv/vmbus_drv.c | 6 ++++++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index a63a795300b9..c4acd1ce7c0c 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -216,10 +216,21 @@ int vmbus_connect(void) cleanup: pr_err("Unable to connect to host\n"); - vmbus_connection.conn_state = DISCONNECTED; - if (vmbus_connection.work_queue) + vmbus_connection.conn_state = DISCONNECTED; + vmbus_disconnect(); + + kfree(msginfo); + + return ret; +} + +void vmbus_disconnect(void) +{ + if (vmbus_connection.work_queue) { + drain_workqueue(vmbus_connection.work_queue); destroy_workqueue(vmbus_connection.work_queue); + } if (vmbus_connection.int_page) { free_pages((unsigned long)vmbus_connection.int_page, 0); @@ -230,10 +241,6 @@ cleanup: free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0); vmbus_connection.monitor_pages[0] = NULL; vmbus_connection.monitor_pages[1] = NULL; - - kfree(msginfo); - - return ret; } /* diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 44b1c9424712..6cf2de9f487a 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -692,6 +692,7 @@ void vmbus_free_channels(void); /* Connection interface */ int vmbus_connect(void); +void vmbus_disconnect(void); int vmbus_post_msg(void *buffer, size_t buflen); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index e334ccc70582..76db97de09fd 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -574,6 +574,10 @@ static void vmbus_onmessage_work(struct work_struct *work) { struct onmessage_work_context *ctx; + /* Do not process messages if we're in DISCONNECTED state */ + if (vmbus_connection.conn_state == DISCONNECTED) + return; + ctx = container_of(work, struct onmessage_work_context, work); vmbus_onmessage(&ctx->msg); @@ -1025,12 +1029,14 @@ cleanup: static void __exit vmbus_exit(void) { + vmbus_connection.conn_state = DISCONNECTED; hv_remove_vmbus_irq(); vmbus_free_channels(); bus_unregister(&hv_bus); hv_cleanup(); acpi_bus_unregister_driver(&vmbus_acpi_driver); hv_cpu_hotplug_quirk(false); + vmbus_disconnect(); } From e72e7ac583ee8adddbc668cdefde7d7d3021be79 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 27 Feb 2015 11:25:55 -0800 Subject: [PATCH 009/133] drivers: hv: vmbus: Teardown synthetic interrupt controllers on module unload SynIC has to be switched off when we unload the module, otherwise registered memory pages can get corrupted after (as Hyper-V host still writes there) and we see the following crashes for random processes: [ 89.116774] BUG: Bad page map in process sh pte:4989c716 pmd:36f81067 [ 89.159454] addr:0000000000437000 vm_flags:00000875 anon_vma: (null) mapping:ffff88007bba55a0 index:37 [ 89.226146] vma->vm_ops->fault: filemap_fault+0x0/0x410 [ 89.257776] vma->vm_file->f_op->mmap: generic_file_mmap+0x0/0x60 [ 89.297570] CPU: 0 PID: 215 Comm: sh Tainted: G B 3.19.0-rc5_bug923184+ #488 [ 89.353738] Hardware name: Microsoft Corporation Virtual Machine/Virtual Machine, BIOS 090006 05/23/2012 [ 89.409138] 0000000000000000 000000004e083d7b ffff880036e9fa18 ffffffff81a68d31 [ 89.468724] 0000000000000000 0000000000437000 ffff880036e9fa68 ffffffff811a1e3a [ 89.519233] 000000004989c716 0000000000000037 ffffea0001edc340 0000000000437000 [ 89.575751] Call Trace: [ 89.591060] [] dump_stack+0x45/0x57 [ 89.625164] [] print_bad_pte+0x1aa/0x250 [ 89.667234] [] vm_normal_page+0x55/0xa0 [ 89.703818] [] unmap_page_range+0x425/0x8a0 [ 89.737982] [] unmap_single_vma+0x81/0xf0 [ 89.780385] [] ? lru_deactivate_fn+0x190/0x190 [ 89.820130] [] unmap_vmas+0x51/0xa0 [ 89.860168] [] exit_mmap+0xac/0x1a0 [ 89.890588] [] mmput+0x63/0x100 [ 89.919205] [] flush_old_exec+0x3f8/0x8b0 [ 89.962135] [] load_elf_binary+0x32b/0x1260 [ 89.998581] [] ? get_user_pages+0x52/0x60 hv_synic_cleanup() function exists but noone calls it now. Do the following: - call hv_synic_cleanup() on each cpu from vmbus_exit(); - write global disable bit through MSR; - use hv_synic_free_cpu() to avoid memory leask and code duplication. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 9 +++++++-- drivers/hv/vmbus_drv.c | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 50e51a51ff8b..39531dcf582f 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -477,6 +477,7 @@ void hv_synic_cleanup(void *arg) union hv_synic_sint shared_sint; union hv_synic_simp simp; union hv_synic_siefp siefp; + union hv_synic_scontrol sctrl; int cpu = smp_processor_id(); if (!hv_context.synic_initialized) @@ -502,6 +503,10 @@ void hv_synic_cleanup(void *arg) wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); - free_page((unsigned long)hv_context.synic_message_page[cpu]); - free_page((unsigned long)hv_context.synic_event_page[cpu]); + /* Disable the global synic bit */ + rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); + sctrl.enable = 0; + wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); + + hv_synic_free_cpu(cpu); } diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 76db97de09fd..d8233d4513a5 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1029,11 +1029,15 @@ cleanup: static void __exit vmbus_exit(void) { + int cpu; + vmbus_connection.conn_state = DISCONNECTED; hv_remove_vmbus_irq(); vmbus_free_channels(); bus_unregister(&hv_bus); hv_cleanup(); + for_each_online_cpu(cpu) + smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); acpi_bus_unregister_driver(&vmbus_acpi_driver); hv_cpu_hotplug_quirk(false); vmbus_disconnect(); From 32a158325acf12842764b1681f53903673f2f22e Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 27 Feb 2015 11:25:56 -0800 Subject: [PATCH 010/133] clockevents: export clockevents_unbind_device instead of clockevents_unbind It looks like clockevents_unbind is being exported by mistake as: - it is static; - it is not listed in include/linux/clockchips.h; - EXPORT_SYMBOL_GPL(clockevents_unbind) follows clockevents_unbind_device() implementation. I think clockevents_unbind_device should be exported instead. This is going to be used to teardown Hyper-V clockevent devices on module unload. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- kernel/time/clockevents.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 55449909f114..888ecc114ddc 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -371,7 +371,7 @@ int clockevents_unbind_device(struct clock_event_device *ced, int cpu) mutex_unlock(&clockevents_mutex); return ret; } -EXPORT_SYMBOL_GPL(clockevents_unbind); +EXPORT_SYMBOL_GPL(clockevents_unbind_device); /** * clockevents_register_device - register a clock event device From e086748c655ab99bac91b87d1bb59d9cc45867b9 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 27 Feb 2015 11:25:57 -0800 Subject: [PATCH 011/133] Drivers: hv: vmbus: Teardown clockevent devices on module unload Newly introduced clockevent devices made it impossible to unload hv_vmbus module as clockevents_config_and_register() takes additional reverence to the module. To make it possible again we do the following: - avoid setting dev->owner for clockevent devices; - implement hv_synic_clockevents_cleanup() doing clockevents_unbind_device(); - call it from vmbus_exit(). In theory hv_synic_clockevents_cleanup() can be merged with hv_synic_cleanup(), however, we call hv_synic_cleanup() from smp_call_function_single() and this doesn't work for clockevents_unbind_device() as it does such call on its own. I opted for a separate function. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 25 ++++++++++++++++++++++++- drivers/hv/hyperv_vmbus.h | 2 ++ drivers/hv/vmbus_drv.c | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 39531dcf582f..d3943bceecc3 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -312,7 +312,11 @@ static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu) dev->features = CLOCK_EVT_FEAT_ONESHOT; dev->cpumask = cpumask_of(cpu); dev->rating = 1000; - dev->owner = THIS_MODULE; + /* + * Avoid settint dev->owner = THIS_MODULE deliberately as doing so will + * result in clockevents_config_and_register() taking additional + * references to the hv_vmbus module making it impossible to unload. + */ dev->set_mode = hv_ce_setmode; dev->set_next_event = hv_ce_set_next_event; @@ -469,6 +473,20 @@ void hv_synic_init(void *arg) return; } +/* + * hv_synic_clockevents_cleanup - Cleanup clockevent devices + */ +void hv_synic_clockevents_cleanup(void) +{ + int cpu; + + if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE)) + return; + + for_each_online_cpu(cpu) + clockevents_unbind_device(hv_context.clk_evt[cpu], cpu); +} + /* * hv_synic_cleanup - Cleanup routine for hv_synic_init(). */ @@ -483,6 +501,11 @@ void hv_synic_cleanup(void *arg) if (!hv_context.synic_initialized) return; + /* Turn off clockevent device */ + if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE) + hv_ce_setmode(CLOCK_EVT_MODE_SHUTDOWN, + hv_context.clk_evt[cpu]); + rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); shared_sint.masked = 1; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 6cf2de9f487a..b055e53bbc6e 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -572,6 +572,8 @@ extern void hv_synic_init(void *irqarg); extern void hv_synic_cleanup(void *arg); +extern void hv_synic_clockevents_cleanup(void); + /* * Host version information. */ diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index d8233d4513a5..6d99aa5c49d6 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1032,6 +1032,7 @@ static void __exit vmbus_exit(void) int cpu; vmbus_connection.conn_state = DISCONNECTED; + hv_synic_clockevents_cleanup(); hv_remove_vmbus_irq(); vmbus_free_channels(); bus_unregister(&hv_bus); From 1896566372579939eb86414ea3d664f18d5e909c Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Fri, 27 Feb 2015 11:25:58 -0800 Subject: [PATCH 012/133] hv: hv_util: move vmbus_open() to a later place Before the line vmbus_open() returns, srv->util_cb can be already running and the variables, like util_fw_version, are needed by the srv->util_cb. So we have to make sure the variables are initialized before the vmbus_open(). CC: "K. Y. Srinivasan" Reviewed-by: Vitaly Kuznetsov Reviewed-by: Jason Wang Signed-off-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_util.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 3b9c9ef0deb8..c5be7737559f 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -340,12 +340,8 @@ static int util_probe(struct hv_device *dev, set_channel_read_state(dev->channel, false); - ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, - srv->util_cb, dev->channel); - if (ret) - goto error; - hv_set_drvdata(dev, srv); + /* * Based on the host; initialize the framework and * service version numbers we will negotiate. @@ -365,6 +361,11 @@ static int util_probe(struct hv_device *dev, hb_srv_version = HB_VERSION; } + ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, + srv->util_cb, dev->channel); + if (ret) + goto error; + return 0; error: From 89f9f6796d41e10e224b0cb0027ddd78cb881f65 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Fri, 27 Feb 2015 11:25:59 -0800 Subject: [PATCH 013/133] hv: vmbus_post_msg: retry the hypercall on some transient errors I got HV_STATUS_INVALID_CONNECTION_ID on Hyper-V 2008 R2 when keeping running "rmmod hv_netvsc; modprobe hv_netvsc; rmmod hv_utils; modprobe hv_utils" in a Linux guest. Looks the host has some kind of throttling mechanism if some kinds of hypercalls are sent too frequently. Without the patch, the driver can occasionally fail to load. Also let's retry HV_STATUS_INSUFFICIENT_MEMORY, though we didn't get it before. Removed 'case -ENOMEM', since the hypervisor doesn't return this. CC: "K. Y. Srinivasan" Reviewed-by: Jason Wang Signed-off-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/uapi/asm/hyperv.h | 2 ++ drivers/hv/connection.c | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/uapi/asm/hyperv.h b/arch/x86/include/uapi/asm/hyperv.h index 90c458e66e13..ce6068dbcfbc 100644 --- a/arch/x86/include/uapi/asm/hyperv.h +++ b/arch/x86/include/uapi/asm/hyperv.h @@ -225,6 +225,8 @@ #define HV_STATUS_INVALID_HYPERCALL_CODE 2 #define HV_STATUS_INVALID_HYPERCALL_INPUT 3 #define HV_STATUS_INVALID_ALIGNMENT 4 +#define HV_STATUS_INSUFFICIENT_MEMORY 11 +#define HV_STATUS_INVALID_CONNECTION_ID 18 #define HV_STATUS_INSUFFICIENT_BUFFERS 19 typedef struct _HV_REFERENCE_TSC_PAGE { diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index c4acd1ce7c0c..af2388fdc6b9 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -440,9 +440,16 @@ int vmbus_post_msg(void *buffer, size_t buflen) ret = hv_post_message(conn_id, 1, buffer, buflen); switch (ret) { + case HV_STATUS_INVALID_CONNECTION_ID: + /* + * We could get this if we send messages too + * frequently. + */ + ret = -EAGAIN; + break; + case HV_STATUS_INSUFFICIENT_MEMORY: case HV_STATUS_INSUFFICIENT_BUFFERS: ret = -ENOMEM; - case -ENOMEM: break; case HV_STATUS_SUCCESS: return ret; @@ -452,7 +459,7 @@ int vmbus_post_msg(void *buffer, size_t buflen) } retries++; - msleep(100); + msleep(1000); } return ret; } From ac0d12b7cee73b4b4b769ea58c62ec7042c6be13 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Fri, 27 Feb 2015 11:26:00 -0800 Subject: [PATCH 014/133] hv: vmbus_open(): reset the channel state on ENOMEM Without this patch, the state is put to CHANNEL_OPENING_STATE, and when the driver is loaded next time, vmbus_open() will fail immediately due to newchannel->state != CHANNEL_OPEN_STATE. CC: "K. Y. Srinivasan" Signed-off-by: Dexuan Cui Reviewed-by: Jason Wang Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 2978f5ee8d2a..26dcf268b093 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -89,9 +89,10 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, get_order(send_ringbuffer_size + recv_ringbuffer_size)); - if (!out) - return -ENOMEM; - + if (!out) { + err = -ENOMEM; + goto error0; + } in = (void *)((unsigned long)out + send_ringbuffer_size); @@ -199,6 +200,7 @@ error0: free_pages((unsigned long)out, get_order(send_ringbuffer_size + recv_ringbuffer_size)); kfree(open_info); + newchannel->state = CHANNEL_OPEN_STATE; return err; } EXPORT_SYMBOL_GPL(vmbus_open); From 08a9513f7417d6dff9a1ab0c163900f503ff84eb Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Fri, 27 Feb 2015 11:26:01 -0800 Subject: [PATCH 015/133] hv: channel: match var type to return type of wait_for_completion return type of wait_for_completion_timeout is unsigned long not int, this patch changes the type of t from int to unsigned long. Signed-off-by: Nicholas Mc Guire Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 26dcf268b093..f6879950e131 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -71,7 +71,8 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, struct vmbus_channel_msginfo *open_info = NULL; void *in, *out; unsigned long flags; - int ret, t, err = 0; + int ret, err = 0; + unsigned long t; spin_lock_irqsave(&newchannel->lock, flags); if (newchannel->state == CHANNEL_OPEN_STATE) { From 51e5181d20086c0b26e871deffeff3ff47cf5c4b Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Fri, 27 Feb 2015 11:26:02 -0800 Subject: [PATCH 016/133] hv: channel_mgmt: match var type to return type of wait_for_completion return type of wait_for_completion_timeout is unsigned long not int, this patch changes the type of t from int to unsigned long. Signed-off-by: Nicholas Mc Guire Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 36bacc75a32a..6be93f09f976 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -789,7 +789,8 @@ int vmbus_request_offers(void) { struct vmbus_channel_message_header *msg; struct vmbus_channel_msginfo *msginfo; - int ret, t; + int ret; + unsigned long t; msginfo = kmalloc(sizeof(*msginfo) + sizeof(struct vmbus_channel_message_header), From b057b3ad16e1c6e21e234cd7f5c61c525da97f17 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Fri, 27 Feb 2015 11:26:03 -0800 Subject: [PATCH 017/133] hv: hv_balloon: match var type to return type of wait_for_completion return type of wait_for_completion_timeout is unsigned long not int, this patch changes the type of t from int to unsigned long. Signed-off-by: Nicholas Mc Guire Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index ff169386b2c7..965c37aab277 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1414,7 +1414,8 @@ static void balloon_onchannelcallback(void *context) static int balloon_probe(struct hv_device *dev, const struct hv_vmbus_device_id *dev_id) { - int ret, t; + int ret; + unsigned long t; struct dm_version_request version_req; struct dm_capabilities cap_msg; From 40384e4bbeb9f2651fe9bffc0062d9f31ef625bf Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 27 Feb 2015 11:26:04 -0800 Subject: [PATCH 018/133] Drivers: hv: vmbus: Fix a bug in the error path in vmbus_open() Correctly rollback state if the failure occurs after we have handed over the ownership of the buffer to the host. Signed-off-by: K. Y. Srinivasan Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index f6879950e131..bf0cf8f3bcaf 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -137,7 +137,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, GFP_KERNEL); if (!open_info) { err = -ENOMEM; - goto error0; + goto error_gpadl; } init_completion(&open_info->waitevent); @@ -153,7 +153,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, if (userdatalen > MAX_USER_DEFINED_BYTES) { err = -EINVAL; - goto error0; + goto error_gpadl; } if (userdatalen) @@ -197,6 +197,9 @@ error1: list_del(&open_info->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); +error_gpadl: + vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle); + error0: free_pages((unsigned long)out, get_order(send_ringbuffer_size + recv_ringbuffer_size)); From 04653a009a63d6a91c60a5449ea97e3ed5e1dc29 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 27 Feb 2015 11:26:05 -0800 Subject: [PATCH 019/133] Drivers: hv: vmbus: Add support for the NetworkDirect GUID NetworkDirect is a service that supports guest RDMA. Define the GUID for this service. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 2 ++ include/linux/hyperv.h | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 6be93f09f976..0ba6b5c303e6 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -413,6 +413,8 @@ static const struct hv_vmbus_device_id hp_devs[] = { { HV_SCSI_GUID, }, /* Network */ { HV_NIC_GUID, }, + /* NetworkDirect Guest RDMA */ + { HV_ND_GUID, }, }; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 26a32b771ee5..7d976ac01fac 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1109,6 +1109,16 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver); 0x9A, 0xE7, 0x6B, 0x17, 0x49, 0x77, 0xC1, 0x92 \ } +/* + * NetworkDirect. This is the guest RDMA service. + * {8c2eaf3d-32a7-4b09-ab99-bd1f1c86b501} + */ +#define HV_ND_GUID \ + .guid = { \ + 0x3d, 0xaf, 0x2e, 0x8c, 0xa7, 0x32, 0x09, 0x4b, \ + 0xab, 0x99, 0xbd, 0x1f, 0x1c, 0x86, 0xb5, 0x01 \ + } + /* * Common header for Hyper-V ICs */ From d15a0301c4157884d1a48a5d76b9ac3e36d71242 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:18:16 -0800 Subject: [PATCH 020/133] Drivers: hv: vmbus: Properly handle child device remove Handle the case when the device may be removed when the device has no driver attached to it. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 6d99aa5c49d6..a12666d075ea 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -508,14 +508,17 @@ static int vmbus_probe(struct device *child_device) */ static int vmbus_remove(struct device *child_device) { - struct hv_driver *drv = drv_to_hv_drv(child_device->driver); + struct hv_driver *drv; struct hv_device *dev = device_to_hv_device(child_device); - if (drv->remove) - drv->remove(dev); - else - pr_err("remove not set for driver %s\n", - dev_name(child_device)); + if (child_device->driver) { + drv = drv_to_hv_drv(child_device->driver); + if (drv->remove) + drv->remove(dev); + else + pr_err("remove not set for driver %s\n", + dev_name(child_device)); + } return 0; } From ed6cfcc5fdf2ebca320b6f74c836e555e18216e1 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:18:17 -0800 Subject: [PATCH 021/133] Drivers: hv: vmbus: Introduce a function to remove a rescinded offer In response to a rescind message, we need to remove the channel and the corresponding device. Cleanup this code path by factoring out the code to remove a channel. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 9 +++++++ drivers/hv/channel_mgmt.c | 49 ++++++++++++++++++++++++--------------- drivers/hv/vmbus_drv.c | 11 ++++++++- include/linux/hyperv.h | 1 + 4 files changed, 50 insertions(+), 20 deletions(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index bf0cf8f3bcaf..9b79aca7e565 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -501,6 +501,15 @@ static int vmbus_close_internal(struct vmbus_channel *channel) put_cpu(); } + /* + * If the channel has been rescinded; process device removal. + */ + if (channel->rescind) { + hv_process_channel_removal(channel, + channel->offermsg.child_relid); + return 0; + } + /* Send a closing message */ msg = &channel->close_msg.msg; diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 0ba6b5c303e6..b93389124ec4 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -207,33 +207,21 @@ static void percpu_channel_deq(void *arg) list_del(&channel->percpu_list); } -/* - * vmbus_process_rescind_offer - - * Rescind the offer by initiating a device removal - */ -static void vmbus_process_rescind_offer(struct work_struct *work) + +void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) { - struct vmbus_channel *channel = container_of(work, - struct vmbus_channel, - work); + struct vmbus_channel_relid_released msg; unsigned long flags; struct vmbus_channel *primary_channel; - struct vmbus_channel_relid_released msg; - struct device *dev; - - if (channel->device_obj) { - dev = get_device(&channel->device_obj->device); - if (dev) { - vmbus_device_unregister(channel->device_obj); - put_device(dev); - } - } memset(&msg, 0, sizeof(struct vmbus_channel_relid_released)); - msg.child_relid = channel->offermsg.child_relid; + msg.child_relid = relid; msg.header.msgtype = CHANNELMSG_RELID_RELEASED; vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); + if (channel == NULL) + return; + if (channel->target_cpu != get_cpu()) { put_cpu(); smp_call_function_single(channel->target_cpu, @@ -256,6 +244,29 @@ static void vmbus_process_rescind_offer(struct work_struct *work) free_channel(channel); } +/* + * vmbus_process_rescind_offer - + * Rescind the offer by initiating a device removal + */ +static void vmbus_process_rescind_offer(struct work_struct *work) +{ + struct vmbus_channel *channel = container_of(work, + struct vmbus_channel, + work); + struct device *dev; + + if (channel->device_obj) { + dev = get_device(&channel->device_obj->device); + if (dev) { + vmbus_device_unregister(channel->device_obj); + put_device(dev); + } + } else { + hv_process_channel_removal(channel, + channel->offermsg.child_relid); + } +} + void vmbus_free_channels(void) { struct vmbus_channel *channel; diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index a12666d075ea..2b7b51d264f1 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -510,14 +510,23 @@ static int vmbus_remove(struct device *child_device) { struct hv_driver *drv; struct hv_device *dev = device_to_hv_device(child_device); + u32 relid = dev->channel->offermsg.child_relid; if (child_device->driver) { drv = drv_to_hv_drv(child_device->driver); if (drv->remove) drv->remove(dev); - else + else { + hv_process_channel_removal(dev->channel, relid); pr_err("remove not set for driver %s\n", dev_name(child_device)); + } + } else { + /* + * We don't have a driver for this device; deal with the + * rescind message by removing the channel. + */ + hv_process_channel_removal(dev->channel, relid); } return 0; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 7d976ac01fac..dd92a854c700 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1226,6 +1226,7 @@ void hv_kvp_onchannelcallback(void *); int hv_vss_init(struct hv_util_service *); void hv_vss_deinit(void); void hv_vss_onchannelcallback(void *); +void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid); extern struct resource hyperv_mmio; From 2dd37cb81580dce6dfb8c5a7d5c37b904a188ae7 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:18:18 -0800 Subject: [PATCH 022/133] Drivers: hv: vmbus: Handle both rescind and offer messages in the same context Execute both ressind and offer messages in the same work context. This serializes these operations and naturally addresses the various corner cases. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 113 ++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 52 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index b93389124ec4..f8528e1e3a77 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -134,6 +134,34 @@ fw_error: EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); +static void vmbus_process_device_unregister(struct work_struct *work) +{ + struct device *dev; + struct vmbus_channel *channel = container_of(work, + struct vmbus_channel, + work); + + dev = get_device(&channel->device_obj->device); + if (dev) { + vmbus_device_unregister(channel->device_obj); + put_device(dev); + } +} + +static void vmbus_sc_creation_cb(struct work_struct *work) +{ + struct vmbus_channel *newchannel = container_of(work, + struct vmbus_channel, + work); + struct vmbus_channel *primary_channel = newchannel->primary_channel; + + /* + * On entry sc_creation_callback has been already verified to + * be non-NULL. + */ + primary_channel->sc_creation_callback(newchannel); +} + /* * alloc_channel - Allocate and initialize a vmbus channel object */ @@ -244,29 +272,6 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) free_channel(channel); } -/* - * vmbus_process_rescind_offer - - * Rescind the offer by initiating a device removal - */ -static void vmbus_process_rescind_offer(struct work_struct *work) -{ - struct vmbus_channel *channel = container_of(work, - struct vmbus_channel, - work); - struct device *dev; - - if (channel->device_obj) { - dev = get_device(&channel->device_obj->device); - if (dev) { - vmbus_device_unregister(channel->device_obj); - put_device(dev); - } - } else { - hv_process_channel_removal(channel, - channel->offermsg.child_relid); - } -} - void vmbus_free_channels(void) { struct vmbus_channel *channel; @@ -281,11 +286,8 @@ void vmbus_free_channels(void) * vmbus_process_offer - Process the offer by creating a channel/device * associated with this offer */ -static void vmbus_process_offer(struct work_struct *work) +static void vmbus_process_offer(struct vmbus_channel *newchannel) { - struct vmbus_channel *newchannel = container_of(work, - struct vmbus_channel, - work); struct vmbus_channel *channel; bool fnew = true; bool enq = false; @@ -349,9 +351,19 @@ static void vmbus_process_offer(struct work_struct *work) newchannel->state = CHANNEL_OPEN_STATE; if (channel->sc_creation_callback != NULL) - channel->sc_creation_callback(newchannel); + /* + * We need to invoke the sub-channel creation + * callback; invoke this in a seperate work + * context since we are currently running on + * the global work context in which we handle + * messages from the host. + */ + INIT_WORK(&newchannel->work, + vmbus_sc_creation_cb); + queue_work(newchannel->controlwq, + &newchannel->work); - goto done_init_rescind; + return; } goto err_free_chan; @@ -392,15 +404,9 @@ static void vmbus_process_offer(struct work_struct *work) kfree(newchannel->device_obj); 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); } @@ -526,8 +532,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) newchannel->monitor_grp = (u8)offer->monitorid / 32; newchannel->monitor_bit = (u8)offer->monitorid % 32; - INIT_WORK(&newchannel->work, vmbus_process_offer); - queue_work(newchannel->controlwq, &newchannel->work); + vmbus_process_offer(newchannel); } /* @@ -544,24 +549,28 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) rescind = (struct vmbus_channel_rescind_offer *)hdr; channel = relid2channel(rescind->child_relid); - if (channel == NULL) - /* Just return here, no channel found */ + if (channel == NULL) { + hv_process_channel_removal(NULL, rescind->child_relid); 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); - spin_unlock_irqrestore(&channel->lock, flags); + + if (channel->device_obj) { + /* + * We will have to unregister this device from the + * driver core. Do this in the per-channel work context. + * Note that we are currently executing on the global + * workq for handling messages from the host. + */ + INIT_WORK(&channel->work, vmbus_process_device_unregister); + queue_work(channel->controlwq, &channel->work); + } else { + hv_process_channel_removal(channel, + channel->offermsg.child_relid); + } } /* From 5b1e5b530753e660080d402b81b88684893157d5 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:18:19 -0800 Subject: [PATCH 023/133] Drivers: hv: vmbus: Remove the channel from the channel list(s) on failure Properly rollback state in vmbus_pocess_offer() in the failure paths. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index f8528e1e3a77..b1e5a5fdaf7f 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -386,7 +386,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) &newchannel->offermsg.offer.if_instance, newchannel); if (!newchannel->device_obj) - goto err_free_chan; + goto err_deq_chan; /* * Add the new device to the bus. This will kick off device-driver @@ -398,15 +398,26 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) pr_err("unable to add child device object (relid %d)\n", newchannel->offermsg.child_relid); - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - list_del(&newchannel->listentry); - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); kfree(newchannel->device_obj); - goto err_free_chan; + goto err_deq_chan; } return; +err_deq_chan: + spin_lock_irqsave(&vmbus_connection.channel_lock, flags); + list_del(&newchannel->listentry); + spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + + if (newchannel->target_cpu != get_cpu()) { + put_cpu(); + smp_call_function_single(newchannel->target_cpu, + percpu_channel_deq, newchannel, true); + } else { + percpu_channel_deq(newchannel); + put_cpu(); + } + err_free_chan: free_channel(newchannel); } From 5380b383ba10915e61e3db09341cf8193f0601ad Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:18:20 -0800 Subject: [PATCH 024/133] Drivers: hv: util: On device remove, close the channel after de-initializing the service When the offer is rescinded, vmbus_close() can free up the channel; deinitialize the service before closing the channel. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index c5be7737559f..7994ec2e4151 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -380,9 +380,9 @@ static int util_remove(struct hv_device *dev) { struct hv_util_service *srv = hv_get_drvdata(dev); - vmbus_close(dev->channel); if (srv->util_deinit) srv->util_deinit(); + vmbus_close(dev->channel); kfree(srv->recv_buffer); return 0; From 37f492ce8148d84261cdb9fe1e925199f47531ae Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:18:21 -0800 Subject: [PATCH 025/133] Drivers: hv: vmbus: Get rid of some unnecessary messages Currently we log messages when either we are not able to map an ID to a channel or when the channel does not have a callback associated (in the channel interrupt handling path). These messages don't add any value, get rid of them. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index af2388fdc6b9..583d7d42b46d 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -318,10 +318,8 @@ static void process_chn_event(u32 relid) */ channel = pcpu_relid2channel(relid); - if (!channel) { - pr_err("channel not found for relid - %u\n", relid); + if (!channel) return; - } /* * A channel once created is persistent even when there @@ -356,10 +354,7 @@ static void process_chn_event(u32 relid) else bytes_to_read = 0; } while (read_state && (bytes_to_read != 0)); - } else { - pr_err("no channel callback for relid - %u\n", relid); } - } /* From b05d8d9ef5ef21d1b18440430f950304836e1aaa Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Sat, 28 Feb 2015 11:38:58 -0800 Subject: [PATCH 026/133] Drivers: hv: hv_balloon: eliminate the trylock path in acquire/release_region_mutex When many memory regions are being added and automatically onlined the following lockup is sometimes observed: INFO: task udevd:1872 blocked for more than 120 seconds. ... Call Trace: [] schedule_timeout+0x22c/0x350 [] wait_for_common+0x10f/0x160 [] ? default_wake_function+0x0/0x20 [] wait_for_completion+0x1d/0x20 [] hv_memory_notifier+0xdc/0x120 [] notifier_call_chain+0x4c/0x70 ... When several memory blocks are going online simultaneously we got several hv_memory_notifier() trying to acquire the ha_region_mutex. When this mutex is being held by hot_add_req() all these competing acquire_region_mutex() do mutex_trylock, fail, and queue themselves into wait_for_completion(..). However when we do complete() from release_region_mutex() only one of them wakes up. This could be solved by changing complete() -> complete_all() memory onlining can be delayed as well, in that case we can still get several hv_memory_notifier() runners at the same time trying to grab the mutex. Only one of them will succeed and the others will hang for forever as complete() is not being called. We don't see this issue often because we have 5sec onlining timeout in hv_mem_hot_add() and usually all udev events arrive in this time frame. Get rid of the trylock path, waiting on the mutex is supposed to provide the required serialization. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 965c37aab277..ea3d33cdf99c 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -534,7 +534,6 @@ struct hv_dynmem_device { struct task_struct *thread; struct mutex ha_region_mutex; - struct completion waiter_event; /* * A list of hot-add regions. @@ -554,38 +553,17 @@ 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); + mutex_lock(&dm_device.ha_region_mutex); break; case MEM_ONLINE: case MEM_CANCEL_ONLINE: - release_region_mutex(true); + mutex_unlock(&dm_device.ha_region_mutex); if (dm_device.ha_waiting) { dm_device.ha_waiting = false; complete(&dm_device.ol_waitevent); @@ -646,7 +624,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); + mutex_unlock(&dm_device.ha_region_mutex); nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn)); ret = add_memory(nid, PFN_PHYS((start_pfn)), (HA_CHUNK << PAGE_SHIFT)); @@ -675,7 +653,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); + mutex_lock(&dm_device.ha_region_mutex); post_status(&dm_device); } @@ -886,7 +864,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); + mutex_lock(&dm_device.ha_region_mutex); pg_start = dm->ha_wrk.ha_page_range.finfo.start_page; pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt; @@ -918,7 +896,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); + mutex_unlock(&dm_device.ha_region_mutex); #endif /* * The result field of the response structure has the @@ -1440,7 +1418,6 @@ 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); From 549fd280b145e2154db5a7d06981d041f8bbf597 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Sat, 28 Feb 2015 11:38:59 -0800 Subject: [PATCH 027/133] Drivers: hv: hv_balloon: report offline pages as being used When hot-added memory pages are not brought online or when some memory blocks are sent offline the subsequent ballooning process kills the guest with OOM killer. This happens as we don't report these pages as neither used nor free and apparently host algorithm considers them as being unused. Keep track of all online/offline operations and report all currently offline pages as being used so host won't try to balloon them out. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index ea3d33cdf99c..53932b3e1973 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -503,6 +503,8 @@ struct hv_dynmem_device { * Number of pages we have currently ballooned out. */ unsigned int num_pages_ballooned; + unsigned int num_pages_onlined; + unsigned int num_pages_added; /* * State to manage the ballooning (up) operation. @@ -556,12 +558,15 @@ static void post_status(struct hv_dynmem_device *dm); static int hv_memory_notifier(struct notifier_block *nb, unsigned long val, void *v) { + struct memory_notify *mem = (struct memory_notify *)v; + switch (val) { case MEM_GOING_ONLINE: mutex_lock(&dm_device.ha_region_mutex); break; case MEM_ONLINE: + dm_device.num_pages_onlined += mem->nr_pages; case MEM_CANCEL_ONLINE: mutex_unlock(&dm_device.ha_region_mutex); if (dm_device.ha_waiting) { @@ -570,8 +575,12 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val, } break; - case MEM_GOING_OFFLINE: case MEM_OFFLINE: + mutex_lock(&dm_device.ha_region_mutex); + dm_device.num_pages_onlined -= mem->nr_pages; + mutex_unlock(&dm_device.ha_region_mutex); + break; + case MEM_GOING_OFFLINE: case MEM_CANCEL_OFFLINE: break; } @@ -896,6 +905,8 @@ 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); + + dm->num_pages_added += resp.page_count; mutex_unlock(&dm_device.ha_region_mutex); #endif /* @@ -1009,17 +1020,21 @@ static void post_status(struct hv_dynmem_device *dm) status.hdr.trans_id = atomic_inc_return(&trans_id); /* - * The host expects the guest to report free memory. - * Further, the host expects the pressure information to - * include the ballooned out pages. - * For a given amount of memory that we are managing, we - * need to compute a floor below which we should not balloon. - * Compute this and add it to the pressure report. + * The host expects the guest to report free and committed memory. + * Furthermore, the host expects the pressure information to include + * the ballooned out pages. For a given amount of memory that we are + * managing we need to compute a floor below which we should not + * balloon. Compute this and add it to the pressure report. + * We also need to report all offline pages (num_pages_added - + * num_pages_onlined) as committed to the host, otherwise it can try + * asking us to balloon them out. */ status.num_avail = val.freeram; status.num_committed = vm_memory_committed() + - dm->num_pages_ballooned + - compute_balloon_floor(); + dm->num_pages_ballooned + + (dm->num_pages_added > dm->num_pages_onlined ? + dm->num_pages_added - dm->num_pages_onlined : 0) + + compute_balloon_floor(); /* * If our transaction ID is no longer current, just don't From 530d15b907038735d4cb008dc5caae7ce4dfc985 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Sat, 28 Feb 2015 11:39:00 -0800 Subject: [PATCH 028/133] Drivers: hv: hv_balloon: refuse to balloon below the floor When host asks us to balloon up we need to be sure we're not committing suicide by overballooning. Use already existent 'floor' metric as our lowest possible value for free ram. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 53932b3e1973..c5bb87244dd7 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1138,6 +1138,8 @@ static void balloon_up(struct work_struct *dummy) bool alloc_error; bool done = false; int i; + struct sysinfo val; + unsigned long floor; /* The host balloons pages in 2M granularity. */ WARN_ON_ONCE(num_pages % PAGES_IN_2M != 0); @@ -1148,6 +1150,15 @@ static void balloon_up(struct work_struct *dummy) */ alloc_unit = 512; + si_meminfo(&val); + floor = compute_balloon_floor(); + + /* Refuse to balloon below the floor, keep the 2M granularity. */ + if (val.freeram - num_pages < floor) { + num_pages = val.freeram > floor ? (val.freeram - floor) : 0; + num_pages -= num_pages % PAGES_IN_2M; + } + while (!done) { bl_resp = (struct dm_balloon_response *)send_buffer; memset(send_buffer, 0, PAGE_SIZE); From 96c1d0581d00f7abe033350edb021a9d947d8d81 Mon Sep 17 00:00:00 2001 From: Nick Meier Date: Sat, 28 Feb 2015 11:39:01 -0800 Subject: [PATCH 029/133] Drivers: hv: vmbus: Add support for VMBus panic notifier handler Hyper-V allows a guest to notify the Hyper-V host that a panic condition occured. This notification can include up to five 64 bit values. These 64 bit values are written into crash MSRs. Once the data has been written into the crash MSRs, the host is then notified by writing into a Crash Control MSR. On the Hyper-V host, the panic notification data is captured in the Windows Event log as a 18590 event. Crash MSRs are defined in appendix H of the Hypervisor Top Level Functional Specification. At the time of this patch, v4.0 is the current functional spec. The URL for the v4.0 document is: http://download.microsoft.com/download/A/B/4/AB43A34E-BDD0-4FA6-BDEF-79EEF16E880B/Hypervisor Top Level Functional Specification v4.0.docx Signed-off-by: Nick Meier Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hyperv_vmbus.h | 11 +++++++++++ drivers/hv/vmbus_drv.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index b055e53bbc6e..88af4ec559c4 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -49,6 +49,17 @@ enum hv_cpuid_function { HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005, }; +#define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE 0x400 + +#define HV_X64_MSR_CRASH_P0 0x40000100 +#define HV_X64_MSR_CRASH_P1 0x40000101 +#define HV_X64_MSR_CRASH_P2 0x40000102 +#define HV_X64_MSR_CRASH_P3 0x40000103 +#define HV_X64_MSR_CRASH_P4 0x40000104 +#define HV_X64_MSR_CRASH_CTL 0x40000105 + +#define HV_CRASH_CTL_CRASH_NOTIFY 0x8000000000000000 + /* Define version of the synthetic interrupt controller. */ #define HV_SYNIC_VERSION (1) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 2b7b51d264f1..526fa8b19cda 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include "hyperv_vmbus.h" static struct acpi_device *hv_acpi_dev; @@ -45,6 +47,31 @@ static struct tasklet_struct msg_dpc; static struct completion probe_event; static int irq; + +int hyperv_panic_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct pt_regs *regs; + + regs = current_pt_regs(); + + wrmsrl(HV_X64_MSR_CRASH_P0, regs->ip); + wrmsrl(HV_X64_MSR_CRASH_P1, regs->ax); + wrmsrl(HV_X64_MSR_CRASH_P2, regs->bx); + wrmsrl(HV_X64_MSR_CRASH_P3, regs->cx); + wrmsrl(HV_X64_MSR_CRASH_P4, regs->dx); + + /* + * Let Hyper-V know there is crash data available + */ + wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY); + return NOTIFY_DONE; +} + +static struct notifier_block hyperv_panic_block = { + .notifier_call = hyperv_panic_event, +}; + struct resource hyperv_mmio = { .name = "hyperv mmio", .flags = IORESOURCE_MEM, @@ -795,6 +822,15 @@ static int vmbus_bus_init(int irq) goto err_alloc; hv_cpu_hotplug_quirk(true); + + /* + * Only register if the crash MSRs are available + */ + if (ms_hyperv.features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { + atomic_notifier_chain_register(&panic_notifier_list, + &hyperv_panic_block); + } + vmbus_request_offers(); return 0; From a13e8bbe851a96a0e78c2bd599bc34082fa697cd Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:39:02 -0800 Subject: [PATCH 030/133] Drivers: hv: vmbus: Use a round-robin algorithm for picking the outgoing channel The current algorithm for picking an outgoing channel was not distributing the load well. Implement a simple round-robin scheme to ensure good distribution of the outgoing traffic. Signed-off-by: K. Y. Srinivasan Reviewed-by: Long Li Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 30 +++++++++++++++--------------- include/linux/hyperv.h | 3 +++ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index b1e5a5fdaf7f..611789139f9b 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -350,6 +350,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) } newchannel->state = CHANNEL_OPEN_STATE; + channel->num_sc++; if (channel->sc_creation_callback != NULL) /* * We need to invoke the sub-channel creation @@ -862,9 +863,8 @@ cleanup: /* * Retrieve the (sub) channel on which to send an outgoing request. - * When a primary channel has multiple sub-channels, we choose a - * channel whose VCPU binding is closest to the VCPU on which - * this call is being made. + * When a primary channel has multiple sub-channels, we try to + * distribute the load equally amongst all available channels. */ struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary) { @@ -872,11 +872,19 @@ struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary) int cur_cpu; struct vmbus_channel *cur_channel; struct vmbus_channel *outgoing_channel = primary; - int cpu_distance, new_cpu_distance; + int next_channel; + int i = 1; if (list_empty(&primary->sc_list)) return outgoing_channel; + next_channel = primary->next_oc++; + + if (next_channel > (primary->num_sc)) { + primary->next_oc = 0; + return outgoing_channel; + } + cur_cpu = hv_context.vp_index[get_cpu()]; put_cpu(); list_for_each_safe(cur, tmp, &primary->sc_list) { @@ -887,18 +895,10 @@ struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary) if (cur_channel->target_vp == cur_cpu) return cur_channel; - cpu_distance = ((outgoing_channel->target_vp > cur_cpu) ? - (outgoing_channel->target_vp - cur_cpu) : - (cur_cpu - outgoing_channel->target_vp)); + if (i == next_channel) + return cur_channel; - new_cpu_distance = ((cur_channel->target_vp > cur_cpu) ? - (cur_channel->target_vp - cur_cpu) : - (cur_cpu - cur_channel->target_vp)); - - if (cpu_distance < new_cpu_distance) - continue; - - outgoing_channel = cur_channel; + i++; } return outgoing_channel; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index dd92a854c700..1ca582457076 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -761,6 +761,9 @@ struct vmbus_channel { * link up channels based on their CPU affinity. */ struct list_head percpu_list; + + int num_sc; + int next_oc; }; static inline void set_channel_read_state(struct vmbus_channel *c, bool state) From 87e93d61708fe2c44875d1ecdb174aad070dbd08 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:39:03 -0800 Subject: [PATCH 031/133] Drivers: hv: vmbus: Suport an API to send pagebuffers with additional control Implement an API for sending pagebuffers that gives more control to the client in terms of setting the vmbus flags as well as deciding when to notify the host. This will be useful for enabling batch processing. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 33 +++++++++++++++++++++++++++------ include/linux/hyperv.h | 9 +++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 9b79aca7e565..f060d1f7bc99 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -636,13 +636,18 @@ int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, EXPORT_SYMBOL(vmbus_sendpacket); /* - * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer - * packets using a GPADL Direct packet type. + * vmbus_sendpacket_pagebuffer_ctl - Send a range of single-page buffer + * packets using a GPADL Direct packet type. This interface allows you + * to control notifying the host. This will be useful for sending + * batched data. Also the sender can control the send flags + * explicitly. */ -int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, +int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, struct hv_page_buffer pagebuffers[], u32 pagecount, void *buffer, u32 bufferlen, - u64 requestid) + u64 requestid, + u32 flags, + bool kick_q) { int ret; int i; @@ -670,7 +675,7 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, /* Setup the descriptor */ desc.type = VM_PKT_DATA_USING_GPA_DIRECT; - desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; + desc.flags = flags; desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ desc.length8 = (u16)(packetlen_aligned >> 3); desc.transactionid = requestid; @@ -691,11 +696,27 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); - if (ret == 0 && signal) + if ((ret == 0) && kick_q && signal) vmbus_setevent(channel); return ret; } + +/* + * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer + * packets using a GPADL Direct packet type. + */ +int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, + struct hv_page_buffer pagebuffers[], + u32 pagecount, void *buffer, u32 bufferlen, + u64 requestid) +{ + u32 flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; + return vmbus_sendpacket_pagebuffer_ctl(channel, pagebuffers, pagecount, + buffer, bufferlen, requestid, + flags, true); + +} EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer); /* diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 1ca582457076..86e1a7a46af3 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -874,6 +874,15 @@ extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, u32 bufferlen, u64 requestid); +extern int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, + struct hv_page_buffer pagebuffers[], + u32 pagecount, + void *buffer, + u32 bufferlen, + u64 requestid, + u32 flags, + bool kick_q); + extern int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, struct hv_multipage_buffer *mpb, void *buffer, From e9395e3f8952110bda60b54ad03ec52c6e9c7dbd Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 28 Feb 2015 11:39:04 -0800 Subject: [PATCH 032/133] Drivers: hv: vmbus: Suport an API to send packet with additional control Implement an API that gives additional control on the what VMBUS flags will be set as well as if the host needs to be signalled. This API will be useful for clients that want to batch up requests to the host. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 43 +++++++++++++++++++++++++----------------- include/linux/hyperv.h | 8 ++++++++ 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index f060d1f7bc99..da53180f5eb4 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -584,23 +584,9 @@ void vmbus_close(struct vmbus_channel *channel) } EXPORT_SYMBOL_GPL(vmbus_close); -/** - * vmbus_sendpacket() - Send the specified buffer on the given channel - * @channel: Pointer to vmbus_channel structure. - * @buffer: Pointer to the buffer you want to receive the data into. - * @bufferlen: Maximum size of what the the buffer will hold - * @requestid: Identifier of the request - * @type: Type of packet that is being send e.g. negotiate, time - * packet etc. - * - * Sends data in @buffer directly to hyper-v via the vmbus - * This will send the data unparsed to hyper-v. - * - * Mainly used by Hyper-V drivers. - */ -int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, +int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, u32 bufferlen, u64 requestid, - enum vmbus_packet_type type, u32 flags) + enum vmbus_packet_type type, u32 flags, bool kick_q) { struct vmpacket_descriptor desc; u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen; @@ -628,11 +614,34 @@ int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); - if (ret == 0 && signal) + if ((ret == 0) && kick_q && signal) vmbus_setevent(channel); return ret; } +EXPORT_SYMBOL(vmbus_sendpacket_ctl); + +/** + * vmbus_sendpacket() - Send the specified buffer on the given channel + * @channel: Pointer to vmbus_channel structure. + * @buffer: Pointer to the buffer you want to receive the data into. + * @bufferlen: Maximum size of what the the buffer will hold + * @requestid: Identifier of the request + * @type: Type of packet that is being send e.g. negotiate, time + * packet etc. + * + * Sends data in @buffer directly to hyper-v via the vmbus + * This will send the data unparsed to hyper-v. + * + * Mainly used by Hyper-V drivers. + */ +int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, + u32 bufferlen, u64 requestid, + enum vmbus_packet_type type, u32 flags) +{ + return vmbus_sendpacket_ctl(channel, buffer, bufferlen, requestid, + type, flags, true); +} EXPORT_SYMBOL(vmbus_sendpacket); /* diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 86e1a7a46af3..80e444bfc9dc 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -867,6 +867,14 @@ extern int vmbus_sendpacket(struct vmbus_channel *channel, enum vmbus_packet_type type, u32 flags); +extern int vmbus_sendpacket_ctl(struct vmbus_channel *channel, + void *buffer, + u32 bufferLen, + u64 requestid, + enum vmbus_packet_type type, + u32 flags, + bool kick_q); + extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, struct hv_page_buffer pagebuffers[], u32 pagecount, From b7d885145538ddedb1ae23b782ab7c7c0a856e9f Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:31 +0200 Subject: [PATCH 033/133] mei: revamp me clients list handling 1. Use rw lock to access the me_clients list 2. Reuse already defined find functions also when removing particular me client 3. Add wrappers for addition and deletion Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 198 +++++++++++++++++++++++++++---------- drivers/misc/mei/client.h | 5 +- drivers/misc/mei/debugfs.c | 19 ++-- drivers/misc/mei/hbm.c | 3 +- drivers/misc/mei/init.c | 1 + drivers/misc/mei/mei_dev.h | 2 + 6 files changed, 164 insertions(+), 64 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index dfbddfe1c7a0..48813c27a47c 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -48,14 +48,14 @@ void mei_me_cl_init(struct mei_me_client *me_cl) */ struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl) { - if (me_cl) - kref_get(&me_cl->refcnt); + if (me_cl && kref_get_unless_zero(&me_cl->refcnt)) + return me_cl; - return me_cl; + return NULL; } /** - * mei_me_cl_release - unlink and free me client + * mei_me_cl_release - free me client * * Locking: called under "dev->device_lock" lock * @@ -65,9 +65,10 @@ 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 * @@ -81,6 +82,65 @@ void mei_me_cl_put(struct mei_me_client *me_cl) kref_put(&me_cl->refcnt, mei_me_cl_release); } +/** + * __mei_me_cl_del - delete me client form the list and decrease + * reference counter + * + * @dev: mei device + * @me_cl: me client + * + * Locking: dev->me_clients_rwsem + */ +static void __mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl) +{ + if (!me_cl) + return; + + list_del(&me_cl->list); + mei_me_cl_put(me_cl); +} + +/** + * mei_me_cl_add - add me client to the list + * + * @dev: mei device + * @me_cl: me client + */ +void mei_me_cl_add(struct mei_device *dev, struct mei_me_client *me_cl) +{ + down_write(&dev->me_clients_rwsem); + list_add(&me_cl->list, &dev->me_clients); + up_write(&dev->me_clients_rwsem); +} + +/** + * __mei_me_cl_by_uuid - locate me client by uuid + * increases ref count + * + * @dev: mei device + * @uuid: me client uuid + * + * Return: me client or NULL if not found + * + * Locking: dev->me_clients_rwsem + */ +static struct mei_me_client *__mei_me_cl_by_uuid(struct mei_device *dev, + const uuid_le *uuid) +{ + struct mei_me_client *me_cl; + const uuid_le *pn; + + WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem)); + + list_for_each_entry(me_cl, &dev->me_clients, list) { + pn = &me_cl->props.protocol_name; + if (uuid_le_cmp(*uuid, *pn) == 0) + return mei_me_cl_get(me_cl); + } + + return NULL; +} + /** * mei_me_cl_by_uuid - locate me client by uuid * increases ref count @@ -88,20 +148,20 @@ void mei_me_cl_put(struct mei_me_client *me_cl) * @dev: mei device * @uuid: me client uuid * - * Locking: called under "dev->device_lock" lock - * * Return: me client or NULL if not found + * + * Locking: dev->me_clients_rwsem */ -struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev, +struct mei_me_client *mei_me_cl_by_uuid(struct mei_device *dev, const uuid_le *uuid) { struct mei_me_client *me_cl; - list_for_each_entry(me_cl, &dev->me_clients, list) - if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0) - return mei_me_cl_get(me_cl); + down_read(&dev->me_clients_rwsem); + me_cl = __mei_me_cl_by_uuid(dev, uuid); + up_read(&dev->me_clients_rwsem); - return NULL; + return me_cl; } /** @@ -111,22 +171,58 @@ struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev, * @dev: the device structure * @client_id: me client id * - * Locking: called under "dev->device_lock" lock - * * Return: me client or NULL if not found + * + * Locking: dev->me_clients_rwsem */ struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id) { - struct mei_me_client *me_cl; + struct mei_me_client *__me_cl, *me_cl = NULL; - list_for_each_entry(me_cl, &dev->me_clients, list) - if (me_cl->client_id == client_id) + down_read(&dev->me_clients_rwsem); + list_for_each_entry(__me_cl, &dev->me_clients, list) { + if (__me_cl->client_id == client_id) { + me_cl = mei_me_cl_get(__me_cl); + break; + } + } + up_read(&dev->me_clients_rwsem); + + return me_cl; +} + +/** + * __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 + * @client_id: me client id + * + * Return: me client or null if not found + * + * Locking: dev->me_clients_rwsem + */ +static struct mei_me_client *__mei_me_cl_by_uuid_id(struct mei_device *dev, + const uuid_le *uuid, u8 client_id) +{ + struct mei_me_client *me_cl; + const uuid_le *pn; + + WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem)); + + list_for_each_entry(me_cl, &dev->me_clients, list) { + pn = &me_cl->props.protocol_name; + if (uuid_le_cmp(*uuid, *pn) == 0 && + me_cl->client_id == client_id) 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 @@ -135,21 +231,18 @@ struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id) * @uuid: me client uuid * @client_id: me client id * - * Locking: called under "dev->device_lock" lock - * - * Return: me client or NULL if not found + * Return: me client or null if not found */ struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 client_id) { struct mei_me_client *me_cl; - 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 mei_me_cl_get(me_cl); + down_read(&dev->me_clients_rwsem); + me_cl = __mei_me_cl_by_uuid_id(dev, uuid, client_id); + up_read(&dev->me_clients_rwsem); - return NULL; + return me_cl; } /** @@ -162,12 +255,14 @@ struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev, */ void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid) { - struct mei_me_client *me_cl, *next; + struct mei_me_client *me_cl; 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); + + down_write(&dev->me_clients_rwsem); + me_cl = __mei_me_cl_by_uuid(dev, uuid); + __mei_me_cl_del(dev, me_cl); + up_write(&dev->me_clients_rwsem); } /** @@ -181,15 +276,14 @@ 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) { - struct mei_me_client *me_cl, *next; - const uuid_le *pn; + struct mei_me_client *me_cl; dev_dbg(dev->dev, "remove %pUl %d\n", uuid, id); - list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) { - pn = &me_cl->props.protocol_name; - if (me_cl->client_id == id && uuid_le_cmp(*uuid, *pn) == 0) - mei_me_cl_put(me_cl); - } + + down_write(&dev->me_clients_rwsem); + me_cl = __mei_me_cl_by_uuid_id(dev, uuid, id); + __mei_me_cl_del(dev, me_cl); + up_write(&dev->me_clients_rwsem); } /** @@ -203,12 +297,12 @@ void mei_me_cl_rm_all(struct mei_device *dev) { struct mei_me_client *me_cl, *next; + down_write(&dev->me_clients_rwsem); list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) - mei_me_cl_put(me_cl); + __mei_me_cl_del(dev, me_cl); + up_write(&dev->me_clients_rwsem); } - - /** * mei_cl_cmp_id - tells if the clients are the same * @@ -535,28 +629,28 @@ int mei_cl_unlink(struct mei_cl *cl) void mei_host_client_init(struct work_struct *work) { - struct mei_device *dev = container_of(work, - struct mei_device, init_work); + struct mei_device *dev = + container_of(work, struct mei_device, init_work); struct mei_me_client *me_cl; - struct mei_client_properties *props; mutex_lock(&dev->device_lock); - list_for_each_entry(me_cl, &dev->me_clients, list) { - props = &me_cl->props; - if (!uuid_le_cmp(props->protocol_name, mei_amthif_guid)) - mei_amthif_host_init(dev); - else if (!uuid_le_cmp(props->protocol_name, mei_wd_guid)) - mei_wd_host_init(dev); - else if (!uuid_le_cmp(props->protocol_name, mei_nfc_guid)) - mei_nfc_host_init(dev); + me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid); + if (me_cl) + mei_amthif_host_init(dev); + + me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid); + if (me_cl) + mei_wd_host_init(dev); + + me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid); + if (me_cl) + mei_nfc_host_init(dev); - } dev->dev_state = MEI_DEV_ENABLED; dev->reset_count = 0; - mutex_unlock(&dev->device_lock); pm_runtime_mark_last_busy(dev->dev); diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index cfcde8e97fc4..80386f9c27e9 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -31,7 +31,10 @@ 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, +void mei_me_cl_add(struct mei_device *dev, struct mei_me_client *me_cl); +void mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl); + +struct mei_me_client *mei_me_cl_by_uuid(struct mei_device *dev, const uuid_le *uuid); struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id); struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev, diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index b125380ee871..50fc6635fab1 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -28,7 +28,7 @@ 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, *n; + struct mei_me_client *me_cl; size_t bufsz = 1; char *buf; int i = 0; @@ -38,15 +38,14 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, #define HDR \ " |id|fix| UUID |con|msg len|sb|refc|\n" - mutex_lock(&dev->device_lock); - + down_read(&dev->me_clients_rwsem); list_for_each_entry(me_cl, &dev->me_clients, list) bufsz++; bufsz *= sizeof(HDR) + 1; buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) { - mutex_unlock(&dev->device_lock); + up_read(&dev->me_clients_rwsem); return -ENOMEM; } @@ -56,10 +55,9 @@ 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_safe(me_cl, n, &dev->me_clients, list) { + list_for_each_entry(me_cl, &dev->me_clients, list) { - me_cl = mei_me_cl_get(me_cl); - if (me_cl) { + if (mei_me_cl_get(me_cl)) { pos += scnprintf(buf + pos, bufsz - pos, "%2d|%2d|%3d|%pUl|%3d|%7d|%2d|%4d|\n", i++, me_cl->client_id, @@ -69,12 +67,13 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, me_cl->props.max_msg_length, me_cl->props.single_recv_buf, atomic_read(&me_cl->refcnt.refcount)); - } - mei_me_cl_put(me_cl); + mei_me_cl_put(me_cl); + } } + out: - mutex_unlock(&dev->device_lock); + up_read(&dev->me_clients_rwsem); ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); kfree(buf); return ret; diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index c8412d41e4f1..4f83e9aaa6f9 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -338,7 +338,8 @@ static int mei_hbm_me_cl_add(struct mei_device *dev, me_cl->client_id = res->me_addr; me_cl->mei_flow_ctrl_creds = 0; - list_add(&me_cl->list, &dev->me_clients); + mei_me_cl_add(dev, me_cl); + return 0; } diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 9306219d5675..106c054f573f 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -387,6 +387,7 @@ void mei_device_init(struct mei_device *dev, INIT_LIST_HEAD(&dev->device_list); INIT_LIST_HEAD(&dev->me_clients); mutex_init(&dev->device_lock); + init_rwsem(&dev->me_clients_rwsem); init_waitqueue_head(&dev->wait_hw_ready); init_waitqueue_head(&dev->wait_pg); init_waitqueue_head(&dev->wait_hbm_start); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 6c6ce9381535..102cc6603eba 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -460,6 +460,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @version : HBM protocol version in use * @hbm_f_pg_supported : hbm feature pgi protocol * + * @me_clients_rwsem: rw lock over me_clients list * @me_clients : list of FW clients * @me_clients_map : FW clients bit map * @host_clients_map : host clients id pool @@ -556,6 +557,7 @@ struct mei_device { struct hbm_version version; unsigned int hbm_f_pg_supported:1; + struct rw_semaphore me_clients_rwsem; struct list_head me_clients; DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX); DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX); From 381a58c70985ca1256b0f51aa6694f79662bb166 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:32 +0200 Subject: [PATCH 034/133] mei: me: use io register wrappers consistently 1. Use mei_device structure as the first argument to the io register access wrappers so we'll have access to the device structure needed for tracing. 2. Use wrapper consistently Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 123 +++++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 57 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index f8fd503dfbd6..ac82b56ccbb5 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -61,45 +61,68 @@ static inline void mei_me_reg_write(const struct mei_me_hw *hw, * * Return: ME_CB_RW register value (u32) */ -static u32 mei_me_mecbrw_read(const struct mei_device *dev) +static inline u32 mei_me_mecbrw_read(const struct mei_device *dev) { return mei_me_reg_read(to_me_hw(dev), ME_CB_RW); } + +/** + * mei_me_hcbww_write - write 32bit data to the host circular buffer + * + * @dev: the device structure + * @data: 32bit data to be written to the host circular buffer + */ +static inline void mei_me_hcbww_write(struct mei_device *dev, u32 data) +{ + mei_me_reg_write(to_me_hw(dev), H_CB_WW, data); +} + /** * mei_me_mecsr_read - Reads 32bit data from the ME CSR * - * @hw: the me hardware structure + * @dev: the device structure * * Return: ME_CSR_HA register value (u32) */ -static inline u32 mei_me_mecsr_read(const struct mei_me_hw *hw) +static inline u32 mei_me_mecsr_read(const struct mei_device *dev) { - return mei_me_reg_read(hw, ME_CSR_HA); + return mei_me_reg_read(to_me_hw(dev), ME_CSR_HA); } /** * mei_hcsr_read - Reads 32bit data from the host CSR * - * @hw: the me hardware structure + * @dev: the device structure * * Return: H_CSR register value (u32) */ -static inline u32 mei_hcsr_read(const struct mei_me_hw *hw) +static inline u32 mei_hcsr_read(const struct mei_device *dev) { - return mei_me_reg_read(hw, H_CSR); + return mei_me_reg_read(to_me_hw(dev), H_CSR); +} + +/** + * mei_hcsr_write - writes H_CSR register to the mei device + * + * @dev: the device structure + * @reg: new register value + */ +static inline void mei_hcsr_write(struct mei_device *dev, u32 reg) +{ + mei_me_reg_write(to_me_hw(dev), H_CSR, reg); } /** * mei_hcsr_set - writes H_CSR register to the mei device, * and ignores the H_IS bit for it is write-one-to-zero. * - * @hw: the me hardware structure - * @hcsr: new register value + * @dev: the device structure + * @reg: new register value */ -static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr) +static inline void mei_hcsr_set(struct mei_device *dev, u32 reg) { - hcsr &= ~H_IS; - mei_me_reg_write(hw, H_CSR, hcsr); + reg &= ~H_IS; + mei_hcsr_write(dev, reg); } /** @@ -141,7 +164,7 @@ static int mei_me_fw_status(struct mei_device *dev, static void mei_me_hw_config(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); - u32 hcsr = mei_hcsr_read(to_me_hw(dev)); + u32 hcsr = mei_hcsr_read(dev); /* Doesn't change in runtime */ dev->hbuf_depth = (hcsr & H_CBD) >> 24; @@ -170,11 +193,10 @@ static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev) */ static void mei_me_intr_clear(struct mei_device *dev) { - struct mei_me_hw *hw = to_me_hw(dev); - u32 hcsr = mei_hcsr_read(hw); + u32 hcsr = mei_hcsr_read(dev); if ((hcsr & H_IS) == H_IS) - mei_me_reg_write(hw, H_CSR, hcsr); + mei_hcsr_write(dev, hcsr); } /** * mei_me_intr_enable - enables mei device interrupts @@ -183,11 +205,10 @@ static void mei_me_intr_clear(struct mei_device *dev) */ static void mei_me_intr_enable(struct mei_device *dev) { - struct mei_me_hw *hw = to_me_hw(dev); - u32 hcsr = mei_hcsr_read(hw); + u32 hcsr = mei_hcsr_read(dev); hcsr |= H_IE; - mei_hcsr_set(hw, hcsr); + mei_hcsr_set(dev, hcsr); } /** @@ -197,11 +218,10 @@ static void mei_me_intr_enable(struct mei_device *dev) */ static void mei_me_intr_disable(struct mei_device *dev) { - struct mei_me_hw *hw = to_me_hw(dev); - u32 hcsr = mei_hcsr_read(hw); + u32 hcsr = mei_hcsr_read(dev); hcsr &= ~H_IE; - mei_hcsr_set(hw, hcsr); + mei_hcsr_set(dev, hcsr); } /** @@ -211,12 +231,11 @@ static void mei_me_intr_disable(struct mei_device *dev) */ static void mei_me_hw_reset_release(struct mei_device *dev) { - struct mei_me_hw *hw = to_me_hw(dev); - u32 hcsr = mei_hcsr_read(hw); + u32 hcsr = mei_hcsr_read(dev); hcsr |= H_IG; hcsr &= ~H_RST; - mei_hcsr_set(hw, hcsr); + mei_hcsr_set(dev, hcsr); /* complete this write before we set host ready on another CPU */ mmiowb(); @@ -231,8 +250,7 @@ static void mei_me_hw_reset_release(struct mei_device *dev) */ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) { - struct mei_me_hw *hw = to_me_hw(dev); - u32 hcsr = mei_hcsr_read(hw); + u32 hcsr = mei_hcsr_read(dev); /* H_RST may be found lit before reset is started, * for example if preceding reset flow hasn't completed. @@ -242,8 +260,8 @@ 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_hcsr_set(hw, hcsr); - hcsr = mei_hcsr_read(hw); + mei_hcsr_set(dev, hcsr); + hcsr = mei_hcsr_read(dev); } hcsr |= H_RST | H_IG | H_IS; @@ -254,13 +272,13 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) hcsr &= ~H_IE; dev->recvd_hw_ready = false; - mei_me_reg_write(hw, H_CSR, hcsr); + mei_hcsr_write(dev, hcsr); /* * Host reads the H_CSR once to ensure that the * posted write to H_CSR completes. */ - hcsr = mei_hcsr_read(hw); + hcsr = mei_hcsr_read(dev); if ((hcsr & H_RST) == 0) dev_warn(dev->dev, "H_RST is not set = 0x%08X", hcsr); @@ -281,11 +299,10 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) */ static void mei_me_host_set_ready(struct mei_device *dev) { - struct mei_me_hw *hw = to_me_hw(dev); - u32 hcsr = mei_hcsr_read(hw); + u32 hcsr = mei_hcsr_read(dev); hcsr |= H_IE | H_IG | H_RDY; - mei_hcsr_set(hw, hcsr); + mei_hcsr_set(dev, hcsr); } /** @@ -296,8 +313,7 @@ static void mei_me_host_set_ready(struct mei_device *dev) */ static bool mei_me_host_is_ready(struct mei_device *dev) { - struct mei_me_hw *hw = to_me_hw(dev); - u32 hcsr = mei_hcsr_read(hw); + u32 hcsr = mei_hcsr_read(dev); return (hcsr & H_RDY) == H_RDY; } @@ -310,8 +326,7 @@ static bool mei_me_host_is_ready(struct mei_device *dev) */ static bool mei_me_hw_is_ready(struct mei_device *dev) { - struct mei_me_hw *hw = to_me_hw(dev); - u32 mecsr = mei_me_mecsr_read(hw); + u32 mecsr = mei_me_mecsr_read(dev); return (mecsr & ME_RDY_HRA) == ME_RDY_HRA; } @@ -368,11 +383,10 @@ static int mei_me_hw_start(struct mei_device *dev) */ static unsigned char mei_hbuf_filled_slots(struct mei_device *dev) { - struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr; char read_ptr, write_ptr; - hcsr = mei_hcsr_read(hw); + hcsr = mei_hcsr_read(dev); read_ptr = (char) ((hcsr & H_CBRP) >> 8); write_ptr = (char) ((hcsr & H_CBWP) >> 16); @@ -439,7 +453,6 @@ static int mei_me_write_message(struct mei_device *dev, struct mei_msg_hdr *header, unsigned char *buf) { - struct mei_me_hw *hw = to_me_hw(dev); unsigned long rem; unsigned long length = header->length; u32 *reg_buf = (u32 *)buf; @@ -457,21 +470,21 @@ static int mei_me_write_message(struct mei_device *dev, if (empty_slots < 0 || dw_cnt > empty_slots) return -EMSGSIZE; - mei_me_reg_write(hw, H_CB_WW, *((u32 *) header)); + mei_me_hcbww_write(dev, *((u32 *) header)); for (i = 0; i < length / 4; i++) - mei_me_reg_write(hw, H_CB_WW, reg_buf[i]); + mei_me_hcbww_write(dev, reg_buf[i]); rem = length & 0x3; if (rem > 0) { u32 reg = 0; memcpy(®, &buf[length - rem], rem); - mei_me_reg_write(hw, H_CB_WW, reg); + mei_me_hcbww_write(dev, reg); } - hcsr = mei_hcsr_read(hw) | H_IG; - mei_hcsr_set(hw, hcsr); + hcsr = mei_hcsr_read(dev) | H_IG; + mei_hcsr_set(dev, hcsr); if (!mei_me_hw_is_ready(dev)) return -EIO; @@ -487,12 +500,11 @@ static int mei_me_write_message(struct mei_device *dev, */ static int mei_me_count_full_read_slots(struct mei_device *dev) { - struct mei_me_hw *hw = to_me_hw(dev); u32 me_csr; char read_ptr, write_ptr; unsigned char buffer_depth, filled_slots; - me_csr = mei_me_mecsr_read(hw); + me_csr = mei_me_mecsr_read(dev); buffer_depth = (unsigned char)((me_csr & ME_CBD_HRA) >> 24); read_ptr = (char) ((me_csr & ME_CBRP_HRA) >> 8); write_ptr = (char) ((me_csr & ME_CBWP_HRA) >> 16); @@ -518,7 +530,6 @@ static int mei_me_count_full_read_slots(struct mei_device *dev) static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, unsigned long buffer_length) { - struct mei_me_hw *hw = to_me_hw(dev); u32 *reg_buf = (u32 *)buffer; u32 hcsr; @@ -531,8 +542,8 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, memcpy(reg_buf, ®, buffer_length); } - hcsr = mei_hcsr_read(hw) | H_IG; - mei_hcsr_set(hw, hcsr); + hcsr = mei_hcsr_read(dev) | H_IG; + mei_hcsr_set(dev, hcsr); return 0; } @@ -649,8 +660,7 @@ reply: */ static bool mei_me_pg_is_enabled(struct mei_device *dev) { - struct mei_me_hw *hw = to_me_hw(dev); - u32 reg = mei_me_reg_read(hw, ME_CSR_HA); + u32 reg = mei_me_mecsr_read(dev); if ((reg & ME_PGIC_HRA) == 0) goto notsupported; @@ -683,14 +693,13 @@ notsupported: irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *) dev_id; - struct mei_me_hw *hw = to_me_hw(dev); - u32 csr_reg = mei_hcsr_read(hw); + u32 hcsr = mei_hcsr_read(dev); - if ((csr_reg & H_IS) != H_IS) + if ((hcsr & H_IS) != H_IS) return IRQ_NONE; /* clear H_IS bit in H_CSR */ - mei_me_reg_write(hw, H_CSR, csr_reg); + mei_hcsr_write(dev, hcsr); return IRQ_WAKE_THREAD; } From a0a927d06d79d59c55ae7ac0b2fd7f3c0ea3c14c Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:33 +0200 Subject: [PATCH 035/133] mei: me: add io register tracing To make debugging a bit easier we add me register access tracing /tracing/events/mei/mei_reg_{read,write} Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/Makefile | 3 ++ drivers/misc/mei/hw-me.c | 31 +++++++++++++-- drivers/misc/mei/mei-trace.c | 25 ++++++++++++ drivers/misc/mei/mei-trace.h | 74 ++++++++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 drivers/misc/mei/mei-trace.c create mode 100644 drivers/misc/mei/mei-trace.h diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 8ebc6cda1373..518914a82b83 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -21,3 +21,6 @@ mei-me-objs += hw-me.o obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o mei-txe-objs := pci-txe.o mei-txe-objs += hw-txe.o + +mei-$(CONFIG_EVENT_TRACING) += mei-trace.o +CFLAGS_mei-trace.o = -I$(src) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index ac82b56ccbb5..d3aef82a6c89 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -25,6 +25,8 @@ #include "hw-me.h" #include "hw-me-regs.h" +#include "mei-trace.h" + /** * mei_me_reg_read - Reads 32bit data from the mei device * @@ -86,7 +88,12 @@ static inline void mei_me_hcbww_write(struct mei_device *dev, u32 data) */ static inline u32 mei_me_mecsr_read(const struct mei_device *dev) { - return mei_me_reg_read(to_me_hw(dev), ME_CSR_HA); + u32 reg; + + reg = mei_me_reg_read(to_me_hw(dev), ME_CSR_HA); + trace_mei_reg_read(dev->dev, "ME_CSR_HA", ME_CSR_HA, reg); + + return reg; } /** @@ -98,7 +105,12 @@ static inline u32 mei_me_mecsr_read(const struct mei_device *dev) */ static inline u32 mei_hcsr_read(const struct mei_device *dev) { - return mei_me_reg_read(to_me_hw(dev), H_CSR); + u32 reg; + + reg = mei_me_reg_read(to_me_hw(dev), H_CSR); + trace_mei_reg_read(dev->dev, "H_CSR", H_CSR, reg); + + return reg; } /** @@ -109,6 +121,7 @@ static inline u32 mei_hcsr_read(const struct mei_device *dev) */ static inline void mei_hcsr_write(struct mei_device *dev, u32 reg) { + trace_mei_reg_write(dev->dev, "H_CSR", H_CSR, reg); mei_me_reg_write(to_me_hw(dev), H_CSR, reg); } @@ -555,9 +568,14 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, static void mei_me_pg_enter(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); - u32 reg = mei_me_reg_read(hw, H_HPG_CSR); + u32 reg; + + reg = mei_me_reg_read(hw, H_HPG_CSR); + trace_mei_reg_read(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg); reg |= H_HPG_CSR_PGI; + + trace_mei_reg_write(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg); mei_me_reg_write(hw, H_HPG_CSR, reg); } @@ -569,11 +587,16 @@ static void mei_me_pg_enter(struct mei_device *dev) static void mei_me_pg_exit(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); - u32 reg = mei_me_reg_read(hw, H_HPG_CSR); + u32 reg; + + reg = mei_me_reg_read(hw, H_HPG_CSR); + trace_mei_reg_read(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg); WARN(!(reg & H_HPG_CSR_PGI), "PGI is not set\n"); reg |= H_HPG_CSR_PGIHEXR; + + trace_mei_reg_write(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg); mei_me_reg_write(hw, H_HPG_CSR, reg); } diff --git a/drivers/misc/mei/mei-trace.c b/drivers/misc/mei/mei-trace.c new file mode 100644 index 000000000000..388efb519138 --- /dev/null +++ b/drivers/misc/mei/mei-trace.c @@ -0,0 +1,25 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#include + +/* sparse doesn't like tracepoint macros */ +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "mei-trace.h" + +EXPORT_TRACEPOINT_SYMBOL(mei_reg_read); +EXPORT_TRACEPOINT_SYMBOL(mei_reg_write); +#endif /* __CHECKER__ */ diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h new file mode 100644 index 000000000000..d5c38d15cdd9 --- /dev/null +++ b/drivers/misc/mei/mei-trace.h @@ -0,0 +1,74 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#if !defined(_MEI_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _MEI_TRACE_H_ + +#include +#include +#include + +#undef TRACE_SYSTEM + +#define TRACE_SYSTEM mei +#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM) + +TRACE_EVENT(mei_reg_read, + TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val), + TP_ARGS(dev, reg, offs, val), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(const char *, reg) + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)) + __entry->reg = reg; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] read %s:[%#x] = %#x", + __get_str(dev), __entry->reg, __entry->offs, __entry->val) +); + +TRACE_EVENT(mei_reg_write, + TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val), + TP_ARGS(dev, reg, offs, val), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(const char *, reg) + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)) + __entry->reg = reg; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] write %s[%#x] = %#x)", + __get_str(dev), __entry->reg, __entry->offs, __entry->val) +); + +#endif /* _MEI_TRACE_H_ */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE mei-trace +#include From 2d1995fce3f9b9a0bdb88d47144509e3b84db0f9 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 10 Feb 2015 10:39:34 +0200 Subject: [PATCH 036/133] mei: me: change power gating function name conventions The current power gating naming was confusing, we wish to swap meanings of register and flow level power gating terms, For registers writing level use terms set and unset: mei_me_pg_set, mei_me_pg_unset For flow/high level use power gating enter and power gating exit terms mei_me_pg_enter_sync, mei_me_pg_exit_sync Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 20 ++++++++++---------- drivers/misc/mei/hw-me.h | 4 ++-- drivers/misc/mei/pci-me.c | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index d3aef82a6c89..6fb75e62a764 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -561,11 +561,11 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, } /** - * mei_me_pg_enter - write pg enter register + * mei_me_pg_set - write pg enter register * * @dev: the device structure */ -static void mei_me_pg_enter(struct mei_device *dev) +static void mei_me_pg_set(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); u32 reg; @@ -580,11 +580,11 @@ static void mei_me_pg_enter(struct mei_device *dev) } /** - * mei_me_pg_exit - write pg exit register + * mei_me_pg_unset - write pg exit register * * @dev: the device structure */ -static void mei_me_pg_exit(struct mei_device *dev) +static void mei_me_pg_unset(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); u32 reg; @@ -601,13 +601,13 @@ static void mei_me_pg_exit(struct mei_device *dev) } /** - * mei_me_pg_set_sync - perform pg entry procedure + * mei_me_pg_enter_sync - perform pg entry procedure * * @dev: the device structure * * Return: 0 on success an error code otherwise */ -int mei_me_pg_set_sync(struct mei_device *dev) +int mei_me_pg_enter_sync(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT); @@ -625,7 +625,7 @@ int mei_me_pg_set_sync(struct mei_device *dev) mutex_lock(&dev->device_lock); if (dev->pg_event == MEI_PG_EVENT_RECEIVED) { - mei_me_pg_enter(dev); + mei_me_pg_set(dev); ret = 0; } else { ret = -ETIME; @@ -638,13 +638,13 @@ int mei_me_pg_set_sync(struct mei_device *dev) } /** - * mei_me_pg_unset_sync - perform pg exit procedure + * mei_me_pg_exit_sync - perform pg exit procedure * * @dev: the device structure * * Return: 0 on success an error code otherwise */ -int mei_me_pg_unset_sync(struct mei_device *dev) +int mei_me_pg_exit_sync(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT); @@ -655,7 +655,7 @@ int mei_me_pg_unset_sync(struct mei_device *dev) dev->pg_event = MEI_PG_EVENT_WAIT; - mei_me_pg_exit(dev); + mei_me_pg_unset(dev); mutex_unlock(&dev->device_lock); wait_event_timeout(dev->wait_pg, diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index d6567af44377..6022d52af6f6 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -71,8 +71,8 @@ extern const struct mei_cfg mei_me_pch8_sps_cfg; struct mei_device *mei_me_dev_init(struct pci_dev *pdev, const struct mei_cfg *cfg); -int mei_me_pg_set_sync(struct mei_device *dev); -int mei_me_pg_unset_sync(struct mei_device *dev); +int mei_me_pg_enter_sync(struct mei_device *dev); +int mei_me_pg_exit_sync(struct mei_device *dev); irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id); irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id); diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index bd3039ab8f98..72fb381a1245 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -389,7 +389,7 @@ static int mei_me_pm_runtime_suspend(struct device *device) mutex_lock(&dev->device_lock); if (mei_write_is_idle(dev)) - ret = mei_me_pg_set_sync(dev); + ret = mei_me_pg_enter_sync(dev); else ret = -EAGAIN; @@ -414,7 +414,7 @@ static int mei_me_pm_runtime_resume(struct device *device) mutex_lock(&dev->device_lock); - ret = mei_me_pg_unset_sync(dev); + ret = mei_me_pg_exit_sync(dev); mutex_unlock(&dev->device_lock); From 3908be6f9aa5517bc717f8ffdaaafd89a1b78471 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 10 Feb 2015 10:39:35 +0200 Subject: [PATCH 037/133] mei: fix function names and format in KDoc Align functions names in KDoc with real ones. Fix comment format to be KDoc and fix wrong syntax there. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 9 +++++---- drivers/misc/mei/hw-txe.c | 2 +- drivers/misc/mei/pci-txe.c | 4 ++-- drivers/misc/mei/wd.c | 18 +++++++++--------- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 48813c27a47c..3e9cf0db5540 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -321,7 +321,7 @@ static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, } /** - * mei_io_list_flush - removes cbs belonging to cl. + * __mei_io_list_flush - removes and frees cbs belonging to cl. * * @list: an instance of our list structure * @cl: host client, can be NULL for flushing the whole list @@ -540,10 +540,11 @@ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) return NULL; } -/** mei_cl_link: allocate host id in the host map +/** + * mei_cl_link - allocate host id in the host map * - * @cl - host client - * @id - fixed host id or -1 for generic one + * @cl: host client + * @id: fixed host id or -1 for generic one * * Return: 0 on success * -EINVAL on incorrect values diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index 618ea721aca8..7abafe7d120d 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -412,7 +412,7 @@ static void mei_txe_intr_disable(struct mei_device *dev) mei_txe_br_reg_write(hw, HIER_REG, 0); } /** - * mei_txe_intr_disable - enable all interrupts + * mei_txe_intr_enable - enable all interrupts * * @dev: the device structure */ diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index c86e2ddbe30a..dcfcba44b6f7 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -63,7 +63,7 @@ static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw) } } /** - * mei_probe - Device Initialization Routine + * mei_txe_probe - Device Initialization Routine * * @pdev: PCI device structure * @ent: entry in mei_txe_pci_tbl @@ -193,7 +193,7 @@ end: } /** - * mei_remove - Device Removal Routine + * mei_txe_remove - Device Removal Routine * * @pdev: PCI device structure * diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index 475f1dea45bf..ac1e6235692d 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c @@ -202,10 +202,10 @@ err: return ret; } -/* +/** * mei_wd_ops_start - wd start command from the watchdog core. * - * @wd_dev - watchdog device struct + * @wd_dev: watchdog device struct * * Return: 0 if success, negative errno code for failure */ @@ -239,10 +239,10 @@ end_unlock: return err; } -/* +/** * mei_wd_ops_stop - wd stop command from the watchdog core. * - * @wd_dev - watchdog device struct + * @wd_dev: watchdog device struct * * Return: 0 if success, negative errno code for failure */ @@ -261,10 +261,10 @@ static int mei_wd_ops_stop(struct watchdog_device *wd_dev) return 0; } -/* +/** * mei_wd_ops_ping - wd ping command from the watchdog core. * - * @wd_dev - watchdog device struct + * @wd_dev: watchdog device struct * * Return: 0 if success, negative errno code for failure */ @@ -311,11 +311,11 @@ end: return ret; } -/* +/** * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core. * - * @wd_dev - watchdog device struct - * @timeout - timeout value to set + * @wd_dev: watchdog device struct + * @timeout: timeout value to set * * Return: 0 if success, negative errno code for failure */ From 3d33ff2457355a9dd3c3178b04ab6669882b306c Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:36 +0200 Subject: [PATCH 038/133] mei: fix device reset on mei_cl_irq_read_msg allocation failure On memory allocation failure mei_cl_irq_read_msg will return with error that will cause device reset. Instead we should propagate error to caller and just clean the read queues. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 6 ++ drivers/misc/mei/interrupt.c | 129 ++++++++++++++++++----------------- drivers/misc/mei/main.c | 17 +++-- drivers/misc/mei/mei_dev.h | 2 + 4 files changed, 86 insertions(+), 68 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index be767f4db26a..025626f4467d 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -322,10 +322,16 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) goto out; } + if (cb->status) { + rets = cb->status; + goto free; + } + r_length = min_t(size_t, length, cb->buf_idx); memcpy(buf, cb->response_buffer.data, r_length); rets = r_length; +free: mei_io_cb_free(cb); cl->reading_state = MEI_IDLE; cl->read_cb = NULL; diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 711cddfa9c99..587cb04a3cf5 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -69,85 +69,91 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl, cl->me_client_id == mei_hdr->me_addr; } /** - * mei_cl_is_reading - checks if the client - * is the one to read this message + * mei_cl_is_reading - checks if the client is in reading state * * @cl: mei client - * @mei_hdr: header of mei message * - * Return: true on match and false otherwise + * Return: true if the client is reading */ -static bool mei_cl_is_reading(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr) +static bool mei_cl_is_reading(struct mei_cl *cl) { - return mei_cl_hbm_equal(cl, mei_hdr) && - cl->state == MEI_FILE_CONNECTED && + return cl->state == MEI_FILE_CONNECTED && cl->reading_state != MEI_READ_COMPLETE; } /** * mei_cl_irq_read_msg - process client message * - * @dev: the device structure + * @cl: reading client * @mei_hdr: header of mei client message - * @complete_list: An instance of our list structure + * @complete_list: completion list * - * Return: 0 on success, <0 on failure. + * Return: always 0 */ -static int mei_cl_irq_read_msg(struct mei_device *dev, +static int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, struct mei_cl_cb *complete_list) { - struct mei_cl *cl; - struct mei_cl_cb *cb, *next; + struct mei_device *dev = cl->dev; + struct mei_cl_cb *cb; unsigned char *buffer = NULL; - list_for_each_entry_safe(cb, next, &dev->read_list.list, list) { - cl = cb->cl; - if (!mei_cl_is_reading(cl, mei_hdr)) - continue; - - cl->reading_state = MEI_READING; - - if (cb->response_buffer.size == 0 || - cb->response_buffer.data == NULL) { - cl_err(dev, cl, "response buffer is not allocated.\n"); - list_del(&cb->list); - return -ENOMEM; - } - - if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) { - cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n", - cb->response_buffer.size, - mei_hdr->length, cb->buf_idx); - buffer = krealloc(cb->response_buffer.data, - mei_hdr->length + cb->buf_idx, - GFP_KERNEL); - - if (!buffer) { - list_del(&cb->list); - return -ENOMEM; - } - cb->response_buffer.data = buffer; - cb->response_buffer.size = - mei_hdr->length + cb->buf_idx; - } - - buffer = cb->response_buffer.data + cb->buf_idx; - mei_read_slots(dev, buffer, mei_hdr->length); - - cb->buf_idx += mei_hdr->length; - if (mei_hdr->msg_complete) { - cl->status = 0; - list_del(&cb->list); - cl_dbg(dev, cl, "completed read length = %lu\n", - cb->buf_idx); - list_add_tail(&cb->list, &complete_list->list); - } - break; + list_for_each_entry(cb, &dev->read_list.list, list) { + if (cl == cb->cl) + break; } - dev_dbg(dev->dev, "message read\n"); + if (&cb->list == &dev->read_list.list) { + dev_err(dev->dev, "no reader found\n"); + goto out; + } + + if (!mei_cl_is_reading(cl)) { + cl_err(dev, cl, "cl is not reading state=%d reading state=%d\n", + cl->state, cl->reading_state); + goto out; + } + + cl->reading_state = MEI_READING; + + if (cb->response_buffer.size == 0 || + cb->response_buffer.data == NULL) { + cl_err(dev, cl, "response buffer is not allocated.\n"); + list_move_tail(&cb->list, &complete_list->list); + cb->status = -ENOMEM; + goto out; + } + + if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) { + cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n", + cb->response_buffer.size, mei_hdr->length, cb->buf_idx); + buffer = krealloc(cb->response_buffer.data, + mei_hdr->length + cb->buf_idx, + GFP_KERNEL); + + if (!buffer) { + cb->status = -ENOMEM; + list_move_tail(&cb->list, &complete_list->list); + goto out; + } + cb->response_buffer.data = buffer; + cb->response_buffer.size = mei_hdr->length + cb->buf_idx; + } + + buffer = cb->response_buffer.data + cb->buf_idx; + mei_read_slots(dev, buffer, mei_hdr->length); + + cb->buf_idx += mei_hdr->length; + if (mei_hdr->msg_complete) { + cl_dbg(dev, cl, "completed read length = %lu\n", + cb->buf_idx); + list_move_tail(&cb->list, &complete_list->list); + } + +out: if (!buffer) { + /* assume that mei_hdr->length <= MEI_RD_MSG_BUF_SIZE */ + BUG_ON(mei_hdr->length > MEI_RD_MSG_BUF_SIZE); mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n", MEI_HDR_PRM(mei_hdr)); @@ -389,14 +395,10 @@ int mei_irq_read_handler(struct mei_device *dev, goto end; } } else { - ret = mei_cl_irq_read_msg(dev, mei_hdr, cmpl_list); - if (ret) { - dev_err(dev->dev, "mei_cl_irq_read_msg failed = %d\n", - ret); - goto end; - } + ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list); } + reset_slots: /* reset the number of slots and header */ *slots = mei_count_full_read_slots(dev); @@ -636,4 +638,3 @@ out: schedule_delayed_work(&dev->timer_work, 2 * HZ); mutex_unlock(&dev->device_lock); } - diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 3c019c0e60eb..cbdbf4af2bf7 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -192,8 +192,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - if (cl->read_cb) { - cb = cl->read_cb; + cb = cl->read_cb; + if (cb) { /* read what left */ if (cb->buf_idx > *offset) goto copy_buffer; @@ -218,7 +218,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, } if (MEI_READ_COMPLETE != cl->reading_state && - !waitqueue_active(&cl->rx_wait)) { + !waitqueue_active(&cl->rx_wait)) { + if (file->f_flags & O_NONBLOCK) { rets = -EAGAIN; goto out; @@ -248,12 +249,20 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, rets = -ENODEV; goto out; } + if (cl->reading_state != MEI_READ_COMPLETE) { rets = 0; goto out; } - /* now copy the data to user space */ + copy_buffer: + /* now copy the data to user space */ + if (cb->status) { + rets = cb->status; + dev_dbg(dev->dev, "read operation failed %d\n", rets); + goto free; + } + dev_dbg(dev->dev, "buf.size = %d buf.idx= %ld\n", cb->response_buffer.size, cb->buf_idx); if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) { diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 102cc6603eba..195e426b08f0 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -199,6 +199,7 @@ struct mei_cl; * @buf_idx: last read index * @read_time: last read operation time stamp (iamthif) * @file_object: pointer to file structure + * @status: io status of the cb * @internal: communication between driver and FW flag */ struct mei_cl_cb { @@ -210,6 +211,7 @@ struct mei_cl_cb { unsigned long buf_idx; unsigned long read_time; struct file *file_object; + int status; u32 internal:1; }; From db4756fd2f16efae8469dd1e37710919a0af9370 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:37 +0200 Subject: [PATCH 039/133] mei: iamthif: fix device reset on mei_amthif_irq_read_msg On failure mei_amthif_irq_read_msg returns an error that will cause device reset but the issue is software one so instead we should propagate error to caller and just clean the read queues. As a side effect also removes useless BUG_ONs Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 74 +++++++++++++++++++++++------------- drivers/misc/mei/interrupt.c | 7 +--- drivers/misc/mei/mei_dev.h | 2 +- 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index c4cb9a984a5f..2ad2f94678c8 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -195,23 +195,26 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, dev_dbg(dev->dev, "woke up from sleep\n"); } + if (cb->status) { + rets = cb->status; + dev_dbg(dev->dev, "read operation failed %d\n", rets); + goto free; + } dev_dbg(dev->dev, "Got amthif data\n"); dev->iamthif_timer = 0; - if (cb) { - timeout = cb->read_time + - mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); - dev_dbg(dev->dev, "amthif timeout = %lud\n", - timeout); + timeout = cb->read_time + + mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); + dev_dbg(dev->dev, "amthif timeout = %lud\n", + timeout); - if (time_after(jiffies, timeout)) { - dev_dbg(dev->dev, "amthif Time out\n"); - /* 15 sec for the message has expired */ - list_del(&cb->list); - rets = -ETIME; - goto free; - } + if (time_after(jiffies, timeout)) { + dev_dbg(dev->dev, "amthif Time out\n"); + /* 15 sec for the message has expired */ + list_del(&cb->list); + rets = -ETIME; + goto free; } /* if the whole message will fit remove it from the list */ if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset)) @@ -501,25 +504,42 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, * mei_amthif_irq_read_msg - read routine after ISR to * handle the read amthif message * - * @dev: the device structure + * @cl: mei client * @mei_hdr: header of amthif message - * @complete_list: An instance of our list structure + * @complete_list: completed callbacks list * - * Return: 0 on success, <0 on failure. + * Return: -ENODEV if cb is NULL 0 otherwise; error message is in cb->status */ -int mei_amthif_irq_read_msg(struct mei_device *dev, +int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, struct mei_cl_cb *complete_list) { + struct mei_device *dev; struct mei_cl_cb *cb; unsigned char *buffer; + int ret = 0; - BUG_ON(mei_hdr->me_addr != dev->iamthif_cl.me_client_id); - BUG_ON(dev->iamthif_state != MEI_IAMTHIF_READING); + dev = cl->dev; + + if (cl->state != MEI_FILE_CONNECTED) + goto err; + + if (dev->iamthif_state != MEI_IAMTHIF_READING) + goto err; + + cb = dev->iamthif_current_cb; + + if (!cb) { + ret = -ENODEV; + goto err; + } + + if (dev->iamthif_mtu < dev->iamthif_msg_buf_index + mei_hdr->length) { + cb->status = -ERANGE; + goto err; + } buffer = dev->iamthif_msg_buf + dev->iamthif_msg_buf_index; - BUG_ON(dev->iamthif_mtu < dev->iamthif_msg_buf_index + mei_hdr->length); - mei_read_slots(dev, buffer, mei_hdr->length); dev->iamthif_msg_buf_index += mei_hdr->length; @@ -527,14 +547,8 @@ int mei_amthif_irq_read_msg(struct mei_device *dev, if (!mei_hdr->msg_complete) return 0; - dev_dbg(dev->dev, "amthif_message_buffer_index =%d\n", - mei_hdr->length); - dev_dbg(dev->dev, "completed amthif read.\n "); - if (!dev->iamthif_current_cb) - return -ENODEV; - cb = dev->iamthif_current_cb; dev->iamthif_current_cb = NULL; dev->iamthif_stall_timer = 0; @@ -543,10 +557,16 @@ int mei_amthif_irq_read_msg(struct mei_device *dev, if (dev->iamthif_ioctl) { /* found the iamthif cb */ dev_dbg(dev->dev, "complete the amthif read cb.\n "); - dev_dbg(dev->dev, "add the amthif read cb to complete.\n "); list_add_tail(&cb->list, &complete_list->list); } + return 0; + +err: + mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); + dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n", + MEI_HDR_PRM(mei_hdr)); + return ret; } /** diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 587cb04a3cf5..151f0c84a65e 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -384,11 +384,8 @@ int mei_irq_read_handler(struct mei_device *dev, goto end; } - if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id && - MEI_FILE_CONNECTED == dev->iamthif_cl.state && - dev->iamthif_state == MEI_IAMTHIF_READING) { - - ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list); + if (cl == &dev->iamthif_cl) { + ret = mei_amthif_irq_read_msg(cl, mei_hdr, cmpl_list); if (ret) { dev_err(dev->dev, "mei_amthif_irq_read_msg failed = %d\n", ret); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 195e426b08f0..b74d156ed600 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -685,7 +685,7 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb); -int mei_amthif_irq_read_msg(struct mei_device *dev, +int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, struct mei_cl_cb *complete_list); int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); From 4e097bc5d5c948ff8a60ac38a5826f5b6699603d Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:38 +0200 Subject: [PATCH 040/133] mei: iamthif: remove useless iamthif_ioctl variable iamthif_ioctl is obsolete and can be safely dropped Currently it is set to true during driver runtime Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 11 +++-------- drivers/misc/mei/interrupt.c | 1 - drivers/misc/mei/mei_dev.h | 2 -- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 2ad2f94678c8..788b00e23353 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -52,7 +52,6 @@ void mei_amthif_reset_params(struct mei_device *dev) dev->iamthif_msg_buf_size = 0; dev->iamthif_msg_buf_index = 0; dev->iamthif_canceled = false; - dev->iamthif_ioctl = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; dev->iamthif_stall_timer = 0; @@ -279,7 +278,6 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) dev->iamthif_current_cb = cb; dev->iamthif_file_object = cb->file_object; dev->iamthif_canceled = false; - dev->iamthif_ioctl = true; dev->iamthif_msg_buf_size = cb->request_buffer.size; memcpy(dev->iamthif_msg_buf, cb->request_buffer.data, cb->request_buffer.size); @@ -375,7 +373,6 @@ void mei_amthif_run_next_cmd(struct mei_device *dev) dev->iamthif_msg_buf_size = 0; dev->iamthif_msg_buf_index = 0; dev->iamthif_canceled = false; - dev->iamthif_ioctl = true; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; dev->iamthif_file_object = NULL; @@ -554,11 +551,9 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, dev->iamthif_stall_timer = 0; cb->buf_idx = dev->iamthif_msg_buf_index; cb->read_time = jiffies; - if (dev->iamthif_ioctl) { - /* found the iamthif cb */ - dev_dbg(dev->dev, "complete the amthif read cb.\n "); - list_add_tail(&cb->list, &complete_list->list); - } + + dev_dbg(dev->dev, "complete the amthif read cb.\n "); + list_add_tail(&cb->list, &complete_list->list); return 0; diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 151f0c84a65e..4cb602fba593 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -589,7 +589,6 @@ void mei_timer(struct work_struct *work) dev->iamthif_msg_buf_size = 0; dev->iamthif_msg_buf_index = 0; dev->iamthif_canceled = false; - dev->iamthif_ioctl = true; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index b74d156ed600..6cc68de580ba 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -488,7 +488,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @iamthif_msg_buf_index : current index in amthif message request buffer * @iamthif_state : amthif processor state * @iamthif_flow_control_pending: amthif waits for flow control - * @iamthif_ioctl : wait for completion if amthif control message * @iamthif_canceled : current amthif command is canceled * * @init_work : work item for the device init @@ -588,7 +587,6 @@ struct mei_device { u32 iamthif_msg_buf_index; enum iamthif_states iamthif_state; bool iamthif_flow_control_pending; - bool iamthif_ioctl; bool iamthif_canceled; struct work_struct init_work; From c54bf3ab7536e062b507b625bfd2befb9b2cb841 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:39 +0200 Subject: [PATCH 041/133] mei: iamthif: send flow control as a regular client Reuse common client mechanism for sending flow control hbm message. Add new function mei_amthif_read_start similar to mei_cl_read_start that puts control flow request onto the control write queue and drop mei_amthif_irq_read function Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 134 +++++++++++++++++++---------------- drivers/misc/mei/interrupt.c | 25 ++----- drivers/misc/mei/mei_dev.h | 2 - 3 files changed, 77 insertions(+), 84 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 788b00e23353..e6f7180fd8f2 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -254,6 +254,47 @@ out: return rets; } +/** + * mei_amthif_read_start - queue message for sending read credential + * + * @cl: host client + * @file: file pointer of message recipient + * + * Return: 0 on success, <0 on failure. + */ +static int mei_amthif_read_start(struct mei_cl *cl, struct file *file) +{ + struct mei_device *dev = cl->dev; + struct mei_cl_cb *cb; + size_t length = dev->iamthif_mtu; + int rets; + + cb = mei_io_cb_init(cl, file); + if (!cb) { + rets = -ENOMEM; + goto err; + } + + rets = mei_io_cb_alloc_resp_buf(cb, length); + if (rets) + goto err; + + cb->fop_type = MEI_FOP_READ; + list_add_tail(&cb->list, &dev->ctrl_wr_list.list); + + dev->iamthif_msg_buf_index = 0; + dev->iamthif_msg_buf_size = 0; + + dev->iamthif_state = MEI_IAMTHIF_READING; + dev->iamthif_file_object = cb->file_object; + dev->iamthif_current_cb = cb; + + return 0; +err: + mei_io_cb_free(cb); + return rets; +} + /** * mei_amthif_send_cmd - send amthif command to the ME * @@ -287,6 +328,7 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) if (ret < 0) return ret; + cb->fop_type = MEI_FOP_WRITE; if (ret && mei_hbuf_acquire(dev)) { ret = 0; if (cb->request_buffer.size > mei_hbuf_max_len(dev)) { @@ -309,19 +351,17 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) if (mei_hdr.msg_complete) { if (mei_cl_flow_ctrl_reduce(cl)) return -EIO; - dev->iamthif_flow_control_pending = true; - dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; - dev_dbg(dev->dev, "add amthif cb to write waiting list\n"); - dev->iamthif_current_cb = cb; - dev->iamthif_file_object = cb->file_object; + cb->status = mei_amthif_read_start(cl, cb->file_object); list_add_tail(&cb->list, &dev->write_waiting_list.list); } else { dev_dbg(dev->dev, "message does not complete, so add amthif cb to write list.\n"); list_add_tail(&cb->list, &dev->write_list.list); } } else { + list_add_tail(&cb->list, &dev->write_list.list); } + return 0; } @@ -336,17 +376,10 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) */ int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb) { - int ret; - if (!dev || !cb) return -ENODEV; - ret = mei_io_cb_alloc_resp_buf(cb, dev->iamthif_mtu); - if (ret) - return ret; - cb->fop_type = MEI_FOP_WRITE; - if (!list_empty(&dev->amthif_cmd_list.list) || dev->iamthif_state != MEI_IAMTHIF_IDLE) { dev_dbg(dev->dev, @@ -383,7 +416,7 @@ void mei_amthif_run_next_cmd(struct mei_device *dev) typeof(*cb), list); if (!cb) return; - list_del(&cb->list); + list_del_init(&cb->list); ret = mei_amthif_send_cmd(dev, cb); if (ret) dev_warn(dev->dev, "amthif write failed status = %d\n", ret); @@ -483,13 +516,7 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, cl->status = 0; if (mei_hdr.msg_complete) { - dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; - dev->iamthif_flow_control_pending = true; - - /* save iamthif cb sent to amthif client */ - cb->buf_idx = dev->iamthif_msg_buf_index; - dev->iamthif_current_cb = cb; - + cb->status = mei_amthif_read_start(cl, cb->file_object); list_move_tail(&cb->list, &dev->write_waiting_list.list); } @@ -505,7 +532,7 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, * @mei_hdr: header of amthif message * @complete_list: completed callbacks list * - * Return: -ENODEV if cb is NULL 0 otherwise; error message is in cb->status + * Return: Always 0; error message is in cb->status */ int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, @@ -514,7 +541,6 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_device *dev; struct mei_cl_cb *cb; unsigned char *buffer; - int ret = 0; dev = cl->dev; @@ -524,10 +550,13 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, if (dev->iamthif_state != MEI_IAMTHIF_READING) goto err; - cb = dev->iamthif_current_cb; + list_for_each_entry(cb, &dev->read_list.list, list) { + if (cl == cb->cl) + break; + } - if (!cb) { - ret = -ENODEV; + if (&cb->list == &dev->read_list.list) { + dev_err(dev->dev, "no reader found\n"); goto err; } @@ -553,7 +582,7 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, cb->read_time = jiffies; dev_dbg(dev->dev, "complete the amthif read cb.\n "); - list_add_tail(&cb->list, &complete_list->list); + list_move_tail(&cb->list, &complete_list->list); return 0; @@ -561,38 +590,6 @@ err: mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n", MEI_HDR_PRM(mei_hdr)); - return ret; -} - -/** - * mei_amthif_irq_read - prepares to read amthif data. - * - * @dev: the device structure. - * @slots: free slots. - * - * Return: 0, OK; otherwise, error. - */ -int mei_amthif_irq_read(struct mei_device *dev, s32 *slots) -{ - u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); - - if (*slots < msg_slots) - return -EMSGSIZE; - - *slots -= msg_slots; - - if (mei_hbm_cl_flow_control_req(dev, &dev->iamthif_cl)) { - dev_dbg(dev->dev, "iamthif flow control failed\n"); - return -EIO; - } - - dev_dbg(dev->dev, "iamthif flow control success\n"); - dev->iamthif_state = MEI_IAMTHIF_READING; - dev->iamthif_flow_control_pending = false; - dev->iamthif_msg_buf_index = 0; - dev->iamthif_msg_buf_size = 0; - dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER; - dev->hbuf_is_ready = mei_hbuf_is_ready(dev); return 0; } @@ -604,17 +601,32 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots) */ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) { + + if (cb->fop_type == MEI_FOP_WRITE) { + if (!cb->status) { + dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER; + mei_io_cb_free(cb); + return; + } + /* + * in case of error enqueue the write cb to complete read list + * so it can be propagated to the reader + */ + list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list); + wake_up_interruptible(&dev->iamthif_cl.wait); + return; + } + if (dev->iamthif_canceled != 1) { dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE; dev->iamthif_stall_timer = 0; memcpy(cb->response_buffer.data, - dev->iamthif_msg_buf, - dev->iamthif_msg_buf_index); + dev->iamthif_msg_buf, dev->iamthif_msg_buf_index); list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list); dev_dbg(dev->dev, "amthif read completed\n"); dev->iamthif_timer = jiffies; dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n", - dev->iamthif_timer); + dev->iamthif_timer); } else { mei_amthif_run_next_cmd(dev); } diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 4cb602fba593..89f2fbce160f 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -43,7 +43,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) list_for_each_entry_safe(cb, next, &compl_list->list, list) { cl = cb->cl; - list_del(&cb->list); + list_del_init(&cb->list); dev_dbg(dev->dev, "completing call back.\n"); if (cl == &dev->iamthif_cl) @@ -386,11 +386,6 @@ int mei_irq_read_handler(struct mei_device *dev, if (cl == &dev->iamthif_cl) { ret = mei_amthif_irq_read_msg(cl, mei_hdr, cmpl_list); - if (ret) { - dev_err(dev->dev, "mei_amthif_irq_read_msg failed = %d\n", - ret); - goto end; - } } else { ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list); } @@ -448,21 +443,9 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) cl = cb->cl; cl->status = 0; - list_del(&cb->list); - if (cb->fop_type == MEI_FOP_WRITE && - cl != &dev->iamthif_cl) { - cl_dbg(dev, cl, "MEI WRITE COMPLETE\n"); - cl->writing_state = MEI_WRITE_COMPLETE; - list_add_tail(&cb->list, &cmpl_list->list); - } - if (cl == &dev->iamthif_cl) { - cl_dbg(dev, cl, "check iamthif flow control.\n"); - if (dev->iamthif_flow_control_pending) { - ret = mei_amthif_irq_read(dev, &slots); - if (ret) - return ret; - } - } + cl_dbg(dev, cl, "MEI WRITE COMPLETE\n"); + cl->writing_state = MEI_WRITE_COMPLETE; + list_move_tail(&cb->list, &cmpl_list->list); } if (dev->wd_state == MEI_WD_STOPPING) { diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 6cc68de580ba..fc460af131d4 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -487,7 +487,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @iamthif_msg_buf_size : size of current amthif message request buffer * @iamthif_msg_buf_index : current index in amthif message request buffer * @iamthif_state : amthif processor state - * @iamthif_flow_control_pending: amthif waits for flow control * @iamthif_canceled : current amthif command is canceled * * @init_work : work item for the device init @@ -586,7 +585,6 @@ struct mei_device { u32 iamthif_msg_buf_size; u32 iamthif_msg_buf_index; enum iamthif_states iamthif_state; - bool iamthif_flow_control_pending; bool iamthif_canceled; struct work_struct init_work; From 8660172e1d6528be02eba78516ff8282e694bb26 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:40 +0200 Subject: [PATCH 042/133] mei: iamthif: use client write functions Reduce code duplication in amthif code by reusing regular client write functions. Add completed flag to cb so amthif client can add rx credits on write completion Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 180 ++++++++++--------------------------- drivers/misc/mei/client.c | 2 + drivers/misc/mei/main.c | 2 +- drivers/misc/mei/mei_dev.h | 8 +- 4 files changed, 52 insertions(+), 140 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index e6f7180fd8f2..916625a8f037 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -298,110 +298,47 @@ err: /** * mei_amthif_send_cmd - send amthif command to the ME * - * @dev: the device structure + * @cl: the host client * @cb: mei call back struct * * Return: 0 on success, <0 on failure. - * */ -static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) +static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb) { - struct mei_msg_hdr mei_hdr; - struct mei_cl *cl; + struct mei_device *dev; int ret; - if (!dev || !cb) + if (!cl->dev || !cb) return -ENODEV; - dev_dbg(dev->dev, "write data to amthif client.\n"); + dev = cl->dev; dev->iamthif_state = MEI_IAMTHIF_WRITING; dev->iamthif_current_cb = cb; dev->iamthif_file_object = cb->file_object; dev->iamthif_canceled = false; - dev->iamthif_msg_buf_size = cb->request_buffer.size; - memcpy(dev->iamthif_msg_buf, cb->request_buffer.data, - cb->request_buffer.size); - cl = &dev->iamthif_cl; - ret = mei_cl_flow_ctrl_creds(cl); + ret = mei_cl_write(cl, cb, false); if (ret < 0) return ret; - cb->fop_type = MEI_FOP_WRITE; - if (ret && mei_hbuf_acquire(dev)) { - ret = 0; - if (cb->request_buffer.size > mei_hbuf_max_len(dev)) { - mei_hdr.length = mei_hbuf_max_len(dev); - mei_hdr.msg_complete = 0; - } else { - mei_hdr.length = cb->request_buffer.size; - mei_hdr.msg_complete = 1; - } - - mei_hdr.host_addr = cl->host_client_id; - mei_hdr.me_addr = cl->me_client_id; - mei_hdr.reserved = 0; - mei_hdr.internal = 0; - dev->iamthif_msg_buf_index += mei_hdr.length; - ret = mei_write_message(dev, &mei_hdr, dev->iamthif_msg_buf); - if (ret) - return ret; - - if (mei_hdr.msg_complete) { - if (mei_cl_flow_ctrl_reduce(cl)) - return -EIO; - cb->status = mei_amthif_read_start(cl, cb->file_object); - list_add_tail(&cb->list, &dev->write_waiting_list.list); - } else { - dev_dbg(dev->dev, "message does not complete, so add amthif cb to write list.\n"); - list_add_tail(&cb->list, &dev->write_list.list); - } - } else { - - list_add_tail(&cb->list, &dev->write_list.list); - } + if (cb->completed) + cb->status = mei_amthif_read_start(cl, cb->file_object); return 0; } -/** - * mei_amthif_write - write amthif data to amthif client - * - * @dev: the device structure - * @cb: mei call back struct - * - * Return: 0 on success, <0 on failure. - * - */ -int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb) -{ - if (!dev || !cb) - return -ENODEV; - - cb->fop_type = MEI_FOP_WRITE; - if (!list_empty(&dev->amthif_cmd_list.list) || - dev->iamthif_state != MEI_IAMTHIF_IDLE) { - dev_dbg(dev->dev, - "amthif state = %d\n", dev->iamthif_state); - dev_dbg(dev->dev, "AMTHIF: add cb to the wait list\n"); - list_add_tail(&cb->list, &dev->amthif_cmd_list.list); - return 0; - } - return mei_amthif_send_cmd(dev, cb); -} /** * mei_amthif_run_next_cmd - send next amt command from queue * * @dev: the device structure + * + * Return: 0 on success, <0 on failure. */ -void mei_amthif_run_next_cmd(struct mei_device *dev) +int mei_amthif_run_next_cmd(struct mei_device *dev) { + struct mei_cl *cl = &dev->iamthif_cl; struct mei_cl_cb *cb; - int ret; - - if (!dev) - return; dev->iamthif_msg_buf_size = 0; dev->iamthif_msg_buf_index = 0; @@ -415,13 +352,37 @@ void mei_amthif_run_next_cmd(struct mei_device *dev) cb = list_first_entry_or_null(&dev->amthif_cmd_list.list, typeof(*cb), list); if (!cb) - return; + return 0; + list_del_init(&cb->list); - ret = mei_amthif_send_cmd(dev, cb); - if (ret) - dev_warn(dev->dev, "amthif write failed status = %d\n", ret); + return mei_amthif_send_cmd(cl, cb); } +/** + * mei_amthif_write - write amthif data to amthif client + * + * @cl: host client + * @cb: mei call back struct + * + * Return: 0 on success, <0 on failure. + */ +int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) +{ + + struct mei_device *dev; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + if (WARN_ON(!cb)) + return -EINVAL; + + dev = cl->dev; + + cb->fop_type = MEI_FOP_WRITE; + list_add_tail(&cb->list, &dev->amthif_cmd_list.list); + return mei_amthif_run_next_cmd(dev); +} unsigned int mei_amthif_poll(struct mei_device *dev, struct file *file, poll_table *wait) @@ -461,65 +422,14 @@ unsigned int mei_amthif_poll(struct mei_device *dev, int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) { - struct mei_device *dev = cl->dev; - struct mei_msg_hdr mei_hdr; - size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index; - u32 msg_slots = mei_data2slots(len); - int slots; - int rets; + int ret; - rets = mei_cl_flow_ctrl_creds(cl); - if (rets < 0) - return rets; + ret = mei_cl_irq_write(cl, cb, cmpl_list); + if (ret) + return ret; - if (rets == 0) { - cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); - return 0; - } - - mei_hdr.host_addr = cl->host_client_id; - mei_hdr.me_addr = cl->me_client_id; - mei_hdr.reserved = 0; - mei_hdr.internal = 0; - - slots = mei_hbuf_empty_slots(dev); - - if (slots >= msg_slots) { - mei_hdr.length = len; - mei_hdr.msg_complete = 1; - /* Split the message only if we can write the whole host buffer */ - } else if (slots == dev->hbuf_depth) { - msg_slots = slots; - len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); - mei_hdr.length = len; - mei_hdr.msg_complete = 0; - } else { - /* wait for next time the host buffer is empty */ - return 0; - } - - dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); - - rets = mei_write_message(dev, &mei_hdr, - dev->iamthif_msg_buf + dev->iamthif_msg_buf_index); - if (rets) { - dev->iamthif_state = MEI_IAMTHIF_IDLE; - cl->status = rets; - list_del(&cb->list); - return rets; - } - - if (mei_cl_flow_ctrl_reduce(cl)) - return -EIO; - - dev->iamthif_msg_buf_index += mei_hdr.length; - cl->status = 0; - - if (mei_hdr.msg_complete) { + if (cb->completed) cb->status = mei_amthif_read_start(cl, cb->file_object); - list_move_tail(&cb->list, &dev->write_waiting_list.list); - } - return 0; } diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 3e9cf0db5540..d9f4e28ac972 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1106,6 +1106,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, cl->status = 0; cl->writing_state = MEI_WRITING; cb->buf_idx += mei_hdr.length; + cb->completed = mei_hdr.msg_complete == 1; if (mei_hdr.msg_complete) { if (mei_cl_flow_ctrl_reduce(cl)) @@ -1194,6 +1195,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) cl->writing_state = MEI_WRITING; cb->buf_idx = mei_hdr.length; + cb->completed = mei_hdr.msg_complete == 1; out: if (mei_hdr.msg_complete) { diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index cbdbf4af2bf7..9d1a8cba81c9 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -401,7 +401,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, } if (cl == &dev->iamthif_cl) { - rets = mei_amthif_write(dev, write_cb); + rets = mei_amthif_write(cl, write_cb); if (rets) { dev_err(dev->dev, diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index fc460af131d4..2f2242f1bed1 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -201,6 +201,7 @@ struct mei_cl; * @file_object: pointer to file structure * @status: io status of the cb * @internal: communication between driver and FW flag + * @completed: the transfer or reception has completed */ struct mei_cl_cb { struct list_head list; @@ -213,6 +214,7 @@ struct mei_cl_cb { struct file *file_object; int status; u32 internal:1; + u32 completed:1; }; /** @@ -662,8 +664,6 @@ void mei_amthif_reset_params(struct mei_device *dev); int mei_amthif_host_init(struct mei_device *dev); -int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *priv_cb); - int mei_amthif_read(struct mei_device *dev, struct file *file, char __user *ubuf, size_t length, loff_t *offset); @@ -675,8 +675,8 @@ int mei_amthif_release(struct mei_device *dev, struct file *file); struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, struct file *file); -void mei_amthif_run_next_cmd(struct mei_device *dev); - +int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb); +int mei_amthif_run_next_cmd(struct mei_device *dev); int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); From 331e4187017e5dc12fddfcca3f8041e5610ea23b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:41 +0200 Subject: [PATCH 043/133] mei: iamthif: use regular client read functions Reduce code duplication in amthif by reusing regular client read functions. The change also removes the need for amthif own buffering Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 71 ++++-------------------------------- drivers/misc/mei/client.h | 2 + drivers/misc/mei/interrupt.c | 37 +++++++++++++------ drivers/misc/mei/mei_dev.h | 6 --- 4 files changed, 35 insertions(+), 81 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 916625a8f037..4060e2f40286 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -49,8 +49,6 @@ void mei_amthif_reset_params(struct mei_device *dev) { /* reset iamthif parameters. */ dev->iamthif_current_cb = NULL; - dev->iamthif_msg_buf_size = 0; - dev->iamthif_msg_buf_index = 0; dev->iamthif_canceled = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; @@ -69,7 +67,6 @@ int mei_amthif_host_init(struct mei_device *dev) { struct mei_cl *cl = &dev->iamthif_cl; struct mei_me_client *me_cl; - unsigned char *msg_buf; int ret; dev->iamthif_state = MEI_IAMTHIF_IDLE; @@ -90,18 +87,6 @@ int mei_amthif_host_init(struct mei_device *dev) dev->iamthif_mtu = me_cl->props.max_msg_length; dev_dbg(dev->dev, "IAMTHIF_MTU = %d\n", dev->iamthif_mtu); - kfree(dev->iamthif_msg_buf); - dev->iamthif_msg_buf = NULL; - - /* allocate storage for ME message buffer */ - msg_buf = kcalloc(dev->iamthif_mtu, - sizeof(unsigned char), GFP_KERNEL); - 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) { @@ -282,9 +267,6 @@ static int mei_amthif_read_start(struct mei_cl *cl, struct file *file) cb->fop_type = MEI_FOP_READ; list_add_tail(&cb->list, &dev->ctrl_wr_list.list); - dev->iamthif_msg_buf_index = 0; - dev->iamthif_msg_buf_size = 0; - dev->iamthif_state = MEI_IAMTHIF_READING; dev->iamthif_file_object = cb->file_object; dev->iamthif_current_cb = cb; @@ -340,8 +322,6 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) struct mei_cl *cl = &dev->iamthif_cl; struct mei_cl_cb *cb; - dev->iamthif_msg_buf_size = 0; - dev->iamthif_msg_buf_index = 0; dev->iamthif_canceled = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; @@ -440,66 +420,33 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, * * @cl: mei client * @mei_hdr: header of amthif message - * @complete_list: completed callbacks list + * @cmpl_list: completed callbacks list * - * Return: Always 0; error message is in cb->status + * Return: -ENODEV if cb is NULL 0 otherwise; error message is in cb->status */ int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, - struct mei_cl_cb *complete_list) + struct mei_cl_cb *cmpl_list) { struct mei_device *dev; - struct mei_cl_cb *cb; - unsigned char *buffer; + int ret; dev = cl->dev; - if (cl->state != MEI_FILE_CONNECTED) - goto err; - if (dev->iamthif_state != MEI_IAMTHIF_READING) - goto err; + return 0; - list_for_each_entry(cb, &dev->read_list.list, list) { - if (cl == cb->cl) - break; - } - - if (&cb->list == &dev->read_list.list) { - dev_err(dev->dev, "no reader found\n"); - goto err; - } - - if (dev->iamthif_mtu < dev->iamthif_msg_buf_index + mei_hdr->length) { - cb->status = -ERANGE; - goto err; - } - - buffer = dev->iamthif_msg_buf + dev->iamthif_msg_buf_index; - mei_read_slots(dev, buffer, mei_hdr->length); - - dev->iamthif_msg_buf_index += mei_hdr->length; + ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list); + if (ret) + return ret; if (!mei_hdr->msg_complete) return 0; dev_dbg(dev->dev, "completed amthif read.\n "); - dev->iamthif_current_cb = NULL; - dev->iamthif_stall_timer = 0; - cb->buf_idx = dev->iamthif_msg_buf_index; - cb->read_time = jiffies; - dev_dbg(dev->dev, "complete the amthif read cb.\n "); - list_move_tail(&cb->list, &complete_list->list); - - return 0; - -err: - mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); - dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n", - MEI_HDR_PRM(mei_hdr)); return 0; } @@ -530,8 +477,6 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) if (dev->iamthif_canceled != 1) { dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE; dev->iamthif_stall_timer = 0; - memcpy(cb->response_buffer.data, - dev->iamthif_msg_buf, dev->iamthif_msg_buf_index); list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list); dev_dbg(dev->dev, "amthif read completed\n"); dev->iamthif_timer = jiffies; diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 80386f9c27e9..21cf626e8908 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -102,6 +102,8 @@ bool mei_cl_is_other_connecting(struct mei_cl *cl); int mei_cl_disconnect(struct mei_cl *cl); int mei_cl_connect(struct mei_cl *cl, struct file *file); int mei_cl_read_start(struct mei_cl *cl, size_t length); +int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr, + struct mei_cl_cb *cmpl_list); int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking); int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 89f2fbce160f..466c1d22fb16 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -81,6 +81,24 @@ static bool mei_cl_is_reading(struct mei_cl *cl) cl->reading_state != MEI_READ_COMPLETE; } +/** + * mei_irq_discard_msg - discard received message + * + * @dev: mei device + * @hdr: message header + */ +static inline +void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr) +{ + /* + * no need to check for size as it is guarantied + * that length fits into rd_msg_buf + */ + mei_read_slots(dev, dev->rd_msg_buf, hdr->length); + dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n", + MEI_HDR_PRM(hdr)); +} + /** * mei_cl_irq_read_msg - process client message * @@ -90,9 +108,9 @@ static bool mei_cl_is_reading(struct mei_cl *cl) * * Return: always 0 */ -static int mei_cl_irq_read_msg(struct mei_cl *cl, - struct mei_msg_hdr *mei_hdr, - struct mei_cl_cb *complete_list) +int mei_cl_irq_read_msg(struct mei_cl *cl, + struct mei_msg_hdr *mei_hdr, + struct mei_cl_cb *complete_list) { struct mei_device *dev = cl->dev; struct mei_cl_cb *cb; @@ -144,20 +162,17 @@ static int mei_cl_irq_read_msg(struct mei_cl *cl, mei_read_slots(dev, buffer, mei_hdr->length); cb->buf_idx += mei_hdr->length; + if (mei_hdr->msg_complete) { + cb->read_time = jiffies; cl_dbg(dev, cl, "completed read length = %lu\n", cb->buf_idx); list_move_tail(&cb->list, &complete_list->list); } out: - if (!buffer) { - /* assume that mei_hdr->length <= MEI_RD_MSG_BUF_SIZE */ - BUG_ON(mei_hdr->length > MEI_RD_MSG_BUF_SIZE); - mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); - dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n", - MEI_HDR_PRM(mei_hdr)); - } + if (!buffer) + mei_irq_discard_msg(dev, mei_hdr); return 0; } @@ -569,8 +584,6 @@ void mei_timer(struct work_struct *work) if (--dev->iamthif_stall_timer == 0) { dev_err(dev->dev, "timer: amthif hanged.\n"); mei_reset(dev); - dev->iamthif_msg_buf_size = 0; - dev->iamthif_msg_buf_index = 0; dev->iamthif_canceled = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 2f2242f1bed1..57a47d6b63ee 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -485,9 +485,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @iamthif_mtu : amthif client max message length * @iamthif_timer : time stamp of current amthif command completion * @iamthif_stall_timer : timer to detect amthif hang - * @iamthif_msg_buf : amthif current message buffer - * @iamthif_msg_buf_size : size of current amthif message request buffer - * @iamthif_msg_buf_index : current index in amthif message request buffer * @iamthif_state : amthif processor state * @iamthif_canceled : current amthif command is canceled * @@ -583,9 +580,6 @@ struct mei_device { int iamthif_mtu; unsigned long iamthif_timer; u32 iamthif_stall_timer; - unsigned char *iamthif_msg_buf; /* Note: memory has to be allocated */ - u32 iamthif_msg_buf_size; - u32 iamthif_msg_buf_index; enum iamthif_states iamthif_state; bool iamthif_canceled; From 5db7514d9333c920791538c850cfb9dbd19025f7 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:42 +0200 Subject: [PATCH 044/133] mei: use only one buffer in callback The callback structure is used exclusively for reading or writing therefore there is no reason to hold both response and request buffers in the callback structure Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 8 +++--- drivers/misc/mei/bus.c | 6 ++--- drivers/misc/mei/client.c | 47 ++++++++---------------------------- drivers/misc/mei/client.h | 3 +-- drivers/misc/mei/interrupt.c | 16 ++++++------ drivers/misc/mei/main.c | 8 +++--- drivers/misc/mei/mei_dev.h | 6 ++--- 7 files changed, 31 insertions(+), 63 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 4060e2f40286..2cc41cb3bb38 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -213,15 +213,15 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, * remove message from deletion list */ - dev_dbg(dev->dev, "amthif cb->response_buffer size - %d\n", - cb->response_buffer.size); + dev_dbg(dev->dev, "amthif cb->buf size - %d\n", + cb->buf.size); dev_dbg(dev->dev, "amthif cb->buf_idx - %lu\n", cb->buf_idx); /* length is being truncated to PAGE_SIZE, however, * the buf_idx may point beyond */ length = min_t(size_t, length, (cb->buf_idx - *offset)); - if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) { + if (copy_to_user(ubuf, cb->buf.data + *offset, length)) { dev_dbg(dev->dev, "failed to copy data to userland\n"); rets = -EFAULT; } else { @@ -260,7 +260,7 @@ static int mei_amthif_read_start(struct mei_cl *cl, struct file *file) goto err; } - rets = mei_io_cb_alloc_resp_buf(cb, length); + rets = mei_io_cb_alloc_buf(cb, length); if (rets) goto err; diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 025626f4467d..36b949a0fddb 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -261,11 +261,11 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, goto out; } - rets = mei_io_cb_alloc_req_buf(cb, length); + rets = mei_io_cb_alloc_buf(cb, length); if (rets < 0) goto out; - memcpy(cb->request_buffer.data, buf, length); + memcpy(cb->buf.data, buf, length); rets = mei_cl_write(cl, cb, blocking); @@ -328,7 +328,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) } r_length = min_t(size_t, length, cb->buf_idx); - memcpy(buf, cb->response_buffer.data, r_length); + memcpy(buf, cb->buf.data, r_length); rets = r_length; free: diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index d9f4e28ac972..5ecb6cc79d70 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -376,8 +376,7 @@ void mei_io_cb_free(struct mei_cl_cb *cb) if (cb == NULL) return; - kfree(cb->request_buffer.data); - kfree(cb->response_buffer.data); + kfree(cb->buf.data); kfree(cb); } @@ -406,7 +405,7 @@ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) } /** - * mei_io_cb_alloc_req_buf - allocate request buffer + * mei_io_cb_alloc_buf - allocate callback buffer * * @cb: io callback structure * @length: size of the buffer @@ -415,7 +414,7 @@ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) * -EINVAL if cb is NULL * -ENOMEM if allocation failed */ -int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length) +int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length) { if (!cb) return -EINVAL; @@ -423,38 +422,12 @@ int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length) if (length == 0) return 0; - cb->request_buffer.data = kmalloc(length, GFP_KERNEL); - if (!cb->request_buffer.data) + cb->buf.data = kmalloc(length, GFP_KERNEL); + if (!cb->buf.data) return -ENOMEM; - cb->request_buffer.size = length; + cb->buf.size = length; return 0; } -/** - * mei_io_cb_alloc_resp_buf - allocate response buffer - * - * @cb: io callback structure - * @length: size of the buffer - * - * Return: 0 on success - * -EINVAL if cb is NULL - * -ENOMEM if allocation failed - */ -int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) -{ - if (!cb) - return -EINVAL; - - if (length == 0) - return 0; - - cb->response_buffer.data = kmalloc(length, GFP_KERNEL); - if (!cb->response_buffer.data) - return -ENOMEM; - cb->response_buffer.size = length; - return 0; -} - - /** * mei_cl_flush_queues - flushes queue lists belonging to cl. @@ -1005,7 +978,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) goto out; } - rets = mei_io_cb_alloc_resp_buf(cb, length); + rets = mei_io_cb_alloc_buf(cb, length); if (rets) goto out; @@ -1059,7 +1032,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, dev = cl->dev; - buf = &cb->request_buffer; + buf = &cb->buf; rets = mei_cl_flow_ctrl_creds(cl); if (rets < 0) @@ -1094,7 +1067,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, } cl_dbg(dev, cl, "buf: size = %d idx = %lu\n", - cb->request_buffer.size, cb->buf_idx); + cb->buf.size, cb->buf_idx); rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); if (rets) { @@ -1144,7 +1117,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) dev = cl->dev; - buf = &cb->request_buffer; + buf = &cb->buf; cl_dbg(dev, cl, "size=%d\n", buf->size); diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 21cf626e8908..d430a6e09ae8 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -49,8 +49,7 @@ void mei_me_cl_rm_all(struct mei_device *dev); */ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp); void mei_io_cb_free(struct mei_cl_cb *priv_cb); -int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length); -int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length); +int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length); /** diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 466c1d22fb16..60469a0053bb 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -134,19 +134,17 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, cl->reading_state = MEI_READING; - if (cb->response_buffer.size == 0 || - cb->response_buffer.data == NULL) { + if (cb->buf.size == 0 || cb->buf.data == NULL) { cl_err(dev, cl, "response buffer is not allocated.\n"); list_move_tail(&cb->list, &complete_list->list); cb->status = -ENOMEM; goto out; } - if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) { + if (cb->buf.size < mei_hdr->length + cb->buf_idx) { cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n", - cb->response_buffer.size, mei_hdr->length, cb->buf_idx); - buffer = krealloc(cb->response_buffer.data, - mei_hdr->length + cb->buf_idx, + cb->buf.size, mei_hdr->length, cb->buf_idx); + buffer = krealloc(cb->buf.data, mei_hdr->length + cb->buf_idx, GFP_KERNEL); if (!buffer) { @@ -154,11 +152,11 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, list_move_tail(&cb->list, &complete_list->list); goto out; } - cb->response_buffer.data = buffer; - cb->response_buffer.size = mei_hdr->length + cb->buf_idx; + cb->buf.data = buffer; + cb->buf.size = mei_hdr->length + cb->buf_idx; } - buffer = cb->response_buffer.data + cb->buf_idx; + buffer = cb->buf.data + cb->buf_idx; mei_read_slots(dev, buffer, mei_hdr->length); cb->buf_idx += mei_hdr->length; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 9d1a8cba81c9..1d44d110ed94 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -264,7 +264,7 @@ copy_buffer: } dev_dbg(dev->dev, "buf.size = %d buf.idx= %ld\n", - cb->response_buffer.size, cb->buf_idx); + cb->buf.size, cb->buf_idx); if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) { rets = -EMSGSIZE; goto free; @@ -274,7 +274,7 @@ copy_buffer: * however buf_idx may point beyond that */ length = min_t(size_t, length, cb->buf_idx - *offset); - if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) { + if (copy_to_user(ubuf, cb->buf.data + *offset, length)) { dev_dbg(dev->dev, "failed to copy data to userland\n"); rets = -EFAULT; goto free; @@ -389,11 +389,11 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = -ENOMEM; goto out; } - rets = mei_io_cb_alloc_req_buf(write_cb, length); + rets = mei_io_cb_alloc_buf(write_cb, length); if (rets) goto out; - rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); + rets = copy_from_user(write_cb->buf.data, ubuf, length); if (rets) { dev_dbg(dev->dev, "failed to copy data from userland\n"); rets = -EFAULT; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 57a47d6b63ee..1a0f6e9588b6 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -194,8 +194,7 @@ struct mei_cl; * @list: link in callback queue * @cl: file client who is running this operation * @fop_type: file operation type - * @request_buffer: buffer to store request data - * @response_buffer: buffer to store response data + * @buf: buffer for data associated with the callback * @buf_idx: last read index * @read_time: last read operation time stamp (iamthif) * @file_object: pointer to file structure @@ -207,8 +206,7 @@ struct mei_cl_cb { struct list_head list; struct mei_cl *cl; enum mei_cb_file_ops fop_type; - struct mei_msg_data request_buffer; - struct mei_msg_data response_buffer; + struct mei_msg_data buf; unsigned long buf_idx; unsigned long read_time; struct file *file_object; From bca67d681c4864b74fa5fae9ee47e562d1e272b1 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:43 +0200 Subject: [PATCH 045/133] mei: always initialize the callback with the intended operation type We set the operation type at initialization time as each cb is used only for a single type of operation As a byproduct we add a convenient wrapper for allocating cb with the data buffer. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 4 +-- drivers/misc/mei/bus.c | 14 +++------ drivers/misc/mei/client.c | 64 +++++++++++++++++++++++++-------------- drivers/misc/mei/client.h | 7 +++-- drivers/misc/mei/hbm.c | 3 +- drivers/misc/mei/main.c | 8 ++--- 6 files changed, 55 insertions(+), 45 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 2cc41cb3bb38..3fdd22395b9f 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -254,7 +254,7 @@ static int mei_amthif_read_start(struct mei_cl *cl, struct file *file) size_t length = dev->iamthif_mtu; int rets; - cb = mei_io_cb_init(cl, file); + cb = mei_io_cb_init(cl, MEI_FOP_READ, file); if (!cb) { rets = -ENOMEM; goto err; @@ -264,7 +264,6 @@ static int mei_amthif_read_start(struct mei_cl *cl, struct file *file) if (rets) goto err; - cb->fop_type = MEI_FOP_READ; list_add_tail(&cb->list, &dev->ctrl_wr_list.list); dev->iamthif_state = MEI_IAMTHIF_READING; @@ -359,7 +358,6 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) dev = cl->dev; - cb->fop_type = MEI_FOP_WRITE; list_add_tail(&cb->list, &dev->amthif_cmd_list.list); return mei_amthif_run_next_cmd(dev); } diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 36b949a0fddb..3e6ffed9402a 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -255,16 +255,12 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, goto out; } - cb = mei_io_cb_init(cl, NULL); + cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL); if (!cb) { rets = -ENOMEM; goto out; } - rets = mei_io_cb_alloc_buf(cb, length); - if (rets < 0) - goto out; - memcpy(cb->buf.data, buf, length); rets = mei_cl_write(cl, cb, blocking); @@ -293,7 +289,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) mutex_lock(&dev->device_lock); if (!cl->read_cb) { - rets = mei_cl_read_start(cl, length); + rets = mei_cl_read_start(cl, length, NULL); if (rets < 0) goto out; } @@ -392,7 +388,7 @@ static void mei_bus_event_work(struct work_struct *work) device->events = 0; /* Prepare for the next read */ - mei_cl_read_start(device->cl, 0); + mei_cl_read_start(device->cl, 0, NULL); } int mei_cl_register_event_cb(struct mei_cl_device *device, @@ -406,7 +402,7 @@ int mei_cl_register_event_cb(struct mei_cl_device *device, device->event_context = context; INIT_WORK(&device->event_work, mei_bus_event_work); - mei_cl_read_start(device->cl, 0); + mei_cl_read_start(device->cl, 0, NULL); return 0; } @@ -448,7 +444,7 @@ int mei_cl_enable_device(struct mei_cl_device *device) mutex_unlock(&dev->device_lock); if (device->event_cb && !cl->read_cb) - mei_cl_read_start(device->cl, 0); + mei_cl_read_start(device->cl, 0, NULL); if (!device->ops || !device->ops->enable) return 0; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 5ecb6cc79d70..57461016f1ff 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -384,11 +384,13 @@ void mei_io_cb_free(struct mei_cl_cb *cb) * mei_io_cb_init - allocate and initialize io callback * * @cl: mei client + * @type: operation type * @fp: pointer to file structure * * Return: mei_cl_cb pointer or NULL; */ -struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) +struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, + struct file *fp) { struct mei_cl_cb *cb; @@ -401,6 +403,7 @@ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) cb->file_object = fp; cb->cl = cl; cb->buf_idx = 0; + cb->fop_type = type; return cb; } @@ -429,6 +432,33 @@ int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length) return 0; } +/** + * mei_cl_alloc_cb - a convenient wrapper for allocating read cb + * + * @cl: host client + * @length: size of the buffer + * @type: operation type + * @fp: associated file pointer (might be NULL) + * + * Return: cb on success and NULL on failure + */ +struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, + enum mei_cb_file_ops type, struct file *fp) +{ + struct mei_cl_cb *cb; + + cb = mei_io_cb_init(cl, type, fp); + if (!cb) + return NULL; + + if (mei_io_cb_alloc_buf(cb, length)) { + mei_io_cb_free(cb); + return NULL; + } + + return cb; +} + /** * mei_cl_flush_queues - flushes queue lists belonging to cl. * @@ -688,13 +718,10 @@ int mei_cl_disconnect(struct mei_cl *cl) return rets; } - cb = mei_io_cb_init(cl, NULL); - if (!cb) { - rets = -ENOMEM; + cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL); + rets = cb ? 0 : -ENOMEM; + if (rets) goto free; - } - - cb->fop_type = MEI_FOP_DISCONNECT; if (mei_hbuf_acquire(dev)) { if (mei_hbm_cl_disconnect_req(dev, cl)) { @@ -795,13 +822,10 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) return rets; } - cb = mei_io_cb_init(cl, file); - if (!cb) { - rets = -ENOMEM; + cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file); + rets = cb ? 0 : -ENOMEM; + if (rets) goto out; - } - - cb->fop_type = MEI_FOP_CONNECT; /* run hbuf acquire last so we don't have to undo */ if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { @@ -934,10 +958,11 @@ out: * * @cl: host client * @length: number of bytes to read + * @fp: pointer to file structure * * Return: 0 on success, <0 on failure. */ -int mei_cl_read_start(struct mei_cl *cl, size_t length) +int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp) { struct mei_device *dev; struct mei_cl_cb *cb; @@ -972,17 +997,11 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) return rets; } - cb = mei_io_cb_init(cl, NULL); - if (!cb) { - rets = -ENOMEM; - goto out; - } - - rets = mei_io_cb_alloc_buf(cb, length); + cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp); + rets = cb ? 0 : -ENOMEM; if (rets) goto out; - cb->fop_type = MEI_FOP_READ; if (mei_hbuf_acquire(dev)) { rets = mei_hbm_cl_flow_control_req(dev, cl); if (rets < 0) @@ -1128,7 +1147,6 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) return rets; } - cb->fop_type = MEI_FOP_WRITE; cb->buf_idx = 0; cl->writing_state = MEI_IDLE; diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index d430a6e09ae8..f7d0285b5f57 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -47,7 +47,8 @@ void mei_me_cl_rm_all(struct mei_device *dev); /* * MEI IO Functions */ -struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp); +struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, + struct file *fp); void mei_io_cb_free(struct mei_cl_cb *priv_cb); int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length); @@ -77,6 +78,8 @@ int mei_cl_unlink(struct mei_cl *cl); int mei_cl_flush_queues(struct mei_cl *cl); struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl); +struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, + enum mei_cb_file_ops type, struct file *fp); int mei_cl_flow_ctrl_creds(struct mei_cl *cl); @@ -100,7 +103,7 @@ static inline bool mei_cl_is_transitioning(struct mei_cl *cl) bool mei_cl_is_other_connecting(struct mei_cl *cl); int mei_cl_disconnect(struct mei_cl *cl); int mei_cl_connect(struct mei_cl *cl, struct file *file); -int mei_cl_read_start(struct mei_cl *cl, size_t length); +int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp); int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr, struct mei_cl_cb *cmpl_list); int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking); diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 4f83e9aaa6f9..2c581dcaf3b1 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -684,10 +684,9 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev, cl->state = MEI_FILE_DISCONNECTED; cl->timer_count = 0; - cb = mei_io_cb_init(cl, NULL); + cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, NULL); if (!cb) return -ENOMEM; - cb->fop_type = MEI_FOP_DISCONNECT_RSP; cl_dbg(dev, cl, "add disconnect response as first\n"); list_add(&cb->list, &dev->ctrl_wr_list.list); } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 1d44d110ed94..369de0a070f1 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -209,7 +209,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, *offset = 0; } - err = mei_cl_read_start(cl, length); + err = mei_cl_read_start(cl, length, file); if (err && err != -EBUSY) { dev_dbg(dev->dev, "mei start read failure with status = %d\n", err); @@ -383,15 +383,11 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, } else if (cl->reading_state == MEI_IDLE) *offset = 0; - - write_cb = mei_io_cb_init(cl, file); + write_cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file); if (!write_cb) { rets = -ENOMEM; goto out; } - rets = mei_io_cb_alloc_buf(write_cb, length); - if (rets) - goto out; rets = copy_from_user(write_cb->buf.data, ubuf, length); if (rets) { From 03b8d3419fdfc02d1984a0db51c8b74426e12605 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:44 +0200 Subject: [PATCH 046/133] mei: add mei_cl_alloc_linked function Add convenient wrapper mei_cl_alloc_linked to simplify error handling Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 35 +++++++++++++++++++++++++++++-- drivers/misc/mei/client.h | 2 ++ drivers/misc/mei/main.c | 17 +++++----------- drivers/misc/mei/nfc.c | 43 +++++++++++++++++++-------------------- 4 files changed, 61 insertions(+), 36 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 57461016f1ff..e263c0713a6d 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -547,11 +547,11 @@ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) * mei_cl_link - allocate host id in the host map * * @cl: host client - * @id: fixed host id or -1 for generic one + * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one * * Return: 0 on success * -EINVAL on incorrect values - * -ENONET if client not found + * -EMFILE if open count exceeded. */ int mei_cl_link(struct mei_cl *cl, int id) { @@ -869,6 +869,37 @@ out: return rets; } +/** + * mei_cl_alloc_linked - allocate and link host client + * + * @dev: the device structure + * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one + * + * Return: cl on success ERR_PTR on failure + */ +struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id) +{ + struct mei_cl *cl; + int ret; + + cl = mei_cl_allocate(dev); + if (!cl) { + ret = -ENOMEM; + goto err; + } + + ret = mei_cl_link(cl, id); + if (ret) + goto err; + + return cl; +err: + kfree(cl); + return ERR_PTR(ret); +} + + + /** * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. * diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index f7d0285b5f57..c3d0e200a642 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -75,6 +75,8 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev); int mei_cl_link(struct mei_cl *cl, int id); int mei_cl_unlink(struct mei_cl *cl); +struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id); + int mei_cl_flush_queues(struct mei_cl *cl); struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 369de0a070f1..10fc3a6a1574 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -59,24 +59,18 @@ static int mei_open(struct inode *inode, struct file *file) mutex_lock(&dev->device_lock); - cl = NULL; - - err = -ENODEV; if (dev->dev_state != MEI_DEV_ENABLED) { dev_dbg(dev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", mei_dev_state_str(dev->dev_state)); + err = -ENODEV; goto err_unlock; } - err = -ENOMEM; - cl = mei_cl_allocate(dev); - if (!cl) - goto err_unlock; - - /* open_handle_count check is handled in the mei_cl_link */ - err = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); - if (err) + cl = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY); + if (IS_ERR(cl)) { + err = PTR_ERR(cl); goto err_unlock; + } file->private_data = cl; @@ -86,7 +80,6 @@ static int mei_open(struct inode *inode, struct file *file) err_unlock: mutex_unlock(&dev->device_lock); - kfree(cl); return err; } diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index bb61a119b8bb..c3bcb63686d7 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -482,8 +482,8 @@ err: int mei_nfc_host_init(struct mei_device *dev) { struct mei_nfc_dev *ndev; - struct mei_cl *cl_info, *cl = NULL; - struct mei_me_client *me_cl; + struct mei_cl *cl_info, *cl; + struct mei_me_client *me_cl = NULL; int ret; @@ -500,17 +500,6 @@ int mei_nfc_host_init(struct mei_device *dev) goto err; } - ndev->cl_info = mei_cl_allocate(dev); - ndev->cl = mei_cl_allocate(dev); - - cl = ndev->cl; - cl_info = ndev->cl_info; - - if (!cl || !cl_info) { - ret = -ENOMEM; - goto err; - } - /* check for valid client id */ me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid); if (!me_cl) { @@ -519,17 +508,21 @@ int mei_nfc_host_init(struct mei_device *dev) goto err; } + cl_info = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY); + if (IS_ERR(cl_info)) { + ret = PTR_ERR(cl_info); + goto err; + } + 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) - goto err; - + me_cl = NULL; list_add_tail(&cl_info->device_link, &dev->device_list); + ndev->cl_info = cl_info; + /* check for valid client id */ me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid); if (!me_cl) { @@ -538,16 +531,21 @@ int mei_nfc_host_init(struct mei_device *dev) goto err; } + cl = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY); + if (IS_ERR(cl)) { + ret = PTR_ERR(cl); + goto err; + } + 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) - goto err; + me_cl = NULL; list_add_tail(&cl->device_link, &dev->device_list); + ndev->cl = cl; + ndev->req_id = 1; INIT_WORK(&ndev->init_work, mei_nfc_init); @@ -557,6 +555,7 @@ int mei_nfc_host_init(struct mei_device *dev) return 0; err: + mei_me_cl_put(me_cl); mei_nfc_free(ndev); return ret; From 928fa6664b362aad70c16f04483414f60743e15e Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:45 +0200 Subject: [PATCH 047/133] mei: simplify io callback disposal Simplify disposal of io callback by removing the callback implicitly from its lookup list inside mei_io_cb_free Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 24 ++++----- drivers/misc/mei/bus.c | 22 ++------- drivers/misc/mei/client.c | 94 ++++++++++++++++++------------------ drivers/misc/mei/hbm.c | 2 +- drivers/misc/mei/interrupt.c | 3 +- drivers/misc/mei/main.c | 29 ++--------- 6 files changed, 66 insertions(+), 108 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 3fdd22395b9f..7b6ed0bbfc9c 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -196,16 +196,16 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, if (time_after(jiffies, timeout)) { dev_dbg(dev->dev, "amthif Time out\n"); /* 15 sec for the message has expired */ - list_del(&cb->list); + list_del_init(&cb->list); rets = -ETIME; goto free; } /* if the whole message will fit remove it from the list */ if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset)) - list_del(&cb->list); + list_del_init(&cb->list); else if (cb->buf_idx > 0 && cb->buf_idx <= *offset) { /* end of the message has been reached */ - list_del(&cb->list); + list_del_init(&cb->list); rets = 0; goto free; } @@ -504,26 +504,22 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) static bool mei_clear_list(struct mei_device *dev, const struct file *file, struct list_head *mei_cb_list) { - struct mei_cl_cb *cb_pos = NULL; - struct mei_cl_cb *cb_next = NULL; + struct mei_cl *cl = &dev->iamthif_cl; + struct mei_cl_cb *cb, *next; bool removed = false; /* list all list member */ - list_for_each_entry_safe(cb_pos, cb_next, mei_cb_list, list) { + list_for_each_entry_safe(cb, next, mei_cb_list, list) { /* check if list member associated with a file */ - if (file == cb_pos->file_object) { - /* remove member from the list */ - list_del(&cb_pos->list); + if (file == cb->file_object) { /* check if cb equal to current iamthif cb */ - if (dev->iamthif_current_cb == cb_pos) { + if (dev->iamthif_current_cb == cb) { dev->iamthif_current_cb = NULL; /* send flow control to iamthif client */ - mei_hbm_cl_flow_control_req(dev, - &dev->iamthif_cl); + mei_hbm_cl_flow_control_req(dev, cl); } /* free all allocated buffers */ - mei_io_cb_free(cb_pos); - cb_pos = NULL; + mei_io_cb_free(cb); removed = true; } } diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 3e6ffed9402a..b5385372693d 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -311,13 +311,13 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) mutex_lock(&dev->device_lock); } - cb = cl->read_cb; if (cl->reading_state != MEI_READ_COMPLETE) { rets = 0; goto out; } + cb = cl->read_cb; if (cb->status) { rets = cb->status; goto free; @@ -329,8 +329,8 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) free: mei_io_cb_free(cb); - cl->reading_state = MEI_IDLE; cl->read_cb = NULL; + cl->reading_state = MEI_IDLE; out: mutex_unlock(&dev->device_lock); @@ -486,23 +486,7 @@ int mei_cl_disable_device(struct mei_cl_device *device) /* Flush queues and remove any pending read */ mei_cl_flush_queues(cl); - - if (cl->read_cb) { - struct mei_cl_cb *cb = NULL; - - cb = mei_cl_find_read_cb(cl); - /* Remove entry from read list */ - if (cb) - list_del(&cb->list); - - cb = cl->read_cb; - cl->read_cb = NULL; - - if (cb) { - mei_io_cb_free(cb); - cb = NULL; - } - } + mei_io_cb_free(cl->read_cb); device->event_cb = NULL; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index e263c0713a6d..624bf0182a50 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -320,52 +320,6 @@ static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, (cl1->me_client_id == cl2->me_client_id); } -/** - * __mei_io_list_flush - removes and frees cbs belonging to cl. - * - * @list: an instance of our list structure - * @cl: host client, can be NULL for flushing the whole list - * @free: whether to free the cbs - */ -static void __mei_io_list_flush(struct mei_cl_cb *list, - struct mei_cl *cl, bool free) -{ - struct mei_cl_cb *cb; - struct mei_cl_cb *next; - - /* enable removing everything if no cl is specified */ - list_for_each_entry_safe(cb, next, &list->list, list) { - if (!cl || mei_cl_cmp_id(cl, cb->cl)) { - list_del(&cb->list); - if (free) - mei_io_cb_free(cb); - } - } -} - -/** - * mei_io_list_flush - removes list entry belonging to cl. - * - * @list: An instance of our list structure - * @cl: host client - */ -void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) -{ - __mei_io_list_flush(list, cl, false); -} - - -/** - * mei_io_list_free - removes cb belonging to cl and free them - * - * @list: An instance of our list structure - * @cl: host client - */ -static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) -{ - __mei_io_list_flush(list, cl, true); -} - /** * mei_io_cb_free - free mei_cb_private related memory * @@ -376,6 +330,7 @@ void mei_io_cb_free(struct mei_cl_cb *cb) if (cb == NULL) return; + list_del(&cb->list); kfree(cb->buf.data); kfree(cb); } @@ -398,8 +353,7 @@ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, if (!cb) return NULL; - mei_io_list_init(cb); - + INIT_LIST_HEAD(&cb->list); cb->file_object = fp; cb->cl = cl; cb->buf_idx = 0; @@ -407,6 +361,50 @@ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, return cb; } +/** + * __mei_io_list_flush - removes and frees cbs belonging to cl. + * + * @list: an instance of our list structure + * @cl: host client, can be NULL for flushing the whole list + * @free: whether to free the cbs + */ +static void __mei_io_list_flush(struct mei_cl_cb *list, + struct mei_cl *cl, bool free) +{ + struct mei_cl_cb *cb, *next; + + /* enable removing everything if no cl is specified */ + list_for_each_entry_safe(cb, next, &list->list, list) { + if (!cl || mei_cl_cmp_id(cl, cb->cl)) { + list_del_init(&cb->list); + if (free) + mei_io_cb_free(cb); + } + } +} + +/** + * mei_io_list_flush - removes list entry belonging to cl. + * + * @list: An instance of our list structure + * @cl: host client + */ +void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) +{ + __mei_io_list_flush(list, cl, false); +} + +/** + * mei_io_list_free - removes cb belonging to cl and free them + * + * @list: An instance of our list structure + * @cl: host client + */ +static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) +{ + __mei_io_list_flush(list, cl, true); +} + /** * mei_io_cb_alloc_buf - allocate callback buffer * diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 2c581dcaf3b1..58da92565c5e 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -639,7 +639,7 @@ static void mei_hbm_cl_res(struct mei_device *dev, continue; if (mei_hbm_cl_addr_equal(cl, rs)) { - list_del(&cb->list); + list_del_init(&cb->list); break; } } diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 60469a0053bb..1e2f3c774853 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -202,7 +202,6 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, cl->state = MEI_FILE_DISCONNECTED; cl->status = 0; - list_del(&cb->list); mei_io_cb_free(cb); return ret; @@ -320,7 +319,7 @@ static int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, if (ret) { cl->status = ret; cb->buf_idx = 0; - list_del(&cb->list); + list_del_init(&cb->list); return ret; } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 10fc3a6a1574..c34853be963f 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -94,7 +94,6 @@ err_unlock: static int mei_release(struct inode *inode, struct file *file) { struct mei_cl *cl = file->private_data; - struct mei_cl_cb *cb; struct mei_device *dev; int rets = 0; @@ -118,23 +117,11 @@ static int mei_release(struct inode *inode, struct file *file) mei_cl_unlink(cl); - - /* free read cb */ - cb = NULL; - if (cl->read_cb) { - cb = mei_cl_find_read_cb(cl); - /* Remove entry from read list */ - if (cb) - list_del(&cb->list); - - cb = cl->read_cb; - cl->read_cb = NULL; - } + mei_io_cb_free(cl->read_cb); + cl->read_cb = NULL; file->private_data = NULL; - mei_io_cb_free(cb); - kfree(cl); out: mutex_unlock(&dev->device_lock); @@ -156,7 +143,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, size_t length, loff_t *offset) { struct mei_cl *cl = file->private_data; - struct mei_cl_cb *cb_pos = NULL; struct mei_cl_cb *cb = NULL; struct mei_device *dev; int rets; @@ -279,13 +265,10 @@ copy_buffer: goto out; free: - cb_pos = mei_cl_find_read_cb(cl); - /* Remove entry from read list */ - if (cb_pos) - list_del(&cb_pos->list); mei_io_cb_free(cb); - cl->reading_state = MEI_IDLE; cl->read_cb = NULL; + + cl->reading_state = MEI_IDLE; out: dev_dbg(dev->dev, "end mei read rets= %d\n", rets); mutex_unlock(&dev->device_lock); @@ -355,7 +338,6 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (time_after(jiffies, timeout) || cl->reading_state == MEI_READ_COMPLETE) { *offset = 0; - list_del(&write_cb->list); mei_io_cb_free(write_cb); write_cb = NULL; } @@ -367,11 +349,10 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, *offset = 0; write_cb = mei_cl_find_read_cb(cl); if (write_cb) { - list_del(&write_cb->list); mei_io_cb_free(write_cb); write_cb = NULL; - cl->reading_state = MEI_IDLE; cl->read_cb = NULL; + cl->reading_state = MEI_IDLE; } } else if (cl->reading_state == MEI_IDLE) *offset = 0; From a9bed61053af13c0768f82c9d1c8793515dd067c Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:46 +0200 Subject: [PATCH 048/133] mei: allow read concurrency Replace clunky read state machine with read stack implemented as per client read list, this is important mostly for mei drivers with unsolicited reads Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 36 ++++++++-------- drivers/misc/mei/client.c | 84 +++++++++++++++++++++++------------- drivers/misc/mei/client.h | 7 +-- drivers/misc/mei/debugfs.c | 2 +- drivers/misc/mei/init.c | 1 - drivers/misc/mei/interrupt.c | 35 ++++----------- drivers/misc/mei/main.c | 42 ++++-------------- drivers/misc/mei/mei_dev.h | 10 ++--- 8 files changed, 98 insertions(+), 119 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index b5385372693d..17ca7e20fb6a 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -288,19 +288,20 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) mutex_lock(&dev->device_lock); - if (!cl->read_cb) { - rets = mei_cl_read_start(cl, length, NULL); - if (rets < 0) - goto out; - } + cb = mei_cl_read_cb(cl, NULL); + if (cb) + goto copy; - if (cl->reading_state != MEI_READ_COMPLETE && - !waitqueue_active(&cl->rx_wait)) { + rets = mei_cl_read_start(cl, length, NULL); + if (rets && rets != -EBUSY) + goto out; + + if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) { mutex_unlock(&dev->device_lock); if (wait_event_interruptible(cl->rx_wait, - cl->reading_state == MEI_READ_COMPLETE || + (!list_empty(&cl->rd_completed)) || mei_cl_is_transitioning(cl))) { if (signal_pending(current)) @@ -309,15 +310,20 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) } mutex_lock(&dev->device_lock); + + if (mei_cl_is_transitioning(cl)) { + rets = -EBUSY; + goto out; + } } - - if (cl->reading_state != MEI_READ_COMPLETE) { + cb = mei_cl_read_cb(cl, NULL); + if (!cb) { rets = 0; goto out; } - cb = cl->read_cb; +copy: if (cb->status) { rets = cb->status; goto free; @@ -329,9 +335,6 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) free: mei_io_cb_free(cb); - cl->read_cb = NULL; - cl->reading_state = MEI_IDLE; - out: mutex_unlock(&dev->device_lock); @@ -443,7 +446,7 @@ int mei_cl_enable_device(struct mei_cl_device *device) mutex_unlock(&dev->device_lock); - if (device->event_cb && !cl->read_cb) + if (device->event_cb) mei_cl_read_start(device->cl, 0, NULL); if (!device->ops || !device->ops->enable) @@ -485,8 +488,7 @@ int mei_cl_disable_device(struct mei_cl_device *device) } /* Flush queues and remove any pending read */ - mei_cl_flush_queues(cl); - mei_io_cb_free(cl->read_cb); + mei_cl_flush_queues(cl, NULL); device->event_cb = NULL; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 624bf0182a50..98a5363e1e8a 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -457,14 +457,56 @@ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, return cb; } +/** + * mei_cl_read_cb - find this cl's callback in the read list + * for a specific file + * + * @cl: host client + * @fp: file pointer (matching cb file object), may be NULL + * + * Return: cb on success, NULL if cb is not found + */ +struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp) +{ + struct mei_cl_cb *cb; + + list_for_each_entry(cb, &cl->rd_completed, list) + if (!fp || fp == cb->file_object) + return cb; + + return NULL; +} + +/** + * mei_cl_read_cb_flush - free client's read pending and completed cbs + * for a specific file + * + * @cl: host client + * @fp: file pointer (matching cb file object), may be NULL + */ +void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp) +{ + struct mei_cl_cb *cb, *next; + + list_for_each_entry_safe(cb, next, &cl->rd_completed, list) + if (!fp || fp == cb->file_object) + mei_io_cb_free(cb); + + + list_for_each_entry_safe(cb, next, &cl->rd_pending, list) + if (!fp || fp == cb->file_object) + mei_io_cb_free(cb); +} + /** * mei_cl_flush_queues - flushes queue lists belonging to cl. * * @cl: host client + * @fp: file pointer (matching cb file object), may be NULL * * Return: 0 on success, -EINVAL if cl or cl->dev is NULL. */ -int mei_cl_flush_queues(struct mei_cl *cl) +int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) { struct mei_device *dev; @@ -474,13 +516,15 @@ int mei_cl_flush_queues(struct mei_cl *cl) dev = cl->dev; cl_dbg(dev, cl, "remove list entry belonging to cl\n"); - mei_io_list_flush(&cl->dev->read_list, cl); mei_io_list_free(&cl->dev->write_list, cl); mei_io_list_free(&cl->dev->write_waiting_list, cl); mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl); + + mei_cl_read_cb_flush(cl, fp); + return 0; } @@ -497,9 +541,10 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) init_waitqueue_head(&cl->wait); init_waitqueue_head(&cl->rx_wait); init_waitqueue_head(&cl->tx_wait); + INIT_LIST_HEAD(&cl->rd_completed); + INIT_LIST_HEAD(&cl->rd_pending); INIT_LIST_HEAD(&cl->link); INIT_LIST_HEAD(&cl->device_link); - cl->reading_state = MEI_IDLE; cl->writing_state = MEI_IDLE; cl->dev = dev; } @@ -523,24 +568,6 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev) return cl; } -/** - * mei_cl_find_read_cb - find this cl's callback in the read list - * - * @cl: host client - * - * Return: cb on success, NULL on error - */ -struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) -{ - struct mei_device *dev = cl->dev; - struct mei_cl_cb *cb; - - list_for_each_entry(cb, &dev->read_list.list, list) - if (mei_cl_cmp_id(cl, cb->cl)) - return cb; - return NULL; -} - /** * mei_cl_link - allocate host id in the host map * @@ -1006,10 +1033,10 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp) if (!mei_cl_is_connected(cl)) return -ENODEV; - if (cl->read_cb) { - cl_dbg(dev, cl, "read is pending.\n"); + /* HW currently supports only one pending read */ + if (!list_empty(&cl->rd_pending)) return -EBUSY; - } + 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); @@ -1036,13 +1063,11 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp) if (rets < 0) goto out; - list_add_tail(&cb->list, &dev->read_list.list); + list_add_tail(&cb->list, &cl->rd_pending); } else { list_add_tail(&cb->list, &dev->ctrl_wr_list.list); } - cl->read_cb = cb; - out: cl_dbg(dev, cl, "rpm: autosuspend\n"); pm_runtime_mark_last_busy(dev->dev); @@ -1268,9 +1293,8 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) if (waitqueue_active(&cl->tx_wait)) wake_up_interruptible(&cl->tx_wait); - } else if (cb->fop_type == MEI_FOP_READ && - MEI_READING == cl->reading_state) { - cl->reading_state = MEI_READ_COMPLETE; + } else if (cb->fop_type == MEI_FOP_READ) { + list_add_tail(&cb->list, &cl->rd_completed); if (waitqueue_active(&cl->rx_wait)) wake_up_interruptible(&cl->rx_wait); else diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index c3d0e200a642..eb02f34b2fe0 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -77,11 +77,12 @@ int mei_cl_unlink(struct mei_cl *cl); struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id); -int mei_cl_flush_queues(struct mei_cl *cl); -struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl); - +struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, + const struct file *fp); +void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp); struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, enum mei_cb_file_ops type, struct file *fp); +int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp); int mei_cl_flow_ctrl_creds(struct mei_cl *cl); diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index 50fc6635fab1..d9cd7e6ee484 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -117,7 +117,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, pos += scnprintf(buf + pos, bufsz - pos, "%2d|%2d|%4d|%5d|%2d|%2d|\n", i, cl->me_client_id, cl->host_client_id, cl->state, - cl->reading_state, cl->writing_state); + !list_empty(&cl->rd_completed), cl->writing_state); i++; } out: diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 106c054f573f..4596401888e5 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -395,7 +395,6 @@ void mei_device_init(struct mei_device *dev, dev->dev_state = MEI_DEV_INITIALIZING; dev->reset_count = 0; - mei_io_list_init(&dev->read_list); mei_io_list_init(&dev->write_list); mei_io_list_init(&dev->write_waiting_list); mei_io_list_init(&dev->ctrl_wr_list); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 1e2f3c774853..3f23629759db 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -68,18 +68,6 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl, return cl->host_client_id == mei_hdr->host_addr && cl->me_client_id == mei_hdr->me_addr; } -/** - * mei_cl_is_reading - checks if the client is in reading state - * - * @cl: mei client - * - * Return: true if the client is reading - */ -static bool mei_cl_is_reading(struct mei_cl *cl) -{ - return cl->state == MEI_FILE_CONNECTED && - cl->reading_state != MEI_READ_COMPLETE; -} /** * mei_irq_discard_msg - discard received message @@ -116,24 +104,18 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_cl_cb *cb; unsigned char *buffer = NULL; - list_for_each_entry(cb, &dev->read_list.list, list) { - if (cl == cb->cl) - break; - } - - if (&cb->list == &dev->read_list.list) { - dev_err(dev->dev, "no reader found\n"); + cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list); + if (!cb) { + cl_err(dev, cl, "pending read cb not found\n"); goto out; } - if (!mei_cl_is_reading(cl)) { - cl_err(dev, cl, "cl is not reading state=%d reading state=%d\n", - cl->state, cl->reading_state); + if (cl->state != MEI_FILE_CONNECTED) { + cl_dbg(dev, cl, "not connected\n"); + cb->status = -ENODEV; goto out; } - cl->reading_state = MEI_READING; - if (cb->buf.size == 0 || cb->buf.data == NULL) { cl_err(dev, cl, "response buffer is not allocated.\n"); list_move_tail(&cb->list, &complete_list->list); @@ -163,8 +145,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, if (mei_hdr->msg_complete) { cb->read_time = jiffies; - cl_dbg(dev, cl, "completed read length = %lu\n", - cb->buf_idx); + cl_dbg(dev, cl, "completed read length = %lu\n", cb->buf_idx); list_move_tail(&cb->list, &complete_list->list); } @@ -281,7 +262,7 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, return ret; } - list_move_tail(&cb->list, &dev->read_list.list); + list_move_tail(&cb->list, &cl->rd_pending); return 0; } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index c34853be963f..d80867e0d803 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -112,14 +112,11 @@ static int mei_release(struct inode *inode, struct file *file) cl_dbg(dev, cl, "disconnecting\n"); rets = mei_cl_disconnect(cl); } - mei_cl_flush_queues(cl); + mei_cl_flush_queues(cl, file); cl_dbg(dev, cl, "removing\n"); mei_cl_unlink(cl); - mei_io_cb_free(cl->read_cb); - cl->read_cb = NULL; - file->private_data = NULL; kfree(cl); @@ -143,8 +140,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, size_t length, loff_t *offset) { struct mei_cl *cl = file->private_data; - struct mei_cl_cb *cb = NULL; struct mei_device *dev; + struct mei_cl_cb *cb = NULL; int rets; int err; @@ -171,7 +168,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - cb = cl->read_cb; + cb = mei_cl_read_cb(cl, file); if (cb) { /* read what left */ if (cb->buf_idx > *offset) @@ -196,9 +193,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - if (MEI_READ_COMPLETE != cl->reading_state && - !waitqueue_active(&cl->rx_wait)) { - + if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) { if (file->f_flags & O_NONBLOCK) { rets = -EAGAIN; goto out; @@ -207,7 +202,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, mutex_unlock(&dev->device_lock); if (wait_event_interruptible(cl->rx_wait, - MEI_READ_COMPLETE == cl->reading_state || + (!list_empty(&cl->rd_completed)) || mei_cl_is_transitioning(cl))) { if (signal_pending(current)) @@ -222,14 +217,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, } } - cb = cl->read_cb; - + cb = mei_cl_read_cb(cl, file); if (!cb) { - rets = -ENODEV; - goto out; - } - - if (cl->reading_state != MEI_READ_COMPLETE) { rets = 0; goto out; } @@ -266,9 +255,7 @@ copy_buffer: free: mei_io_cb_free(cb); - cl->read_cb = NULL; - cl->reading_state = MEI_IDLE; out: dev_dbg(dev->dev, "end mei read rets= %d\n", rets); mutex_unlock(&dev->device_lock); @@ -335,8 +322,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, timeout = write_cb->read_time + mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); - if (time_after(jiffies, timeout) || - cl->reading_state == MEI_READ_COMPLETE) { + if (time_after(jiffies, timeout)) { *offset = 0; mei_io_cb_free(write_cb); write_cb = NULL; @@ -344,19 +330,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, } } - /* free entry used in read */ - if (cl->reading_state == MEI_READ_COMPLETE) { - *offset = 0; - write_cb = mei_cl_find_read_cb(cl); - if (write_cb) { - mei_io_cb_free(write_cb); - write_cb = NULL; - cl->read_cb = NULL; - cl->reading_state = MEI_IDLE; - } - } else if (cl->reading_state == MEI_IDLE) - *offset = 0; - + *offset = 0; write_cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file); if (!write_cb) { rets = -ENOMEM; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 1a0f6e9588b6..f066ecd71939 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -231,9 +231,9 @@ struct mei_cl_cb { * @me_client_id: me/fw id * @mei_flow_ctrl_creds: transmit flow credentials * @timer_count: watchdog timer for operation completion - * @reading_state: state of the rx * @writing_state: state of the tx - * @read_cb: current pending reading callback + * @rd_pending: pending read credits + * @rd_completed: completed read * * @device: device on the mei client bus * @device_link: link to bus clients @@ -251,9 +251,9 @@ struct mei_cl { u8 me_client_id; u8 mei_flow_ctrl_creds; u8 timer_count; - enum mei_file_transaction_states reading_state; enum mei_file_transaction_states writing_state; - struct mei_cl_cb *read_cb; + struct list_head rd_pending; + struct list_head rd_completed; /* MEI CL bus data */ struct mei_cl_device *device; @@ -425,7 +425,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @cdev : character device * @minor : minor number allocated for device * - * @read_list : read completion list * @write_list : write pending list * @write_waiting_list : write completion list * @ctrl_wr_list : pending control write list @@ -501,7 +500,6 @@ struct mei_device { struct cdev cdev; int minor; - struct mei_cl_cb read_list; struct mei_cl_cb write_list; struct mei_cl_cb write_waiting_list; struct mei_cl_cb ctrl_wr_list; From b3de8e3719e582f3182bb504295e4a8e43c8c96f Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 10 Feb 2015 10:39:47 +0200 Subject: [PATCH 049/133] mei: bus: call device disable handler prior to disconnection call device's disable handler prior to disconnection so it can possibly close the communication with fw client in graceful way Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 17ca7e20fb6a..45896f95fed1 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -467,37 +467,34 @@ int mei_cl_disable_device(struct mei_cl_device *device) dev = cl->dev; + if (device->ops && device->ops->disable) + device->ops->disable(device); + + device->event_cb = NULL; + mutex_lock(&dev->device_lock); if (cl->state != MEI_FILE_CONNECTED) { - mutex_unlock(&dev->device_lock); dev_err(dev->dev, "Already disconnected"); - - return 0; + err = 0; + goto out; } cl->state = MEI_FILE_DISCONNECTING; err = mei_cl_disconnect(cl); if (err < 0) { - mutex_unlock(&dev->device_lock); - dev_err(dev->dev, - "Could not disconnect from the ME client"); - - return err; + dev_err(dev->dev, "Could not disconnect from the ME client"); + goto out; } /* Flush queues and remove any pending read */ mei_cl_flush_queues(cl, NULL); - device->event_cb = NULL; - +out: mutex_unlock(&dev->device_lock); + return err; - if (!device->ops || !device->ops->disable) - return 0; - - return device->ops->disable(device); } EXPORT_SYMBOL_GPL(mei_cl_disable_device); From ed99d846f1774e14c8705819c12469eb3855b54e Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Mon, 2 Mar 2015 11:49:23 +0800 Subject: [PATCH 050/133] mei: bus: () can be static drivers/hv/vmbus_drv.c:51:5: sparse: symbol 'hyperv_panic_event' was not declared. Should it be static? drivers/hv/vmbus_drv.c:51:5: sparse: symbol 'hyperv_panic_event' was not declared. Should it be static? drivers/hv/vmbus_drv.c:51:5: sparse: symbol 'hyperv_panic_event' was not declared. Should it be static? drivers/hv/vmbus_drv.c:51:5: sparse: symbol 'hyperv_panic_event' was not declared. Should it be static? Signed-off-by: Fengguang Wu Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 526fa8b19cda..8313e25378cb 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -48,7 +48,7 @@ static struct completion probe_event; static int irq; -int hyperv_panic_event(struct notifier_block *nb, +static int hyperv_panic_event(struct notifier_block *nb, unsigned long event, void *ptr) { struct pt_regs *regs; From 112bdfaa525fd5993e17885861342893f15290b0 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 16 Feb 2015 15:41:02 +0000 Subject: [PATCH 051/133] extcon: arizona: Deobfuscate arizona_extcon_do_magic arizona_extcon_do_magic does not lend a lot of clarity to the purpose of the function, and as all the registers used are described in the datasheet there is no need to obfuscate the code. This patch renames the function to arizona_extcon_hp_clamp, as it controls clamping on the headphone output. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-arizona.c | 36 ++++++++++++++++++-------------- include/linux/mfd/arizona/core.h | 2 +- sound/soc/codecs/arizona.c | 4 ++-- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 63f01c42aed4..95cf7f875bb3 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -136,18 +136,22 @@ static const char *arizona_cable[] = { static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info); -static void arizona_extcon_do_magic(struct arizona_extcon_info *info, - unsigned int magic) +static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info, + bool clamp) { struct arizona *arizona = info->arizona; + unsigned int val = 0; int ret; + if (clamp) + val = ARIZONA_RMV_SHRT_HP1L; + mutex_lock(&arizona->dapm->card->dapm_mutex); - arizona->hpdet_magic = magic; + arizona->hpdet_clamp = clamp; - /* Keep the HP output stages disabled while doing the magic */ - if (magic) { + /* Keep the HP output stages disabled while doing the clamp */ + if (clamp) { ret = regmap_update_bits(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, ARIZONA_OUT1L_ENA | @@ -158,20 +162,20 @@ static void arizona_extcon_do_magic(struct arizona_extcon_info *info, ret); } - ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, - magic); + ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L, + ARIZONA_RMV_SHRT_HP1L, val); if (ret != 0) - dev_warn(arizona->dev, "Failed to do magic: %d\n", + dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret); - ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, - magic); + ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R, + ARIZONA_RMV_SHRT_HP1R, val); if (ret != 0) - dev_warn(arizona->dev, "Failed to do magic: %d\n", + dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret); - /* Restore the desired state while not doing the magic */ - if (!magic) { + /* Restore the desired state while not doing the clamp */ + if (!clamp) { ret = regmap_update_bits(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, ARIZONA_OUT1L_ENA | @@ -603,7 +607,7 @@ done: ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL, 0); - arizona_extcon_do_magic(info, 0); + arizona_extcon_hp_clamp(info, false); if (id_gpio) gpio_set_value_cansleep(id_gpio, 0); @@ -648,7 +652,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info) if (info->mic) arizona_stop_mic(info); - arizona_extcon_do_magic(info, 0x4000); + arizona_extcon_hp_clamp(info, true); ret = regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, @@ -699,7 +703,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info) info->hpdet_active = true; - arizona_extcon_do_magic(info, 0x4000); + arizona_extcon_hp_clamp(info, true); ret = regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 910e3aa1e965..4863548faff7 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -126,7 +126,7 @@ struct arizona { struct regmap_irq_chip_data *aod_irq_chip; struct regmap_irq_chip_data *irq_chip; - bool hpdet_magic; + bool hpdet_clamp; unsigned int hp_ena; struct mutex clk_lock; diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 29202610dd0d..fb58c7ee3780 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -840,8 +840,8 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w, priv->arizona->hp_ena &= ~mask; priv->arizona->hp_ena |= val; - /* Force off if HPDET magic is active */ - if (priv->arizona->hpdet_magic) + /* Force off if HPDET clamp is active */ + if (priv->arizona->hpdet_clamp) val = 0; regmap_update_bits_async(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, From 43f0acd96163754672cfb8c8015c54ec527a2cce Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 16 Feb 2015 15:41:03 +0000 Subject: [PATCH 052/133] extcon: arizona: Fix headphone clamping on wm5110 wm5110 requires slightly different configuration of the headphone clamps to other Arizona devices. Otherwise headphone detection accuracy will be way off. This patch adds the needed clamping. Signed-off-by: Charles Keepax Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-arizona.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 95cf7f875bb3..d9e763cddb50 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -140,11 +140,24 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info, bool clamp) { struct arizona *arizona = info->arizona; - unsigned int val = 0; + unsigned int mask = 0, val = 0; int ret; - if (clamp) - val = ARIZONA_RMV_SHRT_HP1L; + switch (arizona->type) { + case WM5110: + mask = ARIZONA_HP1L_SHRTO | ARIZONA_HP1L_FLWR | + ARIZONA_HP1L_SHRTI; + if (clamp) + val = ARIZONA_HP1L_SHRTO; + else + val = ARIZONA_HP1L_FLWR | ARIZONA_HP1L_SHRTI; + break; + default: + mask = ARIZONA_RMV_SHRT_HP1L; + if (clamp) + val = ARIZONA_RMV_SHRT_HP1L; + break; + }; mutex_lock(&arizona->dapm->card->dapm_mutex); @@ -163,13 +176,13 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info, } ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L, - ARIZONA_RMV_SHRT_HP1L, val); + mask, val); if (ret != 0) dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret); ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R, - ARIZONA_RMV_SHRT_HP1R, val); + mask, val); if (ret != 0) dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret); From b43baf694fde30dc2db2386c347324d1a013f3d1 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 4 Mar 2015 18:41:34 +0200 Subject: [PATCH 053/133] mei: free me client references on host init Fx fixes leak introduced by: commit b7d885145538 ("mei: revamp me clients list handling") Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 98a5363e1e8a..b6fec4d15307 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -668,14 +668,17 @@ void mei_host_client_init(struct work_struct *work) me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid); if (me_cl) mei_amthif_host_init(dev); + mei_me_cl_put(me_cl); me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid); if (me_cl) mei_wd_host_init(dev); + mei_me_cl_put(me_cl); me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid); if (me_cl) mei_nfc_host_init(dev); + mei_me_cl_put(me_cl); dev->dev_state = MEI_DEV_ENABLED; From d2b5851d8583e690eeb5ac8dfff5da92e1f1468f Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 4 Mar 2015 18:41:35 +0200 Subject: [PATCH 054/133] mei: trace: fix missing include to linux/device.h Fix warning (discovered using randconfig) drivers/misc/mei/mei-trace.h:30:24: warning: 'struct device' declared inside parameter list TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val), Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/mei-trace.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h index d5c38d15cdd9..5f4e1a17360b 100644 --- a/drivers/misc/mei/mei-trace.h +++ b/drivers/misc/mei/mei-trace.h @@ -21,6 +21,8 @@ #include #include +#include + #undef TRACE_SYSTEM #define TRACE_SYSTEM mei From f68a8342b1082d9a4b084cb085396e562899bc62 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Sat, 7 Mar 2015 22:58:10 +0900 Subject: [PATCH 055/133] extcon: Rename extcon core driver This patch renames the extcon core driver from extcon-class.c to extcon.c because '-class' postfix is not necessary. Signed-off-by: Chanwoo Choi --- drivers/extcon/Makefile | 2 +- drivers/extcon/{extcon-class.c => extcon.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename drivers/extcon/{extcon-class.c => extcon.c} (100%) diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index e1eb2d50e72b..9204114791a3 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -2,7 +2,7 @@ # Makefile for external connector class (extcon) devices # -obj-$(CONFIG_EXTCON) += extcon-class.o +obj-$(CONFIG_EXTCON) += extcon.o obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon.c similarity index 100% rename from drivers/extcon/extcon-class.c rename to drivers/extcon/extcon.c From 34825e511971e193db16a96350faeb60eff0d842 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Sat, 7 Mar 2015 01:41:36 +0900 Subject: [PATCH 056/133] extcon: Fix the checkpatch warning This patch fixes the checkpatch warning about coding style. Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-max14577.c | 5 +---- drivers/extcon/extcon-max77693.c | 5 +---- drivers/extcon/extcon-max8997.c | 5 +---- drivers/extcon/extcon-rt8973a.c | 6 ++---- drivers/extcon/extcon-sm5502.c | 6 +++--- drivers/extcon/extcon-usb-gpio.c | 2 +- drivers/extcon/extcon.c | 1 + 7 files changed, 10 insertions(+), 20 deletions(-) diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c index c1bf0cf747b0..3823aa4a3a80 100644 --- a/drivers/extcon/extcon-max14577.c +++ b/drivers/extcon/extcon-max14577.c @@ -539,8 +539,6 @@ static void max14577_muic_irq_work(struct work_struct *work) dev_err(info->dev, "failed to handle MUIC interrupt\n"); mutex_unlock(&info->mutex); - - return; } /* @@ -730,8 +728,7 @@ static int max14577_muic_probe(struct platform_device *pdev) muic_irq->name, info); if (ret) { dev_err(&pdev->dev, - "failed: irq request (IRQ: %d," - " error :%d)\n", + "failed: irq request (IRQ: %d, error :%d)\n", muic_irq->irq, ret); return ret; } diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index dfcc5cba25f0..a66bec8f6252 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -1019,8 +1019,6 @@ static void max77693_muic_irq_work(struct work_struct *work) dev_err(info->dev, "failed to handle MUIC interrupt\n"); mutex_unlock(&info->mutex); - - return; } static irqreturn_t max77693_muic_irq_handler(int irq, void *data) @@ -1171,8 +1169,7 @@ static int max77693_muic_probe(struct platform_device *pdev) muic_irq->name, info); if (ret) { dev_err(&pdev->dev, - "failed: irq request (IRQ: %d," - " error :%d)\n", + "failed: irq request (IRQ: %d, error :%d)\n", muic_irq->irq, ret); return ret; } diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index fc1678fa95c4..5774e56c6422 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -579,8 +579,6 @@ static void max8997_muic_irq_work(struct work_struct *work) dev_err(info->dev, "failed to handle MUIC interrupt\n"); mutex_unlock(&info->mutex); - - return; } static irqreturn_t max8997_muic_irq_handler(int irq, void *data) @@ -689,8 +687,7 @@ static int max8997_muic_probe(struct platform_device *pdev) muic_irq->name, info); if (ret) { dev_err(&pdev->dev, - "failed: irq request (IRQ: %d," - " error :%d)\n", + "failed: irq request (IRQ: %d, error :%d)\n", muic_irq->irq, ret); goto err_irq; } diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c index a784b2d5ee72..9ccd5af89d1c 100644 --- a/drivers/extcon/extcon-rt8973a.c +++ b/drivers/extcon/extcon-rt8973a.c @@ -582,10 +582,8 @@ static int rt8973a_muic_i2c_probe(struct i2c_client *i2c, return -EINVAL; info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL); - if (!info) { - dev_err(&i2c->dev, "failed to allocate memory\n"); + if (!info) return -ENOMEM; - } i2c_set_clientdata(i2c, info); info->dev = &i2c->dev; @@ -681,7 +679,7 @@ static int rt8973a_muic_i2c_remove(struct i2c_client *i2c) return 0; } -static struct of_device_id rt8973a_dt_match[] = { +static const struct of_device_id rt8973a_dt_match[] = { { .compatible = "richtek,rt8973a-muic" }, { }, }; diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index b0f7bd82af90..2f93cf307852 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -359,8 +359,8 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info) break; default: dev_dbg(info->dev, - "cannot identify the cable type: adc(0x%x) " - "dev_type1(0x%x)\n", adc, dev_type1); + "cannot identify the cable type: adc(0x%x)\n", + adc); return -EINVAL; }; break; @@ -659,7 +659,7 @@ static int sm5502_muic_i2c_remove(struct i2c_client *i2c) return 0; } -static struct of_device_id sm5502_dt_match[] = { +static const struct of_device_id sm5502_dt_match[] = { { .compatible = "siliconmitus,sm5502-muic" }, { }, }; diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index 3f0bad3ce8aa..de67fce18984 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -214,7 +214,7 @@ static int usb_extcon_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(usb_extcon_pm_ops, usb_extcon_suspend, usb_extcon_resume); -static struct of_device_id usb_extcon_dt_match[] = { +static const struct of_device_id usb_extcon_dt_match[] = { { .compatible = "linux,extcon-usb-gpio", }, { /* sentinel */ } }; diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 8319f25b7145..752ce1208428 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -158,6 +158,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, /* Optional callback given by the user */ if (edev->print_name) { int ret = edev->print_name(edev, buf); + if (ret >= 0) return ret; } From 208d51154c8d7f2c3808b4401132233c5ab21572 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Wed, 25 Feb 2015 15:15:23 +0100 Subject: [PATCH 057/133] checkkconfigsymbols.py: filter reports for tools/ Recent changes to the build system of tools suggest to filter reports for the entire tools directory. Various C preprocessor identifiers are prefixed with CONFIG_ but are NOT defined in Kconfig but in Makefiles in the tools directory. Such identifiers are false positives for most static analysis tools (i.e., scripts/checkkconfigsymbols.py) since the CONFIG_ prefix and the _MODULE suffix is reserved for Kconfig features in CPP and Make syntax. Signed-off-by: Valentin Rothberg Signed-off-by: Greg Kroah-Hartman --- scripts/checkkconfigsymbols.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) mode change 100644 => 100755 scripts/checkkconfigsymbols.py diff --git a/scripts/checkkconfigsymbols.py b/scripts/checkkconfigsymbols.py old mode 100644 new mode 100755 index e9cc689033fe..6445693df669 --- a/scripts/checkkconfigsymbols.py +++ b/scripts/checkkconfigsymbols.py @@ -2,7 +2,7 @@ """Find Kconfig identifiers that are referenced but not defined.""" -# (c) 2014 Valentin Rothberg +# (c) 2014-2015 Valentin Rothberg # (c) 2014 Stefan Hengelein # # Licensed under the terms of the GNU GPL License version 2 @@ -46,8 +46,9 @@ def main(): stdout = stdout[:-1] for gitfile in stdout.rsplit("\n"): - if ".git" in gitfile or "ChangeLog" in gitfile or \ - ".log" in gitfile or os.path.isdir(gitfile): + if ".git" in gitfile or "ChangeLog" in gitfile or \ + ".log" in gitfile or os.path.isdir(gitfile) or \ + gitfile.startswith("tools/"): continue if REGEX_FILE_KCONFIG.match(gitfile): kconfig_files.append(gitfile) From d927c59076f505a056e78a9fc9744069aa052cc8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 15 Mar 2015 13:49:47 +0300 Subject: [PATCH 058/133] extcon: max77843: Fix signedness bug in max77843_muic_set_debounce_time() This patch fixes the variable type of 'ret' from 'unsigned int' to 'int' type because the return type of regmap_update_bits() is 'int' type. Fixes: 27a28d32b4f2 ('extcon: max77843: Add max77843 MUIC driver') Signed-off-by: Dan Carpenter [cw00.choi: Fix the patch description] Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-max77843.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index 598a017cc91b..a3f80ca4a6e3 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -682,7 +682,7 @@ static int max77843_muic_set_debounce_time(struct max77843_muic_info *info, enum max77843_muic_adc_debounce_time time) { struct max77843 *max77843 = info->max77843; - unsigned int ret; + int ret; switch (time) { case MAX77843_DEBOUNCE_TIME_5MS: From 6893d9b51093cf499ee6217e98c50006ec2727c6 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 16 Mar 2015 20:20:26 +0100 Subject: [PATCH 059/133] misc: constify of_device_id array of_device_id is always used as const. (See driver.of_match_table and open firmware functions) Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman --- drivers/misc/carma/carma-fpga-program.c | 2 +- drivers/misc/carma/carma-fpga.c | 2 +- drivers/misc/lis3lv02d/lis3lv02d_i2c.c | 2 +- drivers/misc/lis3lv02d/lis3lv02d_spi.c | 2 +- drivers/misc/sram.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c index 06166ac000e0..fff8c43d9943 100644 --- a/drivers/misc/carma/carma-fpga-program.c +++ b/drivers/misc/carma/carma-fpga-program.c @@ -1142,7 +1142,7 @@ out_return: return ret; } -static struct of_device_id fpga_of_match[] = { +static const struct of_device_id fpga_of_match[] = { { .compatible = "carma,fpga-programmer", }, {}, }; diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 68cdfe151bdb..5aba3fd789de 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -1486,7 +1486,7 @@ static int data_of_remove(struct platform_device *op) return 0; } -static struct of_device_id data_of_match[] = { +static const struct of_device_id data_of_match[] = { { .compatible = "carma,carma-fpga", }, {}, }; diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c index 63fe096d4462..e3e7f1dc27ba 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c @@ -106,7 +106,7 @@ static union axis_conversion lis3lv02d_axis_map = { .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } }; #ifdef CONFIG_OF -static struct of_device_id lis3lv02d_i2c_dt_ids[] = { +static const struct of_device_id lis3lv02d_i2c_dt_ids[] = { { .compatible = "st,lis3lv02d" }, {} }; diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c index bd06d0cfac45..b2f6e1651ac9 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c @@ -61,7 +61,7 @@ static union axis_conversion lis3lv02d_axis_normal = { .as_array = { 1, 2, 3 } }; #ifdef CONFIG_OF -static struct of_device_id lis302dl_spi_dt_ids[] = { +static const struct of_device_id lis302dl_spi_dt_ids[] = { { .compatible = "st,lis302dl-spi" }, {} }; diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 21181fa243df..1ed37cedff49 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -205,7 +205,7 @@ static int sram_remove(struct platform_device *pdev) } #ifdef CONFIG_OF -static struct of_device_id sram_dt_ids[] = { +static const struct of_device_id sram_dt_ids[] = { { .compatible = "mmio-sram" }, {} }; From da2ff527e44bf3af851c1e5d9ac82d248df35417 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 16 Mar 2015 20:17:13 +0100 Subject: [PATCH 060/133] char: constify of_device_id array of_device_id is always used as const. (See driver.of_match_table and open firmware functions) Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman --- drivers/char/hw_random/pasemi-rng.c | 2 +- drivers/char/hw_random/powernv-rng.c | 2 +- drivers/char/hw_random/ppc4xx-rng.c | 2 +- drivers/char/ipmi/ipmi_si_intf.c | 4 ++-- drivers/char/xillybus/xillybus_of.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c index 3eb7bdd7f93b..51cb1d5cc489 100644 --- a/drivers/char/hw_random/pasemi-rng.c +++ b/drivers/char/hw_random/pasemi-rng.c @@ -133,7 +133,7 @@ static int rng_remove(struct platform_device *dev) return 0; } -static struct of_device_id rng_match[] = { +static const struct of_device_id rng_match[] = { { .compatible = "1682m-rng", }, { .compatible = "pasemi,pwrficient-rng", }, { }, diff --git a/drivers/char/hw_random/powernv-rng.c b/drivers/char/hw_random/powernv-rng.c index 3f4f63204560..263a5bb8e605 100644 --- a/drivers/char/hw_random/powernv-rng.c +++ b/drivers/char/hw_random/powernv-rng.c @@ -61,7 +61,7 @@ static int powernv_rng_probe(struct platform_device *pdev) return 0; } -static struct of_device_id powernv_rng_match[] = { +static const struct of_device_id powernv_rng_match[] = { { .compatible = "ibm,power-rng",}, {}, }; diff --git a/drivers/char/hw_random/ppc4xx-rng.c b/drivers/char/hw_random/ppc4xx-rng.c index c85d31a5f9e3..b2cfda0fa93e 100644 --- a/drivers/char/hw_random/ppc4xx-rng.c +++ b/drivers/char/hw_random/ppc4xx-rng.c @@ -123,7 +123,7 @@ static int ppc4xx_rng_remove(struct platform_device *dev) return 0; } -static struct of_device_id ppc4xx_rng_match[] = { +static const struct of_device_id ppc4xx_rng_match[] = { { .compatible = "ppc4xx-rng", }, { .compatible = "amcc,ppc460ex-rng", }, { .compatible = "amcc,ppc440epx-rng", }, diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index f6646ed3047e..e6a6da4ad2f7 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2664,7 +2664,7 @@ static struct pci_driver ipmi_pci_driver = { }; #endif /* CONFIG_PCI */ -static struct of_device_id ipmi_match[]; +static const struct of_device_id ipmi_match[]; static int ipmi_probe(struct platform_device *dev) { #ifdef CONFIG_OF @@ -2761,7 +2761,7 @@ static int ipmi_remove(struct platform_device *dev) return 0; } -static struct of_device_id ipmi_match[] = +static const struct of_device_id ipmi_match[] = { { .type = "ipmi", .compatible = "ipmi-kcs", .data = (void *)(unsigned long) SI_KCS }, diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c index 2002a3a28146..781865084dc1 100644 --- a/drivers/char/xillybus/xillybus_of.c +++ b/drivers/char/xillybus/xillybus_of.c @@ -31,7 +31,7 @@ MODULE_LICENSE("GPL v2"); static const char xillyname[] = "xillybus_of"; /* Match table for of_platform binding */ -static struct of_device_id xillybus_of_match[] = { +static const struct of_device_id xillybus_of_match[] = { { .compatible = "xillybus,xillybus-1.00.a", }, { .compatible = "xlnx,xillybus-1.00.a", }, /* Deprecated */ {} From b1a3f243485fa2643bc75fd70a23bbd7cfc74f2d Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Mon, 16 Mar 2015 12:16:14 +0100 Subject: [PATCH 061/133] checkkconfigsymbols.py: make it Git aware The script now supports to check a specified commit or a specified range of commits (i.e., commit1..commit2). Developers and maintainers are encouraged to use this functionality before sending or merging patches to avoid potential bugs and to keep the code, documentation, etc. clean. This patch adds the following options to the script: -c COMMIT, --commit=COMMIT Check if the specified commit (hash) introduces undefined Kconfig symbols. -d DIFF, --diff=DIFF Diff undefined symbols between two commits. The input format bases on Git log's 'commmit1..commit2'. --force Reset current Git tree even when it's dirty. Note that the first two options require to 'git reset --hard' the user's Git tree. This hard reset is necessary to keep the script fast, but it can lead to the loss of uncommitted data. Hence, the script aborts in case it is executed in a dirty tree. It won't abort if '--force' is passed. If neither -c nor -d is specified, the script defaults to check the entire local tree (i.e., the previous behavior). Signed-off-by: Valentin Rothberg Signed-off-by: Greg Kroah-Hartman --- scripts/checkkconfigsymbols.py | 138 +++++++++++++++++++++++++++++++-- 1 file changed, 132 insertions(+), 6 deletions(-) diff --git a/scripts/checkkconfigsymbols.py b/scripts/checkkconfigsymbols.py index 6445693df669..ce9ca60808b8 100755 --- a/scripts/checkkconfigsymbols.py +++ b/scripts/checkkconfigsymbols.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -"""Find Kconfig identifiers that are referenced but not defined.""" +"""Find Kconfig symbols that are referenced but not defined.""" # (c) 2014-2015 Valentin Rothberg # (c) 2014 Stefan Hengelein @@ -10,7 +10,9 @@ import os import re +import sys from subprocess import Popen, PIPE, STDOUT +from optparse import OptionParser # regex expressions @@ -32,16 +34,140 @@ REGEX_KCONFIG_HELP = re.compile(r"^\s+(help|---help---)\s*$") REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$") +def parse_options(): + """The user interface of this module.""" + usage = "%prog [options]\n\n" \ + "Run this tool to detect Kconfig symbols that are referenced but " \ + "not defined in\nKconfig. The output of this tool has the " \ + "format \'Undefined symbol\\tFile list\'\n\n" \ + "If no option is specified, %prog will default to check your\n" \ + "current tree. Please note that specifying commits will " \ + "\'git reset --hard\'\nyour current tree! You may save " \ + "uncommitted changes to avoid losing data." + + parser = OptionParser(usage=usage) + + parser.add_option('-c', '--commit', dest='commit', action='store', + default="", + help="Check if the specified commit (hash) introduces " + "undefined Kconfig symbols.") + + parser.add_option('-d', '--diff', dest='diff', action='store', + default="", + help="Diff undefined symbols between two commits. The " + "input format bases on Git log's " + "\'commmit1..commit2\'.") + + parser.add_option('', '--force', dest='force', action='store_true', + default=False, + help="Reset current Git tree even when it's dirty.") + + (opts, _) = parser.parse_args() + + if opts.commit and opts.diff: + sys.exit("Please specify only one option at once.") + + if opts.diff and not re.match(r"^[\w\-\.]+\.\.[\w\-\.]+$", opts.diff): + sys.exit("Please specify valid input in the following format: " + "\'commmit1..commit2\'") + + if opts.commit or opts.diff: + if not opts.force and tree_is_dirty(): + sys.exit("The current Git tree is dirty (see 'git status'). " + "Running this script may\ndelete important data since it " + "calls 'git reset --hard' for some performance\nreasons. " + " Please run this script in a clean Git tree or pass " + "'--force' if you\nwant to ignore this warning and " + "continue.") + + return opts + + def main(): """Main function of this module.""" + opts = parse_options() + + if opts.commit or opts.diff: + head = get_head() + + # get commit range + commit_a = None + commit_b = None + if opts.commit: + commit_a = opts.commit + "~" + commit_b = opts.commit + elif opts.diff: + split = opts.diff.split("..") + commit_a = split[0] + commit_b = split[1] + undefined_a = {} + undefined_b = {} + + # get undefined items before the commit + execute("git reset --hard %s" % commit_a) + undefined_a = check_symbols() + + # get undefined items for the commit + execute("git reset --hard %s" % commit_b) + undefined_b = check_symbols() + + # report cases that are present for the commit but not before + for feature in undefined_b: + # feature has not been undefined before + if not feature in undefined_a: + files = undefined_b.get(feature) + print "%s\t%s" % (feature, ", ".join(files)) + # check if there are new files that reference the undefined feature + else: + files = undefined_b.get(feature) - undefined_a.get(feature) + if files: + print "%s\t%s" % (feature, ", ".join(files)) + + # reset to head + execute("git reset --hard %s" % head) + + # default to check the entire tree + else: + undefined = check_symbols() + for feature in undefined: + files = undefined.get(feature) + + +def execute(cmd): + """Execute %cmd and return stdout. Exit in case of error.""" + pop = Popen(cmd, stdout=PIPE, stderr=STDOUT, shell=True) + (stdout, _) = pop.communicate() # wait until finished + if pop.returncode != 0: + sys.exit(stdout) + return stdout + + +def tree_is_dirty(): + """Return true if the current working tree is dirty (i.e., if any file has + been added, deleted, modified, renamed or copied but not committed).""" + stdout = execute("git status --porcelain") + for line in stdout: + if re.findall(r"[URMADC]{1}", line[:2]): + return True + return False + + +def get_head(): + """Return commit hash of current HEAD.""" + stdout = execute("git rev-parse HEAD") + return stdout.strip('\n') + + +def check_symbols(): + """Find undefined Kconfig symbols and return a dict with the symbol as key + and a list of referencing files as value.""" source_files = [] kconfig_files = [] defined_features = set() referenced_features = dict() # {feature: [files]} # use 'git ls-files' to get the worklist - pop = Popen("git ls-files", stdout=PIPE, stderr=STDOUT, shell=True) - (stdout, _) = pop.communicate() # wait until finished + stdout = execute("git ls-files") if len(stdout) > 0 and stdout[-1] == "\n": stdout = stdout[:-1] @@ -62,7 +188,7 @@ def main(): for kfile in kconfig_files: parse_kconfig_file(kfile, defined_features, referenced_features) - print "Undefined symbol used\tFile list" + undefined = {} # {feature: [files]} for feature in sorted(referenced_features): # filter some false positives if feature == "FOO" or feature == "BAR" or \ @@ -73,8 +199,8 @@ def main(): # avoid false positives for kernel modules if feature[:-len("_MODULE")] in defined_features: continue - files = referenced_features.get(feature) - print "%s\t%s" % (feature, ", ".join(files)) + undefined[feature] = referenced_features.get(feature) + return undefined def parse_source_file(sfile, referenced_features): From be7bee9f80c1237e70cc6411e5b432681941247c Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 8 Mar 2015 09:49:50 +0100 Subject: [PATCH 062/133] misc: bh1780: Add module aliases The bh1780gli driver does not create an i2c module alias for the device it supports, preventing the driver from being loaded automatically when needed on non-OF/DT systems. Add it. Signed-off-by: Jean Delvare Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/misc/bh1780gli.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/misc/bh1780gli.c b/drivers/misc/bh1780gli.c index 4c4a59b25537..7f90ce5a569a 100644 --- a/drivers/misc/bh1780gli.c +++ b/drivers/misc/bh1780gli.c @@ -230,6 +230,8 @@ static const struct i2c_device_id bh1780_id[] = { { }, }; +MODULE_DEVICE_TABLE(i2c, bh1780_id); + #ifdef CONFIG_OF static const struct of_device_id of_bh1780_match[] = { { .compatible = "rohm,bh1780gli", }, From 71a49d16f06de2ccdf52ca247d496a2bb1ca36fe Mon Sep 17 00:00:00 2001 From: Abhilash Kesavan Date: Fri, 6 Feb 2015 19:15:26 +0530 Subject: [PATCH 063/133] m32r: add definition of ioremap_wc to io.h Before adding a resource managed ioremap_wc function, we need to have ioremap_wc defined for m32r to prevent build errors. Signed-off-by: Abhilash Kesavan Acked-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- arch/m32r/include/asm/io.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/m32r/include/asm/io.h b/arch/m32r/include/asm/io.h index 6e7787f3dac7..9cc00dbd59ce 100644 --- a/arch/m32r/include/asm/io.h +++ b/arch/m32r/include/asm/io.h @@ -67,6 +67,7 @@ static inline void __iomem *ioremap(unsigned long offset, unsigned long size) extern void iounmap(volatile void __iomem *addr); #define ioremap_nocache(off,size) ioremap(off,size) +#define ioremap_wc ioremap_nocache /* * IO bus memory addresses are also 1:1 with the physical address From 34644524bce91883d5051a7eaf3ec5464ed149bf Mon Sep 17 00:00:00 2001 From: Abhilash Kesavan Date: Fri, 6 Feb 2015 19:15:27 +0530 Subject: [PATCH 064/133] lib: devres: add a helper function for ioremap_wc Implement a resource managed writecombine ioremap function. Signed-off-by: Abhilash Kesavan Acked-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-model/devres.txt | 1 + include/linux/io.h | 2 ++ lib/devres.c | 28 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 6d1e8eeb5990..7fe7fd263aba 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -276,6 +276,7 @@ IOMAP devm_ioport_unmap() devm_ioremap() devm_ioremap_nocache() + devm_ioremap_wc() devm_ioremap_resource() : checks resource, requests memory region, ioremaps devm_iounmap() pcim_iomap() diff --git a/include/linux/io.h b/include/linux/io.h index fa02e55e5a2e..42b33f03d1df 100644 --- a/include/linux/io.h +++ b/include/linux/io.h @@ -64,6 +64,8 @@ void __iomem *devm_ioremap(struct device *dev, resource_size_t offset, resource_size_t size); void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset, resource_size_t size); +void __iomem *devm_ioremap_wc(struct device *dev, resource_size_t offset, + resource_size_t size); void devm_iounmap(struct device *dev, void __iomem *addr); int check_signature(const volatile void __iomem *io_addr, const unsigned char *signature, int length); diff --git a/lib/devres.c b/lib/devres.c index 0f1dd2e9d2c1..fbe2aac522e6 100644 --- a/lib/devres.c +++ b/lib/devres.c @@ -71,6 +71,34 @@ void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset, } EXPORT_SYMBOL(devm_ioremap_nocache); +/** + * devm_ioremap_wc - Managed ioremap_wc() + * @dev: Generic device to remap IO address for + * @offset: BUS offset to map + * @size: Size of map + * + * Managed ioremap_wc(). Map is automatically unmapped on driver detach. + */ +void __iomem *devm_ioremap_wc(struct device *dev, resource_size_t offset, + resource_size_t size) +{ + void __iomem **ptr, *addr; + + ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + addr = ioremap_wc(offset, size); + if (addr) { + *ptr = addr; + devres_add(dev, ptr); + } else + devres_free(ptr); + + return addr; +} +EXPORT_SYMBOL(devm_ioremap_wc); + /** * devm_iounmap - Managed iounmap() * @dev: Generic device to unmap for From 0ab163ad1ea0bb0ccd4ada2a54834041611d76f1 Mon Sep 17 00:00:00 2001 From: Abhilash Kesavan Date: Fri, 6 Feb 2015 19:15:28 +0530 Subject: [PATCH 065/133] misc: sram: switch to ioremap_wc from ioremap Currently, the SRAM allocator returns device memory via ioremap. This causes issues on ARM64 when the internal SoC SRAM allocated by the generic sram driver is used for audio playback. The destination buffer address (which is ioremapped SRAM) is not 64-bit aligned for certain streams (e.g. 44.1k sampling rate). In such cases we get unhandled alignment faults. Use ioremap_wc in place of ioremap which gives us normal non-cacheable memory instead of device memory. Signed-off-by: Abhilash Kesavan Tested-by: Tony Lindgren Tested-by: Heiko Stuebner Acked-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 1ed37cedff49..eeaaf5fca105 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -69,12 +69,23 @@ static int sram_probe(struct platform_device *pdev) INIT_LIST_HEAD(&reserve_list); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - virt_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(virt_base)) - return PTR_ERR(virt_base); + if (!res) { + dev_err(&pdev->dev, "found no memory resource\n"); + return -EINVAL; + } size = resource_size(res); + if (!devm_request_mem_region(&pdev->dev, + res->start, size, pdev->name)) { + dev_err(&pdev->dev, "could not request region for resource\n"); + return -EBUSY; + } + + virt_base = devm_ioremap_wc(&pdev->dev, res->start, size); + if (IS_ERR(virt_base)) + return PTR_ERR(virt_base); + sram = devm_kzalloc(&pdev->dev, sizeof(*sram), GFP_KERNEL); if (!sram) return -ENOMEM; From dae6cdabe45baecfcfd02fbf11d97bb645450fc6 Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Wed, 11 Feb 2015 16:25:38 +0530 Subject: [PATCH 066/133] pcmcia: Use setup_timer This patch introduces the use of function setup_timer. This is done using Coccinelle and semantic patch used is as follows: @@ expression x,y,z; @@ - init_timer (&x); + setup_timer (&x, y, z); - x.function = y; - x.data = z; Signed-off-by: Vaishali Thakkar Signed-off-by: Greg Kroah-Hartman --- drivers/pcmcia/omap_cf.c | 4 +--- drivers/pcmcia/soc_common.c | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c index 8170102d1e93..4e2f501e5548 100644 --- a/drivers/pcmcia/omap_cf.c +++ b/drivers/pcmcia/omap_cf.c @@ -220,9 +220,7 @@ static int __init omap_cf_probe(struct platform_device *pdev) cf = kzalloc(sizeof *cf, GFP_KERNEL); if (!cf) return -ENOMEM; - init_timer(&cf->timer); - cf->timer.function = omap_cf_timer; - cf->timer.data = (unsigned long) cf; + setup_timer(&cf->timer, omap_cf_timer, (unsigned long)cf); cf->pdev = pdev; platform_set_drvdata(pdev, cf); diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index 933f4657515b..eed5e9c05353 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -726,9 +726,8 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt) { int ret; - init_timer(&skt->poll_timer); - skt->poll_timer.function = soc_common_pcmcia_poll_event; - skt->poll_timer.data = (unsigned long)skt; + setup_timer(&skt->poll_timer, soc_common_pcmcia_poll_event, + (unsigned long)skt); skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD; ret = request_resource(&iomem_resource, &skt->res_skt); From 03b225b16d75bccf9bc4d82607964a08148adf61 Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Wed, 11 Feb 2015 16:15:31 +0530 Subject: [PATCH 067/133] pcmcia: Use setup_timer and mod_timer This patch introduces the use of functions setup_timer and mod_timer. This is done using Coccinelle and semantic patch used for this as follows: // @@ expression x,y,z,a,b; @@ -init_timer (&x); +setup_timer (&x, y, z); +mod_timer (&a, b); -x.function = y; -x.data = z; -x.expires = b; -add_timer(&a); // Signed-off-by: Vaishali Thakkar Signed-off-by: Greg Kroah-Hartman --- drivers/pcmcia/pd6729.c | 8 +++----- drivers/pcmcia/yenta_socket.c | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index 34ace4854dc2..0f70b4d58f9e 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c @@ -707,11 +707,9 @@ static int pd6729_pci_probe(struct pci_dev *dev, } } else { /* poll Card status change */ - init_timer(&socket->poll_timer); - socket->poll_timer.function = pd6729_interrupt_wrapper; - socket->poll_timer.data = (unsigned long)socket; - socket->poll_timer.expires = jiffies + HZ; - add_timer(&socket->poll_timer); + setup_timer(&socket->poll_timer, pd6729_interrupt_wrapper, + (unsigned long)socket); + mod_timer(&socket->poll_timer, jiffies + HZ); } for (i = 0; i < MAX_SOCKETS; i++) { diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 8a23ccb41213..965bd8491233 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1236,11 +1236,9 @@ static int yenta_probe(struct pci_dev *dev, const struct pci_device_id *id) if (!socket->cb_irq || request_irq(socket->cb_irq, yenta_interrupt, IRQF_SHARED, "yenta", socket)) { /* No IRQ or request_irq failed. Poll */ socket->cb_irq = 0; /* But zero is a valid IRQ number. */ - init_timer(&socket->poll_timer); - socket->poll_timer.function = yenta_interrupt_wrapper; - socket->poll_timer.data = (unsigned long)socket; - socket->poll_timer.expires = jiffies + HZ; - add_timer(&socket->poll_timer); + setup_timer(&socket->poll_timer, yenta_interrupt_wrapper, + (unsigned long)socket); + mod_timer(&socket->poll_timer, jiffies + HZ); dev_printk(KERN_INFO, &dev->dev, "no PCI IRQ, CardBus support disabled for this " "socket.\n"); From a087146c72bad795bcab80e5987c5b80fa225000 Mon Sep 17 00:00:00 2001 From: Brian Russell Date: Thu, 19 Mar 2015 17:55:26 +0000 Subject: [PATCH 068/133] uio: Request/free irq separate from dev lifecycle Separate irq request/free from the device lifecycle. After device unregister the parent module can call pci_disable_msi. >From the PCI MSI how to: "Before calling this function, a device driver must always call free_irq() on any interrupt for which it previously called request_irq(). Failure to do so results in a BUG_ON(), leaving the device with MSI enabled and thus leaking its vector." So we need to separately free the irq at unregister to allow the device to be kept around in the case of it still having open FDs. Signed-off-by: Brian Russell Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 6276f13e9e12..65bf0676d54a 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -835,7 +835,15 @@ int __uio_register_device(struct module *owner, info->uio_dev = idev; if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) { - ret = devm_request_irq(idev->dev, info->irq, uio_interrupt, + /* + * Note that we deliberately don't use devm_request_irq + * here. The parent module can unregister the UIO device + * and call pci_disable_msi, which requires that this + * irq has been freed. However, the device may have open + * FDs at the time of unregister and therefore may not be + * freed until they are released. + */ + ret = request_irq(info->irq, uio_interrupt, info->irq_flags, info->name, idev); if (ret) goto err_request_irq; @@ -871,6 +879,8 @@ void uio_unregister_device(struct uio_info *info) uio_dev_del_attributes(idev); + free_irq(idev->info->irq, idev); + device_destroy(&uio_class, MKDEV(uio_major, idev->minor)); return; From b9b518f53996b67833b42ca7751b2085b0dc565a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 15 Mar 2015 13:56:04 +0300 Subject: [PATCH 069/133] extcon: max77843: Fix an error code in max77843_init_muic_regmap() The i2c_new_dummy() return the NULL if error happen. So, If i2c_new_dummy() return NULL, max77843_init_muic_regmap() return the proper error value (-ENOMEM); Signed-off-by: Dan Carpenter [cw00.choi: Use -ENOMEM instead of -ENODEV and modify patch description] Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-max77843.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index a3f80ca4a6e3..8db6a926ea07 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -715,7 +715,7 @@ static int max77843_init_muic_regmap(struct max77843 *max77843) if (!max77843->i2c_muic) { dev_err(&max77843->i2c->dev, "Cannot allocate I2C device for MUIC\n"); - return PTR_ERR(max77843->i2c_muic); + return -ENOMEM; } i2c_set_clientdata(max77843->i2c_muic, max77843); From 66bee35f29683fc4a9a530a1c56a0ec45e3f7d72 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 21 Mar 2015 17:26:24 +0100 Subject: [PATCH 070/133] extcon: Fix missing locking when [un]registering notifiers Since extcon.c is using raw_notifiers it must protect the notifier list itself when [un]registering notifiers to avoid the list changing while extcon_update_state is walking the list (through raw_notifier_call_chain). Signed-off-by: Hans de Goede [cw00.choi: Apply this patch to extcon.c driver instead of old extcon-class.c] Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 752ce1208428..4c9f165e4a04 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -445,6 +445,9 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj, const char *extcon_name, const char *cable_name, struct notifier_block *nb) { + unsigned long flags; + int ret; + if (!obj || !cable_name || !nb) return -EINVAL; @@ -462,8 +465,11 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj, obj->internal_nb.notifier_call = _call_per_cable; - return raw_notifier_chain_register(&obj->edev->nh, + spin_lock_irqsave(&obj->edev->lock, flags); + ret = raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb); + spin_unlock_irqrestore(&obj->edev->lock, flags); + return ret; } else { struct class_dev_iter iter; struct extcon_dev *extd; @@ -496,10 +502,17 @@ EXPORT_SYMBOL_GPL(extcon_register_interest); */ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj) { + unsigned long flags; + int ret; + if (!obj) return -EINVAL; - return raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb); + spin_lock_irqsave(&obj->edev->lock, flags); + ret = raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb); + spin_unlock_irqrestore(&obj->edev->lock, flags); + + return ret; } EXPORT_SYMBOL_GPL(extcon_unregister_interest); @@ -516,7 +529,14 @@ EXPORT_SYMBOL_GPL(extcon_unregister_interest); int extcon_register_notifier(struct extcon_dev *edev, struct notifier_block *nb) { - return raw_notifier_chain_register(&edev->nh, nb); + unsigned long flags; + int ret; + + spin_lock_irqsave(&edev->lock, flags); + ret = raw_notifier_chain_register(&edev->nh, nb); + spin_unlock_irqrestore(&edev->lock, flags); + + return ret; } EXPORT_SYMBOL_GPL(extcon_register_notifier); @@ -528,7 +548,14 @@ EXPORT_SYMBOL_GPL(extcon_register_notifier); int extcon_unregister_notifier(struct extcon_dev *edev, struct notifier_block *nb) { - return raw_notifier_chain_unregister(&edev->nh, nb); + unsigned long flags; + int ret; + + spin_lock_irqsave(&edev->lock, flags); + ret = raw_notifier_chain_unregister(&edev->nh, nb); + spin_unlock_irqrestore(&edev->lock, flags); + + return ret; } EXPORT_SYMBOL_GPL(extcon_unregister_notifier); From e9533ae539d5cc3d5fc501529d2df7b77e20449c Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Mon, 23 Mar 2015 18:40:49 +0100 Subject: [PATCH 071/133] checkkconfigsymbols.py: fix sorted output Commit b1a3f243485f ("checkkconfigsymbols.py: make it Git aware") mistakenly removed to print undefined Kconfig symbols in alphabetical order. Furthermore, the script does not print anything anymore when the entire tree is checked (i.e., when no commit is specified). This patch restores the sorted output and adds the missing print for the default case. Additionally, the file lists are now sorted as well which (a) makes it easier to read and (b) makes the output deterministic. Signed-off-by: Valentin Rothberg Signed-off-by: Greg Kroah-Hartman --- scripts/checkkconfigsymbols.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/checkkconfigsymbols.py b/scripts/checkkconfigsymbols.py index ce9ca60808b8..74086a583d8d 100755 --- a/scripts/checkkconfigsymbols.py +++ b/scripts/checkkconfigsymbols.py @@ -112,14 +112,15 @@ def main(): undefined_b = check_symbols() # report cases that are present for the commit but not before - for feature in undefined_b: + for feature in sorted(undefined_b): # feature has not been undefined before if not feature in undefined_a: - files = undefined_b.get(feature) + files = sorted(undefined_b.get(feature)) print "%s\t%s" % (feature, ", ".join(files)) # check if there are new files that reference the undefined feature else: - files = undefined_b.get(feature) - undefined_a.get(feature) + files = sorted(undefined_b.get(feature) - + undefined_a.get(feature)) if files: print "%s\t%s" % (feature, ", ".join(files)) @@ -129,8 +130,9 @@ def main(): # default to check the entire tree else: undefined = check_symbols() - for feature in undefined: - files = undefined.get(feature) + for feature in sorted(undefined): + files = sorted(undefined.get(feature)) + print "%s\t%s" % (feature, ", ".join(files)) def execute(cmd): From bf1361211dd842659b5b882390de687426f3471a Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Tue, 17 Feb 2015 14:29:21 -0800 Subject: [PATCH 072/133] drivers/vmw_vmci: Show correct get_user_pages_fast upon failure As of 240ddd495a9 (vmw_vmci: Convert driver to use get_user_pages_fast()) we no longer user get_user_pages(), thus update the warning. Also convert to pr_debug, which is a more appropriate level of logging. Signed-off-by: Davidlohr Bueso Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_queue_pair.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index 35f19a683822..c3849456332e 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -737,7 +737,8 @@ static int qp_host_get_user_memory(u64 produce_uva, produce_q->kernel_if->num_pages, 1, produce_q->kernel_if->u.h.header_page); if (retval < produce_q->kernel_if->num_pages) { - pr_warn("get_user_pages(produce) failed (retval=%d)", retval); + pr_debug("get_user_pages_fast(produce) failed (retval=%d)", + retval); qp_release_pages(produce_q->kernel_if->u.h.header_page, retval, false); err = VMCI_ERROR_NO_MEM; @@ -748,7 +749,8 @@ static int qp_host_get_user_memory(u64 produce_uva, consume_q->kernel_if->num_pages, 1, consume_q->kernel_if->u.h.header_page); if (retval < consume_q->kernel_if->num_pages) { - pr_warn("get_user_pages(consume) failed (retval=%d)", retval); + pr_debug("get_user_pages_fast(consume) failed (retval=%d)", + retval); qp_release_pages(consume_q->kernel_if->u.h.header_page, retval, false); qp_release_pages(produce_q->kernel_if->u.h.header_page, From 74b5c297f5ecbef0ca128fa7b385b43f036a7984 Mon Sep 17 00:00:00 2001 From: Andy King Date: Thu, 19 Feb 2015 10:33:56 -0800 Subject: [PATCH 073/133] VMCI: Check userland-provided datagram size Ensure that the size filled in by userland in the datagram header matches the size of the buffer passed down in the IOCTL. Note that we account for the size of the header itself in the check. Acked-by: Jorgen Hansen Acked-by: Aditya Sarwade Signed-off-by: Andy King Reported-by: David Ramos Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_driver.c | 2 +- drivers/misc/vmw_vmci/vmci_host.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/misc/vmw_vmci/vmci_driver.c b/drivers/misc/vmw_vmci/vmci_driver.c index 032d35cf93ca..cf264a1bb149 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.1.0-k"); +MODULE_VERSION("1.1.2.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 66fc9921fc85..a721b5d8a9da 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c @@ -395,6 +395,12 @@ static int vmci_host_do_send_datagram(struct vmci_host_dev *vmci_host_dev, return -EFAULT; } + if (VMCI_DG_SIZE(dg) != send_info.len) { + vmci_ioctl_err("datagram size mismatch\n"); + kfree(dg); + return -EINVAL; + } + pr_devel("Datagram dst (handle=0x%x:0x%x) src (handle=0x%x:0x%x), payload (size=%llu bytes)\n", dg->dst.context, dg->dst.resource, dg->src.context, dg->src.resource, From aa6467f190d32b263f7144239c89c63c922d9ff8 Mon Sep 17 00:00:00 2001 From: Jorgen Hansen Date: Mon, 2 Mar 2015 08:19:11 -0800 Subject: [PATCH 074/133] VMCI: Guard against overflow in queue pair allocation The current maximum size of a queue in a queue pair is 128 MB. If we increase that in the future, the queue pair allocation routines may run into overflow issues. This change adds additional checks to guard against this. Acked-by: Andy King Reported-by: Dan Carpenter Signed-off-by: Jorgen Hansen Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_driver.c | 2 +- drivers/misc/vmw_vmci/vmci_queue_pair.c | 31 ++++++++++++++++++------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/misc/vmw_vmci/vmci_driver.c b/drivers/misc/vmw_vmci/vmci_driver.c index cf264a1bb149..b823f9a6e464 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.2.0-k"); +MODULE_VERSION("1.1.3.0-k"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index c3849456332e..f42d9c4e4561 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -295,12 +295,20 @@ static void *qp_alloc_queue(u64 size, u32 flags) { u64 i; struct vmci_queue *queue; - const size_t num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; - const size_t pas_size = num_pages * sizeof(*queue->kernel_if->u.g.pas); - const size_t vas_size = num_pages * sizeof(*queue->kernel_if->u.g.vas); - const size_t queue_size = - sizeof(*queue) + sizeof(*queue->kernel_if) + - pas_size + vas_size; + size_t pas_size; + size_t vas_size; + size_t queue_size = sizeof(*queue) + sizeof(*queue->kernel_if); + const u64 num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; + + if (num_pages > + (SIZE_MAX - queue_size) / + (sizeof(*queue->kernel_if->u.g.pas) + + sizeof(*queue->kernel_if->u.g.vas))) + return NULL; + + pas_size = num_pages * sizeof(*queue->kernel_if->u.g.pas); + vas_size = num_pages * sizeof(*queue->kernel_if->u.g.vas); + queue_size += pas_size + vas_size; queue = vmalloc(queue_size); if (!queue) @@ -615,10 +623,15 @@ static int qp_memcpy_from_queue_iov(void *dest, static struct vmci_queue *qp_host_alloc_queue(u64 size) { struct vmci_queue *queue; - const size_t num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; + size_t queue_page_size; + const u64 num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; const size_t queue_size = sizeof(*queue) + sizeof(*(queue->kernel_if)); - const size_t queue_page_size = - num_pages * sizeof(*queue->kernel_if->u.h.page); + + if (num_pages > (SIZE_MAX - queue_size) / + sizeof(*queue->kernel_if->u.h.page)) + return NULL; + + queue_page_size = num_pages * sizeof(*queue->kernel_if->u.h.page); queue = kzalloc(queue_size + queue_page_size, GFP_KERNEL); if (queue) { From ab3ae0096a6d31e1b244c5c5155f48ef3700329e Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 19 Mar 2015 12:10:09 +0200 Subject: [PATCH 075/133] mei: fix regression on NFC connection In mei_host_client_init function we enable the all internal connected clients including NFC. This is done before we set the device to enabled state and let userspace call open. We need to check only for MEI_FILE_CONNECTED in mei_cl_is_connected in order to enable the communication with the clients before MEI_DEV_ENABLED is set. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index eb02f34b2fe0..7800d1bd8ba1 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -92,9 +92,7 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl); */ static inline bool mei_cl_is_connected(struct mei_cl *cl) { - return cl->dev && - cl->dev->dev_state == MEI_DEV_ENABLED && - cl->state == MEI_FILE_CONNECTED; + return cl->state == MEI_FILE_CONNECTED; } static inline bool mei_cl_is_transitioning(struct mei_cl *cl) { From 03190c67ff72b5c56b24266762ab8abe68970f45 Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Mon, 23 Mar 2015 13:59:46 +0100 Subject: [PATCH 076/133] char: misc: document behaviour of open() an open syscall now assignes file->private_data to a pointer to the miscdevice structure. This reminds people not to duplicate code if they want this and not to depend on it being NULL. Signed-off-by: Martin Kepplinger Signed-off-by: Greg Kroah-Hartman --- drivers/char/misc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/char/misc.c b/drivers/char/misc.c index ffa97d261cf3..c892c296a4de 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -169,7 +169,9 @@ static const struct file_operations misc_fops = { * the minor number requested is used. * * The structure passed is linked into the kernel and may not be - * destroyed until it has been unregistered. + * destroyed until it has been unregistered. By default, an open() + * syscall to the device sets file->private_data to point to the + * structure. Drivers don't need open in fops for this. * * A zero is returned on success and a negative errno code for * failure. From a819a228feae6f936e2696c4946c53f883a3d480 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Mon, 9 Feb 2015 14:43:44 -0500 Subject: [PATCH 077/133] misc: tifm: match return type of wait_for_completion_timeout return type of wait_for_completion_timeout is unsigned long not int. The rc variable is in use for other calls so an additional timeout variable of type unsigned long is added. Signed-off-by: Nicholas Mc Guire Signed-off-by: Greg Kroah-Hartman --- drivers/misc/tifm_7xx1.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index a606c8901e18..a37a42f67088 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -236,6 +236,7 @@ static int tifm_7xx1_resume(struct pci_dev *dev) { struct tifm_adapter *fm = pci_get_drvdata(dev); int rc; + unsigned long timeout; unsigned int good_sockets = 0, bad_sockets = 0; unsigned long flags; unsigned char new_ids[fm->num_sockets]; @@ -272,8 +273,8 @@ static int tifm_7xx1_resume(struct pci_dev *dev) if (good_sockets) { fm->finish_me = &finish_resume; spin_unlock_irqrestore(&fm->lock, flags); - rc = wait_for_completion_timeout(&finish_resume, HZ); - dev_dbg(&dev->dev, "wait returned %d\n", rc); + timeout = wait_for_completion_timeout(&finish_resume, HZ); + dev_dbg(&dev->dev, "wait returned %lu\n", timeout); writel(TIFM_IRQ_FIFOMASK(good_sockets) | TIFM_IRQ_CARDMASK(good_sockets), fm->addr + FM_CLEAR_INTERRUPT_ENABLE); From 860cba13432e071442a39e9bab83258c631d33a6 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Mon, 9 Feb 2015 14:24:25 -0500 Subject: [PATCH 078/133] misc: carma: fixup return type of wait_for_completion_timeout return type of wait_for_completion_timeout is unsigned long not int. The rc variable is renamed timeout to reflect its use and the type adjusted to unsigned long. Signed-off-by: Nicholas Mc Guire Signed-off-by: Greg Kroah-Hartman --- drivers/misc/carma/carma-fpga-program.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c index fff8c43d9943..0b1bd85e4ae6 100644 --- a/drivers/misc/carma/carma-fpga-program.c +++ b/drivers/misc/carma/carma-fpga-program.c @@ -479,6 +479,7 @@ static int fpga_program_block(struct fpga_dev *priv, void *buf, size_t count) static noinline int fpga_program_cpu(struct fpga_dev *priv) { int ret; + unsigned long timeout; /* Disable the programmer */ fpga_programmer_disable(priv); @@ -497,8 +498,8 @@ static noinline int fpga_program_cpu(struct fpga_dev *priv) goto out_disable_controller; /* Wait for the interrupt handler to signal that programming finished */ - ret = wait_for_completion_timeout(&priv->completion, 2 * HZ); - if (!ret) { + timeout = wait_for_completion_timeout(&priv->completion, 2 * HZ); + if (!timeout) { dev_err(priv->dev, "Timed out waiting for completion\n"); ret = -ETIMEDOUT; goto out_disable_controller; @@ -536,6 +537,7 @@ static noinline int fpga_program_dma(struct fpga_dev *priv) struct sg_table table; dma_cookie_t cookie; int ret, i; + unsigned long timeout; /* Disable the programmer */ fpga_programmer_disable(priv); @@ -623,8 +625,8 @@ static noinline int fpga_program_dma(struct fpga_dev *priv) dev_dbg(priv->dev, "enabled the controller\n"); /* Wait for the interrupt handler to signal that programming finished */ - ret = wait_for_completion_timeout(&priv->completion, 2 * HZ); - if (!ret) { + timeout = wait_for_completion_timeout(&priv->completion, 2 * HZ); + if (!timeout) { dev_err(priv->dev, "Timed out waiting for completion\n"); ret = -ETIMEDOUT; goto out_disable_controller; From 2f9763190dd6356eec95548f4cfa8a36a71f767c Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Mon, 9 Feb 2015 14:09:09 -0500 Subject: [PATCH 079/133] misc: mic: fixup return type of wait_for_completion_timeout return type of wait_for_completion_timeout is unsigned long not int. The rc variable is renamed timeout to reflect its use and the type adjusted to unsigned long. Signed-off-by: Nicholas Mc Guire Acked-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_boot.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index ff2b0fb1a6be..d9fa609da061 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -309,7 +309,7 @@ void mic_complete_resume(struct mic_device *mdev) */ void mic_prepare_suspend(struct mic_device *mdev) { - int rc; + unsigned long timeout; #define MIC_SUSPEND_TIMEOUT (60 * HZ) @@ -331,10 +331,10 @@ void mic_prepare_suspend(struct mic_device *mdev) */ mic_set_state(mdev, MIC_SUSPENDING); mutex_unlock(&mdev->mic_mutex); - rc = wait_for_completion_timeout(&mdev->reset_wait, - MIC_SUSPEND_TIMEOUT); + timeout = wait_for_completion_timeout(&mdev->reset_wait, + MIC_SUSPEND_TIMEOUT); /* Force reset the card if the shutdown completion timed out */ - if (!rc) { + if (!timeout) { mutex_lock(&mdev->mic_mutex); mic_set_state(mdev, MIC_SUSPENDED); mutex_unlock(&mdev->mic_mutex); @@ -348,10 +348,10 @@ void mic_prepare_suspend(struct mic_device *mdev) */ mic_set_state(mdev, MIC_SUSPENDED); mutex_unlock(&mdev->mic_mutex); - rc = wait_for_completion_timeout(&mdev->reset_wait, - MIC_SUSPEND_TIMEOUT); + timeout = wait_for_completion_timeout(&mdev->reset_wait, + MIC_SUSPEND_TIMEOUT); /* Force reset the card if the shutdown completion timed out */ - if (!rc) + if (!timeout) mic_stop(mdev, true); break; default: From bd735995308b553cc3c7f6a975aa284b270c7e2c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Feb 2015 15:44:54 +0100 Subject: [PATCH 080/133] misc: Add attribute groups Add groups field to struct miscdevice for passing the attribute groups at device creation. In this way, the driver can avoid the manual call of device_create_file() after the device registration, which is basically a racy operation, in addition to the reduction of manual device_remove_file() calls. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/char/misc.c | 5 +++-- include/linux/miscdevice.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/char/misc.c b/drivers/char/misc.c index c892c296a4de..5bb3a2109ab7 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -207,8 +207,9 @@ int misc_register(struct miscdevice * misc) dev = MKDEV(MISC_MAJOR, misc->minor); - misc->this_device = device_create(misc_class, misc->parent, dev, - misc, "%s", misc->name); + misc->this_device = + device_create_with_groups(misc_class, misc->parent, dev, + misc, misc->groups, "%s", misc->name); if (IS_ERR(misc->this_device)) { int i = DYNAMIC_MINORS - misc->minor - 1; if (i < DYNAMIC_MINORS && i >= 0) diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index ee80dd7d9f60..819077c32690 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -52,6 +52,7 @@ #define MISC_DYNAMIC_MINOR 255 struct device; +struct attribute_group; struct miscdevice { int minor; @@ -60,6 +61,7 @@ struct miscdevice { struct list_head list; struct device *parent; struct device *this_device; + const struct attribute_group **groups; const char *nodename; umode_t mode; }; From 0daa7a0afd0fa7aad83cbde0fb341d7fbeac952c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Feb 2015 15:44:55 +0100 Subject: [PATCH 081/133] hwrng: Avoid manual device_create_file() calls Use the new group field of struct miscdevice for managing the sysfs entries instead of manually adding/removing via device_create_file() and device_remove_file(). This simplifies the code a lot and fixes the possible races. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/char/hw_random/core.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 32a8a867f7f8..0a8194525baf 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -299,11 +299,14 @@ static const struct file_operations rng_chrdev_ops = { .llseek = noop_llseek, }; +static const struct attribute_group *rng_dev_groups[]; + static struct miscdevice rng_miscdev = { .minor = RNG_MISCDEV_MINOR, .name = RNG_MODULE_NAME, .nodename = "hwrng", .fops = &rng_chrdev_ops, + .groups = rng_dev_groups, }; @@ -376,37 +379,22 @@ static DEVICE_ATTR(rng_available, S_IRUGO, hwrng_attr_available_show, NULL); +static struct attribute *rng_dev_attrs[] = { + &dev_attr_rng_current.attr, + &dev_attr_rng_available.attr, + NULL +}; + +ATTRIBUTE_GROUPS(rng_dev); static void __exit unregister_miscdev(void) { - device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available); - device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current); misc_deregister(&rng_miscdev); } static int __init register_miscdev(void) { - int err; - - err = misc_register(&rng_miscdev); - if (err) - goto out; - err = device_create_file(rng_miscdev.this_device, - &dev_attr_rng_current); - if (err) - goto err_misc_dereg; - err = device_create_file(rng_miscdev.this_device, - &dev_attr_rng_available); - if (err) - goto err_remove_current; -out: - return err; - -err_remove_current: - device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current); -err_misc_dereg: - misc_deregister(&rng_miscdev); - goto out; + return misc_register(&rng_miscdev); } static int hwrng_fillfn(void *unused) From fde25d25dbd997067058f7d4c2ff31600157e6f2 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 18 Mar 2015 12:29:21 -0700 Subject: [PATCH 082/133] Drivers: hv: vmbus: Perform device register in the per-channel work element This patch is a continuation of the rescind handling cleanup work. We cannot block in the global message handling work context especially if we are blocking waiting for the host to wake us up. I would like to thank Dexuan Cui for observing this problem. The current char-next branch is broken and this patch fixes the bug. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 143 +++++++++++++++++++++++++++----------- drivers/hv/connection.c | 6 +- drivers/hv/hyperv_vmbus.h | 2 +- 3 files changed, 107 insertions(+), 44 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 611789139f9b..5f8e47bf5ccc 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,10 @@ struct vmbus_channel_message_table_entry { void (*message_handler)(struct vmbus_channel_message_header *msg); }; +struct vmbus_rescind_work { + struct work_struct work; + struct vmbus_channel *channel; +}; /** * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message @@ -134,20 +139,6 @@ fw_error: EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); -static void vmbus_process_device_unregister(struct work_struct *work) -{ - struct device *dev; - struct vmbus_channel *channel = container_of(work, - struct vmbus_channel, - work); - - dev = get_device(&channel->device_obj->device); - if (dev) { - vmbus_device_unregister(channel->device_obj); - put_device(dev); - } -} - static void vmbus_sc_creation_cb(struct work_struct *work) { struct vmbus_channel *newchannel = container_of(work, @@ -220,6 +211,40 @@ static void free_channel(struct vmbus_channel *channel) queue_work(vmbus_connection.work_queue, &channel->work); } +static void process_rescind_fn(struct work_struct *work) +{ + struct vmbus_rescind_work *rc_work; + struct vmbus_channel *channel; + struct device *dev; + + rc_work = container_of(work, struct vmbus_rescind_work, work); + channel = rc_work->channel; + + /* + * We have already acquired a reference on the channel + * and so it cannot vanish underneath us. + * It is possible (while very unlikely) that we may + * get here while the processing of the initial offer + * is still not complete. Deal with this situation by + * just waiting until the channel is in the correct state. + */ + + while (channel->work.func != release_channel) + msleep(1000); + + if (channel->device_obj) { + dev = get_device(&channel->device_obj->device); + if (dev) { + vmbus_device_unregister(channel->device_obj); + put_device(dev); + } + } else { + hv_process_channel_removal(channel, + channel->offermsg.child_relid); + } + kfree(work); +} + static void percpu_channel_enq(void *arg) { struct vmbus_channel *channel = arg; @@ -282,6 +307,46 @@ void vmbus_free_channels(void) } } +static void vmbus_do_device_register(struct work_struct *work) +{ + struct hv_device *device_obj; + int ret; + unsigned long flags; + struct vmbus_channel *newchannel = container_of(work, + struct vmbus_channel, + work); + + ret = vmbus_device_register(newchannel->device_obj); + if (ret != 0) { + pr_err("unable to add child device object (relid %d)\n", + newchannel->offermsg.child_relid); + spin_lock_irqsave(&vmbus_connection.channel_lock, flags); + list_del(&newchannel->listentry); + device_obj = newchannel->device_obj; + newchannel->device_obj = NULL; + spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + + if (newchannel->target_cpu != get_cpu()) { + put_cpu(); + smp_call_function_single(newchannel->target_cpu, + percpu_channel_deq, newchannel, true); + } else { + percpu_channel_deq(newchannel); + put_cpu(); + } + + kfree(device_obj); + if (!newchannel->rescind) { + free_channel(newchannel); + return; + } + } + /* + * The next state for this channel is to be freed. + */ + INIT_WORK(&newchannel->work, release_channel); +} + /* * vmbus_process_offer - Process the offer by creating a channel/device * associated with this offer @@ -291,7 +356,6 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) struct vmbus_channel *channel; bool fnew = true; bool enq = false; - int ret; unsigned long flags; /* Make sure this is a new offer */ @@ -393,16 +457,13 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) * Add the new device to the bus. This will kick off device-driver * binding which eventually invokes the device driver's AddDevice() * method. + * Invoke this call on the per-channel work context. + * Until we return from this function, rescind offer message + * cannot be processed as we are running on the global message + * handling work. */ - ret = vmbus_device_register(newchannel->device_obj); - if (ret != 0) { - pr_err("unable to add child device object (relid %d)\n", - newchannel->offermsg.child_relid); - - kfree(newchannel->device_obj); - goto err_deq_chan; - } - + INIT_WORK(&newchannel->work, vmbus_do_device_register); + queue_work(newchannel->controlwq, &newchannel->work); return; err_deq_chan: @@ -556,33 +617,31 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) { struct vmbus_channel_rescind_offer *rescind; struct vmbus_channel *channel; - unsigned long flags; + struct vmbus_rescind_work *rc_work; rescind = (struct vmbus_channel_rescind_offer *)hdr; - channel = relid2channel(rescind->child_relid); + channel = relid2channel(rescind->child_relid, true); if (channel == NULL) { hv_process_channel_removal(NULL, rescind->child_relid); return; } - spin_lock_irqsave(&channel->lock, flags); - channel->rescind = true; - spin_unlock_irqrestore(&channel->lock, flags); - - if (channel->device_obj) { - /* - * We will have to unregister this device from the - * driver core. Do this in the per-channel work context. - * Note that we are currently executing on the global - * workq for handling messages from the host. - */ - INIT_WORK(&channel->work, vmbus_process_device_unregister); - queue_work(channel->controlwq, &channel->work); - } else { - hv_process_channel_removal(channel, - channel->offermsg.child_relid); + /* + * We have acquired a reference on the channel and have posted + * the rescind state. Perform further cleanup in a work context + * that is different from the global work context in which + * we process messages from the host (we are currently executing + * on that global context. + */ + rc_work = kzalloc(sizeof(struct vmbus_rescind_work), GFP_KERNEL); + if (!rc_work) { + pr_err("Unable to allocate memory for rescind processing "); + return; } + rc_work->channel = channel; + INIT_WORK(&rc_work->work, process_rescind_fn); + schedule_work(&rc_work->work); } /* diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 583d7d42b46d..8bcd3071c84f 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -270,7 +270,7 @@ static struct vmbus_channel *pcpu_relid2channel(u32 relid) * relid2channel - Get the channel object given its * child relative id (ie channel id) */ -struct vmbus_channel *relid2channel(u32 relid) +struct vmbus_channel *relid2channel(u32 relid, bool rescind) { struct vmbus_channel *channel; struct vmbus_channel *found_channel = NULL; @@ -282,6 +282,8 @@ struct vmbus_channel *relid2channel(u32 relid) list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { if (channel->offermsg.child_relid == relid) { found_channel = channel; + if (rescind) + found_channel->rescind = true; break; } else if (!list_empty(&channel->sc_list)) { /* @@ -292,6 +294,8 @@ struct vmbus_channel *relid2channel(u32 relid) sc_list); if (cur_sc->offermsg.child_relid == relid) { found_channel = cur_sc; + if (rescind) + found_channel->rescind = true; break; } } diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 88af4ec559c4..63395896baf9 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -698,7 +698,7 @@ void vmbus_device_unregister(struct hv_device *device_obj); /* VmbusChildDeviceDestroy( */ /* struct hv_device *); */ -struct vmbus_channel *relid2channel(u32 relid); +struct vmbus_channel *relid2channel(u32 relid, bool rescind); void vmbus_free_channels(void); From f3f6eb80872becdbce07f7d2670d3f8ae5119752 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 18 Mar 2015 12:29:22 -0700 Subject: [PATCH 083/133] Drivers: hv: hv_balloon: keep locks balanced on add_memory() failure When add_memory() fails the following BUG is observed: [ 743.646107] hv_balloon: hot_add memory failed error is -17 [ 743.679973] [ 743.680930] ===================================== [ 743.680930] [ BUG: bad unlock balance detected! ] [ 743.680930] 3.19.0-rc5_bug1131426+ #552 Not tainted [ 743.680930] ------------------------------------- [ 743.680930] kworker/0:2/255 is trying to release lock (&dm_device.ha_region_mutex) at: [ 743.680930] [] mutex_unlock+0xe/0x10 [ 743.680930] but there are no more locks to release! This happens as we don't acquire ha_region_mutex and hot_add_req() expects us to as it does unconditional mutex_unlock(). Acquire the lock on the error path. The current char-next branch is broken and this patch fixes the bug. Signed-off-by: Vitaly Kuznetsov Acked-by: Jason Wang Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index c5bb87244dd7..f1f17c5ad8d8 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -652,6 +652,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, } has->ha_end_pfn -= HA_CHUNK; has->covered_end_pfn -= processed_pfn; + mutex_lock(&dm_device.ha_region_mutex); break; } From 5abbbb75d733793b1637441691de95b7665cee85 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 18 Mar 2015 12:29:23 -0700 Subject: [PATCH 084/133] Drivers: hv: hv_balloon: don't lose memory when onlining order is not natural Memory blocks can be onlined in random order. When this order is not natural some memory pages are not onlined because of the redundant check in hv_online_page(). Here is a real world scenario: 1) Host tries to hot-add the following (process_hot_add): pg_start=rg_start=0x48000, pfn_cnt=111616, rg_size=262144 2) This results in adding 4 memory blocks: [ 109.057866] init_memory_mapping: [mem 0x48000000-0x4fffffff] [ 114.102698] init_memory_mapping: [mem 0x50000000-0x57ffffff] [ 119.168039] init_memory_mapping: [mem 0x58000000-0x5fffffff] [ 124.233053] init_memory_mapping: [mem 0x60000000-0x67ffffff] The last one is incomplete but we have special has->covered_end_pfn counter to avoid onlining non-backed frames and hv_bring_pgs_online() function to bring them online later on. 3) Now we have 4 offline memory blocks: /sys/devices/system/memory/memory9-12 $ for f in /sys/devices/system/memory/memory*/state; do echo $f `cat $f`; done | grep -v onlin /sys/devices/system/memory/memory10/state offline /sys/devices/system/memory/memory11/state offline /sys/devices/system/memory/memory12/state offline /sys/devices/system/memory/memory9/state offline 4) We bring them online in non-natural order: $grep MemTotal /proc/meminfo MemTotal: 966348 kB $echo online > /sys/devices/system/memory/memory12/state && grep MemTotal /proc/meminfo MemTotal: 1019596 kB $echo online > /sys/devices/system/memory/memory11/state && grep MemTotal /proc/meminfo MemTotal: 1150668 kB $echo online > /sys/devices/system/memory/memory9/state && grep MemTotal /proc/meminfo MemTotal: 1150668 kB As you can see memory9 block gives us zero additional memory. We can also observe a huge discrepancy between host- and guest-reported memory sizes. The root cause of the issue is the redundant pg >= covered_start_pfn check (and covered_start_pfn advancing) in hv_online_page(). When upper memory block in being onlined before the lower one (memory12 and memory11 in the above case) we advance the covered_start_pfn pointer and all memory9 pages do not pass the check. If the assumption that host always gives us requests in sequential order and pg_start always equals rg_start when the first request for the new HA region is received (that's the case in my testing) is correct than we can get rid of covered_start_pfn and pg >= start_pfn check in hv_online_page() is sufficient. The current char-next branch is broken and this patch fixes the bug. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index f1f17c5ad8d8..014256a2ef88 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -428,14 +428,13 @@ struct dm_info_msg { * currently hot added. We hot add in multiples of 128M * chunks; it is possible that we may not be able to bring * online all the pages in the region. The range - * covered_start_pfn : covered_end_pfn defines the pages that can + * covered_end_pfn defines the pages that can * be brough online. */ struct hv_hotadd_state { struct list_head list; unsigned long start_pfn; - unsigned long covered_start_pfn; unsigned long covered_end_pfn; unsigned long ha_end_pfn; unsigned long end_pfn; @@ -679,8 +678,7 @@ static void hv_online_page(struct page *pg) list_for_each(cur, &dm_device.ha_region_list) { has = list_entry(cur, struct hv_hotadd_state, list); - cur_start_pgp = (unsigned long) - pfn_to_page(has->covered_start_pfn); + cur_start_pgp = (unsigned long)pfn_to_page(has->start_pfn); cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn); if (((unsigned long)pg >= cur_start_pgp) && @@ -692,7 +690,6 @@ static void hv_online_page(struct page *pg) __online_page_set_limits(pg); __online_page_increment_counters(pg); __online_page_free(pg); - has->covered_start_pfn++; } } } @@ -736,10 +733,9 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) * is, update it. */ - if (has->covered_end_pfn != start_pfn) { + if (has->covered_end_pfn != start_pfn) has->covered_end_pfn = start_pfn; - has->covered_start_pfn = start_pfn; - } + return true; } @@ -784,7 +780,6 @@ static unsigned long handle_pg_range(unsigned long pg_start, pgs_ol = pfn_cnt; hv_bring_pgs_online(start_pfn, pgs_ol); has->covered_end_pfn += pgs_ol; - has->covered_start_pfn += pgs_ol; pfn_cnt -= pgs_ol; } @@ -845,7 +840,6 @@ static unsigned long process_hot_add(unsigned long pg_start, list_add_tail(&ha_region->list, &dm_device.ha_region_list); ha_region->start_pfn = rg_start; ha_region->ha_end_pfn = rg_start; - ha_region->covered_start_pfn = pg_start; ha_region->covered_end_pfn = pg_start; ha_region->end_pfn = rg_start + rg_size; } From 5ef5b6927f14f29cacd78fa1fb861661a5367f13 Mon Sep 17 00:00:00 2001 From: Nick Meier Date: Wed, 18 Mar 2015 12:29:24 -0700 Subject: [PATCH 085/133] Drivers: hv: vmbus: Correcting truncation error for constant HV_CRASH_CTL_CRASH_NOTIFY HV_CRASH_CTL_CRASH_NOTIFY is a 64 bit number. Depending on the usage context, the value may be truncated. This patch is in response from the following email from Wu Fengguang : From: Wu Fengguang Subject: [char-misc:char-misc-testing 25/45] drivers/hv/vmbus_drv.c:67:9: sparse: constant 0x8000000000000000 is so big it is unsigned long tree: git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git char-misc-testing head: b3de8e3719e582f3182bb504295e4a8e43c8c96f commit: 96c1d0581d00f7abe033350edb021a9d947d8d81 [25/45] Drivers: hv: vmbus: Add support for VMBus panic notifier handler reproduce: # apt-get install sparse git checkout 96c1d0581d00f7abe033350edb021a9d947d8d81 make ARCH=x86_64 allmodconfig make C=1 CF=-D__CHECK_ENDIAN__ sparse warnings: (new ones prefixed by >>) drivers/hv/vmbus_drv.c:67:9: sparse: constant 0x8000000000000000 is so big it is unsigned long ... Signed-off-by: Nick Meier Reported-by: Wu Fengguang Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hyperv_vmbus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 63395896baf9..c8e27e0fdc99 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -58,7 +58,7 @@ enum hv_cpuid_function { #define HV_X64_MSR_CRASH_P4 0x40000104 #define HV_X64_MSR_CRASH_CTL 0x40000105 -#define HV_CRASH_CTL_CRASH_NOTIFY 0x8000000000000000 +#define HV_CRASH_CTL_CRASH_NOTIFY (1ULL << 63) /* Define version of the synthetic interrupt controller. */ #define HV_SYNIC_VERSION (1) From 177757423fde68a6ce773d2b67b468fbd8367bbc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 18 Mar 2015 12:29:25 -0700 Subject: [PATCH 086/133] hv: vmbus: missing curly braces in vmbus_process_offer() The indenting makes it clear that there were curly braces intended here. Fixes: 2dd37cb81580 ('Drivers: hv: vmbus: Handle both rescind and offer messages in the same context') Signed-off-by: Dan Carpenter Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 5f8e47bf5ccc..25dbbaf42266 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -415,7 +415,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) newchannel->state = CHANNEL_OPEN_STATE; channel->num_sc++; - if (channel->sc_creation_callback != NULL) + if (channel->sc_creation_callback != NULL) { /* * We need to invoke the sub-channel creation * callback; invoke this in a seperate work @@ -427,6 +427,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) vmbus_sc_creation_cb); queue_work(newchannel->controlwq, &newchannel->work); + } return; } From b4affbbb7217e440f80c922b2b304795758d40bb Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 18 Mar 2015 12:29:26 -0700 Subject: [PATCH 087/133] tools: hv: fcopy_daemon: support >2GB files for x86_32 guest Without this patch, hv_fcopy_daemon's hv_copy_data() -> pwrite() will fail for >2GB file offset. The current char-next branch is broken and this patch fixes the bug. Signed-off-by: Alex Ng Signed-off-by: Dexuan Cui Cc: K. Y. Srinivasan Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hv/Makefile b/tools/hv/Makefile index 99ffe61051a7..a8ab79556926 100644 --- a/tools/hv/Makefile +++ b/tools/hv/Makefile @@ -3,7 +3,7 @@ CC = $(CROSS_COMPILE)gcc PTHREAD_LIBS = -lpthread WARNINGS = -Wall -Wextra -CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) +CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) $(shell getconf LFS_CFLAGS) all: hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon %: %.c From f2eddbc9f1a466329c68f3b75e89cfacd2792365 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 18 Mar 2015 12:29:27 -0700 Subject: [PATCH 088/133] Drivers: hv: vmbus: Fix a bug in rescind processing in vmbus_close_internal() When a channel has been rescinded, the close operation is a noop. Restructure the code so we deal with the rescind condition after we properly cleanup the channel. I would like to thank Dexuan Cui for observing this problem. The current code leaks memory when the channel is rescinded. The current char-next branch is broken and this patch fixes the bug. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index da53180f5eb4..2c8206d820f2 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -501,15 +501,6 @@ static int vmbus_close_internal(struct vmbus_channel *channel) put_cpu(); } - /* - * If the channel has been rescinded; process device removal. - */ - if (channel->rescind) { - hv_process_channel_removal(channel, - channel->offermsg.child_relid); - return 0; - } - /* Send a closing message */ msg = &channel->close_msg.msg; @@ -549,6 +540,12 @@ static int vmbus_close_internal(struct vmbus_channel *channel) free_pages((unsigned long)channel->ringbuffer_pages, get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); + /* + * If the channel has been rescinded; process device removal. + */ + if (channel->rescind) + hv_process_channel_removal(channel, + channel->offermsg.child_relid); return ret; } From 4ce50e9491c5c92baf8bb7af85eb25398d3f70af Mon Sep 17 00:00:00 2001 From: Vaughan Cao Date: Wed, 18 Mar 2015 12:29:28 -0700 Subject: [PATCH 089/133] hv: hypervvssd: call endmntent before call setmntent again If freeze fails, vss_operate will re-enter itself to thaw. But it forgets to call endmntent() before it recalls setmntent() again. Signed-off-by: Vaughan Cao Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_vss_daemon.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index 5e63f70bd956..506dd0148828 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -81,6 +81,7 @@ static int vss_operate(int operation) char match[] = "/dev/"; FILE *mounts; struct mntent *ent; + char errdir[1024] = {0}; unsigned int cmd; int error = 0, root_seen = 0, save_errno = 0; @@ -115,6 +116,8 @@ static int vss_operate(int operation) goto err; } + endmntent(mounts); + if (root_seen) { error |= vss_do_freeze("/", cmd); if (error && operation == VSS_OP_FREEZE) @@ -124,16 +127,19 @@ static int vss_operate(int operation) goto out; err: save_errno = errno; + if (ent) { + strncpy(errdir, ent->mnt_dir, sizeof(errdir)-1); + endmntent(mounts); + } vss_operate(VSS_OP_THAW); /* Call syslog after we thaw all filesystems */ if (ent) syslog(LOG_ERR, "FREEZE of %s failed; error:%d %s", - ent->mnt_dir, save_errno, strerror(save_errno)); + errdir, save_errno, strerror(save_errno)); else syslog(LOG_ERR, "FREEZE of / failed; error:%d %s", save_errno, strerror(save_errno)); out: - endmntent(mounts); return error; } From b3a19b36ad7450d4c8caeb7d9f623af3da32a578 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 18 Mar 2015 12:29:29 -0700 Subject: [PATCH 090/133] Drivers: hv: vmbus: Export the vmbus_sendpacket_pagebuffer_ctl() Export the vmbus_sendpacket_pagebuffer_ctl() interface. This export will be used by the netvsc driver. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 2c8206d820f2..fddd3b5e59a9 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -707,6 +707,7 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, return ret; } +EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer_ctl); /* * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer From 5f5cc81733baf7b67a039a931dae282e53bf97b9 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 18 Mar 2015 12:29:30 -0700 Subject: [PATCH 091/133] Drivers: hv: vmbus: Fix a siganlling host signalling issue Handle the case when the write to the ringbuffer fails. In this case, unconditionally signal the host. Since we may have deferred signalling the host based on the kick_q parameter, signalling the host unconditionally in this case deals with the issue. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index fddd3b5e59a9..54da66dc7d16 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -611,7 +611,19 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); - if ((ret == 0) && kick_q && signal) + /* + * Signalling the host is conditional on many factors: + * 1. The ring state changed from being empty to non-empty. + * This is tracked by the variable "signal". + * 2. The variable kick_q tracks if more data will be placed + * on the ring. We will not signal if more data is + * to be placed. + * + * If we cannot write to the ring-buffer; signal the host + * even if we may not have written anything. This is a rare + * enough condition that it should not matter. + */ + if (((ret == 0) && kick_q && signal) || (ret)) vmbus_setevent(channel); return ret; @@ -702,7 +714,19 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); - if ((ret == 0) && kick_q && signal) + /* + * Signalling the host is conditional on many factors: + * 1. The ring state changed from being empty to non-empty. + * This is tracked by the variable "signal". + * 2. The variable kick_q tracks if more data will be placed + * on the ring. We will not signal if more data is + * to be placed. + * + * If we cannot write to the ring-buffer; signal the host + * even if we may not have written anything. This is a rare + * enough condition that it should not matter. + */ + if (((ret == 0) && kick_q && signal) || (ret)) vmbus_setevent(channel); return ret; From 4d8beff2ae07fad85d723b4cdf704b05f0ed4794 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 16 Mar 2015 20:54:38 +0100 Subject: [PATCH 092/133] uio: constify of_device_id array of_device_id is always used as const. (See driver.of_match_table and open firmware functions) Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_pdrv_genirq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index f598ecddc8a7..59c172a0d13a 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -252,7 +252,7 @@ static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = { }; #ifdef CONFIG_OF -static struct of_device_id uio_of_genirq_match[] = { +static const struct of_device_id uio_of_genirq_match[] = { { /* This is filled with module_parm */ }, { /* Sentinel */ }, }; From 0a56c0e1e7ad4dc3721e96c499f3074cb6867cfe Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 16 Mar 2015 20:20:29 +0100 Subject: [PATCH 093/133] w1: constify of_device_id array of_device_id is always used as const. (See driver.of_match_table and open firmware functions) Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/mxc_w1.c | 2 +- drivers/w1/masters/omap_hdq.c | 2 +- drivers/w1/masters/w1-gpio.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index 53bf2c860ad3..a4621757a47f 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c @@ -166,7 +166,7 @@ static int mxc_w1_remove(struct platform_device *pdev) return 0; } -static struct of_device_id mxc_w1_dt_ids[] = { +static const struct of_device_id mxc_w1_dt_ids[] = { { .compatible = "fsl,imx21-owire" }, { /* sentinel */ } }; diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 03321d6a2684..e7d448963a24 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -72,7 +72,7 @@ struct hdq_data { static int omap_hdq_probe(struct platform_device *pdev); static int omap_hdq_remove(struct platform_device *pdev); -static struct of_device_id omap_hdq_dt_ids[] = { +static const struct of_device_id omap_hdq_dt_ids[] = { { .compatible = "ti,omap3-1w" }, {} }; diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index b99a932ad901..8f7848c62811 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -68,7 +68,7 @@ static u8 w1_gpio_read_bit(void *data) } #if defined(CONFIG_OF) -static struct of_device_id w1_gpio_dt_ids[] = { +static const struct of_device_id w1_gpio_dt_ids[] = { { .compatible = "w1-gpio" }, {} }; From 3a267d3b22f56bf5ba379c20604fd28409119698 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 21 Feb 2015 18:53:47 -0800 Subject: [PATCH 094/133] i8k: Remove use of seq_printf return value The seq_printf return value, because it's frequently misused, will eventually be converted to void. See: commit 1f33c41c03da ("seq_file: Rename seq_overflow() to seq_has_overflowed() and make public") Signed-off-by: Joe Perches Acked-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/char/i8k.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 24cc4ed9a780..a43048b5b05f 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -510,13 +510,15 @@ static int i8k_proc_show(struct seq_file *seq, void *offset) * 9) AC power * 10) Fn Key status */ - return seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", - I8K_PROC_FMT, - bios_version, - i8k_get_dmi_data(DMI_PRODUCT_SERIAL), - cpu_temp, - left_fan, right_fan, left_speed, right_speed, - ac_power, fn_key); + seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", + I8K_PROC_FMT, + bios_version, + i8k_get_dmi_data(DMI_PRODUCT_SERIAL), + cpu_temp, + left_fan, right_fan, left_speed, right_speed, + ac_power, fn_key); + + return 0; } static int i8k_open_fs(struct inode *inode, struct file *file) From 160b7daab96636d089fee4e0487da49c70ca4e15 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 16 Mar 2015 20:20:33 +0100 Subject: [PATCH 095/133] coresight-replicator: constify of_device_id array of_device_id is always used as const. (See driver.of_match_table and open firmware functions) Signed-off-by: Fabian Frederick Signed-off-by: Greg Kroah-Hartman --- drivers/coresight/coresight-replicator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/coresight/coresight-replicator.c b/drivers/coresight/coresight-replicator.c index cdf05537d574..75b9abd804e6 100644 --- a/drivers/coresight/coresight-replicator.c +++ b/drivers/coresight/coresight-replicator.c @@ -107,7 +107,7 @@ static int replicator_remove(struct platform_device *pdev) return 0; } -static struct of_device_id replicator_match[] = { +static const struct of_device_id replicator_match[] = { {.compatible = "arm,coresight-replicator"}, {} }; From 73cffdb65e679b98893f484063462c045adcf212 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Mar 2015 08:11:34 -0700 Subject: [PATCH 096/133] Drivers: hv: vmbus: Don't wait after requesting offers Don't wait after sending request for offers to the host. This wait is unnecessary and simply adds 5 seconds to the boot time. Signed-off-by: K. Y. Srinivasan Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 25dbbaf42266..bb39705a89d9 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -884,7 +884,6 @@ int vmbus_request_offers(void) struct vmbus_channel_message_header *msg; struct vmbus_channel_msginfo *msginfo; int ret; - unsigned long t; msginfo = kmalloc(sizeof(*msginfo) + sizeof(struct vmbus_channel_message_header), @@ -892,8 +891,6 @@ int vmbus_request_offers(void) if (!msginfo) return -ENOMEM; - init_completion(&msginfo->waitevent); - msg = (struct vmbus_channel_message_header *)msginfo->msg; msg->msgtype = CHANNELMSG_REQUESTOFFERS; @@ -907,14 +904,6 @@ int vmbus_request_offers(void) goto cleanup; } - t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - - cleanup: kfree(msginfo); From 03515f323c3766f0a69b62743794577bbd9f87c9 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 19 Mar 2015 10:49:42 +0100 Subject: [PATCH 097/133] MAINTAINERS: change Atmel ssc driver entry I take over the maintainship from Voice. Signed-off-by: Nicolas Ferre Cc: Mark Brown Cc: Arnd Bergmann Acked-by: Bo Shen Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 358eb0105e00..d7490020c0f3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1819,7 +1819,7 @@ S: Supported F: drivers/spi/spi-atmel.* ATMEL SSC DRIVER -M: Bo Shen +M: Nicolas Ferre L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: drivers/misc/atmel-ssc.c From 0b9641f5722a9c08cacb534d633ff469ab02a288 Mon Sep 17 00:00:00 2001 From: Gilad Avidov Date: Wed, 25 Mar 2015 11:37:31 -0600 Subject: [PATCH 098/133] spmi: remove wakeup command before slave probe According to spmi spec a slave powers up into startup state and then transitions into active state. Thus, the wakeup command is not required before calling the slave's probe. The wakeup command is only needed for slaves that are in sleep state after receiving the sleep command. Cc: galak@codeaurora.org Reviewed-by: Stephen Boyd Reviewed-by: Sagar Dharia Acked-by: Josh Cartwright Signed-off-by: Gilad Avidov Tested-by: Ivan T. Ivanov Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index 1d92f5103ebf..94938436aef9 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. +/* + * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -316,11 +317,6 @@ static int spmi_drv_probe(struct device *dev) struct spmi_device *sdev = to_spmi_device(dev); int err; - /* Ensure the slave is in ACTIVE state */ - err = spmi_command_wakeup(sdev); - if (err) - goto fail_wakeup; - pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -335,7 +331,6 @@ fail_probe: pm_runtime_disable(dev); pm_runtime_set_suspended(dev); pm_runtime_put_noidle(dev); -fail_wakeup: return err; } From d0c6ae41d12ad7b2ba271f279936327320b6671c Mon Sep 17 00:00:00 2001 From: Gilad Avidov Date: Wed, 25 Mar 2015 11:37:32 -0600 Subject: [PATCH 099/133] spmi: pmic_arb: add support for hw version 2 Qualcomm PMIC Arbiter version-2 changes from version-1 are: - Some different register offsets. - New channel register space, one per PMIC peripheral (ppid). All tx traffic uses these channels. - New observer register space. All rx trafic uses this space. - Different command format for spmi command registers. Reviewed-by: Sagar Dharia Signed-off-by: Gilad Avidov Tested-by: Ivan T. Ivanov Signed-off-by: Greg Kroah-Hartman --- .../bindings/spmi/qcom,spmi-pmic-arb.txt | 6 +- drivers/spmi/spmi-pmic-arb.c | 323 ++++++++++++++---- 2 files changed, 267 insertions(+), 62 deletions(-) diff --git a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt index 715d0998af8e..e16b9b5afc70 100644 --- a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt +++ b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt @@ -1,6 +1,6 @@ Qualcomm SPMI Controller (PMIC Arbiter) -The SPMI PMIC Arbiter is found on the Snapdragon 800 Series. It is an SPMI +The SPMI PMIC Arbiter is found on Snapdragon chipsets. It is an SPMI controller with wrapping arbitration logic to allow for multiple on-chip devices to control a single SPMI master. @@ -19,6 +19,10 @@ Required properties: "core" - core registers "intr" - interrupt controller registers "cnfg" - configuration registers + Registers used only for V2 PMIC Arbiter: + "chnls" - tx-channel per virtual slave registers. + "obsrvr" - rx-channel (called observer) per virtual slave registers. + - reg : address + size pairs describing the PMIC arb register sets; order must correspond with the order of entries in reg-names - #address-cells : must be set to 2 diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 20559ab3466d..d7119db49cfe 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. +/* + * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -25,22 +26,18 @@ /* PMIC Arbiter configuration registers */ #define PMIC_ARB_VERSION 0x0000 +#define PMIC_ARB_VERSION_V2_MIN 0x20010000 #define PMIC_ARB_INT_EN 0x0004 -/* PMIC Arbiter channel registers */ -#define PMIC_ARB_CMD(N) (0x0800 + (0x80 * (N))) -#define PMIC_ARB_CONFIG(N) (0x0804 + (0x80 * (N))) -#define PMIC_ARB_STATUS(N) (0x0808 + (0x80 * (N))) -#define PMIC_ARB_WDATA0(N) (0x0810 + (0x80 * (N))) -#define PMIC_ARB_WDATA1(N) (0x0814 + (0x80 * (N))) -#define PMIC_ARB_RDATA0(N) (0x0818 + (0x80 * (N))) -#define PMIC_ARB_RDATA1(N) (0x081C + (0x80 * (N))) - -/* Interrupt Controller */ -#define SPMI_PIC_OWNER_ACC_STATUS(M, N) (0x0000 + ((32 * (M)) + (4 * (N)))) -#define SPMI_PIC_ACC_ENABLE(N) (0x0200 + (4 * (N))) -#define SPMI_PIC_IRQ_STATUS(N) (0x0600 + (4 * (N))) -#define SPMI_PIC_IRQ_CLEAR(N) (0x0A00 + (4 * (N))) +/* PMIC Arbiter channel registers offsets */ +#define PMIC_ARB_CMD 0x00 +#define PMIC_ARB_CONFIG 0x04 +#define PMIC_ARB_STATUS 0x08 +#define PMIC_ARB_WDATA0 0x10 +#define PMIC_ARB_WDATA1 0x14 +#define PMIC_ARB_RDATA0 0x18 +#define PMIC_ARB_RDATA1 0x1C +#define PMIC_ARB_REG_CHNL(N) (0x800 + 0x4 * (N)) /* Mapping Table */ #define SPMI_MAPPING_TABLE_REG(N) (0x0B00 + (4 * (N))) @@ -52,6 +49,7 @@ #define SPMI_MAPPING_TABLE_LEN 255 #define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */ +#define PPID_TO_CHAN_TABLE_SZ BIT(12) /* PPID is 12bit chan is 1byte*/ /* Ownership Table */ #define SPMI_OWNERSHIP_TABLE_REG(N) (0x0700 + (4 * (N))) @@ -88,6 +86,7 @@ enum pmic_arb_cmd_op_code { /* Maximum number of support PMIC peripherals */ #define PMIC_ARB_MAX_PERIPHS 256 +#define PMIC_ARB_MAX_CHNL 128 #define PMIC_ARB_PERIPH_ID_VALID (1 << 15) #define PMIC_ARB_TIMEOUT_US 100 #define PMIC_ARB_MAX_TRANS_BYTES (8) @@ -98,14 +97,17 @@ enum pmic_arb_cmd_op_code { /* interrupt enable bit */ #define SPMI_PIC_ACC_ENABLE_BIT BIT(0) +struct pmic_arb_ver_ops; + /** * spmi_pmic_arb_dev - SPMI PMIC Arbiter object * - * @base: address of the PMIC Arbiter core registers. + * @rd_base: on v1 "core", on v2 "observer" register base off DT. + * @wr_base: on v1 "core", on v2 "chnls" register base off DT. * @intr: address of the SPMI interrupt control registers. * @cnfg: address of the PMIC Arbiter configuration registers. * @lock: lock to synchronize accesses. - * @channel: which channel to use for accesses. + * @channel: execution environment channel to use for accesses. * @irq: PMIC ARB interrupt. * @ee: the current Execution Environment * @min_apid: minimum APID (used for bounding IRQ search) @@ -113,10 +115,14 @@ enum pmic_arb_cmd_op_code { * @mapping_table: in-memory copy of PPID -> APID mapping table. * @domain: irq domain object for PMIC IRQ domain * @spmic: SPMI controller object - * @apid_to_ppid: cached mapping from APID to PPID + * @apid_to_ppid: in-memory copy of APID -> PPID mapping table. + * @ver_ops: version dependent operations. + * @ppid_to_chan in-memory copy of PPID -> channel (APID) mapping table. + * v2 only. */ struct spmi_pmic_arb_dev { - void __iomem *base; + void __iomem *rd_base; + void __iomem *wr_base; void __iomem *intr; void __iomem *cnfg; raw_spinlock_t lock; @@ -129,17 +135,54 @@ struct spmi_pmic_arb_dev { struct irq_domain *domain; struct spmi_controller *spmic; u16 apid_to_ppid[256]; + const struct pmic_arb_ver_ops *ver_ops; + u8 *ppid_to_chan; +}; + +/** + * pmic_arb_ver: version dependent functionality. + * + * @non_data_cmd: on v1 issues an spmi non-data command. + * on v2 no HW support, returns -EOPNOTSUPP. + * @offset: on v1 offset of per-ee channel. + * on v2 offset of per-ee and per-ppid channel. + * @fmt_cmd: formats a GENI/SPMI command. + * @owner_acc_status: on v1 offset of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn + * on v2 offset of SPMI_PIC_OWNERm_ACC_STATUSn. + * @acc_enable: on v1 offset of PMIC_ARB_SPMI_PIC_ACC_ENABLEn + * on v2 offset of SPMI_PIC_ACC_ENABLEn. + * @irq_status: on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_STATUSn + * on v2 offset of SPMI_PIC_IRQ_STATUSn. + * @irq_clear: on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_CLEARn + * on v2 offset of SPMI_PIC_IRQ_CLEARn. + */ +struct pmic_arb_ver_ops { + /* spmi commands (read_cmd, write_cmd, cmd) functionality */ + u32 (*offset)(struct spmi_pmic_arb_dev *dev, u8 sid, u16 addr); + u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc); + int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid); + /* Interrupts controller functionality (offset of PIC registers) */ + u32 (*owner_acc_status)(u8 m, u8 n); + u32 (*acc_enable)(u8 n); + u32 (*irq_status)(u8 n); + u32 (*irq_clear)(u8 n); }; static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset) { - return readl_relaxed(dev->base + offset); + return readl_relaxed(dev->rd_base + offset); } static inline void pmic_arb_base_write(struct spmi_pmic_arb_dev *dev, u32 offset, u32 val) { - writel_relaxed(val, dev->base + offset); + writel_relaxed(val, dev->wr_base + offset); +} + +static inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb_dev *dev, + u32 offset, u32 val) +{ + writel_relaxed(val, dev->rd_base + offset); } /** @@ -168,15 +211,16 @@ pa_write_data(struct spmi_pmic_arb_dev *dev, const u8 *buf, u32 reg, u8 bc) pmic_arb_base_write(dev, reg, data); } -static int pmic_arb_wait_for_done(struct spmi_controller *ctrl) +static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, + void __iomem *base, u8 sid, u16 addr) { struct spmi_pmic_arb_dev *dev = spmi_controller_get_drvdata(ctrl); u32 status = 0; u32 timeout = PMIC_ARB_TIMEOUT_US; - u32 offset = PMIC_ARB_STATUS(dev->channel); + u32 offset = dev->ver_ops->offset(dev, sid, addr) + PMIC_ARB_STATUS; while (timeout--) { - status = pmic_arb_base_read(dev, offset); + status = readl_relaxed(base + offset); if (status & PMIC_ARB_STATUS_DONE) { if (status & PMIC_ARB_STATUS_DENIED) { @@ -211,26 +255,43 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl) return -ETIMEDOUT; } -/* Non-data command */ -static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) +static int +pmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid) { struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); unsigned long flags; u32 cmd; int rc; + u32 offset = pmic_arb->ver_ops->offset(pmic_arb, sid, 0); + + cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20); + + raw_spin_lock_irqsave(&pmic_arb->lock, flags); + pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd); + rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, 0); + raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); + + return rc; +} + +static int +pmic_arb_non_data_cmd_v2(struct spmi_controller *ctrl, u8 opc, u8 sid) +{ + return -EOPNOTSUPP; +} + +/* Non-data command */ +static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) +{ + struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); + + dev_dbg(&ctrl->dev, "cmd op:0x%x sid:%d\n", opc, sid); /* Check for valid non-data command */ if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP) return -EINVAL; - cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20); - - raw_spin_lock_irqsave(&pmic_arb->lock, flags); - pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); - rc = pmic_arb_wait_for_done(ctrl); - raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); - - return rc; + return pmic_arb->ver_ops->non_data_cmd(ctrl, opc, sid); } static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, @@ -241,10 +302,11 @@ static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, u8 bc = len - 1; u32 cmd; int rc; + u32 offset = pmic_arb->ver_ops->offset(pmic_arb, sid, addr); if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { dev_err(&ctrl->dev, - "pmic-arb supports 1..%d bytes per trans, but %d requested", + "pmic-arb supports 1..%d bytes per trans, but:%zu requested", PMIC_ARB_MAX_TRANS_BYTES, len); return -EINVAL; } @@ -259,20 +321,20 @@ static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, else return -EINVAL; - cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); + cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc); raw_spin_lock_irqsave(&pmic_arb->lock, flags); - pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); - rc = pmic_arb_wait_for_done(ctrl); + pmic_arb_set_rd_cmd(pmic_arb, offset + PMIC_ARB_CMD, cmd); + rc = pmic_arb_wait_for_done(ctrl, pmic_arb->rd_base, sid, addr); if (rc) goto done; - pa_read_data(pmic_arb, buf, PMIC_ARB_RDATA0(pmic_arb->channel), + pa_read_data(pmic_arb, buf, offset + PMIC_ARB_RDATA0, min_t(u8, bc, 3)); if (bc > 3) pa_read_data(pmic_arb, buf + 4, - PMIC_ARB_RDATA1(pmic_arb->channel), bc - 4); + offset + PMIC_ARB_RDATA1, bc - 4); done: raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); @@ -287,10 +349,11 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, u8 bc = len - 1; u32 cmd; int rc; + u32 offset = pmic_arb->ver_ops->offset(pmic_arb, sid, addr); if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { dev_err(&ctrl->dev, - "pmic-arb supports 1..%d bytes per trans, but:%d requested", + "pmic-arb supports 1..%d bytes per trans, but:%zu requested", PMIC_ARB_MAX_TRANS_BYTES, len); return -EINVAL; } @@ -307,19 +370,19 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, else return -EINVAL; - cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); + cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc); /* Write data to FIFOs */ raw_spin_lock_irqsave(&pmic_arb->lock, flags); - pa_write_data(pmic_arb, buf, PMIC_ARB_WDATA0(pmic_arb->channel) - , min_t(u8, bc, 3)); + pa_write_data(pmic_arb, buf, offset + PMIC_ARB_WDATA0, + min_t(u8, bc, 3)); if (bc > 3) pa_write_data(pmic_arb, buf + 4, - PMIC_ARB_WDATA1(pmic_arb->channel), bc - 4); + offset + PMIC_ARB_WDATA1, bc - 4); /* Start the transaction */ - pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); - rc = pmic_arb_wait_for_done(ctrl); + pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd); + rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, addr); raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); return rc; @@ -376,7 +439,7 @@ static void periph_interrupt(struct spmi_pmic_arb_dev *pa, u8 apid) u32 status; int id; - status = readl_relaxed(pa->intr + SPMI_PIC_IRQ_STATUS(apid)); + status = readl_relaxed(pa->intr + pa->ver_ops->irq_status(apid)); while (status) { id = ffs(status) - 1; status &= ~(1 << id); @@ -402,7 +465,7 @@ static void pmic_arb_chained_irq(unsigned int irq, struct irq_desc *desc) for (i = first; i <= last; ++i) { status = readl_relaxed(intr + - SPMI_PIC_OWNER_ACC_STATUS(pa->ee, i)); + pa->ver_ops->owner_acc_status(pa->ee, i)); while (status) { id = ffs(status) - 1; status &= ~(1 << id); @@ -422,7 +485,7 @@ static void qpnpint_irq_ack(struct irq_data *d) u8 data; raw_spin_lock_irqsave(&pa->lock, flags); - writel_relaxed(1 << irq, pa->intr + SPMI_PIC_IRQ_CLEAR(apid)); + writel_relaxed(1 << irq, pa->intr + pa->ver_ops->irq_clear(apid)); raw_spin_unlock_irqrestore(&pa->lock, flags); data = 1 << irq; @@ -439,10 +502,11 @@ static void qpnpint_irq_mask(struct irq_data *d) u8 data; raw_spin_lock_irqsave(&pa->lock, flags); - status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid)); + status = readl_relaxed(pa->intr + pa->ver_ops->acc_enable(apid)); if (status & SPMI_PIC_ACC_ENABLE_BIT) { status = status & ~SPMI_PIC_ACC_ENABLE_BIT; - writel_relaxed(status, pa->intr + SPMI_PIC_ACC_ENABLE(apid)); + writel_relaxed(status, pa->intr + + pa->ver_ops->acc_enable(apid)); } raw_spin_unlock_irqrestore(&pa->lock, flags); @@ -460,10 +524,10 @@ static void qpnpint_irq_unmask(struct irq_data *d) u8 data; raw_spin_lock_irqsave(&pa->lock, flags); - status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid)); + status = readl_relaxed(pa->intr + pa->ver_ops->acc_enable(apid)); if (!(status & SPMI_PIC_ACC_ENABLE_BIT)) { writel_relaxed(status | SPMI_PIC_ACC_ENABLE_BIT, - pa->intr + SPMI_PIC_ACC_ENABLE(apid)); + pa->intr + pa->ver_ops->acc_enable(apid)); } raw_spin_unlock_irqrestore(&pa->lock, flags); @@ -624,6 +688,91 @@ static int qpnpint_irq_domain_map(struct irq_domain *d, return 0; } +/* v1 offset per ee */ +static u32 pmic_arb_offset_v1(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr) +{ + return 0x800 + 0x80 * pa->channel; +} + +/* v2 offset per ppid (chan) and per ee */ +static u32 pmic_arb_offset_v2(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr) +{ + u16 ppid = (sid << 8) | (addr >> 8); + u8 chan = pa->ppid_to_chan[ppid]; + + return 0x1000 * pa->ee + 0x8000 * chan; +} + +static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc) +{ + return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); +} + +static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc) +{ + return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7); +} + +static u32 pmic_arb_owner_acc_status_v1(u8 m, u8 n) +{ + return 0x20 * m + 0x4 * n; +} + +static u32 pmic_arb_owner_acc_status_v2(u8 m, u8 n) +{ + return 0x100000 + 0x1000 * m + 0x4 * n; +} + +static u32 pmic_arb_acc_enable_v1(u8 n) +{ + return 0x200 + 0x4 * n; +} + +static u32 pmic_arb_acc_enable_v2(u8 n) +{ + return 0x1000 * n; +} + +static u32 pmic_arb_irq_status_v1(u8 n) +{ + return 0x600 + 0x4 * n; +} + +static u32 pmic_arb_irq_status_v2(u8 n) +{ + return 0x4 + 0x1000 * n; +} + +static u32 pmic_arb_irq_clear_v1(u8 n) +{ + return 0xA00 + 0x4 * n; +} + +static u32 pmic_arb_irq_clear_v2(u8 n) +{ + return 0x8 + 0x1000 * n; +} + +static const struct pmic_arb_ver_ops pmic_arb_v1 = { + .non_data_cmd = pmic_arb_non_data_cmd_v1, + .offset = pmic_arb_offset_v1, + .fmt_cmd = pmic_arb_fmt_cmd_v1, + .owner_acc_status = pmic_arb_owner_acc_status_v1, + .acc_enable = pmic_arb_acc_enable_v1, + .irq_status = pmic_arb_irq_status_v1, + .irq_clear = pmic_arb_irq_clear_v1, +}; + +static const struct pmic_arb_ver_ops pmic_arb_v2 = { + .non_data_cmd = pmic_arb_non_data_cmd_v2, + .offset = pmic_arb_offset_v2, + .fmt_cmd = pmic_arb_fmt_cmd_v2, + .owner_acc_status = pmic_arb_owner_acc_status_v2, + .acc_enable = pmic_arb_acc_enable_v2, + .irq_status = pmic_arb_irq_status_v2, + .irq_clear = pmic_arb_irq_clear_v2, +}; + static const struct irq_domain_ops pmic_arb_irq_domain_ops = { .map = qpnpint_irq_domain_map, .xlate = qpnpint_irq_domain_dt_translate, @@ -634,8 +783,10 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) struct spmi_pmic_arb_dev *pa; struct spmi_controller *ctrl; struct resource *res; - u32 channel, ee; + void __iomem *core; + u32 channel, ee, hw_ver; int err, i; + bool is_v1; ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); if (!ctrl) @@ -645,12 +796,65 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) pa->spmic = ctrl; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); - pa->base = devm_ioremap_resource(&ctrl->dev, res); - if (IS_ERR(pa->base)) { - err = PTR_ERR(pa->base); + core = devm_ioremap_resource(&ctrl->dev, res); + if (IS_ERR(core)) { + err = PTR_ERR(core); goto err_put_ctrl; } + hw_ver = readl_relaxed(core + PMIC_ARB_VERSION); + is_v1 = (hw_ver < PMIC_ARB_VERSION_V2_MIN); + + dev_info(&ctrl->dev, "PMIC Arb Version-%d (0x%x)\n", (is_v1 ? 1 : 2), + hw_ver); + + if (is_v1) { + pa->ver_ops = &pmic_arb_v1; + pa->wr_base = core; + pa->rd_base = core; + } else { + u8 chan; + u16 ppid; + u32 regval; + + pa->ver_ops = &pmic_arb_v2; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "obsrvr"); + pa->rd_base = devm_ioremap_resource(&ctrl->dev, res); + if (IS_ERR(pa->rd_base)) { + err = PTR_ERR(pa->rd_base); + goto err_put_ctrl; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "chnls"); + pa->wr_base = devm_ioremap_resource(&ctrl->dev, res); + if (IS_ERR(pa->wr_base)) { + err = PTR_ERR(pa->wr_base); + goto err_put_ctrl; + } + + pa->ppid_to_chan = devm_kzalloc(&ctrl->dev, + PPID_TO_CHAN_TABLE_SZ, GFP_KERNEL); + if (!pa->ppid_to_chan) { + err = -ENOMEM; + goto err_put_ctrl; + } + /* + * PMIC_ARB_REG_CHNL is a table in HW mapping channel to ppid. + * ppid_to_chan is an in-memory invert of that table. + */ + for (chan = 0; chan < PMIC_ARB_MAX_CHNL; ++chan) { + regval = readl_relaxed(core + PMIC_ARB_REG_CHNL(chan)); + if (!regval) + continue; + + ppid = (regval >> 8) & 0xFFF; + pa->ppid_to_chan[ppid] = chan; + } + } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr"); pa->intr = devm_ioremap_resource(&ctrl->dev, res); if (IS_ERR(pa->intr)) { @@ -731,9 +935,6 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) if (err) goto err_domain_remove; - dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n", - pmic_arb_base_read(pa, PMIC_ARB_VERSION)); - return 0; err_domain_remove: From c0e6841653e9e96fea9e0f973a785bc66f45b532 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Mon, 9 Mar 2015 14:29:03 +0000 Subject: [PATCH 100/133] dt-bindings: memory-controllers: Add binding for jz4780-nemc Add device tree bindings for the NAND/External Memory Controller (NEMC) on Ingenic JZ4780 Signed-off-by: Alex Smith Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Greg Kroah-Hartman --- .../ingenic,jz4780-nemc.txt | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt diff --git a/Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt b/Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt new file mode 100644 index 000000000000..f936b5589b19 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt @@ -0,0 +1,75 @@ +* Ingenic JZ4780 NAND/external memory controller (NEMC) + +This file documents the device tree bindings for the NEMC external memory +controller in Ingenic JZ4780 + +Required properties: +- compatible: Should be set to one of: + "ingenic,jz4780-nemc" (JZ4780) +- reg: Should specify the NEMC controller registers location and length. +- clocks: Clock for the NEMC controller. +- #address-cells: Must be set to 2. +- #size-cells: Must be set to 1. +- ranges: A set of ranges for each bank describing the physical memory layout. + Each should specify the following 4 integer values: + + 0 + +Each child of the NEMC node describes a device connected to the NEMC. + +Required child node properties: +- reg: Should contain at least one register specifier, given in the following + format: + + + + Multiple registers can be specified across multiple banks. This is needed, + for example, for packaged NAND devices with multiple dies. Such devices + should be grouped into a single node. + +Optional child node properties: +- ingenic,nemc-bus-width: Specifies the bus width in bits. Defaults to 8 bits. +- ingenic,nemc-tAS: Address setup time in nanoseconds. +- ingenic,nemc-tAH: Address hold time in nanoseconds. +- ingenic,nemc-tBP: Burst pitch time in nanoseconds. +- ingenic,nemc-tAW: Access wait time in nanoseconds. +- ingenic,nemc-tSTRV: Static memory recovery time in nanoseconds. + +If a child node references multiple banks in its "reg" property, the same value +for all optional parameters will be configured for all banks. If any optional +parameters are omitted, they will be left unchanged from whatever they are +configured to when the NEMC device is probed (which may be the reset value as +given in the hardware reference manual, or a value configured by the boot +loader). + +Example (NEMC node with a NAND child device attached at CS1): + +nemc: nemc@13410000 { + compatible = "ingenic,jz4780-nemc"; + reg = <0x13410000 0x10000>; + + #address-cells = <2>; + #size-cells = <1>; + + ranges = <1 0 0x1b000000 0x1000000 + 2 0 0x1a000000 0x1000000 + 3 0 0x19000000 0x1000000 + 4 0 0x18000000 0x1000000 + 5 0 0x17000000 0x1000000 + 6 0 0x16000000 0x1000000>; + + clocks = <&cgu JZ4780_CLK_NEMC>; + + nand: nand@1 { + compatible = "ingenic,jz4780-nand"; + reg = <1 0 0x1000000>; + + ingenic,nemc-tAS = <10>; + ingenic,nemc-tAH = <5>; + ingenic,nemc-tBP = <10>; + ingenic,nemc-tAW = <15>; + ingenic,nemc-tSTRV = <100>; + + ... + }; +}; From 911a88829725572820dad9a168e735c606a2fdcb Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Mon, 9 Mar 2015 14:29:04 +0000 Subject: [PATCH 101/133] memory: jz4780-nemc: driver for the NEMC on JZ4780 SoCs Add a driver for the NAND/External Memory Controller (NEMC) on JZ4780 and later SoCs. The primary function of this driver is to configure parameters, such as timings, for external memory devices using data supplied in the device tree. Devices connected to the NEMC are represented in the DT as children of the NEMC node, the driver uses optional properties specified in these child nodes to configure the parameters of each bank. Signed-off-by: Alex Smith Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Greg Kroah-Hartman --- drivers/memory/Kconfig | 9 + drivers/memory/Makefile | 1 + drivers/memory/jz4780-nemc.c | 391 +++++++++++++++++++++++++++++++++++ include/linux/jz4780-nemc.h | 43 ++++ 4 files changed, 444 insertions(+) create mode 100644 drivers/memory/jz4780-nemc.c create mode 100644 include/linux/jz4780-nemc.h diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 191383d8c94d..868036f70f8f 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -83,6 +83,15 @@ config FSL_IFC bool depends on FSL_SOC +config JZ4780_NEMC + bool "Ingenic JZ4780 SoC NEMC driver" + default y + depends on MACH_JZ4780 + help + This driver is for the NAND/External Memory Controller (NEMC) in + the Ingenic JZ4780. This controller is used to handle external + memory devices such as NAND and SRAM. + source "drivers/memory/tegra/Kconfig" endif diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index 6b6548124473..b670441e3cdf 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -13,5 +13,6 @@ obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o +obj-$(CONFIG_JZ4780_NEMC) += jz4780-nemc.o obj-$(CONFIG_TEGRA_MC) += tegra/ diff --git a/drivers/memory/jz4780-nemc.c b/drivers/memory/jz4780-nemc.c new file mode 100644 index 000000000000..919d1925acb9 --- /dev/null +++ b/drivers/memory/jz4780-nemc.c @@ -0,0 +1,391 @@ +/* + * JZ4780 NAND/external memory controller (NEMC) + * + * Copyright (c) 2015 Imagination Technologies + * Author: Alex Smith + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NEMC_SMCRn(n) (0x14 + (((n) - 1) * 4)) +#define NEMC_NFCSR 0x50 + +#define NEMC_SMCR_SMT BIT(0) +#define NEMC_SMCR_BW_SHIFT 6 +#define NEMC_SMCR_BW_MASK (0x3 << NEMC_SMCR_BW_SHIFT) +#define NEMC_SMCR_BW_8 (0 << 6) +#define NEMC_SMCR_TAS_SHIFT 8 +#define NEMC_SMCR_TAS_MASK (0xf << NEMC_SMCR_TAS_SHIFT) +#define NEMC_SMCR_TAH_SHIFT 12 +#define NEMC_SMCR_TAH_MASK (0xf << NEMC_SMCR_TAH_SHIFT) +#define NEMC_SMCR_TBP_SHIFT 16 +#define NEMC_SMCR_TBP_MASK (0xf << NEMC_SMCR_TBP_SHIFT) +#define NEMC_SMCR_TAW_SHIFT 20 +#define NEMC_SMCR_TAW_MASK (0xf << NEMC_SMCR_TAW_SHIFT) +#define NEMC_SMCR_TSTRV_SHIFT 24 +#define NEMC_SMCR_TSTRV_MASK (0x3f << NEMC_SMCR_TSTRV_SHIFT) + +#define NEMC_NFCSR_NFEn(n) BIT(((n) - 1) << 1) +#define NEMC_NFCSR_NFCEn(n) BIT((((n) - 1) << 1) + 1) +#define NEMC_NFCSR_TNFEn(n) BIT(16 + (n) - 1) + +struct jz4780_nemc { + spinlock_t lock; + struct device *dev; + void __iomem *base; + struct clk *clk; + uint32_t clk_period; + unsigned long banks_present; +}; + +/** + * jz4780_nemc_num_banks() - count the number of banks referenced by a device + * @dev: device to count banks for, must be a child of the NEMC. + * + * Return: The number of unique NEMC banks referred to by the specified NEMC + * child device. Unique here means that a device that references the same bank + * multiple times in the its "reg" property will only count once. + */ +unsigned int jz4780_nemc_num_banks(struct device *dev) +{ + const __be32 *prop; + unsigned int bank, count = 0; + unsigned long referenced = 0; + int i = 0; + + while ((prop = of_get_address(dev->of_node, i++, NULL, NULL))) { + bank = of_read_number(prop, 1); + if (!(referenced & BIT(bank))) { + referenced |= BIT(bank); + count++; + } + } + + return count; +} +EXPORT_SYMBOL(jz4780_nemc_num_banks); + +/** + * jz4780_nemc_set_type() - set the type of device connected to a bank + * @dev: child device of the NEMC. + * @bank: bank number to configure. + * @type: type of device connected to the bank. + */ +void jz4780_nemc_set_type(struct device *dev, unsigned int bank, + enum jz4780_nemc_bank_type type) +{ + struct jz4780_nemc *nemc = dev_get_drvdata(dev->parent); + uint32_t nfcsr; + + nfcsr = readl(nemc->base + NEMC_NFCSR); + + /* TODO: Support toggle NAND devices. */ + switch (type) { + case JZ4780_NEMC_BANK_SRAM: + nfcsr &= ~(NEMC_NFCSR_TNFEn(bank) | NEMC_NFCSR_NFEn(bank)); + break; + case JZ4780_NEMC_BANK_NAND: + nfcsr &= ~NEMC_NFCSR_TNFEn(bank); + nfcsr |= NEMC_NFCSR_NFEn(bank); + break; + } + + writel(nfcsr, nemc->base + NEMC_NFCSR); +} +EXPORT_SYMBOL(jz4780_nemc_set_type); + +/** + * jz4780_nemc_assert() - (de-)assert a NAND device's chip enable pin + * @dev: child device of the NEMC. + * @bank: bank number of device. + * @assert: whether the chip enable pin should be asserted. + * + * (De-)asserts the chip enable pin for the NAND device connected to the + * specified bank. + */ +void jz4780_nemc_assert(struct device *dev, unsigned int bank, bool assert) +{ + struct jz4780_nemc *nemc = dev_get_drvdata(dev->parent); + uint32_t nfcsr; + + nfcsr = readl(nemc->base + NEMC_NFCSR); + + if (assert) + nfcsr |= NEMC_NFCSR_NFCEn(bank); + else + nfcsr &= ~NEMC_NFCSR_NFCEn(bank); + + writel(nfcsr, nemc->base + NEMC_NFCSR); +} +EXPORT_SYMBOL(jz4780_nemc_assert); + +static uint32_t jz4780_nemc_clk_period(struct jz4780_nemc *nemc) +{ + unsigned long rate; + + rate = clk_get_rate(nemc->clk); + if (!rate) + return 0; + + /* Return in picoseconds. */ + return div64_ul(1000000000000ull, rate); +} + +static uint32_t jz4780_nemc_ns_to_cycles(struct jz4780_nemc *nemc, uint32_t ns) +{ + return ((ns * 1000) + nemc->clk_period - 1) / nemc->clk_period; +} + +static bool jz4780_nemc_configure_bank(struct jz4780_nemc *nemc, + unsigned int bank, + struct device_node *node) +{ + uint32_t smcr, val, cycles; + + /* + * Conversion of tBP and tAW cycle counts to values supported by the + * hardware (round up to the next supported value). + */ + static const uint32_t convert_tBP_tAW[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + + /* 11 - 12 -> 12 cycles */ + 11, 11, + + /* 13 - 15 -> 15 cycles */ + 12, 12, 12, + + /* 16 - 20 -> 20 cycles */ + 13, 13, 13, 13, 13, + + /* 21 - 25 -> 25 cycles */ + 14, 14, 14, 14, 14, + + /* 26 - 31 -> 31 cycles */ + 15, 15, 15, 15, 15, 15 + }; + + smcr = readl(nemc->base + NEMC_SMCRn(bank)); + smcr &= ~NEMC_SMCR_SMT; + + if (!of_property_read_u32(node, "ingenic,nemc-bus-width", &val)) { + smcr &= ~NEMC_SMCR_BW_MASK; + switch (val) { + case 8: + smcr |= NEMC_SMCR_BW_8; + break; + default: + /* + * Earlier SoCs support a 16 bit bus width (the 4780 + * does not), until those are properly supported, error. + */ + dev_err(nemc->dev, "unsupported bus width: %u\n", val); + return false; + } + } + + if (of_property_read_u32(node, "ingenic,nemc-tAS", &val) == 0) { + smcr &= ~NEMC_SMCR_TAS_MASK; + cycles = jz4780_nemc_ns_to_cycles(nemc, val); + if (cycles > 15) { + dev_err(nemc->dev, "tAS %u is too high (%u cycles)\n", + val, cycles); + return false; + } + + smcr |= cycles << NEMC_SMCR_TAS_SHIFT; + } + + if (of_property_read_u32(node, "ingenic,nemc-tAH", &val) == 0) { + smcr &= ~NEMC_SMCR_TAH_MASK; + cycles = jz4780_nemc_ns_to_cycles(nemc, val); + if (cycles > 15) { + dev_err(nemc->dev, "tAH %u is too high (%u cycles)\n", + val, cycles); + return false; + } + + smcr |= cycles << NEMC_SMCR_TAH_SHIFT; + } + + if (of_property_read_u32(node, "ingenic,nemc-tBP", &val) == 0) { + smcr &= ~NEMC_SMCR_TBP_MASK; + cycles = jz4780_nemc_ns_to_cycles(nemc, val); + if (cycles > 31) { + dev_err(nemc->dev, "tBP %u is too high (%u cycles)\n", + val, cycles); + return false; + } + + smcr |= convert_tBP_tAW[cycles] << NEMC_SMCR_TBP_SHIFT; + } + + if (of_property_read_u32(node, "ingenic,nemc-tAW", &val) == 0) { + smcr &= ~NEMC_SMCR_TAW_MASK; + cycles = jz4780_nemc_ns_to_cycles(nemc, val); + if (cycles > 31) { + dev_err(nemc->dev, "tAW %u is too high (%u cycles)\n", + val, cycles); + return false; + } + + smcr |= convert_tBP_tAW[cycles] << NEMC_SMCR_TAW_SHIFT; + } + + if (of_property_read_u32(node, "ingenic,nemc-tSTRV", &val) == 0) { + smcr &= ~NEMC_SMCR_TSTRV_MASK; + cycles = jz4780_nemc_ns_to_cycles(nemc, val); + if (cycles > 63) { + dev_err(nemc->dev, "tSTRV %u is too high (%u cycles)\n", + val, cycles); + return false; + } + + smcr |= cycles << NEMC_SMCR_TSTRV_SHIFT; + } + + writel(smcr, nemc->base + NEMC_SMCRn(bank)); + return true; +} + +static int jz4780_nemc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct jz4780_nemc *nemc; + struct resource *res; + struct device_node *child; + const __be32 *prop; + unsigned int bank; + unsigned long referenced; + int i, ret; + + nemc = devm_kzalloc(dev, sizeof(*nemc), GFP_KERNEL); + if (!nemc) + return -ENOMEM; + + spin_lock_init(&nemc->lock); + nemc->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nemc->base = devm_ioremap_resource(dev, res); + if (IS_ERR(nemc->base)) { + dev_err(dev, "failed to get I/O memory\n"); + return PTR_ERR(nemc->base); + } + + writel(0, nemc->base + NEMC_NFCSR); + + nemc->clk = devm_clk_get(dev, NULL); + if (IS_ERR(nemc->clk)) { + dev_err(dev, "failed to get clock\n"); + return PTR_ERR(nemc->clk); + } + + ret = clk_prepare_enable(nemc->clk); + if (ret) { + dev_err(dev, "failed to enable clock: %d\n", ret); + return ret; + } + + nemc->clk_period = jz4780_nemc_clk_period(nemc); + if (!nemc->clk_period) { + dev_err(dev, "failed to calculate clock period\n"); + clk_disable_unprepare(nemc->clk); + return -EINVAL; + } + + /* + * Iterate over child devices, check that they do not conflict with + * each other, and register child devices for them. If a child device + * has invalid properties, it is ignored and no platform device is + * registered for it. + */ + for_each_child_of_node(nemc->dev->of_node, child) { + referenced = 0; + i = 0; + while ((prop = of_get_address(child, i++, NULL, NULL))) { + bank = of_read_number(prop, 1); + if (bank < 1 || bank >= JZ4780_NEMC_NUM_BANKS) { + dev_err(nemc->dev, + "%s requests invalid bank %u\n", + child->full_name, bank); + + /* Will continue the outer loop below. */ + referenced = 0; + break; + } + + referenced |= BIT(bank); + } + + if (!referenced) { + dev_err(nemc->dev, "%s has no addresses\n", + child->full_name); + continue; + } else if (nemc->banks_present & referenced) { + dev_err(nemc->dev, "%s conflicts with another node\n", + child->full_name); + continue; + } + + /* Configure bank parameters. */ + for_each_set_bit(bank, &referenced, JZ4780_NEMC_NUM_BANKS) { + if (!jz4780_nemc_configure_bank(nemc, bank, child)) { + referenced = 0; + break; + } + } + + if (referenced) { + if (of_platform_device_create(child, NULL, nemc->dev)) + nemc->banks_present |= referenced; + } + } + + platform_set_drvdata(pdev, nemc); + dev_info(dev, "JZ4780 NEMC initialised\n"); + return 0; +} + +static int jz4780_nemc_remove(struct platform_device *pdev) +{ + struct jz4780_nemc *nemc = platform_get_drvdata(pdev); + + clk_disable_unprepare(nemc->clk); + return 0; +} + +static const struct of_device_id jz4780_nemc_dt_match[] = { + { .compatible = "ingenic,jz4780-nemc" }, + {}, +}; + +static struct platform_driver jz4780_nemc_driver = { + .probe = jz4780_nemc_probe, + .remove = jz4780_nemc_remove, + .driver = { + .name = "jz4780-nemc", + .of_match_table = of_match_ptr(jz4780_nemc_dt_match), + }, +}; + +static int __init jz4780_nemc_init(void) +{ + return platform_driver_register(&jz4780_nemc_driver); +} +subsys_initcall(jz4780_nemc_init); diff --git a/include/linux/jz4780-nemc.h b/include/linux/jz4780-nemc.h new file mode 100644 index 000000000000..e7f1cc7a2284 --- /dev/null +++ b/include/linux/jz4780-nemc.h @@ -0,0 +1,43 @@ +/* + * JZ4780 NAND/external memory controller (NEMC) + * + * Copyright (c) 2015 Imagination Technologies + * Author: Alex Smith + * + * 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 + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __LINUX_JZ4780_NEMC_H__ +#define __LINUX_JZ4780_NEMC_H__ + +#include + +struct device; + +/* + * Number of NEMC banks. Note that there are actually 6, but they are numbered + * from 1. + */ +#define JZ4780_NEMC_NUM_BANKS 7 + +/** + * enum jz4780_nemc_bank_type - device types which can be connected to a bank + * @JZ4780_NEMC_BANK_SRAM: SRAM + * @JZ4780_NEMC_BANK_NAND: NAND + */ +enum jz4780_nemc_bank_type { + JZ4780_NEMC_BANK_SRAM, + JZ4780_NEMC_BANK_NAND, +}; + +extern unsigned int jz4780_nemc_num_banks(struct device *dev); + +extern void jz4780_nemc_set_type(struct device *dev, unsigned int bank, + enum jz4780_nemc_bank_type type); +extern void jz4780_nemc_assert(struct device *dev, unsigned int bank, + bool assert); + +#endif /* __LINUX_JZ4780_NEMC_H__ */ From 16c9c8e1ae228e89b66cbc03ec6c753ee44d39bc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 3 Apr 2015 16:04:21 +0200 Subject: [PATCH 102/133] Revert "uio: constify of_device_id array" This reverts commit 4d8beff2ae07fad85d723b4cdf704b05f0ed4794. It causes build warnings, and it's incorrect as we do write to this structure. Reported-by: Stephen Rothwell Cc: Fabian Frederick Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_pdrv_genirq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 59c172a0d13a..f598ecddc8a7 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -252,7 +252,7 @@ static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = { }; #ifdef CONFIG_OF -static const struct of_device_id uio_of_genirq_match[] = { +static struct of_device_id uio_of_genirq_match[] = { { /* This is filled with module_parm */ }, { /* Sentinel */ }, }; From 0b509d8d336eef6d622d66b3ae2a1fc3a072bf92 Mon Sep 17 00:00:00 2001 From: Tom Van Braeckel Date: Tue, 31 Mar 2015 16:39:21 +0200 Subject: [PATCH 103/133] misc: pass miscdevice through file's private_data Make the miscdevice accessible through the file's private_data. Previously, this was done only when an open() file operation had been registered. If no custom open() file operation was defined, private_data was set to NULL. This subtle quirk was confusing, to the point where kernel code registered *empty* file open operations to have private_data point to the misc device structure and avoid duplicating that logic. And it could easily lead to bugs, where the addition or removal of a custom open() file operation surprisingly changes the initial value of a file's private_data structure. To resolve this, we now place the miscdevice in the file's private_data member unconditionally when open() is called. Signed-off-by: Tom Van Braeckel Signed-off-by: Greg Kroah-Hartman --- drivers/char/misc.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 5bb3a2109ab7..9fd5a91e0d81 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -140,12 +140,17 @@ static int misc_open(struct inode * inode, struct file * file) goto fail; } + /* + * Place the miscdevice in the file's + * private_data so it can be used by the + * file operations, including f_op->open below + */ + file->private_data = c; + err = 0; replace_fops(file, new_fops); - if (file->f_op->open) { - file->private_data = c; + if (file->f_op->open) err = file->f_op->open(inode,file); - } fail: mutex_unlock(&misc_mtx); return err; From f580d730c90c11be0ef4fe88aff3de80845176cb Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Mar 2015 16:46:10 -0700 Subject: [PATCH 104/133] virtio_console: Use bool function return values of true/false not 1/0 Use the normal return values for bool functions Signed-off-by: Joe Perches Reviewed-by: Amit Shah Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 72d7028f779b..50754d203310 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -355,7 +355,7 @@ static inline bool use_multiport(struct ports_device *portdev) * early_init */ if (!portdev->vdev) - return 0; + return false; return __virtio_test_bit(portdev->vdev, VIRTIO_CONSOLE_F_MULTIPORT); } From 149cb911ae242242e5aae698710bf59e804a96e6 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Wed, 1 Apr 2015 10:46:19 +0300 Subject: [PATCH 105/133] spmi: pmic_arb: remove ARM build time dependency Qualcomm PMIC arbiter driver already depends on ARCH_QCOM, which could be either ARM or ARM64. New version of the PMIC arbiter controller is available on 64 bit platforms. Remove ARM dependency to allow driver to be build for 64 bit platforms. Signed-off-by: Ivan T. Ivanov Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig index bf1295e19f89..c8d99563d245 100644 --- a/drivers/spmi/Kconfig +++ b/drivers/spmi/Kconfig @@ -12,7 +12,6 @@ if SPMI config SPMI_MSM_PMIC_ARB tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" - depends on ARM depends on IRQ_DOMAIN depends on ARCH_QCOM || COMPILE_TEST default ARCH_QCOM From be29bc2eaa5360537df422a237c889dc493492f4 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 29 Mar 2015 17:04:27 +0200 Subject: [PATCH 106/133] mic: drop pci_msi_off call on probe pci core now disables msi on probe automatically, drop this from device-specific code. Cc: Bjorn Helgaas Cc: linux-pci@vger.kernel.org Signed-off-by: Michael S. Tsirkin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_intr.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c index d686f2846ac7..b4ca6c884d19 100644 --- a/drivers/misc/mic/host/mic_intr.c +++ b/drivers/misc/mic/host/mic_intr.c @@ -363,8 +363,6 @@ static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev) { int rc; - pci_msi_off(pdev); - /* Enable intx */ pci_intx(pdev, 1); rc = mic_setup_callbacks(mdev); From 127af8828518074ab24e9b5678229513d198d832 Mon Sep 17 00:00:00 2001 From: Eli Billauer Date: Fri, 27 Mar 2015 11:56:06 +0300 Subject: [PATCH 107/133] char: xillybus: Don't return -EFAULT on user-triggered flush The API allows the application to flush a host-to-FPGA stream by calling write() with the data count set to zero. Before this patch, copy_from_user() was called with a non-zero byte count, which possibly made it attempt to read from unmapped user memory. Such attempts caused the driver to return -EFAULT instead of 0, even though the desired operation went through fine. This patch ensures the driver returns 0 on a successful flush. Signed-off-by: Eli Billauer Signed-off-by: Greg Kroah-Hartman --- drivers/char/xillybus/xillybus_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/char/xillybus/xillybus_core.c b/drivers/char/xillybus/xillybus_core.c index b827fa095f1b..77d6c127e691 100644 --- a/drivers/char/xillybus/xillybus_core.c +++ b/drivers/char/xillybus/xillybus_core.c @@ -1237,6 +1237,8 @@ static ssize_t xillybus_write(struct file *filp, const char __user *userbuf, unsigned char *tail; int i; + howmany = 0; + end_offset_plus1 = bufpos >> channel->log2_element_size; From d86fb45b5c52d8f2a3e78e81afd85a2a9d4478d7 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 26 Mar 2015 22:12:49 +0300 Subject: [PATCH 108/133] mcb: request_mem_region() returns NULL on error The code here is checking for IS_ERR() when request_mem_region() only returns NULL on error and never an ERR_PTR. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/mcb/mcb-pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mcb/mcb-pci.c b/drivers/mcb/mcb-pci.c index 0af7361e377f..de36237d7c6b 100644 --- a/drivers/mcb/mcb-pci.c +++ b/drivers/mcb/mcb-pci.c @@ -56,9 +56,9 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) res = request_mem_region(priv->mapbase, CHAM_HEADER_SIZE, KBUILD_MODNAME); - if (IS_ERR(res)) { + if (!res) { dev_err(&pdev->dev, "Failed to request PCI memory\n"); - ret = PTR_ERR(res); + ret = -EBUSY; goto out_disable; } From 8c02a5ba34a1fae6def8cb5a39bb582f09bca49c Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Mon, 30 Mar 2015 14:13:34 -0600 Subject: [PATCH 109/133] coresight: making cpu index lookup arm64 compliant Function "get_logical_index()" is not available on arm64. Instead of adding the function simply using "of_get_cpu_node()" and comparing the return value with cpu handles yields the same result. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/coresight/of_coresight.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/coresight/of_coresight.c b/drivers/coresight/of_coresight.c index c3efa418a86d..f3cc8e97a0f8 100644 --- a/drivers/coresight/of_coresight.c +++ b/drivers/coresight/of_coresight.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -113,7 +114,7 @@ static int of_coresight_alloc_memory(struct device *dev, struct coresight_platform_data *of_get_coresight_platform_data( struct device *dev, struct device_node *node) { - int i = 0, ret = 0; + int i = 0, ret = 0, cpu; struct coresight_platform_data *pdata; struct of_endpoint endpoint, rendpoint; struct device *rdev; @@ -187,17 +188,10 @@ struct coresight_platform_data *of_get_coresight_platform_data( /* Affinity defaults to CPU0 */ pdata->cpu = 0; dn = of_parse_phandle(node, "cpu", 0); - if (dn) { - const u32 *cell; - int len, index; - u64 hwid; - - 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; + for (cpu = 0; dn && cpu < nr_cpu_ids; cpu++) { + if (dn == of_get_cpu_node(cpu, NULL)) { + pdata->cpu = cpu; + break; } } From 72f641fe6818a403aed52fb3a5b8a241ff76c24f Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Mon, 30 Mar 2015 14:13:35 -0600 Subject: [PATCH 110/133] coresight: fixing compilation warnings picked up by 64bit compiler Compiling coresight drivers with a 64-bit compiler highlights a couple of formatting issues, which are fixed by this patch. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/coresight/coresight-etb10.c | 4 ++-- drivers/coresight/coresight-tmc.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/coresight/coresight-etb10.c b/drivers/coresight/coresight-etb10.c index c9acd406f0d0..40049869aecd 100644 --- a/drivers/coresight/coresight-etb10.c +++ b/drivers/coresight/coresight-etb10.c @@ -313,8 +313,8 @@ static ssize_t etb_read(struct file *file, char __user *data, *ppos += len; - dev_dbg(drvdata->dev, "%s: %d bytes copied, %d bytes left\n", - __func__, len, (int) (depth * 4 - *ppos)); + dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n", + __func__, len, (int)(depth * 4 - *ppos)); return len; } diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c index 3ff232f9ddf7..030e097c4fb2 100644 --- a/drivers/coresight/coresight-tmc.c +++ b/drivers/coresight/coresight-tmc.c @@ -533,8 +533,8 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len, *ppos += len; - dev_dbg(drvdata->dev, "%s: %d bytes copied, %d bytes left\n", - __func__, len, (int) (drvdata->size - *ppos)); + dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n", + __func__, len, (int)(drvdata->size - *ppos)); return len; } From 3288731e628e0269c20d86e43b647d0b92f2b3fc Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Mon, 30 Mar 2015 14:13:36 -0600 Subject: [PATCH 111/133] coresight: Adding coresight support for arm64 architecture Most CoreSight blocks are 64-bit ready. As such move configuration entries from "arch/arm/Kconfig.config" to the driver's subdirectory and source the newly created Kconfig from architecture specific Kconfig.debug files. Signed-off-by: Mathieu Poirier Acked-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- arch/arm/Kconfig.debug | 55 +---------------------------------- arch/arm64/Kconfig.debug | 2 ++ drivers/coresight/Kconfig | 61 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 54 deletions(-) create mode 100644 drivers/coresight/Kconfig diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 970de7518341..8d14ad4e1db0 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -1610,59 +1610,6 @@ config DEBUG_SET_MODULE_RONX against certain classes of kernel exploits. If in doubt, say "N". -menuconfig CORESIGHT - bool "CoreSight Tracing Support" - select ARM_AMBA - help - This framework provides a kernel interface for the CoreSight debug - and trace drivers to register themselves with. It's intended to build - a topological view of the CoreSight components based on a DT - specification and configure the right serie of components when a - trace source gets enabled. +source "drivers/coresight/Kconfig" -if CORESIGHT -config CORESIGHT_LINKS_AND_SINKS - bool "CoreSight Link and Sink drivers" - help - This enables support for CoreSight link and sink drivers that are - responsible for transporting and collecting the trace data - respectively. Link and sinks are dynamically aggregated with a trace - entity at run time to form a complete trace path. - -config CORESIGHT_LINK_AND_SINK_TMC - bool "Coresight generic TMC driver" - depends on CORESIGHT_LINKS_AND_SINKS - help - This enables support for the Trace Memory Controller driver. Depending - on its configuration the device can act as a link (embedded trace router - - ETR) or sink (embedded trace FIFO). The driver complies with the - generic implementation of the component without special enhancement or - added features. - -config CORESIGHT_SINK_TPIU - bool "Coresight generic TPIU driver" - depends on CORESIGHT_LINKS_AND_SINKS - help - This enables support for the Trace Port Interface Unit driver, responsible - for bridging the gap between the on-chip coresight components and a trace - port collection engine, typically connected to an external host for use - case capturing more traces than the on-board coresight memory can handle. - -config CORESIGHT_SINK_ETBV10 - bool "Coresight ETBv1.0 driver" - depends on CORESIGHT_LINKS_AND_SINKS - help - This enables support for the Embedded Trace Buffer version 1.0 driver - that complies with the generic implementation of the component without - special enhancement or added features. - -config CORESIGHT_SOURCE_ETM3X - bool "CoreSight Embedded Trace Macrocell 3.x driver" - select CORESIGHT_LINKS_AND_SINKS - help - This driver provides support for processor ETM3.x and PTM1.x modules, - which allows tracing the instructions that a processor is executing - This is primarily useful for instruction level tracing. Depending - the ETM version data tracing may also be available. -endif endmenu diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug index 4a8741073c90..5b2ffd8e6cdb 100644 --- a/arch/arm64/Kconfig.debug +++ b/arch/arm64/Kconfig.debug @@ -89,4 +89,6 @@ config DEBUG_ALIGN_RODATA If in doubt, say N +source "drivers/coresight/Kconfig" + endmenu diff --git a/drivers/coresight/Kconfig b/drivers/coresight/Kconfig new file mode 100644 index 000000000000..fc1f1ae7a49d --- /dev/null +++ b/drivers/coresight/Kconfig @@ -0,0 +1,61 @@ +# +# Coresight configuration +# +menuconfig CORESIGHT + bool "CoreSight Tracing Support" + select ARM_AMBA + help + This framework provides a kernel interface for the CoreSight debug + and trace drivers to register themselves with. It's intended to build + a topological view of the CoreSight components based on a DT + specification and configure the right serie of components when a + trace source gets enabled. + +if CORESIGHT +config CORESIGHT_LINKS_AND_SINKS + bool "CoreSight Link and Sink drivers" + help + This enables support for CoreSight link and sink drivers that are + responsible for transporting and collecting the trace data + respectively. Link and sinks are dynamically aggregated with a trace + entity at run time to form a complete trace path. + +config CORESIGHT_LINK_AND_SINK_TMC + bool "Coresight generic TMC driver" + depends on CORESIGHT_LINKS_AND_SINKS + help + This enables support for the Trace Memory Controller driver. + Depending on its configuration the device can act as a link (embedded + trace router - ETR) or sink (embedded trace FIFO). The driver + complies with the generic implementation of the component without + special enhancement or added features. + +config CORESIGHT_SINK_TPIU + bool "Coresight generic TPIU driver" + depends on CORESIGHT_LINKS_AND_SINKS + help + This enables support for the Trace Port Interface Unit driver, + responsible for bridging the gap between the on-chip coresight + components and a trace for bridging the gap between the on-chip + coresight components and a trace port collection engine, typically + connected to an external host for use case capturing more traces than + the on-board coresight memory can handle. + +config CORESIGHT_SINK_ETBV10 + bool "Coresight ETBv1.0 driver" + depends on CORESIGHT_LINKS_AND_SINKS + help + This enables support for the Embedded Trace Buffer version 1.0 driver + that complies with the generic implementation of the component without + special enhancement or added features. + +config CORESIGHT_SOURCE_ETM3X + bool "CoreSight Embedded Trace Macrocell 3.x driver" + depends on !ARM64 + select CORESIGHT_LINKS_AND_SINKS + help + This driver provides support for processor ETM3.x and PTM1.x modules, + which allows tracing the instructions that a processor is executing + This is primarily useful for instruction level tracing. Depending + the ETM version data tracing may also be available. +endif From b29d5c1f057f583bc15be3d1ec4993e90cc53641 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Mon, 30 Mar 2015 14:13:37 -0600 Subject: [PATCH 112/133] coresight: Correcting documentation typographical error Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- Documentation/trace/coresight.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index 02361552a3ea..77d14d51a670 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -14,7 +14,7 @@ document is concerned with the latter. HW assisted tracing is becoming increasingly useful when dealing with systems that have many SoCs and other components like GPU and DMA engines. ARM has developed a HW assisted tracing solution by means of different components, each -being added to a design at systhesis time to cater to specific tracing needs. +being added to a design at synthesis time to cater to specific tracing needs. Compoments are generally categorised as source, link and sinks and are (usually) discovered using the AMBA bus. From a0a500efab41c761a1d1fd919d2e42a3a3545b2e Mon Sep 17 00:00:00 2001 From: Xia Kaixu Date: Mon, 30 Mar 2015 14:13:38 -0600 Subject: [PATCH 113/133] coresight: adding the LINKSINK block as a sink type >From the TMC TRM, the ETF can be configured as buffer mode, so ETF can be a sink type. Signed-off-by: Xia Kaixu Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/coresight/coresight.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/coresight/coresight.c b/drivers/coresight/coresight.c index c5def9382357..894531d315b8 100644 --- a/drivers/coresight/coresight.c +++ b/drivers/coresight/coresight.c @@ -305,7 +305,9 @@ static int coresight_build_paths(struct coresight_device *csdev, list_add(&csdev->path_link, path); - if (csdev->type == CORESIGHT_DEV_TYPE_SINK && csdev->activated) { + if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || + csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && + csdev->activated) { if (enable) ret = coresight_enable_path(path); else From 223437c72ae008094e43199dea93c3a144c4e153 Mon Sep 17 00:00:00 2001 From: Kaixu Xia Date: Mon, 30 Mar 2015 14:13:39 -0600 Subject: [PATCH 114/133] coresight: remove the unnecessary configuration coresight-default-sink The coresight-default-sink configuration option has been removed from the framework. As such remove it from DT and bindings. Signed-off-by: Kaixu Xia Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/arm/coresight.txt | 1 - arch/arm/boot/dts/hip04.dtsi | 1 - arch/arm/boot/dts/omap3-beagle-xm.dts | 1 - arch/arm/boot/dts/omap3-beagle.dts | 1 - arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 1 - 5 files changed, 5 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index a3089359aaa6..88602b75418e 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -61,7 +61,6 @@ Example: compatible = "arm,coresight-etb10", "arm,primecell"; reg = <0 0x20010000 0 0x1000>; - coresight-default-sink; clocks = <&oscclk6a>; clock-names = "apb_pclk"; port { diff --git a/arch/arm/boot/dts/hip04.dtsi b/arch/arm/boot/dts/hip04.dtsi index 238814596a87..44044f275115 100644 --- a/arch/arm/boot/dts/hip04.dtsi +++ b/arch/arm/boot/dts/hip04.dtsi @@ -275,7 +275,6 @@ compatible = "arm,coresight-etb10", "arm,primecell"; reg = <0 0xe3c42000 0 0x1000>; - coresight-default-sink; clocks = <&clk_375m>; clock-names = "apb_pclk"; port { diff --git a/arch/arm/boot/dts/omap3-beagle-xm.dts b/arch/arm/boot/dts/omap3-beagle-xm.dts index 25f7b0a22114..8cdca51b6984 100644 --- a/arch/arm/boot/dts/omap3-beagle-xm.dts +++ b/arch/arm/boot/dts/omap3-beagle-xm.dts @@ -150,7 +150,6 @@ compatible = "arm,coresight-etb10", "arm,primecell"; reg = <0x5401b000 0x1000>; - coresight-default-sink; clocks = <&emu_src_ck>; clock-names = "apb_pclk"; port { diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts index c792391ef090..6d4c46be8c39 100644 --- a/arch/arm/boot/dts/omap3-beagle.dts +++ b/arch/arm/boot/dts/omap3-beagle.dts @@ -145,7 +145,6 @@ compatible = "arm,coresight-etb10", "arm,primecell"; reg = <0x5401b000 0x1000>; - coresight-default-sink; clocks = <&emu_src_ck>; clock-names = "apb_pclk"; port { diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts index 33920df03640..7a2aeacd62c0 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts @@ -362,7 +362,6 @@ compatible = "arm,coresight-etb10", "arm,primecell"; reg = <0 0x20010000 0 0x1000>; - coresight-default-sink; clocks = <&oscclk6a>; clock-names = "apb_pclk"; port { From a2d6e1849329b7735f2872af4221727c7b9502dd Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Mon, 30 Mar 2015 14:13:40 -0600 Subject: [PATCH 115/133] coresight-tmc: Adding a status interface to sysfs Knowing the state of various control register is always useful for degging and tuning. As such add an entry in sysfs that expose to userspace the most important registers. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/coresight/coresight-tmc.c | 56 +++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c index 030e097c4fb2..7147f3dd363c 100644 --- a/drivers/coresight/coresight-tmc.c +++ b/drivers/coresight/coresight-tmc.c @@ -565,6 +565,59 @@ static const struct file_operations tmc_fops = { .llseek = no_llseek, }; +static ssize_t status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + unsigned long flags; + u32 tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg; + u32 tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr; + u32 devid; + struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = clk_prepare_enable(drvdata->clk); + if (ret) + goto out; + + spin_lock_irqsave(&drvdata->spinlock, flags); + CS_UNLOCK(drvdata->base); + + tmc_rsz = readl_relaxed(drvdata->base + TMC_RSZ); + tmc_sts = readl_relaxed(drvdata->base + TMC_STS); + tmc_rrp = readl_relaxed(drvdata->base + TMC_RRP); + tmc_rwp = readl_relaxed(drvdata->base + TMC_RWP); + tmc_trg = readl_relaxed(drvdata->base + TMC_TRG); + tmc_ctl = readl_relaxed(drvdata->base + TMC_CTL); + tmc_ffsr = readl_relaxed(drvdata->base + TMC_FFSR); + tmc_ffcr = readl_relaxed(drvdata->base + TMC_FFCR); + tmc_mode = readl_relaxed(drvdata->base + TMC_MODE); + tmc_pscr = readl_relaxed(drvdata->base + TMC_PSCR); + devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); + + CS_LOCK(drvdata->base); + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + clk_disable_unprepare(drvdata->clk); + + return sprintf(buf, + "Depth:\t\t0x%x\n" + "Status:\t\t0x%x\n" + "RAM read ptr:\t0x%x\n" + "RAM wrt ptr:\t0x%x\n" + "Trigger cnt:\t0x%x\n" + "Control:\t0x%x\n" + "Flush status:\t0x%x\n" + "Flush ctrl:\t0x%x\n" + "Mode:\t\t0x%x\n" + "PSRC:\t\t0x%x\n" + "DEVID:\t\t0x%x\n", + tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg, + tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr, devid); +out: + return -EINVAL; +} +static DEVICE_ATTR_RO(status); + static ssize_t trigger_cntr_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -593,18 +646,21 @@ static DEVICE_ATTR_RW(trigger_cntr); static struct attribute *coresight_etb_attrs[] = { &dev_attr_trigger_cntr.attr, + &dev_attr_status.attr, NULL, }; ATTRIBUTE_GROUPS(coresight_etb); static struct attribute *coresight_etr_attrs[] = { &dev_attr_trigger_cntr.attr, + &dev_attr_status.attr, NULL, }; ATTRIBUTE_GROUPS(coresight_etr); static struct attribute *coresight_etf_attrs[] = { &dev_attr_trigger_cntr.attr, + &dev_attr_status.attr, NULL, }; ATTRIBUTE_GROUPS(coresight_etf); From 01081f5ab9916603555f236b11f76bb00e4e01e9 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Mon, 30 Mar 2015 14:13:41 -0600 Subject: [PATCH 116/133] coresight: moving to new "hwtracing" directory Keeping drivers related to HW tracing on ARM, i.e coresight, under "drivers/coresight" doesn't make sense when other architectures start rolling out technologies of the same nature. As such creating a new "drivers/hwtracing" directory where all drivers of the same kind can reside, reducing namespace pollution under "drivers/". Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 +- arch/arm/Kconfig.debug | 2 +- arch/arm64/Kconfig.debug | 2 +- drivers/Makefile | 2 +- drivers/{ => hwtracing}/coresight/Kconfig | 0 drivers/{ => hwtracing}/coresight/Makefile | 0 drivers/{ => hwtracing}/coresight/coresight-etb10.c | 0 drivers/{ => hwtracing}/coresight/coresight-etm-cp14.c | 0 drivers/{ => hwtracing}/coresight/coresight-etm.h | 0 drivers/{ => hwtracing}/coresight/coresight-etm3x.c | 0 drivers/{ => hwtracing}/coresight/coresight-funnel.c | 0 drivers/{ => hwtracing}/coresight/coresight-priv.h | 0 drivers/{ => hwtracing}/coresight/coresight-replicator.c | 0 drivers/{ => hwtracing}/coresight/coresight-tmc.c | 0 drivers/{ => hwtracing}/coresight/coresight-tpiu.c | 0 drivers/{ => hwtracing}/coresight/coresight.c | 0 drivers/{ => hwtracing}/coresight/of_coresight.c | 0 17 files changed, 4 insertions(+), 4 deletions(-) rename drivers/{ => hwtracing}/coresight/Kconfig (100%) rename drivers/{ => hwtracing}/coresight/Makefile (100%) rename drivers/{ => hwtracing}/coresight/coresight-etb10.c (100%) rename drivers/{ => hwtracing}/coresight/coresight-etm-cp14.c (100%) rename drivers/{ => hwtracing}/coresight/coresight-etm.h (100%) rename drivers/{ => hwtracing}/coresight/coresight-etm3x.c (100%) rename drivers/{ => hwtracing}/coresight/coresight-funnel.c (100%) rename drivers/{ => hwtracing}/coresight/coresight-priv.h (100%) rename drivers/{ => hwtracing}/coresight/coresight-replicator.c (100%) rename drivers/{ => hwtracing}/coresight/coresight-tmc.c (100%) rename drivers/{ => hwtracing}/coresight/coresight-tpiu.c (100%) rename drivers/{ => hwtracing}/coresight/coresight.c (100%) rename drivers/{ => hwtracing}/coresight/of_coresight.c (100%) diff --git a/MAINTAINERS b/MAINTAINERS index d7490020c0f3..e9e12d75546a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -953,7 +953,7 @@ ARM/CORESIGHT FRAMEWORK AND DRIVERS M: Mathieu Poirier L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -F: drivers/coresight/* +F: drivers/hwtracing/coresight/* F: Documentation/trace/coresight.txt F: Documentation/devicetree/bindings/arm/coresight.txt F: Documentation/ABI/testing/sysfs-bus-coresight-devices-* diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 8d14ad4e1db0..8b0183a9a300 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -1610,6 +1610,6 @@ config DEBUG_SET_MODULE_RONX against certain classes of kernel exploits. If in doubt, say "N". -source "drivers/coresight/Kconfig" +source "drivers/hwtracing/coresight/Kconfig" endmenu diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug index 5b2ffd8e6cdb..d6285ef9b5f9 100644 --- a/arch/arm64/Kconfig.debug +++ b/arch/arm64/Kconfig.debug @@ -89,6 +89,6 @@ config DEBUG_ALIGN_RODATA If in doubt, say N -source "drivers/coresight/Kconfig" +source "drivers/hwtracing/coresight/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 527a6da8d539..46d2554be404 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -163,5 +163,5 @@ obj-$(CONFIG_POWERCAP) += powercap/ obj-$(CONFIG_MCB) += mcb/ obj-$(CONFIG_RAS) += ras/ obj-$(CONFIG_THUNDERBOLT) += thunderbolt/ -obj-$(CONFIG_CORESIGHT) += coresight/ +obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/ obj-$(CONFIG_ANDROID) += android/ diff --git a/drivers/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig similarity index 100% rename from drivers/coresight/Kconfig rename to drivers/hwtracing/coresight/Kconfig diff --git a/drivers/coresight/Makefile b/drivers/hwtracing/coresight/Makefile similarity index 100% rename from drivers/coresight/Makefile rename to drivers/hwtracing/coresight/Makefile diff --git a/drivers/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c similarity index 100% rename from drivers/coresight/coresight-etb10.c rename to drivers/hwtracing/coresight/coresight-etb10.c diff --git a/drivers/coresight/coresight-etm-cp14.c b/drivers/hwtracing/coresight/coresight-etm-cp14.c similarity index 100% rename from drivers/coresight/coresight-etm-cp14.c rename to drivers/hwtracing/coresight/coresight-etm-cp14.c diff --git a/drivers/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h similarity index 100% rename from drivers/coresight/coresight-etm.h rename to drivers/hwtracing/coresight/coresight-etm.h diff --git a/drivers/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c similarity index 100% rename from drivers/coresight/coresight-etm3x.c rename to drivers/hwtracing/coresight/coresight-etm3x.c diff --git a/drivers/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c similarity index 100% rename from drivers/coresight/coresight-funnel.c rename to drivers/hwtracing/coresight/coresight-funnel.c diff --git a/drivers/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h similarity index 100% rename from drivers/coresight/coresight-priv.h rename to drivers/hwtracing/coresight/coresight-priv.h diff --git a/drivers/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c similarity index 100% rename from drivers/coresight/coresight-replicator.c rename to drivers/hwtracing/coresight/coresight-replicator.c diff --git a/drivers/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c similarity index 100% rename from drivers/coresight/coresight-tmc.c rename to drivers/hwtracing/coresight/coresight-tmc.c diff --git a/drivers/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c similarity index 100% rename from drivers/coresight/coresight-tpiu.c rename to drivers/hwtracing/coresight/coresight-tpiu.c diff --git a/drivers/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c similarity index 100% rename from drivers/coresight/coresight.c rename to drivers/hwtracing/coresight/coresight.c diff --git a/drivers/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c similarity index 100% rename from drivers/coresight/of_coresight.c rename to drivers/hwtracing/coresight/of_coresight.c From 652594c7dfd9bf6392e3a727bc69d89a2562d953 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Fri, 27 Mar 2015 09:10:08 -0700 Subject: [PATCH 117/133] hv: run non-blocking message handlers in the dispatch tasklet A work item in vmbus_connection.work_queue can sleep, waiting for a new host message (usually it is some kind of "completion" message). Currently the new message will be handled in the same workqueue, but since work items in the workqueue is serialized, we actually have no chance to handle the new message if the current work item is sleeping -- as as result, the current work item will hang forever. K. Y. has posted the below fix to resolve the issue: Drivers: hv: vmbus: Perform device register in the per-channel work element Actually we can simplify the fix by directly running non-blocking message handlers in the dispatch tasklet (inspired by K. Y.). This patch is the fundamental change. The following 2 patches will simplify the message offering and rescind-offering handling a lot. Signed-off-by: Dexuan Cui Cc: K. Y. Srinivasan Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 41 +++++++++++++++++---------------------- drivers/hv/hyperv_vmbus.h | 17 ++++++++++++++++ drivers/hv/vmbus_drv.c | 23 ++++++++++++++++++---- 3 files changed, 54 insertions(+), 27 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index bb39705a89d9..287f07b1bef6 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -33,11 +33,6 @@ #include "hyperv_vmbus.h" -struct vmbus_channel_message_table_entry { - enum vmbus_channel_message_type message_type; - void (*message_handler)(struct vmbus_channel_message_header *msg); -}; - struct vmbus_rescind_work { struct work_struct work; struct vmbus_channel *channel; @@ -827,25 +822,25 @@ static void vmbus_onversion_response( } /* Channel message dispatch table */ -static struct vmbus_channel_message_table_entry +struct vmbus_channel_message_table_entry channel_message_table[CHANNELMSG_COUNT] = { - {CHANNELMSG_INVALID, NULL}, - {CHANNELMSG_OFFERCHANNEL, vmbus_onoffer}, - {CHANNELMSG_RESCIND_CHANNELOFFER, vmbus_onoffer_rescind}, - {CHANNELMSG_REQUESTOFFERS, NULL}, - {CHANNELMSG_ALLOFFERS_DELIVERED, vmbus_onoffers_delivered}, - {CHANNELMSG_OPENCHANNEL, NULL}, - {CHANNELMSG_OPENCHANNEL_RESULT, vmbus_onopen_result}, - {CHANNELMSG_CLOSECHANNEL, NULL}, - {CHANNELMSG_GPADL_HEADER, NULL}, - {CHANNELMSG_GPADL_BODY, NULL}, - {CHANNELMSG_GPADL_CREATED, vmbus_ongpadl_created}, - {CHANNELMSG_GPADL_TEARDOWN, NULL}, - {CHANNELMSG_GPADL_TORNDOWN, vmbus_ongpadl_torndown}, - {CHANNELMSG_RELID_RELEASED, NULL}, - {CHANNELMSG_INITIATE_CONTACT, NULL}, - {CHANNELMSG_VERSION_RESPONSE, vmbus_onversion_response}, - {CHANNELMSG_UNLOAD, NULL}, + {CHANNELMSG_INVALID, 0, NULL}, + {CHANNELMSG_OFFERCHANNEL, 0, vmbus_onoffer}, + {CHANNELMSG_RESCIND_CHANNELOFFER, 0, vmbus_onoffer_rescind}, + {CHANNELMSG_REQUESTOFFERS, 0, NULL}, + {CHANNELMSG_ALLOFFERS_DELIVERED, 1, vmbus_onoffers_delivered}, + {CHANNELMSG_OPENCHANNEL, 0, NULL}, + {CHANNELMSG_OPENCHANNEL_RESULT, 1, vmbus_onopen_result}, + {CHANNELMSG_CLOSECHANNEL, 0, NULL}, + {CHANNELMSG_GPADL_HEADER, 0, NULL}, + {CHANNELMSG_GPADL_BODY, 0, NULL}, + {CHANNELMSG_GPADL_CREATED, 1, vmbus_ongpadl_created}, + {CHANNELMSG_GPADL_TEARDOWN, 0, NULL}, + {CHANNELMSG_GPADL_TORNDOWN, 1, vmbus_ongpadl_torndown}, + {CHANNELMSG_RELID_RELEASED, 0, NULL}, + {CHANNELMSG_INITIATE_CONTACT, 0, NULL}, + {CHANNELMSG_VERSION_RESPONSE, 1, vmbus_onversion_response}, + {CHANNELMSG_UNLOAD, 0, NULL}, }; /* diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index c8e27e0fdc99..f40a5a935ab6 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -685,6 +685,23 @@ struct vmbus_msginfo { extern struct vmbus_connection vmbus_connection; +enum vmbus_message_handler_type { + /* The related handler can sleep. */ + VMHT_BLOCKING = 0, + + /* The related handler must NOT sleep. */ + VMHT_NON_BLOCKING = 1, +}; + +struct vmbus_channel_message_table_entry { + enum vmbus_channel_message_type message_type; + enum vmbus_message_handler_type handler_type; + void (*message_handler)(struct vmbus_channel_message_header *msg); +}; + +extern struct vmbus_channel_message_table_entry + channel_message_table[CHANNELMSG_COUNT]; + /* General vmbus interface */ struct hv_device *vmbus_device_create(const uuid_le *type, diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 8313e25378cb..c85235e9f245 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -657,21 +657,36 @@ static void vmbus_on_msg_dpc(unsigned long data) void *page_addr = hv_context.synic_message_page[cpu]; struct hv_message *msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; + struct vmbus_channel_message_header *hdr; + struct vmbus_channel_message_table_entry *entry; struct onmessage_work_context *ctx; while (1) { - if (msg->header.message_type == HVMSG_NONE) { + if (msg->header.message_type == HVMSG_NONE) /* no msg */ break; - } else { + + hdr = (struct vmbus_channel_message_header *)msg->u.payload; + + if (hdr->msgtype >= CHANNELMSG_COUNT) { + WARN_ONCE(1, "unknown msgtype=%d\n", hdr->msgtype); + goto msg_handled; + } + + entry = &channel_message_table[hdr->msgtype]; + if (entry->handler_type == VMHT_BLOCKING) { ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC); if (ctx == NULL) continue; + INIT_WORK(&ctx->work, vmbus_onmessage_work); memcpy(&ctx->msg, msg, sizeof(*msg)); - queue_work(vmbus_connection.work_queue, &ctx->work); - } + queue_work(vmbus_connection.work_queue, &ctx->work); + } else + entry->message_handler(hdr); + +msg_handled: msg->header.message_type = HVMSG_NONE; /* From d43e2fe7da320310834467a3fd87a10adb25a221 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Fri, 27 Mar 2015 09:10:09 -0700 Subject: [PATCH 118/133] hv: don't schedule new works in vmbus_onoffer()/vmbus_onoffer_rescind() Since the 2 fucntions can safely run in vmbus_connection.work_queue without hang, we don't need to schedule new work items into the per-channel workqueue. Actally we can even remove the per-channel workqueue now -- we'll do it in the next patch. Signed-off-by: Dexuan Cui Cc: K. Y. Srinivasan Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 157 +++++++------------------------------- drivers/hv/connection.c | 6 +- drivers/hv/hyperv_vmbus.h | 2 +- 3 files changed, 30 insertions(+), 135 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 287f07b1bef6..d69864d4a4d7 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -33,11 +32,6 @@ #include "hyperv_vmbus.h" -struct vmbus_rescind_work { - struct work_struct work; - struct vmbus_channel *channel; -}; - /** * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message * @icmsghdrp: Pointer to msg header structure @@ -134,20 +128,6 @@ fw_error: EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); -static void vmbus_sc_creation_cb(struct work_struct *work) -{ - struct vmbus_channel *newchannel = container_of(work, - struct vmbus_channel, - work); - struct vmbus_channel *primary_channel = newchannel->primary_channel; - - /* - * On entry sc_creation_callback has been already verified to - * be non-NULL. - */ - primary_channel->sc_creation_callback(newchannel); -} - /* * alloc_channel - Allocate and initialize a vmbus channel object */ @@ -206,40 +186,6 @@ static void free_channel(struct vmbus_channel *channel) queue_work(vmbus_connection.work_queue, &channel->work); } -static void process_rescind_fn(struct work_struct *work) -{ - struct vmbus_rescind_work *rc_work; - struct vmbus_channel *channel; - struct device *dev; - - rc_work = container_of(work, struct vmbus_rescind_work, work); - channel = rc_work->channel; - - /* - * We have already acquired a reference on the channel - * and so it cannot vanish underneath us. - * It is possible (while very unlikely) that we may - * get here while the processing of the initial offer - * is still not complete. Deal with this situation by - * just waiting until the channel is in the correct state. - */ - - while (channel->work.func != release_channel) - msleep(1000); - - if (channel->device_obj) { - dev = get_device(&channel->device_obj->device); - if (dev) { - vmbus_device_unregister(channel->device_obj); - put_device(dev); - } - } else { - hv_process_channel_removal(channel, - channel->offermsg.child_relid); - } - kfree(work); -} - static void percpu_channel_enq(void *arg) { struct vmbus_channel *channel = arg; @@ -302,46 +248,6 @@ void vmbus_free_channels(void) } } -static void vmbus_do_device_register(struct work_struct *work) -{ - struct hv_device *device_obj; - int ret; - unsigned long flags; - struct vmbus_channel *newchannel = container_of(work, - struct vmbus_channel, - work); - - ret = vmbus_device_register(newchannel->device_obj); - if (ret != 0) { - pr_err("unable to add child device object (relid %d)\n", - newchannel->offermsg.child_relid); - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - list_del(&newchannel->listentry); - device_obj = newchannel->device_obj; - newchannel->device_obj = NULL; - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); - - if (newchannel->target_cpu != get_cpu()) { - put_cpu(); - smp_call_function_single(newchannel->target_cpu, - percpu_channel_deq, newchannel, true); - } else { - percpu_channel_deq(newchannel); - put_cpu(); - } - - kfree(device_obj); - if (!newchannel->rescind) { - free_channel(newchannel); - return; - } - } - /* - * The next state for this channel is to be freed. - */ - INIT_WORK(&newchannel->work, release_channel); -} - /* * vmbus_process_offer - Process the offer by creating a channel/device * associated with this offer @@ -410,19 +316,8 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) newchannel->state = CHANNEL_OPEN_STATE; channel->num_sc++; - if (channel->sc_creation_callback != NULL) { - /* - * We need to invoke the sub-channel creation - * callback; invoke this in a seperate work - * context since we are currently running on - * the global work context in which we handle - * messages from the host. - */ - INIT_WORK(&newchannel->work, - vmbus_sc_creation_cb); - queue_work(newchannel->controlwq, - &newchannel->work); - } + if (channel->sc_creation_callback != NULL) + channel->sc_creation_callback(newchannel); return; } @@ -453,13 +348,13 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) * Add the new device to the bus. This will kick off device-driver * binding which eventually invokes the device driver's AddDevice() * method. - * Invoke this call on the per-channel work context. - * Until we return from this function, rescind offer message - * cannot be processed as we are running on the global message - * handling work. */ - INIT_WORK(&newchannel->work, vmbus_do_device_register); - queue_work(newchannel->controlwq, &newchannel->work); + if (vmbus_device_register(newchannel->device_obj) != 0) { + pr_err("unable to add child device object (relid %d)\n", + newchannel->offermsg.child_relid); + kfree(newchannel->device_obj); + goto err_deq_chan; + } return; err_deq_chan: @@ -613,31 +508,35 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) { struct vmbus_channel_rescind_offer *rescind; struct vmbus_channel *channel; - struct vmbus_rescind_work *rc_work; + unsigned long flags; + struct device *dev; rescind = (struct vmbus_channel_rescind_offer *)hdr; - channel = relid2channel(rescind->child_relid, true); + channel = relid2channel(rescind->child_relid); if (channel == NULL) { hv_process_channel_removal(NULL, rescind->child_relid); return; } - /* - * We have acquired a reference on the channel and have posted - * the rescind state. Perform further cleanup in a work context - * that is different from the global work context in which - * we process messages from the host (we are currently executing - * on that global context. - */ - rc_work = kzalloc(sizeof(struct vmbus_rescind_work), GFP_KERNEL); - if (!rc_work) { - pr_err("Unable to allocate memory for rescind processing "); - return; + spin_lock_irqsave(&channel->lock, flags); + channel->rescind = true; + spin_unlock_irqrestore(&channel->lock, flags); + + if (channel->device_obj) { + /* + * We will have to unregister this device from the + * driver core. + */ + dev = get_device(&channel->device_obj->device); + if (dev) { + vmbus_device_unregister(channel->device_obj); + put_device(dev); + } + } else { + hv_process_channel_removal(channel, + channel->offermsg.child_relid); } - rc_work->channel = channel; - INIT_WORK(&rc_work->work, process_rescind_fn); - schedule_work(&rc_work->work); } /* diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 8bcd3071c84f..583d7d42b46d 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -270,7 +270,7 @@ static struct vmbus_channel *pcpu_relid2channel(u32 relid) * relid2channel - Get the channel object given its * child relative id (ie channel id) */ -struct vmbus_channel *relid2channel(u32 relid, bool rescind) +struct vmbus_channel *relid2channel(u32 relid) { struct vmbus_channel *channel; struct vmbus_channel *found_channel = NULL; @@ -282,8 +282,6 @@ struct vmbus_channel *relid2channel(u32 relid, bool rescind) list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { if (channel->offermsg.child_relid == relid) { found_channel = channel; - if (rescind) - found_channel->rescind = true; break; } else if (!list_empty(&channel->sc_list)) { /* @@ -294,8 +292,6 @@ struct vmbus_channel *relid2channel(u32 relid, bool rescind) sc_list); if (cur_sc->offermsg.child_relid == relid) { found_channel = cur_sc; - if (rescind) - found_channel->rescind = true; break; } } diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index f40a5a935ab6..887287ad411f 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -715,7 +715,7 @@ void vmbus_device_unregister(struct hv_device *device_obj); /* VmbusChildDeviceDestroy( */ /* struct hv_device *); */ -struct vmbus_channel *relid2channel(u32 relid, bool rescind); +struct vmbus_channel *relid2channel(u32 relid); void vmbus_free_channels(void); From aadc3780f31865edc84c587ab718a33a8eeeb09d Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Fri, 27 Mar 2015 09:10:10 -0700 Subject: [PATCH 119/133] hv: remove the per-channel workqueue It's not necessary any longer, since we can safely run the blocking message handlers in vmbus_connection.work_queue now. Signed-off-by: Dexuan Cui Cc: K. Y. Srinivasan Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 30 +----------------------------- include/linux/hyperv.h | 3 --- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index d69864d4a4d7..0eeb1b3bc048 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -147,43 +147,15 @@ static struct vmbus_channel *alloc_channel(void) INIT_LIST_HEAD(&channel->sc_list); INIT_LIST_HEAD(&channel->percpu_list); - channel->controlwq = alloc_workqueue("hv_vmbus_ctl/%d", WQ_MEM_RECLAIM, - 1, channel->id); - if (!channel->controlwq) { - kfree(channel); - return NULL; - } - return channel; } -/* - * release_hannel - Release the vmbus channel object itself - */ -static void release_channel(struct work_struct *work) -{ - struct vmbus_channel *channel = container_of(work, - struct vmbus_channel, - work); - - destroy_workqueue(channel->controlwq); - - kfree(channel); -} - /* * free_channel - Release the resources used by the vmbus channel object */ static void free_channel(struct vmbus_channel *channel) { - - /* - * We have to release the channel's workqueue/thread in the vmbus's - * workqueue/thread context - * ie we can't destroy ourselves. - */ - INIT_WORK(&channel->work, release_channel); - queue_work(vmbus_connection.work_queue, &channel->work); + kfree(channel); } static void percpu_channel_enq(void *arg) diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 80e444bfc9dc..902c37aef67e 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -653,8 +653,6 @@ struct vmbus_channel { struct hv_device *device_obj; - struct work_struct work; - enum vmbus_channel_state state; struct vmbus_channel_offer_channel offermsg; @@ -675,7 +673,6 @@ struct vmbus_channel { struct hv_ring_buffer_info outbound; /* send to parent */ struct hv_ring_buffer_info inbound; /* receive from parent */ spinlock_t inbound_lock; - struct workqueue_struct *controlwq; struct vmbus_close_msg close_msg; From d6cbd2c3a3db437708520c66f285c69ef028ac1f Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 27 Mar 2015 09:10:11 -0700 Subject: [PATCH 120/133] Drivers: hv: hv_balloon: do not online pages in offline blocks Currently we add memory in 128Mb blocks but the request from host can be aligned differently. In such case we add a partially backed block and when this block goes online we skip onlining pages which are not backed (hv_online_page() callback serves this purpose). When we receive next request for the same host add region we online pages which were not backed before with hv_bring_pgs_online(). However, we don't check if the the block in question was onlined and online this tail unconditionally. This is bad as we avoid all online_pages() logic: these pages are not accounted, we don't send notifications (and hv_balloon is not the only receiver of them),... And, first of all, nobody asked as to online these pages. Solve the issue by checking if the last previously backed page was onlined and onlining the tail only in case it was. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 014256a2ef88..99afef9393aa 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -778,7 +778,17 @@ static unsigned long handle_pg_range(unsigned long pg_start, pgs_ol = has->ha_end_pfn - start_pfn; if (pgs_ol > pfn_cnt) pgs_ol = pfn_cnt; - hv_bring_pgs_online(start_pfn, pgs_ol); + + /* + * Check if the corresponding memory block is already + * online by checking its last previously backed page. + * In case it is we need to bring rest (which was not + * backed previously) online too. + */ + if (start_pfn > has->start_pfn && + !PageReserved(pfn_to_page(start_pfn - 1))) + hv_bring_pgs_online(start_pfn, pgs_ol); + has->covered_end_pfn += pgs_ol; pfn_cnt -= pgs_ol; } From 7fb0e1a65075a871c58cbcf8c877d1f9ae5cd83c Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 27 Mar 2015 09:10:12 -0700 Subject: [PATCH 121/133] Drivers: hv: hv_balloon: eliminate jumps in piecewiese linear floor function Commit 79208c57da53 ("Drivers: hv: hv_balloon: Make adjustments in computing the floor") was inacurate as it introduced a jump in our piecewiese linear 'floor' function: At 2048MB we have: Left limit: 104 + 2048/8 = 360 Right limit: 256 + 2048/16 = 384 (so the right value is 232) We now have to make an adjustment at 8192 boundary: 232 + 8192/16 = 744 512 + 8192/32 = 768 (so the right value is 488) Suggested-by: Laszlo Ersek Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 99afef9393aa..4f5323cedab3 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -976,8 +976,8 @@ static unsigned long compute_balloon_floor(void) * 128 72 (1/2) * 512 168 (1/4) * 2048 360 (1/8) - * 8192 768 (1/16) - * 32768 1536 (1/32) + * 8192 744 (1/16) + * 32768 1512 (1/32) */ if (totalram_pages < MB2PAGES(128)) min_pages = MB2PAGES(8) + (totalram_pages >> 1); @@ -986,9 +986,9 @@ static unsigned long compute_balloon_floor(void) 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); + min_pages = MB2PAGES(232) + (totalram_pages >> 4); else - min_pages = MB2PAGES(512) + (totalram_pages >> 5); + min_pages = MB2PAGES(488) + (totalram_pages >> 5); #undef MB2PAGES return min_pages; } From 0a1a86ac046091d7228c9f3a283dea5be96275dd Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 27 Mar 2015 09:10:13 -0700 Subject: [PATCH 122/133] Drivers: hv: hv_balloon: survive ballooning request with num_pages=0 ... and simplify alloc_balloon_pages() interface by removing redundant alloc_error from it. If we happen to enter balloon_up() with balloon_wrk.num_pages = 0 we will enter infinite 'while (!done)' loop as alloc_balloon_pages() will be always returning 0 and not setting alloc_error. We will also be sending a meaningless message to the host on every iteration. The 'alloc_unit == 1 && alloc_error -> num_ballooned == 0' change and alloc_error elimination requires a special comment. We do alloc_balloon_pages() with 2 different alloc_unit values and there are 4 different alloc_balloon_pages() results, let's check them all. alloc_unit = 512: 1) num_ballooned = 0, alloc_error = 0: we do 'alloc_unit=1' and retry pre- and post-patch. 2) num_ballooned > 0, alloc_error = 0: we check 'num_ballooned == num_pages' and act accordingly, pre- and post-patch. 3) num_ballooned > 0, alloc_error > 0: we report this chunk and remain within the loop, no changes here. 4) num_ballooned = 0, alloc_error > 0: we do 'alloc_unit=1' and retry pre- and post-patch. alloc_unit = 1: 1) num_ballooned = 0, alloc_error = 0: this can happen in two cases: when we passed 'num_pages=0' to alloc_balloon_pages() or when there was no space in bl_resp to place a single response. The second option is not possible as bl_resp is of PAGE_SIZE size and single response 'union dm_mem_page_range' is 8 bytes, but the first one is (in theory, I think that Hyper-V host never places such requests). Pre-patch code loops forever, post-patch code sends a reply with more_pages = 0 and finishes. 2) num_ballooned > 0, alloc_error = 0: we ran out of space in bl_resp, we report partial success and remain within the loop, no changes pre- and post-patch. 3) num_ballooned > 0, alloc_error > 0: pre-patch code finishes, post-patch code does one more try and if there is no progress (we finish with 'num_ballooned = 0') we finish. So we try a bit harder with this patch. 4) num_ballooned = 0, alloc_error > 0: both pre- and post-patch code enter 'more_pages = 0' branch and finish. So this patch has two real effects: 1) We reply with an empty response to 'num_pages=0' request. 2) We try a bit harder on alloc_unit=1 allocations (and reply with an empty tail reply in case we fail). An empty reply should be supported by host as we were able to send it even with pre-patch code when we were not able to allocate a single page. Suggested-by: Laszlo Ersek Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 4f5323cedab3..74312c88534a 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1081,9 +1081,9 @@ static void free_balloon_pages(struct hv_dynmem_device *dm, -static int alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages, - struct dm_balloon_response *bl_resp, int alloc_unit, - bool *alloc_error) +static int alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages, + struct dm_balloon_response *bl_resp, + int alloc_unit) { int i = 0; struct page *pg; @@ -1104,11 +1104,8 @@ static int alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages, __GFP_NOMEMALLOC | __GFP_NOWARN, get_order(alloc_unit << PAGE_SHIFT)); - if (!pg) { - *alloc_error = true; + if (!pg) return i * alloc_unit; - } - dm->num_pages_ballooned += alloc_unit; @@ -1140,7 +1137,6 @@ static void balloon_up(struct work_struct *dummy) struct dm_balloon_response *bl_resp; int alloc_unit; int ret; - bool alloc_error; bool done = false; int i; struct sysinfo val; @@ -1173,18 +1169,15 @@ static void balloon_up(struct work_struct *dummy) num_pages -= num_ballooned; - alloc_error = false; num_ballooned = alloc_balloon_pages(&dm_device, num_pages, - bl_resp, alloc_unit, - &alloc_error); + bl_resp, alloc_unit); if (alloc_unit != 1 && num_ballooned == 0) { alloc_unit = 1; continue; } - if ((alloc_unit == 1 && alloc_error) || - (num_ballooned == num_pages)) { + if (num_ballooned == 0 || num_ballooned == num_pages) { bl_resp->more_pages = 0; done = true; dm_device.state = DM_INITIALIZED; From e1c0d82dab4a4605d3bd1968436f030dfed4a829 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Fri, 27 Mar 2015 09:10:14 -0700 Subject: [PATCH 123/133] hv_vmbus: Add gradually increased delay for retries in vmbus_post_msg() Most of the retries can be done within a millisecond successfully, so we sleep 1ms before the first retry, then gradually increase the retry interval to 2^n with max value of 2048ms. Doing so, we will have shorter overall delay time, because most of the cases succeed within 1-2 attempts. Signed-off-by: Haiyang Zhang Reviewed-by: K. Y. Srinivasan Reviewed-by: Dexuan Cui Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 583d7d42b46d..b27220a425f4 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -422,6 +422,7 @@ int vmbus_post_msg(void *buffer, size_t buflen) union hv_connection_id conn_id; int ret = 0; int retries = 0; + u32 msec = 1; conn_id.asu32 = 0; conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID; @@ -431,7 +432,7 @@ int vmbus_post_msg(void *buffer, size_t buflen) * insufficient resources. Retry the operation a couple of * times before giving up. */ - while (retries < 10) { + while (retries < 20) { ret = hv_post_message(conn_id, 1, buffer, buflen); switch (ret) { @@ -454,7 +455,9 @@ int vmbus_post_msg(void *buffer, size_t buflen) } retries++; - msleep(1000); + msleep(msec); + if (msec < 2048) + msec *= 2; } return ret; } From 1d9013f09203c694e2cba478b05afc6484d55180 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 27 Mar 2015 00:27:57 +0200 Subject: [PATCH 124/133] mei: fix mei_poll operation mei_poll returned with POLLIN w/o checking whether the operation has really completed. remove redundant check and locking in amthif specific handler Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 25 +++++++++++++++---------- drivers/misc/mei/client.c | 2 +- drivers/misc/mei/main.c | 24 +++++++++++------------- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 7b6ed0bbfc9c..3c1fd87ee10b 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -362,6 +362,18 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) return mei_amthif_run_next_cmd(dev); } +/** + * mei_amthif_poll - the amthif poll function + * + * @dev: the device structure + * @file: pointer to file structure + * @wait: pointer to poll_table structure + * + * Return: poll mask + * + * Locking: called under "dev->device_lock" lock + */ + unsigned int mei_amthif_poll(struct mei_device *dev, struct file *file, poll_table *wait) { @@ -369,19 +381,12 @@ unsigned int mei_amthif_poll(struct mei_device *dev, poll_wait(file, &dev->iamthif_cl.wait, wait); - mutex_lock(&dev->device_lock); - if (!mei_cl_is_connected(&dev->iamthif_cl)) { + if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE && + dev->iamthif_file_object == file) { - mask = POLLERR; - - } else if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE && - dev->iamthif_file_object == file) { - - mask |= (POLLIN | POLLRDNORM); - dev_dbg(dev->dev, "run next amthif cb\n"); + mask |= POLLIN | POLLRDNORM; mei_amthif_run_next_cmd(dev); } - mutex_unlock(&dev->device_lock); return mask; } diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index b6fec4d15307..e5aeb6fd1ba4 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1299,7 +1299,7 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) } else if (cb->fop_type == MEI_FOP_READ) { list_add_tail(&cb->list, &cl->rd_completed); if (waitqueue_active(&cl->rx_wait)) - wake_up_interruptible(&cl->rx_wait); + wake_up_interruptible_all(&cl->rx_wait); else mei_cl_bus_rx_event(cl); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index d80867e0d803..a1ec45054988 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -542,6 +542,7 @@ static long mei_compat_ioctl(struct file *file, */ static unsigned int mei_poll(struct file *file, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); struct mei_cl *cl = file->private_data; struct mei_device *dev; unsigned int mask = 0; @@ -558,22 +559,19 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) goto out; } - mutex_unlock(&dev->device_lock); - - - if (cl == &dev->iamthif_cl) - return mei_amthif_poll(dev, file, wait); - - poll_wait(file, &cl->tx_wait, wait); - - mutex_lock(&dev->device_lock); - - if (!mei_cl_is_connected(cl)) { - mask = POLLERR; + if (cl == &dev->iamthif_cl) { + mask = mei_amthif_poll(dev, file, wait); goto out; } - mask |= (POLLIN | POLLRDNORM); + if (req_events & (POLLIN | POLLRDNORM)) { + poll_wait(file, &cl->rx_wait, wait); + + if (!list_empty(&cl->rd_completed)) + mask |= POLLIN | POLLRDNORM; + else + mei_cl_read_start(cl, 0, file); + } out: mutex_unlock(&dev->device_lock); From f3de9b635d93a3d268adda428e1df94091506a42 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 27 Mar 2015 00:27:58 +0200 Subject: [PATCH 125/133] mei: use mei_cl_is_connected consistently Replace open coded check for cl->state !=/== MEI_FILE_CONNECTED with mei_cl_is_connected function. Note that cl->state != MEI_FILE_CONNECTED is not the same as cl->state == MEI_FILE_DISCONNECTED Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 4 ++-- drivers/misc/mei/client.c | 2 +- drivers/misc/mei/interrupt.c | 2 +- drivers/misc/mei/main.c | 13 +++++++------ drivers/misc/mei/wd.c | 18 +++++++++++------- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 45896f95fed1..b724a67696fc 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -238,7 +238,7 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, dev = cl->dev; mutex_lock(&dev->device_lock); - if (cl->state != MEI_FILE_CONNECTED) { + if (!mei_cl_is_connected(cl)) { rets = -ENODEV; goto out; } @@ -474,7 +474,7 @@ int mei_cl_disable_device(struct mei_cl_device *device) mutex_lock(&dev->device_lock); - if (cl->state != MEI_FILE_CONNECTED) { + if (!mei_cl_is_connected(cl)) { dev_err(dev->dev, "Already disconnected"); err = 0; goto out; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index e5aeb6fd1ba4..1e99ef6a54a2 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -876,7 +876,7 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); mutex_lock(&dev->device_lock); - if (cl->state != MEI_FILE_CONNECTED) { + if (!mei_cl_is_connected(cl)) { cl->state = MEI_FILE_DISCONNECTED; /* something went really wrong */ if (!cl->status) diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 3f23629759db..3f84d2edcde4 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -110,7 +110,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, goto out; } - if (cl->state != MEI_FILE_CONNECTED) { + if (!mei_cl_is_connected(cl)) { cl_dbg(dev, cl, "not connected\n"); cb->status = -ENODEV; goto out; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index a1ec45054988..29fa88b6fa17 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -107,7 +107,7 @@ static int mei_release(struct inode *inode, struct file *file) rets = mei_amthif_release(dev, file); goto out; } - if (cl->state == MEI_FILE_CONNECTED) { + if (mei_cl_is_connected(cl)) { cl->state = MEI_FILE_DISCONNECTING; cl_dbg(dev, cl, "disconnecting\n"); rets = mei_cl_disconnect(cl); @@ -309,9 +309,8 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; } - if (cl->state != MEI_FILE_CONNECTED) { - dev_err(dev->dev, "host client = %d, is not connected to ME client = %d", - cl->host_client_id, cl->me_client_id); + if (!mei_cl_is_connected(cl)) { + cl_err(dev, cl, "is not connected"); rets = -ENODEV; goto out; } @@ -418,7 +417,7 @@ static int mei_ioctl_connect_client(struct file *file, */ if (uuid_le_cmp(data->in_client_uuid, mei_amthif_guid) == 0) { dev_dbg(dev->dev, "FW Client is amthi\n"); - if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) { + if (!mei_cl_is_connected(&dev->iamthif_cl)) { rets = -ENODEV; goto end; } @@ -554,7 +553,9 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) mutex_lock(&dev->device_lock); - if (!mei_cl_is_connected(cl)) { + + if (dev->dev_state != MEI_DEV_ENABLED || + !mei_cl_is_connected(cl)) { mask = POLLERR; goto out; } diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index ac1e6235692d..2725f865c3d6 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c @@ -160,9 +160,10 @@ int mei_wd_send(struct mei_device *dev) */ int mei_wd_stop(struct mei_device *dev) { + struct mei_cl *cl = &dev->wd_cl; int ret; - if (dev->wd_cl.state != MEI_FILE_CONNECTED || + if (!mei_cl_is_connected(cl) || dev->wd_state != MEI_WD_RUNNING) return 0; @@ -170,7 +171,7 @@ int mei_wd_stop(struct mei_device *dev) dev->wd_state = MEI_WD_STOPPING; - ret = mei_cl_flow_ctrl_creds(&dev->wd_cl); + ret = mei_cl_flow_ctrl_creds(cl); if (ret < 0) goto err; @@ -211,13 +212,16 @@ err: */ static int mei_wd_ops_start(struct watchdog_device *wd_dev) { - int err = -ENODEV; struct mei_device *dev; + struct mei_cl *cl; + int err = -ENODEV; dev = watchdog_get_drvdata(wd_dev); if (!dev) return -ENODEV; + cl = &dev->wd_cl; + mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { @@ -226,8 +230,8 @@ static int mei_wd_ops_start(struct watchdog_device *wd_dev) goto end_unlock; } - if (dev->wd_cl.state != MEI_FILE_CONNECTED) { - dev_dbg(dev->dev, "MEI Driver is not connected to Watchdog Client\n"); + if (!mei_cl_is_connected(cl)) { + cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n"); goto end_unlock; } @@ -282,8 +286,8 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev) mutex_lock(&dev->device_lock); - if (cl->state != MEI_FILE_CONNECTED) { - dev_err(dev->dev, "wd: not connected.\n"); + if (!mei_cl_is_connected(cl)) { + cl_err(dev, cl, "wd: not connected.\n"); ret = -ENODEV; goto end; } From 6a84d63d22a0ac79ab422b69ef2b4d75002c5641 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 27 Mar 2015 00:27:59 +0200 Subject: [PATCH 126/133] mei: replace check for connection instead of transitioning The function mei_cl_is_transitioning is just opposite of mei_cl_is_connected. What we actually wanted to check is if we lost connection so we can discard the check for transition and check for 'not connected' Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 4 ++-- drivers/misc/mei/client.h | 14 ++++++++------ drivers/misc/mei/main.c | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index b724a67696fc..4cf38c39878a 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -302,7 +302,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) if (wait_event_interruptible(cl->rx_wait, (!list_empty(&cl->rd_completed)) || - mei_cl_is_transitioning(cl))) { + (!mei_cl_is_connected(cl)))) { if (signal_pending(current)) return -EINTR; @@ -311,7 +311,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) mutex_lock(&dev->device_lock); - if (mei_cl_is_transitioning(cl)) { + if (!mei_cl_is_connected(cl)) { rets = -EBUSY; goto out; } diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 7800d1bd8ba1..0a39e5d45171 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -90,16 +90,18 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl); /* * MEI input output function prototype */ + +/** + * mei_cl_is_connected - host client is connected + * + * @cl: host clinet + * + * Return: true if the host clinet is connected + */ static inline bool mei_cl_is_connected(struct mei_cl *cl) { return cl->state == MEI_FILE_CONNECTED; } -static inline bool mei_cl_is_transitioning(struct mei_cl *cl) -{ - return MEI_FILE_INITIALIZING == cl->state || - MEI_FILE_DISCONNECTED == cl->state || - MEI_FILE_DISCONNECTING == cl->state; -} bool mei_cl_is_other_connecting(struct mei_cl *cl); int mei_cl_disconnect(struct mei_cl *cl); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 29fa88b6fa17..7f77f39c24a7 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -203,7 +203,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, if (wait_event_interruptible(cl->rx_wait, (!list_empty(&cl->rd_completed)) || - mei_cl_is_transitioning(cl))) { + (!mei_cl_is_connected(cl)))) { if (signal_pending(current)) return -EINTR; @@ -211,7 +211,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, } mutex_lock(&dev->device_lock); - if (mei_cl_is_transitioning(cl)) { + if (!mei_cl_is_connected(cl)) { rets = -EBUSY; goto out; } From ba0c444153889a9b672974d85a4a57a8eeb49fe3 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Tue, 31 Mar 2015 11:16:40 -0700 Subject: [PATCH 127/133] Drivers: hv: hv_balloon: correctly handle val.freeram Signed-off-by: Vitaly Kuznetsov Reviewed-by: Laszlo Ersek Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 74312c88534a..4052ad8255fa 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1155,7 +1155,7 @@ static void balloon_up(struct work_struct *dummy) floor = compute_balloon_floor(); /* Refuse to balloon below the floor, keep the 2M granularity. */ - if (val.freeram - num_pages < floor) { + if (val.freeram < num_pages || val.freeram - num_pages < floor) { num_pages = val.freeram > floor ? (val.freeram - floor) : 0; num_pages -= num_pages % PAGES_IN_2M; } From 797f88c987b02a8de8d4fac94ec2877b92ec35a2 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Tue, 31 Mar 2015 11:16:41 -0700 Subject: [PATCH 128/133] Drivers: hv: hv_balloon: correctly handle num_pages>INT_MAX case balloon_wrk.num_pages is __u32 and it comes from host in struct dm_balloon where it is also __u32. We, however, use 'int' in balloon_up() and in case we happen to receive num_pages>INT_MAX request we'll end up allocating zero pages as 'num_pages < alloc_unit' check in alloc_balloon_pages() will pass. Change num_pages type to unsigned int. In real life ballooning request come with num_pages in [512, 32768] range so this is more a future-proof/cleanup. Reported-by: Laszlo Ersek Signed-off-by: Vitaly Kuznetsov Reviewed-by: Laszlo Ersek Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 4052ad8255fa..cb5b7dc9797f 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1081,11 +1081,12 @@ static void free_balloon_pages(struct hv_dynmem_device *dm, -static int alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages, - struct dm_balloon_response *bl_resp, - int alloc_unit) +static unsigned int alloc_balloon_pages(struct hv_dynmem_device *dm, + unsigned int num_pages, + struct dm_balloon_response *bl_resp, + int alloc_unit) { - int i = 0; + unsigned int i = 0; struct page *pg; if (num_pages < alloc_unit) @@ -1132,8 +1133,8 @@ static int alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages, static void balloon_up(struct work_struct *dummy) { - int num_pages = dm_device.balloon_wrk.num_pages; - int num_ballooned = 0; + unsigned int num_pages = dm_device.balloon_wrk.num_pages; + unsigned int num_ballooned = 0; struct dm_balloon_response *bl_resp; int alloc_unit; int ret; From cdcd6f824ecb6b5e5fd6729c2552c1254012c3ca Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 27 Mar 2015 15:39:43 +0100 Subject: [PATCH 129/133] lis3lv02d: DT: use s32 to support negative values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit st,axis-{x,y,z} can be negative to imply inverted axis. Apart from that the minimal and maximal threshold may be negative. Signed-off-by: Sebastian Reichel Reviewed-by: Éric Piel Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lis3lv02d/lis3lv02d.c | 37 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index 3ef4627f9cb1..d2b0968b1ded 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -950,6 +950,7 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3) struct lis3lv02d_platform_data *pdata; struct device_node *np = lis3->of_node; u32 val; + s32 sval; if (!lis3->of_node) return 0; @@ -1054,29 +1055,29 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3) if (of_get_property(np, "st,hipass2-disable", NULL)) pdata->hipass_ctrl |= LIS3_HIPASS2_DISABLE; - if (of_get_property(np, "st,axis-x", &val)) - pdata->axis_x = val; - if (of_get_property(np, "st,axis-y", &val)) - pdata->axis_y = val; - if (of_get_property(np, "st,axis-z", &val)) - pdata->axis_z = val; + if (of_property_read_s32(np, "st,axis-x", &sval) == 0) + pdata->axis_x = sval; + if (of_property_read_s32(np, "st,axis-y", &sval) == 0) + pdata->axis_y = sval; + if (of_property_read_s32(np, "st,axis-z", &sval) == 0) + pdata->axis_z = sval; if (of_get_property(np, "st,default-rate", NULL)) pdata->default_rate = val; - if (of_get_property(np, "st,min-limit-x", &val)) - pdata->st_min_limits[0] = val; - if (of_get_property(np, "st,min-limit-y", &val)) - pdata->st_min_limits[1] = val; - if (of_get_property(np, "st,min-limit-z", &val)) - pdata->st_min_limits[2] = val; + if (of_property_read_s32(np, "st,min-limit-x", &sval) == 0) + pdata->st_min_limits[0] = sval; + if (of_property_read_s32(np, "st,min-limit-y", &sval) == 0) + pdata->st_min_limits[1] = sval; + if (of_property_read_s32(np, "st,min-limit-z", &sval) == 0) + pdata->st_min_limits[2] = sval; - if (of_get_property(np, "st,max-limit-x", &val)) - pdata->st_max_limits[0] = val; - if (of_get_property(np, "st,max-limit-y", &val)) - pdata->st_max_limits[1] = val; - if (of_get_property(np, "st,max-limit-z", &val)) - pdata->st_max_limits[2] = val; + if (of_property_read_s32(np, "st,max-limit-x", &sval) == 0) + pdata->st_max_limits[0] = sval; + if (of_property_read_s32(np, "st,max-limit-y", &sval) == 0) + pdata->st_max_limits[1] = sval; + if (of_property_read_s32(np, "st,max-limit-z", &sval) == 0) + pdata->st_max_limits[2] = sval; lis3->pdata = pdata; From c5131a373613713fccd3b7a377d957391c498f21 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 27 Mar 2015 15:39:44 +0100 Subject: [PATCH 130/133] lis3lv02d: DT: add wakeup unit 2 and wakeup threshold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for the the wakeup threshold and support for the second wakeup unit to the DT based setup. Signed-off-by: Sebastian Reichel Reviewed-by: Éric Piel Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lis3lv02d/lis3lv02d.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index d2b0968b1ded..4739689d23ad 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -1032,6 +1032,23 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3) pdata->wakeup_flags |= LIS3_WAKEUP_Z_LO; if (of_get_property(np, "st,wakeup-z-hi", NULL)) pdata->wakeup_flags |= LIS3_WAKEUP_Z_HI; + if (of_get_property(np, "st,wakeup-threshold", &val)) + pdata->wakeup_thresh = val; + + if (of_get_property(np, "st,wakeup2-x-lo", NULL)) + pdata->wakeup_flags2 |= LIS3_WAKEUP_X_LO; + if (of_get_property(np, "st,wakeup2-x-hi", NULL)) + pdata->wakeup_flags2 |= LIS3_WAKEUP_X_HI; + if (of_get_property(np, "st,wakeup2-y-lo", NULL)) + pdata->wakeup_flags2 |= LIS3_WAKEUP_Y_LO; + if (of_get_property(np, "st,wakeup2-y-hi", NULL)) + pdata->wakeup_flags2 |= LIS3_WAKEUP_Y_HI; + if (of_get_property(np, "st,wakeup2-z-lo", NULL)) + pdata->wakeup_flags2 |= LIS3_WAKEUP_Z_LO; + if (of_get_property(np, "st,wakeup2-z-hi", NULL)) + pdata->wakeup_flags2 |= LIS3_WAKEUP_Z_HI; + if (of_get_property(np, "st,wakeup2-threshold", &val)) + pdata->wakeup_thresh2 = val; if (!of_property_read_u32(np, "st,highpass-cutoff-hz", &val)) { switch (val) { From 21e8681862a544517c1c9d6515770efe3897e326 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 27 Mar 2015 15:39:45 +0100 Subject: [PATCH 131/133] Documentation: DT: lis302: update wakeup binding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This updated the documentation of the DT binding to describe the added wakeup threshold and second wakeup engine. It also adds a note, that the axis values may be negative. Signed-off-by: Sebastian Reichel Reviewed-by: Éric Piel Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/misc/lis302.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/misc/lis302.txt b/Documentation/devicetree/bindings/misc/lis302.txt index 6def86f6b053..2a19bff9693f 100644 --- a/Documentation/devicetree/bindings/misc/lis302.txt +++ b/Documentation/devicetree/bindings/misc/lis302.txt @@ -46,11 +46,18 @@ Optional properties for all bus drivers: interrupt 2 - st,wakeup-{x,y,z}-{lo,hi}: set wakeup condition on x/y/z axis for upper/lower limit + - st,wakeup-threshold: set wakeup threshold + - st,wakeup2-{x,y,z}-{lo,hi}: set wakeup condition on x/y/z axis for + upper/lower limit for second wakeup + engine. + - st,wakeup2-threshold: set wakeup threshold for second wakeup + engine. - st,highpass-cutoff-hz=: 1, 2, 4 or 8 for 1Hz, 2Hz, 4Hz or 8Hz of highpass cut-off frequency - st,hipass{1,2}-disable: disable highpass 1/2. - st,default-rate=: set the default rate - - st,axis-{x,y,z}=: set the axis to map to the three coordinates + - st,axis-{x,y,z}=: set the axis to map to the three coordinates. + Negative values can be used for inverted axis. - st,{min,max}-limit-{x,y,z} set the min/max limits for x/y/z axis (used by self-test) From 1ac4e6fee41d6534b6e54dcbed381590e242bdcb Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 27 Mar 2015 15:39:46 +0100 Subject: [PATCH 132/133] DTS: ARM: OMAP3-N900: Add lis3lv02d support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for the N900's accelerometer to the Nokia N900 DTS file. Signed-off-by: Sebastian Reichel Acked-by: Tony Lindgren Reviewed-by: Éric Piel Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/omap3-n900.dts | 52 ++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts index db80f9d376fa..2cab149b191c 100644 --- a/arch/arm/boot/dts/omap3-n900.dts +++ b/arch/arm/boot/dts/omap3-n900.dts @@ -609,6 +609,58 @@ pinctrl-0 = <&i2c3_pins>; clock-frequency = <400000>; + + lis302dl: lis3lv02d@1d { + compatible = "st,lis3lv02d"; + reg = <0x1d>; + + Vdd-supply = <&vaux1>; + Vdd_IO-supply = <&vio>; + + interrupt-parent = <&gpio6>; + interrupts = <21 20>; /* 181 and 180 */ + + /* click flags */ + st,click-single-x; + st,click-single-y; + st,click-single-z; + + /* Limits are 0.5g * value */ + st,click-threshold-x = <8>; + st,click-threshold-y = <8>; + st,click-threshold-z = <10>; + + /* Click must be longer than time limit */ + st,click-time-limit = <9>; + + /* Kind of debounce filter */ + st,click-latency = <50>; + + /* Interrupt line 2 for click detection */ + st,irq2-click; + + st,wakeup-x-hi; + st,wakeup-y-hi; + st,wakeup-threshold = <(800/18)>; /* millig-value / 18 to get HW values */ + + st,wakeup2-z-hi; + st,wakeup2-threshold = <(900/18)>; /* millig-value / 18 to get HW values */ + + st,hipass1-disable; + st,hipass2-disable; + + st,axis-x = <1>; /* LIS3_DEV_X */ + st,axis-y = <(-2)>; /* LIS3_INV_DEV_Y */ + st,axis-z = <(-3)>; /* LIS3_INV_DEV_Z */ + + st,min-limit-x = <(-32)>; + st,min-limit-y = <3>; + st,min-limit-z = <3>; + + st,max-limit-x = <(-3)>; + st,max-limit-y = <32>; + st,max-limit-z = <32>; + }; }; &mmc1 { From ea5505fabd3b59608750bfd3721d0f8bc5c8b0bb Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 13 Apr 2015 13:56:20 +0300 Subject: [PATCH 133/133] mei: trace: remove unused TRACE_SYSTEM_STRING fix warning: include/trace/ftrace.h:28:0: note: this is the location of the previous definition ^ In file included from include/trace/define_trace.h:90:0, from drivers/misc/mei/mei-trace.h:76, from drivers/misc/mei/mei-trace.c:21: include/trace/ftrace.h:28:0: warning: "TRACE_SYSTEM_STRING" redefined Cc: Stephen Rothwell Reported-by: Stephen Rothwell Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/mei-trace.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h index 5f4e1a17360b..47e1bc6551d4 100644 --- a/drivers/misc/mei/mei-trace.h +++ b/drivers/misc/mei/mei-trace.h @@ -24,9 +24,7 @@ #include #undef TRACE_SYSTEM - #define TRACE_SYSTEM mei -#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM) TRACE_EVENT(mei_reg_read, TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val),