linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/6] clk: tegra: add basic support for the DFLL clocksource
@ 2013-12-19 12:36 Paul Walmsley
  2013-12-19 12:36 ` [PATCH 1/6] ARM: tegra: fuse: add functions to read speedo ID and process ID Paul Walmsley
                   ` (5 more replies)
  0 siblings, 6 replies; 19+ messages in thread
From: Paul Walmsley @ 2013-12-19 12:36 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

This patch series adds support for the open-loop mode of the
Tegra DFLL root clock source.  The DFLL can be used to provide an
energy-efficient and reliable clock signal for the Tegra114 fast
CPU cluster.

The core DFLL code requires some SoC-specific integration and
characterization data to function.  For the time being, this has
been implemented via small shim drivers that are probed from DT
data and which pass per-SoC information to the DFLL core code.
Future patches will add board tuning data to the DT files.

This series isn't sufficient for the DFLL to be usable as the
fast CPU clock source.  Further software support is needed to
switch the CPU cluster's clock from the default PLLX to the DFLL.

To test the basic functionality of the DFLL driver, something
like the following can be executed:

mount -t debugfs none /debug
cd /debug/tegra_dfll_fcpu/
echo 0 > enable
cat output_rate   # ensure that it is 0
echo 1 > enable
cat output_rate   # ensure that it is greater than 0

Future DFLL patches are planned to include reset controller
support, Tegra124 support, and closed-loop mode support.


- Paul

---

Danny Huang (1):
      ARM: tegra: fuse: add functions to read speedo ID and process ID

Paul Walmsley (5):
      ARM: tegra114: fuse: add DFLL FCPU minimum voltage override test function
      clk: tegra: add library for the DFLL clocksource (open-loop mode)
      ARM: DTS: tegra: add the DFLL IP block to the T114 SoC file
      ARM: DTS: tegra: add DFLL integration to the Dalmore DTS file
      clk: tegra: add Tegra114 FCPU DFLL clocksource platform driver


 .../bindings/clock/nvidia,tegra114-dfll.txt        |   59 +
 arch/arm/boot/dts/tegra114-dalmore.dts             |    4 
 arch/arm/boot/dts/tegra114.dtsi                    |   10 
 arch/arm/mach-tegra/fuse.c                         |   41 +
 drivers/clk/Kconfig                                |    2 
 drivers/clk/tegra/Kconfig                          |   18 
 drivers/clk/tegra/Makefile                         |   11 
 drivers/clk/tegra/clk-dfll.c                       | 1229 ++++++++++++++++++++
 drivers/clk/tegra/clk-dfll.h                       |   54 +
 drivers/clk/tegra/clk-tegra114-dfll-fcpu.c         |  169 +++
 include/linux/tegra-soc.h                          |    6 
 11 files changed, 1602 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt
 create mode 100644 drivers/clk/tegra/Kconfig
 create mode 100644 drivers/clk/tegra/clk-dfll.c
 create mode 100644 drivers/clk/tegra/clk-dfll.h
 create mode 100644 drivers/clk/tegra/clk-tegra114-dfll-fcpu.c

   text    data     bss     dec     hex filename
8010955  394200  352064 8757219  859fe3 vmlinux.next-20131218
8014811  395096  352064 8761971  85b273 vmlinux.next-20131218-dfll

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 1/6] ARM: tegra: fuse: add functions to read speedo ID and process ID
  2013-12-19 12:36 [PATCH 0/6] clk: tegra: add basic support for the DFLL clocksource Paul Walmsley
@ 2013-12-19 12:36 ` Paul Walmsley
  2013-12-19 23:09   ` Stephen Warren
  2013-12-19 12:36 ` [PATCH 2/6] ARM: tegra114: fuse: add DFLL FCPU minimum voltage override test function Paul Walmsley
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: Paul Walmsley @ 2013-12-19 12:36 UTC (permalink / raw)
  To: linux-arm-kernel

From: Danny Huang <dahuang@nvidia.com>

Add functions to read the speedo and process id of both the CPU and the SoC.
There might be some drivers need the information as well.

Signed-off-by: Danny Huang <dahuang@nvidia.com>
[pwalmsley at nvidia.com: cleaned up the patch description; removed functions
 not used by the DFLL code for now]
Signed-off-by: Paul Walmsley <pwalmsley@nvidia.com>
---
 arch/arm/mach-tegra/fuse.c |   15 +++++++++++++++
 include/linux/tegra-soc.h  |    4 +++-
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c
index c9ac23b385be..e3ca8abe858f 100644
--- a/arch/arm/mach-tegra/fuse.c
+++ b/arch/arm/mach-tegra/fuse.c
@@ -250,3 +250,18 @@ void __init tegra_init_fuse(void)
 		tegra_sku_id, tegra_cpu_process_id,
 		tegra_core_process_id);
 }
+
+int tegra_get_cpu_process_id(void)
+{
+	return tegra_cpu_process_id;
+}
+EXPORT_SYMBOL(tegra_get_cpu_process_id);
+
+int tegra_get_cpu_speedo_id(void)
+{
+	if (tegra_chip_id == TEGRA20)
+		return -EINVAL;
+
+	return tegra_cpu_speedo_id;
+}
+EXPORT_SYMBOL(tegra_get_cpu_speedo_id);
diff --git a/include/linux/tegra-soc.h b/include/linux/tegra-soc.h
index 95f611d78f3a..ec856fdf2ce7 100644
--- a/include/linux/tegra-soc.h
+++ b/include/linux/tegra-soc.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2013 NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -18,5 +18,7 @@
 #define __LINUX_TEGRA_SOC_H_
 
 u32 tegra_read_chipid(void);
+int tegra_get_cpu_process_id(void);
+int tegra_get_cpu_speedo_id(void);
 
 #endif /* __LINUX_TEGRA_SOC_H_ */

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 2/6] ARM: tegra114: fuse: add DFLL FCPU minimum voltage override test function
  2013-12-19 12:36 [PATCH 0/6] clk: tegra: add basic support for the DFLL clocksource Paul Walmsley
  2013-12-19 12:36 ` [PATCH 1/6] ARM: tegra: fuse: add functions to read speedo ID and process ID Paul Walmsley
@ 2013-12-19 12:36 ` Paul Walmsley
  2013-12-19 23:12   ` Stephen Warren
  2013-12-19 12:37 ` [PATCH 3/6] clk: tegra: add library for the DFLL clocksource (open-loop mode) Paul Walmsley
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: Paul Walmsley @ 2013-12-19 12:36 UTC (permalink / raw)
  To: linux-arm-kernel

Add a function to test the internal Tegra114 chip fuse that indicates
whether the VDD_CPU lower voltage limit for the fast CPU cluster
should be overridden.

Signed-off-by: Paul Walmsley <pwalmsley@nvidia.com>
Cc: Stephen Warren <swarren@wwwdotorg.org>
Cc: Thierry Reding <thierry.reding@gmail.com>
---
 arch/arm/mach-tegra/fuse.c |   26 ++++++++++++++++++++++++++
 include/linux/tegra-soc.h  |    2 ++
 2 files changed, 28 insertions(+)

diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c
index e3ca8abe858f..42477aa1b7e9 100644
--- a/arch/arm/mach-tegra/fuse.c
+++ b/arch/arm/mach-tegra/fuse.c
@@ -47,6 +47,15 @@
 #define TEGRA20_FUSE_SPARE_BIT		0x200
 #define TEGRA30_FUSE_SPARE_BIT		0x244
 
+/*
+ * TEGRA114_SPARE_FUSE_VDD_CPU_OVERRIDE: Spare eFuse ID that indicates
+ * whether the minimum VDD_CPU voltage level that the DFLL is
+ * characterized for should be overridden to 900mVdc.  If unset, the
+ * voltage shouldn't be overridden - it will be whatever has been
+ * selected by the CPU Speedo and process IDs.
+ */
+#define TEGRA114_SPARE_FUSE_VDD_CPU_OVERRIDE		61
+
 int tegra_sku_id;
 int tegra_cpu_process_id;
 int tegra_core_process_id;
@@ -265,3 +274,20 @@ int tegra_get_cpu_speedo_id(void)
 	return tegra_cpu_speedo_id;
 }
 EXPORT_SYMBOL(tegra_get_cpu_speedo_id);
+
+/**
+ * tegra114_fuse_read_min_vdd_cpu_override - override DFLL VDD_CPU minimum?
+ *
+ * Returns true if the minimum VDD_CPU voltage level that the DFLL is
+ * characterized for is 900mVdc.  If false, the minimum voltage should
+ * be whatever has been selected by the CPU Speedo and process IDs.
+ */
+bool tegra114_fuse_read_min_vdd_cpu_override(void)
+{
+	if (tegra_chip_id != TEGRA114)
+		return -EINVAL;
+
+	return tegra_spare_fuse(TEGRA114_SPARE_FUSE_VDD_CPU_OVERRIDE) ? true :
+		false;
+}
+EXPORT_SYMBOL(tegra114_fuse_read_min_vdd_cpu_override);
diff --git a/include/linux/tegra-soc.h b/include/linux/tegra-soc.h
index ec856fdf2ce7..fe2ab641555e 100644
--- a/include/linux/tegra-soc.h
+++ b/include/linux/tegra-soc.h
@@ -21,4 +21,6 @@ u32 tegra_read_chipid(void);
 int tegra_get_cpu_process_id(void);
 int tegra_get_cpu_speedo_id(void);
 
+bool tegra114_fuse_read_min_vdd_cpu_override(void);
+
 #endif /* __LINUX_TEGRA_SOC_H_ */

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 3/6] clk: tegra: add library for the DFLL clocksource (open-loop mode)
  2013-12-19 12:36 [PATCH 0/6] clk: tegra: add basic support for the DFLL clocksource Paul Walmsley
  2013-12-19 12:36 ` [PATCH 1/6] ARM: tegra: fuse: add functions to read speedo ID and process ID Paul Walmsley
  2013-12-19 12:36 ` [PATCH 2/6] ARM: tegra114: fuse: add DFLL FCPU minimum voltage override test function Paul Walmsley
@ 2013-12-19 12:37 ` Paul Walmsley
  2013-12-19 23:57   ` Stephen Warren
  2013-12-19 12:49 ` [PATCH 4/6] ARM: DTS: tegra: add the DFLL IP block to the T114 SoC file Paul Walmsley
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: Paul Walmsley @ 2013-12-19 12:37 UTC (permalink / raw)
  To: linux-arm-kernel

Add shared code to support the Tegra DFLL clocksource in open-loop
mode.  This root clocksource is present on the Tegra114 and Tegra124
SoCs.  It's used to generate a clock for the fast CPU cluster.  Future
patches will add support for closed-loop mode.

This code was originally created by Aleksandr Frid <afrid@nvidia.com>.
It's been converted to use DT and integrated into the common clock
framework, and a few minor functional changes have been implemented.

Subsequent patches will add drivers for the Tegra114 and Tegra124 fast
CPU cluster DFLL devices, which rely on this code.

Signed-off-by: Paul Walmsley <pwalmsley@nvidia.com>
Cc: Aleksandr Frid <afrid@nvidia.com>
Cc: Matthew Longnecker <mlongnecker@nvidia.com>
Cc: Tezaswi Raja <traja@nvidia.com>
Cc: Peter De Schrijver <pdeschrijver@nvidia.com>
Cc: Prashant Gaikwad <pgaikwad@nvidia.com>
---
 drivers/clk/Kconfig          |    2 
 drivers/clk/tegra/Kconfig    |    9 
 drivers/clk/tegra/Makefile   |    8 
 drivers/clk/tegra/clk-dfll.c | 1229 ++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/tegra/clk-dfll.h |   54 ++
 5 files changed, 1302 insertions(+)
 create mode 100644 drivers/clk/tegra/Kconfig
 create mode 100644 drivers/clk/tegra/clk-dfll.c
 create mode 100644 drivers/clk/tegra/clk-dfll.h

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 3089f05ba661..ecf004e71a75 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -117,6 +117,8 @@ config COMMON_CLK_KEYSTONE
           Supports clock drivers for Keystone based SOCs. These SOCs have local
 	  a power sleep control module that gate the clock to the IPs and PLLs.
 
