/*
|
*
|
* (C) COPYRIGHT 2011-2016 ARM Limited. All rights reserved.
|
*
|
* This program is free software and is provided to you under the terms of the
|
* GNU General Public License version 2 as published by the Free Software
|
* Foundation, and any use by you of this program is subject to the terms
|
* of such GNU licence.
|
*
|
* A copy of the licence is included with the program, and can also be obtained
|
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
* Boston, MA 02110-1301, USA.
|
*
|
*/
|
|
|
|
#include <linux/io.h>
|
#include <mali_kbase.h>
|
#include "mali_kbase_cpu_vexpress.h"
|
|
#define HZ_IN_MHZ (1000000)
|
|
#define CORETILE_EXPRESS_A9X4_SCC_START (0x100E2000)
|
#define MOTHERBOARD_SYS_CFG_START (0x10000000)
|
#define SYS_CFGDATA_OFFSET (0x000000A0)
|
#define SYS_CFGCTRL_OFFSET (0x000000A4)
|
#define SYS_CFGSTAT_OFFSET (0x000000A8)
|
|
#define SYS_CFGCTRL_START_BIT_VALUE (1 << 31)
|
#define READ_REG_BIT_VALUE (0 << 30)
|
#define DCC_DEFAULT_BIT_VALUE (0 << 26)
|
#define SYS_CFG_OSC_FUNC_BIT_VALUE (1 << 20)
|
#define SITE_DEFAULT_BIT_VALUE (1 << 16)
|
#define BOARD_STACK_POS_DEFAULT_BIT_VALUE (0 << 12)
|
#define DEVICE_DEFAULT_BIT_VALUE (2 << 0)
|
#define SYS_CFG_COMPLETE_BIT_VALUE (1 << 0)
|
#define SYS_CFG_ERROR_BIT_VALUE (1 << 1)
|
|
#define FEED_REG_BIT_MASK (0x0F)
|
#define FCLK_PA_DIVIDE_BIT_SHIFT (0x03)
|
#define FCLK_PB_DIVIDE_BIT_SHIFT (0x07)
|
#define FCLK_PC_DIVIDE_BIT_SHIFT (0x0B)
|
#define AXICLK_PA_DIVIDE_BIT_SHIFT (0x0F)
|
#define AXICLK_PB_DIVIDE_BIT_SHIFT (0x13)
|
|
/* the following three values used for reading
|
* HBI value of the LogicTile daughterboard */
|
#define VE_MOTHERBOARD_PERIPHERALS_SMB_CS7 (0x10000000)
|
#define VE_SYS_PROC_ID1_OFFSET (0x00000088)
|
#define VE_LOGIC_TILE_HBI_MASK (0x00000FFF)
|
|
#define IS_SINGLE_BIT_SET(val, pos) (val&(1<<pos))
|
|
/**
|
* Values used for determining the GPU frequency based on the LogicTile type
|
* Used by the function kbase_get_platform_logic_tile_type
|
*/
|
#define VE_VIRTEX6_GPU_FREQ_MIN 5000
|
#define VE_VIRTEX6_GPU_FREQ_MAX 5000
|
#define VE_VIRTEX7_GPU_FREQ_MIN 40000
|
#define VE_VIRTEX7_GPU_FREQ_MAX 40000
|
#define VE_DEFAULT_GPU_FREQ_MIN 5000
|
#define VE_DEFAULT_GPU_FREQ_MAX 5000
|
|
|
#define CPU_CLOCK_SPEED_UNDEFINED (0)
|
|
static u32 cpu_clock_speed = CPU_CLOCK_SPEED_UNDEFINED;
|
|
static DEFINE_RAW_SPINLOCK(syscfg_lock);
|
/**
|
* kbase_get_vendor_specific_cpu_clock_speed -Retrieves the CPU clock speed
|
* @cpu_clock - the value of CPU clock speed in MHz
|
*
|
* Returns 0 on success, error code otherwise.
|
*
|
* The implementation is platform specific.
|
*/
|
int kbase_get_vexpress_cpu_clock_speed(u32 *cpu_clock)
|
{
|
int err = 0;
|
u32 reg_val = 0;
|
u32 osc2_value = 0;
|
u32 pa_divide = 0;
|
u32 pb_divide = 0;
|
u32 pc_divide = 0;
|
void __iomem *syscfg_reg = NULL;
|
void __iomem *scc_reg = NULL;
|
|
if (CPU_CLOCK_SPEED_UNDEFINED != cpu_clock_speed) {
|
*cpu_clock = cpu_clock_speed;
|
return 0;
|
}
|
|
/* Init the value in case something goes wrong */
|
*cpu_clock = 0;
|
|
/* Map CPU register into virtual memory */
|
syscfg_reg = ioremap(MOTHERBOARD_SYS_CFG_START, 0x1000);
|
if (syscfg_reg == NULL) {
|
err = -EIO;
|
goto syscfg_reg_map_failed;
|
}
|
|
scc_reg = ioremap(CORETILE_EXPRESS_A9X4_SCC_START, 0x1000);
|
if (scc_reg == NULL) {
|
err = -EIO;
|
goto scc_reg_map_failed;
|
}
|
|
raw_spin_lock(&syscfg_lock);
|
|
/* Read SYS regs - OSC2 */
|
reg_val = readl(syscfg_reg + SYS_CFGCTRL_OFFSET);
|
|
/* Check if there is any other undergoing request */
|
if (reg_val & SYS_CFGCTRL_START_BIT_VALUE) {
|
err = -EBUSY;
|
goto ongoing_request;
|
}
|
/* Reset the CGFGSTAT reg */
|
writel(0, (syscfg_reg + SYS_CFGSTAT_OFFSET));
|
|
writel(SYS_CFGCTRL_START_BIT_VALUE | READ_REG_BIT_VALUE |
|
DCC_DEFAULT_BIT_VALUE |
|
SYS_CFG_OSC_FUNC_BIT_VALUE |
|
SITE_DEFAULT_BIT_VALUE |
|
BOARD_STACK_POS_DEFAULT_BIT_VALUE |
|
DEVICE_DEFAULT_BIT_VALUE,
|
(syscfg_reg + SYS_CFGCTRL_OFFSET));
|
/* Wait for the transaction to complete */
|
while (!(readl(syscfg_reg + SYS_CFGSTAT_OFFSET) &
|
SYS_CFG_COMPLETE_BIT_VALUE))
|
;
|
/* Read SYS_CFGSTAT Register to get the status of submitted
|
* transaction */
|
reg_val = readl(syscfg_reg + SYS_CFGSTAT_OFFSET);
|
|
if (reg_val & SYS_CFG_ERROR_BIT_VALUE) {
|
/* Error while setting register */
|
err = -EIO;
|
goto set_reg_error;
|
}
|
|
osc2_value = readl(syscfg_reg + SYS_CFGDATA_OFFSET);
|
/* Read the SCC CFGRW0 register */
|
reg_val = readl(scc_reg);
|
|
/*
|
* Select the appropriate feed:
|
* CFGRW0[0] - CLKOB
|
* CFGRW0[1] - CLKOC
|
* CFGRW0[2] - FACLK (CLK)B FROM AXICLK PLL)
|
*/
|
/* Calculate the FCLK */
|
if (IS_SINGLE_BIT_SET(reg_val, 0)) {
|
/* CFGRW0[0] - CLKOB */
|
/* CFGRW0[6:3] */
|
pa_divide = ((reg_val & (FEED_REG_BIT_MASK <<
|
FCLK_PA_DIVIDE_BIT_SHIFT)) >>
|
FCLK_PA_DIVIDE_BIT_SHIFT);
|
/* CFGRW0[10:7] */
|
pb_divide = ((reg_val & (FEED_REG_BIT_MASK <<
|
FCLK_PB_DIVIDE_BIT_SHIFT)) >>
|
FCLK_PB_DIVIDE_BIT_SHIFT);
|
*cpu_clock = osc2_value * (pa_divide + 1) / (pb_divide + 1);
|
} else if (IS_SINGLE_BIT_SET(reg_val, 1)) {
|
/* CFGRW0[1] - CLKOC */
|
/* CFGRW0[6:3] */
|
pa_divide = ((reg_val & (FEED_REG_BIT_MASK <<
|
FCLK_PA_DIVIDE_BIT_SHIFT)) >>
|
FCLK_PA_DIVIDE_BIT_SHIFT);
|
/* CFGRW0[14:11] */
|
pc_divide = ((reg_val & (FEED_REG_BIT_MASK <<
|
FCLK_PC_DIVIDE_BIT_SHIFT)) >>
|
FCLK_PC_DIVIDE_BIT_SHIFT);
|
*cpu_clock = osc2_value * (pa_divide + 1) / (pc_divide + 1);
|
} else if (IS_SINGLE_BIT_SET(reg_val, 2)) {
|
/* CFGRW0[2] - FACLK */
|
/* CFGRW0[18:15] */
|
pa_divide = ((reg_val & (FEED_REG_BIT_MASK <<
|
AXICLK_PA_DIVIDE_BIT_SHIFT)) >>
|
AXICLK_PA_DIVIDE_BIT_SHIFT);
|
/* CFGRW0[22:19] */
|
pb_divide = ((reg_val & (FEED_REG_BIT_MASK <<
|
AXICLK_PB_DIVIDE_BIT_SHIFT)) >>
|
AXICLK_PB_DIVIDE_BIT_SHIFT);
|
*cpu_clock = osc2_value * (pa_divide + 1) / (pb_divide + 1);
|
} else {
|
err = -EIO;
|
}
|
|
set_reg_error:
|
ongoing_request:
|
raw_spin_unlock(&syscfg_lock);
|
*cpu_clock /= HZ_IN_MHZ;
|
|
if (!err)
|
cpu_clock_speed = *cpu_clock;
|
|
iounmap(scc_reg);
|
|
scc_reg_map_failed:
|
iounmap(syscfg_reg);
|
|
syscfg_reg_map_failed:
|
|
return err;
|
}
|
|
/**
|
* kbase_get_platform_logic_tile_type - determines which LogicTile type
|
* is used by Versatile Express
|
*
|
* When platform_config build parameter is specified as vexpress, i.e.,
|
* platform_config=vexpress, GPU frequency may vary dependent on the
|
* particular platform. The GPU frequency depends on the LogicTile type.
|
*
|
* This function determines which LogicTile type is used by the platform by
|
* reading the HBI value of the daughterboard which holds the LogicTile:
|
*
|
* 0x217 HBI0217 Virtex-6
|
* 0x192 HBI0192 Virtex-5
|
* 0x247 HBI0247 Virtex-7
|
*
|
* Return: HBI value of the logic tile daughterboard, zero if not accessible
|
*/
|
static u32 kbase_get_platform_logic_tile_type(void)
|
{
|
void __iomem *syscfg_reg = NULL;
|
u32 sys_procid1 = 0;
|
|
syscfg_reg = ioremap(VE_MOTHERBOARD_PERIPHERALS_SMB_CS7 + VE_SYS_PROC_ID1_OFFSET, 4);
|
if (NULL != syscfg_reg) {
|
sys_procid1 = readl(syscfg_reg);
|
iounmap(syscfg_reg);
|
}
|
|
return sys_procid1 & VE_LOGIC_TILE_HBI_MASK;
|
}
|
|
u32 kbase_get_platform_min_freq(void)
|
{
|
u32 ve_logic_tile = kbase_get_platform_logic_tile_type();
|
|
switch (ve_logic_tile) {
|
case 0x217:
|
/* Virtex 6, HBI0217 */
|
return VE_VIRTEX6_GPU_FREQ_MIN;
|
case 0x247:
|
/* Virtex 7, HBI0247 */
|
return VE_VIRTEX7_GPU_FREQ_MIN;
|
default:
|
/* all other logic tiles, i.e., Virtex 5 HBI0192
|
* or unsuccessful reading from the platform -
|
* fall back to some default value */
|
return VE_DEFAULT_GPU_FREQ_MIN;
|
}
|
}
|
|
u32 kbase_get_platform_max_freq(void)
|
{
|
u32 ve_logic_tile = kbase_get_platform_logic_tile_type();
|
|
switch (ve_logic_tile) {
|
case 0x217:
|
/* Virtex 6, HBI0217 */
|
return VE_VIRTEX6_GPU_FREQ_MAX;
|
case 0x247:
|
/* Virtex 7, HBI0247 */
|
return VE_VIRTEX7_GPU_FREQ_MAX;
|
default:
|
/* all other logic tiles, i.e., Virtex 5 HBI0192
|
* or unsuccessful reading from the platform -
|
* fall back to some default value */
|
return VE_DEFAULT_GPU_FREQ_MAX;
|
}
|
}
|