1 // SPDX-License-Identifier: GPL-2.0
2 /******************************************************************************
4 * Copyright(c) 2009-2013 Realtek Corporation.
7 * wlanfae <wlanfae@realtek.com>
8 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
11 * Larry Finger <Larry.Finger@lwfinger.net>
13 *****************************************************************************/
16 #include "drv_types.h"
17 #include "usb_ops_linux.h"
18 #include "rtl8188e_spec.h"
19 #include "rtl8188e_hal.h"
21 #include <linux/firmware.h>
22 #include <linux/slab.h>
24 static void _rtl88e_enable_fw_download(struct adapter *adapt, bool enable)
29 tmp = usb_read8(adapt, REG_MCUFWDL);
30 usb_write8(adapt, REG_MCUFWDL, tmp | 0x01);
32 tmp = usb_read8(adapt, REG_MCUFWDL + 2);
33 usb_write8(adapt, REG_MCUFWDL + 2, tmp & 0xf7);
35 tmp = usb_read8(adapt, REG_MCUFWDL);
36 usb_write8(adapt, REG_MCUFWDL, tmp & 0xfe);
38 usb_write8(adapt, REG_MCUFWDL + 1, 0x00);
42 static void _rtl88e_fw_block_write(struct adapter *adapt,
43 const u8 *buffer, u32 size)
45 u32 blk_sz = sizeof(u32);
46 const u8 *byte_buffer;
47 const u32 *dword_buffer = (u32 *)buffer;
48 u32 i, write_address, blk_cnt, remain;
50 blk_cnt = size / blk_sz;
51 remain = size % blk_sz;
53 write_address = FW_8192C_START_ADDRESS;
55 for (i = 0; i < blk_cnt; i++, write_address += blk_sz)
56 usb_write32(adapt, write_address, dword_buffer[i]);
58 byte_buffer = buffer + blk_cnt * blk_sz;
59 for (i = 0; i < remain; i++, write_address++)
60 usb_write8(adapt, write_address, byte_buffer[i]);
63 static void _rtl88e_fw_page_write(struct adapter *adapt,
64 u32 page, const u8 *buffer, u32 size)
67 u8 u8page = (u8)(page & 0x07);
69 value8 = (usb_read8(adapt, REG_MCUFWDL + 2) & 0xF8) | u8page;
71 usb_write8(adapt, (REG_MCUFWDL + 2), value8);
72 _rtl88e_fw_block_write(adapt, buffer, size);
75 static void _rtl88e_write_fw(struct adapter *adapt, u8 *buffer, u32 size)
81 page_no = size / FW_8192C_PAGE_SIZE;
82 remain = size % FW_8192C_PAGE_SIZE;
84 for (page = 0; page < page_no; page++) {
85 offset = page * FW_8192C_PAGE_SIZE;
86 _rtl88e_fw_page_write(adapt, page, (buf_ptr + offset),
91 offset = page_no * FW_8192C_PAGE_SIZE;
93 _rtl88e_fw_page_write(adapt, page, (buf_ptr + offset), remain);
97 static void rtl88e_firmware_selfreset(struct adapter *adapt)
101 u1b_tmp = usb_read8(adapt, REG_SYS_FUNC_EN + 1);
102 usb_write8(adapt, REG_SYS_FUNC_EN + 1, (u1b_tmp & (~BIT(2))));
103 usb_write8(adapt, REG_SYS_FUNC_EN + 1, (u1b_tmp | BIT(2)));
106 static int _rtl88e_fw_free_to_go(struct adapter *adapt)
113 value32 = usb_read32(adapt, REG_MCUFWDL);
114 if (value32 & FWDL_CHKSUM_RPT)
116 } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
118 if (counter >= POLLING_READY_TIMEOUT_COUNT)
121 value32 = usb_read32(adapt, REG_MCUFWDL);
122 value32 |= MCUFWDL_RDY;
123 value32 &= ~WINTINI_RDY;
124 usb_write32(adapt, REG_MCUFWDL, value32);
126 rtl88e_firmware_selfreset(adapt);
130 value32 = usb_read32(adapt, REG_MCUFWDL);
131 if (value32 & WINTINI_RDY) {
136 udelay(FW_8192C_POLLING_DELAY);
138 } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
144 int rtl88eu_download_fw(struct adapter *adapt)
146 struct dvobj_priv *dvobj = adapter_to_dvobj(adapt);
147 struct device *device = dvobj_to_dev(dvobj);
148 const struct firmware *fw;
149 static const char fw_name[] = "rtlwifi/rtl8188eufw.bin";
150 struct rtl92c_firmware_header *pfwheader = NULL;
151 u8 *download_data, *fw_data;
152 size_t download_size;
153 unsigned int trailing_zeros_length;
155 if (request_firmware(&fw, fw_name, device)) {
156 dev_err(device, "Firmware %s not available\n", fw_name);
160 if (fw->size > FW_8188E_SIZE) {
161 dev_err(device, "Firmware size exceed 0x%X. Check it.\n",
163 release_firmware(fw);
167 trailing_zeros_length = (4 - fw->size % 4) % 4;
169 fw_data = kmalloc(fw->size + trailing_zeros_length, GFP_KERNEL);
171 release_firmware(fw);
175 memcpy(fw_data, fw->data, fw->size);
176 memset(fw_data + fw->size, 0, trailing_zeros_length);
178 pfwheader = (struct rtl92c_firmware_header *)fw_data;
180 if (IS_FW_HEADER_EXIST(pfwheader)) {
181 download_data = fw_data + 32;
182 download_size = fw->size + trailing_zeros_length - 32;
184 download_data = fw_data;
185 download_size = fw->size + trailing_zeros_length;
188 release_firmware(fw);
190 if (usb_read8(adapt, REG_MCUFWDL) & RAM_DL_SEL) {
191 usb_write8(adapt, REG_MCUFWDL, 0);
192 rtl88e_firmware_selfreset(adapt);
194 _rtl88e_enable_fw_download(adapt, true);
195 usb_write8(adapt, REG_MCUFWDL,
196 usb_read8(adapt, REG_MCUFWDL) | FWDL_CHKSUM_RPT);
197 _rtl88e_write_fw(adapt, download_data, download_size);
198 _rtl88e_enable_fw_download(adapt, false);
201 return _rtl88e_fw_free_to_go(adapt);