+source "drivers/clk/tegra/Kconfig"
+
 endmenu
 
 source "drivers/clk/mvebu/Kconfig"
diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig
new file mode 100644
index 000000000000..5c5d1a841ef8
--- /dev/null
+++ b/drivers/clk/tegra/Kconfig
@@ -0,0 +1,9 @@
+menu "Tegra clock support"
+	depends on COMMON_CLK
+
+config CLK_TEGRA_DFLL
+	tristate
+	depends on COMMON_CLK
+
+endmenu
+
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
index f7dfb72884a4..03277f640bfe 100644
--- a/drivers/clk/tegra/Makefile
+++ b/drivers/clk/tegra/Makefile
@@ -15,3 +15,11 @@ 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_CLK_TEGRA_DFLL)		+= clk-dfll.o
+
+# XXX -Wno-sign-compare is only needed due to problems with
+# some non-clock-related Linux header files, and can be removed
+# once those headers are fixed
+CFLAGS_clk-dfll.o			+= -Wall -Wextra -Wno-unused-parameter \
+					   -Wno-sign-compare
+
diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
new file mode 100644
index 000000000000..83a907fd4798
--- /dev/null
+++ b/drivers/clk/tegra/clk-dfll.c
@@ -0,0 +1,1229 @@
+/*
+ * clk-dfll.c - Tegra DFLL clock source common code
+ *
+ * Copyright (C) 2012-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.
+ *
+ * ...
+ *
+ * This library is for the DVCO and DFLL IP blocks on the Tegra114
+ * 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.  Tegra114 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.  (This driver
+ * currently only supports open-loop mode.)
+ *
+ * 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.
+ *
+ * To do:
+ * - Missing kerneldoc on the debugfs functions
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/tegra-soc.h>
+
+#include "clk.h"
+#include "clk-dfll.h"
+
+/*
+ * DFLL register offset & bitfield macros
+ */
+
+/* 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_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_FREQ_SHIFT	0
+#define DFLL_FREQ_REQ_FREQ_MASK		(0x7f << DFLL_FREQ_REQ_FREQ_SHIFT)
+#define FREQ_MAX			127
+
+/* DFLL_DROOP_CTRL: droop prevention control */
+#define DFLL_DROOP_CTRL			0x1c
+
+/* DFLL_MONITOR_CTRL: loop data source control */
+#define DFLL_MONITOR_CTRL		0x28
+#define DFLL_MONITOR_CTRL_FREQ		6
+
+/* DFLL_MONITOR_DATA: internal monitoring */
+#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)
+
+/* 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	0
+#define DFLL_I2C_CFG_SLAVE_ADDR_MASK	(0x3ff << DFLL_I2C_CFG_SLAVE_ADDR_SHIFT)
+
+/* 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
+
+/* DFLL_I2C_CLK_DIVISOR: I2C controller clock divisor */
+#define DFLL_I2C_CLK_DIVISOR		0x16c
+#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
+
+/*
+ * DFLL_OUTPUT_LUT: Offset to the start of the 33x8-bit voltage lookup
+ *     table. 32-bit aligned.  Used in I2C mode only.
+ */
+#define DFLL_OUTPUT_LUT			0x200
+
+/*
+ * 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			51000000
+
+/*
+ * DEFAULT_DROOP_CTRL_VAL: the default voltage droop prevention
+ * control register value.  Assumes reference clock rate is
+ * REF_CLOCK_RATE.
+ */
+#define DEFAULT_DROOP_CTRL_VAL		0x00000F00
+
+/**
+ * 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
+ *
+ * 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,
+};
+
+/**
+ * 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_clk_hw - DFLL clk_hw wrapper for the clock framework
+ * @pdev: DFLL platform_device *
+ * @hw: struct clk_hw - for use by the clock framework
+ *
+ * The @pdev is used by the DFLL driver's clock framework interface
+ * functions, to retrieve the DFLL context.
+ */
+struct dfll_clk_hw {
+	struct platform_device		*pdev;
+	struct clk_hw			hw;
+};
+
+/**
+ * struct tegra_dfll - context for a DFLL platform_device *
+ * @soc: pointer to SoC integration and characterization information
+ * @base: virtual address that the DFLL IP block MMIO space is mapped to
+ * @soc_clk: DFLL logic clock input - 51MHz
+ * @ref_clk: Reference clock input - 51MHz
+ * @i2c_clk: PWR_I2C controller clock input
+ * @dfll_clk: our output clock - registered in dfll_init()
+ * @ref_rate: clock rate of @ref_clk.  Does not change once set
+ * @tune_range: current voltage range that the driver believes it's in
+ * @mode: current DFLL hardware operating mode
+ * @freq_req_regval: cache of the DFLL_FREQ_REQ register value
+ * @speedo_id: CPU Speedo ID - from process characterization
+ * @process_id: CPU process ID - from process characterization
+ * @max_reg_offs: maximum register address offset (from the base) in the DFLL
+ * @dent: dentry for DFLL debugfs (at /DRIVER_NAME)
+ * @dfll_clk_hw: CCF clk_hw wrapper for the DFLL output clock
+ * @lock: spinlock to protect accesses to DFLL registers and state
+ *
+ * XXX @freq_req_regval must be re-read after the DFLL IP block loses
+ * context.
+ */
+struct tegra_dfll {
+	struct tegra_dfll_soc_data	*soc;
+
+	void __iomem			*base;
+
+	struct clk			*soc_clk;
+	struct clk			*ref_clk;
+	struct clk			*i2c_clk;
+	struct clk			*dfll_clk;
+	unsigned long			ref_rate;
+
+	enum dfll_ctrl_mode		mode;
+	enum dfll_tune_range		tune_range;
+
+	u32				freq_req_regval;
+
+	u8				speedo_id;
+	u8				process_id;
+
+	u16				max_reg_offs;
+
+	struct dentry			*dent;
+
+	struct dfll_clk_hw		dfll_clk_hw;
+	spinlock_t			lock;	/* see kerneldoc above */
+};
+
+#define to_dfll_clk_hw(_hw) container_of(_hw, struct dfll_clk_hw, 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",
+};
+
+/* Static functions */
+
+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)
+{
+	__raw_writel(val, td->base + offs);
+}
+
+/**
+ * dfll_wmb - ensure all MMIO writes from the CPU to the DFLL have completed
+ * @td: struct tegra_dfll * device context
+ *
+ * Ensure that all writes from the CPU to the memory-mapped I/O space
+ * of the DFLL IP block have completed.  Assumes that the CPU that
+ * this code is currently running on has excluded other CPUs on the
+ * system from accessing the DFLL IP block MMIO space.
+ */
+static inline void dfll_wmb(struct tegra_dfll *td)
+{
+	dfll_readl(td, DFLL_CTRL);
+}
+
+/**
+ * dfll_is_running - is the DFLL currently generating a clock?
+ * @pdev: pointer to the DFLL platform_device
+ *
+ * If the DFLL is currently generating an output clock signal, return
+ * true; otherwise return false.
+ */
+static bool dfll_is_running(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	return (td->mode == DFLL_DISABLED ||
+		td->mode == DFLL_UNINITIALIZED) ? false : true;
+}
+
+/*
+ * 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.  No return value.
+ *
+ * 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->i2c_clk);
+	if (ret) {
+		dev_err(dev, "could not enable I2C clock: %d\n", ret);
+		return 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);
+		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().  Returns 0.
+ */
+int tegra_dfll_runtime_suspend(struct device *dev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(dev);
+
+	clk_disable(td->soc_clk);
+	clk_disable(td->ref_clk);
+	clk_disable(td->i2c_clk);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_dfll_runtime_suspend);
+
+/*
+ * DFLL tuning operations (per-voltage-range tuning settings)
+ */
+
+/**
+ * dfll_set_tune_range - set the internal tune_range variable and log some debug
+ * @pdev: DFLL platform_device *
+ * @range: tune_range to change the internal state variable to
+ *
+ * Set the internal tune_range variable to @range, and optionally
+ * output some debug.  Does not affect the hardware.  No return value.
+ */
+static void dfll_set_tune_range(struct platform_device *pdev,
+				enum dfll_tune_range range)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	td->tune_range = range;
+	pr_debug("%s: set tune range %d\n", __func__, range);
+}
+
+/**
+ * dfll_tune_low - tune to DFLL and CPU settings valid for any voltage
+ * @pdev: DFLL platform_device *
+ *
+ * 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.  The top end of the low-voltage range is
+ * represented by the index (td->tune_high_out_start - 1).  No return
+ * value.
+ */
+static void dfll_tune_low(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	dfll_set_tune_range(pdev, DFLL_TUNE_LOW);
+
+	dfll_writel(td, td->soc->tune0_low, DFLL_TUNE0);
+	dfll_writel(td, td->soc->tune1, DFLL_TUNE1); /* XXX does not change */
+	dfll_wmb(td);
+
+	if (td->soc->set_clock_trimmers_low)
+		td->soc->set_clock_trimmers_low();
+}
+
+/*
+ * I2C output voltage forcing control
+ */
+
+/**
+ * dfll_disable_i2c_output_forcing - turn off I2C output forcing
+ * @pdev: DFLL platform_device *
+ *
+ * Prevent the DFLL's I2C voltage controller from forcing a specific
+ * target voltage value on the I2C bus, rather than using the target
+ * voltage from the control loop.  No return value.
+ */
+static void dfll_disable_i2c_output_forcing(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	if (!(td->freq_req_regval & DFLL_FREQ_REQ_FORCE_ENABLE))
+		return;
+
+	td->freq_req_regval &= ~DFLL_FREQ_REQ_FORCE_ENABLE;
+	dfll_writel(td, td->freq_req_regval, DFLL_FREQ_REQ);
+}
+
+/*
+ * Voltage droop control
+ */
+
+/**
+ * dfll_set_default_droop_ctrl - set the default droop control value
+ * @pdev: DFLL platform_device *
+ *
+ * Set the default voltage droop control value, a product of
+ * characterization.  Called during DFLL driver initialization, and
+ * upon recovery from a context loss event.  Returns 0 upon success or
+ * -ERANGE if the register bitfield value representing @min_rate
+ * cannot be programmed into the hardware.
+ */
+static void dfll_set_default_droop_ctrl(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	dfll_writel(td, DEFAULT_DROOP_CTRL_VAL, DFLL_DROOP_CTRL);
+	dfll_wmb(td);
+}
+
+/*
+ * Output clock scaler control
+ */
+
+/**
+ * dfll_disable_output_clock_scaler - directly output the clock from the DVCO
+ * @pdev: DFLL platform_device *
+ *
+ * Configure the output clock scaler (skipper 3) to not divide the
+ * DVCO output clock, if it's currently dividing the DVCO output
+ * clock.  No return value.
+ */
+static void dfll_disable_output_scaler(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+	u32 v;
+
+	v = td->freq_req_regval >> DFLL_FREQ_REQ_SCALE_SHIFT;
+	v &= DFLL_FREQ_REQ_SCALE_MAX - 1;
+	if (v == DFLL_FREQ_REQ_SCALE_MAX - 1)
+		return;
+
+	td->freq_req_regval |= ((DFLL_FREQ_REQ_SCALE_MAX - 1) <<
+				DFLL_FREQ_REQ_SCALE_SHIFT);
+	dfll_writel(td, td->freq_req_regval, DFLL_FREQ_REQ);
+	dfll_wmb(td);
+}
+
+/**
+ * dfll_read_output_clock_scale - return the output clock scale
+ * @pdev: DFLL platform_device *
+ *
+ * Return the current output clock scaling dividend.  If this is equal
+ * to DFLL_FREQ_REQ_SCALE_MAX, then there's no scaling; otherwise, the
+ * DVCO's output rate will be scaled by the return value divided by
+ * DFLL_FREQ_REQ_SCALE_MAX.
+ */
+static u32 dfll_read_output_clock_scale(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+	u32 s;
+
+	s = td->freq_req_regval;
+	s &= DFLL_FREQ_REQ_SCALE_MASK;
+	s >>= DFLL_FREQ_REQ_SCALE_SHIFT;
+	s += 1;
+
+	return s;
+}
+
+/*
+ * 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  __attribute__((pure)) 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
+ * @pdev: DFLL platform_device *
+ *
+ * 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 platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+	unsigned long flags;
+	u32 v, s;
+	u64 pre_scaler_rate, post_scaler_rate;
+
+	if (td->mode == DFLL_DISABLED || td->mode == DFLL_UNINITIALIZED)
+		return 0;
+
+	spin_lock_irqsave(&td->lock, flags);
+
+	v = dfll_readl(td, DFLL_MONITOR_DATA);
+	v &= DFLL_MONITOR_DATA_VAL_MASK;
+	v >>= DFLL_MONITOR_DATA_VAL_SHIFT;
+	pre_scaler_rate = dfll_calc_monitored_rate(v, td->ref_rate);
+
+	s = dfll_read_output_clock_scale(pdev);
+	post_scaler_rate = (pre_scaler_rate * s) / DFLL_FREQ_REQ_SCALE_MAX;
+
+	spin_unlock_irqrestore(&td->lock, flags);
+
+	return post_scaler_rate;
+}
+
+
+/*
+ * DFLL mode switching
+ */
+
+/**
+ * dfll_set_open_loop_config - prepare to switch to open-loop mode
+ * @pdev: DFLL platform_device *
+ *
+ * Prepare to switch the DFLL to open-loop mode.  This switches the
+ * DFLL to the low-voltage tuning range, ensures that I2C output
+ * forcing is disabled, and disables the output clock rate scaler.
+ * The DFLL's low-voltage tuning range parameters must be
+ * characterized to keep the downstream device stable at any DVCO
+ * input voltage.  No return value.
+ */
+static void dfll_set_open_loop_config(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	/* always tune low (safe) in open loop */
+	if (td->tune_range != DFLL_TUNE_LOW)
+		dfll_tune_low(pdev);
+
+	dfll_disable_i2c_output_forcing(pdev);
+	dfll_disable_output_scaler(pdev);
+}
+
+/*
+ * DFLL enable/disable & open-loop <-> closed-loop transitions
+ */
+
+/**
+ * dfll_set_mode - change the DFLL control mode
+ * @pdev: DFLL platform_device *
+ * @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.  No return value.
+ */
+static void dfll_set_mode(struct platform_device *pdev,
+			  enum dfll_ctrl_mode mode)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	td->mode = mode;
+	dfll_writel(td, mode - 1, DFLL_CTRL);
+	dfll_wmb(td);
+}
+
+/**
+ * dfll_disable - switch from open-loop mode to disabled mode
+ * @pdev: DFLL platform_device *
+ *
+ * Switch from OPEN_LOOP state to DISABLED state.  No return value.
+ */
+static void dfll_disable(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&td->lock, flags);
+
+	if (td->mode != DFLL_OPEN_LOOP) {
+		dev_err(&pdev->dev, "cannot disable DFLL in %s mode\n",
+			mode_name[td->mode]);
+		goto tdd_exit;
+	}
+
+	dfll_set_mode(pdev, DFLL_DISABLED);
+	pm_runtime_put_sync(&pdev->dev);
+
+tdd_exit:
+	spin_unlock_irqrestore(&td->lock, flags);
+}
+
+/**
+ * dfll_enable - switch a disabled DFLL to open-loop mode
+ * @pdev: DFLL platform_device *
+ *
+ * Switch from DISABLED state to OPEN_LOOP state.  Returns 0 upon success
+ * or -EPERM if the DFLL is not currently in open-loop mode.
+ */
+static int dfll_enable(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&td->lock, flags);
+
+	if (td->mode != DFLL_DISABLED) {
+		dev_err(&pdev->dev, "cannot enable DFLL in %s mode\n",
+			mode_name[td->mode]);
+		ret = -EPERM;
+		goto tde_exit;
+	}
+
+	pm_runtime_get_sync(&pdev->dev);
+	dfll_set_mode(pdev, DFLL_OPEN_LOOP);
+
+tde_exit:
+	spin_unlock_irqrestore(&td->lock, flags);
+	return ret;
+}
+
+/*
+ * DFLL initialization code
+ */
+
+/**
+ * dfll_init - Prepare the DFLL IP block for use
+ * @pdev: DFLL platform_device *
+ *
+ * Do everything necessary to prepare the DFLL IP block for use.  The
+ * DFLL will be left in DISABLED state.  Called by dfll_probe().
+ * Returns 0 upon success, or passes along the error from whatever
+ * function returned it.
+ */
+static int dfll_init(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+	int ret;
+
+	if (td->soc->deassert_dvco_reset)
+		td->soc->deassert_dvco_reset();
+
+	ret = clk_prepare(td->i2c_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable i2c_clk\n");
+		return ret;
+	}
+
+	/* Enable module clocks, release control logic reset */
+	ret = clk_prepare(td->ref_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable ref_clk\n");
+		goto di_err1;
+	}
+
+	ret = clk_prepare(td->soc_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable soc_clk\n");
+		goto di_err2;
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_get_sync(&pdev->dev);
+
+	td->ref_rate = clk_get_rate(td->ref_clk);
+	if (td->ref_rate == 0) {
+		dev_err(&pdev->dev, "ref_clk rate cannot be 0\n");
+		ret = -EINVAL;
+		goto di_err3;
+	}
+
+	if (td->ref_rate != REF_CLOCK_RATE) {
+		dev_err(&pdev->dev, "driver does not yet support reference clk rates other than %ul\n", REF_CLOCK_RATE);
+		ret = -EINVAL;
+		goto di_err3;
+	}
+
+	dfll_set_mode(pdev, DFLL_DISABLED);
+
+	td->freq_req_regval = dfll_readl(td, DFLL_FREQ_REQ);
+
+	dfll_writel(td, DFLL_MONITOR_CTRL_FREQ, DFLL_MONITOR_CTRL);
+	dfll_wmb(td);
+
+	dfll_set_default_droop_ctrl(pdev);
+
+	if (td->soc->init_clock_trimmers)
+		td->soc->init_clock_trimmers();
+
+	dfll_set_open_loop_config(pdev);
+
+	spin_lock_init(&td->lock);
+
+	pm_runtime_put_sync(&pdev->dev);
+
+	return 0;
+di_err3:
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	clk_unprepare(td->soc_clk);
+di_err2:
+	clk_unprepare(td->ref_clk);
+di_err1:
+	clk_unprepare(td->i2c_clk);
+
+	if (td->soc->assert_dvco_reset)
+		td->soc->assert_dvco_reset();
+
+	return ret;
+}
+
+/*
+ * Clock framework integration
+ */
+
+static int dfll_clk_is_enabled(struct clk_hw *hw)
+{
+	struct dfll_clk_hw *tdc = to_dfll_clk_hw(hw);
+
+	return dfll_is_running(tdc->pdev);
+}
+
+static int dfll_clk_enable(struct clk_hw *hw)
+{
+	struct dfll_clk_hw *tdc = to_dfll_clk_hw(hw);
+	struct platform_device *pdev = tdc->pdev;
+
+	return dfll_enable(pdev);
+}
+
+static void dfll_clk_disable(struct clk_hw *hw)
+{
+	struct dfll_clk_hw *tdc = to_dfll_clk_hw(hw);
+	struct platform_device *pdev = tdc->pdev;
+
+	dfll_disable(pdev);
+}
+
+static unsigned long dfll_clk_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	struct dfll_clk_hw *tdc = to_dfll_clk_hw(hw);
+	struct platform_device *pdev = tdc->pdev;
+	u64 rate;
+
+	/* XXX Return the target frequency instead in closed-loop mode? */
+	rate = dfll_read_monitor_rate(pdev);
+	WARN_ON(rate > ULONG_MAX);
+
+	return rate & ULONG_MAX;
+}
+
+static const struct clk_ops dfll_clk_ops = {
+	.is_enabled	= dfll_clk_is_enabled,
+	.enable		= dfll_clk_enable,
+	.disable	= dfll_clk_disable,
+	.recalc_rate	= dfll_clk_recalc_rate,
+};
+
+static const char *dfll_clk_parents[] = { "dfll_soc" };
+
+static struct clk_init_data dfll_clk_init_data = {
+	.ops		= &dfll_clk_ops,
+	.parent_names	= dfll_clk_parents,
+	.num_parents	= ARRAY_SIZE(dfll_clk_parents),
+};
+
+/**
+ * dfll_register_clk - register the DFLL output clock with the clock framework
+ * @pdev: DFLL platform_device *
+ *
+ * Register the DFLL's output clock (i.e., the clock source that the
+ * DVCO generates) with the Linux clock framework and clkdev.  Returns
+ * 0 upon success or -EINVAL upon failure.
+ */
+static int dfll_register_clk(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+	int ret;
+
+	if (!td->soc->output_clock_name) {
+		dev_err(&pdev->dev, "missing output clock name\n");
+		return -EINVAL;
+	}
+
+	dfll_clk_init_data.name = td->soc->output_clock_name;
+	td->dfll_clk_hw.pdev = pdev;
+	td->dfll_clk_hw.hw.init = &dfll_clk_init_data;
+
+	td->dfll_clk = clk_register(&pdev->dev, &td->dfll_clk_hw.hw);
+	if (IS_ERR(td->dfll_clk)) {
+		dev_err(&pdev->dev, "DFLL clock registration error\n");
+		return -EINVAL;
+	}
+
+	ret = clk_register_clkdev(td->dfll_clk, td->soc->output_clock_name,
+				  NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "DFLL clkdev registration error\n");
+		goto rdc_err;
+	}
+
+	return 0;
+rdc_err:
+	clk_unregister(td->dfll_clk);
+	return ret;
+}
+
+/**
+ * dfll_unregister_clk - unregister the DFLL output clock
+ * @pdev: DFLL platform_device *
+ *
+ * Unregister the DFLL's output clock from the Linux clock framework
+ * and from clkdev.  No return value.
+ */
+static void dfll_unregister_clk(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	/* XXX Why doesn't clk_unregister_clkdev() exist? */
+
+	clk_unregister(td->dfll_clk);
+	td->dfll_clk = NULL;
+}
+
+/*
+ * Debugfs interface
+ */
+
+#ifdef CONFIG_DEBUG_FS
+
+static int dfll_enable_get(void *data, u64 *val)
+{
+	struct platform_device *pdev = data;
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	*val = (td->mode == DFLL_OPEN_LOOP);
+
+	return 0;
+}
+static int dfll_enable_set(void *data, u64 val)
+{
+	struct platform_device *pdev = data;
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	if (val && !dfll_is_running(pdev))
+		clk_prepare_enable(td->dfll_clk);
+	else if (!val && dfll_is_running(pdev))
+		clk_disable_unprepare(td->dfll_clk);
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(enable_fops, dfll_enable_get, dfll_enable_set,
+			"%llu\n");
+
+static int dfll_output_rate_get(void *data, u64 *val)
+{
+	struct platform_device *pdev = data;
+
+	*val = dfll_read_monitor_rate(pdev);
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(output_rate_fops, dfll_output_rate_get, NULL, "%llu\n");
+
+static int dfll_register_show(struct seq_file *s, void *data)
+{
+	unsigned long flags;
+	u32 offs;
+	struct platform_device *pdev = s->private;
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	spin_lock_irqsave(&td->lock, flags);
+
+	seq_puts(s, "CONTROL REGISTERS:\n");
+	for (offs = 0; offs <= DFLL_MONITOR_DATA; offs += 4)
+		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
+			   dfll_readl(td, offs));
+
+	seq_puts(s, "\nI2C and INTR REGISTERS:\n");
+	for (offs = DFLL_I2C_CFG; offs <= DFLL_I2C_STS; offs += 4)
+		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
+			   dfll_readl(td, offs));
+
+	offs = DFLL_INTR_STS;
+	seq_printf(s, "[0x%02x] = 0x%08x\n", offs, dfll_readl(td, offs));
+	offs = DFLL_INTR_EN;
+	seq_printf(s, "[0x%02x] = 0x%08x\n", offs, dfll_readl(td, offs));
+	offs = DFLL_I2C_CLK_DIVISOR;
+	seq_printf(s, "[0x%02x] = 0x%08x\n", offs, dfll_readl(td, offs));
+
+	seq_puts(s, "\nLUT:\n");
+	for (offs = DFLL_OUTPUT_LUT;
+	     offs < DFLL_OUTPUT_LUT + 4 * MAX_DFLL_VOLTAGES;
+	     offs += 4)
+		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
+			   dfll_readl(td, offs));
+
+	spin_unlock_irqrestore(&td->lock, flags);
+
+	return 0;
+}
+
+static int dfll_register_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dfll_register_show, inode->i_private);
+}
+
+static ssize_t dfll_register_write(struct file *file,
+				   const char __user *userbuf, size_t count,
+				   loff_t *ppos)
+{
+	struct platform_device *pdev = file->f_path.dentry->d_inode->i_private;
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+	char buf[80];
+	u32 offs, val;
+
+	if (sizeof(buf) <= count)
+		return -EINVAL;
+
+	if (copy_from_user(buf, userbuf, count))
+		return -EFAULT;
+
+	buf[count] = '\0';
+
+	if (sscanf(buf, "[0x%8x] = 0x%8x", &offs, &val) != 2)
+		return -EINVAL;
+
+	if (offs > td->max_reg_offs)
+		return -EINVAL;
+
+	/* avoid unaligned accesses */
+	if ((offs & (~0x3)) != offs)
+		return -EINVAL;
+
+	dfll_writel(td, val, offs);
+	dfll_wmb(td);
+
+	return count;
+}
+
+static const struct file_operations dfll_register_fops = {
+	.open		= dfll_register_open,
+	.read		= seq_read,
+	.write		= dfll_register_write,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int dfll_debug_init(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+	int ret;
+
+	if (!td || (td->mode == DFLL_UNINITIALIZED))
+		return 0;
+
+	td->dent = debugfs_create_dir(td->soc->driver_name, NULL);
+	if (!td->dent)
+		return -ENOMEM;
+
+	ret = -ENOMEM;
+
+	if (!debugfs_create_file("enable", S_IRUGO | S_IWUSR,
+				 td->dent, pdev, &enable_fops))
+		goto err_out;
+
+	if (!debugfs_create_file("output_rate", S_IRUGO,
+				 td->dent, pdev, &output_rate_fops))
+		goto err_out;
+
+	if (!debugfs_create_file("registers", S_IRUGO | S_IWUSR,
+				 td->dent, pdev, &dfll_register_fops))
+		goto err_out;
+
+	return 0;
+
+err_out:
+	debugfs_remove_recursive(td->dent);
+	return ret;
+}
+
+#endif		/* CONFIG_DEBUG_FS */
+
+/*
+ * DFLL initialization
+ */
+
+/**
+ * dfll_init_clks - clk_get() the DFLL source clocks
+ * @pdev: DFLL platform_device *
+ *
+ * Call clk_get() on the DFLL source clocks and save the pointers for later
+ * use.  Returns 0 upon success or -ENODEV if one or more of the clocks
+ * couldn't be looked up.
+ */
+static int dfll_init_clks(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+	int ret;
+
+	td->ref_clk = clk_get(&pdev->dev, "ref");
+	if (IS_ERR(td->ref_clk)) {
+		dev_err(&pdev->dev, "missing ref clock\n");
+		ret = -ENODEV;
+		goto tdic_err1;
+	}
+
+	td->soc_clk = clk_get(&pdev->dev, "soc");
+	if (IS_ERR(td->soc_clk)) {
+		dev_err(&pdev->dev, "missing soc clock\n");
+		ret = -ENODEV;
+		goto tdic_err2;
+	}
+
+	td->i2c_clk = clk_get(&pdev->dev, "i2c");
+	if (IS_ERR(td->i2c_clk)) {
+		dev_err(&pdev->dev, "missing i2c clock\n");
+		ret = -ENODEV;
+		goto tdic_err3;
+	}
+
+	return 0;
+
+tdic_err3:
+	clk_put(td->soc_clk);
+tdic_err2:
+	clk_put(td->ref_clk);
+tdic_err1:
+	return ret;
+}
+
+/*
+ * DT data fetch
+ */
+
+/*
+ * API exported to per-SoC platform drivers
+ */
+
+/**
+ * tegra_dfll_register - probe a Tegra DFLL device
+ * @pdev: DFLL platform_device *
+ * @soc: Per-SoC integration and characterization data for this DFLL instance
+ *
+ * Probe and initialize a DFLL device instance.  Intended to be called
+ * by a SoC-specific shim driver that passes in per-SoC integration
+ * and configuration data via @soc.  (The structure record pointed to
+ * by @soc is copied, so the caller can dispose of its own copy once
+ * tegra_dfll_register() returns.)  Returns 0 upon success, -EINVAL if
+ * @soc is null or if some resource data is missing, -ENOMEM if out of
+ * memory, or can pass along error codes from other functions.
+ */
+int tegra_dfll_register(struct platform_device *pdev,
+			struct tegra_dfll_soc_data *soc)
+{
+	struct resource *mem;
+	struct tegra_dfll *td;
+	int ret = -EINVAL;
+	int r;
+
+	if (!soc) {
+		dev_err(&pdev->dev, "missing SoC data\n");
+		return -EINVAL;
+	}
+
+	td = kzalloc(sizeof(*td), GFP_KERNEL);
+	if (!td)
+		return -ENOMEM;
+
+	td->soc = kmemdup(soc, sizeof(struct tegra_dfll_soc_data), GFP_KERNEL);
+	if (!td->soc) {
+		ret = -ENOMEM;
+		goto tdr_err1;
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "missing register MMIO resource\n");
+		ret = -EINVAL;
+		goto tdr_err2;
+	}
+
+	td->base = ioremap(mem->start, resource_size(mem));
+	if (!td->base) {
+		dev_err(&pdev->dev, "couldn't ioremap() register MMIO area\n");
+		ret = -EINVAL;
+		goto tdr_err2;
+	}
+
+	td->max_reg_offs = resource_size(mem) - sizeof(u32);
+
+	td->speedo_id = tegra_get_cpu_speedo_id();
+	td->process_id = tegra_get_cpu_process_id();
+
+	platform_set_drvdata(pdev, td);
+
+	r = dfll_init_clks(pdev);
+	if (r) {
+		dev_err(&pdev->dev, "DFLL clock init error\n");
+		ret = r;
+		goto tdr_err3;
+	}
+
+	/* Enable the clocks and set the device up */
+	ret = dfll_init(pdev);
+	if (ret)
+		goto tdr_err4;
+
+	ret = dfll_register_clk(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "DFLL clk registration failed\n");
+		goto tdr_err4;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	dfll_debug_init(pdev);
+#endif
+
+	dev_info(&pdev->dev, "initialized\n");
+
+	return 0;
+
+tdr_err4:
+	clk_put(td->i2c_clk);
+	clk_put(td->soc_clk);
+	clk_put(td->ref_clk);
+tdr_err3:
+	iounmap(td->base);
+tdr_err2:
+	kfree(td->soc);
+tdr_err1:
+	kfree(td);
+	return ret;
+}
+EXPORT_SYMBOL(tegra_dfll_register);
+
+/**
+ * tegra_dfll_unregister - release all of the DFLL driver resources for a device
+ * @pdev: DFLL platform_device *
+ *
+ * Unbind this driver from the DFLL hardware device represented by
+ * @pdev.  The DFLL must be disabled for this to succeed.  Returns 0
+ * upon success or -EBUSY if the DFLL is still active.
+ */
+int tegra_dfll_unregister(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	/* Try to prevent removal while the DFLL is active */
+	if (td->mode != DFLL_DISABLED) {
+		dev_err(&pdev->dev,
+			"must disable DFLL before removing driver\n");
+		return -EBUSY;
+	}
+
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	dfll_unregister_clk(pdev);
+	if (td->soc->assert_dvco_reset)
+		td->soc->assert_dvco_reset();
+	clk_put(td->i2c_clk);
+	clk_put(td->soc_clk);
+	clk_put(td->ref_clk);
+	iounmap(td->base);
+	kfree(td->soc);
+	kfree(td);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_dfll_unregister);
diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h
new file mode 100644
index 000000000000..2e002c8ef597
--- /dev/null
+++ b/drivers/clk/tegra/clk-dfll.h
@@ -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
+
+/**
+ * struct tegra_dfll_soc - SoC-specific hooks/integration for the DFLL driver
+ * @driver_name: name of the platform_driver representing the DFLL
+ * @output_clock_name: name of the DFLL's output clock
+ * @min_millivolts: minimum voltage (in mV) that the DFLL tuning is valid for
+ * @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 {
+	char *driver_name;
+	char *output_clock_name;
+	unsigned int min_millivolts;
+	u32 tune0_low;
+	u32 tune0_high;
+	u32 tune1;
+	void (*assert_dvco_reset)(void);
+	void (*deassert_dvco_reset)(void);
+	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 */

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 4/6] ARM: DTS: tegra: add the DFLL IP block to the T114 SoC file
  2013-12-19 12:36 [PATCH 0/6] clk: tegra: add basic support for the DFLL clocksource Paul Walmsley
                   ` (2 preceding siblings ...)
  2013-12-19 12:37 ` [PATCH 3/6] clk: tegra: add library for the DFLL clocksource (open-loop mode) Paul Walmsley
