From 100a8fdbf525bb11796692a713c267be6523a890 Mon Sep 17 00:00:00 2001 From: Punit Agrawal Date: Tue, 29 Jul 2014 11:50:48 +0100 Subject: thermal: trace: Trace temperature changes Create a new event to trace the temperature of a thermal zone. Using this event trace the temperature changes of the thermal zone every-time it is updated. Cc: Zhang Rui Cc: Eduardo Valentin Cc: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Punit Agrawal Signed-off-by: Eduardo Valentin --- drivers/thermal/thermal_core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 71b0ec0..6b32391 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -38,6 +38,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + #include "thermal_core.h" #include "thermal_hwmon.h" @@ -463,6 +466,7 @@ static void update_temperature(struct thermal_zone_device *tz) tz->temperature = temp; mutex_unlock(&tz->lock); + trace_thermal_temperature(tz); dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n", tz->last_temperature, tz->temperature); } -- cgit v1.1 From 39811569e43a81417bc0ddca3d0c7658c3dcd4b0 Mon Sep 17 00:00:00 2001 From: Punit Agrawal Date: Tue, 29 Jul 2014 11:50:49 +0100 Subject: thermal: trace: Trace when a cooling device's state is updated Introduce and use an event to trace when a cooling device's state is updated. This is useful to follow the effect of governor decisions on cooling devices. Cc: Zhang Rui Cc: Eduardo Valentin Cc: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Punit Agrawal Signed-off-by: Eduardo Valentin --- drivers/thermal/thermal_core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 6b32391..c74c78d 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1291,6 +1291,7 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev) mutex_unlock(&cdev->lock); cdev->ops->set_cur_state(cdev, target); cdev->updated = true; + trace_cdev_update(cdev, target); dev_dbg(&cdev->device, "set to state %lu\n", target); } EXPORT_SYMBOL(thermal_cdev_update); -- cgit v1.1 From 208cd822a19e683bc890f6708786f2420e172d76 Mon Sep 17 00:00:00 2001 From: Punit Agrawal Date: Tue, 29 Jul 2014 11:50:50 +0100 Subject: thermal: trace: Trace when temperature is above a trip point Create a new event to trace when the temperature is above a trip point. Use the trace-point when handling non-critical and critical trip pionts. Cc: Zhang Rui Cc: Eduardo Valentin Cc: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Punit Agrawal Signed-off-by: Eduardo Valentin --- drivers/thermal/fair_share.c | 12 ++++++++++++ drivers/thermal/step_wise.c | 5 ++++- drivers/thermal/thermal_core.c | 2 ++ 3 files changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/fair_share.c index 944ba2f..6e0a3fb 100644 --- a/drivers/thermal/fair_share.c +++ b/drivers/thermal/fair_share.c @@ -23,6 +23,7 @@ */ #include +#include #include "thermal_core.h" @@ -34,6 +35,7 @@ static int get_trip_level(struct thermal_zone_device *tz) { int count = 0; unsigned long trip_temp; + enum thermal_trip_type trip_type; if (tz->trips == 0 || !tz->ops->get_trip_temp) return 0; @@ -43,6 +45,16 @@ static int get_trip_level(struct thermal_zone_device *tz) if (tz->temperature < trip_temp) break; } + + /* + * count > 0 only if temperature is greater than first trip + * point, in which case, trip_point = count - 1 + */ + if (count > 0) { + tz->ops->get_trip_type(tz, count - 1, &trip_type); + trace_thermal_zone_trip(tz, count - 1, trip_type); + } + return count; } diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index f251521..3b54c2c 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -23,6 +23,7 @@ */ #include +#include #include "thermal_core.h" @@ -129,8 +130,10 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) trend = get_tz_trend(tz, trip); - if (tz->temperature >= trip_temp) + if (tz->temperature >= trip_temp) { throttle = true; + trace_thermal_zone_trip(tz, trip, trip_type); + } dev_dbg(&tz->device, "Trip%d[type=%d,temp=%ld]:trend=%d,throttle=%d\n", trip, trip_type, trip_temp, trend, throttle); diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index c74c78d..454884a 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -371,6 +371,8 @@ static void handle_critical_trips(struct thermal_zone_device *tz, if (tz->temperature < trip_temp) return; + trace_thermal_zone_trip(tz, trip, trip_type); + if (tz->ops->notify) tz->ops->notify(tz, trip, trip_type); -- cgit v1.1 From a020279ee611c31ac163c00b21b975e7ecbb2e9c Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 25 Jul 2014 15:31:58 +0530 Subject: thermal: add support to disable thermal zone from DTS Add support to check status of the thermal zone before registering the zone. This will help on disabling some non-existing thermal zone from the top level DTS file out of common dtsi thermalzone file. For example, we have 3 platforms almost same but thermal zones on this platform are little bit different. Platform 1 and 2 have three thermal zones and platform 3 has two thermal zones. To avoid duplication of the thermal zones entries on each DTS file of platforms,we created one common dtsi file for thermal zone and included this dtsi file from these 3 platform's top level dts file. On common thermal zone com dtsi file, all thermal zone are enabled and need to disable one of thermal zone on platform 3 dts file. For this, we just added entry status = "disabled" for that thermal zone on platform 3 dts file and along with this change to make it work. This way, we reuse the common file and control the enable/disable of the thermal zone from top level dts file. Signed-off-by: Laxman Dewangan Signed-off-by: Eduardo Valentin --- drivers/thermal/of-thermal.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index 4b2b999..f8eb625 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -401,6 +401,10 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, struct of_phandle_args sensor_specs; int ret, id; + /* Check whether child is enabled or not */ + if (!of_device_is_available(child)) + continue; + /* For now, thermal framework supports only 1 sensor per zone */ ret = of_parse_phandle_with_args(child, "thermal-sensors", "#thermal-sensor-cells", @@ -771,6 +775,10 @@ int __init of_parse_thermal_zones(void) struct thermal_zone_device *zone; struct thermal_zone_params *tzp; + /* Check whether child is enabled or not */ + if (!of_device_is_available(child)) + continue; + tz = thermal_of_build_thermal_zone(child); if (IS_ERR(tz)) { pr_err("failed to build thermal zone %s: %ld\n", @@ -838,6 +846,10 @@ void of_thermal_destroy_zones(void) for_each_child_of_node(np, child) { struct thermal_zone_device *zone; + /* Check whether child is enabled or not */ + if (!of_device_is_available(child)) + continue; + zone = thermal_zone_get_zone_by_name(child->name); if (IS_ERR(zone)) continue; -- cgit v1.1 From 3c94f17e72a7bcf689756da100b6051e535c45f4 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Wed, 6 Aug 2014 15:12:09 +0800 Subject: Thermal: imx: add i.mx6sx thermal support i.MX6SX has some new features of thermal interrupt function, there are LOW, HIGH and PANIC irq for thermal sensor, so add platform data to separate different thermal version; The reset value of LOW ALARM is 0 which means the highest temp, so the LOW ALARM will be triggered once irq is enabled, so we need to correct it before enabling thermal irq; Enable PANIC ALARM as critical trip point, it will trigger system reset via SRC module once PANIC IRQ is triggered, it is pure hardware function, so use it instead of software reset by cooling device. Signed-off-by: Anson Huang Tested-by: Shawn Guo Signed-off-by: Eduardo Valentin --- drivers/thermal/imx_thermal.c | 91 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 2c516f2..461bf3d 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,11 @@ #define MISC0 0x0150 #define MISC0_REFTOP_SELBIASOFF (1 << 3) +#define MISC1 0x0160 +#define MISC1_IRQ_TEMPHIGH (1 << 29) +/* Below LOW and PANIC bits are only for TEMPMON_IMX6SX */ +#define MISC1_IRQ_TEMPLOW (1 << 28) +#define MISC1_IRQ_TEMPPANIC (1 << 27) #define TEMPSENSE0 0x0180 #define TEMPSENSE0_ALARM_VALUE_SHIFT 20 @@ -43,6 +49,12 @@ #define TEMPSENSE1 0x0190 #define TEMPSENSE1_MEASURE_FREQ 0xffff +/* Below TEMPSENSE2 is only for TEMPMON_IMX6SX */ +#define TEMPSENSE2 0x0290 +#define TEMPSENSE2_LOW_VALUE_SHIFT 0 +#define TEMPSENSE2_LOW_VALUE_MASK 0xfff +#define TEMPSENSE2_PANIC_VALUE_SHIFT 16 +#define TEMPSENSE2_PANIC_VALUE_MASK 0xfff0000 #define OCOTP_ANA1 0x04e0 @@ -66,6 +78,21 @@ enum imx_thermal_trip { #define FACTOR1 15976 #define FACTOR2 4297157 +#define TEMPMON_IMX6Q 1 +#define TEMPMON_IMX6SX 2 + +struct thermal_soc_data { + u32 version; +}; + +static struct thermal_soc_data thermal_imx6q_data = { + .version = TEMPMON_IMX6Q, +}; + +static struct thermal_soc_data thermal_imx6sx_data = { + .version = TEMPMON_IMX6SX, +}; + struct imx_thermal_data { struct thermal_zone_device *tz; struct thermal_cooling_device *cdev; @@ -79,8 +106,21 @@ struct imx_thermal_data { bool irq_enabled; int irq; struct clk *thermal_clk; + const struct thermal_soc_data *socdata; }; +static void imx_set_panic_temp(struct imx_thermal_data *data, + signed long panic_temp) +{ + struct regmap *map = data->tempmon; + int critical_value; + + critical_value = (data->c2 - panic_temp) / data->c1; + regmap_write(map, TEMPSENSE2 + REG_CLR, TEMPSENSE2_PANIC_VALUE_MASK); + regmap_write(map, TEMPSENSE2 + REG_SET, critical_value << + TEMPSENSE2_PANIC_VALUE_SHIFT); +} + static void imx_set_alarm_temp(struct imx_thermal_data *data, signed long alarm_temp) { @@ -142,13 +182,17 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) /* See imx_get_sensor_data() for formula derivation */ *temp = data->c2 - n_meas * data->c1; - /* Update alarm value to next higher trip point */ - if (data->alarm_temp == data->temp_passive && *temp >= data->temp_passive) - imx_set_alarm_temp(data, data->temp_critical); - if (data->alarm_temp == data->temp_critical && *temp < data->temp_passive) { - imx_set_alarm_temp(data, data->temp_passive); - dev_dbg(&tz->device, "thermal alarm off: T < %lu\n", - data->alarm_temp / 1000); + /* Update alarm value to next higher trip point for TEMPMON_IMX6Q */ + if (data->socdata->version == TEMPMON_IMX6Q) { + if (data->alarm_temp == data->temp_passive && + *temp >= data->temp_passive) + imx_set_alarm_temp(data, data->temp_critical); + if (data->alarm_temp == data->temp_critical && + *temp < data->temp_passive) { + imx_set_alarm_temp(data, data->temp_passive); + dev_dbg(&tz->device, "thermal alarm off: T < %lu\n", + data->alarm_temp / 1000); + } } if (*temp != data->last_temp) { @@ -398,8 +442,17 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev) return IRQ_HANDLED; } +static const struct of_device_id of_imx_thermal_match[] = { + { .compatible = "fsl,imx6q-tempmon", .data = &thermal_imx6q_data, }, + { .compatible = "fsl,imx6sx-tempmon", .data = &thermal_imx6sx_data, }, + { /* end */ } +}; +MODULE_DEVICE_TABLE(of, of_imx_thermal_match); + static int imx_thermal_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = + of_match_device(of_imx_thermal_match, &pdev->dev); struct imx_thermal_data *data; struct cpumask clip_cpus; struct regmap *map; @@ -418,6 +471,20 @@ static int imx_thermal_probe(struct platform_device *pdev) } data->tempmon = map; + data->socdata = of_id->data; + + /* make sure the IRQ flag is clear before enabling irq on i.MX6SX */ + if (data->socdata->version == TEMPMON_IMX6SX) { + regmap_write(map, MISC1 + REG_CLR, MISC1_IRQ_TEMPHIGH | + MISC1_IRQ_TEMPLOW | MISC1_IRQ_TEMPPANIC); + /* + * reset value of LOW ALARM is incorrect, set it to lowest + * value to avoid false trigger of low alarm. + */ + regmap_write(map, TEMPSENSE2 + REG_SET, + TEMPSENSE2_LOW_VALUE_MASK); + } + data->irq = platform_get_irq(pdev, 0); if (data->irq < 0) return data->irq; @@ -489,6 +556,10 @@ static int imx_thermal_probe(struct platform_device *pdev) measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */ regmap_write(map, TEMPSENSE1 + REG_SET, measure_freq); imx_set_alarm_temp(data, data->temp_passive); + + if (data->socdata->version == TEMPMON_IMX6SX) + imx_set_panic_temp(data, data->temp_critical); + regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); @@ -550,12 +621,6 @@ static int imx_thermal_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops, imx_thermal_suspend, imx_thermal_resume); -static const struct of_device_id of_imx_thermal_match[] = { - { .compatible = "fsl,imx6q-tempmon", }, - { /* end */ } -}; -MODULE_DEVICE_TABLE(of, of_imx_thermal_match); - static struct platform_driver imx_thermal = { .driver = { .name = "imx_thermal", -- cgit v1.1 From cd6d92d2aa1556b22cd05acbc5f2cc8e5caafcc4 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 19 Aug 2014 12:38:01 +0800 Subject: pwm: fsl-ftm: Clean up the code This patch intends to prepare for converting to direct regmap API usage. Signed-off-by: Xiubo Li Signed-off-by: Thierry Reding --- drivers/pwm/pwm-fsl-ftm.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index a18bc8f..96982da 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -21,11 +21,10 @@ #include #define FTM_SC 0x00 -#define FTM_SC_CLK_MASK 0x3 -#define FTM_SC_CLK_SHIFT 3 -#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_SHIFT) +#define FTM_SC_CLK_MASK_SHIFT 3 +#define FTM_SC_CLK_MASK (3 << FTM_SC_CLK_MASK_SHIFT) +#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_MASK_SHIFT) #define FTM_SC_PS_MASK 0x7 -#define FTM_SC_PS_SHIFT 0 #define FTM_CNT 0x04 #define FTM_MOD 0x08 @@ -258,7 +257,7 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, } val = readl(fpc->base + FTM_SC); - val &= ~(FTM_SC_PS_MASK << FTM_SC_PS_SHIFT); + val &= ~FTM_SC_PS_MASK; val |= fpc->clk_ps; writel(val, fpc->base + FTM_SC); writel(period - 1, fpc->base + FTM_MOD); @@ -305,7 +304,7 @@ static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) /* select counter clock source */ val = readl(fpc->base + FTM_SC); - val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT); + val &= ~FTM_SC_CLK_MASK; val |= FTM_SC_CLK(fpc->cnt_select); writel(val, fpc->base + FTM_SC); @@ -357,7 +356,7 @@ static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) /* no users left, disable PWM counter clock */ val = readl(fpc->base + FTM_SC); - val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT); + val &= ~FTM_SC_CLK_MASK; writel(val, fpc->base + FTM_SC); clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); -- cgit v1.1 From 42fa98a9c3609c1aff466cb847e421c611cc9157 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 19 Aug 2014 12:38:02 +0800 Subject: pwm: fsl-ftm: Convert to direct regmap API usage The regmap core supports different endian modes for devices. This patch convert to direct regmap API usage, preparing to support big endianness for LS1 SoC. Using the regmap framework it will be easy to support devices that only differ in endianness with the same device driver. Signed-off-by: Xiubo Li Signed-off-by: Thierry Reding --- drivers/pwm/pwm-fsl-ftm.c | 83 +++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index 96982da..0f2cc7e 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #define FTM_SC 0x00 @@ -82,7 +83,7 @@ struct fsl_pwm_chip { unsigned int cnt_select; unsigned int clk_ps; - void __iomem *base; + struct regmap *regmap; int period_ns; @@ -218,10 +219,11 @@ static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, unsigned long period_ns, unsigned long duty_ns) { - unsigned long long val, duty; + unsigned long long duty; + u32 val; - val = readl(fpc->base + FTM_MOD); - duty = duty_ns * (val + 1); + regmap_read(fpc->regmap, FTM_MOD, &val); + duty = (unsigned long long)duty_ns * (val + 1); do_div(duty, period_ns); return (unsigned long)duty; @@ -231,7 +233,7 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct fsl_pwm_chip *fpc = to_fsl_chip(chip); - u32 val, period, duty; + u32 period, duty; mutex_lock(&fpc->lock); @@ -256,11 +258,9 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, return -EINVAL; } - val = readl(fpc->base + FTM_SC); - val &= ~FTM_SC_PS_MASK; - val |= fpc->clk_ps; - writel(val, fpc->base + FTM_SC); - writel(period - 1, fpc->base + FTM_MOD); + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK, + fpc->clk_ps); + regmap_write(fpc->regmap, FTM_MOD, period - 1); fpc->period_ns = period_ns; } @@ -269,8 +269,9 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns); - writel(FTM_CSC_MSB | FTM_CSC_ELSB, fpc->base + FTM_CSC(pwm->hwpwm)); - writel(duty, fpc->base + FTM_CV(pwm->hwpwm)); + regmap_write(fpc->regmap, FTM_CSC(pwm->hwpwm), + FTM_CSC_MSB | FTM_CSC_ELSB); + regmap_write(fpc->regmap, FTM_CV(pwm->hwpwm), duty); return 0; } @@ -282,31 +283,28 @@ static int fsl_pwm_set_polarity(struct pwm_chip *chip, struct fsl_pwm_chip *fpc = to_fsl_chip(chip); u32 val; - val = readl(fpc->base + FTM_POL); + regmap_read(fpc->regmap, FTM_POL, &val); if (polarity == PWM_POLARITY_INVERSED) val |= BIT(pwm->hwpwm); else val &= ~BIT(pwm->hwpwm); - writel(val, fpc->base + FTM_POL); + regmap_write(fpc->regmap, FTM_POL, val); return 0; } static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) { - u32 val; int ret; if (fpc->use_count != 0) return 0; /* select counter clock source */ - val = readl(fpc->base + FTM_SC); - val &= ~FTM_SC_CLK_MASK; - val |= FTM_SC_CLK(fpc->cnt_select); - writel(val, fpc->base + FTM_SC); + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, + FTM_SC_CLK(fpc->cnt_select)); ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]); if (ret) @@ -326,13 +324,10 @@ static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct fsl_pwm_chip *fpc = to_fsl_chip(chip); - u32 val; int ret; mutex_lock(&fpc->lock); - val = readl(fpc->base + FTM_OUTMASK); - val &= ~BIT(pwm->hwpwm); - writel(val, fpc->base + FTM_OUTMASK); + regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), 0); ret = fsl_counter_clock_enable(fpc); mutex_unlock(&fpc->lock); @@ -342,8 +337,6 @@ static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) { - u32 val; - /* * already disabled, do nothing */ @@ -355,9 +348,7 @@ static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) return; /* no users left, disable PWM counter clock */ - val = readl(fpc->base + FTM_SC); - val &= ~FTM_SC_CLK_MASK; - writel(val, fpc->base + FTM_SC); + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, 0); clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); clk_disable_unprepare(fpc->clk[fpc->cnt_select]); @@ -369,14 +360,12 @@ static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) u32 val; mutex_lock(&fpc->lock); - val = readl(fpc->base + FTM_OUTMASK); - val |= BIT(pwm->hwpwm); - writel(val, fpc->base + FTM_OUTMASK); + regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), + BIT(pwm->hwpwm)); fsl_counter_clock_disable(fpc); - val = readl(fpc->base + FTM_OUTMASK); - + regmap_read(fpc->regmap, FTM_OUTMASK, &val); if ((val & 0xFF) == 0xFF) fpc->period_ns = 0; @@ -401,19 +390,28 @@ static int fsl_pwm_init(struct fsl_pwm_chip *fpc) if (ret) return ret; - writel(0x00, fpc->base + FTM_CNTIN); - writel(0x00, fpc->base + FTM_OUTINIT); - writel(0xFF, fpc->base + FTM_OUTMASK); + regmap_write(fpc->regmap, FTM_CNTIN, 0x00); + regmap_write(fpc->regmap, FTM_OUTINIT, 0x00); + regmap_write(fpc->regmap, FTM_OUTMASK, 0xFF); clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); return 0; } +static const struct regmap_config fsl_pwm_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = FTM_PWMLOAD, +}; + static int fsl_pwm_probe(struct platform_device *pdev) { struct fsl_pwm_chip *fpc; struct resource *res; + void __iomem *base; int ret; fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL); @@ -425,9 +423,16 @@ static int fsl_pwm_probe(struct platform_device *pdev) fpc->chip.dev = &pdev->dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fpc->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(fpc->base)) - return PTR_ERR(fpc->base); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + fpc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, + &fsl_pwm_regmap_config); + if (IS_ERR(fpc->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(fpc->regmap); + } fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys"); if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) { -- cgit v1.1 From 00018a8ae5c552a2464e0df15437511ba4f56495 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 21 Aug 2014 20:50:25 -0300 Subject: pwm: fsl-ftm: Select REGMAP_MMIO Commit 42fa98a9c360 ("pwm: fsl-ftm: Convert to direct regmap API usage") introduced the following error when REGMAP_MMIO=n: drivers/built-in.o: In function `fsl_pwm_probe': >> pwm-fsl-ftm.c:(.text+0xd7d7): undefined reference to `devm_regmap_init_mmio_clk' Select select REGMAP_MMIO in order to fix this error. Reported-by: kbuild test robot Signed-off-by: Fabio Estevam Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index b800783..e56e91e 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -101,6 +101,7 @@ config PWM_EP93XX config PWM_FSL_FTM tristate "Freescale FlexTimer Module (FTM) PWM support" depends on OF + select REGMAP_MMIO help Generic FTM PWM framework driver for Freescale VF610 and Layerscape LS-1 SoCs. -- cgit v1.1 From 373c57829a3f9da1405b1fbd3d17e50f8e1f476e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 19 Aug 2014 17:18:29 +0300 Subject: pwm: lpss: Add ACPI and PCI IDs for Intel Braswell This is pretty much the same as Baytrail PWM. Only difference is that the input clock runs on different frequency. Signed-off-by: Alan Cox Signed-off-by: Mika Westerberg Signed-off-by: Thierry Reding --- drivers/pwm/pwm-lpss.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 4df994f..d04eee7 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -48,6 +48,11 @@ static const struct pwm_lpss_boardinfo byt_info = { 25000000 }; +/* Braswell */ +static const struct pwm_lpss_boardinfo bsw_info = { + 19200000 +}; + static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip) { return container_of(chip, struct pwm_lpss_chip, chip); @@ -189,6 +194,8 @@ static void pwm_lpss_remove_pci(struct pci_dev *pdev) static struct pci_device_id pwm_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&byt_info}, { PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&byt_info}, + { PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&bsw_info}, + { PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&bsw_info}, { }, }; MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids); @@ -231,6 +238,7 @@ static int pwm_lpss_remove_platform(struct platform_device *pdev) static const struct acpi_device_id pwm_lpss_acpi_match[] = { { "80860F09", (unsigned long)&byt_info }, + { "80862288", (unsigned long)&bsw_info }, { }, }; MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match); -- cgit v1.1 From c558e39e14c2372394f49e07fbe94e9708b615cb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Aug 2014 19:17:35 +0300 Subject: pwm: lpss: Properly split driver to parts The driver consists of core, PCI, and platform parts. It would be better to split them into separate files. The platform driver is now called pwm-lpss-platform. Thus, previously set CONFIG_PWM_LPSS=m is not enough to build it. But we are on the safe side since it seems no one from outside Intel is using it for now. While here, move to use macros module_pci_driver() and module_platform_driver(). Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Acked-by: Alan Cox [thierry.reding: change select to depends on PWM_LPSS, cleanup] Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 19 +++++- drivers/pwm/Makefile | 2 + drivers/pwm/pwm-lpss-pci.c | 65 +++++++++++++++++++ drivers/pwm/pwm-lpss-platform.c | 68 ++++++++++++++++++++ drivers/pwm/pwm-lpss.c | 136 +++------------------------------------- drivers/pwm/pwm-lpss.h | 32 ++++++++++ 6 files changed, 195 insertions(+), 127 deletions(-) create mode 100644 drivers/pwm/pwm-lpss-pci.c create mode 100644 drivers/pwm/pwm-lpss-platform.c create mode 100644 drivers/pwm/pwm-lpss.h (limited to 'drivers') diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e56e91e..0907416 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -150,7 +150,6 @@ config PWM_LPC32XX config PWM_LPSS tristate "Intel LPSS PWM support" - depends on ACPI help Generic PWM framework driver for Intel Low Power Subsystem PWM controller. @@ -158,6 +157,24 @@ config PWM_LPSS To compile this driver as a module, choose M here: the module will be called pwm-lpss. +config PWM_LPSS_PCI + tristate "Intel LPSS PWM PCI driver" + depends on PWM_LPSS && PCI + help + The PCI driver for Intel Low Power Subsystem PWM controller. + + To compile this driver as a module, choose M here: the module + will be called pwm-lpss-pci. + +config PWM_LPSS_PLATFORM + tristate "Intel LPSS PWM platform driver" + depends on PWM_LPSS && ACPI + help + The platform driver for Intel Low Power Subsystem PWM controller. + + To compile this driver as a module, choose M here: the module + will be called pwm-lpss-platform. + config PWM_MXS tristate "Freescale MXS PWM support" depends on ARCH_MXS && OF diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index f8c577d..c458606 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -13,6 +13,8 @@ obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o +obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o +obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c new file mode 100644 index 0000000..1bfdd89 --- /dev/null +++ b/drivers/pwm/pwm-lpss-pci.c @@ -0,0 +1,65 @@ +/* + * Intel Low Power Subsystem PWM controller PCI driver + * + * Copyright (C) 2014, Intel Corporation + * + * Derived from the original pwm-lpss.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "pwm-lpss.h" + +static int pwm_lpss_probe_pci(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + const struct pwm_lpss_boardinfo *info; + struct pwm_lpss_chip *lpwm; + int err; + + err = pci_enable_device(pdev); + if (err < 0) + return err; + + info = (struct pwm_lpss_boardinfo *)id->driver_data; + lpwm = pwm_lpss_probe(&pdev->dev, &pdev->resource[0], info); + if (IS_ERR(lpwm)) + return PTR_ERR(lpwm); + + pci_set_drvdata(pdev, lpwm); + return 0; +} + +static void pwm_lpss_remove_pci(struct pci_dev *pdev) +{ + struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev); + + pwm_lpss_remove(lpwm); + pci_disable_device(pdev); +} + +static const struct pci_device_id pwm_lpss_pci_ids[] = { + { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info}, + { PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&pwm_lpss_byt_info}, + { PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info}, + { PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info}, + { }, +}; +MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids); + +static struct pci_driver pwm_lpss_driver_pci = { + .name = "pwm-lpss", + .id_table = pwm_lpss_pci_ids, + .probe = pwm_lpss_probe_pci, + .remove = pwm_lpss_remove_pci, +}; +module_pci_driver(pwm_lpss_driver_pci); + +MODULE_DESCRIPTION("PWM PCI driver for Intel LPSS"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c new file mode 100644 index 0000000..18a9c88 --- /dev/null +++ b/drivers/pwm/pwm-lpss-platform.c @@ -0,0 +1,68 @@ +/* + * Intel Low Power Subsystem PWM controller driver + * + * Copyright (C) 2014, Intel Corporation + * + * Derived from the original pwm-lpss.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "pwm-lpss.h" + +static int pwm_lpss_probe_platform(struct platform_device *pdev) +{ + const struct pwm_lpss_boardinfo *info; + const struct acpi_device_id *id; + struct pwm_lpss_chip *lpwm; + struct resource *r; + + id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); + if (!id) + return -ENODEV; + + info = (const struct pwm_lpss_boardinfo *)id->driver_data; + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + lpwm = pwm_lpss_probe(&pdev->dev, r, info); + if (IS_ERR(lpwm)) + return PTR_ERR(lpwm); + + platform_set_drvdata(pdev, lpwm); + return 0; +} + +static int pwm_lpss_remove_platform(struct platform_device *pdev) +{ + struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev); + + return pwm_lpss_remove(lpwm); +} + +static const struct acpi_device_id pwm_lpss_acpi_match[] = { + { "80860F09", (unsigned long)&pwm_lpss_byt_info }, + { "80862288", (unsigned long)&pwm_lpss_bsw_info }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match); + +static struct platform_driver pwm_lpss_driver_platform = { + .driver = { + .name = "pwm-lpss", + .acpi_match_table = pwm_lpss_acpi_match, + }, + .probe = pwm_lpss_probe_platform, + .remove = pwm_lpss_remove_platform, +}; +module_platform_driver(pwm_lpss_driver_platform); + +MODULE_DESCRIPTION("PWM platform driver for Intel LPSS"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:pwm-lpss"); diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index d04eee7..ce9bf14 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -13,15 +13,10 @@ * published by the Free Software Foundation. */ -#include -#include #include #include -#include -#include -#include -static int pci_drv, plat_drv; /* So we know which drivers registered */ +#include "pwm-lpss.h" #define PWM 0x00000000 #define PWM_ENABLE BIT(31) @@ -39,19 +34,17 @@ struct pwm_lpss_chip { unsigned long clk_rate; }; -struct pwm_lpss_boardinfo { - unsigned long clk_rate; -}; - /* BayTrail */ -static const struct pwm_lpss_boardinfo byt_info = { +const struct pwm_lpss_boardinfo pwm_lpss_byt_info = { 25000000 }; +EXPORT_SYMBOL_GPL(pwm_lpss_byt_info); /* Braswell */ -static const struct pwm_lpss_boardinfo bsw_info = { +const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = { 19200000 }; +EXPORT_SYMBOL_GPL(pwm_lpss_bsw_info); static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip) { @@ -123,9 +116,8 @@ static const struct pwm_ops pwm_lpss_ops = { .owner = THIS_MODULE, }; -static struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, - struct resource *r, - const struct pwm_lpss_boardinfo *info) +struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, + const struct pwm_lpss_boardinfo *info) { struct pwm_lpss_chip *lpwm; int ret; @@ -152,8 +144,9 @@ static struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, return lpwm; } +EXPORT_SYMBOL_GPL(pwm_lpss_probe); -static int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) +int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) { u32 ctrl; @@ -162,117 +155,8 @@ static int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) return pwmchip_remove(&lpwm->chip); } - -static int pwm_lpss_probe_pci(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - const struct pwm_lpss_boardinfo *info; - struct pwm_lpss_chip *lpwm; - int err; - - err = pci_enable_device(pdev); - if (err < 0) - return err; - - info = (struct pwm_lpss_boardinfo *)id->driver_data; - lpwm = pwm_lpss_probe(&pdev->dev, &pdev->resource[0], info); - if (IS_ERR(lpwm)) - return PTR_ERR(lpwm); - - pci_set_drvdata(pdev, lpwm); - return 0; -} - -static void pwm_lpss_remove_pci(struct pci_dev *pdev) -{ - struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev); - - pwm_lpss_remove(lpwm); - pci_disable_device(pdev); -} - -static struct pci_device_id pwm_lpss_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&byt_info}, - { PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&byt_info}, - { PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&bsw_info}, - { PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&bsw_info}, - { }, -}; -MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids); - -static struct pci_driver pwm_lpss_driver_pci = { - .name = "pwm-lpss", - .id_table = pwm_lpss_pci_ids, - .probe = pwm_lpss_probe_pci, - .remove = pwm_lpss_remove_pci, -}; - -static int pwm_lpss_probe_platform(struct platform_device *pdev) -{ - const struct pwm_lpss_boardinfo *info; - const struct acpi_device_id *id; - struct pwm_lpss_chip *lpwm; - struct resource *r; - - id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); - if (!id) - return -ENODEV; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - info = (struct pwm_lpss_boardinfo *)id->driver_data; - lpwm = pwm_lpss_probe(&pdev->dev, r, info); - if (IS_ERR(lpwm)) - return PTR_ERR(lpwm); - - platform_set_drvdata(pdev, lpwm); - return 0; -} - -static int pwm_lpss_remove_platform(struct platform_device *pdev) -{ - struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev); - - return pwm_lpss_remove(lpwm); -} - -static const struct acpi_device_id pwm_lpss_acpi_match[] = { - { "80860F09", (unsigned long)&byt_info }, - { "80862288", (unsigned long)&bsw_info }, - { }, -}; -MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match); - -static struct platform_driver pwm_lpss_driver_platform = { - .driver = { - .name = "pwm-lpss", - .acpi_match_table = pwm_lpss_acpi_match, - }, - .probe = pwm_lpss_probe_platform, - .remove = pwm_lpss_remove_platform, -}; - -static int __init pwm_init(void) -{ - pci_drv = pci_register_driver(&pwm_lpss_driver_pci); - plat_drv = platform_driver_register(&pwm_lpss_driver_platform); - if (pci_drv && plat_drv) - return pci_drv; - - return 0; -} -module_init(pwm_init); - -static void __exit pwm_exit(void) -{ - if (!pci_drv) - pci_unregister_driver(&pwm_lpss_driver_pci); - if (!plat_drv) - platform_driver_unregister(&pwm_lpss_driver_platform); -} -module_exit(pwm_exit); +EXPORT_SYMBOL_GPL(pwm_lpss_remove); MODULE_DESCRIPTION("PWM driver for Intel LPSS"); MODULE_AUTHOR("Mika Westerberg "); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:pwm-lpss"); diff --git a/drivers/pwm/pwm-lpss.h b/drivers/pwm/pwm-lpss.h new file mode 100644 index 0000000..aa041bb --- /dev/null +++ b/drivers/pwm/pwm-lpss.h @@ -0,0 +1,32 @@ +/* + * Intel Low Power Subsystem PWM controller driver + * + * Copyright (C) 2014, Intel Corporation + * + * Derived from the original pwm-lpss.c + * + * 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 __PWM_LPSS_H +#define __PWM_LPSS_H + +#include +#include + +struct pwm_lpss_chip; + +struct pwm_lpss_boardinfo { + unsigned long clk_rate; +}; + +extern const struct pwm_lpss_boardinfo pwm_lpss_byt_info; +extern const struct pwm_lpss_boardinfo pwm_lpss_bsw_info; + +struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, + const struct pwm_lpss_boardinfo *info); +int pwm_lpss_remove(struct pwm_lpss_chip *lpwm); + +#endif /* __PWM_LPSS_H */ -- cgit v1.1 From 90927fe9a001340304e0c37dee578e4432b1744e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Aug 2014 19:17:36 +0300 Subject: pwm: lpss: pci: Move to use pcim_enable_device() Let's use managed functions for this driver. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Thierry Reding --- drivers/pwm/pwm-lpss-pci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c index 1bfdd89..cf20d2b 100644 --- a/drivers/pwm/pwm-lpss-pci.c +++ b/drivers/pwm/pwm-lpss-pci.c @@ -23,7 +23,7 @@ static int pwm_lpss_probe_pci(struct pci_dev *pdev, struct pwm_lpss_chip *lpwm; int err; - err = pci_enable_device(pdev); + err = pcim_enable_device(pdev); if (err < 0) return err; @@ -41,7 +41,6 @@ static void pwm_lpss_remove_pci(struct pci_dev *pdev) struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev); pwm_lpss_remove(lpwm); - pci_disable_device(pdev); } static const struct pci_device_id pwm_lpss_pci_ids[] = { -- cgit v1.1 From e0c86a3b63e948e51a47d17382c7cd8711d19750 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Sat, 23 Aug 2014 00:22:45 +0200 Subject: pwm: lpss: Fix build failure on PowerPC An x86 build seems to pull in the linux/io.h include indirectly. On PowerPC that doesn't happen and the build breaks due to the readl() and writel() functions not being declared. Fix this by explicitly including linux/io.h. Reported-by: Stephen Rothwell Signed-off-by: Thierry Reding --- drivers/pwm/pwm-lpss.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index ce9bf14..7a7a934 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -13,6 +13,7 @@ * published by the Free Software Foundation. */ +#include #include #include -- cgit v1.1 From b2b7adeb21745266326d453b95e5d0b1b9cb1d4e Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 23 Aug 2014 13:20:25 +0200 Subject: pwm: lpss: use c99 initializers in structures Use c99 initializers for structures. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @decl@ identifier i1,fld; type T; field list[n] fs; @@ struct i1 { fs T fld; ...}; @bad@ identifier decl.i1,i2; expression e; initializer list[decl.n] is; @@ struct i1 i2 = { is, + .fld = e - e ,...}; // Signed-off-by: Julia Lawall [thierry.reding: rebased and applied same fix for Braswell] Signed-off-by: Thierry Reding --- drivers/pwm/pwm-lpss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 7a7a934..e979825 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -37,13 +37,13 @@ struct pwm_lpss_chip { /* BayTrail */ const struct pwm_lpss_boardinfo pwm_lpss_byt_info = { - 25000000 + .clk_rate = 25000000 }; EXPORT_SYMBOL_GPL(pwm_lpss_byt_info); /* Braswell */ const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = { - 19200000 + .clk_rate = 19200000 }; EXPORT_SYMBOL_GPL(pwm_lpss_bsw_info); -- cgit v1.1 From ad16202de8d884c10ef7637ea3982953519c2418 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 25 Aug 2014 12:38:31 +0300 Subject: pwm: lpss: make it buildable only on X86 There is no sign of this IP block on non-x86 architectures and rather will not be. Thus, make this explicit by applying a direct dependency to X86. Signed-off-by: Andy Shevchenko Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 0907416..3865dfb 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -150,6 +150,7 @@ config PWM_LPC32XX config PWM_LPSS tristate "Intel LPSS PWM support" + depends on X86 help Generic PWM framework driver for Intel Low Power Subsystem PWM controller. -- cgit v1.1 From 533acc0e8df7d6553f11cf91c177211cb6037968 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 18 Aug 2014 17:08:44 +0800 Subject: pwm: Fix possible ZERO_SIZE_PTR pointer dereferencing error. Since we cannot make sure the 'chip->npwm' will always be none zero here, and then if either equal to zero, the kzalloc() will return ZERO_SIZE_PTR, which equals to ((void *)16). So this patch fix this with just doing the zero check before calling kzalloc(). Signed-off-by: Xiubo Li Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index d2c3592..8c748b1 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -236,7 +236,7 @@ int pwmchip_add(struct pwm_chip *chip) int ret; if (!chip || !chip->dev || !chip->ops || !chip->ops->config || - !chip->ops->enable || !chip->ops->disable) + !chip->ops->enable || !chip->ops->disable || !chip->npwm) return -EINVAL; mutex_lock(&pwm_lock); -- cgit v1.1 From bd59bdc898623e6c948a9f900250ce7343cf9012 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Wed, 28 May 2014 18:50:11 +0800 Subject: pwm: imx: Fix the macro MX3_PWMCR_PRESCALER(x) definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds missing parentheses around the argument of the macro MX3_PWMCR_PRESCALER(x) to avoid any potential macro expansion issue. Reported-by: Lothar Waßmann Cc: Thierry Reding Cc: Sascha Hauer Cc: Shawn Guo Cc: Lothar Waßmann Cc: linux-pwm@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Liu Ying Acked-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 5449d91..183225e 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -32,7 +32,7 @@ #define MX3_PWMCR 0x00 /* PWM Control Register */ #define MX3_PWMSAR 0x0C /* PWM Sample Register */ #define MX3_PWMPR 0x10 /* PWM Period Register */ -#define MX3_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4) +#define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4) #define MX3_PWMCR_DOZEEN (1 << 24) #define MX3_PWMCR_WAITEN (1 << 23) #define MX3_PWMCR_DBGEN (1 << 22) -- cgit v1.1 From 40f260c2cebb464dda6916055112963f1421a111 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Wed, 28 May 2014 18:50:12 +0800 Subject: pwm: imx: Cleanup indentation for register definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch contains no logic change to cleanup indentation for register definitions only. Cc: Thierry Reding Cc: Sascha Hauer Cc: Shawn Guo Cc: Lothar Waßmann Cc: linux-pwm@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Liu Ying Acked-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 183225e..fb68534 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -21,24 +21,24 @@ /* i.MX1 and i.MX21 share the same PWM function block: */ -#define MX1_PWMC 0x00 /* PWM Control Register */ -#define MX1_PWMS 0x04 /* PWM Sample Register */ -#define MX1_PWMP 0x08 /* PWM Period Register */ +#define MX1_PWMC 0x00 /* PWM Control Register */ +#define MX1_PWMS 0x04 /* PWM Sample Register */ +#define MX1_PWMP 0x08 /* PWM Period Register */ -#define MX1_PWMC_EN (1 << 4) +#define MX1_PWMC_EN (1 << 4) /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ -#define MX3_PWMCR 0x00 /* PWM Control Register */ -#define MX3_PWMSAR 0x0C /* PWM Sample Register */ -#define MX3_PWMPR 0x10 /* PWM Period Register */ -#define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4) -#define MX3_PWMCR_DOZEEN (1 << 24) -#define MX3_PWMCR_WAITEN (1 << 23) +#define MX3_PWMCR 0x00 /* PWM Control Register */ +#define MX3_PWMSAR 0x0C /* PWM Sample Register */ +#define MX3_PWMPR 0x10 /* PWM Period Register */ +#define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4) +#define MX3_PWMCR_DOZEEN (1 << 24) +#define MX3_PWMCR_WAITEN (1 << 23) #define MX3_PWMCR_DBGEN (1 << 22) -#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) -#define MX3_PWMCR_CLKSRC_IPG (1 << 16) -#define MX3_PWMCR_EN (1 << 0) +#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) +#define MX3_PWMCR_CLKSRC_IPG (1 << 16) +#define MX3_PWMCR_EN (1 << 0) struct imx_chip { struct clk *clk_per; -- cgit v1.1 From 137fd45ffec15db14034990ceac890975cae7a32 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Wed, 28 May 2014 18:50:13 +0800 Subject: pwm: imx: Avoid sample FIFO overflow for i.MX PWM version2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The i.MX PWM version2 is embedded in several i.MX SoCs, such as i.MX27, i.MX51 and i.MX6SL. There is a 4-word (16 bit) sample FIFO in this IP. Each FIFO slot determines the duty period of a PWM waveform in one full cycle. The IP spec mentions that we should not write a fourth sample because the FIFO will become full and triggers a FIFO write error (FWE) which will prevent the PWM from starting once it is enabled. In order to avoid any sample FIFO overflow issue, this patch clears all sample FIFO by doing software reset in the configuration hook when the controller is disabled or waits for a full PWM cycle to get a relinquished FIFO slot when the controller is enabled and the FIFO is fully loaded. The FIFO overflow issue can be reproduced by the following commands on the i.MX6SL EVK platform, assuming we use PWM2 for the debug LED which is driven by the pin HSIC_STROBE and the maximal brightness is 255. echo 0 > /sys/class/leds/user/brightness echo 0 > /sys/class/leds/user/brightness echo 0 > /sys/class/leds/user/brightness echo 0 > /sys/class/leds/user/brightness echo 255 > /sys/class/leds/user/brightness Here, FWE happens (PWMSR register reads 0x58) and the LED can not be lighten. Another way to reproduce the FIFO overflow issue is to run this script: while true; do echo 255 > /sys/class/leds/user/brightness; done Cc: Thierry Reding Cc: Sascha Hauer Cc: Shawn Guo Cc: Lothar Waßmann Cc: linux-pwm@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Liu Ying Acked-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index fb68534..f8b5f10 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,7 @@ /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ #define MX3_PWMCR 0x00 /* PWM Control Register */ +#define MX3_PWMSR 0x04 /* PWM Status Register */ #define MX3_PWMSAR 0x0C /* PWM Sample Register */ #define MX3_PWMPR 0x10 /* PWM Period Register */ #define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4) @@ -38,7 +40,12 @@ #define MX3_PWMCR_DBGEN (1 << 22) #define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) #define MX3_PWMCR_CLKSRC_IPG (1 << 16) +#define MX3_PWMCR_SWR (1 << 3) #define MX3_PWMCR_EN (1 << 0) +#define MX3_PWMSR_FIFOAV_4WORDS 0x4 +#define MX3_PWMSR_FIFOAV_MASK 0x7 + +#define MX3_PWM_SWR_LOOP 5 struct imx_chip { struct clk *clk_per; @@ -103,9 +110,43 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct imx_chip *imx = to_imx_chip(chip); + struct device *dev = chip->dev; unsigned long long c; unsigned long period_cycles, duty_cycles, prescale; - u32 cr; + unsigned int period_ms; + bool enable = test_bit(PWMF_ENABLED, &pwm->flags); + int wait_count = 0, fifoav; + u32 cr, sr; + + /* + * i.MX PWMv2 has a 4-word sample FIFO. + * In order to avoid FIFO overflow issue, we do software reset + * to clear all sample FIFO if the controller is disabled or + * wait for a full PWM cycle to get a relinquished FIFO slot + * when the controller is enabled and the FIFO is fully loaded. + */ + if (enable) { + sr = readl(imx->mmio_base + MX3_PWMSR); + fifoav = sr & MX3_PWMSR_FIFOAV_MASK; + if (fifoav == MX3_PWMSR_FIFOAV_4WORDS) { + period_ms = DIV_ROUND_UP(pwm->period, NSEC_PER_MSEC); + msleep(period_ms); + + sr = readl(imx->mmio_base + MX3_PWMSR); + if (fifoav == (sr & MX3_PWMSR_FIFOAV_MASK)) + dev_warn(dev, "there is no free FIFO slot\n"); + } + } else { + writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR); + do { + usleep_range(200, 1000); + cr = readl(imx->mmio_base + MX3_PWMCR); + } while ((cr & MX3_PWMCR_SWR) && + (wait_count++ < MX3_PWM_SWR_LOOP)); + + if (cr & MX3_PWMCR_SWR) + dev_warn(dev, "software reset timeout\n"); + } c = clk_get_rate(imx->clk_per); c = c * period_ns; @@ -135,7 +176,7 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH; - if (test_bit(PWMF_ENABLED, &pwm->flags)) + if (enable) cr |= MX3_PWMCR_EN; writel(cr, imx->mmio_base + MX3_PWMCR); -- cgit v1.1 From 7264354c0cb8c04bd4a85d24e5d57a0e2417c2fb Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 25 Aug 2014 15:59:25 -0700 Subject: pwm: rockchip: Allow polarity invert on rk3288 The rk3288 has the ability to invert the polarity of the PWM. Let's enable that ability. Note that this increases pwm_cells to 3 for rk3288. Signed-off-by: Doug Anderson Reviewed-by: Caesar Wang Signed-off-by: Thierry Reding --- drivers/pwm/pwm-rockchip.c | 57 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index bdd8644..9442df2 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -24,7 +24,9 @@ #define PWM_ENABLE (1 << 0) #define PWM_CONTINUOUS (1 << 1) #define PWM_DUTY_POSITIVE (1 << 3) +#define PWM_DUTY_NEGATIVE (0 << 3) #define PWM_INACTIVE_NEGATIVE (0 << 4) +#define PWM_INACTIVE_POSITIVE (1 << 4) #define PWM_OUTPUT_LEFT (0 << 5) #define PWM_LP_DISABLE (0 << 8) @@ -45,8 +47,10 @@ struct rockchip_pwm_regs { struct rockchip_pwm_data { struct rockchip_pwm_regs regs; unsigned int prescaler; + const struct pwm_ops *ops; - void (*set_enable)(struct pwm_chip *chip, bool enable); + void (*set_enable)(struct pwm_chip *chip, + struct pwm_device *pwm, bool enable); }; static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) @@ -54,7 +58,8 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) return container_of(c, struct rockchip_pwm_chip, chip); } -static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) +static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, + struct pwm_device *pwm, bool enable) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; @@ -70,14 +75,19 @@ static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) writel_relaxed(val, pc->base + pc->data->regs.ctrl); } -static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, bool enable) +static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, + struct pwm_device *pwm, bool enable) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | - PWM_CONTINUOUS | PWM_DUTY_POSITIVE | - PWM_INACTIVE_NEGATIVE; + PWM_CONTINUOUS; u32 val; + if (pwm->polarity == PWM_POLARITY_INVERSED) + enable_conf |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; + else + enable_conf |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; + val = readl_relaxed(pc->base + pc->data->regs.ctrl); if (enable) @@ -124,6 +134,19 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } +static int rockchip_pwm_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + /* + * No action needed here because pwm->polarity will be set by the core + * and the core will only change polarity when the PWM is not enabled. + * We'll handle things in set_enable(). + */ + + return 0; +} + static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); @@ -133,7 +156,7 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) if (ret) return ret; - pc->data->set_enable(chip, true); + pc->data->set_enable(chip, pwm, true); return 0; } @@ -142,18 +165,26 @@ static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); - pc->data->set_enable(chip, false); + pc->data->set_enable(chip, pwm, false); clk_disable(pc->clk); } -static const struct pwm_ops rockchip_pwm_ops = { +static const struct pwm_ops rockchip_pwm_ops_v1 = { .config = rockchip_pwm_config, .enable = rockchip_pwm_enable, .disable = rockchip_pwm_disable, .owner = THIS_MODULE, }; +static const struct pwm_ops rockchip_pwm_ops_v2 = { + .config = rockchip_pwm_config, + .set_polarity = rockchip_pwm_set_polarity, + .enable = rockchip_pwm_enable, + .disable = rockchip_pwm_disable, + .owner = THIS_MODULE, +}; + static const struct rockchip_pwm_data pwm_data_v1 = { .regs = { .duty = 0x04, @@ -162,6 +193,7 @@ static const struct rockchip_pwm_data pwm_data_v1 = { .ctrl = 0x0c, }, .prescaler = 2, + .ops = &rockchip_pwm_ops_v1, .set_enable = rockchip_pwm_set_enable_v1, }; @@ -173,6 +205,7 @@ static const struct rockchip_pwm_data pwm_data_v2 = { .ctrl = 0x0c, }, .prescaler = 1, + .ops = &rockchip_pwm_ops_v2, .set_enable = rockchip_pwm_set_enable_v2, }; @@ -184,6 +217,7 @@ static const struct rockchip_pwm_data pwm_data_vop = { .ctrl = 0x00, }, .prescaler = 1, + .ops = &rockchip_pwm_ops_v2, .set_enable = rockchip_pwm_set_enable_v2, }; @@ -227,10 +261,15 @@ static int rockchip_pwm_probe(struct platform_device *pdev) pc->data = id->data; pc->chip.dev = &pdev->dev; - pc->chip.ops = &rockchip_pwm_ops; + pc->chip.ops = pc->data->ops; pc->chip.base = -1; pc->chip.npwm = 1; + if (pc->data->ops->set_polarity) { + pc->chip.of_xlate = of_pwm_xlate_with_flags; + pc->chip.of_pwm_n_cells = 3; + } + ret = pwmchip_add(&pc->chip); if (ret < 0) { clk_unprepare(pc->clk); -- cgit v1.1 From e4dbf98f7f169346f57296e173e883b7330076ab Mon Sep 17 00:00:00 2001 From: Peter Feuerer Date: Tue, 22 Jul 2014 17:37:13 +0200 Subject: thermal: Added Bang-bang thermal governor The bang-bang thermal governor uses a hysteresis to switch abruptly on or off a cooling device. It is intended to control fans, which can not be throttled but just switched on or off. Bang-bang cannot be set as default governor as it is intended for special devices only. For those special devices the driver needs to explicitely request it. Cc: Andrew Morton Cc: Zhang Rui Cc: Andreas Mohr Cc: Borislav Petkov Cc: Javi Merino Cc: linux-pm@vger.kernel.org Signed-off-by: Peter Feuerer Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 10 +++ drivers/thermal/Makefile | 1 + drivers/thermal/gov_bang_bang.c | 131 ++++++++++++++++++++++++++++++++++++++++ drivers/thermal/thermal_core.c | 5 ++ drivers/thermal/thermal_core.h | 8 +++ 5 files changed, 155 insertions(+) create mode 100644 drivers/thermal/gov_bang_bang.c (limited to 'drivers') diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 693208e..2500ecc 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -84,6 +84,16 @@ config THERMAL_GOV_STEP_WISE Enable this to manage platform thermals using a simple linear governor. +config THERMAL_GOV_BANG_BANG + bool "Bang Bang thermal governor" + default n + help + Enable this to manage platform thermals using bang bang governor. + + Say 'Y' here if you want to use two point temperature regulation + used for fans without throttling. Some fan drivers depend on this + governor to be enabled (e.g. acerhdf). + config THERMAL_GOV_USER_SPACE bool "User_space thermal governor" help diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 31e232f..b7e6542 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -11,6 +11,7 @@ thermal_sys-$(CONFIG_THERMAL_OF) += of-thermal.o # governors thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o +thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG) += gov_bang_bang.o thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c new file mode 100644 index 0000000..c5dd76b --- /dev/null +++ b/drivers/thermal/gov_bang_bang.c @@ -0,0 +1,131 @@ +/* + * gov_bang_bang.c - A simple thermal throttling governor using hysteresis + * + * Copyright (C) 2014 Peter Feuerer + * + * Based on step_wise.c with following Copyrights: + * Copyright (C) 2012 Intel Corp + * Copyright (C) 2012 Durgadoss R + * + * + * 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 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 "thermal_core.h" + +static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) +{ + long trip_temp; + unsigned long trip_hyst; + struct thermal_instance *instance; + + tz->ops->get_trip_temp(tz, trip, &trip_temp); + tz->ops->get_trip_hyst(tz, trip, &trip_hyst); + + dev_dbg(&tz->device, "Trip%d[temp=%ld]:temp=%d:hyst=%ld\n", + trip, trip_temp, tz->temperature, + trip_hyst); + + mutex_lock(&tz->lock); + + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + if (instance->trip != trip) + continue; + + /* in case fan is in initial state, switch the fan off */ + if (instance->target == THERMAL_NO_TARGET) + instance->target = 0; + + /* in case fan is neither on nor off set the fan to active */ + if (instance->target != 0 && instance->target != 1) { + pr_warn("Thermal instance %s controlled by bang-bang has unexpected state: %ld\n", + instance->name, instance->target); + instance->target = 1; + } + + /* + * enable fan when temperature exceeds trip_temp and disable + * the fan in case it falls below trip_temp minus hysteresis + */ + if (instance->target == 0 && tz->temperature >= trip_temp) + instance->target = 1; + else if (instance->target == 1 && + tz->temperature < trip_temp - trip_hyst) + instance->target = 0; + + dev_dbg(&instance->cdev->device, "target=%d\n", + (int)instance->target); + + instance->cdev->updated = false; /* cdev needs update */ + } + + mutex_unlock(&tz->lock); +} + +/** + * bang_bang_control - controls devices associated with the given zone + * @tz - thermal_zone_device + * @trip - the trip point + * + * Regulation Logic: a two point regulation, deliver cooling state depending + * on the previous state shown in this diagram: + * + * Fan: OFF ON + * + * | + * | + * trip_temp: +---->+ + * | | ^ + * | | | + * | | Temperature + * (trip_temp - hyst): +<----+ + * | + * | + * | + * + * * If the fan is not running and temperature exceeds trip_temp, the fan + * gets turned on. + * * In case the fan is running, temperature must fall below + * (trip_temp - hyst) so that the fan gets turned off again. + * + */ +static int bang_bang_control(struct thermal_zone_device *tz, int trip) +{ + struct thermal_instance *instance; + + thermal_zone_trip_update(tz, trip); + + mutex_lock(&tz->lock); + + list_for_each_entry(instance, &tz->thermal_instances, tz_node) + thermal_cdev_update(instance->cdev); + + mutex_unlock(&tz->lock); + + return 0; +} + +static struct thermal_governor thermal_gov_bang_bang = { + .name = "bang_bang", + .throttle = bang_bang_control, +}; + +int thermal_gov_bang_bang_register(void) +{ + return thermal_register_governor(&thermal_gov_bang_bang); +} + +void thermal_gov_bang_bang_unregister(void) +{ + thermal_unregister_governor(&thermal_gov_bang_bang); +} diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 71b0ec0..4c2726b 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1790,6 +1790,10 @@ static int __init thermal_register_governors(void) if (result) return result; + result = thermal_gov_bang_bang_register(); + if (result) + return result; + return thermal_gov_user_space_register(); } @@ -1797,6 +1801,7 @@ static void thermal_unregister_governors(void) { thermal_gov_step_wise_unregister(); thermal_gov_fair_share_unregister(); + thermal_gov_bang_bang_unregister(); thermal_gov_user_space_unregister(); } diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 3db339f..d15d243 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -69,6 +69,14 @@ static inline int thermal_gov_fair_share_register(void) { return 0; } static inline void thermal_gov_fair_share_unregister(void) {} #endif /* CONFIG_THERMAL_GOV_FAIR_SHARE */ +#ifdef CONFIG_THERMAL_GOV_BANG_BANG +int thermal_gov_bang_bang_register(void); +void thermal_gov_bang_bang_unregister(void); +#else +static inline int thermal_gov_bang_bang_register(void) { return 0; } +static inline void thermal_gov_bang_bang_unregister(void) {} +#endif /* CONFIG_THERMAL_GOV_BANG_BANG */ + #ifdef CONFIG_THERMAL_GOV_USER_SPACE int thermal_gov_user_space_register(void); void thermal_gov_user_space_unregister(void); -- cgit v1.1 From 70145f87139fbc43b726f873813cd91dce371899 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 28 Aug 2014 11:03:14 +0200 Subject: pwm: Fix uninitialized warnings in pwm_get() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With some versions of gcc (e.g. 4.1.2): drivers/pwm/core.c: In function ‘pwm_get’: drivers/pwm/core.c:610: warning: ‘polarity’ may be used uninitialized in this function drivers/pwm/core.c:609: warning: ‘period’ may be used uninitialized in this function While these are false positives, we can get rid of them by refactoring the code to store a pointer to the best match, as suggested before by Thierry Reding. This does require moving the mutex_unlock() down. Fixes: d717ea73e36dd565 ("pwm: Fix period and polarity in pwm_get() for non-perfect matches") Signed-off-by: Geert Uytterhoeven Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 8c748b1..966497d 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -602,12 +602,9 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER); const char *dev_id = dev ? dev_name(dev) : NULL; struct pwm_chip *chip = NULL; - unsigned int index = 0; unsigned int best = 0; - struct pwm_lookup *p; + struct pwm_lookup *p, *chosen = NULL; unsigned int match; - unsigned int period; - enum pwm_polarity polarity; /* look up via DT first */ if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) @@ -653,10 +650,7 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) } if (match > best) { - chip = pwmchip_find_by_name(p->provider); - index = p->index; - period = p->period; - polarity = p->polarity; + chosen = p; if (match != 3) best = match; @@ -665,17 +659,22 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) } } - mutex_unlock(&pwm_lookup_lock); + if (!chosen) + goto out; - if (chip) - pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id); - if (IS_ERR(pwm)) - return pwm; + chip = pwmchip_find_by_name(chosen->provider); + if (!chip) + goto out; - pwm_set_period(pwm, period); - pwm_set_polarity(pwm, polarity); + pwm = pwm_request_from_chip(chip, chosen->index, con_id ?: dev_id); + if (IS_ERR(pwm)) + goto out; + pwm_set_period(pwm, chosen->period); + pwm_set_polarity(pwm, chosen->polarity); +out: + mutex_unlock(&pwm_lookup_lock); return pwm; } EXPORT_SYMBOL_GPL(pwm_get); -- cgit v1.1 From 3230bbfce8a9270acc77fafd0d9ff90e94f28993 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Fri, 14 Mar 2014 00:34:05 +0800 Subject: ACPI: introduce ACPI int340x thermal scan handler Newer laptops and tablets that use ACPI may have thermal sensors and other devices with thermal control capabilities outside the core CPU/SOC, for thermal safety reasons. They are exposed for the OS to use via 1) INT3400 ACPI device object as the master. 2) INT3401 ~ INT340B ACPI device objects as the slaves. This patch introduces a scan handler to enumerate the INT3400 ACPI device object to platform bus, and prevent its slaves from being enumerated before the controller driver being probed. Signed-off-by: Zhang Rui --- drivers/acpi/Makefile | 1 + drivers/acpi/int340x_thermal.c | 51 ++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/internal.h | 1 + drivers/acpi/scan.c | 1 + drivers/thermal/Kconfig | 17 ++++++++++++++ 5 files changed, 71 insertions(+) create mode 100644 drivers/acpi/int340x_thermal.c (limited to 'drivers') diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 505d4d7..c3b2fcb 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -43,6 +43,7 @@ acpi-y += pci_root.o pci_link.o pci_irq.o acpi-y += acpi_lpss.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o +acpi-y += int340x_thermal.o acpi-y += power.o acpi-y += event.o acpi-y += sysfs.o diff --git a/drivers/acpi/int340x_thermal.c b/drivers/acpi/int340x_thermal.c new file mode 100644 index 0000000..2103bb6 --- /dev/null +++ b/drivers/acpi/int340x_thermal.c @@ -0,0 +1,51 @@ +/* + * ACPI support for int340x thermal drivers + * + * Copyright (C) 2014, Intel Corporation + * Authors: Zhang Rui + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "internal.h" + +#define DO_ENUMERATION 0x01 +static const struct acpi_device_id int340x_thermal_device_ids[] = { + {"INT3400", DO_ENUMERATION }, + {"INT3401"}, + {"INT3402"}, + {"INT3403"}, + {"INT3404"}, + {"INT3406"}, + {"INT3407"}, + {"INT3408"}, + {"INT3409"}, + {"INT340A"}, + {"INT340B"}, + {""}, +}; + +static int int340x_thermal_handler_attach(struct acpi_device *adev, + const struct acpi_device_id *id) +{ +#ifdef CONFIG_INT340X_THERMAL + if (id->driver_data == DO_ENUMERATION) + acpi_create_platform_device(adev); +#endif + return 1; +} + +static struct acpi_scan_handler int340x_thermal_handler = { + .ids = int340x_thermal_device_ids, + .attach = int340x_thermal_handler_attach, +}; + +void __init acpi_int340x_thermal_init(void) +{ + acpi_scan_add_handler(&int340x_thermal_handler); +} diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 4c5cf77..de47f9f 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -31,6 +31,7 @@ void acpi_pci_link_init(void); void acpi_processor_init(void); void acpi_platform_init(void); void acpi_pnp_init(void); +void acpi_int340x_thermal_init(void); int acpi_sysfs_init(void); void acpi_container_init(void); void acpi_memory_hotplug_init(void); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 0a817ad..eed9740 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2306,6 +2306,7 @@ int __init acpi_scan_init(void) acpi_container_init(); acpi_memory_hotplug_init(); acpi_pnp_init(); + acpi_int340x_thermal_init(); mutex_lock(&acpi_scan_lock); /* diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 693208e..2ff7416 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -234,6 +234,23 @@ config INTEL_SOC_DTS_THERMAL notification methods.The other trip is a critical trip point, which was set by the driver based on the TJ MAX temperature. +config INT340X_THERMAL + bool + depends on X86 && ACPI + help + Newer laptops and tablets that use ACPI may have thermal sensors and + other devices with thermal control capabilities outside the core + CPU/SOC, for thermal safety reasons. + They are exposed for the OS to use via the INT3400 ACPI device object + as the master, and INT3401~INT340B ACPI device objects as the slaves. + Enable this to expose the temperature information and cooling ability + from these objects to userspace via the normal thermal framework. + This means that a wide range of applications and GUI widgets can show + the information to the user or use this information for making + decisions. For example, the Intel Thermal Daemon can use this + information to allow the user to select his laptop to run without + turning on the fans. + menu "Texas Instruments thermal drivers" source "drivers/thermal/ti-soc-thermal/Kconfig" endmenu -- cgit v1.1 From 047133066e6c2549403fe5a2d619f47ba4212ef5 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Thu, 7 Aug 2014 05:10:22 -0700 Subject: leds: Reorder include directives Reorder include directives so that they are arranged in alphabetical order. Signed-off-by: Jacek Anaszewski Acked-by: Kyungmin Park Cc: Richard Purdie Signed-off-by: Bryan Wu --- drivers/leds/led-class.c | 13 +++++++------ drivers/leds/led-core.c | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index aa29198..4bd4572 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -9,16 +9,17 @@ * published by the Free Software Foundation. */ -#include -#include +#include +#include +#include #include +#include +#include #include +#include +#include #include -#include #include -#include -#include -#include #include "leds.h" static struct class *leds_class; diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 71b40d3..50b579a 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -12,10 +12,11 @@ */ #include +#include #include #include +#include #include -#include #include "leds.h" DECLARE_RWSEM(leds_list_lock); -- cgit v1.1 From 3841961269f76db243339a94005729f10829911e Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Thu, 7 Aug 2014 05:10:24 -0700 Subject: leds: avoid using DEVICE_ATTR macro for max_brightness attribute Make definition of the brightness related sysfs attributes consistent. The modification entails change of the function name: led_max_brightness_show -> max_brightness_show Signed-off-by: Jacek Anaszewski Acked-by: Sakari Ailus Acked-by: Kyungmin Park Cc: Richard Purdie Signed-off-by: Bryan Wu --- drivers/leds/led-class.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 4bd4572..71666a4 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -60,14 +60,14 @@ static ssize_t brightness_store(struct device *dev, } static DEVICE_ATTR_RW(brightness); -static ssize_t led_max_brightness_show(struct device *dev, +static ssize_t max_brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); return sprintf(buf, "%u\n", led_cdev->max_brightness); } -static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL); +static DEVICE_ATTR_RO(max_brightness); #ifdef CONFIG_LEDS_TRIGGERS static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); -- cgit v1.1 From 7f14e6b9c36f6696eb937bc0cf86a7732aa89904 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Fri, 8 Aug 2014 00:09:44 -0700 Subject: leds: lp3944: fix sparse warning Fix sparse warning appeared after changing brightness type in the leds.h from int to enum led_brightness. Signed-off-by: Jacek Anaszewski Acked-by: Kyungmin Park Cc: Richard Purdie Signed-off-by: Bryan Wu --- drivers/leds/leds-lp3944.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c index 8e1abdc..53144fb 100644 --- a/drivers/leds/leds-lp3944.c +++ b/drivers/leds/leds-lp3944.c @@ -335,7 +335,8 @@ static int lp3944_configure(struct i2c_client *client, } /* to expose the default value to userspace */ - led->ldev.brightness = led->status; + led->ldev.brightness = + (enum led_brightness) led->status; /* Set the default led status */ err = lp3944_led_set(led, led->status); -- cgit v1.1 From 914ae25a62e77ebbfa0ce7cbc60edd01cc4d1d31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Tue, 9 Sep 2014 00:40:32 -0700 Subject: leds: trigger: gpio: fix warning in gpio trigger for gpios whose accessor function may sleep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using a GPIO driver whose accessor functions may sleep (e.g. an I2C GPIO extender like PCA9554) the following warning is issued: WARNING: CPU: 0 PID: 665 at drivers/gpio/gpiolib.c:2274 gpiod_get_raw_value+0x3c/0x48() Modules linked in: CPU: 0 PID: 665 Comm: kworker/0:2 Not tainted 3.16.0-karo+ #115 Workqueue: events gpio_trig_work [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (warn_slowpath_common+0x64/0x84) [] (warn_slowpath_common) from [] (warn_slowpath_null+0x1c/0x24) [] (warn_slowpath_null) from [] (gpiod_get_raw_value+0x3c/0x48) [] (gpiod_get_raw_value) from [] (gpio_trig_work+0x1c/0xb0) [] (gpio_trig_work) from [] (process_one_work+0x144/0x38c) [] (process_one_work) from [] (worker_thread+0x60/0x5cc) [] (worker_thread) from [] (kthread+0xb4/0xd0) [] (kthread) from [] (ret_from_fork+0x14/0x24) ---[ end trace cd51a1dad8b86c9c ]--- Fix this by using the _cansleep() variant of gpio_get_value(). Signed-off-by: Lothar Waßmann Signed-off-by: Bryan Wu --- drivers/leds/trigger/ledtrig-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/leds/trigger/ledtrig-gpio.c b/drivers/leds/trigger/ledtrig-gpio.c index 35812e3..c86c418 100644 --- a/drivers/leds/trigger/ledtrig-gpio.c +++ b/drivers/leds/trigger/ledtrig-gpio.c @@ -48,7 +48,7 @@ static void gpio_trig_work(struct work_struct *work) if (!gpio_data->gpio) return; - tmp = gpio_get_value(gpio_data->gpio); + tmp = gpio_get_value_cansleep(gpio_data->gpio); if (gpio_data->inverted) tmp = !tmp; -- cgit v1.1 From 3ef7de5304edf60d0b8674dd7cdacc104e15a93c Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Wed, 20 Aug 2014 06:41:55 -0700 Subject: leds: Improve and export led_update_brightness led_update_brightness helper function used to be exploited only locally in the led-class.c module, where its result was being passed to the brightness_show sysfs callback. With the introduction of v4l2-flash subdevice the same functionality becomes required for reading current brightness from a LED device. This patch adds checking of return value of the brightness_get callback and moves the led_update_brightness() function to the LED subsystem public API. Signed-off-by: Jacek Anaszewski Acked-by: Kyungmin Park Cc: Richard Purdie Signed-off-by: Bryan Wu --- drivers/leds/led-class.c | 6 ------ drivers/leds/led-core.c | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 71666a4..7440c58 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -24,12 +24,6 @@ static struct class *leds_class; -static void led_update_brightness(struct led_classdev *led_cdev) -{ - if (led_cdev->brightness_get) - led_cdev->brightness = led_cdev->brightness_get(led_cdev); -} - static ssize_t brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 50b579a..aaa8eba 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -127,3 +127,19 @@ void led_set_brightness(struct led_classdev *led_cdev, __led_set_brightness(led_cdev, brightness); } EXPORT_SYMBOL(led_set_brightness); + +int led_update_brightness(struct led_classdev *led_cdev) +{ + int ret = 0; + + if (led_cdev->brightness_get) { + ret = led_cdev->brightness_get(led_cdev); + if (ret >= 0) { + led_cdev->brightness = ret; + return 0; + } + } + + return ret; +} +EXPORT_SYMBOL(led_update_brightness); -- cgit v1.1 From e1ea97fef0cd579fd7ef3851548e068eaf2ad9f0 Mon Sep 17 00:00:00 2001 From: Himangi Saraogi Date: Thu, 17 Jul 2014 02:29:53 +0530 Subject: target/configfs: Remove unnecessary null test This patch removes the null test on lun_cg. lun_cg is initialized at the beginning of the function to &lun->lun_group. Since lun_cg is dereferenced prior to the null test, it must be a valid pointer. The following Coccinelle script is used for detecting the change: @r@ expression e,f; identifier g,y; statement S1,S2; @@ *e = &f->g <+... f->y ...+> *if (e != NULL || ...) S1 else S2 Signed-off-by: Himangi Saraogi Acked-by: Julia Lawall Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_fabric_configfs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 7de9f04..7228a18 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -917,8 +917,7 @@ static struct config_group *target_fabric_make_lun( return &lun->lun_group; out: - if (lun_cg) - kfree(lun_cg->default_groups); + kfree(lun_cg->default_groups); return ERR_PTR(errno); } -- cgit v1.1 From c04047eceed45ae210d020868672456c33cae300 Mon Sep 17 00:00:00 2001 From: Andreea-Cristina Bernat Date: Mon, 18 Aug 2014 15:05:37 +0300 Subject: tcm_fc: Replace rcu_assign_pointer() with RCU_INIT_POINTER() The use of "rcu_assign_pointer()" is NULLing out the pointer. According to RCU_INIT_POINTER()'s block comment: "1. This use of RCU_INIT_POINTER() is NULLing out the pointer" it is better to use it instead of rcu_assign_pointer() because it has a smaller overhead. The following Coccinelle semantic patch was used: @@ @@ - rcu_assign_pointer + RCU_INIT_POINTER (..., NULL) Signed-off-by: Andreea-Cristina Bernat Signed-off-by: Nicholas Bellinger --- drivers/target/tcm_fc/tfc_sess.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c index 21ce508..ccee7e3 100644 --- a/drivers/target/tcm_fc/tfc_sess.c +++ b/drivers/target/tcm_fc/tfc_sess.c @@ -98,7 +98,7 @@ static void ft_tport_delete(struct ft_tport *tport) ft_sess_delete_all(tport); lport = tport->lport; BUG_ON(tport != lport->prov[FC_TYPE_FCP]); - rcu_assign_pointer(lport->prov[FC_TYPE_FCP], NULL); + RCU_INIT_POINTER(lport->prov[FC_TYPE_FCP], NULL); tpg = tport->tpg; if (tpg) { -- cgit v1.1 From c6c2a3de36b1e45841888e27bc2f85ef4e471ad3 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 22 Aug 2014 14:54:31 +0200 Subject: target: target_core_ua_h: Add #define of include guard Clearly the file was meant to contain an include guard, but it was missing the #define part. Signed-off-by: Rasmus Villemoes Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_ua.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/target/target_core_ua.h b/drivers/target/target_core_ua.h index be912b3..a6b56b3 100644 --- a/drivers/target/target_core_ua.h +++ b/drivers/target/target_core_ua.h @@ -1,4 +1,5 @@ #ifndef TARGET_CORE_UA_H +#define TARGET_CORE_UA_H /* * From spc4r17, Table D.1: ASC and ASCQ Assignement -- cgit v1.1 From fbecb6596a80554423d00aba92f2752a2ee0a62d Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 2 Sep 2014 17:49:47 -0400 Subject: iscsi-target: remove unused debug code Last user of buf was removed with c6037cc546ca. While at it, free_cpumask_var() handles a NULL argument just fine, so remove the conditionals. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target.c | 6 +----- drivers/target/iscsi/iscsi_target_login.c | 3 +-- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 1f4c794f..30f4a7d 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -3709,7 +3709,6 @@ static inline void iscsit_thread_check_cpumask( struct task_struct *p, int mode) { - char buf[128]; /* * mode == 1 signals iscsi_target_tx_thread() usage. * mode == 0 signals iscsi_target_rx_thread() usage. @@ -3728,8 +3727,6 @@ static inline void iscsit_thread_check_cpumask( * both TX and RX kthreads are scheduled to run on the * same CPU. */ - memset(buf, 0, 128); - cpumask_scnprintf(buf, 128, conn->conn_cpumask); set_cpus_allowed_ptr(p, conn->conn_cpumask); } @@ -4326,8 +4323,7 @@ int iscsit_close_connection( if (conn->conn_tx_hash.tfm) crypto_free_hash(conn->conn_tx_hash.tfm); - if (conn->conn_cpumask) - free_cpumask_var(conn->conn_cpumask); + free_cpumask_var(conn->conn_cpumask); kfree(conn->conn_ops); conn->conn_ops = NULL; diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 5e71ac6..b1ae5cb 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -1190,8 +1190,7 @@ old_sess_out: if (!IS_ERR(conn->conn_tx_hash.tfm)) crypto_free_hash(conn->conn_tx_hash.tfm); - if (conn->conn_cpumask) - free_cpumask_var(conn->conn_cpumask); + free_cpumask_var(conn->conn_cpumask); kfree(conn->conn_ops); -- cgit v1.1 From cb35484231e0b7edf23e192867e5fba955e584cb Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 2 Sep 2014 17:49:50 -0400 Subject: iscsi-target: remove always-true conditions Found by coverity. InitiatorName and InitiatorAlias are static arrays and therefore always non-NULL. At some point in the past they may have been dynamically allocated, but for current code the condition is useless. If the intent was to check InitiatorName[0] instead, I cannot find a use for that either. Let's get rid of it. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_configfs.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index ae03f3e..9059c1e 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -669,12 +669,10 @@ static ssize_t lio_target_nacl_show_info( } else { sess = se_sess->fabric_sess_ptr; - if (sess->sess_ops->InitiatorName) - rb += sprintf(page+rb, "InitiatorName: %s\n", - sess->sess_ops->InitiatorName); - if (sess->sess_ops->InitiatorAlias) - rb += sprintf(page+rb, "InitiatorAlias: %s\n", - sess->sess_ops->InitiatorAlias); + rb += sprintf(page+rb, "InitiatorName: %s\n", + sess->sess_ops->InitiatorName); + rb += sprintf(page+rb, "InitiatorAlias: %s\n", + sess->sess_ops->InitiatorAlias); rb += sprintf(page+rb, "LIO Session ID: %u " "ISID: 0x%02x %02x %02x %02x %02x %02x " -- cgit v1.1 From 5c22e2294156377b7e2d2d99aaffea9ae6994452 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 2 Sep 2014 17:49:51 -0400 Subject: iscsi-target: simplify return statement The return statement cannot be reached without either recovery or dump being set to 1. Therefore the condition always evaluates to true and recovery and dump are useless variables. Found by Coverity. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_erl0.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index 0d1e6ee..a0ae5fc 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c @@ -345,7 +345,6 @@ static int iscsit_dataout_check_datasn( struct iscsi_cmd *cmd, unsigned char *buf) { - int dump = 0, recovery = 0; u32 data_sn = 0; struct iscsi_conn *conn = cmd->conn; struct iscsi_data *hdr = (struct iscsi_data *) buf; @@ -370,13 +369,11 @@ static int iscsit_dataout_check_datasn( pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x" " higher than expected 0x%08x.\n", cmd->init_task_tag, be32_to_cpu(hdr->datasn), data_sn); - recovery = 1; goto recover; } else if (be32_to_cpu(hdr->datasn) < data_sn) { pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x" " lower than expected 0x%08x, discarding payload.\n", cmd->init_task_tag, be32_to_cpu(hdr->datasn), data_sn); - dump = 1; goto dump; } @@ -392,8 +389,7 @@ dump: if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) return DATAOUT_CANNOT_RECOVER; - return (recovery || dump) ? DATAOUT_WITHIN_COMMAND_RECOVERY : - DATAOUT_NORMAL; + return DATAOUT_WITHIN_COMMAND_RECOVERY; } static int iscsit_dataout_pre_datapduinorder_yes( -- cgit v1.1 From 1d30686da4a40029cb48eab28442896b58aeceef Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 17 Sep 2014 13:17:55 -0700 Subject: iscsi-target: Drop duplicate __iscsi_target_login_thread check This patch drops the now duplicate + unnecessary check for -ENODEV from iscsi_transport->iscsit_accept_np() for jumping to out:, or immediately returning 1 in __iscsi_target_login_thread() code. Since commit 81a9c5e72b the jump to out: and returning 1 have the same effect, and end up hitting the ISCSI_NP_THREAD_SHUTDOWN check regardless at the top of __iscsi_target_login_thread() during next loop iteration. So that said, it's safe to go ahead and remove this duplicate check. Reported-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_login.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index b1ae5cb..02d5ccd 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -1267,8 +1267,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) iscsit_put_transport(conn->conn_transport); kfree(conn); conn = NULL; - if (ret == -ENODEV) - goto out; /* Get another socket */ return 1; } -- cgit v1.1 From 94e16e9c59312247de199b5f9bf141d1bd946dd0 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 2 Sep 2014 17:49:53 -0400 Subject: iscsi-target: remove unnecessary check in iscsit_setup_np error path Found by coverity. At this point sock is non-NULL, so the check to unnecessary. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_login.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 02d5ccd..480f2e0 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -978,8 +978,7 @@ int iscsit_setup_np( return 0; fail: np->np_socket = NULL; - if (sock) - sock_release(sock); + sock_release(sock); return ret; } -- cgit v1.1 From fdc84d11a278d468052afc8e17523545fafe6c5f Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 2 Sep 2014 17:49:55 -0400 Subject: iscsi-target: use strlcpy in iscsit_collect_login_stats last_intr_fail_name is a fixed-size array and could theoretically overflow. In reality intrname->value doesn't seem to depend on untrusted input or be anywhere near 224 characters, so the overflow is pretty theoretical. But strlcpy is cheap enough. Found by coverity. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_util.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index fd90b28..5d611d7 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -1479,8 +1479,9 @@ void iscsit_collect_login_stats( if (conn->param_list) intrname = iscsi_find_param_from_key(INITIATORNAME, conn->param_list); - strcpy(ls->last_intr_fail_name, - (intrname ? intrname->value : "Unknown")); + strlcpy(ls->last_intr_fail_name, + (intrname ? intrname->value : "Unknown"), + sizeof(ls->last_intr_fail_name)); ls->last_intr_fail_ip_family = conn->login_family; -- cgit v1.1 From 8d2135592d2ab5c8d7764a4f534afac64e563691 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 2 Sep 2014 17:49:56 -0400 Subject: target: Fix possible memory leak in aptpl_metadata parsing Each case of match_strdup could leak memory if the same argument was present before. I am not too concerned, as it would require a non-sensical combination like "target_lun=foo target_lun=bar", done with root privileges and even then leak just a few bytes per instance. But arg_p is different, as it will always leak memory. Let's plug that one. And while at it, replace some &args[0] with args. Found by coverity. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_configfs.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index bf55c5a..291dc71 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -1263,7 +1263,7 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( { unsigned char *i_fabric = NULL, *i_port = NULL, *isid = NULL; unsigned char *t_fabric = NULL, *t_port = NULL; - char *orig, *ptr, *arg_p, *opts; + char *orig, *ptr, *opts; substring_t args[MAX_OPT_ARGS]; unsigned long long tmp_ll; u64 sa_res_key = 0; @@ -1295,14 +1295,14 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( token = match_token(ptr, tokens, args); switch (token) { case Opt_initiator_fabric: - i_fabric = match_strdup(&args[0]); + i_fabric = match_strdup(args); if (!i_fabric) { ret = -ENOMEM; goto out; } break; case Opt_initiator_node: - i_port = match_strdup(&args[0]); + i_port = match_strdup(args); if (!i_port) { ret = -ENOMEM; goto out; @@ -1316,7 +1316,7 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( } break; case Opt_initiator_sid: - isid = match_strdup(&args[0]); + isid = match_strdup(args); if (!isid) { ret = -ENOMEM; goto out; @@ -1330,15 +1330,9 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( } break; case Opt_sa_res_key: - arg_p = match_strdup(&args[0]); - if (!arg_p) { - ret = -ENOMEM; - goto out; - } - ret = kstrtoull(arg_p, 0, &tmp_ll); + ret = kstrtoull(args->from, 0, &tmp_ll); if (ret < 0) { - pr_err("kstrtoull() failed for" - " sa_res_key=\n"); + pr_err("kstrtoull() failed for sa_res_key=\n"); goto out; } sa_res_key = (u64)tmp_ll; @@ -1370,14 +1364,14 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( * PR APTPL Metadata for Target Port */ case Opt_target_fabric: - t_fabric = match_strdup(&args[0]); + t_fabric = match_strdup(args); if (!t_fabric) { ret = -ENOMEM; goto out; } break; case Opt_target_node: - t_port = match_strdup(&args[0]); + t_port = match_strdup(args); if (!t_port) { ret = -ENOMEM; goto out; -- cgit v1.1 From da0abaee4793bac4047b3bdfd221fc54850bbf5f Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 2 Sep 2014 17:49:57 -0400 Subject: target: Fix memory leak on error in target_fabric_make_mappedlun This patch fixes a memory leak on error in target_fabric_make_mappedlun(), where se_lun_acl memory does not get released on exit. Found by coverity. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_fabric_configfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 7228a18..0638a67 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -320,7 +320,7 @@ static struct config_group *target_fabric_make_mappedlun( struct se_node_acl, acl_group); struct se_portal_group *se_tpg = se_nacl->se_tpg; struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf; - struct se_lun_acl *lacl; + struct se_lun_acl *lacl = NULL; struct config_item *acl_ci; struct config_group *lacl_cg = NULL, *ml_stat_grp = NULL; char *buf; @@ -406,6 +406,7 @@ static struct config_group *target_fabric_make_mappedlun( out: if (lacl_cg) kfree(lacl_cg->default_groups); + kfree(lacl); kfree(buf); return ERR_PTR(ret); } -- cgit v1.1 From 1481473b5656d8841f63c455594f340306c22cb0 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Wed, 17 Sep 2014 15:11:28 -0700 Subject: target: simplify target_fabric_make_lun error path Coverity complained that lun_cg has been dereferenced in all paths leading to NULL check. It didn't mention that only a single path could lead there and the code can be simplified even further. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_fabric_configfs.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 0638a67..dc6c781 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -911,15 +911,12 @@ static struct config_group *target_fabric_make_lun( GFP_KERNEL); if (!port_stat_grp->default_groups) { pr_err("Unable to allocate port_stat_grp->default_groups\n"); - errno = -ENOMEM; - goto out; + kfree(lun_cg->default_groups); + return ERR_PTR(-ENOMEM); } target_stat_setup_port_default_groups(lun); return &lun->lun_group; -out: - kfree(lun_cg->default_groups); - return ERR_PTR(errno); } static void target_fabric_drop_lun( -- cgit v1.1 From 68edbce4fb4b173d3b9880967cfcce0fc3abc8d5 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 2 Sep 2014 17:49:59 -0400 Subject: target: fix pr_out length in iscsi_parse_pr_out_transport_id Old code in iscsi_parse_pr_out_transport_id() was obviously buggy and effectively ignored the high byte. Found by coverity. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_fabric_lib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index 0d1cf8b..35bfe77 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -394,9 +394,9 @@ char *iscsi_parse_pr_out_transport_id( * If the caller wants the TransportID Length, we set that value for the * entire iSCSI Tarnsport ID now. */ - if (out_tid_len != NULL) { - add_len = ((buf[2] >> 8) & 0xff); - add_len |= (buf[3] & 0xff); + if (out_tid_len) { + /* The shift works thanks to integer promotion rules */ + add_len = (buf[2] << 8) | buf[3]; tid_len = strlen(&buf[4]); tid_len += 4; /* Add four bytes for iSCSI Transport ID header */ -- cgit v1.1 From ce31c1b0dc4038a1dec64585d892adb73d9c45f4 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 2 Sep 2014 17:50:00 -0400 Subject: target: correctly handle match_int errors in FILEIO + PSCSI This patch correctly handles match_int() errors in FILEIO + PSCSI backend parameter parsing, which can potentially fail due to a memory allocation failure or invalid argument. Found by coverity. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_file.c | 4 +++- drivers/target/target_core_pscsi.c | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 7d6cdda..ab2c53b 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -762,7 +762,9 @@ static ssize_t fd_set_configfs_dev_params(struct se_device *dev, fd_dev->fbd_flags |= FBDF_HAS_SIZE; break; case Opt_fd_buffered_io: - match_int(args, &arg); + ret = match_int(args, &arg); + if (ret) + goto out; if (arg != 1) { pr_err("bogus fd_buffered_io=%d value\n", arg); ret = -EINVAL; diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 943b1db..a1690a3 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -749,14 +749,18 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_device *dev, ret = -EINVAL; goto out; } - match_int(args, &arg); + ret = match_int(args, &arg); + if (ret) + goto out; pdv->pdv_host_id = arg; pr_debug("PSCSI[%d]: Referencing SCSI Host ID:" " %d\n", phv->phv_host_id, pdv->pdv_host_id); pdv->pdv_flags |= PDF_HAS_VIRT_HOST_ID; break; case Opt_scsi_channel_id: - match_int(args, &arg); + ret = match_int(args, &arg); + if (ret) + goto out; pdv->pdv_channel_id = arg; pr_debug("PSCSI[%d]: Referencing SCSI Channel" " ID: %d\n", phv->phv_host_id, @@ -764,7 +768,9 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_device *dev, pdv->pdv_flags |= PDF_HAS_CHANNEL_ID; break; case Opt_scsi_target_id: - match_int(args, &arg); + ret = match_int(args, &arg); + if (ret) + goto out; pdv->pdv_target_id = arg; pr_debug("PSCSI[%d]: Referencing SCSI Target" " ID: %d\n", phv->phv_host_id, @@ -772,7 +778,9 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_device *dev, pdv->pdv_flags |= PDF_HAS_TARGET_ID; break; case Opt_scsi_lun_id: - match_int(args, &arg); + ret = match_int(args, &arg); + if (ret) + goto out; pdv->pdv_lun_id = arg; pr_debug("PSCSI[%d]: Referencing SCSI LUN ID:" " %d\n", phv->phv_host_id, pdv->pdv_lun_id); -- cgit v1.1 From c435285df112da1125e61d826b03014a4e769386 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 2 Sep 2014 17:50:01 -0400 Subject: target: fix unused shift in core_scsi3_pri_report_capabilities Clearly a right-shift was meant. Effectively doesn't make a difference, as add_len is hard-coded to 8 and the high byte will be zero no matter which way you shift. But I hate leaving bad examples for others to copy. Found by coverity. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_pr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index df35786..281d52e 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -3803,7 +3803,7 @@ core_scsi3_pri_report_capabilities(struct se_cmd *cmd) if (!buf) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - buf[0] = ((add_len << 8) & 0xff); + buf[0] = ((add_len >> 8) & 0xff); buf[1] = (add_len & 0xff); buf[2] |= 0x10; /* CRH: Compatible Reservation Hanlding bit. */ buf[2] |= 0x08; /* SIP_C: Specify Initiator Ports Capable bit */ -- cgit v1.1 From e2e08970100db03bb84fd4a72f9c35bfc18d595a Mon Sep 17 00:00:00 2001 From: Nikolaus Voss Date: Tue, 23 Sep 2014 15:30:21 +0200 Subject: pwm: atmel: Fix calculation of prescale value The prescale value used for calculating the period was incremented afterwards, thus the resulting prescale value is by one too high. This resulted in a PWM frequency only half as high as requested. This patch moves the 64 bit division out of the prescale loop to correct the above issue and make the calculation more efficient. Signed-off-by: Nikolaus Voss Tested-by: Bo Shen Acked-by: Bo Shen Signed-off-by: Thierry Reding --- drivers/pwm/pwm-atmel.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 6e700a5..d3c22de 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -102,7 +102,7 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); - unsigned long clk_rate, prd, dty; + unsigned long prd, dty; unsigned long long div; unsigned int pres = 0; u32 val; @@ -113,20 +113,18 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, return -EBUSY; } - clk_rate = clk_get_rate(atmel_pwm->clk); - div = clk_rate; + /* Calculate the period cycles and prescale value */ + div = (unsigned long long)clk_get_rate(atmel_pwm->clk) * period_ns; + do_div(div, NSEC_PER_SEC); - /* Calculate the period cycles */ while (div > PWM_MAX_PRD) { - div = clk_rate / (1 << pres); - div = div * period_ns; - /* 1/Hz = 100000000 ns */ - do_div(div, 1000000000); - - if (pres++ > PRD_MAX_PRES) { - dev_err(chip->dev, "pres exceeds the maximum value\n"); - return -EINVAL; - } + div >>= 1; + pres++; + } + + if (pres > PRD_MAX_PRES) { + dev_err(chip->dev, "pres exceeds the maximum value\n"); + return -EINVAL; } /* Calculate the duty cycles */ -- cgit v1.1 From 824269c5868d2a7a26417e5ef3841a27d42c6139 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 25 Sep 2014 15:27:00 +0100 Subject: staging:iio:ad5933: Fix NULL pointer deref when enabling buffer In older versions of the IIO framework it was possible to pass a completely different set of channels to iio_buffer_register() as the one that is assigned to the IIO device. Commit 959d2952d124 ("staging:iio: make iio_sw_buffer_preenable much more general.") introduced a restriction that requires that the set of channels that is passed to iio_buffer_register() is a subset of the channels assigned to the IIO device as the IIO core will use the list of channels that is assigned to the device to lookup a channel by scan index in iio_compute_scan_bytes(). If it can not find the channel the function will crash. This patch fixes the issue by making sure that the same set of channels is assigned to the IIO device and passed to iio_buffer_register(). Fixes the follow NULL pointer derefernce kernel crash: Unable to handle kernel NULL pointer dereference at virtual address 00000016 pgd = d53d0000 [00000016] *pgd=1534e831, *pte=00000000, *ppte=00000000 Internal error: Oops: 17 [#1] PREEMPT SMP ARM Modules linked in: CPU: 1 PID: 1626 Comm: bash Not tainted 3.15.0-19969-g2a180eb-dirty #9545 task: d6c124c0 ti: d539a000 task.ti: d539a000 PC is at iio_compute_scan_bytes+0x34/0xa8 LR is at iio_compute_scan_bytes+0x34/0xa8 pc : [] lr : [] psr: 60070013 sp : d539beb8 ip : 00000001 fp : 00000000 r10: 00000002 r9 : 00000000 r8 : 00000001 r7 : 00000000 r6 : d6dc8800 r5 : d7571000 r4 : 00000002 r3 : d7571000 r2 : 00000044 r1 : 00000001 r0 : 00000000 Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user Control: 18c5387d Table: 153d004a DAC: 00000015 Process bash (pid: 1626, stack limit = 0xd539a240) Stack: (0xd539beb8 to 0xd539c000) bea0: c02fc0e4 d7571000 bec0: d76c1640 d6dc8800 d757117c 00000000 d757112c c0305b04 d76c1690 d76c1640 bee0: d7571188 00000002 00000000 d7571000 d539a000 00000000 000dd1c8 c0305d54 bf00: d7571010 0160b868 00000002 c69d3900 d7573278 d7573308 c69d3900 c01ece90 bf20: 00000002 c0103fac c0103f6c d539bf88 00000002 c69d3b00 c69d3b0c c0103468 bf40: 00000000 00000000 d7694a00 00000002 000af408 d539bf88 c000dd84 c00b2f94 bf60: d7694a00 000af408 00000002 d7694a00 d7694a00 00000002 000af408 c000dd84 bf80: 00000000 c00b32d0 00000000 00000000 00000002 b6f1aa78 00000002 000af408 bfa0: 00000004 c000dc00 b6f1aa78 00000002 00000001 000af408 00000002 00000000 bfc0: b6f1aa78 00000002 000af408 00000004 be806a4c 000a6094 00000000 000dd1c8 bfe0: 00000000 be8069cc b6e8ab77 b6ec125c 40070010 00000001 22940489 154a5007 [] (iio_compute_scan_bytes) from [] (__iio_update_buffers+0x248/0x438) [] (__iio_update_buffers) from [] (iio_buffer_store_enable+0x60/0x7c) [] (iio_buffer_store_enable) from [] (dev_attr_store+0x18/0x24) [] (dev_attr_store) from [] (sysfs_kf_write+0x40/0x4c) [] (sysfs_kf_write) from [] (kernfs_fop_write+0x110/0x154) [] (kernfs_fop_write) from [] (vfs_write+0xd0/0x160) [] (vfs_write) from [] (SyS_write+0x40/0x78) [] (SyS_write) from [] (ret_fast_syscall+0x0/0x30) Code: ea00000e e1a01008 e1a00005 ebfff6fc (e5d0a016) Fixes: 959d2952d124 ("staging:iio: make iio_sw_buffer_preenable much more general.") Signed-off-by: Lars-Peter Clausen Cc: Stable@vger.kernel.org Signed-off-by: Jonathan Cameron --- drivers/staging/iio/impedance-analyzer/ad5933.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index 2b96665..3854f99 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -115,6 +115,7 @@ static const struct iio_chan_spec ad5933_channels[] = { .channel = 0, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), .address = AD5933_REG_TEMP_DATA, + .scan_index = -1, .scan_type = { .sign = 's', .realbits = 14, @@ -125,8 +126,6 @@ static const struct iio_chan_spec ad5933_channels[] = { .indexed = 1, .channel = 0, .extend_name = "real_raw", - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE), .address = AD5933_REG_REAL_DATA, .scan_index = 0, .scan_type = { @@ -139,8 +138,6 @@ static const struct iio_chan_spec ad5933_channels[] = { .indexed = 1, .channel = 0, .extend_name = "imag_raw", - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE), .address = AD5933_REG_IMAG_DATA, .scan_index = 1, .scan_type = { @@ -748,14 +745,14 @@ static int ad5933_probe(struct i2c_client *client, indio_dev->name = id->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ad5933_channels; - indio_dev->num_channels = 1; /* only register temp0_input */ + indio_dev->num_channels = ARRAY_SIZE(ad5933_channels); ret = ad5933_register_ring_funcs_and_init(indio_dev); if (ret) goto error_disable_reg; - /* skip temp0_input, register in0_(real|imag)_raw */ - ret = iio_buffer_register(indio_dev, &ad5933_channels[1], 2); + ret = iio_buffer_register(indio_dev, ad5933_channels, + ARRAY_SIZE(ad5933_channels)); if (ret) goto error_unreg_ring; -- cgit v1.1 From 6822ee34ad57b29a3b44df2c2829910f03c34fa4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 25 Sep 2014 15:27:00 +0100 Subject: staging:iio:ad5933: Drop "raw" from channel names "raw" is the name of a channel property, but should not be part of the channel name itself. Signed-off-by: Lars-Peter Clausen Cc: Signed-off-by: Jonathan Cameron --- drivers/staging/iio/impedance-analyzer/ad5933.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index 3854f99..97d4b3f 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -125,7 +125,7 @@ static const struct iio_chan_spec ad5933_channels[] = { .type = IIO_VOLTAGE, .indexed = 1, .channel = 0, - .extend_name = "real_raw", + .extend_name = "real", .address = AD5933_REG_REAL_DATA, .scan_index = 0, .scan_type = { @@ -137,7 +137,7 @@ static const struct iio_chan_spec ad5933_channels[] = { .type = IIO_VOLTAGE, .indexed = 1, .channel = 0, - .extend_name = "imag_raw", + .extend_name = "imag", .address = AD5933_REG_IMAG_DATA, .scan_index = 1, .scan_type = { -- cgit v1.1 From 4cc72346f05ef549403d997d66fd517109e59d24 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Sun, 28 Sep 2014 01:57:14 -0700 Subject: led: gpio: Sort include headers alphabetically If the inlcude headers aren't sorted alphabetically, then the logical choice is to append new ones, however that creates a lot of potential for conflicts or duplicates because every change will then add new includes in the same location. Signed-off-by: Xiubo Li Signed-off-by: Bryan Wu --- drivers/leds/leds-gpio-register.c | 2 +- drivers/leds/leds-gpio.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-gpio-register.c b/drivers/leds/leds-gpio-register.c index 1c4ed55..fbd8934 100644 --- a/drivers/leds/leds-gpio-register.c +++ b/drivers/leds/leds-gpio-register.c @@ -7,9 +7,9 @@ * Free Software Foundation. */ #include +#include #include #include -#include /** * gpio_led_register_device - register a gpio-led device diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 57ff20f..1b1e617 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -10,17 +10,17 @@ * published by the Free Software Foundation. * */ -#include -#include +#include #include +#include #include +#include #include -#include #include +#include +#include #include #include -#include -#include struct gpio_led_data { struct led_classdev cdev; -- cgit v1.1 From a823e76138466225d0a9f45520c5654132939a01 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Sun, 28 Sep 2014 01:57:15 -0700 Subject: led: gpio: Fix possible ZERO_SIZE_PTR pointer dereferencing error. Since we cannot make sure the 'pdata->num_leds' will always be none zero here, and then if it equals to zero, the kmemdup() will return ZERO_SIZE_PTR, which equals to ((void *)16). So this patch fix this with just doing the zero check before calling kmemdup(). Signed-off-by: Xiubo Li Signed-off-by: Bryan Wu --- drivers/leds/leds-gpio-register.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/leds/leds-gpio-register.c b/drivers/leds/leds-gpio-register.c index fbd8934..75717ba 100644 --- a/drivers/leds/leds-gpio-register.c +++ b/drivers/leds/leds-gpio-register.c @@ -28,6 +28,9 @@ struct platform_device *__init gpio_led_register_device( struct platform_device *ret; struct gpio_led_platform_data _pdata = *pdata; + if (!pdata->num_leds) + return ERR_PTR(-EINVAL); + _pdata.leds = kmemdup(pdata->leds, pdata->num_leds * sizeof(*pdata->leds), GFP_KERNEL); if (!_pdata.leds) -- cgit v1.1 From a4c84e6aafda0ddd8cb004c464cd11e47e211049 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Sun, 28 Sep 2014 01:57:16 -0700 Subject: leds: gpio: cleanup the leds-gpio driver Remove stray blank line and space. Signed-off-by: Xiubo Li Signed-off-by: Bryan Wu --- drivers/leds/leds-gpio.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 1b1e617..b4518c8 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -36,7 +36,7 @@ struct gpio_led_data { static void gpio_led_work(struct work_struct *work) { - struct gpio_led_data *led_dat = + struct gpio_led_data *led_dat = container_of(work, struct gpio_led_data, work); if (led_dat->blinking) { @@ -235,14 +235,12 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) } #endif /* CONFIG_OF_GPIO */ - static int gpio_led_probe(struct platform_device *pdev) { struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev); struct gpio_leds_priv *priv; int i, ret = 0; - if (pdata && pdata->num_leds) { priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(pdata->num_leds), -- cgit v1.1 From cc83881f2c57caaf4b14adaffa65595640a59661 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Mon, 30 Jun 2014 16:39:43 -0700 Subject: target: core_tpg_post_dellun can return void Nothing in it can raise an error. Reviewed-by: Christoph Hellwig Signed-off-by: Andy Grover Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_internal.h | 2 +- drivers/target/target_core_tpg.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index de9cab7..463fddc 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -83,7 +83,7 @@ struct se_lun *core_tpg_alloc_lun(struct se_portal_group *, u32); int core_tpg_add_lun(struct se_portal_group *, struct se_lun *, u32, struct se_device *); struct se_lun *core_tpg_pre_dellun(struct se_portal_group *, u32 unpacked_lun); -int core_tpg_post_dellun(struct se_portal_group *, struct se_lun *); +void core_tpg_post_dellun(struct se_portal_group *, struct se_lun *); /* target_core_transport.c */ extern struct kmem_cache *se_tmr_req_cache; diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index fddfae6..b596ab5 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -867,7 +867,7 @@ struct se_lun *core_tpg_pre_dellun( return lun; } -int core_tpg_post_dellun( +void core_tpg_post_dellun( struct se_portal_group *tpg, struct se_lun *lun) { @@ -881,6 +881,4 @@ int core_tpg_post_dellun( spin_unlock(&tpg->tpg_lun_lock); percpu_ref_exit(&lun->lun_ref); - - return 0; } -- cgit v1.1 From cd9d7cbaec8b622eee4edcd8bf481c4047f74915 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Mon, 30 Jun 2014 16:39:44 -0700 Subject: target: Change core_dev_del_lun to take a se_lun instead of unpacked_lun Remove core_tpg_pre_dellun entirely, since we don't need to get/check a pointer we already have. Nothing else can return an error, so core_dev_del_lun can return void. Rename core_tpg_post_dellun to remove_lun - a clearer name, now that pre_dellun is gone. Reviewed-by: Christoph Hellwig Signed-off-by: Andy Grover Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_device.c | 18 ++++---------- drivers/target/target_core_fabric_configfs.c | 2 +- drivers/target/target_core_internal.h | 5 ++-- drivers/target/target_core_tpg.c | 36 +++------------------------- 4 files changed, 11 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 98da901..e784284 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -1250,24 +1250,16 @@ struct se_lun *core_dev_add_lun( * * */ -int core_dev_del_lun( +void core_dev_del_lun( struct se_portal_group *tpg, - u32 unpacked_lun) + struct se_lun *lun) { - struct se_lun *lun; - - lun = core_tpg_pre_dellun(tpg, unpacked_lun); - if (IS_ERR(lun)) - return PTR_ERR(lun); - - core_tpg_post_dellun(tpg, lun); - - pr_debug("%s_TPG[%u]_LUN[%u] - Deactivated %s Logical Unit from" + pr_debug("%s_TPG[%u]_LUN[%u] - Deactivating %s Logical Unit from" " device object\n", tpg->se_tpg_tfo->get_fabric_name(), - tpg->se_tpg_tfo->tpg_get_tag(tpg), unpacked_lun, + tpg->se_tpg_tfo->tpg_get_tag(tpg), lun->unpacked_lun, tpg->se_tpg_tfo->get_fabric_name()); - return 0; + core_tpg_remove_lun(tpg, lun); } struct se_lun *core_get_lun_from_tpg(struct se_portal_group *tpg, u32 unpacked_lun) diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index dc6c781..0c3f901 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -822,7 +822,7 @@ static int target_fabric_port_unlink( tf->tf_ops.fabric_pre_unlink(se_tpg, lun); } - core_dev_del_lun(se_tpg, lun->unpacked_lun); + core_dev_del_lun(se_tpg, lun); return 0; } diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 463fddc..42ef4ab 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -46,7 +46,7 @@ int se_dev_set_fabric_max_sectors(struct se_device *, u32); int se_dev_set_optimal_sectors(struct se_device *, u32); int se_dev_set_block_size(struct se_device *, u32); struct se_lun *core_dev_add_lun(struct se_portal_group *, struct se_device *, u32); -int core_dev_del_lun(struct se_portal_group *, u32); +void core_dev_del_lun(struct se_portal_group *, struct se_lun *); struct se_lun *core_get_lun_from_tpg(struct se_portal_group *, u32); struct se_lun_acl *core_dev_init_initiator_node_lun_acl(struct se_portal_group *, struct se_node_acl *, u32, int *); @@ -82,8 +82,7 @@ void core_tpg_wait_for_nacl_pr_ref(struct se_node_acl *); struct se_lun *core_tpg_alloc_lun(struct se_portal_group *, u32); int core_tpg_add_lun(struct se_portal_group *, struct se_lun *, u32, struct se_device *); -struct se_lun *core_tpg_pre_dellun(struct se_portal_group *, u32 unpacked_lun); -void core_tpg_post_dellun(struct se_portal_group *, struct se_lun *); +void core_tpg_remove_lun(struct se_portal_group *, struct se_lun *); /* target_core_transport.c */ extern struct kmem_cache *se_tmr_req_cache; diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index b596ab5..2596041 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -335,7 +335,7 @@ void core_tpg_clear_object_luns(struct se_portal_group *tpg) continue; spin_unlock(&tpg->tpg_lun_lock); - core_dev_del_lun(tpg, lun->unpacked_lun); + core_dev_del_lun(tpg, lun); spin_lock(&tpg->tpg_lun_lock); } spin_unlock(&tpg->tpg_lun_lock); @@ -667,7 +667,7 @@ static void core_tpg_release_virtual_lun0(struct se_portal_group *se_tpg) { struct se_lun *lun = &se_tpg->tpg_virt_lun0; - core_tpg_post_dellun(se_tpg, lun); + core_tpg_remove_lun(se_tpg, lun); } int core_tpg_register( @@ -837,37 +837,7 @@ int core_tpg_add_lun( return 0; } -struct se_lun *core_tpg_pre_dellun( - struct se_portal_group *tpg, - u32 unpacked_lun) -{ - struct se_lun *lun; - - if (unpacked_lun > (TRANSPORT_MAX_LUNS_PER_TPG-1)) { - pr_err("%s LUN: %u exceeds TRANSPORT_MAX_LUNS_PER_TPG" - "-1: %u for Target Portal Group: %u\n", - tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun, - TRANSPORT_MAX_LUNS_PER_TPG-1, - tpg->se_tpg_tfo->tpg_get_tag(tpg)); - return ERR_PTR(-EOVERFLOW); - } - - spin_lock(&tpg->tpg_lun_lock); - lun = tpg->tpg_lun_list[unpacked_lun]; - if (lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE) { - pr_err("%s Logical Unit Number: %u is not active on" - " Target Portal Group: %u, ignoring request.\n", - tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun, - tpg->se_tpg_tfo->tpg_get_tag(tpg)); - spin_unlock(&tpg->tpg_lun_lock); - return ERR_PTR(-ENODEV); - } - spin_unlock(&tpg->tpg_lun_lock); - - return lun; -} - -void core_tpg_post_dellun( +void core_tpg_remove_lun( struct se_portal_group *tpg, struct se_lun *lun) { -- cgit v1.1 From 9c7d6154bc4b9dfefd580490cdca5f7c72321464 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Mon, 30 Jun 2014 16:39:46 -0700 Subject: target: Remove core_tpg_release_virtual_lun0 function Simple and just called from one place. Reviewed-by: Christoph Hellwig Signed-off-by: Andy Grover Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_tpg.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 2596041..6ec5873 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -663,13 +663,6 @@ static int core_tpg_setup_virtual_lun0(struct se_portal_group *se_tpg) return 0; } -static void core_tpg_release_virtual_lun0(struct se_portal_group *se_tpg) -{ - struct se_lun *lun = &se_tpg->tpg_virt_lun0; - - core_tpg_remove_lun(se_tpg, lun); -} - int core_tpg_register( struct target_core_fabric_ops *tfo, struct se_wwn *se_wwn, @@ -773,7 +766,7 @@ int core_tpg_deregister(struct se_portal_group *se_tpg) spin_unlock_irq(&se_tpg->acl_node_lock); if (se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL) - core_tpg_release_virtual_lun0(se_tpg); + core_tpg_remove_lun(se_tpg, &se_tpg->tpg_virt_lun0); se_tpg->se_tpg_fabric_ptr = NULL; array_free(se_tpg->tpg_lun_list, TRANSPORT_MAX_LUNS_PER_TPG); -- cgit v1.1 From 8f83269048628d7b139dacbfc6cc97befcbdd2e9 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 16 Sep 2014 16:23:10 -0400 Subject: target: simplify core_tmr_release_req() And while at it, do minimal coding style fixes in the area. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_tmr.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index f7cd95e..83de7ae 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -64,21 +64,17 @@ int core_tmr_alloc_req( } EXPORT_SYMBOL(core_tmr_alloc_req); -void core_tmr_release_req( - struct se_tmr_req *tmr) +void core_tmr_release_req(struct se_tmr_req *tmr) { struct se_device *dev = tmr->tmr_dev; unsigned long flags; - if (!dev) { - kfree(tmr); - return; + if (dev) { + spin_lock_irqsave(&dev->se_tmr_lock, flags); + list_del(&tmr->tmr_list); + spin_unlock_irqrestore(&dev->se_tmr_lock, flags); } - spin_lock_irqsave(&dev->se_tmr_lock, flags); - list_del(&tmr->tmr_list); - spin_unlock_irqrestore(&dev->se_tmr_lock, flags); - kfree(tmr); } @@ -90,9 +86,8 @@ static void core_tmr_handle_tas_abort( bool remove = true; /* * TASK ABORTED status (TAS) bit support - */ - if ((tmr_nacl && - (tmr_nacl != cmd->se_sess->se_node_acl)) && tas) { + */ + if ((tmr_nacl && (tmr_nacl != cmd->se_sess->se_node_acl)) && tas) { remove = false; transport_send_task_abort(cmd); } -- cgit v1.1 From 74ed7e62289dc6d388996d7c8f89c2e7e95b9657 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 16 Sep 2014 16:23:11 -0400 Subject: target: remove some smp_mb__after_atomic()s atomic_inc_return() already does an implicit memory barrier and the second case was moved from an atomic to a plain flag operation. If a barrier were needed in the second case, it would have to be smp_mb(), not a variant optimized away for x86 and other architectures. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 7fa62fc..0b43761 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1166,7 +1166,6 @@ transport_check_alloc_task_attr(struct se_cmd *cmd) * Dormant to Active status. */ cmd->se_ordered_id = atomic_inc_return(&dev->dev_ordered_id); - smp_mb__after_atomic(); pr_debug("Allocated se_ordered_id: %u for Task Attr: 0x%02x on %s\n", cmd->se_ordered_id, cmd->sam_task_attr, dev->transport->name); @@ -2896,7 +2895,6 @@ void transport_send_task_abort(struct se_cmd *cmd) if (cmd->se_tfo->write_pending_status(cmd) != 0) { cmd->transport_state |= CMD_T_ABORTED; cmd->se_cmd_flags |= SCF_SEND_DELAYED_TAS; - smp_mb__after_atomic(); return; } } -- cgit v1.1 From 33940d09937276cd3c81f2874faf43e37c2db0e2 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 16 Sep 2014 16:23:12 -0400 Subject: target: encapsulate smp_mb__after_atomic() The target code has a rather generous helping of smp_mb__after_atomic() throughout the code base. Most atomic operations were followed by one and none were preceded by smp_mb__before_atomic(), nor accompanied by a comment explaining the need for a barrier. Instead of trying to prove for every case whether or not it is needed, this patch introduces atomic_inc_mb() and atomic_dec_mb(), which explicitly include the memory barriers before and after the atomic operation. For now they are defined in a target header, although they could be of general use. Most of the existing atomic/mb combinations were replaced by the new helpers. In a few cases the atomic was sandwiched in spin_lock/spin_unlock and I simply removed the barrier. I suspect that in most cases the correct conversion would have been to drop the barrier. I also suspect that a few cases exist where a) the barrier was necessary and b) a second barrier before the atomic would have been necessary and got added by this patch. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/loopback/tcm_loop.c | 6 +-- drivers/target/target_core_alua.c | 33 ++++--------- drivers/target/target_core_device.c | 9 ++-- drivers/target/target_core_pr.c | 90 ++++++++++++---------------------- drivers/target/target_core_transport.c | 18 +++---- drivers/target/target_core_ua.c | 15 ++---- 6 files changed, 56 insertions(+), 115 deletions(-) (limited to 'drivers') diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 340de9d..a7f6dc6 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -960,8 +960,7 @@ static int tcm_loop_port_link( struct tcm_loop_tpg, tl_se_tpg); struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; - atomic_inc(&tl_tpg->tl_tpg_port_count); - smp_mb__after_atomic(); + atomic_inc_mb(&tl_tpg->tl_tpg_port_count); /* * Add Linux/SCSI struct scsi_device by HCTL */ @@ -995,8 +994,7 @@ static void tcm_loop_port_unlink( scsi_remove_device(sd); scsi_device_put(sd); - atomic_dec(&tl_tpg->tl_tpg_port_count); - smp_mb__after_atomic(); + atomic_dec_mb(&tl_tpg->tl_tpg_port_count); pr_debug("TCM_Loop_ConfigFS: Port Unlink Successful\n"); } diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index fbc5ebb..fb87780 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -392,8 +392,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd) if (tg_pt_id != tg_pt_gp->tg_pt_gp_id) continue; - atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); - smp_mb__after_atomic(); + atomic_inc_mb(&tg_pt_gp->tg_pt_gp_ref_cnt); spin_unlock(&dev->t10_alua.tg_pt_gps_lock); @@ -403,8 +402,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd) found = true; spin_lock(&dev->t10_alua.tg_pt_gps_lock); - atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); - smp_mb__after_atomic(); + atomic_dec_mb(&tg_pt_gp->tg_pt_gp_ref_cnt); break; } spin_unlock(&dev->t10_alua.tg_pt_gps_lock); @@ -998,8 +996,7 @@ static void core_alua_do_transition_tg_pt_work(struct work_struct *work) * every I_T nexus other than the I_T nexus on which the SET * TARGET PORT GROUPS command */ - atomic_inc(&mem->tg_pt_gp_mem_ref_cnt); - smp_mb__after_atomic(); + atomic_inc_mb(&mem->tg_pt_gp_mem_ref_cnt); spin_unlock(&tg_pt_gp->tg_pt_gp_lock); spin_lock_bh(&port->sep_alua_lock); @@ -1028,8 +1025,7 @@ static void core_alua_do_transition_tg_pt_work(struct work_struct *work) spin_unlock_bh(&port->sep_alua_lock); spin_lock(&tg_pt_gp->tg_pt_gp_lock); - atomic_dec(&mem->tg_pt_gp_mem_ref_cnt); - smp_mb__after_atomic(); + atomic_dec_mb(&mem->tg_pt_gp_mem_ref_cnt); } spin_unlock(&tg_pt_gp->tg_pt_gp_lock); /* @@ -1063,7 +1059,6 @@ static void core_alua_do_transition_tg_pt_work(struct work_struct *work) core_alua_dump_state(tg_pt_gp->tg_pt_gp_alua_pending_state)); spin_lock(&dev->t10_alua.tg_pt_gps_lock); atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); - smp_mb__after_atomic(); spin_unlock(&dev->t10_alua.tg_pt_gps_lock); if (tg_pt_gp->tg_pt_gp_transition_complete) @@ -1125,7 +1120,6 @@ static int core_alua_do_transition_tg_pt( */ spin_lock(&dev->t10_alua.tg_pt_gps_lock); atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); - smp_mb__after_atomic(); spin_unlock(&dev->t10_alua.tg_pt_gps_lock); if (!explicit && tg_pt_gp->tg_pt_gp_implicit_trans_secs) { @@ -1168,7 +1162,6 @@ int core_alua_do_port_transition( spin_lock(&local_lu_gp_mem->lu_gp_mem_lock); lu_gp = local_lu_gp_mem->lu_gp; atomic_inc(&lu_gp->lu_gp_ref_cnt); - smp_mb__after_atomic(); spin_unlock(&local_lu_gp_mem->lu_gp_mem_lock); /* * For storage objects that are members of the 'default_lu_gp', @@ -1184,8 +1177,7 @@ int core_alua_do_port_transition( l_tg_pt_gp->tg_pt_gp_alua_nacl = l_nacl; rc = core_alua_do_transition_tg_pt(l_tg_pt_gp, new_state, explicit); - atomic_dec(&lu_gp->lu_gp_ref_cnt); - smp_mb__after_atomic(); + atomic_dec_mb(&lu_gp->lu_gp_ref_cnt); return rc; } /* @@ -1198,8 +1190,7 @@ int core_alua_do_port_transition( lu_gp_mem_list) { dev = lu_gp_mem->lu_gp_mem_dev; - atomic_inc(&lu_gp_mem->lu_gp_mem_ref_cnt); - smp_mb__after_atomic(); + atomic_inc_mb(&lu_gp_mem->lu_gp_mem_ref_cnt); spin_unlock(&lu_gp->lu_gp_lock); spin_lock(&dev->t10_alua.tg_pt_gps_lock); @@ -1227,8 +1218,7 @@ int core_alua_do_port_transition( tg_pt_gp->tg_pt_gp_alua_port = NULL; tg_pt_gp->tg_pt_gp_alua_nacl = NULL; } - atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); - smp_mb__after_atomic(); + atomic_inc_mb(&tg_pt_gp->tg_pt_gp_ref_cnt); spin_unlock(&dev->t10_alua.tg_pt_gps_lock); /* * core_alua_do_transition_tg_pt() will always return @@ -1238,16 +1228,14 @@ int core_alua_do_port_transition( new_state, explicit); spin_lock(&dev->t10_alua.tg_pt_gps_lock); - atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); - smp_mb__after_atomic(); + atomic_dec_mb(&tg_pt_gp->tg_pt_gp_ref_cnt); if (rc) break; } spin_unlock(&dev->t10_alua.tg_pt_gps_lock); spin_lock(&lu_gp->lu_gp_lock); - atomic_dec(&lu_gp_mem->lu_gp_mem_ref_cnt); - smp_mb__after_atomic(); + atomic_dec_mb(&lu_gp_mem->lu_gp_mem_ref_cnt); } spin_unlock(&lu_gp->lu_gp_lock); @@ -1260,8 +1248,7 @@ int core_alua_do_port_transition( core_alua_dump_state(new_state)); } - atomic_dec(&lu_gp->lu_gp_ref_cnt); - smp_mb__after_atomic(); + atomic_dec_mb(&lu_gp->lu_gp_ref_cnt); return rc; } diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index e784284..f5057a2 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -224,8 +224,7 @@ struct se_dev_entry *core_get_se_deve_from_rtpi( if (port->sep_rtpi != rtpi) continue; - atomic_inc(&deve->pr_ref_count); - smp_mb__after_atomic(); + atomic_inc_mb(&deve->pr_ref_count); spin_unlock_irq(&nacl->device_list_lock); return deve; @@ -1388,8 +1387,7 @@ int core_dev_add_initiator_node_lun_acl( spin_lock(&lun->lun_acl_lock); list_add_tail(&lacl->lacl_list, &lun->lun_acl_list); - atomic_inc(&lun->lun_acl_count); - smp_mb__after_atomic(); + atomic_inc_mb(&lun->lun_acl_count); spin_unlock(&lun->lun_acl_lock); pr_debug("%s_TPG[%hu]_LUN[%u->%u] - Added %s ACL for " @@ -1422,8 +1420,7 @@ int core_dev_del_initiator_node_lun_acl( spin_lock(&lun->lun_acl_lock); list_del(&lacl->lacl_list); - atomic_dec(&lun->lun_acl_count); - smp_mb__after_atomic(); + atomic_dec_mb(&lun->lun_acl_count); spin_unlock(&lun->lun_acl_lock); core_disable_device_list_for_node(lun, NULL, lacl->mapped_lun, diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 281d52e..48a8010 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -674,8 +674,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( */ spin_lock(&dev->se_port_lock); list_for_each_entry_safe(port, port_tmp, &dev->dev_sep_list, sep_list) { - atomic_inc(&port->sep_tg_pt_ref_cnt); - smp_mb__after_atomic(); + atomic_inc_mb(&port->sep_tg_pt_ref_cnt); spin_unlock(&dev->se_port_lock); spin_lock_bh(&port->sep_alua_lock); @@ -709,8 +708,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( if (strcmp(nacl->initiatorname, nacl_tmp->initiatorname)) continue; - atomic_inc(&deve_tmp->pr_ref_count); - smp_mb__after_atomic(); + atomic_inc_mb(&deve_tmp->pr_ref_count); spin_unlock_bh(&port->sep_alua_lock); /* * Grab a configfs group dependency that is released @@ -722,10 +720,8 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( if (ret < 0) { pr_err("core_scsi3_lunacl_depend" "_item() failed\n"); - atomic_dec(&port->sep_tg_pt_ref_cnt); - smp_mb__after_atomic(); - atomic_dec(&deve_tmp->pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&port->sep_tg_pt_ref_cnt); + atomic_dec_mb(&deve_tmp->pr_ref_count); goto out; } /* @@ -739,10 +735,8 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( nacl_tmp, deve_tmp, NULL, sa_res_key, all_tg_pt, aptpl); if (!pr_reg_atp) { - atomic_dec(&port->sep_tg_pt_ref_cnt); - smp_mb__after_atomic(); - atomic_dec(&deve_tmp->pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&port->sep_tg_pt_ref_cnt); + atomic_dec_mb(&deve_tmp->pr_ref_count); core_scsi3_lunacl_undepend_item(deve_tmp); goto out; } @@ -754,8 +748,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( spin_unlock_bh(&port->sep_alua_lock); spin_lock(&dev->se_port_lock); - atomic_dec(&port->sep_tg_pt_ref_cnt); - smp_mb__after_atomic(); + atomic_dec_mb(&port->sep_tg_pt_ref_cnt); } spin_unlock(&dev->se_port_lock); @@ -1109,8 +1102,7 @@ static struct t10_pr_registration *__core_scsi3_locate_pr_reg( if (dev->dev_attrib.enforce_pr_isids) continue; } - atomic_inc(&pr_reg->pr_res_holders); - smp_mb__after_atomic(); + atomic_inc_mb(&pr_reg->pr_res_holders); spin_unlock(&pr_tmpl->registration_lock); return pr_reg; } @@ -1124,8 +1116,7 @@ static struct t10_pr_registration *__core_scsi3_locate_pr_reg( if (strcmp(isid, pr_reg->pr_reg_isid)) continue; - atomic_inc(&pr_reg->pr_res_holders); - smp_mb__after_atomic(); + atomic_inc_mb(&pr_reg->pr_res_holders); spin_unlock(&pr_tmpl->registration_lock); return pr_reg; } @@ -1154,8 +1145,7 @@ static struct t10_pr_registration *core_scsi3_locate_pr_reg( static void core_scsi3_put_pr_reg(struct t10_pr_registration *pr_reg) { - atomic_dec(&pr_reg->pr_res_holders); - smp_mb__after_atomic(); + atomic_dec_mb(&pr_reg->pr_res_holders); } static int core_scsi3_check_implicit_release( @@ -1348,8 +1338,7 @@ static void core_scsi3_tpg_undepend_item(struct se_portal_group *tpg) configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys, &tpg->tpg_group.cg_item); - atomic_dec(&tpg->tpg_pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&tpg->tpg_pr_ref_count); } static int core_scsi3_nodeacl_depend_item(struct se_node_acl *nacl) @@ -1368,16 +1357,14 @@ static void core_scsi3_nodeacl_undepend_item(struct se_node_acl *nacl) struct se_portal_group *tpg = nacl->se_tpg; if (nacl->dynamic_node_acl) { - atomic_dec(&nacl->acl_pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&nacl->acl_pr_ref_count); return; } configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys, &nacl->acl_group.cg_item); - atomic_dec(&nacl->acl_pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&nacl->acl_pr_ref_count); } static int core_scsi3_lunacl_depend_item(struct se_dev_entry *se_deve) @@ -1407,8 +1394,7 @@ static void core_scsi3_lunacl_undepend_item(struct se_dev_entry *se_deve) * For nacl->dynamic_node_acl=1 */ if (!lun_acl) { - atomic_dec(&se_deve->pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&se_deve->pr_ref_count); return; } nacl = lun_acl->se_lun_nacl; @@ -1417,8 +1403,7 @@ static void core_scsi3_lunacl_undepend_item(struct se_dev_entry *se_deve) configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys, &lun_acl->se_lun_group.cg_item); - atomic_dec(&se_deve->pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&se_deve->pr_ref_count); } static sense_reason_t @@ -1551,15 +1536,13 @@ core_scsi3_decode_spec_i_port( if (!i_str) continue; - atomic_inc(&tmp_tpg->tpg_pr_ref_count); - smp_mb__after_atomic(); + atomic_inc_mb(&tmp_tpg->tpg_pr_ref_count); spin_unlock(&dev->se_port_lock); if (core_scsi3_tpg_depend_item(tmp_tpg)) { pr_err(" core_scsi3_tpg_depend_item()" " for tmp_tpg\n"); - atomic_dec(&tmp_tpg->tpg_pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&tmp_tpg->tpg_pr_ref_count); ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; goto out_unmap; } @@ -1571,10 +1554,8 @@ core_scsi3_decode_spec_i_port( spin_lock_irq(&tmp_tpg->acl_node_lock); dest_node_acl = __core_tpg_get_initiator_node_acl( tmp_tpg, i_str); - if (dest_node_acl) { - atomic_inc(&dest_node_acl->acl_pr_ref_count); - smp_mb__after_atomic(); - } + if (dest_node_acl) + atomic_inc_mb(&dest_node_acl->acl_pr_ref_count); spin_unlock_irq(&tmp_tpg->acl_node_lock); if (!dest_node_acl) { @@ -1586,8 +1567,7 @@ core_scsi3_decode_spec_i_port( if (core_scsi3_nodeacl_depend_item(dest_node_acl)) { pr_err("configfs_depend_item() failed" " for dest_node_acl->acl_group\n"); - atomic_dec(&dest_node_acl->acl_pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&dest_node_acl->acl_pr_ref_count); core_scsi3_tpg_undepend_item(tmp_tpg); ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; goto out_unmap; @@ -1646,8 +1626,7 @@ core_scsi3_decode_spec_i_port( if (core_scsi3_lunacl_depend_item(dest_se_deve)) { pr_err("core_scsi3_lunacl_depend_item()" " failed\n"); - atomic_dec(&dest_se_deve->pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&dest_se_deve->pr_ref_count); core_scsi3_nodeacl_undepend_item(dest_node_acl); core_scsi3_tpg_undepend_item(dest_tpg); ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; @@ -3167,15 +3146,13 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, if (!dest_tf_ops) continue; - atomic_inc(&dest_se_tpg->tpg_pr_ref_count); - smp_mb__after_atomic(); + atomic_inc_mb(&dest_se_tpg->tpg_pr_ref_count); spin_unlock(&dev->se_port_lock); if (core_scsi3_tpg_depend_item(dest_se_tpg)) { pr_err("core_scsi3_tpg_depend_item() failed" " for dest_se_tpg\n"); - atomic_dec(&dest_se_tpg->tpg_pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&dest_se_tpg->tpg_pr_ref_count); ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; goto out_put_pr_reg; } @@ -3271,10 +3248,8 @@ after_iport_check: spin_lock_irq(&dest_se_tpg->acl_node_lock); dest_node_acl = __core_tpg_get_initiator_node_acl(dest_se_tpg, initiator_str); - if (dest_node_acl) { - atomic_inc(&dest_node_acl->acl_pr_ref_count); - smp_mb__after_atomic(); - } + if (dest_node_acl) + atomic_inc_mb(&dest_node_acl->acl_pr_ref_count); spin_unlock_irq(&dest_se_tpg->acl_node_lock); if (!dest_node_acl) { @@ -3288,8 +3263,7 @@ after_iport_check: if (core_scsi3_nodeacl_depend_item(dest_node_acl)) { pr_err("core_scsi3_nodeacl_depend_item() for" " dest_node_acl\n"); - atomic_dec(&dest_node_acl->acl_pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&dest_node_acl->acl_pr_ref_count); dest_node_acl = NULL; ret = TCM_INVALID_PARAMETER_LIST; goto out; @@ -3313,8 +3287,7 @@ after_iport_check: if (core_scsi3_lunacl_depend_item(dest_se_deve)) { pr_err("core_scsi3_lunacl_depend_item() failed\n"); - atomic_dec(&dest_se_deve->pr_ref_count); - smp_mb__after_atomic(); + atomic_dec_mb(&dest_se_deve->pr_ref_count); dest_se_deve = NULL; ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; goto out; @@ -3879,8 +3852,7 @@ core_scsi3_pri_read_full_status(struct se_cmd *cmd) se_tpg = pr_reg->pr_reg_nacl->se_tpg; add_desc_len = 0; - atomic_inc(&pr_reg->pr_res_holders); - smp_mb__after_atomic(); + atomic_inc_mb(&pr_reg->pr_res_holders); spin_unlock(&pr_tmpl->registration_lock); /* * Determine expected length of $FABRIC_MOD specific @@ -3893,8 +3865,7 @@ core_scsi3_pri_read_full_status(struct se_cmd *cmd) pr_warn("SPC-3 PRIN READ_FULL_STATUS ran" " out of buffer: %d\n", cmd->data_length); spin_lock(&pr_tmpl->registration_lock); - atomic_dec(&pr_reg->pr_res_holders); - smp_mb__after_atomic(); + atomic_dec_mb(&pr_reg->pr_res_holders); break; } /* @@ -3955,8 +3926,7 @@ core_scsi3_pri_read_full_status(struct se_cmd *cmd) se_nacl, pr_reg, &format_code, &buf[off+4]); spin_lock(&pr_tmpl->registration_lock); - atomic_dec(&pr_reg->pr_res_holders); - smp_mb__after_atomic(); + atomic_dec_mb(&pr_reg->pr_res_holders); /* * Set the ADDITIONAL DESCRIPTOR LENGTH */ diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 0b43761..115632e 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -752,8 +752,7 @@ void target_qf_do_work(struct work_struct *work) list_for_each_entry_safe(cmd, cmd_tmp, &qf_cmd_list, se_qf_node) { list_del(&cmd->se_qf_node); - atomic_dec(&dev->dev_qf_count); - smp_mb__after_atomic(); + atomic_dec_mb(&dev->dev_qf_count); pr_debug("Processing %s cmd: %p QUEUE_FULL in work queue" " context: %s\n", cmd->se_tfo->get_fabric_name(), cmd, @@ -1721,8 +1720,7 @@ static bool target_handle_task_attr(struct se_cmd *cmd) cmd->t_task_cdb[0], cmd->se_ordered_id); return false; case MSG_ORDERED_TAG: - atomic_inc(&dev->dev_ordered_sync); - smp_mb__after_atomic(); + atomic_inc_mb(&dev->dev_ordered_sync); pr_debug("Added ORDERED for CDB: 0x%02x to ordered list, " " se_ordered_id: %u\n", @@ -1739,8 +1737,7 @@ static bool target_handle_task_attr(struct se_cmd *cmd) /* * For SIMPLE and UNTAGGED Task Attribute commands */ - atomic_inc(&dev->simple_cmds); - smp_mb__after_atomic(); + atomic_inc_mb(&dev->simple_cmds); break; } @@ -1844,8 +1841,7 @@ static void transport_complete_task_attr(struct se_cmd *cmd) return; if (cmd->sam_task_attr == MSG_SIMPLE_TAG) { - atomic_dec(&dev->simple_cmds); - smp_mb__after_atomic(); + atomic_dec_mb(&dev->simple_cmds); dev->dev_cur_ordered_id++; pr_debug("Incremented dev->dev_cur_ordered_id: %u for" " SIMPLE: %u\n", dev->dev_cur_ordered_id, @@ -1856,8 +1852,7 @@ static void transport_complete_task_attr(struct se_cmd *cmd) " HEAD_OF_QUEUE: %u\n", dev->dev_cur_ordered_id, cmd->se_ordered_id); } else if (cmd->sam_task_attr == MSG_ORDERED_TAG) { - atomic_dec(&dev->dev_ordered_sync); - smp_mb__after_atomic(); + atomic_dec_mb(&dev->dev_ordered_sync); dev->dev_cur_ordered_id++; pr_debug("Incremented dev_cur_ordered_id: %u for ORDERED:" @@ -1915,8 +1910,7 @@ static void transport_handle_queue_full( { spin_lock_irq(&dev->qf_cmd_lock); list_add_tail(&cmd->se_qf_node, &cmd->se_dev->qf_cmd_list); - atomic_inc(&dev->dev_qf_count); - smp_mb__after_atomic(); + atomic_inc_mb(&dev->dev_qf_count); spin_unlock_irq(&cmd->se_dev->qf_cmd_lock); schedule_work(&cmd->se_dev->qf_work_queue); diff --git a/drivers/target/target_core_ua.c b/drivers/target/target_core_ua.c index 101858e..1738b16 100644 --- a/drivers/target/target_core_ua.c +++ b/drivers/target/target_core_ua.c @@ -161,8 +161,7 @@ int core_scsi3_ua_allocate( spin_unlock(&deve->ua_lock); spin_unlock_irq(&nacl->device_list_lock); - atomic_inc(&deve->ua_count); - smp_mb__after_atomic(); + atomic_inc_mb(&deve->ua_count); return 0; } list_add_tail(&ua->ua_nacl_list, &deve->ua_list); @@ -174,8 +173,7 @@ int core_scsi3_ua_allocate( nacl->se_tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun, asc, ascq); - atomic_inc(&deve->ua_count); - smp_mb__after_atomic(); + atomic_inc_mb(&deve->ua_count); return 0; } @@ -189,8 +187,7 @@ void core_scsi3_ua_release_all( list_del(&ua->ua_nacl_list); kmem_cache_free(se_ua_cache, ua); - atomic_dec(&deve->ua_count); - smp_mb__after_atomic(); + atomic_dec_mb(&deve->ua_count); } spin_unlock(&deve->ua_lock); } @@ -250,8 +247,7 @@ void core_scsi3_ua_for_check_condition( list_del(&ua->ua_nacl_list); kmem_cache_free(se_ua_cache, ua); - atomic_dec(&deve->ua_count); - smp_mb__after_atomic(); + atomic_dec_mb(&deve->ua_count); } spin_unlock(&deve->ua_lock); spin_unlock_irq(&nacl->device_list_lock); @@ -309,8 +305,7 @@ int core_scsi3_ua_clear_for_request_sense( list_del(&ua->ua_nacl_list); kmem_cache_free(se_ua_cache, ua); - atomic_dec(&deve->ua_count); - smp_mb__after_atomic(); + atomic_dec_mb(&deve->ua_count); } spin_unlock(&deve->ua_lock); spin_unlock_irq(&nacl->device_list_lock); -- cgit v1.1 From f81ccb489a7a641c1bed41b49cf8d72c199c68d5 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 16 Sep 2014 16:23:13 -0400 Subject: target: simplify core_tmr_abort_task list_for_each_entry_safe is necessary if list objects are deleted from the list while traversing it. Not the case here, so we can use the base list_for_each_entry variant. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_tmr.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 83de7ae..fa5e157 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -115,13 +115,12 @@ void core_tmr_abort_task( struct se_tmr_req *tmr, struct se_session *se_sess) { - struct se_cmd *se_cmd, *tmp_cmd; + struct se_cmd *se_cmd; unsigned long flags; int ref_tag; spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); - list_for_each_entry_safe(se_cmd, tmp_cmd, - &se_sess->sess_cmd_list, se_cmd_list) { + list_for_each_entry(se_cmd, &se_sess->sess_cmd_list, se_cmd_list) { if (dev != se_cmd->se_dev) continue; -- cgit v1.1 From c57010420654aca179c500f61e86315a337244ca Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 16 Sep 2014 16:23:14 -0400 Subject: qla_target: remove unused parameter Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/scsi/qla2xxx/qla_target.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index e632e14..577058e 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -401,7 +401,7 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) #if 0 /* FIXME: Re-enable Global event handling.. */ /* Global event */ atomic_inc(&ha->tgt.qla_tgt->tgt_global_resets_count); - qlt_clear_tgt_db(ha->tgt.qla_tgt, 1); + qlt_clear_tgt_db(ha->tgt.qla_tgt); if (!list_empty(&ha->tgt.qla_tgt->sess_list)) { sess = list_entry(ha->tgt.qla_tgt->sess_list.next, typeof(*sess), sess_list_entry); @@ -483,7 +483,7 @@ static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess, } /* ha->hardware_lock supposed to be held on entry */ -static void qlt_clear_tgt_db(struct qla_tgt *tgt, bool local_only) +static void qlt_clear_tgt_db(struct qla_tgt *tgt) { struct qla_tgt_sess *sess; @@ -835,7 +835,7 @@ int qlt_stop_phase1(struct qla_tgt *tgt) mutex_lock(&vha->vha_tgt.tgt_mutex); spin_lock_irqsave(&ha->hardware_lock, flags); tgt->tgt_stop = 1; - qlt_clear_tgt_db(tgt, true); + qlt_clear_tgt_db(tgt); spin_unlock_irqrestore(&ha->hardware_lock, flags); mutex_unlock(&vha->vha_tgt.tgt_mutex); mutex_unlock(&qla_tgt_mutex); -- cgit v1.1 From 55a9066fffd2f533e7ed434b072469ef09d6c476 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 16 Sep 2014 16:23:15 -0400 Subject: qla_target: make some global functions static Also removes the declarations from the header - including two declarations without function definitions or callers. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/scsi/qla2xxx/qla_target.c | 10 +++++----- drivers/scsi/qla2xxx/qla_target.h | 10 ---------- 2 files changed, 5 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 577058e..c22b1c1 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -101,6 +101,7 @@ static void qlt_send_term_exchange(struct scsi_qla_host *ha, struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked); static void qlt_reject_free_srr_imm(struct scsi_qla_host *ha, struct qla_tgt_srr_imm *imm, int ha_lock); +static void qlt_disable_vha(struct scsi_qla_host *vha); /* * Global Variables */ @@ -178,7 +179,7 @@ struct scsi_qla_host *qlt_find_host_by_vp_idx(struct scsi_qla_host *vha, return NULL; } -void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, +static void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, struct atio_from_isp *atio) { ql_dbg(ql_dbg_tgt, vha, 0xe072, @@ -5024,7 +5025,7 @@ void qlt_lport_deregister(struct scsi_qla_host *vha) EXPORT_SYMBOL(qlt_lport_deregister); /* Must be called under HW lock */ -void qlt_set_mode(struct scsi_qla_host *vha) +static void qlt_set_mode(struct scsi_qla_host *vha) { struct qla_hw_data *ha = vha->hw; @@ -5045,7 +5046,7 @@ void qlt_set_mode(struct scsi_qla_host *vha) } /* Must be called under HW lock */ -void qlt_clear_mode(struct scsi_qla_host *vha) +static void qlt_clear_mode(struct scsi_qla_host *vha) { struct qla_hw_data *ha = vha->hw; @@ -5109,8 +5110,7 @@ EXPORT_SYMBOL(qlt_enable_vha); * * Disable Target Mode and reset the adapter */ -void -qlt_disable_vha(struct scsi_qla_host *vha) +static void qlt_disable_vha(struct scsi_qla_host *vha) { struct qla_hw_data *ha = vha->hw; struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h index d1d24fb..bc1aee7 100644 --- a/drivers/scsi/qla2xxx/qla_target.h +++ b/drivers/scsi/qla2xxx/qla_target.h @@ -1003,10 +1003,6 @@ struct qla_tgt_srr_ctio { extern struct qla_tgt_data qla_target; -/* - * Internal function prototypes - */ -void qlt_disable_vha(struct scsi_qla_host *); /* * Function prototypes for qla_target.c logic used by qla2xxx LLD code. @@ -1019,8 +1015,6 @@ extern void qlt_lport_deregister(struct scsi_qla_host *); extern void qlt_unreg_sess(struct qla_tgt_sess *); extern void qlt_fc_port_added(struct scsi_qla_host *, fc_port_t *); extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *); -extern void qlt_set_mode(struct scsi_qla_host *ha); -extern void qlt_clear_mode(struct scsi_qla_host *ha); extern int __init qlt_init(void); extern void qlt_exit(void); extern void qlt_update_vp_map(struct scsi_qla_host *, int); @@ -1053,13 +1047,9 @@ static inline void qla_reverse_ini_mode(struct scsi_qla_host *ha) /* * Exported symbols from qla_target.c LLD logic used by qla2xxx code.. */ -extern void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *, - struct atio_from_isp *); extern void qlt_response_pkt_all_vps(struct scsi_qla_host *, response_t *); extern int qlt_rdy_to_xfer(struct qla_tgt_cmd *); extern int qlt_xmit_response(struct qla_tgt_cmd *, int, uint8_t); -extern int qlt_rdy_to_xfer_dif(struct qla_tgt_cmd *); -extern int qlt_xmit_response_dif(struct qla_tgt_cmd *, int, uint8_t); extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *); extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *); extern void qlt_free_cmd(struct qla_tgt_cmd *cmd); -- cgit v1.1 From f9b6721a9cef94908467abf7a2cacbd15a7d23cb Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 16 Sep 2014 16:23:18 -0400 Subject: qla_target: improve qlt_unmap_sg() Remove the inline attribute. Modern compilers ignore it and the function has grown beyond where inline made sense anyway. Remove the BUG_ON(!cmd->sg_mapped), and instead return if sg_mapped is not set. Every caller is doing this check, so we might as well have it in one place instead of four. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/scsi/qla2xxx/qla_target.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index c22b1c1..68c90ad 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -1409,12 +1409,13 @@ out_err: return -1; } -static inline void qlt_unmap_sg(struct scsi_qla_host *vha, - struct qla_tgt_cmd *cmd) +static void qlt_unmap_sg(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd) { struct qla_hw_data *ha = vha->hw; - BUG_ON(!cmd->sg_mapped); + if (!cmd->sg_mapped) + return; + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction); cmd->sg_mapped = 0; @@ -2403,8 +2404,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, return 0; out_unmap_unlock: - if (cmd->sg_mapped) - qlt_unmap_sg(vha, cmd); + qlt_unmap_sg(vha, cmd); spin_unlock_irqrestore(&ha->hardware_lock, flags); return res; @@ -2468,8 +2468,7 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd) return res; out_unlock_free_unmap: - if (cmd->sg_mapped) - qlt_unmap_sg(vha, cmd); + qlt_unmap_sg(vha, cmd); spin_unlock_irqrestore(&ha->hardware_lock, flags); return res; @@ -2706,8 +2705,7 @@ done: if (!ha_locked && !in_interrupt()) msleep(250); /* just in case */ - if (cmd->sg_mapped) - qlt_unmap_sg(vha, cmd); + qlt_unmap_sg(vha, cmd); vha->hw->tgt.tgt_ops->free_cmd(cmd); } return; @@ -2927,8 +2925,7 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, se_cmd = &cmd->se_cmd; tfo = se_cmd->se_tfo; - if (cmd->sg_mapped) - qlt_unmap_sg(vha, cmd); + qlt_unmap_sg(vha, cmd); if (unlikely(status != CTIO_SUCCESS)) { switch (status & 0xFFFF) { -- cgit v1.1 From db3a99b9921f27fe71ca8c0f218ee810e0e7fb69 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 16 Sep 2014 16:23:19 -0400 Subject: qla_target: rearrange struct qla_tgt_prm On most (non-x86) 64bit platforms this will remove 8 padding bytes from the structure. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/scsi/qla2xxx/qla_target.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h index bc1aee7..c93eeab 100644 --- a/drivers/scsi/qla2xxx/qla_target.h +++ b/drivers/scsi/qla2xxx/qla_target.h @@ -971,11 +971,11 @@ struct qla_tgt_prm { struct qla_tgt *tgt; void *pkt; struct scatterlist *sg; /* cmd data buffer SG vector */ + unsigned char *sense_buffer; int seg_cnt; int req_cnt; uint16_t rq_result; uint16_t scsi_status; - unsigned char *sense_buffer; int sense_buffer_len; int residual; int add_status_pkt; -- cgit v1.1 From 082f58ac4a48d3f5cb4597232cb2ac6823a96f43 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Thu, 25 Sep 2014 06:22:28 -0400 Subject: target: Fix queue full status NULL pointer for SCF_TRANSPORT_TASK_SENSE During temporary resource starvation at lower transport layer, command is placed on queue full retry path, which expose this problem. The TCM queue full handling of SCF_TRANSPORT_TASK_SENSE currently sends the same cmd twice to lower layer. The 1st time led to cmd normal free path. The 2nd time cause Null pointer access. This regression bug was originally introduced v3.1-rc code in the following commit: commit e057f53308a5f071556ee80586b99ee755bf07f5 Author: Christoph Hellwig Date: Mon Oct 17 13:56:41 2011 -0400 target: remove the transport_qf_callback se_cmd callback Signed-off-by: Quinn Tran Signed-off-by: Saurav Kashyap Cc: # v3.1+ Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 115632e..9700ea1 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1871,8 +1871,7 @@ static void transport_complete_qf(struct se_cmd *cmd) if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) { trace_target_cmd_complete(cmd); ret = cmd->se_tfo->queue_status(cmd); - if (ret) - goto out; + goto out; } switch (cmd->data_direction) { -- cgit v1.1 From 20959c4b4078847e629eed8918abb52bfe5f559a Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Wed, 1 Oct 2014 16:07:02 -0700 Subject: target: Remove unneeded check in sbc_parse_cdb The check of SCF_SCSI_DATA_CDB seems to be a remnant from before hch's refactoring of this function. There are no places where that flag is set that cmd->execute_cmd isn't also set. Signed-off-by: Andy Grover Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_sbc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index bd78d92..ebe62af 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -948,7 +948,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) } /* reject any command that we don't have a handler for */ - if (!(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) && !cmd->execute_cmd) + if (!cmd->execute_cmd) return TCM_UNSUPPORTED_SCSI_OPCODE; if (cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) { -- cgit v1.1 From f14bb039a4e8206439d3e9abd92bc76bd142f243 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Wed, 1 Oct 2014 16:07:03 -0700 Subject: uio: Export definition of struct uio_device In order to prevent a O(n) search of the filesystem to link up its uio node with its target configuration, TCMU needs to know the minor number that UIO assigned. Expose the definition of this struct so TCMU can access this field. Signed-off-by: Andy Grover Signed-off-by: Nicholas Bellinger --- drivers/uio/uio.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index a673e5b..60fa627 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -28,18 +28,6 @@ #define UIO_MAX_DEVICES (1U << MINORBITS) -struct uio_device { - struct module *owner; - struct device *dev; - int minor; - atomic_t event; - struct fasync_struct *async_queue; - wait_queue_head_t wait; - struct uio_info *info; - struct kobject *map_dir; - struct kobject *portio_dir; -}; - static int uio_major; static struct cdev *uio_cdev; static DEFINE_IDR(uio_idr); -- cgit v1.1 From 161485e8273001e56b2f20755ad9b6217b601fb3 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 4 Aug 2014 18:16:00 +0200 Subject: efi: Implement mandatory locking for UEFI Runtime Services According to section 7.1 of the UEFI spec, Runtime Services are not fully reentrant, and there are particular combinations of calls that need to be serialized. Use a spinlock to serialize all Runtime Services with respect to all others, even if this is more than strictly needed. We've managed to get away without requiring a runtime services lock until now because most of the interactions with EFI involve EFI variables, and those operations are already serialised with __efivars->lock. Some of the assumptions underlying the decision whether locks are needed or not (e.g., SetVariable() against ResetSystem()) may not apply universally to all [new] architectures that implement UEFI. Rather than try to reason our way out of this, let's just implement at least what the spec requires in terms of locking. Signed-off-by: Ard Biesheuvel Signed-off-by: Matt Fleming --- drivers/firmware/efi/runtime-wrappers.c | 154 +++++++++++++++++++++++++++++--- 1 file changed, 144 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 10daa4b..9694cba 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -14,11 +14,83 @@ * This file is released under the GPLv2. */ +#include #include -#include /* spinlock_t */ +#include +#include #include /* + * According to section 7.1 of the UEFI spec, Runtime Services are not fully + * reentrant, and there are particular combinations of calls that need to be + * serialized. (source: UEFI Specification v2.4A) + * + * Table 31. Rules for Reentry Into Runtime Services + * +------------------------------------+-------------------------------+ + * | If previous call is busy in | Forbidden to call | + * +------------------------------------+-------------------------------+ + * | Any | SetVirtualAddressMap() | + * +------------------------------------+-------------------------------+ + * | ConvertPointer() | ConvertPointer() | + * +------------------------------------+-------------------------------+ + * | SetVariable() | ResetSystem() | + * | UpdateCapsule() | | + * | SetTime() | | + * | SetWakeupTime() | | + * | GetNextHighMonotonicCount() | | + * +------------------------------------+-------------------------------+ + * | GetVariable() | GetVariable() | + * | GetNextVariableName() | GetNextVariableName() | + * | SetVariable() | SetVariable() | + * | QueryVariableInfo() | QueryVariableInfo() | + * | UpdateCapsule() | UpdateCapsule() | + * | QueryCapsuleCapabilities() | QueryCapsuleCapabilities() | + * | GetNextHighMonotonicCount() | GetNextHighMonotonicCount() | + * +------------------------------------+-------------------------------+ + * | GetTime() | GetTime() | + * | SetTime() | SetTime() | + * | GetWakeupTime() | GetWakeupTime() | + * | SetWakeupTime() | SetWakeupTime() | + * +------------------------------------+-------------------------------+ + * + * Due to the fact that the EFI pstore may write to the variable store in + * interrupt context, we need to use a spinlock for at least the groups that + * contain SetVariable() and QueryVariableInfo(). That leaves little else, as + * none of the remaining functions are actually ever called at runtime. + * So let's just use a single spinlock to serialize all Runtime Services calls. + */ +static DEFINE_SPINLOCK(efi_runtime_lock); + +/* + * Some runtime services calls can be reentrant under NMI, even if the table + * above says they are not. (source: UEFI Specification v2.4A) + * + * Table 32. Functions that may be called after Machine Check, INIT and NMI + * +----------------------------+------------------------------------------+ + * | Function | Called after Machine Check, INIT and NMI | + * +----------------------------+------------------------------------------+ + * | GetTime() | Yes, even if previously busy. | + * | GetVariable() | Yes, even if previously busy | + * | GetNextVariableName() | Yes, even if previously busy | + * | QueryVariableInfo() | Yes, even if previously busy | + * | SetVariable() | Yes, even if previously busy | + * | UpdateCapsule() | Yes, even if previously busy | + * | QueryCapsuleCapabilities() | Yes, even if previously busy | + * | ResetSystem() | Yes, even if previously busy | + * +----------------------------+------------------------------------------+ + * + * In order to prevent deadlocks under NMI, the wrappers for these functions + * may only grab the efi_runtime_lock or rtc_lock spinlocks if !efi_in_nmi(). + * However, not all of the services listed are reachable through NMI code paths, + * so the the special handling as suggested by the UEFI spec is only implemented + * for QueryVariableInfo() and SetVariable(), as these can be reached in NMI + * context through efi_pstore_write(). + */ +#ifndef efi_in_nmi +#define efi_in_nmi() (0) +#endif + +/* * As per commit ef68c8f87ed1 ("x86: Serialize EFI time accesses on rtc_lock"), * the EFI specification requires that callers of the time related runtime * functions serialize with other CMOS accesses in the kernel, as the EFI time @@ -32,7 +104,9 @@ static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) efi_status_t status; spin_lock_irqsave(&rtc_lock, flags); + spin_lock(&efi_runtime_lock); status = efi_call_virt(get_time, tm, tc); + spin_unlock(&efi_runtime_lock); spin_unlock_irqrestore(&rtc_lock, flags); return status; } @@ -43,7 +117,9 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm) efi_status_t status; spin_lock_irqsave(&rtc_lock, flags); + spin_lock(&efi_runtime_lock); status = efi_call_virt(set_time, tm); + spin_unlock(&efi_runtime_lock); spin_unlock_irqrestore(&rtc_lock, flags); return status; } @@ -56,7 +132,9 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, efi_status_t status; spin_lock_irqsave(&rtc_lock, flags); + spin_lock(&efi_runtime_lock); status = efi_call_virt(get_wakeup_time, enabled, pending, tm); + spin_unlock(&efi_runtime_lock); spin_unlock_irqrestore(&rtc_lock, flags); return status; } @@ -67,7 +145,9 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) efi_status_t status; spin_lock_irqsave(&rtc_lock, flags); + spin_lock(&efi_runtime_lock); status = efi_call_virt(set_wakeup_time, enabled, tm); + spin_unlock(&efi_runtime_lock); spin_unlock_irqrestore(&rtc_lock, flags); return status; } @@ -78,14 +158,27 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name, unsigned long *data_size, void *data) { - return efi_call_virt(get_variable, name, vendor, attr, data_size, data); + unsigned long flags; + efi_status_t status; + + spin_lock_irqsave(&efi_runtime_lock, flags); + status = efi_call_virt(get_variable, name, vendor, attr, data_size, + data); + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; } static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, efi_char16_t *name, efi_guid_t *vendor) { - return efi_call_virt(get_next_variable, name_size, name, vendor); + unsigned long flags; + efi_status_t status; + + spin_lock_irqsave(&efi_runtime_lock, flags); + status = efi_call_virt(get_next_variable, name_size, name, vendor); + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; } static efi_status_t virt_efi_set_variable(efi_char16_t *name, @@ -94,7 +187,17 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, unsigned long data_size, void *data) { - return efi_call_virt(set_variable, name, vendor, attr, data_size, data); + unsigned long flags; + efi_status_t status; + bool __in_nmi = efi_in_nmi(); + + if (!__in_nmi) + spin_lock_irqsave(&efi_runtime_lock, flags); + status = efi_call_virt(set_variable, name, vendor, attr, data_size, + data); + if (!__in_nmi) + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; } static efi_status_t virt_efi_query_variable_info(u32 attr, @@ -102,16 +205,31 @@ static efi_status_t virt_efi_query_variable_info(u32 attr, u64 *remaining_space, u64 *max_variable_size) { + unsigned long flags; + efi_status_t status; + bool __in_nmi = efi_in_nmi(); + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) return EFI_UNSUPPORTED; - return efi_call_virt(query_variable_info, attr, storage_space, - remaining_space, max_variable_size); + if (!__in_nmi) + spin_lock_irqsave(&efi_runtime_lock, flags); + status = efi_call_virt(query_variable_info, attr, storage_space, + remaining_space, max_variable_size); + if (!__in_nmi) + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; } static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) { - return efi_call_virt(get_next_high_mono_count, count); + unsigned long flags; + efi_status_t status; + + spin_lock_irqsave(&efi_runtime_lock, flags); + status = efi_call_virt(get_next_high_mono_count, count); + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; } static void virt_efi_reset_system(int reset_type, @@ -119,17 +237,27 @@ static void virt_efi_reset_system(int reset_type, unsigned long data_size, efi_char16_t *data) { + unsigned long flags; + + spin_lock_irqsave(&efi_runtime_lock, flags); __efi_call_virt(reset_system, reset_type, status, data_size, data); + spin_unlock_irqrestore(&efi_runtime_lock, flags); } static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, unsigned long count, unsigned long sg_list) { + unsigned long flags; + efi_status_t status; + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) return EFI_UNSUPPORTED; - return efi_call_virt(update_capsule, capsules, count, sg_list); + spin_lock_irqsave(&efi_runtime_lock, flags); + status = efi_call_virt(update_capsule, capsules, count, sg_list); + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; } static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, @@ -137,11 +265,17 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, u64 *max_size, int *reset_type) { + unsigned long flags; + efi_status_t status; + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) return EFI_UNSUPPORTED; - return efi_call_virt(query_capsule_caps, capsules, count, max_size, - reset_type); + spin_lock_irqsave(&efi_runtime_lock, flags); + status = efi_call_virt(query_capsule_caps, capsules, count, max_size, + reset_type); + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; } void efi_native_runtime_setup(void) -- cgit v1.1 From 5a17dae422d7de4b776a9753cd4673a343a25b4b Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Tue, 5 Aug 2014 11:52:11 +0100 Subject: efi: Add efi= parameter parsing to the EFI boot stub We need a way to customize the behaviour of the EFI boot stub, in particular, we need a way to disable the "chunking" workaround, used when reading files from the EFI System Partition. One of my machines doesn't cope well when reading files in 1MB chunks to a buffer above the 4GB mark - it appears that the "chunking" bug workaround triggers another firmware bug. This was only discovered with commit 4bf7111f5016 ("x86/efi: Support initrd loaded above 4G"), and that commit is perfectly valid. The symptom I observed was a corrupt initrd rather than any kind of crash. efi= is now used to specify EFI parameters in two very different execution environments, the EFI boot stub and during kernel boot. There is also a slight performance optimization by enabling efi=nochunk, but that's offset by the fact that you're more likely to run into firmware issues, at least on x86. This is the rationale behind leaving the workaround enabled by default. Also provide some documentation for EFI_READ_CHUNK_SIZE and why we're using the current value of 1MB. Tested-by: Ard Biesheuvel Cc: Roy Franz Cc: Maarten Lankhorst Cc: Leif Lindholm Cc: Borislav Petkov Signed-off-by: Matt Fleming --- drivers/firmware/efi/libstub/arm-stub.c | 4 ++ drivers/firmware/efi/libstub/efi-stub-helper.c | 62 +++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index 480339b..75ee059 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -226,6 +226,10 @@ unsigned long __init efi_entry(void *handle, efi_system_table_t *sys_table, goto fail_free_image; } + status = efi_parse_options(cmdline_ptr); + if (status != EFI_SUCCESS) + pr_efi_err(sys_table, "Failed to parse EFI cmdline options\n"); + /* * Unauthenticated device tree data is a security hazard, so * ignore 'dtb=' unless UEFI Secure Boot is disabled. diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 32d5cca..a920fec 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -15,8 +15,23 @@ #include "efistub.h" +/* + * Some firmware implementations have problems reading files in one go. + * A read chunk size of 1MB seems to work for most platforms. + * + * Unfortunately, reading files in chunks triggers *other* bugs on some + * platforms, so we provide a way to disable this workaround, which can + * be done by passing "efi=nochunk" on the EFI boot stub command line. + * + * If you experience issues with initrd images being corrupt it's worth + * trying efi=nochunk, but chunking is enabled by default because there + * are far more machines that require the workaround than those that + * break with it enabled. + */ #define EFI_READ_CHUNK_SIZE (1024 * 1024) +static unsigned long __chunk_size = EFI_READ_CHUNK_SIZE; + struct file_info { efi_file_handle_t *handle; u64 size; @@ -281,6 +296,49 @@ void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, efi_call_early(free_pages, addr, nr_pages); } +/* + * Parse the ASCII string 'cmdline' for EFI options, denoted by the efi= + * option, e.g. efi=nochunk. + * + * It should be noted that efi= is parsed in two very different + * environments, first in the early boot environment of the EFI boot + * stub, and subsequently during the kernel boot. + */ +efi_status_t efi_parse_options(char *cmdline) +{ + char *str; + + /* + * If no EFI parameters were specified on the cmdline we've got + * nothing to do. + */ + str = strstr(cmdline, "efi="); + if (!str) + return EFI_SUCCESS; + + /* Skip ahead to first argument */ + str += strlen("efi="); + + /* + * Remember, because efi= is also used by the kernel we need to + * skip over arguments we don't understand. + */ + while (*str) { + if (!strncmp(str, "nochunk", 7)) { + str += strlen("nochunk"); + __chunk_size = -1UL; + } + + /* Group words together, delimited by "," */ + while (*str && *str != ',') + str++; + + if (*str == ',') + str++; + } + + return EFI_SUCCESS; +} /* * Check the cmdline for a LILO-style file= arguments. @@ -423,8 +481,8 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, size = files[j].size; while (size) { unsigned long chunksize; - if (size > EFI_READ_CHUNK_SIZE) - chunksize = EFI_READ_CHUNK_SIZE; + if (size > __chunk_size) + chunksize = __chunk_size; else chunksize = size; -- cgit v1.1 From b2e0a54a1296a91b800f316df7bef7d1905e4fd0 Mon Sep 17 00:00:00 2001 From: Dave Young Date: Thu, 14 Aug 2014 17:15:26 +0800 Subject: efi: Move noefi early param code out of x86 arch code noefi param can be used for arches other than X86 later, thus move it out of x86 platform code. Signed-off-by: Dave Young Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 64ecbb5..c8f01a7 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -41,6 +41,19 @@ struct efi __read_mostly efi = { }; EXPORT_SYMBOL(efi); +static bool disable_runtime; +static int __init setup_noefi(char *arg) +{ + disable_runtime = true; + return 0; +} +early_param("noefi", setup_noefi); + +bool efi_runtime_disabled(void) +{ + return disable_runtime; +} + static struct kobject *efi_kobj; static struct kobject *efivars_kobj; -- cgit v1.1 From 5ae3683c380e78aebc60d710617ba2c0dccc9e84 Mon Sep 17 00:00:00 2001 From: Dave Young Date: Thu, 14 Aug 2014 17:15:28 +0800 Subject: efi: Add kernel param efi=noruntime noefi kernel param means actually disabling efi runtime, Per suggestion from Leif Lindholm efi=noruntime should be better. But since noefi is already used in X86 thus just adding another param efi=noruntime for same purpose. Signed-off-by: Dave Young Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index c8f01a7..cebfa36 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -54,6 +54,15 @@ bool efi_runtime_disabled(void) return disable_runtime; } +static int __init parse_efi_cmdline(char *str) +{ + if (parse_option_str(str, "noruntime")) + disable_runtime = true; + + return 0; +} +early_param("efi", parse_efi_cmdline); + static struct kobject *efi_kobj; static struct kobject *efivars_kobj; -- cgit v1.1 From 98d2a6ca14520904a47c46258d3bad02ffcd3f96 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Wed, 3 Sep 2014 13:32:20 +0200 Subject: efi: Introduce efi_md_typeattr_format() At the moment, there are three architectures debug-printing the EFI memory map at initialization: x86, ia64, and arm64. They all use different format strings, plus the EFI memory type and the EFI memory attributes are similarly hard to decode for a human reader. Introduce a helper __init function that formats the memory type and the memory attributes in a unified way, to a user-provided character buffer. The array "memory_type_name" is copied from the arm64 code, temporarily duplicating it. The (otherwise optional) braces around each string literal in the initializer list are dropped in order to match the kernel coding style more closely. The element size is tightened from 32 to 20 bytes (maximum actual string length + 1) so that we can derive the field width from the element size. Signed-off-by: Laszlo Ersek Tested-by: Ard Biesheuvel Acked-by: Ard Biesheuvel [ Dropped useless 'register' keyword, which compiler will ignore ] Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index cebfa36..8590099 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -445,3 +445,60 @@ int __init efi_get_fdt_params(struct efi_fdt_params *params, int verbose) return ret; } #endif /* CONFIG_EFI_PARAMS_FROM_FDT */ + +static __initdata char memory_type_name[][20] = { + "Reserved", + "Loader Code", + "Loader Data", + "Boot Code", + "Boot Data", + "Runtime Code", + "Runtime Data", + "Conventional Memory", + "Unusable Memory", + "ACPI Reclaim Memory", + "ACPI Memory NVS", + "Memory Mapped I/O", + "MMIO Port Space", + "PAL Code" +}; + +char * __init efi_md_typeattr_format(char *buf, size_t size, + const efi_memory_desc_t *md) +{ + char *pos; + int type_len; + u64 attr; + + pos = buf; + if (md->type >= ARRAY_SIZE(memory_type_name)) + type_len = snprintf(pos, size, "[type=%u", md->type); + else + type_len = snprintf(pos, size, "[%-*s", + (int)(sizeof(memory_type_name[0]) - 1), + memory_type_name[md->type]); + if (type_len >= size) + return buf; + + pos += type_len; + size -= type_len; + + attr = md->attribute; + if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | + EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP | + EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RUNTIME)) + snprintf(pos, size, "|attr=0x%016llx]", + (unsigned long long)attr); + else + snprintf(pos, size, "|%3s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", + attr & EFI_MEMORY_RUNTIME ? "RUN" : "", + attr & EFI_MEMORY_XP ? "XP" : "", + attr & EFI_MEMORY_RP ? "RP" : "", + attr & EFI_MEMORY_WP ? "WP" : "", + attr & EFI_MEMORY_UCE ? "UCE" : "", + attr & EFI_MEMORY_WB ? "WB" : "", + attr & EFI_MEMORY_WT ? "WT" : "", + attr & EFI_MEMORY_WC ? "WC" : "", + attr & EFI_MEMORY_UC ? "UC" : ""); + return buf; +} -- cgit v1.1 From b2fce819a841eed21034c10a6fe3a8f43532dfb2 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Sat, 6 Sep 2014 06:02:53 -0700 Subject: efi: Resolve some shadow warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is a really bad idea to declare variables or parameters that have the same name as common types. It is valid C, but it gets surprising if a macro expansion attempts to declare an inner local with that type. Change the local names to eliminate the hazard. Change s16 => str16, s8 => str8. This resolves warnings seen when using W=2 during make, for instance: drivers/firmware/efi/vars.c: In function ‘dup_variable_bug’: drivers/firmware/efi/vars.c:324:44: warning: declaration of ‘s16’ shadows a global declaration [-Wshadow] static void dup_variable_bug(efi_char16_t *s16, efi_guid_t *vendor_guid, drivers/firmware/efi/vars.c:328:8: warning: declaration of ‘s8’ shadows a global declaration [-Wshadow] char *s8; Signed-off-by: Mark Rustad Signed-off-by: Jeff Kirsher Signed-off-by: Matt Fleming --- drivers/firmware/efi/vars.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index f0a4364..1fa724f 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -321,11 +321,11 @@ static unsigned long var_name_strnsize(efi_char16_t *variable_name, * Print a warning when duplicate EFI variables are encountered and * disable the sysfs workqueue since the firmware is buggy. */ -static void dup_variable_bug(efi_char16_t *s16, efi_guid_t *vendor_guid, +static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid, unsigned long len16) { size_t i, len8 = len16 / sizeof(efi_char16_t); - char *s8; + char *str8; /* * Disable the workqueue since the algorithm it uses for @@ -334,16 +334,16 @@ static void dup_variable_bug(efi_char16_t *s16, efi_guid_t *vendor_guid, */ efivar_wq_enabled = false; - s8 = kzalloc(len8, GFP_KERNEL); - if (!s8) + str8 = kzalloc(len8, GFP_KERNEL); + if (!str8) return; for (i = 0; i < len8; i++) - s8[i] = s16[i]; + str8[i] = str16[i]; printk(KERN_WARNING "efivars: duplicate variable: %s-%pUl\n", - s8, vendor_guid); - kfree(s8); + str8, vendor_guid); + kfree(str8); } /** -- cgit v1.1 From 6d80dba1c9fe4316ef626980102b92fa30c7845a Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Tue, 30 Sep 2014 21:58:52 +0100 Subject: efi: Provide a non-blocking SetVariable() operation There are some circumstances that call for trying to write an EFI variable in a non-blocking way. One such scenario is when writing pstore data in efi_pstore_write() via the pstore_dump() kdump callback. Now that we have an EFI runtime spinlock we need a way of aborting if there is contention instead of spinning, since when writing pstore data from the kdump callback, the runtime lock may already be held by the CPU that's running the callback if we crashed in the middle of an EFI variable operation. The situation is sufficiently special that a new EFI variable operation is warranted. Introduce ->set_variable_nonblocking() for this use case. It is an optional EFI backend operation, and need only be implemented by those backends that usually acquire locks to serialize access to EFI variables, as is the case for virt_efi_set_variable() where we now grab the EFI runtime spinlock. Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Ard Biesheuvel Cc: Matthew Garrett Signed-off-by: Matt Fleming --- drivers/firmware/efi/runtime-wrappers.c | 19 +++++++++++++ drivers/firmware/efi/vars.c | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 9694cba..4349206 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -200,6 +200,24 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, return status; } +static efi_status_t +virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, + u32 attr, unsigned long data_size, + void *data) +{ + unsigned long flags; + efi_status_t status; + + if (!spin_trylock_irqsave(&efi_runtime_lock, flags)) + return EFI_NOT_READY; + + status = efi_call_virt(set_variable, name, vendor, attr, data_size, + data); + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; +} + + static efi_status_t virt_efi_query_variable_info(u32 attr, u64 *storage_space, u64 *remaining_space, @@ -287,6 +305,7 @@ void efi_native_runtime_setup(void) efi.get_variable = virt_efi_get_variable; efi.get_next_variable = virt_efi_get_next_variable; efi.set_variable = virt_efi_set_variable; + efi.set_variable_nonblocking = virt_efi_set_variable_nonblocking; efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; efi.reset_system = virt_efi_reset_system; efi.query_variable_info = virt_efi_query_variable_info; diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 1fa724f..fa3c66b 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -595,6 +595,39 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes, } EXPORT_SYMBOL_GPL(efivar_entry_set); +/* + * efivar_entry_set_nonblocking - call set_variable_nonblocking() + * + * This function is guaranteed to not block and is suitable for calling + * from crash/panic handlers. + * + * Crucially, this function will not block if it cannot acquire + * __efivars->lock. Instead, it returns -EBUSY. + */ +static int +efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor, + u32 attributes, unsigned long size, void *data) +{ + const struct efivar_operations *ops = __efivars->ops; + unsigned long flags; + efi_status_t status; + + if (!spin_trylock_irqsave(&__efivars->lock, flags)) + return -EBUSY; + + status = check_var_size(attributes, size + ucs2_strsize(name, 1024)); + if (status != EFI_SUCCESS) { + spin_unlock_irqrestore(&__efivars->lock, flags); + return -ENOSPC; + } + + status = ops->set_variable_nonblocking(name, &vendor, attributes, + size, data); + + spin_unlock_irqrestore(&__efivars->lock, flags); + return efi_status_to_err(status); +} + /** * efivar_entry_set_safe - call set_variable() if enough space in firmware * @name: buffer containing the variable name @@ -622,6 +655,20 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes, if (!ops->query_variable_store) return -ENOSYS; + /* + * If the EFI variable backend provides a non-blocking + * ->set_variable() operation and we're in a context where we + * cannot block, then we need to use it to avoid live-locks, + * since the implication is that the regular ->set_variable() + * will block. + * + * If no ->set_variable_nonblocking() is provided then + * ->set_variable() is assumed to be non-blocking. + */ + if (!block && ops->set_variable_nonblocking) + return efivar_entry_set_nonblocking(name, vendor, attributes, + size, data); + if (!block) { if (!spin_trylock_irqsave(&__efivars->lock, flags)) return -EBUSY; -- cgit v1.1 From 60b4dc7720a5251f5dbda69ad404e0bcb3cb6bfb Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Tue, 30 Sep 2014 15:03:38 +0100 Subject: efi: Delete the in_nmi() conditional runtime locking commit 5dc3826d9f08 ("efi: Implement mandatory locking for UEFI Runtime Services") implemented some conditional locking when accessing variable runtime services that Ingo described as "pretty disgusting". The intention with the !efi_in_nmi() checks was to avoid live-locks when trying to write pstore crash data into an EFI variable. Such lockless accesses are allowed according to the UEFI specification when we're in a "non-recoverable" state, but whether or not things are implemented correctly in actual firmware implementations remains an unanswered question, and so it would seem sensible to avoid doing any kind of unsynchronized variable accesses. Furthermore, the efi_in_nmi() tests are inadequate because they don't account for the case where we call EFI variable services from panic or oops callbacks and aren't executing in NMI context. In other words, live-locking is still possible. Let's just remove the conditional locking altogether. Now we've got the ->set_variable_nonblocking() EFI variable operation we can abort if the runtime lock is already held. Aborting is by far the safest option. Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Ard Biesheuvel Cc: Matthew Garrett Signed-off-by: Matt Fleming --- drivers/firmware/efi/runtime-wrappers.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 4349206..228bbf9 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -86,9 +86,6 @@ static DEFINE_SPINLOCK(efi_runtime_lock); * for QueryVariableInfo() and SetVariable(), as these can be reached in NMI * context through efi_pstore_write(). */ -#ifndef efi_in_nmi -#define efi_in_nmi() (0) -#endif /* * As per commit ef68c8f87ed1 ("x86: Serialize EFI time accesses on rtc_lock"), @@ -189,14 +186,11 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, { unsigned long flags; efi_status_t status; - bool __in_nmi = efi_in_nmi(); - if (!__in_nmi) - spin_lock_irqsave(&efi_runtime_lock, flags); + spin_lock_irqsave(&efi_runtime_lock, flags); status = efi_call_virt(set_variable, name, vendor, attr, data_size, data); - if (!__in_nmi) - spin_unlock_irqrestore(&efi_runtime_lock, flags); + spin_unlock_irqrestore(&efi_runtime_lock, flags); return status; } @@ -225,17 +219,14 @@ static efi_status_t virt_efi_query_variable_info(u32 attr, { unsigned long flags; efi_status_t status; - bool __in_nmi = efi_in_nmi(); if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) return EFI_UNSUPPORTED; - if (!__in_nmi) - spin_lock_irqsave(&efi_runtime_lock, flags); + spin_lock_irqsave(&efi_runtime_lock, flags); status = efi_call_virt(query_variable_info, attr, storage_space, remaining_space, max_variable_size); - if (!__in_nmi) - spin_unlock_irqrestore(&efi_runtime_lock, flags); + spin_unlock_irqrestore(&efi_runtime_lock, flags); return status; } -- cgit v1.1 From 3f71f6da7791a5feae0ff07e718431d1df01273a Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 11 Sep 2014 10:20:40 +0200 Subject: efi: rtc-efi: Export platform:rtc-efi as module alias When the rtc-efi driver is built as a module, we already register the EFI rtc as a platform device if UEFI Runtime Services are enabled. To wire it up to udev, and let the module be loaded automatically, we need to export the 'platform:rtc-efi' alias from the module. Signed-off-by: Ard Biesheuvel Cc: Alessandro Zummo Cc: Mark Salter Cc: Dave Young Signed-off-by: Matt Fleming --- drivers/rtc/rtc-efi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index 8225b89..59a55e7 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c @@ -235,3 +235,4 @@ module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe); MODULE_AUTHOR("dann frazier "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("EFI RTC driver"); +MODULE_ALIAS("platform:rtc-efi"); -- cgit v1.1 From 7c9e7a6fe11c8dc5b3b9d0e889dde73347247584 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Wed, 1 Oct 2014 16:07:05 -0700 Subject: target: Add a user-passthrough backstore Add a LIO storage engine that presents commands to userspace for execution. This would allow more complex backstores to be implemented out-of-kernel, and also make experimentation a-la FUSE (but at the SCSI level -- "SUSE"?) possible. It uses a mmap()able UIO device per LUN to share a command ring and data area. The commands are raw SCSI CDBs and iovs for in/out data. The command ring is also reused for returning scsi command status and optional sense data. This implementation is based on Shaohua Li's earlier version but heavily modified. Differences include: * Shared memory allocated by kernel, not locked-down user pages * Single ring for command request and response * Offsets instead of embedded pointers * Generic SCSI CDB passthrough instead of per-cmd specialization in ring format. * Uses UIO device instead of anon_file passed in mailbox. * Optional in-kernel handling of some commands. The main reason for these differences is to permit greater resiliency if the user process dies or hangs. Things not yet implemented (on purpose): * Zero copy. The data area is flexible enough to allow page flipping or backend-allocated pages to be used by fabrics, but it's not clear these are performance wins. Can come later. * Out-of-order command completion by userspace. Possible to add by just allowing userspace to change cmd_id in rsp cmd entries, but currently not supported. * No locks between kernel cmd submission and completion routines. Sounds like it's possible, but this can come later. * Sparse allocation of mmaped area. Current code vmallocs the whole thing. If the mapped area was larger and not fully mapped then the driver would have more freedom to change cmd and data area sizes based on demand. Current code open issues: * The use of idrs may be overkill -- we maybe can replace them with a simple counter to generate cmd_ids, and a hash table to get a cmd_id's associated pointer. * Use of a free-running counter for cmd ring instead of explicit modulo math. This would require power-of-2 cmd ring size. (Add kconfig depends NET - Randy) Signed-off-by: Andy Grover Signed-off-by: Nicholas Bellinger --- drivers/target/Kconfig | 7 + drivers/target/Makefile | 1 + drivers/target/target_core_transport.c | 4 + drivers/target/target_core_user.c | 1163 ++++++++++++++++++++++++++++++++ 4 files changed, 1175 insertions(+) create mode 100644 drivers/target/target_core_user.c (limited to 'drivers') diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig index dc2d84a..81d44c4 100644 --- a/drivers/target/Kconfig +++ b/drivers/target/Kconfig @@ -31,6 +31,13 @@ config TCM_PSCSI Say Y here to enable the TCM/pSCSI subsystem plugin for non-buffered passthrough access to Linux/SCSI device +config TCM_USER + tristate "TCM/USER Subsystem Plugin for Linux" + depends on UIO && NET + help + Say Y here to enable the TCM/USER subsystem plugin for a userspace + process to handle requests + source "drivers/target/loopback/Kconfig" source "drivers/target/tcm_fc/Kconfig" source "drivers/target/iscsi/Kconfig" diff --git a/drivers/target/Makefile b/drivers/target/Makefile index 85b012d..bbb4a7d 100644 --- a/drivers/target/Makefile +++ b/drivers/target/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_TARGET_CORE) += target_core_mod.o obj-$(CONFIG_TCM_IBLOCK) += target_core_iblock.o obj-$(CONFIG_TCM_FILEIO) += target_core_file.o obj-$(CONFIG_TCM_PSCSI) += target_core_pscsi.o +obj-$(CONFIG_TCM_USER) += target_core_user.o # Fabric modules obj-$(CONFIG_LOOPBACK_TARGET) += loopback/ diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 9700ea1..9ea0d5f 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -232,6 +232,10 @@ void transport_subsystem_check_init(void) if (ret != 0) pr_err("Unable to load target_core_pscsi\n"); + ret = request_module("target_core_user"); + if (ret != 0) + pr_err("Unable to load target_core_user\n"); + sub_api_initialized = 1; } diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c new file mode 100644 index 0000000..6608ecf --- /dev/null +++ b/drivers/target/target_core_user.c @@ -0,0 +1,1163 @@ +/* + * Copyright (C) 2013 Shaohua Li + * Copyright (C) 2014 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Define a shared-memory interface for LIO to pass SCSI commands and + * data to userspace for processing. This is to allow backends that + * are too complex for in-kernel support to be possible. + * + * It uses the UIO framework to do a lot of the device-creation and + * introspection work for us. + * + * See the .h file for how the ring is laid out. Note that while the + * command ring is defined, the particulars of the data area are + * not. Offset values in the command entry point to other locations + * internal to the mmap()ed area. There is separate space outside the + * command ring for data buffers. This leaves maximum flexibility for + * moving buffer allocations, or even page flipping or other + * allocation techniques, without altering the command ring layout. + * + * SECURITY: + * The user process must be assumed to be malicious. There's no way to + * prevent it breaking the command ring protocol if it wants, but in + * order to prevent other issues we must only ever read *data* from + * the shared memory area, not offsets or sizes. This applies to + * command ring entries as well as the mailbox. Extra code needed for + * this may have a 'UAM' comment. + */ + + +#define TCMU_TIME_OUT (30 * MSEC_PER_SEC) + +#define CMDR_SIZE (16 * 4096) +#define DATA_SIZE (257 * 4096) + +#define TCMU_RING_SIZE (CMDR_SIZE + DATA_SIZE) + +static struct device *tcmu_root_device; + +struct tcmu_hba { + u32 host_id; +}; + +/* User wants all cmds or just some */ +enum passthru_level { + TCMU_PASS_ALL = 0, + TCMU_PASS_IO, + TCMU_PASS_INVALID, +}; + +#define TCMU_CONFIG_LEN 256 + +struct tcmu_dev { + struct se_device se_dev; + + char *name; + struct se_hba *hba; + +#define TCMU_DEV_BIT_OPEN 0 +#define TCMU_DEV_BIT_BROKEN 1 + unsigned long flags; + enum passthru_level pass_level; + + struct uio_info uio_info; + + struct tcmu_mailbox *mb_addr; + size_t dev_size; + u32 cmdr_size; + u32 cmdr_last_cleaned; + /* Offset of data ring from start of mb */ + size_t data_off; + size_t data_size; + /* Ring head + tail values. */ + /* Must add data_off and mb_addr to get the address */ + size_t data_head; + size_t data_tail; + + wait_queue_head_t wait_cmdr; + /* TODO should this be a mutex? */ + spinlock_t cmdr_lock; + + struct idr commands; + spinlock_t commands_lock; + + struct timer_list timeout; + + char dev_config[TCMU_CONFIG_LEN]; +}; + +#define TCMU_DEV(_se_dev) container_of(_se_dev, struct tcmu_dev, se_dev) + +#define CMDR_OFF sizeof(struct tcmu_mailbox) + +struct tcmu_cmd { + struct se_cmd *se_cmd; + struct tcmu_dev *tcmu_dev; + + uint16_t cmd_id; + + /* Can't use se_cmd->data_length when cleaning up expired cmds, because if + cmd has been completed then accessing se_cmd is off limits */ + size_t data_length; + + unsigned long deadline; + +#define TCMU_CMD_BIT_EXPIRED 0 + unsigned long flags; +}; + +static struct kmem_cache *tcmu_cmd_cache; + +/* multicast group */ +enum tcmu_multicast_groups { + TCMU_MCGRP_CONFIG, +}; + +static const struct genl_multicast_group tcmu_mcgrps[] = { + [TCMU_MCGRP_CONFIG] = { .name = "config", }, +}; + +/* Our generic netlink family */ +static struct genl_family tcmu_genl_family = { + .id = GENL_ID_GENERATE, + .hdrsize = 0, + .name = "TCM-USER", + .version = 1, + .maxattr = TCMU_ATTR_MAX, + .mcgrps = tcmu_mcgrps, + .n_mcgrps = ARRAY_SIZE(tcmu_mcgrps), +}; + +static struct tcmu_cmd *tcmu_alloc_cmd(struct se_cmd *se_cmd) +{ + struct se_device *se_dev = se_cmd->se_dev; + struct tcmu_dev *udev = TCMU_DEV(se_dev); + struct tcmu_cmd *tcmu_cmd; + int cmd_id; + + tcmu_cmd = kmem_cache_zalloc(tcmu_cmd_cache, GFP_KERNEL); + if (!tcmu_cmd) + return NULL; + + tcmu_cmd->se_cmd = se_cmd; + tcmu_cmd->tcmu_dev = udev; + tcmu_cmd->data_length = se_cmd->data_length; + + tcmu_cmd->deadline = jiffies + msecs_to_jiffies(TCMU_TIME_OUT); + + idr_preload(GFP_KERNEL); + spin_lock_irq(&udev->commands_lock); + cmd_id = idr_alloc(&udev->commands, tcmu_cmd, 0, + USHRT_MAX, GFP_NOWAIT); + spin_unlock_irq(&udev->commands_lock); + idr_preload_end(); + + if (cmd_id < 0) { + kmem_cache_free(tcmu_cmd_cache, tcmu_cmd); + return NULL; + } + tcmu_cmd->cmd_id = cmd_id; + + return tcmu_cmd; +} + +static inline void tcmu_flush_dcache_range(void *vaddr, size_t size) +{ + unsigned long offset = (unsigned long) vaddr & ~PAGE_MASK; + + size = round_up(size+offset, PAGE_SIZE); + vaddr -= offset; + + while (size) { + flush_dcache_page(virt_to_page(vaddr)); + size -= PAGE_SIZE; + } +} + +/* + * Some ring helper functions. We don't assume size is a power of 2 so + * we can't use circ_buf.h. + */ +static inline size_t spc_used(size_t head, size_t tail, size_t size) +{ + int diff = head - tail; + + if (diff >= 0) + return diff; + else + return size + diff; +} + +static inline size_t spc_free(size_t head, size_t tail, size_t size) +{ + /* Keep 1 byte unused or we can't tell full from empty */ + return (size - spc_used(head, tail, size) - 1); +} + +static inline size_t head_to_end(size_t head, size_t size) +{ + return size - head; +} + +#define UPDATE_HEAD(head, used, size) smp_store_release(&head, ((head % size) + used) % size) + +/* + * We can't queue a command until we have space available on the cmd ring *and* space + * space avail on the data ring. + * + * Called with ring lock held. + */ +static bool is_ring_space_avail(struct tcmu_dev *udev, size_t cmd_needed, size_t data_needed) +{ + struct tcmu_mailbox *mb = udev->mb_addr; + size_t space; + u32 cmd_head; + + tcmu_flush_dcache_range(mb, sizeof(*mb)); + + cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ + + space = spc_free(cmd_head, udev->cmdr_last_cleaned, udev->cmdr_size); + if (space < cmd_needed) { + pr_debug("no cmd space: %u %u %u\n", cmd_head, + udev->cmdr_last_cleaned, udev->cmdr_size); + return false; + } + + space = spc_free(udev->data_head, udev->data_tail, udev->data_size); + if (space < data_needed) { + pr_debug("no data space: %zu %zu %zu\n", udev->data_head, + udev->data_tail, udev->data_size); + return false; + } + + return true; +} + +static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) +{ + struct tcmu_dev *udev = tcmu_cmd->tcmu_dev; + struct se_cmd *se_cmd = tcmu_cmd->se_cmd; + size_t base_command_size, command_size; + size_t cmdr_space_needed; + struct tcmu_mailbox *mb; + size_t pad_size; + struct tcmu_cmd_entry *entry; + int i; + struct scatterlist *sg; + struct iovec *iov; + int iov_cnt = 0; + uint32_t cmd_head; + uint64_t cdb_off; + + if (test_bit(TCMU_DEV_BIT_BROKEN, &udev->flags)) + return -EINVAL; + + /* + * Must be a certain minimum size for response sense info, but + * also may be larger if the iov array is large. + * + * iovs = sgl_nents+1, for end-of-ring case, plus another 1 + * b/c size == offsetof one-past-element. + */ + base_command_size = max(offsetof(struct tcmu_cmd_entry, + req.iov[se_cmd->t_data_nents + 2]), + sizeof(struct tcmu_cmd_entry)); + command_size = base_command_size + + round_up(scsi_command_size(se_cmd->t_task_cdb), TCMU_OP_ALIGN_SIZE); + + WARN_ON(command_size & (TCMU_OP_ALIGN_SIZE-1)); + + spin_lock_irq(&udev->cmdr_lock); + + mb = udev->mb_addr; + cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ + if ((command_size > (udev->cmdr_size / 2)) + || tcmu_cmd->data_length > (udev->data_size - 1)) + pr_warn("TCMU: Request of size %zu/%zu may be too big for %u/%zu " + "cmd/data ring buffers\n", command_size, tcmu_cmd->data_length, + udev->cmdr_size, udev->data_size); + + /* + * Cmd end-of-ring space is too small so we need space for a NOP plus + * original cmd - cmds are internally contiguous. + */ + if (head_to_end(cmd_head, udev->cmdr_size) >= command_size) + pad_size = 0; + else + pad_size = head_to_end(cmd_head, udev->cmdr_size); + cmdr_space_needed = command_size + pad_size; + + while (!is_ring_space_avail(udev, cmdr_space_needed, tcmu_cmd->data_length)) { + int ret; + DEFINE_WAIT(__wait); + + prepare_to_wait(&udev->wait_cmdr, &__wait, TASK_INTERRUPTIBLE); + + pr_debug("sleeping for ring space\n"); + spin_unlock_irq(&udev->cmdr_lock); + ret = schedule_timeout(msecs_to_jiffies(TCMU_TIME_OUT)); + finish_wait(&udev->wait_cmdr, &__wait); + if (!ret) { + pr_warn("tcmu: command timed out\n"); + return -ETIMEDOUT; + } + + spin_lock_irq(&udev->cmdr_lock); + + /* We dropped cmdr_lock, cmd_head is stale */ + cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ + } + + if (pad_size) { + entry = (void *) mb + CMDR_OFF + cmd_head; + tcmu_flush_dcache_range(entry, sizeof(*entry)); + tcmu_hdr_set_op(&entry->hdr, TCMU_OP_PAD); + tcmu_hdr_set_len(&entry->hdr, pad_size); + + UPDATE_HEAD(mb->cmd_head, pad_size, udev->cmdr_size); + + cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ + WARN_ON(cmd_head != 0); + } + + entry = (void *) mb + CMDR_OFF + cmd_head; + tcmu_flush_dcache_range(entry, sizeof(*entry)); + tcmu_hdr_set_op(&entry->hdr, TCMU_OP_CMD); + tcmu_hdr_set_len(&entry->hdr, command_size); + entry->cmd_id = tcmu_cmd->cmd_id; + + /* + * Fix up iovecs, and handle if allocation in data ring wrapped. + */ + iov = &entry->req.iov[0]; + for_each_sg(se_cmd->t_data_sg, sg, se_cmd->t_data_nents, i) { + size_t copy_bytes = min((size_t)sg->length, + head_to_end(udev->data_head, udev->data_size)); + void *from = kmap_atomic(sg_page(sg)) + sg->offset; + void *to = (void *) mb + udev->data_off + udev->data_head; + + if (tcmu_cmd->se_cmd->data_direction == DMA_TO_DEVICE) { + memcpy(to, from, copy_bytes); + tcmu_flush_dcache_range(to, copy_bytes); + } + + /* Even iov_base is relative to mb_addr */ + iov->iov_len = copy_bytes; + iov->iov_base = (void *) udev->data_off + udev->data_head; + iov_cnt++; + iov++; + + UPDATE_HEAD(udev->data_head, copy_bytes, udev->data_size); + + /* Uh oh, we wrapped the buffer. Must split sg across 2 iovs. */ + if (sg->length != copy_bytes) { + from += copy_bytes; + copy_bytes = sg->length - copy_bytes; + + iov->iov_len = copy_bytes; + iov->iov_base = (void *) udev->data_off + udev->data_head; + + if (se_cmd->data_direction == DMA_TO_DEVICE) { + to = (void *) mb + udev->data_off + udev->data_head; + memcpy(to, from, copy_bytes); + tcmu_flush_dcache_range(to, copy_bytes); + } + + iov_cnt++; + iov++; + + UPDATE_HEAD(udev->data_head, copy_bytes, udev->data_size); + } + + kunmap_atomic(from); + } + entry->req.iov_cnt = iov_cnt; + + /* All offsets relative to mb_addr, not start of entry! */ + cdb_off = CMDR_OFF + cmd_head + base_command_size; + memcpy((void *) mb + cdb_off, se_cmd->t_task_cdb, scsi_command_size(se_cmd->t_task_cdb)); + entry->req.cdb_off = cdb_off; + tcmu_flush_dcache_range(entry, sizeof(*entry)); + + UPDATE_HEAD(mb->cmd_head, command_size, udev->cmdr_size); + tcmu_flush_dcache_range(mb, sizeof(*mb)); + + spin_unlock_irq(&udev->cmdr_lock); + + /* TODO: only if FLUSH and FUA? */ + uio_event_notify(&udev->uio_info); + + mod_timer(&udev->timeout, + round_jiffies_up(jiffies + msecs_to_jiffies(TCMU_TIME_OUT))); + + return 0; +} + +static int tcmu_queue_cmd(struct se_cmd *se_cmd) +{ + struct se_device *se_dev = se_cmd->se_dev; + struct tcmu_dev *udev = TCMU_DEV(se_dev); + struct tcmu_cmd *tcmu_cmd; + int ret; + + tcmu_cmd = tcmu_alloc_cmd(se_cmd); + if (!tcmu_cmd) + return -ENOMEM; + + ret = tcmu_queue_cmd_ring(tcmu_cmd); + if (ret < 0) { + pr_err("TCMU: Could not queue command\n"); + spin_lock_irq(&udev->commands_lock); + idr_remove(&udev->commands, tcmu_cmd->cmd_id); + spin_unlock_irq(&udev->commands_lock); + + kmem_cache_free(tcmu_cmd_cache, tcmu_cmd); + } + + return ret; +} + +static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry *entry) +{ + struct se_cmd *se_cmd = cmd->se_cmd; + struct tcmu_dev *udev = cmd->tcmu_dev; + + if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) { + /* cmd has been completed already from timeout, just reclaim data + ring space */ + UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); + return; + } + + if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { + memcpy(se_cmd->sense_buffer, entry->rsp.sense_buffer, + se_cmd->scsi_sense_length); + + UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); + } + else if (se_cmd->data_direction == DMA_FROM_DEVICE) { + struct scatterlist *sg; + int i; + + /* It'd be easier to look at entry's iovec again, but UAM */ + for_each_sg(se_cmd->t_data_sg, sg, se_cmd->t_data_nents, i) { + size_t copy_bytes; + void *to; + void *from; + + copy_bytes = min((size_t)sg->length, + head_to_end(udev->data_tail, udev->data_size)); + + to = kmap_atomic(sg_page(sg)) + sg->offset; + WARN_ON(sg->length + sg->offset > PAGE_SIZE); + from = (void *) udev->mb_addr + udev->data_off + udev->data_tail; + tcmu_flush_dcache_range(from, copy_bytes); + memcpy(to, from, copy_bytes); + + UPDATE_HEAD(udev->data_tail, copy_bytes, udev->data_size); + + /* Uh oh, wrapped the data buffer for this sg's data */ + if (sg->length != copy_bytes) { + from = (void *) udev->mb_addr + udev->data_off + udev->data_tail; + WARN_ON(udev->data_tail); + to += copy_bytes; + copy_bytes = sg->length - copy_bytes; + tcmu_flush_dcache_range(from, copy_bytes); + memcpy(to, from, copy_bytes); + + UPDATE_HEAD(udev->data_tail, copy_bytes, udev->data_size); + } + + kunmap_atomic(to); + } + + } else if (se_cmd->data_direction == DMA_TO_DEVICE) { + UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); + } else { + pr_warn("TCMU: data direction was %d!\n", se_cmd->data_direction); + } + + target_complete_cmd(cmd->se_cmd, entry->rsp.scsi_status); + cmd->se_cmd = NULL; + + kmem_cache_free(tcmu_cmd_cache, cmd); +} + +static unsigned int tcmu_handle_completions(struct tcmu_dev *udev) +{ + struct tcmu_mailbox *mb; + LIST_HEAD(cpl_cmds); + unsigned long flags; + int handled = 0; + + if (test_bit(TCMU_DEV_BIT_BROKEN, &udev->flags)) { + pr_err("ring broken, not handling completions\n"); + return 0; + } + + spin_lock_irqsave(&udev->cmdr_lock, flags); + + mb = udev->mb_addr; + tcmu_flush_dcache_range(mb, sizeof(*mb)); + + while (udev->cmdr_last_cleaned != ACCESS_ONCE(mb->cmd_tail)) { + + struct tcmu_cmd_entry *entry = (void *) mb + CMDR_OFF + udev->cmdr_last_cleaned; + struct tcmu_cmd *cmd; + + tcmu_flush_dcache_range(entry, sizeof(*entry)); + + if (tcmu_hdr_get_op(&entry->hdr) == TCMU_OP_PAD) { + UPDATE_HEAD(udev->cmdr_last_cleaned, tcmu_hdr_get_len(&entry->hdr), udev->cmdr_size); + continue; + } + WARN_ON(tcmu_hdr_get_op(&entry->hdr) != TCMU_OP_CMD); + + spin_lock(&udev->commands_lock); + cmd = idr_find(&udev->commands, entry->cmd_id); + if (cmd) + idr_remove(&udev->commands, cmd->cmd_id); + spin_unlock(&udev->commands_lock); + + if (!cmd) { + pr_err("cmd_id not found, ring is broken\n"); + set_bit(TCMU_DEV_BIT_BROKEN, &udev->flags); + break; + } + + tcmu_handle_completion(cmd, entry); + + UPDATE_HEAD(udev->cmdr_last_cleaned, tcmu_hdr_get_len(&entry->hdr), udev->cmdr_size); + + handled++; + } + + if (mb->cmd_tail == mb->cmd_head) + del_timer(&udev->timeout); /* no more pending cmds */ + + spin_unlock_irqrestore(&udev->cmdr_lock, flags); + + wake_up(&udev->wait_cmdr); + + return handled; +} + +static int tcmu_check_expired_cmd(int id, void *p, void *data) +{ + struct tcmu_cmd *cmd = p; + + if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) + return 0; + + if (!time_after(cmd->deadline, jiffies)) + return 0; + + set_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags); + target_complete_cmd(cmd->se_cmd, SAM_STAT_CHECK_CONDITION); + cmd->se_cmd = NULL; + + kmem_cache_free(tcmu_cmd_cache, cmd); + + return 0; +} + +static void tcmu_device_timedout(unsigned long data) +{ + struct tcmu_dev *udev = (struct tcmu_dev *)data; + unsigned long flags; + int handled; + + handled = tcmu_handle_completions(udev); + + pr_warn("%d completions handled from timeout\n", handled); + + spin_lock_irqsave(&udev->commands_lock, flags); + idr_for_each(&udev->commands, tcmu_check_expired_cmd, NULL); + spin_unlock_irqrestore(&udev->commands_lock, flags); + + /* + * We don't need to wakeup threads on wait_cmdr since they have their + * own timeout. + */ +} + +static int tcmu_attach_hba(struct se_hba *hba, u32 host_id) +{ + struct tcmu_hba *tcmu_hba; + + tcmu_hba = kzalloc(sizeof(struct tcmu_hba), GFP_KERNEL); + if (!tcmu_hba) + return -ENOMEM; + + tcmu_hba->host_id = host_id; + hba->hba_ptr = tcmu_hba; + + return 0; +} + +static void tcmu_detach_hba(struct se_hba *hba) +{ + kfree(hba->hba_ptr); + hba->hba_ptr = NULL; +} + +static struct se_device *tcmu_alloc_device(struct se_hba *hba, const char *name) +{ + struct tcmu_dev *udev; + + udev = kzalloc(sizeof(struct tcmu_dev), GFP_KERNEL); + if (!udev) + return NULL; + + udev->name = kstrdup(name, GFP_KERNEL); + if (!udev->name) { + kfree(udev); + return NULL; + } + + udev->hba = hba; + + init_waitqueue_head(&udev->wait_cmdr); + spin_lock_init(&udev->cmdr_lock); + + idr_init(&udev->commands); + spin_lock_init(&udev->commands_lock); + + setup_timer(&udev->timeout, tcmu_device_timedout, + (unsigned long)udev); + + udev->pass_level = TCMU_PASS_ALL; + + return &udev->se_dev; +} + +static int tcmu_irqcontrol(struct uio_info *info, s32 irq_on) +{ + struct tcmu_dev *tcmu_dev = container_of(info, struct tcmu_dev, uio_info); + + tcmu_handle_completions(tcmu_dev); + + return 0; +} + +/* + * mmap code from uio.c. Copied here because we want to hook mmap() + * and this stuff must come along. + */ +static int tcmu_find_mem_index(struct vm_area_struct *vma) +{ + struct tcmu_dev *udev = vma->vm_private_data; + struct uio_info *info = &udev->uio_info; + + if (vma->vm_pgoff < MAX_UIO_MAPS) { + if (info->mem[vma->vm_pgoff].size == 0) + return -1; + return (int)vma->vm_pgoff; + } + return -1; +} + +static int tcmu_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct tcmu_dev *udev = vma->vm_private_data; + struct uio_info *info = &udev->uio_info; + struct page *page; + unsigned long offset; + void *addr; + + int mi = tcmu_find_mem_index(vma); + if (mi < 0) + return VM_FAULT_SIGBUS; + + /* + * We need to subtract mi because userspace uses offset = N*PAGE_SIZE + * to use mem[N]. + */ + offset = (vmf->pgoff - mi) << PAGE_SHIFT; + + addr = (void *)(unsigned long)info->mem[mi].addr + offset; + if (info->mem[mi].memtype == UIO_MEM_LOGICAL) + page = virt_to_page(addr); + else + page = vmalloc_to_page(addr); + get_page(page); + vmf->page = page; + return 0; +} + +static const struct vm_operations_struct tcmu_vm_ops = { + .fault = tcmu_vma_fault, +}; + +static int tcmu_mmap(struct uio_info *info, struct vm_area_struct *vma) +{ + struct tcmu_dev *udev = container_of(info, struct tcmu_dev, uio_info); + + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_ops = &tcmu_vm_ops; + + vma->vm_private_data = udev; + + /* Ensure the mmap is exactly the right size */ + if (vma_pages(vma) != (TCMU_RING_SIZE >> PAGE_SHIFT)) + return -EINVAL; + + return 0; +} + +static int tcmu_open(struct uio_info *info, struct inode *inode) +{ + struct tcmu_dev *udev = container_of(info, struct tcmu_dev, uio_info); + + /* O_EXCL not supported for char devs, so fake it? */ + if (test_and_set_bit(TCMU_DEV_BIT_OPEN, &udev->flags)) + return -EBUSY; + + pr_debug("open\n"); + + return 0; +} + +static int tcmu_release(struct uio_info *info, struct inode *inode) +{ + struct tcmu_dev *udev = container_of(info, struct tcmu_dev, uio_info); + + clear_bit(TCMU_DEV_BIT_OPEN, &udev->flags); + + pr_debug("close\n"); + + return 0; +} + +static int tcmu_netlink_event(enum tcmu_genl_cmd cmd, const char *name, int minor) +{ + struct sk_buff *skb; + void *msg_header; + int ret; + + skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + msg_header = genlmsg_put(skb, 0, 0, &tcmu_genl_family, 0, cmd); + if (!msg_header) { + nlmsg_free(skb); + return -ENOMEM; + } + + ret = nla_put_string(skb, TCMU_ATTR_DEVICE, name); + + ret = nla_put_u32(skb, TCMU_ATTR_MINOR, minor); + + ret = genlmsg_end(skb, msg_header); + if (ret < 0) { + nlmsg_free(skb); + return ret; + } + + ret = genlmsg_multicast(&tcmu_genl_family, skb, 0, + TCMU_MCGRP_CONFIG, GFP_KERNEL); + + /* We don't care if no one is listening */ + if (ret == -ESRCH) + ret = 0; + + return ret; +} + +static int tcmu_configure_device(struct se_device *dev) +{ + struct tcmu_dev *udev = TCMU_DEV(dev); + struct tcmu_hba *hba = udev->hba->hba_ptr; + struct uio_info *info; + struct tcmu_mailbox *mb; + size_t size; + size_t used; + int ret = 0; + char *str; + + info = &udev->uio_info; + + size = snprintf(NULL, 0, "tcm-user/%u/%s/%s", hba->host_id, udev->name, + udev->dev_config); + size += 1; /* for \0 */ + str = kmalloc(size, GFP_KERNEL); + if (!str) + return -ENOMEM; + + used = snprintf(str, size, "tcm-user/%u/%s", hba->host_id, udev->name); + + if (udev->dev_config[0]) + snprintf(str + used, size - used, "/%s", udev->dev_config); + + info->name = str; + + udev->mb_addr = vzalloc(TCMU_RING_SIZE); + if (!udev->mb_addr) { + ret = -ENOMEM; + goto err_vzalloc; + } + + /* mailbox fits in first part of CMDR space */ + udev->cmdr_size = CMDR_SIZE - CMDR_OFF; + udev->data_off = CMDR_SIZE; + udev->data_size = TCMU_RING_SIZE - CMDR_SIZE; + + mb = udev->mb_addr; + mb->version = 1; + mb->cmdr_off = CMDR_OFF; + mb->cmdr_size = udev->cmdr_size; + + WARN_ON(!PAGE_ALIGNED(udev->data_off)); + WARN_ON(udev->data_size % PAGE_SIZE); + + info->version = "1"; + + info->mem[0].name = "tcm-user command & data buffer"; + info->mem[0].addr = (phys_addr_t) udev->mb_addr; + info->mem[0].size = TCMU_RING_SIZE; + info->mem[0].memtype = UIO_MEM_VIRTUAL; + + info->irqcontrol = tcmu_irqcontrol; + info->irq = UIO_IRQ_CUSTOM; + + info->mmap = tcmu_mmap; + info->open = tcmu_open; + info->release = tcmu_release; + + ret = uio_register_device(tcmu_root_device, info); + if (ret) + goto err_register; + + /* Other attributes can be configured in userspace */ + dev->dev_attrib.hw_block_size = 512; + dev->dev_attrib.hw_max_sectors = 128; + dev->dev_attrib.hw_queue_depth = 128; + + ret = tcmu_netlink_event(TCMU_CMD_ADDED_DEVICE, udev->uio_info.name, + udev->uio_info.uio_dev->minor); + if (ret) + goto err_netlink; + + return 0; + +err_netlink: + uio_unregister_device(&udev->uio_info); +err_register: + vfree(udev->mb_addr); +err_vzalloc: + kfree(info->name); + + return ret; +} + +static int tcmu_check_pending_cmd(int id, void *p, void *data) +{ + struct tcmu_cmd *cmd = p; + + if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) + return 0; + return -EINVAL; +} + +static void tcmu_free_device(struct se_device *dev) +{ + struct tcmu_dev *udev = TCMU_DEV(dev); + int i; + + del_timer_sync(&udev->timeout); + + vfree(udev->mb_addr); + + /* Upper layer should drain all requests before calling this */ + spin_lock_irq(&udev->commands_lock); + i = idr_for_each(&udev->commands, tcmu_check_pending_cmd, NULL); + idr_destroy(&udev->commands); + spin_unlock_irq(&udev->commands_lock); + WARN_ON(i); + + /* Device was configured */ + if (udev->uio_info.uio_dev) { + tcmu_netlink_event(TCMU_CMD_REMOVED_DEVICE, udev->uio_info.name, + udev->uio_info.uio_dev->minor); + + uio_unregister_device(&udev->uio_info); + kfree(udev->uio_info.name); + kfree(udev->name); + } + + kfree(udev); +} + +enum { + Opt_dev_config, Opt_dev_size, Opt_err, Opt_pass_level, +}; + +static match_table_t tokens = { + {Opt_dev_config, "dev_config=%s"}, + {Opt_dev_size, "dev_size=%u"}, + {Opt_pass_level, "pass_level=%u"}, + {Opt_err, NULL} +}; + +static ssize_t tcmu_set_configfs_dev_params(struct se_device *dev, + const char *page, ssize_t count) +{ + struct tcmu_dev *udev = TCMU_DEV(dev); + char *orig, *ptr, *opts, *arg_p; + substring_t args[MAX_OPT_ARGS]; + int ret = 0, token; + int arg; + + opts = kstrdup(page, GFP_KERNEL); + if (!opts) + return -ENOMEM; + + orig = opts; + + while ((ptr = strsep(&opts, ",\n")) != NULL) { + if (!*ptr) + continue; + + token = match_token(ptr, tokens, args); + switch (token) { + case Opt_dev_config: + if (match_strlcpy(udev->dev_config, &args[0], + TCMU_CONFIG_LEN) == 0) { + ret = -EINVAL; + break; + } + pr_debug("TCMU: Referencing Path: %s\n", udev->dev_config); + break; + case Opt_dev_size: + arg_p = match_strdup(&args[0]); + if (!arg_p) { + ret = -ENOMEM; + break; + } + ret = kstrtoul(arg_p, 0, (unsigned long *) &udev->dev_size); + kfree(arg_p); + if (ret < 0) + pr_err("kstrtoul() failed for dev_size=\n"); + break; + case Opt_pass_level: + match_int(args, &arg); + if (arg >= TCMU_PASS_INVALID) { + pr_warn("TCMU: Invalid pass_level: %d\n", arg); + break; + } + + pr_debug("TCMU: Setting pass_level to %d\n", arg); + udev->pass_level = arg; + break; + default: + break; + } + } + + kfree(orig); + return (!ret) ? count : ret; +} + +static ssize_t tcmu_show_configfs_dev_params(struct se_device *dev, char *b) +{ + struct tcmu_dev *udev = TCMU_DEV(dev); + ssize_t bl = 0; + + bl = sprintf(b + bl, "Config: %s ", + udev->dev_config[0] ? udev->dev_config : "NULL"); + bl += sprintf(b + bl, "Size: %zu PassLevel: %u\n", + udev->dev_size, udev->pass_level); + + return bl; +} + +static sector_t tcmu_get_blocks(struct se_device *dev) +{ + struct tcmu_dev *udev = TCMU_DEV(dev); + + return div_u64(udev->dev_size - dev->dev_attrib.block_size, + dev->dev_attrib.block_size); +} + +static sense_reason_t +tcmu_execute_rw(struct se_cmd *se_cmd, struct scatterlist *sgl, u32 sgl_nents, + enum dma_data_direction data_direction) +{ + int ret; + + ret = tcmu_queue_cmd(se_cmd); + + if (ret != 0) + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + else + return TCM_NO_SENSE; +} + +static sense_reason_t +tcmu_pass_op(struct se_cmd *se_cmd) +{ + int ret = tcmu_queue_cmd(se_cmd); + + if (ret != 0) + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + else + return TCM_NO_SENSE; +} + +static struct sbc_ops tcmu_sbc_ops = { + .execute_rw = tcmu_execute_rw, + .execute_sync_cache = tcmu_pass_op, + .execute_write_same = tcmu_pass_op, + .execute_write_same_unmap = tcmu_pass_op, + .execute_unmap = tcmu_pass_op, +}; + +static sense_reason_t +tcmu_parse_cdb(struct se_cmd *cmd) +{ + unsigned char *cdb = cmd->t_task_cdb; + struct tcmu_dev *udev = TCMU_DEV(cmd->se_dev); + sense_reason_t ret; + + switch (udev->pass_level) { + case TCMU_PASS_ALL: + /* We're just like pscsi, then */ + /* + * For REPORT LUNS we always need to emulate the response, for everything + * else, pass it up. + */ + switch (cdb[0]) { + case REPORT_LUNS: + cmd->execute_cmd = spc_emulate_report_luns; + break; + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + case WRITE_VERIFY: + cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; + /* FALLTHROUGH */ + default: + cmd->execute_cmd = tcmu_pass_op; + } + ret = TCM_NO_SENSE; + break; + case TCMU_PASS_IO: + ret = sbc_parse_cdb(cmd, &tcmu_sbc_ops); + break; + default: + pr_err("Unknown tcm-user pass level %d\n", udev->pass_level); + ret = TCM_CHECK_CONDITION_ABORT_CMD; + } + + return ret; +} + +static struct se_subsystem_api tcmu_template = { + .name = "user", + .inquiry_prod = "USER", + .inquiry_rev = TCMU_VERSION, + .owner = THIS_MODULE, + .transport_type = TRANSPORT_PLUGIN_VHBA_PDEV, + .attach_hba = tcmu_attach_hba, + .detach_hba = tcmu_detach_hba, + .alloc_device = tcmu_alloc_device, + .configure_device = tcmu_configure_device, + .free_device = tcmu_free_device, + .parse_cdb = tcmu_parse_cdb, + .set_configfs_dev_params = tcmu_set_configfs_dev_params, + .show_configfs_dev_params = tcmu_show_configfs_dev_params, + .get_device_type = sbc_get_device_type, + .get_blocks = tcmu_get_blocks, +}; + +static int __init tcmu_module_init(void) +{ + int ret; + + BUILD_BUG_ON((sizeof(struct tcmu_cmd_entry) % TCMU_OP_ALIGN_SIZE) != 0); + + tcmu_cmd_cache = kmem_cache_create("tcmu_cmd_cache", + sizeof(struct tcmu_cmd), + __alignof__(struct tcmu_cmd), + 0, NULL); + if (!tcmu_cmd_cache) + return -ENOMEM; + + tcmu_root_device = root_device_register("tcm_user"); + if (IS_ERR(tcmu_root_device)) { + ret = PTR_ERR(tcmu_root_device); + goto out_free_cache; + } + + ret = genl_register_family(&tcmu_genl_family); + if (ret < 0) { + goto out_unreg_device; + } + + ret = transport_subsystem_register(&tcmu_template); + if (ret) + goto out_unreg_genl; + + return 0; + +out_unreg_genl: + genl_unregister_family(&tcmu_genl_family); +out_unreg_device: + root_device_unregister(tcmu_root_device); +out_free_cache: + kmem_cache_destroy(tcmu_cmd_cache); + + return ret; +} + +static void __exit tcmu_module_exit(void) +{ + transport_subsystem_release(&tcmu_template); + genl_unregister_family(&tcmu_genl_family); + root_device_unregister(tcmu_root_device); + kmem_cache_destroy(tcmu_cmd_cache); +} + +MODULE_DESCRIPTION("TCM USER subsystem plugin"); +MODULE_AUTHOR("Shaohua Li "); +MODULE_AUTHOR("Andy Grover "); +MODULE_LICENSE("GPL"); + +module_init(tcmu_module_init); +module_exit(tcmu_module_exit); -- cgit v1.1 From 6e14eab90a933c2e936639be390bf231a377b44a Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 1 Oct 2014 23:01:15 -0700 Subject: target/user: Fix up smatch warnings in tcmu_netlink_event This patch fixes up the following unused return smatch warnings: drivers/target/target_core_user.c:778 tcmu_netlink_event warn: unused return: ret = nla_put_string() drivers/target/target_core_user.c:780 tcmu_netlink_event warn: unused `return: ret = nla_put_u32() (Fix up missing semicolon: grover) Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_user.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 6608ecf..ac37ce6 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -763,27 +763,27 @@ static int tcmu_netlink_event(enum tcmu_genl_cmd cmd, const char *name, int mino { struct sk_buff *skb; void *msg_header; - int ret; + int ret = -ENOMEM; skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb) - return -ENOMEM; + return ret; msg_header = genlmsg_put(skb, 0, 0, &tcmu_genl_family, 0, cmd); - if (!msg_header) { - nlmsg_free(skb); - return -ENOMEM; - } + if (!msg_header) + goto free_skb; ret = nla_put_string(skb, TCMU_ATTR_DEVICE, name); + if (ret < 0) + goto free_skb; ret = nla_put_u32(skb, TCMU_ATTR_MINOR, minor); + if (ret < 0) + goto free_skb; ret = genlmsg_end(skb, msg_header); - if (ret < 0) { - nlmsg_free(skb); - return ret; - } + if (ret < 0) + goto free_skb; ret = genlmsg_multicast(&tcmu_genl_family, skb, 0, TCMU_MCGRP_CONFIG, GFP_KERNEL); @@ -793,6 +793,9 @@ static int tcmu_netlink_event(enum tcmu_genl_cmd cmd, const char *name, int mino ret = 0; return ret; +free_skb: + nlmsg_free(skb); + return ret; } static int tcmu_configure_device(struct se_device *dev) -- cgit v1.1 From 1acff63f6ec2622662e647364293cc3ca495401f Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Thu, 2 Oct 2014 21:40:34 -0700 Subject: iser-target: Fix smatch warning Unused return value from down_interruptible Reported-by: Or Gerlitz Signed-off-by: Sagi Grimberg Signed-off-by: Nicholas Bellinger --- drivers/infiniband/ulp/isert/ib_isert.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index d4c7928..40969b6 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -3152,7 +3152,7 @@ isert_accept_np(struct iscsi_np *np, struct iscsi_conn *conn) accept_wait: ret = down_interruptible(&isert_np->np_sem); - if (max_accept > 5) + if (ret || max_accept > 5) return -ENODEV; spin_lock_bh(&np->np_thread_lock); -- cgit v1.1 From 6375f8908255ea7438b60bb5998e6b3e1628500d Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 2 Oct 2014 09:30:55 +0200 Subject: tcm_loop: Fixup tag handling The SCSI command tag is set to the tag assigned from the block layer, not the SCSI-II tag message. So we need to convert it into the correct SCSI-II tag message based on the device flags, not the tag value itself. Signed-off-by: Hannes Reinecke Reviewed-by: Sagi Grimberg Signed-off-by: Nicholas Bellinger --- drivers/target/loopback/tcm_loop.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index a7f6dc6..ab3ab27 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -153,18 +153,11 @@ static int tcm_loop_change_queue_type(struct scsi_device *sdev, int tag) /* * Locate the SAM Task Attr from struct scsi_cmnd * */ -static int tcm_loop_sam_attr(struct scsi_cmnd *sc) -{ - if (sc->device->tagged_supported) { - switch (sc->tag) { - case HEAD_OF_QUEUE_TAG: - return MSG_HEAD_TAG; - case ORDERED_QUEUE_TAG: - return MSG_ORDERED_TAG; - default: - break; - } - } +static int tcm_loop_sam_attr(struct scsi_cmnd *sc, int tag) +{ + if (sc->device->tagged_supported && + sc->device->ordered_tags && tag >= 0) + return MSG_ORDERED_TAG; return MSG_SIMPLE_TAG; } @@ -227,7 +220,7 @@ static void tcm_loop_submission_work(struct work_struct *work) rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd, &tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun, - transfer_length, tcm_loop_sam_attr(sc), + transfer_length, tcm_loop_sam_attr(sc, tl_cmd->sc_cmd_tag), sc->sc_data_direction, 0, scsi_sglist(sc), scsi_sg_count(sc), sgl_bidi, sgl_bidi_count, @@ -266,7 +259,7 @@ static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) } tl_cmd->sc = sc; - tl_cmd->sc_cmd_tag = sc->tag; + tl_cmd->sc_cmd_tag = sc->request->tag; INIT_WORK(&tl_cmd->work, tcm_loop_submission_work); queue_work(tcm_loop_workqueue, &tl_cmd->work); return 0; @@ -370,7 +363,7 @@ static int tcm_loop_abort_task(struct scsi_cmnd *sc) */ tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun, - sc->tag, TMR_ABORT_TASK); + sc->request->tag, TMR_ABORT_TASK); return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; } -- cgit v1.1 From f56574a2b554492703030e3d3b9679c9a07a5d69 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Thu, 2 Oct 2014 10:23:15 -0700 Subject: target/user: Recalculate pad size inside is_ring_space_avail() If more than one thread is waiting for command ring space that includes a PAD, then if the first one finishes (inserts a PAD and a CMD at the start of the cmd ring) then the second one will incorrectly think it still needs to insert a PAD (i.e. cmdr_space_needed is now wrong.) This will lead to it asking for more space than it actually needs, and then inserting a PAD somewhere else than at the end -- not what we want. This patch moves the pad calculation inside is_ring_space_available() so in the above scenario the second thread would then ask for space not including a PAD. The patch also inserts a PAD op based upon an up-to-date cmd_head, instead of the potentially stale value. Signed-off-by: Andy Grover Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_user.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index ac37ce6..9a1b314 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -236,16 +236,26 @@ static inline size_t head_to_end(size_t head, size_t size) * * Called with ring lock held. */ -static bool is_ring_space_avail(struct tcmu_dev *udev, size_t cmd_needed, size_t data_needed) +static bool is_ring_space_avail(struct tcmu_dev *udev, size_t cmd_size, size_t data_needed) { struct tcmu_mailbox *mb = udev->mb_addr; size_t space; u32 cmd_head; + size_t cmd_needed; tcmu_flush_dcache_range(mb, sizeof(*mb)); cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ + /* + * If cmd end-of-ring space is too small then we need space for a NOP plus + * original cmd - cmds are internally contiguous. + */ + if (head_to_end(cmd_head, udev->cmdr_size) >= cmd_size) + cmd_needed = cmd_size; + else + cmd_needed = cmd_size + head_to_end(cmd_head, udev->cmdr_size); + space = spc_free(cmd_head, udev->cmdr_last_cleaned, udev->cmdr_size); if (space < cmd_needed) { pr_debug("no cmd space: %u %u %u\n", cmd_head, @@ -268,9 +278,7 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) struct tcmu_dev *udev = tcmu_cmd->tcmu_dev; struct se_cmd *se_cmd = tcmu_cmd->se_cmd; size_t base_command_size, command_size; - size_t cmdr_space_needed; struct tcmu_mailbox *mb; - size_t pad_size; struct tcmu_cmd_entry *entry; int i; struct scatterlist *sg; @@ -307,17 +315,7 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) "cmd/data ring buffers\n", command_size, tcmu_cmd->data_length, udev->cmdr_size, udev->data_size); - /* - * Cmd end-of-ring space is too small so we need space for a NOP plus - * original cmd - cmds are internally contiguous. - */ - if (head_to_end(cmd_head, udev->cmdr_size) >= command_size) - pad_size = 0; - else - pad_size = head_to_end(cmd_head, udev->cmdr_size); - cmdr_space_needed = command_size + pad_size; - - while (!is_ring_space_avail(udev, cmdr_space_needed, tcmu_cmd->data_length)) { + while (!is_ring_space_avail(udev, command_size, tcmu_cmd->data_length)) { int ret; DEFINE_WAIT(__wait); @@ -338,7 +336,10 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ } - if (pad_size) { + /* Insert a PAD if end-of-ring space is too small */ + if (head_to_end(cmd_head, udev->cmdr_size) < command_size) { + size_t pad_size = head_to_end(cmd_head, udev->cmdr_size); + entry = (void *) mb + CMDR_OFF + cmd_head; tcmu_flush_dcache_range(entry, sizeof(*entry)); tcmu_hdr_set_op(&entry->hdr, TCMU_OP_PAD); -- cgit v1.1 From 7efe665903d0d963b0ebf4cab25cc3ae32c62600 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 3 Oct 2014 13:06:33 +0100 Subject: rtc: Disable EFI rtc for x86 commit da167ad7638759 ("rtc: ia64: allow other architectures to use EFI RTC") inadvertently introduced a regression for x86 because we've been careful not to enable the EFI rtc driver due to the generally buggy implementations of the time-related EFI runtime services. In fact, since the above commit was merged we've seen reports of crashes on 32-bit tablets, https://bugzilla.kernel.org/show_bug.cgi?id=84241#c21 Disable it explicitly for x86 so that we don't give users false hope that this driver will work - it won't, and your machine is likely to crash. Acked-by: Mark Salter Cc: Dave Young Cc: Alessandro Zummo Cc: # v3.17 Signed-off-by: Matt Fleming --- drivers/rtc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a168e96..54ef393 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -806,7 +806,7 @@ config RTC_DRV_DA9063 config RTC_DRV_EFI tristate "EFI RTC" - depends on EFI + depends on EFI && !X86 help If you say yes here you will get support for the EFI Real Time Clock. -- cgit v1.1 From f4c24db1b7ad0ce84409e15744d26c6f86a96840 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Fri, 3 Oct 2014 14:35:56 -0700 Subject: qla_target: don't delete changed nacls The code is currently riddled with "drop the hardware_lock to avoid a deadlock" bugs that expose races. One of those races seems to expose a valid warning in tcm_qla2xxx_clear_nacl_from_fcport_map. Add some bandaid to it. Signed-off-by: Joern Engel Cc: # v3.5+ Signed-off-by: Nicholas Bellinger --- drivers/scsi/qla2xxx/tcm_qla2xxx.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index e2beab9..4747d2c 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -757,7 +757,16 @@ static void tcm_qla2xxx_clear_nacl_from_fcport_map(struct qla_tgt_sess *sess) pr_debug("fc_rport domain: port_id 0x%06x\n", nacl->nport_id); node = btree_remove32(&lport->lport_fcport_map, nacl->nport_id); - WARN_ON(node && (node != se_nacl)); + if (WARN_ON(node && (node != se_nacl))) { + /* + * The nacl no longer matches what we think it should be. + * Most likely a new dynamic acl has been added while + * someone dropped the hardware lock. It clearly is a + * bug elsewhere, but this bit can't make things worse. + */ + btree_insert32(&lport->lport_fcport_map, nacl->nport_id, + node, GFP_ATOMIC); + } pr_debug("Removed from fcport_map: %p for WWNN: 0x%016LX, port_id: 0x%06x\n", se_nacl, nacl->nport_wwnn, nacl->nport_id); -- cgit v1.1 From e24805637d2d270d7975502e9024d473de86afdb Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Sat, 4 Oct 2014 04:23:15 +0000 Subject: target: Fix APTPL metadata handling for dynamic MappedLUNs This patch fixes a bug in handling of SPC-3 PR Activate Persistence across Target Power Loss (APTPL) logic where re-creation of state for MappedLUNs from dynamically generated NodeACLs did not occur during I_T Nexus establishment. It adds the missing core_scsi3_check_aptpl_registration() call during core_tpg_check_initiator_node_acl() -> core_tpg_add_node_to_devs() in order to replay any pre-loaded APTPL metadata state associated with the newly connected SCSI Initiator Port. Cc: Mike Christie Cc: Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_device.c | 3 ++- drivers/target/target_core_pr.c | 6 +++--- drivers/target/target_core_pr.h | 2 +- drivers/target/target_core_tpg.c | 8 ++++++++ 4 files changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index f5057a2..d18dd8b 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -1399,7 +1399,8 @@ int core_dev_add_initiator_node_lun_acl( * Check to see if there are any existing persistent reservation APTPL * pre-registrations that need to be enabled for this LUN ACL.. */ - core_scsi3_check_aptpl_registration(lun->lun_se_dev, tpg, lun, lacl); + core_scsi3_check_aptpl_registration(lun->lun_se_dev, tpg, lun, nacl, + lacl->mapped_lun); return 0; } diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 48a8010..a06edb5 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -937,10 +937,10 @@ int core_scsi3_check_aptpl_registration( struct se_device *dev, struct se_portal_group *tpg, struct se_lun *lun, - struct se_lun_acl *lun_acl) + struct se_node_acl *nacl, + u32 mapped_lun) { - struct se_node_acl *nacl = lun_acl->se_lun_nacl; - struct se_dev_entry *deve = nacl->device_list[lun_acl->mapped_lun]; + struct se_dev_entry *deve = nacl->device_list[mapped_lun]; if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS) return 0; diff --git a/drivers/target/target_core_pr.h b/drivers/target/target_core_pr.h index 2ee2936..749fd7b 100644 --- a/drivers/target/target_core_pr.h +++ b/drivers/target/target_core_pr.h @@ -60,7 +60,7 @@ extern int core_scsi3_alloc_aptpl_registration( unsigned char *, u16, u32, int, int, u8); extern int core_scsi3_check_aptpl_registration(struct se_device *, struct se_portal_group *, struct se_lun *, - struct se_lun_acl *); + struct se_node_acl *, u32); extern void core_scsi3_free_pr_reg_from_nacl(struct se_device *, struct se_node_acl *); extern void core_scsi3_free_all_registrations(struct se_device *); diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 6ec5873..aa2b2d0 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -40,6 +40,7 @@ #include #include "target_core_internal.h" +#include "target_core_pr.h" extern struct se_device *g_lun0_dev; @@ -166,6 +167,13 @@ void core_tpg_add_node_to_devs( core_enable_device_list_for_node(lun, NULL, lun->unpacked_lun, lun_access, acl, tpg); + /* + * Check to see if there are any existing persistent reservation + * APTPL pre-registrations that need to be enabled for this dynamic + * LUN ACL now.. + */ + core_scsi3_check_aptpl_registration(dev, tpg, lun, acl, + lun->unpacked_lun); spin_lock(&tpg->tpg_lun_lock); } spin_unlock(&tpg->tpg_lun_lock); -- cgit v1.1 From 92404e609a2dffc55a9a22540ed48b6f0edc9c59 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Sat, 4 Oct 2014 01:06:08 +0000 Subject: target: Add force_pr_aptpl device attribute This patch adds a force_pr_aptpl device attribute used to force SPC-3 PR Activate Persistence across Target Power Loss (APTPL) operation. This makes PR metadata write-out occur during state change regardless if new PERSISTENT_RESERVE_OUT CDBs have their APTPL feature bit set. This is useful during H/A failover in active/passive setups where all PR state is being re-created on a different node, driven by configfs backend device + export layout and pre-loaded $DEV/pr/res_aptpl_metadata. Cc: Mike Christie Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_configfs.c | 4 ++++ drivers/target/target_core_device.c | 18 ++++++++++++++++++ drivers/target/target_core_internal.h | 1 + drivers/target/target_core_pr.c | 9 +++++++++ 4 files changed, 32 insertions(+) (limited to 'drivers') diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 291dc71..b30fc8f 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -665,6 +665,9 @@ SE_DEV_ATTR(is_nonrot, S_IRUGO | S_IWUSR); DEF_DEV_ATTRIB(emulate_rest_reord); SE_DEV_ATTR(emulate_rest_reord, S_IRUGO | S_IWUSR); +DEF_DEV_ATTRIB(force_pr_aptpl); +SE_DEV_ATTR(force_pr_aptpl, S_IRUGO | S_IWUSR); + DEF_DEV_ATTRIB_RO(hw_block_size); SE_DEV_ATTR_RO(hw_block_size); @@ -719,6 +722,7 @@ static struct configfs_attribute *target_core_dev_attrib_attrs[] = { &target_core_dev_attrib_hw_pi_prot_type.attr, &target_core_dev_attrib_pi_prot_format.attr, &target_core_dev_attrib_enforce_pr_isids.attr, + &target_core_dev_attrib_force_pr_aptpl.attr, &target_core_dev_attrib_is_nonrot.attr, &target_core_dev_attrib_emulate_rest_reord.attr, &target_core_dev_attrib_hw_block_size.attr, diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index d18dd8b..c45f9e9 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -1018,6 +1018,23 @@ int se_dev_set_enforce_pr_isids(struct se_device *dev, int flag) return 0; } +int se_dev_set_force_pr_aptpl(struct se_device *dev, int flag) +{ + if ((flag != 0) && (flag != 1)) { + printk(KERN_ERR "Illegal value %d\n", flag); + return -EINVAL; + } + if (dev->export_count) { + pr_err("dev[%p]: Unable to set force_pr_aptpl while" + " export_count is %d\n", dev, dev->export_count); + return -EINVAL; + } + + dev->dev_attrib.force_pr_aptpl = flag; + pr_debug("dev[%p]: SE Device force_pr_aptpl: %d\n", dev, flag); + return 0; +} + int se_dev_set_is_nonrot(struct se_device *dev, int flag) { if ((flag != 0) && (flag != 1)) { @@ -1544,6 +1561,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) dev->dev_attrib.emulate_3pc = DA_EMULATE_3PC; dev->dev_attrib.pi_prot_type = TARGET_DIF_TYPE0_PROT; dev->dev_attrib.enforce_pr_isids = DA_ENFORCE_PR_ISIDS; + dev->dev_attrib.force_pr_aptpl = DA_FORCE_PR_APTPL; dev->dev_attrib.is_nonrot = DA_IS_NONROT; dev->dev_attrib.emulate_rest_reord = DA_EMULATE_REST_REORD; dev->dev_attrib.max_unmap_lba_count = DA_MAX_UNMAP_LBA_COUNT; diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 42ef4ab..e31f42f 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -38,6 +38,7 @@ int se_dev_set_emulate_3pc(struct se_device *, int); int se_dev_set_pi_prot_type(struct se_device *, int); int se_dev_set_pi_prot_format(struct se_device *, int); int se_dev_set_enforce_pr_isids(struct se_device *, int); +int se_dev_set_force_pr_aptpl(struct se_device *, int); int se_dev_set_is_nonrot(struct se_device *, int); int se_dev_set_emulate_rest_reord(struct se_device *dev, int); int se_dev_set_queue_depth(struct se_device *, u32); diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index a06edb5..8c60a1a 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -895,6 +895,7 @@ static int __core_scsi3_check_aptpl_registration( spin_lock(&pr_tmpl->aptpl_reg_lock); list_for_each_entry_safe(pr_reg, pr_reg_tmp, &pr_tmpl->aptpl_reg_list, pr_reg_aptpl_list) { + if (!strcmp(pr_reg->pr_iport, i_port) && (pr_reg->pr_res_mapped_lun == deve->mapped_lun) && !(strcmp(pr_reg->pr_tport, t_port)) && @@ -3470,6 +3471,7 @@ static unsigned long long core_scsi3_extract_reservation_key(unsigned char *cdb) sense_reason_t target_scsi3_emulate_pr_out(struct se_cmd *cmd) { + struct se_device *dev = cmd->se_dev; unsigned char *cdb = &cmd->t_task_cdb[0]; unsigned char *buf; u64 res_key, sa_res_key; @@ -3534,6 +3536,13 @@ target_scsi3_emulate_pr_out(struct se_cmd *cmd) aptpl = (buf[17] & 0x01); unreg = (buf[17] & 0x02); } + /* + * If the backend device has been configured to force APTPL metadata + * write-out, go ahead and propigate aptpl=1 down now. + */ + if (dev->dev_attrib.force_pr_aptpl) + aptpl = 1; + transport_kunmap_data_sg(cmd); buf = NULL; -- cgit v1.1 From 4250c90b30b8bf2a1a21122ba0484f8f351f152d Mon Sep 17 00:00:00 2001 From: Robin van der Gracht Date: Mon, 29 Sep 2014 15:00:07 +0200 Subject: iio: st_sensors: Fix buffer copy Use byte_for_channel as iterator to properly initialize the buffer. Signed-off-by: Robin van der Gracht Acked-by: Denis Ciocca Signed-off-by: Jonathan Cameron Cc: --- drivers/iio/common/st_sensors/st_sensors_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c index 1665c8e..e18bc67 100644 --- a/drivers/iio/common/st_sensors/st_sensors_buffer.c +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -71,7 +71,7 @@ int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf) goto st_sensors_free_memory; } - for (i = 0; i < n * num_data_channels; i++) { + for (i = 0; i < n * byte_for_channel; i++) { if (i < n) buf[i] = rx_array[i]; else -- cgit v1.1 From 0d0f660d882c1c02748ced13966a2413aa5d6cc2 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Sun, 5 Oct 2014 02:13:03 -0700 Subject: iser-target: Disable TX completion interrupt coalescing This patch explicitly disables TX completion interrupt coalescing logic in isert_put_response() and isert_put_datain() that was originally added as an efficiency optimization in commit 95b60f07. It has been reported that this change can trigger ABORT_TASK timeouts under certain small block workloads, where disabling coalescing was required for stability. According to Sagi, this doesn't impact overall performance, so go ahead and disable it for now. Reported-by: Moussa Ba Reported-by: Sagi Grimberg Cc: # 3.13+ Signed-off-by: Nicholas Bellinger --- drivers/infiniband/ulp/isert/ib_isert.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 40969b6..f719112 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -2183,7 +2183,7 @@ isert_put_response(struct iscsi_conn *conn, struct iscsi_cmd *cmd) isert_cmd->tx_desc.num_sge = 2; } - isert_init_send_wr(isert_conn, isert_cmd, send_wr, true); + isert_init_send_wr(isert_conn, isert_cmd, send_wr, false); pr_debug("Posting SCSI Response IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n"); @@ -2882,7 +2882,7 @@ isert_put_datain(struct iscsi_conn *conn, struct iscsi_cmd *cmd) &isert_cmd->tx_desc.iscsi_header); isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc); isert_init_send_wr(isert_conn, isert_cmd, - &isert_cmd->tx_desc.send_wr, true); + &isert_cmd->tx_desc.send_wr, false); isert_cmd->rdma_wr.s_send_wr.next = &isert_cmd->tx_desc.send_wr; wr->send_wr_num += 1; } -- cgit v1.1 From 673e7bbdb3920b62cfc6c710bea626b0a9b0f43a Mon Sep 17 00:00:00 2001 From: "U. Artie Eoff" Date: Mon, 29 Sep 2014 15:49:32 -0700 Subject: drm/i915: intel_backlight scale() math WA Improper truncated integer division in the scale() function causes actual_brightness != brightness. This (partial) work-around should be sufficient for a majority of use-cases, but it is by no means a complete solution. TODO: Determine how best to scale "user" values to "hw" values, and vice-versa, when the ranges are of different sizes. That would be a buggy scenario even with this work-around. The issue was introduced in the following (v3.17-rc1) commit: 6dda730 drm/i915: respect the VBT minimum backlight brightness Note that for easier backporting this commit adds a duplicated macro. A follow-up cleanup patch rectifies this for 3.18+ v2: (thanks to Chris Wilson) clarify commit message, use rounded division macro v3: -DIV_ROUND_CLOSEST() fails to build with CONFIG_X86_32=y. (Jani) -Use DIV_ROUND_CLOSEST_ULL() instead. (Damien) -v1 and v2 originally authored by Joe Konno. Signed-off-by: U. Artie Eoff Cc: stable@vger.kernel.org Reviewed-By: Joe Konno [danvet: Add backporting note.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_panel.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 1878447..97ff71f 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -398,6 +398,9 @@ intel_panel_detect(struct drm_device *dev) } } +#define DIV_ROUND_CLOSEST_ULL(ll, d) \ +({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) + /** * scale - scale values from one range to another * @@ -419,9 +422,8 @@ static uint32_t scale(uint32_t source_val, source_val = clamp(source_val, source_min, source_max); /* avoid overflows */ - target_val = (uint64_t)(source_val - source_min) * - (target_max - target_min); - do_div(target_val, source_max - source_min); + target_val = DIV_ROUND_CLOSEST_ULL((uint64_t)(source_val - source_min) * + (target_max - target_min), source_max - source_min); target_val += target_min; return target_val; -- cgit v1.1 From 2e5416252af5239ddcd92bc858a494c1ca842778 Mon Sep 17 00:00:00 2001 From: "U. Artie Eoff" Date: Mon, 29 Sep 2014 15:49:33 -0700 Subject: drm/i915: Move DIV_ROUND_CLOSEST_ULL macro to header Move the duplicated DIV_ROUND_CLOSEST_ULL macro into the intel_drv.h header file so that it can be shared between intel_display.c and intel_panel.c. Signed-off-by: U. Artie Eoff Reviewed-By: Joe Konno Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 --- drivers/gpu/drm/i915/intel_drv.h | 3 +++ drivers/gpu/drm/i915/intel_panel.c | 3 --- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5073705..6a0cc36 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -73,9 +73,6 @@ static const uint32_t intel_cursor_formats[] = { DRM_FORMAT_ARGB8888, }; -#define DIV_ROUND_CLOSEST_ULL(ll, d) \ -({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) - static void intel_increase_pllclock(struct drm_device *dev, enum pipe pipe); static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 07ce046..ba71522 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -35,6 +35,9 @@ #include #include +#define DIV_ROUND_CLOSEST_ULL(ll, d) \ +({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) + /** * _wait_for - magic (register) wait macro * diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 97ff71f..0e018cb 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -398,9 +398,6 @@ intel_panel_detect(struct drm_device *dev) } } -#define DIV_ROUND_CLOSEST_ULL(ll, d) \ -({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) - /** * scale - scale values from one range to another * -- cgit v1.1 From 1575e288cb3cddb145ae5c4f107e2a4a6d22c067 Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Tue, 30 Sep 2014 17:44:54 +0800 Subject: regulator: rk808: Fix min_uV for DCDC1 & DCDC2 The min_uv in DCDC1 & DCDC2 should be 712.5mv Signed-off-by: Chris Zhong Reviwed-by: Doug Anderson Signed-off-by: Mark Brown --- drivers/regulator/rk808-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index e305416..196a5c8 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -44,7 +44,7 @@ static const int rk808_buck_config_regs[] = { }; static const struct regulator_linear_range rk808_buck_voltage_ranges[] = { - REGULATOR_LINEAR_RANGE(700000, 0, 63, 12500), + REGULATOR_LINEAR_RANGE(712500, 0, 63, 12500), }; static const struct regulator_linear_range rk808_buck4_voltage_ranges[] = { -- cgit v1.1 From f2fc42b6ac31f4d808da7a9da460dd433a71e976 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Thu, 12 Jun 2014 22:30:34 +0530 Subject: mailbox: rename pl320-ipc specific mailbox.h The patch 30058677 "ARM / highbank: add support for pl320 IPC" added a pl320 IPC specific header file as a generic mailbox.h. This file has been renamed appropriately to allow the introduction of the generic mailbox API framework. Acked-by: Mark Langsdorf Cc: Rafael J. Wysocki Signed-off-by: Suman Anna Reviewed-by: Mark Brown Acked-by: Arnd Bergmann --- drivers/cpufreq/highbank-cpufreq.c | 2 +- drivers/mailbox/pl320-ipc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c index bf8902a..b464f29 100644 --- a/drivers/cpufreq/highbank-cpufreq.c +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #define HB_CPUFREQ_CHANGE_NOTE 0x80000001 diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c index d873cba..f3755e0 100644 --- a/drivers/mailbox/pl320-ipc.c +++ b/drivers/mailbox/pl320-ipc.c @@ -26,7 +26,7 @@ #include #include -#include +#include #define IPCMxSOURCE(m) ((m) * 0x40) #define IPCMxDSET(m) (((m) * 0x40) + 0x004) -- cgit v1.1 From 2b6d83e2b8b7de82331a6a1dcd64b51020a6031c Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Thu, 12 Jun 2014 22:31:19 +0530 Subject: mailbox: Introduce framework for mailbox Introduce common framework for client/protocol drivers and controller drivers of Inter-Processor-Communication (IPC). Client driver developers should have a look at include/linux/mailbox_client.h to understand the part of the API exposed to client drivers. Similarly controller driver developers should have a look at include/linux/mailbox_controller.h Reviewed-by: Mark Brown Signed-off-by: Jassi Brar --- drivers/mailbox/Makefile | 4 + drivers/mailbox/mailbox.c | 465 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 469 insertions(+) create mode 100644 drivers/mailbox/mailbox.c (limited to 'drivers') diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 6d184db..94ed7ce 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -1,3 +1,7 @@ +# Generic MAILBOX API + +obj-$(CONFIG_MAILBOX) += mailbox.o + obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c new file mode 100644 index 0000000..afcb430 --- /dev/null +++ b/drivers/mailbox/mailbox.c @@ -0,0 +1,465 @@ +/* + * Mailbox: Common code for Mailbox controllers and users + * + * Copyright (C) 2013-2014 Linaro Ltd. + * Author: Jassi Brar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TXDONE_BY_IRQ BIT(0) /* controller has remote RTR irq */ +#define TXDONE_BY_POLL BIT(1) /* controller can read status of last TX */ +#define TXDONE_BY_ACK BIT(2) /* S/W ACK recevied by Client ticks the TX */ + +static LIST_HEAD(mbox_cons); +static DEFINE_MUTEX(con_mutex); + +static int add_to_rbuf(struct mbox_chan *chan, void *mssg) +{ + int idx; + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + + /* See if there is any space left */ + if (chan->msg_count == MBOX_TX_QUEUE_LEN) { + spin_unlock_irqrestore(&chan->lock, flags); + return -ENOBUFS; + } + + idx = chan->msg_free; + chan->msg_data[idx] = mssg; + chan->msg_count++; + + if (idx == MBOX_TX_QUEUE_LEN - 1) + chan->msg_free = 0; + else + chan->msg_free++; + + spin_unlock_irqrestore(&chan->lock, flags); + + return idx; +} + +static void msg_submit(struct mbox_chan *chan) +{ + unsigned count, idx; + unsigned long flags; + void *data; + int err; + + spin_lock_irqsave(&chan->lock, flags); + + if (!chan->msg_count || chan->active_req) + goto exit; + + count = chan->msg_count; + idx = chan->msg_free; + if (idx >= count) + idx -= count; + else + idx += MBOX_TX_QUEUE_LEN - count; + + data = chan->msg_data[idx]; + + /* Try to submit a message to the MBOX controller */ + err = chan->mbox->ops->send_data(chan, data); + if (!err) { + chan->active_req = data; + chan->msg_count--; + } +exit: + spin_unlock_irqrestore(&chan->lock, flags); +} + +static void tx_tick(struct mbox_chan *chan, int r) +{ + unsigned long flags; + void *mssg; + + spin_lock_irqsave(&chan->lock, flags); + mssg = chan->active_req; + chan->active_req = NULL; + spin_unlock_irqrestore(&chan->lock, flags); + + /* Submit next message */ + msg_submit(chan); + + /* Notify the client */ + if (mssg && chan->cl->tx_done) + chan->cl->tx_done(chan->cl, mssg, r); + + if (chan->cl->tx_block) + complete(&chan->tx_complete); +} + +static void poll_txdone(unsigned long data) +{ + struct mbox_controller *mbox = (struct mbox_controller *)data; + bool txdone, resched = false; + int i; + + for (i = 0; i < mbox->num_chans; i++) { + struct mbox_chan *chan = &mbox->chans[i]; + + if (chan->active_req && chan->cl) { + resched = true; + txdone = chan->mbox->ops->last_tx_done(chan); + if (txdone) + tx_tick(chan, 0); + } + } + + if (resched) + mod_timer(&mbox->poll, jiffies + + msecs_to_jiffies(mbox->txpoll_period)); +} + +/** + * mbox_chan_received_data - A way for controller driver to push data + * received from remote to the upper layer. + * @chan: Pointer to the mailbox channel on which RX happened. + * @mssg: Client specific message typecasted as void * + * + * After startup and before shutdown any data received on the chan + * is passed on to the API via atomic mbox_chan_received_data(). + * The controller should ACK the RX only after this call returns. + */ +void mbox_chan_received_data(struct mbox_chan *chan, void *mssg) +{ + /* No buffering the received data */ + if (chan->cl->rx_callback) + chan->cl->rx_callback(chan->cl, mssg); +} +EXPORT_SYMBOL_GPL(mbox_chan_received_data); + +/** + * mbox_chan_txdone - A way for controller driver to notify the + * framework that the last TX has completed. + * @chan: Pointer to the mailbox chan on which TX happened. + * @r: Status of last TX - OK or ERROR + * + * The controller that has IRQ for TX ACK calls this atomic API + * to tick the TX state machine. It works only if txdone_irq + * is set by the controller. + */ +void mbox_chan_txdone(struct mbox_chan *chan, int r) +{ + if (unlikely(!(chan->txdone_method & TXDONE_BY_IRQ))) { + dev_err(chan->mbox->dev, + "Controller can't run the TX ticker\n"); + return; + } + + tx_tick(chan, r); +} +EXPORT_SYMBOL_GPL(mbox_chan_txdone); + +/** + * mbox_client_txdone - The way for a client to run the TX state machine. + * @chan: Mailbox channel assigned to this client. + * @r: Success status of last transmission. + * + * The client/protocol had received some 'ACK' packet and it notifies + * the API that the last packet was sent successfully. This only works + * if the controller can't sense TX-Done. + */ +void mbox_client_txdone(struct mbox_chan *chan, int r) +{ + if (unlikely(!(chan->txdone_method & TXDONE_BY_ACK))) { + dev_err(chan->mbox->dev, "Client can't run the TX ticker\n"); + return; + } + + tx_tick(chan, r); +} +EXPORT_SYMBOL_GPL(mbox_client_txdone); + +/** + * mbox_client_peek_data - A way for client driver to pull data + * received from remote by the controller. + * @chan: Mailbox channel assigned to this client. + * + * A poke to controller driver for any received data. + * The data is actually passed onto client via the + * mbox_chan_received_data() + * The call can be made from atomic context, so the controller's + * implementation of peek_data() must not sleep. + * + * Return: True, if controller has, and is going to push after this, + * some data. + * False, if controller doesn't have any data to be read. + */ +bool mbox_client_peek_data(struct mbox_chan *chan) +{ + if (chan->mbox->ops->peek_data) + return chan->mbox->ops->peek_data(chan); + + return false; +} +EXPORT_SYMBOL_GPL(mbox_client_peek_data); + +/** + * mbox_send_message - For client to submit a message to be + * sent to the remote. + * @chan: Mailbox channel assigned to this client. + * @mssg: Client specific message typecasted. + * + * For client to submit data to the controller destined for a remote + * processor. If the client had set 'tx_block', the call will return + * either when the remote receives the data or when 'tx_tout' millisecs + * run out. + * In non-blocking mode, the requests are buffered by the API and a + * non-negative token is returned for each queued request. If the request + * is not queued, a negative token is returned. Upon failure or successful + * TX, the API calls 'tx_done' from atomic context, from which the client + * could submit yet another request. + * The pointer to message should be preserved until it is sent + * over the chan, i.e, tx_done() is made. + * This function could be called from atomic context as it simply + * queues the data and returns a token against the request. + * + * Return: Non-negative integer for successful submission (non-blocking mode) + * or transmission over chan (blocking mode). + * Negative value denotes failure. + */ +int mbox_send_message(struct mbox_chan *chan, void *mssg) +{ + int t; + + if (!chan || !chan->cl) + return -EINVAL; + + t = add_to_rbuf(chan, mssg); + if (t < 0) { + dev_err(chan->mbox->dev, "Try increasing MBOX_TX_QUEUE_LEN\n"); + return t; + } + + msg_submit(chan); + + if (chan->txdone_method == TXDONE_BY_POLL) + poll_txdone((unsigned long)chan->mbox); + + if (chan->cl->tx_block && chan->active_req) { + unsigned long wait; + int ret; + + if (!chan->cl->tx_tout) /* wait forever */ + wait = msecs_to_jiffies(3600000); + else + wait = msecs_to_jiffies(chan->cl->tx_tout); + + ret = wait_for_completion_timeout(&chan->tx_complete, wait); + if (ret == 0) { + t = -EIO; + tx_tick(chan, -EIO); + } + } + + return t; +} +EXPORT_SYMBOL_GPL(mbox_send_message); + +/** + * mbox_request_channel - Request a mailbox channel. + * @cl: Identity of the client requesting the channel. + * @index: Index of mailbox specifier in 'mboxes' property. + * + * The Client specifies its requirements and capabilities while asking for + * a mailbox channel. It can't be called from atomic context. + * The channel is exclusively allocated and can't be used by another + * client before the owner calls mbox_free_channel. + * After assignment, any packet received on this channel will be + * handed over to the client via the 'rx_callback'. + * The framework holds reference to the client, so the mbox_client + * structure shouldn't be modified until the mbox_free_channel returns. + * + * Return: Pointer to the channel assigned to the client if successful. + * ERR_PTR for request failure. + */ +struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index) +{ + struct device *dev = cl->dev; + struct mbox_controller *mbox; + struct of_phandle_args spec; + struct mbox_chan *chan; + unsigned long flags; + int ret; + + if (!dev || !dev->of_node) { + pr_debug("%s: No owner device node\n", __func__); + return ERR_PTR(-ENODEV); + } + + mutex_lock(&con_mutex); + + if (of_parse_phandle_with_args(dev->of_node, "mboxes", + "#mbox-cells", index, &spec)) { + dev_dbg(dev, "%s: can't parse \"mboxes\" property\n", __func__); + mutex_unlock(&con_mutex); + return ERR_PTR(-ENODEV); + } + + chan = NULL; + list_for_each_entry(mbox, &mbox_cons, node) + if (mbox->dev->of_node == spec.np) { + chan = mbox->of_xlate(mbox, &spec); + break; + } + + of_node_put(spec.np); + + if (!chan || chan->cl || !try_module_get(mbox->dev->driver->owner)) { + dev_dbg(dev, "%s: mailbox not free\n", __func__); + mutex_unlock(&con_mutex); + return ERR_PTR(-EBUSY); + } + + spin_lock_irqsave(&chan->lock, flags); + chan->msg_free = 0; + chan->msg_count = 0; + chan->active_req = NULL; + chan->cl = cl; + init_completion(&chan->tx_complete); + + if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone) + chan->txdone_method |= TXDONE_BY_ACK; + + spin_unlock_irqrestore(&chan->lock, flags); + + ret = chan->mbox->ops->startup(chan); + if (ret) { + dev_err(dev, "Unable to startup the chan (%d)\n", ret); + mbox_free_channel(chan); + chan = ERR_PTR(ret); + } + + mutex_unlock(&con_mutex); + return chan; +} +EXPORT_SYMBOL_GPL(mbox_request_channel); + +/** + * mbox_free_channel - The client relinquishes control of a mailbox + * channel by this call. + * @chan: The mailbox channel to be freed. + */ +void mbox_free_channel(struct mbox_chan *chan) +{ + unsigned long flags; + + if (!chan || !chan->cl) + return; + + chan->mbox->ops->shutdown(chan); + + /* The queued TX requests are simply aborted, no callbacks are made */ + spin_lock_irqsave(&chan->lock, flags); + chan->cl = NULL; + chan->active_req = NULL; + if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK)) + chan->txdone_method = TXDONE_BY_POLL; + + module_put(chan->mbox->dev->driver->owner); + spin_unlock_irqrestore(&chan->lock, flags); +} +EXPORT_SYMBOL_GPL(mbox_free_channel); + +static struct mbox_chan * +of_mbox_index_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *sp) +{ + int ind = sp->args[0]; + + if (ind >= mbox->num_chans) + return NULL; + + return &mbox->chans[ind]; +} + +/** + * mbox_controller_register - Register the mailbox controller + * @mbox: Pointer to the mailbox controller. + * + * The controller driver registers its communication channels + */ +int mbox_controller_register(struct mbox_controller *mbox) +{ + int i, txdone; + + /* Sanity check */ + if (!mbox || !mbox->dev || !mbox->ops || !mbox->num_chans) + return -EINVAL; + + if (mbox->txdone_irq) + txdone = TXDONE_BY_IRQ; + else if (mbox->txdone_poll) + txdone = TXDONE_BY_POLL; + else /* It has to be ACK then */ + txdone = TXDONE_BY_ACK; + + if (txdone == TXDONE_BY_POLL) { + mbox->poll.function = &poll_txdone; + mbox->poll.data = (unsigned long)mbox; + init_timer(&mbox->poll); + } + + for (i = 0; i < mbox->num_chans; i++) { + struct mbox_chan *chan = &mbox->chans[i]; + + chan->cl = NULL; + chan->mbox = mbox; + chan->txdone_method = txdone; + spin_lock_init(&chan->lock); + } + + if (!mbox->of_xlate) + mbox->of_xlate = of_mbox_index_xlate; + + mutex_lock(&con_mutex); + list_add_tail(&mbox->node, &mbox_cons); + mutex_unlock(&con_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(mbox_controller_register); + +/** + * mbox_controller_unregister - Unregister the mailbox controller + * @mbox: Pointer to the mailbox controller. + */ +void mbox_controller_unregister(struct mbox_controller *mbox) +{ + int i; + + if (!mbox) + return; + + mutex_lock(&con_mutex); + + list_del(&mbox->node); + + for (i = 0; i < mbox->num_chans; i++) + mbox_free_channel(&mbox->chans[i]); + + if (mbox->txdone_poll) + del_timer_sync(&mbox->poll); + + mutex_unlock(&con_mutex); +} +EXPORT_SYMBOL_GPL(mbox_controller_unregister); -- cgit v1.1 From 62d3ab49b8a5438d11a11605ea1a6d2fe0118f32 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 6 Oct 2014 16:40:13 -0700 Subject: target/file: fix inclusive vfs_fsync_range() end Both of the file target's calls to vfs_fsync_range() got the end offset off by one. The range is inclusive, not exclusive. It would sync a bit more data than was required. The sync path already tested the length of the range and fell back to LLONG_MAX so I copied that pattern in the rw path. This is untested. I found the errors by inspection while following other code. Signed-off-by: Zach Brown Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_file.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index ab2c53b..72c83d9 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -415,7 +415,7 @@ fd_execute_sync_cache(struct se_cmd *cmd) } else { start = cmd->t_task_lba * dev->dev_attrib.block_size; if (cmd->data_length) - end = start + cmd->data_length; + end = start + cmd->data_length - 1; else end = LLONG_MAX; } @@ -680,7 +680,12 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, struct fd_dev *fd_dev = FD_DEV(dev); loff_t start = cmd->t_task_lba * dev->dev_attrib.block_size; - loff_t end = start + cmd->data_length; + loff_t end; + + if (cmd->data_length) + end = start + cmd->data_length - 1; + else + end = LLONG_MAX; vfs_fsync_range(fd_dev->fd_file, start, end, 1); } -- cgit v1.1 From 1180e20606fd7c5d76dc5b2a1594fa51ba5a0f31 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Tue, 7 Oct 2014 18:02:52 -0300 Subject: drm/i915: properly reenable gen8 pipe IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were missing the pipe B/C vblank bits! Take a look at gen8_de_irq_postinstall for a comparison. This should fix a bunch of IGT tests. There are a few more things we could improve on this code, but this should be the minimal fix to unblock us. v2: s/extra_iir/extra_ier/ because IIR doesn't make sense (Ville) Bugzilla:https://bugs.freedesktop.org/show_bug.cgi?id=83640 Testcase: igt/* Cc: stable@vger.kernel.org Signed-off-by: Paulo Zanoni Reviewed-by: Ville Syrjälä Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_irq.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 3201986..54bf5470 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -3458,12 +3458,13 @@ static void gen8_irq_reset(struct drm_device *dev) void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv) { unsigned long irqflags; + uint32_t extra_ier = GEN8_PIPE_VBLANK | GEN8_PIPE_FIFO_UNDERRUN; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_B, dev_priv->de_irq_mask[PIPE_B], - ~dev_priv->de_irq_mask[PIPE_B]); + ~dev_priv->de_irq_mask[PIPE_B] | extra_ier); GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_C, dev_priv->de_irq_mask[PIPE_C], - ~dev_priv->de_irq_mask[PIPE_C]); + ~dev_priv->de_irq_mask[PIPE_C] | extra_ier); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } -- cgit v1.1 From 9c6de47d53a3ce8df1642ae67823688eb98a190a Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Wed, 8 Oct 2014 13:51:34 -0500 Subject: spi: dw: Initialize of_node to discover DT node children The of_node element must be initialized to enable discovery of node children which takes place in the of_register_spi_devices() function. Signed-off-by: Thor Thayer Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 7292158..72e12ba 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -669,6 +669,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->cleanup = dw_spi_cleanup; master->transfer_one_message = dw_spi_transfer_one_message; master->max_speed_hz = dws->max_freq; + master->dev.of_node = dev->of_node; /* Basic HW init */ spi_hw_init(dws); -- cgit v1.1 From 26bb0e9a1a938ec98ee07aa76533f1a711fba706 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Wed, 24 Sep 2014 10:27:10 +0200 Subject: thermal: step_wise: fix: Prevent from binary overflow when trend is dropping It turns out that some boards can have instance->lower greater than 0 and when thermal trend is dropping it results with next_target equal to -1. Since the next_target is defined as unsigned long it is interpreted as 0xFFFFFFFF and larger than instance->upper. As a result the next_target is set to instance->upper which ramps up to maximal cooling device target when the temperature is steadily decreasing. Signed-off-by: Lukasz Majewski Signed-off-by: Zhang Rui --- drivers/thermal/step_wise.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index f251521..6705a0d 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -76,7 +76,7 @@ static unsigned long get_target_state(struct thermal_instance *instance, next_target = instance->upper; break; case THERMAL_TREND_DROPPING: - if (cur_state == instance->lower) { + if (cur_state <= instance->lower) { if (!throttle) next_target = THERMAL_NO_TARGET; } else { -- cgit v1.1 From 75d7ed3b9e7cb79a3b0e1f417fb674d54b4fc668 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 4 Oct 2014 08:50:21 -0300 Subject: iio: adc: mxs-lradc: Disable the clock on probe failure We should disable lradc->clk in the case of errors in the probe function. Signed-off-by: Fabio Estevam Reviewed-by: Marek Vasut Signed-off-by: Jonathan Cameron Cc: --- drivers/staging/iio/adc/mxs-lradc.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index 468327f..d343611 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -1565,14 +1565,16 @@ static int mxs_lradc_probe(struct platform_device *pdev) /* Grab all IRQ sources */ for (i = 0; i < of_cfg->irq_count; i++) { lradc->irq[i] = platform_get_irq(pdev, i); - if (lradc->irq[i] < 0) - return lradc->irq[i]; + if (lradc->irq[i] < 0) { + ret = lradc->irq[i]; + goto err_clk; + } ret = devm_request_irq(dev, lradc->irq[i], mxs_lradc_handle_irq, 0, of_cfg->irq_name[i], iio); if (ret) - return ret; + goto err_clk; } lradc->vref_mv = of_cfg->vref_mv; @@ -1594,7 +1596,7 @@ static int mxs_lradc_probe(struct platform_device *pdev) &mxs_lradc_trigger_handler, &mxs_lradc_buffer_ops); if (ret) - return ret; + goto err_clk; ret = mxs_lradc_trigger_init(iio); if (ret) @@ -1649,6 +1651,8 @@ err_dev: mxs_lradc_trigger_remove(iio); err_trig: iio_triggered_buffer_cleanup(iio); +err_clk: + clk_disable_unprepare(lradc->clk); return ret; } -- cgit v1.1 From 083bf668cb70e47b84db64856606e94beac87f01 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Fri, 14 Mar 2014 14:06:25 +0800 Subject: ACPI: make acpi_create_platform_device() an external API Signed-off-by: Zhang Rui --- drivers/acpi/acpi_platform.c | 1 + drivers/acpi/internal.h | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 2bf9082..a3c89a1 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -113,3 +113,4 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) kfree(resources); return pdev; } +EXPORT_SYMBOL_GPL(acpi_create_platform_device); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index de47f9f..f221d1e 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -169,13 +169,6 @@ static inline void suspend_nvs_restore(void) {} #endif /*-------------------------------------------------------------------------- - Platform bus support - -------------------------------------------------------------------------- */ -struct platform_device; - -struct platform_device *acpi_create_platform_device(struct acpi_device *adev); - -/*-------------------------------------------------------------------------- Video -------------------------------------------------------------------------- */ #if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) -- cgit v1.1 From e3ec483a7e24c6ebb5eb763ee56c65c239701066 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sun, 16 Mar 2014 21:34:16 +0800 Subject: ACPI: add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package() Add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package(), so that we can use this helper for more cases like _ART/_TRT. Signed-off-by: Zhang Rui --- drivers/acpi/utils.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 07c8c5a..1ed7aba9 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -149,6 +149,21 @@ acpi_extract_package(union acpi_object *package, break; } break; + case ACPI_TYPE_LOCAL_REFERENCE: + switch (format_string[i]) { + case 'R': + size_required += sizeof(void *); + tail_offset += sizeof(void *); + break; + default: + printk(KERN_WARNING PREFIX "Invalid package element" + " [%d] got reference," + " expecting [%c]\n", + i, format_string[i]); + return AE_BAD_DATA; + break; + } + break; case ACPI_TYPE_PACKAGE: default: @@ -247,7 +262,18 @@ acpi_extract_package(union acpi_object *package, break; } break; - + case ACPI_TYPE_LOCAL_REFERENCE: + switch (format_string[i]) { + case 'R': + *(void **)head = + (void *)element->reference.handle; + head += sizeof(void *); + break; + default: + /* Should never get here */ + break; + } + break; case ACPI_TYPE_PACKAGE: /* TBD: handle nested packages... */ default: -- cgit v1.1 From 816cab931f288c92a3404b1b984576f4822b0445 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Fri, 14 Mar 2014 12:45:05 +0800 Subject: Thermal: introduce int3400 thermal driver Introduce int3400 thermal driver. And make INT3400 driver enumerate the other int340x thermal components shown in _ART/_TRT. Signed-off-by: Zhang Rui --- drivers/acpi/int340x_thermal.c | 2 +- drivers/thermal/Kconfig | 2 +- drivers/thermal/Makefile | 1 + drivers/thermal/int340x_thermal/Makefile | 1 + drivers/thermal/int340x_thermal/int3400_thermal.c | 245 ++++++++++++++++++++++ 5 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 drivers/thermal/int340x_thermal/Makefile create mode 100644 drivers/thermal/int340x_thermal/int3400_thermal.c (limited to 'drivers') diff --git a/drivers/acpi/int340x_thermal.c b/drivers/acpi/int340x_thermal.c index 2103bb6..a27d31d 100644 --- a/drivers/acpi/int340x_thermal.c +++ b/drivers/acpi/int340x_thermal.c @@ -33,7 +33,7 @@ static const struct acpi_device_id int340x_thermal_device_ids[] = { static int int340x_thermal_handler_attach(struct acpi_device *adev, const struct acpi_device_id *id) { -#ifdef CONFIG_INT340X_THERMAL +#if defined(CONFIG_INT340X_THERMAL) || defined(CONFIG_INT340X_THERMAL_MODULE) if (id->driver_data == DO_ENUMERATION) acpi_create_platform_device(adev); #endif diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 2ff7416..6f5a87a 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -235,7 +235,7 @@ config INTEL_SOC_DTS_THERMAL was set by the driver based on the TJ MAX temperature. config INT340X_THERMAL - bool + tristate "ACPI INT340X thermal drivers" depends on X86 && ACPI help Newer laptops and tablets that use ACPI may have thermal sensors and diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 31e232f..216503e 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -32,4 +32,5 @@ obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o +obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ obj-$(CONFIG_ST_THERMAL) += st/ diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile new file mode 100644 index 0000000..e10a53b --- /dev/null +++ b/drivers/thermal/int340x_thermal/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c new file mode 100644 index 0000000..308c185 --- /dev/null +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c @@ -0,0 +1,245 @@ +/* + * INT3400 thermal driver + * + * Copyright (C) 2014, Intel Corporation + * Authors: Zhang Rui + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include + +struct art { + acpi_handle source; + acpi_handle target; + u64 weight; + u64 ac0_max; + u64 ac1_max; + u64 ac2_max; + u64 ac3_max; + u64 ac4_max; + u64 ac5_max; + u64 ac6_max; + u64 ac7_max; + u64 ac8_max; + u64 ac9_max; +}; + +struct trt { + acpi_handle source; + acpi_handle target; + u64 influence; + u64 sampling_period; + u64 reverved1; + u64 reverved2; + u64 reverved3; + u64 reverved4; +}; + +struct int3400_thermal_priv { + struct acpi_device *adev; + int art_count; + struct art *arts; + int trt_count; + struct trt *trts; +}; + +static int parse_art(struct int3400_thermal_priv *priv) +{ + acpi_handle handle = priv->adev->handle; + acpi_status status; + int result = 0; + int i; + struct acpi_device *adev; + union acpi_object *p; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer element = { 0, NULL }; + struct acpi_buffer art_format = { + sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" }; + + if (!acpi_has_method(handle, "_ART")) + return 0; + + status = acpi_evaluate_object(handle, "_ART", NULL, &buffer); + if (ACPI_FAILURE(status)) + return -ENODEV; + + p = buffer.pointer; + if (!p || (p->type != ACPI_TYPE_PACKAGE)) { + pr_err("Invalid _ART data\n"); + result = -EFAULT; + goto end; + } + + /* ignore p->package.elements[0], as this is _ART Revision field */ + priv->art_count = p->package.count - 1; + priv->arts = kzalloc(sizeof(struct art) * priv->art_count, GFP_KERNEL); + if (!priv->arts) { + result = -ENOMEM; + goto end; + } + + for (i = 0; i < priv->art_count; i++) { + struct art *art = &(priv->arts[i]); + + element.length = sizeof(struct art); + element.pointer = art; + + status = acpi_extract_package(&(p->package.elements[i + 1]), + &art_format, &element); + if (ACPI_FAILURE(status)) { + pr_err("Invalid _ART data"); + result = -EFAULT; + kfree(priv->arts); + goto end; + } + result = acpi_bus_get_device(art->source, &adev); + if (!result) + acpi_create_platform_device(adev, NULL); + else + pr_warn("Failed to get source ACPI device\n"); + result = acpi_bus_get_device(art->target, &adev); + if (!result) + acpi_create_platform_device(adev, NULL); + else + pr_warn("Failed to get source ACPI device\n"); + } +end: + kfree(buffer.pointer); + return result; +} + +static int parse_trt(struct int3400_thermal_priv *priv) +{ + acpi_handle handle = priv->adev->handle; + acpi_status status; + int result = 0; + int i; + struct acpi_device *adev; + union acpi_object *p; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer element = { 0, NULL }; + struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" }; + + if (!acpi_has_method(handle, "_TRT")) + return 0; + + status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer); + if (ACPI_FAILURE(status)) + return -ENODEV; + + p = buffer.pointer; + if (!p || (p->type != ACPI_TYPE_PACKAGE)) { + pr_err("Invalid _TRT data\n"); + result = -EFAULT; + goto end; + } + + priv->trt_count = p->package.count; + priv->trts = kzalloc(sizeof(struct trt) * priv->trt_count, GFP_KERNEL); + if (!priv->trts) { + result = -ENOMEM; + goto end; + } + + for (i = 0; i < priv->trt_count; i++) { + struct trt *trt = &(priv->trts[i]); + + element.length = sizeof(struct trt); + element.pointer = trt; + + status = acpi_extract_package(&(p->package.elements[i]), + &trt_format, &element); + if (ACPI_FAILURE(status)) { + pr_err("Invalid _ART data"); + result = -EFAULT; + kfree(priv->trts); + goto end; + } + + result = acpi_bus_get_device(trt->source, &adev); + if (!result) + acpi_create_platform_device(adev, NULL); + else + pr_warn("Failed to get source ACPI device\n"); + result = acpi_bus_get_device(trt->target, &adev); + if (!result) + acpi_create_platform_device(adev, NULL); + else + pr_warn("Failed to get target ACPI device\n"); + } +end: + kfree(buffer.pointer); + return result; +} + +static int int3400_thermal_probe(struct platform_device *pdev) +{ + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); + struct int3400_thermal_priv *priv; + int result; + + if (!adev) + return -ENODEV; + + priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->adev = adev; + + result = parse_art(priv); + if (result) + goto free_priv; + + result = parse_trt(priv); + if (result) + goto free_art; + + platform_set_drvdata(pdev, priv); + + return 0; +free_art: + kfree(priv->arts); +free_priv: + kfree(priv); + return result; +} + +static int int3400_thermal_remove(struct platform_device *pdev) +{ + struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); + + kfree(priv->trts); + kfree(priv->arts); + kfree(priv); + return 0; +} + +static const struct acpi_device_id int3400_thermal_match[] = { + {"INT3400", 0}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, int3400_thermal_match); + +static struct platform_driver int3400_thermal_driver = { + .probe = int3400_thermal_probe, + .remove = int3400_thermal_remove, + .driver = { + .name = "int3400 thermal", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(int3400_thermal_match), + }, +}; + +module_platform_driver(int3400_thermal_driver); + +MODULE_DESCRIPTION("INT3400 Thermal driver"); +MODULE_AUTHOR("Zhang Rui "); +MODULE_LICENSE("GPL"); -- cgit v1.1 From c5738dddc01be49cf6712f886371177e8df36291 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sun, 23 Mar 2014 23:34:26 +0800 Subject: Thermal: int3400 thermal: add capability to detect supporting UUIDs Signed-off-by: Zhang Rui --- drivers/thermal/int340x_thermal/int3400_thermal.c | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'drivers') diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c index 308c185..65c63ba 100644 --- a/drivers/thermal/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c @@ -41,14 +41,79 @@ struct trt { u64 reverved4; }; +enum int3400_thermal_uuid { + INT3400_THERMAL_PASSIVE_1, + INT3400_THERMAL_PASSIVE_2, + INT3400_THERMAL_ACTIVE, + INT3400_THERMAL_CRITICAL, + INT3400_THERMAL_COOLING_MODE, + INT3400_THERMAL_MAXIMUM_UUID, +}; + +static u8 *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = { + "42A441D6-AE6A-462b-A84B-4A8CE79027D3", + "9E04115A-AE87-4D1C-9500-0F3E340BFE75", + "3A95C389-E4B8-4629-A526-C52C88626BAE", + "97C68AE7-15FA-499c-B8C9-5DA81D606E0A", + "16CAF1B7-DD38-40ed-B1C1-1B8A1913D531", +}; + struct int3400_thermal_priv { struct acpi_device *adev; int art_count; struct art *arts; int trt_count; struct trt *trts; + u8 uuid_bitmap; }; +static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *obja, *objb; + int i, j; + int result = 0; + acpi_status status; + + status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf); + if (ACPI_FAILURE(status)) + return -ENODEV; + + obja = (union acpi_object *)buf.pointer; + if (obja->type != ACPI_TYPE_PACKAGE) { + result = -EINVAL; + goto end; + } + + for (i = 0; i < obja->package.count; i++) { + objb = &obja->package.elements[i]; + if (objb->type != ACPI_TYPE_BUFFER) { + result = -EINVAL; + goto end; + } + + /* UUID must be 16 bytes */ + if (objb->buffer.length != 16) { + result = -EINVAL; + goto end; + } + + for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) { + u8 uuid[16]; + + acpi_str_to_uuid(int3400_thermal_uuids[j], uuid); + if (!strncmp(uuid, objb->buffer.pointer, 16)) { + priv->uuid_bitmap |= (1 << j); + break; + } + } + } + +end: + kfree(buf.pointer); + return result; +} + static int parse_art(struct int3400_thermal_priv *priv) { acpi_handle handle = priv->adev->handle; @@ -193,6 +258,10 @@ static int int3400_thermal_probe(struct platform_device *pdev) priv->adev = adev; + result = int3400_thermal_get_uuids(priv); + if (result) + goto free_priv; + result = parse_art(priv); if (result) goto free_priv; -- cgit v1.1 From 0ab15365ff38c8433f4d0cc2d11a1184e2c991cf Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sun, 23 Mar 2014 23:37:32 +0800 Subject: Thermal: int3400 thermal: register to thermal framework Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 1 + drivers/thermal/int340x_thermal/int3400_thermal.c | 103 ++++++++++++++++++++++ 2 files changed, 104 insertions(+) (limited to 'drivers') diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 6f5a87a..b34c5f5 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -237,6 +237,7 @@ config INTEL_SOC_DTS_THERMAL config INT340X_THERMAL tristate "ACPI INT340X thermal drivers" depends on X86 && ACPI + select THERMAL_GOV_USER_SPACE help Newer laptops and tablets that use ACPI may have thermal sensors and other devices with thermal control capabilities outside the core diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c index 65c63ba..9104b4f 100644 --- a/drivers/thermal/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c @@ -13,6 +13,7 @@ #include #include #include +#include struct art { acpi_handle source; @@ -60,6 +61,8 @@ static u8 *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = { struct int3400_thermal_priv { struct acpi_device *adev; + struct thermal_zone_device *thermal; + int mode; int art_count; struct art *arts; int trt_count; @@ -114,6 +117,36 @@ end: return result; } +static int int3400_thermal_run_osc(acpi_handle handle, + enum int3400_thermal_uuid uuid, bool enable) +{ + u32 ret, buf[2]; + acpi_status status; + int result = 0; + struct acpi_osc_context context = { + .uuid_str = int3400_thermal_uuids[uuid], + .rev = 1, + .cap.length = 8, + }; + + buf[OSC_QUERY_DWORD] = 0; + buf[OSC_SUPPORT_DWORD] = enable; + + context.cap.pointer = buf; + + status = acpi_run_osc(handle, &context); + if (ACPI_SUCCESS(status)) { + ret = *((u32 *)(context.ret.pointer + 4)); + if (ret != enable) + result = -EPERM; + } else + result = -EPERM; + + kfree(context.ret.pointer); + return result; +} + + static int parse_art(struct int3400_thermal_priv *priv) { acpi_handle handle = priv->adev->handle; @@ -243,6 +276,61 @@ end: return result; } +static int int3400_thermal_get_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + *temp = 20 * 1000; /* faked temp sensor with 20C */ + return 0; +} + +static int int3400_thermal_get_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode *mode) +{ + struct int3400_thermal_priv *priv = thermal->devdata; + + if (!priv) + return -EINVAL; + + *mode = priv->mode; + + return 0; +} + +static int int3400_thermal_set_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode mode) +{ + struct int3400_thermal_priv *priv = thermal->devdata; + bool enable; + int result = 0; + + if (!priv) + return -EINVAL; + + if (mode == THERMAL_DEVICE_ENABLED) + enable = true; + else if (mode == THERMAL_DEVICE_DISABLED) + enable = false; + else + return -EINVAL; + + if (enable != priv->mode) { + priv->mode = enable; + /* currently, only PASSIVE COOLING is supported */ + result = int3400_thermal_run_osc(priv->adev->handle, + INT3400_THERMAL_PASSIVE_1, enable); + } + return result; +} + +static struct thermal_zone_device_ops int3400_thermal_ops = { + .get_temp = int3400_thermal_get_temp, +}; + +static struct thermal_zone_params int3400_thermal_params = { + .governor_name = "user_space", + .no_hwmon = true, +}; + static int int3400_thermal_probe(struct platform_device *pdev) { struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); @@ -272,7 +360,21 @@ static int int3400_thermal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); + if (priv->uuid_bitmap & 1 << INT3400_THERMAL_PASSIVE_1) { + int3400_thermal_ops.get_mode = int3400_thermal_get_mode; + int3400_thermal_ops.set_mode = int3400_thermal_set_mode; + } + priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0, + priv, &int3400_thermal_ops, + &int3400_thermal_params, 0, 0); + if (IS_ERR(priv->thermal)) { + result = PTR_ERR(priv->thermal); + goto free_trt; + } + return 0; +free_trt: + kfree(priv->trts); free_art: kfree(priv->arts); free_priv: @@ -284,6 +386,7 @@ static int int3400_thermal_remove(struct platform_device *pdev) { struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); + thermal_zone_device_unregister(priv->thermal); kfree(priv->trts); kfree(priv->arts); kfree(priv); -- cgit v1.1 From bd6ad24342c3bc0476ba3f0b647c5ba0a71b45b4 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 19 Nov 2013 14:25:58 +0800 Subject: ACPI / fan: remove unused macro The _COMPONENT, ACPI_MODULE_NAME(name) and ACPI_FAN_FILE_STATE are not used anywhere so remove them. Signed-off-by: Aaron Lu Signed-off-by: Zhang Rui --- drivers/acpi/fan.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 8acf53e..562d5f3 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -34,10 +34,6 @@ #define PREFIX "ACPI: " #define ACPI_FAN_CLASS "fan" -#define ACPI_FAN_FILE_STATE "state" - -#define _COMPONENT ACPI_FAN_COMPONENT -ACPI_MODULE_NAME("fan"); MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI Fan Driver"); -- cgit v1.1 From 8dd41f78adebb57909cccb0272e74c79e38b5238 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 19 Nov 2013 15:21:24 +0800 Subject: ACPI / fan: remove no need check for device pointer The device pointer will not be NULL in the PM callback and ACPI driver's add/remove callback, so checking NULL for them isn't necessary. Signed-off-by: Aaron Lu Signed-off-by: Zhang Rui --- drivers/acpi/fan.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 562d5f3..df861bb 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -131,9 +131,6 @@ static int acpi_fan_add(struct acpi_device *device) int result = 0; struct thermal_cooling_device *cdev; - if (!device) - return -EINVAL; - strcpy(acpi_device_name(device), "Fan"); strcpy(acpi_device_class(device), ACPI_FAN_CLASS); @@ -177,14 +174,7 @@ end: static int acpi_fan_remove(struct acpi_device *device) { - struct thermal_cooling_device *cdev; - - if (!device) - return -EINVAL; - - cdev = acpi_driver_data(device); - if (!cdev) - return -EINVAL; + struct thermal_cooling_device *cdev = acpi_driver_data(device); sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); sysfs_remove_link(&cdev->device.kobj, "device"); @@ -196,9 +186,6 @@ static int acpi_fan_remove(struct acpi_device *device) #ifdef CONFIG_PM_SLEEP static int acpi_fan_suspend(struct device *dev) { - if (!dev) - return -EINVAL; - acpi_bus_set_power(to_acpi_device(dev)->handle, ACPI_STATE_D0); return AE_OK; @@ -208,9 +195,6 @@ static int acpi_fan_resume(struct device *dev) { int result; - if (!dev) - return -EINVAL; - result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL); if (result) printk(KERN_ERR PREFIX "Error updating fan power state\n"); -- cgit v1.1 From 2bb3a2bf9939f3361e25045f4ef7b136b864c3b8 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 19 Nov 2013 15:43:52 +0800 Subject: ACPI / fan: use acpi_device_xxx_power instead of acpi_bus equivelant When we have the acpi_device pointer, there is no need to pass the device's handle to the acpi_bus_xxx_power functions to get/set/update the device's power state, instead, use the acpi_device_xxx_power functions directly. To make this happen for fan module, export acpi_device_update_power. Signed-off-by: Aaron Lu Signed-off-by: Zhang Rui --- drivers/acpi/device_pm.c | 1 + drivers/acpi/fan.c | 10 +++++----- drivers/acpi/internal.h | 2 -- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 67075f8..9177547 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -343,6 +343,7 @@ int acpi_device_update_power(struct acpi_device *device, int *state_p) return 0; } +EXPORT_SYMBOL_GPL(acpi_device_update_power); int acpi_bus_update_power(acpi_handle handle, int *state_p) { diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index df861bb..fff9696 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -92,7 +92,7 @@ static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long if (!device) return -EINVAL; - result = acpi_bus_update_power(device->handle, &acpi_state); + result = acpi_device_update_power(device, &acpi_state); if (result) return result; @@ -110,7 +110,7 @@ fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) if (!device || (state != 0 && state != 1)) return -EINVAL; - result = acpi_bus_set_power(device->handle, + result = acpi_device_set_power(device, state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); return result; @@ -134,7 +134,7 @@ static int acpi_fan_add(struct acpi_device *device) strcpy(acpi_device_name(device), "Fan"); strcpy(acpi_device_class(device), ACPI_FAN_CLASS); - result = acpi_bus_update_power(device->handle, NULL); + result = acpi_device_update_power(device, NULL); if (result) { printk(KERN_ERR PREFIX "Setting initial power state\n"); goto end; @@ -186,7 +186,7 @@ static int acpi_fan_remove(struct acpi_device *device) #ifdef CONFIG_PM_SLEEP static int acpi_fan_suspend(struct device *dev) { - acpi_bus_set_power(to_acpi_device(dev)->handle, ACPI_STATE_D0); + acpi_device_set_power(to_acpi_device(dev), ACPI_STATE_D0); return AE_OK; } @@ -195,7 +195,7 @@ static int acpi_fan_resume(struct device *dev) { int result; - result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL); + result = acpi_device_update_power(to_acpi_device(dev), NULL); if (result) printk(KERN_ERR PREFIX "Error updating fan power state\n"); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index f221d1e..447f6d6 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -104,8 +104,6 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state); int acpi_power_on_resources(struct acpi_device *device, int state); int acpi_power_transition(struct acpi_device *device, int state); -int acpi_device_update_power(struct acpi_device *device, int *state_p); - int acpi_wakeup_device_init(void); #ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC -- cgit v1.1 From 19593a1fb1f6718406afca5b867dab184289d406 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 19 Nov 2013 16:59:20 +0800 Subject: ACPI / fan: convert to platform driver Convert ACPI fan driver to a platform driver for the purpose of phasing out ACPI bus. Signed-off-by: Aaron Lu Signed-off-by: Zhang Rui --- drivers/acpi/fan.c | 62 ++++++++++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index fff9696..8a5b450 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -30,17 +30,14 @@ #include #include #include - -#define PREFIX "ACPI: " - -#define ACPI_FAN_CLASS "fan" +#include MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI Fan Driver"); MODULE_LICENSE("GPL"); -static int acpi_fan_add(struct acpi_device *device); -static int acpi_fan_remove(struct acpi_device *device); +static int acpi_fan_probe(struct platform_device *pdev); +static int acpi_fan_remove(struct platform_device *pdev); static const struct acpi_device_id fan_device_ids[] = { {"PNP0C0B", 0}, @@ -62,15 +59,14 @@ static struct dev_pm_ops acpi_fan_pm = { #define FAN_PM_OPS_PTR NULL #endif -static struct acpi_driver acpi_fan_driver = { - .name = "fan", - .class = ACPI_FAN_CLASS, - .ids = fan_device_ids, - .ops = { - .add = acpi_fan_add, - .remove = acpi_fan_remove, - }, - .drv.pm = FAN_PM_OPS_PTR, +static struct platform_driver acpi_fan_driver = { + .probe = acpi_fan_probe, + .remove = acpi_fan_remove, + .driver = { + .name = "acpi-fan", + .acpi_match_table = fan_device_ids, + .pm = FAN_PM_OPS_PTR, + }, }; /* thermal cooling device callbacks */ @@ -126,17 +122,15 @@ static const struct thermal_cooling_device_ops fan_cooling_ops = { Driver Interface -------------------------------------------------------------------------- */ -static int acpi_fan_add(struct acpi_device *device) +static int acpi_fan_probe(struct platform_device *pdev) { int result = 0; struct thermal_cooling_device *cdev; - - strcpy(acpi_device_name(device), "Fan"); - strcpy(acpi_device_class(device), ACPI_FAN_CLASS); + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); result = acpi_device_update_power(device, NULL); if (result) { - printk(KERN_ERR PREFIX "Setting initial power state\n"); + dev_err(&pdev->dev, "Setting initial power state\n"); goto end; } @@ -147,24 +141,24 @@ static int acpi_fan_add(struct acpi_device *device) goto end; } - dev_dbg(&device->dev, "registered as cooling_device%d\n", cdev->id); + dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id); - device->driver_data = cdev; - result = sysfs_create_link(&device->dev.kobj, + platform_set_drvdata(pdev, cdev); + result = sysfs_create_link(&pdev->dev.kobj, &cdev->device.kobj, "thermal_cooling"); if (result) - dev_err(&device->dev, "Failed to create sysfs link " + dev_err(&pdev->dev, "Failed to create sysfs link " "'thermal_cooling'\n"); result = sysfs_create_link(&cdev->device.kobj, - &device->dev.kobj, + &pdev->dev.kobj, "device"); if (result) - dev_err(&device->dev, "Failed to create sysfs link " + dev_err(&pdev->dev, "Failed to create sysfs link " "'device'\n"); - printk(KERN_INFO PREFIX "%s [%s] (%s)\n", + dev_info(&pdev->dev, "%s [%s] (%s)\n", acpi_device_name(device), acpi_device_bid(device), !device->power.state ? "on" : "off"); @@ -172,11 +166,11 @@ end: return result; } -static int acpi_fan_remove(struct acpi_device *device) +static int acpi_fan_remove(struct platform_device *pdev) { - struct thermal_cooling_device *cdev = acpi_driver_data(device); + struct thermal_cooling_device *cdev = platform_get_drvdata(pdev); - sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); + sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling"); sysfs_remove_link(&cdev->device.kobj, "device"); thermal_cooling_device_unregister(cdev); @@ -186,7 +180,7 @@ static int acpi_fan_remove(struct acpi_device *device) #ifdef CONFIG_PM_SLEEP static int acpi_fan_suspend(struct device *dev) { - acpi_device_set_power(to_acpi_device(dev), ACPI_STATE_D0); + acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0); return AE_OK; } @@ -195,12 +189,12 @@ static int acpi_fan_resume(struct device *dev) { int result; - result = acpi_device_update_power(to_acpi_device(dev), NULL); + result = acpi_device_update_power(ACPI_COMPANION(dev), NULL); if (result) - printk(KERN_ERR PREFIX "Error updating fan power state\n"); + dev_err(dev, "Error updating fan power state\n"); return result; } #endif -module_acpi_driver(acpi_fan_driver); +module_platform_driver(acpi_fan_driver); -- cgit v1.1 From 9519a6356cbf63b1f22a7a208385dc56092c8b7d Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 1 Apr 2014 15:50:27 +0800 Subject: ACPI / Fan: add ACPI 4.0 style fan support This patch adds support for ACPI 4.0 style fan, lacking part is: no support for 'Low Speed Notification Support', 'Fine Grain Control' is not used yet. It's not clear what to do on suspend/resume callback for 4.0 style ACPI fan, so it does nothing for now. Signed-off-by: Aaron Lu Signed-off-by: Zhang Rui --- drivers/acpi/fan.c | 268 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 241 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 8a5b450..f7d1c80 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -31,6 +31,7 @@ #include #include #include +#include MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI Fan Driver"); @@ -59,6 +60,29 @@ static struct dev_pm_ops acpi_fan_pm = { #define FAN_PM_OPS_PTR NULL #endif +struct acpi_fan_fps { + u64 control; + u64 trip_point; + u64 speed; + u64 noise_level; + u64 power; +}; + +struct acpi_fan_fif { + u64 revision; + u64 fine_grain_ctrl; + u64 step_size; + u64 low_speed_notification; +}; + +struct acpi_fan { + bool acpi4; + struct acpi_fan_fif fif; + struct acpi_fan_fps *fps; + int fps_count; + struct thermal_cooling_device *cdev; +}; + static struct platform_driver acpi_fan_driver = { .probe = acpi_fan_probe, .remove = acpi_fan_remove, @@ -73,21 +97,62 @@ static struct platform_driver acpi_fan_driver = { static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { - /* ACPI fan device only support two states: ON/OFF */ - *state = 1; + struct acpi_device *device = cdev->devdata; + struct acpi_fan *fan = acpi_driver_data(device); + + if (fan->acpi4) + *state = fan->fps_count - 1; + else + *state = 1; return 0; } -static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long - *state) +static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_fan *fan = acpi_driver_data(device); + union acpi_object *obj; + acpi_status status; + int control, i; + + status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, "Get fan state failed\n"); + return status; + } + + obj = buffer.pointer; + if (!obj || obj->type != ACPI_TYPE_PACKAGE || + obj->package.count != 3 || + obj->package.elements[1].type != ACPI_TYPE_INTEGER) { + dev_err(&device->dev, "Invalid _FST data\n"); + status = -EINVAL; + goto err; + } + + control = obj->package.elements[1].integer.value; + for (i = 0; i < fan->fps_count; i++) { + if (control == fan->fps[i].control) + break; + } + if (i == fan->fps_count) { + dev_dbg(&device->dev, "Invalid control value returned\n"); + status = -EINVAL; + goto err; + } + + *state = i; + +err: + kfree(obj); + return status; +} + +static int fan_get_state(struct acpi_device *device, unsigned long *state) { - struct acpi_device *device = cdev->devdata; int result; int acpi_state = ACPI_STATE_D0; - if (!device) - return -EINVAL; - result = acpi_device_update_power(device, &acpi_state); if (result) return result; @@ -97,21 +162,57 @@ static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long return 0; } -static int -fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long + *state) { struct acpi_device *device = cdev->devdata; - int result; + struct acpi_fan *fan = acpi_driver_data(device); + + if (fan->acpi4) + return fan_get_state_acpi4(device, state); + else + return fan_get_state(device, state); +} - if (!device || (state != 0 && state != 1)) +static int fan_set_state(struct acpi_device *device, unsigned long state) +{ + if (state != 0 && state != 1) return -EINVAL; - result = acpi_device_set_power(device, - state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); + return acpi_device_set_power(device, + state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); +} - return result; +static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state) +{ + struct acpi_fan *fan = acpi_driver_data(device); + acpi_status status; + + if (state >= fan->fps_count) + return -EINVAL; + + status = acpi_execute_simple_method(device->handle, "_FSL", + fan->fps[state].control); + if (ACPI_FAILURE(status)) { + dev_dbg(&device->dev, "Failed to set state by _FSL\n"); + return status; + } + + return 0; } +static int +fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_fan *fan = acpi_driver_data(device); + + if (fan->acpi4) + return fan_set_state_acpi4(device, state); + else + return fan_set_state(device, state); + } + static const struct thermal_cooling_device_ops fan_cooling_ops = { .get_max_state = fan_get_max_state, .get_cur_state = fan_get_cur_state, @@ -122,16 +223,125 @@ static const struct thermal_cooling_device_ops fan_cooling_ops = { Driver Interface -------------------------------------------------------------------------- */ +static bool acpi_fan_is_acpi4(struct acpi_device *device) +{ + return acpi_has_method(device->handle, "_FIF") && + acpi_has_method(device->handle, "_FPS") && + acpi_has_method(device->handle, "_FSL") && + acpi_has_method(device->handle, "_FST"); +} + +static int acpi_fan_get_fif(struct acpi_device *device) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_fan *fan = acpi_driver_data(device); + struct acpi_buffer format = { sizeof("NNNN"), "NNNN" }; + struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif }; + union acpi_object *obj; + acpi_status status; + + status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer); + if (ACPI_FAILURE(status)) + return status; + + obj = buffer.pointer; + if (!obj || obj->type != ACPI_TYPE_PACKAGE) { + dev_err(&device->dev, "Invalid _FIF data\n"); + status = -EINVAL; + goto err; + } + + status = acpi_extract_package(obj, &format, &fif); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, "Invalid _FIF element\n"); + status = -EINVAL; + } + +err: + kfree(obj); + return status; +} + +static int acpi_fan_speed_cmp(const void *a, const void *b) +{ + const struct acpi_fan_fps *fps1 = a; + const struct acpi_fan_fps *fps2 = b; + return fps1->speed - fps2->speed; +} + +static int acpi_fan_get_fps(struct acpi_device *device) +{ + struct acpi_fan *fan = acpi_driver_data(device); + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + int i; + + status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer); + if (ACPI_FAILURE(status)) + return status; + + obj = buffer.pointer; + if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) { + dev_err(&device->dev, "Invalid _FPS data\n"); + status = -EINVAL; + goto err; + } + + fan->fps_count = obj->package.count - 1; /* minus revision field */ + fan->fps = devm_kzalloc(&device->dev, + fan->fps_count * sizeof(struct acpi_fan_fps), + GFP_KERNEL); + if (!fan->fps) { + dev_err(&device->dev, "Not enough memory\n"); + status = -ENOMEM; + goto err; + } + for (i = 0; i < fan->fps_count; i++) { + struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" }; + struct acpi_buffer fps = { sizeof(fan->fps[i]), &fan->fps[i] }; + status = acpi_extract_package(&obj->package.elements[i + 1], + &format, &fps); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, "Invalid _FPS element\n"); + break; + } + } + + /* sort the state array according to fan speed in increase order */ + sort(fan->fps, fan->fps_count, sizeof(*fan->fps), + acpi_fan_speed_cmp, NULL); + +err: + kfree(obj); + return status; +} + static int acpi_fan_probe(struct platform_device *pdev) { int result = 0; struct thermal_cooling_device *cdev; + struct acpi_fan *fan; struct acpi_device *device = ACPI_COMPANION(&pdev->dev); - result = acpi_device_update_power(device, NULL); - if (result) { - dev_err(&pdev->dev, "Setting initial power state\n"); - goto end; + fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL); + if (!fan) { + dev_err(&device->dev, "No memory for fan\n"); + return -ENOMEM; + } + device->driver_data = fan; + platform_set_drvdata(pdev, fan); + + if (acpi_fan_is_acpi4(device)) { + if (acpi_fan_get_fif(device) || acpi_fan_get_fps(device)) + goto end; + fan->acpi4 = true; + } else { + result = acpi_device_update_power(device, NULL); + if (result) { + dev_err(&device->dev, "Setting initial power state\n"); + goto end; + } } cdev = thermal_cooling_device_register("Fan", device, @@ -143,7 +353,7 @@ static int acpi_fan_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id); - platform_set_drvdata(pdev, cdev); + fan->cdev = cdev; result = sysfs_create_link(&pdev->dev.kobj, &cdev->device.kobj, "thermal_cooling"); @@ -158,21 +368,17 @@ static int acpi_fan_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to create sysfs link " "'device'\n"); - dev_info(&pdev->dev, "%s [%s] (%s)\n", - acpi_device_name(device), acpi_device_bid(device), - !device->power.state ? "on" : "off"); - end: return result; } static int acpi_fan_remove(struct platform_device *pdev) { - struct thermal_cooling_device *cdev = platform_get_drvdata(pdev); + struct acpi_fan *fan = platform_get_drvdata(pdev); sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling"); - sysfs_remove_link(&cdev->device.kobj, "device"); - thermal_cooling_device_unregister(cdev); + sysfs_remove_link(&fan->cdev->device.kobj, "device"); + thermal_cooling_device_unregister(fan->cdev); return 0; } @@ -180,6 +386,10 @@ static int acpi_fan_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int acpi_fan_suspend(struct device *dev) { + struct acpi_fan *fan = dev_get_drvdata(dev); + if (fan->acpi4) + return 0; + acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0); return AE_OK; @@ -188,6 +398,10 @@ static int acpi_fan_suspend(struct device *dev) static int acpi_fan_resume(struct device *dev) { int result; + struct acpi_fan *fan = dev_get_drvdata(dev); + + if (fan->acpi4) + return 0; result = acpi_device_update_power(ACPI_COMPANION(dev), NULL); if (result) -- cgit v1.1 From d806c6e9cdfd6c5663687ec7109e151c0ff66639 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 1 Apr 2014 15:54:05 +0800 Subject: ACPI / Fan: support INT3404 thermal device INT3404 ACPI object follows the ACPI 5.0 fan object definition as described in section 11.3 of the ACPI 5.0 Specification. Thus we can reuse the ACPI fan driver for INT3404 ACPI object. Signed-off-by: Aaron Lu Signed-off-by: Zhang Rui --- drivers/acpi/fan.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index f7d1c80..e007c49 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -42,6 +42,7 @@ static int acpi_fan_remove(struct platform_device *pdev); static const struct acpi_device_id fan_device_ids[] = { {"PNP0C0B", 0}, + {"INT3404", 0}, {"", 0}, }; MODULE_DEVICE_TABLE(acpi, fan_device_ids); -- cgit v1.1 From 7b83fd9d91a411158f72d36958103c708c3b5a86 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 25 Mar 2014 10:40:09 +0800 Subject: Thermal: move the KELVIN_TO_MILLICELSIUS macro to thermal.h This macro can be used by other component so move it to a common header, but in a slightly different way: define two macros, one macro with an offset and the other doesn't. Signed-off-by: Aaron Lu Signed-off-by: Zhang Rui --- drivers/acpi/thermal.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 112817e..d24fa19 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -528,7 +528,6 @@ static void acpi_thermal_check(void *data) } /* sys I/F for generic thermal sysfs support */ -#define KELVIN_TO_MILLICELSIUS(t, off) (((t) - (off)) * 100) static int thermal_get_temp(struct thermal_zone_device *thermal, unsigned long *temp) @@ -543,7 +542,8 @@ static int thermal_get_temp(struct thermal_zone_device *thermal, if (result) return result; - *temp = KELVIN_TO_MILLICELSIUS(tz->temperature, tz->kelvin_offset); + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(tz->temperature, + tz->kelvin_offset); return 0; } @@ -647,7 +647,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, if (tz->trips.critical.flags.valid) { if (!trip) { - *temp = KELVIN_TO_MILLICELSIUS( + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( tz->trips.critical.temperature, tz->kelvin_offset); return 0; @@ -657,7 +657,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, if (tz->trips.hot.flags.valid) { if (!trip) { - *temp = KELVIN_TO_MILLICELSIUS( + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( tz->trips.hot.temperature, tz->kelvin_offset); return 0; @@ -667,7 +667,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, if (tz->trips.passive.flags.valid) { if (!trip) { - *temp = KELVIN_TO_MILLICELSIUS( + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( tz->trips.passive.temperature, tz->kelvin_offset); return 0; @@ -678,7 +678,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && tz->trips.active[i].flags.valid; i++) { if (!trip) { - *temp = KELVIN_TO_MILLICELSIUS( + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( tz->trips.active[i].temperature, tz->kelvin_offset); return 0; @@ -694,7 +694,7 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal, struct acpi_thermal *tz = thermal->devdata; if (tz->trips.critical.flags.valid) { - *temperature = KELVIN_TO_MILLICELSIUS( + *temperature = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( tz->trips.critical.temperature, tz->kelvin_offset); return 0; @@ -714,8 +714,8 @@ static int thermal_get_trend(struct thermal_zone_device *thermal, if (type == THERMAL_TRIP_ACTIVE) { unsigned long trip_temp; - unsigned long temp = KELVIN_TO_MILLICELSIUS(tz->temperature, - tz->kelvin_offset); + unsigned long temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( + tz->temperature, tz->kelvin_offset); if (thermal_get_trip_temp(thermal, trip, &trip_temp)) return -EINVAL; -- cgit v1.1 From 77e337c6e23e3b9d22e09ffec202a80f755a54c2 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 3 Sep 2014 15:13:02 +0800 Subject: Thermal: introduce INT3402 thermal driver ACPI INT3402 device object could report temperature for the memory module. To expose such information to user space, a thermal zone device is registered for it so that the thermal sysfs interface can expose such information for userspace to use. Signed-off-by: Aaron Lu Signed-off-by: Zhang Rui --- drivers/thermal/int340x_thermal/Makefile | 1 + drivers/thermal/int340x_thermal/int3402_thermal.c | 242 ++++++++++++++++++++++ 2 files changed, 243 insertions(+) create mode 100644 drivers/thermal/int340x_thermal/int3402_thermal.c (limited to 'drivers') diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile index e10a53b..67c98fd 100644 --- a/drivers/thermal/int340x_thermal/Makefile +++ b/drivers/thermal/int340x_thermal/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o +obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o diff --git a/drivers/thermal/int340x_thermal/int3402_thermal.c b/drivers/thermal/int340x_thermal/int3402_thermal.c new file mode 100644 index 0000000..a5d08c1 --- /dev/null +++ b/drivers/thermal/int340x_thermal/int3402_thermal.c @@ -0,0 +1,242 @@ +/* + * INT3402 thermal driver for memory temperature reporting + * + * Copyright (C) 2014, Intel Corporation + * Authors: Aaron Lu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#define ACPI_ACTIVE_COOLING_MAX_NR 10 + +struct active_trip { + unsigned long temp; + int id; + bool valid; +}; + +struct int3402_thermal_data { + unsigned long *aux_trips; + int aux_trip_nr; + unsigned long psv_temp; + int psv_trip_id; + unsigned long crt_temp; + int crt_trip_id; + unsigned long hot_temp; + int hot_trip_id; + struct active_trip act_trips[ACPI_ACTIVE_COOLING_MAX_NR]; + acpi_handle *handle; +}; + +static int int3402_thermal_get_zone_temp(struct thermal_zone_device *zone, + unsigned long *temp) +{ + struct int3402_thermal_data *d = zone->devdata; + unsigned long long tmp; + acpi_status status; + + status = acpi_evaluate_integer(d->handle, "_TMP", NULL, &tmp); + if (ACPI_FAILURE(status)) + return -ENODEV; + + /* _TMP returns the temperature in tenths of degrees Kelvin */ + *temp = DECI_KELVIN_TO_MILLICELSIUS(tmp); + + return 0; +} + +static int int3402_thermal_get_trip_temp(struct thermal_zone_device *zone, + int trip, unsigned long *temp) +{ + struct int3402_thermal_data *d = zone->devdata; + int i; + + if (trip < d->aux_trip_nr) + *temp = d->aux_trips[trip]; + else if (trip == d->crt_trip_id) + *temp = d->crt_temp; + else if (trip == d->psv_trip_id) + *temp = d->psv_temp; + else if (trip == d->hot_trip_id) + *temp = d->hot_temp; + else { + for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) { + if (d->act_trips[i].valid && + d->act_trips[i].id == trip) { + *temp = d->act_trips[i].temp; + break; + } + } + if (i == ACPI_ACTIVE_COOLING_MAX_NR) + return -EINVAL; + } + return 0; +} + +static int int3402_thermal_get_trip_type(struct thermal_zone_device *zone, + int trip, enum thermal_trip_type *type) +{ + struct int3402_thermal_data *d = zone->devdata; + int i; + + if (trip < d->aux_trip_nr) + *type = THERMAL_TRIP_PASSIVE; + else if (trip == d->crt_trip_id) + *type = THERMAL_TRIP_CRITICAL; + else if (trip == d->hot_trip_id) + *type = THERMAL_TRIP_HOT; + else if (trip == d->psv_trip_id) + *type = THERMAL_TRIP_PASSIVE; + else { + for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) { + if (d->act_trips[i].valid && + d->act_trips[i].id == trip) { + *type = THERMAL_TRIP_ACTIVE; + break; + } + } + if (i == ACPI_ACTIVE_COOLING_MAX_NR) + return -EINVAL; + } + return 0; +} + +static int int3402_thermal_set_trip_temp(struct thermal_zone_device *zone, int trip, + unsigned long temp) +{ + struct int3402_thermal_data *d = zone->devdata; + acpi_status status; + char name[10]; + + snprintf(name, sizeof(name), "PAT%d", trip); + status = acpi_execute_simple_method(d->handle, name, + MILLICELSIUS_TO_DECI_KELVIN(temp)); + if (ACPI_FAILURE(status)) + return -EIO; + + d->aux_trips[trip] = temp; + return 0; +} + +static struct thermal_zone_device_ops int3402_thermal_zone_ops = { + .get_temp = int3402_thermal_get_zone_temp, + .get_trip_temp = int3402_thermal_get_trip_temp, + .get_trip_type = int3402_thermal_get_trip_type, + .set_trip_temp = int3402_thermal_set_trip_temp, +}; + +static struct thermal_zone_params int3402_thermal_params = { + .governor_name = "user_space", + .no_hwmon = true, +}; + +static int int3402_thermal_get_temp(acpi_handle handle, char *name, + unsigned long *temp) +{ + unsigned long long r; + acpi_status status; + + status = acpi_evaluate_integer(handle, name, NULL, &r); + if (ACPI_FAILURE(status)) + return -EIO; + + *temp = DECI_KELVIN_TO_MILLICELSIUS(r); + return 0; +} + +static int int3402_thermal_probe(struct platform_device *pdev) +{ + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); + struct int3402_thermal_data *d; + struct thermal_zone_device *zone; + acpi_status status; + unsigned long long trip_cnt; + int trip_mask = 0, i; + + if (!acpi_has_method(adev->handle, "_TMP")) + return -ENODEV; + + d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL); + if (!d) + return -ENOMEM; + + status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt); + if (ACPI_FAILURE(status)) + trip_cnt = 0; + else { + d->aux_trips = devm_kzalloc(&pdev->dev, + sizeof(*d->aux_trips) * trip_cnt, GFP_KERNEL); + if (!d->aux_trips) + return -ENOMEM; + trip_mask = trip_cnt - 1; + d->handle = adev->handle; + d->aux_trip_nr = trip_cnt; + } + + d->crt_trip_id = -1; + if (!int3402_thermal_get_temp(adev->handle, "_CRT", &d->crt_temp)) + d->crt_trip_id = trip_cnt++; + d->hot_trip_id = -1; + if (!int3402_thermal_get_temp(adev->handle, "_HOT", &d->hot_temp)) + d->hot_trip_id = trip_cnt++; + d->psv_trip_id = -1; + if (!int3402_thermal_get_temp(adev->handle, "_PSV", &d->psv_temp)) + d->psv_trip_id = trip_cnt++; + for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) { + char name[5] = { '_', 'A', 'C', '0' + i, '\0' }; + if (int3402_thermal_get_temp(adev->handle, name, + &d->act_trips[i].temp)) + break; + d->act_trips[i].id = trip_cnt++; + d->act_trips[i].valid = true; + } + + zone = thermal_zone_device_register(acpi_device_bid(adev), trip_cnt, + trip_mask, d, + &int3402_thermal_zone_ops, + &int3402_thermal_params, + 0, 0); + if (IS_ERR(zone)) + return PTR_ERR(zone); + platform_set_drvdata(pdev, zone); + + return 0; +} + +static int int3402_thermal_remove(struct platform_device *pdev) +{ + struct thermal_zone_device *zone = platform_get_drvdata(pdev); + + thermal_zone_device_unregister(zone); + return 0; +} + +static const struct acpi_device_id int3402_thermal_match[] = { + {"INT3402", 0}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, int3402_thermal_match); + +static struct platform_driver int3402_thermal_driver = { + .probe = int3402_thermal_probe, + .remove = int3402_thermal_remove, + .driver = { + .name = "int3402 thermal", + .owner = THIS_MODULE, + .acpi_match_table = int3402_thermal_match, + }, +}; + +module_platform_driver(int3402_thermal_driver); + +MODULE_DESCRIPTION("INT3402 Thermal driver"); +MODULE_LICENSE("GPL"); -- cgit v1.1 From 29d05c2ecf396161ef2938a0635707ef5685ef58 Mon Sep 17 00:00:00 2001 From: Adel Gadllah Date: Thu, 9 Oct 2014 08:05:52 +0200 Subject: HID: usbhid: enable always-poll quirk for Elan Touchscreen 009b This device needs the quirk as well. Signed-off-by: Adel Gadllah Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/usbhid/hid-quirks.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index cd9c9e9..27fbd13 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -298,6 +298,7 @@ #define USB_VENDOR_ID_ELAN 0x04f3 #define USB_DEVICE_ID_ELAN_TOUCHSCREEN 0x0089 +#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B 0x009b #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index f3cb5b0..40aac21 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -71,6 +71,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL }, + { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, -- cgit v1.1 From 1af39588f84c7c18f8c6d88342f36513a4ce383c Mon Sep 17 00:00:00 2001 From: Adel Gadllah Date: Thu, 9 Oct 2014 08:05:53 +0200 Subject: HID: usbhid: enable always-poll quirk for Elan Touchscreen 016f This device needs the quirk as well. Tested-by: Kevin Fenzi Signed-off-by: Adel Gadllah Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/usbhid/hid-quirks.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 27fbd13..e23ab8b 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -299,6 +299,7 @@ #define USB_VENDOR_ID_ELAN 0x04f3 #define USB_DEVICE_ID_ELAN_TOUCHSCREEN 0x0089 #define USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B 0x009b +#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F 0x016f #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 40aac21..5014bb5 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -72,6 +72,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B, HID_QUIRK_ALWAYS_POLL }, + { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, -- cgit v1.1 From 4384b8fe162d8aa03905d02073707bcf364cc7ce Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Wed, 3 Sep 2014 15:13:30 +0800 Subject: Thermal: introduce int3403 thermal driver ACPI INT3403 device object can be used to retrieve temperature date from temperature sensors present in the system, and to expose device' performance control. The previous INT3403 thermal driver supports temperature reporting only, thus remove it and introduce this new & enhanced one. Signed-off-by: Lan Tianyu Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 15 - drivers/thermal/Makefile | 1 - drivers/thermal/int3403_thermal.c | 296 -------------- drivers/thermal/int340x_thermal/Makefile | 1 + drivers/thermal/int340x_thermal/int3403_thermal.c | 477 ++++++++++++++++++++++ 5 files changed, 478 insertions(+), 312 deletions(-) delete mode 100644 drivers/thermal/int3403_thermal.c create mode 100644 drivers/thermal/int340x_thermal/int3403_thermal.c (limited to 'drivers') diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index b34c5f5..6f93e5c 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -207,21 +207,6 @@ config X86_PKG_TEMP_THERMAL two trip points which can be set by user to get notifications via thermal notification methods. -config ACPI_INT3403_THERMAL - tristate "ACPI INT3403 thermal driver" - depends on X86 && ACPI - help - Newer laptops and tablets that use ACPI may have thermal sensors - outside the core CPU/SOC for thermal safety reasons. These - temperature sensors are also exposed for the OS to use via the so - called INT3403 ACPI object. This driver will, on devices that have - such sensors, expose the temperature information from these sensors - to userspace via the normal thermal framework. This means that a wide - range of applications and GUI widgets can show this information to - the user or use this information for making decisions. For example, - the Intel Thermal Daemon can use this information to allow the user - to select his laptop to run without turning on the fans. - config INTEL_SOC_DTS_THERMAL tristate "Intel SoCs DTS thermal driver" depends on X86 && IOSF_MBI diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 216503e..39b85b5 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -31,6 +31,5 @@ obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ -obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ obj-$(CONFIG_ST_THERMAL) += st/ diff --git a/drivers/thermal/int3403_thermal.c b/drivers/thermal/int3403_thermal.c deleted file mode 100644 index 17554ee..0000000 --- a/drivers/thermal/int3403_thermal.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * ACPI INT3403 thermal driver - * Copyright (c) 2013, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#include -#include -#include -#include -#include -#include - -#define INT3403_TYPE_SENSOR 0x03 -#define INT3403_PERF_CHANGED_EVENT 0x80 -#define INT3403_THERMAL_EVENT 0x90 - -#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100) -#define KELVIN_OFFSET 2732 -#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off)) - -#define ACPI_INT3403_CLASS "int3403" -#define ACPI_INT3403_FILE_STATE "state" - -struct int3403_sensor { - struct thermal_zone_device *tzone; - unsigned long *thresholds; - unsigned long crit_temp; - int crit_trip_id; - unsigned long psv_temp; - int psv_trip_id; -}; - -static int sys_get_curr_temp(struct thermal_zone_device *tzone, - unsigned long *temp) -{ - struct acpi_device *device = tzone->devdata; - unsigned long long tmp; - acpi_status status; - - status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp); - if (ACPI_FAILURE(status)) - return -EIO; - - *temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET); - - return 0; -} - -static int sys_get_trip_hyst(struct thermal_zone_device *tzone, - int trip, unsigned long *temp) -{ - struct acpi_device *device = tzone->devdata; - unsigned long long hyst; - acpi_status status; - - status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst); - if (ACPI_FAILURE(status)) - return -EIO; - - /* - * Thermal hysteresis represents a temperature difference. - * Kelvin and Celsius have same degree size. So the - * conversion here between tenths of degree Kelvin unit - * and Milli-Celsius unit is just to multiply 100. - */ - *temp = hyst * 100; - - return 0; -} - -static int sys_get_trip_temp(struct thermal_zone_device *tzone, - int trip, unsigned long *temp) -{ - struct acpi_device *device = tzone->devdata; - struct int3403_sensor *obj = acpi_driver_data(device); - - if (trip == obj->crit_trip_id) - *temp = obj->crit_temp; - else if (trip == obj->psv_trip_id) - *temp = obj->psv_temp; - else { - /* - * get_trip_temp is a mandatory callback but - * PATx method doesn't return any value, so return - * cached value, which was last set from user space. - */ - *temp = obj->thresholds[trip]; - } - - return 0; -} - -static int sys_get_trip_type(struct thermal_zone_device *thermal, - int trip, enum thermal_trip_type *type) -{ - struct acpi_device *device = thermal->devdata; - struct int3403_sensor *obj = acpi_driver_data(device); - - /* Mandatory callback, may not mean much here */ - if (trip == obj->crit_trip_id) - *type = THERMAL_TRIP_CRITICAL; - else - *type = THERMAL_TRIP_PASSIVE; - - return 0; -} - -int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip, - unsigned long temp) -{ - struct acpi_device *device = tzone->devdata; - acpi_status status; - char name[10]; - int ret = 0; - struct int3403_sensor *obj = acpi_driver_data(device); - - snprintf(name, sizeof(name), "PAT%d", trip); - if (acpi_has_method(device->handle, name)) { - status = acpi_execute_simple_method(device->handle, name, - MILLI_CELSIUS_TO_DECI_KELVIN(temp, - KELVIN_OFFSET)); - if (ACPI_FAILURE(status)) - ret = -EIO; - else - obj->thresholds[trip] = temp; - } else { - ret = -EIO; - dev_err(&device->dev, "sys_set_trip_temp: method not found\n"); - } - - return ret; -} - -static struct thermal_zone_device_ops tzone_ops = { - .get_temp = sys_get_curr_temp, - .get_trip_temp = sys_get_trip_temp, - .get_trip_type = sys_get_trip_type, - .set_trip_temp = sys_set_trip_temp, - .get_trip_hyst = sys_get_trip_hyst, -}; - -static void acpi_thermal_notify(struct acpi_device *device, u32 event) -{ - struct int3403_sensor *obj; - - if (!device) - return; - - obj = acpi_driver_data(device); - if (!obj) - return; - - switch (event) { - case INT3403_PERF_CHANGED_EVENT: - break; - case INT3403_THERMAL_EVENT: - thermal_zone_device_update(obj->tzone); - break; - default: - dev_err(&device->dev, "Unsupported event [0x%x]\n", event); - break; - } -} - -static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp) -{ - unsigned long long crt; - acpi_status status; - - status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt); - if (ACPI_FAILURE(status)) - return -EIO; - - *temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET); - - return 0; -} - -static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp) -{ - unsigned long long psv; - acpi_status status; - - status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv); - if (ACPI_FAILURE(status)) - return -EIO; - - *temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET); - - return 0; -} - -static int acpi_int3403_add(struct acpi_device *device) -{ - int result = 0; - unsigned long long ptyp; - acpi_status status; - struct int3403_sensor *obj; - unsigned long long trip_cnt; - int trip_mask = 0; - - if (!device) - return -EINVAL; - - status = acpi_evaluate_integer(device->handle, "PTYP", NULL, &ptyp); - if (ACPI_FAILURE(status)) - return -EINVAL; - - if (ptyp != INT3403_TYPE_SENSOR) - return -EINVAL; - - obj = devm_kzalloc(&device->dev, sizeof(*obj), GFP_KERNEL); - if (!obj) - return -ENOMEM; - - device->driver_data = obj; - - status = acpi_evaluate_integer(device->handle, "PATC", NULL, - &trip_cnt); - if (ACPI_FAILURE(status)) - trip_cnt = 0; - - if (trip_cnt) { - /* We have to cache, thresholds can't be readback */ - obj->thresholds = devm_kzalloc(&device->dev, - sizeof(*obj->thresholds) * trip_cnt, - GFP_KERNEL); - if (!obj->thresholds) - return -ENOMEM; - trip_mask = BIT(trip_cnt) - 1; - } - - obj->psv_trip_id = -1; - if (!sys_get_trip_psv(device, &obj->psv_temp)) - obj->psv_trip_id = trip_cnt++; - - obj->crit_trip_id = -1; - if (!sys_get_trip_crt(device, &obj->crit_temp)) - obj->crit_trip_id = trip_cnt++; - - obj->tzone = thermal_zone_device_register(acpi_device_bid(device), - trip_cnt, trip_mask, device, &tzone_ops, - NULL, 0, 0); - if (IS_ERR(obj->tzone)) { - result = PTR_ERR(obj->tzone); - return result; - } - - strcpy(acpi_device_name(device), "INT3403"); - strcpy(acpi_device_class(device), ACPI_INT3403_CLASS); - - return 0; -} - -static int acpi_int3403_remove(struct acpi_device *device) -{ - struct int3403_sensor *obj; - - obj = acpi_driver_data(device); - thermal_zone_device_unregister(obj->tzone); - - return 0; -} - -ACPI_MODULE_NAME("int3403"); -static const struct acpi_device_id int3403_device_ids[] = { - {"INT3403", 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, int3403_device_ids); - -static struct acpi_driver acpi_int3403_driver = { - .name = "INT3403", - .class = ACPI_INT3403_CLASS, - .ids = int3403_device_ids, - .ops = { - .add = acpi_int3403_add, - .remove = acpi_int3403_remove, - .notify = acpi_thermal_notify, - }, -}; - -module_acpi_driver(acpi_int3403_driver); - -MODULE_AUTHOR("Srinivas Pandruvada "); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("ACPI INT3403 thermal driver"); diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile index 67c98fd..c4a5c50 100644 --- a/drivers/thermal/int340x_thermal/Makefile +++ b/drivers/thermal/int340x_thermal/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o +obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o diff --git a/drivers/thermal/int340x_thermal/int3403_thermal.c b/drivers/thermal/int340x_thermal/int3403_thermal.c new file mode 100644 index 0000000..d20dba9 --- /dev/null +++ b/drivers/thermal/int340x_thermal/int3403_thermal.c @@ -0,0 +1,477 @@ +/* + * ACPI INT3403 thermal driver + * Copyright (c) 2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define INT3403_TYPE_SENSOR 0x03 +#define INT3403_TYPE_CHARGER 0x0B +#define INT3403_TYPE_BATTERY 0x0C +#define INT3403_PERF_CHANGED_EVENT 0x80 +#define INT3403_THERMAL_EVENT 0x90 + +#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100) +#define KELVIN_OFFSET 2732 +#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off)) + +struct int3403_sensor { + struct thermal_zone_device *tzone; + unsigned long *thresholds; + unsigned long crit_temp; + int crit_trip_id; + unsigned long psv_temp; + int psv_trip_id; + +}; + +struct int3403_performance_state { + u64 performance; + u64 power; + u64 latency; + u64 linear; + u64 control; + u64 raw_performace; + char *raw_unit; + int reserved; +}; + +struct int3403_cdev { + struct thermal_cooling_device *cdev; + unsigned long max_state; +}; + +struct int3403_priv { + struct platform_device *pdev; + struct acpi_device *adev; + unsigned long long type; + void *priv; +}; + +static int sys_get_curr_temp(struct thermal_zone_device *tzone, + unsigned long *temp) +{ + struct int3403_priv *priv = tzone->devdata; + struct acpi_device *device = priv->adev; + unsigned long long tmp; + acpi_status status; + + status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp); + if (ACPI_FAILURE(status)) + return -EIO; + + *temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET); + + return 0; +} + +static int sys_get_trip_hyst(struct thermal_zone_device *tzone, + int trip, unsigned long *temp) +{ + struct int3403_priv *priv = tzone->devdata; + struct acpi_device *device = priv->adev; + unsigned long long hyst; + acpi_status status; + + status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst); + if (ACPI_FAILURE(status)) + return -EIO; + + *temp = DECI_KELVIN_TO_MILLI_CELSIUS(hyst, KELVIN_OFFSET); + + return 0; +} + +static int sys_get_trip_temp(struct thermal_zone_device *tzone, + int trip, unsigned long *temp) +{ + struct int3403_priv *priv = tzone->devdata; + struct int3403_sensor *obj = priv->priv; + + if (priv->type != INT3403_TYPE_SENSOR || !obj) + return -EINVAL; + + if (trip == obj->crit_trip_id) + *temp = obj->crit_temp; + else if (trip == obj->psv_trip_id) + *temp = obj->psv_temp; + else { + /* + * get_trip_temp is a mandatory callback but + * PATx method doesn't return any value, so return + * cached value, which was last set from user space + */ + *temp = obj->thresholds[trip]; + } + + return 0; +} + +static int sys_get_trip_type(struct thermal_zone_device *thermal, + int trip, enum thermal_trip_type *type) +{ + struct int3403_priv *priv = thermal->devdata; + struct int3403_sensor *obj = priv->priv; + + /* Mandatory callback, may not mean much here */ + if (trip == obj->crit_trip_id) + *type = THERMAL_TRIP_CRITICAL; + else + *type = THERMAL_TRIP_PASSIVE; + + return 0; +} + +int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip, + unsigned long temp) +{ + struct int3403_priv *priv = tzone->devdata; + struct acpi_device *device = priv->adev; + struct int3403_sensor *obj = priv->priv; + acpi_status status; + char name[10]; + int ret = 0; + + snprintf(name, sizeof(name), "PAT%d", trip); + if (acpi_has_method(device->handle, name)) { + status = acpi_execute_simple_method(device->handle, name, + MILLI_CELSIUS_TO_DECI_KELVIN(temp, + KELVIN_OFFSET)); + if (ACPI_FAILURE(status)) + ret = -EIO; + else + obj->thresholds[trip] = temp; + } else { + ret = -EIO; + dev_err(&device->dev, "sys_set_trip_temp: method not found\n"); + } + + return ret; +} + +static struct thermal_zone_device_ops tzone_ops = { + .get_temp = sys_get_curr_temp, + .get_trip_temp = sys_get_trip_temp, + .get_trip_type = sys_get_trip_type, + .set_trip_temp = sys_set_trip_temp, + .get_trip_hyst = sys_get_trip_hyst, +}; + +static struct thermal_zone_params int3403_thermal_params = { + .governor_name = "user_space", + .no_hwmon = true, +}; + +static void int3403_notify(acpi_handle handle, + u32 event, void *data) +{ + struct int3403_priv *priv = data; + struct int3403_sensor *obj; + + if (!priv) + return; + + obj = priv->priv; + if (priv->type != INT3403_TYPE_SENSOR || !obj) + return; + + switch (event) { + case INT3403_PERF_CHANGED_EVENT: + break; + case INT3403_THERMAL_EVENT: + thermal_zone_device_update(obj->tzone); + break; + default: + dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event); + break; + } +} + +static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp) +{ + unsigned long long crt; + acpi_status status; + + status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt); + if (ACPI_FAILURE(status)) + return -EIO; + + *temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET); + + return 0; +} + +static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp) +{ + unsigned long long psv; + acpi_status status; + + status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv); + if (ACPI_FAILURE(status)) + return -EIO; + + *temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET); + + return 0; +} + +static int int3403_sensor_add(struct int3403_priv *priv) +{ + int result = 0; + acpi_status status; + struct int3403_sensor *obj; + unsigned long long trip_cnt; + int trip_mask = 0; + + obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL); + if (!obj) + return -ENOMEM; + + priv->priv = obj; + + status = acpi_evaluate_integer(priv->adev->handle, "PATC", NULL, + &trip_cnt); + if (ACPI_FAILURE(status)) + trip_cnt = 0; + + if (trip_cnt) { + /* We have to cache, thresholds can't be readback */ + obj->thresholds = devm_kzalloc(&priv->pdev->dev, + sizeof(*obj->thresholds) * trip_cnt, + GFP_KERNEL); + if (!obj->thresholds) { + result = -ENOMEM; + goto err_free_obj; + } + trip_mask = BIT(trip_cnt) - 1; + } + + obj->psv_trip_id = -1; + if (!sys_get_trip_psv(priv->adev, &obj->psv_temp)) + obj->psv_trip_id = trip_cnt++; + + obj->crit_trip_id = -1; + if (!sys_get_trip_crt(priv->adev, &obj->crit_temp)) + obj->crit_trip_id = trip_cnt++; + + obj->tzone = thermal_zone_device_register(acpi_device_bid(priv->adev), + trip_cnt, trip_mask, priv, &tzone_ops, + &int3403_thermal_params, 0, 0); + if (IS_ERR(obj->tzone)) { + result = PTR_ERR(obj->tzone); + obj->tzone = NULL; + goto err_free_obj; + } + + result = acpi_install_notify_handler(priv->adev->handle, + ACPI_DEVICE_NOTIFY, int3403_notify, + (void *)priv); + if (result) + goto err_free_obj; + + return 0; + + err_free_obj: + if (obj->tzone) + thermal_zone_device_unregister(obj->tzone); + return result; +} + +static int int3403_sensor_remove(struct int3403_priv *priv) +{ + struct int3403_sensor *obj = priv->priv; + + thermal_zone_device_unregister(obj->tzone); + return 0; +} + +/* INT3403 Cooling devices */ +static int int3403_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct int3403_priv *priv = cdev->devdata; + struct int3403_cdev *obj = priv->priv; + + *state = obj->max_state; + return 0; +} + +static int int3403_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct int3403_priv *priv = cdev->devdata; + unsigned long long level; + acpi_status status; + + status = acpi_evaluate_integer(priv->adev->handle, "PPPC", NULL, &level); + if (ACPI_SUCCESS(status)) { + *state = level; + return 0; + } else + return -EINVAL; +} + +static int +int3403_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + struct int3403_priv *priv = cdev->devdata; + acpi_status status; + + status = acpi_execute_simple_method(priv->adev->handle, "SPPC", state); + if (ACPI_SUCCESS(status)) + return 0; + else + return -EINVAL; +} + +static const struct thermal_cooling_device_ops int3403_cooling_ops = { + .get_max_state = int3403_get_max_state, + .get_cur_state = int3403_get_cur_state, + .set_cur_state = int3403_set_cur_state, +}; + +static int int3403_cdev_add(struct int3403_priv *priv) +{ + int result = 0; + acpi_status status; + struct int3403_cdev *obj; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *p; + + obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL); + if (!obj) + return -ENOMEM; + + status = acpi_evaluate_object(priv->adev->handle, "PPSS", NULL, &buf); + if (ACPI_FAILURE(status)) + return -ENODEV; + + p = buf.pointer; + if (!p || (p->type != ACPI_TYPE_PACKAGE)) { + printk(KERN_WARNING "Invalid PPSS data\n"); + return -EFAULT; + } + + obj->max_state = p->package.count - 1; + obj->cdev = + thermal_cooling_device_register(acpi_device_bid(priv->adev), + priv, &int3403_cooling_ops); + if (IS_ERR(obj->cdev)) + result = PTR_ERR(obj->cdev); + + priv->priv = obj; + + /* TODO: add ACPI notification support */ + + return result; +} + +static int int3403_cdev_remove(struct int3403_priv *priv) +{ + struct int3403_cdev *obj = priv->priv; + + thermal_cooling_device_unregister(obj->cdev); + return 0; +} + +static int int3403_add(struct platform_device *pdev) +{ + struct int3403_priv *priv; + int result = 0; + acpi_status status; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct int3403_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pdev = pdev; + priv->adev = ACPI_COMPANION(&(pdev->dev)); + if (!priv->adev) { + result = -EINVAL; + goto err; + } + + status = acpi_evaluate_integer(priv->adev->handle, "PTYP", + NULL, &priv->type); + if (ACPI_FAILURE(status)) { + result = -EINVAL; + goto err; + } + + platform_set_drvdata(pdev, priv); + switch (priv->type) { + case INT3403_TYPE_SENSOR: + result = int3403_sensor_add(priv); + break; + case INT3403_TYPE_CHARGER: + case INT3403_TYPE_BATTERY: + result = int3403_cdev_add(priv); + break; + default: + result = -EINVAL; + } + + if (result) + goto err; + return result; + +err: + return result; +} + +static int int3403_remove(struct platform_device *pdev) +{ + struct int3403_priv *priv = platform_get_drvdata(pdev); + + switch (priv->type) { + case INT3403_TYPE_SENSOR: + int3403_sensor_remove(priv); + break; + case INT3403_TYPE_CHARGER: + case INT3403_TYPE_BATTERY: + int3403_cdev_remove(priv); + break; + default: + break; + } + + return 0; +} + +static const struct acpi_device_id int3403_device_ids[] = { + {"INT3403", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, int3403_device_ids); + +static struct platform_driver int3403_driver = { + .probe = int3403_add, + .remove = int3403_remove, + .driver = { + .name = "int3403 thermal", + .owner = THIS_MODULE, + .acpi_match_table = int3403_device_ids, + }, +}; + +module_platform_driver(int3403_driver); + +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ACPI INT3403 thermal driver"); -- cgit v1.1 From 52b1c69d7e3cd8bdba0e55bde24093f0779bb29d Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Wed, 3 Sep 2014 15:14:23 +0800 Subject: Thermal: int340x_thermal: expose acpi thermal relationship tables ACPI 4.0 introduced two thermal relationship tables via _ART (active cooling) and _TRT (passive cooling) objects. These tables contain many to many relationships among thermal sensors and cooling devices. This patch parses _ART and _TRT and makes the result available to the userspace via an misc device interface. At the same time, kernel drivers can also request parsing results from internal kernel APIs. The results include source and target devices, influence, and sampling rate in case of _TRT. For _ART, the result shows source device, target device, and weight percentage. Signed-off-by: Jacob Pan Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 5 + drivers/thermal/int340x_thermal/Makefile | 1 + drivers/thermal/int340x_thermal/acpi_thermal_rel.c | 400 +++++++++++++++++++++ drivers/thermal/int340x_thermal/acpi_thermal_rel.h | 84 +++++ 4 files changed, 490 insertions(+) create mode 100644 drivers/thermal/int340x_thermal/acpi_thermal_rel.c create mode 100644 drivers/thermal/int340x_thermal/acpi_thermal_rel.h (limited to 'drivers') diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 6f93e5c..3a89292 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -223,6 +223,7 @@ config INT340X_THERMAL tristate "ACPI INT340X thermal drivers" depends on X86 && ACPI select THERMAL_GOV_USER_SPACE + select ACPI_THERMAL_REL help Newer laptops and tablets that use ACPI may have thermal sensors and other devices with thermal control capabilities outside the core @@ -237,6 +238,10 @@ config INT340X_THERMAL information to allow the user to select his laptop to run without turning on the fans. +config ACPI_THERMAL_REL + tristate + depends on ACPI + menu "Texas Instruments thermal drivers" source "drivers/thermal/ti-soc-thermal/Kconfig" endmenu diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile index c4a5c50..ffe40bf 100644 --- a/drivers/thermal/int340x_thermal/Makefile +++ b/drivers/thermal/int340x_thermal/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o +obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c new file mode 100644 index 0000000..0d8db80 --- /dev/null +++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c @@ -0,0 +1,400 @@ +/* acpi_thermal_rel.c driver for exporting ACPI thermal relationship + * + * Copyright (c) 2014 Intel Corp + * + * 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. + * + */ + +/* + * Two functionalities included: + * 1. Export _TRT, _ART, via misc device interface to the userspace. + * 2. Provide parsing result to kernel drivers + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "acpi_thermal_rel.h" + +static acpi_handle acpi_thermal_rel_handle; +static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock); +static int acpi_thermal_rel_chrdev_count; /* #times opened */ +static int acpi_thermal_rel_chrdev_exclu; /* already open exclusive? */ + +static int acpi_thermal_rel_open(struct inode *inode, struct file *file) +{ + spin_lock(&acpi_thermal_rel_chrdev_lock); + if (acpi_thermal_rel_chrdev_exclu || + (acpi_thermal_rel_chrdev_count && (file->f_flags & O_EXCL))) { + spin_unlock(&acpi_thermal_rel_chrdev_lock); + return -EBUSY; + } + + if (file->f_flags & O_EXCL) + acpi_thermal_rel_chrdev_exclu = 1; + acpi_thermal_rel_chrdev_count++; + + spin_unlock(&acpi_thermal_rel_chrdev_lock); + + return nonseekable_open(inode, file); +} + +static int acpi_thermal_rel_release(struct inode *inode, struct file *file) +{ + spin_lock(&acpi_thermal_rel_chrdev_lock); + acpi_thermal_rel_chrdev_count--; + acpi_thermal_rel_chrdev_exclu = 0; + spin_unlock(&acpi_thermal_rel_chrdev_lock); + + return 0; +} + +/** + * acpi_parse_trt - Thermal Relationship Table _TRT for passive cooling + * + * @handle: ACPI handle of the device contains _TRT + * @art_count: the number of valid entries resulted from parsing _TRT + * @artp: pointer to pointer of array of art entries in parsing result + * @create_dev: whether to create platform devices for target and source + * + */ +int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp, + bool create_dev) +{ + acpi_status status; + int result = 0; + int i; + int nr_bad_entries = 0; + struct trt *trts; + struct acpi_device *adev; + union acpi_object *p; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer element = { 0, NULL }; + struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" }; + + if (!acpi_has_method(handle, "_TRT")) + return 0; + + status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer); + if (ACPI_FAILURE(status)) + return -ENODEV; + + p = buffer.pointer; + if (!p || (p->type != ACPI_TYPE_PACKAGE)) { + pr_err("Invalid _TRT data\n"); + result = -EFAULT; + goto end; + } + + *trt_count = p->package.count; + trts = kzalloc(*trt_count * sizeof(struct trt), GFP_KERNEL); + if (!trts) { + result = -ENOMEM; + goto end; + } + + for (i = 0; i < *trt_count; i++) { + struct trt *trt = &trts[i - nr_bad_entries]; + + element.length = sizeof(struct trt); + element.pointer = trt; + + status = acpi_extract_package(&(p->package.elements[i]), + &trt_format, &element); + if (ACPI_FAILURE(status)) { + nr_bad_entries++; + pr_warn("_TRT package %d is invalid, ignored\n", i); + continue; + } + if (!create_dev) + continue; + + result = acpi_bus_get_device(trt->source, &adev); + if (!result) + acpi_create_platform_device(adev); + else + pr_warn("Failed to get source ACPI device\n"); + + result = acpi_bus_get_device(trt->target, &adev); + if (!result) + acpi_create_platform_device(adev); + else + pr_warn("Failed to get target ACPI device\n"); + } + + *trtp = trts; + /* don't count bad entries */ + *trt_count -= nr_bad_entries; +end: + kfree(buffer.pointer); + return result; +} +EXPORT_SYMBOL(acpi_parse_trt); + +/** + * acpi_parse_art - Parse Active Relationship Table _ART + * + * @handle: ACPI handle of the device contains _ART + * @art_count: the number of valid entries resulted from parsing _ART + * @artp: pointer to pointer of array of art entries in parsing result + * @create_dev: whether to create platform devices for target and source + * + */ +int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp, + bool create_dev) +{ + acpi_status status; + int result = 0; + int i; + int nr_bad_entries = 0; + struct art *arts; + struct acpi_device *adev; + union acpi_object *p; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer element = { 0, NULL }; + struct acpi_buffer art_format = { + sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" }; + + if (!acpi_has_method(handle, "_ART")) + return 0; + + status = acpi_evaluate_object(handle, "_ART", NULL, &buffer); + if (ACPI_FAILURE(status)) + return -ENODEV; + + p = buffer.pointer; + if (!p || (p->type != ACPI_TYPE_PACKAGE)) { + pr_err("Invalid _ART data\n"); + result = -EFAULT; + goto end; + } + + /* ignore p->package.elements[0], as this is _ART Revision field */ + *art_count = p->package.count - 1; + arts = kzalloc(*art_count * sizeof(struct art), GFP_KERNEL); + if (!arts) { + result = -ENOMEM; + goto end; + } + + for (i = 0; i < *art_count; i++) { + struct art *art = &arts[i - nr_bad_entries]; + + element.length = sizeof(struct art); + element.pointer = art; + + status = acpi_extract_package(&(p->package.elements[i + 1]), + &art_format, &element); + if (ACPI_FAILURE(status)) { + pr_warn("_ART package %d is invalid, ignored", i); + nr_bad_entries++; + continue; + } + if (!create_dev) + continue; + + if (art->source) { + result = acpi_bus_get_device(art->source, &adev); + if (!result) + acpi_create_platform_device(adev); + else + pr_warn("Failed to get source ACPI device\n"); + } + if (art->target) { + result = acpi_bus_get_device(art->target, &adev); + if (!result) + acpi_create_platform_device(adev); + else + pr_warn("Failed to get source ACPI device\n"); + } + } + + *artp = arts; + /* don't count bad entries */ + *art_count -= nr_bad_entries; +end: + kfree(buffer.pointer); + return result; +} +EXPORT_SYMBOL(acpi_parse_art); + + +/* get device name from acpi handle */ +static void get_single_name(acpi_handle handle, char *name) +{ + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER}; + + if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer))) + pr_warn("Failed get name from handle\n"); + else { + memcpy(name, buffer.pointer, ACPI_NAME_SIZE); + kfree(buffer.pointer); + } +} + +static int fill_art(char __user *ubuf) +{ + int i; + int ret; + int count; + int art_len; + struct art *arts = NULL; + union art_object *art_user; + + ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false); + if (ret) + goto free_art; + art_len = count * sizeof(union art_object); + art_user = kzalloc(art_len, GFP_KERNEL); + if (!art_user) { + ret = -ENOMEM; + goto free_art; + } + /* now fill in user art data */ + for (i = 0; i < count; i++) { + /* userspace art needs device name instead of acpi reference */ + get_single_name(arts[i].source, art_user[i].source_device); + get_single_name(arts[i].target, art_user[i].target_device); + /* copy the rest int data in addition to source and target */ + memcpy(&art_user[i].weight, &arts[i].weight, + sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2)); + } + + if (copy_to_user(ubuf, art_user, art_len)) + ret = -EFAULT; + kfree(art_user); +free_art: + kfree(arts); + return ret; +} + +static int fill_trt(char __user *ubuf) +{ + int i; + int ret; + int count; + int trt_len; + struct trt *trts = NULL; + union trt_object *trt_user; + + ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false); + if (ret) + goto free_trt; + trt_len = count * sizeof(union trt_object); + trt_user = kzalloc(trt_len, GFP_KERNEL); + if (!trt_user) { + ret = -ENOMEM; + goto free_trt; + } + /* now fill in user trt data */ + for (i = 0; i < count; i++) { + /* userspace trt needs device name instead of acpi reference */ + get_single_name(trts[i].source, trt_user[i].source_device); + get_single_name(trts[i].target, trt_user[i].target_device); + trt_user[i].sample_period = trts[i].sample_period; + trt_user[i].influence = trts[i].influence; + } + + if (copy_to_user(ubuf, trt_user, trt_len)) + ret = -EFAULT; + kfree(trt_user); +free_trt: + kfree(trts); + return ret; +} + +static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd, + unsigned long __arg) +{ + int ret = 0; + unsigned long length = 0; + unsigned long count = 0; + char __user *arg = (void __user *)__arg; + struct trt *trts; + struct art *arts; + + switch (cmd) { + case ACPI_THERMAL_GET_TRT_COUNT: + ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count, + &trts, false); + kfree(trts); + if (!ret) + return put_user(count, (unsigned long __user *)__arg); + return ret; + case ACPI_THERMAL_GET_TRT_LEN: + ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count, + &trts, false); + kfree(trts); + length = count * sizeof(union trt_object); + if (!ret) + return put_user(length, (unsigned long __user *)__arg); + return ret; + case ACPI_THERMAL_GET_TRT: + return fill_trt(arg); + case ACPI_THERMAL_GET_ART_COUNT: + ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count, + &arts, false); + kfree(arts); + if (!ret) + return put_user(count, (unsigned long __user *)__arg); + return ret; + case ACPI_THERMAL_GET_ART_LEN: + ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count, + &arts, false); + kfree(arts); + length = count * sizeof(union art_object); + if (!ret) + return put_user(length, (unsigned long __user *)__arg); + return ret; + + case ACPI_THERMAL_GET_ART: + return fill_art(arg); + + default: + return -ENOTTY; + } +} + +static const struct file_operations acpi_thermal_rel_fops = { + .owner = THIS_MODULE, + .open = acpi_thermal_rel_open, + .release = acpi_thermal_rel_release, + .unlocked_ioctl = acpi_thermal_rel_ioctl, + .llseek = no_llseek, +}; + +static struct miscdevice acpi_thermal_rel_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + "acpi_thermal_rel", + &acpi_thermal_rel_fops +}; + +int acpi_thermal_rel_misc_device_add(acpi_handle handle) +{ + acpi_thermal_rel_handle = handle; + + return misc_register(&acpi_thermal_rel_misc_device); +} +EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add); + +int acpi_thermal_rel_misc_device_remove(acpi_handle handle) +{ + misc_deregister(&acpi_thermal_rel_misc_device); + + return 0; +} +EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove); + +MODULE_AUTHOR("Zhang Rui "); +MODULE_AUTHOR("Jacob Pan + +#define ACPI_THERMAL_MAGIC 's' + +#define ACPI_THERMAL_GET_TRT_LEN _IOR(ACPI_THERMAL_MAGIC, 1, unsigned long) +#define ACPI_THERMAL_GET_ART_LEN _IOR(ACPI_THERMAL_MAGIC, 2, unsigned long) +#define ACPI_THERMAL_GET_TRT_COUNT _IOR(ACPI_THERMAL_MAGIC, 3, unsigned long) +#define ACPI_THERMAL_GET_ART_COUNT _IOR(ACPI_THERMAL_MAGIC, 4, unsigned long) + +#define ACPI_THERMAL_GET_TRT _IOR(ACPI_THERMAL_MAGIC, 5, unsigned long) +#define ACPI_THERMAL_GET_ART _IOR(ACPI_THERMAL_MAGIC, 6, unsigned long) + +struct art { + acpi_handle source; + acpi_handle target; + u64 weight; + u64 ac0_max; + u64 ac1_max; + u64 ac2_max; + u64 ac3_max; + u64 ac4_max; + u64 ac5_max; + u64 ac6_max; + u64 ac7_max; + u64 ac8_max; + u64 ac9_max; +} __packed; + +struct trt { + acpi_handle source; + acpi_handle target; + u64 influence; + u64 sample_period; + u64 reverved1; + u64 reverved2; + u64 reverved3; + u64 reverved4; +} __packed; + +#define ACPI_NR_ART_ELEMENTS 13 +/* for usrspace */ +union art_object { + struct { + char source_device[8]; /* ACPI single name */ + char target_device[8]; /* ACPI single name */ + u64 weight; + u64 ac0_max_level; + u64 ac1_max_level; + u64 ac2_max_level; + u64 ac3_max_level; + u64 ac4_max_level; + u64 ac5_max_level; + u64 ac6_max_level; + u64 ac7_max_level; + u64 ac8_max_level; + u64 ac9_max_level; + }; + u64 __data[ACPI_NR_ART_ELEMENTS]; +}; + +union trt_object { + struct { + char source_device[8]; /* ACPI single name */ + char target_device[8]; /* ACPI single name */ + u64 influence; + u64 sample_period; + u64 reserved[4]; + }; + u64 __data[8]; +}; + +#ifdef __KERNEL__ +int acpi_thermal_rel_misc_device_add(acpi_handle handle); +int acpi_thermal_rel_misc_device_remove(acpi_handle handle); +int acpi_parse_art(acpi_handle handle, int *art_count, struct art **arts, + bool create_dev); +int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trts, + bool create_dev); +#endif + +#endif /* __ACPI_ACPI_THERMAL_H */ -- cgit v1.1 From 6306e68a63f26219f3b1f948e96ba387ddccb272 Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Thu, 12 Jun 2014 08:08:07 -0700 Subject: Thermal: int3400_thermal: use acpi_thermal_rel parsing APIs ACPI _TRT and _ART parsing code has been moved to acpi_thermal_rel such that it can be used by other devices in the future. Use the parsing APIs in acpi_thermal_rel.c instead. Signed-off-by: Jacob Pan Signed-off-by: Zhang Rui --- drivers/thermal/int340x_thermal/int3400_thermal.c | 172 ++-------------------- 1 file changed, 13 insertions(+), 159 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c index 9104b4f..edc1cce 100644 --- a/drivers/thermal/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c @@ -14,33 +14,7 @@ #include #include #include - -struct art { - acpi_handle source; - acpi_handle target; - u64 weight; - u64 ac0_max; - u64 ac1_max; - u64 ac2_max; - u64 ac3_max; - u64 ac4_max; - u64 ac5_max; - u64 ac6_max; - u64 ac7_max; - u64 ac8_max; - u64 ac9_max; -}; - -struct trt { - acpi_handle source; - acpi_handle target; - u64 influence; - u64 sampling_period; - u64 reverved1; - u64 reverved2; - u64 reverved3; - u64 reverved4; -}; +#include "acpi_thermal_rel.h" enum int3400_thermal_uuid { INT3400_THERMAL_PASSIVE_1, @@ -68,6 +42,7 @@ struct int3400_thermal_priv { int trt_count; struct trt *trts; u8 uuid_bitmap; + int rel_misc_dev_res; }; static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv) @@ -146,136 +121,6 @@ static int int3400_thermal_run_osc(acpi_handle handle, return result; } - -static int parse_art(struct int3400_thermal_priv *priv) -{ - acpi_handle handle = priv->adev->handle; - acpi_status status; - int result = 0; - int i; - struct acpi_device *adev; - union acpi_object *p; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_buffer element = { 0, NULL }; - struct acpi_buffer art_format = { - sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" }; - - if (!acpi_has_method(handle, "_ART")) - return 0; - - status = acpi_evaluate_object(handle, "_ART", NULL, &buffer); - if (ACPI_FAILURE(status)) - return -ENODEV; - - p = buffer.pointer; - if (!p || (p->type != ACPI_TYPE_PACKAGE)) { - pr_err("Invalid _ART data\n"); - result = -EFAULT; - goto end; - } - - /* ignore p->package.elements[0], as this is _ART Revision field */ - priv->art_count = p->package.count - 1; - priv->arts = kzalloc(sizeof(struct art) * priv->art_count, GFP_KERNEL); - if (!priv->arts) { - result = -ENOMEM; - goto end; - } - - for (i = 0; i < priv->art_count; i++) { - struct art *art = &(priv->arts[i]); - - element.length = sizeof(struct art); - element.pointer = art; - - status = acpi_extract_package(&(p->package.elements[i + 1]), - &art_format, &element); - if (ACPI_FAILURE(status)) { - pr_err("Invalid _ART data"); - result = -EFAULT; - kfree(priv->arts); - goto end; - } - result = acpi_bus_get_device(art->source, &adev); - if (!result) - acpi_create_platform_device(adev, NULL); - else - pr_warn("Failed to get source ACPI device\n"); - result = acpi_bus_get_device(art->target, &adev); - if (!result) - acpi_create_platform_device(adev, NULL); - else - pr_warn("Failed to get source ACPI device\n"); - } -end: - kfree(buffer.pointer); - return result; -} - -static int parse_trt(struct int3400_thermal_priv *priv) -{ - acpi_handle handle = priv->adev->handle; - acpi_status status; - int result = 0; - int i; - struct acpi_device *adev; - union acpi_object *p; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_buffer element = { 0, NULL }; - struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" }; - - if (!acpi_has_method(handle, "_TRT")) - return 0; - - status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer); - if (ACPI_FAILURE(status)) - return -ENODEV; - - p = buffer.pointer; - if (!p || (p->type != ACPI_TYPE_PACKAGE)) { - pr_err("Invalid _TRT data\n"); - result = -EFAULT; - goto end; - } - - priv->trt_count = p->package.count; - priv->trts = kzalloc(sizeof(struct trt) * priv->trt_count, GFP_KERNEL); - if (!priv->trts) { - result = -ENOMEM; - goto end; - } - - for (i = 0; i < priv->trt_count; i++) { - struct trt *trt = &(priv->trts[i]); - - element.length = sizeof(struct trt); - element.pointer = trt; - - status = acpi_extract_package(&(p->package.elements[i]), - &trt_format, &element); - if (ACPI_FAILURE(status)) { - pr_err("Invalid _ART data"); - result = -EFAULT; - kfree(priv->trts); - goto end; - } - - result = acpi_bus_get_device(trt->source, &adev); - if (!result) - acpi_create_platform_device(adev, NULL); - else - pr_warn("Failed to get source ACPI device\n"); - result = acpi_bus_get_device(trt->target, &adev); - if (!result) - acpi_create_platform_device(adev, NULL); - else - pr_warn("Failed to get target ACPI device\n"); - } -end: - kfree(buffer.pointer); - return result; -} - static int int3400_thermal_get_temp(struct thermal_zone_device *thermal, unsigned long *temp) { @@ -350,11 +195,14 @@ static int int3400_thermal_probe(struct platform_device *pdev) if (result) goto free_priv; - result = parse_art(priv); + result = acpi_parse_art(priv->adev->handle, &priv->art_count, + &priv->arts, true); if (result) goto free_priv; - result = parse_trt(priv); + + result = acpi_parse_trt(priv->adev->handle, &priv->trt_count, + &priv->trts, true); if (result) goto free_art; @@ -372,6 +220,9 @@ static int int3400_thermal_probe(struct platform_device *pdev) goto free_trt; } + priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add( + priv->adev->handle); + return 0; free_trt: kfree(priv->trts); @@ -386,6 +237,9 @@ static int int3400_thermal_remove(struct platform_device *pdev) { struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); + if (!priv->rel_misc_dev_res) + acpi_thermal_rel_misc_device_remove(priv->adev->handle); + thermal_zone_device_unregister(priv->thermal); kfree(priv->trts); kfree(priv->arts); -- cgit v1.1 From a3f2af2547884e02f7e43f995a6c442a4e54f1ea Mon Sep 17 00:00:00 2001 From: Pavitra Kumar Date: Fri, 10 Oct 2014 15:19:46 +0000 Subject: dm stripe: fix potential for leak in stripe_ctr error path Fix a potential struct stripe_c leak that would occur if the chunk_size exceeded the maximum allowed by dm_set_target_max_io_len (UINT_MAX). However, in practice there is no possibility of this occuring given that chunk_size is of type uint32_t. But it is good to fix this to future-proof in case dm_set_target_max_io_len's implementation were to change. Signed-off-by: Pavitra Kumar Signed-off-by: Mike Snitzer --- drivers/md/dm-stripe.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index d1600d2..f8b37d4 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -159,8 +159,10 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv) sc->stripes_shift = __ffs(stripes); r = dm_set_target_max_io_len(ti, chunk_size); - if (r) + if (r) { + kfree(sc); return r; + } ti->num_flush_bios = stripes; ti->num_discard_bios = stripes; -- cgit v1.1 From 865f6d1974ddd9eff7e10820c4f9f7c7179d6659 Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Thu, 9 Oct 2014 11:19:25 -0700 Subject: spi: spidev: Use separate TX and RX bounce buffers By using separate TX and RX bounce buffers, we avoid potential cache flush and invalidation sequence issue that may be encountered when a single bounce buffer is shared between TX and RX Signed-off-by: Ray Jui Reviewed-by: JD (Jiandong) Zheng Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 79 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index e3bc23b..e50039f 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -82,10 +82,11 @@ struct spidev_data { struct spi_device *spi; struct list_head device_entry; - /* buffer is NULL unless this device is open (users > 0) */ + /* TX/RX buffers are NULL unless this device is open (users > 0) */ struct mutex buf_lock; unsigned users; - u8 *buffer; + u8 *tx_buffer; + u8 *rx_buffer; }; static LIST_HEAD(device_list); @@ -135,7 +136,7 @@ static inline ssize_t spidev_sync_write(struct spidev_data *spidev, size_t len) { struct spi_transfer t = { - .tx_buf = spidev->buffer, + .tx_buf = spidev->tx_buffer, .len = len, }; struct spi_message m; @@ -149,7 +150,7 @@ static inline ssize_t spidev_sync_read(struct spidev_data *spidev, size_t len) { struct spi_transfer t = { - .rx_buf = spidev->buffer, + .rx_buf = spidev->rx_buffer, .len = len, }; struct spi_message m; @@ -179,7 +180,7 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) if (status > 0) { unsigned long missing; - missing = copy_to_user(buf, spidev->buffer, status); + missing = copy_to_user(buf, spidev->rx_buffer, status); if (missing == status) status = -EFAULT; else @@ -206,7 +207,7 @@ spidev_write(struct file *filp, const char __user *buf, spidev = filp->private_data; mutex_lock(&spidev->buf_lock); - missing = copy_from_user(spidev->buffer, buf, count); + missing = copy_from_user(spidev->tx_buffer, buf, count); if (missing == 0) status = spidev_sync_write(spidev, count); else @@ -224,7 +225,7 @@ static int spidev_message(struct spidev_data *spidev, struct spi_transfer *k_tmp; struct spi_ioc_transfer *u_tmp; unsigned n, total; - u8 *buf; + u8 *tx_buf, *rx_buf; int status = -EFAULT; spi_message_init(&msg); @@ -236,7 +237,8 @@ static int spidev_message(struct spidev_data *spidev, * We walk the array of user-provided transfers, using each one * to initialize a kernel version of the same transfer. */ - buf = spidev->buffer; + tx_buf = spidev->tx_buffer; + rx_buf = spidev->rx_buffer; total = 0; for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; n; @@ -250,20 +252,21 @@ static int spidev_message(struct spidev_data *spidev, } if (u_tmp->rx_buf) { - k_tmp->rx_buf = buf; + k_tmp->rx_buf = rx_buf; if (!access_ok(VERIFY_WRITE, (u8 __user *) (uintptr_t) u_tmp->rx_buf, u_tmp->len)) goto done; } if (u_tmp->tx_buf) { - k_tmp->tx_buf = buf; - if (copy_from_user(buf, (const u8 __user *) + k_tmp->tx_buf = tx_buf; + if (copy_from_user(tx_buf, (const u8 __user *) (uintptr_t) u_tmp->tx_buf, u_tmp->len)) goto done; } - buf += k_tmp->len; + tx_buf += k_tmp->len; + rx_buf += k_tmp->len; k_tmp->cs_change = !!u_tmp->cs_change; k_tmp->tx_nbits = u_tmp->tx_nbits; @@ -290,17 +293,17 @@ static int spidev_message(struct spidev_data *spidev, goto done; /* copy any rx data out of bounce buffer */ - buf = spidev->buffer; + rx_buf = spidev->rx_buffer; for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { if (u_tmp->rx_buf) { if (__copy_to_user((u8 __user *) - (uintptr_t) u_tmp->rx_buf, buf, + (uintptr_t) u_tmp->rx_buf, rx_buf, u_tmp->len)) { status = -EFAULT; goto done; } } - buf += u_tmp->len; + rx_buf += u_tmp->len; } status = total; @@ -508,22 +511,41 @@ static int spidev_open(struct inode *inode, struct file *filp) break; } } - if (status == 0) { - if (!spidev->buffer) { - spidev->buffer = kmalloc(bufsiz, GFP_KERNEL); - if (!spidev->buffer) { + + if (status) { + pr_debug("spidev: nothing for minor %d\n", iminor(inode)); + goto err_find_dev; + } + + if (!spidev->tx_buffer) { + spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL); + if (!spidev->tx_buffer) { dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); status = -ENOMEM; + goto err_find_dev; } } - if (status == 0) { - spidev->users++; - filp->private_data = spidev; - nonseekable_open(inode, filp); + + if (!spidev->rx_buffer) { + spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL); + if (!spidev->rx_buffer) { + dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); + status = -ENOMEM; + goto err_alloc_rx_buf; } - } else - pr_debug("spidev: nothing for minor %d\n", iminor(inode)); + } + + spidev->users++; + filp->private_data = spidev; + nonseekable_open(inode, filp); + + mutex_unlock(&device_list_lock); + return 0; +err_alloc_rx_buf: + kfree(spidev->tx_buffer); + spidev->tx_buffer = NULL; +err_find_dev: mutex_unlock(&device_list_lock); return status; } @@ -542,8 +564,11 @@ static int spidev_release(struct inode *inode, struct file *filp) if (!spidev->users) { int dofree; - kfree(spidev->buffer); - spidev->buffer = NULL; + kfree(spidev->tx_buffer); + spidev->tx_buffer = NULL; + + kfree(spidev->rx_buffer); + spidev->rx_buffer = NULL; /* ... after we unbound from the underlying device? */ spin_lock_irq(&spidev->spi_lock); -- cgit v1.1 From 3ffa6158f002e096d28ede71be4e0ee8ab20baa2 Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Thu, 9 Oct 2014 11:44:54 -0700 Subject: spi: pl022: Fix incorrect dma_unmap_sg When mapped RX DMA entries are unmapped in an error condition when DMA is firstly configured in the driver, the number of TX DMA entries was passed in, which is incorrect Signed-off-by: Ray Jui Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/spi/spi-pl022.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index f35f723..fc2dd84 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -1106,7 +1106,7 @@ err_rxdesc: pl022->sgt_tx.nents, DMA_TO_DEVICE); err_tx_sgmap: dma_unmap_sg(rxchan->device->dev, pl022->sgt_rx.sgl, - pl022->sgt_tx.nents, DMA_FROM_DEVICE); + pl022->sgt_rx.nents, DMA_FROM_DEVICE); err_rx_sgmap: sg_free_table(&pl022->sgt_tx); err_alloc_tx_sg: -- cgit v1.1 From 60779143b5451095b4bbfb021d39955cb4794913 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 13 Oct 2014 11:34:13 -0400 Subject: Revert "drm/radeon: drop btc_get_max_clock_from_voltage_dependency_table" This reverts commit fc9dfeb1383287631ad5c5a676a2558b799db6e9. There are still some stability problems on some SI boards so bring this back. --- drivers/gpu/drm/radeon/btc_dpm.c | 17 +++++++++++++++++ drivers/gpu/drm/radeon/btc_dpm.h | 2 ++ 2 files changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index 300d971..b718160 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -1170,6 +1170,23 @@ static const struct radeon_blacklist_clocks btc_blacklist_clocks[] = { 25000, 30000, RADEON_SCLK_UP } }; +void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table, + u32 *max_clock) +{ + u32 i, clock = 0; + + if ((table == NULL) || (table->count == 0)) { + *max_clock = clock; + return; + } + + for (i = 0; i < table->count; i++) { + if (clock < table->entries[i].clk) + clock = table->entries[i].clk; + } + *max_clock = clock; +} + void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table, u32 clock, u16 max_voltage, u16 *voltage) { diff --git a/drivers/gpu/drm/radeon/btc_dpm.h b/drivers/gpu/drm/radeon/btc_dpm.h index 1a15e0e..3b6f12b 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.h +++ b/drivers/gpu/drm/radeon/btc_dpm.h @@ -46,6 +46,8 @@ void btc_adjust_clock_combinations(struct radeon_device *rdev, struct rv7xx_pl *pl); void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table, u32 clock, u16 max_voltage, u16 *voltage); +void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table, + u32 *max_clock); void btc_apply_voltage_delta_rules(struct radeon_device *rdev, u16 max_vddc, u16 max_vddci, u16 *vddc, u16 *vddci); -- cgit v1.1 From 1db7802418596880b51d78408f10f25e6fbd8656 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 13 Oct 2014 11:35:06 -0400 Subject: Revert "drm/radeon/dpm: drop clk/voltage dependency filters for SI" This reverts commit 186b1b2ba2a0684e3d2d3703427a993a3b35b16d. There are still some stability problems on some SI boards so bring this back. --- drivers/gpu/drm/radeon/si_dpm.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 9e4d5d7..70e61ff 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -2916,6 +2916,7 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, bool disable_sclk_switching = false; u32 mclk, sclk; u16 vddc, vddci; + u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc; int i; if ((rdev->pm.dpm.new_active_crtc_count > 1) || @@ -2949,6 +2950,29 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, } } + /* limit clocks to max supported clocks based on voltage dependency tables */ + btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk, + &max_sclk_vddc); + btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, + &max_mclk_vddci); + btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, + &max_mclk_vddc); + + for (i = 0; i < ps->performance_level_count; i++) { + if (max_sclk_vddc) { + if (ps->performance_levels[i].sclk > max_sclk_vddc) + ps->performance_levels[i].sclk = max_sclk_vddc; + } + if (max_mclk_vddci) { + if (ps->performance_levels[i].mclk > max_mclk_vddci) + ps->performance_levels[i].mclk = max_mclk_vddci; + } + if (max_mclk_vddc) { + if (ps->performance_levels[i].mclk > max_mclk_vddc) + ps->performance_levels[i].mclk = max_mclk_vddc; + } + } + /* XXX validate the min clocks required for display */ if (disable_mclk_switching) { -- cgit v1.1 From 8000ebf76224a01adab9c1c16c341f54706292d5 Mon Sep 17 00:00:00 2001 From: Ebru Akagunduz Date: Wed, 8 Oct 2014 17:09:46 +0300 Subject: power: ab8500_fg.c: use 64-bit time types This patch changes 32-bit time types to 64-bit in drivers/power/ab8500_fg.c timespec and time_t can only represent signed 32-bit dates but the driver should represent dates that are after January 2038. So used time64.h header file and its proper types and functions. Use time64_t type instead of __kernel_time_t for time_stamps variable of ab8500_fg_avg_cap struct Signed-off-by: Ebru Akagunduz Acked-by: Arnd Bergmann Signed-off-by: Sebastian Reichel --- drivers/power/ab8500_fg.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 217da4b..99a78d3 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -108,7 +109,7 @@ enum ab8500_fg_calibration_state { struct ab8500_fg_avg_cap { int avg; int samples[NBR_AVG_SAMPLES]; - __kernel_time_t time_stamps[NBR_AVG_SAMPLES]; + time64_t time_stamps[NBR_AVG_SAMPLES]; int pos; int nbr_samples; int sum; @@ -386,15 +387,15 @@ static int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr) */ static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample) { - struct timespec ts; + struct timespec64 ts64; struct ab8500_fg_avg_cap *avg = &di->avg_cap; - getnstimeofday(&ts); + getnstimeofday64(&ts64); do { avg->sum += sample - avg->samples[avg->pos]; avg->samples[avg->pos] = sample; - avg->time_stamps[avg->pos] = ts.tv_sec; + avg->time_stamps[avg->pos] = ts64.tv_sec; avg->pos++; if (avg->pos == NBR_AVG_SAMPLES) @@ -407,7 +408,7 @@ static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample) * Check the time stamp for each sample. If too old, * replace with latest sample */ - } while (ts.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]); + } while (ts64.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]); avg->avg = avg->sum / avg->nbr_samples; @@ -446,14 +447,14 @@ static void ab8500_fg_clear_cap_samples(struct ab8500_fg *di) static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample) { int i; - struct timespec ts; + struct timespec64 ts64; struct ab8500_fg_avg_cap *avg = &di->avg_cap; - getnstimeofday(&ts); + getnstimeofday64(&ts64); for (i = 0; i < NBR_AVG_SAMPLES; i++) { avg->samples[i] = sample; - avg->time_stamps[i] = ts.tv_sec; + avg->time_stamps[i] = ts64.tv_sec; } avg->pos = 0; -- cgit v1.1 From f9cfd52262d36a55b39d41e2b0faae632ad57e4c Mon Sep 17 00:00:00 2001 From: Addy Ke Date: Wed, 15 Oct 2014 19:25:49 +0800 Subject: spi/rockchip: fix bug that case spi can't go as fast as slave request Because the minimum divisor in rk3x's spi controller is 2, if spi_clk is less than 2 * sclk_out, we can't get the right divisor. So we must set spi_clk again to match slave request. Signed-off-by: Addy Ke Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index f96ea8a..3044c6c 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -145,6 +145,9 @@ #define RXBUSY (1 << 0) #define TXBUSY (1 << 1) +/* sclk_out: spi master internal logic in rk3x can support 50Mhz */ +#define MAX_SCLK_OUT 50000000 + enum rockchip_ssi_type { SSI_MOTO_SPI = 0, SSI_TI_SSP, @@ -496,6 +499,15 @@ static void rockchip_spi_config(struct rockchip_spi *rs) dmacr |= RF_DMA_EN; } + if (WARN_ON(rs->speed > MAX_SCLK_OUT)) + rs->speed = MAX_SCLK_OUT; + + /* the minimum divsor is 2 */ + if (rs->max_freq < 2 * rs->speed) { + clk_set_rate(rs->spiclk, 2 * rs->speed); + rs->max_freq = clk_get_rate(rs->spiclk); + } + /* div doesn't support odd number */ div = max_t(u32, rs->max_freq / rs->speed, 1); div = (div + 1) & 0xfffe; -- cgit v1.1 From c28be31b11f56b3bb62490dfe5304eaa2724afc2 Mon Sep 17 00:00:00 2001 From: Addy Ke Date: Wed, 15 Oct 2014 19:26:18 +0800 Subject: spi/rockchip: fix bug that cause spi transfer timed out in DMA duplex mode In rx mode, dma must be prepared before spi is enabled. But in tx and tr mode, spi must be enabled first. Signed-off-by: Addy Ke Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 3044c6c..153269b 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -328,6 +328,8 @@ static int rockchip_spi_unprepare_message(struct spi_master *master, spin_unlock_irqrestore(&rs->lock, flags); + spi_enable_chip(rs, 0); + return 0; } @@ -384,6 +386,8 @@ static int rockchip_spi_pio_transfer(struct rockchip_spi *rs) if (rs->tx) wait_for_idle(rs); + spi_enable_chip(rs, 0); + return 0; } @@ -395,8 +399,10 @@ static void rockchip_spi_dma_rxcb(void *data) spin_lock_irqsave(&rs->lock, flags); rs->state &= ~RXBUSY; - if (!(rs->state & TXBUSY)) + if (!(rs->state & TXBUSY)) { + spi_enable_chip(rs, 0); spi_finalize_current_transfer(rs->master); + } spin_unlock_irqrestore(&rs->lock, flags); } @@ -512,8 +518,6 @@ static void rockchip_spi_config(struct rockchip_spi *rs) div = max_t(u32, rs->max_freq / rs->speed, 1); div = (div + 1) & 0xfffe; - spi_enable_chip(rs, 0); - writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0); writel_relaxed(rs->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); @@ -527,8 +531,6 @@ static void rockchip_spi_config(struct rockchip_spi *rs) spi_set_clk(rs, div); dev_dbg(rs->dev, "cr0 0x%x, div %d\n", cr0, div); - - spi_enable_chip(rs, 1); } static int rockchip_spi_transfer_one( @@ -536,7 +538,7 @@ static int rockchip_spi_transfer_one( struct spi_device *spi, struct spi_transfer *xfer) { - int ret = 0; + int ret = 1; struct rockchip_spi *rs = spi_master_get_devdata(master); WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) && @@ -568,17 +570,27 @@ static int rockchip_spi_transfer_one( rs->tmode = CR0_XFM_RO; /* we need prepare dma before spi was enabled */ - if (master->can_dma && master->can_dma(master, spi, xfer)) { + if (master->can_dma && master->can_dma(master, spi, xfer)) rs->use_dma = 1; - rockchip_spi_prepare_dma(rs); - } else { + else rs->use_dma = 0; - } rockchip_spi_config(rs); - if (!rs->use_dma) + if (rs->use_dma) { + if (rs->tmode == CR0_XFM_RO) { + /* rx: dma must be prepared first */ + rockchip_spi_prepare_dma(rs); + spi_enable_chip(rs, 1); + } else { + /* tx or tr: spi must be enabled first */ + spi_enable_chip(rs, 1); + rockchip_spi_prepare_dma(rs); + } + } else { + spi_enable_chip(rs, 1); ret = rockchip_spi_pio_transfer(rs); + } return ret; } -- cgit v1.1 From c9eeb7b813c9525cda34b61dcf4455c52fc58890 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Tue, 14 Oct 2014 17:04:48 +0200 Subject: s390/hmcdrv: Restrict s390 HMC driver to S390 arch This driver is only usable on 64-bit s390 machines. Mark the Kconfig dependencies to that users on other architectures are not prompted for it. Fixes: 8f933b1043e1e5 ("s390/hmcdrv: HMC drive CD/DVD access") Signed-off-by: Josh Boyer Signed-off-by: Martin Schwidefsky --- drivers/s390/char/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index dc24ecf..db2cb1f 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -105,7 +105,7 @@ config SCLP_ASYNC config HMC_DRV def_tristate m prompt "Support for file transfers from HMC drive CD/DVD-ROM" - depends on 64BIT + depends on S390 && 64BIT select CRC16 help This option enables support for file transfers from a Hardware -- cgit v1.1 From e17ac6db2ef99388b750b2141c11974dc5742913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 9 Oct 2014 19:37:15 +0300 Subject: drm/i915: Don't trust the DP_DETECT bit for eDP ports on CHV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On CHV the display DDC pins may be muxed to an alternate function if there's no need for DDC on a specific port, which is the case for eDP ports since there's no way to plug in a DP++ HDMI dongle. This causes problems when trying to determine if the port is present since the the DP_DETECTED bit is the latched state of the DDC SDA pin at boot. If the DDC pins are muxed to an alternate function the bit may indicate that the port isn't present. To work around this look at the VBT as well as the DP_DETECTED bit to determine if we should attempt registering an eDP port. Do this only for ports B and C since port D doesn't support eDP (no PPS/BLC). In theory someone could also wire up a normal DP port w/o DDC lines. That would just mean that simple DP++ HDMI dongles wouldn't work on such a port. With this change we would still fail to register such DP ports. But let's hope no one wires their board in such a way, and if they do we can extend the VBT checks to cover normal DP ports as well. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=84265 Signed-off-by: Ville Syrjälä Reviewed-by: Damien Lespiau Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/intel_display.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6a0cc36..c9e2209 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -12354,27 +12354,36 @@ static void intel_setup_outputs(struct drm_device *dev) if (I915_READ(PCH_DP_D) & DP_DETECTED) intel_dp_init(dev, PCH_DP_D, PORT_D); } else if (IS_VALLEYVIEW(dev)) { - if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIB) & SDVO_DETECTED) { + /* + * The DP_DETECTED bit is the latched state of the DDC + * SDA pin at boot. However since eDP doesn't require DDC + * (no way to plug in a DP->HDMI dongle) the DDC pins for + * eDP ports may have been muxed to an alternate function. + * Thus we can't rely on the DP_DETECTED bit alone to detect + * eDP ports. Consult the VBT as well as DP_DETECTED to + * detect eDP ports. + */ + if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIB) & SDVO_DETECTED) intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIB, PORT_B); - if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED) - intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B); - } + if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED || + intel_dp_is_edp(dev, PORT_B)) + intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B); - if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIC) & SDVO_DETECTED) { + if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIC) & SDVO_DETECTED) intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIC, PORT_C); - if (I915_READ(VLV_DISPLAY_BASE + DP_C) & DP_DETECTED) - intel_dp_init(dev, VLV_DISPLAY_BASE + DP_C, PORT_C); - } + if (I915_READ(VLV_DISPLAY_BASE + DP_C) & DP_DETECTED || + intel_dp_is_edp(dev, PORT_C)) + intel_dp_init(dev, VLV_DISPLAY_BASE + DP_C, PORT_C); if (IS_CHERRYVIEW(dev)) { - if (I915_READ(VLV_DISPLAY_BASE + CHV_HDMID) & SDVO_DETECTED) { + if (I915_READ(VLV_DISPLAY_BASE + CHV_HDMID) & SDVO_DETECTED) intel_hdmi_init(dev, VLV_DISPLAY_BASE + CHV_HDMID, PORT_D); - if (I915_READ(VLV_DISPLAY_BASE + DP_D) & DP_DETECTED) - intel_dp_init(dev, VLV_DISPLAY_BASE + DP_D, PORT_D); - } + /* eDP not supported on port D, so don't check VBT */ + if (I915_READ(VLV_DISPLAY_BASE + DP_D) & DP_DETECTED) + intel_dp_init(dev, VLV_DISPLAY_BASE + DP_D, PORT_D); } intel_dsi_init(dev); -- cgit v1.1 From 07c338ce98263a5af631b991dd8f96cff6ca2548 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 2 Oct 2014 11:16:32 +0300 Subject: drm/i915: fix short vs. long hpd detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix short vs. long hpd detection for non-g4x and non-pch split platforms. Broken since introduction in commit 13cf550448b58abf8f44f5d6a560f2d20871c965 Author: Dave Airlie Date: Wed Jun 18 11:29:35 2014 +1000 drm/i915: rework digital port IRQ handling (v2) Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=83175 Reviewed-by: Ville Syrjälä Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_irq.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 54bf5470..f66392b 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1711,7 +1711,7 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, #define HPD_STORM_DETECT_PERIOD 1000 #define HPD_STORM_THRESHOLD 5 -static int ilk_port_to_hotplug_shift(enum port port) +static int pch_port_to_hotplug_shift(enum port port) { switch (port) { case PORT_A: @@ -1727,7 +1727,7 @@ static int ilk_port_to_hotplug_shift(enum port port) } } -static int g4x_port_to_hotplug_shift(enum port port) +static int i915_port_to_hotplug_shift(enum port port) { switch (port) { case PORT_A: @@ -1785,12 +1785,12 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev, if (port && dev_priv->hpd_irq_port[port]) { bool long_hpd; - if (IS_G4X(dev)) { - dig_shift = g4x_port_to_hotplug_shift(port); - long_hpd = (hotplug_trigger >> dig_shift) & PORTB_HOTPLUG_LONG_DETECT; - } else { - dig_shift = ilk_port_to_hotplug_shift(port); + if (HAS_PCH_SPLIT(dev)) { + dig_shift = pch_port_to_hotplug_shift(port); long_hpd = (dig_hotplug_reg >> dig_shift) & PORTB_HOTPLUG_LONG_DETECT; + } else { + dig_shift = i915_port_to_hotplug_shift(port); + long_hpd = (hotplug_trigger >> dig_shift) & PORTB_HOTPLUG_LONG_DETECT; } DRM_DEBUG_DRIVER("digital hpd port %c - %s\n", -- cgit v1.1 From 83d04c39f9048807a8500e575ae3f1718a3f45bb Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 13 Oct 2014 13:23:48 -0400 Subject: drm/radeon: initialize sadb to NULL in the audio code Fixes kfree of the sadb buffer when it's NULL. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/dce3_1_afmt.c | 2 +- drivers/gpu/drm/radeon/dce6_afmt.c | 2 +- drivers/gpu/drm/radeon/evergreen_hdmi.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/radeon/dce3_1_afmt.c b/drivers/gpu/drm/radeon/dce3_1_afmt.c index 950af15..6b1dbec 100644 --- a/drivers/gpu/drm/radeon/dce3_1_afmt.c +++ b/drivers/gpu/drm/radeon/dce3_1_afmt.c @@ -32,7 +32,7 @@ static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder) struct drm_connector *connector; struct radeon_connector *radeon_connector = NULL; u32 tmp; - u8 *sadb; + u8 *sadb = NULL; int sad_count; list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) { diff --git a/drivers/gpu/drm/radeon/dce6_afmt.c b/drivers/gpu/drm/radeon/dce6_afmt.c index c0bbf68..960a5f0 100644 --- a/drivers/gpu/drm/radeon/dce6_afmt.c +++ b/drivers/gpu/drm/radeon/dce6_afmt.c @@ -155,7 +155,7 @@ void dce6_afmt_write_speaker_allocation(struct drm_encoder *encoder) struct drm_connector *connector; struct radeon_connector *radeon_connector = NULL; u32 offset, tmp; - u8 *sadb; + u8 *sadb = NULL; int sad_count; if (!dig || !dig->afmt || !dig->afmt->pin) diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c index 2514d65..f6a5c30 100644 --- a/drivers/gpu/drm/radeon/evergreen_hdmi.c +++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c @@ -133,7 +133,7 @@ static void dce4_afmt_write_speaker_allocation(struct drm_encoder *encoder) struct drm_connector *connector; struct radeon_connector *radeon_connector = NULL; u32 tmp; - u8 *sadb; + u8 *sadb = NULL; int sad_count; list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) { -- cgit v1.1 From 4910403836ded89803fab201d4b5caaa85de3a89 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 13 Oct 2014 11:51:50 -0400 Subject: drm/radeon: fix speaker allocation setup If the sad_count is 0, set the hw to stereo and change the error message to a warn. A lot of monitors don't set the speaker allocation block. Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/dce3_1_afmt.c | 4 ++-- drivers/gpu/drm/radeon/dce6_afmt.c | 6 +++--- drivers/gpu/drm/radeon/evergreen_hdmi.c | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/radeon/dce3_1_afmt.c b/drivers/gpu/drm/radeon/dce3_1_afmt.c index 6b1dbec..2fe8cfc 100644 --- a/drivers/gpu/drm/radeon/dce3_1_afmt.c +++ b/drivers/gpu/drm/radeon/dce3_1_afmt.c @@ -49,8 +49,8 @@ static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder) sad_count = drm_edid_to_speaker_allocation(radeon_connector->edid, &sadb); if (sad_count < 0) { - DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); - return; + DRM_DEBUG("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); + sad_count = 0; } /* program the speaker allocation */ diff --git a/drivers/gpu/drm/radeon/dce6_afmt.c b/drivers/gpu/drm/radeon/dce6_afmt.c index 960a5f0..f312edf 100644 --- a/drivers/gpu/drm/radeon/dce6_afmt.c +++ b/drivers/gpu/drm/radeon/dce6_afmt.c @@ -176,9 +176,9 @@ void dce6_afmt_write_speaker_allocation(struct drm_encoder *encoder) } sad_count = drm_edid_to_speaker_allocation(radeon_connector_edid(connector), &sadb); - if (sad_count <= 0) { - DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); - return; + if (sad_count < 0) { + DRM_DEBUG("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); + sad_count = 0; } /* program the speaker allocation */ diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c index f6a5c30..53abd9b 100644 --- a/drivers/gpu/drm/radeon/evergreen_hdmi.c +++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c @@ -149,9 +149,9 @@ static void dce4_afmt_write_speaker_allocation(struct drm_encoder *encoder) } sad_count = drm_edid_to_speaker_allocation(radeon_connector_edid(connector), &sadb); - if (sad_count <= 0) { - DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); - return; + if (sad_count < 0) { + DRM_DEBUG("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); + sad_count = 0; } /* program the speaker allocation */ -- cgit v1.1 From adfed2b0587289013f8143c54913ddfd44ac1fd3 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 13 Oct 2014 13:20:02 -0400 Subject: drm/radeon: use gart memory for DMA ring tests Avoids HDP cache flush issues when using vram which can cause ring test failures on certain boards. Signed-off-by: Alex Deucher Cc: Alexander Fyodorov Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/cik_sdma.c | 21 ++++++++++++--------- drivers/gpu/drm/radeon/r600_dma.c | 21 ++++++++++++--------- drivers/gpu/drm/radeon/radeon.h | 2 ++ 3 files changed, 26 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/radeon/cik_sdma.c b/drivers/gpu/drm/radeon/cik_sdma.c index c473c91..7deb2ef 100644 --- a/drivers/gpu/drm/radeon/cik_sdma.c +++ b/drivers/gpu/drm/radeon/cik_sdma.c @@ -618,16 +618,19 @@ int cik_sdma_ring_test(struct radeon_device *rdev, { unsigned i; int r; - void __iomem *ptr = (void *)rdev->vram_scratch.ptr; + unsigned index; u32 tmp; + u64 gpu_addr; - if (!ptr) { - DRM_ERROR("invalid vram scratch pointer\n"); - return -EINVAL; - } + if (ring->idx == R600_RING_TYPE_DMA_INDEX) + index = R600_WB_DMA_RING_TEST_OFFSET; + else + index = CAYMAN_WB_DMA1_RING_TEST_OFFSET; + + gpu_addr = rdev->wb.gpu_addr + index; tmp = 0xCAFEDEAD; - writel(tmp, ptr); + rdev->wb.wb[index/4] = cpu_to_le32(tmp); r = radeon_ring_lock(rdev, ring, 5); if (r) { @@ -635,14 +638,14 @@ int cik_sdma_ring_test(struct radeon_device *rdev, return r; } radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0)); - radeon_ring_write(ring, rdev->vram_scratch.gpu_addr & 0xfffffffc); - radeon_ring_write(ring, upper_32_bits(rdev->vram_scratch.gpu_addr)); + radeon_ring_write(ring, lower_32_bits(gpu_addr)); + radeon_ring_write(ring, upper_32_bits(gpu_addr)); radeon_ring_write(ring, 1); /* number of DWs to follow */ radeon_ring_write(ring, 0xDEADBEEF); radeon_ring_unlock_commit(rdev, ring, false); for (i = 0; i < rdev->usec_timeout; i++) { - tmp = readl(ptr); + tmp = le32_to_cpu(rdev->wb.wb[index/4]); if (tmp == 0xDEADBEEF) break; DRM_UDELAY(1); diff --git a/drivers/gpu/drm/radeon/r600_dma.c b/drivers/gpu/drm/radeon/r600_dma.c index a49db83..d9375a3 100644 --- a/drivers/gpu/drm/radeon/r600_dma.c +++ b/drivers/gpu/drm/radeon/r600_dma.c @@ -241,16 +241,19 @@ int r600_dma_ring_test(struct radeon_device *rdev, { unsigned i; int r; - void __iomem *ptr = (void *)rdev->vram_scratch.ptr; + unsigned index; u32 tmp; + u64 gpu_addr; - if (!ptr) { - DRM_ERROR("invalid vram scratch pointer\n"); - return -EINVAL; - } + if (ring->idx == R600_RING_TYPE_DMA_INDEX) + index = R600_WB_DMA_RING_TEST_OFFSET; + else + index = CAYMAN_WB_DMA1_RING_TEST_OFFSET; + + gpu_addr = rdev->wb.gpu_addr + index; tmp = 0xCAFEDEAD; - writel(tmp, ptr); + rdev->wb.wb[index/4] = cpu_to_le32(tmp); r = radeon_ring_lock(rdev, ring, 4); if (r) { @@ -258,13 +261,13 @@ int r600_dma_ring_test(struct radeon_device *rdev, return r; } radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 1)); - radeon_ring_write(ring, rdev->vram_scratch.gpu_addr & 0xfffffffc); - radeon_ring_write(ring, upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xff); + radeon_ring_write(ring, lower_32_bits(gpu_addr)); + radeon_ring_write(ring, upper_32_bits(gpu_addr) & 0xff); radeon_ring_write(ring, 0xDEADBEEF); radeon_ring_unlock_commit(rdev, ring, false); for (i = 0; i < rdev->usec_timeout; i++) { - tmp = readl(ptr); + tmp = le32_to_cpu(rdev->wb.wb[index/4]); if (tmp == 0xDEADBEEF) break; DRM_UDELAY(1); diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index e01424f..588672d 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1132,6 +1132,8 @@ struct radeon_wb { #define R600_WB_EVENT_OFFSET 3072 #define CIK_WB_CP1_WPTR_OFFSET 3328 #define CIK_WB_CP2_WPTR_OFFSET 3584 +#define R600_WB_DMA_RING_TEST_OFFSET 3588 +#define CAYMAN_WB_DMA1_RING_TEST_OFFSET 3592 /** * struct radeon_pm - power management datas -- cgit v1.1 From 9ace2ef7b78e573cedead0f08052b028181e6eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 9 Oct 2014 15:03:03 +0900 Subject: drm/ttm: Don't skip fpfn check if lpfn is 0 in ttm_bo_mem_compat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Christian König Signed-off-by: Michel Dänzer Signed-off-by: Alex Deucher --- drivers/gpu/drm/ttm/ttm_bo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 8f5cec6..ba74a11 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -994,9 +994,9 @@ static bool ttm_bo_mem_compat(struct ttm_placement *placement, for (i = 0; i < placement->num_placement; i++) { const struct ttm_place *heap = &placement->placement[i]; - if (mem->mm_node && heap->lpfn != 0 && + if (mem->mm_node && (mem->start < heap->fpfn || - mem->start + mem->num_pages > heap->lpfn)) + (heap->lpfn != 0 && (mem->start + mem->num_pages) > heap->lpfn))) continue; *new_flags = heap->flags; @@ -1007,9 +1007,9 @@ static bool ttm_bo_mem_compat(struct ttm_placement *placement, for (i = 0; i < placement->num_busy_placement; i++) { const struct ttm_place *heap = &placement->busy_placement[i]; - if (mem->mm_node && heap->lpfn != 0 && + if (mem->mm_node && (mem->start < heap->fpfn || - mem->start + mem->num_pages > heap->lpfn)) + (heap->lpfn != 0 && (mem->start + mem->num_pages) > heap->lpfn))) continue; *new_flags = heap->flags; -- cgit v1.1 From e300180f71037fd9ed1ca967006fd9f3ee466bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 9 Oct 2014 15:02:59 +0900 Subject: drm/ttm: Don't evict BOs outside of the requested placement range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The radeon driver uses placement range restrictions for several reasons, in particular to make sure BOs in VRAM can be accessed by the CPU, e.g. during a page fault. Without this change, TTM could evict other BOs while trying to satisfy the requested placement, even if the evicted BOs were outside of the requested placement range. Doing so didn't free up any space in the requested placement range, so the (potentially high) eviction cost was incurred for no benefit. Nominating for stable because radeon driver changes in 3.17 made this much more noticeable than before. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=84662 Cc: stable@vger.kernel.org Signed-off-by: Michel Dänzer Signed-off-by: Alex Deucher --- drivers/gpu/drm/ttm/ttm_bo.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index ba74a11..d395b0b 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -709,6 +709,7 @@ out: static int ttm_mem_evict_first(struct ttm_bo_device *bdev, uint32_t mem_type, + const struct ttm_place *place, bool interruptible, bool no_wait_gpu) { @@ -720,8 +721,21 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, spin_lock(&glob->lru_lock); list_for_each_entry(bo, &man->lru, lru) { ret = __ttm_bo_reserve(bo, false, true, false, NULL); - if (!ret) + if (!ret) { + if (place && (place->fpfn || place->lpfn)) { + /* Don't evict this BO if it's outside of the + * requested placement range + */ + if (place->fpfn >= (bo->mem.start + bo->mem.size) || + (place->lpfn && place->lpfn <= bo->mem.start)) { + __ttm_bo_unreserve(bo); + ret = -EBUSY; + continue; + } + } + break; + } } if (ret) { @@ -782,7 +796,7 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, return ret; if (mem->mm_node) break; - ret = ttm_mem_evict_first(bdev, mem_type, + ret = ttm_mem_evict_first(bdev, mem_type, place, interruptible, no_wait_gpu); if (unlikely(ret != 0)) return ret; @@ -1233,7 +1247,7 @@ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, spin_lock(&glob->lru_lock); while (!list_empty(&man->lru)) { spin_unlock(&glob->lru_lock); - ret = ttm_mem_evict_first(bdev, mem_type, false, false); + ret = ttm_mem_evict_first(bdev, mem_type, NULL, false, false); if (ret) { if (allow_errors) { return ret; -- cgit v1.1 From 8e66e134e20b936179ea1535dd4ed19ec4f99dba Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 15 Oct 2014 17:20:55 -0400 Subject: drm/radeon: fix vm page table block size calculation The page offset is 12 bits. For example if we have an 8 GB VM, we'd need 33 bits. The number of bits needed for PD + PT is 21 (33 - 12 or log2(8) + 18), not 20 (log2(8) + 17). Noticed by Alexey during code review. Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/radeon_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 6fbab15..55065d8 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1126,7 +1126,7 @@ static void radeon_check_arguments(struct radeon_device *rdev) if (radeon_vm_block_size == -1) { /* Total bits covered by PD + PTs */ - unsigned bits = ilog2(radeon_vm_size) + 17; + unsigned bits = ilog2(radeon_vm_size) + 18; /* Make sure the PD is 4K in size up to 8GB address space. Above that split equal between PD and PTs */ -- cgit v1.1 From 01467a9b5e7ec7b9e30768bee16ea5861665015b Mon Sep 17 00:00:00 2001 From: Michele Curti Date: Tue, 14 Oct 2014 18:25:09 +0200 Subject: drm/radeon: reduce sparse false positive warnings include radeon_asic.h header file in the various xxx_dpm.c files to reduce sparse false positive warnings. Not so great patch in itself, but reducing warning count from 391 to 258 may help to see real problems.. Signed-off-by: Michele Curti Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/btc_dpm.c | 1 + drivers/gpu/drm/radeon/ci_dpm.c | 1 + drivers/gpu/drm/radeon/cypress_dpm.c | 1 + drivers/gpu/drm/radeon/ni_dpm.c | 1 + drivers/gpu/drm/radeon/r600_dpm.c | 1 + drivers/gpu/drm/radeon/rs780_dpm.c | 1 + drivers/gpu/drm/radeon/rv6xx_dpm.c | 1 + drivers/gpu/drm/radeon/rv770_dpm.c | 1 + drivers/gpu/drm/radeon/si_dpm.c | 1 + drivers/gpu/drm/radeon/sumo_dpm.c | 1 + drivers/gpu/drm/radeon/trinity_dpm.c | 1 + 11 files changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index b718160..0b2929d 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -24,6 +24,7 @@ #include "drmP.h" #include "radeon.h" +#include "radeon_asic.h" #include "btcd.h" #include "r600_dpm.h" #include "cypress_dpm.h" diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index f5c8c04..11a55e9 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -24,6 +24,7 @@ #include #include "drmP.h" #include "radeon.h" +#include "radeon_asic.h" #include "radeon_ucode.h" #include "cikd.h" #include "r600_dpm.h" diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c index 47d31e9..9aad032 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.c +++ b/drivers/gpu/drm/radeon/cypress_dpm.c @@ -24,6 +24,7 @@ #include "drmP.h" #include "radeon.h" +#include "radeon_asic.h" #include "evergreend.h" #include "r600_dpm.h" #include "cypress_dpm.h" diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 715b181..6d2f16c 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -23,6 +23,7 @@ #include "drmP.h" #include "radeon.h" +#include "radeon_asic.h" #include "nid.h" #include "r600_dpm.h" #include "ni_dpm.h" diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index 9c61b74..f6309bd 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -24,6 +24,7 @@ #include "drmP.h" #include "radeon.h" +#include "radeon_asic.h" #include "r600d.h" #include "r600_dpm.h" #include "atom.h" diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c index 02f7710..9031f4b 100644 --- a/drivers/gpu/drm/radeon/rs780_dpm.c +++ b/drivers/gpu/drm/radeon/rs780_dpm.c @@ -24,6 +24,7 @@ #include "drmP.h" #include "radeon.h" +#include "radeon_asic.h" #include "rs780d.h" #include "r600_dpm.h" #include "rs780_dpm.h" diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.c b/drivers/gpu/drm/radeon/rv6xx_dpm.c index e7045b0..6a5c233 100644 --- a/drivers/gpu/drm/radeon/rv6xx_dpm.c +++ b/drivers/gpu/drm/radeon/rv6xx_dpm.c @@ -24,6 +24,7 @@ #include "drmP.h" #include "radeon.h" +#include "radeon_asic.h" #include "rv6xxd.h" #include "r600_dpm.h" #include "rv6xx_dpm.h" diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c index 3c76e1d..755a8f9 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.c +++ b/drivers/gpu/drm/radeon/rv770_dpm.c @@ -24,6 +24,7 @@ #include "drmP.h" #include "radeon.h" +#include "radeon_asic.h" #include "rv770d.h" #include "r600_dpm.h" #include "rv770_dpm.h" diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 70e61ff..a53c2e7 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -23,6 +23,7 @@ #include "drmP.h" #include "radeon.h" +#include "radeon_asic.h" #include "sid.h" #include "r600_dpm.h" #include "si_dpm.h" diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index 3f0e8d7..1f8a883 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -23,6 +23,7 @@ #include "drmP.h" #include "radeon.h" +#include "radeon_asic.h" #include "sumod.h" #include "r600_dpm.h" #include "cypress_dpm.h" diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index 57f7800..b4ec5c4 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -23,6 +23,7 @@ #include "drmP.h" #include "radeon.h" +#include "radeon_asic.h" #include "trinityd.h" #include "r600_dpm.h" #include "trinity_dpm.h" -- cgit v1.1 From 9d28eb12447ee08bb5d1e8bb3195cf20e1ecd1c0 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 16 Oct 2014 14:45:20 -0400 Subject: dm bufio: change __GFP_IO to __GFP_FS in shrinker callbacks The shrinker uses gfp flags to indicate what kind of operation can the driver wait for. If __GFP_IO flag is present, the driver can wait for block I/O operations, if __GFP_FS flag is present, the driver can wait on operations involving the filesystem. dm-bufio tested for __GFP_IO. However, dm-bufio can run on a loop block device that makes calls into the filesystem. If __GFP_IO is present and __GFP_FS isn't, dm-bufio could still block on filesystem operations if it runs on a loop block device. The change from __GFP_IO to __GFP_FS supposedly fixes one observed (though unreproducible) deadlock involving dm-bufio and loop device. Signed-off-by: Mikulas Patocka Signed-off-by: Mike Snitzer Cc: stable@vger.kernel.org --- drivers/md/dm-bufio.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 9ea5b60..0be200b 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -1435,9 +1435,9 @@ static void drop_buffers(struct dm_bufio_client *c) /* * Test if the buffer is unused and too old, and commit it. - * At if noio is set, we must not do any I/O because we hold - * dm_bufio_clients_lock and we would risk deadlock if the I/O gets rerouted to - * different bufio client. + * And if GFP_NOFS is used, we must not do any I/O because we hold + * dm_bufio_clients_lock and we would risk deadlock if the I/O gets + * rerouted to different bufio client. */ static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp, unsigned long max_jiffies) @@ -1445,7 +1445,7 @@ static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp, if (jiffies - b->last_accessed < max_jiffies) return 0; - if (!(gfp & __GFP_IO)) { + if (!(gfp & __GFP_FS)) { if (test_bit(B_READING, &b->state) || test_bit(B_WRITING, &b->state) || test_bit(B_DIRTY, &b->state)) @@ -1487,7 +1487,7 @@ dm_bufio_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) unsigned long freed; c = container_of(shrink, struct dm_bufio_client, shrinker); - if (sc->gfp_mask & __GFP_IO) + if (sc->gfp_mask & __GFP_FS) dm_bufio_lock(c); else if (!dm_bufio_trylock(c)) return SHRINK_STOP; @@ -1504,7 +1504,7 @@ dm_bufio_shrink_count(struct shrinker *shrink, struct shrink_control *sc) unsigned long count; c = container_of(shrink, struct dm_bufio_client, shrinker); - if (sc->gfp_mask & __GFP_IO) + if (sc->gfp_mask & __GFP_FS) dm_bufio_lock(c); else if (!dm_bufio_trylock(c)) return 0; -- cgit v1.1 From d8054749c6795073cb427465a726213d45898f68 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 19 Jun 2014 15:43:29 +0800 Subject: Thermal: int340x thermal: select ACPI fan driver we share the same driver for both ACPI predefined Fan device and INT3404 Fan device, thus we should select the ACPI Fan driver when int340x thermal drivers are enabeld. Signed-off-by: Zhang Rui --- drivers/acpi/Kconfig | 2 +- drivers/thermal/Kconfig | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index d0f3265..b23fe37 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -144,7 +144,7 @@ config ACPI_VIDEO config ACPI_FAN tristate "Fan" - select THERMAL + depends on THERMAL default y help This driver supports ACPI fan devices, allowing user-mode diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 3a89292..e65ca2f 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -224,6 +224,7 @@ config INT340X_THERMAL depends on X86 && ACPI select THERMAL_GOV_USER_SPACE select ACPI_THERMAL_REL + select ACPI_FAN help Newer laptops and tablets that use ACPI may have thermal sensors and other devices with thermal control capabilities outside the core -- cgit v1.1 From 2c2bc7489ef330c920fbcee99468aa36a909998a Mon Sep 17 00:00:00 2001 From: Addy Ke Date: Fri, 17 Oct 2014 09:44:13 +0800 Subject: spi/rockchip: spi controller must be disabled in tx callback too Signed-off-by: Addy Ke Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 153269b..87bc16f 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -418,8 +418,10 @@ static void rockchip_spi_dma_txcb(void *data) spin_lock_irqsave(&rs->lock, flags); rs->state &= ~TXBUSY; - if (!(rs->state & RXBUSY)) + if (!(rs->state & RXBUSY)) { + spi_enable_chip(rs, 0); spi_finalize_current_transfer(rs->master); + } spin_unlock_irqrestore(&rs->lock, flags); } -- cgit v1.1 From d4c5efdb97773f59a2b711754ca0953f24516739 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 26 Aug 2014 23:16:35 -0400 Subject: random: add and use memzero_explicit() for clearing data zatimend has reported that in his environment (3.16/gcc4.8.3/corei7) memset() calls which clear out sensitive data in extract_{buf,entropy, entropy_user}() in random driver are being optimized away by gcc. Add a helper memzero_explicit() (similarly as explicit_bzero() variants) that can be used in such cases where a variable with sensitive data is being cleared out in the end. Other use cases might also be in crypto code. [ I have put this into lib/string.c though, as it's always built-in and doesn't need any dependencies then. ] Fixes kernel bugzilla: 82041 Reported-by: zatimend@hotmail.co.uk Signed-off-by: Daniel Borkmann Acked-by: Hannes Frederic Sowa Cc: Alexey Dobriyan Signed-off-by: Theodore Ts'o Cc: stable@vger.kernel.org --- drivers/char/random.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/char/random.c b/drivers/char/random.c index c18d41d..8c86a95 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1106,7 +1106,7 @@ static void extract_buf(struct entropy_store *r, __u8 *out) __mix_pool_bytes(r, hash.w, sizeof(hash.w)); spin_unlock_irqrestore(&r->lock, flags); - memset(workspace, 0, sizeof(workspace)); + memzero_explicit(workspace, sizeof(workspace)); /* * In case the hash function has some recognizable output @@ -1118,7 +1118,7 @@ static void extract_buf(struct entropy_store *r, __u8 *out) hash.w[2] ^= rol32(hash.w[2], 16); memcpy(out, &hash, EXTRACT_SIZE); - memset(&hash, 0, sizeof(hash)); + memzero_explicit(&hash, sizeof(hash)); } /* @@ -1175,7 +1175,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, } /* Wipe data just returned from memory */ - memset(tmp, 0, sizeof(tmp)); + memzero_explicit(tmp, sizeof(tmp)); return ret; } @@ -1218,7 +1218,7 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf, } /* Wipe data just returned from memory */ - memset(tmp, 0, sizeof(tmp)); + memzero_explicit(tmp, sizeof(tmp)); return ret; } -- cgit v1.1 From 0544e38d5ae90676624350468dea88c93eaeacbf Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 27 Aug 2014 16:53:31 +0100 Subject: drm/armada: add IRQ support back Add IRQ support back so that vblank ioctls work again. Signed-off-by: Russell King --- drivers/gpu/drm/armada/armada_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index e2d5792..9110738 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -190,6 +190,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) if (ret) goto err_comp; + dev->irq_enabled = true; dev->vblank_disable_allowed = 1; ret = armada_fbdev_init(dev); @@ -330,7 +331,7 @@ static struct drm_driver armada_drm_driver = { .desc = "Armada SoC DRM", .date = "20120730", .driver_features = DRIVER_GEM | DRIVER_MODESET | - DRIVER_PRIME, + DRIVER_HAVE_IRQ | DRIVER_PRIME, .ioctls = armada_ioctls, .fops = &armada_drm_fops, }; -- cgit v1.1 From c5488307dd679ea3fc23fec77dbf27191c2becda Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 11 Oct 2014 23:53:35 +0100 Subject: drm/armada: fix page_flip refcounting leak A refcounting leak was found of the original frame buffer attached to the CRTC when using the page_flip ioctl, resulting in the frame buffer never being freed. This was not obvious initially, as if the page flip subsequently re-attaches the original frame buffer, the refcounts will be balanced. However, if the original frame buffer is freed, then it will be leaked. Fix this by ensuring that we take a reference on the incoming fb, but rely on the queued work to drop that ref count. Reviewed-by: Daniel Vetter Signed-off-by: Russell King --- drivers/gpu/drm/armada/armada_crtc.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 9a0cc09..ef6be294 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -945,18 +945,15 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc, armada_reg_queue_end(work->regs, i); /* - * Hold the old framebuffer for the work - DRM appears to drop our - * reference to the old framebuffer in drm_mode_page_flip_ioctl(). + * Ensure that we hold a reference on the new framebuffer. + * This has to match the behaviour in mode_set. */ - drm_framebuffer_reference(work->old_fb); + drm_framebuffer_reference(fb); ret = armada_drm_crtc_queue_frame_work(dcrtc, work); if (ret) { - /* - * Undo our reference above; DRM does not drop the reference - * to this object on error, so that's okay. - */ - drm_framebuffer_unreference(work->old_fb); + /* Undo our reference above */ + drm_framebuffer_unreference(fb); kfree(work); return ret; } -- cgit v1.1 From 178e561f514fca4863c06a4af3501172e5627eb1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 11 Oct 2014 23:57:04 +0100 Subject: drm/armada: convert to use vblank_on/off calls A future commit changes the way various vblank calls behave, which causes drivers to break. Converting to use the drm_crtc_vblank_on/off calls avoids this breakage. Signed-off-by: Russell King --- drivers/gpu/drm/armada/armada_crtc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index ef6be294..e4a1490 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -260,7 +260,7 @@ static void armada_drm_vblank_off(struct armada_crtc *dcrtc) * Tell the DRM core that vblank IRQs aren't going to happen for * a while. This cleans up any pending vblank events for us. */ - drm_vblank_off(dev, dcrtc->num); + drm_crtc_vblank_off(&dcrtc->crtc); /* Handle any pending flip event. */ spin_lock_irq(&dev->event_lock); @@ -289,6 +289,8 @@ static void armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms) armada_drm_crtc_update(dcrtc); if (dpms_blanked(dpms)) armada_drm_vblank_off(dcrtc); + else + drm_crtc_vblank_on(&dcrtc->crtc); } } @@ -526,7 +528,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, /* Wait for pending flips to complete */ wait_event(dcrtc->frame_wait, !dcrtc->frame_work); - drm_vblank_pre_modeset(crtc->dev, dcrtc->num); + drm_crtc_vblank_off(crtc); crtc->mode = *adj; @@ -617,7 +619,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, armada_drm_crtc_update(dcrtc); - drm_vblank_post_modeset(crtc->dev, dcrtc->num); + drm_crtc_vblank_on(crtc); armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms)); return 0; -- cgit v1.1 From 90e55b3812a1245bb674afcc4410ddba7db402f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 29 Sep 2014 11:47:53 +0200 Subject: mtd: m25p80: get rid of spi_get_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simplifies the way we use spi_nor framework and will allow us to drop spi_nor_match_id. Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/devices/m25p80.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index dcda628..822209d 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -197,6 +197,7 @@ static int m25p_probe(struct spi_device *spi) struct m25p *flash; struct spi_nor *nor; enum read_mode mode = SPI_NOR_NORMAL; + char *flash_name = NULL; int ret; data = dev_get_platdata(&spi->dev); @@ -236,12 +237,11 @@ static int m25p_probe(struct spi_device *spi) * If that's the case, respect "type" and ignore a "name". */ if (data && data->type) - id = spi_nor_match_id(data->type); - - /* If we didn't get name from platform, simply use "modalias". */ - if (!id) - id = spi_get_device_id(spi); + flash_name = data->type; + else + flash_name = spi->modalias; + id = spi_nor_match_id(flash_name); ret = spi_nor_scan(nor, id, mode); if (ret) return ret; -- cgit v1.1 From 70f3ce0510afdad7cbaf27ab7ab961377205c782 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 29 Sep 2014 11:47:54 +0200 Subject: mtd: spi-nor: make spi_nor_scan() take a chip type name, not spi_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drivers currently call spi_nor_match_id() and then spi_nor_scan(). This adds a dependency on struct spi_device_id which we want to avoid. Make spi_nor_scan() do it for them. Signed-off-by: Ben Hutchings Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/devices/m25p80.c | 4 +--- drivers/mtd/spi-nor/fsl-quadspi.c | 7 +------ drivers/mtd/spi-nor/spi-nor.c | 13 +++++++++---- 3 files changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 822209d..bd5e4c6 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -193,7 +193,6 @@ static int m25p_probe(struct spi_device *spi) { struct mtd_part_parser_data ppdata; struct flash_platform_data *data; - const struct spi_device_id *id = NULL; struct m25p *flash; struct spi_nor *nor; enum read_mode mode = SPI_NOR_NORMAL; @@ -241,8 +240,7 @@ static int m25p_probe(struct spi_device *spi) else flash_name = spi->modalias; - id = spi_nor_match_id(flash_name); - ret = spi_nor_scan(nor, id, mode); + ret = spi_nor_scan(nor, flash_name, mode); if (ret) return ret; diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 8d659a2..d5269a2 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -881,7 +881,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) /* iterate the subnodes. */ for_each_available_child_of_node(dev->of_node, np) { - const struct spi_device_id *id; char modalias[40]; /* skip the holes */ @@ -909,10 +908,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) if (of_modalias_node(np, modalias, sizeof(modalias)) < 0) goto map_failed; - id = spi_nor_match_id(modalias); - if (!id) - goto map_failed; - ret = of_property_read_u32(np, "spi-max-frequency", &q->clk_rate); if (ret < 0) @@ -921,7 +916,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) /* set the chip address for READID */ fsl_qspi_set_base_addr(q, nor); - ret = spi_nor_scan(nor, id, SPI_NOR_QUAD); + ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD); if (ret) goto map_failed; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index ae16aa2..5c8e399 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -28,6 +28,8 @@ #define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) +static const struct spi_device_id *spi_nor_match_id(const char *name); + /* * Read the status register, returning its value in the location * Return the status register value. @@ -911,9 +913,9 @@ static int spi_nor_check(struct spi_nor *nor) return 0; } -int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, - enum read_mode mode) +int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) { + const struct spi_device_id *id = NULL; struct flash_info *info; struct device *dev = nor->dev; struct mtd_info *mtd = nor->mtd; @@ -925,6 +927,10 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, if (ret) return ret; + id = spi_nor_match_id(name); + if (!id) + return -ENOENT; + info = (void *)id->driver_data; if (info->jedec_id) { @@ -1113,7 +1119,7 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, } EXPORT_SYMBOL_GPL(spi_nor_scan); -const struct spi_device_id *spi_nor_match_id(char *name) +static const struct spi_device_id *spi_nor_match_id(const char *name) { const struct spi_device_id *id = spi_nor_ids; @@ -1124,7 +1130,6 @@ const struct spi_device_id *spi_nor_match_id(char *name) } return NULL; } -EXPORT_SYMBOL_GPL(spi_nor_match_id); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Huang Shijie "); -- cgit v1.1 From aa281ac631008b9c18c405c8880007789f659c7d Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Sun, 19 Oct 2014 19:38:58 +0300 Subject: Boaz Harrosh - Fix broken email address I no longer have access to the Panasas email. So change to an email that can always reach me. Signed-off-by: Boaz Harrosh --- drivers/scsi/osd/Kbuild | 2 +- drivers/scsi/osd/Kconfig | 2 +- drivers/scsi/osd/osd_debug.h | 2 +- drivers/scsi/osd/osd_initiator.c | 4 ++-- drivers/scsi/osd/osd_uld.c | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/osd/Kbuild b/drivers/scsi/osd/Kbuild index 5fd73d77..58cecd4 100644 --- a/drivers/scsi/osd/Kbuild +++ b/drivers/scsi/osd/Kbuild @@ -4,7 +4,7 @@ # Copyright (C) 2008 Panasas Inc. All rights reserved. # # Authors: -# Boaz Harrosh +# Boaz Harrosh # Benny Halevy # # This program is free software; you can redistribute it and/or modify diff --git a/drivers/scsi/osd/Kconfig b/drivers/scsi/osd/Kconfig index a070351..347cc5e 100644 --- a/drivers/scsi/osd/Kconfig +++ b/drivers/scsi/osd/Kconfig @@ -4,7 +4,7 @@ # Copyright (C) 2008 Panasas Inc. All rights reserved. # # Authors: -# Boaz Harrosh +# Boaz Harrosh # Benny Halevy # # This program is free software; you can redistribute it and/or modify diff --git a/drivers/scsi/osd/osd_debug.h b/drivers/scsi/osd/osd_debug.h index 579e491..2634126 100644 --- a/drivers/scsi/osd/osd_debug.h +++ b/drivers/scsi/osd/osd_debug.h @@ -4,7 +4,7 @@ * Copyright (C) 2008 Panasas Inc. All rights reserved. * * Authors: - * Boaz Harrosh + * Boaz Harrosh * Benny Halevy * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index 5f4cbf0..52e243b 100644 --- a/drivers/scsi/osd/osd_initiator.c +++ b/drivers/scsi/osd/osd_initiator.c @@ -7,7 +7,7 @@ * Copyright (C) 2008 Panasas Inc. All rights reserved. * * Authors: - * Boaz Harrosh + * Boaz Harrosh * Benny Halevy * * This program is free software; you can redistribute it and/or modify @@ -57,7 +57,7 @@ enum { OSD_REQ_RETRIES = 1 }; -MODULE_AUTHOR("Boaz Harrosh "); +MODULE_AUTHOR("Boaz Harrosh "); MODULE_DESCRIPTION("open-osd initiator library libosd.ko"); MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c index e1d9a4c..92cdd4b 100644 --- a/drivers/scsi/osd/osd_uld.c +++ b/drivers/scsi/osd/osd_uld.c @@ -10,7 +10,7 @@ * Copyright (C) 2008 Panasas Inc. All rights reserved. * * Authors: - * Boaz Harrosh + * Boaz Harrosh * Benny Halevy * * This program is free software; you can redistribute it and/or modify @@ -74,7 +74,7 @@ static const char osd_name[] = "osd"; static const char *osd_version_string = "open-osd 0.2.1"; -MODULE_AUTHOR("Boaz Harrosh "); +MODULE_AUTHOR("Boaz Harrosh "); MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV_MAJOR(SCSI_OSD_MAJOR); -- cgit v1.1 From 3d5960472de0a245c56ba55e9a59b8d0f4c69cb4 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Thu, 16 Oct 2014 15:07:51 +0200 Subject: hwmon: (menf21bmc) Include linux/err.h Include linux/err.h to get the definitions for IS_ERR() PTR_ERR() and ERR_PTR() used in the driver. This fixes compilation on powerpc targets. Signed-off-by: Johannes Thumshirn Signed-off-by: Guenter Roeck --- drivers/hwmon/menf21bmc_hwmon.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/hwmon/menf21bmc_hwmon.c b/drivers/hwmon/menf21bmc_hwmon.c index c92229d..afc6b58 100644 --- a/drivers/hwmon/menf21bmc_hwmon.c +++ b/drivers/hwmon/menf21bmc_hwmon.c @@ -21,6 +21,7 @@ #include #include #include +#include #define DRV_NAME "menf21bmc_hwmon" -- cgit v1.1 From 6cab7a37f5c048bb2a768f24b0ec748b052fda09 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 8 Oct 2014 16:09:14 +0100 Subject: staging: comedi: (regression) channel list must be set for COMEDI_CMD ioctl `do_cmd_ioctl()`, the handler for the `COMEDI_CMD` ioctl can incorrectly call the Comedi subdevice's `do_cmd()` handler with a NULL channel list pointer. This is a regression as the `do_cmd()` handler has never been expected to deal with that, leading to a kernel OOPS when it tries to dereference it. A NULL channel list pointer is allowed for the `COMEDI_CMDTEST` ioctl, handled by `do_cmdtest_ioctl()` and the subdevice's `do_cmdtest()` handler, but not for the `COMEDI_CMD` ioctl and its handlers. Both `do_cmd_ioctl()` and `do_cmdtest_ioctl()` call `__comedi_get_user_chanlist()` to copy the channel list from user memory into dynamically allocated kernel memory and check it for consistency. That function currently returns 0 if the `user_chanlist` parameter (pointing to the channel list in user memory) is NULL. That's fine for `do_cmdtest_ioctl()`, but `do_cmd_ioctl()` incorrectly assumes the kernel copy of the channel list has been set-up correctly. Fix it by not allowing the `user_chanlist` parameter to be NULL in `__comedi_get_user_chanlist()`, and only calling it from `do_cmdtest_ioctl()` if the parameter is non-NULL. Thanks to Bernd Porr for reporting the bug via an initial patch sent privately. Fixes: c6cd0eefb27b ("staging: comedi: comedi_fops: introduce __comedi_get_user_chanlist()") Reported-by: Bernd Porr Signed-off-by: Ian Abbott Reviewed-by: H Hartley Sweeten Cc: Bernd Porr Cc: # 3.15.y 3.16.y 3.17.y Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/comedi_fops.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 495969f..a9b7fe5 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -1462,10 +1462,6 @@ static int __comedi_get_user_chanlist(struct comedi_device *dev, unsigned int *chanlist; int ret; - /* user_chanlist could be NULL for do_cmdtest ioctls */ - if (!user_chanlist) - return 0; - chanlist = memdup_user(user_chanlist, cmd->chanlist_len * sizeof(unsigned int)); if (IS_ERR(chanlist)) @@ -1609,10 +1605,13 @@ static int do_cmdtest_ioctl(struct comedi_device *dev, s = &dev->subdevices[cmd.subdev]; - /* load channel/gain list */ - ret = __comedi_get_user_chanlist(dev, s, user_chanlist, &cmd); - if (ret) - return ret; + /* user_chanlist can be NULL for COMEDI_CMDTEST ioctl */ + if (user_chanlist) { + /* load channel/gain list */ + ret = __comedi_get_user_chanlist(dev, s, user_chanlist, &cmd); + if (ret) + return ret; + } ret = s->do_cmdtest(dev, s, &cmd); -- cgit v1.1 From ab608c247854faf61ae36d0d3c4a86b2fbb1c2f3 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Thu, 2 Oct 2014 22:41:18 +0800 Subject: drivers/staging/comedi/Kconfig: Let COMEDI_II_PCI20KC depend on HAS_IOMEM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit COMEDI_II_PCI20KC needs HAS_IOMEM, so depend on it. The related error ( with allmodconfig under um): CC [M] drivers/staging/comedi/drivers/ii_pci20kc.o drivers/staging/comedi/drivers/ii_pci20kc.c: In function ‘ii20k_attach’: drivers/staging/comedi/drivers/ii_pci20kc.c:442:2: error: implicit declaration of function ‘ioremap’ [-Werror=implicit-function-declaration] dev->mmio = ioremap(membase, II20K_SIZE); ^ drivers/staging/comedi/drivers/ii_pci20kc.c:442:12: warning: assignment makes pointer from integer without a cast [enabled by default] dev->mmio = ioremap(membase, II20K_SIZE); ^ drivers/staging/comedi/drivers/ii_pci20kc.c: In function ‘ii20k_detach’: drivers/staging/comedi/drivers/ii_pci20kc.c:512:3: error: implicit declaration of function ‘iounmap’ [-Werror=implicit-function-declaration] iounmap(dev->mmio); ^ Signed-off-by: Chen Gang Reviewed-by: H Hartley Sweeten Reviewed-by: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig index a8bc2b5..b709736 100644 --- a/drivers/staging/comedi/Kconfig +++ b/drivers/staging/comedi/Kconfig @@ -426,6 +426,7 @@ config COMEDI_AIO_IIRO_16 config COMEDI_II_PCI20KC tristate "Intelligent Instruments PCI-20001C carrier support" + depends on HAS_IOMEM ---help--- Enable support for Intelligent Instruments PCI-20001C carrier PCI-20001, PCI-20006 and PCI-20341 -- cgit v1.1 From 54d5c5cd0fb232d13c547cae969a0f1312b455b6 Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Fri, 10 Oct 2014 21:41:24 +0200 Subject: staging: rtl8723au: Fix alignment of mac_addr for ether_addr_copy() usage Make sure struct eeprom_priv->mac_addr is 2 byte aligned to work with ether_addr_copy() Reported-by: Dan Carpenter Signed-off-by: Jes Sorensen Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723au/include/rtw_eeprom.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/rtl8723au/include/rtw_eeprom.h b/drivers/staging/rtl8723au/include/rtw_eeprom.h index e5121a2..a86f36e 100644 --- a/drivers/staging/rtl8723au/include/rtw_eeprom.h +++ b/drivers/staging/rtl8723au/include/rtw_eeprom.h @@ -107,12 +107,12 @@ enum rt_customer_id }; struct eeprom_priv { + u8 mac_addr[6]; /* PermanentAddress */ u8 bautoload_fail_flag; u8 bloadfile_fail_flag; u8 bloadmac_fail_flag; /* u8 bempty; */ /* u8 sys_config; */ - u8 mac_addr[6]; /* PermanentAddress */ /* u8 config0; */ u16 channel_plan; /* u8 country_string[3]; */ -- cgit v1.1 From 95ff88688781db2f64042e69bd499e518bbb36e5 Mon Sep 17 00:00:00 2001 From: Ian Morgan Date: Sun, 19 Oct 2014 08:05:13 -0400 Subject: ax88179_178a: fix bonding failure The following patch fixes a bug which causes the ax88179_178a driver to be incapable of being added to a bond. When I brought up the issue with the bonding maintainers, they indicated that the real problem was with the NIC driver which must return zero for success (of setting the MAC address). I see that several other NIC drivers follow that pattern by either simply always returing zero, or by passing through a negative (error) result while rewriting any positive return code to zero. With that same philisophy applied to the ax88179_178a driver, it allows it to work correctly with the bonding driver. I believe this is suitable for queuing in -stable, as it's a small, simple, and obvious fix that corrects a defect with no other known workaround. This patch is against vanilla 3.17(.0). Signed-off-by: Ian Morgan drivers/net/usb/ax88179_178a.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) Signed-off-by: David S. Miller --- drivers/net/usb/ax88179_178a.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index be42757..e6338c1 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -937,6 +937,7 @@ static int ax88179_set_mac_addr(struct net_device *net, void *p) { struct usbnet *dev = netdev_priv(net); struct sockaddr *addr = p; + int ret; if (netif_running(net)) return -EBUSY; @@ -946,8 +947,12 @@ static int ax88179_set_mac_addr(struct net_device *net, void *p) memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); /* Set the MAC address */ - return ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, + ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN, net->dev_addr); + if (ret < 0) + return ret; + + return 0; } static const struct net_device_ops ax88179_netdev_ops = { -- cgit v1.1 From 40ac948e02e01e98474330f259ff842844096541 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 15 Oct 2014 13:42:29 +1000 Subject: drm/gt215/gr: fix initialisation on gddr5 boards The binary driver modifies the default context to have this value, rather than 0x3d0040, *after* it's filled the buffer with the usual golden data. We don't really have anything in place to locate the correct offset to do these type of modifications outside of the generation function, so this will have to do. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c index 552fdbd..1d0e33f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c @@ -113,6 +113,8 @@ #define IS_NVA3F(x) (((x) > 0xa0 && (x) < 0xaa) || (x) == 0xaf) #define IS_NVAAF(x) ((x) >= 0xaa && (x) <= 0xac) +#include + /* * This code deals with PGRAPH contexts on NV50 family cards. Like NV40, it's * the GPU itself that does context-switching, but it needs a special @@ -569,8 +571,12 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) gr_def(ctx, 0x407d08, 0x00010040); else if (device->chipset < 0xa0) gr_def(ctx, 0x407d08, 0x00390040); - else - gr_def(ctx, 0x407d08, 0x003d0040); + else { + if (nouveau_fb(device)->ram->type != NV_MEM_TYPE_GDDR5) + gr_def(ctx, 0x407d08, 0x003d0040); + else + gr_def(ctx, 0x407d08, 0x003c0040); + } gr_def(ctx, 0x407d0c, 0x00000022); } -- cgit v1.1 From 67e26e41ff8aa514826dae79f0b10169b5ba93b4 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 20 Oct 2014 15:49:33 +1000 Subject: drm/nouveau: fix regression on agp boards Extends the fix in f2f9a2cbaf019481feefe231f996d3602612fa99 to also workaround permission issues noticed by people using AGP systems. Cc: stable@vger.kernel.org # 3.16: f2f9a2c: drm/nouveau: fix regression Cc: stable@vger.kernel.org # 3.16+ Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_chan.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index 977fb8f..77c81d6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -395,15 +395,20 @@ nouveau_channel_new(struct nouveau_drm *drm, struct nvif_device *device, struct nouveau_channel **pchan) { struct nouveau_cli *cli = (void *)nvif_client(&device->base); + bool super; int ret; + /* hack until fencenv50 is fixed, and agp access relaxed */ + super = cli->base.super; + cli->base.super = true; + ret = nouveau_channel_ind(drm, device, handle, arg0, pchan); if (ret) { NV_PRINTK(debug, cli, "ib channel create, %d\n", ret); ret = nouveau_channel_dma(drm, device, handle, pchan); if (ret) { NV_PRINTK(debug, cli, "dma channel create, %d\n", ret); - return ret; + goto done; } } @@ -411,8 +416,9 @@ nouveau_channel_new(struct nouveau_drm *drm, struct nvif_device *device, if (ret) { NV_PRINTK(error, cli, "channel failed to initialise, %d\n", ret); nouveau_channel_del(pchan); - return ret; } - return 0; +done: + cli->base.super = super; + return ret; } -- cgit v1.1 From 3a71c05e66e597a1349fdbd5f0bb119d8d9cb850 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 26 Sep 2014 16:14:51 +0200 Subject: pinctrl: baytrail: Clear DIRECT_IRQ bit Direct irq en bit should be cleared for pads using io mode. If not, the io based irq will never be detected. However, this bit can sometimes be misconfigured (BIOS issue). Force clearing of this bit in io mode and trigger a WARN. Signed-off-by: Loic Poulain Acked-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-baytrail.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-baytrail.c b/drivers/pinctrl/pinctrl-baytrail.c index e12e5b0..b83ec87 100644 --- a/drivers/pinctrl/pinctrl-baytrail.c +++ b/drivers/pinctrl/pinctrl-baytrail.c @@ -227,10 +227,14 @@ static int byt_irq_type(struct irq_data *d, unsigned type) spin_lock_irqsave(&vg->lock, flags); value = readl(reg); + WARN(value & BYT_DIRECT_IRQ_EN, + "Bad pad config for io mode, force direct_irq_en bit clearing"); + /* For level trigges the BYT_TRIG_POS and BYT_TRIG_NEG bits * are used to indicate high and low level triggering */ - value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); + value &= ~(BYT_DIRECT_IRQ_EN | BYT_TRIG_POS | BYT_TRIG_NEG | + BYT_TRIG_LVL); switch (type) { case IRQ_TYPE_LEVEL_HIGH: -- cgit v1.1 From dec02f98ae2e341a2e0bb25f27e84867e5f9f64a Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Sat, 4 Oct 2014 17:48:42 +0800 Subject: pwm: Let PWM_CLPS711X depend on HAS_IOMEM PWM_CLPS711X needs HAS_IOMEM, so depend on it, the related error (with allmodconfig under um): MODPOST 1205 modules ERROR: "devm_ioremap_resource" [drivers/pwm/pwm-clps711x.ko] undefined! ERROR: "devm_ioremap" [drivers/net/phy/mdio-bcm-unimac.ko] undefined! Signed-off-by: Chen Gang Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 3865dfb..ef2dd2e 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -83,6 +83,7 @@ config PWM_BFIN config PWM_CLPS711X tristate "CLPS711X PWM support" depends on ARCH_CLPS711X || COMPILE_TEST + depends on HAS_IOMEM help Generic PWM framework driver for Cirrus Logic CLPS711X. -- cgit v1.1 From fca8c0481bc8d751479ca13f454e89a7fdfece03 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 13 Aug 2014 13:51:28 +0200 Subject: watchdog: xilinx: Remove .owner field for driver There is no need to init .owner field. Based on the patch from Peter Griffin "mmc: remove .owner field for drivers using module_platform_driver" This patch removes the superflous .owner field for drivers which use the module_platform_driver API, as this is overriden in platform_driver_register anyway." Signed-off-by: Michal Simek Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/of_xilinx_wdt.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c index 1e6e28d..b2e1b4c 100644 --- a/drivers/watchdog/of_xilinx_wdt.c +++ b/drivers/watchdog/of_xilinx_wdt.c @@ -236,7 +236,6 @@ static struct platform_driver xwdt_driver = { .probe = xwdt_probe, .remove = xwdt_remove, .driver = { - .owner = THIS_MODULE, .name = WATCHDOG_NAME, .of_match_table = xwdt_of_match, }, -- cgit v1.1 From 62ce25439a7ea01eba5c2a6a8284e4aa23890042 Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Wed, 20 Aug 2014 15:26:46 -0400 Subject: powerpc: booke_wdt: Fix build error as a module Building booke_wdt fails when trying to build as a module as there is no early_param() in module. Fix by using module_param() instead of early_param(). Signed-off-by: Pranith Kumar Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/booke_wdt.c | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index 08a7853..e96b09b 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -30,8 +30,6 @@ * occur, and the final time the board will reset. */ -u32 booke_wdt_enabled; -u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; #ifdef CONFIG_PPC_FSL_BOOK3E #define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15)) @@ -41,27 +39,10 @@ u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; #define WDTP_MASK (TCR_WP_MASK) #endif -/* Checks wdt=x and wdt_period=xx command-line option */ -notrace int __init early_parse_wdt(char *p) -{ - if (p && strncmp(p, "0", 1) != 0) - booke_wdt_enabled = 1; - - return 0; -} -early_param("wdt", early_parse_wdt); - -int __init early_parse_wdt_period(char *p) -{ - unsigned long ret; - if (p) { - if (!kstrtol(p, 0, &ret)) - booke_wdt_period = ret; - } - - return 0; -} -early_param("wdt_period", early_parse_wdt_period); +static bool booke_wdt_enabled; +module_param(booke_wdt_enabled, bool, 0); +static int booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; +module_param(booke_wdt_period, int, 0); #ifdef CONFIG_PPC_FSL_BOOK3E @@ -259,5 +240,6 @@ static int __init booke_wdt_init(void) module_init(booke_wdt_init); module_exit(booke_wdt_exit); +MODULE_ALIAS("booke_wdt"); MODULE_DESCRIPTION("PowerPC Book-E watchdog driver"); MODULE_LICENSE("GPL"); -- cgit v1.1 From 1f897a81915222310374cac1a85c0c7104f16249 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 19 Aug 2014 14:57:12 +0300 Subject: watchdog: ts72xx_wdt: Kill superfluous variable in remove There is no need to store the return value of misc_deregister() in a variable. Instead we can just return the value directly. Signed-off-by: Mika Westerberg Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/ts72xx_wdt.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c index afa9d6e..dee9c6c 100644 --- a/drivers/watchdog/ts72xx_wdt.c +++ b/drivers/watchdog/ts72xx_wdt.c @@ -428,11 +428,7 @@ static int ts72xx_wdt_probe(struct platform_device *pdev) static int ts72xx_wdt_remove(struct platform_device *pdev) { - int error; - - error = misc_deregister(&ts72xx_wdt_miscdev); - - return error; + return misc_deregister(&ts72xx_wdt_miscdev); } static struct platform_driver ts72xx_wdt_driver = { -- cgit v1.1 From 0461aea7ec379b00f4acb5d612bfb2f7a497eb92 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 18 Aug 2014 16:12:50 +0800 Subject: watchdog: imx2_wdt: Convert to use regmap framework's endianness method. Signed-off-by: Xiubo Li Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/imx2_wdt.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 68c3d37..f37bb05 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -191,12 +191,10 @@ static struct regmap_config imx2_wdt_regmap_config = { static int __init imx2_wdt_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; struct imx2_wdt_device *wdev; struct watchdog_device *wdog; struct resource *res; void __iomem *base; - bool big_endian; int ret; u32 val; @@ -204,10 +202,6 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) if (!wdev) return -ENOMEM; - big_endian = of_property_read_bool(np, "big-endian"); - if (big_endian) - imx2_wdt_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) -- cgit v1.1 From 58bf016426594e5370e7e7059698a278294db997 Mon Sep 17 00:00:00 2001 From: Harini Katakam Date: Fri, 22 Aug 2014 14:58:01 +0530 Subject: watchdog: Add Cadence WDT driver Add Cadence WDT driver. This is used by Xilinx Zynq. Signed-off-by: Harini Katakam Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 8 + drivers/watchdog/Makefile | 1 + drivers/watchdog/cadence_wdt.c | 516 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 525 insertions(+) create mode 100644 drivers/watchdog/cadence_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e3d5bf0..786f8c8 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -157,6 +157,14 @@ config AT91SAM9X_WATCHDOG Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will reboot your system when the timeout is reached. +config CADENCE_WATCHDOG + tristate "Cadence Watchdog Timer" + depends on ARM + select WATCHDOG_CORE + help + Say Y here if you want to include support for the watchdog + timer in the Xilinx Zynq. + config 21285_WATCHDOG tristate "DC21285 watchdog" depends on FOOTBRIDGE diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index de17014..82af995 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o +obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c new file mode 100644 index 0000000..5927c0a --- /dev/null +++ b/drivers/watchdog/cadence_wdt.c @@ -0,0 +1,516 @@ +/* + * Cadence WDT driver - Used by Xilinx Zynq + * + * Copyright (C) 2010 - 2014 Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CDNS_WDT_DEFAULT_TIMEOUT 10 +/* Supports 1 - 516 sec */ +#define CDNS_WDT_MIN_TIMEOUT 1 +#define CDNS_WDT_MAX_TIMEOUT 516 + +/* Restart key */ +#define CDNS_WDT_RESTART_KEY 0x00001999 + +/* Counter register access key */ +#define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000 + +/* Counter value divisor */ +#define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000 + +/* Clock prescaler value and selection */ +#define CDNS_WDT_PRESCALE_64 64 +#define CDNS_WDT_PRESCALE_512 512 +#define CDNS_WDT_PRESCALE_4096 4096 +#define CDNS_WDT_PRESCALE_SELECT_64 1 +#define CDNS_WDT_PRESCALE_SELECT_512 2 +#define CDNS_WDT_PRESCALE_SELECT_4096 3 + +/* Input clock frequency */ +#define CDNS_WDT_CLK_10MHZ 10000000 +#define CDNS_WDT_CLK_75MHZ 75000000 + +/* Counter maximum value */ +#define CDNS_WDT_COUNTER_MAX 0xFFF + +static int wdt_timeout = CDNS_WDT_DEFAULT_TIMEOUT; +static int nowayout = WATCHDOG_NOWAYOUT; + +module_param(wdt_timeout, int, 0); +MODULE_PARM_DESC(wdt_timeout, + "Watchdog time in seconds. (default=" + __MODULE_STRING(CDNS_WDT_DEFAULT_TIMEOUT) ")"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +/** + * struct cdns_wdt - Watchdog device structure + * @regs: baseaddress of device + * @rst: reset flag + * @clk: struct clk * of a clock source + * @prescaler: for saving prescaler value + * @ctrl_clksel: counter clock prescaler selection + * @io_lock: spinlock for IO register access + * @cdns_wdt_device: watchdog device structure + * @cdns_wdt_notifier: notifier structure + * + * Structure containing parameters specific to cadence watchdog. + */ +struct cdns_wdt { + void __iomem *regs; + bool rst; + struct clk *clk; + u32 prescaler; + u32 ctrl_clksel; + spinlock_t io_lock; + struct watchdog_device cdns_wdt_device; + struct notifier_block cdns_wdt_notifier; +}; + +/* Write access to Registers */ +static inline void cdns_wdt_writereg(struct cdns_wdt *wdt, u32 offset, u32 val) +{ + writel_relaxed(val, wdt->regs + offset); +} + +/*************************Register Map**************************************/ + +/* Register Offsets for the WDT */ +#define CDNS_WDT_ZMR_OFFSET 0x0 /* Zero Mode Register */ +#define CDNS_WDT_CCR_OFFSET 0x4 /* Counter Control Register */ +#define CDNS_WDT_RESTART_OFFSET 0x8 /* Restart Register */ +#define CDNS_WDT_SR_OFFSET 0xC /* Status Register */ + +/* + * Zero Mode Register - This register controls how the time out is indicated + * and also contains the access code to allow writes to the register (0xABC). + */ +#define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */ +#define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */ +#define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */ +#define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */ +#define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */ +/* + * Counter Control register - This register controls how fast the timer runs + * and the reset value and also contains the access code to allow writes to + * the register. + */ +#define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */ + +/** + * cdns_wdt_stop - Stop the watchdog. + * + * @wdd: watchdog device + * + * Read the contents of the ZMR register, clear the WDEN bit + * in the register and set the access key for successful write. + * + * Return: always 0 + */ +static int cdns_wdt_stop(struct watchdog_device *wdd) +{ + struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); + + spin_lock(&wdt->io_lock); + cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, + CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK)); + spin_unlock(&wdt->io_lock); + + return 0; +} + +/** + * cdns_wdt_reload - Reload the watchdog timer (i.e. pat the watchdog). + * + * @wdd: watchdog device + * + * Write the restart key value (0x00001999) to the restart register. + * + * Return: always 0 + */ +static int cdns_wdt_reload(struct watchdog_device *wdd) +{ + struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); + + spin_lock(&wdt->io_lock); + cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, + CDNS_WDT_RESTART_KEY); + spin_unlock(&wdt->io_lock); + + return 0; +} + +/** + * cdns_wdt_start - Enable and start the watchdog. + * + * @wdd: watchdog device + * + * The counter value is calculated according to the formula: + * calculated count = (timeout * clock) / prescaler + 1. + * The calculated count is divided by 0x1000 to obtain the field value + * to write to counter control register. + * Clears the contents of prescaler and counter reset value. Sets the + * prescaler to 4096 and the calculated count and access key + * to write to CCR Register. + * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit) + * or Interrupt signal(IRQEN) with a specified cycles and the access + * key to write to ZMR Register. + * + * Return: always 0 + */ +static int cdns_wdt_start(struct watchdog_device *wdd) +{ + struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned int data = 0; + unsigned short count; + unsigned long clock_f = clk_get_rate(wdt->clk); + + /* + * Counter value divisor to obtain the value of + * counter reset to be written to control register. + */ + count = (wdd->timeout * (clock_f / wdt->prescaler)) / + CDNS_WDT_COUNTER_VALUE_DIVISOR + 1; + + if (count > CDNS_WDT_COUNTER_MAX) + count = CDNS_WDT_COUNTER_MAX; + + spin_lock(&wdt->io_lock); + cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, + CDNS_WDT_ZMR_ZKEY_VAL); + + count = (count << 2) & CDNS_WDT_CCR_CRV_MASK; + + /* Write counter access key first to be able write to register */ + data = count | CDNS_WDT_REGISTER_ACCESS_KEY | wdt->ctrl_clksel; + cdns_wdt_writereg(wdt, CDNS_WDT_CCR_OFFSET, data); + data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 | + CDNS_WDT_ZMR_ZKEY_VAL; + + /* Reset on timeout if specified in device tree. */ + if (wdt->rst) { + data |= CDNS_WDT_ZMR_RSTEN_MASK; + data &= ~CDNS_WDT_ZMR_IRQEN_MASK; + } else { + data &= ~CDNS_WDT_ZMR_RSTEN_MASK; + data |= CDNS_WDT_ZMR_IRQEN_MASK; + } + cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, data); + cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, + CDNS_WDT_RESTART_KEY); + spin_unlock(&wdt->io_lock); + + return 0; +} + +/** + * cdns_wdt_settimeout - Set a new timeout value for the watchdog device. + * + * @wdd: watchdog device + * @new_time: new timeout value that needs to be set + * Return: 0 on success + * + * Update the watchdog_device timeout with new value which is used when + * cdns_wdt_start is called. + */ +static int cdns_wdt_settimeout(struct watchdog_device *wdd, + unsigned int new_time) +{ + wdd->timeout = new_time; + + return cdns_wdt_start(wdd); +} + +/** + * cdns_wdt_irq_handler - Notifies of watchdog timeout. + * + * @irq: interrupt number + * @dev_id: pointer to a platform device structure + * Return: IRQ_HANDLED + * + * The handler is invoked when the watchdog times out and a + * reset on timeout has not been enabled. + */ +static irqreturn_t cdns_wdt_irq_handler(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + + dev_info(&pdev->dev, + "Watchdog timed out. Internal reset not enabled\n"); + + return IRQ_HANDLED; +} + +/* + * Info structure used to indicate the features supported by the device + * to the upper layers. This is defined in watchdog.h header file. + */ +static struct watchdog_info cdns_wdt_info = { + .identity = "cdns_wdt watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +/* Watchdog Core Ops */ +static struct watchdog_ops cdns_wdt_ops = { + .owner = THIS_MODULE, + .start = cdns_wdt_start, + .stop = cdns_wdt_stop, + .ping = cdns_wdt_reload, + .set_timeout = cdns_wdt_settimeout, +}; + +/** + * cdns_wdt_notify_sys - Notifier for reboot or shutdown. + * + * @this: handle to notifier block + * @code: turn off indicator + * @unused: unused + * Return: NOTIFY_DONE + * + * This notifier is invoked whenever the system reboot or shutdown occur + * because we need to disable the WDT before system goes down as WDT might + * reset on the next boot. + */ +static int cdns_wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + struct cdns_wdt *wdt = container_of(this, struct cdns_wdt, + cdns_wdt_notifier); + if (code == SYS_DOWN || code == SYS_HALT) + cdns_wdt_stop(&wdt->cdns_wdt_device); + + return NOTIFY_DONE; +} + +/************************Platform Operations*****************************/ +/** + * cdns_wdt_probe - Probe call for the device. + * + * @pdev: handle to the platform device structure. + * Return: 0 on success, negative error otherwise. + * + * It does all the memory allocation and registration for the device. + */ +static int cdns_wdt_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret, irq; + unsigned long clock_f; + struct cdns_wdt *wdt; + struct watchdog_device *cdns_wdt_device; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + cdns_wdt_device = &wdt->cdns_wdt_device; + cdns_wdt_device->info = &cdns_wdt_info; + cdns_wdt_device->ops = &cdns_wdt_ops; + cdns_wdt_device->timeout = CDNS_WDT_DEFAULT_TIMEOUT; + cdns_wdt_device->min_timeout = CDNS_WDT_MIN_TIMEOUT; + cdns_wdt_device->max_timeout = CDNS_WDT_MAX_TIMEOUT; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(wdt->regs)) + return PTR_ERR(wdt->regs); + + /* Register the interrupt */ + wdt->rst = of_property_read_bool(pdev->dev.of_node, "reset-on-timeout"); + irq = platform_get_irq(pdev, 0); + if (!wdt->rst && irq >= 0) { + ret = devm_request_irq(&pdev->dev, irq, cdns_wdt_irq_handler, 0, + pdev->name, pdev); + if (ret) { + dev_err(&pdev->dev, + "cannot register interrupt handler err=%d\n", + ret); + return ret; + } + } + + /* Initialize the members of cdns_wdt structure */ + cdns_wdt_device->parent = &pdev->dev; + + ret = watchdog_init_timeout(cdns_wdt_device, wdt_timeout, &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "unable to set timeout value\n"); + return ret; + } + + watchdog_set_nowayout(cdns_wdt_device, nowayout); + watchdog_set_drvdata(cdns_wdt_device, wdt); + + wdt->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(wdt->clk)) { + dev_err(&pdev->dev, "input clock not found\n"); + ret = PTR_ERR(wdt->clk); + return ret; + } + + ret = clk_prepare_enable(wdt->clk); + if (ret) { + dev_err(&pdev->dev, "unable to enable clock\n"); + return ret; + } + + clock_f = clk_get_rate(wdt->clk); + if (clock_f <= CDNS_WDT_CLK_75MHZ) { + wdt->prescaler = CDNS_WDT_PRESCALE_512; + wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512; + } else { + wdt->prescaler = CDNS_WDT_PRESCALE_4096; + wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096; + } + + spin_lock_init(&wdt->io_lock); + + wdt->cdns_wdt_notifier.notifier_call = &cdns_wdt_notify_sys; + ret = register_reboot_notifier(&wdt->cdns_wdt_notifier); + if (ret != 0) { + dev_err(&pdev->dev, "cannot register reboot notifier err=%d)\n", + ret); + goto err_clk_disable; + } + + ret = watchdog_register_device(cdns_wdt_device); + if (ret) { + dev_err(&pdev->dev, "Failed to register wdt device\n"); + goto err_clk_disable; + } + platform_set_drvdata(pdev, wdt); + + dev_dbg(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds%s\n", + wdt->regs, cdns_wdt_device->timeout, + nowayout ? ", nowayout" : ""); + + return 0; + +err_clk_disable: + clk_disable_unprepare(wdt->clk); + + return ret; +} + +/** + * cdns_wdt_remove - Probe call for the device. + * + * @pdev: handle to the platform device structure. + * Return: 0 on success, otherwise negative error. + * + * Unregister the device after releasing the resources. + */ +static int cdns_wdt_remove(struct platform_device *pdev) +{ + struct cdns_wdt *wdt = platform_get_drvdata(pdev); + + cdns_wdt_stop(&wdt->cdns_wdt_device); + watchdog_unregister_device(&wdt->cdns_wdt_device); + unregister_reboot_notifier(&wdt->cdns_wdt_notifier); + clk_disable_unprepare(wdt->clk); + + return 0; +} + +/** + * cdns_wdt_shutdown - Stop the device. + * + * @pdev: handle to the platform structure. + * + */ +static void cdns_wdt_shutdown(struct platform_device *pdev) +{ + struct cdns_wdt *wdt = platform_get_drvdata(pdev); + + cdns_wdt_stop(&wdt->cdns_wdt_device); + clk_disable_unprepare(wdt->clk); +} + +/** + * cdns_wdt_suspend - Stop the device. + * + * @dev: handle to the device structure. + * Return: 0 always. + */ +static int __maybe_unused cdns_wdt_suspend(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct cdns_wdt *wdt = platform_get_drvdata(pdev); + + cdns_wdt_stop(&wdt->cdns_wdt_device); + clk_disable_unprepare(wdt->clk); + + return 0; +} + +/** + * cdns_wdt_resume - Resume the device. + * + * @dev: handle to the device structure. + * Return: 0 on success, errno otherwise. + */ +static int __maybe_unused cdns_wdt_resume(struct device *dev) +{ + int ret; + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct cdns_wdt *wdt = platform_get_drvdata(pdev); + + ret = clk_prepare_enable(wdt->clk); + if (ret) { + dev_err(dev, "unable to enable clock\n"); + return ret; + } + cdns_wdt_start(&wdt->cdns_wdt_device); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(cdns_wdt_pm_ops, cdns_wdt_suspend, cdns_wdt_resume); + +static struct of_device_id cdns_wdt_of_match[] = { + { .compatible = "cdns,wdt-r1p2", }, + { /* end of table */ } +}; +MODULE_DEVICE_TABLE(of, cdns_wdt_of_match); + +/* Driver Structure */ +static struct platform_driver cdns_wdt_driver = { + .probe = cdns_wdt_probe, + .remove = cdns_wdt_remove, + .shutdown = cdns_wdt_shutdown, + .driver = { + .name = "cdns-wdt", + .owner = THIS_MODULE, + .of_match_table = cdns_wdt_of_match, + .pm = &cdns_wdt_pm_ops, + }, +}; + +module_platform_driver(cdns_wdt_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Watchdog driver for Cadence WDT"); +MODULE_LICENSE("GPL"); -- cgit v1.1 From dfa07141e7a792aecf98a8a99dd40df0bf91bce2 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 23 Sep 2014 15:42:11 +0800 Subject: watchdog: dw_wdt: initialise TOP_INIT in dw_wdt_set_top() The TOP_INIT, ie bit 4-7 of the WDOG_TIMEOUT_RANGE_REG_OFFSET register may be zero, so the timeout period may be very short after initialization is done, thus the system may be reset soon after enabling. We fix this problem by also initialising the TOP_INIT when setting TOP in function dw_wdt_set_top(). Signed-off-by: Jisheng Zhang Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 9f21029..449c885 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -40,6 +40,7 @@ #define WDOG_CONTROL_REG_OFFSET 0x00 #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 #define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04 +#define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT 4 #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 @@ -106,7 +107,8 @@ static int dw_wdt_set_top(unsigned top_s) } /* Set the new value in the watchdog. */ - writel(top_val, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, + dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); dw_wdt_set_next_heartbeat(); -- cgit v1.1 From 1094ebe9d1e1dde0754ff8cede16159fb20b2f3b Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Thu, 25 Sep 2014 17:51:02 -0500 Subject: watchdog: qcom: add support for KPSS WDT Add a driver for the watchdog timer block found in the Krait Processor Subsystem (KPSS) on the MSM8960, APQ8064, and IPQ8064. Signed-off-by: Josh Cartwright Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 13 ++++ drivers/watchdog/Makefile | 1 + drivers/watchdog/qcom-wdt.c | 186 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 drivers/watchdog/qcom-wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 786f8c8..17b39b8 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -461,6 +461,19 @@ config TEGRA_WATCHDOG To compile this driver as a module, choose M here: the module will be called tegra_wdt. +config QCOM_WDT + tristate "QCOM watchdog" + depends on HAS_IOMEM + depends on ARCH_QCOM + select WATCHDOG_CORE + help + Say Y here to include Watchdog timer support for the watchdog found + on QCOM chipsets. Currently supported targets are the MSM8960, + APQ8064, and IPQ8064. + + To compile this driver as a module, choose M here: the + module will be called qcom_wdt. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 82af995..44f5d68 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o +obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c new file mode 100644 index 0000000..68db322 --- /dev/null +++ b/drivers/watchdog/qcom-wdt.c @@ -0,0 +1,186 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include + +#define WDT_RST 0x0 +#define WDT_EN 0x8 +#define WDT_BITE_TIME 0x24 + +struct qcom_wdt { + struct watchdog_device wdd; + struct clk *clk; + unsigned long rate; + void __iomem *base; +}; + +static inline +struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd) +{ + return container_of(wdd, struct qcom_wdt, wdd); +} + +static int qcom_wdt_start(struct watchdog_device *wdd) +{ + struct qcom_wdt *wdt = to_qcom_wdt(wdd); + + writel(0, wdt->base + WDT_EN); + writel(1, wdt->base + WDT_RST); + writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME); + writel(1, wdt->base + WDT_EN); + return 0; +} + +static int qcom_wdt_stop(struct watchdog_device *wdd) +{ + struct qcom_wdt *wdt = to_qcom_wdt(wdd); + + writel(0, wdt->base + WDT_EN); + return 0; +} + +static int qcom_wdt_ping(struct watchdog_device *wdd) +{ + struct qcom_wdt *wdt = to_qcom_wdt(wdd); + + writel(1, wdt->base + WDT_RST); + return 0; +} + +static int qcom_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + wdd->timeout = timeout; + return qcom_wdt_start(wdd); +} + +static const struct watchdog_ops qcom_wdt_ops = { + .start = qcom_wdt_start, + .stop = qcom_wdt_stop, + .ping = qcom_wdt_ping, + .set_timeout = qcom_wdt_set_timeout, + .owner = THIS_MODULE, +}; + +static const struct watchdog_info qcom_wdt_info = { + .options = WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE + | WDIOF_SETTIMEOUT, + .identity = KBUILD_MODNAME, +}; + +static int qcom_wdt_probe(struct platform_device *pdev) +{ + struct qcom_wdt *wdt; + struct resource *res; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(wdt->base)) + return PTR_ERR(wdt->base); + + wdt->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(wdt->clk)) { + dev_err(&pdev->dev, "failed to get input clock\n"); + return PTR_ERR(wdt->clk); + } + + ret = clk_prepare_enable(wdt->clk); + if (ret) { + dev_err(&pdev->dev, "failed to setup clock\n"); + return ret; + } + + /* + * We use the clock rate to calculate the max timeout, so ensure it's + * not zero to avoid a divide-by-zero exception. + * + * WATCHDOG_CORE assumes units of seconds, if the WDT is clocked such + * that it would bite before a second elapses it's usefulness is + * limited. Bail if this is the case. + */ + wdt->rate = clk_get_rate(wdt->clk); + if (wdt->rate == 0 || + wdt->rate > 0x10000000U) { + dev_err(&pdev->dev, "invalid clock rate\n"); + ret = -EINVAL; + goto err_clk_unprepare; + } + + wdt->wdd.dev = &pdev->dev; + wdt->wdd.info = &qcom_wdt_info; + wdt->wdd.ops = &qcom_wdt_ops; + wdt->wdd.min_timeout = 1; + wdt->wdd.max_timeout = 0x10000000U / wdt->rate; + + /* + * If 'timeout-sec' unspecified in devicetree, assume a 30 second + * default, unless the max timeout is less than 30 seconds, then use + * the max instead. + */ + wdt->wdd.timeout = min(wdt->wdd.max_timeout, 30U); + watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev); + + ret = watchdog_register_device(&wdt->wdd); + if (ret) { + dev_err(&pdev->dev, "failed to register watchdog\n"); + goto err_clk_unprepare; + } + + platform_set_drvdata(pdev, wdt); + return 0; + +err_clk_unprepare: + clk_disable_unprepare(wdt->clk); + return ret; +} + +static int qcom_wdt_remove(struct platform_device *pdev) +{ + struct qcom_wdt *wdt = platform_get_drvdata(pdev); + + watchdog_unregister_device(&wdt->wdd); + clk_disable_unprepare(wdt->clk); + return 0; +} + +static const struct of_device_id qcom_wdt_of_table[] = { + { .compatible = "qcom,kpss-wdt-msm8960", }, + { .compatible = "qcom,kpss-wdt-apq8064", }, + { .compatible = "qcom,kpss-wdt-ipq8064", }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_wdt_of_table); + +static struct platform_driver qcom_watchdog_driver = { + .probe = qcom_wdt_probe, + .remove = qcom_wdt_remove, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = qcom_wdt_of_table, + }, +}; +module_platform_driver(qcom_watchdog_driver); + +MODULE_DESCRIPTION("QCOM KPSS Watchdog Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 2b9366b669679f1388457ec5a62f9dd1d0a78b08 Mon Sep 17 00:00:00 2001 From: Naveen Krishna Chatradhi Date: Wed, 27 Aug 2014 15:17:11 +0530 Subject: watchdog: s3c2410_wdt: Add support for Watchdog device on Exynos7 Exynos7 SoC has a Watchdog for Atlas (A57) cores This patch adds support for the Atlas watchdog. Signed-off-by: Naveen Krishna Chatradhi Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/s3c2410_wdt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 7c6ccd0..015256e 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -155,6 +155,15 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = { .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, }; +static const struct s3c2410_wdt_variant drv_data_exynos7 = { + .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, + .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, + .mask_bit = 0, + .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, + .rst_stat_bit = 23, /* A57 WDTRESET */ + .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, +}; + static const struct of_device_id s3c2410_wdt_match[] = { { .compatible = "samsung,s3c2410-wdt", .data = &drv_data_s3c2410 }, @@ -162,6 +171,8 @@ static const struct of_device_id s3c2410_wdt_match[] = { .data = &drv_data_exynos5250 }, { .compatible = "samsung,exynos5420-wdt", .data = &drv_data_exynos5420 }, + { .compatible = "samsung,exynos7-wdt", + .data = &drv_data_exynos7 }, {}, }; MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); -- cgit v1.1 From 22b1c841e31510c3124c88a13b8a7ada14e2e2d1 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 29 Sep 2014 00:39:47 +0200 Subject: watchdog: add driver for Ricoh RN5T618 watchdog This adds a driver for the watchdog timer available in Ricoh RN5T618 PMIC. The device supports a programmable expiration time of 1, 8, 32 or 128 seconds. Signed-off-by: Beniamino Galvani Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 11 +++ drivers/watchdog/Makefile | 1 + drivers/watchdog/rn5t618_wdt.c | 198 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 drivers/watchdog/rn5t618_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 17b39b8..4edae088 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -327,6 +327,17 @@ config ORION_WATCHDOG To compile this driver as a module, choose M here: the module will be called orion_wdt. +config RN5T618_WATCHDOG + tristate "Ricoh RN5T618 watchdog" + depends on MFD_RN5T618 + select WATCHDOG_CORE + help + If you say yes here you get support for watchdog on the Ricoh + RN5T618 PMIC. + + This driver can also be built as a module. If so, the module + will be called rn5t618_wdt. + config SUNXI_WATCHDOG tristate "Allwinner SoCs watchdog support" depends on ARCH_SUNXI diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 44f5d68..f105999 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o +obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o diff --git a/drivers/watchdog/rn5t618_wdt.c b/drivers/watchdog/rn5t618_wdt.c new file mode 100644 index 0000000..d1c1227 --- /dev/null +++ b/drivers/watchdog/rn5t618_wdt.c @@ -0,0 +1,198 @@ +/* + * Watchdog driver for Ricoh RN5T618 PMIC + * + * Copyright (C) 2014 Beniamino Galvani + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#define DRIVER_NAME "rn5t618-wdt" + +static bool nowayout = WATCHDOG_NOWAYOUT; +static unsigned int timeout; + +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct rn5t618_wdt { + struct watchdog_device wdt_dev; + struct rn5t618 *rn5t618; +}; + +/* + * This array encodes the values of WDOGTIM field for the supported + * watchdog expiration times. If the watchdog is not accessed before + * the timer expiration, the PMU generates an interrupt and if the CPU + * doesn't clear it within one second the system is restarted. + */ +static const struct { + u8 reg_val; + unsigned int time; +} rn5t618_wdt_map[] = { + { 0, 1 }, + { 1, 8 }, + { 2, 32 }, + { 3, 128 }, +}; + +static int rn5t618_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int t) +{ + struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); + int ret, i; + + for (i = 0; i < ARRAY_SIZE(rn5t618_wdt_map); i++) { + if (rn5t618_wdt_map[i].time + 1 >= t) + break; + } + + if (i == ARRAY_SIZE(rn5t618_wdt_map)) + return -EINVAL; + + ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, + RN5T618_WATCHDOG_WDOGTIM_M, + rn5t618_wdt_map[i].reg_val); + if (!ret) + wdt_dev->timeout = rn5t618_wdt_map[i].time; + + return ret; +} + +static int rn5t618_wdt_start(struct watchdog_device *wdt_dev) +{ + struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); + int ret; + + ret = rn5t618_wdt_set_timeout(wdt_dev, wdt_dev->timeout); + if (ret) + return ret; + + /* enable repower-on */ + ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_REPCNT, + RN5T618_REPCNT_REPWRON, + RN5T618_REPCNT_REPWRON); + if (ret) + return ret; + + /* enable watchdog */ + ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, + RN5T618_WATCHDOG_WDOGEN, + RN5T618_WATCHDOG_WDOGEN); + if (ret) + return ret; + + /* enable watchdog interrupt */ + return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIREN, + RN5T618_PWRIRQ_IR_WDOG, + RN5T618_PWRIRQ_IR_WDOG); +} + +static int rn5t618_wdt_stop(struct watchdog_device *wdt_dev) +{ + struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); + + return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, + RN5T618_WATCHDOG_WDOGEN, 0); +} + +static int rn5t618_wdt_ping(struct watchdog_device *wdt_dev) +{ + struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); + unsigned int val; + int ret; + + /* The counter is restarted after a R/W access to watchdog register */ + ret = regmap_read(wdt->rn5t618->regmap, RN5T618_WATCHDOG, &val); + if (ret) + return ret; + + ret = regmap_write(wdt->rn5t618->regmap, RN5T618_WATCHDOG, val); + if (ret) + return ret; + + /* Clear pending watchdog interrupt */ + return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIRQ, + RN5T618_PWRIRQ_IR_WDOG, 0); +} + +static struct watchdog_info rn5t618_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .identity = DRIVER_NAME, +}; + +static struct watchdog_ops rn5t618_wdt_ops = { + .owner = THIS_MODULE, + .start = rn5t618_wdt_start, + .stop = rn5t618_wdt_stop, + .ping = rn5t618_wdt_ping, + .set_timeout = rn5t618_wdt_set_timeout, +}; + +static int rn5t618_wdt_probe(struct platform_device *pdev) +{ + struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent); + struct rn5t618_wdt *wdt; + int min_timeout, max_timeout; + + wdt = devm_kzalloc(&pdev->dev, sizeof(struct rn5t618_wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + min_timeout = rn5t618_wdt_map[0].time; + max_timeout = rn5t618_wdt_map[ARRAY_SIZE(rn5t618_wdt_map) - 1].time; + + wdt->rn5t618 = rn5t618; + wdt->wdt_dev.info = &rn5t618_wdt_info; + wdt->wdt_dev.ops = &rn5t618_wdt_ops; + wdt->wdt_dev.min_timeout = min_timeout; + wdt->wdt_dev.max_timeout = max_timeout; + wdt->wdt_dev.timeout = max_timeout; + wdt->wdt_dev.parent = &pdev->dev; + + watchdog_set_drvdata(&wdt->wdt_dev, wdt); + watchdog_init_timeout(&wdt->wdt_dev, timeout, &pdev->dev); + watchdog_set_nowayout(&wdt->wdt_dev, nowayout); + + platform_set_drvdata(pdev, wdt); + + return watchdog_register_device(&wdt->wdt_dev); +} + +static int rn5t618_wdt_remove(struct platform_device *pdev) +{ + struct rn5t618_wdt *wdt = platform_get_drvdata(pdev); + + watchdog_unregister_device(&wdt->wdt_dev); + + return 0; +} + +static struct platform_driver rn5t618_wdt_driver = { + .probe = rn5t618_wdt_probe, + .remove = rn5t618_wdt_remove, + .driver = { + .name = DRIVER_NAME, + }, +}; + +module_platform_driver(rn5t618_wdt_driver); + +MODULE_AUTHOR("Beniamino Galvani "); +MODULE_DESCRIPTION("RN5T618 watchdog driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 5e9c16e3760893b3721f599f180795ca7160afef Mon Sep 17 00:00:00 2001 From: Krystian Garbaciak Date: Sun, 28 Sep 2014 19:05:45 +0200 Subject: watchdog: Add DA9063 PMIC watchdog driver. This driver supports the watchdog device inside the DA9063 PMIC. Signed-off-by: Krystian Garbaciak Signed-off-by: Philipp Zabel Signed-off-by: Markus Pargmann Acked-by: Steve Twiss Tested-by: Steve Twiss Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 9 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/da9063_wdt.c | 191 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 drivers/watchdog/da9063_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 4edae088..a51ccf3 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -87,6 +87,15 @@ config DA9055_WATCHDOG This driver can also be built as a module. If so, the module will be called da9055_wdt. +config DA9063_WATCHDOG + tristate "Dialog DA9063 Watchdog" + depends on MFD_DA9063 + select WATCHDOG_CORE + help + Support for the watchdog in the DA9063 PMIC. + + This driver can be built as a module. The module name is da9063_wdt. + config GPIO_WATCHDOG tristate "Watchdog device controlled through GPIO-line" depends on OF_GPIO diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index f105999..f8eaaf45 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -176,6 +176,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o # Architecture Independent obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o +obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c new file mode 100644 index 0000000..2cd6b2c --- /dev/null +++ b/drivers/watchdog/da9063_wdt.c @@ -0,0 +1,191 @@ +/* + * Watchdog driver for DA9063 PMICs. + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: Mariusz Wojtasik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Watchdog selector to timeout in seconds. + * 0: WDT disabled; + * others: timeout = 2048 ms * 2^(TWDSCALE-1). + */ +static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; +#define DA9063_TWDSCALE_DISABLE 0 +#define DA9063_TWDSCALE_MIN 1 +#define DA9063_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1) +#define DA9063_WDT_MIN_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MIN] +#define DA9063_WDT_MAX_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MAX] +#define DA9063_WDG_TIMEOUT wdt_timeout[3] + +struct da9063_watchdog { + struct da9063 *da9063; + struct watchdog_device wdtdev; +}; + +static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs) +{ + unsigned int i; + + for (i = DA9063_TWDSCALE_MIN; i <= DA9063_TWDSCALE_MAX; i++) { + if (wdt_timeout[i] >= secs) + return i; + } + + return DA9063_TWDSCALE_MAX; +} + +static int _da9063_wdt_set_timeout(struct da9063 *da9063, unsigned int regval) +{ + return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D, + DA9063_TWDSCALE_MASK, regval); +} + +static int da9063_wdt_start(struct watchdog_device *wdd) +{ + struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); + unsigned int selector; + int ret; + + selector = da9063_wdt_timeout_to_sel(wdt->wdtdev.timeout); + ret = _da9063_wdt_set_timeout(wdt->da9063, selector); + if (ret) + dev_err(wdt->da9063->dev, "Watchdog failed to start (err = %d)\n", + ret); + + return ret; +} + +static int da9063_wdt_stop(struct watchdog_device *wdd) +{ + struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); + int ret; + + ret = regmap_update_bits(wdt->da9063->regmap, DA9063_REG_CONTROL_D, + DA9063_TWDSCALE_MASK, DA9063_TWDSCALE_DISABLE); + if (ret) + dev_alert(wdt->da9063->dev, "Watchdog failed to stop (err = %d)\n", + ret); + + return ret; +} + +static int da9063_wdt_ping(struct watchdog_device *wdd) +{ + struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); + int ret; + + ret = regmap_write(wdt->da9063->regmap, DA9063_REG_CONTROL_F, + DA9063_WATCHDOG); + if (ret) + dev_alert(wdt->da9063->dev, "Failed to ping the watchdog (err = %d)\n", + ret); + + return ret; +} + +static int da9063_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); + unsigned int selector; + int ret; + + selector = da9063_wdt_timeout_to_sel(timeout); + ret = _da9063_wdt_set_timeout(wdt->da9063, selector); + if (ret) + dev_err(wdt->da9063->dev, "Failed to set watchdog timeout (err = %d)\n", + ret); + else + wdd->timeout = wdt_timeout[selector]; + + return ret; +} + +static const struct watchdog_info da9063_watchdog_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "DA9063 Watchdog", +}; + +static const struct watchdog_ops da9063_watchdog_ops = { + .owner = THIS_MODULE, + .start = da9063_wdt_start, + .stop = da9063_wdt_stop, + .ping = da9063_wdt_ping, + .set_timeout = da9063_wdt_set_timeout, +}; + +static int da9063_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct da9063 *da9063; + struct da9063_watchdog *wdt; + + if (!pdev->dev.parent) + return -EINVAL; + + da9063 = dev_get_drvdata(pdev->dev.parent); + if (!da9063) + return -EINVAL; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->da9063 = da9063; + + wdt->wdtdev.info = &da9063_watchdog_info; + wdt->wdtdev.ops = &da9063_watchdog_ops; + wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT; + wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT; + wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT; + + wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; + + watchdog_set_drvdata(&wdt->wdtdev, wdt); + dev_set_drvdata(&pdev->dev, wdt); + + ret = watchdog_register_device(&wdt->wdtdev); + + return ret; +} + +static int da9063_wdt_remove(struct platform_device *pdev) +{ + struct da9063_watchdog *wdt = dev_get_drvdata(&pdev->dev); + + watchdog_unregister_device(&wdt->wdtdev); + + return 0; +} + +static struct platform_driver da9063_wdt_driver = { + .probe = da9063_wdt_probe, + .remove = da9063_wdt_remove, + .driver = { + .name = DA9063_DRVNAME_WATCHDOG, + }, +}; +module_platform_driver(da9063_wdt_driver); + +MODULE_AUTHOR("Mariusz Wojtasik "); +MODULE_DESCRIPTION("Watchdog driver for Dialog DA9063"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DA9063_DRVNAME_WATCHDOG); -- cgit v1.1 From 3281b85c8697938e344d67144ca8ba520fa54d2b Mon Sep 17 00:00:00 2001 From: Janusz Uzycki Date: Mon, 22 Sep 2014 22:55:47 +0200 Subject: stmp3xxx_rtc_wdt: Add suspend/resume PM support There is no conflict with rtc/rtc-stmp3xxx.c parent because modified registers in PM functions of stmp3xxx_rtc_wdt are different. Signed-off-by: Janusz Uzycki Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/stmp3xxx_rtc_wdt.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c index 3804d5e..a62b1b6 100644 --- a/drivers/watchdog/stmp3xxx_rtc_wdt.c +++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c @@ -94,9 +94,33 @@ static int stmp3xxx_wdt_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused stmp3xxx_wdt_suspend(struct device *dev) +{ + struct watchdog_device *wdd = &stmp3xxx_wdd; + + if (watchdog_active(wdd)) + return wdt_stop(wdd); + + return 0; +} + +static int __maybe_unused stmp3xxx_wdt_resume(struct device *dev) +{ + struct watchdog_device *wdd = &stmp3xxx_wdd; + + if (watchdog_active(wdd)) + return wdt_start(wdd); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(stmp3xxx_wdt_pm_ops, + stmp3xxx_wdt_suspend, stmp3xxx_wdt_resume); + static struct platform_driver stmp3xxx_wdt_driver = { .driver = { .name = "stmp3xxx_rtc_wdt", + .pm = &stmp3xxx_wdt_pm_ops, }, .probe = stmp3xxx_wdt_probe, .remove = stmp3xxx_wdt_remove, -- cgit v1.1 From 22e1b8f60f913cf71e688af9b64317b515303f4c Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Sat, 20 Sep 2014 19:06:50 +0200 Subject: ARM: meson: add watchdog driver This patch adds the watchdog driver for the Amlogic Meson SoCs used also to reboot the device. Signed-off-by: Carlo Caione Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 10 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/meson_wdt.c | 236 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 247 insertions(+) create mode 100644 drivers/watchdog/meson_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index a51ccf3..9ff8588 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -494,6 +494,16 @@ config QCOM_WDT To compile this driver as a module, choose M here: the module will be called qcom_wdt. +config MESON_WATCHDOG + tristate "Amlogic Meson SoCs watchdog support" + depends on ARCH_MESON + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer + in Amlogic Meson SoCs. + To compile this driver as a module, choose M here: the + module will be called meson_wdt. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index f8eaaf45..c569ec8 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o +obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/meson_wdt.c b/drivers/watchdog/meson_wdt.c new file mode 100644 index 0000000..37f9f5ec --- /dev/null +++ b/drivers/watchdog/meson_wdt.c @@ -0,0 +1,236 @@ +/* + * Meson Watchdog Driver + * + * Copyright (c) 2014 Carlo Caione + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "meson_wdt" + +#define MESON_WDT_TC 0x00 +#define MESON_WDT_TC_EN BIT(22) +#define MESON_WDT_TC_TM_MASK 0x3fffff +#define MESON_WDT_DC_RESET (3 << 24) + +#define MESON_WDT_RESET 0x04 + +#define MESON_WDT_TIMEOUT 30 +#define MESON_WDT_MIN_TIMEOUT 1 +#define MESON_WDT_MAX_TIMEOUT (MESON_WDT_TC_TM_MASK / 100000) + +#define MESON_SEC_TO_TC(s) ((s) * 100000) + +static bool nowayout = WATCHDOG_NOWAYOUT; +static unsigned int timeout = MESON_WDT_TIMEOUT; + +struct meson_wdt_dev { + struct watchdog_device wdt_dev; + void __iomem *wdt_base; + struct notifier_block restart_handler; +}; + +static int meson_restart_handle(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + u32 tc_reboot = MESON_WDT_DC_RESET | MESON_WDT_TC_EN | 100; + struct meson_wdt_dev *meson_wdt = container_of(this, + struct meson_wdt_dev, + restart_handler); + + while (1) { + writel(tc_reboot, meson_wdt->wdt_base + MESON_WDT_TC); + mdelay(5); + } + + return NOTIFY_DONE; +} + +static int meson_wdt_ping(struct watchdog_device *wdt_dev) +{ + struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); + + writel(0, meson_wdt->wdt_base + MESON_WDT_RESET); + + return 0; +} + +static void meson_wdt_change_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); + u32 reg; + + reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); + reg &= ~MESON_WDT_TC_TM_MASK; + reg |= MESON_SEC_TO_TC(timeout); + writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); +} + +static int meson_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + wdt_dev->timeout = timeout; + + meson_wdt_change_timeout(wdt_dev, timeout); + meson_wdt_ping(wdt_dev); + + return 0; +} + +static int meson_wdt_stop(struct watchdog_device *wdt_dev) +{ + struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); + u32 reg; + + reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); + reg &= ~MESON_WDT_TC_EN; + writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); + + return 0; +} + +static int meson_wdt_start(struct watchdog_device *wdt_dev) +{ + struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); + u32 reg; + + meson_wdt_change_timeout(wdt_dev, meson_wdt->wdt_dev.timeout); + meson_wdt_ping(wdt_dev); + + reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); + reg |= MESON_WDT_TC_EN; + writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); + + return 0; +} + +static const struct watchdog_info meson_wdt_info = { + .identity = DRV_NAME, + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops meson_wdt_ops = { + .owner = THIS_MODULE, + .start = meson_wdt_start, + .stop = meson_wdt_stop, + .ping = meson_wdt_ping, + .set_timeout = meson_wdt_set_timeout, +}; + +static int meson_wdt_probe(struct platform_device *pdev) +{ + struct resource *res; + struct meson_wdt_dev *meson_wdt; + int err; + + meson_wdt = devm_kzalloc(&pdev->dev, sizeof(*meson_wdt), GFP_KERNEL); + if (!meson_wdt) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + meson_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(meson_wdt->wdt_base)) + return PTR_ERR(meson_wdt->wdt_base); + + meson_wdt->wdt_dev.parent = &pdev->dev; + meson_wdt->wdt_dev.info = &meson_wdt_info; + meson_wdt->wdt_dev.ops = &meson_wdt_ops; + meson_wdt->wdt_dev.timeout = MESON_WDT_TIMEOUT; + meson_wdt->wdt_dev.max_timeout = MESON_WDT_MAX_TIMEOUT; + meson_wdt->wdt_dev.min_timeout = MESON_WDT_MIN_TIMEOUT; + + watchdog_set_drvdata(&meson_wdt->wdt_dev, meson_wdt); + + watchdog_init_timeout(&meson_wdt->wdt_dev, timeout, &pdev->dev); + watchdog_set_nowayout(&meson_wdt->wdt_dev, nowayout); + + meson_wdt_stop(&meson_wdt->wdt_dev); + + err = watchdog_register_device(&meson_wdt->wdt_dev); + if (err) + return err; + + platform_set_drvdata(pdev, meson_wdt); + + meson_wdt->restart_handler.notifier_call = meson_restart_handle; + meson_wdt->restart_handler.priority = 128; + err = register_restart_handler(&meson_wdt->restart_handler); + if (err) + dev_err(&pdev->dev, + "cannot register restart handler (err=%d)\n", err); + + dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", + meson_wdt->wdt_dev.timeout, nowayout); + + return 0; +} + +static int meson_wdt_remove(struct platform_device *pdev) +{ + struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev); + + unregister_restart_handler(&meson_wdt->restart_handler); + + watchdog_unregister_device(&meson_wdt->wdt_dev); + + return 0; +} + +static void meson_wdt_shutdown(struct platform_device *pdev) +{ + struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev); + + meson_wdt_stop(&meson_wdt->wdt_dev); +} + +static const struct of_device_id meson_wdt_dt_ids[] = { + { .compatible = "amlogic,meson6-wdt" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, meson_wdt_dt_ids); + +static struct platform_driver meson_wdt_driver = { + .probe = meson_wdt_probe, + .remove = meson_wdt_remove, + .shutdown = meson_wdt_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .of_match_table = meson_wdt_dt_ids, + }, +}; + +module_platform_driver(meson_wdt_driver); + +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Carlo Caione "); +MODULE_DESCRIPTION("Meson Watchdog Timer Driver"); -- cgit v1.1 From 31228f43ab528628c9b5f1351604361aa1d78533 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 23 Sep 2014 15:42:12 +0800 Subject: watchdog: dw_wdt: add restart handler support The kernel core now provides an API to trigger a system restart. Register with it to support restarting the system via. watchdog. Signed-off-by: Jisheng Zhang Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 449c885..9e577a6 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -29,9 +30,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -63,6 +66,7 @@ static struct { unsigned long next_heartbeat; struct timer_list timer; int expect_close; + struct notifier_block restart_handler; } dw_wdt; static inline int dw_wdt_is_enabled(void) @@ -121,6 +125,26 @@ static void dw_wdt_keepalive(void) WDOG_COUNTER_RESTART_REG_OFFSET); } +static int dw_wdt_restart_handle(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + u32 val; + + writel(0, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + val = readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); + if (val & WDOG_CONTROL_REG_WDT_EN_MASK) + writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + + WDOG_COUNTER_RESTART_REG_OFFSET); + else + writel(WDOG_CONTROL_REG_WDT_EN_MASK, + dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); + + /* wait for reset to assert... */ + mdelay(500); + + return NOTIFY_DONE; +} + static void dw_wdt_ping(unsigned long data) { if (time_before(jiffies, dw_wdt.next_heartbeat) || @@ -316,6 +340,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) if (ret) goto out_disable_clk; + dw_wdt.restart_handler.notifier_call = dw_wdt_restart_handle; + dw_wdt.restart_handler.priority = 128; + ret = register_restart_handler(&dw_wdt.restart_handler); + if (ret) + pr_warn("cannot register restart handler\n"); + dw_wdt_set_next_heartbeat(); setup_timer(&dw_wdt.timer, dw_wdt_ping, 0); mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); @@ -330,6 +360,8 @@ out_disable_clk: static int dw_wdt_drv_remove(struct platform_device *pdev) { + unregister_restart_handler(&dw_wdt.restart_handler); + misc_deregister(&dw_wdt_miscdev); clk_disable_unprepare(dw_wdt.clk); -- cgit v1.1 From f286e1335f579dfd970c7fc3f62b248773a47a5c Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 19 Aug 2014 17:45:36 -0700 Subject: watchdog: s3c2410: add restart handler On a lot of Samsung systems the watchdog is responsible for restarting the system and until now this code was contained in plat-samsung/watchdog-reset.c. With the introduction of the restart handlers, this code can now move into driver itself, removing the need for arch-specific code. Tested on a S3C2442 based GTA02 Signed-off-by: Heiko Stuebner Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton --- drivers/watchdog/s3c2410_wdt.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 015256e..8532c3e 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -41,6 +41,8 @@ #include #include #include +#include +#include #define S3C2410_WTCON 0x00 #define S3C2410_WTDAT 0x04 @@ -128,6 +130,7 @@ struct s3c2410_wdt { unsigned long wtdat_save; struct watchdog_device wdt_device; struct notifier_block freq_transition; + struct notifier_block restart_handler; struct s3c2410_wdt_variant *drv_data; struct regmap *pmureg; }; @@ -449,6 +452,31 @@ static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt) } #endif +static int s3c2410wdt_restart(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct s3c2410_wdt *wdt = container_of(this, struct s3c2410_wdt, + restart_handler); + void __iomem *wdt_base = wdt->reg_base; + + /* disable watchdog, to be safe */ + writel(0, wdt_base + S3C2410_WTCON); + + /* put initial values into count and data */ + writel(0x80, wdt_base + S3C2410_WTCNT); + writel(0x80, wdt_base + S3C2410_WTDAT); + + /* set the watchdog to go and reset... */ + writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV16 | + S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x20), + wdt_base + S3C2410_WTCON); + + /* wait for reset to assert... */ + mdelay(500); + + return NOTIFY_DONE; +} + static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) { unsigned int rst_stat; @@ -603,6 +631,12 @@ static int s3c2410wdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, wdt); + wdt->restart_handler.notifier_call = s3c2410wdt_restart; + wdt->restart_handler.priority = 128; + ret = register_restart_handler(&wdt->restart_handler); + if (ret) + pr_err("cannot register restart handler, %d\n", ret); + /* print out a statement of readiness */ wtcon = readl(wdt->reg_base + S3C2410_WTCON); @@ -632,6 +666,8 @@ static int s3c2410wdt_remove(struct platform_device *dev) int ret; struct s3c2410_wdt *wdt = platform_get_drvdata(dev); + unregister_restart_handler(&wdt->restart_handler); + ret = s3c2410wdt_mask_and_disable_reset(wdt, true); if (ret < 0) return ret; -- cgit v1.1 From f974008f07a62171a9dede08250c9a35c2b2b986 Mon Sep 17 00:00:00 2001 From: Olivier Gay Date: Sat, 18 Oct 2014 01:53:39 +0200 Subject: HID: add keyboard input assist hid usages Add keyboard input assist controls usages from approved hid usage table request HUTTR42: http://www.usb.org/developers/hidpage/HUTRR42c.pdf Signed-off-by: Olivier Gay Acked-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/hid-debug.c | 6 ++++++ drivers/hid/hid-input.c | 7 +++++++ 2 files changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 84c3cb1..8bf61d2 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -946,6 +946,12 @@ static const char *keys[KEY_MAX + 1] = { [KEY_BRIGHTNESS_MIN] = "BrightnessMin", [KEY_BRIGHTNESS_MAX] = "BrightnessMax", [KEY_BRIGHTNESS_AUTO] = "BrightnessAuto", + [KEY_KBDINPUTASSIST_PREV] = "KbdInputAssistPrev", + [KEY_KBDINPUTASSIST_NEXT] = "KbdInputAssistNext", + [KEY_KBDINPUTASSIST_PREVGROUP] = "KbdInputAssistPrevGroup", + [KEY_KBDINPUTASSIST_NEXTGROUP] = "KbdInputAssistNextGroup", + [KEY_KBDINPUTASSIST_ACCEPT] = "KbdInputAssistAccept", + [KEY_KBDINPUTASSIST_CANCEL] = "KbdInputAssistCancel", }; static const char *relatives[REL_MAX + 1] = { diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 2df7fdd..56c6c30 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -862,6 +862,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x28b: map_key_clear(KEY_FORWARDMAIL); break; case 0x28c: map_key_clear(KEY_SEND); break; + case 0x2c7: map_key_clear(KEY_KBDINPUTASSIST_PREV); break; + case 0x2c8: map_key_clear(KEY_KBDINPUTASSIST_NEXT); break; + case 0x2c9: map_key_clear(KEY_KBDINPUTASSIST_PREVGROUP); break; + case 0x2ca: map_key_clear(KEY_KBDINPUTASSIST_NEXTGROUP); break; + case 0x2cb: map_key_clear(KEY_KBDINPUTASSIST_ACCEPT); break; + case 0x2cc: map_key_clear(KEY_KBDINPUTASSIST_CANCEL); break; + default: goto ignore; } break; -- cgit v1.1 From 05e487d905ab29b5756d6d1e47e27eefa6693fb3 Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Thu, 25 Sep 2014 17:51:04 -0500 Subject: watchdog: qcom: register a restart notifier The WDT's BITE_TIME warm-reset behavior can be leveraged as a last resort mechanism for triggering chip reset. Usually, other restart methods (such as PS_HOLD) are preferrable for issuing a more complete reset of the chip. As such, keep the priority of the watchdog notifier low. Signed-off-by: Josh Cartwright Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/qcom-wdt.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 68db322..aa85618 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -11,11 +11,13 @@ * */ #include +#include #include #include #include #include #include +#include #include #define WDT_RST 0x0 @@ -26,6 +28,7 @@ struct qcom_wdt { struct watchdog_device wdd; struct clk *clk; unsigned long rate; + struct notifier_block restart_nb; void __iomem *base; }; @@ -84,6 +87,32 @@ static const struct watchdog_info qcom_wdt_info = { .identity = KBUILD_MODNAME, }; +static int qcom_wdt_restart(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct qcom_wdt *wdt = container_of(nb, struct qcom_wdt, restart_nb); + u32 timeout; + + /* + * Trigger watchdog bite: + * Setup BITE_TIME to be 128ms, and enable WDT. + */ + timeout = 128 * wdt->rate / 1000; + + writel(0, wdt->base + WDT_EN); + writel(1, wdt->base + WDT_RST); + writel(timeout, wdt->base + WDT_BITE_TIME); + writel(1, wdt->base + WDT_EN); + + /* + * Actually make sure the above sequence hits hardware before sleeping. + */ + wmb(); + + msleep(150); + return NOTIFY_DONE; +} + static int qcom_wdt_probe(struct platform_device *pdev) { struct qcom_wdt *wdt; @@ -147,6 +176,14 @@ static int qcom_wdt_probe(struct platform_device *pdev) goto err_clk_unprepare; } + /* + * WDT restart notifier has priority 0 (use as a last resort) + */ + wdt->restart_nb.notifier_call = qcom_wdt_restart; + ret = register_restart_handler(&wdt->restart_nb); + if (ret) + dev_err(&pdev->dev, "failed to setup restart handler\n"); + platform_set_drvdata(pdev, wdt); return 0; @@ -159,6 +196,7 @@ static int qcom_wdt_remove(struct platform_device *pdev) { struct qcom_wdt *wdt = platform_get_drvdata(pdev); + unregister_restart_handler(&wdt->restart_nb); watchdog_unregister_device(&wdt->wdd); clk_disable_unprepare(wdt->clk); return 0; -- cgit v1.1 From 334a9d8131254e06685b2af0c0f3cc7b3ec5bd04 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 12 Sep 2014 15:24:36 +0800 Subject: watchdog: imx2_wdt: add restart handler support Register the watchdog as the system restart function to the new introducing kernel restart call chain in the driver instead of providing the restart in machine desc. This restart handler function is from the mxc_restart() in arch/arm/mach-imx/system.c Signed-off-by: Jingchang Lu Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/imx2_wdt.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index f37bb05..7e12f88 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -22,14 +22,17 @@ */ #include +#include #include #include #include #include #include #include +#include #include #include +#include #include #include #include @@ -59,6 +62,7 @@ struct imx2_wdt_device { struct regmap *regmap; struct timer_list timer; /* Pings the watchdog when closed */ struct watchdog_device wdog; + struct notifier_block restart_handler; }; static bool nowayout = WATCHDOG_NOWAYOUT; @@ -77,6 +81,31 @@ static const struct watchdog_info imx2_wdt_info = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, }; +static int imx2_restart_handler(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + unsigned int wcr_enable = IMX2_WDT_WCR_WDE; + struct imx2_wdt_device *wdev = container_of(this, + struct imx2_wdt_device, + restart_handler); + /* Assert SRS signal */ + regmap_write(wdev->regmap, 0, wcr_enable); + /* + * Due to imx6q errata ERR004346 (WDOG: WDOG SRS bit requires to be + * written twice), we add another two writes to ensure there must be at + * least two writes happen in the same one 32kHz clock period. We save + * the target check here, since the writes shouldn't be a huge burden + * for other platforms. + */ + regmap_write(wdev->regmap, 0, wcr_enable); + regmap_write(wdev->regmap, 0, wcr_enable); + + /* wait for reset to assert... */ + mdelay(500); + + return NOTIFY_DONE; +} + static inline void imx2_wdt_setup(struct watchdog_device *wdog) { struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); @@ -251,6 +280,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) return ret; } + wdev->restart_handler.notifier_call = imx2_restart_handler; + wdev->restart_handler.priority = 128; + ret = register_restart_handler(&wdev->restart_handler); + if (ret) + dev_err(&pdev->dev, "cannot register restart handler\n"); + dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n", wdog->timeout, nowayout); @@ -262,6 +297,8 @@ static int __exit imx2_wdt_remove(struct platform_device *pdev) struct watchdog_device *wdog = platform_get_drvdata(pdev); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + unregister_restart_handler(&wdev->restart_handler); + watchdog_unregister_device(wdog); if (imx2_wdt_is_running(wdev)) { -- cgit v1.1 From f2147de334703c7c44372f013d7d466d756e6943 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 22 Sep 2014 00:05:18 +0800 Subject: watchdog: sunxi: support parameterized compatible strings This patch adds support for hardware parameters tied to compatible strings, so similar hardware can reuse the driver. This will be used to support the newer watchdog found in A31 and later SoCs. Differences in the new hardware include separate interrupt lines for each watchdog, and corresponding interrupt control/status registers. Watchdog control registers were also slightly rearranged. Also replace ioread32()/iowrite32() with readl()/writel() in various places changed. Signed-off-by: Chen-Yu Tsai Signed-off-by: Guenter Roeck Acked-by: Heiko Stuebner Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton --- drivers/watchdog/sunxi_wdt.c | 101 ++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 480bb55..a1f7113 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -30,15 +31,11 @@ #define WDT_MAX_TIMEOUT 16 #define WDT_MIN_TIMEOUT 1 -#define WDT_MODE_TIMEOUT(n) ((n) << 3) -#define WDT_TIMEOUT_MASK WDT_MODE_TIMEOUT(0x0F) +#define WDT_TIMEOUT_MASK 0x0F -#define WDT_CTRL 0x00 #define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1)) -#define WDT_MODE 0x04 #define WDT_MODE_EN (1 << 0) -#define WDT_MODE_RST_EN (1 << 1) #define DRV_NAME "sunxi-wdt" #define DRV_VERSION "1.0" @@ -46,15 +43,29 @@ static bool nowayout = WATCHDOG_NOWAYOUT; static unsigned int timeout = WDT_MAX_TIMEOUT; +/* + * This structure stores the register offsets for different variants + * of Allwinner's watchdog hardware. + */ +struct sunxi_wdt_reg { + u8 wdt_ctrl; + u8 wdt_cfg; + u8 wdt_mode; + u8 wdt_timeout_shift; + u8 wdt_reset_mask; + u8 wdt_reset_val; +}; + struct sunxi_wdt_dev { struct watchdog_device wdt_dev; void __iomem *wdt_base; + const struct sunxi_wdt_reg *wdt_regs; struct notifier_block restart_handler; }; /* * wdt_timeout_map maps the watchdog timer interval value in seconds to - * the value of the register WDT_MODE bit 3:6 + * the value of the register WDT_MODE at bits .wdt_timeout_shift ~ +3 * * [timeout seconds] = register value * @@ -82,19 +93,32 @@ static int sunxi_restart_handle(struct notifier_block *this, unsigned long mode, struct sunxi_wdt_dev, restart_handler); void __iomem *wdt_base = sunxi_wdt->wdt_base; + const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; + u32 val; + + /* Set system reset function */ + val = readl(wdt_base + regs->wdt_cfg); + val &= ~(regs->wdt_reset_mask); + val |= regs->wdt_reset_val; + writel(val, wdt_base + regs->wdt_cfg); - /* Enable timer and set reset bit in the watchdog */ - writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE); + /* Set lowest timeout and enable watchdog */ + val = readl(wdt_base + regs->wdt_mode); + val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift); + val |= WDT_MODE_EN; + writel(val, wdt_base + regs->wdt_mode); /* * Restart the watchdog. The default (and lowest) interval * value for the watchdog is 0.5s. */ - writel(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL); + writel(WDT_CTRL_RELOAD, wdt_base + regs->wdt_ctrl); while (1) { mdelay(5); - writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE); + val = readl(wdt_base + regs->wdt_mode); + val |= WDT_MODE_EN; + writel(val, wdt_base + regs->wdt_mode); } return NOTIFY_DONE; } @@ -103,8 +127,9 @@ static int sunxi_wdt_ping(struct watchdog_device *wdt_dev) { struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); void __iomem *wdt_base = sunxi_wdt->wdt_base; + const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; - iowrite32(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL); + writel(WDT_CTRL_RELOAD, wdt_base + regs->wdt_ctrl); return 0; } @@ -114,6 +139,7 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev, { struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); void __iomem *wdt_base = sunxi_wdt->wdt_base; + const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; u32 reg; if (wdt_timeout_map[timeout] == 0) @@ -121,10 +147,10 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev, sunxi_wdt->wdt_dev.timeout = timeout; - reg = ioread32(wdt_base + WDT_MODE); - reg &= ~WDT_TIMEOUT_MASK; - reg |= WDT_MODE_TIMEOUT(wdt_timeout_map[timeout]); - iowrite32(reg, wdt_base + WDT_MODE); + reg = readl(wdt_base + regs->wdt_mode); + reg &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift); + reg |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift; + writel(reg, wdt_base + regs->wdt_mode); sunxi_wdt_ping(wdt_dev); @@ -135,8 +161,9 @@ static int sunxi_wdt_stop(struct watchdog_device *wdt_dev) { struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); void __iomem *wdt_base = sunxi_wdt->wdt_base; + const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; - iowrite32(0, wdt_base + WDT_MODE); + writel(0, wdt_base + regs->wdt_mode); return 0; } @@ -146,6 +173,7 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev) u32 reg; struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); void __iomem *wdt_base = sunxi_wdt->wdt_base; + const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; int ret; ret = sunxi_wdt_set_timeout(&sunxi_wdt->wdt_dev, @@ -153,9 +181,16 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev) if (ret < 0) return ret; - reg = ioread32(wdt_base + WDT_MODE); - reg |= (WDT_MODE_RST_EN | WDT_MODE_EN); - iowrite32(reg, wdt_base + WDT_MODE); + /* Set system reset function */ + reg = readl(wdt_base + regs->wdt_cfg); + reg &= ~(regs->wdt_reset_mask); + reg |= ~(regs->wdt_reset_val); + writel(reg, wdt_base + regs->wdt_cfg); + + /* Enable watchdog */ + reg = readl(wdt_base + regs->wdt_mode); + reg |= WDT_MODE_EN; + writel(reg, wdt_base + regs->wdt_mode); return 0; } @@ -175,9 +210,25 @@ static const struct watchdog_ops sunxi_wdt_ops = { .set_timeout = sunxi_wdt_set_timeout, }; +static const struct sunxi_wdt_reg sun4i_wdt_reg = { + .wdt_ctrl = 0x00, + .wdt_cfg = 0x04, + .wdt_mode = 0x04, + .wdt_timeout_shift = 3, + .wdt_reset_mask = 0x02, + .wdt_reset_val = 0x02, +}; + +static const struct of_device_id sunxi_wdt_dt_ids[] = { + { .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); + static int sunxi_wdt_probe(struct platform_device *pdev) { struct sunxi_wdt_dev *sunxi_wdt; + const struct of_device_id *device; struct resource *res; int err; @@ -187,6 +238,12 @@ static int sunxi_wdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sunxi_wdt); + device = of_match_device(sunxi_wdt_dt_ids, &pdev->dev); + if (!device) + return -ENODEV; + + sunxi_wdt->wdt_regs = device->data; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(sunxi_wdt->wdt_base)) @@ -242,12 +299,6 @@ static void sunxi_wdt_shutdown(struct platform_device *pdev) sunxi_wdt_stop(&sunxi_wdt->wdt_dev); } -static const struct of_device_id sunxi_wdt_dt_ids[] = { - { .compatible = "allwinner,sun4i-a10-wdt" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); - static struct platform_driver sunxi_wdt_driver = { .probe = sunxi_wdt_probe, .remove = sunxi_wdt_remove, -- cgit v1.1 From c5ec618fbf83045b9d51679d809ddd45f990fe0a Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 22 Sep 2014 00:05:19 +0800 Subject: watchdog: sunxi: Add A31 watchdog support This patch adds support for the watchdog hardware found in A31 and newer SoCs. This new hardware has registers at different offsets, and the system reset control has been split out of the "mode" register into a new "configuration" register. Differences not supported by this driver include separate interrupt lines for each watchdog, instead of sharing an interrupt line and registers with the timer block. Signed-off-by: Chen-Yu Tsai Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sunxi_wdt.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index a1f7113..b62301e 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -219,8 +219,18 @@ static const struct sunxi_wdt_reg sun4i_wdt_reg = { .wdt_reset_val = 0x02, }; +static const struct sunxi_wdt_reg sun6i_wdt_reg = { + .wdt_ctrl = 0x10, + .wdt_cfg = 0x14, + .wdt_mode = 0x18, + .wdt_timeout_shift = 4, + .wdt_reset_mask = 0x03, + .wdt_reset_val = 0x01, +}; + static const struct of_device_id sunxi_wdt_dt_ids[] = { { .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg }, + { .compatible = "allwinner,sun6i-a31-wdt", .data = &sun6i_wdt_reg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); -- cgit v1.1 From 71fd380a6b87f384002feceda39fd670ede7ea5f Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Sun, 5 Oct 2014 09:28:33 +0800 Subject: watchdog: Let XILINX_WATCHDOG and TEGRA_WATCHDOG depend on HAS_IOMEM They need HAS_IOMEM, so let them depend on it, the related error (with allmodconfig under um): MODPOST 1205 modules ERROR: "devm_ioremap_resource" [drivers/watchdog/tegra_wdt.ko] undefined! ERROR: "devm_ioremap_resource" [drivers/watchdog/of_xilinx_wdt.ko] undefined! Signed-off-by: Chen Gang Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 9ff8588..d0107d4 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -132,6 +132,7 @@ config WM8350_WATCHDOG config XILINX_WATCHDOG tristate "Xilinx Watchdog timer" + depends on HAS_IOMEM select WATCHDOG_CORE help Watchdog driver for the xps_timebase_wdt ip core. @@ -472,7 +473,7 @@ config SIRFSOC_WATCHDOG config TEGRA_WATCHDOG tristate "Tegra watchdog" - depends on ARCH_TEGRA || COMPILE_TEST + depends on (ARCH_TEGRA || COMPILE_TEST) && HAS_IOMEM select WATCHDOG_CORE help Say Y here to include support for the watchdog timer -- cgit v1.1 From 06980b24cf9bfcc753a07ee362976169bb869869 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Thu, 9 Oct 2014 21:59:16 +0200 Subject: watchdog: meson: remove magic value for reboot This patch removes the magic value used for rebooting the board. This value is useless and leads to a static checker warning as reported by Dan Carpenter. Signed-off-by: Carlo Caione Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/meson_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/meson_wdt.c b/drivers/watchdog/meson_wdt.c index 37f9f5ec..ef6a298 100644 --- a/drivers/watchdog/meson_wdt.c +++ b/drivers/watchdog/meson_wdt.c @@ -51,7 +51,7 @@ struct meson_wdt_dev { static int meson_restart_handle(struct notifier_block *this, unsigned long mode, void *cmd) { - u32 tc_reboot = MESON_WDT_DC_RESET | MESON_WDT_TC_EN | 100; + u32 tc_reboot = MESON_WDT_DC_RESET | MESON_WDT_TC_EN; struct meson_wdt_dev *meson_wdt = container_of(this, struct meson_wdt_dev, restart_handler); -- cgit v1.1 From 4ac4fc9322b1a33f8266f2091dd5ee74a78ff4f8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 17 Sep 2014 16:28:38 -0500 Subject: usb: dwc3: trace: don't dereference pointers The way trace works is that it won't decode strings until we read the actual trace. Because of that, we can't make assumptions of pointers still being valid at the time we read the trace. In order to avoid that, just copy all fields from every struct pointer we need for our traces. Ths patch fixes the following bug: [ 2940.039229] Unable to handle kernel paging request at virtual address 814efa9e [ 2940.046904] pgd = ec3dc000 [ 2940.049737] [814efa9e] *pgd=00000000 [ 2940.053552] Internal error: Oops: 5 [#1] SMP ARM [ 2940.058379] Modules linked in: usb_f_acm u_serial g_serial usb_f_uac2 libcomposite configfs xhci_hcd dwc3 udc_core matrix_keypad dwc3_omap lis3lv02d_i2c lis3lv02d input_polldev [last unloaded: g_audio] [ 2940.077238] CPU: 0 PID: 3020 Comm: tail Tainted: G W 3.17.0-rc5-dirty #1097 [ 2940.085596] task: ed1b1040 ti: ed07c000 task.ti: ed07c000 [ 2940.091258] PC is at strnlen+0x18/0x68 [ 2940.095177] LR is at 0xfffffffe [ 2940.098454] pc : [] lr : [] psr: a0000013 [ 2940.098454] sp : ed07ddb0 ip : ed07ddc0 fp : ed07ddbc [ 2940.110445] r10: c070ff70 r9 : ed07de70 r8 : 00000000 [ 2940.115906] r7 : 814efa9e r6 : ffffffff r5 : ed4b6087 r4 : ed4b50c7 [ 2940.122726] r3 : 00000000 r2 : 814efa9e r1 : ffffffff r0 : 814efa9e [ 2940.129546] Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user [ 2940.137000] Control: 10c5387d Table: ac3dc059 DAC: 00000015 [ 2940.143006] Process tail (pid: 3020, stack limit = 0xed07c248) [ 2940.149098] Stack: (0xed07ddb0 to 0xed07e000) [ 2940.153660] dda0: ed07dde4 ed07ddc0 c0359628 c0356dec [ 2940.162203] ddc0: 00000000 ed4b50c7 bf03ae9c ed4b6087 bf03ae9e 00000002 ed07de3c ed07dde8 [ 2940.170740] dde0: c035ab50 c0359600 ffffffff ffffffff ff0a0000 ffffffff ed07de30 ed4b5088 [ 2940.179275] de00: ed4b50c7 00000fc0 ff0a0004 ffffffff ed4b5088 ed4b5088 00000000 00001000 [ 2940.187810] de20: 00001008 00000fc0 ed4b5088 00000000 ed07de68 ed07de40 c00f1e64 c035a9c4 [ 2940.196341] de40: bf03dae0 ed07de70 ed4b4000 ec25b280 ed4b4000 ec25b280 bf03dae0 ed07de9c [ 2940.204886] de60: ed07de78 bf033324 c00f1e0c bf03ae9c 814efa9e ed428bc0 814eca3e 00000000 [ 2940.213428] de80: 814eba3e ed4b4000 03bd1201 c0c34790 ed07ded4 ed07dea0 c00edc0c bf0332d0 [ 2940.221994] dea0: 000002c7 ed07df10 ed07decc ed07deb8 ed4b4000 0000209c ec278ac0 00000000 [ 2940.230536] dec0: 00002000 ec0db340 ed07def4 ed07ded8 c00ee7ec c00eda90 c00ee7b0 ec278ac0 [ 2940.239075] dee0: ed4b4000 000002d5 ed07df44 ed07def8 c018b8d0 c00ee7bc c0166d3c ec278af0 [ 2940.247621] df00: 0001f090 ed07df78 000002c7 00000000 000002c8 00000000 00000000 ec0db340 [ 2940.256173] df20: 0001f090 ed07df78 ec0db340 00002000 0001f090 00000000 ed07df74 ed07df48 [ 2940.264729] df40: c0166e98 c018b5f4 00000001 c018535c 000168c1 00000000 ec0db340 ec0db340 [ 2940.273284] df60: 00002000 0001f090 ed07dfa4 ed07df78 c01675c4 c0166e0c 000168c1 00000000 [ 2940.281829] df80: 00002000 0000000a 0001f090 00000003 c000f064 ed07c000 00000000 ed07dfa8 [ 2940.290365] dfa0: c000ede0 c0167584 00002000 0000000a 00000003 0001f090 00002000 00000000 [ 2940.298909] dfc0: 00002000 0000000a 0001f090 00000003 7fffe000 0001e1e0 00002004 0000002f [ 2940.307445] dfe0: 00000000 beed38ec 000104c8 b6e6397c 40000010 00000003 00000000 00000000 [ 2940.315992] [] (strnlen) from [] (string.isra.8+0x34/0xe8) [ 2940.323534] [] (string.isra.8) from [] (vsnprintf+0x198/0x3fc) [ 2940.331461] [] (vsnprintf) from [] (trace_seq_printf+0x68/0x94) [ 2940.339494] [] (trace_seq_printf) from [] (ftrace_raw_output_dwc3_log_request+0x60/0x78 [dwc3]) [ 2940.350424] [] (ftrace_raw_output_dwc3_log_request [dwc3]) from [] (print_trace_line+0x188/0x418) [ 2940.361507] [] (print_trace_line) from [] (s_show+0x3c/0x12c) [ 2940.369330] [] (s_show) from [] (seq_read+0x2e8/0x4a0) [ 2940.376519] [] (seq_read) from [] (vfs_read+0x98/0x158) [ 2940.383796] [] (vfs_read) from [] (SyS_read+0x4c/0xa0) [ 2940.390981] [] (SyS_read) from [] (ret_fast_syscall+0x0/0x48) [ 2940.398792] Code: e24cb004 e3510000 e241e001 0a000011 (e5d01000) [ 2940.406980] ---[ end trace d8b38370fbb531f3 ]--- Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/trace.h | 53 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index 78aff1d..60b0f41 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -73,15 +73,23 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl, TP_PROTO(struct usb_ctrlrequest *ctrl), TP_ARGS(ctrl), TP_STRUCT__entry( - __field(struct usb_ctrlrequest *, ctrl) + __field(__u8, bRequestType) + __field(__u8, bRequest) + __field(__le16, wValue) + __field(__le16, wIndex) + __field(__le16, wLength) ), TP_fast_assign( - __entry->ctrl = ctrl; + __entry->bRequestType = ctrl->bRequestType; + __entry->bRequest = ctrl->bRequest; + __entry->wValue = ctrl->wValue; + __entry->wIndex = ctrl->wIndex; + __entry->wLength = ctrl->wLength; ), TP_printk("bRequestType %02x bRequest %02x wValue %04x wIndex %04x wLength %d", - __entry->ctrl->bRequestType, __entry->ctrl->bRequest, - le16_to_cpu(__entry->ctrl->wValue), le16_to_cpu(__entry->ctrl->wIndex), - le16_to_cpu(__entry->ctrl->wLength) + __entry->bRequestType, __entry->bRequest, + le16_to_cpu(__entry->wValue), le16_to_cpu(__entry->wIndex), + le16_to_cpu(__entry->wLength) ) ); @@ -94,15 +102,22 @@ DECLARE_EVENT_CLASS(dwc3_log_request, TP_PROTO(struct dwc3_request *req), TP_ARGS(req), TP_STRUCT__entry( + __dynamic_array(char, name, DWC3_MSG_MAX) __field(struct dwc3_request *, req) + __field(unsigned, actual) + __field(unsigned, length) + __field(int, status) ), TP_fast_assign( + snprintf(__get_str(name), DWC3_MSG_MAX, "%s", req->dep->name); __entry->req = req; + __entry->actual = req->request.actual; + __entry->length = req->request.length; + __entry->status = req->request.status; ), TP_printk("%s: req %p length %u/%u ==> %d", - __entry->req->dep->name, __entry->req, - __entry->req->request.actual, __entry->req->request.length, - __entry->req->request.status + __get_str(name), __entry->req, __entry->actual, __entry->length, + __entry->status ) ); @@ -158,17 +173,17 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd, struct dwc3_gadget_ep_cmd_params *params), TP_ARGS(dep, cmd, params), TP_STRUCT__entry( - __field(struct dwc3_ep *, dep) + __dynamic_array(char, name, DWC3_MSG_MAX) __field(unsigned int, cmd) __field(struct dwc3_gadget_ep_cmd_params *, params) ), TP_fast_assign( - __entry->dep = dep; + snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name); __entry->cmd = cmd; __entry->params = params; ), TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x\n", - __entry->dep->name, dwc3_gadget_ep_cmd_string(__entry->cmd), + __get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd), __entry->cmd, __entry->params->param0, __entry->params->param1, __entry->params->param2 ) @@ -184,16 +199,24 @@ DECLARE_EVENT_CLASS(dwc3_log_trb, TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb), TP_ARGS(dep, trb), TP_STRUCT__entry( - __field(struct dwc3_ep *, dep) + __dynamic_array(char, name, DWC3_MSG_MAX) __field(struct dwc3_trb *, trb) + __field(u32, bpl) + __field(u32, bph) + __field(u32, size) + __field(u32, ctrl) ), TP_fast_assign( - __entry->dep = dep; + snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name); __entry->trb = trb; + __entry->bpl = trb->bpl; + __entry->bph = trb->bph; + __entry->size = trb->size; + __entry->ctrl = trb->ctrl; ), TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x\n", - __entry->dep->name, __entry->trb, __entry->trb->bph, - __entry->trb->bpl, __entry->trb->size, __entry->trb->ctrl + __get_str(name), __entry->trb, __entry->bph, __entry->bpl, + __entry->size, __entry->ctrl ) ); -- cgit v1.1 From 33fb691b3ea4aca6cab4867e09ea2726c784660a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 24 Sep 2014 10:46:46 -0500 Subject: usb: dwc3: ep0: hold our lock in dwc3_gadget_ep0_set_halt dwc3_gadget_ep0_set_halt() will be called without locks held in some cases, so we must hold the lock on our own. While at that, also add a version without locks to be called in certain conditions. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 16 +++++++++++++++- drivers/usb/dwc3/gadget.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index b359387..36f6158 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -271,7 +271,7 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) dwc3_ep0_out_start(dwc); } -int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) +int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) { struct dwc3_ep *dep = to_dwc3_ep(ep); struct dwc3 *dwc = dep->dwc; @@ -281,6 +281,20 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) return 0; } +int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + unsigned long flags; + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep0_set_halt(ep, value); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + void dwc3_ep0_out_start(struct dwc3 *dwc) { int ret; diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 178ad89..f889008 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -82,6 +82,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); void dwc3_ep0_out_start(struct dwc3 *dwc); +int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags); -- cgit v1.1 From 5ad02fb813a9d8f39af1ccb1f2166cc80fd76f09 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 24 Sep 2014 10:48:26 -0500 Subject: usb: dwc3: gadget: move isoc endpoint check to unlocked set_halt __dwc3_gadget_ep_set_halt() is the function which handles the actual halt feature. In order to cope with some extra cleanup comming as a follow-up patch let's move the isochronous endpoint check there too. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3818b26..fd92f63 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1208,6 +1208,11 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) struct dwc3 *dwc = dep->dwc; int ret; + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name); + return -EINVAL; + } + memset(¶ms, 0x00, sizeof(params)); if (value) { @@ -1241,15 +1246,7 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) int ret; spin_lock_irqsave(&dwc->lock, flags); - - if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { - dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name); - ret = -EINVAL; - goto out; - } - ret = __dwc3_gadget_ep_set_halt(dep, value); -out: spin_unlock_irqrestore(&dwc->lock, flags); return ret; -- cgit v1.1 From 95aa4e8d658778f787e2ce205d3018443d027477 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 24 Sep 2014 10:50:14 -0500 Subject: usb: dwc3: gadget: hold the lock through set_wedge()'s life Instead of releasing the lock and calling locked versions of our set_halt() methods, let's hold the lock all the way through and call unlocked versions of those functions. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index fd92f63..b98295e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1257,15 +1257,18 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) struct dwc3_ep *dep = to_dwc3_ep(ep); struct dwc3 *dwc = dep->dwc; unsigned long flags; + int ret; spin_lock_irqsave(&dwc->lock, flags); dep->flags |= DWC3_EP_WEDGE; - spin_unlock_irqrestore(&dwc->lock, flags); if (dep->number == 0 || dep->number == 1) - return dwc3_gadget_ep0_set_halt(ep, 1); + ret = __dwc3_gadget_ep0_set_halt(ep, 1); else - return dwc3_gadget_ep_set_halt(ep, 1); + ret = __dwc3_gadget_ep_set_halt(dep, 1); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; } /* -------------------------------------------------------------------------- */ -- cgit v1.1 From 7a60855972f0d3c014093046cb6f013a1ee5bb19 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 24 Sep 2014 14:19:52 -0500 Subject: usb: dwc3: gadget: fix set_halt() bug with pending transfers According to our Gadget Framework API documentation, ->set_halt() *must* return -EAGAIN if we have pending transfers (on either direction) or FIFO isn't empty (on TX endpoints). Fix this bug so that the mass storage gadget can be used without stall=0 parameter. This patch should be backported to all kernels since v3.2. Cc: # v3.2+ Suggested-by: Alan Stern Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 4 ++-- drivers/usb/dwc3/gadget.c | 16 ++++++++++++---- drivers/usb/dwc3/gadget.h | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 36f6158..ae6b575 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -256,7 +256,7 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) /* stall is always issued on EP0 */ dep = dwc->eps[0]; - __dwc3_gadget_ep_set_halt(dep, 1); + __dwc3_gadget_ep_set_halt(dep, 1, false); dep->flags = DWC3_EP_ENABLED; dwc->delayed_status = false; @@ -480,7 +480,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, return -EINVAL; if (set == 0 && (dep->flags & DWC3_EP_WEDGE)) break; - ret = __dwc3_gadget_ep_set_halt(dep, set); + ret = __dwc3_gadget_ep_set_halt(dep, set, true); if (ret) return -EINVAL; break; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b98295e..f6d1dba 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -581,7 +581,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) /* make sure HW endpoint isn't stalled */ if (dep->flags & DWC3_EP_STALL) - __dwc3_gadget_ep_set_halt(dep, 0); + __dwc3_gadget_ep_set_halt(dep, 0, false); reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); reg &= ~DWC3_DALEPENA_EP(dep->number); @@ -1202,7 +1202,7 @@ out0: return ret; } -int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) { struct dwc3_gadget_ep_cmd_params params; struct dwc3 *dwc = dep->dwc; @@ -1216,6 +1216,14 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) memset(¶ms, 0x00, sizeof(params)); if (value) { + if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) || + (!list_empty(&dep->req_queued) || + !list_empty(&dep->request_list)))) { + dev_dbg(dwc->dev, "%s: pending request, cannot halt\n", + dep->name); + return -EAGAIN; + } + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_SETSTALL, ¶ms); if (ret) @@ -1246,7 +1254,7 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) int ret; spin_lock_irqsave(&dwc->lock, flags); - ret = __dwc3_gadget_ep_set_halt(dep, value); + ret = __dwc3_gadget_ep_set_halt(dep, value, false); spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -1265,7 +1273,7 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) if (dep->number == 0 || dep->number == 1) ret = __dwc3_gadget_ep0_set_halt(ep, 1); else - ret = __dwc3_gadget_ep_set_halt(dep, 1); + ret = __dwc3_gadget_ep_set_halt(dep, 1, false); spin_unlock_irqrestore(&dwc->lock, flags); return ret; diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index f889008..18ae3ea 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -86,7 +86,7 @@ int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags); -int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value); +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); /** * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW -- cgit v1.1 From d7577b389233a74609841492feaf6a55967aa5c8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 09:19:59 -0500 Subject: usb: gadget: function: uvc: conditionally dequeue We shouldn't try to dequeue a NULL pointer. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_video.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index c3e1f27..9cb86bc 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -352,7 +352,8 @@ int uvcg_video_enable(struct uvc_video *video, int enable) if (!enable) { for (i = 0; i < UVC_NUM_REQUESTS; ++i) - usb_ep_dequeue(video->ep, video->req[i]); + if (video->req[i]) + usb_ep_dequeue(video->ep, video->req[i]); uvc_video_free_requests(video); uvcg_queue_enable(&video->queue, 0); -- cgit v1.1 From c92bae753722a0010f1cabfb242581e130378b9f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 09:20:35 -0500 Subject: usb: gadget: function: uvc: make sure to balance ep enable/disable If a set_alt() to the same alternate setting that's already selected is received, functions are required to reset the interface state, this means we must disable all endpoints and reenable them again. This is also documented on our kdoc for struct usb_function * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may * initialize usb_ep.driver data at this time (when it is used). * Note that setting an interface to its current altsetting resets * interface state, and that all interfaces have a disabled state. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e126439..e00e8b7 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -286,11 +286,12 @@ static int uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) { struct uvc_device *uvc = to_uvc(f); + struct usb_composite_dev *cdev = f->config->cdev; struct v4l2_event v4l2_event; struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; int ret; - INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt); + INFO(cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt); if (interface == uvc->control_intf) { if (alt) @@ -299,7 +300,7 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->state == UVC_STATE_DISCONNECTED) { memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_CONNECT; - uvc_event->speed = f->config->cdev->gadget->speed; + uvc_event->speed = cdev->gadget->speed; v4l2_event_queue(uvc->vdev, &v4l2_event); uvc->state = UVC_STATE_CONNECTED; @@ -321,8 +322,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->state != UVC_STATE_STREAMING) return 0; - if (uvc->video.ep) + if (uvc->video.ep) { usb_ep_disable(uvc->video.ep); + uvc->video.ep->driver_data = NULL; + } memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMOFF; @@ -335,14 +338,22 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->state != UVC_STATE_CONNECTED) return 0; - if (uvc->video.ep) { - ret = config_ep_by_speed(f->config->cdev->gadget, - &(uvc->func), uvc->video.ep); - if (ret) - return ret; - usb_ep_enable(uvc->video.ep); + if (!uvc->video.ep) + return -EINVAL; + + if (uvc->video.ep->driver_data) { + INFO(cdev, "reset UVC\n"); + usb_ep_disable(uvc->video.ep); + uvc->video.ep->driver_data = NULL; } + ret = config_ep_by_speed(f->config->cdev->gadget, + &(uvc->func), uvc->video.ep); + if (ret) + return ret; + usb_ep_enable(uvc->video.ep); + uvc->video.ep->driver_data = uvc; + memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMON; v4l2_event_queue(uvc->vdev, &v4l2_event); -- cgit v1.1 From e975be287b87e0862b0f57e7326a79667e32a90a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 11:13:26 -0500 Subject: usb: gadget: function: uvc: return correct alt-setting If our alternate setting has been selected, we must return that on a subsequent Get Interface request even if we're not streaming. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e00e8b7..4138ad5 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -279,7 +279,7 @@ uvc_function_get_alt(struct usb_function *f, unsigned interface) else if (interface != uvc->streaming_intf) return -EINVAL; else - return uvc->state == UVC_STATE_STREAMING ? 1 : 0; + return uvc->video.ep->driver_data ? 1 : 0; } static int -- cgit v1.1 From 52ec49a5e56a27c5b6f8217708783eff39f24c16 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 13:35:54 -0500 Subject: usb: gadget: function: acm: make f_acm pass USB20CV Chapter9 During Halt Endpoint Test, our interrupt endpoint will be disabled, which will clear out ep->desc to NULL. Unless we call config_ep_by_speed() again, we will not be able to enable this endpoint which will make us fail that test. Fixes: f9c56cd (usb: gadget: Clear usb_endpoint_descriptor inside the struct usb_ep on disable) Cc: # v3.4+ Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_acm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 6da4685..aad8165 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -433,12 +433,12 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) dev_vdbg(&cdev->gadget->dev, "reset acm control interface %d\n", intf); usb_ep_disable(acm->notify); - } else { - dev_vdbg(&cdev->gadget->dev, - "init acm ctrl interface %d\n", intf); + } + + if (!acm->notify->desc) if (config_ep_by_speed(cdev->gadget, f, acm->notify)) return -EINVAL; - } + usb_ep_enable(acm->notify); acm->notify->driver_data = acm; -- cgit v1.1 From 62e370785cb337981999ac7ec364e22ffb6c2cd3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 13:41:04 -0500 Subject: usb: gadget: function: uvc: manage our video control endpoint just like any other endpoint, we must enable/disable our video control endpoint based on calls to our ->set_alt() method. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 4138ad5..413a09f 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -297,6 +297,19 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (alt) return -EINVAL; + if (uvc->control_ep->driver_data) { + INFO(cdev, "reset UVC Control\n"); + usb_ep_disable(uvc->control_ep); + uvc->control_ep->driver_data = NULL; + } + + if (!uvc->control_ep->desc) + if (config_ep_by_speed(cdev->gadget, f, uvc->control_ep)) + return -EINVAL; + + usb_ep_enable(uvc->control_ep); + uvc->control_ep->driver_data = uvc; + if (uvc->state == UVC_STATE_DISCONNECTED) { memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_CONNECT; -- cgit v1.1 From e3122f5fedb6d88a043b60822f601f7ab517a465 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 13:43:20 -0500 Subject: usb: gadget: function: uvc: disable endpoints on ->disable() when our ->disable() method is called, we must make sure to teardown all our resources, including endpoints. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 413a09f..945b3bd 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -390,6 +390,16 @@ uvc_function_disable(struct usb_function *f) v4l2_event_queue(uvc->vdev, &v4l2_event); uvc->state = UVC_STATE_DISCONNECTED; + + if (uvc->video.ep->driver_data) { + usb_ep_disable(uvc->video.ep); + uvc->video.ep->driver_data = NULL; + } + + if (uvc->control_ep->driver_data) { + usb_ep_disable(uvc->control_ep); + uvc->control_ep->driver_data = NULL; + } } /* -------------------------------------------------------------------------- -- cgit v1.1 From 703a303c187ef7e3c8daf8a1be343576c9579eaf Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 14:18:14 -0500 Subject: usb: gadget: function: uac2: add wMaxPacketSize to ep desc Endpoint descriptors should pass wMaxPacketSize. Note that this also fixes USB20CV Other Speed Endpoint Descriptor Tests. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index a5a27a5..fa51118 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -772,6 +772,7 @@ struct usb_endpoint_descriptor fs_epout_desc = { .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .wMaxPacketSize = cpu_to_le16(1023), .bInterval = 1, }; @@ -780,6 +781,7 @@ struct usb_endpoint_descriptor hs_epout_desc = { .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .wMaxPacketSize = cpu_to_le16(1024), .bInterval = 4, }; @@ -847,6 +849,7 @@ struct usb_endpoint_descriptor fs_epin_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .wMaxPacketSize = cpu_to_le16(1023), .bInterval = 1, }; @@ -855,6 +858,7 @@ struct usb_endpoint_descriptor hs_epin_desc = { .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .wMaxPacketSize = cpu_to_le16(1024), .bInterval = 4, }; -- cgit v1.1 From f3bb7b298120df8a9b7354e4f6d07e3185c15db7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 14:23:41 -0500 Subject: usb: gadget: function: uac2: prevent double ep disable without this check, f_uac2 would try to disable the same endpoint twice. Fix that. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index fa51118..1146f4d 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -951,6 +951,9 @@ free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep) struct snd_uac2_chip *uac2 = prm->uac2; int i; + if (!prm->ep_enabled) + return; + prm->ep_enabled = false; for (i = 0; i < USB_XFERS; i++) { -- cgit v1.1 From de1e6e799fc4e6f0452737e454267c0bfdf88c62 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 14:24:09 -0500 Subject: usb: gadget: function: uac2: add a release method devices are required to provide a release method. This patch fixes the following WARN(): [ 42.611159] ------------[ cut here ]------------ [ 42.616025] WARNING: CPU: 0 PID: 1453 at drivers/base/core.c:250 device_release+0x94/0xa0() [ 42.624820] Device 'snd_uac2.0' does not have a release() function, it is broken and must be fixed. [ 42.634328] Modules linked in: usb_f_uac2 g_audio(-) libcomposite configfs xhci_hcd snd_soc_davinci_mcasp snd_soc_edma snd_soc_tlv320aic3x snd_soc_omap snd_soc_evm snd_soc_core dwc3 snd_compress omapdrm snd_pcm_dmaengine snd_pcm snd_timer snd fb_sys_fops lis3lv02d_i2c matrix_keypad dwc3_omap lis3lv02d panel_dpi input_polldev soundcore [ 42.665687] CPU: 0 PID: 1453 Comm: modprobe Tainted: G D 3.17.0-rc6-00448-g9f3d0ec-dirty #188 [ 42.675756] [] (unwind_backtrace) from [] (show_stack+0x20/0x24) [ 42.683911] [] (show_stack) from [] (dump_stack+0x8c/0xa4) [ 42.691526] [] (dump_stack) from [] (warn_slowpath_common+0x7c/0xa0) [ 42.700004] [] (warn_slowpath_common) from [] (warn_slowpath_fmt+0x40/0x48) [ 42.709194] [] (warn_slowpath_fmt) from [] (device_release+0x94/0xa0) [ 42.717794] [] (device_release) from [] (kobject_cleanup+0x4c/0x7c) [ 42.726189] [] (kobject_cleanup) from [] (kobject_put+0x60/0x90) [ 42.734316] [] (kobject_put) from [] (put_device+0x24/0x28) [ 42.741995] [] (put_device) from [] (platform_device_unregister+0x2c/0x30) [ 42.751061] [] (platform_device_unregister) from [] (afunc_unbind+0x2c/0x68 [usb_f_uac2]) [ 42.761523] [] (afunc_unbind [usb_f_uac2]) from [] (remove_config.isra.8+0xe8/0x100 [libcomposite]) [ 42.772868] [] (remove_config.isra.8 [libcomposite]) from [] (__composite_unbind+0x48/0xb0 [libcomposite]) [ 42.784855] [] (__composite_unbind [libcomposite]) from [] (composite_unbind+0x1c/0x20 [libcomposite]) [ 42.796446] [] (composite_unbind [libcomposite]) from [] (usb_gadget_remove_driver+0x78/0xb0) [ 42.807224] [] (usb_gadget_remove_driver) from [] (usb_gadget_unregister_driver+0x74/0xb8) [ 42.817742] [] (usb_gadget_unregister_driver) from [] (usb_composite_unregister+0x1c/0x20 [libcomposite]) [ 42.829632] [] (usb_composite_unregister [libcomposite]) from [] (audio_driver_exit+0x14/0x1c [g_audio]) [ 42.841430] [] (audio_driver_exit [g_audio]) from [] (SyS_delete_module+0x120/0x1b0) [ 42.851415] [] (SyS_delete_module) from [] (ret_fast_syscall+0x0/0x48) [ 42.860075] ---[ end trace bb22e678d8d6db7b ]--- root@saruman:~# Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 1146f4d..9296e59 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -512,6 +512,11 @@ static int snd_uac2_remove(struct platform_device *pdev) return 0; } +static void snd_uac2_release(struct device *dev) +{ + dev_dbg(dev, "releasing '%s'\n", dev_name(dev)); +} + static int alsa_uac2_init(struct audio_dev *agdev) { struct snd_uac2_chip *uac2 = &agdev->uac2; @@ -523,6 +528,7 @@ static int alsa_uac2_init(struct audio_dev *agdev) uac2->pdev.id = 0; uac2->pdev.name = uac2_name; + uac2->pdev.dev.release = snd_uac2_release; /* Register snd_uac2 driver */ err = platform_driver_register(&uac2->pdrv); -- cgit v1.1 From 3985f3ab0834edf014ebd19192d9dd77422dea67 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 15:18:20 -0500 Subject: usb: gadget: function: f_obex: fix Interface Descriptor Test On USB20CV's Interface Descriptor Test, a series of SetInterface/GetInterface requests are issued and gadget driver is required to always return correct alternate setting. In one step of the test, g_serial with f_obex was returning the wrong value (1 instead of 0). In order to fix this, we will now hold currently selected alternate setting inside our struct f_obex and just return that from our ->get_alt() implementation. Note that his also simplifies the code a bit. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_obex.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index 5f40080..1a1a490 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -35,6 +35,7 @@ struct f_obex { struct gserial port; u8 ctrl_id; u8 data_id; + u8 cur_alt; u8 port_num; u8 can_activate; }; @@ -235,6 +236,8 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } else goto fail; + obex->cur_alt = alt; + return 0; fail: @@ -245,10 +248,7 @@ static int obex_get_alt(struct usb_function *f, unsigned intf) { struct f_obex *obex = func_to_obex(f); - if (intf == obex->ctrl_id) - return 0; - - return obex->port.in->driver_data ? 1 : 0; + return obex->cur_alt; } static void obex_disable(struct usb_function *f) -- cgit v1.1 From 7d643664ea559b36188cae264047ce3c9bfec3a2 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 24 Sep 2014 10:40:25 +0300 Subject: usb: dwc3: pci: Add PCI ID for Intel Braswell The device controller is the same but it has different PCI ID. Add this new ID to the driver's list of supported IDs. Signed-off-by: Alan Cox Signed-off-by: Mika Westerberg Signed-off-by: Heikki Krogerus Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 436fb08..a36cf66 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -30,6 +30,7 @@ #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd #define PCI_DEVICE_ID_INTEL_BYT 0x0f37 #define PCI_DEVICE_ID_INTEL_MRFLD 0x119e +#define PCI_DEVICE_ID_INTEL_BSW 0x22B7 struct dwc3_pci { struct device *dev; @@ -181,6 +182,7 @@ static const struct pci_device_id dwc3_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3), }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), }, { } /* Terminating Entry */ -- cgit v1.1 From 6856d30c6c0038dc0648009853533af3af6c5ba8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 30 Sep 2014 11:43:20 -0500 Subject: usb: dwc3: ep0: return early on NULL requests if our list of requests is empty, return early. There's really nothing to be done in case our request list is empty anyway because the only situation where we our list is empty, is when we're transferring ZLPs. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index ae6b575..a47cc1e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -789,9 +789,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; - r = next_request(&ep0->request_list); - ur = &r->request; - trb = dwc->ep0_trb; status = DWC3_TRB_SIZE_TRBSTS(trb->size); @@ -804,6 +801,12 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, return; } + r = next_request(&ep0->request_list); + if (!r) + return; + + ur = &r->request; + length = trb->size & DWC3_TRB_SIZE_MASK; if (dwc->ep0_bounced) { -- cgit v1.1 From f1b697525d5428856eaba2be2ee6dc1cf3efbbbe Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 10 Oct 2014 10:39:32 +0800 Subject: ACPICA: acpidump: Add ACPI 1.0 RSDP support. The acpidump currently always uses ACPI 2.0 format to dump RSDP, this patch adds ACPI 1.0 RSDP support. Link: https://bugs.acpica.org/show_bug.cgi?id=1097 Link: https://bugs.acpica.org/show_bug.cgi?id=1103 Signed-off-by: Lv Zheng Reported-and-tested-by: Rudolf Marek Reported-and-tested-by: Rafal Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/actables.h | 2 ++ drivers/acpi/acpica/tbxfroot.c | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index f148827..1afe46e 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -49,6 +49,8 @@ acpi_status acpi_allocate_root_table(u32 initial_table_count); /* * tbxfroot - Root pointer utilities */ +u32 acpi_tb_get_rsdp_length(struct acpi_table_rsdp *rsdp); + acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp); u8 *acpi_tb_scan_memory_for_rsdp(u8 *start_address, u32 length); diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c index 65ab8fe..43a54af 100644 --- a/drivers/acpi/acpica/tbxfroot.c +++ b/drivers/acpi/acpica/tbxfroot.c @@ -50,6 +50,36 @@ ACPI_MODULE_NAME("tbxfroot") /******************************************************************************* * + * FUNCTION: acpi_tb_get_rsdp_length + * + * PARAMETERS: rsdp - Pointer to RSDP + * + * RETURN: Table length + * + * DESCRIPTION: Get the length of the RSDP + * + ******************************************************************************/ +u32 acpi_tb_get_rsdp_length(struct acpi_table_rsdp *rsdp) +{ + + if (!ACPI_VALIDATE_RSDP_SIG(rsdp->signature)) { + + /* BAD Signature */ + + return (0); + } + + /* "Length" field is available if table version >= 2 */ + + if (rsdp->revision >= 2) { + return (rsdp->length); + } else { + return (ACPI_RSDP_CHECKSUM_LENGTH); + } +} + +/******************************************************************************* + * * FUNCTION: acpi_tb_validate_rsdp * * PARAMETERS: rsdp - Pointer to unvalidated RSDP @@ -59,7 +89,8 @@ ACPI_MODULE_NAME("tbxfroot") * DESCRIPTION: Validate the RSDP (ptr) * ******************************************************************************/ -acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp) + +acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp * rsdp) { /* -- cgit v1.1 From f19f1a7e12b40c601a475c4fcb09dc0126d4bc51 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 10 Oct 2014 10:39:39 +0800 Subject: ACPICA: Events: Reduce indent divergences of events files. This patch reduces indent divergences first in order to reduce human intervention work for the follow-up linuxized event patches. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/achware.h | 2 +- drivers/acpi/acpica/aclocal.h | 4 ++-- drivers/acpi/acpica/evgpe.c | 23 ++++++++++++----------- drivers/acpi/acpica/evgpeinit.c | 1 + drivers/acpi/acpica/evxface.c | 9 ++++----- drivers/acpi/acpica/evxfgpe.c | 9 ++++----- drivers/acpi/acpica/hwgpe.c | 2 +- 7 files changed, 25 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index 2ad2351..c318d3e 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -127,7 +127,7 @@ acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, acpi_status acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info, - acpi_event_status * event_status); + acpi_event_status *event_status); acpi_status acpi_hw_disable_all_gpes(void); diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 2747279..c00e7e4 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -413,8 +413,8 @@ struct acpi_gpe_handler_info { acpi_gpe_handler address; /* Address of handler, if any */ void *context; /* Context to be passed to handler */ struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */ - u8 original_flags; /* Original (pre-handler) GPE info */ - u8 originally_enabled; /* True if GPE was originally enabled */ + u8 original_flags; /* Original (pre-handler) GPE info */ + u8 originally_enabled; /* True if GPE was originally enabled */ }; /* Notify info for implicit notify, multiple device objects */ diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index e4ba4de..2095dfb 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -100,13 +100,14 @@ acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info) * * FUNCTION: acpi_ev_enable_gpe * - * PARAMETERS: gpe_event_info - GPE to enable + * PARAMETERS: gpe_event_info - GPE to enable * * RETURN: Status * * DESCRIPTION: Clear a GPE of stale events and enable it. * ******************************************************************************/ + acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) { acpi_status status; @@ -125,6 +126,7 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) } /* Clear the GPE (of stale events) */ + status = acpi_hw_clear_gpe(gpe_event_info); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); @@ -136,7 +138,6 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) return_ACPI_STATUS(status); } - /******************************************************************************* * * FUNCTION: acpi_ev_add_gpe_reference @@ -212,7 +213,7 @@ acpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_info) if (ACPI_SUCCESS(status)) { status = acpi_hw_low_set_gpe(gpe_event_info, - ACPI_GPE_DISABLE); + ACPI_GPE_DISABLE); } if (ACPI_FAILURE(status)) { @@ -334,7 +335,7 @@ struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, * ******************************************************************************/ -u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) +u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list) { acpi_status status; struct acpi_gpe_block_info *gpe_block; @@ -427,7 +428,7 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) /* Check if there is anything active at all in this register */ - enabled_status_byte = (u8) (status_reg & enable_reg); + enabled_status_byte = (u8)(status_reg & enable_reg); if (!enabled_status_byte) { /* No active GPEs in this register, move on */ @@ -450,7 +451,7 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) acpi_ev_gpe_dispatch(gpe_block-> node, &gpe_block-> - event_info[((acpi_size) i * ACPI_GPE_REGISTER_WIDTH) + j], j + gpe_register_info->base_gpe_number); + event_info[((acpi_size) i * ACPI_GPE_REGISTER_WIDTH) + j], j + gpe_register_info->base_gpe_number); } } } @@ -636,7 +637,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context) * ******************************************************************************/ -acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info) +acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info * gpe_event_info) { acpi_status status; @@ -666,9 +667,9 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info) * * FUNCTION: acpi_ev_gpe_dispatch * - * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 - * gpe_event_info - Info for this GPE - * gpe_number - Number relative to the parent GPE block + * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 + * gpe_event_info - Info for this GPE + * gpe_number - Number relative to the parent GPE block * * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED * @@ -681,7 +682,7 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info) u32 acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, - struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) + struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) { acpi_status status; u32 return_value; diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index 49fc7ef..7be9283 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -424,6 +424,7 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, } /* Disable the GPE in case it's been enabled already. */ + (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); /* diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 11e5803..935fd76 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -786,14 +786,13 @@ acpi_install_gpe_handler(acpi_handle gpe_device, handler->method_node = gpe_event_info->dispatch.method_node; handler->original_flags = (u8)(gpe_event_info->flags & (ACPI_GPE_XRUPT_TYPE_MASK | - ACPI_GPE_DISPATCH_MASK)); + ACPI_GPE_DISPATCH_MASK)); /* * If the GPE is associated with a method, it may have been enabled * automatically during initialization, in which case it has to be * disabled now to avoid spurious execution of the handler. */ - if ((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) && gpe_event_info->runtime_count) { handler->originally_enabled = 1; @@ -808,7 +807,7 @@ acpi_install_gpe_handler(acpi_handle gpe_device, gpe_event_info->flags &= ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); - gpe_event_info->flags |= (u8) (type | ACPI_GPE_DISPATCH_HANDLER); + gpe_event_info->flags |= (u8)(type | ACPI_GPE_DISPATCH_HANDLER); acpi_os_release_lock(acpi_gbl_gpe_lock, flags); @@ -893,7 +892,7 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, gpe_event_info->dispatch.method_node = handler->method_node; gpe_event_info->flags &= - ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); + ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); gpe_event_info->flags |= handler->original_flags; /* @@ -946,7 +945,7 @@ ACPI_EXPORT_SYMBOL(acpi_remove_gpe_handler) * handle is returned. * ******************************************************************************/ -acpi_status acpi_acquire_global_lock(u16 timeout, u32 * handle) +acpi_status acpi_acquire_global_lock(u16 timeout, u32 *handle) { acpi_status status; diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 56710a0..2529d3c 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -106,8 +106,8 @@ ACPI_EXPORT_SYMBOL(acpi_update_all_gpes) * * FUNCTION: acpi_enable_gpe * - * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 - * gpe_number - GPE level within the GPE block + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block * * RETURN: Status * @@ -115,7 +115,6 @@ ACPI_EXPORT_SYMBOL(acpi_update_all_gpes) * hardware-enabled. * ******************************************************************************/ - acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) { acpi_status status = AE_BAD_PARAMETER; @@ -490,8 +489,8 @@ ACPI_EXPORT_SYMBOL(acpi_clear_gpe) * * FUNCTION: acpi_get_gpe_status * - * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 - * gpe_number - GPE level within the GPE block + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block * event_status - Where the current status of the event * will be returned * diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index ea62d40..aa884d4 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -202,7 +202,7 @@ acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info * gpe_event_info) acpi_status acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, - acpi_event_status * event_status) + acpi_event_status *event_status) { u32 in_byte; u32 register_bit; -- cgit v1.1 From 437b75123ca5ee36897bfcd1272e96109bed3ea1 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 10 Oct 2014 10:39:45 +0800 Subject: ACPICA: Events: Reduce source code difference in acpi_install_gpe_handler(). There is a sanity check in ACPICA upstream, complaining mis-matched interrupt type for originally enabled GPEs that are going to be dispatched by OSPM handlers. This is only a warning message noting developers such conflict between BIOS and OSPM. This patch ports this warning message from ACPICA upstream to reduce source code difference between Linux and ACPICA upstream. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/evxface.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 935fd76..79b6ed2 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -795,8 +795,16 @@ acpi_install_gpe_handler(acpi_handle gpe_device, */ if ((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) && gpe_event_info->runtime_count) { - handler->originally_enabled = 1; + handler->originally_enabled = TRUE; (void)acpi_ev_remove_gpe_reference(gpe_event_info); + + /* Sanity check of original type against new type */ + + if (type != + (u32)(gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK)) { + ACPI_WARNING((AE_INFO, + "GPE type mismatch (level/edge)")); + } } /* Install the handler */ -- cgit v1.1 From 1809919a309dc8c8faad3c048bfda9a9f3fa0443 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 10 Oct 2014 10:39:51 +0800 Subject: ACPICA: Events: Update GPE handler removal, match behavior of handler install. The originally_enabled check is not paired between acpi_install_gpe_handler() and acpi_remove_gpe_handler(). In ACPICA upstream, there is code to protect original enabled state for ACPI_GPE_DISPATCH_NOTIFY and this commit fixes an issue for this feature. Link: https://github.com/acpica/acpica/commit/967f314c Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/evxface.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 79b6ed2..55a58f3 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -793,8 +793,9 @@ acpi_install_gpe_handler(acpi_handle gpe_device, * automatically during initialization, in which case it has to be * disabled now to avoid spurious execution of the handler. */ - if ((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) - && gpe_event_info->runtime_count) { + if (((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) || + (handler->original_flags & ACPI_GPE_DISPATCH_NOTIFY)) && + gpe_event_info->runtime_count) { handler->originally_enabled = TRUE; (void)acpi_ev_remove_gpe_reference(gpe_event_info); @@ -908,7 +909,8 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, * enabled, it should be enabled at this point to restore the * post-initialization configuration. */ - if ((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) && + if (((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) || + (handler->original_flags & ACPI_GPE_DISPATCH_NOTIFY)) && handler->originally_enabled) { (void)acpi_ev_add_gpe_reference(gpe_event_info); } -- cgit v1.1 From a08f813e58169a8edd01e13d73f60d8561f3ecea Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 10 Oct 2014 10:39:57 +0800 Subject: ACPICA: Events: Reduce source code difference for the ACPI_EVENT_FLAG_HANDLE support. This patch is a partial linuxized result of the following ACPICA commit: ACPICA commit: a73b66c6aa1846d055bb6390d9c9b9902f7d804d Subject: Add "has handler" flag to event/gpe status interfaces. This change adds a new flag, ACPI_EVENT_FLAGS_HAS_HANDLER to the acpi_get_event_status and acpi_get_gpe_status external interfaces. It is set if the event/gpe currently has a handler associated with it. This commit back ports ACPI_EVENT_FLAG_HANDLE from Linux upstream to ACPICA, the flag along with its support code currently can only be found in the Linux upstream and is used by the ACPI sysfs GPE interfaces and the ACPI bus scanning support. Link: https://github.com/acpica/acpica/commit/a73b66c6 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/evxfevnt.c | 40 ++++++++++++++++++++++++++-------------- drivers/acpi/acpica/evxfgpe.c | 3 --- drivers/acpi/acpica/hwgpe.c | 7 +++++++ 3 files changed, 33 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index e286640..a8a077c 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -324,8 +324,9 @@ ACPI_EXPORT_SYMBOL(acpi_clear_event) ******************************************************************************/ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status) { - acpi_status status = AE_OK; - u32 value; + acpi_status status; + acpi_event_status local_event_status = 0; + u32 in_byte; ACPI_FUNCTION_TRACE(acpi_get_event_status); @@ -339,29 +340,40 @@ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status) return_ACPI_STATUS(AE_BAD_PARAMETER); } - /* Get the status of the requested fixed event */ + /* Fixed event currently can be dispatched? */ + + if (acpi_gbl_fixed_event_handlers[event].handler) { + local_event_status |= ACPI_EVENT_FLAG_HANDLE; + } + + /* Fixed event currently enabled? */ status = acpi_read_bit_register(acpi_gbl_fixed_event_info[event]. - enable_register_id, &value); - if (ACPI_FAILURE(status)) + enable_register_id, &in_byte); + if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); + } - *event_status = value; + if (in_byte) { + local_event_status |= ACPI_EVENT_FLAG_ENABLED; + } + + /* Fixed event currently active? */ status = acpi_read_bit_register(acpi_gbl_fixed_event_info[event]. - status_register_id, &value); - if (ACPI_FAILURE(status)) + status_register_id, &in_byte); + if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); + } - if (value) - *event_status |= ACPI_EVENT_FLAG_SET; - - if (acpi_gbl_fixed_event_handlers[event].handler) - *event_status |= ACPI_EVENT_FLAG_HANDLE; + if (in_byte) { + local_event_status |= ACPI_EVENT_FLAG_SET; + } - return_ACPI_STATUS(status); + (*event_status) = local_event_status; + return_ACPI_STATUS(AE_OK); } ACPI_EXPORT_SYMBOL(acpi_get_event_status) diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 2529d3c..e889a53 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -523,9 +523,6 @@ acpi_get_gpe_status(acpi_handle gpe_device, status = acpi_hw_get_gpe_status(gpe_event_info, event_status); - if (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) - *event_status |= ACPI_EVENT_FLAG_HANDLE; - unlock_and_exit: acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index aa884d4..0302bc3 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -216,6 +216,13 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, return (AE_BAD_PARAMETER); } + /* GPE currently handled? */ + + if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) != + ACPI_GPE_DISPATCH_NONE) { + local_event_status |= ACPI_EVENT_FLAG_HANDLE; + } + /* Get the info block for the entire GPE register */ gpe_register_info = gpe_event_info->register_info; -- cgit v1.1 From 2f8572344e65296d13c1a771cacfea60916d91dc Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 10 Oct 2014 10:40:05 +0800 Subject: ACPICA: Events: Reduce source code difference for the ACPI_EVENT_FLAG_HANDLE renaming. This patch is partial linuxized result of the following ACPICA commit: ACPICA commit: a73b66c6aa1846d055bb6390d9c9b9902f7d804d Subject: Add "has handler" flag to event/gpe status interfaces. This change adds a new flag, ACPI_EVENT_FLAGS_HAS_HANDLER to the acpi_get_event_status and acpi_get_gpe_status external interfaces. It is set if the event/gpe currently has a handler associated with it. This patch contains the code to rename ACPI_EVENT_FLAG_HANDLE to ACPI_EVENT_FLAG_HAS_HANDLER, and the corresponding updates of its usages. Link: https://github.com/acpica/acpica/commit/a73b66c6 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/evxfevnt.c | 2 +- drivers/acpi/acpica/hwgpe.c | 2 +- drivers/acpi/scan.c | 2 +- drivers/acpi/sysfs.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index a8a077c..bb8cbf5 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -343,7 +343,7 @@ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status) /* Fixed event currently can be dispatched? */ if (acpi_gbl_fixed_event_handlers[event].handler) { - local_event_status |= ACPI_EVENT_FLAG_HANDLE; + local_event_status |= ACPI_EVENT_FLAG_HAS_HANDLER; } /* Fixed event currently enabled? */ diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index 0302bc3..48ac7b7 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -220,7 +220,7 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) != ACPI_GPE_DISPATCH_NONE) { - local_event_status |= ACPI_EVENT_FLAG_HANDLE; + local_event_status |= ACPI_EVENT_FLAG_HAS_HANDLER; } /* Get the info block for the entire GPE register */ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ae44d86..f1d96e7 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1470,7 +1470,7 @@ static void acpi_wakeup_gpe_init(struct acpi_device *device) if (ACPI_FAILURE(status)) return; - wakeup->flags.run_wake = !!(event_status & ACPI_EVENT_FLAG_HANDLE); + wakeup->flags.run_wake = !!(event_status & ACPI_EVENT_FLAG_HAS_HANDLER); } static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 38cb978..13e577c 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -537,7 +537,7 @@ static ssize_t counter_show(struct kobject *kobj, if (result) goto end; - if (!(status & ACPI_EVENT_FLAG_HANDLE)) + if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER)) size += sprintf(buf + size, " invalid"); else if (status & ACPI_EVENT_FLAG_ENABLED) size += sprintf(buf + size, " enabled"); @@ -581,7 +581,7 @@ static ssize_t counter_set(struct kobject *kobj, if (result) goto end; - if (!(status & ACPI_EVENT_FLAG_HANDLE)) { + if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER)) { printk(KERN_WARNING PREFIX "Can not change Invalid GPE/Fixed Event status\n"); return -EINVAL; -- cgit v1.1 From f2d348fac77f6cf8eb5d0eb37e16b13d8b796bda Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 10 Oct 2014 10:40:11 +0800 Subject: ACPICA: iASL/Disassembler: Add support for hardware summary mapfiles. Adds support for both iASL and the disassembler to create a hardware and connection summary mapfile (via the -lm option.) Linux isn't affected by this patch because iASL is not in the Linux upstream. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/amlresrc.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/acpica/amlresrc.h b/drivers/acpi/acpica/amlresrc.h index f3f8344..3a0beeb 100644 --- a/drivers/acpi/acpica/amlresrc.h +++ b/drivers/acpi/acpica/amlresrc.h @@ -117,6 +117,12 @@ struct asl_resource_node { struct asl_resource_node *next; }; +struct asl_resource_info { + union acpi_parse_object *descriptor_type_op; /* Resource descriptor parse node */ + union acpi_parse_object *mapping_op; /* Used for mapfile support */ + u32 current_byte_offset; /* Offset in resource template */ +}; + /* Macros used to generate AML resource length fields */ #define ACPI_AML_SIZE_LARGE(r) (sizeof (r) - sizeof (struct aml_resource_large_header)) @@ -449,4 +455,32 @@ union aml_resource { u8 byte_item; }; +/* Interfaces used by both the disassembler and compiler */ + +void +mp_save_gpio_info(union acpi_parse_object *op, + union aml_resource *resource, + u32 pin_count, u16 *pin_list, char *device_name); + +void +mp_save_serial_info(union acpi_parse_object *op, + union aml_resource *resource, char *device_name); + +char *mp_get_hid_from_parse_tree(struct acpi_namespace_node *hid_node); + +char *mp_get_hid_via_namestring(char *device_name); + +char *mp_get_connection_info(union acpi_parse_object *op, + u32 pin_index, + struct acpi_namespace_node **target_node, + char **target_name); + +char *mp_get_parent_device_hid(union acpi_parse_object *op, + struct acpi_namespace_node **target_node, + char **parent_device_name); + +char *mp_get_ddn_value(char *device_name); + +char *mp_get_hid_value(struct acpi_namespace_node *device_node); + #endif -- cgit v1.1 From c95f25b03667c50e7184a63bdf4c32dff0de2f6f Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 14 Oct 2014 14:23:38 +0800 Subject: ACPI / EC: Add CPU ID to debugging messages. This patch adds CPU ID to the context entries' debugging output. no functional changes. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index cb6066c..9cb4d0c 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -181,7 +181,8 @@ static bool advance_transaction(struct acpi_ec *ec) u8 status; bool wakeup = false; - pr_debug("===== %s =====\n", in_interrupt() ? "IRQ" : "TASK"); + pr_debug("===== %s (%d) =====\n", + in_interrupt() ? "IRQ" : "TASK", smp_processor_id()); status = acpi_ec_read_status(ec); t = ec->curr; if (!t) -- cgit v1.1 From 459572a7503bccb5435936488088c8db4f51d3ab Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 14 Oct 2014 14:23:43 +0800 Subject: ACPI / EC: Enhance the logs to apply to QR_EC transactions. Currently some logs are applied to new transactions, but QR_EC transactions are not included. This patch merges the code path to make the logs also applying to the QR_EC transactions. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 9cb4d0c..b15a431 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -303,12 +303,15 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, spin_lock_irqsave(&ec->lock, tmp); /* following two actions should be kept atomic */ ec->curr = t; + pr_debug("transaction start (cmd=0x%02x, addr=0x%02x)\n", + t->command, t->wdata ? t->wdata[0] : 0); start_transaction(ec); spin_unlock_irqrestore(&ec->lock, tmp); ret = ec_poll(ec); spin_lock_irqsave(&ec->lock, tmp); if (ec->curr->command == ACPI_EC_COMMAND_QUERY) clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); + pr_debug("transaction end\n"); ec->curr = NULL; spin_unlock_irqrestore(&ec->lock, tmp); return ret; @@ -334,8 +337,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) goto unlock; } } - pr_debug("transaction start (cmd=0x%02x, addr=0x%02x)\n", - t->command, t->wdata ? t->wdata[0] : 0); /* disable GPE during transaction if storm is detected */ if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { /* It has to be disabled, so that it doesn't trigger. */ @@ -356,7 +357,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) t->irq_count); set_bit(EC_FLAGS_GPE_STORM, &ec->flags); } - pr_debug("transaction end\n"); if (ec->global_lock) acpi_release_global_lock(glk); unlock: -- cgit v1.1 From e34c0e2bb4046d574224de3752642177a45a067a Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 14 Oct 2014 14:23:49 +0800 Subject: ACPI / EC: Add detailed command/query debugging information. Developers really don't need to translate EC commands in mind. This patch adds detailed debugging information for the EC commands. The address can be found in the follow-up sequential EC_DATA(W) accesses, thus this patch also removes some of the redundant address information. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index b15a431..fcf667c 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -164,6 +164,27 @@ static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) outb(data, ec->data_addr); } +#ifdef DEBUG +static const char *acpi_ec_cmd_string(u8 cmd) +{ + switch (cmd) { + case 0x80: + return "RD_EC"; + case 0x81: + return "WR_EC"; + case 0x82: + return "BE_EC"; + case 0x83: + return "BD_EC"; + case 0x84: + return "QR_EC"; + } + return "UNKNOWN"; +} +#else +#define acpi_ec_cmd_string(cmd) "UNDEF" +#endif + static int ec_transaction_completed(struct acpi_ec *ec) { unsigned long flags; @@ -199,7 +220,8 @@ static bool advance_transaction(struct acpi_ec *ec) if (t->rlen == t->ri) { t->flags |= ACPI_EC_COMMAND_COMPLETE; if (t->command == ACPI_EC_COMMAND_QUERY) - pr_debug("hardware QR_EC completion\n"); + pr_debug("***** Command(%s) hardware completion *****\n", + acpi_ec_cmd_string(t->command)); wakeup = true; } } else @@ -222,7 +244,8 @@ static bool advance_transaction(struct acpi_ec *ec) t->flags |= ACPI_EC_COMMAND_POLL; t->rdata[t->ri++] = 0x00; t->flags |= ACPI_EC_COMMAND_COMPLETE; - pr_debug("software QR_EC completion\n"); + pr_debug("***** Command(%s) software completion *****\n", + acpi_ec_cmd_string(t->command)); wakeup = true; } else if ((status & ACPI_EC_FLAG_IBF) == 0) { acpi_ec_write_cmd(ec, t->command); @@ -303,15 +326,16 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, spin_lock_irqsave(&ec->lock, tmp); /* following two actions should be kept atomic */ ec->curr = t; - pr_debug("transaction start (cmd=0x%02x, addr=0x%02x)\n", - t->command, t->wdata ? t->wdata[0] : 0); + pr_debug("***** Command(%s) started *****\n", + acpi_ec_cmd_string(t->command)); start_transaction(ec); spin_unlock_irqrestore(&ec->lock, tmp); ret = ec_poll(ec); spin_lock_irqsave(&ec->lock, tmp); if (ec->curr->command == ACPI_EC_COMMAND_QUERY) clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); - pr_debug("transaction end\n"); + pr_debug("***** Command(%s) stopped *****\n", + acpi_ec_cmd_string(t->command)); ec->curr = NULL; spin_unlock_irqrestore(&ec->lock, tmp); return ret; -- cgit v1.1 From d3090b6a6cfa6c7a762d6c13340170ca072b2b81 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 14 Oct 2014 14:23:55 +0800 Subject: ACPI / EC: Refine event/query debugging messages. This patch refines event/query debugging messages to use a unified format as commands. Developers can clearly find different processes by checking different log seperators. No functional changes. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index fcf667c..d0247d3 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -332,8 +332,10 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, spin_unlock_irqrestore(&ec->lock, tmp); ret = ec_poll(ec); spin_lock_irqsave(&ec->lock, tmp); - if (ec->curr->command == ACPI_EC_COMMAND_QUERY) + if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); + pr_debug("***** Event stopped *****\n"); + } pr_debug("***** Command(%s) stopped *****\n", acpi_ec_cmd_string(t->command)); ec->curr = NULL; @@ -617,12 +619,12 @@ static void acpi_ec_run(void *cxt) struct acpi_ec_query_handler *handler = cxt; if (!handler) return; - pr_debug("start query execution\n"); + pr_debug("##### Query(0x%02x) started #####\n", handler->query_bit); if (handler->func) handler->func(handler->data); else if (handler->handle) acpi_evaluate_object(handler->handle, NULL, NULL, NULL); - pr_debug("stop query execution\n"); + pr_debug("##### Query(0x%02x) stopped #####\n", handler->query_bit); kfree(handler); } @@ -645,8 +647,8 @@ static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data) if (!copy) return -ENOMEM; memcpy(copy, handler, sizeof(*copy)); - pr_debug("push query execution (0x%2x) on queue\n", - value); + pr_debug("##### Query(0x%02x) scheduled #####\n", + handler->query_bit); return acpi_os_execute((copy->func) ? OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER, acpi_ec_run, copy); @@ -669,7 +671,7 @@ static int ec_check_sci(struct acpi_ec *ec, u8 state) { if (state & ACPI_EC_FLAG_SCI) { if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { - pr_debug("push gpe query to the queue\n"); + pr_debug("***** Event started *****\n"); return acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ec_gpe_query, ec); } -- cgit v1.1 From 7a73e60e398a61612651d503aef9c81260d33918 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 14 Oct 2014 14:24:01 +0800 Subject: ACPI / EC: Cleanup coding style. This patch cleans up the following coding style issues that are detected by scripts/checkpatch.pl: ERROR: code indent should use tabs where possible ERROR: "foo * bar" should be "foo *bar" WARNING: Missing a blank line after declarations WARNING: EXPORT_SYMBOL(foo); should immediately follow its function/variable WARNING: void function return statements are not generally useful WARNING: else is not generally useful after a break or return WARNING: break is not useful after a goto or return WARNING: braces {} are not necessary for single statement blocks WARNING: line over 80 characters WARNING: msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt No functional changes. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 56 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index d0247d3..3d304ff 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -128,12 +128,13 @@ static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ /* -------------------------------------------------------------------------- - Transaction Management - -------------------------------------------------------------------------- */ + * Transaction Management + * -------------------------------------------------------------------------- */ static inline u8 acpi_ec_read_status(struct acpi_ec *ec) { u8 x = inb(ec->command_addr); + pr_debug("EC_SC(R) = 0x%2.2x " "SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d\n", x, @@ -148,6 +149,7 @@ static inline u8 acpi_ec_read_status(struct acpi_ec *ec) static inline u8 acpi_ec_read_data(struct acpi_ec *ec) { u8 x = inb(ec->data_addr); + pr_debug("EC_DATA(R) = 0x%2.2x\n", x); return x; } @@ -189,6 +191,7 @@ static int ec_transaction_completed(struct acpi_ec *ec) { unsigned long flags; int ret = 0; + spin_lock_irqsave(&ec->lock, flags); if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE)) ret = 1; @@ -288,6 +291,7 @@ static int ec_poll(struct acpi_ec *ec) { unsigned long flags; int repeat = 5; /* number of command restarts */ + while (repeat--) { unsigned long delay = jiffies + msecs_to_jiffies(ec_delay); @@ -320,6 +324,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, { unsigned long tmp; int ret = 0; + if (EC_FLAGS_MSI) udelay(ACPI_EC_MSI_UDELAY); /* start transaction */ @@ -347,6 +352,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) { int status; u32 glk; + if (!ec || (!t) || (t->wlen && !t->wdata) || (t->rlen && !t->rdata)) return -EINVAL; if (t->rdata) @@ -410,7 +416,7 @@ static int acpi_ec_burst_disable(struct acpi_ec *ec) acpi_ec_transaction(ec, &t) : 0; } -static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) +static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 *data) { int result; u8 d; @@ -446,10 +452,9 @@ int ec_read(u8 addr, u8 *val) if (!err) { *val = temp_data; return 0; - } else - return err; + } + return err; } - EXPORT_SYMBOL(ec_read); int ec_write(u8 addr, u8 val) @@ -463,22 +468,21 @@ int ec_write(u8 addr, u8 val) return err; } - EXPORT_SYMBOL(ec_write); int ec_transaction(u8 command, - const u8 * wdata, unsigned wdata_len, - u8 * rdata, unsigned rdata_len) + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len) { struct transaction t = {.command = command, .wdata = wdata, .rdata = rdata, .wlen = wdata_len, .rlen = rdata_len}; + if (!first_ec) return -ENODEV; return acpi_ec_transaction(first_ec, &t); } - EXPORT_SYMBOL(ec_transaction); /* Get the handle to the EC device */ @@ -488,7 +492,6 @@ acpi_handle ec_get_handle(void) return NULL; return first_ec->handle; } - EXPORT_SYMBOL(ec_get_handle); /* @@ -552,13 +555,14 @@ void acpi_ec_unblock_transactions_early(void) clear_bit(EC_FLAGS_BLOCKED, &first_ec->flags); } -static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 * data) +static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 *data) { int result; u8 d; struct transaction t = {.command = ACPI_EC_COMMAND_QUERY, .wdata = NULL, .rdata = &d, .wlen = 0, .rlen = 1}; + if (!ec || !data) return -EINVAL; /* @@ -584,6 +588,7 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, { struct acpi_ec_query_handler *handler = kzalloc(sizeof(struct acpi_ec_query_handler), GFP_KERNEL); + if (!handler) return -ENOMEM; @@ -596,12 +601,12 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, mutex_unlock(&ec->mutex); return 0; } - EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler); void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) { struct acpi_ec_query_handler *handler, *tmp; + mutex_lock(&ec->mutex); list_for_each_entry_safe(handler, tmp, &ec->list, node) { if (query_bit == handler->query_bit) { @@ -611,12 +616,12 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) } mutex_unlock(&ec->mutex); } - EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); static void acpi_ec_run(void *cxt) { struct acpi_ec_query_handler *handler = cxt; + if (!handler) return; pr_debug("##### Query(0x%02x) started #####\n", handler->query_bit); @@ -660,6 +665,7 @@ static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data) static void acpi_ec_gpe_query(void *ec_cxt) { struct acpi_ec *ec = ec_cxt; + if (!ec) return; mutex_lock(&ec->mutex); @@ -694,8 +700,8 @@ static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, } /* -------------------------------------------------------------------------- - Address Space Management - -------------------------------------------------------------------------- */ + * Address Space Management + * -------------------------------------------------------------------------- */ static acpi_status acpi_ec_space_handler(u32 function, acpi_physical_address address, @@ -726,27 +732,26 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, switch (result) { case -EINVAL: return AE_BAD_PARAMETER; - break; case -ENODEV: return AE_NOT_FOUND; - break; case -ETIME: return AE_TIME; - break; default: return AE_OK; } } /* -------------------------------------------------------------------------- - Driver Interface - -------------------------------------------------------------------------- */ + * Driver Interface + * -------------------------------------------------------------------------- */ + static acpi_status ec_parse_io_ports(struct acpi_resource *resource, void *context); static struct acpi_ec *make_acpi_ec(void) { struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); + if (!ec) return NULL; ec->flags = 1 << EC_FLAGS_QUERY_PENDING; @@ -769,9 +774,8 @@ acpi_ec_register_query_methods(acpi_handle handle, u32 level, status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); - if (ACPI_SUCCESS(status) && sscanf(node_name, "_Q%x", &value) == 1) { + if (ACPI_SUCCESS(status) && sscanf(node_name, "_Q%x", &value) == 1) acpi_ec_add_query_handler(ec, value, handle, NULL, NULL); - } return AE_OK; } @@ -780,7 +784,6 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) { acpi_status status; unsigned long long tmp = 0; - struct acpi_ec *ec = context; /* clear addr values, ec_parse_io_ports depend on it */ @@ -808,6 +811,7 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) static int ec_install_handlers(struct acpi_ec *ec) { acpi_status status; + if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) return 0; status = acpi_install_gpe_handler(NULL, ec->gpe, @@ -1105,7 +1109,8 @@ int __init acpi_ec_ecdt_probe(void) boot_ec->data_addr = ecdt_ptr->data.address; boot_ec->gpe = ecdt_ptr->gpe; boot_ec->handle = ACPI_ROOT_OBJECT; - acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, &boot_ec->handle); + acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, + &boot_ec->handle); /* Don't trust ECDT, which comes from ASUSTek */ if (!EC_FLAGS_VALIDATE_ECDT) goto install; @@ -1189,6 +1194,5 @@ static void __exit acpi_ec_exit(void) { acpi_bus_unregister_driver(&acpi_ec_driver); - return; } #endif /* 0 */ -- cgit v1.1 From 51315cdfa0521fff3059cec5fb8ffecc7f37cba7 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Sun, 19 Oct 2014 11:30:27 +0200 Subject: cpufreq: allow driver-specific data This commit extends the cpufreq_driver structure with an additional 'void *driver_data' field that can be filled by the ->probe() function of a cpufreq driver to pass additional custom information to the driver itself. A new function called cpufreq_get_driver_data() is added to allow a cpufreq driver to retrieve those driver data, since they are typically needed from a cpufreq_policy->init() callback, which does not have access to the cpufreq_driver structure. This function call is similar to the existing cpufreq_get_current_driver() function call. Signed-off-by: Thomas Petazzoni Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 24bf76f..058d6e0 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1731,6 +1731,21 @@ const char *cpufreq_get_current_driver(void) } EXPORT_SYMBOL_GPL(cpufreq_get_current_driver); +/** + * cpufreq_get_driver_data - return current driver data + * + * Return the private data of the currently loaded cpufreq + * driver, or NULL if no cpufreq driver is loaded. + */ +void *cpufreq_get_driver_data(void) +{ + if (cpufreq_driver) + return cpufreq_driver->driver_data; + + return NULL; +} +EXPORT_SYMBOL_GPL(cpufreq_get_driver_data); + /********************************************************************* * NOTIFIER LISTS INTERFACE * *********************************************************************/ -- cgit v1.1 From 34e5a5273d6aa0ee8836bd5d6111b135ffae6931 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Sun, 19 Oct 2014 11:30:28 +0200 Subject: cpufreq: cpufreq-dt: extend with platform_data This commit extends the cpufreq-dt driver to take a platform_data structure. This structure is for now used to tell the cpufreq-dt driver the layout of the clocks on the platform, i.e whether all CPUs share the same clock or whether each CPU has a separate clock. Signed-off-by: Thomas Petazzoni Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-dt.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 6bbb8b9..52facda 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,7 @@ try_again: static int cpufreq_init(struct cpufreq_policy *policy) { + struct cpufreq_dt_platform_data *pd; struct cpufreq_frequency_table *freq_table; struct thermal_cooling_device *cdev; struct device_node *np; @@ -265,9 +267,18 @@ static int cpufreq_init(struct cpufreq_policy *policy) policy->driver_data = priv; policy->clk = cpu_clk; - ret = cpufreq_generic_init(policy, freq_table, transition_latency); - if (ret) + ret = cpufreq_table_validate_and_show(policy, freq_table); + if (ret) { + dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__, + ret); goto out_cooling_unregister; + } + + policy->cpuinfo.transition_latency = transition_latency; + + pd = cpufreq_get_driver_data(); + if (pd && !pd->independent_clocks) + cpumask_setall(policy->cpus); of_node_put(np); @@ -335,6 +346,8 @@ static int dt_cpufreq_probe(struct platform_device *pdev) if (!IS_ERR(cpu_reg)) regulator_put(cpu_reg); + dt_cpufreq_driver.driver_data = dev_get_platdata(&pdev->dev); + ret = cpufreq_register_driver(&dt_cpufreq_driver); if (ret) dev_err(cpu_dev, "failed register driver: %d\n", ret); -- cgit v1.1 From a00de1ab21acbd150db341cb56f1897550d6688c Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Sun, 19 Oct 2014 11:30:29 +0200 Subject: cpufreq: cpufreq-dt: adjust message related to regulators The cpufreq-dt driver tries to get a regulator for each CPU. This regulator is optional, but when not present, a scary message "failed to get cpuX regulator" is displayed. To solve this, we reduce the severity of the message from dev_warn() to dev_dbg() and we reword the message to not be as scary. Signed-off-by: Thomas Petazzoni Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-dt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 52facda..92c162a 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -147,8 +147,8 @@ try_again: goto try_again; } - dev_warn(cpu_dev, "failed to get cpu%d regulator: %ld\n", - cpu, PTR_ERR(cpu_reg)); + dev_dbg(cpu_dev, "no regulator for cpu%d: %ld\n", + cpu, PTR_ERR(cpu_reg)); } cpu_clk = clk_get(cpu_dev, NULL); -- cgit v1.1 From 74aa51b5ccd3975392e30d11820dc073c5f2cd32 Mon Sep 17 00:00:00 2001 From: "Preeti U. Murthy" Date: Tue, 14 Oct 2014 13:23:00 +0530 Subject: cpuidle: powernv: Populate cpuidle state details by querying the device-tree We hard code the metrics relevant for cpuidle states in the kernel today. Instead pick them up from the device tree so that they remain relevant and updated for the system that the kernel is running on. Signed-off-by: Preeti U. Murthy Signed-off-by: Shreyas B. Prabhu Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/cpuidle-powernv.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index a64be57..7d3a349 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c @@ -163,7 +163,8 @@ static int powernv_add_idle_states(void) int nr_idle_states = 1; /* Snooze */ int dt_idle_states; const __be32 *idle_state_flags; - u32 len_flags, flags; + const __be32 *idle_state_latency; + u32 len_flags, flags, latency_ns; int i; /* Currently we have snooze statically defined */ @@ -180,18 +181,32 @@ static int powernv_add_idle_states(void) return nr_idle_states; } + idle_state_latency = of_get_property(power_mgt, + "ibm,cpu-idle-state-latencies-ns", NULL); + if (!idle_state_latency) { + pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-latencies-ns\n"); + return nr_idle_states; + } + dt_idle_states = len_flags / sizeof(u32); for (i = 0; i < dt_idle_states; i++) { flags = be32_to_cpu(idle_state_flags[i]); + + /* Cpuidle accepts exit_latency in us and we estimate + * target residency to be 10x exit_latency + */ + latency_ns = be32_to_cpu(idle_state_latency[i]); if (flags & IDLE_USE_INST_NAP) { /* Add NAP state */ strcpy(powernv_states[nr_idle_states].name, "Nap"); strcpy(powernv_states[nr_idle_states].desc, "Nap"); powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIME_VALID; - powernv_states[nr_idle_states].exit_latency = 10; - powernv_states[nr_idle_states].target_residency = 100; + powernv_states[nr_idle_states].exit_latency = + ((unsigned int)latency_ns) / 1000; + powernv_states[nr_idle_states].target_residency = + ((unsigned int)latency_ns / 100); powernv_states[nr_idle_states].enter = &nap_loop; nr_idle_states++; } @@ -202,8 +217,10 @@ static int powernv_add_idle_states(void) strcpy(powernv_states[nr_idle_states].desc, "FastSleep"); powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TIMER_STOP; - powernv_states[nr_idle_states].exit_latency = 300; - powernv_states[nr_idle_states].target_residency = 1000000; + powernv_states[nr_idle_states].exit_latency = + ((unsigned int)latency_ns) / 1000; + powernv_states[nr_idle_states].target_residency = + ((unsigned int)latency_ns / 100); powernv_states[nr_idle_states].enter = &fastsleep_loop; nr_idle_states++; } -- cgit v1.1 From a1ecf3c4560ef8260f73ffb85b425f3a046cd419 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 20 Oct 2014 18:47:34 -0300 Subject: [media] af9035: make sure loading modules is const Make sure that loaded modules are const char strings so we don't load arbitrary modules in the future, nor allow for format string leaks in the module request call. Signed-off-by: Kees Cook Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 00758c8..1896ab2 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -193,8 +193,8 @@ static int af9035_wr_reg_mask(struct dvb_usb_device *d, u32 reg, u8 val, return af9035_wr_regs(d, reg, &val, 1); } -static int af9035_add_i2c_dev(struct dvb_usb_device *d, char *type, u8 addr, - void *platform_data, struct i2c_adapter *adapter) +static int af9035_add_i2c_dev(struct dvb_usb_device *d, const char *type, + u8 addr, void *platform_data, struct i2c_adapter *adapter) { int ret, num; struct state *state = d_to_priv(d); @@ -221,7 +221,7 @@ static int af9035_add_i2c_dev(struct dvb_usb_device *d, char *type, u8 addr, goto err; } - request_module(board_info.type); + request_module("%s", board_info.type); /* register I2C device */ client = i2c_new_device(adapter, &board_info); -- cgit v1.1 From 7383159f413b8b07fe689a25ee55915f86cf2f36 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 20 Oct 2014 18:49:04 -0300 Subject: [media] anysee: make sure loading modules is const Make sure that loaded modules are const char strings so we don't load arbitrary modules in the future, nor allow for format string leaks in the module request call. Signed-off-by: Kees Cook Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/anysee.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index d3c5f23..ae917c0 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -630,8 +630,8 @@ error: return ret; } -static int anysee_add_i2c_dev(struct dvb_usb_device *d, char *type, u8 addr, - void *platform_data) +static int anysee_add_i2c_dev(struct dvb_usb_device *d, const char *type, + u8 addr, void *platform_data) { int ret, num; struct anysee_state *state = d_to_priv(d); @@ -659,7 +659,7 @@ static int anysee_add_i2c_dev(struct dvb_usb_device *d, char *type, u8 addr, goto err; } - request_module(board_info.type); + request_module("%s", board_info.type); /* register I2C device */ client = i2c_new_device(adapter, &board_info); -- cgit v1.1 From 031d705c07bc7b911c49c49ae346e3223b6ced9b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Sep 2014 03:50:18 -0300 Subject: [media] vivid: fix Kconfig FB dependency The vivid driver depends on FB, update the Kconfig accordingly. Signed-off-by: Hans Verkuil Reported-by: Jim Davis Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/Kconfig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/vivid/Kconfig b/drivers/media/platform/vivid/Kconfig index d71139a..c309093 100644 --- a/drivers/media/platform/vivid/Kconfig +++ b/drivers/media/platform/vivid/Kconfig @@ -1,8 +1,11 @@ config VIDEO_VIVID tristate "Virtual Video Test Driver" - depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 + depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 && FB select FONT_SUPPORT select FONT_8x16 + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT select VIDEOBUF2_VMALLOC default n ---help--- -- cgit v1.1 From 430e35724bf008742a211d4c18a9b2dff1f61a86 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Sep 2014 06:48:16 -0300 Subject: [media] em28xx: fix uninitialized variable warning Fix this daily build warning: In file included from build/media_build/v4l/em28xx-core.c:35:0: build/media_build/v4l/em28xx-core.c: In function 'em28xx_audio_setup': build/media_build/v4l/em28xx.h:798:2: warning: 'vid' may be used uninitialized in this function [-Wmaybe-uninitialized] printk(KERN_INFO "%s: "fmt,\ ^ build/media_build/v4l/em28xx-core.c:507:6: note: 'vid' was declared here u32 vid; ^ As far as I can tell 'vid' can not really be used uninitialized here, but the code is sufficiently complex that apparently gcc can't figure that out. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index b5e52fe..901cf2b 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -504,7 +504,7 @@ EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); int em28xx_audio_setup(struct em28xx *dev) { int vid1, vid2, feat, cfg; - u32 vid; + u32 vid = 0; u8 i2s_samplerates; if (dev->chip_id == CHIP_ID_EM2870 || -- cgit v1.1 From a0bd3e0b31500251876da505295834e73f33f6f8 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 2 Oct 2014 11:19:15 -0300 Subject: [media] saa7146: Create a device name before it's used request_irq() uses it, tries to create a procfs file with an empty name otherwise. Link: https://bugzilla.kernel.org/show_bug.cgi?id=83771 Signed-off-by: Lubomir Rintel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/common/saa7146/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c index 97afee6..4418119 100644 --- a/drivers/media/common/saa7146/saa7146_core.c +++ b/drivers/media/common/saa7146/saa7146_core.c @@ -364,6 +364,9 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent goto out; } + /* create a nice device name */ + sprintf(dev->name, "saa7146 (%d)", saa7146_num); + DEB_EE("pci:%p\n", pci); err = pci_enable_device(pci); @@ -438,9 +441,6 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent /* the rest + print status message */ - /* create a nice device name */ - sprintf(dev->name, "saa7146 (%d)", saa7146_num); - pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n", dev->mem, dev->revision, pci->irq, pci->subsystem_vendor, pci->subsystem_device); -- cgit v1.1 From c204e1fafbd50a158a34c8a5bd9682cb04ecb29b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 7 Oct 2014 08:58:55 -0300 Subject: [media] vivid: fix buffer overrun The random_line buffer must be twice the maximum width, but it only allocated the maximum width, so it was only half the size it needed to be. Surprisingly I never saw the kernel fail on this, but the same TPG code used in qv4l2 crashed and valgrind helped me track this bug down. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-tpg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/vivid/vivid-tpg.c b/drivers/media/platform/vivid/vivid-tpg.c index 0c6fa53..cbcd625 100644 --- a/drivers/media/platform/vivid/vivid-tpg.c +++ b/drivers/media/platform/vivid/vivid-tpg.c @@ -136,7 +136,7 @@ int tpg_alloc(struct tpg_data *tpg, unsigned max_w) tpg->black_line[plane] = vzalloc(max_w * pixelsz); if (!tpg->black_line[plane]) return -ENOMEM; - tpg->random_line[plane] = vzalloc(max_w * pixelsz); + tpg->random_line[plane] = vzalloc(max_w * 2 * pixelsz); if (!tpg->random_line[plane]) return -ENOMEM; } -- cgit v1.1 From c601f53f8fe5aab4d8b506104d0fd0a7b6a19922 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 30 Sep 2014 18:28:42 -0300 Subject: [media] v4l: uvcvideo: Fix buffer completion size check Commit e93e7fd9f5a3fffec7792dbcc4c3574653effda7 ("v4l2: uvcvideo: Allow using larger buffers") reworked the buffer size sanity check at buffer completion time to use the frame size instead of the allocated buffer size. However, it introduced two bugs in doing so: - it assigned the allocated buffer size to the frame_size field, instead of assigning the correct frame size - it performed the assignment in the S_FMT handler, resulting in the frame_size field being uninitialized if the userspace application doesn't call S_FMT. Fix both issues by removing the frame_size field and validating the buffer size against the UVC video control dwMaxFrameSize. Fixes: e93e7fd9f5a3 ("v4l2: uvcvideo: Allow using larger buffers") Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_v4l2.c | 1 - drivers/media/usb/uvc/uvc_video.c | 2 +- drivers/media/usb/uvc/uvcvideo.h | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 60a8e2c..378ae02 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -318,7 +318,6 @@ static int uvc_v4l2_set_format(struct uvc_streaming *stream, stream->ctrl = probe; stream->cur_format = format; stream->cur_frame = frame; - stream->frame_size = fmt->fmt.pix.sizeimage; done: mutex_unlock(&stream->mutex); diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 9ace520..df81b9c 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1143,7 +1143,7 @@ static int uvc_video_encode_data(struct uvc_streaming *stream, static void uvc_video_validate_buffer(const struct uvc_streaming *stream, struct uvc_buffer *buf) { - if (stream->frame_size != buf->bytesused && + if (stream->ctrl.dwMaxVideoFrameSize != buf->bytesused && !(stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED)) buf->error = 1; } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 6f676c2..864ada7 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -457,7 +457,6 @@ struct uvc_streaming { struct uvc_format *def_format; struct uvc_format *cur_format; struct uvc_frame *cur_frame; - size_t frame_size; /* Protect access to ctrl, cur_format, cur_frame and hardware video * probe control. -- cgit v1.1 From 143800a5778e577ada53441a42ed1437b3b2b11b Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Wed, 24 Sep 2014 00:06:54 -0300 Subject: [media] cx23885: initialize config structs for T9580 The config structs used for DVBSky T9580 were not initialized. This patch fixes that. Signed-off-by: Olli Salonen Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-dvb.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 13734b8..4cb9031 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -1600,6 +1600,7 @@ static int dvb_register(struct cx23885_tsport *port) break; /* attach tuner */ + memset(&m88ts2022_config, 0, sizeof(m88ts2022_config)); m88ts2022_config.fe = fe0->dvb.frontend; m88ts2022_config.clock = 27000000; memset(&info, 0, sizeof(struct i2c_board_info)); @@ -1635,6 +1636,7 @@ static int dvb_register(struct cx23885_tsport *port) /* port c - terrestrial/cable */ case 2: /* attach frontend */ + memset(&si2168_config, 0, sizeof(si2168_config)); si2168_config.i2c_adapter = &adapter; si2168_config.fe = &fe0->dvb.frontend; si2168_config.ts_mode = SI2168_TS_SERIAL; @@ -1654,6 +1656,7 @@ static int dvb_register(struct cx23885_tsport *port) port->i2c_client_demod = client_demod; /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = fe0->dvb.frontend; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", I2C_NAME_SIZE); -- cgit v1.1 From 9f93c52783faa24c5c6fca216acf0765ad5d8dd6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 24 Sep 2014 07:36:39 -0300 Subject: [media] hackrf: harmless off by one in debug code My static checker complains that "i" could be one element beyond the end of the array. Signed-off-by: Dan Carpenter Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/hackrf/hackrf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c index 328b5ba..fd1fa41 100644 --- a/drivers/media/usb/hackrf/hackrf.c +++ b/drivers/media/usb/hackrf/hackrf.c @@ -932,7 +932,7 @@ static int hackrf_set_bandwidth(struct hackrf_dev *dev) dev->bandwidth->val = bandwidth; dev->bandwidth->cur.val = bandwidth; - dev_dbg(dev->dev, "bandwidth selected=%d\n", bandwidth_lut[i].freq); + dev_dbg(dev->dev, "bandwidth selected=%d\n", bandwidth); u16tmp = 0; u16tmp |= ((bandwidth >> 0) & 0xff) << 0; -- cgit v1.1 From 40d43c4b4cac4c2647bf07110d7b07d35f399a84 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Fri, 17 Oct 2014 13:38:50 +0200 Subject: dm raid: ensure superblock's size matches device's logical block size The dm-raid superblock (struct dm_raid_superblock) is padded to 512 bytes and that size is being used to read it in from the metadata device into one preallocated page. Reading or writing this on a 512-byte sector device works fine but on a 4096-byte sector device this fails. Set the dm-raid superblock's size to the logical block size of the metadata device, because IO at that size is guaranteed too work. Also add a size check to avoid silent partial metadata loss in case the superblock should ever grow past the logical block size or PAGE_SIZE. [includes pointer math fix from Dan Carpenter] Reported-by: "Liuhua Wang" Signed-off-by: Heinz Mauelshagen Signed-off-by: Dan Carpenter Signed-off-by: Mike Snitzer Cc: stable@vger.kernel.org --- drivers/md/dm-raid.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index 4857fa4..a7cb9dd 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -789,8 +789,7 @@ struct dm_raid_superblock { __le32 layout; __le32 stripe_sectors; - __u8 pad[452]; /* Round struct to 512 bytes. */ - /* Always set to 0 when writing. */ + /* Remainder of a logical block is zero-filled when writing (see super_sync()). */ } __packed; static int read_disk_sb(struct md_rdev *rdev, int size) @@ -827,7 +826,7 @@ static void super_sync(struct mddev *mddev, struct md_rdev *rdev) test_bit(Faulty, &(rs->dev[i].rdev.flags))) failed_devices |= (1ULL << i); - memset(sb, 0, sizeof(*sb)); + memset(sb + 1, 0, rdev->sb_size - sizeof(*sb)); sb->magic = cpu_to_le32(DM_RAID_MAGIC); sb->features = cpu_to_le32(0); /* No features yet */ @@ -862,7 +861,11 @@ static int super_load(struct md_rdev *rdev, struct md_rdev *refdev) uint64_t events_sb, events_refsb; rdev->sb_start = 0; - rdev->sb_size = sizeof(*sb); + rdev->sb_size = bdev_logical_block_size(rdev->meta_bdev); + if (rdev->sb_size < sizeof(*sb) || rdev->sb_size > PAGE_SIZE) { + DMERR("superblock size of a logical block is no longer valid"); + return -EINVAL; + } ret = read_disk_sb(rdev, rdev->sb_size); if (ret) -- cgit v1.1 From b6931c9ba728d60c542c39ff037fe6f595c074a2 Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan <_govind@gmx.com> Date: Sun, 19 Oct 2014 14:20:27 +0530 Subject: enic: fix possible deadlock in enic_stop/ enic_rfs_flw_tbl_free The following warning is shown when spinlock debug is enabled. This occurs when enic_flow_may_expire timer function is running and enic_stop is called on same CPU. Fix this by using spink_lock_bh(). ================================= [ INFO: inconsistent lock state ] 3.17.0-netnext-05504-g59f35b8 #268 Not tainted --------------------------------- inconsistent {IN-SOFTIRQ-W} -> {SOFTIRQ-ON-W} usage. ifconfig/443 [HC0[0]:SC0[0]:HE1:SE1] takes: (&(&enic->rfs_h.lock)->rlock){+.?...}, at: enic_rfs_flw_tbl_free+0x34/0xd0 [enic] {IN-SOFTIRQ-W} state was registered at: [] __lock_acquire+0x83f/0x21c0 [] lock_acquire+0xa2/0xd0 [] _raw_spin_lock+0x3c/0x80 [] enic_flow_may_expire+0x25/0x130[enic] [] call_timer_fn+0x77/0x100 [] run_timer_softirq+0x1e3/0x270 [] __do_softirq+0x14e/0x280 [] irq_exit+0x8e/0xb0 [] smp_apic_timer_interrupt+0x3f/0x50 [] apic_timer_interrupt+0x72/0x80 [] default_idle+0x13/0x20 [] arch_cpu_idle+0xa/0x10 [] cpu_startup_entry+0x2c6/0x330 [] start_secondary+0x21d/0x290 irq event stamp: 2997 hardirqs last enabled at (2997): [] _raw_spin_unlock_irqrestore+0x65/0x90 hardirqs last disabled at (2996): [] _raw_spin_lock_irqsave+0x26/0x90 softirqs last enabled at (2968): [] dev_deactivate_many+0x213/0x260 softirqs last disabled at (2966): [] dev_deactivate_many+0x1f3/0x260 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&(&enic->rfs_h.lock)->rlock); lock(&(&enic->rfs_h.lock)->rlock); *** DEADLOCK *** Reported-by: Jan Stancek Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com> Signed-off-by: David S. Miller --- drivers/net/ethernet/cisco/enic/enic_clsf.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cisco/enic/enic_clsf.c b/drivers/net/ethernet/cisco/enic/enic_clsf.c index 69dfd3c..0be6850 100644 --- a/drivers/net/ethernet/cisco/enic/enic_clsf.c +++ b/drivers/net/ethernet/cisco/enic/enic_clsf.c @@ -86,7 +86,7 @@ void enic_rfs_flw_tbl_free(struct enic *enic) int i; enic_rfs_timer_stop(enic); - spin_lock(&enic->rfs_h.lock); + spin_lock_bh(&enic->rfs_h.lock); enic->rfs_h.free = 0; for (i = 0; i < (1 << ENIC_RFS_FLW_BITSHIFT); i++) { struct hlist_head *hhead; @@ -100,7 +100,7 @@ void enic_rfs_flw_tbl_free(struct enic *enic) kfree(n); } } - spin_unlock(&enic->rfs_h.lock); + spin_unlock_bh(&enic->rfs_h.lock); } struct enic_rfs_fltr_node *htbl_fltr_search(struct enic *enic, u16 fltr_id) @@ -128,7 +128,7 @@ void enic_flow_may_expire(unsigned long data) bool res; int j; - spin_lock(&enic->rfs_h.lock); + spin_lock_bh(&enic->rfs_h.lock); for (j = 0; j < ENIC_CLSF_EXPIRE_COUNT; j++) { struct hlist_head *hhead; struct hlist_node *tmp; @@ -148,7 +148,7 @@ void enic_flow_may_expire(unsigned long data) } } } - spin_unlock(&enic->rfs_h.lock); + spin_unlock_bh(&enic->rfs_h.lock); mod_timer(&enic->rfs_h.rfs_may_expire, jiffies + HZ/4); } @@ -183,7 +183,7 @@ int enic_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, return -EPROTONOSUPPORT; tbl_idx = skb_get_hash_raw(skb) & ENIC_RFS_FLW_MASK; - spin_lock(&enic->rfs_h.lock); + spin_lock_bh(&enic->rfs_h.lock); n = htbl_key_search(&enic->rfs_h.ht_head[tbl_idx], &keys); if (n) { /* entry already present */ @@ -277,7 +277,7 @@ int enic_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, } ret_unlock: - spin_unlock(&enic->rfs_h.lock); + spin_unlock_bh(&enic->rfs_h.lock); return res; } -- cgit v1.1 From 39dc90c159c1bcc0fdd42913a7d560b1a1cd3acf Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan <_govind@gmx.com> Date: Sun, 19 Oct 2014 14:20:28 +0530 Subject: enic: Do not call napi_disable when preemption is disabled. In enic_stop, we disable preemption using local_bh_disable(). We disable preemption to wait for busy_poll to finish. napi_disable should not be called here as it might sleep. Moving napi_disable() call out side of local_bh_disable. BUG: sleeping function called from invalid context at include/linux/netdevice.h:477 in_atomic(): 1, irqs_disabled(): 0, pid: 443, name: ifconfig INFO: lockdep is turned off. Preemption disabled at:[] enic_rfs_flw_tbl_free+0x34/0xd0 [enic] CPU: 31 PID: 443 Comm: ifconfig Not tainted 3.17.0-netnext-05504-g59f35b8 #268 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 ffff8800dac10000 ffff88020b8dfcb8 ffffffff8148a57c 0000000000000000 ffff88020b8dfcd0 ffffffff8107e253 ffff8800dac12a40 ffff88020b8dfd10 ffffffffa029305b ffff88020b8dfd48 ffff8800dac10000 ffff88020b8dfd48 Call Trace: [] dump_stack+0x4e/0x7a [] __might_sleep+0x123/0x1a0 [] enic_stop+0xdb/0x4d0 [enic] [] __dev_close_many+0x9d/0xf0 [] __dev_close+0x31/0x50 [] __dev_change_flags+0x98/0x160 [] dev_change_flags+0x24/0x60 [] devinet_ioctl+0x63d/0x710 [] ? might_fault+0x56/0xc0 [] inet_ioctl+0x65/0x90 [] sock_do_ioctl+0x20/0x50 [] sock_ioctl+0x20b/0x2e0 [] do_vfs_ioctl+0x2e0/0x500 [] ? sysret_check+0x22/0x5d [] ? __this_cpu_preempt_check+0x13/0x20 [] ? trace_hardirqs_on_caller+0x119/0x270 [] SyS_ioctl+0x3c/0x80 [] system_call_fastpath+0x1a/0x1f Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com> Signed-off-by: David S. Miller --- drivers/net/ethernet/cisco/enic/enic_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 929bfe7..180e53f 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -1674,13 +1674,13 @@ static int enic_stop(struct net_device *netdev) enic_dev_disable(enic); - local_bh_disable(); for (i = 0; i < enic->rq_count; i++) { napi_disable(&enic->napi[i]); + local_bh_disable(); while (!enic_poll_lock_napi(&enic->rq[i])) mdelay(1); + local_bh_enable(); } - local_bh_enable(); netif_carrier_off(netdev); netif_tx_disable(netdev); -- cgit v1.1 From a5b7616c55e188fe3d6ef686bef402d4703ecb62 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 30 Sep 2014 03:14:55 +0100 Subject: mtd: m25p80,spi-nor: Fix module aliases for m25p80 m25p80's device ID table is now spi_nor_ids, defined in spi-nor. The MODULE_DEVICE_TABLE() macro doesn't work with extern definitions, but its use was also removed at the same time. Now if m25p80 is built as a module it doesn't get the necessary aliases to be loaded automatically. A clean solution to this will involve defining the list of device IDs in spi-nor.h and removing struct spi_device_id from the spi-nor API, but this is quite a large change. As a quick fix suitable for stable, copy the device IDs back into m25p80. Fixes: 03e296f613af ("mtd: m25p80: use the SPI nor framework") Cc: # 3.16.x: 32f1b7c8352f: mtd: move support for struct flash_platform_data into m25p80 Cc: # 3.16.x: 90e55b3812a1: mtd: m25p80: get rid of spi_get_device_id Cc: # 3.16.x: 70f3ce0510af: mtd: spi-nor: make spi_nor_scan() take a chip type name, not spi_device_id Cc: # 3.16.x Signed-off-by: Ben Hutchings Signed-off-by: Brian Norris --- drivers/mtd/devices/m25p80.c | 52 ++++++++++++++++++++++++++++++++++++++++++- drivers/mtd/spi-nor/spi-nor.c | 3 +-- 2 files changed, 52 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index bd5e4c6..ed827cf 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -261,12 +261,62 @@ static int m25p_remove(struct spi_device *spi) } +/* + * XXX This needs to be kept in sync with spi_nor_ids. We can't share + * it with spi-nor, because if this is built as a module then modpost + * won't be able to read it and add appropriate aliases. + */ +static const struct spi_device_id m25p_ids[] = { + {"at25fs010"}, {"at25fs040"}, {"at25df041a"}, {"at25df321a"}, + {"at25df641"}, {"at26f004"}, {"at26df081a"}, {"at26df161a"}, + {"at26df321"}, {"at45db081d"}, + {"en25f32"}, {"en25p32"}, {"en25q32b"}, {"en25p64"}, + {"en25q64"}, {"en25qh128"}, {"en25qh256"}, + {"f25l32pa"}, + {"mr25h256"}, {"mr25h10"}, + {"gd25q32"}, {"gd25q64"}, + {"160s33b"}, {"320s33b"}, {"640s33b"}, + {"mx25l2005a"}, {"mx25l4005a"}, {"mx25l8005"}, {"mx25l1606e"}, + {"mx25l3205d"}, {"mx25l3255e"}, {"mx25l6405d"}, {"mx25l12805d"}, + {"mx25l12855e"},{"mx25l25635e"},{"mx25l25655e"},{"mx66l51235l"}, + {"mx66l1g55g"}, + {"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q256a"}, + {"n25q512a"}, {"n25q512ax3"}, {"n25q00"}, + {"pm25lv512"}, {"pm25lv010"}, {"pm25lq032"}, + {"s25sl032p"}, {"s25sl064p"}, {"s25fl256s0"}, {"s25fl256s1"}, + {"s25fl512s"}, {"s70fl01gs"}, {"s25sl12800"}, {"s25sl12801"}, + {"s25fl129p0"}, {"s25fl129p1"}, {"s25sl004a"}, {"s25sl008a"}, + {"s25sl016a"}, {"s25sl032a"}, {"s25sl064a"}, {"s25fl008k"}, + {"s25fl016k"}, {"s25fl064k"}, + {"sst25vf040b"},{"sst25vf080b"},{"sst25vf016b"},{"sst25vf032b"}, + {"sst25vf064c"},{"sst25wf512"}, {"sst25wf010"}, {"sst25wf020"}, + {"sst25wf040"}, + {"m25p05"}, {"m25p10"}, {"m25p20"}, {"m25p40"}, + {"m25p80"}, {"m25p16"}, {"m25p32"}, {"m25p64"}, + {"m25p128"}, {"n25q032"}, + {"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"}, + {"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"}, + {"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"}, + {"m45pe10"}, {"m45pe80"}, {"m45pe16"}, + {"m25pe20"}, {"m25pe80"}, {"m25pe16"}, + {"m25px16"}, {"m25px32"}, {"m25px32-s0"}, {"m25px32-s1"}, + {"m25px64"}, + {"w25x10"}, {"w25x20"}, {"w25x40"}, {"w25x80"}, + {"w25x16"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"}, + {"w25x64"}, {"w25q64"}, {"w25q128"}, {"w25q80"}, + {"w25q80bl"}, {"w25q128"}, {"w25q256"}, {"cat25c11"}, + {"cat25c03"}, {"cat25c09"}, {"cat25c17"}, {"cat25128"}, + { }, +}; +MODULE_DEVICE_TABLE(spi, m25p_ids); + + static struct spi_driver m25p80_driver = { .driver = { .name = "m25p80", .owner = THIS_MODULE, }, - .id_table = spi_nor_ids, + .id_table = m25p_ids, .probe = m25p_probe, .remove = m25p_remove, diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 5c8e399..c51ee52 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -475,7 +475,7 @@ struct flash_info { * more nor chips. This current list focusses on newer chips, which * have been converging on command sets which including JEDEC ID. */ -const struct spi_device_id spi_nor_ids[] = { +static const struct spi_device_id spi_nor_ids[] = { /* Atmel -- some are (confusingly) marketed as "DataFlash" */ { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, @@ -639,7 +639,6 @@ const struct spi_device_id spi_nor_ids[] = { { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { }, }; -EXPORT_SYMBOL_GPL(spi_nor_ids); static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) { -- cgit v1.1 From 8a2f38ddfeb526c30b3ec209468172a30a38d996 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 24 Sep 2014 11:00:37 +0300 Subject: ACPI / platform: provide default DMA mask Most devices are configured for 32-bit DMA addresses. Setting the mask to 32-bit here removes the need for the drivers to do it separately. Signed-off-by: Heikki Krogerus Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_platform.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 2bf9082..8d099e6 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "internal.h" @@ -102,6 +103,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) pdevinfo.res = resources; pdevinfo.num_res = count; pdevinfo.acpi_node.companion = adev; + pdevinfo.dma_mask = DMA_BIT_MASK(32); pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) dev_err(&adev->dev, "platform device creation failed: %ld\n", -- cgit v1.1 From c572aaf46f71f63ae5914d4e194a955e0ba1b519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 16 Oct 2014 11:39:44 +0200 Subject: qxl: don't create too large primary surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Limit primary to qemu vgamem size, to avoid reaching qemu guest bug "requested primary larger than framebuffer" on resizing screen too large to fit. Remove unneeded and misleading variables. Related to: https://bugzilla.redhat.com/show_bug.cgi?id=1127552 Signed-off-by: Marc-André Lureau Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/gpu/drm/qxl/qxl_display.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index af9e785..0d13962 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -572,7 +572,6 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc, struct qxl_framebuffer *qfb; struct qxl_bo *bo, *old_bo = NULL; struct qxl_crtc *qcrtc = to_qxl_crtc(crtc); - uint32_t width, height, base_offset; bool recreate_primary = false; int ret; int surf_id; @@ -602,9 +601,10 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc, if (qcrtc->index == 0) recreate_primary = true; - width = mode->hdisplay; - height = mode->vdisplay; - base_offset = 0; + if (bo->surf.stride * bo->surf.height > qdev->vram_size) { + DRM_ERROR("Mode doesn't fit in vram size (vgamem)"); + return -EINVAL; + } ret = qxl_bo_reserve(bo, false); if (ret != 0) @@ -618,10 +618,10 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc, if (recreate_primary) { qxl_io_destroy_primary(qdev); qxl_io_log(qdev, - "recreate primary: %dx%d (was %dx%d,%d,%d)\n", - width, height, bo->surf.width, - bo->surf.height, bo->surf.stride, bo->surf.format); - qxl_io_create_primary(qdev, base_offset, bo); + "recreate primary: %dx%d,%d,%d\n", + bo->surf.width, bo->surf.height, + bo->surf.stride, bo->surf.format); + qxl_io_create_primary(qdev, 0, bo); bo->is_primary = true; } -- cgit v1.1 From 37773c4e7f6156c34d852c154ef7ce818867d521 Mon Sep 17 00:00:00 2001 From: Maarten ter Huurne Date: Thu, 9 Oct 2014 11:48:30 +0200 Subject: fbcon: Fix option parsing control flow in fb_console_setup Since strsep is used to tokenize the options string, after each option match the code should use "continue" to get the next token from strsep. This patch applies this pattern consistently. Previously, for "scrollback:" and "map:" the parse code would return (unconditionally: strsep ensures *options != ','), causing any following option to be ignored, while for "vc:" the parse code would go on to parse further options within the same token, which could lead to invalid input being accepted. Signed-off-by: Maarten ter Huurne Acked-by: Paul Cercueil Signed-off-by: Tomi Valkeinen --- drivers/video/console/fbcon.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 57b1d44..eb976ee 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -448,8 +448,10 @@ static int __init fb_console_setup(char *this_opt) return 1; while ((options = strsep(&this_opt, ",")) != NULL) { - if (!strncmp(options, "font:", 5)) + if (!strncmp(options, "font:", 5)) { strlcpy(fontname, options + 5, sizeof(fontname)); + continue; + } if (!strncmp(options, "scrollback:", 11)) { options += 11; @@ -457,13 +459,9 @@ static int __init fb_console_setup(char *this_opt) fbcon_softback_size = simple_strtoul(options, &options, 0); if (*options == 'k' || *options == 'K') { fbcon_softback_size *= 1024; - options++; } - if (*options != ',') - return 1; - options++; - } else - return 1; + } + continue; } if (!strncmp(options, "map:", 4)) { @@ -478,8 +476,7 @@ static int __init fb_console_setup(char *this_opt) fbcon_map_override(); } - - return 1; + continue; } if (!strncmp(options, "vc:", 3)) { @@ -491,7 +488,8 @@ static int __init fb_console_setup(char *this_opt) if (*options++ == '-') last_fb_vc = simple_strtoul(options, &options, 10) - 1; fbcon_is_default = 0; - } + continue; + } if (!strncmp(options, "rotate:", 7)) { options += 7; @@ -499,6 +497,7 @@ static int __init fb_console_setup(char *this_opt) initial_rotation = simple_strtoul(options, &options, 0); if (initial_rotation > 3) initial_rotation = 0; + continue; } } return 1; -- cgit v1.1 From 89f0244e7f732c5007a065c8f0e5ad44343ae1aa Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Tue, 14 Oct 2014 04:53:49 -0700 Subject: video/console: Resolve several shadow warnings Resolve shadow warnings that appear in W=2 builds by renaming the "state" global to "vgastate". Signed-off-by: Mark Rustad Signed-off-by: Jeff Kirsher Signed-off-by: Tomi Valkeinen --- drivers/video/console/vgacon.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 6e6aa70..517f565 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -56,7 +56,7 @@ static int cursor_size_lastfrom; static int cursor_size_lastto; static u32 vgacon_xres; static u32 vgacon_yres; -static struct vgastate state; +static struct vgastate vgastate; #define BLANK 0x0020 @@ -400,7 +400,7 @@ static const char *vgacon_startup(void) vga_video_num_lines = screen_info.orig_video_lines; vga_video_num_columns = screen_info.orig_video_cols; - state.vgabase = NULL; + vgastate.vgabase = NULL; if (screen_info.orig_video_mode == 7) { /* Monochrome display */ @@ -851,12 +851,12 @@ static void vga_set_palette(struct vc_data *vc, unsigned char *table) { int i, j; - vga_w(state.vgabase, VGA_PEL_MSK, 0xff); + vga_w(vgastate.vgabase, VGA_PEL_MSK, 0xff); for (i = j = 0; i < 16; i++) { - vga_w(state.vgabase, VGA_PEL_IW, table[i]); - vga_w(state.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2); - vga_w(state.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2); - vga_w(state.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2); + vga_w(vgastate.vgabase, VGA_PEL_IW, table[i]); + vga_w(vgastate.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2); + vga_w(vgastate.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2); + vga_w(vgastate.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2); } } @@ -1008,7 +1008,7 @@ static int vgacon_blank(struct vc_data *c, int blank, int mode_switch) switch (blank) { case 0: /* Unblank */ if (vga_vesa_blanked) { - vga_vesa_unblank(&state); + vga_vesa_unblank(&vgastate); vga_vesa_blanked = 0; } if (vga_palette_blanked) { @@ -1022,7 +1022,7 @@ static int vgacon_blank(struct vc_data *c, int blank, int mode_switch) case 1: /* Normal blanking */ case -1: /* Obsolete */ if (!mode_switch && vga_video_type == VIDEO_TYPE_VGAC) { - vga_pal_blank(&state); + vga_pal_blank(&vgastate); vga_palette_blanked = 1; return 0; } @@ -1034,7 +1034,7 @@ static int vgacon_blank(struct vc_data *c, int blank, int mode_switch) return 1; default: /* VESA blanking */ if (vga_video_type == VIDEO_TYPE_VGAC) { - vga_vesa_blank(&state, blank - 1); + vga_vesa_blank(&vgastate, blank - 1); vga_vesa_blanked = blank; } return 0; @@ -1280,7 +1280,7 @@ static int vgacon_font_set(struct vc_data *c, struct console_font *font, unsigne (charcount != 256 && charcount != 512)) return -EINVAL; - rc = vgacon_do_font_op(&state, font->data, 1, charcount == 512); + rc = vgacon_do_font_op(&vgastate, font->data, 1, charcount == 512); if (rc) return rc; @@ -1299,7 +1299,7 @@ static int vgacon_font_get(struct vc_data *c, struct console_font *font) font->charcount = vga_512_chars ? 512 : 256; if (!font->data) return 0; - return vgacon_do_font_op(&state, font->data, 0, vga_512_chars); + return vgacon_do_font_op(&vgastate, font->data, 0, vga_512_chars); } #else -- cgit v1.1 From d0124f01ed3dd11338a9fb4ed980af9d3dd6d6bf Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 17 Oct 2014 14:19:15 +0200 Subject: drivers: video: fbdev: atmel_lcdfb.c: remove unnecessary header Remove unnecessary mach/cpu.h header to be able to converge to a multiplatform kernel. Signed-off-by: Alexandre Belloni Acked-by: Nicolas Ferre Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/atmel_lcdfb.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/fbdev/atmel_lcdfb.c b/drivers/video/fbdev/atmel_lcdfb.c index 3bf4031..9ec81d4 100644 --- a/drivers/video/fbdev/atmel_lcdfb.c +++ b/drivers/video/fbdev/atmel_lcdfb.c @@ -27,7 +27,6 @@ #include #include