1/******************************************************************************
2 *
3 * Copyright(c) 2009-2012  Realtek Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12 * more details.
13 *
14 * The full GNU General Public License is included in this distribution in the
15 * file called LICENSE.
16 *
17 * Contact Information:
18 * wlanfae <wlanfae@realtek.com>
19 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
20 * Hsinchu 300, Taiwan.
21 *
22 * Larry Finger <Larry.Finger@lwfinger.net>
23 *
24 *****************************************************************************/
25
26#include "../wifi.h"
27#include "../pci.h"
28#include "../base.h"
29#include "../core.h"
30#include "../rtl8192ce/reg.h"
31#include "../rtl8192ce/def.h"
32#include "fw_common.h"
33#include <linux/export.h>
34#include <linux/kmemleak.h>
35
36static void _rtl92c_enable_fw_download(struct ieee80211_hw *hw, bool enable)
37{
38	struct rtl_priv *rtlpriv = rtl_priv(hw);
39	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
40
41	if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CU) {
42		u32 value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
43		if (enable)
44			value32 |= MCUFWDL_EN;
45		else
46			value32 &= ~MCUFWDL_EN;
47		rtl_write_dword(rtlpriv, REG_MCUFWDL, value32);
48	} else if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CE) {
49		u8 tmp;
50		if (enable) {
51
52			tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
53			rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1,
54				       tmp | 0x04);
55
56			tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
57			rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01);
58
59			tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2);
60			rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7);
61		} else {
62
63			tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
64			rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe);
65
66			rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00);
67		}
68	}
69}
70
71static void _rtl92c_fw_block_write(struct ieee80211_hw *hw,
72				   const u8 *buffer, u32 size)
73{
74	struct rtl_priv *rtlpriv = rtl_priv(hw);
75	u32 blocksize = sizeof(u32);
76	u8 *bufferptr = (u8 *)buffer;
77	u32 *pu4byteptr = (u32 *)buffer;
78	u32 i, offset, blockcount, remainsize;
79
80	blockcount = size / blocksize;
81	remainsize = size % blocksize;
82
83	for (i = 0; i < blockcount; i++) {
84		offset = i * blocksize;
85		rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset),
86				*(pu4byteptr + i));
87	}
88
89	if (remainsize) {
90		offset = blockcount * blocksize;
91		bufferptr += offset;
92		for (i = 0; i < remainsize; i++) {
93			rtl_write_byte(rtlpriv, (FW_8192C_START_ADDRESS +
94						 offset + i), *(bufferptr + i));
95		}
96	}
97}
98
99static void _rtl92c_fw_page_write(struct ieee80211_hw *hw,
100				  u32 page, const u8 *buffer, u32 size)
101{
102	struct rtl_priv *rtlpriv = rtl_priv(hw);
103	u8 value8;
104	u8 u8page = (u8) (page & 0x07);
105
106	value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page;
107
108	rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8);
109	_rtl92c_fw_block_write(hw, buffer, size);
110}
111
112static void _rtl92c_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
113{
114	u32 fwlen = *pfwlen;
115	u8 remain = (u8) (fwlen % 4);
116
117	remain = (remain == 0) ? 0 : (4 - remain);
118
119	while (remain > 0) {
120		pfwbuf[fwlen] = 0;
121		fwlen++;
122		remain--;
123	}
124
125	*pfwlen = fwlen;
126}
127
128static void _rtl92c_write_fw(struct ieee80211_hw *hw,
129			     enum version_8192c version, u8 *buffer, u32 size)
130{
131	struct rtl_priv *rtlpriv = rtl_priv(hw);
132	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
133	bool is_version_b;
134	u8 *bufferptr = (u8 *)buffer;
135
136	RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW size is %d bytes,\n", size);
137	is_version_b = IS_NORMAL_CHIP(version);
138	if (is_version_b) {
139		u32 pageNums, remainsize;
140		u32 page, offset;
141
142		if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CE)
143			_rtl92c_fill_dummy(bufferptr, &size);
144
145		pageNums = size / FW_8192C_PAGE_SIZE;
146		remainsize = size % FW_8192C_PAGE_SIZE;
147
148		if (pageNums > 4) {
149			RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
150				 "Page numbers should not greater then 4\n");
151		}
152
153		for (page = 0; page < pageNums; page++) {
154			offset = page * FW_8192C_PAGE_SIZE;
155			_rtl92c_fw_page_write(hw, page, (bufferptr + offset),
156					      FW_8192C_PAGE_SIZE);
157		}
158
159		if (remainsize) {
160			offset = pageNums * FW_8192C_PAGE_SIZE;
161			page = pageNums;
162			_rtl92c_fw_page_write(hw, page, (bufferptr + offset),
163					      remainsize);
164		}
165	} else {
166		_rtl92c_fw_block_write(hw, buffer, size);
167	}
168}
169
170static int _rtl92c_fw_free_to_go(struct ieee80211_hw *hw)
171{
172	struct rtl_priv *rtlpriv = rtl_priv(hw);
173	int err = -EIO;
174	u32 counter = 0;
175	u32 value32;
176
177	do {
178		value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
179	} while ((counter++ < FW_8192C_POLLING_TIMEOUT_COUNT) &&
180		 (!(value32 & FWDL_ChkSum_rpt)));
181
182	if (counter >= FW_8192C_POLLING_TIMEOUT_COUNT) {
183		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
184			 "chksum report faill ! REG_MCUFWDL:0x%08x .\n",
185			  value32);
186		goto exit;
187	}
188
189	RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
190		 "Checksum report OK ! REG_MCUFWDL:0x%08x .\n", value32);
191
192	value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
193	value32 |= MCUFWDL_RDY;
194	value32 &= ~WINTINI_RDY;
195	rtl_write_dword(rtlpriv, REG_MCUFWDL, value32);
196
197	counter = 0;
198
199	do {
200		value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
201		if (value32 & WINTINI_RDY) {
202			RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
203				 "Polling FW ready success!! REG_MCUFWDL:0x%08x .\n",
204					value32);
205			err = 0;
206			goto exit;
207		}
208
209		mdelay(FW_8192C_POLLING_DELAY);
210
211	} while (counter++ < FW_8192C_POLLING_TIMEOUT_COUNT);
212
213	RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
214		 "Polling FW ready fail!! REG_MCUFWDL:0x%08x .\n", value32);
215
216exit:
217	return err;
218}
219
220int rtl92c_download_fw(struct ieee80211_hw *hw)
221{
222	struct rtl_priv *rtlpriv = rtl_priv(hw);
223	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
224	struct rtl92c_firmware_header *pfwheader;
225	u8 *pfwdata;
226	u32 fwsize;
227	int err;
228	enum version_8192c version = rtlhal->version;
229
230	if (!rtlhal->pfirmware)
231		return 1;
232
233	pfwheader = (struct rtl92c_firmware_header *)rtlhal->pfirmware;
234	pfwdata = (u8 *)rtlhal->pfirmware;
235	fwsize = rtlhal->fwsize;
236
237	if (IS_FW_HEADER_EXIST(pfwheader)) {
238		RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
239			 "Firmware Version(%d), Signature(%#x),Size(%d)\n",
240			  pfwheader->version, pfwheader->signature,
241			  (int)sizeof(struct rtl92c_firmware_header));
242
243		pfwdata = pfwdata + sizeof(struct rtl92c_firmware_header);
244		fwsize = fwsize - sizeof(struct rtl92c_firmware_header);
245	}
246
247	_rtl92c_enable_fw_download(hw, true);
248	_rtl92c_write_fw(hw, version, pfwdata, fwsize);
249	_rtl92c_enable_fw_download(hw, false);
250
251	err = _rtl92c_fw_free_to_go(hw);
252	if (err) {
253		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
254			 "Firmware is not ready to run!\n");
255	} else {
256		RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
257			 "Firmware is ready to run!\n");
258	}
259
260	return 0;
261}
262EXPORT_SYMBOL(rtl92c_download_fw);
263
264static bool _rtl92c_check_fw_read_last_h2c(struct ieee80211_hw *hw, u8 boxnum)
265{
266	struct rtl_priv *rtlpriv = rtl_priv(hw);
267	u8 val_hmetfr, val_mcutst_1;
268	bool result = false;
269
270	val_hmetfr = rtl_read_byte(rtlpriv, REG_HMETFR);
271	val_mcutst_1 = rtl_read_byte(rtlpriv, (REG_MCUTST_1 + boxnum));
272
273	if (((val_hmetfr >> boxnum) & BIT(0)) == 0 && val_mcutst_1 == 0)
274		result = true;
275	return result;
276}
277
278static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw,
279			      u8 element_id, u32 cmd_len, u8 *cmdbuffer)
280{
281	struct rtl_priv *rtlpriv = rtl_priv(hw);
282	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
283	u8 boxnum;
284	u16 box_reg = 0, box_extreg = 0;
285	u8 u1b_tmp;
286	bool isfw_read = false;
287	u8 buf_index = 0;
288	bool bwrite_sucess = false;
289	u8 wait_h2c_limmit = 100;
290	u8 wait_writeh2c_limmit = 100;
291	u8 boxcontent[4], boxextcontent[2];
292	u32 h2c_waitcounter = 0;
293	unsigned long flag;
294	u8 idx;
295
296	RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "come in\n");
297
298	while (true) {
299		spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag);
300		if (rtlhal->h2c_setinprogress) {
301			RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
302				 "H2C set in progress! Wait to set..element_id(%d).\n",
303				 element_id);
304			while (rtlhal->h2c_setinprogress) {
305				spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock,
306						       flag);
307				h2c_waitcounter++;
308				RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
309					 "Wait 100 us (%d times)...\n",
310					  h2c_waitcounter);
311				udelay(100);
312
313				if (h2c_waitcounter > 1000)
314					return;
315				spin_lock_irqsave(&rtlpriv->locks.h2c_lock,
316						  flag);
317			}
318			spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
319		} else {
320			rtlhal->h2c_setinprogress = true;
321			spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
322			break;
323		}
324	}
325
326	while (!bwrite_sucess) {
327		wait_writeh2c_limmit--;
328		if (wait_writeh2c_limmit == 0) {
329			RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
330				 "Write H2C fail because no trigger for FW INT!\n");
331			break;
332		}
333
334		boxnum = rtlhal->last_hmeboxnum;
335		switch (boxnum) {
336		case 0:
337			box_reg = REG_HMEBOX_0;
338			box_extreg = REG_HMEBOX_EXT_0;
339			break;
340		case 1:
341			box_reg = REG_HMEBOX_1;
342			box_extreg = REG_HMEBOX_EXT_1;
343			break;
344		case 2:
345			box_reg = REG_HMEBOX_2;
346			box_extreg = REG_HMEBOX_EXT_2;
347			break;
348		case 3:
349			box_reg = REG_HMEBOX_3;
350			box_extreg = REG_HMEBOX_EXT_3;
351			break;
352		default:
353			RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
354				 "switch case not process\n");
355			break;
356		}
357
358		isfw_read = _rtl92c_check_fw_read_last_h2c(hw, boxnum);
359		while (!isfw_read) {
360			wait_h2c_limmit--;
361			if (wait_h2c_limmit == 0) {
362				RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
363					 "Waiting too long for FW read clear HMEBox(%d)!\n",
364					 boxnum);
365				break;
366			}
367
368			udelay(10);
369
370			isfw_read = _rtl92c_check_fw_read_last_h2c(hw, boxnum);
371			u1b_tmp = rtl_read_byte(rtlpriv, 0x1BF);
372			RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
373				 "Waiting for FW read clear HMEBox(%d)!!! 0x1BF = %2x\n",
374				 boxnum, u1b_tmp);
375		}
376
377		if (!isfw_read) {
378			RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
379				 "Write H2C register BOX[%d] fail!!!!! Fw do not read.\n",
380				 boxnum);
381			break;
382		}
383
384		memset(boxcontent, 0, sizeof(boxcontent));
385		memset(boxextcontent, 0, sizeof(boxextcontent));
386		boxcontent[0] = element_id;
387		RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
388			 "Write element_id box_reg(%4x) = %2x\n",
389			  box_reg, element_id);
390
391		switch (cmd_len) {
392		case 1:
393			boxcontent[0] &= ~(BIT(7));
394			memcpy((u8 *)(boxcontent) + 1,
395			       cmdbuffer + buf_index, 1);
396
397			for (idx = 0; idx < 4; idx++) {
398				rtl_write_byte(rtlpriv, box_reg + idx,
399					       boxcontent[idx]);
400			}
401			break;
402		case 2:
403			boxcontent[0] &= ~(BIT(7));
404			memcpy((u8 *)(boxcontent) + 1,
405			       cmdbuffer + buf_index, 2);
406
407			for (idx = 0; idx < 4; idx++) {
408				rtl_write_byte(rtlpriv, box_reg + idx,
409					       boxcontent[idx]);
410			}
411			break;
412		case 3:
413			boxcontent[0] &= ~(BIT(7));
414			memcpy((u8 *)(boxcontent) + 1,
415			       cmdbuffer + buf_index, 3);
416
417			for (idx = 0; idx < 4; idx++) {
418				rtl_write_byte(rtlpriv, box_reg + idx,
419					       boxcontent[idx]);
420			}
421			break;
422		case 4:
423			boxcontent[0] |= (BIT(7));
424			memcpy((u8 *)(boxextcontent),
425			       cmdbuffer + buf_index, 2);
426			memcpy((u8 *)(boxcontent) + 1,
427			       cmdbuffer + buf_index + 2, 2);
428
429			for (idx = 0; idx < 2; idx++) {
430				rtl_write_byte(rtlpriv, box_extreg + idx,
431					       boxextcontent[idx]);
432			}
433
434			for (idx = 0; idx < 4; idx++) {
435				rtl_write_byte(rtlpriv, box_reg + idx,
436					       boxcontent[idx]);
437			}
438			break;
439		case 5:
440			boxcontent[0] |= (BIT(7));
441			memcpy((u8 *)(boxextcontent),
442			       cmdbuffer + buf_index, 2);
443			memcpy((u8 *)(boxcontent) + 1,
444			       cmdbuffer + buf_index + 2, 3);
445
446			for (idx = 0; idx < 2; idx++) {
447				rtl_write_byte(rtlpriv, box_extreg + idx,
448					       boxextcontent[idx]);
449			}
450
451			for (idx = 0; idx < 4; idx++) {
452				rtl_write_byte(rtlpriv, box_reg + idx,
453					       boxcontent[idx]);
454			}
455			break;
456		default:
457			RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
458				 "switch case not process\n");
459			break;
460		}
461
462		bwrite_sucess = true;
463
464		rtlhal->last_hmeboxnum = boxnum + 1;
465		if (rtlhal->last_hmeboxnum == 4)
466			rtlhal->last_hmeboxnum = 0;
467
468		RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
469			 "pHalData->last_hmeboxnum  = %d\n",
470			  rtlhal->last_hmeboxnum);
471	}
472
473	spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag);
474	rtlhal->h2c_setinprogress = false;
475	spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
476
477	RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "go out\n");
478}
479
480void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw,
481			 u8 element_id, u32 cmd_len, u8 *cmdbuffer)
482{
483	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
484	u32 tmp_cmdbuf[2];
485
486	if (!rtlhal->fw_ready) {
487		RT_ASSERT(false,
488			  "return H2C cmd because of Fw download fail!!!\n");
489		return;
490	}
491
492	memset(tmp_cmdbuf, 0, 8);
493	memcpy(tmp_cmdbuf, cmdbuffer, cmd_len);
494	_rtl92c_fill_h2c_command(hw, element_id, cmd_len, (u8 *)&tmp_cmdbuf);
495
496	return;
497}
498EXPORT_SYMBOL(rtl92c_fill_h2c_cmd);
499
500void rtl92c_firmware_selfreset(struct ieee80211_hw *hw)
501{
502	u8 u1b_tmp;
503	u8 delay = 100;
504	struct rtl_priv *rtlpriv = rtl_priv(hw);
505
506	rtl_write_byte(rtlpriv, REG_HMETFR + 3, 0x20);
507	u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
508
509	while (u1b_tmp & BIT(2)) {
510		delay--;
511		if (delay == 0) {
512			RT_ASSERT(false, "8051 reset fail.\n");
513			break;
514		}
515		udelay(50);
516		u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
517	}
518}
519EXPORT_SYMBOL(rtl92c_firmware_selfreset);
520
521void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode)
522{
523	struct rtl_priv *rtlpriv = rtl_priv(hw);
524	u8 u1_h2c_set_pwrmode[3] = { 0 };
525	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
526
527	RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode);
528
529	SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode);
530	SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode,
531		(rtlpriv->mac80211.p2p) ? ppsc->smart_ps : 1);
532	SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode,
533					      ppsc->reg_max_lps_awakeintvl);
534
535	RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
536		      "rtl92c_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode\n",
537		      u1_h2c_set_pwrmode, 3);
538	rtl92c_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode);
539}
540EXPORT_SYMBOL(rtl92c_set_fw_pwrmode_cmd);
541
542#define BEACON_PG		0 /*->1*/
543#define PSPOLL_PG		2
544#define NULL_PG			3
545#define PROBERSP_PG		4 /*->5*/
546
547#define TOTAL_RESERVED_PKT_LEN	768
548
549static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = {
550	/* page 0 beacon */
551	0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
552	0xFF, 0xFF, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
553	0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x50, 0x08,
554	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
555	0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69,
556	0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C,
557	0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96,
558	0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A,
559	0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C,
560	0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18,
561	0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
562	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
563	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
564	0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02,
565	0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
566	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
567
568	/* page 1 beacon */
569	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
570	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
571	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
572	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
573	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
574	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
575	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
576	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
577	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
578	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
579	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
580	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
581	0x10, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x10, 0x00,
582	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
583	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
584	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
585
586	/* page 2  ps-poll */
587	0xA4, 0x10, 0x01, 0xC0, 0x00, 0x40, 0x10, 0x10,
588	0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
589	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
590	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
591	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
592	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
593	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
594	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
595	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
596	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
597	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
598	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
599	0x18, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00,
600	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
601	0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
602	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
603
604	/* page 3  null */
605	0x48, 0x01, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10,
606	0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
607	0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00,
608	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
609	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
610	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
611	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
612	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
613	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
614	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
615	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
616	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
617	0x72, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00,
618	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
619	0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
620	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
621
622	/* page 4  probe_resp */
623	0x50, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10,
624	0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
625	0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00,
626	0x9E, 0x46, 0x15, 0x32, 0x27, 0xF2, 0x2D, 0x00,
627	0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69,
628	0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C,
629	0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96,
630	0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A,
631	0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C,
632	0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18,
633	0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
634	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
635	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
636	0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02,
637	0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
638	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
639
640	/* page 5  probe_resp */
641	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
642	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
643	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
644	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
645	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
646	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
647	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
648	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
649	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
650	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
651	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
652	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
653	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
654	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
655	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
656	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
657};
658
659void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw,
660	 bool (*cmd_send_packet)(struct ieee80211_hw *, struct sk_buff *))
661{
662	struct rtl_priv *rtlpriv = rtl_priv(hw);
663	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
664	struct sk_buff *skb = NULL;
665
666	u32 totalpacketlen;
667	bool rtstatus;
668	u8 u1rsvdpageloc[3] = { 0 };
669	bool b_dlok = false;
670
671	u8 *beacon;
672	u8 *p_pspoll;
673	u8 *nullfunc;
674	u8 *p_probersp;
675	/*---------------------------------------------------------
676				(1) beacon
677	---------------------------------------------------------*/
678	beacon = &reserved_page_packet[BEACON_PG * 128];
679	SET_80211_HDR_ADDRESS2(beacon, mac->mac_addr);
680	SET_80211_HDR_ADDRESS3(beacon, mac->bssid);
681
682	/*-------------------------------------------------------
683				(2) ps-poll
684	--------------------------------------------------------*/
685	p_pspoll = &reserved_page_packet[PSPOLL_PG * 128];
686	SET_80211_PS_POLL_AID(p_pspoll, (mac->assoc_id | 0xc000));
687	SET_80211_PS_POLL_BSSID(p_pspoll, mac->bssid);
688	SET_80211_PS_POLL_TA(p_pspoll, mac->mac_addr);
689
690	SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(u1rsvdpageloc, PSPOLL_PG);
691
692	/*--------------------------------------------------------
693				(3) null data
694	---------------------------------------------------------*/
695	nullfunc = &reserved_page_packet[NULL_PG * 128];
696	SET_80211_HDR_ADDRESS1(nullfunc, mac->bssid);
697	SET_80211_HDR_ADDRESS2(nullfunc, mac->mac_addr);
698	SET_80211_HDR_ADDRESS3(nullfunc, mac->bssid);
699
700	SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(u1rsvdpageloc, NULL_PG);
701
702	/*---------------------------------------------------------
703				(4) probe response
704	----------------------------------------------------------*/
705	p_probersp = &reserved_page_packet[PROBERSP_PG * 128];
706	SET_80211_HDR_ADDRESS1(p_probersp, mac->bssid);
707	SET_80211_HDR_ADDRESS2(p_probersp, mac->mac_addr);
708	SET_80211_HDR_ADDRESS3(p_probersp, mac->bssid);
709
710	SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(u1rsvdpageloc, PROBERSP_PG);
711
712	totalpacketlen = TOTAL_RESERVED_PKT_LEN;
713
714	RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD,
715		      "rtl92c_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL\n",
716		      &reserved_page_packet[0], totalpacketlen);
717	RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
718		      "rtl92c_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL\n",
719		      u1rsvdpageloc, 3);
720
721
722	skb = dev_alloc_skb(totalpacketlen);
723	memcpy((u8 *)skb_put(skb, totalpacketlen),
724	       &reserved_page_packet, totalpacketlen);
725
726	if (cmd_send_packet)
727		rtstatus = cmd_send_packet(hw, skb);
728	else
729		rtstatus = rtl_cmd_send_packet(hw, skb);
730
731	if (rtstatus)
732		b_dlok = true;
733
734	if (b_dlok) {
735		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
736			 "Set RSVD page location to Fw.\n");
737		RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
738				"H2C_RSVDPAGE:\n",
739				u1rsvdpageloc, 3);
740		rtl92c_fill_h2c_cmd(hw, H2C_RSVDPAGE,
741				    sizeof(u1rsvdpageloc), u1rsvdpageloc);
742	} else
743		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
744			 "Set RSVD page location to Fw FAIL!!!!!!.\n");
745}
746EXPORT_SYMBOL(rtl92c_set_fw_rsvdpagepkt);
747
748void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus)
749{
750	u8 u1_joinbssrpt_parm[1] = { 0 };
751
752	SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(u1_joinbssrpt_parm, mstatus);
753
754	rtl92c_fill_h2c_cmd(hw, H2C_JOINBSSRPT, 1, u1_joinbssrpt_parm);
755}
756EXPORT_SYMBOL(rtl92c_set_fw_joinbss_report_cmd);
757
758static void rtl92c_set_p2p_ctw_period_cmd(struct ieee80211_hw *hw, u8 ctwindow)
759{
760	u8 u1_ctwindow_period[1] = { ctwindow};
761
762	rtl92c_fill_h2c_cmd(hw, H2C_P2P_PS_CTW_CMD, 1, u1_ctwindow_period);
763}
764
765/* refactored routine */
766static void set_noa_data(struct rtl_priv *rtlpriv,
767			 struct rtl_p2p_ps_info *p2pinfo,
768			 struct p2p_ps_offload_t *p2p_ps_offload)
769{
770	int i;
771	u32	start_time, tsf_low;
772
773	/* hw only support 2 set of NoA */
774	for (i = 0 ; i < p2pinfo->noa_num ; i++) {
775		/* To control the reg setting for which NOA*/
776		rtl_write_byte(rtlpriv, 0x5cf, (i << 4));
777		if (i == 0)
778			p2p_ps_offload->noa0_en = 1;
779		else
780			p2p_ps_offload->noa1_en = 1;
781
782		/* config P2P NoA Descriptor Register */
783		rtl_write_dword(rtlpriv, 0x5E0,
784				p2pinfo->noa_duration[i]);
785		rtl_write_dword(rtlpriv, 0x5E4,
786				p2pinfo->noa_interval[i]);
787
788		/*Get Current TSF value */
789		tsf_low = rtl_read_dword(rtlpriv, REG_TSFTR);
790
791		start_time = p2pinfo->noa_start_time[i];
792		if (p2pinfo->noa_count_type[i] != 1) {
793			while (start_time <= (tsf_low+(50*1024))) {
794				start_time += p2pinfo->noa_interval[i];
795				if (p2pinfo->noa_count_type[i] != 255)
796					p2pinfo->noa_count_type[i]--;
797			}
798		}
799		rtl_write_dword(rtlpriv, 0x5E8, start_time);
800		rtl_write_dword(rtlpriv, 0x5EC,
801				p2pinfo->noa_count_type[i]);
802	}
803}
804
805void rtl92c_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
806{
807	struct rtl_priv *rtlpriv = rtl_priv(hw);
808	struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw));
809	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
810	struct rtl_p2p_ps_info *p2pinfo = &(rtlps->p2p_ps_info);
811	struct p2p_ps_offload_t *p2p_ps_offload = &rtlhal->p2p_ps_offload;
812	u16	ctwindow;
813
814	switch (p2p_ps_state) {
815	case P2P_PS_DISABLE:
816			RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
817				 "P2P_PS_DISABLE\n");
818			memset(p2p_ps_offload, 0, sizeof(*p2p_ps_offload));
819			break;
820	case P2P_PS_ENABLE:
821			RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
822				 "P2P_PS_ENABLE\n");
823			/* update CTWindow value. */
824			if (p2pinfo->ctwindow > 0) {
825				p2p_ps_offload->ctwindow_en = 1;
826				ctwindow = p2pinfo->ctwindow;
827				rtl92c_set_p2p_ctw_period_cmd(hw, ctwindow);
828			}
829			/* call refactored routine */
830			set_noa_data(rtlpriv, p2pinfo, p2p_ps_offload);
831
832			if ((p2pinfo->opp_ps == 1) || (p2pinfo->noa_num > 0)) {
833				/* rst p2p circuit */
834				rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST,
835					       BIT(4));
836
837				p2p_ps_offload->offload_en = 1;
838
839				if (P2P_ROLE_GO == rtlpriv->mac80211.p2p) {
840					p2p_ps_offload->role = 1;
841					p2p_ps_offload->allstasleep = 0;
842				} else {
843					p2p_ps_offload->role = 0;
844				}
845
846				p2p_ps_offload->discovery = 0;
847			}
848			break;
849	case P2P_PS_SCAN:
850			RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_SCAN\n");
851			p2p_ps_offload->discovery = 1;
852			break;
853	case P2P_PS_SCAN_DONE:
854			RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
855				 "P2P_PS_SCAN_DONE\n");
856			p2p_ps_offload->discovery = 0;
857			p2pinfo->p2p_ps_state = P2P_PS_ENABLE;
858			break;
859	default:
860			break;
861	}
862
863	rtl92c_fill_h2c_cmd(hw, H2C_P2P_PS_OFFLOAD, 1, (u8 *)p2p_ps_offload);
864
865}
866EXPORT_SYMBOL_GPL(rtl92c_set_p2p_ps_offload_cmd);
867