@ 2013-12-19 12:49 ` Paul Walmsley
  2013-12-20  0:05   ` Stephen Warren
  2013-12-19 12:49 ` [PATCH 5/6] ARM: DTS: tegra: add DFLL integration to the Dalmore DTS file Paul Walmsley
  2013-12-19 12:49 ` [PATCH 6/6] clk: tegra: add Tegra114 FCPU DFLL clocksource platform driver Paul Walmsley
  5 siblings, 1 reply; 19+ messages in thread
From: Paul Walmsley @ 2013-12-19 12:49 UTC (permalink / raw)
  To: linux-arm-kernel

Add basic DT bindings for the DFLL IP block for the NVIDIA Tegra114 SoC.

Signed-off-by: Paul Walmsley <pwalmsley@nvidia.com>
Cc: Matthew Longnecker <mlongnecker@nvidia.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
---
 .../bindings/clock/nvidia,tegra114-dfll.txt        |   43 ++++++++++++++++++++
 arch/arm/boot/dts/tegra114.dtsi                    |   10 +++++
 2 files changed, 53 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt

diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt
new file mode 100644
index 000000000000..b868bf97bc3d
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt
@@ -0,0 +1,43 @@
+NVIDIA Tegra114 DFLL FCPU clocksource data in the SoC DTS file:
+
+Required properties:
+
+- compatible : "nvidia,tegra114-dfll-fcpu"
+
+- reg : Must contain the starting physical address and length for the
+        DFLL's MMIO register space, including the DFLL-to-I2C
+        controller interface and the DFLL's I2C controller.
+
+- clocks : Must contain an array of two-cell arrays, one per clock.
+           DFLL source clocks.  At minimum this should include the
+           reference clock source and the IP block's main clock
+           source.  Also it should contain the DFLL's I2C controller
+           clock source.  The format is <&clock-provider-phandle
+           clock-id>.
+
+- clock-names : Must contain an array of strings, one per 'clocks'
+                two-cell array.  The position in the array of these
+                strings must correspond to the position in the 'clocks'
+                array (see above).  The DFLL driver currently requires
+                the "soc", "ref", and "i2c" clock names to be populated.
+
+
+Optional properties:
+
+- status : device availability -- managed by the DT integration code, not
+           the DFLL driver.  Should be set to "disabled" in the SoC
+           DTS file.
+
+
+Example:
+
+dfll at 70110000 {
+        compatible = "nvidia,tegra114-dfll-fcpu";
+        reg = <0x70110000 0x400>;
+        clocks = <&tegra_car TEGRA114_CLK_DFLL_SOC>,
+                 <&tegra_car TEGRA114_CLK_DFLL_REF>,
+                 <&tegra_car TEGRA114_CLK_I2C5>;
+        clock-names = "soc", "ref", "i2c";
+        status = "disabled";
+};
+
diff --git a/arch/arm/boot/dts/tegra114.dtsi b/arch/arm/boot/dts/tegra114.dtsi
index ae855ec60bbd..1cd59d79e67c 100644
--- a/arch/arm/boot/dts/tegra114.dtsi
+++ b/arch/arm/boot/dts/tegra114.dtsi
@@ -480,6 +480,16 @@
 		};
 	};
 
