18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2013-2015 Freescale Semiconductor, Inc.
48c2ecf20Sopenharmony_ci * Copyright 2017-2018 NXP.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/err.h>
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/of.h>
108c2ecf20Sopenharmony_ci#include <linux/of_address.h>
118c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
128c2ecf20Sopenharmony_ci#include <linux/regmap.h>
138c2ecf20Sopenharmony_ci#include "common.h"
148c2ecf20Sopenharmony_ci#include "hardware.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define REG_SET		0x4
178c2ecf20Sopenharmony_ci#define REG_CLR		0x8
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define ANADIG_REG_2P5		0x130
208c2ecf20Sopenharmony_ci#define ANADIG_REG_CORE		0x140
218c2ecf20Sopenharmony_ci#define ANADIG_ANA_MISC0	0x150
228c2ecf20Sopenharmony_ci#define ANADIG_DIGPROG		0x260
238c2ecf20Sopenharmony_ci#define ANADIG_DIGPROG_IMX6SL	0x280
248c2ecf20Sopenharmony_ci#define ANADIG_DIGPROG_IMX7D	0x800
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define SRC_SBMR2		0x1c
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG	0x40000
298c2ecf20Sopenharmony_ci#define BM_ANADIG_REG_2P5_ENABLE_PULLDOWN	0x8
308c2ecf20Sopenharmony_ci#define BM_ANADIG_REG_CORE_FET_ODRIVE		0x20000000
318c2ecf20Sopenharmony_ci#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG	0x1000
328c2ecf20Sopenharmony_ci/* Below MISC0_DISCON_HIGH_SNVS is only for i.MX6SL */
338c2ecf20Sopenharmony_ci#define BM_ANADIG_ANA_MISC0_DISCON_HIGH_SNVS	0x2000
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic struct regmap *anatop;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic void imx_anatop_enable_weak2p5(bool enable)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	u32 reg, val;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	regmap_read(anatop, ANADIG_ANA_MISC0, &val);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* can only be enabled when stop_mode_config is clear. */
448c2ecf20Sopenharmony_ci	reg = ANADIG_REG_2P5;
458c2ecf20Sopenharmony_ci	reg += (enable && (val & BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG) == 0) ?
468c2ecf20Sopenharmony_ci		REG_SET : REG_CLR;
478c2ecf20Sopenharmony_ci	regmap_write(anatop, reg, BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG);
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic void imx_anatop_enable_fet_odrive(bool enable)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	regmap_write(anatop, ANADIG_REG_CORE + (enable ? REG_SET : REG_CLR),
538c2ecf20Sopenharmony_ci		BM_ANADIG_REG_CORE_FET_ODRIVE);
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic inline void imx_anatop_enable_2p5_pulldown(bool enable)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	regmap_write(anatop, ANADIG_REG_2P5 + (enable ? REG_SET : REG_CLR),
598c2ecf20Sopenharmony_ci		BM_ANADIG_REG_2P5_ENABLE_PULLDOWN);
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic inline void imx_anatop_disconnect_high_snvs(bool enable)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	regmap_write(anatop, ANADIG_ANA_MISC0 + (enable ? REG_SET : REG_CLR),
658c2ecf20Sopenharmony_ci		BM_ANADIG_ANA_MISC0_DISCON_HIGH_SNVS);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_civoid imx_anatop_pre_suspend(void)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	if (imx_mmdc_get_ddr_type() == IMX_DDR_TYPE_LPDDR2)
718c2ecf20Sopenharmony_ci		imx_anatop_enable_2p5_pulldown(true);
728c2ecf20Sopenharmony_ci	else
738c2ecf20Sopenharmony_ci		imx_anatop_enable_weak2p5(true);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	imx_anatop_enable_fet_odrive(true);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (cpu_is_imx6sl())
788c2ecf20Sopenharmony_ci		imx_anatop_disconnect_high_snvs(true);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_civoid imx_anatop_post_resume(void)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	if (imx_mmdc_get_ddr_type() == IMX_DDR_TYPE_LPDDR2)
848c2ecf20Sopenharmony_ci		imx_anatop_enable_2p5_pulldown(false);
858c2ecf20Sopenharmony_ci	else
868c2ecf20Sopenharmony_ci		imx_anatop_enable_weak2p5(false);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	imx_anatop_enable_fet_odrive(false);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (cpu_is_imx6sl())
918c2ecf20Sopenharmony_ci		imx_anatop_disconnect_high_snvs(false);
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_civoid __init imx_init_revision_from_anatop(void)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct device_node *np, *src_np;
978c2ecf20Sopenharmony_ci	void __iomem *anatop_base;
988c2ecf20Sopenharmony_ci	unsigned int revision;
998c2ecf20Sopenharmony_ci	u32 digprog;
1008c2ecf20Sopenharmony_ci	u16 offset = ANADIG_DIGPROG;
1018c2ecf20Sopenharmony_ci	u8 major_part, minor_part;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
1048c2ecf20Sopenharmony_ci	anatop_base = of_iomap(np, 0);
1058c2ecf20Sopenharmony_ci	WARN_ON(!anatop_base);
1068c2ecf20Sopenharmony_ci	if (of_device_is_compatible(np, "fsl,imx6sl-anatop"))
1078c2ecf20Sopenharmony_ci		offset = ANADIG_DIGPROG_IMX6SL;
1088c2ecf20Sopenharmony_ci	if (of_device_is_compatible(np, "fsl,imx7d-anatop"))
1098c2ecf20Sopenharmony_ci		offset = ANADIG_DIGPROG_IMX7D;
1108c2ecf20Sopenharmony_ci	digprog = readl_relaxed(anatop_base + offset);
1118c2ecf20Sopenharmony_ci	iounmap(anatop_base);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/*
1148c2ecf20Sopenharmony_ci	 * On i.MX7D digprog value match linux version format, so
1158c2ecf20Sopenharmony_ci	 * it needn't map again and we can use register value directly.
1168c2ecf20Sopenharmony_ci	 */
1178c2ecf20Sopenharmony_ci	if (of_device_is_compatible(np, "fsl,imx7d-anatop")) {
1188c2ecf20Sopenharmony_ci		revision = digprog & 0xff;
1198c2ecf20Sopenharmony_ci	} else {
1208c2ecf20Sopenharmony_ci		/*
1218c2ecf20Sopenharmony_ci		 * MAJOR: [15:8], the major silicon revison;
1228c2ecf20Sopenharmony_ci		 * MINOR: [7: 0], the minor silicon revison;
1238c2ecf20Sopenharmony_ci		 *
1248c2ecf20Sopenharmony_ci		 * please refer to the i.MX RM for the detailed
1258c2ecf20Sopenharmony_ci		 * silicon revison bit define.
1268c2ecf20Sopenharmony_ci		 * format the major part and minor part to match the
1278c2ecf20Sopenharmony_ci		 * linux kernel soc version format.
1288c2ecf20Sopenharmony_ci		 */
1298c2ecf20Sopenharmony_ci		major_part = (digprog >> 8) & 0xf;
1308c2ecf20Sopenharmony_ci		minor_part = digprog & 0xf;
1318c2ecf20Sopenharmony_ci		revision = ((major_part + 1) << 4) | minor_part;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		if ((digprog >> 16) == MXC_CPU_IMX6ULL) {
1348c2ecf20Sopenharmony_ci			void __iomem *src_base;
1358c2ecf20Sopenharmony_ci			u32 sbmr2;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci			src_np = of_find_compatible_node(NULL, NULL,
1388c2ecf20Sopenharmony_ci						     "fsl,imx6ul-src");
1398c2ecf20Sopenharmony_ci			src_base = of_iomap(src_np, 0);
1408c2ecf20Sopenharmony_ci			of_node_put(src_np);
1418c2ecf20Sopenharmony_ci			WARN_ON(!src_base);
1428c2ecf20Sopenharmony_ci			sbmr2 = readl_relaxed(src_base + SRC_SBMR2);
1438c2ecf20Sopenharmony_ci			iounmap(src_base);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci			/* src_sbmr2 bit 6 is to identify if it is i.MX6ULZ */
1468c2ecf20Sopenharmony_ci			if (sbmr2 & (1 << 6)) {
1478c2ecf20Sopenharmony_ci				digprog &= ~(0xff << 16);
1488c2ecf20Sopenharmony_ci				digprog |= (MXC_CPU_IMX6ULZ << 16);
1498c2ecf20Sopenharmony_ci			}
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci	of_node_put(np);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	mxc_set_cpu_type(digprog >> 16 & 0xff);
1558c2ecf20Sopenharmony_ci	imx_set_soc_revision(revision);
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_civoid __init imx_anatop_init(void)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	anatop = syscon_regmap_lookup_by_compatible("fsl,imx6q-anatop");
1618c2ecf20Sopenharmony_ci	if (IS_ERR(anatop))
1628c2ecf20Sopenharmony_ci		pr_err("%s: failed to find imx6q-anatop regmap!\n", __func__);
1638c2ecf20Sopenharmony_ci}
164