1 // SPDX-License-Identifier: GPL-2.0
2 /******************************************************************************
3 *
4 * Copyright(c) 2009-2013 Realtek Corporation.
5 *
6 * Contact Information:
7 * wlanfae <wlanfae@realtek.com>
8 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
9 * Hsinchu 300, Taiwan.
10 *
11 * Larry Finger <Larry.Finger@lwfinger.net>
12 *
13 *****************************************************************************/
14
15 #include "fw.h"
16 #include "drv_types.h"
17 #include "usb_ops_linux.h"
18 #include "rtl8188e_spec.h"
19 #include "rtl8188e_hal.h"
20
21 #include <linux/firmware.h>
22 #include <linux/slab.h>
23
_rtl88e_enable_fw_download(struct adapter * adapt,bool enable)24 static void _rtl88e_enable_fw_download(struct adapter *adapt, bool enable)
25 {
26 u8 tmp;
27
28 if (enable) {
29 tmp = usb_read8(adapt, REG_MCUFWDL);
30 usb_write8(adapt, REG_MCUFWDL, tmp | 0x01);
31
32 tmp = usb_read8(adapt, REG_MCUFWDL + 2);
33 usb_write8(adapt, REG_MCUFWDL + 2, tmp & 0xf7);
34 } else {
35 tmp = usb_read8(adapt, REG_MCUFWDL);
36 usb_write8(adapt, REG_MCUFWDL, tmp & 0xfe);
37
38 usb_write8(adapt, REG_MCUFWDL + 1, 0x00);
39 }
40 }
41
_rtl88e_fw_block_write(struct adapter * adapt,const u8 * buffer,u32 size)42 static void _rtl88e_fw_block_write(struct adapter *adapt,
43 const u8 *buffer, u32 size)
44 {
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;
49
50 blk_cnt = size / blk_sz;
51 remain = size % blk_sz;
52
53 write_address = FW_8192C_START_ADDRESS;
54
55 for (i = 0; i < blk_cnt; i++, write_address += blk_sz)
56 usb_write32(adapt, write_address, dword_buffer[i]);
57
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]);
61 }
62
_rtl88e_fw_page_write(struct adapter * adapt,u32 page,const u8 * buffer,u32 size)63 static void _rtl88e_fw_page_write(struct adapter *adapt,
64 u32 page, const u8 *buffer, u32 size)
65 {
66 u8 value8;
67 u8 u8page = (u8)(page & 0x07);
68
69 value8 = (usb_read8(adapt, REG_MCUFWDL + 2) & 0xF8) | u8page;
70
71 usb_write8(adapt, (REG_MCUFWDL + 2), value8);
72 _rtl88e_fw_block_write(adapt, buffer, size);
73 }
74
_rtl88e_write_fw(struct adapter * adapt,u8 * buffer,u32 size)75 static void _rtl88e_write_fw(struct adapter *adapt, u8 *buffer, u32 size)
76 {
77 u8 *buf_ptr = buffer;
78 u32 page_no, remain;
79 u32 page, offset;
80
81 page_no = size / FW_8192C_PAGE_SIZE;
82 remain = size % FW_8192C_PAGE_SIZE;
83
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),
87 FW_8192C_PAGE_SIZE);
88 }
89
90 if (remain) {
91 offset = page_no * FW_8192C_PAGE_SIZE;
92 page = page_no;
93 _rtl88e_fw_page_write(adapt, page, (buf_ptr + offset), remain);
94 }
95 }
96
rtl88e_firmware_selfreset(struct adapter * adapt)97 static void rtl88e_firmware_selfreset(struct adapter *adapt)
98 {
99 u8 u1b_tmp;
100
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)));
104 }
105
_rtl88e_fw_free_to_go(struct adapter * adapt)106 static int _rtl88e_fw_free_to_go(struct adapter *adapt)
107 {
108 int err = -EIO;
109 u32 counter = 0;
110 u32 value32;
111
112 do {
113 value32 = usb_read32(adapt, REG_MCUFWDL);
114 if (value32 & FWDL_CHKSUM_RPT)
115 break;
116 } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
117
118 if (counter >= POLLING_READY_TIMEOUT_COUNT)
119 goto exit;
120
121 value32 = usb_read32(adapt, REG_MCUFWDL);
122 value32 |= MCUFWDL_RDY;
123 value32 &= ~WINTINI_RDY;
124 usb_write32(adapt, REG_MCUFWDL, value32);
125
126 rtl88e_firmware_selfreset(adapt);
127 counter = 0;
128
129 do {
130 value32 = usb_read32(adapt, REG_MCUFWDL);
131 if (value32 & WINTINI_RDY) {
132 err = 0;
133 goto exit;
134 }
135
136 udelay(FW_8192C_POLLING_DELAY);
137
138 } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
139
140 exit:
141 return err;
142 }
143
rtl88eu_download_fw(struct adapter * adapt)144 int rtl88eu_download_fw(struct adapter *adapt)
145 {
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;
154
155 if (request_firmware(&fw, fw_name, device)) {
156 dev_err(device, "Firmware %s not available\n", fw_name);
157 return -ENOENT;
158 }
159
160 if (fw->size > FW_8188E_SIZE) {
161 dev_err(device, "Firmware size exceed 0x%X. Check it.\n",
162 FW_8188E_SIZE);
163 release_firmware(fw);
164 return -1;
165 }
166
167 trailing_zeros_length = (4 - fw->size % 4) % 4;
168
169 fw_data = kmalloc(fw->size + trailing_zeros_length, GFP_KERNEL);
170 if (!fw_data) {
171 release_firmware(fw);
172 return -ENOMEM;
173 }
174
175 memcpy(fw_data, fw->data, fw->size);
176 memset(fw_data + fw->size, 0, trailing_zeros_length);
177
178 pfwheader = (struct rtl92c_firmware_header *)fw_data;
179
180 if (IS_FW_HEADER_EXIST(pfwheader)) {
181 download_data = fw_data + 32;
182 download_size = fw->size + trailing_zeros_length - 32;
183 } else {
184 download_data = fw_data;
185 download_size = fw->size + trailing_zeros_length;
186 }
187
188 release_firmware(fw);
189
190 if (usb_read8(adapt, REG_MCUFWDL) & RAM_DL_SEL) {
191 usb_write8(adapt, REG_MCUFWDL, 0);
192 rtl88e_firmware_selfreset(adapt);
193 }
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);
199
200 kfree(fw_data);
201 return _rtl88e_fw_free_to_go(adapt);
202 }
203