]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
thermal: tegra: add hw-throttle for Tegra132
authorWei Ni <wni@nvidia.com>
Wed, 11 May 2016 10:20:18 +0000 (18:20 +0800)
committerZhang Rui <rui.zhang@intel.com>
Tue, 27 Sep 2016 06:02:32 +0000 (14:02 +0800)
Tegra132 use CCROC throttle registers to configure
pulse skiper, set these registers to enable throttle
function for Tegra132.

Signed-off-by: Wei Ni <wni@nvidia.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
drivers/thermal/tegra/soctherm.c
drivers/thermal/tegra/tegra132-soctherm.c
include/dt-bindings/thermal/tegra124-soctherm.h

index dc75f92e1d8ed39380387313b9a70e2129d15a4f..7d2db23d71a32deab7c524cb0c9ad8466886018a 100644 (file)
 #define CAR_SUPER_CCLKG_DIVIDER                        0x36c
 #define CDIVG_USE_THERM_CONTROLS_MASK          BIT(30)
 
+/* ccroc register offsets needed for enabling HW throttling for Tegra132 */
+#define CCROC_SUPER_CCLKG_DIVIDER              0x024
+
+#define CCROC_GLOBAL_CFG                       0x148
+
+#define CCROC_THROT_PSKIP_RAMP_CPU             0x150
+#define CCROC_THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK    BIT(31)
+#define CCROC_THROT_PSKIP_RAMP_DURATION_MASK   (0xffff << 8)
+#define CCROC_THROT_PSKIP_RAMP_STEP_MASK       0xff
+
+#define CCROC_THROT_PSKIP_CTRL_CPU             0x154
+#define CCROC_THROT_PSKIP_CTRL_ENB_MASK                BIT(31)
+#define CCROC_THROT_PSKIP_CTRL_DIVIDEND_MASK   (0xff << 8)
+#define CCROC_THROT_PSKIP_CTRL_DIVISOR_MASK    0xff
+
 /* get val from register(r) mask bits(m) */
 #define REG_GET_MASK(r, m)     (((r) & (m)) >> (ffs(m) - 1))
 /* set val(v) to mask bits(m) of register(r) */
 #define THROT_DELAY_CTRL(throt)                (THROT_DELAY_LITE + \
                                        (THROT_OFFSET * throt))
 
+/* get CCROC_THROT_PSKIP_xxx offset per HIGH/MED/LOW vect*/
+#define CCROC_THROT_OFFSET                     0x0c
+#define CCROC_THROT_PSKIP_CTRL_CPU_REG(vect)    (CCROC_THROT_PSKIP_CTRL_CPU + \
+                                               (CCROC_THROT_OFFSET * vect))
+#define CCROC_THROT_PSKIP_RAMP_CPU_REG(vect)    (CCROC_THROT_PSKIP_RAMP_CPU + \
+                                               (CCROC_THROT_OFFSET * vect))
+
 /* get THERMCTL_LEVELx offset per CPU/GPU/MEM/TSENSE rg and LEVEL0~3 lv */
 #define THERMCTL_LVL_REGS_SIZE         0x20
 #define THERMCTL_LVL_REG(rg, lv)       ((rg) + ((lv) * THERMCTL_LVL_REGS_SIZE))
