Merge "msm: mdss: release all overlay resources if calling process is unknown"
diff --git a/Documentation/devicetree/bindings/arm/msm/acpuclock/clock-a7.txt b/Documentation/devicetree/bindings/arm/msm/acpuclock/clock-a7.txt
new file mode 100644
index 0000000..10eb0fe
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/acpuclock/clock-a7.txt
@@ -0,0 +1,37 @@
+* Qualcomm Application CPU clock driver
+
+clock-a7 is the driver for the Root Clock Generator (rcg) hw which controls
+the cpu rate. RCGs support selecting one of several clock inputs, as well as
+a configurable divider. This hw is different than normal rcgs in that it may
+optionally have a register which encodes the maximum rate supported by hw.
+
+Required properties:
+- compatible: "qcom,clock-a7-8226"
+- reg: pairs of physical address and region size
+- reg-names: "rcg-base" is expected
+- clock-names: list of names of clock inputs
+- qcom,speedX-bin-vZ:
+ A table of CPU frequency (Hz) to regulator voltage (uV) mapping.
+ Format: <freq uV>
+ This represents the max frequency possible for each possible
+ power configuration for a CPU that's binned as speed bin X,
+ speed bin revision Z. Speed bin values can be between [0-7]
+ and the version can be between [0-3].
+
+- cpu-vdd-supply: regulator phandle for cpu power domain.
+
+Optional properties:
+- reg-names: "efuse"
+
+Example:
+ qcom,acpuclk@f9011050 {
+ compatible = "qcom,clock-a7-8226";
+ reg = <0xf9011050 0x8>;
+ reg-names = "rcg_base";
+ cpu-vdd-supply = <&apc_vreg_corner>;
+
+ clock-names = "clk-4", "clk-5";
+ qcom,speed0-bin-v0 =
+ <384000000 1150000>,
+ <600000000 1200000>;
+ };
diff --git a/arch/arm/boot/dts/msm8226-v1-pm.dtsi b/arch/arm/boot/dts/msm8226-v1-pm.dtsi
index 02feec8..16b10fd 100644
--- a/arch/arm/boot/dts/msm8226-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-v1-pm.dtsi
@@ -280,6 +280,8 @@
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
qcom,pc-resets-timer;
+ qcom,cpus-as-clocks;
+ qcom,synced-clocks;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8226-v2-pm.dtsi b/arch/arm/boot/dts/msm8226-v2-pm.dtsi
index 31d5a8f..0e634ff 100644
--- a/arch/arm/boot/dts/msm8226-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-v2-pm.dtsi
@@ -292,6 +292,8 @@
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
qcom,pc-resets-timer;
+ qcom,cpus-as-clocks;
+ qcom,synced-clocks;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8226-v2.dtsi b/arch/arm/boot/dts/msm8226-v2.dtsi
index a57adcd..d74554f 100644
--- a/arch/arm/boot/dts/msm8226-v2.dtsi
+++ b/arch/arm/boot/dts/msm8226-v2.dtsi
@@ -74,10 +74,45 @@
};
&soc {
- qcom,acpuclk@f9011050 {
+ qcom,clock-a7@f9011050 {
reg = <0xf9011050 0x8>,
<0xfc4b80b0 0x8>;
- reg-names = "rcg_base", "pte_efuse";
+ reg-names = "rcg-base", "efuse";
+ qcom,speed0-bin-v2 =
+ < 0 0>,
+ < 384000000 1>,
+ < 787200000 2>,
+ <1190400000 3>;
+ qcom,speed6-bin-v2 =
+ < 0 0>,
+ < 384000000 1>,
+ < 787200000 2>,
+ <1190400000 3>;
+ qcom,speed2-bin-v2 =
+ < 0 0>,
+ < 384000000 1>,
+ < 787200000 2>,
+ <1401600000 3>;
+ qcom,speed5-bin-v2 =
+ < 0 0>,
+ < 384000000 1>,
+ < 787200000 2>,
+ <1401600000 3>;
+ qcom,speed4-bin-v2 =
+ < 0 0>,
+ < 384000000 1>,
+ < 787200000 2>,
+ <149700000 3>;
+ qcom,speed7-bin-v2 =
+ < 0 0>,
+ < 384000000 1>,
+ < 787200000 2>,
+ <1497600000 3>;
+ qcom,speed1-bin-v2 =
+ < 0 0>,
+ < 384000000 1>,
+ < 787200000 2>,
+ <1593600000 3>;
};
};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index c78dacd..c86d3f3 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -1000,11 +1000,37 @@
qcom,scl-gpio = <&msmgpio 19 0>;
};
- qcom,acpuclk@f9011050 {
- compatible = "qcom,acpuclk-a7";
+ qcom,clock-a7@f9011050 {
+ compatible = "qcom,clock-a7-8226";
reg = <0xf9011050 0x8>;
- reg-names = "rcg_base";
- a7_cpu-supply = <&apc_vreg_corner>;
+ reg-names = "rcg-base";
+ clock-names = "clk-4", "clk-5";
+ qcom,speed0-bin-v0 =
+ < 0 0>,
+ < 384000000 1>,
+ < 787200000 2>,
+ <1190400000 3>;
+
+ cpu-vdd-supply = <&apc_vreg_corner>;
+ };
+
+ qcom,msm-cpufreq@0 {
+ reg = <0 4>;
+ compatible = "qcom,msm-cpufreq";
+ qcom,cpu-mem-ports = <1 512>;
+ qcom,cpufreq-table =
+ < 300000 1600 /* 200 MHz */ >,
+ < 384000 1600 /* 200 MHz */ >,
+ < 600000 3200 /* 320 MHz */ >,
+ < 787200 4264 /* 533 MHz */ >,
+ < 998400 4264 /* 533 MHz */ >,
+ < 1094400 4264 /* 533 MHz */ >,
+ < 1190400 4264 /* 533 MHz */ >,
+ < 1305600 4264 /* 533 MHz */ >,
+ < 1344000 4264 /* 533 MHz */ >,
+ < 1401600 4264 /* 533 MHz */ >,
+ < 1497600 4264 /* 533 MHz */ >,
+ < 1593600 4264 /* 533 MHz */ >;
};
qcom,ocmem@fdd00000 {
diff --git a/arch/arm/configs/msm8610-perf_defconfig b/arch/arm/configs/msm8610-perf_defconfig
index 0d443a6..c5f0ddd 100644
--- a/arch/arm/configs/msm8610-perf_defconfig
+++ b/arch/arm/configs/msm8610-perf_defconfig
@@ -374,6 +374,7 @@
CONFIG_QPNP_VIBRATOR=y
CONFIG_QPNP_REVID=y
CONFIG_MSM_IOMMU_V0=y
+CONFIG_SENSORS=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index f467b95..303e911 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -412,6 +412,7 @@
CONFIG_CORESIGHT_WCN_ETM=y
CONFIG_CORESIGHT_RPM_ETM=y
CONFIG_CORESIGHT_EVENT=m
+CONFIG_SENSORS=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index a341c23..c0f6b10 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -435,6 +435,7 @@
select SPARSE_IRQ
select MEMORY_HOLE_CARVEOUT
select QMI_ENCDEC
+ select MSM_CORTEX_A7
config ARCH_MSM8610
bool "MSM8610"
@@ -461,6 +462,7 @@
select MSM_PM8X60 if PM
select MEMORY_HOLE_CARVEOUT
select MSM_BUS_SCALING
+ select MSM_CORTEX_A7
select CPU_FREQ_MSM
select CPU_FREQ
select MSM_PIL
@@ -501,6 +503,7 @@
select MSM_PM8X60 if PM
select MEMORY_HOLE_CARVEOUT
select MSM_BUS_SCALING
+ select MSM_CORTEX_A7
select CPU_FREQ_MSM
select CPU_FREQ
select MSM_PIL
@@ -573,6 +576,9 @@
bool
select ARM_L1_CACHE_SHIFT_6
+config MSM_CORTEX_A7
+ bool
+
config MSM_SMP
select HAVE_SMP
bool
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 3079b64..d269e92 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -23,6 +23,7 @@
endif
obj-y += acpuclock.o
+obj-$(CONFIG_MSM_CORTEX_A7) += clock-a7.o
obj-$(CONFIG_HW_PERF_EVENTS) += perf_trace_counters.o
obj-$(CONFIG_ARCH_MSM_KRAIT) += acpuclock-krait.o clock-krait.o
ifdef CONFIG_ARCH_MSM_KRAIT
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index a883e39..53cea4e 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -3163,6 +3163,8 @@
CLK_LOOKUP("xo", xo_a_clk.c, "f9011050.qcom,acpuclk"),
CLK_LOOKUP("gpll0", gpll0_ao.c, "f9011050.qcom,acpuclk"),
CLK_LOOKUP("a7sspll", a7sspll.c, "f9011050.qcom,acpuclk"),
+ CLK_LOOKUP("clk-4", gpll0_ao.c, "f9011050.qcom,clock-a7"),
+ CLK_LOOKUP("clk-5", a7sspll.c, "f9011050.qcom,clock-a7"),
CLK_LOOKUP("kpss_ahb", kpss_ahb_clk_src.c, ""),
/* WCNSS CLOCKS */
diff --git a/arch/arm/mach-msm/clock-a7.c b/arch/arm/mach-msm/clock-a7.c
new file mode 100644
index 0000000..5b8dc4e
--- /dev/null
+++ b/arch/arm/mach-msm/clock-a7.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2013, 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
+
+#include <mach/clock-generic.h>
+#include "clock-local2.h"
+
+#define UPDATE_CHECK_MAX_LOOPS 200
+
+struct cortex_reg_data {
+ u32 cmd_offset;
+ u32 update_mask;
+ u32 poll_mask;
+};
+
+#define DIV_REG(x) ((x)->base + (x)->div_offset)
+#define SRC_REG(x) ((x)->base + (x)->src_offset)
+#define CMD_REG(x) ((x)->base + \
+ ((struct cortex_reg_data *)(x)->priv)->cmd_offset)
+
+static int update_config(struct mux_div_clk *md)
+{
+ u32 regval, count;
+ struct cortex_reg_data *r = md->priv;
+
+ /* Update the configuration */
+ regval = readl_relaxed(CMD_REG(md));
+ regval |= r->update_mask;
+ writel_relaxed(regval, CMD_REG(md));
+
+ /* Wait for update to take effect */
+ for (count = UPDATE_CHECK_MAX_LOOPS; count > 0; count--) {
+ if (!(readl_relaxed(CMD_REG(md)) &
+ r->poll_mask))
+ return 0;
+ udelay(1);
+ }
+
+ CLK_WARN(&md->c, true, "didn't update its configuration.");
+
+ return -EINVAL;
+}
+
+static void cortex_get_config(struct mux_div_clk *md, u32 *src_sel, u32 *div)
+{
+ u32 regval;
+
+ regval = readl_relaxed(DIV_REG(md));
+ regval &= (md->div_mask << md->div_shift);
+ *div = regval >> md->div_shift;
+ *div = max((u32)1, (*div + 1) / 2);
+
+ regval = readl_relaxed(SRC_REG(md));
+ regval &= (md->src_mask << md->src_shift);
+ *src_sel = regval >> md->src_shift;
+}
+
+static int cortex_set_config(struct mux_div_clk *md, u32 src_sel, u32 div)
+{
+ u32 regval;
+
+ div = div ? ((2 * div) - 1) : 0;
+ regval = readl_relaxed(DIV_REG(md));
+ regval &= ~(md->div_mask << md->div_shift);
+ regval |= div << md->div_shift;
+ writel_relaxed(regval, DIV_REG(md));
+
+ regval = readl_relaxed(SRC_REG(md));
+ regval &= ~(md->src_mask << md->src_shift);
+ regval |= src_sel << md->src_shift;
+ writel_relaxed(regval, SRC_REG(md));
+
+ return update_config(md);
+}
+
+static int cortex_enable(struct mux_div_clk *md)
+{
+ u32 src_sel = parent_to_src_sel(md->parents, md->num_parents,
+ md->c.parent);
+ return cortex_set_config(md, src_sel, md->data.div);
+}
+
+static void cortex_disable(struct mux_div_clk *md)
+{
+ u32 src_sel = parent_to_src_sel(md->parents, md->num_parents,
+ md->safe_parent);
+ cortex_set_config(md, src_sel, md->safe_div);
+}
+
+static bool cortex_is_enabled(struct mux_div_clk *md)
+{
+ return true;
+}
+
+struct mux_div_ops cortex_mux_div_ops = {
+ .set_src_div = cortex_set_config,
+ .get_src_div = cortex_get_config,
+ .is_enabled = cortex_is_enabled,
+ .enable = cortex_enable,
+ .disable = cortex_disable,
+};
+
+static struct cortex_reg_data a7ssmux_priv = {
+ .cmd_offset = 0x0,
+ .update_mask = BIT(0),
+ .poll_mask = BIT(0),
+};
+
+DEFINE_VDD_REGS_INIT(vdd_cpu, 1);
+
+static struct mux_div_clk a7ssmux = {
+ .ops = &cortex_mux_div_ops,
+ .safe_freq = 300000000,
+ .data = {
+ .max_div = 8,
+ .min_div = 1,
+ },
+ .c = {
+ .dbg_name = "a7ssmux",
+ .ops = &clk_ops_mux_div_clk,
+ .vdd_class = &vdd_cpu,
+ CLK_INIT(a7ssmux.c),
+ },
+ .parents = (struct clk_src[8]) {},
+ .priv = &a7ssmux_priv,
+ .div_offset = 0x4,
+ .div_mask = BM(4, 0),
+ .div_shift = 0,
+ .src_offset = 0x4,
+ .src_mask = BM(10, 8) >> 8,
+ .src_shift = 8,
+};
+
+static struct clk_lookup clock_tbl_a7[] = {
+ CLK_LOOKUP("cpu0_clk", a7ssmux.c, "0.qcom,msm-cpufreq"),
+ CLK_LOOKUP("cpu0_clk", a7ssmux.c, "fe805664.qcom,pm-8x60"),
+};
+
+static int of_get_fmax_vdd_class(struct platform_device *pdev, struct clk *c,
+ char *prop_name)
+{
+ struct device_node *of = pdev->dev.of_node;
+ int prop_len, i;
+ struct clk_vdd_class *vdd = c->vdd_class;
+ u32 *array;
+
+ if (!of_find_property(of, prop_name, &prop_len)) {
+ dev_err(&pdev->dev, "missing %s\n", prop_name);
+ return -EINVAL;
+ }
+
+ prop_len /= sizeof(u32);
+ if (prop_len % 2) {
+ dev_err(&pdev->dev, "bad length %d\n", prop_len);
+ return -EINVAL;
+ }
+
+ prop_len /= 2;
+ vdd->level_votes = devm_kzalloc(&pdev->dev, prop_len * sizeof(int),
+ GFP_KERNEL);
+ if (!vdd->level_votes)
+ return -ENOMEM;
+
+ vdd->vdd_uv = devm_kzalloc(&pdev->dev, prop_len * sizeof(int),
+ GFP_KERNEL);
+ if (!vdd->vdd_uv)
+ return -ENOMEM;
+
+ c->fmax = devm_kzalloc(&pdev->dev, prop_len * sizeof(unsigned long),
+ GFP_KERNEL);
+ if (!c->fmax)
+ return -ENOMEM;
+
+ array = devm_kzalloc(&pdev->dev, prop_len * sizeof(u32), GFP_KERNEL);
+ if (!array)
+ return -ENOMEM;
+
+ of_property_read_u32_array(of, prop_name, array, prop_len * 2);
+ for (i = 0; i < prop_len; i++) {
+ c->fmax[i] = array[2 * i];
+ vdd->vdd_uv[i] = array[2 * i + 1];
+ }
+
+ devm_kfree(&pdev->dev, array);
+ vdd->num_levels = prop_len;
+ vdd->cur_level = prop_len;
+ c->num_fmax = prop_len;
+ return 0;
+}
+
+static void get_speed_bin(struct platform_device *pdev, int *bin, int *version)
+{
+ struct resource *res;
+ void __iomem *base;
+ u32 pte_efuse, redundant_sel, valid;
+
+ *bin = 0;
+ *version = 0;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse");
+ if (!res) {
+ dev_info(&pdev->dev,
+ "No speed/PVS binning available. Defaulting to 0!\n");
+ return;
+ }
+
+ base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!base) {
+ dev_warn(&pdev->dev,
+ "Unable to read efuse data. Defaulting to 0!\n");
+ return;
+ }
+
+ pte_efuse = readl_relaxed(base);
+ devm_iounmap(&pdev->dev, base);
+
+ redundant_sel = (pte_efuse >> 24) & 0x7;
+ *bin = pte_efuse & 0x7;
+ valid = (pte_efuse >> 3) & 0x1;
+ *version = (pte_efuse >> 4) & 0x3;
+
+ if (redundant_sel == 1)
+ *bin = (pte_efuse >> 27) & 0x7;
+
+ if (!valid) {
+ dev_info(&pdev->dev, "Speed bin not set. Defaulting to 0!\n");
+ *bin = 0;
+ } else {
+ dev_info(&pdev->dev, "Speed bin: %d\n", *bin);
+ }
+
+ dev_info(&pdev->dev, "PVS version: %d\n", *version);
+
+ return;
+}
+
+static int of_get_clk_src(struct platform_device *pdev, struct clk_src *parents)
+{
+ struct device_node *of = pdev->dev.of_node;
+ int num_parents, i, j, index;
+ struct clk *c;
+ char clk_name[] = "clk-x";
+
+ num_parents = of_property_count_strings(of, "clock-names");
+ if (num_parents <= 0 || num_parents > 8) {
+ dev_err(&pdev->dev, "missing clock-names\n");
+ return -EINVAL;
+ }
+
+ j = 0;
+ for (i = 0; i < 8; i++) {
+ snprintf(clk_name, ARRAY_SIZE(clk_name), "clk-%d", i);
+ index = of_property_match_string(of, "clock-names", clk_name);
+ if (IS_ERR_VALUE(index))
+ continue;
+
+ parents[j].sel = i;
+ parents[j].src = c = devm_clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(c)) {
+ if (c != ERR_PTR(-EPROBE_DEFER))
+ dev_err(&pdev->dev, "clk_get: %s\n fail",
+ clk_name);
+ return PTR_ERR(c);
+ }
+ j++;
+ }
+
+ return num_parents;
+}
+
+static int clock_a7_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int speed_bin = 0, version = 0, rc;
+ unsigned long rate, aux_rate;
+ struct clk *aux_clk, *main_pll;
+ char prop_name[] = "qcom,speedX-bin-vX";
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rcg-base");
+ if (!res) {
+ dev_err(&pdev->dev, "missing rcg-base\n");
+ return -EINVAL;
+ }
+ a7ssmux.base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!a7ssmux.base) {
+ dev_err(&pdev->dev, "ioremap failed for rcg-base\n");
+ return -ENOMEM;
+ }
+
+ vdd_cpu.regulator[0] = devm_regulator_get(&pdev->dev, "cpu-vdd");
+ if (IS_ERR(vdd_cpu.regulator[0])) {
+ if (PTR_ERR(vdd_cpu.regulator[0]) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "unable to get regulator\n");
+ return PTR_ERR(vdd_cpu.regulator[0]);
+ }
+
+ a7ssmux.num_parents = of_get_clk_src(pdev, a7ssmux.parents);
+ if (IS_ERR_VALUE(a7ssmux.num_parents))
+ return a7ssmux.num_parents;
+
+ get_speed_bin(pdev, &speed_bin, &version);
+
+ snprintf(prop_name, ARRAY_SIZE(prop_name),
+ "qcom,speed%d-bin-v%d", speed_bin, version);
+ rc = of_get_fmax_vdd_class(pdev, &a7ssmux.c, prop_name);
+ if (rc) {
+ /* Fall back to most conservative PVS table */
+ dev_err(&pdev->dev, "Unable to load voltage plan %s!\n",
+ prop_name);
+ rc = of_get_fmax_vdd_class(pdev, &a7ssmux.c,
+ "qcom,speed0-bin-v0");
+ if (rc) {
+ dev_err(&pdev->dev,
+ "Unable to load safe voltage plan\n");
+ return rc;
+ }
+ dev_info(&pdev->dev, "Safe voltage plan loaded.\n");
+ }
+
+ rc = msm_clock_register(clock_tbl_a7, ARRAY_SIZE(clock_tbl_a7));
+ if (rc) {
+ dev_err(&pdev->dev, "msm_clock_register failed\n");
+ return rc;
+ }
+
+ /* Force a PLL reconfiguration */
+ aux_clk = a7ssmux.parents[0].src;
+ main_pll = a7ssmux.parents[1].src;
+
+ aux_rate = clk_get_rate(aux_clk);
+ rate = clk_get_rate(&a7ssmux.c);
+ clk_set_rate(&a7ssmux.c, aux_rate);
+ clk_set_rate(main_pll, clk_round_rate(main_pll, 1));
+ clk_set_rate(&a7ssmux.c, rate);
+
+ /*
+ * We don't want the CPU clocks to be turned off at late init
+ * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
+ * refcount of these clocks. Any cpufreq/hotplug manager can assume
+ * that the clocks have already been prepared and enabled by the time
+ * they take over.
+ */
+ WARN(clk_prepare_enable(&a7ssmux.c),
+ "Unable to turn on CPU clock");
+ return 0;
+}
+
+static struct of_device_id clock_a7_match_table[] = {
+ {.compatible = "qcom,clock-a7-8226"},
+ {}
+};
+
+static struct platform_driver clock_a7_driver = {
+ .driver = {
+ .name = "clock-a7",
+ .of_match_table = clock_a7_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init clock_a7_init(void)
+{
+ return platform_driver_probe(&clock_a7_driver, clock_a7_probe);
+}
+device_initcall(clock_a7_init);
diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c
index 35917c3..c3b7229 100644
--- a/arch/arm/mach-msm/clock-debug.c
+++ b/arch/arm/mach-msm/clock-debug.c
@@ -23,6 +23,7 @@
#include <linux/clkdev.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
+#include <linux/io.h>
#include <mach/clk-provider.h>
@@ -412,6 +413,56 @@
.write = clock_parent_write,
};
+void clk_debug_print_hw(struct clk *clk, struct seq_file *f)
+{
+ void __iomem *base;
+ struct clk_register_data *regs;
+ u32 i, j, size;
+
+ if (IS_ERR_OR_NULL(clk))
+ return;
+
+ clk_debug_print_hw(clk->parent, f);
+
+ clock_debug_output(f, false, "%s\n", clk->dbg_name);
+
+ if (!clk->ops->list_registers)
+ return;
+
+ j = 0;
+ base = clk->ops->list_registers(clk, j, ®s, &size);
+ while (!IS_ERR(base)) {
+ for (i = 0; i < size; i++) {
+ u32 val = readl_relaxed(base + regs[i].offset);
+ clock_debug_output(f, false, "%20s: 0x%.8x\n",
+ regs[i].name, val);
+ }
+ j++;
+ base = clk->ops->list_registers(clk, j, ®s, &size);
+ }
+}
+
+static int print_hw_show(struct seq_file *m, void *unused)
+{
+ struct clk *c = m->private;
+ clk_debug_print_hw(c, m);
+
+ return 0;
+}
+
+static int print_hw_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, print_hw_show, inode->i_private);
+}
+
+static const struct file_operations clock_print_hw_fops = {
+ .open = print_hw_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+
static int clock_debug_add(struct clk *clock)
{
char temp[50], *ptr;
@@ -463,6 +514,10 @@
&clock_parent_fops))
goto error;
+ if (!debugfs_create_file("print", S_IRUGO, clk_dir, clock,
+ &clock_print_hw_fops))
+ goto error;
+
return 0;
error:
debugfs_remove_recursive(clk_dir);
diff --git a/arch/arm/mach-msm/clock-pll.c b/arch/arm/mach-msm/clock-pll.c
index a251784..c08df46 100644
--- a/arch/arm/mach-msm/clock-pll.c
+++ b/arch/arm/mach-msm/clock-pll.c
@@ -304,6 +304,22 @@
return HANDOFF_ENABLED_CLK;
}
+static long local_pll_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ struct pll_freq_tbl *nf;
+ struct pll_clk *pll = to_pll_clk(c);
+
+ if (!pll->freq_tbl)
+ return -EINVAL;
+
+ for (nf = pll->freq_tbl; nf->freq_hz != PLL_FREQ_END; nf++)
+ if (nf->freq_hz >= rate)
+ return nf->freq_hz;
+
+ nf--;
+ return nf->freq_hz;
+}
+
static int local_pll_clk_set_rate(struct clk *c, unsigned long rate)
{
struct pll_freq_tbl *nf;
@@ -426,6 +442,7 @@
.enable = sr2_pll_clk_enable,
.disable = local_pll_clk_disable,
.set_rate = local_pll_clk_set_rate,
+ .round_rate = local_pll_clk_round_rate,
.handoff = local_pll_clk_handoff,
};
diff --git a/arch/arm/mach-msm/include/mach/clk-provider.h b/arch/arm/mach-msm/include/mach/clk-provider.h
index 027606e..4529a81 100644
--- a/arch/arm/mach-msm/include/mach/clk-provider.h
+++ b/arch/arm/mach-msm/include/mach/clk-provider.h
@@ -23,6 +23,7 @@
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/regulator/consumer.h>
+#include <linux/seq_file.h>
#include <mach/clk.h>
/*
@@ -41,6 +42,21 @@
#define ENABLE_VOTED 4 /* Bit pol: 1 = running; delay on disable */
#define DELAY 5 /* No bit to check, just delay */
+struct clk_register_data {
+ char *name;
+ u32 offset;
+};
+#ifdef CONFIG_DEBUG_FS
+void clk_debug_print_hw(struct clk *clk, struct seq_file *f);
+#else
+static inline void clk_debug_print_hw(struct clk *clk, struct seq_file *f) {}
+#endif
+
+#define CLK_WARN(clk, cond, fmt, ...) do { \
+ clk_debug_print_hw(clk, NULL); \
+ WARN(cond, "%s: " fmt, (clk)->dbg_name, ##__VA_ARGS__); \
+} while (0)
+
/**
* struct clk_vdd_class - Voltage scaling class
* @class_name: name of the class
@@ -129,6 +145,8 @@
int (*set_parent)(struct clk *clk, struct clk *parent);
struct clk *(*get_parent)(struct clk *clk);
bool (*is_local)(struct clk *clk);
+ void __iomem *(*list_registers)(struct clk *clk, int n,
+ struct clk_register_data **regs, u32 *size);
};
/**
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 2c91351..071fffc 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -866,14 +866,37 @@
struct qseecom_client_send_service_ireq *send_svc_ireq_ptr)
{
int ret = 0;
+ void *req_buf = NULL;
+
if ((req_ptr == NULL) || (send_svc_ireq_ptr == NULL)) {
pr_err("Error with pointer: req_ptr = %p, send_svc_ptr = %p\n",
req_ptr, send_svc_ireq_ptr);
return -EINVAL;
}
+
+ if (((uint32_t)req_ptr->cmd_req_buf <
+ data_ptr->client.user_virt_sb_base)
+ || ((uint32_t)req_ptr->cmd_req_buf >=
+ (data_ptr->client.user_virt_sb_base +
+ data_ptr->client.sb_length))) {
+ pr_err("cmd buffer address not within shared bufffer\n");
+ return -EINVAL;
+ }
+
+
+ if (((uint32_t)req_ptr->resp_buf < data_ptr->client.user_virt_sb_base)
+ || ((uint32_t)req_ptr->resp_buf >=
+ (data_ptr->client.user_virt_sb_base +
+ data_ptr->client.sb_length))){
+ pr_err("response buffer address not within shared bufffer\n");
+ return -EINVAL;
+ }
+
+ req_buf = data_ptr->client.sb_virt;
+
send_svc_ireq_ptr->qsee_cmd_id = req_ptr->cmd_id;
send_svc_ireq_ptr->key_type =
- ((struct qseecom_rpmb_provision_key *)req_ptr->cmd_req_buf)->key_type;
+ ((struct qseecom_rpmb_provision_key *)req_buf)->key_type;
send_svc_ireq_ptr->req_len = req_ptr->cmd_req_len;
send_svc_ireq_ptr->rsp_ptr = (void *)(__qseecom_uvirt_to_kphys(data_ptr,
(uint32_t)req_ptr->resp_buf));
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index c6f6f03..4d194e3 100644
--- a/drivers/thermal/msm_thermal.c
+++ b/drivers/thermal/msm_thermal.c
@@ -37,6 +37,7 @@
#include <linux/regulator/consumer.h>
#define MAX_RAILS 5
+#define MAX_THRESHOLD 2
static struct msm_thermal_data msm_thermal_info;
static uint32_t limited_max_freq = UINT_MAX;
@@ -77,8 +78,10 @@
uint32_t cpu;
bool offline;
bool user_offline;
+ bool thresh_cleared;
const char *sensor_type;
- struct sensor_threshold thresh[2];
+ uint32_t sensor_id;
+ struct sensor_threshold thresh[MAX_THRESHOLD];
};
struct rail {
@@ -624,6 +627,76 @@
return ret;
}
+static int set_and_activate_threshold(uint32_t sensor_id,
+ struct sensor_threshold *threshold)
+{
+ int ret = 0;
+
+ ret = sensor_set_trip(sensor_id, threshold);
+ if (ret != 0) {
+ pr_err("%s: Error in setting trip %d\n",
+ KBUILD_MODNAME, threshold->trip);
+ goto set_done;
+ }
+
+ ret = sensor_activate_trip(sensor_id, threshold, true);
+ if (ret != 0) {
+ pr_err("%s: Error in enabling trip %d\n",
+ KBUILD_MODNAME, threshold->trip);
+ goto set_done;
+ }
+
+set_done:
+ return ret;
+}
+
+static int set_threshold(uint32_t sensor_id,
+ struct sensor_threshold *threshold)
+{
+ struct tsens_device tsens_dev;
+ int i = 0, ret = 0;
+ long temp;
+
+ if ((!threshold) || check_sensor_id(sensor_id)) {
+ pr_err("%s: Invalid input\n", KBUILD_MODNAME);
+ ret = -EINVAL;
+ goto set_threshold_exit;
+ }
+
+ tsens_dev.sensor_num = sensor_id;
+ ret = tsens_get_temp(&tsens_dev, &temp);
+ if (ret) {
+ pr_err("%s: Unable to read TSENS sensor %d\n",
+ KBUILD_MODNAME, tsens_dev.sensor_num);
+ goto set_threshold_exit;
+ }
+ while (i < MAX_THRESHOLD) {
+ switch (threshold[i].trip) {
+ case THERMAL_TRIP_CONFIGURABLE_HI:
+ if (threshold[i].temp >= temp) {
+ ret = set_and_activate_threshold(sensor_id,
+ &threshold[i]);
+ if (ret)
+ goto set_threshold_exit;
+ }
+ break;
+ case THERMAL_TRIP_CONFIGURABLE_LOW:
+ if (threshold[i].temp <= temp) {
+ ret = set_and_activate_threshold(sensor_id,
+ &threshold[i]);
+ if (ret)
+ goto set_threshold_exit;
+ }
+ break;
+ default:
+ break;
+ }
+ i++;
+ }
+set_threshold_exit:
+ return ret;
+}
+
#ifdef CONFIG_SMP
static void __ref do_core_control(long temp)
{
@@ -677,7 +750,7 @@
/* Call with core_control_mutex locked */
static int __ref update_offline_cores(int val)
{
- int cpu = 0;
+ uint32_t cpu = 0;
int ret = 0;
if (!core_control_enabled)
@@ -701,8 +774,7 @@
static __ref int do_hotplug(void *data)
{
int ret = 0;
- int cpu = 0;
- uint32_t mask = 0;
+ uint32_t cpu = 0, mask = 0;
if (!core_control_enabled)
return -EINVAL;
@@ -714,6 +786,11 @@
mutex_lock(&core_control_mutex);
for_each_possible_cpu(cpu) {
+ if (cpus[cpu].thresh_cleared) {
+ set_threshold(cpus[cpu].sensor_id,
+ cpus[cpu].thresh);
+ cpus[cpu].thresh_cleared = false;
+ }
if (cpus[cpu].offline || cpus[cpu].user_offline)
mask |= BIT(cpu);
}
@@ -984,10 +1061,12 @@
default:
break;
}
- if (hotplug_task)
+ if (hotplug_task) {
+ cpu_node->thresh_cleared = true;
complete(&hotplug_notify_complete);
- else
+ } else {
pr_err("%s: Hotplug task is not initialized\n", KBUILD_MODNAME);
+ }
return 0;
}
/* Adjust cpus offlined bit based on temperature reading. */
@@ -1001,8 +1080,7 @@
for_each_possible_cpu(cpu) {
if (!(msm_thermal_info.core_control_mask & BIT(cpus[cpu].cpu)))
continue;
- tsens_dev.sensor_num = sensor_get_id(\
- (char *)cpus[cpu].sensor_type);
+ tsens_dev.sensor_num = cpus[cpu].sensor_id;
if (tsens_get_temp(&tsens_dev, &temp)) {
pr_err("%s: Unable to read TSENS sensor %d\n",
KBUILD_MODNAME, tsens_dev.sensor_num);
@@ -1035,24 +1113,23 @@
return;
for_each_possible_cpu(cpu) {
+ cpus[cpu].cpu = (uint32_t)cpu;
+ cpus[cpu].thresh_cleared = false;
+ cpus[cpu].sensor_id =
+ sensor_get_id((char *)cpus[cpu].sensor_type);
if (!(msm_thermal_info.core_control_mask & BIT(cpus[cpu].cpu)))
continue;
- cpus[cpu].cpu = (uint32_t)cpu;
cpus[cpu].thresh[0].temp = msm_thermal_info.hotplug_temp_degC;
cpus[cpu].thresh[0].trip = THERMAL_TRIP_CONFIGURABLE_HI;
cpus[cpu].thresh[0].notify = hotplug_notify;
cpus[cpu].thresh[0].data = (void *)&cpus[cpu];
- sensor_set_trip(sensor_get_id((char *)cpus[cpu].sensor_type),
- &cpus[cpu].thresh[0]);
cpus[cpu].thresh[1].temp = msm_thermal_info.hotplug_temp_degC -
msm_thermal_info.hotplug_temp_hysteresis_degC;
cpus[cpu].thresh[1].trip = THERMAL_TRIP_CONFIGURABLE_LOW;
cpus[cpu].thresh[1].notify = hotplug_notify;
cpus[cpu].thresh[1].data = (void *)&cpus[cpu];
- sensor_set_trip(sensor_get_id((char *)cpus[cpu].sensor_type),
- &cpus[cpu].thresh[1]);
-
+ set_threshold(cpus[cpu].sensor_id, cpus[cpu].thresh);
}
init_completion(&hotplug_notify_complete);
hotplug_task = kthread_run(do_hotplug, NULL, "msm_thermal:hotplug");
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 8d9da6b..739696d 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -89,30 +89,21 @@
}
EXPORT_SYMBOL(sensor_get_id);
-static long get_min(struct sensor_info *sensor, long temp)
+static int __update_sensor_thresholds(struct sensor_info *sensor)
{
- long min = LONG_MIN;
- struct sensor_threshold *pos, *var;
-
- list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
- if (pos->trip == THERMAL_TRIP_CONFIGURABLE_LOW)
- if (pos->temp < temp && pos->temp > min)
- min = pos->temp;
- }
-
- return min;
-}
-
-static void __update_sensor_thresholds(struct sensor_info *sensor)
-{
- long min = LONG_MIN;
- long max = LONG_MAX;
- long max_of_min = LONG_MIN;
- long min_of_max = LONG_MAX;
+ long max_of_low_thresh = LONG_MIN;
+ long min_of_high_thresh = LONG_MAX;
struct sensor_threshold *pos, *var;
enum thermal_trip_type type;
- int i;
- long curr_temp;
+ int i, ret = 0;
+
+ if (!sensor->tz->ops->set_trip_temp ||
+ !sensor->tz->ops->activate_trip_type ||
+ !sensor->tz->ops->get_trip_type ||
+ !sensor->tz->ops->get_trip_temp) {
+ ret = -ENODEV;
+ goto update_done;
+ }
for (i = 0; ((sensor->max_idx == -1) || (sensor->min_idx == -1)) &&
(sensor->tz->ops->get_trip_type) && (i < sensor->tz->trips);
@@ -128,60 +119,85 @@
THERMAL_TRIP_CONFIGURABLE_HI, &sensor->threshold_max);
}
- sensor->tz->ops->get_temp(sensor->tz, &curr_temp);
list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
+ if (!pos->active)
+ continue;
if (pos->trip == THERMAL_TRIP_CONFIGURABLE_LOW) {
- if (pos->temp > max_of_min)
- max_of_min = pos->temp;
- if (pos->temp < curr_temp && pos->temp > min)
- min = pos->temp;
+ if (pos->temp > max_of_low_thresh)
+ max_of_low_thresh = pos->temp;
}
if (pos->trip == THERMAL_TRIP_CONFIGURABLE_HI) {
- if (pos->temp < min_of_max)
- min_of_max = pos->temp;
- if (pos->temp > curr_temp && pos->temp < max)
- max = pos->temp;
+ if (pos->temp < min_of_high_thresh)
+ min_of_high_thresh = pos->temp;
}
}
- pr_debug("sensor %d: min of max: %ld max of min: %ld\n",
- sensor->sensor_id, max_of_min, min_of_max);
+ pr_debug("sensor %d: Thresholds: max of low: %ld min of high: %ld\n",
+ sensor->sensor_id, max_of_low_thresh,
+ min_of_high_thresh);
- /* If we haven't found a max and min bounding the curr_temp,
- * use the min of max and max of min instead.
- */
- if (max == LONG_MAX)
- max = min_of_max;
- if (min == LONG_MIN) {
- min = get_min(sensor, max);
- if (min == LONG_MIN)
- min = max_of_min;
+ if ((min_of_high_thresh != sensor->threshold_max) &&
+ (min_of_high_thresh != LONG_MAX)) {
+ ret = sensor->tz->ops->set_trip_temp(sensor->tz,
+ sensor->max_idx, min_of_high_thresh);
+ if (ret) {
+ pr_err("sensor %d: Unable to set high threshold %d",
+ sensor->sensor_id, ret);
+ goto update_done;
+ }
+ sensor->threshold_max = min_of_high_thresh;
+ }
+ ret = sensor->tz->ops->activate_trip_type(sensor->tz,
+ sensor->max_idx,
+ (min_of_high_thresh == LONG_MAX) ?
+ THERMAL_TRIP_ACTIVATION_DISABLED :
+ THERMAL_TRIP_ACTIVATION_ENABLED);
+ if (ret) {
+ pr_err("sensor %d: Unable to activate high threshold %d",
+ sensor->sensor_id, ret);
+ goto update_done;
}
- if (sensor->tz->ops->set_trip_temp) {
- if (max != sensor->threshold_max) {
- sensor->tz->ops->set_trip_temp(sensor->tz,
- sensor->max_idx, max);
- sensor->threshold_max = max;
+ if ((max_of_low_thresh != sensor->threshold_min) &&
+ (max_of_low_thresh != LONG_MIN)) {
+ ret = sensor->tz->ops->set_trip_temp(sensor->tz,
+ sensor->min_idx, max_of_low_thresh);
+ if (ret) {
+ pr_err("sensor %d: Unable to set low threshold %d",
+ sensor->sensor_id, ret);
+ goto update_done;
}
- if (min != sensor->threshold_min) {
- sensor->tz->ops->set_trip_temp(sensor->tz,
- sensor->min_idx, min);
- sensor->threshold_min = min;
- }
+ sensor->threshold_min = max_of_low_thresh;
+ }
+ ret = sensor->tz->ops->activate_trip_type(sensor->tz,
+ sensor->min_idx,
+ (max_of_low_thresh == LONG_MIN) ?
+ THERMAL_TRIP_ACTIVATION_DISABLED :
+ THERMAL_TRIP_ACTIVATION_ENABLED);
+ if (ret) {
+ pr_err("sensor %d: Unable to activate low threshold %d",
+ sensor->sensor_id, ret);
+ goto update_done;
}
- pr_debug("sensor %d: curr_temp: %ld min: %ld max: %ld\n",
- sensor->sensor_id, curr_temp,
+ pr_debug("sensor %d: low: %ld high: %ld\n",
+ sensor->sensor_id,
sensor->threshold_min, sensor->threshold_max);
+
+update_done:
+ return ret;
}
static void sensor_update_work(struct work_struct *work)
{
struct sensor_info *sensor = container_of(work, struct sensor_info,
work);
+ int ret = 0;
mutex_lock(&sensor->lock);
- __update_sensor_thresholds(sensor);
+ ret = __update_sensor_thresholds(sensor);
+ if (ret)
+ pr_err("sensor %d: Error %d setting threshold\n",
+ sensor->sensor_id, ret);
mutex_unlock(&sensor->lock);
}
@@ -202,7 +218,7 @@
return 0;
list_for_each_entry_safe(pos, var, &tz->sensor.threshold_list, list) {
- if (pos->trip != trip)
+ if ((pos->trip != trip) || (!pos->active))
continue;
if (((trip == THERMAL_TRIP_CONFIGURABLE_LOW) &&
(pos->temp <= tz->sensor.threshold_min) &&
@@ -210,6 +226,7 @@
((trip == THERMAL_TRIP_CONFIGURABLE_HI) &&
(pos->temp >= tz->sensor.threshold_max) &&
(pos->temp <= temp))) {
+ pos->active = 0;
pos->notify(trip, temp, pos->data);
}
}
@@ -220,6 +237,29 @@
}
EXPORT_SYMBOL(thermal_sensor_trip);
+int sensor_activate_trip(uint32_t sensor_id,
+ struct sensor_threshold *threshold, bool enable)
+{
+ struct sensor_info *sensor = get_sensor(sensor_id);
+ int ret = 0;
+
+ if (!sensor || !threshold) {
+ pr_err("Sensor %d: uninitialized data\n",
+ sensor_id);
+ ret = -ENODEV;
+ goto activate_trip_exit;
+ }
+
+ mutex_lock(&sensor->lock);
+ threshold->active = (enable) ? 1 : 0;
+ ret = __update_sensor_thresholds(sensor);
+ mutex_unlock(&sensor->lock);
+
+activate_trip_exit:
+ return ret;
+}
+EXPORT_SYMBOL(sensor_activate_trip);
+
int sensor_set_trip(uint32_t sensor_id, struct sensor_threshold *threshold)
{
struct sensor_threshold *pos, *var;
@@ -241,8 +281,7 @@
INIT_LIST_HEAD(&threshold->list);
list_add(&threshold->list, &sensor->threshold_list);
}
-
- __update_sensor_thresholds(sensor);
+ threshold->active = 0; /* Do not allow active threshold right away */
mutex_unlock(&sensor->lock);
return 0;
@@ -254,6 +293,7 @@
{
struct sensor_threshold *pos, *var;
struct sensor_info *sensor = get_sensor(sensor_id);
+ int ret = 0;
if (!sensor)
return -ENODEV;
@@ -261,15 +301,16 @@
mutex_lock(&sensor->lock);
list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
if (pos == threshold) {
+ pos->active = 0;
list_del(&pos->list);
break;
}
}
- __update_sensor_thresholds(sensor);
+ ret = __update_sensor_thresholds(sensor);
mutex_unlock(&sensor->lock);
- return 0;
+ return ret;
}
EXPORT_SYMBOL(sensor_cancel_trip);
@@ -283,36 +324,36 @@
return 0;
}
+static void get_trip_threshold(struct thermal_zone_device *tz, int trip,
+ struct sensor_threshold **threshold)
+{
+ enum thermal_trip_type type;
+
+ tz->ops->get_trip_type(tz, trip, &type);
+
+ if (type == THERMAL_TRIP_CONFIGURABLE_HI)
+ *threshold = &tz->tz_threshold[0];
+ else if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
+ *threshold = &tz->tz_threshold[1];
+ else
+ *threshold = NULL;
+}
+
int sensor_set_trip_temp(struct thermal_zone_device *tz,
int trip, long temp)
{
int ret = 0;
- enum thermal_trip_type type;
+ struct sensor_threshold *threshold = NULL;
if (!tz->ops->get_trip_type)
return -EPERM;
- tz->ops->get_trip_type(tz, trip, &type);
- switch (type) {
- case THERMAL_TRIP_CONFIGURABLE_HI:
- tz->tz_threshold[0].temp = temp;
- tz->tz_threshold[0].trip = THERMAL_TRIP_CONFIGURABLE_HI;
- tz->tz_threshold[0].notify = tz_notify_trip;
- tz->tz_threshold[0].data = tz;
- ret = sensor_set_trip(tz->sensor.sensor_id,
- &tz->tz_threshold[0]);
- break;
- case THERMAL_TRIP_CONFIGURABLE_LOW:
- tz->tz_threshold[1].temp = temp;
- tz->tz_threshold[1].trip = THERMAL_TRIP_CONFIGURABLE_LOW;
- tz->tz_threshold[1].notify = tz_notify_trip;
- tz->tz_threshold[1].data = tz;
- ret = sensor_set_trip(tz->sensor.sensor_id,
- &tz->tz_threshold[1]);
- break;
- default:
+ get_trip_threshold(tz, trip, &threshold);
+ if (threshold) {
+ threshold->temp = temp;
+ ret = sensor_set_trip(tz->sensor.sensor_id, threshold);
+ } else {
ret = tz->ops->set_trip_temp(tz, trip, temp);
- break;
}
return ret;
@@ -333,10 +374,12 @@
INIT_LIST_HEAD(&sensor->threshold_list);
INIT_LIST_HEAD(&tz->tz_threshold[0].list);
INIT_LIST_HEAD(&tz->tz_threshold[1].list);
- tz->tz_threshold[0].notify = NULL;
- tz->tz_threshold[0].data = NULL;
- tz->tz_threshold[1].notify = NULL;
- tz->tz_threshold[1].data = NULL;
+ tz->tz_threshold[0].notify = tz_notify_trip;
+ tz->tz_threshold[0].data = tz;
+ tz->tz_threshold[0].trip = THERMAL_TRIP_CONFIGURABLE_HI;
+ tz->tz_threshold[1].notify = tz_notify_trip;
+ tz->tz_threshold[1].data = tz;
+ tz->tz_threshold[1].trip = THERMAL_TRIP_CONFIGURABLE_LOW;
list_add(&sensor->sensor_list, &sensor_info_list);
INIT_WORK(&sensor->work, sensor_update_work);
@@ -489,23 +532,40 @@
const char *buf, size_t count)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
- int trip, result;
+ int trip, result = 0;
+ bool activate;
+ struct sensor_threshold *threshold = NULL;
- if (!tz->ops->activate_trip_type)
- return -EPERM;
+ if (!tz->ops->get_trip_type ||
+ !tz->ops->activate_trip_type) {
+ result = -EPERM;
+ goto trip_activate_exit;
+ }
- if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
- return -EINVAL;
-
- if (!strncmp(buf, "enabled", sizeof("enabled")))
- result = tz->ops->activate_trip_type(tz, trip,
- THERMAL_TRIP_ACTIVATION_ENABLED);
- else if (!strncmp(buf, "disabled", sizeof("disabled")))
- result = tz->ops->activate_trip_type(tz, trip,
- THERMAL_TRIP_ACTIVATION_DISABLED);
- else
+ if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) {
result = -EINVAL;
+ goto trip_activate_exit;
+ }
+ if (!strcmp(buf, "enabled")) {
+ activate = true;
+ } else if (!strcmp(buf, "disabled")) {
+ activate = false;
+ } else {
+ result = -EINVAL;
+ goto trip_activate_exit;
+ }
+
+ get_trip_threshold(tz, trip, &threshold);
+ if (threshold)
+ result = sensor_activate_trip(tz->sensor.sensor_id,
+ threshold, activate);
+ else
+ result = tz->ops->activate_trip_type(tz, trip,
+ activate ? THERMAL_TRIP_ACTIVATION_ENABLED :
+ THERMAL_TRIP_ACTIVATION_DISABLED);
+
+trip_activate_exit:
if (result)
return result;
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index ed6d41b..faf98fe 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -102,6 +102,7 @@
enum thermal_trip_type trip;
int (*notify)(enum thermal_trip_type type, int temp, void *data);
void *data;
+ uint8_t active;
struct list_head list;
};
@@ -189,6 +190,8 @@
int sensor_get_id(char *name);
int sensor_set_trip(uint32_t sensor_id, struct sensor_threshold *threshold);
int sensor_cancel_trip(uint32_t sensor_id, struct sensor_threshold *threshold);
+int sensor_activate_trip(uint32_t sensor_id, struct sensor_threshold *threshold,
+ bool enable);
int thermal_sensor_trip(struct thermal_zone_device *tz,
enum thermal_trip_type trip, long temp);
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index ec99c5f..ba9add2 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -90,7 +90,7 @@
#define WCD9XXX_HPHL_STATUS_READY_WAIT_US 1000
#define WCD9XXX_MUX_SWITCH_READY_WAIT_MS 50
-#define WCD9XXX_MEAS_DELTA_MAX_MV 50
+#define WCD9XXX_MEAS_DELTA_MAX_MV 120
#define WCD9XXX_MEAS_INVALD_RANGE_LOW_MV 20
#define WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV 80
@@ -227,7 +227,7 @@
* setup internal micbias if codec uses internal micbias for
* headset detection
*/
- if (mbhc->mbhc_cfg->use_int_rbias && !mbhc->int_rbias_on) {
+ if (mbhc->mbhc_cfg->use_int_rbias) {
if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
mbhc->mbhc_cb->setup_int_rbias(codec, true);
else
@@ -1103,7 +1103,7 @@
* setup internal micbias if codec uses internal micbias for
* headset detection
*/
- if (mbhc->mbhc_cfg->use_int_rbias && !mbhc->int_rbias_on) {
+ if (mbhc->mbhc_cfg->use_int_rbias) {
if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
mbhc->mbhc_cb->setup_int_rbias(codec, true);
else
@@ -3188,7 +3188,7 @@
* setup internal micbias if codec uses internal micbias for
* headset detection
*/
- if (mbhc->mbhc_cfg->use_int_rbias && !mbhc->int_rbias_on) {
+ if (mbhc->mbhc_cfg->use_int_rbias) {
if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
mbhc->mbhc_cb->setup_int_rbias(codec, true);
else
@@ -4019,7 +4019,6 @@
if (mbhc->mbhc_cfg->use_int_rbias) {
if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) {
mbhc->mbhc_cb->setup_int_rbias(codec, true);
- mbhc->int_rbias_on = true;
} else {
pr_info("%s: internal bias requested but codec did not provide callback\n",
__func__);
@@ -4178,7 +4177,6 @@
case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
- mbhc->int_rbias_on = true;
if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
wcd9xxx_event_to_micbias(event)) {
wcd9xxx_switch_micbias(mbhc, 0);
@@ -4206,7 +4204,6 @@
case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
- mbhc->int_rbias_on = false;
if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
wcd9xxx_event_to_micbias(event)) {
if (mbhc->event_state &
@@ -4502,7 +4499,6 @@
mbhc->mbhc_cb = mbhc_cb;
mbhc->intr_ids = mbhc_cdc_intr_ids;
mbhc->impedance_detect = impedance_det_en;
- mbhc->int_rbias_on = false;
if (mbhc->intr_ids == NULL) {
pr_err("%s: Interrupt mapping not provided\n", __func__);
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
index 7fe9538..39f59bb 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.h
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -336,7 +336,6 @@
u32 rco_clk_rate;
bool update_z;
- bool int_rbias_on;
/* Holds codec specific interrupt mapping */
const struct wcd9xxx_mbhc_intr *intr_ids;
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
index 5d74469..6a22ff2 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.c
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -561,8 +561,15 @@
if (--resmgr->clk_rco_users == 0 &&
resmgr->clk_type == WCD9XXX_CLK_RCO) {
wcd9xxx_disable_clock_block(resmgr);
- snd_soc_update_bits(resmgr->codec,
- WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x00);
+ /* if RCO is enabled, switch from it */
+ if (snd_soc_read(resmgr->codec, WCD9XXX_A_RC_OSC_FREQ)
+ & 0x80) {
+ if (resmgr->codec_type !=
+ WCD9XXX_CDC_TYPE_HELICON)
+ snd_soc_write(resmgr->codec,
+ WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+ wcd9xxx_resmgr_enable_config_mode(resmgr, 0);
+ }
resmgr->clk_type = WCD9XXX_CLK_OFF;
}
break;