+	dfll at 70110000 {
+		compatible = "nvidia,tegra114-dfll-fcpu";
+		reg = <0x70110000 0x400>;
+		clocks = <&tegra_car TEGRA114_CLK_DFLL_SOC>,
+			 <&tegra_car TEGRA114_CLK_DFLL_REF>,
+			 <&tegra_car TEGRA114_CLK_I2C5>;
+		clock-names = "soc", "ref", "i2c";
+		status = "disabled";
+	};
+
 	sdhci at 78000000 {
 		compatible = "nvidia,tegra114-sdhci", "nvidia,tegra30-sdhci";
 		reg = <0x78000000 0x200>;

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 5/6] ARM: DTS: tegra: add DFLL integration to the Dalmore DTS file
  2013-12-19 12:36 [PATCH 0/6] clk: tegra: add basic support for the DFLL clocksource Paul Walmsley
                   ` (3 preceding siblings ...)
  2013-12-19 12:49 ` [PATCH 4/6] ARM: DTS: tegra: add the DFLL IP block to the T114 SoC file Paul Walmsley
@ 2013-12-19 12:49 ` Paul Walmsley
  2013-12-20  0:10   ` Stephen Warren
  2013-12-19 12:49 ` [PATCH 6/6] clk: tegra: add Tegra114 FCPU DFLL clocksource platform driver Paul Walmsley
  5 siblings, 1 reply; 19+ messages in thread
From: Paul Walmsley @ 2013-12-19 12:49 UTC (permalink / raw)
  To: linux-arm-kernel

Expose the DFLL device on the NVIDIA Tegra114 Dalmore board, and connect
the DFLL (and FCPU cluster) voltage regulator.

Signed-off-by: Paul Walmsley <pwalmsley@nvidia.com>
Cc: Matthew Longnecker <mlongnecker@nvidia.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
---
 .../bindings/clock/nvidia,tegra114-dfll.txt        |   16 ++++++++++++++++
 arch/arm/boot/dts/tegra114-dalmore.dts             |    4 ++++
 2 files changed, 20 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt
index b868bf97bc3d..c4072b3f16fc 100644
--- a/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt
+++ b/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt
@@ -41,3 +41,19 @@ dfll at 70110000 {
         status = "disabled";
 };
 
+...
+
+NVIDIA Tegra114 DFLL clocksource data in the board DTS file
+
+Optional properties:
+
+- status : device availability -- managed by the DT integration code, not
+           the DFLL driver.  Should be set to "okay" if the DFLL is to be
+           used on this board type.
+
+
+Example:
+
+dfll at 70110000 {
+        status = "okay";
+};
diff --git a/arch/arm/boot/dts/tegra114-dalmore.dts b/arch/arm/boot/dts/tegra114-dalmore.dts
index 88be40cf8845..2e8e7ae60c1a 100644
--- a/arch/arm/boot/dts/tegra114-dalmore.dts
+++ b/arch/arm/boot/dts/tegra114-dalmore.dts
@@ -1063,6 +1063,10 @@
 		};
 	};
 
+	dfll at 70110000 {
+		status = "okay";
+	};
+
 	sdhci at 78000400 {
 		cd-gpios = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_LOW>;
 		bus-width = <4>;

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 6/6] clk: tegra: add Tegra114 FCPU DFLL clocksource platform driver
  2013-12-19 12:36 [PATCH 0/6] clk: tegra: add basic support for the DFLL clocksource Paul Walmsley
                   ` (4 preceding siblings ...)
  2013-12-19 12:49 ` [PATCH 5/6] ARM: DTS: tegra: add DFLL integration to the Dalmore DTS file Paul Walmsley
@ 2013-12-19 12:49 ` Paul Walmsley
  2013-12-20  0:18   ` Stephen Warren
  5 siblings, 1 reply; 19+ messages in thread
From: Paul Walmsley @ 2013-12-19 12:49 UTC (permalink / raw)
  To: linux-arm-kernel

Add basic platform driver support for the fast CPU cluster DFLL
clocksource found on Tegra114 SoCs.  This small driver selects the
appropriate Tegra114-specific characterization data and integration
code.  It relies on the DFLL common code to do most of the work.

Signed-off-by: Paul Walmsley <pwalmsley@nvidia.com>
Cc: Aleksandr Frid <afrid@nvidia.com>
Cc: Matthew Longnecker <mlongnecker@nvidia.com>
Cc: Peter De Schrijver <pdeschrijver@nvidia.com>
Cc: Prashant Gaikwad <pgaikwad@nvidia.com>
---
 drivers/clk/tegra/Kconfig                  |    9 +
 drivers/clk/tegra/Makefile                 |    5 +
 drivers/clk/tegra/clk-tegra114-dfll-fcpu.c |  169 ++++++++++++++++++++++++++++
 3 files changed, 182 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/tegra/clk-tegra114-dfll-fcpu.c

diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig
index 5c5d1a841ef8..b5b5069017de 100644
--- a/drivers/clk/tegra/Kconfig
+++ b/drivers/clk/tegra/Kconfig
@@ -5,5 +5,14 @@ config CLK_TEGRA_DFLL
 	tristate
 	depends on COMMON_CLK
 
+config CLK_TEGRA114_DFLL_FCPU
+	tristate "Clock driver for the Tegra T114 DFLL FCPU"
+	select CLK_TEGRA_DFLL
+	---help---
+	  Select this if you'd like to use the DFLL root clocksource
+	  present on NVIDIA Tegra114 chips.  This DFLL clocksource can
+	  be used to clock the fast CPU cluster.  Say 'y' or 'm' here
+	  if you're building a kernel intended for use on T114 chips.
+
 endmenu
 
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
index 03277f640bfe..9b4b7357b97d 100644
--- a/drivers/clk/tegra/Makefile
+++ b/drivers/clk/tegra/Makefile
@@ -16,10 +16,13 @@ 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_CLK_TEGRA_DFLL)		+= clk-dfll.o
+obj-$(CONFIG_CLK_TEGRA114_DFLL_FCPU)	+= clk-tegra114-dfll-fcpu.o
 
 # XXX -Wno-sign-compare is only needed due to problems with
 # some non-clock-related Linux header files, and can be removed
 # once those headers are fixed
 CFLAGS_clk-dfll.o			+= -Wall -Wextra -Wno-unused-parameter \
 					   -Wno-sign-compare
-
+CFLAGS_clk-tegra114-dfll-fcpu.o		+= -Wall -Wextra -Wno-unused-parameter \
+					   -Wno-missing-field-initializers \
+					   -Wno-sign-compare
diff --git a/drivers/clk/tegra/clk-tegra114-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra114-dfll-fcpu.c
new file mode 100644
index 000000000000..1665f3cbebeb
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra114-dfll-fcpu.c
@@ -0,0 +1,169 @@
+/*
+ * clk-tegra114-dfll-fcpu.c - Tegra114 DFLL FCPU clock source driver
+ *
+ * Copyright (C) 2012-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.
+ *
+ * To do:
+ * - add support for closed-loop mode
+ * - add support for characterization constant overrides via DT
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+#include <linux/tegra-soc.h>
+
+#include "clk.h"
+#include "clk-dfll.h"
+
+#define DRIVER_NAME			"tegra_dfll_fcpu"
+
+/*
+ * DFLL characterization values
+ */
+
+/*
+ * TUNE0_LOW_0_X_VAL: the value to be programmed into the DFLL's TUNE0
+ * register for T114 chips with a CPU Speedo ID of 0 while the DVCO is
+ * in the low-voltage range.  From device characterization.
+ */
+#define TUNE0_LOW_0_X_VAL		0x00b0019d
+
+/*
+ * MIN_MV_0_X_VAL: the minimum VDD_CPU voltage that the DFLL's
+ * characterization is valid for, for T114 chips with a CPU Speedo ID
+ * of 0.  Can be overridden by fuse settings; see MIN_MV_OVERRIDE_VAL.
+ */
+#define MIN_MV_0_X_VAL			1000
+
+/*
+ * MIN_MV_OVERRIDE_VAL: value to override the VDD_CPU DFLL voltage
+ * floor to, if the VDD_CPU DFLL minimum voltage override fuse is set
+ */
+#define MIN_MV_OVERRIDE_VAL		900
+
+/*
+ * TUNE0_LOW_X_X_VAL: the value to be programmed into the DFLL's TUNE0
+ * register for T114 chips with a nonzero Speedo ID while the DVCO is
+ * in the low-voltage range.  From device characterization.
+ */
+#define TUNE0_LOW_X_X_VAL		0x00b0039d
+
+/*
+ * MIN_MV_X_X_VAL: the minimum VDD_CPU voltage that the DFLL's
+ * characterization is valid for, for T114 chips with a nonzero CPU
+ * Speedo ID.  Can be overridden by fuse settings; see
+ * MIN_MV_OVERRIDE_VAL.
+ */
+#define MIN_MV_X_X_VAL			900
+
+/*
+ * TUNE1_VAL: the value to be programmed into the DFLL's TUNE1
+ * register, from device characterization.
+ */
+#define TUNE1_VAL			0x0000001f
+
+/*
+ * DROOP_RATE_MIN: the highest DVCO rate at which the DFLL's voltage
+ * droop protection activates
+ */
+#define DROOP_RATE_MIN			1000000
+
+
+/*
+ * Platform driver integration
+ */
+
+/**
+ * tegra114_dfll_fcpu_probe - probe the FCPU DFLL IP block
+ * @pdev: DFLL platform_device *
+ *
+ * Called when a DFLL device is bound to this driver by the driver
+ * core.  Takes characterization-specific tweaks into account and sets
+ * up an instance of the DFLL driver.  Returns 0 upon success, or
+ * -ENOMEM if memory couldn't be allocated, or passes along the error
+ * code from tegra_dfll_register().
+ */
+static int tegra114_dfll_fcpu_probe(struct platform_device *pdev)
+{
+	struct tegra_dfll_soc_data soc;
+	int r, speedo_id, process_id;
+
+	speedo_id = tegra_get_cpu_speedo_id();
+	process_id = tegra_get_cpu_process_id();
+
+	memset(&soc, 0, sizeof(soc));
+	soc.driver_name = DRIVER_NAME;
+	soc.tune1 = TUNE1_VAL;
+	soc.output_clock_name = "dfllCPU_out";
+	soc.assert_dvco_reset = tegra114_clock_assert_dfll_dvco_reset;
+	soc.deassert_dvco_reset = tegra114_clock_deassert_dfll_dvco_reset;
+	soc.init_clock_trimmers = tegra114_clock_tune_cpu_trimmers_low;
+	soc.set_clock_trimmers_low = tegra114_clock_tune_cpu_trimmers_low;
+	soc.set_clock_trimmers_high = tegra114_clock_tune_cpu_trimmers_high;
+
+	if ((speedo_id == 1 && (process_id == 0 || process_id == 1)) ||
+	    (speedo_id == 2)) {
+		soc.tune0_low = TUNE0_LOW_X_X_VAL;
+		soc.min_millivolts = MIN_MV_X_X_VAL;
+	} else {
+		if (speedo_id != 0)
+			dev_err(&pdev->dev, "Unrecognized SoC characterization - performance may be affected\n");
+		soc.tune0_low = TUNE0_LOW_0_X_VAL;
+		soc.min_millivolts = MIN_MV_0_X_VAL;
+	}
+
+	if (tegra114_fuse_read_min_vdd_cpu_override())
+		soc.min_millivolts = MIN_MV_OVERRIDE_VAL;
+
+	r = tegra_dfll_register(pdev, &soc);
+	if (!r)
+		return r;
+
+	return 0;
+}
+
+/* Match table for OF platform binding */
+static struct of_device_id tegra114_dfll_fcpu_of_match[] = {
+	{ .compatible = "nvidia,tegra114-dfll-fcpu", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, tegra114_dfll_fcpu_of_match);
+
+static const struct dev_pm_ops tegra114_dfll_pm_ops = {
+	SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
+			   tegra_dfll_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra114_dfll_fcpu_driver = {
+	.probe		= tegra114_dfll_fcpu_probe,
+	.remove		= tegra_dfll_unregister,
+	.driver		= {
+		.name		= DRIVER_NAME,
+		.owner		= THIS_MODULE,
+		.of_match_table = of_match_ptr(tegra114_dfll_fcpu_of_match),
+		.pm		= &tegra114_dfll_pm_ops,
+	},
+};
+
+module_platform_driver(tegra114_dfll_fcpu_driver);
+
+MODULE_DESCRIPTION("Tegra114 DFLL clock source driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Aleksandr Frid <afrid@nvidia.com>");
+MODULE_AUTHOR("Paul Walmsley <pwalmsley@nvidia.com>");

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 1/6] ARM: tegra: fuse: add functions to read speedo ID and process ID
  2013-12-19 12:36 ` [PATCH 1/6] ARM: tegra: fuse: add functions to read speedo ID and process ID Paul Walmsley
