WARN(1, "%s: thermal dvfs is not supported\n", rail->reg_id);
}
+ /* Thermal clock switch is only supported for CPU */
+ if (rail->clk_switch_cdev && (rail != tegra_cpu_rail)) {
+ rail->clk_switch_cdev = NULL;
+ WARN(1, "%s: thermal clock switch is not supported\n",
+ rail->reg_id);
+ }
+
if (!rail->simon_vmin_offsets != !rail->simon_vmin_offs_num) {
rail->simon_vmin_offs_num = 0;
rail->simon_vmin_offsets = NULL;
return NULL;
}
+struct tegra_cooling_device *tegra_dvfs_get_cpu_clk_switch_cdev(void)
+{
+ if (tegra_cpu_rail)
+ return tegra_cpu_rail->clk_switch_cdev;
+ return NULL;
+}
+
static void make_safe_thermal_dvfs(struct dvfs_rail *rail)
{
struct dvfs *d;
}
}
+/* Cooling device to switch the cpu clock source between PLLX and DFLL */
+static int tegra_dvfs_rail_get_clk_switch_cdev_max_state(
+ struct thermal_cooling_device *cdev, unsigned long *max_state)
+{
+ struct dvfs_rail *rail = (struct dvfs_rail *)cdev->devdata;
+ *max_state = rail->clk_switch_cdev->trip_temperatures_num;
+ return 0;
+}
+
+static int tegra_dvfs_rail_get_clk_switch_cdev_cur_state(
+ struct thermal_cooling_device *cdev, unsigned long *cur_state)
+{
+ struct dvfs_rail *rail = (struct dvfs_rail *)cdev->devdata;
+ *cur_state = rail->therm_scale_idx;
+ return 0;
+}
+
+static int tegra_dvfs_rail_set_clk_switch_cdev_state(
+ struct thermal_cooling_device *cdev, unsigned long cur_state)
+{
+ int ret = 0;
+ enum dfll_range use_dfll;
+ struct dvfs_rail *rail = (struct dvfs_rail *)cdev->devdata;
+
+ if (CONFIG_TEGRA_USE_DFLL_RANGE == TEGRA_USE_DFLL_CDEV_CNTRL) {
+ if (rail->therm_scale_idx != cur_state) {
+ rail->therm_scale_idx = cur_state;
+ if (rail->therm_scale_idx == 0)
+ use_dfll = DFLL_RANGE_NONE;
+ else
+ use_dfll = DFLL_RANGE_ALL_RATES;
+
+ ret = tegra_clk_dfll_range_control(use_dfll);
+ }
+ } else {
+ pr_warn("\n%s: Not Allowed:", __func__);
+ pr_warn("DFLL is not under thermal cooling device control\n");
+ return -EACCES;
+ }
+ return ret;
+}
+
+static struct thermal_cooling_device_ops tegra_dvfs_clk_cooling_ops = {
+ .get_max_state = tegra_dvfs_rail_get_clk_switch_cdev_max_state,
+ .get_cur_state = tegra_dvfs_rail_get_clk_switch_cdev_cur_state,
+ .set_cur_state = tegra_dvfs_rail_set_clk_switch_cdev_state,
+};
+
+static void tegra_dvfs_rail_register_clk_switch_cdev(struct dvfs_rail *rail)
+{
+ struct thermal_cooling_device *dev;
+
+ if (!rail->clk_switch_cdev)
+ return;
+
+ dev = thermal_cooling_device_register(rail->clk_switch_cdev->cdev_type,
+ (void *)rail, &tegra_dvfs_clk_cooling_ops);
+ /* report error & set max limits across thermal ranges as safe dvfs */
+ if (IS_ERR_OR_NULL(dev) || list_empty(&dev->thermal_instances)) {
+ pr_err("tegra cooling device %s failed to register\n",
+ rail->clk_switch_cdev->cdev_type);
+ make_safe_thermal_dvfs(rail);
+ }
+}
+
+
/* Cooling device to scale voltage with temperature in pll mode */
static int tegra_dvfs_rail_get_vts_cdev_max_state(
struct thermal_cooling_device *cdev, unsigned long *max_state)
}
}
+int __init tegra_dvfs_rail_init_clk_switch_thermal_profile(
+ int *clk_switch_trips, struct dvfs_rail *rail)
+{
+ int i;
+
+ if (!rail->clk_switch_cdev) {
+ WARN(1, "%s: missing thermal dvfs cooling device\n",
+ rail->reg_id);
+ return -ENOENT;
+ }
+
+ for (i = 0; i < MAX_THERMAL_LIMITS - 1; i++) {
+ if (clk_switch_trips[i] >= clk_switch_trips[i+1])
+ break;
+ }
+
+ /*Only one trip point is allowed for this cdev*/
+ if (i != 0) {
+ WARN(1, "%s: Only one trip point allowed\n", __func__);
+ return -EINVAL;
+ }
+
+ rail->clk_switch_cdev->trip_temperatures_num = i + 1;
+ rail->clk_switch_cdev->trip_temperatures = clk_switch_trips;
+ return 0;
+}
+
void __init tegra_dvfs_rail_init_vmin_thermal_profile(
int *therm_trips_table, int *therm_floors_table,
struct dvfs_rail *rail, struct dvfs_dfll_data *d)
list_for_each_entry(rail, &dvfs_rail_list, node) {
tegra_dvfs_rail_register_vmin_cdev(rail);
tegra_dvfs_rail_register_vts_cdev(rail);
+ tegra_dvfs_rail_register_clk_switch_cdev(rail);
+
}
return 0;
struct tegra_cooling_device *vmin_cdev;
struct tegra_cooling_device *vmax_cdev;
struct tegra_cooling_device *vts_cdev;
+
+ /* Used for CPU clock switch between PLLX and DFLL */
+ struct tegra_cooling_device *clk_switch_cdev;
+
struct rail_alignment alignment;
struct rail_stats stats;
const char *version;
int vmin_trips_table[MAX_THERMAL_LIMITS];
int therm_floors_table[MAX_THERMAL_LIMITS];
int vts_trips_table[MAX_THERMAL_LIMITS];
+ int clk_switch_trips[MAX_THERMAL_LIMITS];
};
#define cpu_cvb_dvfs cvb_dvfs
struct tegra_cooling_device *tegra_dvfs_get_core_vmin_cdev(void);
struct tegra_cooling_device *tegra_dvfs_get_gpu_vmin_cdev(void);
struct tegra_cooling_device *tegra_dvfs_get_gpu_vts_cdev(void);
+struct tegra_cooling_device *tegra_dvfs_get_cpu_clk_switch_cdev(void);
#ifdef CONFIG_TEGRA_USE_SIMON
void tegra_dvfs_rail_init_simon_vmin_offsets(
int *offsets, int offs_num, struct dvfs_rail *rail);
void tegra_dvfs_rail_init_vmax_thermal_profile(
int *therm_trips_table, int *therm_caps_table,
struct dvfs_rail *rail, struct dvfs_dfll_data *d);
+int __init tegra_dvfs_rail_init_clk_switch_thermal_profile(
+ int *clk_switch_trips, struct dvfs_rail *rail);
int tegra_dvfs_rail_init_thermal_dvfs_trips(
int *therm_trips_table, struct dvfs_rail *rail);
int tegra_dvfs_init_thermal_dvfs_voltages(int *millivolts,