]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/blob - drivers/net/wireless/bcmdhd/dhd_custom_net_perf_tegra.c
net: wireless: bcmdhd: boost wifi performance
[sojka/nv-tegra/linux-3.10.git] / drivers / net / wireless / bcmdhd / dhd_custom_net_perf_tegra.c
1 /*
2  * drivers/net/wireless/bcmdhd/dhd_custom_net_perf_tegra.c
3  *
4  * NVIDIA Tegra Network Performance Boost for BCMDHD driver
5  *
6  * Copyright (C) 2015 NVIDIA Corporation. All rights reserved.
7  *
8  * This software is licensed under the terms of the GNU General Public
9  * License version 2, as published by the Free Software Foundation, and
10  * may be copied, distributed, and modified under those terms.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  */
18
19 #include "dhd_custom_net_perf_tegra.h"
20
21 static DEFINE_SEMAPHORE(wifi_sclk_lock);
22 static struct clk *wifi_sclk;
23 static int wifi_sclk_count;
24
25 static void wifi_sclk_enable(void)
26 {
27         if (!wifi_sclk)
28                 return;
29
30         down(&wifi_sclk_lock);
31         if (++wifi_sclk_count == 1) {
32                 pr_debug("%s\n", __func__);
33                 clk_enable(wifi_sclk);
34         }
35         up(&wifi_sclk_lock);
36 }
37
38 static void wifi_sclk_disable(void)
39 {
40         if (!wifi_sclk)
41                 return;
42
43         down(&wifi_sclk_lock);
44         if (--wifi_sclk_count == 0) {
45                 pr_debug("%s\n", __func__);
46                 clk_disable(wifi_sclk);
47         }
48         up(&wifi_sclk_lock);
49 }
50
51 /* network performance policy worker function */
52 static void tegra_net_perf_policy_worker(struct work_struct *work)
53 {
54         struct delayed_work *dwork
55                 = container_of(work, struct delayed_work, work);
56         struct tegra_net_perf_policy *policy
57                 = container_of(dwork, struct tegra_net_perf_policy, dwork);
58         unsigned long now, timeout, flags;
59
60         /* lock net perf policy */
61         spin_lock_irqsave(&policy->lock, flags);
62
63         /* get boost timeout */
64         now = jiffies;
65         timeout = policy->jiffies_boost_timeout;
66
67         /* check boost timeout */
68         if (time_before(now, timeout)) {
69                 /* boost freq */
70                 if (!policy->freq_boost_flag) {
71                         pr_debug("%s: begin freq boost (policy %p)...\n",
72                                 __func__, policy);
73                         /* set freq boost flag */
74                         policy->freq_boost_flag = 1;
75                         /* reschedule later to restore freq */
76                         schedule_delayed_work(dwork, timeout - now);
77                         /* unlock net perf policy */
78                         spin_unlock_irqrestore(&policy->lock, flags);
79                         /* boost freq (by enabling wifi sclk) */
80                         wifi_sclk_enable();
81                         return;
82                 }
83         } else {
84                 /* restore freq */
85                 if (policy->freq_boost_flag) {
86                         pr_debug("%s: end freq boost... (policy %p)\n",
87                                 __func__, policy);
88                         /* clear freq boost flag */
89                         policy->freq_boost_flag = 0;
90                         /* unlock net perf policy */
91                         spin_unlock_irqrestore(&policy->lock, flags);
92                         /* restore freq (by disabling wifi sclk) */
93                         wifi_sclk_disable();
94                         return;
95                 }
96         }
97
98         /* unlock net perf policy */
99         spin_unlock_irqrestore(&policy->lock, flags);
100 }
101
102 /* network performance policy */
103 static struct tegra_net_perf_policy rx_net_perf_policy = {
104         .bps_boost_threshold = TEGRA_NET_PERF_RX_THRESHOLD,
105         .boost_timeout_ms = TEGRA_NET_PERF_RX_TIMEOUT,
106 };
107
108 static struct tegra_net_perf_policy tx_net_perf_policy = {
109         .bps_boost_threshold = TEGRA_NET_PERF_TX_THRESHOLD,
110         .boost_timeout_ms = TEGRA_NET_PERF_TX_TIMEOUT,
111 };
112
113 static void calc_network_rate(struct tegra_net_perf_policy *policy,
114         unsigned int bits)
115 {
116         unsigned long now = jiffies;
117         unsigned long flags;
118         unsigned long delta;
119
120         /* check if bps exceeds threshold for boosting freq */
121         spin_lock_irqsave(&policy->lock, flags);
122         if (!policy->jiffies_prev)
123                 policy->jiffies_prev = now;
124         if (ULONG_MAX - bits < policy->bits) {
125                 policy->jiffies_prev = 0;
126                 policy->bits = bits;
127                 goto unlock;
128         }
129         policy->bits += bits;
130         delta = now - policy->jiffies_prev;
131         if (delta < msecs_to_jiffies(TEGRA_NET_PERF_MIN_SAMPLE_WINDOW))
132                 goto unlock;
133         if ((delta > msecs_to_jiffies(TEGRA_NET_PERF_MAX_SAMPLE_WINDOW)) ||
134                 (policy->bits / (delta + 1) > ULONG_MAX / HZ)) {
135                 policy->jiffies_prev = 0;
136                 policy->bits = bits;
137                 goto unlock;
138         }
139         policy->bps = policy->bits / (delta + 1) * HZ;
140         if (policy->bps < policy->bps_boost_threshold)
141                 goto unlock;
142         policy->jiffies_boost_timeout = now +
143                 msecs_to_jiffies(policy->boost_timeout_ms);
144
145         /* boost freq */
146         if (!policy->freq_boost_flag)
147                 schedule_delayed_work(&policy->dwork, msecs_to_jiffies(0));
148         else {
149                 cancel_delayed_work(&policy->dwork);
150                 schedule_delayed_work(&policy->dwork,
151                         msecs_to_jiffies(policy->boost_timeout_ms));
152         }
153
154 unlock:
155         spin_unlock_irqrestore(&policy->lock, flags);
156 }
157
158 void tegra_net_perf_init(void)
159 {
160         /* initialize static variable(s) */
161         wifi_sclk = clk_get_sys("tegra-wifi", "sclk");
162         if (IS_ERR(wifi_sclk)) {
163                 pr_err("%s: cannot get wifi sclk\n", __func__);
164                 wifi_sclk = NULL;
165         }
166
167         /* initialize wifi sclk rate (will not take effect until clk enable) */
168         if (wifi_sclk)
169                 if (clk_set_rate(wifi_sclk, TEGRA_NET_PERF_WIFI_SCLK_FREQ) < 0)
170                         pr_err("%s: cannot set wifi sclk rate %ld\n",
171                                 __func__, TEGRA_NET_PERF_WIFI_SCLK_FREQ);
172
173         /* sanity-check configuration values */
174         if (rx_net_perf_policy.boost_timeout_ms <
175                 TEGRA_NET_PERF_MIN_SAMPLE_WINDOW)
176                 rx_net_perf_policy.boost_timeout_ms =
177                         TEGRA_NET_PERF_MIN_SAMPLE_WINDOW;
178         if (tx_net_perf_policy.boost_timeout_ms <
179                 TEGRA_NET_PERF_MIN_SAMPLE_WINDOW)
180                 tx_net_perf_policy.boost_timeout_ms =
181                         TEGRA_NET_PERF_MIN_SAMPLE_WINDOW;
182
183         /* initialize network performance policy(s) */
184         INIT_DELAYED_WORK(&rx_net_perf_policy.dwork,
185                 tegra_net_perf_policy_worker);
186         INIT_DELAYED_WORK(&tx_net_perf_policy.dwork,
187                 tegra_net_perf_policy_worker);
188         spin_lock_init(&rx_net_perf_policy.lock);
189         spin_lock_init(&tx_net_perf_policy.lock);
190 }
191
192 void tegra_net_perf_exit(void)
193 {
194         /* uninitialize network performance policy(s) */
195         cancel_delayed_work_sync(&tx_net_perf_policy.dwork);
196         tx_net_perf_policy.freq_boost_flag = 0;
197         cancel_delayed_work_sync(&rx_net_perf_policy.dwork);
198         rx_net_perf_policy.freq_boost_flag = 0;
199
200         /* uninitialize static variable(s) */
201         if (wifi_sclk) {
202                 if (wifi_sclk_count > 0) {
203                         pr_debug("%s: wifi sclk disable\n",
204                                 __func__);
205                         wifi_sclk_count = 0;
206                         clk_disable(wifi_sclk);
207                 }
208                 clk_put(wifi_sclk);
209                 wifi_sclk = NULL;
210         }
211 }
212
213 void tegra_net_perf_rx(struct sk_buff *skb)
214 {
215         /* calc rx data rate (and boost freq if threshold exceeded) */
216         calc_network_rate(&rx_net_perf_policy, skb->len * 8);
217 }
218
219 void tegra_net_perf_tx(struct sk_buff *skb)
220 {
221         /* calc tx data rate (and boost freq if threshold exceeded) */
222         calc_network_rate(&tx_net_perf_policy, skb->len * 8);
223 }