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
++, offset
+= blk_sz
) {
71 usb_write32(adapt
, offset
, pu4BytePtr
[i
]);
74 buf_ptr
+= blk_cnt
* blk_sz
;
75 for (i
= 0; i
< remain
; i
++, offset
++) {
76 usb_write8(adapt
, offset
, buf_ptr
[i
]);
80 static void _rtl88e_fill_dummy(u8
*pfwbuf
, u32
*pfwlen
)
83 u8 remain
= (u8
)(fwlen
% 4);
85 remain
= (remain
== 0) ? 0 : (4 - remain
);
96 static void _rtl88e_fw_page_write(struct adapter
*adapt
,
97 u32 page
, const u8
*buffer
, u32 size
)
100 u8 u8page
= (u8
)(page
& 0x07);
102 value8
= (usb_read8(adapt
, REG_MCUFWDL
+ 2) & 0xF8) | u8page
;
104 usb_write8(adapt
, (REG_MCUFWDL
+ 2), value8
);
105 _rtl88e_fw_block_write(adapt
, buffer
, size
);
108 static void _rtl88e_write_fw(struct adapter
*adapt
, u8
*buffer
, u32 size
)
110 u8
*buf_ptr
= buffer
;
114 _rtl88e_fill_dummy(buf_ptr
, &size
);
116 page_no
= size
/ FW_8192C_PAGE_SIZE
;
117 remain
= size
% FW_8192C_PAGE_SIZE
;
119 for (page
= 0; page
< page_no
; page
++) {
120 offset
= page
* FW_8192C_PAGE_SIZE
;
121 _rtl88e_fw_page_write(adapt
, page
, (buf_ptr
+ offset
),
126 offset
= page_no
* FW_8192C_PAGE_SIZE
;
128 _rtl88e_fw_page_write(adapt
, page
, (buf_ptr
+ offset
), remain
);
132 static void rtl88e_firmware_selfreset(struct adapter
*adapt
)
136 u1b_tmp
= usb_read8(adapt
, REG_SYS_FUNC_EN
+1);
137 usb_write8(adapt
, REG_SYS_FUNC_EN
+1, (u1b_tmp
& (~BIT(2))));
138 usb_write8(adapt
, REG_SYS_FUNC_EN
+1, (u1b_tmp
| BIT(2)));
141 static int _rtl88e_fw_free_to_go(struct adapter
*adapt
)
148 value32
= usb_read32(adapt
, REG_MCUFWDL
);
149 if (value32
& FWDL_ChkSum_rpt
)
151 } while (counter
++ < POLLING_READY_TIMEOUT_COUNT
);
153 if (counter
>= POLLING_READY_TIMEOUT_COUNT
)
156 value32
= usb_read32(adapt
, REG_MCUFWDL
);
157 value32
|= MCUFWDL_RDY
;
158 value32
&= ~WINTINI_RDY
;
159 usb_write32(adapt
, REG_MCUFWDL
, value32
);
161 rtl88e_firmware_selfreset(adapt
);
165 value32
= usb_read32(adapt
, REG_MCUFWDL
);
166 if (value32
& WINTINI_RDY
) {
171 udelay(FW_8192C_POLLING_DELAY
);
173 } while (counter
++ < POLLING_READY_TIMEOUT_COUNT
);
179 int rtl88eu_download_fw(struct adapter
*adapt
)
181 struct hal_data_8188e
*rtlhal
= GET_HAL_DATA(adapt
);
182 struct dvobj_priv
*dvobj
= adapter_to_dvobj(adapt
);
183 struct device
*device
= dvobj_to_dev(dvobj
);
184 const struct firmware
*fw
;
185 const char fw_name
[] = "rtlwifi/rtl8188eufw.bin";
186 struct rtl92c_firmware_header
*pfwheader
= NULL
;
190 if (request_firmware(&fw
, fw_name
, device
)) {
191 dev_err(device
, "Firmware %s not available\n", fw_name
);
195 if (fw
->size
> FW_8188E_SIZE
) {
196 dev_err(device
, "Firmware size exceed 0x%X. Check it.\n",
201 pfwdata
= kzalloc(FW_8188E_SIZE
, GFP_KERNEL
);
205 rtlhal
->pfirmware
= pfwdata
;
206 memcpy(rtlhal
->pfirmware
, fw
->data
, fw
->size
);
207 rtlhal
->fwsize
= fw
->size
;
208 release_firmware(fw
);
210 fwsize
= rtlhal
->fwsize
;
211 pfwheader
= (struct rtl92c_firmware_header
*)pfwdata
;
213 if (IS_FW_HEADER_EXIST(pfwheader
)) {
214 pfwdata
= pfwdata
+ 32;
215 fwsize
= fwsize
- 32;
218 if (usb_read8(adapt
, REG_MCUFWDL
) & RAM_DL_SEL
) {
219 usb_write8(adapt
, REG_MCUFWDL
, 0);
220 rtl88e_firmware_selfreset(adapt
);
222 _rtl88e_enable_fw_download(adapt
, true);
223 usb_write8(adapt
, REG_MCUFWDL
, usb_read8(adapt
, REG_MCUFWDL
) | FWDL_ChkSum_rpt
);
224 _rtl88e_write_fw(adapt
, pfwdata
, fwsize
);
225 _rtl88e_enable_fw_download(adapt
, false);
227 return _rtl88e_fw_free_to_go(adapt
);