blob: 8e695c32557e66f14baf29d008006aeb96872cfb [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
Sameer Thalappileeb8c412012-08-10 17:17:09 -070039#include <mach/msm_smd.h>
Sameer Thalappil19e02352012-09-24 15:26:12 -070040#include <mach/msm_iomap.h>
Stephen Boyd77db8bb2012-06-27 15:15:16 -070041#include <mach/subsystem_restart.h>
42
Sameer Thalappil24db5282012-09-10 11:58:33 -070043#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
44#include "wcnss_prealloc.h"
45#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046
47#define DEVICE "wcnss_wlan"
Sameer Thalappil13780d12013-12-19 17:04:21 -080048#define CTRL_DEVICE "wcnss_ctrl"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049#define VERSION "1.01"
50#define WCNSS_PIL_DEVICE "wcnss"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051
Ankur Nandwanib0039b02011-08-09 14:00:45 -070052/* module params */
53#define WCNSS_CONFIG_UNSPECIFIED (-1)
Arif Hussain677c92d2013-11-01 19:09:11 -070054#define UINT32_MAX (0xFFFFFFFFU)
Sameer Thalappileeb8c412012-08-10 17:17:09 -070055
Ankur Nandwania4510492011-08-29 15:56:23 -070056static int has_48mhz_xo = WCNSS_CONFIG_UNSPECIFIED;
Jeff Johnsonb3377e32011-11-18 23:32:27 -080057module_param(has_48mhz_xo, int, S_IWUSR | S_IRUGO);
Ankur Nandwanib0039b02011-08-09 14:00:45 -070058MODULE_PARM_DESC(has_48mhz_xo, "Is an external 48 MHz XO present");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059
Sameer Thalappil1878d762013-04-24 14:54:39 -070060static int has_calibrated_data = WCNSS_CONFIG_UNSPECIFIED;
61module_param(has_calibrated_data, int, S_IWUSR | S_IRUGO);
62MODULE_PARM_DESC(has_calibrated_data, "whether calibrated data file available");
63
Sameer Thalappil58cec312013-05-13 15:52:08 -070064static int has_autodetect_xo = WCNSS_CONFIG_UNSPECIFIED;
65module_param(has_autodetect_xo, int, S_IWUSR | S_IRUGO);
66MODULE_PARM_DESC(has_autodetect_xo, "Perform auto detect to configure IRIS XO");
67
Sheng Fangbb421672013-03-19 08:27:28 +080068static int do_not_cancel_vote = WCNSS_CONFIG_UNSPECIFIED;
69module_param(do_not_cancel_vote, int, S_IWUSR | S_IRUGO);
70MODULE_PARM_DESC(do_not_cancel_vote, "Do not cancel votes for wcnss");
71
Sameer Thalappiled3f7da2012-11-01 12:54:01 -070072static DEFINE_SPINLOCK(reg_spinlock);
73
74#define MSM_RIVA_PHYS 0x03204000
75#define MSM_PRONTO_PHYS 0xfb21b000
76
77#define RIVA_SPARE_OFFSET 0x0b4
78#define RIVA_SUSPEND_BIT BIT(24)
79
Sameer Thalappil77716e52012-11-08 13:41:04 -080080#define MSM_RIVA_CCU_BASE 0x03200800
81
Sameer Thalappil16cae9b2013-03-04 18:02:50 -080082#define CCU_RIVA_INVALID_ADDR_OFFSET 0x100
83#define CCU_RIVA_LAST_ADDR0_OFFSET 0x104
84#define CCU_RIVA_LAST_ADDR1_OFFSET 0x108
85#define CCU_RIVA_LAST_ADDR2_OFFSET 0x10c
Sameer Thalappil77716e52012-11-08 13:41:04 -080086
Sameer Thalappilbcc06182013-05-30 16:18:57 -070087#define PRONTO_PMU_SPARE_OFFSET 0x1088
88
Sameer Thalappil670197c2013-07-19 16:31:14 -070089#define PRONTO_PMU_COM_GDSCR_OFFSET 0x0024
90#define PRONTO_PMU_COM_GDSCR_SW_COLLAPSE BIT(0)
91#define PRONTO_PMU_COM_GDSCR_HW_CTRL BIT(1)
92
93#define PRONTO_PMU_WLAN_BCR_OFFSET 0x0050
94#define PRONTO_PMU_WLAN_BCR_BLK_ARES BIT(0)
95
96#define PRONTO_PMU_WLAN_GDSCR_OFFSET 0x0054
97#define PRONTO_PMU_WLAN_GDSCR_SW_COLLAPSE BIT(0)
98
Sameer Thalappilbcc06182013-05-30 16:18:57 -070099
100#define PRONTO_PMU_CBCR_OFFSET 0x0008
101#define PRONTO_PMU_CBCR_CLK_EN BIT(0)
102
Mihir Shete2775e4652013-09-20 16:21:49 -0700103#define PRONTO_PMU_COM_CPU_CBCR_OFFSET 0x0030
104#define PRONTO_PMU_COM_AHB_CBCR_OFFSET 0x0034
Mihir Shete31396f42013-10-18 13:34:03 -0700105
106#define PRONTO_PMU_WLAN_AHB_CBCR_OFFSET 0x0074
107#define PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN BIT(0)
108#define PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF BIT(31)
109
110#define PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET 0x0120
111#define PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN BIT(1)
112
Mihir Shete2775e4652013-09-20 16:21:49 -0700113#define PRONTO_PMU_CFG_OFFSET 0x1004
114#define PRONTO_PMU_COM_CSR_OFFSET 0x1040
115#define PRONTO_PMU_SOFT_RESET_OFFSET 0x104C
116
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800117#define MSM_PRONTO_A2XB_BASE 0xfb100400
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800118#define A2XB_CFG_OFFSET 0x00
119#define A2XB_INT_SRC_OFFSET 0x0c
120#define A2XB_TSTBUS_CTRL_OFFSET 0x14
121#define A2XB_TSTBUS_OFFSET 0x18
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800122#define A2XB_ERR_INFO_OFFSET 0x1c
123
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800124#define WCNSS_TSTBUS_CTRL_EN BIT(0)
125#define WCNSS_TSTBUS_CTRL_AXIM (0x02 << 1)
126#define WCNSS_TSTBUS_CTRL_CMDFIFO (0x03 << 1)
127#define WCNSS_TSTBUS_CTRL_WRFIFO (0x04 << 1)
128#define WCNSS_TSTBUS_CTRL_RDFIFO (0x05 << 1)
129#define WCNSS_TSTBUS_CTRL_CTRL (0x07 << 1)
130#define WCNSS_TSTBUS_CTRL_AXIM_CFG0 (0x00 << 6)
131#define WCNSS_TSTBUS_CTRL_AXIM_CFG1 (0x01 << 6)
132#define WCNSS_TSTBUS_CTRL_CTRL_CFG0 (0x00 << 12)
133#define WCNSS_TSTBUS_CTRL_CTRL_CFG1 (0x01 << 12)
134
135#define MSM_PRONTO_CCPU_BASE 0xfb205050
136#define CCU_PRONTO_INVALID_ADDR_OFFSET 0x08
137#define CCU_PRONTO_LAST_ADDR0_OFFSET 0x0c
138#define CCU_PRONTO_LAST_ADDR1_OFFSET 0x10
139#define CCU_PRONTO_LAST_ADDR2_OFFSET 0x14
140
Sameer Thalappil767ff492013-06-19 15:25:21 -0700141#define MSM_PRONTO_SAW2_BASE 0xfb219000
142#define PRONTO_SAW2_SPM_STS_OFFSET 0x0c
143
Sameer Thalappil66d2b392013-07-02 11:32:55 -0700144#define MSM_PRONTO_PLL_BASE 0xfb21b1c0
145#define PRONTO_PLL_STATUS_OFFSET 0x1c
146
Yue Mae490b452013-11-12 00:07:33 -0800147#define MSM_PRONTO_TXP_STATUS 0xfb08040c
Sameer Thalappil670197c2013-07-19 16:31:14 -0700148#define MSM_PRONTO_TXP_PHY_ABORT 0xfb080488
149#define MSM_PRONTO_BRDG_ERR_SRC 0xfb080fb0
150
Sameer Thalappild0952f62013-05-03 10:18:29 -0700151#define WCNSS_DEF_WLAN_RX_BUFF_COUNT 1024
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700152#define WCNSS_VBATT_THRESHOLD 3500000
153#define WCNSS_VBATT_GUARD 200
154#define WCNSS_VBATT_HIGH 3700000
155#define WCNSS_VBATT_LOW 3300000
Sameer Thalappild0952f62013-05-03 10:18:29 -0700156
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700157#define WCNSS_CTRL_CHANNEL "WCNSS_CTRL"
Sameer Thalappil1878d762013-04-24 14:54:39 -0700158#define WCNSS_MAX_FRAME_SIZE (4*1024)
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700159#define WCNSS_VERSION_LEN 30
Sameer Thalappild821d632013-11-06 19:24:59 -0800160#define WCNSS_MAX_BUILD_VER_LEN 256
Sameer Thalappil13780d12013-12-19 17:04:21 -0800161#define WCNSS_MAX_CMD_LEN (128)
162#define WCNSS_MIN_CMD_LEN (3)
163#define WCNSS_MIN_SERIAL_LEN (6)
164
165/* control messages from userspace */
166#define WCNSS_USR_CTRL_MSG_START 0x00000000
167#define WCNSS_USR_SERIAL_NUM (WCNSS_USR_CTRL_MSG_START + 1)
168#define WCNSS_USR_HAS_CAL_DATA (WCNSS_USR_CTRL_MSG_START + 2)
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700169
170/* message types */
171#define WCNSS_CTRL_MSG_START 0x01000000
Sameer Thalappil1878d762013-04-24 14:54:39 -0700172#define WCNSS_VERSION_REQ (WCNSS_CTRL_MSG_START + 0)
173#define WCNSS_VERSION_RSP (WCNSS_CTRL_MSG_START + 1)
174#define WCNSS_NVBIN_DNLD_REQ (WCNSS_CTRL_MSG_START + 2)
175#define WCNSS_NVBIN_DNLD_RSP (WCNSS_CTRL_MSG_START + 3)
176#define WCNSS_CALDATA_UPLD_REQ (WCNSS_CTRL_MSG_START + 4)
177#define WCNSS_CALDATA_UPLD_RSP (WCNSS_CTRL_MSG_START + 5)
178#define WCNSS_CALDATA_DNLD_REQ (WCNSS_CTRL_MSG_START + 6)
179#define WCNSS_CALDATA_DNLD_RSP (WCNSS_CTRL_MSG_START + 7)
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700180#define WCNSS_VBATT_LEVEL_IND (WCNSS_CTRL_MSG_START + 8)
Sameer Thalappild821d632013-11-06 19:24:59 -0800181#define WCNSS_BUILD_VER_REQ (WCNSS_CTRL_MSG_START + 9)
182#define WCNSS_BUILD_VER_RSP (WCNSS_CTRL_MSG_START + 10)
Sameer Thalappilf106a682013-02-16 20:41:11 -0800183
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700184
185#define VALID_VERSION(version) \
186 ((strncmp(version, "INVALID", WCNSS_VERSION_LEN)) ? 1 : 0)
187
Sameer Thalappil1878d762013-04-24 14:54:39 -0700188#define FW_CALDATA_CAPABLE() \
189 ((penv->fw_major >= 1) && (penv->fw_minor >= 5) ? 1 : 0)
190
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700191struct smd_msg_hdr {
Sameer Thalappilf106a682013-02-16 20:41:11 -0800192 unsigned int msg_type;
193 unsigned int msg_len;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700194};
195
196struct wcnss_version {
197 struct smd_msg_hdr hdr;
198 unsigned char major;
199 unsigned char minor;
200 unsigned char version;
201 unsigned char revision;
202};
203
Naresh Jayaram08cee442013-04-26 19:50:00 +0530204struct wcnss_pmic_dump {
205 char reg_name[10];
206 u16 reg_addr;
207};
208
209static struct wcnss_pmic_dump wcnss_pmic_reg_dump[] = {
210 {"S2", 0x1D8},
211 {"L4", 0xB4},
212 {"L10", 0xC0},
213 {"LVS2", 0x62},
214 {"S4", 0x1E8},
215 {"LVS7", 0x06C},
216 {"LVS1", 0x060},
217};
218
Sameer Thalappilf106a682013-02-16 20:41:11 -0800219#define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
220
221/*
222 * On SMD channel 4K of maximum data can be transferred, including message
223 * header, so NV fragment size as next multiple of 1Kb is 3Kb.
224 */
225#define NV_FRAGMENT_SIZE 3072
Sameer Thalappil1878d762013-04-24 14:54:39 -0700226#define MAX_CALIBRATED_DATA_SIZE (64*1024)
227#define LAST_FRAGMENT (1 << 0)
228#define MESSAGE_TO_FOLLOW (1 << 1)
229#define CAN_RECEIVE_CALDATA (1 << 15)
230#define WCNSS_RESP_SUCCESS 1
231#define WCNSS_RESP_FAIL 0
232
Sameer Thalappilf106a682013-02-16 20:41:11 -0800233
234/* Macro to find the total number fragments of the NV bin Image */
235#define TOTALFRAGMENTS(x) (((x % NV_FRAGMENT_SIZE) == 0) ? \
236 (x / NV_FRAGMENT_SIZE) : ((x / NV_FRAGMENT_SIZE) + 1))
237
238struct nvbin_dnld_req_params {
239 /*
240 * Fragment sequence number of the NV bin Image. NV Bin Image
241 * might not fit into one message due to size limitation of
242 * the SMD channel FIFO so entire NV blob is chopped into
243 * multiple fragments starting with seqeunce number 0. The
244 * last fragment is indicated by marking is_last_fragment field
245 * to 1. At receiving side, NV blobs would be concatenated
246 * together without any padding bytes in between.
247 */
248 unsigned short frag_number;
249
250 /*
Sameer Thalappil1878d762013-04-24 14:54:39 -0700251 * bit 0: When set to 1 it indicates that no more fragments will
252 * be sent.
253 * bit 1: When set, a new message will be followed by this message
254 * bit 2- bit 14: Reserved
255 * bit 15: when set, it indicates that the sender is capable of
256 * receiving Calibrated data.
Sameer Thalappilf106a682013-02-16 20:41:11 -0800257 */
Sameer Thalappil1878d762013-04-24 14:54:39 -0700258 unsigned short msg_flags;
Sameer Thalappilf106a682013-02-16 20:41:11 -0800259
260 /* NV Image size (number of bytes) */
261 unsigned int nvbin_buffer_size;
262
263 /*
264 * Following the 'nvbin_buffer_size', there should be
265 * nvbin_buffer_size bytes of NV bin Image i.e.
266 * uint8[nvbin_buffer_size].
267 */
268};
269
Sameer Thalappil1878d762013-04-24 14:54:39 -0700270
Sameer Thalappilf106a682013-02-16 20:41:11 -0800271struct nvbin_dnld_req_msg {
272 /*
273 * Note: The length specified in nvbin_dnld_req_msg messages
274 * should be hdr.msg_len = sizeof(nvbin_dnld_req_msg) +
275 * nvbin_buffer_size.
276 */
277 struct smd_msg_hdr hdr;
278 struct nvbin_dnld_req_params dnld_req_params;
279};
280
Sameer Thalappil1878d762013-04-24 14:54:39 -0700281struct cal_data_params {
282
283 /* The total size of the calibrated data, including all the
284 * fragments.
285 */
286 unsigned int total_size;
287 unsigned short frag_number;
288 /*
289 * bit 0: When set to 1 it indicates that no more fragments will
290 * be sent.
291 * bit 1: When set, a new message will be followed by this message
292 * bit 2- bit 15: Reserved
293 */
294 unsigned short msg_flags;
295 /*
296 * fragment size
297 */
298 unsigned int frag_size;
299 /*
300 * Following the frag_size, frag_size of fragmented
301 * data will be followed.
302 */
303};
304
305struct cal_data_msg {
306 /*
307 * The length specified in cal_data_msg should be
308 * hdr.msg_len = sizeof(cal_data_msg) + frag_size
309 */
310 struct smd_msg_hdr hdr;
311 struct cal_data_params cal_params;
312};
313
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700314struct vbatt_level {
315 u32 curr_volt;
316 u32 threshold;
317};
318
319struct vbatt_message {
320 struct smd_msg_hdr hdr;
321 struct vbatt_level vbatt;
322};
323
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324static struct {
325 struct platform_device *pdev;
326 void *pil;
327 struct resource *mmio_res;
328 struct resource *tx_irq_res;
329 struct resource *rx_irq_res;
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -0700330 struct resource *gpios_5wire;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331 const struct dev_pm_ops *pm_ops;
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800332 int triggered;
333 int smd_channel_ready;
Sameer Thalappild0952f62013-05-03 10:18:29 -0700334 u32 wlan_rx_buff_count;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700335 smd_channel_t *smd_ch;
336 unsigned char wcnss_version[WCNSS_VERSION_LEN];
Sameer Thalappil1878d762013-04-24 14:54:39 -0700337 unsigned char fw_major;
338 unsigned char fw_minor;
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800339 unsigned int serial_number;
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800340 int thermal_mitigation;
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700341 enum wcnss_hw_type wcnss_hw_type;
Sameer Thalappila112bbc2012-05-23 12:20:32 -0700342 void (*tm_notify)(struct device *, int);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700343 struct wcnss_wlan_config wlan_config;
David Collinsb727f7d2011-09-12 11:54:49 -0700344 struct delayed_work wcnss_work;
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700345 struct delayed_work vbatt_work;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700346 struct work_struct wcnssctrl_version_work;
Sameer Thalappilf106a682013-02-16 20:41:11 -0800347 struct work_struct wcnssctrl_nvbin_dnld_work;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700348 struct work_struct wcnssctrl_rx_work;
Sameer Thalappilf138da52012-07-30 12:56:37 -0700349 struct wake_lock wcnss_wake_lock;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700350 void __iomem *msm_wcnss_base;
Sameer Thalappild3aad702013-01-22 18:52:24 -0800351 void __iomem *riva_ccu_base;
352 void __iomem *pronto_a2xb_base;
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800353 void __iomem *pronto_ccpu_base;
Sameer Thalappil767ff492013-06-19 15:25:21 -0700354 void __iomem *pronto_saw2_base;
Sameer Thalappil66d2b392013-07-02 11:32:55 -0700355 void __iomem *pronto_pll_base;
Yue Mae490b452013-11-12 00:07:33 -0800356 void __iomem *wlan_tx_status;
Sameer Thalappil670197c2013-07-19 16:31:14 -0700357 void __iomem *wlan_tx_phy_aborts;
358 void __iomem *wlan_brdg_err_source;
Sameer Thalappil58281ca2013-04-10 18:50:18 -0700359 void __iomem *fiq_reg;
Sameer Thalappil1878d762013-04-24 14:54:39 -0700360 int nv_downloaded;
361 unsigned char *fw_cal_data;
362 unsigned char *user_cal_data;
363 int fw_cal_rcvd;
364 int fw_cal_exp_frag;
365 int fw_cal_available;
366 int user_cal_read;
367 int user_cal_available;
Arif Hussain677c92d2013-11-01 19:09:11 -0700368 u32 user_cal_rcvd;
Sameer Thalappil1878d762013-04-24 14:54:39 -0700369 int user_cal_exp_size;
370 int device_opened;
Sameer Thalappil1d69b8022013-06-10 19:10:07 -0700371 int iris_xo_mode_set;
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700372 int fw_vbatt_state;
Sameer Thalappil13780d12013-12-19 17:04:21 -0800373 int ctrl_device_opened;
Sameer Thalappil1878d762013-04-24 14:54:39 -0700374 struct mutex dev_lock;
Sameer Thalappil13780d12013-12-19 17:04:21 -0800375 struct mutex ctrl_lock;
Sameer Thalappil1878d762013-04-24 14:54:39 -0700376 wait_queue_head_t read_wait;
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700377 struct qpnp_adc_tm_btm_param vbat_monitor_params;
378 struct qpnp_adc_tm_chip *adc_tm_dev;
379 struct mutex vbat_monitor_mutex;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380} *penv = NULL;
381
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800382static ssize_t wcnss_serial_number_show(struct device *dev,
383 struct device_attribute *attr, char *buf)
384{
385 if (!penv)
386 return -ENODEV;
387
388 return scnprintf(buf, PAGE_SIZE, "%08X\n", penv->serial_number);
389}
390
391static ssize_t wcnss_serial_number_store(struct device *dev,
Sameer Thalappilf138da52012-07-30 12:56:37 -0700392 struct device_attribute *attr, const char *buf, size_t count)
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800393{
394 unsigned int value;
395
396 if (!penv)
397 return -ENODEV;
398
399 if (sscanf(buf, "%08X", &value) != 1)
400 return -EINVAL;
401
402 penv->serial_number = value;
403 return count;
404}
405
406static DEVICE_ATTR(serial_number, S_IRUSR | S_IWUSR,
407 wcnss_serial_number_show, wcnss_serial_number_store);
408
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800409
410static ssize_t wcnss_thermal_mitigation_show(struct device *dev,
411 struct device_attribute *attr, char *buf)
412{
413 if (!penv)
414 return -ENODEV;
415
416 return scnprintf(buf, PAGE_SIZE, "%u\n", penv->thermal_mitigation);
417}
418
419static ssize_t wcnss_thermal_mitigation_store(struct device *dev,
Sameer Thalappilf138da52012-07-30 12:56:37 -0700420 struct device_attribute *attr, const char *buf, size_t count)
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800421{
422 int value;
423
424 if (!penv)
425 return -ENODEV;
426
427 if (sscanf(buf, "%d", &value) != 1)
428 return -EINVAL;
429 penv->thermal_mitigation = value;
Jeff Johnson61374652012-04-16 20:51:48 -0700430 if (penv->tm_notify)
Sameer Thalappila112bbc2012-05-23 12:20:32 -0700431 (penv->tm_notify)(dev, value);
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800432 return count;
433}
434
435static DEVICE_ATTR(thermal_mitigation, S_IRUSR | S_IWUSR,
436 wcnss_thermal_mitigation_show, wcnss_thermal_mitigation_store);
437
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700438
439static ssize_t wcnss_version_show(struct device *dev,
440 struct device_attribute *attr, char *buf)
441{
442 if (!penv)
443 return -ENODEV;
444
445 return scnprintf(buf, PAGE_SIZE, "%s", penv->wcnss_version);
446}
447
448static DEVICE_ATTR(wcnss_version, S_IRUSR,
449 wcnss_version_show, NULL);
450
Naresh Jayaram08cee442013-04-26 19:50:00 +0530451void wcnss_riva_dump_pmic_regs(void)
452{
453 int i, rc;
454 u8 val;
455
456 for (i = 0; i < ARRAY_SIZE(wcnss_pmic_reg_dump); i++) {
457 val = 0;
458 rc = pm8xxx_read_register(wcnss_pmic_reg_dump[i].reg_addr,
459 &val);
460 if (rc)
461 pr_err("PMIC READ: Failed to read addr = %d\n",
462 wcnss_pmic_reg_dump[i].reg_addr);
463 else
464 pr_info_ratelimited("PMIC READ: %s addr = %x, value = %x\n",
465 wcnss_pmic_reg_dump[i].reg_name,
466 wcnss_pmic_reg_dump[i].reg_addr, val);
467 }
468}
469
Sameer Thalappil77716e52012-11-08 13:41:04 -0800470/* wcnss_reset_intr() is invoked when host drivers fails to
471 * communicate with WCNSS over SMD; so logging these registers
Sameer Thalappild3aad702013-01-22 18:52:24 -0800472 * helps to know WCNSS failure reason
473 */
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800474void wcnss_riva_log_debug_regs(void)
Sameer Thalappil77716e52012-11-08 13:41:04 -0800475{
Sameer Thalappil77716e52012-11-08 13:41:04 -0800476 void __iomem *ccu_reg;
477 u32 reg = 0;
478
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800479 ccu_reg = penv->riva_ccu_base + CCU_RIVA_INVALID_ADDR_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800480 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800481 pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800482
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800483 ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR0_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800484 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800485 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800486
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800487 ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR1_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800488 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800489 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800490
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800491 ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR2_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800492 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800493 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
Naresh Jayaram08cee442013-04-26 19:50:00 +0530494 wcnss_riva_dump_pmic_regs();
Sameer Thalappil77716e52012-11-08 13:41:04 -0800495
Sameer Thalappil77716e52012-11-08 13:41:04 -0800496}
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800497EXPORT_SYMBOL(wcnss_riva_log_debug_regs);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800498
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800499/* Log pronto debug registers before sending reset interrupt */
500void wcnss_pronto_log_debug_regs(void)
501{
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800502 void __iomem *reg_addr, *tst_addr, *tst_ctrl_addr;
Mihir Shete31396f42013-10-18 13:34:03 -0700503 u32 reg = 0, reg2 = 0, reg3 = 0, reg4 = 0;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800504
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700505
506 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SPARE_OFFSET;
507 reg = readl_relaxed(reg_addr);
508 pr_info_ratelimited("%s: PRONTO_PMU_SPARE %08x\n", __func__, reg);
509
Mihir Shete2775e4652013-09-20 16:21:49 -0700510 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CPU_CBCR_OFFSET;
511 reg = readl_relaxed(reg_addr);
512 pr_info_ratelimited("%s: PRONTO_PMU_COM_CPU_CBCR %08x\n",
513 __func__, reg);
514
515 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_AHB_CBCR_OFFSET;
516 reg = readl_relaxed(reg_addr);
517 pr_info_ratelimited("%s: PRONTO_PMU_COM_AHB_CBCR %08x\n",
518 __func__, reg);
519
520 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CFG_OFFSET;
521 reg = readl_relaxed(reg_addr);
522 pr_info_ratelimited("%s: PRONTO_PMU_CFG %08x\n", __func__, reg);
523
524 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CSR_OFFSET;
525 reg = readl_relaxed(reg_addr);
526 pr_info_ratelimited("%s: PRONTO_PMU_COM_CSR %08x\n",
527 __func__, reg);
528
529 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SOFT_RESET_OFFSET;
530 reg = readl_relaxed(reg_addr);
531 pr_info_ratelimited("%s: PRONTO_PMU_SOFT_RESET %08x\n",
532 __func__, reg);
533
Yue Mae490b452013-11-12 00:07:33 -0800534 reg_addr = penv->pronto_saw2_base + PRONTO_SAW2_SPM_STS_OFFSET;
535 reg = readl_relaxed(reg_addr);
536 pr_info_ratelimited("%s: PRONTO_SAW2_SPM_STS %08x\n", __func__, reg);
537
Sameer Thalappil670197c2013-07-19 16:31:14 -0700538 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_GDSCR_OFFSET;
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700539 reg = readl_relaxed(reg_addr);
Mihir Shete2775e4652013-09-20 16:21:49 -0700540 pr_info_ratelimited("%s: PRONTO_PMU_COM_GDSCR %08x\n",
541 __func__, reg);
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700542 reg >>= 31;
543
544 if (!reg) {
545 pr_info_ratelimited("%s: Cannot log, Pronto common SS is power collapsed\n",
546 __func__);
547 return;
548 }
Sameer Thalappil670197c2013-07-19 16:31:14 -0700549 reg &= ~(PRONTO_PMU_COM_GDSCR_SW_COLLAPSE
550 | PRONTO_PMU_COM_GDSCR_HW_CTRL);
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700551 writel_relaxed(reg, reg_addr);
552
553 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CBCR_OFFSET;
554 reg = readl_relaxed(reg_addr);
555 reg |= PRONTO_PMU_CBCR_CLK_EN;
556 writel_relaxed(reg, reg_addr);
557
Sameer Thalappild3aad702013-01-22 18:52:24 -0800558 reg_addr = penv->pronto_a2xb_base + A2XB_CFG_OFFSET;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800559 reg = readl_relaxed(reg_addr);
560 pr_info_ratelimited("%s: A2XB_CFG_OFFSET %08x\n", __func__, reg);
561
Sameer Thalappild3aad702013-01-22 18:52:24 -0800562 reg_addr = penv->pronto_a2xb_base + A2XB_INT_SRC_OFFSET;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800563 reg = readl_relaxed(reg_addr);
564 pr_info_ratelimited("%s: A2XB_INT_SRC_OFFSET %08x\n", __func__, reg);
565
Sameer Thalappild3aad702013-01-22 18:52:24 -0800566 reg_addr = penv->pronto_a2xb_base + A2XB_ERR_INFO_OFFSET;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800567 reg = readl_relaxed(reg_addr);
568 pr_info_ratelimited("%s: A2XB_ERR_INFO_OFFSET %08x\n", __func__, reg);
569
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800570 reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_INVALID_ADDR_OFFSET;
571 reg = readl_relaxed(reg_addr);
572 pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
573
574 reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR0_OFFSET;
575 reg = readl_relaxed(reg_addr);
576 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
577
578 reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR1_OFFSET;
579 reg = readl_relaxed(reg_addr);
580 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
581
582 reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR2_OFFSET;
583 reg = readl_relaxed(reg_addr);
584 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
585
Sameer Thalappil66d2b392013-07-02 11:32:55 -0700586 reg_addr = penv->pronto_pll_base + PRONTO_PLL_STATUS_OFFSET;
587 reg = readl_relaxed(reg_addr);
588 pr_info_ratelimited("%s: PRONTO_PLL_STATUS %08x\n", __func__, reg);
589
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800590 tst_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_OFFSET;
591 tst_ctrl_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_CTRL_OFFSET;
592
593 /* read data FIFO */
594 reg = 0;
595 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_RDFIFO;
596 writel_relaxed(reg, tst_ctrl_addr);
597 reg = readl_relaxed(tst_addr);
598 pr_info_ratelimited("%s: Read data FIFO testbus %08x\n",
599 __func__, reg);
600
601 /* command FIFO */
602 reg = 0;
603 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CMDFIFO;
604 writel_relaxed(reg, tst_ctrl_addr);
605 reg = readl_relaxed(tst_addr);
606 pr_info_ratelimited("%s: Command FIFO testbus %08x\n",
607 __func__, reg);
608
609 /* write data FIFO */
610 reg = 0;
611 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_WRFIFO;
612 writel_relaxed(reg, tst_ctrl_addr);
613 reg = readl_relaxed(tst_addr);
614 pr_info_ratelimited("%s: Rrite data FIFO testbus %08x\n",
615 __func__, reg);
616
617 /* AXIM SEL CFG0 */
618 reg = 0;
619 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_AXIM |
620 WCNSS_TSTBUS_CTRL_AXIM_CFG0;
621 writel_relaxed(reg, tst_ctrl_addr);
622 reg = readl_relaxed(tst_addr);
623 pr_info_ratelimited("%s: AXIM SEL CFG0 testbus %08x\n",
624 __func__, reg);
625
626 /* AXIM SEL CFG1 */
627 reg = 0;
628 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_AXIM |
629 WCNSS_TSTBUS_CTRL_AXIM_CFG1;
630 writel_relaxed(reg, tst_ctrl_addr);
631 reg = readl_relaxed(tst_addr);
632 pr_info_ratelimited("%s: AXIM SEL CFG1 testbus %08x\n",
633 __func__, reg);
634
635 /* CTRL SEL CFG0 */
636 reg = 0;
637 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CTRL |
638 WCNSS_TSTBUS_CTRL_CTRL_CFG0;
639 writel_relaxed(reg, tst_ctrl_addr);
640 reg = readl_relaxed(tst_addr);
641 pr_info_ratelimited("%s: CTRL SEL CFG0 testbus %08x\n",
642 __func__, reg);
643
644 /* CTRL SEL CFG1 */
645 reg = 0;
646 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CTRL |
647 WCNSS_TSTBUS_CTRL_CTRL_CFG1;
648 writel_relaxed(reg, tst_ctrl_addr);
649 reg = readl_relaxed(tst_addr);
650 pr_info_ratelimited("%s: CTRL SEL CFG1 testbus %08x\n", __func__, reg);
651
Sameer Thalappil670197c2013-07-19 16:31:14 -0700652
653 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_BCR_OFFSET;
654 reg = readl_relaxed(reg_addr);
655
656 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_GDSCR_OFFSET;
657 reg2 = readl_relaxed(reg_addr);
658
Mihir Shete31396f42013-10-18 13:34:03 -0700659 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_AHB_CBCR_OFFSET;
660 reg3 = readl_relaxed(reg_addr);
661 pr_info_ratelimited("%s: PMU_WLAN_AHB_CBCR %08x\n", __func__, reg3);
662
663 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET;
664 reg4 = readl_relaxed(reg_addr);
665 pr_info_ratelimited("%s: PMU_CPU_CMD_RCGR %08x\n", __func__, reg4);
666
Sameer Thalappil670197c2013-07-19 16:31:14 -0700667 if ((reg & PRONTO_PMU_WLAN_BCR_BLK_ARES) ||
Mihir Shete31396f42013-10-18 13:34:03 -0700668 (reg2 & PRONTO_PMU_WLAN_GDSCR_SW_COLLAPSE) ||
669 (!(reg4 & PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN)) ||
670 (reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF) ||
671 (!(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN))) {
Sameer Thalappil670197c2013-07-19 16:31:14 -0700672 pr_info_ratelimited("%s: Cannot log, wlan domain is power collapsed\n",
673 __func__);
674 return;
675 }
676
677 reg = readl_relaxed(penv->wlan_tx_phy_aborts);
678 pr_info_ratelimited("%s: WLAN_TX_PHY_ABORTS %08x\n", __func__, reg);
679
680 reg = readl_relaxed(penv->wlan_brdg_err_source);
681 pr_info_ratelimited("%s: WLAN_BRDG_ERR_SOURCE %08x\n", __func__, reg);
682
Yue Mae490b452013-11-12 00:07:33 -0800683 reg = readl_relaxed(penv->wlan_tx_status);
684 pr_info_ratelimited("%s: WLAN_TX_STATUS %08x\n", __func__, reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800685}
686EXPORT_SYMBOL(wcnss_pronto_log_debug_regs);
687
Sameer Thalappil36f7a1a2013-10-09 18:32:38 -0700688#ifdef CONFIG_WCNSS_REGISTER_DUMP_ON_BITE
689void wcnss_log_debug_regs_on_bite(void)
690{
691 if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
692 wcnss_pronto_log_debug_regs();
693}
694#endif
695
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800696/* interface to reset wcnss by sending the reset interrupt */
Sameer Thalappil19e02352012-09-24 15:26:12 -0700697void wcnss_reset_intr(void)
698{
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800699 if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
700 wcnss_pronto_log_debug_regs();
Sameer Thalappil34920762013-03-21 15:43:28 -0700701 wmb();
Sameer Thalappil58281ca2013-04-10 18:50:18 -0700702 __raw_writel(1 << 16, penv->fiq_reg);
Sameer Thalappil34920762013-03-21 15:43:28 -0700703 } else {
704 wcnss_riva_log_debug_regs();
705 wmb();
706 __raw_writel(1 << 24, MSM_APCS_GCC_BASE + 0x8);
Sameer Thalappil19e02352012-09-24 15:26:12 -0700707 }
708}
709EXPORT_SYMBOL(wcnss_reset_intr);
710
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800711static int wcnss_create_sysfs(struct device *dev)
712{
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800713 int ret;
714
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800715 if (!dev)
716 return -ENODEV;
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800717
718 ret = device_create_file(dev, &dev_attr_serial_number);
719 if (ret)
720 return ret;
721
722 ret = device_create_file(dev, &dev_attr_thermal_mitigation);
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700723 if (ret)
724 goto remove_serial;
725
726 ret = device_create_file(dev, &dev_attr_wcnss_version);
727 if (ret)
728 goto remove_thermal;
729
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800730 return 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700731
732remove_thermal:
733 device_remove_file(dev, &dev_attr_thermal_mitigation);
734remove_serial:
735 device_remove_file(dev, &dev_attr_serial_number);
736
737 return ret;
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800738}
739
740static void wcnss_remove_sysfs(struct device *dev)
741{
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800742 if (dev) {
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800743 device_remove_file(dev, &dev_attr_serial_number);
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800744 device_remove_file(dev, &dev_attr_thermal_mitigation);
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700745 device_remove_file(dev, &dev_attr_wcnss_version);
746 }
747}
748static void wcnss_smd_notify_event(void *data, unsigned int event)
749{
750 int len = 0;
751
752 if (penv != data) {
753 pr_err("wcnss: invalid env pointer in smd callback\n");
754 return;
755 }
756 switch (event) {
757 case SMD_EVENT_DATA:
758 len = smd_read_avail(penv->smd_ch);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700759 if (len < 0) {
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700760 pr_err("wcnss: failed to read from smd %d\n", len);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700761 return;
762 }
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700763 schedule_work(&penv->wcnssctrl_rx_work);
764 break;
765
766 case SMD_EVENT_OPEN:
767 pr_debug("wcnss: opening WCNSS SMD channel :%s",
768 WCNSS_CTRL_CHANNEL);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700769 schedule_work(&penv->wcnssctrl_version_work);
770
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700771 break;
772
773 case SMD_EVENT_CLOSE:
774 pr_debug("wcnss: closing WCNSS SMD channel :%s",
775 WCNSS_CTRL_CHANNEL);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700776 penv->nv_downloaded = 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700777 break;
778
779 default:
780 break;
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800781 }
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800782}
783
David Collinsb727f7d2011-09-12 11:54:49 -0700784static void wcnss_post_bootup(struct work_struct *work)
785{
Sheng Fangbb421672013-03-19 08:27:28 +0800786 if (do_not_cancel_vote == 1) {
787 pr_info("%s: Keeping APPS vote for Iris & WCNSS\n", __func__);
788 return;
789 }
790
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700791 pr_info("%s: Cancel APPS vote for Iris & WCNSS\n", __func__);
David Collinsb727f7d2011-09-12 11:54:49 -0700792
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700793 /* Since WCNSS is up, cancel any APPS vote for Iris & WCNSS VREGs */
David Collinsb727f7d2011-09-12 11:54:49 -0700794 wcnss_wlan_power(&penv->pdev->dev, &penv->wlan_config,
Sameer Thalappil1d69b8022013-06-10 19:10:07 -0700795 WCNSS_WLAN_SWITCH_OFF, NULL);
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700796
797}
798
799static int
800wcnss_pronto_gpios_config(struct device *dev, bool enable)
801{
802 int rc = 0;
803 int i, j;
804 int WCNSS_WLAN_NUM_GPIOS = 5;
805
806 for (i = 0; i < WCNSS_WLAN_NUM_GPIOS; i++) {
807 int gpio = of_get_gpio(dev->of_node, i);
808 if (enable) {
809 rc = gpio_request(gpio, "wcnss_wlan");
810 if (rc) {
811 pr_err("WCNSS gpio_request %d err %d\n",
812 gpio, rc);
813 goto fail;
814 }
815 } else
816 gpio_free(gpio);
817 }
818
819 return rc;
820
821fail:
822 for (j = WCNSS_WLAN_NUM_GPIOS-1; j >= 0; j--) {
823 int gpio = of_get_gpio(dev->of_node, i);
824 gpio_free(gpio);
825 }
826 return rc;
David Collinsb727f7d2011-09-12 11:54:49 -0700827}
828
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -0700829static int
830wcnss_gpios_config(struct resource *gpios_5wire, bool enable)
831{
832 int i, j;
833 int rc = 0;
834
835 for (i = gpios_5wire->start; i <= gpios_5wire->end; i++) {
836 if (enable) {
837 rc = gpio_request(i, gpios_5wire->name);
838 if (rc) {
839 pr_err("WCNSS gpio_request %d err %d\n", i, rc);
840 goto fail;
841 }
842 } else
843 gpio_free(i);
844 }
845
846 return rc;
847
848fail:
849 for (j = i-1; j >= gpios_5wire->start; j--)
850 gpio_free(j);
851 return rc;
852}
853
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700854static int __devinit
855wcnss_wlan_ctrl_probe(struct platform_device *pdev)
856{
Sameer Thalappil0ab82132013-06-10 10:10:05 -0700857 if (!penv || !penv->triggered)
Sameer Thalappil667f43f2011-10-10 11:12:54 -0700858 return -ENODEV;
859
860 penv->smd_channel_ready = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700861
862 pr_info("%s: SMD ctrl channel up\n", __func__);
863
David Collinsb727f7d2011-09-12 11:54:49 -0700864 /* Schedule a work to do any post boot up activity */
865 INIT_DELAYED_WORK(&penv->wcnss_work, wcnss_post_bootup);
866 schedule_delayed_work(&penv->wcnss_work, msecs_to_jiffies(10000));
867
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700868 return 0;
869}
870
Sameer Thalappil13f45182012-07-23 18:02:45 -0700871void wcnss_flush_delayed_boot_votes()
872{
Sameer Thalappilf106a682013-02-16 20:41:11 -0800873 flush_delayed_work(&penv->wcnss_work);
Sameer Thalappil13f45182012-07-23 18:02:45 -0700874}
875EXPORT_SYMBOL(wcnss_flush_delayed_boot_votes);
876
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700877static int __devexit
878wcnss_wlan_ctrl_remove(struct platform_device *pdev)
879{
880 if (penv)
881 penv->smd_channel_ready = 0;
882
883 pr_info("%s: SMD ctrl channel down\n", __func__);
884
885 return 0;
886}
887
888
889static struct platform_driver wcnss_wlan_ctrl_driver = {
890 .driver = {
891 .name = "WLAN_CTRL",
892 .owner = THIS_MODULE,
893 },
894 .probe = wcnss_wlan_ctrl_probe,
895 .remove = __devexit_p(wcnss_wlan_ctrl_remove),
896};
897
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700898static int __devexit
899wcnss_ctrl_remove(struct platform_device *pdev)
900{
901 if (penv && penv->smd_ch)
902 smd_close(penv->smd_ch);
903
904 return 0;
905}
906
907static int __devinit
908wcnss_ctrl_probe(struct platform_device *pdev)
909{
910 int ret = 0;
911
Sameer Thalappil0ab82132013-06-10 10:10:05 -0700912 if (!penv || !penv->triggered)
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700913 return -ENODEV;
914
915 ret = smd_named_open_on_edge(WCNSS_CTRL_CHANNEL, SMD_APPS_WCNSS,
916 &penv->smd_ch, penv, wcnss_smd_notify_event);
917 if (ret < 0) {
918 pr_err("wcnss: cannot open the smd command channel %s: %d\n",
919 WCNSS_CTRL_CHANNEL, ret);
920 return -ENODEV;
921 }
922 smd_disable_read_intr(penv->smd_ch);
923
924 return 0;
925}
926
927/* platform device for WCNSS_CTRL SMD channel */
928static struct platform_driver wcnss_ctrl_driver = {
929 .driver = {
930 .name = "WCNSS_CTRL",
931 .owner = THIS_MODULE,
932 },
933 .probe = wcnss_ctrl_probe,
934 .remove = __devexit_p(wcnss_ctrl_remove),
935};
936
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700937struct device *wcnss_wlan_get_device(void)
938{
939 if (penv && penv->pdev && penv->smd_channel_ready)
940 return &penv->pdev->dev;
941 return NULL;
942}
943EXPORT_SYMBOL(wcnss_wlan_get_device);
944
Sameer Thalappil409ed352011-12-07 10:53:31 -0800945struct platform_device *wcnss_get_platform_device(void)
946{
947 if (penv && penv->pdev)
948 return penv->pdev;
949 return NULL;
950}
951EXPORT_SYMBOL(wcnss_get_platform_device);
952
953struct wcnss_wlan_config *wcnss_get_wlan_config(void)
954{
955 if (penv && penv->pdev)
956 return &penv->wlan_config;
957 return NULL;
958}
959EXPORT_SYMBOL(wcnss_get_wlan_config);
960
Sameer Thalappil1878d762013-04-24 14:54:39 -0700961int wcnss_device_ready(void)
962{
963 if (penv && penv->pdev && penv->nv_downloaded)
964 return 1;
965 return 0;
966}
967EXPORT_SYMBOL(wcnss_device_ready);
968
969
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700970struct resource *wcnss_wlan_get_memory_map(struct device *dev)
971{
972 if (penv && dev && (dev == &penv->pdev->dev) && penv->smd_channel_ready)
973 return penv->mmio_res;
974 return NULL;
975}
976EXPORT_SYMBOL(wcnss_wlan_get_memory_map);
977
978int wcnss_wlan_get_dxe_tx_irq(struct device *dev)
979{
980 if (penv && dev && (dev == &penv->pdev->dev) &&
981 penv->tx_irq_res && penv->smd_channel_ready)
982 return penv->tx_irq_res->start;
983 return WCNSS_WLAN_IRQ_INVALID;
984}
985EXPORT_SYMBOL(wcnss_wlan_get_dxe_tx_irq);
986
987int wcnss_wlan_get_dxe_rx_irq(struct device *dev)
988{
989 if (penv && dev && (dev == &penv->pdev->dev) &&
990 penv->rx_irq_res && penv->smd_channel_ready)
991 return penv->rx_irq_res->start;
992 return WCNSS_WLAN_IRQ_INVALID;
993}
994EXPORT_SYMBOL(wcnss_wlan_get_dxe_rx_irq);
995
996void wcnss_wlan_register_pm_ops(struct device *dev,
997 const struct dev_pm_ops *pm_ops)
998{
999 if (penv && dev && (dev == &penv->pdev->dev) && pm_ops)
1000 penv->pm_ops = pm_ops;
1001}
1002EXPORT_SYMBOL(wcnss_wlan_register_pm_ops);
1003
Sameer Thalappilecdd1002011-09-09 10:52:27 -07001004void wcnss_wlan_unregister_pm_ops(struct device *dev,
1005 const struct dev_pm_ops *pm_ops)
1006{
1007 if (penv && dev && (dev == &penv->pdev->dev) && pm_ops) {
1008 if (pm_ops->suspend != penv->pm_ops->suspend ||
1009 pm_ops->resume != penv->pm_ops->resume)
1010 pr_err("PM APIs dont match with registered APIs\n");
1011 penv->pm_ops = NULL;
1012 }
1013}
1014EXPORT_SYMBOL(wcnss_wlan_unregister_pm_ops);
1015
Sameer Thalappila112bbc2012-05-23 12:20:32 -07001016void wcnss_register_thermal_mitigation(struct device *dev,
1017 void (*tm_notify)(struct device *, int))
Jeff Johnson61374652012-04-16 20:51:48 -07001018{
Sameer Thalappila112bbc2012-05-23 12:20:32 -07001019 if (penv && dev && tm_notify)
Jeff Johnson61374652012-04-16 20:51:48 -07001020 penv->tm_notify = tm_notify;
1021}
1022EXPORT_SYMBOL(wcnss_register_thermal_mitigation);
1023
Sameer Thalappila112bbc2012-05-23 12:20:32 -07001024void wcnss_unregister_thermal_mitigation(
1025 void (*tm_notify)(struct device *, int))
Jeff Johnson61374652012-04-16 20:51:48 -07001026{
1027 if (penv && tm_notify) {
1028 if (tm_notify != penv->tm_notify)
1029 pr_err("tm_notify doesn't match registered\n");
1030 penv->tm_notify = NULL;
1031 }
1032}
1033EXPORT_SYMBOL(wcnss_unregister_thermal_mitigation);
1034
Jeff Johnson8b1f6d02012-02-03 20:43:26 -08001035unsigned int wcnss_get_serial_number(void)
1036{
1037 if (penv)
1038 return penv->serial_number;
1039 return 0;
1040}
1041EXPORT_SYMBOL(wcnss_get_serial_number);
1042
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001043static int enable_wcnss_suspend_notify;
1044
1045static int enable_wcnss_suspend_notify_set(const char *val,
1046 struct kernel_param *kp)
1047{
1048 int ret;
1049
1050 ret = param_set_int(val, kp);
1051 if (ret)
1052 return ret;
1053
1054 if (enable_wcnss_suspend_notify)
1055 pr_debug("Suspend notification activated for wcnss\n");
1056
1057 return 0;
1058}
1059module_param_call(enable_wcnss_suspend_notify, enable_wcnss_suspend_notify_set,
1060 param_get_int, &enable_wcnss_suspend_notify, S_IRUGO | S_IWUSR);
1061
Sameer Thalappil58cec312013-05-13 15:52:08 -07001062int wcnss_xo_auto_detect_enabled(void)
1063{
1064 return (has_autodetect_xo == 1 ? 1 : 0);
1065}
1066
Sameer Thalappil1d69b8022013-06-10 19:10:07 -07001067int wcnss_wlan_iris_xo_mode(void)
1068{
1069 if (penv && penv->pdev && penv->smd_channel_ready)
1070 return penv->iris_xo_mode_set;
1071 return -ENODEV;
1072}
1073EXPORT_SYMBOL(wcnss_wlan_iris_xo_mode);
1074
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001075
1076void wcnss_suspend_notify(void)
1077{
1078 void __iomem *pmu_spare_reg;
1079 u32 reg = 0;
1080 unsigned long flags;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001081
1082 if (!enable_wcnss_suspend_notify)
1083 return;
1084
1085 if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
1086 return;
1087
1088 /* For Riva */
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001089 pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
1090 spin_lock_irqsave(&reg_spinlock, flags);
1091 reg = readl_relaxed(pmu_spare_reg);
1092 reg |= RIVA_SUSPEND_BIT;
1093 writel_relaxed(reg, pmu_spare_reg);
1094 spin_unlock_irqrestore(&reg_spinlock, flags);
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001095}
1096EXPORT_SYMBOL(wcnss_suspend_notify);
1097
1098void wcnss_resume_notify(void)
1099{
1100 void __iomem *pmu_spare_reg;
1101 u32 reg = 0;
1102 unsigned long flags;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001103
1104 if (!enable_wcnss_suspend_notify)
1105 return;
1106
1107 if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
1108 return;
1109
1110 /* For Riva */
1111 pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
1112
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001113 spin_lock_irqsave(&reg_spinlock, flags);
1114 reg = readl_relaxed(pmu_spare_reg);
1115 reg &= ~RIVA_SUSPEND_BIT;
1116 writel_relaxed(reg, pmu_spare_reg);
1117 spin_unlock_irqrestore(&reg_spinlock, flags);
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001118}
1119EXPORT_SYMBOL(wcnss_resume_notify);
1120
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001121static int wcnss_wlan_suspend(struct device *dev)
1122{
1123 if (penv && dev && (dev == &penv->pdev->dev) &&
1124 penv->smd_channel_ready &&
1125 penv->pm_ops && penv->pm_ops->suspend)
1126 return penv->pm_ops->suspend(dev);
1127 return 0;
1128}
1129
1130static int wcnss_wlan_resume(struct device *dev)
1131{
1132 if (penv && dev && (dev == &penv->pdev->dev) &&
1133 penv->smd_channel_ready &&
1134 penv->pm_ops && penv->pm_ops->resume)
1135 return penv->pm_ops->resume(dev);
1136 return 0;
1137}
1138
Sameer Thalappilf138da52012-07-30 12:56:37 -07001139void wcnss_prevent_suspend()
1140{
1141 if (penv)
1142 wake_lock(&penv->wcnss_wake_lock);
1143}
1144EXPORT_SYMBOL(wcnss_prevent_suspend);
1145
1146void wcnss_allow_suspend()
1147{
1148 if (penv)
1149 wake_unlock(&penv->wcnss_wake_lock);
1150}
1151EXPORT_SYMBOL(wcnss_allow_suspend);
1152
Sameer Thalappil8d686d42012-08-24 10:07:31 -07001153int wcnss_hardware_type(void)
1154{
1155 if (penv)
1156 return penv->wcnss_hw_type;
1157 else
1158 return -ENODEV;
1159}
1160EXPORT_SYMBOL(wcnss_hardware_type);
1161
Sameer Thalappil1878d762013-04-24 14:54:39 -07001162int fw_cal_data_available(void)
Sameer Thalappilcf566bb2013-02-26 17:10:25 -08001163{
1164 if (penv)
Sameer Thalappil1878d762013-04-24 14:54:39 -07001165 return penv->fw_cal_available;
Sameer Thalappilcf566bb2013-02-26 17:10:25 -08001166 else
1167 return -ENODEV;
1168}
Sameer Thalappilcf566bb2013-02-26 17:10:25 -08001169
Sameer Thalappild0952f62013-05-03 10:18:29 -07001170u32 wcnss_get_wlan_rx_buff_count(void)
1171{
1172 if (penv)
1173 return penv->wlan_rx_buff_count;
1174 else
1175 return WCNSS_DEF_WLAN_RX_BUFF_COUNT;
1176
1177}
1178EXPORT_SYMBOL(wcnss_get_wlan_rx_buff_count);
1179
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001180static int wcnss_smd_tx(void *data, int len)
1181{
1182 int ret = 0;
1183
1184 ret = smd_write_avail(penv->smd_ch);
1185 if (ret < len) {
1186 pr_err("wcnss: no space available for smd frame\n");
Sameer Thalappilf106a682013-02-16 20:41:11 -08001187 return -ENOSPC;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001188 }
1189 ret = smd_write(penv->smd_ch, data, len);
1190 if (ret < len) {
1191 pr_err("wcnss: failed to write Command %d", len);
1192 ret = -ENODEV;
1193 }
1194 return ret;
1195}
1196
Sameer Thalappilf6fb1892013-09-06 17:33:30 -07001197static void wcnss_notify_vbat(enum qpnp_tm_state state, void *ctx)
1198{
1199 mutex_lock(&penv->vbat_monitor_mutex);
1200 cancel_delayed_work_sync(&penv->vbatt_work);
1201
1202 if (state == ADC_TM_LOW_STATE) {
1203 pr_debug("wcnss: low voltage notification triggered\n");
1204 penv->vbat_monitor_params.state_request =
1205 ADC_TM_HIGH_THR_ENABLE;
1206 penv->vbat_monitor_params.high_thr = WCNSS_VBATT_THRESHOLD +
1207 WCNSS_VBATT_GUARD;
1208 penv->vbat_monitor_params.low_thr = 0;
1209 } else if (state == ADC_TM_HIGH_STATE) {
1210 penv->vbat_monitor_params.state_request =
1211 ADC_TM_LOW_THR_ENABLE;
1212 penv->vbat_monitor_params.low_thr = WCNSS_VBATT_THRESHOLD -
1213 WCNSS_VBATT_GUARD;
1214 penv->vbat_monitor_params.high_thr = 0;
1215 pr_debug("wcnss: high voltage notification triggered\n");
1216 } else {
1217 pr_debug("wcnss: unknown voltage notification state: %d\n",
1218 state);
1219 mutex_unlock(&penv->vbat_monitor_mutex);
1220 return;
1221 }
1222 pr_debug("wcnss: set low thr to %d and high to %d\n",
1223 penv->vbat_monitor_params.low_thr,
1224 penv->vbat_monitor_params.high_thr);
1225
1226 qpnp_adc_tm_channel_measure(penv->adc_tm_dev,
1227 &penv->vbat_monitor_params);
1228 schedule_delayed_work(&penv->vbatt_work, msecs_to_jiffies(2000));
1229 mutex_unlock(&penv->vbat_monitor_mutex);
1230}
1231
1232static int wcnss_setup_vbat_monitoring(void)
1233{
1234 int rc = -1;
1235
1236 if (!penv->adc_tm_dev) {
1237 pr_err("wcnss: not setting up vbatt\n");
1238 return rc;
1239 }
1240 penv->vbat_monitor_params.low_thr = WCNSS_VBATT_THRESHOLD;
1241 penv->vbat_monitor_params.high_thr = WCNSS_VBATT_THRESHOLD;
1242 penv->vbat_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
1243 penv->vbat_monitor_params.channel = VBAT_SNS;
1244 penv->vbat_monitor_params.btm_ctx = (void *)penv;
1245 penv->vbat_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
1246 penv->vbat_monitor_params.threshold_notification = &wcnss_notify_vbat;
1247 pr_debug("wcnss: set low thr to %d and high to %d\n",
1248 penv->vbat_monitor_params.low_thr,
1249 penv->vbat_monitor_params.high_thr);
1250
1251 rc = qpnp_adc_tm_channel_measure(penv->adc_tm_dev,
1252 &penv->vbat_monitor_params);
1253 if (rc)
1254 pr_err("wcnss: tm setup failed: %d\n", rc);
1255
1256 return rc;
1257}
1258
1259static void wcnss_update_vbatt(struct work_struct *work)
1260{
1261 struct vbatt_message vbatt_msg;
1262 int ret = 0;
1263
1264 vbatt_msg.hdr.msg_type = WCNSS_VBATT_LEVEL_IND;
1265 vbatt_msg.hdr.msg_len = sizeof(struct vbatt_message);
1266 vbatt_msg.vbatt.threshold = WCNSS_VBATT_THRESHOLD;
1267
1268 mutex_lock(&penv->vbat_monitor_mutex);
1269 if (penv->vbat_monitor_params.low_thr &&
1270 (penv->fw_vbatt_state == WCNSS_VBATT_LOW ||
1271 penv->fw_vbatt_state == WCNSS_CONFIG_UNSPECIFIED)) {
1272 vbatt_msg.vbatt.curr_volt = WCNSS_VBATT_HIGH;
1273 penv->fw_vbatt_state = WCNSS_VBATT_HIGH;
1274 pr_debug("wcnss: send HIGH BATT to FW\n");
1275 } else if (!penv->vbat_monitor_params.low_thr &&
1276 (penv->fw_vbatt_state == WCNSS_VBATT_HIGH ||
1277 penv->fw_vbatt_state == WCNSS_CONFIG_UNSPECIFIED)){
1278 vbatt_msg.vbatt.curr_volt = WCNSS_VBATT_LOW;
1279 penv->fw_vbatt_state = WCNSS_VBATT_LOW;
1280 pr_debug("wcnss: send LOW BATT to FW\n");
1281 } else {
1282 mutex_unlock(&penv->vbat_monitor_mutex);
1283 return;
1284 }
1285 mutex_unlock(&penv->vbat_monitor_mutex);
1286 ret = wcnss_smd_tx(&vbatt_msg, vbatt_msg.hdr.msg_len);
1287 if (ret < 0)
1288 pr_err("wcnss: smd tx failed\n");
1289 return;
1290}
1291
1292
Sameer Thalappil1878d762013-04-24 14:54:39 -07001293static unsigned char wcnss_fw_status(void)
1294{
1295 int len = 0;
1296 int rc = 0;
1297
1298 unsigned char fw_status = 0xFF;
1299
1300 len = smd_read_avail(penv->smd_ch);
1301 if (len < 1) {
1302 pr_err("%s: invalid firmware status", __func__);
1303 return fw_status;
1304 }
1305
1306 rc = smd_read(penv->smd_ch, &fw_status, 1);
1307 if (rc < 0) {
1308 pr_err("%s: incomplete data read from smd\n", __func__);
1309 return fw_status;
1310 }
1311 return fw_status;
1312}
1313
1314static void wcnss_send_cal_rsp(unsigned char fw_status)
1315{
1316 struct smd_msg_hdr *rsphdr;
1317 unsigned char *msg = NULL;
1318 int rc;
1319
1320 msg = kmalloc((sizeof(struct smd_msg_hdr) + 1), GFP_KERNEL);
1321 if (NULL == msg) {
1322 pr_err("wcnss: %s: failed to get memory\n", __func__);
1323 return;
1324 }
1325
1326 rsphdr = (struct smd_msg_hdr *)msg;
1327 rsphdr->msg_type = WCNSS_CALDATA_UPLD_RSP;
1328 rsphdr->msg_len = sizeof(struct smd_msg_hdr) + 1;
1329 memcpy(msg+sizeof(struct smd_msg_hdr), &fw_status, 1);
1330
1331 rc = wcnss_smd_tx(msg, rsphdr->msg_len);
1332 if (rc < 0)
1333 pr_err("wcnss: smd tx failed\n");
Sameer Thalappil844d3602013-05-24 13:54:24 -07001334
1335 kfree(msg);
Sameer Thalappil1878d762013-04-24 14:54:39 -07001336}
1337
1338/* Collect calibrated data from WCNSS */
1339void extract_cal_data(int len)
1340{
1341 int rc;
1342 struct cal_data_params calhdr;
1343 unsigned char fw_status = WCNSS_RESP_FAIL;
1344
1345 if (len < sizeof(struct cal_data_params)) {
1346 pr_err("wcnss: incomplete cal header length\n");
1347 return;
1348 }
1349
1350 rc = smd_read(penv->smd_ch, (unsigned char *)&calhdr,
1351 sizeof(struct cal_data_params));
1352 if (rc < sizeof(struct cal_data_params)) {
1353 pr_err("wcnss: incomplete cal header read from smd\n");
1354 return;
1355 }
1356
1357 if (penv->fw_cal_exp_frag != calhdr.frag_number) {
1358 pr_err("wcnss: Invalid frgament");
1359 goto exit;
1360 }
1361
1362 if (calhdr.frag_size > WCNSS_MAX_FRAME_SIZE) {
1363 pr_err("wcnss: Invalid fragment size");
1364 goto exit;
1365 }
1366
Sameer Thalappila5e70ae2013-10-22 13:18:20 -07001367 if (penv->fw_cal_available) {
1368 /* ignore cal upload from SSR */
1369 smd_read(penv->smd_ch, NULL, calhdr.frag_size);
1370 penv->fw_cal_exp_frag++;
1371 if (calhdr.msg_flags & LAST_FRAGMENT) {
1372 penv->fw_cal_exp_frag = 0;
1373 goto exit;
1374 }
1375 return;
1376 }
1377
Sameer Thalappil1878d762013-04-24 14:54:39 -07001378 if (0 == calhdr.frag_number) {
1379 if (calhdr.total_size > MAX_CALIBRATED_DATA_SIZE) {
1380 pr_err("wcnss: Invalid cal data size %d",
1381 calhdr.total_size);
1382 goto exit;
1383 }
1384 kfree(penv->fw_cal_data);
1385 penv->fw_cal_rcvd = 0;
1386 penv->fw_cal_data = kmalloc(calhdr.total_size,
1387 GFP_KERNEL);
1388 if (penv->fw_cal_data == NULL) {
1389 smd_read(penv->smd_ch, NULL, calhdr.frag_size);
1390 goto exit;
1391 }
1392 }
1393
1394 mutex_lock(&penv->dev_lock);
1395 if (penv->fw_cal_rcvd + calhdr.frag_size >
1396 MAX_CALIBRATED_DATA_SIZE) {
1397 pr_err("calibrated data size is more than expected %d",
1398 penv->fw_cal_rcvd + calhdr.frag_size);
1399 penv->fw_cal_exp_frag = 0;
1400 penv->fw_cal_rcvd = 0;
1401 smd_read(penv->smd_ch, NULL, calhdr.frag_size);
1402 goto unlock_exit;
1403 }
1404
1405 rc = smd_read(penv->smd_ch, penv->fw_cal_data + penv->fw_cal_rcvd,
1406 calhdr.frag_size);
1407 if (rc < calhdr.frag_size)
1408 goto unlock_exit;
1409
1410 penv->fw_cal_exp_frag++;
1411 penv->fw_cal_rcvd += calhdr.frag_size;
1412
1413 if (calhdr.msg_flags & LAST_FRAGMENT) {
1414 penv->fw_cal_exp_frag = 0;
1415 penv->fw_cal_available = true;
1416 pr_info("wcnss: cal data collection completed\n");
1417 }
1418 mutex_unlock(&penv->dev_lock);
1419 wake_up(&penv->read_wait);
1420
1421 if (penv->fw_cal_available) {
1422 fw_status = WCNSS_RESP_SUCCESS;
1423 wcnss_send_cal_rsp(fw_status);
1424 }
1425 return;
1426
1427unlock_exit:
1428 mutex_unlock(&penv->dev_lock);
1429
1430exit:
1431 wcnss_send_cal_rsp(fw_status);
1432 return;
1433}
1434
1435
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001436static void wcnssctrl_rx_handler(struct work_struct *worker)
1437{
1438 int len = 0;
1439 int rc = 0;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001440 unsigned char buf[sizeof(struct wcnss_version)];
Sameer Thalappild821d632013-11-06 19:24:59 -08001441 unsigned char build[WCNSS_MAX_BUILD_VER_LEN+1];
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001442 struct smd_msg_hdr *phdr;
Sameer Thalappild821d632013-11-06 19:24:59 -08001443 struct smd_msg_hdr smd_msg;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001444 struct wcnss_version *pversion;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001445 int hw_type;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001446 unsigned char fw_status = 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001447
1448 len = smd_read_avail(penv->smd_ch);
1449 if (len > WCNSS_MAX_FRAME_SIZE) {
1450 pr_err("wcnss: frame larger than the allowed size\n");
1451 smd_read(penv->smd_ch, NULL, len);
1452 return;
1453 }
1454 if (len <= 0)
1455 return;
1456
Sameer Thalappil1878d762013-04-24 14:54:39 -07001457 rc = smd_read(penv->smd_ch, buf, sizeof(struct smd_msg_hdr));
1458 if (rc < sizeof(struct smd_msg_hdr)) {
1459 pr_err("wcnss: incomplete header read from smd\n");
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001460 return;
1461 }
Sameer Thalappil1878d762013-04-24 14:54:39 -07001462 len -= sizeof(struct smd_msg_hdr);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001463
1464 phdr = (struct smd_msg_hdr *)buf;
1465
Sameer Thalappilf106a682013-02-16 20:41:11 -08001466 switch (phdr->msg_type) {
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001467
1468 case WCNSS_VERSION_RSP:
Sameer Thalappil1878d762013-04-24 14:54:39 -07001469 if (len != sizeof(struct wcnss_version)
1470 - sizeof(struct smd_msg_hdr)) {
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001471 pr_err("wcnss: invalid version data from wcnss %d\n",
Sameer Thalappil1878d762013-04-24 14:54:39 -07001472 len);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001473 return;
1474 }
Sameer Thalappil1878d762013-04-24 14:54:39 -07001475 rc = smd_read(penv->smd_ch, buf+sizeof(struct smd_msg_hdr),
1476 len);
1477 if (rc < len) {
1478 pr_err("wcnss: incomplete data read from smd\n");
1479 return;
1480 }
1481 pversion = (struct wcnss_version *)buf;
1482 penv->fw_major = pversion->major;
1483 penv->fw_minor = pversion->minor;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001484 snprintf(penv->wcnss_version, WCNSS_VERSION_LEN,
1485 "%02x%02x%02x%02x", pversion->major, pversion->minor,
1486 pversion->version, pversion->revision);
1487 pr_info("wcnss: version %s\n", penv->wcnss_version);
Sameer Thalappilf106a682013-02-16 20:41:11 -08001488 /* schedule work to download nvbin to ccpu */
1489 hw_type = wcnss_hardware_type();
1490 switch (hw_type) {
1491 case WCNSS_RIVA_HW:
1492 /* supported only if riva major >= 1 and minor >= 4 */
1493 if ((pversion->major >= 1) && (pversion->minor >= 4)) {
1494 pr_info("wcnss: schedule dnld work for riva\n");
1495 schedule_work(&penv->wcnssctrl_nvbin_dnld_work);
1496 }
1497 break;
1498
1499 case WCNSS_PRONTO_HW:
Sameer Thalappild821d632013-11-06 19:24:59 -08001500 smd_msg.msg_type = WCNSS_BUILD_VER_REQ;
1501 smd_msg.msg_len = sizeof(smd_msg);
1502 rc = wcnss_smd_tx(&smd_msg, smd_msg.msg_len);
1503 if (rc < 0)
1504 pr_err("wcnss: smd tx failed: %s\n", __func__);
1505
Sameer Thalappilf106a682013-02-16 20:41:11 -08001506 /* supported only if pronto major >= 1 and minor >= 4 */
1507 if ((pversion->major >= 1) && (pversion->minor >= 4)) {
1508 pr_info("wcnss: schedule dnld work for pronto\n");
1509 schedule_work(&penv->wcnssctrl_nvbin_dnld_work);
1510 }
1511 break;
1512
1513 default:
1514 pr_info("wcnss: unknown hw type (%d), will not schedule dnld work\n",
1515 hw_type);
1516 break;
1517 }
1518 break;
1519
Sameer Thalappild821d632013-11-06 19:24:59 -08001520 case WCNSS_BUILD_VER_RSP:
1521 if (len > WCNSS_MAX_BUILD_VER_LEN) {
1522 pr_err("wcnss: invalid build version data from wcnss %d\n",
1523 len);
1524 return;
1525 }
1526 rc = smd_read(penv->smd_ch, build, len);
1527 if (rc < len) {
1528 pr_err("wcnss: incomplete data read from smd\n");
1529 return;
1530 }
1531 build[len] = 0;
1532 pr_info("wcnss: build version %s\n", build);
1533 break;
1534
Sameer Thalappilf106a682013-02-16 20:41:11 -08001535 case WCNSS_NVBIN_DNLD_RSP:
Sameer Thalappil1878d762013-04-24 14:54:39 -07001536 penv->nv_downloaded = true;
1537 fw_status = wcnss_fw_status();
1538 pr_debug("wcnss: received WCNSS_NVBIN_DNLD_RSP from ccpu %u\n",
1539 fw_status);
Sameer Thalappilf6fb1892013-09-06 17:33:30 -07001540 wcnss_setup_vbat_monitoring();
Sameer Thalappil1878d762013-04-24 14:54:39 -07001541 break;
1542
1543 case WCNSS_CALDATA_DNLD_RSP:
1544 penv->nv_downloaded = true;
1545 fw_status = wcnss_fw_status();
1546 pr_debug("wcnss: received WCNSS_CALDATA_DNLD_RSP from ccpu %u\n",
1547 fw_status);
1548 break;
1549
1550 case WCNSS_CALDATA_UPLD_REQ:
Sameer Thalappil1878d762013-04-24 14:54:39 -07001551 extract_cal_data(len);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001552 break;
1553
1554 default:
Sameer Thalappilf106a682013-02-16 20:41:11 -08001555 pr_err("wcnss: invalid message type %d\n", phdr->msg_type);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001556 }
1557 return;
1558}
1559
1560static void wcnss_send_version_req(struct work_struct *worker)
1561{
1562 struct smd_msg_hdr smd_msg;
1563 int ret = 0;
1564
Sameer Thalappilf106a682013-02-16 20:41:11 -08001565 smd_msg.msg_type = WCNSS_VERSION_REQ;
1566 smd_msg.msg_len = sizeof(smd_msg);
1567 ret = wcnss_smd_tx(&smd_msg, smd_msg.msg_len);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001568 if (ret < 0)
1569 pr_err("wcnss: smd tx failed\n");
1570
1571 return;
1572}
1573
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001574static DECLARE_RWSEM(wcnss_pm_sem);
Sameer Thalappil1878d762013-04-24 14:54:39 -07001575
1576static void wcnss_nvbin_dnld(void)
Sameer Thalappilf106a682013-02-16 20:41:11 -08001577{
1578 int ret = 0;
1579 struct nvbin_dnld_req_msg *dnld_req_msg;
1580 unsigned short total_fragments = 0;
1581 unsigned short count = 0;
1582 unsigned short retry_count = 0;
1583 unsigned short cur_frag_size = 0;
1584 unsigned char *outbuffer = NULL;
1585 const void *nv_blob_addr = NULL;
1586 unsigned int nv_blob_size = 0;
1587 const struct firmware *nv = NULL;
Sameer Thalappilf0d89ba2013-03-11 17:33:20 -07001588 struct device *dev = &penv->pdev->dev;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001589
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001590 down_read(&wcnss_pm_sem);
1591
Sameer Thalappilf106a682013-02-16 20:41:11 -08001592 ret = request_firmware(&nv, NVBIN_FILE, dev);
1593
1594 if (ret || !nv || !nv->data || !nv->size) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07001595 pr_err("wcnss: %s: request_firmware failed for %s\n",
1596 __func__, NVBIN_FILE);
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001597 goto out;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001598 }
1599
1600 /*
1601 * First 4 bytes in nv blob is validity bitmap.
1602 * We cannot validate nv, so skip those 4 bytes.
1603 */
1604 nv_blob_addr = nv->data + 4;
1605 nv_blob_size = nv->size - 4;
1606
1607 total_fragments = TOTALFRAGMENTS(nv_blob_size);
1608
1609 pr_info("wcnss: NV bin size: %d, total_fragments: %d\n",
1610 nv_blob_size, total_fragments);
1611
1612 /* get buffer for nv bin dnld req message */
1613 outbuffer = kmalloc((sizeof(struct nvbin_dnld_req_msg) +
1614 NV_FRAGMENT_SIZE), GFP_KERNEL);
1615
1616 if (NULL == outbuffer) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07001617 pr_err("wcnss: %s: failed to get buffer\n", __func__);
Sameer Thalappilf106a682013-02-16 20:41:11 -08001618 goto err_free_nv;
1619 }
1620
1621 dnld_req_msg = (struct nvbin_dnld_req_msg *)outbuffer;
1622
1623 dnld_req_msg->hdr.msg_type = WCNSS_NVBIN_DNLD_REQ;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001624 dnld_req_msg->dnld_req_params.msg_flags = 0;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001625
1626 for (count = 0; count < total_fragments; count++) {
1627 dnld_req_msg->dnld_req_params.frag_number = count;
1628
1629 if (count == (total_fragments - 1)) {
1630 /* last fragment, take care of boundry condition */
1631 cur_frag_size = nv_blob_size % NV_FRAGMENT_SIZE;
1632 if (!cur_frag_size)
1633 cur_frag_size = NV_FRAGMENT_SIZE;
1634
Sameer Thalappil1878d762013-04-24 14:54:39 -07001635 dnld_req_msg->dnld_req_params.msg_flags |=
1636 LAST_FRAGMENT;
1637 dnld_req_msg->dnld_req_params.msg_flags |=
1638 CAN_RECEIVE_CALDATA;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001639 } else {
1640 cur_frag_size = NV_FRAGMENT_SIZE;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001641 dnld_req_msg->dnld_req_params.msg_flags &=
1642 ~LAST_FRAGMENT;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001643 }
1644
1645 dnld_req_msg->dnld_req_params.nvbin_buffer_size =
1646 cur_frag_size;
1647
1648 dnld_req_msg->hdr.msg_len =
1649 sizeof(struct nvbin_dnld_req_msg) + cur_frag_size;
1650
1651 /* copy NV fragment */
1652 memcpy((outbuffer + sizeof(struct nvbin_dnld_req_msg)),
1653 (nv_blob_addr + count * NV_FRAGMENT_SIZE),
1654 cur_frag_size);
1655
1656 ret = wcnss_smd_tx(outbuffer, dnld_req_msg->hdr.msg_len);
1657
1658 retry_count = 0;
1659 while ((ret == -ENOSPC) && (retry_count <= 3)) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07001660 pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
1661 __func__);
Sameer Thalappilf106a682013-02-16 20:41:11 -08001662 pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
1663 count, dnld_req_msg->hdr.msg_len,
1664 total_fragments, retry_count);
1665
1666 /* wait and try again */
1667 msleep(20);
1668 retry_count++;
1669 ret = wcnss_smd_tx(outbuffer,
1670 dnld_req_msg->hdr.msg_len);
1671 }
1672
1673 if (ret < 0) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07001674 pr_err("wcnss: %s: smd tx failed\n", __func__);
Sameer Thalappilf106a682013-02-16 20:41:11 -08001675 pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
1676 count, dnld_req_msg->hdr.msg_len,
1677 total_fragments, retry_count);
1678 goto err_dnld;
1679 }
1680 }
1681
1682err_dnld:
1683 /* free buffer */
1684 kfree(outbuffer);
1685
1686err_free_nv:
1687 /* release firmware */
1688 release_firmware(nv);
1689
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001690out:
1691 up_read(&wcnss_pm_sem);
1692
Sameer Thalappilf106a682013-02-16 20:41:11 -08001693 return;
1694}
1695
Sameer Thalappil1878d762013-04-24 14:54:39 -07001696
1697static void wcnss_caldata_dnld(const void *cal_data,
1698 unsigned int cal_data_size, bool msg_to_follow)
1699{
1700 int ret = 0;
1701 struct cal_data_msg *cal_msg;
1702 unsigned short total_fragments = 0;
1703 unsigned short count = 0;
1704 unsigned short retry_count = 0;
1705 unsigned short cur_frag_size = 0;
1706 unsigned char *outbuffer = NULL;
1707
1708 total_fragments = TOTALFRAGMENTS(cal_data_size);
1709
1710 outbuffer = kmalloc((sizeof(struct cal_data_msg) +
1711 NV_FRAGMENT_SIZE), GFP_KERNEL);
1712
1713 if (NULL == outbuffer) {
1714 pr_err("wcnss: %s: failed to get buffer\n", __func__);
1715 return;
1716 }
1717
1718 cal_msg = (struct cal_data_msg *)outbuffer;
1719
1720 cal_msg->hdr.msg_type = WCNSS_CALDATA_DNLD_REQ;
1721 cal_msg->cal_params.msg_flags = 0;
1722
1723 for (count = 0; count < total_fragments; count++) {
1724 cal_msg->cal_params.frag_number = count;
1725
1726 if (count == (total_fragments - 1)) {
1727 cur_frag_size = cal_data_size % NV_FRAGMENT_SIZE;
1728 if (!cur_frag_size)
1729 cur_frag_size = NV_FRAGMENT_SIZE;
1730
1731 cal_msg->cal_params.msg_flags
1732 |= LAST_FRAGMENT;
1733 if (msg_to_follow)
1734 cal_msg->cal_params.msg_flags |=
1735 MESSAGE_TO_FOLLOW;
1736 } else {
1737 cur_frag_size = NV_FRAGMENT_SIZE;
1738 cal_msg->cal_params.msg_flags &=
1739 ~LAST_FRAGMENT;
1740 }
1741
1742 cal_msg->cal_params.total_size = cal_data_size;
1743 cal_msg->cal_params.frag_size =
1744 cur_frag_size;
1745
1746 cal_msg->hdr.msg_len =
1747 sizeof(struct cal_data_msg) + cur_frag_size;
1748
1749 memcpy((outbuffer + sizeof(struct cal_data_msg)),
1750 (cal_data + count * NV_FRAGMENT_SIZE),
1751 cur_frag_size);
1752
1753 ret = wcnss_smd_tx(outbuffer, cal_msg->hdr.msg_len);
1754
1755 retry_count = 0;
1756 while ((ret == -ENOSPC) && (retry_count <= 3)) {
1757 pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
1758 __func__);
1759 pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
1760 count, cal_msg->hdr.msg_len,
1761 total_fragments, retry_count);
1762
1763 /* wait and try again */
1764 msleep(20);
1765 retry_count++;
1766 ret = wcnss_smd_tx(outbuffer,
1767 cal_msg->hdr.msg_len);
1768 }
1769
1770 if (ret < 0) {
1771 pr_err("wcnss: %s: smd tx failed\n", __func__);
1772 pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
1773 count, cal_msg->hdr.msg_len,
1774 total_fragments, retry_count);
1775 goto err_dnld;
1776 }
1777 }
1778
1779
1780err_dnld:
1781 /* free buffer */
1782 kfree(outbuffer);
1783
1784 return;
1785}
1786
1787
1788static void wcnss_nvbin_dnld_main(struct work_struct *worker)
1789{
1790 int retry = 0;
1791
1792 if (!FW_CALDATA_CAPABLE())
1793 goto nv_download;
1794
1795 if (!penv->fw_cal_available && WCNSS_CONFIG_UNSPECIFIED
1796 != has_calibrated_data && !penv->user_cal_available) {
1797 while (!penv->user_cal_available && retry++ < 5)
1798 msleep(500);
1799 }
Sameer Thalappila5e70ae2013-10-22 13:18:20 -07001800 if (penv->fw_cal_available) {
1801 pr_info_ratelimited("wcnss: cal download, using fw cal");
1802 wcnss_caldata_dnld(penv->fw_cal_data, penv->fw_cal_rcvd, true);
Sameer Thalappil1878d762013-04-24 14:54:39 -07001803
1804 } else if (penv->user_cal_available) {
Sameer Thalappila5e70ae2013-10-22 13:18:20 -07001805 pr_info_ratelimited("wcnss: cal download, using user cal");
Sameer Thalappil1878d762013-04-24 14:54:39 -07001806 wcnss_caldata_dnld(penv->user_cal_data,
1807 penv->user_cal_rcvd, true);
1808 }
1809
1810nv_download:
1811 pr_info_ratelimited("wcnss: NV download");
1812 wcnss_nvbin_dnld();
1813
1814 return;
1815}
1816
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001817static int wcnss_pm_notify(struct notifier_block *b,
1818 unsigned long event, void *p)
1819{
1820 switch (event) {
1821 case PM_SUSPEND_PREPARE:
1822 down_write(&wcnss_pm_sem);
1823 break;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001824
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001825 case PM_POST_SUSPEND:
1826 up_write(&wcnss_pm_sem);
1827 break;
1828 }
1829
1830 return NOTIFY_DONE;
1831}
1832
1833static struct notifier_block wcnss_pm_notifier = {
1834 .notifier_call = wcnss_pm_notify,
1835};
Sameer Thalappil1878d762013-04-24 14:54:39 -07001836
Sameer Thalappil13780d12013-12-19 17:04:21 -08001837static int wcnss_ctrl_open(struct inode *inode, struct file *file)
1838{
1839 int rc = 0;
1840
1841 if (!penv || penv->ctrl_device_opened)
1842 return -EFAULT;
1843
1844 penv->ctrl_device_opened = 1;
1845
1846 return rc;
1847}
1848
1849
1850void process_usr_ctrl_cmd(u8 *buf, size_t len)
1851{
1852 u16 cmd = buf[0] << 8 | buf[1];
1853
1854 switch (cmd) {
1855
1856 case WCNSS_USR_SERIAL_NUM:
1857 if (WCNSS_MIN_SERIAL_LEN > len) {
1858 pr_err("%s: Invalid serial number\n", __func__);
1859 return;
1860 }
1861 penv->serial_number = buf[2] << 24 | buf[3] << 16
1862 | buf[4] << 8 | buf[5];
1863 break;
1864
1865 case WCNSS_USR_HAS_CAL_DATA:
1866 if (1 < buf[2])
1867 pr_err("%s: Invalid data for cal %d\n", __func__,
1868 buf[2]);
1869 has_calibrated_data = buf[2];
1870 break;
1871
1872 default:
1873 pr_err("%s: Invalid command %d\n", __func__, cmd);
1874 break;
1875 }
1876}
1877
1878static ssize_t wcnss_ctrl_write(struct file *fp, const char __user
1879 *user_buffer, size_t count, loff_t *position)
1880{
1881 int rc = 0;
1882 u8 buf[WCNSS_MAX_CMD_LEN];
1883
1884 if (!penv || !penv->ctrl_device_opened || WCNSS_MAX_CMD_LEN < count
1885 || WCNSS_MIN_CMD_LEN > count)
1886 return -EFAULT;
1887
1888 mutex_lock(&penv->ctrl_lock);
1889 rc = copy_from_user(buf, user_buffer, count);
1890 if (0 == rc)
1891 process_usr_ctrl_cmd(buf, count);
1892
1893 mutex_unlock(&penv->ctrl_lock);
1894
1895 return rc;
1896}
1897
1898
1899static const struct file_operations wcnss_ctrl_fops = {
1900 .owner = THIS_MODULE,
1901 .open = wcnss_ctrl_open,
1902 .write = wcnss_ctrl_write,
1903};
1904
1905static struct miscdevice wcnss_usr_ctrl = {
1906 .minor = MISC_DYNAMIC_MINOR,
1907 .name = CTRL_DEVICE,
1908 .fops = &wcnss_ctrl_fops,
1909};
1910
Jeff Johnsonb3377e32011-11-18 23:32:27 -08001911static int
1912wcnss_trigger_config(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001913{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001914 int ret;
Ankur Nandwanib0039b02011-08-09 14:00:45 -07001915 struct qcom_wcnss_opts *pdata;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001916 unsigned long wcnss_phys_addr;
1917 int size = 0;
Sameer Thalappil58281ca2013-04-10 18:50:18 -07001918 struct resource *res;
Sameer Thalappil8d686d42012-08-24 10:07:31 -07001919 int has_pronto_hw = of_property_read_bool(pdev->dev.of_node,
Sameer Thalappil820f87b2013-05-21 20:40:54 -07001920 "qcom,has-pronto-hw");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001921
Sameer Thalappild0952f62013-05-03 10:18:29 -07001922 if (of_property_read_u32(pdev->dev.of_node,
1923 "qcom,wlan-rx-buff-count", &penv->wlan_rx_buff_count)) {
1924 penv->wlan_rx_buff_count = WCNSS_DEF_WLAN_RX_BUFF_COUNT;
1925 }
1926
Jeff Johnsonb3377e32011-11-18 23:32:27 -08001927 /* make sure we are only triggered once */
1928 if (penv->triggered)
1929 return 0;
1930 penv->triggered = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001931
Ankur Nandwanib0039b02011-08-09 14:00:45 -07001932 /* initialize the WCNSS device configuration */
1933 pdata = pdev->dev.platform_data;
Sameer Thalappil8d686d42012-08-24 10:07:31 -07001934 if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo) {
1935 if (has_pronto_hw) {
1936 has_48mhz_xo = of_property_read_bool(pdev->dev.of_node,
Sameer Thalappil820f87b2013-05-21 20:40:54 -07001937 "qcom,has-48mhz-xo");
Sameer Thalappil8d686d42012-08-24 10:07:31 -07001938 } else {
Sameer Thalappil8d686d42012-08-24 10:07:31 -07001939 has_48mhz_xo = pdata->has_48mhz_xo;
1940 }
1941 }
Sameer Thalappilf00dbeb2013-03-01 18:33:30 -08001942 penv->wcnss_hw_type = (has_pronto_hw) ? WCNSS_PRONTO_HW : WCNSS_RIVA_HW;
Ankur Nandwanib0039b02011-08-09 14:00:45 -07001943 penv->wlan_config.use_48mhz_xo = has_48mhz_xo;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001944
Sameer Thalappil58cec312013-05-13 15:52:08 -07001945 if (WCNSS_CONFIG_UNSPECIFIED == has_autodetect_xo && has_pronto_hw) {
1946 has_autodetect_xo = of_property_read_bool(pdev->dev.of_node,
Sameer Thalappil820f87b2013-05-21 20:40:54 -07001947 "qcom,has-autodetect-xo");
Sameer Thalappil58cec312013-05-13 15:52:08 -07001948 }
1949
Jeff Johnson5fda4f82012-01-09 14:15:34 -08001950 penv->thermal_mitigation = 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001951 strlcpy(penv->wcnss_version, "INVALID", WCNSS_VERSION_LEN);
Jeff Johnson5fda4f82012-01-09 14:15:34 -08001952
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -07001953 /* Configure 5 wire GPIOs */
Sameer Thalappil8d686d42012-08-24 10:07:31 -07001954 if (!has_pronto_hw) {
1955 penv->gpios_5wire = platform_get_resource_byname(pdev,
1956 IORESOURCE_IO, "wcnss_gpios_5wire");
1957
1958 /* allocate 5-wire GPIO resources */
1959 if (!penv->gpios_5wire) {
1960 dev_err(&pdev->dev, "insufficient IO resources\n");
1961 ret = -ENOENT;
1962 goto fail_gpio_res;
1963 }
1964 ret = wcnss_gpios_config(penv->gpios_5wire, true);
1965 } else
1966 ret = wcnss_pronto_gpios_config(&pdev->dev, true);
1967
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -07001968 if (ret) {
1969 dev_err(&pdev->dev, "WCNSS gpios config failed.\n");
1970 goto fail_gpio_res;
1971 }
1972
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001973 /* power up the WCNSS */
1974 ret = wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
Sameer Thalappil1d69b8022013-06-10 19:10:07 -07001975 WCNSS_WLAN_SWITCH_ON,
1976 &penv->iris_xo_mode_set);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001977 if (ret) {
1978 dev_err(&pdev->dev, "WCNSS Power-up failed.\n");
1979 goto fail_power;
1980 }
1981
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001982 /* allocate resources */
1983 penv->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
1984 "wcnss_mmio");
1985 penv->tx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1986 "wcnss_wlantx_irq");
1987 penv->rx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1988 "wcnss_wlanrx_irq");
1989
1990 if (!(penv->mmio_res && penv->tx_irq_res && penv->rx_irq_res)) {
1991 dev_err(&pdev->dev, "insufficient resources\n");
1992 ret = -ENOENT;
1993 goto fail_res;
1994 }
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001995 INIT_WORK(&penv->wcnssctrl_rx_work, wcnssctrl_rx_handler);
1996 INIT_WORK(&penv->wcnssctrl_version_work, wcnss_send_version_req);
Sameer Thalappil1878d762013-04-24 14:54:39 -07001997 INIT_WORK(&penv->wcnssctrl_nvbin_dnld_work, wcnss_nvbin_dnld_main);
Jeff Johnson8b1f6d02012-02-03 20:43:26 -08001998
Sameer Thalappilf138da52012-07-30 12:56:37 -07001999 wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss");
2000
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002001 if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
2002 size = 0x3000;
2003 wcnss_phys_addr = MSM_PRONTO_PHYS;
2004 } else {
2005 wcnss_phys_addr = MSM_RIVA_PHYS;
2006 size = SZ_256;
2007 }
2008
2009 penv->msm_wcnss_base = ioremap(wcnss_phys_addr, size);
2010 if (!penv->msm_wcnss_base) {
2011 ret = -ENOMEM;
2012 pr_err("%s: ioremap wcnss physical failed\n", __func__);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002013 goto fail_ioremap;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002014 }
2015
Sameer Thalappild3aad702013-01-22 18:52:24 -08002016 if (wcnss_hardware_type() == WCNSS_RIVA_HW) {
2017 penv->riva_ccu_base = ioremap(MSM_RIVA_CCU_BASE, SZ_512);
2018 if (!penv->riva_ccu_base) {
2019 ret = -ENOMEM;
2020 pr_err("%s: ioremap wcnss physical failed\n", __func__);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002021 goto fail_ioremap2;
Sameer Thalappild3aad702013-01-22 18:52:24 -08002022 }
2023 } else {
2024 penv->pronto_a2xb_base = ioremap(MSM_PRONTO_A2XB_BASE, SZ_512);
2025 if (!penv->pronto_a2xb_base) {
2026 ret = -ENOMEM;
2027 pr_err("%s: ioremap wcnss physical failed\n", __func__);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002028 goto fail_ioremap2;
Sameer Thalappild3aad702013-01-22 18:52:24 -08002029 }
Sameer Thalappil16cae9b2013-03-04 18:02:50 -08002030 penv->pronto_ccpu_base = ioremap(MSM_PRONTO_CCPU_BASE, SZ_512);
2031 if (!penv->pronto_ccpu_base) {
2032 ret = -ENOMEM;
2033 pr_err("%s: ioremap wcnss physical failed\n", __func__);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002034 goto fail_ioremap3;
Sameer Thalappil16cae9b2013-03-04 18:02:50 -08002035 }
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002036 /* for reset FIQ */
2037 res = platform_get_resource_byname(penv->pdev,
2038 IORESOURCE_MEM, "wcnss_fiq");
2039 if (!res) {
2040 dev_err(&pdev->dev, "insufficient irq mem resources\n");
2041 ret = -ENOENT;
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002042 goto fail_ioremap4;
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002043 }
2044 penv->fiq_reg = ioremap_nocache(res->start, resource_size(res));
2045 if (!penv->fiq_reg) {
2046 pr_err("wcnss: %s: ioremap_nocache() failed fiq_reg addr:%pr\n",
2047 __func__, &res->start);
2048 ret = -ENOMEM;
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002049 goto fail_ioremap4;
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002050 }
Sameer Thalappil767ff492013-06-19 15:25:21 -07002051 penv->pronto_saw2_base = ioremap_nocache(MSM_PRONTO_SAW2_BASE,
2052 SZ_32);
2053 if (!penv->pronto_saw2_base) {
2054 pr_err("%s: ioremap wcnss physical(saw2) failed\n",
2055 __func__);
2056 ret = -ENOMEM;
2057 goto fail_ioremap5;
2058 }
Sameer Thalappil66d2b392013-07-02 11:32:55 -07002059 penv->pronto_pll_base = ioremap_nocache(MSM_PRONTO_PLL_BASE,
2060 SZ_64);
2061 if (!penv->pronto_pll_base) {
2062 pr_err("%s: ioremap wcnss physical(pll) failed\n",
2063 __func__);
2064 ret = -ENOMEM;
2065 goto fail_ioremap6;
2066 }
2067
Sameer Thalappil670197c2013-07-19 16:31:14 -07002068 penv->wlan_tx_phy_aborts = ioremap(MSM_PRONTO_TXP_PHY_ABORT,
2069 SZ_8);
2070 if (!penv->wlan_tx_phy_aborts) {
2071 ret = -ENOMEM;
2072 pr_err("%s: ioremap wlan TX PHY failed\n", __func__);
2073 goto fail_ioremap7;
2074 }
2075 penv->wlan_brdg_err_source = ioremap(MSM_PRONTO_BRDG_ERR_SRC,
2076 SZ_8);
2077 if (!penv->wlan_brdg_err_source) {
2078 ret = -ENOMEM;
2079 pr_err("%s: ioremap wlan BRDG ERR failed\n", __func__);
2080 goto fail_ioremap8;
2081 }
Yue Mae490b452013-11-12 00:07:33 -08002082 penv->wlan_tx_status = ioremap(MSM_PRONTO_TXP_STATUS, SZ_8);
2083 if (!penv->wlan_tx_status) {
2084 ret = -ENOMEM;
2085 pr_err("%s: ioremap wlan TX STATUS failed\n", __func__);
2086 goto fail_ioremap9;
2087 }
Sameer Thalappild3aad702013-01-22 18:52:24 -08002088 }
Sameer Thalappilf6fb1892013-09-06 17:33:30 -07002089 penv->adc_tm_dev = qpnp_get_adc_tm(&penv->pdev->dev, "wcnss");
2090 if (IS_ERR(penv->adc_tm_dev)) {
2091 pr_err("%s: adc get failed\n", __func__);
2092 penv->adc_tm_dev = NULL;
2093 } else {
2094 INIT_DELAYED_WORK(&penv->vbatt_work, wcnss_update_vbatt);
2095 penv->fw_vbatt_state = WCNSS_CONFIG_UNSPECIFIED;
2096 }
Sameer Thalappild3aad702013-01-22 18:52:24 -08002097
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002098 /* trigger initialization of the WCNSS */
2099 penv->pil = subsystem_get(WCNSS_PIL_DEVICE);
2100 if (IS_ERR(penv->pil)) {
2101 dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
2102 ret = PTR_ERR(penv->pil);
Sameer Thalappilc78facf2013-07-30 12:07:31 -07002103 wcnss_pronto_log_debug_regs();
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002104 penv->pil = NULL;
2105 goto fail_pil;
2106 }
2107
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002108 return 0;
2109
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002110fail_pil:
2111 if (penv->riva_ccu_base)
2112 iounmap(penv->riva_ccu_base);
Yue Mae490b452013-11-12 00:07:33 -08002113 if (penv->wlan_tx_status)
2114 iounmap(penv->wlan_tx_status);
2115fail_ioremap9:
Sameer Thalappil670197c2013-07-19 16:31:14 -07002116 if (penv->wlan_brdg_err_source)
2117 iounmap(penv->wlan_brdg_err_source);
2118fail_ioremap8:
2119 if (penv->wlan_tx_phy_aborts)
2120 iounmap(penv->wlan_tx_phy_aborts);
2121fail_ioremap7:
Sameer Thalappil66d2b392013-07-02 11:32:55 -07002122 if (penv->pronto_pll_base)
2123 iounmap(penv->pronto_pll_base);
2124fail_ioremap6:
Sameer Thalappil767ff492013-06-19 15:25:21 -07002125 if (penv->pronto_saw2_base)
2126 iounmap(penv->pronto_saw2_base);
2127fail_ioremap5:
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002128 if (penv->fiq_reg)
2129 iounmap(penv->fiq_reg);
2130fail_ioremap4:
2131 if (penv->pronto_ccpu_base)
2132 iounmap(penv->pronto_ccpu_base);
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002133fail_ioremap3:
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002134 if (penv->pronto_a2xb_base)
2135 iounmap(penv->pronto_a2xb_base);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -08002136fail_ioremap2:
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002137 if (penv->msm_wcnss_base)
2138 iounmap(penv->msm_wcnss_base);
Sameer Thalappild3aad702013-01-22 18:52:24 -08002139fail_ioremap:
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002140 wake_lock_destroy(&penv->wcnss_wake_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002141fail_res:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002142 wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
Sameer Thalappil1d69b8022013-06-10 19:10:07 -07002143 WCNSS_WLAN_SWITCH_OFF, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002144fail_power:
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002145 if (has_pronto_hw)
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002146 wcnss_pronto_gpios_config(&pdev->dev, false);
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002147 else
2148 wcnss_gpios_config(penv->gpios_5wire, false);
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -07002149fail_gpio_res:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002150 penv = NULL;
2151 return ret;
2152}
2153
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002154static int wcnss_node_open(struct inode *inode, struct file *file)
2155{
2156 struct platform_device *pdev;
Sameer Thalappild0bb3bb2013-10-09 13:36:26 -07002157 int rc = 0;
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002158
Sameer Thalappil37c20682013-05-31 14:01:25 -07002159 if (!penv)
2160 return -EFAULT;
2161
Sameer Thalappil1878d762013-04-24 14:54:39 -07002162 if (!penv->triggered) {
2163 pr_info(DEVICE " triggered by userspace\n");
2164 pdev = penv->pdev;
Sameer Thalappild0bb3bb2013-10-09 13:36:26 -07002165 rc = wcnss_trigger_config(pdev);
2166 if (rc)
2167 return -EFAULT;
Sameer Thalappil1878d762013-04-24 14:54:39 -07002168 }
2169
2170 mutex_lock(&penv->dev_lock);
2171 penv->user_cal_rcvd = 0;
2172 penv->user_cal_read = 0;
2173 penv->user_cal_available = false;
2174 penv->user_cal_data = NULL;
2175 penv->device_opened = 1;
2176 mutex_unlock(&penv->dev_lock);
2177
Sameer Thalappild0bb3bb2013-10-09 13:36:26 -07002178 return rc;
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002179}
2180
Sameer Thalappil1878d762013-04-24 14:54:39 -07002181static ssize_t wcnss_wlan_read(struct file *fp, char __user
2182 *buffer, size_t count, loff_t *position)
2183{
2184 int rc = 0;
2185
Sameer Thalappil37c20682013-05-31 14:01:25 -07002186 if (!penv || !penv->device_opened)
Sameer Thalappil1878d762013-04-24 14:54:39 -07002187 return -EFAULT;
2188
2189 rc = wait_event_interruptible(penv->read_wait, penv->fw_cal_rcvd
2190 > penv->user_cal_read || penv->fw_cal_available);
2191
2192 if (rc < 0)
2193 return rc;
2194
2195 mutex_lock(&penv->dev_lock);
2196
2197 if (penv->fw_cal_available && penv->fw_cal_rcvd
2198 == penv->user_cal_read) {
2199 rc = 0;
2200 goto exit;
2201 }
2202
2203 if (count > penv->fw_cal_rcvd - penv->user_cal_read)
2204 count = penv->fw_cal_rcvd - penv->user_cal_read;
2205
2206 rc = copy_to_user(buffer, penv->fw_cal_data +
2207 penv->user_cal_read, count);
2208 if (rc == 0) {
2209 penv->user_cal_read += count;
2210 rc = count;
2211 }
2212
2213exit:
2214 mutex_unlock(&penv->dev_lock);
2215 return rc;
2216}
2217
2218/* first (valid) write to this device should be 4 bytes cal file size */
2219static ssize_t wcnss_wlan_write(struct file *fp, const char __user
2220 *user_buffer, size_t count, loff_t *position)
2221{
2222 int rc = 0;
Arif Hussain677c92d2013-11-01 19:09:11 -07002223 size_t size = 0;
Sameer Thalappil1878d762013-04-24 14:54:39 -07002224
Sameer Thalappil37c20682013-05-31 14:01:25 -07002225 if (!penv || !penv->device_opened || penv->user_cal_available)
Sameer Thalappil1878d762013-04-24 14:54:39 -07002226 return -EFAULT;
2227
2228 if (penv->user_cal_rcvd == 0 && count >= 4
2229 && !penv->user_cal_data) {
2230 rc = copy_from_user((void *)&size, user_buffer, 4);
Arif Hussain677c92d2013-11-01 19:09:11 -07002231 if (!size || size > MAX_CALIBRATED_DATA_SIZE) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07002232 pr_err(DEVICE " invalid size to write %d\n", size);
2233 return -EFAULT;
2234 }
2235
2236 rc += count;
2237 count -= 4;
2238 penv->user_cal_exp_size = size;
2239 penv->user_cal_data = kmalloc(size, GFP_KERNEL);
2240 if (penv->user_cal_data == NULL) {
2241 pr_err(DEVICE " no memory to write\n");
2242 return -ENOMEM;
2243 }
2244 if (0 == count)
2245 goto exit;
2246
2247 } else if (penv->user_cal_rcvd == 0 && count < 4)
2248 return -EFAULT;
2249
Arif Hussain677c92d2013-11-01 19:09:11 -07002250 if ((UINT32_MAX - count < penv->user_cal_rcvd) ||
2251 MAX_CALIBRATED_DATA_SIZE < count + penv->user_cal_rcvd) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07002252 pr_err(DEVICE " invalid size to write %d\n", count +
2253 penv->user_cal_rcvd);
2254 rc = -ENOMEM;
2255 goto exit;
2256 }
2257 rc = copy_from_user((void *)penv->user_cal_data +
2258 penv->user_cal_rcvd, user_buffer, count);
2259 if (0 == rc) {
2260 penv->user_cal_rcvd += count;
2261 rc += count;
2262 }
2263 if (penv->user_cal_rcvd == penv->user_cal_exp_size) {
2264 penv->user_cal_available = true;
2265 pr_info_ratelimited("wcnss: user cal written");
2266 }
2267
2268exit:
2269 return rc;
2270}
2271
2272
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002273static const struct file_operations wcnss_node_fops = {
2274 .owner = THIS_MODULE,
2275 .open = wcnss_node_open,
Sameer Thalappil1878d762013-04-24 14:54:39 -07002276 .read = wcnss_wlan_read,
2277 .write = wcnss_wlan_write,
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002278};
2279
2280static struct miscdevice wcnss_misc = {
2281 .minor = MISC_DYNAMIC_MINOR,
2282 .name = DEVICE,
2283 .fops = &wcnss_node_fops,
2284};
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002285
2286static int __devinit
2287wcnss_wlan_probe(struct platform_device *pdev)
2288{
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002289 int ret = 0;
2290
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002291 /* verify we haven't been called more than once */
2292 if (penv) {
2293 dev_err(&pdev->dev, "cannot handle multiple devices.\n");
2294 return -ENODEV;
2295 }
2296
2297 /* create an environment to track the device */
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -08002298 penv = devm_kzalloc(&pdev->dev, sizeof(*penv), GFP_KERNEL);
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002299 if (!penv) {
2300 dev_err(&pdev->dev, "cannot allocate device memory.\n");
2301 return -ENOMEM;
2302 }
2303 penv->pdev = pdev;
2304
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002305 /* register sysfs entries */
2306 ret = wcnss_create_sysfs(&pdev->dev);
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -08002307 if (ret) {
2308 penv = NULL;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002309 return -ENOENT;
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -08002310 }
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002311
Sameer Thalappil1878d762013-04-24 14:54:39 -07002312 mutex_init(&penv->dev_lock);
Sameer Thalappil13780d12013-12-19 17:04:21 -08002313 mutex_init(&penv->ctrl_lock);
Sameer Thalappilf6fb1892013-09-06 17:33:30 -07002314 mutex_init(&penv->vbat_monitor_mutex);
Sameer Thalappil1878d762013-04-24 14:54:39 -07002315 init_waitqueue_head(&penv->read_wait);
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002316
Sameer Thalappild3aad702013-01-22 18:52:24 -08002317 /* Since we were built into the kernel we'll be called as part
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002318 * of kernel initialization. We don't know if userspace
2319 * applications are available to service PIL at this time
2320 * (they probably are not), so we simply create a device node
2321 * here. When userspace is available it should touch the
2322 * device so that we know that WCNSS configuration can take
2323 * place
2324 */
2325 pr_info(DEVICE " probed in built-in mode\n");
Sameer Thalappil13780d12013-12-19 17:04:21 -08002326
2327 misc_register(&wcnss_usr_ctrl);
2328
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002329 return misc_register(&wcnss_misc);
2330
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002331}
2332
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002333static int __devexit
2334wcnss_wlan_remove(struct platform_device *pdev)
2335{
Jeff Johnson8b1f6d02012-02-03 20:43:26 -08002336 wcnss_remove_sysfs(&pdev->dev);
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -08002337 penv = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002338 return 0;
2339}
2340
2341
2342static const struct dev_pm_ops wcnss_wlan_pm_ops = {
2343 .suspend = wcnss_wlan_suspend,
2344 .resume = wcnss_wlan_resume,
2345};
2346
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002347#ifdef CONFIG_WCNSS_CORE_PRONTO
2348static struct of_device_id msm_wcnss_pronto_match[] = {
2349 {.compatible = "qcom,wcnss_wlan"},
2350 {}
2351};
2352#endif
2353
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002354static struct platform_driver wcnss_wlan_driver = {
2355 .driver = {
2356 .name = DEVICE,
2357 .owner = THIS_MODULE,
2358 .pm = &wcnss_wlan_pm_ops,
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002359#ifdef CONFIG_WCNSS_CORE_PRONTO
2360 .of_match_table = msm_wcnss_pronto_match,
2361#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002362 },
2363 .probe = wcnss_wlan_probe,
2364 .remove = __devexit_p(wcnss_wlan_remove),
2365};
2366
2367static int __init wcnss_wlan_init(void)
2368{
Sameer Thalappil24db5282012-09-10 11:58:33 -07002369 int ret = 0;
2370
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002371 platform_driver_register(&wcnss_wlan_driver);
2372 platform_driver_register(&wcnss_wlan_ctrl_driver);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002373 platform_driver_register(&wcnss_ctrl_driver);
Sameer Thalappil944c72d2013-08-14 17:56:17 -07002374 register_pm_notifier(&wcnss_pm_notifier);
Sameer Thalappil24db5282012-09-10 11:58:33 -07002375#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
2376 ret = wcnss_prealloc_init();
2377 if (ret < 0)
2378 pr_err("wcnss: pre-allocation failed\n");
2379#endif
2380
2381 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002382}
2383
2384static void __exit wcnss_wlan_exit(void)
2385{
2386 if (penv) {
2387 if (penv->pil)
Stephen Boyd77db8bb2012-06-27 15:15:16 -07002388 subsystem_put(penv->pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002389 penv = NULL;
2390 }
2391
Sameer Thalappil24db5282012-09-10 11:58:33 -07002392#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
2393 wcnss_prealloc_deinit();
2394#endif
Sameer Thalappil944c72d2013-08-14 17:56:17 -07002395 unregister_pm_notifier(&wcnss_pm_notifier);
2396 platform_driver_unregister(&wcnss_ctrl_driver);
2397 platform_driver_unregister(&wcnss_wlan_ctrl_driver);
2398 platform_driver_unregister(&wcnss_wlan_driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002399}
2400
2401module_init(wcnss_wlan_init);
2402module_exit(wcnss_wlan_exit);
2403
2404MODULE_LICENSE("GPL v2");
2405MODULE_VERSION(VERSION);
2406MODULE_DESCRIPTION(DEVICE "Driver");