diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-08 12:55:41 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-08 12:55:41 +0200 |
commit | ef0625b70dac9405ac9d9928cf767108041a9e51 (patch) | |
tree | 9b2a0ea588e23e477868f593f8f7397eedb71c2c /drivers | |
parent | Merge tag 'compress-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/gi... (diff) | |
parent | mei: mei_txe_fw_sts can be static (diff) | |
download | linux-ef0625b70dac9405ac9d9928cf767108041a9e51.tar.xz linux-ef0625b70dac9405ac9d9928cf767108041a9e51.zip |
Merge tag 'char-misc-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc driver updates from Greg KH:
"Here's the big set of driver patches for char/misc drivers. Nothing
major in here, the shortlog goes into the details. All have been in
the linux-next tree for a while with no issues"
* tag 'char-misc-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (80 commits)
mei: mei_txe_fw_sts can be static
mei: fix kernel-doc warnings
mei: fix KDoc documentation formatting
mei: drop me_client_presentation_num
mei: trivial: fix errors in prints in comments
mei: remove include to pci header from mei module files
mei: push pci cfg structure me hw
mei: remove the reference to pdev from mei_device
mei: move fw_status back to hw ops handlers
mei: get rid of most of the pci dependencies in mei
mei: push all standard settings into mei_device_init
mei: move mei_hbm_hdr function from hbm.h the hbm.c
mei: kill error message for allocation failure
mei: nfc: fix style warning
mei: fix style warning: Missing a blank line after declarations
mei: pg: fix cat and paste error in comments
mei: debugfs: add single buffer indicator
mei: debugfs: adjust print buffer
mei: add hbm and pg state in devstate debugfs print
Drivers: hv: vmbus: Enable interrupt driven flow control
...
Diffstat (limited to 'drivers')
55 files changed, 3020 insertions, 1335 deletions
diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 65525c7e903c..34174d01462e 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -651,6 +651,7 @@ struct i8k_config_data { enum i8k_configs { DELL_LATITUDE_D520, + DELL_LATITUDE_E6540, DELL_PRECISION_490, DELL_STUDIO, DELL_XPS_M140, @@ -661,6 +662,10 @@ static const struct i8k_config_data i8k_config_data[] = { .fan_mult = 1, .fan_max = I8K_FAN_TURBO, }, + [DELL_LATITUDE_E6540] = { + .fan_mult = 1, + .fan_max = I8K_FAN_HIGH, + }, [DELL_PRECISION_490] = { .fan_mult = 1, .fan_max = I8K_FAN_TURBO, @@ -706,6 +711,14 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = { .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520], }, { + .ident = "Dell Latitude E6540", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6540"), + }, + .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_E6540], + }, + { .ident = "Dell Latitude 2", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index 9b1a5ac4881d..c07dfe5c4da3 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -843,7 +843,6 @@ static struct platform_driver hwicap_platform_driver = { .probe = hwicap_drv_probe, .remove = hwicap_drv_remove, .driver = { - .owner = THIS_MODULE, .name = DRIVER_NAME, .of_match_table = hwicap_of_match, }, diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 6f2f4727de2c..6a1f7de6fa54 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -70,8 +70,21 @@ config EXTCON_PALMAS Say Y here to enable support for USB peripheral and USB host detection by palmas usb. +config EXTCON_RT8973A + tristate "RT8973A EXTCON support" + depends on I2C + select IRQ_DOMAIN + select REGMAP_I2C + select REGMAP_IRQ + help + If you say yes here you get support for the MUIC device of + Richtek RT8973A. The RT8973A is a USB port accessory detector + and switch that is optimized to protect low voltage system + from abnormal high input voltage (up to 28V). + config EXTCON_SM5502 tristate "SM5502 EXTCON support" + depends on I2C select IRQ_DOMAIN select REGMAP_I2C select REGMAP_IRQ diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index b38546eb522a..0370b42e5a27 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -10,4 +10,5 @@ obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o 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 diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index 5b7ec274cb63..72f19a37fd01 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -20,16 +20,16 @@ * */ -#include <linux/module.h> -#include <linux/kernel.h> +#include <linux/extcon.h> +#include <linux/extcon/extcon-gpio.h> +#include <linux/gpio.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/workqueue.h> -#include <linux/gpio.h> -#include <linux/extcon.h> -#include <linux/extcon/extcon-gpio.h> struct gpio_extcon_data { struct extcon_dev *edev; diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 77460f2c1ca1..490e27475bac 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -255,10 +255,14 @@ static int max77693_muic_set_debounce_time(struct max77693_muic_info *info, case ADC_DEBOUNCE_TIME_10MS: case ADC_DEBOUNCE_TIME_25MS: case ADC_DEBOUNCE_TIME_38_62MS: - ret = regmap_update_bits(info->max77693->regmap_muic, - MAX77693_MUIC_REG_CTRL3, - CONTROL3_ADCDBSET_MASK, - time << CONTROL3_ADCDBSET_SHIFT); + /* + * Don't touch BTLDset, JIGset when you want to change adc + * debounce time. If it writes other than 0 to BTLDset, JIGset + * muic device will be reset and loose current state. + */ + ret = regmap_write(info->max77693->regmap_muic, + MAX77693_MUIC_REG_CTRL3, + time << CONTROL3_ADCDBSET_SHIFT); if (ret) { dev_err(info->dev, "failed to set ADC debounce time\n"); return ret; @@ -1155,13 +1159,11 @@ static int max77693_muic_probe(struct platform_device *pdev) virq = regmap_irq_get_virq(max77693->irq_data_muic, muic_irq->irq); - if (!virq) { - ret = -EINVAL; - goto err_irq; - } + if (!virq) + return -EINVAL; muic_irq->virq = virq; - ret = request_threaded_irq(virq, NULL, + ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, max77693_muic_irq_handler, IRQF_NO_SUSPEND, muic_irq->name, info); @@ -1170,7 +1172,7 @@ static int max77693_muic_probe(struct platform_device *pdev) "failed: irq request (IRQ: %d," " error :%d)\n", muic_irq->irq, ret); - goto err_irq; + return ret; } } @@ -1179,15 +1181,14 @@ static int max77693_muic_probe(struct platform_device *pdev) max77693_extcon_cable); if (IS_ERR(info->edev)) { dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); - ret = -ENOMEM; - goto err_irq; + return -ENOMEM; } info->edev->name = DEV_NAME; ret = devm_extcon_dev_register(&pdev->dev, info->edev); if (ret) { dev_err(&pdev->dev, "failed to register extcon device\n"); - goto err_irq; + return ret; } /* Initialize MUIC register by using platform data or default data */ @@ -1265,7 +1266,7 @@ static int max77693_muic_probe(struct platform_device *pdev) MAX77693_MUIC_REG_ID, &id); if (ret < 0) { dev_err(&pdev->dev, "failed to read revision number\n"); - goto err_irq; + return ret; } dev_info(info->dev, "device ID : 0x%x\n", id); @@ -1285,20 +1286,12 @@ static int max77693_muic_probe(struct platform_device *pdev) delay_jiffies); return ret; - -err_irq: - while (--i >= 0) - free_irq(muic_irqs[i].virq, info); - return ret; } static int max77693_muic_remove(struct platform_device *pdev) { struct max77693_muic_info *info = platform_get_drvdata(pdev); - int i; - for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) - free_irq(muic_irqs[i].virq, info); cancel_work_sync(&info->irq_work); input_unregister_device(info->dock); diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c new file mode 100644 index 000000000000..a784b2d5ee72 --- /dev/null +++ b/drivers/extcon/extcon-rt8973a.c @@ -0,0 +1,740 @@ +/* + * extcon-rt8973a.c - Richtek RT8973A extcon driver to support USB switches + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * Author: Chanwoo Choi <cw00.choi@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/extcon.h> + +#include "extcon-rt8973a.h" + +#define DELAY_MS_DEFAULT 20000 /* unit: millisecond */ + +struct muic_irq { + unsigned int irq; + const char *name; + unsigned int virq; +}; + +struct reg_data { + u8 reg; + u8 mask; + u8 val; + bool invert; +}; + +struct rt8973a_muic_info { + struct device *dev; + struct extcon_dev *edev; + + struct i2c_client *i2c; + struct regmap *regmap; + + struct regmap_irq_chip_data *irq_data; + struct muic_irq *muic_irqs; + unsigned int num_muic_irqs; + int irq; + bool irq_attach; + bool irq_detach; + bool irq_ovp; + bool irq_otp; + struct work_struct irq_work; + + struct reg_data *reg_data; + unsigned int num_reg_data; + bool auto_config; + + struct mutex mutex; + + /* + * Use delayed workqueue to detect cable state and then + * notify cable state to notifiee/platform through uevent. + * After completing the booting of platform, the extcon provider + * driver should notify cable state to upper layer. + */ + struct delayed_work wq_detcable; +}; + +/* Default value of RT8973A register to bring up MUIC device. */ +static struct reg_data rt8973a_reg_data[] = { + { + .reg = RT8973A_REG_CONTROL1, + .mask = RT8973A_REG_CONTROL1_ADC_EN_MASK + | RT8973A_REG_CONTROL1_USB_CHD_EN_MASK + | RT8973A_REG_CONTROL1_CHGTYP_MASK + | RT8973A_REG_CONTROL1_SWITCH_OPEN_MASK + | RT8973A_REG_CONTROL1_AUTO_CONFIG_MASK + | RT8973A_REG_CONTROL1_INTM_MASK, + .val = RT8973A_REG_CONTROL1_ADC_EN_MASK + | RT8973A_REG_CONTROL1_USB_CHD_EN_MASK + | RT8973A_REG_CONTROL1_CHGTYP_MASK, + .invert = false, + }, + { /* sentinel */ } +}; + +/* List of detectable cables */ +enum { + EXTCON_CABLE_USB = 0, + EXTCON_CABLE_USB_HOST, + EXTCON_CABLE_TA, + EXTCON_CABLE_JIG_OFF_USB, + EXTCON_CABLE_JIG_ON_USB, + EXTCON_CABLE_JIG_OFF_UART, + EXTCON_CABLE_JIG_ON_UART, + + EXTCON_CABLE_END, +}; + +static const char *rt8973a_extcon_cable[] = { + [EXTCON_CABLE_USB] = "USB", + [EXTCON_CABLE_USB_HOST] = "USB-Host", + [EXTCON_CABLE_TA] = "TA", + [EXTCON_CABLE_JIG_OFF_USB] = "JIG-USB-OFF", + [EXTCON_CABLE_JIG_ON_USB] = "JIG-USB-ON", + [EXTCON_CABLE_JIG_OFF_UART] = "JIG-UART-OFF", + [EXTCON_CABLE_JIG_ON_UART] = "JIG-UART-ON", + NULL, +}; + +/* Define OVP (Over Voltage Protection), OTP (Over Temperature Protection) */ +enum rt8973a_event_type { + RT8973A_EVENT_ATTACH = 1, + RT8973A_EVENT_DETACH, + RT8973A_EVENT_OVP, + RT8973A_EVENT_OTP, +}; + +/* Define supported accessory type */ +enum rt8973a_muic_acc_type { + RT8973A_MUIC_ADC_OTG = 0x0, + RT8973A_MUIC_ADC_AUDIO_SEND_END_BUTTON, + RT8973A_MUIC_ADC_AUDIO_REMOTE_S1_BUTTON, + RT8973A_MUIC_ADC_AUDIO_REMOTE_S2_BUTTON, + RT8973A_MUIC_ADC_AUDIO_REMOTE_S3_BUTTON, + RT8973A_MUIC_ADC_AUDIO_REMOTE_S4_BUTTON, + RT8973A_MUIC_ADC_AUDIO_REMOTE_S5_BUTTON, + RT8973A_MUIC_ADC_AUDIO_REMOTE_S6_BUTTON, + RT8973A_MUIC_ADC_AUDIO_REMOTE_S7_BUTTON, + RT8973A_MUIC_ADC_AUDIO_REMOTE_S8_BUTTON, + RT8973A_MUIC_ADC_AUDIO_REMOTE_S9_BUTTON, + RT8973A_MUIC_ADC_AUDIO_REMOTE_S10_BUTTON, + RT8973A_MUIC_ADC_AUDIO_REMOTE_S11_BUTTON, + RT8973A_MUIC_ADC_AUDIO_REMOTE_S12_BUTTON, + RT8973A_MUIC_ADC_RESERVED_ACC_1, + RT8973A_MUIC_ADC_RESERVED_ACC_2, + RT8973A_MUIC_ADC_RESERVED_ACC_3, + RT8973A_MUIC_ADC_RESERVED_ACC_4, + RT8973A_MUIC_ADC_RESERVED_ACC_5, + RT8973A_MUIC_ADC_AUDIO_TYPE2, + RT8973A_MUIC_ADC_PHONE_POWERED_DEV, + RT8973A_MUIC_ADC_UNKNOWN_ACC_1, + RT8973A_MUIC_ADC_UNKNOWN_ACC_2, + RT8973A_MUIC_ADC_TA, + RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB, + RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB, + RT8973A_MUIC_ADC_UNKNOWN_ACC_3, + RT8973A_MUIC_ADC_UNKNOWN_ACC_4, + RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART, + RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART, + RT8973A_MUIC_ADC_UNKNOWN_ACC_5, + RT8973A_MUIC_ADC_OPEN = 0x1f, + + /* The below accessories has same ADC value (0x1f). + So, Device type1 is used to separate specific accessory. */ + /* |---------|--ADC| */ + /* | [7:5]|[4:0]| */ + RT8973A_MUIC_ADC_USB = 0x3f, /* | 001|11111| */ +}; + +/* List of supported interrupt for RT8973A */ +static struct muic_irq rt8973a_muic_irqs[] = { + { RT8973A_INT1_ATTACH, "muic-attach" }, + { RT8973A_INT1_DETACH, "muic-detach" }, + { RT8973A_INT1_CHGDET, "muic-chgdet" }, + { RT8973A_INT1_DCD_T, "muic-dcd-t" }, + { RT8973A_INT1_OVP, "muic-ovp" }, + { RT8973A_INT1_CONNECT, "muic-connect" }, + { RT8973A_INT1_ADC_CHG, "muic-adc-chg" }, + { RT8973A_INT1_OTP, "muic-otp" }, + { RT8973A_INT2_UVLO, "muic-uvlo" }, + { RT8973A_INT2_POR, "muic-por" }, + { RT8973A_INT2_OTP_FET, "muic-otp-fet" }, + { RT8973A_INT2_OVP_FET, "muic-ovp-fet" }, + { RT8973A_INT2_OCP_LATCH, "muic-ocp-latch" }, + { RT8973A_INT2_OCP, "muic-ocp" }, + { RT8973A_INT2_OVP_OCP, "muic-ovp-ocp" }, +}; + +/* Define interrupt list of RT8973A to register regmap_irq */ +static const struct regmap_irq rt8973a_irqs[] = { + /* INT1 interrupts */ + { .reg_offset = 0, .mask = RT8973A_INT1_ATTACH_MASK, }, + { .reg_offset = 0, .mask = RT8973A_INT1_DETACH_MASK, }, + { .reg_offset = 0, .mask = RT8973A_INT1_CHGDET_MASK, }, + { .reg_offset = 0, .mask = RT8973A_INT1_DCD_T_MASK, }, + { .reg_offset = 0, .mask = RT8973A_INT1_OVP_MASK, }, + { .reg_offset = 0, .mask = RT8973A_INT1_CONNECT_MASK, }, + { .reg_offset = 0, .mask = RT8973A_INT1_ADC_CHG_MASK, }, + { .reg_offset = 0, .mask = RT8973A_INT1_OTP_MASK, }, + + /* INT2 interrupts */ + { .reg_offset = 1, .mask = RT8973A_INT2_UVLOT_MASK,}, + { .reg_offset = 1, .mask = RT8973A_INT2_POR_MASK, }, + { .reg_offset = 1, .mask = RT8973A_INT2_OTP_FET_MASK, }, + { .reg_offset = 1, .mask = RT8973A_INT2_OVP_FET_MASK, }, + { .reg_offset = 1, .mask = RT8973A_INT2_OCP_LATCH_MASK, }, + { .reg_offset = 1, .mask = RT8973A_INT2_OCP_MASK, }, + { .reg_offset = 1, .mask = RT8973A_INT2_OVP_OCP_MASK, }, +}; + +static const struct regmap_irq_chip rt8973a_muic_irq_chip = { + .name = "rt8973a", + .status_base = RT8973A_REG_INT1, + .mask_base = RT8973A_REG_INTM1, + .mask_invert = false, + .num_regs = 2, + .irqs = rt8973a_irqs, + .num_irqs = ARRAY_SIZE(rt8973a_irqs), +}; + +/* Define regmap configuration of RT8973A for I2C communication */ +static bool rt8973a_muic_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT8973A_REG_INTM1: + case RT8973A_REG_INTM2: + return true; + default: + break; + } + return false; +} + +static const struct regmap_config rt8973a_muic_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = rt8973a_muic_volatile_reg, + .max_register = RT8973A_REG_END, +}; + +/* Change DM_CON/DP_CON/VBUSIN switch according to cable type */ +static int rt8973a_muic_set_path(struct rt8973a_muic_info *info, + unsigned int con_sw, bool attached) +{ + int ret; + + /* + * Don't need to set h/w path according to cable type + * if Auto-configuration mode of CONTROL1 register is true. + */ + if (info->auto_config) + return 0; + + if (!attached) + con_sw = DM_DP_SWITCH_UART; + + switch (con_sw) { + case DM_DP_SWITCH_OPEN: + case DM_DP_SWITCH_USB: + case DM_DP_SWITCH_UART: + ret = regmap_update_bits(info->regmap, RT8973A_REG_MANUAL_SW1, + RT8973A_REG_MANUAL_SW1_DP_MASK | + RT8973A_REG_MANUAL_SW1_DM_MASK, + con_sw); + if (ret < 0) { + dev_err(info->dev, + "cannot update DM_CON/DP_CON switch\n"); + return ret; + } + break; + default: + dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n", + con_sw); + return -EINVAL; + } + + return 0; +} + +static int rt8973a_muic_get_cable_type(struct rt8973a_muic_info *info) +{ + unsigned int adc, dev1; + int ret, cable_type; + + /* Read ADC value according to external cable or button */ + ret = regmap_read(info->regmap, RT8973A_REG_ADC, &adc); + if (ret) { + dev_err(info->dev, "failed to read ADC register\n"); + return ret; + } + cable_type = adc & RT8973A_REG_ADC_MASK; + + /* Read Device 1 reigster to identify correct cable type */ + ret = regmap_read(info->regmap, RT8973A_REG_DEV1, &dev1); + if (ret) { + dev_err(info->dev, "failed to read DEV1 register\n"); + return ret; + } + + switch (adc) { + case RT8973A_MUIC_ADC_OPEN: + if (dev1 & RT8973A_REG_DEV1_USB_MASK) + cable_type = RT8973A_MUIC_ADC_USB; + else if (dev1 & RT8973A_REG_DEV1_DCPORT_MASK) + cable_type = RT8973A_MUIC_ADC_TA; + else + cable_type = RT8973A_MUIC_ADC_OPEN; + break; + default: + break; + } + + return cable_type; +} + +static int rt8973a_muic_cable_handler(struct rt8973a_muic_info *info, + enum rt8973a_event_type event) +{ + static unsigned int prev_cable_type; + const char **cable_names = info->edev->supported_cable; + unsigned int con_sw = DM_DP_SWITCH_UART; + int ret, idx = 0, cable_type; + bool attached = false; + + if (!cable_names) + return 0; + + switch (event) { + case RT8973A_EVENT_ATTACH: + cable_type = rt8973a_muic_get_cable_type(info); + attached = true; + break; + case RT8973A_EVENT_DETACH: + cable_type = prev_cable_type; + attached = false; + break; + case RT8973A_EVENT_OVP: + case RT8973A_EVENT_OTP: + dev_warn(info->dev, + "happen Over %s issue. Need to disconnect all cables\n", + event == RT8973A_EVENT_OVP ? "Voltage" : "Temperature"); + cable_type = prev_cable_type; + attached = false; + break; + default: + dev_err(info->dev, + "Cannot handle this event (event:%d)\n", event); + return -EINVAL; + } + prev_cable_type = cable_type; + + switch (cable_type) { + case RT8973A_MUIC_ADC_OTG: + idx = EXTCON_CABLE_USB_HOST; + con_sw = DM_DP_SWITCH_USB; + break; + case RT8973A_MUIC_ADC_TA: + idx = EXTCON_CABLE_TA; + con_sw = DM_DP_SWITCH_OPEN; + break; + case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB: + idx = EXTCON_CABLE_JIG_OFF_USB; + con_sw = DM_DP_SWITCH_UART; + break; + case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB: + idx = EXTCON_CABLE_JIG_ON_USB; + con_sw = DM_DP_SWITCH_UART; + break; + case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART: + idx = EXTCON_CABLE_JIG_OFF_UART; + con_sw = DM_DP_SWITCH_UART; + break; + case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART: + idx = EXTCON_CABLE_JIG_ON_UART; + con_sw = DM_DP_SWITCH_UART; + break; + case RT8973A_MUIC_ADC_USB: + idx = EXTCON_CABLE_USB; + con_sw = DM_DP_SWITCH_USB; + break; + case RT8973A_MUIC_ADC_OPEN: + return 0; + case RT8973A_MUIC_ADC_UNKNOWN_ACC_1: + case RT8973A_MUIC_ADC_UNKNOWN_ACC_2: + case RT8973A_MUIC_ADC_UNKNOWN_ACC_3: + case RT8973A_MUIC_ADC_UNKNOWN_ACC_4: + case RT8973A_MUIC_ADC_UNKNOWN_ACC_5: + dev_warn(info->dev, + "Unknown accessory type (adc:0x%x)\n", cable_type); + return 0; + case RT8973A_MUIC_ADC_AUDIO_SEND_END_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_REMOTE_S1_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_REMOTE_S2_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_REMOTE_S3_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_REMOTE_S4_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_REMOTE_S5_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_REMOTE_S6_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_REMOTE_S7_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_REMOTE_S8_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_REMOTE_S9_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_REMOTE_S10_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_REMOTE_S11_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_REMOTE_S12_BUTTON: + case RT8973A_MUIC_ADC_AUDIO_TYPE2: + dev_warn(info->dev, + "Audio device/button type (adc:0x%x)\n", cable_type); + return 0; + case RT8973A_MUIC_ADC_RESERVED_ACC_1: + case RT8973A_MUIC_ADC_RESERVED_ACC_2: + case RT8973A_MUIC_ADC_RESERVED_ACC_3: + case RT8973A_MUIC_ADC_RESERVED_ACC_4: + case RT8973A_MUIC_ADC_RESERVED_ACC_5: + case RT8973A_MUIC_ADC_PHONE_POWERED_DEV: + return 0; + default: + dev_err(info->dev, + "Cannot handle this cable_type (adc:0x%x)\n", + cable_type); + return -EINVAL; + } + + /* Change internal hardware path(DM_CON/DP_CON) */ + ret = rt8973a_muic_set_path(info, con_sw, attached); + if (ret < 0) + return ret; + + /* Change the state of external accessory */ + extcon_set_cable_state(info->edev, cable_names[idx], attached); + + return 0; +} + +static void rt8973a_muic_irq_work(struct work_struct *work) +{ + struct rt8973a_muic_info *info = container_of(work, + struct rt8973a_muic_info, irq_work); + int ret = 0; + + if (!info->edev) + return; + + mutex_lock(&info->mutex); + + /* Detect attached or detached cables */ + if (info->irq_attach) { + ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_ATTACH); + info->irq_attach = false; + } + + if (info->irq_detach) { + ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_DETACH); + info->irq_detach = false; + } + + if (info->irq_ovp) { + ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_OVP); + info->irq_ovp = false; + } + + if (info->irq_otp) { + ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_OTP); + info->irq_otp = false; + } + + if (ret < 0) + dev_err(info->dev, "failed to handle MUIC interrupt\n"); + + mutex_unlock(&info->mutex); +} + +static irqreturn_t rt8973a_muic_irq_handler(int irq, void *data) +{ + struct rt8973a_muic_info *info = data; + int i, irq_type = -1; + + for (i = 0; i < info->num_muic_irqs; i++) + if (irq == info->muic_irqs[i].virq) + irq_type = info->muic_irqs[i].irq; + + switch (irq_type) { + case RT8973A_INT1_ATTACH: + info->irq_attach = true; + break; + case RT8973A_INT1_DETACH: + info->irq_detach = true; + break; + case RT8973A_INT1_OVP: + info->irq_ovp = true; + break; + case RT8973A_INT1_OTP: + info->irq_otp = true; + break; + case RT8973A_INT1_CHGDET: + case RT8973A_INT1_DCD_T: + case RT8973A_INT1_CONNECT: + case RT8973A_INT1_ADC_CHG: + case RT8973A_INT2_UVLO: + case RT8973A_INT2_POR: + case RT8973A_INT2_OTP_FET: + case RT8973A_INT2_OVP_FET: + case RT8973A_INT2_OCP_LATCH: + case RT8973A_INT2_OCP: + case RT8973A_INT2_OVP_OCP: + default: + dev_dbg(info->dev, + "Cannot handle this interrupt (%d)\n", irq_type); + break; + } + + schedule_work(&info->irq_work); + + return IRQ_HANDLED; +} + +static void rt8973a_muic_detect_cable_wq(struct work_struct *work) +{ + struct rt8973a_muic_info *info = container_of(to_delayed_work(work), + struct rt8973a_muic_info, wq_detcable); + int ret; + + /* Notify the state of connector cable or not */ + ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_ATTACH); + if (ret < 0) + dev_warn(info->dev, "failed to detect cable state\n"); +} + +static void rt8973a_init_dev_type(struct rt8973a_muic_info *info) +{ + unsigned int data, vendor_id, version_id; + int i, ret; + + /* To test I2C, Print version_id and vendor_id of RT8973A */ + ret = regmap_read(info->regmap, RT8973A_REG_DEVICE_ID, &data); + if (ret) { + dev_err(info->dev, + "failed to read DEVICE_ID register: %d\n", ret); + return; + } + + vendor_id = ((data & RT8973A_REG_DEVICE_ID_VENDOR_MASK) >> + RT8973A_REG_DEVICE_ID_VENDOR_SHIFT); + version_id = ((data & RT8973A_REG_DEVICE_ID_VERSION_MASK) >> + RT8973A_REG_DEVICE_ID_VERSION_SHIFT); + + dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n", + version_id, vendor_id); + + /* Initiazle the register of RT8973A device to bring-up */ + for (i = 0; i < info->num_reg_data; i++) { + u8 reg = info->reg_data[i].reg; + u8 mask = info->reg_data[i].mask; + u8 val = 0; + + if (info->reg_data[i].invert) + val = ~info->reg_data[i].val; + else + val = info->reg_data[i].val; + + regmap_update_bits(info->regmap, reg, mask, val); + } + + /* Check whether RT8973A is auto swithcing mode or not */ + ret = regmap_read(info->regmap, RT8973A_REG_CONTROL1, &data); + if (ret) { + dev_err(info->dev, + "failed to read CONTROL1 register: %d\n", ret); + return; + } + + data &= RT8973A_REG_CONTROL1_AUTO_CONFIG_MASK; + if (data) { + info->auto_config = true; + dev_info(info->dev, + "Enable Auto-configuration for internal path\n"); + } +} + +static int rt8973a_muic_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device_node *np = i2c->dev.of_node; + struct rt8973a_muic_info *info; + int i, ret, irq_flags; + + if (!np) + return -EINVAL; + + info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL); + if (!info) { + dev_err(&i2c->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + i2c_set_clientdata(i2c, info); + + info->dev = &i2c->dev; + info->i2c = i2c; + info->irq = i2c->irq; + info->muic_irqs = rt8973a_muic_irqs; + info->num_muic_irqs = ARRAY_SIZE(rt8973a_muic_irqs); + info->reg_data = rt8973a_reg_data; + info->num_reg_data = ARRAY_SIZE(rt8973a_reg_data); + + mutex_init(&info->mutex); + + INIT_WORK(&info->irq_work, rt8973a_muic_irq_work); + + info->regmap = devm_regmap_init_i2c(i2c, &rt8973a_muic_regmap_config); + if (IS_ERR(info->regmap)) { + ret = PTR_ERR(info->regmap); + dev_err(info->dev, "failed to allocate register map: %d\n", + ret); + return ret; + } + + /* Support irq domain for RT8973A MUIC device */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED; + ret = regmap_add_irq_chip(info->regmap, info->irq, irq_flags, 0, + &rt8973a_muic_irq_chip, &info->irq_data); + if (ret != 0) { + dev_err(info->dev, "failed to add irq_chip (irq:%d, err:%d)\n", + info->irq, ret); + return ret; + } + + for (i = 0; i < info->num_muic_irqs; i++) { + struct muic_irq *muic_irq = &info->muic_irqs[i]; + unsigned int virq = 0; + + virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq); + if (virq <= 0) + return -EINVAL; + muic_irq->virq = virq; + + ret = devm_request_threaded_irq(info->dev, virq, NULL, + rt8973a_muic_irq_handler, + IRQF_NO_SUSPEND, + muic_irq->name, info); + if (ret) { + dev_err(info->dev, + "failed: irq request (IRQ: %d, error :%d)\n", + muic_irq->irq, ret); + return ret; + } + } + + /* Allocate extcon device */ + info->edev = devm_extcon_dev_allocate(info->dev, rt8973a_extcon_cable); + if (IS_ERR(info->edev)) { + dev_err(info->dev, "failed to allocate memory for extcon\n"); + return -ENOMEM; + } + info->edev->name = np->name; + + /* Register extcon device */ + ret = devm_extcon_dev_register(info->dev, info->edev); + if (ret) { + dev_err(info->dev, "failed to register extcon device\n"); + return ret; + } + + /* + * Detect accessory after completing the initialization of platform + * + * - Use delayed workqueue to detect cable state and then + * notify cable state to notifiee/platform through uevent. + * After completing the booting of platform, the extcon provider + * driver should notify cable state to upper layer. + */ + INIT_DELAYED_WORK(&info->wq_detcable, rt8973a_muic_detect_cable_wq); + queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, + msecs_to_jiffies(DELAY_MS_DEFAULT)); + + /* Initialize RT8973A device and print vendor id and version id */ + rt8973a_init_dev_type(info); + + return 0; +} + +static int rt8973a_muic_i2c_remove(struct i2c_client *i2c) +{ + struct rt8973a_muic_info *info = i2c_get_clientdata(i2c); + + regmap_del_irq_chip(info->irq, info->irq_data); + + return 0; +} + +static struct of_device_id rt8973a_dt_match[] = { + { .compatible = "richtek,rt8973a-muic" }, + { }, +}; + +#ifdef CONFIG_PM_SLEEP +static int rt8973a_muic_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct rt8973a_muic_info *info = i2c_get_clientdata(i2c); + + enable_irq_wake(info->irq); + + return 0; +} + +static int rt8973a_muic_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct rt8973a_muic_info *info = i2c_get_clientdata(i2c); + + disable_irq_wake(info->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(rt8973a_muic_pm_ops, + rt8973a_muic_suspend, rt8973a_muic_resume); + +static const struct i2c_device_id rt8973a_i2c_id[] = { + { "rt8973a", TYPE_RT8973A }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt8973a_i2c_id); + +static struct i2c_driver rt8973a_muic_i2c_driver = { + .driver = { + .name = "rt8973a", + .owner = THIS_MODULE, + .pm = &rt8973a_muic_pm_ops, + .of_match_table = rt8973a_dt_match, + }, + .probe = rt8973a_muic_i2c_probe, + .remove = rt8973a_muic_i2c_remove, + .id_table = rt8973a_i2c_id, +}; + +static int __init rt8973a_muic_i2c_init(void) +{ + return i2c_add_driver(&rt8973a_muic_i2c_driver); +} +subsys_initcall(rt8973a_muic_i2c_init); + +MODULE_DESCRIPTION("Richtek RT8973A Extcon driver"); +MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/extcon/extcon-rt8973a.h b/drivers/extcon/extcon-rt8973a.h new file mode 100644 index 000000000000..9dc3e0227eb7 --- /dev/null +++ b/drivers/extcon/extcon-rt8973a.h @@ -0,0 +1,203 @@ +/* + * rt8973a.h + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * + * 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_EXTCON_RT8973A_H +#define __LINUX_EXTCON_RT8973A_H + +enum rt8973a_types { + TYPE_RT8973A, +}; + +/* RT8973A registers */ +enum rt8973A_reg { + RT8973A_REG_DEVICE_ID = 0x1, + RT8973A_REG_CONTROL1, + RT8973A_REG_INT1, + RT8973A_REG_INT2, + RT8973A_REG_INTM1, + RT8973A_REG_INTM2, + RT8973A_REG_ADC, + RT8973A_REG_RSVD_1, + RT8973A_REG_RSVD_2, + RT8973A_REG_DEV1, + RT8973A_REG_DEV2, + RT8973A_REG_RSVD_3, + RT8973A_REG_RSVD_4, + RT8973A_REG_RSVD_5, + RT8973A_REG_RSVD_6, + RT8973A_REG_RSVD_7, + RT8973A_REG_RSVD_8, + RT8973A_REG_RSVD_9, + RT8973A_REG_MANUAL_SW1, + RT8973A_REG_MANUAL_SW2, + RT8973A_REG_RSVD_10, + RT8973A_REG_RSVD_11, + RT8973A_REG_RSVD_12, + RT8973A_REG_RSVD_13, + RT8973A_REG_RSVD_14, + RT8973A_REG_RSVD_15, + RT8973A_REG_RESET, + + RT8973A_REG_END, +}; + +/* Define RT8973A MASK/SHIFT constant */ +#define RT8973A_REG_DEVICE_ID_VENDOR_SHIFT 0 +#define RT8973A_REG_DEVICE_ID_VERSION_SHIFT 3 +#define RT8973A_REG_DEVICE_ID_VENDOR_MASK (0x7 << RT8973A_REG_DEVICE_ID_VENDOR_SHIFT) +#define RT8973A_REG_DEVICE_ID_VERSION_MASK (0x1f << RT8973A_REG_DEVICE_ID_VERSION_SHIFT) + +#define RT8973A_REG_CONTROL1_INTM_SHIFT 0 +#define RT8973A_REG_CONTROL1_AUTO_CONFIG_SHIFT 2 +#define RT8973A_REG_CONTROL1_I2C_RST_EN_SHIFT 3 +#define RT8973A_REG_CONTROL1_SWITCH_OPEN_SHIFT 4 +#define RT8973A_REG_CONTROL1_CHGTYP_SHIFT 5 +#define RT8973A_REG_CONTROL1_USB_CHD_EN_SHIFT 6 +#define RT8973A_REG_CONTROL1_ADC_EN_SHIFT 7 +#define RT8973A_REG_CONTROL1_INTM_MASK (0x1 << RT8973A_REG_CONTROL1_INTM_SHIFT) +#define RT8973A_REG_CONTROL1_AUTO_CONFIG_MASK (0x1 << RT8973A_REG_CONTROL1_AUTO_CONFIG_SHIFT) +#define RT8973A_REG_CONTROL1_I2C_RST_EN_MASK (0x1 << RT8973A_REG_CONTROL1_I2C_RST_EN_SHIFT) +#define RT8973A_REG_CONTROL1_SWITCH_OPEN_MASK (0x1 << RT8973A_REG_CONTROL1_SWITCH_OPEN_SHIFT) +#define RT8973A_REG_CONTROL1_CHGTYP_MASK (0x1 << RT8973A_REG_CONTROL1_CHGTYP_SHIFT) +#define RT8973A_REG_CONTROL1_USB_CHD_EN_MASK (0x1 << RT8973A_REG_CONTROL1_USB_CHD_EN_SHIFT) +#define RT8973A_REG_CONTROL1_ADC_EN_MASK (0x1 << RT8973A_REG_CONTROL1_ADC_EN_SHIFT) + +#define RT9873A_REG_INTM1_ATTACH_SHIFT 0 +#define RT9873A_REG_INTM1_DETACH_SHIFT 1 +#define RT9873A_REG_INTM1_CHGDET_SHIFT 2 +#define RT9873A_REG_INTM1_DCD_T_SHIFT 3 +#define RT9873A_REG_INTM1_OVP_SHIFT 4 +#define RT9873A_REG_INTM1_CONNECT_SHIFT 5 +#define RT9873A_REG_INTM1_ADC_CHG_SHIFT 6 +#define RT9873A_REG_INTM1_OTP_SHIFT 7 +#define RT9873A_REG_INTM1_ATTACH_MASK (0x1 << RT9873A_REG_INTM1_ATTACH_SHIFT) +#define RT9873A_REG_INTM1_DETACH_MASK (0x1 << RT9873A_REG_INTM1_DETACH_SHIFT) +#define RT9873A_REG_INTM1_CHGDET_MASK (0x1 << RT9873A_REG_INTM1_CHGDET_SHIFT) +#define RT9873A_REG_INTM1_DCD_T_MASK (0x1 << RT9873A_REG_INTM1_DCD_T_SHIFT) +#define RT9873A_REG_INTM1_OVP_MASK (0x1 << RT9873A_REG_INTM1_OVP_SHIFT) +#define RT9873A_REG_INTM1_CONNECT_MASK (0x1 << RT9873A_REG_INTM1_CONNECT_SHIFT) +#define RT9873A_REG_INTM1_ADC_CHG_MASK (0x1 << RT9873A_REG_INTM1_ADC_CHG_SHIFT) +#define RT9873A_REG_INTM1_OTP_MASK (0x1 << RT9873A_REG_INTM1_OTP_SHIFT) + +#define RT9873A_REG_INTM2_UVLO_SHIFT 1 +#define RT9873A_REG_INTM2_POR_SHIFT 2 +#define RT9873A_REG_INTM2_OTP_FET_SHIFT 3 +#define RT9873A_REG_INTM2_OVP_FET_SHIFT 4 +#define RT9873A_REG_INTM2_OCP_LATCH_SHIFT 5 +#define RT9873A_REG_INTM2_OCP_SHIFT 6 +#define RT9873A_REG_INTM2_OVP_OCP_SHIFT 7 +#define RT9873A_REG_INTM2_UVLO_MASK (0x1 << RT9873A_REG_INTM2_UVLO_SHIFT) +#define RT9873A_REG_INTM2_POR_MASK (0x1 << RT9873A_REG_INTM2_POR_SHIFT) +#define RT9873A_REG_INTM2_OTP_FET_MASK (0x1 << RT9873A_REG_INTM2_OTP_FET_SHIFT) +#define RT9873A_REG_INTM2_OVP_FET_MASK (0x1 << RT9873A_REG_INTM2_OVP_FET_SHIFT) +#define RT9873A_REG_INTM2_OCP_LATCH_MASK (0x1 << RT9873A_REG_INTM2_OCP_LATCH_SHIFT) +#define RT9873A_REG_INTM2_OCP_MASK (0x1 << RT9873A_REG_INTM2_OCP_SHIFT) +#define RT9873A_REG_INTM2_OVP_OCP_MASK (0x1 << RT9873A_REG_INTM2_OVP_OCP_SHIFT) + +#define RT8973A_REG_ADC_SHIFT 0 +#define RT8973A_REG_ADC_MASK (0x1f << RT8973A_REG_ADC_SHIFT) + +#define RT8973A_REG_DEV1_OTG_SHIFT 0 +#define RT8973A_REG_DEV1_SDP_SHIFT 2 +#define RT8973A_REG_DEV1_UART_SHIFT 3 +#define RT8973A_REG_DEV1_CAR_KIT_TYPE1_SHIFT 4 +#define RT8973A_REG_DEV1_CDPORT_SHIFT 5 +#define RT8973A_REG_DEV1_DCPORT_SHIFT 6 +#define RT8973A_REG_DEV1_OTG_MASK (0x1 << RT8973A_REG_DEV1_OTG_SHIFT) +#define RT8973A_REG_DEV1_SDP_MASK (0x1 << RT8973A_REG_DEV1_SDP_SHIFT) +#define RT8973A_REG_DEV1_UART_MASK (0x1 << RT8973A_REG_DEV1_UART_SHIFT) +#define RT8973A_REG_DEV1_CAR_KIT_TYPE1_MASK (0x1 << RT8973A_REG_DEV1_CAR_KIT_TYPE1_SHIFT) +#define RT8973A_REG_DEV1_CDPORT_MASK (0x1 << RT8973A_REG_DEV1_CDPORT_SHIFT) +#define RT8973A_REG_DEV1_DCPORT_MASK (0x1 << RT8973A_REG_DEV1_DCPORT_SHIFT) +#define RT8973A_REG_DEV1_USB_MASK (RT8973A_REG_DEV1_SDP_MASK \ + | RT8973A_REG_DEV1_CDPORT_MASK) + +#define RT8973A_REG_DEV2_JIG_USB_ON_SHIFT 0 +#define RT8973A_REG_DEV2_JIG_USB_OFF_SHIFT 1 +#define RT8973A_REG_DEV2_JIG_UART_ON_SHIFT 2 +#define RT8973A_REG_DEV2_JIG_UART_OFF_SHIFT 3 +#define RT8973A_REG_DEV2_JIG_USB_ON_MASK (0x1 << RT8973A_REG_DEV2_JIG_USB_ON_SHIFT) +#define RT8973A_REG_DEV2_JIG_USB_OFF_MASK (0x1 << RT8973A_REG_DEV2_JIG_USB_OFF_SHIFT) +#define RT8973A_REG_DEV2_JIG_UART_ON_MASK (0x1 << RT8973A_REG_DEV2_JIG_UART_ON_SHIFT) +#define RT8973A_REG_DEV2_JIG_UART_OFF_MASK (0x1 << RT8973A_REG_DEV2_JIG_UART_OFF_SHIFT) + +#define RT8973A_REG_MANUAL_SW1_DP_SHIFT 2 +#define RT8973A_REG_MANUAL_SW1_DM_SHIFT 5 +#define RT8973A_REG_MANUAL_SW1_DP_MASK (0x7 << RT8973A_REG_MANUAL_SW1_DP_SHIFT) +#define RT8973A_REG_MANUAL_SW1_DM_MASK (0x7 << RT8973A_REG_MANUAL_SW1_DM_SHIFT) +#define DM_DP_CON_SWITCH_OPEN 0x0 +#define DM_DP_CON_SWITCH_USB 0x1 +#define DM_DP_CON_SWITCH_UART 0x3 +#define DM_DP_SWITCH_OPEN ((DM_DP_CON_SWITCH_OPEN << RT8973A_REG_MANUAL_SW1_DP_SHIFT) \ + | (DM_DP_CON_SWITCH_OPEN << RT8973A_REG_MANUAL_SW1_DM_SHIFT)) +#define DM_DP_SWITCH_USB ((DM_DP_CON_SWITCH_USB << RT8973A_REG_MANUAL_SW1_DP_SHIFT) \ + | (DM_DP_CON_SWITCH_USB << RT8973A_REG_MANUAL_SW1_DM_SHIFT)) +#define DM_DP_SWITCH_UART ((DM_DP_CON_SWITCH_UART << RT8973A_REG_MANUAL_SW1_DP_SHIFT) \ + | (DM_DP_CON_SWITCH_UART << RT8973A_REG_MANUAL_SW1_DM_SHIFT)) + +#define RT8973A_REG_MANUAL_SW2_FET_ON_SHIFT 0 +#define RT8973A_REG_MANUAL_SW2_JIG_ON_SHIFT 2 +#define RT8973A_REG_MANUAL_SW2_BOOT_SW_SHIFT 3 +#define RT8973A_REG_MANUAL_SW2_FET_ON_MASK (0x1 << RT8973A_REG_MANUAL_SW2_FET_ON_SHIFT) +#define RT8973A_REG_MANUAL_SW2_JIG_ON_MASK (0x1 << RT8973A_REG_MANUAL_SW2_JIG_ON_SHIFT) +#define RT8973A_REG_MANUAL_SW2_BOOT_SW_MASK (0x1 << RT8973A_REG_MANUAL_SW2_BOOT_SW_SHIFT) +#define RT8973A_REG_MANUAL_SW2_FET_ON 0 +#define RT8973A_REG_MANUAL_SW2_FET_OFF 0x1 +#define RT8973A_REG_MANUAL_SW2_JIG_OFF 0 +#define RT8973A_REG_MANUAL_SW2_JIG_ON 0x1 +#define RT8973A_REG_MANUAL_SW2_BOOT_SW_ON 0 +#define RT8973A_REG_MANUAL_SW2_BOOT_SW_OFF 0x1 + +#define RT8973A_REG_RESET_SHIFT 0 +#define RT8973A_REG_RESET_MASK (0x1 << RT8973A_REG_RESET_SHIFT) +#define RT8973A_REG_RESET 0x1 + +/* RT8973A Interrupts */ +enum rt8973a_irq { + /* Interrupt1*/ + RT8973A_INT1_ATTACH, + RT8973A_INT1_DETACH, + RT8973A_INT1_CHGDET, + RT8973A_INT1_DCD_T, + RT8973A_INT1_OVP, + RT8973A_INT1_CONNECT, + RT8973A_INT1_ADC_CHG, + RT8973A_INT1_OTP, + + /* Interrupt2*/ + RT8973A_INT2_UVLO, + RT8973A_INT2_POR, + RT8973A_INT2_OTP_FET, + RT8973A_INT2_OVP_FET, + RT8973A_INT2_OCP_LATCH, + RT8973A_INT2_OCP, + RT8973A_INT2_OVP_OCP, + + RT8973A_NUM, +}; + +#define RT8973A_INT1_ATTACH_MASK BIT(0) +#define RT8973A_INT1_DETACH_MASK BIT(1) +#define RT8973A_INT1_CHGDET_MASK BIT(2) +#define RT8973A_INT1_DCD_T_MASK BIT(3) +#define RT8973A_INT1_OVP_MASK BIT(4) +#define RT8973A_INT1_CONNECT_MASK BIT(5) +#define RT8973A_INT1_ADC_CHG_MASK BIT(6) +#define RT8973A_INT1_OTP_MASK BIT(7) +#define RT8973A_INT2_UVLOT_MASK BIT(0) +#define RT8973A_INT2_POR_MASK BIT(1) +#define RT8973A_INT2_OTP_FET_MASK BIT(2) +#define RT8973A_INT2_OVP_FET_MASK BIT(3) +#define RT8973A_INT2_OCP_LATCH_MASK BIT(4) +#define RT8973A_INT2_OCP_MASK BIT(5) +#define RT8973A_INT2_OVP_OCP_MASK BIT(6) + +#endif /* __LINUX_EXTCON_RT8973A_H */ diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index 560d7dccec7b..b0f7bd82af90 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -8,16 +8,10 @@ * 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. - * - * 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 <linux/err.h> #include <linux/i2c.h> -#include <linux/input.h> #include <linux/interrupt.h> #include <linux/irqdomain.h> #include <linux/kernel.h> @@ -26,7 +20,8 @@ #include <linux/regmap.h> #include <linux/slab.h> #include <linux/extcon.h> -#include <linux/extcon/sm5502.h> + +#include "extcon-sm5502.h" #define DELAY_MS_DEFAULT 17000 /* unit: millisecond */ @@ -300,7 +295,7 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info) * If ADC is SM5502_MUIC_ADC_GROUND(0x0), external cable hasn't * connected with to MUIC device. */ - cable_type &= SM5502_REG_ADC_MASK; + cable_type = adc & SM5502_REG_ADC_MASK; if (cable_type == SM5502_MUIC_ADC_GROUND) return SM5502_MUIC_ADC_GROUND; @@ -395,7 +390,7 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, /* Get the type of attached or detached cable */ if (attached) cable_type = sm5502_muic_get_cable_type(info); - else if (!attached) + else cable_type = prev_cable_type; prev_cable_type = cable_type; @@ -457,8 +452,6 @@ static void sm5502_muic_irq_work(struct work_struct *work) dev_err(info->dev, "failed to handle MUIC interrupt\n"); mutex_unlock(&info->mutex); - - return; } /* @@ -617,8 +610,9 @@ static int sm5022_muic_i2c_probe(struct i2c_client *i2c, IRQF_NO_SUSPEND, muic_irq->name, info); if (ret) { - dev_err(info->dev, "failed: irq request (IRQ: %d," - " error :%d)\n", muic_irq->irq, ret); + dev_err(info->dev, + "failed: irq request (IRQ: %d, error :%d)\n", + muic_irq->irq, ret); return ret; } } diff --git a/drivers/extcon/extcon-sm5502.h b/drivers/extcon/extcon-sm5502.h new file mode 100644 index 000000000000..974b53222f56 --- /dev/null +++ b/drivers/extcon/extcon-sm5502.h @@ -0,0 +1,282 @@ +/* + * sm5502.h + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * + * 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_EXTCON_SM5502_H +#define __LINUX_EXTCON_SM5502_H + +enum sm5502_types { + TYPE_SM5502, +}; + +/* SM5502 registers */ +enum sm5502_reg { + SM5502_REG_DEVICE_ID = 0x01, + SM5502_REG_CONTROL, + SM5502_REG_INT1, + SM5502_REG_INT2, + SM5502_REG_INTMASK1, + SM5502_REG_INTMASK2, + SM5502_REG_ADC, + SM5502_REG_TIMING_SET1, + SM5502_REG_TIMING_SET2, + SM5502_REG_DEV_TYPE1, + SM5502_REG_DEV_TYPE2, + SM5502_REG_BUTTON1, + SM5502_REG_BUTTON2, + SM5502_REG_CAR_KIT_STATUS, + SM5502_REG_RSVD1, + SM5502_REG_RSVD2, + SM5502_REG_RSVD3, + SM5502_REG_RSVD4, + SM5502_REG_MANUAL_SW1, + SM5502_REG_MANUAL_SW2, + SM5502_REG_DEV_TYPE3, + SM5502_REG_RSVD5, + SM5502_REG_RSVD6, + SM5502_REG_RSVD7, + SM5502_REG_RSVD8, + SM5502_REG_RSVD9, + SM5502_REG_RESET, + SM5502_REG_RSVD10, + SM5502_REG_RESERVED_ID1, + SM5502_REG_RSVD11, + SM5502_REG_RSVD12, + SM5502_REG_RESERVED_ID2, + SM5502_REG_RSVD13, + SM5502_REG_OCP, + SM5502_REG_RSVD14, + SM5502_REG_RSVD15, + SM5502_REG_RSVD16, + SM5502_REG_RSVD17, + SM5502_REG_RSVD18, + SM5502_REG_RSVD19, + SM5502_REG_RSVD20, + SM5502_REG_RSVD21, + SM5502_REG_RSVD22, + SM5502_REG_RSVD23, + SM5502_REG_RSVD24, + SM5502_REG_RSVD25, + SM5502_REG_RSVD26, + SM5502_REG_RSVD27, + SM5502_REG_RSVD28, + SM5502_REG_RSVD29, + SM5502_REG_RSVD30, + SM5502_REG_RSVD31, + SM5502_REG_RSVD32, + SM5502_REG_RSVD33, + SM5502_REG_RSVD34, + SM5502_REG_RSVD35, + SM5502_REG_RSVD36, + SM5502_REG_RESERVED_ID3, + + SM5502_REG_END, +}; + +/* Define SM5502 MASK/SHIFT constant */ +#define SM5502_REG_DEVICE_ID_VENDOR_SHIFT 0 +#define SM5502_REG_DEVICE_ID_VERSION_SHIFT 3 +#define SM5502_REG_DEVICE_ID_VENDOR_MASK (0x3 << SM5502_REG_DEVICE_ID_VENDOR_SHIFT) +#define SM5502_REG_DEVICE_ID_VERSION_MASK (0x1f << SM5502_REG_DEVICE_ID_VERSION_SHIFT) + +#define SM5502_REG_CONTROL_MASK_INT_SHIFT 0 +#define SM5502_REG_CONTROL_WAIT_SHIFT 1 +#define SM5502_REG_CONTROL_MANUAL_SW_SHIFT 2 +#define SM5502_REG_CONTROL_RAW_DATA_SHIFT 3 +#define SM5502_REG_CONTROL_SW_OPEN_SHIFT 4 +#define SM5502_REG_CONTROL_MASK_INT_MASK (0x1 << SM5502_REG_CONTROL_MASK_INT_SHIFT) +#define SM5502_REG_CONTROL_WAIT_MASK (0x1 << SM5502_REG_CONTROL_WAIT_SHIFT) +#define SM5502_REG_CONTROL_MANUAL_SW_MASK (0x1 << SM5502_REG_CONTROL_MANUAL_SW_SHIFT) +#define SM5502_REG_CONTROL_RAW_DATA_MASK (0x1 << SM5502_REG_CONTROL_RAW_DATA_SHIFT) +#define SM5502_REG_CONTROL_SW_OPEN_MASK (0x1 << SM5502_REG_CONTROL_SW_OPEN_SHIFT) + +#define SM5502_REG_INTM1_ATTACH_SHIFT 0 +#define SM5502_REG_INTM1_DETACH_SHIFT 1 +#define SM5502_REG_INTM1_KP_SHIFT 2 +#define SM5502_REG_INTM1_LKP_SHIFT 3 +#define SM5502_REG_INTM1_LKR_SHIFT 4 +#define SM5502_REG_INTM1_OVP_EVENT_SHIFT 5 +#define SM5502_REG_INTM1_OCP_EVENT_SHIFT 6 +#define SM5502_REG_INTM1_OVP_OCP_DIS_SHIFT 7 +#define SM5502_REG_INTM1_ATTACH_MASK (0x1 << SM5502_REG_INTM1_ATTACH_SHIFT) +#define SM5502_REG_INTM1_DETACH_MASK (0x1 << SM5502_REG_INTM1_DETACH_SHIFT) +#define SM5502_REG_INTM1_KP_MASK (0x1 << SM5502_REG_INTM1_KP_SHIFT) +#define SM5502_REG_INTM1_LKP_MASK (0x1 << SM5502_REG_INTM1_LKP_SHIFT) +#define SM5502_REG_INTM1_LKR_MASK (0x1 << SM5502_REG_INTM1_LKR_SHIFT) +#define SM5502_REG_INTM1_OVP_EVENT_MASK (0x1 << SM5502_REG_INTM1_OVP_EVENT_SHIFT) +#define SM5502_REG_INTM1_OCP_EVENT_MASK (0x1 << SM5502_REG_INTM1_OCP_EVENT_SHIFT) +#define SM5502_REG_INTM1_OVP_OCP_DIS_MASK (0x1 << SM5502_REG_INTM1_OVP_OCP_DIS_SHIFT) + +#define SM5502_REG_INTM2_VBUS_DET_SHIFT 0 +#define SM5502_REG_INTM2_REV_ACCE_SHIFT 1 +#define SM5502_REG_INTM2_ADC_CHG_SHIFT 2 +#define SM5502_REG_INTM2_STUCK_KEY_SHIFT 3 +#define SM5502_REG_INTM2_STUCK_KEY_RCV_SHIFT 4 +#define SM5502_REG_INTM2_MHL_SHIFT 5 +#define SM5502_REG_INTM2_VBUS_DET_MASK (0x1 << SM5502_REG_INTM2_VBUS_DET_SHIFT) +#define SM5502_REG_INTM2_REV_ACCE_MASK (0x1 << SM5502_REG_INTM2_REV_ACCE_SHIFT) +#define SM5502_REG_INTM2_ADC_CHG_MASK (0x1 << SM5502_REG_INTM2_ADC_CHG_SHIFT) +#define SM5502_REG_INTM2_STUCK_KEY_MASK (0x1 << SM5502_REG_INTM2_STUCK_KEY_SHIFT) +#define SM5502_REG_INTM2_STUCK_KEY_RCV_MASK (0x1 << SM5502_REG_INTM2_STUCK_KEY_RCV_SHIFT) +#define SM5502_REG_INTM2_MHL_MASK (0x1 << SM5502_REG_INTM2_MHL_SHIFT) + +#define SM5502_REG_ADC_SHIFT 0 +#define SM5502_REG_ADC_MASK (0x1f << SM5502_REG_ADC_SHIFT) + +#define SM5502_REG_TIMING_SET1_KEY_PRESS_SHIFT 4 +#define SM5502_REG_TIMING_SET1_KEY_PRESS_MASK (0xf << SM5502_REG_TIMING_SET1_KEY_PRESS_SHIFT) +#define TIMING_KEY_PRESS_100MS 0x0 +#define TIMING_KEY_PRESS_200MS 0x1 +#define TIMING_KEY_PRESS_300MS 0x2 +#define TIMING_KEY_PRESS_400MS 0x3 +#define TIMING_KEY_PRESS_500MS 0x4 +#define TIMING_KEY_PRESS_600MS 0x5 +#define TIMING_KEY_PRESS_700MS 0x6 +#define TIMING_KEY_PRESS_800MS 0x7 +#define TIMING_KEY_PRESS_900MS 0x8 +#define TIMING_KEY_PRESS_1000MS 0x9 +#define SM5502_REG_TIMING_SET1_ADC_DET_SHIFT 0 +#define SM5502_REG_TIMING_SET1_ADC_DET_MASK (0xf << SM5502_REG_TIMING_SET1_ADC_DET_SHIFT) +#define TIMING_ADC_DET_50MS 0x0 +#define TIMING_ADC_DET_100MS 0x1 +#define TIMING_ADC_DET_150MS 0x2 +#define TIMING_ADC_DET_200MS 0x3 +#define TIMING_ADC_DET_300MS 0x4 +#define TIMING_ADC_DET_400MS 0x5 +#define TIMING_ADC_DET_500MS 0x6 +#define TIMING_ADC_DET_600MS 0x7 +#define TIMING_ADC_DET_700MS 0x8 +#define TIMING_ADC_DET_800MS 0x9 +#define TIMING_ADC_DET_900MS 0xA +#define TIMING_ADC_DET_1000MS 0xB + +#define SM5502_REG_TIMING_SET2_SW_WAIT_SHIFT 4 +#define SM5502_REG_TIMING_SET2_SW_WAIT_MASK (0xf << SM5502_REG_TIMING_SET2_SW_WAIT_SHIFT) +#define TIMING_SW_WAIT_10MS 0x0 +#define TIMING_SW_WAIT_30MS 0x1 +#define TIMING_SW_WAIT_50MS 0x2 +#define TIMING_SW_WAIT_70MS 0x3 +#define TIMING_SW_WAIT_90MS 0x4 +#define TIMING_SW_WAIT_110MS 0x5 +#define TIMING_SW_WAIT_130MS 0x6 +#define TIMING_SW_WAIT_150MS 0x7 +#define TIMING_SW_WAIT_170MS 0x8 +#define TIMING_SW_WAIT_190MS 0x9 +#define TIMING_SW_WAIT_210MS 0xA +#define SM5502_REG_TIMING_SET2_LONG_KEY_SHIFT 0 +#define SM5502_REG_TIMING_SET2_LONG_KEY_MASK (0xf << SM5502_REG_TIMING_SET2_LONG_KEY_SHIFT) +#define TIMING_LONG_KEY_300MS 0x0 +#define TIMING_LONG_KEY_400MS 0x1 +#define TIMING_LONG_KEY_500MS 0x2 +#define TIMING_LONG_KEY_600MS 0x3 +#define TIMING_LONG_KEY_700MS 0x4 +#define TIMING_LONG_KEY_800MS 0x5 +#define TIMING_LONG_KEY_900MS 0x6 +#define TIMING_LONG_KEY_1000MS 0x7 +#define TIMING_LONG_KEY_1100MS 0x8 +#define TIMING_LONG_KEY_1200MS 0x9 +#define TIMING_LONG_KEY_1300MS 0xA +#define TIMING_LONG_KEY_1400MS 0xB +#define TIMING_LONG_KEY_1500MS 0xC + +#define SM5502_REG_DEV_TYPE1_AUDIO_TYPE1_SHIFT 0 +#define SM5502_REG_DEV_TYPE1_AUDIO_TYPE2_SHIFT 1 +#define SM5502_REG_DEV_TYPE1_USB_SDP_SHIFT 2 +#define SM5502_REG_DEV_TYPE1_UART_SHIFT 3 +#define SM5502_REG_DEV_TYPE1_CAR_KIT_CHARGER_SHIFT 4 +#define SM5502_REG_DEV_TYPE1_USB_CHG_SHIFT 5 +#define SM5502_REG_DEV_TYPE1_DEDICATED_CHG_SHIFT 6 +#define SM5502_REG_DEV_TYPE1_USB_OTG_SHIFT 7 +#define SM5502_REG_DEV_TYPE1_AUDIO_TYPE1_MASK (0x1 << SM5502_REG_DEV_TYPE1_AUDIO_TYPE1_SHIFT) +#define SM5502_REG_DEV_TYPE1_AUDIO_TYPE1__MASK (0x1 << SM5502_REG_DEV_TYPE1_AUDIO_TYPE2_SHIFT) +#define SM5502_REG_DEV_TYPE1_USB_SDP_MASK (0x1 << SM5502_REG_DEV_TYPE1_USB_SDP_SHIFT) +#define SM5502_REG_DEV_TYPE1_UART_MASK (0x1 << SM5502_REG_DEV_TYPE1_UART_SHIFT) +#define SM5502_REG_DEV_TYPE1_CAR_KIT_CHARGER_MASK (0x1 << SM5502_REG_DEV_TYPE1_CAR_KIT_CHARGER_SHIFT) +#define SM5502_REG_DEV_TYPE1_USB_CHG_MASK (0x1 << SM5502_REG_DEV_TYPE1_USB_CHG_SHIFT) +#define SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK (0x1 << SM5502_REG_DEV_TYPE1_DEDICATED_CHG_SHIFT) +#define SM5502_REG_DEV_TYPE1_USB_OTG_MASK (0x1 << SM5502_REG_DEV_TYPE1_USB_OTG_SHIFT) + +#define SM5502_REG_DEV_TYPE2_JIG_USB_ON_SHIFT 0 +#define SM5502_REG_DEV_TYPE2_JIG_USB_OFF_SHIFT 1 +#define SM5502_REG_DEV_TYPE2_JIG_UART_ON_SHIFT 2 +#define SM5502_REG_DEV_TYPE2_JIG_UART_OFF_SHIFT 3 +#define SM5502_REG_DEV_TYPE2_PPD_SHIFT 4 +#define SM5502_REG_DEV_TYPE2_TTY_SHIFT 5 +#define SM5502_REG_DEV_TYPE2_AV_CABLE_SHIFT 6 +#define SM5502_REG_DEV_TYPE2_JIG_USB_ON_MASK (0x1 << SM5502_REG_DEV_TYPE2_JIG_USB_ON_SHIFT) +#define SM5502_REG_DEV_TYPE2_JIG_USB_OFF_MASK (0x1 << SM5502_REG_DEV_TYPE2_JIG_USB_OFF_SHIFT) +#define SM5502_REG_DEV_TYPE2_JIG_UART_ON_MASK (0x1 << SM5502_REG_DEV_TYPE2_JIG_UART_ON_SHIFT) +#define SM5502_REG_DEV_TYPE2_JIG_UART_OFF_MASK (0x1 << SM5502_REG_DEV_TYPE2_JIG_UART_OFF_SHIFT) +#define SM5502_REG_DEV_TYPE2_PPD_MASK (0x1 << SM5502_REG_DEV_TYPE2_PPD_SHIFT) +#define SM5502_REG_DEV_TYPE2_TTY_MASK (0x1 << SM5502_REG_DEV_TYPE2_TTY_SHIFT) +#define SM5502_REG_DEV_TYPE2_AV_CABLE_MASK (0x1 << SM5502_REG_DEV_TYPE2_AV_CABLE_SHIFT) + +#define SM5502_REG_MANUAL_SW1_VBUSIN_SHIFT 0 +#define SM5502_REG_MANUAL_SW1_DP_SHIFT 2 +#define SM5502_REG_MANUAL_SW1_DM_SHIFT 5 +#define SM5502_REG_MANUAL_SW1_VBUSIN_MASK (0x3 << SM5502_REG_MANUAL_SW1_VBUSIN_SHIFT) +#define SM5502_REG_MANUAL_SW1_DP_MASK (0x7 << SM5502_REG_MANUAL_SW1_DP_SHIFT) +#define SM5502_REG_MANUAL_SW1_DM_MASK (0x7 << SM5502_REG_MANUAL_SW1_DM_SHIFT) +#define VBUSIN_SWITCH_OPEN 0x0 +#define VBUSIN_SWITCH_VBUSOUT 0x1 +#define VBUSIN_SWITCH_MIC 0x2 +#define VBUSIN_SWITCH_VBUSOUT_WITH_USB 0x3 +#define DM_DP_CON_SWITCH_OPEN 0x0 +#define DM_DP_CON_SWITCH_USB 0x1 +#define DM_DP_CON_SWITCH_AUDIO 0x2 +#define DM_DP_CON_SWITCH_UART 0x3 +#define DM_DP_SWITCH_OPEN ((DM_DP_CON_SWITCH_OPEN <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \ + | (DM_DP_CON_SWITCH_OPEN <<SM5502_REG_MANUAL_SW1_DM_SHIFT)) +#define DM_DP_SWITCH_USB ((DM_DP_CON_SWITCH_USB <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \ + | (DM_DP_CON_SWITCH_USB <<SM5502_REG_MANUAL_SW1_DM_SHIFT)) +#define DM_DP_SWITCH_AUDIO ((DM_DP_CON_SWITCH_AUDIO <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \ + | (DM_DP_CON_SWITCH_AUDIO <<SM5502_REG_MANUAL_SW1_DM_SHIFT)) +#define DM_DP_SWITCH_UART ((DM_DP_CON_SWITCH_UART <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \ + | (DM_DP_CON_SWITCH_UART <<SM5502_REG_MANUAL_SW1_DM_SHIFT)) + +/* SM5502 Interrupts */ +enum sm5502_irq { + /* INT1 */ + SM5502_IRQ_INT1_ATTACH, + SM5502_IRQ_INT1_DETACH, + SM5502_IRQ_INT1_KP, + SM5502_IRQ_INT1_LKP, + SM5502_IRQ_INT1_LKR, + SM5502_IRQ_INT1_OVP_EVENT, + SM5502_IRQ_INT1_OCP_EVENT, + SM5502_IRQ_INT1_OVP_OCP_DIS, + + /* INT2 */ + SM5502_IRQ_INT2_VBUS_DET, + SM5502_IRQ_INT2_REV_ACCE, + SM5502_IRQ_INT2_ADC_CHG, + SM5502_IRQ_INT2_STUCK_KEY, + SM5502_IRQ_INT2_STUCK_KEY_RCV, + SM5502_IRQ_INT2_MHL, + + SM5502_IRQ_NUM, +}; + +#define SM5502_IRQ_INT1_ATTACH_MASK BIT(0) +#define SM5502_IRQ_INT1_DETACH_MASK BIT(1) +#define SM5502_IRQ_INT1_KP_MASK BIT(2) +#define SM5502_IRQ_INT1_LKP_MASK BIT(3) +#define SM5502_IRQ_INT1_LKR_MASK BIT(4) +#define SM5502_IRQ_INT1_OVP_EVENT_MASK BIT(5) +#define SM5502_IRQ_INT1_OCP_EVENT_MASK BIT(6) +#define SM5502_IRQ_INT1_OVP_OCP_DIS_MASK BIT(7) +#define SM5502_IRQ_INT2_VBUS_DET_MASK BIT(0) +#define SM5502_IRQ_INT2_REV_ACCE_MASK BIT(1) +#define SM5502_IRQ_INT2_ADC_CHG_MASK BIT(2) +#define SM5502_IRQ_INT2_STUCK_KEY_MASK BIT(3) +#define SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK BIT(4) +#define SM5502_IRQ_INT2_MHL_MASK BIT(5) + +#endif /* __LINUX_EXTCON_SM5502_H */ diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 531a593912ec..433f72a1c006 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -165,8 +165,10 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, ret = vmbus_post_msg(open_msg, sizeof(struct vmbus_channel_open_channel)); - if (ret != 0) + if (ret != 0) { + err = ret; goto error1; + } t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ); if (t == 0) { @@ -363,7 +365,6 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, u32 next_gpadl_handle; unsigned long flags; int ret = 0; - int t; next_gpadl_handle = atomic_read(&vmbus_connection.next_gpadl_handle); atomic_inc(&vmbus_connection.next_gpadl_handle); @@ -410,9 +411,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, } } - t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); - BUG_ON(t == 0); - + wait_for_completion(&msginfo->waitevent); /* At this point, we received the gpadl created msg */ *gpadl_handle = gpadlmsg->gpadl; @@ -435,7 +434,7 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) struct vmbus_channel_gpadl_teardown *msg; struct vmbus_channel_msginfo *info; unsigned long flags; - int ret, t; + int ret; info = kmalloc(sizeof(*info) + sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL); @@ -457,11 +456,12 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_gpadl_teardown)); - BUG_ON(ret != 0); - t = wait_for_completion_timeout(&info->waitevent, 5*HZ); - BUG_ON(t == 0); + if (ret) + goto post_msg_err; + + wait_for_completion(&info->waitevent); - /* Received a torndown response */ +post_msg_err: spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); list_del(&info->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); @@ -478,7 +478,7 @@ static void reset_channel_cb(void *arg) channel->onchannel_callback = NULL; } -static void vmbus_close_internal(struct vmbus_channel *channel) +static int vmbus_close_internal(struct vmbus_channel *channel) { struct vmbus_channel_close_channel *msg; int ret; @@ -486,11 +486,14 @@ static void vmbus_close_internal(struct vmbus_channel *channel) channel->state = CHANNEL_OPEN_STATE; channel->sc_creation_callback = NULL; /* Stop callback and cancel the timer asap */ - if (channel->target_cpu != smp_processor_id()) + if (channel->target_cpu != get_cpu()) { + put_cpu(); smp_call_function_single(channel->target_cpu, reset_channel_cb, channel, true); - else + } else { reset_channel_cb(channel); + put_cpu(); + } /* Send a closing message */ @@ -501,11 +504,28 @@ static void vmbus_close_internal(struct vmbus_channel *channel) ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel)); - BUG_ON(ret != 0); + if (ret) { + pr_err("Close failed: close post msg return is %d\n", ret); + /* + * If we failed to post the close msg, + * it is perhaps better to leak memory. + */ + return ret; + } + /* Tear down the gpadl for the channel's ring buffer */ - if (channel->ringbuffer_gpadlhandle) - vmbus_teardown_gpadl(channel, - channel->ringbuffer_gpadlhandle); + if (channel->ringbuffer_gpadlhandle) { + ret = vmbus_teardown_gpadl(channel, + channel->ringbuffer_gpadlhandle); + if (ret) { + pr_err("Close failed: teardown gpadl return %d\n", ret); + /* + * If we failed to teardown gpadl, + * it is perhaps better to leak memory. + */ + return ret; + } + } /* Cleanup the ring buffers for this channel */ hv_ringbuffer_cleanup(&channel->outbound); @@ -514,7 +534,7 @@ static void vmbus_close_internal(struct vmbus_channel *channel) free_pages((unsigned long)channel->ringbuffer_pages, get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); - + return ret; } /* diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index ed9350d42764..a2d1a9612c86 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -224,11 +224,14 @@ static void vmbus_process_rescind_offer(struct work_struct *work) msg.header.msgtype = CHANNELMSG_RELID_RELEASED; vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); - if (channel->target_cpu != smp_processor_id()) + if (channel->target_cpu != get_cpu()) { + put_cpu(); smp_call_function_single(channel->target_cpu, percpu_channel_deq, channel, true); - else + } else { percpu_channel_deq(channel); + put_cpu(); + } if (channel->primary_channel == NULL) { spin_lock_irqsave(&vmbus_connection.channel_lock, flags); @@ -294,12 +297,15 @@ static void vmbus_process_offer(struct work_struct *work) spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); if (enq) { - if (newchannel->target_cpu != smp_processor_id()) + if (newchannel->target_cpu != get_cpu()) { + put_cpu(); smp_call_function_single(newchannel->target_cpu, percpu_channel_enq, newchannel, true); - else + } else { percpu_channel_enq(newchannel); + put_cpu(); + } } if (!fnew) { /* @@ -314,12 +320,15 @@ static void vmbus_process_offer(struct work_struct *work) list_add_tail(&newchannel->sc_list, &channel->sc_list); spin_unlock_irqrestore(&channel->sc_lock, flags); - if (newchannel->target_cpu != smp_processor_id()) + if (newchannel->target_cpu != get_cpu()) { + put_cpu(); smp_call_function_single(newchannel->target_cpu, percpu_channel_enq, newchannel, true); - else + } else { percpu_channel_enq(newchannel); + put_cpu(); + } newchannel->state = CHANNEL_OPEN_STATE; if (channel->sc_creation_callback != NULL) diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index ae22e3c1fc4c..e206619b946e 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -427,10 +427,21 @@ int vmbus_post_msg(void *buffer, size_t buflen) * insufficient resources. Retry the operation a couple of * times before giving up. */ - while (retries < 3) { - ret = hv_post_message(conn_id, 1, buffer, buflen); - if (ret != HV_STATUS_INSUFFICIENT_BUFFERS) + while (retries < 10) { + ret = hv_post_message(conn_id, 1, buffer, buflen); + + switch (ret) { + case HV_STATUS_INSUFFICIENT_BUFFERS: + ret = -ENOMEM; + case -ENOMEM: + break; + case HV_STATUS_SUCCESS: return ret; + default: + pr_err("hv_post_msg() failed; error code:%d\n", ret); + return -EINVAL; + } + retries++; msleep(100); } diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index edfc8488cb03..3e4235c7a47f 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -138,6 +138,8 @@ int hv_init(void) memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); memset(hv_context.synic_message_page, 0, sizeof(void *) * NR_CPUS); + memset(hv_context.post_msg_page, 0, + sizeof(void *) * NR_CPUS); memset(hv_context.vp_index, 0, sizeof(int) * NR_CPUS); memset(hv_context.event_dpc, 0, @@ -217,26 +219,18 @@ int hv_post_message(union hv_connection_id connection_id, enum hv_message_type message_type, void *payload, size_t payload_size) { - struct aligned_input { - u64 alignment8; - struct hv_input_post_message msg; - }; struct hv_input_post_message *aligned_msg; u16 status; - unsigned long addr; if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) return -EMSGSIZE; - addr = (unsigned long)kmalloc(sizeof(struct aligned_input), GFP_ATOMIC); - if (!addr) - return -ENOMEM; - aligned_msg = (struct hv_input_post_message *) - (ALIGN(addr, HV_HYPERCALL_PARAM_ALIGN)); + hv_context.post_msg_page[get_cpu()]; aligned_msg->connectionid = connection_id; + aligned_msg->reserved = 0; aligned_msg->message_type = message_type; aligned_msg->payload_size = payload_size; memcpy((void *)aligned_msg->payload, payload, payload_size); @@ -244,8 +238,7 @@ int hv_post_message(union hv_connection_id connection_id, status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL) & 0xFFFF; - kfree((void *)addr); - + put_cpu(); return status; } @@ -294,6 +287,14 @@ int hv_synic_alloc(void) pr_err("Unable to allocate SYNIC event page\n"); goto err; } + + hv_context.post_msg_page[cpu] = + (void *)get_zeroed_page(GFP_ATOMIC); + + if (hv_context.post_msg_page[cpu] == NULL) { + pr_err("Unable to allocate post msg page\n"); + goto err; + } } return 0; @@ -308,6 +309,8 @@ static void hv_synic_free_cpu(int cpu) free_page((unsigned long)hv_context.synic_event_page[cpu]); if (hv_context.synic_message_page[cpu]) free_page((unsigned long)hv_context.synic_message_page[cpu]); + if (hv_context.post_msg_page[cpu]) + free_page((unsigned long)hv_context.post_msg_page[cpu]); } void hv_synic_free(void) diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 22b750749a39..c386d8dc7223 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -515,6 +515,10 @@ struct hv_context { * per-cpu list of the channels based on their CPU affinity. */ struct list_head percpu_list[NR_CPUS]; + /* + * buffer to post messages to the host. + */ + void *post_msg_page[NR_CPUS]; }; extern struct hv_context hv_context; diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 15db66b74141..6361d124f67d 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -361,6 +361,11 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, ring_info->ring_buffer->read_index = ring_info->ring_buffer->write_index = 0; + /* + * Set the feature bit for enabling flow control. + */ + ring_info->ring_buffer->feature_bits.value = 1; + ring_info->ring_size = buflen; ring_info->ring_datasize = buflen - sizeof(struct hv_ring_buffer); diff --git a/drivers/ipack/carriers/tpci200.c b/drivers/ipack/carriers/tpci200.c index de5e32151a1e..9b23843dcad4 100644 --- a/drivers/ipack/carriers/tpci200.c +++ b/drivers/ipack/carriers/tpci200.c @@ -572,7 +572,8 @@ static int tpci200_pci_probe(struct pci_dev *pdev, /* Register the carrier in the industry pack bus driver */ tpci200->info->ipack_bus = ipack_bus_register(&pdev->dev, TPCI200_NB_SLOT, - &tpci200_bus_ops); + &tpci200_bus_ops, + THIS_MODULE); if (!tpci200->info->ipack_bus) { dev_err(&pdev->dev, "error registering the carrier on ipack driver\n"); diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index e41bef048c23..035d5449227e 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c @@ -55,6 +55,22 @@ struct ipoctal { u8 __iomem *int_space; }; +static inline struct ipoctal *chan_to_ipoctal(struct ipoctal_channel *chan, + unsigned int index) +{ + return container_of(chan, struct ipoctal, channel[index]); +} + +static void ipoctal_reset_channel(struct ipoctal_channel *channel) +{ + iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); + channel->rx_enable = 0; + iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); + iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr); +} + static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty) { struct ipoctal_channel *channel; @@ -72,12 +88,20 @@ static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty) static int ipoctal_open(struct tty_struct *tty, struct file *file) { - struct ipoctal_channel *channel; + struct ipoctal_channel *channel = dev_get_drvdata(tty->dev); + struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); + int err; - channel = dev_get_drvdata(tty->dev); tty->driver_data = channel; - return tty_port_open(&channel->tty_port, tty, file); + if (!ipack_get_carrier(ipoctal->dev)) + return -EBUSY; + + err = tty_port_open(&channel->tty_port, tty, file); + if (err) + ipack_put_carrier(ipoctal->dev); + + return err; } static void ipoctal_reset_stats(struct ipoctal_stats *stats) @@ -151,7 +175,6 @@ static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr) flag = TTY_FRAME; } if (sr & SR_RECEIVED_BREAK) { - iowrite8(CR_CMD_RESET_BREAK_CHANGE, &channel->regs->w.cr); channel->stats.rcv_break++; flag = TTY_BREAK; } @@ -196,6 +219,9 @@ static void ipoctal_irq_channel(struct ipoctal_channel *channel) isr = ioread8(&channel->block_regs->r.isr); sr = ioread8(&channel->regs->r.sr); + if (isr & (IMR_DELTA_BREAK_A | IMR_DELTA_BREAK_B)) + iowrite8(CR_CMD_RESET_BREAK_CHANGE, &channel->regs->w.cr); + if ((sr & SR_TX_EMPTY) && (channel->nb_bytes == 0)) { iowrite8(CR_DISABLE_TX, &channel->regs->w.cr); /* In case of RS-485, change from TX to RX when finishing TX. @@ -304,10 +330,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_A; } - iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); - channel->rx_enable = 0; - iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); + ipoctal_reset_channel(channel); iowrite8(MR1_CHRL_8_BITS | MR1_ERROR_CHAR | MR1_RxINT_RxRDY, &channel->regs->w.mr); /* mr1 */ iowrite8(0, &channel->regs->w.mr); /* mr2 */ @@ -467,11 +490,7 @@ static void ipoctal_set_termios(struct tty_struct *tty, cflag = tty->termios.c_cflag; /* Disable and reset everything before change the setup */ - iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr); + ipoctal_reset_channel(channel); /* Set Bits per chars */ switch (cflag & CSIZE) { @@ -609,12 +628,7 @@ static void ipoctal_hangup(struct tty_struct *tty) tty_port_hangup(&channel->tty_port); - iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); - channel->rx_enable = 0; - iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr); + ipoctal_reset_channel(channel); clear_bit(ASYNCB_INITIALIZED, &channel->tty_port.flags); wake_up_interruptible(&channel->tty_port.open_wait); @@ -627,15 +641,19 @@ static void ipoctal_shutdown(struct tty_struct *tty) if (channel == NULL) return; - iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr); - channel->rx_enable = 0; - iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr); - iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr); + ipoctal_reset_channel(channel); clear_bit(ASYNCB_INITIALIZED, &channel->tty_port.flags); } +static void ipoctal_cleanup(struct tty_struct *tty) +{ + struct ipoctal_channel *channel = tty->driver_data; + struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); + + /* release the carrier driver */ + ipack_put_carrier(ipoctal->dev); +} + static const struct tty_operations ipoctal_fops = { .ioctl = NULL, .open = ipoctal_open, @@ -647,6 +665,7 @@ static const struct tty_operations ipoctal_fops = { .get_icount = ipoctal_get_icount, .hangup = ipoctal_hangup, .shutdown = ipoctal_shutdown, + .cleanup = ipoctal_cleanup, }; static int ipoctal_probe(struct ipack_device *dev) diff --git a/drivers/ipack/devices/ipoctal.h b/drivers/ipack/devices/ipoctal.h index 28f1c4233154..7fede0eb6a0c 100644 --- a/drivers/ipack/devices/ipoctal.h +++ b/drivers/ipack/devices/ipoctal.h @@ -12,7 +12,7 @@ * Software Foundation; version 2 of the License. */ -#ifndef _IPOCTAL_H +#ifndef _IPOCTAL_H_ #define _IPOCTAL_H_ #define NR_CHANNELS 8 diff --git a/drivers/ipack/ipack.c b/drivers/ipack/ipack.c index d0016ba469ed..c0e7b624ce54 100644 --- a/drivers/ipack/ipack.c +++ b/drivers/ipack/ipack.c @@ -206,7 +206,8 @@ static struct bus_type ipack_bus_type = { }; struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots, - const struct ipack_bus_ops *ops) + const struct ipack_bus_ops *ops, + struct module *owner) { int bus_nr; struct ipack_bus_device *bus; @@ -225,6 +226,7 @@ struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots, bus->parent = parent; bus->slots = slots; bus->ops = ops; + bus->owner = owner; return bus; } EXPORT_SYMBOL_GPL(ipack_bus_register); diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index 33f8673d23a6..b432873def96 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -18,7 +18,7 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/slab.h> +#include <linux/device.h> #include <linux/jiffies.h> #include <linux/i2c.h> #include <linux/mutex.h> @@ -159,12 +159,11 @@ static int eeprom_probe(struct i2c_client *client, { struct i2c_adapter *adapter = client->adapter; struct eeprom_data *data; - int err; - if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct eeprom_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; memset(data->data, 0xff, EEPROM_SIZE); i2c_set_clientdata(client, data); @@ -190,22 +189,12 @@ static int eeprom_probe(struct i2c_client *client, } /* create the sysfs eeprom file */ - err = sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr); - if (err) - goto exit_kfree; - - return 0; - -exit_kfree: - kfree(data); -exit: - return err; + return sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr); } static int eeprom_remove(struct i2c_client *client) { sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr); - kfree(i2c_get_clientdata(client)); return 0; } diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 43bbabc96b6c..4cf8f82cfca2 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -5,7 +5,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -45,10 +45,10 @@ MODULE_AUTHOR("Frank Haverkamp <haver@linux.vnet.ibm.com>"); MODULE_AUTHOR("Michael Ruettger <michael@ibmra.de>"); MODULE_AUTHOR("Joerg-Stephan Vogt <jsvogt@de.ibm.com>"); -MODULE_AUTHOR("Michal Jung <mijung@de.ibm.com>"); +MODULE_AUTHOR("Michael Jung <mijung@gmx.net>"); MODULE_DESCRIPTION("GenWQE Card"); -MODULE_VERSION(DRV_VERS_STRING); +MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL"); static char genwqe_driver_name[] = GENWQE_DEVNAME; @@ -346,8 +346,13 @@ static bool genwqe_setup_vf_jtimer(struct genwqe_dev *cd) unsigned int vf; u32 T = genwqe_T_psec(cd); u64 x; + int totalvfs; - for (vf = 0; vf < pci_sriov_get_totalvfs(pci_dev); vf++) { + totalvfs = pci_sriov_get_totalvfs(pci_dev); + if (totalvfs <= 0) + return false; + + for (vf = 0; vf < totalvfs; vf++) { if (cd->vf_jobtimeout_msec[vf] == 0) continue; @@ -383,8 +388,9 @@ static int genwqe_ffdc_buffs_alloc(struct genwqe_dev *cd) /* currently support only the debug units mentioned here */ cd->ffdc[type].entries = e; - cd->ffdc[type].regs = kmalloc(e * sizeof(struct genwqe_reg), - GFP_KERNEL); + cd->ffdc[type].regs = + kmalloc_array(e, sizeof(struct genwqe_reg), + GFP_KERNEL); /* * regs == NULL is ok, the using code treats this as no regs, * Printing warning is ok in this case. @@ -723,8 +729,8 @@ static u64 genwqe_fir_checking(struct genwqe_dev *cd) __genwqe_writeq(cd, sfir_addr, sfir); dev_dbg(&pci_dev->dev, - "[HM] Clearing 2ndary FIR 0x%08x " - "with 0x%016llx\n", sfir_addr, sfir); + "[HM] Clearing 2ndary FIR 0x%08x with 0x%016llx\n", + sfir_addr, sfir); /* * note, these cannot be error-Firs @@ -740,9 +746,8 @@ static u64 genwqe_fir_checking(struct genwqe_dev *cd) __genwqe_writeq(cd, fir_clr_addr, mask); dev_dbg(&pci_dev->dev, - "[HM] Clearing primary FIR 0x%08x " - "with 0x%016llx\n", fir_clr_addr, - mask); + "[HM] Clearing primary FIR 0x%08x with 0x%016llx\n", + fir_clr_addr, mask); } } } @@ -1125,6 +1130,8 @@ static int genwqe_pci_setup(struct genwqe_dev *cd) } cd->num_vfs = pci_sriov_get_totalvfs(pci_dev); + if (cd->num_vfs < 0) + cd->num_vfs = 0; err = genwqe_read_ids(cd); if (err) @@ -1202,8 +1209,8 @@ static int genwqe_probe(struct pci_dev *pci_dev, err = genwqe_health_check_start(cd); if (err < 0) { dev_err(&pci_dev->dev, - "err: cannot start health checking! " - "(err=%d)\n", err); + "err: cannot start health checking! (err=%d)\n", + err); goto out_stop_services; } } @@ -1313,11 +1320,14 @@ static void genwqe_err_resume(struct pci_dev *pci_dev) static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs) { + int rc; struct genwqe_dev *cd = dev_get_drvdata(&dev->dev); if (numvfs > 0) { genwqe_setup_vf_jtimer(cd); - pci_enable_sriov(dev, numvfs); + rc = pci_enable_sriov(dev, numvfs); + if (rc < 0) + return rc; return numvfs; } if (numvfs == 0) { diff --git a/drivers/misc/genwqe/card_base.h b/drivers/misc/genwqe/card_base.h index 67abd8cb2247..c64d7cad1085 100644 --- a/drivers/misc/genwqe/card_base.h +++ b/drivers/misc/genwqe/card_base.h @@ -8,7 +8,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -201,7 +201,8 @@ static inline void genwqe_mapping_init(struct dma_mapping *m, * @ddcb_seq: Sequence number of last DDCB * @ddcbs_in_flight: Currently enqueued DDCBs * @ddcbs_completed: Number of already completed DDCBs - * @busy: Number of -EBUSY returns + * @return_on_busy: Number of -EBUSY returns on full queue + * @wait_on_busy: Number of waits on full queue * @ddcb_daddr: DMA address of first DDCB in the queue * @ddcb_vaddr: Kernel virtual address of first DDCB in the queue * @ddcb_req: Associated requests (one per DDCB) @@ -218,7 +219,8 @@ struct ddcb_queue { unsigned int ddcbs_in_flight; /* number of ddcbs in processing */ unsigned int ddcbs_completed; unsigned int ddcbs_max_in_flight; - unsigned int busy; /* how many times -EBUSY? */ + unsigned int return_on_busy; /* how many times -EBUSY? */ + unsigned int wait_on_busy; dma_addr_t ddcb_daddr; /* DMA address */ struct ddcb *ddcb_vaddr; /* kernel virtual addr for DDCBs */ @@ -226,7 +228,7 @@ struct ddcb_queue { wait_queue_head_t *ddcb_waitqs; /* waitqueue per ddcb */ spinlock_t ddcb_lock; /* exclusive access to queue */ - wait_queue_head_t ddcb_waitq; /* wait for ddcb processing */ + wait_queue_head_t busy_waitq; /* wait for ddcb processing */ /* registers or the respective queue to be used */ u32 IO_QUEUE_CONFIG; @@ -306,7 +308,7 @@ struct genwqe_dev { struct pci_dev *pci_dev; /* PCI device */ void __iomem *mmio; /* BAR-0 MMIO start */ unsigned long mmio_len; - u16 num_vfs; + int num_vfs; u32 vf_jobtimeout_msec[GENWQE_MAX_VFS]; int is_privileged; /* access to all regs possible */ @@ -508,7 +510,7 @@ static inline bool dma_mapping_used(struct dma_mapping *m) * buildup and teardown. */ int __genwqe_execute_ddcb(struct genwqe_dev *cd, - struct genwqe_ddcb_cmd *cmd); + struct genwqe_ddcb_cmd *cmd, unsigned int f_flags); /** * __genwqe_execute_raw_ddcb() - Execute DDCB request without addr translation @@ -520,9 +522,12 @@ int __genwqe_execute_ddcb(struct genwqe_dev *cd, * modification. */ int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd, - struct genwqe_ddcb_cmd *cmd); + struct genwqe_ddcb_cmd *cmd, + unsigned int f_flags); +int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, + struct ddcb_requ *req, + unsigned int f_flags); -int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req); int __genwqe_wait_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req); int __genwqe_purge_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req); diff --git a/drivers/misc/genwqe/card_ddcb.c b/drivers/misc/genwqe/card_ddcb.c index dc9851a5540e..6d51e5f08664 100644 --- a/drivers/misc/genwqe/card_ddcb.c +++ b/drivers/misc/genwqe/card_ddcb.c @@ -5,7 +5,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -185,8 +185,7 @@ static void print_ddcb_info(struct genwqe_dev *cd, struct ddcb_queue *queue) pddcb = queue->ddcb_vaddr; for (i = 0; i < queue->ddcb_max; i++) { dev_err(&pci_dev->dev, - " %c %-3d: RETC=%03x SEQ=%04x " - "HSI=%02X SHI=%02x PRIV=%06llx CMD=%03x\n", + " %c %-3d: RETC=%03x SEQ=%04x HSI=%02X SHI=%02x PRIV=%06llx CMD=%03x\n", i == queue->ddcb_act ? '>' : ' ', i, be16_to_cpu(pddcb->retc_16), @@ -214,6 +213,7 @@ struct genwqe_ddcb_cmd *ddcb_requ_alloc(void) void ddcb_requ_free(struct genwqe_ddcb_cmd *cmd) { struct ddcb_requ *req = container_of(cmd, struct ddcb_requ, cmd); + kfree(req); } @@ -306,7 +306,7 @@ static int enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_queue *queue, new = (old | DDCB_NEXT_BE32); - wmb(); + wmb(); /* need to ensure write ordering */ icrc_hsi_shi = cmpxchg(&prev_ddcb->icrc_hsi_shi_32, old, new); if (icrc_hsi_shi == old) @@ -317,7 +317,7 @@ static int enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_queue *queue, ddcb_mark_tapped(pddcb); num = (u64)ddcb_no << 8; - wmb(); + wmb(); /* need to ensure write ordering */ __genwqe_writeq(cd, queue->IO_QUEUE_OFFSET, num); /* start queue */ return RET_DDCB_TAPPED; @@ -390,8 +390,9 @@ static int genwqe_check_ddcb_queue(struct genwqe_dev *cd, 0x00000000) goto go_home; /* not completed, continue waiting */ - /* Note: DDCB could be purged */ + wmb(); /* Add sync to decouple prev. read operations */ + /* Note: DDCB could be purged */ req = queue->ddcb_req[queue->ddcb_act]; if (req == NULL) { /* this occurs if DDCB is purged, not an error */ @@ -416,9 +417,7 @@ static int genwqe_check_ddcb_queue(struct genwqe_dev *cd, status = __genwqe_readq(cd, queue->IO_QUEUE_STATUS); dev_err(&pci_dev->dev, - "[%s] SEQN=%04x HSI=%02x RETC=%03x " - " Q_ERRCNTS=%016llx Q_STATUS=%016llx\n" - " DDCB_DMA_ADDR=%016llx\n", + "[%s] SEQN=%04x HSI=%02x RETC=%03x Q_ERRCNTS=%016llx Q_STATUS=%016llx DDCB_DMA_ADDR=%016llx\n", __func__, be16_to_cpu(pddcb->seqnum_16), pddcb->hsi, retc_16, errcnts, status, queue->ddcb_daddr + ddcb_offs); @@ -439,8 +438,7 @@ static int genwqe_check_ddcb_queue(struct genwqe_dev *cd, vcrc_16 = be16_to_cpu(pddcb->vcrc_16); if (vcrc != vcrc_16) { printk_ratelimited(KERN_ERR - "%s %s: err: wrong VCRC pre=%02x vcrc_len=%d " - "bytes vcrc_data=%04x is not vcrc_card=%04x\n", + "%s %s: err: wrong VCRC pre=%02x vcrc_len=%d bytes vcrc_data=%04x is not vcrc_card=%04x\n", GENWQE_DEVNAME, dev_name(&pci_dev->dev), pddcb->pre, VCRC_LENGTH(req->cmd.asv_length), vcrc, vcrc_16); @@ -450,8 +448,10 @@ static int genwqe_check_ddcb_queue(struct genwqe_dev *cd, queue->ddcbs_completed++; queue->ddcbs_in_flight--; - /* wake up process waiting for this DDCB */ + /* wake up process waiting for this DDCB, and + processes on the busy queue */ wake_up_interruptible(&queue->ddcb_waitqs[queue->ddcb_act]); + wake_up_interruptible(&queue->busy_waitq); pick_next_one: queue->ddcb_act = (queue->ddcb_act + 1) % queue->ddcb_max; @@ -717,8 +717,7 @@ go_home: genwqe_hexdump(pci_dev, pddcb, sizeof(*pddcb)); dev_err(&pci_dev->dev, - "[%s] err: DDCB#%d not purged and not completed " - "after %d seconds QSTAT=%016llx!!\n", + "[%s] err: DDCB#%d not purged and not completed after %d seconds QSTAT=%016llx!!\n", __func__, req->num, genwqe_ddcb_software_timeout, queue_status); @@ -740,7 +739,7 @@ int genwqe_init_debug_data(struct genwqe_dev *cd, struct genwqe_debug_data *d) } len = sizeof(d->driver_version); - snprintf(d->driver_version, len, "%s", DRV_VERS_STRING); + snprintf(d->driver_version, len, "%s", DRV_VERSION); d->slu_unitcfg = cd->slu_unitcfg; d->app_unitcfg = cd->app_unitcfg; return 0; @@ -748,14 +747,16 @@ int genwqe_init_debug_data(struct genwqe_dev *cd, struct genwqe_debug_data *d) /** * __genwqe_enqueue_ddcb() - Enqueue a DDCB - * @cd: pointer to genwqe device descriptor - * @req: pointer to DDCB execution request + * @cd: pointer to genwqe device descriptor + * @req: pointer to DDCB execution request + * @f_flags: file mode: blocking, non-blocking * * Return: 0 if enqueuing succeeded * -EIO if card is unusable/PCIe problems * -EBUSY if enqueuing failed */ -int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) +int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req, + unsigned int f_flags) { struct ddcb *pddcb; unsigned long flags; @@ -763,6 +764,7 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) struct pci_dev *pci_dev = cd->pci_dev; u16 icrc; + retry: if (cd->card_state != GENWQE_CARD_USED) { printk_ratelimited(KERN_ERR "%s %s: [%s] Card is unusable/PCIe problem Req#%d\n", @@ -788,9 +790,24 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) pddcb = get_next_ddcb(cd, queue, &req->num); /* get ptr and num */ if (pddcb == NULL) { + int rc; + spin_unlock_irqrestore(&queue->ddcb_lock, flags); - queue->busy++; - return -EBUSY; + + if (f_flags & O_NONBLOCK) { + queue->return_on_busy++; + return -EBUSY; + } + + queue->wait_on_busy++; + rc = wait_event_interruptible(queue->busy_waitq, + queue_free_ddcbs(queue) != 0); + dev_dbg(&pci_dev->dev, "[%s] waiting for free DDCB: rc=%d\n", + __func__, rc); + if (rc == -ERESTARTSYS) + return rc; /* interrupted by a signal */ + + goto retry; } if (queue->ddcb_req[req->num] != NULL) { @@ -893,9 +910,11 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) * __genwqe_execute_raw_ddcb() - Setup and execute DDCB * @cd: pointer to genwqe device descriptor * @req: user provided DDCB request + * @f_flags: file mode: blocking, non-blocking */ int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd, - struct genwqe_ddcb_cmd *cmd) + struct genwqe_ddcb_cmd *cmd, + unsigned int f_flags) { int rc = 0; struct pci_dev *pci_dev = cd->pci_dev; @@ -911,7 +930,7 @@ int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd, __func__, cmd->asiv_length); return -EINVAL; } - rc = __genwqe_enqueue_ddcb(cd, req); + rc = __genwqe_enqueue_ddcb(cd, req, f_flags); if (rc != 0) return rc; @@ -1017,7 +1036,8 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue) queue->ddcbs_in_flight = 0; /* statistics */ queue->ddcbs_max_in_flight = 0; queue->ddcbs_completed = 0; - queue->busy = 0; + queue->return_on_busy = 0; + queue->wait_on_busy = 0; queue->ddcb_seq = 0x100; /* start sequence number */ queue->ddcb_max = genwqe_ddcb_max; /* module parameter */ @@ -1057,7 +1077,7 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue) queue->ddcb_next = 0; /* queue is empty */ spin_lock_init(&queue->ddcb_lock); - init_waitqueue_head(&queue->ddcb_waitq); + init_waitqueue_head(&queue->busy_waitq); val64 = ((u64)(queue->ddcb_max - 1) << 8); /* lastptr */ __genwqe_writeq(cd, queue->IO_QUEUE_CONFIG, 0x07); /* iCRC/vCRC */ @@ -1251,10 +1271,8 @@ int genwqe_setup_service_layer(struct genwqe_dev *cd) } rc = genwqe_set_interrupt_capability(cd, GENWQE_MSI_IRQS); - if (rc) { - rc = -ENODEV; + if (rc) goto stop_kthread; - } /* * We must have all wait-queues initialized when we enable the @@ -1307,6 +1325,7 @@ static int queue_wake_up_all(struct genwqe_dev *cd) for (i = 0; i < queue->ddcb_max; i++) wake_up_interruptible(&queue->ddcb_waitqs[queue->ddcb_act]); + wake_up_interruptible(&queue->busy_waitq); spin_unlock_irqrestore(&queue->ddcb_lock, flags); return 0; @@ -1346,8 +1365,8 @@ int genwqe_finish_queue(struct genwqe_dev *cd) break; dev_dbg(&pci_dev->dev, - " DEBUG [%d/%d] waiting for queue to get empty: " - "%d requests!\n", i, waitmax, in_flight); + " DEBUG [%d/%d] waiting for queue to get empty: %d requests!\n", + i, waitmax, in_flight); /* * Severe severe error situation: The card itself has diff --git a/drivers/misc/genwqe/card_ddcb.h b/drivers/misc/genwqe/card_ddcb.h index c4f26720753e..0361a68d79a6 100644 --- a/drivers/misc/genwqe/card_ddcb.h +++ b/drivers/misc/genwqe/card_ddcb.h @@ -8,7 +8,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/misc/genwqe/card_debugfs.c b/drivers/misc/genwqe/card_debugfs.c index c9b4d6d0eb99..c715534e7fe7 100644 --- a/drivers/misc/genwqe/card_debugfs.c +++ b/drivers/misc/genwqe/card_debugfs.c @@ -5,7 +5,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -244,14 +244,16 @@ static int genwqe_ddcb_info_show(struct seq_file *s, void *unused) " ddcbs_in_flight: %u\n" " ddcbs_max_in_flight: %u\n" " ddcbs_completed: %u\n" - " busy: %u\n" + " return_on_busy: %u\n" + " wait_on_busy: %u\n" " irqs_processed: %u\n", queue->ddcb_max, (long long)queue->ddcb_daddr, (long long)queue->ddcb_daddr + (queue->ddcb_max * DDCB_LENGTH), (long long)queue->ddcb_vaddr, queue->ddcbs_in_flight, queue->ddcbs_max_in_flight, queue->ddcbs_completed, - queue->busy, cd->irqs_processed); + queue->return_on_busy, queue->wait_on_busy, + cd->irqs_processed); /* Hardware State */ seq_printf(s, " 0x%08x 0x%016llx IO_QUEUE_CONFIG\n" @@ -323,7 +325,7 @@ static int genwqe_info_show(struct seq_file *s, void *unused) " Base Clock : %u MHz\n" " Arch/SVN Release: %u/%llx\n" " Bitstream : %llx\n", - GENWQE_DEVNAME, DRV_VERS_STRING, dev_name(&pci_dev->dev), + GENWQE_DEVNAME, DRV_VERSION, dev_name(&pci_dev->dev), genwqe_is_privileged(cd) ? "Physical" : "Virtual or no SR-IOV", cd->card_idx, slu_id, app_id, diff --git a/drivers/misc/genwqe/card_dev.c b/drivers/misc/genwqe/card_dev.c index aae42555e2ca..5918586f2f76 100644 --- a/drivers/misc/genwqe/card_dev.c +++ b/drivers/misc/genwqe/card_dev.c @@ -5,7 +5,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -213,9 +213,9 @@ static void genwqe_remove_mappings(struct genwqe_file *cfile) * GENWQE_MAPPING_SGL_TEMP should be removed by tidy up code. */ dev_err(&pci_dev->dev, - "[%s] %d. cleanup mapping: u_vaddr=%p " - "u_kaddr=%016lx dma_addr=%lx\n", __func__, i++, - dma_map->u_vaddr, (unsigned long)dma_map->k_vaddr, + "[%s] %d. cleanup mapping: u_vaddr=%p u_kaddr=%016lx dma_addr=%lx\n", + __func__, i++, dma_map->u_vaddr, + (unsigned long)dma_map->k_vaddr, (unsigned long)dma_map->dma_addr); if (dma_map->type == GENWQE_MAPPING_RAW) { @@ -346,6 +346,7 @@ static int genwqe_open(struct inode *inode, struct file *filp) static int genwqe_fasync(int fd, struct file *filp, int mode) { struct genwqe_file *cdev = (struct genwqe_file *)filp->private_data; + return fasync_helper(fd, filp, mode, &cdev->async_queue); } @@ -515,6 +516,7 @@ static int do_flash_update(struct genwqe_file *cfile, u32 crc; u8 cmdopts; struct genwqe_dev *cd = cfile->cd; + struct file *filp = cfile->filp; struct pci_dev *pci_dev = cd->pci_dev; if ((load->size & 0x3) != 0) @@ -609,7 +611,7 @@ static int do_flash_update(struct genwqe_file *cfile, /* For Genwqe5 we get back the calculated CRC */ *(u64 *)&req->asv[0] = 0ULL; /* 0x80 */ - rc = __genwqe_execute_raw_ddcb(cd, req); + rc = __genwqe_execute_raw_ddcb(cd, req, filp->f_flags); load->retc = req->retc; load->attn = req->attn; @@ -649,6 +651,7 @@ static int do_flash_read(struct genwqe_file *cfile, u8 *xbuf; u8 cmdopts; struct genwqe_dev *cd = cfile->cd; + struct file *filp = cfile->filp; struct pci_dev *pci_dev = cd->pci_dev; struct genwqe_ddcb_cmd *cmd; @@ -726,7 +729,7 @@ static int do_flash_read(struct genwqe_file *cfile, /* we only get back the calculated CRC */ *(u64 *)&cmd->asv[0] = 0ULL; /* 0x80 */ - rc = __genwqe_execute_raw_ddcb(cd, cmd); + rc = __genwqe_execute_raw_ddcb(cd, cmd, filp->f_flags); load->retc = cmd->retc; load->attn = cmd->attn; @@ -987,13 +990,14 @@ static int genwqe_execute_ddcb(struct genwqe_file *cfile, { int rc; struct genwqe_dev *cd = cfile->cd; + struct file *filp = cfile->filp; struct ddcb_requ *req = container_of(cmd, struct ddcb_requ, cmd); rc = ddcb_cmd_fixups(cfile, req); if (rc != 0) return rc; - rc = __genwqe_execute_raw_ddcb(cd, cmd); + rc = __genwqe_execute_raw_ddcb(cd, cmd, filp->f_flags); ddcb_cmd_cleanup(cfile, req); return rc; } @@ -1005,6 +1009,7 @@ static int do_execute_ddcb(struct genwqe_file *cfile, struct genwqe_ddcb_cmd *cmd; struct ddcb_requ *req; struct genwqe_dev *cd = cfile->cd; + struct file *filp = cfile->filp; cmd = ddcb_requ_alloc(); if (cmd == NULL) @@ -1020,7 +1025,7 @@ static int do_execute_ddcb(struct genwqe_file *cfile, if (!raw) rc = genwqe_execute_ddcb(cfile, cmd); else - rc = __genwqe_execute_raw_ddcb(cd, cmd); + rc = __genwqe_execute_raw_ddcb(cd, cmd, filp->f_flags); /* Copy back only the modifed fields. Do not copy ASIV back since the copy got modified by the driver. */ diff --git a/drivers/misc/genwqe/card_sysfs.c b/drivers/misc/genwqe/card_sysfs.c index 7232e40a3ad9..2c33fbca9225 100644 --- a/drivers/misc/genwqe/card_sysfs.c +++ b/drivers/misc/genwqe/card_sysfs.c @@ -5,7 +5,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -91,13 +91,6 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(type); -static ssize_t driver_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%s\n", DRV_VERS_STRING); -} -static DEVICE_ATTR_RO(driver); - static ssize_t tempsens_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -256,7 +249,6 @@ static struct attribute *genwqe_attributes[] = { &dev_attr_next_bitstream.attr, &dev_attr_curr_bitstream.attr, &dev_attr_base_clock.attr, - &dev_attr_driver.attr, &dev_attr_type.attr, &dev_attr_version.attr, &dev_attr_appid.attr, @@ -268,7 +260,6 @@ static struct attribute *genwqe_attributes[] = { }; static struct attribute *genwqe_normal_attributes[] = { - &dev_attr_driver.attr, &dev_attr_type.attr, &dev_attr_version.attr, &dev_attr_appid.attr, diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index a6400f09229c..7cb3b7e41739 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -5,7 +5,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -150,6 +150,7 @@ int genwqe_read_app_id(struct genwqe_dev *cd, char *app_name, int len) memset(app_name, 0, len); for (i = 0, j = 0; j < min(len, 4); j++) { char ch = (char)((app_id >> (24 - j*8)) & 0xff); + if (ch == ' ') continue; app_name[i++] = isprint(ch) ? ch : 'X'; @@ -304,8 +305,7 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, sgl->nr_pages = DIV_ROUND_UP(sgl->fpage_offs + user_size, PAGE_SIZE); sgl->lpage_size = (user_size - sgl->fpage_size) % PAGE_SIZE; - dev_dbg(&pci_dev->dev, "[%s] uaddr=%p usize=%8ld nr_pages=%ld " - "fpage_offs=%lx fpage_size=%ld lpage_size=%ld\n", + dev_dbg(&pci_dev->dev, "[%s] uaddr=%p usize=%8ld nr_pages=%ld fpage_offs=%lx fpage_size=%ld lpage_size=%ld\n", __func__, user_addr, user_size, sgl->nr_pages, sgl->fpage_offs, sgl->fpage_size, sgl->lpage_size); @@ -662,6 +662,7 @@ int genwqe_user_vunmap(struct genwqe_dev *cd, struct dma_mapping *m, u8 genwqe_card_type(struct genwqe_dev *cd) { u64 card_type = cd->slu_unitcfg; + return (u8)((card_type & IO_SLU_UNITCFG_TYPE_MASK) >> 20); } diff --git a/drivers/misc/genwqe/genwqe_driver.h b/drivers/misc/genwqe/genwqe_driver.h index a506e9aa2d57..15355350e076 100644 --- a/drivers/misc/genwqe/genwqe_driver.h +++ b/drivers/misc/genwqe/genwqe_driver.h @@ -8,7 +8,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ #include <asm/byteorder.h> #include <linux/genwqe/genwqe_card.h> -#define DRV_VERS_STRING "2.0.21" +#define DRV_VERSION "2.0.25" /* * Static minor number assignement, until we decide/implement diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index 7e1efd5f58f0..c544f1f50f52 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c @@ -247,3 +247,4 @@ module_spi_driver(lattice_ecp3_driver); MODULE_AUTHOR("Stefan Roese <sr@denx.de>"); MODULE_DESCRIPTION("Lattice ECP3 FPGA configuration via SPI"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(FIRMWARE_NAME); diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 0d6234db00fa..6cdce8477f57 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -20,7 +20,6 @@ #include <linux/types.h> #include <linux/fcntl.h> #include <linux/aio.h> -#include <linux/pci.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/list.h> @@ -29,6 +28,7 @@ #include <linux/uuid.h> #include <linux/jiffies.h> #include <linux/uaccess.h> +#include <linux/slab.h> #include <linux/mei.h> @@ -64,31 +64,32 @@ void mei_amthif_reset_params(struct mei_device *dev) * * @dev: the device structure * + * Return: 0 on success, <0 on failure. */ 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, i; + int ret; dev->iamthif_state = MEI_IAMTHIF_IDLE; mei_cl_init(cl, dev); - i = mei_me_cl_by_uuid(dev, &mei_amthif_guid); - if (i < 0) { - dev_info(&dev->pdev->dev, - "amthif: failed to find the client %d\n", i); + me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid); + if (!me_cl) { + dev_info(dev->dev, "amthif: failed to find the client"); return -ENOTTY; } - cl->me_client_id = dev->me_clients[i].client_id; + cl->me_client_id = me_cl->client_id; + cl->cl_uuid = me_cl->props.protocol_name; /* Assign iamthif_mtu to the value received from ME */ - dev->iamthif_mtu = dev->me_clients[i].props.max_msg_length; - dev_dbg(&dev->pdev->dev, "IAMTHIF_MTU = %d\n", - dev->me_clients[i].props.max_msg_length); + 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; @@ -96,17 +97,15 @@ int mei_amthif_host_init(struct mei_device *dev) /* allocate storage for ME message buffer */ msg_buf = kcalloc(dev->iamthif_mtu, sizeof(unsigned char), GFP_KERNEL); - if (!msg_buf) { - dev_err(&dev->pdev->dev, "amthif: memory allocation for ME message buffer failed.\n"); + if (!msg_buf) return -ENOMEM; - } dev->iamthif_msg_buf = msg_buf; ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID); if (ret < 0) { - dev_err(&dev->pdev->dev, + dev_err(dev->dev, "amthif: failed link client %d\n", ret); return ret; } @@ -124,18 +123,16 @@ int mei_amthif_host_init(struct mei_device *dev) * @dev: the device structure * @file: pointer to file object * - * returns returned a list entry on success, NULL on failure. + * Return: returned a list entry on success, NULL on failure. */ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, struct file *file) { struct mei_cl_cb *cb; - list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list) { - if (cb->cl && cb->cl == &dev->iamthif_cl && - cb->file_object == file) + list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list) + if (cb->file_object == file) return cb; - } return NULL; } @@ -144,15 +141,14 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, * mei_amthif_read - read data from AMTHIF client * * @dev: the device structure - * @if_num: minor number * @file: pointer to file object - * @*ubuf: pointer to user data in user space + * @ubuf: pointer to user data in user space * @length: data length to read * @offset: data read offset * * Locking: called under "dev->device_lock" lock * - * returns + * Return: * returned data length on success, * zero if no data to read, * negative on failure. @@ -160,25 +156,19 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, int mei_amthif_read(struct mei_device *dev, struct file *file, char __user *ubuf, size_t length, loff_t *offset) { - int rets; - int wait_ret; - struct mei_cl_cb *cb = NULL; struct mei_cl *cl = file->private_data; + struct mei_cl_cb *cb; unsigned long timeout; - int i; + int rets; + int wait_ret; /* Only possible if we are in timeout */ - if (!cl || cl != &dev->iamthif_cl) { - dev_dbg(&dev->pdev->dev, "bad file ext.\n"); + if (!cl) { + dev_err(dev->dev, "bad file ext.\n"); return -ETIME; } - i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id); - if (i < 0) { - dev_dbg(&dev->pdev->dev, "amthif client not found.\n"); - return -ENOTTY; - } - dev_dbg(&dev->pdev->dev, "checking amthif data\n"); + dev_dbg(dev->dev, "checking amthif data\n"); cb = mei_amthif_find_read_list_entry(dev, file); /* Check for if we can block or not*/ @@ -186,7 +176,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, return -EAGAIN; - dev_dbg(&dev->pdev->dev, "waiting for amthif data\n"); + dev_dbg(dev->dev, "waiting for amthif data\n"); while (cb == NULL) { /* unlock the Mutex */ mutex_unlock(&dev->device_lock); @@ -200,21 +190,21 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, if (wait_ret) return -ERESTARTSYS; - dev_dbg(&dev->pdev->dev, "woke up from sleep\n"); + dev_dbg(dev->dev, "woke up from sleep\n"); } - dev_dbg(&dev->pdev->dev, "Got amthif data\n"); + 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->pdev->dev, "amthif timeout = %lud\n", + dev_dbg(dev->dev, "amthif timeout = %lud\n", timeout); if (time_after(jiffies, timeout)) { - dev_dbg(&dev->pdev->dev, "amthif Time out\n"); + dev_dbg(dev->dev, "amthif Time out\n"); /* 15 sec for the message has expired */ list_del(&cb->list); rets = -ETIME; @@ -234,16 +224,16 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, * remove message from deletion list */ - dev_dbg(&dev->pdev->dev, "amthif cb->response_buffer size - %d\n", + dev_dbg(dev->dev, "amthif cb->response_buffer size - %d\n", cb->response_buffer.size); - dev_dbg(&dev->pdev->dev, "amthif cb->buf_idx - %lu\n", cb->buf_idx); + 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)) { - dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n"); + dev_dbg(dev->dev, "failed to copy data to userland\n"); rets = -EFAULT; } else { rets = length; @@ -253,7 +243,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, } } free: - dev_dbg(&dev->pdev->dev, "free amthif cb memory.\n"); + dev_dbg(dev->dev, "free amthif cb memory.\n"); *offset = 0; mei_io_cb_free(cb); out: @@ -266,7 +256,7 @@ out: * @dev: the device structure * @cb: mei call back struct * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. * */ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) @@ -277,7 +267,7 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) if (!dev || !cb) return -ENODEV; - dev_dbg(&dev->pdev->dev, "write data to amthif client.\n"); + dev_dbg(dev->dev, "write data to amthif client.\n"); dev->iamthif_state = MEI_IAMTHIF_WRITING; dev->iamthif_current_cb = cb; @@ -316,12 +306,12 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) return -EIO; dev->iamthif_flow_control_pending = true; dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; - dev_dbg(&dev->pdev->dev, "add amthif cb to write waiting list\n"); + dev_dbg(dev->dev, "add amthif cb to write waiting list\n"); dev->iamthif_current_cb = cb; dev->iamthif_file_object = cb->file_object; list_add_tail(&cb->list, &dev->write_waiting_list.list); } else { - dev_dbg(&dev->pdev->dev, "message does not complete, so add amthif cb to write list.\n"); + 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 { @@ -336,7 +326,7 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) * @dev: the device structure * @cb: mei call back struct * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. * */ int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb) @@ -354,25 +344,23 @@ int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb) if (!list_empty(&dev->amthif_cmd_list.list) || dev->iamthif_state != MEI_IAMTHIF_IDLE) { - dev_dbg(&dev->pdev->dev, + dev_dbg(dev->dev, "amthif state = %d\n", dev->iamthif_state); - dev_dbg(&dev->pdev->dev, "AMTHIF: add cb to the wait list\n"); + 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 + * mei_amthif_run_next_cmd - send next amt command from queue * * @dev: the device structure - * - * returns 0 on success, <0 on failure. */ void mei_amthif_run_next_cmd(struct mei_device *dev) { - struct mei_cl_cb *pos = NULL; - struct mei_cl_cb *next = NULL; + struct mei_cl_cb *cb; + struct mei_cl_cb *next; int status; if (!dev) @@ -386,21 +374,17 @@ void mei_amthif_run_next_cmd(struct mei_device *dev) dev->iamthif_timer = 0; dev->iamthif_file_object = NULL; - dev_dbg(&dev->pdev->dev, "complete amthif cmd_list cb.\n"); - - list_for_each_entry_safe(pos, next, &dev->amthif_cmd_list.list, list) { - list_del(&pos->list); + dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); - if (pos->cl && pos->cl == &dev->iamthif_cl) { - status = mei_amthif_send_cmd(dev, pos); - if (status) { - dev_dbg(&dev->pdev->dev, - "amthif write failed status = %d\n", + list_for_each_entry_safe(cb, next, &dev->amthif_cmd_list.list, list) { + list_del(&cb->list); + if (!cb->cl) + continue; + status = mei_amthif_send_cmd(dev, cb); + if (status) + dev_warn(dev->dev, "amthif write failed status = %d\n", status); - return; - } - break; - } + break; } } @@ -421,7 +405,7 @@ unsigned int mei_amthif_poll(struct mei_device *dev, dev->iamthif_file_object == file) { mask |= (POLLIN | POLLRDNORM); - dev_dbg(&dev->pdev->dev, "run next amthif cb\n"); + dev_dbg(dev->dev, "run next amthif cb\n"); mei_amthif_run_next_cmd(dev); } mutex_unlock(&dev->device_lock); @@ -434,12 +418,11 @@ unsigned int mei_amthif_poll(struct mei_device *dev, /** * mei_amthif_irq_write - write iamthif command in irq thread context. * - * @dev: the device structure. - * @cb_pos: callback block. * @cl: private data of the file object. + * @cb: callback block. * @cmpl_list: complete list. * - * returns 0, OK; otherwise, error. + * Return: 0, OK; otherwise, error. */ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) @@ -481,7 +464,7 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, return 0; } - dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); + 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); @@ -514,14 +497,14 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, } /** - * mei_amthif_irq_read_message - read routine after ISR to + * mei_amthif_irq_read_msg - read routine after ISR to * handle the read amthif message * * @dev: the device structure * @mei_hdr: header of amthif message * @complete_list: An instance of our list structure * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_amthif_irq_read_msg(struct mei_device *dev, struct mei_msg_hdr *mei_hdr, @@ -543,10 +526,10 @@ int mei_amthif_irq_read_msg(struct mei_device *dev, if (!mei_hdr->msg_complete) return 0; - dev_dbg(&dev->pdev->dev, "amthif_message_buffer_index =%d\n", + dev_dbg(dev->dev, "amthif_message_buffer_index =%d\n", mei_hdr->length); - dev_dbg(&dev->pdev->dev, "completed amthif read.\n "); + dev_dbg(dev->dev, "completed amthif read.\n "); if (!dev->iamthif_current_cb) return -ENODEV; @@ -559,10 +542,10 @@ int mei_amthif_irq_read_msg(struct mei_device *dev, dev->iamthif_stall_timer = 0; cb->buf_idx = dev->iamthif_msg_buf_index; cb->read_time = jiffies; - if (dev->iamthif_ioctl && cb->cl == &dev->iamthif_cl) { + if (dev->iamthif_ioctl) { /* found the iamthif cb */ - dev_dbg(&dev->pdev->dev, "complete the amthif read cb.\n "); - dev_dbg(&dev->pdev->dev, "add the amthif read cb to complete.\n "); + 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; @@ -574,7 +557,7 @@ int mei_amthif_irq_read_msg(struct mei_device *dev, * @dev: the device structure. * @slots: free slots. * - * returns 0, OK; otherwise, error. + * Return: 0, OK; otherwise, error. */ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots) { @@ -586,11 +569,11 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots) *slots -= msg_slots; if (mei_hbm_cl_flow_control_req(dev, &dev->iamthif_cl)) { - dev_dbg(&dev->pdev->dev, "iamthif flow control failed\n"); + dev_dbg(dev->dev, "iamthif flow control failed\n"); return -EIO; } - dev_dbg(&dev->pdev->dev, "iamthif flow control success\n"); + 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; @@ -604,7 +587,7 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots) * mei_amthif_complete - complete amthif callback. * * @dev: the device structure. - * @cb_pos: callback block. + * @cb: callback block. */ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) { @@ -615,15 +598,15 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) dev->iamthif_msg_buf, dev->iamthif_msg_buf_index); list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list); - dev_dbg(&dev->pdev->dev, "amthif read completed\n"); + dev_dbg(dev->dev, "amthif read completed\n"); dev->iamthif_timer = jiffies; - dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n", + dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n", dev->iamthif_timer); } else { mei_amthif_run_next_cmd(dev); } - dev_dbg(&dev->pdev->dev, "completing amthif call back.\n"); + dev_dbg(dev->dev, "completing amthif call back.\n"); wake_up_interruptible(&dev->iamthif_cl.wait); } @@ -638,7 +621,7 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) * mei_clear_list is called to clear resources associated with file * when application calls close function or Ctrl-C was pressed * - * returns true if callback removed from the list, false otherwise + * Return: true if callback removed from the list, false otherwise */ static bool mei_clear_list(struct mei_device *dev, const struct file *file, struct list_head *mei_cb_list) @@ -678,7 +661,7 @@ static bool mei_clear_list(struct mei_device *dev, * mei_clear_lists is called to clear resources associated with file * when application calls close function or Ctrl-C was pressed * - * returns true if callback removed from the list, false otherwise + * Return: true if callback removed from the list, false otherwise */ static bool mei_clear_lists(struct mei_device *dev, struct file *file) { @@ -719,7 +702,7 @@ static bool mei_clear_lists(struct mei_device *dev, struct file *file) * @dev: device structure * @file: pointer to file structure * -* returns 0 on success, <0 on error +* Return: 0 on success, <0 on error */ int mei_amthif_release(struct mei_device *dev, struct file *file) { @@ -729,11 +712,11 @@ int mei_amthif_release(struct mei_device *dev, struct file *file) if (dev->iamthif_file_object == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) { - dev_dbg(&dev->pdev->dev, "amthif canceled iamthif state %d\n", + dev_dbg(dev->dev, "amthif canceled iamthif state %d\n", dev->iamthif_state); dev->iamthif_canceled = true; if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) { - dev_dbg(&dev->pdev->dev, "run next amthif iamthif cb\n"); + dev_dbg(dev->dev, "run next amthif iamthif cb\n"); mei_amthif_run_next_cmd(dev); } } diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 0e993ef28b94..4d20d60ca38d 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -22,7 +22,6 @@ #include <linux/slab.h> #include <linux/mutex.h> #include <linux/interrupt.h> -#include <linux/pci.h> #include <linux/mei_cl_bus.h> #include "mei_dev.h" @@ -70,7 +69,7 @@ static int mei_cl_device_probe(struct device *dev) dev_dbg(dev, "Device probe\n"); - strncpy(id.name, dev_name(dev), sizeof(id.name)); + strlcpy(id.name, dev_name(dev), sizeof(id.name)); return driver->probe(device, &id); } @@ -147,7 +146,7 @@ static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev, struct mei_cl *cl; list_for_each_entry(cl, &dev->device_list, device_link) { - if (!uuid_le_cmp(uuid, cl->device_uuid)) + if (!uuid_le_cmp(uuid, cl->cl_uuid)) return cl; } @@ -172,7 +171,7 @@ struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, device->cl = cl; device->ops = ops; - device->dev.parent = &dev->pdev->dev; + device->dev.parent = dev->dev; device->dev.bus = &mei_cl_bus_type; device->dev.type = &mei_cl_device_type; @@ -180,7 +179,7 @@ struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, status = device_register(&device->dev); if (status) { - dev_err(&dev->pdev->dev, "Failed to register MEI device\n"); + dev_err(dev->dev, "Failed to register MEI device\n"); kfree(device); return NULL; } @@ -229,8 +228,8 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking) { struct mei_device *dev; + struct mei_me_client *me_cl; struct mei_cl_cb *cb; - int id; int rets; if (WARN_ON(!cl || !cl->dev)) @@ -242,11 +241,11 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, return -ENODEV; /* Check if we have an ME client device */ - id = mei_me_cl_by_id(dev, cl->me_client_id); - if (id < 0) - return id; + me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); + if (!me_cl) + return -ENOTTY; - if (length > dev->me_clients[id].props.max_msg_length) + if (length > me_cl->props.max_msg_length) return -EFBIG; cb = mei_io_cb_init(cl, NULL); @@ -430,7 +429,7 @@ int mei_cl_enable_device(struct mei_cl_device *device) err = mei_cl_connect(cl, NULL); if (err < 0) { mutex_unlock(&dev->device_lock); - dev_err(&dev->pdev->dev, "Could not connect to the ME client"); + dev_err(dev->dev, "Could not connect to the ME client"); return err; } @@ -462,7 +461,7 @@ int mei_cl_disable_device(struct mei_cl_device *device) if (cl->state != MEI_FILE_CONNECTED) { mutex_unlock(&dev->device_lock); - dev_err(&dev->pdev->dev, "Already disconnected"); + dev_err(dev->dev, "Already disconnected"); return 0; } @@ -472,7 +471,7 @@ int mei_cl_disable_device(struct mei_cl_device *device) err = mei_cl_disconnect(cl); if (err < 0) { mutex_unlock(&dev->device_lock); - dev_err(&dev->pdev->dev, + dev_err(dev->dev, "Could not disconnect from the ME client"); return err; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 2da05c0e113d..bc9ba5359bc6 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -14,10 +14,10 @@ * */ -#include <linux/pci.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/delay.h> +#include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/mei.h> @@ -27,47 +27,90 @@ #include "client.h" /** - * mei_me_cl_by_uuid - locate index of me client + * mei_me_cl_by_uuid - locate me client by uuid * * @dev: mei device + * @uuid: me client uuid * * Locking: called under "dev->device_lock" lock * - * returns me client index or -ENOENT if not found + * Return: me client or NULL if not found */ -int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid) +struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev, + const uuid_le *uuid) { - int i; + struct mei_me_client *me_cl; - for (i = 0; i < dev->me_clients_num; ++i) - if (uuid_le_cmp(*uuid, - dev->me_clients[i].props.protocol_name) == 0) - return i; + list_for_each_entry(me_cl, &dev->me_clients, list) + if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0) + return me_cl; - return -ENOENT; + return NULL; } - /** - * mei_me_cl_by_id return index to me_clients for client_id + * mei_me_cl_by_id - locate me client by client id * * @dev: the device structure * @client_id: me client id * * Locking: called under "dev->device_lock" lock * - * returns index on success, -ENOENT on failure. + * Return: me client or NULL if not found */ +struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id) +{ + + struct mei_me_client *me_cl; -int mei_me_cl_by_id(struct mei_device *dev, u8 client_id) + list_for_each_entry(me_cl, &dev->me_clients, list) + if (me_cl->client_id == client_id) + return me_cl; + return NULL; +} + +/** + * mei_me_cl_by_uuid_id - locate me client by client id and uuid + * + * @dev: the device structure + * @uuid: me client uuid + * @client_id: me client id + * + * Locking: called under "dev->device_lock" lock + * + * 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) { - int i; + struct mei_me_client *me_cl; - for (i = 0; i < dev->me_clients_num; i++) - if (dev->me_clients[i].client_id == client_id) - return i; + list_for_each_entry(me_cl, &dev->me_clients, list) + if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 && + me_cl->client_id == client_id) + return me_cl; + return NULL; +} - return -ENOENT; +/** + * mei_me_cl_remove - remove me client matching uuid and client_id + * + * @dev: the device structure + * @uuid: me client uuid + * @client_id: me client address + */ +void mei_me_cl_remove(struct mei_device *dev, const uuid_le *uuid, u8 client_id) +{ + struct mei_me_client *me_cl, *next; + + list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) { + if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 && + me_cl->client_id == client_id) { + list_del(&me_cl->list); + kfree(me_cl); + break; + } + } } @@ -77,7 +120,7 @@ int mei_me_cl_by_id(struct mei_device *dev, u8 client_id) * @cl1: host client 1 * @cl2: host client 2 * - * returns true - if the clients has same host and me ids + * Return: true - if the clients has same host and me ids * false - otherwise */ static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, @@ -117,7 +160,7 @@ static void __mei_io_list_flush(struct mei_cl_cb *list, * @list: An instance of our list structure * @cl: host client */ -static inline void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) +void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) { __mei_io_list_flush(list, cl, false); } @@ -152,10 +195,10 @@ void mei_io_cb_free(struct mei_cl_cb *cb) /** * mei_io_cb_init - allocate and initialize io callback * - * @cl - mei client + * @cl: mei client * @fp: pointer to file structure * - * returns mei_cl_cb pointer or NULL; + * Return: mei_cl_cb pointer or NULL; */ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) { @@ -179,7 +222,7 @@ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) * @cb: io callback structure * @length: size of the buffer * - * returns 0 on success + * Return: 0 on success * -EINVAL if cb is NULL * -ENOMEM if allocation failed */ @@ -203,7 +246,7 @@ int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length) * @cb: io callback structure * @length: size of the buffer * - * returns 0 on success + * Return: 0 on success * -EINVAL if cb is NULL * -ENOMEM if allocation failed */ @@ -228,6 +271,8 @@ int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) * mei_cl_flush_queues - flushes queue lists belonging to cl. * * @cl: host client + * + * Return: 0 on success, -EINVAL if cl or cl->dev is NULL. */ int mei_cl_flush_queues(struct mei_cl *cl) { @@ -273,7 +318,7 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) * mei_cl_allocate - allocates cl structure and sets it up. * * @dev: mei device - * returns The allocated file or NULL on failure + * Return: The allocated file or NULL on failure */ struct mei_cl *mei_cl_allocate(struct mei_device *dev) { @@ -293,7 +338,7 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev) * * @cl: host client * - * returns cb on success, NULL on error + * Return: cb on success, NULL on error */ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) { @@ -311,7 +356,7 @@ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) * @cl - host client * @id - fixed host id or -1 for generic one * - * returns 0 on success + * Return: 0 on success * -EINVAL on incorrect values * -ENONET if client not found */ @@ -331,13 +376,13 @@ int mei_cl_link(struct mei_cl *cl, int id) MEI_CLIENTS_MAX); if (id >= MEI_CLIENTS_MAX) { - dev_err(&dev->pdev->dev, "id exceeded %d", MEI_CLIENTS_MAX); + dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX); return -EMFILE; } open_handle_count = dev->open_handle_count + dev->iamthif_open_count; if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { - dev_err(&dev->pdev->dev, "open_handle_count exceeded %d", + dev_err(dev->dev, "open_handle_count exceeded %d", MEI_MAX_OPEN_HANDLE_COUNT); return -EMFILE; } @@ -359,6 +404,8 @@ int mei_cl_link(struct mei_cl *cl, int id) * mei_cl_unlink - remove me_cl from the list * * @cl: host client + * + * Return: always 0 */ int mei_cl_unlink(struct mei_cl *cl) { @@ -395,19 +442,19 @@ void mei_host_client_init(struct work_struct *work) { struct mei_device *dev = container_of(work, struct mei_device, init_work); - struct mei_client_properties *client_props; - int i; + struct mei_me_client *me_cl; + struct mei_client_properties *props; mutex_lock(&dev->device_lock); - for (i = 0; i < dev->me_clients_num; i++) { - client_props = &dev->me_clients[i].props; + list_for_each_entry(me_cl, &dev->me_clients, list) { + props = &me_cl->props; - if (!uuid_le_cmp(client_props->protocol_name, mei_amthif_guid)) + if (!uuid_le_cmp(props->protocol_name, mei_amthif_guid)) mei_amthif_host_init(dev); - else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid)) + else if (!uuid_le_cmp(props->protocol_name, mei_wd_guid)) mei_wd_host_init(dev); - else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid)) + else if (!uuid_le_cmp(props->protocol_name, mei_nfc_guid)) mei_nfc_host_init(dev); } @@ -417,27 +464,27 @@ void mei_host_client_init(struct work_struct *work) mutex_unlock(&dev->device_lock); - pm_runtime_mark_last_busy(&dev->pdev->dev); - dev_dbg(&dev->pdev->dev, "rpm: autosuspend\n"); - pm_runtime_autosuspend(&dev->pdev->dev); + pm_runtime_mark_last_busy(dev->dev); + dev_dbg(dev->dev, "rpm: autosuspend\n"); + pm_runtime_autosuspend(dev->dev); } /** - * mei_hbuf_acquire: try to acquire host buffer + * mei_hbuf_acquire - try to acquire host buffer * * @dev: the device structure - * returns true if host buffer was acquired + * Return: true if host buffer was acquired */ bool mei_hbuf_acquire(struct mei_device *dev) { if (mei_pg_state(dev) == MEI_PG_ON || dev->pg_event == MEI_PG_EVENT_WAIT) { - dev_dbg(&dev->pdev->dev, "device is in pg\n"); + dev_dbg(dev->dev, "device is in pg\n"); return false; } if (!dev->hbuf_is_ready) { - dev_dbg(&dev->pdev->dev, "hbuf is not ready\n"); + dev_dbg(dev->dev, "hbuf is not ready\n"); return false; } @@ -453,7 +500,7 @@ bool mei_hbuf_acquire(struct mei_device *dev) * * Locking: called under "dev->device_lock" lock * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_cl_disconnect(struct mei_cl *cl) { @@ -471,9 +518,9 @@ int mei_cl_disconnect(struct mei_cl *cl) if (cl->state != MEI_FILE_DISCONNECTING) return 0; - rets = pm_runtime_get(&dev->pdev->dev); + rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { - pm_runtime_put_noidle(&dev->pdev->dev); + pm_runtime_put_noidle(dev->dev); cl_err(dev, cl, "rpm: get failed %d\n", rets); return rets; } @@ -484,7 +531,8 @@ int mei_cl_disconnect(struct mei_cl *cl) goto free; } - cb->fop_type = MEI_FOP_CLOSE; + cb->fop_type = MEI_FOP_DISCONNECT; + if (mei_hbuf_acquire(dev)) { if (mei_hbm_cl_disconnect_req(dev, cl)) { rets = -ENODEV; @@ -501,7 +549,7 @@ int mei_cl_disconnect(struct mei_cl *cl) } mutex_unlock(&dev->device_lock); - wait_event_timeout(dev->wait_recvd_msg, + wait_event_timeout(cl->wait, MEI_FILE_DISCONNECTED == cl->state, mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); @@ -519,8 +567,8 @@ int mei_cl_disconnect(struct mei_cl *cl) mei_io_list_flush(&dev->ctrl_wr_list, cl); free: cl_dbg(dev, cl, "rpm: autosuspend\n"); - pm_runtime_mark_last_busy(&dev->pdev->dev); - pm_runtime_put_autosuspend(&dev->pdev->dev); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); mei_io_cb_free(cb); return rets; @@ -533,7 +581,7 @@ free: * * @cl: private data of the file object * - * returns true if other client is connected, false - otherwise. + * Return: true if other client is connected, false - otherwise. */ bool mei_cl_is_other_connecting(struct mei_cl *cl) { @@ -560,10 +608,11 @@ bool mei_cl_is_other_connecting(struct mei_cl *cl) * mei_cl_connect - connect host client to the me one * * @cl: host client + * @file: pointer to file structure * * Locking: called under "dev->device_lock" lock * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_cl_connect(struct mei_cl *cl, struct file *file) { @@ -576,9 +625,9 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) dev = cl->dev; - rets = pm_runtime_get(&dev->pdev->dev); + rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { - pm_runtime_put_noidle(&dev->pdev->dev); + pm_runtime_put_noidle(dev->dev); cl_err(dev, cl, "rpm: get failed %d\n", rets); return rets; } @@ -606,7 +655,7 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) } mutex_unlock(&dev->device_lock); - wait_event_timeout(dev->wait_recvd_msg, + wait_event_timeout(cl->wait, (cl->state == MEI_FILE_CONNECTED || cl->state == MEI_FILE_DISCONNECTED), mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); @@ -626,8 +675,8 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) out: cl_dbg(dev, cl, "rpm: autosuspend\n"); - pm_runtime_mark_last_busy(&dev->pdev->dev); - pm_runtime_put_autosuspend(&dev->pdev->dev); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); mei_io_cb_free(cb); return rets; @@ -638,7 +687,7 @@ out: * * @cl: private data of the file object * - * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise. + * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. * -ENOENT if mei_cl is not present * -EINVAL if single_recv_buf == 0 */ @@ -646,26 +695,21 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl) { struct mei_device *dev; struct mei_me_client *me_cl; - int id; if (WARN_ON(!cl || !cl->dev)) return -EINVAL; dev = cl->dev; - if (!dev->me_clients_num) - return 0; - if (cl->mei_flow_ctrl_creds > 0) return 1; - id = mei_me_cl_by_id(dev, cl->me_client_id); - if (id < 0) { + me_cl = mei_me_cl_by_id(dev, cl->me_client_id); + if (!me_cl) { cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); - return id; + return -ENOENT; } - me_cl = &dev->me_clients[id]; if (me_cl->mei_flow_ctrl_creds) { if (WARN_ON(me_cl->props.single_recv_buf == 0)) return -EINVAL; @@ -679,7 +723,7 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl) * * @cl: private data of the file object * - * @returns + * Return: * 0 on success * -ENOENT when me client is not found * -EINVAL when ctrl credits are <= 0 @@ -688,21 +732,19 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) { struct mei_device *dev; struct mei_me_client *me_cl; - int id; if (WARN_ON(!cl || !cl->dev)) return -EINVAL; dev = cl->dev; - id = mei_me_cl_by_id(dev, cl->me_client_id); - if (id < 0) { + me_cl = mei_me_cl_by_id(dev, cl->me_client_id); + if (!me_cl) { cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); - return id; + return -ENOENT; } - me_cl = &dev->me_clients[id]; - if (me_cl->props.single_recv_buf != 0) { + if (me_cl->props.single_recv_buf) { if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) return -EINVAL; me_cl->mei_flow_ctrl_creds--; @@ -718,15 +760,16 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) * mei_cl_read_start - the start read client message function. * * @cl: host client + * @length: number of bytes to read * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_cl_read_start(struct mei_cl *cl, size_t length) { struct mei_device *dev; struct mei_cl_cb *cb; + struct mei_me_client *me_cl; int rets; - int i; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; @@ -740,15 +783,15 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) cl_dbg(dev, cl, "read is pending.\n"); return -EBUSY; } - i = mei_me_cl_by_id(dev, cl->me_client_id); - if (i < 0) { + me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); + if (!me_cl) { cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); return -ENOTTY; } - rets = pm_runtime_get(&dev->pdev->dev); + rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { - pm_runtime_put_noidle(&dev->pdev->dev); + pm_runtime_put_noidle(dev->dev); cl_err(dev, cl, "rpm: get failed %d\n", rets); return rets; } @@ -760,7 +803,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) } /* always allocate at least client max message */ - length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length); + length = max_t(size_t, length, me_cl->props.max_msg_length); rets = mei_io_cb_alloc_resp_buf(cb, length); if (rets) goto out; @@ -780,8 +823,8 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) out: cl_dbg(dev, cl, "rpm: autosuspend\n"); - pm_runtime_mark_last_busy(&dev->pdev->dev); - pm_runtime_put_autosuspend(&dev->pdev->dev); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); if (rets) mei_io_cb_free(cb); @@ -797,7 +840,7 @@ out: * @cb: callback block. * @cmpl_list: complete list. * - * returns 0, OK; otherwise error. + * Return: 0, OK; otherwise error. */ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) @@ -874,12 +917,13 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, /** * mei_cl_write - submit a write cb to mei device - assumes device_lock is locked + * assumes device_lock is locked * * @cl: host client - * @cl: write callback with filled data + * @cb: write callback with filled data + * @blocking: block until completed * - * returns number of bytes sent on success, <0 on failure. + * Return: number of bytes sent on success, <0 on failure. */ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) { @@ -900,11 +944,11 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) buf = &cb->request_buffer; - cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size); + cl_dbg(dev, cl, "size=%d\n", buf->size); - rets = pm_runtime_get(&dev->pdev->dev); + rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { - pm_runtime_put_noidle(&dev->pdev->dev); + pm_runtime_put_noidle(dev->dev); cl_err(dev, cl, "rpm: get failed %d\n", rets); return rets; } @@ -979,8 +1023,8 @@ out: rets = buf->size; err: cl_dbg(dev, cl, "rpm: autosuspend\n"); - pm_runtime_mark_last_busy(&dev->pdev->dev); - pm_runtime_put_autosuspend(&dev->pdev->dev); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); return rets; } @@ -1016,7 +1060,7 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) /** * mei_cl_all_disconnect - disconnect forcefully all connected clients * - * @dev - mei device + * @dev: mei device */ void mei_cl_all_disconnect(struct mei_device *dev) @@ -1034,11 +1078,12 @@ void mei_cl_all_disconnect(struct mei_device *dev) /** * mei_cl_all_wakeup - wake up all readers and writers they can be interrupted * - * @dev - mei device + * @dev: mei device */ void mei_cl_all_wakeup(struct mei_device *dev) { struct mei_cl *cl; + list_for_each_entry(cl, &dev->file_list, link) { if (waitqueue_active(&cl->rx_wait)) { cl_dbg(dev, cl, "Waking up reading client!\n"); @@ -1053,8 +1098,8 @@ void mei_cl_all_wakeup(struct mei_device *dev) /** * mei_cl_all_write_clear - clear all pending writes - - * @dev - mei device + * + * @dev: mei device */ void mei_cl_all_write_clear(struct mei_device *dev) { diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 96d5de0389f9..d9d0c1525259 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -24,8 +24,15 @@ #include "mei_dev.h" -int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *cuuid); -int mei_me_cl_by_id(struct mei_device *dev, u8 client_id); +struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev, + const uuid_le *cuuid); +struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id); + +struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev, + const uuid_le *uuid, u8 client_id); + +void mei_me_cl_remove(struct mei_device *dev, + const uuid_le *uuid, u8 client_id); /* * MEI IO Functions @@ -45,6 +52,8 @@ static inline void mei_io_list_init(struct mei_cl_cb *list) { INIT_LIST_HEAD(&list->list); } +void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl); + /* * MEI Host Client Functions */ @@ -101,9 +110,9 @@ void mei_cl_all_write_clear(struct mei_device *dev); #define MEI_CL_PRM(cl) (cl)->host_client_id, (cl)->me_client_id #define cl_dbg(dev, cl, format, arg...) \ - dev_dbg(&(dev)->pdev->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) + dev_dbg((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) #define cl_err(dev, cl, format, arg...) \ - dev_err(&(dev)->pdev->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) + dev_err((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) #endif /* _MEI_CLIENT_H_ */ diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index ced5b777c70f..ce1566715f80 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -17,7 +17,6 @@ #include <linux/kernel.h> #include <linux/device.h> #include <linux/debugfs.h> -#include <linux/pci.h> #include <linux/mei.h> @@ -28,39 +27,47 @@ 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 *cl; - const size_t bufsz = 1024; - char *buf = kzalloc(bufsz, GFP_KERNEL); - int i; + struct mei_me_client *me_cl; + size_t bufsz = 1; + char *buf; + int i = 0; int pos = 0; int ret; - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, - " |id|addr| UUID |con|msg len|\n"); +#define HDR " |id|addr| UUID |con|msg len|sb|\n" mutex_lock(&dev->device_lock); + 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); + return -ENOMEM; + } + + pos += scnprintf(buf + pos, bufsz - pos, HDR); + /* if the driver is not enabled the list won't be consistent */ if (dev->dev_state != MEI_DEV_ENABLED) goto out; - for (i = 0; i < dev->me_clients_num; i++) { - cl = &dev->me_clients[i]; + list_for_each_entry(me_cl, &dev->me_clients, list) { /* skip me clients that cannot be connected */ - if (cl->props.max_number_of_connections == 0) + if (me_cl->props.max_number_of_connections == 0) continue; pos += scnprintf(buf + pos, bufsz - pos, - "%2d|%2d|%4d|%pUl|%3d|%7d|\n", - i, cl->client_id, - cl->props.fixed_address, - &cl->props.protocol_name, - cl->props.max_number_of_connections, - cl->props.max_msg_length); + "%2d|%2d|%4d|%pUl|%3d|%7d|%2d|\n", + i++, me_cl->client_id, + me_cl->props.fixed_address, + &me_cl->props.protocol_name, + me_cl->props.max_number_of_connections, + me_cl->props.max_msg_length, + me_cl->props.single_recv_buf); } out: mutex_unlock(&dev->device_lock); @@ -98,7 +105,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, mutex_lock(&dev->device_lock); - /* if the driver is not enabled the list won't b consitent */ + /* if the driver is not enabled the list won't be consistent */ if (dev->dev_state != MEI_DEV_ENABLED) goto out; @@ -135,8 +142,13 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, if (!buf) return -ENOMEM; - pos += scnprintf(buf + pos, bufsz - pos, "%s\n", + pos += scnprintf(buf + pos, bufsz - pos, "dev: %s\n", mei_dev_state_str(dev->dev_state)); + pos += scnprintf(buf + pos, bufsz - pos, "hbm: %s\n", + mei_hbm_state_str(dev->hbm_state)); + pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n", + mei_pg_is_enabled(dev) ? "ENABLED" : "DISABLED", + mei_pg_state_str(mei_pg_state(dev))); ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); kfree(buf); return ret; @@ -149,7 +161,8 @@ static const struct file_operations mei_dbgfs_fops_devstate = { /** * mei_dbgfs_deregister - Remove the debugfs files and directories - * @mei - pointer to mei device private data + * + * @dev: the mei device structure */ void mei_dbgfs_deregister(struct mei_device *dev) { @@ -160,12 +173,17 @@ void mei_dbgfs_deregister(struct mei_device *dev) } /** - * Add the debugfs files + * mei_dbgfs_register - Add the debugfs files * + * @dev: the mei device structure + * @name: the mei device name + * + * Return: 0 on success, <0 on failure. */ int mei_dbgfs_register(struct mei_device *dev, const char *name) { struct dentry *dir, *f; + dir = debugfs_create_dir(name, NULL); if (!dir) return -ENOMEM; @@ -173,19 +191,19 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name) f = debugfs_create_file("meclients", S_IRUSR, dir, dev, &mei_dbgfs_fops_meclients); if (!f) { - dev_err(&dev->pdev->dev, "meclients: registration failed\n"); + dev_err(dev->dev, "meclients: registration failed\n"); goto err; } f = debugfs_create_file("active", S_IRUSR, dir, dev, &mei_dbgfs_fops_active); if (!f) { - dev_err(&dev->pdev->dev, "meclients: registration failed\n"); + dev_err(dev->dev, "meclients: registration failed\n"); goto err; } f = debugfs_create_file("devstate", S_IRUSR, dir, dev, &mei_dbgfs_fops_devstate); if (!f) { - dev_err(&dev->pdev->dev, "devstate: registration failed\n"); + dev_err(dev->dev, "devstate: registration failed\n"); goto err; } dev->dbgfs_dir = dir; diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 804106209d76..49a2653d91a5 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -15,16 +15,34 @@ */ #include <linux/export.h> -#include <linux/pci.h> #include <linux/sched.h> #include <linux/wait.h> -#include <linux/mei.h> #include <linux/pm_runtime.h> +#include <linux/slab.h> + +#include <linux/mei.h> #include "mei_dev.h" #include "hbm.h" #include "client.h" +static const char *mei_hbm_status_str(enum mei_hbm_status status) +{ +#define MEI_HBM_STATUS(status) case MEI_HBMS_##status: return #status + switch (status) { + MEI_HBM_STATUS(SUCCESS); + MEI_HBM_STATUS(CLIENT_NOT_FOUND); + MEI_HBM_STATUS(ALREADY_EXISTS); + MEI_HBM_STATUS(REJECTED); + MEI_HBM_STATUS(INVALID_PARAMETER); + MEI_HBM_STATUS(NOT_ALLOWED); + MEI_HBM_STATUS(ALREADY_STARTED); + MEI_HBM_STATUS(NOT_STARTED); + default: return "unknown"; + } +#undef MEI_HBM_STATUS +}; + static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status) { #define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status @@ -39,13 +57,29 @@ static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status) #undef MEI_CL_CCS } +const char *mei_hbm_state_str(enum mei_hbm_state state) +{ +#define MEI_HBM_STATE(state) case MEI_HBM_##state: return #state + switch (state) { + MEI_HBM_STATE(IDLE); + MEI_HBM_STATE(STARTING); + MEI_HBM_STATE(STARTED); + MEI_HBM_STATE(ENUM_CLIENTS); + MEI_HBM_STATE(CLIENT_PROPERTIES); + MEI_HBM_STATE(STOPPED); + default: + return "unknown"; + } +#undef MEI_HBM_STATE +} + /** * mei_cl_conn_status_to_errno - convert client connect response * status to error code * * @status: client connect response status * - * returns corresponding error code + * Return: corresponding error code */ static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status) { @@ -71,60 +105,54 @@ void mei_hbm_idle(struct mei_device *dev) } /** - * mei_hbm_reset - reset hbm counters and book keeping data structurs + * mei_me_cl_remove_all - remove all me clients * * @dev: the device structure */ -void mei_hbm_reset(struct mei_device *dev) +static void mei_me_cl_remove_all(struct mei_device *dev) { - dev->me_clients_num = 0; - dev->me_client_presentation_num = 0; - dev->me_client_index = 0; - - kfree(dev->me_clients); - dev->me_clients = NULL; + struct mei_me_client *me_cl, *next; - mei_hbm_idle(dev); + list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) { + list_del(&me_cl->list); + kfree(me_cl); + } } /** - * mei_hbm_me_cl_allocate - allocates storage for me clients + * mei_hbm_reset - reset hbm counters and book keeping data structurs * * @dev: the device structure - * - * returns 0 on success -ENOMEM on allocation failure */ -static int mei_hbm_me_cl_allocate(struct mei_device *dev) +void mei_hbm_reset(struct mei_device *dev) { - struct mei_me_client *clients; - int b; + dev->me_client_index = 0; - mei_hbm_reset(dev); + mei_me_cl_remove_all(dev); - /* count how many ME clients we have */ - for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX) - dev->me_clients_num++; + mei_hbm_idle(dev); +} - if (dev->me_clients_num == 0) - return 0; +/** + * mei_hbm_hdr - construct hbm header + * + * @hdr: hbm header + * @length: payload length + */ - dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n", - dev->me_clients_num * sizeof(struct mei_me_client)); - /* allocate storage for ME clients representation */ - clients = kcalloc(dev->me_clients_num, - sizeof(struct mei_me_client), GFP_KERNEL); - if (!clients) { - dev_err(&dev->pdev->dev, "memory allocation for ME clients failed.\n"); - return -ENOMEM; - } - dev->me_clients = clients; - return 0; +static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length) +{ + hdr->host_addr = 0; + hdr->me_addr = 0; + hdr->length = length; + hdr->msg_complete = 1; + hdr->reserved = 0; } /** * mei_hbm_cl_hdr - construct client hbm header * - * @cl: - client + * @cl: client * @hbm_cmd: host bus message command * @buf: buffer for cl header * @len: buffer length @@ -142,38 +170,87 @@ void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len) } /** - * mei_hbm_cl_addr_equal - tells if they have the same address + * mei_hbm_cl_write - write simple hbm client message * - * @cl: - client - * @buf: buffer with cl header + * @dev: the device structure + * @cl: client + * @hbm_cmd: host bus message command + * @len: buffer length * - * returns true if addresses are the same + * Return: 0 on success, <0 on failure. */ static inline -bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf) +int mei_hbm_cl_write(struct mei_device *dev, + struct mei_cl *cl, u8 hbm_cmd, size_t len) +{ + struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; + + mei_hbm_hdr(mei_hdr, len); + mei_hbm_cl_hdr(cl, hbm_cmd, dev->wr_msg.data, len); + + return mei_write_message(dev, mei_hdr, dev->wr_msg.data); +} + +/** + * mei_hbm_cl_addr_equal - check if the client's and + * the message address match + * + * @cl: client + * @cmd: hbm client message + * + * Return: true if addresses are the same + */ +static inline +bool mei_hbm_cl_addr_equal(struct mei_cl *cl, struct mei_hbm_cl_cmd *cmd) { - struct mei_hbm_cl_cmd *cmd = buf; return cl->host_client_id == cmd->host_addr && cl->me_client_id == cmd->me_addr; } +/** + * mei_hbm_cl_find_by_cmd - find recipient client + * + * @dev: the device structure + * @buf: a buffer with hbm cl command + * + * Return: the recipient client or NULL if not found + */ +static inline +struct mei_cl *mei_hbm_cl_find_by_cmd(struct mei_device *dev, void *buf) +{ + struct mei_hbm_cl_cmd *cmd = (struct mei_hbm_cl_cmd *)buf; + struct mei_cl *cl; + + list_for_each_entry(cl, &dev->file_list, link) + if (mei_hbm_cl_addr_equal(cl, cmd)) + return cl; + return NULL; +} + +/** + * mei_hbm_start_wait - wait for start response message. + * + * @dev: the device structure + * + * Return: 0 on success and < 0 on failure + */ int mei_hbm_start_wait(struct mei_device *dev) { int ret; - if (dev->hbm_state > MEI_HBM_START) + + if (dev->hbm_state > MEI_HBM_STARTING) return 0; mutex_unlock(&dev->device_lock); - ret = wait_event_interruptible_timeout(dev->wait_recvd_msg, - dev->hbm_state == MEI_HBM_IDLE || - dev->hbm_state >= MEI_HBM_STARTED, + ret = wait_event_timeout(dev->wait_hbm_start, + dev->hbm_state != MEI_HBM_STARTING, mei_secs_to_jiffies(MEI_HBM_TIMEOUT)); mutex_lock(&dev->device_lock); - if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) { + if (ret == 0 && (dev->hbm_state <= MEI_HBM_STARTING)) { dev->hbm_state = MEI_HBM_IDLE; - dev_err(&dev->pdev->dev, "waiting for mei start failed\n"); + dev_err(dev->dev, "waiting for mei start failed\n"); return -ETIME; } return 0; @@ -184,7 +261,7 @@ int mei_hbm_start_wait(struct mei_device *dev) * * @dev: the device structure * - * returns 0 on success and < 0 on failure + * Return: 0 on success and < 0 on failure */ int mei_hbm_start_req(struct mei_device *dev) { @@ -193,6 +270,8 @@ int mei_hbm_start_req(struct mei_device *dev) const size_t len = sizeof(struct hbm_host_version_request); int ret; + mei_hbm_reset(dev); + mei_hbm_hdr(mei_hdr, len); /* host start message */ @@ -205,12 +284,12 @@ int mei_hbm_start_req(struct mei_device *dev) dev->hbm_state = MEI_HBM_IDLE; ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); if (ret) { - dev_err(&dev->pdev->dev, "version message write failed: ret = %d\n", + dev_err(dev->dev, "version message write failed: ret = %d\n", ret); return ret; } - dev->hbm_state = MEI_HBM_START; + dev->hbm_state = MEI_HBM_STARTING; dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; return 0; } @@ -220,7 +299,7 @@ int mei_hbm_start_req(struct mei_device *dev) * * @dev: the device structure * - * returns 0 on success and < 0 on failure + * Return: 0 on success and < 0 on failure */ static int mei_hbm_enum_clients_req(struct mei_device *dev) { @@ -238,7 +317,7 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev) ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); if (ret) { - dev_err(&dev->pdev->dev, "enumeration request write failed: ret = %d.\n", + dev_err(dev->dev, "enumeration request write failed: ret = %d.\n", ret); return ret; } @@ -247,12 +326,38 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev) return 0; } +/* + * mei_hbm_me_cl_add - add new me client to the list + * + * @dev: the device structure + * @res: hbm property response + * + * Return: 0 on success and -ENOMEM on allocation failure + */ + +static int mei_hbm_me_cl_add(struct mei_device *dev, + struct hbm_props_response *res) +{ + struct mei_me_client *me_cl; + + me_cl = kzalloc(sizeof(struct mei_me_client), GFP_KERNEL); + if (!me_cl) + return -ENOMEM; + + me_cl->props = res->client_properties; + me_cl->client_id = res->me_addr; + me_cl->mei_flow_ctrl_creds = 0; + + list_add(&me_cl->list, &dev->me_clients); + return 0; +} + /** * mei_hbm_prop_req - request property for a single client * * @dev: the device structure * - * returns 0 on success and < 0 on failure + * Return: 0 on success and < 0 on failure */ static int mei_hbm_prop_req(struct mei_device *dev) @@ -262,11 +367,8 @@ static int mei_hbm_prop_req(struct mei_device *dev) struct hbm_props_request *prop_req; const size_t len = sizeof(struct hbm_props_request); unsigned long next_client_index; - unsigned long client_num; int ret; - client_num = dev->me_client_presentation_num; - next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, dev->me_client_index); @@ -278,21 +380,17 @@ static int mei_hbm_prop_req(struct mei_device *dev) return 0; } - dev->me_clients[client_num].client_id = next_client_index; - dev->me_clients[client_num].mei_flow_ctrl_creds = 0; - mei_hbm_hdr(mei_hdr, len); prop_req = (struct hbm_props_request *)dev->wr_msg.data; memset(prop_req, 0, sizeof(struct hbm_props_request)); - prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD; - prop_req->address = next_client_index; + prop_req->me_addr = next_client_index; ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); if (ret) { - dev_err(&dev->pdev->dev, "properties request write failed: ret = %d\n", + dev_err(dev->dev, "properties request write failed: ret = %d\n", ret); return ret; } @@ -309,7 +407,8 @@ static int mei_hbm_prop_req(struct mei_device *dev) * @dev: the device structure * @pg_cmd: the pg command code * - * This function returns -EIO on write failure + * Return: -EIO on write failure + * -EOPNOTSUPP if the operation is not supported by the protocol */ int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd) { @@ -318,6 +417,9 @@ int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd) const size_t len = sizeof(struct hbm_power_gate); int ret; + if (!dev->hbm_f_pg_supported) + return -EOPNOTSUPP; + mei_hbm_hdr(mei_hdr, len); req = (struct hbm_power_gate *)dev->wr_msg.data; @@ -326,7 +428,7 @@ int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd) ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); if (ret) - dev_err(&dev->pdev->dev, "power gate command write failed.\n"); + dev_err(dev->dev, "power gate command write failed.\n"); return ret; } EXPORT_SYMBOL_GPL(mei_hbm_pg); @@ -334,10 +436,9 @@ EXPORT_SYMBOL_GPL(mei_hbm_pg); /** * mei_hbm_stop_req - send stop request message * - * @dev - mei device - * @cl: client info + * @dev: mei device * - * This function returns -EIO on write failure + * Return: -EIO on write failure */ static int mei_hbm_stop_req(struct mei_device *dev) { @@ -361,19 +462,14 @@ static int mei_hbm_stop_req(struct mei_device *dev) * @dev: the device structure * @cl: client info * - * This function returns -EIO on write failure + * Return: -EIO on write failure */ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl) { - struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; const size_t len = sizeof(struct hbm_flow_control); - mei_hbm_hdr(mei_hdr, len); - mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len); - cl_dbg(dev, cl, "sending flow control\n"); - - return mei_write_message(dev, mei_hdr, dev->wr_msg.data); + return mei_hbm_cl_write(dev, cl, MEI_FLOW_CONTROL_CMD, len); } /** @@ -382,31 +478,26 @@ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl) * @dev: the device structure * @flow: flow control. * - * return 0 on success, < 0 otherwise + * Return: 0 on success, < 0 otherwise */ static int mei_hbm_add_single_flow_creds(struct mei_device *dev, struct hbm_flow_control *flow) { struct mei_me_client *me_cl; - int id; - id = mei_me_cl_by_id(dev, flow->me_addr); - if (id < 0) { - dev_err(&dev->pdev->dev, "no such me client %d\n", + me_cl = mei_me_cl_by_id(dev, flow->me_addr); + if (!me_cl) { + dev_err(dev->dev, "no such me client %d\n", flow->me_addr); - return id; + return -ENOENT; } - me_cl = &dev->me_clients[id]; - if (me_cl->props.single_recv_buf) { - me_cl->mei_flow_ctrl_creds++; - dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n", - flow->me_addr); - dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n", - me_cl->mei_flow_ctrl_creds); - } else { - BUG(); /* error in flow control */ - } + if (WARN_ON(me_cl->props.single_recv_buf == 0)) + return -EINVAL; + + me_cl->mei_flow_ctrl_creds++; + dev_dbg(dev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n", + flow->me_addr, me_cl->mei_flow_ctrl_creds); return 0; } @@ -418,7 +509,7 @@ static int mei_hbm_add_single_flow_creds(struct mei_device *dev, * @flow_control: flow control response bus message */ static void mei_hbm_cl_flow_control_res(struct mei_device *dev, - struct hbm_flow_control *flow_control) + struct hbm_flow_control *flow_control) { struct mei_cl *cl; @@ -428,16 +519,11 @@ static void mei_hbm_cl_flow_control_res(struct mei_device *dev, return; } - /* normal connection */ - list_for_each_entry(cl, &dev->file_list, link) { - if (mei_hbm_cl_addr_equal(cl, flow_control)) { - cl->mei_flow_ctrl_creds++; - dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n", - flow_control->host_addr, flow_control->me_addr); - dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n", - cl->mei_flow_ctrl_creds); - break; - } + cl = mei_hbm_cl_find_by_cmd(dev, flow_control); + if (cl) { + cl->mei_flow_ctrl_creds++; + cl_dbg(dev, cl, "flow control creds = %d.\n", + cl->mei_flow_ctrl_creds); } } @@ -448,17 +534,13 @@ static void mei_hbm_cl_flow_control_res(struct mei_device *dev, * @dev: the device structure * @cl: a client to disconnect from * - * This function returns -EIO on write failure + * Return: -EIO on write failure */ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl) { - struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; const size_t len = sizeof(struct hbm_client_connect_request); - mei_hbm_hdr(mei_hdr, len); - mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, dev->wr_msg.data, len); - - return mei_write_message(dev, mei_hdr, dev->wr_msg.data); + return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_REQ_CMD, len); } /** @@ -467,53 +549,34 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl) * @dev: the device structure * @cl: a client to disconnect from * - * This function returns -EIO on write failure + * Return: -EIO on write failure */ int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl) { - struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; const size_t len = sizeof(struct hbm_client_connect_response); - mei_hbm_hdr(mei_hdr, len); - mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD, dev->wr_msg.data, len); - - return mei_write_message(dev, mei_hdr, dev->wr_msg.data); + return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_RES_CMD, len); } /** - * mei_hbm_cl_disconnect_res - disconnect response from ME + * mei_hbm_cl_disconnect_res - update the client state according + * disconnect response * - * @dev: the device structure - * @rs: disconnect response bus message + * @cl: mei host client + * @cmd: disconnect client response host bus message */ -static void mei_hbm_cl_disconnect_res(struct mei_device *dev, - struct hbm_client_connect_response *rs) +static void mei_hbm_cl_disconnect_res(struct mei_cl *cl, + struct mei_hbm_cl_cmd *cmd) { - struct mei_cl *cl; - struct mei_cl_cb *cb, *next; + struct hbm_client_connect_response *rs = + (struct hbm_client_connect_response *)cmd; - dev_dbg(&dev->pdev->dev, "hbm: disconnect response cl:host=%02d me=%02d status=%d\n", + dev_dbg(cl->dev->dev, "hbm: disconnect response cl:host=%02d me=%02d status=%d\n", rs->me_addr, rs->host_addr, rs->status); - list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) { - cl = cb->cl; - - /* this should not happen */ - if (WARN_ON(!cl)) { - list_del(&cb->list); - return; - } - - if (mei_hbm_cl_addr_equal(cl, rs)) { - list_del(&cb->list); - if (rs->status == MEI_CL_DISCONN_SUCCESS) - cl->state = MEI_FILE_DISCONNECTED; - - cl->status = 0; - cl->timer_count = 0; - break; - } - } + if (rs->status == MEI_CL_DISCONN_SUCCESS) + cl->state = MEI_FILE_DISCONNECTED; + cl->status = 0; } /** @@ -522,38 +585,55 @@ static void mei_hbm_cl_disconnect_res(struct mei_device *dev, * @dev: the device structure * @cl: a client to connect to * - * returns -EIO on write failure + * Return: -EIO on write failure */ int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl) { - struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; const size_t len = sizeof(struct hbm_client_connect_request); - mei_hbm_hdr(mei_hdr, len); - mei_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, dev->wr_msg.data, len); + return mei_hbm_cl_write(dev, cl, CLIENT_CONNECT_REQ_CMD, len); +} - return mei_write_message(dev, mei_hdr, dev->wr_msg.data); +/** + * mei_hbm_cl_connect_res - update the client state according + * connection response + * + * @cl: mei host client + * @cmd: connect client response host bus message + */ +static void mei_hbm_cl_connect_res(struct mei_cl *cl, + struct mei_hbm_cl_cmd *cmd) +{ + struct hbm_client_connect_response *rs = + (struct hbm_client_connect_response *)cmd; + + dev_dbg(cl->dev->dev, "hbm: connect response cl:host=%02d me=%02d status=%s\n", + rs->me_addr, rs->host_addr, + mei_cl_conn_status_str(rs->status)); + + if (rs->status == MEI_CL_CONN_SUCCESS) + cl->state = MEI_FILE_CONNECTED; + else + cl->state = MEI_FILE_DISCONNECTED; + cl->status = mei_cl_conn_status_to_errno(rs->status); } /** - * mei_hbm_cl_connect_res - connect response from the ME + * mei_hbm_cl_res - process hbm response received on behalf + * an client * * @dev: the device structure - * @rs: connect response bus message + * @rs: hbm client message + * @fop_type: file operation type */ -static void mei_hbm_cl_connect_res(struct mei_device *dev, - struct hbm_client_connect_response *rs) +static void mei_hbm_cl_res(struct mei_device *dev, + struct mei_hbm_cl_cmd *rs, + enum mei_cb_file_ops fop_type) { - struct mei_cl *cl; struct mei_cl_cb *cb, *next; - dev_dbg(&dev->pdev->dev, "hbm: connect response cl:host=%02d me=%02d status=%s\n", - rs->me_addr, rs->host_addr, - mei_cl_conn_status_str(rs->status)); - cl = NULL; - list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) { cl = cb->cl; @@ -563,7 +643,7 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, continue; } - if (cb->fop_type != MEI_FOP_CONNECT) + if (cb->fop_type != fop_type) continue; if (mei_hbm_cl_addr_equal(cl, rs)) { @@ -575,12 +655,19 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, if (!cl) return; + switch (fop_type) { + case MEI_FOP_CONNECT: + mei_hbm_cl_connect_res(cl, rs); + break; + case MEI_FOP_DISCONNECT: + mei_hbm_cl_disconnect_res(cl, rs); + break; + default: + return; + } + cl->timer_count = 0; - if (rs->status == MEI_CL_CONN_SUCCESS) - cl->state = MEI_FILE_CONNECTED; - else - cl->state = MEI_FILE_DISCONNECTED; - cl->status = mei_cl_conn_status_to_errno(rs->status); + wake_up(&cl->wait); } @@ -591,7 +678,7 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, * @dev: the device structure. * @disconnect_req: disconnect request bus message from the me * - * returns -ENOMEM on allocation failure + * Return: -ENOMEM on allocation failure */ static int mei_hbm_fw_disconnect_req(struct mei_device *dev, struct hbm_client_connect_request *disconnect_req) @@ -599,34 +686,46 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev, struct mei_cl *cl; struct mei_cl_cb *cb; - list_for_each_entry(cl, &dev->file_list, link) { - if (mei_hbm_cl_addr_equal(cl, disconnect_req)) { - dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n", - disconnect_req->host_addr, - disconnect_req->me_addr); - cl->state = MEI_FILE_DISCONNECTED; - cl->timer_count = 0; - - cb = mei_io_cb_init(cl, 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); + cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req); + if (cl) { + cl_dbg(dev, cl, "disconnect request received\n"); + cl->state = MEI_FILE_DISCONNECTED; + cl->timer_count = 0; - break; - } + cb = mei_io_cb_init(cl, 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); } return 0; } +/** + * mei_hbm_config_features - check what hbm features and commands + * are supported by the fw + * + * @dev: the device structure + */ +static void mei_hbm_config_features(struct mei_device *dev) +{ + /* Power Gating Isolation Support */ + dev->hbm_f_pg_supported = 0; + if (dev->version.major_version > HBM_MAJOR_VERSION_PGI) + dev->hbm_f_pg_supported = 1; + + if (dev->version.major_version == HBM_MAJOR_VERSION_PGI && + dev->version.minor_version >= HBM_MINOR_VERSION_PGI) + dev->hbm_f_pg_supported = 1; +} /** * mei_hbm_version_is_supported - checks whether the driver can * support the hbm version of the device * * @dev: the device structure - * returns true if driver can support hbm version of the device + * Return: true if driver can support hbm version of the device */ bool mei_hbm_version_is_supported(struct mei_device *dev) { @@ -640,44 +739,44 @@ bool mei_hbm_version_is_supported(struct mei_device *dev) * handle the read bus message cmd processing. * * @dev: the device structure - * @mei_hdr: header of bus message + * @hdr: header of bus message * - * returns 0 on success and < 0 on failure + * Return: 0 on success and < 0 on failure */ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) { struct mei_bus_message *mei_msg; - struct mei_me_client *me_client; struct hbm_host_version_response *version_res; - struct hbm_client_connect_response *connect_res; - struct hbm_client_connect_response *disconnect_res; - struct hbm_client_connect_request *disconnect_req; - struct hbm_flow_control *flow_control; struct hbm_props_response *props_res; struct hbm_host_enum_response *enum_res; + struct mei_hbm_cl_cmd *cl_cmd; + struct hbm_client_connect_request *disconnect_req; + struct hbm_flow_control *flow_control; + /* read the message to our buffer */ BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf)); mei_read_slots(dev, dev->rd_msg_buf, hdr->length); mei_msg = (struct mei_bus_message *)dev->rd_msg_buf; + cl_cmd = (struct mei_hbm_cl_cmd *)mei_msg; /* ignore spurious message and prevent reset nesting * hbm is put to idle during system reset */ if (dev->hbm_state == MEI_HBM_IDLE) { - dev_dbg(&dev->pdev->dev, "hbm: state is idle ignore spurious messages\n"); + dev_dbg(dev->dev, "hbm: state is idle ignore spurious messages\n"); return 0; } switch (mei_msg->hbm_cmd) { case HOST_START_RES_CMD: - dev_dbg(&dev->pdev->dev, "hbm: start: response message received.\n"); + dev_dbg(dev->dev, "hbm: start: response message received.\n"); dev->init_clients_timer = 0; version_res = (struct hbm_host_version_response *)mei_msg; - dev_dbg(&dev->pdev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n", + dev_dbg(dev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n", HBM_MAJOR_VERSION, HBM_MINOR_VERSION, version_res->me_max_version.major_version, version_res->me_max_version.minor_version); @@ -693,19 +792,21 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) } if (!mei_hbm_version_is_supported(dev)) { - dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n"); + dev_warn(dev->dev, "hbm: start: version mismatch - stopping the driver.\n"); dev->hbm_state = MEI_HBM_STOPPED; if (mei_hbm_stop_req(dev)) { - dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n"); + dev_err(dev->dev, "hbm: start: failed to send stop request\n"); return -EIO; } break; } + mei_hbm_config_features(dev); + if (dev->dev_state != MEI_DEV_INIT_CLIENTS || - dev->hbm_state != MEI_HBM_START) { - dev_err(&dev->pdev->dev, "hbm: start: state mismatch, [%d, %d]\n", + dev->hbm_state != MEI_HBM_STARTING) { + dev_err(dev->dev, "hbm: start: state mismatch, [%d, %d]\n", dev->dev_state, dev->hbm_state); return -EPROTO; } @@ -713,45 +814,39 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) dev->hbm_state = MEI_HBM_STARTED; if (mei_hbm_enum_clients_req(dev)) { - dev_err(&dev->pdev->dev, "hbm: start: failed to send enumeration request\n"); + dev_err(dev->dev, "hbm: start: failed to send enumeration request\n"); return -EIO; } - wake_up_interruptible(&dev->wait_recvd_msg); + wake_up(&dev->wait_hbm_start); break; case CLIENT_CONNECT_RES_CMD: - dev_dbg(&dev->pdev->dev, "hbm: client connect response: message received.\n"); - - connect_res = (struct hbm_client_connect_response *) mei_msg; - mei_hbm_cl_connect_res(dev, connect_res); - wake_up(&dev->wait_recvd_msg); + dev_dbg(dev->dev, "hbm: client connect response: message received.\n"); + mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_CONNECT); break; case CLIENT_DISCONNECT_RES_CMD: - dev_dbg(&dev->pdev->dev, "hbm: client disconnect response: message received.\n"); - - disconnect_res = (struct hbm_client_connect_response *) mei_msg; - mei_hbm_cl_disconnect_res(dev, disconnect_res); - wake_up(&dev->wait_recvd_msg); + dev_dbg(dev->dev, "hbm: client disconnect response: message received.\n"); + mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_DISCONNECT); break; case MEI_FLOW_CONTROL_CMD: - dev_dbg(&dev->pdev->dev, "hbm: client flow control response: message received.\n"); + dev_dbg(dev->dev, "hbm: client flow control response: message received.\n"); flow_control = (struct hbm_flow_control *) mei_msg; mei_hbm_cl_flow_control_res(dev, flow_control); break; case MEI_PG_ISOLATION_ENTRY_RES_CMD: - dev_dbg(&dev->pdev->dev, "power gate isolation entry response received\n"); + dev_dbg(dev->dev, "power gate isolation entry response received\n"); dev->pg_event = MEI_PG_EVENT_RECEIVED; if (waitqueue_active(&dev->wait_pg)) wake_up(&dev->wait_pg); break; case MEI_PG_ISOLATION_EXIT_REQ_CMD: - dev_dbg(&dev->pdev->dev, "power gate isolation exit request received\n"); + dev_dbg(dev->dev, "power gate isolation exit request received\n"); dev->pg_event = MEI_PG_EVENT_RECEIVED; if (waitqueue_active(&dev->wait_pg)) wake_up(&dev->wait_pg); @@ -761,44 +856,33 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) * this is HW initiated exit from PG. * Start runtime pm resume sequence to exit from PG. */ - pm_request_resume(&dev->pdev->dev); + pm_request_resume(dev->dev); break; case HOST_CLIENT_PROPERTIES_RES_CMD: - dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n"); + dev_dbg(dev->dev, "hbm: properties response: message received.\n"); dev->init_clients_timer = 0; - if (dev->me_clients == NULL) { - dev_err(&dev->pdev->dev, "hbm: properties response: mei_clients not allocated\n"); + if (dev->dev_state != MEI_DEV_INIT_CLIENTS || + dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) { + dev_err(dev->dev, "hbm: properties response: state mismatch, [%d, %d]\n", + dev->dev_state, dev->hbm_state); return -EPROTO; } props_res = (struct hbm_props_response *)mei_msg; - me_client = &dev->me_clients[dev->me_client_presentation_num]; if (props_res->status) { - dev_err(&dev->pdev->dev, "hbm: properties response: wrong status = %d\n", - props_res->status); + dev_err(dev->dev, "hbm: properties response: wrong status = %d %s\n", + props_res->status, + mei_hbm_status_str(props_res->status)); return -EPROTO; } - if (me_client->client_id != props_res->address) { - dev_err(&dev->pdev->dev, "hbm: properties response: address mismatch %d ?= %d\n", - me_client->client_id, props_res->address); - return -EPROTO; - } + mei_hbm_me_cl_add(dev, props_res); - if (dev->dev_state != MEI_DEV_INIT_CLIENTS || - dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) { - dev_err(&dev->pdev->dev, "hbm: properties response: state mismatch, [%d, %d]\n", - dev->dev_state, dev->hbm_state); - return -EPROTO; - } - - me_client->props = props_res->client_properties; dev->me_client_index++; - dev->me_client_presentation_num++; /* request property for the next client */ if (mei_hbm_prop_req(dev)) @@ -807,7 +891,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) break; case HOST_ENUM_RES_CMD: - dev_dbg(&dev->pdev->dev, "hbm: enumeration response: message received\n"); + dev_dbg(dev->dev, "hbm: enumeration response: message received\n"); dev->init_clients_timer = 0; @@ -815,20 +899,15 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) BUILD_BUG_ON(sizeof(dev->me_clients_map) < sizeof(enum_res->valid_addresses)); memcpy(dev->me_clients_map, enum_res->valid_addresses, - sizeof(enum_res->valid_addresses)); + sizeof(enum_res->valid_addresses)); if (dev->dev_state != MEI_DEV_INIT_CLIENTS || dev->hbm_state != MEI_HBM_ENUM_CLIENTS) { - dev_err(&dev->pdev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n", + dev_err(dev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n", dev->dev_state, dev->hbm_state); return -EPROTO; } - if (mei_hbm_me_cl_allocate(dev)) { - dev_err(&dev->pdev->dev, "hbm: enumeration response: cannot allocate clients array\n"); - return -ENOMEM; - } - dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES; /* first property request */ @@ -838,34 +917,34 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) break; case HOST_STOP_RES_CMD: - dev_dbg(&dev->pdev->dev, "hbm: stop response: message received\n"); + dev_dbg(dev->dev, "hbm: stop response: message received\n"); dev->init_clients_timer = 0; if (dev->hbm_state != MEI_HBM_STOPPED) { - dev_err(&dev->pdev->dev, "hbm: stop response: state mismatch, [%d, %d]\n", + dev_err(dev->dev, "hbm: stop response: state mismatch, [%d, %d]\n", dev->dev_state, dev->hbm_state); return -EPROTO; } dev->dev_state = MEI_DEV_POWER_DOWN; - dev_info(&dev->pdev->dev, "hbm: stop response: resetting.\n"); + dev_info(dev->dev, "hbm: stop response: resetting.\n"); /* force the reset */ return -EPROTO; break; case CLIENT_DISCONNECT_REQ_CMD: - dev_dbg(&dev->pdev->dev, "hbm: disconnect request: message received\n"); + dev_dbg(dev->dev, "hbm: disconnect request: message received\n"); disconnect_req = (struct hbm_client_connect_request *)mei_msg; mei_hbm_fw_disconnect_req(dev, disconnect_req); break; case ME_STOP_REQ_CMD: - dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n"); + dev_dbg(dev->dev, "hbm: stop request: message received\n"); dev->hbm_state = MEI_HBM_STOPPED; if (mei_hbm_stop_req(dev)) { - dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n"); + dev_err(dev->dev, "hbm: stop request: failed to send stop request\n"); return -EIO; } break; diff --git a/drivers/misc/mei/hbm.h b/drivers/misc/mei/hbm.h index 683eb2835cec..b7cd3d857fd5 100644 --- a/drivers/misc/mei/hbm.h +++ b/drivers/misc/mei/hbm.h @@ -25,29 +25,24 @@ struct mei_cl; * enum mei_hbm_state - host bus message protocol state * * @MEI_HBM_IDLE : protocol not started - * @MEI_HBM_START : start request message was sent + * @MEI_HBM_STARTING : start request message was sent + * @MEI_HBM_STARTED : start reply message was received * @MEI_HBM_ENUM_CLIENTS : enumeration request was sent * @MEI_HBM_CLIENT_PROPERTIES : acquiring clients properties + * @MEI_HBM_STOPPED : stopping exchange */ enum mei_hbm_state { MEI_HBM_IDLE = 0, - MEI_HBM_START, + MEI_HBM_STARTING, MEI_HBM_STARTED, MEI_HBM_ENUM_CLIENTS, MEI_HBM_CLIENT_PROPERTIES, MEI_HBM_STOPPED, }; -int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr); +const char *mei_hbm_state_str(enum mei_hbm_state state); -static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length) -{ - hdr->host_addr = 0; - hdr->me_addr = 0; - hdr->length = length; - hdr->msg_complete = 1; - hdr->reserved = 0; -} +int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr); void mei_hbm_idle(struct mei_device *dev); void mei_hbm_reset(struct mei_device *dev); diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index a9a0d08f758e..4f2fd6fc1e23 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -28,10 +28,10 @@ /** * mei_me_reg_read - Reads 32bit data from the mei device * - * @dev: the device structure + * @hw: the me hardware structure * @offset: offset from which to read the data * - * returns register value (u32) + * Return: register value (u32) */ static inline u32 mei_me_reg_read(const struct mei_me_hw *hw, unsigned long offset) @@ -43,7 +43,7 @@ static inline u32 mei_me_reg_read(const struct mei_me_hw *hw, /** * mei_me_reg_write - Writes 32bit data to the mei device * - * @dev: the device structure + * @hw: the me hardware structure * @offset: offset from which to write the data * @value: register value to write (u32) */ @@ -59,7 +59,7 @@ static inline void mei_me_reg_write(const struct mei_me_hw *hw, * * @dev: the device structure * - * returns ME_CB_RW register value (u32) + * Return: ME_CB_RW register value (u32) */ static u32 mei_me_mecbrw_read(const struct mei_device *dev) { @@ -68,9 +68,9 @@ static u32 mei_me_mecbrw_read(const struct mei_device *dev) /** * mei_me_mecsr_read - Reads 32bit data from the ME CSR * - * @dev: the device structure + * @hw: the me hardware structure * - * returns ME_CSR_HA register value (u32) + * Return: ME_CSR_HA register value (u32) */ static inline u32 mei_me_mecsr_read(const struct mei_me_hw *hw) { @@ -80,9 +80,9 @@ static inline u32 mei_me_mecsr_read(const struct mei_me_hw *hw) /** * mei_hcsr_read - Reads 32bit data from the host CSR * - * @dev: the device structure + * @hw: the me hardware structure * - * returns H_CSR register value (u32) + * Return: H_CSR register value (u32) */ static inline u32 mei_hcsr_read(const struct mei_me_hw *hw) { @@ -93,7 +93,8 @@ static inline u32 mei_hcsr_read(const struct mei_me_hw *hw) * mei_hcsr_set - writes H_CSR register to the mei device, * and ignores the H_IS bit for it is write-one-to-zero. * - * @dev: the device structure + * @hw: the me hardware structure + * @hcsr: new register value */ static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr) { @@ -101,6 +102,36 @@ static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr) mei_me_reg_write(hw, H_CSR, hcsr); } +/** + * mei_me_fw_status - read fw status register from pci config space + * + * @dev: mei device + * @fw_status: fw status register values + * + * Return: 0 on success, error otherwise + */ +static int mei_me_fw_status(struct mei_device *dev, + struct mei_fw_status *fw_status) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct mei_me_hw *hw = to_me_hw(dev); + const struct mei_fw_status *fw_src = &hw->cfg->fw_status; + int ret; + int i; + + if (!fw_status) + return -EINVAL; + + fw_status->count = fw_src->count; + for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { + ret = pci_read_config_dword(pdev, + fw_src->status[i], &fw_status->status[i]); + if (ret) + return ret; + } + + return 0; +} /** * mei_me_hw_config - configure hw dependent settings @@ -121,17 +152,19 @@ static void mei_me_hw_config(struct mei_device *dev) * mei_me_pg_state - translate internal pg state * to the mei power gating state * - * @hw - me hardware - * returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise + * @dev: mei device + * + * Return: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise */ static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); + return hw->pg_state; } /** - * mei_clear_interrupts - clear and stop interrupts + * mei_me_intr_clear - clear and stop interrupts * * @dev: the device structure */ @@ -139,6 +172,7 @@ 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); + if ((hcsr & H_IS) == H_IS) mei_me_reg_write(hw, H_CSR, hcsr); } @@ -151,12 +185,13 @@ 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); + hcsr |= H_IE; mei_hcsr_set(hw, hcsr); } /** - * mei_disable_interrupts - disables mei device interrupts + * mei_me_intr_disable - disables mei device interrupts * * @dev: the device structure */ @@ -164,6 +199,7 @@ 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); + hcsr &= ~H_IE; mei_hcsr_set(hw, hcsr); } @@ -190,6 +226,8 @@ static void mei_me_hw_reset_release(struct mei_device *dev) * * @dev: the device structure * @intr_enable: if interrupt should be enabled after reset. + * + * Return: always 0 */ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) { @@ -213,10 +251,10 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) hcsr = mei_hcsr_read(hw); if ((hcsr & H_RST) == 0) - dev_warn(&dev->pdev->dev, "H_RST is not set = 0x%08X", hcsr); + dev_warn(dev->dev, "H_RST is not set = 0x%08X", hcsr); if ((hcsr & H_RDY) == H_RDY) - dev_warn(&dev->pdev->dev, "H_RDY is not cleared 0x%08X", hcsr); + dev_warn(dev->dev, "H_RDY is not cleared 0x%08X", hcsr); if (intr_enable == false) mei_me_hw_reset_release(dev); @@ -227,26 +265,27 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) /** * mei_me_host_set_ready - enable device * - * @dev - mei device - * returns bool + * @dev: mei device */ - static void mei_me_host_set_ready(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); + hw->host_hw_state = mei_hcsr_read(hw); hw->host_hw_state |= H_IE | H_IG | H_RDY; mei_hcsr_set(hw, hw->host_hw_state); } + /** * mei_me_host_is_ready - check whether the host has turned ready * - * @dev - mei device - * returns bool + * @dev: mei device + * Return: bool */ static bool mei_me_host_is_ready(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); + hw->host_hw_state = mei_hcsr_read(hw); return (hw->host_hw_state & H_RDY) == H_RDY; } @@ -254,43 +293,53 @@ static bool mei_me_host_is_ready(struct mei_device *dev) /** * mei_me_hw_is_ready - check whether the me(hw) has turned ready * - * @dev - mei device - * returns bool + * @dev: mei device + * Return: bool */ static bool mei_me_hw_is_ready(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); + hw->me_hw_state = mei_me_mecsr_read(hw); return (hw->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA; } +/** + * mei_me_hw_ready_wait - wait until the me(hw) has turned ready + * or timeout is reached + * + * @dev: mei device + * Return: 0 on success, error otherwise + */ static int mei_me_hw_ready_wait(struct mei_device *dev) { - int err; - mutex_unlock(&dev->device_lock); - err = wait_event_interruptible_timeout(dev->wait_hw_ready, + wait_event_timeout(dev->wait_hw_ready, dev->recvd_hw_ready, mei_secs_to_jiffies(MEI_HW_READY_TIMEOUT)); mutex_lock(&dev->device_lock); - if (!err && !dev->recvd_hw_ready) { - if (!err) - err = -ETIME; - dev_err(&dev->pdev->dev, - "wait hw ready failed. status = %d\n", err); - return err; + if (!dev->recvd_hw_ready) { + dev_err(dev->dev, "wait hw ready failed\n"); + return -ETIME; } dev->recvd_hw_ready = false; return 0; } +/** + * mei_me_hw_start - hw start routine + * + * @dev: mei device + * Return: 0 on success, error otherwise + */ static int mei_me_hw_start(struct mei_device *dev) { int ret = mei_me_hw_ready_wait(dev); + if (ret) return ret; - dev_dbg(&dev->pdev->dev, "hw is ready\n"); + dev_dbg(dev->dev, "hw is ready\n"); mei_me_host_set_ready(dev); return ret; @@ -302,7 +351,7 @@ static int mei_me_hw_start(struct mei_device *dev) * * @dev: the device structure * - * returns number of filled slots + * Return: number of filled slots */ static unsigned char mei_hbuf_filled_slots(struct mei_device *dev) { @@ -322,7 +371,7 @@ static unsigned char mei_hbuf_filled_slots(struct mei_device *dev) * * @dev: the device structure * - * returns true if empty, false - otherwise. + * Return: true if empty, false - otherwise. */ static bool mei_me_hbuf_is_empty(struct mei_device *dev) { @@ -334,7 +383,7 @@ static bool mei_me_hbuf_is_empty(struct mei_device *dev) * * @dev: the device structure * - * returns -EOVERFLOW if overflow, otherwise empty slots count + * Return: -EOVERFLOW if overflow, otherwise empty slots count */ static int mei_me_hbuf_empty_slots(struct mei_device *dev) { @@ -350,6 +399,13 @@ static int mei_me_hbuf_empty_slots(struct mei_device *dev) return empty_slots; } +/** + * mei_me_hbuf_max_len - returns size of hw buffer. + * + * @dev: the device structure + * + * Return: size of hw buffer in bytes + */ static size_t mei_me_hbuf_max_len(const struct mei_device *dev) { return dev->hbuf_depth * sizeof(u32) - sizeof(struct mei_msg_hdr); @@ -363,7 +419,7 @@ static size_t mei_me_hbuf_max_len(const struct mei_device *dev) * @header: mei HECI header of message * @buf: message payload will be written * - * This function returns -EIO if write has failed + * Return: -EIO if write has failed */ static int mei_me_write_message(struct mei_device *dev, struct mei_msg_hdr *header, @@ -378,10 +434,10 @@ static int mei_me_write_message(struct mei_device *dev, int i; int empty_slots; - dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header)); + dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header)); empty_slots = mei_hbuf_empty_slots(dev); - dev_dbg(&dev->pdev->dev, "empty slots = %hu.\n", empty_slots); + dev_dbg(dev->dev, "empty slots = %hu.\n", empty_slots); dw_cnt = mei_data2slots(length); if (empty_slots < 0 || dw_cnt > empty_slots) @@ -395,6 +451,7 @@ static int mei_me_write_message(struct mei_device *dev, rem = length & 0x3; if (rem > 0) { u32 reg = 0; + memcpy(®, &buf[length - rem], rem); mei_me_reg_write(hw, H_CB_WW, reg); } @@ -412,7 +469,7 @@ static int mei_me_write_message(struct mei_device *dev, * * @dev: the device structure * - * returns -EOVERFLOW if overflow, otherwise filled slots count + * Return: -EOVERFLOW if overflow, otherwise filled slots count */ static int mei_me_count_full_read_slots(struct mei_device *dev) { @@ -430,7 +487,7 @@ static int mei_me_count_full_read_slots(struct mei_device *dev) if (filled_slots > buffer_depth) return -EOVERFLOW; - dev_dbg(&dev->pdev->dev, "filled_slots =%08x\n", filled_slots); + dev_dbg(dev->dev, "filled_slots =%08x\n", filled_slots); return (int)filled_slots; } @@ -440,6 +497,8 @@ static int mei_me_count_full_read_slots(struct mei_device *dev) * @dev: the device structure * @buffer: message buffer will be written * @buffer_length: message size will be read + * + * Return: always 0 */ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, unsigned long buffer_length) @@ -453,6 +512,7 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, if (buffer_length > 0) { u32 reg = mei_me_mecbrw_read(dev); + memcpy(reg_buf, ®, buffer_length); } @@ -462,7 +522,7 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, } /** - * mei_me_pg_enter - write pg enter register to mei device. + * mei_me_pg_enter - write pg enter register * * @dev: the device structure */ @@ -470,12 +530,13 @@ 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); + reg |= H_HPG_CSR_PGI; mei_me_reg_write(hw, H_HPG_CSR, reg); } /** - * mei_me_pg_enter - write pg enter register to mei device. + * mei_me_pg_exit - write pg exit register * * @dev: the device structure */ @@ -495,7 +556,7 @@ static void mei_me_pg_exit(struct mei_device *dev) * * @dev: the device structure * - * returns 0 on success an error code otherwise + * Return: 0 on success an error code otherwise */ int mei_me_pg_set_sync(struct mei_device *dev) { @@ -532,7 +593,7 @@ int mei_me_pg_set_sync(struct mei_device *dev) * * @dev: the device structure * - * returns 0 on success an error code otherwise + * Return: 0 on success an error code otherwise */ int mei_me_pg_unset_sync(struct mei_device *dev) { @@ -569,7 +630,7 @@ reply: * * @dev: the device structure * - * returns: true is pg supported, false otherwise + * Return: true is pg supported, false otherwise */ static bool mei_me_pg_is_enabled(struct mei_device *dev) { @@ -579,17 +640,13 @@ static bool mei_me_pg_is_enabled(struct mei_device *dev) if ((reg & ME_PGIC_HRA) == 0) goto notsupported; - if (dev->version.major_version < HBM_MAJOR_VERSION_PGI) - goto notsupported; - - if (dev->version.major_version == HBM_MAJOR_VERSION_PGI && - dev->version.minor_version < HBM_MINOR_VERSION_PGI) + if (!dev->hbm_f_pg_supported) goto notsupported; return true; notsupported: - dev_dbg(&dev->pdev->dev, "pg: not supported: HGP = %d hbm version %d.%d ?= %d.%d\n", + dev_dbg(dev->dev, "pg: not supported: HGP = %d hbm version %d.%d ?= %d.%d\n", !!(reg & ME_PGIC_HRA), dev->version.major_version, dev->version.minor_version, @@ -605,7 +662,7 @@ notsupported: * @irq: The irq number * @dev_id: pointer to the device structure * - * returns irqreturn_t + * Return: irqreturn_t */ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) @@ -630,7 +687,7 @@ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) * @irq: The irq number * @dev_id: pointer to the device structure * - * returns irqreturn_t + * Return: irqreturn_t * */ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) @@ -640,19 +697,19 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) s32 slots; int rets = 0; - dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n"); + dev_dbg(dev->dev, "function called after ISR to handle the interrupt processing.\n"); /* initialize our complete list */ mutex_lock(&dev->device_lock); mei_io_list_init(&complete_list); /* Ack the interrupt here * In case of MSI we don't go through the quick handler */ - if (pci_dev_msi_enabled(dev->pdev)) + if (pci_dev_msi_enabled(to_pci_dev(dev->dev))) mei_clear_interrupts(dev); /* check if ME wants a reset */ if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { - dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n"); + dev_warn(dev->dev, "FW not ready: resetting.\n"); schedule_work(&dev->reset_work); goto end; } @@ -661,19 +718,19 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) if (!mei_host_is_ready(dev)) { if (mei_hw_is_ready(dev)) { mei_me_hw_reset_release(dev); - dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); + dev_dbg(dev->dev, "we need to start the dev.\n"); dev->recvd_hw_ready = true; - wake_up_interruptible(&dev->wait_hw_ready); + wake_up(&dev->wait_hw_ready); } else { - dev_dbg(&dev->pdev->dev, "Spurious Interrupt\n"); + dev_dbg(dev->dev, "Spurious Interrupt\n"); } goto end; } /* check slots available for reading */ slots = mei_count_full_read_slots(dev); while (slots > 0) { - dev_dbg(&dev->pdev->dev, "slots to read = %08x\n", slots); + dev_dbg(dev->dev, "slots to read = %08x\n", slots); rets = mei_irq_read_handler(dev, &complete_list, &slots); /* There is a race between ME write and interrupt delivery: * Not all data is always available immediately after the @@ -683,7 +740,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) break; if (rets && dev->dev_state != MEI_DEV_RESETTING) { - dev_err(&dev->pdev->dev, "mei_irq_read_handler ret = %d.\n", + dev_err(dev->dev, "mei_irq_read_handler ret = %d.\n", rets); schedule_work(&dev->reset_work); goto end; @@ -705,13 +762,14 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) mei_irq_compl_handler(dev, &complete_list); end: - dev_dbg(&dev->pdev->dev, "interrupt thread end ret = %d\n", rets); + dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); mutex_unlock(&dev->device_lock); return IRQ_HANDLED; } static const struct mei_hw_ops mei_me_hw_ops = { + .fw_status = mei_me_fw_status, .pg_state = mei_me_pg_state, .host_is_ready = mei_me_host_is_ready, @@ -741,6 +799,7 @@ static const struct mei_hw_ops mei_me_hw_ops = { static bool mei_me_fw_type_nm(struct pci_dev *pdev) { u32 reg; + pci_read_config_dword(pdev, PCI_CFG_HFS_2, ®); /* make sure that bit 9 (NM) is up and bit 10 (DM) is down */ return (reg & 0x600) == 0x200; @@ -809,23 +868,22 @@ const struct mei_cfg mei_me_lpt_cfg = { * @pdev: The pci device structure * @cfg: per device generation config * - * returns The mei_device_device pointer on success, NULL on failure. + * Return: The mei_device_device pointer on success, NULL on failure. */ struct mei_device *mei_me_dev_init(struct pci_dev *pdev, const struct mei_cfg *cfg) { struct mei_device *dev; + struct mei_me_hw *hw; dev = kzalloc(sizeof(struct mei_device) + sizeof(struct mei_me_hw), GFP_KERNEL); if (!dev) return NULL; + hw = to_me_hw(dev); - mei_device_init(dev, cfg); - - dev->ops = &mei_me_hw_ops; - - dev->pdev = pdev; + mei_device_init(dev, &pdev->dev, &mei_me_hw_ops); + hw->cfg = cfg; return dev; } diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 12b0f4bbe1f1..e6a59a62573a 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -19,14 +19,44 @@ #ifndef _MEI_INTERFACE_H_ #define _MEI_INTERFACE_H_ -#include <linux/mei.h> #include <linux/irqreturn.h> +#include <linux/pci.h> +#include <linux/mei.h> + #include "mei_dev.h" #include "client.h" +/* + * mei_cfg - mei device configuration + * + * @fw_status: FW status + * @quirk_probe: device exclusion quirk + */ +struct mei_cfg { + const struct mei_fw_status fw_status; + bool (*quirk_probe)(struct pci_dev *pdev); +}; + + +#define MEI_PCI_DEVICE(dev, cfg) \ + .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \ + .driver_data = (kernel_ulong_t)&(cfg) + + #define MEI_ME_RPM_TIMEOUT 500 /* ms */ +/** + * struct mei_me_hw - me hw specific data + * + * @cfg: per device generation config and ops + * @mem_addr: io memory address + * @host_hw_state: cached host state + * @me_hw_state: cached me (fw) state + * @pg_state: power gating state + */ struct mei_me_hw { + const struct mei_cfg *cfg; void __iomem *mem_addr; /* * hw states of host and fw(ME) diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index f1cd166094f2..c5e1902e493f 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -28,11 +28,12 @@ #include "hbm.h" /** - * mei_txe_reg_read - Reads 32bit data from the device + * mei_txe_reg_read - Reads 32bit data from the txe device * * @base_addr: registers base address * @offset: register offset * + * Return: register value */ static inline u32 mei_txe_reg_read(void __iomem *base_addr, unsigned long offset) @@ -41,7 +42,7 @@ static inline u32 mei_txe_reg_read(void __iomem *base_addr, } /** - * mei_txe_reg_write - Writes 32bit data to the device + * mei_txe_reg_write - Writes 32bit data to the txe device * * @base_addr: registers base address * @offset: register offset @@ -56,10 +57,12 @@ static inline void mei_txe_reg_write(void __iomem *base_addr, /** * mei_txe_sec_reg_read_silent - Reads 32bit data from the SeC BAR * - * @dev: the device structure + * @hw: the txe hardware structure * @offset: register offset * * Doesn't check for aliveness while Reads 32bit data from the SeC BAR + * + * Return: register value */ static inline u32 mei_txe_sec_reg_read_silent(struct mei_txe_hw *hw, unsigned long offset) @@ -70,10 +73,12 @@ static inline u32 mei_txe_sec_reg_read_silent(struct mei_txe_hw *hw, /** * mei_txe_sec_reg_read - Reads 32bit data from the SeC BAR * - * @dev: the device structure + * @hw: the txe hardware structure * @offset: register offset * * Reads 32bit data from the SeC BAR and shout loud if aliveness is not set + * + * Return: register value */ static inline u32 mei_txe_sec_reg_read(struct mei_txe_hw *hw, unsigned long offset) @@ -85,7 +90,7 @@ static inline u32 mei_txe_sec_reg_read(struct mei_txe_hw *hw, * mei_txe_sec_reg_write_silent - Writes 32bit data to the SeC BAR * doesn't check for aliveness * - * @dev: the device structure + * @hw: the txe hardware structure * @offset: register offset * @value: value to write * @@ -100,7 +105,7 @@ static inline void mei_txe_sec_reg_write_silent(struct mei_txe_hw *hw, /** * mei_txe_sec_reg_write - Writes 32bit data to the SeC BAR * - * @dev: the device structure + * @hw: the txe hardware structure * @offset: register offset * @value: value to write * @@ -115,9 +120,10 @@ static inline void mei_txe_sec_reg_write(struct mei_txe_hw *hw, /** * mei_txe_br_reg_read - Reads 32bit data from the Bridge BAR * - * @hw: the device structure + * @hw: the txe hardware structure * @offset: offset from which to read the data * + * Return: the byte read. */ static inline u32 mei_txe_br_reg_read(struct mei_txe_hw *hw, unsigned long offset) @@ -128,7 +134,7 @@ static inline u32 mei_txe_br_reg_read(struct mei_txe_hw *hw, /** * mei_txe_br_reg_write - Writes 32bit data to the Bridge BAR * - * @hw: the device structure + * @hw: the txe hardware structure * @offset: offset from which to write the data * @value: the byte to write */ @@ -147,7 +153,10 @@ static inline void mei_txe_br_reg_write(struct mei_txe_hw *hw, * Request for aliveness change and returns true if the change is * really needed and false if aliveness is already * in the requested state - * Requires device lock to be held + * + * Locking: called under "dev->device_lock" lock + * + * Return: true if request was send */ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req) { @@ -155,7 +164,7 @@ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req) struct mei_txe_hw *hw = to_txe_hw(dev); bool do_req = hw->aliveness != req; - dev_dbg(&dev->pdev->dev, "Aliveness current=%d request=%d\n", + dev_dbg(dev->dev, "Aliveness current=%d request=%d\n", hw->aliveness, req); if (do_req) { dev->pg_event = MEI_PG_EVENT_WAIT; @@ -172,26 +181,31 @@ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req) * * Extract HICR_HOST_ALIVENESS_RESP_ACK bit from * from HICR_HOST_ALIVENESS_REQ register value + * + * Return: SICR_HOST_ALIVENESS_REQ_REQUESTED bit value */ static u32 mei_txe_aliveness_req_get(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 reg; + reg = mei_txe_br_reg_read(hw, SICR_HOST_ALIVENESS_REQ_REG); return reg & SICR_HOST_ALIVENESS_REQ_REQUESTED; } /** * mei_txe_aliveness_get - get aliveness response register value + * * @dev: the device structure * - * Extract HICR_HOST_ALIVENESS_RESP_ACK bit - * from HICR_HOST_ALIVENESS_RESP register value + * Return: HICR_HOST_ALIVENESS_RESP_ACK bit from HICR_HOST_ALIVENESS_RESP + * register */ static u32 mei_txe_aliveness_get(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 reg; + reg = mei_txe_br_reg_read(hw, HICR_HOST_ALIVENESS_RESP_REG); return reg & HICR_HOST_ALIVENESS_RESP_ACK; } @@ -203,7 +217,8 @@ static u32 mei_txe_aliveness_get(struct mei_device *dev) * @expected: expected aliveness value * * Polls for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set - * returns > 0 if the expected value was received, -ETIME otherwise + * + * Return: > 0 if the expected value was received, -ETIME otherwise */ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) { @@ -214,7 +229,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) hw->aliveness = mei_txe_aliveness_get(dev); if (hw->aliveness == expected) { dev->pg_event = MEI_PG_EVENT_IDLE; - dev_dbg(&dev->pdev->dev, + dev_dbg(dev->dev, "aliveness settled after %d msecs\n", t); return t; } @@ -225,7 +240,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) } while (t < SEC_ALIVENESS_WAIT_TIMEOUT); dev->pg_event = MEI_PG_EVENT_IDLE; - dev_err(&dev->pdev->dev, "aliveness timed out\n"); + dev_err(dev->dev, "aliveness timed out\n"); return -ETIME; } @@ -236,7 +251,8 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) * @expected: expected aliveness value * * Waits for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set - * returns returns 0 on success and < 0 otherwise + * + * Return: 0 on success and < 0 otherwise */ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected) { @@ -259,10 +275,10 @@ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected) ret = hw->aliveness == expected ? 0 : -ETIME; if (ret) - dev_warn(&dev->pdev->dev, "aliveness timed out = %ld aliveness = %d event = %d\n", + dev_warn(dev->dev, "aliveness timed out = %ld aliveness = %d event = %d\n", err, hw->aliveness, dev->pg_event); else - dev_dbg(&dev->pdev->dev, "aliveness settled after = %d msec aliveness = %d event = %d\n", + dev_dbg(dev->dev, "aliveness settled after = %d msec aliveness = %d event = %d\n", jiffies_to_msecs(timeout - err), hw->aliveness, dev->pg_event); @@ -274,8 +290,9 @@ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected) * mei_txe_aliveness_set_sync - sets an wait for aliveness to complete * * @dev: the device structure + * @req: requested aliveness value * - * returns returns 0 on success and < 0 otherwise + * Return: 0 on success and < 0 otherwise */ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req) { @@ -289,7 +306,7 @@ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req) * * @dev: the device structure * - * returns: true is pg supported, false otherwise + * Return: true is pg supported, false otherwise */ static bool mei_txe_pg_is_enabled(struct mei_device *dev) { @@ -302,11 +319,12 @@ static bool mei_txe_pg_is_enabled(struct mei_device *dev) * * @dev: the device structure * - * returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise + * Return: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise */ static inline enum mei_pg_state mei_txe_pg_state(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + return hw->aliveness ? MEI_PG_OFF : MEI_PG_ON; } @@ -326,9 +344,10 @@ static void mei_txe_input_ready_interrupt_enable(struct mei_device *dev) } /** - * mei_txe_input_doorbell_set - * - Sets bit 0 in SEC_IPC_INPUT_DOORBELL.IPC_INPUT_DOORBELL. - * @dev: the device structure + * mei_txe_input_doorbell_set - sets bit 0 in + * SEC_IPC_INPUT_DOORBELL.IPC_INPUT_DOORBELL. + * + * @hw: the txe hardware structure */ static void mei_txe_input_doorbell_set(struct mei_txe_hw *hw) { @@ -340,7 +359,7 @@ static void mei_txe_input_doorbell_set(struct mei_txe_hw *hw) /** * mei_txe_output_ready_set - Sets the SICR_SEC_IPC_OUTPUT_STATUS bit to 1 * - * @dev: the device structure + * @hw: the txe hardware structure */ static void mei_txe_output_ready_set(struct mei_txe_hw *hw) { @@ -353,11 +372,14 @@ static void mei_txe_output_ready_set(struct mei_txe_hw *hw) * mei_txe_is_input_ready - check if TXE is ready for receiving data * * @dev: the device structure + * + * Return: true if INPUT STATUS READY bit is set */ static bool mei_txe_is_input_ready(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 status; + status = mei_txe_sec_reg_read(hw, SEC_IPC_INPUT_STATUS_REG); return !!(SEC_IPC_INPUT_STATUS_RDY & status); } @@ -370,6 +392,7 @@ static bool mei_txe_is_input_ready(struct mei_device *dev) static inline void mei_txe_intr_clear(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_sec_reg_write_silent(hw, SEC_IPC_HOST_INT_STATUS_REG, SEC_IPC_HOST_INT_STATUS_PENDING); mei_txe_br_reg_write(hw, HISR_REG, HISR_INT_STS_MSK); @@ -384,6 +407,7 @@ static inline void mei_txe_intr_clear(struct mei_device *dev) static void mei_txe_intr_disable(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_br_reg_write(hw, HHIER_REG, 0); mei_txe_br_reg_write(hw, HIER_REG, 0); } @@ -395,6 +419,7 @@ static void mei_txe_intr_disable(struct mei_device *dev) static void mei_txe_intr_enable(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_br_reg_write(hw, HHIER_REG, IPC_HHIER_MSK); mei_txe_br_reg_write(hw, HIER_REG, HIER_INT_EN_MSK); } @@ -407,6 +432,8 @@ static void mei_txe_intr_enable(struct mei_device *dev) * * Checks if there are pending interrupts * only Aliveness, Readiness, Input ready, and Output doorbell are relevant + * + * Return: true if there are pending interrupts */ static bool mei_txe_pending_interrupts(struct mei_device *dev) { @@ -418,7 +445,7 @@ static bool mei_txe_pending_interrupts(struct mei_device *dev) TXE_INTR_OUT_DB)); if (ret) { - dev_dbg(&dev->pdev->dev, + dev_dbg(dev->dev, "Pending Interrupts InReady=%01d Readiness=%01d, Aliveness=%01d, OutDoor=%01d\n", !!(hw->intr_cause & TXE_INTR_IN_READY), !!(hw->intr_cause & TXE_INTR_READINESS), @@ -440,6 +467,7 @@ static void mei_txe_input_payload_write(struct mei_device *dev, unsigned long idx, u32 value) { struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_sec_reg_write(hw, SEC_IPC_INPUT_PAYLOAD_REG + (idx * sizeof(u32)), value); } @@ -451,12 +479,13 @@ static void mei_txe_input_payload_write(struct mei_device *dev, * @dev: the device structure * @idx: index in the device buffer * - * returns register value at index + * Return: register value at index */ static u32 mei_txe_out_data_read(const struct mei_device *dev, unsigned long idx) { struct mei_txe_hw *hw = to_txe_hw(dev); + return mei_txe_br_reg_read(hw, BRIDGE_IPC_OUTPUT_PAYLOAD_REG + (idx * sizeof(u32))); } @@ -464,26 +493,28 @@ static u32 mei_txe_out_data_read(const struct mei_device *dev, /* Readiness */ /** - * mei_txe_readiness_set_host_rdy + * mei_txe_readiness_set_host_rdy - set host readiness bit * * @dev: the device structure */ static void mei_txe_readiness_set_host_rdy(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_br_reg_write(hw, SICR_HOST_IPC_READINESS_REQ_REG, SICR_HOST_IPC_READINESS_HOST_RDY); } /** - * mei_txe_readiness_clear + * mei_txe_readiness_clear - clear host readiness bit * * @dev: the device structure */ static void mei_txe_readiness_clear(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_br_reg_write(hw, SICR_HOST_IPC_READINESS_REQ_REG, SICR_HOST_IPC_READINESS_RDY_CLR); } @@ -492,10 +523,13 @@ static void mei_txe_readiness_clear(struct mei_device *dev) * the HICR_SEC_IPC_READINESS register value * * @dev: the device structure + * + * Return: the HICR_SEC_IPC_READINESS register value */ static u32 mei_txe_readiness_get(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + return mei_txe_br_reg_read(hw, HICR_SEC_IPC_READINESS_REG); } @@ -504,7 +538,9 @@ static u32 mei_txe_readiness_get(struct mei_device *dev) * mei_txe_readiness_is_sec_rdy - check readiness * for HICR_SEC_IPC_READINESS_SEC_RDY * - * @readiness - cached readiness state + * @readiness: cached readiness state + * + * Return: true if readiness bit is set */ static inline bool mei_txe_readiness_is_sec_rdy(u32 readiness) { @@ -515,10 +551,13 @@ static inline bool mei_txe_readiness_is_sec_rdy(u32 readiness) * mei_txe_hw_is_ready - check if the hw is ready * * @dev: the device structure + * + * Return: true if sec is ready */ static bool mei_txe_hw_is_ready(struct mei_device *dev) { u32 readiness = mei_txe_readiness_get(dev); + return mei_txe_readiness_is_sec_rdy(readiness); } @@ -526,11 +565,14 @@ static bool mei_txe_hw_is_ready(struct mei_device *dev) * mei_txe_host_is_ready - check if the host is ready * * @dev: the device structure + * + * Return: true if host is ready */ static inline bool mei_txe_host_is_ready(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 reg = mei_txe_br_reg_read(hw, HICR_SEC_IPC_READINESS_REG); + return !!(reg & HICR_SEC_IPC_READINESS_HOST_RDY); } @@ -539,7 +581,7 @@ static inline bool mei_txe_host_is_ready(struct mei_device *dev) * * @dev: the device structure * - * returns 0 on success and -ETIME on timeout + * Return: 0 on success and -ETIME on timeout */ static int mei_txe_readiness_wait(struct mei_device *dev) { @@ -551,7 +593,7 @@ static int mei_txe_readiness_wait(struct mei_device *dev) msecs_to_jiffies(SEC_RESET_WAIT_TIMEOUT)); mutex_lock(&dev->device_lock); if (!dev->recvd_hw_ready) { - dev_err(&dev->pdev->dev, "wait for readiness failed\n"); + dev_err(dev->dev, "wait for readiness failed\n"); return -ETIME; } @@ -559,6 +601,42 @@ static int mei_txe_readiness_wait(struct mei_device *dev) return 0; } +static const struct mei_fw_status mei_txe_fw_sts = { + .count = 2, + .status[0] = PCI_CFG_TXE_FW_STS0, + .status[1] = PCI_CFG_TXE_FW_STS1 +}; + +/** + * mei_txe_fw_status - read fw status register from pci config space + * + * @dev: mei device + * @fw_status: fw status register values + * + * Return: 0 on success, error otherwise + */ +static int mei_txe_fw_status(struct mei_device *dev, + struct mei_fw_status *fw_status) +{ + const struct mei_fw_status *fw_src = &mei_txe_fw_sts; + struct pci_dev *pdev = to_pci_dev(dev->dev); + int ret; + int i; + + if (!fw_status) + return -EINVAL; + + fw_status->count = fw_src->count; + for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { + ret = pci_read_config_dword(pdev, + fw_src->status[i], &fw_status->status[i]); + if (ret) + return ret; + } + + return 0; +} + /** * mei_txe_hw_config - configure hardware at the start of the devices * @@ -571,13 +649,14 @@ static void mei_txe_hw_config(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + /* Doesn't change in runtime */ dev->hbuf_depth = PAYLOAD_SIZE / 4; hw->aliveness = mei_txe_aliveness_get(dev); hw->readiness = mei_txe_readiness_get(dev); - dev_dbg(&dev->pdev->dev, "aliveness_resp = 0x%08x, readiness = 0x%08x.\n", + dev_dbg(dev->dev, "aliveness_resp = 0x%08x, readiness = 0x%08x.\n", hw->aliveness, hw->readiness); } @@ -588,7 +667,8 @@ static void mei_txe_hw_config(struct mei_device *dev) * @dev: the device structure * @header: header of message * @buf: message buffer will be written - * returns 1 if success, 0 - otherwise. + * + * Return: 0 if success, <0 - otherwise. */ static int mei_txe_write(struct mei_device *dev, @@ -607,7 +687,7 @@ static int mei_txe_write(struct mei_device *dev, length = header->length; - dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header)); + dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header)); dw_cnt = mei_data2slots(length); if (dw_cnt > slots) @@ -621,8 +701,9 @@ static int mei_txe_write(struct mei_device *dev, if (!mei_txe_is_input_ready(dev)) { struct mei_fw_status fw_status; + mei_fw_status(dev, &fw_status); - dev_err(&dev->pdev->dev, "Input is not ready " FW_STS_FMT "\n", + dev_err(dev->dev, "Input is not ready " FW_STS_FMT "\n", FW_STS_PRM(fw_status)); return -EAGAIN; } @@ -635,6 +716,7 @@ static int mei_txe_write(struct mei_device *dev, rem = length & 0x3; if (rem > 0) { u32 reg = 0; + memcpy(®, &buf[length - rem], rem); mei_txe_input_payload_write(dev, i + 1, reg); } @@ -653,7 +735,7 @@ static int mei_txe_write(struct mei_device *dev, * * @dev: the device structure * - * returns the PAYLOAD_SIZE - 4 + * Return: the PAYLOAD_SIZE - 4 */ static size_t mei_txe_hbuf_max_len(const struct mei_device *dev) { @@ -665,11 +747,12 @@ static size_t mei_txe_hbuf_max_len(const struct mei_device *dev) * * @dev: the device structure * - * returns always hbuf_depth + * Return: always hbuf_depth */ static int mei_txe_hbuf_empty_slots(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + return hw->slots; } @@ -678,7 +761,7 @@ static int mei_txe_hbuf_empty_slots(struct mei_device *dev) * * @dev: the device structure * - * returns always buffer size in dwords count + * Return: always buffer size in dwords count */ static int mei_txe_count_full_read_slots(struct mei_device *dev) { @@ -691,7 +774,7 @@ static int mei_txe_count_full_read_slots(struct mei_device *dev) * * @dev: the device structure * - * returns mei message header + * Return: mei message header */ static u32 mei_txe_read_hdr(const struct mei_device *dev) @@ -705,33 +788,35 @@ static u32 mei_txe_read_hdr(const struct mei_device *dev) * @buf: message buffer will be written * @len: message size will be read * - * returns -EINVAL on error wrong argument and 0 on success + * Return: -EINVAL on error wrong argument and 0 on success */ static int mei_txe_read(struct mei_device *dev, unsigned char *buf, unsigned long len) { struct mei_txe_hw *hw = to_txe_hw(dev); + u32 *reg_buf, reg; + u32 rem; u32 i; - u32 *reg_buf = (u32 *)buf; - u32 rem = len & 0x3; if (WARN_ON(!buf || !len)) return -EINVAL; - dev_dbg(&dev->pdev->dev, - "buffer-length = %lu buf[0]0x%08X\n", + reg_buf = (u32 *)buf; + rem = len & 0x3; + + dev_dbg(dev->dev, "buffer-length = %lu buf[0]0x%08X\n", len, mei_txe_out_data_read(dev, 0)); for (i = 0; i < len / 4; i++) { /* skip header: index starts from 1 */ - u32 reg = mei_txe_out_data_read(dev, i + 1); - dev_dbg(&dev->pdev->dev, "buf[%d] = 0x%08X\n", i, reg); + reg = mei_txe_out_data_read(dev, i + 1); + dev_dbg(dev->dev, "buf[%d] = 0x%08X\n", i, reg); *reg_buf++ = reg; } if (rem) { - u32 reg = mei_txe_out_data_read(dev, i + 1); + reg = mei_txe_out_data_read(dev, i + 1); memcpy(reg_buf, ®, rem); } @@ -745,7 +830,7 @@ static int mei_txe_read(struct mei_device *dev, * @dev: the device structure * @intr_enable: if interrupt should be enabled after reset. * - * returns 0 on success and < 0 in case of error + * Return: 0 on success and < 0 in case of error */ static int mei_txe_hw_reset(struct mei_device *dev, bool intr_enable) { @@ -771,8 +856,7 @@ static int mei_txe_hw_reset(struct mei_device *dev, bool intr_enable) */ if (aliveness_req != hw->aliveness) if (mei_txe_aliveness_poll(dev, aliveness_req) < 0) { - dev_err(&dev->pdev->dev, - "wait for aliveness settle failed ... bailing out\n"); + dev_err(dev->dev, "wait for aliveness settle failed ... bailing out\n"); return -EIO; } @@ -782,14 +866,13 @@ static int mei_txe_hw_reset(struct mei_device *dev, bool intr_enable) if (aliveness_req) { mei_txe_aliveness_set(dev, 0); if (mei_txe_aliveness_poll(dev, 0) < 0) { - dev_err(&dev->pdev->dev, - "wait for aliveness failed ... bailing out\n"); + dev_err(dev->dev, "wait for aliveness failed ... bailing out\n"); return -EIO; } } /* - * Set rediness RDY_CLR bit + * Set readiness RDY_CLR bit */ mei_txe_readiness_clear(dev); @@ -801,7 +884,7 @@ static int mei_txe_hw_reset(struct mei_device *dev, bool intr_enable) * * @dev: the device structure * - * returns 0 on success and < 0 in case of error + * Return: 0 on success an error code otherwise */ static int mei_txe_hw_start(struct mei_device *dev) { @@ -815,7 +898,7 @@ static int mei_txe_hw_start(struct mei_device *dev) ret = mei_txe_readiness_wait(dev); if (ret < 0) { - dev_err(&dev->pdev->dev, "wating for readiness failed\n"); + dev_err(dev->dev, "waiting for readiness failed\n"); return ret; } @@ -831,7 +914,7 @@ static int mei_txe_hw_start(struct mei_device *dev) ret = mei_txe_aliveness_set_sync(dev, 1); if (ret < 0) { - dev_err(&dev->pdev->dev, "wait for aliveness failed ... bailing out\n"); + dev_err(dev->dev, "wait for aliveness failed ... bailing out\n"); return ret; } @@ -857,6 +940,8 @@ static int mei_txe_hw_start(struct mei_device *dev) * * @dev: the device structure * @do_ack: acknowledge interrupts + * + * Return: true if found interrupts to process. */ static bool mei_txe_check_and_ack_intrs(struct mei_device *dev, bool do_ack) { @@ -912,7 +997,8 @@ out: * @irq: The irq number * @dev_id: pointer to the device structure * - * returns irqreturn_t + * Return: IRQ_WAKE_THREAD if interrupt is designed for the device + * IRQ_NONE otherwise */ irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id) { @@ -930,8 +1016,7 @@ irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id) * @irq: The irq number * @dev_id: pointer to the device structure * - * returns irqreturn_t - * + * Return: IRQ_HANDLED */ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) { @@ -941,7 +1026,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) s32 slots; int rets = 0; - dev_dbg(&dev->pdev->dev, "irq thread: Interrupt Registers HHISR|HISR|SEC=%02X|%04X|%02X\n", + dev_dbg(dev->dev, "irq thread: Interrupt Registers HHISR|HISR|SEC=%02X|%04X|%02X\n", mei_txe_br_reg_read(hw, HHISR_REG), mei_txe_br_reg_read(hw, HISR_REG), mei_txe_sec_reg_read_silent(hw, SEC_IPC_HOST_INT_STATUS_REG)); @@ -951,7 +1036,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) mutex_lock(&dev->device_lock); mei_io_list_init(&complete_list); - if (pci_dev_msi_enabled(dev->pdev)) + if (pci_dev_msi_enabled(to_pci_dev(dev->dev))) mei_txe_check_and_ack_intrs(dev, true); /* show irq events */ @@ -965,17 +1050,17 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) * or TXE driver resetting the HECI interface. */ if (test_and_clear_bit(TXE_INTR_READINESS_BIT, &hw->intr_cause)) { - dev_dbg(&dev->pdev->dev, "Readiness Interrupt was received...\n"); + dev_dbg(dev->dev, "Readiness Interrupt was received...\n"); /* Check if SeC is going through reset */ if (mei_txe_readiness_is_sec_rdy(hw->readiness)) { - dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); + dev_dbg(dev->dev, "we need to start the dev.\n"); dev->recvd_hw_ready = true; } else { dev->recvd_hw_ready = false; if (dev->dev_state != MEI_DEV_RESETTING) { - dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n"); + dev_warn(dev->dev, "FW not ready: resetting.\n"); schedule_work(&dev->reset_work); goto end; @@ -992,7 +1077,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) if (test_and_clear_bit(TXE_INTR_ALIVENESS_BIT, &hw->intr_cause)) { /* Clear the interrupt cause */ - dev_dbg(&dev->pdev->dev, + dev_dbg(dev->dev, "Aliveness Interrupt: Status: %d\n", hw->aliveness); dev->pg_event = MEI_PG_EVENT_RECEIVED; if (waitqueue_active(&hw->wait_aliveness_resp)) @@ -1008,7 +1093,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) /* Read from TXE */ rets = mei_irq_read_handler(dev, &complete_list, &slots); if (rets && dev->dev_state != MEI_DEV_RESETTING) { - dev_err(&dev->pdev->dev, + dev_err(dev->dev, "mei_irq_read_handler ret = %d.\n", rets); schedule_work(&dev->reset_work); @@ -1026,7 +1111,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) dev->hbuf_is_ready = mei_hbuf_is_ready(dev); rets = mei_irq_write_handler(dev, &complete_list); if (rets && rets != -EMSGSIZE) - dev_err(&dev->pdev->dev, "mei_irq_write_handler ret = %d.\n", + dev_err(dev->dev, "mei_irq_write_handler ret = %d.\n", rets); dev->hbuf_is_ready = mei_hbuf_is_ready(dev); } @@ -1034,7 +1119,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) mei_irq_compl_handler(dev, &complete_list); end: - dev_dbg(&dev->pdev->dev, "interrupt thread end ret = %d\n", rets); + dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); mutex_unlock(&dev->device_lock); @@ -1046,6 +1131,7 @@ static const struct mei_hw_ops mei_txe_hw_ops = { .host_is_ready = mei_txe_host_is_ready, + .fw_status = mei_txe_fw_status, .pg_state = mei_txe_pg_state, .hw_is_ready = mei_txe_hw_is_ready, @@ -1072,27 +1158,14 @@ static const struct mei_hw_ops mei_txe_hw_ops = { }; -#define MEI_CFG_TXE_FW_STS \ - .fw_status.count = 2, \ - .fw_status.status[0] = PCI_CFG_TXE_FW_STS0, \ - .fw_status.status[1] = PCI_CFG_TXE_FW_STS1 - -const struct mei_cfg mei_txe_cfg = { - MEI_CFG_TXE_FW_STS, -}; - - /** * mei_txe_dev_init - allocates and initializes txe hardware specific structure * - * @pdev - pci device - * @cfg - per device generation config - * - * returns struct mei_device * on success or NULL; + * @pdev: pci device * + * Return: struct mei_device * on success or NULL */ -struct mei_device *mei_txe_dev_init(struct pci_dev *pdev, - const struct mei_cfg *cfg) +struct mei_device *mei_txe_dev_init(struct pci_dev *pdev) { struct mei_device *dev; struct mei_txe_hw *hw; @@ -1102,15 +1175,12 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev, if (!dev) return NULL; - mei_device_init(dev, cfg); + mei_device_init(dev, &pdev->dev, &mei_txe_hw_ops); hw = to_txe_hw(dev); init_waitqueue_head(&hw->wait_aliveness_resp); - dev->ops = &mei_txe_hw_ops; - - dev->pdev = pdev; return dev; } @@ -1120,6 +1190,8 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev, * @dev: the device structure * @addr: physical address start of the range * @range: physical range size + * + * Return: 0 on success an error code otherwise */ int mei_txe_setup_satt2(struct mei_device *dev, phys_addr_t addr, u32 range) { @@ -1151,7 +1223,7 @@ int mei_txe_setup_satt2(struct mei_device *dev, phys_addr_t addr, u32 range) mei_txe_br_reg_write(hw, SATT2_SAP_SIZE_REG, range); mei_txe_br_reg_write(hw, SATT2_BRG_BA_LSB_REG, lo32); mei_txe_br_reg_write(hw, SATT2_CTRL_REG, ctrl); - dev_dbg(&dev->pdev->dev, "SATT2: SAP_SIZE_OFFSET=0x%08X, BRG_BA_LSB_OFFSET=0x%08X, CTRL_OFFSET=0x%08X\n", + dev_dbg(dev->dev, "SATT2: SAP_SIZE_OFFSET=0x%08X, BRG_BA_LSB_OFFSET=0x%08X, CTRL_OFFSET=0x%08X\n", range, lo32, ctrl); return 0; diff --git a/drivers/misc/mei/hw-txe.h b/drivers/misc/mei/hw-txe.h index e244af79167f..ce3ed0b88b0c 100644 --- a/drivers/misc/mei/hw-txe.h +++ b/drivers/misc/mei/hw-txe.h @@ -40,6 +40,7 @@ * @mem_addr: SeC and BRIDGE bars * @aliveness: aliveness (power gating) state of the hardware * @readiness: readiness state of the hardware + * @slots: number of empty slots * @wait_aliveness_resp: aliveness wait queue * @intr_cause: translated interrupt cause */ @@ -61,10 +62,7 @@ static inline struct mei_device *hw_txe_to_mei(struct mei_txe_hw *hw) return container_of((void *)hw, struct mei_device, hw); } -extern const struct mei_cfg mei_txe_cfg; - -struct mei_device *mei_txe_dev_init(struct pci_dev *pdev, - const struct mei_cfg *cfg); +struct mei_device *mei_txe_dev_init(struct pci_dev *pdev); irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id); irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id); diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index dd448e58cc87..16fef6dc4dd7 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -97,23 +97,52 @@ enum mei_stop_reason_types { SYSTEM_S5_ENTRY = 0x08 }; + +/** + * enum mei_hbm_status - mei host bus messages return values + * + * @MEI_HBMS_SUCCESS : status success + * @MEI_HBMS_CLIENT_NOT_FOUND : client not found + * @MEI_HBMS_ALREADY_EXISTS : connection already established + * @MEI_HBMS_REJECTED : connection is rejected + * @MEI_HBMS_INVALID_PARAMETER : invalid parameter + * @MEI_HBMS_NOT_ALLOWED : operation not allowed + * @MEI_HBMS_ALREADY_STARTED : system is already started + * @MEI_HBMS_NOT_STARTED : system not started + * + * @MEI_HBMS_MAX : sentinel + */ +enum mei_hbm_status { + MEI_HBMS_SUCCESS = 0, + MEI_HBMS_CLIENT_NOT_FOUND = 1, + MEI_HBMS_ALREADY_EXISTS = 2, + MEI_HBMS_REJECTED = 3, + MEI_HBMS_INVALID_PARAMETER = 4, + MEI_HBMS_NOT_ALLOWED = 5, + MEI_HBMS_ALREADY_STARTED = 6, + MEI_HBMS_NOT_STARTED = 7, + + MEI_HBMS_MAX +}; + + /* * Client Connect Status * used by hbm_client_connect_response.status */ enum mei_cl_connect_status { - MEI_CL_CONN_SUCCESS = 0x00, - MEI_CL_CONN_NOT_FOUND = 0x01, - MEI_CL_CONN_ALREADY_STARTED = 0x02, - MEI_CL_CONN_OUT_OF_RESOURCES = 0x03, - MEI_CL_CONN_MESSAGE_SMALL = 0x04 + MEI_CL_CONN_SUCCESS = MEI_HBMS_SUCCESS, + MEI_CL_CONN_NOT_FOUND = MEI_HBMS_CLIENT_NOT_FOUND, + MEI_CL_CONN_ALREADY_STARTED = MEI_HBMS_ALREADY_EXISTS, + MEI_CL_CONN_OUT_OF_RESOURCES = MEI_HBMS_REJECTED, + MEI_CL_CONN_MESSAGE_SMALL = MEI_HBMS_INVALID_PARAMETER, }; /* * Client Disconnect Status */ enum mei_cl_disconnect_status { - MEI_CL_DISCONN_SUCCESS = 0x00 + MEI_CL_DISCONN_SUCCESS = MEI_HBMS_SUCCESS }; /* @@ -138,10 +167,10 @@ struct mei_bus_message { * struct hbm_cl_cmd - client specific host bus command * CONNECT, DISCONNECT, and FlOW CONTROL * - * @hbm_cmd - bus message command header - * @me_addr - address of the client in ME - * @host_addr - address of the client in the driver - * @data + * @hbm_cmd: bus message command header + * @me_addr: address of the client in ME + * @host_addr: address of the client in the driver + * @data: generic data */ struct mei_hbm_cl_cmd { u8 hbm_cmd; @@ -206,14 +235,13 @@ struct mei_client_properties { struct hbm_props_request { u8 hbm_cmd; - u8 address; + u8 me_addr; u8 reserved[2]; } __packed; - struct hbm_props_response { u8 hbm_cmd; - u8 address; + u8 me_addr; u8 status; u8 reserved[1]; struct mei_client_properties client_properties; @@ -222,8 +250,8 @@ struct hbm_props_response { /** * struct hbm_power_gate - power gate request/response * - * @hbm_cmd - bus message command header - * @reserved[3] + * @hbm_cmd: bus message command header + * @reserved: reserved */ struct hbm_power_gate { u8 hbm_cmd; @@ -233,10 +261,10 @@ struct hbm_power_gate { /** * struct hbm_client_connect_request - connect/disconnect request * - * @hbm_cmd - bus message command header - * @me_addr - address of the client in ME - * @host_addr - address of the client in the driver - * @reserved + * @hbm_cmd: bus message command header + * @me_addr: address of the client in ME + * @host_addr: address of the client in the driver + * @reserved: reserved */ struct hbm_client_connect_request { u8 hbm_cmd; @@ -248,10 +276,10 @@ struct hbm_client_connect_request { /** * struct hbm_client_connect_response - connect/disconnect response * - * @hbm_cmd - bus message command header - * @me_addr - address of the client in ME - * @host_addr - address of the client in the driver - * @status - status of the request + * @hbm_cmd: bus message command header + * @me_addr: address of the client in ME + * @host_addr: address of the client in the driver + * @status: status of the request */ struct hbm_client_connect_response { u8 hbm_cmd; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 006929222481..7901d076c127 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -15,7 +15,6 @@ */ #include <linux/export.h> -#include <linux/pci.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/delay.h> @@ -43,13 +42,23 @@ const char *mei_dev_state_str(int state) #undef MEI_DEV_STATE } +const char *mei_pg_state_str(enum mei_pg_state state) +{ +#define MEI_PG_STATE(state) case MEI_PG_##state: return #state + switch (state) { + MEI_PG_STATE(OFF); + MEI_PG_STATE(ON); + default: + return "unknown"; + } +#undef MEI_PG_STATE +} + /** - * mei_cancel_work. Cancel mei background jobs + * mei_cancel_work - Cancel mei background jobs * * @dev: the device structure - * - * returns 0 on success or < 0 if the reset hasn't succeeded */ void mei_cancel_work(struct mei_device *dev) { @@ -64,6 +73,8 @@ EXPORT_SYMBOL_GPL(mei_cancel_work); * mei_reset - resets host and fw. * * @dev: the device structure + * + * Return: 0 on success or < 0 if the reset hasn't succeeded */ int mei_reset(struct mei_device *dev) { @@ -76,8 +87,9 @@ int mei_reset(struct mei_device *dev) state != MEI_DEV_POWER_DOWN && state != MEI_DEV_POWER_UP) { struct mei_fw_status fw_status; + mei_fw_status(dev, &fw_status); - dev_warn(&dev->pdev->dev, + dev_warn(dev->dev, "unexpected reset: dev_state = %s " FW_STS_FMT "\n", mei_dev_state_str(state), FW_STS_PRM(fw_status)); } @@ -95,7 +107,7 @@ int mei_reset(struct mei_device *dev) dev->reset_count++; if (dev->reset_count > MEI_MAX_CONSEC_RESET) { - dev_err(&dev->pdev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); + dev_err(dev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); dev->dev_state = MEI_DEV_DISABLED; return -ENODEV; } @@ -116,7 +128,7 @@ int mei_reset(struct mei_device *dev) mei_cl_all_wakeup(dev); /* remove entry if already in list */ - dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); + dev_dbg(dev->dev, "remove iamthif and wd from the file list.\n"); mei_cl_unlink(&dev->wd_cl); mei_cl_unlink(&dev->iamthif_cl); mei_amthif_reset_params(dev); @@ -128,28 +140,28 @@ int mei_reset(struct mei_device *dev) dev->wd_pending = false; if (ret) { - dev_err(&dev->pdev->dev, "hw_reset failed ret = %d\n", ret); + dev_err(dev->dev, "hw_reset failed ret = %d\n", ret); return ret; } if (state == MEI_DEV_POWER_DOWN) { - dev_dbg(&dev->pdev->dev, "powering down: end of reset\n"); + dev_dbg(dev->dev, "powering down: end of reset\n"); dev->dev_state = MEI_DEV_DISABLED; return 0; } ret = mei_hw_start(dev); if (ret) { - dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret); + dev_err(dev->dev, "hw_start failed ret = %d\n", ret); return ret; } - dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); + dev_dbg(dev->dev, "link is established start sending messages.\n"); dev->dev_state = MEI_DEV_INIT_CLIENTS; ret = mei_hbm_start_req(dev); if (ret) { - dev_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret); + dev_err(dev->dev, "hbm_start failed ret = %d\n", ret); dev->dev_state = MEI_DEV_RESETTING; return ret; } @@ -163,11 +175,12 @@ EXPORT_SYMBOL_GPL(mei_reset); * * @dev: the device structure * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_start(struct mei_device *dev) { int ret; + mutex_lock(&dev->device_lock); /* acknowledge interrupt and stop interrupts */ @@ -175,7 +188,7 @@ int mei_start(struct mei_device *dev) mei_hw_config(dev); - dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n"); + dev_dbg(dev->dev, "reset in start the mei device.\n"); dev->reset_count = 0; do { @@ -183,43 +196,43 @@ int mei_start(struct mei_device *dev) ret = mei_reset(dev); if (ret == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) { - dev_err(&dev->pdev->dev, "reset failed ret = %d", ret); + dev_err(dev->dev, "reset failed ret = %d", ret); goto err; } } while (ret); /* we cannot start the device w/o hbm start message completed */ if (dev->dev_state == MEI_DEV_DISABLED) { - dev_err(&dev->pdev->dev, "reset failed"); + dev_err(dev->dev, "reset failed"); goto err; } if (mei_hbm_start_wait(dev)) { - dev_err(&dev->pdev->dev, "HBM haven't started"); + dev_err(dev->dev, "HBM haven't started"); goto err; } if (!mei_host_is_ready(dev)) { - dev_err(&dev->pdev->dev, "host is not ready.\n"); + dev_err(dev->dev, "host is not ready.\n"); goto err; } if (!mei_hw_is_ready(dev)) { - dev_err(&dev->pdev->dev, "ME is not ready.\n"); + dev_err(dev->dev, "ME is not ready.\n"); goto err; } if (!mei_hbm_version_is_supported(dev)) { - dev_dbg(&dev->pdev->dev, "MEI start failed.\n"); + dev_dbg(dev->dev, "MEI start failed.\n"); goto err; } - dev_dbg(&dev->pdev->dev, "link layer has been established.\n"); + dev_dbg(dev->dev, "link layer has been established.\n"); mutex_unlock(&dev->device_lock); return 0; err: - dev_err(&dev->pdev->dev, "link layer initialization failed.\n"); + dev_err(dev->dev, "link layer initialization failed.\n"); dev->dev_state = MEI_DEV_DISABLED; mutex_unlock(&dev->device_lock); return -ENODEV; @@ -231,7 +244,7 @@ EXPORT_SYMBOL_GPL(mei_start); * * @dev: the device structure * - * returns 0 on success or -ENODEV if the restart hasn't succeeded + * Return: 0 on success or -ENODEV if the restart hasn't succeeded */ int mei_restart(struct mei_device *dev) { @@ -249,7 +262,7 @@ int mei_restart(struct mei_device *dev) mutex_unlock(&dev->device_lock); if (err == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) { - dev_err(&dev->pdev->dev, "device disabled = %d\n", err); + dev_err(dev->dev, "device disabled = %d\n", err); return -ENODEV; } @@ -275,7 +288,7 @@ static void mei_reset_work(struct work_struct *work) mutex_unlock(&dev->device_lock); if (dev->dev_state == MEI_DEV_DISABLED) { - dev_err(&dev->pdev->dev, "device disabled = %d\n", ret); + dev_err(dev->dev, "device disabled = %d\n", ret); return; } @@ -286,7 +299,7 @@ static void mei_reset_work(struct work_struct *work) void mei_stop(struct mei_device *dev) { - dev_dbg(&dev->pdev->dev, "stopping the device.\n"); + dev_dbg(dev->dev, "stopping the device.\n"); mei_cancel_work(dev); @@ -312,7 +325,7 @@ EXPORT_SYMBOL_GPL(mei_stop); * * @dev: the device structure * - * returns true of there is no pending write + * Return: true of there is no pending write */ bool mei_write_is_idle(struct mei_device *dev) { @@ -320,7 +333,7 @@ bool mei_write_is_idle(struct mei_device *dev) list_empty(&dev->ctrl_wr_list.list) && list_empty(&dev->write_list.list)); - dev_dbg(&dev->pdev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d\n", + dev_dbg(dev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d\n", idle, mei_dev_state_str(dev->dev_state), list_empty(&dev->ctrl_wr_list.list), @@ -330,36 +343,25 @@ bool mei_write_is_idle(struct mei_device *dev) } EXPORT_SYMBOL_GPL(mei_write_is_idle); -int mei_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status) -{ - int i; - const struct mei_fw_status *fw_src = &dev->cfg->fw_status; - - if (!fw_status) - return -EINVAL; - - fw_status->count = fw_src->count; - for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { - int ret; - ret = pci_read_config_dword(dev->pdev, - fw_src->status[i], &fw_status->status[i]); - if (ret) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(mei_fw_status); - -void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg) +/** + * mei_device_init -- initialize mei_device structure + * + * @dev: the mei device + * @device: the device structure + * @hw_ops: hw operations + */ +void mei_device_init(struct mei_device *dev, + struct device *device, + const struct mei_hw_ops *hw_ops) { /* setup our list array */ INIT_LIST_HEAD(&dev->file_list); INIT_LIST_HEAD(&dev->device_list); + INIT_LIST_HEAD(&dev->me_clients); mutex_init(&dev->device_lock); init_waitqueue_head(&dev->wait_hw_ready); init_waitqueue_head(&dev->wait_pg); - init_waitqueue_head(&dev->wait_recvd_msg); + init_waitqueue_head(&dev->wait_hbm_start); init_waitqueue_head(&dev->wait_stop_wd); dev->dev_state = MEI_DEV_INITIALIZING; dev->reset_count = 0; @@ -389,7 +391,8 @@ void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg) bitmap_set(dev->host_clients_map, 0, 1); dev->pg_event = MEI_PG_EVENT_IDLE; - dev->cfg = cfg; + dev->ops = hw_ops; + dev->dev = device; } EXPORT_SYMBOL_GPL(mei_device_init); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 4e3cba6da3f5..20c6c511f438 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -16,11 +16,11 @@ #include <linux/export.h> -#include <linux/pci.h> #include <linux/kthread.h> #include <linux/interrupt.h> #include <linux/fs.h> #include <linux/jiffies.h> +#include <linux/slab.h> #include <linux/mei.h> @@ -33,8 +33,8 @@ * mei_irq_compl_handler - dispatch complete handlers * for the completed callbacks * - * @dev - mei device - * @compl_list - list of completed cbs + * @dev: mei device + * @compl_list: list of completed cbs */ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) { @@ -47,7 +47,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) if (!cl) continue; - dev_dbg(&dev->pdev->dev, "completing call back.\n"); + dev_dbg(dev->dev, "completing call back.\n"); if (cl == &dev->iamthif_cl) mei_amthif_complete(dev, cb); else @@ -62,7 +62,7 @@ EXPORT_SYMBOL_GPL(mei_irq_compl_handler); * @cl: host client * @mei_hdr: header of mei client message * - * returns true if matches, false otherwise + * Return: true if matches, false otherwise */ static inline int mei_cl_hbm_equal(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr) @@ -72,12 +72,12 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl, } /** * mei_cl_is_reading - checks if the client - is the one to read this message + * is the one to read this message * * @cl: mei client * @mei_hdr: header of mei message * - * returns true on match and false otherwise + * Return: true on match and false otherwise */ static bool mei_cl_is_reading(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr) { @@ -87,13 +87,13 @@ static bool mei_cl_is_reading(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr) } /** - * mei_irq_read_client_message - process client message + * mei_cl_irq_read_msg - process client message * * @dev: the device structure * @mei_hdr: header of mei client message * @complete_list: An instance of our list structure * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ static int mei_cl_irq_read_msg(struct mei_device *dev, struct mei_msg_hdr *mei_hdr, @@ -126,7 +126,6 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, GFP_KERNEL); if (!buffer) { - cl_err(dev, cl, "allocation failed.\n"); list_del(&cb->list); return -ENOMEM; } @@ -149,10 +148,10 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, break; } - dev_dbg(&dev->pdev->dev, "message read\n"); + dev_dbg(dev->dev, "message read\n"); if (!buffer) { mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); - dev_dbg(&dev->pdev->dev, "discarding message " MEI_HDR_FMT "\n", + dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n", MEI_HDR_PRM(mei_hdr)); } @@ -166,7 +165,7 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, * @cb: callback block. * @cmpl_list: complete list. * - * returns 0, OK; otherwise, error. + * Return: 0, OK; otherwise, error. */ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) @@ -195,16 +194,16 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, /** - * mei_cl_irq_close - processes close related operation from + * mei_cl_irq_disconnect - processes close related operation from * interrupt thread context - send disconnect request * * @cl: client * @cb: callback block. * @cmpl_list: complete list. * - * returns 0, OK; otherwise, error. + * Return: 0, OK; otherwise, error. */ -static int mei_cl_irq_close(struct mei_cl *cl, struct mei_cl_cb *cb, +static int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) { struct mei_device *dev = cl->dev; @@ -235,14 +234,14 @@ static int mei_cl_irq_close(struct mei_cl *cl, struct mei_cl_cb *cb, /** - * mei_cl_irq_close - processes client read related operation from the + * mei_cl_irq_read - processes client read related operation from the * interrupt thread context - request for flow control credits * * @cl: client * @cb: callback block. * @cmpl_list: complete list. * - * returns 0, OK; otherwise, error. + * Return: 0, OK; otherwise, error. */ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) @@ -279,7 +278,7 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, * @cb: callback block. * @cmpl_list: complete list. * - * returns 0, OK; otherwise, error. + * Return: 0, OK; otherwise, error. */ static int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) @@ -322,7 +321,7 @@ static int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, * @cmpl_list: An instance of our list structure * @slots: slots to read. * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_irq_read_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list, s32 *slots) @@ -334,20 +333,20 @@ int mei_irq_read_handler(struct mei_device *dev, if (!dev->rd_msg_hdr) { dev->rd_msg_hdr = mei_read_hdr(dev); (*slots)--; - dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots); + dev_dbg(dev->dev, "slots =%08x.\n", *slots); } mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr; - dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); + dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); if (mei_hdr->reserved || !dev->rd_msg_hdr) { - dev_err(&dev->pdev->dev, "corrupted message header 0x%08X\n", + dev_err(dev->dev, "corrupted message header 0x%08X\n", dev->rd_msg_hdr); ret = -EBADMSG; goto end; } if (mei_slots2data(*slots) < mei_hdr->length) { - dev_err(&dev->pdev->dev, "less data available than length=%08x.\n", + dev_err(dev->dev, "less data available than length=%08x.\n", *slots); /* we can't read the message */ ret = -ENODATA; @@ -358,7 +357,7 @@ int mei_irq_read_handler(struct mei_device *dev, if (mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0) { ret = mei_hbm_dispatch(dev, mei_hdr); if (ret) { - dev_dbg(&dev->pdev->dev, "mei_hbm_dispatch failed ret = %d\n", + dev_dbg(dev->dev, "mei_hbm_dispatch failed ret = %d\n", ret); goto end; } @@ -375,7 +374,7 @@ int mei_irq_read_handler(struct mei_device *dev, /* if no recipient cl was found we assume corrupted header */ if (&cl->link == &dev->file_list) { - dev_err(&dev->pdev->dev, "no destination client found 0x%08X\n", + dev_err(dev->dev, "no destination client found 0x%08X\n", dev->rd_msg_hdr); ret = -EBADMSG; goto end; @@ -387,14 +386,14 @@ int mei_irq_read_handler(struct mei_device *dev, ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list); if (ret) { - dev_err(&dev->pdev->dev, "mei_amthif_irq_read_msg failed = %d\n", + dev_err(dev->dev, "mei_amthif_irq_read_msg failed = %d\n", ret); goto end; } } else { ret = mei_cl_irq_read_msg(dev, mei_hdr, cmpl_list); if (ret) { - dev_err(&dev->pdev->dev, "mei_cl_irq_read_msg failed = %d\n", + dev_err(dev->dev, "mei_cl_irq_read_msg failed = %d\n", ret); goto end; } @@ -407,7 +406,7 @@ reset_slots: if (*slots == -EOVERFLOW) { /* overflow - reset */ - dev_err(&dev->pdev->dev, "resetting due to slots overflow.\n"); + dev_err(dev->dev, "resetting due to slots overflow.\n"); /* set the event since message has been read */ ret = -ERANGE; goto end; @@ -425,7 +424,7 @@ EXPORT_SYMBOL_GPL(mei_irq_read_handler); * @dev: the device structure * @cmpl_list: An instance of our list structure * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) { @@ -445,7 +444,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) return -EMSGSIZE; /* complete all waiting for write CB */ - dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n"); + dev_dbg(dev->dev, "complete all waiting for write cb.\n"); list = &dev->write_waiting_list; list_for_each_entry_safe(cb, next, &list->list, list) { @@ -487,7 +486,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) } /* complete control write list CB */ - dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); + dev_dbg(dev->dev, "complete control write list cb.\n"); list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) { cl = cb->cl; if (!cl) { @@ -495,9 +494,9 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) return -ENODEV; } switch (cb->fop_type) { - case MEI_FOP_CLOSE: + case MEI_FOP_DISCONNECT: /* send disconnect message */ - ret = mei_cl_irq_close(cl, cb, cmpl_list); + ret = mei_cl_irq_disconnect(cl, cb, cmpl_list); if (ret) return ret; @@ -528,7 +527,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) } /* complete write list CB */ - dev_dbg(&dev->pdev->dev, "complete write list cb.\n"); + dev_dbg(dev->dev, "complete write list cb.\n"); list_for_each_entry_safe(cb, next, &dev->write_list.list, list) { cl = cb->cl; if (cl == NULL) @@ -556,8 +555,6 @@ void mei_timer(struct work_struct *work) { unsigned long timeout; struct mei_cl *cl; - struct mei_cl_cb *cb_pos = NULL; - struct mei_cl_cb *cb_next = NULL; struct mei_device *dev = container_of(work, struct mei_device, timer_work.work); @@ -571,7 +568,7 @@ void mei_timer(struct work_struct *work) if (dev->init_clients_timer) { if (--dev->init_clients_timer == 0) { - dev_err(&dev->pdev->dev, "timer: init clients timeout hbm_state = %d.\n", + dev_err(dev->dev, "timer: init clients timeout hbm_state = %d.\n", dev->hbm_state); mei_reset(dev); goto out; @@ -586,7 +583,7 @@ void mei_timer(struct work_struct *work) list_for_each_entry(cl, &dev->file_list, link) { if (cl->timer_count) { if (--cl->timer_count == 0) { - dev_err(&dev->pdev->dev, "timer: connect/disconnect timeout.\n"); + dev_err(dev->dev, "timer: connect/disconnect timeout.\n"); mei_reset(dev); goto out; } @@ -598,7 +595,7 @@ void mei_timer(struct work_struct *work) if (dev->iamthif_stall_timer) { if (--dev->iamthif_stall_timer == 0) { - dev_err(&dev->pdev->dev, "timer: amthif hanged.\n"); + dev_err(dev->dev, "timer: amthif hanged.\n"); mei_reset(dev); dev->iamthif_msg_buf_size = 0; dev->iamthif_msg_buf_index = 0; @@ -620,27 +617,20 @@ void mei_timer(struct work_struct *work) timeout = dev->iamthif_timer + mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); - dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n", + dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n", dev->iamthif_timer); - dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout); - dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies); + dev_dbg(dev->dev, "timeout = %ld\n", timeout); + dev_dbg(dev->dev, "jiffies = %ld\n", jiffies); if (time_after(jiffies, timeout)) { /* * User didn't read the AMTHI data on time (15sec) * freeing AMTHI for other requests */ - dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n"); + dev_dbg(dev->dev, "freeing AMTHI for other requests\n"); - list_for_each_entry_safe(cb_pos, cb_next, - &dev->amthif_rd_complete_list.list, list) { - - cl = cb_pos->file_object->private_data; - - /* Finding the AMTHI entry. */ - if (cl == &dev->iamthif_cl) - list_del(&cb_pos->list); - } + mei_io_list_flush(&dev->amthif_rd_complete_list, + &dev->iamthif_cl); mei_io_cb_free(dev->iamthif_current_cb); dev->iamthif_current_cb = NULL; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 401a3d526cd0..beedc91f03a6 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -17,12 +17,12 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/device.h> +#include <linux/slab.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/aio.h> -#include <linux/pci.h> #include <linux/poll.h> #include <linux/init.h> #include <linux/ioctl.h> @@ -44,7 +44,7 @@ * @inode: pointer to inode structure * @file: pointer to file structure * - * returns 0 on success, <0 on error + * Return: 0 on success, <0 on error */ static int mei_open(struct inode *inode, struct file *file) { @@ -63,7 +63,7 @@ static int mei_open(struct inode *inode, struct file *file) err = -ENODEV; if (dev->dev_state != MEI_DEV_ENABLED) { - dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", + dev_dbg(dev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", mei_dev_state_str(dev->dev_state)); goto err_unlock; } @@ -96,7 +96,7 @@ err_unlock: * @inode: pointer to inode structure * @file: pointer to file structure * - * returns 0 on success, <0 on error + * Return: 0 on success, <0 on error */ static int mei_release(struct inode *inode, struct file *file) { @@ -157,7 +157,7 @@ out: * @length: buffer length * @offset: data offset in buffer * - * returns >=0 data length on success , <0 on error + * Return: >=0 data length on success , <0 on error */ static ssize_t mei_read(struct file *file, char __user *ubuf, size_t length, loff_t *offset) @@ -211,7 +211,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, err = mei_cl_read_start(cl, length); if (err && err != -EBUSY) { - dev_dbg(&dev->pdev->dev, + dev_dbg(dev->dev, "mei start read failure with status = %d\n", err); rets = err; goto out; @@ -254,7 +254,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, } /* now copy the data to user space */ copy_buffer: - dev_dbg(&dev->pdev->dev, "buf.size = %d buf.idx= %ld\n", + 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) { rets = -EMSGSIZE; @@ -266,7 +266,7 @@ copy_buffer: length = min_t(size_t, length, cb->buf_idx - *offset); if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) { - dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n"); + dev_dbg(dev->dev, "failed to copy data to userland\n"); rets = -EFAULT; goto free; } @@ -285,7 +285,7 @@ free: cl->reading_state = MEI_IDLE; cl->read_cb = NULL; out: - dev_dbg(&dev->pdev->dev, "end mei read rets= %d\n", rets); + dev_dbg(dev->dev, "end mei read rets= %d\n", rets); mutex_unlock(&dev->device_lock); return rets; } @@ -297,17 +297,17 @@ out: * @length: buffer length * @offset: data offset in buffer * - * returns >=0 data length on success , <0 on error + * Return: >=0 data length on success , <0 on error */ static ssize_t mei_write(struct file *file, const char __user *ubuf, size_t length, loff_t *offset) { struct mei_cl *cl = file->private_data; + struct mei_me_client *me_cl; struct mei_cl_cb *write_cb = NULL; struct mei_device *dev; unsigned long timeout = 0; int rets; - int id; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; @@ -321,8 +321,8 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; } - id = mei_me_cl_by_id(dev, cl->me_client_id); - if (id < 0) { + me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); + if (!me_cl) { rets = -ENOTTY; goto out; } @@ -332,13 +332,13 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; } - if (length > dev->me_clients[id].props.max_msg_length) { + if (length > me_cl->props.max_msg_length) { rets = -EFBIG; goto out; } if (cl->state != MEI_FILE_CONNECTED) { - dev_err(&dev->pdev->dev, "host client = %d, is not connected to ME client = %d", + dev_err(dev->dev, "host client = %d, is not connected to ME client = %d", cl->host_client_id, cl->me_client_id); rets = -ENODEV; goto out; @@ -377,7 +377,6 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, write_cb = mei_io_cb_init(cl, file); if (!write_cb) { - dev_err(&dev->pdev->dev, "write cb allocation failed\n"); rets = -ENOMEM; goto out; } @@ -387,7 +386,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); if (rets) { - dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n"); + dev_dbg(dev->dev, "failed to copy data from userland\n"); rets = -EFAULT; goto out; } @@ -396,7 +395,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = mei_amthif_write(dev, write_cb); if (rets) { - dev_err(&dev->pdev->dev, + dev_err(dev->dev, "amthif write failed with status = %d\n", rets); goto out; } @@ -415,27 +414,23 @@ out: /** * mei_ioctl_connect_client - the connect to fw client IOCTL function * - * @dev: the device structure - * @data: IOCTL connect data, input and output parameters * @file: private data of the file object + * @data: IOCTL connect data, input and output parameters * * Locking: called under "dev->device_lock" lock * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ static int mei_ioctl_connect_client(struct file *file, struct mei_connect_client_data *data) { struct mei_device *dev; struct mei_client *client; + struct mei_me_client *me_cl; struct mei_cl *cl; - int i; int rets; cl = file->private_data; - if (WARN_ON(!cl || !cl->dev)) - return -ENODEV; - dev = cl->dev; if (dev->dev_state != MEI_DEV_ENABLED) { @@ -450,28 +445,29 @@ static int mei_ioctl_connect_client(struct file *file, } /* find ME client we're trying to connect to */ - i = mei_me_cl_by_uuid(dev, &data->in_client_uuid); - if (i < 0 || dev->me_clients[i].props.fixed_address) { - dev_dbg(&dev->pdev->dev, "Cannot connect to FW Client UUID = %pUl\n", + me_cl = mei_me_cl_by_uuid(dev, &data->in_client_uuid); + if (!me_cl || me_cl->props.fixed_address) { + dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n", &data->in_client_uuid); rets = -ENOTTY; goto end; } - cl->me_client_id = dev->me_clients[i].client_id; + cl->me_client_id = me_cl->client_id; + cl->cl_uuid = me_cl->props.protocol_name; - dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n", + dev_dbg(dev->dev, "Connect to FW Client ID = %d\n", cl->me_client_id); - dev_dbg(&dev->pdev->dev, "FW Client - Protocol Version = %d\n", - dev->me_clients[i].props.protocol_version); - dev_dbg(&dev->pdev->dev, "FW Client - Max Msg Len = %d\n", - dev->me_clients[i].props.max_msg_length); + dev_dbg(dev->dev, "FW Client - Protocol Version = %d\n", + me_cl->props.protocol_version); + dev_dbg(dev->dev, "FW Client - Max Msg Len = %d\n", + me_cl->props.max_msg_length); /* if we're connecting to amthif client then we will use the * existing connection */ if (uuid_le_cmp(data->in_client_uuid, mei_amthif_guid) == 0) { - dev_dbg(&dev->pdev->dev, "FW Client is amthi\n"); + dev_dbg(dev->dev, "FW Client is amthi\n"); if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) { rets = -ENODEV; goto end; @@ -484,10 +480,8 @@ static int mei_ioctl_connect_client(struct file *file, file->private_data = &dev->iamthif_cl; client = &data->out_client_properties; - client->max_msg_length = - dev->me_clients[i].props.max_msg_length; - client->protocol_version = - dev->me_clients[i].props.protocol_version; + client->max_msg_length = me_cl->props.max_msg_length; + client->protocol_version = me_cl->props.protocol_version; rets = dev->iamthif_cl.status; goto end; @@ -496,9 +490,9 @@ static int mei_ioctl_connect_client(struct file *file, /* prepare the output buffer */ client = &data->out_client_properties; - client->max_msg_length = dev->me_clients[i].props.max_msg_length; - client->protocol_version = dev->me_clients[i].props.protocol_version; - dev_dbg(&dev->pdev->dev, "Can connect?\n"); + client->max_msg_length = me_cl->props.max_msg_length; + client->protocol_version = me_cl->props.protocol_version; + dev_dbg(dev->dev, "Can connect?\n"); rets = mei_cl_connect(cl, file); @@ -507,7 +501,6 @@ end: return rets; } - /** * mei_ioctl - the IOCTL function * @@ -515,24 +508,22 @@ end: * @cmd: ioctl command * @data: pointer to mei message structure * - * returns 0 on success , <0 on error + * Return: 0 on success , <0 on error */ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) { struct mei_device *dev; struct mei_cl *cl = file->private_data; - struct mei_connect_client_data *connect_data = NULL; + struct mei_connect_client_data connect_data; int rets; - if (cmd != IOCTL_MEI_CONNECT_CLIENT) - return -EINVAL; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; dev = cl->dev; - dev_dbg(&dev->pdev->dev, "IOCTL cmd = 0x%x", cmd); + dev_dbg(dev->dev, "IOCTL cmd = 0x%x", cmd); mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { @@ -540,38 +531,36 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) goto out; } - dev_dbg(&dev->pdev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n"); - - connect_data = kzalloc(sizeof(struct mei_connect_client_data), - GFP_KERNEL); - if (!connect_data) { - rets = -ENOMEM; - goto out; - } - dev_dbg(&dev->pdev->dev, "copy connect data from user\n"); - if (copy_from_user(connect_data, (char __user *)data, + switch (cmd) { + case IOCTL_MEI_CONNECT_CLIENT: + dev_dbg(dev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n"); + if (copy_from_user(&connect_data, (char __user *)data, sizeof(struct mei_connect_client_data))) { - dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n"); - rets = -EFAULT; - goto out; - } - - rets = mei_ioctl_connect_client(file, connect_data); + dev_dbg(dev->dev, "failed to copy data from userland\n"); + rets = -EFAULT; + goto out; + } - /* if all is ok, copying the data back to user. */ - if (rets) - goto out; + rets = mei_ioctl_connect_client(file, &connect_data); + if (rets) + goto out; - dev_dbg(&dev->pdev->dev, "copy connect data to user\n"); - if (copy_to_user((char __user *)data, connect_data, + /* if all is ok, copying the data back to user. */ + if (copy_to_user((char __user *)data, &connect_data, sizeof(struct mei_connect_client_data))) { - dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n"); - rets = -EFAULT; - goto out; + dev_dbg(dev->dev, "failed to copy data to userland\n"); + rets = -EFAULT; + goto out; + } + + break; + + default: + dev_err(dev->dev, ": unsupported ioctl %d.\n", cmd); + rets = -ENOIOCTLCMD; } out: - kfree(connect_data); mutex_unlock(&dev->device_lock); return rets; } @@ -583,7 +572,7 @@ out: * @cmd: ioctl command * @data: pointer to mei message structure * - * returns 0 on success , <0 on error + * Return: 0 on success , <0 on error */ #ifdef CONFIG_COMPAT static long mei_compat_ioctl(struct file *file, @@ -600,7 +589,7 @@ static long mei_compat_ioctl(struct file *file, * @file: pointer to file structure * @wait: pointer to poll_table structure * - * returns poll mask + * Return: poll mask */ static unsigned int mei_poll(struct file *file, poll_table *wait) { @@ -670,7 +659,7 @@ static DEFINE_IDR(mei_idr); * * @dev: device pointer * - * returns allocated minor, or -ENOSPC if no free minor left + * Return: allocated minor, or -ENOSPC if no free minor left */ static int mei_minor_get(struct mei_device *dev) { @@ -681,7 +670,7 @@ static int mei_minor_get(struct mei_device *dev) if (ret >= 0) dev->minor = ret; else if (ret == -ENOSPC) - dev_err(&dev->pdev->dev, "too many mei devices\n"); + dev_err(dev->dev, "too many mei devices\n"); mutex_unlock(&mei_minor_lock); return ret; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 0b0d6135543b..71744b16cc8c 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -129,20 +129,18 @@ enum mei_wd_states { /** * enum mei_cb_file_ops - file operation associated with the callback - * @MEI_FOP_READ - read - * @MEI_FOP_WRITE - write - * @MEI_FOP_CONNECT - connect - * @MEI_FOP_DISCONNECT_RSP - disconnect response - * @MEI_FOP_OPEN - open - * @MEI_FOP_CLOSE - close + * @MEI_FOP_READ: read + * @MEI_FOP_WRITE: write + * @MEI_FOP_CONNECT: connect + * @MEI_FOP_DISCONNECT: disconnect + * @MEI_FOP_DISCONNECT_RSP: disconnect response */ enum mei_cb_file_ops { MEI_FOP_READ = 0, MEI_FOP_WRITE, MEI_FOP_CONNECT, + MEI_FOP_DISCONNECT, MEI_FOP_DISCONNECT_RSP, - MEI_FOP_OPEN, - MEI_FOP_CLOSE }; /* @@ -159,8 +157,8 @@ struct mei_msg_data { /* * struct mei_fw_status - storage of FW status data * - * @count - number of actually available elements in array - * @status - FW status registers + * @count: number of actually available elements in array + * @status: FW status registers */ struct mei_fw_status { int count; @@ -170,11 +168,13 @@ struct mei_fw_status { /** * struct mei_me_client - representation of me (fw) client * - * @props - client properties - * @client_id - me client id - * @mei_flow_ctrl_creds - flow control credits + * @list: link in me client list + * @props: client properties + * @client_id: me client id + * @mei_flow_ctrl_creds: flow control credits */ struct mei_me_client { + struct list_head list; struct mei_client_properties props; u8 client_id; u8 mei_flow_ctrl_creds; @@ -186,8 +186,15 @@ struct mei_cl; /** * struct mei_cl_cb - file operation callback structure * - * @cl - file client who is running this operation - * @fop_type - file operation type + * @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_idx: last read index + * @read_time: last read operation time stamp (iamthif) + * @file_object: pointer to file structure + * @internal: communication between driver and FW flag */ struct mei_cl_cb { struct list_head list; @@ -201,7 +208,29 @@ struct mei_cl_cb { u32 internal:1; }; -/* MEI client instance carried as file->private_data*/ +/** + * struct mei_cl - me client host representation + * carried in file->private_data + * + * @link: link in the clients list + * @dev: mei parent device + * @state: file operation state + * @tx_wait: wait queue for tx completion + * @rx_wait: wait queue for rx completion + * @wait: wait queue for management operation + * @status: connection status + * @cl_uuid: client uuid name + * @host_client_id: host id + * @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 + * + * @device: device on the mei client bus + * @device_link: link to bus clients + */ struct mei_cl { struct list_head link; struct mei_device *dev; @@ -210,7 +239,7 @@ struct mei_cl { wait_queue_head_t rx_wait; wait_queue_head_t wait; int status; - /* ID of client connected */ + uuid_le cl_uuid; u8 host_client_id; u8 me_client_id; u8 mei_flow_ctrl_creds; @@ -222,35 +251,35 @@ struct mei_cl { /* MEI CL bus data */ struct mei_cl_device *device; struct list_head device_link; - uuid_le device_uuid; }; /** struct mei_hw_ops * - * @host_is_ready - query for host readiness + * @host_is_ready : query for host readiness - * @hw_is_ready - query if hw is ready - * @hw_reset - reset hw - * @hw_start - start hw after reset - * @hw_config - configure hw + * @hw_is_ready : query if hw is ready + * @hw_reset : reset hw + * @hw_start : start hw after reset + * @hw_config : configure hw - * @pg_state - power gating state of the device - * @pg_is_enabled - is power gating enabled + * @fw_status : get fw status registers + * @pg_state : power gating state of the device + * @pg_is_enabled : is power gating enabled - * @intr_clear - clear pending interrupts - * @intr_enable - enable interrupts - * @intr_disable - disable interrupts + * @intr_clear : clear pending interrupts + * @intr_enable : enable interrupts + * @intr_disable : disable interrupts - * @hbuf_free_slots - query for write buffer empty slots - * @hbuf_is_ready - query if write buffer is empty - * @hbuf_max_len - query for write buffer max len + * @hbuf_free_slots : query for write buffer empty slots + * @hbuf_is_ready : query if write buffer is empty + * @hbuf_max_len : query for write buffer max len - * @write - write a message to FW + * @write : write a message to FW - * @rdbuf_full_slots - query how many slots are filled + * @rdbuf_full_slots : query how many slots are filled - * @read_hdr - get first 4 bytes (header) - * @read - read a buffer from the FW + * @read_hdr : get first 4 bytes (header) + * @read : read a buffer from the FW */ struct mei_hw_ops { @@ -261,6 +290,8 @@ struct mei_hw_ops { int (*hw_start)(struct mei_device *dev); void (*hw_config)(struct mei_device *dev); + + int (*fw_status)(struct mei_device *dev, struct mei_fw_status *fw_sts); enum mei_pg_state (*pg_state)(struct mei_device *dev); bool (*pg_is_enabled)(struct mei_device *dev); @@ -328,11 +359,12 @@ void mei_cl_bus_exit(void); * when being probed and shall use it for doing ME bus I/O. * * @dev: linux driver model device pointer - * @uuid: me client uuid * @cl: mei client * @ops: ME transport ops + * @event_work: async work to execute event callback * @event_cb: Drivers register this callback to get asynchronous ME * events (e.g. Rx buffer pending) notifications. + * @event_context: event callback run context * @events: Events bitmask sent to the driver. * @priv_data: client private data */ @@ -352,7 +384,7 @@ struct mei_cl_device { }; - /** +/** * enum mei_pg_event - power gating transition events * * @MEI_PG_EVENT_IDLE: the driver is not in power gating transition @@ -376,67 +408,106 @@ enum mei_pg_state { MEI_PG_ON = 1, }; -/* - * mei_cfg - * - * @fw_status - FW status - * @quirk_probe - device exclusion quirk - */ -struct mei_cfg { - const struct mei_fw_status fw_status; - bool (*quirk_probe)(struct pci_dev *pdev); -}; - - -#define MEI_PCI_DEVICE(dev, cfg) \ - .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ - .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \ - .driver_data = (kernel_ulong_t)&(cfg) - +const char *mei_pg_state_str(enum mei_pg_state state); /** * struct mei_device - MEI private device struct - - * @pdev - pointer to pci device struct - * @cdev - character device - * @minor - minor number allocated for device * - * @reset_count - limits the number of consecutive resets - * @hbm_state - state of host bus message protocol - * @pg_event - power gating event - * @mem_addr - mem mapped base register address - - * @hbuf_depth - depth of hardware host/write buffer is slots - * @hbuf_is_ready - query if the host host/write buffer is ready - * @wr_msg - the buffer for hbm control messages - * @cfg - per device generation config and ops + * @dev : device on a bus + * @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 + * @ctrl_rd_list : pending control read list + * + * @file_list : list of opened handles + * @open_handle_count: number of opened handles + * + * @device_lock : big device lock + * @timer_work : MEI timer delayed work (timeouts) + * + * @recvd_hw_ready : hw ready message received flag + * + * @wait_hw_ready : wait queue for receive HW ready message form FW + * @wait_pg : wait queue for receive PG message from FW + * @wait_hbm_start : wait queue for receive HBM start message from FW + * @wait_stop_wd : wait queue for receive WD stop message from FW + * + * @reset_count : number of consecutive resets + * @dev_state : device state + * @hbm_state : state of host bus message protocol + * @init_clients_timer : HBM init handshake timeout + * + * @pg_event : power gating event + * @pg_domain : runtime PM domain + * + * @rd_msg_buf : control messages buffer + * @rd_msg_hdr : read message header storage + * + * @hbuf_depth : depth of hardware host/write buffer is slots + * @hbuf_is_ready : query if the host host/write buffer is ready + * @wr_msg : the buffer for hbm control messages + * + * @version : HBM protocol version in use + * @hbm_f_pg_supported : hbm feature pgi protocol + * + * @me_clients : list of FW clients + * @me_clients_map : FW clients bit map + * @host_clients_map : host clients id pool + * @me_client_index : last FW client index in enumeration + * + * @wd_cl : watchdog client + * @wd_state : watchdog client state + * @wd_pending : watchdog command is pending + * @wd_timeout : watchdog expiration timeout + * @wd_data : watchdog message buffer + * + * @amthif_cmd_list : amthif list for cmd waiting + * @amthif_rd_complete_list : amthif list for reading completed cmd data + * @iamthif_file_object : file for current amthif operation + * @iamthif_cl : amthif host client + * @iamthif_current_cb : amthif current operation callback + * @iamthif_open_count : number of opened amthif connections + * @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_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 + * @reset_work : work item for the device reset + * + * @device_list : mei client bus list + * + * @dbgfs_dir : debugfs mei root directory + * + * @ops: : hw specific operations + * @hw : hw specific data */ struct mei_device { - struct pci_dev *pdev; /* pointer to pci device struct */ + struct device *dev; struct cdev cdev; int minor; - /* - * lists of queues - */ - /* array of pointers to aio lists */ - struct mei_cl_cb read_list; /* driver read queue */ - struct mei_cl_cb write_list; /* driver write queue */ - struct mei_cl_cb write_waiting_list; /* write waiting queue */ - struct mei_cl_cb ctrl_wr_list; /* managed write IOCTL list */ - struct mei_cl_cb ctrl_rd_list; /* managed read IOCTL list */ + 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; + struct mei_cl_cb ctrl_rd_list; - /* - * list of files - */ struct list_head file_list; long open_handle_count; - /* - * lock for the device - */ - struct mutex device_lock; /* device lock */ - struct delayed_work timer_work; /* MEI timer delayed work (timeouts) */ + struct mutex device_lock; + struct delayed_work timer_work; bool recvd_hw_ready; /* @@ -444,7 +515,7 @@ struct mei_device { */ wait_queue_head_t wait_hw_ready; wait_queue_head_t wait_pg; - wait_queue_head_t wait_recvd_msg; + wait_queue_head_t wait_hbm_start; wait_queue_head_t wait_stop_wd; /* @@ -463,7 +534,7 @@ struct mei_device { struct dev_pm_domain pg_domain; #endif /* CONFIG_PM_RUNTIME */ - unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; /* control messages */ + unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; u32 rd_msg_hdr; /* write buffer */ @@ -477,12 +548,11 @@ struct mei_device { } wr_msg; struct hbm_version version; + unsigned int hbm_f_pg_supported:1; - struct mei_me_client *me_clients; /* Note: memory has to be allocated */ + struct list_head me_clients; DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX); DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX); - unsigned long me_clients_num; - unsigned long me_client_presentation_num; unsigned long me_client_index; struct mei_cl wd_cl; @@ -523,7 +593,6 @@ struct mei_device { const struct mei_hw_ops *ops; - const struct mei_cfg *cfg; char hw[0] __aligned(sizeof(void *)); }; @@ -535,8 +604,10 @@ static inline unsigned long mei_secs_to_jiffies(unsigned long sec) /** * mei_data2slots - get slots - number of (dwords) from a message length * + size of the mei header - * @length - size of the messages in bytes - * returns - number of slots + * + * @length: size of the messages in bytes + * + * Return: number of slots */ static inline u32 mei_data2slots(size_t length) { @@ -544,9 +615,11 @@ static inline u32 mei_data2slots(size_t length) } /** - * mei_slots2data- get data in slots - bytes from slots - * @slots - number of available slots - * returns - number of bytes in slots + * mei_slots2data - get data in slots - bytes from slots + * + * @slots: number of available slots + * + * Return: number of bytes in slots */ static inline u32 mei_slots2data(int slots) { @@ -556,7 +629,9 @@ static inline u32 mei_slots2data(int slots) /* * mei init function prototypes */ -void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg); +void mei_device_init(struct mei_device *dev, + struct device *device, + const struct mei_hw_ops *hw_ops); int mei_reset(struct mei_device *dev); int mei_start(struct mei_device *dev); int mei_restart(struct mei_device *dev); @@ -622,12 +697,12 @@ int mei_wd_host_init(struct mei_device *dev); /* * mei_watchdog_register - Registering watchdog interface * once we got connection to the WD Client - * @dev - mei device + * @dev: mei device */ int mei_watchdog_register(struct mei_device *dev); /* * mei_watchdog_unregister - Unregistering watchdog interface - * @dev - mei device + * @dev: mei device */ void mei_watchdog_unregister(struct mei_device *dev); @@ -723,7 +798,11 @@ static inline int mei_count_full_read_slots(struct mei_device *dev) return dev->ops->rdbuf_full_slots(dev); } -int mei_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status); +static inline int mei_fw_status(struct mei_device *dev, + struct mei_fw_status *fw_status) +{ + return dev->ops->fw_status(dev, fw_status); +} #define FW_STS_FMT "%08X %08X" #define FW_STS_PRM(fw_status) \ diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index 5ccc23bc7690..622654323177 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -19,7 +19,8 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/device.h> -#include <linux/pci.h> +#include <linux/slab.h> + #include <linux/mei_cl_bus.h> #include "mei_dev.h" @@ -87,14 +88,20 @@ struct mei_nfc_hci_hdr { #define MEI_NFC_HEADER_SIZE 10 -/** mei_nfc_dev - NFC mei device +/** + * struct mei_nfc_dev - NFC mei device * * @cl: NFC host client * @cl_info: NFC info host client * @init_work: perform connection to the info client + * @send_wq: send completion wait queue * @fw_ivn: NFC Interface Version Number * @vendor_id: NFC manufacturer ID * @radio_type: NFC radio type + * @bus_name: bus name + * + * @req_id: message counter + * @recv_req_id: reception message counter */ struct mei_nfc_dev { struct mei_cl *cl; @@ -163,7 +170,7 @@ static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev) return 0; default: - dev_err(&dev->pdev->dev, "Unknown radio type 0x%x\n", + dev_err(dev->dev, "Unknown radio type 0x%x\n", ndev->radio_type); return -EINVAL; @@ -175,14 +182,14 @@ static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev) ndev->bus_name = "pn544"; return 0; default: - dev_err(&dev->pdev->dev, "Unknown radio type 0x%x\n", + dev_err(dev->dev, "Unknown radio type 0x%x\n", ndev->radio_type); return -EINVAL; } default: - dev_err(&dev->pdev->dev, "Unknown vendor ID 0x%x\n", + dev_err(dev->dev, "Unknown vendor ID 0x%x\n", ndev->vendor_id); return -EINVAL; @@ -231,21 +238,21 @@ static int mei_nfc_connect(struct mei_nfc_dev *ndev) ret = __mei_cl_send(cl, (u8 *)cmd, connect_length); if (ret < 0) { - dev_err(&dev->pdev->dev, "Could not send connect cmd\n"); + dev_err(dev->dev, "Could not send connect cmd\n"); goto err; } bytes_recv = __mei_cl_recv(cl, (u8 *)reply, connect_resp_length); if (bytes_recv < 0) { - dev_err(&dev->pdev->dev, "Could not read connect response\n"); + dev_err(dev->dev, "Could not read connect response\n"); ret = bytes_recv; goto err; } - dev_info(&dev->pdev->dev, "IVN 0x%x Vendor ID 0x%x\n", + dev_info(dev->dev, "IVN 0x%x Vendor ID 0x%x\n", connect_resp->fw_ivn, connect_resp->vendor_id); - dev_info(&dev->pdev->dev, "ME FW %d.%d.%d.%d\n", + dev_info(dev->dev, "ME FW %d.%d.%d.%d\n", connect_resp->me_major, connect_resp->me_minor, connect_resp->me_hotfix, connect_resp->me_build); @@ -279,7 +286,7 @@ static int mei_nfc_if_version(struct mei_nfc_dev *ndev) ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd)); if (ret < 0) { - dev_err(&dev->pdev->dev, "Could not send IF version cmd\n"); + dev_err(dev->dev, "Could not send IF version cmd\n"); return ret; } @@ -293,7 +300,7 @@ static int mei_nfc_if_version(struct mei_nfc_dev *ndev) bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length); if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { - dev_err(&dev->pdev->dev, "Could not read IF version\n"); + dev_err(dev->dev, "Could not read IF version\n"); ret = -EIO; goto err; } @@ -319,7 +326,7 @@ static int mei_nfc_enable(struct mei_cl_device *cldev) ret = mei_nfc_connect(ndev); if (ret < 0) { - dev_err(&dev->pdev->dev, "Could not connect to NFC"); + dev_err(dev->dev, "Could not connect to NFC"); return ret; } @@ -361,7 +368,7 @@ static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length) if (!wait_event_interruptible_timeout(ndev->send_wq, ndev->recv_req_id == ndev->req_id, HZ)) { - dev_err(&dev->pdev->dev, "NFC MEI command timeout\n"); + dev_err(dev->dev, "NFC MEI command timeout\n"); err = -ETIME; } else { ndev->req_id++; @@ -418,8 +425,7 @@ static void mei_nfc_init(struct work_struct *work) if (mei_cl_connect(cl_info, NULL) < 0) { mutex_unlock(&dev->device_lock); - dev_err(&dev->pdev->dev, - "Could not connect to the NFC INFO ME client"); + dev_err(dev->dev, "Could not connect to the NFC INFO ME client"); goto err; } @@ -427,21 +433,19 @@ static void mei_nfc_init(struct work_struct *work) mutex_unlock(&dev->device_lock); if (mei_nfc_if_version(ndev) < 0) { - dev_err(&dev->pdev->dev, "Could not get the NFC interface version"); + dev_err(dev->dev, "Could not get the NFC interface version"); goto err; } - dev_info(&dev->pdev->dev, - "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", + dev_info(dev->dev, "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", ndev->fw_ivn, ndev->vendor_id, ndev->radio_type); mutex_lock(&dev->device_lock); if (mei_cl_disconnect(cl_info) < 0) { mutex_unlock(&dev->device_lock); - dev_err(&dev->pdev->dev, - "Could not disconnect the NFC INFO ME client"); + dev_err(dev->dev, "Could not disconnect the NFC INFO ME client"); goto err; } @@ -449,15 +453,13 @@ static void mei_nfc_init(struct work_struct *work) mutex_unlock(&dev->device_lock); if (mei_nfc_build_bus_name(ndev) < 0) { - dev_err(&dev->pdev->dev, - "Could not build the bus ID name\n"); + dev_err(dev->dev, "Could not build the bus ID name\n"); return; } cldev = mei_cl_add_device(dev, mei_nfc_guid, ndev->bus_name, &nfc_ops); if (!cldev) { - dev_err(&dev->pdev->dev, - "Could not add the NFC device to the MEI bus\n"); + dev_err(dev->dev, "Could not add the NFC device to the MEI bus\n"); goto err; } @@ -472,7 +474,6 @@ err: mei_nfc_free(ndev); mutex_unlock(&dev->device_lock); - return; } @@ -480,7 +481,8 @@ int mei_nfc_host_init(struct mei_device *dev) { struct mei_nfc_dev *ndev = &nfc_dev; struct mei_cl *cl_info, *cl = NULL; - int i, ret; + struct mei_me_client *me_cl; + int ret; /* already initialized */ if (ndev->cl_info) @@ -498,40 +500,38 @@ int mei_nfc_host_init(struct mei_device *dev) } /* check for valid client id */ - i = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid); - if (i < 0) { - dev_info(&dev->pdev->dev, "nfc: failed to find the client\n"); + me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid); + if (!me_cl) { + dev_info(dev->dev, "nfc: failed to find the client\n"); ret = -ENOTTY; goto err; } - cl_info->me_client_id = dev->me_clients[i].client_id; + cl_info->me_client_id = me_cl->client_id; + cl_info->cl_uuid = me_cl->props.protocol_name; ret = mei_cl_link(cl_info, MEI_HOST_CLIENT_ID_ANY); if (ret) goto err; - cl_info->device_uuid = mei_nfc_info_guid; list_add_tail(&cl_info->device_link, &dev->device_list); /* check for valid client id */ - i = mei_me_cl_by_uuid(dev, &mei_nfc_guid); - if (i < 0) { - dev_info(&dev->pdev->dev, "nfc: failed to find the client\n"); + me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid); + if (!me_cl) { + dev_info(dev->dev, "nfc: failed to find the client\n"); ret = -ENOTTY; goto err; } - cl->me_client_id = dev->me_clients[i].client_id; + cl->me_client_id = me_cl->client_id; + cl->cl_uuid = me_cl->props.protocol_name; ret = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); if (ret) goto err; - cl->device_uuid = mei_nfc_guid; - - list_add_tail(&cl->device_link, &dev->device_list); ndev->req_id = 1; @@ -551,6 +551,7 @@ err: void mei_nfc_host_exit(struct mei_device *dev) { struct mei_nfc_dev *ndev = &nfc_dev; + cancel_work_sync(&ndev->init_work); } diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 959c313d84a7..f3225b1643ab 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -98,12 +98,12 @@ static inline void mei_me_unset_pm_domain(struct mei_device *dev) {} #endif /* CONFIG_PM_RUNTIME */ /** - * mei_quirk_probe - probe for devices that doesn't valid ME interface + * mei_me_quirk_probe - probe for devices that doesn't valid ME interface * * @pdev: PCI device structure * @cfg: per generation config * - * returns true if ME Interface is valid, false otherwise + * Return: true if ME Interface is valid, false otherwise */ static bool mei_me_quirk_probe(struct pci_dev *pdev, const struct mei_cfg *cfg) @@ -117,12 +117,12 @@ static bool mei_me_quirk_probe(struct pci_dev *pdev, } /** - * mei_probe - Device Initialization Routine + * mei_me_probe - Device Initialization Routine * * @pdev: PCI device structure * @ent: entry in kcs_pci_tbl * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -249,7 +249,7 @@ end: } /** - * mei_remove - Device Removal Routine + * mei_me_remove - Device Removal Routine * * @pdev: PCI device structure * @@ -430,7 +430,7 @@ static int mei_me_pm_runtime_resume(struct device *device) */ static inline void mei_me_set_pm_domain(struct mei_device *dev) { - struct pci_dev *pdev = dev->pdev; + struct pci_dev *pdev = to_pci_dev(dev->dev); if (pdev->dev.bus && pdev->dev.bus->pm) { dev->pg_domain.ops = *pdev->dev.bus->pm; @@ -451,7 +451,7 @@ static inline void mei_me_set_pm_domain(struct mei_device *dev) static inline void mei_me_unset_pm_domain(struct mei_device *dev) { /* stop using pm callbacks if any */ - dev->pdev->dev.pm_domain = NULL; + dev->dev->pm_domain = NULL; } #endif /* CONFIG_PM_RUNTIME */ diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index 74727dda51c1..bee1c6fb7e75 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -36,7 +36,8 @@ #include "hw-txe.h" static const struct pci_device_id mei_txe_pci_tbl[] = { - {MEI_PCI_DEVICE(0x0F18, mei_txe_cfg)}, /* Baytrail */ + {PCI_VDEVICE(INTEL, 0x0F18)}, /* Baytrail */ + {0, } }; MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl); @@ -52,6 +53,7 @@ static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {} static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw) { int i; + for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) { if (hw->mem_addr[i]) { pci_iounmap(pdev, hw->mem_addr[i]); @@ -65,11 +67,10 @@ static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw) * @pdev: PCI device structure * @ent: entry in mei_txe_pci_tbl * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data); struct mei_device *dev; struct mei_txe_hw *hw; int err; @@ -100,7 +101,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } /* allocates and initializes the mei dev structure */ - dev = mei_txe_dev_init(pdev, cfg); + dev = mei_txe_dev_init(pdev); if (!dev) { err = -ENOMEM; goto release_regions; @@ -377,7 +378,7 @@ static int mei_txe_pm_runtime_resume(struct device *device) */ static inline void mei_txe_set_pm_domain(struct mei_device *dev) { - struct pci_dev *pdev = dev->pdev; + struct pci_dev *pdev = to_pci_dev(dev->dev); if (pdev->dev.bus && pdev->dev.bus->pm) { dev->pg_domain.ops = *pdev->dev.bus->pm; @@ -398,7 +399,7 @@ static inline void mei_txe_set_pm_domain(struct mei_device *dev) static inline void mei_txe_unset_pm_domain(struct mei_device *dev) { /* stop using pm callbacks if any */ - dev->pdev->dev.pm_domain = NULL; + dev->dev->pm_domain = NULL; } #endif /* CONFIG_PM_RUNTIME */ diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index a84a664dfccb..b836dfffceb5 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c @@ -17,7 +17,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/device.h> -#include <linux/pci.h> #include <linux/sched.h> #include <linux/watchdog.h> @@ -42,7 +41,7 @@ const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89, static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) { - dev_dbg(&dev->pdev->dev, "wd: set timeout=%d.\n", timeout); + dev_dbg(dev->dev, "wd: set timeout=%d.\n", timeout); memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE); memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16)); } @@ -52,14 +51,14 @@ static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) * * @dev: the device structure * - * returns -ENOTTY if wd client cannot be found + * Return: -ENOTTY if wd client cannot be found * -EIO if write has failed * 0 on success */ int mei_wd_host_init(struct mei_device *dev) { struct mei_cl *cl = &dev->wd_cl; - int id; + struct mei_me_client *me_cl; int ret; mei_cl_init(cl, dev); @@ -69,25 +68,26 @@ int mei_wd_host_init(struct mei_device *dev) /* check for valid client id */ - id = mei_me_cl_by_uuid(dev, &mei_wd_guid); - if (id < 0) { - dev_info(&dev->pdev->dev, "wd: failed to find the client\n"); + me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid); + if (!me_cl) { + dev_info(dev->dev, "wd: failed to find the client\n"); return -ENOTTY; } - cl->me_client_id = dev->me_clients[id].client_id; + cl->me_client_id = me_cl->client_id; + cl->cl_uuid = me_cl->props.protocol_name; ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); if (ret < 0) { - dev_info(&dev->pdev->dev, "wd: failed link client\n"); + dev_info(dev->dev, "wd: failed link client\n"); return ret; } ret = mei_cl_connect(cl, NULL); if (ret) { - dev_err(&dev->pdev->dev, "wd: failed to connect = %d\n", ret); + dev_err(dev->dev, "wd: failed to connect = %d\n", ret); mei_cl_unlink(cl); return ret; } @@ -105,7 +105,7 @@ int mei_wd_host_init(struct mei_device *dev) * * @dev: the device structure * - * returns 0 if success, + * Return: 0 if success, * -EIO when message send fails * -EINVAL when invalid message is to be sent * -ENODEV on flow control failure @@ -127,19 +127,19 @@ int mei_wd_send(struct mei_device *dev) else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE)) hdr.length = MEI_WD_STOP_MSG_SIZE; else { - dev_err(&dev->pdev->dev, "wd: invalid message is to be sent, aborting\n"); + dev_err(dev->dev, "wd: invalid message is to be sent, aborting\n"); return -EINVAL; } ret = mei_write_message(dev, &hdr, dev->wd_data); if (ret) { - dev_err(&dev->pdev->dev, "wd: write message failed\n"); + dev_err(dev->dev, "wd: write message failed\n"); return ret; } ret = mei_cl_flow_ctrl_reduce(cl); if (ret) { - dev_err(&dev->pdev->dev, "wd: flow_ctrl_reduce failed.\n"); + dev_err(dev->dev, "wd: flow_ctrl_reduce failed.\n"); return ret; } @@ -150,9 +150,8 @@ int mei_wd_send(struct mei_device *dev) * mei_wd_stop - sends watchdog stop message to fw. * * @dev: the device structure - * @preserve: indicate if to keep the timeout value * - * returns 0 if success + * Return: 0 if success * on error: * -EIO when message send fails * -EINVAL when invalid message is to be sent @@ -192,11 +191,10 @@ int mei_wd_stop(struct mei_device *dev) if (dev->wd_state != MEI_WD_IDLE) { /* timeout */ ret = -ETIME; - dev_warn(&dev->pdev->dev, - "wd: stop failed to complete ret=%d.\n", ret); + dev_warn(dev->dev, "wd: stop failed to complete ret=%d\n", ret); goto err; } - dev_dbg(&dev->pdev->dev, "wd: stop completed after %u msec\n", + dev_dbg(dev->dev, "wd: stop completed after %u msec\n", MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret)); return 0; err: @@ -208,7 +206,7 @@ err: * * @wd_dev - watchdog device struct * - * returns 0 if success, negative errno code for failure + * Return: 0 if success, negative errno code for failure */ static int mei_wd_ops_start(struct watchdog_device *wd_dev) { @@ -222,15 +220,13 @@ static int mei_wd_ops_start(struct watchdog_device *wd_dev) mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { - dev_dbg(&dev->pdev->dev, - "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n", + dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n", mei_dev_state_str(dev->dev_state)); goto end_unlock; } if (dev->wd_cl.state != MEI_FILE_CONNECTED) { - dev_dbg(&dev->pdev->dev, - "MEI Driver is not connected to Watchdog Client\n"); + dev_dbg(dev->dev, "MEI Driver is not connected to Watchdog Client\n"); goto end_unlock; } @@ -247,7 +243,7 @@ end_unlock: * * @wd_dev - watchdog device struct * - * returns 0 if success, negative errno code for failure + * Return: 0 if success, negative errno code for failure */ static int mei_wd_ops_stop(struct watchdog_device *wd_dev) { @@ -269,7 +265,7 @@ static int mei_wd_ops_stop(struct watchdog_device *wd_dev) * * @wd_dev - watchdog device struct * - * returns 0 if success, negative errno code for failure + * Return: 0 if success, negative errno code for failure */ static int mei_wd_ops_ping(struct watchdog_device *wd_dev) { @@ -283,7 +279,7 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev) mutex_lock(&dev->device_lock); if (dev->wd_cl.state != MEI_FILE_CONNECTED) { - dev_err(&dev->pdev->dev, "wd: not connected.\n"); + dev_err(dev->dev, "wd: not connected.\n"); ret = -ENODEV; goto end; } @@ -296,7 +292,7 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev) /* Check if we can send the ping to HW*/ if (ret && mei_hbuf_acquire(dev)) { - dev_dbg(&dev->pdev->dev, "wd: sending ping\n"); + dev_dbg(dev->dev, "wd: sending ping\n"); ret = mei_wd_send(dev); if (ret) @@ -317,7 +313,7 @@ end: * @wd_dev - watchdog device struct * @timeout - timeout value to set * - * returns 0 if success, negative errno code for failure + * Return: 0 if success, negative errno code for failure */ static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, unsigned int timeout) @@ -379,13 +375,12 @@ int mei_watchdog_register(struct mei_device *dev) ret = watchdog_register_device(&amt_wd_dev); mutex_lock(&dev->device_lock); if (ret) { - dev_err(&dev->pdev->dev, "wd: unable to register watchdog device = %d.\n", + dev_err(dev->dev, "wd: unable to register watchdog device = %d.\n", ret); return ret; } - dev_dbg(&dev->pdev->dev, - "wd: successfully register watchdog interface.\n"); + dev_dbg(dev->dev, "wd: successfully register watchdog interface.\n"); watchdog_set_drvdata(&amt_wd_dev, dev); return 0; } diff --git a/drivers/misc/spear13xx_pcie_gadget.c b/drivers/misc/spear13xx_pcie_gadget.c index 2e13614d41e8..fe3ad0ca9a3e 100644 --- a/drivers/misc/spear13xx_pcie_gadget.c +++ b/drivers/misc/spear13xx_pcie_gadget.c @@ -9,6 +9,7 @@ * warranty of any kind, whether express or implied. */ +#include <linux/device.h> #include <linux/clk.h> #include <linux/slab.h> #include <linux/delay.h> @@ -743,58 +744,33 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) struct config_item *cg_item; struct configfs_subsystem *subsys; - /* get resource for application registers*/ - - res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res0) { - dev_err(&pdev->dev, "no resource defined\n"); - return -EBUSY; - } - if (!request_mem_region(res0->start, resource_size(res0), - pdev->name)) { - dev_err(&pdev->dev, "pcie gadget region already claimed\n"); - return -EBUSY; - } - /* get resource for dbi registers*/ - - res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res1) { - dev_err(&pdev->dev, "no resource defined\n"); - goto err_rel_res0; - } - if (!request_mem_region(res1->start, resource_size(res1), - pdev->name)) { - dev_err(&pdev->dev, "pcie gadget region already claimed\n"); - goto err_rel_res0; - } - - target = kzalloc(sizeof(*target), GFP_KERNEL); + target = devm_kzalloc(&pdev->dev, sizeof(*target), GFP_KERNEL); if (!target) { dev_err(&pdev->dev, "out of memory\n"); - status = -ENOMEM; - goto err_rel_res; + return -ENOMEM; } cg_item = &target->subsys.su_group.cg_item; sprintf(cg_item->ci_namebuf, "pcie_gadget.%d", pdev->id); cg_item->ci_type = &pcie_gadget_target_type; config = &target->config; - config->va_app_base = (void __iomem *)ioremap(res0->start, - resource_size(res0)); - if (!config->va_app_base) { + + /* get resource for application registers*/ + res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + config->va_app_base = devm_ioremap_resource(&pdev->dev, res0); + if (IS_ERR(config->va_app_base)) { dev_err(&pdev->dev, "ioremap fail\n"); - status = -ENOMEM; - goto err_kzalloc; + return PTR_ERR(config->va_app_base); } + /* get resource for dbi registers*/ + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); config->base = (void __iomem *)res1->start; - config->va_dbi_base = (void __iomem *)ioremap(res1->start, - resource_size(res1)); - if (!config->va_dbi_base) { + config->va_dbi_base = devm_ioremap_resource(&pdev->dev, res1); + if (IS_ERR(config->va_dbi_base)) { dev_err(&pdev->dev, "ioremap fail\n"); - status = -ENOMEM; - goto err_iounmap_app; + return PTR_ERR(config->va_dbi_base); } platform_set_drvdata(pdev, target); @@ -802,15 +778,15 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no update irq?\n"); - status = irq; - goto err_iounmap; + return irq; } - status = request_irq(irq, spear_pcie_gadget_irq, 0, pdev->name, NULL); + status = devm_request_irq(&pdev->dev, irq, spear_pcie_gadget_irq, + 0, pdev->name, NULL); if (status) { dev_err(&pdev->dev, "pcie gadget interrupt IRQ%d already claimed\n", irq); - goto err_iounmap; + return status; } /* Register configfs hooks */ @@ -819,7 +795,7 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) mutex_init(&subsys->su_mutex); status = configfs_register_subsystem(subsys); if (status) - goto err_irq; + return status; /* * init basic pcie application registers @@ -835,13 +811,12 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) clk = clk_get_sys("pcie1", NULL); if (IS_ERR(clk)) { pr_err("%s:couldn't get clk for pcie1\n", __func__); - status = PTR_ERR(clk); - goto err_irq; + return PTR_ERR(clk); } status = clk_enable(clk); if (status) { pr_err("%s:couldn't enable clk for pcie1\n", __func__); - goto err_irq; + return status; } } else if (pdev->id == 2) { /* @@ -851,53 +826,26 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) clk = clk_get_sys("pcie2", NULL); if (IS_ERR(clk)) { pr_err("%s:couldn't get clk for pcie2\n", __func__); - status = PTR_ERR(clk); - goto err_irq; + return PTR_ERR(clk); } status = clk_enable(clk); if (status) { pr_err("%s:couldn't enable clk for pcie2\n", __func__); - goto err_irq; + return status; } } spear13xx_pcie_device_init(config); return 0; -err_irq: - free_irq(irq, NULL); -err_iounmap: - iounmap(config->va_dbi_base); -err_iounmap_app: - iounmap(config->va_app_base); -err_kzalloc: - kfree(target); -err_rel_res: - release_mem_region(res1->start, resource_size(res1)); -err_rel_res0: - release_mem_region(res0->start, resource_size(res0)); - return status; } static int spear_pcie_gadget_remove(struct platform_device *pdev) { - struct resource *res0, *res1; static struct pcie_gadget_target *target; - struct spear_pcie_gadget_config *config; - int irq; - res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - irq = platform_get_irq(pdev, 0); target = platform_get_drvdata(pdev); - config = &target->config; - free_irq(irq, NULL); - iounmap(config->va_dbi_base); - iounmap(config->va_app_base); - release_mem_region(res1->start, resource_size(res1)); - release_mem_region(res0->start, resource_size(res0)); configfs_unregister_subsystem(&target->subsys); - kfree(target); return 0; } diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 1972d57aadb3..54be83d3efdd 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -153,8 +153,9 @@ static void st_reg_complete(struct st_data_s *st_gdata, char err) (st_gdata->list[i]->priv_data, err); pr_info("protocol %d's cb sent %d\n", i, err); if (err) { /* cleanup registered protocol */ - st_gdata->protos_registered--; st_gdata->is_registered[i] = false; + if (st_gdata->protos_registered) + st_gdata->protos_registered--; } } } @@ -639,14 +640,12 @@ long st_unregister(struct st_proto_s *proto) return -EPROTONOSUPPORT; } - st_gdata->protos_registered--; + if (st_gdata->protos_registered) + st_gdata->protos_registered--; + remove_channel_from_table(st_gdata, proto); spin_unlock_irqrestore(&st_gdata->lock, flags); - /* paranoid check */ - if (st_gdata->protos_registered < ST_EMPTY) - st_gdata->protos_registered = ST_EMPTY; - if ((st_gdata->protos_registered == ST_EMPTY) && (!test_bit(ST_REG_PENDING, &st_gdata->st_state))) { pr_info(" all chnl_ids unregistered "); diff --git a/drivers/misc/vmw_vmci/vmci_datagram.c b/drivers/misc/vmw_vmci/vmci_datagram.c index f3cdd904fe4d..822665245588 100644 --- a/drivers/misc/vmw_vmci/vmci_datagram.c +++ b/drivers/misc/vmw_vmci/vmci_datagram.c @@ -328,7 +328,8 @@ int vmci_datagram_dispatch(u32 context_id, BUILD_BUG_ON(sizeof(struct vmci_datagram) != 24); - if (VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) { + if (dg->payload_size > VMCI_MAX_DG_SIZE || + VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) { pr_devel("Payload (size=%llu bytes) too big to send\n", (unsigned long long)dg->payload_size); return VMCI_ERROR_INVALID_ARGS; diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 76ee7750bc5e..f721299eb1ba 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -1702,6 +1702,46 @@ static int parport_ECP_supported(struct parport *pb) } #endif +#ifdef CONFIG_X86_32 +static int intel_bug_present_check_epp(struct parport *pb) +{ + const struct parport_pc_private *priv = pb->private_data; + int bug_present = 0; + + if (priv->ecr) { + /* store value of ECR */ + unsigned char ecr = inb(ECONTROL(pb)); + unsigned char i; + for (i = 0x00; i < 0x80; i += 0x20) { + ECR_WRITE(pb, i); + if (clear_epp_timeout(pb)) { + /* Phony EPP in ECP. */ + bug_present = 1; + break; + } + } + /* return ECR into the inital state */ + ECR_WRITE(pb, ecr); + } + + return bug_present; +} +static int intel_bug_present(struct parport *pb) +{ +/* Check whether the device is legacy, not PCI or PCMCIA. Only legacy is known to be affected. */ + if (pb->dev != NULL) { + return 0; + } + + return intel_bug_present_check_epp(pb); +} +#else +static int intel_bug_present(struct parport *pb) +{ + return 0; +} +#endif /* CONFIG_X86_32 */ + static int parport_ECPPS2_supported(struct parport *pb) { const struct parport_pc_private *priv = pb->private_data; @@ -1722,8 +1762,6 @@ static int parport_ECPPS2_supported(struct parport *pb) static int parport_EPP_supported(struct parport *pb) { - const struct parport_pc_private *priv = pb->private_data; - /* * Theory: * Bit 0 of STR is the EPP timeout bit, this bit is 0 @@ -1742,16 +1780,8 @@ static int parport_EPP_supported(struct parport *pb) return 0; /* No way to clear timeout */ /* Check for Intel bug. */ - if (priv->ecr) { - unsigned char i; - for (i = 0x00; i < 0x80; i += 0x20) { - ECR_WRITE(pb, i); - if (clear_epp_timeout(pb)) { - /* Phony EPP in ECP. */ - return 0; - } - } - } + if (intel_bug_present(pb)) + return 0; pb->modes |= PARPORT_MODE_EPP; |