blob: 8bcf6b2bb0dbfbbc27d36b1f3704f21159cdcbbe [file] [log] [blame]
Sameer Thalappilf106a682013-02-16 20:41:11 -08001/* Copyright (c) 2011-2013, 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>
Sameer Thalappilf106a682013-02-16 20:41:11 -080014#include <linux/firmware.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070015#include <linux/slab.h>
16#include <linux/err.h>
17#include <linux/platform_device.h>
Jeff Johnsonb3377e32011-11-18 23:32:27 -080018#include <linux/miscdevice.h>
19#include <linux/fs.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/wcnss_wlan.h>
Ankur Nandwanib0039b02011-08-09 14:00:45 -070021#include <linux/platform_data/qcom_wcnss_device.h>
David Collinsb727f7d2011-09-12 11:54:49 -070022#include <linux/workqueue.h>
23#include <linux/jiffies.h>
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -070024#include <linux/gpio.h>
Sameer Thalappilf138da52012-07-30 12:56:37 -070025#include <linux/wakelock.h>
Sameer Thalappileeb8c412012-08-10 17:17:09 -070026#include <linux/delay.h>
Sameer Thalappil8d686d42012-08-24 10:07:31 -070027#include <linux/of.h>
28#include <linux/of_gpio.h>
Sameer Thalappiled3f7da2012-11-01 12:54:01 -070029#include <linux/clk.h>
Sameer Thalappil1b3e6112012-12-14 15:16:07 -080030#include <linux/ratelimit.h>
Sameer Thalappil1878d762013-04-24 14:54:39 -070031#include <linux/kthread.h>
32#include <linux/wait.h>
33#include <linux/uaccess.h>
Sameer Thalappil944c72d2013-08-14 17:56:17 -070034#include <linux/suspend.h>
35#include <linux/rwsem.h>
Naresh Jayaram08cee442013-04-26 19:50:00 +053036#include <linux/mfd/pm8xxx/misc.h>
Sameer Thalappilf6fb1892013-09-06 17:33:30 -070037#include <linux/qpnp/qpnp-adc.h>
Stephen Boyd77db8bb2012-06-27 15:15:16 -070038
Yue Ma10d88752014-02-20 17:12:05 -080039#include <mach/board.h>
Sameer Thalappileeb8c412012-08-10 17:17:09 -070040#include <mach/msm_smd.h>
Sameer Thalappil19e02352012-09-24 15:26:12 -070041#include <mach/msm_iomap.h>
Stephen Boyd77db8bb2012-06-27 15:15:16 -070042#include <mach/subsystem_restart.h>
Yue Mafa9638e2013-12-19 16:59:27 -080043#include <mach/subsystem_notif.h>
Stephen Boyd77db8bb2012-06-27 15:15:16 -070044
Sameer Thalappil24db5282012-09-10 11:58:33 -070045#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
46#include "wcnss_prealloc.h"
47#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048
49#define DEVICE "wcnss_wlan"
Sameer Thalappil13780d12013-12-19 17:04:21 -080050#define CTRL_DEVICE "wcnss_ctrl"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051#define VERSION "1.01"
52#define WCNSS_PIL_DEVICE "wcnss"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053
Ankur Nandwanib0039b02011-08-09 14:00:45 -070054/* module params */
55#define WCNSS_CONFIG_UNSPECIFIED (-1)
Arif Hussain677c92d2013-11-01 19:09:11 -070056#define UINT32_MAX (0xFFFFFFFFU)
Sameer Thalappileeb8c412012-08-10 17:17:09 -070057
Ankur Nandwania4510492011-08-29 15:56:23 -070058static int has_48mhz_xo = WCNSS_CONFIG_UNSPECIFIED;
Jeff Johnsonb3377e32011-11-18 23:32:27 -080059module_param(has_48mhz_xo, int, S_IWUSR | S_IRUGO);
Ankur Nandwanib0039b02011-08-09 14:00:45 -070060MODULE_PARM_DESC(has_48mhz_xo, "Is an external 48 MHz XO present");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061
Sameer Thalappil1878d762013-04-24 14:54:39 -070062static int has_calibrated_data = WCNSS_CONFIG_UNSPECIFIED;
63module_param(has_calibrated_data, int, S_IWUSR | S_IRUGO);
64MODULE_PARM_DESC(has_calibrated_data, "whether calibrated data file available");
65
Sameer Thalappil58cec312013-05-13 15:52:08 -070066static int has_autodetect_xo = WCNSS_CONFIG_UNSPECIFIED;
67module_param(has_autodetect_xo, int, S_IWUSR | S_IRUGO);
68MODULE_PARM_DESC(has_autodetect_xo, "Perform auto detect to configure IRIS XO");
69
Sheng Fangbb421672013-03-19 08:27:28 +080070static int do_not_cancel_vote = WCNSS_CONFIG_UNSPECIFIED;
71module_param(do_not_cancel_vote, int, S_IWUSR | S_IRUGO);
72MODULE_PARM_DESC(do_not_cancel_vote, "Do not cancel votes for wcnss");
73
Sameer Thalappiled3f7da2012-11-01 12:54:01 -070074static DEFINE_SPINLOCK(reg_spinlock);
75
76#define MSM_RIVA_PHYS 0x03204000
77#define MSM_PRONTO_PHYS 0xfb21b000
78
79#define RIVA_SPARE_OFFSET 0x0b4
80#define RIVA_SUSPEND_BIT BIT(24)
81
Sameer Thalappil77716e52012-11-08 13:41:04 -080082#define MSM_RIVA_CCU_BASE 0x03200800
83
Sameer Thalappil16cae9b2013-03-04 18:02:50 -080084#define CCU_RIVA_INVALID_ADDR_OFFSET 0x100
85#define CCU_RIVA_LAST_ADDR0_OFFSET 0x104
86#define CCU_RIVA_LAST_ADDR1_OFFSET 0x108
87#define CCU_RIVA_LAST_ADDR2_OFFSET 0x10c
Sameer Thalappil77716e52012-11-08 13:41:04 -080088
Sameer Thalappilbcc06182013-05-30 16:18:57 -070089#define PRONTO_PMU_SPARE_OFFSET 0x1088
90
Sameer Thalappil670197c2013-07-19 16:31:14 -070091#define PRONTO_PMU_COM_GDSCR_OFFSET 0x0024
92#define PRONTO_PMU_COM_GDSCR_SW_COLLAPSE BIT(0)
93#define PRONTO_PMU_COM_GDSCR_HW_CTRL BIT(1)
94
95#define PRONTO_PMU_WLAN_BCR_OFFSET 0x0050
96#define PRONTO_PMU_WLAN_BCR_BLK_ARES BIT(0)
97
98#define PRONTO_PMU_WLAN_GDSCR_OFFSET 0x0054
99#define PRONTO_PMU_WLAN_GDSCR_SW_COLLAPSE BIT(0)
100
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700101
102#define PRONTO_PMU_CBCR_OFFSET 0x0008
103#define PRONTO_PMU_CBCR_CLK_EN BIT(0)
104
Mihir Shete2775e4652013-09-20 16:21:49 -0700105#define PRONTO_PMU_COM_CPU_CBCR_OFFSET 0x0030
106#define PRONTO_PMU_COM_AHB_CBCR_OFFSET 0x0034
Mihir Shete31396f42013-10-18 13:34:03 -0700107
108#define PRONTO_PMU_WLAN_AHB_CBCR_OFFSET 0x0074
109#define PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN BIT(0)
110#define PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF BIT(31)
111
112#define PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET 0x0120
113#define PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN BIT(1)
114
Mihir Shete2775e4652013-09-20 16:21:49 -0700115#define PRONTO_PMU_CFG_OFFSET 0x1004
116#define PRONTO_PMU_COM_CSR_OFFSET 0x1040
117#define PRONTO_PMU_SOFT_RESET_OFFSET 0x104C
118
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800119#define MSM_PRONTO_A2XB_BASE 0xfb100400
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800120#define A2XB_CFG_OFFSET 0x00
121#define A2XB_INT_SRC_OFFSET 0x0c
122#define A2XB_TSTBUS_CTRL_OFFSET 0x14
123#define A2XB_TSTBUS_OFFSET 0x18
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800124#define A2XB_ERR_INFO_OFFSET 0x1c
125
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800126#define WCNSS_TSTBUS_CTRL_EN BIT(0)
127#define WCNSS_TSTBUS_CTRL_AXIM (0x02 << 1)
128#define WCNSS_TSTBUS_CTRL_CMDFIFO (0x03 << 1)
129#define WCNSS_TSTBUS_CTRL_WRFIFO (0x04 << 1)
130#define WCNSS_TSTBUS_CTRL_RDFIFO (0x05 << 1)
131#define WCNSS_TSTBUS_CTRL_CTRL (0x07 << 1)
132#define WCNSS_TSTBUS_CTRL_AXIM_CFG0 (0x00 << 6)
133#define WCNSS_TSTBUS_CTRL_AXIM_CFG1 (0x01 << 6)
134#define WCNSS_TSTBUS_CTRL_CTRL_CFG0 (0x00 << 12)
135#define WCNSS_TSTBUS_CTRL_CTRL_CFG1 (0x01 << 12)
136
137#define MSM_PRONTO_CCPU_BASE 0xfb205050
138#define CCU_PRONTO_INVALID_ADDR_OFFSET 0x08
139#define CCU_PRONTO_LAST_ADDR0_OFFSET 0x0c
140#define CCU_PRONTO_LAST_ADDR1_OFFSET 0x10
141#define CCU_PRONTO_LAST_ADDR2_OFFSET 0x14
142
Sameer Thalappil767ff492013-06-19 15:25:21 -0700143#define MSM_PRONTO_SAW2_BASE 0xfb219000
144#define PRONTO_SAW2_SPM_STS_OFFSET 0x0c
145
Sameer Thalappil66d2b392013-07-02 11:32:55 -0700146#define MSM_PRONTO_PLL_BASE 0xfb21b1c0
147#define PRONTO_PLL_STATUS_OFFSET 0x1c
148
Yue Ma2fd1ffd2014-02-18 16:41:00 -0800149#define MSM_PRONTO_MCU_BASE 0xfb080c00
Mihir Shetee640cf12014-03-13 21:59:22 +0530150#define MCU_APB2PHY_STATUS_OFFSET 0xec
Yue Ma2fd1ffd2014-02-18 16:41:00 -0800151#define MCU_CBR_CCAHB_ERR_OFFSET 0x380
152#define MCU_CBR_CAHB_ERR_OFFSET 0x384
153#define MCU_CBR_CCAHB_TIMEOUT_OFFSET 0x388
154#define MCU_CBR_CAHB_TIMEOUT_OFFSET 0x38c
155#define MCU_DBR_CDAHB_ERR_OFFSET 0x390
156#define MCU_DBR_DAHB_ERR_OFFSET 0x394
157#define MCU_DBR_CDAHB_TIMEOUT_OFFSET 0x398
158#define MCU_DBR_DAHB_TIMEOUT_OFFSET 0x39c
159#define MCU_FDBR_CDAHB_ERR_OFFSET 0x3a0
160#define MCU_FDBR_FDAHB_ERR_OFFSET 0x3a4
161#define MCU_FDBR_CDAHB_TIMEOUT_OFFSET 0x3a8
162#define MCU_FDBR_FDAHB_TIMEOUT_OFFSET 0x3ac
163
Yue Mae490b452013-11-12 00:07:33 -0800164#define MSM_PRONTO_TXP_STATUS 0xfb08040c
Sameer Thalappil670197c2013-07-19 16:31:14 -0700165#define MSM_PRONTO_TXP_PHY_ABORT 0xfb080488
166#define MSM_PRONTO_BRDG_ERR_SRC 0xfb080fb0
167
Sameer Thalappil7f2d82f2013-12-17 11:26:18 -0800168#define MSM_PRONTO_ALARMS_TXCTL 0xfb0120a8
169#define MSM_PRONTO_ALARMS_TACTL 0xfb012448
170
Sameer Thalappild0952f62013-05-03 10:18:29 -0700171#define WCNSS_DEF_WLAN_RX_BUFF_COUNT 1024
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700172#define WCNSS_VBATT_THRESHOLD 3500000
173#define WCNSS_VBATT_GUARD 200
174#define WCNSS_VBATT_HIGH 3700000
175#define WCNSS_VBATT_LOW 3300000
Sameer Thalappild0952f62013-05-03 10:18:29 -0700176
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700177#define WCNSS_CTRL_CHANNEL "WCNSS_CTRL"
Sameer Thalappil1878d762013-04-24 14:54:39 -0700178#define WCNSS_MAX_FRAME_SIZE (4*1024)
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700179#define WCNSS_VERSION_LEN 30
Sameer Thalappild821d632013-11-06 19:24:59 -0800180#define WCNSS_MAX_BUILD_VER_LEN 256
Sameer Thalappil13780d12013-12-19 17:04:21 -0800181#define WCNSS_MAX_CMD_LEN (128)
182#define WCNSS_MIN_CMD_LEN (3)
183#define WCNSS_MIN_SERIAL_LEN (6)
184
185/* control messages from userspace */
186#define WCNSS_USR_CTRL_MSG_START 0x00000000
187#define WCNSS_USR_SERIAL_NUM (WCNSS_USR_CTRL_MSG_START + 1)
188#define WCNSS_USR_HAS_CAL_DATA (WCNSS_USR_CTRL_MSG_START + 2)
Hardik Kantilal Patelab039d82014-03-22 06:30:41 +0530189#define WCNSS_USR_WLAN_MAC_ADDR (WCNSS_USR_CTRL_MSG_START + 3)
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700190
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +0530191#define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x"
192
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700193/* message types */
194#define WCNSS_CTRL_MSG_START 0x01000000
Sameer Thalappil1878d762013-04-24 14:54:39 -0700195#define WCNSS_VERSION_REQ (WCNSS_CTRL_MSG_START + 0)
196#define WCNSS_VERSION_RSP (WCNSS_CTRL_MSG_START + 1)
197#define WCNSS_NVBIN_DNLD_REQ (WCNSS_CTRL_MSG_START + 2)
198#define WCNSS_NVBIN_DNLD_RSP (WCNSS_CTRL_MSG_START + 3)
199#define WCNSS_CALDATA_UPLD_REQ (WCNSS_CTRL_MSG_START + 4)
200#define WCNSS_CALDATA_UPLD_RSP (WCNSS_CTRL_MSG_START + 5)
201#define WCNSS_CALDATA_DNLD_REQ (WCNSS_CTRL_MSG_START + 6)
202#define WCNSS_CALDATA_DNLD_RSP (WCNSS_CTRL_MSG_START + 7)
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700203#define WCNSS_VBATT_LEVEL_IND (WCNSS_CTRL_MSG_START + 8)
Sameer Thalappild821d632013-11-06 19:24:59 -0800204#define WCNSS_BUILD_VER_REQ (WCNSS_CTRL_MSG_START + 9)
205#define WCNSS_BUILD_VER_RSP (WCNSS_CTRL_MSG_START + 10)
Hardik Kantilal Patel880805e2014-05-28 12:50:30 +0530206#define WCNSS_PM_CONFIG_REQ (WCNSS_CTRL_MSG_START + 11)
Sameer Thalappilf106a682013-02-16 20:41:11 -0800207
Leo Changf98087f2013-11-14 15:08:24 -0800208/* max 20mhz channel count */
209#define WCNSS_MAX_CH_NUM 45
Sameer Thalappilfe03e832014-02-19 10:37:16 -0800210#define WCNSS_MAX_PIL_RETRY 3
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700211
212#define VALID_VERSION(version) \
213 ((strncmp(version, "INVALID", WCNSS_VERSION_LEN)) ? 1 : 0)
214
Sameer Thalappil1878d762013-04-24 14:54:39 -0700215#define FW_CALDATA_CAPABLE() \
216 ((penv->fw_major >= 1) && (penv->fw_minor >= 5) ? 1 : 0)
217
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700218struct smd_msg_hdr {
Sameer Thalappilf106a682013-02-16 20:41:11 -0800219 unsigned int msg_type;
220 unsigned int msg_len;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700221};
222
223struct wcnss_version {
224 struct smd_msg_hdr hdr;
225 unsigned char major;
226 unsigned char minor;
227 unsigned char version;
228 unsigned char revision;
229};
230
Naresh Jayaram08cee442013-04-26 19:50:00 +0530231struct wcnss_pmic_dump {
232 char reg_name[10];
233 u16 reg_addr;
234};
235
236static struct wcnss_pmic_dump wcnss_pmic_reg_dump[] = {
237 {"S2", 0x1D8},
238 {"L4", 0xB4},
239 {"L10", 0xC0},
240 {"LVS2", 0x62},
241 {"S4", 0x1E8},
242 {"LVS7", 0x06C},
243 {"LVS1", 0x060},
244};
245
Pradeep Kumar Goudaguntab18e5202014-04-29 11:48:21 +0530246static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
247 void *ss_handle);
248
249static struct notifier_block wnb = {
250 .notifier_call = wcnss_notif_cb,
251};
252
Sameer Thalappilf106a682013-02-16 20:41:11 -0800253#define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
254
255/*
256 * On SMD channel 4K of maximum data can be transferred, including message
257 * header, so NV fragment size as next multiple of 1Kb is 3Kb.
258 */
259#define NV_FRAGMENT_SIZE 3072
Sameer Thalappil1878d762013-04-24 14:54:39 -0700260#define MAX_CALIBRATED_DATA_SIZE (64*1024)
261#define LAST_FRAGMENT (1 << 0)
262#define MESSAGE_TO_FOLLOW (1 << 1)
263#define CAN_RECEIVE_CALDATA (1 << 15)
264#define WCNSS_RESP_SUCCESS 1
265#define WCNSS_RESP_FAIL 0
266
Sameer Thalappilf106a682013-02-16 20:41:11 -0800267
268/* Macro to find the total number fragments of the NV bin Image */
269#define TOTALFRAGMENTS(x) (((x % NV_FRAGMENT_SIZE) == 0) ? \
270 (x / NV_FRAGMENT_SIZE) : ((x / NV_FRAGMENT_SIZE) + 1))
271
272struct nvbin_dnld_req_params {
273 /*
274 * Fragment sequence number of the NV bin Image. NV Bin Image
275 * might not fit into one message due to size limitation of
276 * the SMD channel FIFO so entire NV blob is chopped into
277 * multiple fragments starting with seqeunce number 0. The
278 * last fragment is indicated by marking is_last_fragment field
279 * to 1. At receiving side, NV blobs would be concatenated
280 * together without any padding bytes in between.
281 */
282 unsigned short frag_number;
283
284 /*
Sameer Thalappil1878d762013-04-24 14:54:39 -0700285 * bit 0: When set to 1 it indicates that no more fragments will
286 * be sent.
287 * bit 1: When set, a new message will be followed by this message
288 * bit 2- bit 14: Reserved
289 * bit 15: when set, it indicates that the sender is capable of
290 * receiving Calibrated data.
Sameer Thalappilf106a682013-02-16 20:41:11 -0800291 */
Sameer Thalappil1878d762013-04-24 14:54:39 -0700292 unsigned short msg_flags;
Sameer Thalappilf106a682013-02-16 20:41:11 -0800293
294 /* NV Image size (number of bytes) */
295 unsigned int nvbin_buffer_size;
296
297 /*
298 * Following the 'nvbin_buffer_size', there should be
299 * nvbin_buffer_size bytes of NV bin Image i.e.
300 * uint8[nvbin_buffer_size].
301 */
302};
303
Sameer Thalappil1878d762013-04-24 14:54:39 -0700304
Sameer Thalappilf106a682013-02-16 20:41:11 -0800305struct nvbin_dnld_req_msg {
306 /*
307 * Note: The length specified in nvbin_dnld_req_msg messages
308 * should be hdr.msg_len = sizeof(nvbin_dnld_req_msg) +
309 * nvbin_buffer_size.
310 */
311 struct smd_msg_hdr hdr;
312 struct nvbin_dnld_req_params dnld_req_params;
313};
314
Sameer Thalappil1878d762013-04-24 14:54:39 -0700315struct cal_data_params {
316
317 /* The total size of the calibrated data, including all the
318 * fragments.
319 */
320 unsigned int total_size;
321 unsigned short frag_number;
322 /*
323 * bit 0: When set to 1 it indicates that no more fragments will
324 * be sent.
325 * bit 1: When set, a new message will be followed by this message
326 * bit 2- bit 15: Reserved
327 */
328 unsigned short msg_flags;
329 /*
330 * fragment size
331 */
332 unsigned int frag_size;
333 /*
334 * Following the frag_size, frag_size of fragmented
335 * data will be followed.
336 */
337};
338
339struct cal_data_msg {
340 /*
341 * The length specified in cal_data_msg should be
342 * hdr.msg_len = sizeof(cal_data_msg) + frag_size
343 */
344 struct smd_msg_hdr hdr;
345 struct cal_data_params cal_params;
346};
347
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700348struct vbatt_level {
349 u32 curr_volt;
350 u32 threshold;
351};
352
353struct vbatt_message {
354 struct smd_msg_hdr hdr;
355 struct vbatt_level vbatt;
356};
357
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358static struct {
359 struct platform_device *pdev;
360 void *pil;
361 struct resource *mmio_res;
362 struct resource *tx_irq_res;
363 struct resource *rx_irq_res;
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -0700364 struct resource *gpios_5wire;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365 const struct dev_pm_ops *pm_ops;
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800366 int triggered;
367 int smd_channel_ready;
Sameer Thalappild0952f62013-05-03 10:18:29 -0700368 u32 wlan_rx_buff_count;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700369 smd_channel_t *smd_ch;
370 unsigned char wcnss_version[WCNSS_VERSION_LEN];
Sameer Thalappil1878d762013-04-24 14:54:39 -0700371 unsigned char fw_major;
372 unsigned char fw_minor;
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800373 unsigned int serial_number;
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800374 int thermal_mitigation;
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700375 enum wcnss_hw_type wcnss_hw_type;
Sameer Thalappila112bbc2012-05-23 12:20:32 -0700376 void (*tm_notify)(struct device *, int);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377 struct wcnss_wlan_config wlan_config;
David Collinsb727f7d2011-09-12 11:54:49 -0700378 struct delayed_work wcnss_work;
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700379 struct delayed_work vbatt_work;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700380 struct work_struct wcnssctrl_version_work;
Hardik Kantilal Patel880805e2014-05-28 12:50:30 +0530381 struct work_struct wcnss_pm_config_work;
Sameer Thalappilf106a682013-02-16 20:41:11 -0800382 struct work_struct wcnssctrl_nvbin_dnld_work;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700383 struct work_struct wcnssctrl_rx_work;
Sameer Thalappilf138da52012-07-30 12:56:37 -0700384 struct wake_lock wcnss_wake_lock;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700385 void __iomem *msm_wcnss_base;
Sameer Thalappild3aad702013-01-22 18:52:24 -0800386 void __iomem *riva_ccu_base;
387 void __iomem *pronto_a2xb_base;
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800388 void __iomem *pronto_ccpu_base;
Sameer Thalappil767ff492013-06-19 15:25:21 -0700389 void __iomem *pronto_saw2_base;
Sameer Thalappil66d2b392013-07-02 11:32:55 -0700390 void __iomem *pronto_pll_base;
Yue Ma2fd1ffd2014-02-18 16:41:00 -0800391 void __iomem *pronto_mcu_base;
Yue Mae490b452013-11-12 00:07:33 -0800392 void __iomem *wlan_tx_status;
Sameer Thalappil670197c2013-07-19 16:31:14 -0700393 void __iomem *wlan_tx_phy_aborts;
394 void __iomem *wlan_brdg_err_source;
Sameer Thalappil7f2d82f2013-12-17 11:26:18 -0800395 void __iomem *alarms_txctl;
396 void __iomem *alarms_tactl;
Sameer Thalappil58281ca2013-04-10 18:50:18 -0700397 void __iomem *fiq_reg;
Sameer Thalappil1878d762013-04-24 14:54:39 -0700398 int nv_downloaded;
399 unsigned char *fw_cal_data;
400 unsigned char *user_cal_data;
401 int fw_cal_rcvd;
402 int fw_cal_exp_frag;
403 int fw_cal_available;
404 int user_cal_read;
405 int user_cal_available;
Arif Hussain677c92d2013-11-01 19:09:11 -0700406 u32 user_cal_rcvd;
Sameer Thalappil1878d762013-04-24 14:54:39 -0700407 int user_cal_exp_size;
408 int device_opened;
Sameer Thalappil1d69b8022013-06-10 19:10:07 -0700409 int iris_xo_mode_set;
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700410 int fw_vbatt_state;
Sameer Thalappil13780d12013-12-19 17:04:21 -0800411 int ctrl_device_opened;
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +0530412 char wlan_nv_macAddr[WLAN_MAC_ADDR_SIZE];
Sameer Thalappil1878d762013-04-24 14:54:39 -0700413 struct mutex dev_lock;
Sameer Thalappil13780d12013-12-19 17:04:21 -0800414 struct mutex ctrl_lock;
Sameer Thalappil1878d762013-04-24 14:54:39 -0700415 wait_queue_head_t read_wait;
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700416 struct qpnp_adc_tm_btm_param vbat_monitor_params;
417 struct qpnp_adc_tm_chip *adc_tm_dev;
418 struct mutex vbat_monitor_mutex;
Leo Changf98087f2013-11-14 15:08:24 -0800419 u16 unsafe_ch_count;
420 u16 unsafe_ch_list[WCNSS_MAX_CH_NUM];
Yue Mafa9638e2013-12-19 16:59:27 -0800421 void *wcnss_notif_hdle;
Mihir Shete173dafe2014-03-13 18:50:49 +0530422 u8 is_shutdown;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700423} *penv = NULL;
424
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +0530425static ssize_t wcnss_wlan_macaddr_store(struct device *dev,
426 struct device_attribute *attr, const char *buf, size_t count)
427{
428 char macAddr[WLAN_MAC_ADDR_SIZE];
429
430 if (!penv)
431 return -ENODEV;
432
433 pr_debug("%s: Receive MAC Addr From user space: %s\n", __func__, buf);
434
435 if (WLAN_MAC_ADDR_SIZE != sscanf(buf, MAC_ADDRESS_STR,
436 (int *)&macAddr[0], (int *)&macAddr[1],
437 (int *)&macAddr[2], (int *)&macAddr[3],
438 (int *)&macAddr[4], (int *)&macAddr[5])) {
439
440 pr_err("%s: Failed to Copy MAC\n", __func__);
441 return -EINVAL;
442 }
443
444 memcpy(penv->wlan_nv_macAddr, macAddr, sizeof(penv->wlan_nv_macAddr));
445
446 pr_info("%s: Write MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
447 penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
448 penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
449 penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
450
451 return count;
452}
453
454static ssize_t wcnss_wlan_macaddr_show(struct device *dev,
455 struct device_attribute *attr, char *buf)
456{
457 if (!penv)
458 return -ENODEV;
459
460 return scnprintf(buf, PAGE_SIZE, MAC_ADDRESS_STR,
461 penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
462 penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
463 penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
464}
465
466static DEVICE_ATTR(wcnss_mac_addr, S_IRUSR | S_IWUSR,
467 wcnss_wlan_macaddr_show, wcnss_wlan_macaddr_store);
468
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800469static ssize_t wcnss_serial_number_show(struct device *dev,
470 struct device_attribute *attr, char *buf)
471{
472 if (!penv)
473 return -ENODEV;
474
475 return scnprintf(buf, PAGE_SIZE, "%08X\n", penv->serial_number);
476}
477
478static ssize_t wcnss_serial_number_store(struct device *dev,
Sameer Thalappilf138da52012-07-30 12:56:37 -0700479 struct device_attribute *attr, const char *buf, size_t count)
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800480{
481 unsigned int value;
482
483 if (!penv)
484 return -ENODEV;
485
486 if (sscanf(buf, "%08X", &value) != 1)
487 return -EINVAL;
488
489 penv->serial_number = value;
490 return count;
491}
492
493static DEVICE_ATTR(serial_number, S_IRUSR | S_IWUSR,
494 wcnss_serial_number_show, wcnss_serial_number_store);
495
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800496
497static ssize_t wcnss_thermal_mitigation_show(struct device *dev,
498 struct device_attribute *attr, char *buf)
499{
500 if (!penv)
501 return -ENODEV;
502
503 return scnprintf(buf, PAGE_SIZE, "%u\n", penv->thermal_mitigation);
504}
505
506static ssize_t wcnss_thermal_mitigation_store(struct device *dev,
Sameer Thalappilf138da52012-07-30 12:56:37 -0700507 struct device_attribute *attr, const char *buf, size_t count)
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800508{
509 int value;
510
511 if (!penv)
512 return -ENODEV;
513
514 if (sscanf(buf, "%d", &value) != 1)
515 return -EINVAL;
516 penv->thermal_mitigation = value;
Jeff Johnson61374652012-04-16 20:51:48 -0700517 if (penv->tm_notify)
Sameer Thalappila112bbc2012-05-23 12:20:32 -0700518 (penv->tm_notify)(dev, value);
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800519 return count;
520}
521
522static DEVICE_ATTR(thermal_mitigation, S_IRUSR | S_IWUSR,
523 wcnss_thermal_mitigation_show, wcnss_thermal_mitigation_store);
524
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700525
526static ssize_t wcnss_version_show(struct device *dev,
527 struct device_attribute *attr, char *buf)
528{
529 if (!penv)
530 return -ENODEV;
531
532 return scnprintf(buf, PAGE_SIZE, "%s", penv->wcnss_version);
533}
534
535static DEVICE_ATTR(wcnss_version, S_IRUSR,
536 wcnss_version_show, NULL);
537
Naresh Jayaram08cee442013-04-26 19:50:00 +0530538void wcnss_riva_dump_pmic_regs(void)
539{
540 int i, rc;
541 u8 val;
542
543 for (i = 0; i < ARRAY_SIZE(wcnss_pmic_reg_dump); i++) {
544 val = 0;
545 rc = pm8xxx_read_register(wcnss_pmic_reg_dump[i].reg_addr,
546 &val);
547 if (rc)
548 pr_err("PMIC READ: Failed to read addr = %d\n",
549 wcnss_pmic_reg_dump[i].reg_addr);
550 else
551 pr_info_ratelimited("PMIC READ: %s addr = %x, value = %x\n",
552 wcnss_pmic_reg_dump[i].reg_name,
553 wcnss_pmic_reg_dump[i].reg_addr, val);
554 }
555}
556
Sameer Thalappil77716e52012-11-08 13:41:04 -0800557/* wcnss_reset_intr() is invoked when host drivers fails to
558 * communicate with WCNSS over SMD; so logging these registers
Sameer Thalappild3aad702013-01-22 18:52:24 -0800559 * helps to know WCNSS failure reason
560 */
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800561void wcnss_riva_log_debug_regs(void)
Sameer Thalappil77716e52012-11-08 13:41:04 -0800562{
Sameer Thalappil77716e52012-11-08 13:41:04 -0800563 void __iomem *ccu_reg;
564 u32 reg = 0;
565
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800566 ccu_reg = penv->riva_ccu_base + CCU_RIVA_INVALID_ADDR_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800567 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800568 pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800569
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800570 ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR0_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800571 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800572 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800573
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800574 ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR1_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800575 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800576 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800577
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800578 ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR2_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800579 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800580 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
Naresh Jayaram08cee442013-04-26 19:50:00 +0530581 wcnss_riva_dump_pmic_regs();
Sameer Thalappil77716e52012-11-08 13:41:04 -0800582
Sameer Thalappil77716e52012-11-08 13:41:04 -0800583}
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800584EXPORT_SYMBOL(wcnss_riva_log_debug_regs);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800585
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800586/* Log pronto debug registers before sending reset interrupt */
587void wcnss_pronto_log_debug_regs(void)
588{
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800589 void __iomem *reg_addr, *tst_addr, *tst_ctrl_addr;
Mihir Shete31396f42013-10-18 13:34:03 -0700590 u32 reg = 0, reg2 = 0, reg3 = 0, reg4 = 0;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800591
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700592
593 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SPARE_OFFSET;
594 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800595 pr_err("PRONTO_PMU_SPARE %08x\n", reg);
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700596
Mihir Shete2775e4652013-09-20 16:21:49 -0700597 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CPU_CBCR_OFFSET;
598 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800599 pr_err("PRONTO_PMU_COM_CPU_CBCR %08x\n", reg);
Mihir Shete2775e4652013-09-20 16:21:49 -0700600
601 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_AHB_CBCR_OFFSET;
602 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800603 pr_err("PRONTO_PMU_COM_AHB_CBCR %08x\n", reg);
Mihir Shete2775e4652013-09-20 16:21:49 -0700604
605 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CFG_OFFSET;
606 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800607 pr_err("PRONTO_PMU_CFG %08x\n", reg);
Mihir Shete2775e4652013-09-20 16:21:49 -0700608
609 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CSR_OFFSET;
610 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800611 pr_err("PRONTO_PMU_COM_CSR %08x\n", reg);
Mihir Shete2775e4652013-09-20 16:21:49 -0700612
613 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SOFT_RESET_OFFSET;
614 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800615 pr_err("PRONTO_PMU_SOFT_RESET %08x\n", reg);
Mihir Shete2775e4652013-09-20 16:21:49 -0700616
Yue Mae490b452013-11-12 00:07:33 -0800617 reg_addr = penv->pronto_saw2_base + PRONTO_SAW2_SPM_STS_OFFSET;
618 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800619 pr_err("PRONTO_SAW2_SPM_STS %08x\n", reg);
Yue Mae490b452013-11-12 00:07:33 -0800620
Yue Ma450baae2014-02-19 14:39:09 -0800621 reg_addr = penv->pronto_pll_base + PRONTO_PLL_STATUS_OFFSET;
622 reg = readl_relaxed(reg_addr);
623 pr_err("PRONTO_PLL_STATUS %08x\n", reg);
624
625 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET;
626 reg4 = readl_relaxed(reg_addr);
627 pr_err("PMU_CPU_CMD_RCGR %08x\n", reg4);
628
Sameer Thalappil670197c2013-07-19 16:31:14 -0700629 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_GDSCR_OFFSET;
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700630 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800631 pr_err("PRONTO_PMU_COM_GDSCR %08x\n", reg);
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700632 reg >>= 31;
633
634 if (!reg) {
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800635 pr_err("Cannot log, Pronto common SS is power collapsed\n");
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700636 return;
637 }
Sameer Thalappil670197c2013-07-19 16:31:14 -0700638 reg &= ~(PRONTO_PMU_COM_GDSCR_SW_COLLAPSE
639 | PRONTO_PMU_COM_GDSCR_HW_CTRL);
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700640 writel_relaxed(reg, reg_addr);
641
642 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CBCR_OFFSET;
643 reg = readl_relaxed(reg_addr);
644 reg |= PRONTO_PMU_CBCR_CLK_EN;
645 writel_relaxed(reg, reg_addr);
646
Sameer Thalappild3aad702013-01-22 18:52:24 -0800647 reg_addr = penv->pronto_a2xb_base + A2XB_CFG_OFFSET;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800648 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800649 pr_err("A2XB_CFG_OFFSET %08x\n", reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800650
Sameer Thalappild3aad702013-01-22 18:52:24 -0800651 reg_addr = penv->pronto_a2xb_base + A2XB_INT_SRC_OFFSET;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800652 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800653 pr_err("A2XB_INT_SRC_OFFSET %08x\n", reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800654
Sameer Thalappild3aad702013-01-22 18:52:24 -0800655 reg_addr = penv->pronto_a2xb_base + A2XB_ERR_INFO_OFFSET;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800656 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800657 pr_err("A2XB_ERR_INFO_OFFSET %08x\n", reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800658
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800659 reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_INVALID_ADDR_OFFSET;
660 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800661 pr_err("CCU_CCPU_INVALID_ADDR %08x\n", reg);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800662
663 reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR0_OFFSET;
664 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800665 pr_err("CCU_CCPU_LAST_ADDR0 %08x\n", reg);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800666
667 reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR1_OFFSET;
668 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800669 pr_err("CCU_CCPU_LAST_ADDR1 %08x\n", reg);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800670
671 reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR2_OFFSET;
672 reg = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800673 pr_err("CCU_CCPU_LAST_ADDR2 %08x\n", reg);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800674
675 tst_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_OFFSET;
676 tst_ctrl_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_CTRL_OFFSET;
677
678 /* read data FIFO */
679 reg = 0;
680 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_RDFIFO;
681 writel_relaxed(reg, tst_ctrl_addr);
682 reg = readl_relaxed(tst_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800683 pr_err("Read data FIFO testbus %08x\n", reg);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800684
685 /* command FIFO */
686 reg = 0;
687 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CMDFIFO;
688 writel_relaxed(reg, tst_ctrl_addr);
689 reg = readl_relaxed(tst_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800690 pr_err("Command FIFO testbus %08x\n", reg);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800691
692 /* write data FIFO */
693 reg = 0;
694 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_WRFIFO;
695 writel_relaxed(reg, tst_ctrl_addr);
696 reg = readl_relaxed(tst_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800697 pr_err("Rrite data FIFO testbus %08x\n", reg);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800698
699 /* AXIM SEL CFG0 */
700 reg = 0;
701 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_AXIM |
702 WCNSS_TSTBUS_CTRL_AXIM_CFG0;
703 writel_relaxed(reg, tst_ctrl_addr);
704 reg = readl_relaxed(tst_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800705 pr_err("AXIM SEL CFG0 testbus %08x\n", reg);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800706
707 /* AXIM SEL CFG1 */
708 reg = 0;
709 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_AXIM |
710 WCNSS_TSTBUS_CTRL_AXIM_CFG1;
711 writel_relaxed(reg, tst_ctrl_addr);
712 reg = readl_relaxed(tst_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800713 pr_err("AXIM SEL CFG1 testbus %08x\n", reg);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800714
715 /* CTRL SEL CFG0 */
716 reg = 0;
717 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CTRL |
718 WCNSS_TSTBUS_CTRL_CTRL_CFG0;
719 writel_relaxed(reg, tst_ctrl_addr);
720 reg = readl_relaxed(tst_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800721 pr_err("CTRL SEL CFG0 testbus %08x\n", reg);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800722
723 /* CTRL SEL CFG1 */
724 reg = 0;
725 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CTRL |
726 WCNSS_TSTBUS_CTRL_CTRL_CFG1;
727 writel_relaxed(reg, tst_ctrl_addr);
728 reg = readl_relaxed(tst_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800729 pr_err("CTRL SEL CFG1 testbus %08x\n", reg);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800730
Sameer Thalappil670197c2013-07-19 16:31:14 -0700731
732 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_BCR_OFFSET;
733 reg = readl_relaxed(reg_addr);
734
735 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_GDSCR_OFFSET;
736 reg2 = readl_relaxed(reg_addr);
737
Mihir Shete31396f42013-10-18 13:34:03 -0700738 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_AHB_CBCR_OFFSET;
739 reg3 = readl_relaxed(reg_addr);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800740 pr_err("PMU_WLAN_AHB_CBCR %08x\n", reg3);
Mihir Shete31396f42013-10-18 13:34:03 -0700741
Sameer Thalappil670197c2013-07-19 16:31:14 -0700742 if ((reg & PRONTO_PMU_WLAN_BCR_BLK_ARES) ||
Mihir Shete31396f42013-10-18 13:34:03 -0700743 (reg2 & PRONTO_PMU_WLAN_GDSCR_SW_COLLAPSE) ||
744 (!(reg4 & PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN)) ||
745 (reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF) ||
746 (!(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN))) {
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800747 pr_err("Cannot log, wlan domain is power collapsed\n");
Sameer Thalappil670197c2013-07-19 16:31:14 -0700748 return;
749 }
750
Yue Ma2fd1ffd2014-02-18 16:41:00 -0800751 msleep(50);
752
Sameer Thalappil670197c2013-07-19 16:31:14 -0700753 reg = readl_relaxed(penv->wlan_tx_phy_aborts);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800754 pr_err("WLAN_TX_PHY_ABORTS %08x\n", reg);
Sameer Thalappil670197c2013-07-19 16:31:14 -0700755
Mihir Shetee640cf12014-03-13 21:59:22 +0530756 reg_addr = penv->pronto_mcu_base + MCU_APB2PHY_STATUS_OFFSET;
757 reg = readl_relaxed(reg_addr);
758 pr_err("MCU_APB2PHY_STATUS %08x\n", reg);
759
Yue Ma2fd1ffd2014-02-18 16:41:00 -0800760 reg_addr = penv->pronto_mcu_base + MCU_CBR_CCAHB_ERR_OFFSET;
761 reg = readl_relaxed(reg_addr);
762 pr_err("MCU_CBR_CCAHB_ERR %08x\n", reg);
763
764 reg_addr = penv->pronto_mcu_base + MCU_CBR_CAHB_ERR_OFFSET;
765 reg = readl_relaxed(reg_addr);
766 pr_err("MCU_CBR_CAHB_ERR %08x\n", reg);
767
768 reg_addr = penv->pronto_mcu_base + MCU_CBR_CCAHB_TIMEOUT_OFFSET;
769 reg = readl_relaxed(reg_addr);
770 pr_err("MCU_CBR_CCAHB_TIMEOUT %08x\n", reg);
771
772 reg_addr = penv->pronto_mcu_base + MCU_CBR_CAHB_TIMEOUT_OFFSET;
773 reg = readl_relaxed(reg_addr);
774 pr_err("MCU_CBR_CAHB_TIMEOUT %08x\n", reg);
775
776 reg_addr = penv->pronto_mcu_base + MCU_DBR_CDAHB_ERR_OFFSET;
777 reg = readl_relaxed(reg_addr);
778 pr_err("MCU_DBR_CDAHB_ERR %08x\n", reg);
779
780 reg_addr = penv->pronto_mcu_base + MCU_DBR_DAHB_ERR_OFFSET;
781 reg = readl_relaxed(reg_addr);
782 pr_err("MCU_DBR_DAHB_ERR %08x\n", reg);
783
784 reg_addr = penv->pronto_mcu_base + MCU_DBR_CDAHB_TIMEOUT_OFFSET;
785 reg = readl_relaxed(reg_addr);
786 pr_err("MCU_DBR_CDAHB_TIMEOUT %08x\n", reg);
787
788 reg_addr = penv->pronto_mcu_base + MCU_DBR_DAHB_TIMEOUT_OFFSET;
789 reg = readl_relaxed(reg_addr);
790 pr_err("MCU_DBR_DAHB_TIMEOUT %08x\n", reg);
791
792 reg_addr = penv->pronto_mcu_base + MCU_FDBR_CDAHB_ERR_OFFSET;
793 reg = readl_relaxed(reg_addr);
794 pr_err("MCU_FDBR_CDAHB_ERR %08x\n", reg);
795
796 reg_addr = penv->pronto_mcu_base + MCU_FDBR_FDAHB_ERR_OFFSET;
797 reg = readl_relaxed(reg_addr);
798 pr_err("MCU_FDBR_FDAHB_ERR %08x\n", reg);
799
800 reg_addr = penv->pronto_mcu_base + MCU_FDBR_CDAHB_TIMEOUT_OFFSET;
801 reg = readl_relaxed(reg_addr);
802 pr_err("MCU_FDBR_CDAHB_TIMEOUT %08x\n", reg);
803
804 reg_addr = penv->pronto_mcu_base + MCU_FDBR_FDAHB_TIMEOUT_OFFSET;
805 reg = readl_relaxed(reg_addr);
806 pr_err("MCU_FDBR_FDAHB_TIMEOUT %08x\n", reg);
807
Sameer Thalappil670197c2013-07-19 16:31:14 -0700808 reg = readl_relaxed(penv->wlan_brdg_err_source);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800809 pr_err("WLAN_BRDG_ERR_SOURCE %08x\n", reg);
Sameer Thalappil670197c2013-07-19 16:31:14 -0700810
Yue Mae490b452013-11-12 00:07:33 -0800811 reg = readl_relaxed(penv->wlan_tx_status);
Sameer Thalappild9936ba2013-12-17 14:49:22 -0800812 pr_err("WLAN_TXP_STATUS %08x\n", reg);
Sameer Thalappil7f2d82f2013-12-17 11:26:18 -0800813
814 reg = readl_relaxed(penv->alarms_txctl);
815 pr_err("ALARMS_TXCTL %08x\n", reg);
816
817 reg = readl_relaxed(penv->alarms_tactl);
818 pr_err("ALARMS_TACTL %08x\n", reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800819}
820EXPORT_SYMBOL(wcnss_pronto_log_debug_regs);
821
Sameer Thalappil36f7a1a2013-10-09 18:32:38 -0700822#ifdef CONFIG_WCNSS_REGISTER_DUMP_ON_BITE
Yue Ma10d88752014-02-20 17:12:05 -0800823static void wcnss_log_iris_regs(void)
824{
825 int i;
826 u32 reg_val;
827 u32 regs_array[] = {
828 0x04, 0x05, 0x11, 0x1e, 0x40, 0x48,
829 0x49, 0x4b, 0x00, 0x01, 0x4d};
830
831 pr_info("IRIS Registers [address] : value\n");
832
833 for (i = 0; i < ARRAY_SIZE(regs_array); i++) {
834 reg_val = wcnss_rf_read_reg(regs_array[i]);
835 pr_info("[0x%08x] : 0x%08x\n", regs_array[i], reg_val);
836 }
837}
838
Sameer Thalappil36f7a1a2013-10-09 18:32:38 -0700839void wcnss_log_debug_regs_on_bite(void)
840{
Yue Madd5e3962013-12-20 18:55:01 -0800841 struct platform_device *pdev = wcnss_get_platform_device();
842 struct clk *measure;
843 struct clk *wcnss_debug_mux;
844 unsigned long clk_rate;
845
846 if (wcnss_hardware_type() != WCNSS_PRONTO_HW)
847 return;
848
849 measure = clk_get(&pdev->dev, "measure");
850 wcnss_debug_mux = clk_get(&pdev->dev, "wcnss_debug");
851
852 if (!IS_ERR(measure) && !IS_ERR(wcnss_debug_mux)) {
853 if (clk_set_parent(measure, wcnss_debug_mux))
854 return;
855
856 clk_rate = clk_get_rate(measure);
857 pr_debug("wcnss: clock frequency is: %luHz\n", clk_rate);
858
Yue Ma10d88752014-02-20 17:12:05 -0800859 if (clk_rate) {
Yue Madd5e3962013-12-20 18:55:01 -0800860 wcnss_pronto_log_debug_regs();
Yue Ma10d88752014-02-20 17:12:05 -0800861 } else {
Yue Madd5e3962013-12-20 18:55:01 -0800862 pr_err("clock frequency is zero, cannot access PMU or other registers\n");
Yue Ma10d88752014-02-20 17:12:05 -0800863 wcnss_log_iris_regs();
864 }
Yue Madd5e3962013-12-20 18:55:01 -0800865 }
Sameer Thalappil36f7a1a2013-10-09 18:32:38 -0700866}
867#endif
868
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800869/* interface to reset wcnss by sending the reset interrupt */
Sameer Thalappil19e02352012-09-24 15:26:12 -0700870void wcnss_reset_intr(void)
871{
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800872 if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
873 wcnss_pronto_log_debug_regs();
Sameer Thalappil34920762013-03-21 15:43:28 -0700874 wmb();
Sameer Thalappil58281ca2013-04-10 18:50:18 -0700875 __raw_writel(1 << 16, penv->fiq_reg);
Sameer Thalappil34920762013-03-21 15:43:28 -0700876 } else {
877 wcnss_riva_log_debug_regs();
878 wmb();
879 __raw_writel(1 << 24, MSM_APCS_GCC_BASE + 0x8);
Sameer Thalappil19e02352012-09-24 15:26:12 -0700880 }
881}
882EXPORT_SYMBOL(wcnss_reset_intr);
883
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800884static int wcnss_create_sysfs(struct device *dev)
885{
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800886 int ret;
887
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800888 if (!dev)
889 return -ENODEV;
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800890
891 ret = device_create_file(dev, &dev_attr_serial_number);
892 if (ret)
893 return ret;
894
895 ret = device_create_file(dev, &dev_attr_thermal_mitigation);
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700896 if (ret)
897 goto remove_serial;
898
899 ret = device_create_file(dev, &dev_attr_wcnss_version);
900 if (ret)
901 goto remove_thermal;
902
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +0530903 ret = device_create_file(dev, &dev_attr_wcnss_mac_addr);
904 if (ret)
905 goto remove_version;
906
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800907 return 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700908
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +0530909remove_version:
910 device_remove_file(dev, &dev_attr_wcnss_version);
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700911remove_thermal:
912 device_remove_file(dev, &dev_attr_thermal_mitigation);
913remove_serial:
914 device_remove_file(dev, &dev_attr_serial_number);
915
916 return ret;
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800917}
918
919static void wcnss_remove_sysfs(struct device *dev)
920{
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800921 if (dev) {
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800922 device_remove_file(dev, &dev_attr_serial_number);
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800923 device_remove_file(dev, &dev_attr_thermal_mitigation);
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700924 device_remove_file(dev, &dev_attr_wcnss_version);
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +0530925 device_remove_file(dev, &dev_attr_wcnss_mac_addr);
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700926 }
927}
928static void wcnss_smd_notify_event(void *data, unsigned int event)
929{
930 int len = 0;
931
932 if (penv != data) {
933 pr_err("wcnss: invalid env pointer in smd callback\n");
934 return;
935 }
936 switch (event) {
937 case SMD_EVENT_DATA:
938 len = smd_read_avail(penv->smd_ch);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700939 if (len < 0) {
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700940 pr_err("wcnss: failed to read from smd %d\n", len);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700941 return;
942 }
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700943 schedule_work(&penv->wcnssctrl_rx_work);
944 break;
945
946 case SMD_EVENT_OPEN:
947 pr_debug("wcnss: opening WCNSS SMD channel :%s",
948 WCNSS_CTRL_CHANNEL);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700949 schedule_work(&penv->wcnssctrl_version_work);
Hardik Kantilal Patel880805e2014-05-28 12:50:30 +0530950 schedule_work(&penv->wcnss_pm_config_work);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700951
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700952 break;
953
954 case SMD_EVENT_CLOSE:
955 pr_debug("wcnss: closing WCNSS SMD channel :%s",
956 WCNSS_CTRL_CHANNEL);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700957 penv->nv_downloaded = 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700958 break;
959
960 default:
961 break;
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800962 }
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800963}
964
David Collinsb727f7d2011-09-12 11:54:49 -0700965static void wcnss_post_bootup(struct work_struct *work)
966{
Sheng Fangbb421672013-03-19 08:27:28 +0800967 if (do_not_cancel_vote == 1) {
968 pr_info("%s: Keeping APPS vote for Iris & WCNSS\n", __func__);
969 return;
970 }
971
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700972 pr_info("%s: Cancel APPS vote for Iris & WCNSS\n", __func__);
David Collinsb727f7d2011-09-12 11:54:49 -0700973
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700974 /* Since WCNSS is up, cancel any APPS vote for Iris & WCNSS VREGs */
David Collinsb727f7d2011-09-12 11:54:49 -0700975 wcnss_wlan_power(&penv->pdev->dev, &penv->wlan_config,
Sameer Thalappil1d69b8022013-06-10 19:10:07 -0700976 WCNSS_WLAN_SWITCH_OFF, NULL);
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700977
978}
979
980static int
981wcnss_pronto_gpios_config(struct device *dev, bool enable)
982{
983 int rc = 0;
984 int i, j;
985 int WCNSS_WLAN_NUM_GPIOS = 5;
986
987 for (i = 0; i < WCNSS_WLAN_NUM_GPIOS; i++) {
988 int gpio = of_get_gpio(dev->of_node, i);
989 if (enable) {
990 rc = gpio_request(gpio, "wcnss_wlan");
991 if (rc) {
992 pr_err("WCNSS gpio_request %d err %d\n",
993 gpio, rc);
994 goto fail;
995 }
996 } else
997 gpio_free(gpio);
998 }
999
1000 return rc;
1001
1002fail:
1003 for (j = WCNSS_WLAN_NUM_GPIOS-1; j >= 0; j--) {
1004 int gpio = of_get_gpio(dev->of_node, i);
1005 gpio_free(gpio);
1006 }
1007 return rc;
David Collinsb727f7d2011-09-12 11:54:49 -07001008}
1009
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -07001010static int
1011wcnss_gpios_config(struct resource *gpios_5wire, bool enable)
1012{
1013 int i, j;
1014 int rc = 0;
1015
1016 for (i = gpios_5wire->start; i <= gpios_5wire->end; i++) {
1017 if (enable) {
1018 rc = gpio_request(i, gpios_5wire->name);
1019 if (rc) {
1020 pr_err("WCNSS gpio_request %d err %d\n", i, rc);
1021 goto fail;
1022 }
1023 } else
1024 gpio_free(i);
1025 }
1026
1027 return rc;
1028
1029fail:
1030 for (j = i-1; j >= gpios_5wire->start; j--)
1031 gpio_free(j);
1032 return rc;
1033}
1034
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001035static int __devinit
1036wcnss_wlan_ctrl_probe(struct platform_device *pdev)
1037{
Sameer Thalappil0ab82132013-06-10 10:10:05 -07001038 if (!penv || !penv->triggered)
Sameer Thalappil667f43f2011-10-10 11:12:54 -07001039 return -ENODEV;
1040
1041 penv->smd_channel_ready = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001042
1043 pr_info("%s: SMD ctrl channel up\n", __func__);
1044
David Collinsb727f7d2011-09-12 11:54:49 -07001045 /* Schedule a work to do any post boot up activity */
1046 INIT_DELAYED_WORK(&penv->wcnss_work, wcnss_post_bootup);
1047 schedule_delayed_work(&penv->wcnss_work, msecs_to_jiffies(10000));
1048
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001049 return 0;
1050}
1051
Sameer Thalappil13f45182012-07-23 18:02:45 -07001052void wcnss_flush_delayed_boot_votes()
1053{
Sameer Thalappilf106a682013-02-16 20:41:11 -08001054 flush_delayed_work(&penv->wcnss_work);
Sameer Thalappil13f45182012-07-23 18:02:45 -07001055}
1056EXPORT_SYMBOL(wcnss_flush_delayed_boot_votes);
1057
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001058static int __devexit
1059wcnss_wlan_ctrl_remove(struct platform_device *pdev)
1060{
1061 if (penv)
1062 penv->smd_channel_ready = 0;
1063
1064 pr_info("%s: SMD ctrl channel down\n", __func__);
1065
1066 return 0;
1067}
1068
1069
1070static struct platform_driver wcnss_wlan_ctrl_driver = {
1071 .driver = {
1072 .name = "WLAN_CTRL",
1073 .owner = THIS_MODULE,
1074 },
1075 .probe = wcnss_wlan_ctrl_probe,
1076 .remove = __devexit_p(wcnss_wlan_ctrl_remove),
1077};
1078
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001079static int __devexit
1080wcnss_ctrl_remove(struct platform_device *pdev)
1081{
1082 if (penv && penv->smd_ch)
1083 smd_close(penv->smd_ch);
1084
1085 return 0;
1086}
1087
1088static int __devinit
1089wcnss_ctrl_probe(struct platform_device *pdev)
1090{
1091 int ret = 0;
1092
Sameer Thalappil0ab82132013-06-10 10:10:05 -07001093 if (!penv || !penv->triggered)
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001094 return -ENODEV;
1095
1096 ret = smd_named_open_on_edge(WCNSS_CTRL_CHANNEL, SMD_APPS_WCNSS,
1097 &penv->smd_ch, penv, wcnss_smd_notify_event);
1098 if (ret < 0) {
1099 pr_err("wcnss: cannot open the smd command channel %s: %d\n",
1100 WCNSS_CTRL_CHANNEL, ret);
1101 return -ENODEV;
1102 }
1103 smd_disable_read_intr(penv->smd_ch);
1104
1105 return 0;
1106}
1107
1108/* platform device for WCNSS_CTRL SMD channel */
1109static struct platform_driver wcnss_ctrl_driver = {
1110 .driver = {
1111 .name = "WCNSS_CTRL",
1112 .owner = THIS_MODULE,
1113 },
1114 .probe = wcnss_ctrl_probe,
1115 .remove = __devexit_p(wcnss_ctrl_remove),
1116};
1117
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001118struct device *wcnss_wlan_get_device(void)
1119{
1120 if (penv && penv->pdev && penv->smd_channel_ready)
1121 return &penv->pdev->dev;
1122 return NULL;
1123}
1124EXPORT_SYMBOL(wcnss_wlan_get_device);
1125
Sameer Thalappil409ed352011-12-07 10:53:31 -08001126struct platform_device *wcnss_get_platform_device(void)
1127{
1128 if (penv && penv->pdev)
1129 return penv->pdev;
1130 return NULL;
1131}
1132EXPORT_SYMBOL(wcnss_get_platform_device);
1133
1134struct wcnss_wlan_config *wcnss_get_wlan_config(void)
1135{
1136 if (penv && penv->pdev)
1137 return &penv->wlan_config;
1138 return NULL;
1139}
1140EXPORT_SYMBOL(wcnss_get_wlan_config);
1141
Sameer Thalappil1878d762013-04-24 14:54:39 -07001142int wcnss_device_ready(void)
1143{
Mihir Shete173dafe2014-03-13 18:50:49 +05301144 if (penv && penv->pdev && penv->nv_downloaded &&
1145 !wcnss_device_is_shutdown())
Sameer Thalappil1878d762013-04-24 14:54:39 -07001146 return 1;
1147 return 0;
1148}
1149EXPORT_SYMBOL(wcnss_device_ready);
1150
Mihir Shete173dafe2014-03-13 18:50:49 +05301151int wcnss_device_is_shutdown(void)
1152{
1153 if (penv && penv->is_shutdown)
1154 return 1;
1155 return 0;
1156}
1157EXPORT_SYMBOL(wcnss_device_is_shutdown);
Sameer Thalappil1878d762013-04-24 14:54:39 -07001158
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001159struct resource *wcnss_wlan_get_memory_map(struct device *dev)
1160{
1161 if (penv && dev && (dev == &penv->pdev->dev) && penv->smd_channel_ready)
1162 return penv->mmio_res;
1163 return NULL;
1164}
1165EXPORT_SYMBOL(wcnss_wlan_get_memory_map);
1166
1167int wcnss_wlan_get_dxe_tx_irq(struct device *dev)
1168{
1169 if (penv && dev && (dev == &penv->pdev->dev) &&
1170 penv->tx_irq_res && penv->smd_channel_ready)
1171 return penv->tx_irq_res->start;
1172 return WCNSS_WLAN_IRQ_INVALID;
1173}
1174EXPORT_SYMBOL(wcnss_wlan_get_dxe_tx_irq);
1175
1176int wcnss_wlan_get_dxe_rx_irq(struct device *dev)
1177{
1178 if (penv && dev && (dev == &penv->pdev->dev) &&
1179 penv->rx_irq_res && penv->smd_channel_ready)
1180 return penv->rx_irq_res->start;
1181 return WCNSS_WLAN_IRQ_INVALID;
1182}
1183EXPORT_SYMBOL(wcnss_wlan_get_dxe_rx_irq);
1184
1185void wcnss_wlan_register_pm_ops(struct device *dev,
1186 const struct dev_pm_ops *pm_ops)
1187{
1188 if (penv && dev && (dev == &penv->pdev->dev) && pm_ops)
1189 penv->pm_ops = pm_ops;
1190}
1191EXPORT_SYMBOL(wcnss_wlan_register_pm_ops);
1192
Sameer Thalappilecdd1002011-09-09 10:52:27 -07001193void wcnss_wlan_unregister_pm_ops(struct device *dev,
1194 const struct dev_pm_ops *pm_ops)
1195{
1196 if (penv && dev && (dev == &penv->pdev->dev) && pm_ops) {
1197 if (pm_ops->suspend != penv->pm_ops->suspend ||
1198 pm_ops->resume != penv->pm_ops->resume)
1199 pr_err("PM APIs dont match with registered APIs\n");
1200 penv->pm_ops = NULL;
1201 }
1202}
1203EXPORT_SYMBOL(wcnss_wlan_unregister_pm_ops);
1204
Sameer Thalappila112bbc2012-05-23 12:20:32 -07001205void wcnss_register_thermal_mitigation(struct device *dev,
1206 void (*tm_notify)(struct device *, int))
Jeff Johnson61374652012-04-16 20:51:48 -07001207{
Sameer Thalappila112bbc2012-05-23 12:20:32 -07001208 if (penv && dev && tm_notify)
Jeff Johnson61374652012-04-16 20:51:48 -07001209 penv->tm_notify = tm_notify;
1210}
1211EXPORT_SYMBOL(wcnss_register_thermal_mitigation);
1212
Sameer Thalappila112bbc2012-05-23 12:20:32 -07001213void wcnss_unregister_thermal_mitigation(
1214 void (*tm_notify)(struct device *, int))
Jeff Johnson61374652012-04-16 20:51:48 -07001215{
1216 if (penv && tm_notify) {
1217 if (tm_notify != penv->tm_notify)
1218 pr_err("tm_notify doesn't match registered\n");
1219 penv->tm_notify = NULL;
1220 }
1221}
1222EXPORT_SYMBOL(wcnss_unregister_thermal_mitigation);
1223
Jeff Johnson8b1f6d02012-02-03 20:43:26 -08001224unsigned int wcnss_get_serial_number(void)
1225{
1226 if (penv)
1227 return penv->serial_number;
1228 return 0;
1229}
1230EXPORT_SYMBOL(wcnss_get_serial_number);
1231
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +05301232int wcnss_get_wlan_mac_address(char mac_addr[WLAN_MAC_ADDR_SIZE])
1233{
1234 if (!penv)
1235 return -ENODEV;
1236
1237 memcpy(mac_addr, penv->wlan_nv_macAddr, WLAN_MAC_ADDR_SIZE);
1238 pr_debug("%s: Get MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
1239 penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
1240 penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
1241 penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
1242 return 0;
1243}
1244EXPORT_SYMBOL(wcnss_get_wlan_mac_address);
1245
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001246static int enable_wcnss_suspend_notify;
1247
1248static int enable_wcnss_suspend_notify_set(const char *val,
1249 struct kernel_param *kp)
1250{
1251 int ret;
1252
1253 ret = param_set_int(val, kp);
1254 if (ret)
1255 return ret;
1256
1257 if (enable_wcnss_suspend_notify)
1258 pr_debug("Suspend notification activated for wcnss\n");
1259
1260 return 0;
1261}
1262module_param_call(enable_wcnss_suspend_notify, enable_wcnss_suspend_notify_set,
1263 param_get_int, &enable_wcnss_suspend_notify, S_IRUGO | S_IWUSR);
1264
Sameer Thalappil58cec312013-05-13 15:52:08 -07001265int wcnss_xo_auto_detect_enabled(void)
1266{
1267 return (has_autodetect_xo == 1 ? 1 : 0);
1268}
1269
Sameer Thalappil1d69b8022013-06-10 19:10:07 -07001270int wcnss_wlan_iris_xo_mode(void)
1271{
1272 if (penv && penv->pdev && penv->smd_channel_ready)
1273 return penv->iris_xo_mode_set;
1274 return -ENODEV;
1275}
1276EXPORT_SYMBOL(wcnss_wlan_iris_xo_mode);
1277
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001278
1279void wcnss_suspend_notify(void)
1280{
1281 void __iomem *pmu_spare_reg;
1282 u32 reg = 0;
1283 unsigned long flags;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001284
1285 if (!enable_wcnss_suspend_notify)
1286 return;
1287
1288 if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
1289 return;
1290
1291 /* For Riva */
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001292 pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
1293 spin_lock_irqsave(&reg_spinlock, flags);
1294 reg = readl_relaxed(pmu_spare_reg);
1295 reg |= RIVA_SUSPEND_BIT;
1296 writel_relaxed(reg, pmu_spare_reg);
1297 spin_unlock_irqrestore(&reg_spinlock, flags);
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001298}
1299EXPORT_SYMBOL(wcnss_suspend_notify);
1300
1301void wcnss_resume_notify(void)
1302{
1303 void __iomem *pmu_spare_reg;
1304 u32 reg = 0;
1305 unsigned long flags;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001306
1307 if (!enable_wcnss_suspend_notify)
1308 return;
1309
1310 if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
1311 return;
1312
1313 /* For Riva */
1314 pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
1315
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001316 spin_lock_irqsave(&reg_spinlock, flags);
1317 reg = readl_relaxed(pmu_spare_reg);
1318 reg &= ~RIVA_SUSPEND_BIT;
1319 writel_relaxed(reg, pmu_spare_reg);
1320 spin_unlock_irqrestore(&reg_spinlock, flags);
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001321}
1322EXPORT_SYMBOL(wcnss_resume_notify);
1323
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001324static int wcnss_wlan_suspend(struct device *dev)
1325{
1326 if (penv && dev && (dev == &penv->pdev->dev) &&
1327 penv->smd_channel_ready &&
1328 penv->pm_ops && penv->pm_ops->suspend)
1329 return penv->pm_ops->suspend(dev);
1330 return 0;
1331}
1332
1333static int wcnss_wlan_resume(struct device *dev)
1334{
1335 if (penv && dev && (dev == &penv->pdev->dev) &&
1336 penv->smd_channel_ready &&
1337 penv->pm_ops && penv->pm_ops->resume)
1338 return penv->pm_ops->resume(dev);
1339 return 0;
1340}
1341
Sameer Thalappilf138da52012-07-30 12:56:37 -07001342void wcnss_prevent_suspend()
1343{
1344 if (penv)
1345 wake_lock(&penv->wcnss_wake_lock);
1346}
1347EXPORT_SYMBOL(wcnss_prevent_suspend);
1348
1349void wcnss_allow_suspend()
1350{
1351 if (penv)
1352 wake_unlock(&penv->wcnss_wake_lock);
1353}
1354EXPORT_SYMBOL(wcnss_allow_suspend);
1355
Sameer Thalappil8d686d42012-08-24 10:07:31 -07001356int wcnss_hardware_type(void)
1357{
1358 if (penv)
1359 return penv->wcnss_hw_type;
1360 else
1361 return -ENODEV;
1362}
1363EXPORT_SYMBOL(wcnss_hardware_type);
1364
Sameer Thalappil1878d762013-04-24 14:54:39 -07001365int fw_cal_data_available(void)
Sameer Thalappilcf566bb2013-02-26 17:10:25 -08001366{
1367 if (penv)
Sameer Thalappil1878d762013-04-24 14:54:39 -07001368 return penv->fw_cal_available;
Sameer Thalappilcf566bb2013-02-26 17:10:25 -08001369 else
1370 return -ENODEV;
1371}
Sameer Thalappilcf566bb2013-02-26 17:10:25 -08001372
Sameer Thalappild0952f62013-05-03 10:18:29 -07001373u32 wcnss_get_wlan_rx_buff_count(void)
1374{
1375 if (penv)
1376 return penv->wlan_rx_buff_count;
1377 else
1378 return WCNSS_DEF_WLAN_RX_BUFF_COUNT;
1379
1380}
1381EXPORT_SYMBOL(wcnss_get_wlan_rx_buff_count);
1382
Leo Changf98087f2013-11-14 15:08:24 -08001383int wcnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count)
1384{
1385 if (penv && unsafe_ch_list &&
1386 (ch_count <= WCNSS_MAX_CH_NUM)) {
1387 memcpy((char *)penv->unsafe_ch_list,
1388 (char *)unsafe_ch_list, ch_count * sizeof(u16));
1389 penv->unsafe_ch_count = ch_count;
1390 return 0;
1391 } else
1392 return -ENODEV;
1393}
1394EXPORT_SYMBOL(wcnss_set_wlan_unsafe_channel);
1395
1396int wcnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 buffer_size,
1397 u16 *ch_count)
1398{
1399 if (penv) {
1400 if (buffer_size < penv->unsafe_ch_count * sizeof(u16))
1401 return -ENODEV;
1402 memcpy((char *)unsafe_ch_list,
1403 (char *)penv->unsafe_ch_list,
1404 penv->unsafe_ch_count * sizeof(u16));
1405 *ch_count = penv->unsafe_ch_count;
1406 return 0;
1407 } else
1408 return -ENODEV;
1409}
1410EXPORT_SYMBOL(wcnss_get_wlan_unsafe_channel);
1411
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001412static int wcnss_smd_tx(void *data, int len)
1413{
1414 int ret = 0;
1415
1416 ret = smd_write_avail(penv->smd_ch);
1417 if (ret < len) {
1418 pr_err("wcnss: no space available for smd frame\n");
Sameer Thalappilf106a682013-02-16 20:41:11 -08001419 return -ENOSPC;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001420 }
1421 ret = smd_write(penv->smd_ch, data, len);
1422 if (ret < len) {
1423 pr_err("wcnss: failed to write Command %d", len);
1424 ret = -ENODEV;
1425 }
1426 return ret;
1427}
1428
Sameer Thalappilf6fb1892013-09-06 17:33:30 -07001429static void wcnss_notify_vbat(enum qpnp_tm_state state, void *ctx)
1430{
1431 mutex_lock(&penv->vbat_monitor_mutex);
1432 cancel_delayed_work_sync(&penv->vbatt_work);
1433
1434 if (state == ADC_TM_LOW_STATE) {
1435 pr_debug("wcnss: low voltage notification triggered\n");
1436 penv->vbat_monitor_params.state_request =
1437 ADC_TM_HIGH_THR_ENABLE;
1438 penv->vbat_monitor_params.high_thr = WCNSS_VBATT_THRESHOLD +
1439 WCNSS_VBATT_GUARD;
1440 penv->vbat_monitor_params.low_thr = 0;
1441 } else if (state == ADC_TM_HIGH_STATE) {
1442 penv->vbat_monitor_params.state_request =
1443 ADC_TM_LOW_THR_ENABLE;
1444 penv->vbat_monitor_params.low_thr = WCNSS_VBATT_THRESHOLD -
1445 WCNSS_VBATT_GUARD;
1446 penv->vbat_monitor_params.high_thr = 0;
1447 pr_debug("wcnss: high voltage notification triggered\n");
1448 } else {
1449 pr_debug("wcnss: unknown voltage notification state: %d\n",
1450 state);
1451 mutex_unlock(&penv->vbat_monitor_mutex);
1452 return;
1453 }
1454 pr_debug("wcnss: set low thr to %d and high to %d\n",
1455 penv->vbat_monitor_params.low_thr,
1456 penv->vbat_monitor_params.high_thr);
1457
1458 qpnp_adc_tm_channel_measure(penv->adc_tm_dev,
1459 &penv->vbat_monitor_params);
1460 schedule_delayed_work(&penv->vbatt_work, msecs_to_jiffies(2000));
1461 mutex_unlock(&penv->vbat_monitor_mutex);
1462}
1463
1464static int wcnss_setup_vbat_monitoring(void)
1465{
1466 int rc = -1;
1467
1468 if (!penv->adc_tm_dev) {
1469 pr_err("wcnss: not setting up vbatt\n");
1470 return rc;
1471 }
1472 penv->vbat_monitor_params.low_thr = WCNSS_VBATT_THRESHOLD;
1473 penv->vbat_monitor_params.high_thr = WCNSS_VBATT_THRESHOLD;
1474 penv->vbat_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
1475 penv->vbat_monitor_params.channel = VBAT_SNS;
1476 penv->vbat_monitor_params.btm_ctx = (void *)penv;
1477 penv->vbat_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
1478 penv->vbat_monitor_params.threshold_notification = &wcnss_notify_vbat;
1479 pr_debug("wcnss: set low thr to %d and high to %d\n",
1480 penv->vbat_monitor_params.low_thr,
1481 penv->vbat_monitor_params.high_thr);
1482
1483 rc = qpnp_adc_tm_channel_measure(penv->adc_tm_dev,
1484 &penv->vbat_monitor_params);
1485 if (rc)
1486 pr_err("wcnss: tm setup failed: %d\n", rc);
1487
1488 return rc;
1489}
1490
1491static void wcnss_update_vbatt(struct work_struct *work)
1492{
1493 struct vbatt_message vbatt_msg;
1494 int ret = 0;
1495
1496 vbatt_msg.hdr.msg_type = WCNSS_VBATT_LEVEL_IND;
1497 vbatt_msg.hdr.msg_len = sizeof(struct vbatt_message);
1498 vbatt_msg.vbatt.threshold = WCNSS_VBATT_THRESHOLD;
1499
1500 mutex_lock(&penv->vbat_monitor_mutex);
1501 if (penv->vbat_monitor_params.low_thr &&
1502 (penv->fw_vbatt_state == WCNSS_VBATT_LOW ||
1503 penv->fw_vbatt_state == WCNSS_CONFIG_UNSPECIFIED)) {
1504 vbatt_msg.vbatt.curr_volt = WCNSS_VBATT_HIGH;
1505 penv->fw_vbatt_state = WCNSS_VBATT_HIGH;
1506 pr_debug("wcnss: send HIGH BATT to FW\n");
1507 } else if (!penv->vbat_monitor_params.low_thr &&
1508 (penv->fw_vbatt_state == WCNSS_VBATT_HIGH ||
1509 penv->fw_vbatt_state == WCNSS_CONFIG_UNSPECIFIED)){
1510 vbatt_msg.vbatt.curr_volt = WCNSS_VBATT_LOW;
1511 penv->fw_vbatt_state = WCNSS_VBATT_LOW;
1512 pr_debug("wcnss: send LOW BATT to FW\n");
1513 } else {
1514 mutex_unlock(&penv->vbat_monitor_mutex);
1515 return;
1516 }
1517 mutex_unlock(&penv->vbat_monitor_mutex);
1518 ret = wcnss_smd_tx(&vbatt_msg, vbatt_msg.hdr.msg_len);
1519 if (ret < 0)
1520 pr_err("wcnss: smd tx failed\n");
1521 return;
1522}
1523
1524
Sameer Thalappil1878d762013-04-24 14:54:39 -07001525static unsigned char wcnss_fw_status(void)
1526{
1527 int len = 0;
1528 int rc = 0;
1529
1530 unsigned char fw_status = 0xFF;
1531
1532 len = smd_read_avail(penv->smd_ch);
1533 if (len < 1) {
1534 pr_err("%s: invalid firmware status", __func__);
1535 return fw_status;
1536 }
1537
1538 rc = smd_read(penv->smd_ch, &fw_status, 1);
1539 if (rc < 0) {
1540 pr_err("%s: incomplete data read from smd\n", __func__);
1541 return fw_status;
1542 }
1543 return fw_status;
1544}
1545
1546static void wcnss_send_cal_rsp(unsigned char fw_status)
1547{
1548 struct smd_msg_hdr *rsphdr;
1549 unsigned char *msg = NULL;
1550 int rc;
1551
1552 msg = kmalloc((sizeof(struct smd_msg_hdr) + 1), GFP_KERNEL);
1553 if (NULL == msg) {
1554 pr_err("wcnss: %s: failed to get memory\n", __func__);
1555 return;
1556 }
1557
1558 rsphdr = (struct smd_msg_hdr *)msg;
1559 rsphdr->msg_type = WCNSS_CALDATA_UPLD_RSP;
1560 rsphdr->msg_len = sizeof(struct smd_msg_hdr) + 1;
1561 memcpy(msg+sizeof(struct smd_msg_hdr), &fw_status, 1);
1562
1563 rc = wcnss_smd_tx(msg, rsphdr->msg_len);
1564 if (rc < 0)
1565 pr_err("wcnss: smd tx failed\n");
Sameer Thalappil844d3602013-05-24 13:54:24 -07001566
1567 kfree(msg);
Sameer Thalappil1878d762013-04-24 14:54:39 -07001568}
1569
1570/* Collect calibrated data from WCNSS */
1571void extract_cal_data(int len)
1572{
1573 int rc;
1574 struct cal_data_params calhdr;
1575 unsigned char fw_status = WCNSS_RESP_FAIL;
1576
1577 if (len < sizeof(struct cal_data_params)) {
1578 pr_err("wcnss: incomplete cal header length\n");
1579 return;
1580 }
1581
1582 rc = smd_read(penv->smd_ch, (unsigned char *)&calhdr,
1583 sizeof(struct cal_data_params));
1584 if (rc < sizeof(struct cal_data_params)) {
1585 pr_err("wcnss: incomplete cal header read from smd\n");
1586 return;
1587 }
1588
1589 if (penv->fw_cal_exp_frag != calhdr.frag_number) {
1590 pr_err("wcnss: Invalid frgament");
1591 goto exit;
1592 }
1593
1594 if (calhdr.frag_size > WCNSS_MAX_FRAME_SIZE) {
1595 pr_err("wcnss: Invalid fragment size");
1596 goto exit;
1597 }
1598
Sameer Thalappila5e70ae2013-10-22 13:18:20 -07001599 if (penv->fw_cal_available) {
1600 /* ignore cal upload from SSR */
1601 smd_read(penv->smd_ch, NULL, calhdr.frag_size);
1602 penv->fw_cal_exp_frag++;
1603 if (calhdr.msg_flags & LAST_FRAGMENT) {
1604 penv->fw_cal_exp_frag = 0;
1605 goto exit;
1606 }
1607 return;
1608 }
1609
Sameer Thalappil1878d762013-04-24 14:54:39 -07001610 if (0 == calhdr.frag_number) {
1611 if (calhdr.total_size > MAX_CALIBRATED_DATA_SIZE) {
1612 pr_err("wcnss: Invalid cal data size %d",
1613 calhdr.total_size);
1614 goto exit;
1615 }
1616 kfree(penv->fw_cal_data);
1617 penv->fw_cal_rcvd = 0;
1618 penv->fw_cal_data = kmalloc(calhdr.total_size,
1619 GFP_KERNEL);
1620 if (penv->fw_cal_data == NULL) {
1621 smd_read(penv->smd_ch, NULL, calhdr.frag_size);
1622 goto exit;
1623 }
1624 }
1625
1626 mutex_lock(&penv->dev_lock);
1627 if (penv->fw_cal_rcvd + calhdr.frag_size >
1628 MAX_CALIBRATED_DATA_SIZE) {
1629 pr_err("calibrated data size is more than expected %d",
1630 penv->fw_cal_rcvd + calhdr.frag_size);
1631 penv->fw_cal_exp_frag = 0;
1632 penv->fw_cal_rcvd = 0;
1633 smd_read(penv->smd_ch, NULL, calhdr.frag_size);
1634 goto unlock_exit;
1635 }
1636
1637 rc = smd_read(penv->smd_ch, penv->fw_cal_data + penv->fw_cal_rcvd,
1638 calhdr.frag_size);
1639 if (rc < calhdr.frag_size)
1640 goto unlock_exit;
1641
1642 penv->fw_cal_exp_frag++;
1643 penv->fw_cal_rcvd += calhdr.frag_size;
1644
1645 if (calhdr.msg_flags & LAST_FRAGMENT) {
1646 penv->fw_cal_exp_frag = 0;
1647 penv->fw_cal_available = true;
1648 pr_info("wcnss: cal data collection completed\n");
1649 }
1650 mutex_unlock(&penv->dev_lock);
1651 wake_up(&penv->read_wait);
1652
1653 if (penv->fw_cal_available) {
1654 fw_status = WCNSS_RESP_SUCCESS;
1655 wcnss_send_cal_rsp(fw_status);
1656 }
1657 return;
1658
1659unlock_exit:
1660 mutex_unlock(&penv->dev_lock);
1661
1662exit:
1663 wcnss_send_cal_rsp(fw_status);
1664 return;
1665}
1666
1667
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001668static void wcnssctrl_rx_handler(struct work_struct *worker)
1669{
1670 int len = 0;
1671 int rc = 0;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001672 unsigned char buf[sizeof(struct wcnss_version)];
Sameer Thalappild821d632013-11-06 19:24:59 -08001673 unsigned char build[WCNSS_MAX_BUILD_VER_LEN+1];
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001674 struct smd_msg_hdr *phdr;
Sameer Thalappild821d632013-11-06 19:24:59 -08001675 struct smd_msg_hdr smd_msg;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001676 struct wcnss_version *pversion;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001677 int hw_type;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001678 unsigned char fw_status = 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001679
1680 len = smd_read_avail(penv->smd_ch);
1681 if (len > WCNSS_MAX_FRAME_SIZE) {
1682 pr_err("wcnss: frame larger than the allowed size\n");
1683 smd_read(penv->smd_ch, NULL, len);
1684 return;
1685 }
1686 if (len <= 0)
1687 return;
1688
Sameer Thalappil1878d762013-04-24 14:54:39 -07001689 rc = smd_read(penv->smd_ch, buf, sizeof(struct smd_msg_hdr));
1690 if (rc < sizeof(struct smd_msg_hdr)) {
1691 pr_err("wcnss: incomplete header read from smd\n");
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001692 return;
1693 }
Sameer Thalappil1878d762013-04-24 14:54:39 -07001694 len -= sizeof(struct smd_msg_hdr);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001695
1696 phdr = (struct smd_msg_hdr *)buf;
1697
Sameer Thalappilf106a682013-02-16 20:41:11 -08001698 switch (phdr->msg_type) {
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001699
1700 case WCNSS_VERSION_RSP:
Sameer Thalappil1878d762013-04-24 14:54:39 -07001701 if (len != sizeof(struct wcnss_version)
1702 - sizeof(struct smd_msg_hdr)) {
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001703 pr_err("wcnss: invalid version data from wcnss %d\n",
Sameer Thalappil1878d762013-04-24 14:54:39 -07001704 len);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001705 return;
1706 }
Sameer Thalappil1878d762013-04-24 14:54:39 -07001707 rc = smd_read(penv->smd_ch, buf+sizeof(struct smd_msg_hdr),
1708 len);
1709 if (rc < len) {
1710 pr_err("wcnss: incomplete data read from smd\n");
1711 return;
1712 }
1713 pversion = (struct wcnss_version *)buf;
1714 penv->fw_major = pversion->major;
1715 penv->fw_minor = pversion->minor;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001716 snprintf(penv->wcnss_version, WCNSS_VERSION_LEN,
1717 "%02x%02x%02x%02x", pversion->major, pversion->minor,
1718 pversion->version, pversion->revision);
1719 pr_info("wcnss: version %s\n", penv->wcnss_version);
Sameer Thalappilf106a682013-02-16 20:41:11 -08001720 /* schedule work to download nvbin to ccpu */
1721 hw_type = wcnss_hardware_type();
1722 switch (hw_type) {
1723 case WCNSS_RIVA_HW:
1724 /* supported only if riva major >= 1 and minor >= 4 */
1725 if ((pversion->major >= 1) && (pversion->minor >= 4)) {
1726 pr_info("wcnss: schedule dnld work for riva\n");
1727 schedule_work(&penv->wcnssctrl_nvbin_dnld_work);
1728 }
1729 break;
1730
1731 case WCNSS_PRONTO_HW:
Sameer Thalappild821d632013-11-06 19:24:59 -08001732 smd_msg.msg_type = WCNSS_BUILD_VER_REQ;
1733 smd_msg.msg_len = sizeof(smd_msg);
1734 rc = wcnss_smd_tx(&smd_msg, smd_msg.msg_len);
1735 if (rc < 0)
1736 pr_err("wcnss: smd tx failed: %s\n", __func__);
1737
Sameer Thalappilf106a682013-02-16 20:41:11 -08001738 /* supported only if pronto major >= 1 and minor >= 4 */
1739 if ((pversion->major >= 1) && (pversion->minor >= 4)) {
1740 pr_info("wcnss: schedule dnld work for pronto\n");
1741 schedule_work(&penv->wcnssctrl_nvbin_dnld_work);
1742 }
1743 break;
1744
1745 default:
1746 pr_info("wcnss: unknown hw type (%d), will not schedule dnld work\n",
1747 hw_type);
1748 break;
1749 }
1750 break;
1751
Sameer Thalappild821d632013-11-06 19:24:59 -08001752 case WCNSS_BUILD_VER_RSP:
1753 if (len > WCNSS_MAX_BUILD_VER_LEN) {
1754 pr_err("wcnss: invalid build version data from wcnss %d\n",
1755 len);
1756 return;
1757 }
1758 rc = smd_read(penv->smd_ch, build, len);
1759 if (rc < len) {
1760 pr_err("wcnss: incomplete data read from smd\n");
1761 return;
1762 }
1763 build[len] = 0;
1764 pr_info("wcnss: build version %s\n", build);
1765 break;
1766
Sameer Thalappilf106a682013-02-16 20:41:11 -08001767 case WCNSS_NVBIN_DNLD_RSP:
Sameer Thalappil1878d762013-04-24 14:54:39 -07001768 penv->nv_downloaded = true;
1769 fw_status = wcnss_fw_status();
1770 pr_debug("wcnss: received WCNSS_NVBIN_DNLD_RSP from ccpu %u\n",
1771 fw_status);
Sameer Thalappilf6fb1892013-09-06 17:33:30 -07001772 wcnss_setup_vbat_monitoring();
Sameer Thalappil1878d762013-04-24 14:54:39 -07001773 break;
1774
1775 case WCNSS_CALDATA_DNLD_RSP:
1776 penv->nv_downloaded = true;
1777 fw_status = wcnss_fw_status();
1778 pr_debug("wcnss: received WCNSS_CALDATA_DNLD_RSP from ccpu %u\n",
1779 fw_status);
1780 break;
1781
1782 case WCNSS_CALDATA_UPLD_REQ:
Sameer Thalappil1878d762013-04-24 14:54:39 -07001783 extract_cal_data(len);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001784 break;
1785
1786 default:
Sameer Thalappilf106a682013-02-16 20:41:11 -08001787 pr_err("wcnss: invalid message type %d\n", phdr->msg_type);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001788 }
1789 return;
1790}
1791
1792static void wcnss_send_version_req(struct work_struct *worker)
1793{
1794 struct smd_msg_hdr smd_msg;
1795 int ret = 0;
1796
Sameer Thalappilf106a682013-02-16 20:41:11 -08001797 smd_msg.msg_type = WCNSS_VERSION_REQ;
1798 smd_msg.msg_len = sizeof(smd_msg);
1799 ret = wcnss_smd_tx(&smd_msg, smd_msg.msg_len);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001800 if (ret < 0)
1801 pr_err("wcnss: smd tx failed\n");
1802
1803 return;
1804}
1805
Hardik Kantilal Patel880805e2014-05-28 12:50:30 +05301806static void wcnss_send_pm_config(struct work_struct *worker)
1807{
1808 struct smd_msg_hdr *hdr;
1809 unsigned char *msg = NULL;
1810 int rc, prop_len;
1811 u32 *payload;
1812
1813 if (!of_find_property(penv->pdev->dev.of_node,
1814 "qcom,wcnss-pm", &prop_len))
1815 return;
1816
1817 msg = kmalloc((sizeof(struct smd_msg_hdr) + prop_len), GFP_KERNEL);
1818
1819 if (NULL == msg) {
1820 pr_err("wcnss: %s: failed to allocate memory\n", __func__);
1821 return;
1822 }
1823
1824 payload = (u32 *)(msg + sizeof(struct smd_msg_hdr));
1825
1826 prop_len /= sizeof(int);
1827
1828 rc = of_property_read_u32_array(penv->pdev->dev.of_node,
1829 "qcom,wcnss-pm", payload, prop_len);
1830 if (rc < 0) {
1831 pr_err("wcnss: property read failed\n");
1832 kfree(msg);
1833 return;
1834 }
1835
1836 pr_debug("%s:size=%d: <%d, %d, %d, %d, %d>\n", __func__,
1837 prop_len, *payload, *(payload+1), *(payload+2),
1838 *(payload+3), *(payload+4));
1839
1840 hdr = (struct smd_msg_hdr *)msg;
1841 hdr->msg_type = WCNSS_PM_CONFIG_REQ;
1842 hdr->msg_len = sizeof(struct smd_msg_hdr) + prop_len;
1843
1844 rc = wcnss_smd_tx(msg, hdr->msg_len);
1845 if (rc < 0)
1846 pr_err("wcnss: smd tx failed\n");
1847
1848 kfree(msg);
1849 return;
1850}
1851
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001852static DECLARE_RWSEM(wcnss_pm_sem);
Sameer Thalappil1878d762013-04-24 14:54:39 -07001853
1854static void wcnss_nvbin_dnld(void)
Sameer Thalappilf106a682013-02-16 20:41:11 -08001855{
1856 int ret = 0;
1857 struct nvbin_dnld_req_msg *dnld_req_msg;
1858 unsigned short total_fragments = 0;
1859 unsigned short count = 0;
1860 unsigned short retry_count = 0;
1861 unsigned short cur_frag_size = 0;
1862 unsigned char *outbuffer = NULL;
1863 const void *nv_blob_addr = NULL;
1864 unsigned int nv_blob_size = 0;
1865 const struct firmware *nv = NULL;
Sameer Thalappilf0d89ba2013-03-11 17:33:20 -07001866 struct device *dev = &penv->pdev->dev;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001867
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001868 down_read(&wcnss_pm_sem);
1869
Sameer Thalappilf106a682013-02-16 20:41:11 -08001870 ret = request_firmware(&nv, NVBIN_FILE, dev);
1871
1872 if (ret || !nv || !nv->data || !nv->size) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07001873 pr_err("wcnss: %s: request_firmware failed for %s\n",
1874 __func__, NVBIN_FILE);
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001875 goto out;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001876 }
1877
1878 /*
1879 * First 4 bytes in nv blob is validity bitmap.
1880 * We cannot validate nv, so skip those 4 bytes.
1881 */
1882 nv_blob_addr = nv->data + 4;
1883 nv_blob_size = nv->size - 4;
1884
1885 total_fragments = TOTALFRAGMENTS(nv_blob_size);
1886
1887 pr_info("wcnss: NV bin size: %d, total_fragments: %d\n",
1888 nv_blob_size, total_fragments);
1889
1890 /* get buffer for nv bin dnld req message */
1891 outbuffer = kmalloc((sizeof(struct nvbin_dnld_req_msg) +
1892 NV_FRAGMENT_SIZE), GFP_KERNEL);
1893
1894 if (NULL == outbuffer) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07001895 pr_err("wcnss: %s: failed to get buffer\n", __func__);
Sameer Thalappilf106a682013-02-16 20:41:11 -08001896 goto err_free_nv;
1897 }
1898
1899 dnld_req_msg = (struct nvbin_dnld_req_msg *)outbuffer;
1900
1901 dnld_req_msg->hdr.msg_type = WCNSS_NVBIN_DNLD_REQ;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001902 dnld_req_msg->dnld_req_params.msg_flags = 0;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001903
1904 for (count = 0; count < total_fragments; count++) {
1905 dnld_req_msg->dnld_req_params.frag_number = count;
1906
1907 if (count == (total_fragments - 1)) {
1908 /* last fragment, take care of boundry condition */
1909 cur_frag_size = nv_blob_size % NV_FRAGMENT_SIZE;
1910 if (!cur_frag_size)
1911 cur_frag_size = NV_FRAGMENT_SIZE;
1912
Sameer Thalappil1878d762013-04-24 14:54:39 -07001913 dnld_req_msg->dnld_req_params.msg_flags |=
1914 LAST_FRAGMENT;
1915 dnld_req_msg->dnld_req_params.msg_flags |=
1916 CAN_RECEIVE_CALDATA;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001917 } else {
1918 cur_frag_size = NV_FRAGMENT_SIZE;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001919 dnld_req_msg->dnld_req_params.msg_flags &=
1920 ~LAST_FRAGMENT;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001921 }
1922
1923 dnld_req_msg->dnld_req_params.nvbin_buffer_size =
1924 cur_frag_size;
1925
1926 dnld_req_msg->hdr.msg_len =
1927 sizeof(struct nvbin_dnld_req_msg) + cur_frag_size;
1928
1929 /* copy NV fragment */
1930 memcpy((outbuffer + sizeof(struct nvbin_dnld_req_msg)),
1931 (nv_blob_addr + count * NV_FRAGMENT_SIZE),
1932 cur_frag_size);
1933
1934 ret = wcnss_smd_tx(outbuffer, dnld_req_msg->hdr.msg_len);
1935
1936 retry_count = 0;
1937 while ((ret == -ENOSPC) && (retry_count <= 3)) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07001938 pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
1939 __func__);
Sameer Thalappilf106a682013-02-16 20:41:11 -08001940 pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
1941 count, dnld_req_msg->hdr.msg_len,
1942 total_fragments, retry_count);
1943
1944 /* wait and try again */
1945 msleep(20);
1946 retry_count++;
1947 ret = wcnss_smd_tx(outbuffer,
1948 dnld_req_msg->hdr.msg_len);
1949 }
1950
1951 if (ret < 0) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07001952 pr_err("wcnss: %s: smd tx failed\n", __func__);
Sameer Thalappilf106a682013-02-16 20:41:11 -08001953 pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
1954 count, dnld_req_msg->hdr.msg_len,
1955 total_fragments, retry_count);
1956 goto err_dnld;
1957 }
1958 }
1959
1960err_dnld:
1961 /* free buffer */
1962 kfree(outbuffer);
1963
1964err_free_nv:
1965 /* release firmware */
1966 release_firmware(nv);
1967
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001968out:
1969 up_read(&wcnss_pm_sem);
1970
Sameer Thalappilf106a682013-02-16 20:41:11 -08001971 return;
1972}
1973
Sameer Thalappil1878d762013-04-24 14:54:39 -07001974
1975static void wcnss_caldata_dnld(const void *cal_data,
1976 unsigned int cal_data_size, bool msg_to_follow)
1977{
1978 int ret = 0;
1979 struct cal_data_msg *cal_msg;
1980 unsigned short total_fragments = 0;
1981 unsigned short count = 0;
1982 unsigned short retry_count = 0;
1983 unsigned short cur_frag_size = 0;
1984 unsigned char *outbuffer = NULL;
1985
1986 total_fragments = TOTALFRAGMENTS(cal_data_size);
1987
1988 outbuffer = kmalloc((sizeof(struct cal_data_msg) +
1989 NV_FRAGMENT_SIZE), GFP_KERNEL);
1990
1991 if (NULL == outbuffer) {
1992 pr_err("wcnss: %s: failed to get buffer\n", __func__);
1993 return;
1994 }
1995
1996 cal_msg = (struct cal_data_msg *)outbuffer;
1997
1998 cal_msg->hdr.msg_type = WCNSS_CALDATA_DNLD_REQ;
1999 cal_msg->cal_params.msg_flags = 0;
2000
2001 for (count = 0; count < total_fragments; count++) {
2002 cal_msg->cal_params.frag_number = count;
2003
2004 if (count == (total_fragments - 1)) {
2005 cur_frag_size = cal_data_size % NV_FRAGMENT_SIZE;
2006 if (!cur_frag_size)
2007 cur_frag_size = NV_FRAGMENT_SIZE;
2008
2009 cal_msg->cal_params.msg_flags
2010 |= LAST_FRAGMENT;
2011 if (msg_to_follow)
2012 cal_msg->cal_params.msg_flags |=
2013 MESSAGE_TO_FOLLOW;
2014 } else {
2015 cur_frag_size = NV_FRAGMENT_SIZE;
2016 cal_msg->cal_params.msg_flags &=
2017 ~LAST_FRAGMENT;
2018 }
2019
2020 cal_msg->cal_params.total_size = cal_data_size;
2021 cal_msg->cal_params.frag_size =
2022 cur_frag_size;
2023
2024 cal_msg->hdr.msg_len =
2025 sizeof(struct cal_data_msg) + cur_frag_size;
2026
2027 memcpy((outbuffer + sizeof(struct cal_data_msg)),
2028 (cal_data + count * NV_FRAGMENT_SIZE),
2029 cur_frag_size);
2030
2031 ret = wcnss_smd_tx(outbuffer, cal_msg->hdr.msg_len);
2032
2033 retry_count = 0;
2034 while ((ret == -ENOSPC) && (retry_count <= 3)) {
2035 pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
2036 __func__);
2037 pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
2038 count, cal_msg->hdr.msg_len,
2039 total_fragments, retry_count);
2040
2041 /* wait and try again */
2042 msleep(20);
2043 retry_count++;
2044 ret = wcnss_smd_tx(outbuffer,
2045 cal_msg->hdr.msg_len);
2046 }
2047
2048 if (ret < 0) {
2049 pr_err("wcnss: %s: smd tx failed\n", __func__);
2050 pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
2051 count, cal_msg->hdr.msg_len,
2052 total_fragments, retry_count);
2053 goto err_dnld;
2054 }
2055 }
2056
2057
2058err_dnld:
2059 /* free buffer */
2060 kfree(outbuffer);
2061
2062 return;
2063}
2064
2065
2066static void wcnss_nvbin_dnld_main(struct work_struct *worker)
2067{
2068 int retry = 0;
2069
2070 if (!FW_CALDATA_CAPABLE())
2071 goto nv_download;
2072
2073 if (!penv->fw_cal_available && WCNSS_CONFIG_UNSPECIFIED
2074 != has_calibrated_data && !penv->user_cal_available) {
2075 while (!penv->user_cal_available && retry++ < 5)
2076 msleep(500);
2077 }
Sameer Thalappila5e70ae2013-10-22 13:18:20 -07002078 if (penv->fw_cal_available) {
2079 pr_info_ratelimited("wcnss: cal download, using fw cal");
2080 wcnss_caldata_dnld(penv->fw_cal_data, penv->fw_cal_rcvd, true);
Sameer Thalappil1878d762013-04-24 14:54:39 -07002081
2082 } else if (penv->user_cal_available) {
Sameer Thalappila5e70ae2013-10-22 13:18:20 -07002083 pr_info_ratelimited("wcnss: cal download, using user cal");
Sameer Thalappil1878d762013-04-24 14:54:39 -07002084 wcnss_caldata_dnld(penv->user_cal_data,
2085 penv->user_cal_rcvd, true);
2086 }
2087
2088nv_download:
2089 pr_info_ratelimited("wcnss: NV download");
2090 wcnss_nvbin_dnld();
2091
2092 return;
2093}
2094
Sameer Thalappil944c72d2013-08-14 17:56:17 -07002095static int wcnss_pm_notify(struct notifier_block *b,
2096 unsigned long event, void *p)
2097{
2098 switch (event) {
2099 case PM_SUSPEND_PREPARE:
2100 down_write(&wcnss_pm_sem);
2101 break;
Sameer Thalappil1878d762013-04-24 14:54:39 -07002102
Sameer Thalappil944c72d2013-08-14 17:56:17 -07002103 case PM_POST_SUSPEND:
2104 up_write(&wcnss_pm_sem);
2105 break;
2106 }
2107
2108 return NOTIFY_DONE;
2109}
2110
2111static struct notifier_block wcnss_pm_notifier = {
2112 .notifier_call = wcnss_pm_notify,
2113};
Sameer Thalappil1878d762013-04-24 14:54:39 -07002114
Sameer Thalappil13780d12013-12-19 17:04:21 -08002115static int wcnss_ctrl_open(struct inode *inode, struct file *file)
2116{
2117 int rc = 0;
2118
2119 if (!penv || penv->ctrl_device_opened)
2120 return -EFAULT;
2121
2122 penv->ctrl_device_opened = 1;
2123
2124 return rc;
2125}
2126
2127
2128void process_usr_ctrl_cmd(u8 *buf, size_t len)
2129{
2130 u16 cmd = buf[0] << 8 | buf[1];
2131
2132 switch (cmd) {
2133
2134 case WCNSS_USR_SERIAL_NUM:
2135 if (WCNSS_MIN_SERIAL_LEN > len) {
2136 pr_err("%s: Invalid serial number\n", __func__);
2137 return;
2138 }
2139 penv->serial_number = buf[2] << 24 | buf[3] << 16
2140 | buf[4] << 8 | buf[5];
2141 break;
2142
2143 case WCNSS_USR_HAS_CAL_DATA:
2144 if (1 < buf[2])
2145 pr_err("%s: Invalid data for cal %d\n", __func__,
2146 buf[2]);
2147 has_calibrated_data = buf[2];
2148 break;
2149
Hardik Kantilal Patelab039d82014-03-22 06:30:41 +05302150 case WCNSS_USR_WLAN_MAC_ADDR:
2151 memcpy(&penv->wlan_nv_macAddr, &buf[2],
2152 sizeof(penv->wlan_nv_macAddr));
2153
2154 pr_debug("%s: MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
2155 penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
2156 penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
2157 penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
2158 break;
2159
Sameer Thalappil13780d12013-12-19 17:04:21 -08002160 default:
2161 pr_err("%s: Invalid command %d\n", __func__, cmd);
2162 break;
2163 }
2164}
2165
2166static ssize_t wcnss_ctrl_write(struct file *fp, const char __user
2167 *user_buffer, size_t count, loff_t *position)
2168{
2169 int rc = 0;
2170 u8 buf[WCNSS_MAX_CMD_LEN];
2171
2172 if (!penv || !penv->ctrl_device_opened || WCNSS_MAX_CMD_LEN < count
2173 || WCNSS_MIN_CMD_LEN > count)
2174 return -EFAULT;
2175
2176 mutex_lock(&penv->ctrl_lock);
2177 rc = copy_from_user(buf, user_buffer, count);
2178 if (0 == rc)
2179 process_usr_ctrl_cmd(buf, count);
2180
2181 mutex_unlock(&penv->ctrl_lock);
2182
2183 return rc;
2184}
2185
2186
2187static const struct file_operations wcnss_ctrl_fops = {
2188 .owner = THIS_MODULE,
2189 .open = wcnss_ctrl_open,
2190 .write = wcnss_ctrl_write,
2191};
2192
2193static struct miscdevice wcnss_usr_ctrl = {
2194 .minor = MISC_DYNAMIC_MINOR,
2195 .name = CTRL_DEVICE,
2196 .fops = &wcnss_ctrl_fops,
2197};
2198
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002199static int
2200wcnss_trigger_config(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002201{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002202 int ret;
Ankur Nandwanib0039b02011-08-09 14:00:45 -07002203 struct qcom_wcnss_opts *pdata;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002204 unsigned long wcnss_phys_addr;
2205 int size = 0;
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002206 struct resource *res;
Sameer Thalappilfe03e832014-02-19 10:37:16 -08002207 int pil_retry = 0;
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002208 int has_pronto_hw = of_property_read_bool(pdev->dev.of_node,
Sameer Thalappil820f87b2013-05-21 20:40:54 -07002209 "qcom,has-pronto-hw");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002210
Sameer Thalappild0952f62013-05-03 10:18:29 -07002211 if (of_property_read_u32(pdev->dev.of_node,
2212 "qcom,wlan-rx-buff-count", &penv->wlan_rx_buff_count)) {
2213 penv->wlan_rx_buff_count = WCNSS_DEF_WLAN_RX_BUFF_COUNT;
2214 }
2215
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002216 /* make sure we are only triggered once */
2217 if (penv->triggered)
2218 return 0;
2219 penv->triggered = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002220
Ankur Nandwanib0039b02011-08-09 14:00:45 -07002221 /* initialize the WCNSS device configuration */
2222 pdata = pdev->dev.platform_data;
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002223 if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo) {
2224 if (has_pronto_hw) {
2225 has_48mhz_xo = of_property_read_bool(pdev->dev.of_node,
Sameer Thalappil820f87b2013-05-21 20:40:54 -07002226 "qcom,has-48mhz-xo");
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002227 } else {
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002228 has_48mhz_xo = pdata->has_48mhz_xo;
2229 }
2230 }
Sameer Thalappilf00dbeb2013-03-01 18:33:30 -08002231 penv->wcnss_hw_type = (has_pronto_hw) ? WCNSS_PRONTO_HW : WCNSS_RIVA_HW;
Ankur Nandwanib0039b02011-08-09 14:00:45 -07002232 penv->wlan_config.use_48mhz_xo = has_48mhz_xo;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002233
Sameer Thalappil58cec312013-05-13 15:52:08 -07002234 if (WCNSS_CONFIG_UNSPECIFIED == has_autodetect_xo && has_pronto_hw) {
2235 has_autodetect_xo = of_property_read_bool(pdev->dev.of_node,
Sameer Thalappil820f87b2013-05-21 20:40:54 -07002236 "qcom,has-autodetect-xo");
Sameer Thalappil58cec312013-05-13 15:52:08 -07002237 }
2238
Jeff Johnson5fda4f82012-01-09 14:15:34 -08002239 penv->thermal_mitigation = 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002240 strlcpy(penv->wcnss_version, "INVALID", WCNSS_VERSION_LEN);
Jeff Johnson5fda4f82012-01-09 14:15:34 -08002241
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -07002242 /* Configure 5 wire GPIOs */
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002243 if (!has_pronto_hw) {
2244 penv->gpios_5wire = platform_get_resource_byname(pdev,
2245 IORESOURCE_IO, "wcnss_gpios_5wire");
2246
2247 /* allocate 5-wire GPIO resources */
2248 if (!penv->gpios_5wire) {
2249 dev_err(&pdev->dev, "insufficient IO resources\n");
2250 ret = -ENOENT;
2251 goto fail_gpio_res;
2252 }
2253 ret = wcnss_gpios_config(penv->gpios_5wire, true);
2254 } else
2255 ret = wcnss_pronto_gpios_config(&pdev->dev, true);
2256
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -07002257 if (ret) {
2258 dev_err(&pdev->dev, "WCNSS gpios config failed.\n");
2259 goto fail_gpio_res;
2260 }
2261
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002262 /* power up the WCNSS */
2263 ret = wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
Sameer Thalappil1d69b8022013-06-10 19:10:07 -07002264 WCNSS_WLAN_SWITCH_ON,
2265 &penv->iris_xo_mode_set);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002266 if (ret) {
2267 dev_err(&pdev->dev, "WCNSS Power-up failed.\n");
2268 goto fail_power;
2269 }
2270
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002271 /* allocate resources */
2272 penv->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
2273 "wcnss_mmio");
2274 penv->tx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
2275 "wcnss_wlantx_irq");
2276 penv->rx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
2277 "wcnss_wlanrx_irq");
2278
2279 if (!(penv->mmio_res && penv->tx_irq_res && penv->rx_irq_res)) {
2280 dev_err(&pdev->dev, "insufficient resources\n");
2281 ret = -ENOENT;
2282 goto fail_res;
2283 }
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002284 INIT_WORK(&penv->wcnssctrl_rx_work, wcnssctrl_rx_handler);
2285 INIT_WORK(&penv->wcnssctrl_version_work, wcnss_send_version_req);
Hardik Kantilal Patel880805e2014-05-28 12:50:30 +05302286 INIT_WORK(&penv->wcnss_pm_config_work, wcnss_send_pm_config);
Sameer Thalappil1878d762013-04-24 14:54:39 -07002287 INIT_WORK(&penv->wcnssctrl_nvbin_dnld_work, wcnss_nvbin_dnld_main);
Jeff Johnson8b1f6d02012-02-03 20:43:26 -08002288
Sameer Thalappilf138da52012-07-30 12:56:37 -07002289 wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss");
2290
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002291 if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
2292 size = 0x3000;
2293 wcnss_phys_addr = MSM_PRONTO_PHYS;
2294 } else {
2295 wcnss_phys_addr = MSM_RIVA_PHYS;
2296 size = SZ_256;
2297 }
2298
2299 penv->msm_wcnss_base = ioremap(wcnss_phys_addr, size);
2300 if (!penv->msm_wcnss_base) {
2301 ret = -ENOMEM;
2302 pr_err("%s: ioremap wcnss physical failed\n", __func__);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002303 goto fail_ioremap;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002304 }
2305
Sameer Thalappild3aad702013-01-22 18:52:24 -08002306 if (wcnss_hardware_type() == WCNSS_RIVA_HW) {
2307 penv->riva_ccu_base = ioremap(MSM_RIVA_CCU_BASE, SZ_512);
2308 if (!penv->riva_ccu_base) {
2309 ret = -ENOMEM;
2310 pr_err("%s: ioremap wcnss physical failed\n", __func__);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002311 goto fail_ioremap2;
Sameer Thalappild3aad702013-01-22 18:52:24 -08002312 }
2313 } else {
2314 penv->pronto_a2xb_base = ioremap(MSM_PRONTO_A2XB_BASE, SZ_512);
2315 if (!penv->pronto_a2xb_base) {
2316 ret = -ENOMEM;
2317 pr_err("%s: ioremap wcnss physical failed\n", __func__);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002318 goto fail_ioremap2;
Sameer Thalappild3aad702013-01-22 18:52:24 -08002319 }
Sameer Thalappil16cae9b2013-03-04 18:02:50 -08002320 penv->pronto_ccpu_base = ioremap(MSM_PRONTO_CCPU_BASE, SZ_512);
2321 if (!penv->pronto_ccpu_base) {
2322 ret = -ENOMEM;
2323 pr_err("%s: ioremap wcnss physical failed\n", __func__);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002324 goto fail_ioremap3;
Sameer Thalappil16cae9b2013-03-04 18:02:50 -08002325 }
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002326 /* for reset FIQ */
2327 res = platform_get_resource_byname(penv->pdev,
2328 IORESOURCE_MEM, "wcnss_fiq");
2329 if (!res) {
2330 dev_err(&pdev->dev, "insufficient irq mem resources\n");
2331 ret = -ENOENT;
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002332 goto fail_ioremap4;
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002333 }
2334 penv->fiq_reg = ioremap_nocache(res->start, resource_size(res));
2335 if (!penv->fiq_reg) {
2336 pr_err("wcnss: %s: ioremap_nocache() failed fiq_reg addr:%pr\n",
2337 __func__, &res->start);
2338 ret = -ENOMEM;
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002339 goto fail_ioremap4;
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002340 }
Sameer Thalappil767ff492013-06-19 15:25:21 -07002341 penv->pronto_saw2_base = ioremap_nocache(MSM_PRONTO_SAW2_BASE,
2342 SZ_32);
2343 if (!penv->pronto_saw2_base) {
2344 pr_err("%s: ioremap wcnss physical(saw2) failed\n",
2345 __func__);
2346 ret = -ENOMEM;
2347 goto fail_ioremap5;
2348 }
Sameer Thalappil66d2b392013-07-02 11:32:55 -07002349 penv->pronto_pll_base = ioremap_nocache(MSM_PRONTO_PLL_BASE,
2350 SZ_64);
2351 if (!penv->pronto_pll_base) {
2352 pr_err("%s: ioremap wcnss physical(pll) failed\n",
2353 __func__);
2354 ret = -ENOMEM;
2355 goto fail_ioremap6;
2356 }
2357
Sameer Thalappil670197c2013-07-19 16:31:14 -07002358 penv->wlan_tx_phy_aborts = ioremap(MSM_PRONTO_TXP_PHY_ABORT,
2359 SZ_8);
2360 if (!penv->wlan_tx_phy_aborts) {
2361 ret = -ENOMEM;
2362 pr_err("%s: ioremap wlan TX PHY failed\n", __func__);
2363 goto fail_ioremap7;
2364 }
2365 penv->wlan_brdg_err_source = ioremap(MSM_PRONTO_BRDG_ERR_SRC,
2366 SZ_8);
2367 if (!penv->wlan_brdg_err_source) {
2368 ret = -ENOMEM;
2369 pr_err("%s: ioremap wlan BRDG ERR failed\n", __func__);
2370 goto fail_ioremap8;
2371 }
Yue Mae490b452013-11-12 00:07:33 -08002372 penv->wlan_tx_status = ioremap(MSM_PRONTO_TXP_STATUS, SZ_8);
2373 if (!penv->wlan_tx_status) {
2374 ret = -ENOMEM;
2375 pr_err("%s: ioremap wlan TX STATUS failed\n", __func__);
2376 goto fail_ioremap9;
2377 }
Sameer Thalappil7f2d82f2013-12-17 11:26:18 -08002378 penv->alarms_txctl = ioremap(MSM_PRONTO_ALARMS_TXCTL, SZ_8);
2379 if (!penv->alarms_txctl) {
2380 ret = -ENOMEM;
2381 pr_err("%s: ioremap alarms TXCTL failed\n", __func__);
2382 goto fail_ioremap10;
2383 }
2384 penv->alarms_tactl = ioremap(MSM_PRONTO_ALARMS_TACTL, SZ_8);
2385 if (!penv->alarms_tactl) {
2386 ret = -ENOMEM;
2387 pr_err("%s: ioremap alarms TACTL failed\n", __func__);
2388 goto fail_ioremap11;
2389 }
Yue Ma2fd1ffd2014-02-18 16:41:00 -08002390 penv->pronto_mcu_base = ioremap(MSM_PRONTO_MCU_BASE, SZ_1K);
2391 if (!penv->pronto_mcu_base) {
2392 ret = -ENOMEM;
2393 pr_err("%s: ioremap wcnss physical(mcu) failed\n",
2394 __func__);
2395 goto fail_ioremap12;
2396 }
Sameer Thalappild3aad702013-01-22 18:52:24 -08002397 }
Sameer Thalappilf6fb1892013-09-06 17:33:30 -07002398 penv->adc_tm_dev = qpnp_get_adc_tm(&penv->pdev->dev, "wcnss");
2399 if (IS_ERR(penv->adc_tm_dev)) {
2400 pr_err("%s: adc get failed\n", __func__);
2401 penv->adc_tm_dev = NULL;
2402 } else {
2403 INIT_DELAYED_WORK(&penv->vbatt_work, wcnss_update_vbatt);
2404 penv->fw_vbatt_state = WCNSS_CONFIG_UNSPECIFIED;
2405 }
Sameer Thalappild3aad702013-01-22 18:52:24 -08002406
Sameer Thalappilfe03e832014-02-19 10:37:16 -08002407 do {
2408 /* trigger initialization of the WCNSS */
2409 penv->pil = subsystem_get(WCNSS_PIL_DEVICE);
2410 if (IS_ERR(penv->pil)) {
2411 dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
2412 ret = PTR_ERR(penv->pil);
2413 wcnss_pronto_log_debug_regs();
2414 }
2415 } while (pil_retry++ < WCNSS_MAX_PIL_RETRY && IS_ERR(penv->pil));
2416
2417 if (pil_retry >= WCNSS_MAX_PIL_RETRY) {
Yue Ma162af742014-04-02 16:50:41 -07002418 wcnss_reset_intr();
Pradeep Kumar Goudaguntab18e5202014-04-29 11:48:21 +05302419 if (penv->wcnss_notif_hdle)
2420 subsys_notif_unregister_notifier(penv->wcnss_notif_hdle,
2421 &wnb);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002422 penv->pil = NULL;
2423 goto fail_pil;
2424 }
2425
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002426 return 0;
2427
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002428fail_pil:
2429 if (penv->riva_ccu_base)
2430 iounmap(penv->riva_ccu_base);
Yue Ma2fd1ffd2014-02-18 16:41:00 -08002431 if (penv->pronto_mcu_base)
2432 iounmap(penv->pronto_mcu_base);
2433fail_ioremap12:
Sameer Thalappil7f2d82f2013-12-17 11:26:18 -08002434 if (penv->alarms_tactl)
2435 iounmap(penv->alarms_tactl);
2436fail_ioremap11:
2437 if (penv->alarms_txctl)
2438 iounmap(penv->alarms_txctl);
2439fail_ioremap10:
Yue Mae490b452013-11-12 00:07:33 -08002440 if (penv->wlan_tx_status)
2441 iounmap(penv->wlan_tx_status);
2442fail_ioremap9:
Sameer Thalappil670197c2013-07-19 16:31:14 -07002443 if (penv->wlan_brdg_err_source)
2444 iounmap(penv->wlan_brdg_err_source);
2445fail_ioremap8:
2446 if (penv->wlan_tx_phy_aborts)
2447 iounmap(penv->wlan_tx_phy_aborts);
2448fail_ioremap7:
Sameer Thalappil66d2b392013-07-02 11:32:55 -07002449 if (penv->pronto_pll_base)
2450 iounmap(penv->pronto_pll_base);
2451fail_ioremap6:
Sameer Thalappil767ff492013-06-19 15:25:21 -07002452 if (penv->pronto_saw2_base)
2453 iounmap(penv->pronto_saw2_base);
2454fail_ioremap5:
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002455 if (penv->fiq_reg)
2456 iounmap(penv->fiq_reg);
2457fail_ioremap4:
2458 if (penv->pronto_ccpu_base)
2459 iounmap(penv->pronto_ccpu_base);
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002460fail_ioremap3:
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002461 if (penv->pronto_a2xb_base)
2462 iounmap(penv->pronto_a2xb_base);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -08002463fail_ioremap2:
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002464 if (penv->msm_wcnss_base)
2465 iounmap(penv->msm_wcnss_base);
Sameer Thalappild3aad702013-01-22 18:52:24 -08002466fail_ioremap:
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002467 wake_lock_destroy(&penv->wcnss_wake_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002468fail_res:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002469 wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
Sameer Thalappil1d69b8022013-06-10 19:10:07 -07002470 WCNSS_WLAN_SWITCH_OFF, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002471fail_power:
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002472 if (has_pronto_hw)
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002473 wcnss_pronto_gpios_config(&pdev->dev, false);
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002474 else
2475 wcnss_gpios_config(penv->gpios_5wire, false);
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -07002476fail_gpio_res:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002477 penv = NULL;
2478 return ret;
2479}
2480
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002481static int wcnss_node_open(struct inode *inode, struct file *file)
2482{
2483 struct platform_device *pdev;
Sameer Thalappild0bb3bb2013-10-09 13:36:26 -07002484 int rc = 0;
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002485
Sameer Thalappil37c20682013-05-31 14:01:25 -07002486 if (!penv)
2487 return -EFAULT;
2488
Sameer Thalappil1878d762013-04-24 14:54:39 -07002489 if (!penv->triggered) {
2490 pr_info(DEVICE " triggered by userspace\n");
2491 pdev = penv->pdev;
Sameer Thalappild0bb3bb2013-10-09 13:36:26 -07002492 rc = wcnss_trigger_config(pdev);
2493 if (rc)
2494 return -EFAULT;
Sameer Thalappil1878d762013-04-24 14:54:39 -07002495 }
2496
2497 mutex_lock(&penv->dev_lock);
2498 penv->user_cal_rcvd = 0;
2499 penv->user_cal_read = 0;
2500 penv->user_cal_available = false;
2501 penv->user_cal_data = NULL;
2502 penv->device_opened = 1;
2503 mutex_unlock(&penv->dev_lock);
2504
Sameer Thalappild0bb3bb2013-10-09 13:36:26 -07002505 return rc;
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002506}
2507
Sameer Thalappil1878d762013-04-24 14:54:39 -07002508static ssize_t wcnss_wlan_read(struct file *fp, char __user
2509 *buffer, size_t count, loff_t *position)
2510{
2511 int rc = 0;
2512
Sameer Thalappil37c20682013-05-31 14:01:25 -07002513 if (!penv || !penv->device_opened)
Sameer Thalappil1878d762013-04-24 14:54:39 -07002514 return -EFAULT;
2515
2516 rc = wait_event_interruptible(penv->read_wait, penv->fw_cal_rcvd
2517 > penv->user_cal_read || penv->fw_cal_available);
2518
2519 if (rc < 0)
2520 return rc;
2521
2522 mutex_lock(&penv->dev_lock);
2523
2524 if (penv->fw_cal_available && penv->fw_cal_rcvd
2525 == penv->user_cal_read) {
2526 rc = 0;
2527 goto exit;
2528 }
2529
2530 if (count > penv->fw_cal_rcvd - penv->user_cal_read)
2531 count = penv->fw_cal_rcvd - penv->user_cal_read;
2532
2533 rc = copy_to_user(buffer, penv->fw_cal_data +
2534 penv->user_cal_read, count);
2535 if (rc == 0) {
2536 penv->user_cal_read += count;
2537 rc = count;
2538 }
2539
2540exit:
2541 mutex_unlock(&penv->dev_lock);
2542 return rc;
2543}
2544
2545/* first (valid) write to this device should be 4 bytes cal file size */
2546static ssize_t wcnss_wlan_write(struct file *fp, const char __user
2547 *user_buffer, size_t count, loff_t *position)
2548{
2549 int rc = 0;
Arif Hussain677c92d2013-11-01 19:09:11 -07002550 size_t size = 0;
Sameer Thalappil1878d762013-04-24 14:54:39 -07002551
Sameer Thalappil37c20682013-05-31 14:01:25 -07002552 if (!penv || !penv->device_opened || penv->user_cal_available)
Sameer Thalappil1878d762013-04-24 14:54:39 -07002553 return -EFAULT;
2554
2555 if (penv->user_cal_rcvd == 0 && count >= 4
2556 && !penv->user_cal_data) {
2557 rc = copy_from_user((void *)&size, user_buffer, 4);
Arif Hussain677c92d2013-11-01 19:09:11 -07002558 if (!size || size > MAX_CALIBRATED_DATA_SIZE) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07002559 pr_err(DEVICE " invalid size to write %d\n", size);
2560 return -EFAULT;
2561 }
2562
2563 rc += count;
2564 count -= 4;
2565 penv->user_cal_exp_size = size;
2566 penv->user_cal_data = kmalloc(size, GFP_KERNEL);
2567 if (penv->user_cal_data == NULL) {
2568 pr_err(DEVICE " no memory to write\n");
2569 return -ENOMEM;
2570 }
2571 if (0 == count)
2572 goto exit;
2573
2574 } else if (penv->user_cal_rcvd == 0 && count < 4)
2575 return -EFAULT;
2576
Arif Hussain677c92d2013-11-01 19:09:11 -07002577 if ((UINT32_MAX - count < penv->user_cal_rcvd) ||
2578 MAX_CALIBRATED_DATA_SIZE < count + penv->user_cal_rcvd) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07002579 pr_err(DEVICE " invalid size to write %d\n", count +
2580 penv->user_cal_rcvd);
2581 rc = -ENOMEM;
2582 goto exit;
2583 }
2584 rc = copy_from_user((void *)penv->user_cal_data +
2585 penv->user_cal_rcvd, user_buffer, count);
2586 if (0 == rc) {
2587 penv->user_cal_rcvd += count;
2588 rc += count;
2589 }
2590 if (penv->user_cal_rcvd == penv->user_cal_exp_size) {
2591 penv->user_cal_available = true;
2592 pr_info_ratelimited("wcnss: user cal written");
2593 }
2594
2595exit:
2596 return rc;
2597}
2598
2599
Yue Mafa9638e2013-12-19 16:59:27 -08002600static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
2601 void *ss_handle)
2602{
2603 pr_debug("%s: wcnss notification event: %lu\n", __func__, code);
2604
2605 if (SUBSYS_POWERUP_FAILURE == code)
2606 wcnss_pronto_log_debug_regs();
Mihir Shete173dafe2014-03-13 18:50:49 +05302607 else if (SUBSYS_BEFORE_SHUTDOWN == code)
2608 penv->is_shutdown = 1;
2609 else if (SUBSYS_AFTER_POWERUP == code)
2610 penv->is_shutdown = 0;
Yue Mafa9638e2013-12-19 16:59:27 -08002611
2612 return NOTIFY_DONE;
2613}
2614
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002615static const struct file_operations wcnss_node_fops = {
2616 .owner = THIS_MODULE,
2617 .open = wcnss_node_open,
Sameer Thalappil1878d762013-04-24 14:54:39 -07002618 .read = wcnss_wlan_read,
2619 .write = wcnss_wlan_write,
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002620};
2621
2622static struct miscdevice wcnss_misc = {
2623 .minor = MISC_DYNAMIC_MINOR,
2624 .name = DEVICE,
2625 .fops = &wcnss_node_fops,
2626};
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002627
2628static int __devinit
2629wcnss_wlan_probe(struct platform_device *pdev)
2630{
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002631 int ret = 0;
2632
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002633 /* verify we haven't been called more than once */
2634 if (penv) {
2635 dev_err(&pdev->dev, "cannot handle multiple devices.\n");
2636 return -ENODEV;
2637 }
2638
2639 /* create an environment to track the device */
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -08002640 penv = devm_kzalloc(&pdev->dev, sizeof(*penv), GFP_KERNEL);
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002641 if (!penv) {
2642 dev_err(&pdev->dev, "cannot allocate device memory.\n");
2643 return -ENOMEM;
2644 }
2645 penv->pdev = pdev;
2646
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002647 /* register sysfs entries */
2648 ret = wcnss_create_sysfs(&pdev->dev);
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -08002649 if (ret) {
2650 penv = NULL;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002651 return -ENOENT;
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -08002652 }
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002653
Yue Mafa9638e2013-12-19 16:59:27 -08002654 /* register wcnss event notification */
2655 penv->wcnss_notif_hdle = subsys_notif_register_notifier("wcnss", &wnb);
2656 if (IS_ERR(penv->wcnss_notif_hdle)) {
2657 pr_err("wcnss: register event notification failed!\n");
2658 return PTR_ERR(penv->wcnss_notif_hdle);
2659 }
2660
Sameer Thalappil1878d762013-04-24 14:54:39 -07002661 mutex_init(&penv->dev_lock);
Sameer Thalappil13780d12013-12-19 17:04:21 -08002662 mutex_init(&penv->ctrl_lock);
Sameer Thalappilf6fb1892013-09-06 17:33:30 -07002663 mutex_init(&penv->vbat_monitor_mutex);
Sameer Thalappil1878d762013-04-24 14:54:39 -07002664 init_waitqueue_head(&penv->read_wait);
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002665
Sameer Thalappild3aad702013-01-22 18:52:24 -08002666 /* Since we were built into the kernel we'll be called as part
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002667 * of kernel initialization. We don't know if userspace
2668 * applications are available to service PIL at this time
2669 * (they probably are not), so we simply create a device node
2670 * here. When userspace is available it should touch the
2671 * device so that we know that WCNSS configuration can take
2672 * place
2673 */
2674 pr_info(DEVICE " probed in built-in mode\n");
Sameer Thalappil13780d12013-12-19 17:04:21 -08002675
2676 misc_register(&wcnss_usr_ctrl);
2677
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002678 return misc_register(&wcnss_misc);
2679
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002680}
2681
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002682static int __devexit
2683wcnss_wlan_remove(struct platform_device *pdev)
2684{
Yue Mafa9638e2013-12-19 16:59:27 -08002685 if (penv->wcnss_notif_hdle)
2686 subsys_notif_unregister_notifier(penv->wcnss_notif_hdle, &wnb);
Jeff Johnson8b1f6d02012-02-03 20:43:26 -08002687 wcnss_remove_sysfs(&pdev->dev);
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -08002688 penv = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002689 return 0;
2690}
2691
2692
2693static const struct dev_pm_ops wcnss_wlan_pm_ops = {
2694 .suspend = wcnss_wlan_suspend,
2695 .resume = wcnss_wlan_resume,
2696};
2697
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002698#ifdef CONFIG_WCNSS_CORE_PRONTO
2699static struct of_device_id msm_wcnss_pronto_match[] = {
2700 {.compatible = "qcom,wcnss_wlan"},
2701 {}
2702};
2703#endif
2704
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002705static struct platform_driver wcnss_wlan_driver = {
2706 .driver = {
2707 .name = DEVICE,
2708 .owner = THIS_MODULE,
2709 .pm = &wcnss_wlan_pm_ops,
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002710#ifdef CONFIG_WCNSS_CORE_PRONTO
2711 .of_match_table = msm_wcnss_pronto_match,
2712#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002713 },
2714 .probe = wcnss_wlan_probe,
2715 .remove = __devexit_p(wcnss_wlan_remove),
2716};
2717
2718static int __init wcnss_wlan_init(void)
2719{
Sameer Thalappil24db5282012-09-10 11:58:33 -07002720 int ret = 0;
2721
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002722 platform_driver_register(&wcnss_wlan_driver);
2723 platform_driver_register(&wcnss_wlan_ctrl_driver);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002724 platform_driver_register(&wcnss_ctrl_driver);
Sameer Thalappil944c72d2013-08-14 17:56:17 -07002725 register_pm_notifier(&wcnss_pm_notifier);
Sameer Thalappil24db5282012-09-10 11:58:33 -07002726#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
2727 ret = wcnss_prealloc_init();
2728 if (ret < 0)
2729 pr_err("wcnss: pre-allocation failed\n");
2730#endif
2731
2732 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002733}
2734
2735static void __exit wcnss_wlan_exit(void)
2736{
2737 if (penv) {
2738 if (penv->pil)
Stephen Boyd77db8bb2012-06-27 15:15:16 -07002739 subsystem_put(penv->pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002740 penv = NULL;
2741 }
2742
Sameer Thalappil24db5282012-09-10 11:58:33 -07002743#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
2744 wcnss_prealloc_deinit();
2745#endif
Sameer Thalappil944c72d2013-08-14 17:56:17 -07002746 unregister_pm_notifier(&wcnss_pm_notifier);
2747 platform_driver_unregister(&wcnss_ctrl_driver);
2748 platform_driver_unregister(&wcnss_wlan_ctrl_driver);
2749 platform_driver_unregister(&wcnss_wlan_driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002750}
2751
2752module_init(wcnss_wlan_init);
2753module_exit(wcnss_wlan_exit);
2754
2755MODULE_LICENSE("GPL v2");
2756MODULE_VERSION(VERSION);
2757MODULE_DESCRIPTION(DEVICE "Driver");