fw_common.c 9.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/******************************************************************************
 *
 * Copyright(c) 2009-2014  Realtek Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * The full GNU General Public License is included in this distribution in the
 * file called LICENSE.
 *
 * Contact Information:
 * wlanfae <wlanfae@realtek.com>
 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
 * Hsinchu 300, Taiwan.
 *
 * Larry Finger <Larry.Finger@lwfinger.net>
 *
 *****************************************************************************/

#include "../wifi.h"
27 28
#include "../pci.h"
#include "../base.h"
29 30 31 32 33 34 35 36 37 38
#include "fw_common.h"
#include <linux/module.h>

void rtl8723_enable_fw_download(struct ieee80211_hw *hw, bool enable)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	u8 tmp;

	if (enable) {
		tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
39 40
		rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1,
			       tmp | 0x04);
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98

		tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
		rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01);

		tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2);
		rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7);
	} else {
		tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
		rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe);

		rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00);
	}
}
EXPORT_SYMBOL_GPL(rtl8723_enable_fw_download);

void rtl8723_fw_block_write(struct ieee80211_hw *hw,
			    const u8 *buffer, u32 size)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	u32 blocksize = sizeof(u32);
	u8 *bufferptr = (u8 *)buffer;
	u32 *pu4byteptr = (u32 *)buffer;
	u32 i, offset, blockcount, remainsize;

	blockcount = size / blocksize;
	remainsize = size % blocksize;

	for (i = 0; i < blockcount; i++) {
		offset = i * blocksize;
		rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset),
				*(pu4byteptr + i));
	}
	if (remainsize) {
		offset = blockcount * blocksize;
		bufferptr += offset;
		for (i = 0; i < remainsize; i++) {
			rtl_write_byte(rtlpriv,
				       (FW_8192C_START_ADDRESS + offset + i),
				       *(bufferptr + i));
		}
	}
}
EXPORT_SYMBOL_GPL(rtl8723_fw_block_write);

void rtl8723_fw_page_write(struct ieee80211_hw *hw,
			   u32 page, const u8 *buffer, u32 size)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	u8 value8;
	u8 u8page = (u8) (page & 0x07);

	value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page;

	rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8);
	rtl8723_fw_block_write(hw, buffer, size);
}
EXPORT_SYMBOL_GPL(rtl8723_fw_page_write);

99
void rtl8723_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
100 101 102 103 104 105 106 107 108 109 110 111 112
{
	u32 fwlen = *pfwlen;
	u8 remain = (u8) (fwlen % 4);

	remain = (remain == 0) ? 0 : (4 - remain);

	while (remain > 0) {
		pfwbuf[fwlen] = 0;
		fwlen++;
		remain--;
	}
	*pfwlen = fwlen;
}
113
EXPORT_SYMBOL(rtl8723_fill_dummy);
114 115

void rtl8723_write_fw(struct ieee80211_hw *hw,
116
		      enum version_8723e version,
117
		      u8 *buffer, u32 size, u8 max_page)
118 119
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
120
	u8 *bufferptr = buffer;
121
	u32 page_nums, remain_size;
122 123
	u32 page, offset;

124
	RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW size is %d bytes,\n", size);
125

126
	rtl8723_fill_dummy(bufferptr, &size);
127

128 129
	page_nums = size / FW_8192C_PAGE_SIZE;
	remain_size = size % FW_8192C_PAGE_SIZE;
130

131
	if (page_nums > max_page) {
132
		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
133
			 "Page numbers should not greater than %d\n", max_page);
134
	}
135
	for (page = 0; page < page_nums; page++) {
136 137 138 139
		offset = page * FW_8192C_PAGE_SIZE;
		rtl8723_fw_page_write(hw, page, (bufferptr + offset),
				      FW_8192C_PAGE_SIZE);
	}
140 141 142 143

	if (remain_size) {
		offset = page_nums * FW_8192C_PAGE_SIZE;
		page = page_nums;
144
		rtl8723_fw_page_write(hw, page, (bufferptr + offset),
145
				      remain_size);
146
	}
147
	RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW write done.\n");
148 149 150 151 152
}
EXPORT_SYMBOL_GPL(rtl8723_write_fw);

void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw)
{
153
	u8 u1b_tmp;
154 155 156 157
	u8 delay = 100;
	struct rtl_priv *rtlpriv = rtl_priv(hw);

	rtl_write_byte(rtlpriv, REG_HMETFR + 3, 0x20);
158
	u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
159

160
	while (u1b_tmp & BIT(2)) {
161 162 163 164
		delay--;
		if (delay == 0)
			break;
		udelay(50);
165
		u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
166 167
	}
	if (delay == 0) {
168 169 170
		u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
		rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1,
			       u1b_tmp&(~BIT(2)));
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
	}
}
EXPORT_SYMBOL_GPL(rtl8723ae_firmware_selfreset);