@ 2013-12-19 23:09   ` Stephen Warren
  0 siblings, 0 replies; 19+ messages in thread
From: Stephen Warren @ 2013-12-19 23:09 UTC (permalink / raw)
  To: linux-arm-kernel

On 12/19/2013 05:36 AM, Paul Walmsley wrote:
> From: Danny Huang <dahuang@nvidia.com>
> 
> Add functions to read the speedo and process id of both the CPU and the SoC.
> There might be some drivers need the information as well.

> diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c

> +int tegra_get_cpu_process_id(void)
> +{
> +	return tegra_cpu_process_id;
> +}
> +EXPORT_SYMBOL(tegra_get_cpu_process_id);
> +
> +int tegra_get_cpu_speedo_id(void)
> +{
> +	if (tegra_chip_id == TEGRA20)
> +		return -EINVAL;
> +
> +	return tegra_cpu_speedo_id;
> +}

Olof specifically went through the code and removed such functions in
the past. He argued that consumers of this data should just read those
tegra_cpu_process_id/... variables directly instead.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 2/6] ARM: tegra114: fuse: add DFLL FCPU minimum voltage override test function
  2013-12-19 12:36 ` [PATCH 2/6] ARM: tegra114: fuse: add DFLL FCPU minimum voltage override test function Paul Walmsley
