devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420
@ 2014-05-22 17:21 Abhilash Kesavan
  2014-05-22 17:21 ` [PATCH RFC v2 1/7] clk: exynos5250: add aliases for clocks used by devfreq Abhilash Kesavan
                   ` (7 more replies)
  0 siblings, 8 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-05-22 17:21 UTC (permalink / raw)
  To: linux-arm-kernel, kgene.kim, t.figa, myungjoo.ham,
	rafael.j.wysocki, linux-pm, devicetree
  Cc: kesavan.abhilash

This patchset adds devfreq support on Exynos5250 and Exynos5420.
The patches add the missing INT clock support for exynos5250 and
also add a new 5420 driver. We also have patches which add the
PPMU node and fix a typo in the exynos5250 driver.

Changes since RFC v1:
	- Exynos5420 support has been added to the existent Exynos5250
	  driver itself.
	- Rebased on Chanwoo Choi's devfreq consolidation patchset.
	- Removed unused clocks from the clock list
	- Used the PPMU nodes with different compatible strings to
	  differentiate between exynos5250 and exynos5420.

The patches have been tested on Exynos5250 based Snow board and Exynos5420
based Peach-Pit boards. They have been prepared on linux-next(20140521) with
the devfreq for-next branch merged. Also applied is the "devfreq resource
management" series from Chanwoo Choi[1].
[1] https://lkml.org/lkml/2014/4/25/826

Abhilash Kesavan (3):
  ARM: dts: Add PPMU device tree support for Exynos5250
  ARM: dts: Add PPMU device tree support for Exynos5420
  PM / devfreq: exynos: Fix typo in macro

Andrew Bresticker (2):
  clk: exynos5250: add aliases for clocks used by devfreq
  PM / devfreq: exynos5250: migrate to common-clock

Arjun.K.V (2):
  clk: exynos5420: Add aliases for clocks used by devfreq
  PM / devfreq: Add devfreq driver for Exynos5420

 .../bindings/arm/exynos/ppmu-exynos5.txt           |   27 +
 arch/arm/boot/dts/exynos5250.dtsi                  |    8 +
 arch/arm/boot/dts/exynos5420.dtsi                  |    7 +
 drivers/clk/samsung/clk-exynos5250.c               |   25 +-
 drivers/clk/samsung/clk-exynos5420.c               |   73 ++-
 drivers/devfreq/Kconfig                            |   10 +-
 drivers/devfreq/exynos/exynos5_bus.c               |  665 +++++++++++++++++---
 drivers/devfreq/exynos/exynos_ppmu.h               |    2 +-
 8 files changed, 703 insertions(+), 114 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt

-- 
1.7.9.5


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

