1 /******************************************************************************
3 * Copyright(c) 2009-2013 Realtek Corporation.
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.
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
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 * The full GNU General Public License is included in this distribution in the
19 * file called LICENSE.
21 * Contact Information:
22 * wlanfae <wlanfae@realtek.com>
23 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
24 * Hsinchu 300, Taiwan.
26 * Larry Finger <Larry.Finger@lwfinger.net>
28 *****************************************************************************/
31 #include "drv_types.h"
32 #include "usb_ops_linux.h"
33 #include "rtl8188e_spec.h"
34 #include "rtl8188e_hal.h"
36 #include <linux/firmware.h>
37 #include <linux/kmemleak.h>
39 static void _rtl88e_enable_fw_download(struct adapter
*adapt
, bool enable
)
44 tmp
= usb_read8(adapt
, REG_MCUFWDL
);
45 usb_write8(adapt
, REG_MCUFWDL
, tmp
| 0x01);
47 tmp
= usb_read8(adapt
, REG_MCUFWDL
+ 2);
48 usb_write8(adapt
, REG_MCUFWDL
+ 2, tmp
& 0xf7);
50 tmp
= usb_read8(adapt
, REG_MCUFWDL
);
51 usb_write8(adapt
, REG_MCUFWDL
, tmp
& 0xfe);
53 usb_write8(adapt
, REG_MCUFWDL
+ 1, 0x00);
57 static void _rtl88e_fw_block_write(struct adapter
*adapt
,
58 const u8
*buffer
, u32 size
)
60 u32 blk_sz
= sizeof(u32
);
61 u8
*buf_ptr
= (u8
*)buffer
;
62 u32
*pu4BytePtr
= (u32
*)buffer
;
63 u32 i
, offset
, blk_cnt
, remain
;
65 blk_cnt
= size
/ blk_sz
;
66 remain
= size
% blk_sz
;
68 offset
= FW_8192C_START_ADDRESS
;
70 for (i
= 0; i
< blk_cnt
; i
++) {
71 usb_write32(adapt
, offset
, pu4BytePtr
[i
]);
76 offset
= blk_cnt
* blk_sz
;
78 for (i
= 0; i
< remain
; i
++) {
79 usb_write8(adapt
, (FW_8192C_START_ADDRESS
+
80 offset
+ i
), buf_ptr
[i
]);
85 static void _rtl88e_fill_dummy(u8
*pfwbuf
, u32
*pfwlen
)
88 u8 remain
= (u8
)(fwlen
% 4);
90 remain
= (remain
== 0) ? 0 : (4 - remain
);
101 static void _rtl88e_fw_page_write(struct adapter
*adapt
,
102 u32 page
, const u8
*buffer
, u32 size
)
105 u8 u8page
= (u8
)(page
& 0x07);
107 value8
= (usb_read8(adapt
, REG_MCUFWDL
+ 2) & 0xF8) | u8page
;
109 usb_write8(adapt
, (REG_MCUFWDL
+ 2), value8
);
110 _rtl88e_fw_block_write(adapt
, buffer
, size
);
113 static void _rtl88e_write_fw(struct adapter
*adapt
, u8
*buffer
, u32 size
)
115 u8
*buf_ptr
= buffer
;
119 _rtl88e_fill_dummy(buf_ptr
, &size
);
121 page_no
= size
/ FW_8192C_PAGE_SIZE
;
122 remain
= size
% FW_8192C_PAGE_SIZE
;
124 for (page
= 0; page
< page_no
; page
++) {
125 offset
= page
* FW_8192C_PAGE_SIZE
;
126 _rtl88e_fw_page_write(adapt
, page
, (buf_ptr
+ offset
),
131 offset
= page_no
* FW_8192C_PAGE_SIZE
;
133 _rtl88e_fw_page_write(adapt
, page
, (buf_ptr
+ offset
), remain
);
137 static void rtl88e_firmware_selfreset(struct adapter
*adapt
)
141 u1b_tmp
= usb_read8(adapt
, REG_SYS_FUNC_EN
+1);
142 usb_write8(adapt
, REG_SYS_FUNC_EN
+1, (u1b_tmp
& (~BIT(2))));
143 usb_write8(adapt
, REG_SYS_FUNC_EN
+1, (u1b_tmp
| BIT(2)));
146 static int _rtl88e_fw_free_to_go(struct adapter
*adapt
)
153 value32
= usb_read32(adapt
, REG_MCUFWDL
);
154 if (value32
& FWDL_ChkSum_rpt
)
156 } while (counter
++ < POLLING_READY_TIMEOUT_COUNT
);
158 if (counter
>= POLLING_READY_TIMEOUT_COUNT
)
161 value32
= usb_read32(adapt
, REG_MCUFWDL
);
162 value32
|= MCUFWDL_RDY
;
163 value32
&= ~WINTINI_RDY
;
164 usb_write32(adapt
, REG_MCUFWDL
, value32
);
166 rtl88e_firmware_selfreset(adapt
);
170 value32
= usb_read32(adapt
, REG_MCUFWDL
);
171 if (value32
& WINTINI_RDY
) {
176 udelay(FW_8192C_POLLING_DELAY
);
178 } while (counter
++ < POLLING_READY_TIMEOUT_COUNT
);
184 int rtl88eu_download_fw(struct adapter
*adapt
)
186 struct hal_data_8188e
*rtlhal
= GET_HAL_DATA(adapt
);
187 struct dvobj_priv
*dvobj
= adapter_to_dvobj(adapt
);
188 struct device
*device
= dvobj_to_dev(dvobj
);
189 const struct firmware
*fw
;
190 const char fw_name
[] = "rtlwifi/rtl8188eufw.bin";
191 struct rtl92c_firmware_header
*pfwheader
= NULL
;
195 if (request_firmware(&fw
, fw_name
, device
)) {
196 dev_err(device
, "Firmware %s not available\n", fw_name
);
200 if (fw
->size
> FW_8188E_SIZE
) {
201 dev_err(device
, "Firmware size exceed 0x%X. Check it.\n",
206 pfwdata
= kzalloc(FW_8188E_SIZE
, GFP_KERNEL
);
210 rtlhal
->pfirmware
= pfwdata
;
211 memcpy(rtlhal
->pfirmware
, fw
->data
, fw
->size
);
212 rtlhal
->fwsize
= fw
->size
;
213 release_firmware(fw
);
215 fwsize
= rtlhal
->fwsize
;
216 pfwheader
= (struct rtl92c_firmware_header
*)pfwdata
;
218 if (IS_FW_HEADER_EXIST(pfwheader
)) {
219 pfwdata
= pfwdata
+ 32;
220 fwsize
= fwsize
- 32;
223 if (usb_read8(adapt
, REG_MCUFWDL
) & RAM_DL_SEL
) {
224 usb_write8(adapt
, REG_MCUFWDL
, 0);
225 rtl88e_firmware_selfreset(adapt
);
227 _rtl88e_enable_fw_download(adapt
, true);
228 usb_write8(adapt
, REG_MCUFWDL
, usb_read8(adapt
, REG_MCUFWDL
) | FWDL_ChkSum_rpt
);
229 _rtl88e_write_fw(adapt
, pfwdata
, fwsize
);
230 _rtl88e_enable_fw_download(adapt
, false);
232 return _rtl88e_fw_free_to_go(adapt
);