From 5f474cadf30154f907d41c9577ea5a7fbc6d4ac6 Mon Sep 17 00:00:00 2001 From: Rajkumar Ayyasamy Date: Mon, 9 Nov 2020 18:56:20 +0530 Subject: [PATCH 1/2] ipq5018: add BT IPC over shared memory driver https://source.codeaurora.org/quic/qsdk/oss/kernel/linux-msm/tree/drivers/soc/qcom/bt?h=win.coretech.1.0 Signed-off-by: Rajkumar Ayyasamy Change-Id: If52b52ebe8774063f52f7b597991fbb562526db6 --- board/qca/arm/ipq5018/Makefile | 1 + board/qca/arm/ipq5018/bt.h | 208 +++++++++++++++++ board/qca/arm/ipq5018/bt_ipc.c | 396 +++++++++++++++++++++++++++++++++ 3 files changed, 605 insertions(+) create mode 100644 board/qca/arm/ipq5018/bt.h create mode 100644 board/qca/arm/ipq5018/bt_ipc.c diff --git a/board/qca/arm/ipq5018/Makefile b/board/qca/arm/ipq5018/Makefile index af85fabdea..ea342f2bcd 100644 --- a/board/qca/arm/ipq5018/Makefile +++ b/board/qca/arm/ipq5018/Makefile @@ -2,4 +2,5 @@ ccflags-y += -I$(srctree)/board/qca/arm/ipq5018 cppflags-y += -I$(srctree)/board/qca/arm/ipq5018 obj-y := ipq5018.o obj-y += clock.o +obj-$(CONFIG_IPQ_BT_SUPPORT) += bt_ipc.o diff --git a/board/qca/arm/ipq5018/bt.h b/board/qca/arm/ipq5018/bt.h new file mode 100644 index 0000000000..92af9e27e6 --- /dev/null +++ b/board/qca/arm/ipq5018/bt.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _BT_H +#define _BT_H + +#include + +#define PAS_ID 0xC +#define CMD_ID 0x14 +#define BT_M0_WARM_RST_ORIDE 0x0 +#define BT_M0_WARM_RST 0x4 + +#define IOCTL_IPC_BOOT 0xBE +#define IPC_TX_QSIZE 0x20 + +#define TO_APPS_ADDR(a) (btmem->virt + (int)(uintptr_t)a) +#define TO_BT_ADDR(a) (a - btmem->virt) +#define IPC_LBUF_SZ(w, x, y, z) (((TO_BT_ADDR((void *)w) + w->x) - w->y) / w->z) + +#define IPC_MSG_HDR_SZ (4u) +#define IPC_MSG_PLD_SZ (40u) +#define IPC_TOTAL_MSG_SZ (IPC_MSG_HDR_SZ + IPC_MSG_PLD_SZ) + +#define IPC_LMSG_MASK (0x8000u) +#define IPC_RACK_MASK (0x4000u) +#define IPC_PKT_TYPE_MASK (0x0300u) +#define IPC_MSG_ID_MASK (0x00FFu) + +#define IPC_LMSG_TYPE ((uint16_t) IPC_LMSG_MASK) +#define IPC_SMSG_TYPE ((uint16_t) 0x0000u) +#define IPC_REQ_ACK ((uint16_t) IPC_RACK_MASK) +#define IPC_NO_ACK ((uint16_t) 0x0000u) +#define IPC_PKT_TYPE_CUST ((uint16_t) 0x0000u) +#define IPC_PKT_TYPE_HCI ((uint16_t) 0x0100u) +#define IPC_PKT_TYPE_AUDIO ((uint16_t) 0x0200u) +#define IPC_PKT_TYPE_RFU (IPC_PKT_TYPE_MASK) + +#define IPC_LMSG_SHIFT (15u) +#define IPC_RACK_SHIFT (14u) +#define IPC_PKT_TYPE_SHIFT (8u) + +#define GET_NO_OF_BLOCKS(a, b) ((a + b - 1) / b) + +#define GET_RX_INDEX_FROM_BUF(x, y) ((x - btmem->rx_ctxt->lring_buf) / y) + +#define GET_TX_INDEX_FROM_BUF(x, y) ((x - btmem->tx_ctxt->lring_buf) / y) + +#define IS_RX_MEM_NON_CONTIGIOUS(pBuf, len, sz) \ + ((pBuf + len) > \ + (btmem->rx_ctxt->lring_buf + \ + (sz * btmem->rx_ctxt->lmsg_buf_cnt))) + +/** Message header format. + * + * --------------------------------------------------------------- + * BitPos | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 - 0 | + * --------------------------------------------------------------- + * Field | Long Msg |rAck| RFU | PktType | msgID | + * --------------------------------------------------------------- + * + * - Long Msg : + * + * - reqAck : This is interpreted by receiver for sending acknowledegement + * to sender i.e. send a ack IPC interrupt if set. + * Use @ref IS_REQ_ACK or @ref IS_NO_ACK + * to determine ack is requested or not. + * + * - RFU : Reserved for future use. + * + * - pktType : + * + * - msgID : Contains unique message ID within a Category. + * Use @ref IPC_GET_MSG_ID to get message ID. + */ +#define IPC_ConstructMsgHeader(msgID, reqAck, pktType, longMsg) \ + (((uint8_t) longMsg << IPC_LMSG_SHIFT) | \ + ((uint8_t) reqAck << IPC_RACK_SHIFT) | \ + ((uint16_t) pktType << IPC_PKT_TYPE_SHIFT) | msgID) + +#define IPC_GET_PKT_TYPE(hdr) \ + ((enum ipc_pkt_type)((hdr & IPC_PKT_TYPE_MASK) >> IPC_PKT_TYPE_SHIFT)) + +#define IS_LONG_MSG(hdr) ((hdr & IPC_LMSG_MASK) == IPC_LMSG_TYPE) +#define IS_SHORT_MSG(hdr) ((hdr & IPC_LMSG_MASK) == IPC_SMSG_TYPE) + +#define IS_REQ_ACK(hdr) ((hdr & IPC_RACK_MASK) == IPC_REQ_ACK) +#define IS_NO_ACK(hdr) ((hdr & IPC_RACK_MASK) == IPC_NO_ACK) + +#define IS_HCI_PKT(hdr) ((hdr & IPC_PKT_TYPE_MASK) == IPC_PKT_TYPE_HCI) +#define IS_CUST_PKT(hdr) ((hdr & IPC_PKT_TYPE_MASK) == IPC_PKT_TYPE_CUST) + +#define IPC_GET_MSG_ID(hdr) ((uint8_t)(hdr & IPC_MSG_ID_MASK)) + +#define IPC_CMD_IPC_STOP (0x01) +#define IPC_CMD_SWITCH_TO_UART (0x02) +#define IPC_CMD_PREPARE_DUMP (0x03) +#define IPC_CMD_COLLECT_DUMP (0x04) +#define IPC_CMD_IPC_START (0x05) + +#define BT_RAM_START 0x7000000 +#define BT_RAM_PATCH 0x7020250 +#define BT_RAM_SIZE 0x58000 +#define SYSCON 0x0B111000 + +/*------------------------------------------------------------------------- + * Type Declarations + * ------------------------------------------------------------------------ + */ + +enum ipc_pkt_type { + IPC_CUST_PKT, + IPC_HCI_PKT, + IPC_AUDIO_PKT, + IPC_PKT_MAX +}; + +struct long_msg_info { + uint16_t smsg_free_cnt; + uint16_t lmsg_free_cnt; + uint8_t ridx; + uint8_t widx; +}; + +struct ipc_aux_ptr { + uint32_t len; + uint32_t buf; +}; + +struct ring_buffer { + uint16_t msg_hdr; + uint16_t len; + + union { + uint8_t smsg_data[IPC_MSG_PLD_SZ]; + uint32_t lmsg_data; + } payload; +}; + +struct ring_buffer_info { + uint32_t rbuf; + uint8_t ring_buf_cnt; + uint8_t ridx; + uint8_t widx; + uint8_t tidx; + uint32_t next; +}; + +struct context_info { + uint16_t TotalMemorySize; + uint8_t lmsg_buf_cnt; + uint8_t smsg_buf_cnt; + struct ring_buffer_info sring_buf_info; + uint32_t sring_buf; + uint32_t lring_buf; + uint32_t reserved; +}; + + +struct bt_mem { + phys_addr_t phys; + phys_addr_t reloc; + void __iomem *virt; + size_t size; + struct context_info *tx_ctxt; + struct context_info *rx_ctxt; + struct long_msg_info lmsg_ctxt; +}; + +struct bt_ipc { + uint32_t regmap; + int offset; + int bit; + int irq; + atomic_t tx_q_cnt; +}; + +struct bt_descriptor { + void __iomem *warm_reset; + struct bt_ipc ipc; + struct bt_mem btmem; + int (*sendmsg_cb)(struct bt_descriptor *, unsigned char *, int); + void (*recvmsg_cb)(struct bt_descriptor *, unsigned char *, int); + atomic_t state; + atomic_t tx_in_progress; + unsigned char *buf; + uint32_t len; +}; + +struct ipc_intent { + uint8_t *buf; + uint16_t len; +}; + +extern int bt_ipc_sendmsg(struct bt_descriptor *btDesc, unsigned char *buf, int len ); +extern void bt_ipc_init(struct bt_descriptor *btDesc); +extern void bt_ipc_worker(struct bt_descriptor *btDesc); +#endif /* _BT_H */ diff --git a/board/qca/arm/ipq5018/bt_ipc.c b/board/qca/arm/ipq5018/bt_ipc.c new file mode 100644 index 0000000000..18caf82270 --- /dev/null +++ b/board/qca/arm/ipq5018/bt_ipc.c @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include +#include "bt.h" + +#include +#include + +#include + +static void *bt_ipc_alloc_lmsg(struct bt_descriptor *btDesc, uint32_t len, + struct ipc_aux_ptr *aux_ptr, uint8_t *is_lbuf_full) +{ + uint8_t idx; + uint8_t blks; + uint8_t blks_consumed; + struct bt_mem *btmem = &btDesc->btmem; + uint32_t lsz = IPC_LBUF_SZ(btmem->tx_ctxt, TotalMemorySize, lring_buf, + lmsg_buf_cnt); + + if (btmem->tx_ctxt->lring_buf == 0) { + printf("no long message buffer not initialized\n"); + return ERR_PTR(-ENODEV); + } + + blks = GET_NO_OF_BLOCKS(len, lsz); + + if (!btmem->lmsg_ctxt.lmsg_free_cnt || + (blks > btmem->lmsg_ctxt.lmsg_free_cnt)) + return ERR_PTR(-EAGAIN); + + idx = btmem->lmsg_ctxt.widx; + + if ((btmem->lmsg_ctxt.widx + blks) > btmem->tx_ctxt->lmsg_buf_cnt) { + blks_consumed = btmem->tx_ctxt->lmsg_buf_cnt - idx; + aux_ptr->len = len - (blks_consumed * lsz); + aux_ptr->buf = btmem->tx_ctxt->lring_buf; + } + + btmem->lmsg_ctxt.widx = (btmem->lmsg_ctxt.widx + blks) % + btmem->tx_ctxt->lmsg_buf_cnt; + + btmem->lmsg_ctxt.lmsg_free_cnt -= blks; + + if (btmem->lmsg_ctxt.lmsg_free_cnt <= + ((btmem->tx_ctxt->lmsg_buf_cnt * 20) / 100)) + *is_lbuf_full = 1; + + return (TO_APPS_ADDR(btmem->tx_ctxt->lring_buf) + (idx * lsz)); +} + +static struct ring_buffer_info *bt_ipc_get_tx_rbuf(struct bt_descriptor *btDesc, + uint8_t *is_sbuf_full) +{ + uint8_t idx; + struct ring_buffer_info *rinfo; + struct bt_mem *btmem = &btDesc->btmem; + + for (rinfo = &(btmem->tx_ctxt->sring_buf_info); rinfo != NULL; + rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) { + idx = (rinfo->widx + 1) % (btmem->tx_ctxt->smsg_buf_cnt); + + if (idx != rinfo->tidx) { + btmem->lmsg_ctxt.smsg_free_cnt--; + + if (btmem->lmsg_ctxt.smsg_free_cnt <= + ((btmem->tx_ctxt->smsg_buf_cnt * 20) / 100)) + *is_sbuf_full = 1; + + return rinfo; + } + } + + return ERR_PTR(-EAGAIN); +} + +int bt_ipc_send_msg(struct bt_descriptor *btDesc, uint16_t msg_hdr, + const uint8_t *pData, uint16_t len, bool dequeue) +{ + int ret = 0; + struct bt_mem *btmem = &btDesc->btmem; + struct ring_buffer_info *rinfo; + struct ring_buffer *rbuf; + uint8_t is_lbuf_full = 0; + uint8_t is_sbuf_full = 0; + struct ipc_aux_ptr aux_ptr; + void *lmsg_data; + + rinfo = bt_ipc_get_tx_rbuf(btDesc, &is_sbuf_full); + if (IS_ERR(rinfo)) { + printf("short msg buf full, queuing msg[%d]\n", + atomic_read(&btDesc->ipc.tx_q_cnt)); + ret = PTR_ERR(rinfo); + return ret; + } + + rbuf = &((struct ring_buffer *)(TO_APPS_ADDR( + rinfo->rbuf)))[rinfo->widx]; + rbuf->msg_hdr = msg_hdr; + rbuf->len = len; + + if (len > IPC_MSG_PLD_SZ) { + rbuf->msg_hdr = rbuf->msg_hdr | IPC_LMSG_MASK; + + aux_ptr.len = 0; + aux_ptr.buf = 0; + + lmsg_data = bt_ipc_alloc_lmsg(btDesc, len, + &aux_ptr, &is_lbuf_full); + + if (IS_ERR(lmsg_data)) { + printf("long msg buf full, queuing msg[%d]\n", + atomic_read(&btDesc->ipc.tx_q_cnt)); + ret = PTR_ERR(lmsg_data); + return ret; + } + + memcpy_toio(lmsg_data, pData, + (len - aux_ptr.len)); + + if (aux_ptr.buf) { + memcpy_toio(TO_APPS_ADDR(aux_ptr.buf), + (pData + (len - aux_ptr.len)), aux_ptr.len); + } + + rbuf->payload.lmsg_data = TO_BT_ADDR(lmsg_data); + } else { + memcpy_toio(rbuf->payload.smsg_data, pData, len); + } + + if (is_sbuf_full || is_lbuf_full) + rbuf->msg_hdr = rbuf->msg_hdr | IPC_RACK_MASK; + + rinfo->widx = (rinfo->widx + 1) % btmem->tx_ctxt->smsg_buf_cnt; + + writel( BIT(btDesc->ipc.bit), btDesc->ipc.regmap+ btDesc->ipc.offset); + + return ret; +} + +static +void bt_ipc_free_lmsg(struct bt_descriptor *btDesc, uint32_t lmsg, uint16_t len) +{ + uint8_t idx; + uint8_t blks; + struct bt_mem *btmem = &btDesc->btmem; + uint32_t lsz = IPC_LBUF_SZ(btmem->tx_ctxt, TotalMemorySize, lring_buf, + lmsg_buf_cnt); + + idx = GET_TX_INDEX_FROM_BUF(lmsg, lsz); + + if (idx != btmem->lmsg_ctxt.ridx) + return; + + blks = GET_NO_OF_BLOCKS(len, lsz); + + btmem->lmsg_ctxt.ridx = (btmem->lmsg_ctxt.ridx + blks) % + btmem->tx_ctxt->lmsg_buf_cnt; + + btmem->lmsg_ctxt.lmsg_free_cnt += blks; +} + +static void bt_ipc_cust_msg(struct bt_descriptor *btDesc, uint8_t msgid) +{ + struct bt_mem *btmem = &btDesc->btmem; + uint16_t msg_hdr = 0; + int ret; + + msg_hdr |= msgid; + + switch (msgid) { + case IPC_CMD_IPC_STOP: + printf("BT IPC Stopped, gracefully stopping APSS IPC\n"); + break; + case IPC_CMD_SWITCH_TO_UART: + printf("Configured UART, Swithing BT to debug mode\n"); + break; + case IPC_CMD_PREPARE_DUMP: + printf("IPQ crashed, inform BT to prepare dump\n"); + break; + case IPC_CMD_COLLECT_DUMP: + printf("BT Crashed, gracefully stopping IPC\n"); + return; + case IPC_CMD_IPC_START: + btmem->tx_ctxt = (struct context_info *)((void *) + btmem->rx_ctxt + btmem->rx_ctxt->TotalMemorySize); + btmem->lmsg_ctxt.widx = 0; + btmem->lmsg_ctxt.ridx = 0; + btmem->lmsg_ctxt.smsg_free_cnt = btmem->tx_ctxt->smsg_buf_cnt; + btmem->lmsg_ctxt.lmsg_free_cnt = btmem->tx_ctxt->lmsg_buf_cnt; + atomic_set(&btDesc->state, 1); + + printf("BT IPC Started, starting APSS IPC\n"); + return; + default: + printf("invalid custom message\n"); + return; + } + + if (unlikely(!atomic_read(&btDesc->state))) { + printf("BT IPC not initialized, no message sent\n"); + return; + } + + atomic_set(&btDesc->state, 0); + + ret = bt_ipc_send_msg(btDesc, msg_hdr, NULL, 0, true); + if (ret) + printf("err: sending message\n"); +} + +static bool bt_ipc_process_peer_msgs(struct bt_descriptor *btDesc, + struct ring_buffer_info *rinfo, uint8_t *pRxMsgCount) +{ + struct bt_mem *btmem = &btDesc->btmem; + struct ring_buffer *rbuf; + uint8_t ridx, lbuf_idx; + uint8_t blks_consumed; + struct ipc_aux_ptr aux_ptr; + enum ipc_pkt_type pktType = IPC_CUST_PKT; + bool ackReqd = false; + uint8_t *rxbuf = NULL; + uint32_t lsz = IPC_LBUF_SZ(btmem->rx_ctxt, TotalMemorySize, lring_buf, + lmsg_buf_cnt); + + ridx = rinfo->ridx; + + rbuf = &((struct ring_buffer *)(TO_APPS_ADDR( + btmem->rx_ctxt->sring_buf_info.rbuf)))[ridx]; + + while (ridx != rinfo->widx) { + memset(&aux_ptr, 0, sizeof(struct ipc_aux_ptr)); + + rbuf = &((struct ring_buffer *)(TO_APPS_ADDR( + btmem->rx_ctxt->sring_buf_info.rbuf)))[ridx]; + + if (IS_LONG_MSG(rbuf->msg_hdr)) { + rxbuf = TO_APPS_ADDR(rbuf->payload.lmsg_data); + + if (IS_RX_MEM_NON_CONTIGIOUS(rbuf->payload.lmsg_data, + rbuf->len, lsz)) { + + lbuf_idx = GET_RX_INDEX_FROM_BUF( + rbuf->payload.lmsg_data, lsz); + + blks_consumed = btmem->rx_ctxt->lmsg_buf_cnt - + lbuf_idx; + aux_ptr.len = rbuf->len - (blks_consumed * lsz); + aux_ptr.buf = btmem->rx_ctxt->lring_buf; + } + } else { + rxbuf = rbuf->payload.smsg_data; + } + + if (IS_REQ_ACK(rbuf->msg_hdr)) + ackReqd = true; + + pktType = IPC_GET_PKT_TYPE(rbuf->msg_hdr); + + switch (pktType) { + case IPC_HCI_PKT: + btDesc->buf = kzalloc(rbuf->len, GFP_ATOMIC); + if (!btDesc->buf) + return -ENOMEM; + + memcpy_fromio(btDesc->buf, rxbuf, (rbuf->len - aux_ptr.len)); + + if (aux_ptr.buf) + memcpy_fromio(btDesc->buf + (rbuf->len - aux_ptr.len), + TO_APPS_ADDR(aux_ptr.buf), aux_ptr.len); + + btDesc->len = rbuf->len; + atomic_set(&btDesc->tx_in_progress, 0); + break; + case IPC_CUST_PKT: + bt_ipc_cust_msg(btDesc, IPC_GET_MSG_ID(rbuf->msg_hdr)); + break; + case IPC_AUDIO_PKT: + break; + default: + break; + } + + ridx = (ridx + 1) % rinfo->ring_buf_cnt; + } + + rinfo->ridx = ridx; + + return ackReqd; +} + +static void bt_ipc_process_ack(struct bt_descriptor *btDesc) +{ + struct ring_buffer_info *rinfo; + struct bt_mem *btmem = &btDesc->btmem; + + for (rinfo = &btmem->tx_ctxt->sring_buf_info; rinfo != NULL; + rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) { + uint8_t tidx = rinfo->tidx; + struct ring_buffer *rbuf = (struct ring_buffer *) + TO_APPS_ADDR(rinfo->rbuf); + + while (tidx != rinfo->ridx) { + if (IS_LONG_MSG(rbuf[tidx].msg_hdr)) { + bt_ipc_free_lmsg(btDesc, + rbuf[tidx].payload.lmsg_data, + rbuf[tidx].len); + } + + tidx = (tidx + 1) % btmem->tx_ctxt->smsg_buf_cnt; + btmem->lmsg_ctxt.smsg_free_cnt++; + } + + rinfo->tidx = tidx; + } +} + +int bt_ipc_sendmsg(struct bt_descriptor *btDesc, unsigned char *buf, int len) +{ + int ret; + uint16_t msg_hdr = 0x100; + + if (unlikely(!atomic_read(&btDesc->state))) { + printf("BT IPC not initialized, no message sent\n"); + return -ENODEV; + } + + atomic_set(&btDesc->tx_in_progress, 1); + ret = bt_ipc_send_msg(btDesc, msg_hdr, (uint8_t *)buf, (uint16_t)len, + true); + if (ret) + printf("err: sending message\n"); + + return ret; +} + +void bt_ipc_worker(struct bt_descriptor *btDesc) +{ + struct ring_buffer_info *rinfo; + + struct bt_mem *btmem = &btDesc->btmem; + bool ackReqd = false; + + if (unlikely(!atomic_read(&btDesc->state))) { + btmem->rx_ctxt = (struct context_info *)(btmem->virt + 0xe000); + if (btmem->rx_ctxt->sring_buf_info.widx != 0x1) + return; + } + else + bt_ipc_process_ack(btDesc); + + for (rinfo = &(btmem->rx_ctxt->sring_buf_info); rinfo != NULL; + rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) { + if (bt_ipc_process_peer_msgs(btDesc, rinfo, + &btmem->rx_ctxt->smsg_buf_cnt)) { + ackReqd = true; + } + } + + if (ackReqd) { + writel( BIT(btDesc->ipc.bit), btDesc->ipc.regmap+ btDesc->ipc.offset); + } + +} + +void bt_ipc_init(struct bt_descriptor *btDesc) +{ + struct bt_mem *btmem; + + btmem = &btDesc->btmem; + btmem->phys = BT_RAM_START; + btmem->reloc = BT_RAM_START; + btmem->size = BT_RAM_SIZE; + btmem->virt = (void __iomem *)BT_RAM_START; + + btDesc->ipc.regmap = SYSCON; + btDesc->ipc.offset = 8; + btDesc->ipc.bit = 23; + atomic_set(&btDesc->state, 0); + atomic_set(&btDesc->tx_in_progress, 0); +} From 2343ff363f07f41686f6858008f6db9b89e33c18 Mon Sep 17 00:00:00 2001 From: Rajkumar Ayyasamy Date: Mon, 9 Nov 2020 16:12:39 +0530 Subject: [PATCH 2/2] ipq5018: Add BT beaconing support Signed-off-by: Rajkumar Ayyasamy Change-Id: Ibe8dc716d0f4bafbbf1ec7f3b776bb4a6f891f45 --- arch/arm/cpu/armv7/qca/common/scm.c | 79 +++++++++ arch/arm/include/asm/arch-ipq5018/clk.h | 9 +- arch/arm/include/asm/arch-qca-common/scm.h | 11 ++ board/qca/arm/ipq5018/clock.c | 6 + board/qca/arm/ipq5018/ipq5018.c | 183 +++++++++++++++++++++ board/qca/arm/ipq5018/ipq5018.h | 52 ++++++ include/configs/ipq5018.h | 2 + 7 files changed, 341 insertions(+), 1 deletion(-) diff --git a/arch/arm/cpu/armv7/qca/common/scm.c b/arch/arm/cpu/armv7/qca/common/scm.c index e3c06a347a..5fbe66850e 100644 --- a/arch/arm/cpu/armv7/qca/common/scm.c +++ b/arch/arm/cpu/armv7/qca/common/scm.c @@ -550,6 +550,85 @@ int qca_scm_secure_authenticate(void *cmd_buf, size_t cmd_len) return ret; } + +#ifdef CONFIG_IPQ_BT_SUPPORT +int qti_scm_otp(u32 peripheral) +{ + int ret; + + if (is_scm_armv8()) + { + struct qca_scm_desc desc = {0}; + + desc.arginfo = QCA_SCM_ARGS(1); + desc.args[0] = peripheral; + + ret = scm_call_64(SCM_SVC_PIL, SCM_CMD_OTP, &desc); + } + else + { + u32 cmd = peripheral; + + ret = scm_call(SCM_SVC_PIL, SCM_CMD_OTP, &cmd, sizeof(cmd), + NULL, 0); + } + + return ret; +} + +int qti_scm_pas_init_image(u32 peripheral, u32 addr) +{ + int ret; + + if (is_scm_armv8()) + { + struct qca_scm_desc desc = {0}; + + desc.arginfo = QCA_SCM_ARGS(2, SCM_VAL, SCM_IO_WRITE); + desc.args[0] = peripheral; + desc.args[1] = addr; + + ret = scm_call_64(SCM_SVC_PIL, SCM_PAS_INIT_IMAGE_CMD, &desc); + } + else + { + struct { + u32 proc; + u32 image_addr; + } request; + request.proc = peripheral; + request.image_addr = addr; + ret = scm_call(SCM_SVC_PIL, SCM_PAS_INIT_IMAGE_CMD, &request, + sizeof(request), NULL, 0); + } + + return ret; +} + +int qti_pas_and_auth_reset(u32 peripheral) +{ + int ret; + u32 cmd = peripheral; + + if (is_scm_armv8()) + { + struct qca_scm_desc desc = {0}; + + desc.arginfo = QCA_SCM_ARGS(1); + desc.args[0] = peripheral; + + ret = scm_call_64(SCM_SVC_PIL, SCM_PAS_AUTH_AND_RESET_CMD, &desc); + } + else + { + ret = scm_call(SCM_SVC_PIL, SCM_PAS_AUTH_AND_RESET_CMD, &cmd, sizeof(cmd), + NULL, 0); + } + + return ret; +} +#endif + #else int qca_scm_call(u32 svc_id, u32 cmd_id, void *buf, size_t len) { diff --git a/arch/arm/include/asm/arch-ipq5018/clk.h b/arch/arm/include/asm/arch-ipq5018/clk.h index 6c8769668d..4e553d4900 100644 --- a/arch/arm/include/asm/arch-ipq5018/clk.h +++ b/arch/arm/include/asm/arch-ipq5018/clk.h @@ -14,6 +14,7 @@ #ifndef IPQ5018_CLK_H #define IPQ5018_CLK_H +#define CLK_ENABLE 0x1 /* I2C clocks configuration */ #ifdef CONFIG_IPQ5018_I2C @@ -26,8 +27,14 @@ #define CMD_UPDATE 0x1 #define ROOT_EN 0x2 -#define CLK_ENABLE 0x1 + void i2c_clock_config(void); #endif + +#ifdef CONFIG_IPQ_BT_SUPPORT +#define GCC_BTSS_LPO_CBCR 0x181C004 +void enable_btss_lpo_clk(void); +#endif + #endif /*IPQ5018_CLK_H*/ diff --git a/arch/arm/include/asm/arch-qca-common/scm.h b/arch/arm/include/asm/arch-qca-common/scm.h index a31cac4584..fa7024e1d0 100644 --- a/arch/arm/include/asm/arch-qca-common/scm.h +++ b/arch/arm/include/asm/arch-qca-common/scm.h @@ -34,6 +34,12 @@ #define SCM_SVC_ID_SHIFT 0xA #define IS_CALL_AVAIL_CMD 0x1 +#ifdef CONFIG_IPQ_BT_SUPPORT +#define SCM_PAS_INIT_IMAGE_CMD 0x1 +#define SCM_PAS_AUTH_AND_RESET_CMD 0x5 +#define SCM_CMD_OTP 0x15 +#endif + /* scm_v8 */ #define SCM_VAL 0x0 #define SCM_IO_READ 0x1 @@ -130,6 +136,11 @@ int is_scm_sec_auth_available(u32 svc_id, u32 cmd_id); #ifdef CONFIG_IPQ_TZT int qca_scm(u32 svc_id, u32 cmd_id, u32 ownr_id, u32 *addr, u32 len); #endif +#ifdef CONFIG_IPQ_BT_SUPPORT +int qti_scm_otp(u32 peripheral); +int qti_scm_pas_init_image(u32 peripheral, u32 addr); +int qti_pas_and_auth_reset(u32 peripheral); +#endif #define MAX_QCA_SCM_RETS 3 #define MAX_QCA_SCM_ARGS 10 #define SCM_READ_OP 1 diff --git a/board/qca/arm/ipq5018/clock.c b/board/qca/arm/ipq5018/clock.c index 7dcaf2d040..247dc798f6 100644 --- a/board/qca/arm/ipq5018/clock.c +++ b/board/qca/arm/ipq5018/clock.c @@ -35,3 +35,9 @@ void i2c_clock_config(void) } #endif +#ifdef CONFIG_IPQ_BT_SUPPORT +void enable_btss_lpo_clk(void) +{ + writel(CLK_ENABLE, GCC_BTSS_LPO_CBCR); +} +#endif diff --git a/board/qca/arm/ipq5018/ipq5018.c b/board/qca/arm/ipq5018/ipq5018.c index 0ef2de738b..e3b061486c 100644 --- a/board/qca/arm/ipq5018/ipq5018.c +++ b/board/qca/arm/ipq5018/ipq5018.c @@ -32,6 +32,12 @@ #ifdef CONFIG_QPIC_NAND #include #endif +#ifdef CONFIG_IPQ_BT_SUPPORT +#include +#include "bt.h" +#include "bt_binary_array.h" +#include +#endif #define DLOAD_MAGIC_COOKIE 0x10 @@ -477,6 +483,180 @@ void fdt_fixup_auto_restart(void *blob) return; } +#ifdef CONFIG_IPQ_BT_SUPPORT +unsigned char hci_reset[] = +{0x01, 0x03, 0x0c, 0x00}; + +unsigned char adv_data[] = +{0x01, 0X08, 0X20, 0X20, 0X1F, 0X0A, 0X09, 0X71, + 0X75, 0X61, 0X6c, 0X63, 0X6f, 0X6d, 0X6d, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00}; + +unsigned char set_interval[] = +{0X01, 0X06, 0X20, 0X0F, 0X20, 0X00, 0X20, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X07, 0X00}; + +unsigned char start_beacon[] = +{0x01, 0x0A, 0x20, 0x01, 0x01}; + +unsigned char *hci_cmds[] = { + hci_reset, + adv_data, + set_interval, + start_beacon +}; + +int wait_for_bt_event(struct bt_descriptor *btDesc, u8 bt_wait) +{ + int val, timeout = 0; + + do{ + udelay(10); + bt_ipc_worker(btDesc); + + if (bt_wait == BT_WAIT_FOR_START) + val = !atomic_read(&btDesc->state); + else + val = atomic_read(&btDesc->tx_in_progress); + + if (timeout++ >= BT_TIMEOUT_US/10) { + printf(" %s timed out \n", __func__); + return -ETIMEDOUT; + } + + } while (val); + + return 0; +} + +static int initialize_nvm(struct bt_descriptor *btDesc, + void *fileaddr, u32 filesize) +{ + unsigned char *buffer = fileaddr; + int bytes_read = 0, bytes_consumed = 0, ret; + HCI_Packet_t *hci_packet = NULL; + + while(bytes_consumed < filesize ) + { + bytes_read = (filesize - bytes_consumed) > NVM_SEGMENT_SIZE ? + NVM_SEGMENT_SIZE : filesize - bytes_consumed; + /* Constructing a HCI Packet to write NVM Segments to BTSS */ + hci_packet = (HCI_Packet_t*)malloc(sizeof(HCI_Packet_t) + + NVM_SEGMENT_SIZE); + + if(!hci_packet) + { + printf("Cannot allocate memory to HCI Packet \n"); + return -ENOMEM; + } + + /* Initializing HCI Packet Header */ + hci_packet->HCIPacketType = ptHCICommandPacket; + + /* Populating TLV Request Packet in HCI */ + LE_UNALIGNED(&(hci_packet->HCIPayload.opcode), TLV_REQ_OPCODE); + LE_UNALIGNED(&(hci_packet->HCIPayload.parameter_total_length), + (bytes_read + DATA_REMAINING_LENGTH)); + hci_packet->HCIPayload.command_request = TLV_COMMAND_REQUEST; + hci_packet->HCIPayload.tlv_segment_length = bytes_read; + memcpy(hci_packet->HCIPayload.tlv_segment_data, buffer, + bytes_read); + + bt_ipc_sendmsg(btDesc, (u8*)hci_packet, + sizeof(HCI_Packet_t) + bytes_read); + + free(hci_packet); + bytes_consumed += bytes_read; + buffer = fileaddr + bytes_consumed; + + ret = wait_for_bt_event(btDesc, BT_WAIT_FOR_TX_COMPLETE); + if(ret || *((u8 *)btDesc->buf + TLV_RESPONSE_STATUS_INDEX) != 0) + { + printf( "\n NVM download failed\n"); + if (!ret) { + kfree(btDesc->buf); + btDesc->buf = NULL; + } + return -EINVAL; + } + kfree(btDesc->buf); + btDesc->buf = NULL; + } + + printf("NVM download successful \n"); + bt_ipc_worker(btDesc); + return 0; +} + +int send_bt_hci_cmds(struct bt_descriptor *btDesc) +{ + int ret, i; + int count = sizeof hci_cmds/ sizeof(unsigned char *); + + for (i = 0; i < count; i++) { + bt_ipc_sendmsg(btDesc, hci_cmds[i], sizeof hci_cmds[i]); + ret = wait_for_bt_event(btDesc, BT_WAIT_FOR_TX_COMPLETE); + if (ret) + return ret; + + /*btDesc->buf will have response data with length btDesc->len*/ + kfree(btDesc->buf); + btDesc->buf = NULL; + } + return 0; +} + +int bt_init(void) +{ + struct bt_descriptor *btDesc; + int ret; + + btDesc = kzalloc(sizeof(*btDesc), GFP_KERNEL); + if (!btDesc) + return -ENOMEM; + + bt_ipc_init(btDesc); + + enable_btss_lpo_clk(); + ret = qti_scm_pas_init_image(PAS_ID, (u32)bt_fw_patchmdt); + if (ret) { + printf("patch auth failed\n"); + return ret; + } + + printf("patch authenticated successfully\n"); + + memcpy((void*)BT_RAM_PATCH, (void*)bt_fw_patchb02, + sizeof bt_fw_patchb02); + + ret = qti_pas_and_auth_reset(PAS_ID); + + if (ret) { + printf("BT out of reset failed\n"); + return ret; + } + + ret = wait_for_bt_event(btDesc, BT_WAIT_FOR_START); + if (ret) + return ret; + + ret = initialize_nvm(btDesc, (void*)mpnv10bin, sizeof mpnv10bin); + if (ret) + return ret; + + ret = wait_for_bt_event(btDesc, BT_WAIT_FOR_START); + if (ret) + return ret; + + send_bt_hci_cmds(btDesc); + + return 0; +} +#endif + void reset_crashdump(void) { unsigned int ret = 0; @@ -588,6 +768,9 @@ void board_nand_init(void) } #endif +#ifdef CONFIG_IPQ_BT_SUPPORT + bt_init(); +#endif #ifdef CONFIG_QCA_SPI int gpio_node; gpio_node = fdt_path_offset(gd->fdt_blob, "/spi/spi_gpio"); diff --git a/board/qca/arm/ipq5018/ipq5018.h b/board/qca/arm/ipq5018/ipq5018.h index 3e0964864f..1a73ceca83 100644 --- a/board/qca/arm/ipq5018/ipq5018.h +++ b/board/qca/arm/ipq5018/ipq5018.h @@ -588,4 +588,56 @@ typedef enum { SMEM_MAX_SIZE = SMEM_SPI_FLASH_ADDR_LEN + 1, } smem_mem_type_t; +#ifdef CONFIG_IPQ_BT_SUPPORT +#define NVM_SEGMENT_SIZE 243 +#define TLV_REQ_OPCODE 0xFC00 +#define TLV_COMMAND_REQUEST 0x1E +#define DATA_REMAINING_LENGTH 2 +#define TLV_RESPONSE_PACKET_SIZE 8 +#define TLV_RESPONSE_STATUS_INDEX 6 + +#define PACKED_STRUCT __attribute__((__packed__)) + +#define LE_UNALIGNED(x, y) \ +{ \ + ((u8 *)(x))[0] = ((u8)(((u32)(y)) & 0xFF)); \ + ((u8 *)(x))[1] = ((u8)((((u32)(y)) >> 8) & 0xFF)); \ +} + +typedef enum +{ + ptHCICommandPacket = 0x01, /* Simple HCI Command Packet */ + ptHCIACLDataPacket = 0x02, /* HCI ACL Data Packet Type. */ + ptHCISCODataPacket = 0x03, /* HCI SCO Data Packet Type. */ + ptHCIeSCODataPacket= 0x03, /* HCI eSCO Data Packet Type. */ + ptHCIEventPacket = 0x04, /* HCI Event Packet Type. */ + ptHCIAdditional = 0x05 /* Starting Point for Additional*/ +} HCI_PacketType_t; + +typedef struct _tlv_download_req +{ + u16 opcode; + u8 parameter_total_length; + u8 command_request; + u8 tlv_segment_length; + u8 tlv_segment_data[0]; + +} PACKED_STRUCT tlv_download_req; + +typedef struct _tagHCI_Packet_t +{ + u8 HCIPacketType; + tlv_download_req HCIPayload; +} PACKED_STRUCT HCI_Packet_t; + +typedef enum { + BT_WAIT_FOR_START = 0, + BT_WAIT_FOR_TX_COMPLETE = 1, + BT_WAIT_FOR_STOP = 2, +} bt_wait; + +#define BT_TIMEOUT_US 50000 + +int bt_init(void); +#endif #endif /* _IPQ5018_CDP_H_ */ diff --git a/include/configs/ipq5018.h b/include/configs/ipq5018.h index eac8fcad99..bece713695 100644 --- a/include/configs/ipq5018.h +++ b/include/configs/ipq5018.h @@ -425,4 +425,6 @@ extern loff_t board_env_size; #define INTERNAL_96MHZ +/*#define CONFIG_IPQ_BT_SUPPORT*/ + #endif /* _IPQ5018_H */