From: Thierry Reding <treding@xxxxxxxxxx> The SOCTHERM hardware found on Tegra implements a way of throttling the CPU and GPU when a given temperature threshold is reached. As opposed to traditional cooling devices, the programming for this happens during the initialization stage rather than dynamically at runtime when the thermal framework gets notified of thresholds being crossed. Use the newly introduced ->bind() and ->unbind() operations to make sure the SOCTHERM programming happens at the right time. This allows us to get rid of calls to the get_thermal_instance() helper which is not supposed to be accessed by drivers. Reported-by: Daniel Lezcano <daniel.lezcano@xxxxxxxxxx> Signed-off-by: Thierry Reding <treding@xxxxxxxxxx> --- drivers/thermal/tegra/soctherm.c | 146 ++++++++++++++----------------- 1 file changed, 67 insertions(+), 79 deletions(-) diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 220873298d77..cdc8764e88aa 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -303,6 +303,8 @@ struct tegra_thermctl_zone { struct tegra_soctherm *ts; struct thermal_zone_device *tz; const struct tegra_tsensor_group *sg; + /* instance of an internal throttle cooling device */ + struct thermal_cooling_device *cdev; }; struct soctherm_oc_cfg { @@ -315,6 +317,7 @@ struct soctherm_oc_cfg { }; struct soctherm_throt_cfg { + struct tegra_soctherm *soctherm; const char *name; unsigned int id; u8 priority; @@ -585,10 +588,10 @@ static int tsensor_group_thermtrip_get(struct tegra_soctherm *ts, int id) static int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, int trip_id, int temp) { struct tegra_thermctl_zone *zone = tz->devdata; - struct tegra_soctherm *ts = zone->ts; - struct thermal_trip trip; const struct tegra_tsensor_group *sg = zone->sg; + struct tegra_soctherm *ts = zone->ts; struct device *dev = zone->dev; + struct thermal_trip trip; int ret; if (!tz) @@ -610,26 +613,14 @@ static int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, int trip return 0; } else if (trip.type == THERMAL_TRIP_HOT) { - int i; - - for (i = 0; i < THROTTLE_SIZE; i++) { - struct thermal_cooling_device *cdev; - struct soctherm_throt_cfg *stc; - - if (!ts->throt_cfgs[i].init) - continue; - - cdev = ts->throt_cfgs[i].cdev; - if (get_thermal_instance(tz, cdev, trip_id)) - stc = find_throttle_cfg_by_name(ts, cdev->type); - else - continue; + if (zone->cdev) { + struct soctherm_throt_cfg *stc = zone->cdev->devdata; return throttrip_program(dev, sg, stc, temp); } } - return 0; + return ret; } static void thermal_irq_enable(struct tegra_thermctl_zone *zn) @@ -687,26 +678,6 @@ static const struct thermal_zone_device_ops tegra_of_thermal_ops = { .set_trips = tegra_thermctl_set_trips, }; -static int get_hot_temp(struct thermal_zone_device *tz, int *trip_id, int *temp) -{ - int i, ret; - struct thermal_trip trip; - - for (i = 0; i < thermal_zone_get_num_trips(tz); i++) { - - ret = thermal_zone_get_trip(tz, i, &trip); - if (ret) - return -EINVAL; - - if (trip.type == THERMAL_TRIP_HOT) { - *trip_id = i; - return 0; - } - } - - return -EINVAL; -} - /** * tegra_soctherm_set_hwtrips() - set HW trip point from DT data * @dev: struct device * of the SOC_THERM instance @@ -736,8 +707,7 @@ static int tegra_soctherm_set_hwtrips(struct device *dev, struct thermal_zone_device *tz) { struct tegra_soctherm *ts = dev_get_drvdata(dev); - struct soctherm_throt_cfg *stc; - int i, trip, temperature, ret; + int temperature, ret; /* Get thermtrips. If missing, try to get critical trips. */ temperature = tsensor_group_thermtrip_get(ts, sg->id); @@ -754,42 +724,6 @@ static int tegra_soctherm_set_hwtrips(struct device *dev, dev_info(dev, "thermtrip: will shut down when %s reaches %d mC\n", sg->name, temperature); - ret = get_hot_temp(tz, &trip, &temperature); - if (ret) { - dev_info(dev, "throttrip: %s: missing hot temperature\n", - sg->name); - return 0; - } - - for (i = 0; i < THROTTLE_OC1; i++) { - struct thermal_cooling_device *cdev; - - if (!ts->throt_cfgs[i].init) - continue; - - cdev = ts->throt_cfgs[i].cdev; - if (get_thermal_instance(tz, cdev, trip)) - stc = find_throttle_cfg_by_name(ts, cdev->type); - else - continue; - - ret = throttrip_program(dev, sg, stc, temperature); - if (ret) { - dev_err(dev, "throttrip: %s: error during enable\n", - sg->name); - return ret; - } - - dev_info(dev, - "throttrip: will throttle when %s reaches %d mC\n", - sg->name, temperature); - break; - } - - if (i == THROTTLE_SIZE) - dev_info(dev, "throttrip: %s: missing throttle cdev\n", - sg->name); - return 0; } @@ -1497,6 +1431,55 @@ static int soctherm_clk_enable(struct platform_device *pdev, bool enable) return 0; } +static int throt_bind(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, int trip_id, + unsigned long upper, unsigned long lower, + unsigned int weight) +{ + struct tegra_thermctl_zone *zone = tz->devdata; + struct device *dev = &cdev->device; + struct thermal_trip trip; + int err; + + err = thermal_zone_get_trip(tz, trip_id, &trip); + if (err < 0) + return err; + + if (trip.type == THERMAL_TRIP_HOT) { + struct soctherm_throt_cfg *stc = cdev->devdata; + + err = throttrip_program(zone->dev, zone->sg, stc, trip.temperature); + if (err < 0) { + dev_err(dev, "throttrip: %s: error during enable\n", + zone->sg->name); + return err; + } + + dev_info(dev, "throttrip: will throttle when %s reaches %d mC\n", + zone->sg->name, trip.temperature); + + /* keep a reference to this for ->set_trip_temp() */ + zone->cdev = cdev; + } + + return 0; +} + +static void throt_unbind(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, int trip_id) +{ + struct tegra_thermctl_zone *zone = tz->devdata; + struct thermal_trip trip; + int err; + + err = __thermal_zone_get_trip(tz, trip_id, &trip); + if (err < 0) + return; + + if (trip.type == THERMAL_TRIP_HOT) + zone->cdev = NULL; +} + static int throt_get_cdev_max_state(struct thermal_cooling_device *cdev, unsigned long *max_state) { @@ -1507,7 +1490,8 @@ static int throt_get_cdev_max_state(struct thermal_cooling_device *cdev, static int throt_get_cdev_cur_state(struct thermal_cooling_device *cdev, unsigned long *cur_state) { - struct tegra_soctherm *ts = cdev->devdata; + struct soctherm_throt_cfg *stc = cdev->devdata; + struct tegra_soctherm *ts = stc->soctherm; u32 r; r = readl(ts->regs + THROT_STATUS); @@ -1526,6 +1510,8 @@ static int throt_set_cdev_state(struct thermal_cooling_device *cdev, } static const struct thermal_cooling_device_ops throt_cooling_ops = { + .bind = throt_bind, + .unbind = throt_unbind, .get_max_state = throt_get_cdev_max_state, .get_cur_state = throt_get_cdev_cur_state, .set_cur_state = throt_set_cdev_state, @@ -1576,8 +1562,8 @@ static int soctherm_thermtrips_parse(struct platform_device *pdev) } static void soctherm_oc_cfg_parse(struct device *dev, - struct device_node *np_oc, - struct soctherm_throt_cfg *stc) + struct device_node *np_oc, + struct soctherm_throt_cfg *stc) { u32 val; @@ -1694,13 +1680,15 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) if (err) continue; + stc->soctherm = ts; + if (stc->id >= THROTTLE_OC1) { soctherm_oc_cfg_parse(dev, np_stcc, stc); stc->init = true; } else { tcd = thermal_of_cooling_device_register(np_stcc, - (char *)name, ts, + (char *)name, stc, &throt_cooling_ops); if (IS_ERR_OR_NULL(tcd)) { dev_err(dev, -- 2.39.1