* [PATCH RFC v2 1/7] clk: exynos5250: add aliases for clocks used by devfreq
  2014-05-22 17:21 [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420 Abhilash Kesavan
@ 2014-05-22 17:21 ` Abhilash Kesavan
  2014-07-15 18:33   ` [PATCH v3 " Abhilash Kesavan
  2014-05-22 17:21 ` [PATCH RFC v2 2/7] clk: exynos5420: Add " Abhilash Kesavan
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Abhilash Kesavan @ 2014-05-22 17:21 UTC (permalink / raw)
  To: linux-arm-kernel, kgene.kim, t.figa, myungjoo.ham,
	rafael.j.wysocki, linux-pm, devicetree
  Cc: kesavan.abhilash

From: Andrew Bresticker <abrestic@chromium.org>

Devfreq does not support DT-based lookup of these peripheral clocks,
so add aliases for them.

Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
 drivers/clk/samsung/clk-exynos5250.c |   25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
index 8848859..be2d7d8 100644
--- a/drivers/clk/samsung/clk-exynos5250.c
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -202,6 +202,11 @@ PNAME(mout_aclk400_p)	= { "mout_aclk400_g3d_mid", "mout_gpll" };
 PNAME(mout_aclk200_sub_p) = { "fin_pll", "div_aclk200" };
 PNAME(mout_aclk266_sub_p) = { "fin_pll", "div_aclk266" };
 PNAME(mout_aclk333_sub_p) = { "fin_pll", "div_aclk333" };
+PNAME(mout_aclk300_disp1_p) = { "mout_aclk300_disp1_mid",
+				"mout_aclk300_disp1_mid1" };
+PNAME(mout_aclk300_gscl_p) = { "mout_aclk300_gscl_mid",
+				"mout_aclk300_gscl_mid1" };
+PNAME(mout_aclk300_gscl_mid1_p) = { "mout_vpll", "mout_cpll" };
 PNAME(mout_hdmi_p)	= { "div_hdmi_pixel", "sclk_hdmiphy" };
 PNAME(mout_usb3_p)	= { "mout_mpll_user", "mout_cpll" };
 PNAME(mout_group1_p)	= { "fin_pll", "fin_pll", "sclk_hdmi27m",
@@ -278,9 +283,17 @@ static struct samsung_mux_clock exynos5250_mux_clks[] __initdata = {
 	 */
 	MUX(0, "mout_aclk166", mout_aclk166_p, SRC_TOP0, 8, 1),
 	MUX(0, "mout_aclk200", mout_aclk200_p, SRC_TOP0, 12, 1),
+	MUX(0, "mout_aclk300_disp1_mid", mout_aclk200_p, SRC_TOP0, 14, 1),
+	MUX(0, "mout_aclk300_disp1", mout_aclk300_disp1_p, SRC_TOP0, 15, 1),
 	MUX(0, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1),
 	MUX(0, "mout_aclk400_g3d_mid", mout_aclk200_p, SRC_TOP0, 20, 1),
+	MUX(0, "mout_aclk300_gscl_mid", mout_aclk200_p, SRC_TOP0, 24, 1),
+	MUX(0, "mout_aclk300_gscl", mout_aclk300_gscl_p, SRC_TOP0, 25, 2),
 
+	MUX(0, "mout_aclk300_disp1_mid1", mout_aclk300_gscl_mid1_p,
+					SRC_TOP1, 8, 1),
+	MUX(0, "mout_aclk300_gscl_mid1", mout_aclk300_gscl_mid1_p,
+					SRC_TOP1, 12, 1),
 	MUX(0, "mout_aclk400_g3d", mout_aclk400_p, SRC_TOP1, 28, 1),
 
 	MUX(0, "mout_cpll", mout_cpll_p, SRC_TOP2, 8, 1),
@@ -357,13 +370,17 @@ static struct samsung_div_clock exynos5250_div_clks[] __initdata = {
 	 * CMU_TOP
 	 */
 	DIV(0, "div_aclk66", "div_aclk66_pre", DIV_TOP0, 0, 3),
-	DIV(0, "div_aclk166", "mout_aclk166", DIV_TOP0, 8, 3),
-	DIV(0, "div_aclk200", "mout_aclk200", DIV_TOP0, 12, 3),
-	DIV(0, "div_aclk266", "mout_mpll_user", DIV_TOP0, 16, 3),
-	DIV(0, "div_aclk333", "mout_aclk333", DIV_TOP0, 20, 3),
+	DIV_A(0, "div_aclk166", "mout_aclk166", DIV_TOP0, 8, 3, "aclk166_d"),
+	DIV_A(0, "div_aclk200", "mout_aclk200", DIV_TOP0, 12, 3, "aclk200_d"),
+	DIV_A(0, "div_aclk266", "mout_mpll_user", DIV_TOP0, 16, 3, "aclk266_d"),
+	DIV_A(0, "div_aclk333", "mout_aclk333", DIV_TOP0, 20, 3, "aclk333_d"),
 	DIV(0, "div_aclk400_g3d", "mout_aclk400_g3d", DIV_TOP0,
 							24, 3),
+	DIV_A(0, "div_aclk300_disp1", "mout_aclk300_disp1",
+					DIV_TOP0, 28, 3, "aclk300_disp1_d"),
 
+	DIV_A(0, "div_aclk300_gscl", "mout_aclk300_gscl",
+					DIV_TOP1, 12, 3, "aclk300_gscl_d"),
 	DIV(0, "div_aclk66_pre", "mout_mpll_user", DIV_TOP1, 24, 3),
 
 	DIV(0, "div_cam_bayer", "mout_cam_bayer", DIV_GSCL, 12, 4),
-- 
1.7.9.5


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

* [PATCH RFC v2 2/7] clk: exynos5420: Add aliases for clocks used by devfreq
  2014-05-22 17:21 [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420 Abhilash Kesavan
  2014-05-22 17:21 ` [PATCH RFC v2 1/7] clk: exynos5250: add aliases for clocks used by devfreq Abhilash Kesavan
@ 2014-05-22 17:21 ` Abhilash Kesavan
  2014-07-15 18:34   ` [PATCH v3 " Abhilash Kesavan
  2014-05-22 17:21 ` [PATCH RFC v2 4/7] ARM: dts: Add PPMU device tree support for Exynos5420 Abhilash Kesavan
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Abhilash Kesavan @ 2014-05-22 17:21 UTC (permalink / raw)
  To: linux-arm-kernel, kgene.kim, t.figa, myungjoo.ham,
	rafael.j.wysocki, linux-pm, devicetree
  Cc: kesavan.abhilash

From: "Arjun.K.V" <arjun.kv@samsung.com>

Devfreq does not support DT-based lookup of these peripheral clocks,
so add aliases for them.

Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
Signed-off-by: Arjun.K.V <arjun.kv@samsung.com>
Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
 drivers/clk/samsung/clk-exynos5420.c |   73 ++++++++++++++++++++--------------
 1 file changed, 44 insertions(+), 29 deletions(-)

diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
index 9d7d7ee..93b9422 100644
--- a/drivers/clk/samsung/clk-exynos5420.c
+++ b/drivers/clk/samsung/clk-exynos5420.c
@@ -554,21 +554,25 @@ struct samsung_mux_clock exynos5420_mux_clks[] __initdata = {
 	MUX(0, "mout_aclk400_isp", mout_group1_p, SRC_TOP0, 0, 2),
 	MUX_A(0, "mout_aclk400_mscl", mout_group1_p,
 				SRC_TOP0, 4, 2, "aclk400_mscl"),
-	MUX(0, "mout_aclk400_wcore", mout_group1_p, SRC_TOP0, 16, 2),
-	MUX(0, "mout_aclk100_noc", mout_group1_p, SRC_TOP0, 20, 2),
-
+	MUX_A(0, "mout_aclk400_wcore", mout_group1_p,
+				SRC_TOP0, 16, 2, "aclk400_wcore"),
+	MUX_A(0, "mout_aclk100_noc", mout_group1_p,
+				SRC_TOP0, 20, 2, "aclk100_noc"),
 	MUX(0, "mout_aclk333_432_gscl", mout_group4_p, SRC_TOP1, 0, 2),
 	MUX(0, "mout_aclk333_432_isp", mout_group4_p,
 				SRC_TOP1, 4, 2),
 	MUX(0, "mout_aclk333_432_isp0", mout_group4_p, SRC_TOP1, 12, 2),
-	MUX(0, "mout_aclk266", mout_group1_p, SRC_TOP1, 20, 2),
+	MUX_A(0, "mout_aclk266", mout_group1_p, SRC_TOP1, 20, 2, "aclk266"),
 	MUX(0, "mout_aclk333", mout_group1_p, SRC_TOP1, 28, 2),
 
-	MUX(0, "mout_aclk400_disp1", mout_group1_p, SRC_TOP2, 4, 2),
+	MUX_A(0, "mout_aclk400_disp1", mout_group1_p,
+				SRC_TOP2, 4, 2, "aclk400_disp1"),
 	MUX(0, "mout_aclk333_g2d", mout_group1_p, SRC_TOP2, 8, 2),
 	MUX(0, "mout_aclk266_g2d", mout_group1_p, SRC_TOP2, 12, 2),
-	MUX(0, "mout_aclk300_jpeg", mout_group1_p, SRC_TOP2, 20, 2),
-	MUX(0, "mout_aclk300_disp1", mout_group1_p, SRC_TOP2, 24, 2),
+	MUX_A(0, "mout_aclk300_jpeg", mout_group1_p,
+				SRC_TOP2, 20, 2, "aclk300_jpeg"),
+	MUX_A(0, "mout_aclk300_disp1", mout_group1_p,
+				SRC_TOP2, 24, 2, "aclk300_disp1"),
 	MUX(0, "mout_aclk300_gscl", mout_group1_p, SRC_TOP2, 28, 2),
 
 	MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7, 20, 2),
@@ -577,8 +581,8 @@ struct samsung_mux_clock exynos5420_mux_clks[] __initdata = {
 };
 
 struct samsung_div_clock exynos5420_div_clks[] __initdata = {
-	DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore_bpll",
-			DIV_TOP0, 16, 3),
+	DIV_A(0, "dout_aclk400_wcore", "mout_aclk400_wcore_bpll",
+			DIV_TOP0, 16, 3, "aclk400_wcore_d"),
 };
 
 static struct samsung_mux_clock exynos5x_mux_clks[] __initdata = {
@@ -593,12 +597,15 @@ static struct samsung_mux_clock exynos5x_mux_clks[] __initdata = {
 	MUX(0, "mout_kfc", mout_kfc_p, SRC_KFC, 16, 1),
 
 	MUX(0, "mout_aclk200", mout_group1_p, SRC_TOP0, 8, 2),
-	MUX(0, "mout_aclk200_fsys2", mout_group1_p, SRC_TOP0, 12, 2),
-	MUX(0, "mout_pclk200_fsys", mout_group1_p, SRC_TOP0, 24, 2),
-	MUX(0, "mout_aclk200_fsys", mout_group1_p, SRC_TOP0, 28, 2),
+	MUX_A(0, "mout_aclk200_fsys2", mout_group1_p,
+		SRC_TOP0, 12, 2, "aclk200_fsys2"),
+	MUX_A(0, "mout_pclk200_fsys", mout_group1_p,
+		SRC_TOP0, 24, 2, "pclk200_fsys"),
+	MUX_A(0, "mout_aclk200_fsys", mout_group1_p,
+		SRC_TOP0, 28, 2, "aclk200_fsys"),
 
-	MUX(0, "mout_aclk66", mout_group1_p, SRC_TOP1, 8, 2),
-	MUX(0, "mout_aclk166", mout_group1_p, SRC_TOP1, 24, 2),
+	MUX_A(0, "mout_aclk66", mout_group1_p, SRC_TOP1, 8, 2, "aclk66"),
+	MUX_A(0, "mout_aclk166", mout_group1_p, SRC_TOP1, 24, 2, "aclk166"),
 
 	MUX(0, "mout_aclk_g3d", mout_group5_p, SRC_TOP2, 16, 1),
 
@@ -650,14 +657,14 @@ static struct samsung_mux_clock exynos5x_mux_clks[] __initdata = {
 	MUX(0, "mout_user_aclk300_gscl", mout_user_aclk300_gscl_p,
 			SRC_TOP5, 28, 1),
 
-	MUX(0, "mout_sclk_mpll", mout_mpll_p, SRC_TOP6, 0, 1),
+	MUX_A(0, "mout_sclk_mpll", mout_mpll_p, SRC_TOP6, 0, 1, "mout_mpll"),
 	MUX(CLK_MOUT_VPLL, "mout_sclk_vpll", mout_vpll_p, SRC_TOP6, 4, 1),
 	MUX(0, "mout_sclk_spll", mout_spll_p, SRC_TOP6, 8, 1),
-	MUX(0, "mout_sclk_ipll", mout_ipll_p, SRC_TOP6, 12, 1),
+	MUX_A(0, "mout_sclk_ipll", mout_ipll_p, SRC_TOP6, 12, 1, "mout_ipll"),
 	MUX(0, "mout_sclk_rpll", mout_rpll_p, SRC_TOP6, 16, 1),
 	MUX(0, "mout_sclk_epll", mout_epll_p, SRC_TOP6, 20, 1),
-	MUX(0, "mout_sclk_dpll", mout_dpll_p, SRC_TOP6, 24, 1),
-	MUX(0, "mout_sclk_cpll", mout_cpll_p, SRC_TOP6, 28, 1),
+	MUX_A(0, "mout_sclk_dpll", mout_dpll_p, SRC_TOP6, 24, 1, "mout_dpll"),
+	MUX_A(0, "mout_sclk_cpll", mout_cpll_p, SRC_TOP6, 28, 1, "mout_cpll"),
 
 	MUX(0, "mout_sw_aclk400_isp", mout_sw_aclk400_isp_p,
 			SRC_TOP10, 0, 1),
@@ -751,29 +758,36 @@ static struct samsung_div_clock exynos5x_div_clks[] __initdata = {
 	DIV(0, "sclk_kpll", "mout_kpll", DIV_KFC0, 24, 3),
 
 	DIV(0, "dout_aclk400_isp", "mout_aclk400_isp", DIV_TOP0, 0, 3),
-	DIV(0, "dout_aclk400_mscl", "mout_aclk400_mscl", DIV_TOP0, 4, 3),
+	DIV_A(0, "dout_aclk400_mscl", "mout_aclk400_mscl",
+			DIV_TOP0, 4, 3, "aclk400_mscl_d"),
 	DIV(0, "dout_aclk200", "mout_aclk200", DIV_TOP0, 8, 3),
-	DIV(0, "dout_aclk200_fsys2", "mout_aclk200_fsys2", DIV_TOP0, 12, 3),
-	DIV(0, "dout_aclk100_noc", "mout_aclk100_noc", DIV_TOP0, 20, 3),
-	DIV(0, "dout_pclk200_fsys", "mout_pclk200_fsys", DIV_TOP0, 24, 3),
-	DIV(0, "dout_aclk200_fsys", "mout_aclk200_fsys", DIV_TOP0, 28, 3),
+	DIV_A(0, "dout_aclk200_fsys2", "mout_aclk200_fsys2",
+			DIV_TOP0, 12, 3, "aclk200_fsys2_d"),
+	DIV_A(0, "dout_aclk100_noc", "mout_aclk100_noc",
+			DIV_TOP0, 20, 3, "aclk100_noc_d"),
+	DIV_A(0, "dout_pclk200_fsys", "mout_pclk200_fsys",
+			DIV_TOP0, 24, 3, "pclk200_fsys_d"),
+	DIV_A(0, "dout_aclk200_fsys", "mout_aclk200_fsys",
+			DIV_TOP0, 28, 3, "aclk200_fsys_d"),
 
 	DIV(0, "dout_aclk333_432_gscl", "mout_aclk333_432_gscl",
 			DIV_TOP1, 0, 3),
 	DIV(0, "dout_aclk333_432_isp", "mout_aclk333_432_isp",
 			DIV_TOP1, 4, 3),
-	DIV(0, "dout_aclk66", "mout_aclk66", DIV_TOP1, 8, 6),
+	DIV_A(0, "dout_aclk66", "mout_aclk66", DIV_TOP1, 8, 6, "aclk66_d"),
 	DIV(0, "dout_aclk333_432_isp0", "mout_aclk333_432_isp0",
 			DIV_TOP1, 16, 3),
-	DIV(0, "dout_aclk266", "mout_aclk266", DIV_TOP1, 20, 3),
-	DIV(0, "dout_aclk166", "mout_aclk166", DIV_TOP1, 24, 3),
+	DIV_A(0, "dout_aclk266", "mout_aclk266", DIV_TOP1, 20, 3, "aclk266_d"),
+	DIV_A(0, "dout_aclk166", "mout_aclk166", DIV_TOP1, 24, 3, "aclk166_d"),
 	DIV(0, "dout_aclk333", "mout_aclk333", DIV_TOP1, 28, 3),
 
 	DIV(0, "dout_aclk333_g2d", "mout_aclk333_g2d", DIV_TOP2, 8, 3),
 	DIV(0, "dout_aclk266_g2d", "mout_aclk266_g2d", DIV_TOP2, 12, 3),
 	DIV(0, "dout_aclk_g3d", "mout_aclk_g3d", DIV_TOP2, 16, 3),
-	DIV(0, "dout_aclk300_jpeg", "mout_aclk300_jpeg", DIV_TOP2, 20, 3),
-	DIV(0, "dout_aclk300_disp1", "mout_aclk300_disp1", DIV_TOP2, 24, 3),
+	DIV_A(0, "dout_aclk300_jpeg", "mout_aclk300_jpeg",
+			DIV_TOP2, 20, 3, "aclk300_jpeg_d"),
+	DIV_A(0, "dout_aclk300_disp1", "mout_aclk300_disp1",
+			DIV_TOP2, 24, 3, "aclk300_disp1_d"),
 	DIV(0, "dout_aclk300_gscl", "mout_aclk300_gscl", DIV_TOP2, 28, 3),
 
 	/* DISP1 Block */
@@ -782,7 +796,8 @@ static struct samsung_div_clock exynos5x_div_clks[] __initdata = {
 	DIV(0, "dout_dp1", "mout_dp1", DIV_DISP10, 24, 4),
 	DIV(CLK_DOUT_PIXEL, "dout_hdmi_pixel", "mout_pixel", DIV_DISP10, 28, 4),
 	DIV(0, "dout_disp1_blk", "aclk200_disp1", DIV2_RATIO0, 16, 2),
-	DIV(0, "dout_aclk400_disp1", "mout_aclk400_disp1", DIV_TOP2, 4, 3),
+	DIV_A(0, "dout_aclk400_disp1", "mout_aclk400_disp1",
+			DIV_TOP2, 4, 3, "aclk400_disp1_d"),
 
 	/* Audio Block */
 	DIV(0, "dout_maudio0", "mout_maudio0", DIV_MAU, 20, 4),
-- 
1.7.9.5


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

* [PATCH RFC v2 3/7] ARM: dts: Add PPMU device tree support for Exynos5250
       [not found] ` <1400779322-4410-1-git-send-email-a.kesavan-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
@ 2014-05-22 17:21   ` Abhilash Kesavan
  2014-07-15 18:34     ` [PATCH v3 " Abhilash Kesavan
  2014-05-22 17:22   ` [PATCH RFC v2 7/7] PM / devfreq: Add devfreq driver for Exynos5420 Abhilash Kesavan
  1 sibling, 1 reply; 32+ messages in thread
From: Abhilash Kesavan @ 2014-05-22 17:21 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	kgene.kim-Sze3O3UU22JBDgjK7y7TUQ, t.figa-Sze3O3UU22JBDgjK7y7TUQ,
	myungjoo.ham-Sze3O3UU22JBDgjK7y7TUQ,
	rafael.j.wysocki-ral2JQCrhuEAvxtiuMwx3w,
	linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: kesavan.abhilash-Re5JQEeQqe8AvxtiuMwx3w

PPMU is required by the exynos5250 devfreq driver. Add a device
tree node for it.

Signed-off-by: Abhilash Kesavan <a.kesavan-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
 .../bindings/arm/exynos/ppmu-exynos5.txt           |   27 ++++++++++++++++++++
 arch/arm/boot/dts/exynos5250.dtsi                  |    8 ++++++
 2 files changed, 35 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt

diff --git a/Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt b/Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt
new file mode 100644
index 0000000..08a4aaf
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt
@@ -0,0 +1,27 @@
+Exynos PPMU driver
+-------------------
+
+Performance events are primitive values used to get performance data. These
+events provide information about the behavior of the SoC that can be used
+when analyzing system performance. These events are made visible using the
+PPMU logic.
+Exynos PPMU driver is used by the exynos5 devfreq drivers to control the
+bus frequency/voltage.
+
+Required properties:
+- compatible: "samsung,exynos5250-int-busfreq", "samsung,exynos5420-int-busfreq"
+- reg:
+	* physical base address of the PPMUs (e.g DDR, Right Bus, Left bus etc)
+	and length of memory mapped region.
+
+Example:
+--------
+
+	ppmu {
+		compatible = "samsung,exynos5250-int-busfreq";
+		reg = <0x10C40000 0x2000
+		       0x10C50000 0x2000
+		       0x10C60000 0x2000
+		       0x10CB0000 0x2000
+		       0x13660000 0x2000>;
+	};
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index 68a3e6f..f20355a 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -750,4 +750,12 @@
 		clocks = <&clock 348>;
 		clock-names = "secss";
 	};
+
+	ppmu {
+		compatible = "samsung,exynos5250-int-busfreq";
+	            reg = <0x10C40000 0x2000	/* PPMU_DDR_C */
+			   0x10C50000 0x2000	/* PPMU_DDR_L */
+			   0x10CB0000 0x2000	/* PPMU_DDR_R */
+			   0x13660000 0x2000>;	/* PPMU_RIGHT */
+	};
 };
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH RFC v2 4/7] ARM: dts: Add PPMU device tree support for Exynos5420
  2014-05-22 17:21 [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420 Abhilash Kesavan
  2014-05-22 17:21 ` [PATCH RFC v2 1/7] clk: exynos5250: add aliases for clocks used by devfreq Abhilash Kesavan
  2014-05-22 17:21 ` [PATCH RFC v2 2/7] clk: exynos5420: Add " Abhilash Kesavan
@ 2014-05-22 17:21 ` Abhilash Kesavan
  2014-05-22 17:50   ` Sergei Shtylyov
  2014-07-15 18:34   ` [PATCH v3 " Abhilash Kesavan
  2014-05-22 17:22 ` [PATCH RFC v2 5/7] PM / devfreq: exynos5250: migrate to common-clock Abhilash Kesavan
                   ` (4 subsequent siblings)
  7 siblings, 2 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-05-22 17:21 UTC (permalink / raw)
  To: linux-arm-kernel, kgene.kim, t.figa, myungjoo.ham,
	rafael.j.wysocki, linux-pm, devicetree
  Cc: kesavan.abhilash

PPMU is required by the exynos5420 devfreq driver. Add a device
tree node for it.

Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
 arch/arm/boot/dts/exynos5420.dtsi |    7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi
index 8e7e35c..6ab7b03 100644
--- a/arch/arm/boot/dts/exynos5420.dtsi
+++ b/arch/arm/boot/dts/exynos5420.dtsi
@@ -833,4 +833,11 @@
 		samsung,pmu-syscon = <&pmu_system_controller>;
 		#phy-cells = <1>;
 	};
+
+	ppmu {
+		compatible = "samsung,exynos5420-int-busfreq";
+		reg = <0x10D00000 0x2000	/* PPMU_DMC_0_0 */
+		       0x10D10000 0x2000	/* PPMU_DMC_0_1 */
+		       0x10D60000 0x2000>;	/* PPMU_DMC_1_0 */
+	};
 };
-- 
1.7.9.5


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

* [PATCH RFC v2 5/7] PM / devfreq: exynos5250: migrate to common-clock
  2014-05-22 17:21 [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420 Abhilash Kesavan
                   ` (2 preceding siblings ...)
  2014-05-22 17:21 ` [PATCH RFC v2 4/7] ARM: dts: Add PPMU device tree support for Exynos5420 Abhilash Kesavan
@ 2014-05-22 17:22 ` Abhilash Kesavan
  2014-07-15 18:35   ` [PATCH v3 " Abhilash Kesavan
  2014-05-22 17:22 ` [PATCH RFC v2 6/7] PM / devfreq: exynos: Fix typo in macro Abhilash Kesavan
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Abhilash Kesavan @ 2014-05-22 17:22 UTC (permalink / raw)
  To: linux-arm-kernel, kgene.kim, t.figa, myungjoo.ham,
	rafael.j.wysocki, linux-pm, devicetree
  Cc: kesavan.abhilash

From: Andrew Bresticker <abrestic@chromium.org>

Use the common-clock framework to scale frequencies for the various
peripheral clocks on the Exynos5250.

Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
 drivers/devfreq/exynos/exynos5_bus.c |  131 ++++++++++++++++++++++++++++++----
 1 file changed, 119 insertions(+), 12 deletions(-)

diff --git a/drivers/devfreq/exynos/exynos5_bus.c b/drivers/devfreq/exynos/exynos5_bus.c
index 6cd0392..1196653 100644
--- a/drivers/devfreq/exynos/exynos5_bus.c
+++ b/drivers/devfreq/exynos/exynos5_bus.c
@@ -57,7 +57,6 @@ struct busfreq_data_int {
 	struct notifier_block pm_notifier;
 	struct mutex lock;
 	struct pm_qos_request int_req;
-	struct clk *int_clk;
 };
 
 struct int_bus_opp_table {
@@ -66,6 +65,17 @@ struct int_bus_opp_table {
 	unsigned long volt;
 };
 
+struct int_clk_table {
+	unsigned int idx;
+	unsigned long freq;
+};
+
+struct int_clk {
+	const char *clk_name;
+	struct clk *clk;
+	struct int_clk_table *freq_table;
+};
+
 static struct int_bus_opp_table exynos5_int_opp_table[] = {
 	{LV_0, 266000, 1025000},
 	{LV_1, 200000, 1025000},
@@ -75,6 +85,98 @@ static struct int_bus_opp_table exynos5_int_opp_table[] = {
 	{0, 0, 0},
 };
 
+static struct int_clk_table aclk_166[] = {
+	{LV_0, 167000},
+	{LV_1, 111000},
+	{LV_2,  84000},
+	{LV_3,  84000},
+	{LV_4,  42000},
+};
+
+static struct int_clk_table aclk_200[] = {
+	{LV_0, 200000},
+	{LV_1, 160000},
+	{LV_2, 160000},
+	{LV_3, 134000},
+	{LV_4, 100000},
+};
+
+static struct int_clk_table aclk_266[] = {
+	{LV_0, 267000},
+	{LV_1, 200000},
+	{LV_2, 160000},
+	{LV_3, 134000},
+	{LV_4, 100000},
+};
+
+static struct int_clk_table aclk_333[] = {
+	{LV_0, 333000},
+	{LV_1, 167000},
+	{LV_2, 111000},
+	{LV_3, 111000},
+	{LV_4,  42000},
+};
+
+static struct int_clk_table aclk_300_disp1[] = {
+	{LV_0, 267000},
+	{LV_1, 267000},
+	{LV_2, 267000},
+	{LV_3, 267000},
+	{LV_4, 200000},
+};
+
+static struct int_clk_table aclk_300_gscl[] = {
+	{LV_0, 267000},
+	{LV_1, 267000},
+	{LV_2, 267000},
+	{LV_3, 200000},
+	{LV_4, 100000},
+};
+
+#define EXYNOS5_INT_CLK(name, tbl) {		\
+	.clk_name = name,			\
+	.freq_table = tbl,			\
+}
+
+static struct int_clk exynos5_int_clks[] = {
+	EXYNOS5_INT_CLK("aclk166_d", aclk_166),
+	EXYNOS5_INT_CLK("aclk200_d", aclk_200),
+	EXYNOS5_INT_CLK("aclk266_d", aclk_266),
+	EXYNOS5_INT_CLK("aclk333_d", aclk_333),
+	EXYNOS5_INT_CLK("aclk300_disp1_d", aclk_300_disp1),
+	EXYNOS5_INT_CLK("aclk300_gscl_d", aclk_300_gscl),
+};
+
+static int exynos5_int_set_rate(struct busfreq_data_int *data,
+				unsigned long rate)
+{
+	int index, i;
+
+	for (index = 0; index < ARRAY_SIZE(exynos5_int_opp_table); index++) {
+		if (exynos5_int_opp_table[index].clk == rate)
+			break;
+	}
+
+	if (index >= _LV_END)
+		return -EINVAL;
+
+	/* Change the system clock divider values */
+	for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
+		struct int_clk *clk_info = &exynos5_int_clks[i];
+		int ret;
+
+		ret = clk_set_rate(clk_info->clk,
+				clk_info->freq_table[index].freq * 1000);
+		if (ret) {
+			dev_err(data->dev, "Failed to set %s rate: %d\n",
+				clk_info->clk_name, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 static int exynos5_int_setvolt(struct busfreq_data_int *data,
 				unsigned long volt)
 {
@@ -126,7 +228,7 @@ static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
 	if (err)
 		goto out;
 
-	err = clk_set_rate(data->int_clk, freq * 1000);
+	err = exynos5_int_set_rate(data, freq);
 
 	if (err)
 		goto out;
@@ -220,7 +322,7 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
 		if (err)
 			goto unlock;
 
-		err = clk_set_rate(data->int_clk, freq * 1000);
+		err = exynos5_int_set_rate(data, freq);
 
 		if (err)
 			goto unlock;
@@ -300,10 +402,15 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 		return PTR_ERR(data->vdd_int);
 	}
 
-	data->int_clk = devm_clk_get(dev, "int_clk");
-	if (IS_ERR(data->int_clk)) {
-		dev_err(dev, "Cannot get clock \"int_clk\"\n");
-		return PTR_ERR(data->int_clk);
+	for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
+		struct int_clk *clk_info = &exynos5_int_clks[i];
+
+		clk_info->clk = devm_clk_get(dev, clk_info->clk_name);
+		if (IS_ERR(clk_info->clk)) {
+			dev_err(dev, "Failed to get clock %s\n",
+				clk_info->clk_name);
+			return PTR_ERR(clk_info->clk);
+		}
 	}
 
 	rcu_read_lock();
@@ -320,16 +427,16 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 	rcu_read_unlock();
 	data->curr_freq = initial_freq;
 
-	err = clk_set_rate(data->int_clk, initial_freq * 1000);
+	err = exynos5_int_setvolt(data, initial_volt);
+	if (err)
+		return err;
+
+	err = exynos5_int_set_rate(data, initial_freq);
 	if (err) {
 		dev_err(dev, "Failed to set initial frequency\n");
 		return err;
 	}
 
-	err = exynos5_int_setvolt(data, initial_volt);
-	if (err)
-		return err;
-
 	platform_set_drvdata(pdev, data);
 
 	busfreq_mon_reset(ppmu_data);
-- 
1.7.9.5


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

* [PATCH RFC v2 6/7] PM / devfreq: exynos: Fix typo in macro
  2014-05-22 17:21 [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420 Abhilash Kesavan
                   ` (3 preceding siblings ...)
  2014-05-22 17:22 ` [PATCH RFC v2 5/7] PM / devfreq: exynos5250: migrate to common-clock Abhilash Kesavan
@ 2014-05-22 17:22 ` Abhilash Kesavan
  2014-07-15 18:35   ` [PATCH v3 " Abhilash Kesavan
       [not found] ` <1400779322-4410-1-git-send-email-a.kesavan-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Abhilash Kesavan @ 2014-05-22 17:22 UTC (permalink / raw)
  To: linux-arm-kernel, kgene.kim, t.figa, myungjoo.ham,
	rafael.j.wysocki, linux-pm, devicetree
  Cc: kesavan.abhilash

Fix PPMU_BEVTSEL macro being used in the PPMU driver.

Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
 drivers/devfreq/exynos/exynos_ppmu.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/devfreq/exynos/exynos_ppmu.h b/drivers/devfreq/exynos/exynos_ppmu.h
index 71f17ba..d5c14eb 100644
--- a/drivers/devfreq/exynos/exynos_ppmu.h
+++ b/drivers/devfreq/exynos/exynos_ppmu.h
@@ -37,7 +37,7 @@
 
 #define PPMU_BEVT0SEL		0x1000
 #define PPMU_BEVTSEL_OFFSET	0x100
-#define PPMU_BEVTSEL(x)		(PPMU_BEVT0SEL + (ch * PPMU_BEVTSEL_OFFSET))
+#define PPMU_BEVTSEL(ch)	(PPMU_BEVT0SEL + (ch * PPMU_BEVTSEL_OFFSET))
 
 /* For Event Selection */
 #define RD_DATA_COUNT		0x5
-- 
1.7.9.5


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

* [PATCH RFC v2 7/7] PM / devfreq: Add devfreq driver for Exynos5420
       [not found] ` <1400779322-4410-1-git-send-email-a.kesavan-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
  2014-05-22 17:21   ` [PATCH RFC v2 3/7] ARM: dts: Add PPMU device tree support for Exynos5250 Abhilash Kesavan
@ 2014-05-22 17:22   ` Abhilash Kesavan
  2014-07-15 18:36     ` [PATCH v3 " Abhilash Kesavan
  1 sibling, 1 reply; 32+ messages in thread
From: Abhilash Kesavan @ 2014-05-22 17:22 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	kgene.kim-Sze3O3UU22JBDgjK7y7TUQ, t.figa-Sze3O3UU22JBDgjK7y7TUQ,
	myungjoo.ham-Sze3O3UU22JBDgjK7y7TUQ,
	rafael.j.wysocki-ral2JQCrhuEAvxtiuMwx3w,
	linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: kesavan.abhilash-Re5JQEeQqe8AvxtiuMwx3w

From: "Arjun.K.V" <arjun.kv-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>

Exynos5420 bus device devfreq driver monitors PPMU counters and
adjusts INT domain operating frequencies and voltages. Support
for 5420 has been added to the extant 5250 support.

Signed-off-by: Arjun.K.V <arjun.kv-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Signed-off-by: Andrew Bresticker <abrestic-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Signed-off-by: Doug Anderson <dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Signed-off-by: Abhilash Kesavan <a.kesavan-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
 drivers/devfreq/Kconfig              |   10 +-
 drivers/devfreq/exynos/exynos5_bus.c |  598 ++++++++++++++++++++++++++++------
 2 files changed, 508 insertions(+), 100 deletions(-)

diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index c023c57..4e84615 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -80,14 +80,14 @@ config ARM_EXYNOS4_BUS_DEVFREQ
 	  (CONFIG_EXYNOS_ASV).
 
 config ARM_EXYNOS5_BUS_DEVFREQ
-	bool "ARM Exynos5250 Bus DEVFREQ Driver"
-	depends on SOC_EXYNOS5250
+	bool "ARM Exynos5 Bus DEVFREQ Driver"
+	depends on (SOC_EXYNOS5250 || SOC_EXYNOS5420)
 	select ARCH_HAS_OPP
 	select DEVFREQ_GOV_SIMPLE_ONDEMAND
 	select PM_OPP
 	help
-	  This adds the DEVFREQ driver for Exynos5250 bus interface (vdd_int).
-	  It reads PPMU counters of memory controllers and adjusts the
-	  operating frequencies and voltages with OPP support.
+	  This adds the DEVFREQ driver for Exynos5250/5420 bus interface
+	  (vdd_int). It reads PPMU counters of memory controllers and adjusts
+	  the operating frequencies and voltages with OPP support.
 
 endif # PM_DEVFREQ
diff --git a/drivers/devfreq/exynos/exynos5_bus.c b/drivers/devfreq/exynos/exynos5_bus.c
index 1196653..7ce5b76 100644
--- a/drivers/devfreq/exynos/exynos5_bus.c
+++ b/drivers/devfreq/exynos/exynos5_bus.c
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2012-14 Samsung Electronics Co., Ltd.
  *		http://www.samsung.com/
  *
  * EXYNOS5 INT clock frequency scaling support using DEVFREQ framework
  * Based on work done by Jonghwan Choi <jhbird.choi-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
- * Support for only EXYNOS5250 is present.
+ * Support for EXYNOS5250 and Exynos5420 is present.
  *
  * 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
@@ -14,23 +14,28 @@
 
 #include <linux/module.h>
 #include <linux/devfreq.h>
-#include <linux/io.h>
 #include <linux/pm_opp.h>
 #include <linux/slab.h>
 #include <linux/suspend.h>
 #include <linux/clk.h>
-#include <linux/delay.h>
 #include <linux/platform_device.h>
-#include <linux/pm_qos.h>
 #include <linux/regulator/consumer.h>
 #include <linux/of_address.h>
-#include <linux/of_platform.h>
 
 #include "exynos_ppmu.h"
 
-#define MAX_SAFEVOLT			1100000 /* 1.10V */
-/* Assume that the bus is saturated if the utilization is 25% */
-#define INT_BUS_SATURATION_RATIO	25
+/* Assume that we need to bump up the level if the utilization is 10% */
+#define INT_BUS_SATURATION_RATIO	10
+#define INT_VOLT_STEP_UV		12500
+
+struct exynos5_busfreq_drv_data {
+	int busf_type;
+};
+
+enum exynos5_busf_type {
+	TYPE_BUSF_EXYNOS5250,
+	TYPE_BUSF_EXYNOS5420,
+};
 
 enum int_level_idx {
 	LV_0,
@@ -41,12 +46,31 @@ enum int_level_idx {
 	_LV_END
 };
 
-enum exynos_ppmu_list {
+enum exynos5250_ppmu_list {
+	PPMU_DDR_C,
+	PPMU_DDR_L,
+	PPMU_DDR_R,
 	PPMU_RIGHT,
-	PPMU_END,
+	PPMU_END_5250,
+};
+
+enum exynos5420_ppmu_list {
+	PPMU_DMC_0_1,
+	PPMU_DMC_1_0,
+	PPMU_DMC_1_1,
+	PPMU_END_5420,
+};
+
+enum int_bus_pll {
+	C_PLL = 0,
+	D_PLL,
+	M_PLL,
+	I_PLL,
 };
 
 struct busfreq_data_int {
+	enum exynos5_busf_type type;
+	struct list_head list;
 	struct device *dev;
 	struct devfreq *devfreq;
 	struct regulator *vdd_int;
@@ -56,7 +80,11 @@ struct busfreq_data_int {
 
 	struct notifier_block pm_notifier;
 	struct mutex lock;
-	struct pm_qos_request int_req;
+
+	struct clk *mout_mpll;
+	struct clk *mout_dpll;
+	struct clk *mout_cpll;
+	struct clk *mout_ipll;
 };
 
 struct int_bus_opp_table {
@@ -70,13 +98,29 @@ struct int_clk_table {
 	unsigned long freq;
 };
 
-struct int_clk {
+struct int_simple_clk {
 	const char *clk_name;
 	struct clk *clk;
 	struct int_clk_table *freq_table;
 };
 
-static struct int_bus_opp_table exynos5_int_opp_table[] = {
+struct int_clk_info {
+	unsigned int idx;
+	unsigned long target_freq;
+	enum int_bus_pll src_pll;
+};
+
+struct int_comp_clks {
+	struct list_head node;
+	const char *mux_clk_name;   /* The parent of the div clock */
+	struct clk *mux_clk;
+	const char *div_clk_name;
+	struct clk *div_clk;
+	struct int_clk_info *clk_info;
+};
+
+static struct int_bus_opp_table *exynos5_int_opp_table;
+static struct int_bus_opp_table exynos5250_int_opp_table[] = {
 	{LV_0, 266000, 1025000},
 	{LV_1, 200000, 1025000},
 	{LV_2, 160000, 1025000},
@@ -85,7 +129,16 @@ static struct int_bus_opp_table exynos5_int_opp_table[] = {
 	{0, 0, 0},
 };
 
-static struct int_clk_table aclk_166[] = {
+static struct int_bus_opp_table exynos5420_int_opp_table[] = {
+	{LV_0, 400000, 1100000},
+	{LV_1, 333000, 1100000},
+	{LV_2, 222000, 1100000},
+	{LV_3, 111000, 1100000},
+	{LV_4,  83000, 1100000},
+	{0, 0, 0},
+};
+
+static struct int_clk_table exynos5250_aclk_166[] = {
 	{LV_0, 167000},
 	{LV_1, 111000},
 	{LV_2,  84000},
@@ -93,7 +146,7 @@ static struct int_clk_table aclk_166[] = {
 	{LV_4,  42000},
 };
 
-static struct int_clk_table aclk_200[] = {
+static struct int_clk_table exynos5250_aclk_200[] = {
 	{LV_0, 200000},
 	{LV_1, 160000},
 	{LV_2, 160000},
@@ -101,7 +154,7 @@ static struct int_clk_table aclk_200[] = {
 	{LV_4, 100000},
 };
 
-static struct int_clk_table aclk_266[] = {
+static struct int_clk_table exynos5250_aclk_266[] = {
 	{LV_0, 267000},
 	{LV_1, 200000},
 	{LV_2, 160000},
@@ -109,7 +162,7 @@ static struct int_clk_table aclk_266[] = {
 	{LV_4, 100000},
 };
 
-static struct int_clk_table aclk_333[] = {
+static struct int_clk_table exynos5250_aclk_333[] = {
 	{LV_0, 333000},
 	{LV_1, 167000},
 	{LV_2, 111000},
@@ -117,7 +170,7 @@ static struct int_clk_table aclk_333[] = {
 	{LV_4,  42000},
 };
 
-static struct int_clk_table aclk_300_disp1[] = {
+static struct int_clk_table exynos5250_aclk_300_disp1[] = {
 	{LV_0, 267000},
 	{LV_1, 267000},
 	{LV_2, 267000},
@@ -125,7 +178,7 @@ static struct int_clk_table aclk_300_disp1[] = {
 	{LV_4, 200000},
 };
 
-static struct int_clk_table aclk_300_gscl[] = {
+static struct int_clk_table exynos5250_aclk_300_gscl[] = {
 	{LV_0, 267000},
 	{LV_1, 267000},
 	{LV_2, 267000},
@@ -133,27 +186,183 @@ static struct int_clk_table aclk_300_gscl[] = {
 	{LV_4, 100000},
 };
 
-#define EXYNOS5_INT_CLK(name, tbl) {		\
+#define EXYNOS5250_INT_CLK(name, tbl) {		\
 	.clk_name = name,			\
 	.freq_table = tbl,			\
 }
 
-static struct int_clk exynos5_int_clks[] = {
-	EXYNOS5_INT_CLK("aclk166_d", aclk_166),
-	EXYNOS5_INT_CLK("aclk200_d", aclk_200),
-	EXYNOS5_INT_CLK("aclk266_d", aclk_266),
-	EXYNOS5_INT_CLK("aclk333_d", aclk_333),
-	EXYNOS5_INT_CLK("aclk300_disp1_d", aclk_300_disp1),
-	EXYNOS5_INT_CLK("aclk300_gscl_d", aclk_300_gscl),
+static struct int_simple_clk exynos5250_int_clks[] = {
+	EXYNOS5250_INT_CLK("aclk166_d", exynos5250_aclk_166),
+	EXYNOS5250_INT_CLK("aclk200_d", exynos5250_aclk_200),
+	EXYNOS5250_INT_CLK("aclk266_d", exynos5250_aclk_266),
+	EXYNOS5250_INT_CLK("aclk333_d", exynos5250_aclk_333),
+	EXYNOS5250_INT_CLK("aclk300_disp1_d", exynos5250_aclk_300_disp1),
+	EXYNOS5250_INT_CLK("aclk300_gscl_d", exynos5250_aclk_300_gscl),
+};
+
+static struct int_clk_info exynos5420_aclk_200_fsys[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 200000, D_PLL},
+	{LV_1, 200000, D_PLL},
+	{LV_2, 150000, D_PLL},
+	{LV_3, 100000, D_PLL},
+	{LV_4, 100000, D_PLL},
+};
+
+static struct int_clk_info exynos5420_pclk_200_fsys[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 200000, D_PLL},
+	{LV_1, 150000, D_PLL},
+	{LV_2, 150000, D_PLL},
+	{LV_3, 100000, D_PLL},
+	{LV_4, 100000, D_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_100_noc[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 100000, D_PLL},
+	{LV_1,  86000, D_PLL},
+	{LV_2,  75000, D_PLL},
+	{LV_3,  75000, D_PLL},
+	{LV_4,  75000, D_PLL},
 };
 
-static int exynos5_int_set_rate(struct busfreq_data_int *data,
+static struct int_clk_info exynos5420_aclk_400_wcore[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 400000, M_PLL},
+	{LV_1, 333000, C_PLL},
+	{LV_2, 333000, C_PLL},
+	{LV_3, 333000, C_PLL},
+	{LV_4, 333000, C_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_200_fsys2[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 200000, D_PLL},
+	{LV_1, 200000, D_PLL},
+	{LV_2, 150000, D_PLL},
+	{LV_3, 100000, D_PLL},
+	{LV_4, 100000, D_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_400_mscl[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 400000, M_PLL},
+	{LV_1, 333000, C_PLL},
+	{LV_2, 222000, C_PLL},
+	{LV_3, 167000, C_PLL},
+	{LV_4,  84000, C_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_166[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 167000, C_PLL},
+	{LV_1, 134000, C_PLL},
+	{LV_2, 111000, C_PLL},
+	{LV_3,  84000, C_PLL},
+	{LV_4,  84000, C_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_266[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 267000, M_PLL},
+	{LV_1, 160000, M_PLL},
+	{LV_2, 134000, M_PLL},
+	{LV_3, 134000, M_PLL},
+	{LV_4,  86000, D_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_66[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0,  67000, C_PLL},
+	{LV_1,  67000, C_PLL},
+	{LV_2,  67000, C_PLL},
+	{LV_3,  67000, C_PLL},
+	{LV_4,  67000, C_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_300_disp1[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 200000, D_PLL},
+	{LV_1, 200000, D_PLL},
+	{LV_2, 200000, D_PLL},
+	{LV_3, 200000, D_PLL},
+	{LV_4, 120000, D_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_400_disp1[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 300000, D_PLL},
+	{LV_1, 300000, D_PLL},
+	{LV_2, 200000, D_PLL},
+	{LV_3, 200000, D_PLL},
+	{LV_4, 120000, D_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_300_jpeg[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 300000, D_PLL},
+	{LV_1, 300000, D_PLL},
+	{LV_2, 200000, D_PLL},
+	{LV_3, 150000, D_PLL},
+	{LV_4,  75000, D_PLL},
+};
+
+#define EXYNOS5420_INT_PM_CLK(NAME, CLK, PCLK, CLK_INFO)	\
+static struct int_comp_clks int_pm_clks_##NAME = {	\
+	.mux_clk_name = CLK,				\
+	.div_clk_name = PCLK,				\
+	.clk_info = CLK_INFO,				\
+}
+
+EXYNOS5420_INT_PM_CLK(aclk_200_fsys, "aclk200_fsys",
+			"aclk200_fsys_d", exynos5420_aclk_200_fsys);
+EXYNOS5420_INT_PM_CLK(pclk_200_fsys, "pclk200_fsys",
+			"pclk200_fsys_d", exynos5420_pclk_200_fsys);
+EXYNOS5420_INT_PM_CLK(aclk_100_noc, "aclk100_noc",
+			"aclk100_noc_d", exynos5420_aclk_100_noc);
+EXYNOS5420_INT_PM_CLK(aclk_400_wcore, "aclk400_wcore",
+			"aclk400_wcore_d", exynos5420_aclk_400_wcore);
+EXYNOS5420_INT_PM_CLK(aclk_200_fsys2, "aclk200_fsys2",
+			"aclk200_fsys2_d", exynos5420_aclk_200_fsys2);
+EXYNOS5420_INT_PM_CLK(aclk_400_mscl, "aclk400_mscl",
+			"aclk400_mscl_d", exynos5420_aclk_400_mscl);
+EXYNOS5420_INT_PM_CLK(aclk_166, "aclk166",
+			"aclk166_d", exynos5420_aclk_166);
+EXYNOS5420_INT_PM_CLK(aclk_266, "aclk266",
+			"aclk266_d", exynos5420_aclk_266);
+EXYNOS5420_INT_PM_CLK(aclk_66, "aclk66",
+			"aclk66_d", exynos5420_aclk_66);
+EXYNOS5420_INT_PM_CLK(aclk_300_disp1, "aclk300_disp1",
+			"aclk300_disp1_d", exynos5420_aclk_300_disp1);
+EXYNOS5420_INT_PM_CLK(aclk_300_jpeg, "aclk300_jpeg",
+			"aclk300_jpeg_d", exynos5420_aclk_300_jpeg);
+EXYNOS5420_INT_PM_CLK(aclk_400_disp1, "aclk400_disp1",
+			"aclk400_disp1_d", exynos5420_aclk_400_disp1);
+
+static struct int_comp_clks *exynos5420_int_pm_clks[] = {
+	&int_pm_clks_aclk_200_fsys,
+	&int_pm_clks_pclk_200_fsys,
+	&int_pm_clks_aclk_100_noc,
+	&int_pm_clks_aclk_400_wcore,
+	&int_pm_clks_aclk_200_fsys2,
+	&int_pm_clks_aclk_400_mscl,
+	&int_pm_clks_aclk_166,
+	&int_pm_clks_aclk_266,
+	&int_pm_clks_aclk_66,
+	&int_pm_clks_aclk_300_disp1,
+	&int_pm_clks_aclk_300_jpeg,
+	&int_pm_clks_aclk_400_disp1,
+	NULL,
+};
+
+static int exynos5250_int_set_rate(struct busfreq_data_int *data,
 				unsigned long rate)
 {
 	int index, i;
 
-	for (index = 0; index < ARRAY_SIZE(exynos5_int_opp_table); index++) {
-		if (exynos5_int_opp_table[index].clk == rate)
+	for (index = 0; index < ARRAY_SIZE(exynos5250_int_opp_table); index++) {
+		if (exynos5250_int_opp_table[index].clk == rate)
 			break;
 	}
 
@@ -161,8 +370,8 @@ static int exynos5_int_set_rate(struct busfreq_data_int *data,
 		return -EINVAL;
 
 	/* Change the system clock divider values */
-	for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
-		struct int_clk *clk_info = &exynos5_int_clks[i];
+	for (i = 0; i < ARRAY_SIZE(exynos5250_int_clks); i++) {
+		struct int_simple_clk *clk_info = &exynos5250_int_clks[i];
 		int ret;
 
 		ret = clk_set_rate(clk_info->clk,
@@ -177,10 +386,111 @@ static int exynos5_int_set_rate(struct busfreq_data_int *data,
 	return 0;
 }
 
+static struct clk *exynos5420_find_pll(struct busfreq_data_int *data,
+				    enum int_bus_pll target_pll)
+{
+	struct clk *target_src_clk = NULL;
+
+	switch (target_pll) {
+	case C_PLL:
+		target_src_clk = data->mout_cpll;
+		break;
+	case M_PLL:
+		target_src_clk = data->mout_mpll;
+		break;
+	case D_PLL:
+		target_src_clk = data->mout_dpll;
+		break;
+	case I_PLL:
+		target_src_clk = data->mout_ipll;
+		break;
+	default:
+		break;
+	}
+
+	return target_src_clk;
+}
+
+static int exynos5420_int_set_rate(struct busfreq_data_int *data,
+		unsigned long target_freq, unsigned long pre_freq)
+{
+	unsigned int i;
+	unsigned long tar_rate;
+	int target_idx = -EINVAL;
+	int pre_idx = -EINVAL;
+	struct int_comp_clks *int_clk;
+	struct clk *new_src_pll;
+	struct clk *old_src_pll;
+	unsigned long old_src_rate, new_src_rate;
+	unsigned long rate1, rate2, rate3, rate4;
+
+	/* Find the levels for target and previous frequencies */
+	for (i = 0; i < _LV_END; i++) {
+		if (exynos5420_int_opp_table[i].clk == target_freq)
+			target_idx = exynos5420_int_opp_table[i].idx;
+		if (exynos5420_int_opp_table[i].clk == pre_freq)
+			pre_idx = exynos5420_int_opp_table[i].idx;
+	}
+
+	list_for_each_entry(int_clk, &data->list, node) {
+		tar_rate = int_clk->clk_info[target_idx].target_freq * 1000;
+
+		old_src_pll = clk_get_parent(int_clk->mux_clk);
+		new_src_pll = exynos5420_find_pll(data,
+				int_clk->clk_info[target_idx].src_pll);
+
+		if (old_src_pll == new_src_pll) {
+			/* No need to change pll */
+			clk_set_rate(int_clk->div_clk, tar_rate);
+			pr_debug("%s: %s now %lu (%lu)\n", __func__,
+				 int_clk->mux_clk_name,
+				 clk_get_rate(int_clk->div_clk), tar_rate);
+			continue;
+		}
+
+		old_src_rate = clk_get_rate(old_src_pll);
+		new_src_rate = clk_get_rate(new_src_pll);
+		rate1 = clk_get_rate(int_clk->div_clk);
+
+		/*
+		 * If we're switching to a faster PLL we should bump up the
+		 * divider before switching.
+		 */
+		if (new_src_rate > old_src_rate) {
+			int new_div;
+			unsigned long tmp_rate;
+
+			new_div = DIV_ROUND_UP(new_src_rate, tar_rate);
+			tmp_rate = DIV_ROUND_UP(old_src_rate, new_div);
+			clk_set_rate(int_clk->div_clk, tmp_rate);
+		}
+		rate2 = clk_get_rate(int_clk->div_clk);
+
+		/* We can safely change the mux now */
+		clk_set_parent(int_clk->mux_clk, new_src_pll);
+		rate3 = clk_get_rate(int_clk->div_clk);
+
+		/*
+		 * Give us a proper divider; technically not needed in the case
+		 * where we pre-calculated the divider above (the new_src_rate >
+		 * old_src_rate case), but let's be formal about it.
+		 */
+		clk_set_rate(int_clk->div_clk, tar_rate);
+		rate4 = clk_get_rate(int_clk->div_clk);
+
+		pr_debug("%s: %s => PLL %d; %lu=>%lu=>%lu=>%lu (%lu)\n",
+			 __func__, int_clk->mux_clk_name,
+			 int_clk->clk_info[target_idx].src_pll,
+			 rate1, rate2, rate3, rate4, tar_rate);
+	}
+	return 0;
+}
+
 static int exynos5_int_setvolt(struct busfreq_data_int *data,
 				unsigned long volt)
 {
-	return regulator_set_voltage(data->vdd_int, volt, MAX_SAFEVOLT);
+	return regulator_set_voltage(data->vdd_int, volt,
+				volt + INT_VOLT_STEP_UV);
 }
 
 static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
@@ -218,18 +528,15 @@ static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
 	if (data->disabled)
 		goto out;
 
-	if (freq > exynos5_int_opp_table[0].clk)
-		pm_qos_update_request(&data->int_req, freq * 16 / 1000);
-	else
-		pm_qos_update_request(&data->int_req, -1);
-
 	if (old_freq < freq)
 		err = exynos5_int_setvolt(data, volt);
 	if (err)
 		goto out;
 
-	err = exynos5_int_set_rate(data, freq);
-
+	if (data->type == TYPE_BUSF_EXYNOS5250)
+		err = exynos5250_int_set_rate(data, freq);
+	else
+		err = exynos5420_int_set_rate(data, freq, old_freq);
 	if (err)
 		goto out;
 
@@ -267,16 +574,20 @@ static int exynos5_int_get_dev_status(struct device *dev,
 }
 
 static struct devfreq_dev_profile exynos5_devfreq_int_profile = {
-	.initial_freq		= 160000,
-	.polling_ms		= 100,
+	.polling_ms		= 10,
 	.target			= exynos5_busfreq_int_target,
 	.get_dev_status		= exynos5_int_get_dev_status,
 };
 
-static int exynos5250_init_int_tables(struct busfreq_data_int *data)
+static int exynos5_init_int_tables(struct busfreq_data_int *data)
 {
 	int i, err = 0;
 
+	if (data->type == TYPE_BUSF_EXYNOS5250)
+		exynos5_int_opp_table = exynos5250_int_opp_table;
+	else
+		exynos5_int_opp_table = exynos5420_int_opp_table;
+
 	for (i = LV_0; i < _LV_END; i++) {
 		err = dev_pm_opp_add(data->dev, exynos5_int_opp_table[i].clk,
 				exynos5_int_opp_table[i].volt);
@@ -297,6 +608,7 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
 	struct dev_pm_opp *opp;
 	unsigned long maxfreq = ULONG_MAX;
 	unsigned long freq;
+	unsigned long old_freq;
 	unsigned long volt;
 	int err = 0;
 
@@ -322,8 +634,14 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
 		if (err)
 			goto unlock;
 
-		err = exynos5_int_set_rate(data, freq);
+		old_freq = data->curr_freq;
 
+		if (data->type == TYPE_BUSF_EXYNOS5250)
+			err = exynos5250_int_set_rate(data, freq);
+		else if (data->type == TYPE_BUSF_EXYNOS5420)
+			err = exynos5420_int_set_rate(data, freq, old_freq);
+		else
+			err = -EINVAL;
 		if (err)
 			goto unlock;
 
@@ -345,16 +663,38 @@ unlock:
 	return NOTIFY_DONE;
 }
 
+static const struct of_device_id exynos5_busfreq_dt_match[];
+
+static inline int exynos5_busfreq_get_driver_data(struct platform_device *pdev)
+{
+#ifdef CONFIG_OF
+	struct exynos5_busfreq_drv_data *data;
+
+	if (pdev->dev.of_node) {
+		const struct of_device_id *match;
+
+		match = of_match_node(exynos5_busfreq_dt_match,
+					pdev->dev.of_node);
+		data = (struct exynos5_busfreq_drv_data *) match->data;
+		return data->busf_type;
+	}
+#endif
+	return platform_get_device_id(pdev)->driver_data;
+}
+
 static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 {
 	struct busfreq_data_int *data;
 	struct busfreq_ppmu_data *ppmu_data;
+	struct device_node *np = pdev->dev.of_node;
 	struct dev_pm_opp *opp;
 	struct device *dev = &pdev->dev;
-	struct device_node *np;
 	unsigned long initial_freq;
 	unsigned long initial_volt;
+	struct clk *mux_clk, *div_clk;
+	struct int_comp_clks *int_pm_clk;
 	int err = 0;
+	int nr_clk;
 	int i;
 
 	data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data_int),
@@ -364,22 +704,27 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	}
 
+	INIT_LIST_HEAD(&data->list);
+	data->type = exynos5_busfreq_get_driver_data(pdev);
+
 	ppmu_data = &data->ppmu_data;
-	ppmu_data->ppmu_end = PPMU_END;
+	if (data->type == TYPE_BUSF_EXYNOS5250) {
+		ppmu_data->ppmu_end = PPMU_END_5250;
+	} else if (data->type == TYPE_BUSF_EXYNOS5420) {
+		ppmu_data->ppmu_end = PPMU_END_5420;
+	} else {
+		dev_err(dev, "Cannot determine the device id %d\n", data->type);
+		return -EINVAL;
+	}
+
 	ppmu_data->ppmu = devm_kzalloc(dev,
-				       sizeof(struct exynos_ppmu) * PPMU_END,
-				       GFP_KERNEL);
+			sizeof(struct exynos_ppmu) * (ppmu_data->ppmu_end),
+			GFP_KERNEL);
 	if (!ppmu_data->ppmu) {
 		dev_err(dev, "Failed to allocate memory for exynos_ppmu\n");
 		return -ENOMEM;
 	}
 
-	np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu");
-	if (np == NULL) {
-		pr_err("Unable to find PPMU node\n");
-		return -ENOENT;
-	}
-
 	for (i = 0; i < ppmu_data->ppmu_end; i++) {
 		/* map PPMU memory region */
 		ppmu_data->ppmu[i].hw_base = of_iomap(np, i);
@@ -388,13 +733,17 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 			return -ENOMEM;
 		}
 	}
+
 	data->pm_notifier.notifier_call = exynos5_busfreq_int_pm_notifier_event;
 	data->dev = dev;
 	mutex_init(&data->lock);
 
-	err = exynos5250_init_int_tables(data);
-	if (err)
+	err = exynos5_init_int_tables(data);
+	if (err) {
+		dev_err(dev, "Cannot initialize busfreq table %d\n",
+			     data->type);
 		return err;
+	}
 
 	data->vdd_int = devm_regulator_get(dev, "vdd_int");
 	if (IS_ERR(data->vdd_int)) {
@@ -402,18 +751,70 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 		return PTR_ERR(data->vdd_int);
 	}
 
-	for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
-		struct int_clk *clk_info = &exynos5_int_clks[i];
+	if (data->type == TYPE_BUSF_EXYNOS5250) {
+		for (i = 0; i < ARRAY_SIZE(exynos5250_int_clks); i++) {
+			struct int_simple_clk *clk_info =
+				&exynos5250_int_clks[i];
+
+			clk_info->clk = devm_clk_get(dev, clk_info->clk_name);
+			if (IS_ERR(clk_info->clk)) {
+				dev_err(dev, "Failed to get clock %s\n",
+					clk_info->clk_name);
+				return PTR_ERR(clk_info->clk);
+			}
+		}
+	} else {
+		data->mout_ipll = devm_clk_get(dev, "mout_ipll");
+		if (IS_ERR(data->mout_ipll)) {
+			dev_err(dev, "Cannot get clock \"mout_ipll\"\n");
+			return PTR_ERR(data->mout_ipll);
+		}
 
-		clk_info->clk = devm_clk_get(dev, clk_info->clk_name);
-		if (IS_ERR(clk_info->clk)) {
-			dev_err(dev, "Failed to get clock %s\n",
-				clk_info->clk_name);
-			return PTR_ERR(clk_info->clk);
+		data->mout_mpll = devm_clk_get(dev, "mout_mpll");
+		if (IS_ERR(data->mout_mpll)) {
+			dev_err(dev, "Cannot get clock \"mout_mpll\"\n");
+			return PTR_ERR(data->mout_mpll);
+		}
+
+		data->mout_dpll = devm_clk_get(dev, "mout_dpll");
+		if (IS_ERR(data->mout_dpll)) {
+			dev_err(dev, "Cannot get clock \"mout_dpll\"\n");
+			return PTR_ERR(data->mout_dpll);
+		}
+
+		data->mout_cpll = devm_clk_get(dev, "mout_cpll");
+		if (IS_ERR(data->mout_cpll)) {
+			dev_err(dev, "Cannot get clock \"mout_cpll\"\n");
+			return PTR_ERR(data->mout_cpll);
+		}
+
+		for (nr_clk = 0; exynos5420_int_pm_clks[nr_clk] != NULL;
+								nr_clk++) {
+			int_pm_clk = exynos5420_int_pm_clks[nr_clk];
+			mux_clk = devm_clk_get(dev, int_pm_clk->mux_clk_name);
+			if (IS_ERR(mux_clk)) {
+				dev_err(dev, "Cannot get mux clock: %s\n",
+						int_pm_clk->mux_clk_name);
+				return PTR_ERR(mux_clk);
+			}
+			div_clk = devm_clk_get(dev, int_pm_clk->div_clk_name);
+			if (IS_ERR(div_clk)) {
+				dev_err(dev, "Cannot get div clock: %s\n",
+						int_pm_clk->div_clk_name);
+				return PTR_ERR(div_clk);
+			}
+			int_pm_clk->mux_clk = mux_clk;
+			int_pm_clk->div_clk = div_clk;
+			list_add_tail(&int_pm_clk->node, &data->list);
 		}
 	}
 
 	rcu_read_lock();
+	if (data->type == TYPE_BUSF_EXYNOS5250)
+		exynos5_devfreq_int_profile.initial_freq = 160000;
+	else
+		exynos5_devfreq_int_profile.initial_freq = 333000;
+
 	opp = dev_pm_opp_find_freq_floor(dev,
 			&exynos5_devfreq_int_profile.initial_freq);
 	if (IS_ERR(opp)) {
@@ -428,10 +829,15 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 	data->curr_freq = initial_freq;
 
 	err = exynos5_int_setvolt(data, initial_volt);
-	if (err)
+	if (err) {
+		dev_err(dev, "Failed to set initial voltage\n");
 		return err;
+	}
 
-	err = exynos5_int_set_rate(data, initial_freq);
+	if (data->type == TYPE_BUSF_EXYNOS5250)
+		err = exynos5250_int_set_rate(data, initial_freq);
+	else
+		err = exynos5420_int_set_rate(data, initial_freq, initial_freq);
 	if (err) {
 		dev_err(dev, "Failed to set initial frequency\n");
 		return err;
@@ -439,28 +845,31 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, data);
 
-	busfreq_mon_reset(ppmu_data);
-
-	data->devfreq = devm_devfreq_add_device(dev, &exynos5_devfreq_int_profile,
-					   "simple_ondemand", NULL);
+	data->devfreq = devm_devfreq_add_device(dev,
+		&exynos5_devfreq_int_profile, "simple_ondemand", NULL);
 	if (IS_ERR(data->devfreq))
 		return PTR_ERR(data->devfreq);
 
+	/*
+	 * Start PPMU (Performance Profiling Monitoring Unit) to check
+	 * utilization of each IP in the Exynos4 SoC.
+	 */
+	busfreq_mon_reset(ppmu_data);
+
+	/* Register opp_notifier for Exynos5 busfreq */
 	err = devm_devfreq_register_opp_notifier(dev, data->devfreq);
 	if (err < 0) {
 		dev_err(dev, "Failed to register opp notifier\n");
 		return err;
 	}
 
+	/* Register pm_notifier for Exynos5 busfreq */
 	err = register_pm_notifier(&data->pm_notifier);
 	if (err) {
 		dev_err(dev, "Failed to setup pm notifier\n");
 		return err;
 	}
 
-	/* TODO: Add a new QOS class for int/mif bus */
-	pm_qos_add_request(&data->int_req, PM_QOS_NETWORK_THROUGHPUT, -1);
-
 	return 0;
 }
 
@@ -468,7 +877,7 @@ static int exynos5_busfreq_int_remove(struct platform_device *pdev)
 {
 	struct busfreq_data_int *data = platform_get_drvdata(pdev);
 
-	pm_qos_remove_request(&data->int_req);
+	/* Unregister all of notifier chain */
 	unregister_pm_notifier(&data->pm_notifier);
 
 	return 0;
@@ -492,8 +901,24 @@ static const struct dev_pm_ops exynos5_busfreq_int_pm = {
 static SIMPLE_DEV_PM_OPS(exynos5_busfreq_int_pm_ops, NULL,
 			 exynos5_busfreq_int_resume);
 
-/* platform device pointer for exynos5 devfreq device. */
-static struct platform_device *exynos5_devfreq_pdev;
+#ifdef CONFIG_OF
+static struct exynos5_busfreq_drv_data exynos_busfreq_data_array[] = {
+	[TYPE_BUSF_EXYNOS5250] = { TYPE_BUSF_EXYNOS5250 },
+	[TYPE_BUSF_EXYNOS5420] = { TYPE_BUSF_EXYNOS5420 },
+};
+
+static const struct of_device_id exynos5_busfreq_dt_match[] = {
+	{
+		.compatible = "samsung,exynos5250-int-busfreq",
+		.data = &exynos_busfreq_data_array[TYPE_BUSF_EXYNOS5250],
+	}, {
+		.compatible = "samsung,exynos5420-int-busfreq",
+		.data = &exynos_busfreq_data_array[TYPE_BUSF_EXYNOS5420],
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, exynos5_busfreq_dt_match);
+#endif
 
 static struct platform_driver exynos5_busfreq_int_driver = {
 	.probe		= exynos5_busfreq_int_probe,
@@ -502,35 +927,18 @@ static struct platform_driver exynos5_busfreq_int_driver = {
 		.name		= "exynos5-bus-int",
 		.owner		= THIS_MODULE,
 		.pm		= &exynos5_busfreq_int_pm_ops,
+		.of_match_table	= of_match_ptr(exynos5_busfreq_dt_match),
 	},
 };
 
 static int __init exynos5_busfreq_int_init(void)
 {
-	int ret;
-
-	ret = platform_driver_register(&exynos5_busfreq_int_driver);
-	if (ret < 0)
-		goto out;
-
-	exynos5_devfreq_pdev =
-		platform_device_register_simple("exynos5-bus-int", -1, NULL, 0);
-	if (IS_ERR(exynos5_devfreq_pdev)) {
-		ret = PTR_ERR(exynos5_devfreq_pdev);
-		goto out1;
-	}
-
-	return 0;
-out1:
-	platform_driver_unregister(&exynos5_busfreq_int_driver);
-out:
-	return ret;
+	return platform_driver_register(&exynos5_busfreq_int_driver);
 }
 late_initcall(exynos5_busfreq_int_init);
 
 static void __exit exynos5_busfreq_int_exit(void)
 {
-	platform_device_unregister(exynos5_devfreq_pdev);
 	platform_driver_unregister(&exynos5_busfreq_int_driver);
 }
 module_exit(exynos5_busfreq_int_exit);
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH RFC v2 4/7] ARM: dts: Add PPMU device tree support for Exynos5420
  2014-05-22 17:21 ` [PATCH RFC v2 4/7] ARM: dts: Add PPMU device tree support for Exynos5420 Abhilash Kesavan
@ 2014-05-22 17:50   ` Sergei Shtylyov
  2014-06-16  7:01     ` Abhilash Kesavan
  2014-07-15 18:34   ` [PATCH v3 " Abhilash Kesavan
  1 sibling, 1 reply; 32+ messages in thread
From: Sergei Shtylyov @ 2014-05-22 17:50 UTC (permalink / raw)
  To: Abhilash Kesavan, linux-arm-kernel, kgene.kim, t.figa,
	myungjoo.ham, rafael.j.wysocki, linux-pm, devicetree
  Cc: kesavan.abhilash

On 05/22/2014 09:21 PM, Abhilash Kesavan wrote:

> PPMU is required by the exynos5420 devfreq driver. Add a device
> tree node for it.

> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
> ---
>   arch/arm/boot/dts/exynos5420.dtsi |    7 +++++++
>   1 file changed, 7 insertions(+)

> diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi
> index 8e7e35c..6ab7b03 100644
> --- a/arch/arm/boot/dts/exynos5420.dtsi
> +++ b/arch/arm/boot/dts/exynos5420.dtsi
> @@ -833,4 +833,11 @@
>   		samsung,pmu-syscon = <&pmu_system_controller>;
>   		#phy-cells = <1>;
>   	};
> +
> +	ppmu {

    Shouldn't it have the unit-address part, like "ppmu@10d00000"?

> +		compatible = "samsung,exynos5420-int-busfreq";
> +		reg = <0x10D00000 0x2000	/* PPMU_DMC_0_0 */
> +		       0x10D10000 0x2000	/* PPMU_DMC_0_1 */
> +		       0x10D60000 0x2000>;	/* PPMU_DMC_1_0 */
> +	};
>   };

WBR, Sergei


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

* Re: [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420
  2014-05-22 17:21 [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420 Abhilash Kesavan
                   ` (5 preceding siblings ...)
       [not found] ` <1400779322-4410-1-git-send-email-a.kesavan-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
@ 2014-05-23  5:08 ` Chanwoo Choi
  2014-06-16  6:59   ` Abhilash Kesavan
  2014-07-15 18:33 ` [PATCH v3 " Abhilash Kesavan
  7 siblings, 1 reply; 32+ messages in thread
From: Chanwoo Choi @ 2014-05-23  5:08 UTC (permalink / raw)
  To: Abhilash Kesavan
  Cc: linux-arm-kernel, kgene.kim, t.figa, myungjoo.ham,
	rafael.j.wysocki, linux-pm, devicetree, kesavan.abhilash

Hi,

On 05/23/2014 02:21 AM, Abhilash Kesavan wrote:
> This patchset adds devfreq support on Exynos5250 and Exynos5420.
> The patches add the missing INT clock support for exynos5250 and
> also add a new 5420 driver. We also have patches which add the
> PPMU node and fix a typo in the exynos5250 driver.
> 
> Changes since RFC v1:
> 	- Exynos5420 support has been added to the existent Exynos5250
> 	  driver itself.
> 	- Rebased on Chanwoo Choi's devfreq consolidation patchset.
> 	- Removed unused clocks from the clock list
> 	- Used the PPMU nodes with different compatible strings to
> 	  differentiate between exynos5250 and exynos5420.
> 
> The patches have been tested on Exynos5250 based Snow board and Exynos5420
> based Peach-Pit boards. They have been prepared on linux-next(20140521) with
> the devfreq for-next branch merged. Also applied is the "devfreq resource
> management" series from Chanwoo Choi[1].
> [1] https://lkml.org/lkml/2014/4/25/826

I sent v2 patchset as following after fixed minor issue:
[PATCHv2 0/5] devfreq: Support resource management functions and code clean
- https://lkml.org/lkml/2014/5/9/74

Best Regards,
Chanwoo Choi

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

* Re: [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420
  2014-05-23  5:08 ` [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420 Chanwoo Choi
@ 2014-06-16  6:59   ` Abhilash Kesavan
  0 siblings, 0 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-06-16  6:59 UTC (permalink / raw)
  To: Chanwoo Choi
  Cc: linux-arm-kernel, Kukjin Kim, Tomasz Figa, myungjoo.ham,
	rafael.j.wysocki@intel.com, linux-pm@vger.kernel.org, devicetree

Hi Chanwoo,

On Fri, May 23, 2014 at 10:38 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> Hi,
>
> On 05/23/2014 02:21 AM, Abhilash Kesavan wrote:
>> This patchset adds devfreq support on Exynos5250 and Exynos5420.
>> The patches add the missing INT clock support for exynos5250 and
>> also add a new 5420 driver. We also have patches which add the
>> PPMU node and fix a typo in the exynos5250 driver.
>>
>> Changes since RFC v1:
>>       - Exynos5420 support has been added to the existent Exynos5250
>>         driver itself.
>>       - Rebased on Chanwoo Choi's devfreq consolidation patchset.
>>       - Removed unused clocks from the clock list
>>       - Used the PPMU nodes with different compatible strings to
>>         differentiate between exynos5250 and exynos5420.
>>
>> The patches have been tested on Exynos5250 based Snow board and Exynos5420
>> based Peach-Pit boards. They have been prepared on linux-next(20140521) with
>> the devfreq for-next branch merged. Also applied is the "devfreq resource
>> management" series from Chanwoo Choi[1].
>> [1] https://lkml.org/lkml/2014/4/25/826
>
> I sent v2 patchset as following after fixed minor issue:
> [PATCHv2 0/5] devfreq: Support resource management functions and code clean
> - https://lkml.org/lkml/2014/5/9/74
Sorry for the delayed response, I was not in office for the last
couple of weeks. I have got the link wrong here, my patchset was in
fact based on your v2 patchset.

Myungjoo and Chanwoo, do you have any other comments on this patchset
or should I re-base this on the latest devfreq tree and re-send ?

Regards,
Abhilash
>
> Best Regards,
> Chanwoo Choi

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

* Re: [PATCH RFC v2 4/7] ARM: dts: Add PPMU device tree support for Exynos5420
  2014-05-22 17:50   ` Sergei Shtylyov
@ 2014-06-16  7:01     ` Abhilash Kesavan
  0 siblings, 0 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-06-16  7:01 UTC (permalink / raw)
  To: Sergei Shtylyov
  Cc: linux-arm-kernel, Kukjin Kim, Tomasz Figa, myungjoo.ham,
	rafael.j.wysocki@intel.com, linux-pm@vger.kernel.org, devicetree

Hi Sergei,

On Thu, May 22, 2014 at 11:20 PM, Sergei Shtylyov
<sergei.shtylyov@cogentembedded.com> wrote:
> On 05/22/2014 09:21 PM, Abhilash Kesavan wrote:
>
>> PPMU is required by the exynos5420 devfreq driver. Add a device
>> tree node for it.
>
>
>> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
>> ---
>>   arch/arm/boot/dts/exynos5420.dtsi |    7 +++++++
>>   1 file changed, 7 insertions(+)
>
>
>> diff --git a/arch/arm/boot/dts/exynos5420.dtsi
>> b/arch/arm/boot/dts/exynos5420.dtsi
>> index 8e7e35c..6ab7b03 100644
>> --- a/arch/arm/boot/dts/exynos5420.dtsi
>> +++ b/arch/arm/boot/dts/exynos5420.dtsi
>> @@ -833,4 +833,11 @@
>>                 samsung,pmu-syscon = <&pmu_system_controller>;
>>                 #phy-cells = <1>;
>>         };
>> +
>> +       ppmu {
>
>
>    Shouldn't it have the unit-address part, like "ppmu@10d00000"?
Will fix.

Regards,
Abhilash
>
>
>> +               compatible = "samsung,exynos5420-int-busfreq";
>> +               reg = <0x10D00000 0x2000        /* PPMU_DMC_0_0 */
>> +                      0x10D10000 0x2000        /* PPMU_DMC_0_1 */
>> +                      0x10D60000 0x2000>;      /* PPMU_DMC_1_0 */
>> +       };
>>   };
>
>
> WBR, Sergei
>

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

* [PATCH v3 0/7]  Devfreq support for Exynos5250/5420
  2014-05-22 17:21 [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420 Abhilash Kesavan
                   ` (6 preceding siblings ...)
  2014-05-23  5:08 ` [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420 Chanwoo Choi
@ 2014-07-15 18:33 ` Abhilash Kesavan
  7 siblings, 0 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-15 18:33 UTC (permalink / raw)
  To: myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, t.figa, kesavan.abhilash, devicetree

This patchset adds devfreq support on Exynos5250 and Exynos5420.
The patches add the missing INT clock support for exynos5250 and
also add a new 5420 driver. We also have patches which add the
PPMU node and fix a typo in the exynos5250 driver.

Changes since RFC v1:
	- Exynos5420 support has been added to the existent Exynos5250
	  driver itself.
	- Rebased on Chanwoo Choi's devfreq consolidation patchset.
	- Removed unused clocks from the clock list
	- Used the PPMU nodes with different compatible strings to
	  differentiate between exynos5250 and exynos5420.
Changes since RFC v2:
	- Re-based on the latest devfreq/samsung clock tree.
	- Fixed comment from Sergei Shtylyov.

The patches have been tested on Exynos5250 based Snow board and Exynos5420
based Peach-Pit boards. They have been prepared on linux-next(20140714) with
the samsung clk-next branch merged[1].

[1] //git.kernel.org/pub/scm/linux/kernel/git/tfiga/samsung-clk.git

Abhilash Kesavan (3):
  ARM: dts: Add PPMU device tree support for Exynos5250
  ARM: dts: Add PPMU device tree support for Exynos5420
  PM / devfreq: exynos: Fix typo in macro

Andrew Bresticker (2):
  clk: exynos5250: add aliases for clocks used by devfreq
  PM / devfreq: exynos5250: migrate to common-clock

Arjun.K.V (2):
  clk: exynos5420: Add aliases for clocks used by devfreq
  PM / devfreq: Add devfreq driver for Exynos5420

 .../bindings/arm/exynos/ppmu-exynos5.txt           |   26 +
 arch/arm/boot/dts/exynos5250.dtsi                  |    8 +
 arch/arm/boot/dts/exynos5420.dtsi                  |    7 +
 drivers/clk/samsung/clk-exynos5250.c               |   25 +-
 drivers/clk/samsung/clk-exynos5420.c               |   73 ++-
 drivers/devfreq/Kconfig                            |   10 +-
 drivers/devfreq/exynos/exynos5_bus.c               |  665 +++++++++++++++++---
 drivers/devfreq/exynos/exynos_ppmu.h               |    2 +-
 8 files changed, 702 insertions(+), 114 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt

-- 
1.7.9.5


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

* [PATCH v3 1/7] clk: exynos5250: add aliases for clocks used by devfreq
  2014-05-22 17:21 ` [PATCH RFC v2 1/7] clk: exynos5250: add aliases for clocks used by devfreq Abhilash Kesavan
@ 2014-07-15 18:33   ` Abhilash Kesavan
  2014-07-16 10:50     ` Tomasz Figa
  0 siblings, 1 reply; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-15 18:33 UTC (permalink / raw)
  To: myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, t.figa, kesavan.abhilash, devicetree

From: Andrew Bresticker <abrestic@chromium.org>

Devfreq does not support DT-based lookup of these peripheral clocks,
so add aliases for them.

Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
 drivers/clk/samsung/clk-exynos5250.c |   25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
index 184f642..48a2264 100644
--- a/drivers/clk/samsung/clk-exynos5250.c
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -226,6 +226,11 @@ PNAME(mout_aclk200_p)	= { "mout_mpll_user", "mout_bpll_user" };
 PNAME(mout_aclk400_p)	= { "mout_aclk400_g3d_mid", "mout_gpll" };
 PNAME(mout_aclk200_sub_p) = { "fin_pll", "div_aclk200" };
 PNAME(mout_aclk266_sub_p) = { "fin_pll", "div_aclk266" };
+PNAME(mout_aclk300_disp1_p) = { "mout_aclk300_disp1_mid",
+				"mout_aclk300_disp1_mid1" };
+PNAME(mout_aclk300_gscl_p) = { "mout_aclk300_gscl_mid",
+				"mout_aclk300_gscl_mid1" };
+PNAME(mout_aclk300_gscl_mid1_p) = { "mout_vpll", "mout_cpll" };
 PNAME(mout_aclk333_sub_p) = { "fin_pll", "div_aclk333" };
 PNAME(mout_aclk400_isp_sub_p) = { "fin_pll", "div_aclk400_isp" };
 PNAME(mout_hdmi_p)	= { "div_hdmi_pixel", "sclk_hdmiphy" };
@@ -304,9 +309,17 @@ static struct samsung_mux_clock exynos5250_mux_clks[] __initdata = {
 	 */
 	MUX(0, "mout_aclk166", mout_aclk166_p, SRC_TOP0, 8, 1),
 	MUX(0, "mout_aclk200", mout_aclk200_p, SRC_TOP0, 12, 1),
+	MUX(0, "mout_aclk300_disp1_mid", mout_aclk200_p, SRC_TOP0, 14, 1),
+	MUX(0, "mout_aclk300_disp1", mout_aclk300_disp1_p, SRC_TOP0, 15, 1),
 	MUX(0, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1),
 	MUX(0, "mout_aclk400_g3d_mid", mout_aclk200_p, SRC_TOP0, 20, 1),
+	MUX(0, "mout_aclk300_gscl_mid", mout_aclk200_p, SRC_TOP0, 24, 1),
+	MUX(0, "mout_aclk300_gscl", mout_aclk300_gscl_p, SRC_TOP0, 25, 2),
 
+	MUX(0, "mout_aclk300_disp1_mid1", mout_aclk300_gscl_mid1_p,
+					SRC_TOP1, 8, 1),
+	MUX(0, "mout_aclk300_gscl_mid1", mout_aclk300_gscl_mid1_p,
+					SRC_TOP1, 12, 1),
 	MUX(0, "mout_aclk400_isp", mout_aclk200_p, SRC_TOP1, 24, 1),
 	MUX(0, "mout_aclk400_g3d", mout_aclk400_p, SRC_TOP1, 28, 1),
 
@@ -387,13 +400,17 @@ static struct samsung_div_clock exynos5250_div_clks[] __initdata = {
 	 * CMU_TOP
 	 */
 	DIV(0, "div_aclk66", "div_aclk66_pre", DIV_TOP0, 0, 3),
-	DIV(0, "div_aclk166", "mout_aclk166", DIV_TOP0, 8, 3),
-	DIV(0, "div_aclk200", "mout_aclk200", DIV_TOP0, 12, 3),
-	DIV(0, "div_aclk266", "mout_mpll_user", DIV_TOP0, 16, 3),
-	DIV(0, "div_aclk333", "mout_aclk333", DIV_TOP0, 20, 3),
+	DIV_A(0, "div_aclk166", "mout_aclk166", DIV_TOP0, 8, 3, "aclk166_d"),
+	DIV_A(0, "div_aclk200", "mout_aclk200", DIV_TOP0, 12, 3, "aclk200_d"),
+	DIV_A(0, "div_aclk266", "mout_mpll_user", DIV_TOP0, 16, 3, "aclk266_d"),
+	DIV_A(0, "div_aclk333", "mout_aclk333", DIV_TOP0, 20, 3, "aclk333_d"),
 	DIV(0, "div_aclk400_g3d", "mout_aclk400_g3d", DIV_TOP0,
 							24, 3),
+	DIV_A(0, "div_aclk300_disp1", "mout_aclk300_disp1",
+					DIV_TOP0, 28, 3, "aclk300_disp1_d"),
 
+	DIV_A(0, "div_aclk300_gscl", "mout_aclk300_gscl",
+					DIV_TOP1, 12, 3, "aclk300_gscl_d"),
 	DIV(0, "div_aclk400_isp", "mout_aclk400_isp", DIV_TOP1, 20, 3),
 	DIV(0, "div_aclk66_pre", "mout_mpll_user", DIV_TOP1, 24, 3),
 
-- 
1.7.9.5


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

* [PATCH v3 2/7] clk: exynos5420: Add aliases for clocks used by devfreq
  2014-05-22 17:21 ` [PATCH RFC v2 2/7] clk: exynos5420: Add " Abhilash Kesavan
@ 2014-07-15 18:34   ` Abhilash Kesavan
  2014-07-16 10:51     ` Tomasz Figa
  0 siblings, 1 reply; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-15 18:34 UTC (permalink / raw)
  To: myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, t.figa, kesavan.abhilash, devicetree

From: "Arjun.K.V" <arjun.kv@samsung.com>

Devfreq does not support DT-based lookup of these peripheral clocks,
so add aliases for them.

Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
Signed-off-by: Arjun.K.V <arjun.kv@samsung.com>
Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
 drivers/clk/samsung/clk-exynos5420.c |   73 ++++++++++++++++++++--------------
 1 file changed, 44 insertions(+), 29 deletions(-)

diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
index a4e6cc7..10f2287 100644
--- a/drivers/clk/samsung/clk-exynos5420.c
+++ b/drivers/clk/samsung/clk-exynos5420.c
@@ -554,21 +554,25 @@ struct samsung_mux_clock exynos5420_mux_clks[] __initdata = {
 	MUX(0, "mout_aclk400_isp", mout_group1_p, SRC_TOP0, 0, 2),
 	MUX_A(0, "mout_aclk400_mscl", mout_group1_p,
 				SRC_TOP0, 4, 2, "aclk400_mscl"),
-	MUX(0, "mout_aclk400_wcore", mout_group1_p, SRC_TOP0, 16, 2),
-	MUX(0, "mout_aclk100_noc", mout_group1_p, SRC_TOP0, 20, 2),
-
+	MUX_A(0, "mout_aclk400_wcore", mout_group1_p,
+				SRC_TOP0, 16, 2, "aclk400_wcore"),
+	MUX_A(0, "mout_aclk100_noc", mout_group1_p,
+				SRC_TOP0, 20, 2, "aclk100_noc"),
 	MUX(0, "mout_aclk333_432_gscl", mout_group4_p, SRC_TOP1, 0, 2),
 	MUX(0, "mout_aclk333_432_isp", mout_group4_p,
 				SRC_TOP1, 4, 2),
 	MUX(0, "mout_aclk333_432_isp0", mout_group4_p, SRC_TOP1, 12, 2),
-	MUX(0, "mout_aclk266", mout_group1_p, SRC_TOP1, 20, 2),
+	MUX_A(0, "mout_aclk266", mout_group1_p, SRC_TOP1, 20, 2, "aclk266"),
 	MUX(0, "mout_aclk333", mout_group1_p, SRC_TOP1, 28, 2),
 
-	MUX(0, "mout_aclk400_disp1", mout_group1_p, SRC_TOP2, 4, 2),
+	MUX_A(0, "mout_aclk400_disp1", mout_group1_p,
+				SRC_TOP2, 4, 2, "aclk400_disp1"),
 	MUX(0, "mout_aclk333_g2d", mout_group1_p, SRC_TOP2, 8, 2),
 	MUX(0, "mout_aclk266_g2d", mout_group1_p, SRC_TOP2, 12, 2),
-	MUX(0, "mout_aclk300_jpeg", mout_group1_p, SRC_TOP2, 20, 2),
-	MUX(0, "mout_aclk300_disp1", mout_group1_p, SRC_TOP2, 24, 2),
+	MUX_A(0, "mout_aclk300_jpeg", mout_group1_p,
+				SRC_TOP2, 20, 2, "aclk300_jpeg"),
+	MUX_A(0, "mout_aclk300_disp1", mout_group1_p,
+				SRC_TOP2, 24, 2, "aclk300_disp1"),
 	MUX(0, "mout_aclk300_gscl", mout_group1_p, SRC_TOP2, 28, 2),
 
 	MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7, 20, 2),
@@ -577,8 +581,8 @@ struct samsung_mux_clock exynos5420_mux_clks[] __initdata = {
 };
 
 struct samsung_div_clock exynos5420_div_clks[] __initdata = {
-	DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore_bpll",
-			DIV_TOP0, 16, 3),
+	DIV_A(0, "dout_aclk400_wcore", "mout_aclk400_wcore_bpll",
+			DIV_TOP0, 16, 3, "aclk400_wcore_d"),
 };
 
 static struct samsung_mux_clock exynos5x_mux_clks[] __initdata = {
@@ -593,12 +597,15 @@ static struct samsung_mux_clock exynos5x_mux_clks[] __initdata = {
 	MUX(0, "mout_kfc", mout_kfc_p, SRC_KFC, 16, 1),
 
 	MUX(0, "mout_aclk200", mout_group1_p, SRC_TOP0, 8, 2),
-	MUX(0, "mout_aclk200_fsys2", mout_group1_p, SRC_TOP0, 12, 2),
-	MUX(0, "mout_pclk200_fsys", mout_group1_p, SRC_TOP0, 24, 2),
-	MUX(0, "mout_aclk200_fsys", mout_group1_p, SRC_TOP0, 28, 2),
+	MUX_A(0, "mout_aclk200_fsys2", mout_group1_p,
+		SRC_TOP0, 12, 2, "aclk200_fsys2"),
+	MUX_A(0, "mout_pclk200_fsys", mout_group1_p,
+		SRC_TOP0, 24, 2, "pclk200_fsys"),
+	MUX_A(0, "mout_aclk200_fsys", mout_group1_p,
+		SRC_TOP0, 28, 2, "aclk200_fsys"),
 
-	MUX(0, "mout_aclk66", mout_group1_p, SRC_TOP1, 8, 2),
-	MUX(0, "mout_aclk166", mout_group1_p, SRC_TOP1, 24, 2),
+	MUX_A(0, "mout_aclk66", mout_group1_p, SRC_TOP1, 8, 2, "aclk66"),
+	MUX_A(0, "mout_aclk166", mout_group1_p, SRC_TOP1, 24, 2, "aclk166"),
 
 	MUX(0, "mout_aclk_g3d", mout_group5_p, SRC_TOP2, 16, 1),
 
@@ -651,14 +658,14 @@ static struct samsung_mux_clock exynos5x_mux_clks[] __initdata = {
 	MUX(0, "mout_user_aclk300_gscl", mout_user_aclk300_gscl_p,
 			SRC_TOP5, 28, 1),
 
-	MUX(0, "mout_sclk_mpll", mout_mpll_p, SRC_TOP6, 0, 1),
+	MUX_A(0, "mout_sclk_mpll", mout_mpll_p, SRC_TOP6, 0, 1, "mout_mpll"),
 	MUX(CLK_MOUT_VPLL, "mout_sclk_vpll", mout_vpll_p, SRC_TOP6, 4, 1),
 	MUX(0, "mout_sclk_spll", mout_spll_p, SRC_TOP6, 8, 1),
-	MUX(0, "mout_sclk_ipll", mout_ipll_p, SRC_TOP6, 12, 1),
+	MUX_A(0, "mout_sclk_ipll", mout_ipll_p, SRC_TOP6, 12, 1, "mout_ipll"),
 	MUX(0, "mout_sclk_rpll", mout_rpll_p, SRC_TOP6, 16, 1),
 	MUX(0, "mout_sclk_epll", mout_epll_p, SRC_TOP6, 20, 1),
-	MUX(0, "mout_sclk_dpll", mout_dpll_p, SRC_TOP6, 24, 1),
-	MUX(0, "mout_sclk_cpll", mout_cpll_p, SRC_TOP6, 28, 1),
+	MUX_A(0, "mout_sclk_dpll", mout_dpll_p, SRC_TOP6, 24, 1, "mout_dpll"),
+	MUX_A(0, "mout_sclk_cpll", mout_cpll_p, SRC_TOP6, 28, 1, "mout_cpll"),
 
 	MUX(0, "mout_sw_aclk400_isp", mout_sw_aclk400_isp_p,
 			SRC_TOP10, 0, 1),
@@ -753,29 +760,36 @@ static struct samsung_div_clock exynos5x_div_clks[] __initdata = {
 	DIV(0, "sclk_kpll", "mout_kpll", DIV_KFC0, 24, 3),
 
 	DIV(0, "dout_aclk400_isp", "mout_aclk400_isp", DIV_TOP0, 0, 3),
-	DIV(0, "dout_aclk400_mscl", "mout_aclk400_mscl", DIV_TOP0, 4, 3),
+	DIV_A(0, "dout_aclk400_mscl", "mout_aclk400_mscl",
+			DIV_TOP0, 4, 3, "aclk400_mscl_d"),
 	DIV(0, "dout_aclk200", "mout_aclk200", DIV_TOP0, 8, 3),
-	DIV(0, "dout_aclk200_fsys2", "mout_aclk200_fsys2", DIV_TOP0, 12, 3),
-	DIV(0, "dout_aclk100_noc", "mout_aclk100_noc", DIV_TOP0, 20, 3),
-	DIV(0, "dout_pclk200_fsys", "mout_pclk200_fsys", DIV_TOP0, 24, 3),
-	DIV(0, "dout_aclk200_fsys", "mout_aclk200_fsys", DIV_TOP0, 28, 3),
+	DIV_A(0, "dout_aclk200_fsys2", "mout_aclk200_fsys2",
+			DIV_TOP0, 12, 3, "aclk200_fsys2_d"),
+	DIV_A(0, "dout_aclk100_noc", "mout_aclk100_noc",
+			DIV_TOP0, 20, 3, "aclk100_noc_d"),
+	DIV_A(0, "dout_pclk200_fsys", "mout_pclk200_fsys",
+			DIV_TOP0, 24, 3, "pclk200_fsys_d"),
+	DIV_A(0, "dout_aclk200_fsys", "mout_aclk200_fsys",
+			DIV_TOP0, 28, 3, "aclk200_fsys_d"),
 
 	DIV(0, "dout_aclk333_432_gscl", "mout_aclk333_432_gscl",
 			DIV_TOP1, 0, 3),
 	DIV(0, "dout_aclk333_432_isp", "mout_aclk333_432_isp",
 			DIV_TOP1, 4, 3),
-	DIV(0, "dout_aclk66", "mout_aclk66", DIV_TOP1, 8, 6),
+	DIV_A(0, "dout_aclk66", "mout_aclk66", DIV_TOP1, 8, 6, "aclk66_d"),
 	DIV(0, "dout_aclk333_432_isp0", "mout_aclk333_432_isp0",
 			DIV_TOP1, 16, 3),
-	DIV(0, "dout_aclk266", "mout_aclk266", DIV_TOP1, 20, 3),
-	DIV(0, "dout_aclk166", "mout_aclk166", DIV_TOP1, 24, 3),
+	DIV_A(0, "dout_aclk266", "mout_aclk266", DIV_TOP1, 20, 3, "aclk266_d"),
+	DIV_A(0, "dout_aclk166", "mout_aclk166", DIV_TOP1, 24, 3, "aclk166_d"),
 	DIV(0, "dout_aclk333", "mout_aclk333", DIV_TOP1, 28, 3),
 
 	DIV(0, "dout_aclk333_g2d", "mout_aclk333_g2d", DIV_TOP2, 8, 3),
 	DIV(0, "dout_aclk266_g2d", "mout_aclk266_g2d", DIV_TOP2, 12, 3),
 	DIV(0, "dout_aclk_g3d", "mout_aclk_g3d", DIV_TOP2, 16, 3),
-	DIV(0, "dout_aclk300_jpeg", "mout_aclk300_jpeg", DIV_TOP2, 20, 3),
-	DIV(0, "dout_aclk300_disp1", "mout_aclk300_disp1", DIV_TOP2, 24, 3),
+	DIV_A(0, "dout_aclk300_jpeg", "mout_aclk300_jpeg",
+			DIV_TOP2, 20, 3, "aclk300_jpeg_d"),
+	DIV_A(0, "dout_aclk300_disp1", "mout_aclk300_disp1",
+			DIV_TOP2, 24, 3, "aclk300_disp1_d"),
 	DIV(0, "dout_aclk300_gscl", "mout_aclk300_gscl", DIV_TOP2, 28, 3),
 
 	/* DISP1 Block */
@@ -784,7 +798,8 @@ static struct samsung_div_clock exynos5x_div_clks[] __initdata = {
 	DIV(0, "dout_dp1", "mout_dp1", DIV_DISP10, 24, 4),
 	DIV(CLK_DOUT_PIXEL, "dout_hdmi_pixel", "mout_pixel", DIV_DISP10, 28, 4),
 	DIV(0, "dout_disp1_blk", "aclk200_disp1", DIV2_RATIO0, 16, 2),
-	DIV(0, "dout_aclk400_disp1", "mout_aclk400_disp1", DIV_TOP2, 4, 3),
+	DIV_A(0, "dout_aclk400_disp1", "mout_aclk400_disp1",
+			DIV_TOP2, 4, 3, "aclk400_disp1_d"),
 
 	/* Audio Block */
 	DIV(0, "dout_maudio0", "mout_maudio0", DIV_MAU, 20, 4),
-- 
1.7.9.5


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

* [PATCH v3 3/7] ARM: dts: Add PPMU device tree support for Exynos5250
  2014-05-22 17:21   ` [PATCH RFC v2 3/7] ARM: dts: Add PPMU device tree support for Exynos5250 Abhilash Kesavan
@ 2014-07-15 18:34     ` Abhilash Kesavan
  2014-07-16 10:55       ` Tomasz Figa
  2014-07-16 10:56       ` Tomasz Figa
  0 siblings, 2 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-15 18:34 UTC (permalink / raw)
  To: myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, t.figa, kesavan.abhilash, devicetree

PPMU is required by the exynos5250 devfreq driver. Add a device
tree node for it.

Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
 .../bindings/arm/exynos/ppmu-exynos5.txt           |   26 ++++++++++++++++++++
 arch/arm/boot/dts/exynos5250.dtsi                  |    8 ++++++
 2 files changed, 34 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt

diff --git a/Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt b/Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt
new file mode 100644
index 0000000..a6a2eba
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt
@@ -0,0 +1,26 @@
+Exynos PPMU driver
+-------------------
+
+Performance events are primitive values used to get performance data. These
+events provide information about the behavior of the SoC that can be used
+when analyzing system performance. These events are made visible using the
+PPMU logic.
+Exynos PPMU driver is used by the exynos5 devfreq drivers to control the
+bus frequency/voltage.
+
+Required properties:
+- compatible: "samsung,exynos5250-int-busfreq", "samsung,exynos5420-int-busfreq"
+- reg:
+	* physical base address of the PPMUs (e.g DDR, Right Bus, Left bus etc)
+	and length of memory mapped region.
+
+Example:
+--------
+
+	ppmu@0x10C40000 {
+		compatible = "samsung,exynos5250-int-busfreq";
+		reg = <0x10C40000 0x2000
+		       0x10C50000 0x2000
+		       0x10CB0000 0x2000
+		       0x13660000 0x2000>;
+	};
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index 834fb5a..5284f00 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -778,4 +778,12 @@
 		clocks = <&clock CLK_SSS>;
 		clock-names = "secss";
 	};
+
+	ppmu@10C40000 {
+		compatible = "samsung,exynos5250-int-busfreq";
+		reg = <0x10C40000 0x2000	/* PPMU_DDR_C */
+		       0x10C50000 0x2000	/* PPMU_DDR_L */
+		       0x10CB0000 0x2000	/* PPMU_DDR_R */
+		       0x13660000 0x2000>;	/* PPMU_RIGHT */
+	};
 };
-- 
1.7.9.5


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

* [PATCH v3 4/7] ARM: dts: Add PPMU device tree support for Exynos5420
  2014-05-22 17:21 ` [PATCH RFC v2 4/7] ARM: dts: Add PPMU device tree support for Exynos5420 Abhilash Kesavan
  2014-05-22 17:50   ` Sergei Shtylyov
@ 2014-07-15 18:34   ` Abhilash Kesavan
  1 sibling, 0 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-15 18:34 UTC (permalink / raw)
  To: myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, t.figa, kesavan.abhilash, devicetree

PPMU is required by the exynos5420 devfreq driver. Add a device
tree node for it.

Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
 arch/arm/boot/dts/exynos5420.dtsi |    7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi
index 1595722..6d6c00f 100644
--- a/arch/arm/boot/dts/exynos5420.dtsi
+++ b/arch/arm/boot/dts/exynos5420.dtsi
@@ -882,4 +882,11 @@
 		samsung,sysreg-phandle = <&sysreg_system_controller>;
 		samsung,pmureg-phandle = <&pmu_system_controller>;
 	};
+
+	ppmu@0x10D00000 {
+		compatible = "samsung,exynos5420-int-busfreq";
+		reg = <0x10D00000 0x2000	/* PPMU_DMC_0_0 */
+		       0x10D10000 0x2000	/* PPMU_DMC_0_1 */
+		       0x10D60000 0x2000>;	/* PPMU_DMC_1_0 */
+	};
 };
-- 
1.7.9.5


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

* [PATCH v3 5/7] PM / devfreq: exynos5250: migrate to common-clock
  2014-05-22 17:22 ` [PATCH RFC v2 5/7] PM / devfreq: exynos5250: migrate to common-clock Abhilash Kesavan
@ 2014-07-15 18:35   ` Abhilash Kesavan
  2014-07-16 11:03     ` Tomasz Figa
  0 siblings, 1 reply; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-15 18:35 UTC (permalink / raw)
  To: myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, t.figa, kesavan.abhilash, devicetree

From: Andrew Bresticker <abrestic@chromium.org>

Use the common-clock framework to scale frequencies for the various
peripheral clocks on the Exynos5250.

Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
 drivers/devfreq/exynos/exynos5_bus.c |  131 ++++++++++++++++++++++++++++++----
 1 file changed, 119 insertions(+), 12 deletions(-)

diff --git a/drivers/devfreq/exynos/exynos5_bus.c b/drivers/devfreq/exynos/exynos5_bus.c
index 6cd0392..1196653 100644
--- a/drivers/devfreq/exynos/exynos5_bus.c
+++ b/drivers/devfreq/exynos/exynos5_bus.c
@@ -57,7 +57,6 @@ struct busfreq_data_int {
 	struct notifier_block pm_notifier;
 	struct mutex lock;
 	struct pm_qos_request int_req;
-	struct clk *int_clk;
 };
 
 struct int_bus_opp_table {
@@ -66,6 +65,17 @@ struct int_bus_opp_table {
 	unsigned long volt;
 };
 
+struct int_clk_table {
+	unsigned int idx;
+	unsigned long freq;
+};
+
+struct int_clk {
+	const char *clk_name;
+	struct clk *clk;
+	struct int_clk_table *freq_table;
+};
+
 static struct int_bus_opp_table exynos5_int_opp_table[] = {
 	{LV_0, 266000, 1025000},
 	{LV_1, 200000, 1025000},
@@ -75,6 +85,98 @@ static struct int_bus_opp_table exynos5_int_opp_table[] = {
 	{0, 0, 0},
 };
 
+static struct int_clk_table aclk_166[] = {
+	{LV_0, 167000},
+	{LV_1, 111000},
+	{LV_2,  84000},
+	{LV_3,  84000},
+	{LV_4,  42000},
+};
+
+static struct int_clk_table aclk_200[] = {
+	{LV_0, 200000},
+	{LV_1, 160000},
+	{LV_2, 160000},
+	{LV_3, 134000},
+	{LV_4, 100000},
+};
+
+static struct int_clk_table aclk_266[] = {
+	{LV_0, 267000},
+	{LV_1, 200000},
+	{LV_2, 160000},
+	{LV_3, 134000},
+	{LV_4, 100000},
+};
+
+static struct int_clk_table aclk_333[] = {
+	{LV_0, 333000},
+	{LV_1, 167000},
+	{LV_2, 111000},
+	{LV_3, 111000},
+	{LV_4,  42000},
+};
+
+static struct int_clk_table aclk_300_disp1[] = {
+	{LV_0, 267000},
+	{LV_1, 267000},
+	{LV_2, 267000},
+	{LV_3, 267000},
+	{LV_4, 200000},
+};
+
+static struct int_clk_table aclk_300_gscl[] = {
+	{LV_0, 267000},
+	{LV_1, 267000},
+	{LV_2, 267000},
+	{LV_3, 200000},
+	{LV_4, 100000},
+};
+
+#define EXYNOS5_INT_CLK(name, tbl) {		\
+	.clk_name = name,			\
+	.freq_table = tbl,			\
+}
+
+static struct int_clk exynos5_int_clks[] = {
+	EXYNOS5_INT_CLK("aclk166_d", aclk_166),
+	EXYNOS5_INT_CLK("aclk200_d", aclk_200),
+	EXYNOS5_INT_CLK("aclk266_d", aclk_266),
+	EXYNOS5_INT_CLK("aclk333_d", aclk_333),
+	EXYNOS5_INT_CLK("aclk300_disp1_d", aclk_300_disp1),
+	EXYNOS5_INT_CLK("aclk300_gscl_d", aclk_300_gscl),
+};
+
+static int exynos5_int_set_rate(struct busfreq_data_int *data,
+				unsigned long rate)
+{
+	int index, i;
+
+	for (index = 0; index < ARRAY_SIZE(exynos5_int_opp_table); index++) {
+		if (exynos5_int_opp_table[index].clk == rate)
+			break;
+	}
+
+	if (index >= _LV_END)
+		return -EINVAL;
+
+	/* Change the system clock divider values */
+	for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
+		struct int_clk *clk_info = &exynos5_int_clks[i];
+		int ret;
+
+		ret = clk_set_rate(clk_info->clk,
+				clk_info->freq_table[index].freq * 1000);
+		if (ret) {
+			dev_err(data->dev, "Failed to set %s rate: %d\n",
+				clk_info->clk_name, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 static int exynos5_int_setvolt(struct busfreq_data_int *data,
 				unsigned long volt)
 {
@@ -126,7 +228,7 @@ static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
 	if (err)
 		goto out;
 
-	err = clk_set_rate(data->int_clk, freq * 1000);
+	err = exynos5_int_set_rate(data, freq);
 
 	if (err)
 		goto out;
@@ -220,7 +322,7 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
 		if (err)
 			goto unlock;
 
-		err = clk_set_rate(data->int_clk, freq * 1000);
+		err = exynos5_int_set_rate(data, freq);
 
 		if (err)
 			goto unlock;
@@ -300,10 +402,15 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 		return PTR_ERR(data->vdd_int);
 	}
 
-	data->int_clk = devm_clk_get(dev, "int_clk");
-	if (IS_ERR(data->int_clk)) {
-		dev_err(dev, "Cannot get clock \"int_clk\"\n");
-		return PTR_ERR(data->int_clk);
+	for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
+		struct int_clk *clk_info = &exynos5_int_clks[i];
+
+		clk_info->clk = devm_clk_get(dev, clk_info->clk_name);
+		if (IS_ERR(clk_info->clk)) {
+			dev_err(dev, "Failed to get clock %s\n",
+				clk_info->clk_name);
+			return PTR_ERR(clk_info->clk);
+		}
 	}
 
 	rcu_read_lock();
@@ -320,16 +427,16 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 	rcu_read_unlock();
 	data->curr_freq = initial_freq;
 
-	err = clk_set_rate(data->int_clk, initial_freq * 1000);
+	err = exynos5_int_setvolt(data, initial_volt);
+	if (err)
+		return err;
+
+	err = exynos5_int_set_rate(data, initial_freq);
 	if (err) {
 		dev_err(dev, "Failed to set initial frequency\n");
 		return err;
 	}
 
-	err = exynos5_int_setvolt(data, initial_volt);
-	if (err)
-		return err;
-
 	platform_set_drvdata(pdev, data);
 
 	busfreq_mon_reset(ppmu_data);
-- 
1.7.9.5


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

* [PATCH v3 6/7] PM / devfreq: exynos: Fix typo in macro
  2014-05-22 17:22 ` [PATCH RFC v2 6/7] PM / devfreq: exynos: Fix typo in macro Abhilash Kesavan
@ 2014-07-15 18:35   ` Abhilash Kesavan
  0 siblings, 0 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-15 18:35 UTC (permalink / raw)
  To: myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, t.figa, kesavan.abhilash, devicetree

Fix PPMU_BEVTSEL macro being used in the PPMU driver.

Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
 drivers/devfreq/exynos/exynos_ppmu.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/devfreq/exynos/exynos_ppmu.h b/drivers/devfreq/exynos/exynos_ppmu.h
index 71f17ba..d5c14eb 100644
--- a/drivers/devfreq/exynos/exynos_ppmu.h
+++ b/drivers/devfreq/exynos/exynos_ppmu.h
@@ -37,7 +37,7 @@
 
 #define PPMU_BEVT0SEL		0x1000
 #define PPMU_BEVTSEL_OFFSET	0x100
-#define PPMU_BEVTSEL(x)		(PPMU_BEVT0SEL + (ch * PPMU_BEVTSEL_OFFSET))
+#define PPMU_BEVTSEL(ch)	(PPMU_BEVT0SEL + (ch * PPMU_BEVTSEL_OFFSET))
 
 /* For Event Selection */
 #define RD_DATA_COUNT		0x5
-- 
1.7.9.5


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

* [PATCH v3 7/7] PM / devfreq: Add devfreq driver for Exynos5420
  2014-05-22 17:22   ` [PATCH RFC v2 7/7] PM / devfreq: Add devfreq driver for Exynos5420 Abhilash Kesavan
@ 2014-07-15 18:36     ` Abhilash Kesavan
  2014-07-16 11:55       ` Tomasz Figa
  0 siblings, 1 reply; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-15 18:36 UTC (permalink / raw)
  To: myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, t.figa, kesavan.abhilash, devicetree

From: "Arjun.K.V" <arjun.kv@samsung.com>

Exynos5420 bus device devfreq driver monitors PPMU counters and
adjusts INT domain operating frequencies and voltages. Support
for 5420 has been added to the extant 5250 support.

Signed-off-by: Arjun.K.V <arjun.kv@samsung.com>
Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Doug Anderson <dianders@chromium.org>
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
 drivers/devfreq/Kconfig              |   10 +-
 drivers/devfreq/exynos/exynos5_bus.c |  598 ++++++++++++++++++++++++++++------
 2 files changed, 508 insertions(+), 100 deletions(-)

diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 49e74c1..705f951 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -79,14 +79,14 @@ config ARM_EXYNOS4_BUS_DEVFREQ
 	  This does not yet operate with optimal voltages.
 
 config ARM_EXYNOS5_BUS_DEVFREQ
-	bool "ARM Exynos5250 Bus DEVFREQ Driver"
-	depends on SOC_EXYNOS5250
+	bool "ARM Exynos5 Bus DEVFREQ Driver"
+	depends on (SOC_EXYNOS5250 || SOC_EXYNOS5420)
 	select ARCH_HAS_OPP
 	select DEVFREQ_GOV_SIMPLE_ONDEMAND
 	select PM_OPP
 	help
-	  This adds the DEVFREQ driver for Exynos5250 bus interface (vdd_int).
-	  It reads PPMU counters of memory controllers and adjusts the
-	  operating frequencies and voltages with OPP support.
+	  This adds the DEVFREQ driver for Exynos5250/5420 bus interface
+	  (vdd_int). It reads PPMU counters of memory controllers and adjusts
+	  the operating frequencies and voltages with OPP support.
 
 endif # PM_DEVFREQ
diff --git a/drivers/devfreq/exynos/exynos5_bus.c b/drivers/devfreq/exynos/exynos5_bus.c
index 1196653..7ce5b76 100644
--- a/drivers/devfreq/exynos/exynos5_bus.c
+++ b/drivers/devfreq/exynos/exynos5_bus.c
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2012-14 Samsung Electronics Co., Ltd.
  *		http://www.samsung.com/
  *
  * EXYNOS5 INT clock frequency scaling support using DEVFREQ framework
  * Based on work done by Jonghwan Choi <jhbird.choi@samsung.com>
- * Support for only EXYNOS5250 is present.
+ * Support for EXYNOS5250 and Exynos5420 is present.
  *
  * 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
@@ -14,23 +14,28 @@
 
 #include <linux/module.h>
 #include <linux/devfreq.h>
-#include <linux/io.h>
 #include <linux/pm_opp.h>
 #include <linux/slab.h>
 #include <linux/suspend.h>
 #include <linux/clk.h>
-#include <linux/delay.h>
 #include <linux/platform_device.h>
-#include <linux/pm_qos.h>
 #include <linux/regulator/consumer.h>
 #include <linux/of_address.h>
-#include <linux/of_platform.h>
 
 #include "exynos_ppmu.h"
 
-#define MAX_SAFEVOLT			1100000 /* 1.10V */
-/* Assume that the bus is saturated if the utilization is 25% */
-#define INT_BUS_SATURATION_RATIO	25
+/* Assume that we need to bump up the level if the utilization is 10% */
+#define INT_BUS_SATURATION_RATIO	10
+#define INT_VOLT_STEP_UV		12500
+
+struct exynos5_busfreq_drv_data {
+	int busf_type;
+};
+
+enum exynos5_busf_type {
+	TYPE_BUSF_EXYNOS5250,
+	TYPE_BUSF_EXYNOS5420,
+};
 
 enum int_level_idx {
 	LV_0,
@@ -41,12 +46,31 @@ enum int_level_idx {
 	_LV_END
 };
 
-enum exynos_ppmu_list {
+enum exynos5250_ppmu_list {
+	PPMU_DDR_C,
+	PPMU_DDR_L,
+	PPMU_DDR_R,
 	PPMU_RIGHT,
-	PPMU_END,
+	PPMU_END_5250,
+};
+
+enum exynos5420_ppmu_list {
+	PPMU_DMC_0_1,
+	PPMU_DMC_1_0,
+	PPMU_DMC_1_1,
+	PPMU_END_5420,
+};
+
+enum int_bus_pll {
+	C_PLL = 0,
+	D_PLL,
+	M_PLL,
+	I_PLL,
 };
 
 struct busfreq_data_int {
+	enum exynos5_busf_type type;
+	struct list_head list;
 	struct device *dev;
 	struct devfreq *devfreq;
 	struct regulator *vdd_int;
@@ -56,7 +80,11 @@ struct busfreq_data_int {
 
 	struct notifier_block pm_notifier;
 	struct mutex lock;
-	struct pm_qos_request int_req;
+
+	struct clk *mout_mpll;
+	struct clk *mout_dpll;
+	struct clk *mout_cpll;
+	struct clk *mout_ipll;
 };
 
 struct int_bus_opp_table {
@@ -70,13 +98,29 @@ struct int_clk_table {
 	unsigned long freq;
 };
 
-struct int_clk {
+struct int_simple_clk {
 	const char *clk_name;
 	struct clk *clk;
 	struct int_clk_table *freq_table;
 };
 
-static struct int_bus_opp_table exynos5_int_opp_table[] = {
+struct int_clk_info {
+	unsigned int idx;
+	unsigned long target_freq;
+	enum int_bus_pll src_pll;
+};
+
+struct int_comp_clks {
+	struct list_head node;
+	const char *mux_clk_name;   /* The parent of the div clock */
+	struct clk *mux_clk;
+	const char *div_clk_name;
+	struct clk *div_clk;
+	struct int_clk_info *clk_info;
+};
+
+static struct int_bus_opp_table *exynos5_int_opp_table;
+static struct int_bus_opp_table exynos5250_int_opp_table[] = {
 	{LV_0, 266000, 1025000},
 	{LV_1, 200000, 1025000},
 	{LV_2, 160000, 1025000},
@@ -85,7 +129,16 @@ static struct int_bus_opp_table exynos5_int_opp_table[] = {
 	{0, 0, 0},
 };
 
-static struct int_clk_table aclk_166[] = {
+static struct int_bus_opp_table exynos5420_int_opp_table[] = {
+	{LV_0, 400000, 1100000},
+	{LV_1, 333000, 1100000},
+	{LV_2, 222000, 1100000},
+	{LV_3, 111000, 1100000},
+	{LV_4,  83000, 1100000},
+	{0, 0, 0},
+};
+
+static struct int_clk_table exynos5250_aclk_166[] = {
 	{LV_0, 167000},
 	{LV_1, 111000},
 	{LV_2,  84000},
@@ -93,7 +146,7 @@ static struct int_clk_table aclk_166[] = {
 	{LV_4,  42000},
 };
 
-static struct int_clk_table aclk_200[] = {
+static struct int_clk_table exynos5250_aclk_200[] = {
 	{LV_0, 200000},
 	{LV_1, 160000},
 	{LV_2, 160000},
@@ -101,7 +154,7 @@ static struct int_clk_table aclk_200[] = {
 	{LV_4, 100000},
 };
 
-static struct int_clk_table aclk_266[] = {
+static struct int_clk_table exynos5250_aclk_266[] = {
 	{LV_0, 267000},
 	{LV_1, 200000},
 	{LV_2, 160000},
@@ -109,7 +162,7 @@ static struct int_clk_table aclk_266[] = {
 	{LV_4, 100000},
 };
 
-static struct int_clk_table aclk_333[] = {
+static struct int_clk_table exynos5250_aclk_333[] = {
 	{LV_0, 333000},
 	{LV_1, 167000},
 	{LV_2, 111000},
@@ -117,7 +170,7 @@ static struct int_clk_table aclk_333[] = {
 	{LV_4,  42000},
 };
 
-static struct int_clk_table aclk_300_disp1[] = {
+static struct int_clk_table exynos5250_aclk_300_disp1[] = {
 	{LV_0, 267000},
 	{LV_1, 267000},
 	{LV_2, 267000},
@@ -125,7 +178,7 @@ static struct int_clk_table aclk_300_disp1[] = {
 	{LV_4, 200000},
 };
 
-static struct int_clk_table aclk_300_gscl[] = {
+static struct int_clk_table exynos5250_aclk_300_gscl[] = {
 	{LV_0, 267000},
 	{LV_1, 267000},
 	{LV_2, 267000},
@@ -133,27 +186,183 @@ static struct int_clk_table aclk_300_gscl[] = {
 	{LV_4, 100000},
 };
 
-#define EXYNOS5_INT_CLK(name, tbl) {		\
+#define EXYNOS5250_INT_CLK(name, tbl) {		\
 	.clk_name = name,			\
 	.freq_table = tbl,			\
 }
 
-static struct int_clk exynos5_int_clks[] = {
-	EXYNOS5_INT_CLK("aclk166_d", aclk_166),
-	EXYNOS5_INT_CLK("aclk200_d", aclk_200),
-	EXYNOS5_INT_CLK("aclk266_d", aclk_266),
-	EXYNOS5_INT_CLK("aclk333_d", aclk_333),
-	EXYNOS5_INT_CLK("aclk300_disp1_d", aclk_300_disp1),
-	EXYNOS5_INT_CLK("aclk300_gscl_d", aclk_300_gscl),
+static struct int_simple_clk exynos5250_int_clks[] = {
+	EXYNOS5250_INT_CLK("aclk166_d", exynos5250_aclk_166),
+	EXYNOS5250_INT_CLK("aclk200_d", exynos5250_aclk_200),
+	EXYNOS5250_INT_CLK("aclk266_d", exynos5250_aclk_266),
+	EXYNOS5250_INT_CLK("aclk333_d", exynos5250_aclk_333),
+	EXYNOS5250_INT_CLK("aclk300_disp1_d", exynos5250_aclk_300_disp1),
+	EXYNOS5250_INT_CLK("aclk300_gscl_d", exynos5250_aclk_300_gscl),
+};
+
+static struct int_clk_info exynos5420_aclk_200_fsys[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 200000, D_PLL},
+	{LV_1, 200000, D_PLL},
+	{LV_2, 150000, D_PLL},
+	{LV_3, 100000, D_PLL},
+	{LV_4, 100000, D_PLL},
+};
+
+static struct int_clk_info exynos5420_pclk_200_fsys[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 200000, D_PLL},
+	{LV_1, 150000, D_PLL},
+	{LV_2, 150000, D_PLL},
+	{LV_3, 100000, D_PLL},
+	{LV_4, 100000, D_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_100_noc[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 100000, D_PLL},
+	{LV_1,  86000, D_PLL},
+	{LV_2,  75000, D_PLL},
+	{LV_3,  75000, D_PLL},
+	{LV_4,  75000, D_PLL},
 };
 
-static int exynos5_int_set_rate(struct busfreq_data_int *data,
+static struct int_clk_info exynos5420_aclk_400_wcore[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 400000, M_PLL},
+	{LV_1, 333000, C_PLL},
+	{LV_2, 333000, C_PLL},
+	{LV_3, 333000, C_PLL},
+	{LV_4, 333000, C_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_200_fsys2[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 200000, D_PLL},
+	{LV_1, 200000, D_PLL},
+	{LV_2, 150000, D_PLL},
+	{LV_3, 100000, D_PLL},
+	{LV_4, 100000, D_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_400_mscl[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 400000, M_PLL},
+	{LV_1, 333000, C_PLL},
+	{LV_2, 222000, C_PLL},
+	{LV_3, 167000, C_PLL},
+	{LV_4,  84000, C_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_166[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 167000, C_PLL},
+	{LV_1, 134000, C_PLL},
+	{LV_2, 111000, C_PLL},
+	{LV_3,  84000, C_PLL},
+	{LV_4,  84000, C_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_266[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 267000, M_PLL},
+	{LV_1, 160000, M_PLL},
+	{LV_2, 134000, M_PLL},
+	{LV_3, 134000, M_PLL},
+	{LV_4,  86000, D_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_66[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0,  67000, C_PLL},
+	{LV_1,  67000, C_PLL},
+	{LV_2,  67000, C_PLL},
+	{LV_3,  67000, C_PLL},
+	{LV_4,  67000, C_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_300_disp1[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 200000, D_PLL},
+	{LV_1, 200000, D_PLL},
+	{LV_2, 200000, D_PLL},
+	{LV_3, 200000, D_PLL},
+	{LV_4, 120000, D_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_400_disp1[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 300000, D_PLL},
+	{LV_1, 300000, D_PLL},
+	{LV_2, 200000, D_PLL},
+	{LV_3, 200000, D_PLL},
+	{LV_4, 120000, D_PLL},
+};
+
+static struct int_clk_info exynos5420_aclk_300_jpeg[] = {
+	/* Level, Freq, Parent_Pll */
+	{LV_0, 300000, D_PLL},
+	{LV_1, 300000, D_PLL},
+	{LV_2, 200000, D_PLL},
+	{LV_3, 150000, D_PLL},
+	{LV_4,  75000, D_PLL},
+};
+
+#define EXYNOS5420_INT_PM_CLK(NAME, CLK, PCLK, CLK_INFO)	\
+static struct int_comp_clks int_pm_clks_##NAME = {	\
+	.mux_clk_name = CLK,				\
+	.div_clk_name = PCLK,				\
+	.clk_info = CLK_INFO,				\
+}
+
+EXYNOS5420_INT_PM_CLK(aclk_200_fsys, "aclk200_fsys",
+			"aclk200_fsys_d", exynos5420_aclk_200_fsys);
+EXYNOS5420_INT_PM_CLK(pclk_200_fsys, "pclk200_fsys",
+			"pclk200_fsys_d", exynos5420_pclk_200_fsys);
+EXYNOS5420_INT_PM_CLK(aclk_100_noc, "aclk100_noc",
+			"aclk100_noc_d", exynos5420_aclk_100_noc);
+EXYNOS5420_INT_PM_CLK(aclk_400_wcore, "aclk400_wcore",
+			"aclk400_wcore_d", exynos5420_aclk_400_wcore);
+EXYNOS5420_INT_PM_CLK(aclk_200_fsys2, "aclk200_fsys2",
+			"aclk200_fsys2_d", exynos5420_aclk_200_fsys2);
+EXYNOS5420_INT_PM_CLK(aclk_400_mscl, "aclk400_mscl",
+			"aclk400_mscl_d", exynos5420_aclk_400_mscl);
+EXYNOS5420_INT_PM_CLK(aclk_166, "aclk166",
+			"aclk166_d", exynos5420_aclk_166);
+EXYNOS5420_INT_PM_CLK(aclk_266, "aclk266",
+			"aclk266_d", exynos5420_aclk_266);
+EXYNOS5420_INT_PM_CLK(aclk_66, "aclk66",
+			"aclk66_d", exynos5420_aclk_66);
+EXYNOS5420_INT_PM_CLK(aclk_300_disp1, "aclk300_disp1",
+			"aclk300_disp1_d", exynos5420_aclk_300_disp1);
+EXYNOS5420_INT_PM_CLK(aclk_300_jpeg, "aclk300_jpeg",
+			"aclk300_jpeg_d", exynos5420_aclk_300_jpeg);
+EXYNOS5420_INT_PM_CLK(aclk_400_disp1, "aclk400_disp1",
+			"aclk400_disp1_d", exynos5420_aclk_400_disp1);
+
+static struct int_comp_clks *exynos5420_int_pm_clks[] = {
+	&int_pm_clks_aclk_200_fsys,
+	&int_pm_clks_pclk_200_fsys,
+	&int_pm_clks_aclk_100_noc,
+	&int_pm_clks_aclk_400_wcore,
+	&int_pm_clks_aclk_200_fsys2,
+	&int_pm_clks_aclk_400_mscl,
+	&int_pm_clks_aclk_166,
+	&int_pm_clks_aclk_266,
+	&int_pm_clks_aclk_66,
+	&int_pm_clks_aclk_300_disp1,
+	&int_pm_clks_aclk_300_jpeg,
+	&int_pm_clks_aclk_400_disp1,
+	NULL,
+};
+
+static int exynos5250_int_set_rate(struct busfreq_data_int *data,
 				unsigned long rate)
 {
 	int index, i;
 
-	for (index = 0; index < ARRAY_SIZE(exynos5_int_opp_table); index++) {
-		if (exynos5_int_opp_table[index].clk == rate)
+	for (index = 0; index < ARRAY_SIZE(exynos5250_int_opp_table); index++) {
+		if (exynos5250_int_opp_table[index].clk == rate)
 			break;
 	}
 
@@ -161,8 +370,8 @@ static int exynos5_int_set_rate(struct busfreq_data_int *data,
 		return -EINVAL;
 
 	/* Change the system clock divider values */
-	for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
-		struct int_clk *clk_info = &exynos5_int_clks[i];
+	for (i = 0; i < ARRAY_SIZE(exynos5250_int_clks); i++) {
+		struct int_simple_clk *clk_info = &exynos5250_int_clks[i];
 		int ret;
 
 		ret = clk_set_rate(clk_info->clk,
@@ -177,10 +386,111 @@ static int exynos5_int_set_rate(struct busfreq_data_int *data,
 	return 0;
 }
 
+static struct clk *exynos5420_find_pll(struct busfreq_data_int *data,
+				    enum int_bus_pll target_pll)
+{
+	struct clk *target_src_clk = NULL;
+
+	switch (target_pll) {
+	case C_PLL:
+		target_src_clk = data->mout_cpll;
+		break;
+	case M_PLL:
+		target_src_clk = data->mout_mpll;
+		break;
+	case D_PLL:
+		target_src_clk = data->mout_dpll;
+		break;
+	case I_PLL:
+		target_src_clk = data->mout_ipll;
+		break;
+	default:
+		break;
+	}
+
+	return target_src_clk;
+}
+
+static int exynos5420_int_set_rate(struct busfreq_data_int *data,
+		unsigned long target_freq, unsigned long pre_freq)
+{
+	unsigned int i;
+	unsigned long tar_rate;
+	int target_idx = -EINVAL;
+	int pre_idx = -EINVAL;
+	struct int_comp_clks *int_clk;
+	struct clk *new_src_pll;
+	struct clk *old_src_pll;
+	unsigned long old_src_rate, new_src_rate;
+	unsigned long rate1, rate2, rate3, rate4;
+
+	/* Find the levels for target and previous frequencies */
+	for (i = 0; i < _LV_END; i++) {
+		if (exynos5420_int_opp_table[i].clk == target_freq)
+			target_idx = exynos5420_int_opp_table[i].idx;
+		if (exynos5420_int_opp_table[i].clk == pre_freq)
+			pre_idx = exynos5420_int_opp_table[i].idx;
+	}
+
+	list_for_each_entry(int_clk, &data->list, node) {
+		tar_rate = int_clk->clk_info[target_idx].target_freq * 1000;
+
+		old_src_pll = clk_get_parent(int_clk->mux_clk);
+		new_src_pll = exynos5420_find_pll(data,
+				int_clk->clk_info[target_idx].src_pll);
+
+		if (old_src_pll == new_src_pll) {
+			/* No need to change pll */
+			clk_set_rate(int_clk->div_clk, tar_rate);
+			pr_debug("%s: %s now %lu (%lu)\n", __func__,
+				 int_clk->mux_clk_name,
+				 clk_get_rate(int_clk->div_clk), tar_rate);
+			continue;
+		}
+
+		old_src_rate = clk_get_rate(old_src_pll);
+		new_src_rate = clk_get_rate(new_src_pll);
+		rate1 = clk_get_rate(int_clk->div_clk);
+
+		/*
+		 * If we're switching to a faster PLL we should bump up the
+		 * divider before switching.
+		 */
+		if (new_src_rate > old_src_rate) {
+			int new_div;
+			unsigned long tmp_rate;
+
+			new_div = DIV_ROUND_UP(new_src_rate, tar_rate);
+			tmp_rate = DIV_ROUND_UP(old_src_rate, new_div);
+			clk_set_rate(int_clk->div_clk, tmp_rate);
+		}
+		rate2 = clk_get_rate(int_clk->div_clk);
+
+		/* We can safely change the mux now */
+		clk_set_parent(int_clk->mux_clk, new_src_pll);
+		rate3 = clk_get_rate(int_clk->div_clk);
+
+		/*
+		 * Give us a proper divider; technically not needed in the case
+		 * where we pre-calculated the divider above (the new_src_rate >
+		 * old_src_rate case), but let's be formal about it.
+		 */
+		clk_set_rate(int_clk->div_clk, tar_rate);
+		rate4 = clk_get_rate(int_clk->div_clk);
+
+		pr_debug("%s: %s => PLL %d; %lu=>%lu=>%lu=>%lu (%lu)\n",
+			 __func__, int_clk->mux_clk_name,
+			 int_clk->clk_info[target_idx].src_pll,
+			 rate1, rate2, rate3, rate4, tar_rate);
+	}
+	return 0;
+}
+
 static int exynos5_int_setvolt(struct busfreq_data_int *data,
 				unsigned long volt)
 {
-	return regulator_set_voltage(data->vdd_int, volt, MAX_SAFEVOLT);
+	return regulator_set_voltage(data->vdd_int, volt,
+				volt + INT_VOLT_STEP_UV);
 }
 
 static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
@@ -218,18 +528,15 @@ static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
 	if (data->disabled)
 		goto out;
 
-	if (freq > exynos5_int_opp_table[0].clk)
-		pm_qos_update_request(&data->int_req, freq * 16 / 1000);
-	else
-		pm_qos_update_request(&data->int_req, -1);
-
 	if (old_freq < freq)
 		err = exynos5_int_setvolt(data, volt);
 	if (err)
 		goto out;
 
-	err = exynos5_int_set_rate(data, freq);
-
+	if (data->type == TYPE_BUSF_EXYNOS5250)
+		err = exynos5250_int_set_rate(data, freq);
+	else
+		err = exynos5420_int_set_rate(data, freq, old_freq);
 	if (err)
 		goto out;
 
@@ -267,16 +574,20 @@ static int exynos5_int_get_dev_status(struct device *dev,
 }
 
 static struct devfreq_dev_profile exynos5_devfreq_int_profile = {
-	.initial_freq		= 160000,
-	.polling_ms		= 100,
+	.polling_ms		= 10,
 	.target			= exynos5_busfreq_int_target,
 	.get_dev_status		= exynos5_int_get_dev_status,
 };
 
-static int exynos5250_init_int_tables(struct busfreq_data_int *data)
+static int exynos5_init_int_tables(struct busfreq_data_int *data)
 {
 	int i, err = 0;
 
+	if (data->type == TYPE_BUSF_EXYNOS5250)
+		exynos5_int_opp_table = exynos5250_int_opp_table;
+	else
+		exynos5_int_opp_table = exynos5420_int_opp_table;
+
 	for (i = LV_0; i < _LV_END; i++) {
 		err = dev_pm_opp_add(data->dev, exynos5_int_opp_table[i].clk,
 				exynos5_int_opp_table[i].volt);
@@ -297,6 +608,7 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
 	struct dev_pm_opp *opp;
 	unsigned long maxfreq = ULONG_MAX;
 	unsigned long freq;
+	unsigned long old_freq;
 	unsigned long volt;
 	int err = 0;
 
@@ -322,8 +634,14 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
 		if (err)
 			goto unlock;
 
-		err = exynos5_int_set_rate(data, freq);
+		old_freq = data->curr_freq;
 
+		if (data->type == TYPE_BUSF_EXYNOS5250)
+			err = exynos5250_int_set_rate(data, freq);
+		else if (data->type == TYPE_BUSF_EXYNOS5420)
+			err = exynos5420_int_set_rate(data, freq, old_freq);
+		else
+			err = -EINVAL;
 		if (err)
 			goto unlock;
 
@@ -345,16 +663,38 @@ unlock:
 	return NOTIFY_DONE;
 }
 
+static const struct of_device_id exynos5_busfreq_dt_match[];
+
+static inline int exynos5_busfreq_get_driver_data(struct platform_device *pdev)
+{
+#ifdef CONFIG_OF
+	struct exynos5_busfreq_drv_data *data;
+
+	if (pdev->dev.of_node) {
+		const struct of_device_id *match;
+
+		match = of_match_node(exynos5_busfreq_dt_match,
+					pdev->dev.of_node);
+		data = (struct exynos5_busfreq_drv_data *) match->data;
+		return data->busf_type;
+	}
+#endif
+	return platform_get_device_id(pdev)->driver_data;
+}
+
 static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 {
 	struct busfreq_data_int *data;
 	struct busfreq_ppmu_data *ppmu_data;
+	struct device_node *np = pdev->dev.of_node;
 	struct dev_pm_opp *opp;
 	struct device *dev = &pdev->dev;
-	struct device_node *np;
 	unsigned long initial_freq;
 	unsigned long initial_volt;
+	struct clk *mux_clk, *div_clk;
+	struct int_comp_clks *int_pm_clk;
 	int err = 0;
+	int nr_clk;
 	int i;
 
 	data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data_int),
@@ -364,22 +704,27 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	}
 
+	INIT_LIST_HEAD(&data->list);
+	data->type = exynos5_busfreq_get_driver_data(pdev);
+
 	ppmu_data = &data->ppmu_data;
-	ppmu_data->ppmu_end = PPMU_END;
+	if (data->type == TYPE_BUSF_EXYNOS5250) {
+		ppmu_data->ppmu_end = PPMU_END_5250;
+	} else if (data->type == TYPE_BUSF_EXYNOS5420) {
+		ppmu_data->ppmu_end = PPMU_END_5420;
+	} else {
+		dev_err(dev, "Cannot determine the device id %d\n", data->type);
+		return -EINVAL;
+	}
+
 	ppmu_data->ppmu = devm_kzalloc(dev,
-				       sizeof(struct exynos_ppmu) * PPMU_END,
-				       GFP_KERNEL);
+			sizeof(struct exynos_ppmu) * (ppmu_data->ppmu_end),
+			GFP_KERNEL);
 	if (!ppmu_data->ppmu) {
 		dev_err(dev, "Failed to allocate memory for exynos_ppmu\n");
 		return -ENOMEM;
 	}
 
-	np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu");
-	if (np == NULL) {
-		pr_err("Unable to find PPMU node\n");
-		return -ENOENT;
-	}
-
 	for (i = 0; i < ppmu_data->ppmu_end; i++) {
 		/* map PPMU memory region */
 		ppmu_data->ppmu[i].hw_base = of_iomap(np, i);
@@ -388,13 +733,17 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 			return -ENOMEM;
 		}
 	}
+
 	data->pm_notifier.notifier_call = exynos5_busfreq_int_pm_notifier_event;
 	data->dev = dev;
 	mutex_init(&data->lock);
 
-	err = exynos5250_init_int_tables(data);
-	if (err)
+	err = exynos5_init_int_tables(data);
+	if (err) {
+		dev_err(dev, "Cannot initialize busfreq table %d\n",
+			     data->type);
 		return err;
+	}
 
 	data->vdd_int = devm_regulator_get(dev, "vdd_int");
 	if (IS_ERR(data->vdd_int)) {
@@ -402,18 +751,70 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 		return PTR_ERR(data->vdd_int);
 	}
 
-	for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
-		struct int_clk *clk_info = &exynos5_int_clks[i];
+	if (data->type == TYPE_BUSF_EXYNOS5250) {
+		for (i = 0; i < ARRAY_SIZE(exynos5250_int_clks); i++) {
+			struct int_simple_clk *clk_info =
+				&exynos5250_int_clks[i];
+
+			clk_info->clk = devm_clk_get(dev, clk_info->clk_name);
+			if (IS_ERR(clk_info->clk)) {
+				dev_err(dev, "Failed to get clock %s\n",
+					clk_info->clk_name);
+				return PTR_ERR(clk_info->clk);
+			}
+		}
+	} else {
+		data->mout_ipll = devm_clk_get(dev, "mout_ipll");
+		if (IS_ERR(data->mout_ipll)) {
+			dev_err(dev, "Cannot get clock \"mout_ipll\"\n");
+			return PTR_ERR(data->mout_ipll);
+		}
 
-		clk_info->clk = devm_clk_get(dev, clk_info->clk_name);
-		if (IS_ERR(clk_info->clk)) {
-			dev_err(dev, "Failed to get clock %s\n",
-				clk_info->clk_name);
-			return PTR_ERR(clk_info->clk);
+		data->mout_mpll = devm_clk_get(dev, "mout_mpll");
+		if (IS_ERR(data->mout_mpll)) {
+			dev_err(dev, "Cannot get clock \"mout_mpll\"\n");
+			return PTR_ERR(data->mout_mpll);
+		}
+
+		data->mout_dpll = devm_clk_get(dev, "mout_dpll");
+		if (IS_ERR(data->mout_dpll)) {
+			dev_err(dev, "Cannot get clock \"mout_dpll\"\n");
+			return PTR_ERR(data->mout_dpll);
+		}
+
+		data->mout_cpll = devm_clk_get(dev, "mout_cpll");
+		if (IS_ERR(data->mout_cpll)) {
+			dev_err(dev, "Cannot get clock \"mout_cpll\"\n");
+			return PTR_ERR(data->mout_cpll);
+		}
+
+		for (nr_clk = 0; exynos5420_int_pm_clks[nr_clk] != NULL;
+								nr_clk++) {
+			int_pm_clk = exynos5420_int_pm_clks[nr_clk];
+			mux_clk = devm_clk_get(dev, int_pm_clk->mux_clk_name);
+			if (IS_ERR(mux_clk)) {
+				dev_err(dev, "Cannot get mux clock: %s\n",
+						int_pm_clk->mux_clk_name);
+				return PTR_ERR(mux_clk);
+			}
+			div_clk = devm_clk_get(dev, int_pm_clk->div_clk_name);
+			if (IS_ERR(div_clk)) {
+				dev_err(dev, "Cannot get div clock: %s\n",
+						int_pm_clk->div_clk_name);
+				return PTR_ERR(div_clk);
+			}
+			int_pm_clk->mux_clk = mux_clk;
+			int_pm_clk->div_clk = div_clk;
+			list_add_tail(&int_pm_clk->node, &data->list);
 		}
 	}
 
 	rcu_read_lock();
+	if (data->type == TYPE_BUSF_EXYNOS5250)
+		exynos5_devfreq_int_profile.initial_freq = 160000;
+	else
+		exynos5_devfreq_int_profile.initial_freq = 333000;
+
 	opp = dev_pm_opp_find_freq_floor(dev,
 			&exynos5_devfreq_int_profile.initial_freq);
 	if (IS_ERR(opp)) {
@@ -428,10 +829,15 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 	data->curr_freq = initial_freq;
 
 	err = exynos5_int_setvolt(data, initial_volt);
-	if (err)
+	if (err) {
+		dev_err(dev, "Failed to set initial voltage\n");
 		return err;
+	}
 
-	err = exynos5_int_set_rate(data, initial_freq);
+	if (data->type == TYPE_BUSF_EXYNOS5250)
+		err = exynos5250_int_set_rate(data, initial_freq);
+	else
+		err = exynos5420_int_set_rate(data, initial_freq, initial_freq);
 	if (err) {
 		dev_err(dev, "Failed to set initial frequency\n");
 		return err;
@@ -439,28 +845,31 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, data);
 
-	busfreq_mon_reset(ppmu_data);
-
-	data->devfreq = devm_devfreq_add_device(dev, &exynos5_devfreq_int_profile,
-					   "simple_ondemand", NULL);
+	data->devfreq = devm_devfreq_add_device(dev,
+		&exynos5_devfreq_int_profile, "simple_ondemand", NULL);
 	if (IS_ERR(data->devfreq))
 		return PTR_ERR(data->devfreq);
 
+	/*
+	 * Start PPMU (Performance Profiling Monitoring Unit) to check
+	 * utilization of each IP in the Exynos4 SoC.
+	 */
+	busfreq_mon_reset(ppmu_data);
+
+	/* Register opp_notifier for Exynos5 busfreq */
 	err = devm_devfreq_register_opp_notifier(dev, data->devfreq);
 	if (err < 0) {
 		dev_err(dev, "Failed to register opp notifier\n");
 		return err;
 	}
 
+	/* Register pm_notifier for Exynos5 busfreq */
 	err = register_pm_notifier(&data->pm_notifier);
 	if (err) {
 		dev_err(dev, "Failed to setup pm notifier\n");
 		return err;
 	}
 
-	/* TODO: Add a new QOS class for int/mif bus */
-	pm_qos_add_request(&data->int_req, PM_QOS_NETWORK_THROUGHPUT, -1);
-
 	return 0;
 }
 
@@ -468,7 +877,7 @@ static int exynos5_busfreq_int_remove(struct platform_device *pdev)
 {
 	struct busfreq_data_int *data = platform_get_drvdata(pdev);
 
-	pm_qos_remove_request(&data->int_req);
+	/* Unregister all of notifier chain */
 	unregister_pm_notifier(&data->pm_notifier);
 
 	return 0;
@@ -492,8 +901,24 @@ static const struct dev_pm_ops exynos5_busfreq_int_pm = {
 static SIMPLE_DEV_PM_OPS(exynos5_busfreq_int_pm_ops, NULL,
 			 exynos5_busfreq_int_resume);
 
-/* platform device pointer for exynos5 devfreq device. */
-static struct platform_device *exynos5_devfreq_pdev;
+#ifdef CONFIG_OF
+static struct exynos5_busfreq_drv_data exynos_busfreq_data_array[] = {
+	[TYPE_BUSF_EXYNOS5250] = { TYPE_BUSF_EXYNOS5250 },
+	[TYPE_BUSF_EXYNOS5420] = { TYPE_BUSF_EXYNOS5420 },
+};
+
+static const struct of_device_id exynos5_busfreq_dt_match[] = {
+	{
+		.compatible = "samsung,exynos5250-int-busfreq",
+		.data = &exynos_busfreq_data_array[TYPE_BUSF_EXYNOS5250],
+	}, {
+		.compatible = "samsung,exynos5420-int-busfreq",
+		.data = &exynos_busfreq_data_array[TYPE_BUSF_EXYNOS5420],
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, exynos5_busfreq_dt_match);
+#endif
 
 static struct platform_driver exynos5_busfreq_int_driver = {
 	.probe		= exynos5_busfreq_int_probe,
@@ -502,35 +927,18 @@ static struct platform_driver exynos5_busfreq_int_driver = {
 		.name		= "exynos5-bus-int",
 		.owner		= THIS_MODULE,
 		.pm		= &exynos5_busfreq_int_pm_ops,
+		.of_match_table	= of_match_ptr(exynos5_busfreq_dt_match),
 	},
 };
 
 static int __init exynos5_busfreq_int_init(void)
 {
-	int ret;
-
-	ret = platform_driver_register(&exynos5_busfreq_int_driver);
-	if (ret < 0)
-		goto out;
-
-	exynos5_devfreq_pdev =
-		platform_device_register_simple("exynos5-bus-int", -1, NULL, 0);
-	if (IS_ERR(exynos5_devfreq_pdev)) {
-		ret = PTR_ERR(exynos5_devfreq_pdev);
-		goto out1;
-	}
-
-	return 0;
-out1:
-	platform_driver_unregister(&exynos5_busfreq_int_driver);
-out:
-	return ret;
+	return platform_driver_register(&exynos5_busfreq_int_driver);
 }
 late_initcall(exynos5_busfreq_int_init);
 
 static void __exit exynos5_busfreq_int_exit(void)
 {
-	platform_device_unregister(exynos5_devfreq_pdev);
 	platform_driver_unregister(&exynos5_busfreq_int_driver);
 }
 module_exit(exynos5_busfreq_int_exit);
-- 
1.7.9.5


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

* Re: [PATCH v3 1/7] clk: exynos5250: add aliases for clocks used by devfreq
  2014-07-15 18:33   ` [PATCH v3 " Abhilash Kesavan
@ 2014-07-16 10:50     ` Tomasz Figa
  2014-07-16 18:04       ` Abhilash Kesavan
  0 siblings, 1 reply; 32+ messages in thread
From: Tomasz Figa @ 2014-07-16 10:50 UTC (permalink / raw)
  To: Abhilash Kesavan, myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, kesavan.abhilash, devicetree

Hi Abhilash, Andrew,

On 15.07.2014 20:33, Abhilash Kesavan wrote:
> From: Andrew Bresticker <abrestic@chromium.org>
> 
> Devfreq does not support DT-based lookup of these peripheral clocks,
> so add aliases for them.

I'm afraid I have to NAK this. This is going backwards. Please fix the
devfreq driver instead.

Best regards,
Tomasz

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

* Re: [PATCH v3 2/7] clk: exynos5420: Add aliases for clocks used by devfreq
  2014-07-15 18:34   ` [PATCH v3 " Abhilash Kesavan
@ 2014-07-16 10:51     ` Tomasz Figa
  0 siblings, 0 replies; 32+ messages in thread
From: Tomasz Figa @ 2014-07-16 10:51 UTC (permalink / raw)
  To: Abhilash Kesavan, myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, kesavan.abhilash, devicetree

Hi Abhilash, Arun, Arjun, Andrew,

On 15.07.2014 20:34, Abhilash Kesavan wrote:
> From: "Arjun.K.V" <arjun.kv@samsung.com>
> 
> Devfreq does not support DT-based lookup of these peripheral clocks,
> so add aliases for them.
> 
> Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
> Signed-off-by: Arjun.K.V <arjun.kv@samsung.com>
> Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
> ---
>  drivers/clk/samsung/clk-exynos5420.c |   73 ++++++++++++++++++++--------------
>  1 file changed, 44 insertions(+), 29 deletions(-)
> 

I'm really sorry, but no more aliases for DT-only platforms, NAK.

Best regards,
Tomasz

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

* Re: [PATCH v3 3/7] ARM: dts: Add PPMU device tree support for Exynos5250
  2014-07-15 18:34     ` [PATCH v3 " Abhilash Kesavan
@ 2014-07-16 10:55       ` Tomasz Figa
  2014-07-16 18:00         ` Abhilash Kesavan
  2014-07-16 10:56       ` Tomasz Figa
  1 sibling, 1 reply; 32+ messages in thread
From: Tomasz Figa @ 2014-07-16 10:55 UTC (permalink / raw)
  To: Abhilash Kesavan, myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, kesavan.abhilash, devicetree, Mark Rutland, Rob Herring

Hi Abhilash,

Please see my comments inline. Also CCing some DT maintainers.

On 15.07.2014 20:34, Abhilash Kesavan wrote:
> PPMU is required by the exynos5250 devfreq driver. Add a device
> tree node for it.
> 
> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
> ---
>  .../bindings/arm/exynos/ppmu-exynos5.txt           |   26 ++++++++++++++++++++
>  arch/arm/boot/dts/exynos5250.dtsi                  |    8 ++++++
>  2 files changed, 34 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt
> 
> diff --git a/Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt b/Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt
> new file mode 100644
> index 0000000..a6a2eba
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt
> @@ -0,0 +1,26 @@
> +Exynos PPMU driver
> +-------------------
> +
> +Performance events are primitive values used to get performance data. These
> +events provide information about the behavior of the SoC that can be used
> +when analyzing system performance. These events are made visible using the
> +PPMU logic.
> +Exynos PPMU driver is used by the exynos5 devfreq drivers to control the
> +bus frequency/voltage.
> +
> +Required properties:
> +- compatible: "samsung,exynos5250-int-busfreq", "samsung,exynos5420-int-busfreq"

If this is a binding for PPMU, shouldn't the compatible string contain
the "ppmu" string too? E.g. "samsung,exynos5250-ppmu",
"samsung,exynos5420-ppmu".

> +- reg:
> +	* physical base address of the PPMUs (e.g DDR, Right Bus, Left bus etc)
> +	and length of memory mapped region.

Since PPMU are separate IP blocks, they should have their own device
nodes with only address ranges of their own. Representing all PPMUs in
the SoC with a single node is incorrect.

Best regards,
Tomasz

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

* Re: [PATCH v3 3/7] ARM: dts: Add PPMU device tree support for Exynos5250
  2014-07-15 18:34     ` [PATCH v3 " Abhilash Kesavan
  2014-07-16 10:55       ` Tomasz Figa
@ 2014-07-16 10:56       ` Tomasz Figa
  2014-07-16 18:01         ` Abhilash Kesavan
  1 sibling, 1 reply; 32+ messages in thread
From: Tomasz Figa @ 2014-07-16 10:56 UTC (permalink / raw)
  To: Abhilash Kesavan, myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, kesavan.abhilash, devicetree

Actually one more comment.

On 15.07.2014 20:34, Abhilash Kesavan wrote:

[snip]

> diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
> index 834fb5a..5284f00 100644
> --- a/arch/arm/boot/dts/exynos5250.dtsi
> +++ b/arch/arm/boot/dts/exynos5250.dtsi
> @@ -778,4 +778,12 @@
>  		clocks = <&clock CLK_SSS>;
>  		clock-names = "secss";
>  	};
> +
> +	ppmu@10C40000 {
> +		compatible = "samsung,exynos5250-int-busfreq";
> +		reg = <0x10C40000 0x2000	/* PPMU_DDR_C */
> +		       0x10C50000 0x2000	/* PPMU_DDR_L */
> +		       0x10CB0000 0x2000	/* PPMU_DDR_R */
> +		       0x13660000 0x2000>;	/* PPMU_RIGHT */
> +	};
>  };
> 

Please separate patches adding the binding from patches modifying device
tree sources. Those are two logically separate changes.

Best regards,
Tomasz

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

* Re: [PATCH v3 5/7] PM / devfreq: exynos5250: migrate to common-clock
  2014-07-15 18:35   ` [PATCH v3 " Abhilash Kesavan
@ 2014-07-16 11:03     ` Tomasz Figa
  2014-07-16 18:00       ` Abhilash Kesavan
  0 siblings, 1 reply; 32+ messages in thread
From: Tomasz Figa @ 2014-07-16 11:03 UTC (permalink / raw)
  To: Abhilash Kesavan, myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, kesavan.abhilash, devicetree

Hi Abhilash, Andrew,

Please see my comments inline.

On 15.07.2014 20:35, Abhilash Kesavan wrote:
> From: Andrew Bresticker <abrestic@chromium.org>
> 
> Use the common-clock framework to scale frequencies for the various
> peripheral clocks on the Exynos5250.
> 
> Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
> ---
>  drivers/devfreq/exynos/exynos5_bus.c |  131 ++++++++++++++++++++++++++++++----
>  1 file changed, 119 insertions(+), 12 deletions(-)

[snip]

> +
>  static struct int_bus_opp_table exynos5_int_opp_table[] = {
>  	{LV_0, 266000, 1025000},
>  	{LV_1, 200000, 1025000},
> @@ -75,6 +85,98 @@ static struct int_bus_opp_table exynos5_int_opp_table[] = {
>  	{0, 0, 0},
>  };
>  
> +static struct int_clk_table aclk_166[] = {
> +	{LV_0, 167000},
> +	{LV_1, 111000},
> +	{LV_2,  84000},
> +	{LV_3,  84000},
> +	{LV_4,  42000},
> +};

[snip]

> +static struct int_clk_table aclk_300_gscl[] = {
> +	{LV_0, 267000},
> +	{LV_1, 267000},
> +	{LV_2, 267000},
> +	{LV_3, 200000},
> +	{LV_4, 100000},
> +};

Shouldn't you manage this using OPP framework and parse this from DT?

> +
> +#define EXYNOS5_INT_CLK(name, tbl) {		\
> +	.clk_name = name,			\
> +	.freq_table = tbl,			\
> +}

[snip]

> @@ -320,16 +427,16 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>  	rcu_read_unlock();
>  	data->curr_freq = initial_freq;
>  
> -	err = clk_set_rate(data->int_clk, initial_freq * 1000);
> +	err = exynos5_int_setvolt(data, initial_volt);
> +	if (err)
> +		return err;
> +
> +	err = exynos5_int_set_rate(data, initial_freq);
>  	if (err) {
>  		dev_err(dev, "Failed to set initial frequency\n");
>  		return err;
>  	}
>  
> -	err = exynos5_int_setvolt(data, initial_volt);
> -	if (err)
> -		return err;
> -

Whether voltage or rate should be set first depends on current settings
and initial_{freq,volt}. Also it might be more convenient to simply call
exynos5_busfreq_int_target() here.

Best regards,
Tomasz

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

* Re: [PATCH v3 7/7] PM / devfreq: Add devfreq driver for Exynos5420
  2014-07-15 18:36     ` [PATCH v3 " Abhilash Kesavan
@ 2014-07-16 11:55       ` Tomasz Figa
  2014-07-16 17:59         ` Abhilash Kesavan
  0 siblings, 1 reply; 32+ messages in thread
From: Tomasz Figa @ 2014-07-16 11:55 UTC (permalink / raw)
  To: Abhilash Kesavan, myungjoo.ham, linux-pm, kgene.kim
  Cc: rjw, kesavan.abhilash, devicetree

Hi Abhilash,

Please see my comments inline.

On 15.07.2014 20:36, Abhilash Kesavan wrote:
> From: "Arjun.K.V" <arjun.kv@samsung.com>
> 
> Exynos5420 bus device devfreq driver monitors PPMU counters and
> adjusts INT domain operating frequencies and voltages. Support
> for 5420 has been added to the extant 5250 support.
> 
> Signed-off-by: Arjun.K.V <arjun.kv@samsung.com>
> Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
> Signed-off-by: Doug Anderson <dianders@chromium.org>
> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
> ---
>  drivers/devfreq/Kconfig              |   10 +-
>  drivers/devfreq/exynos/exynos5_bus.c |  598 ++++++++++++++++++++++++++++------
>  2 files changed, 508 insertions(+), 100 deletions(-)

[snip]

>  
> -#define MAX_SAFEVOLT			1100000 /* 1.10V */
> -/* Assume that the bus is saturated if the utilization is 25% */
> -#define INT_BUS_SATURATION_RATIO	25
> +/* Assume that we need to bump up the level if the utilization is 10% */
> +#define INT_BUS_SATURATION_RATIO	10

There is nothing about this change in commit message and changing the
ratio from 25% to 10% seems to be rather significant. Please give the
rationale for this change.

> +#define INT_VOLT_STEP_UV		12500
> +
> +struct exynos5_busfreq_drv_data {
> +	int busf_type;
> +};
> +
> +enum exynos5_busf_type {
> +	TYPE_BUSF_EXYNOS5250,
> +	TYPE_BUSF_EXYNOS5420,
> +};

Using this kind of enums is discouraged. Rather than that, just create a
structure that describes the differences between variants and use this
as type.

>  
>  enum int_level_idx {
>  	LV_0,
> @@ -41,12 +46,31 @@ enum int_level_idx {
>  	_LV_END
>  };

[snip]

> +
> +static struct int_bus_opp_table *exynos5_int_opp_table;
> +static struct int_bus_opp_table exynos5250_int_opp_table[] = {
>  	{LV_0, 266000, 1025000},
>  	{LV_1, 200000, 1025000},
>  	{LV_2, 160000, 1025000},

[snip]

> +static struct int_clk_info exynos5420_aclk_300_jpeg[] = {
> +	/* Level, Freq, Parent_Pll */
> +	{LV_0, 300000, D_PLL},
> +	{LV_1, 300000, D_PLL},
> +	{LV_2, 200000, D_PLL},
> +	{LV_3, 150000, D_PLL},
> +	{LV_4,  75000, D_PLL},
> +};

These should be parsed from DT.

> +
> +#define EXYNOS5420_INT_PM_CLK(NAME, CLK, PCLK, CLK_INFO)	\
> +static struct int_comp_clks int_pm_clks_##NAME = {	\
> +	.mux_clk_name = CLK,				\
> +	.div_clk_name = PCLK,				\
> +	.clk_info = CLK_INFO,				\
> +}
> +
> +EXYNOS5420_INT_PM_CLK(aclk_200_fsys, "aclk200_fsys",
> +			"aclk200_fsys_d", exynos5420_aclk_200_fsys);
> +EXYNOS5420_INT_PM_CLK(pclk_200_fsys, "pclk200_fsys",
> +			"pclk200_fsys_d", exynos5420_pclk_200_fsys);
> +EXYNOS5420_INT_PM_CLK(aclk_100_noc, "aclk100_noc",
> +			"aclk100_noc_d", exynos5420_aclk_100_noc);
> +EXYNOS5420_INT_PM_CLK(aclk_400_wcore, "aclk400_wcore",
> +			"aclk400_wcore_d", exynos5420_aclk_400_wcore);
> +EXYNOS5420_INT_PM_CLK(aclk_200_fsys2, "aclk200_fsys2",
> +			"aclk200_fsys2_d", exynos5420_aclk_200_fsys2);
> +EXYNOS5420_INT_PM_CLK(aclk_400_mscl, "aclk400_mscl",
> +			"aclk400_mscl_d", exynos5420_aclk_400_mscl);
> +EXYNOS5420_INT_PM_CLK(aclk_166, "aclk166",
> +			"aclk166_d", exynos5420_aclk_166);
> +EXYNOS5420_INT_PM_CLK(aclk_266, "aclk266",
> +			"aclk266_d", exynos5420_aclk_266);
> +EXYNOS5420_INT_PM_CLK(aclk_66, "aclk66",
> +			"aclk66_d", exynos5420_aclk_66);
> +EXYNOS5420_INT_PM_CLK(aclk_300_disp1, "aclk300_disp1",
> +			"aclk300_disp1_d", exynos5420_aclk_300_disp1);
> +EXYNOS5420_INT_PM_CLK(aclk_300_jpeg, "aclk300_jpeg",
> +			"aclk300_jpeg_d", exynos5420_aclk_300_jpeg);
> +EXYNOS5420_INT_PM_CLK(aclk_400_disp1, "aclk400_disp1",
> +			"aclk400_disp1_d", exynos5420_aclk_400_disp1);

List of the clocks should be parsed from DT as well, without hardcoding
data for every SoC in the driver.

> +
> +static struct int_comp_clks *exynos5420_int_pm_clks[] = {
> +	&int_pm_clks_aclk_200_fsys,
> +	&int_pm_clks_pclk_200_fsys,
> +	&int_pm_clks_aclk_100_noc,
> +	&int_pm_clks_aclk_400_wcore,
> +	&int_pm_clks_aclk_200_fsys2,
> +	&int_pm_clks_aclk_400_mscl,
> +	&int_pm_clks_aclk_166,
> +	&int_pm_clks_aclk_266,
> +	&int_pm_clks_aclk_66,
> +	&int_pm_clks_aclk_300_disp1,
> +	&int_pm_clks_aclk_300_jpeg,
> +	&int_pm_clks_aclk_400_disp1,
> +	NULL,
> +};
> +
> +static int exynos5250_int_set_rate(struct busfreq_data_int *data,
>  				unsigned long rate)
>  {
>  	int index, i;
>  
> -	for (index = 0; index < ARRAY_SIZE(exynos5_int_opp_table); index++) {
> -		if (exynos5_int_opp_table[index].clk == rate)
> +	for (index = 0; index < ARRAY_SIZE(exynos5250_int_opp_table); index++) {
> +		if (exynos5250_int_opp_table[index].clk == rate)
>  			break;
>  	}
>  
> @@ -161,8 +370,8 @@ static int exynos5_int_set_rate(struct busfreq_data_int *data,
>  		return -EINVAL;
>  
>  	/* Change the system clock divider values */
> -	for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
> -		struct int_clk *clk_info = &exynos5_int_clks[i];
> +	for (i = 0; i < ARRAY_SIZE(exynos5250_int_clks); i++) {
> +		struct int_simple_clk *clk_info = &exynos5250_int_clks[i];
>  		int ret;
>  
>  		ret = clk_set_rate(clk_info->clk,
> @@ -177,10 +386,111 @@ static int exynos5_int_set_rate(struct busfreq_data_int *data,
>  	return 0;
>  }
>  
> +static struct clk *exynos5420_find_pll(struct busfreq_data_int *data,
> +				    enum int_bus_pll target_pll)
> +{
> +	struct clk *target_src_clk = NULL;
> +
> +	switch (target_pll) {
> +	case C_PLL:
> +		target_src_clk = data->mout_cpll;
> +		break;
> +	case M_PLL:
> +		target_src_clk = data->mout_mpll;
> +		break;
> +	case D_PLL:
> +		target_src_clk = data->mout_dpll;
> +		break;
> +	case I_PLL:
> +		target_src_clk = data->mout_ipll;
> +		break;
> +	default:
> +		break;
> +	}

What about storing the clocks in an array? Then all you would need to do
could be as simple as accessing data->plls[target_pll].

> +
> +	return target_src_clk;
> +}
> +
> +static int exynos5420_int_set_rate(struct busfreq_data_int *data,
> +		unsigned long target_freq, unsigned long pre_freq)
> +{
> +	unsigned int i;
> +	unsigned long tar_rate;
> +	int target_idx = -EINVAL;
> +	int pre_idx = -EINVAL;
> +	struct int_comp_clks *int_clk;
> +	struct clk *new_src_pll;
> +	struct clk *old_src_pll;
> +	unsigned long old_src_rate, new_src_rate;
> +	unsigned long rate1, rate2, rate3, rate4;
> +
> +	/* Find the levels for target and previous frequencies */
> +	for (i = 0; i < _LV_END; i++) {
> +		if (exynos5420_int_opp_table[i].clk == target_freq)
> +			target_idx = exynos5420_int_opp_table[i].idx;
> +		if (exynos5420_int_opp_table[i].clk == pre_freq)
> +			pre_idx = exynos5420_int_opp_table[i].idx;
> +	}
> +
> +	list_for_each_entry(int_clk, &data->list, node) {
> +		tar_rate = int_clk->clk_info[target_idx].target_freq * 1000;
> +
> +		old_src_pll = clk_get_parent(int_clk->mux_clk);
> +		new_src_pll = exynos5420_find_pll(data,
> +				int_clk->clk_info[target_idx].src_pll);
> +
> +		if (old_src_pll == new_src_pll) {
> +			/* No need to change pll */
> +			clk_set_rate(int_clk->div_clk, tar_rate);
> +			pr_debug("%s: %s now %lu (%lu)\n", __func__,
> +				 int_clk->mux_clk_name,
> +				 clk_get_rate(int_clk->div_clk), tar_rate);
> +			continue;
> +		}
> +
> +		old_src_rate = clk_get_rate(old_src_pll);
> +		new_src_rate = clk_get_rate(new_src_pll);
> +		rate1 = clk_get_rate(int_clk->div_clk);
> +
> +		/*
> +		 * If we're switching to a faster PLL we should bump up the
> +		 * divider before switching.
> +		 */
> +		if (new_src_rate > old_src_rate) {
> +			int new_div;
> +			unsigned long tmp_rate;
> +
> +			new_div = DIV_ROUND_UP(new_src_rate, tar_rate);
> +			tmp_rate = DIV_ROUND_UP(old_src_rate, new_div);
> +			clk_set_rate(int_clk->div_clk, tmp_rate);
> +		}
> +		rate2 = clk_get_rate(int_clk->div_clk);
> +
> +		/* We can safely change the mux now */
> +		clk_set_parent(int_clk->mux_clk, new_src_pll);
> +		rate3 = clk_get_rate(int_clk->div_clk);
> +
> +		/*
> +		 * Give us a proper divider; technically not needed in the case
> +		 * where we pre-calculated the divider above (the new_src_rate >
> +		 * old_src_rate case), but let's be formal about it.
> +		 */
> +		clk_set_rate(int_clk->div_clk, tar_rate);
> +		rate4 = clk_get_rate(int_clk->div_clk);
> +
> +		pr_debug("%s: %s => PLL %d; %lu=>%lu=>%lu=>%lu (%lu)\n",
> +			 __func__, int_clk->mux_clk_name,
> +			 int_clk->clk_info[target_idx].src_pll,
> +			 rate1, rate2, rate3, rate4, tar_rate);
> +	}
> +	return 0;
> +}

The above function looks like it could be made much more generic and
reused for Exynos5250 as well.

> +
>  static int exynos5_int_setvolt(struct busfreq_data_int *data,
>  				unsigned long volt)
>  {
> -	return regulator_set_voltage(data->vdd_int, volt, MAX_SAFEVOLT);
> +	return regulator_set_voltage(data->vdd_int, volt,
> +				volt + INT_VOLT_STEP_UV);
>  }
>  
>  static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
> @@ -218,18 +528,15 @@ static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
>  	if (data->disabled)
>  		goto out;
>  
> -	if (freq > exynos5_int_opp_table[0].clk)
> -		pm_qos_update_request(&data->int_req, freq * 16 / 1000);
> -	else
> -		pm_qos_update_request(&data->int_req, -1);
> -
>  	if (old_freq < freq)
>  		err = exynos5_int_setvolt(data, volt);
>  	if (err)
>  		goto out;
>  
> -	err = exynos5_int_set_rate(data, freq);
> -
> +	if (data->type == TYPE_BUSF_EXYNOS5250)
> +		err = exynos5250_int_set_rate(data, freq);
> +	else
> +		err = exynos5420_int_set_rate(data, freq, old_freq);
>  	if (err)
>  		goto out;
>  
> @@ -267,16 +574,20 @@ static int exynos5_int_get_dev_status(struct device *dev,
>  }
>  
>  static struct devfreq_dev_profile exynos5_devfreq_int_profile = {
> -	.initial_freq		= 160000,
> -	.polling_ms		= 100,
> +	.polling_ms		= 10,

Another change not mentioned in commit message.

>  	.target			= exynos5_busfreq_int_target,
>  	.get_dev_status		= exynos5_int_get_dev_status,
>  };
>  
> -static int exynos5250_init_int_tables(struct busfreq_data_int *data)
> +static int exynos5_init_int_tables(struct busfreq_data_int *data)
>  {
>  	int i, err = 0;
>  
> +	if (data->type == TYPE_BUSF_EXYNOS5250)
> +		exynos5_int_opp_table = exynos5250_int_opp_table;
> +	else
> +		exynos5_int_opp_table = exynos5420_int_opp_table;
> +
>  	for (i = LV_0; i < _LV_END; i++) {
>  		err = dev_pm_opp_add(data->dev, exynos5_int_opp_table[i].clk,
>  				exynos5_int_opp_table[i].volt);
> @@ -297,6 +608,7 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
>  	struct dev_pm_opp *opp;
>  	unsigned long maxfreq = ULONG_MAX;
>  	unsigned long freq;
> +	unsigned long old_freq;
>  	unsigned long volt;
>  	int err = 0;
>  
> @@ -322,8 +634,14 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
>  		if (err)
>  			goto unlock;
>  
> -		err = exynos5_int_set_rate(data, freq);
> +		old_freq = data->curr_freq;
>  
> +		if (data->type == TYPE_BUSF_EXYNOS5250)
> +			err = exynos5250_int_set_rate(data, freq);
> +		else if (data->type == TYPE_BUSF_EXYNOS5420)
> +			err = exynos5420_int_set_rate(data, freq, old_freq);
> +		else
> +			err = -EINVAL;
>  		if (err)
>  			goto unlock;
>  
> @@ -345,16 +663,38 @@ unlock:
>  	return NOTIFY_DONE;
>  }
>  
> +static const struct of_device_id exynos5_busfreq_dt_match[];
> +
> +static inline int exynos5_busfreq_get_driver_data(struct platform_device *pdev)
> +{
> +#ifdef CONFIG_OF

Exynos is DT-only, so there is no need to handle non-DT cases.

> +	struct exynos5_busfreq_drv_data *data;
> +
> +	if (pdev->dev.of_node) {
> +		const struct of_device_id *match;
> +
> +		match = of_match_node(exynos5_busfreq_dt_match,
> +					pdev->dev.of_node);
> +		data = (struct exynos5_busfreq_drv_data *) match->data;
> +		return data->busf_type;
> +	}
> +#endif
> +	return platform_get_device_id(pdev)->driver_data;
> +}
> +
>  static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>  {
>  	struct busfreq_data_int *data;
>  	struct busfreq_ppmu_data *ppmu_data;
> +	struct device_node *np = pdev->dev.of_node;
>  	struct dev_pm_opp *opp;
>  	struct device *dev = &pdev->dev;
> -	struct device_node *np;
>  	unsigned long initial_freq;
>  	unsigned long initial_volt;
> +	struct clk *mux_clk, *div_clk;
> +	struct int_comp_clks *int_pm_clk;
>  	int err = 0;
> +	int nr_clk;
>  	int i;
>  
>  	data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data_int),
> @@ -364,22 +704,27 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>  		return -ENOMEM;
>  	}
>  
> +	INIT_LIST_HEAD(&data->list);
> +	data->type = exynos5_busfreq_get_driver_data(pdev);
> +
>  	ppmu_data = &data->ppmu_data;
> -	ppmu_data->ppmu_end = PPMU_END;
> +	if (data->type == TYPE_BUSF_EXYNOS5250) {
> +		ppmu_data->ppmu_end = PPMU_END_5250;
> +	} else if (data->type == TYPE_BUSF_EXYNOS5420) {
> +		ppmu_data->ppmu_end = PPMU_END_5420;
> +	} else {
> +		dev_err(dev, "Cannot determine the device id %d\n", data->type);
> +		return -EINVAL;
> +	}
> +
>  	ppmu_data->ppmu = devm_kzalloc(dev,
> -				       sizeof(struct exynos_ppmu) * PPMU_END,
> -				       GFP_KERNEL);
> +			sizeof(struct exynos_ppmu) * (ppmu_data->ppmu_end),
> +			GFP_KERNEL);
>  	if (!ppmu_data->ppmu) {
>  		dev_err(dev, "Failed to allocate memory for exynos_ppmu\n");
>  		return -ENOMEM;
>  	}
>  
> -	np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu");
> -	if (np == NULL) {
> -		pr_err("Unable to find PPMU node\n");
> -		return -ENOENT;
> -	}

This was actually closer to the right solution than what this series
does. Actually there was similar attempt already, but for Exynos4 and I
even suggested how this could be handled properly. Please see [1] for
the whole discussion.

[1]
https://www.mail-archive.com/linux-samsung-soc@vger.kernel.org/msg27491.html

> -
>  	for (i = 0; i < ppmu_data->ppmu_end; i++) {
>  		/* map PPMU memory region */
>  		ppmu_data->ppmu[i].hw_base = of_iomap(np, i);
> @@ -388,13 +733,17 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>  			return -ENOMEM;
>  		}
>  	}
> +
>  	data->pm_notifier.notifier_call = exynos5_busfreq_int_pm_notifier_event;
>  	data->dev = dev;
>  	mutex_init(&data->lock);
>  
> -	err = exynos5250_init_int_tables(data);
> -	if (err)
> +	err = exynos5_init_int_tables(data);
> +	if (err) {
> +		dev_err(dev, "Cannot initialize busfreq table %d\n",
> +			     data->type);
>  		return err;
> +	}
>  
>  	data->vdd_int = devm_regulator_get(dev, "vdd_int");
>  	if (IS_ERR(data->vdd_int)) {
> @@ -402,18 +751,70 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>  		return PTR_ERR(data->vdd_int);
>  	}
>  
> -	for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
> -		struct int_clk *clk_info = &exynos5_int_clks[i];
> +	if (data->type == TYPE_BUSF_EXYNOS5250) {
> +		for (i = 0; i < ARRAY_SIZE(exynos5250_int_clks); i++) {
> +			struct int_simple_clk *clk_info =
> +				&exynos5250_int_clks[i];
> +
> +			clk_info->clk = devm_clk_get(dev, clk_info->clk_name);
> +			if (IS_ERR(clk_info->clk)) {
> +				dev_err(dev, "Failed to get clock %s\n",
> +					clk_info->clk_name);
> +				return PTR_ERR(clk_info->clk);
> +			}
> +		}
> +	} else {
> +		data->mout_ipll = devm_clk_get(dev, "mout_ipll");
> +		if (IS_ERR(data->mout_ipll)) {
> +			dev_err(dev, "Cannot get clock \"mout_ipll\"\n");
> +			return PTR_ERR(data->mout_ipll);
> +		}
>  
> -		clk_info->clk = devm_clk_get(dev, clk_info->clk_name);
> -		if (IS_ERR(clk_info->clk)) {
> -			dev_err(dev, "Failed to get clock %s\n",
> -				clk_info->clk_name);
> -			return PTR_ERR(clk_info->clk);
> +		data->mout_mpll = devm_clk_get(dev, "mout_mpll");
> +		if (IS_ERR(data->mout_mpll)) {
> +			dev_err(dev, "Cannot get clock \"mout_mpll\"\n");
> +			return PTR_ERR(data->mout_mpll);
> +		}
> +
> +		data->mout_dpll = devm_clk_get(dev, "mout_dpll");
> +		if (IS_ERR(data->mout_dpll)) {
> +			dev_err(dev, "Cannot get clock \"mout_dpll\"\n");
> +			return PTR_ERR(data->mout_dpll);
> +		}
> +
> +		data->mout_cpll = devm_clk_get(dev, "mout_cpll");
> +		if (IS_ERR(data->mout_cpll)) {
> +			dev_err(dev, "Cannot get clock \"mout_cpll\"\n");
> +			return PTR_ERR(data->mout_cpll);
> +		}
> +
> +		for (nr_clk = 0; exynos5420_int_pm_clks[nr_clk] != NULL;
> +								nr_clk++) {
> +			int_pm_clk = exynos5420_int_pm_clks[nr_clk];
> +			mux_clk = devm_clk_get(dev, int_pm_clk->mux_clk_name);
> +			if (IS_ERR(mux_clk)) {
> +				dev_err(dev, "Cannot get mux clock: %s\n",
> +						int_pm_clk->mux_clk_name);
> +				return PTR_ERR(mux_clk);
> +			}
> +			div_clk = devm_clk_get(dev, int_pm_clk->div_clk_name);
> +			if (IS_ERR(div_clk)) {
> +				dev_err(dev, "Cannot get div clock: %s\n",
> +						int_pm_clk->div_clk_name);
> +				return PTR_ERR(div_clk);
> +			}
> +			int_pm_clk->mux_clk = mux_clk;
> +			int_pm_clk->div_clk = div_clk;
> +			list_add_tail(&int_pm_clk->node, &data->list);

All those could be probably handled with an array and a for loop.

In general, this patch apparently contains many separate changes and not
only is hard to review but also hard to debug potential problems - git
bisect has commit granularity.

Please refactor the driver step by step first and then add support for
new SoC after it has all the needed features.

Best regards,
Tomasz

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

* Re: [PATCH v3 7/7] PM / devfreq: Add devfreq driver for Exynos5420
  2014-07-16 11:55       ` Tomasz Figa
@ 2014-07-16 17:59         ` Abhilash Kesavan
  2014-07-28 12:01           ` Abhilash Kesavan
  0 siblings, 1 reply; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-16 17:59 UTC (permalink / raw)
  To: Tomasz Figa, 최찬우
  Cc: myungjoo.ham, linux-pm@vger.kernel.org, Kukjin Kim, devicetree,
	rafael.j.wysocki@intel.com

Hi Tomasz,

On Wed, Jul 16, 2014 at 5:25 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> Hi Abhilash,
>
> Please see my comments inline.
>
> On 15.07.2014 20:36, Abhilash Kesavan wrote:
>> From: "Arjun.K.V" <arjun.kv@samsung.com>
>>
>> Exynos5420 bus device devfreq driver monitors PPMU counters and
>> adjusts INT domain operating frequencies and voltages. Support
>> for 5420 has been added to the extant 5250 support.
>>
>> Signed-off-by: Arjun.K.V <arjun.kv@samsung.com>
>> Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
>> Signed-off-by: Doug Anderson <dianders@chromium.org>
>> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
>> ---
>>  drivers/devfreq/Kconfig              |   10 +-
>>  drivers/devfreq/exynos/exynos5_bus.c |  598 ++++++++++++++++++++++++++++------
>>  2 files changed, 508 insertions(+), 100 deletions(-)
>
> [snip]
>
>>
>> -#define MAX_SAFEVOLT                 1100000 /* 1.10V */
>> -/* Assume that the bus is saturated if the utilization is 25% */
>> -#define INT_BUS_SATURATION_RATIO     25
>> +/* Assume that we need to bump up the level if the utilization is 10% */
>> +#define INT_BUS_SATURATION_RATIO     10
>
> There is nothing about this change in commit message and changing the
> ratio from 25% to 10% seems to be rather significant. Please give the
> rationale for this change.
Will do.
>
>> +#define INT_VOLT_STEP_UV             12500
>> +
>> +struct exynos5_busfreq_drv_data {
>> +     int busf_type;
>> +};
>> +
>> +enum exynos5_busf_type {
>> +     TYPE_BUSF_EXYNOS5250,
>> +     TYPE_BUSF_EXYNOS5420,
>> +};
>
> Using this kind of enums is discouraged. Rather than that, just create a
> structure that describes the differences between variants and use this
> as type.
OK.
>
>>
>>  enum int_level_idx {
>>       LV_0,
>> @@ -41,12 +46,31 @@ enum int_level_idx {
>>       _LV_END
>>  };
>
> [snip]
>
>> +
>> +static struct int_bus_opp_table *exynos5_int_opp_table;
>> +static struct int_bus_opp_table exynos5250_int_opp_table[] = {
>>       {LV_0, 266000, 1025000},
>>       {LV_1, 200000, 1025000},
>>       {LV_2, 160000, 1025000},
>
> [snip]
>
>> +static struct int_clk_info exynos5420_aclk_300_jpeg[] = {
>> +     /* Level, Freq, Parent_Pll */
>> +     {LV_0, 300000, D_PLL},
>> +     {LV_1, 300000, D_PLL},
>> +     {LV_2, 200000, D_PLL},
>> +     {LV_3, 150000, D_PLL},
>> +     {LV_4,  75000, D_PLL},
>> +};
>
> These should be parsed from DT.
OK.
>
>> +
>> +#define EXYNOS5420_INT_PM_CLK(NAME, CLK, PCLK, CLK_INFO)     \
>> +static struct int_comp_clks int_pm_clks_##NAME = {   \
>> +     .mux_clk_name = CLK,                            \
>> +     .div_clk_name = PCLK,                           \
>> +     .clk_info = CLK_INFO,                           \
>> +}
>> +
>> +EXYNOS5420_INT_PM_CLK(aclk_200_fsys, "aclk200_fsys",
>> +                     "aclk200_fsys_d", exynos5420_aclk_200_fsys);
>> +EXYNOS5420_INT_PM_CLK(pclk_200_fsys, "pclk200_fsys",
>> +                     "pclk200_fsys_d", exynos5420_pclk_200_fsys);
>> +EXYNOS5420_INT_PM_CLK(aclk_100_noc, "aclk100_noc",
>> +                     "aclk100_noc_d", exynos5420_aclk_100_noc);
>> +EXYNOS5420_INT_PM_CLK(aclk_400_wcore, "aclk400_wcore",
>> +                     "aclk400_wcore_d", exynos5420_aclk_400_wcore);
>> +EXYNOS5420_INT_PM_CLK(aclk_200_fsys2, "aclk200_fsys2",
>> +                     "aclk200_fsys2_d", exynos5420_aclk_200_fsys2);
>> +EXYNOS5420_INT_PM_CLK(aclk_400_mscl, "aclk400_mscl",
>> +                     "aclk400_mscl_d", exynos5420_aclk_400_mscl);
>> +EXYNOS5420_INT_PM_CLK(aclk_166, "aclk166",
>> +                     "aclk166_d", exynos5420_aclk_166);
>> +EXYNOS5420_INT_PM_CLK(aclk_266, "aclk266",
>> +                     "aclk266_d", exynos5420_aclk_266);
>> +EXYNOS5420_INT_PM_CLK(aclk_66, "aclk66",
>> +                     "aclk66_d", exynos5420_aclk_66);
>> +EXYNOS5420_INT_PM_CLK(aclk_300_disp1, "aclk300_disp1",
>> +                     "aclk300_disp1_d", exynos5420_aclk_300_disp1);
>> +EXYNOS5420_INT_PM_CLK(aclk_300_jpeg, "aclk300_jpeg",
>> +                     "aclk300_jpeg_d", exynos5420_aclk_300_jpeg);
>> +EXYNOS5420_INT_PM_CLK(aclk_400_disp1, "aclk400_disp1",
>> +                     "aclk400_disp1_d", exynos5420_aclk_400_disp1);
>
> List of the clocks should be parsed from DT as well, without hardcoding
> data for every SoC in the driver.
>
>> +
>> +static struct int_comp_clks *exynos5420_int_pm_clks[] = {
>> +     &int_pm_clks_aclk_200_fsys,
>> +     &int_pm_clks_pclk_200_fsys,
>> +     &int_pm_clks_aclk_100_noc,
>> +     &int_pm_clks_aclk_400_wcore,
>> +     &int_pm_clks_aclk_200_fsys2,
>> +     &int_pm_clks_aclk_400_mscl,
>> +     &int_pm_clks_aclk_166,
>> +     &int_pm_clks_aclk_266,
>> +     &int_pm_clks_aclk_66,
>> +     &int_pm_clks_aclk_300_disp1,
>> +     &int_pm_clks_aclk_300_jpeg,
>> +     &int_pm_clks_aclk_400_disp1,
>> +     NULL,
>> +};
>> +
>> +static int exynos5250_int_set_rate(struct busfreq_data_int *data,
>>                               unsigned long rate)
>>  {
>>       int index, i;
>>
>> -     for (index = 0; index < ARRAY_SIZE(exynos5_int_opp_table); index++) {
>> -             if (exynos5_int_opp_table[index].clk == rate)
>> +     for (index = 0; index < ARRAY_SIZE(exynos5250_int_opp_table); index++) {
>> +             if (exynos5250_int_opp_table[index].clk == rate)
>>                       break;
>>       }
>>
>> @@ -161,8 +370,8 @@ static int exynos5_int_set_rate(struct busfreq_data_int *data,
>>               return -EINVAL;
>>
>>       /* Change the system clock divider values */
>> -     for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
>> -             struct int_clk *clk_info = &exynos5_int_clks[i];
>> +     for (i = 0; i < ARRAY_SIZE(exynos5250_int_clks); i++) {
>> +             struct int_simple_clk *clk_info = &exynos5250_int_clks[i];
>>               int ret;
>>
>>               ret = clk_set_rate(clk_info->clk,
>> @@ -177,10 +386,111 @@ static int exynos5_int_set_rate(struct busfreq_data_int *data,
>>       return 0;
>>  }
>>
>> +static struct clk *exynos5420_find_pll(struct busfreq_data_int *data,
>> +                                 enum int_bus_pll target_pll)
>> +{
>> +     struct clk *target_src_clk = NULL;
>> +
>> +     switch (target_pll) {
>> +     case C_PLL:
>> +             target_src_clk = data->mout_cpll;
>> +             break;
>> +     case M_PLL:
>> +             target_src_clk = data->mout_mpll;
>> +             break;
>> +     case D_PLL:
>> +             target_src_clk = data->mout_dpll;
>> +             break;
>> +     case I_PLL:
>> +             target_src_clk = data->mout_ipll;
>> +             break;
>> +     default:
>> +             break;
>> +     }
>
> What about storing the clocks in an array? Then all you would need to do
> could be as simple as accessing data->plls[target_pll].
OK.
>
>> +
>> +     return target_src_clk;
>> +}
>> +
>> +static int exynos5420_int_set_rate(struct busfreq_data_int *data,
>> +             unsigned long target_freq, unsigned long pre_freq)
>> +{
>> +     unsigned int i;
>> +     unsigned long tar_rate;
>> +     int target_idx = -EINVAL;
>> +     int pre_idx = -EINVAL;
>> +     struct int_comp_clks *int_clk;
>> +     struct clk *new_src_pll;
>> +     struct clk *old_src_pll;
>> +     unsigned long old_src_rate, new_src_rate;
>> +     unsigned long rate1, rate2, rate3, rate4;
>> +
>> +     /* Find the levels for target and previous frequencies */
>> +     for (i = 0; i < _LV_END; i++) {
>> +             if (exynos5420_int_opp_table[i].clk == target_freq)
>> +                     target_idx = exynos5420_int_opp_table[i].idx;
>> +             if (exynos5420_int_opp_table[i].clk == pre_freq)
>> +                     pre_idx = exynos5420_int_opp_table[i].idx;
>> +     }
>> +
>> +     list_for_each_entry(int_clk, &data->list, node) {
>> +             tar_rate = int_clk->clk_info[target_idx].target_freq * 1000;
>> +
>> +             old_src_pll = clk_get_parent(int_clk->mux_clk);
>> +             new_src_pll = exynos5420_find_pll(data,
>> +                             int_clk->clk_info[target_idx].src_pll);
>> +
>> +             if (old_src_pll == new_src_pll) {
>> +                     /* No need to change pll */
>> +                     clk_set_rate(int_clk->div_clk, tar_rate);
>> +                     pr_debug("%s: %s now %lu (%lu)\n", __func__,
>> +                              int_clk->mux_clk_name,
>> +                              clk_get_rate(int_clk->div_clk), tar_rate);
>> +                     continue;
>> +             }
>> +
>> +             old_src_rate = clk_get_rate(old_src_pll);
>> +             new_src_rate = clk_get_rate(new_src_pll);
>> +             rate1 = clk_get_rate(int_clk->div_clk);
>> +
>> +             /*
>> +              * If we're switching to a faster PLL we should bump up the
>> +              * divider before switching.
>> +              */
>> +             if (new_src_rate > old_src_rate) {
>> +                     int new_div;
>> +                     unsigned long tmp_rate;
>> +
>> +                     new_div = DIV_ROUND_UP(new_src_rate, tar_rate);
>> +                     tmp_rate = DIV_ROUND_UP(old_src_rate, new_div);
>> +                     clk_set_rate(int_clk->div_clk, tmp_rate);
>> +             }
>> +             rate2 = clk_get_rate(int_clk->div_clk);
>> +
>> +             /* We can safely change the mux now */
>> +             clk_set_parent(int_clk->mux_clk, new_src_pll);
>> +             rate3 = clk_get_rate(int_clk->div_clk);
>> +
>> +             /*
>> +              * Give us a proper divider; technically not needed in the case
>> +              * where we pre-calculated the divider above (the new_src_rate >
>> +              * old_src_rate case), but let's be formal about it.
>> +              */
>> +             clk_set_rate(int_clk->div_clk, tar_rate);
>> +             rate4 = clk_get_rate(int_clk->div_clk);
>> +
>> +             pr_debug("%s: %s => PLL %d; %lu=>%lu=>%lu=>%lu (%lu)\n",
>> +                      __func__, int_clk->mux_clk_name,
>> +                      int_clk->clk_info[target_idx].src_pll,
>> +                      rate1, rate2, rate3, rate4, tar_rate);
>> +     }
>> +     return 0;
>> +}
>
> The above function looks like it could be made much more generic and
> reused for Exynos5250 as well.
I will look at making it common for the two,
>
>> +
>>  static int exynos5_int_setvolt(struct busfreq_data_int *data,
>>                               unsigned long volt)
>>  {
>> -     return regulator_set_voltage(data->vdd_int, volt, MAX_SAFEVOLT);
>> +     return regulator_set_voltage(data->vdd_int, volt,
>> +                             volt + INT_VOLT_STEP_UV);
>>  }
>>
>>  static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
>> @@ -218,18 +528,15 @@ static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
>>       if (data->disabled)
>>               goto out;
>>
>> -     if (freq > exynos5_int_opp_table[0].clk)
>> -             pm_qos_update_request(&data->int_req, freq * 16 / 1000);
>> -     else
>> -             pm_qos_update_request(&data->int_req, -1);
>> -
>>       if (old_freq < freq)
>>               err = exynos5_int_setvolt(data, volt);
>>       if (err)
>>               goto out;
>>
>> -     err = exynos5_int_set_rate(data, freq);
>> -
>> +     if (data->type == TYPE_BUSF_EXYNOS5250)
>> +             err = exynos5250_int_set_rate(data, freq);
>> +     else
>> +             err = exynos5420_int_set_rate(data, freq, old_freq);
>>       if (err)
>>               goto out;
>>
>> @@ -267,16 +574,20 @@ static int exynos5_int_get_dev_status(struct device *dev,
>>  }
>>
>>  static struct devfreq_dev_profile exynos5_devfreq_int_profile = {
>> -     .initial_freq           = 160000,
>> -     .polling_ms             = 100,
>> +     .polling_ms             = 10,
>
> Another change not mentioned in commit message.
>
>>       .target                 = exynos5_busfreq_int_target,
>>       .get_dev_status         = exynos5_int_get_dev_status,
>>  };
>>
>> -static int exynos5250_init_int_tables(struct busfreq_data_int *data)
>> +static int exynos5_init_int_tables(struct busfreq_data_int *data)
>>  {
>>       int i, err = 0;
>>
>> +     if (data->type == TYPE_BUSF_EXYNOS5250)
>> +             exynos5_int_opp_table = exynos5250_int_opp_table;
>> +     else
>> +             exynos5_int_opp_table = exynos5420_int_opp_table;
>> +
>>       for (i = LV_0; i < _LV_END; i++) {
>>               err = dev_pm_opp_add(data->dev, exynos5_int_opp_table[i].clk,
>>                               exynos5_int_opp_table[i].volt);
>> @@ -297,6 +608,7 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
>>       struct dev_pm_opp *opp;
>>       unsigned long maxfreq = ULONG_MAX;
>>       unsigned long freq;
>> +     unsigned long old_freq;
>>       unsigned long volt;
>>       int err = 0;
>>
>> @@ -322,8 +634,14 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
>>               if (err)
>>                       goto unlock;
>>
>> -             err = exynos5_int_set_rate(data, freq);
>> +             old_freq = data->curr_freq;
>>
>> +             if (data->type == TYPE_BUSF_EXYNOS5250)
>> +                     err = exynos5250_int_set_rate(data, freq);
>> +             else if (data->type == TYPE_BUSF_EXYNOS5420)
>> +                     err = exynos5420_int_set_rate(data, freq, old_freq);
>> +             else
>> +                     err = -EINVAL;
>>               if (err)
>>                       goto unlock;
>>
>> @@ -345,16 +663,38 @@ unlock:
>>       return NOTIFY_DONE;
>>  }
>>
>> +static const struct of_device_id exynos5_busfreq_dt_match[];
>> +
>> +static inline int exynos5_busfreq_get_driver_data(struct platform_device *pdev)
>> +{
>> +#ifdef CONFIG_OF
>
> Exynos is DT-only, so there is no need to handle non-DT cases.
OK.
>
>> +     struct exynos5_busfreq_drv_data *data;
>> +
>> +     if (pdev->dev.of_node) {
>> +             const struct of_device_id *match;
>> +
>> +             match = of_match_node(exynos5_busfreq_dt_match,
>> +                                     pdev->dev.of_node);
>> +             data = (struct exynos5_busfreq_drv_data *) match->data;
>> +             return data->busf_type;
>> +     }
>> +#endif
>> +     return platform_get_device_id(pdev)->driver_data;
>> +}
>> +
>>  static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>>  {
>>       struct busfreq_data_int *data;
>>       struct busfreq_ppmu_data *ppmu_data;
>> +     struct device_node *np = pdev->dev.of_node;
>>       struct dev_pm_opp *opp;
>>       struct device *dev = &pdev->dev;
>> -     struct device_node *np;
>>       unsigned long initial_freq;
>>       unsigned long initial_volt;
>> +     struct clk *mux_clk, *div_clk;
>> +     struct int_comp_clks *int_pm_clk;
>>       int err = 0;
>> +     int nr_clk;
>>       int i;
>>
>>       data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data_int),
>> @@ -364,22 +704,27 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>>               return -ENOMEM;
>>       }
>>
>> +     INIT_LIST_HEAD(&data->list);
>> +     data->type = exynos5_busfreq_get_driver_data(pdev);
>> +
>>       ppmu_data = &data->ppmu_data;
>> -     ppmu_data->ppmu_end = PPMU_END;
>> +     if (data->type == TYPE_BUSF_EXYNOS5250) {
>> +             ppmu_data->ppmu_end = PPMU_END_5250;
>> +     } else if (data->type == TYPE_BUSF_EXYNOS5420) {
>> +             ppmu_data->ppmu_end = PPMU_END_5420;
>> +     } else {
>> +             dev_err(dev, "Cannot determine the device id %d\n", data->type);
>> +             return -EINVAL;
>> +     }
>> +
>>       ppmu_data->ppmu = devm_kzalloc(dev,
>> -                                    sizeof(struct exynos_ppmu) * PPMU_END,
>> -                                    GFP_KERNEL);
>> +                     sizeof(struct exynos_ppmu) * (ppmu_data->ppmu_end),
>> +                     GFP_KERNEL);
>>       if (!ppmu_data->ppmu) {
>>               dev_err(dev, "Failed to allocate memory for exynos_ppmu\n");
>>               return -ENOMEM;
>>       }
>>
>> -     np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu");
>> -     if (np == NULL) {
>> -             pr_err("Unable to find PPMU node\n");
>> -             return -ENOENT;
>> -     }
>
> This was actually closer to the right solution than what this series
> does. Actually there was similar attempt already, but for Exynos4 and I
> even suggested how this could be handled properly. Please see [1] for
> the whole discussion.
>
> [1]
> https://www.mail-archive.com/linux-samsung-soc@vger.kernel.org/msg27491.html
Chanwoo are you working on this for exynos4 ? As far as I can see
there has been no further effort in adding the  power plane and ppmu
bindings. So, before I start working on this I wanted to confirm that
you don't have something in the pipeline.

Tomasz, I have read through the thread and as per your suggestions
this is how the dt bindings (for my INT use case) would look like:

ppmu_dmc0_0: ppmu@10D00000 {
        /* Resources of PPMU */
}

ppmu_dmc0_1: ppmu@10D00000 {
        /* Resources of PPMU */
}

ppmu_dmc_1_0: ppmu@10D00000 {
        /* Resources of PPMU */
}

power-plane-int {
        compatible = "exynos5420-int, power-plane";
        samsung,ppmus = <&ppmu_dmc_0_0>, &ppmu_dmc_0_1>, &ppmu_dmc_1_0>;
        samsung,devices = ??
        clock-names = "aclk200_fsys", "pclk200_fsys", "aclk100_noc"...;
        clocks = <&clock CLK_ACLK200_FSYS2>,...;
        vdd_int-supply = <&buck3_reg>; /* VDD_INT */
        operating-points = <
                                   266000, 1300000
                                   200000, 1250000
                                   160000, 1225000
                                   133000, 1200000
                                   >;
};

Here are my doubts regarding this:
1) The INT bus consists of several IPs like MFC, DISP, HDMI, ISP etc.
The 3 PPMUs listed above monitor the load on the TOP block which these
IPs are a part of. What should be the "samsung, devices" associated
with it ?
2) I have currently listed the virtual INT bus frequencies in
"operating points" property. So, when there is a high load on the INT
bus, there would be a request for say a virtual frequency of 266MHz
which in turn would bump up several clocks (like MFC, DISP etc) to a
desired level. Is it OK to have the virtual INT bus frequencies listed
in DT ?
3) There are over 10 clocks in 5420 that need to be controlled as part
of the INT domain. Should all these clocks, parent clocks, hardware
recommended source PLLs and the recommended clock rates (at each of
the virtual INT bus frequencies) also be part of the power plane node
? So that I parse all these clocks from dt and associate them with
each of the virtual bus rates. Is that the correct approach ?
>
>> -
>>       for (i = 0; i < ppmu_data->ppmu_end; i++) {
>>               /* map PPMU memory region */
>>               ppmu_data->ppmu[i].hw_base = of_iomap(np, i);
>> @@ -388,13 +733,17 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>>                       return -ENOMEM;
>>               }
>>       }
>> +
>>       data->pm_notifier.notifier_call = exynos5_busfreq_int_pm_notifier_event;
>>       data->dev = dev;
>>       mutex_init(&data->lock);
>>
>> -     err = exynos5250_init_int_tables(data);
>> -     if (err)
>> +     err = exynos5_init_int_tables(data);
>> +     if (err) {
>> +             dev_err(dev, "Cannot initialize busfreq table %d\n",
>> +                          data->type);
>>               return err;
>> +     }
>>
>>       data->vdd_int = devm_regulator_get(dev, "vdd_int");
>>       if (IS_ERR(data->vdd_int)) {
>> @@ -402,18 +751,70 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>>               return PTR_ERR(data->vdd_int);
>>       }
>>
>> -     for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
>> -             struct int_clk *clk_info = &exynos5_int_clks[i];
>> +     if (data->type == TYPE_BUSF_EXYNOS5250) {
>> +             for (i = 0; i < ARRAY_SIZE(exynos5250_int_clks); i++) {
>> +                     struct int_simple_clk *clk_info =
>> +                             &exynos5250_int_clks[i];
>> +
>> +                     clk_info->clk = devm_clk_get(dev, clk_info->clk_name);
>> +                     if (IS_ERR(clk_info->clk)) {
>> +                             dev_err(dev, "Failed to get clock %s\n",
>> +                                     clk_info->clk_name);
>> +                             return PTR_ERR(clk_info->clk);
>> +                     }
>> +             }
>> +     } else {
>> +             data->mout_ipll = devm_clk_get(dev, "mout_ipll");
>> +             if (IS_ERR(data->mout_ipll)) {
>> +                     dev_err(dev, "Cannot get clock \"mout_ipll\"\n");
>> +                     return PTR_ERR(data->mout_ipll);
>> +             }
>>
>> -             clk_info->clk = devm_clk_get(dev, clk_info->clk_name);
>> -             if (IS_ERR(clk_info->clk)) {
>> -                     dev_err(dev, "Failed to get clock %s\n",
>> -                             clk_info->clk_name);
>> -                     return PTR_ERR(clk_info->clk);
>> +             data->mout_mpll = devm_clk_get(dev, "mout_mpll");
>> +             if (IS_ERR(data->mout_mpll)) {
>> +                     dev_err(dev, "Cannot get clock \"mout_mpll\"\n");
>> +                     return PTR_ERR(data->mout_mpll);
>> +             }
>> +
>> +             data->mout_dpll = devm_clk_get(dev, "mout_dpll");
>> +             if (IS_ERR(data->mout_dpll)) {
>> +                     dev_err(dev, "Cannot get clock \"mout_dpll\"\n");
>> +                     return PTR_ERR(data->mout_dpll);
>> +             }
>> +
>> +             data->mout_cpll = devm_clk_get(dev, "mout_cpll");
>> +             if (IS_ERR(data->mout_cpll)) {
>> +                     dev_err(dev, "Cannot get clock \"mout_cpll\"\n");
>> +                     return PTR_ERR(data->mout_cpll);
>> +             }
>> +
>> +             for (nr_clk = 0; exynos5420_int_pm_clks[nr_clk] != NULL;
>> +                                                             nr_clk++) {
>> +                     int_pm_clk = exynos5420_int_pm_clks[nr_clk];
>> +                     mux_clk = devm_clk_get(dev, int_pm_clk->mux_clk_name);
>> +                     if (IS_ERR(mux_clk)) {
>> +                             dev_err(dev, "Cannot get mux clock: %s\n",
>> +                                             int_pm_clk->mux_clk_name);
>> +                             return PTR_ERR(mux_clk);
>> +                     }
>> +                     div_clk = devm_clk_get(dev, int_pm_clk->div_clk_name);
>> +                     if (IS_ERR(div_clk)) {
>> +                             dev_err(dev, "Cannot get div clock: %s\n",
>> +                                             int_pm_clk->div_clk_name);
>> +                             return PTR_ERR(div_clk);
>> +                     }
>> +                     int_pm_clk->mux_clk = mux_clk;
>> +                     int_pm_clk->div_clk = div_clk;
>> +                     list_add_tail(&int_pm_clk->node, &data->list);
>
> All those could be probably handled with an array and a for loop.
>
> In general, this patch apparently contains many separate changes and not
> only is hard to review but also hard to debug potential problems - git
> bisect has commit granularity.
>
> Please refactor the driver step by step first and then add support for
> new SoC after it has all the needed features.
You are right. I will first post patches adding dt support for only
5250. If that looks good then will add for 5420.
Thanks for the comprehensive review.

Regards,
Abhilash
>
> Best regards,
> Tomasz

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

* Re: [PATCH v3 5/7] PM / devfreq: exynos5250: migrate to common-clock
  2014-07-16 11:03     ` Tomasz Figa
@ 2014-07-16 18:00       ` Abhilash Kesavan
  0 siblings, 0 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-16 18:00 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: myungjoo.ham, linux-pm@vger.kernel.org, Kukjin Kim, devicetree,
	rafael.j.wysocki@intel.com

Hi Tomasz,

On Wed, Jul 16, 2014 at 4:33 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> Hi Abhilash, Andrew,
>
> Please see my comments inline.
>
> On 15.07.2014 20:35, Abhilash Kesavan wrote:
>> From: Andrew Bresticker <abrestic@chromium.org>
>>
>> Use the common-clock framework to scale frequencies for the various
>> peripheral clocks on the Exynos5250.
>>
>> Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
>> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
>> ---
>>  drivers/devfreq/exynos/exynos5_bus.c |  131 ++++++++++++++++++++++++++++++----
>>  1 file changed, 119 insertions(+), 12 deletions(-)
>
> [snip]
>
>> +
>>  static struct int_bus_opp_table exynos5_int_opp_table[] = {
>>       {LV_0, 266000, 1025000},
>>       {LV_1, 200000, 1025000},
>> @@ -75,6 +85,98 @@ static struct int_bus_opp_table exynos5_int_opp_table[] = {
>>       {0, 0, 0},
>>  };
>>
>> +static struct int_clk_table aclk_166[] = {
>> +     {LV_0, 167000},
>> +     {LV_1, 111000},
>> +     {LV_2,  84000},
>> +     {LV_3,  84000},
>> +     {LV_4,  42000},
>> +};
>
> [snip]
>
>> +static struct int_clk_table aclk_300_gscl[] = {
>> +     {LV_0, 267000},
>> +     {LV_1, 267000},
>> +     {LV_2, 267000},
>> +     {LV_3, 200000},
>> +     {LV_4, 100000},
>> +};
>
> Shouldn't you manage this using OPP framework and parse this from DT?
OK, one of the questions I had was about the handling of virtual INT
bus levels (frequencies and voltages). I have consolidated my
understanding of how the bindings should look and questions I had in
the driver thread.
>
>> +
>> +#define EXYNOS5_INT_CLK(name, tbl) {         \
>> +     .clk_name = name,                       \
>> +     .freq_table = tbl,                      \
>> +}
>
> [snip]
>
>> @@ -320,16 +427,16 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>>       rcu_read_unlock();
>>       data->curr_freq = initial_freq;
>>
>> -     err = clk_set_rate(data->int_clk, initial_freq * 1000);
>> +     err = exynos5_int_setvolt(data, initial_volt);
>> +     if (err)
>> +             return err;
>> +
>> +     err = exynos5_int_set_rate(data, initial_freq);
>>       if (err) {
>>               dev_err(dev, "Failed to set initial frequency\n");
>>               return err;
>>       }
>>
>> -     err = exynos5_int_setvolt(data, initial_volt);
>> -     if (err)
>> -             return err;
>> -
>
> Whether voltage or rate should be set first depends on current settings
> and initial_{freq,volt}. Also it might be more convenient to simply call
> exynos5_busfreq_int_target() here.
Wouldn't setting voltage first always be safe ? Yes, just calling
target would be better. Will modify.

Regards,
Abhilash
>
> Best regards,
> Tomasz

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

* Re: [PATCH v3 3/7] ARM: dts: Add PPMU device tree support for Exynos5250
  2014-07-16 10:55       ` Tomasz Figa
@ 2014-07-16 18:00         ` Abhilash Kesavan
  0 siblings, 0 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-16 18:00 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: myungjoo.ham, linux-pm@vger.kernel.org, Kukjin Kim, devicetree,
	Mark Rutland, Rob Herring, rafael.j.wysocki@intel.com

Hi Tomasz,

On Wed, Jul 16, 2014 at 4:25 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> Hi Abhilash,
>
> Please see my comments inline. Also CCing some DT maintainers.
>
> On 15.07.2014 20:34, Abhilash Kesavan wrote:
>> PPMU is required by the exynos5250 devfreq driver. Add a device
>> tree node for it.
>>
>> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
>> ---
>>  .../bindings/arm/exynos/ppmu-exynos5.txt           |   26 ++++++++++++++++++++
>>  arch/arm/boot/dts/exynos5250.dtsi                  |    8 ++++++
>>  2 files changed, 34 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt
>>
>> diff --git a/Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt b/Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt
>> new file mode 100644
>> index 0000000..a6a2eba
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/exynos/ppmu-exynos5.txt
>> @@ -0,0 +1,26 @@
>> +Exynos PPMU driver
>> +-------------------
>> +
>> +Performance events are primitive values used to get performance data. These
>> +events provide information about the behavior of the SoC that can be used
>> +when analyzing system performance. These events are made visible using the
>> +PPMU logic.
>> +Exynos PPMU driver is used by the exynos5 devfreq drivers to control the
>> +bus frequency/voltage.
>> +
>> +Required properties:
>> +- compatible: "samsung,exynos5250-int-busfreq", "samsung,exynos5420-int-busfreq"
>
> If this is a binding for PPMU, shouldn't the compatible string contain
> the "ppmu" string too? E.g. "samsung,exynos5250-ppmu",
> "samsung,exynos5420-ppmu".
>
>> +- reg:
>> +     * physical base address of the PPMUs (e.g DDR, Right Bus, Left bus etc)
>> +     and length of memory mapped region.
>
> Since PPMU are separate IP blocks, they should have their own device
> nodes with only address ranges of their own. Representing all PPMUs in
> the SoC with a single node is incorrect.
OK, I will re-work this.
>
> Best regards,
> Tomasz

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

* Re: [PATCH v3 3/7] ARM: dts: Add PPMU device tree support for Exynos5250
  2014-07-16 10:56       ` Tomasz Figa
@ 2014-07-16 18:01         ` Abhilash Kesavan
  0 siblings, 0 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-16 18:01 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: myungjoo.ham, linux-pm@vger.kernel.org, Kukjin Kim, devicetree,
	rafael.j.wysocki@intel.com

Hi Tomasz,

On Wed, Jul 16, 2014 at 4:26 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> Actually one more comment.
>
> On 15.07.2014 20:34, Abhilash Kesavan wrote:
>
> [snip]
>
>> diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
>> index 834fb5a..5284f00 100644
>> --- a/arch/arm/boot/dts/exynos5250.dtsi
>> +++ b/arch/arm/boot/dts/exynos5250.dtsi
>> @@ -778,4 +778,12 @@
>>               clocks = <&clock CLK_SSS>;
>>               clock-names = "secss";
>>       };
>> +
>> +     ppmu@10C40000 {
>> +             compatible = "samsung,exynos5250-int-busfreq";
>> +             reg = <0x10C40000 0x2000        /* PPMU_DDR_C */
>> +                    0x10C50000 0x2000        /* PPMU_DDR_L */
>> +                    0x10CB0000 0x2000        /* PPMU_DDR_R */
>> +                    0x13660000 0x2000>;      /* PPMU_RIGHT */
>> +     };
>>  };
>>
>
> Please separate patches adding the binding from patches modifying device
> tree sources. Those are two logically separate changes.
Will split them in the next version.
>
> Best regards,
> Tomasz

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

* Re: [PATCH v3 1/7] clk: exynos5250: add aliases for clocks used by devfreq
  2014-07-16 10:50     ` Tomasz Figa
@ 2014-07-16 18:04       ` Abhilash Kesavan
  0 siblings, 0 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-16 18:04 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: myungjoo.ham, linux-pm@vger.kernel.org, Kukjin Kim, devicetree,
	rafael.j.wysocki@intel.com

Hi Tomasz,

On Wed, Jul 16, 2014 at 4:20 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> Hi Abhilash, Andrew,
>
> On 15.07.2014 20:33, Abhilash Kesavan wrote:
>> From: Andrew Bresticker <abrestic@chromium.org>
>>
>> Devfreq does not support DT-based lookup of these peripheral clocks,
>> so add aliases for them.
>
> I'm afraid I have to NAK this. This is going backwards. Please fix the
> devfreq driver instead.
Will start modifying based on discussions in the other thread.

Thanks,
Abhilash
>
> Best regards,
> Tomasz

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

* Re: [PATCH v3 7/7] PM / devfreq: Add devfreq driver for Exynos5420
  2014-07-16 17:59         ` Abhilash Kesavan
@ 2014-07-28 12:01           ` Abhilash Kesavan
  0 siblings, 0 replies; 32+ messages in thread
From: Abhilash Kesavan @ 2014-07-28 12:01 UTC (permalink / raw)
  To: Tomasz Figa, 최찬우
  Cc: myungjoo.ham, linux-pm@vger.kernel.org, Kukjin Kim, devicetree,
	rafael.j.wysocki@intel.com

Hi Tomasz,

On Wed, Jul 16, 2014 at 11:29 PM, Abhilash Kesavan
<kesavan.abhilash@gmail.com> wrote:
> Hi Tomasz,
>
> On Wed, Jul 16, 2014 at 5:25 PM, Tomasz Figa <t.figa@samsung.com> wrote:
>> Hi Abhilash,
>>
>> Please see my comments inline.
>>
>> On 15.07.2014 20:36, Abhilash Kesavan wrote:
>>> From: "Arjun.K.V" <arjun.kv@samsung.com>
>>>
>>> Exynos5420 bus device devfreq driver monitors PPMU counters and
>>> adjusts INT domain operating frequencies and voltages. Support
>>> for 5420 has been added to the extant 5250 support.
>>>
>>> Signed-off-by: Arjun.K.V <arjun.kv@samsung.com>
>>> Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
>>> Signed-off-by: Doug Anderson <dianders@chromium.org>
>>> Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
>>> ---
>>>  drivers/devfreq/Kconfig              |   10 +-
>>>  drivers/devfreq/exynos/exynos5_bus.c |  598 ++++++++++++++++++++++++++++------
>>>  2 files changed, 508 insertions(+), 100 deletions(-)
>>
>> [snip]
>>
>>>
>>> -#define MAX_SAFEVOLT                 1100000 /* 1.10V */
>>> -/* Assume that the bus is saturated if the utilization is 25% */
>>> -#define INT_BUS_SATURATION_RATIO     25
>>> +/* Assume that we need to bump up the level if the utilization is 10% */
>>> +#define INT_BUS_SATURATION_RATIO     10
>>
>> There is nothing about this change in commit message and changing the
>> ratio from 25% to 10% seems to be rather significant. Please give the
>> rationale for this change.
> Will do.
>>
>>> +#define INT_VOLT_STEP_UV             12500
>>> +
>>> +struct exynos5_busfreq_drv_data {
>>> +     int busf_type;
>>> +};
>>> +
>>> +enum exynos5_busf_type {
>>> +     TYPE_BUSF_EXYNOS5250,
>>> +     TYPE_BUSF_EXYNOS5420,
>>> +};
>>
>> Using this kind of enums is discouraged. Rather than that, just create a
>> structure that describes the differences between variants and use this
>> as type.
> OK.
>>
>>>
>>>  enum int_level_idx {
>>>       LV_0,
>>> @@ -41,12 +46,31 @@ enum int_level_idx {
>>>       _LV_END
>>>  };
>>
>> [snip]
>>
>>> +
>>> +static struct int_bus_opp_table *exynos5_int_opp_table;
>>> +static struct int_bus_opp_table exynos5250_int_opp_table[] = {
>>>       {LV_0, 266000, 1025000},
>>>       {LV_1, 200000, 1025000},
>>>       {LV_2, 160000, 1025000},
>>
>> [snip]
>>
>>> +static struct int_clk_info exynos5420_aclk_300_jpeg[] = {
>>> +     /* Level, Freq, Parent_Pll */
>>> +     {LV_0, 300000, D_PLL},
>>> +     {LV_1, 300000, D_PLL},
>>> +     {LV_2, 200000, D_PLL},
>>> +     {LV_3, 150000, D_PLL},
>>> +     {LV_4,  75000, D_PLL},
>>> +};
>>
>> These should be parsed from DT.
> OK.
>>
>>> +
>>> +#define EXYNOS5420_INT_PM_CLK(NAME, CLK, PCLK, CLK_INFO)     \
>>> +static struct int_comp_clks int_pm_clks_##NAME = {   \
>>> +     .mux_clk_name = CLK,                            \
>>> +     .div_clk_name = PCLK,                           \
>>> +     .clk_info = CLK_INFO,                           \
>>> +}
>>> +
>>> +EXYNOS5420_INT_PM_CLK(aclk_200_fsys, "aclk200_fsys",
>>> +                     "aclk200_fsys_d", exynos5420_aclk_200_fsys);
>>> +EXYNOS5420_INT_PM_CLK(pclk_200_fsys, "pclk200_fsys",
>>> +                     "pclk200_fsys_d", exynos5420_pclk_200_fsys);
>>> +EXYNOS5420_INT_PM_CLK(aclk_100_noc, "aclk100_noc",
>>> +                     "aclk100_noc_d", exynos5420_aclk_100_noc);
>>> +EXYNOS5420_INT_PM_CLK(aclk_400_wcore, "aclk400_wcore",
>>> +                     "aclk400_wcore_d", exynos5420_aclk_400_wcore);
>>> +EXYNOS5420_INT_PM_CLK(aclk_200_fsys2, "aclk200_fsys2",
>>> +                     "aclk200_fsys2_d", exynos5420_aclk_200_fsys2);
>>> +EXYNOS5420_INT_PM_CLK(aclk_400_mscl, "aclk400_mscl",
>>> +                     "aclk400_mscl_d", exynos5420_aclk_400_mscl);
>>> +EXYNOS5420_INT_PM_CLK(aclk_166, "aclk166",
>>> +                     "aclk166_d", exynos5420_aclk_166);
>>> +EXYNOS5420_INT_PM_CLK(aclk_266, "aclk266",
>>> +                     "aclk266_d", exynos5420_aclk_266);
>>> +EXYNOS5420_INT_PM_CLK(aclk_66, "aclk66",
>>> +                     "aclk66_d", exynos5420_aclk_66);
>>> +EXYNOS5420_INT_PM_CLK(aclk_300_disp1, "aclk300_disp1",
>>> +                     "aclk300_disp1_d", exynos5420_aclk_300_disp1);
>>> +EXYNOS5420_INT_PM_CLK(aclk_300_jpeg, "aclk300_jpeg",
>>> +                     "aclk300_jpeg_d", exynos5420_aclk_300_jpeg);
>>> +EXYNOS5420_INT_PM_CLK(aclk_400_disp1, "aclk400_disp1",
>>> +                     "aclk400_disp1_d", exynos5420_aclk_400_disp1);
>>
>> List of the clocks should be parsed from DT as well, without hardcoding
>> data for every SoC in the driver.
>>
>>> +
>>> +static struct int_comp_clks *exynos5420_int_pm_clks[] = {
>>> +     &int_pm_clks_aclk_200_fsys,
>>> +     &int_pm_clks_pclk_200_fsys,
>>> +     &int_pm_clks_aclk_100_noc,
>>> +     &int_pm_clks_aclk_400_wcore,
>>> +     &int_pm_clks_aclk_200_fsys2,
>>> +     &int_pm_clks_aclk_400_mscl,
>>> +     &int_pm_clks_aclk_166,
>>> +     &int_pm_clks_aclk_266,
>>> +     &int_pm_clks_aclk_66,
>>> +     &int_pm_clks_aclk_300_disp1,
>>> +     &int_pm_clks_aclk_300_jpeg,
>>> +     &int_pm_clks_aclk_400_disp1,
>>> +     NULL,
>>> +};
>>> +
>>> +static int exynos5250_int_set_rate(struct busfreq_data_int *data,
>>>                               unsigned long rate)
>>>  {
>>>       int index, i;
>>>
>>> -     for (index = 0; index < ARRAY_SIZE(exynos5_int_opp_table); index++) {
>>> -             if (exynos5_int_opp_table[index].clk == rate)
>>> +     for (index = 0; index < ARRAY_SIZE(exynos5250_int_opp_table); index++) {
>>> +             if (exynos5250_int_opp_table[index].clk == rate)
>>>                       break;
>>>       }
>>>
>>> @@ -161,8 +370,8 @@ static int exynos5_int_set_rate(struct busfreq_data_int *data,
>>>               return -EINVAL;
>>>
>>>       /* Change the system clock divider values */
>>> -     for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
>>> -             struct int_clk *clk_info = &exynos5_int_clks[i];
>>> +     for (i = 0; i < ARRAY_SIZE(exynos5250_int_clks); i++) {
>>> +             struct int_simple_clk *clk_info = &exynos5250_int_clks[i];
>>>               int ret;
>>>
>>>               ret = clk_set_rate(clk_info->clk,
>>> @@ -177,10 +386,111 @@ static int exynos5_int_set_rate(struct busfreq_data_int *data,
>>>       return 0;
>>>  }
>>>
>>> +static struct clk *exynos5420_find_pll(struct busfreq_data_int *data,
>>> +                                 enum int_bus_pll target_pll)
>>> +{
>>> +     struct clk *target_src_clk = NULL;
>>> +
>>> +     switch (target_pll) {
>>> +     case C_PLL:
>>> +             target_src_clk = data->mout_cpll;
>>> +             break;
>>> +     case M_PLL:
>>> +             target_src_clk = data->mout_mpll;
>>> +             break;
>>> +     case D_PLL:
>>> +             target_src_clk = data->mout_dpll;
>>> +             break;
>>> +     case I_PLL:
>>> +             target_src_clk = data->mout_ipll;
>>> +             break;
>>> +     default:
>>> +             break;
>>> +     }
>>
>> What about storing the clocks in an array? Then all you would need to do
>> could be as simple as accessing data->plls[target_pll].
> OK.
>>
>>> +
>>> +     return target_src_clk;
>>> +}
>>> +
>>> +static int exynos5420_int_set_rate(struct busfreq_data_int *data,
>>> +             unsigned long target_freq, unsigned long pre_freq)
>>> +{
>>> +     unsigned int i;
>>> +     unsigned long tar_rate;
>>> +     int target_idx = -EINVAL;
>>> +     int pre_idx = -EINVAL;
>>> +     struct int_comp_clks *int_clk;
>>> +     struct clk *new_src_pll;
>>> +     struct clk *old_src_pll;
>>> +     unsigned long old_src_rate, new_src_rate;
>>> +     unsigned long rate1, rate2, rate3, rate4;
>>> +
>>> +     /* Find the levels for target and previous frequencies */
>>> +     for (i = 0; i < _LV_END; i++) {
>>> +             if (exynos5420_int_opp_table[i].clk == target_freq)
>>> +                     target_idx = exynos5420_int_opp_table[i].idx;
>>> +             if (exynos5420_int_opp_table[i].clk == pre_freq)
>>> +                     pre_idx = exynos5420_int_opp_table[i].idx;
>>> +     }
>>> +
>>> +     list_for_each_entry(int_clk, &data->list, node) {
>>> +             tar_rate = int_clk->clk_info[target_idx].target_freq * 1000;
>>> +
>>> +             old_src_pll = clk_get_parent(int_clk->mux_clk);
>>> +             new_src_pll = exynos5420_find_pll(data,
>>> +                             int_clk->clk_info[target_idx].src_pll);
>>> +
>>> +             if (old_src_pll == new_src_pll) {
>>> +                     /* No need to change pll */
>>> +                     clk_set_rate(int_clk->div_clk, tar_rate);
>>> +                     pr_debug("%s: %s now %lu (%lu)\n", __func__,
>>> +                              int_clk->mux_clk_name,
>>> +                              clk_get_rate(int_clk->div_clk), tar_rate);
>>> +                     continue;
>>> +             }
>>> +
>>> +             old_src_rate = clk_get_rate(old_src_pll);
>>> +             new_src_rate = clk_get_rate(new_src_pll);
>>> +             rate1 = clk_get_rate(int_clk->div_clk);
>>> +
>>> +             /*
>>> +              * If we're switching to a faster PLL we should bump up the
>>> +              * divider before switching.
>>> +              */
>>> +             if (new_src_rate > old_src_rate) {
>>> +                     int new_div;
>>> +                     unsigned long tmp_rate;
>>> +
>>> +                     new_div = DIV_ROUND_UP(new_src_rate, tar_rate);
>>> +                     tmp_rate = DIV_ROUND_UP(old_src_rate, new_div);
>>> +                     clk_set_rate(int_clk->div_clk, tmp_rate);
>>> +             }
>>> +             rate2 = clk_get_rate(int_clk->div_clk);
>>> +
>>> +             /* We can safely change the mux now */
>>> +             clk_set_parent(int_clk->mux_clk, new_src_pll);
>>> +             rate3 = clk_get_rate(int_clk->div_clk);
>>> +
>>> +             /*
>>> +              * Give us a proper divider; technically not needed in the case
>>> +              * where we pre-calculated the divider above (the new_src_rate >
>>> +              * old_src_rate case), but let's be formal about it.
>>> +              */
>>> +             clk_set_rate(int_clk->div_clk, tar_rate);
>>> +             rate4 = clk_get_rate(int_clk->div_clk);
>>> +
>>> +             pr_debug("%s: %s => PLL %d; %lu=>%lu=>%lu=>%lu (%lu)\n",
>>> +                      __func__, int_clk->mux_clk_name,
>>> +                      int_clk->clk_info[target_idx].src_pll,
>>> +                      rate1, rate2, rate3, rate4, tar_rate);
>>> +     }
>>> +     return 0;
>>> +}
>>
>> The above function looks like it could be made much more generic and
>> reused for Exynos5250 as well.
> I will look at making it common for the two,
>>
>>> +
>>>  static int exynos5_int_setvolt(struct busfreq_data_int *data,
>>>                               unsigned long volt)
>>>  {
>>> -     return regulator_set_voltage(data->vdd_int, volt, MAX_SAFEVOLT);
>>> +     return regulator_set_voltage(data->vdd_int, volt,
>>> +                             volt + INT_VOLT_STEP_UV);
>>>  }
>>>
>>>  static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
>>> @@ -218,18 +528,15 @@ static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,
>>>       if (data->disabled)
>>>               goto out;
>>>
>>> -     if (freq > exynos5_int_opp_table[0].clk)
>>> -             pm_qos_update_request(&data->int_req, freq * 16 / 1000);
>>> -     else
>>> -             pm_qos_update_request(&data->int_req, -1);
>>> -
>>>       if (old_freq < freq)
>>>               err = exynos5_int_setvolt(data, volt);
>>>       if (err)
>>>               goto out;
>>>
>>> -     err = exynos5_int_set_rate(data, freq);
>>> -
>>> +     if (data->type == TYPE_BUSF_EXYNOS5250)
>>> +             err = exynos5250_int_set_rate(data, freq);
>>> +     else
>>> +             err = exynos5420_int_set_rate(data, freq, old_freq);
>>>       if (err)
>>>               goto out;
>>>
>>> @@ -267,16 +574,20 @@ static int exynos5_int_get_dev_status(struct device *dev,
>>>  }
>>>
>>>  static struct devfreq_dev_profile exynos5_devfreq_int_profile = {
>>> -     .initial_freq           = 160000,
>>> -     .polling_ms             = 100,
>>> +     .polling_ms             = 10,
>>
>> Another change not mentioned in commit message.
>>
>>>       .target                 = exynos5_busfreq_int_target,
>>>       .get_dev_status         = exynos5_int_get_dev_status,
>>>  };
>>>
>>> -static int exynos5250_init_int_tables(struct busfreq_data_int *data)
>>> +static int exynos5_init_int_tables(struct busfreq_data_int *data)
>>>  {
>>>       int i, err = 0;
>>>
>>> +     if (data->type == TYPE_BUSF_EXYNOS5250)
>>> +             exynos5_int_opp_table = exynos5250_int_opp_table;
>>> +     else
>>> +             exynos5_int_opp_table = exynos5420_int_opp_table;
>>> +
>>>       for (i = LV_0; i < _LV_END; i++) {
>>>               err = dev_pm_opp_add(data->dev, exynos5_int_opp_table[i].clk,
>>>                               exynos5_int_opp_table[i].volt);
>>> @@ -297,6 +608,7 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
>>>       struct dev_pm_opp *opp;
>>>       unsigned long maxfreq = ULONG_MAX;
>>>       unsigned long freq;
>>> +     unsigned long old_freq;
>>>       unsigned long volt;
>>>       int err = 0;
>>>
>>> @@ -322,8 +634,14 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,
>>>               if (err)
>>>                       goto unlock;
>>>
>>> -             err = exynos5_int_set_rate(data, freq);
>>> +             old_freq = data->curr_freq;
>>>
>>> +             if (data->type == TYPE_BUSF_EXYNOS5250)
>>> +                     err = exynos5250_int_set_rate(data, freq);
>>> +             else if (data->type == TYPE_BUSF_EXYNOS5420)
>>> +                     err = exynos5420_int_set_rate(data, freq, old_freq);
>>> +             else
>>> +                     err = -EINVAL;
>>>               if (err)
>>>                       goto unlock;
>>>
>>> @@ -345,16 +663,38 @@ unlock:
>>>       return NOTIFY_DONE;
>>>  }
>>>
>>> +static const struct of_device_id exynos5_busfreq_dt_match[];
>>> +
>>> +static inline int exynos5_busfreq_get_driver_data(struct platform_device *pdev)
>>> +{
>>> +#ifdef CONFIG_OF
>>
>> Exynos is DT-only, so there is no need to handle non-DT cases.
> OK.
>>
>>> +     struct exynos5_busfreq_drv_data *data;
>>> +
>>> +     if (pdev->dev.of_node) {
>>> +             const struct of_device_id *match;
>>> +
>>> +             match = of_match_node(exynos5_busfreq_dt_match,
>>> +                                     pdev->dev.of_node);
>>> +             data = (struct exynos5_busfreq_drv_data *) match->data;
>>> +             return data->busf_type;
>>> +     }
>>> +#endif
>>> +     return platform_get_device_id(pdev)->driver_data;
>>> +}
>>> +
>>>  static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>>>  {
>>>       struct busfreq_data_int *data;
>>>       struct busfreq_ppmu_data *ppmu_data;
>>> +     struct device_node *np = pdev->dev.of_node;
>>>       struct dev_pm_opp *opp;
>>>       struct device *dev = &pdev->dev;
>>> -     struct device_node *np;
>>>       unsigned long initial_freq;
>>>       unsigned long initial_volt;
>>> +     struct clk *mux_clk, *div_clk;
>>> +     struct int_comp_clks *int_pm_clk;
>>>       int err = 0;
>>> +     int nr_clk;
>>>       int i;
>>>
>>>       data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data_int),
>>> @@ -364,22 +704,27 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>>>               return -ENOMEM;
>>>       }
>>>
>>> +     INIT_LIST_HEAD(&data->list);
>>> +     data->type = exynos5_busfreq_get_driver_data(pdev);
>>> +
>>>       ppmu_data = &data->ppmu_data;
>>> -     ppmu_data->ppmu_end = PPMU_END;
>>> +     if (data->type == TYPE_BUSF_EXYNOS5250) {
>>> +             ppmu_data->ppmu_end = PPMU_END_5250;
>>> +     } else if (data->type == TYPE_BUSF_EXYNOS5420) {
>>> +             ppmu_data->ppmu_end = PPMU_END_5420;
>>> +     } else {
>>> +             dev_err(dev, "Cannot determine the device id %d\n", data->type);
>>> +             return -EINVAL;
>>> +     }
>>> +
>>>       ppmu_data->ppmu = devm_kzalloc(dev,
>>> -                                    sizeof(struct exynos_ppmu) * PPMU_END,
>>> -                                    GFP_KERNEL);
>>> +                     sizeof(struct exynos_ppmu) * (ppmu_data->ppmu_end),
>>> +                     GFP_KERNEL);
>>>       if (!ppmu_data->ppmu) {
>>>               dev_err(dev, "Failed to allocate memory for exynos_ppmu\n");
>>>               return -ENOMEM;
>>>       }
>>>
>>> -     np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu");
>>> -     if (np == NULL) {
>>> -             pr_err("Unable to find PPMU node\n");
>>> -             return -ENOENT;
>>> -     }
>>
>> This was actually closer to the right solution than what this series
>> does. Actually there was similar attempt already, but for Exynos4 and I
>> even suggested how this could be handled properly. Please see [1] for
>> the whole discussion.
>>
>> [1]
>> https://www.mail-archive.com/linux-samsung-soc@vger.kernel.org/msg27491.html
> Chanwoo are you working on this for exynos4 ? As far as I can see
> there has been no further effort in adding the  power plane and ppmu
> bindings. So, before I start working on this I wanted to confirm that
> you don't have something in the pipeline.
>
> Tomasz, I have read through the thread and as per your suggestions
> this is how the dt bindings (for my INT use case) would look like:
>
> ppmu_dmc0_0: ppmu@10D00000 {
>         /* Resources of PPMU */
> }
>
> ppmu_dmc0_1: ppmu@10D00000 {
>         /* Resources of PPMU */
> }
>
> ppmu_dmc_1_0: ppmu@10D00000 {
>         /* Resources of PPMU */
> }
>
> power-plane-int {
>         compatible = "exynos5420-int, power-plane";
>         samsung,ppmus = <&ppmu_dmc_0_0>, &ppmu_dmc_0_1>, &ppmu_dmc_1_0>;
>         samsung,devices = ??
>         clock-names = "aclk200_fsys", "pclk200_fsys", "aclk100_noc"...;
>         clocks = <&clock CLK_ACLK200_FSYS2>,...;
>         vdd_int-supply = <&buck3_reg>; /* VDD_INT */
>         operating-points = <
>                                    266000, 1300000
>                                    200000, 1250000
>                                    160000, 1225000
>                                    133000, 1200000
>                                    >;
> };
>
> Here are my doubts regarding this:
> 1) The INT bus consists of several IPs like MFC, DISP, HDMI, ISP etc.
> The 3 PPMUs listed above monitor the load on the TOP block which these
> IPs are a part of. What should be the "samsung, devices" associated
> with it ?
> 2) I have currently listed the virtual INT bus frequencies in
> "operating points" property. So, when there is a high load on the INT
> bus, there would be a request for say a virtual frequency of 266MHz
> which in turn would bump up several clocks (like MFC, DISP etc) to a
> desired level. Is it OK to have the virtual INT bus frequencies listed
> in DT ?
> 3) There are over 10 clocks in 5420 that need to be controlled as part
> of the INT domain. Should all these clocks, parent clocks, hardware
> recommended source PLLs and the recommended clock rates (at each of
> the virtual INT bus frequencies) also be part of the power plane node
> ? So that I parse all these clocks from dt and associate them with
> each of the virtual bus rates. Is that the correct approach ?

Could you please clarify my doubts so that I may take this forward.

Regards,
Abhilash
>>
>>> -
>>>       for (i = 0; i < ppmu_data->ppmu_end; i++) {
>>>               /* map PPMU memory region */
>>>               ppmu_data->ppmu[i].hw_base = of_iomap(np, i);
>>> @@ -388,13 +733,17 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>>>                       return -ENOMEM;
>>>               }
>>>       }
>>> +
>>>       data->pm_notifier.notifier_call = exynos5_busfreq_int_pm_notifier_event;
>>>       data->dev = dev;
>>>       mutex_init(&data->lock);
>>>
>>> -     err = exynos5250_init_int_tables(data);
>>> -     if (err)
>>> +     err = exynos5_init_int_tables(data);
>>> +     if (err) {
>>> +             dev_err(dev, "Cannot initialize busfreq table %d\n",
>>> +                          data->type);
>>>               return err;
>>> +     }
>>>
>>>       data->vdd_int = devm_regulator_get(dev, "vdd_int");
>>>       if (IS_ERR(data->vdd_int)) {
>>> @@ -402,18 +751,70 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
>>>               return PTR_ERR(data->vdd_int);
>>>       }
>>>
>>> -     for (i = 0; i < ARRAY_SIZE(exynos5_int_clks); i++) {
>>> -             struct int_clk *clk_info = &exynos5_int_clks[i];
>>> +     if (data->type == TYPE_BUSF_EXYNOS5250) {
>>> +             for (i = 0; i < ARRAY_SIZE(exynos5250_int_clks); i++) {
>>> +                     struct int_simple_clk *clk_info =
>>> +                             &exynos5250_int_clks[i];
>>> +
>>> +                     clk_info->clk = devm_clk_get(dev, clk_info->clk_name);
>>> +                     if (IS_ERR(clk_info->clk)) {
>>> +                             dev_err(dev, "Failed to get clock %s\n",
>>> +                                     clk_info->clk_name);
>>> +                             return PTR_ERR(clk_info->clk);
>>> +                     }
>>> +             }
>>> +     } else {
>>> +             data->mout_ipll = devm_clk_get(dev, "mout_ipll");
>>> +             if (IS_ERR(data->mout_ipll)) {
>>> +                     dev_err(dev, "Cannot get clock \"mout_ipll\"\n");
>>> +                     return PTR_ERR(data->mout_ipll);
>>> +             }
>>>
>>> -             clk_info->clk = devm_clk_get(dev, clk_info->clk_name);
>>> -             if (IS_ERR(clk_info->clk)) {
>>> -                     dev_err(dev, "Failed to get clock %s\n",
>>> -                             clk_info->clk_name);
>>> -                     return PTR_ERR(clk_info->clk);
>>> +             data->mout_mpll = devm_clk_get(dev, "mout_mpll");
>>> +             if (IS_ERR(data->mout_mpll)) {
>>> +                     dev_err(dev, "Cannot get clock \"mout_mpll\"\n");
>>> +                     return PTR_ERR(data->mout_mpll);
>>> +             }
>>> +
>>> +             data->mout_dpll = devm_clk_get(dev, "mout_dpll");
>>> +             if (IS_ERR(data->mout_dpll)) {
>>> +                     dev_err(dev, "Cannot get clock \"mout_dpll\"\n");
>>> +                     return PTR_ERR(data->mout_dpll);
>>> +             }
>>> +
>>> +             data->mout_cpll = devm_clk_get(dev, "mout_cpll");
>>> +             if (IS_ERR(data->mout_cpll)) {
>>> +                     dev_err(dev, "Cannot get clock \"mout_cpll\"\n");
>>> +                     return PTR_ERR(data->mout_cpll);
>>> +             }
>>> +
>>> +             for (nr_clk = 0; exynos5420_int_pm_clks[nr_clk] != NULL;
>>> +                                                             nr_clk++) {
>>> +                     int_pm_clk = exynos5420_int_pm_clks[nr_clk];
>>> +                     mux_clk = devm_clk_get(dev, int_pm_clk->mux_clk_name);
>>> +                     if (IS_ERR(mux_clk)) {
>>> +                             dev_err(dev, "Cannot get mux clock: %s\n",
>>> +                                             int_pm_clk->mux_clk_name);
>>> +                             return PTR_ERR(mux_clk);
>>> +                     }
>>> +                     div_clk = devm_clk_get(dev, int_pm_clk->div_clk_name);
>>> +                     if (IS_ERR(div_clk)) {
>>> +                             dev_err(dev, "Cannot get div clock: %s\n",
>>> +                                             int_pm_clk->div_clk_name);
>>> +                             return PTR_ERR(div_clk);
>>> +                     }
>>> +                     int_pm_clk->mux_clk = mux_clk;
>>> +                     int_pm_clk->div_clk = div_clk;
>>> +                     list_add_tail(&int_pm_clk->node, &data->list);
>>
>> All those could be probably handled with an array and a for loop.
>>
>> In general, this patch apparently contains many separate changes and not
>> only is hard to review but also hard to debug potential problems - git
>> bisect has commit granularity.
>>
>> Please refactor the driver step by step first and then add support for
>> new SoC after it has all the needed features.
> You are right. I will first post patches adding dt support for only
> 5250. If that looks good then will add for 5420.
> Thanks for the comprehensive review.
>
> Regards,
> Abhilash
>>
>> Best regards,
>> Tomasz

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

end of thread, other threads:[~2014-07-28 12:01 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-05-22 17:21 [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420 Abhilash Kesavan
2014-05-22 17:21 ` [PATCH RFC v2 1/7] clk: exynos5250: add aliases for clocks used by devfreq Abhilash Kesavan
2014-07-15 18:33   ` [PATCH v3 " Abhilash Kesavan
2014-07-16 10:50     ` Tomasz Figa
2014-07-16 18:04       ` Abhilash Kesavan
2014-05-22 17:21 ` [PATCH RFC v2 2/7] clk: exynos5420: Add " Abhilash Kesavan
2014-07-15 18:34   ` [PATCH v3 " Abhilash Kesavan
2014-07-16 10:51     ` Tomasz Figa
2014-05-22 17:21 ` [PATCH RFC v2 4/7] ARM: dts: Add PPMU device tree support for Exynos5420 Abhilash Kesavan
2014-05-22 17:50   ` Sergei Shtylyov
2014-06-16  7:01     ` Abhilash Kesavan
2014-07-15 18:34   ` [PATCH v3 " Abhilash Kesavan
2014-05-22 17:22 ` [PATCH RFC v2 5/7] PM / devfreq: exynos5250: migrate to common-clock Abhilash Kesavan
2014-07-15 18:35   ` [PATCH v3 " Abhilash Kesavan
2014-07-16 11:03     ` Tomasz Figa
2014-07-16 18:00       ` Abhilash Kesavan
2014-05-22 17:22 ` [PATCH RFC v2 6/7] PM / devfreq: exynos: Fix typo in macro Abhilash Kesavan
2014-07-15 18:35   ` [PATCH v3 " Abhilash Kesavan
     [not found] ` <1400779322-4410-1-git-send-email-a.kesavan-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2014-05-22 17:21   ` [PATCH RFC v2 3/7] ARM: dts: Add PPMU device tree support for Exynos5250 Abhilash Kesavan
2014-07-15 18:34     ` [PATCH v3 " Abhilash Kesavan
2014-07-16 10:55       ` Tomasz Figa
2014-07-16 18:00         ` Abhilash Kesavan
2014-07-16 10:56       ` Tomasz Figa
2014-07-16 18:01         ` Abhilash Kesavan
2014-05-22 17:22   ` [PATCH RFC v2 7/7] PM / devfreq: Add devfreq driver for Exynos5420 Abhilash Kesavan
2014-07-15 18:36     ` [PATCH v3 " Abhilash Kesavan
2014-07-16 11:55       ` Tomasz Figa
2014-07-16 17:59         ` Abhilash Kesavan
2014-07-28 12:01           ` Abhilash Kesavan
2014-05-23  5:08 ` [PATCH RFC v2 0/7] Devfreq support for Exynos5250/5420 Chanwoo Choi
2014-06-16  6:59   ` Abhilash Kesavan
2014-07-15 18:33 ` [PATCH v3 " Abhilash Kesavan

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).