arm: tegra: soctherm: Support configuring OC alarms
Diwakar Tundlam [Tue, 19 Mar 2013 07:23:54 +0000 (00:23 -0700)]
Added support to program throttling when OC alarms are triggered.
Support OC1-OC5 with CPU+GPU throttling with preset throttle depths.
Disable suspend cooling device when any OC alarm is configured.

Enhanced regs debug node to display more information about OC alarm
configuration and status.

Enable OC2 and OC4 handling for Dalmore and Pluto.
See Bug 1216535 for testing info to trigger throttling with OC4.

Bug 1206300
Bug 1216535

Change-Id: I32eb161ce34ae52725e81c1852d5b00a0b88314a
Signed-off-by: Diwakar Tundlam <dtundlam@nvidia.com>
Reviewed-on: http://git-master/r/211013
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit

arch/arm/mach-tegra/board-dalmore-power.c
arch/arm/mach-tegra/board-pluto-power.c
arch/arm/mach-tegra/tegra11_soctherm.c
arch/arm/mach-tegra/tegra11_soctherm.h

index cb0299c..ad6ab97 100644 (file)
@@ -1369,9 +1369,40 @@ static struct soctherm_platform_data dalmore_soctherm_data = {
        },
        .throttle = {
                [THROTTLE_HEAVY] = {
+                       .priority = 100,
                        .devs = {
                                [THROTTLE_DEV_CPU] = {
-                                       .enable = 1,
+                                       .enable = true,
+                                       .depth = 80,
+                               },
+                       },
+               },
+               [THROTTLE_OC2] = {
+                       .throt_mode = BRIEF,
+                       .polarity = 0,
+                       .devs = {
+                               [THROTTLE_DEV_CPU] = {
+                                       .enable = true,
+                                       .depth = 50,
+                               },
+                               [THROTTLE_DEV_GPU] = {
+                                       .enable = true,
+                                       .depth = 50,
+                               },
+                       },
+               },
+               [THROTTLE_OC4] = {
+                       .throt_mode = BRIEF,
+                       .polarity = 1,
+                       .intr = true,
+                       .devs = {
+                               [THROTTLE_DEV_CPU] = {
+                                       .enable = true,
+                                       .depth = 50,
+                               },
+                               [THROTTLE_DEV_GPU] = {
+                                       .enable = true,
+                                       .depth = 50,
                                },
                        },
                },
index 8f40bde..8b89032 100644 (file)
@@ -816,9 +816,40 @@ static struct soctherm_platform_data pluto_soctherm_data = {
        },
        .throttle = {
                [THROTTLE_HEAVY] = {
+                       .priority = 100,
                        .devs = {
                                [THROTTLE_DEV_CPU] = {
-                                       .enable = 1,
+                                       .enable = true,
+                                       .depth = 80,
+                               },
+                       },
+               },
+               [THROTTLE_OC2] = {
+                       .throt_mode = BRIEF,
+                       .polarity = 0,
+                       .devs = {
+                               [THROTTLE_DEV_CPU] = {
+                                       .enable = true,
+                                       .depth = 50,
+                               },
+                               [THROTTLE_DEV_GPU] = {
+                                       .enable = true,
+                                       .depth = 50,
+                               },
+                       },
+               },
+               [THROTTLE_OC4] = {
+                       .throt_mode = BRIEF,
+                       .polarity = 1,
+                       .intr = true,
+                       .devs = {
+                               [THROTTLE_DEV_CPU] = {
+                                       .enable = true,
+                                       .depth = 50,
+                               },
+                               [THROTTLE_DEV_GPU] = {
+                                       .enable = true,
+                                       .depth = 50,
                                },
                        },
                },
index e102fac..1896fbb 100644 (file)
@@ -250,6 +250,45 @@ static const int precision; /* default 0 -> low precision */
 
 #define THROT_GLOBAL_CFG       0x400
 
+#define OC1_CFG                                0x310
+#define OC1_CFG_LONG_LATENCY_SHIFT     6
+#define OC1_CFG_LONG_LATENCY_MASK      0x1
+#define OC1_CFG_HW_RESTORE_SHIFT       5
+#define OC1_CFG_HW_RESTORE_MASK                0x1
+#define OC1_CFG_PWR_GOOD_MASK_SHIFT    4
+#define OC1_CFG_PWR_GOOD_MASK_MASK     0x1
+#define OC1_CFG_THROTTLE_MODE_SHIFT    2
+#define OC1_CFG_THROTTLE_MODE_MASK     0x3
+#define OC1_CFG_ALARM_POLARITY_SHIFT   1
+#define OC1_CFG_ALARM_POLARITY_MASK    0x1
+#define OC1_CFG_EN_THROTTLE_SHIFT      0
+#define OC1_CFG_EN_THROTTLE_MASK       0x1
+
+#define OC1_CNT_THRESHOLD              0x314
+#define OC1_THRESHOLD_PERIOD           0x318
+#define OC1_ALARM_COUNT                        0x31c
+#define OC1_FILTER                     0x320
+
+#define OC1_STATS                      0x3a8
+
+#define OC_INTR_STATUS                 0x39c
+#define OC_INTR_ENABLE                 0x3a0
+#define OC_INTR_DISABLE                        0x3a4
+#define OC_INTR_POS_OC1_SHIFT          0
+#define OC_INTR_POS_OC1_MASK           0x1
+#define OC_INTR_POS_OC2_SHIFT          1
+#define OC_INTR_POS_OC2_MASK           0x1
+#define OC_INTR_POS_OC3_SHIFT          2
+#define OC_INTR_POS_OC3_MASK           0x1
+#define OC_INTR_POS_OC4_SHIFT          3
+#define OC_INTR_POS_OC4_MASK           0x1
+#define OC_INTR_POS_OC5_SHIFT          4
+#define OC_INTR_POS_OC5_MASK           0x1
+
+#define OC_STATS_CTL                   0x3c4
+#define OC_STATS_CTL_CLR_ALL           0x2
+#define OC_STATS_CTL_EN_ALL            0x1
+
 #define CPU_PSKIP_STATUS                       0x418
 #define CPU_PSKIP_STATUS_M_SHIFT               12
 #define CPU_PSKIP_STATUS_M_MASK                        0xff
@@ -359,6 +398,24 @@ static const int precision; /* default 0 -> low precision */
                                                (THROT_OFFSET * throt))
 #define THROT_DELAY_CTRL(throt)                        (THROT_DELAY_LITE + \
                                                (THROT_OFFSET * throt))