@@ -195,6 +217,7 @@ struct soctherm_throt_cfg {
        const char *name;
        unsigned int id;
        u8 priority;
+       u8 cpu_throt_level;
        u32 cpu_throt_depth;
        struct thermal_cooling_device *cdev;
        bool init;
@@ -206,6 +229,7 @@ struct tegra_soctherm {
        struct clk *clock_soctherm;
        void __iomem *regs;
        void __iomem *clk_regs;
+       void __iomem *ccroc_regs;
 
        u32 *calib;
        struct thermal_zone_device **thermctl_tzs;
@@ -241,6 +265,31 @@ static inline u32 clk_readl(struct tegra_soctherm *ts, u32 reg)
        return readl(ts->clk_regs + reg);
 }
 
+/**
+ * ccroc_writel() - writes a value to a CCROC register
+ * @ts: pointer to a struct tegra_soctherm
+ * @v: the value to write
+ * @reg: the register offset
+ *
+ * Writes @v to @reg.  No return value.
+ */
+static inline void ccroc_writel(struct tegra_soctherm *ts, u32 value, u32 reg)
+{
+       writel(value, (ts->ccroc_regs + reg));
+}
+
+/**
+ * ccroc_readl() - reads specified register from CCROC IP block
+ * @ts: pointer to a struct tegra_soctherm
+ * @reg: register address to be read
+ *
+ * Return: the value of the register
+ */
+static inline u32 ccroc_readl(struct tegra_soctherm *ts, u32 reg)
+{
+       return readl(ts->ccroc_regs + reg);
+}
+
 static void enable_tsensor(struct tegra_soctherm *tegra, unsigned int i)
 {
        const struct tegra_tsensor *sensor = &tegra->soc->tsensors[i];
@@ -552,9 +601,6 @@ static int tegra_soctherm_set_hwtrips(struct device *dev,
                 sg->name, temperature);
 
 set_throttle:
-       if (ts->soc->use_ccroc)
-               return 0;
-
        ret = get_hot_temp(tz, &trip, &temperature);
        if (ret) {
                dev_warn(dev, "throttrip: %s: missing hot temperature\n",
@@ -676,9 +722,6 @@ static int regs_show(struct seq_file *s, void *data)
        state = REG_GET_MASK(r, SENSOR_TEMP2_MEM_TEMP_MASK);
        seq_printf(s, " MEM(%d)\n", translate_temp(state));
 
-       if (ts->soc->use_ccroc)
-               return 0;
-
        for (i = 0; i < ts->soc->num_ttgs; i++) {
                seq_printf(s, "%s:\n", ttgs[i]->name);
                for (level = 0; level < 4; level++) {
@@ -779,12 +822,17 @@ static int regs_show(struct seq_file *s, void *data)
        seq_printf(s, "enabled(%d)\n", state);
 
        r = readl(ts->regs + CPU_PSKIP_STATUS);
-       state = REG_GET_MASK(r, XPU_PSKIP_STATUS_M_MASK);
-       seq_printf(s, "CPU PSKIP STATUS: M(%d) ", state);
-       state = REG_GET_MASK(r, XPU_PSKIP_STATUS_N_MASK);
-       seq_printf(s, "N(%d) ", state);
-       state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK);
-       seq_printf(s, "enabled(%d)\n", state);
+       if (ts->soc->use_ccroc) {
+               state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK);
+               seq_printf(s, "CPU PSKIP STATUS: enabled(%d)\n", state);
+       } else {
+               state = REG_GET_MASK(r, XPU_PSKIP_STATUS_M_MASK);
+               seq_printf(s, "CPU PSKIP STATUS: M(%d) ", state);
+               state = REG_GET_MASK(r, XPU_PSKIP_STATUS_N_MASK);
+               seq_printf(s, "N(%d) ", state);
+               state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK);
+               seq_printf(s, "enabled(%d)\n", state);
+       }
 
        return 0;
 }
@@ -939,15 +987,29 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev)
                }
                stc->priority = val;
 
-               r = of_property_read_u32(np_stcc, "nvidia,cpu-throt-percent",
-                                        &val);
-               if (r) {
-                       dev_info(dev,
-                                "throttle-cfg: %s: missing cpu-throt-percent\n",
-                                name);
-                       continue;
+               if (ts->soc->use_ccroc) {
+                       r = of_property_read_u32(np_stcc,
+                                                "nvidia,cpu-throt-level",
+                                                &val);
+                       if (r) {
+                               dev_info(dev,
+                                        "throttle-cfg: %s: missing cpu-throt-level\n",
+                                        name);
+                               continue;
+                       }
+                       stc->cpu_throt_level = val;
+               } else {
+                       r = of_property_read_u32(np_stcc,
+                                                "nvidia,cpu-throt-percent",
+                                                &val);
+                       if (r) {
+                               dev_info(dev,
+                                        "throttle-cfg: %s: missing cpu-throt-percent\n",
+                                        name);
+                               continue;
+                       }
+                       stc->cpu_throt_depth = val;
                }
-               stc->cpu_throt_depth = val;
 
                tcd = thermal_of_cooling_device_register(np_stcc,
                                                         (char *)name, ts,
@@ -967,6 +1029,96 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev)
        of_node_put(np_stc);
 }
 
