xref: /openbmc/linux/drivers/bluetooth/btrtl.c (revision fac59652993f075d57860769c99045b3ca18780d)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2db33c77dSCarlo Caione /*
3db33c77dSCarlo Caione  *  Bluetooth support for Realtek devices
4db33c77dSCarlo Caione  *
5db33c77dSCarlo Caione  *  Copyright (C) 2015 Endless Mobile, Inc.
6db33c77dSCarlo Caione  */
7db33c77dSCarlo Caione 
8db33c77dSCarlo Caione #include <linux/module.h>
9db33c77dSCarlo Caione #include <linux/firmware.h>
10db33c77dSCarlo Caione #include <asm/unaligned.h>
11db33c77dSCarlo Caione #include <linux/usb.h>
12db33c77dSCarlo Caione 
13db33c77dSCarlo Caione #include <net/bluetooth/bluetooth.h>
14db33c77dSCarlo Caione #include <net/bluetooth/hci_core.h>
15db33c77dSCarlo Caione 
16db33c77dSCarlo Caione #include "btrtl.h"
17db33c77dSCarlo Caione 
18db33c77dSCarlo Caione #define VERSION "0.1"
19db33c77dSCarlo Caione 
20c0123cb6SVasily Khoruzhick #define RTL_CHIP_8723CS_CG	3
21c0123cb6SVasily Khoruzhick #define RTL_CHIP_8723CS_VF	4
22c0123cb6SVasily Khoruzhick #define RTL_CHIP_8723CS_XX	5
23db33c77dSCarlo Caione #define RTL_EPATCH_SIGNATURE	"Realtech"
249a24ce5eSMax Chou #define RTL_EPATCH_SIGNATURE_V2	"RTBTCore"
25c0123cb6SVasily Khoruzhick #define RTL_ROM_LMP_8703B	0x8703
26db33c77dSCarlo Caione #define RTL_ROM_LMP_8723A	0x1200
27db33c77dSCarlo Caione #define RTL_ROM_LMP_8723B	0x8723
28db33c77dSCarlo Caione #define RTL_ROM_LMP_8821A	0x8821
29db33c77dSCarlo Caione #define RTL_ROM_LMP_8761A	0x8761
301110a2dbSLarry Finger #define RTL_ROM_LMP_8822B	0x8822
310d484db6SMax Chou #define RTL_ROM_LMP_8852A	0x8852
327948fe1cSMax Chou #define RTL_ROM_LMP_8851B	0x8851
33b85b0ee1SMartin Blumenstingl #define RTL_CONFIG_MAGIC	0x8723ab55
34db33c77dSCarlo Caione 
35044014ceSHilda Wu #define RTL_VSC_OP_COREDUMP	0xfcff
36044014ceSHilda Wu 
37907f8499SAlex Lu #define IC_MATCH_FL_LMPSUBV	(1 << 0)
38907f8499SAlex Lu #define IC_MATCH_FL_HCIREV	(1 << 1)
39c50903e3SMartin Blumenstingl #define IC_MATCH_FL_HCIVER	(1 << 2)
40c50903e3SMartin Blumenstingl #define IC_MATCH_FL_HCIBUS	(1 << 3)
41c0123cb6SVasily Khoruzhick #define IC_MATCH_FL_CHIP_TYPE	(1 << 4)
426f9ff246SMax Chou #define IC_INFO(lmps, hcir, hciv, bus) \
436f9ff246SMax Chou 	.match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | \
446f9ff246SMax Chou 		       IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS, \
45907f8499SAlex Lu 	.lmp_subver = (lmps), \
466f9ff246SMax Chou 	.hci_rev = (hcir), \
476f9ff246SMax Chou 	.hci_ver = (hciv), \
486f9ff246SMax Chou 	.hci_bus = (bus)
49907f8499SAlex Lu 
509a24ce5eSMax Chou #define	RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}})
519a24ce5eSMax Chou #define	RTL_CHIP_REV    (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}})
529a24ce5eSMax Chou #define	RTL_SEC_PROJ    (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0x0D, 0x00, 0xb0}})
539a24ce5eSMax Chou 
549a24ce5eSMax Chou #define RTL_PATCH_SNIPPETS		0x01
559a24ce5eSMax Chou #define RTL_PATCH_DUMMY_HEADER		0x02
569a24ce5eSMax Chou #define RTL_PATCH_SECURITY_HEADER	0x03
579a24ce5eSMax Chou 
589ab9235fSMax Chou enum btrtl_chip_id {
599ab9235fSMax Chou 	CHIP_ID_8723A,
609ab9235fSMax Chou 	CHIP_ID_8723B,
619ab9235fSMax Chou 	CHIP_ID_8821A,
629ab9235fSMax Chou 	CHIP_ID_8761A,
639ab9235fSMax Chou 	CHIP_ID_8822B = 8,
649ab9235fSMax Chou 	CHIP_ID_8723D,
659ab9235fSMax Chou 	CHIP_ID_8821C,
669ab9235fSMax Chou 	CHIP_ID_8822C = 13,
679ab9235fSMax Chou 	CHIP_ID_8761B,
689ab9235fSMax Chou 	CHIP_ID_8852A = 18,
6918e8055cSMax Chou 	CHIP_ID_8852B = 20,
708b1d66b5SMax Chou 	CHIP_ID_8852C = 25,
717948fe1cSMax Chou 	CHIP_ID_8851B = 36,
729ab9235fSMax Chou };
739ab9235fSMax Chou 
74907f8499SAlex Lu struct id_table {
75907f8499SAlex Lu 	__u16 match_flags;
76907f8499SAlex Lu 	__u16 lmp_subver;
77907f8499SAlex Lu 	__u16 hci_rev;
78c50903e3SMartin Blumenstingl 	__u8 hci_ver;
79c50903e3SMartin Blumenstingl 	__u8 hci_bus;
80c0123cb6SVasily Khoruzhick 	__u8 chip_type;
81907f8499SAlex Lu 	bool config_needed;
8226503ad2SMartin Blumenstingl 	bool has_rom_version;
83f4bcba0eSMarcel Holtmann 	bool has_msft_ext;
84907f8499SAlex Lu 	char *fw_name;
85907f8499SAlex Lu 	char *cfg_name;
86044014ceSHilda Wu 	char *hw_info;
87907f8499SAlex Lu };
88907f8499SAlex Lu 
8926503ad2SMartin Blumenstingl struct btrtl_device_info {
9026503ad2SMartin Blumenstingl 	const struct id_table *ic_info;
9126503ad2SMartin Blumenstingl 	u8 rom_version;
9226503ad2SMartin Blumenstingl 	u8 *fw_data;
9326503ad2SMartin Blumenstingl 	int fw_len;
9426503ad2SMartin Blumenstingl 	u8 *cfg_data;
9526503ad2SMartin Blumenstingl 	int cfg_len;
961996d9caSKai-Heng Feng 	bool drop_fw;
979ab9235fSMax Chou 	int project_id;
989a24ce5eSMax Chou 	u8 key_id;
999a24ce5eSMax Chou 	struct list_head patch_subsecs;
10026503ad2SMartin Blumenstingl };
10126503ad2SMartin Blumenstingl 
102907f8499SAlex Lu static const struct id_table ic_id_table[] = {
1036f9ff246SMax Chou 	/* 8723A */
1046f9ff246SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8723A, 0xb, 0x6, HCI_USB),
10526503ad2SMartin Blumenstingl 	  .config_needed = false,
10626503ad2SMartin Blumenstingl 	  .has_rom_version = false,
107bd003fb3SMax Chou 	  .fw_name = "rtl_bt/rtl8723a_fw",
108044014ceSHilda Wu 	  .cfg_name = NULL,
109044014ceSHilda Wu 	  .hw_info = "rtl8723au" },
11026503ad2SMartin Blumenstingl 
111c50903e3SMartin Blumenstingl 	/* 8723BS */
1126f9ff246SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_UART),
113c50903e3SMartin Blumenstingl 	  .config_needed = true,
114c50903e3SMartin Blumenstingl 	  .has_rom_version = true,
115bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8723bs_fw",
116044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8723bs_config",
117044014ceSHilda Wu 	  .hw_info  = "rtl8723bs" },
118c50903e3SMartin Blumenstingl 
119907f8499SAlex Lu 	/* 8723B */
1206f9ff246SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_USB),
121907f8499SAlex Lu 	  .config_needed = false,
12226503ad2SMartin Blumenstingl 	  .has_rom_version = true,
123bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8723b_fw",
124044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8723b_config",
125044014ceSHilda Wu 	  .hw_info  = "rtl8723bu" },
126907f8499SAlex Lu 
127c0123cb6SVasily Khoruzhick 	/* 8723CS-CG */
128c0123cb6SVasily Khoruzhick 	{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
129c0123cb6SVasily Khoruzhick 			 IC_MATCH_FL_HCIBUS,
130c0123cb6SVasily Khoruzhick 	  .lmp_subver = RTL_ROM_LMP_8703B,
131c0123cb6SVasily Khoruzhick 	  .chip_type = RTL_CHIP_8723CS_CG,
132c0123cb6SVasily Khoruzhick 	  .hci_bus = HCI_UART,
133c0123cb6SVasily Khoruzhick 	  .config_needed = true,
134c0123cb6SVasily Khoruzhick 	  .has_rom_version = true,
135bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8723cs_cg_fw",
136044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8723cs_cg_config",
137044014ceSHilda Wu 	  .hw_info  = "rtl8723cs-cg" },
138c0123cb6SVasily Khoruzhick 
139c0123cb6SVasily Khoruzhick 	/* 8723CS-VF */
140c0123cb6SVasily Khoruzhick 	{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
141c0123cb6SVasily Khoruzhick 			 IC_MATCH_FL_HCIBUS,
142c0123cb6SVasily Khoruzhick 	  .lmp_subver = RTL_ROM_LMP_8703B,
143c0123cb6SVasily Khoruzhick 	  .chip_type = RTL_CHIP_8723CS_VF,
144c0123cb6SVasily Khoruzhick 	  .hci_bus = HCI_UART,
145c0123cb6SVasily Khoruzhick 	  .config_needed = true,
146c0123cb6SVasily Khoruzhick 	  .has_rom_version = true,
147bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8723cs_vf_fw",
148044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8723cs_vf_config",
149044014ceSHilda Wu 	  .hw_info  = "rtl8723cs-vf" },
150c0123cb6SVasily Khoruzhick 
151c0123cb6SVasily Khoruzhick 	/* 8723CS-XX */
152c0123cb6SVasily Khoruzhick 	{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
153c0123cb6SVasily Khoruzhick 			 IC_MATCH_FL_HCIBUS,
154c0123cb6SVasily Khoruzhick 	  .lmp_subver = RTL_ROM_LMP_8703B,
155c0123cb6SVasily Khoruzhick 	  .chip_type = RTL_CHIP_8723CS_XX,
156c0123cb6SVasily Khoruzhick 	  .hci_bus = HCI_UART,
157c0123cb6SVasily Khoruzhick 	  .config_needed = true,
158c0123cb6SVasily Khoruzhick 	  .has_rom_version = true,
159bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8723cs_xx_fw",
160044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8723cs_xx_config",
161044014ceSHilda Wu 	  .hw_info  = "rtl8723cs" },
162c0123cb6SVasily Khoruzhick 
163907f8499SAlex Lu 	/* 8723D */
1646f9ff246SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB),
165907f8499SAlex Lu 	  .config_needed = true,
16626503ad2SMartin Blumenstingl 	  .has_rom_version = true,
167bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8723d_fw",
168044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8723d_config",
169044014ceSHilda Wu 	  .hw_info  = "rtl8723du" },
170907f8499SAlex Lu 
171c50903e3SMartin Blumenstingl 	/* 8723DS */
1726f9ff246SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_UART),
173c50903e3SMartin Blumenstingl 	  .config_needed = true,
174c50903e3SMartin Blumenstingl 	  .has_rom_version = true,
175bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8723ds_fw",
176044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8723ds_config",
177044014ceSHilda Wu 	  .hw_info  = "rtl8723ds" },
178c50903e3SMartin Blumenstingl 
179907f8499SAlex Lu 	/* 8821A */
1806f9ff246SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8821A, 0xa, 0x6, HCI_USB),
181907f8499SAlex Lu 	  .config_needed = false,
18226503ad2SMartin Blumenstingl 	  .has_rom_version = true,
183bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8821a_fw",
184044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8821a_config",
185044014ceSHilda Wu 	  .hw_info  = "rtl8821au" },
186907f8499SAlex Lu 
187907f8499SAlex Lu 	/* 8821C */
1886f9ff246SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_USB),
189907f8499SAlex Lu 	  .config_needed = false,
19026503ad2SMartin Blumenstingl 	  .has_rom_version = true,
1914d51fb04SMarcel Holtmann 	  .has_msft_ext = true,
192bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8821c_fw",
193044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8821c_config",
194044014ceSHilda Wu 	  .hw_info  = "rtl8821cu" },
195907f8499SAlex Lu 
19610c9e010SChris Morgan 	/* 8821CS */
19710c9e010SChris Morgan 	{ IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_UART),
19810c9e010SChris Morgan 	  .config_needed = true,
19910c9e010SChris Morgan 	  .has_rom_version = true,
20010c9e010SChris Morgan 	  .has_msft_ext = true,
201bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8821cs_fw",
202044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8821cs_config",
203044014ceSHilda Wu 	  .hw_info  = "rtl8821cs" },
20410c9e010SChris Morgan 
205907f8499SAlex Lu 	/* 8761A */
2066f9ff246SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8761A, 0xa, 0x6, HCI_USB),
207907f8499SAlex Lu 	  .config_needed = false,
20826503ad2SMartin Blumenstingl 	  .has_rom_version = true,
209bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8761a_fw",
210044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8761a_config",
211044014ceSHilda Wu 	  .hw_info  = "rtl8761au" },
212907f8499SAlex Lu 
21304896832SZiqian SUN (Zamir) 	/* 8761B */
2149fd2e294SJoakim Tjernlund 	{ IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_UART),
21504896832SZiqian SUN (Zamir) 	  .config_needed = false,
21604896832SZiqian SUN (Zamir) 	  .has_rom_version = true,
217f4bcba0eSMarcel Holtmann 	  .has_msft_ext = true,
218bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8761b_fw",
219044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8761b_config",
220044014ceSHilda Wu 	  .hw_info  = "rtl8761btv" },
22104896832SZiqian SUN (Zamir) 
2229fd2e294SJoakim Tjernlund 	/* 8761BU */
2239fd2e294SJoakim Tjernlund 	{ IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_USB),
2249fd2e294SJoakim Tjernlund 	  .config_needed = false,
2259fd2e294SJoakim Tjernlund 	  .has_rom_version = true,
226bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8761bu_fw",
227044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8761bu_config",
228044014ceSHilda Wu 	  .hw_info  = "rtl8761bu" },
2299fd2e294SJoakim Tjernlund 
230848fc616SMax Chou 	/* 8822C with UART interface */
231b050c5bbSVyacheslav Bocharov 	{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0x8, HCI_UART),
232b050c5bbSVyacheslav Bocharov 	  .config_needed = true,
233b050c5bbSVyacheslav Bocharov 	  .has_rom_version = true,
234b050c5bbSVyacheslav Bocharov 	  .has_msft_ext = true,
235bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8822cs_fw",
236044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8822cs_config",
237044014ceSHilda Wu 	  .hw_info  = "rtl8822cs" },
238b050c5bbSVyacheslav Bocharov 
239b050c5bbSVyacheslav Bocharov 	/* 8822C with UART interface */
2406f9ff246SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_UART),
241848fc616SMax Chou 	  .config_needed = true,
242848fc616SMax Chou 	  .has_rom_version = true,
243f4bcba0eSMarcel Holtmann 	  .has_msft_ext = true,
244bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8822cs_fw",
245044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8822cs_config",
246044014ceSHilda Wu 	  .hw_info  = "rtl8822cs" },
247848fc616SMax Chou 
2488ecfdc95SAlex Lu 	/* 8822C with USB interface */
2496f9ff246SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_USB),
2508ecfdc95SAlex Lu 	  .config_needed = false,
2518ecfdc95SAlex Lu 	  .has_rom_version = true,
252f4bcba0eSMarcel Holtmann 	  .has_msft_ext = true,
253bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8822cu_fw",
254044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8822cu_config",
255044014ceSHilda Wu 	  .hw_info  = "rtl8822cu" },
2568ecfdc95SAlex Lu 
257907f8499SAlex Lu 	/* 8822B */
2586f9ff246SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8822B, 0xb, 0x7, HCI_USB),
259907f8499SAlex Lu 	  .config_needed = true,
26026503ad2SMartin Blumenstingl 	  .has_rom_version = true,
261f4bcba0eSMarcel Holtmann 	  .has_msft_ext = true,
262bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8822b_fw",
263044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8822b_config",
264044014ceSHilda Wu 	  .hw_info  = "rtl8822bu" },
2650d484db6SMax Chou 
2660d484db6SMax Chou 	/* 8852A */
2670d484db6SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8852A, 0xa, 0xb, HCI_USB),
2680d484db6SMax Chou 	  .config_needed = false,
2690d484db6SMax Chou 	  .has_rom_version = true,
270f4bcba0eSMarcel Holtmann 	  .has_msft_ext = true,
271bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8852au_fw",
272044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8852au_config",
273044014ceSHilda Wu 	  .hw_info  = "rtl8852au" },
27418e8055cSMax Chou 
275fe4b71b5SVictor Hassan 	/* 8852B with UART interface */
276fe4b71b5SVictor Hassan 	{ IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_UART),
277fe4b71b5SVictor Hassan 	  .config_needed = true,
278fe4b71b5SVictor Hassan 	  .has_rom_version = true,
279fe4b71b5SVictor Hassan 	  .has_msft_ext = true,
280bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8852bs_fw",
281044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8852bs_config",
282044014ceSHilda Wu 	  .hw_info  = "rtl8852bs" },
283fe4b71b5SVictor Hassan 
28418e8055cSMax Chou 	/* 8852B */
28518e8055cSMax Chou 	{ IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_USB),
28618e8055cSMax Chou 	  .config_needed = false,
28718e8055cSMax Chou 	  .has_rom_version = true,
28818e8055cSMax Chou 	  .has_msft_ext = true,
289bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8852bu_fw",
290044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8852bu_config",
291044014ceSHilda Wu 	  .hw_info  = "rtl8852bu" },
2928b1d66b5SMax Chou 
2938b1d66b5SMax Chou 	/* 8852C */
2948b1d66b5SMax Chou 	{ IC_INFO(RTL_ROM_LMP_8852A, 0xc, 0xc, HCI_USB),
2958b1d66b5SMax Chou 	  .config_needed = false,
2968b1d66b5SMax Chou 	  .has_rom_version = true,
2978b1d66b5SMax Chou 	  .has_msft_ext = true,
298bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8852cu_fw",
299044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8852cu_config",
300044014ceSHilda Wu 	  .hw_info  = "rtl8852cu" },
3017948fe1cSMax Chou 
3027948fe1cSMax Chou 	/* 8851B */
3037948fe1cSMax Chou 	{ IC_INFO(RTL_ROM_LMP_8851B, 0xb, 0xc, HCI_USB),
3047948fe1cSMax Chou 	  .config_needed = false,
3057948fe1cSMax Chou 	  .has_rom_version = true,
3067948fe1cSMax Chou 	  .has_msft_ext = false,
307bd003fb3SMax Chou 	  .fw_name  = "rtl_bt/rtl8851bu_fw",
308044014ceSHilda Wu 	  .cfg_name = "rtl_bt/rtl8851bu_config",
309044014ceSHilda Wu 	  .hw_info  = "rtl8851bu" },
310907f8499SAlex Lu 	};
311907f8499SAlex Lu 
btrtl_match_ic(u16 lmp_subver,u16 hci_rev,u8 hci_ver,u8 hci_bus,u8 chip_type)312c50903e3SMartin Blumenstingl static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
313c0123cb6SVasily Khoruzhick 					     u8 hci_ver, u8 hci_bus,
314c0123cb6SVasily Khoruzhick 					     u8 chip_type)
31526503ad2SMartin Blumenstingl {
31626503ad2SMartin Blumenstingl 	int i;
31726503ad2SMartin Blumenstingl 
31826503ad2SMartin Blumenstingl 	for (i = 0; i < ARRAY_SIZE(ic_id_table); i++) {
31926503ad2SMartin Blumenstingl 		if ((ic_id_table[i].match_flags & IC_MATCH_FL_LMPSUBV) &&
32026503ad2SMartin Blumenstingl 		    (ic_id_table[i].lmp_subver != lmp_subver))
32126503ad2SMartin Blumenstingl 			continue;
32226503ad2SMartin Blumenstingl 		if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIREV) &&
32326503ad2SMartin Blumenstingl 		    (ic_id_table[i].hci_rev != hci_rev))
32426503ad2SMartin Blumenstingl 			continue;
325c50903e3SMartin Blumenstingl 		if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIVER) &&
326c50903e3SMartin Blumenstingl 		    (ic_id_table[i].hci_ver != hci_ver))
327c50903e3SMartin Blumenstingl 			continue;
328c50903e3SMartin Blumenstingl 		if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) &&
329c50903e3SMartin Blumenstingl 		    (ic_id_table[i].hci_bus != hci_bus))
330c50903e3SMartin Blumenstingl 			continue;
331c0123cb6SVasily Khoruzhick 		if ((ic_id_table[i].match_flags & IC_MATCH_FL_CHIP_TYPE) &&
332c0123cb6SVasily Khoruzhick 		    (ic_id_table[i].chip_type != chip_type))
333c0123cb6SVasily Khoruzhick 			continue;
33426503ad2SMartin Blumenstingl 
33526503ad2SMartin Blumenstingl 		break;
33626503ad2SMartin Blumenstingl 	}
33726503ad2SMartin Blumenstingl 	if (i >= ARRAY_SIZE(ic_id_table))
33826503ad2SMartin Blumenstingl 		return NULL;
33926503ad2SMartin Blumenstingl 
34026503ad2SMartin Blumenstingl 	return &ic_id_table[i];
34126503ad2SMartin Blumenstingl }
34226503ad2SMartin Blumenstingl 
btrtl_read_local_version(struct hci_dev * hdev)343240b64a8SAlex Lu static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev)
344240b64a8SAlex Lu {
345240b64a8SAlex Lu 	struct sk_buff *skb;
346240b64a8SAlex Lu 
347240b64a8SAlex Lu 	skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
348240b64a8SAlex Lu 			     HCI_INIT_TIMEOUT);
349240b64a8SAlex Lu 	if (IS_ERR(skb)) {
350240b64a8SAlex Lu 		rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION failed (%ld)",
351240b64a8SAlex Lu 			    PTR_ERR(skb));
352240b64a8SAlex Lu 		return skb;
353240b64a8SAlex Lu 	}
354240b64a8SAlex Lu 
355240b64a8SAlex Lu 	if (skb->len != sizeof(struct hci_rp_read_local_version)) {
356240b64a8SAlex Lu 		rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION event length mismatch");
357240b64a8SAlex Lu 		kfree_skb(skb);
358240b64a8SAlex Lu 		return ERR_PTR(-EIO);
359240b64a8SAlex Lu 	}
360240b64a8SAlex Lu 
361240b64a8SAlex Lu 	return skb;
362240b64a8SAlex Lu }
363240b64a8SAlex Lu 
rtl_read_rom_version(struct hci_dev * hdev,u8 * version)364db33c77dSCarlo Caione static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
365db33c77dSCarlo Caione {
366db33c77dSCarlo Caione 	struct rtl_rom_version_evt *rom_version;
367db33c77dSCarlo Caione 	struct sk_buff *skb;
368db33c77dSCarlo Caione 
369db33c77dSCarlo Caione 	/* Read RTL ROM version command */
370db33c77dSCarlo Caione 	skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT);
371db33c77dSCarlo Caione 	if (IS_ERR(skb)) {
372f1300c03SAlex Lu 		rtl_dev_err(hdev, "Read ROM version failed (%ld)",
373a5c76e67SHans de Goede 			    PTR_ERR(skb));
374db33c77dSCarlo Caione 		return PTR_ERR(skb);
375db33c77dSCarlo Caione 	}
376db33c77dSCarlo Caione 
377db33c77dSCarlo Caione 	if (skb->len != sizeof(*rom_version)) {
378f1300c03SAlex Lu 		rtl_dev_err(hdev, "version event length mismatch");
379db33c77dSCarlo Caione 		kfree_skb(skb);
380db33c77dSCarlo Caione 		return -EIO;
381db33c77dSCarlo Caione 	}
382db33c77dSCarlo Caione 
383db33c77dSCarlo Caione 	rom_version = (struct rtl_rom_version_evt *)skb->data;
384f1300c03SAlex Lu 	rtl_dev_info(hdev, "rom_version status=%x version=%x",
3852064ee33SMarcel Holtmann 		     rom_version->status, rom_version->version);
386db33c77dSCarlo Caione 
387db33c77dSCarlo Caione 	*version = rom_version->version;
388db33c77dSCarlo Caione 
389db33c77dSCarlo Caione 	kfree_skb(skb);
390db33c77dSCarlo Caione 	return 0;
391db33c77dSCarlo Caione }
392db33c77dSCarlo Caione 
btrtl_vendor_read_reg16(struct hci_dev * hdev,struct rtl_vendor_cmd * cmd,u8 * rp)3939a24ce5eSMax Chou static int btrtl_vendor_read_reg16(struct hci_dev *hdev,
3949a24ce5eSMax Chou 				   struct rtl_vendor_cmd *cmd, u8 *rp)
3959a24ce5eSMax Chou {
3969a24ce5eSMax Chou 	struct sk_buff *skb;
3979a24ce5eSMax Chou 	int err = 0;
3989a24ce5eSMax Chou 
3999a24ce5eSMax Chou 	skb = __hci_cmd_sync(hdev, 0xfc61, sizeof(*cmd), cmd,
4009a24ce5eSMax Chou 			     HCI_INIT_TIMEOUT);
4019a24ce5eSMax Chou 	if (IS_ERR(skb)) {
4029a24ce5eSMax Chou 		err = PTR_ERR(skb);
4039a24ce5eSMax Chou 		rtl_dev_err(hdev, "RTL: Read reg16 failed (%d)", err);
4049a24ce5eSMax Chou 		return err;
4059a24ce5eSMax Chou 	}
4069a24ce5eSMax Chou 
4079a24ce5eSMax Chou 	if (skb->len != 3 || skb->data[0]) {
4089a24ce5eSMax Chou 		bt_dev_err(hdev, "RTL: Read reg16 length mismatch");
4099a24ce5eSMax Chou 		kfree_skb(skb);
4109a24ce5eSMax Chou 		return -EIO;
4119a24ce5eSMax Chou 	}
4129a24ce5eSMax Chou 
4139a24ce5eSMax Chou 	if (rp)
4149a24ce5eSMax Chou 		memcpy(rp, skb->data + 1, 2);
4159a24ce5eSMax Chou 
4169a24ce5eSMax Chou 	kfree_skb(skb);
4179a24ce5eSMax Chou 
4189a24ce5eSMax Chou 	return 0;
4199a24ce5eSMax Chou }
4209a24ce5eSMax Chou 
rtl_iov_pull_data(struct rtl_iovec * iov,u32 len)4219a24ce5eSMax Chou static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len)
4229a24ce5eSMax Chou {
4239a24ce5eSMax Chou 	void *data = iov->data;
4249a24ce5eSMax Chou 
4259a24ce5eSMax Chou 	if (iov->len < len)
4269a24ce5eSMax Chou 		return NULL;
4279a24ce5eSMax Chou 
4289a24ce5eSMax Chou 	iov->data += len;
4299a24ce5eSMax Chou 	iov->len  -= len;
4309a24ce5eSMax Chou 
4319a24ce5eSMax Chou 	return data;
4329a24ce5eSMax Chou }
4339a24ce5eSMax Chou 
btrtl_insert_ordered_subsec(struct rtl_subsection * node,struct btrtl_device_info * btrtl_dev)4349a24ce5eSMax Chou static void btrtl_insert_ordered_subsec(struct rtl_subsection *node,
4359a24ce5eSMax Chou 					struct btrtl_device_info *btrtl_dev)
4369a24ce5eSMax Chou {
4379a24ce5eSMax Chou 	struct list_head *pos;
4389a24ce5eSMax Chou 	struct list_head *next;
4399a24ce5eSMax Chou 	struct rtl_subsection *subsec;
4409a24ce5eSMax Chou 
4419a24ce5eSMax Chou 	list_for_each_safe(pos, next, &btrtl_dev->patch_subsecs) {
4429a24ce5eSMax Chou 		subsec = list_entry(pos, struct rtl_subsection, list);
4439a24ce5eSMax Chou 		if (subsec->prio >= node->prio)
4449a24ce5eSMax Chou 			break;
4459a24ce5eSMax Chou 	}
4469a24ce5eSMax Chou 	__list_add(&node->list, pos->prev, pos);
4479a24ce5eSMax Chou }
4489a24ce5eSMax Chou 
btrtl_parse_section(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev,u32 opcode,u8 * data,u32 len)4499a24ce5eSMax Chou static int btrtl_parse_section(struct hci_dev *hdev,
4509a24ce5eSMax Chou 			       struct btrtl_device_info *btrtl_dev, u32 opcode,
4519a24ce5eSMax Chou 			       u8 *data, u32 len)
4529a24ce5eSMax Chou {
4539a24ce5eSMax Chou 	struct rtl_section_hdr *hdr;
4549a24ce5eSMax Chou 	struct rtl_subsection *subsec;
4559a24ce5eSMax Chou 	struct rtl_common_subsec *common_subsec;
4569a24ce5eSMax Chou 	struct rtl_sec_hdr *sec_hdr;
4579a24ce5eSMax Chou 	int i;
4589a24ce5eSMax Chou 	u8 *ptr;
4599a24ce5eSMax Chou 	u16 num_subsecs;
4609a24ce5eSMax Chou 	u32 subsec_len;
4619a24ce5eSMax Chou 	int rc = 0;
4629a24ce5eSMax Chou 	struct rtl_iovec iov = {
4639a24ce5eSMax Chou 		.data = data,
4649a24ce5eSMax Chou 		.len  = len,
4659a24ce5eSMax Chou 	};
4669a24ce5eSMax Chou 
4679a24ce5eSMax Chou 	hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
4689a24ce5eSMax Chou 	if (!hdr)
4699a24ce5eSMax Chou 		return -EINVAL;
4709a24ce5eSMax Chou 	num_subsecs = le16_to_cpu(hdr->num);
4719a24ce5eSMax Chou 
4729a24ce5eSMax Chou 	for (i = 0; i < num_subsecs; i++) {
4739a24ce5eSMax Chou 		common_subsec = rtl_iov_pull_data(&iov, sizeof(*common_subsec));
4749a24ce5eSMax Chou 		if (!common_subsec)
4759a24ce5eSMax Chou 			break;
4769a24ce5eSMax Chou 		subsec_len = le32_to_cpu(common_subsec->len);
4779a24ce5eSMax Chou 
4789a24ce5eSMax Chou 		rtl_dev_dbg(hdev, "subsec, eco 0x%02x, len %08x",
4799a24ce5eSMax Chou 			    common_subsec->eco, subsec_len);
4809a24ce5eSMax Chou 
4819a24ce5eSMax Chou 		ptr = rtl_iov_pull_data(&iov, subsec_len);
4829a24ce5eSMax Chou 		if (!ptr)
4839a24ce5eSMax Chou 			break;
4849a24ce5eSMax Chou 
4859a24ce5eSMax Chou 		if (common_subsec->eco != btrtl_dev->rom_version + 1)
4869a24ce5eSMax Chou 			continue;
4879a24ce5eSMax Chou 
4889a24ce5eSMax Chou 		switch (opcode) {
4899a24ce5eSMax Chou 		case RTL_PATCH_SECURITY_HEADER:
4909a24ce5eSMax Chou 			sec_hdr = (void *)common_subsec;
4919a24ce5eSMax Chou 			if (sec_hdr->key_id != btrtl_dev->key_id)
4929a24ce5eSMax Chou 				continue;
4939a24ce5eSMax Chou 			break;
4949a24ce5eSMax Chou 		}
4959a24ce5eSMax Chou 
4969a24ce5eSMax Chou 		subsec = kzalloc(sizeof(*subsec), GFP_KERNEL);
4979a24ce5eSMax Chou 		if (!subsec)
4989a24ce5eSMax Chou 			return -ENOMEM;
4999a24ce5eSMax Chou 		subsec->opcode = opcode;
5009a24ce5eSMax Chou 		subsec->prio = common_subsec->prio;
5019a24ce5eSMax Chou 		subsec->len  = subsec_len;
5029a24ce5eSMax Chou 		subsec->data = ptr;
5039a24ce5eSMax Chou 		btrtl_insert_ordered_subsec(subsec, btrtl_dev);
5049a24ce5eSMax Chou 		rc  += subsec_len;
5059a24ce5eSMax Chou 	}
5069a24ce5eSMax Chou 
5079a24ce5eSMax Chou 	return rc;
5089a24ce5eSMax Chou }
5099a24ce5eSMax Chou 
rtlbt_parse_firmware_v2(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev,unsigned char ** _buf)5109a24ce5eSMax Chou static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
5119a24ce5eSMax Chou 				   struct btrtl_device_info *btrtl_dev,
5129a24ce5eSMax Chou 				   unsigned char **_buf)
5139a24ce5eSMax Chou {
5149a24ce5eSMax Chou 	struct rtl_epatch_header_v2 *hdr;
5159a24ce5eSMax Chou 	int rc;
5169a24ce5eSMax Chou 	u8 reg_val[2];
5179a24ce5eSMax Chou 	u8 key_id;
5189a24ce5eSMax Chou 	u32 num_sections;
5199a24ce5eSMax Chou 	struct rtl_section *section;
5209a24ce5eSMax Chou 	struct rtl_subsection *entry, *tmp;
5219a24ce5eSMax Chou 	u32 section_len;
5229a24ce5eSMax Chou 	u32 opcode;
5239a24ce5eSMax Chou 	int len = 0;
5249a24ce5eSMax Chou 	int i;
5259a24ce5eSMax Chou 	u8 *ptr;
5269a24ce5eSMax Chou 	struct rtl_iovec iov = {
5279a24ce5eSMax Chou 		.data = btrtl_dev->fw_data,
5289a24ce5eSMax Chou 		.len  = btrtl_dev->fw_len - 7, /* Cut the tail */
5299a24ce5eSMax Chou 	};
5309a24ce5eSMax Chou 
5319a24ce5eSMax Chou 	rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
5329a24ce5eSMax Chou 	if (rc < 0)
5339a24ce5eSMax Chou 		return -EIO;
5349a24ce5eSMax Chou 	key_id = reg_val[0];
5359a24ce5eSMax Chou 
5369a24ce5eSMax Chou 	rtl_dev_dbg(hdev, "%s: key id %u", __func__, key_id);
5379a24ce5eSMax Chou 
5389a24ce5eSMax Chou 	btrtl_dev->key_id = key_id;
5399a24ce5eSMax Chou 
5409a24ce5eSMax Chou 	hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
5419a24ce5eSMax Chou 	if (!hdr)
5429a24ce5eSMax Chou 		return -EINVAL;
5439a24ce5eSMax Chou 	num_sections = le32_to_cpu(hdr->num_sections);
5449a24ce5eSMax Chou 
5459a24ce5eSMax Chou 	rtl_dev_dbg(hdev, "FW version %08x-%08x", *((u32 *)hdr->fw_version),
5469a24ce5eSMax Chou 		    *((u32 *)(hdr->fw_version + 4)));
5479a24ce5eSMax Chou 
5489a24ce5eSMax Chou 	for (i = 0; i < num_sections; i++) {
5499a24ce5eSMax Chou 		section = rtl_iov_pull_data(&iov, sizeof(*section));
5509a24ce5eSMax Chou 		if (!section)
5519a24ce5eSMax Chou 			break;
5529a24ce5eSMax Chou 		section_len = le32_to_cpu(section->len);
5539a24ce5eSMax Chou 		opcode      = le32_to_cpu(section->opcode);
5549a24ce5eSMax Chou 
5559a24ce5eSMax Chou 		rtl_dev_dbg(hdev, "opcode 0x%04x", section->opcode);
5569a24ce5eSMax Chou 
5579a24ce5eSMax Chou 		ptr = rtl_iov_pull_data(&iov, section_len);
5589a24ce5eSMax Chou 		if (!ptr)
5599a24ce5eSMax Chou 			break;
5609a24ce5eSMax Chou 
5619a24ce5eSMax Chou 		switch (opcode) {
5629a24ce5eSMax Chou 		case RTL_PATCH_SNIPPETS:
5639a24ce5eSMax Chou 			rc = btrtl_parse_section(hdev, btrtl_dev, opcode,
5649a24ce5eSMax Chou 						 ptr, section_len);
5659a24ce5eSMax Chou 			break;
5669a24ce5eSMax Chou 		case RTL_PATCH_SECURITY_HEADER:
5679a24ce5eSMax Chou 			/* If key_id from chip is zero, ignore all security
5689a24ce5eSMax Chou 			 * headers.
5699a24ce5eSMax Chou 			 */
5709a24ce5eSMax Chou 			if (!key_id)
5719a24ce5eSMax Chou 				break;
5729a24ce5eSMax Chou 			rc = btrtl_parse_section(hdev, btrtl_dev, opcode,
5739a24ce5eSMax Chou 						 ptr, section_len);
5749a24ce5eSMax Chou 			break;
5759a24ce5eSMax Chou 		case RTL_PATCH_DUMMY_HEADER:
5769a24ce5eSMax Chou 			rc = btrtl_parse_section(hdev, btrtl_dev, opcode,
5779a24ce5eSMax Chou 						 ptr, section_len);
5789a24ce5eSMax Chou 			break;
5799a24ce5eSMax Chou 		default:
5809a24ce5eSMax Chou 			rc = 0;
5819a24ce5eSMax Chou 			break;
5829a24ce5eSMax Chou 		}
5839a24ce5eSMax Chou 		if (rc < 0) {
5849a24ce5eSMax Chou 			rtl_dev_err(hdev, "RTL: Parse section (%u) err %d",
5859a24ce5eSMax Chou 				    opcode, rc);
5869a24ce5eSMax Chou 			return rc;
5879a24ce5eSMax Chou 		}
5889a24ce5eSMax Chou 		len += rc;
5899a24ce5eSMax Chou 	}
5909a24ce5eSMax Chou 
5919a24ce5eSMax Chou 	if (!len)
5929a24ce5eSMax Chou 		return -ENODATA;
5939a24ce5eSMax Chou 
5949a24ce5eSMax Chou 	/* Allocate mem and copy all found subsecs. */
5959a24ce5eSMax Chou 	ptr = kvmalloc(len, GFP_KERNEL);
5969a24ce5eSMax Chou 	if (!ptr)
5979a24ce5eSMax Chou 		return -ENOMEM;
5989a24ce5eSMax Chou 
5999a24ce5eSMax Chou 	len = 0;
6009a24ce5eSMax Chou 	list_for_each_entry_safe(entry, tmp, &btrtl_dev->patch_subsecs, list) {
6019a24ce5eSMax Chou 		rtl_dev_dbg(hdev, "RTL: opcode %08x, addr %p, len 0x%x",
6029a24ce5eSMax Chou 			    entry->opcode, entry->data, entry->len);
6039a24ce5eSMax Chou 		memcpy(ptr + len, entry->data, entry->len);
6049a24ce5eSMax Chou 		len += entry->len;
6059a24ce5eSMax Chou 	}
6069a24ce5eSMax Chou 
6079a24ce5eSMax Chou 	if (!len)
6089a24ce5eSMax Chou 		return -EPERM;
6099a24ce5eSMax Chou 
6109a24ce5eSMax Chou 	*_buf = ptr;
6119a24ce5eSMax Chou 	return len;
6129a24ce5eSMax Chou }
6139a24ce5eSMax Chou 
rtlbt_parse_firmware(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev,unsigned char ** _buf)61426503ad2SMartin Blumenstingl static int rtlbt_parse_firmware(struct hci_dev *hdev,
61526503ad2SMartin Blumenstingl 				struct btrtl_device_info *btrtl_dev,
616db33c77dSCarlo Caione 				unsigned char **_buf)
617db33c77dSCarlo Caione {
618e5070e07SColin Ian King 	static const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
619044014ceSHilda Wu 	struct btrealtek_data *coredump_info = hci_get_priv(hdev);
620db33c77dSCarlo Caione 	struct rtl_epatch_header *epatch_info;
621db33c77dSCarlo Caione 	unsigned char *buf;
62226503ad2SMartin Blumenstingl 	int i, len;
623db33c77dSCarlo Caione 	size_t min_size;
62426503ad2SMartin Blumenstingl 	u8 opcode, length, data;
625db33c77dSCarlo Caione 	int project_id = -1;
626db33c77dSCarlo Caione 	const unsigned char *fwptr, *chip_id_base;
627db33c77dSCarlo Caione 	const unsigned char *patch_length_base, *patch_offset_base;
628db33c77dSCarlo Caione 	u32 patch_offset = 0;
629db33c77dSCarlo Caione 	u16 patch_length, num_patches;
6301110a2dbSLarry Finger 	static const struct {
6311110a2dbSLarry Finger 		__u16 lmp_subver;
6321110a2dbSLarry Finger 		__u8 id;
6331110a2dbSLarry Finger 	} project_id_to_lmp_subver[] = {
6341110a2dbSLarry Finger 		{ RTL_ROM_LMP_8723A, 0 },
6351110a2dbSLarry Finger 		{ RTL_ROM_LMP_8723B, 1 },
6361110a2dbSLarry Finger 		{ RTL_ROM_LMP_8821A, 2 },
6371110a2dbSLarry Finger 		{ RTL_ROM_LMP_8761A, 3 },
638c0123cb6SVasily Khoruzhick 		{ RTL_ROM_LMP_8703B, 7 },
6391110a2dbSLarry Finger 		{ RTL_ROM_LMP_8822B, 8 },
640907f8499SAlex Lu 		{ RTL_ROM_LMP_8723B, 9 },	/* 8723D */
641907f8499SAlex Lu 		{ RTL_ROM_LMP_8821A, 10 },	/* 8821C */
6428ecfdc95SAlex Lu 		{ RTL_ROM_LMP_8822B, 13 },	/* 8822C */
64304896832SZiqian SUN (Zamir) 		{ RTL_ROM_LMP_8761A, 14 },	/* 8761B */
6440d484db6SMax Chou 		{ RTL_ROM_LMP_8852A, 18 },	/* 8852A */
64518e8055cSMax Chou 		{ RTL_ROM_LMP_8852A, 20 },	/* 8852B */
6468b1d66b5SMax Chou 		{ RTL_ROM_LMP_8852A, 25 },	/* 8852C */
6477948fe1cSMax Chou 		{ RTL_ROM_LMP_8851B, 36 },	/* 8851B */
648db33c77dSCarlo Caione 	};
649db33c77dSCarlo Caione 
6509a24ce5eSMax Chou 	if (btrtl_dev->fw_len <= 8)
6519a24ce5eSMax Chou 		return -EINVAL;
6529a24ce5eSMax Chou 
6539a24ce5eSMax Chou 	if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8))
6549a24ce5eSMax Chou 		min_size = sizeof(struct rtl_epatch_header) +
6559a24ce5eSMax Chou 				sizeof(extension_sig) + 3;
6569a24ce5eSMax Chou 	else if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V2, 8))
6579a24ce5eSMax Chou 		min_size = sizeof(struct rtl_epatch_header_v2) +
6589a24ce5eSMax Chou 				sizeof(extension_sig) + 3;
6599a24ce5eSMax Chou 	else
6609a24ce5eSMax Chou 		return -EINVAL;
6619a24ce5eSMax Chou 
66226503ad2SMartin Blumenstingl 	if (btrtl_dev->fw_len < min_size)
663db33c77dSCarlo Caione 		return -EINVAL;
664db33c77dSCarlo Caione 
66526503ad2SMartin Blumenstingl 	fwptr = btrtl_dev->fw_data + btrtl_dev->fw_len - sizeof(extension_sig);
666db33c77dSCarlo Caione 	if (memcmp(fwptr, extension_sig, sizeof(extension_sig)) != 0) {
667f1300c03SAlex Lu 		rtl_dev_err(hdev, "extension section signature mismatch");
668db33c77dSCarlo Caione 		return -EINVAL;
669db33c77dSCarlo Caione 	}
670db33c77dSCarlo Caione 
671db33c77dSCarlo Caione 	/* Loop from the end of the firmware parsing instructions, until
672db33c77dSCarlo Caione 	 * we find an instruction that identifies the "project ID" for the
673db33c77dSCarlo Caione 	 * hardware supported by this firwmare file.
67438f230f1Sshaomin Deng 	 * Once we have that, we double-check that project_id is suitable
675db33c77dSCarlo Caione 	 * for the hardware we are working with.
676db33c77dSCarlo Caione 	 */
67726503ad2SMartin Blumenstingl 	while (fwptr >= btrtl_dev->fw_data + (sizeof(*epatch_info) + 3)) {
678db33c77dSCarlo Caione 		opcode = *--fwptr;
679db33c77dSCarlo Caione 		length = *--fwptr;
680db33c77dSCarlo Caione 		data = *--fwptr;
681db33c77dSCarlo Caione 
682db33c77dSCarlo Caione 		BT_DBG("check op=%x len=%x data=%x", opcode, length, data);
683db33c77dSCarlo Caione 
684db33c77dSCarlo Caione 		if (opcode == 0xff) /* EOF */
685db33c77dSCarlo Caione 			break;
686db33c77dSCarlo Caione 
687db33c77dSCarlo Caione 		if (length == 0) {
688f1300c03SAlex Lu 			rtl_dev_err(hdev, "found instruction with length 0");
689db33c77dSCarlo Caione 			return -EINVAL;
690db33c77dSCarlo Caione 		}
691db33c77dSCarlo Caione 
692db33c77dSCarlo Caione 		if (opcode == 0 && length == 1) {
693db33c77dSCarlo Caione 			project_id = data;
694db33c77dSCarlo Caione 			break;
695db33c77dSCarlo Caione 		}
696db33c77dSCarlo Caione 
697db33c77dSCarlo Caione 		fwptr -= length;
698db33c77dSCarlo Caione 	}
699db33c77dSCarlo Caione 
700db33c77dSCarlo Caione 	if (project_id < 0) {
701f1300c03SAlex Lu 		rtl_dev_err(hdev, "failed to find version instruction");
702db33c77dSCarlo Caione 		return -EINVAL;
703db33c77dSCarlo Caione 	}
704db33c77dSCarlo Caione 
7051110a2dbSLarry Finger 	/* Find project_id in table */
7061110a2dbSLarry Finger 	for (i = 0; i < ARRAY_SIZE(project_id_to_lmp_subver); i++) {
7079ab9235fSMax Chou 		if (project_id == project_id_to_lmp_subver[i].id) {
7089ab9235fSMax Chou 			btrtl_dev->project_id = project_id;
7091110a2dbSLarry Finger 			break;
7101110a2dbSLarry Finger 		}
7119ab9235fSMax Chou 	}
7121110a2dbSLarry Finger 
7131110a2dbSLarry Finger 	if (i >= ARRAY_SIZE(project_id_to_lmp_subver)) {
714f1300c03SAlex Lu 		rtl_dev_err(hdev, "unknown project id %d", project_id);
715db33c77dSCarlo Caione 		return -EINVAL;
716db33c77dSCarlo Caione 	}
717db33c77dSCarlo Caione 
71826503ad2SMartin Blumenstingl 	if (btrtl_dev->ic_info->lmp_subver !=
71926503ad2SMartin Blumenstingl 				project_id_to_lmp_subver[i].lmp_subver) {
720f1300c03SAlex Lu 		rtl_dev_err(hdev, "firmware is for %x but this is a %x",
72126503ad2SMartin Blumenstingl 			    project_id_to_lmp_subver[i].lmp_subver,
72226503ad2SMartin Blumenstingl 			    btrtl_dev->ic_info->lmp_subver);
723db33c77dSCarlo Caione 		return -EINVAL;
724db33c77dSCarlo Caione 	}
725db33c77dSCarlo Caione 
7269a24ce5eSMax Chou 	if (memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8) != 0) {
7279a24ce5eSMax Chou 		if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V2, 8))
7289a24ce5eSMax Chou 			return rtlbt_parse_firmware_v2(hdev, btrtl_dev, _buf);
729f1300c03SAlex Lu 		rtl_dev_err(hdev, "bad EPATCH signature");
730db33c77dSCarlo Caione 		return -EINVAL;
731db33c77dSCarlo Caione 	}
732db33c77dSCarlo Caione 
7339a24ce5eSMax Chou 	epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data;
734db33c77dSCarlo Caione 	num_patches = le16_to_cpu(epatch_info->num_patches);
735044014ceSHilda Wu 
736db33c77dSCarlo Caione 	BT_DBG("fw_version=%x, num_patches=%d",
737db33c77dSCarlo Caione 	       le32_to_cpu(epatch_info->fw_version), num_patches);
738044014ceSHilda Wu 	coredump_info->rtl_dump.fw_version = le32_to_cpu(epatch_info->fw_version);
739db33c77dSCarlo Caione 
740db33c77dSCarlo Caione 	/* After the rtl_epatch_header there is a funky patch metadata section.
741db33c77dSCarlo Caione 	 * Assuming 2 patches, the layout is:
742db33c77dSCarlo Caione 	 * ChipID1 ChipID2 PatchLength1 PatchLength2 PatchOffset1 PatchOffset2
743db33c77dSCarlo Caione 	 *
744db33c77dSCarlo Caione 	 * Find the right patch for this chip.
745db33c77dSCarlo Caione 	 */
746db33c77dSCarlo Caione 	min_size += 8 * num_patches;
74726503ad2SMartin Blumenstingl 	if (btrtl_dev->fw_len < min_size)
748db33c77dSCarlo Caione 		return -EINVAL;
749db33c77dSCarlo Caione 
75026503ad2SMartin Blumenstingl 	chip_id_base = btrtl_dev->fw_data + sizeof(struct rtl_epatch_header);
751db33c77dSCarlo Caione 	patch_length_base = chip_id_base + (sizeof(u16) * num_patches);
752db33c77dSCarlo Caione 	patch_offset_base = patch_length_base + (sizeof(u16) * num_patches);
753db33c77dSCarlo Caione 	for (i = 0; i < num_patches; i++) {
754db33c77dSCarlo Caione 		u16 chip_id = get_unaligned_le16(chip_id_base +
755db33c77dSCarlo Caione 						 (i * sizeof(u16)));
75626503ad2SMartin Blumenstingl 		if (chip_id == btrtl_dev->rom_version + 1) {
757db33c77dSCarlo Caione 			patch_length = get_unaligned_le16(patch_length_base +
758db33c77dSCarlo Caione 							  (i * sizeof(u16)));
759db33c77dSCarlo Caione 			patch_offset = get_unaligned_le32(patch_offset_base +
760db33c77dSCarlo Caione 							  (i * sizeof(u32)));
761db33c77dSCarlo Caione 			break;
762db33c77dSCarlo Caione 		}
763db33c77dSCarlo Caione 	}
764db33c77dSCarlo Caione 
765db33c77dSCarlo Caione 	if (!patch_offset) {
766a5c76e67SHans de Goede 		rtl_dev_err(hdev, "didn't find patch for chip id %d",
767a5c76e67SHans de Goede 			    btrtl_dev->rom_version);
768db33c77dSCarlo Caione 		return -EINVAL;
769db33c77dSCarlo Caione 	}
770db33c77dSCarlo Caione 
771db33c77dSCarlo Caione 	BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
772db33c77dSCarlo Caione 	min_size = patch_offset + patch_length;
77326503ad2SMartin Blumenstingl 	if (btrtl_dev->fw_len < min_size)
774db33c77dSCarlo Caione 		return -EINVAL;
775db33c77dSCarlo Caione 
776db33c77dSCarlo Caione 	/* Copy the firmware into a new buffer and write the version at
777db33c77dSCarlo Caione 	 * the end.
778db33c77dSCarlo Caione 	 */
779db33c77dSCarlo Caione 	len = patch_length;
780268d3636SMaxim Mikityanskiy 	buf = kvmalloc(patch_length, GFP_KERNEL);
781db33c77dSCarlo Caione 	if (!buf)
782db33c77dSCarlo Caione 		return -ENOMEM;
783db33c77dSCarlo Caione 
784268d3636SMaxim Mikityanskiy 	memcpy(buf, btrtl_dev->fw_data + patch_offset, patch_length - 4);
785db33c77dSCarlo Caione 	memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4);
786db33c77dSCarlo Caione 
787db33c77dSCarlo Caione 	*_buf = buf;
788db33c77dSCarlo Caione 	return len;
789db33c77dSCarlo Caione }
790db33c77dSCarlo Caione 
rtl_download_firmware(struct hci_dev * hdev,const unsigned char * data,int fw_len)791db33c77dSCarlo Caione static int rtl_download_firmware(struct hci_dev *hdev,
792db33c77dSCarlo Caione 				 const unsigned char *data, int fw_len)
793db33c77dSCarlo Caione {
794db33c77dSCarlo Caione 	struct rtl_download_cmd *dl_cmd;
795db33c77dSCarlo Caione 	int frag_num = fw_len / RTL_FRAG_LEN + 1;
796db33c77dSCarlo Caione 	int frag_len = RTL_FRAG_LEN;
797db33c77dSCarlo Caione 	int ret = 0;
798db33c77dSCarlo Caione 	int i;
7999a24ce5eSMax Chou 	int j = 0;
800240b64a8SAlex Lu 	struct sk_buff *skb;
801240b64a8SAlex Lu 	struct hci_rp_read_local_version *rp;
802db33c77dSCarlo Caione 
803db33c77dSCarlo Caione 	dl_cmd = kmalloc(sizeof(struct rtl_download_cmd), GFP_KERNEL);
804db33c77dSCarlo Caione 	if (!dl_cmd)
805db33c77dSCarlo Caione 		return -ENOMEM;
806db33c77dSCarlo Caione 
807db33c77dSCarlo Caione 	for (i = 0; i < frag_num; i++) {
808db33c77dSCarlo Caione 		struct sk_buff *skb;
809db33c77dSCarlo Caione 
8109a24ce5eSMax Chou 		dl_cmd->index = j++;
8119a24ce5eSMax Chou 		if (dl_cmd->index == 0x7f)
8129a24ce5eSMax Chou 			j = 1;
813cf0d9a70SMax Chou 
814db33c77dSCarlo Caione 		if (i == (frag_num - 1)) {
815db33c77dSCarlo Caione 			dl_cmd->index |= 0x80; /* data end */
816db33c77dSCarlo Caione 			frag_len = fw_len % RTL_FRAG_LEN;
817db33c77dSCarlo Caione 		}
8189a24ce5eSMax Chou 		rtl_dev_dbg(hdev, "download fw (%d/%d). index = %d", i,
8199a24ce5eSMax Chou 				frag_num, dl_cmd->index);
820db33c77dSCarlo Caione 		memcpy(dl_cmd->data, data, frag_len);
821db33c77dSCarlo Caione 
822db33c77dSCarlo Caione 		/* Send download command */
823db33c77dSCarlo Caione 		skb = __hci_cmd_sync(hdev, 0xfc20, frag_len + 1, dl_cmd,
824db33c77dSCarlo Caione 				     HCI_INIT_TIMEOUT);
825db33c77dSCarlo Caione 		if (IS_ERR(skb)) {
826f1300c03SAlex Lu 			rtl_dev_err(hdev, "download fw command failed (%ld)",
827a5c76e67SHans de Goede 				    PTR_ERR(skb));
828d171dfb6SMax Chou 			ret = PTR_ERR(skb);
829db33c77dSCarlo Caione 			goto out;
830db33c77dSCarlo Caione 		}
831db33c77dSCarlo Caione 
832db33c77dSCarlo Caione 		if (skb->len != sizeof(struct rtl_download_response)) {
833f1300c03SAlex Lu 			rtl_dev_err(hdev, "download fw event length mismatch");
834db33c77dSCarlo Caione 			kfree_skb(skb);
835db33c77dSCarlo Caione 			ret = -EIO;
836db33c77dSCarlo Caione 			goto out;
837db33c77dSCarlo Caione 		}
838db33c77dSCarlo Caione 
839db33c77dSCarlo Caione 		kfree_skb(skb);
840db33c77dSCarlo Caione 		data += RTL_FRAG_LEN;
841db33c77dSCarlo Caione 	}
842db33c77dSCarlo Caione 
843240b64a8SAlex Lu 	skb = btrtl_read_local_version(hdev);
844240b64a8SAlex Lu 	if (IS_ERR(skb)) {
845240b64a8SAlex Lu 		ret = PTR_ERR(skb);
846240b64a8SAlex Lu 		rtl_dev_err(hdev, "read local version failed");
847240b64a8SAlex Lu 		goto out;
848240b64a8SAlex Lu 	}
849240b64a8SAlex Lu 
850240b64a8SAlex Lu 	rp = (struct hci_rp_read_local_version *)skb->data;
851240b64a8SAlex Lu 	rtl_dev_info(hdev, "fw version 0x%04x%04x",
852240b64a8SAlex Lu 		     __le16_to_cpu(rp->hci_rev), __le16_to_cpu(rp->lmp_subver));
853240b64a8SAlex Lu 	kfree_skb(skb);
854240b64a8SAlex Lu 
855db33c77dSCarlo Caione out:
856db33c77dSCarlo Caione 	kfree(dl_cmd);
857db33c77dSCarlo Caione 	return ret;
858db33c77dSCarlo Caione }
859db33c77dSCarlo Caione 
rtl_load_file(struct hci_dev * hdev,const char * name,u8 ** buff)86026503ad2SMartin Blumenstingl static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff)
8611110a2dbSLarry Finger {
8621110a2dbSLarry Finger 	const struct firmware *fw;
8631110a2dbSLarry Finger 	int ret;
8641110a2dbSLarry Finger 
865f1300c03SAlex Lu 	rtl_dev_info(hdev, "loading %s", name);
8661110a2dbSLarry Finger 	ret = request_firmware(&fw, name, &hdev->dev);
867abed84a0SLarry Finger 	if (ret < 0)
8681110a2dbSLarry Finger 		return ret;
8691110a2dbSLarry Finger 	ret = fw->size;
870268d3636SMaxim Mikityanskiy 	*buff = kvmalloc(fw->size, GFP_KERNEL);
871268d3636SMaxim Mikityanskiy 	if (*buff)
872268d3636SMaxim Mikityanskiy 		memcpy(*buff, fw->data, ret);
873268d3636SMaxim Mikityanskiy 	else
874c3327bdeSDan Carpenter 		ret = -ENOMEM;
8751110a2dbSLarry Finger 
8761110a2dbSLarry Finger 	release_firmware(fw);
8771110a2dbSLarry Finger 
8781110a2dbSLarry Finger 	return ret;
8791110a2dbSLarry Finger }
8801110a2dbSLarry Finger 
btrtl_setup_rtl8723a(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev)88126503ad2SMartin Blumenstingl static int btrtl_setup_rtl8723a(struct hci_dev *hdev,
88226503ad2SMartin Blumenstingl 				struct btrtl_device_info *btrtl_dev)
883db33c77dSCarlo Caione {
88426503ad2SMartin Blumenstingl 	if (btrtl_dev->fw_len < 8)
88526503ad2SMartin Blumenstingl 		return -EINVAL;
886db33c77dSCarlo Caione 
887db33c77dSCarlo Caione 	/* Check that the firmware doesn't have the epatch signature
888db33c77dSCarlo Caione 	 * (which is only for RTL8723B and newer).
889db33c77dSCarlo Caione 	 */
89026503ad2SMartin Blumenstingl 	if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8)) {
891f1300c03SAlex Lu 		rtl_dev_err(hdev, "unexpected EPATCH signature!");
892907f8499SAlex Lu 		return -EINVAL;
893907f8499SAlex Lu 	}
894907f8499SAlex Lu 
89526503ad2SMartin Blumenstingl 	return rtl_download_firmware(hdev, btrtl_dev->fw_data,
89626503ad2SMartin Blumenstingl 				     btrtl_dev->fw_len);
897db33c77dSCarlo Caione }
898db33c77dSCarlo Caione 
btrtl_setup_rtl8723b(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev)89926503ad2SMartin Blumenstingl static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
90026503ad2SMartin Blumenstingl 				struct btrtl_device_info *btrtl_dev)
90126503ad2SMartin Blumenstingl {
90226503ad2SMartin Blumenstingl 	unsigned char *fw_data = NULL;
90326503ad2SMartin Blumenstingl 	int ret;
90426503ad2SMartin Blumenstingl 	u8 *tbuff;
90526503ad2SMartin Blumenstingl 
90626503ad2SMartin Blumenstingl 	ret = rtlbt_parse_firmware(hdev, btrtl_dev, &fw_data);
907db33c77dSCarlo Caione 	if (ret < 0)
908db33c77dSCarlo Caione 		goto out;
909db33c77dSCarlo Caione 
91026503ad2SMartin Blumenstingl 	if (btrtl_dev->cfg_len > 0) {
911268d3636SMaxim Mikityanskiy 		tbuff = kvzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL);
9121110a2dbSLarry Finger 		if (!tbuff) {
9131110a2dbSLarry Finger 			ret = -ENOMEM;
914db33c77dSCarlo Caione 			goto out;
9151110a2dbSLarry Finger 		}
9161110a2dbSLarry Finger 
9171110a2dbSLarry Finger 		memcpy(tbuff, fw_data, ret);
918268d3636SMaxim Mikityanskiy 		kvfree(fw_data);
9191110a2dbSLarry Finger 
92026503ad2SMartin Blumenstingl 		memcpy(tbuff + ret, btrtl_dev->cfg_data, btrtl_dev->cfg_len);
92126503ad2SMartin Blumenstingl 		ret += btrtl_dev->cfg_len;
9221110a2dbSLarry Finger 
9231110a2dbSLarry Finger 		fw_data = tbuff;
9241110a2dbSLarry Finger 	}
9251110a2dbSLarry Finger 
926f1300c03SAlex Lu 	rtl_dev_info(hdev, "cfg_sz %d, total sz %d", btrtl_dev->cfg_len, ret);
9271110a2dbSLarry Finger 
9281110a2dbSLarry Finger 	ret = rtl_download_firmware(hdev, fw_data, ret);
929db33c77dSCarlo Caione 
930db33c77dSCarlo Caione out:
931268d3636SMaxim Mikityanskiy 	kvfree(fw_data);
932db33c77dSCarlo Caione 	return ret;
933db33c77dSCarlo Caione }
934db33c77dSCarlo Caione 
btrtl_coredump(struct hci_dev * hdev)935044014ceSHilda Wu static void btrtl_coredump(struct hci_dev *hdev)
936044014ceSHilda Wu {
937044014ceSHilda Wu 	static const u8 param[] = { 0x00, 0x00 };
938044014ceSHilda Wu 
939044014ceSHilda Wu 	__hci_cmd_send(hdev, RTL_VSC_OP_COREDUMP, sizeof(param), param);
940044014ceSHilda Wu }
941044014ceSHilda Wu 
btrtl_dmp_hdr(struct hci_dev * hdev,struct sk_buff * skb)942044014ceSHilda Wu static void btrtl_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
943044014ceSHilda Wu {
944044014ceSHilda Wu 	struct btrealtek_data *coredump_info = hci_get_priv(hdev);
945044014ceSHilda Wu 	char buf[80];
946044014ceSHilda Wu 
947044014ceSHilda Wu 	if (coredump_info->rtl_dump.controller)
948044014ceSHilda Wu 		snprintf(buf, sizeof(buf), "Controller Name: %s\n",
949044014ceSHilda Wu 			 coredump_info->rtl_dump.controller);
950044014ceSHilda Wu 	else
951044014ceSHilda Wu 		snprintf(buf, sizeof(buf), "Controller Name: Unknown\n");
952044014ceSHilda Wu 	skb_put_data(skb, buf, strlen(buf));
953044014ceSHilda Wu 
954044014ceSHilda Wu 	snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n",
955044014ceSHilda Wu 		 coredump_info->rtl_dump.fw_version);
956044014ceSHilda Wu 	skb_put_data(skb, buf, strlen(buf));
957044014ceSHilda Wu 
958044014ceSHilda Wu 	snprintf(buf, sizeof(buf), "Driver: %s\n", coredump_info->rtl_dump.driver_name);
959044014ceSHilda Wu 	skb_put_data(skb, buf, strlen(buf));
960044014ceSHilda Wu 
961044014ceSHilda Wu 	snprintf(buf, sizeof(buf), "Vendor: Realtek\n");
962044014ceSHilda Wu 	skb_put_data(skb, buf, strlen(buf));
963044014ceSHilda Wu }
964044014ceSHilda Wu 
btrtl_register_devcoredump_support(struct hci_dev * hdev)9659ee25286SMax Chou static void btrtl_register_devcoredump_support(struct hci_dev *hdev)
966044014ceSHilda Wu {
9679ee25286SMax Chou 	hci_devcd_register(hdev, btrtl_coredump, btrtl_dmp_hdr, NULL);
968044014ceSHilda Wu 
969044014ceSHilda Wu }
970044014ceSHilda Wu 
btrtl_set_driver_name(struct hci_dev * hdev,const char * driver_name)971044014ceSHilda Wu void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name)
972044014ceSHilda Wu {
973044014ceSHilda Wu 	struct btrealtek_data *coredump_info = hci_get_priv(hdev);
974044014ceSHilda Wu 
975044014ceSHilda Wu 	coredump_info->rtl_dump.driver_name = driver_name;
976044014ceSHilda Wu }
977044014ceSHilda Wu EXPORT_SYMBOL_GPL(btrtl_set_driver_name);
978044014ceSHilda Wu 
rtl_has_chip_type(u16 lmp_subver)979c0123cb6SVasily Khoruzhick static bool rtl_has_chip_type(u16 lmp_subver)
980c0123cb6SVasily Khoruzhick {
981c0123cb6SVasily Khoruzhick 	switch (lmp_subver) {
982c0123cb6SVasily Khoruzhick 	case RTL_ROM_LMP_8703B:
983c0123cb6SVasily Khoruzhick 		return true;
984c0123cb6SVasily Khoruzhick 	default:
985c0123cb6SVasily Khoruzhick 		break;
986c0123cb6SVasily Khoruzhick 	}
987c0123cb6SVasily Khoruzhick 
988c0123cb6SVasily Khoruzhick 	return  false;
989c0123cb6SVasily Khoruzhick }
990c0123cb6SVasily Khoruzhick 
rtl_read_chip_type(struct hci_dev * hdev,u8 * type)991c0123cb6SVasily Khoruzhick static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type)
992c0123cb6SVasily Khoruzhick {
993c0123cb6SVasily Khoruzhick 	struct rtl_chip_type_evt *chip_type;
994c0123cb6SVasily Khoruzhick 	struct sk_buff *skb;
995c0123cb6SVasily Khoruzhick 	const unsigned char cmd_buf[] = {0x00, 0x94, 0xa0, 0x00, 0xb0};
996c0123cb6SVasily Khoruzhick 
997c0123cb6SVasily Khoruzhick 	/* Read RTL chip type command */
998c0123cb6SVasily Khoruzhick 	skb = __hci_cmd_sync(hdev, 0xfc61, 5, cmd_buf, HCI_INIT_TIMEOUT);
999c0123cb6SVasily Khoruzhick 	if (IS_ERR(skb)) {
1000c0123cb6SVasily Khoruzhick 		rtl_dev_err(hdev, "Read chip type failed (%ld)",
1001c0123cb6SVasily Khoruzhick 			    PTR_ERR(skb));
1002c0123cb6SVasily Khoruzhick 		return PTR_ERR(skb);
1003c0123cb6SVasily Khoruzhick 	}
1004c0123cb6SVasily Khoruzhick 
1005c0123cb6SVasily Khoruzhick 	chip_type = skb_pull_data(skb, sizeof(*chip_type));
1006c0123cb6SVasily Khoruzhick 	if (!chip_type) {
1007c0123cb6SVasily Khoruzhick 		rtl_dev_err(hdev, "RTL chip type event length mismatch");
1008c0123cb6SVasily Khoruzhick 		kfree_skb(skb);
1009c0123cb6SVasily Khoruzhick 		return -EIO;
1010c0123cb6SVasily Khoruzhick 	}
1011c0123cb6SVasily Khoruzhick 
1012c0123cb6SVasily Khoruzhick 	rtl_dev_info(hdev, "chip_type status=%x type=%x",
1013c0123cb6SVasily Khoruzhick 		     chip_type->status, chip_type->type);
1014c0123cb6SVasily Khoruzhick 
1015c0123cb6SVasily Khoruzhick 	*type = chip_type->type & 0x0f;
1016c0123cb6SVasily Khoruzhick 
1017c0123cb6SVasily Khoruzhick 	kfree_skb(skb);
1018c0123cb6SVasily Khoruzhick 	return 0;
1019c0123cb6SVasily Khoruzhick }
1020c0123cb6SVasily Khoruzhick 
btrtl_free(struct btrtl_device_info * btrtl_dev)102126503ad2SMartin Blumenstingl void btrtl_free(struct btrtl_device_info *btrtl_dev)
1022db33c77dSCarlo Caione {
10239a24ce5eSMax Chou 	struct rtl_subsection *entry, *tmp;
10249a24ce5eSMax Chou 
1025268d3636SMaxim Mikityanskiy 	kvfree(btrtl_dev->fw_data);
1026268d3636SMaxim Mikityanskiy 	kvfree(btrtl_dev->cfg_data);
10279a24ce5eSMax Chou 
10289a24ce5eSMax Chou 	list_for_each_entry_safe(entry, tmp, &btrtl_dev->patch_subsecs, list) {
10299a24ce5eSMax Chou 		list_del(&entry->list);
10309a24ce5eSMax Chou 		kfree(entry);
10319a24ce5eSMax Chou 	}
10329a24ce5eSMax Chou 
103326503ad2SMartin Blumenstingl 	kfree(btrtl_dev);
103426503ad2SMartin Blumenstingl }
103526503ad2SMartin Blumenstingl EXPORT_SYMBOL_GPL(btrtl_free);
103626503ad2SMartin Blumenstingl 
btrtl_initialize(struct hci_dev * hdev,const char * postfix)10371cc194caSHans de Goede struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
10381cc194caSHans de Goede 					   const char *postfix)
103926503ad2SMartin Blumenstingl {
1040044014ceSHilda Wu 	struct btrealtek_data *coredump_info = hci_get_priv(hdev);
104126503ad2SMartin Blumenstingl 	struct btrtl_device_info *btrtl_dev;
1042db33c77dSCarlo Caione 	struct sk_buff *skb;
1043db33c77dSCarlo Caione 	struct hci_rp_read_local_version *resp;
10446b42f04eSMax Chou 	struct hci_command_hdr *cmd;
1045bd003fb3SMax Chou 	char fw_name[40];
10461cc194caSHans de Goede 	char cfg_name[40];
1047907f8499SAlex Lu 	u16 hci_rev, lmp_subver;
10489a24ce5eSMax Chou 	u8 hci_ver, lmp_ver, chip_type = 0;
104926503ad2SMartin Blumenstingl 	int ret;
10509a24ce5eSMax Chou 	u8 reg_val[2];
105126503ad2SMartin Blumenstingl 
105226503ad2SMartin Blumenstingl 	btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
105326503ad2SMartin Blumenstingl 	if (!btrtl_dev) {
105426503ad2SMartin Blumenstingl 		ret = -ENOMEM;
105526503ad2SMartin Blumenstingl 		goto err_alloc;
105626503ad2SMartin Blumenstingl 	}
1057db33c77dSCarlo Caione 
10589a24ce5eSMax Chou 	INIT_LIST_HEAD(&btrtl_dev->patch_subsecs);
10599a24ce5eSMax Chou 
10609a24ce5eSMax Chou check_version:
10619a24ce5eSMax Chou 	ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_SUBVER, reg_val);
10629a24ce5eSMax Chou 	if (ret < 0)
10639a24ce5eSMax Chou 		goto err_free;
10649a24ce5eSMax Chou 	lmp_subver = get_unaligned_le16(reg_val);
10659a24ce5eSMax Chou 
10669a24ce5eSMax Chou 	if (lmp_subver == RTL_ROM_LMP_8822B) {
10679a24ce5eSMax Chou 		ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_REV, reg_val);
10689a24ce5eSMax Chou 		if (ret < 0)
10699a24ce5eSMax Chou 			goto err_free;
10709a24ce5eSMax Chou 		hci_rev = get_unaligned_le16(reg_val);
10719a24ce5eSMax Chou 
10729a24ce5eSMax Chou 		/* 8822E */
10739a24ce5eSMax Chou 		if (hci_rev == 0x000e) {
10749a24ce5eSMax Chou 			hci_ver = 0x0c;
10759a24ce5eSMax Chou 			lmp_ver = 0x0c;
10769a24ce5eSMax Chou 			btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev,
10779a24ce5eSMax Chou 							    hci_ver, hdev->bus,
10789a24ce5eSMax Chou 							    chip_type);
10799a24ce5eSMax Chou 			goto next;
10809a24ce5eSMax Chou 		}
10819a24ce5eSMax Chou 	}
10829a24ce5eSMax Chou 
1083db33c77dSCarlo Caione 	skb = btrtl_read_local_version(hdev);
108426503ad2SMartin Blumenstingl 	if (IS_ERR(skb)) {
108526503ad2SMartin Blumenstingl 		ret = PTR_ERR(skb);
108626503ad2SMartin Blumenstingl 		goto err_free;
108726503ad2SMartin Blumenstingl 	}
1088db33c77dSCarlo Caione 
1089db33c77dSCarlo Caione 	resp = (struct hci_rp_read_local_version *)skb->data;
1090db33c77dSCarlo Caione 
1091c50903e3SMartin Blumenstingl 	hci_ver    = resp->hci_ver;
1092907f8499SAlex Lu 	hci_rev    = le16_to_cpu(resp->hci_rev);
10939a24ce5eSMax Chou 	lmp_ver    = resp->lmp_ver;
1094db33c77dSCarlo Caione 	lmp_subver = le16_to_cpu(resp->lmp_subver);
10951996d9caSKai-Heng Feng 
10969a24ce5eSMax Chou 	kfree_skb(skb);
10979a24ce5eSMax Chou 
1098c0123cb6SVasily Khoruzhick 	if (rtl_has_chip_type(lmp_subver)) {
1099c0123cb6SVasily Khoruzhick 		ret = rtl_read_chip_type(hdev, &chip_type);
1100c0123cb6SVasily Khoruzhick 		if (ret)
1101c0123cb6SVasily Khoruzhick 			goto err_free;
1102c0123cb6SVasily Khoruzhick 	}
1103c0123cb6SVasily Khoruzhick 
1104cd36742aSHilda Wu 	btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,
1105c0123cb6SVasily Khoruzhick 					    hdev->bus, chip_type);
1106cd36742aSHilda Wu 
11079a24ce5eSMax Chou next:
11089a24ce5eSMax Chou 	rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x",
11099a24ce5eSMax Chou 		     hci_ver, hci_rev,
11109a24ce5eSMax Chou 		     lmp_ver, lmp_subver);
11119a24ce5eSMax Chou 
11129a24ce5eSMax Chou 	if (!btrtl_dev->ic_info && !btrtl_dev->drop_fw)
11131996d9caSKai-Heng Feng 		btrtl_dev->drop_fw = true;
11149a24ce5eSMax Chou 	else
11159a24ce5eSMax Chou 		btrtl_dev->drop_fw = false;
11161996d9caSKai-Heng Feng 
11171996d9caSKai-Heng Feng 	if (btrtl_dev->drop_fw) {
11186b42f04eSMax Chou 		skb = bt_skb_alloc(sizeof(*cmd), GFP_KERNEL);
1119f5e8e215SColin Ian King 		if (!skb)
11209a24ce5eSMax Chou 			goto err_free;
11211996d9caSKai-Heng Feng 
11226b42f04eSMax Chou 		cmd = skb_put(skb, HCI_COMMAND_HDR_SIZE);
11236b42f04eSMax Chou 		cmd->opcode = cpu_to_le16(0xfc66);
11246b42f04eSMax Chou 		cmd->plen = 0;
11256b42f04eSMax Chou 
11261996d9caSKai-Heng Feng 		hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
11271996d9caSKai-Heng Feng 
11289a24ce5eSMax Chou 		ret = hdev->send(hdev, skb);
11299a24ce5eSMax Chou 		if (ret < 0) {
11309a24ce5eSMax Chou 			bt_dev_err(hdev, "sending frame failed (%d)", ret);
11319a24ce5eSMax Chou 			kfree_skb(skb);
11329a24ce5eSMax Chou 			goto err_free;
11339a24ce5eSMax Chou 		}
11341996d9caSKai-Heng Feng 
11351996d9caSKai-Heng Feng 		/* Ensure the above vendor command is sent to controller and
11361996d9caSKai-Heng Feng 		 * process has done.
11371996d9caSKai-Heng Feng 		 */
11381996d9caSKai-Heng Feng 		msleep(200);
11391996d9caSKai-Heng Feng 
11409a24ce5eSMax Chou 		goto check_version;
11411996d9caSKai-Heng Feng 	}
11421996d9caSKai-Heng Feng 
114326503ad2SMartin Blumenstingl 	if (!btrtl_dev->ic_info) {
1144d182215dSAlex Lu 		rtl_dev_info(hdev, "unknown IC info, lmp subver %04x, hci rev %04x, hci ver %04x",
1145c50903e3SMartin Blumenstingl 			    lmp_subver, hci_rev, hci_ver);
114600df214bSKai-Heng Feng 		return btrtl_dev;
114726503ad2SMartin Blumenstingl 	}
114826503ad2SMartin Blumenstingl 
114926503ad2SMartin Blumenstingl 	if (btrtl_dev->ic_info->has_rom_version) {
115026503ad2SMartin Blumenstingl 		ret = rtl_read_rom_version(hdev, &btrtl_dev->rom_version);
115126503ad2SMartin Blumenstingl 		if (ret)
115226503ad2SMartin Blumenstingl 			goto err_free;
115326503ad2SMartin Blumenstingl 	}
115426503ad2SMartin Blumenstingl 
1155bd003fb3SMax Chou 	if (!btrtl_dev->ic_info->fw_name) {
1156bd003fb3SMax Chou 		ret = -ENOMEM;
1157bd003fb3SMax Chou 		goto err_free;
1158bd003fb3SMax Chou 	}
1159bd003fb3SMax Chou 
1160bd003fb3SMax Chou 	btrtl_dev->fw_len = -EIO;
1161bd003fb3SMax Chou 	if (lmp_subver == RTL_ROM_LMP_8852A && hci_rev == 0x000c) {
1162bd003fb3SMax Chou 		snprintf(fw_name, sizeof(fw_name), "%s_v2.bin",
1163bd003fb3SMax Chou 				btrtl_dev->ic_info->fw_name);
1164bd003fb3SMax Chou 		btrtl_dev->fw_len = rtl_load_file(hdev, fw_name,
116526503ad2SMartin Blumenstingl 				&btrtl_dev->fw_data);
1166bd003fb3SMax Chou 	}
1167bd003fb3SMax Chou 
1168bd003fb3SMax Chou 	if (btrtl_dev->fw_len < 0) {
1169bd003fb3SMax Chou 		snprintf(fw_name, sizeof(fw_name), "%s.bin",
1170bd003fb3SMax Chou 				btrtl_dev->ic_info->fw_name);
1171bd003fb3SMax Chou 		btrtl_dev->fw_len = rtl_load_file(hdev, fw_name,
1172bd003fb3SMax Chou 				&btrtl_dev->fw_data);
1173bd003fb3SMax Chou 	}
1174bd003fb3SMax Chou 
117526503ad2SMartin Blumenstingl 	if (btrtl_dev->fw_len < 0) {
1176f1300c03SAlex Lu 		rtl_dev_err(hdev, "firmware file %s not found",
117726503ad2SMartin Blumenstingl 			    btrtl_dev->ic_info->fw_name);
117826503ad2SMartin Blumenstingl 		ret = btrtl_dev->fw_len;
117926503ad2SMartin Blumenstingl 		goto err_free;
118026503ad2SMartin Blumenstingl 	}
118126503ad2SMartin Blumenstingl 
118226503ad2SMartin Blumenstingl 	if (btrtl_dev->ic_info->cfg_name) {
11831cc194caSHans de Goede 		if (postfix) {
11841cc194caSHans de Goede 			snprintf(cfg_name, sizeof(cfg_name), "%s-%s.bin",
11851cc194caSHans de Goede 				 btrtl_dev->ic_info->cfg_name, postfix);
11861cc194caSHans de Goede 		} else {
11871cc194caSHans de Goede 			snprintf(cfg_name, sizeof(cfg_name), "%s.bin",
11881cc194caSHans de Goede 				 btrtl_dev->ic_info->cfg_name);
11891cc194caSHans de Goede 		}
11901cc194caSHans de Goede 		btrtl_dev->cfg_len = rtl_load_file(hdev, cfg_name,
119126503ad2SMartin Blumenstingl 						   &btrtl_dev->cfg_data);
119226503ad2SMartin Blumenstingl 		if (btrtl_dev->ic_info->config_needed &&
119326503ad2SMartin Blumenstingl 		    btrtl_dev->cfg_len <= 0) {
1194f1300c03SAlex Lu 			rtl_dev_err(hdev, "mandatory config file %s not found",
119526503ad2SMartin Blumenstingl 				    btrtl_dev->ic_info->cfg_name);
119626503ad2SMartin Blumenstingl 			ret = btrtl_dev->cfg_len;
119726503ad2SMartin Blumenstingl 			goto err_free;
119826503ad2SMartin Blumenstingl 		}
119926503ad2SMartin Blumenstingl 	}
120026503ad2SMartin Blumenstingl 
12017f6a750aSArchie Pusaka 	/* The following chips supports the Microsoft vendor extension,
12027f6a750aSArchie Pusaka 	 * therefore set the corresponding VsMsftOpCode.
1203673fae14SMiao-chen Chou 	 */
1204f4bcba0eSMarcel Holtmann 	if (btrtl_dev->ic_info->has_msft_ext)
1205673fae14SMiao-chen Chou 		hci_set_msft_opcode(hdev, 0xFCF0);
1206673fae14SMiao-chen Chou 
1207044014ceSHilda Wu 	if (btrtl_dev->ic_info)
1208044014ceSHilda Wu 		coredump_info->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
1209044014ceSHilda Wu 
121026503ad2SMartin Blumenstingl 	return btrtl_dev;
121126503ad2SMartin Blumenstingl 
121226503ad2SMartin Blumenstingl err_free:
121326503ad2SMartin Blumenstingl 	btrtl_free(btrtl_dev);
121426503ad2SMartin Blumenstingl err_alloc:
121526503ad2SMartin Blumenstingl 	return ERR_PTR(ret);
121626503ad2SMartin Blumenstingl }
121726503ad2SMartin Blumenstingl EXPORT_SYMBOL_GPL(btrtl_initialize);
121826503ad2SMartin Blumenstingl 
btrtl_download_firmware(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev)121926503ad2SMartin Blumenstingl int btrtl_download_firmware(struct hci_dev *hdev,
122026503ad2SMartin Blumenstingl 			    struct btrtl_device_info *btrtl_dev)
122126503ad2SMartin Blumenstingl {
1222044014ceSHilda Wu 	int err = 0;
1223044014ceSHilda Wu 
1224db33c77dSCarlo Caione 	/* Match a set of subver values that correspond to stock firmware,
1225db33c77dSCarlo Caione 	 * which is not compatible with standard btusb.
1226db33c77dSCarlo Caione 	 * If matched, upload an alternative firmware that does conform to
1227db33c77dSCarlo Caione 	 * standard btusb. Once that firmware is uploaded, the subver changes
1228db33c77dSCarlo Caione 	 * to a different value.
1229db33c77dSCarlo Caione 	 */
123000df214bSKai-Heng Feng 	if (!btrtl_dev->ic_info) {
1231f1300c03SAlex Lu 		rtl_dev_info(hdev, "assuming no firmware upload needed");
1232044014ceSHilda Wu 		err = 0;
1233044014ceSHilda Wu 		goto done;
123400df214bSKai-Heng Feng 	}
123500df214bSKai-Heng Feng 
123626503ad2SMartin Blumenstingl 	switch (btrtl_dev->ic_info->lmp_subver) {
1237db33c77dSCarlo Caione 	case RTL_ROM_LMP_8723A:
1238044014ceSHilda Wu 		err = btrtl_setup_rtl8723a(hdev, btrtl_dev);
1239044014ceSHilda Wu 		break;
1240db33c77dSCarlo Caione 	case RTL_ROM_LMP_8723B:
1241db33c77dSCarlo Caione 	case RTL_ROM_LMP_8821A:
1242db33c77dSCarlo Caione 	case RTL_ROM_LMP_8761A:
12431110a2dbSLarry Finger 	case RTL_ROM_LMP_8822B:
12440d484db6SMax Chou 	case RTL_ROM_LMP_8852A:
1245c0123cb6SVasily Khoruzhick 	case RTL_ROM_LMP_8703B:
12467948fe1cSMax Chou 	case RTL_ROM_LMP_8851B:
1247044014ceSHilda Wu 		err = btrtl_setup_rtl8723b(hdev, btrtl_dev);
1248044014ceSHilda Wu 		break;
1249db33c77dSCarlo Caione 	default:
1250f1300c03SAlex Lu 		rtl_dev_info(hdev, "assuming no firmware upload needed");
1251044014ceSHilda Wu 		break;
1252db33c77dSCarlo Caione 	}
1253044014ceSHilda Wu 
1254044014ceSHilda Wu done:
12559ee25286SMax Chou 	btrtl_register_devcoredump_support(hdev);
1256044014ceSHilda Wu 
1257044014ceSHilda Wu 	return err;
1258db33c77dSCarlo Caione }
125926503ad2SMartin Blumenstingl EXPORT_SYMBOL_GPL(btrtl_download_firmware);
126026503ad2SMartin Blumenstingl 
btrtl_set_quirks(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev)12613011faa2SArchie Pusaka void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
126226503ad2SMartin Blumenstingl {
126365251e2eSAlex Lu 	/* Enable controller to do both LE scan and BR/EDR inquiry
126465251e2eSAlex Lu 	 * simultaneously.
126565251e2eSAlex Lu 	 */
126665251e2eSAlex Lu 	set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
126765251e2eSAlex Lu 
126805672a2cSAbhishek Pandit-Subedi 	/* Enable central-peripheral role (able to create new connections with
126905672a2cSAbhishek Pandit-Subedi 	 * an existing connection in slave role).
127005672a2cSAbhishek Pandit-Subedi 	 */
12719ab9235fSMax Chou 	/* Enable WBS supported for the specific Realtek devices. */
12729ab9235fSMax Chou 	switch (btrtl_dev->project_id) {
12739ab9235fSMax Chou 	case CHIP_ID_8822C:
12749ab9235fSMax Chou 	case CHIP_ID_8852A:
127518e8055cSMax Chou 	case CHIP_ID_8852B:
12768b1d66b5SMax Chou 	case CHIP_ID_8852C:
12777948fe1cSMax Chou 	case CHIP_ID_8851B:
127805672a2cSAbhishek Pandit-Subedi 		set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
12799ab9235fSMax Chou 		set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
1280a479e713SHilda Wu 
1281a479e713SHilda Wu 		/* RTL8852C needs to transmit mSBC data continuously without
1282a479e713SHilda Wu 		 * the zero length of USB packets for the ALT 6 supported chips
1283a479e713SHilda Wu 		 */
1284a479e713SHilda Wu 		if (btrtl_dev->project_id == CHIP_ID_8852C)
1285a479e713SHilda Wu 			btrealtek_set_flag(hdev, REALTEK_ALT6_CONTINUOUS_TX_CHIP);
1286a479e713SHilda Wu 
12879e14606dSHilda Wu 		if (btrtl_dev->project_id == CHIP_ID_8852A ||
1288*cc026a7fSHilda Wu 		    btrtl_dev->project_id == CHIP_ID_8852B ||
12899e14606dSHilda Wu 		    btrtl_dev->project_id == CHIP_ID_8852C)
12909e14606dSHilda Wu 			set_bit(HCI_QUIRK_USE_MSFT_EXT_ADDRESS_FILTER, &hdev->quirks);
12919e14606dSHilda Wu 
1292099c6d31SJoseph Hwang 		hci_set_aosp_capable(hdev);
129305672a2cSAbhishek Pandit-Subedi 		break;
129405672a2cSAbhishek Pandit-Subedi 	default:
129505672a2cSAbhishek Pandit-Subedi 		rtl_dev_dbg(hdev, "Central-peripheral role not enabled.");
12969ab9235fSMax Chou 		rtl_dev_dbg(hdev, "WBS supported not enabled.");
129705672a2cSAbhishek Pandit-Subedi 		break;
129805672a2cSAbhishek Pandit-Subedi 	}
1299c0123cb6SVasily Khoruzhick 
1300253cf30eSMax Chou 	if (!btrtl_dev->ic_info)
1301253cf30eSMax Chou 		return;
1302253cf30eSMax Chou 
1303c0123cb6SVasily Khoruzhick 	switch (btrtl_dev->ic_info->lmp_subver) {
1304c0123cb6SVasily Khoruzhick 	case RTL_ROM_LMP_8703B:
1305c0123cb6SVasily Khoruzhick 		/* 8723CS reports two pages for local ext features,
1306c0123cb6SVasily Khoruzhick 		 * but it doesn't support any features from page 2 -
1307c0123cb6SVasily Khoruzhick 		 * it either responds with garbage or with error status
1308c0123cb6SVasily Khoruzhick 		 */
1309c0123cb6SVasily Khoruzhick 		set_bit(HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2,
1310c0123cb6SVasily Khoruzhick 			&hdev->quirks);
1311c0123cb6SVasily Khoruzhick 		break;
1312c0123cb6SVasily Khoruzhick 	default:
1313c0123cb6SVasily Khoruzhick 		break;
1314c0123cb6SVasily Khoruzhick 	}
13153011faa2SArchie Pusaka }
13163011faa2SArchie Pusaka EXPORT_SYMBOL_GPL(btrtl_set_quirks);
13173011faa2SArchie Pusaka 
btrtl_setup_realtek(struct hci_dev * hdev)13183011faa2SArchie Pusaka int btrtl_setup_realtek(struct hci_dev *hdev)
13193011faa2SArchie Pusaka {
13203011faa2SArchie Pusaka 	struct btrtl_device_info *btrtl_dev;
13213011faa2SArchie Pusaka 	int ret;
13223011faa2SArchie Pusaka 
13233011faa2SArchie Pusaka 	btrtl_dev = btrtl_initialize(hdev, NULL);
13243011faa2SArchie Pusaka 	if (IS_ERR(btrtl_dev))
13253011faa2SArchie Pusaka 		return PTR_ERR(btrtl_dev);
13263011faa2SArchie Pusaka 
13273011faa2SArchie Pusaka 	ret = btrtl_download_firmware(hdev, btrtl_dev);
13283011faa2SArchie Pusaka 
13293011faa2SArchie Pusaka 	btrtl_set_quirks(hdev, btrtl_dev);
133005672a2cSAbhishek Pandit-Subedi 
133105672a2cSAbhishek Pandit-Subedi 	btrtl_free(btrtl_dev);
133226503ad2SMartin Blumenstingl 	return ret;
133326503ad2SMartin Blumenstingl }
1334db33c77dSCarlo Caione EXPORT_SYMBOL_GPL(btrtl_setup_realtek);
1335db33c77dSCarlo Caione 
btrtl_shutdown_realtek(struct hci_dev * hdev)13367af3f558SJian-Hong Pan int btrtl_shutdown_realtek(struct hci_dev *hdev)
13377af3f558SJian-Hong Pan {
13387af3f558SJian-Hong Pan 	struct sk_buff *skb;
13397af3f558SJian-Hong Pan 	int ret;
13407af3f558SJian-Hong Pan 
13417af3f558SJian-Hong Pan 	/* According to the vendor driver, BT must be reset on close to avoid
13427af3f558SJian-Hong Pan 	 * firmware crash.
13437af3f558SJian-Hong Pan 	 */
13447af3f558SJian-Hong Pan 	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
13457af3f558SJian-Hong Pan 	if (IS_ERR(skb)) {
13467af3f558SJian-Hong Pan 		ret = PTR_ERR(skb);
13477af3f558SJian-Hong Pan 		bt_dev_err(hdev, "HCI reset during shutdown failed");
13487af3f558SJian-Hong Pan 		return ret;
13497af3f558SJian-Hong Pan 	}
13507af3f558SJian-Hong Pan 	kfree_skb(skb);
13517af3f558SJian-Hong Pan 
13527af3f558SJian-Hong Pan 	return 0;
13537af3f558SJian-Hong Pan }
13547af3f558SJian-Hong Pan EXPORT_SYMBOL_GPL(btrtl_shutdown_realtek);
13557af3f558SJian-Hong Pan 
btrtl_convert_baudrate(u32 device_baudrate)1356b85b0ee1SMartin Blumenstingl static unsigned int btrtl_convert_baudrate(u32 device_baudrate)
1357b85b0ee1SMartin Blumenstingl {
1358b85b0ee1SMartin Blumenstingl 	switch (device_baudrate) {
1359b85b0ee1SMartin Blumenstingl 	case 0x0252a00a:
1360b85b0ee1SMartin Blumenstingl 		return 230400;
1361b85b0ee1SMartin Blumenstingl 
1362b85b0ee1SMartin Blumenstingl 	case 0x05f75004:
1363b85b0ee1SMartin Blumenstingl 		return 921600;
1364b85b0ee1SMartin Blumenstingl 
1365b85b0ee1SMartin Blumenstingl 	case 0x00005004:
1366b85b0ee1SMartin Blumenstingl 		return 1000000;
1367b85b0ee1SMartin Blumenstingl 
1368b85b0ee1SMartin Blumenstingl 	case 0x04928002:
1369b85b0ee1SMartin Blumenstingl 	case 0x01128002:
1370b85b0ee1SMartin Blumenstingl 		return 1500000;
1371b85b0ee1SMartin Blumenstingl 
1372b85b0ee1SMartin Blumenstingl 	case 0x00005002:
1373b85b0ee1SMartin Blumenstingl 		return 2000000;
1374b85b0ee1SMartin Blumenstingl 
1375b85b0ee1SMartin Blumenstingl 	case 0x0000b001:
1376b85b0ee1SMartin Blumenstingl 		return 2500000;
1377b85b0ee1SMartin Blumenstingl 
1378b85b0ee1SMartin Blumenstingl 	case 0x04928001:
1379b85b0ee1SMartin Blumenstingl 		return 3000000;
1380b85b0ee1SMartin Blumenstingl 
1381b85b0ee1SMartin Blumenstingl 	case 0x052a6001:
1382b85b0ee1SMartin Blumenstingl 		return 3500000;
1383b85b0ee1SMartin Blumenstingl 
1384b85b0ee1SMartin Blumenstingl 	case 0x00005001:
1385b85b0ee1SMartin Blumenstingl 		return 4000000;
1386b85b0ee1SMartin Blumenstingl 
1387b85b0ee1SMartin Blumenstingl 	case 0x0252c014:
1388b85b0ee1SMartin Blumenstingl 	default:
1389b85b0ee1SMartin Blumenstingl 		return 115200;
1390b85b0ee1SMartin Blumenstingl 	}
1391b85b0ee1SMartin Blumenstingl }
1392b85b0ee1SMartin Blumenstingl 
btrtl_get_uart_settings(struct hci_dev * hdev,struct btrtl_device_info * btrtl_dev,unsigned int * controller_baudrate,u32 * device_baudrate,bool * flow_control)1393b85b0ee1SMartin Blumenstingl int btrtl_get_uart_settings(struct hci_dev *hdev,
1394b85b0ee1SMartin Blumenstingl 			    struct btrtl_device_info *btrtl_dev,
1395b85b0ee1SMartin Blumenstingl 			    unsigned int *controller_baudrate,
1396b85b0ee1SMartin Blumenstingl 			    u32 *device_baudrate, bool *flow_control)
1397b85b0ee1SMartin Blumenstingl {
1398b85b0ee1SMartin Blumenstingl 	struct rtl_vendor_config *config;
1399b85b0ee1SMartin Blumenstingl 	struct rtl_vendor_config_entry *entry;
1400b85b0ee1SMartin Blumenstingl 	int i, total_data_len;
1401b85b0ee1SMartin Blumenstingl 	bool found = false;
1402b85b0ee1SMartin Blumenstingl 
1403b85b0ee1SMartin Blumenstingl 	total_data_len = btrtl_dev->cfg_len - sizeof(*config);
1404b85b0ee1SMartin Blumenstingl 	if (total_data_len <= 0) {
1405f1300c03SAlex Lu 		rtl_dev_warn(hdev, "no config loaded");
1406b85b0ee1SMartin Blumenstingl 		return -EINVAL;
1407b85b0ee1SMartin Blumenstingl 	}
1408b85b0ee1SMartin Blumenstingl 
1409b85b0ee1SMartin Blumenstingl 	config = (struct rtl_vendor_config *)btrtl_dev->cfg_data;
1410b85b0ee1SMartin Blumenstingl 	if (le32_to_cpu(config->signature) != RTL_CONFIG_MAGIC) {
1411f1300c03SAlex Lu 		rtl_dev_err(hdev, "invalid config magic");
1412b85b0ee1SMartin Blumenstingl 		return -EINVAL;
1413b85b0ee1SMartin Blumenstingl 	}
1414b85b0ee1SMartin Blumenstingl 
1415b85b0ee1SMartin Blumenstingl 	if (total_data_len < le16_to_cpu(config->total_len)) {
1416f1300c03SAlex Lu 		rtl_dev_err(hdev, "config is too short");
1417b85b0ee1SMartin Blumenstingl 		return -EINVAL;
1418b85b0ee1SMartin Blumenstingl 	}
1419b85b0ee1SMartin Blumenstingl 
1420b85b0ee1SMartin Blumenstingl 	for (i = 0; i < total_data_len; ) {
1421b85b0ee1SMartin Blumenstingl 		entry = ((void *)config->entry) + i;
1422b85b0ee1SMartin Blumenstingl 
1423b85b0ee1SMartin Blumenstingl 		switch (le16_to_cpu(entry->offset)) {
1424b85b0ee1SMartin Blumenstingl 		case 0xc:
1425b85b0ee1SMartin Blumenstingl 			if (entry->len < sizeof(*device_baudrate)) {
1426f1300c03SAlex Lu 				rtl_dev_err(hdev, "invalid UART config entry");
1427b85b0ee1SMartin Blumenstingl 				return -EINVAL;
1428b85b0ee1SMartin Blumenstingl 			}
1429b85b0ee1SMartin Blumenstingl 
1430b85b0ee1SMartin Blumenstingl 			*device_baudrate = get_unaligned_le32(entry->data);
1431b85b0ee1SMartin Blumenstingl 			*controller_baudrate = btrtl_convert_baudrate(
1432b85b0ee1SMartin Blumenstingl 							*device_baudrate);
1433b85b0ee1SMartin Blumenstingl 
1434b85b0ee1SMartin Blumenstingl 			if (entry->len >= 13)
1435b85b0ee1SMartin Blumenstingl 				*flow_control = !!(entry->data[12] & BIT(2));
1436b85b0ee1SMartin Blumenstingl 			else
1437b85b0ee1SMartin Blumenstingl 				*flow_control = false;
1438b85b0ee1SMartin Blumenstingl 
1439b85b0ee1SMartin Blumenstingl 			found = true;
1440b85b0ee1SMartin Blumenstingl 			break;
1441b85b0ee1SMartin Blumenstingl 
1442b85b0ee1SMartin Blumenstingl 		default:
1443f1300c03SAlex Lu 			rtl_dev_dbg(hdev, "skipping config entry 0x%x (len %u)",
1444b85b0ee1SMartin Blumenstingl 				   le16_to_cpu(entry->offset), entry->len);
1445b85b0ee1SMartin Blumenstingl 			break;
1446515d6798SYueHaibing 		}
1447b85b0ee1SMartin Blumenstingl 
1448b85b0ee1SMartin Blumenstingl 		i += sizeof(*entry) + entry->len;
1449b85b0ee1SMartin Blumenstingl 	}
1450b85b0ee1SMartin Blumenstingl 
1451b85b0ee1SMartin Blumenstingl 	if (!found) {
1452f1300c03SAlex Lu 		rtl_dev_err(hdev, "no UART config entry found");
1453b85b0ee1SMartin Blumenstingl 		return -ENOENT;
1454b85b0ee1SMartin Blumenstingl 	}
1455b85b0ee1SMartin Blumenstingl 
1456f1300c03SAlex Lu 	rtl_dev_dbg(hdev, "device baudrate = 0x%08x", *device_baudrate);
1457f1300c03SAlex Lu 	rtl_dev_dbg(hdev, "controller baudrate = %u", *controller_baudrate);
1458f1300c03SAlex Lu 	rtl_dev_dbg(hdev, "flow control %d", *flow_control);
1459b85b0ee1SMartin Blumenstingl 
1460b85b0ee1SMartin Blumenstingl 	return 0;
1461b85b0ee1SMartin Blumenstingl }
1462b85b0ee1SMartin Blumenstingl EXPORT_SYMBOL_GPL(btrtl_get_uart_settings);
1463b85b0ee1SMartin Blumenstingl 
1464db33c77dSCarlo Caione MODULE_AUTHOR("Daniel Drake <drake@endlessm.com>");
1465db33c77dSCarlo Caione MODULE_DESCRIPTION("Bluetooth support for Realtek devices ver " VERSION);
1466db33c77dSCarlo Caione MODULE_VERSION(VERSION);
1467db33c77dSCarlo Caione MODULE_LICENSE("GPL");
1468f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723a_fw.bin");
1469f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723b_fw.bin");
1470f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723b_config.bin");
1471c50903e3SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723bs_fw.bin");
1472c50903e3SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723bs_config.bin");
1473c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_fw.bin");
1474c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_config.bin");
1475c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_fw.bin");
1476c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_config.bin");
1477c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_fw.bin");
1478c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_config.bin");
1479bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8723d_fw.bin");
1480bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8723d_config.bin");
1481c50903e3SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723ds_fw.bin");
1482c50903e3SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723ds_config.bin");
1483f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8761a_fw.bin");
1484f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8761a_config.bin");
1485bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8761b_fw.bin");
1486bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8761b_config.bin");
1487bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8761bu_fw.bin");
1488bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8761bu_config.bin");
1489f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin");
1490f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin");
1491bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8821c_fw.bin");
1492bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8821c_config.bin");
1493bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8821cs_fw.bin");
1494bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8821cs_config.bin");
1495f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8822b_fw.bin");
1496f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8822b_config.bin");
1497bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8822cs_fw.bin");
1498bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8822cs_config.bin");
1499bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8822cu_fw.bin");
1500bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8822cu_config.bin");
1501bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8851bu_fw.bin");
1502bb23f07cSDan Gora MODULE_FIRMWARE("rtl_bt/rtl8851bu_config.bin");
15030d484db6SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852au_fw.bin");
15040d484db6SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852au_config.bin");
1505fe4b71b5SVictor Hassan MODULE_FIRMWARE("rtl_bt/rtl8852bs_fw.bin");
1506fe4b71b5SVictor Hassan MODULE_FIRMWARE("rtl_bt/rtl8852bs_config.bin");
150718e8055cSMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852bu_fw.bin");
150818e8055cSMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852bu_config.bin");
15098b1d66b5SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw.bin");
1510bd003fb3SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw_v2.bin");
15118b1d66b5SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852cu_config.bin");
1512