1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * pmi backend for the cbe_cpufreq driver 4 * 5 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007 6 * 7 * Author: Christian Krafft <krafft@de.ibm.com> 8 */ 9 10#include <linux/kernel.h> 11#include <linux/types.h> 12#include <linux/timer.h> 13#include <linux/init.h> 14#include <linux/pm_qos.h> 15#include <linux/slab.h> 16 17#include <asm/processor.h> 18#include <asm/pmi.h> 19#include <asm/cell-regs.h> 20 21#ifdef DEBUG 22#include <asm/time.h> 23#endif 24 25#include "ppc_cbe_cpufreq.h" 26 27bool cbe_cpufreq_has_pmi = false; 28EXPORT_SYMBOL_GPL(cbe_cpufreq_has_pmi); 29 30/* 31 * hardware specific functions 32 */ 33 34int cbe_cpufreq_set_pmode_pmi(int cpu, unsigned int pmode) 35{ 36 int ret; 37 pmi_message_t pmi_msg; 38#ifdef DEBUG 39 long time; 40#endif 41 pmi_msg.type = PMI_TYPE_FREQ_CHANGE; 42 pmi_msg.data1 = cbe_cpu_to_node(cpu); 43 pmi_msg.data2 = pmode; 44 45#ifdef DEBUG 46 time = jiffies; 47#endif 48 pmi_send_message(pmi_msg); 49 50#ifdef DEBUG 51 time = jiffies - time; 52 time = jiffies_to_msecs(time); 53 pr_debug("had to wait %lu ms for a transition using " \ 54 "PMI\n", time); 55#endif 56 ret = pmi_msg.data2; 57 pr_debug("PMI returned slow mode %d\n", ret); 58 59 return ret; 60} 61EXPORT_SYMBOL_GPL(cbe_cpufreq_set_pmode_pmi); 62 63 64static void cbe_cpufreq_handle_pmi(pmi_message_t pmi_msg) 65{ 66 struct cpufreq_policy *policy; 67 struct freq_qos_request *req; 68 u8 node, slow_mode; 69 int cpu, ret; 70 71 BUG_ON(pmi_msg.type != PMI_TYPE_FREQ_CHANGE); 72 73 node = pmi_msg.data1; 74 slow_mode = pmi_msg.data2; 75 76 cpu = cbe_node_to_cpu(node); 77 78 pr_debug("cbe_handle_pmi: node: %d max_freq: %d\n", node, slow_mode); 79 80 policy = cpufreq_cpu_get(cpu); 81 if (!policy) { 82 pr_warn("cpufreq policy not found cpu%d\n", cpu); 83 return; 84 } 85 86 req = policy->driver_data; 87 88 ret = freq_qos_update_request(req, 89 policy->freq_table[slow_mode].frequency); 90 if (ret < 0) 91 pr_warn("Failed to update freq constraint: %d\n", ret); 92 else 93 pr_debug("limiting node %d to slow mode %d\n", node, slow_mode); 94 95 cpufreq_cpu_put(policy); 96} 97 98static struct pmi_handler cbe_pmi_handler = { 99 .type = PMI_TYPE_FREQ_CHANGE, 100 .handle_pmi_message = cbe_cpufreq_handle_pmi, 101}; 102 103void cbe_cpufreq_pmi_policy_init(struct cpufreq_policy *policy) 104{ 105 struct freq_qos_request *req; 106 int ret; 107 108 if (!cbe_cpufreq_has_pmi) 109 return; 110 111 req = kzalloc(sizeof(*req), GFP_KERNEL); 112 if (!req) 113 return; 114 115 ret = freq_qos_add_request(&policy->constraints, req, FREQ_QOS_MAX, 116 policy->freq_table[0].frequency); 117 if (ret < 0) { 118 pr_err("Failed to add freq constraint (%d)\n", ret); 119 kfree(req); 120 return; 121 } 122 123 policy->driver_data = req; 124} 125EXPORT_SYMBOL_GPL(cbe_cpufreq_pmi_policy_init); 126 127void cbe_cpufreq_pmi_policy_exit(struct cpufreq_policy *policy) 128{ 129 struct freq_qos_request *req = policy->driver_data; 130 131 if (cbe_cpufreq_has_pmi) { 132 freq_qos_remove_request(req); 133 kfree(req); 134 } 135} 136EXPORT_SYMBOL_GPL(cbe_cpufreq_pmi_policy_exit); 137 138void cbe_cpufreq_pmi_init(void) 139{ 140 if (!pmi_register_handler(&cbe_pmi_handler)) 141 cbe_cpufreq_has_pmi = true; 142} 143EXPORT_SYMBOL_GPL(cbe_cpufreq_pmi_init); 144 145void cbe_cpufreq_pmi_exit(void) 146{ 147 pmi_unregister_handler(&cbe_pmi_handler); 148 cbe_cpufreq_has_pmi = false; 149} 150EXPORT_SYMBOL_GPL(cbe_cpufreq_pmi_exit); 151