162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DB8500 PRCM Unit driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) STMicroelectronics 2009 662306a36Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2010 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com> 962306a36Sopenharmony_ci * Author: Sundar Iyer <sundar.iyer@stericsson.com> 1062306a36Sopenharmony_ci * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * U8500 PRCM Unit interface driver 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/export.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/err.h> 2062306a36Sopenharmony_ci#include <linux/spinlock.h> 2162306a36Sopenharmony_ci#include <linux/io.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/mutex.h> 2462306a36Sopenharmony_ci#include <linux/completion.h> 2562306a36Sopenharmony_ci#include <linux/irq.h> 2662306a36Sopenharmony_ci#include <linux/jiffies.h> 2762306a36Sopenharmony_ci#include <linux/bitops.h> 2862306a36Sopenharmony_ci#include <linux/fs.h> 2962306a36Sopenharmony_ci#include <linux/of.h> 3062306a36Sopenharmony_ci#include <linux/of_address.h> 3162306a36Sopenharmony_ci#include <linux/of_irq.h> 3262306a36Sopenharmony_ci#include <linux/platform_device.h> 3362306a36Sopenharmony_ci#include <linux/uaccess.h> 3462306a36Sopenharmony_ci#include <linux/mfd/core.h> 3562306a36Sopenharmony_ci#include <linux/mfd/dbx500-prcmu.h> 3662306a36Sopenharmony_ci#include <linux/mfd/abx500/ab8500.h> 3762306a36Sopenharmony_ci#include <linux/regulator/db8500-prcmu.h> 3862306a36Sopenharmony_ci#include <linux/regulator/machine.h> 3962306a36Sopenharmony_ci#include "db8500-prcmu-regs.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Index of different voltages to be used when accessing AVSData */ 4262306a36Sopenharmony_ci#define PRCM_AVS_BASE 0x2FC 4362306a36Sopenharmony_ci#define PRCM_AVS_VBB_RET (PRCM_AVS_BASE + 0x0) 4462306a36Sopenharmony_ci#define PRCM_AVS_VBB_MAX_OPP (PRCM_AVS_BASE + 0x1) 4562306a36Sopenharmony_ci#define PRCM_AVS_VBB_100_OPP (PRCM_AVS_BASE + 0x2) 4662306a36Sopenharmony_ci#define PRCM_AVS_VBB_50_OPP (PRCM_AVS_BASE + 0x3) 4762306a36Sopenharmony_ci#define PRCM_AVS_VARM_MAX_OPP (PRCM_AVS_BASE + 0x4) 4862306a36Sopenharmony_ci#define PRCM_AVS_VARM_100_OPP (PRCM_AVS_BASE + 0x5) 4962306a36Sopenharmony_ci#define PRCM_AVS_VARM_50_OPP (PRCM_AVS_BASE + 0x6) 5062306a36Sopenharmony_ci#define PRCM_AVS_VARM_RET (PRCM_AVS_BASE + 0x7) 5162306a36Sopenharmony_ci#define PRCM_AVS_VAPE_100_OPP (PRCM_AVS_BASE + 0x8) 5262306a36Sopenharmony_ci#define PRCM_AVS_VAPE_50_OPP (PRCM_AVS_BASE + 0x9) 5362306a36Sopenharmony_ci#define PRCM_AVS_VMOD_100_OPP (PRCM_AVS_BASE + 0xA) 5462306a36Sopenharmony_ci#define PRCM_AVS_VMOD_50_OPP (PRCM_AVS_BASE + 0xB) 5562306a36Sopenharmony_ci#define PRCM_AVS_VSAFE (PRCM_AVS_BASE + 0xC) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define PRCM_AVS_VOLTAGE 0 5862306a36Sopenharmony_ci#define PRCM_AVS_VOLTAGE_MASK 0x3f 5962306a36Sopenharmony_ci#define PRCM_AVS_ISSLOWSTARTUP 6 6062306a36Sopenharmony_ci#define PRCM_AVS_ISSLOWSTARTUP_MASK (1 << PRCM_AVS_ISSLOWSTARTUP) 6162306a36Sopenharmony_ci#define PRCM_AVS_ISMODEENABLE 7 6262306a36Sopenharmony_ci#define PRCM_AVS_ISMODEENABLE_MASK (1 << PRCM_AVS_ISMODEENABLE) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define PRCM_BOOT_STATUS 0xFFF 6562306a36Sopenharmony_ci#define PRCM_ROMCODE_A2P 0xFFE 6662306a36Sopenharmony_ci#define PRCM_ROMCODE_P2A 0xFFD 6762306a36Sopenharmony_ci#define PRCM_XP70_CUR_PWR_STATE 0xFFC /* 4 BYTES */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define PRCM_SW_RST_REASON 0xFF8 /* 2 bytes */ 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define _PRCM_MBOX_HEADER 0xFE8 /* 16 bytes */ 7262306a36Sopenharmony_ci#define PRCM_MBOX_HEADER_REQ_MB0 (_PRCM_MBOX_HEADER + 0x0) 7362306a36Sopenharmony_ci#define PRCM_MBOX_HEADER_REQ_MB1 (_PRCM_MBOX_HEADER + 0x1) 7462306a36Sopenharmony_ci#define PRCM_MBOX_HEADER_REQ_MB2 (_PRCM_MBOX_HEADER + 0x2) 7562306a36Sopenharmony_ci#define PRCM_MBOX_HEADER_REQ_MB3 (_PRCM_MBOX_HEADER + 0x3) 7662306a36Sopenharmony_ci#define PRCM_MBOX_HEADER_REQ_MB4 (_PRCM_MBOX_HEADER + 0x4) 7762306a36Sopenharmony_ci#define PRCM_MBOX_HEADER_REQ_MB5 (_PRCM_MBOX_HEADER + 0x5) 7862306a36Sopenharmony_ci#define PRCM_MBOX_HEADER_ACK_MB0 (_PRCM_MBOX_HEADER + 0x8) 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* Req Mailboxes */ 8162306a36Sopenharmony_ci#define PRCM_REQ_MB0 0xFDC /* 12 bytes */ 8262306a36Sopenharmony_ci#define PRCM_REQ_MB1 0xFD0 /* 12 bytes */ 8362306a36Sopenharmony_ci#define PRCM_REQ_MB2 0xFC0 /* 16 bytes */ 8462306a36Sopenharmony_ci#define PRCM_REQ_MB3 0xE4C /* 372 bytes */ 8562306a36Sopenharmony_ci#define PRCM_REQ_MB4 0xE48 /* 4 bytes */ 8662306a36Sopenharmony_ci#define PRCM_REQ_MB5 0xE44 /* 4 bytes */ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* Ack Mailboxes */ 8962306a36Sopenharmony_ci#define PRCM_ACK_MB0 0xE08 /* 52 bytes */ 9062306a36Sopenharmony_ci#define PRCM_ACK_MB1 0xE04 /* 4 bytes */ 9162306a36Sopenharmony_ci#define PRCM_ACK_MB2 0xE00 /* 4 bytes */ 9262306a36Sopenharmony_ci#define PRCM_ACK_MB3 0xDFC /* 4 bytes */ 9362306a36Sopenharmony_ci#define PRCM_ACK_MB4 0xDF8 /* 4 bytes */ 9462306a36Sopenharmony_ci#define PRCM_ACK_MB5 0xDF4 /* 4 bytes */ 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* Mailbox 0 headers */ 9762306a36Sopenharmony_ci#define MB0H_POWER_STATE_TRANS 0 9862306a36Sopenharmony_ci#define MB0H_CONFIG_WAKEUPS_EXE 1 9962306a36Sopenharmony_ci#define MB0H_READ_WAKEUP_ACK 3 10062306a36Sopenharmony_ci#define MB0H_CONFIG_WAKEUPS_SLEEP 4 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define MB0H_WAKEUP_EXE 2 10362306a36Sopenharmony_ci#define MB0H_WAKEUP_SLEEP 5 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* Mailbox 0 REQs */ 10662306a36Sopenharmony_ci#define PRCM_REQ_MB0_AP_POWER_STATE (PRCM_REQ_MB0 + 0x0) 10762306a36Sopenharmony_ci#define PRCM_REQ_MB0_AP_PLL_STATE (PRCM_REQ_MB0 + 0x1) 10862306a36Sopenharmony_ci#define PRCM_REQ_MB0_ULP_CLOCK_STATE (PRCM_REQ_MB0 + 0x2) 10962306a36Sopenharmony_ci#define PRCM_REQ_MB0_DO_NOT_WFI (PRCM_REQ_MB0 + 0x3) 11062306a36Sopenharmony_ci#define PRCM_REQ_MB0_WAKEUP_8500 (PRCM_REQ_MB0 + 0x4) 11162306a36Sopenharmony_ci#define PRCM_REQ_MB0_WAKEUP_4500 (PRCM_REQ_MB0 + 0x8) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Mailbox 0 ACKs */ 11462306a36Sopenharmony_ci#define PRCM_ACK_MB0_AP_PWRSTTR_STATUS (PRCM_ACK_MB0 + 0x0) 11562306a36Sopenharmony_ci#define PRCM_ACK_MB0_READ_POINTER (PRCM_ACK_MB0 + 0x1) 11662306a36Sopenharmony_ci#define PRCM_ACK_MB0_WAKEUP_0_8500 (PRCM_ACK_MB0 + 0x4) 11762306a36Sopenharmony_ci#define PRCM_ACK_MB0_WAKEUP_0_4500 (PRCM_ACK_MB0 + 0x8) 11862306a36Sopenharmony_ci#define PRCM_ACK_MB0_WAKEUP_1_8500 (PRCM_ACK_MB0 + 0x1C) 11962306a36Sopenharmony_ci#define PRCM_ACK_MB0_WAKEUP_1_4500 (PRCM_ACK_MB0 + 0x20) 12062306a36Sopenharmony_ci#define PRCM_ACK_MB0_EVENT_4500_NUMBERS 20 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* Mailbox 1 headers */ 12362306a36Sopenharmony_ci#define MB1H_ARM_APE_OPP 0x0 12462306a36Sopenharmony_ci#define MB1H_RESET_MODEM 0x2 12562306a36Sopenharmony_ci#define MB1H_REQUEST_APE_OPP_100_VOLT 0x3 12662306a36Sopenharmony_ci#define MB1H_RELEASE_APE_OPP_100_VOLT 0x4 12762306a36Sopenharmony_ci#define MB1H_RELEASE_USB_WAKEUP 0x5 12862306a36Sopenharmony_ci#define MB1H_PLL_ON_OFF 0x6 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* Mailbox 1 Requests */ 13162306a36Sopenharmony_ci#define PRCM_REQ_MB1_ARM_OPP (PRCM_REQ_MB1 + 0x0) 13262306a36Sopenharmony_ci#define PRCM_REQ_MB1_APE_OPP (PRCM_REQ_MB1 + 0x1) 13362306a36Sopenharmony_ci#define PRCM_REQ_MB1_PLL_ON_OFF (PRCM_REQ_MB1 + 0x4) 13462306a36Sopenharmony_ci#define PLL_SOC0_OFF 0x1 13562306a36Sopenharmony_ci#define PLL_SOC0_ON 0x2 13662306a36Sopenharmony_ci#define PLL_SOC1_OFF 0x4 13762306a36Sopenharmony_ci#define PLL_SOC1_ON 0x8 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* Mailbox 1 ACKs */ 14062306a36Sopenharmony_ci#define PRCM_ACK_MB1_CURRENT_ARM_OPP (PRCM_ACK_MB1 + 0x0) 14162306a36Sopenharmony_ci#define PRCM_ACK_MB1_CURRENT_APE_OPP (PRCM_ACK_MB1 + 0x1) 14262306a36Sopenharmony_ci#define PRCM_ACK_MB1_APE_VOLTAGE_STATUS (PRCM_ACK_MB1 + 0x2) 14362306a36Sopenharmony_ci#define PRCM_ACK_MB1_DVFS_STATUS (PRCM_ACK_MB1 + 0x3) 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* Mailbox 2 headers */ 14662306a36Sopenharmony_ci#define MB2H_DPS 0x0 14762306a36Sopenharmony_ci#define MB2H_AUTO_PWR 0x1 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* Mailbox 2 REQs */ 15062306a36Sopenharmony_ci#define PRCM_REQ_MB2_SVA_MMDSP (PRCM_REQ_MB2 + 0x0) 15162306a36Sopenharmony_ci#define PRCM_REQ_MB2_SVA_PIPE (PRCM_REQ_MB2 + 0x1) 15262306a36Sopenharmony_ci#define PRCM_REQ_MB2_SIA_MMDSP (PRCM_REQ_MB2 + 0x2) 15362306a36Sopenharmony_ci#define PRCM_REQ_MB2_SIA_PIPE (PRCM_REQ_MB2 + 0x3) 15462306a36Sopenharmony_ci#define PRCM_REQ_MB2_SGA (PRCM_REQ_MB2 + 0x4) 15562306a36Sopenharmony_ci#define PRCM_REQ_MB2_B2R2_MCDE (PRCM_REQ_MB2 + 0x5) 15662306a36Sopenharmony_ci#define PRCM_REQ_MB2_ESRAM12 (PRCM_REQ_MB2 + 0x6) 15762306a36Sopenharmony_ci#define PRCM_REQ_MB2_ESRAM34 (PRCM_REQ_MB2 + 0x7) 15862306a36Sopenharmony_ci#define PRCM_REQ_MB2_AUTO_PM_SLEEP (PRCM_REQ_MB2 + 0x8) 15962306a36Sopenharmony_ci#define PRCM_REQ_MB2_AUTO_PM_IDLE (PRCM_REQ_MB2 + 0xC) 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* Mailbox 2 ACKs */ 16262306a36Sopenharmony_ci#define PRCM_ACK_MB2_DPS_STATUS (PRCM_ACK_MB2 + 0x0) 16362306a36Sopenharmony_ci#define HWACC_PWR_ST_OK 0xFE 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* Mailbox 3 headers */ 16662306a36Sopenharmony_ci#define MB3H_ANC 0x0 16762306a36Sopenharmony_ci#define MB3H_SIDETONE 0x1 16862306a36Sopenharmony_ci#define MB3H_SYSCLK 0xE 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* Mailbox 3 Requests */ 17162306a36Sopenharmony_ci#define PRCM_REQ_MB3_ANC_FIR_COEFF (PRCM_REQ_MB3 + 0x0) 17262306a36Sopenharmony_ci#define PRCM_REQ_MB3_ANC_IIR_COEFF (PRCM_REQ_MB3 + 0x20) 17362306a36Sopenharmony_ci#define PRCM_REQ_MB3_ANC_SHIFTER (PRCM_REQ_MB3 + 0x60) 17462306a36Sopenharmony_ci#define PRCM_REQ_MB3_ANC_WARP (PRCM_REQ_MB3 + 0x64) 17562306a36Sopenharmony_ci#define PRCM_REQ_MB3_SIDETONE_FIR_GAIN (PRCM_REQ_MB3 + 0x68) 17662306a36Sopenharmony_ci#define PRCM_REQ_MB3_SIDETONE_FIR_COEFF (PRCM_REQ_MB3 + 0x6C) 17762306a36Sopenharmony_ci#define PRCM_REQ_MB3_SYSCLK_MGT (PRCM_REQ_MB3 + 0x16C) 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* Mailbox 4 headers */ 18062306a36Sopenharmony_ci#define MB4H_DDR_INIT 0x0 18162306a36Sopenharmony_ci#define MB4H_MEM_ST 0x1 18262306a36Sopenharmony_ci#define MB4H_HOTDOG 0x12 18362306a36Sopenharmony_ci#define MB4H_HOTMON 0x13 18462306a36Sopenharmony_ci#define MB4H_HOT_PERIOD 0x14 18562306a36Sopenharmony_ci#define MB4H_A9WDOG_CONF 0x16 18662306a36Sopenharmony_ci#define MB4H_A9WDOG_EN 0x17 18762306a36Sopenharmony_ci#define MB4H_A9WDOG_DIS 0x18 18862306a36Sopenharmony_ci#define MB4H_A9WDOG_LOAD 0x19 18962306a36Sopenharmony_ci#define MB4H_A9WDOG_KICK 0x20 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* Mailbox 4 Requests */ 19262306a36Sopenharmony_ci#define PRCM_REQ_MB4_DDR_ST_AP_SLEEP_IDLE (PRCM_REQ_MB4 + 0x0) 19362306a36Sopenharmony_ci#define PRCM_REQ_MB4_DDR_ST_AP_DEEP_IDLE (PRCM_REQ_MB4 + 0x1) 19462306a36Sopenharmony_ci#define PRCM_REQ_MB4_ESRAM0_ST (PRCM_REQ_MB4 + 0x3) 19562306a36Sopenharmony_ci#define PRCM_REQ_MB4_HOTDOG_THRESHOLD (PRCM_REQ_MB4 + 0x0) 19662306a36Sopenharmony_ci#define PRCM_REQ_MB4_HOTMON_LOW (PRCM_REQ_MB4 + 0x0) 19762306a36Sopenharmony_ci#define PRCM_REQ_MB4_HOTMON_HIGH (PRCM_REQ_MB4 + 0x1) 19862306a36Sopenharmony_ci#define PRCM_REQ_MB4_HOTMON_CONFIG (PRCM_REQ_MB4 + 0x2) 19962306a36Sopenharmony_ci#define PRCM_REQ_MB4_HOT_PERIOD (PRCM_REQ_MB4 + 0x0) 20062306a36Sopenharmony_ci#define HOTMON_CONFIG_LOW BIT(0) 20162306a36Sopenharmony_ci#define HOTMON_CONFIG_HIGH BIT(1) 20262306a36Sopenharmony_ci#define PRCM_REQ_MB4_A9WDOG_0 (PRCM_REQ_MB4 + 0x0) 20362306a36Sopenharmony_ci#define PRCM_REQ_MB4_A9WDOG_1 (PRCM_REQ_MB4 + 0x1) 20462306a36Sopenharmony_ci#define PRCM_REQ_MB4_A9WDOG_2 (PRCM_REQ_MB4 + 0x2) 20562306a36Sopenharmony_ci#define PRCM_REQ_MB4_A9WDOG_3 (PRCM_REQ_MB4 + 0x3) 20662306a36Sopenharmony_ci#define A9WDOG_AUTO_OFF_EN BIT(7) 20762306a36Sopenharmony_ci#define A9WDOG_AUTO_OFF_DIS 0 20862306a36Sopenharmony_ci#define A9WDOG_ID_MASK 0xf 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* Mailbox 5 Requests */ 21162306a36Sopenharmony_ci#define PRCM_REQ_MB5_I2C_SLAVE_OP (PRCM_REQ_MB5 + 0x0) 21262306a36Sopenharmony_ci#define PRCM_REQ_MB5_I2C_HW_BITS (PRCM_REQ_MB5 + 0x1) 21362306a36Sopenharmony_ci#define PRCM_REQ_MB5_I2C_REG (PRCM_REQ_MB5 + 0x2) 21462306a36Sopenharmony_ci#define PRCM_REQ_MB5_I2C_VAL (PRCM_REQ_MB5 + 0x3) 21562306a36Sopenharmony_ci#define PRCMU_I2C_WRITE(slave) (((slave) << 1) | BIT(6)) 21662306a36Sopenharmony_ci#define PRCMU_I2C_READ(slave) (((slave) << 1) | BIT(0) | BIT(6)) 21762306a36Sopenharmony_ci#define PRCMU_I2C_STOP_EN BIT(3) 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/* Mailbox 5 ACKs */ 22062306a36Sopenharmony_ci#define PRCM_ACK_MB5_I2C_STATUS (PRCM_ACK_MB5 + 0x1) 22162306a36Sopenharmony_ci#define PRCM_ACK_MB5_I2C_VAL (PRCM_ACK_MB5 + 0x3) 22262306a36Sopenharmony_ci#define I2C_WR_OK 0x1 22362306a36Sopenharmony_ci#define I2C_RD_OK 0x2 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci#define NUM_MB 8 22662306a36Sopenharmony_ci#define MBOX_BIT BIT 22762306a36Sopenharmony_ci#define ALL_MBOX_BITS (MBOX_BIT(NUM_MB) - 1) 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* 23062306a36Sopenharmony_ci * Wakeups/IRQs 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci#define WAKEUP_BIT_RTC BIT(0) 23462306a36Sopenharmony_ci#define WAKEUP_BIT_RTT0 BIT(1) 23562306a36Sopenharmony_ci#define WAKEUP_BIT_RTT1 BIT(2) 23662306a36Sopenharmony_ci#define WAKEUP_BIT_HSI0 BIT(3) 23762306a36Sopenharmony_ci#define WAKEUP_BIT_HSI1 BIT(4) 23862306a36Sopenharmony_ci#define WAKEUP_BIT_CA_WAKE BIT(5) 23962306a36Sopenharmony_ci#define WAKEUP_BIT_USB BIT(6) 24062306a36Sopenharmony_ci#define WAKEUP_BIT_ABB BIT(7) 24162306a36Sopenharmony_ci#define WAKEUP_BIT_ABB_FIFO BIT(8) 24262306a36Sopenharmony_ci#define WAKEUP_BIT_SYSCLK_OK BIT(9) 24362306a36Sopenharmony_ci#define WAKEUP_BIT_CA_SLEEP BIT(10) 24462306a36Sopenharmony_ci#define WAKEUP_BIT_AC_WAKE_ACK BIT(11) 24562306a36Sopenharmony_ci#define WAKEUP_BIT_SIDE_TONE_OK BIT(12) 24662306a36Sopenharmony_ci#define WAKEUP_BIT_ANC_OK BIT(13) 24762306a36Sopenharmony_ci#define WAKEUP_BIT_SW_ERROR BIT(14) 24862306a36Sopenharmony_ci#define WAKEUP_BIT_AC_SLEEP_ACK BIT(15) 24962306a36Sopenharmony_ci#define WAKEUP_BIT_ARM BIT(17) 25062306a36Sopenharmony_ci#define WAKEUP_BIT_HOTMON_LOW BIT(18) 25162306a36Sopenharmony_ci#define WAKEUP_BIT_HOTMON_HIGH BIT(19) 25262306a36Sopenharmony_ci#define WAKEUP_BIT_MODEM_SW_RESET_REQ BIT(20) 25362306a36Sopenharmony_ci#define WAKEUP_BIT_GPIO0 BIT(23) 25462306a36Sopenharmony_ci#define WAKEUP_BIT_GPIO1 BIT(24) 25562306a36Sopenharmony_ci#define WAKEUP_BIT_GPIO2 BIT(25) 25662306a36Sopenharmony_ci#define WAKEUP_BIT_GPIO3 BIT(26) 25762306a36Sopenharmony_ci#define WAKEUP_BIT_GPIO4 BIT(27) 25862306a36Sopenharmony_ci#define WAKEUP_BIT_GPIO5 BIT(28) 25962306a36Sopenharmony_ci#define WAKEUP_BIT_GPIO6 BIT(29) 26062306a36Sopenharmony_ci#define WAKEUP_BIT_GPIO7 BIT(30) 26162306a36Sopenharmony_ci#define WAKEUP_BIT_GPIO8 BIT(31) 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic struct { 26462306a36Sopenharmony_ci bool valid; 26562306a36Sopenharmony_ci struct prcmu_fw_version version; 26662306a36Sopenharmony_ci} fw_info; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic struct irq_domain *db8500_irq_domain; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* 27162306a36Sopenharmony_ci * This vector maps irq numbers to the bits in the bit field used in 27262306a36Sopenharmony_ci * communication with the PRCMU firmware. 27362306a36Sopenharmony_ci * 27462306a36Sopenharmony_ci * The reason for having this is to keep the irq numbers contiguous even though 27562306a36Sopenharmony_ci * the bits in the bit field are not. (The bits also have a tendency to move 27662306a36Sopenharmony_ci * around, to further complicate matters.) 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci#define IRQ_INDEX(_name) ((IRQ_PRCMU_##_name)) 27962306a36Sopenharmony_ci#define IRQ_ENTRY(_name)[IRQ_INDEX(_name)] = (WAKEUP_BIT_##_name) 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci#define IRQ_PRCMU_RTC 0 28262306a36Sopenharmony_ci#define IRQ_PRCMU_RTT0 1 28362306a36Sopenharmony_ci#define IRQ_PRCMU_RTT1 2 28462306a36Sopenharmony_ci#define IRQ_PRCMU_HSI0 3 28562306a36Sopenharmony_ci#define IRQ_PRCMU_HSI1 4 28662306a36Sopenharmony_ci#define IRQ_PRCMU_CA_WAKE 5 28762306a36Sopenharmony_ci#define IRQ_PRCMU_USB 6 28862306a36Sopenharmony_ci#define IRQ_PRCMU_ABB 7 28962306a36Sopenharmony_ci#define IRQ_PRCMU_ABB_FIFO 8 29062306a36Sopenharmony_ci#define IRQ_PRCMU_ARM 9 29162306a36Sopenharmony_ci#define IRQ_PRCMU_MODEM_SW_RESET_REQ 10 29262306a36Sopenharmony_ci#define IRQ_PRCMU_GPIO0 11 29362306a36Sopenharmony_ci#define IRQ_PRCMU_GPIO1 12 29462306a36Sopenharmony_ci#define IRQ_PRCMU_GPIO2 13 29562306a36Sopenharmony_ci#define IRQ_PRCMU_GPIO3 14 29662306a36Sopenharmony_ci#define IRQ_PRCMU_GPIO4 15 29762306a36Sopenharmony_ci#define IRQ_PRCMU_GPIO5 16 29862306a36Sopenharmony_ci#define IRQ_PRCMU_GPIO6 17 29962306a36Sopenharmony_ci#define IRQ_PRCMU_GPIO7 18 30062306a36Sopenharmony_ci#define IRQ_PRCMU_GPIO8 19 30162306a36Sopenharmony_ci#define IRQ_PRCMU_CA_SLEEP 20 30262306a36Sopenharmony_ci#define IRQ_PRCMU_HOTMON_LOW 21 30362306a36Sopenharmony_ci#define IRQ_PRCMU_HOTMON_HIGH 22 30462306a36Sopenharmony_ci#define NUM_PRCMU_WAKEUPS 23 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic u32 prcmu_irq_bit[NUM_PRCMU_WAKEUPS] = { 30762306a36Sopenharmony_ci IRQ_ENTRY(RTC), 30862306a36Sopenharmony_ci IRQ_ENTRY(RTT0), 30962306a36Sopenharmony_ci IRQ_ENTRY(RTT1), 31062306a36Sopenharmony_ci IRQ_ENTRY(HSI0), 31162306a36Sopenharmony_ci IRQ_ENTRY(HSI1), 31262306a36Sopenharmony_ci IRQ_ENTRY(CA_WAKE), 31362306a36Sopenharmony_ci IRQ_ENTRY(USB), 31462306a36Sopenharmony_ci IRQ_ENTRY(ABB), 31562306a36Sopenharmony_ci IRQ_ENTRY(ABB_FIFO), 31662306a36Sopenharmony_ci IRQ_ENTRY(CA_SLEEP), 31762306a36Sopenharmony_ci IRQ_ENTRY(ARM), 31862306a36Sopenharmony_ci IRQ_ENTRY(HOTMON_LOW), 31962306a36Sopenharmony_ci IRQ_ENTRY(HOTMON_HIGH), 32062306a36Sopenharmony_ci IRQ_ENTRY(MODEM_SW_RESET_REQ), 32162306a36Sopenharmony_ci IRQ_ENTRY(GPIO0), 32262306a36Sopenharmony_ci IRQ_ENTRY(GPIO1), 32362306a36Sopenharmony_ci IRQ_ENTRY(GPIO2), 32462306a36Sopenharmony_ci IRQ_ENTRY(GPIO3), 32562306a36Sopenharmony_ci IRQ_ENTRY(GPIO4), 32662306a36Sopenharmony_ci IRQ_ENTRY(GPIO5), 32762306a36Sopenharmony_ci IRQ_ENTRY(GPIO6), 32862306a36Sopenharmony_ci IRQ_ENTRY(GPIO7), 32962306a36Sopenharmony_ci IRQ_ENTRY(GPIO8) 33062306a36Sopenharmony_ci}; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci#define VALID_WAKEUPS (BIT(NUM_PRCMU_WAKEUP_INDICES) - 1) 33362306a36Sopenharmony_ci#define WAKEUP_ENTRY(_name)[PRCMU_WAKEUP_INDEX_##_name] = (WAKEUP_BIT_##_name) 33462306a36Sopenharmony_cistatic u32 prcmu_wakeup_bit[NUM_PRCMU_WAKEUP_INDICES] = { 33562306a36Sopenharmony_ci WAKEUP_ENTRY(RTC), 33662306a36Sopenharmony_ci WAKEUP_ENTRY(RTT0), 33762306a36Sopenharmony_ci WAKEUP_ENTRY(RTT1), 33862306a36Sopenharmony_ci WAKEUP_ENTRY(HSI0), 33962306a36Sopenharmony_ci WAKEUP_ENTRY(HSI1), 34062306a36Sopenharmony_ci WAKEUP_ENTRY(USB), 34162306a36Sopenharmony_ci WAKEUP_ENTRY(ABB), 34262306a36Sopenharmony_ci WAKEUP_ENTRY(ABB_FIFO), 34362306a36Sopenharmony_ci WAKEUP_ENTRY(ARM) 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/* 34762306a36Sopenharmony_ci * mb0_transfer - state needed for mailbox 0 communication. 34862306a36Sopenharmony_ci * @lock: The transaction lock. 34962306a36Sopenharmony_ci * @dbb_events_lock: A lock used to handle concurrent access to (parts of) 35062306a36Sopenharmony_ci * the request data. 35162306a36Sopenharmony_ci * @mask_work: Work structure used for (un)masking wakeup interrupts. 35262306a36Sopenharmony_ci * @req: Request data that need to persist between requests. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_cistatic struct { 35562306a36Sopenharmony_ci spinlock_t lock; 35662306a36Sopenharmony_ci spinlock_t dbb_irqs_lock; 35762306a36Sopenharmony_ci struct work_struct mask_work; 35862306a36Sopenharmony_ci struct mutex ac_wake_lock; 35962306a36Sopenharmony_ci struct completion ac_wake_work; 36062306a36Sopenharmony_ci struct { 36162306a36Sopenharmony_ci u32 dbb_irqs; 36262306a36Sopenharmony_ci u32 dbb_wakeups; 36362306a36Sopenharmony_ci u32 abb_events; 36462306a36Sopenharmony_ci } req; 36562306a36Sopenharmony_ci} mb0_transfer; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/* 36862306a36Sopenharmony_ci * mb1_transfer - state needed for mailbox 1 communication. 36962306a36Sopenharmony_ci * @lock: The transaction lock. 37062306a36Sopenharmony_ci * @work: The transaction completion structure. 37162306a36Sopenharmony_ci * @ape_opp: The current APE OPP. 37262306a36Sopenharmony_ci * @ack: Reply ("acknowledge") data. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_cistatic struct { 37562306a36Sopenharmony_ci struct mutex lock; 37662306a36Sopenharmony_ci struct completion work; 37762306a36Sopenharmony_ci u8 ape_opp; 37862306a36Sopenharmony_ci struct { 37962306a36Sopenharmony_ci u8 header; 38062306a36Sopenharmony_ci u8 arm_opp; 38162306a36Sopenharmony_ci u8 ape_opp; 38262306a36Sopenharmony_ci u8 ape_voltage_status; 38362306a36Sopenharmony_ci } ack; 38462306a36Sopenharmony_ci} mb1_transfer; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/* 38762306a36Sopenharmony_ci * mb2_transfer - state needed for mailbox 2 communication. 38862306a36Sopenharmony_ci * @lock: The transaction lock. 38962306a36Sopenharmony_ci * @work: The transaction completion structure. 39062306a36Sopenharmony_ci * @auto_pm_lock: The autonomous power management configuration lock. 39162306a36Sopenharmony_ci * @auto_pm_enabled: A flag indicating whether autonomous PM is enabled. 39262306a36Sopenharmony_ci * @req: Request data that need to persist between requests. 39362306a36Sopenharmony_ci * @ack: Reply ("acknowledge") data. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_cistatic struct { 39662306a36Sopenharmony_ci struct mutex lock; 39762306a36Sopenharmony_ci struct completion work; 39862306a36Sopenharmony_ci spinlock_t auto_pm_lock; 39962306a36Sopenharmony_ci bool auto_pm_enabled; 40062306a36Sopenharmony_ci struct { 40162306a36Sopenharmony_ci u8 status; 40262306a36Sopenharmony_ci } ack; 40362306a36Sopenharmony_ci} mb2_transfer; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci/* 40662306a36Sopenharmony_ci * mb3_transfer - state needed for mailbox 3 communication. 40762306a36Sopenharmony_ci * @lock: The request lock. 40862306a36Sopenharmony_ci * @sysclk_lock: A lock used to handle concurrent sysclk requests. 40962306a36Sopenharmony_ci * @sysclk_work: Work structure used for sysclk requests. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_cistatic struct { 41262306a36Sopenharmony_ci spinlock_t lock; 41362306a36Sopenharmony_ci struct mutex sysclk_lock; 41462306a36Sopenharmony_ci struct completion sysclk_work; 41562306a36Sopenharmony_ci} mb3_transfer; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/* 41862306a36Sopenharmony_ci * mb4_transfer - state needed for mailbox 4 communication. 41962306a36Sopenharmony_ci * @lock: The transaction lock. 42062306a36Sopenharmony_ci * @work: The transaction completion structure. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_cistatic struct { 42362306a36Sopenharmony_ci struct mutex lock; 42462306a36Sopenharmony_ci struct completion work; 42562306a36Sopenharmony_ci} mb4_transfer; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/* 42862306a36Sopenharmony_ci * mb5_transfer - state needed for mailbox 5 communication. 42962306a36Sopenharmony_ci * @lock: The transaction lock. 43062306a36Sopenharmony_ci * @work: The transaction completion structure. 43162306a36Sopenharmony_ci * @ack: Reply ("acknowledge") data. 43262306a36Sopenharmony_ci */ 43362306a36Sopenharmony_cistatic struct { 43462306a36Sopenharmony_ci struct mutex lock; 43562306a36Sopenharmony_ci struct completion work; 43662306a36Sopenharmony_ci struct { 43762306a36Sopenharmony_ci u8 status; 43862306a36Sopenharmony_ci u8 value; 43962306a36Sopenharmony_ci } ack; 44062306a36Sopenharmony_ci} mb5_transfer; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic atomic_t ac_wake_req_state = ATOMIC_INIT(0); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/* Spinlocks */ 44562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(prcmu_lock); 44662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(clkout_lock); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci/* Global var to runtime determine TCDM base for v2 or v1 */ 44962306a36Sopenharmony_cistatic __iomem void *tcdm_base; 45062306a36Sopenharmony_cistatic __iomem void *prcmu_base; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistruct clk_mgt { 45362306a36Sopenharmony_ci u32 offset; 45462306a36Sopenharmony_ci u32 pllsw; 45562306a36Sopenharmony_ci int branch; 45662306a36Sopenharmony_ci bool clk38div; 45762306a36Sopenharmony_ci}; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cienum { 46062306a36Sopenharmony_ci PLL_RAW, 46162306a36Sopenharmony_ci PLL_FIX, 46262306a36Sopenharmony_ci PLL_DIV 46362306a36Sopenharmony_ci}; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(clk_mgt_lock); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci#define CLK_MGT_ENTRY(_name, _branch, _clk38div)[PRCMU_##_name] = \ 46862306a36Sopenharmony_ci { (PRCM_##_name##_MGT), 0 , _branch, _clk38div} 46962306a36Sopenharmony_cistatic struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = { 47062306a36Sopenharmony_ci CLK_MGT_ENTRY(SGACLK, PLL_DIV, false), 47162306a36Sopenharmony_ci CLK_MGT_ENTRY(UARTCLK, PLL_FIX, true), 47262306a36Sopenharmony_ci CLK_MGT_ENTRY(MSP02CLK, PLL_FIX, true), 47362306a36Sopenharmony_ci CLK_MGT_ENTRY(MSP1CLK, PLL_FIX, true), 47462306a36Sopenharmony_ci CLK_MGT_ENTRY(I2CCLK, PLL_FIX, true), 47562306a36Sopenharmony_ci CLK_MGT_ENTRY(SDMMCCLK, PLL_DIV, true), 47662306a36Sopenharmony_ci CLK_MGT_ENTRY(SLIMCLK, PLL_FIX, true), 47762306a36Sopenharmony_ci CLK_MGT_ENTRY(PER1CLK, PLL_DIV, true), 47862306a36Sopenharmony_ci CLK_MGT_ENTRY(PER2CLK, PLL_DIV, true), 47962306a36Sopenharmony_ci CLK_MGT_ENTRY(PER3CLK, PLL_DIV, true), 48062306a36Sopenharmony_ci CLK_MGT_ENTRY(PER5CLK, PLL_DIV, true), 48162306a36Sopenharmony_ci CLK_MGT_ENTRY(PER6CLK, PLL_DIV, true), 48262306a36Sopenharmony_ci CLK_MGT_ENTRY(PER7CLK, PLL_DIV, true), 48362306a36Sopenharmony_ci CLK_MGT_ENTRY(LCDCLK, PLL_FIX, true), 48462306a36Sopenharmony_ci CLK_MGT_ENTRY(BMLCLK, PLL_DIV, true), 48562306a36Sopenharmony_ci CLK_MGT_ENTRY(HSITXCLK, PLL_DIV, true), 48662306a36Sopenharmony_ci CLK_MGT_ENTRY(HSIRXCLK, PLL_DIV, true), 48762306a36Sopenharmony_ci CLK_MGT_ENTRY(HDMICLK, PLL_FIX, false), 48862306a36Sopenharmony_ci CLK_MGT_ENTRY(APEATCLK, PLL_DIV, true), 48962306a36Sopenharmony_ci CLK_MGT_ENTRY(APETRACECLK, PLL_DIV, true), 49062306a36Sopenharmony_ci CLK_MGT_ENTRY(MCDECLK, PLL_DIV, true), 49162306a36Sopenharmony_ci CLK_MGT_ENTRY(IPI2CCLK, PLL_FIX, true), 49262306a36Sopenharmony_ci CLK_MGT_ENTRY(DSIALTCLK, PLL_FIX, false), 49362306a36Sopenharmony_ci CLK_MGT_ENTRY(DMACLK, PLL_DIV, true), 49462306a36Sopenharmony_ci CLK_MGT_ENTRY(B2R2CLK, PLL_DIV, true), 49562306a36Sopenharmony_ci CLK_MGT_ENTRY(TVCLK, PLL_FIX, true), 49662306a36Sopenharmony_ci CLK_MGT_ENTRY(SSPCLK, PLL_FIX, true), 49762306a36Sopenharmony_ci CLK_MGT_ENTRY(RNGCLK, PLL_FIX, true), 49862306a36Sopenharmony_ci CLK_MGT_ENTRY(UICCCLK, PLL_FIX, false), 49962306a36Sopenharmony_ci}; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistruct dsiclk { 50262306a36Sopenharmony_ci u32 divsel_mask; 50362306a36Sopenharmony_ci u32 divsel_shift; 50462306a36Sopenharmony_ci u32 divsel; 50562306a36Sopenharmony_ci}; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic struct dsiclk dsiclk[2] = { 50862306a36Sopenharmony_ci { 50962306a36Sopenharmony_ci .divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_MASK, 51062306a36Sopenharmony_ci .divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_SHIFT, 51162306a36Sopenharmony_ci .divsel = PRCM_DSI_PLLOUT_SEL_PHI, 51262306a36Sopenharmony_ci }, 51362306a36Sopenharmony_ci { 51462306a36Sopenharmony_ci .divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_MASK, 51562306a36Sopenharmony_ci .divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_SHIFT, 51662306a36Sopenharmony_ci .divsel = PRCM_DSI_PLLOUT_SEL_PHI, 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci}; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistruct dsiescclk { 52162306a36Sopenharmony_ci u32 en; 52262306a36Sopenharmony_ci u32 div_mask; 52362306a36Sopenharmony_ci u32 div_shift; 52462306a36Sopenharmony_ci}; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic struct dsiescclk dsiescclk[3] = { 52762306a36Sopenharmony_ci { 52862306a36Sopenharmony_ci .en = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_EN, 52962306a36Sopenharmony_ci .div_mask = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_MASK, 53062306a36Sopenharmony_ci .div_shift = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_SHIFT, 53162306a36Sopenharmony_ci }, 53262306a36Sopenharmony_ci { 53362306a36Sopenharmony_ci .en = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_EN, 53462306a36Sopenharmony_ci .div_mask = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_MASK, 53562306a36Sopenharmony_ci .div_shift = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_SHIFT, 53662306a36Sopenharmony_ci }, 53762306a36Sopenharmony_ci { 53862306a36Sopenharmony_ci .en = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_EN, 53962306a36Sopenharmony_ci .div_mask = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_MASK, 54062306a36Sopenharmony_ci .div_shift = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_SHIFT, 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci}; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ciu32 db8500_prcmu_read(unsigned int reg) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci return readl(prcmu_base + reg); 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_civoid db8500_prcmu_write(unsigned int reg, u32 value) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci unsigned long flags; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci spin_lock_irqsave(&prcmu_lock, flags); 55462306a36Sopenharmony_ci writel(value, (prcmu_base + reg)); 55562306a36Sopenharmony_ci spin_unlock_irqrestore(&prcmu_lock, flags); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_civoid db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci u32 val; 56162306a36Sopenharmony_ci unsigned long flags; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci spin_lock_irqsave(&prcmu_lock, flags); 56462306a36Sopenharmony_ci val = readl(prcmu_base + reg); 56562306a36Sopenharmony_ci val = ((val & ~mask) | (value & mask)); 56662306a36Sopenharmony_ci writel(val, (prcmu_base + reg)); 56762306a36Sopenharmony_ci spin_unlock_irqrestore(&prcmu_lock, flags); 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistruct prcmu_fw_version *prcmu_get_fw_version(void) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci return fw_info.valid ? &fw_info.version : NULL; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic bool prcmu_is_ulppll_disabled(void) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci struct prcmu_fw_version *ver; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci ver = prcmu_get_fw_version(); 58062306a36Sopenharmony_ci return ver && ver->project == PRCMU_FW_PROJECT_U8420_SYSCLK; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cibool prcmu_has_arm_maxopp(void) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci return (readb(tcdm_base + PRCM_AVS_VARM_MAX_OPP) & 58662306a36Sopenharmony_ci PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci/** 59062306a36Sopenharmony_ci * prcmu_set_rc_a2p - This function is used to run few power state sequences 59162306a36Sopenharmony_ci * @val: Value to be set, i.e. transition requested 59262306a36Sopenharmony_ci * Returns: 0 on success, -EINVAL on invalid argument 59362306a36Sopenharmony_ci * 59462306a36Sopenharmony_ci * This function is used to run the following power state sequences - 59562306a36Sopenharmony_ci * any state to ApReset, ApDeepSleep to ApExecute, ApExecute to ApDeepSleep 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_ciint prcmu_set_rc_a2p(enum romcode_write val) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci if (val < RDY_2_DS || val > RDY_2_XP70_RST) 60062306a36Sopenharmony_ci return -EINVAL; 60162306a36Sopenharmony_ci writeb(val, (tcdm_base + PRCM_ROMCODE_A2P)); 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/** 60662306a36Sopenharmony_ci * prcmu_get_rc_p2a - This function is used to get power state sequences 60762306a36Sopenharmony_ci * Returns: the power transition that has last happened 60862306a36Sopenharmony_ci * 60962306a36Sopenharmony_ci * This function can return the following transitions- 61062306a36Sopenharmony_ci * any state to ApReset, ApDeepSleep to ApExecute, ApExecute to ApDeepSleep 61162306a36Sopenharmony_ci */ 61262306a36Sopenharmony_cienum romcode_read prcmu_get_rc_p2a(void) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci return readb(tcdm_base + PRCM_ROMCODE_P2A); 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/** 61862306a36Sopenharmony_ci * prcmu_get_xp70_current_state - Return the current XP70 power mode 61962306a36Sopenharmony_ci * Returns: Returns the current AP(ARM) power mode: init, 62062306a36Sopenharmony_ci * apBoot, apExecute, apDeepSleep, apSleep, apIdle, apReset 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_cienum ap_pwrst prcmu_get_xp70_current_state(void) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci return readb(tcdm_base + PRCM_XP70_CUR_PWR_STATE); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci/** 62862306a36Sopenharmony_ci * prcmu_config_clkout - Configure one of the programmable clock outputs. 62962306a36Sopenharmony_ci * @clkout: The CLKOUT number (0 or 1). 63062306a36Sopenharmony_ci * @source: The clock to be used (one of the PRCMU_CLKSRC_*). 63162306a36Sopenharmony_ci * @div: The divider to be applied. 63262306a36Sopenharmony_ci * 63362306a36Sopenharmony_ci * Configures one of the programmable clock outputs (CLKOUTs). 63462306a36Sopenharmony_ci * @div should be in the range [1,63] to request a configuration, or 0 to 63562306a36Sopenharmony_ci * inform that the configuration is no longer requested. 63662306a36Sopenharmony_ci */ 63762306a36Sopenharmony_ciint prcmu_config_clkout(u8 clkout, u8 source, u8 div) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci static int requests[2]; 64062306a36Sopenharmony_ci int r = 0; 64162306a36Sopenharmony_ci unsigned long flags; 64262306a36Sopenharmony_ci u32 val; 64362306a36Sopenharmony_ci u32 bits; 64462306a36Sopenharmony_ci u32 mask; 64562306a36Sopenharmony_ci u32 div_mask; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci BUG_ON(clkout > 1); 64862306a36Sopenharmony_ci BUG_ON(div > 63); 64962306a36Sopenharmony_ci BUG_ON((clkout == 0) && (source > PRCMU_CLKSRC_CLK009)); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (!div && !requests[clkout]) 65262306a36Sopenharmony_ci return -EINVAL; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (clkout == 0) { 65562306a36Sopenharmony_ci div_mask = PRCM_CLKOCR_CLKODIV0_MASK; 65662306a36Sopenharmony_ci mask = (PRCM_CLKOCR_CLKODIV0_MASK | PRCM_CLKOCR_CLKOSEL0_MASK); 65762306a36Sopenharmony_ci bits = ((source << PRCM_CLKOCR_CLKOSEL0_SHIFT) | 65862306a36Sopenharmony_ci (div << PRCM_CLKOCR_CLKODIV0_SHIFT)); 65962306a36Sopenharmony_ci } else { 66062306a36Sopenharmony_ci div_mask = PRCM_CLKOCR_CLKODIV1_MASK; 66162306a36Sopenharmony_ci mask = (PRCM_CLKOCR_CLKODIV1_MASK | PRCM_CLKOCR_CLKOSEL1_MASK | 66262306a36Sopenharmony_ci PRCM_CLKOCR_CLK1TYPE); 66362306a36Sopenharmony_ci bits = ((source << PRCM_CLKOCR_CLKOSEL1_SHIFT) | 66462306a36Sopenharmony_ci (div << PRCM_CLKOCR_CLKODIV1_SHIFT)); 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci bits &= mask; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci spin_lock_irqsave(&clkout_lock, flags); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci val = readl(PRCM_CLKOCR); 67162306a36Sopenharmony_ci if (val & div_mask) { 67262306a36Sopenharmony_ci if (div) { 67362306a36Sopenharmony_ci if ((val & mask) != bits) { 67462306a36Sopenharmony_ci r = -EBUSY; 67562306a36Sopenharmony_ci goto unlock_and_return; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci } else { 67862306a36Sopenharmony_ci if ((val & mask & ~div_mask) != bits) { 67962306a36Sopenharmony_ci r = -EINVAL; 68062306a36Sopenharmony_ci goto unlock_and_return; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci writel((bits | (val & ~mask)), PRCM_CLKOCR); 68562306a36Sopenharmony_ci requests[clkout] += (div ? 1 : -1); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ciunlock_and_return: 68862306a36Sopenharmony_ci spin_unlock_irqrestore(&clkout_lock, flags); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci return r; 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ciint db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci unsigned long flags; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci BUG_ON((state < PRCMU_AP_SLEEP) || (PRCMU_AP_DEEP_IDLE < state)); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci spin_lock_irqsave(&mb0_transfer.lock, flags); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0)) 70262306a36Sopenharmony_ci cpu_relax(); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci writeb(MB0H_POWER_STATE_TRANS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0)); 70562306a36Sopenharmony_ci writeb(state, (tcdm_base + PRCM_REQ_MB0_AP_POWER_STATE)); 70662306a36Sopenharmony_ci writeb((keep_ap_pll ? 1 : 0), (tcdm_base + PRCM_REQ_MB0_AP_PLL_STATE)); 70762306a36Sopenharmony_ci writeb((keep_ulp_clk ? 1 : 0), 70862306a36Sopenharmony_ci (tcdm_base + PRCM_REQ_MB0_ULP_CLOCK_STATE)); 70962306a36Sopenharmony_ci writeb(0, (tcdm_base + PRCM_REQ_MB0_DO_NOT_WFI)); 71062306a36Sopenharmony_ci writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci spin_unlock_irqrestore(&mb0_transfer.lock, flags); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ciu8 db8500_prcmu_get_power_state_result(void) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci return readb(tcdm_base + PRCM_ACK_MB0_AP_PWRSTTR_STATUS); 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci/* This function should only be called while mb0_transfer.lock is held. */ 72362306a36Sopenharmony_cistatic void config_wakeups(void) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci const u8 header[2] = { 72662306a36Sopenharmony_ci MB0H_CONFIG_WAKEUPS_EXE, 72762306a36Sopenharmony_ci MB0H_CONFIG_WAKEUPS_SLEEP 72862306a36Sopenharmony_ci }; 72962306a36Sopenharmony_ci static u32 last_dbb_events; 73062306a36Sopenharmony_ci static u32 last_abb_events; 73162306a36Sopenharmony_ci u32 dbb_events; 73262306a36Sopenharmony_ci u32 abb_events; 73362306a36Sopenharmony_ci unsigned int i; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci dbb_events = mb0_transfer.req.dbb_irqs | mb0_transfer.req.dbb_wakeups; 73662306a36Sopenharmony_ci dbb_events |= (WAKEUP_BIT_AC_WAKE_ACK | WAKEUP_BIT_AC_SLEEP_ACK); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci abb_events = mb0_transfer.req.abb_events; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if ((dbb_events == last_dbb_events) && (abb_events == last_abb_events)) 74162306a36Sopenharmony_ci return; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 74462306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0)) 74562306a36Sopenharmony_ci cpu_relax(); 74662306a36Sopenharmony_ci writel(dbb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_8500)); 74762306a36Sopenharmony_ci writel(abb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_4500)); 74862306a36Sopenharmony_ci writeb(header[i], (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0)); 74962306a36Sopenharmony_ci writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET); 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci last_dbb_events = dbb_events; 75262306a36Sopenharmony_ci last_abb_events = abb_events; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_civoid db8500_prcmu_enable_wakeups(u32 wakeups) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci unsigned long flags; 75862306a36Sopenharmony_ci u32 bits; 75962306a36Sopenharmony_ci int i; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci BUG_ON(wakeups != (wakeups & VALID_WAKEUPS)); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci for (i = 0, bits = 0; i < NUM_PRCMU_WAKEUP_INDICES; i++) { 76462306a36Sopenharmony_ci if (wakeups & BIT(i)) 76562306a36Sopenharmony_ci bits |= prcmu_wakeup_bit[i]; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci spin_lock_irqsave(&mb0_transfer.lock, flags); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci mb0_transfer.req.dbb_wakeups = bits; 77162306a36Sopenharmony_ci config_wakeups(); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci spin_unlock_irqrestore(&mb0_transfer.lock, flags); 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_civoid db8500_prcmu_config_abb_event_readout(u32 abb_events) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci unsigned long flags; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci spin_lock_irqsave(&mb0_transfer.lock, flags); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci mb0_transfer.req.abb_events = abb_events; 78362306a36Sopenharmony_ci config_wakeups(); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci spin_unlock_irqrestore(&mb0_transfer.lock, flags); 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_civoid db8500_prcmu_get_abb_event_buffer(void __iomem **buf) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci if (readb(tcdm_base + PRCM_ACK_MB0_READ_POINTER) & 1) 79162306a36Sopenharmony_ci *buf = (tcdm_base + PRCM_ACK_MB0_WAKEUP_1_4500); 79262306a36Sopenharmony_ci else 79362306a36Sopenharmony_ci *buf = (tcdm_base + PRCM_ACK_MB0_WAKEUP_0_4500); 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci/** 79762306a36Sopenharmony_ci * db8500_prcmu_set_arm_opp - set the appropriate ARM OPP 79862306a36Sopenharmony_ci * @opp: The new ARM operating point to which transition is to be made 79962306a36Sopenharmony_ci * Returns: 0 on success, non-zero on failure 80062306a36Sopenharmony_ci * 80162306a36Sopenharmony_ci * This function sets the operating point of the ARM. 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ciint db8500_prcmu_set_arm_opp(u8 opp) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci int r; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (opp < ARM_NO_CHANGE || opp > ARM_EXTCLK) 80862306a36Sopenharmony_ci return -EINVAL; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci r = 0; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci mutex_lock(&mb1_transfer.lock); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) 81562306a36Sopenharmony_ci cpu_relax(); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); 81862306a36Sopenharmony_ci writeb(opp, (tcdm_base + PRCM_REQ_MB1_ARM_OPP)); 81962306a36Sopenharmony_ci writeb(APE_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_APE_OPP)); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); 82262306a36Sopenharmony_ci wait_for_completion(&mb1_transfer.work); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) || 82562306a36Sopenharmony_ci (mb1_transfer.ack.arm_opp != opp)) 82662306a36Sopenharmony_ci r = -EIO; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci mutex_unlock(&mb1_transfer.lock); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return r; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci/** 83462306a36Sopenharmony_ci * db8500_prcmu_get_arm_opp - get the current ARM OPP 83562306a36Sopenharmony_ci * 83662306a36Sopenharmony_ci * Returns: the current ARM OPP 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_ciint db8500_prcmu_get_arm_opp(void) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_ARM_OPP); 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci/** 84462306a36Sopenharmony_ci * db8500_prcmu_get_ddr_opp - get the current DDR OPP 84562306a36Sopenharmony_ci * 84662306a36Sopenharmony_ci * Returns: the current DDR OPP 84762306a36Sopenharmony_ci */ 84862306a36Sopenharmony_ciint db8500_prcmu_get_ddr_opp(void) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci return readb(PRCM_DDR_SUBSYS_APE_MINBW); 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci/* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */ 85462306a36Sopenharmony_cistatic void request_even_slower_clocks(bool enable) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci u32 clock_reg[] = { 85762306a36Sopenharmony_ci PRCM_ACLK_MGT, 85862306a36Sopenharmony_ci PRCM_DMACLK_MGT 85962306a36Sopenharmony_ci }; 86062306a36Sopenharmony_ci unsigned long flags; 86162306a36Sopenharmony_ci unsigned int i; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci spin_lock_irqsave(&clk_mgt_lock, flags); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* Grab the HW semaphore. */ 86662306a36Sopenharmony_ci while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) 86762306a36Sopenharmony_ci cpu_relax(); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(clock_reg); i++) { 87062306a36Sopenharmony_ci u32 val; 87162306a36Sopenharmony_ci u32 div; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci val = readl(prcmu_base + clock_reg[i]); 87462306a36Sopenharmony_ci div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK); 87562306a36Sopenharmony_ci if (enable) { 87662306a36Sopenharmony_ci if ((div <= 1) || (div > 15)) { 87762306a36Sopenharmony_ci pr_err("prcmu: Bad clock divider %d in %s\n", 87862306a36Sopenharmony_ci div, __func__); 87962306a36Sopenharmony_ci goto unlock_and_return; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci div <<= 1; 88262306a36Sopenharmony_ci } else { 88362306a36Sopenharmony_ci if (div <= 2) 88462306a36Sopenharmony_ci goto unlock_and_return; 88562306a36Sopenharmony_ci div >>= 1; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) | 88862306a36Sopenharmony_ci (div & PRCM_CLK_MGT_CLKPLLDIV_MASK)); 88962306a36Sopenharmony_ci writel(val, prcmu_base + clock_reg[i]); 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ciunlock_and_return: 89362306a36Sopenharmony_ci /* Release the HW semaphore. */ 89462306a36Sopenharmony_ci writel(0, PRCM_SEM); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci spin_unlock_irqrestore(&clk_mgt_lock, flags); 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/** 90062306a36Sopenharmony_ci * db8500_prcmu_set_ape_opp - set the appropriate APE OPP 90162306a36Sopenharmony_ci * @opp: The new APE operating point to which transition is to be made 90262306a36Sopenharmony_ci * Returns: 0 on success, non-zero on failure 90362306a36Sopenharmony_ci * 90462306a36Sopenharmony_ci * This function sets the operating point of the APE. 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_ciint db8500_prcmu_set_ape_opp(u8 opp) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci int r = 0; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (opp == mb1_transfer.ape_opp) 91162306a36Sopenharmony_ci return 0; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci mutex_lock(&mb1_transfer.lock); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP) 91662306a36Sopenharmony_ci request_even_slower_clocks(false); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if ((opp != APE_100_OPP) && (mb1_transfer.ape_opp != APE_100_OPP)) 91962306a36Sopenharmony_ci goto skip_message; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) 92262306a36Sopenharmony_ci cpu_relax(); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); 92562306a36Sopenharmony_ci writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP)); 92662306a36Sopenharmony_ci writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp), 92762306a36Sopenharmony_ci (tcdm_base + PRCM_REQ_MB1_APE_OPP)); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); 93062306a36Sopenharmony_ci wait_for_completion(&mb1_transfer.work); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) || 93362306a36Sopenharmony_ci (mb1_transfer.ack.ape_opp != opp)) 93462306a36Sopenharmony_ci r = -EIO; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ciskip_message: 93762306a36Sopenharmony_ci if ((!r && (opp == APE_50_PARTLY_25_OPP)) || 93862306a36Sopenharmony_ci (r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP))) 93962306a36Sopenharmony_ci request_even_slower_clocks(true); 94062306a36Sopenharmony_ci if (!r) 94162306a36Sopenharmony_ci mb1_transfer.ape_opp = opp; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci mutex_unlock(&mb1_transfer.lock); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci return r; 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci/** 94962306a36Sopenharmony_ci * db8500_prcmu_get_ape_opp - get the current APE OPP 95062306a36Sopenharmony_ci * 95162306a36Sopenharmony_ci * Returns: the current APE OPP 95262306a36Sopenharmony_ci */ 95362306a36Sopenharmony_ciint db8500_prcmu_get_ape_opp(void) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_APE_OPP); 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci/** 95962306a36Sopenharmony_ci * db8500_prcmu_request_ape_opp_100_voltage - Request APE OPP 100% voltage 96062306a36Sopenharmony_ci * @enable: true to request the higher voltage, false to drop a request. 96162306a36Sopenharmony_ci * 96262306a36Sopenharmony_ci * Calls to this function to enable and disable requests must be balanced. 96362306a36Sopenharmony_ci */ 96462306a36Sopenharmony_ciint db8500_prcmu_request_ape_opp_100_voltage(bool enable) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci int r = 0; 96762306a36Sopenharmony_ci u8 header; 96862306a36Sopenharmony_ci static unsigned int requests; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci mutex_lock(&mb1_transfer.lock); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci if (enable) { 97362306a36Sopenharmony_ci if (0 != requests++) 97462306a36Sopenharmony_ci goto unlock_and_return; 97562306a36Sopenharmony_ci header = MB1H_REQUEST_APE_OPP_100_VOLT; 97662306a36Sopenharmony_ci } else { 97762306a36Sopenharmony_ci if (requests == 0) { 97862306a36Sopenharmony_ci r = -EIO; 97962306a36Sopenharmony_ci goto unlock_and_return; 98062306a36Sopenharmony_ci } else if (1 != requests--) { 98162306a36Sopenharmony_ci goto unlock_and_return; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci header = MB1H_RELEASE_APE_OPP_100_VOLT; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) 98762306a36Sopenharmony_ci cpu_relax(); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci writeb(header, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); 99262306a36Sopenharmony_ci wait_for_completion(&mb1_transfer.work); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if ((mb1_transfer.ack.header != header) || 99562306a36Sopenharmony_ci ((mb1_transfer.ack.ape_voltage_status & BIT(0)) != 0)) 99662306a36Sopenharmony_ci r = -EIO; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ciunlock_and_return: 99962306a36Sopenharmony_ci mutex_unlock(&mb1_transfer.lock); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci return r; 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci/** 100562306a36Sopenharmony_ci * prcmu_release_usb_wakeup_state - release the state required by a USB wakeup 100662306a36Sopenharmony_ci * 100762306a36Sopenharmony_ci * This function releases the power state requirements of a USB wakeup. 100862306a36Sopenharmony_ci */ 100962306a36Sopenharmony_ciint prcmu_release_usb_wakeup_state(void) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci int r = 0; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci mutex_lock(&mb1_transfer.lock); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) 101662306a36Sopenharmony_ci cpu_relax(); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci writeb(MB1H_RELEASE_USB_WAKEUP, 101962306a36Sopenharmony_ci (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); 102262306a36Sopenharmony_ci wait_for_completion(&mb1_transfer.work); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci if ((mb1_transfer.ack.header != MB1H_RELEASE_USB_WAKEUP) || 102562306a36Sopenharmony_ci ((mb1_transfer.ack.ape_voltage_status & BIT(0)) != 0)) 102662306a36Sopenharmony_ci r = -EIO; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci mutex_unlock(&mb1_transfer.lock); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci return r; 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic int request_pll(u8 clock, bool enable) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci int r = 0; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (clock == PRCMU_PLLSOC0) 103862306a36Sopenharmony_ci clock = (enable ? PLL_SOC0_ON : PLL_SOC0_OFF); 103962306a36Sopenharmony_ci else if (clock == PRCMU_PLLSOC1) 104062306a36Sopenharmony_ci clock = (enable ? PLL_SOC1_ON : PLL_SOC1_OFF); 104162306a36Sopenharmony_ci else 104262306a36Sopenharmony_ci return -EINVAL; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci mutex_lock(&mb1_transfer.lock); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) 104762306a36Sopenharmony_ci cpu_relax(); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci writeb(MB1H_PLL_ON_OFF, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); 105062306a36Sopenharmony_ci writeb(clock, (tcdm_base + PRCM_REQ_MB1_PLL_ON_OFF)); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); 105362306a36Sopenharmony_ci wait_for_completion(&mb1_transfer.work); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci if (mb1_transfer.ack.header != MB1H_PLL_ON_OFF) 105662306a36Sopenharmony_ci r = -EIO; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci mutex_unlock(&mb1_transfer.lock); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci return r; 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci/** 106462306a36Sopenharmony_ci * db8500_prcmu_set_epod - set the state of a EPOD (power domain) 106562306a36Sopenharmony_ci * @epod_id: The EPOD to set 106662306a36Sopenharmony_ci * @epod_state: The new EPOD state 106762306a36Sopenharmony_ci * 106862306a36Sopenharmony_ci * This function sets the state of a EPOD (power domain). It may not be called 106962306a36Sopenharmony_ci * from interrupt context. 107062306a36Sopenharmony_ci */ 107162306a36Sopenharmony_ciint db8500_prcmu_set_epod(u16 epod_id, u8 epod_state) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci int r = 0; 107462306a36Sopenharmony_ci bool ram_retention = false; 107562306a36Sopenharmony_ci int i; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* check argument */ 107862306a36Sopenharmony_ci BUG_ON(epod_id >= NUM_EPOD_ID); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci /* set flag if retention is possible */ 108162306a36Sopenharmony_ci switch (epod_id) { 108262306a36Sopenharmony_ci case EPOD_ID_SVAMMDSP: 108362306a36Sopenharmony_ci case EPOD_ID_SIAMMDSP: 108462306a36Sopenharmony_ci case EPOD_ID_ESRAM12: 108562306a36Sopenharmony_ci case EPOD_ID_ESRAM34: 108662306a36Sopenharmony_ci ram_retention = true; 108762306a36Sopenharmony_ci break; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* check argument */ 109162306a36Sopenharmony_ci BUG_ON(epod_state > EPOD_STATE_ON); 109262306a36Sopenharmony_ci BUG_ON(epod_state == EPOD_STATE_RAMRET && !ram_retention); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci /* get lock */ 109562306a36Sopenharmony_ci mutex_lock(&mb2_transfer.lock); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* wait for mailbox */ 109862306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(2)) 109962306a36Sopenharmony_ci cpu_relax(); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* fill in mailbox */ 110262306a36Sopenharmony_ci for (i = 0; i < NUM_EPOD_ID; i++) 110362306a36Sopenharmony_ci writeb(EPOD_STATE_NO_CHANGE, (tcdm_base + PRCM_REQ_MB2 + i)); 110462306a36Sopenharmony_ci writeb(epod_state, (tcdm_base + PRCM_REQ_MB2 + epod_id)); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci writeb(MB2H_DPS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB2)); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci writel(MBOX_BIT(2), PRCM_MBOX_CPU_SET); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci /* 111162306a36Sopenharmony_ci * The current firmware version does not handle errors correctly, 111262306a36Sopenharmony_ci * and we cannot recover if there is an error. 111362306a36Sopenharmony_ci * This is expected to change when the firmware is updated. 111462306a36Sopenharmony_ci */ 111562306a36Sopenharmony_ci if (!wait_for_completion_timeout(&mb2_transfer.work, 111662306a36Sopenharmony_ci msecs_to_jiffies(20000))) { 111762306a36Sopenharmony_ci pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n", 111862306a36Sopenharmony_ci __func__); 111962306a36Sopenharmony_ci r = -EIO; 112062306a36Sopenharmony_ci goto unlock_and_return; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (mb2_transfer.ack.status != HWACC_PWR_ST_OK) 112462306a36Sopenharmony_ci r = -EIO; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ciunlock_and_return: 112762306a36Sopenharmony_ci mutex_unlock(&mb2_transfer.lock); 112862306a36Sopenharmony_ci return r; 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci/** 113262306a36Sopenharmony_ci * prcmu_configure_auto_pm - Configure autonomous power management. 113362306a36Sopenharmony_ci * @sleep: Configuration for ApSleep. 113462306a36Sopenharmony_ci * @idle: Configuration for ApIdle. 113562306a36Sopenharmony_ci */ 113662306a36Sopenharmony_civoid prcmu_configure_auto_pm(struct prcmu_auto_pm_config *sleep, 113762306a36Sopenharmony_ci struct prcmu_auto_pm_config *idle) 113862306a36Sopenharmony_ci{ 113962306a36Sopenharmony_ci u32 sleep_cfg; 114062306a36Sopenharmony_ci u32 idle_cfg; 114162306a36Sopenharmony_ci unsigned long flags; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci BUG_ON((sleep == NULL) || (idle == NULL)); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci sleep_cfg = (sleep->sva_auto_pm_enable & 0xF); 114662306a36Sopenharmony_ci sleep_cfg = ((sleep_cfg << 4) | (sleep->sia_auto_pm_enable & 0xF)); 114762306a36Sopenharmony_ci sleep_cfg = ((sleep_cfg << 8) | (sleep->sva_power_on & 0xFF)); 114862306a36Sopenharmony_ci sleep_cfg = ((sleep_cfg << 8) | (sleep->sia_power_on & 0xFF)); 114962306a36Sopenharmony_ci sleep_cfg = ((sleep_cfg << 4) | (sleep->sva_policy & 0xF)); 115062306a36Sopenharmony_ci sleep_cfg = ((sleep_cfg << 4) | (sleep->sia_policy & 0xF)); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci idle_cfg = (idle->sva_auto_pm_enable & 0xF); 115362306a36Sopenharmony_ci idle_cfg = ((idle_cfg << 4) | (idle->sia_auto_pm_enable & 0xF)); 115462306a36Sopenharmony_ci idle_cfg = ((idle_cfg << 8) | (idle->sva_power_on & 0xFF)); 115562306a36Sopenharmony_ci idle_cfg = ((idle_cfg << 8) | (idle->sia_power_on & 0xFF)); 115662306a36Sopenharmony_ci idle_cfg = ((idle_cfg << 4) | (idle->sva_policy & 0xF)); 115762306a36Sopenharmony_ci idle_cfg = ((idle_cfg << 4) | (idle->sia_policy & 0xF)); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci spin_lock_irqsave(&mb2_transfer.auto_pm_lock, flags); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci /* 116262306a36Sopenharmony_ci * The autonomous power management configuration is done through 116362306a36Sopenharmony_ci * fields in mailbox 2, but these fields are only used as shared 116462306a36Sopenharmony_ci * variables - i.e. there is no need to send a message. 116562306a36Sopenharmony_ci */ 116662306a36Sopenharmony_ci writel(sleep_cfg, (tcdm_base + PRCM_REQ_MB2_AUTO_PM_SLEEP)); 116762306a36Sopenharmony_ci writel(idle_cfg, (tcdm_base + PRCM_REQ_MB2_AUTO_PM_IDLE)); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci mb2_transfer.auto_pm_enabled = 117062306a36Sopenharmony_ci ((sleep->sva_auto_pm_enable == PRCMU_AUTO_PM_ON) || 117162306a36Sopenharmony_ci (sleep->sia_auto_pm_enable == PRCMU_AUTO_PM_ON) || 117262306a36Sopenharmony_ci (idle->sva_auto_pm_enable == PRCMU_AUTO_PM_ON) || 117362306a36Sopenharmony_ci (idle->sia_auto_pm_enable == PRCMU_AUTO_PM_ON)); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci spin_unlock_irqrestore(&mb2_transfer.auto_pm_lock, flags); 117662306a36Sopenharmony_ci} 117762306a36Sopenharmony_ciEXPORT_SYMBOL(prcmu_configure_auto_pm); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_cibool prcmu_is_auto_pm_enabled(void) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci return mb2_transfer.auto_pm_enabled; 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cistatic int request_sysclk(bool enable) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci int r; 118762306a36Sopenharmony_ci unsigned long flags; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci r = 0; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci mutex_lock(&mb3_transfer.sysclk_lock); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci spin_lock_irqsave(&mb3_transfer.lock, flags); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(3)) 119662306a36Sopenharmony_ci cpu_relax(); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci writeb((enable ? ON : OFF), (tcdm_base + PRCM_REQ_MB3_SYSCLK_MGT)); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci writeb(MB3H_SYSCLK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB3)); 120162306a36Sopenharmony_ci writel(MBOX_BIT(3), PRCM_MBOX_CPU_SET); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci spin_unlock_irqrestore(&mb3_transfer.lock, flags); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* 120662306a36Sopenharmony_ci * The firmware only sends an ACK if we want to enable the 120762306a36Sopenharmony_ci * SysClk, and it succeeds. 120862306a36Sopenharmony_ci */ 120962306a36Sopenharmony_ci if (enable && !wait_for_completion_timeout(&mb3_transfer.sysclk_work, 121062306a36Sopenharmony_ci msecs_to_jiffies(20000))) { 121162306a36Sopenharmony_ci pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n", 121262306a36Sopenharmony_ci __func__); 121362306a36Sopenharmony_ci r = -EIO; 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci mutex_unlock(&mb3_transfer.sysclk_lock); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci return r; 121962306a36Sopenharmony_ci} 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic int request_timclk(bool enable) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci u32 val; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci /* 122662306a36Sopenharmony_ci * On the U8420_CLKSEL firmware, the ULP (Ultra Low Power) 122762306a36Sopenharmony_ci * PLL is disabled so we cannot use doze mode, this will 122862306a36Sopenharmony_ci * stop the clock on this firmware. 122962306a36Sopenharmony_ci */ 123062306a36Sopenharmony_ci if (prcmu_is_ulppll_disabled()) 123162306a36Sopenharmony_ci val = 0; 123262306a36Sopenharmony_ci else 123362306a36Sopenharmony_ci val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if (!enable) 123662306a36Sopenharmony_ci val |= PRCM_TCR_STOP_TIMERS | 123762306a36Sopenharmony_ci PRCM_TCR_DOZE_MODE | 123862306a36Sopenharmony_ci PRCM_TCR_TENSEL_MASK; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci writel(val, PRCM_TCR); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci return 0; 124362306a36Sopenharmony_ci} 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_cistatic int request_clock(u8 clock, bool enable) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci u32 val; 124862306a36Sopenharmony_ci unsigned long flags; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci spin_lock_irqsave(&clk_mgt_lock, flags); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci /* Grab the HW semaphore. */ 125362306a36Sopenharmony_ci while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) 125462306a36Sopenharmony_ci cpu_relax(); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci val = readl(prcmu_base + clk_mgt[clock].offset); 125762306a36Sopenharmony_ci if (enable) { 125862306a36Sopenharmony_ci val |= (PRCM_CLK_MGT_CLKEN | clk_mgt[clock].pllsw); 125962306a36Sopenharmony_ci } else { 126062306a36Sopenharmony_ci clk_mgt[clock].pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK); 126162306a36Sopenharmony_ci val &= ~(PRCM_CLK_MGT_CLKEN | PRCM_CLK_MGT_CLKPLLSW_MASK); 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci writel(val, prcmu_base + clk_mgt[clock].offset); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci /* Release the HW semaphore. */ 126662306a36Sopenharmony_ci writel(0, PRCM_SEM); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci spin_unlock_irqrestore(&clk_mgt_lock, flags); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci return 0; 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_cistatic int request_sga_clock(u8 clock, bool enable) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci u32 val; 127662306a36Sopenharmony_ci int ret; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci if (enable) { 127962306a36Sopenharmony_ci val = readl(PRCM_CGATING_BYPASS); 128062306a36Sopenharmony_ci writel(val | PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS); 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci ret = request_clock(clock, enable); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci if (!ret && !enable) { 128662306a36Sopenharmony_ci val = readl(PRCM_CGATING_BYPASS); 128762306a36Sopenharmony_ci writel(val & ~PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS); 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci return ret; 129162306a36Sopenharmony_ci} 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_cistatic inline bool plldsi_locked(void) 129462306a36Sopenharmony_ci{ 129562306a36Sopenharmony_ci return (readl(PRCM_PLLDSI_LOCKP) & 129662306a36Sopenharmony_ci (PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 | 129762306a36Sopenharmony_ci PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3)) == 129862306a36Sopenharmony_ci (PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 | 129962306a36Sopenharmony_ci PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3); 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic int request_plldsi(bool enable) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci int r = 0; 130562306a36Sopenharmony_ci u32 val; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci writel((PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP | 130862306a36Sopenharmony_ci PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI), (enable ? 130962306a36Sopenharmony_ci PRCM_MMIP_LS_CLAMP_CLR : PRCM_MMIP_LS_CLAMP_SET)); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci val = readl(PRCM_PLLDSI_ENABLE); 131262306a36Sopenharmony_ci if (enable) 131362306a36Sopenharmony_ci val |= PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE; 131462306a36Sopenharmony_ci else 131562306a36Sopenharmony_ci val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE; 131662306a36Sopenharmony_ci writel(val, PRCM_PLLDSI_ENABLE); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci if (enable) { 131962306a36Sopenharmony_ci unsigned int i; 132062306a36Sopenharmony_ci bool locked = plldsi_locked(); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci for (i = 10; !locked && (i > 0); --i) { 132362306a36Sopenharmony_ci udelay(100); 132462306a36Sopenharmony_ci locked = plldsi_locked(); 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci if (locked) { 132762306a36Sopenharmony_ci writel(PRCM_APE_RESETN_DSIPLL_RESETN, 132862306a36Sopenharmony_ci PRCM_APE_RESETN_SET); 132962306a36Sopenharmony_ci } else { 133062306a36Sopenharmony_ci writel((PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP | 133162306a36Sopenharmony_ci PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI), 133262306a36Sopenharmony_ci PRCM_MMIP_LS_CLAMP_SET); 133362306a36Sopenharmony_ci val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE; 133462306a36Sopenharmony_ci writel(val, PRCM_PLLDSI_ENABLE); 133562306a36Sopenharmony_ci r = -EAGAIN; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci } else { 133862306a36Sopenharmony_ci writel(PRCM_APE_RESETN_DSIPLL_RESETN, PRCM_APE_RESETN_CLR); 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci return r; 134162306a36Sopenharmony_ci} 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_cistatic int request_dsiclk(u8 n, bool enable) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci u32 val; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci val = readl(PRCM_DSI_PLLOUT_SEL); 134862306a36Sopenharmony_ci val &= ~dsiclk[n].divsel_mask; 134962306a36Sopenharmony_ci val |= ((enable ? dsiclk[n].divsel : PRCM_DSI_PLLOUT_SEL_OFF) << 135062306a36Sopenharmony_ci dsiclk[n].divsel_shift); 135162306a36Sopenharmony_ci writel(val, PRCM_DSI_PLLOUT_SEL); 135262306a36Sopenharmony_ci return 0; 135362306a36Sopenharmony_ci} 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_cistatic int request_dsiescclk(u8 n, bool enable) 135662306a36Sopenharmony_ci{ 135762306a36Sopenharmony_ci u32 val; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci val = readl(PRCM_DSITVCLK_DIV); 136062306a36Sopenharmony_ci enable ? (val |= dsiescclk[n].en) : (val &= ~dsiescclk[n].en); 136162306a36Sopenharmony_ci writel(val, PRCM_DSITVCLK_DIV); 136262306a36Sopenharmony_ci return 0; 136362306a36Sopenharmony_ci} 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci/** 136662306a36Sopenharmony_ci * db8500_prcmu_request_clock() - Request for a clock to be enabled or disabled. 136762306a36Sopenharmony_ci * @clock: The clock for which the request is made. 136862306a36Sopenharmony_ci * @enable: Whether the clock should be enabled (true) or disabled (false). 136962306a36Sopenharmony_ci * 137062306a36Sopenharmony_ci * This function should only be used by the clock implementation. 137162306a36Sopenharmony_ci * Do not use it from any other place! 137262306a36Sopenharmony_ci */ 137362306a36Sopenharmony_ciint db8500_prcmu_request_clock(u8 clock, bool enable) 137462306a36Sopenharmony_ci{ 137562306a36Sopenharmony_ci if (clock == PRCMU_SGACLK) 137662306a36Sopenharmony_ci return request_sga_clock(clock, enable); 137762306a36Sopenharmony_ci else if (clock < PRCMU_NUM_REG_CLOCKS) 137862306a36Sopenharmony_ci return request_clock(clock, enable); 137962306a36Sopenharmony_ci else if (clock == PRCMU_TIMCLK) 138062306a36Sopenharmony_ci return request_timclk(enable); 138162306a36Sopenharmony_ci else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) 138262306a36Sopenharmony_ci return request_dsiclk((clock - PRCMU_DSI0CLK), enable); 138362306a36Sopenharmony_ci else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) 138462306a36Sopenharmony_ci return request_dsiescclk((clock - PRCMU_DSI0ESCCLK), enable); 138562306a36Sopenharmony_ci else if (clock == PRCMU_PLLDSI) 138662306a36Sopenharmony_ci return request_plldsi(enable); 138762306a36Sopenharmony_ci else if (clock == PRCMU_SYSCLK) 138862306a36Sopenharmony_ci return request_sysclk(enable); 138962306a36Sopenharmony_ci else if ((clock == PRCMU_PLLSOC0) || (clock == PRCMU_PLLSOC1)) 139062306a36Sopenharmony_ci return request_pll(clock, enable); 139162306a36Sopenharmony_ci else 139262306a36Sopenharmony_ci return -EINVAL; 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_cistatic unsigned long pll_rate(void __iomem *reg, unsigned long src_rate, 139662306a36Sopenharmony_ci int branch) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci u64 rate; 139962306a36Sopenharmony_ci u32 val; 140062306a36Sopenharmony_ci u32 d; 140162306a36Sopenharmony_ci u32 div = 1; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci val = readl(reg); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci rate = src_rate; 140662306a36Sopenharmony_ci rate *= ((val & PRCM_PLL_FREQ_D_MASK) >> PRCM_PLL_FREQ_D_SHIFT); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci d = ((val & PRCM_PLL_FREQ_N_MASK) >> PRCM_PLL_FREQ_N_SHIFT); 140962306a36Sopenharmony_ci if (d > 1) 141062306a36Sopenharmony_ci div *= d; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci d = ((val & PRCM_PLL_FREQ_R_MASK) >> PRCM_PLL_FREQ_R_SHIFT); 141362306a36Sopenharmony_ci if (d > 1) 141462306a36Sopenharmony_ci div *= d; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci if (val & PRCM_PLL_FREQ_SELDIV2) 141762306a36Sopenharmony_ci div *= 2; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci if ((branch == PLL_FIX) || ((branch == PLL_DIV) && 142062306a36Sopenharmony_ci (val & PRCM_PLL_FREQ_DIV2EN) && 142162306a36Sopenharmony_ci ((reg == PRCM_PLLSOC0_FREQ) || 142262306a36Sopenharmony_ci (reg == PRCM_PLLARM_FREQ) || 142362306a36Sopenharmony_ci (reg == PRCM_PLLDDR_FREQ)))) 142462306a36Sopenharmony_ci div *= 2; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci (void)do_div(rate, div); 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci return (unsigned long)rate; 142962306a36Sopenharmony_ci} 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci#define ROOT_CLOCK_RATE 38400000 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_cistatic unsigned long clock_rate(u8 clock) 143462306a36Sopenharmony_ci{ 143562306a36Sopenharmony_ci u32 val; 143662306a36Sopenharmony_ci u32 pllsw; 143762306a36Sopenharmony_ci unsigned long rate = ROOT_CLOCK_RATE; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci val = readl(prcmu_base + clk_mgt[clock].offset); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci if (val & PRCM_CLK_MGT_CLK38) { 144262306a36Sopenharmony_ci if (clk_mgt[clock].clk38div && (val & PRCM_CLK_MGT_CLK38DIV)) 144362306a36Sopenharmony_ci rate /= 2; 144462306a36Sopenharmony_ci return rate; 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci val |= clk_mgt[clock].pllsw; 144862306a36Sopenharmony_ci pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK); 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci if (pllsw == PRCM_CLK_MGT_CLKPLLSW_SOC0) 145162306a36Sopenharmony_ci rate = pll_rate(PRCM_PLLSOC0_FREQ, rate, clk_mgt[clock].branch); 145262306a36Sopenharmony_ci else if (pllsw == PRCM_CLK_MGT_CLKPLLSW_SOC1) 145362306a36Sopenharmony_ci rate = pll_rate(PRCM_PLLSOC1_FREQ, rate, clk_mgt[clock].branch); 145462306a36Sopenharmony_ci else if (pllsw == PRCM_CLK_MGT_CLKPLLSW_DDR) 145562306a36Sopenharmony_ci rate = pll_rate(PRCM_PLLDDR_FREQ, rate, clk_mgt[clock].branch); 145662306a36Sopenharmony_ci else 145762306a36Sopenharmony_ci return 0; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci if ((clock == PRCMU_SGACLK) && 146062306a36Sopenharmony_ci (val & PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN)) { 146162306a36Sopenharmony_ci u64 r = (rate * 10); 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci (void)do_div(r, 25); 146462306a36Sopenharmony_ci return (unsigned long)r; 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci val &= PRCM_CLK_MGT_CLKPLLDIV_MASK; 146762306a36Sopenharmony_ci if (val) 146862306a36Sopenharmony_ci return rate / val; 146962306a36Sopenharmony_ci else 147062306a36Sopenharmony_ci return 0; 147162306a36Sopenharmony_ci} 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_cistatic unsigned long armss_rate(void) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci u32 r; 147662306a36Sopenharmony_ci unsigned long rate; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci r = readl(PRCM_ARM_CHGCLKREQ); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci if (r & PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ) { 148162306a36Sopenharmony_ci /* External ARMCLKFIX clock */ 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci rate = pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_FIX); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci /* Check PRCM_ARM_CHGCLKREQ divider */ 148662306a36Sopenharmony_ci if (!(r & PRCM_ARM_CHGCLKREQ_PRCM_ARM_DIVSEL)) 148762306a36Sopenharmony_ci rate /= 2; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci /* Check PRCM_ARMCLKFIX_MGT divider */ 149062306a36Sopenharmony_ci r = readl(PRCM_ARMCLKFIX_MGT); 149162306a36Sopenharmony_ci r &= PRCM_CLK_MGT_CLKPLLDIV_MASK; 149262306a36Sopenharmony_ci rate /= r; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci } else {/* ARM PLL */ 149562306a36Sopenharmony_ci rate = pll_rate(PRCM_PLLARM_FREQ, ROOT_CLOCK_RATE, PLL_DIV); 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci return rate; 149962306a36Sopenharmony_ci} 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_cistatic unsigned long dsiclk_rate(u8 n) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci u32 divsel; 150462306a36Sopenharmony_ci u32 div = 1; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci divsel = readl(PRCM_DSI_PLLOUT_SEL); 150762306a36Sopenharmony_ci divsel = ((divsel & dsiclk[n].divsel_mask) >> dsiclk[n].divsel_shift); 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci if (divsel == PRCM_DSI_PLLOUT_SEL_OFF) 151062306a36Sopenharmony_ci divsel = dsiclk[n].divsel; 151162306a36Sopenharmony_ci else 151262306a36Sopenharmony_ci dsiclk[n].divsel = divsel; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci switch (divsel) { 151562306a36Sopenharmony_ci case PRCM_DSI_PLLOUT_SEL_PHI_4: 151662306a36Sopenharmony_ci div *= 2; 151762306a36Sopenharmony_ci fallthrough; 151862306a36Sopenharmony_ci case PRCM_DSI_PLLOUT_SEL_PHI_2: 151962306a36Sopenharmony_ci div *= 2; 152062306a36Sopenharmony_ci fallthrough; 152162306a36Sopenharmony_ci case PRCM_DSI_PLLOUT_SEL_PHI: 152262306a36Sopenharmony_ci return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), 152362306a36Sopenharmony_ci PLL_RAW) / div; 152462306a36Sopenharmony_ci default: 152562306a36Sopenharmony_ci return 0; 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci} 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_cistatic unsigned long dsiescclk_rate(u8 n) 153062306a36Sopenharmony_ci{ 153162306a36Sopenharmony_ci u32 div; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci div = readl(PRCM_DSITVCLK_DIV); 153462306a36Sopenharmony_ci div = ((div & dsiescclk[n].div_mask) >> (dsiescclk[n].div_shift)); 153562306a36Sopenharmony_ci return clock_rate(PRCMU_TVCLK) / max((u32)1, div); 153662306a36Sopenharmony_ci} 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ciunsigned long prcmu_clock_rate(u8 clock) 153962306a36Sopenharmony_ci{ 154062306a36Sopenharmony_ci if (clock < PRCMU_NUM_REG_CLOCKS) 154162306a36Sopenharmony_ci return clock_rate(clock); 154262306a36Sopenharmony_ci else if (clock == PRCMU_TIMCLK) 154362306a36Sopenharmony_ci return prcmu_is_ulppll_disabled() ? 154462306a36Sopenharmony_ci 32768 : ROOT_CLOCK_RATE / 16; 154562306a36Sopenharmony_ci else if (clock == PRCMU_SYSCLK) 154662306a36Sopenharmony_ci return ROOT_CLOCK_RATE; 154762306a36Sopenharmony_ci else if (clock == PRCMU_PLLSOC0) 154862306a36Sopenharmony_ci return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, PLL_RAW); 154962306a36Sopenharmony_ci else if (clock == PRCMU_PLLSOC1) 155062306a36Sopenharmony_ci return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, PLL_RAW); 155162306a36Sopenharmony_ci else if (clock == PRCMU_ARMSS) 155262306a36Sopenharmony_ci return armss_rate(); 155362306a36Sopenharmony_ci else if (clock == PRCMU_PLLDDR) 155462306a36Sopenharmony_ci return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_RAW); 155562306a36Sopenharmony_ci else if (clock == PRCMU_PLLDSI) 155662306a36Sopenharmony_ci return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), 155762306a36Sopenharmony_ci PLL_RAW); 155862306a36Sopenharmony_ci else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) 155962306a36Sopenharmony_ci return dsiclk_rate(clock - PRCMU_DSI0CLK); 156062306a36Sopenharmony_ci else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) 156162306a36Sopenharmony_ci return dsiescclk_rate(clock - PRCMU_DSI0ESCCLK); 156262306a36Sopenharmony_ci else 156362306a36Sopenharmony_ci return 0; 156462306a36Sopenharmony_ci} 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_cistatic unsigned long clock_source_rate(u32 clk_mgt_val, int branch) 156762306a36Sopenharmony_ci{ 156862306a36Sopenharmony_ci if (clk_mgt_val & PRCM_CLK_MGT_CLK38) 156962306a36Sopenharmony_ci return ROOT_CLOCK_RATE; 157062306a36Sopenharmony_ci clk_mgt_val &= PRCM_CLK_MGT_CLKPLLSW_MASK; 157162306a36Sopenharmony_ci if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_SOC0) 157262306a36Sopenharmony_ci return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, branch); 157362306a36Sopenharmony_ci else if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_SOC1) 157462306a36Sopenharmony_ci return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, branch); 157562306a36Sopenharmony_ci else if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_DDR) 157662306a36Sopenharmony_ci return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, branch); 157762306a36Sopenharmony_ci else 157862306a36Sopenharmony_ci return 0; 157962306a36Sopenharmony_ci} 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_cistatic u32 clock_divider(unsigned long src_rate, unsigned long rate) 158262306a36Sopenharmony_ci{ 158362306a36Sopenharmony_ci u32 div; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci div = (src_rate / rate); 158662306a36Sopenharmony_ci if (div == 0) 158762306a36Sopenharmony_ci return 1; 158862306a36Sopenharmony_ci if (rate < (src_rate / div)) 158962306a36Sopenharmony_ci div++; 159062306a36Sopenharmony_ci return div; 159162306a36Sopenharmony_ci} 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_cistatic long round_clock_rate(u8 clock, unsigned long rate) 159462306a36Sopenharmony_ci{ 159562306a36Sopenharmony_ci u32 val; 159662306a36Sopenharmony_ci u32 div; 159762306a36Sopenharmony_ci unsigned long src_rate; 159862306a36Sopenharmony_ci long rounded_rate; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci val = readl(prcmu_base + clk_mgt[clock].offset); 160162306a36Sopenharmony_ci src_rate = clock_source_rate((val | clk_mgt[clock].pllsw), 160262306a36Sopenharmony_ci clk_mgt[clock].branch); 160362306a36Sopenharmony_ci div = clock_divider(src_rate, rate); 160462306a36Sopenharmony_ci if (val & PRCM_CLK_MGT_CLK38) { 160562306a36Sopenharmony_ci if (clk_mgt[clock].clk38div) { 160662306a36Sopenharmony_ci if (div > 2) 160762306a36Sopenharmony_ci div = 2; 160862306a36Sopenharmony_ci } else { 160962306a36Sopenharmony_ci div = 1; 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci } else if ((clock == PRCMU_SGACLK) && (div == 3)) { 161262306a36Sopenharmony_ci u64 r = (src_rate * 10); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci (void)do_div(r, 25); 161562306a36Sopenharmony_ci if (r <= rate) 161662306a36Sopenharmony_ci return (unsigned long)r; 161762306a36Sopenharmony_ci } 161862306a36Sopenharmony_ci rounded_rate = (src_rate / min(div, (u32)31)); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci return rounded_rate; 162162306a36Sopenharmony_ci} 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_cistatic const unsigned long db8500_armss_freqs[] = { 162462306a36Sopenharmony_ci 199680000, 162562306a36Sopenharmony_ci 399360000, 162662306a36Sopenharmony_ci 798720000, 162762306a36Sopenharmony_ci 998400000 162862306a36Sopenharmony_ci}; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci/* The DB8520 has slightly higher ARMSS max frequency */ 163162306a36Sopenharmony_cistatic const unsigned long db8520_armss_freqs[] = { 163262306a36Sopenharmony_ci 199680000, 163362306a36Sopenharmony_ci 399360000, 163462306a36Sopenharmony_ci 798720000, 163562306a36Sopenharmony_ci 1152000000 163662306a36Sopenharmony_ci}; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_cistatic long round_armss_rate(unsigned long rate) 163962306a36Sopenharmony_ci{ 164062306a36Sopenharmony_ci unsigned long freq = 0; 164162306a36Sopenharmony_ci const unsigned long *freqs; 164262306a36Sopenharmony_ci int nfreqs; 164362306a36Sopenharmony_ci int i; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci if (fw_info.version.project == PRCMU_FW_PROJECT_U8520) { 164662306a36Sopenharmony_ci freqs = db8520_armss_freqs; 164762306a36Sopenharmony_ci nfreqs = ARRAY_SIZE(db8520_armss_freqs); 164862306a36Sopenharmony_ci } else { 164962306a36Sopenharmony_ci freqs = db8500_armss_freqs; 165062306a36Sopenharmony_ci nfreqs = ARRAY_SIZE(db8500_armss_freqs); 165162306a36Sopenharmony_ci } 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci /* Find the corresponding arm opp from the cpufreq table. */ 165462306a36Sopenharmony_ci for (i = 0; i < nfreqs; i++) { 165562306a36Sopenharmony_ci freq = freqs[i]; 165662306a36Sopenharmony_ci if (rate <= freq) 165762306a36Sopenharmony_ci break; 165862306a36Sopenharmony_ci } 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci /* Return the last valid value, even if a match was not found. */ 166162306a36Sopenharmony_ci return freq; 166262306a36Sopenharmony_ci} 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci#define MIN_PLL_VCO_RATE 600000000ULL 166562306a36Sopenharmony_ci#define MAX_PLL_VCO_RATE 1680640000ULL 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_cistatic long round_plldsi_rate(unsigned long rate) 166862306a36Sopenharmony_ci{ 166962306a36Sopenharmony_ci long rounded_rate = 0; 167062306a36Sopenharmony_ci unsigned long src_rate; 167162306a36Sopenharmony_ci unsigned long rem; 167262306a36Sopenharmony_ci u32 r; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci src_rate = clock_rate(PRCMU_HDMICLK); 167562306a36Sopenharmony_ci rem = rate; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci for (r = 7; (rem > 0) && (r > 0); r--) { 167862306a36Sopenharmony_ci u64 d; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci d = (r * rate); 168162306a36Sopenharmony_ci (void)do_div(d, src_rate); 168262306a36Sopenharmony_ci if (d < 6) 168362306a36Sopenharmony_ci d = 6; 168462306a36Sopenharmony_ci else if (d > 255) 168562306a36Sopenharmony_ci d = 255; 168662306a36Sopenharmony_ci d *= src_rate; 168762306a36Sopenharmony_ci if (((2 * d) < (r * MIN_PLL_VCO_RATE)) || 168862306a36Sopenharmony_ci ((r * MAX_PLL_VCO_RATE) < (2 * d))) 168962306a36Sopenharmony_ci continue; 169062306a36Sopenharmony_ci (void)do_div(d, r); 169162306a36Sopenharmony_ci if (rate < d) { 169262306a36Sopenharmony_ci if (rounded_rate == 0) 169362306a36Sopenharmony_ci rounded_rate = (long)d; 169462306a36Sopenharmony_ci break; 169562306a36Sopenharmony_ci } 169662306a36Sopenharmony_ci if ((rate - d) < rem) { 169762306a36Sopenharmony_ci rem = (rate - d); 169862306a36Sopenharmony_ci rounded_rate = (long)d; 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci } 170162306a36Sopenharmony_ci return rounded_rate; 170262306a36Sopenharmony_ci} 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_cistatic long round_dsiclk_rate(unsigned long rate) 170562306a36Sopenharmony_ci{ 170662306a36Sopenharmony_ci u32 div; 170762306a36Sopenharmony_ci unsigned long src_rate; 170862306a36Sopenharmony_ci long rounded_rate; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci src_rate = pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), 171162306a36Sopenharmony_ci PLL_RAW); 171262306a36Sopenharmony_ci div = clock_divider(src_rate, rate); 171362306a36Sopenharmony_ci rounded_rate = (src_rate / ((div > 2) ? 4 : div)); 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci return rounded_rate; 171662306a36Sopenharmony_ci} 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_cistatic long round_dsiescclk_rate(unsigned long rate) 171962306a36Sopenharmony_ci{ 172062306a36Sopenharmony_ci u32 div; 172162306a36Sopenharmony_ci unsigned long src_rate; 172262306a36Sopenharmony_ci long rounded_rate; 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci src_rate = clock_rate(PRCMU_TVCLK); 172562306a36Sopenharmony_ci div = clock_divider(src_rate, rate); 172662306a36Sopenharmony_ci rounded_rate = (src_rate / min(div, (u32)255)); 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci return rounded_rate; 172962306a36Sopenharmony_ci} 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_cilong prcmu_round_clock_rate(u8 clock, unsigned long rate) 173262306a36Sopenharmony_ci{ 173362306a36Sopenharmony_ci if (clock < PRCMU_NUM_REG_CLOCKS) 173462306a36Sopenharmony_ci return round_clock_rate(clock, rate); 173562306a36Sopenharmony_ci else if (clock == PRCMU_ARMSS) 173662306a36Sopenharmony_ci return round_armss_rate(rate); 173762306a36Sopenharmony_ci else if (clock == PRCMU_PLLDSI) 173862306a36Sopenharmony_ci return round_plldsi_rate(rate); 173962306a36Sopenharmony_ci else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) 174062306a36Sopenharmony_ci return round_dsiclk_rate(rate); 174162306a36Sopenharmony_ci else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) 174262306a36Sopenharmony_ci return round_dsiescclk_rate(rate); 174362306a36Sopenharmony_ci else 174462306a36Sopenharmony_ci return (long)prcmu_clock_rate(clock); 174562306a36Sopenharmony_ci} 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_cistatic void set_clock_rate(u8 clock, unsigned long rate) 174862306a36Sopenharmony_ci{ 174962306a36Sopenharmony_ci u32 val; 175062306a36Sopenharmony_ci u32 div; 175162306a36Sopenharmony_ci unsigned long src_rate; 175262306a36Sopenharmony_ci unsigned long flags; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci spin_lock_irqsave(&clk_mgt_lock, flags); 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci /* Grab the HW semaphore. */ 175762306a36Sopenharmony_ci while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) 175862306a36Sopenharmony_ci cpu_relax(); 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci val = readl(prcmu_base + clk_mgt[clock].offset); 176162306a36Sopenharmony_ci src_rate = clock_source_rate((val | clk_mgt[clock].pllsw), 176262306a36Sopenharmony_ci clk_mgt[clock].branch); 176362306a36Sopenharmony_ci div = clock_divider(src_rate, rate); 176462306a36Sopenharmony_ci if (val & PRCM_CLK_MGT_CLK38) { 176562306a36Sopenharmony_ci if (clk_mgt[clock].clk38div) { 176662306a36Sopenharmony_ci if (div > 1) 176762306a36Sopenharmony_ci val |= PRCM_CLK_MGT_CLK38DIV; 176862306a36Sopenharmony_ci else 176962306a36Sopenharmony_ci val &= ~PRCM_CLK_MGT_CLK38DIV; 177062306a36Sopenharmony_ci } 177162306a36Sopenharmony_ci } else if (clock == PRCMU_SGACLK) { 177262306a36Sopenharmony_ci val &= ~(PRCM_CLK_MGT_CLKPLLDIV_MASK | 177362306a36Sopenharmony_ci PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN); 177462306a36Sopenharmony_ci if (div == 3) { 177562306a36Sopenharmony_ci u64 r = (src_rate * 10); 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci (void)do_div(r, 25); 177862306a36Sopenharmony_ci if (r <= rate) { 177962306a36Sopenharmony_ci val |= PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN; 178062306a36Sopenharmony_ci div = 0; 178162306a36Sopenharmony_ci } 178262306a36Sopenharmony_ci } 178362306a36Sopenharmony_ci val |= min(div, (u32)31); 178462306a36Sopenharmony_ci } else { 178562306a36Sopenharmony_ci val &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK; 178662306a36Sopenharmony_ci val |= min(div, (u32)31); 178762306a36Sopenharmony_ci } 178862306a36Sopenharmony_ci writel(val, prcmu_base + clk_mgt[clock].offset); 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci /* Release the HW semaphore. */ 179162306a36Sopenharmony_ci writel(0, PRCM_SEM); 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci spin_unlock_irqrestore(&clk_mgt_lock, flags); 179462306a36Sopenharmony_ci} 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_cistatic int set_armss_rate(unsigned long rate) 179762306a36Sopenharmony_ci{ 179862306a36Sopenharmony_ci unsigned long freq; 179962306a36Sopenharmony_ci u8 opps[] = { ARM_EXTCLK, ARM_50_OPP, ARM_100_OPP, ARM_MAX_OPP }; 180062306a36Sopenharmony_ci const unsigned long *freqs; 180162306a36Sopenharmony_ci int nfreqs; 180262306a36Sopenharmony_ci int i; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci if (fw_info.version.project == PRCMU_FW_PROJECT_U8520) { 180562306a36Sopenharmony_ci freqs = db8520_armss_freqs; 180662306a36Sopenharmony_ci nfreqs = ARRAY_SIZE(db8520_armss_freqs); 180762306a36Sopenharmony_ci } else { 180862306a36Sopenharmony_ci freqs = db8500_armss_freqs; 180962306a36Sopenharmony_ci nfreqs = ARRAY_SIZE(db8500_armss_freqs); 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci /* Find the corresponding arm opp from the cpufreq table. */ 181362306a36Sopenharmony_ci for (i = 0; i < nfreqs; i++) { 181462306a36Sopenharmony_ci freq = freqs[i]; 181562306a36Sopenharmony_ci if (rate == freq) 181662306a36Sopenharmony_ci break; 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci if (rate != freq) 182062306a36Sopenharmony_ci return -EINVAL; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci /* Set the new arm opp. */ 182362306a36Sopenharmony_ci pr_debug("SET ARM OPP 0x%02x\n", opps[i]); 182462306a36Sopenharmony_ci return db8500_prcmu_set_arm_opp(opps[i]); 182562306a36Sopenharmony_ci} 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_cistatic int set_plldsi_rate(unsigned long rate) 182862306a36Sopenharmony_ci{ 182962306a36Sopenharmony_ci unsigned long src_rate; 183062306a36Sopenharmony_ci unsigned long rem; 183162306a36Sopenharmony_ci u32 pll_freq = 0; 183262306a36Sopenharmony_ci u32 r; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci src_rate = clock_rate(PRCMU_HDMICLK); 183562306a36Sopenharmony_ci rem = rate; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci for (r = 7; (rem > 0) && (r > 0); r--) { 183862306a36Sopenharmony_ci u64 d; 183962306a36Sopenharmony_ci u64 hwrate; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci d = (r * rate); 184262306a36Sopenharmony_ci (void)do_div(d, src_rate); 184362306a36Sopenharmony_ci if (d < 6) 184462306a36Sopenharmony_ci d = 6; 184562306a36Sopenharmony_ci else if (d > 255) 184662306a36Sopenharmony_ci d = 255; 184762306a36Sopenharmony_ci hwrate = (d * src_rate); 184862306a36Sopenharmony_ci if (((2 * hwrate) < (r * MIN_PLL_VCO_RATE)) || 184962306a36Sopenharmony_ci ((r * MAX_PLL_VCO_RATE) < (2 * hwrate))) 185062306a36Sopenharmony_ci continue; 185162306a36Sopenharmony_ci (void)do_div(hwrate, r); 185262306a36Sopenharmony_ci if (rate < hwrate) { 185362306a36Sopenharmony_ci if (pll_freq == 0) 185462306a36Sopenharmony_ci pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) | 185562306a36Sopenharmony_ci (r << PRCM_PLL_FREQ_R_SHIFT)); 185662306a36Sopenharmony_ci break; 185762306a36Sopenharmony_ci } 185862306a36Sopenharmony_ci if ((rate - hwrate) < rem) { 185962306a36Sopenharmony_ci rem = (rate - hwrate); 186062306a36Sopenharmony_ci pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) | 186162306a36Sopenharmony_ci (r << PRCM_PLL_FREQ_R_SHIFT)); 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci if (pll_freq == 0) 186562306a36Sopenharmony_ci return -EINVAL; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci pll_freq |= (1 << PRCM_PLL_FREQ_N_SHIFT); 186862306a36Sopenharmony_ci writel(pll_freq, PRCM_PLLDSI_FREQ); 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci return 0; 187162306a36Sopenharmony_ci} 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_cistatic void set_dsiclk_rate(u8 n, unsigned long rate) 187462306a36Sopenharmony_ci{ 187562306a36Sopenharmony_ci u32 val; 187662306a36Sopenharmony_ci u32 div; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci div = clock_divider(pll_rate(PRCM_PLLDSI_FREQ, 187962306a36Sopenharmony_ci clock_rate(PRCMU_HDMICLK), PLL_RAW), rate); 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci dsiclk[n].divsel = (div == 1) ? PRCM_DSI_PLLOUT_SEL_PHI : 188262306a36Sopenharmony_ci (div == 2) ? PRCM_DSI_PLLOUT_SEL_PHI_2 : 188362306a36Sopenharmony_ci /* else */ PRCM_DSI_PLLOUT_SEL_PHI_4; 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci val = readl(PRCM_DSI_PLLOUT_SEL); 188662306a36Sopenharmony_ci val &= ~dsiclk[n].divsel_mask; 188762306a36Sopenharmony_ci val |= (dsiclk[n].divsel << dsiclk[n].divsel_shift); 188862306a36Sopenharmony_ci writel(val, PRCM_DSI_PLLOUT_SEL); 188962306a36Sopenharmony_ci} 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_cistatic void set_dsiescclk_rate(u8 n, unsigned long rate) 189262306a36Sopenharmony_ci{ 189362306a36Sopenharmony_ci u32 val; 189462306a36Sopenharmony_ci u32 div; 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci div = clock_divider(clock_rate(PRCMU_TVCLK), rate); 189762306a36Sopenharmony_ci val = readl(PRCM_DSITVCLK_DIV); 189862306a36Sopenharmony_ci val &= ~dsiescclk[n].div_mask; 189962306a36Sopenharmony_ci val |= (min(div, (u32)255) << dsiescclk[n].div_shift); 190062306a36Sopenharmony_ci writel(val, PRCM_DSITVCLK_DIV); 190162306a36Sopenharmony_ci} 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ciint prcmu_set_clock_rate(u8 clock, unsigned long rate) 190462306a36Sopenharmony_ci{ 190562306a36Sopenharmony_ci if (clock < PRCMU_NUM_REG_CLOCKS) 190662306a36Sopenharmony_ci set_clock_rate(clock, rate); 190762306a36Sopenharmony_ci else if (clock == PRCMU_ARMSS) 190862306a36Sopenharmony_ci return set_armss_rate(rate); 190962306a36Sopenharmony_ci else if (clock == PRCMU_PLLDSI) 191062306a36Sopenharmony_ci return set_plldsi_rate(rate); 191162306a36Sopenharmony_ci else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) 191262306a36Sopenharmony_ci set_dsiclk_rate((clock - PRCMU_DSI0CLK), rate); 191362306a36Sopenharmony_ci else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) 191462306a36Sopenharmony_ci set_dsiescclk_rate((clock - PRCMU_DSI0ESCCLK), rate); 191562306a36Sopenharmony_ci return 0; 191662306a36Sopenharmony_ci} 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ciint db8500_prcmu_config_esram0_deep_sleep(u8 state) 191962306a36Sopenharmony_ci{ 192062306a36Sopenharmony_ci if ((state > ESRAM0_DEEP_SLEEP_STATE_RET) || 192162306a36Sopenharmony_ci (state < ESRAM0_DEEP_SLEEP_STATE_OFF)) 192262306a36Sopenharmony_ci return -EINVAL; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci mutex_lock(&mb4_transfer.lock); 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) 192762306a36Sopenharmony_ci cpu_relax(); 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci writeb(MB4H_MEM_ST, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4)); 193062306a36Sopenharmony_ci writeb(((DDR_PWR_STATE_OFFHIGHLAT << 4) | DDR_PWR_STATE_ON), 193162306a36Sopenharmony_ci (tcdm_base + PRCM_REQ_MB4_DDR_ST_AP_SLEEP_IDLE)); 193262306a36Sopenharmony_ci writeb(DDR_PWR_STATE_ON, 193362306a36Sopenharmony_ci (tcdm_base + PRCM_REQ_MB4_DDR_ST_AP_DEEP_IDLE)); 193462306a36Sopenharmony_ci writeb(state, (tcdm_base + PRCM_REQ_MB4_ESRAM0_ST)); 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET); 193762306a36Sopenharmony_ci wait_for_completion(&mb4_transfer.work); 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci mutex_unlock(&mb4_transfer.lock); 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci return 0; 194262306a36Sopenharmony_ci} 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ciint db8500_prcmu_config_hotdog(u8 threshold) 194562306a36Sopenharmony_ci{ 194662306a36Sopenharmony_ci mutex_lock(&mb4_transfer.lock); 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) 194962306a36Sopenharmony_ci cpu_relax(); 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci writeb(threshold, (tcdm_base + PRCM_REQ_MB4_HOTDOG_THRESHOLD)); 195262306a36Sopenharmony_ci writeb(MB4H_HOTDOG, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4)); 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET); 195562306a36Sopenharmony_ci wait_for_completion(&mb4_transfer.work); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci mutex_unlock(&mb4_transfer.lock); 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci return 0; 196062306a36Sopenharmony_ci} 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ciint db8500_prcmu_config_hotmon(u8 low, u8 high) 196362306a36Sopenharmony_ci{ 196462306a36Sopenharmony_ci mutex_lock(&mb4_transfer.lock); 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) 196762306a36Sopenharmony_ci cpu_relax(); 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci writeb(low, (tcdm_base + PRCM_REQ_MB4_HOTMON_LOW)); 197062306a36Sopenharmony_ci writeb(high, (tcdm_base + PRCM_REQ_MB4_HOTMON_HIGH)); 197162306a36Sopenharmony_ci writeb((HOTMON_CONFIG_LOW | HOTMON_CONFIG_HIGH), 197262306a36Sopenharmony_ci (tcdm_base + PRCM_REQ_MB4_HOTMON_CONFIG)); 197362306a36Sopenharmony_ci writeb(MB4H_HOTMON, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4)); 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET); 197662306a36Sopenharmony_ci wait_for_completion(&mb4_transfer.work); 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci mutex_unlock(&mb4_transfer.lock); 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci return 0; 198162306a36Sopenharmony_ci} 198262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(db8500_prcmu_config_hotmon); 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_cistatic int config_hot_period(u16 val) 198562306a36Sopenharmony_ci{ 198662306a36Sopenharmony_ci mutex_lock(&mb4_transfer.lock); 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) 198962306a36Sopenharmony_ci cpu_relax(); 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci writew(val, (tcdm_base + PRCM_REQ_MB4_HOT_PERIOD)); 199262306a36Sopenharmony_ci writeb(MB4H_HOT_PERIOD, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4)); 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET); 199562306a36Sopenharmony_ci wait_for_completion(&mb4_transfer.work); 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci mutex_unlock(&mb4_transfer.lock); 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci return 0; 200062306a36Sopenharmony_ci} 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ciint db8500_prcmu_start_temp_sense(u16 cycles32k) 200362306a36Sopenharmony_ci{ 200462306a36Sopenharmony_ci if (cycles32k == 0xFFFF) 200562306a36Sopenharmony_ci return -EINVAL; 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci return config_hot_period(cycles32k); 200862306a36Sopenharmony_ci} 200962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(db8500_prcmu_start_temp_sense); 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ciint db8500_prcmu_stop_temp_sense(void) 201262306a36Sopenharmony_ci{ 201362306a36Sopenharmony_ci return config_hot_period(0xFFFF); 201462306a36Sopenharmony_ci} 201562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(db8500_prcmu_stop_temp_sense); 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_cistatic int prcmu_a9wdog(u8 cmd, u8 d0, u8 d1, u8 d2, u8 d3) 201862306a36Sopenharmony_ci{ 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci mutex_lock(&mb4_transfer.lock); 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) 202362306a36Sopenharmony_ci cpu_relax(); 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci writeb(d0, (tcdm_base + PRCM_REQ_MB4_A9WDOG_0)); 202662306a36Sopenharmony_ci writeb(d1, (tcdm_base + PRCM_REQ_MB4_A9WDOG_1)); 202762306a36Sopenharmony_ci writeb(d2, (tcdm_base + PRCM_REQ_MB4_A9WDOG_2)); 202862306a36Sopenharmony_ci writeb(d3, (tcdm_base + PRCM_REQ_MB4_A9WDOG_3)); 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci writeb(cmd, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4)); 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET); 203362306a36Sopenharmony_ci wait_for_completion(&mb4_transfer.work); 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci mutex_unlock(&mb4_transfer.lock); 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci return 0; 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci} 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ciint db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off) 204262306a36Sopenharmony_ci{ 204362306a36Sopenharmony_ci BUG_ON(num == 0 || num > 0xf); 204462306a36Sopenharmony_ci return prcmu_a9wdog(MB4H_A9WDOG_CONF, num, 0, 0, 204562306a36Sopenharmony_ci sleep_auto_off ? A9WDOG_AUTO_OFF_EN : 204662306a36Sopenharmony_ci A9WDOG_AUTO_OFF_DIS); 204762306a36Sopenharmony_ci} 204862306a36Sopenharmony_ciEXPORT_SYMBOL(db8500_prcmu_config_a9wdog); 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ciint db8500_prcmu_enable_a9wdog(u8 id) 205162306a36Sopenharmony_ci{ 205262306a36Sopenharmony_ci return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0); 205362306a36Sopenharmony_ci} 205462306a36Sopenharmony_ciEXPORT_SYMBOL(db8500_prcmu_enable_a9wdog); 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ciint db8500_prcmu_disable_a9wdog(u8 id) 205762306a36Sopenharmony_ci{ 205862306a36Sopenharmony_ci return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0); 205962306a36Sopenharmony_ci} 206062306a36Sopenharmony_ciEXPORT_SYMBOL(db8500_prcmu_disable_a9wdog); 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ciint db8500_prcmu_kick_a9wdog(u8 id) 206362306a36Sopenharmony_ci{ 206462306a36Sopenharmony_ci return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0); 206562306a36Sopenharmony_ci} 206662306a36Sopenharmony_ciEXPORT_SYMBOL(db8500_prcmu_kick_a9wdog); 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci/* 206962306a36Sopenharmony_ci * timeout is 28 bit, in ms. 207062306a36Sopenharmony_ci */ 207162306a36Sopenharmony_ciint db8500_prcmu_load_a9wdog(u8 id, u32 timeout) 207262306a36Sopenharmony_ci{ 207362306a36Sopenharmony_ci return prcmu_a9wdog(MB4H_A9WDOG_LOAD, 207462306a36Sopenharmony_ci (id & A9WDOG_ID_MASK) | 207562306a36Sopenharmony_ci /* 207662306a36Sopenharmony_ci * Put the lowest 28 bits of timeout at 207762306a36Sopenharmony_ci * offset 4. Four first bits are used for id. 207862306a36Sopenharmony_ci */ 207962306a36Sopenharmony_ci (u8)((timeout << 4) & 0xf0), 208062306a36Sopenharmony_ci (u8)((timeout >> 4) & 0xff), 208162306a36Sopenharmony_ci (u8)((timeout >> 12) & 0xff), 208262306a36Sopenharmony_ci (u8)((timeout >> 20) & 0xff)); 208362306a36Sopenharmony_ci} 208462306a36Sopenharmony_ciEXPORT_SYMBOL(db8500_prcmu_load_a9wdog); 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci/** 208762306a36Sopenharmony_ci * prcmu_abb_read() - Read register value(s) from the ABB. 208862306a36Sopenharmony_ci * @slave: The I2C slave address. 208962306a36Sopenharmony_ci * @reg: The (start) register address. 209062306a36Sopenharmony_ci * @value: The read out value(s). 209162306a36Sopenharmony_ci * @size: The number of registers to read. 209262306a36Sopenharmony_ci * 209362306a36Sopenharmony_ci * Reads register value(s) from the ABB. 209462306a36Sopenharmony_ci * @size has to be 1 for the current firmware version. 209562306a36Sopenharmony_ci */ 209662306a36Sopenharmony_ciint prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) 209762306a36Sopenharmony_ci{ 209862306a36Sopenharmony_ci int r; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci if (size != 1) 210162306a36Sopenharmony_ci return -EINVAL; 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci mutex_lock(&mb5_transfer.lock); 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) 210662306a36Sopenharmony_ci cpu_relax(); 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci writeb(0, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB5)); 210962306a36Sopenharmony_ci writeb(PRCMU_I2C_READ(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP)); 211062306a36Sopenharmony_ci writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS)); 211162306a36Sopenharmony_ci writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG)); 211262306a36Sopenharmony_ci writeb(0, (tcdm_base + PRCM_REQ_MB5_I2C_VAL)); 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET); 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci if (!wait_for_completion_timeout(&mb5_transfer.work, 211762306a36Sopenharmony_ci msecs_to_jiffies(20000))) { 211862306a36Sopenharmony_ci pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n", 211962306a36Sopenharmony_ci __func__); 212062306a36Sopenharmony_ci r = -EIO; 212162306a36Sopenharmony_ci } else { 212262306a36Sopenharmony_ci r = ((mb5_transfer.ack.status == I2C_RD_OK) ? 0 : -EIO); 212362306a36Sopenharmony_ci } 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci if (!r) 212662306a36Sopenharmony_ci *value = mb5_transfer.ack.value; 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci mutex_unlock(&mb5_transfer.lock); 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci return r; 213162306a36Sopenharmony_ci} 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci/** 213462306a36Sopenharmony_ci * prcmu_abb_write_masked() - Write masked register value(s) to the ABB. 213562306a36Sopenharmony_ci * @slave: The I2C slave address. 213662306a36Sopenharmony_ci * @reg: The (start) register address. 213762306a36Sopenharmony_ci * @value: The value(s) to write. 213862306a36Sopenharmony_ci * @mask: The mask(s) to use. 213962306a36Sopenharmony_ci * @size: The number of registers to write. 214062306a36Sopenharmony_ci * 214162306a36Sopenharmony_ci * Writes masked register value(s) to the ABB. 214262306a36Sopenharmony_ci * For each @value, only the bits set to 1 in the corresponding @mask 214362306a36Sopenharmony_ci * will be written. The other bits are not changed. 214462306a36Sopenharmony_ci * @size has to be 1 for the current firmware version. 214562306a36Sopenharmony_ci */ 214662306a36Sopenharmony_ciint prcmu_abb_write_masked(u8 slave, u8 reg, u8 *value, u8 *mask, u8 size) 214762306a36Sopenharmony_ci{ 214862306a36Sopenharmony_ci int r; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci if (size != 1) 215162306a36Sopenharmony_ci return -EINVAL; 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci mutex_lock(&mb5_transfer.lock); 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) 215662306a36Sopenharmony_ci cpu_relax(); 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci writeb(~*mask, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB5)); 215962306a36Sopenharmony_ci writeb(PRCMU_I2C_WRITE(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP)); 216062306a36Sopenharmony_ci writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS)); 216162306a36Sopenharmony_ci writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG)); 216262306a36Sopenharmony_ci writeb(*value, (tcdm_base + PRCM_REQ_MB5_I2C_VAL)); 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET); 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci if (!wait_for_completion_timeout(&mb5_transfer.work, 216762306a36Sopenharmony_ci msecs_to_jiffies(20000))) { 216862306a36Sopenharmony_ci pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n", 216962306a36Sopenharmony_ci __func__); 217062306a36Sopenharmony_ci r = -EIO; 217162306a36Sopenharmony_ci } else { 217262306a36Sopenharmony_ci r = ((mb5_transfer.ack.status == I2C_WR_OK) ? 0 : -EIO); 217362306a36Sopenharmony_ci } 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci mutex_unlock(&mb5_transfer.lock); 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci return r; 217862306a36Sopenharmony_ci} 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci/** 218162306a36Sopenharmony_ci * prcmu_abb_write() - Write register value(s) to the ABB. 218262306a36Sopenharmony_ci * @slave: The I2C slave address. 218362306a36Sopenharmony_ci * @reg: The (start) register address. 218462306a36Sopenharmony_ci * @value: The value(s) to write. 218562306a36Sopenharmony_ci * @size: The number of registers to write. 218662306a36Sopenharmony_ci * 218762306a36Sopenharmony_ci * Writes register value(s) to the ABB. 218862306a36Sopenharmony_ci * @size has to be 1 for the current firmware version. 218962306a36Sopenharmony_ci */ 219062306a36Sopenharmony_ciint prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) 219162306a36Sopenharmony_ci{ 219262306a36Sopenharmony_ci u8 mask = ~0; 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci return prcmu_abb_write_masked(slave, reg, value, &mask, size); 219562306a36Sopenharmony_ci} 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci/** 219862306a36Sopenharmony_ci * prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem 219962306a36Sopenharmony_ci */ 220062306a36Sopenharmony_ciint prcmu_ac_wake_req(void) 220162306a36Sopenharmony_ci{ 220262306a36Sopenharmony_ci u32 val; 220362306a36Sopenharmony_ci int ret = 0; 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci mutex_lock(&mb0_transfer.ac_wake_lock); 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci val = readl(PRCM_HOSTACCESS_REQ); 220862306a36Sopenharmony_ci if (val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ) 220962306a36Sopenharmony_ci goto unlock_and_return; 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci atomic_set(&ac_wake_req_state, 1); 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci /* 221462306a36Sopenharmony_ci * Force Modem Wake-up before hostaccess_req ping-pong. 221562306a36Sopenharmony_ci * It prevents Modem to enter in Sleep while acking the hostaccess 221662306a36Sopenharmony_ci * request. The 31us delay has been calculated by HWI. 221762306a36Sopenharmony_ci */ 221862306a36Sopenharmony_ci val |= PRCM_HOSTACCESS_REQ_WAKE_REQ; 221962306a36Sopenharmony_ci writel(val, PRCM_HOSTACCESS_REQ); 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci udelay(31); 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci val |= PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ; 222462306a36Sopenharmony_ci writel(val, PRCM_HOSTACCESS_REQ); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, 222762306a36Sopenharmony_ci msecs_to_jiffies(5000))) { 222862306a36Sopenharmony_ci pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n", 222962306a36Sopenharmony_ci __func__); 223062306a36Sopenharmony_ci ret = -EFAULT; 223162306a36Sopenharmony_ci } 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ciunlock_and_return: 223462306a36Sopenharmony_ci mutex_unlock(&mb0_transfer.ac_wake_lock); 223562306a36Sopenharmony_ci return ret; 223662306a36Sopenharmony_ci} 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci/** 223962306a36Sopenharmony_ci * prcmu_ac_sleep_req - called when ARM no longer needs to talk to modem 224062306a36Sopenharmony_ci */ 224162306a36Sopenharmony_civoid prcmu_ac_sleep_req(void) 224262306a36Sopenharmony_ci{ 224362306a36Sopenharmony_ci u32 val; 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci mutex_lock(&mb0_transfer.ac_wake_lock); 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci val = readl(PRCM_HOSTACCESS_REQ); 224862306a36Sopenharmony_ci if (!(val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ)) 224962306a36Sopenharmony_ci goto unlock_and_return; 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci writel((val & ~PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), 225262306a36Sopenharmony_ci PRCM_HOSTACCESS_REQ); 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, 225562306a36Sopenharmony_ci msecs_to_jiffies(5000))) { 225662306a36Sopenharmony_ci pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n", 225762306a36Sopenharmony_ci __func__); 225862306a36Sopenharmony_ci } 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci atomic_set(&ac_wake_req_state, 0); 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ciunlock_and_return: 226362306a36Sopenharmony_ci mutex_unlock(&mb0_transfer.ac_wake_lock); 226462306a36Sopenharmony_ci} 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_cibool db8500_prcmu_is_ac_wake_requested(void) 226762306a36Sopenharmony_ci{ 226862306a36Sopenharmony_ci return (atomic_read(&ac_wake_req_state) != 0); 226962306a36Sopenharmony_ci} 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci/** 227262306a36Sopenharmony_ci * db8500_prcmu_system_reset - System reset 227362306a36Sopenharmony_ci * 227462306a36Sopenharmony_ci * Saves the reset reason code and then sets the APE_SOFTRST register which 227562306a36Sopenharmony_ci * fires interrupt to fw 227662306a36Sopenharmony_ci * 227762306a36Sopenharmony_ci * @reset_code: The reason for system reset 227862306a36Sopenharmony_ci */ 227962306a36Sopenharmony_civoid db8500_prcmu_system_reset(u16 reset_code) 228062306a36Sopenharmony_ci{ 228162306a36Sopenharmony_ci writew(reset_code, (tcdm_base + PRCM_SW_RST_REASON)); 228262306a36Sopenharmony_ci writel(1, PRCM_APE_SOFTRST); 228362306a36Sopenharmony_ci} 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci/** 228662306a36Sopenharmony_ci * db8500_prcmu_get_reset_code - Retrieve SW reset reason code 228762306a36Sopenharmony_ci * 228862306a36Sopenharmony_ci * Retrieves the reset reason code stored by prcmu_system_reset() before 228962306a36Sopenharmony_ci * last restart. 229062306a36Sopenharmony_ci */ 229162306a36Sopenharmony_ciu16 db8500_prcmu_get_reset_code(void) 229262306a36Sopenharmony_ci{ 229362306a36Sopenharmony_ci return readw(tcdm_base + PRCM_SW_RST_REASON); 229462306a36Sopenharmony_ci} 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci/** 229762306a36Sopenharmony_ci * db8500_prcmu_modem_reset - ask the PRCMU to reset modem 229862306a36Sopenharmony_ci */ 229962306a36Sopenharmony_civoid db8500_prcmu_modem_reset(void) 230062306a36Sopenharmony_ci{ 230162306a36Sopenharmony_ci mutex_lock(&mb1_transfer.lock); 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) 230462306a36Sopenharmony_ci cpu_relax(); 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci writeb(MB1H_RESET_MODEM, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); 230762306a36Sopenharmony_ci writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); 230862306a36Sopenharmony_ci wait_for_completion(&mb1_transfer.work); 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci /* 231162306a36Sopenharmony_ci * No need to check return from PRCMU as modem should go in reset state 231262306a36Sopenharmony_ci * This state is already managed by upper layer 231362306a36Sopenharmony_ci */ 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci mutex_unlock(&mb1_transfer.lock); 231662306a36Sopenharmony_ci} 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_cistatic void ack_dbb_wakeup(void) 231962306a36Sopenharmony_ci{ 232062306a36Sopenharmony_ci unsigned long flags; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci spin_lock_irqsave(&mb0_transfer.lock, flags); 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0)) 232562306a36Sopenharmony_ci cpu_relax(); 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci writeb(MB0H_READ_WAKEUP_ACK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0)); 232862306a36Sopenharmony_ci writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET); 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci spin_unlock_irqrestore(&mb0_transfer.lock, flags); 233162306a36Sopenharmony_ci} 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_cistatic inline void print_unknown_header_warning(u8 n, u8 header) 233462306a36Sopenharmony_ci{ 233562306a36Sopenharmony_ci pr_warn("prcmu: Unknown message header (%d) in mailbox %d\n", 233662306a36Sopenharmony_ci header, n); 233762306a36Sopenharmony_ci} 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_cistatic bool read_mailbox_0(void) 234062306a36Sopenharmony_ci{ 234162306a36Sopenharmony_ci bool r; 234262306a36Sopenharmony_ci u32 ev; 234362306a36Sopenharmony_ci unsigned int n; 234462306a36Sopenharmony_ci u8 header; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci header = readb(tcdm_base + PRCM_MBOX_HEADER_ACK_MB0); 234762306a36Sopenharmony_ci switch (header) { 234862306a36Sopenharmony_ci case MB0H_WAKEUP_EXE: 234962306a36Sopenharmony_ci case MB0H_WAKEUP_SLEEP: 235062306a36Sopenharmony_ci if (readb(tcdm_base + PRCM_ACK_MB0_READ_POINTER) & 1) 235162306a36Sopenharmony_ci ev = readl(tcdm_base + PRCM_ACK_MB0_WAKEUP_1_8500); 235262306a36Sopenharmony_ci else 235362306a36Sopenharmony_ci ev = readl(tcdm_base + PRCM_ACK_MB0_WAKEUP_0_8500); 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci if (ev & (WAKEUP_BIT_AC_WAKE_ACK | WAKEUP_BIT_AC_SLEEP_ACK)) 235662306a36Sopenharmony_ci complete(&mb0_transfer.ac_wake_work); 235762306a36Sopenharmony_ci if (ev & WAKEUP_BIT_SYSCLK_OK) 235862306a36Sopenharmony_ci complete(&mb3_transfer.sysclk_work); 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_ci ev &= mb0_transfer.req.dbb_irqs; 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci for (n = 0; n < NUM_PRCMU_WAKEUPS; n++) { 236362306a36Sopenharmony_ci if (ev & prcmu_irq_bit[n]) 236462306a36Sopenharmony_ci generic_handle_domain_irq(db8500_irq_domain, n); 236562306a36Sopenharmony_ci } 236662306a36Sopenharmony_ci r = true; 236762306a36Sopenharmony_ci break; 236862306a36Sopenharmony_ci default: 236962306a36Sopenharmony_ci print_unknown_header_warning(0, header); 237062306a36Sopenharmony_ci r = false; 237162306a36Sopenharmony_ci break; 237262306a36Sopenharmony_ci } 237362306a36Sopenharmony_ci writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR); 237462306a36Sopenharmony_ci return r; 237562306a36Sopenharmony_ci} 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_cistatic bool read_mailbox_1(void) 237862306a36Sopenharmony_ci{ 237962306a36Sopenharmony_ci mb1_transfer.ack.header = readb(tcdm_base + PRCM_MBOX_HEADER_REQ_MB1); 238062306a36Sopenharmony_ci mb1_transfer.ack.arm_opp = readb(tcdm_base + 238162306a36Sopenharmony_ci PRCM_ACK_MB1_CURRENT_ARM_OPP); 238262306a36Sopenharmony_ci mb1_transfer.ack.ape_opp = readb(tcdm_base + 238362306a36Sopenharmony_ci PRCM_ACK_MB1_CURRENT_APE_OPP); 238462306a36Sopenharmony_ci mb1_transfer.ack.ape_voltage_status = readb(tcdm_base + 238562306a36Sopenharmony_ci PRCM_ACK_MB1_APE_VOLTAGE_STATUS); 238662306a36Sopenharmony_ci writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR); 238762306a36Sopenharmony_ci complete(&mb1_transfer.work); 238862306a36Sopenharmony_ci return false; 238962306a36Sopenharmony_ci} 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_cistatic bool read_mailbox_2(void) 239262306a36Sopenharmony_ci{ 239362306a36Sopenharmony_ci mb2_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB2_DPS_STATUS); 239462306a36Sopenharmony_ci writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR); 239562306a36Sopenharmony_ci complete(&mb2_transfer.work); 239662306a36Sopenharmony_ci return false; 239762306a36Sopenharmony_ci} 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_cistatic bool read_mailbox_3(void) 240062306a36Sopenharmony_ci{ 240162306a36Sopenharmony_ci writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR); 240262306a36Sopenharmony_ci return false; 240362306a36Sopenharmony_ci} 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_cistatic bool read_mailbox_4(void) 240662306a36Sopenharmony_ci{ 240762306a36Sopenharmony_ci u8 header; 240862306a36Sopenharmony_ci bool do_complete = true; 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci header = readb(tcdm_base + PRCM_MBOX_HEADER_REQ_MB4); 241162306a36Sopenharmony_ci switch (header) { 241262306a36Sopenharmony_ci case MB4H_MEM_ST: 241362306a36Sopenharmony_ci case MB4H_HOTDOG: 241462306a36Sopenharmony_ci case MB4H_HOTMON: 241562306a36Sopenharmony_ci case MB4H_HOT_PERIOD: 241662306a36Sopenharmony_ci case MB4H_A9WDOG_CONF: 241762306a36Sopenharmony_ci case MB4H_A9WDOG_EN: 241862306a36Sopenharmony_ci case MB4H_A9WDOG_DIS: 241962306a36Sopenharmony_ci case MB4H_A9WDOG_LOAD: 242062306a36Sopenharmony_ci case MB4H_A9WDOG_KICK: 242162306a36Sopenharmony_ci break; 242262306a36Sopenharmony_ci default: 242362306a36Sopenharmony_ci print_unknown_header_warning(4, header); 242462306a36Sopenharmony_ci do_complete = false; 242562306a36Sopenharmony_ci break; 242662306a36Sopenharmony_ci } 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR); 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci if (do_complete) 243162306a36Sopenharmony_ci complete(&mb4_transfer.work); 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_ci return false; 243462306a36Sopenharmony_ci} 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_cistatic bool read_mailbox_5(void) 243762306a36Sopenharmony_ci{ 243862306a36Sopenharmony_ci mb5_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB5_I2C_STATUS); 243962306a36Sopenharmony_ci mb5_transfer.ack.value = readb(tcdm_base + PRCM_ACK_MB5_I2C_VAL); 244062306a36Sopenharmony_ci writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR); 244162306a36Sopenharmony_ci complete(&mb5_transfer.work); 244262306a36Sopenharmony_ci return false; 244362306a36Sopenharmony_ci} 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_cistatic bool read_mailbox_6(void) 244662306a36Sopenharmony_ci{ 244762306a36Sopenharmony_ci writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR); 244862306a36Sopenharmony_ci return false; 244962306a36Sopenharmony_ci} 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_cistatic bool read_mailbox_7(void) 245262306a36Sopenharmony_ci{ 245362306a36Sopenharmony_ci writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR); 245462306a36Sopenharmony_ci return false; 245562306a36Sopenharmony_ci} 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_cistatic bool (* const read_mailbox[NUM_MB])(void) = { 245862306a36Sopenharmony_ci read_mailbox_0, 245962306a36Sopenharmony_ci read_mailbox_1, 246062306a36Sopenharmony_ci read_mailbox_2, 246162306a36Sopenharmony_ci read_mailbox_3, 246262306a36Sopenharmony_ci read_mailbox_4, 246362306a36Sopenharmony_ci read_mailbox_5, 246462306a36Sopenharmony_ci read_mailbox_6, 246562306a36Sopenharmony_ci read_mailbox_7 246662306a36Sopenharmony_ci}; 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_cistatic irqreturn_t prcmu_irq_handler(int irq, void *data) 246962306a36Sopenharmony_ci{ 247062306a36Sopenharmony_ci u32 bits; 247162306a36Sopenharmony_ci u8 n; 247262306a36Sopenharmony_ci irqreturn_t r; 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci bits = (readl(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS); 247562306a36Sopenharmony_ci if (unlikely(!bits)) 247662306a36Sopenharmony_ci return IRQ_NONE; 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci r = IRQ_HANDLED; 247962306a36Sopenharmony_ci for (n = 0; bits; n++) { 248062306a36Sopenharmony_ci if (bits & MBOX_BIT(n)) { 248162306a36Sopenharmony_ci bits -= MBOX_BIT(n); 248262306a36Sopenharmony_ci if (read_mailbox[n]()) 248362306a36Sopenharmony_ci r = IRQ_WAKE_THREAD; 248462306a36Sopenharmony_ci } 248562306a36Sopenharmony_ci } 248662306a36Sopenharmony_ci return r; 248762306a36Sopenharmony_ci} 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_cistatic irqreturn_t prcmu_irq_thread_fn(int irq, void *data) 249062306a36Sopenharmony_ci{ 249162306a36Sopenharmony_ci ack_dbb_wakeup(); 249262306a36Sopenharmony_ci return IRQ_HANDLED; 249362306a36Sopenharmony_ci} 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_cistatic void prcmu_mask_work(struct work_struct *work) 249662306a36Sopenharmony_ci{ 249762306a36Sopenharmony_ci unsigned long flags; 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci spin_lock_irqsave(&mb0_transfer.lock, flags); 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci config_wakeups(); 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci spin_unlock_irqrestore(&mb0_transfer.lock, flags); 250462306a36Sopenharmony_ci} 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_cistatic void prcmu_irq_mask(struct irq_data *d) 250762306a36Sopenharmony_ci{ 250862306a36Sopenharmony_ci unsigned long flags; 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags); 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_ci mb0_transfer.req.dbb_irqs &= ~prcmu_irq_bit[d->hwirq]; 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags); 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci if (d->irq != IRQ_PRCMU_CA_SLEEP) 251762306a36Sopenharmony_ci schedule_work(&mb0_transfer.mask_work); 251862306a36Sopenharmony_ci} 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_cistatic void prcmu_irq_unmask(struct irq_data *d) 252162306a36Sopenharmony_ci{ 252262306a36Sopenharmony_ci unsigned long flags; 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags); 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci mb0_transfer.req.dbb_irqs |= prcmu_irq_bit[d->hwirq]; 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags); 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci if (d->irq != IRQ_PRCMU_CA_SLEEP) 253162306a36Sopenharmony_ci schedule_work(&mb0_transfer.mask_work); 253262306a36Sopenharmony_ci} 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_cistatic void noop(struct irq_data *d) 253562306a36Sopenharmony_ci{ 253662306a36Sopenharmony_ci} 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_cistatic struct irq_chip prcmu_irq_chip = { 253962306a36Sopenharmony_ci .name = "prcmu", 254062306a36Sopenharmony_ci .irq_disable = prcmu_irq_mask, 254162306a36Sopenharmony_ci .irq_ack = noop, 254262306a36Sopenharmony_ci .irq_mask = prcmu_irq_mask, 254362306a36Sopenharmony_ci .irq_unmask = prcmu_irq_unmask, 254462306a36Sopenharmony_ci}; 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_cistatic char *fw_project_name(u32 project) 254762306a36Sopenharmony_ci{ 254862306a36Sopenharmony_ci switch (project) { 254962306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8500: 255062306a36Sopenharmony_ci return "U8500"; 255162306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8400: 255262306a36Sopenharmony_ci return "U8400"; 255362306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U9500: 255462306a36Sopenharmony_ci return "U9500"; 255562306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8500_MBB: 255662306a36Sopenharmony_ci return "U8500 MBB"; 255762306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8500_C1: 255862306a36Sopenharmony_ci return "U8500 C1"; 255962306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8500_C2: 256062306a36Sopenharmony_ci return "U8500 C2"; 256162306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8500_C3: 256262306a36Sopenharmony_ci return "U8500 C3"; 256362306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8500_C4: 256462306a36Sopenharmony_ci return "U8500 C4"; 256562306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U9500_MBL: 256662306a36Sopenharmony_ci return "U9500 MBL"; 256762306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8500_SSG1: 256862306a36Sopenharmony_ci return "U8500 Samsung 1"; 256962306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8500_MBL2: 257062306a36Sopenharmony_ci return "U8500 MBL2"; 257162306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8520: 257262306a36Sopenharmony_ci return "U8520 MBL"; 257362306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8420: 257462306a36Sopenharmony_ci return "U8420"; 257562306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8500_SSG2: 257662306a36Sopenharmony_ci return "U8500 Samsung 2"; 257762306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U8420_SYSCLK: 257862306a36Sopenharmony_ci return "U8420-sysclk"; 257962306a36Sopenharmony_ci case PRCMU_FW_PROJECT_U9540: 258062306a36Sopenharmony_ci return "U9540"; 258162306a36Sopenharmony_ci case PRCMU_FW_PROJECT_A9420: 258262306a36Sopenharmony_ci return "A9420"; 258362306a36Sopenharmony_ci case PRCMU_FW_PROJECT_L8540: 258462306a36Sopenharmony_ci return "L8540"; 258562306a36Sopenharmony_ci case PRCMU_FW_PROJECT_L8580: 258662306a36Sopenharmony_ci return "L8580"; 258762306a36Sopenharmony_ci default: 258862306a36Sopenharmony_ci return "Unknown"; 258962306a36Sopenharmony_ci } 259062306a36Sopenharmony_ci} 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_cistatic int db8500_irq_map(struct irq_domain *d, unsigned int virq, 259362306a36Sopenharmony_ci irq_hw_number_t hwirq) 259462306a36Sopenharmony_ci{ 259562306a36Sopenharmony_ci irq_set_chip_and_handler(virq, &prcmu_irq_chip, 259662306a36Sopenharmony_ci handle_simple_irq); 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_ci return 0; 259962306a36Sopenharmony_ci} 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_cistatic const struct irq_domain_ops db8500_irq_ops = { 260262306a36Sopenharmony_ci .map = db8500_irq_map, 260362306a36Sopenharmony_ci .xlate = irq_domain_xlate_twocell, 260462306a36Sopenharmony_ci}; 260562306a36Sopenharmony_ci 260662306a36Sopenharmony_cistatic int db8500_irq_init(struct device_node *np) 260762306a36Sopenharmony_ci{ 260862306a36Sopenharmony_ci int i; 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci db8500_irq_domain = irq_domain_add_simple( 261162306a36Sopenharmony_ci np, NUM_PRCMU_WAKEUPS, 0, 261262306a36Sopenharmony_ci &db8500_irq_ops, NULL); 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci if (!db8500_irq_domain) { 261562306a36Sopenharmony_ci pr_err("Failed to create irqdomain\n"); 261662306a36Sopenharmony_ci return -ENOSYS; 261762306a36Sopenharmony_ci } 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci /* All wakeups will be used, so create mappings for all */ 262062306a36Sopenharmony_ci for (i = 0; i < NUM_PRCMU_WAKEUPS; i++) 262162306a36Sopenharmony_ci irq_create_mapping(db8500_irq_domain, i); 262262306a36Sopenharmony_ci 262362306a36Sopenharmony_ci return 0; 262462306a36Sopenharmony_ci} 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_cistatic void dbx500_fw_version_init(struct device_node *np) 262762306a36Sopenharmony_ci{ 262862306a36Sopenharmony_ci void __iomem *tcpm_base; 262962306a36Sopenharmony_ci u32 version; 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci tcpm_base = of_iomap(np, 1); 263262306a36Sopenharmony_ci if (!tcpm_base) { 263362306a36Sopenharmony_ci pr_err("no prcmu tcpm mem region provided\n"); 263462306a36Sopenharmony_ci return; 263562306a36Sopenharmony_ci } 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci version = readl(tcpm_base + DB8500_PRCMU_FW_VERSION_OFFSET); 263862306a36Sopenharmony_ci fw_info.version.project = (version & 0xFF); 263962306a36Sopenharmony_ci fw_info.version.api_version = (version >> 8) & 0xFF; 264062306a36Sopenharmony_ci fw_info.version.func_version = (version >> 16) & 0xFF; 264162306a36Sopenharmony_ci fw_info.version.errata = (version >> 24) & 0xFF; 264262306a36Sopenharmony_ci strncpy(fw_info.version.project_name, 264362306a36Sopenharmony_ci fw_project_name(fw_info.version.project), 264462306a36Sopenharmony_ci PRCMU_FW_PROJECT_NAME_LEN); 264562306a36Sopenharmony_ci fw_info.valid = true; 264662306a36Sopenharmony_ci pr_info("PRCMU firmware: %s(%d), version %d.%d.%d\n", 264762306a36Sopenharmony_ci fw_info.version.project_name, 264862306a36Sopenharmony_ci fw_info.version.project, 264962306a36Sopenharmony_ci fw_info.version.api_version, 265062306a36Sopenharmony_ci fw_info.version.func_version, 265162306a36Sopenharmony_ci fw_info.version.errata); 265262306a36Sopenharmony_ci iounmap(tcpm_base); 265362306a36Sopenharmony_ci} 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_civoid __init db8500_prcmu_early_init(void) 265662306a36Sopenharmony_ci{ 265762306a36Sopenharmony_ci /* 265862306a36Sopenharmony_ci * This is a temporary remap to bring up the clocks. It is 265962306a36Sopenharmony_ci * subsequently replaces with a real remap. After the merge of 266062306a36Sopenharmony_ci * the mailbox subsystem all of this early code goes away, and the 266162306a36Sopenharmony_ci * clock driver can probe independently. An early initcall will 266262306a36Sopenharmony_ci * still be needed, but it can be diverted into drivers/clk/ux500. 266362306a36Sopenharmony_ci */ 266462306a36Sopenharmony_ci struct device_node *np; 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "stericsson,db8500-prcmu"); 266762306a36Sopenharmony_ci prcmu_base = of_iomap(np, 0); 266862306a36Sopenharmony_ci if (!prcmu_base) { 266962306a36Sopenharmony_ci of_node_put(np); 267062306a36Sopenharmony_ci pr_err("%s: ioremap() of prcmu registers failed!\n", __func__); 267162306a36Sopenharmony_ci return; 267262306a36Sopenharmony_ci } 267362306a36Sopenharmony_ci dbx500_fw_version_init(np); 267462306a36Sopenharmony_ci of_node_put(np); 267562306a36Sopenharmony_ci 267662306a36Sopenharmony_ci spin_lock_init(&mb0_transfer.lock); 267762306a36Sopenharmony_ci spin_lock_init(&mb0_transfer.dbb_irqs_lock); 267862306a36Sopenharmony_ci mutex_init(&mb0_transfer.ac_wake_lock); 267962306a36Sopenharmony_ci init_completion(&mb0_transfer.ac_wake_work); 268062306a36Sopenharmony_ci mutex_init(&mb1_transfer.lock); 268162306a36Sopenharmony_ci init_completion(&mb1_transfer.work); 268262306a36Sopenharmony_ci mb1_transfer.ape_opp = APE_NO_CHANGE; 268362306a36Sopenharmony_ci mutex_init(&mb2_transfer.lock); 268462306a36Sopenharmony_ci init_completion(&mb2_transfer.work); 268562306a36Sopenharmony_ci spin_lock_init(&mb2_transfer.auto_pm_lock); 268662306a36Sopenharmony_ci spin_lock_init(&mb3_transfer.lock); 268762306a36Sopenharmony_ci mutex_init(&mb3_transfer.sysclk_lock); 268862306a36Sopenharmony_ci init_completion(&mb3_transfer.sysclk_work); 268962306a36Sopenharmony_ci mutex_init(&mb4_transfer.lock); 269062306a36Sopenharmony_ci init_completion(&mb4_transfer.work); 269162306a36Sopenharmony_ci mutex_init(&mb5_transfer.lock); 269262306a36Sopenharmony_ci init_completion(&mb5_transfer.work); 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci INIT_WORK(&mb0_transfer.mask_work, prcmu_mask_work); 269562306a36Sopenharmony_ci} 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_cistatic void init_prcm_registers(void) 269862306a36Sopenharmony_ci{ 269962306a36Sopenharmony_ci u32 val; 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci val = readl(PRCM_A9PL_FORCE_CLKEN); 270262306a36Sopenharmony_ci val &= ~(PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN | 270362306a36Sopenharmony_ci PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN); 270462306a36Sopenharmony_ci writel(val, (PRCM_A9PL_FORCE_CLKEN)); 270562306a36Sopenharmony_ci} 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci/* 270862306a36Sopenharmony_ci * Power domain switches (ePODs) modeled as regulators for the DB8500 SoC 270962306a36Sopenharmony_ci */ 271062306a36Sopenharmony_cistatic struct regulator_consumer_supply db8500_vape_consumers[] = { 271162306a36Sopenharmony_ci REGULATOR_SUPPLY("v-ape", NULL), 271262306a36Sopenharmony_ci REGULATOR_SUPPLY("v-i2c", "nmk-i2c.0"), 271362306a36Sopenharmony_ci REGULATOR_SUPPLY("v-i2c", "nmk-i2c.1"), 271462306a36Sopenharmony_ci REGULATOR_SUPPLY("v-i2c", "nmk-i2c.2"), 271562306a36Sopenharmony_ci REGULATOR_SUPPLY("v-i2c", "nmk-i2c.3"), 271662306a36Sopenharmony_ci REGULATOR_SUPPLY("v-i2c", "nmk-i2c.4"), 271762306a36Sopenharmony_ci /* "v-mmc" changed to "vcore" in the mainline kernel */ 271862306a36Sopenharmony_ci REGULATOR_SUPPLY("vcore", "sdi0"), 271962306a36Sopenharmony_ci REGULATOR_SUPPLY("vcore", "sdi1"), 272062306a36Sopenharmony_ci REGULATOR_SUPPLY("vcore", "sdi2"), 272162306a36Sopenharmony_ci REGULATOR_SUPPLY("vcore", "sdi3"), 272262306a36Sopenharmony_ci REGULATOR_SUPPLY("vcore", "sdi4"), 272362306a36Sopenharmony_ci REGULATOR_SUPPLY("v-dma", "dma40.0"), 272462306a36Sopenharmony_ci REGULATOR_SUPPLY("v-ape", "ab8500-usb.0"), 272562306a36Sopenharmony_ci /* "v-uart" changed to "vcore" in the mainline kernel */ 272662306a36Sopenharmony_ci REGULATOR_SUPPLY("vcore", "uart0"), 272762306a36Sopenharmony_ci REGULATOR_SUPPLY("vcore", "uart1"), 272862306a36Sopenharmony_ci REGULATOR_SUPPLY("vcore", "uart2"), 272962306a36Sopenharmony_ci REGULATOR_SUPPLY("v-ape", "nmk-ske-keypad.0"), 273062306a36Sopenharmony_ci REGULATOR_SUPPLY("v-hsi", "ste_hsi.0"), 273162306a36Sopenharmony_ci REGULATOR_SUPPLY("vddvario", "smsc911x.0"), 273262306a36Sopenharmony_ci}; 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_cistatic struct regulator_consumer_supply db8500_vsmps2_consumers[] = { 273562306a36Sopenharmony_ci REGULATOR_SUPPLY("musb_1v8", "ab8500-usb.0"), 273662306a36Sopenharmony_ci /* AV8100 regulator */ 273762306a36Sopenharmony_ci REGULATOR_SUPPLY("hdmi_1v8", "0-0070"), 273862306a36Sopenharmony_ci}; 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_cistatic struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = { 274162306a36Sopenharmony_ci REGULATOR_SUPPLY("vsupply", "b2r2_bus"), 274262306a36Sopenharmony_ci REGULATOR_SUPPLY("vsupply", "mcde"), 274362306a36Sopenharmony_ci}; 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci/* SVA MMDSP regulator switch */ 274662306a36Sopenharmony_cistatic struct regulator_consumer_supply db8500_svammdsp_consumers[] = { 274762306a36Sopenharmony_ci REGULATOR_SUPPLY("sva-mmdsp", "cm_control"), 274862306a36Sopenharmony_ci}; 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci/* SVA pipe regulator switch */ 275162306a36Sopenharmony_cistatic struct regulator_consumer_supply db8500_svapipe_consumers[] = { 275262306a36Sopenharmony_ci REGULATOR_SUPPLY("sva-pipe", "cm_control"), 275362306a36Sopenharmony_ci}; 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci/* SIA MMDSP regulator switch */ 275662306a36Sopenharmony_cistatic struct regulator_consumer_supply db8500_siammdsp_consumers[] = { 275762306a36Sopenharmony_ci REGULATOR_SUPPLY("sia-mmdsp", "cm_control"), 275862306a36Sopenharmony_ci}; 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci/* SIA pipe regulator switch */ 276162306a36Sopenharmony_cistatic struct regulator_consumer_supply db8500_siapipe_consumers[] = { 276262306a36Sopenharmony_ci REGULATOR_SUPPLY("sia-pipe", "cm_control"), 276362306a36Sopenharmony_ci}; 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_cistatic struct regulator_consumer_supply db8500_sga_consumers[] = { 276662306a36Sopenharmony_ci REGULATOR_SUPPLY("v-mali", NULL), 276762306a36Sopenharmony_ci}; 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci/* ESRAM1 and 2 regulator switch */ 277062306a36Sopenharmony_cistatic struct regulator_consumer_supply db8500_esram12_consumers[] = { 277162306a36Sopenharmony_ci REGULATOR_SUPPLY("esram12", "cm_control"), 277262306a36Sopenharmony_ci}; 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_ci/* ESRAM3 and 4 regulator switch */ 277562306a36Sopenharmony_cistatic struct regulator_consumer_supply db8500_esram34_consumers[] = { 277662306a36Sopenharmony_ci REGULATOR_SUPPLY("v-esram34", "mcde"), 277762306a36Sopenharmony_ci REGULATOR_SUPPLY("esram34", "cm_control"), 277862306a36Sopenharmony_ci REGULATOR_SUPPLY("lcla_esram", "dma40.0"), 277962306a36Sopenharmony_ci}; 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_cistatic struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { 278262306a36Sopenharmony_ci [DB8500_REGULATOR_VAPE] = { 278362306a36Sopenharmony_ci .constraints = { 278462306a36Sopenharmony_ci .name = "db8500-vape", 278562306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 278662306a36Sopenharmony_ci .always_on = true, 278762306a36Sopenharmony_ci }, 278862306a36Sopenharmony_ci .consumer_supplies = db8500_vape_consumers, 278962306a36Sopenharmony_ci .num_consumer_supplies = ARRAY_SIZE(db8500_vape_consumers), 279062306a36Sopenharmony_ci }, 279162306a36Sopenharmony_ci [DB8500_REGULATOR_VARM] = { 279262306a36Sopenharmony_ci .constraints = { 279362306a36Sopenharmony_ci .name = "db8500-varm", 279462306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 279562306a36Sopenharmony_ci }, 279662306a36Sopenharmony_ci }, 279762306a36Sopenharmony_ci [DB8500_REGULATOR_VMODEM] = { 279862306a36Sopenharmony_ci .constraints = { 279962306a36Sopenharmony_ci .name = "db8500-vmodem", 280062306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 280162306a36Sopenharmony_ci }, 280262306a36Sopenharmony_ci }, 280362306a36Sopenharmony_ci [DB8500_REGULATOR_VPLL] = { 280462306a36Sopenharmony_ci .constraints = { 280562306a36Sopenharmony_ci .name = "db8500-vpll", 280662306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 280762306a36Sopenharmony_ci }, 280862306a36Sopenharmony_ci }, 280962306a36Sopenharmony_ci [DB8500_REGULATOR_VSMPS1] = { 281062306a36Sopenharmony_ci .constraints = { 281162306a36Sopenharmony_ci .name = "db8500-vsmps1", 281262306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 281362306a36Sopenharmony_ci }, 281462306a36Sopenharmony_ci }, 281562306a36Sopenharmony_ci [DB8500_REGULATOR_VSMPS2] = { 281662306a36Sopenharmony_ci .constraints = { 281762306a36Sopenharmony_ci .name = "db8500-vsmps2", 281862306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 281962306a36Sopenharmony_ci }, 282062306a36Sopenharmony_ci .consumer_supplies = db8500_vsmps2_consumers, 282162306a36Sopenharmony_ci .num_consumer_supplies = ARRAY_SIZE(db8500_vsmps2_consumers), 282262306a36Sopenharmony_ci }, 282362306a36Sopenharmony_ci [DB8500_REGULATOR_VSMPS3] = { 282462306a36Sopenharmony_ci .constraints = { 282562306a36Sopenharmony_ci .name = "db8500-vsmps3", 282662306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 282762306a36Sopenharmony_ci }, 282862306a36Sopenharmony_ci }, 282962306a36Sopenharmony_ci [DB8500_REGULATOR_VRF1] = { 283062306a36Sopenharmony_ci .constraints = { 283162306a36Sopenharmony_ci .name = "db8500-vrf1", 283262306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 283362306a36Sopenharmony_ci }, 283462306a36Sopenharmony_ci }, 283562306a36Sopenharmony_ci [DB8500_REGULATOR_SWITCH_SVAMMDSP] = { 283662306a36Sopenharmony_ci /* dependency to u8500-vape is handled outside regulator framework */ 283762306a36Sopenharmony_ci .constraints = { 283862306a36Sopenharmony_ci .name = "db8500-sva-mmdsp", 283962306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 284062306a36Sopenharmony_ci }, 284162306a36Sopenharmony_ci .consumer_supplies = db8500_svammdsp_consumers, 284262306a36Sopenharmony_ci .num_consumer_supplies = ARRAY_SIZE(db8500_svammdsp_consumers), 284362306a36Sopenharmony_ci }, 284462306a36Sopenharmony_ci [DB8500_REGULATOR_SWITCH_SVAMMDSPRET] = { 284562306a36Sopenharmony_ci .constraints = { 284662306a36Sopenharmony_ci /* "ret" means "retention" */ 284762306a36Sopenharmony_ci .name = "db8500-sva-mmdsp-ret", 284862306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 284962306a36Sopenharmony_ci }, 285062306a36Sopenharmony_ci }, 285162306a36Sopenharmony_ci [DB8500_REGULATOR_SWITCH_SVAPIPE] = { 285262306a36Sopenharmony_ci /* dependency to u8500-vape is handled outside regulator framework */ 285362306a36Sopenharmony_ci .constraints = { 285462306a36Sopenharmony_ci .name = "db8500-sva-pipe", 285562306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 285662306a36Sopenharmony_ci }, 285762306a36Sopenharmony_ci .consumer_supplies = db8500_svapipe_consumers, 285862306a36Sopenharmony_ci .num_consumer_supplies = ARRAY_SIZE(db8500_svapipe_consumers), 285962306a36Sopenharmony_ci }, 286062306a36Sopenharmony_ci [DB8500_REGULATOR_SWITCH_SIAMMDSP] = { 286162306a36Sopenharmony_ci /* dependency to u8500-vape is handled outside regulator framework */ 286262306a36Sopenharmony_ci .constraints = { 286362306a36Sopenharmony_ci .name = "db8500-sia-mmdsp", 286462306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 286562306a36Sopenharmony_ci }, 286662306a36Sopenharmony_ci .consumer_supplies = db8500_siammdsp_consumers, 286762306a36Sopenharmony_ci .num_consumer_supplies = ARRAY_SIZE(db8500_siammdsp_consumers), 286862306a36Sopenharmony_ci }, 286962306a36Sopenharmony_ci [DB8500_REGULATOR_SWITCH_SIAMMDSPRET] = { 287062306a36Sopenharmony_ci .constraints = { 287162306a36Sopenharmony_ci .name = "db8500-sia-mmdsp-ret", 287262306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 287362306a36Sopenharmony_ci }, 287462306a36Sopenharmony_ci }, 287562306a36Sopenharmony_ci [DB8500_REGULATOR_SWITCH_SIAPIPE] = { 287662306a36Sopenharmony_ci /* dependency to u8500-vape is handled outside regulator framework */ 287762306a36Sopenharmony_ci .constraints = { 287862306a36Sopenharmony_ci .name = "db8500-sia-pipe", 287962306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 288062306a36Sopenharmony_ci }, 288162306a36Sopenharmony_ci .consumer_supplies = db8500_siapipe_consumers, 288262306a36Sopenharmony_ci .num_consumer_supplies = ARRAY_SIZE(db8500_siapipe_consumers), 288362306a36Sopenharmony_ci }, 288462306a36Sopenharmony_ci [DB8500_REGULATOR_SWITCH_SGA] = { 288562306a36Sopenharmony_ci .supply_regulator = "db8500-vape", 288662306a36Sopenharmony_ci .constraints = { 288762306a36Sopenharmony_ci .name = "db8500-sga", 288862306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 288962306a36Sopenharmony_ci }, 289062306a36Sopenharmony_ci .consumer_supplies = db8500_sga_consumers, 289162306a36Sopenharmony_ci .num_consumer_supplies = ARRAY_SIZE(db8500_sga_consumers), 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci }, 289462306a36Sopenharmony_ci [DB8500_REGULATOR_SWITCH_B2R2_MCDE] = { 289562306a36Sopenharmony_ci .supply_regulator = "db8500-vape", 289662306a36Sopenharmony_ci .constraints = { 289762306a36Sopenharmony_ci .name = "db8500-b2r2-mcde", 289862306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 289962306a36Sopenharmony_ci }, 290062306a36Sopenharmony_ci .consumer_supplies = db8500_b2r2_mcde_consumers, 290162306a36Sopenharmony_ci .num_consumer_supplies = ARRAY_SIZE(db8500_b2r2_mcde_consumers), 290262306a36Sopenharmony_ci }, 290362306a36Sopenharmony_ci [DB8500_REGULATOR_SWITCH_ESRAM12] = { 290462306a36Sopenharmony_ci /* 290562306a36Sopenharmony_ci * esram12 is set in retention and supplied by Vsafe when Vape is off, 290662306a36Sopenharmony_ci * no need to hold Vape 290762306a36Sopenharmony_ci */ 290862306a36Sopenharmony_ci .constraints = { 290962306a36Sopenharmony_ci .name = "db8500-esram12", 291062306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 291162306a36Sopenharmony_ci }, 291262306a36Sopenharmony_ci .consumer_supplies = db8500_esram12_consumers, 291362306a36Sopenharmony_ci .num_consumer_supplies = ARRAY_SIZE(db8500_esram12_consumers), 291462306a36Sopenharmony_ci }, 291562306a36Sopenharmony_ci [DB8500_REGULATOR_SWITCH_ESRAM12RET] = { 291662306a36Sopenharmony_ci .constraints = { 291762306a36Sopenharmony_ci .name = "db8500-esram12-ret", 291862306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 291962306a36Sopenharmony_ci }, 292062306a36Sopenharmony_ci }, 292162306a36Sopenharmony_ci [DB8500_REGULATOR_SWITCH_ESRAM34] = { 292262306a36Sopenharmony_ci /* 292362306a36Sopenharmony_ci * esram34 is set in retention and supplied by Vsafe when Vape is off, 292462306a36Sopenharmony_ci * no need to hold Vape 292562306a36Sopenharmony_ci */ 292662306a36Sopenharmony_ci .constraints = { 292762306a36Sopenharmony_ci .name = "db8500-esram34", 292862306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 292962306a36Sopenharmony_ci }, 293062306a36Sopenharmony_ci .consumer_supplies = db8500_esram34_consumers, 293162306a36Sopenharmony_ci .num_consumer_supplies = ARRAY_SIZE(db8500_esram34_consumers), 293262306a36Sopenharmony_ci }, 293362306a36Sopenharmony_ci [DB8500_REGULATOR_SWITCH_ESRAM34RET] = { 293462306a36Sopenharmony_ci .constraints = { 293562306a36Sopenharmony_ci .name = "db8500-esram34-ret", 293662306a36Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 293762306a36Sopenharmony_ci }, 293862306a36Sopenharmony_ci }, 293962306a36Sopenharmony_ci}; 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_cistatic const struct mfd_cell common_prcmu_devs[] = { 294262306a36Sopenharmony_ci MFD_CELL_NAME("db8500_wdt"), 294362306a36Sopenharmony_ci MFD_CELL_NAME("db8500-cpuidle"), 294462306a36Sopenharmony_ci}; 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_cistatic const struct mfd_cell db8500_prcmu_devs[] = { 294762306a36Sopenharmony_ci MFD_CELL_OF("db8500-prcmu-regulators", NULL, 294862306a36Sopenharmony_ci &db8500_regulators, sizeof(db8500_regulators), 0, 294962306a36Sopenharmony_ci "stericsson,db8500-prcmu-regulator"), 295062306a36Sopenharmony_ci MFD_CELL_OF("db8500-thermal", 295162306a36Sopenharmony_ci NULL, NULL, 0, 0, "stericsson,db8500-thermal"), 295262306a36Sopenharmony_ci}; 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_cistatic int db8500_prcmu_register_ab8500(struct device *parent) 295562306a36Sopenharmony_ci{ 295662306a36Sopenharmony_ci struct device_node *np; 295762306a36Sopenharmony_ci struct resource ab850x_resource; 295862306a36Sopenharmony_ci const struct mfd_cell ab8500_cell = { 295962306a36Sopenharmony_ci .name = "ab8500-core", 296062306a36Sopenharmony_ci .of_compatible = "stericsson,ab8500", 296162306a36Sopenharmony_ci .id = AB8500_VERSION_AB8500, 296262306a36Sopenharmony_ci .resources = &ab850x_resource, 296362306a36Sopenharmony_ci .num_resources = 1, 296462306a36Sopenharmony_ci }; 296562306a36Sopenharmony_ci const struct mfd_cell ab8505_cell = { 296662306a36Sopenharmony_ci .name = "ab8505-core", 296762306a36Sopenharmony_ci .of_compatible = "stericsson,ab8505", 296862306a36Sopenharmony_ci .id = AB8500_VERSION_AB8505, 296962306a36Sopenharmony_ci .resources = &ab850x_resource, 297062306a36Sopenharmony_ci .num_resources = 1, 297162306a36Sopenharmony_ci }; 297262306a36Sopenharmony_ci const struct mfd_cell *ab850x_cell; 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci if (!parent->of_node) 297562306a36Sopenharmony_ci return -ENODEV; 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_ci /* Look up the device node, sneak the IRQ out of it */ 297862306a36Sopenharmony_ci for_each_child_of_node(parent->of_node, np) { 297962306a36Sopenharmony_ci if (of_device_is_compatible(np, ab8500_cell.of_compatible)) { 298062306a36Sopenharmony_ci ab850x_cell = &ab8500_cell; 298162306a36Sopenharmony_ci break; 298262306a36Sopenharmony_ci } 298362306a36Sopenharmony_ci if (of_device_is_compatible(np, ab8505_cell.of_compatible)) { 298462306a36Sopenharmony_ci ab850x_cell = &ab8505_cell; 298562306a36Sopenharmony_ci break; 298662306a36Sopenharmony_ci } 298762306a36Sopenharmony_ci } 298862306a36Sopenharmony_ci if (!np) { 298962306a36Sopenharmony_ci dev_info(parent, "could not find AB850X node in the device tree\n"); 299062306a36Sopenharmony_ci return -ENODEV; 299162306a36Sopenharmony_ci } 299262306a36Sopenharmony_ci of_irq_to_resource_table(np, &ab850x_resource, 1); 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_ci return mfd_add_devices(parent, 0, ab850x_cell, 1, NULL, 0, NULL); 299562306a36Sopenharmony_ci} 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_cistatic int db8500_prcmu_probe(struct platform_device *pdev) 299862306a36Sopenharmony_ci{ 299962306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 300062306a36Sopenharmony_ci int irq = 0, err = 0; 300162306a36Sopenharmony_ci struct resource *res; 300262306a36Sopenharmony_ci 300362306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu"); 300462306a36Sopenharmony_ci if (!res) { 300562306a36Sopenharmony_ci dev_err(&pdev->dev, "no prcmu memory region provided\n"); 300662306a36Sopenharmony_ci return -EINVAL; 300762306a36Sopenharmony_ci } 300862306a36Sopenharmony_ci prcmu_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); 300962306a36Sopenharmony_ci if (!prcmu_base) { 301062306a36Sopenharmony_ci dev_err(&pdev->dev, 301162306a36Sopenharmony_ci "failed to ioremap prcmu register memory\n"); 301262306a36Sopenharmony_ci return -ENOMEM; 301362306a36Sopenharmony_ci } 301462306a36Sopenharmony_ci init_prcm_registers(); 301562306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm"); 301662306a36Sopenharmony_ci if (!res) { 301762306a36Sopenharmony_ci dev_err(&pdev->dev, "no prcmu tcdm region provided\n"); 301862306a36Sopenharmony_ci return -EINVAL; 301962306a36Sopenharmony_ci } 302062306a36Sopenharmony_ci tcdm_base = devm_ioremap(&pdev->dev, res->start, 302162306a36Sopenharmony_ci resource_size(res)); 302262306a36Sopenharmony_ci if (!tcdm_base) { 302362306a36Sopenharmony_ci dev_err(&pdev->dev, 302462306a36Sopenharmony_ci "failed to ioremap prcmu-tcdm register memory\n"); 302562306a36Sopenharmony_ci return -ENOMEM; 302662306a36Sopenharmony_ci } 302762306a36Sopenharmony_ci 302862306a36Sopenharmony_ci /* Clean up the mailbox interrupts after pre-kernel code. */ 302962306a36Sopenharmony_ci writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 303262306a36Sopenharmony_ci if (irq <= 0) 303362306a36Sopenharmony_ci return irq; 303462306a36Sopenharmony_ci 303562306a36Sopenharmony_ci err = request_threaded_irq(irq, prcmu_irq_handler, 303662306a36Sopenharmony_ci prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL); 303762306a36Sopenharmony_ci if (err < 0) { 303862306a36Sopenharmony_ci pr_err("prcmu: Failed to allocate IRQ_DB8500_PRCMU1.\n"); 303962306a36Sopenharmony_ci return err; 304062306a36Sopenharmony_ci } 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci db8500_irq_init(np); 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_ci prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); 304562306a36Sopenharmony_ci 304662306a36Sopenharmony_ci err = mfd_add_devices(&pdev->dev, 0, common_prcmu_devs, 304762306a36Sopenharmony_ci ARRAY_SIZE(common_prcmu_devs), NULL, 0, db8500_irq_domain); 304862306a36Sopenharmony_ci if (err) { 304962306a36Sopenharmony_ci pr_err("prcmu: Failed to add subdevices\n"); 305062306a36Sopenharmony_ci return err; 305162306a36Sopenharmony_ci } 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci /* TODO: Remove restriction when clk definitions are available. */ 305462306a36Sopenharmony_ci if (!of_machine_is_compatible("st-ericsson,u8540")) { 305562306a36Sopenharmony_ci err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs, 305662306a36Sopenharmony_ci ARRAY_SIZE(db8500_prcmu_devs), NULL, 0, 305762306a36Sopenharmony_ci db8500_irq_domain); 305862306a36Sopenharmony_ci if (err) { 305962306a36Sopenharmony_ci mfd_remove_devices(&pdev->dev); 306062306a36Sopenharmony_ci pr_err("prcmu: Failed to add subdevices\n"); 306162306a36Sopenharmony_ci return err; 306262306a36Sopenharmony_ci } 306362306a36Sopenharmony_ci } 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci err = db8500_prcmu_register_ab8500(&pdev->dev); 306662306a36Sopenharmony_ci if (err) { 306762306a36Sopenharmony_ci mfd_remove_devices(&pdev->dev); 306862306a36Sopenharmony_ci pr_err("prcmu: Failed to add ab8500 subdevice\n"); 306962306a36Sopenharmony_ci return err; 307062306a36Sopenharmony_ci } 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_ci pr_info("DB8500 PRCMU initialized\n"); 307362306a36Sopenharmony_ci return err; 307462306a36Sopenharmony_ci} 307562306a36Sopenharmony_cistatic const struct of_device_id db8500_prcmu_match[] = { 307662306a36Sopenharmony_ci { .compatible = "stericsson,db8500-prcmu"}, 307762306a36Sopenharmony_ci { }, 307862306a36Sopenharmony_ci}; 307962306a36Sopenharmony_ci 308062306a36Sopenharmony_cistatic struct platform_driver db8500_prcmu_driver = { 308162306a36Sopenharmony_ci .driver = { 308262306a36Sopenharmony_ci .name = "db8500-prcmu", 308362306a36Sopenharmony_ci .of_match_table = db8500_prcmu_match, 308462306a36Sopenharmony_ci }, 308562306a36Sopenharmony_ci .probe = db8500_prcmu_probe, 308662306a36Sopenharmony_ci}; 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_cistatic int __init db8500_prcmu_init(void) 308962306a36Sopenharmony_ci{ 309062306a36Sopenharmony_ci return platform_driver_register(&db8500_prcmu_driver); 309162306a36Sopenharmony_ci} 309262306a36Sopenharmony_cicore_initcall(db8500_prcmu_init); 3093