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 17 #include <asm/processor.h> 18 #include <asm/prom.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 dev_pm_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 = dev_pm_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 dev_pm_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 = dev_pm_qos_add_request(get_cpu_device(policy->cpu), req, 117 DEV_PM_QOS_MAX_FREQUENCY, 118 policy->freq_table[0].frequency); 119 if (ret < 0) { 120 pr_err("Failed to add freq constraint (%d)\n", ret); 121 kfree(req); 122 return; 123 } 124 125 policy->driver_data = req; 126 } 127 EXPORT_SYMBOL_GPL(cbe_cpufreq_pmi_policy_init); 128 129 void cbe_cpufreq_pmi_policy_exit(struct cpufreq_policy *policy) 130 { 131 struct dev_pm_qos_request *req = policy->driver_data; 132 133 if (cbe_cpufreq_has_pmi) { 134 dev_pm_qos_remove_request(req); 135 kfree(req); 136 } 137 } 138 EXPORT_SYMBOL_GPL(cbe_cpufreq_pmi_policy_exit); 139 140 void cbe_cpufreq_pmi_init(void) 141 { 142 if (!pmi_register_handler(&cbe_pmi_handler)) 143 cbe_cpufreq_has_pmi = true; 144 } 145 EXPORT_SYMBOL_GPL(cbe_cpufreq_pmi_init); 146 147 void cbe_cpufreq_pmi_exit(void) 148 { 149 pmi_unregister_handler(&cbe_pmi_handler); 150 cbe_cpufreq_has_pmi = false; 151 } 152 EXPORT_SYMBOL_GPL(cbe_cpufreq_pmi_exit); 153