text: /*
   * drivers/soc/tegra/pmc.c
   *
   * Copyright (c) 2010 Google, Inc
   * Copyright (c) 2012-2020, NVIDIA CORPORATION. All rights reserved.
   *
   * Author:
   *	Colin Cross <ccross@google.com>
   *
   * This software is licensed under the terms of the GNU General Public
   * License version 2, as published by the Free Software Foundation, and
   * may be copied, distributed, and modified under those terms.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   */
  
  #define pr_fmt(fmt) "tegra-pmc: " fmt
  
  #include <linux/kernel.h>
  #include <linux/clk.h>
  #include <linux/clk/tegra.h>
  #include <linux/debugfs.h>
  #include <linux/delay.h>
  #include <linux/err.h>
  #include <linux/export.h>
  #include <linux/gpio.h>
  #include <linux/init.h>
  #include <linux/interrupt.h>
  #include <linux/io.h>
  #include <linux/irq.h>
  #include <linux/memblock.h>
  #include <linux/of.h>
  #include <linux/of_address.h>
  #include <linux/of_gpio.h>
  #include <linux/of_platform.h>
  #include <linux/pinctrl/pinctrl.h>
  #include <linux/pinctrl/pinconf-generic.h>
  #include <linux/pinctrl/pinconf.h>
  #include <linux/platform_device.h>
  #include <linux/psci.h>
  #include <linux/reboot.h>
  #include <linux/reset.h>
  #include <linux/seq_file.h>
  #include <linux/slab.h>
  #include <linux/spinlock.h>
  #include <linux/syscore_ops.h>
  #include <linux/wakeup_reason.h>
  #include <soc/tegra/chip-id.h>
  #include <linux/tegra_prod.h>
  #include <linux/power/reset/system-pmic.h>
  #include <linux/notifier.h>
  #include <linux/regulator/consumer.h>
  #include <linux/uaccess.h>
  #include <linux/irqchip/tegra.h>
  
  #include <dt-bindings/soc/tegra-pmc.h>
  
  #include <soc/tegra/common.h>
  #include <soc/tegra/fuse.h>
  #include <soc/tegra/pmc.h>
  
  #include <asm/system_misc.h>
  
  #include <dt-bindings/pinctrl/pinctrl-tegra-io-pad.h>
  
  #define PMC_CNTRL			0x0
  #define  PMC_CNTRL_LATCH_WAKEUPS	(1 << 5)
  #define  PMC_CNTRL_PWRREQ_POLARITY	(1 << 8)   /* core power req polarity */
  #define  PMC_CNTRL_PWRREQ_OE		(1 << 9)   /* core power req enable */
  #define  PMC_CNTRL_SYSCLK_POLARITY	(1 << 10)  /* sys clk polarity */
  #define  PMC_CNTRL_SYSCLK_OE		(1 << 11)  /* system clock enable */
  #define  PMC_CNTRL_SIDE_EFFECT_LP0	(1 << 14)  /* LP0 when CPU pwr gated */
  #define  PMC_CNTRL_CPU_PWRREQ_POLARITY	(1 << 15)  /* CPU pwr req polarity */
  #define  PMC_CNTRL_CPU_PWRREQ_OE	(1 << 16)  /* CPU pwr req enable */
  #define  PMC_CNTRL_INTR_POLARITY	(1 << 17)  /* inverts INTR polarity */
  
  #define PMC_WAKE_MASK			0xc
  #define PMC_WAKE_LEVEL			0x10
  #define PMC_WAKE_STATUS			0x14
  #define PMC_SW_WAKE_STATUS		0x18
  
  #define DPD_SAMPLE			0x020
  #define  DPD_SAMPLE_ENABLE		(1 << 0)
  #define  DPD_SAMPLE_DISABLE		(0 << 0)
  
  #define DPD_ENABLE			0x24
  #define  DPD_ENABLE_ON			(1 << 0)
  #define  DPD_ENABLE_TSC_MULT_ENABLE	(1 << 1)
  
  #define PWRGATE_TOGGLE			0x30
  #define  PWRGATE_TOGGLE_START		(1 << 8)
  
  #define REMOVE_CLAMPING			0x34
  
  #define PWRGATE_STATUS			0x38
  
  #define PMC_COREPWRGOOD_TIMER		0x3c
  
  #define PMC_PWR_DET_ENABLE		0x48
  
  #define PMC_SCRATCH0			0x50
  #define  PMC_SCRATCH0_MODE_RECOVERY	(1 << 31)
  #define  PMC_SCRATCH0_MODE_BOOTLOADER	(1 << 30)
  #define  PMC_SCRATCH0_MODE_RCM		(1 << 1)
  #define  PMC_SCRATCH0_MODE_MASK		(PMC_SCRATCH0_MODE_RECOVERY | \
  					 PMC_SCRATCH0_MODE_BOOTLOADER | \
  					 PMC_SCRATCH0_MODE_RCM)
  
  #define PMC_SCRATCH1			0x54
  
  #define PMC_CPUPWRGOOD_TIMER		0xc8
  #define PMC_CPUPWROFF_TIMER		0xcc
  
  #define PMC_WAKE_DELAY			0xe0
  #define PMC_COREPWROFF_TIMER		PMC_WAKE_DELAY
  
  #define PMC_PWR_DET_VAL			0xe4
  
  #define KERNEL_PANIC_FLAG		(1 << 24)
  
  #define PMC_SCRATCH41			0x140
  
  #define PMC_WAKE2_MASK			0x160
  #define PMC_WAKE2_LEVEL			0x164
  #define PMC_WAKE2_STATUS		0x168
  #define PMC_SW_WAKE2_STATUS		0x16c
  
  #define PMC_SENSOR_CTRL			0x1b0
  #define PMC_SENSOR_CTRL_SCRATCH_WRITE	(1 << 2)
  #define PMC_SENSOR_CTRL_ENABLE_RST	(1 << 1)
  
  #define IO_DPD_REQ			0x1b8
  #define  IO_DPD_CSIA			(1 << 0)
  #define  IO_DPD_CSIB			(1 << 1)
  #define  IO_DPD_DSI			(1 << 2)
  #define  IO_DPD_MIPI_BIAS		(1 << 3)
  #define  IO_DPD_PEX_BIAS		(1 << 4)
  #define  IO_DPD_PEX_CLK1		(1 << 5)
  #define  IO_DPD_PEX_CLK2		(1 << 6)
  #define  IO_DPD_PEX_CLK3		(1 << 7)
  #define  IO_DPD_DAC			(1 << 8)
  #define  IO_DPD_USB0			(1 << 9)
  #define  IO_DPD_USB1			(1 << 10)
  #define  IO_DPD_USB2			(1 << 11)
  #define  IO_DPD_USB_BIAS		(1 << 12)
  #define  IO_DPD_NAND			(1 << 13)
  #define  IO_DPD_UART			(1 << 14)
  #define  IO_DPD_BB			(1 << 15)
  #define  IO_DPD_VI			(1 << 16)
  #define  IO_DPD_AUDIO			(1 << 17)
  #define  IO_DPD_LCD			(1 << 18)
  #define  IO_DPD_HSIC			(1 << 19)
  #define  IO_DPD_REQ_CODE_IDLE		(0 << 30)
  #define  IO_DPD_REQ_CODE_OFF		(1 << 30)
  #define  IO_DPD_REQ_CODE_ON		(2 << 30)
  #define  IO_DPD_REQ_CODE_MASK		(3 << 30)
  #define  IO_DPD_ENABLE_LSB		30
  
  #define IO_DPD_STATUS			0x1bc
  
  #define IO_DPD2_REQ			0x1c0
  #define  IO_DPD2_PEX_CNTRL		(1 << 0)
  #define  IO_DPD2_SDMMC1			(1 << 1)
  #define  IO_DPD2_SDMMC3			(1 << 2)
  #define  IO_DPD2_SDMMC4			(1 << 3)
  #define  IO_DPD2_CAM			(1 << 4)
  #define  IO_DPD2_RES_RAIL		(1 << 5)
  #define  IO_DPD2_HV			(1 << 6)
  #define  IO_DPD2_DSIB			(1 << 7)
  #define  IO_DPD2_DSIC			(1 << 8)
  #define  IO_DPD2_DSID			(1 << 9)
  #define  IO_DPD2_CSIC			(1 << 10)
  #define  IO_DPD2_CSID			(1 << 11)
  #define  IO_DPD2_CSIE			(1 << 12)
  #define  IO_DPD2_ENABLE_LSB		30
  
  #define IO_DPD2_STATUS			0x1c4
  #define SEL_DPD_TIM			0x1c8
  
  #define DPD_STATE_CHANGE_DELAY		700
  
  #define PMC_SCRATCH54			0x258
  #define PMC_SCRATCH54_DATA_SHIFT	8
  #define PMC_SCRATCH54_ADDR_SHIFT	0
  
  #define PMC_SCRATCH55			0x25c
  #define PMC_SCRATCH55_RESET_TEGRA	(1 << 31)
  #define PMC_SCRATCH55_CNTRL_ID_SHIFT	27
  #define PMC_SCRATCH55_PINMUX_SHIFT	24
  #define PMC_SCRATCH55_16BITOP		(1 << 15)
  #define PMC_SCRATCH55_CHECKSUM_SHIFT	16
  #define PMC_SCRATCH55_I2CSLV1_SHIFT	0
  
  #define GPU_RG_CNTRL			0x2d4
  
  #define PMC_FUSE_CTRL                   0x450
  #define PMC_FUSE_CTRL_ENABLE_REDIRECTION	(1 << 0)
  #define PMC_FUSE_CTRL_ENABLE_REDIRECTION_STICKY	(1 << 1)
  #define PMC_FUSE_CTRL_PS18_LATCH_SET    (1 << 8)
  #define PMC_FUSE_CTRL_PS18_LATCH_CLEAR  (1 << 9)
  
  /* Scratch 250: Bootrom i2c command base */
  #define PMC_BR_COMMAND_BASE		0x908
  
  /* USB2 SLEEPWALK registers */
  #define UTMIP(_port, _offset1, _offset2) \
  		(((_port) <= 2) ? (_offset1) : (_offset2))
  
  #define APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(x)	UTMIP(x, 0x1fc, 0x4d0)
  #define   UTMIP_MASTER_ENABLE(x)		UTMIP(x, BIT(8 * (x)), BIT(0))
  #define   UTMIP_FSLS_USE_PMC(x)			UTMIP(x, BIT(8 * (x) + 1), \
  							BIT(1))
  #define   UTMIP_PCTRL_USE_PMC(x)		UTMIP(x, BIT(8 * (x) + 2), \
  							BIT(2))
  #define   UTMIP_TCTRL_USE_PMC(x)		UTMIP(x, BIT(8 * (x) + 3), \
  							BIT(3))
  #define   UTMIP_WAKE_VAL(_port, _value)		(((_value) & 0xf) << \
  					(UTMIP(_port, 8 * (_port) + 4, 4)))
  #define   UTMIP_WAKE_VAL_NONE(_port)		UTMIP_WAKE_VAL(_port, 12)
  #define   UTMIP_WAKE_VAL_ANY(_port)		UTMIP_WAKE_VAL(_port, 15)
  
  #define APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG1	(0x4d0)
  #define   UTMIP_RPU_SWITC_LOW_USE_PMC_PX(x)	BIT((x) + 8)
  #define   UTMIP_RPD_CTRL_USE_PMC_PX(x)		BIT((x) + 16)
  
  #define APBDEV_PMC_UTMIP_MASTER_CONFIG		(0x274)
  #define   UTMIP_PWR(x)				UTMIP(x, BIT(x), BIT(4))
  #define   UHSIC_PWR(x)				BIT(3)
  
  #define APBDEV_PMC_USB_DEBOUNCE_DEL		(0xec)
  #define   DEBOUNCE_VAL(x)			(((x) & 0xffff) << 0)
  #define   UTMIP_LINE_DEB_CNT(x)			(((x) & 0xf) << 16)
  #define   UHSIC_LINE_DEB_CNT(x)			(((x) & 0xf) << 20)
  
  #define APBDEV_PMC_UTMIP_UHSIC_FAKE(x)		UTMIP(x, 0x218, 0x294)
  #define   UTMIP_FAKE_USBOP_VAL(x)		UTMIP(x, BIT(4 * (x)), BIT(8))
  #define   UTMIP_FAKE_USBON_VAL(x)		UTMIP(x, BIT(4 * (x) + 1), \
  							BIT(9))
  #define   UTMIP_FAKE_USBOP_EN(x)		UTMIP(x, BIT(4 * (x) + 2), \
  							BIT(10))
  #define   UTMIP_FAKE_USBON_EN(x)		UTMIP(x, BIT(4 * (x) + 3), \
  							BIT(11))
  
  #define APBDEV_PMC_UTMIP_UHSIC_SLEEPWALK_CFG(x)	UTMIP(x, 0x200, 0x288)
  #define   UTMIP_LINEVAL_WALK_EN(x)		UTMIP(x, BIT(8 * (x) + 7), \
  							BIT(15))
  
  #define APBDEV_PMC_USB_AO			(0xf0)
  #define   USBOP_VAL_PD(x)			UTMIP(x, BIT(4 * (x)), BIT(20))
  #define   USBON_VAL_PD(x)			UTMIP(x, BIT(4 * (x) + 1), \
  							BIT(21))
  #define   STROBE_VAL_PD(x)			BIT(12)
  #define   DATA0_VAL_PD(x)			BIT(13)
  #define   DATA1_VAL_PD				BIT(24)
  
  #define APBDEV_PMC_UTMIP_UHSIC_SAVED_STATE(x)	UTMIP(x, 0x1f0, 0x280)
  #define   SPEED(_port, _value)			(((_value) & 0x3) << \
  						(UTMIP(_port, 8 * (_port), 8)))
  #define   UTMI_HS(_port)			SPEED(_port, 0)
  #define   UTMI_FS(_port)			SPEED(_port, 1)
  #define   UTMI_LS(_port)			SPEED(_port, 2)
  #define   UTMI_RST(_port)			SPEED(_port, 3)
  
  #define APBDEV_PMC_UTMIP_UHSIC_TRIGGERS		(0x1ec)
  #define   UTMIP_CLR_WALK_PTR(x)			UTMIP(x, BIT(x), BIT(16))
  #define   UTMIP_CAP_CFG(x)			UTMIP(x, BIT((x) + 4), BIT(17))
  #define   UTMIP_CLR_WAKE_ALARM(x)		UTMIP(x, BIT((x) + 12), \
  							BIT(19))
  #define   UHSIC_CLR_WALK_PTR			BIT(3)
  #define   UHSIC_CLR_WAKE_ALARM			BIT(15)
  
  #define APBDEV_PMC_UTMIP_SLEEPWALK_PX(x)	UTMIP(x, 0x204 + (4 * (x)), \
  							0x4e0)
  /* phase A */
  #define   UTMIP_USBOP_RPD_A			BIT(0)
  #define   UTMIP_USBON_RPD_A			BIT(1)
  #define   UTMIP_AP_A				BIT(4)
  #define   UTMIP_AN_A				BIT(5)
  #define   UTMIP_HIGHZ_A				BIT(6)
  /* phase B */
  #define   UTMIP_USBOP_RPD_B			BIT(8)
  #define   UTMIP_USBON_RPD_B			BIT(9)
  #define   UTMIP_AP_B				BIT(12)
  #define   UTMIP_AN_B				BIT(13)
  #define   UTMIP_HIGHZ_B				BIT(14)
  /* phase C */
  #define   UTMIP_USBOP_RPD_C			BIT(16)
  #define   UTMIP_USBON_RPD_C			BIT(17)
  #define   UTMIP_AP_C				BIT(20)
  #define   UTMIP_AN_C				BIT(21)
  #define   UTMIP_HIGHZ_C				BIT(22)
  /* phase D */
  #define   UTMIP_USBOP_RPD_D			BIT(24)
  #define   UTMIP_USBON_RPD_D			BIT(25)
  #define   UTMIP_AP_D				BIT(28)
  #define   UTMIP_AN_D				BIT(29)
  #define   UTMIP_HIGHZ_D				BIT(30)
  
  #define APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP	(0x26c)
  #define   UTMIP_LINE_WAKEUP_EN(x)		UTMIP(x, BIT(x), BIT(4))
  #define   UHSIC_LINE_WAKEUP_EN			BIT(3)
  
  #define APBDEV_PMC_UTMIP_TERM_PAD_CFG		(0x1f8)
  #define   PCTRL_VAL(x)				(((x) & 0x3f) << 1)
  #define   TCTRL_VAL(x)				(((x) & 0x3f) << 7)
  
  #define APBDEV_PMC_UTMIP_PAD_CFGX(x)		(0x4c0 + (4 * (x)))
  #define   RPD_CTRL_PX(x)			(((x) & 0x1f) << 22)
  
  #define APBDEV_PMC_UHSIC_SLEEP_CFG		\
  		APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(0)
  #define   UHSIC_MASTER_ENABLE			BIT(24)
  #define   UHSIC_WAKE_VAL(_value)		(((_value) & 0xf) << 28)
  #define   UHSIC_WAKE_VAL_SD10			UHSIC_WAKE_VAL(2)
  #define   UHSIC_WAKE_VAL_NONE			UHSIC_WAKE_VAL(12)
  
  #define APBDEV_PMC_UHSIC_FAKE			APBDEV_PMC_UTMIP_UHSIC_FAKE(0)
  #define   UHSIC_FAKE_STROBE_VAL			BIT(12)
  #define   UHSIC_FAKE_DATA_VAL			BIT(13)
  #define   UHSIC_FAKE_STROBE_EN			BIT(14)
  #define   UHSIC_FAKE_DATA_EN			BIT(15)
  
  #define APBDEV_PMC_UHSIC_SAVED_STATE		\
  		APBDEV_PMC_UTMIP_UHSIC_SAVED_STATE(0)
  #define   UHSIC_MODE(_value)			(((_value) & 0x1) << 24)
  #define   UHSIC_HS				UHSIC_MODE(0)
  #define   UHSIC_RST				UHSIC_MODE(1)
  
  #define APBDEV_PMC_UHSIC_SLEEPWALK_CFG		\
  		APBDEV_PMC_UTMIP_UHSIC_SLEEPWALK_CFG(0)
  #define   UHSIC_WAKE_WALK_EN			BIT(30)
  #define   UHSIC_LINEVAL_WALK_EN			BIT(31)
  
  #define APBDEV_PMC_UHSIC_SLEEPWALK_P0		(0x210)
  #define   UHSIC_DATA0_RPD_A			BIT(1)
  #define   UHSIC_DATA0_RPU_B			BIT(11)
  #define   UHSIC_DATA0_RPU_C			BIT(19)
  #define   UHSIC_DATA0_RPU_D			BIT(27)
  #define   UHSIC_STROBE_RPU_A			BIT(2)
  #define   UHSIC_STROBE_RPD_B			BIT(8)
  #define   UHSIC_STROBE_RPD_C			BIT(16)
  #define   UHSIC_STROBE_RPD_D			BIT(24)
  
  /* Bootrom comand register */
  #define PMC_REG_8bit_MASK			0xFF
  #define PMC_REG_16bit_MASK			0xFFFF
  #define PMC_BR_COMMAND_I2C_ADD_MASK		0x7F
  #define PMC_BR_COMMAND_WR_COMMANDS_MASK		0x3F
  #define PMC_BR_COMMAND_WR_COMMANDS_SHIFT	8
  #define PMC_BR_COMMAND_OPERAND_SHIFT		15
  #define PMC_BR_COMMAND_CSUM_MASK		0xFF
  #define PMC_BR_COMMAND_CSUM_SHIFT		16
  #define PMC_BR_COMMAND_PMUX_MASK		0x7
  #define PMC_BR_COMMAND_PMUX_SHIFT		24
  #define PMC_BR_COMMAND_CTRL_ID_MASK		0x7
  #define PMC_BR_COMMAND_CTRL_ID_SHIFT		27
  #define PMC_BR_COMMAND_CTRL_TYPE_SHIFT		30
  #define PMC_BR_COMMAND_RST_EN_SHIFT		31
  
  /* io dpd off request code */
  #define IO_DPD_CODE_OFF		1
  
  /*** Tegra186 register definitions **/
  #define PMC_IMPL_HALT_IN_FIQ_MASK		BIT(28)
  
  #define PMC_RST_LEVEL_MASK			0x3
  #define PMC_RST_LEVEL_SHIFT			0x0
  #define PMC_RST_SOURCE_MASK			0x3C
  #define PMC_RST_SOURCE_SHIFT			0x2
  #define T210_PMC_RST_LEVEL_MASK			0x7
  
  #define TEGRA210_PMC_DPD_PADS_ORIDE_BLINK	BIT(20)
  #define TEGRA210_PMC_CTRL_BLINK_EN		BIT(7)
  
  /*** Tegra210b01 led soft blink **/
  #define PMC_LED_BREATHING_EN		BIT(0)
  #define PMC_SHORT_LOW_PERIOD_EN	BIT(1)
  #define PMC_LED_SOFT_BLINK_1CYCLE_NS	32000000
  
  #define NR_SMC_REGS		6
  #define PMC_READ		0xaa
  #define PMC_WRITE		0xbb
  #define TEGRA_SIP_PMC_COMMAND_FID 0xC2FFFE00
  
  struct pmc_smc_regs {
  	u64 args[NR_SMC_REGS];
  };
  
  static void send_smc(u32 func, struct pmc_smc_regs *regs)
  {
  	u32 ret = func;
  
  	asm volatile(
  		"mov x0, %0\n"
  		"ldp x1, x2, [%1, #16 * 0]\n"
  		"ldp x3, x4, [%1, #16 * 1]\n"
  		"ldp x5, x6, [%1, #16 * 2]\n"
  		"smc #0\n"
  		"mov %0, x0\n"
  		"stp x1, x2, [%1, #16 * 0]\n"
  		: "+r" (ret)
  		: "r" (regs)
  		: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8",
  		  "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17");
  	if (ret) {
  			pr_err("%s: failed (ret=%d)\n", __func__, ret);
  			WARN_ON(1);
  	}
  }
  
  struct io_dpd_reg_info {
  	u32 req_reg_off;
  	u8 dpd_code_lsb;
  };
  
  static struct io_dpd_reg_info t3_io_dpd_req_regs[] = {
  	{0x1b8, 30},
  	{0x1c0, 30},
  };
  
  enum pmc_regs {
  	TEGRA_PMC_CNTRL,
  	TEGRA_PMC_WAKE_MASK,
  	TEGRA_PMC_WAKE_LEVEL,
  	TEGRA_PMC_WAKE_STATUS,
  	TEGRA_PMC_WAKE_DELAY,
  	TEGRA_PMC_SW_WAKE_STATUS,
  	TEGRA_PMC_DPD_PADS_ORIDE,
  	TEGRA_PMC_BLINK_TIMER,
  	TEGRA_PMC_WAKE2_MASK,
  	TEGRA_PMC_WAKE2_LEVEL,
  	TEGRA_PMC_WAKE2_STATUS,
  	TEGRA_PMC_SW_WAKE2_STATUS,
  	TEGRA_PMC_IO_DPD_SAMPLE,
  	TEGRA_PMC_IO_DPD_ENABLE,
  	TEGRA_PMC_IO_DPD_REQ,
  	TEGRA_PMC_IO_DPD_STATUS,
  	TEGRA_PMC_IO_DPD2_REQ,
  	TEGRA_PMC_IO_DPD2_STATUS,
  	TEGRA_PMC_SEL_DPD_TIM,
  	TEGRA_PMC_PWR_NO_IOPOWER,
  	TEGRA_PMC_PWR_DET_ENABLE,
  	TEGRA_PMC_PWR_DET_VAL,
  	TEGRA_PMC_REMOVE_CLAMPING,
  	TEGRA_PMC_PWRGATE_TOGGLE,
  	TEGRA_PMC_PWRGATE_STATUS,
  	TEGRA_PMC_COREPWRGOOD_TIMER,
  	TEGRA_PMC_CPUPWRGOOD_TIMER,
  	TEGRA_PMC_CPUPWROFF_TIMER,
  	TEGRA_PMC_COREPWROFF_TIMER,
  	TEGRA_PMC_SENSOR_CTRL,
  	TEGRA_PMC_GPU_RG_CNTRL,
  	TEGRA_PMC_FUSE_CTRL,
  	TEGRA_PMC_BR_COMMAND_BASE,
  	TEGRA_PMC_SCRATCH0,
  	TEGRA_PMC_SCRATCH1,
  	TEGRA_PMC_SCRATCH37,
  	TEGRA_PMC_SCRATCH41,
  	TEGRA_PMC_SCRATCH43,
  	TEGRA_PMC_SCRATCH54,
  	TEGRA_PMC_SCRATCH55,
  	TEGRA_PMC_RST_STATUS,
  	TEGRA_PMC_IMPL_RAMDUMP_CTL_STATUS,
  	TEGRA_PMC_SATA_PWRGT_0,
  	TEGRA_PMC_UFSHC_PWR_CNTRL_0,
  	TEGRA_PMC_E_33V_PWR,
  	TEGRA_PMC_E_18V_PWR,
  	TEGRA_PMC_LED_BREATHING_CTRL,
  	TEGRA_PMC_LED_BREATHING_COUNTER0,
  	TEGRA_PMC_LED_BREATHING_COUNTER1,
  	TEGRA_PMC_LED_BREATHING_COUNTER2,
  	TEGRA_PMC_LED_BREATHING_COUNTER3,
  	TEGRA_PMC_LED_BREATHING_STATUS,
  
  	/* Last entry */
  	TEGRA_PMC_MAX_REG,
  };
  
  static bool get_secure_pmc_setting(void);
  static u32 tegra_pmc_reg_readl(enum pmc_regs reg);
  static void tegra_pmc_reg_writel(u32 value, enum pmc_regs reg);
  
  /* Bootrom commands structures */
  struct tegra_bootrom_block {
  	const char *name;
  	int address;
  	bool reg_8bits;
  	bool data_8bits;
  	bool i2c_controller;
  	int controller_id;
  	bool enable_reset;
  	int ncommands;
  	u32 *commands;
  };
  
  struct tegra_bootrom_commands {
  	u32 command_retry_count;
  	u32 delay_between_commands;
  	u32 wait_before_bus_clear;
  	struct tegra_bootrom_block *blocks;
  	int nblocks;
  };
  
  static struct tegra_bootrom_commands *br_rst_commands;
  static struct tegra_bootrom_commands *br_off_commands;
  
  #define PMC_PWR_NO_IOPOWER	0x44
  
  static DEFINE_SPINLOCK(pwr_lock);
  
  struct tegra_pmc_io_pad_soc {
  	const char *name;
  	unsigned int dpd;
  	unsigned int voltage;
  	unsigned int io_power;
  	const unsigned int pins[1];
  	unsigned int npins;
  	int dpd_req_reg;
  	int dpd_status_reg;
  	int dpd_timer_reg;
  	int dpd_sample_reg;
  	bool bdsdmem_cfc;
  	unsigned int io_pad_pwr_det_enable_reg;
  	unsigned int io_pad_pwr_det_val_reg;
  	int pad_uv_0;
  	int pad_uv_1;
  };
  
  struct tegra_pmc_soc {
  	unsigned int num_powergates;
  	const char *const *powergates;
  	unsigned int num_cpu_powergates;
  	const u8 *cpu_powergates;
  	const struct tegra_pmc_io_pad_soc *io_pads;
  	unsigned int num_io_pads;
  	const struct pinctrl_pin_desc *descs;
  	unsigned int num_descs;
  	const unsigned long *rmap;
  
  	bool has_tsense_reset;
  	bool has_gpu_clamps;
  	bool has_ps18;
  	bool has_bootrom_command;
  	bool has_pclk_clock;
  	bool has_interrupt_polarity_support;
  	bool has_reboot_base_address;
  	bool show_legacy_reset_status;
  	bool skip_lp0_vector_setup;
  	bool skip_legacy_pmc_init;
  	bool skip_power_gate_debug_fs_init;
  	bool skip_restart_register;
  	bool skip_arm_pm_restart;
  	bool has_misc_base_address;
  	bool sata_power_gate_in_misc;
  };
  
  struct tegra_io_pad_regulator {
  	const struct tegra_pmc_io_pad_soc *pad;
  	struct regulator *regulator;
  	struct notifier_block nb;
  };
  
  /**
   * struct tegra_pmc - NVIDIA Tegra PMC
   * @base: pointer to I/O remapped register region
   * @reboot_base: pointer to I/O remapped register region for reboot address
   * @early_base: pointer to I/O remapped register region for early init
   * @clk: pointer to pclk clock
   * @rate: currently configured rate of pclk
   * @suspend_mode: lowest suspend mode available
   * @cpu_good_time: CPU power good time (in microseconds)
   * @cpu_off_time: CPU power off time (in microsecends)
   * @core_osc_time: core power good OSC time (in microseconds)
   * @core_pmu_time: core power good PMU time (in microseconds)
   * @core_off_time: core power off time (in microseconds)
   * @corereq_high: core power request is active-high
   * @sysclkreq_high: system clock request is active-high
   * @combined_req: combined power request for CPU & core
   * @cpu_pwr_good_en: CPU power good signal is enabled
   * @lp0_vec_phys: physical base address of the LP0 warm boot code
   * @lp0_vec_size: size of the LP0 warm boot code
   * @powergates_lock: mutex for power gate register access
   * @pctl: pinctrl handle which is returned after registering pinctrl
   * @pinctrl_desc: Pincontrol descriptor for IO pads
   */
  struct tegra_pmc {
  	struct device *dev;
  	void __iomem *base;
  	void __iomem *reboot_base;
  	void __iomem *early_base;
  	void __iomem *misc_base;
  	struct clk *clk;
  
  	const struct tegra_pmc_soc *soc;
  
  	unsigned long rate;
  
  	enum tegra_suspend_mode suspend_mode;
  	u32 cpu_good_time;
  	u32 cpu_off_time;
  	u32 core_osc_time;
  	u32 core_pmu_time;
  	u32 core_off_time;
  	bool corereq_high;
  	bool sysclkreq_high;
  	bool combined_req;
  	bool cpu_pwr_good_en;
  	u32 lp0_vec_phys;
  	u32 lp0_vec_size;
  
  	struct mutex powergates_lock;
  	struct pinctrl_dev *pctl;
  	struct pinctrl_desc pinctrl_desc;
  	bool *allow_dynamic_switch;
  	bool voltage_switch_restriction_enabled;
  	struct tegra_prod *tprod;
  };
  
  #ifdef CONFIG_PM_SLEEP
  #define PMC_WAKE_TYPE_INDEX	0
  #define PMC_WAKE_MASK_INDEX	1
  #define PMC_TRIGGER_TYPE_INDEX	2
  #define PMC_OF_ARGS_COUNT	3
  struct pmc_wakeup {
  	u32 wake_type;
  	u32 wake_mask_offset;
  	u32 irq_num;
  	struct list_head list;
  };
  
  struct pmc_lp0_wakeup {
  	struct device_node *of_node;
  	u64 enable;
  	u64 level;
  	u64 level_any;
  	struct list_head wake_list;
  };
  static struct pmc_lp0_wakeup tegra_lp0_wakeup;
  static u32 io_dpd_reg, io_dpd2_reg;
  #endif
  
  static struct tegra_pmc *pmc = &(struct tegra_pmc) {
  	.base = NULL,
  	.suspend_mode = TEGRA_SUSPEND_NONE,
  	.lp0_vec_phys = 0,
  	.lp0_vec_size = 0,
  };
  
  static const char * const nvcsi_ab_bricks_pads[] = {
  	"csia",
  	"csib",
  };
  
  static const char * const nvcsi_cdef_bricks_pads[] = {
  	"csic",
  	"csid",
  	"csie",
  	"csif",
  };
  
  /* PMC misc register read/write with pmc register enums */
  static u32 tegra_pmc_misc_readl(enum pmc_regs reg)
  {
  	return readl(pmc->misc_base + pmc->soc->rmap[reg]);
  }
  
  static void tegra_pmc_misc_writel(u32 value, enum pmc_regs reg)
  {
  	writel(value, pmc->misc_base + pmc->soc->rmap[reg]);
  }
  
  static void tegra_pmc_misc_register_update(enum pmc_regs reg,
  					   unsigned long mask,
  					   unsigned long val)
  {
  	u32 pmc_reg;
  
  	pmc_reg = tegra_pmc_misc_readl(reg);
  	pmc_reg = (pmc_reg & ~mask) | (val & mask);
  	tegra_pmc_misc_writel(pmc_reg, reg);
  }
  
  static void tegra_pmc_register_update(enum pmc_regs reg,
  				      unsigned long mask,
  				      unsigned long val)
  {
  	u32 pmc_reg;
  
  	pmc_reg = tegra_pmc_reg_readl(reg);
  	pmc_reg = (pmc_reg & ~mask) | (val & mask);
  	tegra_pmc_reg_writel(pmc_reg, reg);
  }
  
  #ifdef CONFIG_PM_SLEEP
  int tegra_read_wake_status(u32 *wake_status)
  {
  	if (soc_is_tegra186_n_later())
  		return tegra18x_read_wake_status(wake_status);
  
  	return 0;
  }
  #endif
  
  #ifndef CONFIG_TEGRA_POWERGATE
  /**
   * tegra_powergate_set() - set the state of a partition
   * @id: partition ID
   * @new_state: new state of the partition
   */
  static int tegra_powergate_set(int id, bool new_state)
  {
  	bool status;
  
  	mutex_lock(&pmc->powergates_lock);
  
  	status = tegra_pmc_reg_readl(TEGRA_PMC_PWRGATE_STATUS) & (1 << id);
  
  	if (status == new_state) {
  		mutex_unlock(&pmc->powergates_lock);
  		return 0;
  	}
  
  	tegra_pmc_reg_writel(PWRGATE_TOGGLE_START | id,
  			     TEGRA_PMC_PWRGATE_TOGGLE);
  
  	mutex_unlock(&pmc->powergates_lock);
  
  	return 0;
  }
  
  /**
   * tegra_powergate_power_on() - power on partition
   * @id: partition ID
   */
  int tegra_powergate_power_on(int id)
  {
  	if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
  		return -EINVAL;
  
  	return tegra_powergate_set(id, true);
  }
  
  /**
   * tegra_powergate_power_off() - power off partition
   * @id: partition ID
   */
  int tegra_powergate_power_off(int id)
  {
  	if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
  		return -EINVAL;
  
  	return tegra_powergate_set(id, false);
  }
  EXPORT_SYMBOL(tegra_powergate_power_off);
  
  /**
   * tegra_powergate_is_powered() - check if partition is powered
   * @id: partition ID
   */
  int tegra_powergate_is_powered(int id)
  {
  	u32 status;
  
  	if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
  		return -EINVAL;
  
  	status = tegra_pmc_reg_readl(TEGRA_PMC_PWRGATE_STATUS) & (1 << id);
  	return !!status;
  }
  
  /**
   * tegra_powergate_remove_clamping() - remove power clamps for partition
   * @id: partition ID
   */
  int tegra_powergate_remove_clamping(int id)
  {
  	u32 mask;
  
  	if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
  		return -EINVAL;
  
  	/*
  	 * On Tegra124 and later, the clamps for the GPU are controlled by a
  	 * separate register (with different semantics).
  	 */
  	if (id == TEGRA_POWERGATE_3D) {
  		if (pmc->soc->has_gpu_clamps) {
  			tegra_pmc_reg_writel(0, TEGRA_PMC_GPU_RG_CNTRL);
  			return 0;
  		}
  	}
  
  	/*
  	 * Tegra 2 has a bug where PCIE and VDE clamping masks are
  	 * swapped relatively to the partition ids
  	 */
  	if (id == TEGRA_POWERGATE_VDEC)
  		mask = (1 << TEGRA_POWERGATE_PCIE);
  	else if (id == TEGRA_POWERGATE_PCIE)
  		mask = (1 << TEGRA_POWERGATE_VDEC);
  	else
  		mask = (1 << id);
  
  	tegra_pmc_reg_writel(mask, TEGRA_PMC_REMOVE_CLAMPING);
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_powergate_remove_clamping);
  
  /**
   * tegra_powergate_sequence_power_up() - power up partition
   * @id: partition ID
   * @clk: clock for partition
   * @rst: reset for partition
   *
   * Must be called with clk disabled, and returns with clk enabled.
   */
  int tegra_powergate_sequence_power_up(int id, struct clk *clk,
  				      struct reset_control *rst)
  {
  	int ret;
  
  	reset_control_assert(rst);
  
  	ret = tegra_powergate_power_on(id);
  	if (ret)
  		goto err_power;
  
  	ret = clk_prepare_enable(clk);
  	if (ret)
  		goto err_clk;
  
  	usleep_range(10, 20);
  
  	ret = tegra_powergate_remove_clamping(id);
  	if (ret)
  		goto err_clamp;
  
  	usleep_range(10, 20);
  	reset_control_deassert(rst);
  
  	return 0;
  
  err_clamp:
  	clk_disable_unprepare(clk);
  err_clk:
  	tegra_powergate_power_off(id);
  err_power:
  	return ret;
  }
  EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
  
  #ifdef CONFIG_SMP
  /**
   * tegra_get_cpu_powergate_id() - convert from CPU ID to partition ID
   * @cpuid: CPU partition ID
   *
   * Returns the partition ID corresponding to the CPU partition ID or a
   * negative error code on failure.
   */
  static int tegra_get_cpu_powergate_id(int cpuid)
  {
  	if (pmc->soc && cpuid > 0 && cpuid < pmc->soc->num_cpu_powergates)
  		return pmc->soc->cpu_powergates[cpuid];
  
  	return -EINVAL;
  }
  
  /**
   * tegra_pmc_cpu_is_powered() - check if CPU partition is powered
   * @cpuid: CPU partition ID
   */
  bool tegra_pmc_cpu_is_powered(int cpuid)
  {
  	int id;
  
  	id = tegra_get_cpu_powergate_id(cpuid);
  	if (id < 0)
  		return false;
  
  	return tegra_powergate_is_powered(id);
  }
  
  /**
   * tegra_pmc_cpu_power_on() - power on CPU partition
   * @cpuid: CPU partition ID
   */
  int tegra_pmc_cpu_power_on(int cpuid)
  {
  	int id;
  
  	id = tegra_get_cpu_powergate_id(cpuid);
  	if (id < 0)
  		return id;
  
  	return tegra_powergate_set(id, true);
  }
  
  /**
   * tegra_pmc_cpu_remove_clamping() - remove power clamps for CPU partition
   * @cpuid: CPU partition ID
   */
  int tegra_pmc_cpu_remove_clamping(int cpuid)
  {
  	int id;
  
  	id = tegra_get_cpu_powergate_id(cpuid);
  	if (id < 0)
  		return id;
  
  	return tegra_powergate_remove_clamping(id);
  }
  #endif /* CONFIG_SMP */
  #endif /* CONFIG_TEGRA_POWERGATE */
  
  /**
   * tegra_pmc_get_system_reset_reason() - last reset reason status
   */
  enum tegra_system_reset_reason tegra_pmc_get_system_reset_reason(void)
  {
  	u32 reset_reg, rst_src;
  	enum tegra_system_reset_reason tegra_rst_rsn_sts;
  
  	reset_reg = tegra_pmc_reg_readl(TEGRA_PMC_RST_STATUS);
  
  	if (pmc->soc->show_legacy_reset_status) {
  		rst_src = reset_reg & T210_PMC_RST_LEVEL_MASK;
  		switch (rst_src) {
  		case 0:
  			tegra_rst_rsn_sts = TEGRA_POWER_ON_RESET;
  			break;
  
  		case 1:
  			tegra_rst_rsn_sts = TEGRA_WATCHDOG;
  			break;
  
  		case 2:
  			tegra_rst_rsn_sts = TEGRA_SENSOR;
  			break;
  
  		case 3:
  			tegra_rst_rsn_sts = TEGRA_SOFTWARE_RESET;
  			break;
  
  		case 4:
  			tegra_rst_rsn_sts = TEGRA_LP0;
  			break;
  
  		case 5:
  			tegra_rst_rsn_sts = TEGRA_AOTAG;
  			break;
  
  		default:
  			tegra_rst_rsn_sts = TEGRA_RESET_REASON_MAX;
  		}
  	} else {
  		rst_src = (reset_reg & PMC_RST_SOURCE_MASK) >> PMC_RST_SOURCE_SHIFT;
  		tegra_rst_rsn_sts = rst_src;
  	}
  
  	return tegra_rst_rsn_sts;
  }
  EXPORT_SYMBOL(tegra_pmc_get_system_reset_reason);
  
  enum tegra_system_reset_level tegra_pmc_get_system_reset_level(void)
  {
  	u32 rst_lvl, reset_reg;
  	enum tegra_system_reset_level tegra_rst_lvl_sts;
  
  	reset_reg = tegra_pmc_reg_readl(TEGRA_PMC_RST_STATUS);
  
  	if (pmc->soc->show_legacy_reset_status) {
  		tegra_rst_lvl_sts = TEGRA_RESET_LEVEL_MAX;
  	} else {
  		rst_lvl = (reset_reg & PMC_RST_LEVEL_MASK) >> PMC_RST_LEVEL_SHIFT;
  		tegra_rst_lvl_sts = rst_lvl;
  	}
  
  	return tegra_rst_lvl_sts;
  }
  
  static void tegra_pmc_program_reboot_reason(const char *cmd)
  {
  	u32 value;
  
  	value = tegra_pmc_reg_readl(TEGRA_PMC_SCRATCH0);
  	value &= ~PMC_SCRATCH0_MODE_MASK;
  
  	if (cmd) {
  		if (strcmp(cmd, "recovery") == 0)
  			value |= PMC_SCRATCH0_MODE_RECOVERY;
  
  		if (strcmp(cmd, "bootloader") == 0)
  			value |= PMC_SCRATCH0_MODE_BOOTLOADER;
  
  		if (strcmp(cmd, "forced-recovery") == 0)
  			value |= PMC_SCRATCH0_MODE_RCM;
  	}
  
  	tegra_pmc_reg_writel(value, TEGRA_PMC_SCRATCH0);
  }
  
  static int tegra_pmc_restart_notify(struct notifier_block *this,
  				    unsigned long action, void *data)
  {
  	const char *cmd = data;
  	u32 value;
  
  	tegra_pmc_program_reboot_reason(cmd);
  
  	value = tegra_pmc_reg_readl(TEGRA_PMC_CNTRL);
  	value |= 0x10;
  	tegra_pmc_reg_writel(value, TEGRA_PMC_CNTRL);
  
  	return NOTIFY_DONE;
  }
  
  static struct notifier_block tegra_pmc_restart_handler = {
  	.notifier_call = tegra_pmc_restart_notify,
  	.priority = 128,
  };
  
  static int tegra_pmc_panic_handler(struct notifier_block *this,
  				    unsigned long action, void *data)
  {
  	u32 pmc_reg_val;
  
  	pmc_reg_val = tegra_pmc_reg_readl(TEGRA_PMC_SCRATCH37);
  	tegra_pmc_reg_writel((pmc_reg_val | KERNEL_PANIC_FLAG), TEGRA_PMC_SCRATCH37);
  
  	return NOTIFY_DONE;
  }
  
  
  struct notifier_block tegra_pmc_panic_notifier = {
  	.notifier_call = tegra_pmc_panic_handler,
  	.priority      = INT_MAX-1,
  };
  
  #ifndef CONFIG_TEGRA_POWERGATE
  static int powergate_show(struct seq_file *s, void *data)
  {
  	unsigned int i;
  
  	seq_printf(s, " powergate powered\n");
  	seq_printf(s, "------------------\n");
  
  	for (i = 0; i < pmc->soc->num_powergates; i++) {
  		if (!pmc->soc->powergates[i])
  			continue;
  
  		seq_printf(s, " %9s %7s\n", pmc->soc->powergates[i],
  			   tegra_powergate_is_powered(i) ? "yes" : "no");
  	}
  
  	return 0;
  }
  
  static int powergate_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, powergate_show, inode->i_private);
  }
  
  static const struct file_operations powergate_fops = {
  	.open = powergate_open,
  	.read = seq_read,
  	.llseek = seq_lseek,
  	.release = single_release,
  };
  
  static int tegra_powergate_debugfs_init(void)
  {
  	struct dentry *d;
  
  	if (pmc->soc->skip_power_gate_debug_fs_init)
  		return 0;
  
  	d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
  				&powergate_fops);
  	if (!d)
  		return -ENOMEM;
  
  	return 0;
  }
  #else
  static int tegra_powergate_debugfs_init(void)
  {
  	return 0;
  }
  #endif
  
  #ifndef CONFIG_TEGRA_POWERGATE
  /* Legacy APIs for IO pad enable/disable */
  int tegra_io_rail_power_on(int id)
  {
  	switch (id) {
  	case TEGRA_IO_RAIL_LVDS:
  		return tegra_pmc_io_pad_low_power_disable("lvds");
  	case TEGRA_IO_RAIL_HDMI:
  		return tegra_pmc_io_pad_low_power_disable("hdmi");
  	default:
  		return -EINVAL;
  	}
  }
  EXPORT_SYMBOL(tegra_io_rail_power_on);
  
  int tegra_io_rail_power_off(int id)
  {
  	switch (id) {
  	case TEGRA_IO_RAIL_LVDS:
  		return tegra_pmc_io_pad_low_power_enable("lvds");
  	case TEGRA_IO_RAIL_HDMI:
  		return tegra_pmc_io_pad_low_power_enable("hdmi");
  	default:
  		return -EINVAL;
  	}
  }
  EXPORT_SYMBOL(tegra_io_rail_power_off);
  #endif /* CONFIG_TEGRA_POWERGATE */
  
  void tegra_pmc_write_bootrom_command(u32 command_offset, unsigned long val)
  {
  	tegra_pmc_writel(val, command_offset + PMC_BR_COMMAND_BASE);
  }
  EXPORT_SYMBOL(tegra_pmc_write_bootrom_command);
  
  void tegra_pmc_reset_system(void)
  {
  	u32 val;
  
  	val = tegra_pmc_reg_readl(TEGRA_PMC_CNTRL);
  	val |= 0x10;
  	tegra_pmc_reg_writel(val, TEGRA_PMC_CNTRL);
  }
  EXPORT_SYMBOL(tegra_pmc_reset_system);
  
  int tegra_pmc_clear_reboot_reason(u32 reason)
  {
  	u32 val;
  
  	val = readl_relaxed(pmc->reboot_base +
  			    pmc->soc->rmap[TEGRA_PMC_SCRATCH0]);
  	val &= ~reason;
  	writel_relaxed(val, pmc->reboot_base +
  		       pmc->soc->rmap[TEGRA_PMC_SCRATCH0]);
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_clear_reboot_reason);
  
  int tegra_pmc_set_reboot_reason(u32 reason)
  {
  	u32 val;
  
  	val = readl_relaxed(pmc->reboot_base +
  			    pmc->soc->rmap[TEGRA_PMC_SCRATCH0]);
  	val |= reason;
  	writel_relaxed(val, pmc->reboot_base +
  		       pmc->soc->rmap[TEGRA_PMC_SCRATCH0]);
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_set_reboot_reason);
  
  /* UFS power gate control */
  void tegra_pmc_ufs_pwrcntrl_update(unsigned long mask, unsigned long val)
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&pwr_lock, flags);
  	tegra_pmc_register_update(TEGRA_PMC_UFSHC_PWR_CNTRL_0, mask, val);
  	spin_unlock_irqrestore(&pwr_lock, flags);
  }
  EXPORT_SYMBOL(tegra_pmc_ufs_pwrcntrl_update);
  
  unsigned long tegra_pmc_ufs_pwrcntrl_get(void)
  {
  	return tegra_pmc_reg_readl(TEGRA_PMC_UFSHC_PWR_CNTRL_0);
  }
  EXPORT_SYMBOL(tegra_pmc_ufs_pwrcntrl_get);
  
  /* SATA power gate control */
  void tegra_pmc_sata_pwrgt_update(unsigned long mask, unsigned long val)
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&pwr_lock, flags);
  	if (pmc->soc->sata_power_gate_in_misc)
  		tegra_pmc_misc_register_update(TEGRA_PMC_SATA_PWRGT_0,
  					       mask, val);
  	else
  		tegra_pmc_register_update(TEGRA_PMC_SATA_PWRGT_0,
  					  mask, val);
  	spin_unlock_irqrestore(&pwr_lock, flags);
  }
  EXPORT_SYMBOL(tegra_pmc_sata_pwrgt_update);
  
  unsigned long tegra_pmc_sata_pwrgt_get(void)
  {
  	if (pmc->soc->sata_power_gate_in_misc)
  		return tegra_pmc_misc_readl(TEGRA_PMC_SATA_PWRGT_0);
  	else
  		return tegra_pmc_reg_readl(TEGRA_PMC_SATA_PWRGT_0);
  }
  EXPORT_SYMBOL(tegra_pmc_sata_pwrgt_get);
  
  int tegra_pmc_save_se_context_buffer_address(u32 add)
  {
  	tegra_pmc_reg_writel(add, TEGRA_PMC_SCRATCH43);
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_save_se_context_buffer_address);
  
  u32 tegra_pmc_get_se_context_buffer_address(void)
  {
  	return tegra_pmc_reg_readl(TEGRA_PMC_SCRATCH43);
  }
  EXPORT_SYMBOL(tegra_pmc_get_se_context_buffer_address);
  
  /* cleans io dpd settings from bootloader during kernel init */
  static void _tegra_bl_io_dpd_cleanup(void)
  {
  	int i;
  	unsigned int dpd_mask;
  	unsigned int dpd_status;
  
  	pr_info("Clear bootloader IO dpd settings\n");
  	/* clear all dpd requests from bootloader */
  	for (i = 0; i < ARRAY_SIZE(t3_io_dpd_req_regs); i++) {
  		dpd_mask = ((1 << t3_io_dpd_req_regs[i].dpd_code_lsb) - 1);
  		dpd_mask |= (IO_DPD_CODE_OFF <<
  			t3_io_dpd_req_regs[i].dpd_code_lsb);
  		tegra_pmc_writel(dpd_mask, t3_io_dpd_req_regs[i].req_reg_off);
  		/* dpd status register is next to req reg in tegra3 */
  		dpd_status =
  			tegra_pmc_readl(t3_io_dpd_req_regs[i].req_reg_off + 4);
  	}
  	return;
  }
  
  void tegra_pmc_io_dpd_clear(void)
  {
  	_tegra_bl_io_dpd_cleanup();
  }
  EXPORT_SYMBOL(tegra_pmc_io_dpd_clear);
  
  /* T210 USB2 SLEEPWALK APIs */
  int tegra_pmc_utmi_phy_enable_sleepwalk(int port, enum usb_device_speed speed,
  					struct tegra_utmi_pad_config *config)
  {
  	u32 reg;
  
  	pr_info("PMC %s : port %d, speed %d\n", __func__, port, speed);
  
  	/* ensure sleepwalk logic is disabled */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  	reg &= ~UTMIP_MASTER_ENABLE(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  
  	/* ensure sleepwalk logics are in low power mode */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_MASTER_CONFIG);
  	reg |= UTMIP_PWR(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_MASTER_CONFIG);
  
  	/* set debounce time */
  	reg = tegra_pmc_readl(APBDEV_PMC_USB_DEBOUNCE_DEL);
  	reg &= ~UTMIP_LINE_DEB_CNT(~0);
  	reg |= UTMIP_LINE_DEB_CNT(0x1);
  	tegra_pmc_writel(reg, APBDEV_PMC_USB_DEBOUNCE_DEL);
  
  	/* ensure fake events of sleepwalk logic are desiabled */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_FAKE(port));
  	reg &= ~(UTMIP_FAKE_USBOP_VAL(port) | UTMIP_FAKE_USBON_VAL(port) |
  			UTMIP_FAKE_USBOP_EN(port) | UTMIP_FAKE_USBON_EN(port));
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_FAKE(port));
  
  	/* ensure wake events of sleepwalk logic are not latched */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP);
  	reg &= ~UTMIP_LINE_WAKEUP_EN(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP);
  
  	/* disable wake event triggers of sleepwalk logic */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  	reg &= ~UTMIP_WAKE_VAL(port, ~0);
  	reg |= UTMIP_WAKE_VAL_NONE(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  
  	/* power down the line state detectors of the pad */
  	reg = tegra_pmc_readl(APBDEV_PMC_USB_AO);
  	reg |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
  	tegra_pmc_writel(reg, APBDEV_PMC_USB_AO);
  
  	/* save state per speed */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_SAVED_STATE(port));
  	reg &= ~SPEED(port, ~0);
  	if (speed == USB_SPEED_HIGH)
  		reg |= UTMI_HS(port);
  	else if (speed == USB_SPEED_FULL)
  		reg |= UTMI_FS(port);
  	else if (speed == USB_SPEED_LOW)
  		reg |= UTMI_LS(port);
  	else
  		reg |= UTMI_RST(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_SAVED_STATE(port));
  
  	/* enable the trigger of the sleepwalk logic */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port));
  	reg |= UTMIP_LINEVAL_WALK_EN(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port));
  
  	/* reset the walk pointer and clear the alarm of the sleepwalk logic,
  	 * as well as capture the configuration of the USB2.0 pad
  	 */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_TRIGGERS);
  	reg |= (UTMIP_CLR_WALK_PTR(port) | UTMIP_CLR_WAKE_ALARM(port) |
  		UTMIP_CAP_CFG(port));
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_TRIGGERS);
  
  	/* program electrical parameters read from XUSB PADCTL */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_TERM_PAD_CFG);
  	reg &= ~(TCTRL_VAL(~0) | PCTRL_VAL(~0));
  	reg |= (TCTRL_VAL(config->tctrl) | PCTRL_VAL(config->pctrl));
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_TERM_PAD_CFG);
  
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_PAD_CFGX(port));
  	reg &= ~RPD_CTRL_PX(~0);
  	reg |= RPD_CTRL_PX(config->rpd_ctrl);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_PAD_CFGX(port));
  
  	/* setup the pull-ups and pull-downs of the signals during the four
  	 * stages of sleepwalk.
  	 * if device is connected, program sleepwalk logic to maintain a J and
  	 * keep driving K upon seeing remote wake.
  	 */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_SLEEPWALK_PX(port));
  	reg = (UTMIP_USBOP_RPD_A | UTMIP_USBOP_RPD_B | UTMIP_USBOP_RPD_C |
  		UTMIP_USBOP_RPD_D);
  	reg |= (UTMIP_USBON_RPD_A | UTMIP_USBON_RPD_B | UTMIP_USBON_RPD_C |
  		UTMIP_USBON_RPD_D);
  	if (speed == USB_SPEED_UNKNOWN) {
  		reg |= (UTMIP_HIGHZ_A | UTMIP_HIGHZ_B | UTMIP_HIGHZ_C |
  			UTMIP_HIGHZ_D);
  	} else if ((speed == USB_SPEED_HIGH) || (speed == USB_SPEED_FULL)) {
  		/* J state: D+/D- = high/low, K state: D+/D- = low/high */
  		reg |= UTMIP_HIGHZ_A;
  		reg |= UTMIP_AP_A;
  		reg |= (UTMIP_AN_B | UTMIP_AN_C | UTMIP_AN_D);
  	} else if (speed == USB_SPEED_LOW) {
  		/* J state: D+/D- = low/high, K state: D+/D- = high/low */
  		reg |= UTMIP_HIGHZ_A;
  		reg |= UTMIP_AN_A;
  		reg |= (UTMIP_AP_B | UTMIP_AP_C | UTMIP_AP_D);
  	}
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_SLEEPWALK_PX(port));
  
  	/* power up the line state detectors of the pad */
  	reg = tegra_pmc_readl(APBDEV_PMC_USB_AO);
  	reg &= ~(USBOP_VAL_PD(port) | USBON_VAL_PD(port));
  	tegra_pmc_writel(reg, APBDEV_PMC_USB_AO);
  
  	usleep_range(50, 100);
  
  	/* switch the electric control of the USB2.0 pad to PMC */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  	reg |= (UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) |
  			UTMIP_TCTRL_USE_PMC(port));
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG1);
  	reg |= (UTMIP_RPD_CTRL_USE_PMC_PX(port) |
  			UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port));
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG1);
  
  	/* set the wake signaling trigger events */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  	reg &= ~UTMIP_WAKE_VAL(port, ~0);
  	reg |= UTMIP_WAKE_VAL_ANY(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  
  	/* enable the wake detection */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  	reg |= UTMIP_MASTER_ENABLE(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP);
  	reg |= UTMIP_LINE_WAKEUP_EN(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP);
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_utmi_phy_enable_sleepwalk);
  
  int tegra_pmc_utmi_phy_disable_sleepwalk(int port)
  {
  	u32 reg;
  
  	pr_info("PMC %s : port %d\n", __func__, port);
  
  	/* disable the wake detection */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  	reg &= ~UTMIP_MASTER_ENABLE(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP);
  	reg &= ~UTMIP_LINE_WAKEUP_EN(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP);
  
  	/* switch the electric control of the USB2.0 pad to XUSB or USB2 */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  	reg &= ~(UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) |
  			UTMIP_TCTRL_USE_PMC(port));
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG1);
  	reg &= ~(UTMIP_RPD_CTRL_USE_PMC_PX(port) |
  			UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port));
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG1);
  
  	/* disable wake event triggers of sleepwalk logic */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  	reg &= ~UTMIP_WAKE_VAL(port, ~0);
  	reg |= UTMIP_WAKE_VAL_NONE(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_SLEEP_CFG(port));
  
  	/* power down the line state detectors of the port */
  	reg = tegra_pmc_readl(APBDEV_PMC_USB_AO);
  	reg |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
  	tegra_pmc_writel(reg, APBDEV_PMC_USB_AO);
  
  	/* clear alarm of the sleepwalk logic */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_TRIGGERS);
  	reg |= UTMIP_CLR_WAKE_ALARM(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_TRIGGERS);
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_utmi_phy_disable_sleepwalk);
  
  int tegra_pmc_hsic_phy_enable_sleepwalk(int port)
  {
  	u32 reg;
  
  	pr_info("PMC %s : port %dn", __func__, port);
  
  	/* ensure sleepwalk logic is disabled */
  	reg = tegra_pmc_readl(APBDEV_PMC_UHSIC_SLEEP_CFG);
  	reg &= ~UHSIC_MASTER_ENABLE;
  	tegra_pmc_writel(reg, APBDEV_PMC_UHSIC_SLEEP_CFG);
  
  	/* ensure sleepwalk logics are in low power mode */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_MASTER_CONFIG);
  	reg |= UHSIC_PWR(port);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_MASTER_CONFIG);
  
  	/* set debounce time */
  	reg = tegra_pmc_readl(APBDEV_PMC_USB_DEBOUNCE_DEL);
  	reg &= ~UHSIC_LINE_DEB_CNT(~0);
  	reg |= UHSIC_LINE_DEB_CNT(0x1);
  	tegra_pmc_writel(reg, APBDEV_PMC_USB_DEBOUNCE_DEL);
  
  	/* ensure fake events of sleepwalk logic are desiabled */
  	reg = tegra_pmc_readl(APBDEV_PMC_UHSIC_FAKE);
  	reg &= ~(UHSIC_FAKE_STROBE_VAL | UHSIC_FAKE_DATA_VAL |
  			UHSIC_FAKE_STROBE_EN | UHSIC_FAKE_DATA_EN);
  	tegra_pmc_writel(reg, APBDEV_PMC_UHSIC_FAKE);
  
  	/* ensure wake events of sleepwalk logic are not latched */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP);
  	reg &= ~UHSIC_LINE_WAKEUP_EN;
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP);
  
  	/* disable wake event triggers of sleepwalk logic */
  	reg = tegra_pmc_readl(APBDEV_PMC_UHSIC_SLEEP_CFG);
  	reg &= ~UHSIC_WAKE_VAL(~0);
  	reg |= UHSIC_WAKE_VAL_NONE;
  	tegra_pmc_writel(reg, APBDEV_PMC_UHSIC_SLEEP_CFG);
  
  	/* power down the line state detectors of the port */
  	reg = tegra_pmc_readl(APBDEV_PMC_USB_AO);
  	reg |= (STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD);
  	tegra_pmc_writel(reg, APBDEV_PMC_USB_AO);
  
  	/* save state, HSIC always comes up as HS */
  	reg = tegra_pmc_readl(APBDEV_PMC_UHSIC_SAVED_STATE);
  	reg &= ~UHSIC_MODE(~0);
  	reg |= UHSIC_HS;
  	tegra_pmc_writel(reg, APBDEV_PMC_UHSIC_SAVED_STATE);
  
  	/* enable the trigger of the sleepwalk logic */
  	reg = tegra_pmc_readl(APBDEV_PMC_UHSIC_SLEEPWALK_CFG);
  	reg |= (UHSIC_WAKE_WALK_EN | UHSIC_LINEVAL_WALK_EN);
  	tegra_pmc_writel(reg, APBDEV_PMC_UHSIC_SLEEPWALK_CFG);
  
  	/* reset the walk pointer and clear the alarm of the sleepwalk logic,
  	 * as well as capture the configuration of the USB2.0 port
  	 */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_TRIGGERS);
  	reg |= (UHSIC_CLR_WALK_PTR | UHSIC_CLR_WAKE_ALARM);
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_TRIGGERS);
  
  	/* setup the pull-ups and pull-downs of the signals during the four
  	 * stages of sleepwalk.
  	 * maintain a HSIC IDLE and keep driving HSIC RESUME upon remote wake
  	 */
  	reg = tegra_pmc_readl(APBDEV_PMC_UHSIC_SLEEPWALK_P0);
  	reg = (UHSIC_DATA0_RPD_A | UHSIC_DATA0_RPU_B | UHSIC_DATA0_RPU_C |
  		UHSIC_DATA0_RPU_D);
  	reg |= (UHSIC_STROBE_RPU_A | UHSIC_STROBE_RPD_B | UHSIC_STROBE_RPD_C |
  		UHSIC_STROBE_RPD_D);
  	tegra_pmc_writel(reg, APBDEV_PMC_UHSIC_SLEEPWALK_P0);
  
  	/* power up the line state detectors of the port */
  	reg = tegra_pmc_readl(APBDEV_PMC_USB_AO);
  	reg &= ~(STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD);
  	tegra_pmc_writel(reg, APBDEV_PMC_USB_AO);
  
  	usleep_range(50, 100);
  
  	/* set the wake signaling trigger events */
  	reg = tegra_pmc_readl(APBDEV_PMC_UHSIC_SLEEP_CFG);
  	reg &= ~UHSIC_WAKE_VAL(~0);
  	reg |= UHSIC_WAKE_VAL_SD10;
  	tegra_pmc_writel(reg, APBDEV_PMC_UHSIC_SLEEP_CFG);
  
  	/* enable the wake detection */
  	reg = tegra_pmc_readl(APBDEV_PMC_UHSIC_SLEEP_CFG);
  	reg |= UHSIC_MASTER_ENABLE;
  	tegra_pmc_writel(reg, APBDEV_PMC_UHSIC_SLEEP_CFG);
  
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP);
  	reg |= UHSIC_LINE_WAKEUP_EN;
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP);
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_hsic_phy_enable_sleepwalk);
  
  int tegra_pmc_hsic_phy_disable_sleepwalk(int port)
  {
  	u32 reg;
  
  	pr_info("PMC %s : port %dn", __func__, port);
  
  	/* disable the wake detection */
  	reg = tegra_pmc_readl(APBDEV_PMC_UHSIC_SLEEP_CFG);
  	reg &= ~UHSIC_MASTER_ENABLE;
  	tegra_pmc_writel(reg, APBDEV_PMC_UHSIC_SLEEP_CFG);
  
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP);
  	reg &= ~UHSIC_LINE_WAKEUP_EN;
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_LINE_WAKEUP);
  
  	/* disable wake event triggers of sleepwalk logic */
  	reg = tegra_pmc_readl(APBDEV_PMC_UHSIC_SLEEP_CFG);
  	reg &= ~UHSIC_WAKE_VAL(~0);
  	reg |= UHSIC_WAKE_VAL_NONE;
  	tegra_pmc_writel(reg, APBDEV_PMC_UHSIC_SLEEP_CFG);
  
  	/* power down the line state detectors of the port */
  	reg = tegra_pmc_readl(APBDEV_PMC_USB_AO);
  	reg |= (STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD);
  	tegra_pmc_writel(reg, APBDEV_PMC_USB_AO);
  
  	/* clear alarm of the sleepwalk logic */
  	reg = tegra_pmc_readl(APBDEV_PMC_UTMIP_UHSIC_TRIGGERS);
  	reg |= UHSIC_CLR_WAKE_ALARM;
  	tegra_pmc_writel(reg, APBDEV_PMC_UTMIP_UHSIC_TRIGGERS);
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_hsic_phy_disable_sleepwalk);
  
  void tegra_pmc_fuse_control_ps18_latch_set(void)
  {
  	u32 val;
  
  	if (!pmc->soc->has_ps18)
  		return;
  
  	if (pmc->soc->has_misc_base_address) {
  		val = tegra_pmc_misc_readl(TEGRA_PMC_FUSE_CTRL);
  		val &= ~(PMC_FUSE_CTRL_PS18_LATCH_CLEAR);
  		tegra_pmc_misc_writel(val, TEGRA_PMC_FUSE_CTRL);
  		mdelay(1);
  		val |= PMC_FUSE_CTRL_PS18_LATCH_SET;
  		tegra_pmc_misc_writel(val, TEGRA_PMC_FUSE_CTRL);
  	} else {
  		val = tegra_pmc_reg_readl(TEGRA_PMC_FUSE_CTRL);
  		val &= ~(PMC_FUSE_CTRL_PS18_LATCH_CLEAR);
  		tegra_pmc_reg_writel(val, TEGRA_PMC_FUSE_CTRL);
  		mdelay(1);
  		val |= PMC_FUSE_CTRL_PS18_LATCH_SET;
  		tegra_pmc_reg_writel(val, TEGRA_PMC_FUSE_CTRL);
  	}
  
  	mdelay(1);
  }
  EXPORT_SYMBOL(tegra_pmc_fuse_control_ps18_latch_set);
  
  void tegra_pmc_fuse_control_ps18_latch_clear(void)
  {
  	u32 val;
  
  	if (!pmc->soc->has_ps18)
  		return;
  
  	if (pmc->soc->has_misc_base_address) {
  		val = tegra_pmc_misc_readl(TEGRA_PMC_FUSE_CTRL);
  		val &= ~(PMC_FUSE_CTRL_PS18_LATCH_SET);
  		tegra_pmc_misc_writel(val, TEGRA_PMC_FUSE_CTRL);
  		mdelay(1);
  		val |= PMC_FUSE_CTRL_PS18_LATCH_CLEAR;
  		tegra_pmc_misc_writel(val, TEGRA_PMC_FUSE_CTRL);
  	} else {
  		val = tegra_pmc_reg_readl(TEGRA_PMC_FUSE_CTRL);
  		val &= ~(PMC_FUSE_CTRL_PS18_LATCH_SET);
  		tegra_pmc_reg_writel(val, TEGRA_PMC_FUSE_CTRL);
  		mdelay(1);
  		val |= PMC_FUSE_CTRL_PS18_LATCH_CLEAR;
  		tegra_pmc_reg_writel(val, TEGRA_PMC_FUSE_CTRL);
  	}
  	mdelay(1);
  }
  EXPORT_SYMBOL(tegra_pmc_fuse_control_ps18_latch_clear);
  
  void tegra_pmc_fuse_disable_mirroring(void)
  {
  	u32 val;
  
  	if (pmc->soc->has_misc_base_address) {
  		val = tegra_pmc_misc_readl(TEGRA_PMC_FUSE_CTRL);
  		if (val & PMC_FUSE_CTRL_ENABLE_REDIRECTION) {
  			val &= ~PMC_FUSE_CTRL_ENABLE_REDIRECTION;
  			tegra_pmc_misc_writel(val, TEGRA_PMC_FUSE_CTRL);
  		}
  	} else {
  		val = tegra_pmc_reg_readl(TEGRA_PMC_FUSE_CTRL);
  		if (val & PMC_FUSE_CTRL_ENABLE_REDIRECTION) {
  			val &= ~PMC_FUSE_CTRL_ENABLE_REDIRECTION;
  			tegra_pmc_reg_writel(val, TEGRA_PMC_FUSE_CTRL);
  		}
  	}
  }
  EXPORT_SYMBOL(tegra_pmc_fuse_disable_mirroring);
  
  void tegra_pmc_fuse_enable_mirroring(void)
  {
  	u32 val;
  
  	if (pmc->soc->has_misc_base_address) {
  		val = tegra_pmc_misc_readl(TEGRA_PMC_FUSE_CTRL);
  		if (!(val & PMC_FUSE_CTRL_ENABLE_REDIRECTION)) {
  			val |= PMC_FUSE_CTRL_ENABLE_REDIRECTION;
  			tegra_pmc_misc_writel(val, TEGRA_PMC_FUSE_CTRL);
  		}
  	} else {
  		val = tegra_pmc_reg_readl(TEGRA_PMC_FUSE_CTRL);
  		if (!(val & PMC_FUSE_CTRL_ENABLE_REDIRECTION)) {
  			val |= PMC_FUSE_CTRL_ENABLE_REDIRECTION;
  			tegra_pmc_reg_writel(val, TEGRA_PMC_FUSE_CTRL);
  		}
  	}
  }
  EXPORT_SYMBOL(tegra_pmc_fuse_enable_mirroring);
  
  bool tegra_pmc_fuse_is_redirection_enabled(void)
  {
  	u32 val;
  
  	if (pmc->soc->has_misc_base_address)
  		val = tegra_pmc_misc_readl(TEGRA_PMC_FUSE_CTRL);
  	else
  		val = tegra_pmc_reg_readl(TEGRA_PMC_FUSE_CTRL);
  
  	if (val & PMC_FUSE_CTRL_ENABLE_REDIRECTION_STICKY)
  		return true;
  	return false;
  }
  EXPORT_SYMBOL(tegra_pmc_fuse_is_redirection_enabled);
  
  #ifdef CONFIG_PM_SLEEP
  static void tegra_pmc_remove_dpd_req(void)
  {
  	/* Clear DPD req */
  	tegra_pmc_reg_writel(io_dpd_reg | IO_DPD_REQ_CODE_OFF,
  			     TEGRA_PMC_IO_DPD_REQ);
  	tegra_pmc_reg_readl(TEGRA_PMC_IO_DPD_REQ); /* unblock posted write */
  	/* delay apb_clk * (SEL_DPD_TIM*5) */
  	udelay(DPD_STATE_CHANGE_DELAY);
  
  	tegra_pmc_reg_writel(io_dpd2_reg | IO_DPD_REQ_CODE_OFF,
  			     TEGRA_PMC_IO_DPD2_REQ);
  	tegra_pmc_readl(TEGRA_PMC_IO_DPD2_REQ); /* unblock posted write */
  	udelay(DPD_STATE_CHANGE_DELAY);
  }
  
  static void tegra_pmc_clear_dpd_sample(void)
  {
  	/* Clear DPD sample */
  	tegra_pmc_reg_writel(0x0, TEGRA_PMC_IO_DPD_SAMPLE);
  }
  
  static void tegra_pmc_add_wakeup_event(struct of_phandle_args *ph_args,
  				       struct device *dev,
  				       struct device_node *np)
  {
  	struct platform_device *pdev;
  	struct pmc_wakeup *pmc_wake_source;
  	struct irq_desc *irqd;
  	struct irq_data *irq_data;
  	int pmc_wake_type, wake;
  	int irq, pmc_trigger_type;
  
  	if (ph_args->np != tegra_lp0_wakeup.of_node)
  		return;
  	if (ph_args->args_count != PMC_OF_ARGS_COUNT)
  		return;
  
  	pdev = to_platform_device(dev);
  	irq = platform_get_irq(pdev, 0);
  	pmc_wake_type = ph_args->args[PMC_WAKE_TYPE_INDEX];
  
  	switch (pmc_wake_type) {
  	case PMC_WAKE_TYPE_GPIO:
  		if (irq < 0) {
  			int gpio;
  
  			gpio = of_get_named_gpio(np, "gpios", 0);
  			irq = gpio_to_irq(gpio);
  			if (WARN_ON(irq < 0))
  				return;
  		}
  		irqd = irq_to_desc(irq);
  		irq_data = &irqd->irq_data;
  		pmc_trigger_type = irqd_get_trigger_type(irq_data);
  		break;
  	case PMC_WAKE_TYPE_EVENT:
  		pmc_trigger_type = ph_args->args[PMC_TRIGGER_TYPE_INDEX];
  		break;
  	default:
  		return;
  	}
  
  	pmc_wake_source = kzalloc(sizeof(*pmc_wake_source), GFP_KERNEL);
  	if (!pmc_wake_source)
  		return;
  
  	pmc_wake_source->wake_type = pmc_wake_type;
  	pmc_wake_source->irq_num = irq;
  	pmc_wake_source->wake_mask_offset = ph_args->args[PMC_WAKE_MASK_INDEX];
  	wake = pmc_wake_source->wake_mask_offset;
  
  	list_add_tail(&pmc_wake_source->list, &tegra_lp0_wakeup.wake_list);
  
  	tegra_lp0_wakeup.enable |= 1ULL << wake;
  	switch (pmc_trigger_type) {
  	case IRQF_TRIGGER_FALLING:
  	case IRQF_TRIGGER_LOW:
  		tegra_lp0_wakeup.level &= ~(1ULL << wake);
  		tegra_lp0_wakeup.level_any &= ~(1ULL << wake);
  		break;
  	case IRQF_TRIGGER_HIGH:
  	case IRQF_TRIGGER_RISING:
  		tegra_lp0_wakeup.level |= (1ULL << wake);
  		tegra_lp0_wakeup.level_any &= ~(1ULL << wake);
  		break;
  	case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING:
  		tegra_lp0_wakeup.level_any |= (1ULL << wake);
  		break;
  	default:
  		break;
  	}
  }
  
  static void tegra_of_device_add_pmc_wake(struct device *dev)
  {
  	struct of_phandle_args ph_args;
  	struct device_node *np = NULL;
  	int child_node_num, i = 0;
  
  	child_node_num = of_get_child_count(dev->of_node);
  	while (!of_parse_phandle_with_args(dev->of_node,
  					   "nvidia,pmc-wakeup",
  					   "#nvidia,wake-cells",
  					   i++, &ph_args)) {
  		tegra_pmc_add_wakeup_event(&ph_args, dev, dev->of_node);
  		of_node_put(ph_args.np);
  	}
  	if (child_node_num > 0) {
  		for_each_child_of_node(dev->of_node, np) {
  			i = 0;
  			while (!of_parse_phandle_with_args(np,
  							   "nvidia,pmc-wakeup",
  							   "#nvidia,wake-cells",
  							   i++, &ph_args)) {
  				tegra_pmc_add_wakeup_event(&ph_args, dev, np);
  				of_node_put(ph_args.np);
  			}
  		}
  	}
  }
  
  static int tegra_pmc_wake_notifier_call(struct notifier_block *nb,
  					unsigned long event, void *data)
  {
  	struct device *dev = data;
  
  	switch (event) {
  	case BUS_NOTIFY_BOUND_DRIVER:
  		if (dev->of_node)
  			tegra_of_device_add_pmc_wake(dev);
  		break;
  	}
  	return NOTIFY_DONE;
  }
  
  static struct notifier_block tegra_pmc_wake_notifier = {
  	.notifier_call = tegra_pmc_wake_notifier_call,
  };
  
  static int __init tegra_pmc_lp0_wakeup_init(void)
  {
  	if (!soc_is_tegra210_n_before())
  		return 0;
  
  	bus_register_notifier(&platform_bus_type, &tegra_pmc_wake_notifier);
  	return 0;
  }
  arch_initcall(tegra_pmc_lp0_wakeup_init);
  
  static inline void write_pmc_wake_mask(u64 value)
  {
  	pr_info("PMC wake enable = 0x%llx\n", value);
  	tegra_pmc_reg_writel((u32)value, TEGRA_PMC_WAKE_MASK);
  	if (tegra_get_chip_id() != TEGRA20)
  		tegra_pmc_reg_writel((u32)(value >> 32), TEGRA_PMC_WAKE2_MASK);
  }
  
  static inline u64 read_pmc_wake_level(void)
  {
  	u64 reg;
  
  	reg = tegra_pmc_reg_readl(TEGRA_PMC_WAKE_LEVEL);
  	if (tegra_get_chip_id() != TEGRA20)
  		reg |= ((u64)tegra_pmc_reg_readl(TEGRA_PMC_WAKE2_LEVEL)) << 32;
  
  	return reg;
  }
  
  static inline void write_pmc_wake_level(u64 value)
  {
  	pr_info("PMC wake level = 0x%llx\n", value);
  	tegra_pmc_reg_writel((u32)value, TEGRA_PMC_WAKE_LEVEL);
  	if (tegra_get_chip_id() != TEGRA20)
  		tegra_pmc_reg_writel((u32)(value >> 32), TEGRA_PMC_WAKE2_LEVEL);
  }
  
  static inline u64 read_pmc_wake_status(void)
  {
  	u64 reg;
  
  	reg = tegra_pmc_reg_readl(TEGRA_PMC_WAKE_STATUS);
  	if (tegra_get_chip_id() != TEGRA20)
  		reg |= ((u64)tegra_pmc_reg_readl(TEGRA_PMC_WAKE2_STATUS)) << 32;
  
  	return reg;
  }
  
  static inline void clear_pmc_wake_status(void)
  {
  	u32 reg;
  
  	reg = tegra_pmc_reg_readl(TEGRA_PMC_WAKE_STATUS);
  	if (reg)
  		tegra_pmc_reg_writel(reg, TEGRA_PMC_WAKE_STATUS);
  	if (tegra_get_chip_id() != TEGRA20) {
  		reg = tegra_pmc_reg_readl(TEGRA_PMC_WAKE2_STATUS);
  		if (reg)
  			tegra_pmc_reg_writel(reg, TEGRA_PMC_WAKE2_STATUS);
  	}
  }
  
  static inline u64 read_pmc_sw_wake_status(void)
  {
  	u64 reg;
  
  	reg = tegra_pmc_reg_readl(TEGRA_PMC_SW_WAKE_STATUS);
  	if (tegra_get_chip_id() != TEGRA20)
  		reg |= ((u64)tegra_pmc_reg_readl(TEGRA_PMC_SW_WAKE2_STATUS)) << 32;
  
  	return reg;
  }
  
  static inline void clear_pmc_sw_wake_status(void)
  {
  	tegra_pmc_reg_writel(0, TEGRA_PMC_SW_WAKE_STATUS);
  	if (tegra_get_chip_id() != TEGRA20)
  		tegra_pmc_reg_writel(0, TEGRA_PMC_SW_WAKE2_STATUS);
  }
  
  /* translate lp0 wake sources back into irqs to catch edge triggered wakeups */
  static void tegra_pmc_wake_syscore_resume(void)
  {
  	struct pmc_wakeup *wake;
  	struct irq_desc *desc;
  	u64 wake_status = read_pmc_wake_status();
  
  	pr_info("PMC wake status = 0x%llx\n", wake_status);
  
  	list_for_each_entry(wake, &tegra_lp0_wakeup.wake_list, list) {
  		if (!(wake_status & BIT(wake->wake_mask_offset)))
  			continue;
  
  		if (wake->irq_num <= 0) {
  			pr_info("Resume caused by PMC WAKE%d\n",
  				wake->wake_mask_offset);
  			continue;
  		}
  
  		desc = irq_to_desc(wake->irq_num);
  		if (!desc || !desc->action || !desc->action->name) {
  			pr_info("Resume caused by PMC WAKE%d, irq %d\n",
  				wake->wake_mask_offset, wake->irq_num);
  			log_wakeup_reason(wake->irq_num);
  			continue;
  		}
  
  		pr_info("Resume caused by PMC WAKE%d, %s\n",
  			wake->wake_mask_offset, desc->action->name);
  		generic_handle_irq(wake->irq_num);
  	}
  }
  
  static int tegra_pmc_wake_syscore_suspend(void)
  {
  	u32 reg;
  	u64 status;
  	u64 lvl;
  	u64 wake_level;
  	u64 wake_enb;
  
  	clear_pmc_sw_wake_status();
  
  	/* enable PMC wake */
  	reg = tegra_pmc_reg_readl(TEGRA_PMC_CNTRL);
  	reg |= PMC_CNTRL_LATCH_WAKEUPS;
  	tegra_pmc_reg_writel(reg, TEGRA_PMC_CNTRL);
  	udelay(120);
  
  	reg &= ~PMC_CNTRL_LATCH_WAKEUPS;
  	tegra_pmc_reg_writel(reg, TEGRA_PMC_CNTRL);
  	udelay(120);
  
  	status = read_pmc_sw_wake_status();
  
  	lvl = read_pmc_wake_level();
  
  	/*
  	 * flip the wakeup trigger for any-edge triggered pads
  	 * which are currently asserting as wakeups
  	 */
  	lvl ^= status;
  
  	lvl &= tegra_lp0_wakeup.level_any;
  
  	wake_level = lvl | tegra_lp0_wakeup.level;
  	wake_enb = tegra_lp0_wakeup.enable;
  
  	/* Clear PMC Wake Status registers while going to suspend */
  	clear_pmc_wake_status();
  	write_pmc_wake_level(wake_level);
  	write_pmc_wake_mask(wake_enb);
  
  	return 0;
  }
  
  static int tegra_pmc_suspend(void)
  {
  #ifdef CONFIG_ARM
  	tegra_pmc_reg_writel(virt_to_phys(tegra_resume), TEGRA_PMC_SCRATCH41);
  #else /* CONFIG_ARM64 */
  	enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode();
  	tegra_pmc_enter_suspend_mode(mode);
  #endif
  
  	return 0;
  }
  
  static void tegra_pmc_resume(void)
  {
  	u32 chip_id = tegra_get_chip_id();
  
  	switch (chip_id) {
  	case TEGRA114:
  	case TEGRA124:
  	case TEGRA132:
  		tegra_pmc_remove_dpd_req();
  		break;
  	default:
  		break;
  	}
  
  	tegra_pmc_clear_dpd_sample();
  
  	/* Clear DPD Enable */
  	switch (chip_id) {
  	case TEGRA20:
  	case TEGRA30:
  	case TEGRA114:
  		break;
  	default:
  		/* Don't access the secure DPD_ENABLE register
  		 * if the secure-pmc flag is enabled.
  		 */
  		if (!get_secure_pmc_setting())
  			tegra_pmc_reg_writel(0x0, TEGRA_PMC_IO_DPD_ENABLE);
  		break;
  	}
  
  #ifdef CONFIG_ARM
  	tegra_pmc_reg_writel(0x0, TEGRA_PMC_SCRATCH41);
  #endif
  }
  
  static void set_core_power_timers(void)
  {
  	unsigned long osc, pmu, off;
  
  	osc = DIV_ROUND_UP_ULL(pmc->core_osc_time * 8192, 1000000);
  	pmu = DIV_ROUND_UP_ULL(pmc->core_pmu_time * 32768, 1000000);
  	off = DIV_ROUND_UP_ULL(pmc->core_off_time * 32768, 1000000);
  
  	tegra_pmc_reg_writel(((osc << 8) & 0xff00) | (pmu & 0xff),
  			 TEGRA_PMC_COREPWRGOOD_TIMER);
  	tegra_pmc_reg_writel(off, TEGRA_PMC_COREPWROFF_TIMER);
  }
  
  enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)
  {
  	return pmc->suspend_mode;
  }
  
  void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode)
  {
  	if (mode < TEGRA_SUSPEND_NONE || mode >= TEGRA_MAX_SUSPEND_MODE)
  		return;
  
  	pmc->suspend_mode = mode;
  }
  
  void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode)
  {
  	unsigned long long rate = 0;
  	u32 boot_flag, cntrl_value;
  
  	cntrl_value = tegra_pmc_reg_readl(TEGRA_PMC_CNTRL);
  	cntrl_value &= ~PMC_CNTRL_SIDE_EFFECT_LP0;
  	if (pmc->combined_req)
  		cntrl_value &= ~PMC_CNTRL_PWRREQ_OE;
  	else
  		cntrl_value |= PMC_CNTRL_PWRREQ_OE;
  	cntrl_value |= PMC_CNTRL_CPU_PWRREQ_OE;
  
  	switch (mode) {
  	case TEGRA_SUSPEND_SC7:
  		rate = 32768;
  		break;
  
  	case TEGRA_SUSPEND_LP0:
  		/*
  		 * Enable DPD sample to trigger sampling pads data and direction
  		 * in which pad will be driven during LP0 mode.
  		 */
  		tegra_pmc_reg_writel(0x1, TEGRA_PMC_IO_DPD_SAMPLE);
  
  		/*
  		 * Power down IO logic
  		 */
  		switch (tegra_get_chip_id()) {
  		case TEGRA114:
  		case TEGRA124:
  		case TEGRA132:
  			io_dpd_reg = IO_DPD_CSIA | IO_DPD_CSIB | IO_DPD_DSI |
  				IO_DPD_MIPI_BIAS | IO_DPD_PEX_BIAS |
  				IO_DPD_PEX_CLK1 | IO_DPD_PEX_CLK2 |
  				IO_DPD_PEX_CLK3 | IO_DPD_DAC | IO_DPD_USB0 |
  				IO_DPD_USB1 | IO_DPD_USB2 | IO_DPD_USB_BIAS |
  				IO_DPD_UART | IO_DPD_BB | IO_DPD_VI |
  				IO_DPD_AUDIO | IO_DPD_LCD | IO_DPD_HSIC;
  			io_dpd2_reg = IO_DPD2_PEX_CNTRL | IO_DPD2_SDMMC1 |
  				IO_DPD2_SDMMC3 | IO_DPD2_SDMMC4 | IO_DPD2_CAM |
  				IO_DPD2_RES_RAIL | IO_DPD2_HV | IO_DPD2_DSIB |
  				IO_DPD2_DSIC | IO_DPD2_DSID | IO_DPD2_CSIC |
  				IO_DPD2_CSID | IO_DPD2_CSIE;
  			break;
  		default:
  			break;
  		}
  		tegra_pmc_reg_writel(io_dpd_reg | IO_DPD_REQ_CODE_ON,
  				     TEGRA_PMC_IO_DPD_REQ);
  		tegra_pmc_reg_readl(TEGRA_PMC_IO_DPD_REQ); /* unblock posted write */
  
  		/* delay apb_clk * (SEL_DPD_TIM*5) */
  		udelay(DPD_STATE_CHANGE_DELAY);
  
  		tegra_pmc_reg_writel(io_dpd2_reg | IO_DPD_REQ_CODE_ON,
  				     TEGRA_PMC_IO_DPD2_REQ);
  		tegra_pmc_reg_readl(TEGRA_PMC_IO_DPD2_REQ); /* unblock posted write */
  		udelay(DPD_STATE_CHANGE_DELAY);
  
  		/* Set warmboot flag */
  		boot_flag = tegra_pmc_reg_readl(TEGRA_PMC_SCRATCH0);
  		tegra_pmc_reg_writel(boot_flag | 1, TEGRA_PMC_SCRATCH0);
  
  		if (pmc->lp0_vec_phys != 0)
  			tegra_pmc_reg_writel(pmc->lp0_vec_phys, TEGRA_PMC_SCRATCH1);
  		cntrl_value |= PMC_CNTRL_SIDE_EFFECT_LP0;
  
  	case TEGRA_SUSPEND_LP1:
  		rate = 32768;
  		break;
  
  	case TEGRA_SUSPEND_LP2:
  		rate = clk_get_rate(pmc->clk);
  		break;
  
  	default:
  		break;
  	}
  
  	if (WARN_ON_ONCE(rate == 0))
  		rate = 100000000;
  
  	if (rate != pmc->rate) {
  		u64 ticks;
  
  		ticks = pmc->cpu_good_time * rate + USEC_PER_SEC - 1;
  		do_div(ticks, USEC_PER_SEC);
  		tegra_pmc_reg_writel(ticks, TEGRA_PMC_CPUPWRGOOD_TIMER);
  
  		ticks = pmc->cpu_off_time * rate + USEC_PER_SEC - 1;
  		do_div(ticks, USEC_PER_SEC);
  		tegra_pmc_reg_writel(ticks, TEGRA_PMC_CPUPWROFF_TIMER);
  
  		wmb();
  
  		pmc->rate = rate;
  	}
  
  	tegra_pmc_reg_writel(cntrl_value, TEGRA_PMC_CNTRL);
  }
  #else
  #define tegra_pmc_suspend		NULL
  #define tegra_pmc_resume		NULL
  #define tegra_pmc_wake_syscore_suspend	NULL
  #define tegra_pmc_wake_syscore_resume	NULL
  static inline void set_core_power_timers(void) { }
  #endif
  
  static struct syscore_ops tegra_pmc_syscore_ops = {
  	.suspend = tegra_pmc_suspend,
  	.resume = tegra_pmc_resume,
  	.save = tegra_pmc_suspend,
  	.restore = tegra_pmc_resume,
  };
  
  static struct syscore_ops tegra_pmc_wake_syscore_ops = {
  	.suspend = tegra_pmc_wake_syscore_suspend,
  	.resume = tegra_pmc_wake_syscore_resume,
  	.save = tegra_pmc_wake_syscore_suspend,
  	.restore = tegra_pmc_wake_syscore_resume,
  };
  
  static void tegra_pmc_syscore_init(void)
  {
  	register_syscore_ops(&tegra_pmc_syscore_ops);
  }
  
  static void tegra_pmc_wake_syscore_init(void)
  {
  	register_syscore_ops(&tegra_pmc_wake_syscore_ops);
  }
  
  /* PMC Bootrom commands */
  static int tegra_pmc_parse_bootrom_cmd(struct device *dev,
  				       struct device_node *np,
  				       struct tegra_bootrom_commands **br_cmds)
  {
  	struct device_node *child;
  	struct tegra_bootrom_commands *bcommands;
  	int *command_ptr;
  	struct tegra_bootrom_block *block;
  	int nblocks;
  	u32 reg, data, pval;
  	u32 *wr_commands;
  	int count, nblock, ncommands, i, reg_shift;
  	int ret;
  	int sz_bcommand, sz_blocks;
  
  	nblocks = of_get_available_child_count(np);
  	if (!nblocks) {
  		dev_info(dev, "PMC: No Bootrom Command\n");
  		return -ENOENT;
  	}
  
  	count = 0;
  	for_each_available_child_of_node(np, child) {
  		ret = of_property_count_u32_elems(child,
  						  "nvidia,write-commands");
  		if (ret < 0) {
  			dev_err(dev, "PMC: Node %s does not have write-commnds\n",
  				child->full_name);
  			return -EINVAL;
  		}
  		count += ret / 2;
  	}
  
  	sz_bcommand = (sizeof(*bcommands) + 0x3) & ~0x3;
  	sz_blocks = (sizeof(*block) + 0x3) & ~0x3;
  	bcommands = devm_kzalloc(dev,  sz_bcommand + nblocks * sz_blocks +
  				 count * sizeof(u32), GFP_KERNEL);
  	if (!bcommands)
  		return -ENOMEM;
  
  	bcommands->nblocks = nblocks;
  	bcommands->blocks = (void *)bcommands + sz_bcommand;
  	command_ptr = (void *)bcommands->blocks + nblocks * sz_blocks;
  
  	of_property_read_u32(np, "nvidia,command-retries-count",
  			     &bcommands->command_retry_count);
  	of_property_read_u32(np, "nvidia,delay-between-commands-us",
  			     &bcommands->delay_between_commands);
  
  	ret = of_property_read_u32(np, "nvidia,wait-before-start-bus-clear-us",
  				   &bcommands->wait_before_bus_clear);
  	if (ret < 0)
  		of_property_read_u32(np, "nvidia,wait-start-bus-clear-us",
  				     &bcommands->wait_before_bus_clear);
  
  	nblock = 0;
  	for_each_available_child_of_node(np, child) {
  		block = &bcommands->blocks[nblock];
  		ret = of_property_read_u32(child, "reg", &pval);
  		if (ret) {
  			dev_err(dev, "PMC: Reg property missing on block %s\n",
  				child->name);
  			return ret;
  		}
  		block->address = pval;
  		of_property_read_string(child, "nvidia,command-names",
  					&block->name);
  		block->reg_8bits = !of_property_read_bool(child,
  					"nvidia,enable-16bit-register");
  		block->data_8bits = !of_property_read_bool(child,
  					"nvidia,enable-16bit-data");
  		block->i2c_controller = of_property_read_bool(child,
  					"nvidia,controller-type-i2c");
  		block->enable_reset = of_property_read_bool(child,
  					"nvidia,enable-controller-reset");
  		count = of_property_count_u32_elems(child,
  						    "nvidia,write-commands");
  		ncommands = count / 2;
  
  		block->commands = command_ptr;
  		command_ptr += ncommands;
  		wr_commands = block->commands;
  		reg_shift = (block->data_8bits) ? 8 : 16;
  		for (i = 0; i < ncommands; ++i) {
  			of_property_read_u32_index(child,
  						   "nvidia,write-commands",
  						   i * 2, &reg);
  			of_property_read_u32_index(child,
  						   "nvidia,write-commands",
  						   i * 2 + 1, &data);
  
  			wr_commands[i] = (data << reg_shift) | reg;
  		}
  		block->ncommands = ncommands;
  		nblock++;
  	}
  	*br_cmds = bcommands;
  
  	return 0;
  }
  
  static int tegra_pmc_read_bootrom_cmd(struct device *dev,
  		      struct tegra_bootrom_commands **br_rst_cmds,
  		      struct tegra_bootrom_commands **br_off_cmds)
  {
  	struct device_node *np = dev->of_node;
  	struct device_node *br_np, *rst_np, *off_np;
  	int ret;
  
  	*br_rst_cmds = NULL;
  	*br_off_cmds = NULL;
  
  	br_np = of_find_node_by_name(np, "bootrom-commands");
  	if (!br_np) {
  		dev_info(dev, "PMC: Bootrom commmands not found\n");
  		return -ENOENT;
  	}
  
  	rst_np = of_find_node_by_name(br_np, "reset-commands");
  	if (!rst_np) {
  		dev_info(dev, "PMC: bootrom-commands used for reset\n");
  		rst_np = br_np;
  	}
  
  	ret = tegra_pmc_parse_bootrom_cmd(dev, rst_np, br_rst_cmds);
  	if (ret < 0)
  		return ret;
  
  	if (rst_np == br_np)
  		return 0;
  
  	off_np = of_find_node_by_name(br_np, "power-off-commands");
  	if (!off_np)
  		return 0;
  	ret = tegra_pmc_parse_bootrom_cmd(dev, off_np, br_off_cmds);
  	if (ret < 0)
  		return ret;
  
  	return 0;
  }
  
  static int tegra_pmc_configure_bootrom_scratch(struct device *dev,
  		struct tegra_bootrom_commands *br_commands)
  {
  	struct tegra_bootrom_block *block;
  	int i, j, k;
  	u32 cmd;
  	int reg_offset = 1;
  	u32 reg_data_mask;
  	int cmd_pw;
  	u32 block_add, block_val, csum;
  
  	for (i = 0; i < br_commands->nblocks; ++i) {
  		block = &br_commands->blocks[i];
  
  		cmd = block->address & PMC_BR_COMMAND_I2C_ADD_MASK;
  		cmd |= block->ncommands << PMC_BR_COMMAND_WR_COMMANDS_SHIFT;
  		if (!block->reg_8bits || !block->data_8bits)
  			cmd |= BIT(PMC_BR_COMMAND_OPERAND_SHIFT);
  
  		if (block->enable_reset)
  			cmd |= BIT(PMC_BR_COMMAND_RST_EN_SHIFT);
  
  		cmd |= (block->controller_id & PMC_BR_COMMAND_CTRL_ID_MASK) <<
  					PMC_BR_COMMAND_CTRL_ID_SHIFT;
  
  		/* Checksum will be added after parsing from reg/data */
  		tegra_pmc_write_bootrom_command(reg_offset * 4, cmd);
  		block_add = reg_offset * 4;
  		block_val = cmd;
  		reg_offset++;
  
  		cmd_pw = (block->reg_8bits && block->data_8bits) ? 2 : 1;
  		reg_data_mask = (cmd_pw == 1) ? 0xFFFF : 0xFFFFFFFFUL;
  		csum = 0;
  
  		for (j = 0; j < block->ncommands; j++) {
  			cmd = block->commands[j] & reg_data_mask;
  			if (cmd_pw == 2) {
  				j++;
  				if (j == block->ncommands)
  					goto reg_update;
  				cmd |= (block->commands[j] & reg_data_mask) <<
  					16;
  			}
  reg_update:
  			tegra_pmc_write_bootrom_command(reg_offset * 4, cmd);
  			for (k = 0; k < 4; ++k)
  				csum += (cmd >> (k * 8)) & 0xFF;
  			reg_offset++;
  		}
  		for (k = 0; k < 4; ++k)
  			csum += (block_val >> (k * 8)) & 0xFF;
  		csum = 0x100 - csum;
  		block_val = (block_val & 0xFF00FFFF) | ((csum & 0xFF) << 16);
  		tegra_pmc_write_bootrom_command(block_add, block_val);
  	}
  
  	cmd = br_commands->command_retry_count & 0x7;
  	cmd |= (br_commands->delay_between_commands & 0x1F) << 3;
  	cmd |= (br_commands->nblocks & 0x7) << 8;
  	cmd |= (br_commands->wait_before_bus_clear & 0x1F) << 11;
  	tegra_pmc_write_bootrom_command(0, cmd);
  
  	return 0;
  }
  
  static int tegra_pmc_init_bootrom_power_off_cmd(struct device *dev)
  {
  	int ret;
  
  	if (!br_off_commands) {
  		dev_info(dev, "PMC: Power Off Command not available\n");
  		return 0;
  	}
  
  	ret = tegra_pmc_configure_bootrom_scratch(NULL, br_off_commands);
  	if (ret < 0) {
  		dev_err(dev, "PMC: Failed to configure power-off command: %d\n",
  			ret);
  		return ret;
  	}
  
  	dev_info(dev, "PMC: Successfully configure power-off commands\n");
  
  	return 0;
  }
  
  static void tegra_pmc_soc_power_off(void)
  {
  	tegra_pmc_init_bootrom_power_off_cmd(pmc->dev);
  	tegra_pmc_reset_system();
  }
  
  static int tegra_pmc_init_boorom_cmds(struct device *dev)
  {
  	int ret;
  
  	ret = tegra_pmc_read_bootrom_cmd(dev, &br_rst_commands,
  					 &br_off_commands);
  	if (ret < 0) {
  		if (ret == -ENOENT)
  			ret = 0;
  		else
  			dev_info(dev,
  				 "PMC: Failed to read bootrom cmd: %d\n", ret);
  
  		return ret;
  	}
  
  	if (br_off_commands)
  		set_soc_specific_power_off(tegra_pmc_soc_power_off);
  
  	ret = tegra_pmc_configure_bootrom_scratch(dev, br_rst_commands);
  	if (ret < 0) {
  		dev_info(dev, "PMC: Failed to write bootrom scratch register: %d\n",
  			 ret);
  		return ret;
  	}
  
  	dev_info(dev, "PMC: Successfully configure bootrom reset commands\n");
  
  	return 0;
  }
  
  /* IO Pads configurations */
  static int tegra_pmc_io_pad_prepare(const struct tegra_pmc_io_pad_soc *pad)
  {
  	unsigned long rate, value;
  
  	if (pad->dpd == UINT_MAX)
  		return -ENOTSUPP;
  
  	if (!pmc->clk)
  		return 0;
  
  	rate = clk_get_rate(pmc->clk);
  	if (!rate) {
  		dev_err(pmc->dev, "Failed to get clock rate\n");
  		return -ENODEV;
  	}
  
  	tegra_pmc_reg_writel(DPD_SAMPLE_ENABLE, pad->dpd_sample_reg);
  
  	/* must be at least 200 ns, in APB (PCLK) clock cycles */
  	value = DIV_ROUND_UP(1000000000, rate);
  	value = DIV_ROUND_UP(200, value);
  	tegra_pmc_reg_writel(value, pad->dpd_timer_reg);
  
  	return 0;
  }
  
  static int tegra_pmc_io_pad_poll(const struct tegra_pmc_io_pad_soc *pad,
  				 u32 val, unsigned long timeout)
  {
  	u32 mask = BIT(pad->dpd);
  	u32 value;
  
  	timeout = jiffies + msecs_to_jiffies(timeout);
  
  	usleep_range(10, 10);
  	while (time_after(timeout, jiffies)) {
  		value = tegra_pmc_reg_readl(pad->dpd_status_reg);
  		if ((value & mask) == val)
  			return 0;
  
  		usleep_range(250, 1000);
  	}
  
  	return -ETIMEDOUT;
  }
  
  static void tegra_pmc_io_pad_unprepare(const struct tegra_pmc_io_pad_soc *pad)
  {
  	if (!pmc->clk)
  		return;
  
  	tegra_pmc_reg_writel(DPD_SAMPLE_DISABLE, pad->dpd_sample_reg);
  }
  
  /**
   * tegra_pmc_io_pad_power_enable() - enable power to I/O pad
   * @pad: Tegra I/O pad SOC data for which to enable power
   *
   * Returns: 0 on success or a negative error code on failure.
   */
  static int tegra_pmc_io_pad_power_enable(const struct tegra_pmc_io_pad_soc *pad)
  {
  	int err;
  
  	mutex_lock(&pmc->powergates_lock);
  
  	err = tegra_pmc_io_pad_prepare(pad);
  	if (err < 0) {
  		dev_err(pmc->dev, "Failed to prepare I/O pad %s: %d\n",
  			pad->name, err);
  		goto unlock;
  	}
  
  	tegra_pmc_reg_writel(IO_DPD_REQ_CODE_OFF | BIT(pad->dpd), pad->dpd_req_reg);
  
  	err = tegra_pmc_io_pad_poll(pad, 0, 250);
  	if (err < 0) {
  		dev_err(pmc->dev, "Failed to enable I/O pad %s: %d\n",
  			pad->name, err);
  		dev_err(pmc->dev, "DPDREQ: 0x%08x DPD2REQ:: 0x%08x"
  			"DPD_STATUS: 0x%08x DPD2_STATUS:: 0x%08x\n",
  			tegra_pmc_reg_readl(TEGRA_PMC_IO_DPD_REQ),
  			tegra_pmc_reg_readl(TEGRA_PMC_IO_DPD2_REQ),
  			tegra_pmc_reg_readl(TEGRA_PMC_IO_DPD_STATUS),
  			tegra_pmc_reg_readl(TEGRA_PMC_IO_DPD2_STATUS));
  		goto unlock;
  	}
  
  	tegra_pmc_io_pad_unprepare(pad);
  
  unlock:
  	mutex_unlock(&pmc->powergates_lock);
  	return err;
  }
  
  /**
   * tegra_pmc_io_pad_power_disable() - disable power to I/O pad
   * @pad: Tegra I/O pad SOC data for which to disable power
   *
   * Returns: 0 on success or a negative error code on failure.
   */
  static int tegra_pmc_io_pad_power_disable(
  			const struct tegra_pmc_io_pad_soc *pad)
  {
  	int err;
  
  	mutex_lock(&pmc->powergates_lock);
  
  	err = tegra_pmc_io_pad_prepare(pad);
  	if (err < 0) {
  		dev_err(pmc->dev, "Failed to prepare I/O pad %s: %d\n",
  			pad->name, err);
  		goto unlock;
  	}
  
  	tegra_pmc_reg_writel(IO_DPD_REQ_CODE_ON | BIT(pad->dpd), pad->dpd_req_reg);
  
  	err = tegra_pmc_io_pad_poll(pad, BIT(pad->dpd), 250);
  	if (err < 0) {
  		dev_err(pmc->dev, "Failed to disable I/O pad %s: %d\n",
  			pad->name, err);
  		dev_err(pmc->dev, "DPDREQ: 0x%08x DPD2REQ:: 0x%08x"
  			"DPD_STATUS: 0x%08x DPD2_STATUS:: 0x%08x\n",
  			tegra_pmc_reg_readl(TEGRA_PMC_IO_DPD_REQ),
  			tegra_pmc_reg_readl(TEGRA_PMC_IO_DPD2_REQ),
  			tegra_pmc_reg_readl(TEGRA_PMC_IO_DPD_STATUS),
  			tegra_pmc_reg_readl(TEGRA_PMC_IO_DPD2_STATUS));
  		goto unlock;
  	}
  
  	tegra_pmc_io_pad_unprepare(pad);
  
  unlock:
  	mutex_unlock(&pmc->powergates_lock);
  	return err;
  }
  
  static int _tegra_pmc_io_pad_set_voltage(const struct tegra_pmc_io_pad_soc *pad,
  					 int io_pad_uv)
  {
  	u32 value;
  
  	if (pad->voltage == UINT_MAX)
  		return -ENOTSUPP;
  
  	if ((io_pad_uv != pad->pad_uv_0) &&
  	    (io_pad_uv != pad->pad_uv_1))
  		return -EINVAL;
  
  	mutex_lock(&pmc->powergates_lock);
  
  	/* write-enable PMC_PWR_DET_VALUE[pad->voltage] */
  	if (pad->io_pad_pwr_det_enable_reg != UINT_MAX) {
  		value = tegra_pmc_reg_readl(pad->io_pad_pwr_det_enable_reg);
  		value |= BIT(pad->voltage);
  		tegra_pmc_reg_writel(value, pad->io_pad_pwr_det_enable_reg);
  	}
  
  	/* update I/O voltage */
  	value = tegra_pmc_reg_readl(pad->io_pad_pwr_det_val_reg);
  
  	if (io_pad_uv == pad->pad_uv_0)
  		value &= ~BIT(pad->voltage);
  	else
  		value |= BIT(pad->voltage);
  
  	tegra_pmc_reg_writel(value, pad->io_pad_pwr_det_val_reg);
  
  	mutex_unlock(&pmc->powergates_lock);
  
  	usleep_range(100, 250);
  
  	return 0;
  }
  
  static int _tegra_pmc_io_pad_get_voltage(const struct tegra_pmc_io_pad_soc *pad)
  {
  	u32 value;
  
  	if (pad->voltage == UINT_MAX)
  		return -ENOTSUPP;
  
  	value = tegra_pmc_reg_readl(pad->io_pad_pwr_det_val_reg);
  
  	if ((value & BIT(pad->voltage)) == 0)
  		return pad->pad_uv_0;
  
  	return pad->pad_uv_1;
  }
  
  /**
   * tegra_pmc_io_pad_is_powered() - check if IO pad is powered
   * @pad: Tegra I/O pad SOC data for which power status need to check
   *
   * Return 1 if power-ON, 0 if power OFF and error number in
   * negative if pad ID is not valid or power down not supported
   * on given IO pad.
   */
  static int tegra_pmc_io_pad_is_powered(const struct tegra_pmc_io_pad_soc *pad)
  {
  	u32 value;
  
  	if (pad->dpd == UINT_MAX)
  		return -ENOTSUPP;
  
  	value = tegra_pmc_reg_readl(pad->dpd_status_reg);
  
  	return !(value & BIT(pad->dpd));
  }
  
  static int tegra_pmc_io_pads_pinctrl_get_groups_count(
  			struct pinctrl_dev *pctldev)
  {
  	struct tegra_pmc *tpmc = pinctrl_dev_get_drvdata(pctldev);
  
  	return tpmc->soc->num_io_pads;
  }
  
  static const char *tegra_pmc_io_pads_pinctrl_get_group_name(
  		struct pinctrl_dev *pctldev, unsigned int group)
  {
  	struct tegra_pmc *tpmc = pinctrl_dev_get_drvdata(pctldev);
  
  	return tpmc->soc->io_pads[group].name;
  }
  
  static int tegra_pmc_io_pads_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
  						    unsigned int group,
  						    const unsigned int **pins,
  						    unsigned int *num_pins)
  {
  	struct tegra_pmc *tpmc = pinctrl_dev_get_drvdata(pctldev);
  
  	*pins = tpmc->soc->io_pads[group].pins;
  	*num_pins = tpmc->soc->io_pads[group].npins;
  
  	return 0;
  }
  
  enum tegra_io_rail_pads_params {
  	TEGRA_IO_PAD_POWER_SOURCE_VOLTAGE = PIN_CONFIG_END + 1,
  	TEGRA_IO_PAD_DYNAMIC_VOLTAGE_SWITCH = PIN_CONFIG_END + 2,
  };
  
  static const struct pinconf_generic_params tegra_io_pads_cfg_params[] = {
  	{
  		.property = "nvidia,power-source-voltage",
  		.param = TEGRA_IO_PAD_POWER_SOURCE_VOLTAGE,
  	}, {
  		.property = "nvidia,enable-voltage-switching",
  		.param = TEGRA_IO_PAD_DYNAMIC_VOLTAGE_SWITCH,
  	},
  };
  
  static const struct pinctrl_ops tegra_pmc_io_pads_pinctrl_ops = {
  	.get_groups_count = tegra_pmc_io_pads_pinctrl_get_groups_count,
  	.get_group_name	= tegra_pmc_io_pads_pinctrl_get_group_name,
  	.get_group_pins	= tegra_pmc_io_pads_pinctrl_get_group_pins,
  	.dt_node_to_map	= pinconf_generic_dt_node_to_map_pin,
  	.dt_free_map	= pinconf_generic_dt_free_map,
  };
  
  static int tegra_pmc_io_pads_pinconf_get(struct pinctrl_dev *pctldev,
  					 unsigned int pin,
  					 unsigned long *config)
  {
  	struct tegra_pmc *tpmc = pinctrl_dev_get_drvdata(pctldev);
  	u16 param = pinconf_to_config_param(*config);
  	const struct tegra_pmc_io_pad_soc *pad = &tpmc->soc->io_pads[pin];
  	u16 arg = 0;
  	int ret;
  
  	switch (param) {
  	case PIN_CONFIG_LOW_POWER_MODE:
  		ret = tegra_pmc_io_pad_is_powered(pad);
  		if (ret < 0)
  			return ret;
  		arg = !ret;
  		break;
  
  	case TEGRA_IO_PAD_POWER_SOURCE_VOLTAGE:
  		if (pmc->soc->io_pads[pin].voltage == UINT_MAX)
  			return -EINVAL;
  
  		ret = _tegra_pmc_io_pad_get_voltage(pad);
  		if (ret < 0)
  			return ret;
  		arg = ret;
  
  		break;
  
  	case TEGRA_IO_PAD_DYNAMIC_VOLTAGE_SWITCH:
  		if (pmc->soc->io_pads[pin].voltage == UINT_MAX)
  			return -EINVAL;
  
  		if (pmc->voltage_switch_restriction_enabled &&
  		    pmc->allow_dynamic_switch[pin])
  			arg = 1;
  		else
  			arg = 0;
  		break;
  
  	default:
  		dev_dbg(tpmc->dev, "I/O pad %s does not support param %d\n",
  			pad->name, param);
  		return -EINVAL;
  	}
  
  	*config = pinconf_to_config_packed(param, arg);
  
  	return 0;
  }
  
  static int tegra_pmc_io_pads_pinconf_set(struct pinctrl_dev *pctldev,
  					 unsigned int pin,
  					 unsigned long *configs,
  					 unsigned int num_configs)
  {
  	struct tegra_pmc *tpmc = pinctrl_dev_get_drvdata(pctldev);
  	const struct tegra_pmc_io_pad_soc *pad = &tpmc->soc->io_pads[pin];
  	unsigned int i;
  
  	for (i = 0; i < num_configs; i++) {
  		int ret;
  		u16 param_val = pinconf_to_config_argument(configs[i]);
  		u16 param = pinconf_to_config_param(configs[i]);
  
  		switch (param) {
  		case PIN_CONFIG_LOW_POWER_MODE:
  			if (param_val)
  				ret = tegra_pmc_io_pad_power_disable(pad);
  			else
  				ret = tegra_pmc_io_pad_power_enable(pad);
  			if (ret < 0) {
  				dev_err(tpmc->dev,
  					"Failed to set low power %s of I/O pad %s: %d\n",
  					(param_val) ? "disable" : "enable",
  					pad->name, ret);
  				return ret;
  			}
  
  			break;
  
  		case TEGRA_IO_PAD_POWER_SOURCE_VOLTAGE:
  			if (pmc->soc->io_pads[pin].voltage == UINT_MAX)
  				return -EINVAL;
  
  			if (pmc->voltage_switch_restriction_enabled &&
  			    !pmc->allow_dynamic_switch[pin]) {
  				dev_err(tpmc->dev,
  					"IO Pad %s: Dynamic voltage switching not allowed\n",
  					pad->name);
  				return -EINVAL;
  			}
  
  			ret = _tegra_pmc_io_pad_set_voltage(pad, param_val);
  			if (ret < 0) {
  				dev_err(tpmc->dev,
  					"Failed to set voltage %d of pin %u: %d\n",
  					param_val, pin, ret);
  				return ret;
  			}
  
  			break;
  
  		case TEGRA_IO_PAD_DYNAMIC_VOLTAGE_SWITCH:
  			if (pmc->soc->io_pads[pin].voltage == UINT_MAX)
  				return -EINVAL;
  
  			pmc->allow_dynamic_switch[pin] = true;
  			break;
  
  		default:
  			dev_err(tpmc->dev, "I/O pad %s does not support param %d\n",
  				pad->name, param);
  			return -EINVAL;
  		}
  	}
  
  	return 0;
  }
  
  #ifdef CONFIG_DEBUG_FS
  static void tegra_pmc_io_pads_pinconf_dbg_show(struct pinctrl_dev *pctldev,
  					       struct seq_file *s,
  					       unsigned int pin)
  {
  	struct tegra_pmc *tpmc = pinctrl_dev_get_drvdata(pctldev);
  	unsigned long config = 0;
  	u16 param, param_val;
  	int ret;
  	int i;
  
  	for (i = 0; i < tpmc->pinctrl_desc.num_custom_params; ++i) {
  		param = tpmc->pinctrl_desc.custom_params[i].param;
  		config = pinconf_to_config_packed(param, 0);
  		ret = tegra_pmc_io_pads_pinconf_get(pctldev, pin, &config);
  		if (ret < 0)
  			continue;
  		param_val = pinconf_to_config_argument(config);
  		switch (param) {
  		case TEGRA_IO_PAD_POWER_SOURCE_VOLTAGE:
  			if (param_val == TEGRA_IO_PAD_VOLTAGE_1200000UV)
  				seq_puts(s, "\n\t\tPad voltage 1200000uV");
  			else if (param_val == TEGRA_IO_PAD_VOLTAGE_1800000UV)
  				seq_puts(s, "\n\t\tPad voltage 1800000uV");
  			else
  				seq_puts(s, "\n\t\tPad voltage 3300000uV");
  			break;
  
  		case TEGRA_IO_PAD_DYNAMIC_VOLTAGE_SWITCH:
  			seq_printf(s, "\n\t\tSwitching voltage: %s",
  				 (param_val) ? "Enable" : "Disable");
  			break;
  		default:
  			break;
  		}
  	}
  }
  #else
  static void tegra_pmc_io_pads_pinconf_dbg_show(struct pinctrl_dev *pctldev,
  					       struct seq_file *s,
  					       unsigned int pin)
  {
  }
  
  #endif
  
  static const struct pinconf_ops tegra_pmc_io_pads_pinconf_ops = {
  	.pin_config_get = tegra_pmc_io_pads_pinconf_get,
  	.pin_config_set = tegra_pmc_io_pads_pinconf_set,
  	.pin_config_dbg_show = tegra_pmc_io_pads_pinconf_dbg_show,
  	.is_generic = true,
  };
  
  static int tegra_pmc_io_pads_pinctrl_init(struct tegra_pmc *pmc)
  {
  	if (!pmc->soc->num_descs)
  		return 0;
  
  	pmc->allow_dynamic_switch = devm_kzalloc(pmc->dev, pmc->soc->num_descs *
  					 sizeof(*pmc->allow_dynamic_switch),
  					 GFP_KERNEL);
  	if (!pmc->allow_dynamic_switch) {
  		dev_err(pmc->dev, "Failed to allocate allow_dynamic_switch\n");
  		return 0;
  	}
  
  	pmc->voltage_switch_restriction_enabled = false;
  	pmc->pinctrl_desc.name = "pinctrl-pmc-io-pads";
  	pmc->pinctrl_desc.pctlops = &tegra_pmc_io_pads_pinctrl_ops;
  	pmc->pinctrl_desc.confops = &tegra_pmc_io_pads_pinconf_ops;
  	pmc->pinctrl_desc.pins = pmc->soc->descs;
  	pmc->pinctrl_desc.npins = pmc->soc->num_descs;
  	pmc->pinctrl_desc.custom_params = tegra_io_pads_cfg_params;
  	pmc->pinctrl_desc.num_custom_params =
  				ARRAY_SIZE(tegra_io_pads_cfg_params);
  
  	pmc->pctl = devm_pinctrl_register(pmc->dev, &pmc->pinctrl_desc, pmc);
  	if (IS_ERR(pmc->pctl)) {
  		int ret = PTR_ERR(pmc->pctl);
  
  		dev_err(pmc->dev, "Failed to register pinctrl-io-pad: %d\n",
  			ret);
  		return ret;
  	}
  
  	pmc->voltage_switch_restriction_enabled =
  			of_property_read_bool(pmc->dev->of_node,
  			      "nvidia,restrict-voltage-switch");
  
  	return 0;
  }
  
  static const struct tegra_pmc_io_pad_soc *tegra_pmc_get_pad_by_name(
  				const char *pad_name)
  {
  	unsigned int i;
  
  	for (i = 0; i < pmc->soc->num_io_pads; ++i) {
  		if (!strcmp(pad_name, pmc->soc->io_pads[i].name))
  			return &pmc->soc->io_pads[i];
  	}
  
  	return NULL;
  }
  
  static int tegra_pmc_get_dpd_masks_by_names(const char * const *io_pads,
  					    int n_iopads, u32 *mask)
  {
  	const struct tegra_pmc_io_pad_soc *pad;
  	int i;
  
  	*mask = 0;
  
  	for (i = 0; i < n_iopads; i++) {
  		pad = tegra_pmc_get_pad_by_name(io_pads[i]);
  		if (!pad) {
  			dev_err(pmc->dev, "IO pad %s not found\n", io_pads[i]);
  			return -EINVAL;
  		}
  		*mask |= BIT(pad->dpd);
  	}
  
  	return 0;
  }
  
  int tegra_pmc_io_pad_low_power_enable(const char *pad_name)
  {
  	const struct tegra_pmc_io_pad_soc *pad;
  
  	pad = tegra_pmc_get_pad_by_name(pad_name);
  	if (!pad) {
  		dev_err(pmc->dev, "IO Pad %s not found\n", pad_name);
  		return -EINVAL;
  	}
  
  	return tegra_pmc_io_pad_power_disable(pad);
  }
  EXPORT_SYMBOL(tegra_pmc_io_pad_low_power_enable);
  
  int tegra_pmc_io_pad_low_power_disable(const char *pad_name)
  {
  	const struct tegra_pmc_io_pad_soc *pad;
  
  	pad = tegra_pmc_get_pad_by_name(pad_name);
  	if (!pad) {
  		dev_err(pmc->dev, "IO Pad %s not found\n", pad_name);
  		return -EINVAL;
  	}
  
  	return tegra_pmc_io_pad_power_enable(pad);
  }
  EXPORT_SYMBOL(tegra_pmc_io_pad_low_power_disable);
  
  int tegra_pmc_io_pad_set_voltage(const char *pad_name, unsigned int pad_uv)
  {
  	int io_pad_uv;
  	const struct tegra_pmc_io_pad_soc *pad;
  
  	pad = tegra_pmc_get_pad_by_name(pad_name);
  	if (!pad) {
  		dev_err(pmc->dev, "IO Pad %s not found\n", pad_name);
  		return -EINVAL;
  	}
  
  	switch (pad_uv) {
  	case 1800000:
  		io_pad_uv = TEGRA_IO_PAD_VOLTAGE_1800000UV;
  		break;
  
  	case 3300000:
  		io_pad_uv = TEGRA_IO_PAD_VOLTAGE_3300000UV;
  		break;
  
  	default:
  		return -EINVAL;
  	}
  
  	return _tegra_pmc_io_pad_set_voltage(pad, io_pad_uv);
  }
  EXPORT_SYMBOL(tegra_pmc_io_pad_set_voltage);
  
  int tegra_pmc_io_pad_get_voltage(const char *pad_name)
  {
  	int io_pad_uv;
  	const struct tegra_pmc_io_pad_soc *pad;
  
  	pad = tegra_pmc_get_pad_by_name(pad_name);
  	if (!pad) {
  		dev_err(pmc->dev, "IO Pad %s not found\n", pad_name);
  		return -EINVAL;
  	}
  
  	io_pad_uv = _tegra_pmc_io_pad_get_voltage(pad);
  	if (io_pad_uv < 0)
  		return -EINVAL;
  
  	switch (io_pad_uv) {
  	case TEGRA_IO_PAD_VOLTAGE_1800000UV:
  		return 1800000;
  
  	case TEGRA_IO_PAD_VOLTAGE_3300000UV:
  		return 3300000;
  
  	default:
  		break;
  	}
  
  	return -EINVAL;
  }
  EXPORT_SYMBOL(tegra_pmc_io_pad_get_voltage);
  
  int tegra_pmc_nvcsi_brick_getstatus(const char *pad_name)
  {
  	const struct tegra_pmc_io_pad_soc *pad;
  	u32 value;
  
  	pad = tegra_pmc_get_pad_by_name(pad_name);
  	if (!pad) {
  		dev_err(pmc->dev, "IO Pad %s not found\n", pad_name);
  		return -EINVAL;
  	}
  
  	value = tegra_pmc_reg_readl(pad->dpd_status_reg);
  	return !!(value & BIT(pad->dpd));
  }
  EXPORT_SYMBOL(tegra_pmc_nvcsi_brick_getstatus);
  
  int tegra_pmc_nvcsi_ab_brick_dpd_enable(void)
  {
  	u32 pad_mask;
  	int ret;
  
  	ret = tegra_pmc_get_dpd_masks_by_names(nvcsi_ab_bricks_pads,
  					       ARRAY_SIZE(nvcsi_ab_bricks_pads),
  					       &pad_mask);
  	if (ret < 0)
  		return ret;
  
  	tegra_pmc_reg_writel(IO_DPD_REQ_CODE_ON | pad_mask, TEGRA_PMC_IO_DPD_REQ);
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_nvcsi_ab_brick_dpd_enable);
  
  int tegra_pmc_nvcsi_ab_brick_dpd_disable(void)
  {
  	u32 pad_mask;
  	int ret;
  
  	ret = tegra_pmc_get_dpd_masks_by_names(nvcsi_ab_bricks_pads,
  					       ARRAY_SIZE(nvcsi_ab_bricks_pads),
  					       &pad_mask);
  	if (ret < 0)
  		return ret;
  
  	tegra_pmc_reg_writel(IO_DPD_REQ_CODE_OFF | pad_mask, TEGRA_PMC_IO_DPD_REQ);
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_nvcsi_ab_brick_dpd_disable);
  
  int tegra_pmc_nvcsi_cdef_brick_dpd_enable(void)
  {
  	u32 pad_mask;
  	int ret;
  
  	ret = tegra_pmc_get_dpd_masks_by_names(nvcsi_cdef_bricks_pads,
  					       ARRAY_SIZE(nvcsi_cdef_bricks_pads),
  					       &pad_mask);
  	if (ret < 0)
  		return ret;
  
  	tegra_pmc_reg_writel(IO_DPD_REQ_CODE_ON | pad_mask, TEGRA_PMC_IO_DPD2_REQ);
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_nvcsi_cdef_brick_dpd_enable);
  
  int tegra_pmc_nvcsi_cdef_brick_dpd_disable(void)
  {
  	u32 pad_mask = 0;
  	int ret;
  
  	ret = tegra_pmc_get_dpd_masks_by_names(nvcsi_cdef_bricks_pads,
  					       ARRAY_SIZE(nvcsi_cdef_bricks_pads),
  					       &pad_mask);
  	if (ret < 0)
  		return ret;
  
  	tegra_pmc_reg_writel(IO_DPD_REQ_CODE_OFF | pad_mask, TEGRA_PMC_IO_DPD2_REQ);
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_nvcsi_cdef_brick_dpd_disable);
  
  int tegra_pmc_pwm_blink_enable(void)
  {
  	tegra_pmc_register_update(TEGRA_PMC_DPD_PADS_ORIDE,
  				  TEGRA210_PMC_DPD_PADS_ORIDE_BLINK,
  				  TEGRA210_PMC_DPD_PADS_ORIDE_BLINK);
  
  	tegra_pmc_register_update(TEGRA_PMC_CNTRL, TEGRA210_PMC_CTRL_BLINK_EN,
  				  TEGRA210_PMC_CTRL_BLINK_EN);
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_pwm_blink_enable);
  
  int tegra_pmc_pwm_blink_disable(void)
  {
  	tegra_pmc_register_update(TEGRA_PMC_CNTRL, TEGRA210_PMC_CTRL_BLINK_EN,
  				  0);
  
  	tegra_pmc_register_update(TEGRA_PMC_DPD_PADS_ORIDE,
  				  TEGRA210_PMC_DPD_PADS_ORIDE_BLINK, 0);
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_pwm_blink_disable);
  
  int tegra_pmc_pwm_blink_config(int duty_ns, int period_ns)
  {
  	int data_on;
  	int data_off;
  	u32 val;
  
  	tegra_pmc_register_update(TEGRA_PMC_CNTRL,
  				  TEGRA210_PMC_CTRL_BLINK_EN, 0);
  	udelay(64);
  
  	/* 16 x 32768 Hz = 1000000000/(32768*16) = 488281ns */
  	data_on = (duty_ns - 30517) / 488281;
  	data_off = (period_ns - duty_ns - 30517) / 488281;
  
  	if (data_off > 0xFFFF)
  		data_off = 0xFFFF;
  
  	if (data_on > 0x7FFF)
  		data_on = 0x7FFF;
  
  	val = (data_off << 16) | BIT(15) | data_on;
  	tegra_pmc_reg_writel(val, TEGRA_PMC_BLINK_TIMER);
  	udelay(64);
  	tegra_pmc_register_update(TEGRA_PMC_CNTRL,
  				  TEGRA210_PMC_CTRL_BLINK_EN, 1);
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_pwm_blink_config);
  
  int tegra_pmc_soft_led_blink_enable(void)
  {
  	tegra_pmc_register_update(TEGRA_PMC_DPD_PADS_ORIDE,
  				  TEGRA210_PMC_DPD_PADS_ORIDE_BLINK,
  				  TEGRA210_PMC_DPD_PADS_ORIDE_BLINK);
  
  	tegra_pmc_register_update(TEGRA_PMC_CNTRL,
  				  TEGRA210_PMC_CTRL_BLINK_EN, 0);
  
  	tegra_pmc_register_update(TEGRA_PMC_LED_BREATHING_CTRL,
  				  PMC_LED_BREATHING_EN,
  				  PMC_LED_BREATHING_EN);
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_soft_led_blink_enable);
  
  int tegra_pmc_soft_led_blink_disable(void)
  {
  	tegra_pmc_register_update(TEGRA_PMC_DPD_PADS_ORIDE,
  				  TEGRA210_PMC_DPD_PADS_ORIDE_BLINK,
  				  TEGRA210_PMC_DPD_PADS_ORIDE_BLINK);
  
  	tegra_pmc_register_update(TEGRA_PMC_LED_BREATHING_CTRL,
  				  PMC_LED_BREATHING_EN, 0);
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_soft_led_blink_disable);
  
  int tegra_pmc_soft_led_blink_configure(int duty_cycle_ns, int ll_period_ns,
  				       int ramp_time_ns)
  {
  	int plateau_cnt;
  	int plateau_ns;
  	int period;
  
  	if (duty_cycle_ns) {
  		plateau_ns = duty_cycle_ns - (2 * ramp_time_ns);
  		if (plateau_ns < 0) {
  			dev_err(pmc->dev, "duty cycle is less than 2xramptime:\n");
  			return -EINVAL;
  		}
  
  		plateau_cnt = plateau_ns / PMC_LED_SOFT_BLINK_1CYCLE_NS;
  		tegra_pmc_reg_writel(plateau_cnt, TEGRA_PMC_LED_BREATHING_COUNTER1);
  	}
  
  	if (ll_period_ns) {
  		period = ll_period_ns / PMC_LED_SOFT_BLINK_1CYCLE_NS;
  		tegra_pmc_reg_writel(period, TEGRA_PMC_LED_BREATHING_COUNTER3);
  	}
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_soft_led_blink_configure);
  
  int tegra_pmc_soft_led_blink_set_ramptime(int ramp_time_ns)
  {
  	u32 nsteps;
  	u32 rt_nanoseconds = 0;
  
  	if (ramp_time_ns < 0)
  		return -EINVAL;
  
  	/* (n + 1) x (n + 2) * 1 cycle = ramp_time */
  	/* 1 cycle = 1/32 KHz duration = 32000000ns*/
  	for (nsteps = 0; rt_nanoseconds < ramp_time_ns; nsteps++) {
  		rt_nanoseconds = (nsteps * nsteps) + (3 * nsteps) + 2;
  		rt_nanoseconds = rt_nanoseconds * PMC_LED_SOFT_BLINK_1CYCLE_NS;
  	}
  
  	tegra_pmc_reg_writel(nsteps - 1, TEGRA_PMC_LED_BREATHING_COUNTER0);
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_soft_led_blink_set_ramptime);
  
  int tegra_pmc_soft_led_blink_set_short_period(int short_low_period_ns)
  {
  	u32 period;
  
  	if (short_low_period_ns < 0)
  		return -EINVAL;
  
  	if (short_low_period_ns) {
  		/* enable and configure short low period */
  		period = short_low_period_ns / PMC_LED_SOFT_BLINK_1CYCLE_NS;
  
  		tegra_pmc_reg_writel(period, TEGRA_PMC_LED_BREATHING_COUNTER2);
  		tegra_pmc_register_update(TEGRA_PMC_LED_BREATHING_CTRL,
  					  PMC_SHORT_LOW_PERIOD_EN,
  					  PMC_SHORT_LOW_PERIOD_EN);
  	} else {
  		/* disable short low period */
  		tegra_pmc_register_update(TEGRA_PMC_LED_BREATHING_CTRL,
  					  PMC_SHORT_LOW_PERIOD_EN, 0);
  	}
  
  	return 0;
  }
  EXPORT_SYMBOL(tegra_pmc_soft_led_blink_set_short_period);
  
  static int tegra_pmc_parse_dt(struct tegra_pmc *pmc, struct device_node *np)
  {
  	u32 value, values[2];
  
  	if (of_property_read_u32(np, "nvidia,suspend-mode", &value)) {
  	} else {
  		switch (value) {
  		case 0:
  			if (tegra_get_chip_id() == TEGRA210)
  				pmc->suspend_mode = TEGRA_SUSPEND_SC7;
  			else
  				pmc->suspend_mode = TEGRA_SUSPEND_LP0;
  			break;
  
  		case 1:
  			pmc->suspend_mode = TEGRA_SUSPEND_LP1;
  			break;
  
  		case 2:
  			pmc->suspend_mode = TEGRA_SUSPEND_LP2;
  			break;
  
  		default:
  			pmc->suspend_mode = TEGRA_SUSPEND_NONE;
  			break;
  		}
  	}
  
  #ifdef CONFIG_ARM
  	pmc->suspend_mode = tegra_pm_validate_suspend_mode(pmc->suspend_mode);
  #endif
  
  	if (of_property_read_u32(np, "nvidia,cpu-pwr-good-time", &value))
  		pmc->suspend_mode = TEGRA_SUSPEND_NONE;
  
  	pmc->cpu_good_time = value;
  
  	if (of_property_read_u32(np, "nvidia,cpu-pwr-off-time", &value))
  		pmc->suspend_mode = TEGRA_SUSPEND_NONE;
  
  	pmc->cpu_off_time = value;
  
  	if (of_property_read_u32_array(np, "nvidia,core-pwr-good-time",
  				       values, ARRAY_SIZE(values)))
  		pmc->suspend_mode = TEGRA_SUSPEND_NONE;
  
  	pmc->core_osc_time = values[0];
  	pmc->core_pmu_time = values[1];
  
  	if (of_property_read_u32(np, "nvidia,core-pwr-off-time", &value))
  		pmc->suspend_mode = TEGRA_SUSPEND_NONE;
  
  	pmc->core_off_time = value;
  
  	pmc->corereq_high = of_property_read_bool(np,
  				"nvidia,core-pwr-req-active-high");
  	if (!pmc->corereq_high)
  		pmc->corereq_high = of_property_read_bool(np,
  					"nvidia,core-power-req-active-high");
  
  	pmc->sysclkreq_high = of_property_read_bool(np,
  				"nvidia,sys-clock-req-active-high");
  
  	pmc->combined_req = of_property_read_bool(np,
  				"nvidia,combined-power-req");
  
  	pmc->cpu_pwr_good_en = of_property_read_bool(np,
  				"nvidia,cpu-pwr-good-en");
  
  	if (pmc->lp0_vec_size && pmc->lp0_vec_phys)
  		goto out;
  
  	values[0] = values[1] = 0;
  	if (of_property_read_u32_array(np, "nvidia,lp0-vec", values,
  				       ARRAY_SIZE(values)))
  		if (pmc->suspend_mode == TEGRA_SUSPEND_LP0)
  			pmc->suspend_mode = TEGRA_SUSPEND_LP1;
  
  	pmc->lp0_vec_phys = values[0];
  	pmc->lp0_vec_size = values[1];
  
  out:
  	return 0;
  }
  
  static void tegra_pmc_init(struct tegra_pmc *pmc)
  {
  	u32 value;
  
  	if (pmc->soc->skip_legacy_pmc_init)
  		return;
  
  	/* Always enable CPU power request */
  	value = tegra_pmc_reg_readl(TEGRA_PMC_CNTRL);
  	value |= PMC_CNTRL_CPU_PWRREQ_OE;
  	tegra_pmc_reg_writel(value, TEGRA_PMC_CNTRL);
  
  	value = tegra_pmc_reg_readl(TEGRA_PMC_CNTRL);
  
  	if (pmc->sysclkreq_high)
  		value &= ~PMC_CNTRL_SYSCLK_POLARITY;
  	else
  		value |= PMC_CNTRL_SYSCLK_POLARITY;
  
  	if (pmc->corereq_high)
  		value &= ~PMC_CNTRL_PWRREQ_POLARITY;
  	else
  		value |= PMC_CNTRL_PWRREQ_POLARITY;
  
  	/* configure the output polarity while the request is tristated */
  	tegra_pmc_reg_writel(value, TEGRA_PMC_CNTRL);
  
  	/* now enable the request */
  	value = tegra_pmc_reg_readl(TEGRA_PMC_CNTRL);
  	value |= PMC_CNTRL_SYSCLK_OE;
  	tegra_pmc_reg_writel(value, TEGRA_PMC_CNTRL);
  
  	set_core_power_timers();
  	tegra_pmc_syscore_init();
  	tegra_pmc_wake_syscore_init();
  }
  
  void tegra_pmc_init_tsense_reset(struct tegra_pmc *pmc)
  {
  	static const char disabled[] = "emergency thermal reset disabled";
  	u32 pmu_addr, ctrl_id, reg_addr, reg_data, pinmux;
  	struct device *dev = pmc->dev;
  	struct device_node *np;
  	u32 value, checksum;
  
  	if (!pmc->soc->has_tsense_reset)
  		return;
  
  	np = of_get_child_by_name(pmc->dev->of_node, "i2c-thermtrip");
  	if (!np) {
  		dev_warn(dev, "i2c-thermtrip node not found, %s.\n", disabled);
  		return;
  	}
  
  	if (of_property_read_u32(np, "nvidia,i2c-controller-id", &ctrl_id)) {
  		dev_err(dev, "I2C controller ID missing, %s.\n", disabled);
  		goto out;
  	}
  
  	if (of_property_read_u32(np, "nvidia,bus-addr", &pmu_addr)) {
  		dev_err(dev, "nvidia,bus-addr missing, %s.\n", disabled);
  		goto out;
  	}
  
  	if (of_property_read_u32(np, "nvidia,reg-addr", &reg_addr)) {
  		dev_err(dev, "nvidia,reg-addr missing, %s.\n", disabled);
  		goto out;
  	}
  
  	if (of_property_read_u32(np, "nvidia,reg-data", &reg_data)) {
  		dev_err(dev, "nvidia,reg-data missing, %s.\n", disabled);
  		goto out;
  	}
  
  	if (of_property_read_u32(np, "nvidia,pinmux-id", &pinmux))
  		pinmux = 0;
  
  	value = tegra_pmc_reg_readl(TEGRA_PMC_SENSOR_CTRL);
  	value |= PMC_SENSOR_CTRL_SCRATCH_WRITE;
  	tegra_pmc_reg_writel(value, TEGRA_PMC_SENSOR_CTRL);
  
  	value = (reg_data << PMC_SCRATCH54_DATA_SHIFT) |
  		(reg_addr << PMC_SCRATCH54_ADDR_SHIFT);
  	tegra_pmc_reg_writel(value, TEGRA_PMC_SCRATCH54);
  
  	value = PMC_SCRATCH55_RESET_TEGRA;
  	value |= ctrl_id << PMC_SCRATCH55_CNTRL_ID_SHIFT;
  	value |= pinmux << PMC_SCRATCH55_PINMUX_SHIFT;
  	value |= pmu_addr << PMC_SCRATCH55_I2CSLV1_SHIFT;
  
  	/*
  	 * Calculate checksum of SCRATCH54, SCRATCH55 fields. Bits 23:16 will
  	 * contain the checksum and are currently zero, so they are not added.
  	 */
  	checksum = reg_addr + reg_data + (value & 0xff) + ((value >> 8) & 0xff)
  		+ ((value >> 24) & 0xff);
  	checksum &= 0xff;
  	checksum = 0x100 - checksum;
  
  	value |= checksum << PMC_SCRATCH55_CHECKSUM_SHIFT;
  
  	tegra_pmc_reg_writel(value, TEGRA_PMC_SCRATCH55);
  
  	value = tegra_pmc_reg_readl(TEGRA_PMC_SENSOR_CTRL);
  	value |= PMC_SENSOR_CTRL_ENABLE_RST;
  	tegra_pmc_reg_writel(value, TEGRA_PMC_SENSOR_CTRL);
  
  	dev_info(pmc->dev, "emergency thermal reset enabled\n");
  
  out:
  	of_node_put(np);
  }
  
  #ifdef CONFIG_DEBUG_FS
  struct tegra_pmc_scratch_export_info {
  	const char **reg_names;
  	u32 *reg_offset;
  	int cnt_reg_offset;
  	int cnt_reg_names;
  };
  static struct tegra_pmc_scratch_export_info scratch_info;
  
  static inline u32 tegra_pmc_debug_scratch_readl(u32 reg)
  {
  	return readl(pmc->reboot_base + reg);
  }
  
  static inline void tegra_pmc_debug_scratch_writel(u32 val, u32 reg)
  {
  	writel(val, pmc->reboot_base + reg);
  }
  
  static ssize_t tegra_pmc_debug_scratch_reg_read(struct file *file,
  						char __user *user_buf,
  						size_t count, loff_t *ppos)
  {
  	char buf[64] = {};
  	unsigned char *dfsname = file->f_path.dentry->d_iname;
  	ssize_t ret;
  	u32 value;
  	int id;
  
  	for (id = 0; id < scratch_info.cnt_reg_offset; id++) {
  		if (!strcmp(dfsname, scratch_info.reg_names[id]))
  			break;
  	}
  
  	if (id == scratch_info.cnt_reg_offset)
  		return -EINVAL;
  
  	value = tegra_pmc_debug_scratch_readl(scratch_info.reg_offset[id]);
  	ret = snprintf(buf, sizeof(buf), "Reg: 0x%x : Value: 0x%x\n",
  				scratch_info.reg_offset[id], value);
  
  	return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
  }
  
  static ssize_t tegra_pmc_debug_scratch_reg_write(struct file *file,
  						 const char __user *user_buf,
  						 size_t count, loff_t *ppos)
  {
  	char buf[64] = { };
  	unsigned char *dfsname = file->f_path.dentry->d_iname;
  	ssize_t buf_size;
  	u32 value = 0;
  	int id;
  
  	for (id = 0; id < scratch_info.cnt_reg_offset; id++) {
  		if (!strcmp(dfsname, scratch_info.reg_names[id]))
  			break;
  	}
  	if (id == scratch_info.cnt_reg_offset)
  		return -EINVAL;
  
  	buf_size = min(count, (sizeof(buf) - 1));
  	if (copy_from_user(buf, user_buf, buf_size))
  		return -EFAULT;
  
  	if (!sscanf(buf, "%x\n", &value))
  		return -EINVAL;
  
  	pr_info("PMC reg: 0x%x Value: 0x%x\n", scratch_info.reg_offset[id], value);
  	tegra_pmc_debug_scratch_writel(value, scratch_info.reg_offset[id]);
  
  	return count;
  }
  
  static const struct file_operations pmc_debugfs_fops = {
  	.open		= simple_open,
  	.write		= tegra_pmc_debug_scratch_reg_write,
  	.read		= tegra_pmc_debug_scratch_reg_read,
  };
  
  static int tegra_pmc_debug_scratch_reg_init(struct tegra_pmc *pmc)
  {
  	struct device_node *np = pmc->dev->of_node;
  	const char *srname;
  	struct property *prop;
  	int count, i;
  	int ret;
  	int cnt_reg_names, cnt_reg_offset;
  	struct dentry *dbgfs_root;
  
  	cnt_reg_offset = of_property_count_u32_elems(np,
  					"export-pmc-scratch-reg-offset");
  	if (cnt_reg_offset <= 0) {
  		dev_info(pmc->dev, "scratch reg offset dts data not present\n");
  		return -EINVAL;
  	}
  
  	scratch_info.cnt_reg_offset = cnt_reg_offset;
  
  	cnt_reg_names = of_property_count_strings(np,
  				"export-pmc-scratch-reg-name");
  	if (cnt_reg_names < 0 || (cnt_reg_offset != cnt_reg_names)) {
  		dev_info(pmc->dev, "reg offset and names count not matching\n");
  		return -EINVAL;
  	}
  
  	scratch_info.cnt_reg_names = cnt_reg_names;
  	scratch_info.reg_names = devm_kzalloc(pmc->dev, (cnt_reg_offset + 1) *
  					   sizeof(*scratch_info.reg_names),
  					   GFP_KERNEL);
  	if (!scratch_info.reg_names)
  		return -ENOMEM;
  
  	count = 0;
  	of_property_for_each_string(np, "export-pmc-scratch-reg-name",
  				    prop, srname)
  		scratch_info.reg_names[count++] = srname;
  
  	scratch_info.reg_names[count] = NULL;
  
  	scratch_info.reg_offset = devm_kzalloc(pmc->dev, sizeof(u32) *
  					    cnt_reg_offset, GFP_KERNEL);
  	if (!scratch_info.reg_offset)
  		return -ENOMEM;
  
  	ret = of_property_read_u32_array(np, "export-pmc-scratch-reg-offset",
  					 scratch_info.reg_offset, cnt_reg_offset);
  	if (ret < 0)
  		return -ENODEV;
  
  	dbgfs_root = debugfs_create_dir("PMC", NULL);
  	if (!dbgfs_root) {
  		dev_info(pmc->dev, "PMC:Failed to create debugfs dir\n");
  		return -ENOMEM;
  	}
  
  	for (i = 0; i < cnt_reg_offset; i++) {
  		debugfs_create_file(scratch_info.reg_names[i], S_IRUGO | S_IWUSR,
  				    dbgfs_root, NULL, &pmc_debugfs_fops);
  		dev_info(pmc->dev, "create /sys/kernel/debug/%s/%s\n",
  			 dbgfs_root->d_name.name, scratch_info.reg_names[i]);
  	}
  
  	return 0;
  }
  
  #else
  static int tegra_pmc_debug_scratch_reg_init(struct tegra_pmc *pmc)
  {
  	return 0;
  }
  #endif
  
  bool tegra_pmc_is_halt_in_fiq(void)
  {
  	return !!(PMC_IMPL_HALT_IN_FIQ_MASK &
  		tegra_pmc_reg_readl(TEGRA_PMC_IMPL_RAMDUMP_CTL_STATUS));
  }
  EXPORT_SYMBOL(tegra_pmc_is_halt_in_fiq);
  
  static void tegra_pmc_halt_in_fiq_init(struct tegra_pmc *pmc)
  {
  	struct device_node *np = pmc->dev->of_node;
  
  	if (!of_property_read_bool(np, "nvidia,enable-halt-in-fiq"))
  		return;
  
  	tegra_pmc_register_update(TEGRA_PMC_IMPL_RAMDUMP_CTL_STATUS,
  				  PMC_IMPL_HALT_IN_FIQ_MASK,
  				  PMC_IMPL_HALT_IN_FIQ_MASK);
  }
  
  char *pmc_reset_reason_string[] = {
  	"TEGRA_POWER_ON_RESET",
  	"TEGRA_AO_WATCHDOG",
  	"TEGRA_BCCPLEX_WATCHDOG",
  	"TEGRA_BPMP_WATCHDOG",
  	"TEGRA_SCE_WATCHDOG",
  	"TEGRA_SPE_WATCHDOG",
  	"TEGRA_APE_WATCHDOG",
  	"TEGRA_LCCPLEX_WATCHDOG",
  	"TEGRA_SENSOR",
  	"TEGRA_AOTAG",
  	"TEGRA_VFSENSOR",
  	"TEGRA_SOFTWARE_RESET",
  	"TEGRA_SC7",
  	"TEGRA_HSM",
  	"TEGRA_CSITE",
  	"TEGRA_WATCHDOG",
  	"TEGRA_LP0",
  	"TEGRA_RESET_REASON_MAX"
  };
  
  static char *pmc_reset_level_string[] = {
  	"TEGRA_RESET_LEVEL_L0",
  	"TEGRA_RESET_LEVEL_L1",
  	"TEGRA_RESET_LEVEL_L2",
  	"TEGRA_RESET_LEVEL_WARM",
  	"TEGRA_RESET_LEVEL_NOT_SUPPORTED",
  };
  
  static void tegra_pmc_show_reset_status(void)
  {
  	u32 reset_reg;
  	enum tegra_system_reset_reason reset_reason = tegra_pmc_get_system_reset_reason();
  	enum tegra_system_reset_level reset_level = tegra_pmc_get_system_reset_level();
  
  	reset_reg = tegra_pmc_reg_readl(TEGRA_PMC_RST_STATUS);
  
  	pr_info("### PMC reset source: %s\n", pmc_reset_reason_string[reset_reason]);
  	pr_info("### PMC reset level: %s\n", pmc_reset_level_string[reset_level]);
  	pr_info("### PMC reset status reg: 0x%x\n", reset_reg);
  }
  
  #if defined(CONFIG_DEBUG_FS)
  static int pmc_reset_show(struct seq_file *s, void *data)
  {
  	enum tegra_system_reset_reason reset_reason = tegra_pmc_get_system_reset_reason();
  	enum tegra_system_reset_level reset_level = tegra_pmc_get_system_reset_level();
  
  	seq_printf(s, "### PMC reset source: %s\n", pmc_reset_reason_string[reset_reason]);
  	seq_printf(s, "### PMC reset level: %s\n", pmc_reset_level_string[reset_level]);
  
  	return 0;
  }
  
  static int pmc_reset_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, pmc_reset_show, inode->i_private);
  }
  
  static const struct file_operations pmc_reset_fops = {
  	.open = pmc_reset_open,
  	.read = seq_read,
  	.llseek = seq_lseek,
  	.release = single_release,
  };
  
  static void tegra_pmc_reset_debugfs_init(struct device *dev)
  {
  	struct dentry *d;
  
  	d = debugfs_create_file("pmc-reset", S_IRUGO, NULL, NULL,
  				&pmc_reset_fops);
  	if (!d)
  		dev_err(dev, "Error in creating the debugFS for pmc-reset\n");
  }
  #else
  static void tegra_pmc_reset_debugfs_init(struct device *dev)
  {
  }
  #endif
  
  #if defined(CONFIG_SYSFS)
  static ssize_t tegra_reset_reason_show(struct kobject *kobj,
  		struct kobj_attribute *attr, char *buf)
  {
  	enum tegra_system_reset_reason reset_status =
  		tegra_pmc_get_system_reset_reason();
  	return sprintf(buf, "%s\n", pmc_reset_reason_string[reset_status]);
  }
  
  static ssize_t tegra_reset_level_show(struct kobject *kobj,
  		struct kobj_attribute *attr, char *buf)
  {
  	enum tegra_system_reset_level reset_level =
  		tegra_pmc_get_system_reset_level();
  	return sprintf(buf, "%s\n", pmc_reset_level_string[reset_level]);
  }
  
  static struct kobj_attribute tegra_rst_reason =
  		__ATTR_RO(tegra_reset_reason);
  
  static struct kobj_attribute tegra_rst_level =
  		__ATTR_RO(tegra_reset_level);
  
  static struct attribute *tegra_attributes[] = {
  	&tegra_rst_reason.attr,
  	&tegra_rst_level.attr,
  	NULL,
  };
  
  static const struct attribute_group pmc_attribute_group = {
  	.attrs = tegra_attributes,
  };
  
  static void tegra_pmc_reset_sysfs_init(struct device *dev)
  {
  	int error;
  	static struct kobject *pmc_kobject;
  
  	pmc_kobject = kobject_create_and_add("pmc", kernel_kobj);
  
  	if (!pmc_kobject) {
  		dev_err(dev, "Failed to create sysfs dir - /sys/kernel/pmc\n");
  		return;
  	}
  
  	error = sysfs_create_group(pmc_kobject, &pmc_attribute_group);
  
  	if (error)
  		dev_err(dev, "Failed to create sysfs entries for pmc\n");
  }
  #else
  static void tegra_pmc_reset_sysfs_init(struct device *dev)
  {
  }
  #endif
  
  static int tegra_pmc_probe(struct platform_device *pdev)
  {
  	void __iomem *base;
  	void __iomem *io_map_base[5] = {NULL};
  	int mem_count = 0;
  	struct resource *res;
  	int err;
  
  	err = tegra_pmc_parse_dt(pmc, pdev->dev.of_node);
  	if (err < 0)
  		return err;
  
  	/* take over the memory region from the early initialization */
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	pmc->base = devm_ioremap_resource(&pdev->dev, res);
  	if (IS_ERR(pmc->base))
  		return PTR_ERR(pmc->base);
  
  	io_map_base[mem_count++] = pmc->base;
  
  	/* unmap the base address from the early init, then mark it to NULL */
  	iounmap(pmc->early_base);
  	pmc->early_base = NULL;
  
  	if (pmc->soc->has_reboot_base_address) {
  		base =  pmc->reboot_base;
  		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
  		pmc->reboot_base = devm_ioremap_resource(&pdev->dev, res);
  		if (IS_ERR(pmc->reboot_base))
  			return PTR_ERR(pmc->reboot_base);
  
  		io_map_base[mem_count++] = pmc->reboot_base;
  		iounmap(base);
  	} else {
  		pmc->reboot_base = pmc->base;
  	}
  
  	if (pmc->soc->has_misc_base_address) {
  		base =  pmc->misc_base;
  		res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
  		pmc->misc_base = devm_ioremap_resource(&pdev->dev, res);
  		if (IS_ERR(pmc->misc_base))
  			return PTR_ERR(pmc->misc_base);
  
  		io_map_base[mem_count++] = pmc->misc_base;
  		iounmap(base);
  	} else {
  		pmc->misc_base = pmc->base;
  	}
  
  	while (mem_count < 5) {
  		res = platform_get_resource(pdev, IORESOURCE_MEM, mem_count);
  		if (!res)
  			break;
  
  		base = devm_ioremap_resource(&pdev->dev, res);
  		if (IS_ERR(base))
  			return PTR_ERR(base);
  
  		io_map_base[mem_count++] = base;
  	}
  
  	if (pmc->soc->has_pclk_clock) {
  		pmc->clk = devm_clk_get(&pdev->dev, "pclk");
  		if (IS_ERR(pmc->clk)) {
  			err = PTR_ERR(pmc->clk);
  			dev_err(&pdev->dev, "failed to get pclk: %d\n", err);
  			return err;
  		}
  	}
  
  	pmc->dev = &pdev->dev;
  
  	tegra_pmc_init(pmc);
  
  	tegra_pmc_init_tsense_reset(pmc);
  
  	tegra_pmc_halt_in_fiq_init(pmc);
  
  	tegra_pmc_debug_scratch_reg_init(pmc);
  
  	tegra_pmc_show_reset_status();
  	tegra_pmc_reset_debugfs_init(&pdev->dev);
  	tegra_pmc_reset_sysfs_init(&pdev->dev);
  
  	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
  		err = tegra_powergate_debugfs_init();
  		if (err < 0)
  			return err;
  	}
  
  	pmc->tprod = devm_tegra_prod_get(&pdev->dev);
  	if (IS_ERR_OR_NULL(pmc->tprod)) {
  		pmc->tprod = NULL;
  	}
  
  	if (pmc->tprod) {
  		err = tegra_prod_set_by_name(io_map_base, "prod", pmc->tprod);
  		if (!err)
  			pr_info("PMC Prod config success\n");
  		else
  			pr_info("Failed to configure PMC prod: %d\n", err);
  	}
  
  	if (!pmc->soc->skip_restart_register) {
  		err = register_restart_handler(&tegra_pmc_restart_handler);
  		if (err) {
  			dev_err(&pdev->dev, "unable to register restart handler, %d\n",
  				err);
  			return err;
  		}
  	}
  
  	err = tegra_pmc_io_pads_pinctrl_init(pmc);
  	if (err < 0)
  		return err;
  
  	/* Register as pad controller */
  	err = tegra_io_pads_padctrl_init(&pdev->dev);
  	if (err)
  		pr_err("ERROR: Pad control driver init failed: %d\n", err);
  
  	if (pmc->soc->has_bootrom_command)
  		tegra_pmc_init_boorom_cmds(&pdev->dev);
  
  	/* handle PMC reboot reason with PSCI */
  	if (!pmc->soc->skip_arm_pm_restart && arm_pm_restart)
  		psci_handle_reboot_cmd = tegra_pmc_program_reboot_reason;
  
  	/* For writing to kernel panic flag for cboot on t21x chips */
  	if (tegra_hidrev_get_chipid(tegra_read_chipid()) == TEGRA210) {
  		err = atomic_notifier_chain_register(&panic_notifier_list,
  			&tegra_pmc_panic_notifier);
  		if (err != 0) {
  			pr_err("ERROR: Failed to register PMC panic notifier: %d", err);
  		}
  	}
  
  	return 0;
  }
  
  #ifdef CONFIG_TEGRA_POWERGATE
  #define TEGRA_POWERGATE_CPU     0
  #define TEGRA_POWERGATE_3D      1
  #define TEGRA_POWERGATE_VENC    2
  #define TEGRA_POWERGATE_PCIE    3
  #define TEGRA_POWERGATE_VDEC    4
  #define TEGRA_POWERGATE_L2      5
  #define TEGRA_POWERGATE_MPE     6
  #define TEGRA_POWERGATE_HEG     7
  #define TEGRA_POWERGATE_SATA    8
  #define TEGRA_POWERGATE_CPU1    9
  #define TEGRA_POWERGATE_CPU2    10
  #define TEGRA_POWERGATE_CPU3    11
  #define TEGRA_POWERGATE_CELP    12
  #define TEGRA_POWERGATE_3D1     13
  #define TEGRA_POWERGATE_CPU0    14
  #define TEGRA_POWERGATE_C0NC    15
  #define TEGRA_POWERGATE_C1NC    16
  #define TEGRA_POWERGATE_SOR     17
  #define TEGRA_POWERGATE_DIS     18
  #define TEGRA_POWERGATE_DISB    19
  #define TEGRA_POWERGATE_XUSBA   20
  #define TEGRA_POWERGATE_XUSBB   21
  #define TEGRA_POWERGATE_XUSBC   22
  #define TEGRA_POWERGATE_VIC     23
  #define TEGRA_POWERGATE_IRAM    24
  #define TEGRA_POWERGATE_NVDEC   25
  #define TEGRA_POWERGATE_NVJPG   26
  #define TEGRA_POWERGATE_AUD     27
  #define TEGRA_POWERGATE_DFD     28
  #define TEGRA_POWERGATE_VE2     29
  #endif
  
  static const char * const tegra20_powergates[] = {
  	[TEGRA_POWERGATE_CPU] = "cpu",
  	[TEGRA_POWERGATE_3D] = "3d",
  	[TEGRA_POWERGATE_VENC] = "venc",
  	[TEGRA_POWERGATE_VDEC] = "vdec",
  	[TEGRA_POWERGATE_PCIE] = "pcie",
  	[TEGRA_POWERGATE_L2] = "l2",
  	[TEGRA_POWERGATE_MPE] = "mpe",
  };
  
  static const struct tegra_pmc_soc tegra20_pmc_soc = {
  	.num_powergates = ARRAY_SIZE(tegra20_powergates),
  	.powergates = tegra20_powergates,
  	.num_cpu_powergates = 0,
  	.cpu_powergates = NULL,
  	.has_tsense_reset = false,
  	.has_gpu_clamps = false,
  	.has_bootrom_command = false,
  };
  
  static const char * const tegra30_powergates[] = {
  	[TEGRA_POWERGATE_CPU] = "cpu0",
  	[TEGRA_POWERGATE_3D] = "3d0",
  	[TEGRA_POWERGATE_VENC] = "venc",
  	[TEGRA_POWERGATE_VDEC] = "vdec",
  	[TEGRA_POWERGATE_PCIE] = "pcie",
  	[TEGRA_POWERGATE_L2] = "l2",
  	[TEGRA_POWERGATE_MPE] = "mpe",
  	[TEGRA_POWERGATE_HEG] = "heg",
  	[TEGRA_POWERGATE_SATA] = "sata",
  	[TEGRA_POWERGATE_CPU1] = "cpu1",
  	[TEGRA_POWERGATE_CPU2] = "cpu2",
  	[TEGRA_POWERGATE_CPU3] = "cpu3",
  	[TEGRA_POWERGATE_CELP] = "celp",
  	[TEGRA_POWERGATE_3D1] = "3d1",
  };
  
  static const u8 tegra30_cpu_powergates[] = {
  	TEGRA_POWERGATE_CPU,
  	TEGRA_POWERGATE_CPU1,
  	TEGRA_POWERGATE_CPU2,
  	TEGRA_POWERGATE_CPU3,
  };
  
  static const struct tegra_pmc_soc tegra30_pmc_soc = {
  	.num_powergates = ARRAY_SIZE(tegra30_powergates),
  	.powergates = tegra30_powergates,
  	.num_cpu_powergates = ARRAY_SIZE(tegra30_cpu_powergates),
  	.cpu_powergates = tegra30_cpu_powergates,
  	.has_tsense_reset = true,
  	.has_gpu_clamps = false,
  	.has_bootrom_command = false,
  };
  
  static const char * const tegra114_powergates[] = {
  	[TEGRA_POWERGATE_CPU] = "crail",
  	[TEGRA_POWERGATE_3D] = "3d",
  	[TEGRA_POWERGATE_VENC] = "venc",
  	[TEGRA_POWERGATE_VDEC] = "vdec",
  	[TEGRA_POWERGATE_MPE] = "mpe",
  	[TEGRA_POWERGATE_HEG] = "heg",
  	[TEGRA_POWERGATE_CPU1] = "cpu1",
  	[TEGRA_POWERGATE_CPU2] = "cpu2",
  	[TEGRA_POWERGATE_CPU3] = "cpu3",
  	[TEGRA_POWERGATE_CELP] = "celp",
  	[TEGRA_POWERGATE_CPU0] = "cpu0",
  	[TEGRA_POWERGATE_C0NC] = "c0nc",
  	[TEGRA_POWERGATE_C1NC] = "c1nc",
  	[TEGRA_POWERGATE_DIS] = "dis",
  	[TEGRA_POWERGATE_DISB] = "disb",
  	[TEGRA_POWERGATE_XUSBA] = "xusba",
  	[TEGRA_POWERGATE_XUSBB] = "xusbb",
  	[TEGRA_POWERGATE_XUSBC] = "xusbc",
  };
  
  static const u8 tegra114_cpu_powergates[] = {
  	TEGRA_POWERGATE_CPU0,
  	TEGRA_POWERGATE_CPU1,
  	TEGRA_POWERGATE_CPU2,
  	TEGRA_POWERGATE_CPU3,
  };
  
  static const struct tegra_pmc_soc tegra114_pmc_soc = {
  	.num_powergates = ARRAY_SIZE(tegra114_powergates),
  	.powergates = tegra114_powergates,
  	.num_cpu_powergates = ARRAY_SIZE(tegra114_cpu_powergates),
  	.cpu_powergates = tegra114_cpu_powergates,
  	.has_tsense_reset = true,
  	.has_gpu_clamps = false,
  	.has_bootrom_command = false,
  };
  
  static const char * const tegra124_powergates[] = {
  	[TEGRA_POWERGATE_CPU] = "crail",
  	[TEGRA_POWERGATE_3D] = "3d",
  	[TEGRA_POWERGATE_VENC] = "venc",
  	[TEGRA_POWERGATE_PCIE] = "pcie",
  	[TEGRA_POWERGATE_VDEC] = "vdec",
  	[TEGRA_POWERGATE_L2] = "l2",
  	[TEGRA_POWERGATE_MPE] = "mpe",
  	[TEGRA_POWERGATE_HEG] = "heg",
  	[TEGRA_POWERGATE_SATA] = "sata",
  	[TEGRA_POWERGATE_CPU1] = "cpu1",
  	[TEGRA_POWERGATE_CPU2] = "cpu2",
  	[TEGRA_POWERGATE_CPU3] = "cpu3",
  	[TEGRA_POWERGATE_CELP] = "celp",
  	[TEGRA_POWERGATE_CPU0] = "cpu0",
  	[TEGRA_POWERGATE_C0NC] = "c0nc",
  	[TEGRA_POWERGATE_C1NC] = "c1nc",
  	[TEGRA_POWERGATE_SOR] = "sor",
  	[TEGRA_POWERGATE_DIS] = "dis",
  	[TEGRA_POWERGATE_DISB] = "disb",
  	[TEGRA_POWERGATE_XUSBA] = "xusba",
  	[TEGRA_POWERGATE_XUSBB] = "xusbb",
  	[TEGRA_POWERGATE_XUSBC] = "xusbc",
  	[TEGRA_POWERGATE_VIC] = "vic",
  	[TEGRA_POWERGATE_IRAM] = "iram",
  };
  
  static const u8 tegra124_cpu_powergates[] = {
  	TEGRA_POWERGATE_CPU0,
  	TEGRA_POWERGATE_CPU1,
  	TEGRA_POWERGATE_CPU2,
  	TEGRA_POWERGATE_CPU3,
  };
  
  static const struct tegra_pmc_soc tegra124_pmc_soc = {
  	.num_powergates = ARRAY_SIZE(tegra124_powergates),
  	.powergates = tegra124_powergates,
  	.num_cpu_powergates = ARRAY_SIZE(tegra124_cpu_powergates),
  	.cpu_powergates = tegra124_cpu_powergates,
  	.has_tsense_reset = true,
  	.has_gpu_clamps = true,
  	.has_bootrom_command = false,
  };
  
  static const unsigned long tegra210_register_map[TEGRA_PMC_MAX_REG] = {
  	[TEGRA_PMC_CNTRL]		=  0x00,
  	[TEGRA_PMC_WAKE_MASK]		=  0x0c,
  	[TEGRA_PMC_WAKE_LEVEL]		=  0x10,
  	[TEGRA_PMC_WAKE_STATUS]		=  0x14,
  	[TEGRA_PMC_SW_WAKE_STATUS]	=  0x18,
  	[TEGRA_PMC_DPD_PADS_ORIDE]	=  0x1c,
  	[TEGRA_PMC_BLINK_TIMER]		=  0x40,
  	[TEGRA_PMC_WAKE_DELAY]		=  0xe0,
  	[TEGRA_PMC_WAKE2_MASK]		=  0x160,
  	[TEGRA_PMC_WAKE2_LEVEL]		=  0x164,
  	[TEGRA_PMC_WAKE2_STATUS]	=  0x168,
  	[TEGRA_PMC_SW_WAKE2_STATUS]	=  0x16c,
  	[TEGRA_PMC_IO_DPD_SAMPLE]	=  0x20,
  	[TEGRA_PMC_IO_DPD_ENABLE]	=  0x24,
  	[TEGRA_PMC_IO_DPD_REQ]		=  0x1b8,
  	[TEGRA_PMC_IO_DPD_STATUS]	=  0x1bc,
  	[TEGRA_PMC_IO_DPD2_REQ]		=  0x1c0,
  	[TEGRA_PMC_IO_DPD2_STATUS]	=  0x1c4,
  	[TEGRA_PMC_SEL_DPD_TIM]		=  0x1c8,
  	[TEGRA_PMC_PWR_NO_IOPOWER]	=  0x44,
  	[TEGRA_PMC_PWR_DET_ENABLE]	=  0x48,
  	[TEGRA_PMC_PWR_DET_VAL]		=  0xe4,
  	[TEGRA_PMC_REMOVE_CLAMPING]	=  0x34,
  	[TEGRA_PMC_PWRGATE_TOGGLE]	=  0x30,
  	[TEGRA_PMC_PWRGATE_STATUS]	=  0x38,
  	[TEGRA_PMC_COREPWRGOOD_TIMER]	=  0x3c,
  	[TEGRA_PMC_COREPWROFF_TIMER]	=  0xe0,
  	[TEGRA_PMC_CPUPWRGOOD_TIMER]	=  0xc8,
  	[TEGRA_PMC_CPUPWROFF_TIMER]	=  0xcc,
  	[TEGRA_PMC_SENSOR_CTRL]		=  0x1b0,
  	[TEGRA_PMC_RST_STATUS]		=  0x1b4,
  	[TEGRA_PMC_GPU_RG_CNTRL]	=  0x2d4,
  	[TEGRA_PMC_FUSE_CTRL]		=  0x450,
  	[TEGRA_PMC_BR_COMMAND_BASE]	=  0x908,
  	[TEGRA_PMC_SCRATCH0]		=  0x50,
  	[TEGRA_PMC_SCRATCH1]		=  0x54,
  	[TEGRA_PMC_SCRATCH37]		=  0x130,
  	[TEGRA_PMC_SCRATCH41]		=  0x140,
  	[TEGRA_PMC_SCRATCH43]		=  0x22c,
  	[TEGRA_PMC_SCRATCH54]		=  0x258,
  	[TEGRA_PMC_SCRATCH55]		=  0x25c,
  	[TEGRA_PMC_LED_BREATHING_CTRL]	= 0xb48,
  	[TEGRA_PMC_LED_BREATHING_COUNTER0]	= 0xb4c,
  	[TEGRA_PMC_LED_BREATHING_COUNTER1]	= 0xb50,
  	[TEGRA_PMC_LED_BREATHING_COUNTER2]	= 0xb54,
  	[TEGRA_PMC_LED_BREATHING_COUNTER3]	= 0xb58,
  	[TEGRA_PMC_LED_BREATHING_STATUS]	= 0xb5c,
  };
  
  static const char * const tegra210_powergates[] = {
  	[TEGRA_POWERGATE_CPU] = "crail",
  	[TEGRA_POWERGATE_3D] = "3d",
  	[TEGRA_POWERGATE_VENC] = "venc",
  	[TEGRA_POWERGATE_PCIE] = "pcie",
  	[TEGRA_POWERGATE_L2] = "l2",
  	[TEGRA_POWERGATE_MPE] = "mpe",
  	[TEGRA_POWERGATE_HEG] = "heg",
  	[TEGRA_POWERGATE_SATA] = "sata",
  	[TEGRA_POWERGATE_CPU1] = "cpu1",
  	[TEGRA_POWERGATE_CPU2] = "cpu2",
  	[TEGRA_POWERGATE_CPU3] = "cpu3",
  	[TEGRA_POWERGATE_CELP] = "celp",
  	[TEGRA_POWERGATE_CPU0] = "cpu0",
  	[TEGRA_POWERGATE_C0NC] = "c0nc",
  	[TEGRA_POWERGATE_C1NC] = "c1nc",
  	[TEGRA_POWERGATE_SOR] = "sor",
  	[TEGRA_POWERGATE_DIS] = "dis",
  	[TEGRA_POWERGATE_DISB] = "disb",
  	[TEGRA_POWERGATE_XUSBA] = "xusba",
  	[TEGRA_POWERGATE_XUSBB] = "xusbb",
  	[TEGRA_POWERGATE_XUSBC] = "xusbc",
  	[TEGRA_POWERGATE_VIC] = "vic",
  	[TEGRA_POWERGATE_IRAM] = "iram",
  	[TEGRA_POWERGATE_NVDEC] = "nvdec",
  	[TEGRA_POWERGATE_NVJPG] = "nvjpg",
  	[TEGRA_POWERGATE_AUD] = "aud",
  	[TEGRA_POWERGATE_DFD] = "dfd",
  	[TEGRA_POWERGATE_VE2] = "ve2",
  };
  
  static const u8 tegra210_cpu_powergates[] = {
  	TEGRA_POWERGATE_CPU0,
  	TEGRA_POWERGATE_CPU1,
  	TEGRA_POWERGATE_CPU2,
  	TEGRA_POWERGATE_CPU3,
  };
  
  #define TEGRA210X_IO_PAD_CONFIG(_pin, _npins, _name, _dpd,	\
  			       _vbit, _iopower, _reg, _bds)	\
  	{							\
  		.name =  #_name,				\
  		.pins = {(_pin)},				\
  		.npins = _npins,				\
  		.dpd = _dpd,					\
  		.voltage = _vbit,				\
  		.io_power = _iopower,				\
  		.dpd_req_reg = TEGRA_PMC_IO_##_reg##_REQ,	\
  		.dpd_status_reg = TEGRA_PMC_IO_##_reg##_STATUS,	\
  		.dpd_timer_reg = TEGRA_PMC_SEL_DPD_TIM,		\
  		.dpd_sample_reg = TEGRA_PMC_IO_DPD_SAMPLE,	\
  		.bdsdmem_cfc = _bds,				\
  		.io_pad_pwr_det_enable_reg = TEGRA_PMC_PWR_DET_ENABLE, \
  		.io_pad_pwr_det_val_reg = TEGRA_PMC_PWR_DET_VAL, \
  		.pad_uv_0 = TEGRA_IO_PAD_VOLTAGE_1800000UV,	\
  		.pad_uv_1 = TEGRA_IO_PAD_VOLTAGE_3300000UV,	\
  	},
  
  #define TEGRA210_IO_PAD_CONFIG(_pin, _npins, _name, _dpd,	\
  			       _vbit, _iopower, _reg)		\
  	TEGRA210X_IO_PAD_CONFIG(_pin, _npins, _name, _dpd,	\
  				_vbit, _iopower, _reg, false)
  
  /**
   * All IO pads of Tegra SoCs do not support the low power and multi level
   * voltage configurations for its pads.
   * Defining macros for different cases as follows:
   * TEGRA_IO_PAD_LPONLY : IO pad which support low power state but
   *			 operate in single level of IO voltage.
   * TEGRA_IO_PAD_LP_N_PV: IO pad which support low power state as well as
   *			 it can operate in multi-level voltages.
   * TEGRA_IO_PAD_PVONLY:  IO pad which does not support low power state but
   *			 it can operate in multi-level voltages.
   */
  #define TEGRA210_IO_PAD_LPONLY(_pin, _name, _dpd, _reg)	\
  	TEGRA210_IO_PAD_CONFIG(_pin, 1, _name, _dpd, UINT_MAX, UINT_MAX, _reg)
  
  #define TEGRA210_IO_PAD_LP_N_PV(_pin, _name, _dpd, _vbit, _io , _reg)  \
  	TEGRA210_IO_PAD_CONFIG(_pin, 1, _name, _dpd, _vbit, _io, _reg)
  
  #define TEGRA210_IO_PAD_PVONLY(_pin, _name, _vbit, _io, _reg)	\
  	TEGRA210_IO_PAD_CONFIG(_pin, 0, _name, UINT_MAX, _vbit, _io, _reg)
  
  #define TEGRA210_IO_PAD_DESC_LP(_pin, _name, _dpd, _reg)	\
  	{					\
  		.number = _pin,			\
  		.name = #_name,			\
  	},
  #define TEGRA210_IO_PAD_DESC_LP_N_PV(_pin, _name, _dpd, _vbit, _io, _reg) \
  	TEGRA210_IO_PAD_DESC_LP(_pin, _name, _dpd, _reg)
  
  #define TEGRA210_IO_PAD_DESC_PV(_pin, _name, _vbit, _io, _reg) \
  	TEGRA210_IO_PAD_DESC_LP(_pin, _name, UINT_MAX, _reg)
  
  #define TEGRA210_IO_PAD_TABLE(_lponly_, _pvonly_, _lp_n_pv_)	\
  	_lp_n_pv_(0, audio, 17, 5, 5, DPD)		\
  	_lp_n_pv_(1, audio-hv, 29, 18, 18, DPD2)	\
  	_lp_n_pv_(2, cam, 4, 10, 10, DPD2)		\
  	_lponly_(3, csia, 0, DPD)			\
  	_lponly_(4, csib, 1, DPD)			\
  	_lponly_(5, csic, 10, DPD2)			\
  	_lponly_(6, csid, 11, DPD2)			\
  	_lponly_(7, csie, 12, DPD2)			\
  	_lponly_(8, csif, 13, DPD2)			\
  	_lp_n_pv_(9, dbg, 25, 19, 19, DPD)		\
  	_lponly_(10, debug-nonao, 26, DPD)		\
  	_lp_n_pv_(11, dmic, 18, 20, 20, DPD2)		\
  	_lponly_(12, dp, 19, DPD2)			\
  	_lponly_(13, dsi, 2, DPD)			\
  	_lponly_(14, dsib, 7, DPD2)			\
  	_lponly_(15, dsic, 8, DPD2)			\
  	_lponly_(16, dsid, 9, DPD2)			\
  	_lponly_(17, emmc, 3, DPD2)			\
  	_lponly_(18, emmc2, 5, DPD2)			\
  	_lp_n_pv_(19, gpio, 27, 21, 21, DPD)		\
  	_lponly_(20, hdmi, 28, DPD)			\
  	_lponly_(21, hsic, 19, DPD)			\
  	_lponly_(22, lvds, 25, DPD2)			\
  	_lponly_(23, mipi-bias, 3, DPD)			\
  	_lponly_(24, pex-bias, 4, DPD)			\
  	_lponly_(25, pex-clk1, 5, DPD)			\
  	_lponly_(26, pex-clk2, 6, DPD)			\
  	_pvonly_(27, pex-ctrl, 11, 11, DPD2)		\
  	_lp_n_pv_(28, sdmmc1, 1, 12, 12, DPD2)		\
  	_lp_n_pv_(29, sdmmc3, 2, 13, 13, DPD2)		\
  	_lp_n_pv_(30, spi, 14, 22, 22, DPD2)		\
  	_lp_n_pv_(31, spi-hv, 15, 23, 23, DPD2)		\
  	_lp_n_pv_(32, uart, 14, 2, 2, DPD)		\
  	_lponly_(33, usb0, 9, DPD)			\
  	_lponly_(34, usb1, 10, DPD)			\
  	_lponly_(35, usb2, 11, DPD)			\
  	_lponly_(36, usb3, 18, DPD)			\
  	_lponly_(37, usb-bias, 12, DPD)			\
  	_pvonly_(38, sys, 12, UINT_MAX, DPD)
  
  static const struct tegra_pmc_io_pad_soc tegra210_io_pads[] = {
  	TEGRA210_IO_PAD_TABLE(TEGRA210_IO_PAD_LPONLY, TEGRA210_IO_PAD_PVONLY,
  			      TEGRA210_IO_PAD_LP_N_PV)
  };
  
  static const struct pinctrl_pin_desc tegra210_io_pads_pinctrl_desc[] = {
  	TEGRA210_IO_PAD_TABLE(TEGRA210_IO_PAD_DESC_LP, TEGRA210_IO_PAD_DESC_PV,
  			      TEGRA210_IO_PAD_DESC_LP_N_PV)
  };
  
  static const struct tegra_pmc_soc tegra210_pmc_soc = {
  	.num_powergates = ARRAY_SIZE(tegra210_powergates),
  	.powergates = tegra210_powergates,
  	.num_cpu_powergates = ARRAY_SIZE(tegra210_cpu_powergates),
  	.cpu_powergates = tegra210_cpu_powergates,
  	.has_tsense_reset = true,
  	.has_gpu_clamps = true,
  	.has_ps18 = true,
  	.has_bootrom_command = true,
  	.has_pclk_clock = true,
  	.has_interrupt_polarity_support = true,
  	.show_legacy_reset_status = true,
  	.has_reboot_base_address = false,
  	.skip_lp0_vector_setup = false,
  	.skip_legacy_pmc_init = false,
  	.skip_power_gate_debug_fs_init = false,
  	.skip_restart_register = false,
  	.skip_arm_pm_restart = false,
  	.num_io_pads = ARRAY_SIZE(tegra210_io_pads),
  	.io_pads = tegra210_io_pads,
  	.num_descs = ARRAY_SIZE(tegra210_io_pads_pinctrl_desc),
  	.descs = tegra210_io_pads_pinctrl_desc,
  	.rmap = tegra210_register_map,
  	.has_misc_base_address = false,
  	.sata_power_gate_in_misc = false,
  };
  
  #define TEGRA210B01_IO_PAD_LP_N_PV(_pin, _name, _dpd, _vbit, _io, _reg, _bds) \
  	TEGRA210X_IO_PAD_CONFIG(_pin, 1, _name, _dpd, _vbit, _io, _reg, _bds)
  
  #define TEGRA210B01_IO_PAD_DESC_LP_N_PV(_pin, _name, _dpd, _vbit, _io, _reg, \
  					_bds) \
  	TEGRA210_IO_PAD_DESC_LP(_pin, _name, _dpd, _reg)
  
  #define TEGRA210B01_IO_PAD_TABLE(_lponly_, _pvonly_, _lp_n_pv_)	\
  	_lp_n_pv_(0, audio, 17, 5, 5, DPD, false)	\
  	_lp_n_pv_(1, audio-hv, 29, 18, 18, DPD2, true)	\
  	_lp_n_pv_(2, cam, 4, 10, 10, DPD2, false)	\
  	_lponly_(3, csia, 0, DPD)			\
  	_lponly_(4, csib, 1, DPD)			\
  	_lponly_(5, csic, 10, DPD2)			\
  	_lponly_(6, csid, 11, DPD2)			\
  	_lponly_(7, csie, 12, DPD2)			\
  	_lponly_(8, csif, 13, DPD2)			\
  	_lp_n_pv_(9, dbg, 25, 19, 19, DPD, false)	\
  	_lponly_(10, debug-nonao, 26, DPD)		\
  	_lp_n_pv_(11, dmic, 18, 20, 20, DPD2, false)	\
  	_lponly_(12, dp, 19, DPD2)			\
  	_lponly_(13, dsi, 2, DPD)			\
  	_lponly_(14, dsib, 7, DPD2)			\
  	_lponly_(15, dsic, 8, DPD2)			\
  	_lponly_(16, dsid, 9, DPD2)			\
  	_lponly_(17, emmc, 3, DPD2)			\
  	_lponly_(18, emmc2, 5, DPD2)			\
  	_lp_n_pv_(19, gpio, 27, 21, 21, DPD, true)	\
  	_lponly_(20, hdmi, 28, DPD)			\
  	_lponly_(21, hsic, 19, DPD)			\
  	_lponly_(22, lvds, 25, DPD2)			\
  	_lponly_(23, mipi-bias, 3, DPD)			\
  	_lponly_(24, pex-bias, 4, DPD)			\
  	_lponly_(25, pex-clk1, 5, DPD)			\
  	_lponly_(26, pex-clk2, 6, DPD)			\
  	_pvonly_(27, pex-ctrl, 11, 11, DPD2)		\
  	_lp_n_pv_(28, sdmmc1, 1, 12, 12, DPD2, true)	\
  	_lp_n_pv_(29, sdmmc3, 2, 13, 13, DPD2, true)	\
  	_lp_n_pv_(30, spi, 14, 22, 22, DPD2, false)	\
  	_lp_n_pv_(31, spi-hv, 15, 23, 23, DPD2, false)	\
  	_lp_n_pv_(32, uart, 14, 2, 2, DPD, false)	\
  	_lponly_(33, usb0, 9, DPD)			\
  	_lponly_(34, usb1, 10, DPD)			\
  	_lponly_(35, usb2, 11, DPD)			\
  	_lponly_(36, usb3, 18, DPD)			\
  	_lponly_(37, usb-bias, 12, DPD)			\
  	_pvonly_(38, sys, 12, UINT_MAX, DPD)
  
  static const struct tegra_pmc_io_pad_soc tegra210b01_io_pads[] = {
  	TEGRA210B01_IO_PAD_TABLE(TEGRA210_IO_PAD_LPONLY, TEGRA210_IO_PAD_PVONLY,
  			      TEGRA210B01_IO_PAD_LP_N_PV)
  };
  
  static const struct pinctrl_pin_desc tegra210b01_io_pads_pinctrl_desc[] = {
  	TEGRA210B01_IO_PAD_TABLE(TEGRA210_IO_PAD_DESC_LP,
  		TEGRA210_IO_PAD_DESC_PV, TEGRA210B01_IO_PAD_DESC_LP_N_PV)
  };
  
  static const struct tegra_pmc_soc tegra210b01_pmc_soc = {
  	.num_powergates = ARRAY_SIZE(tegra210_powergates),
  	.powergates = tegra210_powergates,
  	.num_cpu_powergates = ARRAY_SIZE(tegra210_cpu_powergates),
  	.cpu_powergates = tegra210_cpu_powergates,
  	.has_tsense_reset = true,
  	.has_gpu_clamps = true,
  	.has_ps18 = true,
  	.has_bootrom_command = true,
  	.has_pclk_clock = true,
  	.has_interrupt_polarity_support = true,
  	.show_legacy_reset_status = true,
  	.has_reboot_base_address = false,
  	.skip_lp0_vector_setup = false,
  	.skip_legacy_pmc_init = false,
  	.skip_power_gate_debug_fs_init = false,
  	.skip_restart_register = false,
  	.skip_arm_pm_restart = false,
  	.num_io_pads = ARRAY_SIZE(tegra210b01_io_pads),
  	.io_pads = tegra210b01_io_pads,
  	.num_descs = ARRAY_SIZE(tegra210b01_io_pads_pinctrl_desc),
  	.descs = tegra210b01_io_pads_pinctrl_desc,
  	.rmap = tegra210_register_map,
  };
  
  /* Tegra 186 register map */
  static const unsigned long tegra186_register_map[TEGRA_PMC_MAX_REG] = {
  	[TEGRA_PMC_CNTRL]			= 0x0,
  	[TEGRA_PMC_DPD_PADS_ORIDE]		= 0x08,
  	[TEGRA_PMC_BLINK_TIMER]			= 0x30,
  	[TEGRA_PMC_FUSE_CTRL]			= 0x100,
  	[TEGRA_PMC_IMPL_RAMDUMP_CTL_STATUS]	= 0x10C,
  	[TEGRA_PMC_E_18V_PWR]			= 0x3C,
  	[TEGRA_PMC_E_33V_PWR]			= 0x40,
  	[TEGRA_PMC_PWR_NO_IOPOWER]		= 0x44,
  	[TEGRA_PMC_RST_STATUS]			= 0x70,
  	[TEGRA_PMC_SATA_PWRGT_0]		= 0x68,
  	[TEGRA_PMC_UFSHC_PWR_CNTRL_0]		= 0xF4,
  	[TEGRA_PMC_IO_DPD_REQ]			= 0x74,
  	[TEGRA_PMC_IO_DPD_STATUS]		= 0x78,
  	[TEGRA_PMC_IO_DPD2_REQ]			= 0x7C,
  	[TEGRA_PMC_IO_DPD2_STATUS]		= 0x80,
  	[TEGRA_PMC_SCRATCH0]			= 0x2000,
  };
  
  #define TEGRA186_IO_PAD_CONFIG(_pin, _npins, _name, _dpd_reg, _dpd_bit,     \
  			       _padv_reg, _padv_bit, _v0, _v1, _iopwr_bit,  \
  			       _bds)  \
  	{							\
  		.name =  #_name,				\
  		.pins = {(_pin)},				\
  		.npins = _npins,				\
  		.dpd_req_reg = TEGRA_PMC_IO_##_dpd_reg##_REQ,	\
  		.dpd_status_reg = TEGRA_PMC_IO_##_dpd_reg##_STATUS,	\
  		.dpd_timer_reg = TEGRA_PMC_SEL_DPD_TIM,		\
  		.dpd_sample_reg = TEGRA_PMC_IO_DPD_SAMPLE,	\
  		.dpd = _dpd_bit,				\
  		.io_pad_pwr_det_val_reg = TEGRA_PMC_##_padv_reg##_PWR, \
  		.io_pad_pwr_det_enable_reg = UINT_MAX,		\
  		.pad_uv_0 = TEGRA_IO_PAD_VOLTAGE_##_v0##000UV,	\
  		.pad_uv_1 = TEGRA_IO_PAD_VOLTAGE_##_v1##000UV,	\
  		.voltage = _padv_bit,				\
  		.io_power = _iopwr_bit,				\
  		.bdsdmem_cfc = _bds,				\
  	},
  
  #define TEGRA186_IO_PAD_LPONLY(_pin, _name, _dpd_reg, _dpd_bit, _iopwr_bit, \
  			       _bds)					    \
  	TEGRA186_IO_PAD_CONFIG(_pin, 1, _name, _dpd_reg, _dpd_bit,	    \
  			       E_33V, UINT_MAX, 1200, 1200, _iopwr_bit, _bds)
  
  #define TEGRA186_IO_PAD_LP_N_PV(_pin, _name, _dpd_reg, _dpd_bit, _padv_reg, \
  				_padv_bit, _v0, _v1, _iopwr_bit, _bds)	    \
  	TEGRA186_IO_PAD_CONFIG(_pin, 1, _name, _dpd_reg, _dpd_bit,	    \
  			       _padv_reg, _padv_bit, _v0, _v1, _iopwr_bit,  \
  			       _bds)
  
  #define TEGRA186_IO_PAD_PVONLY(_pin, _name, _padv_reg, _padv_bit, _v0, _v1, \
  			       _iopwr_bit, _bds)			    \
  	TEGRA186_IO_PAD_CONFIG(_pin, 1, _name, DPD, UINT_MAX, _padv_reg,    \
  			       _padv_bit, _v0, _v1, _iopwr_bit, _bds)
  
  #define TEGRA186_IO_PAD_DESC_LP(_pin, _name, _dpd_reg, _dpd_bit, _iopwr_bit, \
  				_bds)					     \
  	{								\
  		.number = _pin,						\
  		.name = #_name,						\
  	},
  
  #define TEGRA186_IO_PAD_DESC_LP_N_PV(_pin, _name, _dpd_reg, _dpd_bit,	\
  				     _padv_reg, _padv_bit, _v0, _v1,	\
  				     _iopwr_bit, _bds)			\
  	TEGRA186_IO_PAD_DESC_LP(_pin, _name, _dpd_reg, _dpd_bit, _iopwr_bit, _bds)
  
  #define TEGRA186_IO_PAD_DESC_PV(_pin, _name, _padv_reg, _padv_bit, _v0, _v1, \
  			       _iopwr_bit, _bds)			    \
  	TEGRA186_IO_PAD_DESC_LP(_pin, _name, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX)
  
  #define TEGRA186_IO_PAD_TABLE(_lponly_, _pvonly_, _lp_n_pv_)		\
          _lponly_(0, csia, DPD, 0, UINT_MAX, false)			\
          _lponly_(1, csib, DPD, 1, UINT_MAX, false)			\
          _lponly_(2, dsi, DPD, 2, UINT_MAX, false)			\
          _lponly_(3, mipi-bias, DPD, 3, 9, false)			\
          _lponly_(4, pex-clk-bias, DPD, 4, UINT_MAX, false)		\
          _lponly_(5, pex-clk3, DPD, 5, UINT_MAX, false)			\
          _lponly_(6, pex-clk2, DPD, 6, UINT_MAX, false)			\
          _lponly_(7, pex-clk1, DPD, 7, UINT_MAX, false)			\
          _lponly_(8, usb0, DPD, 9, UINT_MAX, false)			\
          _lponly_(9, usb1, DPD, 10, UINT_MAX, false)			\
          _lponly_(10, usb2, DPD, 11, UINT_MAX, false)			\
          _lponly_(11, usb-bias, DPD, 12, UINT_MAX, false)		\
          _lponly_(12, uart, DPD, 14, 2, false)				\
          _lponly_(13, audio, DPD, 17, 5, false)				\
          _lponly_(14, hsic, DPD, 19, UINT_MAX, false)			\
          _lp_n_pv_(15, dbg, DPD, 25, E_18V, 4, 1200, 1800, 19, false)	\
          _lponly_(16, hdmi-dp0, DPD, 28, UINT_MAX, false)		\
          _lponly_(17, hdmi-dp1, DPD, 29, UINT_MAX, false)		\
          _lponly_(18, pex-ctrl, DPD2, 0, 11, false)			\
          _lp_n_pv_(19, sdmmc2-hv, DPD2, 2, E_33V, 5, 1800, 3300, 30, true) \
          _lponly_(20, sdmmc4, DPD2, 4, 14, false)			\
          _lponly_(21, cam, DPD2, 6, 10, false)				\
          _lponly_(22, dsib, DPD2, 8, UINT_MAX, false)			\
          _lponly_(23, dsic, DPD2, 9, UINT_MAX, false)			\
          _lponly_(24, dsid, DPD2, 10, UINT_MAX, false)			\
          _lponly_(25, csic, DPD2, 11, UINT_MAX, false)			\
          _lponly_(26, csid, DPD2, 12, UINT_MAX, false)			\
          _lponly_(27, csie, DPD2, 13, UINT_MAX, false)			\
          _lponly_(28, csif, DPD2, 14, UINT_MAX, false)			\
          _lp_n_pv_(29, spi, DPD2, 15, E_18V, 5, 1200, 1800, 22, false)	\
          _lp_n_pv_(30, ufs, DPD2, 17, E_18V, 0, 1200, 1800, 6, false)	\
          _lp_n_pv_(31, dmic-hv, DPD2, 20, E_33V, 2, 1800, 3300, 28, true)   \
          _lponly_(32, edp, DPD2, 21, 4, false)				\
          _lp_n_pv_(33, sdmmc1-hv, DPD2, 23, E_33V, 4, 1800, 3300, 15, true) \
          _lp_n_pv_(34, sdmmc3-hv, DPD2, 24, E_33V, 6, 1800, 3300, 31, true) \
          _lponly_(35, conn, DPD2, 28, 3, false)				\
          _lp_n_pv_(36, audio-hv, DPD2, 29, E_33V, 1, 1800, 3300, 18, true) \
  	_pvonly_(37, ao-hv, E_33V, 0, 1800, 3300, 27, true)
  
  static const struct tegra_pmc_io_pad_soc tegra186_io_pads[] = {
  	TEGRA186_IO_PAD_TABLE(TEGRA186_IO_PAD_LPONLY, TEGRA186_IO_PAD_PVONLY,
  			      TEGRA186_IO_PAD_LP_N_PV)
  };
  
  static const struct pinctrl_pin_desc tegra186_io_pads_pinctrl_desc[] = {
  	TEGRA186_IO_PAD_TABLE(TEGRA186_IO_PAD_DESC_LP, TEGRA186_IO_PAD_DESC_PV,
  			      TEGRA186_IO_PAD_DESC_LP_N_PV)
  };
  
  static const struct tegra_pmc_soc tegra186_pmc_soc = {
  	.has_tsense_reset = false,
  	.has_pclk_clock = false,
  	.has_interrupt_polarity_support = false,
  	.show_legacy_reset_status = false,
  	.has_reboot_base_address = true,
  	.skip_lp0_vector_setup = true,
  	.skip_legacy_pmc_init = true,
  	.skip_power_gate_debug_fs_init = true,
  	.skip_restart_register = true,
  	.skip_arm_pm_restart = true,
  	.num_io_pads = ARRAY_SIZE(tegra186_io_pads),
  	.io_pads = tegra186_io_pads,
  	.num_descs = ARRAY_SIZE(tegra186_io_pads_pinctrl_desc),
  	.descs = tegra186_io_pads_pinctrl_desc,
  	.rmap = tegra186_register_map,
  	.has_ps18 = true,
  	.has_misc_base_address = false,
  	.sata_power_gate_in_misc = false,
  };
  
  /* Tegra 194 register map */
  static const unsigned long tegra194_register_map[TEGRA_PMC_MAX_REG] = {
  	[TEGRA_PMC_FUSE_CTRL]			= 0x10,
  	[TEGRA_PMC_IMPL_RAMDUMP_CTL_STATUS]	= 0x10C,
  	[TEGRA_PMC_E_18V_PWR]			= 0x3C,
  	[TEGRA_PMC_E_33V_PWR]			= 0x40,
  	[TEGRA_PMC_RST_STATUS]			= 0x70,
  	[TEGRA_PMC_SATA_PWRGT_0]		= 0x8,
  	[TEGRA_PMC_UFSHC_PWR_CNTRL_0]		= 0xF4,
  	[TEGRA_PMC_IO_DPD_REQ]			= 0x74,
  	[TEGRA_PMC_IO_DPD_STATUS]		= 0x78,
  	[TEGRA_PMC_IO_DPD2_REQ]			= 0x7C,
  	[TEGRA_PMC_IO_DPD2_STATUS]		= 0x80,
  	[TEGRA_PMC_SCRATCH0]			= 0x2000,
  };
  
  #define TEGRA194_IO_PAD_TABLE(_lponly_, _pvonly_, _lp_n_pv_)		\
  	_lponly_(0, csia, DPD, 0, UINT_MAX, false)			\
  	_lponly_(1, csib, DPD, 1, UINT_MAX, false)			\
  	_lponly_(2, mipi-bias, DPD, 3, UINT_MAX, false)			\
  	_lponly_(3, pex-clk-bias, DPD, 4, UINT_MAX, false)		\
  	_lponly_(4, pex-clk3, DPD, 5, UINT_MAX, false)			\
  	_lponly_(5, pex-clk2, DPD, 6, UINT_MAX, false)			\
  	_lponly_(6, pex-clk1, DPD, 7, UINT_MAX, false)			\
  	_lponly_(7, uart, DPD, 14, UINT_MAX, false)			\
  	_lponly_(8, audio, DPD, 17, UINT_MAX, false)			\
  	_lp_n_pv_(9, dbg, DPD, 25, E_18V, 4, 1200, 1800, UINT_MAX, false)	\
  	_lponly_(10, hdmi-dp3, DPD, 26, UINT_MAX, false)		\
  	_lponly_(11, hdmi-dp2, DPD, 27, UINT_MAX, false)		\
  	_lponly_(12, hdmi-dp0, DPD, 28, UINT_MAX, false)		\
  	_lponly_(13, hdmi-dp1, DPD, 29, UINT_MAX, false)		\
  	_lponly_(14, pex-ctrl, DPD2, 0, UINT_MAX, false)		\
  	_lponly_(15, sdmmc4, DPD2, 4, UINT_MAX, false)			\
  	_lponly_(16, cam, DPD2, 6, UINT_MAX, false)			\
  	_lponly_(17, csic, DPD2, 11, UINT_MAX, false)			\
  	_lponly_(18, csid, DPD2, 12, UINT_MAX, false)			\
  	_lponly_(19, csie, DPD2, 13, UINT_MAX, false)			\
  	_lponly_(20, csif, DPD2, 14, UINT_MAX, false)			\
  	_lp_n_pv_(21, spi, DPD2, 15, E_18V, 5, 1200, 1800, UINT_MAX, false)	\
  	_lp_n_pv_(22, ufs, DPD2, 17, E_18V, 0, 1200, 1800, UINT_MAX, false)	\
  	_lponly_(23, edp, DPD2, 21, UINT_MAX, false)				\
  	_lp_n_pv_(24, sdmmc1-hv, DPD2, 23, E_33V, 4, 1800, 3300, UINT_MAX, true) \
  	_lp_n_pv_(25, sdmmc3-hv, DPD2, 24, E_33V, 6, 1800, 3300, UINT_MAX, true) \
  	_lponly_(26, conn, DPD2, 28, UINT_MAX, false)				\
  	_lp_n_pv_(27, audio-hv, DPD2, 29, E_33V, 1, 1800, 3300, UINT_MAX, true) \
  	_pvonly_(28, ao-hv, E_33V, 0, 1800, 3300, UINT_MAX, true)	\
  	_lponly_(29, csig, DPD2, 18, UINT_MAX, false)			\
  	_lponly_(30, csih, DPD2, 19, UINT_MAX, false)
  
  static const struct tegra_pmc_io_pad_soc tegra194_io_pads[] = {
  	TEGRA194_IO_PAD_TABLE(TEGRA186_IO_PAD_LPONLY, TEGRA186_IO_PAD_PVONLY,
  			      TEGRA186_IO_PAD_LP_N_PV)
  };
  
  static const struct pinctrl_pin_desc tegra194_io_pads_pinctrl_desc[] = {
  	TEGRA194_IO_PAD_TABLE(TEGRA186_IO_PAD_DESC_LP, TEGRA186_IO_PAD_DESC_PV,
  			      TEGRA186_IO_PAD_DESC_LP_N_PV)
  };
  
  static const struct tegra_pmc_soc tegra194_pmc_soc = {
  	.has_tsense_reset = false,
  	.has_pclk_clock = false,
  	.has_interrupt_polarity_support = false,
  	.show_legacy_reset_status = false,
  	.has_reboot_base_address = true,
  	.skip_lp0_vector_setup = true,
  	.skip_legacy_pmc_init = true,
  	.skip_power_gate_debug_fs_init = true,
  	.skip_restart_register = true,
  	.skip_arm_pm_restart = true,
  	.num_io_pads = ARRAY_SIZE(tegra194_io_pads),
  	.io_pads = tegra194_io_pads,
  	.num_descs = ARRAY_SIZE(tegra194_io_pads_pinctrl_desc),
  	.descs = tegra194_io_pads_pinctrl_desc,
  	.rmap = tegra194_register_map,
  	.has_ps18 = true,
  	.has_misc_base_address = true,
  	.sata_power_gate_in_misc = true,
  };
  
  static const struct of_device_id tegra_pmc_match[] = {
  	{ .compatible = "nvidia,tegra194-pmc", .data = &tegra194_pmc_soc },
  	{ .compatible = "nvidia,tegra186-pmc", .data = &tegra186_pmc_soc },
  	{ .compatible = "nvidia,tegra210-pmc", .data = &tegra210_pmc_soc },
  	{
  		.compatible = "nvidia,tegra210b01-pmc",
  		.data = &tegra210b01_pmc_soc
  	},
  	{ .compatible = "nvidia,tegra132-pmc", .data = &tegra124_pmc_soc },
  	{ .compatible = "nvidia,tegra124-pmc", .data = &tegra124_pmc_soc },
  	{ .compatible = "nvidia,tegra114-pmc", .data = &tegra114_pmc_soc },
  	{ .compatible = "nvidia,tegra30-pmc", .data = &tegra30_pmc_soc },
  	{ .compatible = "nvidia,tegra20-pmc", .data = &tegra20_pmc_soc },
  	{ }
  };
  
  static struct platform_driver tegra_pmc_driver = {
  	.driver = {
  		.name = "tegra-pmc",
  		.suppress_bind_attrs = true,
  		.of_match_table = tegra_pmc_match,
  	},
  	.probe = tegra_pmc_probe,
  };
  
  static int __init _tegra_pmc_driver_init(void)
  {
  	return platform_driver_register(&tegra_pmc_driver);
  }
  arch_initcall(_tegra_pmc_driver_init);
  
  /*
   * Looking for lp0_vec=size@start.
   */
  static int __init tegra_lp0_vec_arg(char *options)
  {
  
  	char *p = options;
  
  	pmc->lp0_vec_size = memparse(p, &p);
  
  	if (*p == '@')
  		pmc->lp0_vec_phys = memparse(p+1, NULL);
  
  	if (!pmc->lp0_vec_size || !pmc->lp0_vec_phys) {
  		pmc->lp0_vec_size = 0;
  		pmc->lp0_vec_phys = 0;
  	}
  
  	return 0;
  }
  early_param("lp0_vec", tegra_lp0_vec_arg);
  
  /*
   * Early initialization to allow access to registers in the very early boot
   * process.
   */
  static int __init tegra_pmc_early_init(void)
  {
  	const struct of_device_id *match;
  	struct device_node *np;
  	struct resource regs;
  	bool invert;
  	u32 value;
  
  	np = of_find_matching_node_and_match(NULL, tegra_pmc_match, &match);
  	if (!np) {
  		/*
  		 * Fall back to legacy initialization for 32-bit ARM only. All
  		 * 64-bit ARM device tree files for Tegra are required to have
  		 * a PMC node.
  		 *
  		 * This is for backwards-compatibility with old device trees
  		 * that didn't contain a PMC node. Note that in this case the
  		 * SoC data can't be matched and therefore powergating is
  		 * disabled.
  		 */
  		if (IS_ENABLED(CONFIG_ARM) && soc_is_tegra210_n_before()) {
  			pr_warn("DT node not found, powergating disabled\n");
  
  			regs.start = 0x7000e400;
  			regs.end = 0x7000e7ff;
  			regs.flags = IORESOURCE_MEM;
  
  			pr_warn("Using memory region %pR\n", &regs);
  		} else {
  			/*
  			 * At this point we're not running on Tegra, so play
  			 * nice with multi-platform kernels.
  			 */
  			return 0;
  		}
  	} else {
  		/*
  		 * Extract information from the device tree if we've found a
  		 * matching node.
  		 */
  		if (of_address_to_resource(np, 0, &regs) < 0) {
  			pr_err("failed to get PMC registers\n");
  			return -ENXIO;
  		}
  
  		pmc->soc = match->data;
  	}
  
  	pmc->base = ioremap_nocache(regs.start, resource_size(&regs));
  	if (!pmc->base) {
  		pr_err("failed to map PMC registers\n");
  		return -ENXIO;
  	}
  
  	if (pmc->soc && pmc->soc->has_reboot_base_address) {
  		pmc->reboot_base = of_iomap(np, 1);
  		if (!pmc->reboot_base) {
  			pr_err("Failed to map reboot PMC registers\n");
  			return -ENXIO;
  		}
  	} else {
  		pmc->reboot_base = pmc->base;
  	}
  
  	if (pmc->soc && pmc->soc->has_misc_base_address) {
  		pmc->misc_base = of_iomap(np, 2);
  		if (!pmc->misc_base) {
  			pr_err("Failed to map misc PMC registers\n");
  			return -ENXIO;
  		}
  	} else {
  		pmc->misc_base = pmc->base;
  	}
  
  	mutex_init(&pmc->powergates_lock);
  
  	/* backup the base address */
  	pmc->early_base = pmc->base;
  
  	/*
  	 * Invert the interrupt polarity if a PMC device tree node exists and
  	 * contains the nvidia,invert-interrupt property.
  	 */
  	if (pmc->soc && pmc->soc->has_interrupt_polarity_support) {
  		invert = of_property_read_bool(np, "nvidia,invert-interrupt");
  		value = tegra_pmc_reg_readl(TEGRA_PMC_CNTRL);
  
  		if (invert)
  			value |= PMC_CNTRL_INTR_POLARITY;
  		else
  			value &= ~PMC_CNTRL_INTR_POLARITY;
  
  		tegra_pmc_reg_writel(value, TEGRA_PMC_CNTRL);
  	}
  
  #ifdef CONFIG_PM_SLEEP
  	if (pmc->soc && pmc->soc->skip_lp0_vector_setup)
  		goto skip_lp0_setup;
  
  	tegra_lp0_wakeup.of_node = np;
  	INIT_LIST_HEAD(&tegra_lp0_wakeup.wake_list);
  
  	if (pmc->lp0_vec_size &&
  	    (pmc->lp0_vec_phys < memblock_end_of_DRAM()))
  		if (memblock_reserve(pmc->lp0_vec_phys, pmc->lp0_vec_size))
  			pr_err("Failed to reserve lp0_vec %08llx@%08llx\n",
  			       (u64)pmc->lp0_vec_size,
  			       (u64)pmc->lp0_vec_phys);
  skip_lp0_setup:
  #endif
  
  	return 0;
  }
  early_initcall(tegra_pmc_early_init);
  
  static bool get_secure_pmc_setting(void)
  {
  	struct device_node *np;
  	static bool secure_pmc;
  	static bool initialized;
  	struct resource regs;
  
  	if (!initialized) {
  		initialized = true;
  		np = of_find_matching_node(NULL, tegra_pmc_match);
  		if (!np) {
  			pr_err("%s: compatible node not found\n", __func__);
  			secure_pmc = false;
  			return secure_pmc;
  		}
  		secure_pmc = of_find_property(np,"nvidia,secure-pmc",NULL);
  		pr_info("%s: done secure_pmc=%d\n", __func__, secure_pmc);
  
  		/*
  		 * Initialize pmc->base for calls which are performed much
  		 * before tegra_pmc_probe() or tegra_pmc_early_init().
  		 */
  		if (!secure_pmc) {
  			if (!(of_address_to_resource(np, 0, &regs) < 0))
  				pmc->base = ioremap_nocache(regs.start,
  						resource_size(&regs));
  			else {
  				pr_err("failed to set pmc-base\n");
  				WARN_ON(1);
  			}
  		}
  	}
  	return secure_pmc;
  }
  
  /* PMC register read/write/update with offset from the base */
  u32 tegra_pmc_readl(unsigned long offset)
  {
  	struct pmc_smc_regs regs;
  
  	if (get_secure_pmc_setting()) {
  		regs.args[0] = PMC_READ;
  		regs.args[1] = offset;
  		regs.args[2] = 0;
  		regs.args[3] = 0;
  		regs.args[4] = 0;
  		regs.args[5] = 0;
  		send_smc(TEGRA_SIP_PMC_COMMAND_FID, &regs);
  		return (u32)regs.args[0];
  	}
  	return readl(pmc->base + offset);
  }
  EXPORT_SYMBOL(tegra_pmc_readl);
  
  void tegra_pmc_writel(u32 value, unsigned long offset)
  {
  	struct pmc_smc_regs regs;
  
  	if (get_secure_pmc_setting()) {
  		regs.args[0] = PMC_WRITE;
  		regs.args[1] = offset;
  		regs.args[2] = value;
  		regs.args[3] = 0;
  		regs.args[4] = 0;
  		regs.args[5] = 0;
  		send_smc(TEGRA_SIP_PMC_COMMAND_FID, &regs);
  	} else {
  		writel(value, pmc->base + offset);
  	}
  }
  EXPORT_SYMBOL(tegra_pmc_writel);
  
  void tegra_pmc_writel_relaxed(u32 value, unsigned long offset)
  {
  	if (get_secure_pmc_setting())
  		tegra_pmc_writel(value, offset);
  	else
  		writel_relaxed(value, pmc->base + offset);
  }
  EXPORT_SYMBOL(tegra_pmc_writel_relaxed);
  
  /* PMC register read/write/update with pmc register enums */
  static u32 tegra_pmc_reg_readl(enum pmc_regs reg)
  {
  	return tegra_pmc_readl(pmc->soc->rmap[reg]);
  }
  
  static void tegra_pmc_reg_writel(u32 value, enum pmc_regs reg)
  {
  	tegra_pmc_writel(value, pmc->soc->rmap[reg]);
  }
  
  static void pmc_iopower_enable(const struct tegra_pmc_io_pad_soc *pad)
  {
  	if (pad->io_power == UINT_MAX)
  		return;
  
  	tegra_pmc_register_update(TEGRA_PMC_PWR_NO_IOPOWER,
  				  BIT(pad->io_power), 0);
  }
  
  static void pmc_iopower_disable(const struct tegra_pmc_io_pad_soc *pad)
  {
  	if (pad->io_power == UINT_MAX)
  		return;
  
  	tegra_pmc_register_update(TEGRA_PMC_PWR_NO_IOPOWER, BIT(pad->io_power),
  				  BIT(pad->io_power));
  }
  
  static int pmc_iopower_get_status(const struct tegra_pmc_io_pad_soc *pad)
  {
  	unsigned int no_iopower;
  
  	if (pad->io_power == UINT_MAX)
  		return 1;
  
  	no_iopower = tegra_pmc_reg_readl(TEGRA_PMC_PWR_NO_IOPOWER);
  
  	return !(no_iopower & BIT(pad->io_power));
  }
  
  
  static int tegra_pmc_io_rail_change_notify_cb(struct notifier_block *nb,
  					      unsigned long event, void *v)
  {
  	struct tegra_io_pad_regulator *tip_reg;
  	const struct tegra_pmc_io_pad_soc *pad;
  	unsigned long flags;
  
  	if (!(event & (REGULATOR_EVENT_POST_ENABLE |
  		       REGULATOR_EVENT_PRE_DISABLE |
  		       REGULATOR_EVENT_PRE_ENABLE |
  		       REGULATOR_EVENT_DISABLE)))
  		return NOTIFY_OK;
  
  	tip_reg = container_of(nb, struct tegra_io_pad_regulator, nb);
  	pad = tip_reg->pad;
  
  	spin_lock_irqsave(&pwr_lock, flags);
  
  	if (pad->bdsdmem_cfc) {
  		if (event & REGULATOR_EVENT_PRE_ENABLE)
  			pmc_iopower_enable(pad);
  
  		if (event & REGULATOR_EVENT_DISABLE)
  			pmc_iopower_disable(pad);
  	} else {
  		if (event & REGULATOR_EVENT_POST_ENABLE)
  			pmc_iopower_enable(pad);
  
  		if (event & REGULATOR_EVENT_PRE_DISABLE)
  			pmc_iopower_disable(pad);
  	}
  
  	dev_dbg(pmc->dev, "tegra-iopower: %s: event 0x%08lx state: %d\n",
  		pad->name, event, pmc_iopower_get_status(pad));
  
  	spin_unlock_irqrestore(&pwr_lock, flags);
  
  	return NOTIFY_OK;
  }
  
  static int tegra_pmc_io_power_init_one(struct device *dev,
  				       const struct tegra_pmc_io_pad_soc *pad,
  				       u32 *disabled_mask,
  				       bool enable_pad_volt_config)
  {
  	struct tegra_io_pad_regulator *tip_reg;
  	char regname[32]; /* 32 is max size of property name */
  	char *prefix;
  	int curr_io_uv;
  	int ret;
  
  	prefix = "vddio";
  	snprintf(regname, 32, "%s-%s-supply", prefix, pad->name);
  	if (!of_find_property(dev->of_node, regname, NULL)) {
  		prefix = "iopower";
  		snprintf(regname, 32, "%s-%s-supply", prefix, pad->name);
  		if (!of_find_property(dev->of_node, regname, NULL)) {
  			dev_info(dev, "Regulator supply %s not available\n",
  				 regname);
  			return 0;
  		}
  	}
  
  	tip_reg = devm_kzalloc(dev, sizeof(*tip_reg), GFP_KERNEL);
  	if (!tip_reg)
  		return -ENOMEM;
  
  	tip_reg->pad = pad;
  
  	snprintf(regname, 32, "%s-%s", prefix, pad->name);
  	tip_reg->regulator = devm_regulator_get(dev, regname);
  	if (IS_ERR(tip_reg->regulator)) {
  		ret = PTR_ERR(tip_reg->regulator);
  		dev_err(dev, "Failed to get regulator %s: %d\n", regname, ret);
  		return ret;
  	}
  
  	if (!enable_pad_volt_config)
  		goto skip_pad_config;
  
  	ret = regulator_get_voltage(tip_reg->regulator);
  	if (ret < 0) {
  		dev_err(dev, "Failed to get IO rail %s voltage: %d\n",
  			regname, ret);
  		return ret;
  	}
  
  	if (ret == 1200000)
  		curr_io_uv = TEGRA_IO_PAD_VOLTAGE_1200000UV;
  	else if (ret == 1800000)
  		curr_io_uv = TEGRA_IO_PAD_VOLTAGE_1800000UV;
  	else
  		curr_io_uv = TEGRA_IO_PAD_VOLTAGE_3300000UV;
  
  	ret = _tegra_pmc_io_pad_set_voltage(pad, curr_io_uv);
  	if (ret < 0) {
  		dev_err(dev, "Failed to set voltage %duV of I/O pad %s: %d\n",
  			curr_io_uv, pad->name, ret);
  		return ret;
  	}
  
  skip_pad_config:
  	tip_reg->nb.notifier_call = tegra_pmc_io_rail_change_notify_cb;
  	ret = devm_regulator_register_notifier(tip_reg->regulator,
  					       &tip_reg->nb);
  	if (ret < 0) {
  		dev_err(dev, "Failed to register regulator %s notifier: %d\n",
  			regname, ret);
  		return ret;
  	}
  
  	if (regulator_is_enabled(tip_reg->regulator)) {
  		pmc_iopower_enable(pad);
  	} else {
  		*disabled_mask |= BIT(pad->io_power);
  		pmc_iopower_disable(pad);
  	}
  
  	return 0;
  }
  
  #ifdef CONFIG_DEBUG_FS
  static int io_pad_show(struct seq_file *s, void *data)
  {
  	unsigned int i;
  
  	for (i = 0; i < pmc->soc->num_io_pads; i++) {
  		const struct tegra_pmc_io_pad_soc *pad = &pmc->soc->io_pads[i];
  		seq_printf(s, "%16s: dpd = %2d, v = %2d io_power = %2d ",
  			   pad->name, pad->dpd, pad->voltage, pad->io_power);
  		seq_printf(s, "pins[0] = %2d npins = %2d dpdreq = 0x%-3x ",
  			   pad->pins[0], pad->npins, pad->dpd_req_reg);
  		seq_printf(s, "dpdsts = 0x%-3x dpdtmr = 0x%-3x ",
  			   pad->dpd_status_reg, pad->dpd_timer_reg);
  		seq_printf(s, "dpdsmpl = 0x%-3x ", pad->dpd_sample_reg);
  		seq_printf(s, "bds = %d detenb = 0x%-3x detval = 0x%-3x ",
  			   pad->bdsdmem_cfc, pad->io_pad_pwr_det_enable_reg,
  			   pad->io_pad_pwr_det_val_reg);
  		seq_printf(s, "pad_uv_0 = %d pad_uv_1 = %d\n",
  			   pad->pad_uv_0, pad->pad_uv_1);
  	}
  
  	return 0;
  }
  
  static int io_pad_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, io_pad_show, inode->i_private);
  }
  
  static const struct file_operations io_pad_fops = {
  	.open = io_pad_open,
  	.read = seq_read,
  	.llseek = seq_lseek,
  	.release = single_release,
  };
  
  static void tegra_pmc_io_pad_debugfs_init(struct device *dev)
  {
  	struct dentry *d;
  
  	d = debugfs_create_file("tegra-pmc-io-pads", S_IRUGO, NULL, NULL,
  				&io_pad_fops);
  	if (!d)
  		dev_err(dev, "Error in creating the debugFS for pmc-io-pad\n");
  }
  #else
  static void tegra_pmc_io_pad_debugfs_init(struct device *dev)
  {
  }
  #endif
  
  static int tegra_pmc_iopower_probe(struct platform_device *pdev)
  {
  	struct device *dev = &pdev->dev;
  	bool enable_pad_volt_config = false;
  	u32 pwrio_disabled_mask = 0;
  	int i, ret;
  
  	if (!pmc->base) {
  		dev_err(dev, "PMC Driver is not ready\n");
  		return -EPROBE_DEFER;
  	}
  
  	enable_pad_volt_config = of_property_read_bool(dev->of_node,
  					"nvidia,auto-pad-voltage-config");
  
  	for (i = 0; i < pmc->soc->num_io_pads; ++i) {
  		if (pmc->soc->io_pads[i].io_power == UINT_MAX)
  			continue;
  
  		ret = tegra_pmc_io_power_init_one(&pdev->dev,
  						  &pmc->soc->io_pads[i],
  						  &pwrio_disabled_mask,
  						  enable_pad_volt_config);
  		if (ret < 0)
  			dev_info(dev, "io-power cell %s init failed: %d\n",
  				 pmc->soc->io_pads[i].name, ret);
  	}
  
  	dev_info(dev, "NO_IOPOWER setting 0x%x\n", pwrio_disabled_mask);
  	tegra_pmc_io_pad_debugfs_init(dev);
  	return 0;
  }
  
  static const struct of_device_id tegra_pmc_iopower_match[] = {
  	{ .compatible = "nvidia,tegra186-pmc-iopower", },
  	{ .compatible = "nvidia,tegra210-pmc-iopower", },
  	{ }
  };
  
  static struct platform_driver tegra_pmc_iopower_driver = {
  	.probe   = tegra_pmc_iopower_probe,
  	.driver  = {
  		.name  = "tegra-pmc-iopower",
  		.owner = THIS_MODULE,
  		.of_match_table = tegra_pmc_iopower_match,
  	},
  };
  
  builtin_platform_driver(tegra_pmc_iopower_driver);
  