Commit 2a63bbca authored by Pradeep Kumar Chitrapu's avatar Pradeep Kumar Chitrapu Committed by Kalle Valo
Browse files

ath11k: add thermal cooling device support



Thermal cooling device support is added to control the temperature by
throttling the data transmission for the given duration. Throttling is
done by suspending all data tx queues by given percentage of time. The
thermal device allows user to configure duty cycle.

Throttling can be disabled by setting the duty cycle to 0. The cooling
device can be found under /sys/class/thermal/cooling_deviceX/.
Corresponding soft link to this device can be found under phy folder.

/sys/class/ieee80211/phy*/device/cooling_device.

To set duty cycle as 40%,

echo 40 >/sys/class/ieee80211/phy*/device/cooling_device/cur_state

Signed-off-by: default avatarPradeep Kumar Chitrapu <pradeepc@codeaurora.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent f9680c75
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ ath11k-y += core.o \
ath11k-$(CONFIG_ATH11K_DEBUGFS) += debug_htt_stats.o debugfs_sta.o
ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o
ath11k-$(CONFIG_ATH11K_TRACING) += trace.o
ath11k-$(CONFIG_THERMAL) += thermal.o

# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
+11 −1
Original line number Diff line number Diff line
@@ -392,11 +392,19 @@ static int ath11k_core_pdev_create(struct ath11k_base *ab)
		goto err_mac_unregister;
	}

	ret = ath11k_thermal_register(ab);
	if (ret) {
		ath11k_err(ab, "could not register thermal device: %d\n",
			   ret);
		goto err_dp_pdev_free;
	}

	return 0;

err_dp_pdev_free:
	ath11k_dp_pdev_free(ab);
err_mac_unregister:
	ath11k_mac_unregister(ab);

err_pdev_debug:
	ath11k_debug_pdev_destroy(ab);

@@ -405,6 +413,7 @@ err_pdev_debug:

static void ath11k_core_pdev_destroy(struct ath11k_base *ab)
{
	ath11k_thermal_unregister(ab);
	ath11k_mac_unregister(ab);
	ath11k_ahb_ext_irq_disable(ab);
	ath11k_dp_pdev_free(ab);
@@ -569,6 +578,7 @@ static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab)
	int ret;

	mutex_lock(&ab->core_lock);
	ath11k_thermal_unregister(ab);
	ath11k_ahb_ext_irq_disable(ab);
	ath11k_dp_pdev_free(ab);
	ath11k_ahb_stop(ab);
+2 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include "hw.h"
#include "hal_rx.h"
#include "reg.h"
#include "thermal.h"

#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)

@@ -526,6 +527,7 @@ struct ath11k {
	struct ath11k_debug debug;
#endif
	bool dfs_block_radar_events;
	struct ath11k_thermal thermal;
};

struct ath11k_band_cap {
+145 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
 * Copyright (c) 2020 The Linux Foundation. All rights reserved.
 */

#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/thermal.h>
#include "core.h"
#include "debug.h"

static int
ath11k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
				      unsigned long *state)
{
	*state = ATH11K_THERMAL_THROTTLE_MAX;

	return 0;
}

static int
ath11k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
				      unsigned long *state)
{
	struct ath11k *ar = cdev->devdata;

	mutex_lock(&ar->conf_mutex);
	*state = ar->thermal.throttle_state;
	mutex_unlock(&ar->conf_mutex);

	return 0;
}

static int
ath11k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
				      unsigned long throttle_state)
{
	struct ath11k *ar = cdev->devdata;
	int ret;

	if (throttle_state > ATH11K_THERMAL_THROTTLE_MAX) {
		ath11k_warn(ar->ab, "throttle state %ld is exceeding the limit %d\n",
			    throttle_state, ATH11K_THERMAL_THROTTLE_MAX);
		return -EINVAL;
	}
	mutex_lock(&ar->conf_mutex);
	ret = ath11k_thermal_set_throttling(ar, throttle_state);
	if (ret == 0)
		ar->thermal.throttle_state = throttle_state;
	mutex_unlock(&ar->conf_mutex);
	return ret;
}