@ 2013-12-19 23:12   ` Stephen Warren
  0 siblings, 0 replies; 19+ messages in thread
From: Stephen Warren @ 2013-12-19 23:12 UTC (permalink / raw)
  To: linux-arm-kernel

On 12/19/2013 05:36 AM, Paul Walmsley wrote:
> Add a function to test the internal Tegra114 chip fuse that indicates
> whether the VDD_CPU lower voltage limit for the fast CPU cluster
> should be overridden.

> diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c

> +bool tegra114_fuse_read_min_vdd_cpu_override(void)
> +{
> +	if (tegra_chip_id != TEGRA114)
> +		return -EINVAL;

-EINVAL doesn't translate to "bool" too well...

> +
> +	return tegra_spare_fuse(TEGRA114_SPARE_FUSE_VDD_CPU_OVERRIDE) ? true :
> +		false;

In the spirit of my comment on patch 1 and consistency, instead perhaps
just read this fuse at boot, and assign it to a global?

BTW, why the need for "? true : false" - doesn't the compiler do the
conversion correctly itself. Perhaps it's to avoid a performance
warning, in which case a leading !! seems to be more popular.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 3/6] clk: tegra: add library for the DFLL clocksource (open-loop mode)
  2013-12-19 12:37 ` [PATCH 3/6] clk: tegra: add library for the DFLL clocksource (open-loop mode) Paul Walmsley
@ 2013-12-19 23:57   ` Stephen Warren
  0 siblings, 0 replies; 19+ messages in thread
From: Stephen Warren @ 2013-12-19 23:57 UTC (permalink / raw)
  To: linux-arm-kernel

On 12/19/2013 05:37 AM, Paul Walmsley wrote:
> Add shared code to support the Tegra DFLL clocksource in open-loop
> mode.  This root clocksource is present on the Tegra114 and Tegra124

"root clocksource" implies no parents, yet the clock object this driver
registers lists "dfll_soc" as its parent.

> SoCs.  It's used to generate a clock for the fast CPU cluster.  Future
> patches will add support for closed-loop mode.
> 
> This code was originally created by Aleksandr Frid <afrid@nvidia.com>.
> It's been converted to use DT and integrated into the common clock
> framework, and a few minor functional changes have been implemented.
> 
> Subsequent patches will add drivers for the Tegra114 and Tegra124 fast
> CPU cluster DFLL devices, which rely on this code.

> diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig

> +config CLK_TEGRA_DFLL
> +	tristate
> +	depends on COMMON_CLK

Is there a need to make this optional? There's nothing configurable in
the rest of the Tegra clock driver, and it'd be nicer if the rest of the
kernel (e.g. Tegra cpufreq driver?) could simply always assume that DFLL
were available.

> +

Blank line at EOF. Same in Makefile.

> diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile

> +# XXX -Wno-sign-compare is only needed due to problems with
> +# some non-clock-related Linux header files, and can be removed
> +# once those headers are fixed
> +CFLAGS_clk-dfll.o			+= -Wall -Wextra -Wno-unused-parameter \
> +					   -Wno-sign-compare

Aside from the no-sign-compare issue (which I assume is being fixed in
parallel before this is applied?) is there really a need to second-guess
the kernel's -W flags?

> diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
> +/*
> + * clk-dfll.c - Tegra DFLL clock source common code

It'd be best not to put filenames in comments; it's just one more thing
to update if the file gets renamed.

> +struct tegra_dfll {
...
> +	u8				speedo_id;
> +	u8				process_id;

I don't think you need a copy of these; just read the globals directly?

> +static bool dfll_is_running(struct platform_device *pdev)
> +{
> +	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
> +
> +	return (td->mode == DFLL_DISABLED ||
> +		td->mode == DFLL_UNINITIALIZED) ? false : true;

Why not:

	return td->mode == DFLL_OPEN_LOOP;

or at least:

	return td->mode != DFLL_DISABLED &&
		td->mode != DFLL_UNINITIALIZED;

> +static void dfll_set_tune_range(struct platform_device *pdev,
> +				enum dfll_tune_range range)
> +{
> +	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
> +
> +	td->tune_range = range;

Do we really need a (private, single-use) function just to write a field
in a structure?

> +static u64  __attribute__((pure)) dfll_calc_monitored_rate(u32 monitor_data,

Does the compiler really need to be told about pure? It seems pretty
obvious from the code, and I imagine the compiler is smart enough to
tell. It looks like __pure would be preferred:

include/linux/compiler-gcc.h:94:#define __pure __attribute__((pure))

> +static u64 dfll_read_monitor_rate(struct platform_device *pdev)
...
> +	if (td->mode == DFLL_DISABLED || td->mode == DFLL_UNINITIALIZED)
> +		return 0;

if (!dfll_is_running(td))
	return 0;

> +static int dfll_init(struct platform_device *pdev)

> +	pm_runtime_enable(&pdev->dev);
> +	pm_runtime_get_sync(&pdev->dev);
> +
> +	td->ref_rate = clk_get_rate(td->ref_clk);

Doesn't clk_get_rate() work before clk_enable() (which happens in
pm_runtime_get_sync()), so you could simplify the error-handling here by
checking the rate a little earlier, outside the pm_runtime_get/put?

> +	if (td->ref_rate == 0) {
> +		dev_err(&pdev->dev, "ref_clk rate cannot be 0\n");
> +		ret = -EINVAL;
> +		goto di_err3;
> +	}
> +

> +	pm_runtime_put_sync(&pdev->dev);

Would plain pm_runtime_put() work just as well, and avoid the put if
something else enables the driver/dfll clock very soon after?

> +static const char *dfll_clk_parents[] = { "dfll_soc" };
> +
> +static struct clk_init_data dfll_clk_init_data = {
> +	.ops		= &dfll_clk_ops,
> +	.parent_names	= dfll_clk_parents,
> +	.num_parents	= ARRAY_SIZE(dfll_clk_parents),
> +};

Does a root clock actually have any parents?

> +static void dfll_unregister_clk(struct platform_device *pdev)
> +{
> +	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
> +
> +	/* XXX Why doesn't clk_unregister_clkdev() exist? */

Hmm. Seems like we need a patch to the clkdev core before this series?

> +static int dfll_enable_get(void *data, u64 *val)
> +{
> +	struct platform_device *pdev = data;
> +	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
> +
> +	*val = (td->mode == DFLL_OPEN_LOOP);

Use dfll_is_running()? Given td->mode embodies both enable/disable and
the current mode, would it make more sense to have a debugfs file that
exposed the raw enum, so you could both tell is-enabled and the current
mode?

> +
> +	return 0;
> +}
> +static int dfll_enable_set(void *data, u64 val)

Missing blank line.

> +{
> +	struct platform_device *pdev = data;
> +	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
> +
> +	if (val && !dfll_is_running(pdev))
> +		clk_prepare_enable(td->dfll_clk);
> +	else if (!val && dfll_is_running(pdev))
> +		clk_disable_unprepare(td->dfll_clk);

This is going to allow unbalanced prepare_enable/disable_unprepare calls
simply by writing stuff to the debugfs file. Do we want to make debugfs
more friendly than that? If so, perhaps keep some state around to
indicate the last requested state from debugfs, and only make the clock
calls if the new requested state is different from the previous
debugfs-requested state, rather than different to the current-actual state?

> +static int dfll_register_show(struct seq_file *s, void *data)

regmap will give you this function for free. Perhaps its considered too
much overhead though?

> +static ssize_t dfll_register_write(struct file *file,
> +				   const char __user *userbuf, size_t count,
> +				   loff_t *ppos)
> +{
> +	struct platform_device *pdev = file->f_path.dentry->d_inode->i_private;
> +	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
> +	char buf[80];
> +	u32 offs, val;
> +
> +	if (sizeof(buf) <= count)
> +		return -EINVAL;

Don't you need to check whether ppos==0, to detect continuation rather
than initial writes?

For register twiddling, why not just use a user-space app that mmap()s
/dev/mem and blasts in the values? That would remove the need to put
this into the kernel. This also feels like a function that should be
implemented as a common utility, not open-coded into every driver. regmap?

> +static int dfll_debug_init(struct platform_device *pdev)
> +{
> +	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
> +	int ret;
> +
> +	if (!td || (td->mode == DFLL_UNINITIALIZED))
> +		return 0;

Isn't td->mode always == DFLL_UNINITIALIZED when this function is
called, since the mode is only set when dfll_enable() is called, which
is only called when dfll_clk_enable() is called, and the clock isn't
enabled when this function is called?

> +int tegra_dfll_register(struct platform_device *pdev,
> +			struct tegra_dfll_soc_data *soc)

> +	td = kzalloc(sizeof(*td), GFP_KERNEL);

devm_kzalloc would simplify error-handling.

> +	td->soc = kmemdup(soc, sizeof(struct tegra_dfll_soc_data), GFP_KERNEL);

Why not just store the pointer?

> +	td->base = ioremap(mem->start, resource_size(mem));

devm_ioremap()

> +	clk_put(td->i2c_clk);

I evidently should have mentioned devm_clk_get() somewhere too.

> +int tegra_dfll_unregister(struct platform_device *pdev)

> +	pm_runtime_put_sync(&pdev->dev);

What is that matching? dfll_init() does a matched get/put, as do
dfll_enable/disable, which in turn should be called in a matched fashion
from any client's clk_enable/disable.

> diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h

> + * clk-dfll.h - prototypes and macros for the Tegra DFLL clocksource driver

Remove filename?

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 4/6] ARM: DTS: tegra: add the DFLL IP block to the T114 SoC file
  2013-12-19 12:49 ` [PATCH 4/6] ARM: DTS: tegra: add the DFLL IP block to the T114 SoC file Paul Walmsley