+/**
+ * throttlectl_cpu_level_cfg() - programs CCROC NV_THERM level config
+ * @level: describing the level LOW/MED/HIGH of throttling
+ *
+ * It's necessary to set up the CPU-local CCROC NV_THERM instance with
+ * the M/N values desired for each level. This function does this.
+ *
+ * This function pre-programs the CCROC NV_THERM levels in terms of
+ * pre-configured "Low", "Medium" or "Heavy" throttle levels which are
+ * mapped to THROT_LEVEL_LOW, THROT_LEVEL_MED and THROT_LEVEL_HVY.
+ */
+static void throttlectl_cpu_level_cfg(struct tegra_soctherm *ts, int level)
+{
+       u8 depth, dividend;
+       u32 r;
+
+       switch (level) {
+       case TEGRA_SOCTHERM_THROT_LEVEL_LOW:
+               depth = 50;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_MED:
+               depth = 75;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_HIGH:
+               depth = 80;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_NONE:
+               return;
+       default:
+               return;
+       }
+
+       dividend = THROT_DEPTH_DIVIDEND(depth);
+
+       /* setup PSKIP in ccroc nv_therm registers */
+       r = ccroc_readl(ts, CCROC_THROT_PSKIP_RAMP_CPU_REG(level));
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_RAMP_DURATION_MASK, 0xff);
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_RAMP_STEP_MASK, 0xf);
+       ccroc_writel(ts, r, CCROC_THROT_PSKIP_RAMP_CPU_REG(level));
+
+       r = ccroc_readl(ts, CCROC_THROT_PSKIP_CTRL_CPU_REG(level));
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_ENB_MASK, 1);
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_DIVIDEND_MASK, dividend);
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_DIVISOR_MASK, 0xff);
+       ccroc_writel(ts, r, CCROC_THROT_PSKIP_CTRL_CPU_REG(level));
+}
+
+/**
+ * throttlectl_cpu_level_select() - program CPU pulse skipper config
+ * @throt: the LIGHT/HEAVY of throttle event id
+ *
+ * Pulse skippers are used to throttle clock frequencies.  This
+ * function programs the pulse skippers based on @throt and platform
+ * data.  This function is used on SoCs which have CPU-local pulse
+ * skipper control, such as T13x. It programs soctherm's interface to
+ * Denver:CCROC NV_THERM in terms of Low, Medium and HIGH throttling
+ * vectors. PSKIP_BYPASS mode is set as required per HW spec.
+ */
+static void throttlectl_cpu_level_select(struct tegra_soctherm *ts,
+                                        enum soctherm_throttle_id throt)
+{
+       u32 r, throt_vect;
+
+       /* Denver:CCROC NV_THERM interface N:3 Mapping */
+       switch (ts->throt_cfgs[throt].cpu_throt_level) {
+       case TEGRA_SOCTHERM_THROT_LEVEL_LOW:
+               throt_vect = THROT_VECT_LOW;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_MED:
+               throt_vect = THROT_VECT_MED;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_HIGH:
+               throt_vect = THROT_VECT_HIGH;
+               break;
+       default:
+               throt_vect = THROT_VECT_NONE;
+               break;
+       }
+
+       r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU));
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1);
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT_CPU_MASK, throt_vect);
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT2_CPU_MASK, throt_vect);
+       writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU));
+
+       /* bypass sequencer in soc_therm as it is programmed in ccroc */
+       r = REG_SET_MASK(0, THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK, 1);
+       writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU));
+}
+
 /**
  * throttlectl_cpu_mn() - program CPU pulse skipper configuration
  * @throt: the LIGHT/HEAVY of throttle event id
@@ -1017,7 +1169,10 @@ static void soctherm_throttle_program(struct tegra_soctherm *ts,
                return;
 
        /* Setup PSKIP parameters */
-       throttlectl_cpu_mn(ts, throt);
+       if (ts->soc->use_ccroc)
+               throttlectl_cpu_level_select(ts, throt);
+       else
+               throttlectl_cpu_mn(ts, throt);
 
        r = REG_SET_MASK(0, THROT_PRIORITY_LITE_PRIO_MASK, stc.priority);
        writel(r, ts->regs + THROT_PRIORITY_CTRL(throt));
@@ -1040,16 +1195,31 @@ static void tegra_soctherm_throttle(struct device *dev)
        u32 v;
        int i;
 
+       /* configure LOW, MED and HIGH levels for CCROC NV_THERM */
+       if (ts->soc->use_ccroc) {
+               throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_LOW);
+               throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_MED);
+               throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_HIGH);
+       }
+
        /* Thermal HW throttle programming */
        for (i = 0; i < THROTTLE_SIZE; i++)
                soctherm_throttle_program(ts, i);
 
        v = REG_SET_MASK(0, THROT_GLOBAL_ENB_MASK, 1);