+#define ALARM_CFG(throt)                       (OC1_CFG + \
+                                               (ALARM_OFFSET * (throt - \
+                                                               THROTTLE_OC1)))
+#define ALARM_CNT_THRESHOLD(throt)             (OC1_CNT_THRESHOLD + \
+                                               (ALARM_OFFSET * (throt - \
+                                                               THROTTLE_OC1)))
+#define ALARM_THRESHOLD_PERIOD(throt)          (OC1_THRESHOLD_PERIOD + \
+                                               (ALARM_OFFSET * (throt - \
+                                                               THROTTLE_OC1)))
+#define ALARM_ALARM_COUNT(throt)               (OC1_ALARM_COUNT + \
+                                               (ALARM_OFFSET * (throt - \
+                                                               THROTTLE_OC1)))
+#define ALARM_FILTER(throt)                    (OC1_FILTER + \
+                                               (ALARM_OFFSET * (throt - \
+                                                               THROTTLE_OC1)))
+#define ALARM_STATS(throt)                     (OC1_STATS + \
+                                               (4 * (throt - THROTTLE_OC1)))
+
 #define THROT_DEPTH_DIVIDEND(depth)    ((256 * (100 - (depth)) / 100) - 1)
 #define THROT_DEPTH_DEFAULT            (80)
 #define THROT_DEPTH(th, dp)            {                       \
@@ -427,6 +484,7 @@ static struct thermal_zone_device *thz[THERM_SIZE];
 #endif
 static struct workqueue_struct *workqueue;
 static struct work_struct work_thermal;
+static struct work_struct work_edp;
 
 static u32 fuse_calib_base_cp;
 static u32 fuse_calib_base_ft;
