1 /*
2  * Copyright (c) 2024 Cypress Semiconductor Corporation (an Infineon company) or
3  * an affiliate of Cypress Semiconductor Corporation
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8  /**
9   * @brief Zephyr CYW20829 driver.
10   *
11   *  This driver uses btstack-integration asset as hosts platform adaptation layer
12   *  (porting layer) for CYW20829. btstack-integration layer implements/
13   *  invokes the interfaces defined by BTSTACK to enable communication
14   *  with the BT controller by using IPC_BTSS (IPC Bluetooth sub-system interface).
15   *  Zephyr CYW20829 driver implements wiced_bt_**** functions requreds for
16   *  btstack-integration asset and Zephyr Bluetooth driver interface
17   *  (defined in struct bt_hci_driver).
18   *
19   *                                               CM33 (application core)
20   *                                   |=========================================|
21   *                                   |            |-------------------------|  |
22   *                                   |            |     Zephyr application  |  |
23   *                                   |            |-------------------------|  |
24   *                                   |                               |         |
25   *                                   |                         |------------|  |
26   *                                   |                         |  Zephyr    |  |
27   *                                   |                         |  Bluetooth |  |
28   *      CM33 (BTSS core)             |                         |  Host      |  |
29   *  |=====================|          |                         |------------|  |
30   *  |                     |          |                               |         |
31   *  |  |---------------|  |          |   |--------------|      | -----------|  |
32   *  |  | Bluetooth     |  | IPC_BTSS |   | btstack-     |      |  Zephyr    |  |
33   *  |  | Controller FW |  | <--------|-> | integration  | ---- |  CYW20829  |  |
34   *  |  |---------------|  |          |   | asset        |      |  driver    |  |
35   *  |                     |          |   |--------------|      |------------|  |
36   *  |=====================|          |                                         |
37   *            |                      |=========================================|
38   *  |====================|
39   *  |     CYW20829       |
40   *  |     Bluetooth      |
41   *  |====================|
42   *
43   *  NOTE:
44   *   cyw920829 requires fetch binary files of Bluetooth controller firmware.
45   *   To fetch Binary Blobs:  west blobs fetch hal_infineon
46   *
47   */
48 
49 #include <errno.h>
50 #include <stddef.h>
51 #include <string.h>
52 
53 #include <zephyr/arch/cpu.h>
54 #include <zephyr/bluetooth/bluetooth.h>
55 #include <zephyr/bluetooth/hci.h>
56 #include <zephyr/drivers/bluetooth.h>
57 #include <zephyr/drivers/uart.h>
58 #include <zephyr/init.h>
59 #include <zephyr/sys/byteorder.h>
60 #include <zephyr/sys/util.h>
61 #include <zephyr/logging/log.h>
62 
63 #include <wiced_bt_stack_platform.h>
64 #include <cybt_platform_config.h>
65 #include <cybt_platform_trace.h>
66 #include <cybt_platform_hci.h>
67 #include <cybt_platform_task.h>
68 
69 #include <cyabs_rtos.h>
70 #include <cybt_result.h>
71 
72 #define LOG_LEVEL  CONFIG_BT_HCI_DRIVER_LOG_LEVEL
73 #include <zephyr/logging/log.h>
74 LOG_MODULE_REGISTER(cyw208xx);
75 
76 #define DT_DRV_COMPAT infineon_cyw208xx_hci
77 
78 struct cyw208xx_data {
79 	bt_hci_recv_t recv;
80 };
81 
82 enum {
83 	BT_HCI_VND_OP_DOWNLOAD_MINIDRIVER = 0xFC2E,
84 	BT_HCI_VND_OP_WRITE_RAM = 0xFC4C,
85 	BT_HCI_VND_OP_LAUNCH_RAM = 0xFC4E,
86 	BT_HCI_VND_OP_UPDATE_BAUDRATE = 0xFC18,
87 };
88 
89 /* Externs for CY43xxx controller FW */
90 extern const uint8_t brcm_patchram_buf[];
91 extern const int brcm_patch_ram_length;
92 
93 #define CYBSP_BT_PLATFORM_CFG_SLEEP_MODE_LP_ENABLED   (1)
94 
95 static K_SEM_DEFINE(hci_sem, 1, 1);
96 static K_SEM_DEFINE(cybt_platform_task_init_sem, 0, 1);
97 
98 
99 /******************************************************************************
100  *                          Function Declarations
101  ******************************************************************************/
102 extern void host_stack_platform_interface_init(void);
103 extern void cybt_platform_hci_wait_for_boot_fully_up(bool is_from_isr);
104 extern uint8_t *host_stack_get_acl_to_lower_buffer(wiced_bt_transport_t transport, uint32_t size);
105 extern wiced_result_t host_stack_send_acl_to_lower(wiced_bt_transport_t transport,
106 						   uint8_t *data, uint16_t len);
107 extern wiced_result_t host_stack_send_cmd_to_lower(uint8_t *cmd, uint16_t cmd_len);
108 extern wiced_result_t host_stack_send_iso_to_lower(uint8_t *data, uint16_t len);
109 extern cybt_result_t cybt_platform_msg_to_bt_task(const uint16_t msg, bool is_from_isr);
110 extern void cybt_bttask_deinit(void);
111 
cyw208xx_bt_firmware_download(const uint8_t * firmware_image,uint32_t size)112 static int cyw208xx_bt_firmware_download(const uint8_t *firmware_image, uint32_t size)
113 {
114 	uint8_t *data = (uint8_t *)firmware_image;
115 	volatile uint32_t remaining_length = size;
116 	struct net_buf *buf;
117 	int err;
118 
119 	LOG_DBG("Executing Fw downloading for CYW208xx device");
120 
121 	/* The firmware image (.hcd format) contains a collection of hci_write_ram
122 	 * command + a block of the image, followed by a hci_write_ram image at the end.
123 	 * Parse and send each individual command and wait for the response. This is to
124 	 * ensure the integrity of the firmware image sent to the bluetooth chip.
125 	 */
126 	while (remaining_length) {
127 		size_t data_length = data[2]; /* data length from firmware image block */
128 		uint16_t op_code = *(uint16_t *)data;
129 
130 		/* Allocate buffer for hci_write_ram/hci_launch_ram command. */
131 		buf = bt_hci_cmd_create(op_code, data_length);
132 		if (buf == NULL) {
133 			LOG_ERR("Unable to allocate command buffer");
134 			return err;
135 		}
136 
137 		/* Add data part of packet */
138 		net_buf_add_mem(buf, &data[3], data_length);
139 
140 		/* Send hci_write_ram command. */
141 		err = bt_hci_cmd_send_sync(op_code, buf, NULL);
142 		if (err) {
143 			return err;
144 		}
145 
146 		switch (op_code) {
147 		case BT_HCI_VND_OP_WRITE_RAM:
148 			/* Update remaining length and data pointer:
149 			 * content of data length + 2 bytes of opcode and 1 byte of data length.
150 			 */
151 			data += data_length + 3;
152 			remaining_length -= data_length + 3;
153 			break;
154 
155 		case BT_HCI_VND_OP_LAUNCH_RAM:
156 			remaining_length = 0;
157 			break;
158 
159 		default:
160 			return -ENOMEM;
161 		}
162 	}
163 
164 	LOG_DBG("Fw downloading complete");
165 	return 0;
166 }
167 
cyw208xx_setup(const struct device * dev,const struct bt_hci_setup_params * params)168 static int cyw208xx_setup(const struct device *dev, const struct bt_hci_setup_params *params)
169 {
170 	ARG_UNUSED(dev);
171 	ARG_UNUSED(params);
172 	int err;
173 
174 	/* Send HCI_RESET */
175 	err = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, NULL);
176 	if (err) {
177 		return err;
178 	}
179 
180 	/* BT firmware download */
181 	err = cyw208xx_bt_firmware_download(brcm_patchram_buf, (uint32_t)brcm_patch_ram_length);
182 	if (err) {
183 		return err;
184 	}
185 
186 	/* Waiting when BLE up after firmware launch */
187 	cybt_platform_hci_wait_for_boot_fully_up(false);
188 	return 0;
189 }
190 
cyw208xx_open(const struct device * dev,bt_hci_recv_t recv)191 static int cyw208xx_open(const struct device *dev, bt_hci_recv_t recv)
192 {
193 	int err;
194 	struct cyw208xx_data *hci = dev->data;
195 
196 	hci->recv = recv;
197 
198 	/* Initialize Bluetooth platform related OS tasks. */
199 	err = cybt_platform_task_init((void *)NULL);
200 	if (err) {
201 		return err;
202 	}
203 
204 	/* Wait until cybt platform task starts */
205 	k_sem_take(&cybt_platform_task_init_sem, K_FOREVER);
206 
207 	return 0;
208 }
209 
cyw208xx_close(const struct device * dev)210 static int cyw208xx_close(const struct device *dev)
211 {
212 	struct cyw208xx_data *hci = dev->data;
213 
214 	/* Send SHUTDOWN event, BT task will release resources and tervinate task */
215 	cybt_platform_msg_to_bt_task(BT_EVT_TASK_SHUTDOWN, false);
216 
217 	cybt_bttask_deinit();
218 
219 	k_sem_reset(&cybt_platform_task_init_sem);
220 	hci->recv = NULL;
221 
222 	return 0;
223 }
224 
cyw208xx_send(const struct device * dev,struct net_buf * buf)225 static int cyw208xx_send(const struct device *dev, struct net_buf *buf)
226 {
227 	ARG_UNUSED(dev);
228 
229 	int ret = 0;
230 
231 	k_sem_take(&hci_sem, K_FOREVER);
232 
233 	LOG_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf), buf->len);
234 
235 	switch (bt_buf_get_type(buf)) {
236 	case BT_BUF_ACL_OUT:
237 		uint8_t *bt_msg = host_stack_get_acl_to_lower_buffer(BT_TRANSPORT_LE, buf->len);
238 
239 		memcpy(bt_msg, buf->data, buf->len);
240 		ret = host_stack_send_acl_to_lower(BT_TRANSPORT_LE, bt_msg, buf->len);
241 		break;
242 
243 	case BT_BUF_CMD:
244 		ret = host_stack_send_cmd_to_lower(buf->data, buf->len);
245 		break;
246 
247 	case BT_BUF_ISO_OUT:
248 		ret = host_stack_send_iso_to_lower(buf->data, buf->len);
249 		break;
250 
251 	default:
252 		LOG_ERR("Unknown type %u", bt_buf_get_type(buf));
253 		ret = EIO;
254 		goto done;
255 	}
256 
257 	LOG_HEXDUMP_DBG(buf->data, buf->len, "Final HCI buffer:");
258 
259 	if (ret) {
260 		LOG_ERR("SPI write error %d", ret);
261 	}
262 
263 done:
264 	k_sem_give(&hci_sem);
265 	net_buf_unref(buf);
266 	return ret ? -EIO : 0;
267 }
268 
269 static const struct bt_hci_driver_api drv = {
270 	.open = cyw208xx_open,
271 	.close = cyw208xx_close,
272 	.send = cyw208xx_send,
273 	.setup = cyw208xx_setup
274 };
275 
cyw208xx_hci_init(const struct device * dev)276 static int cyw208xx_hci_init(const struct device *dev)
277 {
278 	ARG_UNUSED(dev);
279 
280 	const cybt_platform_config_t cybsp_bt_platform_cfg = {
281 		.hci_config = {
282 			.hci_transport = CYBT_HCI_IPC,
283 		},
284 
285 		.controller_config = {
286 			.sleep_mode = {
287 				.sleep_mode_enabled = CYBSP_BT_PLATFORM_CFG_SLEEP_MODE_LP_ENABLED,
288 			},
289 		}
290 	};
291 
292 	/* Configure platform specific settings for the BT device */
293 	cybt_platform_config_init(&cybsp_bt_platform_cfg);
294 
295 	return 0;
296 }
297 
298 /* Implements wiced_bt_**** functions requreds for the btstack-integration asset */
299 
wiced_bt_dev_vendor_specific_command(uint16_t opcode,uint8_t param_len,uint8_t * param_buf,wiced_bt_dev_vendor_specific_command_complete_cback_t cback)300 wiced_result_t wiced_bt_dev_vendor_specific_command(uint16_t opcode, uint8_t param_len,
301 	uint8_t *param_buf, wiced_bt_dev_vendor_specific_command_complete_cback_t cback)
302 
303 {
304 	/*
305 	 * This function is using only by btstack-integration asset
306 	 * for enable LPM.
307 	 */
308 	struct net_buf *buf = NULL;
309 
310 	/* Allocate a HCI command buffer */
311 	buf = bt_hci_cmd_create(opcode, param_len);
312 	if (!buf) {
313 		LOG_ERR("Unable to allocate buffer");
314 		return WICED_NO_MEMORY;
315 	}
316 
317 	/* Add data part of packet */
318 	net_buf_add_mem(buf, param_buf, param_len);
319 	bt_hci_cmd_send(opcode, buf);
320 
321 	return WICED_BT_SUCCESS;
322 }
323 
wiced_bt_process_hci(hci_packet_type_t pti,uint8_t * data,uint32_t length)324 void wiced_bt_process_hci(hci_packet_type_t pti, uint8_t *data, uint32_t length)
325 {
326 	const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0));
327 	struct cyw208xx_data *hci = dev->data;
328 	struct net_buf *buf = NULL;
329 	size_t buf_tailroom = 0;
330 
331 	switch (pti) {
332 	case HCI_PACKET_TYPE_EVENT:
333 		buf = bt_buf_get_evt(data[0], 0, K_NO_WAIT);
334 		if (!buf) {
335 			LOG_ERR("Failed to allocate the buffer for RX: EVENT ");
336 			return;
337 		}
338 		break;
339 
340 	case HCI_PACKET_TYPE_ACL:
341 		buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_NO_WAIT);
342 		if (!buf) {
343 			LOG_ERR("Failed to allocate the buffer for RX: ACL ");
344 			return;
345 		}
346 		bt_buf_set_type(buf, BT_BUF_ACL_IN);
347 		break;
348 
349 	case HCI_PACKET_TYPE_SCO:
350 		/* NA */
351 		break;
352 
353 	case HCI_PACKET_TYPE_ISO:
354 		buf = bt_buf_get_rx(BT_BUF_ISO_IN, K_NO_WAIT);
355 		if (!buf) {
356 			LOG_ERR("Failed to allocate the buffer for RX: ISO ");
357 			return;
358 		}
359 		break;
360 
361 	default:
362 		return;
363 
364 	}
365 
366 	buf_tailroom = net_buf_tailroom(buf);
367 	if (buf_tailroom < length) {
368 		LOG_WRN("Not enough space for rx data");
369 		return;
370 	}
371 	net_buf_add_mem(buf, data, length);
372 
373 	/* Provide the buffer to the host */
374 	hci->recv(dev, buf);
375 }
376 
wiced_bt_process_hci_events(uint8_t * data,uint32_t length)377 void wiced_bt_process_hci_events(uint8_t *data, uint32_t length)
378 {
379 	wiced_bt_process_hci(HCI_PACKET_TYPE_EVENT, data, length);
380 }
381 
wiced_bt_process_acl_data(uint8_t * data,uint32_t length)382 void wiced_bt_process_acl_data(uint8_t *data, uint32_t length)
383 {
384 	wiced_bt_process_hci(HCI_PACKET_TYPE_ACL, data, length);
385 }
386 
wiced_bt_process_isoc_data(uint8_t * data,uint32_t length)387 void wiced_bt_process_isoc_data(uint8_t *data, uint32_t length)
388 {
389 	wiced_bt_process_hci(HCI_PACKET_TYPE_ISO, data, length);
390 }
391 
wiced_bt_stack_init_internal(wiced_bt_management_cback_t mgmt_cback,wiced_bt_internal_post_stack_init_cb post_stack_cb,wiced_bt_internal_stack_evt_handler_cb evt_handler_cb)392 void wiced_bt_stack_init_internal(wiced_bt_management_cback_t mgmt_cback,
393 				  wiced_bt_internal_post_stack_init_cb post_stack_cb,
394 				  wiced_bt_internal_stack_evt_handler_cb evt_handler_cb)
395 {
396 	k_sem_give(&cybt_platform_task_init_sem);
397 }
398 
399 /* Keep below empty functions, used in the btstack_integration assets for Wiced BT stack. */
wiced_bt_stack_indicate_lower_tx_complete(void)400 void wiced_bt_stack_indicate_lower_tx_complete(void)
401 {
402 	/* NA for Zephyr */
403 }
404 
wiced_bt_stack_shutdown(void)405 void wiced_bt_stack_shutdown(void)
406 {
407 	/* NA for Zephyr */
408 }
409 
wiced_bt_process_timer(void)410 void wiced_bt_process_timer(void)
411 {
412 	/* NA for Zephyr */
413 }
414 
415 #define CYW208XX_DEVICE_INIT(inst) \
416 	static struct cyw208xx_data cyw208xx_data_##inst = { \
417 	}; \
418 	DEVICE_DT_INST_DEFINE(inst, cyw208xx_hci_init, NULL, &cyw208xx_data_##inst, NULL, \
419 			      POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &drv)
420 
421 /* Only one instance supported */
422 CYW208XX_DEVICE_INIT(0)
423