mirror of
https://github.com/torvalds/linux.git
synced 2024-11-29 07:31:29 +00:00
clk: tegra: Changes for v4.3-rc1
This contains the DFLL driver needed to implement CPU frequency scaling on Tegra. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJVzfAzAAoJEN0jrNd/PrOhmjwP/iUYYxJ2kQ8lOxIxJ6bujBAF 2PTdT5NOAKu0HJkKDHDtGO8gKRqZOPpyfoI8Ol5Xy/C0lIJ/uYoXqCZxfGjb2671 XzGQFcd8j12qHu0lR5YD8wbivfBXzi4RCwFudTtdZi59dZqat8qUxtvHhtSVs1X9 I6bN63ykXuHENAhVLdfz0+Trh4sMffOQ+jILQneB6OoKg+GGZpPkJM4WUhrilVKI Rw8MHxOlI5/2BUwxSRpu6K5BO1YVfWN5kgD/oyYrzRpd7p2Lxf15NTK/1zMOQ0Pt MD3J3enNqLT4brvnLstCChdaXz9r+CN4ut28Tnp7ocvBHIA/+1RjOfRTmsdcYRCE CelKsymLgEIFGZfQO57PDZXf5nEbnjhgshj1vNEWrXq6B8tgYeemwFKl1f7r6qEO pYmhLDV+AdP6+97FX1ySvBpBVY+GSJxOTupWYb3EvU2iwl9kfdcUs/EOQeo0oz+j coMWHQE9lcawcsQlbdhgq5uRGNrD9s4OkZAC4Ga5+ZiNo88gUzgE84WqYhQ6GJNE If+7RcJeCY4g2tpQJJtBM6kObtd+MWa7zSzHBjks7Il7y9C8uqpI5zgG3sufh/sG TmgxPv1FUEZBNowv6eA36euo4iw2XhZ3MFg2tlJdwgRj8+cfyK+fue/skCXuToaH fqeiJYcfpOv8s5JXfU3P =QWKO -----END PGP SIGNATURE----- Merge tag 'tegra-for-4.3-clk' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into clk-next clk: tegra: Changes for v4.3-rc1 This contains the DFLL driver needed to implement CPU frequency scaling on Tegra.
This commit is contained in:
commit
a7c602bf42
@ -0,0 +1,79 @@
|
||||
NVIDIA Tegra124 DFLL FCPU clocksource
|
||||
|
||||
This binding uses the common clock binding:
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
The DFLL IP block on Tegra is a root clocksource designed for clocking
|
||||
the fast CPU cluster. It consists of a free-running voltage controlled
|
||||
oscillator connected to the CPU voltage rail (VDD_CPU), and a closed loop
|
||||
control module that will automatically adjust the VDD_CPU voltage by
|
||||
communicating with an off-chip PMIC either via an I2C bus or via PWM signals.
|
||||
Currently only the I2C mode is supported by these bindings.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "nvidia,tegra124-dfll"
|
||||
- reg : Defines the following set of registers, in the order listed:
|
||||
- registers for the DFLL control logic.
|
||||
- registers for the I2C output logic.
|
||||
- registers for the integrated I2C master controller.
|
||||
- look-up table RAM for voltage register values.
|
||||
- interrupts: Should contain the DFLL block interrupt.
|
||||
- clocks: Must contain an entry for each entry in clock-names.
|
||||
See clock-bindings.txt for details.
|
||||
- clock-names: Must include the following entries:
|
||||
- soc: Clock source for the DFLL control logic.
|
||||
- ref: The closed loop reference clock
|
||||
- i2c: Clock source for the integrated I2C master.
|
||||
- resets: Must contain an entry for each entry in reset-names.
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names: Must include the following entries:
|
||||
- dvco: Reset control for the DFLL DVCO.
|
||||
- #clock-cells: Must be 0.
|
||||
- clock-output-names: Name of the clock output.
|
||||
- vdd-cpu-supply: Regulator for the CPU voltage rail that the DFLL
|
||||
hardware will start controlling. The regulator will be queried for
|
||||
the I2C register, control values and supported voltages.
|
||||
|
||||
Required properties for the control loop parameters:
|
||||
- nvidia,sample-rate: Sample rate of the DFLL control loop.
|
||||
- nvidia,droop-ctrl: See the register CL_DVFS_DROOP_CTRL in the TRM.
|
||||
- nvidia,force-mode: See the field DFLL_PARAMS_FORCE_MODE in the TRM.
|
||||
- nvidia,cf: Numeric value, see the field DFLL_PARAMS_CF_PARAM in the TRM.
|
||||
- nvidia,ci: Numeric value, see the field DFLL_PARAMS_CI_PARAM in the TRM.
|
||||
- nvidia,cg: Numeric value, see the field DFLL_PARAMS_CG_PARAM in the TRM.
|
||||
|
||||
Optional properties for the control loop parameters:
|
||||
- nvidia,cg-scale: Boolean value, see the field DFLL_PARAMS_CG_SCALE in the TRM.
|
||||
|
||||
Required properties for I2C mode:
|
||||
- nvidia,i2c-fs-rate: I2C transfer rate, if using full speed mode.
|
||||
|
||||
Example:
|
||||
|
||||
clock@0,70110000 {
|
||||
compatible = "nvidia,tegra124-dfll";
|
||||
reg = <0 0x70110000 0 0x100>, /* DFLL control */
|
||||
<0 0x70110000 0 0x100>, /* I2C output control */
|
||||
<0 0x70110100 0 0x100>, /* Integrated I2C controller */
|
||||
<0 0x70110200 0 0x100>; /* Look-up table RAM */
|
||||
interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&tegra_car TEGRA124_CLK_DFLL_SOC>,
|
||||
<&tegra_car TEGRA124_CLK_DFLL_REF>,
|
||||
<&tegra_car TEGRA124_CLK_I2C5>;
|
||||
clock-names = "soc", "ref", "i2c";
|
||||
resets = <&tegra_car TEGRA124_RST_DFLL_DVCO>;
|
||||
reset-names = "dvco";
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "dfllCPU_out";
|
||||
vdd-cpu-supply = <&vdd_cpu>;
|
||||
status = "okay";
|
||||
|
||||
nvidia,sample-rate = <12500>;
|
||||
nvidia,droop-ctrl = <0x00000f00>;
|
||||
nvidia,force-mode = <1>;
|
||||
nvidia,cf = <10>;
|
||||
nvidia,ci = <0>;
|
||||
nvidia,cg = <2>;
|
||||
|
||||
nvidia,i2c-fs-rate = <400000>;
|
||||
};
|
@ -8,6 +8,7 @@ menuconfig ARCH_TEGRA
|
||||
select HAVE_ARM_SCU if SMP
|
||||
select HAVE_ARM_TWD if SMP
|
||||
select PINCTRL
|
||||
select PM_OPP
|
||||
select ARCH_HAS_RESET_CONTROLLER
|
||||
select RESET_CONTROLLER
|
||||
select SOC_BUS
|
||||
|
@ -1,5 +1,6 @@
|
||||
obj-y += clk.o
|
||||
obj-y += clk-audio-sync.o
|
||||
obj-y += clk-dfll.o
|
||||
obj-y += clk-divider.o
|
||||
obj-y += clk-periph.o
|
||||
obj-y += clk-periph-gate.o
|
||||
@ -16,4 +17,6 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124-dfll-fcpu.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o
|
||||
obj-y += cvb.o
|
||||
|
1755
drivers/clk/tegra/clk-dfll.c
Normal file
1755
drivers/clk/tegra/clk-dfll.c
Normal file
File diff suppressed because it is too large
Load Diff
54
drivers/clk/tegra/clk-dfll.h
Normal file
54
drivers/clk/tegra/clk-dfll.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* clk-dfll.h - prototypes and macros for the Tegra DFLL clocksource driver
|
||||
* Copyright (C) 2013 NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* Aleksandr Frid <afrid@nvidia.com>
|
||||
* Paul Walmsley <pwalmsley@nvidia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_CLK_TEGRA_CLK_DFLL_H
|
||||
#define __DRIVERS_CLK_TEGRA_CLK_DFLL_H
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* struct tegra_dfll_soc_data - SoC-specific hooks/integration for the DFLL driver
|
||||
* @opp_dev: struct device * that holds the OPP table for the DFLL
|
||||
* @min_millivolts: minimum voltage (in mV) that the DFLL can operate
|
||||
* @tune0_low: DFLL tuning register 0 (low voltage range)
|
||||
* @tune0_high: DFLL tuning register 0 (high voltage range)
|
||||
* @tune1: DFLL tuning register 1
|
||||
* @assert_dvco_reset: fn ptr to place the DVCO in reset
|
||||
* @deassert_dvco_reset: fn ptr to release the DVCO reset
|
||||
* @set_clock_trimmers_high: fn ptr to tune clock trimmers for high voltage
|
||||
* @set_clock_trimmers_low: fn ptr to tune clock trimmers for low voltage
|
||||
*/
|
||||
struct tegra_dfll_soc_data {
|
||||
struct device *dev;
|
||||
unsigned int min_millivolts;
|
||||
u32 tune0_low;
|
||||
u32 tune0_high;
|
||||
u32 tune1;
|
||||
void (*init_clock_trimmers)(void);
|
||||
void (*set_clock_trimmers_high)(void);
|
||||
void (*set_clock_trimmers_low)(void);
|
||||
};
|
||||
|
||||
int tegra_dfll_register(struct platform_device *pdev,
|
||||
struct tegra_dfll_soc_data *soc);
|
||||
int tegra_dfll_unregister(struct platform_device *pdev);
|
||||
int tegra_dfll_runtime_suspend(struct device *dev);
|
||||
int tegra_dfll_runtime_resume(struct device *dev);
|
||||
|
||||
#endif /* __DRIVERS_CLK_TEGRA_CLK_DFLL_H */
|
@ -43,7 +43,9 @@ static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4",
|
||||
|
||||
static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
|
||||
"pll_p", "pll_p_out4", "unused",
|
||||
"unused", "pll_x" };
|
||||
"unused", "pll_x", "unused", "unused",
|
||||
"unused", "unused", "unused", "unused",
|
||||
"dfllCPU_out" };
|
||||
|
||||
static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
|
||||
"pll_p", "pll_p_out4", "unused",
|
||||
|
166
drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
Normal file
166
drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Tegra124 DFLL FCPU clock source driver
|
||||
*
|
||||
* Copyright (C) 2012-2014 NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* Aleksandr Frid <afrid@nvidia.com>
|
||||
* Paul Walmsley <pwalmsley@nvidia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
||||
#include "clk.h"
|
||||
#include "clk-dfll.h"
|
||||
#include "cvb.h"
|
||||
|
||||
/* Maximum CPU frequency, indexed by CPU speedo id */
|
||||
static const unsigned long cpu_max_freq_table[] = {
|
||||
[0] = 2014500000UL,
|
||||
[1] = 2320500000UL,
|
||||
[2] = 2116500000UL,
|
||||
[3] = 2524500000UL,
|
||||
};
|
||||
|
||||
static const struct cvb_table tegra124_cpu_cvb_tables[] = {
|
||||
{
|
||||
.speedo_id = -1,
|
||||
.process_id = -1,
|
||||
.min_millivolts = 900,
|
||||
.max_millivolts = 1260,
|
||||
.alignment = {
|
||||
.step_uv = 10000, /* 10mV */
|
||||
},
|
||||
.speedo_scale = 100,
|
||||
.voltage_scale = 1000,
|
||||
.cvb_table = {
|
||||
{204000000UL, {1112619, -29295, 402} },
|
||||
{306000000UL, {1150460, -30585, 402} },
|
||||
{408000000UL, {1190122, -31865, 402} },
|
||||
{510000000UL, {1231606, -33155, 402} },
|
||||
{612000000UL, {1274912, -34435, 402} },
|
||||
{714000000UL, {1320040, -35725, 402} },
|
||||
{816000000UL, {1366990, -37005, 402} },
|
||||
{918000000UL, {1415762, -38295, 402} },
|
||||
{1020000000UL, {1466355, -39575, 402} },
|
||||
{1122000000UL, {1518771, -40865, 402} },
|
||||
{1224000000UL, {1573009, -42145, 402} },
|
||||
{1326000000UL, {1629068, -43435, 402} },
|
||||
{1428000000UL, {1686950, -44715, 402} },
|
||||
{1530000000UL, {1746653, -46005, 402} },
|
||||
{1632000000UL, {1808179, -47285, 402} },
|
||||
{1734000000UL, {1871526, -48575, 402} },
|
||||
{1836000000UL, {1936696, -49855, 402} },
|
||||
{1938000000UL, {2003687, -51145, 402} },
|
||||
{2014500000UL, {2054787, -52095, 402} },
|
||||
{2116500000UL, {2124957, -53385, 402} },
|
||||
{2218500000UL, {2196950, -54665, 402} },
|
||||
{2320500000UL, {2270765, -55955, 402} },
|
||||
{2422500000UL, {2346401, -57235, 402} },
|
||||
{2524500000UL, {2437299, -58535, 402} },
|
||||
{0, { 0, 0, 0} },
|
||||
},
|
||||
.cpu_dfll_data = {
|
||||
.tune0_low = 0x005020ff,
|
||||
.tune0_high = 0x005040ff,
|
||||
.tune1 = 0x00000060,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
|
||||
{
|
||||
int process_id, speedo_id, speedo_value;
|
||||
struct tegra_dfll_soc_data *soc;
|
||||
const struct cvb_table *cvb;
|
||||
|
||||
process_id = tegra_sku_info.cpu_process_id;
|
||||
speedo_id = tegra_sku_info.cpu_speedo_id;
|
||||
speedo_value = tegra_sku_info.cpu_speedo_value;
|
||||
|
||||
if (speedo_id >= ARRAY_SIZE(cpu_max_freq_table)) {
|
||||
dev_err(&pdev->dev, "unknown max CPU freq for speedo_id=%d\n",
|
||||
speedo_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
soc = devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL);
|
||||
if (!soc)
|
||||
return -ENOMEM;
|
||||
|
||||
soc->dev = get_cpu_device(0);
|
||||
if (!soc->dev) {
|
||||
dev_err(&pdev->dev, "no CPU0 device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
cvb = tegra_cvb_build_opp_table(tegra124_cpu_cvb_tables,
|
||||
ARRAY_SIZE(tegra124_cpu_cvb_tables),
|
||||
process_id, speedo_id, speedo_value,
|
||||
cpu_max_freq_table[speedo_id],
|
||||
soc->dev);
|
||||
if (IS_ERR(cvb)) {
|
||||
dev_err(&pdev->dev, "couldn't build OPP table: %ld\n",
|
||||
PTR_ERR(cvb));
|
||||
return PTR_ERR(cvb);
|
||||
}
|
||||
|
||||
soc->min_millivolts = cvb->min_millivolts;
|
||||
soc->tune0_low = cvb->cpu_dfll_data.tune0_low;
|
||||
soc->tune0_high = cvb->cpu_dfll_data.tune0_high;
|
||||
soc->tune1 = cvb->cpu_dfll_data.tune1;
|
||||
|
||||
return tegra_dfll_register(pdev, soc);
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra124-dfll", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra124_dfll_fcpu_of_match);
|
||||
|
||||
static const struct dev_pm_ops tegra124_dfll_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
|
||||
tegra_dfll_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver tegra124_dfll_fcpu_driver = {
|
||||
.probe = tegra124_dfll_fcpu_probe,
|
||||
.remove = tegra_dfll_unregister,
|
||||
.driver = {
|
||||
.name = "tegra124-dfll",
|
||||
.of_match_table = tegra124_dfll_fcpu_of_match,
|
||||
.pm = &tegra124_dfll_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init tegra124_dfll_fcpu_init(void)
|
||||
{
|
||||
return platform_driver_register(&tegra124_dfll_fcpu_driver);
|
||||
}
|
||||
module_init(tegra124_dfll_fcpu_init);
|
||||
|
||||
static void __exit tegra124_dfll_fcpu_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tegra124_dfll_fcpu_driver);
|
||||
}
|
||||
module_exit(tegra124_dfll_fcpu_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Tegra124 DFLL clock source driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Aleksandr Frid <afrid@nvidia.com>");
|
||||
MODULE_AUTHOR("Paul Walmsley <pwalmsley@nvidia.com>");
|
@ -23,6 +23,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <dt-bindings/clock/tegra124-car.h>
|
||||
#include <dt-bindings/reset/tegra124-car.h>
|
||||
|
||||
#include "clk.h"
|
||||
#include "clk-id.h"
|
||||
@ -38,6 +39,9 @@
|
||||
#define CLK_SOURCE_CSITE 0x1d4
|
||||
#define CLK_SOURCE_EMC 0x19c
|
||||
|
||||
#define RST_DFLL_DVCO 0x2f4
|
||||
#define DVFS_DFLL_RESET_SHIFT 0
|
||||
|
||||
#define PLLC_BASE 0x80
|
||||
#define PLLC_OUT 0x84
|
||||
#define PLLC_MISC2 0x88
|
||||
@ -93,6 +97,8 @@
|
||||
#define PMC_PLLM_WB0_OVERRIDE 0x1dc
|
||||
#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0
|
||||
|
||||
#define CCLKG_BURST_POLICY 0x368
|
||||
|
||||
#define UTMIP_PLL_CFG2 0x488
|
||||
#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xffff) << 6)
|
||||
#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18)
|
||||
@ -125,6 +131,8 @@
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static struct cpu_clk_suspend_context {
|
||||
u32 clk_csite_src;
|
||||
u32 cclkg_burst;
|
||||
u32 cclkg_divider;
|
||||
} tegra124_cpu_clk_sctx;
|
||||
#endif
|
||||
|
||||
@ -1318,12 +1326,22 @@ static void tegra124_cpu_clock_suspend(void)
|
||||
tegra124_cpu_clk_sctx.clk_csite_src =
|
||||
readl(clk_base + CLK_SOURCE_CSITE);
|
||||
writel(3 << 30, clk_base + CLK_SOURCE_CSITE);
|
||||
|
||||
tegra124_cpu_clk_sctx.cclkg_burst =
|
||||
readl(clk_base + CCLKG_BURST_POLICY);
|
||||
tegra124_cpu_clk_sctx.cclkg_divider =
|
||||
readl(clk_base + CCLKG_BURST_POLICY + 4);
|
||||
}
|
||||
|
||||
static void tegra124_cpu_clock_resume(void)
|
||||
{
|
||||
writel(tegra124_cpu_clk_sctx.clk_csite_src,
|
||||
clk_base + CLK_SOURCE_CSITE);
|
||||
|
||||
writel(tegra124_cpu_clk_sctx.cclkg_burst,
|
||||
clk_base + CCLKG_BURST_POLICY);
|
||||
writel(tegra124_cpu_clk_sctx.cclkg_divider,
|
||||
clk_base + CCLKG_BURST_POLICY + 4);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1413,6 +1431,68 @@ static void __init tegra124_clock_apply_init_table(void)
|
||||
tegra_init_from_table(tegra124_init_table, clks, TEGRA124_CLK_CLK_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra124_car_barrier - wait for pending writes to the CAR to complete
|
||||
*
|
||||
* Wait for any outstanding writes to the CAR MMIO space from this CPU
|
||||
* to complete before continuing execution. No return value.
|
||||
*/
|
||||
static void tegra124_car_barrier(void)
|
||||
{
|
||||
readl_relaxed(clk_base + RST_DFLL_DVCO);
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra124_clock_assert_dfll_dvco_reset - assert the DFLL's DVCO reset
|
||||
*
|
||||
* Assert the reset line of the DFLL's DVCO. No return value.
|
||||
*/
|
||||
void tegra124_clock_assert_dfll_dvco_reset(void)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
v = readl_relaxed(clk_base + RST_DFLL_DVCO);
|
||||
v |= (1 << DVFS_DFLL_RESET_SHIFT);
|
||||
writel_relaxed(v, clk_base + RST_DFLL_DVCO);
|
||||
tegra124_car_barrier();
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra124_clock_deassert_dfll_dvco_reset - deassert the DFLL's DVCO reset
|
||||
*
|
||||
* Deassert the reset line of the DFLL's DVCO, allowing the DVCO to
|
||||
* operate. No return value.
|
||||
*/
|
||||
void tegra124_clock_deassert_dfll_dvco_reset(void)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
v = readl_relaxed(clk_base + RST_DFLL_DVCO);
|
||||
v &= ~(1 << DVFS_DFLL_RESET_SHIFT);
|
||||
writel_relaxed(v, clk_base + RST_DFLL_DVCO);
|
||||
tegra124_car_barrier();
|
||||
}
|
||||
|
||||
int tegra124_reset_assert(unsigned long id)
|
||||
{
|
||||
if (id == TEGRA124_RST_DFLL_DVCO)
|
||||
tegra124_clock_assert_dfll_dvco_reset();
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tegra124_reset_deassert(unsigned long id)
|
||||
{
|
||||
if (id == TEGRA124_RST_DFLL_DVCO)
|
||||
tegra124_clock_deassert_dfll_dvco_reset();
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra132_clock_apply_init_table - initialize clocks on Tegra132 SoCs
|
||||
*
|
||||
@ -1498,6 +1578,8 @@ static void __init tegra124_132_clock_init_post(struct device_node *np)
|
||||
{
|
||||
tegra_super_clk_gen4_init(clk_base, pmc_base, tegra124_clks,
|
||||
&pll_x_params);
|
||||
tegra_init_special_resets(1, tegra124_reset_assert,
|
||||
tegra124_reset_deassert);
|
||||
tegra_add_of_provider(np);
|
||||
|
||||
clks[TEGRA124_CLK_EMC] = tegra_clk_register_emc(clk_base, np,
|
||||
|
@ -50,7 +50,6 @@
|
||||
#define RST_DEVICES_L 0x004
|
||||
#define RST_DEVICES_H 0x008
|
||||
#define RST_DEVICES_U 0x00C
|
||||
#define RST_DFLL_DVCO 0x2F4
|
||||
#define RST_DEVICES_V 0x358
|
||||
#define RST_DEVICES_W 0x35C
|
||||
#define RST_DEVICES_X 0x28C
|
||||
@ -80,6 +79,11 @@ static struct clk **clks;
|
||||
static int clk_num;
|
||||
static struct clk_onecell_data clk_data;
|
||||
|
||||
/* Handlers for SoC-specific reset lines */
|
||||
static int (*special_reset_assert)(unsigned long);
|
||||
static int (*special_reset_deassert)(unsigned long);
|
||||
static unsigned int num_special_reset;
|
||||
|
||||
static struct tegra_clk_periph_regs periph_regs[] = {
|
||||
[0] = {
|
||||
.enb_reg = CLK_OUT_ENB_L,
|
||||
@ -153,19 +157,29 @@ static int tegra_clk_rst_assert(struct reset_controller_dev *rcdev,
|
||||
*/
|
||||
tegra_read_chipid();
|
||||
|
||||
writel_relaxed(BIT(id % 32),
|
||||
clk_base + periph_regs[id / 32].rst_set_reg);
|
||||
if (id < periph_banks * 32) {
|
||||
writel_relaxed(BIT(id % 32),
|
||||
clk_base + periph_regs[id / 32].rst_set_reg);
|
||||
return 0;
|
||||
} else if (id < periph_banks * 32 + num_special_reset) {
|
||||
return special_reset_assert(id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int tegra_clk_rst_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
writel_relaxed(BIT(id % 32),
|
||||
clk_base + periph_regs[id / 32].rst_clr_reg);
|
||||
if (id < periph_banks * 32) {
|
||||
writel_relaxed(BIT(id % 32),
|
||||
clk_base + periph_regs[id / 32].rst_clr_reg);
|
||||
return 0;
|
||||
} else if (id < periph_banks * 32 + num_special_reset) {
|
||||
return special_reset_deassert(id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct tegra_clk_periph_regs *get_reg_bank(int clkid)
|
||||
@ -287,10 +301,19 @@ void __init tegra_add_of_provider(struct device_node *np)
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
||||
|
||||
rst_ctlr.of_node = np;
|
||||
rst_ctlr.nr_resets = periph_banks * 32;
|
||||
rst_ctlr.nr_resets = periph_banks * 32 + num_special_reset;
|
||||
reset_controller_register(&rst_ctlr);
|
||||
}
|
||||
|
||||
void __init tegra_init_special_resets(unsigned int num,
|
||||
int (*assert)(unsigned long),
|
||||
int (*deassert)(unsigned long))
|
||||
{
|
||||
num_special_reset = num;
|
||||
special_reset_assert = assert;
|
||||
special_reset_deassert = deassert;
|
||||
}
|
||||
|
||||
void __init tegra_register_devclks(struct tegra_devclk *dev_clks, int num)
|
||||
{
|
||||
int i;
|
||||
|
@ -591,6 +591,9 @@ struct tegra_devclk {
|
||||
char *con_id;
|
||||
};
|
||||
|
||||
void tegra_init_special_resets(unsigned int num, int (*assert)(unsigned long),
|
||||
int (*deassert)(unsigned long));
|
||||
|
||||
void tegra_init_from_table(struct tegra_clk_init_table *tbl,
|
||||
struct clk *clks[], int clk_max);
|
||||
|
||||
|
140
drivers/clk/tegra/cvb.c
Normal file
140
drivers/clk/tegra/cvb.c
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Utility functions for parsing Tegra CVB voltage tables
|
||||
*
|
||||
* Copyright (C) 2012-2014 NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pm_opp.h>
|
||||
|
||||
#include "cvb.h"
|
||||
|
||||
/* cvb_mv = ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) */
|
||||
static inline int get_cvb_voltage(int speedo, int s_scale,
|
||||
const struct cvb_coefficients *cvb)
|
||||
{
|
||||
int mv;
|
||||
|
||||
/* apply only speedo scale: output mv = cvb_mv * v_scale */
|
||||
mv = DIV_ROUND_CLOSEST(cvb->c2 * speedo, s_scale);
|
||||
mv = DIV_ROUND_CLOSEST((mv + cvb->c1) * speedo, s_scale) + cvb->c0;
|
||||
return mv;
|
||||
}
|
||||
|
||||
static int round_cvb_voltage(int mv, int v_scale,
|
||||
const struct rail_alignment *align)
|
||||
{
|
||||
/* combined: apply voltage scale and round to cvb alignment step */
|
||||
int uv;
|
||||
int step = (align->step_uv ? : 1000) * v_scale;
|
||||
int offset = align->offset_uv * v_scale;
|
||||
|
||||
uv = max(mv * 1000, offset) - offset;
|
||||
uv = DIV_ROUND_UP(uv, step) * align->step_uv + align->offset_uv;
|
||||
return uv / 1000;
|
||||
}
|
||||
|
||||
enum {
|
||||
DOWN,
|
||||
UP
|
||||
};
|
||||
|
||||
static int round_voltage(int mv, const struct rail_alignment *align, int up)
|
||||
{
|
||||
if (align->step_uv) {
|
||||
int uv;
|
||||
|
||||
uv = max(mv * 1000, align->offset_uv) - align->offset_uv;
|
||||
uv = (uv + (up ? align->step_uv - 1 : 0)) / align->step_uv;
|
||||
return (uv * align->step_uv + align->offset_uv) / 1000;
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
static int build_opp_table(const struct cvb_table *d,
|
||||
int speedo_value,
|
||||
unsigned long max_freq,
|
||||
struct device *opp_dev)
|
||||
{
|
||||
int i, ret, dfll_mv, min_mv, max_mv;
|
||||
const struct cvb_table_freq_entry *table = NULL;
|
||||
const struct rail_alignment *align = &d->alignment;
|
||||
|
||||
min_mv = round_voltage(d->min_millivolts, align, UP);
|
||||
max_mv = round_voltage(d->max_millivolts, align, DOWN);
|
||||
|
||||
for (i = 0; i < MAX_DVFS_FREQS; i++) {
|
||||
table = &d->cvb_table[i];
|
||||
if (!table->freq || (table->freq > max_freq))
|
||||
break;
|
||||
|
||||
/*
|
||||
* FIXME after clk_round_rate/clk_determine_rate prototypes
|
||||
* have been updated
|
||||
*/
|
||||
if (table->freq & (1<<31))
|
||||
continue;
|
||||
|
||||
dfll_mv = get_cvb_voltage(
|
||||
speedo_value, d->speedo_scale, &table->coefficients);
|
||||
dfll_mv = round_cvb_voltage(dfll_mv, d->voltage_scale, align);
|
||||
dfll_mv = clamp(dfll_mv, min_mv, max_mv);
|
||||
|
||||
ret = dev_pm_opp_add(opp_dev, table->freq, dfll_mv * 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_cvb_build_opp_table - build OPP table from Tegra CVB tables
|
||||
* @cvb_tables: array of CVB tables
|
||||
* @sz: size of the previously mentioned array
|
||||
* @process_id: process id of the HW module
|
||||
* @speedo_id: speedo id of the HW module
|
||||
* @speedo_value: speedo value of the HW module
|
||||
* @max_rate: highest safe clock rate
|
||||
* @opp_dev: the struct device * for which the OPP table is built
|
||||
*
|
||||
* On Tegra, a CVB table encodes the relationship between operating voltage
|
||||
* and safe maximal frequency for a given module (e.g. GPU or CPU). This
|
||||
* function calculates the optimal voltage-frequency operating points
|
||||
* for the given arguments and exports them via the OPP library for the
|
||||
* given @opp_dev. Returns a pointer to the struct cvb_table that matched
|
||||
* or an ERR_PTR on failure.
|
||||
*/
|
||||
const struct cvb_table *tegra_cvb_build_opp_table(
|
||||
const struct cvb_table *cvb_tables,
|
||||
size_t sz, int process_id,
|
||||
int speedo_id, int speedo_value,
|
||||
unsigned long max_rate,
|
||||
struct device *opp_dev)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < sz; i++) {
|
||||
const struct cvb_table *d = &cvb_tables[i];
|
||||
|
||||
if (d->speedo_id != -1 && d->speedo_id != speedo_id)
|
||||
continue;
|
||||
if (d->process_id != -1 && d->process_id != process_id)
|
||||
continue;
|
||||
|
||||
ret = build_opp_table(d, speedo_value, max_rate, opp_dev);
|
||||
return ret ? ERR_PTR(ret) : d;
|
||||
}
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
67
drivers/clk/tegra/cvb.h
Normal file
67
drivers/clk/tegra/cvb.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Utility functions for parsing Tegra CVB voltage tables
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_CLK_TEGRA_CVB_H
|
||||
#define __DRIVERS_CLK_TEGRA_CVB_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct device;
|
||||
|
||||
#define MAX_DVFS_FREQS 40
|
||||
|
||||
struct rail_alignment {
|
||||
int offset_uv;
|
||||
int step_uv;
|
||||
};
|
||||
|
||||
struct cvb_coefficients {
|
||||
int c0;
|
||||
int c1;
|
||||
int c2;
|
||||
};
|
||||
|
||||
struct cvb_table_freq_entry {
|
||||
unsigned long freq;
|
||||
struct cvb_coefficients coefficients;
|
||||
};
|
||||
|
||||
struct cvb_cpu_dfll_data {
|
||||
u32 tune0_low;
|
||||
u32 tune0_high;
|
||||
u32 tune1;
|
||||
};
|
||||
|
||||
struct cvb_table {
|
||||
int speedo_id;
|
||||
int process_id;
|
||||
|
||||
int min_millivolts;
|
||||
int max_millivolts;
|
||||
struct rail_alignment alignment;
|
||||
|
||||
int speedo_scale;
|
||||
int voltage_scale;
|
||||
struct cvb_table_freq_entry cvb_table[MAX_DVFS_FREQS];
|
||||
struct cvb_cpu_dfll_data cpu_dfll_data;
|
||||
};
|
||||
|
||||
const struct cvb_table *tegra_cvb_build_opp_table(
|
||||
const struct cvb_table *cvb_tables,
|
||||
size_t sz, int process_id,
|
||||
int speedo_id, int speedo_value,
|
||||
unsigned long max_rate,
|
||||
struct device *opp_dev);
|
||||
|
||||
#endif
|
12
include/dt-bindings/reset/tegra124-car.h
Normal file
12
include/dt-bindings/reset/tegra124-car.h
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* This header provides Tegra124-specific constants for binding
|
||||
* nvidia,tegra124-car.
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_RESET_TEGRA124_CAR_H
|
||||
#define _DT_BINDINGS_RESET_TEGRA124_CAR_H
|
||||
|
||||
#define TEGRA124_RESET(x) (6 * 32 + (x))
|
||||
#define TEGRA124_RST_DFLL_DVCO TEGRA124_RESET(0)
|
||||
|
||||
#endif /* _DT_BINDINGS_RESET_TEGRA124_CAR_H */
|
Loading…
Reference in New Issue
Block a user