blob: 05314b1a4eb7216544d0bff010a538c87c0984b8 [file] [log] [blame]
Radhakrishna Jiguru1c9b2252013-08-27 23:57:48 +05301/*
2 **************************************************************************
Stephen Wanga428d0b2017-01-12 21:30:01 -08003 * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
Radhakrishna Jiguru1c9b2252013-08-27 23:57:48 +05304 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all copies.
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
13 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 **************************************************************************
15 */
Abhishek Rastogibc74e432013-04-02 10:28:22 +053016
17/*
18 * nss_init.c
19 * NSS init APIs
20 *
21 */
Abhishek Rastogibc74e432013-04-02 10:28:22 +053022#include "nss_core.h"
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -080023#if (NSS_PM_SUPPORT == 1)
Pamidipati, Vijay7f413b52013-09-24 19:07:12 +053024#include "nss_pm.h"
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -080025#endif
Abhishek Rastogi9da47472014-03-18 19:46:15 +053026#include "nss_tx_rx_common.h"
Stephen Wang38e89bc2014-11-06 11:34:45 -080027#include "nss_data_plane.h"
Stephen Wang1f6ad492016-01-27 23:42:06 -080028#include "nss_capwap.h"
Pamidipati, Vijay7f413b52013-09-24 19:07:12 +053029
Abhishek Rastogibc74e432013-04-02 10:28:22 +053030#include <nss_hal.h>
31
32#include <linux/module.h>
33#include <linux/platform_device.h>
wthomas442c7972013-08-05 14:28:17 -070034#include <linux/proc_fs.h>
35#include <linux/device.h>
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -080036
37#if (NSS_DT_SUPPORT == 1)
Stephen Wang463f1cf2016-03-29 15:25:51 -070038#if (NSS_FABRIC_SCALING_SUPPORT == 1)
Stephen Wang8ffd17f2016-03-07 14:03:40 -080039#include <linux/fab_scaling.h>
Stephen Wang1017ad12016-03-14 10:18:06 -070040#endif
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -080041#include <linux/of.h>
42#include <linux/of_net.h>
43#include <linux/of_irq.h>
44#include <linux/of_address.h>
45#include <linux/reset.h>
46#else
Abhishek Rastogibc74e432013-04-02 10:28:22 +053047#include <mach/msm_nss.h>
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -080048#endif
Abhishek Rastogibc74e432013-04-02 10:28:22 +053049
wthomas442c7972013-08-05 14:28:17 -070050#include <linux/sysctl.h>
51#include <linux/regulator/consumer.h>
Thomas Wufb6a6842013-10-23 13:14:27 -070052#include <linux/clk.h>
wthomas442c7972013-08-05 14:28:17 -070053
Abhishek Rastogibc74e432013-04-02 10:28:22 +053054/*
55 * Global declarations
56 */
Abhishek Rastogi5cd2e4c2013-11-13 18:09:08 +053057int nss_ctl_redirect __read_mostly = 0;
Sakthi Vignesh Radhakrishnanaf39aad2014-03-31 11:31:03 -070058int nss_ctl_debug __read_mostly = 0;
Saurabh Misra96998db2014-07-10 12:15:48 -070059int nss_ctl_logbuf __read_mostly = 0;
Sundarajan Srinivasanf9c4a232014-11-18 13:25:40 -080060int nss_jumbo_mru __read_mostly = 0;
61int nss_paged_mode __read_mostly = 0;
Radha krishna Simha Jiguru28a0a252015-08-04 16:34:09 +053062int nss_skip_nw_process = 0x0;
63module_param(nss_skip_nw_process, int, S_IRUGO);
Abhishek Rastogibc74e432013-04-02 10:28:22 +053064
65/*
Pamidipati, Vijay5d27d812013-11-22 16:48:11 +053066 * PM client handle
67 */
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -080068#if (NSS_PM_SUPPORT == 1)
Pamidipati, Vijay5d27d812013-11-22 16:48:11 +053069static void *pm_client;
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -080070#endif
Pamidipati, Vijay5d27d812013-11-22 16:48:11 +053071
72/*
wthomas626147f2013-09-18 13:12:40 -070073 * Handler to send NSS messages
74 */
Thomas Wufb6a6842013-10-23 13:14:27 -070075struct clk *nss_core0_clk;
wthomas626147f2013-09-18 13:12:40 -070076
77/*
Thomas Wucd6b35a2015-07-14 10:17:48 -070078 * Handle fabric requests - only on new kernel
79 */
80#if (NSS_DT_SUPPORT == 1)
81struct clk *nss_fab0_clk;
82struct clk *nss_fab1_clk;
Samarjeet Banerjee69731a22016-07-21 13:04:59 +053083bool nss_crypto_is_scaled = false;
Thomas Wucd6b35a2015-07-14 10:17:48 -070084#endif
85
86/*
Abhishek Rastogibc74e432013-04-02 10:28:22 +053087 * Top level nss context structure
88 */
89struct nss_top_instance nss_top_main;
wthomas442c7972013-08-05 14:28:17 -070090struct nss_cmd_buffer nss_cmd_buf;
wthomas626147f2013-09-18 13:12:40 -070091struct nss_runtime_sampling nss_runtime_samples;
92struct workqueue_struct *nss_wq;
93
94/*
95 * Work Queue to handle messages to Kernel
96 */
97nss_work_t *nss_work;
Abhishek Rastogibc74e432013-04-02 10:28:22 +053098
Arunkumar T79990cb2015-06-05 10:53:16 +053099extern struct of_device_id nss_dt_ids[];
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530100
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530101/*
102 * nss_probe()
Arunkumar T79990cb2015-06-05 10:53:16 +0530103 * HLOS device probe callback
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530104 */
Ram Chandra Jangir6577f382016-05-31 12:16:28 +0530105static inline int nss_probe(struct platform_device *nss_dev)
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530106{
Arunkumar T79990cb2015-06-05 10:53:16 +0530107 return nss_hal_probe(nss_dev);
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530108}
109
110/*
111 * nss_remove()
Arunkumar T79990cb2015-06-05 10:53:16 +0530112 * HLOS device remove callback
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530113 */
Ram Chandra Jangir6577f382016-05-31 12:16:28 +0530114static inline int nss_remove(struct platform_device *nss_dev)
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530115{
Arunkumar T79990cb2015-06-05 10:53:16 +0530116 return nss_hal_remove(nss_dev);
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530117}
118
Stephen Wang90c67de2016-04-26 15:15:59 -0700119#if (NSS_DT_SUPPORT == 1)
120/*
121 * Platform Device ID for NSS core.
122 */
123struct of_device_id nss_dt_ids[] = {
124 { .compatible = "qcom,nss" },
125 { .compatible = "qcom,nss0" },
126 { .compatible = "qcom,nss1" },
127 {},
128};
129MODULE_DEVICE_TABLE(of, nss_dt_ids);
130#endif
131
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530132/*
133 * nss_driver
134 * Platform driver structure for NSS
135 */
136struct platform_driver nss_driver = {
137 .probe = nss_probe,
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -0800138 .remove = nss_remove,
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530139 .driver = {
140 .name = "qca-nss",
141 .owner = THIS_MODULE,
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -0800142#if (NSS_DT_SUPPORT == 1)
143 .of_match_table = of_match_ptr(nss_dt_ids),
144#endif
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530145 },
146};
147
Arunkumar T28b2d742015-06-16 22:15:58 +0530148#if (NSS_FREQ_SCALE_SUPPORT == 1)
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530149/*
Kiran Kumar C.S.K69fd5992014-01-06 20:58:14 +0530150 * nss_reset_frequency_stats_samples()
151 * Reset all frequency sampling state when auto scaling is turned off.
152 */
153static void nss_reset_frequency_stats_samples (void)
154{
155 nss_runtime_samples.buffer_index = 0;
156 nss_runtime_samples.sum = 0;
157 nss_runtime_samples.average = 0;
158 nss_runtime_samples.sample_count = 0;
159 nss_runtime_samples.message_rate_limit = 0;
Kiran Kumar C.S.K69fd5992014-01-06 20:58:14 +0530160 nss_runtime_samples.freq_scale_rate_limit_down = 0;
161}
162
163/*
wthomas626147f2013-09-18 13:12:40 -0700164 ***************************************************************************************************
Thomas Wufb6a6842013-10-23 13:14:27 -0700165 * nss_wq_function() is used to queue up requests to change NSS frequencies.
166 * The function will take care of NSS notices and also control clock.
167 * The auto rate algorithmn will queue up requests or the procfs may also queue up these requests.
wthomas626147f2013-09-18 13:12:40 -0700168 ***************************************************************************************************
169 */
170
171/*
172 * nss_wq_function()
173 * Added to Handle BH requests to kernel
174 */
175void nss_wq_function (struct work_struct *work)
176{
177 nss_work_t *my_work = (nss_work_t *)work;
Vijay Vigneshana94c2652016-06-02 12:39:35 +0530178#if (NSS_DT_SUPPORT == 1)
179 nss_crypto_pm_event_callback_t crypto_pm_cb;
Samarjeet Banerjee69731a22016-07-21 13:04:59 +0530180 bool auto_scale;
181 bool turbo;
182
c_athandfde2e912017-01-10 15:40:12 +0530183 mutex_lock(&nss_top_main.wq_lock);
Samarjeet Banerjee69731a22016-07-21 13:04:59 +0530184 /*
Samarjeet Banerjeeb126e0f2016-08-05 20:58:27 +0530185 * If crypto clock is in Turbo, disable scaling for other
186 * NSS subsystem components and retain them at turbo
Samarjeet Banerjee69731a22016-07-21 13:04:59 +0530187 */
188 if (nss_crypto_is_scaled) {
Samarjeet Banerjeeb126e0f2016-08-05 20:58:27 +0530189 nss_cmd_buf.current_freq = nss_runtime_samples.freq_scale[NSS_FREQ_HIGH_SCALE].frequency;
c_athandfde2e912017-01-10 15:40:12 +0530190 mutex_unlock(&nss_top_main.wq_lock);
Samarjeet Banerjee69731a22016-07-21 13:04:59 +0530191 return;
192 }
Vijay Vigneshana94c2652016-06-02 12:39:35 +0530193#endif
wthomas626147f2013-09-18 13:12:40 -0700194
Guojun Jin32a3c6d2015-05-06 12:27:52 -0700195 nss_freq_change(&nss_top_main.nss[NSS_CORE_0], my_work->frequency, my_work->stats_enable, 0);
Guojun Jin9d782812015-05-12 14:01:25 -0700196 if (nss_top_main.nss[NSS_CORE_1].state == NSS_CORE_STATE_INITIALIZED) {
197 nss_freq_change(&nss_top_main.nss[NSS_CORE_1], my_work->frequency, my_work->stats_enable, 0);
198 }
Thomas Wufb6a6842013-10-23 13:14:27 -0700199 clk_set_rate(nss_core0_clk, my_work->frequency);
Guojun Jin32a3c6d2015-05-06 12:27:52 -0700200 nss_freq_change(&nss_top_main.nss[NSS_CORE_0], my_work->frequency, my_work->stats_enable, 1);
Guojun Jin9d782812015-05-12 14:01:25 -0700201 if (nss_top_main.nss[NSS_CORE_1].state == NSS_CORE_STATE_INITIALIZED) {
202 nss_freq_change(&nss_top_main.nss[NSS_CORE_1], my_work->frequency, my_work->stats_enable, 1);
203 }
wthomas626147f2013-09-18 13:12:40 -0700204
Thomas Wucd6b35a2015-07-14 10:17:48 -0700205/*
206 * If we are running NSS_PM_SUPPORT, we are on banana
207 * otherwise, we check if we are are on new kernel by checking if the
208 * fabric lookups are not NULL (success in init()))
209 */
Thomas Wu0d112192015-04-13 11:37:22 -0700210#if (NSS_PM_SUPPORT == 1)
Thomas Wucd6b35a2015-07-14 10:17:48 -0700211 if (!pm_client) {
Pamidipati, Vijay5d27d812013-11-22 16:48:11 +0530212 goto out;
213 }
214
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800215 if (my_work->frequency >= NSS_FREQ_733) {
Pamidipati, Vijay5d27d812013-11-22 16:48:11 +0530216 nss_pm_set_perf_level(pm_client, NSS_PM_PERF_LEVEL_TURBO);
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800217 } else if (my_work->frequency > NSS_FREQ_110) {
Pamidipati, Vijay5d27d812013-11-22 16:48:11 +0530218 nss_pm_set_perf_level(pm_client, NSS_PM_PERF_LEVEL_NOMINAL);
219 } else {
220 nss_pm_set_perf_level(pm_client, NSS_PM_PERF_LEVEL_IDLE);
221 }
Thomas Wu0d112192015-04-13 11:37:22 -0700222
Pamidipati, Vijay5d27d812013-11-22 16:48:11 +0530223out:
Thomas Wucd6b35a2015-07-14 10:17:48 -0700224#else
225#if (NSS_DT_SUPPORT == 1)
Stephen Wang463f1cf2016-03-29 15:25:51 -0700226#if (NSS_FABRIC_SCALING_SUPPORT == 1)
Stephen Wang8ffd17f2016-03-07 14:03:40 -0800227 scale_fabrics();
Stephen Wang1017ad12016-03-14 10:18:06 -0700228#endif
Samarjeet Banerjee69731a22016-07-21 13:04:59 +0530229 if ((nss_fab0_clk != NULL) && (nss_fab1_clk != NULL)) {
Thomas Wucd6b35a2015-07-14 10:17:48 -0700230 if (my_work->frequency >= NSS_FREQ_733) {
231 clk_set_rate(nss_fab0_clk, NSS_FABRIC0_TURBO);
232 clk_set_rate(nss_fab1_clk, NSS_FABRIC1_TURBO);
233 } else if (my_work->frequency > NSS_FREQ_110) {
234 clk_set_rate(nss_fab0_clk, NSS_FABRIC0_NOMINAL);
235 clk_set_rate(nss_fab1_clk, NSS_FABRIC1_NOMINAL);
236 } else {
237 clk_set_rate(nss_fab0_clk, NSS_FABRIC0_IDLE);
238 clk_set_rate(nss_fab1_clk, NSS_FABRIC1_IDLE);
239 }
Samarjeet Banerjeecdfc0bd2016-05-28 00:22:31 +0530240
241 /*
242 * notify crypto about the clock change
243 */
244 crypto_pm_cb = nss_top_main.crypto_pm_callback;
245 if (crypto_pm_cb) {
246 turbo = (my_work->frequency >= NSS_FREQ_733);
Samarjeet Banerjee69731a22016-07-21 13:04:59 +0530247 auto_scale = nss_cmd_buf.auto_scale;
248 nss_crypto_is_scaled = crypto_pm_cb(nss_top_main.crypto_pm_ctx, turbo, auto_scale);
Samarjeet Banerjeecdfc0bd2016-05-28 00:22:31 +0530249 }
Thomas Wucd6b35a2015-07-14 10:17:48 -0700250 }
251#endif
Thomas Wu0d112192015-04-13 11:37:22 -0700252#endif
c_athandfde2e912017-01-10 15:40:12 +0530253 mutex_unlock(&nss_top_main.wq_lock);
wthomas626147f2013-09-18 13:12:40 -0700254 kfree((void *)work);
255}
256
257/*
wthomas442c7972013-08-05 14:28:17 -0700258 * nss_current_freq_handler()
259 * Handle Userspace Frequency Change Requests
260 */
Stephen Wang52e6d342016-03-29 15:02:33 -0700261static int nss_current_freq_handler(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
wthomas442c7972013-08-05 14:28:17 -0700262{
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800263 int ret, i;
wthomas626147f2013-09-18 13:12:40 -0700264
265 BUG_ON(!nss_wq);
wthomas442c7972013-08-05 14:28:17 -0700266
267 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
268
Thomas Wuc151f2e2015-09-08 10:59:44 -0700269 if (!*lenp || (*ppos && !write)) {
wthomas626147f2013-09-18 13:12:40 -0700270 printk("Frequency Set to %d\n", nss_cmd_buf.current_freq);
Thomas Wuc151f2e2015-09-08 10:59:44 -0700271 *lenp = 0;
wthomasd39fa822013-08-22 16:44:23 -0700272 return ret;
wthomas442c7972013-08-05 14:28:17 -0700273 }
wthomasd39fa822013-08-22 16:44:23 -0700274
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800275 /*
276 * Check if frequency exists in frequency Table
277 */
278 i = 0;
Thomas Wu7132bd32015-05-07 15:03:06 -0700279 while (i < NSS_FREQ_MAX_SCALE) {
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800280 if (nss_runtime_samples.freq_scale[i].frequency == nss_cmd_buf.current_freq) {
281 break;
282 }
283 i++;
284 }
Thomas Wu7132bd32015-05-07 15:03:06 -0700285 if (i == NSS_FREQ_MAX_SCALE) {
Thomas Wufb6a6842013-10-23 13:14:27 -0700286 printk("Frequency not found. Please check Frequency Table\n");
Thomas Wub7683af2016-07-07 14:19:09 -0700287 nss_cmd_buf.current_freq = nss_runtime_samples.freq_scale[nss_runtime_samples.freq_scale_index].frequency;
wthomas626147f2013-09-18 13:12:40 -0700288 return ret;
wthomasd39fa822013-08-22 16:44:23 -0700289 }
290
Thomas Wub7683af2016-07-07 14:19:09 -0700291 /*
292 * Turn off Auto Scale
293 */
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800294 nss_cmd_buf.auto_scale = 0;
295 nss_runtime_samples.freq_scale_ready = 0;
Thomas Wub7683af2016-07-07 14:19:09 -0700296 nss_runtime_samples.freq_scale_index = i;
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800297
Thomas Wu7409bce2014-05-21 10:56:07 -0700298 nss_work = (nss_work_t *)kmalloc(sizeof(nss_work_t), GFP_ATOMIC);
wthomas626147f2013-09-18 13:12:40 -0700299 if (!nss_work) {
300 nss_info("NSS Freq WQ kmalloc fail");
301 return ret;
302 }
303 INIT_WORK((struct work_struct *)nss_work, nss_wq_function);
304 nss_work->frequency = nss_cmd_buf.current_freq;
Kiran Kumar C.S.K69fd5992014-01-06 20:58:14 +0530305 nss_work->stats_enable = 0;
306
Thomas Wub7683af2016-07-07 14:19:09 -0700307 /*
308 * Ensure we start with a fresh set of samples later
309 */
Kiran Kumar C.S.K69fd5992014-01-06 20:58:14 +0530310 nss_reset_frequency_stats_samples();
311
wthomas626147f2013-09-18 13:12:40 -0700312 queue_work(nss_wq, (struct work_struct *)nss_work);
313
wthomas442c7972013-08-05 14:28:17 -0700314 return ret;
315}
316
317/*
318 * nss_auto_scale_handler()
319 * Enables or Disable Auto Scaling
320 */
Stephen Wang52e6d342016-03-29 15:02:33 -0700321static int nss_auto_scale_handler(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
wthomas442c7972013-08-05 14:28:17 -0700322{
323 int ret;
324
325 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
326
Thomas Wuc151f2e2015-09-08 10:59:44 -0700327 if (!*lenp || (*ppos && !write)) {
wthomas626147f2013-09-18 13:12:40 -0700328 return ret;
329 }
330
Thomas Wufb6a6842013-10-23 13:14:27 -0700331 if (nss_cmd_buf.auto_scale != 1) {
wthomas626147f2013-09-18 13:12:40 -0700332 /*
Kiran Kumar C.S.K69fd5992014-01-06 20:58:14 +0530333 * Is auto scaling currently enabled? If so, send the command to
334 * disable stats reporting to NSS
wthomas626147f2013-09-18 13:12:40 -0700335 */
Kiran Kumar C.S.K69fd5992014-01-06 20:58:14 +0530336 if (nss_runtime_samples.freq_scale_ready != 0) {
337 nss_cmd_buf.current_freq = nss_runtime_samples.freq_scale[nss_runtime_samples.freq_scale_index].frequency;
Thomas Wu7409bce2014-05-21 10:56:07 -0700338 nss_work = (nss_work_t *)kmalloc(sizeof(nss_work_t), GFP_ATOMIC);
Kiran Kumar C.S.K69fd5992014-01-06 20:58:14 +0530339 if (!nss_work) {
340 nss_info("NSS Freq WQ kmalloc fail");
341 return ret;
342 }
343 INIT_WORK((struct work_struct *)nss_work, nss_wq_function);
344 nss_work->frequency = nss_cmd_buf.current_freq;
345 nss_work->stats_enable = 0;
346 queue_work(nss_wq, (struct work_struct *)nss_work);
347 nss_runtime_samples.freq_scale_ready = 0;
348
349 /*
350 * The current samples would be stale later when scaling is
351 * enabled again, hence reset them
352 */
353 nss_reset_frequency_stats_samples();
354 }
Thomas Wufb6a6842013-10-23 13:14:27 -0700355 return ret;
wthomas626147f2013-09-18 13:12:40 -0700356 }
wthomas442c7972013-08-05 14:28:17 -0700357
Thomas Wufb6a6842013-10-23 13:14:27 -0700358 /*
359 * Auto Scaling is already being done
360 */
361 if (nss_runtime_samples.freq_scale_ready == 1) {
362 return ret;
363 }
364
365 /*
366 * Setup default values - Middle of Freq Scale Band
367 */
368 nss_runtime_samples.freq_scale_index = 1;
369 nss_cmd_buf.current_freq = nss_runtime_samples.freq_scale[nss_runtime_samples.freq_scale_index].frequency;
370
Thomas Wu7409bce2014-05-21 10:56:07 -0700371 nss_work = (nss_work_t *)kmalloc(sizeof(nss_work_t), GFP_ATOMIC);
Thomas Wufb6a6842013-10-23 13:14:27 -0700372 if (!nss_work) {
373 nss_info("NSS Freq WQ kmalloc fail");
374 return ret;
375 }
376 INIT_WORK((struct work_struct *)nss_work, nss_wq_function);
377 nss_work->frequency = nss_cmd_buf.current_freq;
Kiran Kumar C.S.K69fd5992014-01-06 20:58:14 +0530378 nss_work->stats_enable = 1;
Thomas Wufb6a6842013-10-23 13:14:27 -0700379 queue_work(nss_wq, (struct work_struct *)nss_work);
380
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800381 nss_cmd_buf.auto_scale = 0;
Thomas Wufb6a6842013-10-23 13:14:27 -0700382 nss_runtime_samples.freq_scale_ready = 1;
383
wthomas442c7972013-08-05 14:28:17 -0700384 return ret;
385}
386
387/*
388 * nss_get_freq_table_handler()
389 * Display Support Freq and Ex how to Change.
390 */
Stephen Wang52e6d342016-03-29 15:02:33 -0700391static int nss_get_freq_table_handler(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
wthomas442c7972013-08-05 14:28:17 -0700392{
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800393 int ret, i;
wthomas442c7972013-08-05 14:28:17 -0700394
395 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
396
Thomas Wuc151f2e2015-09-08 10:59:44 -0700397 if (write) {
398 return ret;
399 }
400
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800401 printk("Frequency Supported - ");
Thomas Wuc151f2e2015-09-08 10:59:44 -0700402
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800403 i = 0;
Thomas Wu7132bd32015-05-07 15:03:06 -0700404 while (i < NSS_FREQ_MAX_SCALE) {
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800405 printk("%dMhz ", nss_runtime_samples.freq_scale[i].frequency/1000000);
406 i++;
Thomas Wu0a0a9c92013-11-21 15:28:19 -0800407 }
Thomas Wu0e2fc4f2015-03-04 15:39:14 -0800408 printk("\n");
wthomas442c7972013-08-05 14:28:17 -0700409
Thomas Wuc151f2e2015-09-08 10:59:44 -0700410 *lenp = 0;
wthomas442c7972013-08-05 14:28:17 -0700411 return ret;
412}
413
414/*
Thomas Wu05495be2013-12-19 14:24:24 -0800415 * nss_get_average_inst_handler()
416 * Display AVG Inst Per Ms.
417 */
Stephen Wang52e6d342016-03-29 15:02:33 -0700418static int nss_get_average_inst_handler(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
Thomas Wu05495be2013-12-19 14:24:24 -0800419{
420 int ret;
421
422 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
423
Thomas Wuc151f2e2015-09-08 10:59:44 -0700424 if (write) {
425 return ret;
Thomas Wu05495be2013-12-19 14:24:24 -0800426 }
427
Thomas Wuc151f2e2015-09-08 10:59:44 -0700428 printk("Current Inst Per Ms %x\n", nss_runtime_samples.average);
429
430 *lenp = 0;
Thomas Wu05495be2013-12-19 14:24:24 -0800431 return ret;
432}
Arunkumar T28b2d742015-06-16 22:15:58 +0530433#endif
Thomas Wu05495be2013-12-19 14:24:24 -0800434
Sundarajan Srinivasan758e8f42014-12-08 14:56:24 -0800435#if (NSS_FW_DBG_SUPPORT == 1)
Thomas Wu05495be2013-12-19 14:24:24 -0800436/*
Abhishek Rastogi1626a902013-11-21 17:09:49 +0530437 * nss_debug_handler()
438 * Enable NSS debug output
439 */
Stephen Wang52e6d342016-03-29 15:02:33 -0700440static int nss_debug_handler(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
Abhishek Rastogi1626a902013-11-21 17:09:49 +0530441{
442 int ret;
443
444 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
445 if (!ret) {
446 if ((write) && (nss_ctl_debug != 0)) {
447 printk("Enabling NSS SPI Debug\n");
448 nss_hal_debug_enable();
449 }
450 }
451
452 return ret;
453}
Sundarajan Srinivasan758e8f42014-12-08 14:56:24 -0800454#endif
Abhishek Rastogi1626a902013-11-21 17:09:49 +0530455
456/*
Thomas Wu52075f42014-02-06 16:32:42 -0800457 * nss_coredump_handler()
458 * Send Signal To Coredump NSS Cores
459 */
Stephen Wang52e6d342016-03-29 15:02:33 -0700460static int nss_coredump_handler(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
Thomas Wu52075f42014-02-06 16:32:42 -0800461{
Guojun Jin32a3c6d2015-05-06 12:27:52 -0700462 struct nss_ctx_instance *nss_ctx = &nss_top_main.nss[NSS_CORE_0];
Thomas Wu52075f42014-02-06 16:32:42 -0800463 int ret;
464
465 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
466 if (!ret) {
Guojun Jin244ecab2017-03-16 16:04:25 -0700467 /*
468 * if nss_cmd_buf.coredump is not 0 or 1, panic will be disabled
469 * when NSS FW crashes, so OEM/ODM have a chance to use mdump
470 * to dump crash dump (coredump) and send dump to us for analysis.
471 */
472 if ((write) && (nss_ctl_debug != 0) && nss_cmd_buf.coredump == 1) {
Thomas Wu52075f42014-02-06 16:32:42 -0800473 printk("Coredumping to DDR\n");
Stephen Wang90c67de2016-04-26 15:15:59 -0700474 nss_hal_send_interrupt(nss_ctx, NSS_H2N_INTR_TRIGGER_COREDUMP);
Thomas Wu52075f42014-02-06 16:32:42 -0800475 }
476 }
477
478 return ret;
479}
480
481/*
Sundarajan Srinivasanf9c4a232014-11-18 13:25:40 -0800482 * nss_jumbo_mru_handler()
483 * Sysctl to modify nss_jumbo_mru
484 */
Stephen Wang52e6d342016-03-29 15:02:33 -0700485static int nss_jumbo_mru_handler(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
Sundarajan Srinivasanf9c4a232014-11-18 13:25:40 -0800486{
487 int ret;
488
489 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
490 if (ret) {
491 return ret;
492 }
493
494 if (write) {
495 nss_core_set_jumbo_mru(nss_jumbo_mru);
496 nss_info("jumbo_mru set to %d\n", nss_jumbo_mru);
497 }
498
499 return ret;
500}
501
502/* nss_paged_mode_handler()
503 * Sysctl to modify nss_paged_mode.
504 */
505
Stephen Wang52e6d342016-03-29 15:02:33 -0700506static int nss_paged_mode_handler(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
Sundarajan Srinivasanf9c4a232014-11-18 13:25:40 -0800507{
508 int ret;
509
510 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
511 if (ret) {
512 return ret;
513 }
514
515 if (write) {
516 nss_core_set_paged_mode(nss_paged_mode);
517 nss_info("paged_mode set to %d\n", nss_paged_mode);
518 }
519
520 return ret;
521}
522
Arunkumar T28b2d742015-06-16 22:15:58 +0530523#if (NSS_FREQ_SCALE_SUPPORT == 1)
Sundarajan Srinivasanf9c4a232014-11-18 13:25:40 -0800524/*
wthomas442c7972013-08-05 14:28:17 -0700525 * sysctl-tuning infrastructure.
526 */
Stephen Wang52e6d342016-03-29 15:02:33 -0700527static struct ctl_table nss_freq_table[] = {
wthomas442c7972013-08-05 14:28:17 -0700528 {
529 .procname = "current_freq",
530 .data = &nss_cmd_buf.current_freq,
531 .maxlen = sizeof(int),
532 .mode = 0644,
533 .proc_handler = &nss_current_freq_handler,
534 },
535 {
536 .procname = "freq_table",
537 .data = &nss_cmd_buf.max_freq,
538 .maxlen = sizeof(int),
539 .mode = 0644,
540 .proc_handler = &nss_get_freq_table_handler,
541 },
542 {
543 .procname = "auto_scale",
544 .data = &nss_cmd_buf.auto_scale,
545 .maxlen = sizeof(int),
546 .mode = 0644,
547 .proc_handler = &nss_auto_scale_handler,
548 },
Thomas Wu05495be2013-12-19 14:24:24 -0800549 {
550 .procname = "inst_per_sec",
551 .data = &nss_cmd_buf.average_inst,
552 .maxlen = sizeof(int),
553 .mode = 0644,
554 .proc_handler = &nss_get_average_inst_handler,
555 },
wthomas442c7972013-08-05 14:28:17 -0700556 { }
557};
Arunkumar T28b2d742015-06-16 22:15:58 +0530558#endif
wthomas442c7972013-08-05 14:28:17 -0700559
Stephen Wang52e6d342016-03-29 15:02:33 -0700560static struct ctl_table nss_general_table[] = {
Abhishek Rastogi5cd2e4c2013-11-13 18:09:08 +0530561 {
562 .procname = "redirect",
563 .data = &nss_ctl_redirect,
564 .maxlen = sizeof(int),
565 .mode = 0644,
Vijay Dewangan9db18752014-09-15 16:25:01 -0700566 .proc_handler = proc_dointvec,
Abhishek Rastogi5cd2e4c2013-11-13 18:09:08 +0530567 },
Sundarajan Srinivasan758e8f42014-12-08 14:56:24 -0800568#if (NSS_FW_DBG_SUPPORT == 1)
Abhishek Rastogi1626a902013-11-21 17:09:49 +0530569 {
570 .procname = "debug",
571 .data = &nss_ctl_debug,
572 .maxlen = sizeof(int),
573 .mode = 0644,
Vijay Dewangan9db18752014-09-15 16:25:01 -0700574 .proc_handler = &nss_debug_handler,
Abhishek Rastogi1626a902013-11-21 17:09:49 +0530575 },
Sundarajan Srinivasan758e8f42014-12-08 14:56:24 -0800576#endif
Thomas Wu52075f42014-02-06 16:32:42 -0800577 {
578 .procname = "coredump",
579 .data = &nss_cmd_buf.coredump,
580 .maxlen = sizeof(int),
581 .mode = 0644,
Vijay Dewangan9db18752014-09-15 16:25:01 -0700582 .proc_handler = &nss_coredump_handler,
Thomas Wu52075f42014-02-06 16:32:42 -0800583 },
Pamidipati, Vijayefcc4692014-05-09 14:47:38 +0530584 {
Saurabh Misra96998db2014-07-10 12:15:48 -0700585 .procname = "logbuf",
586 .data = &nss_ctl_logbuf,
587 .maxlen = sizeof(int),
588 .mode = 0644,
589 .proc_handler = &nss_logbuffer_handler,
590 },
Sundarajan Srinivasanf9c4a232014-11-18 13:25:40 -0800591 {
592 .procname = "jumbo_mru",
593 .data = &nss_jumbo_mru,
594 .maxlen = sizeof(int),
595 .mode = 0644,
596 .proc_handler = &nss_jumbo_mru_handler,
597 },
598 {
599 .procname = "paged_mode",
600 .data = &nss_paged_mode,
601 .maxlen = sizeof(int),
602 .mode = 0644,
603 .proc_handler = &nss_paged_mode_handler,
604 },
Abhishek Rastogi5cd2e4c2013-11-13 18:09:08 +0530605 { }
606};
607
Stephen Wang52e6d342016-03-29 15:02:33 -0700608static struct ctl_table nss_clock_dir[] = {
Arunkumar T28b2d742015-06-16 22:15:58 +0530609#if (NSS_FREQ_SCALE_SUPPORT == 1)
wthomas442c7972013-08-05 14:28:17 -0700610 {
Abhishek Rastogi5cd2e4c2013-11-13 18:09:08 +0530611 .procname = "clock",
612 .mode = 0555,
613 .child = nss_freq_table,
614 },
Arunkumar T28b2d742015-06-16 22:15:58 +0530615#endif
Abhishek Rastogi5cd2e4c2013-11-13 18:09:08 +0530616 {
617 .procname = "general",
618 .mode = 0555,
619 .child = nss_general_table,
wthomas442c7972013-08-05 14:28:17 -0700620 },
621 { }
622};
623
Stephen Wang52e6d342016-03-29 15:02:33 -0700624static struct ctl_table nss_root_dir[] = {
wthomas442c7972013-08-05 14:28:17 -0700625 {
626 .procname = "nss",
627 .mode = 0555,
628 .child = nss_clock_dir,
629 },
630 { }
631};
632
Stephen Wang52e6d342016-03-29 15:02:33 -0700633static struct ctl_table nss_root[] = {
wthomas442c7972013-08-05 14:28:17 -0700634 {
635 .procname = "dev",
636 .mode = 0555,
637 .child = nss_root_dir,
638 },
639 { }
640};
641
642static struct ctl_table_header *nss_dev_header;
643
644/*
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530645 * nss_init()
646 * Registers nss driver
647 */
648static int __init nss_init(void)
649{
Radha krishna Simha Jiguru3295b8b2017-03-12 10:54:15 +0530650#if (NSS_DT_SUPPORT == 1)
651 struct device_node *cmn = NULL;
652#endif
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530653 nss_info("Init NSS driver");
654
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -0800655#if (NSS_DT_SUPPORT == 1)
656 /*
Radha krishna Simha Jiguru3295b8b2017-03-12 10:54:15 +0530657 * Get reference to NSS common device node
658 */
659 cmn = of_find_node_by_name(NULL, "nss-common");
660 if (!cmn) {
661 nss_info_always("qca-nss-drv.ko is loaded for symbol link\n");
662 return 0;
663 }
664 of_node_put(cmn);
665
666 /*
Stephen Wang90c67de2016-04-26 15:15:59 -0700667 * Pick up HAL by target information
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -0800668 */
Stephen Wang90c67de2016-04-26 15:15:59 -0700669#if defined(NSS_HAL_IPQ806X_SUPPORT)
670 if (of_machine_is_compatible("qcom,ipq8064") || of_machine_is_compatible("qcom,ipq8062")) {
671 nss_top_main.hal_ops = &nss_hal_ipq806x_ops;
Stephen Wang5901def2016-05-09 16:21:03 -0700672 nss_top_main.data_plane_ops = &nss_data_plane_gmac_ops;
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -0800673 }
Stephen Wang90c67de2016-04-26 15:15:59 -0700674#endif
Stephen Wangbe348022016-05-02 17:11:34 -0700675#if defined(NSS_HAL_IPQ807x_SUPPORT)
676 if (of_machine_is_compatible("qcom,ipq807x")) {
677 nss_top_main.hal_ops = &nss_hal_ipq807x_ops;
Stephen Wangdf8323b2016-05-09 22:51:22 -0700678 nss_top_main.data_plane_ops = &nss_data_plane_edma_ops;
Stephen Wangbe348022016-05-02 17:11:34 -0700679 }
680#endif
Stephen Wang90c67de2016-04-26 15:15:59 -0700681#if defined(NSS_HAL_FSM9010_SUPPORT)
682 if (of_machine_is_compatible("qcom,fsm9010")) {
683 nss_top_main.hal_ops = &nss_hal_fsm9010_ops;
Stephen Wang5901def2016-05-09 16:21:03 -0700684 nss_top_main.data_plane_ops = &nss_data_plane_gmac_ops;
Stephen Wang90c67de2016-04-26 15:15:59 -0700685 }
686#endif
687 if (!nss_top_main.hal_ops) {
688 nss_info_always("No supported HAL compiled on this platform\n");
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -0800689 return -EFAULT;
690 }
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -0800691#else
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530692 /*
Stephen Wang90c67de2016-04-26 15:15:59 -0700693 * For banana, only ipq806x is supported
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530694 */
Stephen Wang90c67de2016-04-26 15:15:59 -0700695 nss_top_main.hal_ops = &nss_hal_ipq806x_ops;
Stephen Wang5901def2016-05-09 16:21:03 -0700696 nss_top_main.data_plane_ops = &nss_data_plane_gmac_ops;
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530697
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -0800698#endif /* NSS_DT_SUPPORT */
Stephen Wang90c67de2016-04-26 15:15:59 -0700699 nss_top_main.nss_hal_common_init_done = false;
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -0800700
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530701 /*
Stephen Wangb42ddc52015-12-17 18:10:35 -0800702 * Initialize data_plane workqueue
703 */
704 if (nss_data_plane_init_delay_work()) {
705 nss_warning("Error initializing nss_data_plane_workqueue\n");
706 return -EFAULT;
707 }
708
709 /*
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530710 * Enable spin locks
711 */
712 spin_lock_init(&(nss_top_main.lock));
713 spin_lock_init(&(nss_top_main.stats_lock));
c_athandfde2e912017-01-10 15:40:12 +0530714 mutex_init(&(nss_top_main.wq_lock));
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530715
716 /*
Abhishek Rastogi38cffff2013-06-02 11:25:47 +0530717 * Enable NSS statistics
718 */
719 nss_stats_init();
720
721 /*
wthomas442c7972013-08-05 14:28:17 -0700722 * Register sysctl table.
723 */
724 nss_dev_header = register_sysctl_table(nss_root);
725
726 /*
Vijay Dewangan9db18752014-09-15 16:25:01 -0700727 * Registering sysctl for ipv4/6 specific config.
728 */
729 nss_ipv4_register_sysctl();
730 nss_ipv6_register_sysctl();
731
Vijay Dewanganac7efc42015-02-09 16:04:53 -0800732 /*
Stephen Wang49b474b2016-03-25 10:40:30 -0700733 * Registering sysctl for n2h specific config.
Vijay Dewanganac7efc42015-02-09 16:04:53 -0800734 */
Stephen Wang49b474b2016-03-25 10:40:30 -0700735 nss_n2h_register_sysctl();
Vijay Dewanganac7efc42015-02-09 16:04:53 -0800736
Vijay Dewangan9db18752014-09-15 16:25:01 -0700737 /*
wthomas626147f2013-09-18 13:12:40 -0700738 * Setup Runtime Sample values
739 */
wthomas626147f2013-09-18 13:12:40 -0700740 nss_runtime_samples.freq_scale_index = 1;
741 nss_runtime_samples.freq_scale_ready = 0;
Thomas Wu9681f7e2013-11-06 13:12:57 -0800742 nss_runtime_samples.freq_scale_rate_limit_down = 0;
wthomas626147f2013-09-18 13:12:40 -0700743 nss_runtime_samples.buffer_index = 0;
744 nss_runtime_samples.sum = 0;
745 nss_runtime_samples.sample_count = 0;
746 nss_runtime_samples.average = 0;
747 nss_runtime_samples.message_rate_limit = 0;
Kiran Kumar C.S.K69fd5992014-01-06 20:58:14 +0530748 nss_runtime_samples.initialized = 0;
wthomas626147f2013-09-18 13:12:40 -0700749
Thomas Wu05495be2013-12-19 14:24:24 -0800750 nss_cmd_buf.current_freq = nss_runtime_samples.freq_scale[nss_runtime_samples.freq_scale_index].frequency;
751
wthomas626147f2013-09-18 13:12:40 -0700752 /*
753 * Initial Workqueue
754 */
755 nss_wq = create_workqueue("nss_freq_queue");
756
Thomas Wu0d112192015-04-13 11:37:22 -0700757#if (NSS_PM_SUPPORT == 1)
wthomas626147f2013-09-18 13:12:40 -0700758 /*
Pamidipati, Vijay7f413b52013-09-24 19:07:12 +0530759 * Initialize NSS Bus PM module
760 */
761 nss_pm_init();
762
763 /*
Pamidipati, Vijay5d27d812013-11-22 16:48:11 +0530764 * Register with Bus driver
765 */
766 pm_client = nss_pm_client_register(NSS_PM_CLIENT_NETAP);
767 if (!pm_client) {
768 nss_warning("Error registering with PM driver");
769 }
Sundarajan Srinivasan4691ba62014-11-07 11:24:07 -0800770#endif
Pamidipati, Vijay5d27d812013-11-22 16:48:11 +0530771
772 /*
Radha krishna Simha Jiguru7f424d52015-02-10 19:41:01 +0530773 * Initialize mtu size needed as start
774 */
Stephen Wanga428d0b2017-01-12 21:30:01 -0800775 nss_top_main.prev_mtu_sz = ETH_DATA_LEN;
Radha krishna Simha Jiguru7f424d52015-02-10 19:41:01 +0530776
777 /*
Guojun Jin1cb79522015-06-22 22:34:22 -0700778 * register panic handler and timeout control
779 */
780 nss_coredump_notify_register();
781 nss_coredump_init_delay_work();
782
783 /*
Stephen Wang1f6ad492016-01-27 23:42:06 -0800784 * Init capwap
785 */
786 nss_capwap_init();
787
788 /*
Amit Gupta316729b2016-08-12 12:21:15 +0530789 * INIT ppe on supported platform
790 */
791 if (of_machine_is_compatible("qcom,ipq807x")) {
792 nss_ppe_init();
793 }
794
795 /*
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530796 * Register platform_driver
797 */
798 return platform_driver_register(&nss_driver);
799}
800
801/*
802 * nss_cleanup()
803 * Unregisters nss driver
804 */
805static void __exit nss_cleanup(void)
806{
807 nss_info("Exit NSS driver");
wthomas442c7972013-08-05 14:28:17 -0700808
809 if (nss_dev_header)
810 unregister_sysctl_table(nss_dev_header);
811
Vijay Dewangan9db18752014-09-15 16:25:01 -0700812 /*
Vijay Dewangan488e5372014-12-29 21:40:11 -0800813 * Unregister n2h specific sysctl
814 */
Stephen Wang49b474b2016-03-25 10:40:30 -0700815 nss_n2h_unregister_sysctl();
Vijay Dewangan488e5372014-12-29 21:40:11 -0800816
817 /*
Vijay Dewangan9db18752014-09-15 16:25:01 -0700818 * Unregister ipv4/6 specific sysctl
819 */
820 nss_ipv4_unregister_sysctl();
821 nss_ipv6_unregister_sysctl();
822
Stephen Wangb42ddc52015-12-17 18:10:35 -0800823 nss_data_plane_destroy_delay_work();
824
Amit Gupta316729b2016-08-12 12:21:15 +0530825 /*
826 * cleanup ppe on supported platform
827 */
828 if (of_machine_is_compatible("qcom,ipq807x")) {
829 nss_ppe_free();
830 }
831
Abhishek Rastogibc74e432013-04-02 10:28:22 +0530832 platform_driver_unregister(&nss_driver);
833}
834
835module_init(nss_init);
836module_exit(nss_cleanup);
837
838MODULE_DESCRIPTION("QCA NSS Driver");
839MODULE_AUTHOR("Qualcomm Atheros Inc");
840MODULE_LICENSE("Dual BSD/GPL");