blob: 66f67ce5d938f5f9dcecd3d86a609da48d6a648d [file] [log] [blame]
Ram Chandrasekar47f9b502018-02-05 15:04:36 -07001/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
Ram Chandrasekara5911a32017-03-13 11:45:30 -06002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
14
15#include <linux/module.h>
16#include <linux/slab.h>
17#include <linux/thermal.h>
18#include <linux/of.h>
19#include <linux/of_device.h>
20#include <linux/of_irq.h>
21#include <linux/platform_device.h>
22#include <linux/sched.h>
23#include <linux/io.h>
24#include <linux/interrupt.h>
25#include <linux/timer.h>
26#include <linux/pm_opp.h>
27#include <linux/cpu_cooling.h>
28#include <linux/atomic.h>
Ram Chandrasekar6d393b92017-07-21 15:37:33 -060029#include <linux/regulator/consumer.h>
Ram Chandrasekara5911a32017-03-13 11:45:30 -060030
31#include <asm/smp_plat.h>
32#include <asm/cacheflush.h>
33
34#include <soc/qcom/scm.h>
35
36#include "../thermal_core.h"
Ram Chandrasekar1d1adbb2017-05-18 17:44:50 -060037#include "lmh_dbg.h"
Ram Chandrasekara5911a32017-03-13 11:45:30 -060038
Ram Chandrasekar2a3dfb22017-05-04 16:32:20 -060039#define CREATE_TRACE_POINTS
40#include <trace/events/lmh.h>
41
Ram Chandrasekara5911a32017-03-13 11:45:30 -060042#define LIMITS_DCVSH 0x10
43#define LIMITS_PROFILE_CHANGE 0x01
44#define LIMITS_NODE_DCVS 0x44435653
45
46#define LIMITS_SUB_FN_THERMAL 0x54484D4C
47#define LIMITS_SUB_FN_CRNT 0x43524E54
48#define LIMITS_SUB_FN_REL 0x52454C00
49#define LIMITS_SUB_FN_BCL 0x42434C00
50#define LIMITS_SUB_FN_GENERAL 0x47454E00
51
52#define LIMITS_ALGO_MODE_ENABLE 0x454E424C
53
54#define LIMITS_HI_THRESHOLD 0x48494748
55#define LIMITS_LOW_THRESHOLD 0x4C4F5700
56#define LIMITS_ARM_THRESHOLD 0x41524D00
57
58#define LIMITS_CLUSTER_0 0x6370302D
59#define LIMITS_CLUSTER_1 0x6370312D
60
Ram Chandrasekara30d0292017-11-13 14:17:39 -070061#define LIMITS_FREQ_CAP 0x46434150
Ram Chandrasekara5911a32017-03-13 11:45:30 -060062
63#define LIMITS_TEMP_DEFAULT 75000
Ram Chandrasekar83c0a532017-05-02 16:06:58 -060064#define LIMITS_TEMP_HIGH_THRESH_MAX 120000
Ram Chandrasekara5911a32017-03-13 11:45:30 -060065#define LIMITS_LOW_THRESHOLD_OFFSET 500
66#define LIMITS_POLLING_DELAY_MS 10
Ram Chandrasekar2a3dfb22017-05-04 16:32:20 -060067#define LIMITS_CLUSTER_0_REQ 0x17D43704
68#define LIMITS_CLUSTER_1_REQ 0x17D45F04
69#define LIMITS_CLUSTER_0_INT_CLR 0x17D78808
70#define LIMITS_CLUSTER_1_INT_CLR 0x17D70808
Ram Chandrasekara5911a32017-03-13 11:45:30 -060071#define LIMITS_CLUSTER_0_MIN_FREQ 0x17D78BC0
72#define LIMITS_CLUSTER_1_MIN_FREQ 0x17D70BC0
73#define dcvsh_get_frequency(_val, _max) do { \
74 _max = (_val) & 0x3FF; \
75 _max *= 19200; \
76} while (0)
77#define FREQ_KHZ_TO_HZ(_val) ((_val) * 1000)
78#define FREQ_HZ_TO_KHZ(_val) ((_val) / 1000)
79
80enum lmh_hw_trips {
81 LIMITS_TRIP_ARM,
82 LIMITS_TRIP_HI,
83 LIMITS_TRIP_MAX,
84};
85
Ram Chandrasekar2d8a9b62017-04-05 17:38:26 -060086struct __limits_cdev_data {
87 struct thermal_cooling_device *cdev;
88 u32 max_freq;
89 u32 min_freq;
90};
91
Ram Chandrasekara5911a32017-03-13 11:45:30 -060092struct limits_dcvs_hw {
93 char sensor_name[THERMAL_NAME_LENGTH];
94 uint32_t affinity;
95 uint32_t temp_limits[LIMITS_TRIP_MAX];
96 int irq_num;
97 void *osm_hw_reg;
98 void *int_clr_reg;
99 void *min_freq_reg;
100 cpumask_t core_map;
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700101 struct delayed_work freq_poll_work;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600102 unsigned long max_freq;
103 unsigned long min_freq;
104 unsigned long hw_freq_limit;
Ram Chandrasekard8fd4be2017-08-04 15:12:31 -0600105 struct device_attribute lmh_freq_attr;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600106 struct list_head list;
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700107 bool is_irq_enabled;
Ram Chandrasekar2d8a9b62017-04-05 17:38:26 -0600108 struct mutex access_lock;
109 struct __limits_cdev_data *cdev_data;
Ram Chandrasekar6d393b92017-07-21 15:37:33 -0600110 struct regulator *isens_reg;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600111};
112
113LIST_HEAD(lmh_dcvs_hw_list);
Ram Chandrasekar3600a182017-05-10 12:47:28 -0600114DEFINE_MUTEX(lmh_dcvs_list_access);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600115
116static int limits_dcvs_get_freq_limits(uint32_t cpu, unsigned long *max_freq,
117 unsigned long *min_freq)
118{
119 unsigned long freq_ceil = UINT_MAX, freq_floor = 0;
120 struct device *cpu_dev = NULL;
121 int ret = 0;
122
123 cpu_dev = get_cpu_device(cpu);
124 if (!cpu_dev) {
125 pr_err("Error in get CPU%d device\n", cpu);
126 return -ENODEV;
127 }
128
129 rcu_read_lock();
130 dev_pm_opp_find_freq_floor(cpu_dev, &freq_ceil);
131 dev_pm_opp_find_freq_ceil(cpu_dev, &freq_floor);
132 rcu_read_unlock();
133
134 *max_freq = freq_ceil / 1000;
135 *min_freq = freq_floor / 1000;
136
137 return ret;
138}
139
140static unsigned long limits_mitigation_notify(struct limits_dcvs_hw *hw)
141{
142 uint32_t val = 0;
143 struct device *cpu_dev = NULL;
144 unsigned long freq_val, max_limit = 0;
145 struct dev_pm_opp *opp_entry;
146
147 val = readl_relaxed(hw->osm_hw_reg);
148 dcvsh_get_frequency(val, max_limit);
149 cpu_dev = get_cpu_device(cpumask_first(&hw->core_map));
150 if (!cpu_dev) {
151 pr_err("Error in get CPU%d device\n",
152 cpumask_first(&hw->core_map));
153 goto notify_exit;
154 }
155
Ram Chandrasekar2a3dfb22017-05-04 16:32:20 -0600156 pr_debug("CPU:%d max value read:%lu\n",
157 cpumask_first(&hw->core_map),
158 max_limit);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600159 freq_val = FREQ_KHZ_TO_HZ(max_limit);
160 rcu_read_lock();
161 opp_entry = dev_pm_opp_find_freq_floor(cpu_dev, &freq_val);
162 /*
163 * Hardware mitigation frequency can be lower than the lowest
164 * possible CPU frequency. In that case freq floor call will
165 * fail with -ERANGE and we need to match to the lowest
166 * frequency using freq_ceil.
167 */
168 if (IS_ERR(opp_entry) && PTR_ERR(opp_entry) == -ERANGE) {
169 opp_entry = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_val);
170 if (IS_ERR(opp_entry))
171 dev_err(cpu_dev, "frequency:%lu. opp error:%ld\n",
172 freq_val, PTR_ERR(opp_entry));
173 }
174 rcu_read_unlock();
175 max_limit = FREQ_HZ_TO_KHZ(freq_val);
176
177 sched_update_cpu_freq_min_max(&hw->core_map, 0, max_limit);
Ram Chandrasekar2a3dfb22017-05-04 16:32:20 -0600178 pr_debug("CPU:%d max limit:%lu\n", cpumask_first(&hw->core_map),
179 max_limit);
180 trace_lmh_dcvs_freq(cpumask_first(&hw->core_map), max_limit);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600181
182notify_exit:
183 hw->hw_freq_limit = max_limit;
184 return max_limit;
185}
186
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700187static void limits_dcvs_poll(struct work_struct *work)
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600188{
189 unsigned long max_limit = 0;
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700190 struct limits_dcvs_hw *hw = container_of(work,
191 struct limits_dcvs_hw,
192 freq_poll_work.work);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600193
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700194 mutex_lock(&hw->access_lock);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600195 if (hw->max_freq == UINT_MAX)
196 limits_dcvs_get_freq_limits(cpumask_first(&hw->core_map),
197 &hw->max_freq, &hw->min_freq);
198 max_limit = limits_mitigation_notify(hw);
199 if (max_limit >= hw->max_freq) {
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600200 writel_relaxed(0xFF, hw->int_clr_reg);
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700201 hw->is_irq_enabled = true;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600202 enable_irq(hw->irq_num);
203 } else {
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700204 mod_delayed_work(system_highpri_wq, &hw->freq_poll_work,
205 msecs_to_jiffies(LIMITS_POLLING_DELAY_MS));
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600206 }
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700207 mutex_unlock(&hw->access_lock);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600208}
209
210static void lmh_dcvs_notify(struct limits_dcvs_hw *hw)
211{
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700212 if (hw->is_irq_enabled) {
213 hw->is_irq_enabled = false;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600214 disable_irq_nosync(hw->irq_num);
215 limits_mitigation_notify(hw);
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700216 mod_delayed_work(system_highpri_wq, &hw->freq_poll_work,
217 msecs_to_jiffies(LIMITS_POLLING_DELAY_MS));
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600218 }
219}
220
221static irqreturn_t lmh_dcvs_handle_isr(int irq, void *data)
222{
223 struct limits_dcvs_hw *hw = data;
224
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700225 mutex_lock(&hw->access_lock);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600226 lmh_dcvs_notify(hw);
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700227 mutex_unlock(&hw->access_lock);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600228
229 return IRQ_HANDLED;
230}
231
232static int limits_dcvs_write(uint32_t node_id, uint32_t fn,
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700233 uint32_t setting, uint32_t val, uint32_t val1,
234 bool enable_val1)
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600235{
236 int ret;
237 struct scm_desc desc_arg;
238 uint32_t *payload = NULL;
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700239 uint32_t payload_len;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600240
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700241 payload_len = ((enable_val1) ? 6 : 5) * sizeof(uint32_t);
242 payload = kzalloc(payload_len, GFP_KERNEL);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600243 if (!payload)
244 return -ENOMEM;
245
246 payload[0] = fn; /* algorithm */
247 payload[1] = 0; /* unused sub-algorithm */
248 payload[2] = setting;
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700249 payload[3] = enable_val1 ? 2 : 1; /* number of values */
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600250 payload[4] = val;
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700251 if (enable_val1)
252 payload[5] = val1;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600253
254 desc_arg.args[0] = SCM_BUFFER_PHYS(payload);
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700255 desc_arg.args[1] = payload_len;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600256 desc_arg.args[2] = LIMITS_NODE_DCVS;
257 desc_arg.args[3] = node_id;
258 desc_arg.args[4] = 0; /* version */
259 desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL,
260 SCM_VAL, SCM_VAL);
261
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700262 dmac_flush_range(payload, (void *)payload + payload_len);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600263 ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, LIMITS_DCVSH), &desc_arg);
264
265 kfree(payload);
266
267 return ret;
268}
269
270static int lmh_get_temp(void *data, int *val)
271{
272 /*
273 * LMH DCVSh hardware doesn't support temperature read.
274 * return a default value for the thermal core to aggregate
275 * the thresholds
276 */
277 *val = LIMITS_TEMP_DEFAULT;
278
279 return 0;
280}
281
282static int lmh_set_trips(void *data, int low, int high)
283{
284 struct limits_dcvs_hw *hw = (struct limits_dcvs_hw *)data;
285 int ret = 0;
286
Ram Chandrasekar83c0a532017-05-02 16:06:58 -0600287 if (high >= LIMITS_TEMP_HIGH_THRESH_MAX || low < 0) {
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600288 pr_err("Value out of range low:%d high:%d\n",
289 low, high);
290 return -EINVAL;
291 }
292
293 /* Sanity check limits before writing to the hardware */
294 if (low >= high)
295 return -EINVAL;
296
297 hw->temp_limits[LIMITS_TRIP_HI] = (uint32_t)high;
298 hw->temp_limits[LIMITS_TRIP_ARM] = (uint32_t)low;
299
300 ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_THERMAL,
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700301 LIMITS_ARM_THRESHOLD, low, 0, 0);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600302 if (ret)
303 return ret;
304 ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_THERMAL,
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700305 LIMITS_HI_THRESHOLD, high, 0, 0);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600306 if (ret)
307 return ret;
308 ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_THERMAL,
309 LIMITS_LOW_THRESHOLD,
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700310 high - LIMITS_LOW_THRESHOLD_OFFSET,
311 0, 0);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600312 if (ret)
313 return ret;
314
315 return ret;
316}
317
318static struct thermal_zone_of_device_ops limits_sensor_ops = {
319 .get_temp = lmh_get_temp,
320 .set_trips = lmh_set_trips,
321};
322
323static struct limits_dcvs_hw *get_dcvsh_hw_from_cpu(int cpu)
324{
325 struct limits_dcvs_hw *hw;
326
Ram Chandrasekar3600a182017-05-10 12:47:28 -0600327 mutex_lock(&lmh_dcvs_list_access);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600328 list_for_each_entry(hw, &lmh_dcvs_hw_list, list) {
Ram Chandrasekar3600a182017-05-10 12:47:28 -0600329 if (cpumask_test_cpu(cpu, &hw->core_map)) {
330 mutex_unlock(&lmh_dcvs_list_access);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600331 return hw;
Ram Chandrasekar3600a182017-05-10 12:47:28 -0600332 }
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600333 }
Ram Chandrasekar3600a182017-05-10 12:47:28 -0600334 mutex_unlock(&lmh_dcvs_list_access);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600335
336 return NULL;
337}
338
339static int enable_lmh(void)
340{
341 int ret = 0;
342 struct scm_desc desc_arg;
343
344 desc_arg.args[0] = 1;
345 desc_arg.arginfo = SCM_ARGS(1, SCM_VAL);
346 ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, LIMITS_PROFILE_CHANGE),
347 &desc_arg);
348 if (ret) {
349 pr_err("Error switching profile:[1]. err:%d\n", ret);
350 return ret;
351 }
352
353 return ret;
354}
355
356static int lmh_set_max_limit(int cpu, u32 freq)
357{
358 struct limits_dcvs_hw *hw = get_dcvsh_hw_from_cpu(cpu);
Ram Chandrasekar2d8a9b62017-04-05 17:38:26 -0600359 int ret = 0, cpu_idx, idx = 0;
360 u32 max_freq = U32_MAX;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600361
362 if (!hw)
363 return -EINVAL;
364
Ram Chandrasekar2d8a9b62017-04-05 17:38:26 -0600365 mutex_lock(&hw->access_lock);
366 for_each_cpu(cpu_idx, &hw->core_map) {
367 if (cpu_idx == cpu)
Ram Chandrasekard467cbc2017-09-12 15:20:29 -0600368 /*
369 * If there is no limits restriction for CPU scaling max
370 * frequency, vote for a very high value. This will allow
371 * the CPU to use the boost frequencies.
372 */
373 hw->cdev_data[idx].max_freq =
374 (freq == hw->max_freq) ? U32_MAX : freq;
Ram Chandrasekar2d8a9b62017-04-05 17:38:26 -0600375 if (max_freq > hw->cdev_data[idx].max_freq)
376 max_freq = hw->cdev_data[idx].max_freq;
377 idx++;
378 }
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700379 ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_THERMAL,
380 LIMITS_FREQ_CAP, max_freq,
381 (max_freq == U32_MAX) ? 0 : 1, 1);
Ram Chandrasekar2a3dfb22017-05-04 16:32:20 -0600382 lmh_dcvs_notify(hw);
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700383 mutex_unlock(&hw->access_lock);
Ram Chandrasekar2d8a9b62017-04-05 17:38:26 -0600384
385 return ret;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600386}
387
388static int lmh_set_min_limit(int cpu, u32 freq)
389{
390 struct limits_dcvs_hw *hw = get_dcvsh_hw_from_cpu(cpu);
Ram Chandrasekar2d8a9b62017-04-05 17:38:26 -0600391 int cpu_idx, idx = 0;
392 u32 min_freq = 0;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600393
394 if (!hw)
395 return -EINVAL;
396
Ram Chandrasekar2d8a9b62017-04-05 17:38:26 -0600397 mutex_lock(&hw->access_lock);
398 for_each_cpu(cpu_idx, &hw->core_map) {
399 if (cpu_idx == cpu)
400 hw->cdev_data[idx].min_freq = freq;
401 if (min_freq < hw->cdev_data[idx].min_freq)
402 min_freq = hw->cdev_data[idx].min_freq;
403 idx++;
404 }
405 if (min_freq != hw->min_freq)
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600406 writel_relaxed(0x01, hw->min_freq_reg);
407 else
408 writel_relaxed(0x00, hw->min_freq_reg);
Ram Chandrasekar2d8a9b62017-04-05 17:38:26 -0600409 mutex_unlock(&hw->access_lock);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600410
411 return 0;
412}
413static struct cpu_cooling_ops cd_ops = {
414 .ceil_limit = lmh_set_max_limit,
415 .floor_limit = lmh_set_min_limit,
416};
417
Ram Chandrasekar3600a182017-05-10 12:47:28 -0600418static int limits_cpu_online(unsigned int online_cpu)
419{
420 struct limits_dcvs_hw *hw = get_dcvsh_hw_from_cpu(online_cpu);
421 unsigned int idx = 0, cpu = 0;
422
423 if (!hw)
424 return 0;
425
426 for_each_cpu(cpu, &hw->core_map) {
427 cpumask_t cpu_mask = { CPU_BITS_NONE };
428
429 if (cpu != online_cpu) {
430 idx++;
431 continue;
432 } else if (hw->cdev_data[idx].cdev) {
433 return 0;
434 }
435 cpumask_set_cpu(cpu, &cpu_mask);
436 hw->cdev_data[idx].max_freq = U32_MAX;
437 hw->cdev_data[idx].min_freq = 0;
438 hw->cdev_data[idx].cdev = cpufreq_platform_cooling_register(
439 &cpu_mask, &cd_ops);
440 if (IS_ERR_OR_NULL(hw->cdev_data[idx].cdev)) {
441 pr_err("CPU:%u cooling device register error:%ld\n",
442 cpu, PTR_ERR(hw->cdev_data[idx].cdev));
443 hw->cdev_data[idx].cdev = NULL;
444 } else {
445 pr_debug("CPU:%u cooling device registered\n", cpu);
446 }
447 break;
448
449 }
450
451 return 0;
452}
453
Ram Chandrasekar6d393b92017-07-21 15:37:33 -0600454static void limits_isens_vref_ldo_init(struct platform_device *pdev,
455 struct limits_dcvs_hw *hw)
456{
457 int ret = 0;
458 uint32_t settings[3];
459
460 hw->isens_reg = devm_regulator_get(&pdev->dev, "isens_vref");
461 if (IS_ERR_OR_NULL(hw->isens_reg)) {
462 if (PTR_ERR(hw->isens_reg) == -ENODEV)
463 return;
464
465 pr_err("Regulator:isens_vref init error:%ld\n",
466 PTR_ERR(hw->isens_reg));
467 return;
468 }
469 ret = of_property_read_u32_array(pdev->dev.of_node,
470 "isens-vref-settings",
471 settings, 3);
472 if (ret) {
473 pr_err("Regulator:isens_vref settings read error:%d\n",
474 ret);
475 devm_regulator_put(hw->isens_reg);
476 return;
477 }
478 ret = regulator_set_voltage(hw->isens_reg, settings[0], settings[1]);
479 if (ret) {
480 pr_err("Regulator:isens_vref set voltage error:%d\n", ret);
481 devm_regulator_put(hw->isens_reg);
482 return;
483 }
484 ret = regulator_set_load(hw->isens_reg, settings[2]);
485 if (ret) {
486 pr_err("Regulator:isens_vref set load error:%d\n", ret);
487 devm_regulator_put(hw->isens_reg);
488 return;
489 }
490 if (regulator_enable(hw->isens_reg)) {
491 pr_err("Failed to enable regulator:isens_vref\n");
492 devm_regulator_put(hw->isens_reg);
493 return;
494 }
495}
496
Ram Chandrasekard8fd4be2017-08-04 15:12:31 -0600497static ssize_t
498lmh_freq_limit_show(struct device *dev, struct device_attribute *devattr,
499 char *buf)
500{
501 struct limits_dcvs_hw *hw = container_of(devattr,
502 struct limits_dcvs_hw,
503 lmh_freq_attr);
504
505 return snprintf(buf, PAGE_SIZE, "%lu\n", hw->hw_freq_limit);
506}
507
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600508static int limits_dcvs_probe(struct platform_device *pdev)
509{
510 int ret;
511 int affinity = -1;
512 struct limits_dcvs_hw *hw;
513 struct thermal_zone_device *tzdev;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600514 struct device_node *dn = pdev->dev.of_node;
515 struct device_node *cpu_node, *lmh_node;
516 uint32_t request_reg, clear_reg, min_reg;
517 unsigned long max_freq, min_freq;
Ram Chandrasekar3600a182017-05-10 12:47:28 -0600518 int cpu;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600519 cpumask_t mask = { CPU_BITS_NONE };
520
521 for_each_possible_cpu(cpu) {
522 cpu_node = of_cpu_device_node_get(cpu);
523 if (!cpu_node)
524 continue;
525 lmh_node = of_parse_phandle(cpu_node, "qcom,lmh-dcvs", 0);
526 if (lmh_node == dn) {
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600527 /*set the cpumask*/
528 cpumask_set_cpu(cpu, &(mask));
529 }
530 of_node_put(cpu_node);
531 of_node_put(lmh_node);
532 }
533
534 /*
535 * We return error if none of the CPUs have
536 * reference to our LMH node
537 */
Ram Chandrasekar04e66672017-04-17 21:57:19 -0600538 if (cpumask_empty(&mask))
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600539 return -EINVAL;
540
541 ret = limits_dcvs_get_freq_limits(cpumask_first(&mask), &max_freq,
542 &min_freq);
543 if (ret)
544 return ret;
545 hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
546 if (!hw)
547 return -ENOMEM;
Ram Chandrasekar2d8a9b62017-04-05 17:38:26 -0600548 hw->cdev_data = devm_kcalloc(&pdev->dev, cpumask_weight(&mask),
549 sizeof(*hw->cdev_data),
550 GFP_KERNEL);
551 if (!hw->cdev_data)
552 return -ENOMEM;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600553
554 cpumask_copy(&hw->core_map, &mask);
Ram Chandrasekar04e66672017-04-17 21:57:19 -0600555 ret = of_property_read_u32(dn, "qcom,affinity", &affinity);
556 if (ret)
557 return -ENODEV;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600558 switch (affinity) {
559 case 0:
560 hw->affinity = LIMITS_CLUSTER_0;
561 break;
562 case 1:
563 hw->affinity = LIMITS_CLUSTER_1;
564 break;
565 default:
566 return -EINVAL;
567 };
568
569 /* Enable the thermal algorithm early */
570 ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_THERMAL,
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700571 LIMITS_ALGO_MODE_ENABLE, 1, 0, 0);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600572 if (ret)
573 return ret;
574 /* Enable the LMH outer loop algorithm */
575 ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_CRNT,
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700576 LIMITS_ALGO_MODE_ENABLE, 1, 0, 0);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600577 if (ret)
578 return ret;
579 /* Enable the Reliability algorithm */
580 ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_REL,
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700581 LIMITS_ALGO_MODE_ENABLE, 1, 0, 0);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600582 if (ret)
583 return ret;
584 /* Enable the BCL algorithm */
585 ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_BCL,
Ram Chandrasekara30d0292017-11-13 14:17:39 -0700586 LIMITS_ALGO_MODE_ENABLE, 1, 0, 0);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600587 if (ret)
588 return ret;
589 ret = enable_lmh();
590 if (ret)
591 return ret;
592
593 /*
594 * Setup virtual thermal zones for each LMH-DCVS hardware
595 * The sensor does not do actual thermal temperature readings
596 * but does support setting thresholds for trips.
597 * Let's register with thermal framework, so we have the ability
598 * to set low/high thresholds.
599 */
600 hw->temp_limits[LIMITS_TRIP_HI] = INT_MAX;
601 hw->temp_limits[LIMITS_TRIP_ARM] = 0;
602 hw->hw_freq_limit = hw->max_freq = max_freq;
603 hw->min_freq = min_freq;
604 snprintf(hw->sensor_name, sizeof(hw->sensor_name), "limits_sensor-%02d",
605 affinity);
606 tzdev = thermal_zone_of_sensor_register(&pdev->dev, 0, hw,
607 &limits_sensor_ops);
608 if (IS_ERR_OR_NULL(tzdev))
609 return PTR_ERR(tzdev);
610
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600611 switch (affinity) {
612 case 0:
613 request_reg = LIMITS_CLUSTER_0_REQ;
614 clear_reg = LIMITS_CLUSTER_0_INT_CLR;
615 min_reg = LIMITS_CLUSTER_0_MIN_FREQ;
616 break;
617 case 1:
618 request_reg = LIMITS_CLUSTER_1_REQ;
619 clear_reg = LIMITS_CLUSTER_1_INT_CLR;
620 min_reg = LIMITS_CLUSTER_1_MIN_FREQ;
621 break;
622 default:
Ram Chandrasekar758bbf12017-05-09 15:19:48 -0600623 ret = -EINVAL;
624 goto unregister_sensor;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600625 };
626
Ram Chandrasekar758bbf12017-05-09 15:19:48 -0600627 hw->min_freq_reg = devm_ioremap(&pdev->dev, min_reg, 0x4);
628 if (!hw->min_freq_reg) {
629 pr_err("min frequency enable register remap failed\n");
630 ret = -ENOMEM;
631 goto unregister_sensor;
632 }
633
Ram Chandrasekar758bbf12017-05-09 15:19:48 -0600634 mutex_init(&hw->access_lock);
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700635 INIT_DEFERRABLE_WORK(&hw->freq_poll_work, limits_dcvs_poll);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600636 hw->osm_hw_reg = devm_ioremap(&pdev->dev, request_reg, 0x4);
637 if (!hw->osm_hw_reg) {
638 pr_err("register remap failed\n");
Ram Chandrasekar758bbf12017-05-09 15:19:48 -0600639 goto probe_exit;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600640 }
641 hw->int_clr_reg = devm_ioremap(&pdev->dev, clear_reg, 0x4);
642 if (!hw->int_clr_reg) {
643 pr_err("interrupt clear reg remap failed\n");
Ram Chandrasekar758bbf12017-05-09 15:19:48 -0600644 goto probe_exit;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600645 }
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600646
647 hw->irq_num = of_irq_get(pdev->dev.of_node, 0);
648 if (hw->irq_num < 0) {
Ram Chandrasekar758bbf12017-05-09 15:19:48 -0600649 pr_err("Error getting IRQ number. err:%d\n", hw->irq_num);
650 goto probe_exit;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600651 }
Ram Chandrasekar47f9b502018-02-05 15:04:36 -0700652 hw->is_irq_enabled = true;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600653 ret = devm_request_threaded_irq(&pdev->dev, hw->irq_num, NULL,
654 lmh_dcvs_handle_isr, IRQF_TRIGGER_HIGH | IRQF_ONESHOT
655 | IRQF_NO_SUSPEND, hw->sensor_name, hw);
656 if (ret) {
657 pr_err("Error registering for irq. err:%d\n", ret);
Ram Chandrasekar758bbf12017-05-09 15:19:48 -0600658 ret = 0;
659 goto probe_exit;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600660 }
Ram Chandrasekar6d393b92017-07-21 15:37:33 -0600661 limits_isens_vref_ldo_init(pdev, hw);
Ram Chandrasekard8fd4be2017-08-04 15:12:31 -0600662 hw->lmh_freq_attr.attr.name = "lmh_freq_limit";
663 hw->lmh_freq_attr.show = lmh_freq_limit_show;
664 hw->lmh_freq_attr.attr.mode = 0444;
665 device_create_file(&pdev->dev, &hw->lmh_freq_attr);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600666
Ram Chandrasekar758bbf12017-05-09 15:19:48 -0600667probe_exit:
Ram Chandrasekar3600a182017-05-10 12:47:28 -0600668 mutex_lock(&lmh_dcvs_list_access);
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600669 INIT_LIST_HEAD(&hw->list);
670 list_add(&hw->list, &lmh_dcvs_hw_list);
Ram Chandrasekar3600a182017-05-10 12:47:28 -0600671 mutex_unlock(&lmh_dcvs_list_access);
Ram Chandrasekar1d1adbb2017-05-18 17:44:50 -0600672 lmh_debug_register(pdev);
Ram Chandrasekar3600a182017-05-10 12:47:28 -0600673
674 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "lmh-dcvs/cdev:online",
675 limits_cpu_online, NULL);
676 if (ret < 0)
677 goto unregister_sensor;
678 ret = 0;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600679
680 return ret;
Ram Chandrasekar758bbf12017-05-09 15:19:48 -0600681
Ram Chandrasekar758bbf12017-05-09 15:19:48 -0600682unregister_sensor:
683 thermal_zone_of_sensor_unregister(&pdev->dev, tzdev);
684
685 return ret;
Ram Chandrasekara5911a32017-03-13 11:45:30 -0600686}
687
688static const struct of_device_id limits_dcvs_match[] = {
689 { .compatible = "qcom,msm-hw-limits", },
690 {},
691};
692
693static struct platform_driver limits_dcvs_driver = {
694 .probe = limits_dcvs_probe,
695 .driver = {
696 .name = KBUILD_MODNAME,
697 .of_match_table = limits_dcvs_match,
698 },
699};
700builtin_platform_driver(limits_dcvs_driver);