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