@ 2013-12-20  0:05   ` Stephen Warren
  2014-01-14  6:27     ` Paul Walmsley
       [not found]     ` <52D4D314.3000208@nvidia.com>
  0 siblings, 2 replies; 19+ messages in thread
From: Stephen Warren @ 2013-12-20  0:05 UTC (permalink / raw)
  To: linux-arm-kernel

On 12/19/2013 05:49 AM, Paul Walmsley wrote:
> Add basic DT bindings for the DFLL IP block for the NVIDIA Tegra114 SoC.

> diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt

> +- clocks : Must contain an array of two-cell arrays, one per clock.
> +           DFLL source clocks.  At minimum this should include the
> +           reference clock source and the IP block's main clock
> +           source.  Also it should contain the DFLL's I2C controller
> +           clock source.  The format is <&clock-provider-phandle
> +           clock-id>.

Entries in "clocks" aren't two cells, they're a phandle plus as many
cells as the node referenced by the phandle specifies.

> +
> +- clock-names : Must contain an array of strings, one per 'clocks'
> +                two-cell array.  The position in the array of these

clock-names defines the set of entries in clocks, not the other way around.

> +                strings must correspond to the position in the 'clocks'
> +                array (see above).  The DFLL driver currently requires
> +                the "soc", "ref", and "i2c" clock names to be populated.

The standard wording used by all the Tegra clock client bindings is now:

- 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
  - ref
  - i2c

For consistency, it'd be nice to adopt the same style here.

> +Optional properties:
> +
> +- status : device availability -- managed by the DT integration code, not
> +           the DFLL driver.  Should be set to "disabled" in the SoC
> +           DTS file.

That's such a core property that it's not worth documenting in every
single binding.

> +

Blank line at EOF.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 5/6] ARM: DTS: tegra: add DFLL integration to the Dalmore DTS file
  2013-12-19 12:49 ` [PATCH 5/6] ARM: DTS: tegra: add DFLL integration to the Dalmore DTS file Paul Walmsley
@ 2013-12-20  0:10   ` Stephen Warren
  2014-01-14  6:36     ` Paul Walmsley
  0 siblings, 1 reply; 19+ messages in thread
From: Stephen Warren @ 2013-12-20  0:10 UTC (permalink / raw)
  To: linux-arm-kernel

On 12/19/2013 05:49 AM, Paul Walmsley wrote:
> Expose the DFLL device on the NVIDIA Tegra114 Dalmore board, and connect
> the DFLL (and FCPU cluster) voltage regulator.

> diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt

> +NVIDIA Tegra114 DFLL clocksource data in the board DTS file
> +
> +Optional properties:
> +
> +- status : device availability -- managed by the DT integration code, not
> +           the DFLL driver.  Should be set to "okay" if the DFLL is to be
> +           used on this board type.

There's certainly no need to document the same DT property twice. The DT
docs are about documenting the schema. If the DT author decides to split
the properties between a .dtsi and a .dts file, that's irrelevant to the
schema.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 6/6] clk: tegra: add Tegra114 FCPU DFLL clocksource platform driver
  2013-12-19 12:49 ` [PATCH 6/6] clk: tegra: add Tegra114 FCPU DFLL clocksource platform driver Paul Walmsley
@ 2013-12-20  0:18   ` Stephen Warren
  0 siblings, 0 replies; 19+ messages in thread
From: Stephen Warren @ 2013-12-20  0:18 UTC (permalink / raw)
  To: linux-arm-kernel

On 12/19/2013 05:49 AM, Paul Walmsley wrote:
> Add basic platform driver support for the fast CPU cluster DFLL
> clocksource found on Tegra114 SoCs.  This small driver selects the
> appropriate Tegra114-specific characterization data and integration
> code.  It relies on the DFLL common code to do most of the work.

> diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig

> +config CLK_TEGRA114_DFLL_FCPU
> +	tristate "Clock driver for the Tegra T114 DFLL FCPU"

depends ARCH_TEGRA_114_SOC?

> +CFLAGS_clk-tegra114-dfll-fcpu.o		+= -Wall -Wextra -Wno-unused-parameter \
> +					   -Wno-missing-field-initializers \
> +					   -Wno-sign-compare

I'd drop this too.

> diff --git a/drivers/clk/tegra/clk-tegra114-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra114-dfll-fcpu.c

> + * clk-tegra114-dfll-fcpu.c - Tegra114 DFLL FCPU clock source driver

I'd drop the filename.

> + * Copyright (C) 2012-2013 NVIDIA Corporation.  All rights reserved.

This is 2012-2013 whereas the other files were only 2013?

> +#define DROOP_RATE_MIN			1000000
> +
> +

Double blank line.

> +static int tegra114_dfll_fcpu_probe(struct platform_device *pdev)
> +{
> +	struct tegra_dfll_soc_data soc;

Since most of the data in that struct is static, why not just make it a
static global, and initialize the constant fields. That would avoid the
need to copy it inside tegra_dfll_register(), and avoid the need to
assign the fields later in this function.

> +	int r, speedo_id, process_id;
> +
> +	speedo_id = tegra_get_cpu_speedo_id();
> +	process_id = tegra_get_cpu_process_id();
> +
> +	memset(&soc, 0, sizeof(soc));
> +	soc.driver_name = DRIVER_NAME;

Why is that needed? Shouldn't the library code be using
dev_name(&pdev->dev) or similar?

> +	r = tegra_dfll_register(pdev, &soc);
> +	if (!r)
> +		return r;
> +
> +	return 0;

if (!r) means if (r == 0) so this always returns 0. I assume that meant
if (r), but even simpler would be:

	return tegra_dfll_register(...);

> +static struct platform_driver tegra114_dfll_fcpu_driver = {
...
> +};
> +
> +module_platform_driver(tegra114_dfll_fcpu_driver);

The blank line before module_platform_driver() is customarily omitted.

> +MODULE_LICENSE("GPL");

"GPL v2"

> +MODULE_ALIAS("platform:" DRIVER_NAME);

That isn't needed since we only probe using DT now.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 4/6] ARM: DTS: tegra: add the DFLL IP block to the T114 SoC file
  2013-12-20  0:05   ` Stephen Warren
@ 2014-01-14  6:27     ` Paul Walmsley
  2014-01-14  6:32       ` Paul Walmsley
  2014-01-15 19:50       ` Gerhard Sittig
       [not found]     ` <52D4D314.3000208@nvidia.com>
  1 sibling, 2 replies; 19+ messages in thread
From: Paul Walmsley @ 2014-01-14  6:27 UTC (permalink / raw)
  To: linux-arm-kernel


(Resending, and revising - the original wasn't fully baked in several 
regards..)

Hello Stephen,

thanks for your review.

On Thu, 19 Dec 2013, Stephen Warren wrote:

> On 12/19/2013 05:49 AM, Paul Walmsley wrote:
>> Add basic DT bindings for the DFLL IP block for the NVIDIA Tegra114 SoC.
>
>> diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt
>
>> +- clocks : Must contain an array of two-cell arrays, one per clock.
>> +           DFLL source clocks.  At minimum this should include the
>> +           reference clock source and the IP block's main clock
>> +           source.  Also it should contain the DFLL's I2C controller
>> +           clock source.  The format is <&clock-provider-phandle
>> +           clock-id>.
>
> Entries in "clocks" aren't two cells, they're a phandle plus as many
> cells as the node referenced by the phandle specifies.

It's worth noting that the clock binding documentation itself refers to 
pairs:

----

clocks:		List of phandle and clock specifier pairs, one pair
 		for each clock input to the device.  Note: if the
 		clock provider specifies '0' for #clock-cells, then
 		only the phandle portion of the pair will appear.

----

https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/clock/clock-bindings.txt#n50

But given the ambiguity of that documentation, I basically agree, so have 
changed it to:

- clocks : Must contain an array of clock specifiers, one per clock.
            DFLL source clocks.  At minimum this should include the
            reference clock source and the IP block's main clock
            source.  Also it should contain the DFLL's I2C controller
            clock source.  The format is <&clock-provider-phandle
            clock-id>.

>> +
>> +- clock-names : Must contain an array of strings, one per 'clocks'
>> +                two-cell array.  The position in the array of these
>
> clock-names defines the set of entries in clocks, not the other way around.

Hmm.  Referring to the DT clock binding documentation, it lists the 
'clock-names' property as optional, and the 'clocks' property as 
mandatory:

-----

==Clock consumers==

Required properties:
clocks:		List of phandle and clock specifier pairs, one pair
 		for each clock input to the device.  Note: if the
 		clock provider specifies '0' for #clock-cells, then
 		only the phandle portion of the pair will appear.

Optional properties:
clock-names:	List of clock input name strings sorted in the same
 		order as the clocks property.  Consumers drivers
 		will use clock-names to match clock input names
 		with clocks specifiers.

-----

https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/clock/clock-bindings.txt#n47

Considering that of_clk_get() doesn't require names, the situation appears 
to be the way that the original patch described it (modulo the part about 
"two cells.")

>> +                strings must correspond to the position in the 'clocks'
>> +                array (see above).  The DFLL driver currently requires
>> +                the "soc", "ref", and "i2c" clock names to be populated.
>
> The standard wording used by all the Tegra clock client bindings is now:
>
> - 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
>  - ref
>  - i2c
>
> For consistency, it'd be nice to adopt the same style here.

I've altered the 'clock-names' format along the lines of what you've 
suggested, but have not changed the 'clocks' format, per the above 
discussion:

-----

- clocks : Must contain an array of clock specifiers, one per clock.
            DFLL source clocks.  At minimum this should include the
            reference clock source and the IP block's main clock
            source.  Also it should contain the DFLL's I2C controller
            clock source.  The format is <&clock-provider-phandle
            clock-id>.

- clock-names : Must contain an array of strings, one per 'clocks'
                 cell.  The position in the array of these strings must
                 correspond to the position in the 'clocks' array (see
                 above).
   - soc
   - ref
   - i2c

-----


>> +Optional properties:
>> +
>> +- status : device availability -- managed by the DT integration code, not
>> +           the DFLL driver.  Should be set to "disabled" in the SoC
>> +           DTS file.
>
> That's such a core property that it's not worth documenting in every
> single binding.

That's fine.  Removed.

>
>> +
>
> Blank line at EOF.


Hehe.  Removed.

- Paul

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 4/6] ARM: DTS: tegra: add the DFLL IP block to the T114 SoC file
  2014-01-14  6:27     ` Paul Walmsley
@ 2014-01-14  6:32       ` Paul Walmsley
  2014-01-15 19:50       ` Gerhard Sittig
  1 sibling, 0 replies; 19+ messages in thread
From: Paul Walmsley @ 2014-01-14  6:32 UTC (permalink / raw)
  To: linux-arm-kernel


On 01/13/2014 10:27 PM, Paul Walmsley wrote:
> The format is <&clock-provider-phandle clock-id>.

Ah, one other change: the above string has been dropped.  Pesky examples ;-)

