blob: d2b381b159fad4d04f7bc993bad3349162bb108b [file] [log] [blame]
Sameer Thalappilf106a682013-02-16 20:41:11 -08001/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/module.h>
Sameer Thalappilf106a682013-02-16 20:41:11 -080014#include <linux/firmware.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070015#include <linux/slab.h>
16#include <linux/err.h>
17#include <linux/platform_device.h>
Jeff Johnsonb3377e32011-11-18 23:32:27 -080018#include <linux/miscdevice.h>
19#include <linux/fs.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/wcnss_wlan.h>
Ankur Nandwanib0039b02011-08-09 14:00:45 -070021#include <linux/platform_data/qcom_wcnss_device.h>
David Collinsb727f7d2011-09-12 11:54:49 -070022#include <linux/workqueue.h>
23#include <linux/jiffies.h>
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -070024#include <linux/gpio.h>
Sameer Thalappilf138da52012-07-30 12:56:37 -070025#include <linux/wakelock.h>
Sameer Thalappileeb8c412012-08-10 17:17:09 -070026#include <linux/delay.h>
Sameer Thalappil8d686d42012-08-24 10:07:31 -070027#include <linux/of.h>
28#include <linux/of_gpio.h>
Sameer Thalappiled3f7da2012-11-01 12:54:01 -070029#include <linux/clk.h>
Sameer Thalappil1b3e6112012-12-14 15:16:07 -080030#include <linux/ratelimit.h>
Sameer Thalappil1878d762013-04-24 14:54:39 -070031#include <linux/kthread.h>
32#include <linux/wait.h>
33#include <linux/uaccess.h>
Sameer Thalappil944c72d2013-08-14 17:56:17 -070034#include <linux/suspend.h>
35#include <linux/rwsem.h>
Naresh Jayaram08cee442013-04-26 19:50:00 +053036#include <linux/mfd/pm8xxx/misc.h>
Sameer Thalappilf6fb1892013-09-06 17:33:30 -070037#include <linux/qpnp/qpnp-adc.h>
Stephen Boyd77db8bb2012-06-27 15:15:16 -070038
Yue Ma10d88752014-02-20 17:12:05 -080039#include <mach/board.h>
Sameer Thalappileeb8c412012-08-10 17:17:09 -070040#include <mach/msm_smd.h>
Sameer Thalappil19e02352012-09-24 15:26:12 -070041#include <mach/msm_iomap.h>
Stephen Boyd77db8bb2012-06-27 15:15:16 -070042#include <mach/subsystem_restart.h>
43
Sameer Thalappil24db5282012-09-10 11:58:33 -070044#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
45#include "wcnss_prealloc.h"
46#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047
48#define DEVICE "wcnss_wlan"
Sameer Thalappil13780d12013-12-19 17:04:21 -080049#define CTRL_DEVICE "wcnss_ctrl"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050#define VERSION "1.01"
51#define WCNSS_PIL_DEVICE "wcnss"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052
Ankur Nandwanib0039b02011-08-09 14:00:45 -070053/* module params */
54#define WCNSS_CONFIG_UNSPECIFIED (-1)
Arif Hussain677c92d2013-11-01 19:09:11 -070055#define UINT32_MAX (0xFFFFFFFFU)
Sameer Thalappileeb8c412012-08-10 17:17:09 -070056
Ankur Nandwania4510492011-08-29 15:56:23 -070057static int has_48mhz_xo = WCNSS_CONFIG_UNSPECIFIED;
Jeff Johnsonb3377e32011-11-18 23:32:27 -080058module_param(has_48mhz_xo, int, S_IWUSR | S_IRUGO);
Ankur Nandwanib0039b02011-08-09 14:00:45 -070059MODULE_PARM_DESC(has_48mhz_xo, "Is an external 48 MHz XO present");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060
Sameer Thalappil1878d762013-04-24 14:54:39 -070061static int has_calibrated_data = WCNSS_CONFIG_UNSPECIFIED;
62module_param(has_calibrated_data, int, S_IWUSR | S_IRUGO);
63MODULE_PARM_DESC(has_calibrated_data, "whether calibrated data file available");
64
Sameer Thalappil58cec312013-05-13 15:52:08 -070065static int has_autodetect_xo = WCNSS_CONFIG_UNSPECIFIED;
66module_param(has_autodetect_xo, int, S_IWUSR | S_IRUGO);
67MODULE_PARM_DESC(has_autodetect_xo, "Perform auto detect to configure IRIS XO");
68
Sheng Fangbb421672013-03-19 08:27:28 +080069static int do_not_cancel_vote = WCNSS_CONFIG_UNSPECIFIED;
70module_param(do_not_cancel_vote, int, S_IWUSR | S_IRUGO);
71MODULE_PARM_DESC(do_not_cancel_vote, "Do not cancel votes for wcnss");
72
Sameer Thalappiled3f7da2012-11-01 12:54:01 -070073static DEFINE_SPINLOCK(reg_spinlock);
74
75#define MSM_RIVA_PHYS 0x03204000
76#define MSM_PRONTO_PHYS 0xfb21b000
77
78#define RIVA_SPARE_OFFSET 0x0b4
79#define RIVA_SUSPEND_BIT BIT(24)
80
Sameer Thalappil77716e52012-11-08 13:41:04 -080081#define MSM_RIVA_CCU_BASE 0x03200800
82
Sameer Thalappil16cae9b2013-03-04 18:02:50 -080083#define CCU_RIVA_INVALID_ADDR_OFFSET 0x100
84#define CCU_RIVA_LAST_ADDR0_OFFSET 0x104
85#define CCU_RIVA_LAST_ADDR1_OFFSET 0x108
86#define CCU_RIVA_LAST_ADDR2_OFFSET 0x10c
Sameer Thalappil77716e52012-11-08 13:41:04 -080087
Sameer Thalappilbcc06182013-05-30 16:18:57 -070088#define PRONTO_PMU_SPARE_OFFSET 0x1088
89
Sameer Thalappil670197c2013-07-19 16:31:14 -070090#define PRONTO_PMU_COM_GDSCR_OFFSET 0x0024
91#define PRONTO_PMU_COM_GDSCR_SW_COLLAPSE BIT(0)
92#define PRONTO_PMU_COM_GDSCR_HW_CTRL BIT(1)
93
94#define PRONTO_PMU_WLAN_BCR_OFFSET 0x0050
95#define PRONTO_PMU_WLAN_BCR_BLK_ARES BIT(0)
96
97#define PRONTO_PMU_WLAN_GDSCR_OFFSET 0x0054
98#define PRONTO_PMU_WLAN_GDSCR_SW_COLLAPSE BIT(0)
99
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700100
101#define PRONTO_PMU_CBCR_OFFSET 0x0008
102#define PRONTO_PMU_CBCR_CLK_EN BIT(0)
103
Mihir Shete2775e4652013-09-20 16:21:49 -0700104#define PRONTO_PMU_COM_CPU_CBCR_OFFSET 0x0030
105#define PRONTO_PMU_COM_AHB_CBCR_OFFSET 0x0034
Mihir Shete31396f42013-10-18 13:34:03 -0700106
107#define PRONTO_PMU_WLAN_AHB_CBCR_OFFSET 0x0074
108#define PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN BIT(0)
109#define PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF BIT(31)
110
111#define PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET 0x0120
112#define PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN BIT(1)
113
Mihir Shete2775e4652013-09-20 16:21:49 -0700114#define PRONTO_PMU_CFG_OFFSET 0x1004
115#define PRONTO_PMU_COM_CSR_OFFSET 0x1040
116#define PRONTO_PMU_SOFT_RESET_OFFSET 0x104C
117
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800118#define MSM_PRONTO_A2XB_BASE 0xfb100400
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800119#define A2XB_CFG_OFFSET 0x00
120#define A2XB_INT_SRC_OFFSET 0x0c
121#define A2XB_TSTBUS_CTRL_OFFSET 0x14
122#define A2XB_TSTBUS_OFFSET 0x18
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800123#define A2XB_ERR_INFO_OFFSET 0x1c
124
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800125#define WCNSS_TSTBUS_CTRL_EN BIT(0)
126#define WCNSS_TSTBUS_CTRL_AXIM (0x02 << 1)
127#define WCNSS_TSTBUS_CTRL_CMDFIFO (0x03 << 1)
128#define WCNSS_TSTBUS_CTRL_WRFIFO (0x04 << 1)
129#define WCNSS_TSTBUS_CTRL_RDFIFO (0x05 << 1)
130#define WCNSS_TSTBUS_CTRL_CTRL (0x07 << 1)
131#define WCNSS_TSTBUS_CTRL_AXIM_CFG0 (0x00 << 6)
132#define WCNSS_TSTBUS_CTRL_AXIM_CFG1 (0x01 << 6)
133#define WCNSS_TSTBUS_CTRL_CTRL_CFG0 (0x00 << 12)
134#define WCNSS_TSTBUS_CTRL_CTRL_CFG1 (0x01 << 12)
135
136#define MSM_PRONTO_CCPU_BASE 0xfb205050
137#define CCU_PRONTO_INVALID_ADDR_OFFSET 0x08
138#define CCU_PRONTO_LAST_ADDR0_OFFSET 0x0c
139#define CCU_PRONTO_LAST_ADDR1_OFFSET 0x10
140#define CCU_PRONTO_LAST_ADDR2_OFFSET 0x14
141
Sameer Thalappil767ff492013-06-19 15:25:21 -0700142#define MSM_PRONTO_SAW2_BASE 0xfb219000
143#define PRONTO_SAW2_SPM_STS_OFFSET 0x0c
144
Sameer Thalappil66d2b392013-07-02 11:32:55 -0700145#define MSM_PRONTO_PLL_BASE 0xfb21b1c0
146#define PRONTO_PLL_STATUS_OFFSET 0x1c
147
Yue Mae490b452013-11-12 00:07:33 -0800148#define MSM_PRONTO_TXP_STATUS 0xfb08040c
Sameer Thalappil670197c2013-07-19 16:31:14 -0700149#define MSM_PRONTO_TXP_PHY_ABORT 0xfb080488
150#define MSM_PRONTO_BRDG_ERR_SRC 0xfb080fb0
151
Sameer Thalappil7f2d82f2013-12-17 11:26:18 -0800152#define MSM_PRONTO_ALARMS_TXCTL 0xfb0120a8
153#define MSM_PRONTO_ALARMS_TACTL 0xfb012448
154
Sameer Thalappild0952f62013-05-03 10:18:29 -0700155#define WCNSS_DEF_WLAN_RX_BUFF_COUNT 1024
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700156#define WCNSS_VBATT_THRESHOLD 3500000
157#define WCNSS_VBATT_GUARD 200
158#define WCNSS_VBATT_HIGH 3700000
159#define WCNSS_VBATT_LOW 3300000
Sameer Thalappild0952f62013-05-03 10:18:29 -0700160
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700161#define WCNSS_CTRL_CHANNEL "WCNSS_CTRL"
Sameer Thalappil1878d762013-04-24 14:54:39 -0700162#define WCNSS_MAX_FRAME_SIZE (4*1024)
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700163#define WCNSS_VERSION_LEN 30
Sameer Thalappild821d632013-11-06 19:24:59 -0800164#define WCNSS_MAX_BUILD_VER_LEN 256
Sameer Thalappil13780d12013-12-19 17:04:21 -0800165#define WCNSS_MAX_CMD_LEN (128)
166#define WCNSS_MIN_CMD_LEN (3)
167#define WCNSS_MIN_SERIAL_LEN (6)
168
169/* control messages from userspace */
170#define WCNSS_USR_CTRL_MSG_START 0x00000000
171#define WCNSS_USR_SERIAL_NUM (WCNSS_USR_CTRL_MSG_START + 1)
172#define WCNSS_USR_HAS_CAL_DATA (WCNSS_USR_CTRL_MSG_START + 2)
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700173
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +0530174#define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x"
175
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700176/* message types */
177#define WCNSS_CTRL_MSG_START 0x01000000
Sameer Thalappil1878d762013-04-24 14:54:39 -0700178#define WCNSS_VERSION_REQ (WCNSS_CTRL_MSG_START + 0)
179#define WCNSS_VERSION_RSP (WCNSS_CTRL_MSG_START + 1)
180#define WCNSS_NVBIN_DNLD_REQ (WCNSS_CTRL_MSG_START + 2)
181#define WCNSS_NVBIN_DNLD_RSP (WCNSS_CTRL_MSG_START + 3)
182#define WCNSS_CALDATA_UPLD_REQ (WCNSS_CTRL_MSG_START + 4)
183#define WCNSS_CALDATA_UPLD_RSP (WCNSS_CTRL_MSG_START + 5)
184#define WCNSS_CALDATA_DNLD_REQ (WCNSS_CTRL_MSG_START + 6)
185#define WCNSS_CALDATA_DNLD_RSP (WCNSS_CTRL_MSG_START + 7)
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700186#define WCNSS_VBATT_LEVEL_IND (WCNSS_CTRL_MSG_START + 8)
Sameer Thalappild821d632013-11-06 19:24:59 -0800187#define WCNSS_BUILD_VER_REQ (WCNSS_CTRL_MSG_START + 9)
188#define WCNSS_BUILD_VER_RSP (WCNSS_CTRL_MSG_START + 10)
Sameer Thalappilf106a682013-02-16 20:41:11 -0800189
Leo Changf98087f2013-11-14 15:08:24 -0800190/* max 20mhz channel count */
191#define WCNSS_MAX_CH_NUM 45
Sameer Thalappilfe03e832014-02-19 10:37:16 -0800192#define WCNSS_MAX_PIL_RETRY 3
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700193
194#define VALID_VERSION(version) \
195 ((strncmp(version, "INVALID", WCNSS_VERSION_LEN)) ? 1 : 0)
196
Sameer Thalappil1878d762013-04-24 14:54:39 -0700197#define FW_CALDATA_CAPABLE() \
198 ((penv->fw_major >= 1) && (penv->fw_minor >= 5) ? 1 : 0)
199
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700200struct smd_msg_hdr {
Sameer Thalappilf106a682013-02-16 20:41:11 -0800201 unsigned int msg_type;
202 unsigned int msg_len;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700203};
204
205struct wcnss_version {
206 struct smd_msg_hdr hdr;
207 unsigned char major;
208 unsigned char minor;
209 unsigned char version;
210 unsigned char revision;
211};
212
Naresh Jayaram08cee442013-04-26 19:50:00 +0530213struct wcnss_pmic_dump {
214 char reg_name[10];
215 u16 reg_addr;
216};
217
218static struct wcnss_pmic_dump wcnss_pmic_reg_dump[] = {
219 {"S2", 0x1D8},
220 {"L4", 0xB4},
221 {"L10", 0xC0},
222 {"LVS2", 0x62},
223 {"S4", 0x1E8},
224 {"LVS7", 0x06C},
225 {"LVS1", 0x060},
226};
227
Sameer Thalappilf106a682013-02-16 20:41:11 -0800228#define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
229
230/*
231 * On SMD channel 4K of maximum data can be transferred, including message
232 * header, so NV fragment size as next multiple of 1Kb is 3Kb.
233 */
234#define NV_FRAGMENT_SIZE 3072
Sameer Thalappil1878d762013-04-24 14:54:39 -0700235#define MAX_CALIBRATED_DATA_SIZE (64*1024)
236#define LAST_FRAGMENT (1 << 0)
237#define MESSAGE_TO_FOLLOW (1 << 1)
238#define CAN_RECEIVE_CALDATA (1 << 15)
239#define WCNSS_RESP_SUCCESS 1
240#define WCNSS_RESP_FAIL 0
241
Sameer Thalappilf106a682013-02-16 20:41:11 -0800242
243/* Macro to find the total number fragments of the NV bin Image */
244#define TOTALFRAGMENTS(x) (((x % NV_FRAGMENT_SIZE) == 0) ? \
245 (x / NV_FRAGMENT_SIZE) : ((x / NV_FRAGMENT_SIZE) + 1))
246
247struct nvbin_dnld_req_params {
248 /*
249 * Fragment sequence number of the NV bin Image. NV Bin Image
250 * might not fit into one message due to size limitation of
251 * the SMD channel FIFO so entire NV blob is chopped into
252 * multiple fragments starting with seqeunce number 0. The
253 * last fragment is indicated by marking is_last_fragment field
254 * to 1. At receiving side, NV blobs would be concatenated
255 * together without any padding bytes in between.
256 */
257 unsigned short frag_number;
258
259 /*
Sameer Thalappil1878d762013-04-24 14:54:39 -0700260 * bit 0: When set to 1 it indicates that no more fragments will
261 * be sent.
262 * bit 1: When set, a new message will be followed by this message
263 * bit 2- bit 14: Reserved
264 * bit 15: when set, it indicates that the sender is capable of
265 * receiving Calibrated data.
Sameer Thalappilf106a682013-02-16 20:41:11 -0800266 */
Sameer Thalappil1878d762013-04-24 14:54:39 -0700267 unsigned short msg_flags;
Sameer Thalappilf106a682013-02-16 20:41:11 -0800268
269 /* NV Image size (number of bytes) */
270 unsigned int nvbin_buffer_size;
271
272 /*
273 * Following the 'nvbin_buffer_size', there should be
274 * nvbin_buffer_size bytes of NV bin Image i.e.
275 * uint8[nvbin_buffer_size].
276 */
277};
278
Sameer Thalappil1878d762013-04-24 14:54:39 -0700279
Sameer Thalappilf106a682013-02-16 20:41:11 -0800280struct nvbin_dnld_req_msg {
281 /*
282 * Note: The length specified in nvbin_dnld_req_msg messages
283 * should be hdr.msg_len = sizeof(nvbin_dnld_req_msg) +
284 * nvbin_buffer_size.
285 */
286 struct smd_msg_hdr hdr;
287 struct nvbin_dnld_req_params dnld_req_params;
288};
289
Sameer Thalappil1878d762013-04-24 14:54:39 -0700290struct cal_data_params {
291
292 /* The total size of the calibrated data, including all the
293 * fragments.
294 */
295 unsigned int total_size;
296 unsigned short frag_number;
297 /*
298 * bit 0: When set to 1 it indicates that no more fragments will
299 * be sent.
300 * bit 1: When set, a new message will be followed by this message
301 * bit 2- bit 15: Reserved
302 */
303 unsigned short msg_flags;
304 /*
305 * fragment size
306 */
307 unsigned int frag_size;
308 /*
309 * Following the frag_size, frag_size of fragmented
310 * data will be followed.
311 */
312};
313
314struct cal_data_msg {
315 /*
316 * The length specified in cal_data_msg should be
317 * hdr.msg_len = sizeof(cal_data_msg) + frag_size
318 */
319 struct smd_msg_hdr hdr;
320 struct cal_data_params cal_params;
321};
322
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700323struct vbatt_level {
324 u32 curr_volt;
325 u32 threshold;
326};
327
328struct vbatt_message {
329 struct smd_msg_hdr hdr;
330 struct vbatt_level vbatt;
331};
332
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700333static struct {
334 struct platform_device *pdev;
335 void *pil;
336 struct resource *mmio_res;
337 struct resource *tx_irq_res;
338 struct resource *rx_irq_res;
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -0700339 struct resource *gpios_5wire;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340 const struct dev_pm_ops *pm_ops;
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800341 int triggered;
342 int smd_channel_ready;
Sameer Thalappild0952f62013-05-03 10:18:29 -0700343 u32 wlan_rx_buff_count;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700344 smd_channel_t *smd_ch;
345 unsigned char wcnss_version[WCNSS_VERSION_LEN];
Sameer Thalappil1878d762013-04-24 14:54:39 -0700346 unsigned char fw_major;
347 unsigned char fw_minor;
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800348 unsigned int serial_number;
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800349 int thermal_mitigation;
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700350 enum wcnss_hw_type wcnss_hw_type;
Sameer Thalappila112bbc2012-05-23 12:20:32 -0700351 void (*tm_notify)(struct device *, int);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700352 struct wcnss_wlan_config wlan_config;
David Collinsb727f7d2011-09-12 11:54:49 -0700353 struct delayed_work wcnss_work;
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700354 struct delayed_work vbatt_work;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700355 struct work_struct wcnssctrl_version_work;
Sameer Thalappilf106a682013-02-16 20:41:11 -0800356 struct work_struct wcnssctrl_nvbin_dnld_work;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700357 struct work_struct wcnssctrl_rx_work;
Sameer Thalappilf138da52012-07-30 12:56:37 -0700358 struct wake_lock wcnss_wake_lock;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -0700359 void __iomem *msm_wcnss_base;
Sameer Thalappild3aad702013-01-22 18:52:24 -0800360 void __iomem *riva_ccu_base;
361 void __iomem *pronto_a2xb_base;
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800362 void __iomem *pronto_ccpu_base;
Sameer Thalappil767ff492013-06-19 15:25:21 -0700363 void __iomem *pronto_saw2_base;
Sameer Thalappil66d2b392013-07-02 11:32:55 -0700364 void __iomem *pronto_pll_base;
Yue Mae490b452013-11-12 00:07:33 -0800365 void __iomem *wlan_tx_status;
Sameer Thalappil670197c2013-07-19 16:31:14 -0700366 void __iomem *wlan_tx_phy_aborts;
367 void __iomem *wlan_brdg_err_source;
Sameer Thalappil7f2d82f2013-12-17 11:26:18 -0800368 void __iomem *alarms_txctl;
369 void __iomem *alarms_tactl;
Sameer Thalappil58281ca2013-04-10 18:50:18 -0700370 void __iomem *fiq_reg;
Sameer Thalappil1878d762013-04-24 14:54:39 -0700371 int nv_downloaded;
372 unsigned char *fw_cal_data;
373 unsigned char *user_cal_data;
374 int fw_cal_rcvd;
375 int fw_cal_exp_frag;
376 int fw_cal_available;
377 int user_cal_read;
378 int user_cal_available;
Arif Hussain677c92d2013-11-01 19:09:11 -0700379 u32 user_cal_rcvd;
Sameer Thalappil1878d762013-04-24 14:54:39 -0700380 int user_cal_exp_size;
381 int device_opened;
Sameer Thalappil1d69b8022013-06-10 19:10:07 -0700382 int iris_xo_mode_set;
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700383 int fw_vbatt_state;
Sameer Thalappil13780d12013-12-19 17:04:21 -0800384 int ctrl_device_opened;
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +0530385 char wlan_nv_macAddr[WLAN_MAC_ADDR_SIZE];
Sameer Thalappil1878d762013-04-24 14:54:39 -0700386 struct mutex dev_lock;
Sameer Thalappil13780d12013-12-19 17:04:21 -0800387 struct mutex ctrl_lock;
Sameer Thalappil1878d762013-04-24 14:54:39 -0700388 wait_queue_head_t read_wait;
Sameer Thalappilf6fb1892013-09-06 17:33:30 -0700389 struct qpnp_adc_tm_btm_param vbat_monitor_params;
390 struct qpnp_adc_tm_chip *adc_tm_dev;
391 struct mutex vbat_monitor_mutex;
Leo Changf98087f2013-11-14 15:08:24 -0800392 u16 unsafe_ch_count;
393 u16 unsafe_ch_list[WCNSS_MAX_CH_NUM];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394} *penv = NULL;
395
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +0530396static ssize_t wcnss_wlan_macaddr_store(struct device *dev,
397 struct device_attribute *attr, const char *buf, size_t count)
398{
399 char macAddr[WLAN_MAC_ADDR_SIZE];
400
401 if (!penv)
402 return -ENODEV;
403
404 pr_debug("%s: Receive MAC Addr From user space: %s\n", __func__, buf);
405
406 if (WLAN_MAC_ADDR_SIZE != sscanf(buf, MAC_ADDRESS_STR,
407 (int *)&macAddr[0], (int *)&macAddr[1],
408 (int *)&macAddr[2], (int *)&macAddr[3],
409 (int *)&macAddr[4], (int *)&macAddr[5])) {
410
411 pr_err("%s: Failed to Copy MAC\n", __func__);
412 return -EINVAL;
413 }
414
415 memcpy(penv->wlan_nv_macAddr, macAddr, sizeof(penv->wlan_nv_macAddr));
416
417 pr_info("%s: Write MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
418 penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
419 penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
420 penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
421
422 return count;
423}
424
425static ssize_t wcnss_wlan_macaddr_show(struct device *dev,
426 struct device_attribute *attr, char *buf)
427{
428 if (!penv)
429 return -ENODEV;
430
431 return scnprintf(buf, PAGE_SIZE, MAC_ADDRESS_STR,
432 penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
433 penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
434 penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
435}
436
437static DEVICE_ATTR(wcnss_mac_addr, S_IRUSR | S_IWUSR,
438 wcnss_wlan_macaddr_show, wcnss_wlan_macaddr_store);
439
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800440static ssize_t wcnss_serial_number_show(struct device *dev,
441 struct device_attribute *attr, char *buf)
442{
443 if (!penv)
444 return -ENODEV;
445
446 return scnprintf(buf, PAGE_SIZE, "%08X\n", penv->serial_number);
447}
448
449static ssize_t wcnss_serial_number_store(struct device *dev,
Sameer Thalappilf138da52012-07-30 12:56:37 -0700450 struct device_attribute *attr, const char *buf, size_t count)
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800451{
452 unsigned int value;
453
454 if (!penv)
455 return -ENODEV;
456
457 if (sscanf(buf, "%08X", &value) != 1)
458 return -EINVAL;
459
460 penv->serial_number = value;
461 return count;
462}
463
464static DEVICE_ATTR(serial_number, S_IRUSR | S_IWUSR,
465 wcnss_serial_number_show, wcnss_serial_number_store);
466
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800467
468static ssize_t wcnss_thermal_mitigation_show(struct device *dev,
469 struct device_attribute *attr, char *buf)
470{
471 if (!penv)
472 return -ENODEV;
473
474 return scnprintf(buf, PAGE_SIZE, "%u\n", penv->thermal_mitigation);
475}
476
477static ssize_t wcnss_thermal_mitigation_store(struct device *dev,
Sameer Thalappilf138da52012-07-30 12:56:37 -0700478 struct device_attribute *attr, const char *buf, size_t count)
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800479{
480 int value;
481
482 if (!penv)
483 return -ENODEV;
484
485 if (sscanf(buf, "%d", &value) != 1)
486 return -EINVAL;
487 penv->thermal_mitigation = value;
Jeff Johnson61374652012-04-16 20:51:48 -0700488 if (penv->tm_notify)
Sameer Thalappila112bbc2012-05-23 12:20:32 -0700489 (penv->tm_notify)(dev, value);
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800490 return count;
491}
492
493static DEVICE_ATTR(thermal_mitigation, S_IRUSR | S_IWUSR,
494 wcnss_thermal_mitigation_show, wcnss_thermal_mitigation_store);
495
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700496
497static ssize_t wcnss_version_show(struct device *dev,
498 struct device_attribute *attr, char *buf)
499{
500 if (!penv)
501 return -ENODEV;
502
503 return scnprintf(buf, PAGE_SIZE, "%s", penv->wcnss_version);
504}
505
506static DEVICE_ATTR(wcnss_version, S_IRUSR,
507 wcnss_version_show, NULL);
508
Naresh Jayaram08cee442013-04-26 19:50:00 +0530509void wcnss_riva_dump_pmic_regs(void)
510{
511 int i, rc;
512 u8 val;
513
514 for (i = 0; i < ARRAY_SIZE(wcnss_pmic_reg_dump); i++) {
515 val = 0;
516 rc = pm8xxx_read_register(wcnss_pmic_reg_dump[i].reg_addr,
517 &val);
518 if (rc)
519 pr_err("PMIC READ: Failed to read addr = %d\n",
520 wcnss_pmic_reg_dump[i].reg_addr);
521 else
522 pr_info_ratelimited("PMIC READ: %s addr = %x, value = %x\n",
523 wcnss_pmic_reg_dump[i].reg_name,
524 wcnss_pmic_reg_dump[i].reg_addr, val);
525 }
526}
527
Sameer Thalappil77716e52012-11-08 13:41:04 -0800528/* wcnss_reset_intr() is invoked when host drivers fails to
529 * communicate with WCNSS over SMD; so logging these registers
Sameer Thalappild3aad702013-01-22 18:52:24 -0800530 * helps to know WCNSS failure reason
531 */
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800532void wcnss_riva_log_debug_regs(void)
Sameer Thalappil77716e52012-11-08 13:41:04 -0800533{
Sameer Thalappil77716e52012-11-08 13:41:04 -0800534 void __iomem *ccu_reg;
535 u32 reg = 0;
536
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800537 ccu_reg = penv->riva_ccu_base + CCU_RIVA_INVALID_ADDR_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800538 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800539 pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800540
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800541 ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR0_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800542 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800543 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800544
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800545 ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR1_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800546 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800547 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800548
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800549 ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR2_OFFSET;
Sameer Thalappil77716e52012-11-08 13:41:04 -0800550 reg = readl_relaxed(ccu_reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800551 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
Naresh Jayaram08cee442013-04-26 19:50:00 +0530552 wcnss_riva_dump_pmic_regs();
Sameer Thalappil77716e52012-11-08 13:41:04 -0800553
Sameer Thalappil77716e52012-11-08 13:41:04 -0800554}
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800555EXPORT_SYMBOL(wcnss_riva_log_debug_regs);
Sameer Thalappil77716e52012-11-08 13:41:04 -0800556
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800557/* Log pronto debug registers before sending reset interrupt */
558void wcnss_pronto_log_debug_regs(void)
559{
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800560 void __iomem *reg_addr, *tst_addr, *tst_ctrl_addr;
Mihir Shete31396f42013-10-18 13:34:03 -0700561 u32 reg = 0, reg2 = 0, reg3 = 0, reg4 = 0;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800562
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700563
564 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SPARE_OFFSET;
565 reg = readl_relaxed(reg_addr);
566 pr_info_ratelimited("%s: PRONTO_PMU_SPARE %08x\n", __func__, reg);
567
Mihir Shete2775e4652013-09-20 16:21:49 -0700568 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CPU_CBCR_OFFSET;
569 reg = readl_relaxed(reg_addr);
570 pr_info_ratelimited("%s: PRONTO_PMU_COM_CPU_CBCR %08x\n",
571 __func__, reg);
572
573 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_AHB_CBCR_OFFSET;
574 reg = readl_relaxed(reg_addr);
575 pr_info_ratelimited("%s: PRONTO_PMU_COM_AHB_CBCR %08x\n",
576 __func__, reg);
577
578 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CFG_OFFSET;
579 reg = readl_relaxed(reg_addr);
580 pr_info_ratelimited("%s: PRONTO_PMU_CFG %08x\n", __func__, reg);
581
582 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CSR_OFFSET;
583 reg = readl_relaxed(reg_addr);
584 pr_info_ratelimited("%s: PRONTO_PMU_COM_CSR %08x\n",
585 __func__, reg);
586
587 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SOFT_RESET_OFFSET;
588 reg = readl_relaxed(reg_addr);
589 pr_info_ratelimited("%s: PRONTO_PMU_SOFT_RESET %08x\n",
590 __func__, reg);
591
Yue Mae490b452013-11-12 00:07:33 -0800592 reg_addr = penv->pronto_saw2_base + PRONTO_SAW2_SPM_STS_OFFSET;
593 reg = readl_relaxed(reg_addr);
594 pr_info_ratelimited("%s: PRONTO_SAW2_SPM_STS %08x\n", __func__, reg);
595
Yue Ma450baae2014-02-19 14:39:09 -0800596 reg_addr = penv->pronto_pll_base + PRONTO_PLL_STATUS_OFFSET;
597 reg = readl_relaxed(reg_addr);
598 pr_err("PRONTO_PLL_STATUS %08x\n", reg);
599
600 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET;
601 reg4 = readl_relaxed(reg_addr);
602 pr_err("PMU_CPU_CMD_RCGR %08x\n", reg4);
603
Sameer Thalappil670197c2013-07-19 16:31:14 -0700604 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_GDSCR_OFFSET;
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700605 reg = readl_relaxed(reg_addr);
Mihir Shete2775e4652013-09-20 16:21:49 -0700606 pr_info_ratelimited("%s: PRONTO_PMU_COM_GDSCR %08x\n",
607 __func__, reg);
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700608 reg >>= 31;
609
610 if (!reg) {
611 pr_info_ratelimited("%s: Cannot log, Pronto common SS is power collapsed\n",
612 __func__);
613 return;
614 }
Sameer Thalappil670197c2013-07-19 16:31:14 -0700615 reg &= ~(PRONTO_PMU_COM_GDSCR_SW_COLLAPSE
616 | PRONTO_PMU_COM_GDSCR_HW_CTRL);
Sameer Thalappilbcc06182013-05-30 16:18:57 -0700617 writel_relaxed(reg, reg_addr);
618
619 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CBCR_OFFSET;
620 reg = readl_relaxed(reg_addr);
621 reg |= PRONTO_PMU_CBCR_CLK_EN;
622 writel_relaxed(reg, reg_addr);
623
Sameer Thalappild3aad702013-01-22 18:52:24 -0800624 reg_addr = penv->pronto_a2xb_base + A2XB_CFG_OFFSET;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800625 reg = readl_relaxed(reg_addr);
626 pr_info_ratelimited("%s: A2XB_CFG_OFFSET %08x\n", __func__, reg);
627
Sameer Thalappild3aad702013-01-22 18:52:24 -0800628 reg_addr = penv->pronto_a2xb_base + A2XB_INT_SRC_OFFSET;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800629 reg = readl_relaxed(reg_addr);
630 pr_info_ratelimited("%s: A2XB_INT_SRC_OFFSET %08x\n", __func__, reg);
631
Sameer Thalappild3aad702013-01-22 18:52:24 -0800632 reg_addr = penv->pronto_a2xb_base + A2XB_ERR_INFO_OFFSET;
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800633 reg = readl_relaxed(reg_addr);
634 pr_info_ratelimited("%s: A2XB_ERR_INFO_OFFSET %08x\n", __func__, reg);
635
Sameer Thalappil16cae9b2013-03-04 18:02:50 -0800636 reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_INVALID_ADDR_OFFSET;
637 reg = readl_relaxed(reg_addr);
638 pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
639
640 reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR0_OFFSET;
641 reg = readl_relaxed(reg_addr);
642 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
643
644 reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR1_OFFSET;
645 reg = readl_relaxed(reg_addr);
646 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
647
648 reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR2_OFFSET;
649 reg = readl_relaxed(reg_addr);
650 pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
651
652 tst_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_OFFSET;
653 tst_ctrl_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_CTRL_OFFSET;
654
655 /* read data FIFO */
656 reg = 0;
657 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_RDFIFO;
658 writel_relaxed(reg, tst_ctrl_addr);
659 reg = readl_relaxed(tst_addr);
660 pr_info_ratelimited("%s: Read data FIFO testbus %08x\n",
661 __func__, reg);
662
663 /* command FIFO */
664 reg = 0;
665 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CMDFIFO;
666 writel_relaxed(reg, tst_ctrl_addr);
667 reg = readl_relaxed(tst_addr);
668 pr_info_ratelimited("%s: Command FIFO testbus %08x\n",
669 __func__, reg);
670
671 /* write data FIFO */
672 reg = 0;
673 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_WRFIFO;
674 writel_relaxed(reg, tst_ctrl_addr);
675 reg = readl_relaxed(tst_addr);
676 pr_info_ratelimited("%s: Rrite data FIFO testbus %08x\n",
677 __func__, reg);
678
679 /* AXIM SEL CFG0 */
680 reg = 0;
681 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_AXIM |
682 WCNSS_TSTBUS_CTRL_AXIM_CFG0;
683 writel_relaxed(reg, tst_ctrl_addr);
684 reg = readl_relaxed(tst_addr);
685 pr_info_ratelimited("%s: AXIM SEL CFG0 testbus %08x\n",
686 __func__, reg);
687
688 /* AXIM SEL CFG1 */
689 reg = 0;
690 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_AXIM |
691 WCNSS_TSTBUS_CTRL_AXIM_CFG1;
692 writel_relaxed(reg, tst_ctrl_addr);
693 reg = readl_relaxed(tst_addr);
694 pr_info_ratelimited("%s: AXIM SEL CFG1 testbus %08x\n",
695 __func__, reg);
696
697 /* CTRL SEL CFG0 */
698 reg = 0;
699 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CTRL |
700 WCNSS_TSTBUS_CTRL_CTRL_CFG0;
701 writel_relaxed(reg, tst_ctrl_addr);
702 reg = readl_relaxed(tst_addr);
703 pr_info_ratelimited("%s: CTRL SEL CFG0 testbus %08x\n",
704 __func__, reg);
705
706 /* CTRL SEL CFG1 */
707 reg = 0;
708 reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CTRL |
709 WCNSS_TSTBUS_CTRL_CTRL_CFG1;
710 writel_relaxed(reg, tst_ctrl_addr);
711 reg = readl_relaxed(tst_addr);
712 pr_info_ratelimited("%s: CTRL SEL CFG1 testbus %08x\n", __func__, reg);
713
Sameer Thalappil670197c2013-07-19 16:31:14 -0700714
715 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_BCR_OFFSET;
716 reg = readl_relaxed(reg_addr);
717
718 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_GDSCR_OFFSET;
719 reg2 = readl_relaxed(reg_addr);
720
Mihir Shete31396f42013-10-18 13:34:03 -0700721 reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_AHB_CBCR_OFFSET;
722 reg3 = readl_relaxed(reg_addr);
723 pr_info_ratelimited("%s: PMU_WLAN_AHB_CBCR %08x\n", __func__, reg3);
724
Sameer Thalappil670197c2013-07-19 16:31:14 -0700725 if ((reg & PRONTO_PMU_WLAN_BCR_BLK_ARES) ||
Mihir Shete31396f42013-10-18 13:34:03 -0700726 (reg2 & PRONTO_PMU_WLAN_GDSCR_SW_COLLAPSE) ||
727 (!(reg4 & PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN)) ||
728 (reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF) ||
729 (!(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN))) {
Sameer Thalappil670197c2013-07-19 16:31:14 -0700730 pr_info_ratelimited("%s: Cannot log, wlan domain is power collapsed\n",
731 __func__);
732 return;
733 }
734
735 reg = readl_relaxed(penv->wlan_tx_phy_aborts);
736 pr_info_ratelimited("%s: WLAN_TX_PHY_ABORTS %08x\n", __func__, reg);
737
738 reg = readl_relaxed(penv->wlan_brdg_err_source);
739 pr_info_ratelimited("%s: WLAN_BRDG_ERR_SOURCE %08x\n", __func__, reg);
740
Yue Mae490b452013-11-12 00:07:33 -0800741 reg = readl_relaxed(penv->wlan_tx_status);
742 pr_info_ratelimited("%s: WLAN_TX_STATUS %08x\n", __func__, reg);
Sameer Thalappil7f2d82f2013-12-17 11:26:18 -0800743
744 reg = readl_relaxed(penv->alarms_txctl);
745 pr_err("ALARMS_TXCTL %08x\n", reg);
746
747 reg = readl_relaxed(penv->alarms_tactl);
748 pr_err("ALARMS_TACTL %08x\n", reg);
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800749}
750EXPORT_SYMBOL(wcnss_pronto_log_debug_regs);
751
Sameer Thalappil36f7a1a2013-10-09 18:32:38 -0700752#ifdef CONFIG_WCNSS_REGISTER_DUMP_ON_BITE
Yue Ma10d88752014-02-20 17:12:05 -0800753static void wcnss_log_iris_regs(void)
754{
755 int i;
756 u32 reg_val;
757 u32 regs_array[] = {
758 0x04, 0x05, 0x11, 0x1e, 0x40, 0x48,
759 0x49, 0x4b, 0x00, 0x01, 0x4d};
760
761 pr_info("IRIS Registers [address] : value\n");
762
763 for (i = 0; i < ARRAY_SIZE(regs_array); i++) {
764 reg_val = wcnss_rf_read_reg(regs_array[i]);
765 pr_info("[0x%08x] : 0x%08x\n", regs_array[i], reg_val);
766 }
767}
768
Sameer Thalappil36f7a1a2013-10-09 18:32:38 -0700769void wcnss_log_debug_regs_on_bite(void)
770{
Yue Madd5e3962013-12-20 18:55:01 -0800771 struct platform_device *pdev = wcnss_get_platform_device();
772 struct clk *measure;
773 struct clk *wcnss_debug_mux;
774 unsigned long clk_rate;
775
776 if (wcnss_hardware_type() != WCNSS_PRONTO_HW)
777 return;
778
779 measure = clk_get(&pdev->dev, "measure");
780 wcnss_debug_mux = clk_get(&pdev->dev, "wcnss_debug");
781
782 if (!IS_ERR(measure) && !IS_ERR(wcnss_debug_mux)) {
783 if (clk_set_parent(measure, wcnss_debug_mux))
784 return;
785
786 clk_rate = clk_get_rate(measure);
787 pr_debug("wcnss: clock frequency is: %luHz\n", clk_rate);
788
Yue Ma10d88752014-02-20 17:12:05 -0800789 if (clk_rate) {
Yue Madd5e3962013-12-20 18:55:01 -0800790 wcnss_pronto_log_debug_regs();
Yue Ma10d88752014-02-20 17:12:05 -0800791 } else {
Yue Madd5e3962013-12-20 18:55:01 -0800792 pr_err("clock frequency is zero, cannot access PMU or other registers\n");
Yue Ma10d88752014-02-20 17:12:05 -0800793 wcnss_log_iris_regs();
794 }
Yue Madd5e3962013-12-20 18:55:01 -0800795 }
Sameer Thalappil36f7a1a2013-10-09 18:32:38 -0700796}
797#endif
798
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800799/* interface to reset wcnss by sending the reset interrupt */
Sameer Thalappil19e02352012-09-24 15:26:12 -0700800void wcnss_reset_intr(void)
801{
Sameer Thalappil1b3e6112012-12-14 15:16:07 -0800802 if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
803 wcnss_pronto_log_debug_regs();
Sameer Thalappil34920762013-03-21 15:43:28 -0700804 wmb();
Sameer Thalappil58281ca2013-04-10 18:50:18 -0700805 __raw_writel(1 << 16, penv->fiq_reg);
Sameer Thalappil34920762013-03-21 15:43:28 -0700806 } else {
807 wcnss_riva_log_debug_regs();
808 wmb();
809 __raw_writel(1 << 24, MSM_APCS_GCC_BASE + 0x8);
Sameer Thalappil19e02352012-09-24 15:26:12 -0700810 }
811}
812EXPORT_SYMBOL(wcnss_reset_intr);
813
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800814static int wcnss_create_sysfs(struct device *dev)
815{
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800816 int ret;
817
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800818 if (!dev)
819 return -ENODEV;
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800820
821 ret = device_create_file(dev, &dev_attr_serial_number);
822 if (ret)
823 return ret;
824
825 ret = device_create_file(dev, &dev_attr_thermal_mitigation);
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700826 if (ret)
827 goto remove_serial;
828
829 ret = device_create_file(dev, &dev_attr_wcnss_version);
830 if (ret)
831 goto remove_thermal;
832
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +0530833 ret = device_create_file(dev, &dev_attr_wcnss_mac_addr);
834 if (ret)
835 goto remove_version;
836
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800837 return 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700838
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +0530839remove_version:
840 device_remove_file(dev, &dev_attr_wcnss_version);
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700841remove_thermal:
842 device_remove_file(dev, &dev_attr_thermal_mitigation);
843remove_serial:
844 device_remove_file(dev, &dev_attr_serial_number);
845
846 return ret;
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800847}
848
849static void wcnss_remove_sysfs(struct device *dev)
850{
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800851 if (dev) {
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800852 device_remove_file(dev, &dev_attr_serial_number);
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800853 device_remove_file(dev, &dev_attr_thermal_mitigation);
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700854 device_remove_file(dev, &dev_attr_wcnss_version);
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +0530855 device_remove_file(dev, &dev_attr_wcnss_mac_addr);
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700856 }
857}
858static void wcnss_smd_notify_event(void *data, unsigned int event)
859{
860 int len = 0;
861
862 if (penv != data) {
863 pr_err("wcnss: invalid env pointer in smd callback\n");
864 return;
865 }
866 switch (event) {
867 case SMD_EVENT_DATA:
868 len = smd_read_avail(penv->smd_ch);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700869 if (len < 0) {
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700870 pr_err("wcnss: failed to read from smd %d\n", len);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700871 return;
872 }
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700873 schedule_work(&penv->wcnssctrl_rx_work);
874 break;
875
876 case SMD_EVENT_OPEN:
877 pr_debug("wcnss: opening WCNSS SMD channel :%s",
878 WCNSS_CTRL_CHANNEL);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700879 schedule_work(&penv->wcnssctrl_version_work);
880
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700881 break;
882
883 case SMD_EVENT_CLOSE:
884 pr_debug("wcnss: closing WCNSS SMD channel :%s",
885 WCNSS_CTRL_CHANNEL);
Sameer Thalappil1878d762013-04-24 14:54:39 -0700886 penv->nv_downloaded = 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -0700887 break;
888
889 default:
890 break;
Jeff Johnson5fda4f82012-01-09 14:15:34 -0800891 }
Jeff Johnson8b1f6d02012-02-03 20:43:26 -0800892}
893
David Collinsb727f7d2011-09-12 11:54:49 -0700894static void wcnss_post_bootup(struct work_struct *work)
895{
Sheng Fangbb421672013-03-19 08:27:28 +0800896 if (do_not_cancel_vote == 1) {
897 pr_info("%s: Keeping APPS vote for Iris & WCNSS\n", __func__);
898 return;
899 }
900
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700901 pr_info("%s: Cancel APPS vote for Iris & WCNSS\n", __func__);
David Collinsb727f7d2011-09-12 11:54:49 -0700902
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700903 /* Since WCNSS is up, cancel any APPS vote for Iris & WCNSS VREGs */
David Collinsb727f7d2011-09-12 11:54:49 -0700904 wcnss_wlan_power(&penv->pdev->dev, &penv->wlan_config,
Sameer Thalappil1d69b8022013-06-10 19:10:07 -0700905 WCNSS_WLAN_SWITCH_OFF, NULL);
Sameer Thalappil8d686d42012-08-24 10:07:31 -0700906
907}
908
909static int
910wcnss_pronto_gpios_config(struct device *dev, bool enable)
911{
912 int rc = 0;
913 int i, j;
914 int WCNSS_WLAN_NUM_GPIOS = 5;
915
916 for (i = 0; i < WCNSS_WLAN_NUM_GPIOS; i++) {
917 int gpio = of_get_gpio(dev->of_node, i);
918 if (enable) {
919 rc = gpio_request(gpio, "wcnss_wlan");
920 if (rc) {
921 pr_err("WCNSS gpio_request %d err %d\n",
922 gpio, rc);
923 goto fail;
924 }
925 } else
926 gpio_free(gpio);
927 }
928
929 return rc;
930
931fail:
932 for (j = WCNSS_WLAN_NUM_GPIOS-1; j >= 0; j--) {
933 int gpio = of_get_gpio(dev->of_node, i);
934 gpio_free(gpio);
935 }
936 return rc;
David Collinsb727f7d2011-09-12 11:54:49 -0700937}
938
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -0700939static int
940wcnss_gpios_config(struct resource *gpios_5wire, bool enable)
941{
942 int i, j;
943 int rc = 0;
944
945 for (i = gpios_5wire->start; i <= gpios_5wire->end; i++) {
946 if (enable) {
947 rc = gpio_request(i, gpios_5wire->name);
948 if (rc) {
949 pr_err("WCNSS gpio_request %d err %d\n", i, rc);
950 goto fail;
951 }
952 } else
953 gpio_free(i);
954 }
955
956 return rc;
957
958fail:
959 for (j = i-1; j >= gpios_5wire->start; j--)
960 gpio_free(j);
961 return rc;
962}
963
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700964static int __devinit
965wcnss_wlan_ctrl_probe(struct platform_device *pdev)
966{
Sameer Thalappil0ab82132013-06-10 10:10:05 -0700967 if (!penv || !penv->triggered)
Sameer Thalappil667f43f2011-10-10 11:12:54 -0700968 return -ENODEV;
969
970 penv->smd_channel_ready = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700971
972 pr_info("%s: SMD ctrl channel up\n", __func__);
973
David Collinsb727f7d2011-09-12 11:54:49 -0700974 /* Schedule a work to do any post boot up activity */
975 INIT_DELAYED_WORK(&penv->wcnss_work, wcnss_post_bootup);
976 schedule_delayed_work(&penv->wcnss_work, msecs_to_jiffies(10000));
977
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700978 return 0;
979}
980
Sameer Thalappil13f45182012-07-23 18:02:45 -0700981void wcnss_flush_delayed_boot_votes()
982{
Sameer Thalappilf106a682013-02-16 20:41:11 -0800983 flush_delayed_work(&penv->wcnss_work);
Sameer Thalappil13f45182012-07-23 18:02:45 -0700984}
985EXPORT_SYMBOL(wcnss_flush_delayed_boot_votes);
986
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700987static int __devexit
988wcnss_wlan_ctrl_remove(struct platform_device *pdev)
989{
990 if (penv)
991 penv->smd_channel_ready = 0;
992
993 pr_info("%s: SMD ctrl channel down\n", __func__);
994
995 return 0;
996}
997
998
999static struct platform_driver wcnss_wlan_ctrl_driver = {
1000 .driver = {
1001 .name = "WLAN_CTRL",
1002 .owner = THIS_MODULE,
1003 },
1004 .probe = wcnss_wlan_ctrl_probe,
1005 .remove = __devexit_p(wcnss_wlan_ctrl_remove),
1006};
1007
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001008static int __devexit
1009wcnss_ctrl_remove(struct platform_device *pdev)
1010{
1011 if (penv && penv->smd_ch)
1012 smd_close(penv->smd_ch);
1013
1014 return 0;
1015}
1016
1017static int __devinit
1018wcnss_ctrl_probe(struct platform_device *pdev)
1019{
1020 int ret = 0;
1021
Sameer Thalappil0ab82132013-06-10 10:10:05 -07001022 if (!penv || !penv->triggered)
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001023 return -ENODEV;
1024
1025 ret = smd_named_open_on_edge(WCNSS_CTRL_CHANNEL, SMD_APPS_WCNSS,
1026 &penv->smd_ch, penv, wcnss_smd_notify_event);
1027 if (ret < 0) {
1028 pr_err("wcnss: cannot open the smd command channel %s: %d\n",
1029 WCNSS_CTRL_CHANNEL, ret);
1030 return -ENODEV;
1031 }
1032 smd_disable_read_intr(penv->smd_ch);
1033
1034 return 0;
1035}
1036
1037/* platform device for WCNSS_CTRL SMD channel */
1038static struct platform_driver wcnss_ctrl_driver = {
1039 .driver = {
1040 .name = "WCNSS_CTRL",
1041 .owner = THIS_MODULE,
1042 },
1043 .probe = wcnss_ctrl_probe,
1044 .remove = __devexit_p(wcnss_ctrl_remove),
1045};
1046
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001047struct device *wcnss_wlan_get_device(void)
1048{
1049 if (penv && penv->pdev && penv->smd_channel_ready)
1050 return &penv->pdev->dev;
1051 return NULL;
1052}
1053EXPORT_SYMBOL(wcnss_wlan_get_device);
1054
Sameer Thalappil409ed352011-12-07 10:53:31 -08001055struct platform_device *wcnss_get_platform_device(void)
1056{
1057 if (penv && penv->pdev)
1058 return penv->pdev;
1059 return NULL;
1060}
1061EXPORT_SYMBOL(wcnss_get_platform_device);
1062
1063struct wcnss_wlan_config *wcnss_get_wlan_config(void)
1064{
1065 if (penv && penv->pdev)
1066 return &penv->wlan_config;
1067 return NULL;
1068}
1069EXPORT_SYMBOL(wcnss_get_wlan_config);
1070
Sameer Thalappil1878d762013-04-24 14:54:39 -07001071int wcnss_device_ready(void)
1072{
1073 if (penv && penv->pdev && penv->nv_downloaded)
1074 return 1;
1075 return 0;
1076}
1077EXPORT_SYMBOL(wcnss_device_ready);
1078
1079
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001080struct resource *wcnss_wlan_get_memory_map(struct device *dev)
1081{
1082 if (penv && dev && (dev == &penv->pdev->dev) && penv->smd_channel_ready)
1083 return penv->mmio_res;
1084 return NULL;
1085}
1086EXPORT_SYMBOL(wcnss_wlan_get_memory_map);
1087
1088int wcnss_wlan_get_dxe_tx_irq(struct device *dev)
1089{
1090 if (penv && dev && (dev == &penv->pdev->dev) &&
1091 penv->tx_irq_res && penv->smd_channel_ready)
1092 return penv->tx_irq_res->start;
1093 return WCNSS_WLAN_IRQ_INVALID;
1094}
1095EXPORT_SYMBOL(wcnss_wlan_get_dxe_tx_irq);
1096
1097int wcnss_wlan_get_dxe_rx_irq(struct device *dev)
1098{
1099 if (penv && dev && (dev == &penv->pdev->dev) &&
1100 penv->rx_irq_res && penv->smd_channel_ready)
1101 return penv->rx_irq_res->start;
1102 return WCNSS_WLAN_IRQ_INVALID;
1103}
1104EXPORT_SYMBOL(wcnss_wlan_get_dxe_rx_irq);
1105
1106void wcnss_wlan_register_pm_ops(struct device *dev,
1107 const struct dev_pm_ops *pm_ops)
1108{
1109 if (penv && dev && (dev == &penv->pdev->dev) && pm_ops)
1110 penv->pm_ops = pm_ops;
1111}
1112EXPORT_SYMBOL(wcnss_wlan_register_pm_ops);
1113
Sameer Thalappilecdd1002011-09-09 10:52:27 -07001114void wcnss_wlan_unregister_pm_ops(struct device *dev,
1115 const struct dev_pm_ops *pm_ops)
1116{
1117 if (penv && dev && (dev == &penv->pdev->dev) && pm_ops) {
1118 if (pm_ops->suspend != penv->pm_ops->suspend ||
1119 pm_ops->resume != penv->pm_ops->resume)
1120 pr_err("PM APIs dont match with registered APIs\n");
1121 penv->pm_ops = NULL;
1122 }
1123}
1124EXPORT_SYMBOL(wcnss_wlan_unregister_pm_ops);
1125
Sameer Thalappila112bbc2012-05-23 12:20:32 -07001126void wcnss_register_thermal_mitigation(struct device *dev,
1127 void (*tm_notify)(struct device *, int))
Jeff Johnson61374652012-04-16 20:51:48 -07001128{
Sameer Thalappila112bbc2012-05-23 12:20:32 -07001129 if (penv && dev && tm_notify)
Jeff Johnson61374652012-04-16 20:51:48 -07001130 penv->tm_notify = tm_notify;
1131}
1132EXPORT_SYMBOL(wcnss_register_thermal_mitigation);
1133
Sameer Thalappila112bbc2012-05-23 12:20:32 -07001134void wcnss_unregister_thermal_mitigation(
1135 void (*tm_notify)(struct device *, int))
Jeff Johnson61374652012-04-16 20:51:48 -07001136{
1137 if (penv && tm_notify) {
1138 if (tm_notify != penv->tm_notify)
1139 pr_err("tm_notify doesn't match registered\n");
1140 penv->tm_notify = NULL;
1141 }
1142}
1143EXPORT_SYMBOL(wcnss_unregister_thermal_mitigation);
1144
Jeff Johnson8b1f6d02012-02-03 20:43:26 -08001145unsigned int wcnss_get_serial_number(void)
1146{
1147 if (penv)
1148 return penv->serial_number;
1149 return 0;
1150}
1151EXPORT_SYMBOL(wcnss_get_serial_number);
1152
Hardik Kantilal Patel0a6ed942013-12-17 19:33:07 +05301153int wcnss_get_wlan_mac_address(char mac_addr[WLAN_MAC_ADDR_SIZE])
1154{
1155 if (!penv)
1156 return -ENODEV;
1157
1158 memcpy(mac_addr, penv->wlan_nv_macAddr, WLAN_MAC_ADDR_SIZE);
1159 pr_debug("%s: Get MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
1160 penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
1161 penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
1162 penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
1163 return 0;
1164}
1165EXPORT_SYMBOL(wcnss_get_wlan_mac_address);
1166
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001167static int enable_wcnss_suspend_notify;
1168
1169static int enable_wcnss_suspend_notify_set(const char *val,
1170 struct kernel_param *kp)
1171{
1172 int ret;
1173
1174 ret = param_set_int(val, kp);
1175 if (ret)
1176 return ret;
1177
1178 if (enable_wcnss_suspend_notify)
1179 pr_debug("Suspend notification activated for wcnss\n");
1180
1181 return 0;
1182}
1183module_param_call(enable_wcnss_suspend_notify, enable_wcnss_suspend_notify_set,
1184 param_get_int, &enable_wcnss_suspend_notify, S_IRUGO | S_IWUSR);
1185
Sameer Thalappil58cec312013-05-13 15:52:08 -07001186int wcnss_xo_auto_detect_enabled(void)
1187{
1188 return (has_autodetect_xo == 1 ? 1 : 0);
1189}
1190
Sameer Thalappil1d69b8022013-06-10 19:10:07 -07001191int wcnss_wlan_iris_xo_mode(void)
1192{
1193 if (penv && penv->pdev && penv->smd_channel_ready)
1194 return penv->iris_xo_mode_set;
1195 return -ENODEV;
1196}
1197EXPORT_SYMBOL(wcnss_wlan_iris_xo_mode);
1198
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001199
1200void wcnss_suspend_notify(void)
1201{
1202 void __iomem *pmu_spare_reg;
1203 u32 reg = 0;
1204 unsigned long flags;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001205
1206 if (!enable_wcnss_suspend_notify)
1207 return;
1208
1209 if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
1210 return;
1211
1212 /* For Riva */
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001213 pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
1214 spin_lock_irqsave(&reg_spinlock, flags);
1215 reg = readl_relaxed(pmu_spare_reg);
1216 reg |= RIVA_SUSPEND_BIT;
1217 writel_relaxed(reg, pmu_spare_reg);
1218 spin_unlock_irqrestore(&reg_spinlock, flags);
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001219}
1220EXPORT_SYMBOL(wcnss_suspend_notify);
1221
1222void wcnss_resume_notify(void)
1223{
1224 void __iomem *pmu_spare_reg;
1225 u32 reg = 0;
1226 unsigned long flags;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001227
1228 if (!enable_wcnss_suspend_notify)
1229 return;
1230
1231 if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
1232 return;
1233
1234 /* For Riva */
1235 pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
1236
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001237 spin_lock_irqsave(&reg_spinlock, flags);
1238 reg = readl_relaxed(pmu_spare_reg);
1239 reg &= ~RIVA_SUSPEND_BIT;
1240 writel_relaxed(reg, pmu_spare_reg);
1241 spin_unlock_irqrestore(&reg_spinlock, flags);
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07001242}
1243EXPORT_SYMBOL(wcnss_resume_notify);
1244
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001245static int wcnss_wlan_suspend(struct device *dev)
1246{
1247 if (penv && dev && (dev == &penv->pdev->dev) &&
1248 penv->smd_channel_ready &&
1249 penv->pm_ops && penv->pm_ops->suspend)
1250 return penv->pm_ops->suspend(dev);
1251 return 0;
1252}
1253
1254static int wcnss_wlan_resume(struct device *dev)
1255{
1256 if (penv && dev && (dev == &penv->pdev->dev) &&
1257 penv->smd_channel_ready &&
1258 penv->pm_ops && penv->pm_ops->resume)
1259 return penv->pm_ops->resume(dev);
1260 return 0;
1261}
1262
Sameer Thalappilf138da52012-07-30 12:56:37 -07001263void wcnss_prevent_suspend()
1264{
1265 if (penv)
1266 wake_lock(&penv->wcnss_wake_lock);
1267}
1268EXPORT_SYMBOL(wcnss_prevent_suspend);
1269
1270void wcnss_allow_suspend()
1271{
1272 if (penv)
1273 wake_unlock(&penv->wcnss_wake_lock);
1274}
1275EXPORT_SYMBOL(wcnss_allow_suspend);
1276
Sameer Thalappil8d686d42012-08-24 10:07:31 -07001277int wcnss_hardware_type(void)
1278{
1279 if (penv)
1280 return penv->wcnss_hw_type;
1281 else
1282 return -ENODEV;
1283}
1284EXPORT_SYMBOL(wcnss_hardware_type);
1285
Sameer Thalappil1878d762013-04-24 14:54:39 -07001286int fw_cal_data_available(void)
Sameer Thalappilcf566bb2013-02-26 17:10:25 -08001287{
1288 if (penv)
Sameer Thalappil1878d762013-04-24 14:54:39 -07001289 return penv->fw_cal_available;
Sameer Thalappilcf566bb2013-02-26 17:10:25 -08001290 else
1291 return -ENODEV;
1292}
Sameer Thalappilcf566bb2013-02-26 17:10:25 -08001293
Sameer Thalappild0952f62013-05-03 10:18:29 -07001294u32 wcnss_get_wlan_rx_buff_count(void)
1295{
1296 if (penv)
1297 return penv->wlan_rx_buff_count;
1298 else
1299 return WCNSS_DEF_WLAN_RX_BUFF_COUNT;
1300
1301}
1302EXPORT_SYMBOL(wcnss_get_wlan_rx_buff_count);
1303
Leo Changf98087f2013-11-14 15:08:24 -08001304int wcnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count)
1305{
1306 if (penv && unsafe_ch_list &&
1307 (ch_count <= WCNSS_MAX_CH_NUM)) {
1308 memcpy((char *)penv->unsafe_ch_list,
1309 (char *)unsafe_ch_list, ch_count * sizeof(u16));
1310 penv->unsafe_ch_count = ch_count;
1311 return 0;
1312 } else
1313 return -ENODEV;
1314}
1315EXPORT_SYMBOL(wcnss_set_wlan_unsafe_channel);
1316
1317int wcnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 buffer_size,
1318 u16 *ch_count)
1319{
1320 if (penv) {
1321 if (buffer_size < penv->unsafe_ch_count * sizeof(u16))
1322 return -ENODEV;
1323 memcpy((char *)unsafe_ch_list,
1324 (char *)penv->unsafe_ch_list,
1325 penv->unsafe_ch_count * sizeof(u16));
1326 *ch_count = penv->unsafe_ch_count;
1327 return 0;
1328 } else
1329 return -ENODEV;
1330}
1331EXPORT_SYMBOL(wcnss_get_wlan_unsafe_channel);
1332
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001333static int wcnss_smd_tx(void *data, int len)
1334{
1335 int ret = 0;
1336
1337 ret = smd_write_avail(penv->smd_ch);
1338 if (ret < len) {
1339 pr_err("wcnss: no space available for smd frame\n");
Sameer Thalappilf106a682013-02-16 20:41:11 -08001340 return -ENOSPC;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001341 }
1342 ret = smd_write(penv->smd_ch, data, len);
1343 if (ret < len) {
1344 pr_err("wcnss: failed to write Command %d", len);
1345 ret = -ENODEV;
1346 }
1347 return ret;
1348}
1349
Sameer Thalappilf6fb1892013-09-06 17:33:30 -07001350static void wcnss_notify_vbat(enum qpnp_tm_state state, void *ctx)
1351{
1352 mutex_lock(&penv->vbat_monitor_mutex);
1353 cancel_delayed_work_sync(&penv->vbatt_work);
1354
1355 if (state == ADC_TM_LOW_STATE) {
1356 pr_debug("wcnss: low voltage notification triggered\n");
1357 penv->vbat_monitor_params.state_request =
1358 ADC_TM_HIGH_THR_ENABLE;
1359 penv->vbat_monitor_params.high_thr = WCNSS_VBATT_THRESHOLD +
1360 WCNSS_VBATT_GUARD;
1361 penv->vbat_monitor_params.low_thr = 0;
1362 } else if (state == ADC_TM_HIGH_STATE) {
1363 penv->vbat_monitor_params.state_request =
1364 ADC_TM_LOW_THR_ENABLE;
1365 penv->vbat_monitor_params.low_thr = WCNSS_VBATT_THRESHOLD -
1366 WCNSS_VBATT_GUARD;
1367 penv->vbat_monitor_params.high_thr = 0;
1368 pr_debug("wcnss: high voltage notification triggered\n");
1369 } else {
1370 pr_debug("wcnss: unknown voltage notification state: %d\n",
1371 state);
1372 mutex_unlock(&penv->vbat_monitor_mutex);
1373 return;
1374 }
1375 pr_debug("wcnss: set low thr to %d and high to %d\n",
1376 penv->vbat_monitor_params.low_thr,
1377 penv->vbat_monitor_params.high_thr);
1378
1379 qpnp_adc_tm_channel_measure(penv->adc_tm_dev,
1380 &penv->vbat_monitor_params);
1381 schedule_delayed_work(&penv->vbatt_work, msecs_to_jiffies(2000));
1382 mutex_unlock(&penv->vbat_monitor_mutex);
1383}
1384
1385static int wcnss_setup_vbat_monitoring(void)
1386{
1387 int rc = -1;
1388
1389 if (!penv->adc_tm_dev) {
1390 pr_err("wcnss: not setting up vbatt\n");
1391 return rc;
1392 }
1393 penv->vbat_monitor_params.low_thr = WCNSS_VBATT_THRESHOLD;
1394 penv->vbat_monitor_params.high_thr = WCNSS_VBATT_THRESHOLD;
1395 penv->vbat_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
1396 penv->vbat_monitor_params.channel = VBAT_SNS;
1397 penv->vbat_monitor_params.btm_ctx = (void *)penv;
1398 penv->vbat_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
1399 penv->vbat_monitor_params.threshold_notification = &wcnss_notify_vbat;
1400 pr_debug("wcnss: set low thr to %d and high to %d\n",
1401 penv->vbat_monitor_params.low_thr,
1402 penv->vbat_monitor_params.high_thr);
1403
1404 rc = qpnp_adc_tm_channel_measure(penv->adc_tm_dev,
1405 &penv->vbat_monitor_params);
1406 if (rc)
1407 pr_err("wcnss: tm setup failed: %d\n", rc);
1408
1409 return rc;
1410}
1411
1412static void wcnss_update_vbatt(struct work_struct *work)
1413{
1414 struct vbatt_message vbatt_msg;
1415 int ret = 0;
1416
1417 vbatt_msg.hdr.msg_type = WCNSS_VBATT_LEVEL_IND;
1418 vbatt_msg.hdr.msg_len = sizeof(struct vbatt_message);
1419 vbatt_msg.vbatt.threshold = WCNSS_VBATT_THRESHOLD;
1420
1421 mutex_lock(&penv->vbat_monitor_mutex);
1422 if (penv->vbat_monitor_params.low_thr &&
1423 (penv->fw_vbatt_state == WCNSS_VBATT_LOW ||
1424 penv->fw_vbatt_state == WCNSS_CONFIG_UNSPECIFIED)) {
1425 vbatt_msg.vbatt.curr_volt = WCNSS_VBATT_HIGH;
1426 penv->fw_vbatt_state = WCNSS_VBATT_HIGH;
1427 pr_debug("wcnss: send HIGH BATT to FW\n");
1428 } else if (!penv->vbat_monitor_params.low_thr &&
1429 (penv->fw_vbatt_state == WCNSS_VBATT_HIGH ||
1430 penv->fw_vbatt_state == WCNSS_CONFIG_UNSPECIFIED)){
1431 vbatt_msg.vbatt.curr_volt = WCNSS_VBATT_LOW;
1432 penv->fw_vbatt_state = WCNSS_VBATT_LOW;
1433 pr_debug("wcnss: send LOW BATT to FW\n");
1434 } else {
1435 mutex_unlock(&penv->vbat_monitor_mutex);
1436 return;
1437 }
1438 mutex_unlock(&penv->vbat_monitor_mutex);
1439 ret = wcnss_smd_tx(&vbatt_msg, vbatt_msg.hdr.msg_len);
1440 if (ret < 0)
1441 pr_err("wcnss: smd tx failed\n");
1442 return;
1443}
1444
1445
Sameer Thalappil1878d762013-04-24 14:54:39 -07001446static unsigned char wcnss_fw_status(void)
1447{
1448 int len = 0;
1449 int rc = 0;
1450
1451 unsigned char fw_status = 0xFF;
1452
1453 len = smd_read_avail(penv->smd_ch);
1454 if (len < 1) {
1455 pr_err("%s: invalid firmware status", __func__);
1456 return fw_status;
1457 }
1458
1459 rc = smd_read(penv->smd_ch, &fw_status, 1);
1460 if (rc < 0) {
1461 pr_err("%s: incomplete data read from smd\n", __func__);
1462 return fw_status;
1463 }
1464 return fw_status;
1465}
1466
1467static void wcnss_send_cal_rsp(unsigned char fw_status)
1468{
1469 struct smd_msg_hdr *rsphdr;
1470 unsigned char *msg = NULL;
1471 int rc;
1472
1473 msg = kmalloc((sizeof(struct smd_msg_hdr) + 1), GFP_KERNEL);
1474 if (NULL == msg) {
1475 pr_err("wcnss: %s: failed to get memory\n", __func__);
1476 return;
1477 }
1478
1479 rsphdr = (struct smd_msg_hdr *)msg;
1480 rsphdr->msg_type = WCNSS_CALDATA_UPLD_RSP;
1481 rsphdr->msg_len = sizeof(struct smd_msg_hdr) + 1;
1482 memcpy(msg+sizeof(struct smd_msg_hdr), &fw_status, 1);
1483
1484 rc = wcnss_smd_tx(msg, rsphdr->msg_len);
1485 if (rc < 0)
1486 pr_err("wcnss: smd tx failed\n");
Sameer Thalappil844d3602013-05-24 13:54:24 -07001487
1488 kfree(msg);
Sameer Thalappil1878d762013-04-24 14:54:39 -07001489}
1490
1491/* Collect calibrated data from WCNSS */
1492void extract_cal_data(int len)
1493{
1494 int rc;
1495 struct cal_data_params calhdr;
1496 unsigned char fw_status = WCNSS_RESP_FAIL;
1497
1498 if (len < sizeof(struct cal_data_params)) {
1499 pr_err("wcnss: incomplete cal header length\n");
1500 return;
1501 }
1502
1503 rc = smd_read(penv->smd_ch, (unsigned char *)&calhdr,
1504 sizeof(struct cal_data_params));
1505 if (rc < sizeof(struct cal_data_params)) {
1506 pr_err("wcnss: incomplete cal header read from smd\n");
1507 return;
1508 }
1509
1510 if (penv->fw_cal_exp_frag != calhdr.frag_number) {
1511 pr_err("wcnss: Invalid frgament");
1512 goto exit;
1513 }
1514
1515 if (calhdr.frag_size > WCNSS_MAX_FRAME_SIZE) {
1516 pr_err("wcnss: Invalid fragment size");
1517 goto exit;
1518 }
1519
Sameer Thalappila5e70ae2013-10-22 13:18:20 -07001520 if (penv->fw_cal_available) {
1521 /* ignore cal upload from SSR */
1522 smd_read(penv->smd_ch, NULL, calhdr.frag_size);
1523 penv->fw_cal_exp_frag++;
1524 if (calhdr.msg_flags & LAST_FRAGMENT) {
1525 penv->fw_cal_exp_frag = 0;
1526 goto exit;
1527 }
1528 return;
1529 }
1530
Sameer Thalappil1878d762013-04-24 14:54:39 -07001531 if (0 == calhdr.frag_number) {
1532 if (calhdr.total_size > MAX_CALIBRATED_DATA_SIZE) {
1533 pr_err("wcnss: Invalid cal data size %d",
1534 calhdr.total_size);
1535 goto exit;
1536 }
1537 kfree(penv->fw_cal_data);
1538 penv->fw_cal_rcvd = 0;
1539 penv->fw_cal_data = kmalloc(calhdr.total_size,
1540 GFP_KERNEL);
1541 if (penv->fw_cal_data == NULL) {
1542 smd_read(penv->smd_ch, NULL, calhdr.frag_size);
1543 goto exit;
1544 }
1545 }
1546
1547 mutex_lock(&penv->dev_lock);
1548 if (penv->fw_cal_rcvd + calhdr.frag_size >
1549 MAX_CALIBRATED_DATA_SIZE) {
1550 pr_err("calibrated data size is more than expected %d",
1551 penv->fw_cal_rcvd + calhdr.frag_size);
1552 penv->fw_cal_exp_frag = 0;
1553 penv->fw_cal_rcvd = 0;
1554 smd_read(penv->smd_ch, NULL, calhdr.frag_size);
1555 goto unlock_exit;
1556 }
1557
1558 rc = smd_read(penv->smd_ch, penv->fw_cal_data + penv->fw_cal_rcvd,
1559 calhdr.frag_size);
1560 if (rc < calhdr.frag_size)
1561 goto unlock_exit;
1562
1563 penv->fw_cal_exp_frag++;
1564 penv->fw_cal_rcvd += calhdr.frag_size;
1565
1566 if (calhdr.msg_flags & LAST_FRAGMENT) {
1567 penv->fw_cal_exp_frag = 0;
1568 penv->fw_cal_available = true;
1569 pr_info("wcnss: cal data collection completed\n");
1570 }
1571 mutex_unlock(&penv->dev_lock);
1572 wake_up(&penv->read_wait);
1573
1574 if (penv->fw_cal_available) {
1575 fw_status = WCNSS_RESP_SUCCESS;
1576 wcnss_send_cal_rsp(fw_status);
1577 }
1578 return;
1579
1580unlock_exit:
1581 mutex_unlock(&penv->dev_lock);
1582
1583exit:
1584 wcnss_send_cal_rsp(fw_status);
1585 return;
1586}
1587
1588
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001589static void wcnssctrl_rx_handler(struct work_struct *worker)
1590{
1591 int len = 0;
1592 int rc = 0;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001593 unsigned char buf[sizeof(struct wcnss_version)];
Sameer Thalappild821d632013-11-06 19:24:59 -08001594 unsigned char build[WCNSS_MAX_BUILD_VER_LEN+1];
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001595 struct smd_msg_hdr *phdr;
Sameer Thalappild821d632013-11-06 19:24:59 -08001596 struct smd_msg_hdr smd_msg;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001597 struct wcnss_version *pversion;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001598 int hw_type;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001599 unsigned char fw_status = 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001600
1601 len = smd_read_avail(penv->smd_ch);
1602 if (len > WCNSS_MAX_FRAME_SIZE) {
1603 pr_err("wcnss: frame larger than the allowed size\n");
1604 smd_read(penv->smd_ch, NULL, len);
1605 return;
1606 }
1607 if (len <= 0)
1608 return;
1609
Sameer Thalappil1878d762013-04-24 14:54:39 -07001610 rc = smd_read(penv->smd_ch, buf, sizeof(struct smd_msg_hdr));
1611 if (rc < sizeof(struct smd_msg_hdr)) {
1612 pr_err("wcnss: incomplete header read from smd\n");
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001613 return;
1614 }
Sameer Thalappil1878d762013-04-24 14:54:39 -07001615 len -= sizeof(struct smd_msg_hdr);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001616
1617 phdr = (struct smd_msg_hdr *)buf;
1618
Sameer Thalappilf106a682013-02-16 20:41:11 -08001619 switch (phdr->msg_type) {
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001620
1621 case WCNSS_VERSION_RSP:
Sameer Thalappil1878d762013-04-24 14:54:39 -07001622 if (len != sizeof(struct wcnss_version)
1623 - sizeof(struct smd_msg_hdr)) {
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001624 pr_err("wcnss: invalid version data from wcnss %d\n",
Sameer Thalappil1878d762013-04-24 14:54:39 -07001625 len);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001626 return;
1627 }
Sameer Thalappil1878d762013-04-24 14:54:39 -07001628 rc = smd_read(penv->smd_ch, buf+sizeof(struct smd_msg_hdr),
1629 len);
1630 if (rc < len) {
1631 pr_err("wcnss: incomplete data read from smd\n");
1632 return;
1633 }
1634 pversion = (struct wcnss_version *)buf;
1635 penv->fw_major = pversion->major;
1636 penv->fw_minor = pversion->minor;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001637 snprintf(penv->wcnss_version, WCNSS_VERSION_LEN,
1638 "%02x%02x%02x%02x", pversion->major, pversion->minor,
1639 pversion->version, pversion->revision);
1640 pr_info("wcnss: version %s\n", penv->wcnss_version);
Sameer Thalappilf106a682013-02-16 20:41:11 -08001641 /* schedule work to download nvbin to ccpu */
1642 hw_type = wcnss_hardware_type();
1643 switch (hw_type) {
1644 case WCNSS_RIVA_HW:
1645 /* supported only if riva major >= 1 and minor >= 4 */
1646 if ((pversion->major >= 1) && (pversion->minor >= 4)) {
1647 pr_info("wcnss: schedule dnld work for riva\n");
1648 schedule_work(&penv->wcnssctrl_nvbin_dnld_work);
1649 }
1650 break;
1651
1652 case WCNSS_PRONTO_HW:
Sameer Thalappild821d632013-11-06 19:24:59 -08001653 smd_msg.msg_type = WCNSS_BUILD_VER_REQ;
1654 smd_msg.msg_len = sizeof(smd_msg);
1655 rc = wcnss_smd_tx(&smd_msg, smd_msg.msg_len);
1656 if (rc < 0)
1657 pr_err("wcnss: smd tx failed: %s\n", __func__);
1658
Sameer Thalappilf106a682013-02-16 20:41:11 -08001659 /* supported only if pronto major >= 1 and minor >= 4 */
1660 if ((pversion->major >= 1) && (pversion->minor >= 4)) {
1661 pr_info("wcnss: schedule dnld work for pronto\n");
1662 schedule_work(&penv->wcnssctrl_nvbin_dnld_work);
1663 }
1664 break;
1665
1666 default:
1667 pr_info("wcnss: unknown hw type (%d), will not schedule dnld work\n",
1668 hw_type);
1669 break;
1670 }
1671 break;
1672
Sameer Thalappild821d632013-11-06 19:24:59 -08001673 case WCNSS_BUILD_VER_RSP:
1674 if (len > WCNSS_MAX_BUILD_VER_LEN) {
1675 pr_err("wcnss: invalid build version data from wcnss %d\n",
1676 len);
1677 return;
1678 }
1679 rc = smd_read(penv->smd_ch, build, len);
1680 if (rc < len) {
1681 pr_err("wcnss: incomplete data read from smd\n");
1682 return;
1683 }
1684 build[len] = 0;
1685 pr_info("wcnss: build version %s\n", build);
1686 break;
1687
Sameer Thalappilf106a682013-02-16 20:41:11 -08001688 case WCNSS_NVBIN_DNLD_RSP:
Sameer Thalappil1878d762013-04-24 14:54:39 -07001689 penv->nv_downloaded = true;
1690 fw_status = wcnss_fw_status();
1691 pr_debug("wcnss: received WCNSS_NVBIN_DNLD_RSP from ccpu %u\n",
1692 fw_status);
Sameer Thalappilf6fb1892013-09-06 17:33:30 -07001693 wcnss_setup_vbat_monitoring();
Sameer Thalappil1878d762013-04-24 14:54:39 -07001694 break;
1695
1696 case WCNSS_CALDATA_DNLD_RSP:
1697 penv->nv_downloaded = true;
1698 fw_status = wcnss_fw_status();
1699 pr_debug("wcnss: received WCNSS_CALDATA_DNLD_RSP from ccpu %u\n",
1700 fw_status);
1701 break;
1702
1703 case WCNSS_CALDATA_UPLD_REQ:
Sameer Thalappil1878d762013-04-24 14:54:39 -07001704 extract_cal_data(len);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001705 break;
1706
1707 default:
Sameer Thalappilf106a682013-02-16 20:41:11 -08001708 pr_err("wcnss: invalid message type %d\n", phdr->msg_type);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001709 }
1710 return;
1711}
1712
1713static void wcnss_send_version_req(struct work_struct *worker)
1714{
1715 struct smd_msg_hdr smd_msg;
1716 int ret = 0;
1717
Sameer Thalappilf106a682013-02-16 20:41:11 -08001718 smd_msg.msg_type = WCNSS_VERSION_REQ;
1719 smd_msg.msg_len = sizeof(smd_msg);
1720 ret = wcnss_smd_tx(&smd_msg, smd_msg.msg_len);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07001721 if (ret < 0)
1722 pr_err("wcnss: smd tx failed\n");
1723
1724 return;
1725}
1726
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001727static DECLARE_RWSEM(wcnss_pm_sem);
Sameer Thalappil1878d762013-04-24 14:54:39 -07001728
1729static void wcnss_nvbin_dnld(void)
Sameer Thalappilf106a682013-02-16 20:41:11 -08001730{
1731 int ret = 0;
1732 struct nvbin_dnld_req_msg *dnld_req_msg;
1733 unsigned short total_fragments = 0;
1734 unsigned short count = 0;
1735 unsigned short retry_count = 0;
1736 unsigned short cur_frag_size = 0;
1737 unsigned char *outbuffer = NULL;
1738 const void *nv_blob_addr = NULL;
1739 unsigned int nv_blob_size = 0;
1740 const struct firmware *nv = NULL;
Sameer Thalappilf0d89ba2013-03-11 17:33:20 -07001741 struct device *dev = &penv->pdev->dev;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001742
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001743 down_read(&wcnss_pm_sem);
1744
Sameer Thalappilf106a682013-02-16 20:41:11 -08001745 ret = request_firmware(&nv, NVBIN_FILE, dev);
1746
1747 if (ret || !nv || !nv->data || !nv->size) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07001748 pr_err("wcnss: %s: request_firmware failed for %s\n",
1749 __func__, NVBIN_FILE);
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001750 goto out;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001751 }
1752
1753 /*
1754 * First 4 bytes in nv blob is validity bitmap.
1755 * We cannot validate nv, so skip those 4 bytes.
1756 */
1757 nv_blob_addr = nv->data + 4;
1758 nv_blob_size = nv->size - 4;
1759
1760 total_fragments = TOTALFRAGMENTS(nv_blob_size);
1761
1762 pr_info("wcnss: NV bin size: %d, total_fragments: %d\n",
1763 nv_blob_size, total_fragments);
1764
1765 /* get buffer for nv bin dnld req message */
1766 outbuffer = kmalloc((sizeof(struct nvbin_dnld_req_msg) +
1767 NV_FRAGMENT_SIZE), GFP_KERNEL);
1768
1769 if (NULL == outbuffer) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07001770 pr_err("wcnss: %s: failed to get buffer\n", __func__);
Sameer Thalappilf106a682013-02-16 20:41:11 -08001771 goto err_free_nv;
1772 }
1773
1774 dnld_req_msg = (struct nvbin_dnld_req_msg *)outbuffer;
1775
1776 dnld_req_msg->hdr.msg_type = WCNSS_NVBIN_DNLD_REQ;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001777 dnld_req_msg->dnld_req_params.msg_flags = 0;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001778
1779 for (count = 0; count < total_fragments; count++) {
1780 dnld_req_msg->dnld_req_params.frag_number = count;
1781
1782 if (count == (total_fragments - 1)) {
1783 /* last fragment, take care of boundry condition */
1784 cur_frag_size = nv_blob_size % NV_FRAGMENT_SIZE;
1785 if (!cur_frag_size)
1786 cur_frag_size = NV_FRAGMENT_SIZE;
1787
Sameer Thalappil1878d762013-04-24 14:54:39 -07001788 dnld_req_msg->dnld_req_params.msg_flags |=
1789 LAST_FRAGMENT;
1790 dnld_req_msg->dnld_req_params.msg_flags |=
1791 CAN_RECEIVE_CALDATA;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001792 } else {
1793 cur_frag_size = NV_FRAGMENT_SIZE;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001794 dnld_req_msg->dnld_req_params.msg_flags &=
1795 ~LAST_FRAGMENT;
Sameer Thalappilf106a682013-02-16 20:41:11 -08001796 }
1797
1798 dnld_req_msg->dnld_req_params.nvbin_buffer_size =
1799 cur_frag_size;
1800
1801 dnld_req_msg->hdr.msg_len =
1802 sizeof(struct nvbin_dnld_req_msg) + cur_frag_size;
1803
1804 /* copy NV fragment */
1805 memcpy((outbuffer + sizeof(struct nvbin_dnld_req_msg)),
1806 (nv_blob_addr + count * NV_FRAGMENT_SIZE),
1807 cur_frag_size);
1808
1809 ret = wcnss_smd_tx(outbuffer, dnld_req_msg->hdr.msg_len);
1810
1811 retry_count = 0;
1812 while ((ret == -ENOSPC) && (retry_count <= 3)) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07001813 pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
1814 __func__);
Sameer Thalappilf106a682013-02-16 20:41:11 -08001815 pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
1816 count, dnld_req_msg->hdr.msg_len,
1817 total_fragments, retry_count);
1818
1819 /* wait and try again */
1820 msleep(20);
1821 retry_count++;
1822 ret = wcnss_smd_tx(outbuffer,
1823 dnld_req_msg->hdr.msg_len);
1824 }
1825
1826 if (ret < 0) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07001827 pr_err("wcnss: %s: smd tx failed\n", __func__);
Sameer Thalappilf106a682013-02-16 20:41:11 -08001828 pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
1829 count, dnld_req_msg->hdr.msg_len,
1830 total_fragments, retry_count);
1831 goto err_dnld;
1832 }
1833 }
1834
1835err_dnld:
1836 /* free buffer */
1837 kfree(outbuffer);
1838
1839err_free_nv:
1840 /* release firmware */
1841 release_firmware(nv);
1842
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001843out:
1844 up_read(&wcnss_pm_sem);
1845
Sameer Thalappilf106a682013-02-16 20:41:11 -08001846 return;
1847}
1848
Sameer Thalappil1878d762013-04-24 14:54:39 -07001849
1850static void wcnss_caldata_dnld(const void *cal_data,
1851 unsigned int cal_data_size, bool msg_to_follow)
1852{
1853 int ret = 0;
1854 struct cal_data_msg *cal_msg;
1855 unsigned short total_fragments = 0;
1856 unsigned short count = 0;
1857 unsigned short retry_count = 0;
1858 unsigned short cur_frag_size = 0;
1859 unsigned char *outbuffer = NULL;
1860
1861 total_fragments = TOTALFRAGMENTS(cal_data_size);
1862
1863 outbuffer = kmalloc((sizeof(struct cal_data_msg) +
1864 NV_FRAGMENT_SIZE), GFP_KERNEL);
1865
1866 if (NULL == outbuffer) {
1867 pr_err("wcnss: %s: failed to get buffer\n", __func__);
1868 return;
1869 }
1870
1871 cal_msg = (struct cal_data_msg *)outbuffer;
1872
1873 cal_msg->hdr.msg_type = WCNSS_CALDATA_DNLD_REQ;
1874 cal_msg->cal_params.msg_flags = 0;
1875
1876 for (count = 0; count < total_fragments; count++) {
1877 cal_msg->cal_params.frag_number = count;
1878
1879 if (count == (total_fragments - 1)) {
1880 cur_frag_size = cal_data_size % NV_FRAGMENT_SIZE;
1881 if (!cur_frag_size)
1882 cur_frag_size = NV_FRAGMENT_SIZE;
1883
1884 cal_msg->cal_params.msg_flags
1885 |= LAST_FRAGMENT;
1886 if (msg_to_follow)
1887 cal_msg->cal_params.msg_flags |=
1888 MESSAGE_TO_FOLLOW;
1889 } else {
1890 cur_frag_size = NV_FRAGMENT_SIZE;
1891 cal_msg->cal_params.msg_flags &=
1892 ~LAST_FRAGMENT;
1893 }
1894
1895 cal_msg->cal_params.total_size = cal_data_size;
1896 cal_msg->cal_params.frag_size =
1897 cur_frag_size;
1898
1899 cal_msg->hdr.msg_len =
1900 sizeof(struct cal_data_msg) + cur_frag_size;
1901
1902 memcpy((outbuffer + sizeof(struct cal_data_msg)),
1903 (cal_data + count * NV_FRAGMENT_SIZE),
1904 cur_frag_size);
1905
1906 ret = wcnss_smd_tx(outbuffer, cal_msg->hdr.msg_len);
1907
1908 retry_count = 0;
1909 while ((ret == -ENOSPC) && (retry_count <= 3)) {
1910 pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
1911 __func__);
1912 pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
1913 count, cal_msg->hdr.msg_len,
1914 total_fragments, retry_count);
1915
1916 /* wait and try again */
1917 msleep(20);
1918 retry_count++;
1919 ret = wcnss_smd_tx(outbuffer,
1920 cal_msg->hdr.msg_len);
1921 }
1922
1923 if (ret < 0) {
1924 pr_err("wcnss: %s: smd tx failed\n", __func__);
1925 pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
1926 count, cal_msg->hdr.msg_len,
1927 total_fragments, retry_count);
1928 goto err_dnld;
1929 }
1930 }
1931
1932
1933err_dnld:
1934 /* free buffer */
1935 kfree(outbuffer);
1936
1937 return;
1938}
1939
1940
1941static void wcnss_nvbin_dnld_main(struct work_struct *worker)
1942{
1943 int retry = 0;
1944
1945 if (!FW_CALDATA_CAPABLE())
1946 goto nv_download;
1947
1948 if (!penv->fw_cal_available && WCNSS_CONFIG_UNSPECIFIED
1949 != has_calibrated_data && !penv->user_cal_available) {
1950 while (!penv->user_cal_available && retry++ < 5)
1951 msleep(500);
1952 }
Sameer Thalappila5e70ae2013-10-22 13:18:20 -07001953 if (penv->fw_cal_available) {
1954 pr_info_ratelimited("wcnss: cal download, using fw cal");
1955 wcnss_caldata_dnld(penv->fw_cal_data, penv->fw_cal_rcvd, true);
Sameer Thalappil1878d762013-04-24 14:54:39 -07001956
1957 } else if (penv->user_cal_available) {
Sameer Thalappila5e70ae2013-10-22 13:18:20 -07001958 pr_info_ratelimited("wcnss: cal download, using user cal");
Sameer Thalappil1878d762013-04-24 14:54:39 -07001959 wcnss_caldata_dnld(penv->user_cal_data,
1960 penv->user_cal_rcvd, true);
1961 }
1962
1963nv_download:
1964 pr_info_ratelimited("wcnss: NV download");
1965 wcnss_nvbin_dnld();
1966
1967 return;
1968}
1969
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001970static int wcnss_pm_notify(struct notifier_block *b,
1971 unsigned long event, void *p)
1972{
1973 switch (event) {
1974 case PM_SUSPEND_PREPARE:
1975 down_write(&wcnss_pm_sem);
1976 break;
Sameer Thalappil1878d762013-04-24 14:54:39 -07001977
Sameer Thalappil944c72d2013-08-14 17:56:17 -07001978 case PM_POST_SUSPEND:
1979 up_write(&wcnss_pm_sem);
1980 break;
1981 }
1982
1983 return NOTIFY_DONE;
1984}
1985
1986static struct notifier_block wcnss_pm_notifier = {
1987 .notifier_call = wcnss_pm_notify,
1988};
Sameer Thalappil1878d762013-04-24 14:54:39 -07001989
Sameer Thalappil13780d12013-12-19 17:04:21 -08001990static int wcnss_ctrl_open(struct inode *inode, struct file *file)
1991{
1992 int rc = 0;
1993
1994 if (!penv || penv->ctrl_device_opened)
1995 return -EFAULT;
1996
1997 penv->ctrl_device_opened = 1;
1998
1999 return rc;
2000}
2001
2002
2003void process_usr_ctrl_cmd(u8 *buf, size_t len)
2004{
2005 u16 cmd = buf[0] << 8 | buf[1];
2006
2007 switch (cmd) {
2008
2009 case WCNSS_USR_SERIAL_NUM:
2010 if (WCNSS_MIN_SERIAL_LEN > len) {
2011 pr_err("%s: Invalid serial number\n", __func__);
2012 return;
2013 }
2014 penv->serial_number = buf[2] << 24 | buf[3] << 16
2015 | buf[4] << 8 | buf[5];
2016 break;
2017
2018 case WCNSS_USR_HAS_CAL_DATA:
2019 if (1 < buf[2])
2020 pr_err("%s: Invalid data for cal %d\n", __func__,
2021 buf[2]);
2022 has_calibrated_data = buf[2];
2023 break;
2024
2025 default:
2026 pr_err("%s: Invalid command %d\n", __func__, cmd);
2027 break;
2028 }
2029}
2030
2031static ssize_t wcnss_ctrl_write(struct file *fp, const char __user
2032 *user_buffer, size_t count, loff_t *position)
2033{
2034 int rc = 0;
2035 u8 buf[WCNSS_MAX_CMD_LEN];
2036
2037 if (!penv || !penv->ctrl_device_opened || WCNSS_MAX_CMD_LEN < count
2038 || WCNSS_MIN_CMD_LEN > count)
2039 return -EFAULT;
2040
2041 mutex_lock(&penv->ctrl_lock);
2042 rc = copy_from_user(buf, user_buffer, count);
2043 if (0 == rc)
2044 process_usr_ctrl_cmd(buf, count);
2045
2046 mutex_unlock(&penv->ctrl_lock);
2047
2048 return rc;
2049}
2050
2051
2052static const struct file_operations wcnss_ctrl_fops = {
2053 .owner = THIS_MODULE,
2054 .open = wcnss_ctrl_open,
2055 .write = wcnss_ctrl_write,
2056};
2057
2058static struct miscdevice wcnss_usr_ctrl = {
2059 .minor = MISC_DYNAMIC_MINOR,
2060 .name = CTRL_DEVICE,
2061 .fops = &wcnss_ctrl_fops,
2062};
2063
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002064static int
2065wcnss_trigger_config(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002066{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002067 int ret;
Ankur Nandwanib0039b02011-08-09 14:00:45 -07002068 struct qcom_wcnss_opts *pdata;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002069 unsigned long wcnss_phys_addr;
2070 int size = 0;
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002071 struct resource *res;
Sameer Thalappilfe03e832014-02-19 10:37:16 -08002072 int pil_retry = 0;
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002073 int has_pronto_hw = of_property_read_bool(pdev->dev.of_node,
Sameer Thalappil820f87b2013-05-21 20:40:54 -07002074 "qcom,has-pronto-hw");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002075
Sameer Thalappild0952f62013-05-03 10:18:29 -07002076 if (of_property_read_u32(pdev->dev.of_node,
2077 "qcom,wlan-rx-buff-count", &penv->wlan_rx_buff_count)) {
2078 penv->wlan_rx_buff_count = WCNSS_DEF_WLAN_RX_BUFF_COUNT;
2079 }
2080
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002081 /* make sure we are only triggered once */
2082 if (penv->triggered)
2083 return 0;
2084 penv->triggered = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002085
Ankur Nandwanib0039b02011-08-09 14:00:45 -07002086 /* initialize the WCNSS device configuration */
2087 pdata = pdev->dev.platform_data;
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002088 if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo) {
2089 if (has_pronto_hw) {
2090 has_48mhz_xo = of_property_read_bool(pdev->dev.of_node,
Sameer Thalappil820f87b2013-05-21 20:40:54 -07002091 "qcom,has-48mhz-xo");
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002092 } else {
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002093 has_48mhz_xo = pdata->has_48mhz_xo;
2094 }
2095 }
Sameer Thalappilf00dbeb2013-03-01 18:33:30 -08002096 penv->wcnss_hw_type = (has_pronto_hw) ? WCNSS_PRONTO_HW : WCNSS_RIVA_HW;
Ankur Nandwanib0039b02011-08-09 14:00:45 -07002097 penv->wlan_config.use_48mhz_xo = has_48mhz_xo;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002098
Sameer Thalappil58cec312013-05-13 15:52:08 -07002099 if (WCNSS_CONFIG_UNSPECIFIED == has_autodetect_xo && has_pronto_hw) {
2100 has_autodetect_xo = of_property_read_bool(pdev->dev.of_node,
Sameer Thalappil820f87b2013-05-21 20:40:54 -07002101 "qcom,has-autodetect-xo");
Sameer Thalappil58cec312013-05-13 15:52:08 -07002102 }
2103
Jeff Johnson5fda4f82012-01-09 14:15:34 -08002104 penv->thermal_mitigation = 0;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002105 strlcpy(penv->wcnss_version, "INVALID", WCNSS_VERSION_LEN);
Jeff Johnson5fda4f82012-01-09 14:15:34 -08002106
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -07002107 /* Configure 5 wire GPIOs */
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002108 if (!has_pronto_hw) {
2109 penv->gpios_5wire = platform_get_resource_byname(pdev,
2110 IORESOURCE_IO, "wcnss_gpios_5wire");
2111
2112 /* allocate 5-wire GPIO resources */
2113 if (!penv->gpios_5wire) {
2114 dev_err(&pdev->dev, "insufficient IO resources\n");
2115 ret = -ENOENT;
2116 goto fail_gpio_res;
2117 }
2118 ret = wcnss_gpios_config(penv->gpios_5wire, true);
2119 } else
2120 ret = wcnss_pronto_gpios_config(&pdev->dev, true);
2121
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -07002122 if (ret) {
2123 dev_err(&pdev->dev, "WCNSS gpios config failed.\n");
2124 goto fail_gpio_res;
2125 }
2126
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002127 /* power up the WCNSS */
2128 ret = wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
Sameer Thalappil1d69b8022013-06-10 19:10:07 -07002129 WCNSS_WLAN_SWITCH_ON,
2130 &penv->iris_xo_mode_set);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002131 if (ret) {
2132 dev_err(&pdev->dev, "WCNSS Power-up failed.\n");
2133 goto fail_power;
2134 }
2135
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002136 /* allocate resources */
2137 penv->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
2138 "wcnss_mmio");
2139 penv->tx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
2140 "wcnss_wlantx_irq");
2141 penv->rx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
2142 "wcnss_wlanrx_irq");
2143
2144 if (!(penv->mmio_res && penv->tx_irq_res && penv->rx_irq_res)) {
2145 dev_err(&pdev->dev, "insufficient resources\n");
2146 ret = -ENOENT;
2147 goto fail_res;
2148 }
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002149 INIT_WORK(&penv->wcnssctrl_rx_work, wcnssctrl_rx_handler);
2150 INIT_WORK(&penv->wcnssctrl_version_work, wcnss_send_version_req);
Sameer Thalappil1878d762013-04-24 14:54:39 -07002151 INIT_WORK(&penv->wcnssctrl_nvbin_dnld_work, wcnss_nvbin_dnld_main);
Jeff Johnson8b1f6d02012-02-03 20:43:26 -08002152
Sameer Thalappilf138da52012-07-30 12:56:37 -07002153 wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss");
2154
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002155 if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
2156 size = 0x3000;
2157 wcnss_phys_addr = MSM_PRONTO_PHYS;
2158 } else {
2159 wcnss_phys_addr = MSM_RIVA_PHYS;
2160 size = SZ_256;
2161 }
2162
2163 penv->msm_wcnss_base = ioremap(wcnss_phys_addr, size);
2164 if (!penv->msm_wcnss_base) {
2165 ret = -ENOMEM;
2166 pr_err("%s: ioremap wcnss physical failed\n", __func__);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002167 goto fail_ioremap;
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002168 }
2169
Sameer Thalappild3aad702013-01-22 18:52:24 -08002170 if (wcnss_hardware_type() == WCNSS_RIVA_HW) {
2171 penv->riva_ccu_base = ioremap(MSM_RIVA_CCU_BASE, SZ_512);
2172 if (!penv->riva_ccu_base) {
2173 ret = -ENOMEM;
2174 pr_err("%s: ioremap wcnss physical failed\n", __func__);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002175 goto fail_ioremap2;
Sameer Thalappild3aad702013-01-22 18:52:24 -08002176 }
2177 } else {
2178 penv->pronto_a2xb_base = ioremap(MSM_PRONTO_A2XB_BASE, SZ_512);
2179 if (!penv->pronto_a2xb_base) {
2180 ret = -ENOMEM;
2181 pr_err("%s: ioremap wcnss physical failed\n", __func__);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002182 goto fail_ioremap2;
Sameer Thalappild3aad702013-01-22 18:52:24 -08002183 }
Sameer Thalappil16cae9b2013-03-04 18:02:50 -08002184 penv->pronto_ccpu_base = ioremap(MSM_PRONTO_CCPU_BASE, SZ_512);
2185 if (!penv->pronto_ccpu_base) {
2186 ret = -ENOMEM;
2187 pr_err("%s: ioremap wcnss physical failed\n", __func__);
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002188 goto fail_ioremap3;
Sameer Thalappil16cae9b2013-03-04 18:02:50 -08002189 }
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002190 /* for reset FIQ */
2191 res = platform_get_resource_byname(penv->pdev,
2192 IORESOURCE_MEM, "wcnss_fiq");
2193 if (!res) {
2194 dev_err(&pdev->dev, "insufficient irq mem resources\n");
2195 ret = -ENOENT;
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002196 goto fail_ioremap4;
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002197 }
2198 penv->fiq_reg = ioremap_nocache(res->start, resource_size(res));
2199 if (!penv->fiq_reg) {
2200 pr_err("wcnss: %s: ioremap_nocache() failed fiq_reg addr:%pr\n",
2201 __func__, &res->start);
2202 ret = -ENOMEM;
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002203 goto fail_ioremap4;
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002204 }
Sameer Thalappil767ff492013-06-19 15:25:21 -07002205 penv->pronto_saw2_base = ioremap_nocache(MSM_PRONTO_SAW2_BASE,
2206 SZ_32);
2207 if (!penv->pronto_saw2_base) {
2208 pr_err("%s: ioremap wcnss physical(saw2) failed\n",
2209 __func__);
2210 ret = -ENOMEM;
2211 goto fail_ioremap5;
2212 }
Sameer Thalappil66d2b392013-07-02 11:32:55 -07002213 penv->pronto_pll_base = ioremap_nocache(MSM_PRONTO_PLL_BASE,
2214 SZ_64);
2215 if (!penv->pronto_pll_base) {
2216 pr_err("%s: ioremap wcnss physical(pll) failed\n",
2217 __func__);
2218 ret = -ENOMEM;
2219 goto fail_ioremap6;
2220 }
2221
Sameer Thalappil670197c2013-07-19 16:31:14 -07002222 penv->wlan_tx_phy_aborts = ioremap(MSM_PRONTO_TXP_PHY_ABORT,
2223 SZ_8);
2224 if (!penv->wlan_tx_phy_aborts) {
2225 ret = -ENOMEM;
2226 pr_err("%s: ioremap wlan TX PHY failed\n", __func__);
2227 goto fail_ioremap7;
2228 }
2229 penv->wlan_brdg_err_source = ioremap(MSM_PRONTO_BRDG_ERR_SRC,
2230 SZ_8);
2231 if (!penv->wlan_brdg_err_source) {
2232 ret = -ENOMEM;
2233 pr_err("%s: ioremap wlan BRDG ERR failed\n", __func__);
2234 goto fail_ioremap8;
2235 }
Yue Mae490b452013-11-12 00:07:33 -08002236 penv->wlan_tx_status = ioremap(MSM_PRONTO_TXP_STATUS, SZ_8);
2237 if (!penv->wlan_tx_status) {
2238 ret = -ENOMEM;
2239 pr_err("%s: ioremap wlan TX STATUS failed\n", __func__);
2240 goto fail_ioremap9;
2241 }
Sameer Thalappil7f2d82f2013-12-17 11:26:18 -08002242 penv->alarms_txctl = ioremap(MSM_PRONTO_ALARMS_TXCTL, SZ_8);
2243 if (!penv->alarms_txctl) {
2244 ret = -ENOMEM;
2245 pr_err("%s: ioremap alarms TXCTL failed\n", __func__);
2246 goto fail_ioremap10;
2247 }
2248 penv->alarms_tactl = ioremap(MSM_PRONTO_ALARMS_TACTL, SZ_8);
2249 if (!penv->alarms_tactl) {
2250 ret = -ENOMEM;
2251 pr_err("%s: ioremap alarms TACTL failed\n", __func__);
2252 goto fail_ioremap11;
2253 }
Sameer Thalappild3aad702013-01-22 18:52:24 -08002254 }
Sameer Thalappilf6fb1892013-09-06 17:33:30 -07002255 penv->adc_tm_dev = qpnp_get_adc_tm(&penv->pdev->dev, "wcnss");
2256 if (IS_ERR(penv->adc_tm_dev)) {
2257 pr_err("%s: adc get failed\n", __func__);
2258 penv->adc_tm_dev = NULL;
2259 } else {
2260 INIT_DELAYED_WORK(&penv->vbatt_work, wcnss_update_vbatt);
2261 penv->fw_vbatt_state = WCNSS_CONFIG_UNSPECIFIED;
2262 }
Sameer Thalappild3aad702013-01-22 18:52:24 -08002263
Sameer Thalappilfe03e832014-02-19 10:37:16 -08002264 do {
2265 /* trigger initialization of the WCNSS */
2266 penv->pil = subsystem_get(WCNSS_PIL_DEVICE);
2267 if (IS_ERR(penv->pil)) {
2268 dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
2269 ret = PTR_ERR(penv->pil);
2270 wcnss_pronto_log_debug_regs();
2271 }
2272 } while (pil_retry++ < WCNSS_MAX_PIL_RETRY && IS_ERR(penv->pil));
2273
2274 if (pil_retry >= WCNSS_MAX_PIL_RETRY) {
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002275 penv->pil = NULL;
2276 goto fail_pil;
2277 }
2278
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002279 return 0;
2280
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002281fail_pil:
2282 if (penv->riva_ccu_base)
2283 iounmap(penv->riva_ccu_base);
Sameer Thalappil7f2d82f2013-12-17 11:26:18 -08002284 if (penv->alarms_tactl)
2285 iounmap(penv->alarms_tactl);
2286fail_ioremap11:
2287 if (penv->alarms_txctl)
2288 iounmap(penv->alarms_txctl);
2289fail_ioremap10:
Yue Mae490b452013-11-12 00:07:33 -08002290 if (penv->wlan_tx_status)
2291 iounmap(penv->wlan_tx_status);
2292fail_ioremap9:
Sameer Thalappil670197c2013-07-19 16:31:14 -07002293 if (penv->wlan_brdg_err_source)
2294 iounmap(penv->wlan_brdg_err_source);
2295fail_ioremap8:
2296 if (penv->wlan_tx_phy_aborts)
2297 iounmap(penv->wlan_tx_phy_aborts);
2298fail_ioremap7:
Sameer Thalappil66d2b392013-07-02 11:32:55 -07002299 if (penv->pronto_pll_base)
2300 iounmap(penv->pronto_pll_base);
2301fail_ioremap6:
Sameer Thalappil767ff492013-06-19 15:25:21 -07002302 if (penv->pronto_saw2_base)
2303 iounmap(penv->pronto_saw2_base);
2304fail_ioremap5:
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002305 if (penv->fiq_reg)
2306 iounmap(penv->fiq_reg);
2307fail_ioremap4:
2308 if (penv->pronto_ccpu_base)
2309 iounmap(penv->pronto_ccpu_base);
Sameer Thalappil58281ca2013-04-10 18:50:18 -07002310fail_ioremap3:
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002311 if (penv->pronto_a2xb_base)
2312 iounmap(penv->pronto_a2xb_base);
Sameer Thalappil16cae9b2013-03-04 18:02:50 -08002313fail_ioremap2:
Sameer Thalappil0ab82132013-06-10 10:10:05 -07002314 if (penv->msm_wcnss_base)
2315 iounmap(penv->msm_wcnss_base);
Sameer Thalappild3aad702013-01-22 18:52:24 -08002316fail_ioremap:
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002317 wake_lock_destroy(&penv->wcnss_wake_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002318fail_res:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002319 wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
Sameer Thalappil1d69b8022013-06-10 19:10:07 -07002320 WCNSS_WLAN_SWITCH_OFF, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002321fail_power:
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002322 if (has_pronto_hw)
Sameer Thalappiled3f7da2012-11-01 12:54:01 -07002323 wcnss_pronto_gpios_config(&pdev->dev, false);
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002324 else
2325 wcnss_gpios_config(penv->gpios_5wire, false);
Ankur Nandwaniad0d9ac2011-09-26 11:49:25 -07002326fail_gpio_res:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002327 penv = NULL;
2328 return ret;
2329}
2330
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002331static int wcnss_node_open(struct inode *inode, struct file *file)
2332{
2333 struct platform_device *pdev;
Sameer Thalappild0bb3bb2013-10-09 13:36:26 -07002334 int rc = 0;
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002335
Sameer Thalappil37c20682013-05-31 14:01:25 -07002336 if (!penv)
2337 return -EFAULT;
2338
Sameer Thalappil1878d762013-04-24 14:54:39 -07002339 if (!penv->triggered) {
2340 pr_info(DEVICE " triggered by userspace\n");
2341 pdev = penv->pdev;
Sameer Thalappild0bb3bb2013-10-09 13:36:26 -07002342 rc = wcnss_trigger_config(pdev);
2343 if (rc)
2344 return -EFAULT;
Sameer Thalappil1878d762013-04-24 14:54:39 -07002345 }
2346
2347 mutex_lock(&penv->dev_lock);
2348 penv->user_cal_rcvd = 0;
2349 penv->user_cal_read = 0;
2350 penv->user_cal_available = false;
2351 penv->user_cal_data = NULL;
2352 penv->device_opened = 1;
2353 mutex_unlock(&penv->dev_lock);
2354
Sameer Thalappild0bb3bb2013-10-09 13:36:26 -07002355 return rc;
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002356}
2357
Sameer Thalappil1878d762013-04-24 14:54:39 -07002358static ssize_t wcnss_wlan_read(struct file *fp, char __user
2359 *buffer, size_t count, loff_t *position)
2360{
2361 int rc = 0;
2362
Sameer Thalappil37c20682013-05-31 14:01:25 -07002363 if (!penv || !penv->device_opened)
Sameer Thalappil1878d762013-04-24 14:54:39 -07002364 return -EFAULT;
2365
2366 rc = wait_event_interruptible(penv->read_wait, penv->fw_cal_rcvd
2367 > penv->user_cal_read || penv->fw_cal_available);
2368
2369 if (rc < 0)
2370 return rc;
2371
2372 mutex_lock(&penv->dev_lock);
2373
2374 if (penv->fw_cal_available && penv->fw_cal_rcvd
2375 == penv->user_cal_read) {
2376 rc = 0;
2377 goto exit;
2378 }
2379
2380 if (count > penv->fw_cal_rcvd - penv->user_cal_read)
2381 count = penv->fw_cal_rcvd - penv->user_cal_read;
2382
2383 rc = copy_to_user(buffer, penv->fw_cal_data +
2384 penv->user_cal_read, count);
2385 if (rc == 0) {
2386 penv->user_cal_read += count;
2387 rc = count;
2388 }
2389
2390exit:
2391 mutex_unlock(&penv->dev_lock);
2392 return rc;
2393}
2394
2395/* first (valid) write to this device should be 4 bytes cal file size */
2396static ssize_t wcnss_wlan_write(struct file *fp, const char __user
2397 *user_buffer, size_t count, loff_t *position)
2398{
2399 int rc = 0;
Arif Hussain677c92d2013-11-01 19:09:11 -07002400 size_t size = 0;
Sameer Thalappil1878d762013-04-24 14:54:39 -07002401
Sameer Thalappil37c20682013-05-31 14:01:25 -07002402 if (!penv || !penv->device_opened || penv->user_cal_available)
Sameer Thalappil1878d762013-04-24 14:54:39 -07002403 return -EFAULT;
2404
2405 if (penv->user_cal_rcvd == 0 && count >= 4
2406 && !penv->user_cal_data) {
2407 rc = copy_from_user((void *)&size, user_buffer, 4);
Arif Hussain677c92d2013-11-01 19:09:11 -07002408 if (!size || size > MAX_CALIBRATED_DATA_SIZE) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07002409 pr_err(DEVICE " invalid size to write %d\n", size);
2410 return -EFAULT;
2411 }
2412
2413 rc += count;
2414 count -= 4;
2415 penv->user_cal_exp_size = size;
2416 penv->user_cal_data = kmalloc(size, GFP_KERNEL);
2417 if (penv->user_cal_data == NULL) {
2418 pr_err(DEVICE " no memory to write\n");
2419 return -ENOMEM;
2420 }
2421 if (0 == count)
2422 goto exit;
2423
2424 } else if (penv->user_cal_rcvd == 0 && count < 4)
2425 return -EFAULT;
2426
Arif Hussain677c92d2013-11-01 19:09:11 -07002427 if ((UINT32_MAX - count < penv->user_cal_rcvd) ||
2428 MAX_CALIBRATED_DATA_SIZE < count + penv->user_cal_rcvd) {
Sameer Thalappil1878d762013-04-24 14:54:39 -07002429 pr_err(DEVICE " invalid size to write %d\n", count +
2430 penv->user_cal_rcvd);
2431 rc = -ENOMEM;
2432 goto exit;
2433 }
2434 rc = copy_from_user((void *)penv->user_cal_data +
2435 penv->user_cal_rcvd, user_buffer, count);
2436 if (0 == rc) {
2437 penv->user_cal_rcvd += count;
2438 rc += count;
2439 }
2440 if (penv->user_cal_rcvd == penv->user_cal_exp_size) {
2441 penv->user_cal_available = true;
2442 pr_info_ratelimited("wcnss: user cal written");
2443 }
2444
2445exit:
2446 return rc;
2447}
2448
2449
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002450static const struct file_operations wcnss_node_fops = {
2451 .owner = THIS_MODULE,
2452 .open = wcnss_node_open,
Sameer Thalappil1878d762013-04-24 14:54:39 -07002453 .read = wcnss_wlan_read,
2454 .write = wcnss_wlan_write,
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002455};
2456
2457static struct miscdevice wcnss_misc = {
2458 .minor = MISC_DYNAMIC_MINOR,
2459 .name = DEVICE,
2460 .fops = &wcnss_node_fops,
2461};
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002462
2463static int __devinit
2464wcnss_wlan_probe(struct platform_device *pdev)
2465{
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002466 int ret = 0;
2467
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002468 /* verify we haven't been called more than once */
2469 if (penv) {
2470 dev_err(&pdev->dev, "cannot handle multiple devices.\n");
2471 return -ENODEV;
2472 }
2473
2474 /* create an environment to track the device */
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -08002475 penv = devm_kzalloc(&pdev->dev, sizeof(*penv), GFP_KERNEL);
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002476 if (!penv) {
2477 dev_err(&pdev->dev, "cannot allocate device memory.\n");
2478 return -ENOMEM;
2479 }
2480 penv->pdev = pdev;
2481
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002482 /* register sysfs entries */
2483 ret = wcnss_create_sysfs(&pdev->dev);
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -08002484 if (ret) {
2485 penv = NULL;
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002486 return -ENOENT;
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -08002487 }
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002488
Sameer Thalappil1878d762013-04-24 14:54:39 -07002489 mutex_init(&penv->dev_lock);
Sameer Thalappil13780d12013-12-19 17:04:21 -08002490 mutex_init(&penv->ctrl_lock);
Sameer Thalappilf6fb1892013-09-06 17:33:30 -07002491 mutex_init(&penv->vbat_monitor_mutex);
Sameer Thalappil1878d762013-04-24 14:54:39 -07002492 init_waitqueue_head(&penv->read_wait);
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002493
Sameer Thalappild3aad702013-01-22 18:52:24 -08002494 /* Since we were built into the kernel we'll be called as part
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002495 * of kernel initialization. We don't know if userspace
2496 * applications are available to service PIL at this time
2497 * (they probably are not), so we simply create a device node
2498 * here. When userspace is available it should touch the
2499 * device so that we know that WCNSS configuration can take
2500 * place
2501 */
2502 pr_info(DEVICE " probed in built-in mode\n");
Sameer Thalappil13780d12013-12-19 17:04:21 -08002503
2504 misc_register(&wcnss_usr_ctrl);
2505
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002506 return misc_register(&wcnss_misc);
2507
Jeff Johnsonb3377e32011-11-18 23:32:27 -08002508}
2509
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002510static int __devexit
2511wcnss_wlan_remove(struct platform_device *pdev)
2512{
Jeff Johnson8b1f6d02012-02-03 20:43:26 -08002513 wcnss_remove_sysfs(&pdev->dev);
Sameer Thalappil7e61a6d2012-11-14 11:28:41 -08002514 penv = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002515 return 0;
2516}
2517
2518
2519static const struct dev_pm_ops wcnss_wlan_pm_ops = {
2520 .suspend = wcnss_wlan_suspend,
2521 .resume = wcnss_wlan_resume,
2522};
2523
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002524#ifdef CONFIG_WCNSS_CORE_PRONTO
2525static struct of_device_id msm_wcnss_pronto_match[] = {
2526 {.compatible = "qcom,wcnss_wlan"},
2527 {}
2528};
2529#endif
2530
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002531static struct platform_driver wcnss_wlan_driver = {
2532 .driver = {
2533 .name = DEVICE,
2534 .owner = THIS_MODULE,
2535 .pm = &wcnss_wlan_pm_ops,
Sameer Thalappil8d686d42012-08-24 10:07:31 -07002536#ifdef CONFIG_WCNSS_CORE_PRONTO
2537 .of_match_table = msm_wcnss_pronto_match,
2538#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002539 },
2540 .probe = wcnss_wlan_probe,
2541 .remove = __devexit_p(wcnss_wlan_remove),
2542};
2543
2544static int __init wcnss_wlan_init(void)
2545{
Sameer Thalappil24db5282012-09-10 11:58:33 -07002546 int ret = 0;
2547
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002548 platform_driver_register(&wcnss_wlan_driver);
2549 platform_driver_register(&wcnss_wlan_ctrl_driver);
Sameer Thalappileeb8c412012-08-10 17:17:09 -07002550 platform_driver_register(&wcnss_ctrl_driver);
Sameer Thalappil944c72d2013-08-14 17:56:17 -07002551 register_pm_notifier(&wcnss_pm_notifier);
Sameer Thalappil24db5282012-09-10 11:58:33 -07002552#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
2553 ret = wcnss_prealloc_init();
2554 if (ret < 0)
2555 pr_err("wcnss: pre-allocation failed\n");
2556#endif
2557
2558 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002559}
2560
2561static void __exit wcnss_wlan_exit(void)
2562{
2563 if (penv) {
2564 if (penv->pil)
Stephen Boyd77db8bb2012-06-27 15:15:16 -07002565 subsystem_put(penv->pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002566 penv = NULL;
2567 }
2568
Sameer Thalappil24db5282012-09-10 11:58:33 -07002569#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
2570 wcnss_prealloc_deinit();
2571#endif
Sameer Thalappil944c72d2013-08-14 17:56:17 -07002572 unregister_pm_notifier(&wcnss_pm_notifier);
2573 platform_driver_unregister(&wcnss_ctrl_driver);
2574 platform_driver_unregister(&wcnss_wlan_ctrl_driver);
2575 platform_driver_unregister(&wcnss_wlan_driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002576}
2577
2578module_init(wcnss_wlan_init);
2579module_exit(wcnss_wlan_exit);
2580
2581MODULE_LICENSE("GPL v2");
2582MODULE_VERSION(VERSION);
2583MODULE_DESCRIPTION(DEVICE "Driver");