Commit | Line | Data |
---|---|---|
8c0d17b6 SW |
1 | // SPDX-License-Identifier: ISC |
2 | /* Copyright (C) 2021 MediaTek Inc. | |
3 | * | |
4 | */ | |
5 | #include <linux/module.h> | |
6 | #include <linux/firmware.h> | |
7 | ||
8 | #include <net/bluetooth/bluetooth.h> | |
9 | #include <net/bluetooth/hci_core.h> | |
10 | ||
11 | #include "btmtk.h" | |
12 | ||
13 | #define VERSION "0.1" | |
14 | ||
15 | /* It is for mt79xx download rom patch*/ | |
16 | #define MTK_FW_ROM_PATCH_HEADER_SIZE 32 | |
17 | #define MTK_FW_ROM_PATCH_GD_SIZE 64 | |
18 | #define MTK_FW_ROM_PATCH_SEC_MAP_SIZE 64 | |
19 | #define MTK_SEC_MAP_COMMON_SIZE 12 | |
20 | #define MTK_SEC_MAP_NEED_SEND_SIZE 52 | |
21 | ||
22 | struct btmtk_patch_header { | |
23 | u8 datetime[16]; | |
24 | u8 platform[4]; | |
25 | __le16 hwver; | |
26 | __le16 swver; | |
27 | __le32 magicnum; | |
28 | } __packed; | |
29 | ||
30 | struct btmtk_global_desc { | |
31 | __le32 patch_ver; | |
32 | __le32 sub_sys; | |
33 | __le32 feature_opt; | |
34 | __le32 section_num; | |
35 | } __packed; | |
36 | ||
37 | struct btmtk_section_map { | |
38 | __le32 sectype; | |
39 | __le32 secoffset; | |
40 | __le32 secsize; | |
41 | union { | |
42 | __le32 u4SecSpec[13]; | |
43 | struct { | |
44 | __le32 dlAddr; | |
45 | __le32 dlsize; | |
46 | __le32 seckeyidx; | |
47 | __le32 alignlen; | |
48 | __le32 sectype; | |
49 | __le32 dlmodecrctype; | |
50 | __le32 crc; | |
51 | __le32 reserved[6]; | |
52 | } bin_info_spec; | |
53 | }; | |
54 | } __packed; | |
55 | ||
56 | int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname, | |
57 | wmt_cmd_sync_func_t wmt_cmd_sync) | |
58 | { | |
59 | struct btmtk_hci_wmt_params wmt_params; | |
60 | struct btmtk_global_desc *globaldesc = NULL; | |
61 | struct btmtk_section_map *sectionmap; | |
62 | const struct firmware *fw; | |
63 | const u8 *fw_ptr; | |
64 | const u8 *fw_bin_ptr; | |
65 | int err, dlen, i, status; | |
66 | u8 flag, first_block, retry; | |
67 | u32 section_num, dl_size, section_offset; | |
68 | u8 cmd[64]; | |
69 | ||
70 | err = request_firmware(&fw, fwname, &hdev->dev); | |
71 | if (err < 0) { | |
72 | bt_dev_err(hdev, "Failed to load firmware file (%d)", err); | |
73 | return err; | |
74 | } | |
75 | ||
76 | fw_ptr = fw->data; | |
77 | fw_bin_ptr = fw_ptr; | |
78 | globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE); | |
79 | section_num = le32_to_cpu(globaldesc->section_num); | |
80 | ||
81 | for (i = 0; i < section_num; i++) { | |
82 | first_block = 1; | |
83 | fw_ptr = fw_bin_ptr; | |
84 | sectionmap = (struct btmtk_section_map *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE + | |
85 | MTK_FW_ROM_PATCH_GD_SIZE + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i); | |
86 | ||
87 | section_offset = le32_to_cpu(sectionmap->secoffset); | |
88 | dl_size = le32_to_cpu(sectionmap->bin_info_spec.dlsize); | |
89 | ||
90 | if (dl_size > 0) { | |
91 | retry = 20; | |
92 | while (retry > 0) { | |
93 | cmd[0] = 0; /* 0 means legacy dl mode. */ | |
94 | memcpy(cmd + 1, | |
95 | fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE + | |
96 | MTK_FW_ROM_PATCH_GD_SIZE + | |
97 | MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i + | |
98 | MTK_SEC_MAP_COMMON_SIZE, | |
99 | MTK_SEC_MAP_NEED_SEND_SIZE + 1); | |
100 | ||
101 | wmt_params.op = BTMTK_WMT_PATCH_DWNLD; | |
102 | wmt_params.status = &status; | |
103 | wmt_params.flag = 0; | |
104 | wmt_params.dlen = MTK_SEC_MAP_NEED_SEND_SIZE + 1; | |
105 | wmt_params.data = &cmd; | |
106 | ||
107 | err = wmt_cmd_sync(hdev, &wmt_params); | |
108 | if (err < 0) { | |
109 | bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)", | |
110 | err); | |
111 | goto err_release_fw; | |
112 | } | |
113 | ||
114 | if (status == BTMTK_WMT_PATCH_UNDONE) { | |
115 | break; | |
116 | } else if (status == BTMTK_WMT_PATCH_PROGRESS) { | |
117 | msleep(100); | |
118 | retry--; | |
119 | } else if (status == BTMTK_WMT_PATCH_DONE) { | |
120 | goto next_section; | |
121 | } else { | |
122 | bt_dev_err(hdev, "Failed wmt patch dwnld status (%d)", | |
123 | status); | |
995d948c | 124 | err = -EIO; |
8c0d17b6 SW |
125 | goto err_release_fw; |
126 | } | |
127 | } | |
128 | ||
129 | fw_ptr += section_offset; | |
130 | wmt_params.op = BTMTK_WMT_PATCH_DWNLD; | |
131 | wmt_params.status = NULL; | |
132 | ||
133 | while (dl_size > 0) { | |
134 | dlen = min_t(int, 250, dl_size); | |
135 | if (first_block == 1) { | |
136 | flag = 1; | |
137 | first_block = 0; | |
138 | } else if (dl_size - dlen <= 0) { | |
139 | flag = 3; | |
140 | } else { | |
141 | flag = 2; | |
142 | } | |
143 | ||
144 | wmt_params.flag = flag; | |
145 | wmt_params.dlen = dlen; | |
146 | wmt_params.data = fw_ptr; | |
147 | ||
148 | err = wmt_cmd_sync(hdev, &wmt_params); | |
149 | if (err < 0) { | |
150 | bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)", | |
151 | err); | |
152 | goto err_release_fw; | |
153 | } | |
154 | ||
155 | dl_size -= dlen; | |
156 | fw_ptr += dlen; | |
157 | } | |
158 | } | |
159 | next_section: | |
160 | continue; | |
161 | } | |
162 | /* Wait a few moments for firmware activation done */ | |
163 | usleep_range(100000, 120000); | |
164 | ||
165 | err_release_fw: | |
166 | release_firmware(fw); | |
167 | ||
168 | return err; | |
169 | } | |
170 | EXPORT_SYMBOL_GPL(btmtk_setup_firmware_79xx); | |
171 | ||
172 | int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname, | |
173 | wmt_cmd_sync_func_t wmt_cmd_sync) | |
174 | { | |
175 | struct btmtk_hci_wmt_params wmt_params; | |
176 | const struct firmware *fw; | |
177 | const u8 *fw_ptr; | |
178 | size_t fw_size; | |
179 | int err, dlen; | |
180 | u8 flag, param; | |
181 | ||
182 | err = request_firmware(&fw, fwname, &hdev->dev); | |
183 | if (err < 0) { | |
184 | bt_dev_err(hdev, "Failed to load firmware file (%d)", err); | |
185 | return err; | |
186 | } | |
187 | ||
188 | /* Power on data RAM the firmware relies on. */ | |
189 | param = 1; | |
190 | wmt_params.op = BTMTK_WMT_FUNC_CTRL; | |
191 | wmt_params.flag = 3; | |
192 | wmt_params.dlen = sizeof(param); | |
193 | wmt_params.data = ¶m; | |
194 | wmt_params.status = NULL; | |
195 | ||
196 | err = wmt_cmd_sync(hdev, &wmt_params); | |
197 | if (err < 0) { | |
198 | bt_dev_err(hdev, "Failed to power on data RAM (%d)", err); | |
199 | goto err_release_fw; | |
200 | } | |
201 | ||
202 | fw_ptr = fw->data; | |
203 | fw_size = fw->size; | |
204 | ||
205 | /* The size of patch header is 30 bytes, should be skip */ | |
206 | if (fw_size < 30) { | |
207 | err = -EINVAL; | |
208 | goto err_release_fw; | |
209 | } | |
210 | ||
211 | fw_size -= 30; | |
212 | fw_ptr += 30; | |
213 | flag = 1; | |
214 | ||
215 | wmt_params.op = BTMTK_WMT_PATCH_DWNLD; | |
216 | wmt_params.status = NULL; | |
217 | ||
218 | while (fw_size > 0) { | |
219 | dlen = min_t(int, 250, fw_size); | |
220 | ||
221 | /* Tell device the position in sequence */ | |
222 | if (fw_size - dlen <= 0) | |
223 | flag = 3; | |
224 | else if (fw_size < fw->size - 30) | |
225 | flag = 2; | |
226 | ||
227 | wmt_params.flag = flag; | |
228 | wmt_params.dlen = dlen; | |
229 | wmt_params.data = fw_ptr; | |
230 | ||
231 | err = wmt_cmd_sync(hdev, &wmt_params); | |
232 | if (err < 0) { | |
233 | bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)", | |
234 | err); | |
235 | goto err_release_fw; | |
236 | } | |
237 | ||
238 | fw_size -= dlen; | |
239 | fw_ptr += dlen; | |
240 | } | |
241 | ||
242 | wmt_params.op = BTMTK_WMT_RST; | |
243 | wmt_params.flag = 4; | |
244 | wmt_params.dlen = 0; | |
245 | wmt_params.data = NULL; | |
246 | wmt_params.status = NULL; | |
247 | ||
248 | /* Activate funciton the firmware providing to */ | |
249 | err = wmt_cmd_sync(hdev, &wmt_params); | |
250 | if (err < 0) { | |
251 | bt_dev_err(hdev, "Failed to send wmt rst (%d)", err); | |
252 | goto err_release_fw; | |
253 | } | |
254 | ||
255 | /* Wait a few moments for firmware activation done */ | |
256 | usleep_range(10000, 12000); | |
257 | ||
258 | err_release_fw: | |
259 | release_firmware(fw); | |
260 | ||
261 | return err; | |
262 | } | |
263 | EXPORT_SYMBOL_GPL(btmtk_setup_firmware); | |
264 | ||
265 | int btmtk_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) | |
266 | { | |
267 | struct sk_buff *skb; | |
268 | long ret; | |
269 | ||
270 | skb = __hci_cmd_sync(hdev, 0xfc1a, 6, bdaddr, HCI_INIT_TIMEOUT); | |
271 | if (IS_ERR(skb)) { | |
272 | ret = PTR_ERR(skb); | |
273 | bt_dev_err(hdev, "changing Mediatek device address failed (%ld)", | |
274 | ret); | |
275 | return ret; | |
276 | } | |
277 | kfree_skb(skb); | |
278 | ||
279 | return 0; | |
280 | } | |
281 | EXPORT_SYMBOL_GPL(btmtk_set_bdaddr); | |
282 | ||
283 | MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); | |
284 | MODULE_AUTHOR("Mark Chen <mark-yw.chen@mediatek.com>"); | |
285 | MODULE_DESCRIPTION("Bluetooth support for MediaTek devices ver " VERSION); | |
286 | MODULE_VERSION(VERSION); | |
287 | MODULE_LICENSE("GPL"); | |
f5c3f989 | 288 | MODULE_FIRMWARE(FIRMWARE_MT7622); |
8c0d17b6 SW |
289 | MODULE_FIRMWARE(FIRMWARE_MT7663); |
290 | MODULE_FIRMWARE(FIRMWARE_MT7668); | |
291 | MODULE_FIRMWARE(FIRMWARE_MT7961); |