@@ -443,6 +501,11 @@ static const char *const therm_names[] = {
 static const char *const throt_names[] = {
        [THROTTLE_LIGHT]   = "light",
        [THROTTLE_HEAVY]   = "heavy",
+       [THROTTLE_OC1]     = "oc1",
+       [THROTTLE_OC2]     = "oc2",
+       [THROTTLE_OC3]     = "oc3",
+       [THROTTLE_OC4]     = "oc4",
+       [THROTTLE_OC5]     = "oc5",
 };
 
 static const char *const throt_dev_names[] = {
@@ -1012,6 +1075,7 @@ static int __init soctherm_thermal_sys_init(void)
 {
        char name[THERMAL_NAME_LENGTH];
        struct soctherm_therm *therm;
+       bool oc_en = false;
        int i, j, k;
 
        if (!soctherm_init_platform_done)
@@ -1062,7 +1126,27 @@ static int __init soctherm_thermal_sys_init(void)
                                            (strnstr(therm->trips[j].cdev_type,
                                                     "light",
                                                     THERMAL_NAME_LENGTH)
-                                            && k == THROTTLE_HEAVY))
+                                            && k == THROTTLE_HEAVY) ||
+                                           (strnstr(therm->trips[j].cdev_type,
+                                                    "oc1",
+                                                    THERMAL_NAME_LENGTH)
+                                            && k == THROTTLE_OC1) ||
+                                           (strnstr(therm->trips[j].cdev_type,
+                                                    "oc2",
+                                                    THERMAL_NAME_LENGTH)
+                                            && k == THROTTLE_OC2) ||
+                                           (strnstr(therm->trips[j].cdev_type,
+                                                    "oc3",
+                                                    THERMAL_NAME_LENGTH)
+                                            && k == THROTTLE_OC3) ||
+                                           (strnstr(therm->trips[j].cdev_type,
+                                                    "oc4",
+                                                    THERMAL_NAME_LENGTH)
+                                            && k == THROTTLE_OC4) ||
+                                           (strnstr(therm->trips[j].cdev_type,
+                                                    "oc5",
+                                                    THERMAL_NAME_LENGTH)
+                                            && k == THROTTLE_OC5))
                                                continue;
 
                                        thermal_cooling_device_register(
@@ -1089,10 +1173,19 @@ static int __init soctherm_thermal_sys_init(void)
                                        therm->tzp,
                                        therm->passive_delay,
                                        0);
+
+               for (k = THROTTLE_OC1; !oc_en && k < THROTTLE_SIZE; k++)
+                       if (plat_data.throttle[k].devs[therm2dev[i]].enable)
+                               oc_en = true;
        }
 
-       thermal_cooling_device_register("suspend_soctherm",
-                                       0, &soctherm_suspend_ops);
+       /* do not enable suspend feature if any OC alarms are enabled */
+       if (!oc_en)
+               thermal_cooling_device_register("suspend_soctherm", 0,
+                                               &soctherm_suspend_ops);
+       else
+               pr_warn("soctherm: Suspend feature CANNOT be enabled %s\n",
+                       "when any OC alarm is enabled");
 
        soctherm_update();
        return 0;
@@ -1175,6 +1268,121 @@ static void soctherm_work_func(struct work_struct *work)
        }
 }
 
