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