-       writel(v, ts->regs + THROT_GLOBAL_CFG);
+       if (ts->soc->use_ccroc) {
+               ccroc_writel(ts, v, CCROC_GLOBAL_CFG);
 
-       v = clk_readl(ts, CAR_SUPER_CCLKG_DIVIDER);
-       v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1);
-       clk_writel(ts, v, CAR_SUPER_CCLKG_DIVIDER);
+               v = ccroc_readl(ts, CCROC_SUPER_CCLKG_DIVIDER);
+               v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1);
+               ccroc_writel(ts, v, CCROC_SUPER_CCLKG_DIVIDER);
+       } else {
+               writel(v, ts->regs + THROT_GLOBAL_CFG);
+
+               v = clk_readl(ts, CAR_SUPER_CCLKG_DIVIDER);
+               v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1);
+               clk_writel(ts, v, CAR_SUPER_CCLKG_DIVIDER);
+       }
 
        /* initialize stats collection */
        v = STATS_CTL_CLR_DN | STATS_CTL_EN_DN |
@@ -1084,9 +1254,6 @@ static void soctherm_init(struct platform_device *pdev)
        writel(pdiv, tegra->regs + SENSOR_PDIV);
        writel(hotspot, tegra->regs + SENSOR_HOTSPOT_OFF);
 
-       if (tegra->soc->use_ccroc)
-               return;
-
        /* Configure hw throttle */
        tegra_soctherm_throttle(&pdev->dev);
 }
@@ -1157,6 +1324,14 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev, "can't get car clk registers");
                        return PTR_ERR(tegra->clk_regs);
                }
+       } else {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  "ccroc-reg");
+               tegra->ccroc_regs = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(tegra->ccroc_regs)) {
+                       dev_err(&pdev->dev, "can't get ccroc registers");
+                       return PTR_ERR(tegra->ccroc_regs);
+               }
        }
 
        tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm");
@@ -1207,8 +1382,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
        if (err)
                return err;
 
-       if (!tegra->soc->use_ccroc)
-               soctherm_init_hw_throt_cdev(pdev);
+       soctherm_init_hw_throt_cdev(pdev);
 
        soctherm_init(pdev);
 
index ae5d61af2df39ef6de7ac70b9236556237270750..97fa30501eb19eeec32c05c7365e4f7ab9a8af3f 100644 (file)
 #define TEGRA132_THERMTRIP_CPU_THRESH_MASK     (0xff << 8)
 #define TEGRA132_THERMTRIP_TSENSE_THRESH_MASK  0xff
 
+#define TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK  (0xff << 17)
+#define TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK  (0xff << 9)
+
 #define TEGRA132_THRESH_GRAIN                  1000
+#define TEGRA132_BPTT                          8
 
 static const struct tegra_tsensor_configuration tegra132_tsensor_config = {
        .tall = 16300,
@@ -51,6 +55,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_cpu = {
        .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA132_THERMTRIP_CPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA132_THERMTRIP_CPU_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra132_tsensor_group_gpu = {
@@ -66,6 +73,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_gpu = {
        .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA132_THERMTRIP_GPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra132_tsensor_group_pll = {
@@ -79,6 +89,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_pll = {
        .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA132_THERMTRIP_TSENSE_EN_MASK,
        .thermtrip_threshold_mask = TEGRA132_THERMTRIP_TSENSE_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
+       .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra132_tsensor_group_mem = {
@@ -94,6 +107,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_mem = {
        .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA132_THERMTRIP_MEM_EN_MASK,
        .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
+       .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group *tegra132_tsensor_groups[] = {
@@ -193,5 +209,6 @@ const struct tegra_soctherm_soc tegra132_soctherm = {
        .num_ttgs = ARRAY_SIZE(tegra132_tsensor_groups),
        .tfuse = &tegra132_soctherm_fuse,
        .thresh_grain = TEGRA132_THRESH_GRAIN,
+       .bptt = TEGRA132_BPTT,
        .use_ccroc = true,
 };
index 729ab9fc325e2a5d728d929fdc8b3332d4867eef..2a99f1d52bb5e778648ebb0882bc6fb1d9f4eb2c 100644 (file)
@@ -11,4 +11,9 @@
 #define TEGRA124_SOCTHERM_SENSOR_PLLX 3
 #define TEGRA124_SOCTHERM_SENSOR_NUM 4
 
+#define TEGRA_SOCTHERM_THROT_LEVEL_LOW  0
+#define TEGRA_SOCTHERM_THROT_LEVEL_MED  1
+#define TEGRA_SOCTHERM_THROT_LEVEL_HIGH 2
+#define TEGRA_SOCTHERM_THROT_LEVEL_NONE -1
+
 #endif