// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2025, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mailbox_controller.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <soc/tegra/fuse.h>
#include <dt-bindings/mailbox/tegra186-hsp.h>
#include "mailbox.h"
#define HSP_INT_IE(x) (0x100 + ((x) * 4))
#define HSP_INT_IV 0x300
#define HSP_INT_IR 0x304
#define HSP_INT_EMPTY_SHIFT 0
#define HSP_INT_EMPTY_MASK 0xff
#define HSP_INT_FULL_SHIFT 8
#define HSP_INT_FULL_MASK 0xff
#define HSP_INT_DIMENSIONING 0x380
#define HSP_DB_TRIGGER 0x0
#define HSP_DB_ENABLE 0x4
#define HSP_DB_RAW 0x8
#define HSP_DB_PENDING 0xc
#define HSP_SM_SHRD_MBOX 0x0
#define HSP_SM_SHRD_MBOX_FULL BIT(31)
#define HSP_SM_SHRD_MBOX_FULL_INT_IE 0x04
#define HSP_SM_SHRD_MBOX_EMPTY_INT_IE 0x08
#define HSP_SHRD_MBOX_TYPE1_TAG 0x40
#define HSP_SHRD_MBOX_TYPE1_DATA0 0x48
#define HSP_SHRD_MBOX_TYPE1_DATA1 0x4c
#define HSP_SHRD_MBOX_TYPE1_DATA2 0x50
#define HSP_SHRD_MBOX_TYPE1_DATA3 0x54
#define HSP_DB_CCPLEX 1
#define HSP_DB_BPMP 3
#define HSP_DB_MAX 7
#define HSP_MBOX_TYPE_MASK 0xff
struct tegra_hsp_channel;
struct tegra_hsp;
struct tegra_hsp_channel {
struct tegra_hsp *hsp;
struct mbox_chan *chan;
void __iomem *regs;
};
struct tegra_hsp_doorbell {
struct tegra_hsp_channel channel;
struct list_head list;
const char *name;
unsigned int master;
unsigned int index;
};
struct tegra_hsp_sm_ops {
void (*send)(struct tegra_hsp_channel *channel, void *data);
void (*recv)(struct tegra_hsp_channel *channel);
};
struct tegra_hsp_mailbox {
struct tegra_hsp_channel channel;
const struct tegra_hsp_sm_ops *ops;
unsigned int index;
bool producer;
};
struct tegra_hsp_db_map {
const char *name;
unsigned int master;
unsigned int index;
};
struct tegra_hsp_soc {
const struct tegra_hsp_db_map *map;
bool has_per_mb_ie;
bool has_128_bit_mb;
unsigned int reg_stride;
/* Shifts for dimensioning register. */
unsigned int si_shift;
unsigned int db_shift;
unsigned int as_shift;
unsigned int ss_shift;
unsigned int sm_shift;
/* Masks for dimensioning register. */
unsigned int si_mask;
unsigned int db_mask;
unsigned int as_mask;
unsigned int ss_mask;
unsigned int sm_mask;
};
struct tegra_hsp {
struct device *dev;
const struct tegra_hsp_soc *soc;
struct mbox_controller mbox_db;
struct mbox_controller mbox_sm;
void __iomem *regs;
unsigned int doorbell_irq;
unsigned int *shared_irqs;
unsigned int shared_irq;
unsigned int num_sm;
unsigned int num_as;
unsigned int num_ss;
unsigned int num_db;
unsigned int num_si;
spinlock_t lock;
struct lock_class_key lock_key;
struct list_head doorbells;
struct tegra_hsp_mailbox *mailboxes;
unsigned long mask;
};
static inline u32 tegra_hsp_readl(struct tegra_hsp *hsp, unsigned int offset)
{
return readl(hsp->regs + offset);
}
static inline void tegra_hsp_writel(struct tegra_hsp *hsp, u32 value,
unsigned int offset)
{
writel(value, hsp->regs + offset);
}
static inline u32 tegra_hsp_channel_readl(struct tegra_hsp_channel *channel,
unsigned int offset)
{
return readl(channel->regs + offset);
}
static inline void tegra_hsp_channel_writel(struct tegra_hsp_channel *channel,
u32 value, unsigned int offset)
{
writel(value, channel->regs + offset);
}
static bool tegra_hsp_doorbell_can_ring(struct tegra_hsp_doorbell *db)
{
u32 value;
value = tegra_hsp_channel_readl(&db->channel, HSP_DB_ENABLE);
return (value & BIT(TEGRA_HSP_DB_MASTER_CCPLEX)) != 0;
}
static struct tegra_hsp_doorbell *
__tegra_hsp_doorbell_get(struct tegra_hsp *hsp, unsigned int master)
{
struct tegra_hsp_doorbell *entry;
list_for_each_entry(entry, &hsp->doorbells, list)
if (entry->master == master)
return entry;
return NULL;
}
static struct