955 lines
24 KiB
C
955 lines
24 KiB
C
/****************************************************************************
|
|
* drivers/thermal/thermal_core.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#ifdef CONFIG_PM
|
|
#include <nuttx/power/pm.h>
|
|
#endif
|
|
|
|
#include <debug.h>
|
|
#include <stdio.h>
|
|
#include <sys/boardctl.h>
|
|
|
|
#include "sched/sched.h"
|
|
#include "thermal_core.h"
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static int zone_bind_cooling (FAR struct thermal_zone_device_s *zdev,
|
|
int trip,
|
|
FAR struct thermal_cooling_device_s *cdev,
|
|
unsigned int upper, unsigned int lower,
|
|
unsigned int weight);
|
|
static void zone_unbind_cooling(FAR struct thermal_zone_device_s *zdev,
|
|
int trip,
|
|
FAR struct thermal_cooling_device_s *cdev);
|
|
|
|
static FAR struct thermal_governor_s *
|
|
find_governor_by_name (FAR const char *name);
|
|
static int zone_set_governor (FAR struct thermal_zone_device_s *zdev,
|
|
FAR struct thermal_governor_s *gov);
|
|
|
|
static void device_bind (FAR struct thermal_zone_device_s *zdev,
|
|
FAR struct thermal_cooling_device_s *cdev);
|
|
static void device_unbind (FAR struct thermal_zone_device_s *zdev,
|
|
FAR struct thermal_cooling_device_s *cdev);
|
|
|
|
#ifdef CONFIG_PM
|
|
static void thermal_pm_notify(FAR struct pm_callback_s *cb, int domain,
|
|
enum pm_state_e pmstate);
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static struct list_node
|
|
g_cooling_dev_list = LIST_INITIAL_VALUE(g_cooling_dev_list);
|
|
|
|
static struct list_node
|
|
g_governor_list = LIST_INITIAL_VALUE(g_governor_list);
|
|
|
|
static struct list_node
|
|
g_zone_dev_list = LIST_INITIAL_VALUE(g_zone_dev_list);
|
|
|
|
static mutex_t g_thermal_lock = NXMUTEX_INITIALIZER;
|
|
|
|
static FAR struct thermal_governor_s *g_def_governor = NULL;
|
|
|
|
#ifdef CONFIG_PM
|
|
struct pm_callback_s g_thermal_pm_cb =
|
|
{
|
|
.notify = thermal_pm_notify,
|
|
};
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
static int zone_set_governor(FAR struct thermal_zone_device_s *zdev,
|
|
FAR struct thermal_governor_s *gov)
|
|
{
|
|
int ret = OK;
|
|
|
|
/* The caller must use `g_thermal_lock` to protect zones and governors */
|
|
|
|
if (zdev->governor && zdev->governor->unbind_from_tz)
|
|
{
|
|
zdev->governor->unbind_from_tz(zdev);
|
|
}
|
|
|
|
if (gov && gov->bind_to_tz)
|
|
{
|
|
ret = gov->bind_to_tz(zdev);
|
|
if (ret < 0)
|
|
{
|
|
if (zdev->governor->bind_to_tz(zdev) < 0)
|
|
{
|
|
zdev->governor = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
zdev->governor = gov;
|
|
return ret;
|
|
}
|
|
|
|
static int zone_bind_cooling(FAR struct thermal_zone_device_s *zdev,
|
|
int trip,
|
|
FAR struct thermal_cooling_device_s *cdev,
|
|
unsigned int upper, unsigned int lower,
|
|
unsigned int weight)
|
|
{
|
|
FAR struct thermal_instance_s *ins;
|
|
FAR struct thermal_instance_s *pos;
|
|
unsigned int max_state;
|
|
int ret;
|
|
|
|
ret = cdev->ops->get_max_state(cdev, &max_state);
|
|
if (ret < 0)
|
|
{
|
|
therr("Get max state failed!\n");
|
|
return ret;
|
|
}
|
|
|
|
list_for_every_entry(&zdev->instance_list, pos,
|
|
struct thermal_instance_s, zdev_node)
|
|
{
|
|
if (zdev == pos->zdev && cdev == pos->cdev && trip == pos->trip)
|
|
{
|
|
thwarn("Instance %s %s %d already exist!",
|
|
zdev->name, cdev->name, trip);
|
|
return -EEXIST;
|
|
}
|
|
}
|
|
|
|
ins = kmm_malloc(sizeof(struct thermal_instance_s));
|
|
if (!ins)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ins->zdev = zdev;
|
|
ins->cdev = cdev;
|
|
ins->trip = trip;
|
|
ins->target = THERMAL_NO_TARGET;
|
|
|
|
if (lower == THERMAL_NO_LIMIT)
|
|
{
|
|
ins->lower = 0;
|
|
}
|
|
else
|
|
{
|
|
ins->lower = lower;
|
|
}
|
|
|
|
if (upper == THERMAL_NO_LIMIT)
|
|
{
|
|
ins->upper = max_state;
|
|
}
|
|
else
|
|
{
|
|
ins->upper = upper;
|
|
}
|
|
|
|
ins->weight = weight;
|
|
|
|
thinfo("Adding instance zdev:%-4s cdev:%-4s h:%2u l:%2u t:%d\n",
|
|
zdev->name, cdev->name, ins->upper, ins->lower, ins->trip);
|
|
|
|
list_add_tail(&zdev->instance_list, &ins->zdev_node);
|
|
list_add_tail(&cdev->instance_list, &ins->cdev_node);
|
|
return OK;
|
|
}
|
|
|
|
static void zone_unbind_cooling(FAR struct thermal_zone_device_s *zdev,
|
|
int trip,
|
|
FAR struct thermal_cooling_device_s *cdev)
|
|
{
|
|
FAR struct thermal_instance_s *ins;
|
|
|
|
list_for_every_entry(&zdev->instance_list, ins,
|
|
struct thermal_instance_s, zdev_node)
|
|
{
|
|
if (zdev == ins->zdev && cdev == ins->cdev && trip == ins->trip)
|
|
{
|
|
list_delete(&ins->zdev_node);
|
|
list_delete(&ins->cdev_node);
|
|
|
|
kmm_free(ins);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void device_bind(FAR struct thermal_zone_device_s *zdev,
|
|
FAR struct thermal_cooling_device_s *cdev)
|
|
{
|
|
FAR const struct thermal_zone_map_s *map;
|
|
FAR const struct thermal_zone_trip_s *trip;
|
|
int ret;
|
|
int i;
|
|
int j;
|
|
|
|
for (i = 0; i < zdev->params->num_maps; i++)
|
|
{
|
|
map = &zdev->params->maps[i];
|
|
|
|
if (strcmp(map->cdev_name, cdev->name))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (j = 0; j < zdev->params->num_trips; j++)
|
|
{
|
|
trip = &zdev->params->trips[j];
|
|
if (strcmp(map->trip_name, trip->name))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ret = zone_bind_cooling(zdev, j, cdev, map->high, map->low,
|
|
map->weight);
|
|
if (ret < 0)
|
|
{
|
|
therr("Failed to bind %s and %s, trip %d\n",
|
|
zdev->name, cdev->name, j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void device_unbind(FAR struct thermal_zone_device_s *zdev,
|
|
FAR struct thermal_cooling_device_s *cdev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < zdev->params->num_trips; i++)
|
|
{
|
|
zone_unbind_cooling(zdev, i, cdev);
|
|
}
|
|
}
|
|
|
|
static FAR struct thermal_governor_s *
|
|
find_governor_by_name(FAR const char *name)
|
|
{
|
|
FAR struct thermal_governor_s *gov;
|
|
|
|
if (!name)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* The caller must use `g_thermal_lock` to protect governors */
|
|
|
|
list_for_every_entry(&g_governor_list, gov, struct thermal_governor_s,
|
|
node)
|
|
{
|
|
if (!strcmp(gov->name, name))
|
|
{
|
|
return gov;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static void thermal_pm_notify(FAR struct pm_callback_s *cb, int domain,
|
|
enum pm_state_e pmstate)
|
|
{
|
|
FAR struct thermal_zone_device_s *zdev;
|
|
|
|
switch (pmstate)
|
|
{
|
|
case PM_SLEEP:
|
|
{
|
|
list_for_every_entry(&g_zone_dev_list, zdev,
|
|
struct thermal_zone_device_s, node)
|
|
{
|
|
work_cancel(LPWORK, &zdev->monitor);
|
|
}
|
|
}
|
|
break;
|
|
case PM_RESTORE:
|
|
case PM_NORMAL:
|
|
case PM_IDLE:
|
|
case PM_STANDBY:
|
|
{
|
|
list_for_every_entry(&g_zone_dev_list, zdev,
|
|
struct thermal_zone_device_s, node)
|
|
{
|
|
if (zdev->enabled && work_available(&zdev->monitor))
|
|
{
|
|
work_queue(LPWORK, &zdev->monitor,
|
|
(worker_t)thermal_zone_device_update, zdev,
|
|
zdev->params->polling_delay);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
int thermal_zone_enable(FAR struct thermal_zone_device_s *zdev,
|
|
bool enabled)
|
|
{
|
|
nxmutex_lock(&g_thermal_lock);
|
|
|
|
if (enabled == zdev->enabled)
|
|
{
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
return OK;
|
|
}
|
|
|
|
zdev->enabled = enabled;
|
|
work_cancel_sync(LPWORK, &zdev->monitor);
|
|
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
|
|
thermal_zone_device_update(zdev);
|
|
return OK;
|
|
}
|
|
|
|
int thermal_zone_get_trend(FAR struct thermal_zone_device_s *zdev)
|
|
{
|
|
enum thermal_trend_e trend;
|
|
|
|
if (zdev->ops->get_trend)
|
|
{
|
|
if (!zdev->ops->get_trend(zdev, &trend))
|
|
{
|
|
return trend;
|
|
}
|
|
}
|
|
|
|
if (zdev->last_temperature == THERMAL_INVALID_TEMP ||
|
|
zdev->temperature == THERMAL_INVALID_TEMP)
|
|
{
|
|
trend = THERMAL_TREND_STABLE;
|
|
}
|
|
else if (zdev->last_temperature > zdev->temperature)
|
|
{
|
|
trend = THERMAL_TREND_DROPPING;
|
|
}
|
|
else if (zdev->last_temperature < zdev->temperature)
|
|
{
|
|
trend = THERMAL_TREND_RAISING;
|
|
}
|
|
else
|
|
{
|
|
trend = THERMAL_TREND_STABLE;
|
|
}
|
|
|
|
return trend;
|
|
}
|
|
|
|
int thermal_zone_get_trip_temp(FAR struct thermal_zone_device_s *zdev,
|
|
int trip,
|
|
FAR int *temp)
|
|
{
|
|
if (!temp || trip >= zdev->params->num_trips)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
*temp = zdev->params->trips[trip].temp;
|
|
return OK;
|
|
}
|
|
|
|
int thermal_zone_get_trip_type(FAR struct thermal_zone_device_s *zdev,
|
|
int trip,
|
|
FAR enum thermal_trip_type_e *type)
|
|
{
|
|
if (!type || trip >= zdev->params->num_trips)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
*type = zdev->params->trips[trip].type;
|
|
return OK;
|
|
}
|
|
|
|
int thermal_zone_get_trip_hyst(FAR struct thermal_zone_device_s *zdev,
|
|
int trip,
|
|
FAR int *hyst)
|
|
{
|
|
if (!hyst || trip >= zdev->params->num_trips)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
*hyst = zdev->params->trips[trip].hyst;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: thermal_register_governor
|
|
*
|
|
* Description:
|
|
* Register governor
|
|
*
|
|
* Input Parameters:
|
|
* gov - the struct thermal_governor_s addr
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) is returned on success. Otherwise a negated errno value is
|
|
* returned to indicate the nature of the failure.
|
|
****************************************************************************/
|
|
|
|
int thermal_register_governor(FAR struct thermal_governor_s *gov)
|
|
{
|
|
FAR struct thermal_governor_s *pos;
|
|
|
|
if (!gov || !gov->throttle)
|
|
{
|
|
therr("Invalid governor!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
nxmutex_lock(&g_thermal_lock);
|
|
|
|
list_for_every_entry(&g_governor_list, pos,
|
|
struct thermal_governor_s, node)
|
|
{
|
|
if (!strcmp(gov->name, pos->name))
|
|
{
|
|
thwarn("Governor (%s) already exists!", gov->name);
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
return -EEXIST;
|
|
}
|
|
}
|
|
|
|
list_add_tail(&g_governor_list, &gov->node);
|
|
|
|
thinfo("Register governor %s\n", gov->name);
|
|
|
|
/* Default governor */
|
|
|
|
if (!strcmp(gov->name, CONFIG_THERMAL_DEFAULT_GOVERNOR))
|
|
{
|
|
g_def_governor = gov;
|
|
thinfo("Default governor %s registered!\n", g_def_governor->name);
|
|
}
|
|
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: thermal_unregister_governor
|
|
*
|
|
* Description:
|
|
* Unregister governor
|
|
*
|
|
* Input Parameters:
|
|
* gov - the struct thermal_governor_s addr
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
****************************************************************************/
|
|
|
|
void thermal_unregister_governor(FAR struct thermal_governor_s *gov)
|
|
{
|
|
FAR struct thermal_zone_device_s *zdev;
|
|
|
|
if (!gov)
|
|
{
|
|
return;
|
|
}
|
|
|
|
nxmutex_lock(&g_thermal_lock);
|
|
|
|
list_for_every_entry(&g_zone_dev_list, zdev,
|
|
struct thermal_zone_device_s, node)
|
|
{
|
|
if (zdev->governor == gov)
|
|
{
|
|
zone_set_governor(zdev, NULL);
|
|
}
|
|
}
|
|
|
|
list_delete(&gov->node);
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: thermal_cooling_device_register
|
|
*
|
|
* Description:
|
|
* Register thermal cooling device.
|
|
*
|
|
* Input Parameters:
|
|
* name - Name of cooling device
|
|
* devdata - Device driver data
|
|
* ops - Operations
|
|
*
|
|
* Returned Value:
|
|
* Addr of created cooling device entry.
|
|
****************************************************************************/
|
|
|
|
FAR struct thermal_cooling_device_s *
|
|
thermal_cooling_device_register(FAR const char *name, void *devdata,
|
|
FAR const struct thermal_cooling_device_ops_s *ops)
|
|
{
|
|
FAR struct thermal_zone_device_s *zdev;
|
|
FAR struct thermal_cooling_device_s *cdev;
|
|
|
|
nxmutex_lock(&g_thermal_lock);
|
|
|
|
list_for_every_entry(&g_cooling_dev_list, cdev,
|
|
struct thermal_cooling_device_s, node)
|
|
{
|
|
if (!strcmp(cdev->name, name))
|
|
{
|
|
thwarn("Cooling device (%s) already exists!", name);
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
cdev = kmm_zalloc(sizeof(*cdev));
|
|
if (!cdev)
|
|
{
|
|
therr("Cannot allocate memory for cooling device registering!\n");
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
return NULL;
|
|
}
|
|
|
|
strlcpy(cdev->name, name, THERMAL_NAME_LEN);
|
|
cdev->ops = ops;
|
|
cdev->devdata = devdata;
|
|
|
|
list_initialize(&cdev->instance_list);
|
|
list_add_tail(&g_cooling_dev_list, &cdev->node);
|
|
|
|
list_for_every_entry(&g_zone_dev_list, zdev,
|
|
struct thermal_zone_device_s, node)
|
|
{
|
|
device_bind(zdev, cdev);
|
|
}
|
|
|
|
thinfo("Registered cooling device %s\n", cdev->name);
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
|
|
return cdev;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: thermal_cooling_device_unregister
|
|
*
|
|
* Description:
|
|
* Unregister thermal cooling device.
|
|
*
|
|
* Input Parameters:
|
|
* cdev - Cooling Device
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
****************************************************************************/
|
|
|
|
void
|
|
thermal_cooling_device_unregister(FAR struct thermal_cooling_device_s *cdev)
|
|
{
|
|
FAR struct thermal_zone_device_s *zdev;
|
|
|
|
/* Unbind */
|
|
|
|
nxmutex_lock(&g_thermal_lock);
|
|
|
|
list_delete(&cdev->node);
|
|
|
|
list_for_every_entry(&g_zone_dev_list, zdev,
|
|
struct thermal_zone_device_s, node)
|
|
{
|
|
device_unbind(zdev, cdev);
|
|
}
|
|
|
|
kmm_free(cdev);
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: thermal_cooling_device_update
|
|
*
|
|
* Description:
|
|
* Update thermal cooling device.
|
|
*
|
|
* Input Parameters:
|
|
* cdev - Cooling Device.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
****************************************************************************/
|
|
|
|
void thermal_cooling_device_update(FAR struct thermal_cooling_device_s *cdev)
|
|
{
|
|
FAR struct thermal_instance_s *instance;
|
|
unsigned int target = THERMAL_NO_TARGET;
|
|
unsigned int current = THERMAL_NO_TARGET;
|
|
int ret;
|
|
|
|
ret = cdev->ops->get_state(cdev, ¤t);
|
|
if (ret < 0)
|
|
{
|
|
thwarn("Thermal get cooling state failed!\n");
|
|
return;
|
|
}
|
|
|
|
list_for_every_entry(&cdev->instance_list, instance,
|
|
struct thermal_instance_s, cdev_node)
|
|
{
|
|
if ((instance->target != THERMAL_NO_TARGET) &&
|
|
(instance->target > target ||
|
|
target == THERMAL_NO_TARGET))
|
|
{
|
|
target = instance->target;
|
|
}
|
|
}
|
|
|
|
if (target != THERMAL_NO_TARGET && target != current)
|
|
{
|
|
ret = cdev->ops->set_state(cdev, target);
|
|
if (ret < 0)
|
|
{
|
|
thwarn("Thermal set cooling state of %s failed!\n", cdev->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: thermal_zone_device_register
|
|
*
|
|
* Description:
|
|
* Register thermal zone device.
|
|
*
|
|
* Input Parameters:
|
|
* name - Name of zone.
|
|
* devdata - Device driver data.
|
|
* ops - Operations of zone deivce.
|
|
* params - Parameter of zone device.
|
|
*
|
|
* Returned Value:
|
|
* Addr of created zone device entry.
|
|
****************************************************************************/
|
|
|
|
struct thermal_zone_device_s *
|
|
thermal_zone_device_register(FAR const char *name,
|
|
FAR void *devdata,
|
|
FAR const struct thermal_zone_device_ops_s *ops,
|
|
FAR const struct thermal_zone_params_s *params)
|
|
{
|
|
FAR struct thermal_cooling_device_s *cdev;
|
|
FAR struct thermal_governor_s *gov;
|
|
FAR struct thermal_zone_device_s *pos;
|
|
FAR struct thermal_zone_device_s *zdev;
|
|
int i;
|
|
|
|
if (!ops || !ops->get_temp)
|
|
{
|
|
therr("Invalid zone operations!\n");
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < params->num_trips; i++)
|
|
{
|
|
if (params->trips[i].type >= THERMAL_TRIP_TYPE_MAX)
|
|
{
|
|
therr("Invalid trip type (%d)!\n", params->trips[i].type);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
nxmutex_lock(&g_thermal_lock);
|
|
|
|
list_for_every_entry(&g_zone_dev_list, pos,
|
|
struct thermal_zone_device_s, node)
|
|
{
|
|
if (!strcmp(name, pos->name))
|
|
{
|
|
thwarn("Zone device (%s) already exists!", name);
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
zdev = kmm_zalloc(sizeof(struct thermal_zone_device_s));
|
|
if (!zdev)
|
|
{
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
return NULL;
|
|
}
|
|
|
|
zdev->ops = ops;
|
|
zdev->devdata = devdata;
|
|
zdev->enabled = true;
|
|
|
|
strlcpy(zdev->name, name, THERMAL_NAME_LEN);
|
|
|
|
zdev->params = params;
|
|
zdev->temperature = THERMAL_INVALID_TEMP;
|
|
|
|
list_initialize(&zdev->instance_list);
|
|
|
|
/* Set governor */
|
|
|
|
gov = find_governor_by_name(zdev->params->gov_name);
|
|
zone_set_governor(zdev, gov ? gov : g_def_governor);
|
|
|
|
thinfo("Set governor of zone %s to %s.\n", zdev->name,
|
|
zdev->governor ? zdev->governor->name : "");
|
|
|
|
list_add_tail(&g_zone_dev_list, &zdev->node);
|
|
|
|
list_for_every_entry(&g_cooling_dev_list, cdev,
|
|
struct thermal_cooling_device_s, node)
|
|
{
|
|
device_bind(zdev, cdev);
|
|
}
|
|
|
|
#ifdef CONFIG_THERMAL_PROCFS
|
|
thermal_zone_procfs_register(zdev);
|
|
#endif
|
|
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
|
|
thinfo("Registered zone device %s\n", zdev->name);
|
|
thermal_zone_device_update(zdev);
|
|
return zdev;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: thermal_zone_device_unregister
|
|
*
|
|
* Description:
|
|
* Unregister thermal zone device.
|
|
*
|
|
* Input Parameters:
|
|
* zdev - Zone Device
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
****************************************************************************/
|
|
|
|
void thermal_zone_device_unregister(FAR struct thermal_zone_device_s *zdev)
|
|
{
|
|
FAR struct thermal_cooling_device_s *cdev;
|
|
|
|
/* Disable Zone */
|
|
|
|
thermal_zone_enable(zdev, false);
|
|
|
|
/* Unbind */
|
|
|
|
nxmutex_lock(&g_thermal_lock);
|
|
|
|
list_for_every_entry(&g_cooling_dev_list, cdev,
|
|
struct thermal_cooling_device_s, node)
|
|
{
|
|
device_unbind(zdev, cdev);
|
|
}
|
|
|
|
list_delete(&zdev->node);
|
|
|
|
#ifdef CONFIG_THERMAL_PROCFS
|
|
thermal_zone_procfs_unregister(zdev);
|
|
#endif
|
|
|
|
zone_set_governor(zdev, NULL);
|
|
kmm_free(zdev);
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: thermal_zone_device_update
|
|
*
|
|
* Description:
|
|
* Update thermal zone device.
|
|
* Get temperature from sensor and throttle if necessary.
|
|
*
|
|
* Input Parameters:
|
|
* zdev - Zone Device
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
****************************************************************************/
|
|
|
|
void thermal_zone_device_update(FAR struct thermal_zone_device_s *zdev)
|
|
{
|
|
int trip_high = INT_MAX;
|
|
int trip_low = INT_MIN;
|
|
int trip;
|
|
int temp;
|
|
int ret;
|
|
|
|
nxmutex_lock(&g_thermal_lock);
|
|
|
|
/* Update termerature */
|
|
|
|
if (!zdev->enabled)
|
|
{
|
|
goto unlock;
|
|
}
|
|
|
|
ret = zdev->ops->get_temp(zdev, &temp);
|
|
if (ret < 0)
|
|
{
|
|
therr("Failed to get temperature from zone %s \n", zdev->name);
|
|
goto unlock;
|
|
}
|
|
|
|
zdev->last_temperature = zdev->temperature;
|
|
zdev->temperature = temp;
|
|
|
|
for (trip = 0; trip < zdev->params->num_trips; trip++)
|
|
{
|
|
enum thermal_trip_type_e type;
|
|
int temp_low;
|
|
int hyst;
|
|
|
|
thermal_zone_get_trip_temp(zdev, trip, &temp);
|
|
thermal_zone_get_trip_hyst(zdev, trip, &hyst);
|
|
thermal_zone_get_trip_type(zdev, trip, &type);
|
|
|
|
if (zdev->temperature < temp && trip_high > temp)
|
|
{
|
|
trip_high = temp;
|
|
}
|
|
|
|
temp_low = temp - hyst;
|
|
|
|
if (zdev->temperature > temp_low && trip_low < temp_low)
|
|
{
|
|
trip_low = temp_low;
|
|
}
|
|
|
|
/* Critical */
|
|
|
|
if (type == THERMAL_CRITICAL && zdev->temperature >= temp)
|
|
{
|
|
therr("Thermal critical (%d), resetting...\n", zdev->temperature);
|
|
#ifdef CONFIG_BOARDCTL_RESET
|
|
boardctl(BOARDIOC_RESET, BOARDIOC_SOFTRESETCAUSE_THERMAL);
|
|
#endif
|
|
}
|
|
else if (zdev->governor)
|
|
{
|
|
zdev->governor->throttle(zdev, trip);
|
|
}
|
|
else if(g_def_governor)
|
|
{
|
|
g_def_governor->throttle(zdev, trip);
|
|
}
|
|
else
|
|
{
|
|
therr("No valid governor!\n");
|
|
}
|
|
}
|
|
|
|
if (zdev->ops->set_trips)
|
|
{
|
|
ret = zdev->ops->set_trips(zdev, trip_low, trip_high);
|
|
if (ret < 0)
|
|
{
|
|
thwarn("Set trip points (l:%d, h:%d) for %s failed\n",
|
|
trip_low, trip_high, zdev->name);
|
|
}
|
|
}
|
|
|
|
work_queue(LPWORK, &zdev->monitor, (worker_t)thermal_zone_device_update,
|
|
zdev, zdev->params->polling_delay);
|
|
|
|
unlock:
|
|
nxmutex_unlock(&g_thermal_lock);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: thermal_init
|
|
*
|
|
* Description:
|
|
* Init thermal framework
|
|
*
|
|
* Input Parameters:
|
|
* None
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) is returned on success. Otherwise a negated errno value is
|
|
* returned to indicate the nature of the failure.
|
|
****************************************************************************/
|
|
|
|
int thermal_init(void)
|
|
{
|
|
int ret = OK;
|
|
|
|
#ifdef CONFIG_THERMAL_GOVERNOR_STEP_WISE
|
|
ret = thermal_register_step_wise_governor();
|
|
if (ret < 0)
|
|
{
|
|
therr("Register step wise governor failed!\n");
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_THERMAL_DUMMY
|
|
ret = thermal_dummy_init();
|
|
if (ret < 0)
|
|
{
|
|
therr("Dummy driver init failed!\n");
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_THERMAL_CDEV_CPUFREQ
|
|
if (NULL == thermal_cpufreq_cooling_register())
|
|
{
|
|
return -ENOTSUP;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_PM
|
|
ret = pm_register(&g_thermal_pm_cb);
|
|
if (ret < 0)
|
|
{
|
|
therr("Register suspend notifier failed!\n");
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|