| /* | 
 |  * Copyright (c) 2019 Nordic Semiconductor ASA | 
 |  * | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  */ | 
 |  | 
 | #include <drivers/ipm.h> | 
 |  | 
 | #include <openamp/open_amp.h> | 
 | #include <metal/sys.h> | 
 | #include <metal/device.h> | 
 | #include <metal/alloc.h> | 
 |  | 
 | #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) | 
 | #define LOG_MODULE_NAME bt_hci_driver_nrf53 | 
 | #include "common/log.h" | 
 |  | 
 | void bt_rpmsg_rx(u8_t *data, size_t len); | 
 |  | 
 | static K_SEM_DEFINE(ready_sem, 0, 1); | 
 | static K_SEM_DEFINE(rx_sem, 0, 1); | 
 |  | 
 | static K_THREAD_STACK_DEFINE(bt_rpmsg_rx_thread_stack, | 
 | 			     CONFIG_BT_RPMSG_NRF53_RX_STACK_SIZE); | 
 | static struct k_thread bt_rpmsg_rx_thread_data; | 
 |  | 
 | static struct device *ipm_tx_handle; | 
 | static struct device *ipm_rx_handle; | 
 |  | 
 | /* Configuration defines */ | 
 |  | 
 | #define SHM_START_ADDR      (DT_IPC_SHM_BASE_ADDRESS + 0x400) | 
 | #define SHM_SIZE            0x7c00 | 
 | #define SHM_DEVICE_NAME     "sram0.shm" | 
 |  | 
 | #define VRING_COUNT         2 | 
 | #define VRING_TX_ADDRESS    (SHM_START_ADDR + SHM_SIZE - 0x400) | 
 | #define VRING_RX_ADDRESS    (VRING_TX_ADDRESS - 0x400) | 
 | #define VRING_ALIGNMENT     4 | 
 | #define VRING_SIZE          16 | 
 |  | 
 | #define VDEV_STATUS_ADDR    DT_IPC_SHM_BASE_ADDRESS | 
 |  | 
 | /* End of configuration defines */ | 
 |  | 
 | static metal_phys_addr_t shm_physmap[] = { SHM_START_ADDR }; | 
 | static struct metal_device shm_device = { | 
 | 	.name = SHM_DEVICE_NAME, | 
 | 	.bus = NULL, | 
 | 	.num_regions = 1, | 
 | 	.regions = { | 
 | 		{ | 
 | 			.virt       = (void *) SHM_START_ADDR, | 
 | 			.physmap    = shm_physmap, | 
 | 			.size       = SHM_SIZE, | 
 | 			.page_shift = 0xffffffff, | 
 | 			.page_mask  = 0xffffffff, | 
 | 			.mem_flags  = 0, | 
 | 			.ops        = { NULL }, | 
 | 		}, | 
 | 	}, | 
 | 	.node = { NULL }, | 
 | 	.irq_num = 0, | 
 | 	.irq_info = NULL | 
 | }; | 
 |  | 
 | static struct virtqueue *vq[2]; | 
 | static struct rpmsg_endpoint ep; | 
 |  | 
 | static unsigned char virtio_get_status(struct virtio_device *vdev) | 
 | { | 
 | 	return VIRTIO_CONFIG_STATUS_DRIVER_OK; | 
 | } | 
 |  | 
 | static void virtio_set_status(struct virtio_device *vdev, unsigned char status) | 
 | { | 
 | 	sys_write8(status, VDEV_STATUS_ADDR); | 
 | } | 
 |  | 
 | static u32_t virtio_get_features(struct virtio_device *vdev) | 
 | { | 
 | 	return BIT(VIRTIO_RPMSG_F_NS); | 
 | } | 
 |  | 
 | static void virtio_set_features(struct virtio_device *vdev, u32_t features) | 
 | { | 
 | 	/* No need for implementation */ | 
 | } | 
 |  | 
 | static void virtio_notify(struct virtqueue *vq) | 
 | { | 
 | 	int status; | 
 |  | 
 | 	status = ipm_send(ipm_tx_handle, 0, 0, NULL, 0); | 
 | 	if (status != 0) { | 
 | 		BT_ERR("ipm_send failed to notify: %d", status); | 
 | 	} | 
 | } | 
 |  | 
 | const struct virtio_dispatch dispatch = { | 
 | 	.get_status = virtio_get_status, | 
 | 	.set_status = virtio_set_status, | 
 | 	.get_features = virtio_get_features, | 
 | 	.set_features = virtio_set_features, | 
 | 	.notify = virtio_notify, | 
 | }; | 
 |  | 
 | static void ipm_callback(void *context, u32_t id, volatile void *data) | 
 | { | 
 | 	BT_DBG("Got callback of id %u", id); | 
 | 	k_sem_give(&rx_sem); | 
 | } | 
 |  | 
 | static int endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, | 
 | 	u32_t src, void *priv) | 
 | { | 
 | 	BT_DBG("Received message of %u bytes.", len); | 
 | 	BT_HEXDUMP_DBG((uint8_t *)data, len, "Data:"); | 
 |  | 
 | 	bt_rpmsg_rx(data, len); | 
 |  | 
 | 	return RPMSG_SUCCESS; | 
 | } | 
 |  | 
 | static void rpmsg_service_unbind(struct rpmsg_endpoint *ep) | 
 | { | 
 | 	rpmsg_destroy_ept(ep); | 
 | } | 
 |  | 
 | static void ns_bind_cb(struct rpmsg_device *rdev, const char *name, u32_t dest) | 
 | { | 
 | 	(void)rpmsg_create_ept(&ep, | 
 | 				rdev, | 
 | 				name, | 
 | 				RPMSG_ADDR_ANY, | 
 | 				dest, | 
 | 				endpoint_cb, | 
 | 				rpmsg_service_unbind); | 
 |  | 
 | 	k_sem_give(&ready_sem); | 
 | } | 
 |  | 
 | static void bt_rpmsg_rx_thread(void *p1, void *p2, void *p3) | 
 | { | 
 | 	ARG_UNUSED(p1); | 
 | 	ARG_UNUSED(p2); | 
 | 	ARG_UNUSED(p3); | 
 |  | 
 | 	while (1) { | 
 | 		int status = k_sem_take(&rx_sem, K_FOREVER); | 
 |  | 
 | 		if (status == 0) { | 
 | 			virtqueue_notification(vq[0]); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | int bt_rpmsg_platform_init(void) | 
 | { | 
 | 	int err; | 
 | 	struct metal_init_params metal_params = METAL_INIT_DEFAULTS; | 
 |  | 
 | 	static struct virtio_vring_info     rvrings[2]; | 
 | 	static struct rpmsg_virtio_shm_pool shpool; | 
 | 	static struct virtio_device         vdev; | 
 | 	static struct rpmsg_virtio_device   rvdev; | 
 | 	static struct metal_io_region       *io; | 
 | 	static struct metal_device          *device; | 
 |  | 
 | 	/* Setup thread for RX data processing. */ | 
 | 	k_thread_create(&bt_rpmsg_rx_thread_data, bt_rpmsg_rx_thread_stack, | 
 | 			K_THREAD_STACK_SIZEOF(bt_rpmsg_rx_thread_stack), | 
 | 			bt_rpmsg_rx_thread, NULL, NULL, NULL, | 
 | 			K_PRIO_COOP(CONFIG_BT_RPMSG_NRF53_RX_PRIO), | 
 | 			0, K_NO_WAIT); | 
 |  | 
 | 	/* Libmetal setup */ | 
 | 	err = metal_init(&metal_params); | 
 | 	if (err) { | 
 | 		BT_ERR("metal_init: failed - error code %d", err); | 
 | 		return err; | 
 | 	} | 
 |  | 
 | 	err = metal_register_generic_device(&shm_device); | 
 | 	if (err) { | 
 | 		BT_ERR("Couldn't register shared memory device: %d", err); | 
 | 		return err; | 
 | 	} | 
 |  | 
 | 	err = metal_device_open("generic", SHM_DEVICE_NAME, &device); | 
 | 	if (err) { | 
 | 		BT_ERR("metal_device_open failed: %d", err); | 
 | 		return err; | 
 | 	} | 
 |  | 
 | 	io = metal_device_io_region(device, 0); | 
 | 	if (!io) { | 
 | 		BT_ERR("metal_device_io_region failed to get region"); | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	/* IPM setup */ | 
 | 	ipm_tx_handle = device_get_binding("IPM_0"); | 
 | 	if (!ipm_tx_handle) { | 
 | 		BT_ERR("Could not get TX IPM device handle"); | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	ipm_rx_handle = device_get_binding("IPM_1"); | 
 | 	if (!ipm_rx_handle) { | 
 | 		BT_ERR("Could not get RX IPM device handle"); | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	ipm_register_callback(ipm_rx_handle, ipm_callback, NULL); | 
 |  | 
 | 	/* Virtqueue setup */ | 
 | 	vq[0] = virtqueue_allocate(VRING_SIZE); | 
 | 	if (!vq[0]) { | 
 | 		BT_ERR("virtqueue_allocate failed to alloc vq[0]"); | 
 | 		return -ENOMEM; | 
 | 	} | 
 |  | 
 | 	vq[1] = virtqueue_allocate(VRING_SIZE); | 
 | 	if (!vq[1]) { | 
 | 		BT_ERR("virtqueue_allocate failed to alloc vq[1]"); | 
 | 		return -ENOMEM; | 
 | 	} | 
 |  | 
 | 	rvrings[0].io = io; | 
 | 	rvrings[0].info.vaddr = (void *)VRING_TX_ADDRESS; | 
 | 	rvrings[0].info.num_descs = VRING_SIZE; | 
 | 	rvrings[0].info.align = VRING_ALIGNMENT; | 
 | 	rvrings[0].vq = vq[0]; | 
 |  | 
 | 	rvrings[1].io = io; | 
 | 	rvrings[1].info.vaddr = (void *)VRING_RX_ADDRESS; | 
 | 	rvrings[1].info.num_descs = VRING_SIZE; | 
 | 	rvrings[1].info.align = VRING_ALIGNMENT; | 
 | 	rvrings[1].vq = vq[1]; | 
 |  | 
 | 	vdev.role = RPMSG_MASTER; | 
 | 	vdev.vrings_num = VRING_COUNT; | 
 | 	vdev.func = &dispatch; | 
 | 	vdev.vrings_info = &rvrings[0]; | 
 |  | 
 | 	rpmsg_virtio_init_shm_pool(&shpool, (void *)SHM_START_ADDR, SHM_SIZE); | 
 | 	err = rpmsg_init_vdev(&rvdev, &vdev, ns_bind_cb, io, &shpool); | 
 | 	if (err) { | 
 | 		BT_ERR("rpmsg_init_vdev failed %d", err); | 
 | 		return err; | 
 | 	} | 
 |  | 
 | 	/* Wait til nameservice ep is setup */ | 
 | 	k_sem_take(&ready_sem, K_FOREVER); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int bt_rpmsg_platform_send(struct net_buf *buf) | 
 | { | 
 | 	return rpmsg_send(&ep, buf->data, buf->len); | 
 | } |