mirror of
https://git.codelinaro.org/clo/qsdk/oss/boot/u-boot-2016.git
synced 2025-12-10 07:44:53 +01:00
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 <arajkuma@codeaurora.org> Change-Id: If52b52ebe8774063f52f7b597991fbb562526db6
396 lines
9.8 KiB
C
396 lines
9.8 KiB
C
/*
|
|
* 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 <linux/io.h>
|
|
#include <asm/atomic.h>
|
|
#include <asm-generic/atomic-long.h>
|
|
|
|
#include <linux/ctype.h>
|
|
#include "bt.h"
|
|
|
|
#include <common.h>
|
|
#include <memalign.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
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);
|
|
}
|