Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/cpufreq/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 8 kB image not shown  

Quelle  loongson3_cpufreq.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * CPUFreq driver for the Loongson-3 processors.
 *
 * All revisions of Loongson-3 processor support cpu_has_scalefreq feature.
 *
 * Author: Huacai Chen <chenhuacai@loongson.cn>
 * Copyright (C) 2024 Loongson Technology Corporation Limited
 */

#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/units.h>

#include <asm/idle.h>
#include <asm/loongarch.h>
#include <asm/loongson.h>

/* Message */
union smc_message {
 u32 value;
 struct {
  u32 id  : 4;
  u32 info : 4;
  u32 val  : 16;
  u32 cmd  : 6;
  u32 extra : 1;
  u32 complete : 1;
 };
};

/* Command return values */
#define CMD_OK    0 /* No error */
#define CMD_ERROR   1 /* Regular error */
#define CMD_NOCMD   2 /* Command does not support */
#define CMD_INVAL   3 /* Invalid Parameter */

/* Version commands */
/*
 * CMD_GET_VERSION - Get interface version
 * Input: none
 * Output: version
 */

#define CMD_GET_VERSION   0x1

/* Feature commands */
/*
 * CMD_GET_FEATURE - Get feature state
 * Input: feature ID
 * Output: feature flag
 */

#define CMD_GET_FEATURE   0x2

/*
 * CMD_SET_FEATURE - Set feature state
 * Input: feature ID, feature flag
 * output: none
 */

#define CMD_SET_FEATURE   0x3

/* Feature IDs */
#define FEATURE_SENSOR   0
#define FEATURE_FAN   1
#define FEATURE_DVFS   2

/* Sensor feature flags */
#define FEATURE_SENSOR_ENABLE  BIT(0)
#define FEATURE_SENSOR_SAMPLE  BIT(1)

/* Fan feature flags */
#define FEATURE_FAN_ENABLE  BIT(0)
#define FEATURE_FAN_AUTO  BIT(1)

/* DVFS feature flags */
#define FEATURE_DVFS_ENABLE  BIT(0)
#define FEATURE_DVFS_BOOST  BIT(1)
#define FEATURE_DVFS_AUTO  BIT(2)
#define FEATURE_DVFS_SINGLE_BOOST BIT(3)

/* Sensor commands */
/*
 * CMD_GET_SENSOR_NUM - Get number of sensors
 * Input: none
 * Output: number
 */

#define CMD_GET_SENSOR_NUM  0x4

/*
 * CMD_GET_SENSOR_STATUS - Get sensor status
 * Input: sensor ID, type
 * Output: sensor status
 */

#define CMD_GET_SENSOR_STATUS  0x5

/* Sensor types */
#define SENSOR_INFO_TYPE  0
#define SENSOR_INFO_TYPE_TEMP  1

/* Fan commands */
/*
 * CMD_GET_FAN_NUM - Get number of fans
 * Input: none
 * Output: number
 */

#define CMD_GET_FAN_NUM   0x6

/*
 * CMD_GET_FAN_INFO - Get fan status
 * Input: fan ID, type
 * Output: fan info
 */

#define CMD_GET_FAN_INFO  0x7

/*
 * CMD_SET_FAN_INFO - Set fan status
 * Input: fan ID, type, value
 * Output: none
 */

#define CMD_SET_FAN_INFO  0x8

/* Fan types */
#define FAN_INFO_TYPE_LEVEL  0

/* DVFS commands */
/*
 * CMD_GET_FREQ_LEVEL_NUM - Get number of freq levels
 * Input: CPU ID
 * Output: number
 */

#define CMD_GET_FREQ_LEVEL_NUM  0x9

/*
 * CMD_GET_FREQ_BOOST_LEVEL - Get the first boost level
 * Input: CPU ID
 * Output: number
 */

#define CMD_GET_FREQ_BOOST_LEVEL 0x10

/*
 * CMD_GET_FREQ_LEVEL_INFO - Get freq level info
 * Input: CPU ID, level ID
 * Output: level info
 */

#define CMD_GET_FREQ_LEVEL_INFO  0x11

/*
 * CMD_GET_FREQ_INFO - Get freq info
 * Input: CPU ID, type
 * Output: freq info
 */

#define CMD_GET_FREQ_INFO  0x12

/*
 * CMD_SET_FREQ_INFO - Set freq info
 * Input: CPU ID, type, value
 * Output: none
 */

#define CMD_SET_FREQ_INFO  0x13

/* Freq types */
#define FREQ_INFO_TYPE_FREQ  0
#define FREQ_INFO_TYPE_LEVEL  1

#define FREQ_MAX_LEVEL   16

struct loongson3_freq_data {
 unsigned int def_freq_level;
 struct cpufreq_frequency_table table[];
};

static struct mutex cpufreq_mutex[MAX_PACKAGES];
static struct cpufreq_driver loongson3_cpufreq_driver;
static DEFINE_PER_CPU(struct loongson3_freq_data *, freq_data);

static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 extra)
{
 int retries;
 unsigned int cpu = raw_smp_processor_id();
 unsigned int package = cpu_data[cpu].package;
 union smc_message msg, last;

 mutex_lock(&cpufreq_mutex[package]);

 last.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);
 if (!last.complete) {
  mutex_unlock(&cpufreq_mutex[package]);
  return -EPERM;
 }

 msg.id  = id;
 msg.info = info;
 msg.cmd  = cmd;
 msg.val  = val;
 msg.extra = extra;
 msg.complete = 0;

 iocsr_write32(msg.value, LOONGARCH_IOCSR_SMCMBX);
 iocsr_write32(iocsr_read32(LOONGARCH_IOCSR_MISC_FUNC) | IOCSR_MISC_FUNC_SOFT_INT,
        LOONGARCH_IOCSR_MISC_FUNC);

 for (retries = 0; retries < 10000; retries++) {
  msg.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);
  if (msg.complete)
   break;

  usleep_range(8, 12);
 }

 if (!msg.complete || msg.cmd != CMD_OK) {
  mutex_unlock(&cpufreq_mutex[package]);
  return -EPERM;
 }

 mutex_unlock(&cpufreq_mutex[package]);

 return msg.val;
}

