18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2019 NXP.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/delay.h>
78c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "dcss-dev.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define DCSS_CTXLD_CONTROL_STATUS	0x0
158c2ecf20Sopenharmony_ci#define   CTXLD_ENABLE			BIT(0)
168c2ecf20Sopenharmony_ci#define   ARB_SEL			BIT(1)
178c2ecf20Sopenharmony_ci#define   RD_ERR_EN			BIT(2)
188c2ecf20Sopenharmony_ci#define   DB_COMP_EN			BIT(3)
198c2ecf20Sopenharmony_ci#define   SB_HP_COMP_EN			BIT(4)
208c2ecf20Sopenharmony_ci#define   SB_LP_COMP_EN			BIT(5)
218c2ecf20Sopenharmony_ci#define   DB_PEND_SB_REC_EN		BIT(6)
228c2ecf20Sopenharmony_ci#define   SB_PEND_DISP_ACTIVE_EN	BIT(7)
238c2ecf20Sopenharmony_ci#define   AHB_ERR_EN			BIT(8)
248c2ecf20Sopenharmony_ci#define   RD_ERR			BIT(16)
258c2ecf20Sopenharmony_ci#define   DB_COMP			BIT(17)
268c2ecf20Sopenharmony_ci#define   SB_HP_COMP			BIT(18)
278c2ecf20Sopenharmony_ci#define   SB_LP_COMP			BIT(19)
288c2ecf20Sopenharmony_ci#define   DB_PEND_SB_REC		BIT(20)
298c2ecf20Sopenharmony_ci#define   SB_PEND_DISP_ACTIVE		BIT(21)
308c2ecf20Sopenharmony_ci#define   AHB_ERR			BIT(22)
318c2ecf20Sopenharmony_ci#define DCSS_CTXLD_DB_BASE_ADDR		0x10
328c2ecf20Sopenharmony_ci#define DCSS_CTXLD_DB_COUNT		0x14
338c2ecf20Sopenharmony_ci#define DCSS_CTXLD_SB_BASE_ADDR		0x18
348c2ecf20Sopenharmony_ci#define DCSS_CTXLD_SB_COUNT		0x1C
358c2ecf20Sopenharmony_ci#define   SB_HP_COUNT_POS		0
368c2ecf20Sopenharmony_ci#define   SB_HP_COUNT_MASK		0xffff
378c2ecf20Sopenharmony_ci#define   SB_LP_COUNT_POS		16
388c2ecf20Sopenharmony_ci#define   SB_LP_COUNT_MASK		0xffff0000
398c2ecf20Sopenharmony_ci#define DCSS_AHB_ERR_ADDR		0x20
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define CTXLD_IRQ_COMPLETION		(DB_COMP | SB_HP_COMP | SB_LP_COMP)
428c2ecf20Sopenharmony_ci#define CTXLD_IRQ_ERROR			(RD_ERR | DB_PEND_SB_REC | AHB_ERR)
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* The following sizes are in context loader entries, 8 bytes each. */
458c2ecf20Sopenharmony_ci#define CTXLD_DB_CTX_ENTRIES		1024	/* max 65536 */
468c2ecf20Sopenharmony_ci#define CTXLD_SB_LP_CTX_ENTRIES		10240	/* max 65536 */
478c2ecf20Sopenharmony_ci#define CTXLD_SB_HP_CTX_ENTRIES		20000	/* max 65536 */
488c2ecf20Sopenharmony_ci#define CTXLD_SB_CTX_ENTRIES		(CTXLD_SB_LP_CTX_ENTRIES + \
498c2ecf20Sopenharmony_ci					 CTXLD_SB_HP_CTX_ENTRIES)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* Sizes, in entries, of the DB, SB_HP and SB_LP context regions. */
528c2ecf20Sopenharmony_cistatic u16 dcss_ctxld_ctx_size[3] = {
538c2ecf20Sopenharmony_ci	CTXLD_DB_CTX_ENTRIES,
548c2ecf20Sopenharmony_ci	CTXLD_SB_HP_CTX_ENTRIES,
558c2ecf20Sopenharmony_ci	CTXLD_SB_LP_CTX_ENTRIES
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* this represents an entry in the context loader map */
598c2ecf20Sopenharmony_cistruct dcss_ctxld_item {
608c2ecf20Sopenharmony_ci	u32 val;
618c2ecf20Sopenharmony_ci	u32 ofs;
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define CTX_ITEM_SIZE			sizeof(struct dcss_ctxld_item)
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistruct dcss_ctxld {
678c2ecf20Sopenharmony_ci	struct device *dev;
688c2ecf20Sopenharmony_ci	void __iomem *ctxld_reg;
698c2ecf20Sopenharmony_ci	int irq;
708c2ecf20Sopenharmony_ci	bool irq_en;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	struct dcss_ctxld_item *db[2];
738c2ecf20Sopenharmony_ci	struct dcss_ctxld_item *sb_hp[2];
748c2ecf20Sopenharmony_ci	struct dcss_ctxld_item *sb_lp[2];
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	dma_addr_t db_paddr[2];
778c2ecf20Sopenharmony_ci	dma_addr_t sb_paddr[2];
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	u16 ctx_size[2][3]; /* holds the sizes of DB, SB_HP and SB_LP ctx */
808c2ecf20Sopenharmony_ci	u8 current_ctx;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	bool in_use;
838c2ecf20Sopenharmony_ci	bool armed;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	spinlock_t lock; /* protects concurent access to private data */
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic irqreturn_t dcss_ctxld_irq_handler(int irq, void *data)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct dcss_ctxld *ctxld = data;
918c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = dcss_drv_dev_to_dcss(ctxld->dev);
928c2ecf20Sopenharmony_ci	u32 irq_status;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	irq_status = dcss_readl(ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (irq_status & CTXLD_IRQ_COMPLETION &&
978c2ecf20Sopenharmony_ci	    !(irq_status & CTXLD_ENABLE) && ctxld->in_use) {
988c2ecf20Sopenharmony_ci		ctxld->in_use = false;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		if (dcss && dcss->disable_callback)
1018c2ecf20Sopenharmony_ci			dcss->disable_callback(dcss);
1028c2ecf20Sopenharmony_ci	} else if (irq_status & CTXLD_IRQ_ERROR) {
1038c2ecf20Sopenharmony_ci		/*
1048c2ecf20Sopenharmony_ci		 * Except for throwing an error message and clearing the status
1058c2ecf20Sopenharmony_ci		 * register, there's not much we can do here.
1068c2ecf20Sopenharmony_ci		 */
1078c2ecf20Sopenharmony_ci		dev_err(ctxld->dev, "ctxld: error encountered: %08x\n",
1088c2ecf20Sopenharmony_ci			irq_status);
1098c2ecf20Sopenharmony_ci		dev_err(ctxld->dev, "ctxld: db=%d, sb_hp=%d, sb_lp=%d\n",
1108c2ecf20Sopenharmony_ci			ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_DB],
1118c2ecf20Sopenharmony_ci			ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_SB_HP],
1128c2ecf20Sopenharmony_ci			ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_SB_LP]);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	dcss_clr(irq_status & (CTXLD_IRQ_ERROR | CTXLD_IRQ_COMPLETION),
1168c2ecf20Sopenharmony_ci		 ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int dcss_ctxld_irq_config(struct dcss_ctxld *ctxld,
1228c2ecf20Sopenharmony_ci				 struct platform_device *pdev)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	int ret;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	ctxld->irq = platform_get_irq_byname(pdev, "ctxld");
1278c2ecf20Sopenharmony_ci	if (ctxld->irq < 0)
1288c2ecf20Sopenharmony_ci		return ctxld->irq;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	ret = request_irq(ctxld->irq, dcss_ctxld_irq_handler,
1318c2ecf20Sopenharmony_ci			  0, "dcss_ctxld", ctxld);
1328c2ecf20Sopenharmony_ci	if (ret) {
1338c2ecf20Sopenharmony_ci		dev_err(ctxld->dev, "ctxld: irq request failed.\n");
1348c2ecf20Sopenharmony_ci		return ret;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	ctxld->irq_en = true;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return 0;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic void dcss_ctxld_hw_cfg(struct dcss_ctxld *ctxld)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	dcss_writel(RD_ERR_EN | SB_HP_COMP_EN |
1458c2ecf20Sopenharmony_ci		    DB_PEND_SB_REC_EN | AHB_ERR_EN | RD_ERR | AHB_ERR,
1468c2ecf20Sopenharmony_ci		    ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void dcss_ctxld_free_ctx(struct dcss_ctxld *ctxld)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct dcss_ctxld_item *ctx;
1528c2ecf20Sopenharmony_ci	int i;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
1558c2ecf20Sopenharmony_ci		if (ctxld->db[i]) {
1568c2ecf20Sopenharmony_ci			dma_free_coherent(ctxld->dev,
1578c2ecf20Sopenharmony_ci					  CTXLD_DB_CTX_ENTRIES * sizeof(*ctx),
1588c2ecf20Sopenharmony_ci					  ctxld->db[i], ctxld->db_paddr[i]);
1598c2ecf20Sopenharmony_ci			ctxld->db[i] = NULL;
1608c2ecf20Sopenharmony_ci			ctxld->db_paddr[i] = 0;
1618c2ecf20Sopenharmony_ci		}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		if (ctxld->sb_hp[i]) {
1648c2ecf20Sopenharmony_ci			dma_free_coherent(ctxld->dev,
1658c2ecf20Sopenharmony_ci					  CTXLD_SB_CTX_ENTRIES * sizeof(*ctx),
1668c2ecf20Sopenharmony_ci					  ctxld->sb_hp[i], ctxld->sb_paddr[i]);
1678c2ecf20Sopenharmony_ci			ctxld->sb_hp[i] = NULL;
1688c2ecf20Sopenharmony_ci			ctxld->sb_paddr[i] = 0;
1698c2ecf20Sopenharmony_ci		}
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic int dcss_ctxld_alloc_ctx(struct dcss_ctxld *ctxld)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct dcss_ctxld_item *ctx;
1768c2ecf20Sopenharmony_ci	int i;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
1798c2ecf20Sopenharmony_ci		ctx = dma_alloc_coherent(ctxld->dev,
1808c2ecf20Sopenharmony_ci					 CTXLD_DB_CTX_ENTRIES * sizeof(*ctx),
1818c2ecf20Sopenharmony_ci					 &ctxld->db_paddr[i], GFP_KERNEL);
1828c2ecf20Sopenharmony_ci		if (!ctx)
1838c2ecf20Sopenharmony_ci			return -ENOMEM;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		ctxld->db[i] = ctx;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		ctx = dma_alloc_coherent(ctxld->dev,
1888c2ecf20Sopenharmony_ci					 CTXLD_SB_CTX_ENTRIES * sizeof(*ctx),
1898c2ecf20Sopenharmony_ci					 &ctxld->sb_paddr[i], GFP_KERNEL);
1908c2ecf20Sopenharmony_ci		if (!ctx)
1918c2ecf20Sopenharmony_ci			return -ENOMEM;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		ctxld->sb_hp[i] = ctx;
1948c2ecf20Sopenharmony_ci		ctxld->sb_lp[i] = ctx + CTXLD_SB_HP_CTX_ENTRIES;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return 0;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciint dcss_ctxld_init(struct dcss_dev *dcss, unsigned long ctxld_base)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	struct dcss_ctxld *ctxld;
2038c2ecf20Sopenharmony_ci	int ret;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	ctxld = kzalloc(sizeof(*ctxld), GFP_KERNEL);
2068c2ecf20Sopenharmony_ci	if (!ctxld)
2078c2ecf20Sopenharmony_ci		return -ENOMEM;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	dcss->ctxld = ctxld;
2108c2ecf20Sopenharmony_ci	ctxld->dev = dcss->dev;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	spin_lock_init(&ctxld->lock);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	ret = dcss_ctxld_alloc_ctx(ctxld);
2158c2ecf20Sopenharmony_ci	if (ret) {
2168c2ecf20Sopenharmony_ci		dev_err(dcss->dev, "ctxld: cannot allocate context memory.\n");
2178c2ecf20Sopenharmony_ci		goto err;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ctxld->ctxld_reg = ioremap(ctxld_base, SZ_4K);
2218c2ecf20Sopenharmony_ci	if (!ctxld->ctxld_reg) {
2228c2ecf20Sopenharmony_ci		dev_err(dcss->dev, "ctxld: unable to remap ctxld base\n");
2238c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2248c2ecf20Sopenharmony_ci		goto err;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	ret = dcss_ctxld_irq_config(ctxld, to_platform_device(dcss->dev));
2288c2ecf20Sopenharmony_ci	if (ret)
2298c2ecf20Sopenharmony_ci		goto err_irq;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	dcss_ctxld_hw_cfg(ctxld);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return 0;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cierr_irq:
2368c2ecf20Sopenharmony_ci	iounmap(ctxld->ctxld_reg);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cierr:
2398c2ecf20Sopenharmony_ci	dcss_ctxld_free_ctx(ctxld);
2408c2ecf20Sopenharmony_ci	kfree(ctxld);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return ret;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_civoid dcss_ctxld_exit(struct dcss_ctxld *ctxld)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	free_irq(ctxld->irq, ctxld);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (ctxld->ctxld_reg)
2508c2ecf20Sopenharmony_ci		iounmap(ctxld->ctxld_reg);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	dcss_ctxld_free_ctx(ctxld);
2538c2ecf20Sopenharmony_ci	kfree(ctxld);
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int dcss_ctxld_enable_locked(struct dcss_ctxld *ctxld)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	int curr_ctx = ctxld->current_ctx;
2598c2ecf20Sopenharmony_ci	u32 db_base, sb_base, sb_count;
2608c2ecf20Sopenharmony_ci	u32 sb_hp_cnt, sb_lp_cnt, db_cnt;
2618c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = dcss_drv_dev_to_dcss(ctxld->dev);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (!dcss)
2648c2ecf20Sopenharmony_ci		return 0;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	dcss_dpr_write_sysctrl(dcss->dpr);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	dcss_scaler_write_sclctrl(dcss->scaler);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	sb_hp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_HP];
2718c2ecf20Sopenharmony_ci	sb_lp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_LP];
2728c2ecf20Sopenharmony_ci	db_cnt = ctxld->ctx_size[curr_ctx][CTX_DB];
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* make sure SB_LP context area comes after SB_HP */
2758c2ecf20Sopenharmony_ci	if (sb_lp_cnt &&
2768c2ecf20Sopenharmony_ci	    ctxld->sb_lp[curr_ctx] != ctxld->sb_hp[curr_ctx] + sb_hp_cnt) {
2778c2ecf20Sopenharmony_ci		struct dcss_ctxld_item *sb_lp_adjusted;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		sb_lp_adjusted = ctxld->sb_hp[curr_ctx] + sb_hp_cnt;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		memcpy(sb_lp_adjusted, ctxld->sb_lp[curr_ctx],
2828c2ecf20Sopenharmony_ci		       sb_lp_cnt * CTX_ITEM_SIZE);
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	db_base = db_cnt ? ctxld->db_paddr[curr_ctx] : 0;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	dcss_writel(db_base, ctxld->ctxld_reg + DCSS_CTXLD_DB_BASE_ADDR);
2888c2ecf20Sopenharmony_ci	dcss_writel(db_cnt, ctxld->ctxld_reg + DCSS_CTXLD_DB_COUNT);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (sb_hp_cnt)
2918c2ecf20Sopenharmony_ci		sb_count = ((sb_hp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK) |
2928c2ecf20Sopenharmony_ci			   ((sb_lp_cnt << SB_LP_COUNT_POS) & SB_LP_COUNT_MASK);
2938c2ecf20Sopenharmony_ci	else
2948c2ecf20Sopenharmony_ci		sb_count = (sb_lp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	sb_base = sb_count ? ctxld->sb_paddr[curr_ctx] : 0;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	dcss_writel(sb_base, ctxld->ctxld_reg + DCSS_CTXLD_SB_BASE_ADDR);
2998c2ecf20Sopenharmony_ci	dcss_writel(sb_count, ctxld->ctxld_reg + DCSS_CTXLD_SB_COUNT);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* enable the context loader */
3028c2ecf20Sopenharmony_ci	dcss_set(CTXLD_ENABLE, ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	ctxld->in_use = true;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/*
3078c2ecf20Sopenharmony_ci	 * Toggle the current context to the alternate one so that any updates
3088c2ecf20Sopenharmony_ci	 * in the modules' settings take place there.
3098c2ecf20Sopenharmony_ci	 */
3108c2ecf20Sopenharmony_ci	ctxld->current_ctx ^= 1;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	ctxld->ctx_size[ctxld->current_ctx][CTX_DB] = 0;
3138c2ecf20Sopenharmony_ci	ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] = 0;
3148c2ecf20Sopenharmony_ci	ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] = 0;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	return 0;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ciint dcss_ctxld_enable(struct dcss_ctxld *ctxld)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	spin_lock_irq(&ctxld->lock);
3228c2ecf20Sopenharmony_ci	ctxld->armed = true;
3238c2ecf20Sopenharmony_ci	spin_unlock_irq(&ctxld->lock);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	return 0;
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_civoid dcss_ctxld_kick(struct dcss_ctxld *ctxld)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	unsigned long flags;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctxld->lock, flags);
3338c2ecf20Sopenharmony_ci	if (ctxld->armed && !ctxld->in_use) {
3348c2ecf20Sopenharmony_ci		ctxld->armed = false;
3358c2ecf20Sopenharmony_ci		dcss_ctxld_enable_locked(ctxld);
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctxld->lock, flags);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_civoid dcss_ctxld_write_irqsafe(struct dcss_ctxld *ctxld, u32 ctx_id, u32 val,
3418c2ecf20Sopenharmony_ci			      u32 reg_ofs)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	int curr_ctx = ctxld->current_ctx;
3448c2ecf20Sopenharmony_ci	struct dcss_ctxld_item *ctx[] = {
3458c2ecf20Sopenharmony_ci		[CTX_DB] = ctxld->db[curr_ctx],
3468c2ecf20Sopenharmony_ci		[CTX_SB_HP] = ctxld->sb_hp[curr_ctx],
3478c2ecf20Sopenharmony_ci		[CTX_SB_LP] = ctxld->sb_lp[curr_ctx]
3488c2ecf20Sopenharmony_ci	};
3498c2ecf20Sopenharmony_ci	int item_idx = ctxld->ctx_size[curr_ctx][ctx_id];
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (item_idx + 1 > dcss_ctxld_ctx_size[ctx_id]) {
3528c2ecf20Sopenharmony_ci		WARN_ON(1);
3538c2ecf20Sopenharmony_ci		return;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	ctx[ctx_id][item_idx].val = val;
3578c2ecf20Sopenharmony_ci	ctx[ctx_id][item_idx].ofs = reg_ofs;
3588c2ecf20Sopenharmony_ci	ctxld->ctx_size[curr_ctx][ctx_id] += 1;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_civoid dcss_ctxld_write(struct dcss_ctxld *ctxld, u32 ctx_id,
3628c2ecf20Sopenharmony_ci		      u32 val, u32 reg_ofs)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	spin_lock_irq(&ctxld->lock);
3658c2ecf20Sopenharmony_ci	dcss_ctxld_write_irqsafe(ctxld, ctx_id, val, reg_ofs);
3668c2ecf20Sopenharmony_ci	spin_unlock_irq(&ctxld->lock);
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cibool dcss_ctxld_is_flushed(struct dcss_ctxld *ctxld)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	return ctxld->ctx_size[ctxld->current_ctx][CTX_DB] == 0 &&
3728c2ecf20Sopenharmony_ci		ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] == 0 &&
3738c2ecf20Sopenharmony_ci		ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] == 0;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ciint dcss_ctxld_resume(struct dcss_ctxld *ctxld)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	dcss_ctxld_hw_cfg(ctxld);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (!ctxld->irq_en) {
3818c2ecf20Sopenharmony_ci		enable_irq(ctxld->irq);
3828c2ecf20Sopenharmony_ci		ctxld->irq_en = true;
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return 0;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ciint dcss_ctxld_suspend(struct dcss_ctxld *ctxld)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	int ret = 0;
3918c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(500);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (!dcss_ctxld_is_flushed(ctxld)) {
3948c2ecf20Sopenharmony_ci		dcss_ctxld_kick(ctxld);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		while (!time_after(jiffies, timeout) && ctxld->in_use)
3978c2ecf20Sopenharmony_ci			msleep(20);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout))
4008c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	spin_lock_irq(&ctxld->lock);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (ctxld->irq_en) {
4068c2ecf20Sopenharmony_ci		disable_irq_nosync(ctxld->irq);
4078c2ecf20Sopenharmony_ci		ctxld->irq_en = false;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* reset context region and sizes */
4118c2ecf20Sopenharmony_ci	ctxld->current_ctx = 0;
4128c2ecf20Sopenharmony_ci	ctxld->ctx_size[0][CTX_DB] = 0;
4138c2ecf20Sopenharmony_ci	ctxld->ctx_size[0][CTX_SB_HP] = 0;
4148c2ecf20Sopenharmony_ci	ctxld->ctx_size[0][CTX_SB_LP] = 0;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	spin_unlock_irq(&ctxld->lock);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	return ret;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_civoid dcss_ctxld_assert_locked(struct dcss_ctxld *ctxld)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	lockdep_assert_held(&ctxld->lock);
4248c2ecf20Sopenharmony_ci}
425