brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · 08db878 Raw
252 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Surface Platform Profile / Performance Mode driver for Surface System4 * Aggregator Module (thermal and fan subsystem).5 *6 * Copyright (C) 2021-2022 Maximilian Luz <luzmaximilian@gmail.com>7 */8 9#include <linux/unaligned.h>10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/platform_profile.h>13#include <linux/types.h>14 15#include <linux/surface_aggregator/device.h>16 17// Enum for the platform performance profile sent to the TMP module.18enum ssam_tmp_profile {19	SSAM_TMP_PROFILE_NORMAL             = 1,20	SSAM_TMP_PROFILE_BATTERY_SAVER      = 2,21	SSAM_TMP_PROFILE_BETTER_PERFORMANCE = 3,22	SSAM_TMP_PROFILE_BEST_PERFORMANCE   = 4,23};24 25// Enum for the fan profile sent to the FAN module. This fan profile is26// only sent to the EC if the 'has_fan' property is set. The integers are27// not a typo, they differ from the performance profile indices.28enum ssam_fan_profile {29	SSAM_FAN_PROFILE_NORMAL             = 2,30	SSAM_FAN_PROFILE_BATTERY_SAVER      = 1,31	SSAM_FAN_PROFILE_BETTER_PERFORMANCE = 3,32	SSAM_FAN_PROFILE_BEST_PERFORMANCE   = 4,33};34 35struct ssam_tmp_profile_info {36	__le32 profile;37	__le16 unknown1;38	__le16 unknown2;39} __packed;40 41struct ssam_platform_profile_device {42	struct ssam_device *sdev;43	struct platform_profile_handler handler;44	bool has_fan;45};46 47SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_profile_get, struct ssam_tmp_profile_info, {48	.target_category = SSAM_SSH_TC_TMP,49	.command_id      = 0x02,50});51 52SSAM_DEFINE_SYNC_REQUEST_CL_W(__ssam_tmp_profile_set, __le32, {53	.target_category = SSAM_SSH_TC_TMP,54	.command_id      = 0x03,55});56 57SSAM_DEFINE_SYNC_REQUEST_W(__ssam_fan_profile_set, u8, {58	.target_category = SSAM_SSH_TC_FAN,59	.target_id = SSAM_SSH_TID_SAM,60	.command_id = 0x0e,61	.instance_id = 0x01,62});63 64static int ssam_tmp_profile_get(struct ssam_device *sdev, enum ssam_tmp_profile *p)65{66	struct ssam_tmp_profile_info info;67	int status;68 69	status = ssam_retry(__ssam_tmp_profile_get, sdev, &info);70	if (status < 0)71		return status;72 73	*p = le32_to_cpu(info.profile);74	return 0;75}76 77static int ssam_tmp_profile_set(struct ssam_device *sdev, enum ssam_tmp_profile p)78{79	const __le32 profile_le = cpu_to_le32(p);80 81	return ssam_retry(__ssam_tmp_profile_set, sdev, &profile_le);82}83 84static int ssam_fan_profile_set(struct ssam_device *sdev, enum ssam_fan_profile p)85{86	const u8 profile = p;87 88	return ssam_retry(__ssam_fan_profile_set, sdev->ctrl, &profile);89}90 91static int convert_ssam_tmp_to_profile(struct ssam_device *sdev, enum ssam_tmp_profile p)92{93	switch (p) {94	case SSAM_TMP_PROFILE_NORMAL:95		return PLATFORM_PROFILE_BALANCED;96 97	case SSAM_TMP_PROFILE_BATTERY_SAVER:98		return PLATFORM_PROFILE_LOW_POWER;99 100	case SSAM_TMP_PROFILE_BETTER_PERFORMANCE:101		return PLATFORM_PROFILE_BALANCED_PERFORMANCE;102 103	case SSAM_TMP_PROFILE_BEST_PERFORMANCE:104		return PLATFORM_PROFILE_PERFORMANCE;105 106	default:107		dev_err(&sdev->dev, "invalid performance profile: %d", p);108		return -EINVAL;109	}110}111 112 113static int convert_profile_to_ssam_tmp(struct ssam_device *sdev, enum platform_profile_option p)114{115	switch (p) {116	case PLATFORM_PROFILE_LOW_POWER:117		return SSAM_TMP_PROFILE_BATTERY_SAVER;118 119	case PLATFORM_PROFILE_BALANCED:120		return SSAM_TMP_PROFILE_NORMAL;121 122	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:123		return SSAM_TMP_PROFILE_BETTER_PERFORMANCE;124 125	case PLATFORM_PROFILE_PERFORMANCE:126		return SSAM_TMP_PROFILE_BEST_PERFORMANCE;127 128	default:129		/* This should have already been caught by platform_profile_store(). */130		WARN(true, "unsupported platform profile");131		return -EOPNOTSUPP;132	}133}134 135static int convert_profile_to_ssam_fan(struct ssam_device *sdev, enum platform_profile_option p)136{137	switch (p) {138	case PLATFORM_PROFILE_LOW_POWER:139		return SSAM_FAN_PROFILE_BATTERY_SAVER;140 141	case PLATFORM_PROFILE_BALANCED:142		return SSAM_FAN_PROFILE_NORMAL;143 144	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:145		return SSAM_FAN_PROFILE_BETTER_PERFORMANCE;146 147	case PLATFORM_PROFILE_PERFORMANCE:148		return SSAM_FAN_PROFILE_BEST_PERFORMANCE;149 150	default:151		/* This should have already been caught by platform_profile_store(). */152		WARN(true, "unsupported platform profile");153		return -EOPNOTSUPP;154	}155}156 157static int ssam_platform_profile_get(struct platform_profile_handler *pprof,158				     enum platform_profile_option *profile)159{160	struct ssam_platform_profile_device *tpd;161	enum ssam_tmp_profile tp;162	int status;163 164	tpd = container_of(pprof, struct ssam_platform_profile_device, handler);165 166	status = ssam_tmp_profile_get(tpd->sdev, &tp);167	if (status)168		return status;169 170	status = convert_ssam_tmp_to_profile(tpd->sdev, tp);171	if (status < 0)172		return status;173 174	*profile = status;175	return 0;176}177 178static int ssam_platform_profile_set(struct platform_profile_handler *pprof,179				     enum platform_profile_option profile)180{181	struct ssam_platform_profile_device *tpd;182	int tp;183 184	tpd = container_of(pprof, struct ssam_platform_profile_device, handler);185 186	tp = convert_profile_to_ssam_tmp(tpd->sdev, profile);187	if (tp < 0)188		return tp;189 190	tp = ssam_tmp_profile_set(tpd->sdev, tp);191	if (tp < 0)192		return tp;193 194	if (tpd->has_fan) {195		tp = convert_profile_to_ssam_fan(tpd->sdev, profile);196		if (tp < 0)197			return tp;198		tp = ssam_fan_profile_set(tpd->sdev, tp);199	}200 201	return tp;202}203 204static int surface_platform_profile_probe(struct ssam_device *sdev)205{206	struct ssam_platform_profile_device *tpd;207 208	tpd = devm_kzalloc(&sdev->dev, sizeof(*tpd), GFP_KERNEL);209	if (!tpd)210		return -ENOMEM;211 212	tpd->sdev = sdev;213 214	tpd->handler.profile_get = ssam_platform_profile_get;215	tpd->handler.profile_set = ssam_platform_profile_set;216 217	tpd->has_fan = device_property_read_bool(&sdev->dev, "has_fan");218 219	set_bit(PLATFORM_PROFILE_LOW_POWER, tpd->handler.choices);220	set_bit(PLATFORM_PROFILE_BALANCED, tpd->handler.choices);221	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, tpd->handler.choices);222	set_bit(PLATFORM_PROFILE_PERFORMANCE, tpd->handler.choices);223 224	return platform_profile_register(&tpd->handler);225}226 227static void surface_platform_profile_remove(struct ssam_device *sdev)228{229	platform_profile_remove();230}231 232static const struct ssam_device_id ssam_platform_profile_match[] = {233	{ SSAM_SDEV(TMP, SAM, 0x00, 0x01) },234	{ },235};236MODULE_DEVICE_TABLE(ssam, ssam_platform_profile_match);237 238static struct ssam_device_driver surface_platform_profile = {239	.probe = surface_platform_profile_probe,240	.remove = surface_platform_profile_remove,241	.match_table = ssam_platform_profile_match,242	.driver = {243		.name = "surface_platform_profile",244		.probe_type = PROBE_PREFER_ASYNCHRONOUS,245	},246};247module_ssam_device_driver(surface_platform_profile);248 249MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");250MODULE_DESCRIPTION("Platform Profile Support for Surface System Aggregator Module");251MODULE_LICENSE("GPL");252