static unsigned int loongson3_cpufreq_get(unsigned int cpu)
{
 int ret;

 ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0);

 return ret * KILO;
}

static int loongson3_cpufreq_target(struct cpufreq_policy *policy, unsigned int index)
{
 int ret;

 ret = do_service_request(cpu_data[policy->cpu].core,
     FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, index, 0);

 return (ret >= 0) ? 0 : ret;
}

static int configure_freq_table(int cpu)
{
 int i, ret, boost_level, max_level, freq_level;
 struct platform_device *pdev = cpufreq_get_driver_data();
 struct loongson3_freq_data *data;

 if (per_cpu(freq_data, cpu))
  return 0;

 ret = do_service_request(cpu, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0);
 if (ret < 0)
  return ret;
 max_level = ret;

 ret = do_service_request(cpu, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0);
 if (ret < 0)
  return ret;
 boost_level = ret;

 freq_level = min(max_level, FREQ_MAX_LEVEL);
 data = devm_kzalloc(&pdev->dev, struct_size(data, table, freq_level + 1), GFP_KERNEL);
 if (!data)
  return -ENOMEM;

 data->def_freq_level = boost_level - 1;

 for (i = 0; i < freq_level; i++) {
  ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0);
  if (ret < 0) {
   devm_kfree(&pdev->dev, data);
   return ret;
  }

  data->table[i].frequency = ret * KILO;
  data->table[i].flags = (i >= boost_level) ? CPUFREQ_BOOST_FREQ : 0;
 }

 data->table[freq_level].flags = 0;
 data->table[freq_level].frequency = CPUFREQ_TABLE_END;

 per_cpu(freq_data, cpu) = data;

 return 0;
}

static int loongson3_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
 int i, ret, cpu = policy->cpu;

 ret = configure_freq_table(cpu);
 if (ret < 0)
  return ret;

 policy->cpuinfo.transition_latency = 10000;
 policy->freq_table = per_cpu(freq_data, cpu)->table;
 policy->suspend_freq = policy->freq_table[per_cpu(freq_data, cpu)->def_freq_level].frequency;
 cpumask_copy(policy->cpus, topology_sibling_cpumask(cpu));

 for_each_cpu(i, policy->cpus) {
  if (i != cpu)
   per_cpu(freq_data, i) = per_cpu(freq_data, cpu);
 }

 return 0;
}

static void loongson3_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{
 int cpu = policy->cpu;

 loongson3_cpufreq_target(policy, per_cpu(freq_data, cpu)->def_freq_level);
}

static int loongson3_cpufreq_cpu_online(struct cpufreq_policy *policy)
{
 return 0;
}

static int loongson3_cpufreq_cpu_offline(struct cpufreq_policy *policy)
{
 return 0;
}

static struct cpufreq_driver loongson3_cpufreq_driver = {
 .name = "loongson3",
 .flags = CPUFREQ_CONST_LOOPS,
 .init = loongson3_cpufreq_cpu_init,
 .exit = loongson3_cpufreq_cpu_exit,
 .online = loongson3_cpufreq_cpu_online,
 .offline = loongson3_cpufreq_cpu_offline,
 .get = loongson3_cpufreq_get,
 .target_index = loongson3_cpufreq_target,
 .verify = cpufreq_generic_frequency_table_verify,
 .set_boost = cpufreq_boost_set_sw,
 .suspend = cpufreq_generic_suspend,
};

static int loongson3_cpufreq_probe(struct platform_device *pdev)
{
 int i, ret;

 for (i = 0; i < MAX_PACKAGES; i++) {
  ret = devm_mutex_init(&pdev->dev, &cpufreq_mutex[i]);
  if (ret)
   return ret;
 }

 ret = do_service_request(0, 0, CMD_GET_VERSION, 0, 0);
 if (ret <= 0)
  return -EPERM;

 ret = do_service_request(FEATURE_DVFS, 0, CMD_SET_FEATURE,
     FEATURE_DVFS_ENABLE | FEATURE_DVFS_BOOST, 0);
 if (ret < 0)
  return -EPERM;

 loongson3_cpufreq_driver.driver_data = pdev;

 ret = cpufreq_register_driver(&loongson3_cpufreq_driver);
 if (ret)
  return ret;

 pr_info("cpufreq: Loongson-3 CPU frequency driver.\n");

 return 0;
}

static void loongson3_cpufreq_remove(struct platform_device *pdev)
{
 cpufreq_unregister_driver(&loongson3_cpufreq_driver);
}

static struct platform_device_id cpufreq_id_table[] = {
 { "loongson3_cpufreq", },
 { /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, cpufreq_id_table);

static struct platform_driver loongson3_platform_driver = {
 .driver = {
  .name = "loongson3_cpufreq",
 },
 .id_table = cpufreq_id_table,
 .probe = loongson3_cpufreq_probe,
 .remove = loongson3_cpufreq_remove,
};
module_platform_driver(loongson3_platform_driver);

MODULE_AUTHOR("Huacai Chen ");
MODULE_DESCRIPTION("CPUFreq driver for Loongson-3 processors");
MODULE_LICENSE("GPL");

Messung V0.5
C=92 H=89 G=90

¤ Dauer der Verarbeitung: 0.21 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.