+static inline void soctherm_oc_intr_enable(enum soctherm_throttle_id alarm,
+                                          bool enable)
+{
+       u32 r;
+
+       if (!enable)
+               return;
+
+       switch (alarm) {
+       case THROTTLE_OC1:
+               r = REG_SET(0, OC_INTR_POS_OC1, 1);
+               break;
+       case THROTTLE_OC2:
+               r = REG_SET(0, OC_INTR_POS_OC2, 1);
+               break;
+       case THROTTLE_OC3:
+               r = REG_SET(0, OC_INTR_POS_OC3, 1);
+               break;
+       case THROTTLE_OC4:
+               r = REG_SET(0, OC_INTR_POS_OC4, 1);
+               break;
+       case THROTTLE_OC5:
+               r = REG_SET(0, OC_INTR_POS_OC5, 1);
+               break;
+       default:
+               r = 0;
+               break;
+       }
+       soctherm_writel(r, OC_INTR_ENABLE);
+}
+
+/* Return 0 (success) if you want to  reenable OC alarm intr. */
+static int soctherm_handle_alarm(enum soctherm_throttle_id alarm)
+{
+       int rv = -EINVAL;
+
+       switch (alarm) {
+       case THROTTLE_OC1:
+               pr_warn("soctherm: Unexpected OC1 alarm\n");
+               /* add OC1 alarm handling code here */
+               break;
+
+       case THROTTLE_OC2:
+               pr_info("soctherm: Successfully handled OC2 alarm\n");
+               /* TODO: add OC2 alarm handling code here */
+               rv = 0;
+               break;
+
+       case THROTTLE_OC3:
+               pr_warn("soctherm: Unexpected OC3 alarm\n");
+               /* add OC3 alarm handling code here */
+               break;
+
+       case THROTTLE_OC4:
+               pr_info("soctherm: Successfully handled OC4 alarm\n");
+               /* TODO: add OC4 alarm handling code here */
+               rv = 0;
+
+               break;
+       case THROTTLE_OC5:
+               pr_warn("soctherm: Unexpected OC5 alarm\n");
+               /* add OC5 alarm handling code here */
+               break;
+
+       default:
+               break;
+       }
+
+       if (rv)
+               pr_err("soctherm: ERROR in handling %s alarm\n",
+                       throt_names[alarm]);
+
+       return rv;
+}
+
+static void soctherm_edp_work_func(struct work_struct *work)
+{
+       u32 st, ex, oc1, oc2, oc3, oc4, oc5;
+
+       st = soctherm_readl(OC_INTR_STATUS);
+
+       /* deliberately clear expected interrupts handled in SW */
+       oc1 = REG_GET_BIT(st, OC_INTR_POS_OC1);
+       oc2 = REG_GET_BIT(st, OC_INTR_POS_OC2);
+       oc3 = REG_GET_BIT(st, OC_INTR_POS_OC3);
+       oc4 = REG_GET_BIT(st, OC_INTR_POS_OC4);
+       oc5 = REG_GET_BIT(st, OC_INTR_POS_OC5);
+       ex = oc1 | oc2 | oc3 | oc4 | oc5;
+
+       if (ex) {
+               soctherm_writel(st, OC_INTR_STATUS);
+               st &= ~ex;
+
+               if (oc1 && !soctherm_handle_alarm(THROTTLE_OC1))
+                       soctherm_oc_intr_enable(THROTTLE_OC1, true);
+
+               if (oc2 && !soctherm_handle_alarm(THROTTLE_OC2))
+                       soctherm_oc_intr_enable(THROTTLE_OC2, true);
+
+               if (oc3 && !soctherm_handle_alarm(THROTTLE_OC3))
+                       soctherm_oc_intr_enable(THROTTLE_OC3, true);
+
+               if (oc4 && !soctherm_handle_alarm(THROTTLE_OC4))
+                       soctherm_oc_intr_enable(THROTTLE_OC4, true);
+
+               if (oc5 && !soctherm_handle_alarm(THROTTLE_OC5))
+                       soctherm_oc_intr_enable(THROTTLE_OC5, true);
+       }
+
+       if (st) {
+               pr_err("soctherm: Ignored unexpected OC ALARM 0x%08x\n", st);
+               soctherm_writel(st, OC_INTR_STATUS);
+       }
+}
+
 static irqreturn_t soctherm_isr(int irq, void *arg_data)
 {
        u32 r;
@@ -1187,6 +1395,18 @@ static irqreturn_t soctherm_isr(int irq, void *arg_data)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t soctherm_edp_isr(int irq, void *arg_data)
+{
+       u32 r;
+
+       queue_work(workqueue, &work_edp);
+
+       r = soctherm_readl(OC_INTR_STATUS);
+       soctherm_writel(r, OC_INTR_DISABLE);
+
+       return IRQ_HANDLED;
+}
+
 static void tegra11_soctherm_throttle_program(enum soctherm_throttle_id throt)
 {
        u32 r;
@@ -1231,6 +1451,27 @@ static void tegra11_soctherm_throttle_program(enum soctherm_throttle_id throt)
                r = REG_SET(0, THROT_PRIORITY_LOCK_PRIORITY, data->priority);
                soctherm_writel(r, THROT_PRIORITY_LOCK);
        }
+
+       if (!throt_enable || (throt < THROTTLE_OC1))
+               return;
+
+       /* ----- configure OC alarms ----- */
+
+       if (!(data->throt_mode == BRIEF || data->throt_mode == STICKY))
+               pr_warn("soctherm: Invalid throt_mode in %s\n",
+                       throt_names[throt]);
+
+       r = soctherm_readl(ALARM_CFG(throt));
+       r = REG_SET(r, OC1_CFG_HW_RESTORE, 1);
+       r = REG_SET(r, OC1_CFG_THROTTLE_MODE, data->throt_mode);
+       r = REG_SET(r, OC1_CFG_ALARM_POLARITY, data->polarity);
+       r = REG_SET(r, OC1_CFG_EN_THROTTLE, 1);
+       soctherm_writel(r, ALARM_CFG(throt));
+
+       soctherm_oc_intr_enable(throt, data->intr);
+
+       soctherm_writel(data->period, ALARM_THRESHOLD_PERIOD(throt)); /* usec */
+       soctherm_writel(0xffffffff, ALARM_FILTER(throt));
 }
 
 static void soctherm_tsense_program(enum soctherm_sense sensor,
@@ -1526,6 +1767,7 @@ static int soctherm_init_platform_data(void)
        r = STATS_CTL_CLR_DN | STATS_CTL_EN_DN |
                STATS_CTL_CLR_UP | STATS_CTL_EN_UP;
        soctherm_writel(r, STATS_CTL);
+       soctherm_writel(OC_STATS_CTL_EN_ALL, OC_STATS_CTL);
 
        /* Enable PMC to shutdown */
        soctherm_therm_trip_init(plat_data.tshut_pmu_trip_data);
@@ -1554,8 +1796,11 @@ static int soctherm_suspend(void)
 
        if (!soctherm_suspended) {
                soctherm_writel((u32)-1, TH_INTR_DISABLE);
+               soctherm_writel((u32)-1, OC_INTR_DISABLE);
                disable_irq(INT_THERMAL);
+               disable_irq(INT_EDP);
                cancel_work_sync(&work_thermal);
+               cancel_work_sync(&work_edp);
                soctherm_clk_enable(false);
                soctherm_init_platform_done = false;
                soctherm_suspended = true;
@@ -1576,6 +1821,7 @@ static int soctherm_resume(void)
                soctherm_init_platform_done = true;
                soctherm_update();
                enable_irq(INT_THERMAL);
+               enable_irq(INT_EDP);
        }
 
        spin_unlock(&soctherm_suspend_resume_lock);
@@ -1624,13 +1870,18 @@ int __init tegra11_soctherm_init(struct soctherm_platform_data *data)
 
        /* enable interrupts */
        workqueue = create_singlethread_workqueue("soctherm-wq");
-       INIT_WORK(&work_thermal, soctherm_work_func);
 
+       INIT_WORK(&work_thermal, soctherm_work_func);
        err = request_irq(INT_THERMAL, soctherm_isr, 0,
                          "soctherm_thermal", NULL);
        if (err < 0)
                return -1;
 
+       INIT_WORK(&work_edp, soctherm_edp_work_func);
+       err = request_irq(INT_EDP, soctherm_edp_isr, 0, "soctherm_edp", NULL);
+       if (err < 0)
+               return -1;
+
        return 0;
 }
 
