Files
kernel_common_drivers/drivers/host/host_poll.c
T
bangzheng.liu 03cdeebe7b DSP: SC2 far field voice early suspend with dsp [2/3]
PD#SWPL-139800

Problem:
SC2 far field voice need early suspend and record
with dsp.

Solution:
When DSP waked up by VAD, then:
1. DSP notify to AOCPU (would fail by unknown cmd) and ARM
during early suspend.
2. DSP notify to AOCPU (would fail by unknown cmd) and ARM
(would fail by timeout) and do retry after ARM deep sleep and
before AOCPU STR poweroff.
3. DSP notify to AOCPU after AOCPU STR poweroff.
4. when ffv not supported, dsp do not start vwe.

Verify:
sc2_ah212

Change-Id: I0e610ab7dd76c362c71a9ec98ce589bf7d04beda
Signed-off-by: bangzheng.liu <bangzheng.liu@amlogic.com>
2024-07-11 22:05:15 -07:00

228 lines
6.0 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
//#define DEBUG
#include "host.h"
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/kdebug.h>
#include <linux/notifier.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/of.h>
#include <linux/mailbox_client.h>
#include <linux/amlogic/aml_mbox.h>
#include <dt-bindings/firmware/amlogic,firmware.h>
#include "sysfs.h"
#include "host_poll.h"
struct ring_buffer {
unsigned int magic;
unsigned int basepaddr;
unsigned int size;
unsigned int head; /*buffer head: move forward only when tail reaches head*/
unsigned int headr; /*read position: move forward only when buffer is read/printed*/
unsigned int tail; /*write position:move forward only when writing to buffer*/
char buffer[4];
};
#define DSP_LOGBUFF_MAGIC 0x1234ABCD
#define BUFF_LEN 256
static unsigned int health_polling_ms = 1000;
static int logbuff_polling_ms = 50;
struct ring_buffer *ring_buffer;
module_param(health_polling_ms, uint, 0644);
module_param(logbuff_polling_ms, uint, 0644);
static bool host_health_hang_check(struct host_module *host)
{
u32 cur_cnt = 0;
bool ret = false;
host->hang = 0;
cur_cnt = readl(host->health_reg);
pr_debug("[%s][%s][%u %u]\n", __func__,
host->host_data->name,
host->pre_cnt, cur_cnt);
if (cur_cnt == host->pre_cnt) {
host->hang = 1;
ret = true;
}
host->pre_cnt = cur_cnt;
host->cur_cnt = cur_cnt;
return ret;
}
static void host_health_monitor(struct work_struct *work)
{
char data[20], *envp[] = { data, NULL };
struct host_module *host_firm = container_of((struct delayed_work *)work,
struct host_module, host_monitor_work);
if (host_health_hang_check(host_firm)) {
snprintf(data, sizeof(data), "ACTION=%s_WTD", host_firm->host_data->name);
kobject_uevent_env(&host_firm->dev->kobj, KOBJ_CHANGE,
envp);
pr_debug("[%s][%s_HANG]\n", __func__, host_firm->host_data->name);
}
queue_delayed_work(host_firm->host_wq, &host_firm->host_monitor_work,
msecs_to_jiffies(health_polling_ms));
}
void host_health_monitor_start(struct host_module *host)
{
host->pre_cnt = 0;
host->cur_cnt = 0;
if (IS_ERR_OR_NULL(host->health_reg))
host->health_reg = NULL;
if (host->health_reg) {
host->host_wq = create_workqueue("host_wq");
if (host->health_polling_ms)
health_polling_ms = host->health_polling_ms;
INIT_DEFERRABLE_WORK(&host->host_monitor_work, host_health_monitor);
queue_delayed_work(host->host_wq, &host->host_monitor_work,
msecs_to_jiffies(health_polling_ms));
}
}
void host_health_monitor_stop(struct host_module *host)
{
if (!host->host_wq)
return;
dev_dbg(host->dev, "[%s %d]\n", __func__, __LINE__);
cancel_delayed_work_sync(&host->host_monitor_work);
flush_workqueue(host->host_wq);
destroy_workqueue(host->host_wq);
host->host_wq = NULL;
}
static u32 get_buff_len(struct ring_buffer *rbuf)
{
if (rbuf->tail < rbuf->headr)
return rbuf->tail + rbuf->size - rbuf->headr;
else
return rbuf->tail - rbuf->headr;
}
static u32 host_get_logbuff(struct ring_buffer *rbuf, char *name)
{
char buff[BUFF_LEN + 1] = {0};
u32 idx = 0;
char *ringbuff;
u32 len = 0;
len = get_buff_len(ring_buffer);
if (!len)
return 0;
if (len > BUFF_LEN)
len = BUFF_LEN;
ringbuff = rbuf->buffer;
for (idx = 0; idx < len; idx++) {
buff[idx] = *(ringbuff + rbuf->headr);
rbuf->headr += 1;
rbuf->headr %= rbuf->size;
}
buff[len] = '\0';
pr_info("[%s-%u]%s", name, len, buff);
return len;
}
static int die_notify(struct notifier_block *nb,
unsigned long cmd, void *data)
{
return NOTIFY_DONE;
}
static void host_polling_logbuff(struct work_struct *work)
{
struct host_module *host_firm = container_of((struct delayed_work *)work,
struct host_module, host_logbuff_work);
if (IS_ERR_OR_NULL(ring_buffer))
return;
host_get_logbuff(ring_buffer, host_firm->host_data->name);
queue_delayed_work(host_firm->host_logbuff_wq, &host_firm->host_logbuff_work,
msecs_to_jiffies(logbuff_polling_ms));
}
static void host_polling_logbuff_start(struct host_module *host)
{
if (host->logbuff_polling_ms)
host->host_logbuff_wq = create_workqueue("host_logbuff_wq");
INIT_DEFERRABLE_WORK(&host->host_logbuff_work, host_polling_logbuff);
queue_delayed_work(host->host_logbuff_wq, &host->host_logbuff_work,
msecs_to_jiffies(logbuff_polling_ms));
}
int host_logbuff_start(struct host_module *host)
{
struct ring_buffer rbuf;
char data[] = "AP request logbuff";
int ret;
if (!(host->logbuff_polling_ms))
return -EINVAL;
// wait for remote mcu boot up
msleep(100);
ret = aml_mbox_transfer_data(host->mbox_chan_to_dev, MBOX_CMD_HIFI5_SYSLOG_START,
data, sizeof(data), &rbuf, sizeof(rbuf),
MBOX_SYNC);
if (ret < 0) {
dev_err(host->dev, "mbox transfer data error %d\n", ret);
return ret;
}
if (rbuf.magic == DSP_LOGBUFF_MAGIC) {
if (host->start_pos == DDR_SRAM && host->addr_remap)
rbuf.basepaddr = (rbuf.basepaddr & 0x000fffff) | host->phys_remap_addr;
if (pfn_valid(__phys_to_pfn(rbuf.basepaddr)))
ring_buffer = (struct ring_buffer *)__phys_to_virt(rbuf.basepaddr);
else
ring_buffer = (struct ring_buffer *)ioremap_cache(rbuf.basepaddr,
rbuf.size + sizeof(rbuf) - 4);
pr_debug("[%s %d][0x%x 0x%x %u %u %u %u]\n", __func__, __LINE__,
ring_buffer->magic, ring_buffer->basepaddr,
ring_buffer->head, ring_buffer->headr,
ring_buffer->tail, ring_buffer->size);
if (host->logbuff_polling_ms)
logbuff_polling_ms = host->logbuff_polling_ms;
host_polling_logbuff_start(host);
host->nb.notifier_call = die_notify;
register_die_notifier(&host->nb);
} else {
ret = -EINVAL;
ring_buffer = NULL;
}
return ret;
}
void host_logbuff_stop(struct host_module *host)
{
if (IS_ERR_OR_NULL(ring_buffer))
return;
if (!host->host_logbuff_wq)
return;
pr_debug("[%s %d]\n", __func__, __LINE__);
cancel_delayed_work_sync(&host->host_logbuff_work);
flush_workqueue(host->host_logbuff_wq);
unregister_die_notifier(&host->nb);
destroy_workqueue(host->host_logbuff_wq);
host->host_logbuff_wq = NULL;
}