From 6c467a106f4c93a05b359b6732bb2161d74a29ae Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 25 Nov 2016 13:44:36 +0000 Subject: extcon: arizona: Simplify micd_pol_gpio handling Currently we handle both old style GPIO and new style GPIOD differently simplify this slightly by converting the old style GPIO to a GPIOD and just using that from then on. Signed-off-by: Charles Keepax Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-arizona.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index d836d4ce5ee4..ed78b7c26627 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -236,12 +236,8 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode) mode %= info->micd_num_modes; - if (arizona->pdata.micd_pol_gpio > 0) - gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio, - info->micd_modes[mode].gpio); - else - gpiod_set_value_cansleep(info->micd_pol_gpio, - info->micd_modes[mode].gpio); + gpiod_set_value_cansleep(info->micd_pol_gpio, + info->micd_modes[mode].gpio); regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, ARIZONA_MICD_BIAS_SRC_MASK, @@ -1412,21 +1408,21 @@ static int arizona_extcon_probe(struct platform_device *pdev) regmap_update_bits(arizona->regmap, ARIZONA_GP_SWITCH_1, ARIZONA_SW1_MODE_MASK, arizona->pdata.gpsw); - if (arizona->pdata.micd_pol_gpio > 0) { + if (pdata->micd_pol_gpio > 0) { if (info->micd_modes[0].gpio) mode = GPIOF_OUT_INIT_HIGH; else mode = GPIOF_OUT_INIT_LOW; - ret = devm_gpio_request_one(&pdev->dev, - arizona->pdata.micd_pol_gpio, - mode, - "MICD polarity"); + ret = devm_gpio_request_one(&pdev->dev, pdata->micd_pol_gpio, + mode, "MICD polarity"); if (ret != 0) { dev_err(arizona->dev, "Failed to request GPIO%d: %d\n", - arizona->pdata.micd_pol_gpio, ret); + pdata->micd_pol_gpio, ret); goto err_register; } + + info->micd_pol_gpio = gpio_to_desc(pdata->micd_pol_gpio); } else { if (info->micd_modes[0].gpio) mode = GPIOD_OUT_HIGH; -- cgit v1.2.3 From 8a522bf2d4f788306443d36b26b54f0aedcdfdbe Mon Sep 17 00:00:00 2001 From: Peter Foley Date: Sun, 27 Nov 2016 21:37:20 -0500 Subject: extcon: adc-jack: Fix incompatible pointer type warning This patch fixes the incompatible warning of extcon-adc-jack.c driver when calling devm_extcon_dev_allocate(). Signed-off-by: Peter Foley [cw00.choi: Modify the patch title and descritpion] Signed-off-by: Chanwoo Choi --- include/linux/extcon/extcon-adc-jack.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/extcon/extcon-adc-jack.h b/include/linux/extcon/extcon-adc-jack.h index a0e03b13b449..2aa32075bca1 100644 --- a/include/linux/extcon/extcon-adc-jack.h +++ b/include/linux/extcon/extcon-adc-jack.h @@ -59,7 +59,7 @@ struct adc_jack_pdata { const char *name; const char *consumer_channel; - const enum extcon *cable_names; + const unsigned int *cable_names; /* The last entry's state should be 0 */ struct adc_jack_cond *adc_conditions; -- cgit v1.2.3 From e7d9dd5ab15fa6d895929176592d6946bf329cf8 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sat, 3 Dec 2016 16:56:49 +0800 Subject: extcon: Return error code on failure Function get_zeroed_page() returns a NULL pointer if there is no enough memory. In function extcon_sync(), it returns 0 if the call to get_zeroed_page() fails. The return value 0 indicates success in the context, which is incosistent with the execution status. This patch fixes the bug by returning -ENOMEM. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=188611 Signed-off-by: Pan Bian Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 78298460d168..7c1e3a7b14e0 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -453,7 +453,7 @@ int extcon_sync(struct extcon_dev *edev, unsigned int id) dev_err(&edev->dev, "out of memory in extcon_set_state\n"); kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE); - return 0; + return -ENOMEM; } length = name_show(&edev->dev, NULL, prop_buf); -- cgit v1.2.3 From af57fa4de24654aa4b2ee648dcd14a3e31bce3a6 Mon Sep 17 00:00:00 2001 From: Srikant Ritolia Date: Wed, 7 Dec 2016 17:29:39 +0530 Subject: extcon: Restructure multi-line comments to follow codingstyle Aligning all block comments in extcon subsystem as per linux coding style. Found using checkpatch.pl script. Signed-off-by: Srikant Ritolia Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-max14577.c | 6 ++++-- drivers/extcon/extcon-max77693.c | 12 ++++++++---- drivers/extcon/extcon-max77843.c | 24 ++++++++++++++++-------- drivers/extcon/extcon-rt8973a.c | 6 ++++-- drivers/extcon/extcon-sm5502.c | 6 ++++-- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c index 12e26c4e7763..f6414b7fa5bc 100644 --- a/drivers/extcon/extcon-max14577.c +++ b/drivers/extcon/extcon-max14577.c @@ -531,8 +531,10 @@ static int max14577_parse_irq(struct max14577_muic_info *info, int irq_type) case MAX14577_IRQ_INT1_ADC: case MAX14577_IRQ_INT1_ADCLOW: case MAX14577_IRQ_INT1_ADCERR: - /* Handle all of accessory except for - type of charger accessory */ + /* + * Handle all of accessory except for + * type of charger accessory. + */ info->irq_adc = true; return 1; case MAX14577_IRQ_INT2_CHGTYP: diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 68dbcb814b2f..62163468f205 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -188,8 +188,10 @@ enum max77693_muic_acc_type { MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE, MAX77693_MUIC_ADC_OPEN, - /* The below accessories have same ADC value so ADCLow and - ADC1K bit is used to separate specific accessory */ + /* + * The below accessories have same ADC value so ADCLow and + * ADC1K bit is used to separate specific accessory. + */ /* ADC|VBVolot|ADCLow|ADC1K| */ MAX77693_MUIC_GND_USB_HOST = 0x100, /* 0x0| 0| 0| 0| */ MAX77693_MUIC_GND_USB_HOST_VB = 0x104, /* 0x0| 1| 0| 0| */ @@ -970,8 +972,10 @@ static void max77693_muic_irq_work(struct work_struct *work) case MAX77693_MUIC_IRQ_INT1_ADC_LOW: case MAX77693_MUIC_IRQ_INT1_ADC_ERR: case MAX77693_MUIC_IRQ_INT1_ADC1K: - /* Handle all of accessory except for - type of charger accessory */ + /* + * Handle all of accessory except for + * type of charger accessory. + */ ret = max77693_muic_adc_handler(info); break; case MAX77693_MUIC_IRQ_INT2_CHGTYP: diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index 5d11fdf36e94..6e722d552cf1 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -97,8 +97,10 @@ enum max77843_muic_accessory_type { MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1, MAX77843_MUIC_ADC_OPEN, - /* The blow accessories should check - not only ADC value but also ADC1K and VBVolt value. */ + /* + * The below accessories should check + * not only ADC value but also ADC1K and VBVolt value. + */ /* Offset|ADC1K|VBVolt| */ MAX77843_MUIC_GND_USB_HOST = 0x100, /* 0x1| 0| 0| */ MAX77843_MUIC_GND_USB_HOST_VB = 0x101, /* 0x1| 0| 1| */ @@ -265,16 +267,20 @@ static int max77843_muic_get_cable_type(struct max77843_muic_info *info, /* Check GROUND accessory with charger cable */ if (adc == MAX77843_MUIC_ADC_GROUND) { if (chg_type == MAX77843_MUIC_CHG_NONE) { - /* The following state when charger cable is + /* + * The following state when charger cable is * disconnected but the GROUND accessory still - * connected */ + * connected. + */ *attached = false; cable_type = info->prev_chg_type; info->prev_chg_type = MAX77843_MUIC_CHG_NONE; } else { - /* The following state when charger cable is - * connected on the GROUND accessory */ + /* + * The following state when charger cable is + * connected on the GROUND accessory. + */ *attached = true; cable_type = MAX77843_MUIC_CHG_GND; info->prev_chg_type = MAX77843_MUIC_CHG_GND; @@ -299,11 +305,13 @@ static int max77843_muic_get_cable_type(struct max77843_muic_info *info, } else { *attached = true; - /* Offset|ADC1K|VBVolt| + /* + * Offset|ADC1K|VBVolt| * 0x1| 0| 0| USB-HOST * 0x1| 0| 1| USB-HOST with VB * 0x1| 1| 0| MHL - * 0x1| 1| 1| MHL with VB */ + * 0x1| 1| 1| MHL with VB + */ /* Get ADC1K register bit */ gnd_type = (info->status[MAX77843_MUIC_STATUS1] & MAX77843_MUIC_STATUS1_ADC1K_MASK); diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c index 174c388739ea..3e882aa107e8 100644 --- a/drivers/extcon/extcon-rt8973a.c +++ b/drivers/extcon/extcon-rt8973a.c @@ -142,8 +142,10 @@ enum rt8973a_muic_acc_type { 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. */ + /* + * 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| */ diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index b22325688503..106ef0297b53 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -135,8 +135,10 @@ enum sm5502_muic_acc_type { SM5502_MUIC_ADC_AUDIO_TYPE1, SM5502_MUIC_ADC_OPEN = 0x1f, - /* The below accessories have same ADC value (0x1f or 0x1e). - So, Device type1 is used to separate specific accessory. */ + /* + * The below accessories have same ADC value (0x1f or 0x1e). + * So, Device type1 is used to separate specific accessory. + */ /* |---------|--ADC| */ /* | [7:5]|[4:0]| */ SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e, /* | 001|11110| */ -- cgit v1.2.3 From 9fe172b9be532acc23e35ba693700383ab775e66 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Dec 2016 10:45:31 +0200 Subject: extcon: palmas: Check the parent instance to prevent the NULL extcon-palmas must be child of palmas and expects parent's drvdata to be valid. Check for non NULL parent drvdata and fail if it is NULL. Not doing so will result in a NULL pointer dereference later in the probe() parent drvdata is NULL (e.g. misplaced extcon-palmas node in device tree). Signed-off-by: Roger Quadros Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-palmas.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index 634ba70782de..a128fd2eb187 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -190,6 +190,11 @@ static int palmas_usb_probe(struct platform_device *pdev) struct palmas_usb *palmas_usb; int status; + if (!palmas) { + dev_err(&pdev->dev, "failed to get valid parent\n"); + return -EINVAL; + } + palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL); if (!palmas_usb) return -ENOMEM; -- cgit v1.2.3 From 768842413997c3c946a716427b78cc308a4fdae0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 19 Dec 2016 01:13:06 +0100 Subject: extcon: axp288: Remove dependency on non-existing platform_data When the extcon_axp288 driver was originally merged, it was merged with a dependency on some other driver providing platform data for it. However such another driver was never merged, so the extcon_axp288 as merged upstream has never worked, its probe method simply always returns -ENODEV. This commit drops the dependency on the pdata always being there, instead it treats not having pdata as the pdata having a NULL gpio_mux_control, something which the code was already prepared to handle. Note that the code for controlling the mux_control gpio is left in place, as this may be necessary to allow the axp288 pmic to properly detect the charger type (instead of assuming 500mA max charge current) on some tablets. This will make it easier for future patches to add support for this gpio by getting the gpio info from somewhere. Signed-off-by: Hans de Goede Acked-by: Chanwoo Choi Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-axp288.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index 42f41e808292..a84fab87e201 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -112,7 +112,7 @@ struct axp288_extcon_info { struct device *dev; struct regmap *regmap; struct regmap_irq_chip_data *regmap_irqc; - struct axp288_extcon_pdata *pdata; + struct gpio_desc *gpio_mux_cntl; int irq[EXTCON_IRQ_END]; struct extcon_dev *edev; struct notifier_block extcon_nb; @@ -216,8 +216,8 @@ notify_otg: * If VBUS is absent Connect D+/D- lines to PMIC for BC * detection. Else connect them to SOC for USB communication. */ - if (info->pdata->gpio_mux_cntl) - gpiod_set_value(info->pdata->gpio_mux_cntl, + if (info->gpio_mux_cntl) + gpiod_set_value(info->gpio_mux_cntl, vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC : EXTCON_GPIO_MUX_SEL_PMIC); @@ -271,6 +271,7 @@ static int axp288_extcon_probe(struct platform_device *pdev) { struct axp288_extcon_info *info; struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + struct axp288_extcon_pdata *pdata = pdev->dev.platform_data; int ret, i, pirq, gpio; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); @@ -280,15 +281,9 @@ static int axp288_extcon_probe(struct platform_device *pdev) info->dev = &pdev->dev; info->regmap = axp20x->regmap; info->regmap_irqc = axp20x->regmap_irqc; - info->pdata = pdev->dev.platform_data; - - if (!info->pdata) { - /* Try ACPI provided pdata via device properties */ - if (!device_property_present(&pdev->dev, - "axp288_extcon_data\n")) - dev_err(&pdev->dev, "failed to get platform data\n"); - return -ENODEV; - } + if (pdata) + info->gpio_mux_cntl = pdata->gpio_mux_cntl; + platform_set_drvdata(pdev, info); axp288_extcon_log_rsi(info); @@ -316,15 +311,15 @@ static int axp288_extcon_probe(struct platform_device *pdev) } /* Set up gpio control for USB Mux */ - if (info->pdata->gpio_mux_cntl) { - gpio = desc_to_gpio(info->pdata->gpio_mux_cntl); + if (info->gpio_mux_cntl) { + gpio = desc_to_gpio(info->gpio_mux_cntl); ret = devm_gpio_request(&pdev->dev, gpio, "USB_MUX"); if (ret < 0) { dev_err(&pdev->dev, "failed to request the gpio=%d\n", gpio); return ret; } - gpiod_direction_output(info->pdata->gpio_mux_cntl, + gpiod_direction_output(info->gpio_mux_cntl, EXTCON_GPIO_MUX_SEL_PMIC); } -- cgit v1.2.3 From 1490d157e0759b12913cc0d3b734a03bbcb8cb61 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 19 Dec 2016 01:13:07 +0100 Subject: extcon: axp288: Remove usb_phy notification code The usb_phy based intel-usb-phy code never got merged into the mainline kernel, so the devm_usb_get_phy() call will always fail, blocking the driver from loading. Since new drivers should use the generic-phy framework, not the old-style usb_phy stuff, keeping this around is not useful. Therefor this patch removes the usb_phy notification bits, which together with the patch to remove the platform_data dependency, makes this driver actually successfully probe on systems with an axp288 pmic. Signed-off-by: Hans de Goede Acked-by: Chanwoo Choi Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-axp288.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index a84fab87e201..3d5e84ea489d 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -116,7 +115,6 @@ struct axp288_extcon_info { int irq[EXTCON_IRQ_END]; struct extcon_dev *edev; struct notifier_block extcon_nb; - struct usb_phy *otg; }; /* Power up/down reason string array */ @@ -220,9 +218,6 @@ notify_otg: gpiod_set_value(info->gpio_mux_cntl, vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC : EXTCON_GPIO_MUX_SEL_PMIC); - - atomic_notifier_call_chain(&info->otg->notifier, - vbus_attach ? USB_EVENT_VBUS : USB_EVENT_NONE, NULL); } if (notify_charger) @@ -303,13 +298,6 @@ static int axp288_extcon_probe(struct platform_device *pdev) return ret; } - /* Get otg transceiver phy */ - info->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); - if (IS_ERR(info->otg)) { - dev_err(&pdev->dev, "failed to get otg transceiver\n"); - return PTR_ERR(info->otg); - } - /* Set up gpio control for USB Mux */ if (info->gpio_mux_cntl) { gpio = desc_to_gpio(info->gpio_mux_cntl); -- cgit v1.2.3 From 3fe1e0e2ab509863c1e9809a085508d8bce3e079 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 19 Dec 2016 01:13:08 +0100 Subject: extcon: axp288: Simplify axp288_handle_chrg_det_event axp288_handle_chrg_det_event only gets called on change interrupts (so not that often), extcon_set_state_sync() checks itself if there are any actual changes before notifying listeners, and gpiod_set_value is not really expensive either. So we can simply always do both on each interrupt removing a bunch of somewhat magic looking code from axp288_handle_chrg_det_event. Signed-off-by: Hans de Goede Acked-by: Chanwoo Choi Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-axp288.c | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index 3d5e84ea489d..43b3637a71a9 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -154,7 +154,6 @@ static void axp288_extcon_log_rsi(struct axp288_extcon_info *info) static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) { - static bool notify_otg, notify_charger; static unsigned int cable; int ret, stat, cfg, pwr_stat; u8 chrg_type; @@ -168,7 +167,7 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) vbus_attach = (pwr_stat & PS_STAT_VBUS_PRESENT); if (!vbus_attach) - goto notify_otg; + goto no_vbus; /* Check charger detection completion status */ ret = regmap_read(info->regmap, AXP288_BC_GLOBAL_REG, &cfg); @@ -188,19 +187,14 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) switch (chrg_type) { case DET_STAT_SDP: dev_dbg(info->dev, "sdp cable is connected\n"); - notify_otg = true; - notify_charger = true; cable = EXTCON_CHG_USB_SDP; break; case DET_STAT_CDP: dev_dbg(info->dev, "cdp cable is connected\n"); - notify_otg = true; - notify_charger = true; cable = EXTCON_CHG_USB_CDP; break; case DET_STAT_DCP: dev_dbg(info->dev, "dcp cable is connected\n"); - notify_charger = true; cable = EXTCON_CHG_USB_DCP; break; default: @@ -208,24 +202,17 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) "disconnect or unknown or ID event\n"); } -notify_otg: - if (notify_otg) { - /* - * If VBUS is absent Connect D+/D- lines to PMIC for BC - * detection. Else connect them to SOC for USB communication. - */ - if (info->gpio_mux_cntl) - gpiod_set_value(info->gpio_mux_cntl, - vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC - : EXTCON_GPIO_MUX_SEL_PMIC); - } - - if (notify_charger) - extcon_set_state_sync(info->edev, cable, vbus_attach); - - /* Clear the flags on disconnect event */ - if (!vbus_attach) - notify_otg = notify_charger = false; +no_vbus: + /* + * If VBUS is absent Connect D+/D- lines to PMIC for BC + * detection. Else connect them to SOC for USB communication. + */ + if (info->gpio_mux_cntl) + gpiod_set_value(info->gpio_mux_cntl, + vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC + : EXTCON_GPIO_MUX_SEL_PMIC); + + extcon_set_state_sync(info->edev, cable, vbus_attach); return 0; -- cgit v1.2.3 From 5d2199ea340d158587b09e2ab95908c210fd4742 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 19 Dec 2016 01:13:09 +0100 Subject: extcon: axp288: Fix possibly reporting 2 cables in state true When the charger type changes from e.g. SDP to CDP, without Vbus being seen as low in between axp288_handle_chrg_det_event would set the state for the new cable type to true, without clearing the state of the previous cable type to false. This commit fixes this and also gets rid of the function local static cable variable, properly storing all drv state in the axp288_extcon_info struct. Signed-off-by: Hans de Goede Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-axp288.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index 43b3637a71a9..98289a266810 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -115,6 +115,7 @@ struct axp288_extcon_info { int irq[EXTCON_IRQ_END]; struct extcon_dev *edev; struct notifier_block extcon_nb; + unsigned int previous_cable; }; /* Power up/down reason string array */ @@ -154,9 +155,9 @@ static void axp288_extcon_log_rsi(struct axp288_extcon_info *info) static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) { - static unsigned int cable; int ret, stat, cfg, pwr_stat; u8 chrg_type; + unsigned int cable = info->previous_cable; bool vbus_attach = false; ret = regmap_read(info->regmap, AXP288_PS_STAT_REG, &pwr_stat); @@ -212,7 +213,11 @@ no_vbus: vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC : EXTCON_GPIO_MUX_SEL_PMIC); - extcon_set_state_sync(info->edev, cable, vbus_attach); + extcon_set_state_sync(info->edev, info->previous_cable, false); + if (vbus_attach) { + extcon_set_state_sync(info->edev, cable, vbus_attach); + info->previous_cable = cable; + } return 0; @@ -263,6 +268,7 @@ static int axp288_extcon_probe(struct platform_device *pdev) info->dev = &pdev->dev; info->regmap = axp20x->regmap; info->regmap_irqc = axp20x->regmap_irqc; + info->previous_cable = EXTCON_NONE; if (pdata) info->gpio_mux_cntl = pdata->gpio_mux_cntl; -- cgit v1.2.3 From 5757aca10146061befd168dab37fb0db1ccd8f73 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 19 Dec 2016 01:13:11 +0100 Subject: extcon: axp288: Use vbus-valid instead of -present to determine cable presence The vbus-present bit in the power status register also gets set to 1 when a usb-host cable (id-pin shorted to ground) is plugged in and a 5v boost converter is supplying 5v to the otg usb bus. This causes a "disconnect or unknown or ID event" warning in dmesg as well as the extcon device to report the last detected charger cable type as being connected even though none is connected. This commit switches to checking the vbus-valid bit instead, which is only 1 when both vbus is present and the vbus-path is enabled in the vbus-path control register (the vbus-path gets disabled when a usb-host cable is detected, to avoid the pmic drawing power from the 5v boost converter). Signed-off-by: Hans de Goede Acked-by: Chanwoo Choi Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-axp288.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index 98289a266810..f8a326a005ec 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -166,7 +166,7 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) return ret; } - vbus_attach = (pwr_stat & PS_STAT_VBUS_PRESENT); + vbus_attach = (pwr_stat & PS_STAT_VBUS_VALID); if (!vbus_attach) goto no_vbus; -- cgit v1.2.3 From be1749528ea847c4a969b499fad8c4f98c44fac1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 19 Dec 2016 01:13:12 +0100 Subject: extcon: axp288: Remove unnecessary irq?_en register writes Setting the irq_enable bits is taken care of by the irq chip when we request the irqs and the driver should not be meddling with the irq?_en registers itself. Signed-off-by: Hans de Goede Acked-by: Chanwoo Choi Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-axp288.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index f8a326a005ec..0d3cf808539f 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -70,12 +70,6 @@ #define DET_STAT_CDP 2 #define DET_STAT_DCP 3 -/* IRQ enable-1 register */ -#define PWRSRC_IRQ_CFG_MASK (BIT(4)|BIT(3)|BIT(2)) - -/* IRQ enable-6 register */ -#define BC12_IRQ_CFG_MASK BIT(1) - enum axp288_extcon_reg { AXP288_PS_STAT_REG = 0x00, AXP288_PS_BOOT_REASON_REG = 0x02, @@ -83,8 +77,6 @@ enum axp288_extcon_reg { AXP288_BC_VBUS_CNTL_REG = 0x2d, AXP288_BC_USB_STAT_REG = 0x2e, AXP288_BC_DET_STAT_REG = 0x2f, - AXP288_PWRSRC_IRQ_CFG_REG = 0x40, - AXP288_BC12_IRQ_CFG_REG = 0x45, }; enum axp288_mux_select { @@ -240,15 +232,10 @@ static irqreturn_t axp288_extcon_isr(int irq, void *data) return IRQ_HANDLED; } -static void axp288_extcon_enable_irq(struct axp288_extcon_info *info) +static void axp288_extcon_enable(struct axp288_extcon_info *info) { - /* Unmask VBUS interrupt */ - regmap_write(info->regmap, AXP288_PWRSRC_IRQ_CFG_REG, - PWRSRC_IRQ_CFG_MASK); regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG, BC_GLOBAL_RUN, 0); - /* Unmask the BC1.2 complete interrupts */ - regmap_write(info->regmap, AXP288_BC12_IRQ_CFG_REG, BC12_IRQ_CFG_MASK); /* Enable the charger detection logic */ regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG, BC_GLOBAL_RUN, BC_GLOBAL_RUN); @@ -325,8 +312,8 @@ static int axp288_extcon_probe(struct platform_device *pdev) } } - /* Enable interrupts */ - axp288_extcon_enable_irq(info); + /* Start charger cable type detection */ + axp288_extcon_enable(info); return 0; } -- cgit v1.2.3 From dd3a55fc688b835b25fc73a16f17a75b6059858d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 19 Dec 2016 01:13:13 +0100 Subject: extcon: axp288: Fix the module not auto-loading Add a MODULE_DEVICE_TABLE to fix the module not auto-loading. Signed-off-by: Hans de Goede Acked-by: Chanwoo Choi Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-axp288.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index 0d3cf808539f..3e145e2a4860 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -318,8 +318,15 @@ static int axp288_extcon_probe(struct platform_device *pdev) return 0; } +static const struct platform_device_id axp288_extcon_table[] = { + { .name = "axp288_extcon" }, + {}, +}; +MODULE_DEVICE_TABLE(platform, axp288_extcon_table); + static struct platform_driver axp288_extcon_driver = { .probe = axp288_extcon_probe, + .id_table = axp288_extcon_table, .driver = { .name = "axp288_extcon", }, -- cgit v1.2.3 From 01b4c9a1ae07a25d208cad0da7dd288007a22984 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 19 Dec 2016 21:02:33 +0900 Subject: extcon: Remove potential problem when calling extcon_register_notifier() This patch removes the potential problem of extcon_register_notifier() when edev parameter is NULL. When edev is NULL, this function returns the first extcon device which includes the sepecific external connector of second paramter. But, it don't guarantee the same operation in all cases. To remove this confusion and potential problem, this patch fixes it. Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 7c1e3a7b14e0..d0e367959c91 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -906,35 +906,16 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, unsigned long flags; int ret, idx = -EINVAL; - if (!nb) + if (!edev || !nb) return -EINVAL; - if (edev) { - idx = find_cable_index_by_id(edev, id); - if (idx < 0) - return idx; - - spin_lock_irqsave(&edev->lock, flags); - ret = raw_notifier_chain_register(&edev->nh[idx], nb); - spin_unlock_irqrestore(&edev->lock, flags); - } else { - struct extcon_dev *extd; - - mutex_lock(&extcon_dev_list_lock); - list_for_each_entry(extd, &extcon_dev_list, entry) { - idx = find_cable_index_by_id(extd, id); - if (idx >= 0) - break; - } - mutex_unlock(&extcon_dev_list_lock); + idx = find_cable_index_by_id(edev, id); + if (idx < 0) + return idx; - if (idx >= 0) { - edev = extd; - return extcon_register_notifier(extd, id, nb); - } else { - ret = -ENODEV; - } - } + spin_lock_irqsave(&edev->lock, flags); + ret = raw_notifier_chain_register(&edev->nh[idx], nb); + spin_unlock_irqrestore(&edev->lock, flags); return ret; } -- cgit v1.2.3 From db6228612ce297949621a62e9d2331ee55016778 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 21 Dec 2016 14:10:47 +0800 Subject: extcon: Add documentation for EXTCON_CHG_USB_* and EXTCON_USB_* Current there is both "EXTCON_USB" and "EXTCON_CHG_USB_SDP" which both seem to suggest a standard downstream port. But there is no documentation describing how these relate. Thus add documentation to describe EXTCON_CHG_USB_SDP should always appear together with EXTCON_USB, and EXTCON_CHG_USB_ACA would normally appear with EXTCON_USB_HOST. Signed-off-by: Baolin Wang Signed-off-by: Chanwoo Choi --- include/linux/extcon.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/linux/extcon.h b/include/linux/extcon.h index b871c0cb1f02..00201230bea5 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -46,7 +46,14 @@ #define EXTCON_USB 1 #define EXTCON_USB_HOST 2 -/* Charging external connector */ +/* + * Charging external connector + * + * When one SDP charger connector was reported, we should also report + * the USB connector, which means EXTCON_CHG_USB_SDP should always + * appear together with EXTCON_USB. The same as ACA charger connector, + * EXTCON_CHG_USB_ACA would normally appear with EXTCON_USB_HOST. + */ #define EXTCON_CHG_USB_SDP 5 /* Standard Downstream Port */ #define EXTCON_CHG_USB_DCP 6 /* Dedicated Charging Port */ #define EXTCON_CHG_USB_CDP 7 /* Charging Downstream Port */ -- cgit v1.2.3 From 5298b8365832e13158e41e205a31a9505eef4c94 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 21 Dec 2016 15:51:26 +0900 Subject: extcon: axp288: Set EXTCON_USB when EXTCON_CHG_USB_SDP was set According to the documentation, we should set the EXTCON_USB when one SDP charger connector was reported. Signed-off-by: Baolin Wang Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-axp288.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index 3e145e2a4860..f4fd03e58e37 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -96,6 +96,7 @@ static const unsigned int axp288_extcon_cables[] = { EXTCON_CHG_USB_SDP, EXTCON_CHG_USB_CDP, EXTCON_CHG_USB_DCP, + EXTCON_USB, EXTCON_NONE, }; @@ -206,8 +207,15 @@ no_vbus: : EXTCON_GPIO_MUX_SEL_PMIC); extcon_set_state_sync(info->edev, info->previous_cable, false); + if (info->previous_cable == EXTCON_CHG_USB_SDP) + extcon_set_state_sync(info->edev, EXTCON_USB, false); + if (vbus_attach) { extcon_set_state_sync(info->edev, cable, vbus_attach); + if (cable == EXTCON_CHG_USB_SDP) + extcon_set_state_sync(info->edev, EXTCON_USB, + vbus_attach); + info->previous_cable = cable; } -- cgit v1.2.3 From 2f556bdb9f2e3794722c0d9186db9799b35071c4 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Wed, 21 Dec 2016 12:20:25 +0100 Subject: extcon: int3496: Add Intel INT3496 ACPI device extcon driver Add an extcon driver for USB OTG ports controlled by an Intel INT3496 ACPI device (e.g. Baytrail, Cherrytrail devices). Signed-off-by: David Cohen [hdgoede@redhat.com: Port to current kernel, cleanup, submit upstream] [hdgoede@redhat.com: Add Documentation/extcon/intel-int3496.txt] Signed-off-by: Hans de Goede Signed-off-by: Chanwoo Choi --- Documentation/extcon/intel-int3496.txt | 22 ++++ drivers/extcon/Kconfig | 10 ++ drivers/extcon/Makefile | 1 + drivers/extcon/extcon-intel-int3496.c | 179 +++++++++++++++++++++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 Documentation/extcon/intel-int3496.txt create mode 100644 drivers/extcon/extcon-intel-int3496.c diff --git a/Documentation/extcon/intel-int3496.txt b/Documentation/extcon/intel-int3496.txt new file mode 100644 index 000000000000..af0b366c25b7 --- /dev/null +++ b/Documentation/extcon/intel-int3496.txt @@ -0,0 +1,22 @@ +Intel INT3496 ACPI device extcon driver documentation +----------------------------------------------------- + +The Intel INT3496 ACPI device extcon driver is a driver for ACPI +devices with an acpi-id of INT3496, such as found for example on +Intel Baytrail and Cherrytrail tablets. + +This ACPI device describes how the OS can read the id-pin of the devices' +USB-otg port, as well as how it optionally can enable Vbus output on the +otg port and how it can optionally control the muxing of the data pins +between an USB host and an USB peripheral controller. + +The ACPI devices exposes this functionality by returning an array with up +to 3 gpio descriptors from its ACPI _CRS (Current Resource Settings) call: + +Index 0: The input gpio for the id-pin, this is always present and valid +Index 1: The output gpio for enabling Vbus output from the device to the otg + port, write 1 to enable the Vbus output (this gpio descriptor may + be absent or invalid) +Index 2: The output gpio for muxing of the data pins between the USB host and + the USB peripheral controller, write 1 to mux to the peripheral + controller diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 04788d92ea52..96bbae579c0b 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -42,6 +42,16 @@ config EXTCON_GPIO Say Y here to enable GPIO based extcon support. Note that GPIO extcon supports single state per extcon instance. +config EXTCON_INTEL_INT3496 + tristate "Intel INT3496 ACPI device extcon driver" + depends on GPIOLIB && ACPI + help + Say Y here to enable extcon support for USB OTG ports controlled by + an Intel INT3496 ACPI device. + + This ACPI device is typically found on Intel Baytrail or Cherrytrail + based tablets, or other Baytrail / Cherrytrail devices. + config EXTCON_MAX14577 tristate "Maxim MAX14577/77836 EXTCON Support" depends on MFD_MAX14577 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 31a0a999c4fb..237ac3f953c2 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o obj-$(CONFIG_EXTCON_AXP288) += extcon-axp288.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o +obj-$(CONFIG_EXTCON_INTEL_INT3496) += extcon-intel-int3496.o obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o obj-$(CONFIG_EXTCON_MAX3355) += extcon-max3355.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o diff --git a/drivers/extcon/extcon-intel-int3496.c b/drivers/extcon/extcon-intel-int3496.c new file mode 100644 index 000000000000..a3131b036de6 --- /dev/null +++ b/drivers/extcon/extcon-intel-int3496.c @@ -0,0 +1,179 @@ +/* + * Intel INT3496 ACPI device extcon driver + * + * Copyright (c) 2016 Hans de Goede + * + * Based on android x86 kernel code which is: + * + * Copyright (c) 2014, Intel Corporation. + * Author: David Cohen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#define INT3496_GPIO_USB_ID 0 +#define INT3496_GPIO_VBUS_EN 1 +#define INT3496_GPIO_USB_MUX 2 +#define DEBOUNCE_TIME msecs_to_jiffies(50) + +struct int3496_data { + struct device *dev; + struct extcon_dev *edev; + struct delayed_work work; + struct gpio_desc *gpio_usb_id; + struct gpio_desc *gpio_vbus_en; + struct gpio_desc *gpio_usb_mux; + int usb_id_irq; +}; + +static const unsigned int int3496_cable[] = { + EXTCON_USB_HOST, + EXTCON_NONE, +}; + +static void int3496_do_usb_id(struct work_struct *work) +{ + struct int3496_data *data = + container_of(work, struct int3496_data, work.work); + int id = gpiod_get_value_cansleep(data->gpio_usb_id); + + /* id == 1: PERIPHERAL, id == 0: HOST */ + dev_dbg(data->dev, "Connected %s cable\n", id ? "PERIPHERAL" : "HOST"); + + /* + * Peripheral: set USB mux to peripheral and disable VBUS + * Host: set USB mux to host and enable VBUS + */ + if (!IS_ERR(data->gpio_usb_mux)) + gpiod_direction_output(data->gpio_usb_mux, id); + + if (!IS_ERR(data->gpio_vbus_en)) + gpiod_direction_output(data->gpio_vbus_en, !id); + + extcon_set_state_sync(data->edev, EXTCON_USB_HOST, !id); +} + +static irqreturn_t int3496_thread_isr(int irq, void *priv) +{ + struct int3496_data *data = priv; + + /* Let the pin settle before processing it */ + mod_delayed_work(system_wq, &data->work, DEBOUNCE_TIME); + + return IRQ_HANDLED; +} + +static int int3496_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct int3496_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + INIT_DELAYED_WORK(&data->work, int3496_do_usb_id); + + data->gpio_usb_id = devm_gpiod_get_index(dev, "id", + INT3496_GPIO_USB_ID, + GPIOD_IN); + if (IS_ERR(data->gpio_usb_id)) { + ret = PTR_ERR(data->gpio_usb_id); + dev_err(dev, "can't request USB ID GPIO: %d\n", ret); + return ret; + } + + data->usb_id_irq = gpiod_to_irq(data->gpio_usb_id); + if (data->usb_id_irq <= 0) { + dev_err(dev, "can't get USB ID IRQ: %d\n", data->usb_id_irq); + return -EINVAL; + } + + data->gpio_vbus_en = devm_gpiod_get_index(dev, "vbus en", + INT3496_GPIO_VBUS_EN, + GPIOD_ASIS); + if (IS_ERR(data->gpio_vbus_en)) + dev_info(dev, "can't request VBUS EN GPIO\n"); + + data->gpio_usb_mux = devm_gpiod_get_index(dev, "usb mux", + INT3496_GPIO_USB_MUX, + GPIOD_ASIS); + if (IS_ERR(data->gpio_usb_mux)) + dev_info(dev, "can't request USB MUX GPIO\n"); + + /* register extcon device */ + data->edev = devm_extcon_dev_allocate(dev, int3496_cable); + if (IS_ERR(data->edev)) + return -ENOMEM; + + ret = devm_extcon_dev_register(dev, data->edev); + if (ret < 0) { + dev_err(dev, "can't register extcon device: %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(dev, data->usb_id_irq, + NULL, int3496_thread_isr, + IRQF_SHARED | IRQF_ONESHOT | + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + dev_name(dev), data); + if (ret < 0) { + dev_err(dev, "can't request IRQ for USB ID GPIO: %d\n", ret); + return ret; + } + + /* queue initial processing of id-pin */ + queue_delayed_work(system_wq, &data->work, 0); + + platform_set_drvdata(pdev, data); + + return 0; +} + +static int int3496_remove(struct platform_device *pdev) +{ + struct int3496_data *data = platform_get_drvdata(pdev); + + devm_free_irq(&pdev->dev, data->usb_id_irq, data); + cancel_delayed_work_sync(&data->work); + + return 0; +} + +static struct acpi_device_id int3496_acpi_match[] = { + { "INT3496" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, int3496_acpi_match); + +static struct platform_driver int3496_driver = { + .driver = { + .name = "intel-int3496", + .acpi_match_table = int3496_acpi_match, + }, + .probe = int3496_probe, + .remove = int3496_remove, +}; + +module_platform_driver(int3496_driver); + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("Intel INT3496 ACPI device extcon driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 6e3a7e89ff1a13d6e09f63ce9eff4ad3fad7b544 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 26 Dec 2016 20:47:13 +0900 Subject: extcon: adc-jack: Use the internal data instead of using struct extcon_dev This patch uses the internal dev instance instead of using the field of struct extcon_dev because the core structure (extcon_dev) of extcon have to be touched by only extcon core driver. Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-adc-jack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index bc538708c753..6f6537ab0a79 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -67,7 +67,7 @@ static void adc_jack_handler(struct work_struct *work) ret = iio_read_channel_raw(data->chan, &adc_val); if (ret < 0) { - dev_err(&data->edev->dev, "read channel() error: %d\n", ret); + dev_err(data->dev, "read channel() error: %d\n", ret); return; } -- cgit v1.2.3 From e6cf046543763878614d51e8283abd40d5e5327e Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 26 Dec 2016 20:37:38 +0900 Subject: extcon: Move defintion of struct extcon_dev to driver/extcon directory This patch moves the 'struct extcon_dev' of extcon subsystem to driver/extcon/extcon.h header file because the struct extcon_dev have to be handled by extcon API to guarantee the consistency of strcut extcon_dev. If external drivers are able to touch the struct extcon_dev directly, it might cause the critical and unknown problem. Signed-off-by: Chanwoo Choi --- drivers/extcon/devres.c | 2 +- drivers/extcon/extcon.c | 3 ++- drivers/extcon/extcon.h | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/extcon.h | 57 +-------------------------------------------- 4 files changed, 66 insertions(+), 58 deletions(-) create mode 100644 drivers/extcon/extcon.h diff --git a/drivers/extcon/devres.c b/drivers/extcon/devres.c index e686acd1c459..b40eb1805927 100644 --- a/drivers/extcon/devres.c +++ b/drivers/extcon/devres.c @@ -14,7 +14,7 @@ * GNU General Public License for more details. */ -#include +#include "extcon.h" static int devm_extcon_dev_match(struct device *dev, void *res, void *data) { diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index d0e367959c91..591582b0d2b7 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -30,11 +30,12 @@ #include #include #include -#include #include #include #include +#include "extcon.h" + #define SUPPORTED_CABLE_MAX 32 #define CABLE_NAME_MAX 30 diff --git a/drivers/extcon/extcon.h b/drivers/extcon/extcon.h new file mode 100644 index 000000000000..993ddccafe11 --- /dev/null +++ b/drivers/extcon/extcon.h @@ -0,0 +1,62 @@ +#ifndef __LINUX_EXTCON_INTERNAL_H__ +#define __LINUX_EXTCON_INTERNAL_H__ + +#include + +/** + * struct extcon_dev - An extcon device represents one external connector. + * @name: The name of this extcon device. Parent device name is + * used if NULL. + * @supported_cable: Array of supported cable names ending with EXTCON_NONE. + * If supported_cable is NULL, cable name related APIs + * are disabled. + * @mutually_exclusive: Array of mutually exclusive set of cables that cannot + * be attached simultaneously. The array should be + * ending with NULL or be NULL (no mutually exclusive + * cables). For example, if it is { 0x7, 0x30, 0}, then, + * {0, 1}, {0, 1, 2}, {0, 2}, {1, 2}, or {4, 5} cannot + * be attached simulataneously. {0x7, 0} is equivalent to + * {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there + * can be no simultaneous connections. + * @dev: Device of this extcon. + * @state: Attach/detach state of this extcon. Do not provide at + * register-time. + * @nh: Notifier for the state change events from this extcon + * @entry: To support list of extcon devices so that users can + * search for extcon devices based on the extcon name. + * @lock: + * @max_supported: Internal value to store the number of cables. + * @extcon_dev_type: Device_type struct to provide attribute_groups + * customized for each extcon device. + * @cables: Sysfs subdirectories. Each represents one cable. + * + * In most cases, users only need to provide "User initializing data" of + * this struct when registering an extcon. In some exceptional cases, + * optional callbacks may be needed. However, the values in "internal data" + * are overwritten by register function. + */ +struct extcon_dev { + /* Optional user initializing data */ + const char *name; + const unsigned int *supported_cable; + const u32 *mutually_exclusive; + + /* Internal data. Please do not set. */ + struct device dev; + struct raw_notifier_head *nh; + struct list_head entry; + int max_supported; + spinlock_t lock; /* could be called by irq handler */ + u32 state; + + /* /sys/class/extcon/.../cable.n/... */ + struct device_type extcon_dev_type; + struct extcon_cable *cables; + + /* /sys/class/extcon/.../mutually_exclusive/... */ + struct attribute_group attr_g_muex; + struct attribute **attrs_muex; + struct device_attribute *d_attrs_muex; +}; + +#endif /* __LINUX_EXTCON_INTERNAL_H__ */ diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 00201230bea5..d57e52443841 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -167,62 +167,7 @@ union extcon_property_value { }; struct extcon_cable; - -/** - * struct extcon_dev - An extcon device represents one external connector. - * @name: The name of this extcon device. Parent device name is - * used if NULL. - * @supported_cable: Array of supported cable names ending with EXTCON_NONE. - * If supported_cable is NULL, cable name related APIs - * are disabled. - * @mutually_exclusive: Array of mutually exclusive set of cables that cannot - * be attached simultaneously. The array should be - * ending with NULL or be NULL (no mutually exclusive - * cables). For example, if it is { 0x7, 0x30, 0}, then, - * {0, 1}, {0, 1, 2}, {0, 2}, {1, 2}, or {4, 5} cannot - * be attached simulataneously. {0x7, 0} is equivalent to - * {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there - * can be no simultaneous connections. - * @dev: Device of this extcon. - * @state: Attach/detach state of this extcon. Do not provide at - * register-time. - * @nh: Notifier for the state change events from this extcon - * @entry: To support list of extcon devices so that users can - * search for extcon devices based on the extcon name. - * @lock: - * @max_supported: Internal value to store the number of cables. - * @extcon_dev_type: Device_type struct to provide attribute_groups - * customized for each extcon device. - * @cables: Sysfs subdirectories. Each represents one cable. - * - * In most cases, users only need to provide "User initializing data" of - * this struct when registering an extcon. In some exceptional cases, - * optional callbacks may be needed. However, the values in "internal data" - * are overwritten by register function. - */ -struct extcon_dev { - /* Optional user initializing data */ - const char *name; - const unsigned int *supported_cable; - const u32 *mutually_exclusive; - - /* Internal data. Please do not set. */ - struct device dev; - struct raw_notifier_head *nh; - struct list_head entry; - int max_supported; - spinlock_t lock; /* could be called by irq handler */ - u32 state; - - /* /sys/class/extcon/.../cable.n/... */ - struct device_type extcon_dev_type; - struct extcon_cable *cables; - - /* /sys/class/extcon/.../mutually_exclusive/... */ - struct attribute_group attr_g_muex; - struct attribute **attrs_muex; - struct device_attribute *d_attrs_muex; -}; +struct extcon_dev; #if IS_ENABLED(CONFIG_EXTCON) -- cgit v1.2.3 From bcb7440e76a96c8a244bd683142a38f7d5cecb93 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 4 Jan 2017 15:19:51 +0800 Subject: extcon: usb-gpio: Add pinctrl operation during system PM At some systems, the pinctrl setting will be lost or needs to set as "sleep" state to save power consumption. So, we need to configure pinctrl as "sleep" state when system enters suspend, and as "default" state after system resumes. In this way, the pinctrl value can be recovered as "default" state after resuming. Signed-off-by: Peter Chen Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-usb-gpio.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index d589c5feff3d..a5e1882b4ca6 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -27,6 +27,7 @@ #include #include #include +#include #define USB_GPIO_DEBOUNCE_MS 20 /* ms */ @@ -245,6 +246,9 @@ static int usb_extcon_suspend(struct device *dev) if (info->vbus_gpiod) disable_irq(info->vbus_irq); + if (!device_may_wakeup(dev)) + pinctrl_pm_select_sleep_state(dev); + return ret; } @@ -253,6 +257,9 @@ static int usb_extcon_resume(struct device *dev) struct usb_extcon_info *info = dev_get_drvdata(dev); int ret = 0; + if (!device_may_wakeup(dev)) + pinctrl_pm_select_default_state(dev); + if (device_may_wakeup(dev)) { if (info->id_gpiod) { ret = disable_irq_wake(info->id_irq); -- cgit v1.2.3 From 62a37443e93bbae74410cb72aa9d7e15a1da0b98 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Tue, 3 Jan 2017 13:50:54 +0800 Subject: extcon: Add documentation for EXTCON_CHG_USB_SLOW/FAST Currently there are no documentation for EXTCON_CHG_USB_SLOW/FAST charger connector. These names don't mean much and no guide to tell users how to use it, thus try to add documentation to make them clear. Suggested-by: NeilBrown Signed-off-by: Baolin Wang [cw00.choi: Use the 'connector' expression instead of 'cable'] Signed-off-by: Chanwoo Choi --- include/linux/extcon.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/extcon.h b/include/linux/extcon.h index d57e52443841..242157cad25d 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -53,6 +53,10 @@ * the USB connector, which means EXTCON_CHG_USB_SDP should always * appear together with EXTCON_USB. The same as ACA charger connector, * EXTCON_CHG_USB_ACA would normally appear with EXTCON_USB_HOST. + * + * The EXTCON_CHG_USB_SLOW connector can provide at least 500mA of + * current at 5V. The EXTCON_CHG_USB_FAST connector can provide at + * least 1A of current at 5V. */ #define EXTCON_CHG_USB_SDP 5 /* Standard Downstream Port */ #define EXTCON_CHG_USB_DCP 6 /* Dedicated Charging Port */ -- cgit v1.2.3 From 3c5f0e076833c407cca372c663d47499ae4dab45 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 2 Jan 2017 13:03:03 +0900 Subject: extcon: Add new EXTCON_CHG_USB_PD type for USB Power Delivery This patch adds the new EXTCON_CHG_USB_PD for USB PD (Power Delivery)[1]. The USB Power Delivery specification specifies that USB cable provides the increased power more than 7.5W to device with larger power demand. The EXTCON_CHG_USB_PD has the EXTCON_TYPE_CHG and EXTCON_TYPE_USB type. [1] https://en.wikipedia.org/wiki/USB#PD Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 5 +++++ include/linux/extcon.h | 1 + 2 files changed, 6 insertions(+) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 591582b0d2b7..768e36769870 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -99,6 +99,11 @@ struct __extcon_info { .id = EXTCON_CHG_WPT, .name = "WPT", }, + [EXTCON_CHG_USB_PD] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_PD, + .name = "PD", + }, /* Jack external connector */ [EXTCON_JACK_MICROPHONE] = { diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 242157cad25d..7010fb01a81a 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -65,6 +65,7 @@ #define EXTCON_CHG_USB_FAST 9 #define EXTCON_CHG_USB_SLOW 10 #define EXTCON_CHG_WPT 11 /* Wireless Power Transfer */ +#define EXTCON_CHG_USB_PD 12 /* USB Power Delivery */ /* Jack external connector */ #define EXTCON_JACK_MICROPHONE 20 -- cgit v1.2.3 From 86d6cda68f371744a28003cb32b51aaf231b7ca5 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Sat, 7 Jan 2017 05:17:36 +0900 Subject: extcon: Modify the name of EXTCON_USB_HOST connector This patch renames the EXTCON_USB_HOST by using '-' char because the name of all external connector use the '-' char instead of '_' char. - "USB_HOST" -> "USB-HOST" Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 768e36769870..09ac5e70c2f3 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -60,7 +60,7 @@ struct __extcon_info { [EXTCON_USB_HOST] = { .type = EXTCON_TYPE_USB, .id = EXTCON_USB_HOST, - .name = "USB_HOST", + .name = "USB-HOST", }, /* Charging external connector */ -- cgit v1.2.3 From c0bb03924f1a80e7f65900e36c8e6b3dc167c5f8 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 7 Dec 2016 01:16:24 -0800 Subject: Drivers: hv: vmbus: Raise retry/wait limits in vmbus_post_msg() DoS protection conditions were altered in WS2016 and now it's easy to get -EAGAIN returned from vmbus_post_msg() (e.g. when we try changing MTU on a netvsc device in a loop). All vmbus_post_msg() callers don't retry the operation and we usually end up with a non-functional device or crash. While host's DoS protection conditions are unknown to me my tests show that it can take up to 10 seconds before the message is sent so doing udelay() is not an option, we really need to sleep. Almost all vmbus_post_msg() callers are ready to sleep but there is one special case: vmbus_initiate_unload() which can be called from interrupt/NMI context and we can't sleep there. I'm also not sure about the lonely vmbus_send_tl_connect_request() which has no in-tree users but its external users are most likely waiting for the host to reply so sleeping there is also appropriate. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 17 +++++++++-------- drivers/hv/channel_mgmt.c | 10 ++++++---- drivers/hv/connection.c | 17 ++++++++++++----- drivers/hv/hyperv_vmbus.h | 2 +- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 5fb4c6d9209b..d5b8d9fd50bb 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -181,7 +181,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); ret = vmbus_post_msg(open_msg, - sizeof(struct vmbus_channel_open_channel)); + sizeof(struct vmbus_channel_open_channel), true); if (ret != 0) { err = ret; @@ -233,7 +233,7 @@ int vmbus_send_tl_connect_request(const uuid_le *shv_guest_servie_id, conn_msg.guest_endpoint_id = *shv_guest_servie_id; conn_msg.host_service_id = *shv_host_servie_id; - return vmbus_post_msg(&conn_msg, sizeof(conn_msg)); + return vmbus_post_msg(&conn_msg, sizeof(conn_msg), true); } EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request); @@ -419,7 +419,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - - sizeof(*msginfo)); + sizeof(*msginfo), true); if (ret != 0) goto cleanup; @@ -433,8 +433,8 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, gpadl_body->gpadl = next_gpadl_handle; ret = vmbus_post_msg(gpadl_body, - submsginfo->msgsize - - sizeof(*submsginfo)); + submsginfo->msgsize - sizeof(*submsginfo), + true); if (ret != 0) goto cleanup; @@ -485,8 +485,8 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) list_add_tail(&info->msglistentry, &vmbus_connection.chn_msg_list); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - ret = vmbus_post_msg(msg, - sizeof(struct vmbus_channel_gpadl_teardown)); + ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_gpadl_teardown), + true); if (ret) goto post_msg_err; @@ -557,7 +557,8 @@ static int vmbus_close_internal(struct vmbus_channel *channel) msg->header.msgtype = CHANNELMSG_CLOSECHANNEL; msg->child_relid = channel->offermsg.child_relid; - ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel)); + ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel), + true); if (ret) { pr_err("Close failed: close post msg return is %d\n", ret); diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 26b419203f16..b1e85d24bb4e 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -321,7 +321,8 @@ static void vmbus_release_relid(u32 relid) memset(&msg, 0, sizeof(struct vmbus_channel_relid_released)); msg.child_relid = relid; msg.header.msgtype = CHANNELMSG_RELID_RELEASED; - vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); + vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released), + true); } void hv_event_tasklet_disable(struct vmbus_channel *channel) @@ -728,7 +729,8 @@ void vmbus_initiate_unload(bool crash) init_completion(&vmbus_connection.unload_event); memset(&hdr, 0, sizeof(struct vmbus_channel_message_header)); hdr.msgtype = CHANNELMSG_UNLOAD; - vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header)); + vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header), + !crash); /* * vmbus_initiate_unload() is also called on crash and the crash can be @@ -1116,8 +1118,8 @@ int vmbus_request_offers(void) msg->msgtype = CHANNELMSG_REQUESTOFFERS; - ret = vmbus_post_msg(msg, - sizeof(struct vmbus_channel_message_header)); + ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_message_header), + true); if (ret != 0) { pr_err("Unable to request offers - %d\n", ret); diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 6ce8b874e833..9b72ebcd37bc 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -111,7 +111,8 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); ret = vmbus_post_msg(msg, - sizeof(struct vmbus_channel_initiate_contact)); + sizeof(struct vmbus_channel_initiate_contact), + true); if (ret != 0) { spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); list_del(&msginfo->msglistentry); @@ -435,7 +436,7 @@ void vmbus_on_event(unsigned long data) /* * vmbus_post_msg - Send a msg on the vmbus's message connection */ -int vmbus_post_msg(void *buffer, size_t buflen) +int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep) { union hv_connection_id conn_id; int ret = 0; @@ -450,7 +451,7 @@ int vmbus_post_msg(void *buffer, size_t buflen) * insufficient resources. Retry the operation a couple of * times before giving up. */ - while (retries < 20) { + while (retries < 100) { ret = hv_post_message(conn_id, 1, buffer, buflen); switch (ret) { @@ -473,8 +474,14 @@ int vmbus_post_msg(void *buffer, size_t buflen) } retries++; - udelay(usec); - if (usec < 2048) + if (can_sleep && usec > 1000) + msleep(usec / 1000); + else if (usec < MAX_UDELAY_MS * 1000) + udelay(usec); + else + mdelay(usec / 1000); + + if (usec < 256000) usec *= 2; } return ret; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 0675b395ce5c..27982df20421 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -683,7 +683,7 @@ void vmbus_free_channels(void); int vmbus_connect(void); void vmbus_disconnect(void); -int vmbus_post_msg(void *buffer, size_t buflen); +int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep); void vmbus_on_event(unsigned long data); void vmbus_on_msg_dpc(unsigned long data); -- cgit v1.2.3 From 421b8f20d3c381b215f988b42428f56fc3b82405 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 7 Dec 2016 01:16:25 -0800 Subject: hv: allocate synic pages for all present CPUs It may happen that not all CPUs are online when we do hv_synic_alloc() and in case more CPUs come online later we may try accessing these allocated structures. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index b44b32f21e61..dc11296d50fd 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -411,7 +411,7 @@ int hv_synic_alloc(void) goto err; } - for_each_online_cpu(cpu) { + for_each_present_cpu(cpu) { hv_context.event_dpc[cpu] = kmalloc(size, GFP_ATOMIC); if (hv_context.event_dpc[cpu] == NULL) { pr_err("Unable to allocate event dpc\n"); @@ -482,7 +482,7 @@ void hv_synic_free(void) int cpu; kfree(hv_context.hv_numa_map); - for_each_online_cpu(cpu) + for_each_present_cpu(cpu) hv_synic_free_cpu(cpu); } -- cgit v1.2.3 From 3c7630d35009e6635e5b58d62de554fd5b6db5df Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 7 Dec 2016 01:16:26 -0800 Subject: hv: init percpu_list in hv_synic_alloc() Initializing hv_context.percpu_list in hv_synic_alloc() helps to prevent a crash in percpu_channel_enq() when not all CPUs were online during initialization and it naturally belongs there. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index dc11296d50fd..13937563c9db 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -457,6 +457,8 @@ int hv_synic_alloc(void) pr_err("Unable to allocate post msg page\n"); goto err; } + + INIT_LIST_HEAD(&hv_context.percpu_list[cpu]); } return 0; @@ -552,8 +554,6 @@ void hv_synic_init(void *arg) rdmsrl(HV_X64_MSR_VP_INDEX, vp_index); hv_context.vp_index[cpu] = (u32)vp_index; - INIT_LIST_HEAD(&hv_context.percpu_list[cpu]); - /* * Register the per-cpu clockevent source. */ -- cgit v1.2.3 From 56ef6718a1d8d77745033c5291e025ce18504159 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 7 Dec 2016 01:16:27 -0800 Subject: hv: don't reset hv_context.tsc_page on crash It may happen that secondary CPUs are still alive and resetting hv_context.tsc_page will cause a consequent crash in read_hv_clock_tsc() as we don't check for it being not NULL there. It is safe as we're not freeing this page anyways. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 13937563c9db..fbd8ce6d7ff3 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -309,9 +309,10 @@ void hv_cleanup(bool crash) hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); - if (!crash) + if (!crash) { vfree(hv_context.tsc_page); - hv_context.tsc_page = NULL; + hv_context.tsc_page = NULL; + } } #endif } -- cgit v1.2.3 From e7e97dd8b77ee7366f2f8c70a033bf5fa05ec2e0 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 7 Dec 2016 01:16:28 -0800 Subject: Drivers: hv: vmbus: Prevent sending data on a rescinded channel After the channel is rescinded, the host does not read from the rescinded channel. Fail writes to a channel that has already been rescinded. If we permit writes on a rescinded channel, since the host will not respond we will have situations where we will be unable to unload vmbus drivers that cannot have any outstanding requests to the host at the point they are unoaded. Signed-off-by: K. Y. Srinivasan Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/hv/ring_buffer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index cd49cb17eb7f..2cd402986858 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -298,6 +298,9 @@ int hv_ringbuffer_write(struct vmbus_channel *channel, unsigned long flags = 0; struct hv_ring_buffer_info *outring_info = &channel->outbound; + if (channel->rescind) + return -ENODEV; + for (i = 0; i < kv_count; i++) totalbytes_towrite += kv_list[i].iov_len; @@ -350,6 +353,10 @@ int hv_ringbuffer_write(struct vmbus_channel *channel, spin_unlock_irqrestore(&outring_info->ring_lock, flags); hv_signal_on_write(old_write, channel, kick_q); + + if (channel->rescind) + return -ENODEV; + return 0; } -- cgit v1.2.3 From 76d36ab79820430f73c584673aef10ba2446fced Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 7 Dec 2016 14:53:11 -0800 Subject: hv: switch to cpuhp state machine for synic init/cleanup To make it possible to online/offline CPUs switch to cpuhp infrastructure for doing hv_synic_init()/hv_synic_cleanup(). Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 15 +++++++-------- drivers/hv/hyperv_vmbus.h | 4 ++-- drivers/hv/vmbus_drv.c | 19 +++++++++++-------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index fbd8ce6d7ff3..8dbbed4efa45 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -496,7 +496,7 @@ void hv_synic_free(void) * retrieve the initialized message and event pages. Otherwise, we create and * initialize the message and event pages. */ -void hv_synic_init(void *arg) +int hv_synic_init(unsigned int cpu) { u64 version; union hv_synic_simp simp; @@ -505,10 +505,8 @@ void hv_synic_init(void *arg) union hv_synic_scontrol sctrl; u64 vp_index; - int cpu = smp_processor_id(); - if (!hv_context.hypercall_page) - return; + return -EFAULT; /* Check the version */ rdmsrl(HV_X64_MSR_SVERSION, version); @@ -563,7 +561,7 @@ void hv_synic_init(void *arg) HV_TIMER_FREQUENCY, HV_MIN_DELTA_TICKS, HV_MAX_MAX_DELTA_TICKS); - return; + return 0; } /* @@ -583,16 +581,15 @@ void hv_synic_clockevents_cleanup(void) /* * hv_synic_cleanup - Cleanup routine for hv_synic_init(). */ -void hv_synic_cleanup(void *arg) +int hv_synic_cleanup(unsigned int cpu) { union hv_synic_sint shared_sint; union hv_synic_simp simp; union hv_synic_siefp siefp; union hv_synic_scontrol sctrl; - int cpu = smp_processor_id(); if (!hv_context.synic_initialized) - return; + return -EFAULT; /* Turn off clockevent device */ if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE) { @@ -624,4 +621,6 @@ void hv_synic_cleanup(void *arg) rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); sctrl.enable = 0; wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); + + return 0; } diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 27982df20421..83beea748c6f 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -505,9 +505,9 @@ extern int hv_synic_alloc(void); extern void hv_synic_free(void); -extern void hv_synic_init(void *irqarg); +extern int hv_synic_init(unsigned int cpu); -extern void hv_synic_cleanup(void *arg); +extern int hv_synic_cleanup(unsigned int cpu); extern void hv_synic_clockevents_cleanup(void); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 230c62e7f567..a16d519b7e37 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -54,6 +54,7 @@ static struct acpi_device *hv_acpi_dev; static struct completion probe_event; +static int hyperv_cpuhp_online; static void hyperv_report_panic(struct pt_regs *regs) { @@ -997,7 +998,12 @@ static int vmbus_bus_init(void) * Initialize the per-cpu interrupt state and * connect to the host. */ - on_each_cpu(hv_synic_init, NULL, 1); + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/hyperv:online", + hv_synic_init, hv_synic_cleanup); + if (ret < 0) + goto err_alloc; + hyperv_cpuhp_online = ret; + ret = vmbus_connect(); if (ret) goto err_connect; @@ -1019,7 +1025,7 @@ static int vmbus_bus_init(void) return 0; err_connect: - on_each_cpu(hv_synic_cleanup, NULL, 1); + cpuhp_remove_state(hyperv_cpuhp_online); err_alloc: hv_synic_free(); hv_remove_vmbus_irq(); @@ -1478,12 +1484,9 @@ static struct acpi_driver vmbus_acpi_driver = { static void hv_kexec_handler(void) { - int cpu; - hv_synic_clockevents_cleanup(); vmbus_initiate_unload(false); - for_each_online_cpu(cpu) - smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); + cpuhp_remove_state(hyperv_cpuhp_online); hv_cleanup(false); }; @@ -1495,7 +1498,7 @@ static void hv_crash_handler(struct pt_regs *regs) * doing the cleanup for current CPU only. This should be sufficient * for kdump. */ - hv_synic_cleanup(NULL); + hv_synic_cleanup(smp_processor_id()); hv_cleanup(true); }; @@ -1559,8 +1562,8 @@ static void __exit vmbus_exit(void) hv_cleanup(false); for_each_online_cpu(cpu) { tasklet_kill(hv_context.event_dpc[cpu]); - smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); } + cpuhp_remove_state(hyperv_cpuhp_online); hv_synic_free(); acpi_bus_unregister_driver(&vmbus_acpi_driver); if (vmbus_proto_version > VERSION_WIN7) -- cgit v1.2.3 From 523b94087078f7f5ac10b7d9cd04277927031c39 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 7 Dec 2016 14:53:12 -0800 Subject: hv: make CPU offlining prevention fine-grained Since commit e513229b4c38 ("Drivers: hv: vmbus: prevent cpu offlining on newer hypervisors") cpu offlining was disabled. It is still true that we can't offline CPUs which have VMBus channels bound to them but we may have 'free' CPUs (e.v. we booted with maxcpus= parameter and onlined CPUs after VMBus was initialized), these CPUs may be disabled without issues. In future, we may even allow closing CPUs which have only sub-channels assinged to them by closing these sub-channels. All devices will continue to work. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 31 +++++++++++++++++++++++++++++++ drivers/hv/vmbus_drv.c | 9 ++++----- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 8dbbed4efa45..714e1ebc834c 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -587,10 +587,41 @@ int hv_synic_cleanup(unsigned int cpu) union hv_synic_simp simp; union hv_synic_siefp siefp; union hv_synic_scontrol sctrl; + struct vmbus_channel *channel, *sc; + bool channel_found = false; + unsigned long flags; if (!hv_context.synic_initialized) return -EFAULT; + /* + * Search for channels which are bound to the CPU we're about to + * cleanup. In case we find one and vmbus is still connected we need to + * fail, this will effectively prevent CPU offlining. There is no way + * we can re-bind channels to different CPUs for now. + */ + mutex_lock(&vmbus_connection.channel_mutex); + list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { + if (channel->target_cpu == cpu) { + channel_found = true; + break; + } + spin_lock_irqsave(&channel->lock, flags); + list_for_each_entry(sc, &channel->sc_list, sc_list) { + if (sc->target_cpu == cpu) { + channel_found = true; + break; + } + } + spin_unlock_irqrestore(&channel->lock, flags); + if (channel_found) + break; + } + mutex_unlock(&vmbus_connection.channel_mutex); + + if (channel_found && vmbus_connection.conn_state == CONNECTED) + return -EBUSY; + /* Turn off clockevent device */ if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE) { clockevents_unbind_device(hv_context.clk_evt[cpu], cpu); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index a16d519b7e37..565bdd16134a 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1008,9 +1008,6 @@ static int vmbus_bus_init(void) if (ret) goto err_connect; - if (vmbus_proto_version > VERSION_WIN7) - cpu_hotplug_disable(); - /* * Only register if the crash MSRs are available */ @@ -1486,6 +1483,9 @@ static void hv_kexec_handler(void) { hv_synic_clockevents_cleanup(); vmbus_initiate_unload(false); + vmbus_connection.conn_state = DISCONNECTED; + /* Make sure conn_state is set as hv_synic_cleanup checks for it */ + mb(); cpuhp_remove_state(hyperv_cpuhp_online); hv_cleanup(false); }; @@ -1498,6 +1498,7 @@ static void hv_crash_handler(struct pt_regs *regs) * doing the cleanup for current CPU only. This should be sufficient * for kdump. */ + vmbus_connection.conn_state = DISCONNECTED; hv_synic_cleanup(smp_processor_id()); hv_cleanup(true); }; @@ -1566,8 +1567,6 @@ static void __exit vmbus_exit(void) cpuhp_remove_state(hyperv_cpuhp_online); hv_synic_free(); acpi_bus_unregister_driver(&vmbus_acpi_driver); - if (vmbus_proto_version > VERSION_WIN7) - cpu_hotplug_enable(); } -- cgit v1.2.3 From ccb61f8a99e6c29df4fb96a65dad4fad740d5be9 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 22 Dec 2016 16:54:00 -0800 Subject: Drivers: hv: vmbus: Fix a rescind handling bug The host can rescind a channel that has been offered to the guest and once the channel is rescinded, the host does not respond to any requests on that channel. Deal with the case where the guest may be blocked waiting for a response from the host. Signed-off-by: K. Y. Srinivasan Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 18 ++++++++++++++++++ drivers/hv/channel_mgmt.c | 25 +++++++++++++++++++++++++ include/linux/hyperv.h | 1 + 3 files changed, 44 insertions(+) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index d5b8d9fd50bb..be34547cdb68 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -157,6 +157,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, } init_completion(&open_info->waitevent); + open_info->waiting_channel = newchannel; open_msg = (struct vmbus_channel_open_channel *)open_info->msg; open_msg->header.msgtype = CHANNELMSG_OPENCHANNEL; @@ -194,6 +195,11 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, list_del(&open_info->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + if (newchannel->rescind) { + err = -ENODEV; + goto error_free_gpadl; + } + if (open_info->response.open_result.status) { err = -EAGAIN; goto error_free_gpadl; @@ -405,6 +411,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, return ret; init_completion(&msginfo->waitevent); + msginfo->waiting_channel = channel; gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg; gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER; @@ -441,6 +448,11 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, } wait_for_completion(&msginfo->waitevent); + if (channel->rescind) { + ret = -ENODEV; + goto cleanup; + } + /* At this point, we received the gpadl created msg */ *gpadl_handle = gpadlmsg->gpadl; @@ -474,6 +486,7 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) return -ENOMEM; init_completion(&info->waitevent); + info->waiting_channel = channel; msg = (struct vmbus_channel_gpadl_teardown *)info->msg; @@ -493,6 +506,11 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) wait_for_completion(&info->waitevent); + if (channel->rescind) { + ret = -ENODEV; + goto post_msg_err; + } + post_msg_err: spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); list_del(&info->msglistentry); diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index b1e85d24bb4e..0af7e39006c8 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -147,6 +147,29 @@ static const struct { { HV_RDV_GUID }, }; +/* + * The rescinded channel may be blocked waiting for a response from the host; + * take care of that. + */ +static void vmbus_rescind_cleanup(struct vmbus_channel *channel) +{ + struct vmbus_channel_msginfo *msginfo; + unsigned long flags; + + + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + + list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, + msglistentry) { + + if (msginfo->waiting_channel == channel) { + complete(&msginfo->waitevent); + break; + } + } + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); +} + static bool is_unsupported_vmbus_devs(const uuid_le *guid) { int i; @@ -825,6 +848,8 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) channel->rescind = true; spin_unlock_irqrestore(&channel->lock, flags); + vmbus_rescind_cleanup(channel); + if (channel->device_obj) { if (channel->chn_rescind_callback) { channel->chn_rescind_callback(channel); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 42fe43fb0c80..7ea20bd7cdd1 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -627,6 +627,7 @@ struct vmbus_channel_msginfo { /* Synchronize the request/response if needed */ struct completion waitevent; + struct vmbus_channel *waiting_channel; union { struct vmbus_channel_version_supported version_supported; struct vmbus_channel_open_result open_result; -- cgit v1.2.3 From 5a66fecbf6aa528e375cbebccb1061cc58d80c84 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 22 Dec 2016 16:54:01 -0800 Subject: Drivers: hv: util: kvp: Fix a rescind processing issue KVP may use a char device to support the communication between the user level daemon and the driver. When the KVP channel is rescinded we need to make sure that the char device is fully cleaned up before we can process a new KVP offer from the host. Implement this logic. Signed-off-by: K. Y. Srinivasan Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 5e1fdc8d32ab..3abfc5983c97 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -88,6 +88,7 @@ static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); static const char kvp_devname[] = "vmbus/hv_kvp"; static u8 *recv_buffer; static struct hvutil_transport *hvt; +static struct completion release_event; /* * Register the kernel component with the user-level daemon. * As part of this registration, pass the LIC version number. @@ -716,6 +717,7 @@ static void kvp_on_reset(void) if (cancel_delayed_work_sync(&kvp_timeout_work)) kvp_respond_to_host(NULL, HV_E_FAIL); kvp_transaction.state = HVUTIL_DEVICE_INIT; + complete(&release_event); } int @@ -724,6 +726,7 @@ hv_kvp_init(struct hv_util_service *srv) recv_buffer = srv->recv_buffer; kvp_transaction.recv_channel = srv->channel; + init_completion(&release_event); /* * When this driver loads, the user level daemon that * processes the host requests may not yet be running. @@ -747,4 +750,5 @@ void hv_kvp_deinit(void) cancel_delayed_work_sync(&kvp_timeout_work); cancel_work_sync(&kvp_sendkey_work); hvutil_transport_destroy(hvt); + wait_for_completion(&release_event); } -- cgit v1.2.3 From 20951c7535b5e6af46bc37b7142105f716df739c Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 22 Dec 2016 16:54:02 -0800 Subject: Drivers: hv: util: Fcopy: Fix a rescind processing issue Fcopy may use a char device to support the communication between the user level daemon and the driver. When the Fcopy channel is rescinded we need to make sure that the char device is fully cleaned up before we can process a new Fcopy offer from the host. Implement this logic. Signed-off-by: K. Y. Srinivasan Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_fcopy.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c index 8b2ba98831ec..e47d8c9db03a 100644 --- a/drivers/hv/hv_fcopy.c +++ b/drivers/hv/hv_fcopy.c @@ -61,6 +61,7 @@ static DECLARE_WORK(fcopy_send_work, fcopy_send_data); static const char fcopy_devname[] = "vmbus/hv_fcopy"; static u8 *recv_buffer; static struct hvutil_transport *hvt; +static struct completion release_event; /* * This state maintains the version number registered by the daemon. */ @@ -317,6 +318,7 @@ static void fcopy_on_reset(void) if (cancel_delayed_work_sync(&fcopy_timeout_work)) fcopy_respond_to_host(HV_E_FAIL); + complete(&release_event); } int hv_fcopy_init(struct hv_util_service *srv) @@ -324,6 +326,7 @@ int hv_fcopy_init(struct hv_util_service *srv) recv_buffer = srv->recv_buffer; fcopy_transaction.recv_channel = srv->channel; + init_completion(&release_event); /* * When this driver loads, the user level daemon that * processes the host requests may not yet be running. @@ -345,4 +348,5 @@ void hv_fcopy_deinit(void) fcopy_transaction.state = HVUTIL_DEVICE_DYING; cancel_delayed_work_sync(&fcopy_timeout_work); hvutil_transport_destroy(hvt); + wait_for_completion(&release_event); } -- cgit v1.2.3 From d77044d142e960f7b5f814a91ecb8bcf86aa552c Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 22 Dec 2016 16:54:03 -0800 Subject: Drivers: hv: util: Backup: Fix a rescind processing issue VSS may use a char device to support the communication between the user level daemon and the driver. When the VSS channel is rescinded we need to make sure that the char device is fully cleaned up before we can process a new VSS offer from the host. Implement this logic. Signed-off-by: K. Y. Srinivasan Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_snapshot.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index eee238cc60bd..4e543dbb731a 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -69,6 +69,7 @@ static int dm_reg_value; static const char vss_devname[] = "vmbus/hv_vss"; static __u8 *recv_buffer; static struct hvutil_transport *hvt; +static struct completion release_event; static void vss_timeout_func(struct work_struct *dummy); static void vss_handle_request(struct work_struct *dummy); @@ -345,11 +346,13 @@ static void vss_on_reset(void) if (cancel_delayed_work_sync(&vss_timeout_work)) vss_respond_to_host(HV_E_FAIL); vss_transaction.state = HVUTIL_DEVICE_INIT; + complete(&release_event); } int hv_vss_init(struct hv_util_service *srv) { + init_completion(&release_event); if (vmbus_proto_version < VERSION_WIN8_1) { pr_warn("Integration service 'Backup (volume snapshot)'" " not supported on this host version.\n"); @@ -382,4 +385,5 @@ void hv_vss_deinit(void) cancel_delayed_work_sync(&vss_timeout_work); cancel_work_sync(&vss_handle_request_work); hvutil_transport_destroy(hvt); + wait_for_completion(&release_event); } -- cgit v1.2.3 From ddc5c9a37be4517270453ce909d3dfcc8de58230 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sat, 3 Dec 2016 17:29:28 +0800 Subject: misc: set error code when devm_kstrdup fails In function sram_reserve_regions(), the value of return variable ret should be negative on failures. However, the value of ret may be 0 even if the call to devm_kstrdup() returns a NULL pointer. This patch explicitly assigns "-ENOMEM" to ret on the path that devm_kstrdup() fails. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=188651 Signed-off-by: Pan Bian Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index b33ab8ce47ab..07ec2a8a9343 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -249,8 +249,10 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) block->label = devm_kstrdup(sram->dev, label, GFP_KERNEL); - if (!block->label) + if (!block->label) { + ret = -ENOMEM; goto err_chunks; + } dev_dbg(sram->dev, "found %sblock '%s' 0x%x-0x%x\n", block->export ? "exported " : "", block->label, -- cgit v1.2.3 From 816c9311f1144a03da1fdc4feb2f6b0d3299fca0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 9 Jan 2017 11:20:16 +0300 Subject: misc: mic: double free on ioctl error path This function only has one caller. Freeing "vdev" here leads to a use after free bug. There are several other error paths in this function but this is the only one which frees "vdev". It looks like the kfree() can be safely removed. Fixes: 61e9c905df78 ("misc: mic: Enable VOP host side functionality") Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_vringh.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c index 88e45234d527..fed992e2c258 100644 --- a/drivers/misc/mic/vop/vop_vringh.c +++ b/drivers/misc/mic/vop/vop_vringh.c @@ -292,7 +292,6 @@ static int vop_virtio_add_device(struct vop_vdev *vdev, if (ret) { dev_err(vop_dev(vdev), "%s %d err %d\n", __func__, __LINE__, ret); - kfree(vdev); return ret; } -- cgit v1.2.3 From f17c941cbcdccc4fef49a10622ce2ffd2321a143 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Thu, 15 Dec 2016 15:40:33 +0100 Subject: mmtimer: add member name to the miscdevice declaration Since the struct miscdevice have many members, it is dangerous to init it without members name relying only on member order. This patch add member name to the init declaration. Signed-off-by: Corentin Labbe Signed-off-by: Greg Kroah-Hartman --- drivers/char/mmtimer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index f786b18ac500..b708c85dc9c1 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -463,9 +463,9 @@ static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma) } static struct miscdevice mmtimer_miscdev = { - SGI_MMTIMER, - MMTIMER_NAME, - &mmtimer_fops + .minor = SGI_MMTIMER, + .name = MMTIMER_NAME, + .fops = &mmtimer_fops }; static struct timespec sgi_clock_offset; -- cgit v1.2.3 From 8681d5b04f812d9cc5cde72bbbb5081f8f1c9728 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Thu, 15 Dec 2016 15:53:23 +0100 Subject: ds1302: remove unneeded linux/miscdevice.h include drivers/char/ds1302.c does not use any miscdevice so the inclusion of linux/miscdevice.h is unnecessary. This patch remove this inclusion. Signed-off-by: Corentin Labbe Signed-off-by: Greg Kroah-Hartman --- drivers/char/ds1302.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/char/ds1302.c b/drivers/char/ds1302.c index 7d34b203718a..c614a56e68cc 100644 --- a/drivers/char/ds1302.c +++ b/drivers/char/ds1302.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 874bcd00f520cac297aefade201c4efc07fc8d17 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Thu, 15 Dec 2016 20:36:33 +0100 Subject: apm-emulation: move APM_MINOR_DEV to include/linux/miscdevice.h This patch move the define for APM_MINOR_DEV to include/linux/miscdevice.h It is better that all minor number definitions are in the same place. Signed-off-by: Corentin Labbe Signed-off-by: Greg Kroah-Hartman --- drivers/char/apm-emulation.c | 7 ------- include/linux/miscdevice.h | 1 + 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c index dd9dfa15e9d1..1dfb9f8de171 100644 --- a/drivers/char/apm-emulation.c +++ b/drivers/char/apm-emulation.c @@ -31,13 +31,6 @@ #include #include - -/* - * The apm_bios device is one of the misc char devices. - * This is its minor number. - */ -#define APM_MINOR_DEV 134 - /* * One option can be changed at boot time as follows: * apm=on/off enable/disable APM diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index ed30d5d713e3..0590263c462c 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -22,6 +22,7 @@ /*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */ #define WATCHDOG_MINOR 130 /* Watchdog timer */ #define TEMP_MINOR 131 /* Temperature Sensor */ +#define APM_MINOR_DEV 134 #define RTC_MINOR 135 #define EFI_RTC_MINOR 136 /* EFI Time services */ #define VHCI_MINOR 137 -- cgit v1.2.3 From f2cfa58b136e4b06a9b9db7af5ef62fbb5992f62 Mon Sep 17 00:00:00 2001 From: Max Bires Date: Tue, 3 Jan 2017 08:18:07 -0800 Subject: char: lack of bool string made CONFIG_DEVPORT always on Without a bool string present, using "# CONFIG_DEVPORT is not set" in defconfig files would not actually unset devport. This esnured that /dev/port was always on, but there are reasons a user may wish to disable it (smaller kernel, attack surface reduction) if it's not being used. Adding a message here in order to make this user visible. Signed-off-by: Max Bires Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/char/Kconfig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index fde005ef9d36..4ee2a10207d0 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -571,9 +571,12 @@ config TELCLOCK controlling the behavior of this hardware. config DEVPORT - bool + bool "/dev/port character device" depends on ISA || PCI default y + help + Say Y here if you want to support the /dev/port device. The /dev/port + device is similar to /dev/mem, but for I/O ports. source "drivers/s390/char/Kconfig" -- cgit v1.2.3 From e1873a479a4ac536661ad951a4b10fe1b26cec7b Mon Sep 17 00:00:00 2001 From: Cao jin Date: Mon, 19 Dec 2016 15:23:56 +0800 Subject: genwqe: drop .link_reset() In AER recovery, pci_error_handlers.link_reset() is never called, drop it now. Signed-off-by: Cao jin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_base.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 6c1f49a85023..4fd21e86ad56 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -1336,7 +1336,6 @@ static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs) static struct pci_error_handlers genwqe_err_handler = { .error_detected = genwqe_err_error_detected, .mmio_enabled = genwqe_err_result_none, - .link_reset = genwqe_err_result_none, .slot_reset = genwqe_err_slot_reset, .resume = genwqe_err_resume, }; -- cgit v1.2.3 From b0f2d7d546d37697d3f50753904f6f0c549b62bc Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Sat, 10 Dec 2016 23:10:41 +0000 Subject: VME: Remove node entry from vme_driver The vme_driver structure currently has a "node" entry. This entry is never used and it's intended purpose has been lost to the mists of time. Remove the entry from vme_driver to avoid confusion. Signed-off-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- include/linux/vme.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/vme.h b/include/linux/vme.h index 8c589176c2f8..ec5e8bf6118e 100644 --- a/include/linux/vme.h +++ b/include/linux/vme.h @@ -108,7 +108,6 @@ struct vme_dev { }; struct vme_driver { - struct list_head node; const char *name; int (*match)(struct vme_dev *); int (*probe)(struct vme_dev *); -- cgit v1.2.3 From 8e8e333179d6ea50fd0491b138e71dc9d98378ba Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Wed, 18 Jan 2017 13:08:19 +0100 Subject: checkkconfigsymbols.py: don't sort similar symbols Don't sort the list of string-similar Kconfig symbols alphabetically to preserve the correct order of string similarity. Signed-off-by: Valentin Rothberg Signed-off-by: Greg Kroah-Hartman --- scripts/checkkconfigsymbols.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/checkkconfigsymbols.py b/scripts/checkkconfigsymbols.py index 3820f00b066a..f1d6b14081ac 100755 --- a/scripts/checkkconfigsymbols.py +++ b/scripts/checkkconfigsymbols.py @@ -2,7 +2,7 @@ """Find Kconfig symbols that are referenced but not defined.""" -# (c) 2014-2016 Valentin Rothberg +# (c) 2014-2017 Valentin Rothberg # (c) 2014 Stefan Hengelein # # Licensed under the terms of the GNU GPL License version 2 @@ -269,7 +269,7 @@ def find_sims(symbol, ignore, defined=[]): """Return a list of max. ten Kconfig symbols that are string-similar to @symbol.""" if defined: - return sorted(difflib.get_close_matches(symbol, set(defined), 10)) + return difflib.get_close_matches(symbol, set(defined), 10) pool = Pool(cpu_count(), init_worker) kfiles = [] @@ -284,7 +284,7 @@ def find_sims(symbol, ignore, defined=[]): for res in pool.map(parse_kconfig_files, arglist): defined.extend(res[0]) - return sorted(difflib.get_close_matches(symbol, set(defined), 10)) + return difflib.get_close_matches(symbol, set(defined), 10) def get_files(): -- cgit v1.2.3 From cfad6425382ec81acd073fdf24361e1c30b4cb36 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 13 Jan 2017 15:16:52 +0300 Subject: eeprom: Add IDT 89HPESx EEPROM/CSR driver This driver provides an access to EEPROM of IDT PCIe-switches. IDT PCIe- switches expose a simple SMBus interface to perform IO-operations from/to EEPROM, which is located at private (so called Master) SMBus. The driver creates a simple binary sysfs-file to have an access to the EEPROM using the SMBus-slave interface in the i2c-device susfs-directory: /sys/bus/i2c/devices/-/eeprom In case if read-only flag is specified at dts-node of the device, User-space applications won't be able to write to the EEPROM sysfs-node. Additionally IDT 89HPESx SMBus interface has an ability to read/write values of device CSRs. This driver exposes debugfs-file to perform simple IO-operations using that ability for just basic debug purpose. Particularly the next file is created in the specific debugfs-directory: /sys/kernel/debug/idt_csr/ Format of the debugfs-file value is: $ cat /sys/kernel/debug/idt_csr/-/; : So reading the content of the file gives current CSR address and it value. If User-space application wishes to change current CSR address, it can just write a proper value to the sysfs-file: $ echo "" > /sys/kernel/debug/idt_csr/-/ If it wants to change the CSR value as well, the format of the write operation is: $ echo ":" > \ /sys/kernel/debug/idt_csr/-/; CSR address and value can be any of hexadecimal, decimal or octal format. Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/Kconfig | 10 + drivers/misc/eeprom/Makefile | 1 + drivers/misc/eeprom/idt_89hpesx.c | 1587 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1598 insertions(+) create mode 100644 drivers/misc/eeprom/idt_89hpesx.c diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index c4e41c26649e..de58762097c4 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -100,4 +100,14 @@ config EEPROM_DIGSY_MTC_CFG If unsure, say N. +config EEPROM_IDT_89HPESX + tristate "IDT 89HPESx PCIe-swtiches EEPROM / CSR support" + depends on I2C && SYSFS + help + Enable this driver to get read/write access to EEPROM / CSRs + over IDT PCIe-swtich i2c-slave interface. + + This driver can also be built as a module. If so, the module + will be called idt_89hpesx. + endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index fc1e81d29267..90a52624ddeb 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_EEPROM_MAX6875) += max6875.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o +obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c new file mode 100644 index 000000000000..25d47d09e1cb --- /dev/null +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -0,0 +1,1587 @@ +/* + * This file is provided under a GPLv2 license. When using or + * redistributing this file, you may do so under that license. + * + * GPL LICENSE SUMMARY + * + * Copyright (C) 2016 T-Platforms. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, it can be found . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * IDT PCIe-switch NTB Linux driver + * + * Contact Information: + * Serge Semin , + */ +/* + * NOTE of the IDT 89HPESx SMBus-slave interface driver + * This driver primarily is developed to have an access to EEPROM device of + * IDT PCIe-switches. IDT provides a simple SMBus interface to perform IO- + * operations from/to EEPROM, which is located at private (so called Master) + * SMBus of switches. Using that interface this the driver creates a simple + * binary sysfs-file in the device directory: + * /sys/bus/i2c/devices/-/eeprom + * In case if read-only flag is specified in the dts-node of device desription, + * User-space applications won't be able to write to the EEPROM sysfs-node. + * Additionally IDT 89HPESx SMBus interface has an ability to write/read + * data of device CSRs. This driver exposes debugf-file to perform simple IO + * operations using that ability for just basic debug purpose. Particularly + * next file is created in the specific debugfs-directory: + * /sys/kernel/debug/idt_csr/ + * Format of the debugfs-node is: + * $ cat /sys/kernel/debug/idt_csr/-/; + * : + * So reading the content of the file gives current CSR address and it value. + * If User-space application wishes to change current CSR address, + * it can just write a proper value to the sysfs-file: + * $ echo "" > /sys/kernel/debug/idt_csr/-/ + * If it wants to change the CSR value as well, the format of the write + * operation is: + * $ echo ":" > \ + * /sys/kernel/debug/idt_csr/-/; + * CSR address and value can be any of hexadecimal, decimal or octal format. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IDT_NAME "89hpesx" +#define IDT_89HPESX_DESC "IDT 89HPESx SMBus-slave interface driver" +#define IDT_89HPESX_VER "1.0" + +MODULE_DESCRIPTION(IDT_89HPESX_DESC); +MODULE_VERSION(IDT_89HPESX_VER); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("T-platforms"); + +/* + * csr_dbgdir - CSR read/write operations Debugfs directory + */ +static struct dentry *csr_dbgdir; + +/* + * struct idt_89hpesx_dev - IDT 89HPESx device data structure + * @eesize: Size of EEPROM in bytes (calculated from "idt,eecompatible") + * @eero: EEPROM Read-only flag + * @eeaddr: EEPROM custom address + * + * @inieecmd: Initial cmd value for EEPROM read/write operations + * @inicsrcmd: Initial cmd value for CSR read/write operations + * @iniccode: Initialial command code value for IO-operations + * + * @csr: CSR address to perform read operation + * + * @smb_write: SMBus write method + * @smb_read: SMBus read method + * @smb_mtx: SMBus mutex + * + * @client: i2c client used to perform IO operations + * + * @ee_file: EEPROM read/write sysfs-file + * @csr_file: CSR read/write debugfs-node + */ +struct idt_smb_seq; +struct idt_89hpesx_dev { + u32 eesize; + bool eero; + u8 eeaddr; + + u8 inieecmd; + u8 inicsrcmd; + u8 iniccode; + + u16 csr; + + int (*smb_write)(struct idt_89hpesx_dev *, const struct idt_smb_seq *); + int (*smb_read)(struct idt_89hpesx_dev *, struct idt_smb_seq *); + struct mutex smb_mtx; + + struct i2c_client *client; + + struct bin_attribute *ee_file; + struct dentry *csr_dir; + struct dentry *csr_file; +}; + +/* + * struct idt_smb_seq - sequence of data to be read/written from/to IDT 89HPESx + * @ccode: SMBus command code + * @bytecnt: Byte count of operation + * @data: Data to by written + */ +struct idt_smb_seq { + u8 ccode; + u8 bytecnt; + u8 *data; +}; + +/* + * struct idt_eeprom_seq - sequence of data to be read/written from/to EEPROM + * @cmd: Transaction CMD + * @eeaddr: EEPROM custom address + * @memaddr: Internal memory address of EEPROM + * @data: Data to be written at the memory address + */ +struct idt_eeprom_seq { + u8 cmd; + u8 eeaddr; + u16 memaddr; + u8 data; +} __packed; + +/* + * struct idt_csr_seq - sequence of data to be read/written from/to CSR + * @cmd: Transaction CMD + * @csraddr: Internal IDT device CSR address + * @data: Data to be read/written from/to the CSR address + */ +struct idt_csr_seq { + u8 cmd; + u16 csraddr; + u32 data; +} __packed; + +/* + * SMBus command code macros + * @CCODE_END: Indicates the end of transaction + * @CCODE_START: Indicates the start of transaction + * @CCODE_CSR: CSR read/write transaction + * @CCODE_EEPROM: EEPROM read/write transaction + * @CCODE_BYTE: Supplied data has BYTE length + * @CCODE_WORD: Supplied data has WORD length + * @CCODE_BLOCK: Supplied data has variable length passed in bytecnt + * byte right following CCODE byte + */ +#define CCODE_END ((u8)0x01) +#define CCODE_START ((u8)0x02) +#define CCODE_CSR ((u8)0x00) +#define CCODE_EEPROM ((u8)0x04) +#define CCODE_BYTE ((u8)0x00) +#define CCODE_WORD ((u8)0x20) +#define CCODE_BLOCK ((u8)0x40) +#define CCODE_PEC ((u8)0x80) + +/* + * EEPROM command macros + * @EEPROM_OP_WRITE: EEPROM write operation + * @EEPROM_OP_READ: EEPROM read operation + * @EEPROM_USA: Use specified address of EEPROM + * @EEPROM_NAERR: EEPROM device is not ready to respond + * @EEPROM_LAERR: EEPROM arbitration loss error + * @EEPROM_MSS: EEPROM misplace start & stop bits error + * @EEPROM_WR_CNT: Bytes count to perform write operation + * @EEPROM_WRRD_CNT: Bytes count to write before reading + * @EEPROM_RD_CNT: Bytes count to perform read operation + * @EEPROM_DEF_SIZE: Fall back size of EEPROM + * @EEPROM_DEF_ADDR: Defatul EEPROM address + * @EEPROM_TOUT: Timeout before retry read operation if eeprom is busy + */ +#define EEPROM_OP_WRITE ((u8)0x00) +#define EEPROM_OP_READ ((u8)0x01) +#define EEPROM_USA ((u8)0x02) +#define EEPROM_NAERR ((u8)0x08) +#define EEPROM_LAERR ((u8)0x10) +#define EEPROM_MSS ((u8)0x20) +#define EEPROM_WR_CNT ((u8)5) +#define EEPROM_WRRD_CNT ((u8)4) +#define EEPROM_RD_CNT ((u8)5) +#define EEPROM_DEF_SIZE ((u16)4096) +#define EEPROM_DEF_ADDR ((u8)0x50) +#define EEPROM_TOUT (100) + +/* + * CSR command macros + * @CSR_DWE: Enable all four bytes of the operation + * @CSR_OP_WRITE: CSR write operation + * @CSR_OP_READ: CSR read operation + * @CSR_RERR: Read operation error + * @CSR_WERR: Write operation error + * @CSR_WR_CNT: Bytes count to perform write operation + * @CSR_WRRD_CNT: Bytes count to write before reading + * @CSR_RD_CNT: Bytes count to perform read operation + * @CSR_MAX: Maximum CSR address + * @CSR_DEF: Default CSR address + * @CSR_REAL_ADDR: CSR real unshifted address + */ +#define CSR_DWE ((u8)0x0F) +#define CSR_OP_WRITE ((u8)0x00) +#define CSR_OP_READ ((u8)0x10) +#define CSR_RERR ((u8)0x40) +#define CSR_WERR ((u8)0x80) +#define CSR_WR_CNT ((u8)7) +#define CSR_WRRD_CNT ((u8)3) +#define CSR_RD_CNT ((u8)7) +#define CSR_MAX ((u32)0x3FFFF) +#define CSR_DEF ((u16)0x0000) +#define CSR_REAL_ADDR(val) ((unsigned int)val << 2) + +/* + * IDT 89HPESx basic register + * @IDT_VIDDID_CSR: PCIe VID and DID of IDT 89HPESx + * @IDT_VID_MASK: Mask of VID + */ +#define IDT_VIDDID_CSR ((u32)0x0000) +#define IDT_VID_MASK ((u32)0xFFFF) + +/* + * IDT 89HPESx can send NACK when new command is sent before previous one + * fininshed execution. In this case driver retries operation + * certain times. + * @RETRY_CNT: Number of retries before giving up and fail + * @idt_smb_safe: Generate a retry loop on corresponding SMBus method + */ +#define RETRY_CNT (128) +#define idt_smb_safe(ops, args...) ({ \ + int __retry = RETRY_CNT; \ + s32 __sts; \ + do { \ + __sts = i2c_smbus_ ## ops ## _data(args); \ + } while (__retry-- && __sts < 0); \ + __sts; \ +}) + +/*=========================================================================== + * i2c bus level IO-operations + *=========================================================================== + */ + +/* + * idt_smb_write_byte() - SMBus write method when I2C_SMBUS_BYTE_DATA operation + * is only available + * @pdev: Pointer to the driver data + * @seq: Sequence of data to be written + */ +static int idt_smb_write_byte(struct idt_89hpesx_dev *pdev, + const struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + int idx; + + /* Loop over the supplied data sending byte one-by-one */ + for (idx = 0; idx < seq->bytecnt; idx++) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BYTE; + if (idx == 0) + ccode |= CCODE_START; + if (idx == seq->bytecnt - 1) + ccode |= CCODE_END; + + /* Send data to the device */ + sts = idt_smb_safe(write_byte, pdev->client, ccode, + seq->data[idx]); + if (sts != 0) + return (int)sts; + } + + return 0; +} + +/* + * idt_smb_read_byte() - SMBus read method when I2C_SMBUS_BYTE_DATA operation + * is only available + * @pdev: Pointer to the driver data + * @seq: Buffer to read data to + */ +static int idt_smb_read_byte(struct idt_89hpesx_dev *pdev, + struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + int idx; + + /* Loop over the supplied buffer receiving byte one-by-one */ + for (idx = 0; idx < seq->bytecnt; idx++) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BYTE; + if (idx == 0) + ccode |= CCODE_START; + if (idx == seq->bytecnt - 1) + ccode |= CCODE_END; + + /* Read data from the device */ + sts = idt_smb_safe(read_byte, pdev->client, ccode); + if (sts < 0) + return (int)sts; + + seq->data[idx] = (u8)sts; + } + + return 0; +} + +/* + * idt_smb_write_word() - SMBus write method when I2C_SMBUS_BYTE_DATA and + * I2C_FUNC_SMBUS_WORD_DATA operations are available + * @pdev: Pointer to the driver data + * @seq: Sequence of data to be written + */ +static int idt_smb_write_word(struct idt_89hpesx_dev *pdev, + const struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + int idx, evencnt; + + /* Calculate the even count of data to send */ + evencnt = seq->bytecnt - (seq->bytecnt % 2); + + /* Loop over the supplied data sending two bytes at a time */ + for (idx = 0; idx < evencnt; idx += 2) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_WORD; + if (idx == 0) + ccode |= CCODE_START; + if (idx == evencnt - 2) + ccode |= CCODE_END; + + /* Send word data to the device */ + sts = idt_smb_safe(write_word, pdev->client, ccode, + *(u16 *)&seq->data[idx]); + if (sts != 0) + return (int)sts; + } + + /* If there is odd number of bytes then send just one last byte */ + if (seq->bytecnt != evencnt) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BYTE | CCODE_END; + if (idx == 0) + ccode |= CCODE_START; + + /* Send byte data to the device */ + sts = idt_smb_safe(write_byte, pdev->client, ccode, + seq->data[idx]); + if (sts != 0) + return (int)sts; + } + + return 0; +} + +/* + * idt_smb_read_word() - SMBus read method when I2C_SMBUS_BYTE_DATA and + * I2C_FUNC_SMBUS_WORD_DATA operations are available + * @pdev: Pointer to the driver data + * @seq: Buffer to read data to + */ +static int idt_smb_read_word(struct idt_89hpesx_dev *pdev, + struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + int idx, evencnt; + + /* Calculate the even count of data to send */ + evencnt = seq->bytecnt - (seq->bytecnt % 2); + + /* Loop over the supplied data reading two bytes at a time */ + for (idx = 0; idx < evencnt; idx += 2) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_WORD; + if (idx == 0) + ccode |= CCODE_START; + if (idx == evencnt - 2) + ccode |= CCODE_END; + + /* Read word data from the device */ + sts = idt_smb_safe(read_word, pdev->client, ccode); + if (sts < 0) + return (int)sts; + + *(u16 *)&seq->data[idx] = (u16)sts; + } + + /* If there is odd number of bytes then receive just one last byte */ + if (seq->bytecnt != evencnt) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BYTE | CCODE_END; + if (idx == 0) + ccode |= CCODE_START; + + /* Read last data byte from the device */ + sts = idt_smb_safe(read_byte, pdev->client, ccode); + if (sts < 0) + return (int)sts; + + seq->data[idx] = (u8)sts; + } + + return 0; +} + +/* + * idt_smb_write_block() - SMBus write method when I2C_SMBUS_BLOCK_DATA + * operation is available + * @pdev: Pointer to the driver data + * @seq: Sequence of data to be written + */ +static int idt_smb_write_block(struct idt_89hpesx_dev *pdev, + const struct idt_smb_seq *seq) +{ + u8 ccode; + + /* Return error if too much data passed to send */ + if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; + + /* Send block of data to the device */ + return idt_smb_safe(write_block, pdev->client, ccode, seq->bytecnt, + seq->data); +} + +/* + * idt_smb_read_block() - SMBus read method when I2C_SMBUS_BLOCK_DATA + * operation is available + * @pdev: Pointer to the driver data + * @seq: Buffer to read data to + */ +static int idt_smb_read_block(struct idt_89hpesx_dev *pdev, + struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + + /* Return error if too much data passed to send */ + if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; + + /* Read block of data from the device */ + sts = idt_smb_safe(read_block, pdev->client, ccode, seq->data); + if (sts != seq->bytecnt) + return (sts < 0 ? sts : -ENODATA); + + return 0; +} + +/* + * idt_smb_write_i2c_block() - SMBus write method when I2C_SMBUS_I2C_BLOCK_DATA + * operation is available + * @pdev: Pointer to the driver data + * @seq: Sequence of data to be written + * + * NOTE It's usual SMBus write block operation, except the actual data length is + * sent as first byte of data + */ +static int idt_smb_write_i2c_block(struct idt_89hpesx_dev *pdev, + const struct idt_smb_seq *seq) +{ + u8 ccode, buf[I2C_SMBUS_BLOCK_MAX + 1]; + + /* Return error if too much data passed to send */ + if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + + /* Collect the data to send. Length byte must be added prior the data */ + buf[0] = seq->bytecnt; + memcpy(&buf[1], seq->data, seq->bytecnt); + + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; + + /* Send length and block of data to the device */ + return idt_smb_safe(write_i2c_block, pdev->client, ccode, + seq->bytecnt + 1, buf); +} + +/* + * idt_smb_read_i2c_block() - SMBus read method when I2C_SMBUS_I2C_BLOCK_DATA + * operation is available + * @pdev: Pointer to the driver data + * @seq: Buffer to read data to + * + * NOTE It's usual SMBus read block operation, except the actual data length is + * retrieved as first byte of data + */ +static int idt_smb_read_i2c_block(struct idt_89hpesx_dev *pdev, + struct idt_smb_seq *seq) +{ + u8 ccode, buf[I2C_SMBUS_BLOCK_MAX + 1]; + s32 sts; + + /* Return error if too much data passed to send */ + if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; + + /* Read length and block of data from the device */ + sts = idt_smb_safe(read_i2c_block, pdev->client, ccode, + seq->bytecnt + 1, buf); + if (sts != seq->bytecnt + 1) + return (sts < 0 ? sts : -ENODATA); + if (buf[0] != seq->bytecnt) + return -ENODATA; + + /* Copy retrieved data to the output data buffer */ + memcpy(seq->data, &buf[1], seq->bytecnt); + + return 0; +} + +/*=========================================================================== + * EEPROM IO-operations + *=========================================================================== + */ + +/* + * idt_eeprom_read_byte() - read just one byte from EEPROM + * @pdev: Pointer to the driver data + * @memaddr: Start EEPROM memory address + * @data: Data to be written to EEPROM + */ +static int idt_eeprom_read_byte(struct idt_89hpesx_dev *pdev, u16 memaddr, + u8 *data) +{ + struct device *dev = &pdev->client->dev; + struct idt_eeprom_seq eeseq; + struct idt_smb_seq smbseq; + int ret, retry; + + /* Initialize SMBus sequence fields */ + smbseq.ccode = pdev->iniccode | CCODE_EEPROM; + smbseq.data = (u8 *)&eeseq; + + /* + * Sometimes EEPROM may respond with NACK if it's busy with previous + * operation, so we need to perform a few attempts of read cycle + */ + retry = RETRY_CNT; + do { + /* Send EEPROM memory address to read data from */ + smbseq.bytecnt = EEPROM_WRRD_CNT; + eeseq.cmd = pdev->inieecmd | EEPROM_OP_READ; + eeseq.eeaddr = pdev->eeaddr; + eeseq.memaddr = cpu_to_le16(memaddr); + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to init eeprom addr 0x%02hhx", + memaddr); + break; + } + + /* Perform read operation */ + smbseq.bytecnt = EEPROM_RD_CNT; + ret = pdev->smb_read(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to read eeprom data 0x%02hhx", + memaddr); + break; + } + + /* Restart read operation if the device is busy */ + if (retry && (eeseq.cmd & EEPROM_NAERR)) { + dev_dbg(dev, "EEPROM busy, retry reading after %d ms", + EEPROM_TOUT); + msleep(EEPROM_TOUT); + continue; + } + + /* Check whether IDT successfully read data from EEPROM */ + if (eeseq.cmd & (EEPROM_NAERR | EEPROM_LAERR | EEPROM_MSS)) { + dev_err(dev, + "Communication with eeprom failed, cmd 0x%hhx", + eeseq.cmd); + ret = -EREMOTEIO; + break; + } + + /* Save retrieved data and exit the loop */ + *data = eeseq.data; + break; + } while (retry--); + + /* Return the status of operation */ + return ret; +} + +/* + * idt_eeprom_write() - EEPROM write operation + * @pdev: Pointer to the driver data + * @memaddr: Start EEPROM memory address + * @len: Length of data to be written + * @data: Data to be written to EEPROM + */ +static int idt_eeprom_write(struct idt_89hpesx_dev *pdev, u16 memaddr, u16 len, + const u8 *data) +{ + struct device *dev = &pdev->client->dev; + struct idt_eeprom_seq eeseq; + struct idt_smb_seq smbseq; + int ret; + u16 idx; + + /* Initialize SMBus sequence fields */ + smbseq.ccode = pdev->iniccode | CCODE_EEPROM; + smbseq.data = (u8 *)&eeseq; + + /* Send data byte-by-byte, checking if it is successfully written */ + for (idx = 0; idx < len; idx++, memaddr++) { + /* Lock IDT SMBus device */ + mutex_lock(&pdev->smb_mtx); + + /* Perform write operation */ + smbseq.bytecnt = EEPROM_WR_CNT; + eeseq.cmd = pdev->inieecmd | EEPROM_OP_WRITE; + eeseq.eeaddr = pdev->eeaddr; + eeseq.memaddr = cpu_to_le16(memaddr); + eeseq.data = data[idx]; + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, + "Failed to write 0x%04hx:0x%02hhx to eeprom", + memaddr, data[idx]); + goto err_mutex_unlock; + } + + /* + * Check whether the data is successfully written by reading + * from the same EEPROM memory address. + */ + eeseq.data = ~data[idx]; + ret = idt_eeprom_read_byte(pdev, memaddr, &eeseq.data); + if (ret != 0) + goto err_mutex_unlock; + + /* Check whether the read byte is the same as written one */ + if (eeseq.data != data[idx]) { + dev_err(dev, "Values don't match 0x%02hhx != 0x%02hhx", + eeseq.data, data[idx]); + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* Unlock IDT SMBus device */ +err_mutex_unlock: + mutex_unlock(&pdev->smb_mtx); + if (ret != 0) + return ret; + } + + return 0; +} + +/* + * idt_eeprom_read() - EEPROM read operation + * @pdev: Pointer to the driver data + * @memaddr: Start EEPROM memory address + * @len: Length of data to read + * @buf: Buffer to read data to + */ +static int idt_eeprom_read(struct idt_89hpesx_dev *pdev, u16 memaddr, u16 len, + u8 *buf) +{ + int ret; + u16 idx; + + /* Read data byte-by-byte, retrying if it wasn't successful */ + for (idx = 0; idx < len; idx++, memaddr++) { + /* Lock IDT SMBus device */ + mutex_lock(&pdev->smb_mtx); + + /* Just read the byte to the buffer */ + ret = idt_eeprom_read_byte(pdev, memaddr, &buf[idx]); + + /* Unlock IDT SMBus device */ + mutex_unlock(&pdev->smb_mtx); + + /* Return error if read operation failed */ + if (ret != 0) + return ret; + } + + return 0; +} + +/*=========================================================================== + * CSR IO-operations + *=========================================================================== + */ + +/* + * idt_csr_write() - CSR write operation + * @pdev: Pointer to the driver data + * @csraddr: CSR address (with no two LS bits) + * @data: Data to be written to CSR + */ +static int idt_csr_write(struct idt_89hpesx_dev *pdev, u16 csraddr, + const u32 data) +{ + struct device *dev = &pdev->client->dev; + struct idt_csr_seq csrseq; + struct idt_smb_seq smbseq; + int ret; + + /* Initialize SMBus sequence fields */ + smbseq.ccode = pdev->iniccode | CCODE_CSR; + smbseq.data = (u8 *)&csrseq; + + /* Lock IDT SMBus device */ + mutex_lock(&pdev->smb_mtx); + + /* Perform write operation */ + smbseq.bytecnt = CSR_WR_CNT; + csrseq.cmd = pdev->inicsrcmd | CSR_OP_WRITE; + csrseq.csraddr = cpu_to_le16(csraddr); + csrseq.data = cpu_to_le32(data); + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to write 0x%04x: 0x%04x to csr", + CSR_REAL_ADDR(csraddr), data); + goto err_mutex_unlock; + } + + /* Send CSR address to read data from */ + smbseq.bytecnt = CSR_WRRD_CNT; + csrseq.cmd = pdev->inicsrcmd | CSR_OP_READ; + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to init csr address 0x%04x", + CSR_REAL_ADDR(csraddr)); + goto err_mutex_unlock; + } + + /* Perform read operation */ + smbseq.bytecnt = CSR_RD_CNT; + ret = pdev->smb_read(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to read csr 0x%04x", + CSR_REAL_ADDR(csraddr)); + goto err_mutex_unlock; + } + + /* Check whether IDT successfully retrieved CSR data */ + if (csrseq.cmd & (CSR_RERR | CSR_WERR)) { + dev_err(dev, "IDT failed to perform CSR r/w"); + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* Unlock IDT SMBus device */ +err_mutex_unlock: + mutex_unlock(&pdev->smb_mtx); + + return ret; +} + +/* + * idt_csr_read() - CSR read operation + * @pdev: Pointer to the driver data + * @csraddr: CSR address (with no two LS bits) + * @data: Data to be written to CSR + */ +static int idt_csr_read(struct idt_89hpesx_dev *pdev, u16 csraddr, u32 *data) +{ + struct device *dev = &pdev->client->dev; + struct idt_csr_seq csrseq; + struct idt_smb_seq smbseq; + int ret; + + /* Initialize SMBus sequence fields */ + smbseq.ccode = pdev->iniccode | CCODE_CSR; + smbseq.data = (u8 *)&csrseq; + + /* Lock IDT SMBus device */ + mutex_lock(&pdev->smb_mtx); + + /* Send CSR register address before reading it */ + smbseq.bytecnt = CSR_WRRD_CNT; + csrseq.cmd = pdev->inicsrcmd | CSR_OP_READ; + csrseq.csraddr = cpu_to_le16(csraddr); + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to init csr address 0x%04x", + CSR_REAL_ADDR(csraddr)); + goto err_mutex_unlock; + } + + /* Perform read operation */ + smbseq.bytecnt = CSR_RD_CNT; + ret = pdev->smb_read(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to read csr 0x%04hx", + CSR_REAL_ADDR(csraddr)); + goto err_mutex_unlock; + } + + /* Check whether IDT successfully retrieved CSR data */ + if (csrseq.cmd & (CSR_RERR | CSR_WERR)) { + dev_err(dev, "IDT failed to perform CSR r/w"); + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* Save data retrieved from IDT */ + *data = le32_to_cpu(csrseq.data); + + /* Unlock IDT SMBus device */ +err_mutex_unlock: + mutex_unlock(&pdev->smb_mtx); + + return ret; +} + +/*=========================================================================== + * Sysfs/debugfs-nodes IO-operations + *=========================================================================== + */ + +/* + * eeprom_write() - EEPROM sysfs-node write callback + * @filep: Pointer to the file system node + * @kobj: Pointer to the kernel object related to the sysfs-node + * @attr: Attributes of the file + * @buf: Buffer to write data to + * @off: Offset at which data should be written to + * @count: Number of bytes to write + */ +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct idt_89hpesx_dev *pdev; + int ret; + + /* Retrieve driver data */ + pdev = dev_get_drvdata(kobj_to_dev(kobj)); + + /* Perform EEPROM write operation */ + ret = idt_eeprom_write(pdev, (u16)off, (u16)count, (u8 *)buf); + return (ret != 0 ? ret : count); +} + +/* + * eeprom_read() - EEPROM sysfs-node read callback + * @filep: Pointer to the file system node + * @kobj: Pointer to the kernel object related to the sysfs-node + * @attr: Attributes of the file + * @buf: Buffer to write data to + * @off: Offset at which data should be written to + * @count: Number of bytes to write + */ +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct idt_89hpesx_dev *pdev; + int ret; + + /* Retrieve driver data */ + pdev = dev_get_drvdata(kobj_to_dev(kobj)); + + /* Perform EEPROM read operation */ + ret = idt_eeprom_read(pdev, (u16)off, (u16)count, (u8 *)buf); + return (ret != 0 ? ret : count); +} + +/* + * idt_dbgfs_csr_write() - CSR debugfs-node write callback + * @filep: Pointer to the file system file descriptor + * @buf: Buffer to read data from + * @count: Size of the buffer + * @offp: Offset within the file + * + * It accepts either "0x:0x" for saving register address + * and writing value to specified DWORD register or "0x" for + * just saving register address in order to perform next read operation. + * + * WARNING No spaces are allowed. Incoming string must be strictly formated as: + * ":". Register address must be aligned within 4 bytes + * (one DWORD). + */ +static ssize_t idt_dbgfs_csr_write(struct file *filep, const char __user *ubuf, + size_t count, loff_t *offp) +{ + struct idt_89hpesx_dev *pdev = filep->private_data; + char *colon_ch, *csraddr_str, *csrval_str; + int ret, csraddr_len, csrval_len; + u32 csraddr, csrval; + char *buf; + + /* Copy data from User-space */ + buf = kmalloc(count + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = simple_write_to_buffer(buf, count, offp, ubuf, count); + if (ret < 0) + goto free_buf; + buf[count] = 0; + + /* Find position of colon in the buffer */ + colon_ch = strnchr(buf, count, ':'); + + /* + * If there is colon passed then new CSR value should be parsed as + * well, so allocate buffer for CSR address substring. + * If no colon is found, then string must have just one number with + * no new CSR value + */ + if (colon_ch != NULL) { + csraddr_len = colon_ch - buf; + csraddr_str = + kmalloc(sizeof(char)*(csraddr_len + 1), GFP_KERNEL); + if (csraddr_str == NULL) + return -ENOMEM; + /* Copy the register address to the substring buffer */ + strncpy(csraddr_str, buf, csraddr_len); + csraddr_str[csraddr_len] = '\0'; + /* Register value must follow the colon */ + csrval_str = colon_ch + 1; + csrval_len = strnlen(csrval_str, count - csraddr_len); + } else /* if (str_colon == NULL) */ { + csraddr_str = (char *)buf; /* Just to shut warning up */ + csraddr_len = strnlen(csraddr_str, count); + csrval_str = NULL; + csrval_len = 0; + } + + /* Convert CSR address to u32 value */ + ret = kstrtou32(csraddr_str, 0, &csraddr); + if (ret != 0) + goto free_csraddr_str; + + /* Check whether passed register address is valid */ + if (csraddr > CSR_MAX || !IS_ALIGNED(csraddr, SZ_4)) { + ret = -EINVAL; + goto free_csraddr_str; + } + + /* Shift register address to the right so to have u16 address */ + pdev->csr = (csraddr >> 2); + + /* Parse new CSR value and send it to IDT, if colon has been found */ + if (colon_ch != NULL) { + ret = kstrtou32(csrval_str, 0, &csrval); + if (ret != 0) + goto free_csraddr_str; + + ret = idt_csr_write(pdev, pdev->csr, csrval); + if (ret != 0) + goto free_csraddr_str; + } + + /* Free memory only if colon has been found */ +free_csraddr_str: + if (colon_ch != NULL) + kfree(csraddr_str); + + /* Free buffer allocated for data retrieved from User-space */ +free_buf: + kfree(buf); + + return (ret != 0 ? ret : count); +} + +/* + * idt_dbgfs_csr_read() - CSR debugfs-node read callback + * @filep: Pointer to the file system file descriptor + * @buf: Buffer to write data to + * @count: Size of the buffer + * @offp: Offset within the file + * + * It just prints the pair "0x:0x" to passed buffer. + */ +#define CSRBUF_SIZE ((size_t)32) +static ssize_t idt_dbgfs_csr_read(struct file *filep, char __user *ubuf, + size_t count, loff_t *offp) +{ + struct idt_89hpesx_dev *pdev = filep->private_data; + u32 csraddr, csrval; + char buf[CSRBUF_SIZE]; + int ret, size; + + /* Perform CSR read operation */ + ret = idt_csr_read(pdev, pdev->csr, &csrval); + if (ret != 0) + return ret; + + /* Shift register address to the left so to have real address */ + csraddr = ((u32)pdev->csr << 2); + + /* Print the "0x:0x" to buffer */ + size = snprintf(buf, CSRBUF_SIZE, "0x%05x:0x%08x\n", + (unsigned int)csraddr, (unsigned int)csrval); + + /* Copy data to User-space */ + return simple_read_from_buffer(ubuf, count, offp, buf, size); +} + +/* + * eeprom_attribute - EEPROM sysfs-node attributes + * + * NOTE Size will be changed in compliance with OF node. EEPROM attribute will + * be read-only as well if the corresponding flag is specified in OF node. + */ +static BIN_ATTR_RW(eeprom, EEPROM_DEF_SIZE); + +/* + * csr_dbgfs_ops - CSR debugfs-node read/write operations + */ +static const struct file_operations csr_dbgfs_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = idt_dbgfs_csr_write, + .read = idt_dbgfs_csr_read +}; + +/*=========================================================================== + * Driver init/deinit methods + *=========================================================================== + */ + +/* + * idt_set_defval() - disable EEPROM access by default + * @pdev: Pointer to the driver data + */ +static void idt_set_defval(struct idt_89hpesx_dev *pdev) +{ + /* If OF info is missing then use next values */ + pdev->eesize = 0; + pdev->eero = true; + pdev->inieecmd = 0; + pdev->eeaddr = 0; +} + +#ifdef CONFIG_OF +static const struct i2c_device_id ee_ids[]; +/* + * idt_ee_match_id() - check whether the node belongs to compatible EEPROMs + */ +static const struct i2c_device_id *idt_ee_match_id(struct device_node *node) +{ + const struct i2c_device_id *id = ee_ids; + char devname[I2C_NAME_SIZE]; + + /* Retrieve the device name without manufacturer name */ + if (of_modalias_node(node, devname, sizeof(devname))) + return NULL; + + /* Search through the device name */ + while (id->name[0]) { + if (strcmp(devname, id->name) == 0) + return id; + id++; + } + return NULL; +} + +/* + * idt_get_ofdata() - get IDT i2c-device parameters from device tree + * @pdev: Pointer to the driver data + */ +static void idt_get_ofdata(struct idt_89hpesx_dev *pdev) +{ + const struct device_node *node = pdev->client->dev.of_node; + struct device *dev = &pdev->client->dev; + + /* Read dts node parameters */ + if (node) { + const struct i2c_device_id *ee_id = NULL; + struct device_node *child; + const __be32 *addr_be; + int len; + + /* Walk through all child nodes looking for compatible one */ + for_each_available_child_of_node(node, child) { + ee_id = idt_ee_match_id(child); + if (IS_ERR_OR_NULL(ee_id)) { + dev_warn(dev, "Skip unsupported child node %s", + child->full_name); + continue; + } else + break; + } + + /* If there is no child EEPROM device, then set zero size */ + if (!ee_id) { + idt_set_defval(pdev); + return; + } + + /* Retrieve EEPROM size */ + pdev->eesize = (u32)ee_id->driver_data; + + /* Get custom EEPROM address from 'reg' attribute */ + addr_be = of_get_property(child, "reg", &len); + if (!addr_be || (len < sizeof(*addr_be))) { + dev_warn(dev, "No reg on %s, use default address %d", + child->full_name, EEPROM_DEF_ADDR); + pdev->inieecmd = 0; + pdev->eeaddr = EEPROM_DEF_ADDR << 1; + } else { + pdev->inieecmd = EEPROM_USA; + pdev->eeaddr = be32_to_cpup(addr_be) << 1; + } + + /* Check EEPROM 'read-only' flag */ + if (of_get_property(child, "read-only", NULL)) + pdev->eero = true; + else /* if (!of_get_property(node, "read-only", NULL)) */ + pdev->eero = false; + + dev_dbg(dev, "EEPROM of %u bytes found by %hhu", + pdev->eesize, pdev->eeaddr); + } else { + dev_warn(dev, "No dts node, EEPROM access disabled"); + idt_set_defval(pdev); + } +} +#else +static void idt_get_ofdata(struct idt_89hpesx_dev *pdev) +{ + struct device *dev = &pdev->client->dev; + + dev_warn(dev, "OF table is unsupported, EEPROM access disabled"); + + /* Nothing we can do, just set the default values */ + idt_set_defval(pdev); +} +#endif /* CONFIG_OF */ + +/* + * idt_create_pdev() - create and init data structure of the driver + * @client: i2c client of IDT PCIe-switch device + */ +static struct idt_89hpesx_dev *idt_create_pdev(struct i2c_client *client) +{ + struct idt_89hpesx_dev *pdev; + + /* Allocate memory for driver data */ + pdev = devm_kmalloc(&client->dev, sizeof(struct idt_89hpesx_dev), + GFP_KERNEL); + if (pdev == NULL) + return ERR_PTR(-ENOMEM); + + /* Initialize basic fields of the data */ + pdev->client = client; + i2c_set_clientdata(client, pdev); + + /* Read OF nodes information */ + idt_get_ofdata(pdev); + + /* Initialize basic CSR CMD field - use full DWORD-sized r/w ops */ + pdev->inicsrcmd = CSR_DWE; + pdev->csr = CSR_DEF; + + /* Enable Packet Error Checking if it's supported by adapter */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) { + pdev->iniccode = CCODE_PEC; + client->flags |= I2C_CLIENT_PEC; + } else /* PEC is unsupported */ { + pdev->iniccode = 0; + } + + return pdev; +} + +/* + * idt_free_pdev() - free data structure of the driver + * @pdev: Pointer to the driver data + */ +static void idt_free_pdev(struct idt_89hpesx_dev *pdev) +{ + /* Clear driver data from device private field */ + i2c_set_clientdata(pdev->client, NULL); + + /* Just free memory allocated for data */ + devm_kfree(&pdev->client->dev, pdev); +} + +/* + * idt_set_smbus_ops() - set supported SMBus operations + * @pdev: Pointer to the driver data + * Return status of smbus check operations + */ +static int idt_set_smbus_ops(struct idt_89hpesx_dev *pdev) +{ + struct i2c_adapter *adapter = pdev->client->adapter; + struct device *dev = &pdev->client->dev; + + /* Check i2c adapter read functionality */ + if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { + pdev->smb_read = idt_smb_read_block; + dev_dbg(dev, "SMBus block-read op chosen"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + pdev->smb_read = idt_smb_read_i2c_block; + dev_dbg(dev, "SMBus i2c-block-read op chosen"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA) && + i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + pdev->smb_read = idt_smb_read_word; + dev_warn(dev, "Use slow word/byte SMBus read ops"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + pdev->smb_read = idt_smb_read_byte; + dev_warn(dev, "Use slow byte SMBus read op"); + } else /* no supported smbus read operations */ { + dev_err(dev, "No supported SMBus read op"); + return -EPFNOSUPPORT; + } + + /* Check i2c adapter write functionality */ + if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)) { + pdev->smb_write = idt_smb_write_block; + dev_dbg(dev, "SMBus block-write op chosen"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { + pdev->smb_write = idt_smb_write_i2c_block; + dev_dbg(dev, "SMBus i2c-block-write op chosen"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_WORD_DATA) && + i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + pdev->smb_write = idt_smb_write_word; + dev_warn(dev, "Use slow word/byte SMBus write op"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + pdev->smb_write = idt_smb_write_byte; + dev_warn(dev, "Use slow byte SMBus write op"); + } else /* no supported smbus write operations */ { + dev_err(dev, "No supported SMBus write op"); + return -EPFNOSUPPORT; + } + + /* Initialize IDT SMBus slave interface mutex */ + mutex_init(&pdev->smb_mtx); + + return 0; +} + +/* + * idt_check_dev() - check whether it's really IDT 89HPESx device + * @pdev: Pointer to the driver data + * Return status of i2c adapter check operation + */ +static int idt_check_dev(struct idt_89hpesx_dev *pdev) +{ + struct device *dev = &pdev->client->dev; + u32 viddid; + int ret; + + /* Read VID and DID directly from IDT memory space */ + ret = idt_csr_read(pdev, IDT_VIDDID_CSR, &viddid); + if (ret != 0) { + dev_err(dev, "Failed to read VID/DID"); + return ret; + } + + /* Check whether it's IDT device */ + if ((viddid & IDT_VID_MASK) != PCI_VENDOR_ID_IDT) { + dev_err(dev, "Got unsupported VID/DID: 0x%08x", viddid); + return -ENODEV; + } + + dev_info(dev, "Found IDT 89HPES device VID:0x%04x, DID:0x%04x", + (viddid & IDT_VID_MASK), (viddid >> 16)); + + return 0; +} + +/* + * idt_create_sysfs_files() - create sysfs attribute files + * @pdev: Pointer to the driver data + * Return status of operation + */ +static int idt_create_sysfs_files(struct idt_89hpesx_dev *pdev) +{ + struct device *dev = &pdev->client->dev; + int ret; + + /* Don't do anything if EEPROM isn't accessible */ + if (pdev->eesize == 0) { + dev_dbg(dev, "Skip creating sysfs-files"); + return 0; + } + + /* Allocate memory for attribute file */ + pdev->ee_file = devm_kmalloc(dev, sizeof(*pdev->ee_file), GFP_KERNEL); + if (!pdev->ee_file) + return -ENOMEM; + + /* Copy the declared EEPROM attr structure to change some of fields */ + memcpy(pdev->ee_file, &bin_attr_eeprom, sizeof(*pdev->ee_file)); + + /* In case of read-only EEPROM get rid of write ability */ + if (pdev->eero) { + pdev->ee_file->attr.mode &= ~0200; + pdev->ee_file->write = NULL; + } + /* Create EEPROM sysfs file */ + pdev->ee_file->size = pdev->eesize; + ret = sysfs_create_bin_file(&dev->kobj, pdev->ee_file); + if (ret != 0) { + kfree(pdev->ee_file); + dev_err(dev, "Failed to create EEPROM sysfs-node"); + return ret; + } + + return 0; +} + +/* + * idt_remove_sysfs_files() - remove sysfs attribute files + * @pdev: Pointer to the driver data + */ +static void idt_remove_sysfs_files(struct idt_89hpesx_dev *pdev) +{ + struct device *dev = &pdev->client->dev; + + /* Don't do anything if EEPROM wasn't accessible */ + if (pdev->eesize == 0) + return; + + /* Remove EEPROM sysfs file */ + sysfs_remove_bin_file(&dev->kobj, pdev->ee_file); + + /* Free memory allocated for bin_attribute structure */ + kfree(pdev->ee_file); +} + +/* + * idt_create_dbgfs_files() - create debugfs files + * @pdev: Pointer to the driver data + */ +#define CSRNAME_LEN ((size_t)32) +static void idt_create_dbgfs_files(struct idt_89hpesx_dev *pdev) +{ + struct i2c_client *cli = pdev->client; + char fname[CSRNAME_LEN]; + + /* Create Debugfs directory for CSR file */ + snprintf(fname, CSRNAME_LEN, "%d-%04hx", cli->adapter->nr, cli->addr); + pdev->csr_dir = debugfs_create_dir(fname, csr_dbgdir); + + /* Create Debugfs file for CSR read/write operations */ + pdev->csr_file = debugfs_create_file(cli->name, 0600, + pdev->csr_dir, pdev, &csr_dbgfs_ops); +} + +/* + * idt_remove_dbgfs_files() - remove debugfs files + * @pdev: Pointer to the driver data + */ +static void idt_remove_dbgfs_files(struct idt_89hpesx_dev *pdev) +{ + /* Remove CSR directory and it sysfs-node */ + debugfs_remove_recursive(pdev->csr_dir); +} + +/* + * idt_probe() - IDT 89HPESx driver probe() callback method + */ +static int idt_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct idt_89hpesx_dev *pdev; + int ret; + + /* Create driver data */ + pdev = idt_create_pdev(client); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + /* Set SMBus operations */ + ret = idt_set_smbus_ops(pdev); + if (ret != 0) + goto err_free_pdev; + + /* Check whether it is truly IDT 89HPESx device */ + ret = idt_check_dev(pdev); + if (ret != 0) + goto err_free_pdev; + + /* Create sysfs files */ + ret = idt_create_sysfs_files(pdev); + if (ret != 0) + goto err_free_pdev; + + /* Create debugfs files */ + idt_create_dbgfs_files(pdev); + + return 0; + +err_free_pdev: + idt_free_pdev(pdev); + + return ret; +} + +/* + * idt_remove() - IDT 89HPESx driver remove() callback method + */ +static int idt_remove(struct i2c_client *client) +{ + struct idt_89hpesx_dev *pdev = i2c_get_clientdata(client); + + /* Remove debugfs files first */ + idt_remove_dbgfs_files(pdev); + + /* Remove sysfs files */ + idt_remove_sysfs_files(pdev); + + /* Discard driver data structure */ + idt_free_pdev(pdev); + + return 0; +} + +/* + * ee_ids - array of supported EEPROMs + */ +static const struct i2c_device_id ee_ids[] = { + { "24c32", 4096}, + { "24c64", 8192}, + { "24c128", 16384}, + { "24c256", 32768}, + { "24c512", 65536}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ee_ids); + +/* + * idt_ids - supported IDT 89HPESx devices + */ +static const struct i2c_device_id idt_ids[] = { + { "89hpes8nt2", 0 }, + { "89hpes12nt3", 0 }, + + { "89hpes24nt6ag2", 0 }, + { "89hpes32nt8ag2", 0 }, + { "89hpes32nt8bg2", 0 }, + { "89hpes12nt12g2", 0 }, + { "89hpes16nt16g2", 0 }, + { "89hpes24nt24g2", 0 }, + { "89hpes32nt24ag2", 0 }, + { "89hpes32nt24bg2", 0 }, + + { "89hpes12n3", 0 }, + { "89hpes12n3a", 0 }, + { "89hpes24n3", 0 }, + { "89hpes24n3a", 0 }, + + { "89hpes32h8", 0 }, + { "89hpes32h8g2", 0 }, + { "89hpes48h12", 0 }, + { "89hpes48h12g2", 0 }, + { "89hpes48h12ag2", 0 }, + { "89hpes16h16", 0 }, + { "89hpes22h16", 0 }, + { "89hpes22h16g2", 0 }, + { "89hpes34h16", 0 }, + { "89hpes34h16g2", 0 }, + { "89hpes64h16", 0 }, + { "89hpes64h16g2", 0 }, + { "89hpes64h16ag2", 0 }, + + /* { "89hpes3t3", 0 }, // No SMBus-slave iface */ + { "89hpes12t3g2", 0 }, + { "89hpes24t3g2", 0 }, + /* { "89hpes4t4", 0 }, // No SMBus-slave iface */ + { "89hpes16t4", 0 }, + { "89hpes4t4g2", 0 }, + { "89hpes10t4g2", 0 }, + { "89hpes16t4g2", 0 }, + { "89hpes16t4ag2", 0 }, + { "89hpes5t5", 0 }, + { "89hpes6t5", 0 }, + { "89hpes8t5", 0 }, + { "89hpes8t5a", 0 }, + { "89hpes24t6", 0 }, + { "89hpes6t6g2", 0 }, + { "89hpes24t6g2", 0 }, + { "89hpes16t7", 0 }, + { "89hpes32t8", 0 }, + { "89hpes32t8g2", 0 }, + { "89hpes48t12", 0 }, + { "89hpes48t12g2", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, idt_ids); + +/* + * idt_driver - IDT 89HPESx driver structure + */ +static struct i2c_driver idt_driver = { + .driver = { + .name = IDT_NAME, + .owner = THIS_MODULE, + }, + .probe = idt_probe, + .remove = idt_remove, + .id_table = idt_ids, +}; + +/* + * idt_init() - IDT 89HPESx driver init() callback method + */ +static int __init idt_init(void) +{ + /* Create Debugfs directory first */ + if (debugfs_initialized()) + csr_dbgdir = debugfs_create_dir("idt_csr", NULL); + + /* Add new i2c-device driver */ + return i2c_add_driver(&idt_driver); +} +module_init(idt_init); + +/* + * idt_exit() - IDT 89HPESx driver exit() callback method + */ +static void __exit idt_exit(void) +{ + /* Discard debugfs directory and all files if any */ + debugfs_remove_recursive(csr_dbgdir); + + /* Unregister i2c-device driver */ + i2c_del_driver(&idt_driver); +} +module_exit(idt_exit); -- cgit v1.2.3 From a0227cf344e2a33bd4454cf09e56a2a9a8fbfc1d Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 13 Jan 2017 15:16:53 +0300 Subject: eeprom: Add IDT 89HPESx driver bindings file IDT 89HPESx PCIe-switches exposes SMBus interface to have an access to the device CSRs and EEPROM. So to properly utilize the interface functionality, developer should declare a valid dts-file node, which would refer to the corresponding 89HPESx device. Signed-off-by: Serge Semin Acked-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/misc/idt_89hpesx.txt | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/idt_89hpesx.txt diff --git a/Documentation/devicetree/bindings/misc/idt_89hpesx.txt b/Documentation/devicetree/bindings/misc/idt_89hpesx.txt new file mode 100644 index 000000000000..b9093b79ab7d --- /dev/null +++ b/Documentation/devicetree/bindings/misc/idt_89hpesx.txt @@ -0,0 +1,44 @@ +EEPROM / CSR SMBus-slave interface of IDT 89HPESx devices + +Required properties: + - compatible : should be "," + Basically there is only one manufacturer: idt, but some + compatible devices may be produced in future. Following devices + are supported: 89hpes8nt2, 89hpes12nt3, 89hpes24nt6ag2, + 89hpes32nt8ag2, 89hpes32nt8bg2, 89hpes12nt12g2, 89hpes16nt16g2, + 89hpes24nt24g2, 89hpes32nt24ag2, 89hpes32nt24bg2; + 89hpes12n3, 89hpes12n3a, 89hpes24n3, 89hpes24n3a; + 89hpes32h8, 89hpes32h8g2, 89hpes48h12, 89hpes48h12g2, + 89hpes48h12ag2, 89hpes16h16, 89hpes22h16, 89hpes22h16g2, + 89hpes34h16, 89hpes34h16g2, 89hpes64h16, 89hpes64h16g2, + 89hpes64h16ag2; + 89hpes12t3g2, 89hpes24t3g2, 89hpes16t4, 89hpes4t4g2, + 89hpes10t4g2, 89hpes16t4g2, 89hpes16t4ag2, 89hpes5t5, + 89hpes6t5, 89hpes8t5, 89hpes8t5a, 89hpes24t6, 89hpes6t6g2, + 89hpes24t6g2, 89hpes16t7, 89hpes32t8, 89hpes32t8g2, + 89hpes48t12, 89hpes48t12g2. + - reg : I2C address of the IDT 89HPESx device. + +Optionally there can be EEPROM-compatible subnode: + - compatible: There are five EEPROM devices supported: 24c32, 24c64, 24c128, + 24c256 and 24c512 differed by size. + - reg: Custom address of EEPROM device (If not specified IDT 89HPESx + (optional) device will try to communicate with EEPROM sited by default + address - 0x50) + - read-only : Parameterless property disables writes to the EEPROM + (optional) + +Example: + idt@60 { + compatible = "idt,89hpes32nt8ag2"; + reg = <0x74>; + #address-cells = <1>; + #size-cells = <0>; + + eeprom@50 { + compatible = "onsemi,24c64"; + reg = <0x50>; + read-only; + }; + }; + -- cgit v1.2.3 From 3f646ed70ccd1c4e5c1263d2922247d28c8e08f0 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 18 Jan 2017 16:45:00 -0700 Subject: Drivers: hv: vmbus: Move the definition of hv_x64_msr_hypercall_contents As part of the effort to separate out architecture specific code, move the definition of hv_x64_msr_hypercall_contents to x86 specific header file. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/mshyperv.h | 12 ++++++++++++ drivers/hv/hyperv_vmbus.h | 15 --------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index aaf59b7da98a..188ddfdde2b9 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -13,6 +13,18 @@ struct ms_hyperv_info { extern struct ms_hyperv_info ms_hyperv; +/* + * Declare the MSR used to setup pages used to communicate with the hypervisor. + */ +union hv_x64_msr_hypercall_contents { + u64 as_uint64; + struct { + u64 enable:1; + u64 reserved:11; + u64 guest_physical_address:52; + }; +}; + void hyperv_callback_vector(void); #ifdef CONFIG_TRACING #define trace_hyperv_callback_vector hyperv_callback_vector diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 83beea748c6f..a1ff03677e23 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -306,21 +306,6 @@ union hv_x64_msr_guest_os_id_contents { }; }; -/* - * Declare the MSR used to setup pages used to communicate with the hypervisor. - */ -#define HV_X64_MSR_HYPERCALL 0x40000001 - -union hv_x64_msr_hypercall_contents { - u64 as_uint64; - struct { - u64 enable:1; - u64 reserved:11; - u64 guest_physical_address:52; - }; -}; - - enum { VMBUS_MESSAGE_CONNECTION_ID = 1, VMBUS_MESSAGE_PORT_ID = 1, -- cgit v1.2.3 From 352c9624242d5836ad8a960826183011367871a4 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 18 Jan 2017 16:45:01 -0700 Subject: Drivers: hv: vmbus: Move the definition of generate_guest_id() As part of the effort to separate out architecture specific code, move the definition of generate_guest_id() to x86 specific header file. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/mshyperv.h | 43 +++++++++++++++++++++++++++++++++++++++++ drivers/hv/hyperv_vmbus.h | 43 ----------------------------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 188ddfdde2b9..15a0c275c82e 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -25,6 +25,49 @@ union hv_x64_msr_hypercall_contents { }; }; +/* + * The guest OS needs to register the guest ID with the hypervisor. + * The guest ID is a 64 bit entity and the structure of this ID is + * specified in the Hyper-V specification: + * + * msdn.microsoft.com/en-us/library/windows/hardware/ff542653%28v=vs.85%29.aspx + * + * While the current guideline does not specify how Linux guest ID(s) + * need to be generated, our plan is to publish the guidelines for + * Linux and other guest operating systems that currently are hosted + * on Hyper-V. The implementation here conforms to this yet + * unpublished guidelines. + * + * + * Bit(s) + * 63 - Indicates if the OS is Open Source or not; 1 is Open Source + * 62:56 - Os Type; Linux is 0x100 + * 55:48 - Distro specific identification + * 47:16 - Linux kernel version number + * 15:0 - Distro specific identification + * + * + */ + +#define HV_LINUX_VENDOR_ID 0x8800 + +/* + * Generate the guest ID based on the guideline described above. + */ + +static inline __u64 generate_guest_id(__u64 d_info1, __u64 kernel_version, + __u64 d_info2) +{ + __u64 guest_id = 0; + + guest_id = (((__u64)HV_LINUX_VENDOR_ID) << 56); + guest_id |= (d_info1 << 48); + guest_id |= (kernel_version << 16); + guest_id |= d_info2; + + return guest_id; +} + void hyperv_callback_vector(void); #ifdef CONFIG_TRACING #define trace_hyperv_callback_vector hyperv_callback_vector diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index a1ff03677e23..da57626786b7 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -320,49 +320,6 @@ enum { #define HV_PRESENT_BIT 0x80000000 -/* - * The guest OS needs to register the guest ID with the hypervisor. - * The guest ID is a 64 bit entity and the structure of this ID is - * specified in the Hyper-V specification: - * - * http://msdn.microsoft.com/en-us/library/windows/hardware/ff542653%28v=vs.85%29.aspx - * - * While the current guideline does not specify how Linux guest ID(s) - * need to be generated, our plan is to publish the guidelines for - * Linux and other guest operating systems that currently are hosted - * on Hyper-V. The implementation here conforms to this yet - * unpublished guidelines. - * - * - * Bit(s) - * 63 - Indicates if the OS is Open Source or not; 1 is Open Source - * 62:56 - Os Type; Linux is 0x100 - * 55:48 - Distro specific identification - * 47:16 - Linux kernel version number - * 15:0 - Distro specific identification - * - * - */ - -#define HV_LINUX_VENDOR_ID 0x8100 - -/* - * Generate the guest ID based on the guideline described above. - */ - -static inline __u64 generate_guest_id(__u8 d_info1, __u32 kernel_version, - __u16 d_info2) -{ - __u64 guest_id = 0; - - guest_id = (((__u64)HV_LINUX_VENDOR_ID) << 48); - guest_id |= (((__u64)(d_info1)) << 48); - guest_id |= (((__u64)(kernel_version)) << 16); - guest_id |= ((__u64)(d_info2)); - - return guest_id; -} - #define HV_CPU_POWER_MANAGEMENT (1 << 0) #define HV_RECOMMENDATIONS_MAX 4 -- cgit v1.2.3 From 8730046c1498e8fb8c9a124789893944e8ce8220 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 18 Jan 2017 16:45:02 -0700 Subject: Drivers: hv vmbus: Move Hypercall page setup out of common code As part of the effort to separate out architecture specific code, move the hypercall page setup to an architecture specific file. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + arch/x86/Kbuild | 3 ++ arch/x86/hyperv/Makefile | 1 + arch/x86/hyperv/hv_init.c | 62 +++++++++++++++++++++++++++++++++++++++++ arch/x86/include/asm/mshyperv.h | 5 ++++ arch/x86/kernel/cpu/mshyperv.c | 7 +++++ drivers/hv/hv.c | 45 ++---------------------------- 7 files changed, 82 insertions(+), 42 deletions(-) create mode 100644 arch/x86/hyperv/Makefile create mode 100644 arch/x86/hyperv/hv_init.c diff --git a/MAINTAINERS b/MAINTAINERS index c36976d3bd1a..be8de24fd6dd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5962,6 +5962,7 @@ S: Maintained F: arch/x86/include/asm/mshyperv.h F: arch/x86/include/uapi/asm/hyperv.h F: arch/x86/kernel/cpu/mshyperv.c +F: arch/x86/hyperv F: drivers/hid/hid-hyperv.c F: drivers/hv/ F: drivers/input/serio/hyperv-keyboard.c diff --git a/arch/x86/Kbuild b/arch/x86/Kbuild index eb3abf8ac44e..586b786b3edf 100644 --- a/arch/x86/Kbuild +++ b/arch/x86/Kbuild @@ -7,6 +7,9 @@ obj-$(CONFIG_KVM) += kvm/ # Xen paravirtualization support obj-$(CONFIG_XEN) += xen/ +# Hyper-V paravirtualization support +obj-$(CONFIG_HYPERVISOR_GUEST) += hyperv/ + # lguest paravirtualization support obj-$(CONFIG_LGUEST_GUEST) += lguest/ diff --git a/arch/x86/hyperv/Makefile b/arch/x86/hyperv/Makefile new file mode 100644 index 000000000000..171ae09864d7 --- /dev/null +++ b/arch/x86/hyperv/Makefile @@ -0,0 +1 @@ +obj-y := hv_init.o diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c new file mode 100644 index 000000000000..3206bfda586d --- /dev/null +++ b/arch/x86/hyperv/hv_init.c @@ -0,0 +1,62 @@ +/* + * X86 specific Hyper-V initialization code. + * + * Copyright (C) 2016, Microsoft, Inc. + * + * Author : K. Y. Srinivasan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +void *hv_hypercall_pg; +/* + * This function is to be invoked early in the boot sequence after the + * hypervisor has been detected. + * + * 1. Setup the hypercall page. + */ +void hyperv_init(void) +{ + u64 guest_id; + union hv_x64_msr_hypercall_contents hypercall_msr; + + if (x86_hyper != &x86_hyper_ms_hyperv) + return; + + /* + * Setup the hypercall page and enable hypercalls. + * 1. Register the guest ID + * 2. Enable the hypercall and register the hypercall page + */ + guest_id = generate_guest_id(0, LINUX_VERSION_CODE, 0); + wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id); + + hv_hypercall_pg = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC); + if (hv_hypercall_pg == NULL) { + wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0); + return; + } + + rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + hypercall_msr.enable = 1; + hypercall_msr.guest_physical_address = vmalloc_to_pfn(hv_hypercall_pg); + wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); +} +EXPORT_SYMBOL_GPL(hv_hypercall_pg); diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 15a0c275c82e..e5f57e15a507 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -80,4 +80,9 @@ void hv_setup_kexec_handler(void (*handler)(void)); void hv_remove_kexec_handler(void); void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs)); void hv_remove_crash_handler(void); + +#if IS_ENABLED(CONFIG_HYPERV) +void hyperv_init(void); +extern void *hv_hypercall_pg; +#endif #endif diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 65e20c97e04b..c5a1e9ba9ae0 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -227,6 +227,13 @@ static void __init ms_hyperv_init_platform(void) */ if (efi_enabled(EFI_BOOT)) x86_platform.get_nmi_reason = hv_get_nmi_reason; + +#if IS_ENABLED(CONFIG_HYPERV) + /* + * Setup the hook to get control post apic initialization. + */ + x86_platform.apic_post_init = hyperv_init; +#endif } const __refconst struct hypervisor_x86 x86_hyper_ms_hyperv = { diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 714e1ebc834c..d8d41542d93c 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -193,7 +193,6 @@ int hv_init(void) { int max_leaf; union hv_x64_msr_hypercall_contents hypercall_msr; - void *virtaddr = NULL; memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); memset(hv_context.synic_message_page, 0, @@ -211,33 +210,15 @@ int hv_init(void) max_leaf = query_hypervisor_info(); - /* - * Write our OS ID. - */ - hv_context.guestid = generate_guest_id(0, LINUX_VERSION_CODE, 0); - wrmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid); /* See if the hypercall page is already set */ - rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - - virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC); - - if (!virtaddr) - goto cleanup; - - hypercall_msr.enable = 1; - - hypercall_msr.guest_physical_address = vmalloc_to_pfn(virtaddr); - wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - - /* Confirm that hypercall page did get setup. */ hypercall_msr.as_uint64 = 0; rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); if (!hypercall_msr.enable) - goto cleanup; + return -ENOTSUPP; - hv_context.hypercall_page = virtaddr; + hv_context.hypercall_page = hv_hypercall_pg; #ifdef CONFIG_X86_64 if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { @@ -261,15 +242,6 @@ int hv_init(void) return 0; cleanup: - if (virtaddr) { - if (hypercall_msr.enable) { - hypercall_msr.as_uint64 = 0; - wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - } - - vfree(virtaddr); - } - return -ENOTSUPP; } @@ -280,20 +252,9 @@ cleanup: */ void hv_cleanup(bool crash) { - union hv_x64_msr_hypercall_contents hypercall_msr; - - /* Reset our OS id */ - wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0); - - if (hv_context.hypercall_page) { - hypercall_msr.as_uint64 = 0; - wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - if (!crash) - vfree(hv_context.hypercall_page); - hv_context.hypercall_page = NULL; - } #ifdef CONFIG_X86_64 + union hv_x64_msr_hypercall_contents hypercall_msr; /* * Cleanup the TSC page based CS. */ -- cgit v1.2.3 From 6ab42a66d2cc10afefea9f9e5d9a5ad5a836d254 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 18 Jan 2017 16:45:03 -0700 Subject: Drivers: hv: vmbus: Move Hypercall invocation code out of common code As part of the effort to separate out architecture specific code, move the hypercall invocation code to an architecture specific file. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/hyperv/hv_init.c | 54 +++++++++++++++++++++++++++++++++++++---- arch/x86/include/asm/mshyperv.h | 1 - drivers/hv/hv.c | 52 --------------------------------------- drivers/hv/hyperv_vmbus.h | 1 - 4 files changed, 49 insertions(+), 59 deletions(-) diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 3206bfda586d..b5c8e04deacb 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -25,7 +25,7 @@ #include #include -void *hv_hypercall_pg; +static void *hypercall_pg; /* * This function is to be invoked early in the boot sequence after the * hypervisor has been detected. @@ -48,15 +48,59 @@ void hyperv_init(void) guest_id = generate_guest_id(0, LINUX_VERSION_CODE, 0); wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id); - hv_hypercall_pg = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC); - if (hv_hypercall_pg == NULL) { + hypercall_pg = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC); + if (hypercall_pg == NULL) { wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0); return; } rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); hypercall_msr.enable = 1; - hypercall_msr.guest_physical_address = vmalloc_to_pfn(hv_hypercall_pg); + hypercall_msr.guest_physical_address = vmalloc_to_pfn(hypercall_pg); wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); } -EXPORT_SYMBOL_GPL(hv_hypercall_pg); + +/* + * hv_do_hypercall- Invoke the specified hypercall + */ +u64 hv_do_hypercall(u64 control, void *input, void *output) +{ + u64 input_address = (input) ? virt_to_phys(input) : 0; + u64 output_address = (output) ? virt_to_phys(output) : 0; +#ifdef CONFIG_X86_64 + u64 hv_status = 0; + + if (!hypercall_pg) + return (u64)ULLONG_MAX; + + __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8"); + __asm__ __volatile__("call *%3" : "=a" (hv_status) : + "c" (control), "d" (input_address), + "m" (hypercall_pg)); + + return hv_status; + +#else + + u32 control_hi = control >> 32; + u32 control_lo = control & 0xFFFFFFFF; + u32 hv_status_hi = 1; + u32 hv_status_lo = 1; + u32 input_address_hi = input_address >> 32; + u32 input_address_lo = input_address & 0xFFFFFFFF; + u32 output_address_hi = output_address >> 32; + u32 output_address_lo = output_address & 0xFFFFFFFF; + + if (!hypercall_pg) + return (u64)ULLONG_MAX; + + __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi), + "=a"(hv_status_lo) : "d" (control_hi), + "a" (control_lo), "b" (input_address_hi), + "c" (input_address_lo), "D"(output_address_hi), + "S"(output_address_lo), "m" (hypercall_pg)); + + return hv_status_lo | ((u64)hv_status_hi << 32); +#endif /* !x86_64 */ +} +EXPORT_SYMBOL_GPL(hv_do_hypercall); diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index e5f57e15a507..ed8e07399071 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -83,6 +83,5 @@ void hv_remove_crash_handler(void); #if IS_ENABLED(CONFIG_HYPERV) void hyperv_init(void); -extern void *hv_hypercall_pg; #endif #endif diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index d8d41542d93c..fd3b9b98a29d 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -36,7 +36,6 @@ /* The one and only */ struct hv_context hv_context = { .synic_initialized = false, - .hypercall_page = NULL, }; #define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */ @@ -88,52 +87,6 @@ static int query_hypervisor_info(void) return max_leaf; } -/* - * hv_do_hypercall- Invoke the specified hypercall - */ -u64 hv_do_hypercall(u64 control, void *input, void *output) -{ - u64 input_address = (input) ? virt_to_phys(input) : 0; - u64 output_address = (output) ? virt_to_phys(output) : 0; - void *hypercall_page = hv_context.hypercall_page; -#ifdef CONFIG_X86_64 - u64 hv_status = 0; - - if (!hypercall_page) - return (u64)ULLONG_MAX; - - __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8"); - __asm__ __volatile__("call *%3" : "=a" (hv_status) : - "c" (control), "d" (input_address), - "m" (hypercall_page)); - - return hv_status; - -#else - - u32 control_hi = control >> 32; - u32 control_lo = control & 0xFFFFFFFF; - u32 hv_status_hi = 1; - u32 hv_status_lo = 1; - u32 input_address_hi = input_address >> 32; - u32 input_address_lo = input_address & 0xFFFFFFFF; - u32 output_address_hi = output_address >> 32; - u32 output_address_lo = output_address & 0xFFFFFFFF; - - if (!hypercall_page) - return (u64)ULLONG_MAX; - - __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi), - "=a"(hv_status_lo) : "d" (control_hi), - "a" (control_lo), "b" (input_address_hi), - "c" (input_address_lo), "D"(output_address_hi), - "S"(output_address_lo), "m" (hypercall_page)); - - return hv_status_lo | ((u64)hv_status_hi << 32); -#endif /* !x86_64 */ -} -EXPORT_SYMBOL_GPL(hv_do_hypercall); - #ifdef CONFIG_X86_64 static u64 read_hv_clock_tsc(struct clocksource *arg) { @@ -218,8 +171,6 @@ int hv_init(void) if (!hypercall_msr.enable) return -ENOTSUPP; - hv_context.hypercall_page = hv_hypercall_pg; - #ifdef CONFIG_X86_64 if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { union hv_x64_msr_hypercall_contents tsc_msr; @@ -466,9 +417,6 @@ int hv_synic_init(unsigned int cpu) union hv_synic_scontrol sctrl; u64 vp_index; - if (!hv_context.hypercall_page) - return -EFAULT; - /* Check the version */ rdmsrl(HV_X64_MSR_SVERSION, version); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index da57626786b7..09485269d537 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -371,7 +371,6 @@ struct hv_context { */ u64 guestid; - void *hypercall_page; void *tsc_page; bool synic_initialized; -- cgit v1.2.3 From ad90a3de9dd12e8d5f738d2ff0fdef3272bd363f Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 10 Jan 2017 13:50:54 -0500 Subject: pc104: Introduce the PC104 Kconfig option PC/104 form factor devices serve a specific niche of embedded system users; most Linux users will not have PC/104 form factor devices. This patch introduces the PC104 Kconfig option, which should be used to filter PC/104 specific device drivers and options, so that only those users interested in PC/104 related options are exposed to them. Signed-off-by: William Breathitt Gray Signed-off-by: Greg Kroah-Hartman --- init/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/init/Kconfig b/init/Kconfig index e1a937348a3e..aca8881b9117 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1720,6 +1720,13 @@ config PERF_USE_VMALLOC help See tools/perf/design.txt for details +config PC104 + bool "PC/104 support" + help + Expose PC/104 form factor device drivers and options available for + selection and configuration. Enable this option if your target + machine has a PC/104 bus. + menu "Kernel Performance Events And Counters" config PERF_EVENTS -- cgit v1.2.3 From f95bd041203b9f27a14e6ab733b9598049d7ffef Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 13 Jan 2017 11:00:25 +0100 Subject: memory: aemif: allow passing device lookup table as platform data TI aemif driver creates its own subnodes of the device tree in order to guarantee that all child devices are probed after the AEMIF timing parameters are configured. Some devices (e.g. da850) use struct of_dev_auxdata for clock lookup but nodes created from within the aemif driver can't access the lookup table. Create a platform data structure that holds a pointer to of_dev_auxdata so that we can use it with of_platform_populate(). Signed-off-by: Bartosz Golaszewski Acked-by: Sekhar Nori Acked-by: Santosh Shilimkar Signed-off-by: Greg Kroah-Hartman --- drivers/memory/ti-aemif.c | 8 +++++++- include/linux/platform_data/ti-aemif.h | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 include/linux/platform_data/ti-aemif.h diff --git a/drivers/memory/ti-aemif.c b/drivers/memory/ti-aemif.c index a579a0f25840..22c1aeeb6421 100644 --- a/drivers/memory/ti-aemif.c +++ b/drivers/memory/ti-aemif.c @@ -20,6 +20,7 @@ #include #include #include +#include #define TA_SHIFT 2 #define RHOLD_SHIFT 4 @@ -335,6 +336,8 @@ static int aemif_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct device_node *child_np; struct aemif_device *aemif; + struct aemif_platform_data *pdata; + struct of_dev_auxdata *dev_lookup; if (np == NULL) return 0; @@ -343,6 +346,9 @@ static int aemif_probe(struct platform_device *pdev) if (!aemif) return -ENOMEM; + pdata = dev_get_platdata(&pdev->dev); + dev_lookup = pdata ? pdata->dev_lookup : NULL; + platform_set_drvdata(pdev, aemif); aemif->clk = devm_clk_get(dev, NULL); @@ -390,7 +396,7 @@ static int aemif_probe(struct platform_device *pdev) * parameters are set. */ for_each_available_child_of_node(np, child_np) { - ret = of_platform_populate(child_np, NULL, NULL, dev); + ret = of_platform_populate(child_np, NULL, dev_lookup, dev); if (ret < 0) goto error; } diff --git a/include/linux/platform_data/ti-aemif.h b/include/linux/platform_data/ti-aemif.h new file mode 100644 index 000000000000..ac72e115093c --- /dev/null +++ b/include/linux/platform_data/ti-aemif.h @@ -0,0 +1,23 @@ +/* + * TI DaVinci AEMIF platform glue. + * + * Copyright (C) 2017 BayLibre SAS + * + * Author: + * Bartosz Golaszewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __TI_DAVINCI_AEMIF_DATA_H__ +#define __TI_DAVINCI_AEMIF_DATA_H__ + +#include + +struct aemif_platform_data { + struct of_dev_auxdata *dev_lookup; +}; + +#endif /* __TI_DAVINCI_AEMIF_DATA_H__ */ -- cgit v1.2.3 From d8e22fb4ccace71731193d3cf85d08e238b6e4f9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 13 Jan 2017 11:00:26 +0100 Subject: ARM: da850: add the nand dev_id to the clock lookup table The aemif driver can now access struct of_dev_auxdata (using platform data). Add the device id to the clock lookup table for the nand clock and create a separate lookup table for aemif subnodes. Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-davinci/da850.c | 10 +--------- arch/arm/mach-davinci/da8xx-dt.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 1d873d15b545..9780829f8a05 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -557,15 +557,7 @@ static struct clk_lookup da850_clks[] = { CLK("da830-mmc.0", NULL, &mmcsd0_clk), CLK("da830-mmc.1", NULL, &mmcsd1_clk), CLK("ti-aemif", NULL, &aemif_clk), - /* - * The only user of this clock is davinci_nand and it get's it through - * con_id. The nand node itself is created from within the aemif - * driver to guarantee that it's probed after the aemif timing - * parameters are configured. of_dev_auxdata is not accessible from - * the aemif driver and can't be passed to of_platform_populate(). For - * that reason we're leaving the dev_id here as NULL. - */ - CLK(NULL, "aemif", &aemif_nand_clk), + CLK("davinci-nand.0", "aemif", &aemif_nand_clk), CLK("ohci-da8xx", "usb11", &usb11_clk), CLK("musb-da8xx", "usb20", &usb20_clk), CLK("spi_davinci.0", NULL, &spi0_clk), diff --git a/arch/arm/mach-davinci/da8xx-dt.c b/arch/arm/mach-davinci/da8xx-dt.c index 9ee44da6eb7b..06205fe4c120 100644 --- a/arch/arm/mach-davinci/da8xx-dt.c +++ b/arch/arm/mach-davinci/da8xx-dt.c @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -18,6 +19,15 @@ #include "cp_intc.h" #include +static struct of_dev_auxdata da850_aemif_auxdata_lookup[] = { + OF_DEV_AUXDATA("ti,davinci-nand", 0x62000000, "davinci-nand.0", NULL), + {} +}; + +static struct aemif_platform_data aemif_data = { + .dev_lookup = da850_aemif_auxdata_lookup, +}; + static struct of_dev_auxdata da850_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("ti,davinci-i2c", 0x01c22000, "i2c_davinci.1", NULL), OF_DEV_AUXDATA("ti,davinci-i2c", 0x01e28000, "i2c_davinci.2", NULL), @@ -37,7 +47,7 @@ static struct of_dev_auxdata da850_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("ti,davinci-dm6467-emac", 0x01e20000, "davinci_emac.1", NULL), OF_DEV_AUXDATA("ti,da830-mcasp-audio", 0x01d00000, "davinci-mcasp.0", NULL), - OF_DEV_AUXDATA("ti,da850-aemif", 0x68000000, "ti-aemif", NULL), + OF_DEV_AUXDATA("ti,da850-aemif", 0x68000000, "ti-aemif", &aemif_data), OF_DEV_AUXDATA("ti,da850-tilcdc", 0x01e13000, "da8xx_lcdc.0", NULL), OF_DEV_AUXDATA("ti,da830-ohci", 0x01e25000, "ohci-da8xx", NULL), OF_DEV_AUXDATA("ti,da830-musb", 0x01e00000, "musb-da8xx", NULL), -- cgit v1.2.3 From 4474f4c40a9c607c7317e686b23619b7b768004f Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Mon, 16 Jan 2017 18:00:00 +0000 Subject: coresight: STM: Balance enable/disable The stm is automatically enabled when an application sets the policy via ->link() call back by using coresight_enable(), which keeps the refcount of the current users of the STM. However, the unlink() callback issues stm_disable() directly, which leaves the STM turned off, without the coresight layer knowing about it. This prevents any further uses of the STM hardware as the coresight layer still thinks the STM is turned on and doesn't enable the hardware when required. Even manually enabling the STM via sysfs can't really enable the hw. e.g, $ echo 1 > $CS_DEVS/$ETR/enable_sink $ mkdir -p $CONFIG_FS/stp-policy/$source.0/stm_test/ $ echo 32768 65535 > $CONFIG_FS/stp-policy/$source.0/stm_test/channels $ echo 64 > $CS_DEVS/$source/traceid $ ./stm_app Sending 64000 byte blocks of pattern 0 at 0us intervals Success to map channel(32768~32783) to 0xffffa95fa000 Sending on channel 32768 $ dd if=/dev/$ETR of=~/trace.bin.1 597+1 records in 597+1 records out 305920 bytes (306 kB) copied, 0.399952 s, 765 kB/s $ ./stm_app Sending 64000 byte blocks of pattern 0 at 0us intervals Success to map channel(32768~32783) to 0xffff7e9e2000 Sending on channel 32768 $ dd if=/dev/$ETR of=~/trace.bin.2 0+0 records in 0+0 records out 0 bytes (0 B) copied, 0.0232083 s, 0.0 kB/s Note that we don't get any data from the ETR for the second session. Also dmesg shows : [ 77.520458] coresight-tmc 20800000.etr: TMC-ETR enabled [ 77.537097] coresight-replicator etr_replicator@20890000: REPLICATOR enabled [ 77.558828] coresight-replicator main_replicator@208a0000: REPLICATOR enabled [ 77.581068] coresight-funnel 208c0000.main_funnel: FUNNEL inport 0 enabled [ 77.602217] coresight-tmc 20840000.etf: TMC-ETF enabled [ 77.618422] coresight-stm 20860000.stm: STM tracing enabled [ 139.554252] coresight-stm 20860000.stm: STM tracing disabled # End of first tracing session [ 146.351135] coresight-tmc 20800000.etr: TMC read start [ 146.514486] coresight-tmc 20800000.etr: TMC read end # Note that the STM is not turned on via stm_generic_link()->coresight_enable() # and hence none of the components are turned on. [ 152.479080] coresight-tmc 20800000.etr: TMC read start [ 152.542632] coresight-tmc 20800000.etr: TMC read end This patch fixes the problem by balancing the unlink operation by using the coresight_disable(), keeping the coresight layer in sync with the hardware state and thus allowing normal usage of the STM component. Fixes: commit 237483aa5cf43 ("coresight: stm: adding driver for CoreSight STM component") Cc: Pratik Patel Cc: Greg Kroah-Hartman Cc: stable@vger.kernel.org # 4.7+ Acked-by: Mathieu Poirier Reviewed-by: Chunyan Zhang Reported-by: Robert Walker Signed-off-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-stm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index e4c55c5f9988..93fc26f01bab 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -356,7 +356,7 @@ static void stm_generic_unlink(struct stm_data *stm_data, if (!drvdata || !drvdata->csdev) return; - stm_disable(drvdata->csdev, NULL); + coresight_disable(drvdata->csdev); } static phys_addr_t -- cgit v1.2.3 From 7a11a1d1b58873b2e5a6922dcdc23b6b339b14ba Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 11 Jan 2017 15:56:44 +0100 Subject: lkdtm: hide stack overflow warning for corrupt-stack test After the latest change to make sure the compiler actually does a memset, it is now smart enough to flag the stack overflow at compile time, at least with gcc-7.0: drivers/misc/lkdtm_bugs.c: In function 'lkdtm_CORRUPT_STACK': drivers/misc/lkdtm_bugs.c:88:144: warning: 'memset' writing 64 bytes into a region of size 8 overflows the destination [-Wstringop-overflow=] To outsmart the compiler again, this moves the memset into a noinline function where (for now) it doesn't see that we intentionally write broken code here. Fixes: c55d240003ae ("lkdtm: Prevent the compiler from optimising lkdtm_CORRUPT_STACK()") Signed-off-by: Arnd Bergmann Acked-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lkdtm_bugs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm_bugs.c index 91edd0b55e5c..bb3bb8ef5f44 100644 --- a/drivers/misc/lkdtm_bugs.c +++ b/drivers/misc/lkdtm_bugs.c @@ -80,12 +80,17 @@ void lkdtm_OVERFLOW(void) (void) recursive_loop(recur_count); } +static noinline void __lkdtm_CORRUPT_STACK(void *stack) +{ + memset(stack, 'a', 64); +} + noinline void lkdtm_CORRUPT_STACK(void) { /* Use default char array length that triggers stack protection. */ char data[8]; + __lkdtm_CORRUPT_STACK(&data); - memset((void *)data, 'a', 64); pr_info("Corrupted stack with '%16s'...\n", data); } -- cgit v1.2.3 From 29fe7d59bdd8cea561187fca155dbf5cee918bb8 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 20 Jan 2017 02:17:16 +0200 Subject: mei: make mei_io_list_flush static mei_io_list_flush is used only in client.c so make it local to the file and mark static. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 2 +- drivers/misc/mei/client.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index b0395601c6ae..3aaa86235fe0 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -404,7 +404,7 @@ static void __mei_io_list_flush(struct mei_cl_cb *list, * @list: An instance of our list structure * @cl: host client */ -void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) +static inline void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) { __mei_io_list_flush(list, cl, false); } diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index f2545af9be7b..dc80ad5c9fa2 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -93,7 +93,6 @@ 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 -- cgit v1.2.3 From 669c256cb9fb29a540ce4befb4328d88c665e523 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 20 Jan 2017 02:17:17 +0200 Subject: mei: make mei_cl_set_disconnected static mei_cl_set_disconnected is used only in client.c, so make it local to the file and mark static. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 2 +- drivers/misc/mei/client.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 3aaa86235fe0..da1c0703910a 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -756,7 +756,7 @@ static void mei_cl_wake_all(struct mei_cl *cl) * * @cl: host client */ -void mei_cl_set_disconnected(struct mei_cl *cl) +static void mei_cl_set_disconnected(struct mei_cl *cl) { struct mei_device *dev = cl->dev; diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index dc80ad5c9fa2..ab92a542661b 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -208,7 +208,6 @@ static inline u8 mei_cl_host_addr(const struct mei_cl *cl) } int mei_cl_disconnect(struct mei_cl *cl); -void mei_cl_set_disconnected(struct mei_cl *cl); int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, -- cgit v1.2.3 From 63ed4e0c67df332681ebfef6eca6852da28d6300 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:46 -0700 Subject: Drivers: hv: vmbus: Consolidate all Hyper-V specific clocksource code As part of the effort to separate out architecture specific code, consolidate all Hyper-V specific clocksource code to an architecture specific code. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/hyperv/hv_init.c | 105 ++++++++++++++++++++++++++++++++++++++++ arch/x86/include/asm/mshyperv.h | 12 +++++ arch/x86/kernel/cpu/mshyperv.c | 23 --------- drivers/hv/hv.c | 95 ------------------------------------ drivers/hv/hyperv_vmbus.h | 8 --- 5 files changed, 117 insertions(+), 126 deletions(-) diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index b5c8e04deacb..860233af4568 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -24,6 +24,79 @@ #include #include #include +#include + + +#ifdef CONFIG_X86_64 + +static struct ms_hyperv_tsc_page *tsc_pg; + +static u64 read_hv_clock_tsc(struct clocksource *arg) +{ + u64 current_tick; + + if (tsc_pg->tsc_sequence != 0) { + /* + * Use the tsc page to compute the value. + */ + + while (1) { + u64 tmp; + u32 sequence = tsc_pg->tsc_sequence; + u64 cur_tsc; + u64 scale = tsc_pg->tsc_scale; + s64 offset = tsc_pg->tsc_offset; + + rdtscll(cur_tsc); + /* current_tick = ((cur_tsc *scale) >> 64) + offset */ + asm("mulq %3" + : "=d" (current_tick), "=a" (tmp) + : "a" (cur_tsc), "r" (scale)); + + current_tick += offset; + if (tsc_pg->tsc_sequence == sequence) + return current_tick; + + if (tsc_pg->tsc_sequence != 0) + continue; + /* + * Fallback using MSR method. + */ + break; + } + } + rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); + return current_tick; +} + +static struct clocksource hyperv_cs_tsc = { + .name = "hyperv_clocksource_tsc_page", + .rating = 400, + .read = read_hv_clock_tsc, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; +#endif + +static u64 read_hv_clock_msr(struct clocksource *arg) +{ + u64 current_tick; + /* + * Read the partition counter to get the current tick count. This count + * is set to 0 when the partition is created and is incremented in + * 100 nanosecond units. + */ + rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); + return current_tick; +} + +static struct clocksource hyperv_cs_msr = { + .name = "hyperv_clocksource_msr", + .rating = 400, + .read = read_hv_clock_msr, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; static void *hypercall_pg; /* @@ -31,6 +104,7 @@ static void *hypercall_pg; * hypervisor has been detected. * * 1. Setup the hypercall page. + * 2. Register Hyper-V specific clocksource. */ void hyperv_init(void) { @@ -58,6 +132,37 @@ void hyperv_init(void) hypercall_msr.enable = 1; hypercall_msr.guest_physical_address = vmalloc_to_pfn(hypercall_pg); wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + + /* + * Register Hyper-V specific clocksource. + */ +#ifdef CONFIG_X86_64 + if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { + union hv_x64_msr_hypercall_contents tsc_msr; + + tsc_pg = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL); + if (!tsc_pg) { + clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100); + return; + } + + rdmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); + + tsc_msr.enable = 1; + tsc_msr.guest_physical_address = vmalloc_to_pfn(tsc_pg); + + wrmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); + clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); + return; + } +#endif + /* + * For 32 bit guests just use the MSR based mechanism for reading + * the partition counter. + */ + + if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE) + clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100); } /* diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index ed8e07399071..adfe8cc9f7e3 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -25,6 +25,18 @@ union hv_x64_msr_hypercall_contents { }; }; +/* + * TSC page layout. + */ + +struct ms_hyperv_tsc_page { + volatile u32 tsc_sequence; + u32 reserved1; + volatile u64 tsc_scale; + volatile s64 tsc_offset; + u64 reserved2[509]; +}; + /* * The guest OS needs to register the guest ID with the hypervisor. * The guest ID is a 64 bit entity and the structure of this ID is diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index c5a1e9ba9ae0..d3705a44971c 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -133,26 +133,6 @@ static uint32_t __init ms_hyperv_platform(void) return 0; } -static u64 read_hv_clock(struct clocksource *arg) -{ - u64 current_tick; - /* - * Read the partition counter to get the current tick count. This count - * is set to 0 when the partition is created and is incremented in - * 100 nanosecond units. - */ - rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); - return current_tick; -} - -static struct clocksource hyperv_cs = { - .name = "hyperv_clocksource", - .rating = 400, /* use this when running on Hyperv*/ - .read = read_hv_clock, - .mask = CLOCKSOURCE_MASK(64), - .flags = CLOCK_SOURCE_IS_CONTINUOUS, -}; - static unsigned char hv_get_nmi_reason(void) { return 0; @@ -208,9 +188,6 @@ static void __init ms_hyperv_init_platform(void) "hv_nmi_unknown"); #endif - if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE) - clocksource_register_hz(&hyperv_cs, NSEC_PER_SEC/100); - #ifdef CONFIG_X86_IO_APIC no_timer_check = 1; #endif diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index fd3b9b98a29d..1a33b59776d3 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -87,56 +87,6 @@ static int query_hypervisor_info(void) return max_leaf; } -#ifdef CONFIG_X86_64 -static u64 read_hv_clock_tsc(struct clocksource *arg) -{ - u64 current_tick; - struct ms_hyperv_tsc_page *tsc_pg = hv_context.tsc_page; - - if (tsc_pg->tsc_sequence != 0) { - /* - * Use the tsc page to compute the value. - */ - - while (1) { - u64 tmp; - u32 sequence = tsc_pg->tsc_sequence; - u64 cur_tsc; - u64 scale = tsc_pg->tsc_scale; - s64 offset = tsc_pg->tsc_offset; - - rdtscll(cur_tsc); - /* current_tick = ((cur_tsc *scale) >> 64) + offset */ - asm("mulq %3" - : "=d" (current_tick), "=a" (tmp) - : "a" (cur_tsc), "r" (scale)); - - current_tick += offset; - if (tsc_pg->tsc_sequence == sequence) - return current_tick; - - if (tsc_pg->tsc_sequence != 0) - continue; - /* - * Fallback using MSR method. - */ - break; - } - } - rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); - return current_tick; -} - -static struct clocksource hyperv_cs_tsc = { - .name = "hyperv_clocksource_tsc_page", - .rating = 425, - .read = read_hv_clock_tsc, - .mask = CLOCKSOURCE_MASK(64), - .flags = CLOCK_SOURCE_IS_CONTINUOUS, -}; -#endif - - /* * hv_init - Main initialization routine. * @@ -171,29 +121,7 @@ int hv_init(void) if (!hypercall_msr.enable) return -ENOTSUPP; -#ifdef CONFIG_X86_64 - if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { - union hv_x64_msr_hypercall_contents tsc_msr; - void *va_tsc; - - va_tsc = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL); - if (!va_tsc) - goto cleanup; - hv_context.tsc_page = va_tsc; - - rdmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); - - tsc_msr.enable = 1; - tsc_msr.guest_physical_address = vmalloc_to_pfn(va_tsc); - - wrmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); - clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); - } -#endif return 0; - -cleanup: - return -ENOTSUPP; } /* @@ -204,29 +132,6 @@ cleanup: void hv_cleanup(bool crash) { -#ifdef CONFIG_X86_64 - union hv_x64_msr_hypercall_contents hypercall_msr; - /* - * Cleanup the TSC page based CS. - */ - if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { - /* - * Crash can happen in an interrupt context and unregistering - * a clocksource is impossible and redundant in this case. - */ - if (!oops_in_progress) { - clocksource_change_rating(&hyperv_cs_tsc, 10); - clocksource_unregister(&hyperv_cs_tsc); - } - - hypercall_msr.as_uint64 = 0; - wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); - if (!crash) { - vfree(hv_context.tsc_page); - hv_context.tsc_page = NULL; - } - } -#endif } /* diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 09485269d537..947455d30707 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -416,14 +416,6 @@ struct hv_context { extern struct hv_context hv_context; -struct ms_hyperv_tsc_page { - volatile u32 tsc_sequence; - u32 reserved1; - volatile u64 tsc_scale; - volatile s64 tsc_offset; - u64 reserved2[509]; -}; - struct hv_ring_buffer_debug_info { u32 current_interrupt_mask; u32 current_read_index; -- cgit v1.2.3 From 8de8af7e0873c4fdac2205327dff922819e16657 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:47 -0700 Subject: Drivers: hv: vmbus: Move the extracting of Hypervisor version information As part of the effort to separate out architecture specific code, extract hypervisor version information in an architecture specific file. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/mshyperv.h | 19 ++++++++++++++++ arch/x86/kernel/cpu/mshyperv.c | 20 +++++++++++++++++ drivers/hv/connection.c | 7 ++---- drivers/hv/hv.c | 49 ----------------------------------------- drivers/hv/hyperv_vmbus.h | 27 ----------------------- 5 files changed, 41 insertions(+), 81 deletions(-) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index adfe8cc9f7e3..54729e3cba47 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -5,6 +5,25 @@ #include #include +/* + * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent + * is set by CPUID(HVCPUID_VERSION_FEATURES). + */ +enum hv_cpuid_function { + HVCPUID_VERSION_FEATURES = 0x00000001, + HVCPUID_VENDOR_MAXFUNCTION = 0x40000000, + HVCPUID_INTERFACE = 0x40000001, + + /* + * The remaining functions depend on the value of + * HVCPUID_INTERFACE + */ + HVCPUID_VERSION = 0x40000002, + HVCPUID_FEATURES = 0x40000003, + HVCPUID_ENLIGHTENMENT_INFO = 0x40000004, + HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005, +}; + struct ms_hyperv_info { u32 features; u32 misc_features; diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index d3705a44971c..b5375b9497b3 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -160,6 +160,11 @@ static int hv_nmi_unknown(unsigned int val, struct pt_regs *regs) static void __init ms_hyperv_init_platform(void) { + int hv_host_info_eax; + int hv_host_info_ebx; + int hv_host_info_ecx; + int hv_host_info_edx; + /* * Extract the features and hints */ @@ -170,6 +175,21 @@ static void __init ms_hyperv_init_platform(void) pr_info("HyperV: features 0x%x, hints 0x%x\n", ms_hyperv.features, ms_hyperv.hints); + /* + * Extract host information. + */ + if (cpuid_eax(HVCPUID_VENDOR_MAXFUNCTION) >= HVCPUID_VERSION) { + hv_host_info_eax = cpuid_eax(HVCPUID_VERSION); + hv_host_info_ebx = cpuid_ebx(HVCPUID_VERSION); + hv_host_info_ecx = cpuid_ecx(HVCPUID_VERSION); + hv_host_info_edx = cpuid_edx(HVCPUID_VERSION); + + pr_info("Hyper-V Host Build:%d-%d.%d-%d-%d.%d\n", + hv_host_info_eax, hv_host_info_ebx >> 16, + hv_host_info_ebx & 0xFFFF, hv_host_info_ecx, + hv_host_info_edx >> 24, hv_host_info_edx & 0xFFFFFF); + } + #ifdef CONFIG_X86_LOCAL_APIC if (ms_hyperv.features & HV_X64_MSR_APIC_FREQUENCY_AVAILABLE) { /* diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 9b72ebcd37bc..307a5a8937f6 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -221,11 +221,8 @@ int vmbus_connect(void) goto cleanup; vmbus_proto_version = version; - pr_info("Hyper-V Host Build:%d-%d.%d-%d-%d.%d; Vmbus version:%d.%d\n", - host_info_eax, host_info_ebx >> 16, - host_info_ebx & 0xFFFF, host_info_ecx, - host_info_edx >> 24, host_info_edx & 0xFFFFFF, - version >> 16, version & 0xFFFF); + pr_info("Vmbus version:%d.%d\n", + version >> 16, version & 0xFFFF); kfree(msginfo); return 0; diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 1a33b59776d3..9985a347ed03 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -42,51 +42,6 @@ struct hv_context hv_context = { #define HV_MAX_MAX_DELTA_TICKS 0xffffffff #define HV_MIN_DELTA_TICKS 1 -/* - * query_hypervisor_info - Get version info of the windows hypervisor - */ -unsigned int host_info_eax; -unsigned int host_info_ebx; -unsigned int host_info_ecx; -unsigned int host_info_edx; - -static int query_hypervisor_info(void) -{ - unsigned int eax; - unsigned int ebx; - unsigned int ecx; - unsigned int edx; - unsigned int max_leaf; - unsigned int op; - - /* - * Its assumed that this is called after confirming that Viridian - * is present. Query id and revision. - */ - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HVCPUID_VENDOR_MAXFUNCTION; - cpuid(op, &eax, &ebx, &ecx, &edx); - - max_leaf = eax; - - if (max_leaf >= HVCPUID_VERSION) { - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HVCPUID_VERSION; - cpuid(op, &eax, &ebx, &ecx, &edx); - host_info_eax = eax; - host_info_ebx = ebx; - host_info_ecx = ecx; - host_info_edx = edx; - } - return max_leaf; -} - /* * hv_init - Main initialization routine. * @@ -94,7 +49,6 @@ static int query_hypervisor_info(void) */ int hv_init(void) { - int max_leaf; union hv_x64_msr_hypercall_contents hypercall_msr; memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); @@ -111,9 +65,6 @@ int hv_init(void) memset(hv_context.clk_evt, 0, sizeof(void *) * NR_CPUS); - max_leaf = query_hypervisor_info(); - - /* See if the hypercall page is already set */ hypercall_msr.as_uint64 = 0; rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 947455d30707..a7e35c842fed 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -40,25 +40,6 @@ */ #define HV_UTIL_NEGO_TIMEOUT 55 -/* - * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent - * is set by CPUID(HVCPUID_VERSION_FEATURES). - */ -enum hv_cpuid_function { - HVCPUID_VERSION_FEATURES = 0x00000001, - HVCPUID_VENDOR_MAXFUNCTION = 0x40000000, - HVCPUID_INTERFACE = 0x40000001, - - /* - * The remaining functions depend on the value of - * HVCPUID_INTERFACE - */ - HVCPUID_VERSION = 0x40000002, - HVCPUID_FEATURES = 0x40000003, - HVCPUID_ENLIGHTENMENT_INFO = 0x40000004, - HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005, -}; - #define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE 0x400 #define HV_X64_MSR_CRASH_P0 0x40000100 @@ -444,14 +425,6 @@ extern int hv_synic_cleanup(unsigned int cpu); extern void hv_synic_clockevents_cleanup(void); -/* - * Host version information. - */ -extern unsigned int host_info_eax; -extern unsigned int host_info_ebx; -extern unsigned int host_info_ecx; -extern unsigned int host_info_edx; - /* Interface */ -- cgit v1.2.3 From d058fa7e98ff01a4b4750a2210fc19906db3cbe1 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:48 -0700 Subject: Drivers: hv: vmbus: Move the crash notification function As part of the effort to separate out architecture specific code, move the crash notification function. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/hyperv/hv_init.c | 26 ++++++++++++++++++++++++++ arch/x86/include/asm/mshyperv.h | 1 + arch/x86/include/uapi/asm/hyperv.h | 8 ++++++++ drivers/hv/hyperv_vmbus.h | 10 ---------- drivers/hv/vmbus_drv.c | 25 ------------------------- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 860233af4568..ce5fc7394814 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -209,3 +209,29 @@ u64 hv_do_hypercall(u64 control, void *input, void *output) #endif /* !x86_64 */ } EXPORT_SYMBOL_GPL(hv_do_hypercall); + +void hyperv_report_panic(struct pt_regs *regs) +{ + static bool panic_reported; + + /* + * We prefer to report panic on 'die' chain as we have proper + * registers to report, but if we miss it (e.g. on BUG()) we need + * to report it on 'panic'. + */ + if (panic_reported) + return; + panic_reported = true; + + wrmsrl(HV_X64_MSR_CRASH_P0, regs->ip); + wrmsrl(HV_X64_MSR_CRASH_P1, regs->ax); + wrmsrl(HV_X64_MSR_CRASH_P2, regs->bx); + wrmsrl(HV_X64_MSR_CRASH_P3, regs->cx); + wrmsrl(HV_X64_MSR_CRASH_P4, regs->dx); + + /* + * Let Hyper-V know there is crash data available + */ + wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY); +} +EXPORT_SYMBOL_GPL(hyperv_report_panic); diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 54729e3cba47..64e682d88684 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -114,5 +114,6 @@ void hv_remove_crash_handler(void); #if IS_ENABLED(CONFIG_HYPERV) void hyperv_init(void); +void hyperv_report_panic(struct pt_regs *regs); #endif #endif diff --git a/arch/x86/include/uapi/asm/hyperv.h b/arch/x86/include/uapi/asm/hyperv.h index 9b1a91834ac8..3a20ccf787b8 100644 --- a/arch/x86/include/uapi/asm/hyperv.h +++ b/arch/x86/include/uapi/asm/hyperv.h @@ -73,6 +73,9 @@ */ #define HV_X64_MSR_STAT_PAGES_AVAILABLE (1 << 8) +/* Crash MSR available */ +#define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE (1 << 10) + /* * Feature identification: EBX indicates which flags were specified at * partition creation. The format is the same as the partition creation @@ -144,6 +147,11 @@ */ #define HV_X64_RELAXED_TIMING_RECOMMENDED (1 << 5) +/* + * Crash notification flag. + */ +#define HV_CRASH_CTL_CRASH_NOTIFY (1ULL << 63) + /* MSR used to identify the guest OS. */ #define HV_X64_MSR_GUEST_OS_ID 0x40000000 diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index a7e35c842fed..59eb28c45ff5 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -40,16 +40,6 @@ */ #define HV_UTIL_NEGO_TIMEOUT 55 -#define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE 0x400 - -#define HV_X64_MSR_CRASH_P0 0x40000100 -#define HV_X64_MSR_CRASH_P1 0x40000101 -#define HV_X64_MSR_CRASH_P2 0x40000102 -#define HV_X64_MSR_CRASH_P3 0x40000103 -#define HV_X64_MSR_CRASH_P4 0x40000104 -#define HV_X64_MSR_CRASH_CTL 0x40000105 - -#define HV_CRASH_CTL_CRASH_NOTIFY (1ULL << 63) /* Define version of the synthetic interrupt controller. */ #define HV_SYNIC_VERSION (1) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 565bdd16134a..8e81346114d4 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -56,31 +56,6 @@ static struct completion probe_event; static int hyperv_cpuhp_online; -static void hyperv_report_panic(struct pt_regs *regs) -{ - static bool panic_reported; - - /* - * We prefer to report panic on 'die' chain as we have proper - * registers to report, but if we miss it (e.g. on BUG()) we need - * to report it on 'panic'. - */ - if (panic_reported) - return; - panic_reported = true; - - wrmsrl(HV_X64_MSR_CRASH_P0, regs->ip); - wrmsrl(HV_X64_MSR_CRASH_P1, regs->ax); - wrmsrl(HV_X64_MSR_CRASH_P2, regs->bx); - wrmsrl(HV_X64_MSR_CRASH_P3, regs->cx); - wrmsrl(HV_X64_MSR_CRASH_P4, regs->dx); - - /* - * Let Hyper-V know there is crash data available - */ - wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY); -} - static int hyperv_panic_event(struct notifier_block *nb, unsigned long val, void *args) { -- cgit v1.2.3 From 73638cddaad861a5ebb2b119d8b318d4bded8f8d Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:49 -0700 Subject: Drivers: hv: vmbus: Move the check for hypercall page setup As part of the effort to separate out architecture specific code, move the check for detecting if the hypercall page is setup. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/hyperv/hv_init.c | 15 +++++++++++++++ arch/x86/include/asm/mshyperv.h | 1 + drivers/hv/hv.c | 7 +------ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index ce5fc7394814..d289bc29d282 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -235,3 +235,18 @@ void hyperv_report_panic(struct pt_regs *regs) wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY); } EXPORT_SYMBOL_GPL(hyperv_report_panic); + +bool hv_is_hypercall_page_setup(void) +{ + union hv_x64_msr_hypercall_contents hypercall_msr; + + /* Check if the hypercall page is setup */ + hypercall_msr.as_uint64 = 0; + rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + + if (!hypercall_msr.enable) + return false; + + return true; +} +EXPORT_SYMBOL_GPL(hv_is_hypercall_page_setup); diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 64e682d88684..c843ef64defe 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -115,5 +115,6 @@ void hv_remove_crash_handler(void); #if IS_ENABLED(CONFIG_HYPERV) void hyperv_init(void); void hyperv_report_panic(struct pt_regs *regs); +bool hv_is_hypercall_page_setup(void); #endif #endif diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 9985a347ed03..d28a8731baa0 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -49,7 +49,6 @@ struct hv_context hv_context = { */ int hv_init(void) { - union hv_x64_msr_hypercall_contents hypercall_msr; memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); memset(hv_context.synic_message_page, 0, @@ -65,11 +64,7 @@ int hv_init(void) memset(hv_context.clk_evt, 0, sizeof(void *) * NR_CPUS); - /* See if the hypercall page is already set */ - hypercall_msr.as_uint64 = 0; - rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - - if (!hypercall_msr.enable) + if (!hv_is_hypercall_page_setup()) return -ENOTSUPP; return 0; -- cgit v1.2.3 From e810e48c0c9a1a1ebb90cfe966bce6dc80ce08e7 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:50 -0700 Subject: Drivers: hv: vmbus: Move the code to signal end of message As part of the effort to separate out architecture specific code, move the code for signaling end of message. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/mshyperv.h | 37 +++++++++++++++++++++++++++++++++++++ drivers/hv/channel_mgmt.c | 1 + drivers/hv/hyperv_vmbus.h | 35 ----------------------------------- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index c843ef64defe..b57b470ac2a7 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -99,6 +99,43 @@ static inline __u64 generate_guest_id(__u64 d_info1, __u64 kernel_version, return guest_id; } + +/* Free the message slot and signal end-of-message if required */ +static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) +{ + /* + * On crash we're reading some other CPU's message page and we need + * to be careful: this other CPU may already had cleared the header + * and the host may already had delivered some other message there. + * In case we blindly write msg->header.message_type we're going + * to lose it. We can still lose a message of the same type but + * we count on the fact that there can only be one + * CHANNELMSG_UNLOAD_RESPONSE and we don't care about other messages + * on crash. + */ + if (cmpxchg(&msg->header.message_type, old_msg_type, + HVMSG_NONE) != old_msg_type) + return; + + /* + * Make sure the write to MessageType (ie set to + * HVMSG_NONE) happens before we read the + * MessagePending and EOMing. Otherwise, the EOMing + * will not deliver any more messages since there is + * no empty slot + */ + mb(); + + if (msg->header.message_flags.msg_pending) { + /* + * This will cause message queue rescan to + * possibly deliver another msg from the + * hypervisor + */ + wrmsrl(HV_X64_MSR_EOM, 0); + } +} + void hyperv_callback_vector(void); #ifdef CONFIG_TRACING #define trace_hyperv_callback_vector hyperv_callback_vector diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 0af7e39006c8..49d77be90ca4 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "hyperv_vmbus.h" diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 59eb28c45ff5..e9f5d2c2fb6b 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -521,41 +521,6 @@ struct vmbus_channel_message_table_entry { extern struct vmbus_channel_message_table_entry channel_message_table[CHANNELMSG_COUNT]; -/* Free the message slot and signal end-of-message if required */ -static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) -{ - /* - * On crash we're reading some other CPU's message page and we need - * to be careful: this other CPU may already had cleared the header - * and the host may already had delivered some other message there. - * In case we blindly write msg->header.message_type we're going - * to lose it. We can still lose a message of the same type but - * we count on the fact that there can only be one - * CHANNELMSG_UNLOAD_RESPONSE and we don't care about other messages - * on crash. - */ - if (cmpxchg(&msg->header.message_type, old_msg_type, - HVMSG_NONE) != old_msg_type) - return; - - /* - * Make sure the write to MessageType (ie set to - * HVMSG_NONE) happens before we read the - * MessagePending and EOMing. Otherwise, the EOMing - * will not deliver any more messages since there is - * no empty slot - */ - mb(); - - if (msg->header.message_flags.msg_pending) { - /* - * This will cause message queue rescan to - * possibly deliver another msg from the - * hypervisor - */ - wrmsrl(HV_X64_MSR_EOM, 0); - } -} /* General vmbus interface */ -- cgit v1.2.3 From d5116b4091ecca271c249ede43a49c1245920558 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:51 -0700 Subject: Drivers: hv: vmbus: Restructure the clockevents code Move the relevant code that programs the hypervisor to an architecture specific file. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/mshyperv.h | 4 ++++ drivers/hv/hv.c | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index b57b470ac2a7..a58c201f3412 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -136,6 +136,10 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) } } +#define hv_get_current_tick(tick) rdmsrl(HV_X64_MSR_TIME_REF_COUNT, tick) +#define hv_init_timer(timer, tick) wrmsrl(timer, tick) +#define hv_init_timer_config(config, val) wrmsrl(config, val) + void hyperv_callback_vector(void); #ifdef CONFIG_TRACING #define trace_hyperv_callback_vector hyperv_callback_vector diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index d28a8731baa0..ae5436e9c8a4 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -118,16 +118,16 @@ static int hv_ce_set_next_event(unsigned long delta, WARN_ON(!clockevent_state_oneshot(evt)); - rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); + hv_get_current_tick(current_tick); current_tick += delta; - wrmsrl(HV_X64_MSR_STIMER0_COUNT, current_tick); + hv_init_timer(HV_X64_MSR_STIMER0_COUNT, current_tick); return 0; } static int hv_ce_shutdown(struct clock_event_device *evt) { - wrmsrl(HV_X64_MSR_STIMER0_COUNT, 0); - wrmsrl(HV_X64_MSR_STIMER0_CONFIG, 0); + hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0); + hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0); return 0; } @@ -139,7 +139,7 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt) timer_cfg.enable = 1; timer_cfg.auto_enable = 1; timer_cfg.sintx = VMBUS_MESSAGE_SINT; - wrmsrl(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64); + hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64); return 0; } -- cgit v1.2.3 From 305f7549c9298247723c255baddb7a54b4e63050 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:52 -0700 Subject: Drivers: hv: util: Use hv_get_current_tick() to get current tick As part of the effort to interact with Hyper-V in an instruction set architecture independent way, use the new API to get the current tick. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index e7707747f56d..7c90e3334a88 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "hyperv_vmbus.h" @@ -199,7 +200,7 @@ static void hv_set_host_time(struct work_struct *work) */ u64 current_tick; - rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); + hv_get_current_tick(current_tick); newtime += (current_tick - wrk->ref_time); } host_tns = (newtime - WLTIMEDELTA) * 100; -- cgit v1.2.3 From d383877db60bcc7fd02d1051a90e078d731dfb59 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:53 -0700 Subject: Drivers: hv: vmbus: Get rid of an unsused variable The version variable while it is initialized is not used; get rid of it. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index ae5436e9c8a4..ced2077bb925 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -261,16 +261,12 @@ void hv_synic_free(void) */ int hv_synic_init(unsigned int cpu) { - u64 version; union hv_synic_simp simp; union hv_synic_siefp siefp; union hv_synic_sint shared_sint; union hv_synic_scontrol sctrl; u64 vp_index; - /* Check the version */ - rdmsrl(HV_X64_MSR_SVERSION, version); - /* Setup the Synic's message page */ rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); simp.simp_enabled = 1; -- cgit v1.2.3 From 155e4a2f28a59e5344dfa7c5d003161fe59a5bf2 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:54 -0700 Subject: Drivers: hv: vmbus: Define APIs to manipulate the message page As part of cleaning up architecture specific code, define APIs to manipulate the message page. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/mshyperv.h | 3 +++ drivers/hv/hv.c | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index a58c201f3412..1e75141bc123 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -140,6 +140,9 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) #define hv_init_timer(timer, tick) wrmsrl(timer, tick) #define hv_init_timer_config(config, val) wrmsrl(config, val) +#define hv_get_simp(val) rdmsrl(HV_X64_MSR_SIMP, val) +#define hv_set_simp(val) wrmsrl(HV_X64_MSR_SIMP, val) + void hyperv_callback_vector(void); #ifdef CONFIG_TRACING #define trace_hyperv_callback_vector hyperv_callback_vector diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index ced2077bb925..04ad97749884 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -268,12 +268,12 @@ int hv_synic_init(unsigned int cpu) u64 vp_index; /* Setup the Synic's message page */ - rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); + hv_get_simp(simp.as_uint64); simp.simp_enabled = 1; simp.base_simp_gpa = virt_to_phys(hv_context.synic_message_page[cpu]) >> PAGE_SHIFT; - wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); + hv_set_simp(simp.as_uint64); /* Setup the Synic's event page */ rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); @@ -392,11 +392,11 @@ int hv_synic_cleanup(unsigned int cpu) /* Disable the interrupt */ wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); - rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); + hv_get_simp(simp.as_uint64); simp.simp_enabled = 0; simp.base_simp_gpa = 0; - wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); + hv_set_simp(simp.as_uint64); rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); siefp.siefp_enabled = 0; -- cgit v1.2.3 From 8e307bf82d76ab02e95a00d132d926f04db6ccab Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:55 -0700 Subject: Drivers: hv: vmbus: Define APIs to manipulate the event page As part of cleaning up architecture specific code, define APIs to manipulate the event page. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/mshyperv.h | 3 +++ drivers/hv/hv.c | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 1e75141bc123..2ea7e16fc678 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -143,6 +143,9 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) #define hv_get_simp(val) rdmsrl(HV_X64_MSR_SIMP, val) #define hv_set_simp(val) wrmsrl(HV_X64_MSR_SIMP, val) +#define hv_get_siefp(val) rdmsrl(HV_X64_MSR_SIEFP, val) +#define hv_set_siefp(val) wrmsrl(HV_X64_MSR_SIEFP, val) + void hyperv_callback_vector(void); #ifdef CONFIG_TRACING #define trace_hyperv_callback_vector hyperv_callback_vector diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 04ad97749884..5680aeed585c 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -276,12 +276,12 @@ int hv_synic_init(unsigned int cpu) hv_set_simp(simp.as_uint64); /* Setup the Synic's event page */ - rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); + hv_get_siefp(siefp.as_uint64); siefp.siefp_enabled = 1; siefp.base_siefp_gpa = virt_to_phys(hv_context.synic_event_page[cpu]) >> PAGE_SHIFT; - wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); + hv_set_siefp(siefp.as_uint64); /* Setup the shared SINT. */ rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); @@ -398,11 +398,11 @@ int hv_synic_cleanup(unsigned int cpu) hv_set_simp(simp.as_uint64); - rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); + hv_get_siefp(siefp.as_uint64); siefp.siefp_enabled = 0; siefp.base_siefp_gpa = 0; - wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); + hv_set_siefp(siefp.as_uint64); /* Disable the global synic bit */ rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); -- cgit v1.2.3 From 06d1d98a839f196e94cb726008fb2118e430f356 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:56 -0700 Subject: Drivers: hv: vmbus: Define APIs to manipulate the synthetic interrupt controller As part of cleaning up architecture specific code, define APIs to manipulate the interrupt controller state. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/mshyperv.h | 3 +++ drivers/hv/hv.c | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 2ea7e16fc678..1ea19a57cf4c 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -146,6 +146,9 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) #define hv_get_siefp(val) rdmsrl(HV_X64_MSR_SIEFP, val) #define hv_set_siefp(val) wrmsrl(HV_X64_MSR_SIEFP, val) +#define hv_get_synic_state(val) rdmsrl(HV_X64_MSR_SCONTROL, val) +#define hv_set_synic_state(val) wrmsrl(HV_X64_MSR_SCONTROL, val) + void hyperv_callback_vector(void); #ifdef CONFIG_TRACING #define trace_hyperv_callback_vector hyperv_callback_vector diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 5680aeed585c..7cb036d4b243 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -294,10 +294,10 @@ int hv_synic_init(unsigned int cpu) wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); /* Enable the global synic bit */ - rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); + hv_get_synic_state(sctrl.as_uint64); sctrl.enable = 1; - wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); + hv_set_synic_state(sctrl.as_uint64); hv_context.synic_initialized = true; @@ -405,9 +405,9 @@ int hv_synic_cleanup(unsigned int cpu) hv_set_siefp(siefp.as_uint64); /* Disable the global synic bit */ - rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); + hv_get_synic_state(sctrl.as_uint64); sctrl.enable = 0; - wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); + hv_set_synic_state(sctrl.as_uint64); return 0; } -- cgit v1.2.3 From 7297ff0ca9db7e2d830841035b95d8b94b529142 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:57 -0700 Subject: Drivers: hv: vmbus: Define an API to retrieve virtual processor index As part of cleaning up architecture specific code, define an API to retrieve the virtual procesor index. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/mshyperv.h | 2 ++ drivers/hv/hv.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 1ea19a57cf4c..2d40bfc57e7a 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -149,6 +149,8 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) #define hv_get_synic_state(val) rdmsrl(HV_X64_MSR_SCONTROL, val) #define hv_set_synic_state(val) wrmsrl(HV_X64_MSR_SCONTROL, val) +#define hv_get_vp_index(index) rdmsrl(HV_X64_MSR_VP_INDEX, index) + void hyperv_callback_vector(void); #ifdef CONFIG_TRACING #define trace_hyperv_callback_vector hyperv_callback_vector diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 7cb036d4b243..945719026223 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -306,7 +306,7 @@ int hv_synic_init(unsigned int cpu) * of cpuid and Linux' notion of cpuid. * This array will be indexed using Linux cpuid. */ - rdmsrl(HV_X64_MSR_VP_INDEX, vp_index); + hv_get_vp_index(vp_index); hv_context.vp_index[cpu] = (u32)vp_index; /* -- cgit v1.2.3 From 37e11d5c7052a5ca55ef807731c75218ea341b4c Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:58 -0700 Subject: Drivers: hv: vmbus: Define an APIs to manage interrupt state As part of cleaning up architecture specific code, define APIs to manage interrupt state. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/mshyperv.h | 3 +++ drivers/hv/hv.c | 12 ++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 2d40bfc57e7a..42505d1158d6 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -151,6 +151,9 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) #define hv_get_vp_index(index) rdmsrl(HV_X64_MSR_VP_INDEX, index) +#define hv_get_synint_state(int_num, val) rdmsrl(int_num, val) +#define hv_set_synint_state(int_num, val) wrmsrl(int_num, val) + void hyperv_callback_vector(void); #ifdef CONFIG_TRACING #define trace_hyperv_callback_vector hyperv_callback_vector diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 945719026223..60594fa3250d 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -284,14 +284,16 @@ int hv_synic_init(unsigned int cpu) hv_set_siefp(siefp.as_uint64); /* Setup the shared SINT. */ - rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); + hv_get_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, + shared_sint.as_uint64); shared_sint.as_uint64 = 0; shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR; shared_sint.masked = false; shared_sint.auto_eoi = true; - wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); + hv_set_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, + shared_sint.as_uint64); /* Enable the global synic bit */ hv_get_synic_state(sctrl.as_uint64); @@ -384,13 +386,15 @@ int hv_synic_cleanup(unsigned int cpu) hv_ce_shutdown(hv_context.clk_evt[cpu]); } - rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); + hv_get_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, + shared_sint.as_uint64); shared_sint.masked = 1; /* Need to correctly cleanup in the case of SMP!!! */ /* Disable the interrupt */ - wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); + hv_set_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, + shared_sint.as_uint64); hv_get_simp(simp.as_uint64); simp.simp_enabled = 0; -- cgit v1.2.3 From 8e27a236312c4ab6dc8dbd303552b771d3569cf1 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 19 Jan 2017 11:51:59 -0700 Subject: Drivers: hv: vmbus: Cleanup hyperv_vmbus.h Get rid of all unused definitions. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hyperv_vmbus.h | 152 +--------------------------------------------- 1 file changed, 2 insertions(+), 150 deletions(-) diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index e9f5d2c2fb6b..2463ef93c1f6 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -41,66 +41,11 @@ #define HV_UTIL_NEGO_TIMEOUT 55 -/* Define version of the synthetic interrupt controller. */ -#define HV_SYNIC_VERSION (1) -#define HV_ANY_VP (0xFFFFFFFF) -/* Define synthetic interrupt controller flag constants. */ -#define HV_EVENT_FLAGS_COUNT (256 * 8) #define HV_EVENT_FLAGS_BYTE_COUNT (256) #define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32)) -/* Define invalid partition identifier. */ -#define HV_PARTITION_ID_INVALID ((u64)0x0) - -/* Define port type. */ -enum hv_port_type { - HVPORT_MSG = 1, - HVPORT_EVENT = 2, - HVPORT_MONITOR = 3 -}; - -/* Define port information structure. */ -struct hv_port_info { - enum hv_port_type port_type; - u32 padding; - union { - struct { - u32 target_sint; - u32 target_vp; - u64 rsvdz; - } message_port_info; - struct { - u32 target_sint; - u32 target_vp; - u16 base_flag_number; - u16 flag_count; - u32 rsvdz; - } event_port_info; - struct { - u64 monitor_address; - u64 rsvdz; - } monitor_port_info; - }; -}; - -struct hv_connection_info { - enum hv_port_type port_type; - u32 padding; - union { - struct { - u64 rsvdz; - } message_connection_info; - struct { - u64 rsvdz; - } event_connection_info; - struct { - u64 monitor_address; - } monitor_connection_info; - }; -}; - /* * Timer configuration register. */ @@ -117,8 +62,6 @@ union hv_timer_config { }; }; -/* Define the number of message buffers associated with each port. */ -#define HV_PORT_MESSAGE_BUFFER_COUNT (16) /* Define the synthetic interrupt controller event flags format. */ union hv_synic_event_flags { @@ -126,11 +69,6 @@ union hv_synic_event_flags { u32 flags32[HV_EVENT_FLAGS_DWORD_COUNT]; }; -/* Define the synthetic interrupt flags page layout. */ -struct hv_synic_event_flags_page { - union hv_synic_event_flags sintevent_flags[HV_SYNIC_SINT_COUNT]; -}; - /* Define SynIC control register. */ union hv_synic_scontrol { u64 as_uint64; @@ -232,6 +170,8 @@ struct hv_monitor_page { u8 rsvdz4[1984]; }; +#define HV_HYPERCALL_PARAM_ALIGN sizeof(u64) + /* Definition of the hv_post_message hypercall input structure. */ struct hv_input_post_message { union hv_connection_id connectionid; @@ -241,41 +181,6 @@ struct hv_input_post_message { u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; }; -/* - * Versioning definitions used for guests reporting themselves to the - * hypervisor, and visa versa. - */ - -/* Version info reported by guest OS's */ -enum hv_guest_os_vendor { - HVGUESTOS_VENDOR_MICROSOFT = 0x0001 -}; - -enum hv_guest_os_microsoft_ids { - HVGUESTOS_MICROSOFT_UNDEFINED = 0x00, - HVGUESTOS_MICROSOFT_MSDOS = 0x01, - HVGUESTOS_MICROSOFT_WINDOWS3X = 0x02, - HVGUESTOS_MICROSOFT_WINDOWS9X = 0x03, - HVGUESTOS_MICROSOFT_WINDOWSNT = 0x04, - HVGUESTOS_MICROSOFT_WINDOWSCE = 0x05 -}; - -/* - * Declare the MSR used to identify the guest OS. - */ -#define HV_X64_MSR_GUEST_OS_ID 0x40000000 - -union hv_x64_msr_guest_os_id_contents { - u64 as_uint64; - struct { - u64 build_number:16; - u64 service_version:8; /* Service Pack, etc. */ - u64 minor_version:8; - u64 major_version:8; - u64 os_id:8; /* enum hv_guest_os_microsoft_ids (if Vendor=MS) */ - u64 vendor_id:16; /* enum hv_guest_os_vendor */ - }; -}; enum { VMBUS_MESSAGE_CONNECTION_ID = 1, @@ -287,55 +192,6 @@ enum { VMBUS_MESSAGE_SINT = 2, }; -/* #defines */ - -#define HV_PRESENT_BIT 0x80000000 - - -#define HV_CPU_POWER_MANAGEMENT (1 << 0) -#define HV_RECOMMENDATIONS_MAX 4 - -#define HV_X64_MAX 5 -#define HV_CAPS_MAX 8 - - -#define HV_HYPERCALL_PARAM_ALIGN sizeof(u64) - - -/* Service definitions */ - -#define HV_SERVICE_PARENT_PORT (0) -#define HV_SERVICE_PARENT_CONNECTION (0) - -#define HV_SERVICE_CONNECT_RESPONSE_SUCCESS (0) -#define HV_SERVICE_CONNECT_RESPONSE_INVALID_PARAMETER (1) -#define HV_SERVICE_CONNECT_RESPONSE_UNKNOWN_SERVICE (2) -#define HV_SERVICE_CONNECT_RESPONSE_CONNECTION_REJECTED (3) - -#define HV_SERVICE_CONNECT_REQUEST_MESSAGE_ID (1) -#define HV_SERVICE_CONNECT_RESPONSE_MESSAGE_ID (2) -#define HV_SERVICE_DISCONNECT_REQUEST_MESSAGE_ID (3) -#define HV_SERVICE_DISCONNECT_RESPONSE_MESSAGE_ID (4) -#define HV_SERVICE_MAX_MESSAGE_ID (4) - -#define HV_SERVICE_PROTOCOL_VERSION (0x0010) -#define HV_CONNECT_PAYLOAD_BYTE_COUNT 64 - -/* #define VMBUS_REVISION_NUMBER 6 */ - -/* Our local vmbus's port and connection id. Anything >0 is fine */ -/* #define VMBUS_PORT_ID 11 */ - -/* 628180B8-308D-4c5e-B7DB-1BEB62E62EF4 */ -static const uuid_le VMBUS_SERVICE_ID = { - .b = { - 0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c, - 0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4 - }, -}; - - - struct hv_context { /* We only support running on top of Hyper-V * So at this point this really can only contain the Hyper-V ID @@ -531,10 +387,6 @@ struct hv_device *vmbus_device_create(const uuid_le *type, int vmbus_device_register(struct hv_device *child_device_obj); void vmbus_device_unregister(struct hv_device *device_obj); -/* static void */ -/* VmbusChildDeviceDestroy( */ -/* struct hv_device *); */ - struct vmbus_channel *relid2channel(u32 relid); void vmbus_free_channels(void); -- cgit v1.2.3 From 567ab5a81ba569b823b51f4af74f26c437e98b56 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 24 Jan 2017 16:54:07 +0200 Subject: extcon: palmas: Use dev_dbg macro for the debug messages User is not interested in USB cable events appearing on the console. Use dev_dbg() instead of dev_info() for these events. Signed-off-by: Roger Quadros Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-palmas.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index a128fd2eb187..ca904e8b3235 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -62,7 +62,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) { palmas_usb->linkstat = PALMAS_USB_STATE_VBUS; extcon_set_state_sync(edev, EXTCON_USB, true); - dev_info(palmas_usb->dev, "USB cable is attached\n"); + dev_dbg(palmas_usb->dev, "USB cable is attached\n"); } else { dev_dbg(palmas_usb->dev, "Spurious connect event detected\n"); @@ -71,7 +71,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) { palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_state_sync(edev, EXTCON_USB, false); - dev_info(palmas_usb->dev, "USB cable is detached\n"); + dev_dbg(palmas_usb->dev, "USB cable is detached\n"); } else { dev_dbg(palmas_usb->dev, "Spurious disconnect event detected\n"); @@ -99,7 +99,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); palmas_usb->linkstat = PALMAS_USB_STATE_ID; extcon_set_state_sync(edev, EXTCON_USB_HOST, true); - dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); + dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n"); } else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) && (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, @@ -107,17 +107,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_state_sync(edev, EXTCON_USB_HOST, false); - dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); + dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n"); } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) && (!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) { palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_state_sync(edev, EXTCON_USB_HOST, false); - dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); + dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n"); } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) && (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { palmas_usb->linkstat = PALMAS_USB_STATE_ID; extcon_set_state_sync(edev, EXTCON_USB_HOST, true); - dev_info(palmas_usb->dev, " USB-HOST cable is attached\n"); + dev_dbg(palmas_usb->dev, " USB-HOST cable is attached\n"); } return IRQ_HANDLED; @@ -138,10 +138,10 @@ static void palmas_gpio_id_detect(struct work_struct *work) if (id) { extcon_set_state_sync(edev, EXTCON_USB_HOST, false); - dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); + dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n"); } else { extcon_set_state_sync(edev, EXTCON_USB_HOST, true); - dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); + dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n"); } } -- cgit v1.2.3 From 3c84dc7e79f5bd676599a79a6bfed4d30a38ae30 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 23 Jan 2017 12:53:36 +0100 Subject: char: xilinx_hwicap: Remove pointless local variables Remove unused variables which generates these warnings: [linux-4.10-rc5/drivers/char/xilinx_hwicap/buffer_icap.c:301]: (style) Variable 'num_writes' is modified but its new value is never used. [linux-4.10-rc5/drivers/char/xilinx_hwicap/buffer_icap.c:356]: (style) Variable 'read_count' is modified but its new value is never used. Reported-by: David Binderman Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/char/xilinx_hwicap/buffer_icap.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/char/xilinx_hwicap/buffer_icap.c b/drivers/char/xilinx_hwicap/buffer_icap.c index 53c3882e4981..35981cae1afa 100644 --- a/drivers/char/xilinx_hwicap/buffer_icap.c +++ b/drivers/char/xilinx_hwicap/buffer_icap.c @@ -269,7 +269,6 @@ int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data, { int status; s32 buffer_count = 0; - s32 num_writes = 0; bool dirty = false; u32 i; void __iomem *base_address = drvdata->base_address; @@ -298,7 +297,6 @@ int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data, } buffer_count = 0; - num_writes++; dirty = false; } @@ -328,7 +326,6 @@ int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data, { int status; s32 buffer_count = 0; - s32 read_count = 0; u32 i; void __iomem *base_address = drvdata->base_address; @@ -353,7 +350,6 @@ int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data, } buffer_count = 0; - read_count++; } /* Copy data from bram */ -- cgit v1.2.3 From acf50ec773703f7043944aaf957b625a82fc9ce0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 24 Jan 2017 14:38:38 +0000 Subject: eeprom: fix memory leak on buf when failed allocation of csraddr_str The error return path When csraddr_str fails to free buf, causing a memory leak. Fix this by returning via the free_buf label that performs the necessary cleanup. Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/idt_89hpesx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 25d47d09e1cb..56bc5c2dfe60 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -965,8 +965,10 @@ static ssize_t idt_dbgfs_csr_write(struct file *filep, const char __user *ubuf, csraddr_len = colon_ch - buf; csraddr_str = kmalloc(sizeof(char)*(csraddr_len + 1), GFP_KERNEL); - if (csraddr_str == NULL) - return -ENOMEM; + if (csraddr_str == NULL) { + ret = -ENOMEM; + goto free_buf; + } /* Copy the register address to the substring buffer */ strncpy(csraddr_str, buf, csraddr_len); csraddr_str[csraddr_len] = '\0'; -- cgit v1.2.3 From 8ade6039b8404a28a7f76eac37a92c464b69499a Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 20 Jan 2017 21:57:57 +0100 Subject: eeprom: fix platform_no_drv_owner.cocci warnings No need to set .owner here. The core will do it. Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Serge Semin Signed-off-by: Julia Lawall Signed-off-by: Fengguang Wu Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/idt_89hpesx.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 56bc5c2dfe60..4db0d2cb08c6 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1554,7 +1554,6 @@ MODULE_DEVICE_TABLE(i2c, idt_ids); static struct i2c_driver idt_driver = { .driver = { .name = IDT_NAME, - .owner = THIS_MODULE, }, .probe = idt_probe, .remove = idt_remove, -- cgit v1.2.3 From 9ba60573638e2006170ebcc5489fb1e068afbc8f Mon Sep 17 00:00:00 2001 From: Juerg Haefliger Date: Thu, 19 Jan 2017 11:40:13 +0100 Subject: lkdtm: Fix Oops when unloading the module No jprobe is registered when the module is loaded without specifying a crashpoint that uses a jprobe. At the moment, we unconditionally try to unregister the jprobe on module unload which results in an Oops. Add a check to fix this. Signed-off-by: Juerg Haefliger Acked-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lkdtm_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c index 7eeb71a75549..4d44084071d8 100644 --- a/drivers/misc/lkdtm_core.c +++ b/drivers/misc/lkdtm_core.c @@ -535,7 +535,9 @@ static void __exit lkdtm_module_exit(void) /* Handle test-specific clean-up. */ lkdtm_usercopy_exit(); - unregister_jprobe(lkdtm_jprobe); + if (lkdtm_jprobe != NULL) + unregister_jprobe(lkdtm_jprobe); + pr_info("Crash point unregistered\n"); } -- cgit v1.2.3 From 9797484ba83d68f18fe1cbd964b7cd830f78f0f7 Mon Sep 17 00:00:00 2001 From: Stefano Babic Date: Fri, 20 Jan 2017 10:38:20 -0500 Subject: VME: restore bus_remove function causing incomplete module unload Commit 050c3d52cc7810d9d17b8cd231708609af6876ae ("vme: make core vme support explicitly non-modular") dropped the remove function because it appeared as if it was for removal of the bus, which is not supported. However, vme_bus_remove() is called when a VME device is removed from the bus and not when the bus is removed; as it calls the VME device driver's cleanup function. Without this function, the remove() in the VME device driver is never called and VME device drivers cannot be reloaded again. Here we restore the remove function that was deleted in that commit, and the reference to the function in the bus structure. Fixes: 050c3d52cc78 ("vme: make core vme support explicitly non-modular") Cc: Manohar Vanga Acked-by: Martyn Welch Cc: devel@driverdev.osuosl.org Signed-off-by: Stefano Babic Signed-off-by: Paul Gortmaker Cc: stable # 4.9 Signed-off-by: Greg Kroah-Hartman --- drivers/vme/vme.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c index bdbadaa47ef3..0035cf79760a 100644 --- a/drivers/vme/vme.c +++ b/drivers/vme/vme.c @@ -1625,10 +1625,25 @@ static int vme_bus_probe(struct device *dev) return retval; } +static int vme_bus_remove(struct device *dev) +{ + int retval = -ENODEV; + struct vme_driver *driver; + struct vme_dev *vdev = dev_to_vme_dev(dev); + + driver = dev->platform_data; + + if (driver->remove != NULL) + retval = driver->remove(vdev); + + return retval; +} + struct bus_type vme_bus_type = { .name = "vme", .match = vme_bus_match, .probe = vme_bus_probe, + .remove = vme_bus_remove, }; EXPORT_SYMBOL(vme_bus_type); -- cgit v1.2.3 From f09444639099584bc4784dfcd85ada67c6f33e0f Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 23 Jan 2017 10:41:22 -0700 Subject: coresight: fix kernel panic caused by invalid CPU Commit d52c9750f150 ("coresight: reset "enable_sink" flag when need be") caused a kernel panic because of the using of an invalid value: after 'for_each_cpu(cpu, mask)', value of local variable 'cpu' become invalid, causes following 'cpu_to_node' access invalid memory area. This patch brings the deleted 'cpu = cpumask_first(mask)' back. Panic log: $ perf record -e cs_etm// ls Unable to handle kernel paging request at virtual address fffe801804af4f10 pgd = ffff8017ce031600 [fffe801804af4f10] *pgd=0000000000000000, *pud=0000000000000000 Internal error: Oops: 96000004 [#1] SMP Modules linked in: CPU: 33 PID: 1619 Comm: perf Not tainted 4.7.1+ #16 Hardware name: Huawei Taishan 2280 /CH05TEVBA, BIOS 1.10 11/24/2016 task: ffff8017cb0c8400 ti: ffff8017cb154000 task.ti: ffff8017cb154000 PC is at tmc_alloc_etf_buffer+0x60/0xd4 LR is at tmc_alloc_etf_buffer+0x44/0xd4 pc : [] lr : [] pstate: 60000145 sp : ffff8017cb157b40 x29: ffff8017cb157b40 x28: 0000000000000000 ...skip... 7a60: ffff000008c64dc8 0000000000000006 0000000000000253 ffffffffffffffff 7a80: 0000000000000000 0000000000000000 ffff0000080872cc 0000000000000001 [] tmc_alloc_etf_buffer+0x60/0xd4 [] etm_setup_aux+0x1dc/0x1e8 [] rb_alloc_aux+0x2b0/0x338 [] perf_mmap+0x414/0x568 [] mmap_region+0x324/0x544 [] do_mmap+0x334/0x3e0 [] vm_mmap_pgoff+0xa4/0xc8 [] SyS_mmap_pgoff+0xb0/0x22c [] sys_mmap+0x18/0x28 [] el0_svc_naked+0x24/0x28 Code: 912040a5 d0001c00 f873d821 911c6000 (b8656822) ---[ end trace 98933da8f92b0c9a ]--- Signed-off-by: Wang Nan Cc: Xia Kaixu Cc: Li Zefan Cc: Mathieu Poirier Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Fixes: d52c9750f150 ("coresight: reset "enable_sink" flag when need be") Signed-off-by: Mathieu Poirier Cc: stable # 4.10 Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm-perf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 17741969026e..26cfac3e6de7 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -242,6 +242,7 @@ static void *etm_setup_aux(int event_cpu, void **pages, if (!sink_ops(sink)->alloc_buffer) goto err; + cpu = cpumask_first(mask); /* Get the AUX specific data from the sink buffer */ event_data->snk_config = sink_ops(sink)->alloc_buffer(sink, cpu, pages, -- cgit v1.2.3 From ae3fabcda00de54029b5342a3d6d82d65e7c4e6b Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Mon, 23 Jan 2017 10:41:23 -0700 Subject: coresight: etm4x: Fix enabling of cycle accurate tracing in perf. Using perf record 'cyclacc' option in cs_etm event was not setting up cycle accurate trace correctly. Corrects bit set in TRCCONFIGR to enable cycle accurate trace. Programs TRCCCCTLR with a valid threshold value as required by ETMv4 spec. Signed-off-by: Mike Leach Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 7 +++++-- drivers/hwtracing/coresight/coresight-etm4x.h | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 031480f2c34d..f432febdda49 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -216,8 +216,11 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, goto out; /* Go from generic option to ETMv4 specifics */ - if (attr->config & BIT(ETM_OPT_CYCACC)) - config->cfg |= ETMv4_MODE_CYCACC; + if (attr->config & BIT(ETM_OPT_CYCACC)) { + config->cfg |= BIT(4); + /* TRM: Must program this for cycacc to work */ + config->ccctlr = ETM_CYC_THRESHOLD_DEFAULT; + } if (attr->config & BIT(ETM_OPT_TS)) config->cfg |= ETMv4_MODE_TIMESTAMP; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index ba8d3f86de21..b3b5ea7b7fb3 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -146,6 +146,7 @@ #define ETM_ARCH_V4 0x40 #define ETMv4_SYNC_MASK 0x1F #define ETM_CYC_THRESHOLD_MASK 0xFFF +#define ETM_CYC_THRESHOLD_DEFAULT 0x100 #define ETMv4_EVENT_MASK 0xFF #define ETM_CNTR_MAX_VAL 0xFFFF #define ETM_TRACEID_MASK 0x3f -- cgit v1.2.3 From 27a7e2a72c02785156ca3876bd7ca57f56a433ff Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Mon, 23 Jan 2017 10:41:24 -0700 Subject: coresight: etm4x: Fix timestamp configuration when working from perf When using the ETM4x tracers from the perf interface two trace options are available: cycle accurate and timestamp. Enabling the timestamp feature is done by setting TRCCONFIGR.TS (bit 11). The position of the timestamp bit in that register coincidentally happens to be the same as what was chosen to enable timestamping from the 'mode' sysFS entry. The code does the right thing but the semantic is wrong. This patch sets TRCCONFIGR.TS explicitly, as it is done from the sysFS interface. That way timestamps are set the same way from both perf and sysFS and there is no misunderstanding as to what is intended. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index f432febdda49..d1340fb4e457 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -222,7 +222,8 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, config->ccctlr = ETM_CYC_THRESHOLD_DEFAULT; } if (attr->config & BIT(ETM_OPT_TS)) - config->cfg |= ETMv4_MODE_TIMESTAMP; + /* bit[11], Global timestamp tracing bit */ + config->cfg |= BIT(11); out: return ret; -- cgit v1.2.3 From 8333eb153a9fd86028abd0942c6aec0705764410 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 9 Jan 2017 01:13:00 +0100 Subject: w1: omap_hdq: Free resources on error path In case of error returned by '_omap_hdq_reset()', free resources as done elsewhere in this function. This patch slighly changes the semantic of the code. It now propagates the error code returned by '_omap_hdq_reset()' instead of returning -EINVAL unconditionally. Signed-off-by: Christophe JAILLET Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/omap_hdq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index bb09de633939..fb190c259607 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -715,7 +715,7 @@ static int omap_hdq_probe(struct platform_device *pdev) ret = _omap_hdq_reset(hdq_data); if (ret) { dev_dbg(&pdev->dev, "reset failed\n"); - return -EINVAL; + goto err_irq; } rev = hdq_reg_in(hdq_data, OMAP_HDQ_REVISION); -- cgit v1.2.3 From 61cd1b4cd1e8f7f7642ab64529d9bd52e8374641 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Wed, 18 Jan 2017 21:31:11 +0100 Subject: w1: ds2490: USB transfer buffers need to be DMAable ds2490 driver was doing USB transfers from / to buffers on a stack. This is not permitted and made the driver non-working with vmapped stacks. Since all these transfers are done under the same bus_mutex lock we can simply use shared buffers in a device private structure for two most common of them. While we are at it, let's also fix a comparison between int and size_t in ds9490r_search() which made the driver spin in this function if state register get requests were failing. Signed-off-by: Maciej S. Szmigiero Cc: stable Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/ds2490.c | 142 ++++++++++++++++++++++++++------------------ 1 file changed, 84 insertions(+), 58 deletions(-) diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index 049a884a756f..59d74d1b47a8 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -153,6 +153,9 @@ struct ds_device */ u16 spu_bit; + u8 st_buf[ST_SIZE]; + u8 byte_buf; + struct w1_bus_master master; }; @@ -174,7 +177,6 @@ struct ds_status u8 data_in_buffer_status; u8 reserved1; u8 reserved2; - }; static struct usb_device_id ds_id_table [] = { @@ -244,28 +246,6 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index) return err; } -static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st, - unsigned char *buf, int size) -{ - int count, err; - - memset(st, 0, sizeof(*st)); - - count = 0; - err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev, - dev->ep[EP_STATUS]), buf, size, &count, 1000); - if (err < 0) { - pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n", - dev->ep[EP_STATUS], err); - return err; - } - - if (count >= sizeof(*st)) - memcpy(st, buf, sizeof(*st)); - - return count; -} - static inline void ds_print_msg(unsigned char *buf, unsigned char *str, int off) { pr_info("%45s: %8x\n", str, buf[off]); @@ -324,6 +304,35 @@ static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count) } } +static int ds_recv_status(struct ds_device *dev, struct ds_status *st, + bool dump) +{ + int count, err; + + if (st) + memset(st, 0, sizeof(*st)); + + count = 0; + err = usb_interrupt_msg(dev->udev, + usb_rcvintpipe(dev->udev, + dev->ep[EP_STATUS]), + dev->st_buf, sizeof(dev->st_buf), + &count, 1000); + if (err < 0) { + pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n", + dev->ep[EP_STATUS], err); + return err; + } + + if (dump) + ds_dump_status(dev, dev->st_buf, count); + + if (st && count >= sizeof(*st)) + memcpy(st, dev->st_buf, sizeof(*st)); + + return count; +} + static void ds_reset_device(struct ds_device *dev) { ds_send_control_cmd(dev, CTL_RESET_DEVICE, 0); @@ -344,7 +353,6 @@ static void ds_reset_device(struct ds_device *dev) static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) { int count, err; - struct ds_status st; /* Careful on size. If size is less than what is available in * the input buffer, the device fails the bulk transfer and @@ -359,14 +367,9 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]), buf, size, &count, 1000); if (err < 0) { - u8 buf[ST_SIZE]; - int count; - pr_info("Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]); usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN])); - - count = ds_recv_status_nodump(dev, &st, buf, sizeof(buf)); - ds_dump_status(dev, buf, count); + ds_recv_status(dev, NULL, true); return err; } @@ -404,7 +407,6 @@ int ds_stop_pulse(struct ds_device *dev, int limit) { struct ds_status st; int count = 0, err = 0; - u8 buf[ST_SIZE]; do { err = ds_send_control(dev, CTL_HALT_EXE_IDLE, 0); @@ -413,7 +415,7 @@ int ds_stop_pulse(struct ds_device *dev, int limit) err = ds_send_control(dev, CTL_RESUME_EXE, 0); if (err) break; - err = ds_recv_status_nodump(dev, &st, buf, sizeof(buf)); + err = ds_recv_status(dev, &st, false); if (err) break; @@ -456,18 +458,17 @@ int ds_detect(struct ds_device *dev, struct ds_status *st) static int ds_wait_status(struct ds_device *dev, struct ds_status *st) { - u8 buf[ST_SIZE]; int err, count = 0; do { st->status = 0; - err = ds_recv_status_nodump(dev, st, buf, sizeof(buf)); + err = ds_recv_status(dev, st, false); #if 0 if (err >= 0) { int i; printk("0x%x: count=%d, status: ", dev->ep[EP_STATUS], err); for (i=0; ist_buf[i]); printk("\n"); } #endif @@ -485,7 +486,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st) * can do something with it). */ if (err > 16 || count >= 100 || err < 0) - ds_dump_status(dev, buf, err); + ds_dump_status(dev, dev->st_buf, err); /* Extended data isn't an error. Well, a short is, but the dump * would have already told the user that and we can't do anything @@ -608,7 +609,6 @@ static int ds_write_byte(struct ds_device *dev, u8 byte) { int err; struct ds_status st; - u8 rbyte; err = ds_send_control(dev, COMM_BYTE_IO | COMM_IM | dev->spu_bit, byte); if (err) @@ -621,11 +621,11 @@ static int ds_write_byte(struct ds_device *dev, u8 byte) if (err) return err; - err = ds_recv_data(dev, &rbyte, sizeof(rbyte)); + err = ds_recv_data(dev, &dev->byte_buf, 1); if (err < 0) return err; - return !(byte == rbyte); + return !(byte == dev->byte_buf); } static int ds_read_byte(struct ds_device *dev, u8 *byte) @@ -712,7 +712,6 @@ static void ds9490r_search(void *data, struct w1_master *master, int err; u16 value, index; struct ds_status st; - u8 st_buf[ST_SIZE]; int search_limit; int found = 0; int i; @@ -724,7 +723,12 @@ static void ds9490r_search(void *data, struct w1_master *master, /* FIFO 128 bytes, bulk packet size 64, read a multiple of the * packet size. */ - u64 buf[2*64/8]; + const size_t bufsize = 2 * 64; + u64 *buf; + + buf = kmalloc(bufsize, GFP_KERNEL); + if (!buf) + return; mutex_lock(&master->bus_mutex); @@ -745,10 +749,9 @@ static void ds9490r_search(void *data, struct w1_master *master, do { schedule_timeout(jtime); - if (ds_recv_status_nodump(dev, &st, st_buf, sizeof(st_buf)) < - sizeof(st)) { + err = ds_recv_status(dev, &st, false); + if (err < 0 || err < sizeof(st)) break; - } if (st.data_in_buffer_status) { /* Bulk in can receive partial ids, but when it does @@ -758,7 +761,7 @@ static void ds9490r_search(void *data, struct w1_master *master, * bulk without first checking if status says there * is data to read. */ - err = ds_recv_data(dev, (u8 *)buf, sizeof(buf)); + err = ds_recv_data(dev, (u8 *)buf, bufsize); if (err < 0) break; for (i = 0; i < err/8; ++i) { @@ -794,9 +797,14 @@ static void ds9490r_search(void *data, struct w1_master *master, } search_out: mutex_unlock(&master->bus_mutex); + kfree(buf); } #if 0 +/* + * FIXME: if this disabled code is ever used in the future all ds_send_data() + * calls must be changed to use a DMAable buffer. + */ static int ds_match_access(struct ds_device *dev, u64 init) { int err; @@ -845,13 +853,12 @@ static int ds_set_path(struct ds_device *dev, u64 init) static u8 ds9490r_touch_bit(void *data, u8 bit) { - u8 ret; struct ds_device *dev = data; - if (ds_touch_bit(dev, bit, &ret)) + if (ds_touch_bit(dev, bit, &dev->byte_buf)) return 0; - return ret; + return dev->byte_buf; } #if 0 @@ -866,13 +873,12 @@ static u8 ds9490r_read_bit(void *data) { struct ds_device *dev = data; int err; - u8 bit = 0; - err = ds_touch_bit(dev, 1, &bit); + err = ds_touch_bit(dev, 1, &dev->byte_buf); if (err) return 0; - return bit & 1; + return dev->byte_buf & 1; } #endif @@ -887,32 +893,52 @@ static u8 ds9490r_read_byte(void *data) { struct ds_device *dev = data; int err; - u8 byte = 0; - err = ds_read_byte(dev, &byte); + err = ds_read_byte(dev, &dev->byte_buf); if (err) return 0; - return byte; + return dev->byte_buf; } static void ds9490r_write_block(void *data, const u8 *buf, int len) { struct ds_device *dev = data; + u8 *tbuf; + + if (len <= 0) + return; + + tbuf = kmalloc(len, GFP_KERNEL); + if (!tbuf) + return; - ds_write_block(dev, (u8 *)buf, len); + memcpy(tbuf, buf, len); + ds_write_block(dev, tbuf, len); + + kfree(tbuf); } static u8 ds9490r_read_block(void *data, u8 *buf, int len) { struct ds_device *dev = data; int err; + u8 *tbuf; - err = ds_read_block(dev, buf, len); - if (err < 0) + if (len <= 0) + return 0; + + tbuf = kmalloc(len, GFP_KERNEL); + if (!tbuf) return 0; - return len; + err = ds_read_block(dev, tbuf, len); + if (err >= 0) + memcpy(buf, tbuf, len); + + kfree(tbuf); + + return err >= 0 ? len : 0; } static u8 ds9490r_reset(void *data) -- cgit v1.2.3 From dd6478d68b16ce3d165b9d0e4ac0c021923e0f5a Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Thu, 19 Jan 2017 21:26:51 +0100 Subject: w1: add DS2405 addressable switch driver This adds a driver for a DS2405 1-wire single-channel addressable switch. The DS2405 can also work as a single-channel binary remote sensor. This driver supports two attributes: "state" and "output" which are the same attribute names as supported by existing DS2406, DS2408 and DS2413 drivers. Signed-off-by: Maciej S. Szmigiero Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/Kconfig | 8 ++ drivers/w1/slaves/Makefile | 1 + drivers/w1/slaves/w1_ds2405.c | 238 ++++++++++++++++++++++++++++++++++++++++++ drivers/w1/w1_family.h | 1 + drivers/w1/w1_io.c | 1 + 5 files changed, 249 insertions(+) create mode 100644 drivers/w1/slaves/w1_ds2405.c diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index cfe74d09932e..0ef9f2663dbd 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -16,6 +16,14 @@ config W1_SLAVE_SMEM Say Y here if you want to connect 1-wire simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire. +config W1_SLAVE_DS2405 + tristate "DS2405 Addressable Switch" + help + Say Y or M here if you want to use a DS2405 1-wire + single-channel addressable switch. + This device can also work as a single-channel + binary remote sensor. + config W1_SLAVE_DS2408 tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)" help diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 1e9989afe7bf..b4a358955ef9 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o +obj-$(CONFIG_W1_SLAVE_DS2405) += w1_ds2405.o obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o obj-$(CONFIG_W1_SLAVE_DS2413) += w1_ds2413.o obj-$(CONFIG_W1_SLAVE_DS2406) += w1_ds2406.o diff --git a/drivers/w1/slaves/w1_ds2405.c b/drivers/w1/slaves/w1_ds2405.c new file mode 100644 index 000000000000..1f350cf2b6ec --- /dev/null +++ b/drivers/w1/slaves/w1_ds2405.c @@ -0,0 +1,238 @@ +/* + * w1_ds2405.c + * + * Copyright (c) 2017 Maciej S. Szmigiero + * Based on w1_therm.c copyright (c) 2004 Evgeniy Polyakov + * + * + * This program is free software; you can redistribute it and/or modify + * it under the therms 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 +#include +#include +#include +#include +#include +#include + +#include "../w1.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maciej S. Szmigiero "); +MODULE_DESCRIPTION("Driver for 1-wire Dallas DS2405 PIO."); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2405)); + +static int w1_ds2405_select(struct w1_slave *sl, bool only_active) +{ + struct w1_master *dev = sl->master; + + u64 dev_addr = le64_to_cpu(*(u64 *)&sl->reg_num); + unsigned int bit_ctr; + + if (w1_reset_bus(dev) != 0) + return 0; + + /* + * We cannot use a normal Match ROM command + * since doing so would toggle PIO state + */ + w1_write_8(dev, only_active ? W1_ALARM_SEARCH : W1_SEARCH); + + for (bit_ctr = 0; bit_ctr < 64; bit_ctr++) { + int bit2send = !!(dev_addr & BIT(bit_ctr)); + u8 ret; + + ret = w1_triplet(dev, bit2send); + + if ((ret & (BIT(0) | BIT(1))) == + (BIT(0) | BIT(1))) /* no devices found */ + return 0; + + if (!!(ret & BIT(2)) != bit2send) + /* wrong direction taken - no such device */ + return 0; + } + + return 1; +} + +static int w1_ds2405_read_pio(struct w1_slave *sl) +{ + if (w1_ds2405_select(sl, true)) + return 0; /* "active" means PIO is low */ + + if (w1_ds2405_select(sl, false)) + return 1; + + return -ENODEV; +} + +static ssize_t state_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct w1_slave *sl = dev_to_w1_slave(device); + struct w1_master *dev = sl->master; + + int ret; + ssize_t f_retval; + u8 state; + + ret = mutex_lock_interruptible(&dev->bus_mutex); + if (ret) + return ret; + + if (!w1_ds2405_select(sl, false)) { + f_retval = -ENODEV; + goto out_unlock; + } + + state = w1_read_8(dev); + if (state != 0 && + state != 0xff) { + dev_err(device, "non-consistent state %x\n", state); + f_retval = -EIO; + goto out_unlock; + } + + *buf = state ? '1' : '0'; + f_retval = 1; + +out_unlock: + w1_reset_bus(dev); + mutex_unlock(&dev->bus_mutex); + + return f_retval; +} + +static ssize_t output_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct w1_slave *sl = dev_to_w1_slave(device); + struct w1_master *dev = sl->master; + + int ret; + ssize_t f_retval; + + ret = mutex_lock_interruptible(&dev->bus_mutex); + if (ret) + return ret; + + ret = w1_ds2405_read_pio(sl); + if (ret < 0) { + f_retval = ret; + goto out_unlock; + } + + *buf = ret ? '1' : '0'; + f_retval = 1; + +out_unlock: + w1_reset_bus(dev); + mutex_unlock(&dev->bus_mutex); + + return f_retval; +} + +static ssize_t output_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct w1_slave *sl = dev_to_w1_slave(device); + struct w1_master *dev = sl->master; + + int ret, current_pio; + unsigned int val; + ssize_t f_retval; + + if (count < 1) + return -EINVAL; + + if (sscanf(buf, " %u%n", &val, &ret) < 1) + return -EINVAL; + + if (val != 0 && val != 1) + return -EINVAL; + + f_retval = ret; + + ret = mutex_lock_interruptible(&dev->bus_mutex); + if (ret) + return ret; + + current_pio = w1_ds2405_read_pio(sl); + if (current_pio < 0) { + f_retval = current_pio; + goto out_unlock; + } + + if (current_pio == val) + goto out_unlock; + + if (w1_reset_bus(dev) != 0) { + f_retval = -ENODEV; + goto out_unlock; + } + + /* + * can't use w1_reset_select_slave() here since it uses Skip ROM if + * there is only one device on bus + */ + do { + u64 dev_addr = le64_to_cpu(*(u64 *)&sl->reg_num); + u8 cmd[9]; + + cmd[0] = W1_MATCH_ROM; + memcpy(&cmd[1], &dev_addr, sizeof(dev_addr)); + + w1_write_block(dev, cmd, sizeof(cmd)); + } while (0); + +out_unlock: + w1_reset_bus(dev); + mutex_unlock(&dev->bus_mutex); + + return f_retval; +} + +static DEVICE_ATTR_RO(state); +static DEVICE_ATTR_RW(output); + +static struct attribute *w1_ds2405_attrs[] = { + &dev_attr_state.attr, + &dev_attr_output.attr, + NULL +}; + +ATTRIBUTE_GROUPS(w1_ds2405); + +static struct w1_family_ops w1_ds2405_fops = { + .groups = w1_ds2405_groups +}; + +static struct w1_family w1_family_ds2405 = { + .fid = W1_FAMILY_DS2405, + .fops = &w1_ds2405_fops +}; + +static int __init w1_ds2405_init(void) +{ + return w1_register_family(&w1_family_ds2405); +} + +static void __exit w1_ds2405_fini(void) +{ + w1_unregister_family(&w1_family_ds2405); +} + +module_init(w1_ds2405_init); +module_exit(w1_ds2405_fini); diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index 10a7a0767187..66cb0d2fe049 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -30,6 +30,7 @@ #define W1_FAMILY_BQ27000 0x01 #define W1_FAMILY_SMEM_01 0x01 #define W1_FAMILY_SMEM_81 0x81 +#define W1_FAMILY_DS2405 0x05 #define W1_THERM_DS18S20 0x10 #define W1_FAMILY_DS28E04 0x1C #define W1_COUNTER_DS2423 0x1D diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index f4bc8c100a01..a209869eff76 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -233,6 +233,7 @@ u8 w1_triplet(struct w1_master *dev, int bdir) return retval; } } +EXPORT_SYMBOL_GPL(w1_triplet); /** * w1_read_8() - Reads 8 bits. -- cgit v1.2.3 From d2ce4ea1a0b0162e5d2d7e7942ab6f5cc2063d5a Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 21 Jan 2017 23:50:18 +0100 Subject: w1: don't leak refcount on slave attach failure in w1_attach_slave_device() Near the beginning of w1_attach_slave_device() we increment a w1 master reference count. Later, when we are going to exit this function without actually attaching a slave device (due to failure of __w1_attach_slave_device()) we need to decrement this reference count back. Signed-off-by: Maciej S. Szmigiero Cc: stable@vger.kernel.org Fixes: 9fcbbac5ded489 ("w1: process w1 netlink commands in w1_process thread") Cc: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/w1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index e213c678bbfe..ab0931e7a9bb 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -763,6 +763,7 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) dev_err(&dev->dev, "%s: Attaching %s failed.\n", __func__, sl->name); w1_family_put(sl->family); + atomic_dec(&sl->master->refcnt); kfree(sl); return err; } -- cgit v1.2.3 From 8a0934bf9b2c5d4b6ead0bf6f3f3a21c540053c1 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Thu, 5 Jan 2017 14:37:46 -0600 Subject: w1: Fixup source file headers Remove filename from file, this is not done anymore as it doesn't add anything and usually is incorrect as filename change often. Also shorten the GPL to the more common address-less version and remove excess white-space. Signed-off-by: Andrew F. Davis Signed-off-by: Greg Kroah-Hartman --- drivers/w1/w1.c | 7 ------- drivers/w1/w1.h | 7 ------- drivers/w1/w1_family.c | 7 ------- drivers/w1/w1_family.h | 7 ------- drivers/w1/w1_int.c | 7 ------- drivers/w1/w1_int.h | 7 ------- drivers/w1/w1_io.c | 7 ------- drivers/w1/w1_log.h | 7 ------- drivers/w1/w1_netlink.c | 7 ------- drivers/w1/w1_netlink.h | 7 ------- 10 files changed, 70 deletions(-) diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index ab0931e7a9bb..90a3d9338fd2 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -1,9 +1,6 @@ /* - * w1.c - * * Copyright (c) 2004 Evgeniy Polyakov * - * * 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 @@ -13,10 +10,6 @@ * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index 129895f562b0..758a7a6322e9 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -1,9 +1,6 @@ /* - * w1.h - * * Copyright (c) 2004 Evgeniy Polyakov * - * * 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 @@ -13,10 +10,6 @@ * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __W1_H diff --git a/drivers/w1/w1_family.c b/drivers/w1/w1_family.c index 1dc3051f7d76..df1c9bb90eb5 100644 --- a/drivers/w1/w1_family.c +++ b/drivers/w1/w1_family.c @@ -1,9 +1,6 @@ /* - * w1_family.c - * * Copyright (c) 2004 Evgeniy Polyakov * - * * 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 @@ -13,10 +10,6 @@ * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index 66cb0d2fe049..c4a6b257a367 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -1,9 +1,6 @@ /* - * w1_family.h - * * Copyright (c) 2004 Evgeniy Polyakov * - * * 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 @@ -13,10 +10,6 @@ * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __W1_FAMILY_H diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c index 20f766afa4c7..4ce1b66d5092 100644 --- a/drivers/w1/w1_int.c +++ b/drivers/w1/w1_int.c @@ -1,9 +1,6 @@ /* - * w1_int.c - * * Copyright (c) 2004 Evgeniy Polyakov * - * * 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 @@ -13,10 +10,6 @@ * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/w1/w1_int.h b/drivers/w1/w1_int.h index 2ad7d4414bed..371989159216 100644 --- a/drivers/w1/w1_int.h +++ b/drivers/w1/w1_int.h @@ -1,9 +1,6 @@ /* - * w1_int.h - * * Copyright (c) 2004 Evgeniy Polyakov * - * * 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 @@ -13,10 +10,6 @@ * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __W1_INT_H diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index a209869eff76..de8bebc27896 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -1,9 +1,6 @@ /* - * w1_io.c - * * Copyright (c) 2004 Evgeniy Polyakov * - * * 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 @@ -13,10 +10,6 @@ * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/w1/w1_log.h b/drivers/w1/w1_log.h index f9eecff23b8d..dd1422b6afbb 100644 --- a/drivers/w1/w1_log.h +++ b/drivers/w1/w1_log.h @@ -1,9 +1,6 @@ /* - * w1_log.h - * * Copyright (c) 2004 Evgeniy Polyakov * - * * 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 @@ -13,10 +10,6 @@ * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __W1_LOG_H diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c index 881597a191b8..49e520ca79c5 100644 --- a/drivers/w1/w1_netlink.c +++ b/drivers/w1/w1_netlink.c @@ -1,9 +1,6 @@ /* - * w1_netlink.c - * * Copyright (c) 2003 Evgeniy Polyakov * - * * 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 @@ -13,10 +10,6 @@ * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/w1/w1_netlink.h b/drivers/w1/w1_netlink.h index c99a9ce05e62..b389e5ff5fa5 100644 --- a/drivers/w1/w1_netlink.h +++ b/drivers/w1/w1_netlink.h @@ -1,9 +1,6 @@ /* - * w1_netlink.h - * * Copyright (c) 2003 Evgeniy Polyakov * - * * 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 @@ -13,10 +10,6 @@ * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __W1_NETLINK_H -- cgit v1.2.3 From cdd1737cba7a6453c9453c55c4537a15cc1f49b2 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Thu, 12 Jan 2017 14:52:18 -0600 Subject: misc: sram: Split sram data structures into local header In preparation of a coming file split of the sram driver, move the common data structures into a local header file that can be shared between files related to the sram driver. Signed-off-by: Dave Gerlach Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram.c | 30 ++---------------------------- drivers/misc/sram.h | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 28 deletions(-) create mode 100644 drivers/misc/sram.h diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 07ec2a8a9343..5a6e001845c2 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -31,35 +31,9 @@ #include #include -#define SRAM_GRANULARITY 32 - -struct sram_partition { - void __iomem *base; - - struct gen_pool *pool; - struct bin_attribute battr; - struct mutex lock; -}; - -struct sram_dev { - struct device *dev; - void __iomem *virt_base; - - struct gen_pool *pool; - struct clk *clk; +#include "sram.h" - struct sram_partition *partition; - u32 partitions; -}; - -struct sram_reserve { - struct list_head list; - u32 start; - u32 size; - bool export; - bool pool; - const char *label; -}; +#define SRAM_GRANULARITY 32 static ssize_t sram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, diff --git a/drivers/misc/sram.h b/drivers/misc/sram.h new file mode 100644 index 000000000000..52501a84468c --- /dev/null +++ b/drivers/misc/sram.h @@ -0,0 +1,39 @@ +/* + * Defines for the SRAM driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __SRAM_H +#define __SRAM_H + +struct sram_partition { + void __iomem *base; + + struct gen_pool *pool; + struct bin_attribute battr; + struct mutex lock; + struct list_head list; +}; + +struct sram_dev { + struct device *dev; + void __iomem *virt_base; + + struct gen_pool *pool; + struct clk *clk; + + struct sram_partition *partition; + u32 partitions; +}; + +struct sram_reserve { + struct list_head list; + u32 start; + u32 size; + bool export; + bool pool; + const char *label; +}; +#endif /* __SRAM_H */ -- cgit v1.2.3 From 728bbe75c82fdc6bdf157652a4351856ed08afc4 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Thu, 12 Jan 2017 14:52:19 -0600 Subject: misc: sram: Introduce support code for protect-exec sram type Some platforms, like many ARM SoCs, require the ability to run code from on-chip memory like SRAM for tasks like reconfiguring the SDRAM controller or entering low-power sleep modes. In order to do this we must be able to allocate memory that the code can be copied to but then change the mapping to be read-only and executable so that no memory is both writable and executable at the same time to avoid opening any unneccesary security holes. By using the existing "pool" partition type that the SRAM driver allows we can create a memory space that will already be exposed by the genalloc framework to allow for allocating memory but we must extend this to meet the executable requirements. By making use of various set_memory_* APIs we can change the attributes of pages to make them writable for code upload but then read-only and executable when we want to actually run code. Because SRAM is a shared resource we need a centralized manager of these set memory calls. Because the SRAM driver itself is responsible for allocating the memory we can introduce a sram_copy_exec API for the driver that works like memcpy but also manages the page attributes and locking to allow multiple users of the same SRAM space to all copy their code over independent of other each before starting execution. It is maintained in a separate file from the core SRAM driver to allow it to be selectively built depending on whether or not a platform has the appropriate set_memory_* APIs. A future patch will integrate it with the core SRAM driver. Signed-off-by: Dave Gerlach Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram-exec.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/misc/sram.h | 18 ++++++++ include/linux/sram.h | 27 ++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 drivers/misc/sram-exec.c create mode 100644 include/linux/sram.h diff --git a/drivers/misc/sram-exec.c b/drivers/misc/sram-exec.c new file mode 100644 index 000000000000..ac522417c462 --- /dev/null +++ b/drivers/misc/sram-exec.c @@ -0,0 +1,105 @@ +/* + * SRAM protect-exec region helper functions + * + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Dave Gerlach + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include + +#include "sram.h" + +static DEFINE_MUTEX(exec_pool_list_mutex); +static LIST_HEAD(exec_pool_list); + +int sram_check_protect_exec(struct sram_dev *sram, struct sram_reserve *block, + struct sram_partition *part) +{ + unsigned long base = (unsigned long)part->base; + unsigned long end = base + block->size; + + if (!PAGE_ALIGNED(base) || !PAGE_ALIGNED(end)) { + dev_err(sram->dev, + "SRAM pool marked with 'protect-exec' is not page aligned and will not be created.\n"); + return -ENOMEM; + } + + return 0; +} + +int sram_add_protect_exec(struct sram_partition *part) +{ + mutex_lock(&exec_pool_list_mutex); + list_add_tail(&part->list, &exec_pool_list); + mutex_unlock(&exec_pool_list_mutex); + + return 0; +} + +/** + * sram_exec_copy - copy data to a protected executable region of sram + * + * @pool: struct gen_pool retrieved that is part of this sram + * @dst: Destination address for the copy, that must be inside pool + * @src: Source address for the data to copy + * @size: Size of copy to perform, which starting from dst, must reside in pool + * + * This helper function allows sram driver to act as central control location + * of 'protect-exec' pools which are normal sram pools but are always set + * read-only and executable except when copying data to them, at which point + * they are set to read-write non-executable, to make sure no memory is + * writeable and executable at the same time. This region must be page-aligned + * and is checked during probe, otherwise page attribute manipulation would + * not be possible. + */ +int sram_exec_copy(struct gen_pool *pool, void *dst, void *src, + size_t size) +{ + struct sram_partition *part = NULL, *p; + unsigned long base; + int pages; + + mutex_lock(&exec_pool_list_mutex); + list_for_each_entry(p, &exec_pool_list, list) { + if (p->pool == pool) + part = p; + } + mutex_unlock(&exec_pool_list_mutex); + + if (!part) + return -EINVAL; + + if (!addr_in_gen_pool(pool, (unsigned long)dst, size)) + return -EINVAL; + + base = (unsigned long)part->base; + pages = PAGE_ALIGN(size) / PAGE_SIZE; + + mutex_lock(&part->lock); + + set_memory_nx((unsigned long)base, pages); + set_memory_rw((unsigned long)base, pages); + + memcpy(dst, src, size); + + set_memory_ro((unsigned long)base, pages); + set_memory_x((unsigned long)base, pages); + + mutex_unlock(&part->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(sram_exec_copy); diff --git a/drivers/misc/sram.h b/drivers/misc/sram.h index 52501a84468c..b268cd3f55bb 100644 --- a/drivers/misc/sram.h +++ b/drivers/misc/sram.h @@ -36,4 +36,22 @@ struct sram_reserve { bool pool; const char *label; }; + +#ifdef CONFIG_SRAM_EXEC +int sram_check_protect_exec(struct sram_dev *sram, struct sram_reserve *block, + struct sram_partition *part); +int sram_add_protect_exec(struct sram_partition *part); +#else +static inline int sram_check_protect_exec(struct sram_dev *sram, + struct sram_reserve *block, + struct sram_partition *part) +{ + return -ENODEV; +} + +static inline int sram_add_protect_exec(struct sram_partition *part) +{ + return -ENODEV; +} +#endif /* CONFIG_SRAM_EXEC */ #endif /* __SRAM_H */ diff --git a/include/linux/sram.h b/include/linux/sram.h new file mode 100644 index 000000000000..c97dcbe8ce25 --- /dev/null +++ b/include/linux/sram.h @@ -0,0 +1,27 @@ +/* + * Generic SRAM Driver Interface + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __LINUX_SRAM_H__ +#define __LINUX_SRAM_H__ + +struct gen_pool; + +#ifdef CONFIG_SRAM_EXEC +int sram_exec_copy(struct gen_pool *pool, void *dst, void *src, size_t size); +#else +static inline int sram_exec_copy(struct gen_pool *pool, void *dst, void *src, + size_t size) +{ + return -ENODEV; +} +#endif /* CONFIG_SRAM_EXEC */ +#endif /* __LINUX_SRAM_H__ */ -- cgit v1.2.3 From 37afff0d87c9939843c664970af6c6d952f95712 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Thu, 12 Jan 2017 14:52:20 -0600 Subject: misc: sram: Integrate protect-exec reserved sram area type Introduce a new "protect-exec" reserved sram area type which is makes use of the the existing functionality provided for the "pool" sram region type for use with the genalloc framework and with the added requirement that it be maintained as read-only and executable while allowing for an arbitrary number of drivers to share the space. This introduces a common way to maintain a region of sram as read-only and executable and also introduces a helper function, sram_exec_copy, which allows for copying data to this protected region while maintaining locking to avoid conflicts between multiple users of the same space. A region of memory that is marked with the "protect-exec" flag in the device tree also has the requirement of providing a page aligned block of memory so that the page attribute manipulation does not affect surrounding regions. Also, selectively enable this only for builds that support set_memory_* calls, for now just ARM, through the use of Kconfig. Signed-off-by: Dave Gerlach Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/sram/sram.txt | 6 ++++++ drivers/misc/Kconfig | 4 ++++ drivers/misc/Makefile | 1 + drivers/misc/sram.c | 21 +++++++++++++++++++-- drivers/misc/sram.h | 1 + 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/sram/sram.txt b/Documentation/devicetree/bindings/sram/sram.txt index 068c2c03c38f..267da4410aef 100644 --- a/Documentation/devicetree/bindings/sram/sram.txt +++ b/Documentation/devicetree/bindings/sram/sram.txt @@ -42,6 +42,12 @@ Optional properties in the area nodes: and in use by another device or devices - export : indicates that the reserved SRAM area may be accessed outside of the kernel, e.g. by bootloader or userspace +- protect-exec : Same as 'pool' above but with the additional + constraint that code wil be run from the region and + that the memory is maintained as read-only, executable + during code execution. NOTE: This region must be page + aligned on start and end in order to properly allow + manipulation of the page attributes. - label : the name for the reserved partition, if omitted, the label is taken from the node name excluding the unit address. diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 64971baf11fa..0444a8f9b094 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -474,11 +474,15 @@ config SRAM bool "Generic on-chip SRAM driver" depends on HAS_IOMEM select GENERIC_ALLOCATOR + select SRAM_EXEC if ARM help This driver allows you to declare a memory region to be managed by the genalloc API. It is supposed to be used for small on-chip SRAM areas found on many SoCs. +config SRAM_EXEC + bool + config VEXPRESS_SYSCFG bool "Versatile Express System Configuration driver" depends on VEXPRESS_CONFIG diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 31983366090a..7a3ea89339b4 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o obj-$(CONFIG_SRAM) += sram.o +obj-$(CONFIG_SRAM_EXEC) += sram-exec.o obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 5a6e001845c2..d1185b78cf9a 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -122,6 +122,18 @@ static int sram_add_partition(struct sram_dev *sram, struct sram_reserve *block, if (ret) return ret; } + if (block->protect_exec) { + ret = sram_check_protect_exec(sram, block, part); + if (ret) + return ret; + + ret = sram_add_pool(sram, block, start, part); + if (ret) + return ret; + + sram_add_protect_exec(part); + } + sram->partitions++; return 0; @@ -207,7 +219,11 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) if (of_find_property(child, "pool", NULL)) block->pool = true; - if ((block->export || block->pool) && block->size) { + if (of_find_property(child, "protect-exec", NULL)) + block->protect_exec = true; + + if ((block->export || block->pool || block->protect_exec) && + block->size) { exports++; label = NULL; @@ -269,7 +285,8 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) goto err_chunks; } - if ((block->export || block->pool) && block->size) { + if ((block->export || block->pool || block->protect_exec) && + block->size) { ret = sram_add_partition(sram, block, res->start + block->start); if (ret) { diff --git a/drivers/misc/sram.h b/drivers/misc/sram.h index b268cd3f55bb..c181ce4c8fca 100644 --- a/drivers/misc/sram.h +++ b/drivers/misc/sram.h @@ -34,6 +34,7 @@ struct sram_reserve { u32 size; bool export; bool pool; + bool protect_exec; const char *label; }; -- cgit v1.2.3 From 4aa2b480204638c3b8801132efa9cb4af6000a05 Mon Sep 17 00:00:00 2001 From: Bai Ping Date: Sun, 22 Jan 2017 23:02:36 +0000 Subject: nvmem: octop: Add support for imx6ul i.MX6UL is an new SOC of i.MX6 family. Enable ocotp driver support for this SOC. Signed-off-by: Bai Ping Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index 8e7b120696fa..b8ca1e677b01 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -73,6 +73,7 @@ static const struct of_device_id imx_ocotp_dt_ids[] = { { .compatible = "fsl,imx6q-ocotp", (void *)128 }, { .compatible = "fsl,imx6sl-ocotp", (void *)64 }, { .compatible = "fsl,imx6sx-ocotp", (void *)128 }, + { .compatible = "fsl,imx6ul-ocotp", (void *)128 }, { }, }; MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); -- cgit v1.2.3 From 1e928fff9a7444cbfbbb2420eb8cec79bfa53c6f Mon Sep 17 00:00:00 2001 From: Bai Ping Date: Sun, 22 Jan 2017 23:02:37 +0000 Subject: devicetree: bindings: nvmem: Add compatible string for imx6ul Add new compatible string for i.MX6UL SOC. Signed-off-by: Bai Ping Acked-by: Rob Herring Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/imx-ocotp.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/nvmem/imx-ocotp.txt b/Documentation/devicetree/bindings/nvmem/imx-ocotp.txt index 383d5889e95a..966a72ecc6bd 100644 --- a/Documentation/devicetree/bindings/nvmem/imx-ocotp.txt +++ b/Documentation/devicetree/bindings/nvmem/imx-ocotp.txt @@ -1,13 +1,15 @@ Freescale i.MX6 On-Chip OTP Controller (OCOTP) device tree bindings This binding represents the on-chip eFuse OTP controller found on -i.MX6Q/D, i.MX6DL/S, i.MX6SL, and i.MX6SX SoCs. +i.MX6Q/D, i.MX6DL/S, i.MX6SL, i.MX6SX and i.MX6UL SoCs. Required properties: - compatible: should be one of "fsl,imx6q-ocotp" (i.MX6Q/D/DL/S), "fsl,imx6sl-ocotp" (i.MX6SL), or - "fsl,imx6sx-ocotp" (i.MX6SX), followed by "syscon". + "fsl,imx6sx-ocotp" (i.MX6SX), + "fsl,imx6ul-ocotp" (i.MX6UL), + followed by "syscon". - reg: Should contain the register base and length. - clocks: Should contain a phandle pointing to the gated peripheral clock. -- cgit v1.2.3 From 3b4a687726f0019115bd903d9724db6cdcbb00fe Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Sun, 22 Jan 2017 23:02:38 +0000 Subject: nvmem: core: Allow ignoring length when reading a cell nvmem_cell_read() API fills in the argument 'len' with the number of bytes read from the cell. Many users don't care about this length value. So allow users to pass a NULL pointer to this len field. Signed-off-by: Vivek Gautam Reviewed-by: Stephen Boyd Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 398ea7f54826..78aa06a578d0 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -970,7 +970,8 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem, if (cell->bit_offset || cell->nbits) nvmem_shift_read_buffer_in_place(cell, buf); - *len = cell->bytes; + if (len) + *len = cell->bytes; return 0; } @@ -979,7 +980,8 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem, * nvmem_cell_read() - Read a given nvmem cell * * @cell: nvmem cell to be read. - * @len: pointer to length of cell which will be populated on successful read. + * @len: pointer to length of cell which will be populated on successful read; + * can be NULL. * * Return: ERR_PTR() on error or a valid pointer to a buffer on success. The * buffer should be freed by the consumer with a kfree(). -- cgit v1.2.3 From 29143268ec7d292ae4531bdf8deb6e55239a0051 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Sun, 22 Jan 2017 23:02:39 +0000 Subject: nvmem: core: Correct a bunch of function documentations Correct the documentation for arguments to a number of functions. Signed-off-by: Vivek Gautam Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 78aa06a578d0..9c0592127d7e 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -608,7 +608,7 @@ static struct nvmem_device *nvmem_find(const char *name) /** * of_nvmem_device_get() - Get nvmem device from a given id * - * @dev node: Device tree node that uses the nvmem device + * @np: Device tree node that uses the nvmem device. * @id: nvmem name from nvmem-names property. * * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device @@ -634,8 +634,8 @@ EXPORT_SYMBOL_GPL(of_nvmem_device_get); /** * nvmem_device_get() - Get nvmem device from a given id * - * @dev : Device that uses the nvmem device - * @id: nvmem name from nvmem-names property. + * @dev: Device that uses the nvmem device. + * @dev_name: name of the requested nvmem device. * * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device * on success. @@ -674,6 +674,7 @@ static void devm_nvmem_device_release(struct device *dev, void *res) /** * devm_nvmem_device_put() - put alredy got nvmem device * + * @dev: Device that uses the nvmem device. * @nvmem: pointer to nvmem device allocated by devm_nvmem_cell_get(), * that needs to be released. */ @@ -702,8 +703,8 @@ EXPORT_SYMBOL_GPL(nvmem_device_put); /** * devm_nvmem_device_get() - Get nvmem cell of device form a given id * - * @dev node: Device tree node that uses the nvmem cell - * @id: nvmem name in nvmems property. + * @dev: Device that requests the nvmem device. + * @id: name id for the requested nvmem device. * * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_cell * on success. The nvmem_cell will be freed by the automatically once the @@ -745,8 +746,8 @@ static struct nvmem_cell *nvmem_cell_get_from_list(const char *cell_id) /** * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id * - * @dev node: Device tree node that uses the nvmem cell - * @id: nvmem cell name from nvmem-cell-names property. + * @np: Device tree node that uses the nvmem cell. + * @name: nvmem cell name from nvmem-cell-names property. * * Return: Will be an ERR_PTR() on error or a valid pointer * to a struct nvmem_cell. The nvmem_cell will be freed by the @@ -830,8 +831,8 @@ EXPORT_SYMBOL_GPL(of_nvmem_cell_get); /** * nvmem_cell_get() - Get nvmem cell of device form a given cell name * - * @dev node: Device tree node that uses the nvmem cell - * @id: nvmem cell name to get. + * @dev: Device that requests the nvmem cell. + * @cell_id: nvmem cell name to get. * * Return: Will be an ERR_PTR() on error or a valid pointer * to a struct nvmem_cell. The nvmem_cell will be freed by the @@ -859,8 +860,8 @@ static void devm_nvmem_cell_release(struct device *dev, void *res) /** * devm_nvmem_cell_get() - Get nvmem cell of device form a given id * - * @dev node: Device tree node that uses the nvmem cell - * @id: nvmem id in nvmem-names property. + * @dev: Device that requests the nvmem cell. + * @id: nvmem cell name id to get. * * Return: Will be an ERR_PTR() on error or a valid pointer * to a struct nvmem_cell. The nvmem_cell will be freed by the @@ -900,7 +901,8 @@ static int devm_nvmem_cell_match(struct device *dev, void *res, void *data) * devm_nvmem_cell_put() - Release previously allocated nvmem cell * from devm_nvmem_cell_get. * - * @cell: Previously allocated nvmem cell by devm_nvmem_cell_get() + * @dev: Device that requests the nvmem cell. + * @cell: Previously allocated nvmem cell by devm_nvmem_cell_get(). */ void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell) { @@ -916,7 +918,7 @@ EXPORT_SYMBOL(devm_nvmem_cell_put); /** * nvmem_cell_put() - Release previously allocated nvmem cell. * - * @cell: Previously allocated nvmem cell by nvmem_cell_get() + * @cell: Previously allocated nvmem cell by nvmem_cell_get(). */ void nvmem_cell_put(struct nvmem_cell *cell) { @@ -1128,7 +1130,7 @@ EXPORT_SYMBOL_GPL(nvmem_device_cell_read); * nvmem_device_cell_write() - Write cell to a given nvmem device * * @nvmem: nvmem device to be written to. - * @info: nvmem cell info to be written + * @info: nvmem cell info to be written. * @buf: buffer to be written to cell. * * Return: length of bytes written or negative error code on failure. -- cgit v1.2.3 From fd0c478c0dd800449e2d70025ef742679a16a8de Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Sun, 22 Jan 2017 23:02:40 +0000 Subject: nvmem: core: Allow getting nvmem cell with a NULL cell id The nvmem cell with a NULL cell name/id should be the one with no accompanying 'nvmem-cell-names' property, and thus will be the cell at index 0 in the device tree. So, we default to index 0 and update the cell index only when nvmem cell name id exists. Suggested-by: Stephen Boyd Signed-off-by: Vivek Gautam Reviewed-by: Stephen Boyd Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 9c0592127d7e..408b521ee520 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -747,7 +747,9 @@ static struct nvmem_cell *nvmem_cell_get_from_list(const char *cell_id) * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id * * @np: Device tree node that uses the nvmem cell. - * @name: nvmem cell name from nvmem-cell-names property. + * @name: nvmem cell name from nvmem-cell-names property, or NULL + * for the cell at index 0 (the lone cell with no accompanying + * nvmem-cell-names property). * * Return: Will be an ERR_PTR() on error or a valid pointer * to a struct nvmem_cell. The nvmem_cell will be freed by the @@ -760,9 +762,12 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, struct nvmem_cell *cell; struct nvmem_device *nvmem; const __be32 *addr; - int rval, len, index; + int rval, len; + int index = 0; - index = of_property_match_string(np, "nvmem-cell-names", name); + /* if cell name exists, find index to the name */ + if (name) + index = of_property_match_string(np, "nvmem-cell-names", name); cell_np = of_parse_phandle(np, "nvmem-cells", index); if (!cell_np) -- cgit v1.2.3 From 67fd553ce0e55a468ee24f61ddf6b40ba610bbb5 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 23 Jan 2017 08:11:05 -0800 Subject: test_firmware: move misc_device down This will make further changes easier to review. Signed-off-by: Luis R. Rodriguez Signed-off-by: Greg Kroah-Hartman --- lib/test_firmware.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/test_firmware.c b/lib/test_firmware.c index a3e8ec3fb1c5..1cb9bf9eb41f 100644 --- a/lib/test_firmware.c +++ b/lib/test_firmware.c @@ -42,12 +42,6 @@ static const struct file_operations test_fw_fops = { .read = test_fw_misc_read, }; -static struct miscdevice test_fw_misc_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "test_firmware", - .fops = &test_fw_fops, -}; - static ssize_t trigger_request_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -132,6 +126,12 @@ out: } static DEVICE_ATTR_WO(trigger_async_request); +static struct miscdevice test_fw_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "test_firmware", + .fops = &test_fw_fops, +}; + static int __init test_firmware_init(void) { int rc; -- cgit v1.2.3 From 083a93b0c1f2eff2561305d0b95ac238fbfd100b Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 23 Jan 2017 08:11:06 -0800 Subject: test_firmware: use device attribute groups This simplifies init and exit. Signed-off-by: Luis R. Rodriguez Signed-off-by: Greg Kroah-Hartman --- lib/test_firmware.c | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/lib/test_firmware.c b/lib/test_firmware.c index 1cb9bf9eb41f..38cc188c4d3c 100644 --- a/lib/test_firmware.c +++ b/lib/test_firmware.c @@ -126,10 +126,21 @@ out: } static DEVICE_ATTR_WO(trigger_async_request); +#define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr + +static struct attribute *test_dev_attrs[] = { + TEST_FW_DEV_ATTR(trigger_request), + TEST_FW_DEV_ATTR(trigger_async_request), + NULL, +}; + +ATTRIBUTE_GROUPS(test_dev); + static struct miscdevice test_fw_misc_device = { .minor = MISC_DYNAMIC_MINOR, .name = "test_firmware", .fops = &test_fw_fops, + .groups = test_dev_groups, }; static int __init test_firmware_init(void) @@ -141,30 +152,10 @@ static int __init test_firmware_init(void) pr_err("could not register misc device: %d\n", rc); return rc; } - rc = device_create_file(test_fw_misc_device.this_device, - &dev_attr_trigger_request); - if (rc) { - pr_err("could not create sysfs interface: %d\n", rc); - goto dereg; - } - - rc = device_create_file(test_fw_misc_device.this_device, - &dev_attr_trigger_async_request); - if (rc) { - pr_err("could not create async sysfs interface: %d\n", rc); - goto remove_file; - } pr_warn("interface ready\n"); return 0; - -remove_file: - device_remove_file(test_fw_misc_device.this_device, - &dev_attr_trigger_async_request); -dereg: - misc_deregister(&test_fw_misc_device); - return rc; } module_init(test_firmware_init); @@ -172,10 +163,6 @@ module_init(test_firmware_init); static void __exit test_firmware_exit(void) { release_firmware(test_firmware); - device_remove_file(test_fw_misc_device.this_device, - &dev_attr_trigger_async_request); - device_remove_file(test_fw_misc_device.this_device, - &dev_attr_trigger_request); misc_deregister(&test_fw_misc_device); pr_warn("removed interface\n"); } -- cgit v1.2.3 From afb999cdef69148f366839e74470d8f5375ba5f1 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 23 Jan 2017 08:11:07 -0800 Subject: tools: firmware: check for distro fallback udev cancel rule Some distributions (Debian, OpenSUSE) have a udev rule in place to cancel all fallback mechanism uevents immediately. This would obviously make it hard to test against the fallback mechanism test interface, so we need to check for this. Signed-off-by: Luis R. Rodriguez Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/firmware/fw_userhelper.sh | 28 +++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/firmware/fw_userhelper.sh b/tools/testing/selftests/firmware/fw_userhelper.sh index b9983f8e09f6..01c626a1f226 100755 --- a/tools/testing/selftests/firmware/fw_userhelper.sh +++ b/tools/testing/selftests/firmware/fw_userhelper.sh @@ -64,9 +64,33 @@ trap "test_finish" EXIT echo "ABCD0123" >"$FW" NAME=$(basename "$FW") +DEVPATH="$DIR"/"nope-$NAME"/loading + # Test failure when doing nothing (timeout works). -echo 1 >/sys/class/firmware/timeout -echo -n "$NAME" >"$DIR"/trigger_request +echo -n 2 >/sys/class/firmware/timeout +echo -n "nope-$NAME" >"$DIR"/trigger_request 2>/dev/null & + +# Give the kernel some time to load the loading file, must be less +# than the timeout above. +sleep 1 +if [ ! -f $DEVPATH ]; then + echo "$0: fallback mechanism immediately cancelled" + echo "" + echo "The file never appeared: $DEVPATH" + echo "" + echo "This might be a distribution udev rule setup by your distribution" + echo "to immediately cancel all fallback requests, this must be" + echo "removed before running these tests. To confirm look for" + echo "a firmware rule like /lib/udev/rules.d/50-firmware.rules" + echo "and see if you have something like this:" + echo "" + echo "SUBSYSTEM==\"firmware\", ACTION==\"add\", ATTR{loading}=\"-1\"" + echo "" + echo "If you do remove this file or comment out this line before" + echo "proceeding with these tests." + exit 1 +fi + if diff -q "$FW" /dev/test_firmware >/dev/null ; then echo "$0: firmware was not expected to match" >&2 exit 1 -- cgit v1.2.3 From 823b0221a522bb22dcd278a06fca308a95545471 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 23 Jan 2017 08:11:08 -0800 Subject: tools: firmware: rename fallback mechanism script Calling it user mode helper only creates confusion. Signed-off-by: Luis R. Rodriguez Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/firmware/Makefile | 2 +- tools/testing/selftests/firmware/fw_fallback.sh | 124 ++++++++++++++++++++++ tools/testing/selftests/firmware/fw_userhelper.sh | 123 --------------------- 3 files changed, 125 insertions(+), 124 deletions(-) create mode 100755 tools/testing/selftests/firmware/fw_fallback.sh delete mode 100755 tools/testing/selftests/firmware/fw_userhelper.sh diff --git a/tools/testing/selftests/firmware/Makefile b/tools/testing/selftests/firmware/Makefile index 9bf82234855b..1894d625af2d 100644 --- a/tools/testing/selftests/firmware/Makefile +++ b/tools/testing/selftests/firmware/Makefile @@ -3,7 +3,7 @@ # No binaries, but make sure arg-less "make" doesn't trigger "run_tests" all: -TEST_PROGS := fw_filesystem.sh fw_userhelper.sh +TEST_PROGS := fw_filesystem.sh fw_fallback.sh include ../lib.mk diff --git a/tools/testing/selftests/firmware/fw_fallback.sh b/tools/testing/selftests/firmware/fw_fallback.sh new file mode 100755 index 000000000000..f694afb7d12d --- /dev/null +++ b/tools/testing/selftests/firmware/fw_fallback.sh @@ -0,0 +1,124 @@ +#!/bin/sh +# This validates that the kernel will fall back to using the fallback mechanism +# to load firmware it can't find on disk itself. We must request a firmware +# that the kernel won't find, and any installed helper (e.g. udev) also +# won't find so that we can do the load ourself manually. +set -e + +modprobe test_firmware + +DIR=/sys/devices/virtual/misc/test_firmware + +# CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/ +# These days no one enables CONFIG_FW_LOADER_USER_HELPER so check for that +# as an indicator for CONFIG_FW_LOADER_USER_HELPER. +HAS_FW_LOADER_USER_HELPER=$(if [ -d /sys/class/firmware/ ]; then echo yes; else echo no; fi) + +if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then + OLD_TIMEOUT=$(cat /sys/class/firmware/timeout) +else + echo "usermode helper disabled so ignoring test" + exit 0 +fi + +FWPATH=$(mktemp -d) +FW="$FWPATH/test-firmware.bin" + +test_finish() +{ + echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout + rm -f "$FW" + rmdir "$FWPATH" +} + +load_fw() +{ + local name="$1" + local file="$2" + + # This will block until our load (below) has finished. + echo -n "$name" >"$DIR"/trigger_request & + + # Give kernel a chance to react. + local timeout=10 + while [ ! -e "$DIR"/"$name"/loading ]; do + sleep 0.1 + timeout=$(( $timeout - 1 )) + if [ "$timeout" -eq 0 ]; then + echo "$0: firmware interface never appeared" >&2 + exit 1 + fi + done + + echo 1 >"$DIR"/"$name"/loading + cat "$file" >"$DIR"/"$name"/data + echo 0 >"$DIR"/"$name"/loading + + # Wait for request to finish. + wait +} + +trap "test_finish" EXIT + +# This is an unlikely real-world firmware content. :) +echo "ABCD0123" >"$FW" +NAME=$(basename "$FW") + +DEVPATH="$DIR"/"nope-$NAME"/loading + +# Test failure when doing nothing (timeout works). +echo -n 2 >/sys/class/firmware/timeout +echo -n "nope-$NAME" >"$DIR"/trigger_request 2>/dev/null & + +# Give the kernel some time to load the loading file, must be less +# than the timeout above. +sleep 1 +if [ ! -f $DEVPATH ]; then + echo "$0: fallback mechanism immediately cancelled" + echo "" + echo "The file never appeared: $DEVPATH" + echo "" + echo "This might be a distribution udev rule setup by your distribution" + echo "to immediately cancel all fallback requests, this must be" + echo "removed before running these tests. To confirm look for" + echo "a firmware rule like /lib/udev/rules.d/50-firmware.rules" + echo "and see if you have something like this:" + echo "" + echo "SUBSYSTEM==\"firmware\", ACTION==\"add\", ATTR{loading}=\"-1\"" + echo "" + echo "If you do remove this file or comment out this line before" + echo "proceeding with these tests." + exit 1 +fi + +if diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not expected to match" >&2 + exit 1 +else + echo "$0: timeout works" +fi + +# Put timeout high enough for us to do work but not so long that failures +# slow down this test too much. +echo 4 >/sys/class/firmware/timeout + +# Load this script instead of the desired firmware. +load_fw "$NAME" "$0" +if diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not expected to match" >&2 + exit 1 +else + echo "$0: firmware comparison works" +fi + +# Do a proper load, which should work correctly. +load_fw "$NAME" "$FW" +if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not loaded" >&2 + exit 1 +else + echo "$0: fallback mechanism works" + +fi + +exit 0 diff --git a/tools/testing/selftests/firmware/fw_userhelper.sh b/tools/testing/selftests/firmware/fw_userhelper.sh deleted file mode 100755 index 01c626a1f226..000000000000 --- a/tools/testing/selftests/firmware/fw_userhelper.sh +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/sh -# This validates that the kernel will fall back to using the user helper -# to load firmware it can't find on disk itself. We must request a firmware -# that the kernel won't find, and any installed helper (e.g. udev) also -# won't find so that we can do the load ourself manually. -set -e - -modprobe test_firmware - -DIR=/sys/devices/virtual/misc/test_firmware - -# CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/ -# These days no one enables CONFIG_FW_LOADER_USER_HELPER so check for that -# as an indicator for CONFIG_FW_LOADER_USER_HELPER. -HAS_FW_LOADER_USER_HELPER=$(if [ -d /sys/class/firmware/ ]; then echo yes; else echo no; fi) - -if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then - OLD_TIMEOUT=$(cat /sys/class/firmware/timeout) -else - echo "usermode helper disabled so ignoring test" - exit 0 -fi - -FWPATH=$(mktemp -d) -FW="$FWPATH/test-firmware.bin" - -test_finish() -{ - echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout - rm -f "$FW" - rmdir "$FWPATH" -} - -load_fw() -{ - local name="$1" - local file="$2" - - # This will block until our load (below) has finished. - echo -n "$name" >"$DIR"/trigger_request & - - # Give kernel a chance to react. - local timeout=10 - while [ ! -e "$DIR"/"$name"/loading ]; do - sleep 0.1 - timeout=$(( $timeout - 1 )) - if [ "$timeout" -eq 0 ]; then - echo "$0: firmware interface never appeared" >&2 - exit 1 - fi - done - - echo 1 >"$DIR"/"$name"/loading - cat "$file" >"$DIR"/"$name"/data - echo 0 >"$DIR"/"$name"/loading - - # Wait for request to finish. - wait -} - -trap "test_finish" EXIT - -# This is an unlikely real-world firmware content. :) -echo "ABCD0123" >"$FW" -NAME=$(basename "$FW") - -DEVPATH="$DIR"/"nope-$NAME"/loading - -# Test failure when doing nothing (timeout works). -echo -n 2 >/sys/class/firmware/timeout -echo -n "nope-$NAME" >"$DIR"/trigger_request 2>/dev/null & - -# Give the kernel some time to load the loading file, must be less -# than the timeout above. -sleep 1 -if [ ! -f $DEVPATH ]; then - echo "$0: fallback mechanism immediately cancelled" - echo "" - echo "The file never appeared: $DEVPATH" - echo "" - echo "This might be a distribution udev rule setup by your distribution" - echo "to immediately cancel all fallback requests, this must be" - echo "removed before running these tests. To confirm look for" - echo "a firmware rule like /lib/udev/rules.d/50-firmware.rules" - echo "and see if you have something like this:" - echo "" - echo "SUBSYSTEM==\"firmware\", ACTION==\"add\", ATTR{loading}=\"-1\"" - echo "" - echo "If you do remove this file or comment out this line before" - echo "proceeding with these tests." - exit 1 -fi - -if diff -q "$FW" /dev/test_firmware >/dev/null ; then - echo "$0: firmware was not expected to match" >&2 - exit 1 -else - echo "$0: timeout works" -fi - -# Put timeout high enough for us to do work but not so long that failures -# slow down this test too much. -echo 4 >/sys/class/firmware/timeout - -# Load this script instead of the desired firmware. -load_fw "$NAME" "$0" -if diff -q "$FW" /dev/test_firmware >/dev/null ; then - echo "$0: firmware was not expected to match" >&2 - exit 1 -else - echo "$0: firmware comparison works" -fi - -# Do a proper load, which should work correctly. -load_fw "$NAME" "$FW" -if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then - echo "$0: firmware was not loaded" >&2 - exit 1 -else - echo "$0: user helper firmware loading works" -fi - -exit 0 -- cgit v1.2.3 From eb67bc3ffd1796e198fc923da2ba15beb7965529 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 23 Jan 2017 08:11:09 -0800 Subject: tools: firmware: add fallback cancelation testing Add a test case for cancelling the sync fallback mechanism. Signed-off-by: Luis R. Rodriguez Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/firmware/fw_fallback.sh | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tools/testing/selftests/firmware/fw_fallback.sh b/tools/testing/selftests/firmware/fw_fallback.sh index f694afb7d12d..68e27e5f27a4 100755 --- a/tools/testing/selftests/firmware/fw_fallback.sh +++ b/tools/testing/selftests/firmware/fw_fallback.sh @@ -58,6 +58,31 @@ load_fw() wait } +load_fw_cancel() +{ + local name="$1" + local file="$2" + + # This will block until our load (below) has finished. + echo -n "$name" >"$DIR"/trigger_request 2>/dev/null & + + # Give kernel a chance to react. + local timeout=10 + while [ ! -e "$DIR"/"$name"/loading ]; do + sleep 0.1 + timeout=$(( $timeout - 1 )) + if [ "$timeout" -eq 0 ]; then + echo "$0: firmware interface never appeared" >&2 + exit 1 + fi + done + + echo -1 >"$DIR"/"$name"/loading + + # Wait for request to finish. + wait +} + trap "test_finish" EXIT # This is an unlikely real-world firmware content. :) @@ -118,7 +143,14 @@ if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then exit 1 else echo "$0: fallback mechanism works" +fi +load_fw_cancel "nope-$NAME" "$FW" +if diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was expected to be cancelled" >&2 + exit 1 +else + echo "$0: cancelling fallback mechanism works" fi exit 0 -- cgit v1.2.3 From 061132d2b9c9504a9f314dcd73f6483a7d8cd1e8 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 23 Jan 2017 08:11:10 -0800 Subject: test_firmware: add test custom fallback trigger We have no custom fallback mechanism test interface. Provide one. This tests both the custom fallback mechanism and cancelling the it. Signed-off-by: Luis R. Rodriguez Signed-off-by: Greg Kroah-Hartman --- lib/test_firmware.c | 45 ++++++++++++++++ tools/testing/selftests/firmware/fw_fallback.sh | 68 +++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/lib/test_firmware.c b/lib/test_firmware.c index 38cc188c4d3c..09371b0a9baf 100644 --- a/lib/test_firmware.c +++ b/lib/test_firmware.c @@ -126,11 +126,56 @@ out: } static DEVICE_ATTR_WO(trigger_async_request); +static ssize_t trigger_custom_fallback_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + char *name; + + name = kstrndup(buf, count, GFP_KERNEL); + if (!name) + return -ENOSPC; + + pr_info("loading '%s' using custom fallback mechanism\n", name); + + mutex_lock(&test_fw_mutex); + release_firmware(test_firmware); + test_firmware = NULL; + rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name, + dev, GFP_KERNEL, NULL, + trigger_async_request_cb); + if (rc) { + pr_info("async load of '%s' failed: %d\n", name, rc); + kfree(name); + goto out; + } + /* Free 'name' ASAP, to test for race conditions */ + kfree(name); + + wait_for_completion(&async_fw_done); + + if (test_firmware) { + pr_info("loaded: %zu\n", test_firmware->size); + rc = count; + } else { + pr_err("failed to async load firmware\n"); + rc = -ENODEV; + } + +out: + mutex_unlock(&test_fw_mutex); + + return rc; +} +static DEVICE_ATTR_WO(trigger_custom_fallback); + #define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr static struct attribute *test_dev_attrs[] = { TEST_FW_DEV_ATTR(trigger_request), TEST_FW_DEV_ATTR(trigger_async_request), + TEST_FW_DEV_ATTR(trigger_custom_fallback), NULL, }; diff --git a/tools/testing/selftests/firmware/fw_fallback.sh b/tools/testing/selftests/firmware/fw_fallback.sh index 68e27e5f27a4..2e4c22d5abf7 100755 --- a/tools/testing/selftests/firmware/fw_fallback.sh +++ b/tools/testing/selftests/firmware/fw_fallback.sh @@ -83,6 +83,58 @@ load_fw_cancel() wait } +load_fw_custom() +{ + local name="$1" + local file="$2" + + echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null & + + # Give kernel a chance to react. + local timeout=10 + while [ ! -e "$DIR"/"$name"/loading ]; do + sleep 0.1 + timeout=$(( $timeout - 1 )) + if [ "$timeout" -eq 0 ]; then + echo "$0: firmware interface never appeared" >&2 + exit 1 + fi + done + + echo 1 >"$DIR"/"$name"/loading + cat "$file" >"$DIR"/"$name"/data + echo 0 >"$DIR"/"$name"/loading + + # Wait for request to finish. + wait +} + + +load_fw_custom_cancel() +{ + local name="$1" + local file="$2" + + echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null & + + # Give kernel a chance to react. + local timeout=10 + while [ ! -e "$DIR"/"$name"/loading ]; do + sleep 0.1 + timeout=$(( $timeout - 1 )) + if [ "$timeout" -eq 0 ]; then + echo "$0: firmware interface never appeared" >&2 + exit 1 + fi + done + + echo -1 >"$DIR"/"$name"/loading + + # Wait for request to finish. + wait +} + + trap "test_finish" EXIT # This is an unlikely real-world firmware content. :) @@ -153,4 +205,20 @@ else echo "$0: cancelling fallback mechanism works" fi +load_fw_custom "$NAME" "$FW" +if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not loaded" >&2 + exit 1 +else + echo "$0: custom fallback loading mechanism works" +fi + +load_fw_custom_cancel "nope-$NAME" "$FW" +if diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was expected to be cancelled" >&2 + exit 1 +else + echo "$0: cancelling custom fallback mechanism works" +fi + exit 0 -- cgit v1.2.3 From aed74b140e10725caf8a982ea26610d1dd1f612c Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 26 Jan 2017 16:00:36 +0300 Subject: eeprom: idt_89hpesx: Discard memory freeing allocated by devm_kmalloc Indeed, the data structure is allocated by device resource manager, so the driver doesn't need to free anything on remove() callback. Reported-by: Julia Lawall Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/idt_89hpesx.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 4db0d2cb08c6..5d48aecbe208 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1229,9 +1229,6 @@ static void idt_free_pdev(struct idt_89hpesx_dev *pdev) { /* Clear driver data from device private field */ i2c_set_clientdata(pdev->client, NULL); - - /* Just free memory allocated for data */ - devm_kfree(&pdev->client->dev, pdev); } /* -- cgit v1.2.3 From f2d697604cc860a177b793f669b22ef9c13131b9 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 25 Jan 2017 14:09:52 +0000 Subject: eeprom: idt_89hpesx: Drop kfree for memory allocated with devm_kzalloc It's not necessary to free memory allocated with devm_kzalloc and using kfree leads to a double free. Fixes: cfad6425382e ("eeprom: Add IDT 89HPESx EEPROM/CSR driver") Signed-off-by: Wei Yongjun Signed-off-by: Dan Carpenter Acked-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/idt_89hpesx.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 5d48aecbe208..4a22a1d99395 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1358,7 +1358,6 @@ static int idt_create_sysfs_files(struct idt_89hpesx_dev *pdev) pdev->ee_file->size = pdev->eesize; ret = sysfs_create_bin_file(&dev->kobj, pdev->ee_file); if (ret != 0) { - kfree(pdev->ee_file); dev_err(dev, "Failed to create EEPROM sysfs-node"); return ret; } @@ -1380,9 +1379,6 @@ static void idt_remove_sysfs_files(struct idt_89hpesx_dev *pdev) /* Remove EEPROM sysfs file */ sysfs_remove_bin_file(&dev->kobj, pdev->ee_file); - - /* Free memory allocated for bin_attribute structure */ - kfree(pdev->ee_file); } /* -- cgit v1.2.3 From 17244623a4c0f68d3f02c9c74d9b6ae259425826 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Sat, 28 Jan 2017 12:37:13 -0700 Subject: hv_util: switch to using timespec64 do_settimeofday() is deprecated, use do_settimeofday64() instead. Signed-off-by: Vitaly Kuznetsov Acked-by: John Stultz Acked-by: Thomas Gleixner Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 7c90e3334a88..51528bf92777 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -185,7 +185,7 @@ static void hv_set_host_time(struct work_struct *work) struct adj_time_work *wrk; s64 host_tns; u64 newtime; - struct timespec host_ts; + struct timespec64 host_ts; wrk = container_of(work, struct adj_time_work, work); @@ -204,9 +204,9 @@ static void hv_set_host_time(struct work_struct *work) newtime += (current_tick - wrk->ref_time); } host_tns = (newtime - WLTIMEDELTA) * 100; - host_ts = ns_to_timespec(host_tns); + host_ts = ns_to_timespec64(host_tns); - do_settimeofday(&host_ts); + do_settimeofday64(&host_ts); } /* -- cgit v1.2.3 From d6f3609d2b4c6d0eec01f398cb685e50da3e6013 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Sat, 28 Jan 2017 12:37:14 -0700 Subject: Drivers: hv: restore hypervcall page cleanup before kexec We need to cleanup the hypercall page before doing kexec/kdump or the new kernel may crash if it tries to use it. Reuse the now-empty hv_cleanup function renaming it to hyperv_cleanup and moving to the arch specific code. Fixes: 8730046c1498 ("Drivers: hv vmbus: Move Hypercall page setup out of common code") Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/hyperv/hv_init.c | 16 ++++++++++++++++ arch/x86/include/asm/mshyperv.h | 1 + drivers/hv/hv.c | 10 ---------- drivers/hv/hyperv_vmbus.h | 2 -- drivers/hv/vmbus_drv.c | 10 +++------- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index d289bc29d282..d6b018b86c42 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -165,6 +165,22 @@ void hyperv_init(void) clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100); } +/* + * This routine is called before kexec/kdump, it does the required cleanup. + */ +void hyperv_cleanup(void) +{ + union hv_x64_msr_hypercall_contents hypercall_msr; + + /* Reset our OS id */ + wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0); + + /* Reset the hypercall page */ + hypercall_msr.as_uint64 = 0; + wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); +} +EXPORT_SYMBOL_GPL(hyperv_cleanup); + /* * hv_do_hypercall- Invoke the specified hypercall */ diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 42505d1158d6..f8dc3700de67 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -171,5 +171,6 @@ void hv_remove_crash_handler(void); void hyperv_init(void); void hyperv_report_panic(struct pt_regs *regs); bool hv_is_hypercall_page_setup(void); +void hyperv_cleanup(void); #endif #endif diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 60594fa3250d..0f73237bed0a 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -70,16 +70,6 @@ int hv_init(void) return 0; } -/* - * hv_cleanup - Cleanup routine. - * - * This routine is called normally during driver unloading or exiting. - */ -void hv_cleanup(bool crash) -{ - -} - /* * hv_post_message - Post a message using the hypervisor message IPC. * diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 2463ef93c1f6..86b56b677dc3 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -255,8 +255,6 @@ struct hv_ring_buffer_debug_info { extern int hv_init(void); -extern void hv_cleanup(bool crash); - extern int hv_post_message(union hv_connection_id connection_id, enum hv_message_type message_type, void *payload, size_t payload_size); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 8e81346114d4..f8ebe13cf251 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -962,7 +962,7 @@ static int vmbus_bus_init(void) ret = bus_register(&hv_bus); if (ret) - goto err_cleanup; + return ret; hv_setup_vmbus_irq(vmbus_isr); @@ -1004,9 +1004,6 @@ err_alloc: bus_unregister(&hv_bus); -err_cleanup: - hv_cleanup(false); - return ret; } @@ -1462,7 +1459,7 @@ static void hv_kexec_handler(void) /* Make sure conn_state is set as hv_synic_cleanup checks for it */ mb(); cpuhp_remove_state(hyperv_cpuhp_online); - hv_cleanup(false); + hyperv_cleanup(); }; static void hv_crash_handler(struct pt_regs *regs) @@ -1475,7 +1472,7 @@ static void hv_crash_handler(struct pt_regs *regs) */ vmbus_connection.conn_state = DISCONNECTED; hv_synic_cleanup(smp_processor_id()); - hv_cleanup(true); + hyperv_cleanup(); }; static int __init hv_acpi_init(void) @@ -1535,7 +1532,6 @@ static void __exit vmbus_exit(void) &hyperv_panic_block); } bus_unregister(&hv_bus); - hv_cleanup(false); for_each_online_cpu(cpu) { tasklet_kill(hv_context.event_dpc[cpu]); } -- cgit v1.2.3 From 5647dbf8f0807a35421bd0232247b02413ef2cab Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Sat, 28 Jan 2017 12:37:15 -0700 Subject: Drivers: hv: restore TSC page cleanup before kexec We need to cleanup the TSC page before doing kexec/kdump or the new kernel may crash if it tries to use it. Fixes: 63ed4e0c67df ("Drivers: hv: vmbus: Consolidate all Hyper-V specific clocksource code") Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- arch/x86/hyperv/hv_init.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index d6b018b86c42..b371d0e984a9 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -178,6 +178,10 @@ void hyperv_cleanup(void) /* Reset the hypercall page */ hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + + /* Reset the TSC page */ + hypercall_msr.as_uint64 = 0; + wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); } EXPORT_SYMBOL_GPL(hyperv_cleanup); -- cgit v1.2.3 From ad6d41253bf91eabb41626683c35a712ba27a20c Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Sat, 28 Jan 2017 12:37:16 -0700 Subject: Drivers: hv: balloon: add a fall through comment to hv_memory_notifier() Coverity scan gives a warning when there is fall through in a switch without a comment. This fall through is intentional as ol_waitevent needs to be completed to unblock hv_mem_hot_add() allowing it to process next requests regardless of the result of if we were able to online this block. Reported-by: Stephen Hemminger Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 14c3dc4bd23c..5fd03e59cee5 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -587,6 +587,7 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val, spin_lock_irqsave(&dm_device.ha_lock, flags); dm_device.num_pages_onlined += mem->nr_pages; spin_unlock_irqrestore(&dm_device.ha_lock, flags); + /* Fall through */ case MEM_CANCEL_ONLINE: if (dm_device.ha_waiting) { dm_device.ha_waiting = false; -- cgit v1.2.3 From a1656454131880980bc3a5313c8bf66ef5990c91 Mon Sep 17 00:00:00 2001 From: Alex Ng Date: Sat, 28 Jan 2017 12:37:17 -0700 Subject: Drivers: hv: vmbus: Use all supported IC versions to negotiate Previously, we were assuming that each IC protocol version was tied to a specific host version. For example, some Windows 10 preview hosts only support v3 TimeSync even though driver assumes v4 is supported by all Windows 10 hosts. The guest will stop trying to negotiate even though older supported versions may still be offered by the host. Make IC version negotiation more robust by going through all versions that are supported by the guest. Fixes: 3da0401b4d0e ("Drivers: hv: utils: Fix the mapping between host version and protocol to use") Reported-by: Rolf Neugebauer Signed-off-by: Alex Ng Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 80 +++++++++++++++++++++++++++------------- drivers/hv/hv_fcopy.c | 20 +++++++--- drivers/hv/hv_kvp.c | 41 +++++++++------------ drivers/hv/hv_snapshot.c | 18 +++++++-- drivers/hv/hv_util.c | 94 +++++++++++++++++++++++++---------------------- include/linux/hyperv.h | 7 ++-- 6 files changed, 154 insertions(+), 106 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 49d77be90ca4..de90a9900fee 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -204,33 +204,34 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel) * @buf: Raw buffer channel data * * @icmsghdrp is of type &struct icmsg_hdr. - * @negop is of type &struct icmsg_negotiate. * Set up and fill in default negotiate response message. * - * The fw_version specifies the framework version that - * we can support and srv_version specifies the service - * version we can support. + * The fw_version and fw_vercnt specifies the framework version that + * we can support. + * + * The srv_version and srv_vercnt specifies the service + * versions we can support. + * + * Versions are given in decreasing order. + * + * nego_fw_version and nego_srv_version store the selected protocol versions. * * Mainly used by Hyper-V drivers. */ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, - struct icmsg_negotiate *negop, u8 *buf, - int fw_version, int srv_version) + u8 *buf, const int *fw_version, int fw_vercnt, + const int *srv_version, int srv_vercnt, + int *nego_fw_version, int *nego_srv_version) { int icframe_major, icframe_minor; int icmsg_major, icmsg_minor; int fw_major, fw_minor; int srv_major, srv_minor; - int i; + int i, j; bool found_match = false; + struct icmsg_negotiate *negop; icmsghdrp->icmsgsize = 0x10; - fw_major = (fw_version >> 16); - fw_minor = (fw_version & 0xFFFF); - - srv_major = (srv_version >> 16); - srv_minor = (srv_version & 0xFFFF); - negop = (struct icmsg_negotiate *)&buf[ sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; @@ -246,13 +247,22 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, * support. */ - for (i = 0; i < negop->icframe_vercnt; i++) { - if ((negop->icversion_data[i].major == fw_major) && - (negop->icversion_data[i].minor == fw_minor)) { - icframe_major = negop->icversion_data[i].major; - icframe_minor = negop->icversion_data[i].minor; - found_match = true; + for (i = 0; i < fw_vercnt; i++) { + fw_major = (fw_version[i] >> 16); + fw_minor = (fw_version[i] & 0xFFFF); + + for (j = 0; j < negop->icframe_vercnt; j++) { + if ((negop->icversion_data[j].major == fw_major) && + (negop->icversion_data[j].minor == fw_minor)) { + icframe_major = negop->icversion_data[j].major; + icframe_minor = negop->icversion_data[j].minor; + found_match = true; + break; + } } + + if (found_match) + break; } if (!found_match) @@ -260,14 +270,26 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, found_match = false; - for (i = negop->icframe_vercnt; - (i < negop->icframe_vercnt + negop->icmsg_vercnt); i++) { - if ((negop->icversion_data[i].major == srv_major) && - (negop->icversion_data[i].minor == srv_minor)) { - icmsg_major = negop->icversion_data[i].major; - icmsg_minor = negop->icversion_data[i].minor; - found_match = true; + for (i = 0; i < srv_vercnt; i++) { + srv_major = (srv_version[i] >> 16); + srv_minor = (srv_version[i] & 0xFFFF); + + for (j = negop->icframe_vercnt; + (j < negop->icframe_vercnt + negop->icmsg_vercnt); + j++) { + + if ((negop->icversion_data[j].major == srv_major) && + (negop->icversion_data[j].minor == srv_minor)) { + + icmsg_major = negop->icversion_data[j].major; + icmsg_minor = negop->icversion_data[j].minor; + found_match = true; + break; + } } + + if (found_match) + break; } /* @@ -284,6 +306,12 @@ fw_error: negop->icmsg_vercnt = 1; } + if (nego_fw_version) + *nego_fw_version = (icframe_major << 16) | icframe_minor; + + if (nego_srv_version) + *nego_srv_version = (icmsg_major << 16) | icmsg_minor; + negop->icversion_data[0].major = icframe_major; negop->icversion_data[0].minor = icframe_minor; negop->icversion_data[1].major = icmsg_major; diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c index e47d8c9db03a..0a315e6aa589 100644 --- a/drivers/hv/hv_fcopy.c +++ b/drivers/hv/hv_fcopy.c @@ -31,6 +31,16 @@ #define WIN8_SRV_MINOR 1 #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) +#define FCOPY_VER_COUNT 1 +static const int fcopy_versions[] = { + WIN8_SRV_VERSION +}; + +#define FW_VER_COUNT 1 +static const int fw_versions[] = { + UTIL_FW_VERSION +}; + /* * Global state maintained for transaction that is being processed. * For a class of integration services, including the "file copy service", @@ -228,8 +238,6 @@ void hv_fcopy_onchannelcallback(void *context) u64 requestid; struct hv_fcopy_hdr *fcopy_msg; struct icmsg_hdr *icmsghdr; - struct icmsg_negotiate *negop = NULL; - int util_fw_version; int fcopy_srv_version; if (fcopy_transaction.state > HVUTIL_READY) @@ -243,10 +251,10 @@ void hv_fcopy_onchannelcallback(void *context) icmsghdr = (struct icmsg_hdr *)&recv_buffer[ sizeof(struct vmbuspipe_hdr)]; if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) { - util_fw_version = UTIL_FW_VERSION; - fcopy_srv_version = WIN8_SRV_VERSION; - vmbus_prep_negotiate_resp(icmsghdr, negop, recv_buffer, - util_fw_version, fcopy_srv_version); + vmbus_prep_negotiate_resp(icmsghdr, recv_buffer, + fw_versions, FW_VER_COUNT, + fcopy_versions, FCOPY_VER_COUNT, + NULL, &fcopy_srv_version); } else { fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 3abfc5983c97..2cc670442f6c 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -46,6 +46,19 @@ #define WIN8_SRV_MINOR 0 #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) +#define KVP_VER_COUNT 3 +static const int kvp_versions[] = { + WIN8_SRV_VERSION, + WIN7_SRV_VERSION, + WS2008_SRV_VERSION +}; + +#define FW_VER_COUNT 2 +static const int fw_versions[] = { + UTIL_FW_VERSION, + UTIL_WS2K8_FW_VERSION +}; + /* * Global state maintained for transaction that is being processed. For a class * of integration services, including the "KVP service", the specified protocol @@ -610,8 +623,6 @@ void hv_kvp_onchannelcallback(void *context) struct hv_kvp_msg *kvp_msg; struct icmsg_hdr *icmsghdrp; - struct icmsg_negotiate *negop = NULL; - int util_fw_version; int kvp_srv_version; static enum {NEGO_NOT_STARTED, NEGO_IN_PROGRESS, @@ -640,28 +651,10 @@ void hv_kvp_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - /* - * Based on the host, select appropriate - * framework and service versions we will - * negotiate. - */ - switch (vmbus_proto_version) { - case (VERSION_WS2008): - util_fw_version = UTIL_WS2K8_FW_VERSION; - kvp_srv_version = WS2008_SRV_VERSION; - break; - case (VERSION_WIN7): - util_fw_version = UTIL_FW_VERSION; - kvp_srv_version = WIN7_SRV_VERSION; - break; - default: - util_fw_version = UTIL_FW_VERSION; - kvp_srv_version = WIN8_SRV_VERSION; - } - vmbus_prep_negotiate_resp(icmsghdrp, negop, - recv_buffer, util_fw_version, - kvp_srv_version); - + vmbus_prep_negotiate_resp(icmsghdrp, + recv_buffer, fw_versions, FW_VER_COUNT, + kvp_versions, KVP_VER_COUNT, + NULL, &kvp_srv_version); } else { kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index 4e543dbb731a..d14f10b924a0 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -31,6 +31,16 @@ #define VSS_MINOR 0 #define VSS_VERSION (VSS_MAJOR << 16 | VSS_MINOR) +#define VSS_VER_COUNT 1 +static const int vss_versions[] = { + VSS_VERSION +}; + +#define FW_VER_COUNT 1 +static const int fw_versions[] = { + UTIL_FW_VERSION +}; + /* * Timeout values are based on expecations from host */ @@ -297,7 +307,6 @@ void hv_vss_onchannelcallback(void *context) struct icmsg_hdr *icmsghdrp; - struct icmsg_negotiate *negop = NULL; if (vss_transaction.state > HVUTIL_READY) return; @@ -310,9 +319,10 @@ void hv_vss_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - vmbus_prep_negotiate_resp(icmsghdrp, negop, - recv_buffer, UTIL_FW_VERSION, - VSS_VERSION); + vmbus_prep_negotiate_resp(icmsghdrp, + recv_buffer, fw_versions, FW_VER_COUNT, + vss_versions, VSS_VER_COUNT, + NULL, NULL); } else { vss_msg = (struct hv_vss_msg *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 51528bf92777..891537ae579f 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -58,7 +58,31 @@ static int sd_srv_version; static int ts_srv_version; static int hb_srv_version; -static int util_fw_version; + +#define SD_VER_COUNT 2 +static const int sd_versions[] = { + SD_VERSION, + SD_VERSION_1 +}; + +#define TS_VER_COUNT 3 +static const int ts_versions[] = { + TS_VERSION, + TS_VERSION_3, + TS_VERSION_1 +}; + +#define HB_VER_COUNT 2 +static const int hb_versions[] = { + HB_VERSION, + HB_VERSION_1 +}; + +#define FW_VER_COUNT 2 +static const int fw_versions[] = { + UTIL_FW_VERSION, + UTIL_WS2K8_FW_VERSION +}; static void shutdown_onchannelcallback(void *context); static struct hv_util_service util_shutdown = { @@ -119,7 +143,6 @@ static void shutdown_onchannelcallback(void *context) struct shutdown_msg_data *shutdown_msg; struct icmsg_hdr *icmsghdrp; - struct icmsg_negotiate *negop = NULL; vmbus_recvpacket(channel, shut_txf_buf, PAGE_SIZE, &recvlen, &requestid); @@ -129,9 +152,14 @@ static void shutdown_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - vmbus_prep_negotiate_resp(icmsghdrp, negop, - shut_txf_buf, util_fw_version, - sd_srv_version); + if (vmbus_prep_negotiate_resp(icmsghdrp, shut_txf_buf, + fw_versions, FW_VER_COUNT, + sd_versions, SD_VER_COUNT, + NULL, &sd_srv_version)) { + pr_info("Shutdown IC version %d.%d\n", + sd_srv_version >> 16, + sd_srv_version & 0xFFFF); + } } else { shutdown_msg = (struct shutdown_msg_data *)&shut_txf_buf[ @@ -254,7 +282,6 @@ static void timesync_onchannelcallback(void *context) struct ictimesync_data *timedatap; struct ictimesync_ref_data *refdata; u8 *time_txf_buf = util_timesynch.recv_buffer; - struct icmsg_negotiate *negop = NULL; vmbus_recvpacket(channel, time_txf_buf, PAGE_SIZE, &recvlen, &requestid); @@ -264,12 +291,14 @@ static void timesync_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - vmbus_prep_negotiate_resp(icmsghdrp, negop, - time_txf_buf, - util_fw_version, - ts_srv_version); - pr_info("Using TimeSync version %d.%d\n", - ts_srv_version >> 16, ts_srv_version & 0xFFFF); + if (vmbus_prep_negotiate_resp(icmsghdrp, time_txf_buf, + fw_versions, FW_VER_COUNT, + ts_versions, TS_VER_COUNT, + NULL, &ts_srv_version)) { + pr_info("TimeSync version %d.%d\n", + ts_srv_version >> 16, + ts_srv_version & 0xFFFF); + } } else { if (ts_srv_version > TS_VERSION_3) { refdata = (struct ictimesync_ref_data *) @@ -313,7 +342,6 @@ static void heartbeat_onchannelcallback(void *context) struct icmsg_hdr *icmsghdrp; struct heartbeat_msg_data *heartbeat_msg; u8 *hbeat_txf_buf = util_heartbeat.recv_buffer; - struct icmsg_negotiate *negop = NULL; while (1) { @@ -327,9 +355,16 @@ static void heartbeat_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - vmbus_prep_negotiate_resp(icmsghdrp, negop, - hbeat_txf_buf, util_fw_version, - hb_srv_version); + if (vmbus_prep_negotiate_resp(icmsghdrp, + hbeat_txf_buf, + fw_versions, FW_VER_COUNT, + hb_versions, HB_VER_COUNT, + NULL, &hb_srv_version)) { + + pr_info("Heartbeat version %d.%d\n", + hb_srv_version >> 16, + hb_srv_version & 0xFFFF); + } } else { heartbeat_msg = (struct heartbeat_msg_data *)&hbeat_txf_buf[ @@ -379,33 +414,6 @@ static int util_probe(struct hv_device *dev, hv_set_drvdata(dev, srv); - /* - * Based on the host; initialize the framework and - * service version numbers we will negotiate. - */ - switch (vmbus_proto_version) { - case (VERSION_WS2008): - util_fw_version = UTIL_WS2K8_FW_VERSION; - sd_srv_version = SD_VERSION_1; - ts_srv_version = TS_VERSION_1; - hb_srv_version = HB_VERSION_1; - break; - case VERSION_WIN7: - case VERSION_WIN8: - case VERSION_WIN8_1: - util_fw_version = UTIL_FW_VERSION; - sd_srv_version = SD_VERSION; - ts_srv_version = TS_VERSION_3; - hb_srv_version = HB_VERSION; - break; - case VERSION_WIN10: - default: - util_fw_version = UTIL_FW_VERSION; - sd_srv_version = SD_VERSION; - ts_srv_version = TS_VERSION; - hb_srv_version = HB_VERSION; - } - ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, srv->util_cb, dev->channel); if (ret) diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 7ea20bd7cdd1..85b26f06e172 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1445,9 +1445,10 @@ struct hyperv_service_callback { }; #define MAX_SRV_VER 0x7ffffff -extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *, - struct icmsg_negotiate *, u8 *, int, - int); +extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf, + const int *fw_version, int fw_vercnt, + const int *srv_version, int srv_vercnt, + int *nego_fw_version, int *nego_srv_version); void hv_event_tasklet_disable(struct vmbus_channel *channel); void hv_event_tasklet_enable(struct vmbus_channel *channel); -- cgit v1.2.3 From 1274a690f6b2bd2b37447c47e3062afa8aa43f93 Mon Sep 17 00:00:00 2001 From: Alex Ng Date: Sat, 28 Jan 2017 12:37:18 -0700 Subject: Drivers: hv: Log the negotiated IC versions. Log the negotiated IC versions. Signed-off-by: Alex Ng Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_fcopy.c | 9 +++++++-- drivers/hv/hv_kvp.c | 8 ++++++-- drivers/hv/hv_snapshot.c | 11 ++++++++--- drivers/hv/hv_util.c | 4 ++-- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c index 0a315e6aa589..9aee6014339d 100644 --- a/drivers/hv/hv_fcopy.c +++ b/drivers/hv/hv_fcopy.c @@ -251,10 +251,15 @@ void hv_fcopy_onchannelcallback(void *context) icmsghdr = (struct icmsg_hdr *)&recv_buffer[ sizeof(struct vmbuspipe_hdr)]; if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) { - vmbus_prep_negotiate_resp(icmsghdr, recv_buffer, + if (vmbus_prep_negotiate_resp(icmsghdr, recv_buffer, fw_versions, FW_VER_COUNT, fcopy_versions, FCOPY_VER_COUNT, - NULL, &fcopy_srv_version); + NULL, &fcopy_srv_version)) { + + pr_info("FCopy IC version %d.%d\n", + fcopy_srv_version >> 16, + fcopy_srv_version & 0xFFFF); + } } else { fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 2cc670442f6c..de263712e247 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -651,10 +651,14 @@ void hv_kvp_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - vmbus_prep_negotiate_resp(icmsghdrp, + if (vmbus_prep_negotiate_resp(icmsghdrp, recv_buffer, fw_versions, FW_VER_COUNT, kvp_versions, KVP_VER_COUNT, - NULL, &kvp_srv_version); + NULL, &kvp_srv_version)) { + pr_info("KVP IC version %d.%d\n", + kvp_srv_version >> 16, + kvp_srv_version & 0xFFFF); + } } else { kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index d14f10b924a0..bcc03f0748d6 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -304,7 +304,7 @@ void hv_vss_onchannelcallback(void *context) u32 recvlen; u64 requestid; struct hv_vss_msg *vss_msg; - + int vss_srv_version; struct icmsg_hdr *icmsghdrp; @@ -319,10 +319,15 @@ void hv_vss_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - vmbus_prep_negotiate_resp(icmsghdrp, + if (vmbus_prep_negotiate_resp(icmsghdrp, recv_buffer, fw_versions, FW_VER_COUNT, vss_versions, VSS_VER_COUNT, - NULL, NULL); + NULL, &vss_srv_version)) { + + pr_info("VSS IC version %d.%d\n", + vss_srv_version >> 16, + vss_srv_version & 0xFFFF); + } } else { vss_msg = (struct hv_vss_msg *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 891537ae579f..d42ede78a9dd 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -295,7 +295,7 @@ static void timesync_onchannelcallback(void *context) fw_versions, FW_VER_COUNT, ts_versions, TS_VER_COUNT, NULL, &ts_srv_version)) { - pr_info("TimeSync version %d.%d\n", + pr_info("TimeSync IC version %d.%d\n", ts_srv_version >> 16, ts_srv_version & 0xFFFF); } @@ -361,7 +361,7 @@ static void heartbeat_onchannelcallback(void *context) hb_versions, HB_VER_COUNT, NULL, &hb_srv_version)) { - pr_info("Heartbeat version %d.%d\n", + pr_info("Heartbeat IC version %d.%d\n", hb_srv_version >> 16, hb_srv_version & 0xFFFF); } -- cgit v1.2.3 From 9ecb839f16a353e71175981a098842fda4ddc7f1 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 27 Jan 2017 16:32:38 +0200 Subject: mei: bus: cancel and disable callback after release call A driver on the mei bus may rely on the availability of the receive callback during driver remove() call, e.g. mei_wdt. Move callbacks dismantling after the remove() call to unblock that scenario. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 2d9c5dd06e42..3bb1f1500f6b 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -665,6 +665,10 @@ static int mei_cl_device_remove(struct device *dev) if (!cldev || !dev->driver) return 0; + cldrv = to_mei_cl_driver(dev->driver); + if (cldrv->remove) + ret = cldrv->remove(cldev); + if (cldev->rx_cb) { cancel_work_sync(&cldev->rx_work); cldev->rx_cb = NULL; @@ -674,10 +678,6 @@ static int mei_cl_device_remove(struct device *dev) cldev->notif_cb = NULL; } - cldrv = to_mei_cl_driver(dev->driver); - if (cldrv->remove) - ret = cldrv->remove(cldev); - module_put(THIS_MODULE); dev->driver = NULL; return ret; -- cgit v1.2.3 From 5d88246090c5e9b178eb05bcfb52bbaad2ae48f3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 27 Jan 2017 16:32:39 +0200 Subject: mei: bus: prevent hardware module unload if device on bus is active The hardware module should not be unloaded if the bus has active devices. Get get_/put_ bus parent module upon client device connection/disconnection, to prevent the hardware managing module to disappear underneath. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 31 +++++++++++++++++++++++++++++++ drivers/misc/mei/client.c | 10 ++++++++-- drivers/misc/mei/mei_dev.h | 2 ++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 3bb1f1500f6b..b67f15b53067 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -541,6 +541,37 @@ out: } EXPORT_SYMBOL_GPL(mei_cldev_disable); +/** + * mei_cl_bus_module_get - acquire module of the underlying + * hw module. + * + * @cl: host client + * + * Return: true on success; false if the module was removed. + */ +bool mei_cl_bus_module_get(struct mei_cl *cl) +{ + struct mei_cl_device *cldev = cl->cldev; + + if (!cldev) + return true; + + return try_module_get(cldev->bus->dev->driver->owner); +} + +/** + * mei_cl_bus_module_put - release the underlying hw module. + * + * @cl: host client + */ +void mei_cl_bus_module_put(struct mei_cl *cl) +{ + struct mei_cl_device *cldev = cl->cldev; + + if (cldev) + module_put(cldev->bus->dev->driver->owner); +} + /** * mei_cl_device_find - find matching entry in the driver id table * diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index da1c0703910a..619b4702347f 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -774,6 +774,8 @@ static void mei_cl_set_disconnected(struct mei_cl *cl) cl->tx_flow_ctrl_creds = 0; cl->timer_count = 0; + mei_cl_bus_module_put(cl); + if (!cl->me_cl) return; @@ -1077,13 +1079,17 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, dev = cl->dev; + if (!mei_cl_bus_module_get(cl)) + return -ENODEV; + rets = mei_cl_set_connecting(cl, me_cl); if (rets) - return rets; + goto nortpm; if (mei_cl_is_fixed_address(cl)) { cl->state = MEI_FILE_CONNECTED; - return 0; + rets = 0; + goto nortpm; } rets = pm_runtime_get(dev->dev); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 8dadb98662a9..33ff4b7e356d 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -328,6 +328,8 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length, bool mei_cl_bus_rx_event(struct mei_cl *cl); bool mei_cl_bus_notify_event(struct mei_cl *cl); void mei_cl_bus_remove_devices(struct mei_device *bus); +bool mei_cl_bus_module_get(struct mei_cl *cl); +void mei_cl_bus_module_put(struct mei_cl *cl); int mei_cl_bus_init(void); void mei_cl_bus_exit(void); -- cgit v1.2.3 From 57080e88240a46c62cf7538de009545eb03d2568 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 27 Jan 2017 16:32:40 +0200 Subject: mei: bus: unregister callbacks upon me client disable call Stop and unregister receive and notification callbacks from the disable function, to allow its later re-enablement. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index b67f15b53067..cb3e9e0ca049 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -498,6 +498,25 @@ out: } EXPORT_SYMBOL_GPL(mei_cldev_enable); +/** + * mei_cldev_unregister_callbacks - internal wrapper for unregistering + * callbacks. + * + * @cldev: client device + */ +static void mei_cldev_unregister_callbacks(struct mei_cl_device *cldev) +{ + if (cldev->rx_cb) { + cancel_work_sync(&cldev->rx_work); + cldev->rx_cb = NULL; + } + + if (cldev->notif_cb) { + cancel_work_sync(&cldev->notif_work); + cldev->notif_cb = NULL; + } +} + /** * mei_cldev_disable - disable me client device * disconnect form the me client @@ -519,6 +538,8 @@ int mei_cldev_disable(struct mei_cl_device *cldev) bus = cldev->bus; + mei_cldev_unregister_callbacks(cldev); + mutex_lock(&bus->device_lock); if (!mei_cl_is_connected(cl)) { @@ -700,14 +721,7 @@ static int mei_cl_device_remove(struct device *dev) if (cldrv->remove) ret = cldrv->remove(cldev); - if (cldev->rx_cb) { - cancel_work_sync(&cldev->rx_work); - cldev->rx_cb = NULL; - } - if (cldev->notif_cb) { - cancel_work_sync(&cldev->notif_work); - cldev->notif_cb = NULL; - } + mei_cldev_unregister_callbacks(cldev); module_put(THIS_MODULE); dev->driver = NULL; -- cgit v1.2.3 From 7c47d2ca0feca767479329da23523ed798acb854 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 27 Jan 2017 16:32:41 +0200 Subject: mei: return error on notification request to a disconnected client Request for a notification from a disconnected client will be ignored silently by the FW but the caller should know that the operation hasn't succeeded. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 619b4702347f..ecfaef93457d 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1331,6 +1331,9 @@ int mei_cl_notify_request(struct mei_cl *cl, return -EOPNOTSUPP; } + if (!mei_cl_is_connected(cl)) + return -ENODEV; + rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { pm_runtime_put_noidle(dev->dev); -- cgit v1.2.3 From 6c0d6701a551ac344e63935535db0494110c5f4e Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 27 Jan 2017 16:32:42 +0200 Subject: mei: abort waiting for notification on unsupported HW On legacy HW, pre Skylake, the notifications are not supported, return -EOPNOTSUPP in mei_cl_notify_get and prevent waiting indefinitely. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index ecfaef93457d..923fad39535c 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1428,6 +1428,11 @@ int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev) dev = cl->dev; + if (!dev->hbm_f_ev_supported) { + cl_dbg(dev, cl, "notifications not supported\n"); + return -EOPNOTSUPP; + } + if (!mei_cl_is_connected(cl)) return -ENODEV; -- cgit v1.2.3 From 6537ae2f20412dd1c7464272dfff77d5afb50d25 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 27 Jan 2017 16:32:43 +0200 Subject: mei: amthif: clean command queue upon disconnection In order to prevent memory leak clean up the amthif command queue upon disconnection. The issue may happen only on error path as the command queue is cleaned upon file descriptor close. And remove the cleanup from mei_cl_flush_queues as this code is never reached for amthif client. Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 923fad39535c..d9e3e68bab89 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -546,7 +546,6 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) mei_io_list_free(&cl->dev->write_waiting_list, cl); mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); - mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); mei_cl_read_cb_flush(cl, fp); @@ -769,6 +768,7 @@ static void mei_cl_set_disconnected(struct mei_cl *cl) mei_io_list_free(&dev->write_waiting_list, cl); mei_io_list_flush(&dev->ctrl_rd_list, cl); mei_io_list_flush(&dev->ctrl_wr_list, cl); + mei_io_list_free(&dev->amthif_cmd_list, cl); mei_cl_wake_all(cl); cl->rx_flow_ctrl_creds = 0; cl->tx_flow_ctrl_creds = 0; -- cgit v1.2.3 From 9ecdbc58f96bd145669510822bc44eafde3c7351 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 27 Jan 2017 16:32:44 +0200 Subject: mei: amthif: allow the read completion after close The amthif client connection is shared over multiple file descriptors. In case a file descriptor was closed immediately after a write, the read credits should be still available so the pending reads can be cleaned from the queue, hence we cannot drop the control read list, this is done only upon connection close. Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 466afb2611c6..2d553cd6d4d0 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -353,9 +353,9 @@ int mei_amthif_release(struct mei_device *dev, struct file *file) dev->iamthif_canceled = true; } + /* Don't clean ctrl_rd_list here, the reads has to be completed */ mei_clear_list(file, &dev->amthif_cmd_list.list); mei_clear_list(file, &cl->rd_completed); - mei_clear_list(file, &dev->ctrl_rd_list.list); return 0; } -- cgit v1.2.3 From 962ff7bcec243dc5ff6dd3cbad6ed585e3177556 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 27 Jan 2017 16:32:45 +0200 Subject: mei: replace callback structures used as list head by list_head mei_dev structure used struct mei_cl_cb type variables as for holding callbacks list heads. Replace them by the actual struct list_head as there is no other info that is handled. This slims down the mei_dev structure and mostly streamline the code. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 11 +++++----- drivers/misc/mei/client.c | 52 ++++++++++++++++++++++---------------------- drivers/misc/mei/client.h | 20 +++++------------ drivers/misc/mei/hbm.c | 2 +- drivers/misc/mei/hw-me.c | 10 ++++----- drivers/misc/mei/hw-txe.c | 10 ++++----- drivers/misc/mei/init.c | 22 +++++++++---------- drivers/misc/mei/interrupt.c | 36 +++++++++++++++--------------- drivers/misc/mei/mei_dev.h | 20 ++++++++--------- 9 files changed, 85 insertions(+), 98 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 2d553cd6d4d0..366ef1fe9b90 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -132,8 +132,7 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); - cb = list_first_entry_or_null(&dev->amthif_cmd_list.list, - typeof(*cb), list); + cb = list_first_entry_or_null(&dev->amthif_cmd_list, typeof(*cb), list); if (!cb) { dev->iamthif_state = MEI_IAMTHIF_IDLE; cl->fp = NULL; @@ -167,7 +166,7 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) struct mei_device *dev = cl->dev; - list_add_tail(&cb->list, &dev->amthif_cmd_list.list); + list_add_tail(&cb->list, &dev->amthif_cmd_list); /* * The previous request is still in processing, queue this one. @@ -211,7 +210,7 @@ unsigned int mei_amthif_poll(struct file *file, poll_table *wait) * 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) + struct list_head *cmpl_list) { int ret; @@ -237,7 +236,7 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, */ int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev; int ret; @@ -354,7 +353,7 @@ int mei_amthif_release(struct mei_device *dev, struct file *file) } /* Don't clean ctrl_rd_list here, the reads has to be completed */ - mei_clear_list(file, &dev->amthif_cmd_list.list); + mei_clear_list(file, &dev->amthif_cmd_list); mei_clear_list(file, &cl->rd_completed); return 0; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index d9e3e68bab89..838a4a349f9e 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -379,17 +379,17 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, /** * __mei_io_list_flush - removes and frees cbs belonging to cl. * - * @list: an instance of our list structure + * @head: an instance of our list structure * @cl: host client, can be NULL for flushing the whole list * @free: whether to free the cbs */ -static void __mei_io_list_flush(struct mei_cl_cb *list, +static void __mei_io_list_flush(struct list_head *head, struct mei_cl *cl, bool free) { struct mei_cl_cb *cb, *next; /* enable removing everything if no cl is specified */ - list_for_each_entry_safe(cb, next, &list->list, list) { + list_for_each_entry_safe(cb, next, head, list) { if (!cl || mei_cl_cmp_id(cl, cb->cl)) { list_del_init(&cb->list); if (free) @@ -401,23 +401,23 @@ static void __mei_io_list_flush(struct mei_cl_cb *list, /** * mei_io_list_flush - removes list entry belonging to cl. * - * @list: An instance of our list structure + * @head: 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) +static inline void mei_io_list_flush(struct list_head *head, struct mei_cl *cl) { - __mei_io_list_flush(list, cl, false); + __mei_io_list_flush(head, cl, false); } /** * mei_io_list_free - removes cb belonging to cl and free them * - * @list: An instance of our list structure + * @head: An instance of our list structure * @cl: host client */ -static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) +static inline void mei_io_list_free(struct list_head *head, struct mei_cl *cl) { - __mei_io_list_flush(list, cl, true); + __mei_io_list_flush(head, cl, true); } /** @@ -479,7 +479,7 @@ struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length, if (!cb) return NULL; - list_add_tail(&cb->list, &cl->dev->ctrl_wr_list.list); + list_add_tail(&cb->list, &cl->dev->ctrl_wr_list); return cb; } @@ -831,7 +831,7 @@ static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) return ret; } - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list); cl->timer_count = MEI_CONNECT_TIMEOUT; mei_schedule_stall_timer(dev); @@ -849,7 +849,7 @@ static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) * Return: 0, OK; otherwise, error. */ int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -864,7 +864,7 @@ int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, ret = mei_cl_send_disconnect(cl, cb); if (ret) - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return ret; } @@ -986,7 +986,7 @@ static bool mei_cl_is_other_connecting(struct mei_cl *cl) dev = cl->dev; - list_for_each_entry(cb, &dev->ctrl_rd_list.list, list) { + list_for_each_entry(cb, &dev->ctrl_rd_list, list) { if (cb->fop_type == MEI_FOP_CONNECT && mei_cl_me_id(cl) == mei_cl_me_id(cb->cl)) return true; @@ -1017,7 +1017,7 @@ static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) return ret; } - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list); cl->timer_count = MEI_CONNECT_TIMEOUT; mei_schedule_stall_timer(dev); return 0; @@ -1033,7 +1033,7 @@ static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) * Return: 0, OK; otherwise, error. */ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -1051,7 +1051,7 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, rets = mei_cl_send_connect(cl, cb); if (rets) - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return rets; } @@ -1276,7 +1276,7 @@ enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req) * Return: 0 on such and error otherwise. */ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -1294,11 +1294,11 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, ret = mei_hbm_cl_notify_req(dev, cl, request); if (ret) { cl->status = ret; - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return ret; } - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list); return 0; } @@ -1353,7 +1353,7 @@ int mei_cl_notify_request(struct mei_cl *cl, rets = -ENODEV; goto out; } - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list); } mutex_unlock(&dev->device_lock); @@ -1533,7 +1533,7 @@ nortpm: * 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) + struct list_head *cmpl_list) { struct mei_device *dev; struct mei_msg_data *buf; @@ -1605,13 +1605,13 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, } if (mei_hdr.msg_complete) - list_move_tail(&cb->list, &dev->write_waiting_list.list); + list_move_tail(&cb->list, &dev->write_waiting_list); return 0; err: cl->status = rets; - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return rets; } @@ -1701,9 +1701,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb) out: if (mei_hdr.msg_complete) - list_add_tail(&cb->list, &dev->write_waiting_list.list); + list_add_tail(&cb->list, &dev->write_waiting_list); else - list_add_tail(&cb->list, &dev->write_list.list); + list_add_tail(&cb->list, &dev->write_list); cb = NULL; if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index ab92a542661b..f48cfa026268 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -84,16 +84,6 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl) */ void mei_io_cb_free(struct mei_cl_cb *priv_cb); -/** - * mei_io_list_init - Sets up a queue list. - * - * @list: An instance cl callback structure - */ -static inline void mei_io_list_init(struct mei_cl_cb *list) -{ - INIT_LIST_HEAD(&list->list); -} - /* * MEI Host Client Functions */ @@ -209,17 +199,17 @@ static inline u8 mei_cl_host_addr(const struct mei_cl *cl) int mei_cl_disconnect(struct mei_cl *cl); int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, const struct file *file); int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp); int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb); @@ -230,7 +220,7 @@ enum mei_cb_file_ops mei_cl_notify_req2fop(u8 request); int mei_cl_notify_request(struct mei_cl *cl, const struct file *file, u8 request); int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev); void mei_cl_notify(struct mei_cl *cl); diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 25b4a1ba522d..ba3a774c8d71 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -815,7 +815,7 @@ static void mei_hbm_cl_res(struct mei_device *dev, struct mei_cl_cb *cb, *next; cl = NULL; - list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) { + list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list, list) { cl = cb->cl; diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index a05375a3338a..06a579952e3f 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1189,7 +1189,7 @@ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *) dev_id; - struct mei_cl_cb complete_list; + struct list_head cmpl_list; s32 slots; u32 hcsr; int rets = 0; @@ -1201,7 +1201,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) hcsr = mei_hcsr_read(dev); me_intr_clear(dev, hcsr); - mei_io_list_init(&complete_list); + INIT_LIST_HEAD(&cmpl_list); /* check if ME wants a reset */ if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { @@ -1227,7 +1227,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) slots = mei_count_full_read_slots(dev); while (slots > 0) { dev_dbg(dev->dev, "slots to read = %08x\n", slots); - rets = mei_irq_read_handler(dev, &complete_list, &slots); + rets = mei_irq_read_handler(dev, &cmpl_list, &slots); /* There is a race between ME write and interrupt delivery: * Not all data is always available immediately after the * interrupt, so try to read again on the next interrupt. @@ -1252,11 +1252,11 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) */ if (dev->pg_event != MEI_PG_EVENT_WAIT && dev->pg_event != MEI_PG_EVENT_RECEIVED) { - rets = mei_irq_write_handler(dev, &complete_list); + rets = mei_irq_write_handler(dev, &cmpl_list); dev->hbuf_is_ready = mei_hbuf_is_ready(dev); } - mei_irq_compl_handler(dev, &complete_list); + mei_irq_compl_handler(dev, &cmpl_list); end: dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index e9f8c0aeec13..6259b11d440c 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -1057,7 +1057,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *) dev_id; struct mei_txe_hw *hw = to_txe_hw(dev); - struct mei_cl_cb complete_list; + struct list_head cmpl_list; s32 slots; int rets = 0; @@ -1069,7 +1069,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) /* initialize our complete list */ mutex_lock(&dev->device_lock); - mei_io_list_init(&complete_list); + INIT_LIST_HEAD(&cmpl_list); if (pci_dev_msi_enabled(to_pci_dev(dev->dev))) mei_txe_check_and_ack_intrs(dev, true); @@ -1126,7 +1126,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) slots = mei_count_full_read_slots(dev); if (test_and_clear_bit(TXE_INTR_OUT_DB_BIT, &hw->intr_cause)) { /* Read from TXE */ - rets = mei_irq_read_handler(dev, &complete_list, &slots); + rets = mei_irq_read_handler(dev, &cmpl_list, &slots); if (rets && dev->dev_state != MEI_DEV_RESETTING) { dev_err(dev->dev, "mei_irq_read_handler ret = %d.\n", rets); @@ -1144,14 +1144,14 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) if (hw->aliveness && dev->hbuf_is_ready) { /* get the real register value */ dev->hbuf_is_ready = mei_hbuf_is_ready(dev); - rets = mei_irq_write_handler(dev, &complete_list); + rets = mei_irq_write_handler(dev, &cmpl_list); if (rets && rets != -EMSGSIZE) dev_err(dev->dev, "mei_irq_write_handler ret = %d.\n", rets); dev->hbuf_is_ready = mei_hbuf_is_ready(dev); } - mei_irq_compl_handler(dev, &complete_list); + mei_irq_compl_handler(dev, &cmpl_list); end: dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 41e5760a6886..cfb1cdf176fa 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -349,16 +349,16 @@ EXPORT_SYMBOL_GPL(mei_stop); bool mei_write_is_idle(struct mei_device *dev) { bool idle = (dev->dev_state == MEI_DEV_ENABLED && - list_empty(&dev->ctrl_wr_list.list) && - list_empty(&dev->write_list.list) && - list_empty(&dev->write_waiting_list.list)); + list_empty(&dev->ctrl_wr_list) && + list_empty(&dev->write_list) && + list_empty(&dev->write_waiting_list)); dev_dbg(dev->dev, "write pg: is idle[%d] state=%s ctrl=%01d write=%01d wwait=%01d\n", idle, mei_dev_state_str(dev->dev_state), - list_empty(&dev->ctrl_wr_list.list), - list_empty(&dev->write_list.list), - list_empty(&dev->write_waiting_list.list)); + list_empty(&dev->ctrl_wr_list), + list_empty(&dev->write_list), + list_empty(&dev->write_waiting_list)); return idle; } @@ -388,17 +388,17 @@ void mei_device_init(struct mei_device *dev, dev->dev_state = MEI_DEV_INITIALIZING; dev->reset_count = 0; - mei_io_list_init(&dev->write_list); - mei_io_list_init(&dev->write_waiting_list); - mei_io_list_init(&dev->ctrl_wr_list); - mei_io_list_init(&dev->ctrl_rd_list); + INIT_LIST_HEAD(&dev->write_list); + INIT_LIST_HEAD(&dev->write_waiting_list); + INIT_LIST_HEAD(&dev->ctrl_wr_list); + INIT_LIST_HEAD(&dev->ctrl_rd_list); INIT_DELAYED_WORK(&dev->timer_work, mei_timer); INIT_WORK(&dev->reset_work, mei_reset_work); INIT_WORK(&dev->bus_rescan_work, mei_cl_bus_rescan_work); INIT_LIST_HEAD(&dev->iamthif_cl.link); - mei_io_list_init(&dev->amthif_cmd_list); + INIT_LIST_HEAD(&dev->amthif_cmd_list); bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); dev->open_handle_count = 0; diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index b584749bcc4a..406e9e2b2fff 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -35,14 +35,14 @@ * for the completed callbacks * * @dev: mei device - * @compl_list: list of completed cbs + * @cmpl_list: list of completed cbs */ -void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) +void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list) { struct mei_cl_cb *cb, *next; struct mei_cl *cl; - list_for_each_entry_safe(cb, next, &compl_list->list, list) { + list_for_each_entry_safe(cb, next, cmpl_list, list) { cl = cb->cl; list_del_init(&cb->list); @@ -92,13 +92,13 @@ void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr) * * @cl: reading client * @mei_hdr: header of mei client message - * @complete_list: completion list + * @cmpl_list: completion list * * Return: always 0 */ int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, - struct mei_cl_cb *complete_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; struct mei_cl_cb *cb; @@ -144,7 +144,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, if (mei_hdr->msg_complete) { cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx); - list_move_tail(&cb->list, &complete_list->list); + list_move_tail(&cb->list, cmpl_list); } else { pm_runtime_mark_last_busy(dev->dev); pm_request_autosuspend(dev->dev); @@ -154,7 +154,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, discard: if (cb) - list_move_tail(&cb->list, &complete_list->list); + list_move_tail(&cb->list, cmpl_list); mei_irq_discard_msg(dev, mei_hdr); return 0; } @@ -169,7 +169,7 @@ discard: * 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) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -183,7 +183,7 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, return -EMSGSIZE; ret = mei_hbm_cl_disconnect_rsp(dev, cl); - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return ret; } @@ -199,7 +199,7 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, * 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) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -219,7 +219,7 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, if (ret) { cl->status = ret; cb->buf_idx = 0; - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return ret; } @@ -249,7 +249,7 @@ static inline bool hdr_is_fixed(struct mei_msg_hdr *mei_hdr) * Return: 0 on success, <0 on failure. */ int mei_irq_read_handler(struct mei_device *dev, - struct mei_cl_cb *cmpl_list, s32 *slots) + struct list_head *cmpl_list, s32 *slots) { struct mei_msg_hdr *mei_hdr; struct mei_cl *cl; @@ -347,12 +347,11 @@ EXPORT_SYMBOL_GPL(mei_irq_read_handler); * * Return: 0 on success, <0 on failure. */ -int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) +int mei_irq_write_handler(struct mei_device *dev, struct list_head *cmpl_list) { struct mei_cl *cl; struct mei_cl_cb *cb, *next; - struct mei_cl_cb *list; s32 slots; int ret; @@ -367,19 +366,18 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) /* complete all waiting for write CB */ 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) { + list_for_each_entry_safe(cb, next, &dev->write_waiting_list, list) { cl = cb->cl; cl->status = 0; cl_dbg(dev, cl, "MEI WRITE COMPLETE\n"); cl->writing_state = MEI_WRITE_COMPLETE; - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); } /* complete control write list CB */ dev_dbg(dev->dev, "complete control write list cb.\n"); - list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) { + list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list, list) { cl = cb->cl; switch (cb->fop_type) { case MEI_FOP_DISCONNECT: @@ -423,7 +421,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) } /* complete write list CB */ dev_dbg(dev->dev, "complete write list cb.\n"); - list_for_each_entry_safe(cb, next, &dev->write_list.list, list) { + list_for_each_entry_safe(cb, next, &dev->write_list, list) { cl = cb->cl; if (cl == &dev->iamthif_cl) ret = mei_amthif_irq_write(cl, cb, cmpl_list); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 33ff4b7e356d..d41aac53a2ac 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -441,10 +441,10 @@ struct mei_device { struct cdev cdev; int minor; - 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; + struct list_head write_list; + struct list_head write_waiting_list; + struct list_head ctrl_wr_list; + struct list_head ctrl_rd_list; struct list_head file_list; long open_handle_count; @@ -501,7 +501,7 @@ struct mei_device { bool override_fixed_address; /* amthif list for cmd waiting */ - struct mei_cl_cb amthif_cmd_list; + struct list_head amthif_cmd_list; struct mei_cl iamthif_cl; long iamthif_open_count; u32 iamthif_stall_timer; @@ -573,10 +573,10 @@ void mei_cancel_work(struct mei_device *dev); void mei_timer(struct work_struct *work); void mei_schedule_stall_timer(struct mei_device *dev); int mei_irq_read_handler(struct mei_device *dev, - struct mei_cl_cb *cmpl_list, s32 *slots); + struct list_head *cmpl_list, s32 *slots); -int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list); -void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list); +int mei_irq_write_handler(struct mei_device *dev, struct list_head *cmpl_list); +void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list); /* * AMTHIF - AMT Host Interface Functions @@ -592,12 +592,12 @@ int mei_amthif_release(struct mei_device *dev, struct file *file); int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_amthif_run_next_cmd(struct mei_device *dev); int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, - struct mei_cl_cb *complete_list); + struct list_head *cmpl_list); int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); /* -- cgit v1.2.3 From f046192d98c9a929c3eefdd65b885000d341d969 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 27 Jan 2017 16:32:46 +0200 Subject: mei: revamp io list cleanup function. Specify in function names by which object is the io list filtered: cl for a client and fp for file descriptor. In that course a code duplication is resolved by dropping mei_cl_read_cb_flush and mei_clear_list and using mei_io_list_free_fp function. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 34 ++++--------------- drivers/misc/mei/client.c | 83 ++++++++++++++++++++++------------------------- drivers/misc/mei/client.h | 2 +- 3 files changed, 47 insertions(+), 72 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 366ef1fe9b90..0e7406ccb6dd 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -310,51 +310,31 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) } } -/** - * mei_clear_list - removes all callbacks associated with file - * from mei_cb_list - * - * @file: file structure - * @mei_cb_list: callbacks list - * - * mei_clear_list is called to clear resources associated with file - * when application calls close function or Ctrl-C was pressed - */ -static void mei_clear_list(const struct file *file, - struct list_head *mei_cb_list) -{ - struct mei_cl_cb *cb, *next; - - list_for_each_entry_safe(cb, next, mei_cb_list, list) - if (file == cb->fp) - mei_io_cb_free(cb); -} - /** * mei_amthif_release - the release function * * @dev: device structure -* @file: pointer to file structure +* @fp: pointer to file structure * * Return: 0 on success, <0 on error */ -int mei_amthif_release(struct mei_device *dev, struct file *file) +int mei_amthif_release(struct mei_device *dev, struct file *fp) { - struct mei_cl *cl = file->private_data; + struct mei_cl *cl = fp->private_data; if (dev->iamthif_open_count > 0) dev->iamthif_open_count--; - if (cl->fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) { + if (cl->fp == fp && dev->iamthif_state != MEI_IAMTHIF_IDLE) { dev_dbg(dev->dev, "amthif canceled iamthif state %d\n", - dev->iamthif_state); + dev->iamthif_state); dev->iamthif_canceled = true; } /* Don't clean ctrl_rd_list here, the reads has to be completed */ - mei_clear_list(file, &dev->amthif_cmd_list); - mei_clear_list(file, &cl->rd_completed); + mei_io_list_free_fp(&dev->amthif_cmd_list, fp); + mei_io_list_free_fp(&cl->rd_completed, fp); return 0; } diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 838a4a349f9e..68fe37b5bc52 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -377,14 +377,14 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, } /** - * __mei_io_list_flush - removes and frees cbs belonging to cl. + * __mei_io_list_flush_cl - removes and frees cbs belonging to cl. * * @head: an instance of our list structure * @cl: host client, can be NULL for flushing the whole list * @free: whether to free the cbs */ -static void __mei_io_list_flush(struct list_head *head, - struct mei_cl *cl, bool free) +static void __mei_io_list_flush_cl(struct list_head *head, + const struct mei_cl *cl, bool free) { struct mei_cl_cb *cb, *next; @@ -399,25 +399,42 @@ static void __mei_io_list_flush(struct list_head *head, } /** - * mei_io_list_flush - removes list entry belonging to cl. + * mei_io_list_flush_cl - removes list entry belonging to cl. * * @head: An instance of our list structure * @cl: host client */ -static inline void mei_io_list_flush(struct list_head *head, struct mei_cl *cl) +static inline void mei_io_list_flush_cl(struct list_head *head, + const struct mei_cl *cl) { - __mei_io_list_flush(head, cl, false); + __mei_io_list_flush_cl(head, cl, false); } /** - * mei_io_list_free - removes cb belonging to cl and free them + * mei_io_list_free_cl - removes cb belonging to cl and free them * * @head: An instance of our list structure * @cl: host client */ -static inline void mei_io_list_free(struct list_head *head, struct mei_cl *cl) +static inline void mei_io_list_free_cl(struct list_head *head, + const struct mei_cl *cl) { - __mei_io_list_flush(head, cl, true); + __mei_io_list_flush_cl(head, cl, true); +} + +/** + * mei_io_list_free_fp - free cb from a list that matches file pointer + * + * @head: io list + * @fp: file pointer (matching cb file object), may be NULL + */ +void mei_io_list_free_fp(struct list_head *head, const struct file *fp) +{ + struct mei_cl_cb *cb, *next; + + list_for_each_entry_safe(cb, next, head, list) + if (!fp || fp == cb->fp) + mei_io_cb_free(cb); } /** @@ -503,27 +520,6 @@ struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp) return NULL; } -/** - * mei_cl_read_cb_flush - free client's read pending and completed cbs - * for a specific file - * - * @cl: host client - * @fp: file pointer (matching cb file object), may be NULL - */ -void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp) -{ - struct mei_cl_cb *cb, *next; - - list_for_each_entry_safe(cb, next, &cl->rd_completed, list) - if (!fp || fp == cb->fp) - mei_io_cb_free(cb); - - - list_for_each_entry_safe(cb, next, &cl->rd_pending, list) - if (!fp || fp == cb->fp) - mei_io_cb_free(cb); -} - /** * mei_cl_flush_queues - flushes queue lists belonging to cl. * @@ -542,17 +538,16 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) dev = cl->dev; cl_dbg(dev, cl, "remove list entry belonging to cl\n"); - mei_io_list_free(&cl->dev->write_list, cl); - mei_io_list_free(&cl->dev->write_waiting_list, cl); - mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); - mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); - - mei_cl_read_cb_flush(cl, fp); + mei_io_list_free_cl(&cl->dev->write_list, cl); + mei_io_list_free_cl(&cl->dev->write_waiting_list, cl); + mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl); + mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl); + mei_io_list_free_fp(&cl->rd_pending, fp); + mei_io_list_free_fp(&cl->rd_completed, fp); return 0; } - /** * mei_cl_init - initializes cl. * @@ -764,11 +759,11 @@ static void mei_cl_set_disconnected(struct mei_cl *cl) return; cl->state = MEI_FILE_DISCONNECTED; - mei_io_list_free(&dev->write_list, cl); - mei_io_list_free(&dev->write_waiting_list, cl); - mei_io_list_flush(&dev->ctrl_rd_list, cl); - mei_io_list_flush(&dev->ctrl_wr_list, cl); - mei_io_list_free(&dev->amthif_cmd_list, cl); + mei_io_list_free_cl(&dev->write_list, cl); + mei_io_list_free_cl(&dev->write_waiting_list, cl); + mei_io_list_flush_cl(&dev->ctrl_rd_list, cl); + mei_io_list_flush_cl(&dev->ctrl_wr_list, cl); + mei_io_list_free_cl(&dev->amthif_cmd_list, cl); mei_cl_wake_all(cl); cl->rx_flow_ctrl_creds = 0; cl->tx_flow_ctrl_creds = 0; @@ -1123,8 +1118,8 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, if (!mei_cl_is_connected(cl)) { if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) { - mei_io_list_flush(&dev->ctrl_rd_list, cl); - mei_io_list_flush(&dev->ctrl_wr_list, cl); + mei_io_list_flush_cl(&dev->ctrl_rd_list, cl); + mei_io_list_flush_cl(&dev->ctrl_wr_list, cl); /* ignore disconnect return valuue; * in case of failure reset will be invoked */ diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index f48cfa026268..545ae319ba90 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -83,6 +83,7 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl) * MEI IO Functions */ void mei_io_cb_free(struct mei_cl_cb *priv_cb); +void mei_io_list_free_fp(struct list_head *head, const struct file *fp); /* * MEI Host Client Functions @@ -99,7 +100,6 @@ struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev); struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp); -void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp); struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, enum mei_cb_file_ops type, const struct file *fp); -- cgit v1.2.3 From f8a096059fc5f719301d314e5d7451f1bab5032a Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 26 Jan 2017 17:16:26 +0200 Subject: mei: simplify error handling via devres function. Use devm_ and pcim_ functions to make error handling simpler and code smaller and tidier. Based on original patch by mei: me: use managed functions pcim_* and devm_* https://lkml.org/lkml/2016/2/1/339 Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 6 ++-- drivers/misc/mei/hw-txe.c | 4 +-- drivers/misc/mei/hw-txe.h | 2 +- drivers/misc/mei/pci-me.c | 50 +++++++-------------------------- drivers/misc/mei/pci-txe.c | 69 +++++++--------------------------------------- 5 files changed, 26 insertions(+), 105 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 06a579952e3f..befeac52c349 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1389,7 +1389,7 @@ const struct mei_cfg mei_me_pch8_sps_cfg = { * @pdev: The pci device structure * @cfg: per device generation config * - * Return: The mei_device_device pointer on success, NULL on failure. + * Return: The mei_device pointer on success, NULL on failure. */ struct mei_device *mei_me_dev_init(struct pci_dev *pdev, const struct mei_cfg *cfg) @@ -1397,8 +1397,8 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev, struct mei_device *dev; struct mei_me_hw *hw; - dev = kzalloc(sizeof(struct mei_device) + - sizeof(struct mei_me_hw), GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) + + sizeof(struct mei_me_hw), GFP_KERNEL); if (!dev) return NULL; hw = to_me_hw(dev); diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index 6259b11d440c..24e4a4c96606 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -1207,8 +1207,8 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev) struct mei_device *dev; struct mei_txe_hw *hw; - dev = kzalloc(sizeof(struct mei_device) + - sizeof(struct mei_txe_hw), GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) + + sizeof(struct mei_txe_hw), GFP_KERNEL); if (!dev) return NULL; diff --git a/drivers/misc/mei/hw-txe.h b/drivers/misc/mei/hw-txe.h index ce3ed0b88b0c..e1e8b66d7648 100644 --- a/drivers/misc/mei/hw-txe.h +++ b/drivers/misc/mei/hw-txe.h @@ -45,7 +45,7 @@ * @intr_cause: translated interrupt cause */ struct mei_txe_hw { - void __iomem *mem_addr[NUM_OF_MEM_BARS]; + void __iomem * const *mem_addr; u32 aliveness; u32 readiness; u32 slots; diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index f9c6ec4b98ab..0a668fdfbbe9 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -149,18 +149,18 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return -ENODEV; /* enable pci dev */ - err = pci_enable_device(pdev); + err = pcim_enable_device(pdev); if (err) { dev_err(&pdev->dev, "failed to enable pci device.\n"); goto end; } /* set PCI host mastering */ pci_set_master(pdev); - /* pci request regions for mei driver */ - err = pci_request_regions(pdev, KBUILD_MODNAME); + /* pci request regions and mapping IO device memory for mei driver */ + err = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); if (err) { dev_err(&pdev->dev, "failed to get pci regions.\n"); - goto disable_device; + goto end; } if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) || @@ -173,24 +173,18 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } if (err) { dev_err(&pdev->dev, "No usable DMA configuration, aborting\n"); - goto release_regions; + goto end; } - /* allocates and initializes the mei dev structure */ dev = mei_me_dev_init(pdev, cfg); if (!dev) { err = -ENOMEM; - goto release_regions; + goto end; } hw = to_me_hw(dev); - /* mapping IO device memory */ - hw->mem_addr = pci_iomap(pdev, 0, 0); - if (!hw->mem_addr) { - dev_err(&pdev->dev, "mapping I/O device memory failure.\n"); - err = -ENOMEM; - goto free_device; - } + hw->mem_addr = pcim_iomap_table(pdev)[0]; + pci_enable_msi(pdev); /* request and enable interrupt */ @@ -203,7 +197,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) { dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n", pdev->irq); - goto disable_msi; + goto end; } if (mei_start(dev)) { @@ -242,15 +236,6 @@ release_irq: mei_cancel_work(dev); mei_disable_interrupts(dev); free_irq(pdev->irq, dev); -disable_msi: - pci_disable_msi(pdev); - pci_iounmap(pdev, hw->mem_addr); -free_device: - kfree(dev); -release_regions: - pci_release_regions(pdev); -disable_device: - pci_disable_device(pdev); end: dev_err(&pdev->dev, "initialization failed.\n"); return err; @@ -267,7 +252,6 @@ end: static void mei_me_remove(struct pci_dev *pdev) { struct mei_device *dev; - struct mei_me_hw *hw; dev = pci_get_drvdata(pdev); if (!dev) @@ -276,33 +260,19 @@ static void mei_me_remove(struct pci_dev *pdev) if (mei_pg_is_enabled(dev)) pm_runtime_get_noresume(&pdev->dev); - hw = to_me_hw(dev); - - dev_dbg(&pdev->dev, "stop\n"); mei_stop(dev); if (!pci_dev_run_wake(pdev)) mei_me_unset_pm_domain(dev); - /* disable interrupts */ mei_disable_interrupts(dev); free_irq(pdev->irq, dev); - pci_disable_msi(pdev); - - if (hw->mem_addr) - pci_iounmap(pdev, hw->mem_addr); mei_deregister(dev); - - kfree(dev); - - pci_release_regions(pdev); - pci_disable_device(pdev); - - } + #ifdef CONFIG_PM_SLEEP static int mei_me_pci_suspend(struct device *device) { diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index 58ffd30dcc91..fe088b40daf9 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -52,17 +52,6 @@ static inline void mei_txe_set_pm_domain(struct mei_device *dev) {} static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {} #endif /* CONFIG_PM */ -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]); - hw->mem_addr[i] = NULL; - } - } -} /** * mei_txe_probe - Device Initialization Routine * @@ -75,22 +64,22 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct mei_device *dev; struct mei_txe_hw *hw; + const int mask = BIT(SEC_BAR) | BIT(BRIDGE_BAR); int err; - int i; /* enable pci dev */ - err = pci_enable_device(pdev); + err = pcim_enable_device(pdev); if (err) { dev_err(&pdev->dev, "failed to enable pci device.\n"); goto end; } /* set PCI host mastering */ pci_set_master(pdev); - /* pci request regions for mei driver */ - err = pci_request_regions(pdev, KBUILD_MODNAME); + /* pci request regions and mapping IO device memory for mei driver */ + err = pcim_iomap_regions(pdev, mask, KBUILD_MODNAME); if (err) { dev_err(&pdev->dev, "failed to get pci regions.\n"); - goto disable_device; + goto end; } err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); @@ -98,7 +87,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (err) { dev_err(&pdev->dev, "No suitable DMA available.\n"); - goto release_regions; + goto end; } } @@ -106,20 +95,10 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev = mei_txe_dev_init(pdev); if (!dev) { err = -ENOMEM; - goto release_regions; + goto end; } hw = to_txe_hw(dev); - - /* mapping IO device memory */ - for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) { - hw->mem_addr[i] = pci_iomap(pdev, i, 0); - if (!hw->mem_addr[i]) { - dev_err(&pdev->dev, "mapping I/O device memory failure.\n"); - err = -ENOMEM; - goto free_device; - } - } - + hw->mem_addr = pcim_iomap_table(pdev); pci_enable_msi(pdev); @@ -140,7 +119,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) { dev_err(&pdev->dev, "mei: request_threaded_irq failure. irq = %d\n", pdev->irq); - goto free_device; + goto end; } if (mei_start(dev)) { @@ -173,23 +152,9 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) stop: mei_stop(dev); release_irq: - mei_cancel_work(dev); - - /* disable interrupts */ mei_disable_interrupts(dev); - free_irq(pdev->irq, dev); - pci_disable_msi(pdev); - -free_device: - mei_txe_pci_iounmap(pdev, hw); - - kfree(dev); -release_regions: - pci_release_regions(pdev); -disable_device: - pci_disable_device(pdev); end: dev_err(&pdev->dev, "initialization failed.\n"); return err; @@ -206,38 +171,24 @@ end: static void mei_txe_remove(struct pci_dev *pdev) { struct mei_device *dev; - struct mei_txe_hw *hw; dev = pci_get_drvdata(pdev); if (!dev) { - dev_err(&pdev->dev, "mei: dev =NULL\n"); + dev_err(&pdev->dev, "mei: dev == NULL\n"); return; } pm_runtime_get_noresume(&pdev->dev); - hw = to_txe_hw(dev); - mei_stop(dev); if (!pci_dev_run_wake(pdev)) mei_txe_unset_pm_domain(dev); - /* disable interrupts */ mei_disable_interrupts(dev); free_irq(pdev->irq, dev); - pci_disable_msi(pdev); - - pci_set_drvdata(pdev, NULL); - - mei_txe_pci_iounmap(pdev, hw); mei_deregister(dev); - - kfree(dev); - - pci_release_regions(pdev); - pci_disable_device(pdev); } -- cgit v1.2.3 From cadf8106661c061ab5041282a8e088de4e470526 Mon Sep 17 00:00:00 2001 From: Alexander Dahl Date: Sat, 28 Jan 2017 10:45:32 +0100 Subject: doc: convert UIO howto from docbook to sphinx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Converted with tmplcvt. Only some tiny things needed manual fixing. Signed-off-by: Alexander Dahl Cc: Hans-Jürgen Koch Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/Makefile | 2 +- Documentation/DocBook/uio-howto.tmpl | 1112 -------------------------------- Documentation/driver-api/index.rst | 1 + Documentation/driver-api/uio-howto.rst | 705 ++++++++++++++++++++ MAINTAINERS | 2 +- 5 files changed, 708 insertions(+), 1114 deletions(-) delete mode 100644 Documentation/DocBook/uio-howto.tmpl create mode 100644 Documentation/driver-api/uio-howto.rst diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index a6eb7dcd4dd5..5fd8f5effd0c 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -11,7 +11,7 @@ DOCBOOKS := z8530book.xml \ writing_usb_driver.xml networking.xml \ kernel-api.xml filesystems.xml lsm.xml kgdb.xml \ gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \ - genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \ + genericirq.xml s390-drivers.xml scsi.xml \ sh.xml regulator.xml w1.xml \ writing_musb_glue_layer.xml iio.xml diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl deleted file mode 100644 index 5210f8a577c6..000000000000 --- a/Documentation/DocBook/uio-howto.tmpl +++ /dev/null @@ -1,1112 +0,0 @@ - - - - - -The Userspace I/O HOWTO - - - Hans-Jürgen - Koch - Linux developer, Linutronix - - - Linutronix - - -
- hjk@hansjkoch.de -
-
-
- - - 2006-2008 - Hans-Jürgen Koch. - - - 2009 - Red Hat Inc, Michael S. Tsirkin (mst@redhat.com) - - - - -This documentation is Free Software licensed under the terms of the -GPL version 2. - - - -2006-12-11 - - - This HOWTO describes concept and usage of Linux kernel's - Userspace I/O system. - - - - - 0.10 - 2016-10-17 - sch - Added generic hyperv driver - - - - 0.9 - 2009-07-16 - mst - Added generic pci driver - - - - 0.8 - 2008-12-24 - hjk - Added name attributes in mem and portio sysfs directories. - - - - 0.7 - 2008-12-23 - hjk - Added generic platform drivers and offset attribute. - - - 0.6 - 2008-12-05 - hjk - Added description of portio sysfs attributes. - - - 0.5 - 2008-05-22 - hjk - Added description of write() function. - - - 0.4 - 2007-11-26 - hjk - Removed section about uio_dummy. - - - 0.3 - 2007-04-29 - hjk - Added section about userspace drivers. - - - 0.2 - 2007-02-13 - hjk - Update after multiple mappings were added. - - - 0.1 - 2006-12-11 - hjk - First draft. - - -
- - - -About this document - - - -Translations - -If you know of any translations for this document, or you are -interested in translating it, please email me -hjk@hansjkoch.de. - - - - -Preface - - For many types of devices, creating a Linux kernel driver is - overkill. All that is really needed is some way to handle an - interrupt and provide access to the memory space of the - device. The logic of controlling the device does not - necessarily have to be within the kernel, as the device does - not need to take advantage of any of other resources that the - kernel provides. One such common class of devices that are - like this are for industrial I/O cards. - - - To address this situation, the userspace I/O system (UIO) was - designed. For typical industrial I/O cards, only a very small - kernel module is needed. The main part of the driver will run in - user space. This simplifies development and reduces the risk of - serious bugs within a kernel module. - - - Please note that UIO is not an universal driver interface. Devices - that are already handled well by other kernel subsystems (like - networking or serial or USB) are no candidates for an UIO driver. - Hardware that is ideally suited for an UIO driver fulfills all of - the following: - - - - The device has memory that can be mapped. The device can be - controlled completely by writing to this memory. - - - The device usually generates interrupts. - - - The device does not fit into one of the standard kernel - subsystems. - - - - - -Acknowledgments - I'd like to thank Thomas Gleixner and Benedikt Spranger of - Linutronix, who have not only written most of the UIO code, but also - helped greatly writing this HOWTO by giving me all kinds of background - information. - - - -Feedback - Find something wrong with this document? (Or perhaps something - right?) I would love to hear from you. Please email me at - hjk@hansjkoch.de. - - - - - -About UIO - -If you use UIO for your card's driver, here's what you get: - - - - only one small kernel module to write and maintain. - - - develop the main part of your driver in user space, - with all the tools and libraries you're used to. - - - bugs in your driver won't crash the kernel. - - - updates of your driver can take place without recompiling - the kernel. - - - - -How UIO works - - Each UIO device is accessed through a device file and several - sysfs attribute files. The device file will be called - /dev/uio0 for the first device, and - /dev/uio1, /dev/uio2 - and so on for subsequent devices. - - - /dev/uioX is used to access the - address space of the card. Just use - mmap() to access registers or RAM - locations of your card. - - - - Interrupts are handled by reading from - /dev/uioX. A blocking - read() from - /dev/uioX will return as soon as an - interrupt occurs. You can also use - select() on - /dev/uioX to wait for an interrupt. The - integer value read from /dev/uioX - represents the total interrupt count. You can use this number - to figure out if you missed some interrupts. - - - For some hardware that has more than one interrupt source internally, - but not separate IRQ mask and status registers, there might be - situations where userspace cannot determine what the interrupt source - was if the kernel handler disables them by writing to the chip's IRQ - register. In such a case, the kernel has to disable the IRQ completely - to leave the chip's register untouched. Now the userspace part can - determine the cause of the interrupt, but it cannot re-enable - interrupts. Another cornercase is chips where re-enabling interrupts - is a read-modify-write operation to a combined IRQ status/acknowledge - register. This would be racy if a new interrupt occurred - simultaneously. - - - To address these problems, UIO also implements a write() function. It - is normally not used and can be ignored for hardware that has only a - single interrupt source or has separate IRQ mask and status registers. - If you need it, however, a write to /dev/uioX - will call the irqcontrol() function implemented - by the driver. You have to write a 32-bit value that is usually either - 0 or 1 to disable or enable interrupts. If a driver does not implement - irqcontrol(), write() will - return with -ENOSYS. - - - - To handle interrupts properly, your custom kernel module can - provide its own interrupt handler. It will automatically be - called by the built-in handler. - - - - For cards that don't generate interrupts but need to be - polled, there is the possibility to set up a timer that - triggers the interrupt handler at configurable time intervals. - This interrupt simulation is done by calling - uio_event_notify() - from the timer's event handler. - - - - Each driver provides attributes that are used to read or write - variables. These attributes are accessible through sysfs - files. A custom kernel driver module can add its own - attributes to the device owned by the uio driver, but not added - to the UIO device itself at this time. This might change in the - future if it would be found to be useful. - - - - The following standard attributes are provided by the UIO - framework: - - - - - name: The name of your device. It is - recommended to use the name of your kernel module for this. - - - - - version: A version string defined by your - driver. This allows the user space part of your driver to deal - with different versions of the kernel module. - - - - - event: The total number of interrupts - handled by the driver since the last time the device node was - read. - - - - - These attributes appear under the - /sys/class/uio/uioX directory. Please - note that this directory might be a symlink, and not a real - directory. Any userspace code that accesses it must be able - to handle this. - - - Each UIO device can make one or more memory regions available for - memory mapping. This is necessary because some industrial I/O cards - require access to more than one PCI memory region in a driver. - - - Each mapping has its own directory in sysfs, the first mapping - appears as /sys/class/uio/uioX/maps/map0/. - Subsequent mappings create directories map1/, - map2/, and so on. These directories will only - appear if the size of the mapping is not 0. - - - Each mapX/ directory contains four read-only files - that show attributes of the memory: - - - - - name: A string identifier for this mapping. This - is optional, the string can be empty. Drivers can set this to make it - easier for userspace to find the correct mapping. - - - - - addr: The address of memory that can be mapped. - - - - - size: The size, in bytes, of the memory - pointed to by addr. - - - - - offset: The offset, in bytes, that has to be - added to the pointer returned by mmap() to get - to the actual device memory. This is important if the device's memory - is not page aligned. Remember that pointers returned by - mmap() are always page aligned, so it is good - style to always add this offset. - - - - - - From userspace, the different mappings are distinguished by adjusting - the offset parameter of the - mmap() call. To map the memory of mapping N, you - have to use N times the page size as your offset: - - -offset = N * getpagesize(); - - - - Sometimes there is hardware with memory-like regions that can not be - mapped with the technique described here, but there are still ways to - access them from userspace. The most common example are x86 ioports. - On x86 systems, userspace can access these ioports using - ioperm(), iopl(), - inb(), outb(), and similar - functions. - - - Since these ioport regions can not be mapped, they will not appear under - /sys/class/uio/uioX/maps/ like the normal memory - described above. Without information about the port regions a hardware - has to offer, it becomes difficult for the userspace part of the - driver to find out which ports belong to which UIO device. - - - To address this situation, the new directory - /sys/class/uio/uioX/portio/ was added. It only - exists if the driver wants to pass information about one or more port - regions to userspace. If that is the case, subdirectories named - port0, port1, and so on, - will appear underneath - /sys/class/uio/uioX/portio/. - - - Each portX/ directory contains four read-only - files that show name, start, size, and type of the port region: - - - - - name: A string identifier for this port region. - The string is optional and can be empty. Drivers can set it to make it - easier for userspace to find a certain port region. - - - - - start: The first port of this region. - - - - - size: The number of ports in this region. - - - - - porttype: A string describing the type of port. - - - - - - - - - - -Writing your own kernel module - - Please have a look at uio_cif.c as an - example. The following paragraphs explain the different - sections of this file. - - - -struct uio_info - - This structure tells the framework the details of your driver, - Some of the members are required, others are optional. - - - - -const char *name: Required. The name of your driver as -it will appear in sysfs. I recommend using the name of your module for this. - - - -const char *version: Required. This string appears in -/sys/class/uio/uioX/version. - - - -struct uio_mem mem[ MAX_UIO_MAPS ]: Required if you -have memory that can be mapped with mmap(). For each -mapping you need to fill one of the uio_mem structures. -See the description below for details. - - - -struct uio_port port[ MAX_UIO_PORTS_REGIONS ]: Required -if you want to pass information about ioports to userspace. For each port -region you need to fill one of the uio_port structures. -See the description below for details. - - - -long irq: Required. If your hardware generates an -interrupt, it's your modules task to determine the irq number during -initialization. If you don't have a hardware generated interrupt but -want to trigger the interrupt handler in some other way, set -irq to UIO_IRQ_CUSTOM. -If you had no interrupt at all, you could set -irq to UIO_IRQ_NONE, though this -rarely makes sense. - - - -unsigned long irq_flags: Required if you've set -irq to a hardware interrupt number. The flags given -here will be used in the call to request_irq(). - - - -int (*mmap)(struct uio_info *info, struct vm_area_struct -*vma): Optional. If you need a special -mmap() function, you can set it here. If this -pointer is not NULL, your mmap() will be called -instead of the built-in one. - - - -int (*open)(struct uio_info *info, struct inode *inode) -: Optional. You might want to have your own -open(), e.g. to enable interrupts only when your -device is actually used. - - - -int (*release)(struct uio_info *info, struct inode *inode) -: Optional. If you define your own -open(), you will probably also want a custom -release() function. - - - -int (*irqcontrol)(struct uio_info *info, s32 irq_on) -: Optional. If you need to be able to enable or disable -interrupts from userspace by writing to /dev/uioX, -you can implement this function. The parameter irq_on -will be 0 to disable interrupts and 1 to enable them. - - - - -Usually, your device will have one or more memory regions that can be mapped -to user space. For each region, you have to set up a -struct uio_mem in the mem[] array. -Here's a description of the fields of struct uio_mem: - - - - -const char *name: Optional. Set this to help identify -the memory region, it will show up in the corresponding sysfs node. - - - -int memtype: Required if the mapping is used. Set this to -UIO_MEM_PHYS if you you have physical memory on your -card to be mapped. Use UIO_MEM_LOGICAL for logical -memory (e.g. allocated with kmalloc()). There's also -UIO_MEM_VIRTUAL for virtual memory. - - - -phys_addr_t addr: Required if the mapping is used. -Fill in the address of your memory block. This address is the one that -appears in sysfs. - - - -resource_size_t size: Fill in the size of the -memory block that addr points to. If size -is zero, the mapping is considered unused. Note that you -must initialize size with zero for -all unused mappings. - - - -void *internal_addr: If you have to access this memory -region from within your kernel module, you will want to map it internally by -using something like ioremap(). Addresses -returned by this function cannot be mapped to user space, so you must not -store it in addr. Use internal_addr -instead to remember such an address. - - - - -Please do not touch the map element of -struct uio_mem! It is used by the UIO framework -to set up sysfs files for this mapping. Simply leave it alone. - - - -Sometimes, your device can have one or more port regions which can not be -mapped to userspace. But if there are other possibilities for userspace to -access these ports, it makes sense to make information about the ports -available in sysfs. For each region, you have to set up a -struct uio_port in the port[] array. -Here's a description of the fields of struct uio_port: - - - - -char *porttype: Required. Set this to one of the predefined -constants. Use UIO_PORT_X86 for the ioports found in x86 -architectures. - - - -unsigned long start: Required if the port region is used. -Fill in the number of the first port of this region. - - - -unsigned long size: Fill in the number of ports in this -region. If size is zero, the region is considered unused. -Note that you must initialize size -with zero for all unused regions. - - - - -Please do not touch the portio element of -struct uio_port! It is used internally by the UIO -framework to set up sysfs files for this region. Simply leave it alone. - - - - - -Adding an interrupt handler - - What you need to do in your interrupt handler depends on your - hardware and on how you want to handle it. You should try to - keep the amount of code in your kernel interrupt handler low. - If your hardware requires no action that you - have to perform after each interrupt, - then your handler can be empty. If, on the other - hand, your hardware needs some action to - be performed after each interrupt, then you - must do it in your kernel module. Note - that you cannot rely on the userspace part of your driver. Your - userspace program can terminate at any time, possibly leaving - your hardware in a state where proper interrupt handling is - still required. - - - - There might also be applications where you want to read data - from your hardware at each interrupt and buffer it in a piece - of kernel memory you've allocated for that purpose. With this - technique you could avoid loss of data if your userspace - program misses an interrupt. - - - - A note on shared interrupts: Your driver should support - interrupt sharing whenever this is possible. It is possible if - and only if your driver can detect whether your hardware has - triggered the interrupt or not. This is usually done by looking - at an interrupt status register. If your driver sees that the - IRQ bit is actually set, it will perform its actions, and the - handler returns IRQ_HANDLED. If the driver detects that it was - not your hardware that caused the interrupt, it will do nothing - and return IRQ_NONE, allowing the kernel to call the next - possible interrupt handler. - - - - If you decide not to support shared interrupts, your card - won't work in computers with no free interrupts. As this - frequently happens on the PC platform, you can save yourself a - lot of trouble by supporting interrupt sharing. - - - - -Using uio_pdrv for platform devices - - In many cases, UIO drivers for platform devices can be handled in a - generic way. In the same place where you define your - struct platform_device, you simply also implement - your interrupt handler and fill your - struct uio_info. A pointer to this - struct uio_info is then used as - platform_data for your platform device. - - - You also need to set up an array of struct resource - containing addresses and sizes of your memory mappings. This - information is passed to the driver using the - .resource and .num_resources - elements of struct platform_device. - - - You now have to set the .name element of - struct platform_device to - "uio_pdrv" to use the generic UIO platform device - driver. This driver will fill the mem[] array - according to the resources given, and register the device. - - - The advantage of this approach is that you only have to edit a file - you need to edit anyway. You do not have to create an extra driver. - - - - -Using uio_pdrv_genirq for platform devices - - Especially in embedded devices, you frequently find chips where the - irq pin is tied to its own dedicated interrupt line. In such cases, - where you can be really sure the interrupt is not shared, we can take - the concept of uio_pdrv one step further and use a - generic interrupt handler. That's what - uio_pdrv_genirq does. - - - The setup for this driver is the same as described above for - uio_pdrv, except that you do not implement an - interrupt handler. The .handler element of - struct uio_info must remain - NULL. The .irq_flags element - must not contain IRQF_SHARED. - - - You will set the .name element of - struct platform_device to - "uio_pdrv_genirq" to use this driver. - - - The generic interrupt handler of uio_pdrv_genirq - will simply disable the interrupt line using - disable_irq_nosync(). After doing its work, - userspace can reenable the interrupt by writing 0x00000001 to the UIO - device file. The driver already implements an - irq_control() to make this possible, you must not - implement your own. - - - Using uio_pdrv_genirq not only saves a few lines of - interrupt handler code. You also do not need to know anything about - the chip's internal registers to create the kernel part of the driver. - All you need to know is the irq number of the pin the chip is - connected to. - - - - -Using uio_dmem_genirq for platform devices - - In addition to statically allocated memory ranges, they may also be - a desire to use dynamically allocated regions in a user space driver. - In particular, being able to access memory made available through the - dma-mapping API, may be particularly useful. The - uio_dmem_genirq driver provides a way to accomplish - this. - - - This driver is used in a similar manner to the - "uio_pdrv_genirq" driver with respect to interrupt - configuration and handling. - - - Set the .name element of - struct platform_device to - "uio_dmem_genirq" to use this driver. - - - When using this driver, fill in the .platform_data - element of struct platform_device, which is of type - struct uio_dmem_genirq_pdata and which contains the - following elements: - - - struct uio_info uioinfo: The same - structure used as the uio_pdrv_genirq platform - data - unsigned int *dynamic_region_sizes: - Pointer to list of sizes of dynamic memory regions to be mapped into - user space. - - unsigned int num_dynamic_regions: - Number of elements in dynamic_region_sizes array. - - - - The dynamic regions defined in the platform data will be appended to - the mem[] array after the platform device - resources, which implies that the total number of static and dynamic - memory regions cannot exceed MAX_UIO_MAPS. - - - The dynamic memory regions will be allocated when the UIO device file, - /dev/uioX is opened. - Similar to static memory resources, the memory region information for - dynamic regions is then visible via sysfs at - /sys/class/uio/uioX/maps/mapY/*. - The dynamic memory regions will be freed when the UIO device file is - closed. When no processes are holding the device file open, the address - returned to userspace is ~0. - - - - - - - -Writing a driver in userspace - - Once you have a working kernel module for your hardware, you can - write the userspace part of your driver. You don't need any special - libraries, your driver can be written in any reasonable language, - you can use floating point numbers and so on. In short, you can - use all the tools and libraries you'd normally use for writing a - userspace application. - - - -Getting information about your UIO device - - Information about all UIO devices is available in sysfs. The - first thing you should do in your driver is check - name and version to - make sure your talking to the right device and that its kernel - driver has the version you expect. - - - You should also make sure that the memory mapping you need - exists and has the size you expect. - - - There is a tool called lsuio that lists - UIO devices and their attributes. It is available here: - - - - http://www.osadl.org/projects/downloads/UIO/user/ - - - With lsuio you can quickly check if your - kernel module is loaded and which attributes it exports. - Have a look at the manpage for details. - - - The source code of lsuio can serve as an - example for getting information about an UIO device. - The file uio_helper.c contains a lot of - functions you could use in your userspace driver code. - - - - -mmap() device memory - - After you made sure you've got the right device with the - memory mappings you need, all you have to do is to call - mmap() to map the device's memory - to userspace. - - - The parameter offset of the - mmap() call has a special meaning - for UIO devices: It is used to select which mapping of - your device you want to map. To map the memory of - mapping N, you have to use N times the page size as - your offset: - - - offset = N * getpagesize(); - - - N starts from zero, so if you've got only one memory - range to map, set offset = 0. - A drawback of this technique is that memory is always - mapped beginning with its start address. - - - - -Waiting for interrupts - - After you successfully mapped your devices memory, you - can access it like an ordinary array. Usually, you will - perform some initialization. After that, your hardware - starts working and will generate an interrupt as soon - as it's finished, has some data available, or needs your - attention because an error occurred. - - - /dev/uioX is a read-only file. A - read() will always block until an - interrupt occurs. There is only one legal value for the - count parameter of - read(), and that is the size of a - signed 32 bit integer (4). Any other value for - count causes read() - to fail. The signed 32 bit integer read is the interrupt - count of your device. If the value is one more than the value - you read the last time, everything is OK. If the difference - is greater than one, you missed interrupts. - - - You can also use select() on - /dev/uioX. - - - - - - - -Generic PCI UIO driver - - The generic driver is a kernel module named uio_pci_generic. - It can work with any device compliant to PCI 2.3 (circa 2002) and - any compliant PCI Express device. Using this, you only need to - write the userspace driver, removing the need to write - a hardware-specific kernel module. - - - -Making the driver recognize the device - -Since the driver does not declare any device ids, it will not get loaded -automatically and will not automatically bind to any devices, you must load it -and allocate id to the driver yourself. For example: - - modprobe uio_pci_generic - echo "8086 10f5" > /sys/bus/pci/drivers/uio_pci_generic/new_id - - - -If there already is a hardware specific kernel driver for your device, the -generic driver still won't bind to it, in this case if you want to use the -generic driver (why would you?) you'll have to manually unbind the hardware -specific driver and bind the generic driver, like this: - - echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind - echo -n 0000:00:19.0 > /sys/bus/pci/drivers/uio_pci_generic/bind - - - -You can verify that the device has been bound to the driver -by looking for it in sysfs, for example like the following: - - ls -l /sys/bus/pci/devices/0000:00:19.0/driver - -Which if successful should print - - .../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic - -Note that the generic driver will not bind to old PCI 2.2 devices. -If binding the device failed, run the following command: - - dmesg - -and look in the output for failure reasons - - - - -Things to know about uio_pci_generic - -Interrupts are handled using the Interrupt Disable bit in the PCI command -register and Interrupt Status bit in the PCI status register. All devices -compliant to PCI 2.3 (circa 2002) and all compliant PCI Express devices should -support these bits. uio_pci_generic detects this support, and won't bind to -devices which do not support the Interrupt Disable Bit in the command register. - - -On each interrupt, uio_pci_generic sets the Interrupt Disable bit. -This prevents the device from generating further interrupts -until the bit is cleared. The userspace driver should clear this -bit before blocking and waiting for more interrupts. - - - -Writing userspace driver using uio_pci_generic - -Userspace driver can use pci sysfs interface, or the -libpci libray that wraps it, to talk to the device and to -re-enable interrupts by writing to the command register. - - - -Example code using uio_pci_generic - -Here is some sample userspace driver code using uio_pci_generic: - -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> - -int main() -{ - int uiofd; - int configfd; - int err; - int i; - unsigned icount; - unsigned char command_high; - - uiofd = open("/dev/uio0", O_RDONLY); - if (uiofd < 0) { - perror("uio open:"); - return errno; - } - configfd = open("/sys/class/uio/uio0/device/config", O_RDWR); - if (configfd < 0) { - perror("config open:"); - return errno; - } - - /* Read and cache command value */ - err = pread(configfd, &command_high, 1, 5); - if (err != 1) { - perror("command config read:"); - return errno; - } - command_high &= ~0x4; - - for(i = 0;; ++i) { - /* Print out a message, for debugging. */ - if (i == 0) - fprintf(stderr, "Started uio test driver.\n"); - else - fprintf(stderr, "Interrupts: %d\n", icount); - - /****************************************/ - /* Here we got an interrupt from the - device. Do something to it. */ - /****************************************/ - - /* Re-enable interrupts. */ - err = pwrite(configfd, &command_high, 1, 5); - if (err != 1) { - perror("config write:"); - break; - } - - /* Wait for next interrupt. */ - err = read(uiofd, &icount, 4); - if (err != 4) { - perror("uio read:"); - break; - } - - } - return errno; -} - - - - - - - - - -Generic Hyper-V UIO driver - - The generic driver is a kernel module named uio_hv_generic. - It supports devices on the Hyper-V VMBus similar to uio_pci_generic - on PCI bus. - - - -Making the driver recognize the device - -Since the driver does not declare any device GUID's, it will not get loaded -automatically and will not automatically bind to any devices, you must load it -and allocate id to the driver yourself. For example, to use the network device -GUID: - - modprobe uio_hv_generic - echo "f8615163-df3e-46c5-913f-f2d2f965ed0e" > /sys/bus/vmbus/drivers/uio_hv_generic/new_id - - - -If there already is a hardware specific kernel driver for the device, the -generic driver still won't bind to it, in this case if you want to use the -generic driver (why would you?) you'll have to manually unbind the hardware -specific driver and bind the generic driver, like this: - - echo -n vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3 > /sys/bus/vmbus/drivers/hv_netvsc/unbind - echo -n vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3 > /sys/bus/vmbus/drivers/uio_hv_generic/bind - - - -You can verify that the device has been bound to the driver -by looking for it in sysfs, for example like the following: - - ls -l /sys/bus/vmbus/devices/vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3/driver - -Which if successful should print - - .../vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3/driver -> ../../../bus/vmbus/drivers/uio_hv_generic - - - - - -Things to know about uio_hv_generic - -On each interrupt, uio_hv_generic sets the Interrupt Disable bit. -This prevents the device from generating further interrupts -until the bit is cleared. The userspace driver should clear this -bit before blocking and waiting for more interrupts. - - - - - -Further information - - - - OSADL homepage. - - - - Linutronix homepage. - - - - -
diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index 5475a2807e7a..c5a1cd0a4ae7 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -30,6 +30,7 @@ available subsections can be seen below. miscellaneous vme 80211/index + uio-howto .. only:: subproject and html diff --git a/Documentation/driver-api/uio-howto.rst b/Documentation/driver-api/uio-howto.rst new file mode 100644 index 000000000000..f73d660b2956 --- /dev/null +++ b/Documentation/driver-api/uio-howto.rst @@ -0,0 +1,705 @@ +======================= +The Userspace I/O HOWTO +======================= + +:Author: Hans-Jürgen Koch Linux developer, Linutronix +:Date: 2006-12-11 + +About this document +=================== + +Translations +------------ + +If you know of any translations for this document, or you are interested +in translating it, please email me hjk@hansjkoch.de. + +Preface +------- + +For many types of devices, creating a Linux kernel driver is overkill. +All that is really needed is some way to handle an interrupt and provide +access to the memory space of the device. The logic of controlling the +device does not necessarily have to be within the kernel, as the device +does not need to take advantage of any of other resources that the +kernel provides. One such common class of devices that are like this are +for industrial I/O cards. + +To address this situation, the userspace I/O system (UIO) was designed. +For typical industrial I/O cards, only a very small kernel module is +needed. The main part of the driver will run in user space. This +simplifies development and reduces the risk of serious bugs within a +kernel module. + +Please note that UIO is not an universal driver interface. Devices that +are already handled well by other kernel subsystems (like networking or +serial or USB) are no candidates for an UIO driver. Hardware that is +ideally suited for an UIO driver fulfills all of the following: + +- The device has memory that can be mapped. The device can be + controlled completely by writing to this memory. + +- The device usually generates interrupts. + +- The device does not fit into one of the standard kernel subsystems. + +Acknowledgments +--------------- + +I'd like to thank Thomas Gleixner and Benedikt Spranger of Linutronix, +who have not only written most of the UIO code, but also helped greatly +writing this HOWTO by giving me all kinds of background information. + +Feedback +-------- + +Find something wrong with this document? (Or perhaps something right?) I +would love to hear from you. Please email me at hjk@hansjkoch.de. + +About UIO +========= + +If you use UIO for your card's driver, here's what you get: + +- only one small kernel module to write and maintain. + +- develop the main part of your driver in user space, with all the + tools and libraries you're used to. + +- bugs in your driver won't crash the kernel. + +- updates of your driver can take place without recompiling the kernel. + +How UIO works +------------- + +Each UIO device is accessed through a device file and several sysfs +attribute files. The device file will be called ``/dev/uio0`` for the +first device, and ``/dev/uio1``, ``/dev/uio2`` and so on for subsequent +devices. + +``/dev/uioX`` is used to access the address space of the card. Just use +:c:func:`mmap()` to access registers or RAM locations of your card. + +Interrupts are handled by reading from ``/dev/uioX``. A blocking +:c:func:`read()` from ``/dev/uioX`` will return as soon as an +interrupt occurs. You can also use :c:func:`select()` on +``/dev/uioX`` to wait for an interrupt. The integer value read from +``/dev/uioX`` represents the total interrupt count. You can use this +number to figure out if you missed some interrupts. + +For some hardware that has more than one interrupt source internally, +but not separate IRQ mask and status registers, there might be +situations where userspace cannot determine what the interrupt source +was if the kernel handler disables them by writing to the chip's IRQ +register. In such a case, the kernel has to disable the IRQ completely +to leave the chip's register untouched. Now the userspace part can +determine the cause of the interrupt, but it cannot re-enable +interrupts. Another cornercase is chips where re-enabling interrupts is +a read-modify-write operation to a combined IRQ status/acknowledge +register. This would be racy if a new interrupt occurred simultaneously. + +To address these problems, UIO also implements a write() function. It is +normally not used and can be ignored for hardware that has only a single +interrupt source or has separate IRQ mask and status registers. If you +need it, however, a write to ``/dev/uioX`` will call the +:c:func:`irqcontrol()` function implemented by the driver. You have +to write a 32-bit value that is usually either 0 or 1 to disable or +enable interrupts. If a driver does not implement +:c:func:`irqcontrol()`, :c:func:`write()` will return with +``-ENOSYS``. + +To handle interrupts properly, your custom kernel module can provide its +own interrupt handler. It will automatically be called by the built-in +handler. + +For cards that don't generate interrupts but need to be polled, there is +the possibility to set up a timer that triggers the interrupt handler at +configurable time intervals. This interrupt simulation is done by +calling :c:func:`uio_event_notify()` from the timer's event +handler. + +Each driver provides attributes that are used to read or write +variables. These attributes are accessible through sysfs files. A custom +kernel driver module can add its own attributes to the device owned by +the uio driver, but not added to the UIO device itself at this time. +This might change in the future if it would be found to be useful. + +The following standard attributes are provided by the UIO framework: + +- ``name``: The name of your device. It is recommended to use the name + of your kernel module for this. + +- ``version``: A version string defined by your driver. This allows the + user space part of your driver to deal with different versions of the + kernel module. + +- ``event``: The total number of interrupts handled by the driver since + the last time the device node was read. + +These attributes appear under the ``/sys/class/uio/uioX`` directory. +Please note that this directory might be a symlink, and not a real +directory. Any userspace code that accesses it must be able to handle +this. + +Each UIO device can make one or more memory regions available for memory +mapping. This is necessary because some industrial I/O cards require +access to more than one PCI memory region in a driver. + +Each mapping has its own directory in sysfs, the first mapping appears +as ``/sys/class/uio/uioX/maps/map0/``. Subsequent mappings create +directories ``map1/``, ``map2/``, and so on. These directories will only +appear if the size of the mapping is not 0. + +Each ``mapX/`` directory contains four read-only files that show +attributes of the memory: + +- ``name``: A string identifier for this mapping. This is optional, the + string can be empty. Drivers can set this to make it easier for + userspace to find the correct mapping. + +- ``addr``: The address of memory that can be mapped. + +- ``size``: The size, in bytes, of the memory pointed to by addr. + +- ``offset``: The offset, in bytes, that has to be added to the pointer + returned by :c:func:`mmap()` to get to the actual device memory. + This is important if the device's memory is not page aligned. + Remember that pointers returned by :c:func:`mmap()` are always + page aligned, so it is good style to always add this offset. + +From userspace, the different mappings are distinguished by adjusting +the ``offset`` parameter of the :c:func:`mmap()` call. To map the +memory of mapping N, you have to use N times the page size as your +offset:: + + offset = N * getpagesize(); + +Sometimes there is hardware with memory-like regions that can not be +mapped with the technique described here, but there are still ways to +access them from userspace. The most common example are x86 ioports. On +x86 systems, userspace can access these ioports using +:c:func:`ioperm()`, :c:func:`iopl()`, :c:func:`inb()`, +:c:func:`outb()`, and similar functions. + +Since these ioport regions can not be mapped, they will not appear under +``/sys/class/uio/uioX/maps/`` like the normal memory described above. +Without information about the port regions a hardware has to offer, it +becomes difficult for the userspace part of the driver to find out which +ports belong to which UIO device. + +To address this situation, the new directory +``/sys/class/uio/uioX/portio/`` was added. It only exists if the driver +wants to pass information about one or more port regions to userspace. +If that is the case, subdirectories named ``port0``, ``port1``, and so +on, will appear underneath ``/sys/class/uio/uioX/portio/``. + +Each ``portX/`` directory contains four read-only files that show name, +start, size, and type of the port region: + +- ``name``: A string identifier for this port region. The string is + optional and can be empty. Drivers can set it to make it easier for + userspace to find a certain port region. + +- ``start``: The first port of this region. + +- ``size``: The number of ports in this region. + +- ``porttype``: A string describing the type of port. + +Writing your own kernel module +============================== + +Please have a look at ``uio_cif.c`` as an example. The following +paragraphs explain the different sections of this file. + +struct uio_info +--------------- + +This structure tells the framework the details of your driver, Some of +the members are required, others are optional. + +- ``const char *name``: Required. The name of your driver as it will + appear in sysfs. I recommend using the name of your module for this. + +- ``const char *version``: Required. This string appears in + ``/sys/class/uio/uioX/version``. + +- ``struct uio_mem mem[ MAX_UIO_MAPS ]``: Required if you have memory + that can be mapped with :c:func:`mmap()`. For each mapping you + need to fill one of the ``uio_mem`` structures. See the description + below for details. + +- ``struct uio_port port[ MAX_UIO_PORTS_REGIONS ]``: Required if you + want to pass information about ioports to userspace. For each port + region you need to fill one of the ``uio_port`` structures. See the + description below for details. + +- ``long irq``: Required. If your hardware generates an interrupt, it's + your modules task to determine the irq number during initialization. + If you don't have a hardware generated interrupt but want to trigger + the interrupt handler in some other way, set ``irq`` to + ``UIO_IRQ_CUSTOM``. If you had no interrupt at all, you could set + ``irq`` to ``UIO_IRQ_NONE``, though this rarely makes sense. + +- ``unsigned long irq_flags``: Required if you've set ``irq`` to a + hardware interrupt number. The flags given here will be used in the + call to :c:func:`request_irq()`. + +- ``int (*mmap)(struct uio_info *info, struct vm_area_struct *vma)``: + Optional. If you need a special :c:func:`mmap()` + function, you can set it here. If this pointer is not NULL, your + :c:func:`mmap()` will be called instead of the built-in one. + +- ``int (*open)(struct uio_info *info, struct inode *inode)``: + Optional. You might want to have your own :c:func:`open()`, + e.g. to enable interrupts only when your device is actually used. + +- ``int (*release)(struct uio_info *info, struct inode *inode)``: + Optional. If you define your own :c:func:`open()`, you will + probably also want a custom :c:func:`release()` function. + +- ``int (*irqcontrol)(struct uio_info *info, s32 irq_on)``: + Optional. If you need to be able to enable or disable interrupts + from userspace by writing to ``/dev/uioX``, you can implement this + function. The parameter ``irq_on`` will be 0 to disable interrupts + and 1 to enable them. + +Usually, your device will have one or more memory regions that can be +mapped to user space. For each region, you have to set up a +``struct uio_mem`` in the ``mem[]`` array. Here's a description of the +fields of ``struct uio_mem``: + +- ``const char *name``: Optional. Set this to help identify the memory + region, it will show up in the corresponding sysfs node. + +- ``int memtype``: Required if the mapping is used. Set this to + ``UIO_MEM_PHYS`` if you you have physical memory on your card to be + mapped. Use ``UIO_MEM_LOGICAL`` for logical memory (e.g. allocated + with :c:func:`kmalloc()`). There's also ``UIO_MEM_VIRTUAL`` for + virtual memory. + +- ``phys_addr_t addr``: Required if the mapping is used. Fill in the + address of your memory block. This address is the one that appears in + sysfs. + +- ``resource_size_t size``: Fill in the size of the memory block that + ``addr`` points to. If ``size`` is zero, the mapping is considered + unused. Note that you *must* initialize ``size`` with zero for all + unused mappings. + +- ``void *internal_addr``: If you have to access this memory region + from within your kernel module, you will want to map it internally by + using something like :c:func:`ioremap()`. Addresses returned by + this function cannot be mapped to user space, so you must not store + it in ``addr``. Use ``internal_addr`` instead to remember such an + address. + +Please do not touch the ``map`` element of ``struct uio_mem``! It is +used by the UIO framework to set up sysfs files for this mapping. Simply +leave it alone. + +Sometimes, your device can have one or more port regions which can not +be mapped to userspace. But if there are other possibilities for +userspace to access these ports, it makes sense to make information +about the ports available in sysfs. For each region, you have to set up +a ``struct uio_port`` in the ``port[]`` array. Here's a description of +the fields of ``struct uio_port``: + +- ``char *porttype``: Required. Set this to one of the predefined + constants. Use ``UIO_PORT_X86`` for the ioports found in x86 + architectures. + +- ``unsigned long start``: Required if the port region is used. Fill in + the number of the first port of this region. + +- ``unsigned long size``: Fill in the number of ports in this region. + If ``size`` is zero, the region is considered unused. Note that you + *must* initialize ``size`` with zero for all unused regions. + +Please do not touch the ``portio`` element of ``struct uio_port``! It is +used internally by the UIO framework to set up sysfs files for this +region. Simply leave it alone. + +Adding an interrupt handler +--------------------------- + +What you need to do in your interrupt handler depends on your hardware +and on how you want to handle it. You should try to keep the amount of +code in your kernel interrupt handler low. If your hardware requires no +action that you *have* to perform after each interrupt, then your +handler can be empty. + +If, on the other hand, your hardware *needs* some action to be performed +after each interrupt, then you *must* do it in your kernel module. Note +that you cannot rely on the userspace part of your driver. Your +userspace program can terminate at any time, possibly leaving your +hardware in a state where proper interrupt handling is still required. + +There might also be applications where you want to read data from your +hardware at each interrupt and buffer it in a piece of kernel memory +you've allocated for that purpose. With this technique you could avoid +loss of data if your userspace program misses an interrupt. + +A note on shared interrupts: Your driver should support interrupt +sharing whenever this is possible. It is possible if and only if your +driver can detect whether your hardware has triggered the interrupt or +not. This is usually done by looking at an interrupt status register. If +your driver sees that the IRQ bit is actually set, it will perform its +actions, and the handler returns IRQ_HANDLED. If the driver detects +that it was not your hardware that caused the interrupt, it will do +nothing and return IRQ_NONE, allowing the kernel to call the next +possible interrupt handler. + +If you decide not to support shared interrupts, your card won't work in +computers with no free interrupts. As this frequently happens on the PC +platform, you can save yourself a lot of trouble by supporting interrupt +sharing. + +Using uio_pdrv for platform devices +----------------------------------- + +In many cases, UIO drivers for platform devices can be handled in a +generic way. In the same place where you define your +``struct platform_device``, you simply also implement your interrupt +handler and fill your ``struct uio_info``. A pointer to this +``struct uio_info`` is then used as ``platform_data`` for your platform +device. + +You also need to set up an array of ``struct resource`` containing +addresses and sizes of your memory mappings. This information is passed +to the driver using the ``.resource`` and ``.num_resources`` elements of +``struct platform_device``. + +You now have to set the ``.name`` element of ``struct platform_device`` +to ``"uio_pdrv"`` to use the generic UIO platform device driver. This +driver will fill the ``mem[]`` array according to the resources given, +and register the device. + +The advantage of this approach is that you only have to edit a file you +need to edit anyway. You do not have to create an extra driver. + +Using uio_pdrv_genirq for platform devices +------------------------------------------ + +Especially in embedded devices, you frequently find chips where the irq +pin is tied to its own dedicated interrupt line. In such cases, where +you can be really sure the interrupt is not shared, we can take the +concept of ``uio_pdrv`` one step further and use a generic interrupt +handler. That's what ``uio_pdrv_genirq`` does. + +The setup for this driver is the same as described above for +``uio_pdrv``, except that you do not implement an interrupt handler. The +``.handler`` element of ``struct uio_info`` must remain ``NULL``. The +``.irq_flags`` element must not contain ``IRQF_SHARED``. + +You will set the ``.name`` element of ``struct platform_device`` to +``"uio_pdrv_genirq"`` to use this driver. + +The generic interrupt handler of ``uio_pdrv_genirq`` will simply disable +the interrupt line using :c:func:`disable_irq_nosync()`. After +doing its work, userspace can reenable the interrupt by writing +0x00000001 to the UIO device file. The driver already implements an +:c:func:`irq_control()` to make this possible, you must not +implement your own. + +Using ``uio_pdrv_genirq`` not only saves a few lines of interrupt +handler code. You also do not need to know anything about the chip's +internal registers to create the kernel part of the driver. All you need +to know is the irq number of the pin the chip is connected to. + +Using uio_dmem_genirq for platform devices +------------------------------------------ + +In addition to statically allocated memory ranges, they may also be a +desire to use dynamically allocated regions in a user space driver. In +particular, being able to access memory made available through the +dma-mapping API, may be particularly useful. The ``uio_dmem_genirq`` +driver provides a way to accomplish this. + +This driver is used in a similar manner to the ``"uio_pdrv_genirq"`` +driver with respect to interrupt configuration and handling. + +Set the ``.name`` element of ``struct platform_device`` to +``"uio_dmem_genirq"`` to use this driver. + +When using this driver, fill in the ``.platform_data`` element of +``struct platform_device``, which is of type +``struct uio_dmem_genirq_pdata`` and which contains the following +elements: + +- ``struct uio_info uioinfo``: The same structure used as the + ``uio_pdrv_genirq`` platform data + +- ``unsigned int *dynamic_region_sizes``: Pointer to list of sizes of + dynamic memory regions to be mapped into user space. + +- ``unsigned int num_dynamic_regions``: Number of elements in + ``dynamic_region_sizes`` array. + +The dynamic regions defined in the platform data will be appended to the +`` mem[] `` array after the platform device resources, which implies +that the total number of static and dynamic memory regions cannot exceed +``MAX_UIO_MAPS``. + +The dynamic memory regions will be allocated when the UIO device file, +``/dev/uioX`` is opened. Similar to static memory resources, the memory +region information for dynamic regions is then visible via sysfs at +``/sys/class/uio/uioX/maps/mapY/*``. The dynamic memory regions will be +freed when the UIO device file is closed. When no processes are holding +the device file open, the address returned to userspace is ~0. + +Writing a driver in userspace +============================= + +Once you have a working kernel module for your hardware, you can write +the userspace part of your driver. You don't need any special libraries, +your driver can be written in any reasonable language, you can use +floating point numbers and so on. In short, you can use all the tools +and libraries you'd normally use for writing a userspace application. + +Getting information about your UIO device +----------------------------------------- + +Information about all UIO devices is available in sysfs. The first thing +you should do in your driver is check ``name`` and ``version`` to make +sure your talking to the right device and that its kernel driver has the +version you expect. + +You should also make sure that the memory mapping you need exists and +has the size you expect. + +There is a tool called ``lsuio`` that lists UIO devices and their +attributes. It is available here: + +http://www.osadl.org/projects/downloads/UIO/user/ + +With ``lsuio`` you can quickly check if your kernel module is loaded and +which attributes it exports. Have a look at the manpage for details. + +The source code of ``lsuio`` can serve as an example for getting +information about an UIO device. The file ``uio_helper.c`` contains a +lot of functions you could use in your userspace driver code. + +mmap() device memory +-------------------- + +After you made sure you've got the right device with the memory mappings +you need, all you have to do is to call :c:func:`mmap()` to map the +device's memory to userspace. + +The parameter ``offset`` of the :c:func:`mmap()` call has a special +meaning for UIO devices: It is used to select which mapping of your +device you want to map. To map the memory of mapping N, you have to use +N times the page size as your offset:: + + offset = N * getpagesize(); + +N starts from zero, so if you've got only one memory range to map, set +``offset = 0``. A drawback of this technique is that memory is always +mapped beginning with its start address. + +Waiting for interrupts +---------------------- + +After you successfully mapped your devices memory, you can access it +like an ordinary array. Usually, you will perform some initialization. +After that, your hardware starts working and will generate an interrupt +as soon as it's finished, has some data available, or needs your +attention because an error occurred. + +``/dev/uioX`` is a read-only file. A :c:func:`read()` will always +block until an interrupt occurs. There is only one legal value for the +``count`` parameter of :c:func:`read()`, and that is the size of a +signed 32 bit integer (4). Any other value for ``count`` causes +:c:func:`read()` to fail. The signed 32 bit integer read is the +interrupt count of your device. If the value is one more than the value +you read the last time, everything is OK. If the difference is greater +than one, you missed interrupts. + +You can also use :c:func:`select()` on ``/dev/uioX``. + +Generic PCI UIO driver +====================== + +The generic driver is a kernel module named uio_pci_generic. It can +work with any device compliant to PCI 2.3 (circa 2002) and any compliant +PCI Express device. Using this, you only need to write the userspace +driver, removing the need to write a hardware-specific kernel module. + +Making the driver recognize the device +-------------------------------------- + +Since the driver does not declare any device ids, it will not get loaded +automatically and will not automatically bind to any devices, you must +load it and allocate id to the driver yourself. For example:: + + modprobe uio_pci_generic + echo "8086 10f5" > /sys/bus/pci/drivers/uio_pci_generic/new_id + +If there already is a hardware specific kernel driver for your device, +the generic driver still won't bind to it, in this case if you want to +use the generic driver (why would you?) you'll have to manually unbind +the hardware specific driver and bind the generic driver, like this:: + + echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind + echo -n 0000:00:19.0 > /sys/bus/pci/drivers/uio_pci_generic/bind + +You can verify that the device has been bound to the driver by looking +for it in sysfs, for example like the following:: + + ls -l /sys/bus/pci/devices/0000:00:19.0/driver + +Which if successful should print:: + + .../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic + +Note that the generic driver will not bind to old PCI 2.2 devices. If +binding the device failed, run the following command:: + + dmesg + +and look in the output for failure reasons. + +Things to know about uio_pci_generic +------------------------------------ + +Interrupts are handled using the Interrupt Disable bit in the PCI +command register and Interrupt Status bit in the PCI status register. +All devices compliant to PCI 2.3 (circa 2002) and all compliant PCI +Express devices should support these bits. uio_pci_generic detects +this support, and won't bind to devices which do not support the +Interrupt Disable Bit in the command register. + +On each interrupt, uio_pci_generic sets the Interrupt Disable bit. +This prevents the device from generating further interrupts until the +bit is cleared. The userspace driver should clear this bit before +blocking and waiting for more interrupts. + +Writing userspace driver using uio_pci_generic +------------------------------------------------ + +Userspace driver can use pci sysfs interface, or the libpci library that +wraps it, to talk to the device and to re-enable interrupts by writing +to the command register. + +Example code using uio_pci_generic +---------------------------------- + +Here is some sample userspace driver code using uio_pci_generic:: + + #include + #include + #include + #include + #include + #include + #include + + int main() + { + int uiofd; + int configfd; + int err; + int i; + unsigned icount; + unsigned char command_high; + + uiofd = open("/dev/uio0", O_RDONLY); + if (uiofd < 0) { + perror("uio open:"); + return errno; + } + configfd = open("/sys/class/uio/uio0/device/config", O_RDWR); + if (configfd < 0) { + perror("config open:"); + return errno; + } + + /* Read and cache command value */ + err = pread(configfd, &command_high, 1, 5); + if (err != 1) { + perror("command config read:"); + return errno; + } + command_high &= ~0x4; + + for(i = 0;; ++i) { + /* Print out a message, for debugging. */ + if (i == 0) + fprintf(stderr, "Started uio test driver.\n"); + else + fprintf(stderr, "Interrupts: %d\n", icount); + + /****************************************/ + /* Here we got an interrupt from the + device. Do something to it. */ + /****************************************/ + + /* Re-enable interrupts. */ + err = pwrite(configfd, &command_high, 1, 5); + if (err != 1) { + perror("config write:"); + break; + } + + /* Wait for next interrupt. */ + err = read(uiofd, &icount, 4); + if (err != 4) { + perror("uio read:"); + break; + } + + } + return errno; + } + +Generic Hyper-V UIO driver +========================== + +The generic driver is a kernel module named uio_hv_generic. It +supports devices on the Hyper-V VMBus similar to uio_pci_generic on +PCI bus. + +Making the driver recognize the device +-------------------------------------- + +Since the driver does not declare any device GUID's, it will not get +loaded automatically and will not automatically bind to any devices, you +must load it and allocate id to the driver yourself. For example, to use +the network device GUID:: + + modprobe uio_hv_generic + echo "f8615163-df3e-46c5-913f-f2d2f965ed0e" > /sys/bus/vmbus/drivers/uio_hv_generic/new_id + +If there already is a hardware specific kernel driver for the device, +the generic driver still won't bind to it, in this case if you want to +use the generic driver (why would you?) you'll have to manually unbind +the hardware specific driver and bind the generic driver, like this:: + + echo -n vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3 > /sys/bus/vmbus/drivers/hv_netvsc/unbind + echo -n vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3 > /sys/bus/vmbus/drivers/uio_hv_generic/bind + +You can verify that the device has been bound to the driver by looking +for it in sysfs, for example like the following:: + + ls -l /sys/bus/vmbus/devices/vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3/driver + +Which if successful should print:: + + .../vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3/driver -> ../../../bus/vmbus/drivers/uio_hv_generic + +Things to know about uio_hv_generic +----------------------------------- + +On each interrupt, uio_hv_generic sets the Interrupt Disable bit. This +prevents the device from generating further interrupts until the bit is +cleared. The userspace driver should clear this bit before blocking and +waiting for more interrupts. + +Further information +=================== + +- `OSADL homepage. `_ + +- `Linutronix homepage. `_ diff --git a/MAINTAINERS b/MAINTAINERS index be8de24fd6dd..6f6efd2e706a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12966,7 +12966,7 @@ USERSPACE I/O (UIO) M: Greg Kroah-Hartman S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git -F: Documentation/DocBook/uio-howto.tmpl +F: Documentation/driver-api/uio-howto.rst F: drivers/uio/ F: include/linux/uio*.h -- cgit v1.2.3 From 3bb434cdcc6af3d4e70ba041e6f596e465d11e14 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 1 Feb 2017 15:42:57 +0100 Subject: vmw_vmci: switch to pci_irq_alloc_vectors Cleans up the IRQ management code a lot, including removing a lot of state from the per-device structure. Signed-off-by: Christoph Hellwig Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_guest.c | 75 ++++++++++---------------------------- include/linux/vmw_vmci_defs.h | 7 ---- 2 files changed, 20 insertions(+), 62 deletions(-) diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index 189b32519748..9d659542a335 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c @@ -54,10 +54,7 @@ struct vmci_guest_device { struct device *dev; /* PCI device we are attached to */ void __iomem *iobase; - unsigned int irq; - unsigned int intr_type; bool exclusive_vectors; - struct msix_entry msix_entries[VMCI_MAX_INTRS]; struct tasklet_struct datagram_tasklet; struct tasklet_struct bm_tasklet; @@ -368,30 +365,6 @@ static void vmci_process_bitmap(unsigned long data) vmci_dbell_scan_notification_entries(dev->notification_bitmap); } -/* - * Enable MSI-X. Try exclusive vectors first, then shared vectors. - */ -static int vmci_enable_msix(struct pci_dev *pdev, - struct vmci_guest_device *vmci_dev) -{ - int i; - int result; - - for (i = 0; i < VMCI_MAX_INTRS; ++i) { - vmci_dev->msix_entries[i].entry = i; - vmci_dev->msix_entries[i].vector = i; - } - - result = pci_enable_msix_exact(pdev, - vmci_dev->msix_entries, VMCI_MAX_INTRS); - if (result == 0) - vmci_dev->exclusive_vectors = true; - else if (result == -ENOSPC) - result = pci_enable_msix_exact(pdev, vmci_dev->msix_entries, 1); - - return result; -} - /* * Interrupt handler for legacy or MSI interrupt, or for first MSI-X * interrupt (vector VMCI_INTR_DATAGRAM). @@ -406,7 +379,7 @@ static irqreturn_t vmci_interrupt(int irq, void *_dev) * Otherwise we must read the ICR to determine what to do. */ - if (dev->intr_type == VMCI_INTR_TYPE_MSIX && dev->exclusive_vectors) { + if (dev->exclusive_vectors) { tasklet_schedule(&dev->datagram_tasklet); } else { unsigned int icr; @@ -491,7 +464,6 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, } vmci_dev->dev = &pdev->dev; - vmci_dev->intr_type = VMCI_INTR_TYPE_INTX; vmci_dev->exclusive_vectors = false; vmci_dev->iobase = iobase; @@ -592,26 +564,26 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, * Enable interrupts. Try MSI-X first, then MSI, and then fallback on * legacy interrupts. */ - if (!vmci_disable_msix && !vmci_enable_msix(pdev, vmci_dev)) { - vmci_dev->intr_type = VMCI_INTR_TYPE_MSIX; - vmci_dev->irq = vmci_dev->msix_entries[0].vector; - } else if (!vmci_disable_msi && !pci_enable_msi(pdev)) { - vmci_dev->intr_type = VMCI_INTR_TYPE_MSI; - vmci_dev->irq = pdev->irq; + error = pci_alloc_irq_vectors(pdev, VMCI_MAX_INTRS, VMCI_MAX_INTRS, + PCI_IRQ_MSIX); + if (error) { + error = pci_alloc_irq_vectors(pdev, 1, 1, + PCI_IRQ_MSIX | PCI_IRQ_MSI | PCI_IRQ_LEGACY); + if (error) + goto err_remove_bitmap; } else { - vmci_dev->intr_type = VMCI_INTR_TYPE_INTX; - vmci_dev->irq = pdev->irq; + vmci_dev->exclusive_vectors = true; } /* * Request IRQ for legacy or MSI interrupts, or for first * MSI-X vector. */ - error = request_irq(vmci_dev->irq, vmci_interrupt, IRQF_SHARED, - KBUILD_MODNAME, vmci_dev); + error = request_irq(pci_irq_vector(pdev, 0), vmci_interrupt, + IRQF_SHARED, KBUILD_MODNAME, vmci_dev); if (error) { dev_err(&pdev->dev, "Irq %u in use: %d\n", - vmci_dev->irq, error); + pci_irq_vector(pdev, 0), error); goto err_disable_msi; } @@ -622,13 +594,13 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, * between the vectors. */ if (vmci_dev->exclusive_vectors) { - error = request_irq(vmci_dev->msix_entries[1].vector, + error = request_irq(pci_irq_vector(pdev, 1), vmci_interrupt_bm, 0, KBUILD_MODNAME, vmci_dev); if (error) { dev_err(&pdev->dev, "Failed to allocate irq %u: %d\n", - vmci_dev->msix_entries[1].vector, error); + pci_irq_vector(pdev, 1), error); goto err_free_irq; } } @@ -651,15 +623,12 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, return 0; err_free_irq: - free_irq(vmci_dev->irq, vmci_dev); + free_irq(pci_irq_vector(pdev, 0), vmci_dev); tasklet_kill(&vmci_dev->datagram_tasklet); tasklet_kill(&vmci_dev->bm_tasklet); err_disable_msi: - if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSIX) - pci_disable_msix(pdev); - else if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSI) - pci_disable_msi(pdev); + pci_free_irq_vectors(pdev); vmci_err = vmci_event_unsubscribe(ctx_update_sub_id); if (vmci_err < VMCI_SUCCESS) @@ -719,14 +688,10 @@ static void vmci_guest_remove_device(struct pci_dev *pdev) * MSI-X, we might have multiple vectors, each with their own * IRQ, which we must free too. */ - free_irq(vmci_dev->irq, vmci_dev); - if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSIX) { - if (vmci_dev->exclusive_vectors) - free_irq(vmci_dev->msix_entries[1].vector, vmci_dev); - pci_disable_msix(pdev); - } else if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSI) { - pci_disable_msi(pdev); - } + if (vmci_dev->exclusive_vectors) + free_irq(pci_irq_vector(pdev, 1), vmci_dev); + free_irq(pci_irq_vector(pdev, 0), vmci_dev); + pci_free_irq_vectors(pdev); tasklet_kill(&vmci_dev->datagram_tasklet); tasklet_kill(&vmci_dev->bm_tasklet); diff --git a/include/linux/vmw_vmci_defs.h b/include/linux/vmw_vmci_defs.h index 1bd31a38c51e..b724ef7005de 100644 --- a/include/linux/vmw_vmci_defs.h +++ b/include/linux/vmw_vmci_defs.h @@ -54,13 +54,6 @@ #define VMCI_IMR_DATAGRAM 0x1 #define VMCI_IMR_NOTIFICATION 0x2 -/* Interrupt type. */ -enum { - VMCI_INTR_TYPE_INTX = 0, - VMCI_INTR_TYPE_MSI = 1, - VMCI_INTR_TYPE_MSIX = 2, -}; - /* Maximum MSI/MSI-X interrupt vectors in the device. */ #define VMCI_MAX_INTRS 2 -- cgit v1.2.3 From e1ef962bf1255cea2f165f3a7374766a7ceb4308 Mon Sep 17 00:00:00 2001 From: Miguel Bernal Marin Date: Tue, 31 Jan 2017 18:02:16 -0600 Subject: misc: fix typo on Kconfig When panel driver was moved from staging to misc a new line was missing to be added on Kconfig file. Fixes: 305b37bd01c2 ("misc: Move panel driver out of staging") Signed-off-by: Miguel Bernal Marin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0444a8f9b094..b4d6aac83824 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -491,6 +491,7 @@ config VEXPRESS_SYSCFG ARM Ltd. Versatile Express uses specialised platform configuration bus. System Configuration interface is one of the possible means of generating transactions on this bus. + config PANEL tristate "Parallel port LCD/Keypad Panel support" depends on PARPORT -- cgit v1.2.3 From 9c7daa61d91647fba3b986a9941d3fb531b9b9ff Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 2 Feb 2017 11:26:53 +0200 Subject: mei: me: add a wrapper to set host generated data interrupt Consolidate setting H_IG, an interrupt from host towards hw, into a wrapper to eliminate code duplication. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index befeac52c349..23294b845c42 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -139,6 +139,19 @@ static inline void mei_hcsr_set(struct mei_device *dev, u32 reg) mei_hcsr_write(dev, reg); } +/** + * mei_hcsr_set_hig - set host interrupt (set H_IG) + * + * @dev: the device structure + */ +static inline void mei_hcsr_set_hig(struct mei_device *dev) +{ + u32 hcsr; + + hcsr = mei_hcsr_read(dev) | H_IG; + mei_hcsr_set(dev, hcsr); +} + /** * mei_me_d0i3c_read - Reads 32bit data from the D0I3C register * @@ -505,7 +518,6 @@ static int mei_me_hbuf_write(struct mei_device *dev, unsigned long rem; unsigned long length = header->length; u32 *reg_buf = (u32 *)buf; - u32 hcsr; u32 dw_cnt; int i; int empty_slots; @@ -532,8 +544,7 @@ static int mei_me_hbuf_write(struct mei_device *dev, mei_me_hcbww_write(dev, reg); } - hcsr = mei_hcsr_read(dev) | H_IG; - mei_hcsr_set(dev, hcsr); + mei_hcsr_set_hig(dev); if (!mei_me_hw_is_ready(dev)) return -EIO; @@ -580,7 +591,6 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, unsigned long buffer_length) { u32 *reg_buf = (u32 *)buffer; - u32 hcsr; for (; buffer_length >= sizeof(u32); buffer_length -= sizeof(u32)) *reg_buf++ = mei_me_mecbrw_read(dev); @@ -591,8 +601,7 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, memcpy(reg_buf, ®, buffer_length); } - hcsr = mei_hcsr_read(dev) | H_IG; - mei_hcsr_set(dev, hcsr); + mei_hcsr_set_hig(dev); return 0; } -- cgit v1.2.3 From 47f60a017169d1d3c6cb086ece272f272f110845 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 2 Feb 2017 11:26:54 +0200 Subject: mei: me: generate an interrupt if the hw indicates reset. In rare case the driver may lose connection with the device after device reset due to a missed interrupt. The driver will unlock the flow by generating an interrupt towards the firmware (HIG) when the device is in the resetting state. The FW is able to ignore the interrupt during orderly flow. The effected platforms are skylake and newer. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 23294b845c42..71216affcab1 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -393,6 +393,19 @@ static bool mei_me_hw_is_ready(struct mei_device *dev) return (mecsr & ME_RDY_HRA) == ME_RDY_HRA; } +/** + * mei_me_hw_is_resetting - check whether the me(hw) is in reset + * + * @dev: mei device + * Return: bool + */ +static bool mei_me_hw_is_resetting(struct mei_device *dev) +{ + u32 mecsr = mei_me_mecsr_read(dev); + + return (mecsr & ME_RST_HRA) == ME_RST_HRA; +} + /** * mei_me_hw_ready_wait - wait until the me(hw) has turned ready * or timeout is reached @@ -1219,6 +1232,9 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) goto end; } + if (mei_me_hw_is_resetting(dev)) + mei_hcsr_set_hig(dev); + mei_me_pg_intr(dev, me_intr_src(hcsr)); /* check if we need to start the dev */ -- cgit v1.2.3 From 3b28f4f2c2c7fcb6770cdfe5e8cd284ee1bc9783 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Thu, 2 Feb 2017 18:00:44 +0100 Subject: checkkconfigsymbols.py: support Kconfig's 'imply' statement Support the new imply statement in Kconfig. The imply statement has been added by commit 237e3ad0f195 ("Kconfig: Introduce the "imply" keyword") and is a weak version of a select, but the target symbol can still be turned off. Signed-off-by: Andreas Ziegler Signed-off-by: Valentin Rothberg Signed-off-by: Greg Kroah-Hartman --- scripts/checkkconfigsymbols.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/checkkconfigsymbols.py b/scripts/checkkconfigsymbols.py index f1d6b14081ac..8cd16c65d3c5 100755 --- a/scripts/checkkconfigsymbols.py +++ b/scripts/checkkconfigsymbols.py @@ -24,7 +24,7 @@ SYMBOL = r"(?:\w*[A-Z0-9]\w*){2,}" DEF = r"^\s*(?:menu){,1}config\s+(" + SYMBOL + r")\s*" EXPR = r"(?:" + OPERATORS + r"|\s|" + SYMBOL + r")+" DEFAULT = r"default\s+.*?(?:if\s.+){,1}" -STMT = r"^\s*(?:if|select|depends\s+on|(?:" + DEFAULT + r"))\s+" + EXPR +STMT = r"^\s*(?:if|select|imply|depends\s+on|(?:" + DEFAULT + r"))\s+" + EXPR SOURCE_SYMBOL = r"(?:\W|\b)+[D]{,1}CONFIG_(" + SYMBOL + r")" # regex objects -- cgit v1.2.3