summaryrefslogtreecommitdiff
path: root/gxp-thermal.c
diff options
context:
space:
mode:
authorNeela Chithirala <chithiralan@google.com>2022-01-17 04:41:54 +0000
committerNeela Chithirala <chithiralan@google.com>2022-02-03 06:27:20 +0000
commit3ccb2479717de3089dbbcb894ddd045b2ddc256c (patch)
treea577f284ff42d11b1fcfb7c338a7f0b59b10672a /gxp-thermal.c
parente14069f1739b05c7a7f60ae73c8ce14b91ef12e0 (diff)
downloadgs201-3ccb2479717de3089dbbcb894ddd045b2ddc256c.tar.gz
Merge branch 'gs201-release' to android13-gs-pixel-5.10
* gs201-release: gxp: Fix multicore VDs not shutting down clean Bug: 215303765 gxp: Rework VD locking and remove mailbox locking Bug: 189018271 gxp: initial commit for thermal driver Bug: 177217526 gxp: Add wakelock interface and make debugfs wakelock aware Bug: 215192870 gxp: Hook-up pm ops for driver suspend/resume Bug: 204924965 gxp: Dynamically power BLK_AUR on and off Bug: 204924965 gxp: support GXP_PLATFORM=GEM5 Bug: 204942713 gxp: Remove delay waiting for FW mailbox init Bug: 207037428 gxp: Fix infrequent crash during mailbox release gxp: Release FW file on firmware loading errors gxp: return GXP_RESP_CANCELLED if timeout occurs Bug: 207432733 gxp: Remove initial 10ms delay when disabling telemetry gxp: Cast telemetry buffer IOVAs to u32s before use gxp: check sscoredump by CONFIG_SUBSYSTEM_COREDUMP gxp: Fix double-lock hang in gxp_telemetry_vma_close gxp: Log driver git commit hash on probe Bug: 206744969 gxp: Add ioctl for reading the TOP global counter gxp: Implement eventfd signalling for telemetry gxp: Notify running cores of telemetry state changes gxp: Add notification interface Signed-off-by: Neela Chithirala <chithiralan@google.com> Change-Id: Ic7cd7b81ee643371c600ac208ae33d6344ed7f1b
Diffstat (limited to 'gxp-thermal.c')
-rw-r--r--gxp-thermal.c328
1 files changed, 328 insertions, 0 deletions
diff --git a/gxp-thermal.c b/gxp-thermal.c
new file mode 100644
index 0000000..71cb67e
--- /dev/null
+++ b/gxp-thermal.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Platform thermal driver for GXP.
+ *
+ * Copyright (C) 2021 Google LLC
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+#include <linux/version.h>
+
+#ifdef CONFIG_GXP_CLOUDRIPPER
+#include <linux/acpm_dvfs.h>
+#endif
+
+#include "gxp-internal.h"
+#include "gxp-pm.h"
+#include "gxp-thermal.h"
+#include "gxp-lpm.h"
+
+#define MAX_NUM_GXP_STATES 10
+#define OF_DATA_NUM_MAX (MAX_NUM_GXP_STATES * 2)
+
+/*
+ * Value comes from internal measurement
+ * https://docs.google.com/spreadsheets
+ * /d/1owRNFlm9EH-7IsycHXBnctyzGAd5j-VyLQOZ1ysFb7c
+ */
+static struct gxp_state_pwr state_pwr_map[MAX_NUM_GXP_STATES] = {
+ {1055000, 180},
+ {750000, 72},
+ {373000, 21},
+ {178000, 10},
+};
+
+static int gxp_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct gxp_thermal_manager *thermal = cdev->devdata;
+
+ if (!thermal->gxp_num_states)
+ return -EIO;
+
+ *state = thermal->gxp_num_states - 1;
+ return 0;
+}
+
+/*
+ * Set cooling state.
+ */
+static int gxp_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long cooling_state)
+{
+ int ret = 0;
+ struct gxp_thermal_manager *thermal = cdev->devdata;
+ struct device *dev = thermal->gxp->dev;
+ unsigned long pwr_state;
+
+ if (cooling_state >= thermal->gxp_num_states) {
+ dev_err(dev, "%s: invalid cooling state %lu\n", __func__,
+ cooling_state);
+ return -EINVAL;
+ }
+
+ mutex_lock(&thermal->lock);
+ cooling_state = max(thermal->sysfs_req, cooling_state);
+ if (cooling_state >= ARRAY_SIZE(state_pwr_map)) {
+ dev_err(dev, "Unsupported cooling state: %lu\n", cooling_state);
+ ret = -EINVAL;
+ goto out;
+ }
+ pwr_state = state_pwr_map[cooling_state].state;
+ dev_dbg(dev, "setting policy %ld\n", pwr_state);
+ if (cooling_state != thermal->cooling_state) {
+#ifdef CONFIG_GXP_CLOUDRIPPER
+ ret = exynos_acpm_set_policy(AUR_DVFS_DOMAIN,
+ pwr_state < AUR_UUD ? AUR_UUD : pwr_state);
+#endif
+ if (ret) {
+ dev_err(dev,
+ "error setting gxp cooling policy: %d\n", ret);
+ goto out;
+ }
+ thermal->cooling_state = cooling_state;
+ } else {
+ ret = -EALREADY;
+ }
+
+out:
+ mutex_unlock(&thermal->lock);
+ return ret;
+}
+
+static int gxp_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ int ret = 0;
+ struct gxp_thermal_manager *thermal = cdev->devdata;
+
+ mutex_lock(&thermal->lock);
+ *state = thermal->cooling_state;
+ if (*state >= thermal->gxp_num_states) {
+ dev_err(thermal->gxp->dev,
+ "Unknown cooling state: %lu, resetting\n", *state);
+ ret = -EINVAL;
+ goto out;
+ }
+out:
+ mutex_unlock(&thermal->lock);
+ return ret;
+}
+
+static int gxp_state2power_internal(unsigned long state, u32 *power,
+ struct gxp_thermal_manager *thermal)
+{
+ int i;
+
+ for (i = 0; i < thermal->gxp_num_states; i++) {
+ if (state == state_pwr_map[i].state) {
+ *power = state_pwr_map[i].power;
+ return 0;
+ }
+ }
+ dev_err(thermal->gxp->dev, "Unknown state req for: %lu\n", state);
+ *power = 0;
+ return -EINVAL;
+}
+
+static int gxp_get_requested_power(struct thermal_cooling_device *cdev,
+ u32 *power)
+{
+ /* Use ACTIVE_NOM as default value */
+ unsigned long power_state = AUR_NOM;
+ struct gxp_thermal_manager *cooling = cdev->devdata;
+#ifdef CONFIG_GXP_CLOUDRIPPER
+
+ power_state = exynos_acpm_get_rate(AUR_DVFS_DOMAIN, 0);
+#endif
+ return gxp_state2power_internal(power_state, power,
+ cooling);
+}
+
+/* TODO(b/213272324): Move state2power table to dts */
+static int gxp_state2power(struct thermal_cooling_device *cdev,
+ unsigned long state, u32 *power)
+{
+ struct gxp_thermal_manager *thermal = cdev->devdata;
+
+ if (state >= thermal->gxp_num_states) {
+ dev_err(thermal->gxp->dev, "%s: invalid state: %lu\n", __func__,
+ state);
+ return -EINVAL;
+ }
+
+ return gxp_state2power_internal(state_pwr_map[state].state, power,
+ thermal);
+}
+
+static int gxp_power2state(struct thermal_cooling_device *cdev,
+ u32 power, unsigned long *state)
+{
+ int i, penultimate_throttle_state;
+ struct gxp_thermal_manager *thermal = cdev->devdata;
+
+ *state = 0;
+ /* Less than 2 state means we cannot really throttle */
+ if (thermal->gxp_num_states < 2)
+ return thermal->gxp_num_states == 1 ? 0 : -EIO;
+
+ penultimate_throttle_state = thermal->gxp_num_states - 2;
+ /*
+ * argument "power" is the maximum allowed power consumption in mW as
+ * defined by the PID control loop. Check for the first state that is
+ * less than or equal to the current allowed power. state_pwr_map is
+ * descending, so lowest power consumption is last value in the array
+ * return lowest state even if it consumes more power than allowed as
+ * not all platforms can handle throttling below an active state
+ */
+ for (i = penultimate_throttle_state; i >= 0; --i) {
+ if (power < state_pwr_map[i].power) {
+ *state = i + 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+static struct thermal_cooling_device_ops gxp_cooling_ops = {
+ .get_max_state = gxp_get_max_state,
+ .get_cur_state = gxp_get_cur_state,
+ .set_cur_state = gxp_set_cur_state,
+ .get_requested_power = gxp_get_requested_power,
+ .state2power = gxp_state2power,
+ .power2state = gxp_power2state,
+};
+
+static void gxp_thermal_exit(struct gxp_thermal_manager *thermal)
+{
+ if (!IS_ERR_OR_NULL(thermal->cdev))
+ thermal_cooling_device_unregister(thermal->cdev);
+}
+
+static void devm_gxp_thermal_release(struct device *dev, void *res)
+{
+ struct gxp_thermal_manager *thermal = res;
+
+ gxp_thermal_exit(thermal);
+}
+
+static ssize_t
+user_vote_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_cooling_device *cdev =
+ container_of(dev, struct thermal_cooling_device,
+ device);
+ struct gxp_thermal_manager *cooling = cdev->devdata;
+
+ if (!cooling)
+ return -ENODEV;
+
+ return sysfs_emit(buf, "%lu\n", cooling->sysfs_req);
+}
+
+static ssize_t user_vote_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct thermal_cooling_device *cdev =
+ container_of(dev, struct thermal_cooling_device,
+ device);
+ struct gxp_thermal_manager *cooling = cdev->devdata;
+ int ret;
+ unsigned long state;
+
+ if (!cooling)
+ return -ENODEV;
+
+ ret = kstrtoul(buf, 0, &state);
+ if (ret)
+ return ret;
+
+ if (state >= cooling->gxp_num_states)
+ return -EINVAL;
+
+ mutex_lock(&cdev->lock);
+ cooling->sysfs_req = state;
+ cdev->updated = false;
+ mutex_unlock(&cdev->lock);
+ thermal_cdev_update(cdev);
+ return count;
+}
+
+static DEVICE_ATTR_RW(user_vote);
+
+static int
+gxp_thermal_cooling_register(struct gxp_thermal_manager *thermal, char *type)
+{
+ struct device_node *cooling_node = NULL;
+
+ thermal->op_data = NULL;
+ thermal->gxp_num_states = ARRAY_SIZE(state_pwr_map);
+
+ mutex_init(&thermal->lock);
+ cooling_node = of_find_node_by_name(NULL, GXP_COOLING_NAME);
+
+ /* TODO: Change this to fatal error once dts change is merged */
+ if (!cooling_node)
+ dev_warn(thermal->gxp->dev, "failed to find cooling node\n");
+ /* Initialize the cooling state as 0, means "no cooling" */
+ thermal->cooling_state = 0;
+ thermal->cdev = thermal_of_cooling_device_register(
+ cooling_node, type, thermal, &gxp_cooling_ops);
+ if (IS_ERR(thermal->cdev))
+ return PTR_ERR(thermal->cdev);
+
+ return device_create_file(&thermal->cdev->device, &dev_attr_user_vote);
+}
+
+static int cooling_init(struct gxp_thermal_manager *thermal, struct device *dev)
+{
+ int err;
+ struct dentry *d;
+
+ d = debugfs_create_dir("cooling", thermal->gxp->d_entry);
+ /* don't let debugfs creation failure abort the init procedure */
+ if (IS_ERR_OR_NULL(d))
+ dev_warn(dev, "failed to create debug fs for cooling");
+ thermal->cooling_root = d;
+
+ err = gxp_thermal_cooling_register(thermal, GXP_COOLING_NAME);
+ if (err) {
+ dev_err(dev, "failed to initialize external cooling\n");
+ gxp_thermal_exit(thermal);
+ return err;
+ }
+ return 0;
+}
+
+struct gxp_thermal_manager
+*gxp_thermal_init(struct gxp_dev *gxp)
+{
+ struct device *dev = gxp->dev;
+ struct gxp_thermal_manager *thermal;
+ int err;
+
+ thermal = devres_alloc(devm_gxp_thermal_release, sizeof(*thermal),
+ GFP_KERNEL);
+ if (!thermal)
+ return ERR_PTR(-ENOMEM);
+
+ thermal->gxp = gxp;
+ err = cooling_init(thermal, dev);
+ if (err) {
+ devres_free(thermal);
+ return ERR_PTR(err);
+ }
+
+ devres_add(dev, thermal);
+ return thermal;
+}