void rtl8723be_firmware_selfreset(struct ieee80211_hw *hw)
{
	u8 u1b_tmp;
	struct rtl_priv *rtlpriv = rtl_priv(hw);

	u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1);
	rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, (u1b_tmp & (~BIT(0))));

	u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
	rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, (u1b_tmp & (~BIT(2))));
	udelay(50);

	u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1);
	rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, (u1b_tmp | BIT(0)));

	u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
	rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, (u1b_tmp | BIT(2)));

	RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
		 "  _8051Reset8723be(): 8051 reset success .\n");
}
EXPORT_SYMBOL_GPL(rtl8723be_firmware_selfreset);

198 199
int rtl8723_fw_free_to_go(struct ieee80211_hw *hw, bool is_8723be,
			  int max_count)
200 201 202 203 204 205 206 207
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	int err = -EIO;
	u32 counter = 0;
	u32 value32;

	do {
		value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
208
	} while ((counter++ < max_count) &&
209 210
		 (!(value32 & FWDL_CHKSUM_RPT)));

211
	if (counter >= max_count) {
212
		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
213
			 "chksum report fail ! REG_MCUFWDL:0x%08x .\n",
214 215 216 217 218 219
			 value32);
		goto exit;
	}
	RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
		 "Checksum report OK ! REG_MCUFWDL:0x%08x .\n", value32);

220
	value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL) | MCUFWDL_RDY;
221 222 223 224 225 226 227 228 229 230 231
	value32 &= ~WINTINI_RDY;
	rtl_write_dword(rtlpriv, REG_MCUFWDL, value32);

	if (is_8723be)
		rtl8723be_firmware_selfreset(hw);
	counter = 0;

	do {
		value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
		if (value32 & WINTINI_RDY) {
			RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
232
				 "Polling FW ready success!! REG_MCUFWDL:0x%08x .\n",
233 234 235 236 237
				 value32);
			err = 0;
			goto exit;
		}

238 239 240
		mdelay(FW_8192C_POLLING_DELAY);

	} while (counter++ < max_count);
241 242 243 244 245 246 247 248 249 250 251

	RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
		 "Polling FW ready fail!! REG_MCUFWDL:0x%08x .\n",
		 value32);

exit:
	return err;
}
EXPORT_SYMBOL_GPL(rtl8723_fw_free_to_go);

int rtl8723_download_fw(struct ieee80211_hw *hw,
252
			bool is_8723be, int max_count)
253 254 255
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
256
	struct rtl8723e_firmware_header *pfwheader;
257 258 259 260
	u8 *pfwdata;
	u32 fwsize;
	int err;
	enum version_8723e version = rtlhal->version;
261
	int max_page;
262 263 264 265

	if (!rtlhal->pfirmware)
		return 1;

266
	pfwheader = (struct rtl8723e_firmware_header *)rtlhal->pfirmware;
267
	pfwdata = rtlhal->pfirmware;
268 269
	fwsize = rtlhal->fwsize;

270
	if (!is_8723be)
271 272 273
		max_page = 6;
	else
		max_page = 8;
274
	if (rtlpriv->cfg->ops->is_fw_header(pfwheader)) {
275
		RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
276 277
			 "Firmware Version(%d), Signature(%#x), Size(%d)\n",
			 pfwheader->version, pfwheader->signature,
278
			 (int)sizeof(struct rtl8723e_firmware_header));
279

280 281
		pfwdata = pfwdata + sizeof(struct rtl8723e_firmware_header);
		fwsize = fwsize - sizeof(struct rtl8723e_firmware_header);
282
	}
283 284

	if (rtl_read_byte(rtlpriv, REG_MCUFWDL)&BIT(7)) {
285 286 287 288
		if (is_8723be)
			rtl8723be_firmware_selfreset(hw);
		else
			rtl8723ae_firmware_selfreset(hw);
289
		rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00);
290
	}
291
	rtl8723_enable_fw_download(hw, true);
292
	rtl8723_write_fw(hw, version, pfwdata, fwsize, max_page);
293
	rtl8723_enable_fw_download(hw, false);
294

295
	err = rtl8723_fw_free_to_go(hw, is_8723be, max_count);
296 297 298 299
	if (err) {
		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
			 "Firmware is not ready to run!\n");
	} else {
300
		RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
			 "Firmware is ready to run!\n");
	}
	return 0;
}
EXPORT_SYMBOL_GPL(rtl8723_download_fw);

bool rtl8723_cmd_send_packet(struct ieee80211_hw *hw,
			     struct sk_buff *skb)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
	struct rtl8192_tx_ring *ring;
	struct rtl_tx_desc *pdesc;
	struct sk_buff *pskb = NULL;
	u8 own;
	unsigned long flags;

	ring = &rtlpci->tx_ring[BEACON_QUEUE];

	pskb = __skb_dequeue(&ring->queue);
	if (pskb)
		kfree_skb(pskb);

	spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);

	pdesc = &ring->desc[0];
	own = (u8) rtlpriv->cfg->ops->get_desc((u8 *)pdesc, true, HW_DESC_OWN);

	rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *)pdesc, 1, 1, skb);

	__skb_queue_tail(&ring->queue, skb);

	spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags);

	rtlpriv->cfg->ops->tx_polling(hw, BEACON_QUEUE);

	return true;
}
EXPORT_SYMBOL_GPL(rtl8723_cmd_send_packet);