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 27 bool cbe_cpufreq_has_pmi = false; 28 EXPORT_SYMBOL_GPL(cbe_cpufreq_has_pmi); 29 30 /* 31 * hardware specific functions 32 */ 33 34 int 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 } 61 EXPORT_SYMBOL_GPL(cbe_cpufreq_set_pmode_pmi); 62 63 64 static 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 98 static struct pmi_handler cbe_pmi_handler = { 99 .type = PMI_TYPE_FREQ_CHANGE, 100 .handle_pmi_message = cbe_cpufreq_handle_pmi, 101 }; 102 103 void 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 } 125 EXPORT_SYMBOL_GPL(cbe_cpufreq_pmi_policy_init); 126 127 void 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 } 136 EXPORT_SYMBOL_GPL(cbe_cpufreq_pmi_policy_exit); 137 138 void cbe_cpufreq_pmi_init(void) 139 { 140 if (!pmi_register_handler(&cbe_pmi_handler)) 141 cbe_cpufreq_has_pmi = true; 142 } 143 EXPORT_SYMBOL_GPL(cbe_cpufreq_pmi_init); 144 145 void cbe_cpufreq_pmi_exit(void) 146 { 147 pmi_unregister_handler(&cbe_pmi_handler); 148 cbe_cpufreq_has_pmi = false; 149 } 150 EXPORT_SYMBOL_GPL(cbe_cpufreq_pmi_exit); 151