diff options
| author | Osvaldo Banuelos <osvaldob@codeaurora.org> | 2016-06-21 09:19:16 -0700 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-07-26 18:38:28 -0700 |
| commit | 3c7530a2d8035698cf839c9b060945ec30dec041 (patch) | |
| tree | 8fe45e98b9a7bbf1d9b46454d1210d8d91395389 | |
| parent | 9992623763c36dc7c46329e69d8b03beac126761 (diff) | |
clk: msm: osm: add support for APM crossover corner
Add support to determine the APM threshold corner via open-loop
voltages in the VDD regulator OPP table. The threshold corner
is used by OSM to determine the APM supply for each DCVS setpoint.
The crossover corner is used by OSM to request a specific voltage
during the APM switch transition.
CRs-Fixed: 1021656 1030444
Change-Id: Iac04f6db8e85b3651a33b6c9bff667365cae891d
Signed-off-by: Osvaldo Banuelos <osvaldob@codeaurora.org>
| -rw-r--r-- | Documentation/devicetree/bindings/arm/msm/qcom,osm.txt | 10 | ||||
| -rw-r--r-- | drivers/clk/msm/clock-osm.c | 85 |
2 files changed, 87 insertions, 8 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt index 2bd7653af5a3..cee9b942a9e3 100644 --- a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt +++ b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt @@ -182,6 +182,14 @@ Properties: command register for each of the two clusters managed by the OSM controller. +- qcom,apm-threshold-voltage + Usage: required + Value type: <u32> + Definition: Specifies the APM threshold voltage in microvolts. If the + VDD_APCC supply voltage is above or at this level, then the + APM is switched to use VDD_APCC. If VDD_APCC is below + this level, then the APM is switched to use VDD_MX. + - qcom,apm-mode-ctl Usage: required Value type: <prop-encoded-array> @@ -392,6 +400,8 @@ Example: qcom,apm-ctrl-status = <0x179d000c 0x179d0018>; + qcom,apm-threshold-voltage = <832000>; + qcom,pwrcl-apcs-mem-acc-cfg = <0x179d1360 0x179d1364 0x179d1364>; qcom,perfcl-apcs-mem-acc-cfg = diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c index c30c88b951b1..4953f818d790 100644 --- a/drivers/clk/msm/clock-osm.c +++ b/drivers/clk/msm/clock-osm.c @@ -187,7 +187,7 @@ enum clk_osm_trace_packet_id { #define MAX_MEM_ACC_VAL_PER_LEVEL 3 #define MAX_MEM_ACC_VALUES (MAX_MEM_ACC_LEVELS * \ MAX_MEM_ACC_VAL_PER_LEVEL) -#define MEM_ACC_READ_MASK 0xff +#define MEM_ACC_APM_READ_MASK 0xff #define TRACE_CTRL 0x1F38 #define TRACE_CTRL_EN_MASK BIT(0) @@ -303,6 +303,7 @@ struct clk_osm { u32 cluster_num; u32 irq; u32 apm_crossover_vc; + u32 apm_threshold_vc; u32 cycle_counter_reads; u32 cycle_counter_delay; u32 cycle_counter_factor; @@ -556,8 +557,8 @@ static void clk_osm_print_osm_table(struct clk_osm *c) lval, table[i].spare_data); } - pr_debug("APM crossover corner: %d\n", - c->apm_crossover_vc); + pr_debug("APM threshold corner=%d, crossover corner=%d\n", + c->apm_threshold_vc, c->apm_crossover_vc); } static int clk_osm_get_lut(struct platform_device *pdev, @@ -1121,10 +1122,21 @@ exit: static int clk_osm_resolve_crossover_corners(struct clk_osm *c, struct platform_device *pdev) { + struct regulator *regulator = c->vdd_reg; struct dev_pm_opp *opp; unsigned long freq = 0; - int vc, rc = 0; + int vc, i, threshold, rc = 0; + u32 corner_volt, data; + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,apm-threshold-voltage", + &threshold); + if (rc) { + pr_info("qcom,apm-threshold-voltage property not specified\n"); + return rc; + } + /* Determine crossover virtual corner */ rcu_read_lock(); opp = dev_pm_opp_find_freq_exact(&c->vdd_dev->dev, freq, true); if (IS_ERR(opp)) { @@ -1143,6 +1155,48 @@ static int clk_osm_resolve_crossover_corners(struct clk_osm *c, vc--; c->apm_crossover_vc = vc; + /* Determine threshold virtual corner */ + for (i = 0; i < OSM_TABLE_SIZE; i++) { + freq = c->osm_table[i].frequency; + /* + * Only frequencies that are supported across all configurations + * are present in the OPP table associated with the regulator + * device. + */ + data = (c->osm_table[i].freq_data & GENMASK(18, 16)) >> 16; + if (data != MAX_CONFIG) + continue; + + rcu_read_lock(); + opp = dev_pm_opp_find_freq_exact(&c->vdd_dev->dev, freq, true); + if (IS_ERR(opp)) { + rc = PTR_ERR(opp); + if (rc == -ERANGE) + pr_err("Frequency %lu not found\n", freq); + goto exit; + } + + vc = dev_pm_opp_get_voltage(opp); + if (!vc) { + pr_err("No virtual corner found for frequency %lu\n", + freq); + rc = -ERANGE; + goto exit; + } + + rcu_read_unlock(); + + corner_volt = regulator_list_corner_voltage(regulator, vc); + + /* CPR virtual corners are zero-based numbered */ + vc--; + + if (corner_volt >= threshold) { + c->apm_threshold_vc = vc; + break; + } + } + return 0; exit: rcu_read_unlock(); @@ -1458,7 +1512,7 @@ static void clk_osm_program_mem_acc_regs(struct clk_osm *c) clk_osm_write_reg(c, MEM_ACC_INSTR_COMP(1), SEQ_REG(56)); clk_osm_write_reg(c, MEM_ACC_INSTR_COMP(2), SEQ_REG(57)); clk_osm_write_reg(c, MEM_ACC_INSTR_COMP(3), SEQ_REG(58)); - clk_osm_write_reg(c, MEM_ACC_READ_MASK, SEQ_REG(59)); + clk_osm_write_reg(c, MEM_ACC_APM_READ_MASK, SEQ_REG(59)); for (i = 0; i < MAX_MEM_ACC_VALUES; i++) clk_osm_write_reg(c, c->apcs_mem_acc_val[i], @@ -1708,17 +1762,32 @@ static void clk_osm_do_additional_setup(struct clk_osm *c, static void clk_osm_apm_vc_setup(struct clk_osm *c) { /* - * APM crossover virtual corner at which the switch - * from APC to MX and vice-versa should take place. + * APM crossover virtual corner corresponds to switching + * voltage during APM transition. APM threshold virtual + * corner is the first corner which requires switch + * sequence of APM from MX to APC. */ if (c->secure_init) { - clk_osm_write_reg(c, c->apm_crossover_vc, SEQ_REG(1)); + clk_osm_write_reg(c, c->apm_threshold_vc, SEQ_REG(1)); + clk_osm_write_reg(c, c->apm_crossover_vc, SEQ_REG(72)); + clk_osm_write_reg(c, c->pbases[OSM_BASE] + SEQ_REG(74), + SEQ_REG(8)); + clk_osm_write_reg(c, 0x3b | c->apm_threshold_vc << 6, + SEQ_REG(73)); + clk_osm_write_reg(c, 0x39 | c->apm_threshold_vc << 6, + SEQ_REG(76)); /* Ensure writes complete before returning */ mb(); } else { scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(1), + c->apm_threshold_vc); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(72), c->apm_crossover_vc); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(73), + 0x3b | c->apm_threshold_vc << 6); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(76), + 0x39 | c->apm_threshold_vc << 6); } } |
