GNU Linux-libre 4.19.245-gnu1
[releases.git] / drivers / staging / rtl8188eu / hal / fw.c
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
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
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
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
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
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
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
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         const char fw_name[] = "/*(DEBLOBBED)*/";
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 (reject_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, usb_read8(adapt, REG_MCUFWDL) | FWDL_ChkSum_rpt);
196         _rtl88e_write_fw(adapt, download_data, download_size);
197         _rtl88e_enable_fw_download(adapt, false);
198
199         kfree(fw_data);
200         return _rtl88e_fw_free_to_go(adapt);
201 }