summaryrefslogtreecommitdiff
path: root/drivers/edp/sysedp_reactive_capping.c
blob: 9a0cdd30fcd381042202f1ce6db6d19f86bd94f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/sysedp.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>

#define STATE_MAX_MW	20000
#define STATE_STEP_MW	500
#define NSTATES		(STATE_MAX_MW / STATE_STEP_MW + 1)

inline unsigned int count_state(int mw)
{
	int state;
	state = mw > 0 ? mw / STATE_STEP_MW + 1 : 0;
	return min(state, NSTATES - 1);
}

static void oc_throttle_alarm(struct sysedp_reactive_capping_platform_data *h)
{
	mutex_lock(&h->mutex);

	h->cur_capping_mw += h->step_alarm_mw;
	h->cur_capping_mw = min(h->cur_capping_mw, h->max_capping_mw);

	cancel_delayed_work(&h->work);

	sysedp_set_state(&h->sysedpc, count_state(h->cur_capping_mw));

	schedule_delayed_work(&h->work, msecs_to_jiffies(h->relax_ms));

	mutex_unlock(&h->mutex);
}

static void oc_throttle_work(struct work_struct *work)
{
	struct sysedp_reactive_capping_platform_data *h;
	h = container_of(to_delayed_work(work),
			 struct sysedp_reactive_capping_platform_data,
			 work);
	mutex_lock(&h->mutex);
	h->cur_capping_mw -= h->step_relax_mw;
	h->cur_capping_mw = max(h->cur_capping_mw, 0);

	sysedp_set_state(&h->sysedpc, count_state(h->cur_capping_mw));

	if (h->cur_capping_mw)
		schedule_delayed_work(&h->work, msecs_to_jiffies(h->relax_ms));

	mutex_unlock(&h->mutex);
}

static irqreturn_t sysedp_reactive_capping_irq_handler(int irq, void *data)
{
	if (!data)
		return IRQ_NONE;

	oc_throttle_alarm(data);
	return IRQ_HANDLED;
}


static unsigned int capping_states[NSTATES];

static int sysedp_reactive_capping_probe(struct platform_device *pdev)
{
	int ret, i;
	struct sysedp_reactive_capping_platform_data *pdata;

	pdata = pdev->dev.platform_data;

	if (!pdata)
		return -EINVAL;

	/* update static capping_states table */
	for (i = 0; i < NSTATES; i++)
		capping_states[i] = i * STATE_STEP_MW;

	/* sysedpc consumer name must be initialized */
	if (pdata->sysedpc.name[0] == '\0')
		return -EINVAL;
	pdata->sysedpc.states = capping_states;
	pdata->sysedpc.num_states = ARRAY_SIZE(capping_states);
	ret = sysedp_register_consumer(&pdata->sysedpc);
	if (ret) {
		pr_err("sysedp_reactive_capping_probe: consumer register failed (%d)\n",
		       ret);
		return ret;
	}
	mutex_init(&pdata->mutex);
	INIT_DELAYED_WORK(&pdata->work, oc_throttle_work);

	ret = request_threaded_irq(pdata->irq,
				   NULL,
				   sysedp_reactive_capping_irq_handler,
				   pdata->irq_flags,
				   pdata->sysedpc.name,
				   pdata);
	if (ret) {
		pr_err("sysedp_reactive_capping_probe: request_threaded_irq failed (%d)\n",
		       ret);
		sysedp_unregister_consumer(&pdata->sysedpc);
		return ret;
	}

	return 0;
}

static struct platform_driver sysedp_reactive_capping_driver = {
	.probe = sysedp_reactive_capping_probe,
	.driver = {
		.name = "sysedp_reactive_capping",
		.owner = THIS_MODULE
	}
};

static __init int sysedp_reactive_capping_init(void)
{
	return platform_driver_register(&sysedp_reactive_capping_driver);
}
late_initcall(sysedp_reactive_capping_init);