- Paul

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 5/6] ARM: DTS: tegra: add DFLL integration to the Dalmore DTS file
  2013-12-20  0:10   ` Stephen Warren
@ 2014-01-14  6:36     ` Paul Walmsley
  0 siblings, 0 replies; 19+ messages in thread
From: Paul Walmsley @ 2014-01-14  6:36 UTC (permalink / raw)
  To: linux-arm-kernel



On Thu, 19 Dec 2013, Stephen Warren wrote:

> On 12/19/2013 05:49 AM, Paul Walmsley wrote:
>> Expose the DFLL device on the NVIDIA Tegra114 Dalmore board, and connect
>> the DFLL (and FCPU cluster) voltage regulator.
>
>> diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt
>
>> +NVIDIA Tegra114 DFLL clocksource data in the board DTS file
>> +
>> +Optional properties:
>> +
>> +- status : device availability -- managed by the DT integration code, not
>> +           the DFLL driver.  Should be set to "okay" if the DFLL is to be
>> +           used on this board type.
>
> There's certainly no need to document the same DT property twice.

I've just dropped this section, per your earlier suggestion.

- Paul

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 4/6] ARM: DTS: tegra: add the DFLL IP block to the T114 SoC file
       [not found]     ` <52D4D314.3000208@nvidia.com>
@ 2014-01-14 17:43       ` Stephen Warren
  0 siblings, 0 replies; 19+ messages in thread
From: Stephen Warren @ 2014-01-14 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

On 01/13/2014 11:03 PM, Paul Walmsley wrote:
> On 12/19/2013 04:05 PM, Stephen Warren wrote:
>> On 12/19/2013 05:49 AM, Paul Walmsley wrote:
>>> Add basic DT bindings for the DFLL IP block for the NVIDIA Tegra114 SoC.
>>> diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt

>>> +- clock-names : Must contain an array of strings, one per 'clocks'
>>> +                two-cell array.  The position in the array of these
>> clock-names defines the set of entries in clocks, not the other way around.
> 
> Hmm.  If one refers to the DT clock binding documentation, it lists the
> 'clock-names' property as optional, and the 'clocks' property as mandatory:
> 
> -----
> 
> |==Clock consumers==
> 
> Required properties:
> clocks:		List of phandle and clock specifier pairs, one pair
> 		for each clock input to the device.  Note: if the
> 		clock provider specifies '0' for #clock-cells, then
> 		only the phandle portion of the pair will appear.
> 
> Optional properties:
> clock-names:	List of clock input name strings sorted in the same
> 		order as the clocks property.  Consumers drivers
> 		will use clock-names to match clock input names
> 		with clocks specifiers.
> 
> -----
> |
> 
> 
>  https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/clock/clock-bindings.txt#n47
> 
> Considering that of_clk_get() doesn't require names, it certainly
> appears to be the way that the original patch described it (modulo the
> bit about 'cells'; which has been fixed.)

If the binding defines that clock must exist at certain indices in the
clock property, and clock-names isn't used, then the clocks property
defines which clocks must exist.

However, if the binding is defining clock names, then the clock-names
property is the master that defines which clocks exist. In this case,
the index of entries in the clocks property is undefined by the binding;
it must simply match the clock-names property, which can be arbitrarily
ordered by the user.

Since this is a new binding, we should use the clock-names style to
ensure easy future backwards-compatible changes.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 4/6] ARM: DTS: tegra: add the DFLL IP block to the T114 SoC file
  2014-01-14  6:27     ` Paul Walmsley
  2014-01-14  6:32       ` Paul Walmsley
@ 2014-01-15 19:50       ` Gerhard Sittig
  2014-01-15 20:09         ` Paul Walmsley
  1 sibling, 1 reply; 19+ messages in thread
From: Gerhard Sittig @ 2014-01-15 19:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 13, 2014 at 22:27 -0800, Paul Walmsley wrote:
> 
> On Thu, 19 Dec 2013, Stephen Warren wrote:
> 
> >On 12/19/2013 05:49 AM, Paul Walmsley wrote:
> >>Add basic DT bindings for the DFLL IP block for the NVIDIA Tegra114 SoC.
> >
> >>diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt
> >
> >>+- clocks : Must contain an array of two-cell arrays, one per clock.
> >>+           DFLL source clocks.  At minimum this should include the
> >>+           reference clock source and the IP block's main clock
> >>+           source.  Also it should contain the DFLL's I2C controller
> >>+           clock source.  The format is <&clock-provider-phandle
> >>+           clock-id>.
> >
> >Entries in "clocks" aren't two cells, they're a phandle plus as many
> >cells as the node referenced by the phandle specifies.
> 
> It's worth noting that the clock binding documentation itself refers
> to pairs:
> 
> ----
> 
> clocks:		List of phandle and clock specifier pairs, one pair
> 		for each clock input to the device.  Note: if the
> 		clock provider specifies '0' for #clock-cells, then
> 		only the phandle portion of the pair will appear.
> 
> ----
> 
> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/clock/clock-bindings.txt#n50
> 
> But given the ambiguity of that documentation, I basically agree, so
> have changed it to:

Please note that there neither is an ambiguity nor a conflict
here, and that you actually acknowledge what Stephen said:

Clocks get referenced by a pair which consists of a phandle for
the clock provider and a clock specifier.  The clock specifier is
made of as many cells as the clock provider's #clock-cells
property defines (including none, or any other number than 1).

This is exactly what Stephen said:  A "clocks" item does not need
to have two cells.  The pair of phandle and clock specifier don't
necessarily translate into two cells, instead the number of cells
depends on the clock provider.

Also note that the phandle is specifically _not_ part of the
clock specifier.


virtually yours
Gerhard Sittig
-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr. 5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80  Email: office at denx.de

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 4/6] ARM: DTS: tegra: add the DFLL IP block to the T114 SoC file
  2014-01-15 19:50       ` Gerhard Sittig
@ 2014-01-15 20:09         ` Paul Walmsley
  0 siblings, 0 replies; 19+ messages in thread
From: Paul Walmsley @ 2014-01-15 20:09 UTC (permalink / raw)
  To: linux-arm-kernel

On 01/15/2014 11:50 AM, Gerhard Sittig wrote:
> On Mon, Jan 13, 2014 at 22:27 -0800, Paul Walmsley wrote:
>> On Thu, 19 Dec 2013, Stephen Warren wrote:
>>
>>> On 12/19/2013 05:49 AM, Paul Walmsley wrote:
>>>> Add basic DT bindings for the DFLL IP block for the NVIDIA Tegra114 SoC.
>>>> diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra114-dfll.txt
>>>> +- clocks : Must contain an array of two-cell arrays, one per clock.
>>>> +           DFLL source clocks.  At minimum this should include the
>>>> +           reference clock source and the IP block's main clock
>>>> +           source.  Also it should contain the DFLL's I2C controller
>>>> +           clock source.  The format is <&clock-provider-phandle
>>>> +           clock-id>.
>>> Entries in "clocks" aren't two cells, they're a phandle plus as many
>>> cells as the node referenced by the phandle specifies.
>> It's worth noting that the clock binding documentation itself refers
>> to pairs:
>>
>> ----
>>
>> clocks:		List of phandle and clock specifier pairs, one pair
>> 		for each clock input to the device.  Note: if the
>> 		clock provider specifies '0' for #clock-cells, then
>> 		only the phandle portion of the pair will appear.
>>
>> ----
>>
>> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/clock/clock-bindings.txt#n50
>>
>> But given the ambiguity of that documentation, I basically agree, so
>> have changed it to:
> Please note that there neither is an ambiguity nor a conflict
> here, and that you actually acknowledge what Stephen said:

I do not agree that the Documentation is unambiguous.

It is not correct to refer to a "pair" without a second item as a "pair."

> This is exactly what Stephen said:  A "clocks" item does not need
> to have two cells.  The pair of phandle and clock specifier don't
> necessarily translate into two cells, instead the number of cells
> depends on the clock provider.

I do agree with this, and have updated the documentation accordingly.


- Paul

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2014-01-15 20:09 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-12-19 12:36 [PATCH 0/6] clk: tegra: add basic support for the DFLL clocksource Paul Walmsley
2013-12-19 12:36 ` [PATCH 1/6] ARM: tegra: fuse: add functions to read speedo ID and process ID Paul Walmsley
2013-12-19 23:09   ` Stephen Warren
2013-12-19 12:36 ` [PATCH 2/6] ARM: tegra114: fuse: add DFLL FCPU minimum voltage override test function Paul Walmsley
2013-12-19 23:12   ` Stephen Warren
2013-12-19 12:37 ` [PATCH 3/6] clk: tegra: add library for the DFLL clocksource (open-loop mode) Paul Walmsley
2013-12-19 23:57   ` Stephen Warren
2013-12-19 12:49 ` [PATCH 4/6] ARM: DTS: tegra: add the DFLL IP block to the T114 SoC file Paul Walmsley
2013-12-20  0:05   ` Stephen Warren
2014-01-14  6:27     ` Paul Walmsley
2014-01-14  6:32       ` Paul Walmsley
2014-01-15 19:50       ` Gerhard Sittig
2014-01-15 20:09         ` Paul Walmsley
     [not found]     ` <52D4D314.3000208@nvidia.com>
2014-01-14 17:43       ` Stephen Warren
2013-12-19 12:49 ` [PATCH 5/6] ARM: DTS: tegra: add DFLL integration to the Dalmore DTS file Paul Walmsley
2013-12-20  0:10   ` Stephen Warren
2014-01-14  6:36     ` Paul Walmsley
2013-12-19 12:49 ` [PATCH 6/6] clk: tegra: add Tegra114 FCPU DFLL clocksource platform driver Paul Walmsley
2013-12-20  0:18   ` Stephen Warren

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).