Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Surface Platform Profile / Performance Mode driver for Surface System
0004  * Aggregator Module (thermal subsystem).
0005  *
0006  * Copyright (C) 2021-2022 Maximilian Luz <luzmaximilian@gmail.com>
0007  */
0008 
0009 #include <asm/unaligned.h>
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/platform_profile.h>
0013 #include <linux/types.h>
0014 
0015 #include <linux/surface_aggregator/device.h>
0016 
0017 enum ssam_tmp_profile {
0018     SSAM_TMP_PROFILE_NORMAL             = 1,
0019     SSAM_TMP_PROFILE_BATTERY_SAVER      = 2,
0020     SSAM_TMP_PROFILE_BETTER_PERFORMANCE = 3,
0021     SSAM_TMP_PROFILE_BEST_PERFORMANCE   = 4,
0022 };
0023 
0024 struct ssam_tmp_profile_info {
0025     __le32 profile;
0026     __le16 unknown1;
0027     __le16 unknown2;
0028 } __packed;
0029 
0030 struct ssam_tmp_profile_device {
0031     struct ssam_device *sdev;
0032     struct platform_profile_handler handler;
0033 };
0034 
0035 SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_profile_get, struct ssam_tmp_profile_info, {
0036     .target_category = SSAM_SSH_TC_TMP,
0037     .command_id      = 0x02,
0038 });
0039 
0040 SSAM_DEFINE_SYNC_REQUEST_CL_W(__ssam_tmp_profile_set, __le32, {
0041     .target_category = SSAM_SSH_TC_TMP,
0042     .command_id      = 0x03,
0043 });
0044 
0045 static int ssam_tmp_profile_get(struct ssam_device *sdev, enum ssam_tmp_profile *p)
0046 {
0047     struct ssam_tmp_profile_info info;
0048     int status;
0049 
0050     status = ssam_retry(__ssam_tmp_profile_get, sdev, &info);
0051     if (status < 0)
0052         return status;
0053 
0054     *p = le32_to_cpu(info.profile);
0055     return 0;
0056 }
0057 
0058 static int ssam_tmp_profile_set(struct ssam_device *sdev, enum ssam_tmp_profile p)
0059 {
0060     __le32 profile_le = cpu_to_le32(p);
0061 
0062     return ssam_retry(__ssam_tmp_profile_set, sdev, &profile_le);
0063 }
0064 
0065 static int convert_ssam_to_profile(struct ssam_device *sdev, enum ssam_tmp_profile p)
0066 {
0067     switch (p) {
0068     case SSAM_TMP_PROFILE_NORMAL:
0069         return PLATFORM_PROFILE_BALANCED;
0070 
0071     case SSAM_TMP_PROFILE_BATTERY_SAVER:
0072         return PLATFORM_PROFILE_LOW_POWER;
0073 
0074     case SSAM_TMP_PROFILE_BETTER_PERFORMANCE:
0075         return PLATFORM_PROFILE_BALANCED_PERFORMANCE;
0076 
0077     case SSAM_TMP_PROFILE_BEST_PERFORMANCE:
0078         return PLATFORM_PROFILE_PERFORMANCE;
0079 
0080     default:
0081         dev_err(&sdev->dev, "invalid performance profile: %d", p);
0082         return -EINVAL;
0083     }
0084 }
0085 
0086 static int convert_profile_to_ssam(struct ssam_device *sdev, enum platform_profile_option p)
0087 {
0088     switch (p) {
0089     case PLATFORM_PROFILE_LOW_POWER:
0090         return SSAM_TMP_PROFILE_BATTERY_SAVER;
0091 
0092     case PLATFORM_PROFILE_BALANCED:
0093         return SSAM_TMP_PROFILE_NORMAL;
0094 
0095     case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
0096         return SSAM_TMP_PROFILE_BETTER_PERFORMANCE;
0097 
0098     case PLATFORM_PROFILE_PERFORMANCE:
0099         return SSAM_TMP_PROFILE_BEST_PERFORMANCE;
0100 
0101     default:
0102         /* This should have already been caught by platform_profile_store(). */
0103         WARN(true, "unsupported platform profile");
0104         return -EOPNOTSUPP;
0105     }
0106 }
0107 
0108 static int ssam_platform_profile_get(struct platform_profile_handler *pprof,
0109                      enum platform_profile_option *profile)
0110 {
0111     struct ssam_tmp_profile_device *tpd;
0112     enum ssam_tmp_profile tp;
0113     int status;
0114 
0115     tpd = container_of(pprof, struct ssam_tmp_profile_device, handler);
0116 
0117     status = ssam_tmp_profile_get(tpd->sdev, &tp);
0118     if (status)
0119         return status;
0120 
0121     status = convert_ssam_to_profile(tpd->sdev, tp);
0122     if (status < 0)
0123         return status;
0124 
0125     *profile = status;
0126     return 0;
0127 }
0128 
0129 static int ssam_platform_profile_set(struct platform_profile_handler *pprof,
0130                      enum platform_profile_option profile)
0131 {
0132     struct ssam_tmp_profile_device *tpd;
0133     int tp;
0134 
0135     tpd = container_of(pprof, struct ssam_tmp_profile_device, handler);
0136 
0137     tp = convert_profile_to_ssam(tpd->sdev, profile);
0138     if (tp < 0)
0139         return tp;
0140 
0141     return ssam_tmp_profile_set(tpd->sdev, tp);
0142 }
0143 
0144 static int surface_platform_profile_probe(struct ssam_device *sdev)
0145 {
0146     struct ssam_tmp_profile_device *tpd;
0147 
0148     tpd = devm_kzalloc(&sdev->dev, sizeof(*tpd), GFP_KERNEL);
0149     if (!tpd)
0150         return -ENOMEM;
0151 
0152     tpd->sdev = sdev;
0153 
0154     tpd->handler.profile_get = ssam_platform_profile_get;
0155     tpd->handler.profile_set = ssam_platform_profile_set;
0156 
0157     set_bit(PLATFORM_PROFILE_LOW_POWER, tpd->handler.choices);
0158     set_bit(PLATFORM_PROFILE_BALANCED, tpd->handler.choices);
0159     set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, tpd->handler.choices);
0160     set_bit(PLATFORM_PROFILE_PERFORMANCE, tpd->handler.choices);
0161 
0162     platform_profile_register(&tpd->handler);
0163     return 0;
0164 }
0165 
0166 static void surface_platform_profile_remove(struct ssam_device *sdev)
0167 {
0168     platform_profile_remove();
0169 }
0170 
0171 static const struct ssam_device_id ssam_platform_profile_match[] = {
0172     { SSAM_SDEV(TMP, 0x01, 0x00, 0x01) },
0173     { },
0174 };
0175 MODULE_DEVICE_TABLE(ssam, ssam_platform_profile_match);
0176 
0177 static struct ssam_device_driver surface_platform_profile = {
0178     .probe = surface_platform_profile_probe,
0179     .remove = surface_platform_profile_remove,
0180     .match_table = ssam_platform_profile_match,
0181     .driver = {
0182         .name = "surface_platform_profile",
0183         .probe_type = PROBE_PREFER_ASYNCHRONOUS,
0184     },
0185 };
0186 module_ssam_device_driver(surface_platform_profile);
0187 
0188 MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
0189 MODULE_DESCRIPTION("Platform Profile Support for Surface System Aggregator Module");
0190 MODULE_LICENSE("GPL");