diff options
| -rw-r--r-- | Documentation/devicetree/bindings/clock/nvidia,tegra124-dfll.txt | 79 | ||||
| -rw-r--r-- | arch/arm/boot/dts/tegra114.dtsi | 5 | ||||
| -rw-r--r-- | arch/arm/boot/dts/tegra124-jetson-tk1.dts | 25 | ||||
| -rw-r--r-- | arch/arm/boot/dts/tegra124-venice2.dts | 10 | ||||
| -rw-r--r-- | arch/arm/boot/dts/tegra124.dtsi | 50 | ||||
| -rw-r--r-- | arch/arm/boot/dts/tegra20.dtsi | 5 | ||||
| -rw-r--r-- | arch/arm/boot/dts/tegra30.dtsi | 5 | ||||
| -rw-r--r-- | arch/arm/mach-tegra/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/clk/tegra/Makefile | 3 | ||||
| -rw-r--r-- | drivers/clk/tegra/clk-dfll.c | 1755 | ||||
| -rw-r--r-- | drivers/clk/tegra/clk-dfll.h | 54 | ||||
| -rw-r--r-- | drivers/clk/tegra/clk-tegra-super-gen4.c | 4 | ||||
| -rw-r--r-- | drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 166 | ||||
| -rw-r--r-- | drivers/clk/tegra/clk-tegra124.c | 82 | ||||
| -rw-r--r-- | drivers/clk/tegra/clk.c | 39 | ||||
| -rw-r--r-- | drivers/clk/tegra/clk.h | 3 | ||||
| -rw-r--r-- | drivers/clk/tegra/cvb.c | 140 | ||||
| -rw-r--r-- | drivers/clk/tegra/cvb.h | 67 | ||||
| -rw-r--r-- | drivers/pinctrl/pinctrl-tegra.c | 19 | ||||
| -rw-r--r-- | include/dt-bindings/reset/tegra124-car.h | 12 |
20 files changed, 2505 insertions, 19 deletions
diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra124-dfll.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra124-dfll.txt new file mode 100644 index 000000000000..ee7e5fd4a50b --- /dev/null +++ b/Documentation/devicetree/bindings/clock/nvidia,tegra124-dfll.txt @@ -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>; +}; diff --git a/arch/arm/boot/dts/tegra114.dtsi b/arch/arm/boot/dts/tegra114.dtsi index f58a3d9d5f13..9d4f86e9c50a 100644 --- a/arch/arm/boot/dts/tegra114.dtsi +++ b/arch/arm/boot/dts/tegra114.dtsi @@ -214,9 +214,9 @@ #dma-cells = <1>; }; - ahb: ahb@6000c004 { + ahb: ahb@6000c000 { compatible = "nvidia,tegra114-ahb", "nvidia,tegra30-ahb"; - reg = <0x6000c004 0x14c>; + reg = <0x6000c000 0x150>; }; gpio: gpio@6000d000 { @@ -234,6 +234,7 @@ gpio-controller; #interrupt-cells = <2>; interrupt-controller; + gpio-ranges = <&pinmux 0 0 246>; }; apbmisc@70000800 { diff --git a/arch/arm/boot/dts/tegra124-jetson-tk1.dts b/arch/arm/boot/dts/tegra124-jetson-tk1.dts index bd43ed6d6ec7..66b4451eb2ca 100644 --- a/arch/arm/boot/dts/tegra124-jetson-tk1.dts +++ b/arch/arm/boot/dts/tegra124-jetson-tk1.dts @@ -53,6 +53,14 @@ }; }; + gpu@0,57000000 { + /* + * Node left disabled on purpose - the bootloader will enable + * it after having set the VPR up + */ + vdd-supply = <&vdd_gpu>; + }; + pinmux: pinmux@0,70000868 { pinctrl-names = "boot"; pinctrl-0 = <&state_boot>; @@ -1462,7 +1470,7 @@ vin-ldo9-10-supply = <&vdd_5v0_sys>; vin-ldo11-supply = <&vdd_3v3_run>; - sd0 { + vdd_cpu: sd0 { regulator-name = "+VDD_CPU_AP"; regulator-min-microvolt = <700000>; regulator-max-microvolt = <1400000>; @@ -1514,7 +1522,7 @@ regulator-always-on; }; - sd6 { + vdd_gpu: sd6 { regulator-name = "+VDD_GPU_AP"; regulator-min-microvolt = <650000>; regulator-max-microvolt = <1200000>; @@ -1694,6 +1702,13 @@ non-removable; }; + /* CPU DFLL clock */ + clock@0,70110000 { + status = "okay"; + vdd-cpu-supply = <&vdd_cpu>; + nvidia,i2c-fs-rate = <400000>; + }; + ahub@0,70300000 { i2s@0,70301100 { status = "okay"; @@ -1732,6 +1747,12 @@ }; }; + cpus { + cpu@0 { + vdd-cpu-supply = <&vdd_cpu>; + }; + }; + gpio-keys { compatible = "gpio-keys"; diff --git a/arch/arm/boot/dts/tegra124-venice2.dts b/arch/arm/boot/dts/tegra124-venice2.dts index 79e724bb7df7..cfbdf429b45d 100644 --- a/arch/arm/boot/dts/tegra124-venice2.dts +++ b/arch/arm/boot/dts/tegra124-venice2.dts @@ -43,6 +43,14 @@ }; }; + gpu@0,57000000 { + /* + * Node left disabled on purpose - the bootloader will enable + * it after having set the VPR up + */ + vdd-supply = <&vdd_gpu>; + }; + pinmux: pinmux@0,70000868 { pinctrl-names = "boot"; pinctrl-0 = <&pinmux_boot>; @@ -735,7 +743,7 @@ regulator-always-on; }; - sd6 { + vdd_gpu: sd6 { regulator-name = "+VDD_GPU_AP"; regulator-min-microvolt = <650000>; regulator-max-microvolt = <1200000>; diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi index 01a9f742b08f..1e204a6de12c 100644 --- a/arch/arm/boot/dts/tegra124.dtsi +++ b/arch/arm/boot/dts/tegra124.dtsi @@ -4,6 +4,7 @@ #include <dt-bindings/pinctrl/pinctrl-tegra.h> #include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h> #include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/reset/tegra124-car.h> #include <dt-bindings/thermal/tegra124-soctherm.h> #include "skeleton.dtsi" @@ -188,6 +189,9 @@ clock-names = "gpu", "pwr"; resets = <&tegra_car 184>; reset-names = "gpu"; + + iommus = <&mc TEGRA_SWGROUP_GPU>; + status = "disabled"; }; @@ -254,6 +258,7 @@ gpio-controller; #interrupt-cells = <2>; interrupt-controller; + gpio-ranges = <&pinmux 0 0 251>; }; apbdma: dma@0,60020000 { @@ -702,6 +707,30 @@ #thermal-sensor-cells = <1>; }; + dfll: 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"; + nvidia,sample-rate = <12500>; + nvidia,droop-ctrl = <0x00000f00>; + nvidia,force-mode = <1>; + nvidia,cf = <10>; + nvidia,ci = <0>; + nvidia,cg = <2>; + status = "disabled"; + }; + ahub@0,70300000 { compatible = "nvidia,tegra124-ahub"; reg = <0x0 0x70300000 0x0 0x200>, @@ -922,6 +951,15 @@ device_type = "cpu"; compatible = "arm,cortex-a15"; reg = <0>; + + clocks = <&tegra_car TEGRA124_CLK_CCLK_G>, + <&tegra_car TEGRA124_CLK_CCLK_LP>, + <&tegra_car TEGRA124_CLK_PLL_X>, + <&tegra_car TEGRA124_CLK_PLL_P>, + <&dfll>; + clock-names = "cpu_g", "cpu_lp", "pll_x", "pll_p", "dfll"; + /* FIXME: what's the actual transition time? */ + clock-latency = <300000>; }; cpu@1 { @@ -943,6 +981,18 @@ }; }; + pmu { + compatible = "arm,cortex-a15-pmu"; + interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>; + interrupt-affinity = <&{/cpus/cpu@0}>, + <&{/cpus/cpu@1}>, + <&{/cpus/cpu@2}>, + <&{/cpus/cpu@3}>; + }; + thermal-zones { cpu { polling-delay-passive = <1000>; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index f444b67f55c6..e058709e6d98 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -225,9 +225,9 @@ #dma-cells = <1>; }; - ahb@6000c004 { + ahb@6000c000 { compatible = "nvidia,tegra20-ahb"; - reg = <0x6000c004 0x10c>; /* AHB Arbitration + Gizmo Controller */ + reg = <0x6000c000 0x110>; /* AHB Arbitration + Gizmo Controller */ }; gpio: gpio@6000d000 { @@ -244,6 +244,7 @@ gpio-controller; #interrupt-cells = <2>; interrupt-controller; + gpio-ranges = <&pinmux 0 0 224>; }; apbmisc@70000800 { diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index 782b11b2af6a..fe04fb5e155f 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -329,9 +329,9 @@ #dma-cells = <1>; }; - ahb: ahb@6000c004 { + ahb: ahb@6000c000 { compatible = "nvidia,tegra30-ahb"; - reg = <0x6000c004 0x14c>; /* AHB Arbitration + Gizmo Controller */ + reg = <0x6000c000 0x150>; /* AHB Arbitration + Gizmo Controller */ }; gpio: gpio@6000d000 { @@ -349,6 +349,7 @@ gpio-controller; #interrupt-cells = <2>; interrupt-controller; + gpio-ranges = <&pinmux 0 0 248>; }; apbmisc@70000800 { diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 5d1a318f1302..0fa4c5f8b1be 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -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 diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index aec862ba7a17..826c325dc2e8 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -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 diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c new file mode 100644 index 000000000000..109a79b95238 --- /dev/null +++ b/drivers/clk/tegra/clk-dfll.c @@ -0,0 +1,1755 @@ +/* + * clk-dfll.c - Tegra DFLL clock source common code + * + * 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. + * + * This library is for the DVCO and DFLL IP blocks on the Tegra124 + * SoC. These IP blocks together are also known at NVIDIA as + * "CL-DVFS". To try to avoid confusion, this code refers to them + * collectively as the "DFLL." + * + * The DFLL is a root clocksource which tolerates some amount of + * supply voltage noise. Tegra124 uses it to clock the fast CPU + * complex when the target CPU speed is above a particular rate. The + * DFLL can be operated in either open-loop mode or closed-loop mode. + * In open-loop mode, the DFLL generates an output clock appropriate + * to the supply voltage. In closed-loop mode, when configured with a + * target frequency, the DFLL minimizes supply voltage while + * delivering an average frequency equal to the target. + * + * Devices clocked by the DFLL must be able to tolerate frequency + * variation. In the case of the CPU, it's important to note that the + * CPU cycle time will vary. This has implications for + * performance-measurement code and any code that relies on the CPU + * cycle time to delay for a certain length of time. + * + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pm_opp.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/seq_file.h> + +#include "clk-dfll.h" + +/* + * DFLL control registers - access via dfll_{readl,writel} + */ + +/* DFLL_CTRL: DFLL control register */ +#define DFLL_CTRL 0x00 +#define DFLL_CTRL_MODE_MASK 0x03 + +/* DFLL_CONFIG: DFLL sample rate control */ +#define DFLL_CONFIG 0x04 +#define DFLL_CONFIG_DIV_MASK 0xff +#define DFLL_CONFIG_DIV_PRESCALE 32 + +/* DFLL_PARAMS: tuning coefficients for closed loop integrator */ +#define DFLL_PARAMS 0x08 +#define DFLL_PARAMS_CG_SCALE (0x1 << 24) +#define DFLL_PARAMS_FORCE_MODE_SHIFT 22 +#define DFLL_PARAMS_FORCE_MODE_MASK (0x3 << DFLL_PARAMS_FORCE_MODE_SHIFT) +#define DFLL_PARAMS_CF_PARAM_SHIFT 16 +#define DFLL_PARAMS_CF_PARAM_MASK (0x3f << DFLL_PARAMS_CF_PARAM_SHIFT) +#define DFLL_PARAMS_CI_PARAM_SHIFT 8 +#define DFLL_PARAMS_CI_PARAM_MASK (0x7 << DFLL_PARAMS_CI_PARAM_SHIFT) +#define DFLL_PARAMS_CG_PARAM_SHIFT 0 +#define DFLL_PARAMS_CG_PARAM_MASK (0xff << DFLL_PARAMS_CG_PARAM_SHIFT) + +/* DFLL_TUNE0: delay line configuration register 0 */ +#define DFLL_TUNE0 0x0c + +/* DFLL_TUNE1: delay line configuration register 1 */ +#define DFLL_TUNE1 0x10 + +/* DFLL_FREQ_REQ: target DFLL frequency control */ +#define DFLL_FREQ_REQ 0x14 +#define DFLL_FREQ_REQ_FORCE_ENABLE (0x1 << 28) +#define DFLL_FREQ_REQ_FORCE_SHIFT 16 +#define DFLL_FREQ_REQ_FORCE_MASK (0xfff << DFLL_FREQ_REQ_FORCE_SHIFT) +#define FORCE_MAX 2047 +#define FORCE_MIN -2048 +#define DFLL_FREQ_REQ_SCALE_SHIFT 8 +#define DFLL_FREQ_REQ_SCALE_MASK (0xff << DFLL_FREQ_REQ_SCALE_SHIFT) +#define DFLL_FREQ_REQ_SCALE_MAX 256 +#define DFLL_FREQ_REQ_FREQ_VALID (0x1 << 7) +#define DFLL_FREQ_REQ_MULT_SHIFT 0 +#define DFLL_FREQ_REG_MULT_MASK (0x7f << DFLL_FREQ_REQ_MULT_SHIFT) +#define FREQ_MAX 127 + +/* DFLL_DROOP_CTRL: droop prevention control */ +#define DFLL_DROOP_CTRL 0x1c + +/* DFLL_OUTPUT_CFG: closed loop mode control registers */ +/* NOTE: access via dfll_i2c_{readl,writel} */ +#define DFLL_OUTPUT_CFG 0x20 +#define DFLL_OUTPUT_CFG_I2C_ENABLE (0x1 << 30) +#define OUT_MASK 0x3f +#define DFLL_OUTPUT_CFG_SAFE_SHIFT 24 +#define DFLL_OUTPUT_CFG_SAFE_MASK \ + (OUT_MASK << DFLL_OUTPUT_CFG_SAFE_SHIFT) +#define DFLL_OUTPUT_CFG_MAX_SHIFT 16 +#define DFLL_OUTPUT_CFG_MAX_MASK \ + (OUT_MASK << DFLL_OUTPUT_CFG_MAX_SHIFT) +#define DFLL_OUTPUT_CFG_MIN_SHIFT 8 +#define DFLL_OUTPUT_CFG_MIN_MASK \ + (OUT_MASK << DFLL_OUTPUT_CFG_MIN_SHIFT) +#define DFLL_OUTPUT_CFG_PWM_DELTA (0x1 << 7) +#define DFLL_OUTPUT_CFG_PWM_ENABLE (0x1 << 6) +#define DFLL_OUTPUT_CFG_PWM_DIV_SHIFT 0 +#define DFLL_OUTPUT_CFG_PWM_DIV_MASK \ + (OUT_MASK << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT) + +/* DFLL_OUTPUT_FORCE: closed loop mode voltage forcing control */ +#define DFLL_OUTPUT_FORCE 0x24 +#define DFLL_OUTPUT_FORCE_ENABLE (0x1 << 6) +#define DFLL_OUTPUT_FORCE_VALUE_SHIFT 0 +#define DFLL_OUTPUT_FORCE_VALUE_MASK \ + (OUT_MASK << DFLL_OUTPUT_FORCE_VALUE_SHIFT) + +/* DFLL_MONITOR_CTRL: internal monitor data source control */ +#define DFLL_MONITOR_CTRL 0x28 +#define DFLL_MONITOR_CTRL_FREQ 6 + +/* DFLL_MONITOR_DATA: internal monitor data output */ +#define DFLL_MONITOR_DATA 0x2c +#define DFLL_MONITOR_DATA_NEW_MASK (0x1 << 16) +#define DFLL_MONITOR_DATA_VAL_SHIFT 0 +#define DFLL_MONITOR_DATA_VAL_MASK (0xFFFF << DFLL_MONITOR_DATA_VAL_SHIFT) + +/* + * I2C output control registers - access via dfll_i2c_{readl,writel} + */ + +/* DFLL_I2C_CFG: I2C controller configuration register */ +#define DFLL_I2C_CFG 0x40 +#define DFLL_I2C_CFG_ARB_ENABLE (0x1 << 20) +#define DFLL_I2C_CFG_HS_CODE_SHIFT 16 +#define DFLL_I2C_CFG_HS_CODE_MASK (0x7 << DFLL_I2C_CFG_HS_CODE_SHIFT) +#define DFLL_I2C_CFG_PACKET_ENABLE (0x1 << 15) +#define DFLL_I2C_CFG_SIZE_SHIFT 12 +#define DFLL_I2C_CFG_SIZE_MASK (0x7 << DFLL_I2C_CFG_SIZE_SHIFT) +#define DFLL_I2C_CFG_SLAVE_ADDR_10 (0x1 << 10) +#define DFLL_I2C_CFG_SLAVE_ADDR_SHIFT_7BIT 1 +#define DFLL_I2C_CFG_SLAVE_ADDR_SHIFT_10BIT 0 + +/* DFLL_I2C_VDD_REG_ADDR: PMIC I2C address for closed loop mode */ +#define DFLL_I2C_VDD_REG_ADDR 0x44 + +/* DFLL_I2C_STS: I2C controller status */ +#define DFLL_I2C_STS 0x48 +#define DFLL_I2C_STS_I2C_LAST_SHIFT 1 +#define DFLL_I2C_STS_I2C_REQ_PENDING 0x1 + +/* DFLL_INTR_STS: DFLL interrupt status register */ +#define DFLL_INTR_STS 0x5c + +/* DFLL_INTR_EN: DFLL interrupt enable register */ +#define DFLL_INTR_EN 0x60 +#define DFLL_INTR_MIN_MASK 0x1 +#define DFLL_INTR_MAX_MASK 0x2 + +/* + * Integrated I2C controller registers - relative to td->i2c_controller_base + */ + +/* DFLL_I2C_CLK_DIVISOR: I2C controller clock divisor */ +#define DFLL_I2C_CLK_DIVISOR 0x6c +#define DFLL_I2C_CLK_DIVISOR_MASK 0xffff +#define DFLL_I2C_CLK_DIVISOR_FS_SHIFT 16 +#define DFLL_I2C_CLK_DIVISOR_HS_SHIFT 0 +#define DFLL_I2C_CLK_DIVISOR_PREDIV 8 +#define DFLL_I2C_CLK_DIVISOR_HSMODE_PREDIV 12 + +/* + * Other constants + */ + +/* MAX_DFLL_VOLTAGES: number of LUT entries in the DFLL IP block */ +#define MAX_DFLL_VOLTAGES 33 + +/* + * REF_CLK_CYC_PER_DVCO_SAMPLE: the number of ref_clk cycles that the hardware + * integrates the DVCO counter over - used for debug rate monitoring and + * droop control + */ +#define REF_CLK_CYC_PER_DVCO_SAMPLE 4 + +/* + * REF_CLOCK_RATE: the DFLL reference clock rate currently supported by this + * driver, in Hz + */ +#define REF_CLOCK_RATE 51000000UL + +#define DVCO_RATE_TO_MULT(rate, ref_rate) ((rate) / ((ref_rate) / 2)) +#define MULT_TO_DVCO_RATE(mult, ref_rate) ((mult) * ((ref_rate) / 2)) + +/** + * enum dfll_ctrl_mode - DFLL hardware operating mode + * @DFLL_UNINITIALIZED: (uninitialized state - not in hardware bitfield) + * @DFLL_DISABLED: DFLL not generating an output clock + * @DFLL_OPEN_LOOP: DVCO running, but DFLL not adjusting voltage + * @DFLL_CLOSED_LOOP: DVCO running, and DFLL adjusting voltage to match + * the requested rate + * + * The integer corresponding to the last two states, minus one, is + * written to the DFLL hardware to change operating modes. + */ +enum dfll_ctrl_mode { + DFLL_UNINITIALIZED = 0, + DFLL_DISABLED = 1, + DFLL_OPEN_LOOP = 2, + DFLL_CLOSED_LOOP = 3, +}; + +/** + * enum dfll_tune_range - voltage range that the driver believes it's in + * @DFLL_TUNE_UNINITIALIZED: DFLL tuning not yet programmed + * @DFLL_TUNE_LOW: DFLL in the low-voltage range (or open-loop mode) + * + * Some DFLL tuning parameters may need to change depending on the + * DVCO's voltage; these states represent the ranges that the driver + * supports. These are software states; these values are never + * written into registers. + */ +enum dfll_tune_range { + DFLL_TUNE_UNINITIALIZED = 0, + DFLL_TUNE_LOW = 1, +}; + +/** + * struct dfll_rate_req - target DFLL rate request data + * @rate: target frequency, after the postscaling + * @dvco_target_rate: target frequency, after the postscaling + * @lut_index: LUT index at which voltage the dvco_target_rate will be reached + * @mult_bits: value to program to the MULT bits of the DFLL_FREQ_REQ register + * @scale_bits: value to program to the SCALE bits of the DFLL_FREQ_REQ register + */ +struct dfll_rate_req { + unsigned long rate; + unsigned long dvco_target_rate; + int lut_index; + u8 mult_bits; + u8 scale_bits; +}; + +struct tegra_dfll { + struct device *dev; + struct tegra_dfll_soc_data *soc; + + void __iomem *base; + void __iomem *i2c_base; + void __iomem *i2c_controller_base; + void __iomem *lut_base; + + struct regulator *vdd_reg; + struct clk *soc_clk; + struct clk *ref_clk; + struct clk *i2c_clk; + struct clk *dfll_clk; + struct reset_control *dvco_rst; + unsigned long ref_rate; + unsigned long i2c_clk_rate; + unsigned long dvco_rate_min; + + enum dfll_ctrl_mode mode; + enum dfll_tune_range tune_range; + struct dentry *debugfs_dir; + struct clk_hw dfll_clk_hw; + const char *output_clock_name; + struct dfll_rate_req last_req; + unsigned long last_unrounded_rate; + + /* Parameters from DT */ + u32 droop_ctrl; + u32 sample_rate; + u32 force_mode; + u32 cf; + u32 ci; + u32 cg; + bool cg_scale; + + /* I2C interface parameters */ + u32 i2c_fs_rate; + u32 i2c_reg; + u32 i2c_slave_addr; + + /* i2c_lut array entries are regulator framework selectors */ + unsigned i2c_lut[MAX_DFLL_VOLTAGES]; + int i2c_lut_size; + u8 lut_min, lut_max, lut_safe; +}; + +#define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw) + +/* mode_name: map numeric DFLL modes to names for friendly console messages */ +static const char * const mode_name[] = { + [DFLL_UNINITIALIZED] = "uninitialized", + [DFLL_DISABLED] = "disabled", + [DFLL_OPEN_LOOP] = "open_loop", + [DFLL_CLOSED_LOOP] = "closed_loop", +}; + +/* + * Register accessors + */ + +static inline u32 dfll_readl(struct tegra_dfll *td, u32 offs) +{ + return __raw_readl(td->base + offs); +} + +static inline void dfll_writel(struct tegra_dfll *td, u32 val, u32 offs) +{ + WARN_ON(offs >= DFLL_I2C_CFG); + __raw_writel(val, td->base + offs); +} + +static inline void dfll_wmb(struct tegra_dfll *td) +{ + dfll_readl(td, DFLL_CTRL); +} + +/* I2C output control registers - for addresses above DFLL_I2C_CFG */ + +static inline u32 dfll_i2c_readl(struct tegra_dfll *td, u32 offs) +{ + return __raw_readl(td->i2c_base + offs); +} + +static inline void dfll_i2c_writel(struct tegra_dfll *td, u32 val, u32 offs) +{ + __raw_writel(val, td->i2c_base + offs); +} + +static inline void dfll_i2c_wmb(struct tegra_dfll *td) +{ + dfll_i2c_readl(td, DFLL_I2C_CFG); +} + +/** + * dfll_is_running - is the DFLL currently generating a clock? + * @td: DFLL instance + * + * If the DFLL is currently generating an output clock signal, return + * true; otherwise return false. + */ +static bool dfll_is_running(struct tegra_dfll *td) +{ + return td->mode >= DFLL_OPEN_LOOP; +} + +/* + * Runtime PM suspend/resume callbacks + */ + +/** + * tegra_dfll_runtime_resume - enable all clocks needed by the DFLL + * @dev: DFLL device * + * + * Enable all clocks needed by the DFLL. Assumes that clk_prepare() + * has already been called on all the clocks. + * + * XXX Should also handle context restore when returning from off. + */ +int tegra_dfll_runtime_resume(struct device *dev) +{ + struct tegra_dfll *td = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(td->ref_clk); + if (ret) { + dev_err(dev, "could not enable ref clock: %d\n", ret); + return ret; + } + + ret = clk_enable(td->soc_clk); + if (ret) { + dev_err(dev, "could not enable register clock: %d\n", ret); + clk_disable(td->ref_clk); + return ret; + } + + ret = clk_enable(td->i2c_clk); + if (ret) { + dev_err(dev, "could not enable i2c clock: %d\n", ret); + clk_disable(td->soc_clk); + clk_disable(td->ref_clk); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(tegra_dfll_runtime_resume); + +/** + * tegra_dfll_runtime_suspend - disable all clocks needed by the DFLL + * @dev: DFLL device * + * + * Disable all clocks needed by the DFLL. Assumes that other code + * will later call clk_unprepare(). + */ +int tegra_dfll_runtime_suspend(struct device *dev) +{ + struct tegra_dfll *td = dev_get_drvdata(dev); + + clk_disable(td->ref_clk); + clk_disable(td->soc_clk); + clk_disable(td->i2c_clk); + + return 0; +} +EXPORT_SYMBOL(tegra_dfll_runtime_suspend); + +/* + * DFLL tuning operations (per-voltage-range tuning settings) + */ + +/** + * dfll_tune_low - tune to DFLL and CPU settings valid for any voltage + * @td: DFLL instance + * + * Tune the DFLL oscillator parameters and the CPU clock shaper for + * the low-voltage range. These settings are valid for any voltage, + * but may not be optimal. + */ +static void dfll_tune_low(struct tegra_dfll *td) +{ + td->tune_range = DFLL_TUNE_LOW; + + dfll_writel(td, td->soc->tune0_low, DFLL_TUNE0); + dfll_writel(td, td->soc->tune1, DFLL_TUNE1); + dfll_wmb(td); + + if (td->soc->set_clock_trimmers_low) + td->soc->set_clock_trimmers_low(); +} + +/* + * Output clock scaler helpers + */ + +/** + * dfll_scale_dvco_rate - calculate scaled rate from the DVCO rate + * @scale_bits: clock scaler value (bits in the DFLL_FREQ_REQ_SCALE field) + * @dvco_rate: the DVCO rate + * + * Apply the same scaling formula that the DFLL hardware uses to scale + * the DVCO rate. + */ +static unsigned long dfll_scale_dvco_rate(int scale_bits, + unsigned long dvco_rate) +{ + return (u64)dvco_rate * (scale_bits + 1) / DFLL_FREQ_REQ_SCALE_MAX; +} + +/* + * Monitor control + */ + +/** + * dfll_calc_monitored_rate - convert DFLL_MONITOR_DATA_VAL rate into real freq + * @monitor_data: value read from the DFLL_MONITOR_DATA_VAL bitfield + * @ref_rate: DFLL reference clock rate + * + * Convert @monitor_data from DFLL_MONITOR_DATA_VAL units into cycles + * per second. Returns the converted value. + */ +static u64 dfll_calc_monitored_rate(u32 monitor_data, + unsigned long ref_rate) +{ + return monitor_data * (ref_rate / REF_CLK_CYC_PER_DVCO_SAMPLE); +} + +/** + * dfll_read_monitor_rate - return the DFLL's output rate from internal monitor + * @td: DFLL instance + * + * If the DFLL is enabled, return the last rate reported by the DFLL's + * internal monitoring hardware. This works in both open-loop and + * closed-loop mode, and takes the output scaler setting into account. + * Assumes that the monitor was programmed to monitor frequency before + * the sample period started. If the driver believes that the DFLL is + * currently uninitialized or disabled, it will return 0, since + * otherwise the DFLL monitor data register will return the last + * measured rate from when the DFLL was active. + */ +static u64 dfll_read_monitor_rate(struct tegra_dfll *td) +{ + u32 v, s; + u64 pre_scaler_rate, post_scaler_rate; + + if (!dfll_is_running(td)) + return 0; + + v = dfll_readl(td, DFLL_MONITOR_DATA); + v = (v & DFLL_MONITOR_DATA_VAL_MASK) >> DFLL_MONITOR_DATA_VAL_SHIFT; + pre_scaler_rate = dfll_calc_monitored_rate(v, td->ref_rate); + + s = dfll_readl(td, DFLL_FREQ_REQ); + s = (s & DFLL_FREQ_REQ_SCALE_MASK) >> DFLL_FREQ_REQ_SCALE_SHIFT; + post_scaler_rate = dfll_scale_dvco_rate(s, pre_scaler_rate); + + return post_scaler_rate; +} + +/* + * DFLL mode switching + */ + +/** + * dfll_set_mode - change the DFLL control mode + * @td: DFLL instance + * @mode: DFLL control mode (see enum dfll_ctrl_mode) + * + * Change the DFLL's operating mode between disabled, open-loop mode, + * and closed-loop mode, or vice versa. + */ +static void dfll_set_mode(struct tegra_dfll *td, + enum dfll_ctrl_mode mode) +{ + td->mode = mode; + dfll_writel(td, mode - 1, DFLL_CTRL); + dfll_wmb(td); +} + |
