// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2016-2018 Linaro Ltd.
* Copyright (C) 2014 Sony Mobile Communications AB
* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/soc/qcom/mdt_loader.h>
#include "qcom_common.h"
#include "qcom_pil_info.h"
#include "qcom_q6v5.h"
#define WCSS_CRASH_REASON 421
/* Q6SS Register Offsets */
#define Q6SS_RESET_REG 0x014
#define Q6SS_GFMUX_CTL_REG 0x020
#define Q6SS_PWR_CTL_REG 0x030
#define Q6SS_MEM_PWR_CTL 0x0B0
#define Q6SS_STRAP_ACC 0x110
#define Q6SS_CGC_OVERRIDE 0x034
#define Q6SS_BCR_REG 0x6000
/* AXI Halt Register Offsets */
#define AXI_HALTREQ_REG 0x0
#define AXI_HALTACK_REG 0x4
#define AXI_IDLE_REG 0x8
#define HALT_ACK_TIMEOUT_MS 100
/* Q6SS_RESET */
#define Q6SS_STOP_CORE BIT(0)
#define Q6SS_CORE_ARES BIT(1)
#define Q6SS_BUS_ARES_ENABLE BIT(2)
/* Q6SS_BRC_RESET */
#define Q6SS_BRC_BLK_ARES BIT(0)
/* Q6SS_GFMUX_CTL */
#define Q6SS_CLK_ENABLE BIT(1)
#define Q6SS_SWITCH_CLK_SRC BIT(8)
/* Q6SS_PWR_CTL */
#define Q6SS_L2DATA_STBY_N BIT(18)
#define Q6SS_SLP_RET_N BIT(19)
#define Q6SS_CLAMP_IO BIT(20)
#define QDSS_BHS_ON BIT(21)
#define QDSS_Q6_MEMORIES GENMASK(15, 0)
/* Q6SS parameters */
#define Q6SS_LDO_BYP BIT(25)
#define Q6SS_BHS_ON BIT(24)
#define Q6SS_CLAMP_WL BIT(21)
#define Q6SS_CLAMP_QMC_MEM BIT(22)
#define HALT_CHECK_MAX_LOOPS 200
#define Q6SS_XO_CBCR GENMASK(5, 3)
#define Q6SS_SLEEP_CBCR GENMASK(5, 2)
/* Q6SS config/status registers */
#define TCSR_GLOBAL_CFG0 0x0
#define TCSR_GLOBAL_CFG1 0x4
#define SSCAON_CONFIG 0x8
#define SSCAON_STATUS 0xc
#define Q6SS_BHS_STATUS 0x78
#define Q6SS_RST_EVB 0x10
#define BHS_EN_REST_ACK BIT(0)
#define SSCAON_ENABLE BIT(13)
#define SSCAON_BUS_EN BIT(15)
#define SSCAON_BUS_MUX_MASK GENMASK(18, 16)
#define MEM_BANKS 19
#define TCSR_WCSS_CLK_MASK 0x1F
#define TCSR_WCSS_CLK_ENABLE 0x14
#define MAX_HALT_REG 3
enum {
WCSS_IPQ8074,
WCSS_QCS404,
};
struct wcss_data {
const char *firmware_name;
unsigned int crash_reason_smem;
u32 version;
bool aon_reset_required;
bool wcss_q6_reset_required;
const char *ssr_name;
const char *sysmon_name;
int ssctl_id;
const struct rproc_ops *ops;
bool requires_force_stop;
};
struct q6v5_wcss {
struct device *dev;
void __iomem *reg_base;
void __iomem *rmb_base;
struct regmap *halt_map;
u32 halt_q6;
u32 halt_wcss;
u32 halt_nc;
struct clk *xo;
struct clk *ahbfabric_cbcr_clk;
struct clk *gcc_abhs_cbcr;
struct clk *gcc_axim_cbcr;
struct clk *lcc_csr_cbcr;
struct clk *ahbs_cbcr;
struct clk *tcm_slave_cbcr;
struct clk *qdsp6ss_abhm_cbcr;
struct clk *qdsp6ss_sleep_cbcr;
struct clk *qdsp6ss_axim_cbcr;
struct clk *qdsp6ss_xo_cbcr;
struct clk *qdsp6ss_core_gfmux;
struct clk *lcc_bcr_sleep;
struct regulator *cx_supply;
struct qcom_sysmon *sysmon;
struct reset_control *wcss_aon_reset;
struct reset_control *wcss_reset;
struct reset_control *wcss_q6_reset;
struct reset_control *wcss_q6_bcr_reset;
struct qcom_q6v5 q6v5;
phys_addr_t mem_phys;
phys_addr_t mem_reloc;
void *mem_region;
size_t mem_size;
unsigned int crash_reason_smem;
u32 version;
bool requires_force_stop;
struct qcom_rproc_glink glink_subdev;
struct qcom_rproc_ssr ssr_subdev;
};
static int q6v5_wcss_reset(struct q6v5_wcss *wcss)
{
int ret;
u32 val;
int i;
/* Assert resets, stop core */
val = readl(wcss->reg_base + Q6SS_RESET_REG);
val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;
writel(val, wcss->reg_base + Q6SS_RESET_REG);
/* BHS require xo cbcr to be enabled */
val = readl(wcss->reg_base + Q6SS_XO_CBCR);
val |= 0x1;
writel(val, wcss->reg_base + Q6SS_XO_CBCR);
/* Read CLKOFF bit to go low indicating CLK is enabled */
ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,
val, !(val & BIT(31)), 1,
HALT_CHECK_MAX_LOOPS);
if (ret) {
dev_err(wcss->dev,
"xo cbcr enabling timed out (rc:%d)\n", ret);
return ret;
}
/* Enable power block headswitch and wait for it to stabilize */
val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
val |= Q6SS_BHS_ON;
writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
udelay(1);
/* Put LDO in bypass mode */
val |= Q6SS_LDO_BYP;
writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
/* Deassert Q6 compiler memory clamp */
val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
val &= ~Q6SS_CLAMP_QMC_MEM;
writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
/* Deassert memory peripheral sleep and L2 memory standby */
val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N;
writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
/* Turn on L1, L2, ETB and JU memories 1 at a time */
val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
for (i