@@ -1831,6 +2082,33 @@ static int regs_show(struct seq_file *s, void *data)
                        r = soctherm_readl(THROT_DELAY_CTRL(i));
                        state = REG_GET(r, THROT_DELAY_LITE_DELAY);
                        seq_printf(s, "%5d  ", state);
+
+                       if (i >= THROTTLE_OC1) {
+                               r = soctherm_readl(ALARM_CFG(i));
+                               state = REG_GET(r, OC1_CFG_LONG_LATENCY);
+                               seq_printf(s, "%2d  ", state);
+                               state = REG_GET(r, OC1_CFG_HW_RESTORE);
+                               seq_printf(s, "%2d  ", state);
+                               state = REG_GET(r, OC1_CFG_PWR_GOOD_MASK);
+                               seq_printf(s, "%2d  ", state);
+                               state = REG_GET(r, OC1_CFG_THROTTLE_MODE);
+                               seq_printf(s, "%2d  ", state);
+                               state = REG_GET(r, OC1_CFG_ALARM_POLARITY);
+                               seq_printf(s, "%2d  ", state);
+                               state = REG_GET(r, OC1_CFG_EN_THROTTLE);
+                               seq_printf(s, "%2d  ", state);
+
+                               r = soctherm_readl(ALARM_CNT_THRESHOLD(i));
+                               seq_printf(s, "%8d  ", r);
+                               r = soctherm_readl(ALARM_THRESHOLD_PERIOD(i));
+                               seq_printf(s, "%8d  ", r);
+                               r = soctherm_readl(ALARM_ALARM_COUNT(i));
+                               seq_printf(s, "%8d  ", r);
+                               r = soctherm_readl(ALARM_FILTER(i));
+                               seq_printf(s, "%8d  ", r);
+                               r = soctherm_readl(ALARM_STATS(i));
+                               seq_printf(s, "%8d  ", r);
+                       }
                        seq_printf(s, "\n");
                }
        }
index 2022671..306aa83 100644 (file)
@@ -44,6 +44,11 @@ enum soctherm_therm_id {
 enum soctherm_throttle_id {
        THROTTLE_LIGHT = 0,
        THROTTLE_HEAVY,
+       THROTTLE_OC1,
+       THROTTLE_OC2,
+       THROTTLE_OC3,
+       THROTTLE_OC4,
+       THROTTLE_OC5,
        THROTTLE_SIZE,
 };
 
@@ -81,8 +86,19 @@ struct soctherm_throttle_dev {
        u8 step;
 };
 
+enum throt_mode {
+       DISABLED = 0,
+       STICKY,
+       BRIEF,
+       RESERVED,
+};
+
 struct soctherm_throttle {
+       u8 throt_mode;
+       u8 polarity;
        u8 priority;
+       u8 period;
+       bool intr;
        struct soctherm_throttle_dev devs[THROTTLE_DEV_SIZE];
 };