fw.c revision d6c28c23f89b00a01b34670f0f1ddcdc2e0bca67
1/****************************************************************************** 2 * 3 * Copyright(c) 2009-2013 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 * 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 17 * 18 * The full GNU General Public License is included in this distribution in the 19 * file called LICENSE. 20 * 21 * Contact Information: 22 * wlanfae <wlanfae@realtek.com> 23 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, 24 * Hsinchu 300, Taiwan. 25 * 26 * Larry Finger <Larry.Finger@lwfinger.net> 27 * 28 *****************************************************************************/ 29 30#include "fw.h" 31#include "drv_types.h" 32#include "usb_ops_linux.h" 33#include "rtl8188e_spec.h" 34#include "rtl8188e_hal.h" 35 36#include <linux/firmware.h> 37#include <linux/kmemleak.h> 38 39static void _rtl88e_enable_fw_download(struct adapter *adapt, bool enable) 40{ 41 u8 tmp; 42 43 if (enable) { 44 tmp = usb_read8(adapt, REG_MCUFWDL); 45 usb_write8(adapt, REG_MCUFWDL, tmp | 0x01); 46 47 tmp = usb_read8(adapt, REG_MCUFWDL + 2); 48 usb_write8(adapt, REG_MCUFWDL + 2, tmp & 0xf7); 49 } else { 50 tmp = usb_read8(adapt, REG_MCUFWDL); 51 usb_write8(adapt, REG_MCUFWDL, tmp & 0xfe); 52 53 usb_write8(adapt, REG_MCUFWDL + 1, 0x00); 54 } 55} 56 57static void _rtl88e_fw_block_write(struct adapter *adapt, 58 const u8 *buffer, u32 size) 59{ 60 u32 blk_sz = sizeof(u32); 61 u8 *buf_ptr = (u8 *)buffer; 62 u32 *pu4BytePtr = (u32 *)buffer; 63 u32 i, offset, blk_cnt, remain; 64 65 blk_cnt = size / blk_sz; 66 remain = size % blk_sz; 67 68 for (i = 0; i < blk_cnt; i++) { 69 offset = i * blk_sz; 70 usb_write32(adapt, (FW_8192C_START_ADDRESS + offset), 71 *(pu4BytePtr + i)); 72 } 73 74 if (remain) { 75 offset = blk_cnt * blk_sz; 76 buf_ptr += offset; 77 for (i = 0; i < remain; i++) { 78 usb_write8(adapt, (FW_8192C_START_ADDRESS + 79 offset + i), *(buf_ptr + i)); 80 } 81 } 82} 83 84static void _rtl88e_fill_dummy(u8 *pfwbuf, u32 *pfwlen) 85{ 86 u32 fwlen = *pfwlen; 87 u8 remain = (u8) (fwlen % 4); 88 89 remain = (remain == 0) ? 0 : (4 - remain); 90 91 while (remain > 0) { 92 pfwbuf[fwlen] = 0; 93 fwlen++; 94 remain--; 95 } 96 97 *pfwlen = fwlen; 98} 99 100static void _rtl88e_fw_page_write(struct adapter *adapt, 101 u32 page, const u8 *buffer, u32 size) 102{ 103 u8 value8; 104 u8 u8page = (u8) (page & 0x07); 105 106 value8 = (usb_read8(adapt, REG_MCUFWDL + 2) & 0xF8) | u8page; 107 108 usb_write8(adapt, (REG_MCUFWDL + 2), value8); 109 _rtl88e_fw_block_write(adapt, buffer, size); 110} 111 112static void _rtl88e_write_fw(struct adapter *adapt, u8 *buffer, u32 size) 113{ 114 u8 *buf_ptr = buffer; 115 u32 page_no, remain; 116 u32 page, offset; 117 118 _rtl88e_fill_dummy(buf_ptr, &size); 119 120 page_no = size / FW_8192C_PAGE_SIZE; 121 remain = size % FW_8192C_PAGE_SIZE; 122 123 for (page = 0; page < page_no; page++) { 124 offset = page * FW_8192C_PAGE_SIZE; 125 _rtl88e_fw_page_write(adapt, page, (buf_ptr + offset), 126 FW_8192C_PAGE_SIZE); 127 } 128 129 if (remain) { 130 offset = page_no * FW_8192C_PAGE_SIZE; 131 page = page_no; 132 _rtl88e_fw_page_write(adapt, page, (buf_ptr + offset), remain); 133 } 134} 135 136static void rtl88e_firmware_selfreset(struct adapter *adapt) 137{ 138 u8 u1b_tmp; 139 140 u1b_tmp = usb_read8(adapt, REG_SYS_FUNC_EN+1); 141 usb_write8(adapt, REG_SYS_FUNC_EN+1, (u1b_tmp & (~BIT(2)))); 142 usb_write8(adapt, REG_SYS_FUNC_EN+1, (u1b_tmp | BIT(2))); 143} 144 145static int _rtl88e_fw_free_to_go(struct adapter *adapt) 146{ 147 int err = -EIO; 148 u32 counter = 0; 149 u32 value32; 150 151 do { 152 value32 = usb_read32(adapt, REG_MCUFWDL); 153 if (value32 & FWDL_ChkSum_rpt) 154 break; 155 } while (counter++ < POLLING_READY_TIMEOUT_COUNT); 156 157 if (counter >= POLLING_READY_TIMEOUT_COUNT) { 158 goto exit; 159 } 160 161 value32 = usb_read32(adapt, REG_MCUFWDL); 162 value32 |= MCUFWDL_RDY; 163 value32 &= ~WINTINI_RDY; 164 usb_write32(adapt, REG_MCUFWDL, value32); 165 166 rtl88e_firmware_selfreset(adapt); 167 counter = 0; 168 169 do { 170 value32 = usb_read32(adapt, REG_MCUFWDL); 171 if (value32 & WINTINI_RDY) { 172 err = 0; 173 goto exit; 174 } 175 176 udelay(FW_8192C_POLLING_DELAY); 177 178 } while (counter++ < POLLING_READY_TIMEOUT_COUNT); 179 180exit: 181 return err; 182} 183 184int rtl88e_download_fw(struct adapter *adapt) 185{ 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; 192 u8 *pfwdata; 193 u32 fwsize; 194 int err; 195 196 if (request_firmware(&fw, fw_name, device)){ 197 dev_err(device, "Firmware %s not available\n", fw_name); 198 return -ENOENT; 199 } 200 201 if (fw->size > FW_8188E_SIZE) { 202 dev_err(device,"Firmware size exceed 0x%X. Check it.\n", 203 FW_8188E_SIZE); 204 return -1; 205 } 206 207 pfwdata = kzalloc(FW_8188E_SIZE, GFP_KERNEL); 208 if (!pfwdata) 209 return -ENOMEM; 210 211 rtlhal->pfirmware = pfwdata; 212 memcpy(rtlhal->pfirmware, fw->data, fw->size); 213 rtlhal->fwsize = fw->size; 214 release_firmware(fw); 215 216 fwsize = rtlhal->fwsize; 217 pfwheader = (struct rtl92c_firmware_header *)pfwdata; 218 219 if (IS_FW_HEADER_EXIST(pfwheader)) { 220 pfwdata = pfwdata + 32; 221 fwsize = fwsize - 32; 222 } 223 224 if (usb_read8(adapt, REG_MCUFWDL) & RAM_DL_SEL) { 225 usb_write8(adapt, REG_MCUFWDL, 0); 226 rtl88e_firmware_selfreset(adapt); 227 } 228 _rtl88e_enable_fw_download(adapt, true); 229 usb_write8(adapt, REG_MCUFWDL, usb_read8(adapt, REG_MCUFWDL) | FWDL_ChkSum_rpt); 230 _rtl88e_write_fw(adapt, pfwdata, fwsize); 231 _rtl88e_enable_fw_download(adapt, false); 232 233 err = _rtl88e_fw_free_to_go(adapt); 234 235 return err; 236} 237