blob: 55b41922290ca9130dfab4a21d85b60bc2d04974 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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#include <linux/module.h>
14#include <linux/slab.h>
15#include <linux/err.h>
16#include <linux/platform_device.h>
Jeff Johnsonb3377e32011-11-18 23:32:27 -080017#include <linux/miscdevice.h>
18#include <linux/fs.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070019#include <linux/wcnss_wlan.h>
Ankur Nandwanib0039b02011-08-09 14:00:45 -070020#include <linux/platform_data/qcom_wcnss_device.h>
David Collinsb727f7d2011-09-12 11:54:49 -070021#include <linux/workqueue.h>
22#include <linux/jiffies.h>
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -070023#include <linux/gpio.h>
Sameer Thalappilf138da52012-07-30 12:56:37 -070024#include <linux/wakelock.h>
Sameer Thalappileeb8c412012-08-10 17:17:09 -070025#include <linux/delay.h>
Sameer Thalappil8d686d42012-08-24 10:07:31 -070026#include <linux/of.h>
27#include <linux/of_gpio.h>
Sameer Thalappiled3f7da2012-11-01 12:54:01 -070028#include <linux/clk.h>
Sameer Thalappil1b3e6112012-12-14 15:16:07 -080029#include <linux/ratelimit.h>
Stephen Boyd77db8bb2012-06-27 15:15:16 -070030
Sameer Thalappileeb8c412012-08-10 17:17:09 -070031#include <mach/msm_smd.h>
Sameer Thalappil19e02352012-09-24 15:26:12 -070032#include <mach/msm_iomap.h>
Stephen Boyd77db8bb2012-06-27 15:15:16 -070033#include <mach/subsystem_restart.h>
34
Sameer Thalappil24db5282012-09-10 11:58:33 -070035#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
36#include "wcnss_prealloc.h"
37#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038
39#define DEVICE "wcnss_wlan"
40#define VERSION "1.01"
41#define WCNSS_PIL_DEVICE "wcnss"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042
Ankur Nandwanib0039b02011-08-09 14:00:45 -070043/* module params */
44#define WCNSS_CONFIG_UNSPECIFIED (-1)
Sameer Thalappileeb8c412012-08-10 17:17:09 -070045
Ankur Nandwania4510492011-08-29 15:56:23 -070046static int has_48mhz_xo = WCNSS_CONFIG_UNSPECIFIED;
Jeff Johnsonb3377e32011-11-18 23:32:27 -080047module_param(has_48mhz_xo, int, S_IWUSR | S_IRUGO);
Ankur Nandwanib0039b02011-08-09 14:00:45 -070048MODULE_PARM_DESC(has_48mhz_xo, "Is an external 48 MHz XO present");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049
Sameer Thalappiled3f7da2012-11-01 12:54:01 -070050static DEFINE_SPINLOCK(reg_spinlock);
51
52#define MSM_RIVA_PHYS 0x03204000
53#define MSM_PRONTO_PHYS 0xfb21b000
54
55#define RIVA_SPARE_OFFSET 0x0b4
56#define RIVA_SUSPEND_BIT BIT(24)
57
Sameer Thalappil77716e52012-11-08 13:41:04 -080058#define MSM_RIVA_CCU_BASE 0x03200800
59
60#define CCU_INVALID_ADDR_OFFSET 0x100
61#define CCU_LAST_ADDR0_OFFSET 0x104
62#define CCU_LAST_ADDR1_OFFSET 0x108
63#define CCU_LAST_ADDR2_OFFSET 0x10c
64
Sameer Thalappil1b3e6112012-12-14 15:16:07 -080065#define MSM_PRONTO_A2XB_BASE 0xfb100400
66#define A2XB_CFG_OFFSET 0x00
67#define A2XB_INT_SRC_OFFSET 0x0c
68#define A2XB_ERR_INFO_OFFSET 0x1c
69
Sameer Thalappileeb8c412012-08-10 17:17:09 -070070#define WCNSS_CTRL_CHANNEL "WCNSS_CTRL"
71#define WCNSS_MAX_FRAME_SIZE 500
72#define WCNSS_VERSION_LEN 30
73
74/* message types */
75#define WCNSS_CTRL_MSG_START 0x01000000
76#define WCNSS_VERSION_REQ (WCNSS_CTRL_MSG_START + 0)
77#define WCNSS_VERSION_RSP (WCNSS_CTRL_MSG_START + 1)
78
79#define VALID_VERSION(version) \
80 ((strncmp(version, "INVALID", WCNSS_VERSION_LEN)) ? 1 : 0)
81
82struct smd_msg_hdr {
83 unsigned int type;
84 unsigned int len;
85};
86
87struct wcnss_version {
88 struct smd_msg_hdr hdr;
89 unsigned char major;
90 unsigned char minor;
91 unsigned char version;
92 unsigned char revision;
93};
94
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095static struct {
96 struct platform_device *pdev;
97 void *pil;
98 struct resource *mmio_res;
99 struct resource *tx_irq_res;
100 struct resource *rx_irq_res;
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -0700101 struct resource *gpios_5wire;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102 const struct dev_pm_ops *pm_ops;
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800103 int triggered;
104 int smd_channel_ready;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700105 smd_channel_t *smd_ch;
106 unsigned char wcnss_version[WCNSS_VERSION_LEN];
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800107 unsigned int serial_number;
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800108 int thermal_mitigation;
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700109 enum wcnss_hw_type wcnss_hw_type;
Sameer Thalappila112bbc2012-05-23 12:20:32 -0700110 void (*tm_notify)(struct device *, int);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 struct wcnss_wlan_config wlan_config;
David Collinsb727f7d2011-09-12 11:54:49 -0700112 struct delayed_work wcnss_work;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700113 struct work_struct wcnssctrl_version_work;
114 struct work_struct wcnssctrl_rx_work;
Sameer Thalappilf138da52012-07-30 12:56:37 -0700115 struct wake_lock wcnss_wake_lock;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700116 void __iomem *msm_wcnss_base;
Sameer Thalappild3aad702013-01-22 18:52:24 -0800117 void __iomem *riva_ccu_base;
118 void __iomem *pronto_a2xb_base;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700119} *penv = NULL;
120
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800121static ssize_t wcnss_serial_number_show(struct device *dev,
122 struct device_attribute *attr, char *buf)
123{
124 if (!penv)
125 return -ENODEV;
126
127 return scnprintf(buf, PAGE_SIZE, "%08X\n", penv->serial_number);
128}
129
130static ssize_t wcnss_serial_number_store(struct device *dev,
Sameer Thalappilf138da52012-07-30 12:56:37 -0700131 struct device_attribute *attr, const char *buf, size_t count)
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800132{
133 unsigned int value;
134
135 if (!penv)
136 return -ENODEV;
137
138 if (sscanf(buf, "%08X", &value) != 1)
139 return -EINVAL;
140
141 penv->serial_number = value;
142 return count;
143}
144
145static DEVICE_ATTR(serial_number, S_IRUSR | S_IWUSR,
146 wcnss_serial_number_show, wcnss_serial_number_store);
147
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800148
149static ssize_t wcnss_thermal_mitigation_show(struct device *dev,
150 struct device_attribute *attr, char *buf)
151{
152 if (!penv)
153 return -ENODEV;
154
155 return scnprintf(buf, PAGE_SIZE, "%u\n", penv->thermal_mitigation);
156}
157
158static ssize_t wcnss_thermal_mitigation_store(struct device *dev,
Sameer Thalappilf138da52012-07-30 12:56:37 -0700159 struct device_attribute *attr, const char *buf, size_t count)
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800160{
161 int value;
162
163 if (!penv)
164 return -ENODEV;
165
166 if (sscanf(buf, "%d", &value) != 1)
167 return -EINVAL;
168 penv->thermal_mitigation = value;
Jeff Johnson61374652012-04-16 20:51:48 -0700169 if (penv->tm_notify)
Sameer Thalappila112bbc2012-05-23 12:20:32 -0700170 (penv->tm_notify)(dev, value);
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800171 return count;
172}
173
174static DEVICE_ATTR(thermal_mitigation, S_IRUSR | S_IWUSR,
175 wcnss_thermal_mitigation_show, wcnss_thermal_mitigation_store);
176
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700177
178static ssize_t wcnss_version_show(struct device *dev,
179 struct device_attribute *attr, char *buf)
180{
181 if (!penv)
182 return -ENODEV;
183
184 return scnprintf(buf, PAGE_SIZE, "%s", penv->wcnss_version);
185}
186
187static DEVICE_ATTR(wcnss_version, S_IRUSR,
188 wcnss_version_show, NULL);
189
Sameer Thalappil77716e52012-11-08 13:41:04 -0800190/* wcnss_reset_intr() is invoked when host drivers fails to
191 * communicate with WCNSS over SMD; so logging these registers
Sameer Thalappild3aad702013-01-22 18:52:24 -0800192 * helps to know WCNSS failure reason
193 */
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800194void wcnss_riva_log_debug_regs(void)
Sameer Thalappil77716e52012-11-08 13:41:04 -0800195{
Sameer Thalappil77716e52012-11-08 13:41:04 -0800196 void __iomem *ccu_reg;
197 u32 reg = 0;
198
Sameer Thalappild3aad702013-01-22 18:52:24 -0800199 ccu_reg = penv->riva_ccu_base + CCU_INVALID_ADDR_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800200 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800201 pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800202
Sameer Thalappild3aad702013-01-22 18:52:24 -0800203 ccu_reg = penv->riva_ccu_base + CCU_LAST_ADDR0_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800204 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800205 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800206
Sameer Thalappild3aad702013-01-22 18:52:24 -0800207 ccu_reg = penv->riva_ccu_base + CCU_LAST_ADDR1_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800208 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800209 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800210
Sameer Thalappild3aad702013-01-22 18:52:24 -0800211 ccu_reg = penv->riva_ccu_base + CCU_LAST_ADDR2_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800212 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800213 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800214
Sameer Thalappil77716e52012-11-08 13:41:04 -0800215}
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800216EXPORT_SYMBOL(wcnss_riva_log_debug_regs);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800217
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800218/* Log pronto debug registers before sending reset interrupt */
219void wcnss_pronto_log_debug_regs(void)
220{
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800221 void __iomem *reg_addr;
222 u32 reg = 0;
223
Sameer Thalappild3aad702013-01-22 18:52:24 -0800224 reg_addr = penv->pronto_a2xb_base + A2XB_CFG_OFFSET;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800225 reg = readl_relaxed(reg_addr);
226 pr_info_ratelimited("%s: A2XB_CFG_OFFSET %08x\n", __func__, reg);
227
Sameer Thalappild3aad702013-01-22 18:52:24 -0800228 reg_addr = penv->pronto_a2xb_base + A2XB_INT_SRC_OFFSET;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800229 reg = readl_relaxed(reg_addr);
230 pr_info_ratelimited("%s: A2XB_INT_SRC_OFFSET %08x\n", __func__, reg);
231
Sameer Thalappild3aad702013-01-22 18:52:24 -0800232 reg_addr = penv->pronto_a2xb_base + A2XB_ERR_INFO_OFFSET;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800233 reg = readl_relaxed(reg_addr);
234 pr_info_ratelimited("%s: A2XB_ERR_INFO_OFFSET %08x\n", __func__, reg);
235
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800236}
237EXPORT_SYMBOL(wcnss_pronto_log_debug_regs);
238
239/* interface to reset wcnss by sending the reset interrupt */
Sameer Thalappil19e02352012-09-24 15:26:12 -0700240void wcnss_reset_intr(void)
241{
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800242 if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
243 wcnss_pronto_log_debug_regs();
Sameer Thalappil19e02352012-09-24 15:26:12 -0700244 pr_err("%s: reset interrupt not supported\n", __func__);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800245 return;
Sameer Thalappil19e02352012-09-24 15:26:12 -0700246 }
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800247 wcnss_riva_log_debug_regs();
Sameer Thalappil77716e52012-11-08 13:41:04 -0800248 wmb();
249 __raw_writel(1 << 24, MSM_APCS_GCC_BASE + 0x8);
Sameer Thalappil19e02352012-09-24 15:26:12 -0700250}
251EXPORT_SYMBOL(wcnss_reset_intr);
252
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800253static int wcnss_create_sysfs(struct device *dev)
254{
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800255 int ret;
256
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800257 if (!dev)
258 return -ENODEV;
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800259
260 ret = device_create_file(dev, &dev_attr_serial_number);
261 if (ret)
262 return ret;
263
264 ret = device_create_file(dev, &dev_attr_thermal_mitigation);
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700265 if (ret)
266 goto remove_serial;
267
268 ret = device_create_file(dev, &dev_attr_wcnss_version);
269 if (ret)
270 goto remove_thermal;
271
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800272 return 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700273
274remove_thermal:
275 device_remove_file(dev, &dev_attr_thermal_mitigation);
276remove_serial:
277 device_remove_file(dev, &dev_attr_serial_number);
278
279 return ret;
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800280}
281
282static void wcnss_remove_sysfs(struct device *dev)
283{
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800284 if (dev) {
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800285 device_remove_file(dev, &dev_attr_serial_number);
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800286 device_remove_file(dev, &dev_attr_thermal_mitigation);
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700287 device_remove_file(dev, &dev_attr_wcnss_version);
288 }
289}
290static void wcnss_smd_notify_event(void *data, unsigned int event)
291{
292 int len = 0;
293
294 if (penv != data) {
295 pr_err("wcnss: invalid env pointer in smd callback\n");
296 return;
297 }
298 switch (event) {
299 case SMD_EVENT_DATA:
300 len = smd_read_avail(penv->smd_ch);
301 if (len < 0)
302 pr_err("wcnss: failed to read from smd %d\n", len);
303 schedule_work(&penv->wcnssctrl_rx_work);
304 break;
305
306 case SMD_EVENT_OPEN:
307 pr_debug("wcnss: opening WCNSS SMD channel :%s",
308 WCNSS_CTRL_CHANNEL);
309 if (!VALID_VERSION(penv->wcnss_version))
310 schedule_work(&penv->wcnssctrl_version_work);
311 break;
312
313 case SMD_EVENT_CLOSE:
314 pr_debug("wcnss: closing WCNSS SMD channel :%s",
315 WCNSS_CTRL_CHANNEL);
316 break;
317
318 default:
319 break;
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800320 }
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800321}
322
David Collinsb727f7d2011-09-12 11:54:49 -0700323static void wcnss_post_bootup(struct work_struct *work)
324{
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700325 pr_info("%s: Cancel APPS vote for Iris & WCNSS\n", __func__);
David Collinsb727f7d2011-09-12 11:54:49 -0700326
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700327 /* Since WCNSS is up, cancel any APPS vote for Iris & WCNSS VREGs */
David Collinsb727f7d2011-09-12 11:54:49 -0700328 wcnss_wlan_power(&penv->pdev->dev, &penv->wlan_config,
329 WCNSS_WLAN_SWITCH_OFF);
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700330
331}
332
333static int
334wcnss_pronto_gpios_config(struct device *dev, bool enable)
335{
336 int rc = 0;
337 int i, j;
338 int WCNSS_WLAN_NUM_GPIOS = 5;
339
340 for (i = 0; i < WCNSS_WLAN_NUM_GPIOS; i++) {
341 int gpio = of_get_gpio(dev->of_node, i);
342 if (enable) {
343 rc = gpio_request(gpio, "wcnss_wlan");
344 if (rc) {
345 pr_err("WCNSS gpio_request %d err %d\n",
346 gpio, rc);
347 goto fail;
348 }
349 } else
350 gpio_free(gpio);
351 }
352
353 return rc;
354
355fail:
356 for (j = WCNSS_WLAN_NUM_GPIOS-1; j >= 0; j--) {
357 int gpio = of_get_gpio(dev->of_node, i);
358 gpio_free(gpio);
359 }
360 return rc;
David Collinsb727f7d2011-09-12 11:54:49 -0700361}
362
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -0700363static int
364wcnss_gpios_config(struct resource *gpios_5wire, bool enable)
365{
366 int i, j;
367 int rc = 0;
368
369 for (i = gpios_5wire->start; i <= gpios_5wire->end; i++) {
370 if (enable) {
371 rc = gpio_request(i, gpios_5wire->name);
372 if (rc) {
373 pr_err("WCNSS gpio_request %d err %d\n", i, rc);
374 goto fail;
375 }
376 } else
377 gpio_free(i);
378 }
379
380 return rc;
381
382fail:
383 for (j = i-1; j >= gpios_5wire->start; j--)
384 gpio_free(j);
385 return rc;
386}
387
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388static int __devinit
389wcnss_wlan_ctrl_probe(struct platform_device *pdev)
390{
Sameer Thalappil667f43f2011-10-10 11:12:54 -0700391 if (!penv)
392 return -ENODEV;
393
394 penv->smd_channel_ready = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700395
396 pr_info("%s: SMD ctrl channel up\n", __func__);
397
David Collinsb727f7d2011-09-12 11:54:49 -0700398 /* Schedule a work to do any post boot up activity */
399 INIT_DELAYED_WORK(&penv->wcnss_work, wcnss_post_bootup);
400 schedule_delayed_work(&penv->wcnss_work, msecs_to_jiffies(10000));
401
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700402 return 0;
403}
404
Sameer Thalappil13f45182012-07-23 18:02:45 -0700405void wcnss_flush_delayed_boot_votes()
406{
407 flush_delayed_work_sync(&penv->wcnss_work);
408}
409EXPORT_SYMBOL(wcnss_flush_delayed_boot_votes);
410
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411static int __devexit
412wcnss_wlan_ctrl_remove(struct platform_device *pdev)
413{
414 if (penv)
415 penv->smd_channel_ready = 0;
416
417 pr_info("%s: SMD ctrl channel down\n", __func__);
418
419 return 0;
420}
421
422
423static struct platform_driver wcnss_wlan_ctrl_driver = {
424 .driver = {
425 .name = "WLAN_CTRL",
426 .owner = THIS_MODULE,
427 },
428 .probe = wcnss_wlan_ctrl_probe,
429 .remove = __devexit_p(wcnss_wlan_ctrl_remove),
430};
431
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700432static int __devexit
433wcnss_ctrl_remove(struct platform_device *pdev)
434{
435 if (penv && penv->smd_ch)
436 smd_close(penv->smd_ch);
437
438 return 0;
439}
440
441static int __devinit
442wcnss_ctrl_probe(struct platform_device *pdev)
443{
444 int ret = 0;
445
446 if (!penv)
447 return -ENODEV;
448
449 ret = smd_named_open_on_edge(WCNSS_CTRL_CHANNEL, SMD_APPS_WCNSS,
450 &penv->smd_ch, penv, wcnss_smd_notify_event);
451 if (ret < 0) {
452 pr_err("wcnss: cannot open the smd command channel %s: %d\n",
453 WCNSS_CTRL_CHANNEL, ret);
454 return -ENODEV;
455 }
456 smd_disable_read_intr(penv->smd_ch);
457
458 return 0;
459}
460
461/* platform device for WCNSS_CTRL SMD channel */
462static struct platform_driver wcnss_ctrl_driver = {
463 .driver = {
464 .name = "WCNSS_CTRL",
465 .owner = THIS_MODULE,
466 },
467 .probe = wcnss_ctrl_probe,
468 .remove = __devexit_p(wcnss_ctrl_remove),
469};
470
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700471struct device *wcnss_wlan_get_device(void)
472{
473 if (penv && penv->pdev && penv->smd_channel_ready)
474 return &penv->pdev->dev;
475 return NULL;
476}
477EXPORT_SYMBOL(wcnss_wlan_get_device);
478
Sameer Thalappil409ed352011-12-07 10:53:31 -0800479struct platform_device *wcnss_get_platform_device(void)
480{
481 if (penv && penv->pdev)
482 return penv->pdev;
483 return NULL;
484}
485EXPORT_SYMBOL(wcnss_get_platform_device);
486
487struct wcnss_wlan_config *wcnss_get_wlan_config(void)
488{
489 if (penv && penv->pdev)
490 return &penv->wlan_config;
491 return NULL;
492}
493EXPORT_SYMBOL(wcnss_get_wlan_config);
494
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700495struct resource *wcnss_wlan_get_memory_map(struct device *dev)
496{
497 if (penv && dev && (dev == &penv->pdev->dev) && penv->smd_channel_ready)
498 return penv->mmio_res;
499 return NULL;
500}
501EXPORT_SYMBOL(wcnss_wlan_get_memory_map);
502
503int wcnss_wlan_get_dxe_tx_irq(struct device *dev)
504{
505 if (penv && dev && (dev == &penv->pdev->dev) &&
506 penv->tx_irq_res && penv->smd_channel_ready)
507 return penv->tx_irq_res->start;
508 return WCNSS_WLAN_IRQ_INVALID;
509}
510EXPORT_SYMBOL(wcnss_wlan_get_dxe_tx_irq);
511
512int wcnss_wlan_get_dxe_rx_irq(struct device *dev)
513{
514 if (penv && dev && (dev == &penv->pdev->dev) &&
515 penv->rx_irq_res && penv->smd_channel_ready)
516 return penv->rx_irq_res->start;
517 return WCNSS_WLAN_IRQ_INVALID;
518}
519EXPORT_SYMBOL(wcnss_wlan_get_dxe_rx_irq);
520
521void wcnss_wlan_register_pm_ops(struct device *dev,
522 const struct dev_pm_ops *pm_ops)
523{
524 if (penv && dev && (dev == &penv->pdev->dev) && pm_ops)
525 penv->pm_ops = pm_ops;
526}
527EXPORT_SYMBOL(wcnss_wlan_register_pm_ops);
528
Sameer Thalappilecdd1002011-09-09 10:52:27 -0700529void wcnss_wlan_unregister_pm_ops(struct device *dev,
530 const struct dev_pm_ops *pm_ops)
531{
532 if (penv && dev && (dev == &penv->pdev->dev) && pm_ops) {
533 if (pm_ops->suspend != penv->pm_ops->suspend ||
534 pm_ops->resume != penv->pm_ops->resume)
535 pr_err("PM APIs dont match with registered APIs\n");
536 penv->pm_ops = NULL;
537 }
538}
539EXPORT_SYMBOL(wcnss_wlan_unregister_pm_ops);
540
Sameer Thalappila112bbc2012-05-23 12:20:32 -0700541void wcnss_register_thermal_mitigation(struct device *dev,
542 void (*tm_notify)(struct device *, int))
Jeff Johnson61374652012-04-16 20:51:48 -0700543{
Sameer Thalappila112bbc2012-05-23 12:20:32 -0700544 if (penv && dev && tm_notify)
Jeff Johnson61374652012-04-16 20:51:48 -0700545 penv->tm_notify = tm_notify;
546}
547EXPORT_SYMBOL(wcnss_register_thermal_mitigation);
548
Sameer Thalappila112bbc2012-05-23 12:20:32 -0700549void wcnss_unregister_thermal_mitigation(
550 void (*tm_notify)(struct device *, int))
Jeff Johnson61374652012-04-16 20:51:48 -0700551{
552 if (penv && tm_notify) {
553 if (tm_notify != penv->tm_notify)
554 pr_err("tm_notify doesn't match registered\n");
555 penv->tm_notify = NULL;
556 }
557}
558EXPORT_SYMBOL(wcnss_unregister_thermal_mitigation);
559
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800560unsigned int wcnss_get_serial_number(void)
561{
562 if (penv)
563 return penv->serial_number;
564 return 0;
565}
566EXPORT_SYMBOL(wcnss_get_serial_number);
567
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700568static int enable_wcnss_suspend_notify;
569
570static int enable_wcnss_suspend_notify_set(const char *val,
571 struct kernel_param *kp)
572{
573 int ret;
574
575 ret = param_set_int(val, kp);
576 if (ret)
577 return ret;
578
579 if (enable_wcnss_suspend_notify)
580 pr_debug("Suspend notification activated for wcnss\n");
581
582 return 0;
583}
584module_param_call(enable_wcnss_suspend_notify, enable_wcnss_suspend_notify_set,
585 param_get_int, &enable_wcnss_suspend_notify, S_IRUGO | S_IWUSR);
586
587
588void wcnss_suspend_notify(void)
589{
590 void __iomem *pmu_spare_reg;
591 u32 reg = 0;
592 unsigned long flags;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700593
594 if (!enable_wcnss_suspend_notify)
595 return;
596
597 if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
598 return;
599
600 /* For Riva */
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700601 pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
602 spin_lock_irqsave(&reg_spinlock, flags);
603 reg = readl_relaxed(pmu_spare_reg);
604 reg |= RIVA_SUSPEND_BIT;
605 writel_relaxed(reg, pmu_spare_reg);
606 spin_unlock_irqrestore(&reg_spinlock, flags);
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700607}
608EXPORT_SYMBOL(wcnss_suspend_notify);
609
610void wcnss_resume_notify(void)
611{
612 void __iomem *pmu_spare_reg;
613 u32 reg = 0;
614 unsigned long flags;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700615
616 if (!enable_wcnss_suspend_notify)
617 return;
618
619 if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
620 return;
621
622 /* For Riva */
623 pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
624
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700625 spin_lock_irqsave(&reg_spinlock, flags);
626 reg = readl_relaxed(pmu_spare_reg);
627 reg &= ~RIVA_SUSPEND_BIT;
628 writel_relaxed(reg, pmu_spare_reg);
629 spin_unlock_irqrestore(&reg_spinlock, flags);
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700630}
631EXPORT_SYMBOL(wcnss_resume_notify);
632
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700633static int wcnss_wlan_suspend(struct device *dev)
634{
635 if (penv && dev && (dev == &penv->pdev->dev) &&
636 penv->smd_channel_ready &&
637 penv->pm_ops && penv->pm_ops->suspend)
638 return penv->pm_ops->suspend(dev);
639 return 0;
640}
641
642static int wcnss_wlan_resume(struct device *dev)
643{
644 if (penv && dev && (dev == &penv->pdev->dev) &&
645 penv->smd_channel_ready &&
646 penv->pm_ops && penv->pm_ops->resume)
647 return penv->pm_ops->resume(dev);
648 return 0;
649}
650
Sameer Thalappilf138da52012-07-30 12:56:37 -0700651void wcnss_prevent_suspend()
652{
653 if (penv)
654 wake_lock(&penv->wcnss_wake_lock);
655}
656EXPORT_SYMBOL(wcnss_prevent_suspend);
657
658void wcnss_allow_suspend()
659{
660 if (penv)
661 wake_unlock(&penv->wcnss_wake_lock);
662}
663EXPORT_SYMBOL(wcnss_allow_suspend);
664
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700665int wcnss_hardware_type(void)
666{
667 if (penv)
668 return penv->wcnss_hw_type;
669 else
670 return -ENODEV;
671}
672EXPORT_SYMBOL(wcnss_hardware_type);
673
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700674static int wcnss_smd_tx(void *data, int len)
675{
676 int ret = 0;
677
678 ret = smd_write_avail(penv->smd_ch);
679 if (ret < len) {
680 pr_err("wcnss: no space available for smd frame\n");
681 ret = -ENOSPC;
682 }
683 ret = smd_write(penv->smd_ch, data, len);
684 if (ret < len) {
685 pr_err("wcnss: failed to write Command %d", len);
686 ret = -ENODEV;
687 }
688 return ret;
689}
690
691static void wcnssctrl_rx_handler(struct work_struct *worker)
692{
693 int len = 0;
694 int rc = 0;
695 unsigned char buf[WCNSS_MAX_FRAME_SIZE];
696 struct smd_msg_hdr *phdr;
697 struct wcnss_version *pversion;
698
699 len = smd_read_avail(penv->smd_ch);
700 if (len > WCNSS_MAX_FRAME_SIZE) {
701 pr_err("wcnss: frame larger than the allowed size\n");
702 smd_read(penv->smd_ch, NULL, len);
703 return;
704 }
705 if (len <= 0)
706 return;
707
708 rc = smd_read(penv->smd_ch, buf, len);
709 if (rc < len) {
710 pr_err("wcnss: incomplete data read from smd\n");
711 return;
712 }
713
714 phdr = (struct smd_msg_hdr *)buf;
715
716 switch (phdr->type) {
717
718 case WCNSS_VERSION_RSP:
719 pversion = (struct wcnss_version *)buf;
720 if (len != sizeof(struct wcnss_version)) {
721 pr_err("wcnss: invalid version data from wcnss %d\n",
722 len);
723 return;
724 }
725 snprintf(penv->wcnss_version, WCNSS_VERSION_LEN,
726 "%02x%02x%02x%02x", pversion->major, pversion->minor,
727 pversion->version, pversion->revision);
728 pr_info("wcnss: version %s\n", penv->wcnss_version);
729 break;
730
731 default:
732 pr_err("wcnss: invalid message type %d\n", phdr->type);
733 }
734 return;
735}
736
737static void wcnss_send_version_req(struct work_struct *worker)
738{
739 struct smd_msg_hdr smd_msg;
740 int ret = 0;
741
742 smd_msg.type = WCNSS_VERSION_REQ;
743 smd_msg.len = sizeof(smd_msg);
744 ret = wcnss_smd_tx(&smd_msg, smd_msg.len);
745 if (ret < 0)
746 pr_err("wcnss: smd tx failed\n");
747
748 return;
749}
750
Jeff Johnsonb3377e32011-11-18 23:32:27 -0800751static int
752wcnss_trigger_config(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700753{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700754 int ret;
Ankur Nandwanib0039b02011-08-09 14:00:45 -0700755 struct qcom_wcnss_opts *pdata;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700756 unsigned long wcnss_phys_addr;
757 int size = 0;
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700758 int has_pronto_hw = of_property_read_bool(pdev->dev.of_node,
759 "qcom,has_pronto_hw");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700760
Jeff Johnsonb3377e32011-11-18 23:32:27 -0800761 /* make sure we are only triggered once */
762 if (penv->triggered)
763 return 0;
764 penv->triggered = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700765
Ankur Nandwanib0039b02011-08-09 14:00:45 -0700766 /* initialize the WCNSS device configuration */
767 pdata = pdev->dev.platform_data;
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700768 if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo) {
769 if (has_pronto_hw) {
770 has_48mhz_xo = of_property_read_bool(pdev->dev.of_node,
771 "qcom,has_48mhz_xo");
772 penv->wcnss_hw_type = WCNSS_PRONTO_HW;
773 } else {
774 penv->wcnss_hw_type = WCNSS_RIVA_HW;
775 has_48mhz_xo = pdata->has_48mhz_xo;
776 }
777 }
Ankur Nandwanib0039b02011-08-09 14:00:45 -0700778 penv->wlan_config.use_48mhz_xo = has_48mhz_xo;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700779
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800780 penv->thermal_mitigation = 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700781 strlcpy(penv->wcnss_version, "INVALID", WCNSS_VERSION_LEN);
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800782
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -0700783 /* Configure 5 wire GPIOs */
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700784 if (!has_pronto_hw) {
785 penv->gpios_5wire = platform_get_resource_byname(pdev,
786 IORESOURCE_IO, "wcnss_gpios_5wire");
787
788 /* allocate 5-wire GPIO resources */
789 if (!penv->gpios_5wire) {
790 dev_err(&pdev->dev, "insufficient IO resources\n");
791 ret = -ENOENT;
792 goto fail_gpio_res;
793 }
794 ret = wcnss_gpios_config(penv->gpios_5wire, true);
795 } else
796 ret = wcnss_pronto_gpios_config(&pdev->dev, true);
797
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -0700798 if (ret) {
799 dev_err(&pdev->dev, "WCNSS gpios config failed.\n");
800 goto fail_gpio_res;
801 }
802
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803 /* power up the WCNSS */
804 ret = wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
805 WCNSS_WLAN_SWITCH_ON);
806 if (ret) {
807 dev_err(&pdev->dev, "WCNSS Power-up failed.\n");
808 goto fail_power;
809 }
810
811 /* trigger initialization of the WCNSS */
Stephen Boyd77db8bb2012-06-27 15:15:16 -0700812 penv->pil = subsystem_get(WCNSS_PIL_DEVICE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700813 if (IS_ERR(penv->pil)) {
814 dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
815 ret = PTR_ERR(penv->pil);
816 penv->pil = NULL;
817 goto fail_pil;
818 }
819
820 /* allocate resources */
821 penv->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
822 "wcnss_mmio");
823 penv->tx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
824 "wcnss_wlantx_irq");
825 penv->rx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
826 "wcnss_wlanrx_irq");
827
828 if (!(penv->mmio_res && penv->tx_irq_res && penv->rx_irq_res)) {
829 dev_err(&pdev->dev, "insufficient resources\n");
830 ret = -ENOENT;
831 goto fail_res;
832 }
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700833 INIT_WORK(&penv->wcnssctrl_rx_work, wcnssctrl_rx_handler);
834 INIT_WORK(&penv->wcnssctrl_version_work, wcnss_send_version_req);
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800835
Sameer Thalappilf138da52012-07-30 12:56:37 -0700836 wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss");
837
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700838 if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
839 size = 0x3000;
840 wcnss_phys_addr = MSM_PRONTO_PHYS;
841 } else {
842 wcnss_phys_addr = MSM_RIVA_PHYS;
843 size = SZ_256;
844 }
845
846 penv->msm_wcnss_base = ioremap(wcnss_phys_addr, size);
847 if (!penv->msm_wcnss_base) {
848 ret = -ENOMEM;
849 pr_err("%s: ioremap wcnss physical failed\n", __func__);
850 goto fail_wake;
851 }
852
Sameer Thalappild3aad702013-01-22 18:52:24 -0800853 if (wcnss_hardware_type() == WCNSS_RIVA_HW) {
854 penv->riva_ccu_base = ioremap(MSM_RIVA_CCU_BASE, SZ_512);
855 if (!penv->riva_ccu_base) {
856 ret = -ENOMEM;
857 pr_err("%s: ioremap wcnss physical failed\n", __func__);
858 goto fail_ioremap;
859 }
860 } else {
861 penv->pronto_a2xb_base = ioremap(MSM_PRONTO_A2XB_BASE, SZ_512);
862 if (!penv->pronto_a2xb_base) {
863 ret = -ENOMEM;
864 pr_err("%s: ioremap wcnss physical failed\n", __func__);
865 goto fail_ioremap;
866 }
867 }
868
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700869 return 0;
870
Sameer Thalappild3aad702013-01-22 18:52:24 -0800871fail_ioremap:
872 iounmap(penv->msm_wcnss_base);
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700873fail_wake:
874 wake_lock_destroy(&penv->wcnss_wake_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700875fail_res:
876 if (penv->pil)
Stephen Boyd77db8bb2012-06-27 15:15:16 -0700877 subsystem_put(penv->pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700878fail_pil:
879 wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
880 WCNSS_WLAN_SWITCH_OFF);
881fail_power:
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700882 if (has_pronto_hw)
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700883 wcnss_pronto_gpios_config(&pdev->dev, false);
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700884 else
885 wcnss_gpios_config(penv->gpios_5wire, false);
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -0700886fail_gpio_res:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700887 penv = NULL;
888 return ret;
889}
890
Jeff Johnsonb3377e32011-11-18 23:32:27 -0800891#ifndef MODULE
892static int wcnss_node_open(struct inode *inode, struct file *file)
893{
894 struct platform_device *pdev;
895
896 pr_info(DEVICE " triggered by userspace\n");
897
898 pdev = penv->pdev;
899 return wcnss_trigger_config(pdev);
900}
901
902static const struct file_operations wcnss_node_fops = {
903 .owner = THIS_MODULE,
904 .open = wcnss_node_open,
905};
906
907static struct miscdevice wcnss_misc = {
908 .minor = MISC_DYNAMIC_MINOR,
909 .name = DEVICE,
910 .fops = &wcnss_node_fops,
911};
912#endif /* ifndef MODULE */
913
914
915static int __devinit
916wcnss_wlan_probe(struct platform_device *pdev)
917{
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700918 int ret = 0;
919
Jeff Johnsonb3377e32011-11-18 23:32:27 -0800920 /* verify we haven't been called more than once */
921 if (penv) {
922 dev_err(&pdev->dev, "cannot handle multiple devices.\n");
923 return -ENODEV;
924 }
925
926 /* create an environment to track the device */
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -0800927 penv = devm_kzalloc(&pdev->dev, sizeof(*penv), GFP_KERNEL);
Jeff Johnsonb3377e32011-11-18 23:32:27 -0800928 if (!penv) {
929 dev_err(&pdev->dev, "cannot allocate device memory.\n");
930 return -ENOMEM;
931 }
932 penv->pdev = pdev;
933
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700934 /* register sysfs entries */
935 ret = wcnss_create_sysfs(&pdev->dev);
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -0800936 if (ret) {
937 penv = NULL;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700938 return -ENOENT;
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -0800939 }
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700940
941
Jeff Johnsonb3377e32011-11-18 23:32:27 -0800942#ifdef MODULE
943
Sameer Thalappild3aad702013-01-22 18:52:24 -0800944 /* Since we were built as a module, we are running because
Jeff Johnsonb3377e32011-11-18 23:32:27 -0800945 * the module was loaded, therefore we assume userspace
946 * applications are available to service PIL, so we can
947 * trigger the WCNSS configuration now
948 */
949 pr_info(DEVICE " probed in MODULE mode\n");
950 return wcnss_trigger_config(pdev);
951
952#else
953
Sameer Thalappild3aad702013-01-22 18:52:24 -0800954 /* Since we were built into the kernel we'll be called as part
Jeff Johnsonb3377e32011-11-18 23:32:27 -0800955 * of kernel initialization. We don't know if userspace
956 * applications are available to service PIL at this time
957 * (they probably are not), so we simply create a device node
958 * here. When userspace is available it should touch the
959 * device so that we know that WCNSS configuration can take
960 * place
961 */
962 pr_info(DEVICE " probed in built-in mode\n");
963 return misc_register(&wcnss_misc);
964
965#endif
966}
967
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700968static int __devexit
969wcnss_wlan_remove(struct platform_device *pdev)
970{
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800971 wcnss_remove_sysfs(&pdev->dev);
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -0800972 penv = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700973 return 0;
974}
975
976
977static const struct dev_pm_ops wcnss_wlan_pm_ops = {
978 .suspend = wcnss_wlan_suspend,
979 .resume = wcnss_wlan_resume,
980};
981
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700982#ifdef CONFIG_WCNSS_CORE_PRONTO
983static struct of_device_id msm_wcnss_pronto_match[] = {
984 {.compatible = "qcom,wcnss_wlan"},
985 {}
986};
987#endif
988
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700989static struct platform_driver wcnss_wlan_driver = {
990 .driver = {
991 .name = DEVICE,
992 .owner = THIS_MODULE,
993 .pm = &wcnss_wlan_pm_ops,
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700994#ifdef CONFIG_WCNSS_CORE_PRONTO
995 .of_match_table = msm_wcnss_pronto_match,
996#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700997 },
998 .probe = wcnss_wlan_probe,
999 .remove = __devexit_p(wcnss_wlan_remove),
1000};
1001
1002static int __init wcnss_wlan_init(void)
1003{
Sameer Thalappil24db5282012-09-10 11:58:33 -07001004 int ret = 0;
1005
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001006 platform_driver_register(&wcnss_wlan_driver);
1007 platform_driver_register(&wcnss_wlan_ctrl_driver);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001008 platform_driver_register(&wcnss_ctrl_driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001009
Sameer Thalappil24db5282012-09-10 11:58:33 -07001010#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
1011 ret = wcnss_prealloc_init();
1012 if (ret < 0)
1013 pr_err("wcnss: pre-allocation failed\n");
1014#endif
1015
1016 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001017}
1018
1019static void __exit wcnss_wlan_exit(void)
1020{
1021 if (penv) {
1022 if (penv->pil)
Stephen Boyd77db8bb2012-06-27 15:15:16 -07001023 subsystem_put(penv->pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001024
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001025
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001026 penv = NULL;
1027 }
1028
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001029 platform_driver_unregister(&wcnss_ctrl_driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001030 platform_driver_unregister(&wcnss_wlan_ctrl_driver);
1031 platform_driver_unregister(&wcnss_wlan_driver);
Sameer Thalappil24db5282012-09-10 11:58:33 -07001032#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
1033 wcnss_prealloc_deinit();
1034#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001035}
1036
1037module_init(wcnss_wlan_init);
1038module_exit(wcnss_wlan_exit);
1039
1040MODULE_LICENSE("GPL v2");
1041MODULE_VERSION(VERSION);
1042MODULE_DESCRIPTION(DEVICE "Driver");