static struct thermal_cooling_device_ops ath11k_thermal_ops = {
	.get_max_state = ath11k_thermal_get_max_throttle_state,
	.get_cur_state = ath11k_thermal_get_cur_throttle_state,
	.set_cur_state = ath11k_thermal_set_cur_throttle_state,
};

int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state)
{
	struct ath11k_base *sc = ar->ab;
	struct thermal_mitigation_params param;
	int ret = 0;

	lockdep_assert_held(&ar->conf_mutex);

	if (ar->state != ATH11K_STATE_ON)
		return 0;

	memset(&param, 0, sizeof(param));
	param.pdev_id = ar->pdev->pdev_id;
	param.enable = throttle_state ? 1 : 0;
	param.dc = ATH11K_THERMAL_DEFAULT_DUTY_CYCLE;
	param.dc_per_event = 0xFFFFFFFF;

	param.levelconf[0].tmplwm = ATH11K_THERMAL_TEMP_LOW_MARK;
	param.levelconf[0].tmphwm = ATH11K_THERMAL_TEMP_HIGH_MARK;
	param.levelconf[0].dcoffpercent = throttle_state;
	param.levelconf[0].priority = 0; /* disable all data tx queues */

	ret = ath11k_wmi_send_thermal_mitigation_param_cmd(ar, &param);
	if (ret) {
		ath11k_warn(sc, "failed to send thermal mitigation duty cycle %u ret %d\n",
			    throttle_state, ret);
	}

	return ret;
}

int ath11k_thermal_register(struct ath11k_base *sc)
{
	struct thermal_cooling_device *cdev;
	struct ath11k *ar;
	struct ath11k_pdev *pdev;
	int i, ret;

	for (i = 0; i < sc->num_radios; i++) {
		pdev = &sc->pdevs[i];
		ar = pdev->ar;
		if (!ar)
			continue;

		cdev = thermal_cooling_device_register("ath11k_thermal", ar,
						       &ath11k_thermal_ops);

		if (IS_ERR(cdev)) {
			ath11k_err(sc, "failed to setup thermal device result: %ld\n",
				   PTR_ERR(cdev));
			return -EINVAL;
		}

		ret = sysfs_create_link(&ar->hw->wiphy->dev.kobj, &cdev->device.kobj,
					"cooling_device");
		if (ret) {
			ath11k_err(sc, "failed to create cooling device symlink\n");
			goto err_thermal_destroy;
		}

		ar->thermal.cdev = cdev;
	}

	return 0;

err_thermal_destroy:
	ath11k_thermal_unregister(sc);
	return ret;
}

void ath11k_thermal_unregister(struct ath11k_base *sc)
{
	struct ath11k *ar;
	struct ath11k_pdev *pdev;
	int i;

	for (i = 0; i < sc->num_radios; i++) {
		pdev = &sc->pdevs[i];
		ar = pdev->ar;
		if (!ar)
			continue;

		sysfs_remove_link(&ar->hw->wiphy->dev.kobj, "cooling_device");
		thermal_cooling_device_unregister(ar->thermal.cdev);
	}
}
+40 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
 * Copyright (c) 2020 The Linux Foundation. All rights reserved.
 */

#ifndef _ATH11K_THERMAL_
#define _ATH11K_THERMAL_

#define ATH11K_THERMAL_TEMP_LOW_MARK -100
#define ATH11K_THERMAL_TEMP_HIGH_MARK 150
#define ATH11K_THERMAL_THROTTLE_MAX     100
#define ATH11K_THERMAL_DEFAULT_DUTY_CYCLE 100

struct ath11k_thermal {
	struct thermal_cooling_device *cdev;

	/* protected by conf_mutex */
	u32 throttle_state;
};

#if IS_REACHABLE(CONFIG_THERMAL)
int ath11k_thermal_register(struct ath11k_base *sc);
void ath11k_thermal_unregister(struct ath11k_base *sc);
int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state);
#else
static inline int ath11k_thermal_register(struct ath11k_base *sc)
{
	return 0;
}

static inline void ath11k_thermal_unregister(struct ath11k *ar)
{
}

static inline int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state)
{
}

#endif
#endif /* _ATH11K_THERMAL_ */
Loading