kernel_samsung_a53x/drivers/vision3/dsp/hardware/P0/dsp-hw-p0-interface.c
2024-06-15 16:02:09 -03:00

332 lines
7.5 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0
/*
* Samsung Exynos SoC series dsp driver
*
* Copyright (c) 2019 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*/
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <dt-bindings/soc/samsung/exynos-dsp.h>
#include "dsp-log.h"
#include "dsp-dump.h"
#include "dsp-hw-common-init.h"
#include "dsp-hw-p0-system.h"
#include "dsp-hw-p0-memory.h"
#include "dsp-hw-p0-ctrl.h"
#include "dsp-hw-p0-dump.h"
#include "dsp-hw-p0-interface.h"
#define DSP_ISR_TIMER_REPEAT_TIME (1)
enum dsp_p0_irq_id {
DSP_P0_IRQ_MBOX_NS,
DSP_P0_IRQ_COUNT,
};
struct dsp_hw_p0_interface {
int irq[DSP_P0_IRQ_COUNT];
};
static void __dsp_hw_p0_interface_isr(struct dsp_interface *itf);
static int dsp_hw_p0_interface_send_irq(struct dsp_interface *itf, int status)
{
int ret;
dsp_enter();
if (status & BIT(DSP_TO_CC_INT_RESET)) {
dsp_ctrl_writel(DSP_P0_DNCC_NS_IRQ_SET_TO_CC, 0x1 << 0);
} else if (status & BIT(DSP_TO_CC_INT_MAILBOX)) {
dsp_ctrl_writel(DSP_P0_DNCC_NS_IRQ_SET_TO_CC, 0x1 << 1);
} else {
dsp_err("wrong interrupt requested(%x)\n", status);
ret = -EINVAL;
goto p_err;
}
dsp_leave();
return 0;
p_err:
return ret;
}
static int dsp_hw_p0_interface_check_irq(struct dsp_interface *itf)
{
unsigned int status;
unsigned long flags;
dsp_enter();
spin_lock_irqsave(&itf->irq_lock, flags);
status = dsp_ctrl_readl(DSP_P0_DNCC_NS_MBOX_FR_CC_TO_HOST_INTR);
__dsp_hw_p0_interface_isr(itf);
if (!status)
goto p_end;
dsp_ctrl_writel(DSP_P0_DNCC_NS_MBOX_FR_CC_TO_HOST_INTR, 0x0);
dsp_leave();
p_end:
spin_unlock_irqrestore(&itf->irq_lock, flags);
return 0;
}
static void __dsp_hw_p0_interface_isr_boot_done(struct dsp_interface *itf)
{
dsp_enter();
if (!(itf->sys->system_flag & BIT(DSP_SYSTEM_BOOT))) {
itf->sys->system_flag = BIT(DSP_SYSTEM_BOOT);
wake_up(&itf->sys->system_wq);
} else {
dsp_warn("boot_done interrupt occurred incorrectly\n");
}
dsp_leave();
}
static void __dsp_hw_p0_interface_isr_reset_done(struct dsp_interface *itf)
{
dsp_enter();
if (!(itf->sys->system_flag & BIT(DSP_SYSTEM_RESET))) {
itf->sys->system_flag = BIT(DSP_SYSTEM_RESET);
wake_up(&itf->sys->system_wq);
} else {
dsp_warn("reset_done interrupt occurred incorrectly\n");
}
dsp_leave();
}
static void __dsp_hw_p0_interface_isr_reset_request(struct dsp_interface *itf)
{
dsp_enter();
dsp_info("reset request\n");
dsp_leave();
}
static void __dsp_hw_p0_interface_isr_mailbox(struct dsp_interface *itf)
{
dsp_enter();
itf->sys->mailbox.ops->receive_task(&itf->sys->mailbox);
dsp_leave();
}
static void __dsp_hw_p0_interface_isr(struct dsp_interface *itf)
{
unsigned int status;
dsp_enter();
status = dsp_ctrl_dhcp_readl(
DSP_P0_DHCP_IDX(DSP_P0_DHCP_TO_HOST_INT_STATUS));
if (status & BIT(DSP_TO_HOST_INT_BOOT)) {
__dsp_hw_p0_interface_isr_boot_done(itf);
} else if (status & BIT(DSP_TO_HOST_INT_MAILBOX)) {
__dsp_hw_p0_interface_isr_mailbox(itf);
} else if (status & BIT(DSP_TO_HOST_INT_RESET_DONE)) {
__dsp_hw_p0_interface_isr_reset_done(itf);
} else if (status & BIT(DSP_TO_HOST_INT_RESET_REQUEST)) {
__dsp_hw_p0_interface_isr_reset_request(itf);
} else {
dsp_dbg("interrupt status is invalid (%u)\n", status);
return;
}
dsp_ctrl_dhcp_writel(
DSP_P0_DHCP_IDX(DSP_P0_DHCP_TO_HOST_INT_STATUS), 0);
dsp_leave();
}
static irqreturn_t dsp_hw_p0_interface_isr1(int irq, void *data)
{
struct dsp_interface *itf;
unsigned int status;
unsigned long flags;
dsp_enter();
itf = (struct dsp_interface *)data;
spin_lock_irqsave(&itf->irq_lock, flags);
status = dsp_ctrl_readl(DSP_P0_DNCC_NS_MBOX_FR_CC_TO_HOST_INTR);
if (!status) {
dsp_warn("interrupt status is unstable\n");
goto p_end;
}
__dsp_hw_p0_interface_isr(itf);
dsp_ctrl_writel(DSP_P0_DNCC_NS_MBOX_FR_CC_TO_HOST_INTR, 0x0);
dsp_leave();
p_end:
spin_unlock_irqrestore(&itf->irq_lock, flags);
return IRQ_HANDLED;
}
static const irq_handler_t isr_list[DSP_P0_IRQ_COUNT] = {
dsp_hw_p0_interface_isr1,
};
static int dsp_hw_p0_interface_start(struct dsp_interface *itf)
{
struct dsp_hw_p0_interface *itf_sub;
dsp_enter();
itf_sub = itf->sub_data;
enable_irq(itf_sub->irq[DSP_P0_IRQ_MBOX_NS]);
dsp_leave();
return 0;
}
static int dsp_hw_p0_interface_stop(struct dsp_interface *itf)
{
struct dsp_hw_p0_interface *itf_sub;
dsp_enter();
itf_sub = itf->sub_data;
dsp_ctrl_writel(DSP_P0_DNCC_NS_MBOX_FR_CC_TO_HOST_INTR, 0x0);
dsp_ctrl_writel(DSP_P0_DNCC_NS_IRQ_MBOX_ENABLE_FR_CC, 0x0);
disable_irq(itf_sub->irq[DSP_P0_IRQ_MBOX_NS]);
dsp_leave();
return 0;
}
#ifdef ENABLE_DSP_VELOCE
static void dsp_interface_isr_timer(struct timer_list *t)
{
struct dsp_interface *itf;
unsigned int status;
dsp_enter();
itf = from_timer(itf, t, isr_timer);
status = dsp_ctrl_readl(DSP_P0_DNCC_NS_MBOX_FR_CC_TO_HOST_INTR);
if (status)
dsp_hw_p0_interface_isr1(0, itf);
mod_timer(&itf->isr_timer,
jiffies + msecs_to_jiffies(DSP_ISR_TIMER_REPEAT_TIME));
dsp_leave();
}
#endif
static int dsp_hw_p0_interface_open(struct dsp_interface *itf)
{
dsp_enter();
#ifdef ENABLE_DSP_VELOCE
timer_setup(&itf->isr_timer, dsp_interface_isr_timer, 0);
mod_timer(&itf->isr_timer,
jiffies + msecs_to_jiffies(DSP_ISR_TIMER_REPEAT_TIME));
#endif
dsp_leave();
return 0;
}
static int dsp_hw_p0_interface_close(struct dsp_interface *itf)
{
dsp_enter();
#ifdef ENABLE_DSP_VELOCE
del_timer_sync(&itf->isr_timer);
#endif
dsp_leave();
return 0;
}
static int dsp_hw_p0_interface_probe(struct dsp_interface *itf, void *sys)
{
int ret;
struct dsp_hw_p0_interface *itf_sub;
struct platform_device *pdev;
int idx;
dsp_enter();
itf->sys = sys;
itf->sfr = itf->sys->sfr;
itf->irq_count = DSP_P0_IRQ_COUNT;
pdev = to_platform_device(itf->sys->dev);
itf_sub = kzalloc(sizeof(*itf_sub), GFP_KERNEL);
if (!itf_sub) {
ret = -ENOMEM;
dsp_err("Failed to alloc itf_sub\n");
goto p_err_itf_sub;
}
itf->sub_data = itf_sub;
for (idx = 0; idx < itf->irq_count; ++idx) {
ret = platform_get_irq(pdev, idx);
if (ret < 0) {
dsp_err("Failed to get irq%d(%d)\n", idx, ret);
goto p_err_irq;
}
itf_sub->irq[idx] = ret;
ret = devm_request_irq(itf->sys->dev, itf_sub->irq[idx],
isr_list[idx], 0, dev_name(itf->sys->dev), itf);
if (ret) {
dsp_err("Failed to request irq%d(%d)\n", idx, ret);
goto p_err_irq;
}
disable_irq(itf_sub->irq[idx]);
}
spin_lock_init(&itf->irq_lock);
dsp_leave();
return 0;
p_err_irq:
for (idx -= 1; idx >= 0; --idx) {
enable_irq(itf_sub->irq[idx]);
devm_free_irq(itf->sys->dev, itf_sub->irq[idx], itf);
}
kfree(itf->sub_data);
p_err_itf_sub:
return ret;
}
static void dsp_hw_p0_interface_remove(struct dsp_interface *itf)
{
int idx;
struct dsp_hw_p0_interface *itf_sub;
dsp_enter();
itf_sub = itf->sub_data;
for (idx = 0; idx < itf->irq_count; ++idx) {
enable_irq(itf_sub->irq[idx]);
devm_free_irq(itf->sys->dev, itf_sub->irq[idx], itf);
}
kfree(itf->sub_data);
dsp_leave();
}
static const struct dsp_interface_ops p0_interface_ops = {
.send_irq = dsp_hw_p0_interface_send_irq,
.check_irq = dsp_hw_p0_interface_check_irq,
.start = dsp_hw_p0_interface_start,
.stop = dsp_hw_p0_interface_stop,
.open = dsp_hw_p0_interface_open,
.close = dsp_hw_p0_interface_close,
.probe = dsp_hw_p0_interface_probe,
.remove = dsp_hw_p0_interface_remove,
};
int dsp_hw_p0_interface_register_ops(void)
{
int ret;
dsp_enter();
ret = dsp_hw_common_register_ops(DSP_DEVICE_ID_P0, DSP_HW_OPS_INTERFACE,
&p0_interface_ops,
sizeof(p0_interface_ops) / sizeof(void *));
if (ret)
goto p_err;
dsp_leave();
return 0;
p_err:
return ret;
}