/* * drivers/soc/tegra/pmc.c * * Copyright (c) 2010 Google, Inc * Copyright (c) 2012-2020, NVIDIA CORPORATION. All rights reserved. * * Author: * Colin Cross * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 /* PMIC watchdog reset bit */ #define PMIC_WATCHDOG_RESET 0x02 #ifdef CONFIG_ARM64 #define SMC_ARG0 "x0" #define SMC_ARG1 "x1" #define SMC_ARG2 "x2" #define SMC_ARG3 "x3" #define SMC_ARCH_EXTENSION "" #define SMC_REGISTERS_TRASHED "x4","x5","x6","x7","x8","x9","x10","x11", \ "x12","x13","x14","x15","x16","x17" #else #define SMC_ARG0 "r0" #define SMC_ARG1 "r1" #define SMC_ARG2 "r2" #define SMC_ARG3 "r3" #define SMC_ARCH_EXTENSION ".arch_extension sec\n" #define SMC_REGISTERS_TRASHED "ip" #endif struct pmc_smc_regs { u64 args[NR_SMC_REGS]; }; static inline ulong send_smc(u32 func, struct pmc_smc_regs *regs) { register ulong _r0 asm(SMC_ARG0) = func; register ulong _r1 asm(SMC_ARG1) = (*regs).args[0]; register ulong _r2 asm(SMC_ARG2) = (*regs).args[1]; register ulong _r3 asm(SMC_ARG3) = (*regs).args[2]; asm volatile( __asmeq("%0", SMC_ARG0) __asmeq("%1", SMC_ARG1) __asmeq("%2", SMC_ARG2) __asmeq("%3", SMC_ARG3) __asmeq("%4", SMC_ARG0) __asmeq("%5", SMC_ARG1) __asmeq("%6", SMC_ARG2) __asmeq("%7", SMC_ARG3) SMC_ARCH_EXTENSION "smc #0" /* switch to secure world */ : "=r" (_r0), "=r" (_r1), "=r" (_r2), "=r" (_r3) : "r" (_r0), "r" (_r1), "r" (_r2), "r" (_r3) : SMC_REGISTERS_TRASHED); (*regs).args[0] = _r1; return _r0; } 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_SCRATCH203, 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: /* In case of PMIC watchdog, Reset is Power On Reset. * PMIC status register is saved in SRATCH203 register. * PMC driver checks watchdog status bit to identify * POR is because of watchdog timer reset */ if (tegra_pmc_reg_readl(TEGRA_PMC_SCRATCH203) & PMIC_WATCHDOG_RESET) tegra_rst_rsn_sts = PMIC_WATCHDOG_POR; else 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, ®); 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", ®_addr)) { dev_err(dev, "nvidia,reg-addr missing, %s.\n", disabled); goto out; } if (of_property_read_u32(np, "nvidia,reg-data", ®_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", "PMIC_WATCHDOG_POR", "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_SCRATCH203] = 0x84c, [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", ®s); } 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, ®s) < 0) { pr_err("failed to get PMC registers\n"); return -ENXIO; } pmc->soc = match->data; } pmc->base = ioremap_nocache(regs.start, resource_size(®s)); 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, ®s) < 0)) pmc->base = ioremap_nocache(regs.start, resource_size(®s)); 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, ®s); 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, ®s); } 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);